(
input: CreateConnectionInput,
)
| 3740 | // ------------------------------------------------------------------ |
| 3741 | |
| 3742 | const connectionsCreate = ( |
| 3743 | input: CreateConnectionInput, |
| 3744 | ): Effect.Effect< |
| 3745 | Connection, |
| 3746 | | IntegrationNotFoundError |
| 3747 | | ConnectionAlreadyExistsError |
| 3748 | | CredentialProviderNotRegisteredError |
| 3749 | | InvalidConnectionInputError |
| 3750 | | OrgWriteDeniedError |
| 3751 | | StorageFailure |
| 3752 | > => |
| 3753 | Effect.gen(function* () { |
| 3754 | yield* guardOrgWrite(input.owner); |
| 3755 | const name = connectionIdentifier(String(input.name)); |
| 3756 | // Typed (not StorageError) so the HTTP edge can answer 400 with the |
| 3757 | // reason instead of an opaque 500 — callers can act on it. |
| 3758 | if (input.owner === "user" && subject == null) { |
| 3759 | return yield* new InvalidConnectionInputError({ |
| 3760 | message: |
| 3761 | 'Cannot create a personal connection: this context has no user subject. Create it with owner "org", or connect as a signed-in user.', |
| 3762 | }); |
| 3763 | } |
| 3764 | const integrationRow = yield* findIntegrationRow(input.integration); |
| 3765 | if (!integrationRow) { |
| 3766 | return yield* new IntegrationNotFoundError({ |
| 3767 | slug: input.integration, |
| 3768 | }); |
| 3769 | } |
| 3770 | |
| 3771 | // Create is never a replace. This early check answers the common case |
| 3772 | // with a typed 409 before any other work, but it is NOT the guard |
| 3773 | // against concurrent creates — the row insert below is: the |
| 3774 | // transaction re-checks, the primary key breaks the tie, and the |
| 3775 | // provider write happens only after the insert wins. |
| 3776 | const duplicate = yield* findConnectionRow({ |
| 3777 | owner: input.owner, |
| 3778 | integration: input.integration, |
| 3779 | name, |
| 3780 | }); |
| 3781 | let retryingRowId: string | null = null; |
| 3782 | let retryingItemIds: readonly string[] = []; |
| 3783 | if (duplicate) { |
| 3784 | const duplicateItemIds = Object.values(connectionItemIds(duplicate)); |
| 3785 | const duplicateProvider = credentialProviders.get(duplicate.provider); |
| 3786 | const duplicateAttempt = parseCredentialWriteAttempt(duplicate.credential_write); |
| 3787 | const belongsToCrashedRuntime = |
| 3788 | duplicateItemIds.length > 0 && |
| 3789 | duplicateAttempt !== null && |
| 3790 | duplicateAttempt.runtimeId !== credentialWriteRuntimeId; |
| 3791 | const hasMissingCredential = |
| 3792 | belongsToCrashedRuntime && duplicateProvider?.set |
| 3793 | ? (yield* Effect.forEach(duplicateItemIds, (itemId) => |
| 3794 | duplicateProvider |
| 3795 | .get(ProviderItemId.make(itemId)) |
| 3796 | .pipe(Effect.map((value) => value === null)), |
| 3797 | )).some(Boolean) |
| 3798 | : false; |
| 3799 | retryingRowId = hasMissingCredential ? storageRowId(duplicate) : null; |
no test coverage detected