- Combine DROP CONSTRAINT and ADD CONSTRAINT into a single ALTER TABLE
statement for PostgreSQL, eliminating the brief window where no FK exists
- Reword MySQL transaction comment to clarify that ALTER TABLE causes
implicit commits in InnoDB, so the wrapper doesn't provide full atomicity
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
- Close PG race window: drop old FK and add NOT VALID FK before batch
cleanup, so no new orphans can be inserted during cleanup
- Extract batch-delete helpers (batchDeleteOrphansPg, batchDeleteOrphansMysql)
to reduce duplication across up/down and dialects
- Fix ShareUpdateExclusiveLock comment to be more precise
- Make changeset message more descriptive
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
The search FK migration uses transaction: false for PostgreSQL's benefit,
but this left MySQL and SQLite branches non-atomic. A failure between
dropForeign and the new addForeign would leave the table with no FK
constraint. Wrap those branches in explicit knex.transaction() calls.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Prevent orphan cleanup queries from incorrectly matching rows with NULL
entity_id via LEFT JOIN, and use DROP CONSTRAINT IF EXISTS for safer
partial re-runs given transaction: false.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Address review feedback: replace NOT IN subquery with LEFT JOIN for
MySQL batch deletes. Since MySQL doesn't support LIMIT in multi-table
DELETE, orphan entity_ids are found via SELECT first, then deleted in
a separate statement.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
The search FK migration previously ran all DDL and a bulk DELETE in a
single transaction, holding AccessExclusiveLock for the entire duration.
On large tables this blocks all reads for potentially minutes or hours.
This restructures the migration per database engine:
- PostgreSQL: batch-deletes orphans before DDL, uses NOT VALID to skip
full table scan under AccessExclusiveLock, then VALIDATE CONSTRAINT
under the weaker ShareUpdateExclusiveLock
- MySQL: batch-deletes orphans with LIMIT before DDL
- SQLite: unchanged simple approach (no locking concerns)
Also sets transaction: false so the batched deletes run outside the
DDL transaction.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Add a `defaultUserTransformer.useVerifiedEmails` config option to the
githubOrg provider, making the verified domain email behavior from #32997
opt-in rather than the default.
Signed-off-by: Jonathan Roebuck <jroebuck@spotify.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Bump cleye to ^2.3.0 (which pulls in type-flag 4.1.0) and enable
booleanFlagNegation: true in all cli() invocations so that boolean
flags automatically support --no-<flag> prefix negation.
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
Made-with: Cursor
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
Made-with: Cursor
Move the disabled rule after the pointer cursor rules so that
disabled rows always show cursor: not-allowed, even when they
have data-href or data-selection-mode.
Signed-off-by: Johan Persson <johanopersson@gmail.com>
Wrap column header text in a label span with text-overflow: ellipsis
so that long headers truncate instead of wrapping to multiple lines.
Signed-off-by: Johan Persson <johanopersson@gmail.com>
Row always passed onAction to React Aria even when no handler or
href was set, causing all rows to appear interactive. Now onAction
is only passed when there is an actual interaction. CSS explicitly
sets cursor: default on rows and scopes cursor: pointer to rows
with href, selection mode, or pressable state.
Signed-off-by: Johan Persson <johanopersson@gmail.com>
Replace the @aws-sdk/nested-clients/sts mock with a mock of
fromTemporaryCredentials itself. This avoids depending on AWS SDK
internal implementation details while still verifying that the correct
role ARN, session name, and external ID are passed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Adds a catalog config option to set the default conflict strategy when
registering locations, so adopters can default to 'refresh' instead of
'reject' without requiring each caller to specify it explicitly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Simplify by updating refresh_state directly (next_update_at=now,
result_hash='') to force reprocessing, removing the need for
RefreshService wiring in the location store.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
The store now owns the conflict resolution and refresh logic directly,
keeping DefaultLocationService as a thin pass-through layer.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Adds an optional `onConflict` query parameter to the location creation
endpoint. When set to 'refresh', a conflict due to an already-registered
location triggers a refresh of the existing location entity instead of
returning a 409 error. The default behavior ('reject') is unchanged.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Replace the brittle @smithy/shared-ini-file-loader internals hack with
temp credential files and AWS_SHARED_CREDENTIALS_FILE/AWS_CONFIG_FILE
env vars, which is the official AWS SDK mechanism for this.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>