From 137eb8519a8e90c8beccebd2ba1c1f47d0f99bd5 Mon Sep 17 00:00:00 2001 From: Dan Dorman Date: Tue, 22 Sep 2026 11:53:31 -0600 Subject: [PATCH 1/5] feat(pipes)!: Expand connected account support Shared and multiple provider connections need request and response contracts that stay consistent end to end. Keep SDK-side corrections while the generator and source-schema fixes are handled upstream. BREAKING CHANGE: Credential responses now use discriminated unions. Narrow on active and authMethod before reading variant-specific fields. --- .oagen-manifest.json | 86 +++- src/pipes/fixtures/api-key-installation.json | 5 + .../fixtures/connected-account-input.json | 7 + src/pipes/fixtures/connected-account.json | 8 + .../fixtures/create-data-integration.json | 10 + ...ata-integration-access-token-response.json | 3 +- .../data-integration-credentials-input.json | 5 + ...data-integration-credentials-response.json | 3 +- .../data-integration-installation.json | 9 + src/pipes/fixtures/data-integration.json | 6 + ...ata-integration-authorize-url-request.json | 6 +- ...a-integrations-get-user-token-request.json | 5 +- ...-list-response-data-connected-account.json | 8 + .../data-integrations-list-response-data.json | 34 +- .../data-integrations-list-response.json | 34 +- ...a-integrations-upsert-api-key-request.json | 2 + ...ons-upsert-client-credentials-request.json | 11 + ...integrations-vend-credentials-request.json | 5 +- src/pipes/fixtures/list-data-integration.json | 6 + .../organization-connected-account.json | 8 + .../fixtures/update-data-integration.json | 5 + .../api-key-installation.interface.ts | 16 + ...rize-data-integration-options.interface.ts | 10 +- ...connected-account-auth-method.interface.ts | 1 + ...ected-account-connection-role.interface.ts | 9 + .../connected-account-dto.interface.ts | 6 +- ...connected-account-input-state.interface.ts | 9 + .../connected-account-input.interface.ts | 24 + .../connected-account-state.interface.ts | 1 + .../interfaces/connected-account.interface.ts | 21 +- ...data-integration-auth-methods.interface.ts | 10 + ...ntegration-credential-options.interface.ts | 8 +- ...eate-data-integration-options.interface.ts | 18 +- ...te-data-integration-ownership.interface.ts | 9 + .../create-data-integration.interface.ts | 30 +- ...ion-connected-account-options.interface.ts | 22 + ...ser-connected-account-options.interface.ts | 4 +- .../custom-provider-definition.interface.ts | 14 +- ...data-integration-auth-methods.interface.ts | 10 + ...ration-credentials-input-type.interface.ts | 9 + ...integration-credentials-input.interface.ts | 18 + ...edentials-response-credential.interface.ts | 74 ++- ...egration-credentials-response.interface.ts | 29 +- ...-installation-connection-role.interface.ts | 9 + ...data-integration-installation.interface.ts | 30 ++ .../data-integration-ownership.interface.ts | 9 + .../interfaces/data-integration.interface.ts | 26 +- ...-url-request-connection-owner.interface.ts | 10 + ...gration-authorize-url-request.interface.ts | 12 +- ...oken-request-connection-owner.interface.ts | 9 + ...ations-get-user-token-request.interface.ts | 15 +- ...st-response-data-auth-methods.interface.ts | 1 + ...connected-account-auth-method.interface.ts | 1 + ...ected-account-connection-role.interface.ts | 9 + ...sponse-data-connected-account.interface.ts | 21 +- ...esponse-data-connection-owner.interface.ts | 9 + ...tegrations-list-response-data.interface.ts | 16 +- ...ta-integrations-list-response.interface.ts | 2 +- ...-key-request-connection-owner.interface.ts | 9 + ...ations-upsert-api-key-request.interface.ts | 10 +- ...ials-request-connection-owner.interface.ts | 9 + ...rt-client-credentials-request.interface.ts | 30 ++ ...ials-request-connection-owner.interface.ts | 9 + ...ions-vend-credentials-request.interface.ts | 15 +- ...ion-connected-account-options.interface.ts | 12 + ...tion-data-integration-options.interface.ts | 6 + ...ser-connected-account-options.interface.ts | 4 + .../get-access-token-options.interface.ts | 8 +- ...ion-connected-account-options.interface.ts | 12 + ...tion-data-integration-options.interface.ts | 6 + ...ser-connected-account-options.interface.ts | 4 + src/pipes/interfaces/index.ts | 33 ++ ...ist-data-integrations-options.interface.ts | 7 +- ...zation-data-providers-options.interface.ts | 8 + ...t-user-data-providers-options.interface.ts | 2 + ...ation-connected-account-state.interface.ts | 9 + ...rganization-connected-account.interface.ts | 27 + .../interfaces/pipes-ownership.interface.ts | 9 + ...te-custom-provider-definition.interface.ts | 6 +- ...a-integration-api-key-options.interface.ts | 10 +- ...on-client-credentials-options.interface.ts | 8 + ...date-data-integration-options.interface.ts | 15 +- .../update-data-integration.interface.ts | 19 +- ...ion-connected-account-options.interface.ts | 26 + ...tion-data-integration-options.interface.ts | 6 + ...ser-connected-account-options.interface.ts | 8 +- src/pipes/pipes-requests.spec.ts | 448 +++++++++++++++++ src/pipes/pipes.spec.ts | 202 +++++++- src/pipes/pipes.ts | 461 ++++++++++++++++-- .../api-key-installation.serializer.ts | 14 + .../connected-account-input.serializer.ts | 17 + .../connected-account.serializer.ts | 6 + .../create-data-integration.serializer.ts | 12 +- ...ntegration-credentials-input.serializer.ts | 14 + ...dentials-response-credential.serializer.ts | 37 +- ...gration-credentials-response.serializer.ts | 28 +- ...ata-integration-installation.serializer.ts | 18 + .../data-integration.serializer.ts | 13 +- ...ration-authorize-url-request.serializer.ts | 2 + ...tions-get-user-token-request.serializer.ts | 3 + ...ponse-data-connected-account.serializer.ts | 6 + ...egrations-list-response-data.serializer.ts | 7 + ...tions-upsert-api-key-request.serializer.ts | 2 + ...t-client-credentials-request.serializer.ts | 18 + ...ons-vend-credentials-request.serializer.ts | 3 + src/pipes/serializers/index.ts | 6 + ...ganization-connected-account.serializer.ts | 18 + .../update-data-integration.serializer.ts | 9 +- 108 files changed, 2247 insertions(+), 221 deletions(-) create mode 100644 src/pipes/fixtures/api-key-installation.json create mode 100644 src/pipes/fixtures/connected-account-input.json create mode 100644 src/pipes/fixtures/data-integration-credentials-input.json create mode 100644 src/pipes/fixtures/data-integration-installation.json create mode 100644 src/pipes/fixtures/data-integrations-upsert-client-credentials-request.json create mode 100644 src/pipes/fixtures/organization-connected-account.json create mode 100644 src/pipes/interfaces/api-key-installation.interface.ts create mode 100644 src/pipes/interfaces/connected-account-connection-role.interface.ts create mode 100644 src/pipes/interfaces/connected-account-input-state.interface.ts create mode 100644 src/pipes/interfaces/connected-account-input.interface.ts create mode 100644 src/pipes/interfaces/create-data-integration-auth-methods.interface.ts create mode 100644 src/pipes/interfaces/create-data-integration-ownership.interface.ts create mode 100644 src/pipes/interfaces/create-organization-connected-account-options.interface.ts create mode 100644 src/pipes/interfaces/data-integration-auth-methods.interface.ts create mode 100644 src/pipes/interfaces/data-integration-credentials-input-type.interface.ts create mode 100644 src/pipes/interfaces/data-integration-credentials-input.interface.ts create mode 100644 src/pipes/interfaces/data-integration-installation-connection-role.interface.ts create mode 100644 src/pipes/interfaces/data-integration-installation.interface.ts create mode 100644 src/pipes/interfaces/data-integration-ownership.interface.ts create mode 100644 src/pipes/interfaces/data-integrations-get-data-integration-authorize-url-request-connection-owner.interface.ts create mode 100644 src/pipes/interfaces/data-integrations-get-user-token-request-connection-owner.interface.ts create mode 100644 src/pipes/interfaces/data-integrations-list-response-data-connected-account-connection-role.interface.ts create mode 100644 src/pipes/interfaces/data-integrations-list-response-data-connection-owner.interface.ts create mode 100644 src/pipes/interfaces/data-integrations-upsert-api-key-request-connection-owner.interface.ts create mode 100644 src/pipes/interfaces/data-integrations-upsert-client-credentials-request-connection-owner.interface.ts create mode 100644 src/pipes/interfaces/data-integrations-upsert-client-credentials-request.interface.ts create mode 100644 src/pipes/interfaces/data-integrations-vend-credentials-request-connection-owner.interface.ts create mode 100644 src/pipes/interfaces/delete-organization-connected-account-options.interface.ts create mode 100644 src/pipes/interfaces/delete-organization-data-integration-options.interface.ts create mode 100644 src/pipes/interfaces/get-organization-connected-account-options.interface.ts create mode 100644 src/pipes/interfaces/get-organization-data-integration-options.interface.ts create mode 100644 src/pipes/interfaces/list-organization-data-providers-options.interface.ts create mode 100644 src/pipes/interfaces/organization-connected-account-state.interface.ts create mode 100644 src/pipes/interfaces/organization-connected-account.interface.ts create mode 100644 src/pipes/interfaces/pipes-ownership.interface.ts create mode 100644 src/pipes/interfaces/update-data-integration-client-credentials-options.interface.ts create mode 100644 src/pipes/interfaces/update-organization-connected-account-options.interface.ts create mode 100644 src/pipes/interfaces/update-organization-data-integration-options.interface.ts create mode 100644 src/pipes/pipes-requests.spec.ts create mode 100644 src/pipes/serializers/api-key-installation.serializer.ts create mode 100644 src/pipes/serializers/connected-account-input.serializer.ts create mode 100644 src/pipes/serializers/data-integration-credentials-input.serializer.ts create mode 100644 src/pipes/serializers/data-integration-installation.serializer.ts create mode 100644 src/pipes/serializers/data-integrations-upsert-client-credentials-request.serializer.ts create mode 100644 src/pipes/serializers/organization-connected-account.serializer.ts diff --git a/.oagen-manifest.json b/.oagen-manifest.json index f4f865519..0842af7b1 100644 --- a/.oagen-manifest.json +++ b/.oagen-manifest.json @@ -1,7 +1,6 @@ { "version": 2, "language": "node", - "generatedAt": "2026-06-16T18:02:10.415Z", "files": [ "src/api-keys/interfaces/create-validation-options.interface.ts", "src/api-keys/interfaces/delete-api-key-options.interface.ts", @@ -82,47 +81,132 @@ "src/groups/serializers/index.ts", "src/groups/serializers/update-group.serializer.ts", "src/groups/serializers/user-organization-membership-base-list-data.serializer.ts", + "src/pipes/fixtures/api-key-installation.json", + "src/pipes/fixtures/connected-account-input.json", "src/pipes/fixtures/connected-account.json", + "src/pipes/fixtures/create-data-integration.json", + "src/pipes/fixtures/custom-provider-definition.json", "src/pipes/fixtures/data-integration-access-token-response-access-token.json", "src/pipes/fixtures/data-integration-access-token-response.json", "src/pipes/fixtures/data-integration-authorize-url-response.json", + "src/pipes/fixtures/data-integration-credential.json", + "src/pipes/fixtures/data-integration-credentials-input.json", + "src/pipes/fixtures/data-integration-credentials-response-credential.json", + "src/pipes/fixtures/data-integration-credentials-response.json", + "src/pipes/fixtures/data-integration-custom-provider.json", + "src/pipes/fixtures/data-integration-installation.json", + "src/pipes/fixtures/data-integration.json", "src/pipes/fixtures/data-integrations-get-data-integration-authorize-url-request.json", "src/pipes/fixtures/data-integrations-get-user-token-request.json", "src/pipes/fixtures/data-integrations-list-response-data-connected-account.json", "src/pipes/fixtures/data-integrations-list-response-data.json", "src/pipes/fixtures/data-integrations-list-response.json", + "src/pipes/fixtures/data-integrations-upsert-api-key-request.json", + "src/pipes/fixtures/data-integrations-upsert-client-credentials-request.json", + "src/pipes/fixtures/data-integrations-vend-credentials-request.json", + "src/pipes/fixtures/list-data-integration.json", + "src/pipes/fixtures/organization-connected-account.json", + "src/pipes/fixtures/update-custom-provider-definition.json", + "src/pipes/fixtures/update-data-integration.json", + "src/pipes/interfaces/api-key-installation.interface.ts", "src/pipes/interfaces/authorize-data-integration-options.interface.ts", + "src/pipes/interfaces/connected-account-auth-method.interface.ts", + "src/pipes/interfaces/connected-account-connection-role.interface.ts", + "src/pipes/interfaces/connected-account-input-state.interface.ts", + "src/pipes/interfaces/connected-account-input.interface.ts", "src/pipes/interfaces/connected-account-state.interface.ts", "src/pipes/interfaces/connected-account.interface.ts", + "src/pipes/interfaces/create-data-integration-auth-methods.interface.ts", + "src/pipes/interfaces/create-data-integration-ownership.interface.ts", "src/pipes/interfaces/create-data-integration-token-options.interface.ts", + "src/pipes/interfaces/create-data-integration.interface.ts", + "src/pipes/interfaces/create-organization-connected-account-options.interface.ts", + "src/pipes/interfaces/custom-provider-definition-authenticate-via.interface.ts", + "src/pipes/interfaces/custom-provider-definition.interface.ts", "src/pipes/interfaces/data-integration-access-token-response-access-token.interface.ts", "src/pipes/interfaces/data-integration-access-token-response-error.interface.ts", "src/pipes/interfaces/data-integration-access-token-response.interface.ts", + "src/pipes/interfaces/data-integration-auth-methods.interface.ts", "src/pipes/interfaces/data-integration-authorize-url-response.interface.ts", + "src/pipes/interfaces/data-integration-credential-type.interface.ts", + "src/pipes/interfaces/data-integration-credential.interface.ts", + "src/pipes/interfaces/data-integration-credentials-input-type.interface.ts", + "src/pipes/interfaces/data-integration-credentials-input.interface.ts", + "src/pipes/interfaces/data-integration-credentials-response-credential.interface.ts", + "src/pipes/interfaces/data-integration-credentials-response-error.interface.ts", + "src/pipes/interfaces/data-integration-credentials-response.interface.ts", + "src/pipes/interfaces/data-integration-custom-provider-authenticate-via.interface.ts", + "src/pipes/interfaces/data-integration-custom-provider.interface.ts", + "src/pipes/interfaces/data-integration-installation-connection-role.interface.ts", + "src/pipes/interfaces/data-integration-installation.interface.ts", + "src/pipes/interfaces/data-integration-ownership.interface.ts", + "src/pipes/interfaces/data-integration-state.interface.ts", + "src/pipes/interfaces/data-integration.interface.ts", + "src/pipes/interfaces/data-integrations-get-data-integration-authorize-url-request-connection-owner.interface.ts", "src/pipes/interfaces/data-integrations-get-data-integration-authorize-url-request.interface.ts", + "src/pipes/interfaces/data-integrations-get-user-token-request-connection-owner.interface.ts", "src/pipes/interfaces/data-integrations-get-user-token-request.interface.ts", + "src/pipes/interfaces/data-integrations-list-response-data-auth-methods.interface.ts", + "src/pipes/interfaces/data-integrations-list-response-data-connected-account-auth-method.interface.ts", + "src/pipes/interfaces/data-integrations-list-response-data-connected-account-connection-role.interface.ts", "src/pipes/interfaces/data-integrations-list-response-data-connected-account-state.interface.ts", "src/pipes/interfaces/data-integrations-list-response-data-connected-account.interface.ts", + "src/pipes/interfaces/data-integrations-list-response-data-connection-owner.interface.ts", "src/pipes/interfaces/data-integrations-list-response-data-ownership.interface.ts", "src/pipes/interfaces/data-integrations-list-response-data.interface.ts", "src/pipes/interfaces/data-integrations-list-response.interface.ts", + "src/pipes/interfaces/data-integrations-upsert-api-key-request-connection-owner.interface.ts", + "src/pipes/interfaces/data-integrations-upsert-api-key-request.interface.ts", + "src/pipes/interfaces/data-integrations-upsert-client-credentials-request-connection-owner.interface.ts", + "src/pipes/interfaces/data-integrations-upsert-client-credentials-request.interface.ts", + "src/pipes/interfaces/data-integrations-vend-credentials-request-connection-owner.interface.ts", + "src/pipes/interfaces/data-integrations-vend-credentials-request.interface.ts", + "src/pipes/interfaces/delete-organization-connected-account-options.interface.ts", + "src/pipes/interfaces/delete-organization-data-integration-options.interface.ts", "src/pipes/interfaces/delete-user-connected-account-options.interface.ts", + "src/pipes/interfaces/get-organization-connected-account-options.interface.ts", + "src/pipes/interfaces/get-organization-data-integration-options.interface.ts", "src/pipes/interfaces/get-user-connected-account-options.interface.ts", "src/pipes/interfaces/index.ts", + "src/pipes/interfaces/list-organization-data-providers-options.interface.ts", "src/pipes/interfaces/list-user-data-providers-options.interface.ts", + "src/pipes/interfaces/organization-connected-account-state.interface.ts", + "src/pipes/interfaces/organization-connected-account.interface.ts", + "src/pipes/interfaces/pipes-ownership.interface.ts", + "src/pipes/interfaces/update-custom-provider-definition-authenticate-via.interface.ts", + "src/pipes/interfaces/update-custom-provider-definition.interface.ts", + "src/pipes/interfaces/update-data-integration-client-credentials-options.interface.ts", + "src/pipes/interfaces/update-data-integration.interface.ts", + "src/pipes/interfaces/update-organization-connected-account-options.interface.ts", + "src/pipes/interfaces/update-organization-data-integration-options.interface.ts", "src/pipes/pipes.spec.ts", "src/pipes/pipes.ts", "src/pipes/serializers.spec.ts", + "src/pipes/serializers/api-key-installation.serializer.ts", + "src/pipes/serializers/connected-account-input.serializer.ts", "src/pipes/serializers/connected-account.serializer.ts", + "src/pipes/serializers/custom-provider-definition.serializer.ts", "src/pipes/serializers/data-integration-access-token-response-access-token.serializer.ts", "src/pipes/serializers/data-integration-access-token-response.serializer.ts", "src/pipes/serializers/data-integration-authorize-url-response.serializer.ts", + "src/pipes/serializers/data-integration-credential.serializer.ts", + "src/pipes/serializers/data-integration-credentials-input.serializer.ts", + "src/pipes/serializers/data-integration-credentials-response-credential.serializer.ts", + "src/pipes/serializers/data-integration-credentials-response.serializer.ts", + "src/pipes/serializers/data-integration-custom-provider.serializer.ts", + "src/pipes/serializers/data-integration-installation.serializer.ts", + "src/pipes/serializers/data-integration.serializer.ts", "src/pipes/serializers/data-integrations-get-data-integration-authorize-url-request.serializer.ts", "src/pipes/serializers/data-integrations-get-user-token-request.serializer.ts", "src/pipes/serializers/data-integrations-list-response-data-connected-account.serializer.ts", "src/pipes/serializers/data-integrations-list-response-data.serializer.ts", "src/pipes/serializers/data-integrations-list-response.serializer.ts", + "src/pipes/serializers/data-integrations-upsert-api-key-request.serializer.ts", + "src/pipes/serializers/data-integrations-upsert-client-credentials-request.serializer.ts", + "src/pipes/serializers/data-integrations-vend-credentials-request.serializer.ts", "src/pipes/serializers/index.ts", + "src/pipes/serializers/organization-connected-account.serializer.ts", + "src/pipes/serializers/update-custom-provider-definition.serializer.ts", "src/radar/fixtures/radar-list-entry-already-present-response.json", "src/radar/fixtures/radar-standalone-assess-request.json", "src/radar/fixtures/radar-standalone-delete-radar-list-entry-request.json", diff --git a/src/pipes/fixtures/api-key-installation.json b/src/pipes/fixtures/api-key-installation.json new file mode 100644 index 000000000..3506192b0 --- /dev/null +++ b/src/pipes/fixtures/api-key-installation.json @@ -0,0 +1,5 @@ +{ + "secret": "sk-1234567890abcdef", + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT" +} diff --git a/src/pipes/fixtures/connected-account-input.json b/src/pipes/fixtures/connected-account-input.json new file mode 100644 index 000000000..b63b73cd8 --- /dev/null +++ b/src/pipes/fixtures/connected-account-input.json @@ -0,0 +1,7 @@ +{ + "access_token": "gho_16C7e42F292c6912E7710c838347Ae178B4a", + "refresh_token": "ghr_xxxxxxxxxxxxxxxxxxxx", + "expires_at": "2025-12-31T23:59:59.000Z", + "scopes": ["repo", "user:email"], + "state": "connected" +} diff --git a/src/pipes/fixtures/connected-account.json b/src/pipes/fixtures/connected-account.json index 9bb04f217..29c49bf1a 100644 --- a/src/pipes/fixtures/connected-account.json +++ b/src/pipes/fixtures/connected-account.json @@ -1,11 +1,19 @@ { "object": "connected_account", "id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_role": "compatibility", + "account_identifier": "workspace_123", + "account_display_name": "Acme production", "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", "organization_id": null, "scopes": ["repo", "user:email"], "auth_method": "oauth", "api_key_last_4": null, + "client_id": "3MVG9dZJodJWxft2VoStSCVwPFsx0eDcpVc", + "client_secret_last_4": "cdef", + "config": { + "instance_url": "https://example.my.salesforce.com" + }, "state": "connected", "created_at": "2024-01-16T14:20:00.000Z", "updated_at": "2024-01-16T14:20:00.000Z" diff --git a/src/pipes/fixtures/create-data-integration.json b/src/pipes/fixtures/create-data-integration.json index 0b2ef4f3f..403094c3b 100644 --- a/src/pipes/fixtures/create-data-integration.json +++ b/src/pipes/fixtures/create-data-integration.json @@ -1,13 +1,23 @@ { "provider": "github", + "ownership": "user", "description": "Production GitHub app", "enabled": true, "scopes": ["repo", "read:org"], + "auth_methods": ["oauth"], + "config": { + "account": "myorg-myaccount" + }, "credentials": { "type": "custom", "client_id": "Iv1.abc123", "client_secret": "secret_…" }, + "api_key": { + "secret": "sk-1234567890abcdef", + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT" + }, "custom_provider": { "name": "My OAuth App", "authorization_url": "https://provider.example.com/oauth/authorize", diff --git a/src/pipes/fixtures/data-integration-access-token-response.json b/src/pipes/fixtures/data-integration-access-token-response.json index 14f59204b..2b0c3da0c 100644 --- a/src/pipes/fixtures/data-integration-access-token-response.json +++ b/src/pipes/fixtures/data-integration-access-token-response.json @@ -6,6 +6,5 @@ "expires_at": "2025-12-31T23:59:59.000Z", "scopes": ["repo", "user:email"], "missing_scopes": [] - }, - "error": "not_installed" + } } diff --git a/src/pipes/fixtures/data-integration-credentials-input.json b/src/pipes/fixtures/data-integration-credentials-input.json new file mode 100644 index 000000000..e50987654 --- /dev/null +++ b/src/pipes/fixtures/data-integration-credentials-input.json @@ -0,0 +1,5 @@ +{ + "type": "custom", + "client_id": "Iv1.abc123", + "client_secret": "secret_…" +} diff --git a/src/pipes/fixtures/data-integration-credentials-response.json b/src/pipes/fixtures/data-integration-credentials-response.json index ab0ad7979..38c5d8b52 100644 --- a/src/pipes/fixtures/data-integration-credentials-response.json +++ b/src/pipes/fixtures/data-integration-credentials-response.json @@ -7,6 +7,5 @@ "expires_at": "2025-12-31T23:59:59.000Z", "scopes": ["repo", "user:email"], "missing_scopes": [] - }, - "error": "not_installed" + } } diff --git a/src/pipes/fixtures/data-integration-installation.json b/src/pipes/fixtures/data-integration-installation.json new file mode 100644 index 000000000..820a93446 --- /dev/null +++ b/src/pipes/fixtures/data-integration-installation.json @@ -0,0 +1,9 @@ +{ + "id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_role": "compatibility", + "account_identifier": "workspace_123", + "account_display_name": "Acme production", + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": null, + "api_key_last_4": "cdef" +} diff --git a/src/pipes/fixtures/data-integration.json b/src/pipes/fixtures/data-integration.json index 84883e642..7d147608d 100644 --- a/src/pipes/fixtures/data-integration.json +++ b/src/pipes/fixtures/data-integration.json @@ -3,16 +3,22 @@ "id": "data_integration_01EHZNVPK3SFK441A1RGBFSHRT", "slug": "github", "integration_type": "github", + "ownership": "user", "description": "Production GitHub app", "enabled": true, "state": "valid", "scopes": ["repo", "read:org"], "redirect_uri": "https://api.workos.com/data-integrations/github/dik_01EHZNVPK3SFK441A1RGBFSHRT/callback", + "auth_methods": ["oauth"], "credentials": { "type": "custom", "client_id": "Iv1.abc123", "redacted_client_secret": "6789" }, + "installation": null, + "config": { + "account": "myorg-myaccount" + }, "custom_provider": { "name": "My OAuth App", "authorization_url": "https://provider.example.com/oauth/authorize", diff --git a/src/pipes/fixtures/data-integrations-get-data-integration-authorize-url-request.json b/src/pipes/fixtures/data-integrations-get-data-integration-authorize-url-request.json index b38a7cd52..4a7ea84c3 100644 --- a/src/pipes/fixtures/data-integrations-get-data-integration-authorize-url-request.json +++ b/src/pipes/fixtures/data-integrations-get-data-integration-authorize-url-request.json @@ -1,5 +1,9 @@ { "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", - "return_to": "https://example.com/callback" + "connection_owner": "user", + "return_to": "https://example.com/callback", + "config": { + "subdomain": "acme" + } } diff --git a/src/pipes/fixtures/data-integrations-get-user-token-request.json b/src/pipes/fixtures/data-integrations-get-user-token-request.json index ef8c44380..9b96a12f0 100644 --- a/src/pipes/fixtures/data-integrations-get-user-token-request.json +++ b/src/pipes/fixtures/data-integrations-get-user-token-request.json @@ -1,4 +1,7 @@ { "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", - "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT" + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "connected_account_id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_owner": "user", + "supports_multiple_connections": true } diff --git a/src/pipes/fixtures/data-integrations-list-response-data-connected-account.json b/src/pipes/fixtures/data-integrations-list-response-data-connected-account.json index 997f762c3..1103670fe 100644 --- a/src/pipes/fixtures/data-integrations-list-response-data-connected-account.json +++ b/src/pipes/fixtures/data-integrations-list-response-data-connected-account.json @@ -1,11 +1,19 @@ { "object": "connected_account", "id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_role": "compatibility", + "account_identifier": "workspace_123", + "account_display_name": "Acme production", "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", "organization_id": "test_organizationId", "scopes": ["repo", "user:email"], "auth_method": "oauth", "api_key_last_4": null, + "client_id": "3MVG9dZJodJWxft2VoStSCVwPFsx0eDcpVc", + "client_secret_last_4": "cdef", + "config": { + "instance_url": "https://example.my.salesforce.com" + }, "state": "connected", "created_at": "test_createdAt", "updated_at": "test_updatedAt", diff --git a/src/pipes/fixtures/data-integrations-list-response-data.json b/src/pipes/fixtures/data-integrations-list-response-data.json index 6e3d95f66..c3f83c315 100644 --- a/src/pipes/fixtures/data-integrations-list-response-data.json +++ b/src/pipes/fixtures/data-integrations-list-response-data.json @@ -8,20 +8,52 @@ "credentials_type": "test_credentialsType", "scopes": ["repo", "user:email"], "auth_methods": ["oauth"], + "connection_owner": "user", "ownership": "userland_user", "created_at": "test_createdAt", "updated_at": "test_updatedAt", "connected_account": { "object": "connected_account", "id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_role": "compatibility", + "account_identifier": "workspace_123", + "account_display_name": "Acme production", "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", "organization_id": "test_organizationId", "scopes": ["repo", "user:email"], "auth_method": "oauth", "api_key_last_4": null, + "client_id": "3MVG9dZJodJWxft2VoStSCVwPFsx0eDcpVc", + "client_secret_last_4": "cdef", + "config": { + "instance_url": "https://example.my.salesforce.com" + }, "state": "connected", "created_at": "test_createdAt", "updated_at": "test_updatedAt", "userland_user_id": "test_userlandUserId" - } + }, + "connected_accounts": [ + { + "object": "connected_account", + "id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_role": "compatibility", + "account_identifier": "workspace_123", + "account_display_name": "Acme production", + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "test_organizationId", + "scopes": ["repo", "user:email"], + "auth_method": "oauth", + "api_key_last_4": null, + "client_id": "3MVG9dZJodJWxft2VoStSCVwPFsx0eDcpVc", + "client_secret_last_4": "cdef", + "config": { + "instance_url": "https://example.my.salesforce.com" + }, + "state": "connected", + "created_at": "test_createdAt", + "updated_at": "test_updatedAt", + "userland_user_id": "test_userlandUserId" + } + ] } diff --git a/src/pipes/fixtures/data-integrations-list-response.json b/src/pipes/fixtures/data-integrations-list-response.json index 1b98060ac..c69556a35 100644 --- a/src/pipes/fixtures/data-integrations-list-response.json +++ b/src/pipes/fixtures/data-integrations-list-response.json @@ -11,22 +11,54 @@ "credentials_type": "test_credentialsType", "scopes": ["repo", "user:email"], "auth_methods": ["oauth"], + "connection_owner": "user", "ownership": "userland_user", "created_at": "test_createdAt", "updated_at": "test_updatedAt", "connected_account": { "object": "connected_account", "id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_role": "compatibility", + "account_identifier": "workspace_123", + "account_display_name": "Acme production", "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", "organization_id": "test_organizationId", "scopes": ["repo", "user:email"], "auth_method": "oauth", "api_key_last_4": null, + "client_id": "3MVG9dZJodJWxft2VoStSCVwPFsx0eDcpVc", + "client_secret_last_4": "cdef", + "config": { + "instance_url": "https://example.my.salesforce.com" + }, "state": "connected", "created_at": "test_createdAt", "updated_at": "test_updatedAt", "userland_user_id": "test_userlandUserId" - } + }, + "connected_accounts": [ + { + "object": "connected_account", + "id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_role": "compatibility", + "account_identifier": "workspace_123", + "account_display_name": "Acme production", + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "test_organizationId", + "scopes": ["repo", "user:email"], + "auth_method": "oauth", + "api_key_last_4": null, + "client_id": "3MVG9dZJodJWxft2VoStSCVwPFsx0eDcpVc", + "client_secret_last_4": "cdef", + "config": { + "instance_url": "https://example.my.salesforce.com" + }, + "state": "connected", + "created_at": "test_createdAt", + "updated_at": "test_updatedAt", + "userland_user_id": "test_userlandUserId" + } + ] } ] } diff --git a/src/pipes/fixtures/data-integrations-upsert-api-key-request.json b/src/pipes/fixtures/data-integrations-upsert-api-key-request.json index bf0bd81be..45952523c 100644 --- a/src/pipes/fixtures/data-integrations-upsert-api-key-request.json +++ b/src/pipes/fixtures/data-integrations-upsert-api-key-request.json @@ -1,5 +1,7 @@ { "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "connected_account_id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_owner": "user", "secret": "sk-1234567890abcdef" } diff --git a/src/pipes/fixtures/data-integrations-upsert-client-credentials-request.json b/src/pipes/fixtures/data-integrations-upsert-client-credentials-request.json new file mode 100644 index 000000000..9497d49c2 --- /dev/null +++ b/src/pipes/fixtures/data-integrations-upsert-client-credentials-request.json @@ -0,0 +1,11 @@ +{ + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "connected_account_id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_owner": "user", + "client_id": "3MVG9...", + "client_secret": "shhh-secret", + "config": { + "salesforce_host": "acme.my.salesforce.com" + } +} diff --git a/src/pipes/fixtures/data-integrations-vend-credentials-request.json b/src/pipes/fixtures/data-integrations-vend-credentials-request.json index ef8c44380..9b96a12f0 100644 --- a/src/pipes/fixtures/data-integrations-vend-credentials-request.json +++ b/src/pipes/fixtures/data-integrations-vend-credentials-request.json @@ -1,4 +1,7 @@ { "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", - "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT" + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "connected_account_id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_owner": "user", + "supports_multiple_connections": true } diff --git a/src/pipes/fixtures/list-data-integration.json b/src/pipes/fixtures/list-data-integration.json index 8668f7d1d..f370a98f1 100644 --- a/src/pipes/fixtures/list-data-integration.json +++ b/src/pipes/fixtures/list-data-integration.json @@ -5,16 +5,22 @@ "id": "data_integration_01EHZNVPK3SFK441A1RGBFSHRT", "slug": "github", "integration_type": "github", + "ownership": "user", "description": "Production GitHub app", "enabled": true, "state": "valid", "scopes": ["repo", "read:org"], "redirect_uri": "https://api.workos.com/data-integrations/github/dik_01EHZNVPK3SFK441A1RGBFSHRT/callback", + "auth_methods": ["oauth"], "credentials": { "type": "custom", "client_id": "Iv1.abc123", "redacted_client_secret": "6789" }, + "installation": null, + "config": { + "account": "myorg-myaccount" + }, "custom_provider": { "name": "My OAuth App", "authorization_url": "https://provider.example.com/oauth/authorize", diff --git a/src/pipes/fixtures/organization-connected-account.json b/src/pipes/fixtures/organization-connected-account.json new file mode 100644 index 000000000..a4191bc8c --- /dev/null +++ b/src/pipes/fixtures/organization-connected-account.json @@ -0,0 +1,8 @@ +{ + "access_token": "gho_16C7e42F292c6912E7710c838347Ae178B4a", + "refresh_token": "ghr_xxxxxxxxxxxxxxxxxxxx", + "expires_at": "2025-12-31T23:59:59.000Z", + "scopes": ["repo", "user:email"], + "state": "connected", + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT" +} diff --git a/src/pipes/fixtures/update-data-integration.json b/src/pipes/fixtures/update-data-integration.json index 260c76ac3..a8311b9b5 100644 --- a/src/pipes/fixtures/update-data-integration.json +++ b/src/pipes/fixtures/update-data-integration.json @@ -7,6 +7,11 @@ "client_id": "Iv1.abc123", "client_secret": "secret_…" }, + "api_key": { + "secret": "sk-1234567890abcdef", + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT" + }, "custom_provider": { "name": "My OAuth App", "authorization_url": "https://provider.example.com/oauth/authorize", diff --git a/src/pipes/interfaces/api-key-installation.interface.ts b/src/pipes/interfaces/api-key-installation.interface.ts new file mode 100644 index 000000000..2bf46687f --- /dev/null +++ b/src/pipes/interfaces/api-key-installation.interface.ts @@ -0,0 +1,16 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface ApiKeyInstallation { + /** The API key secret to store for the tenant. */ + secret: string; + /** The User identifier the API key is installed for. */ + userId: string; + /** An Organization identifier to scope the installation to a specific organization. */ + organizationId?: string; +} + +export interface ApiKeyInstallationResponse { + secret: string; + user_id: string; + organization_id?: string; +} diff --git a/src/pipes/interfaces/authorize-data-integration-options.interface.ts b/src/pipes/interfaces/authorize-data-integration-options.interface.ts index 53305f767..f6261bbf4 100644 --- a/src/pipes/interfaces/authorize-data-integration-options.interface.ts +++ b/src/pipes/interfaces/authorize-data-integration-options.interface.ts @@ -1,12 +1,8 @@ // This file is auto-generated by oagen. Do not edit. -export interface AuthorizeDataIntegrationOptions { +import type { DataIntegrationsGetDataIntegrationAuthorizeUrlRequest } from './data-integrations-get-data-integration-authorize-url-request.interface'; + +export interface AuthorizeDataIntegrationOptions extends DataIntegrationsGetDataIntegrationAuthorizeUrlRequest { /** The slug identifier of the provider (e.g., `github`, `slack`, `notion`). */ slug: string; - /** The ID of the user to authorize. */ - userId: string; - /** An organization ID to scope the authorization to a specific organization. */ - organizationId?: string; - /** The URL to redirect the user to after authorization. */ - returnTo?: string; } diff --git a/src/pipes/interfaces/connected-account-auth-method.interface.ts b/src/pipes/interfaces/connected-account-auth-method.interface.ts index e64ca1398..3c5efaf15 100644 --- a/src/pipes/interfaces/connected-account-auth-method.interface.ts +++ b/src/pipes/interfaces/connected-account-auth-method.interface.ts @@ -3,6 +3,7 @@ export const ConnectedAccountAuthMethod = { OAuth: 'oauth', ApiKey: 'api_key', + ClientCredentials: 'client_credentials', } as const; export type ConnectedAccountAuthMethod = diff --git a/src/pipes/interfaces/connected-account-connection-role.interface.ts b/src/pipes/interfaces/connected-account-connection-role.interface.ts new file mode 100644 index 000000000..603d17211 --- /dev/null +++ b/src/pipes/interfaces/connected-account-connection-role.interface.ts @@ -0,0 +1,9 @@ +// This file is auto-generated by oagen. Do not edit. + +export const ConnectedAccountConnectionRole = { + Compatibility: 'compatibility', + Standard: 'standard', +} as const; + +export type ConnectedAccountConnectionRole = + (typeof ConnectedAccountConnectionRole)[keyof typeof ConnectedAccountConnectionRole]; diff --git a/src/pipes/interfaces/connected-account-dto.interface.ts b/src/pipes/interfaces/connected-account-dto.interface.ts index 85a135513..358361f4c 100644 --- a/src/pipes/interfaces/connected-account-dto.interface.ts +++ b/src/pipes/interfaces/connected-account-dto.interface.ts @@ -1,6 +1,6 @@ // This file is auto-generated by oagen. Do not edit. -import type { ConnectedAccountState } from './connected-account-state.interface'; +import type { ConnectedAccountInputState } from './connected-account-input-state.interface'; export interface ConnectedAccountDto { /** The OAuth access token for the connected account. */ @@ -12,7 +12,7 @@ export interface ConnectedAccountDto { /** The OAuth scopes granted for this connection. */ scopes?: string[]; /** Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. */ - state?: ConnectedAccountState; + state?: ConnectedAccountInputState; } export interface ConnectedAccountDtoResponse { @@ -20,5 +20,5 @@ export interface ConnectedAccountDtoResponse { refresh_token?: string; expires_at?: string; scopes?: string[]; - state?: ConnectedAccountState; + state?: ConnectedAccountInputState; } diff --git a/src/pipes/interfaces/connected-account-input-state.interface.ts b/src/pipes/interfaces/connected-account-input-state.interface.ts new file mode 100644 index 000000000..0382ff002 --- /dev/null +++ b/src/pipes/interfaces/connected-account-input-state.interface.ts @@ -0,0 +1,9 @@ +// This file is auto-generated by oagen. Do not edit. + +export const ConnectedAccountInputState = { + Connected: 'connected', + NeedsReauthorization: 'needs_reauthorization', +} as const; + +export type ConnectedAccountInputState = + (typeof ConnectedAccountInputState)[keyof typeof ConnectedAccountInputState]; diff --git a/src/pipes/interfaces/connected-account-input.interface.ts b/src/pipes/interfaces/connected-account-input.interface.ts new file mode 100644 index 000000000..0a76584a5 --- /dev/null +++ b/src/pipes/interfaces/connected-account-input.interface.ts @@ -0,0 +1,24 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { ConnectedAccountInputState } from './connected-account-input-state.interface'; + +export interface ConnectedAccountInput { + /** The OAuth access token for the connected account. */ + accessToken?: string; + /** The OAuth refresh token for the connected account. */ + refreshToken?: string; + /** The ISO-8601 timestamp when the access token expires. Required when `access_token` is provided for tokens that expire. */ + expiresAt?: Date; + /** The OAuth scopes granted for this connection. */ + scopes?: string[]; + /** Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. */ + state?: ConnectedAccountInputState; +} + +export interface ConnectedAccountInputResponse { + access_token?: string; + refresh_token?: string; + expires_at?: string; + scopes?: string[]; + state?: ConnectedAccountInputState; +} diff --git a/src/pipes/interfaces/connected-account-state.interface.ts b/src/pipes/interfaces/connected-account-state.interface.ts index 50c2be5ad..49beb89e4 100644 --- a/src/pipes/interfaces/connected-account-state.interface.ts +++ b/src/pipes/interfaces/connected-account-state.interface.ts @@ -3,6 +3,7 @@ export const ConnectedAccountState = { Connected: 'connected', NeedsReauthorization: 'needs_reauthorization', + Disconnected: 'disconnected', } as const; export type ConnectedAccountState = diff --git a/src/pipes/interfaces/connected-account.interface.ts b/src/pipes/interfaces/connected-account.interface.ts index 5d180b7b8..b02fb249c 100644 --- a/src/pipes/interfaces/connected-account.interface.ts +++ b/src/pipes/interfaces/connected-account.interface.ts @@ -1,5 +1,6 @@ // This file is auto-generated by oagen. Do not edit. +import type { ConnectedAccountConnectionRole } from './connected-account-connection-role.interface'; import type { ConnectedAccountAuthMethod } from './connected-account-auth-method.interface'; import type { ConnectedAccountState } from './connected-account-state.interface'; @@ -8,16 +9,28 @@ export interface ConnectedAccount { object: 'connected_account'; /** The unique identifier of the connected account. */ id: string; + /** Whether this row is the compatibility connection visible to undeclared clients or a standard peer for plural-aware clients. The role does not indicate preference or creation order. */ + connectionRole?: ConnectedAccountConnectionRole; + /** A best-effort identifier for the provider account this connection points at. It is correlation metadata, not the connection identifier or a selector. */ + accountIdentifier?: string | null; + /** A mutable, non-unique display name for the provider account connection. */ + accountDisplayName?: string | null; /** The [User](https://workos.com/docs/reference/authkit/user) identifier associated with this connection. */ userId: string | null; /** The [Organization](https://workos.com/docs/reference/organization) identifier associated with this connection, or `null` if not scoped to an organization. */ organizationId: string | null; /** The OAuth scopes granted for this connection. */ scopes: string[]; - /** The authentication method used for this connection (`oauth` or `api_key`). Defaults to `oauth` if absent. */ + /** The authentication method used for this connection (`oauth`, `api_key`, or `client_credentials`). Defaults to `oauth` if absent. */ authMethod?: ConnectedAccountAuthMethod; /** The last four characters of the API key, or `null` for OAuth connections. */ apiKeyLast4?: string | null; + /** The client ID supplied for this connection. Only present when `auth_method` is `client_credentials`. */ + clientId?: string | null; + /** The last four characters of the client secret supplied for this connection, or `null` when it can't be read. Only present when `auth_method` is `client_credentials`. */ + clientSecretLast4?: string | null; + /** The connection-level configuration values stored for this connection — the fields the provider declares at `installation` scope, excluding any it declares as secret. Only present when `auth_method` is `client_credentials`. */ + config?: Record; /** * The state of the connected account: * - `connected`: The connection is active and tokens are valid. @@ -34,11 +47,17 @@ export interface ConnectedAccount { export interface ConnectedAccountResponse { object: 'connected_account'; id: string; + connection_role?: ConnectedAccountConnectionRole; + account_identifier?: string | null; + account_display_name?: string | null; user_id: string | null; organization_id: string | null; scopes: string[]; auth_method?: ConnectedAccountAuthMethod; api_key_last_4?: string | null; + client_id?: string | null; + client_secret_last_4?: string | null; + config?: Record; state: ConnectedAccountState; created_at: string; updated_at: string; diff --git a/src/pipes/interfaces/create-data-integration-auth-methods.interface.ts b/src/pipes/interfaces/create-data-integration-auth-methods.interface.ts new file mode 100644 index 000000000..dbc7882d0 --- /dev/null +++ b/src/pipes/interfaces/create-data-integration-auth-methods.interface.ts @@ -0,0 +1,10 @@ +// This file is auto-generated by oagen. Do not edit. + +export const CreateDataIntegrationAuthMethods = { + OAuth: 'oauth', + ApiKey: 'api_key', + ClientCredentials: 'client_credentials', +} as const; + +export type CreateDataIntegrationAuthMethods = + (typeof CreateDataIntegrationAuthMethods)[keyof typeof CreateDataIntegrationAuthMethods]; diff --git a/src/pipes/interfaces/create-data-integration-credential-options.interface.ts b/src/pipes/interfaces/create-data-integration-credential-options.interface.ts index 2a4b53dbd..beab3fc0d 100644 --- a/src/pipes/interfaces/create-data-integration-credential-options.interface.ts +++ b/src/pipes/interfaces/create-data-integration-credential-options.interface.ts @@ -1,10 +1,8 @@ // This file is auto-generated by oagen. Do not edit. -export interface CreateDataIntegrationCredentialOptions { +import type { DataIntegrationsVendCredentialsRequest } from './data-integrations-vend-credentials-request.interface'; + +export interface CreateDataIntegrationCredentialOptions extends DataIntegrationsVendCredentialsRequest { /** The identifier of the integration. */ slug: string; - /** A [User](https://workos.com/docs/reference/authkit/user) identifier. */ - userId: string; - /** An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. */ - organizationId?: string; } diff --git a/src/pipes/interfaces/create-data-integration-options.interface.ts b/src/pipes/interfaces/create-data-integration-options.interface.ts index 0c359badc..26122fb34 100644 --- a/src/pipes/interfaces/create-data-integration-options.interface.ts +++ b/src/pipes/interfaces/create-data-integration-options.interface.ts @@ -1,19 +1,5 @@ // This file is auto-generated by oagen. Do not edit. -import type { DataIntegrationCredentialsDto } from './data-integration-credentials-dto.interface'; -import type { CustomProviderDefinition } from './custom-provider-definition.interface'; +import type { CreateDataIntegration } from './create-data-integration.interface'; -export interface CreateDataIntegrationOptions { - /** The provider to create a Data Integration for. For a built-in provider use its slug (e.g. `github`, `slack`). For a custom provider, this is the new provider slug and `custom_provider` must be supplied. A custom provider slug cannot shadow an existing global provider slug. */ - provider: string; - /** An optional description of the Data Integration. */ - description?: string | null; - /** Whether the Data Integration is enabled. Defaults to `false`. */ - enabled?: boolean; - /** The OAuth scopes to request for the Data Integration. Defaults to the provider's configured scopes when omitted. */ - scopes?: string[] | null; - /** The credentials to configure for the Data Integration. Required for both built-in and custom providers. */ - credentials?: DataIntegrationCredentialsDto; - /** The OAuth definition for a custom provider. Supply this to define a custom provider; omit it to create an integration for a built-in provider. */ - customProvider?: CustomProviderDefinition; -} +export type CreateDataIntegrationOptions = CreateDataIntegration; diff --git a/src/pipes/interfaces/create-data-integration-ownership.interface.ts b/src/pipes/interfaces/create-data-integration-ownership.interface.ts new file mode 100644 index 000000000..7e3a8ed58 --- /dev/null +++ b/src/pipes/interfaces/create-data-integration-ownership.interface.ts @@ -0,0 +1,9 @@ +// This file is auto-generated by oagen. Do not edit. + +export const CreateDataIntegrationOwnership = { + User: 'user', + Organization: 'organization', +} as const; + +export type CreateDataIntegrationOwnership = + (typeof CreateDataIntegrationOwnership)[keyof typeof CreateDataIntegrationOwnership]; diff --git a/src/pipes/interfaces/create-data-integration.interface.ts b/src/pipes/interfaces/create-data-integration.interface.ts index 14462c011..a48310522 100644 --- a/src/pipes/interfaces/create-data-integration.interface.ts +++ b/src/pipes/interfaces/create-data-integration.interface.ts @@ -1,34 +1,52 @@ // This file is auto-generated by oagen. Do not edit. import type { - DataIntegrationCredentialsDto, - DataIntegrationCredentialsDtoResponse, -} from './data-integration-credentials-dto.interface'; + DataIntegrationCredentialsInput, + DataIntegrationCredentialsInputResponse, +} from './data-integration-credentials-input.interface'; +import type { + ApiKeyInstallation, + ApiKeyInstallationResponse, +} from './api-key-installation.interface'; import type { CustomProviderDefinition, CustomProviderDefinitionResponse, } from './custom-provider-definition.interface'; +import type { CreateDataIntegrationOwnership } from './create-data-integration-ownership.interface'; +import type { CreateDataIntegrationAuthMethods } from './create-data-integration-auth-methods.interface'; export interface CreateDataIntegration { /** The provider to create a Data Integration for. For a built-in provider use its slug (e.g. `github`, `slack`). For a custom provider, this is the new provider slug and `custom_provider` must be supplied. A custom provider slug cannot shadow an existing global provider slug. */ provider: string; + /** Who owns the Data Integration. `user` (the default) creates the integration users connect their own accounts to; `organization` creates the root organizations connect to. Ownership is fixed at creation, and one integration of each ownership may exist per provider. Independent of `credentials.type`. */ + ownership?: CreateDataIntegrationOwnership; /** An optional description of the Data Integration. */ description?: string | null; /** Whether the Data Integration is enabled. Defaults to `false`. */ enabled?: boolean; /** The OAuth scopes to request for the Data Integration. Defaults to the provider's configured scopes when omitted. */ scopes?: string[] | null; - /** The credentials to configure for the Data Integration. Required for both built-in and custom providers. */ - credentials?: DataIntegrationCredentialsDto; + /** How accounts authenticate with the provider. Defaults to `["oauth"]`. Use `["api_key"]` to declare an API key integration; `credentials` is then not required and keys are supplied per-tenant (optionally via `api_key` on this request). Use `["client_credentials"]` to declare a client-credentials integration; `credentials` is likewise not required and client credentials are supplied per-tenant. */ + authMethods?: CreateDataIntegrationAuthMethods[]; + /** Provider-specific config values (e.g. a Snowflake `account`), keyed by the config field. Only fields the built-in provider declares are accepted. */ + config?: Record; + /** The OAuth credentials to configure for the Data Integration. Required for OAuth integrations; omit when `auth_methods` is `["api_key"]`. */ + credentials?: DataIntegrationCredentialsInput; + /** An optional API key to install for the first tenant on an `api_key` integration. Omit to declare a keyless integration; tenants can be added later via the per-installation API key path. */ + apiKey?: ApiKeyInstallation; /** The OAuth definition for a custom provider. Supply this to define a custom provider; omit it to create an integration for a built-in provider. */ customProvider?: CustomProviderDefinition; } export interface CreateDataIntegrationResponse { provider: string; + ownership?: CreateDataIntegrationOwnership; description?: string | null; enabled?: boolean; scopes?: string[] | null; - credentials?: DataIntegrationCredentialsDtoResponse; + auth_methods?: CreateDataIntegrationAuthMethods[]; + config?: Record; + credentials?: DataIntegrationCredentialsInputResponse; + api_key?: ApiKeyInstallationResponse; custom_provider?: CustomProviderDefinitionResponse; } diff --git a/src/pipes/interfaces/create-organization-connected-account-options.interface.ts b/src/pipes/interfaces/create-organization-connected-account-options.interface.ts new file mode 100644 index 000000000..85d6cdbc5 --- /dev/null +++ b/src/pipes/interfaces/create-organization-connected-account-options.interface.ts @@ -0,0 +1,22 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { OrganizationConnectedAccountState } from './organization-connected-account-state.interface'; + +export interface CreateOrganizationConnectedAccountOptions { + /** An [Organization](https://workos.com/docs/reference/organization) identifier. */ + organizationId: string; + /** The slug identifier of the provider (e.g., `github`, `slack`, `notion`). */ + slug: string; + /** The OAuth access token for the connected account. */ + accessToken?: string; + /** The OAuth refresh token for the connected account. */ + refreshToken?: string; + /** The ISO-8601 timestamp when the access token expires. Required when `access_token` is provided for tokens that expire. */ + expiresAt?: Date; + /** The OAuth scopes granted for this connection. */ + scopes?: string[]; + /** Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. */ + state?: OrganizationConnectedAccountState; + /** The [User](https://workos.com/docs/reference/authkit/user) identifier of the organization member on whose behalf the connected account is being imported or updated. The user must be an active member of the organization. */ + userId: string; +} diff --git a/src/pipes/interfaces/create-user-connected-account-options.interface.ts b/src/pipes/interfaces/create-user-connected-account-options.interface.ts index 9b6139429..cadacbdd7 100644 --- a/src/pipes/interfaces/create-user-connected-account-options.interface.ts +++ b/src/pipes/interfaces/create-user-connected-account-options.interface.ts @@ -1,6 +1,6 @@ // This file is auto-generated by oagen. Do not edit. -import type { ConnectedAccountState } from './connected-account-state.interface'; +import type { ConnectedAccountInputState } from './connected-account-input-state.interface'; export interface CreateUserConnectedAccountOptions { /** A [User](https://workos.com/docs/reference/authkit/user) identifier. */ @@ -18,5 +18,5 @@ export interface CreateUserConnectedAccountOptions { /** The OAuth scopes granted for this connection. */ scopes?: string[]; /** Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. */ - state?: ConnectedAccountState; + state?: ConnectedAccountInputState; } diff --git a/src/pipes/interfaces/custom-provider-definition.interface.ts b/src/pipes/interfaces/custom-provider-definition.interface.ts index d470e9c78..a6c78383c 100644 --- a/src/pipes/interfaces/custom-provider-definition.interface.ts +++ b/src/pipes/interfaces/custom-provider-definition.interface.ts @@ -5,11 +5,11 @@ import type { CustomProviderDefinitionAuthenticateVia } from './custom-provider- export interface CustomProviderDefinition { /** A descriptive name for the custom provider. */ name: string; - /** The provider's OAuth authorization endpoint. */ - authorizationUrl: string; - /** The provider's OAuth token endpoint. */ - tokenUrl: string; - /** The endpoint used to refresh tokens, if different from the token endpoint. */ + /** The provider's OAuth authorization endpoint. Required for OAuth providers; omit for `api_key` providers. Must be a static URL: `${config.…}` placeholders are resolved against a provider's declared config fields, which custom providers cannot declare. */ + authorizationUrl?: string; + /** The provider's OAuth token endpoint. Required for OAuth and `client_credentials` providers; omit for `api_key` providers. Must be a static URL: `${config.…}` placeholders are resolved against a provider's declared config fields, which custom providers cannot declare. */ + tokenUrl?: string; + /** The endpoint used to refresh tokens, if different from the token endpoint. Must be a static URL, like the other endpoints. */ refreshTokenUrl?: string | null; /** Whether PKCE is used during the authorization code flow. Defaults to `true`. */ pkceEnabled?: boolean; @@ -29,8 +29,8 @@ export interface CustomProviderDefinition { export interface CustomProviderDefinitionResponse { name: string; - authorization_url: string; - token_url: string; + authorization_url?: string; + token_url?: string; refresh_token_url?: string | null; pkce_enabled?: boolean; request_scope_separator?: string; diff --git a/src/pipes/interfaces/data-integration-auth-methods.interface.ts b/src/pipes/interfaces/data-integration-auth-methods.interface.ts new file mode 100644 index 000000000..dc992fd99 --- /dev/null +++ b/src/pipes/interfaces/data-integration-auth-methods.interface.ts @@ -0,0 +1,10 @@ +// This file is auto-generated by oagen. Do not edit. + +export const DataIntegrationAuthMethods = { + OAuth: 'oauth', + ApiKey: 'api_key', + ClientCredentials: 'client_credentials', +} as const; + +export type DataIntegrationAuthMethods = + (typeof DataIntegrationAuthMethods)[keyof typeof DataIntegrationAuthMethods]; diff --git a/src/pipes/interfaces/data-integration-credentials-input-type.interface.ts b/src/pipes/interfaces/data-integration-credentials-input-type.interface.ts new file mode 100644 index 000000000..4e7af3b30 --- /dev/null +++ b/src/pipes/interfaces/data-integration-credentials-input-type.interface.ts @@ -0,0 +1,9 @@ +// This file is auto-generated by oagen. Do not edit. + +export const DataIntegrationCredentialsInputType = { + Custom: 'custom', + Organization: 'organization', +} as const; + +export type DataIntegrationCredentialsInputType = + (typeof DataIntegrationCredentialsInputType)[keyof typeof DataIntegrationCredentialsInputType]; diff --git a/src/pipes/interfaces/data-integration-credentials-input.interface.ts b/src/pipes/interfaces/data-integration-credentials-input.interface.ts new file mode 100644 index 000000000..b4f9f41b2 --- /dev/null +++ b/src/pipes/interfaces/data-integration-credentials-input.interface.ts @@ -0,0 +1,18 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { DataIntegrationCredentialsInputType } from './data-integration-credentials-input-type.interface'; + +export interface DataIntegrationCredentialsInput { + /** The credentials type. `custom` uses your own OAuth app credentials; `organization` has each organization supply its own credentials (configured per-organization). */ + type: DataIntegrationCredentialsInputType; + /** OAuth client ID for the provider app. Required when `type` is `custom`; omit for `organization`. */ + clientId?: string; + /** OAuth client secret for the provider app. Required when `type` is `custom`; omit for `organization`. */ + clientSecret?: string; +} + +export interface DataIntegrationCredentialsInputResponse { + type: DataIntegrationCredentialsInputType; + client_id?: string; + client_secret?: string; +} diff --git a/src/pipes/interfaces/data-integration-credentials-response-credential.interface.ts b/src/pipes/interfaces/data-integration-credentials-response-credential.interface.ts index 59290742e..b701b4e4e 100644 --- a/src/pipes/interfaces/data-integration-credentials-response-credential.interface.ts +++ b/src/pipes/interfaces/data-integration-credentials-response-credential.interface.ts @@ -1,26 +1,52 @@ -// This file is auto-generated by oagen. Do not edit. +// @oagen-ignore-file +// Hand-maintained to preserve the spec's nested credential discriminants. -/** The credential object containing the vended secret. */ -export interface DataIntegrationCredentialsResponseCredential { - /** Distinguishes the credential object. */ - object: 'credential'; - /** The authentication method for this credential. Additional values may be added in the future; handle unknown values gracefully. */ - authMethod: 'oauth'; - /** The OAuth access token. */ - value: string; - /** The ISO-8601 formatted timestamp indicating when the credential expires. */ - expiresAt: string | null; - /** The scopes granted to the access token. */ - scopes: string[]; - /** If the integration has requested scopes that aren't present on the access token, they're listed here. */ - missingScopes: string[]; -} +/** A vended credential, discriminated by its authentication method. */ +export type DataIntegrationCredentialsResponseCredential = + | { + object: 'credential'; + authMethod: 'oauth'; + value: string; + expiresAt: string | null; + scopes: string[]; + missingScopes: string[]; + } + | { + object: 'credential'; + authMethod: 'api_key'; + value: string; + } + | { + object: 'credential'; + authMethod: 'client_credentials'; + value: string; + expiresAt: string | null; + scopes: string[]; + missingScopes: string[]; + /** Non-sensitive provider token response fields, such as an instance URL. */ + metadata: Record; + }; -export interface DataIntegrationCredentialsResponseCredentialResponse { - object: 'credential'; - auth_method: 'oauth'; - value: string; - expires_at: string | null; - scopes: string[]; - missing_scopes: string[]; -} +export type DataIntegrationCredentialsResponseCredentialResponse = + | { + object: 'credential'; + auth_method: 'oauth'; + value: string; + expires_at: string | null; + scopes: string[]; + missing_scopes: string[]; + } + | { + object: 'credential'; + auth_method: 'api_key'; + value: string; + } + | { + object: 'credential'; + auth_method: 'client_credentials'; + value: string; + expires_at: string | null; + scopes: string[]; + missing_scopes: string[]; + metadata: Record; + }; diff --git a/src/pipes/interfaces/data-integration-credentials-response.interface.ts b/src/pipes/interfaces/data-integration-credentials-response.interface.ts index 458d23a82..d90bf88fe 100644 --- a/src/pipes/interfaces/data-integration-credentials-response.interface.ts +++ b/src/pipes/interfaces/data-integration-credentials-response.interface.ts @@ -1,4 +1,5 @@ -// This file is auto-generated by oagen. Do not edit. +// @oagen-ignore-file +// Hand-maintained to preserve the spec's nested credential discriminants. import type { DataIntegrationCredentialsResponseCredential, @@ -6,21 +7,13 @@ import type { } from './data-integration-credentials-response-credential.interface'; import type { DataIntegrationCredentialsResponseError } from './data-integration-credentials-response-error.interface'; -export interface DataIntegrationCredentialsResponse { - /** Indicates credentials are available. */ - active?: true; - /** The credential object containing the vended secret. */ - credential?: DataIntegrationCredentialsResponseCredential; - /** - * The reason credentials are unavailable. Additional values may be added in the future; handle unknown values gracefully. - * - `"not_installed"`: The user does not have the integration installed. - * - `"needs_reauthorization"`: The user needs to reauthorize the integration. - */ - error?: DataIntegrationCredentialsResponseError; -} +export type DataIntegrationCredentialsResponse = + | { active: true; credential: DataIntegrationCredentialsResponseCredential } + | { active: false; error: DataIntegrationCredentialsResponseError }; -export interface DataIntegrationCredentialsResponseWire { - active?: true; - credential?: DataIntegrationCredentialsResponseCredentialResponse; - error?: DataIntegrationCredentialsResponseError; -} +export type DataIntegrationCredentialsResponseWire = + | { + active: true; + credential: DataIntegrationCredentialsResponseCredentialResponse; + } + | { active: false; error: DataIntegrationCredentialsResponseError }; diff --git a/src/pipes/interfaces/data-integration-installation-connection-role.interface.ts b/src/pipes/interfaces/data-integration-installation-connection-role.interface.ts new file mode 100644 index 000000000..cc46a945f --- /dev/null +++ b/src/pipes/interfaces/data-integration-installation-connection-role.interface.ts @@ -0,0 +1,9 @@ +// This file is auto-generated by oagen. Do not edit. + +export const DataIntegrationInstallationConnectionRole = { + Compatibility: 'compatibility', + Standard: 'standard', +} as const; + +export type DataIntegrationInstallationConnectionRole = + (typeof DataIntegrationInstallationConnectionRole)[keyof typeof DataIntegrationInstallationConnectionRole]; diff --git a/src/pipes/interfaces/data-integration-installation.interface.ts b/src/pipes/interfaces/data-integration-installation.interface.ts new file mode 100644 index 000000000..e832337d3 --- /dev/null +++ b/src/pipes/interfaces/data-integration-installation.interface.ts @@ -0,0 +1,30 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { DataIntegrationInstallationConnectionRole } from './data-integration-installation-connection-role.interface'; + +export interface DataIntegrationInstallation { + /** Unique identifier of the installation. */ + id: string; + /** Whether this is the compatibility connection visible to undeclared clients or a standard connection for plural-aware clients. */ + connectionRole: DataIntegrationInstallationConnectionRole; + /** A best-effort provider account identifier used for correlation, not connection selection. */ + accountIdentifier: string | null; + /** A mutable, non-unique display name for this connection. */ + accountDisplayName: string | null; + /** The User the API key was installed for. Null on an `organization`-owned integration, whose installations belong to the organization. */ + userId: string | null; + /** The Organization the installation is scoped to (or owned by, on an `organization`-owned integration), or null when unscoped. */ + organizationId: string | null; + /** The last four characters of the stored API key. The full key is never returned. */ + apiKeyLast4: string | null; +} + +export interface DataIntegrationInstallationResponse { + id: string; + connection_role: DataIntegrationInstallationConnectionRole; + account_identifier: string | null; + account_display_name: string | null; + user_id: string | null; + organization_id: string | null; + api_key_last_4: string | null; +} diff --git a/src/pipes/interfaces/data-integration-ownership.interface.ts b/src/pipes/interfaces/data-integration-ownership.interface.ts new file mode 100644 index 000000000..d30497564 --- /dev/null +++ b/src/pipes/interfaces/data-integration-ownership.interface.ts @@ -0,0 +1,9 @@ +// This file is auto-generated by oagen. Do not edit. + +export const DataIntegrationOwnership = { + User: 'user', + Organization: 'organization', +} as const; + +export type DataIntegrationOwnership = + (typeof DataIntegrationOwnership)[keyof typeof DataIntegrationOwnership]; diff --git a/src/pipes/interfaces/data-integration.interface.ts b/src/pipes/interfaces/data-integration.interface.ts index afd94340b..82f6005cf 100644 --- a/src/pipes/interfaces/data-integration.interface.ts +++ b/src/pipes/interfaces/data-integration.interface.ts @@ -4,11 +4,17 @@ import type { DataIntegrationCredential, DataIntegrationCredentialResponse, } from './data-integration-credential.interface'; +import type { + DataIntegrationInstallation, + DataIntegrationInstallationResponse, +} from './data-integration-installation.interface'; import type { DataIntegrationCustomProvider, DataIntegrationCustomProviderResponse, } from './data-integration-custom-provider.interface'; +import type { DataIntegrationOwnership } from './data-integration-ownership.interface'; import type { DataIntegrationState } from './data-integration-state.interface'; +import type { DataIntegrationAuthMethods } from './data-integration-auth-methods.interface'; export interface DataIntegration { /** Distinguishes the Data Integration object. */ @@ -19,6 +25,8 @@ export interface DataIntegration { slug: string; /** The integration type derived from the provider. */ integrationType: string; + /** Who owns the Data Integration: `user` when users connect their own accounts, `organization` when organizations connect. Fixed at creation. */ + ownership?: DataIntegrationOwnership; /** An optional description of the Data Integration. */ description: string | null; /** Whether the Data Integration is enabled. */ @@ -27,10 +35,16 @@ export interface DataIntegration { state: DataIntegrationState; /** The OAuth scopes configured for the Data Integration. `null` when the provider's configured scopes are used. */ scopes: string[] | null; - /** The OAuth redirect URI to register with the provider when configuring the custom application. */ + /** The OAuth redirect URI to register with the provider when configuring the custom application. Empty for `api_key` and `client_credentials` integrations, which run no authorization redirect. */ redirectUri: string; - /** The credentials configured for the Data Integration. */ - credentials: DataIntegrationCredential; + /** How accounts authenticate with the provider for this Data Integration. */ + authMethods?: DataIntegrationAuthMethods[]; + /** The integration-level OAuth app credentials. `null` for `api_key` and `client_credentials` integrations, which hold no integration-level credentials (secrets are installed per-tenant). */ + credentials: DataIntegrationCredential | null; + /** The tenant installation created when an API key was supplied at creation time; `null` otherwise. Not populated on list/get responses. */ + installation?: DataIntegrationInstallation | null; + /** Provider-specific config values set on the Data Integration (e.g. a Snowflake `account`), keyed by config field. Only fields the provider declares are accepted. */ + config?: Record; /** The OAuth definition when this is a custom provider; `null` for built-in providers. */ customProvider: DataIntegrationCustomProvider | null; /** An ISO 8601 timestamp. */ @@ -44,12 +58,16 @@ export interface DataIntegrationResponse { id: string; slug: string; integration_type: string; + ownership?: DataIntegrationOwnership; description: string | null; enabled: boolean; state: DataIntegrationState; scopes: string[] | null; redirect_uri: string; - credentials: DataIntegrationCredentialResponse; + auth_methods?: DataIntegrationAuthMethods[]; + credentials: DataIntegrationCredentialResponse | null; + installation?: DataIntegrationInstallationResponse | null; + config?: Record; custom_provider: DataIntegrationCustomProviderResponse | null; created_at: string; updated_at: string; diff --git a/src/pipes/interfaces/data-integrations-get-data-integration-authorize-url-request-connection-owner.interface.ts b/src/pipes/interfaces/data-integrations-get-data-integration-authorize-url-request-connection-owner.interface.ts new file mode 100644 index 000000000..39c233823 --- /dev/null +++ b/src/pipes/interfaces/data-integrations-get-data-integration-authorize-url-request-connection-owner.interface.ts @@ -0,0 +1,10 @@ +// This file is auto-generated by oagen. Do not edit. + +export const DataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner = + { + User: 'user', + Organization: 'organization', + } as const; + +export type DataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner = + (typeof DataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner)[keyof typeof DataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner]; diff --git a/src/pipes/interfaces/data-integrations-get-data-integration-authorize-url-request.interface.ts b/src/pipes/interfaces/data-integrations-get-data-integration-authorize-url-request.interface.ts index d59991717..830f25c51 100644 --- a/src/pipes/interfaces/data-integrations-get-data-integration-authorize-url-request.interface.ts +++ b/src/pipes/interfaces/data-integrations-get-data-integration-authorize-url-request.interface.ts @@ -1,16 +1,24 @@ // This file is auto-generated by oagen. Do not edit. +import type { DataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner } from './data-integrations-get-data-integration-authorize-url-request-connection-owner.interface'; + export interface DataIntegrationsGetDataIntegrationAuthorizeUrlRequest { - /** The ID of the user to authorize. */ + /** The ID of the user to authorize. When `connection_owner` is `organization`, this is the user authorizing on behalf of the organization; they must be an active member of the organization and do not become the owner of the resulting connected account. */ userId: string; - /** An organization ID to scope the authorization to a specific organization. */ + /** An organization ID to scope the authorization to a specific organization. Required when `connection_owner` is `organization`. */ organizationId?: string; + /** Who will own the connected account. `user` (the default) connects the user's own account. `organization` connects the organization's shared account and requires `organization_id`. */ + connectionOwner?: DataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner; /** The URL to redirect the user to after authorization. */ returnTo?: string; + /** Connect-time config values for the provider-declared `installation`-scope fields (e.g. a Zendesk `subdomain`), keyed by the config field. Only fields the provider declares may be supplied, and required fields must be provided unless already pinned on the integration. */ + config?: Record; } export interface DataIntegrationsGetDataIntegrationAuthorizeUrlRequestResponse { user_id: string; organization_id?: string; + connection_owner?: DataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner; return_to?: string; + config?: Record; } diff --git a/src/pipes/interfaces/data-integrations-get-user-token-request-connection-owner.interface.ts b/src/pipes/interfaces/data-integrations-get-user-token-request-connection-owner.interface.ts new file mode 100644 index 000000000..9d67a0329 --- /dev/null +++ b/src/pipes/interfaces/data-integrations-get-user-token-request-connection-owner.interface.ts @@ -0,0 +1,9 @@ +// This file is auto-generated by oagen. Do not edit. + +export const DataIntegrationsGetUserTokenRequestConnectionOwner = { + User: 'user', + Organization: 'organization', +} as const; + +export type DataIntegrationsGetUserTokenRequestConnectionOwner = + (typeof DataIntegrationsGetUserTokenRequestConnectionOwner)[keyof typeof DataIntegrationsGetUserTokenRequestConnectionOwner]; diff --git a/src/pipes/interfaces/data-integrations-get-user-token-request.interface.ts b/src/pipes/interfaces/data-integrations-get-user-token-request.interface.ts index 24c3875d3..d0b449d93 100644 --- a/src/pipes/interfaces/data-integrations-get-user-token-request.interface.ts +++ b/src/pipes/interfaces/data-integrations-get-user-token-request.interface.ts @@ -1,13 +1,24 @@ // This file is auto-generated by oagen. Do not edit. +import type { DataIntegrationsGetUserTokenRequestConnectionOwner } from './data-integrations-get-user-token-request-connection-owner.interface'; + export interface DataIntegrationsGetUserTokenRequest { - /** A [User](https://workos.com/docs/reference/authkit/user) identifier. */ + /** A [User](https://workos.com/docs/reference/authkit/user) identifier. When `connection_owner` is `organization`, this is the user the credentials are vended on behalf of; they must be an active member of the organization. */ userId: string; - /** An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. */ + /** An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. Required when `connection_owner` is `organization`. */ organizationId?: string | null; + /** A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to select a specific connection when the user has several for this provider. */ + connectedAccountId?: string; + /** Which connection to vend from. `user` (the default) vends the user's own connection and requires `user_id`. `organization` vends the organization's shared connection and requires `organization_id`. */ + connectionOwner?: DataIntegrationsGetUserTokenRequestConnectionOwner; + /** Set to `true` to use the plural connection contract. If no `connected_account_id` is supplied and several connections match, the request returns `account_selection_required`. When omitted or `false`, only the compatibility connection is considered. */ + supportsMultipleConnections?: boolean; } export interface DataIntegrationsGetUserTokenRequestResponse { user_id: string; organization_id?: string | null; + connected_account_id?: string; + connection_owner?: DataIntegrationsGetUserTokenRequestConnectionOwner; + supports_multiple_connections?: boolean; } diff --git a/src/pipes/interfaces/data-integrations-list-response-data-auth-methods.interface.ts b/src/pipes/interfaces/data-integrations-list-response-data-auth-methods.interface.ts index dfe4175b9..067baf3b1 100644 --- a/src/pipes/interfaces/data-integrations-list-response-data-auth-methods.interface.ts +++ b/src/pipes/interfaces/data-integrations-list-response-data-auth-methods.interface.ts @@ -3,6 +3,7 @@ export const DataIntegrationsListResponseDataAuthMethods = { OAuth: 'oauth', ApiKey: 'api_key', + ClientCredentials: 'client_credentials', } as const; export type DataIntegrationsListResponseDataAuthMethods = diff --git a/src/pipes/interfaces/data-integrations-list-response-data-connected-account-auth-method.interface.ts b/src/pipes/interfaces/data-integrations-list-response-data-connected-account-auth-method.interface.ts index 892bf63e8..55d52f63f 100644 --- a/src/pipes/interfaces/data-integrations-list-response-data-connected-account-auth-method.interface.ts +++ b/src/pipes/interfaces/data-integrations-list-response-data-connected-account-auth-method.interface.ts @@ -3,6 +3,7 @@ export const DataIntegrationsListResponseDataConnectedAccountAuthMethod = { OAuth: 'oauth', ApiKey: 'api_key', + ClientCredentials: 'client_credentials', } as const; export type DataIntegrationsListResponseDataConnectedAccountAuthMethod = diff --git a/src/pipes/interfaces/data-integrations-list-response-data-connected-account-connection-role.interface.ts b/src/pipes/interfaces/data-integrations-list-response-data-connected-account-connection-role.interface.ts new file mode 100644 index 000000000..a29407259 --- /dev/null +++ b/src/pipes/interfaces/data-integrations-list-response-data-connected-account-connection-role.interface.ts @@ -0,0 +1,9 @@ +// This file is auto-generated by oagen. Do not edit. + +export const DataIntegrationsListResponseDataConnectedAccountConnectionRole = { + Compatibility: 'compatibility', + Standard: 'standard', +} as const; + +export type DataIntegrationsListResponseDataConnectedAccountConnectionRole = + (typeof DataIntegrationsListResponseDataConnectedAccountConnectionRole)[keyof typeof DataIntegrationsListResponseDataConnectedAccountConnectionRole]; diff --git a/src/pipes/interfaces/data-integrations-list-response-data-connected-account.interface.ts b/src/pipes/interfaces/data-integrations-list-response-data-connected-account.interface.ts index fd7da74b4..d533d4bd3 100644 --- a/src/pipes/interfaces/data-integrations-list-response-data-connected-account.interface.ts +++ b/src/pipes/interfaces/data-integrations-list-response-data-connected-account.interface.ts @@ -1,5 +1,6 @@ // This file is auto-generated by oagen. Do not edit. +import type { DataIntegrationsListResponseDataConnectedAccountConnectionRole } from './data-integrations-list-response-data-connected-account-connection-role.interface'; import type { DataIntegrationsListResponseDataConnectedAccountAuthMethod } from './data-integrations-list-response-data-connected-account-auth-method.interface'; import type { DataIntegrationsListResponseDataConnectedAccountState } from './data-integrations-list-response-data-connected-account-state.interface'; @@ -8,16 +9,28 @@ export interface DataIntegrationsListResponseDataConnectedAccount { object: 'connected_account'; /** The unique identifier of the connected account. */ id: string; + /** Whether this row is the compatibility connection visible to undeclared clients or a standard peer for plural-aware clients. The role does not indicate preference or creation order. */ + connectionRole?: DataIntegrationsListResponseDataConnectedAccountConnectionRole; + /** A best-effort identifier for the provider account this connection points at. It is correlation metadata, not the connection identifier or a selector. */ + accountIdentifier?: string | null; + /** A mutable, non-unique display name for the provider account connection. */ + accountDisplayName?: string | null; /** The [User](https://workos.com/docs/reference/authkit/user) identifier associated with this connection. */ userId: string | null; /** The [Organization](https://workos.com/docs/reference/organization) identifier associated with this connection, or `null` if not scoped to an organization. */ organizationId: string | null; /** The OAuth scopes granted for this connection. */ scopes: string[]; - /** The authentication method used for this connection (`oauth` or `api_key`). Defaults to `oauth` if absent. */ + /** The authentication method used for this connection (`oauth`, `api_key`, or `client_credentials`). Defaults to `oauth` if absent. */ authMethod?: DataIntegrationsListResponseDataConnectedAccountAuthMethod; /** The last four characters of the API key, or `null` for OAuth connections. */ apiKeyLast4?: string | null; + /** The client ID supplied for this connection. Only present when `auth_method` is `client_credentials`. */ + clientId?: string | null; + /** The last four characters of the client secret supplied for this connection, or `null` when it can't be read. Only present when `auth_method` is `client_credentials`. */ + clientSecretLast4?: string | null; + /** The connection-level configuration values stored for this connection — the fields the provider declares at `installation` scope, excluding any it declares as secret. Only present when `auth_method` is `client_credentials`. */ + config?: Record; /** * The state of the connected account: * - `connected`: The connection is active and tokens are valid. @@ -39,11 +52,17 @@ export interface DataIntegrationsListResponseDataConnectedAccount { export interface DataIntegrationsListResponseDataConnectedAccountResponse { object: 'connected_account'; id: string; + connection_role?: DataIntegrationsListResponseDataConnectedAccountConnectionRole; + account_identifier?: string | null; + account_display_name?: string | null; user_id: string | null; organization_id: string | null; scopes: string[]; auth_method?: DataIntegrationsListResponseDataConnectedAccountAuthMethod; api_key_last_4?: string | null; + client_id?: string | null; + client_secret_last_4?: string | null; + config?: Record; state: DataIntegrationsListResponseDataConnectedAccountState; created_at: string; updated_at: string; diff --git a/src/pipes/interfaces/data-integrations-list-response-data-connection-owner.interface.ts b/src/pipes/interfaces/data-integrations-list-response-data-connection-owner.interface.ts new file mode 100644 index 000000000..eeb17332c --- /dev/null +++ b/src/pipes/interfaces/data-integrations-list-response-data-connection-owner.interface.ts @@ -0,0 +1,9 @@ +// This file is auto-generated by oagen. Do not edit. + +export const DataIntegrationsListResponseDataConnectionOwner = { + User: 'user', + Organization: 'organization', +} as const; + +export type DataIntegrationsListResponseDataConnectionOwner = + (typeof DataIntegrationsListResponseDataConnectionOwner)[keyof typeof DataIntegrationsListResponseDataConnectionOwner]; diff --git a/src/pipes/interfaces/data-integrations-list-response-data.interface.ts b/src/pipes/interfaces/data-integrations-list-response-data.interface.ts index 8d3206c5d..7dd0127de 100644 --- a/src/pipes/interfaces/data-integrations-list-response-data.interface.ts +++ b/src/pipes/interfaces/data-integrations-list-response-data.interface.ts @@ -5,6 +5,7 @@ import type { DataIntegrationsListResponseDataConnectedAccountResponse, } from './data-integrations-list-response-data-connected-account.interface'; import type { DataIntegrationsListResponseDataAuthMethods } from './data-integrations-list-response-data-auth-methods.interface'; +import type { DataIntegrationsListResponseDataConnectionOwner } from './data-integrations-list-response-data-connection-owner.interface'; import type { DataIntegrationsListResponseDataOwnership } from './data-integrations-list-response-data-ownership.interface'; export interface DataIntegrationsListResponseData { @@ -24,16 +25,23 @@ export interface DataIntegrationsListResponseData { credentialsType: string; /** The OAuth scopes configured for this provider, or `null` if none are configured. */ scopes: string[] | null; - /** The authentication methods supported by this provider (`oauth`, `api_key`, or both). Defaults to `["oauth"]` if absent. */ + /** The authentication methods supported by this provider (`oauth`, `api_key`, `client_credentials`, or a combination). Defaults to `["oauth"]` if absent. */ authMethods?: DataIntegrationsListResponseDataAuthMethods[]; - /** Whether the provider is owned by a user or organization. */ + /** Who owns connections made through this provider: `user` for connections owned by individual users, or `organization` for a connection shared by every member of the organization. A provider row can exist before any connected account does. */ + connectionOwner?: DataIntegrationsListResponseDataConnectionOwner; + /** + * Use `connection_owner` instead. Legacy spelling of the same value: `userland_user` corresponds to `connection_owner: "user"` and `organization` to `connection_owner: "organization"`. + * @deprecated + */ ownership: DataIntegrationsListResponseDataOwnership; /** The timestamp when the provider was created. */ createdAt: string; /** The timestamp when the provider was last updated. */ updatedAt: string; - /** The user's [connected account](https://workos.com/docs/reference/pipes/connected-account) for this provider, or `null` if the user has not connected. */ + /** The user's compatibility [connected account](https://workos.com/docs/reference/pipes/connected-account) for this provider, or `null` when the compatibility slot is empty. This legacy field never selects a standard connection. */ connectedAccount: DataIntegrationsListResponseDataConnectedAccount | null; + /** The user's connected accounts for this provider in the requested ownership context. This contains only the compatibility connection unless `supports_multiple_connections` is `true`. */ + connectedAccounts?: DataIntegrationsListResponseDataConnectedAccount[]; } export interface DataIntegrationsListResponseDataResponse { @@ -46,8 +54,10 @@ export interface DataIntegrationsListResponseDataResponse { credentials_type: string; scopes: string[] | null; auth_methods?: DataIntegrationsListResponseDataAuthMethods[]; + connection_owner?: DataIntegrationsListResponseDataConnectionOwner; ownership: DataIntegrationsListResponseDataOwnership; created_at: string; updated_at: string; connected_account: DataIntegrationsListResponseDataConnectedAccountResponse | null; + connected_accounts?: DataIntegrationsListResponseDataConnectedAccountResponse[]; } diff --git a/src/pipes/interfaces/data-integrations-list-response.interface.ts b/src/pipes/interfaces/data-integrations-list-response.interface.ts index 5afef10dd..ddb60b4e5 100644 --- a/src/pipes/interfaces/data-integrations-list-response.interface.ts +++ b/src/pipes/interfaces/data-integrations-list-response.interface.ts @@ -8,7 +8,7 @@ import type { export interface DataIntegrationsListResponse { /** Indicates this is a list response. */ object: 'list'; - /** A list of [providers](https://workos.com/docs/reference/pipes/provider), each including a [`connected_account`](https://workos.com/docs/reference/pipes/connected-account) field with the user's connection status. */ + /** A list of [providers](https://workos.com/docs/reference/pipes/provider), each including the legacy `connected_account` field and the additive `connected_accounts` collection. */ data: DataIntegrationsListResponseData[]; } diff --git a/src/pipes/interfaces/data-integrations-upsert-api-key-request-connection-owner.interface.ts b/src/pipes/interfaces/data-integrations-upsert-api-key-request-connection-owner.interface.ts new file mode 100644 index 000000000..d824fd8b2 --- /dev/null +++ b/src/pipes/interfaces/data-integrations-upsert-api-key-request-connection-owner.interface.ts @@ -0,0 +1,9 @@ +// This file is auto-generated by oagen. Do not edit. + +export const DataIntegrationsUpsertApiKeyRequestConnectionOwner = { + User: 'user', + Organization: 'organization', +} as const; + +export type DataIntegrationsUpsertApiKeyRequestConnectionOwner = + (typeof DataIntegrationsUpsertApiKeyRequestConnectionOwner)[keyof typeof DataIntegrationsUpsertApiKeyRequestConnectionOwner]; diff --git a/src/pipes/interfaces/data-integrations-upsert-api-key-request.interface.ts b/src/pipes/interfaces/data-integrations-upsert-api-key-request.interface.ts index 81d215a35..2b4671d23 100644 --- a/src/pipes/interfaces/data-integrations-upsert-api-key-request.interface.ts +++ b/src/pipes/interfaces/data-integrations-upsert-api-key-request.interface.ts @@ -1,10 +1,16 @@ // This file is auto-generated by oagen. Do not edit. +import type { DataIntegrationsUpsertApiKeyRequestConnectionOwner } from './data-integrations-upsert-api-key-request-connection-owner.interface'; + export interface DataIntegrationsUpsertApiKeyRequest { /** A [User](https://workos.com/docs/reference/authkit/user) identifier. */ userId: string; - /** An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. */ + /** An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. Required when `connection_owner` is `organization`. */ organizationId?: string; + /** A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to rotate a specific existing connection. */ + connectedAccountId?: string; + /** Whose connection to create or rotate. `user` (the default) addresses the connection owned by `user_id`. `organization` addresses the connection shared by every member of `organization_id`; `user_id` then identifies the member performing the request and must be an active member of the organization. */ + connectionOwner?: DataIntegrationsUpsertApiKeyRequestConnectionOwner; /** The API key secret to store for this integration. */ secret: string; } @@ -12,5 +18,7 @@ export interface DataIntegrationsUpsertApiKeyRequest { export interface DataIntegrationsUpsertApiKeyRequestResponse { user_id: string; organization_id?: string; + connected_account_id?: string; + connection_owner?: DataIntegrationsUpsertApiKeyRequestConnectionOwner; secret: string; } diff --git a/src/pipes/interfaces/data-integrations-upsert-client-credentials-request-connection-owner.interface.ts b/src/pipes/interfaces/data-integrations-upsert-client-credentials-request-connection-owner.interface.ts new file mode 100644 index 000000000..ddc710fe8 --- /dev/null +++ b/src/pipes/interfaces/data-integrations-upsert-client-credentials-request-connection-owner.interface.ts @@ -0,0 +1,9 @@ +// This file is auto-generated by oagen. Do not edit. + +export const DataIntegrationsUpsertClientCredentialsRequestConnectionOwner = { + User: 'user', + Organization: 'organization', +} as const; + +export type DataIntegrationsUpsertClientCredentialsRequestConnectionOwner = + (typeof DataIntegrationsUpsertClientCredentialsRequestConnectionOwner)[keyof typeof DataIntegrationsUpsertClientCredentialsRequestConnectionOwner]; diff --git a/src/pipes/interfaces/data-integrations-upsert-client-credentials-request.interface.ts b/src/pipes/interfaces/data-integrations-upsert-client-credentials-request.interface.ts new file mode 100644 index 000000000..200a80fd2 --- /dev/null +++ b/src/pipes/interfaces/data-integrations-upsert-client-credentials-request.interface.ts @@ -0,0 +1,30 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { DataIntegrationsUpsertClientCredentialsRequestConnectionOwner } from './data-integrations-upsert-client-credentials-request-connection-owner.interface'; + +export interface DataIntegrationsUpsertClientCredentialsRequest { + /** A [User](https://workos.com/docs/reference/authkit/user) identifier. */ + userId: string; + /** An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. Required when `connection_owner` is `organization`. */ + organizationId?: string; + /** A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to rotate a specific existing connection. */ + connectedAccountId?: string; + /** Whose connection to create or rotate. `user` (the default) addresses the connection owned by `user_id`. `organization` addresses the connection shared by every member of `organization_id`; `user_id` then identifies the member performing the request and must be an active member of the organization. */ + connectionOwner?: DataIntegrationsUpsertClientCredentialsRequestConnectionOwner; + /** The OAuth client ID to store for this integration. */ + clientId: string; + /** The OAuth client secret to store for this integration. */ + clientSecret: string; + /** Provider-specific configuration values collected for this installation, keyed by the provider's config field descriptors. */ + config?: Record; +} + +export interface DataIntegrationsUpsertClientCredentialsRequestResponse { + user_id: string; + organization_id?: string; + connected_account_id?: string; + connection_owner?: DataIntegrationsUpsertClientCredentialsRequestConnectionOwner; + client_id: string; + client_secret: string; + config?: Record; +} diff --git a/src/pipes/interfaces/data-integrations-vend-credentials-request-connection-owner.interface.ts b/src/pipes/interfaces/data-integrations-vend-credentials-request-connection-owner.interface.ts new file mode 100644 index 000000000..b03e1e494 --- /dev/null +++ b/src/pipes/interfaces/data-integrations-vend-credentials-request-connection-owner.interface.ts @@ -0,0 +1,9 @@ +// This file is auto-generated by oagen. Do not edit. + +export const DataIntegrationsVendCredentialsRequestConnectionOwner = { + User: 'user', + Organization: 'organization', +} as const; + +export type DataIntegrationsVendCredentialsRequestConnectionOwner = + (typeof DataIntegrationsVendCredentialsRequestConnectionOwner)[keyof typeof DataIntegrationsVendCredentialsRequestConnectionOwner]; diff --git a/src/pipes/interfaces/data-integrations-vend-credentials-request.interface.ts b/src/pipes/interfaces/data-integrations-vend-credentials-request.interface.ts index eccd207a6..f660e1fe7 100644 --- a/src/pipes/interfaces/data-integrations-vend-credentials-request.interface.ts +++ b/src/pipes/interfaces/data-integrations-vend-credentials-request.interface.ts @@ -1,13 +1,24 @@ // This file is auto-generated by oagen. Do not edit. +import type { DataIntegrationsVendCredentialsRequestConnectionOwner } from './data-integrations-vend-credentials-request-connection-owner.interface'; + export interface DataIntegrationsVendCredentialsRequest { - /** A [User](https://workos.com/docs/reference/authkit/user) identifier. */ + /** A [User](https://workos.com/docs/reference/authkit/user) identifier. When `connection_owner` is `organization`, this is the user the credentials are vended on behalf of; they must be an active member of the organization. */ userId: string; - /** An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. */ + /** An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. Required when `connection_owner` is `organization`. */ organizationId?: string; + /** A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to select a specific connection when the user has several for this provider. */ + connectedAccountId?: string; + /** Which connection to vend from. `user` (the default) vends the user's own connection and requires `user_id`. `organization` vends the organization's shared connection and requires `organization_id`. */ + connectionOwner?: DataIntegrationsVendCredentialsRequestConnectionOwner; + /** Set to `true` to use the plural connection contract. If no `connected_account_id` is supplied and several connections match, the request returns `account_selection_required`. When omitted or `false`, only the compatibility connection is considered. */ + supportsMultipleConnections?: boolean; } export interface DataIntegrationsVendCredentialsRequestResponse { user_id: string; organization_id?: string; + connected_account_id?: string; + connection_owner?: DataIntegrationsVendCredentialsRequestConnectionOwner; + supports_multiple_connections?: boolean; } diff --git a/src/pipes/interfaces/delete-organization-connected-account-options.interface.ts b/src/pipes/interfaces/delete-organization-connected-account-options.interface.ts new file mode 100644 index 000000000..80030f6a7 --- /dev/null +++ b/src/pipes/interfaces/delete-organization-connected-account-options.interface.ts @@ -0,0 +1,12 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface DeleteOrganizationConnectedAccountOptions { + /** An [Organization](https://workos.com/docs/reference/organization) identifier. */ + organizationId: string; + /** The slug identifier of the provider (e.g., `github`, `slack`, `notion`). */ + slug: string; + /** Set to `true` to use the plural connection contract. When omitted or `false`, only the compatibility connection is considered. */ + supportsMultipleConnections?: boolean; + /** A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to select the connection to delete. */ + connectedAccountId?: string; +} diff --git a/src/pipes/interfaces/delete-organization-data-integration-options.interface.ts b/src/pipes/interfaces/delete-organization-data-integration-options.interface.ts new file mode 100644 index 000000000..64c21ed49 --- /dev/null +++ b/src/pipes/interfaces/delete-organization-data-integration-options.interface.ts @@ -0,0 +1,6 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface DeleteOrganizationDataIntegrationOptions { + /** The slug identifier of the data integration. */ + slug: string; +} diff --git a/src/pipes/interfaces/delete-user-connected-account-options.interface.ts b/src/pipes/interfaces/delete-user-connected-account-options.interface.ts index c044d1f4d..1cfc63189 100644 --- a/src/pipes/interfaces/delete-user-connected-account-options.interface.ts +++ b/src/pipes/interfaces/delete-user-connected-account-options.interface.ts @@ -7,4 +7,8 @@ export interface DeleteUserConnectedAccountOptions { slug: string; /** An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter if the connection is scoped to an organization. */ organizationId?: string; + /** Set to `true` to use the plural connection contract. Defaults to the compatibility connection. */ + supportsMultipleConnections?: boolean; + /** Select the connected account to delete. */ + connectedAccountId?: string; } diff --git a/src/pipes/interfaces/get-access-token-options.interface.ts b/src/pipes/interfaces/get-access-token-options.interface.ts index f5d18d739..edfc175d8 100644 --- a/src/pipes/interfaces/get-access-token-options.interface.ts +++ b/src/pipes/interfaces/get-access-token-options.interface.ts @@ -1,10 +1,8 @@ // This file is auto-generated by oagen. Do not edit. -export interface GetAccessTokenOptions { +import type { DataIntegrationsGetUserTokenRequest } from './data-integrations-get-user-token-request.interface'; + +export interface GetAccessTokenOptions extends DataIntegrationsGetUserTokenRequest { /** The identifier of the integration. */ provider: string; - /** A [User](https://workos.com/docs/reference/authkit/user) identifier. */ - userId: string; - /** An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. */ - organizationId?: string | null; } diff --git a/src/pipes/interfaces/get-organization-connected-account-options.interface.ts b/src/pipes/interfaces/get-organization-connected-account-options.interface.ts new file mode 100644 index 000000000..f20eaf181 --- /dev/null +++ b/src/pipes/interfaces/get-organization-connected-account-options.interface.ts @@ -0,0 +1,12 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface GetOrganizationConnectedAccountOptions { + /** An [Organization](https://workos.com/docs/reference/organization) identifier. */ + organizationId: string; + /** The slug identifier of the provider (e.g., `github`, `slack`, `notion`). */ + slug: string; + /** Set to `true` to use the plural connection contract. When omitted or `false`, only the compatibility connection is considered. */ + supportsMultipleConnections?: boolean; + /** A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to select a specific connection when the organization has several for this provider. */ + connectedAccountId?: string; +} diff --git a/src/pipes/interfaces/get-organization-data-integration-options.interface.ts b/src/pipes/interfaces/get-organization-data-integration-options.interface.ts new file mode 100644 index 000000000..6995b1b09 --- /dev/null +++ b/src/pipes/interfaces/get-organization-data-integration-options.interface.ts @@ -0,0 +1,6 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface GetOrganizationDataIntegrationOptions { + /** The slug identifier of the data integration. */ + slug: string; +} diff --git a/src/pipes/interfaces/get-user-connected-account-options.interface.ts b/src/pipes/interfaces/get-user-connected-account-options.interface.ts index e35ebc59c..6ddc0d129 100644 --- a/src/pipes/interfaces/get-user-connected-account-options.interface.ts +++ b/src/pipes/interfaces/get-user-connected-account-options.interface.ts @@ -7,4 +7,8 @@ export interface GetUserConnectedAccountOptions { slug: string; /** An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter if the connection is scoped to an organization. */ organizationId?: string; + /** Set to `true` to use the plural connection contract. Defaults to the compatibility connection. */ + supportsMultipleConnections?: boolean; + /** Select a specific connected account when the user has several for this provider. */ + connectedAccountId?: string; } diff --git a/src/pipes/interfaces/index.ts b/src/pipes/interfaces/index.ts index ef2be9722..e8ba841e1 100644 --- a/src/pipes/interfaces/index.ts +++ b/src/pipes/interfaces/index.ts @@ -1,53 +1,86 @@ // This file is auto-generated by oagen. Do not edit. export * from './access-token.interface'; +export * from './api-key-installation.interface'; export * from './authorize-data-integration-options.interface'; export * from './connected-account-auth-method.interface'; +export * from './connected-account-connection-role.interface'; export * from './connected-account-dto.interface'; +export * from './connected-account-input-state.interface'; +export * from './connected-account-input.interface'; export * from './connected-account-state.interface'; export * from './connected-account.interface'; +export * from './create-data-integration-auth-methods.interface'; export * from './create-data-integration-credential-options.interface'; export * from './create-data-integration-options.interface'; +export * from './create-data-integration-ownership.interface'; export * from './create-data-integration-token-options.interface'; export * from './create-data-integration.interface'; +export * from './create-organization-connected-account-options.interface'; export * from './create-user-connected-account-options.interface'; export * from './custom-provider-definition-authenticate-via.interface'; export * from './custom-provider-definition.interface'; export * from './data-integration-access-token-response-access-token.interface'; export * from './data-integration-access-token-response-error.interface'; export * from './data-integration-access-token-response.interface'; +export * from './data-integration-auth-methods.interface'; export * from './data-integration-authorize-url-response.interface'; export * from './data-integration-credential-type.interface'; export * from './data-integration-credential.interface'; export * from './data-integration-credentials-dto.interface'; +export * from './data-integration-credentials-input-type.interface'; +export * from './data-integration-credentials-input.interface'; export * from './data-integration-credentials-response-credential.interface'; export * from './data-integration-credentials-response-error.interface'; +export * from './data-integration-credentials-response.interface'; export * from './data-integration-credentials-type.interface'; export * from './data-integration-custom-provider-authenticate-via.interface'; export * from './data-integration-custom-provider.interface'; +export * from './data-integration-installation-connection-role.interface'; +export * from './data-integration-installation.interface'; +export * from './data-integration-ownership.interface'; export * from './data-integration-state.interface'; export * from './data-integration.interface'; +export * from './data-integrations-get-data-integration-authorize-url-request-connection-owner.interface'; export * from './data-integrations-get-data-integration-authorize-url-request.interface'; +export * from './data-integrations-get-user-token-request-connection-owner.interface'; export * from './data-integrations-get-user-token-request.interface'; export * from './data-integrations-list-response-data-auth-methods.interface'; export * from './data-integrations-list-response-data-connected-account-auth-method.interface'; +export * from './data-integrations-list-response-data-connected-account-connection-role.interface'; export * from './data-integrations-list-response-data-connected-account-state.interface'; export * from './data-integrations-list-response-data-connected-account.interface'; +export * from './data-integrations-list-response-data-connection-owner.interface'; export * from './data-integrations-list-response-data-ownership.interface'; export * from './data-integrations-list-response-data.interface'; export * from './data-integrations-list-response.interface'; +export * from './data-integrations-upsert-api-key-request-connection-owner.interface'; export * from './data-integrations-upsert-api-key-request.interface'; +export * from './data-integrations-upsert-client-credentials-request-connection-owner.interface'; +export * from './data-integrations-upsert-client-credentials-request.interface'; +export * from './data-integrations-vend-credentials-request-connection-owner.interface'; export * from './data-integrations-vend-credentials-request.interface'; export * from './delete-data-integration-options.interface'; +export * from './delete-organization-connected-account-options.interface'; +export * from './delete-organization-data-integration-options.interface'; export * from './delete-user-connected-account-options.interface'; export * from './get-access-token-options.interface'; export * from './get-data-integration-options.interface'; +export * from './get-organization-connected-account-options.interface'; +export * from './get-organization-data-integration-options.interface'; export * from './get-user-connected-account-options.interface'; export * from './list-data-integrations-options.interface'; +export * from './list-organization-data-providers-options.interface'; export * from './list-user-data-providers-options.interface'; +export * from './organization-connected-account-state.interface'; +export * from './organization-connected-account.interface'; +export * from './pipes-ownership.interface'; export * from './update-custom-provider-definition-authenticate-via.interface'; export * from './update-custom-provider-definition.interface'; export * from './update-data-integration-api-key-options.interface'; +export * from './update-data-integration-client-credentials-options.interface'; export * from './update-data-integration-options.interface'; export * from './update-data-integration.interface'; +export * from './update-organization-connected-account-options.interface'; +export * from './update-organization-data-integration-options.interface'; export * from './update-user-connected-account-options.interface'; diff --git a/src/pipes/interfaces/list-data-integrations-options.interface.ts b/src/pipes/interfaces/list-data-integrations-options.interface.ts index 9b6b684e4..47508121d 100644 --- a/src/pipes/interfaces/list-data-integrations-options.interface.ts +++ b/src/pipes/interfaces/list-data-integrations-options.interface.ts @@ -2,4 +2,9 @@ import type { PaginationOptions } from '../../common/interfaces/pagination-options.interface'; -export type ListDataIntegrationsOptions = PaginationOptions; +import type { PipesOwnership } from './pipes-ownership.interface'; + +export interface ListDataIntegrationsOptions extends PaginationOptions { + /** Filter integrations by who owns their connected accounts. */ + ownership?: PipesOwnership; +} diff --git a/src/pipes/interfaces/list-organization-data-providers-options.interface.ts b/src/pipes/interfaces/list-organization-data-providers-options.interface.ts new file mode 100644 index 000000000..b2c1ff0fe --- /dev/null +++ b/src/pipes/interfaces/list-organization-data-providers-options.interface.ts @@ -0,0 +1,8 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface ListOrganizationDataProvidersOptions { + /** An [Organization](https://workos.com/docs/reference/organization) identifier to list providers and connected accounts for. */ + organizationId: string; + /** Set to `true` to use the plural connection contract. When omitted or `false`, only the compatibility connection is considered. */ + supportsMultipleConnections?: boolean; +} diff --git a/src/pipes/interfaces/list-user-data-providers-options.interface.ts b/src/pipes/interfaces/list-user-data-providers-options.interface.ts index b51dc0dde..00556ffda 100644 --- a/src/pipes/interfaces/list-user-data-providers-options.interface.ts +++ b/src/pipes/interfaces/list-user-data-providers-options.interface.ts @@ -5,4 +5,6 @@ export interface ListUserDataProvidersOptions { userId: string; /** An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to filter connections for a specific organization. */ organizationId?: string; + /** Set to `true` to include all connected accounts rather than only the compatibility connection. */ + supportsMultipleConnections?: boolean; } diff --git a/src/pipes/interfaces/organization-connected-account-state.interface.ts b/src/pipes/interfaces/organization-connected-account-state.interface.ts new file mode 100644 index 000000000..db3dcf72f --- /dev/null +++ b/src/pipes/interfaces/organization-connected-account-state.interface.ts @@ -0,0 +1,9 @@ +// This file is auto-generated by oagen. Do not edit. + +export const OrganizationConnectedAccountState = { + Connected: 'connected', + NeedsReauthorization: 'needs_reauthorization', +} as const; + +export type OrganizationConnectedAccountState = + (typeof OrganizationConnectedAccountState)[keyof typeof OrganizationConnectedAccountState]; diff --git a/src/pipes/interfaces/organization-connected-account.interface.ts b/src/pipes/interfaces/organization-connected-account.interface.ts new file mode 100644 index 000000000..31f9ddb56 --- /dev/null +++ b/src/pipes/interfaces/organization-connected-account.interface.ts @@ -0,0 +1,27 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { OrganizationConnectedAccountState } from './organization-connected-account-state.interface'; + +export interface OrganizationConnectedAccount { + /** The OAuth access token for the connected account. */ + accessToken?: string; + /** The OAuth refresh token for the connected account. */ + refreshToken?: string; + /** The ISO-8601 timestamp when the access token expires. Required when `access_token` is provided for tokens that expire. */ + expiresAt?: Date; + /** The OAuth scopes granted for this connection. */ + scopes?: string[]; + /** Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. */ + state?: OrganizationConnectedAccountState; + /** The [User](https://workos.com/docs/reference/authkit/user) identifier of the organization member on whose behalf the connected account is being imported or updated. The user must be an active member of the organization. */ + userId: string; +} + +export interface OrganizationConnectedAccountResponse { + access_token?: string; + refresh_token?: string; + expires_at?: string; + scopes?: string[]; + state?: OrganizationConnectedAccountState; + user_id: string; +} diff --git a/src/pipes/interfaces/pipes-ownership.interface.ts b/src/pipes/interfaces/pipes-ownership.interface.ts new file mode 100644 index 000000000..f8e19d55a --- /dev/null +++ b/src/pipes/interfaces/pipes-ownership.interface.ts @@ -0,0 +1,9 @@ +// This file is auto-generated by oagen. Do not edit. + +export const PipesOwnership = { + User: 'user', + Organization: 'organization', +} as const; + +export type PipesOwnership = + (typeof PipesOwnership)[keyof typeof PipesOwnership]; diff --git a/src/pipes/interfaces/update-custom-provider-definition.interface.ts b/src/pipes/interfaces/update-custom-provider-definition.interface.ts index ed7804877..7c2644bda 100644 --- a/src/pipes/interfaces/update-custom-provider-definition.interface.ts +++ b/src/pipes/interfaces/update-custom-provider-definition.interface.ts @@ -5,11 +5,11 @@ import type { UpdateCustomProviderDefinitionAuthenticateVia } from './update-cus export interface UpdateCustomProviderDefinition { /** A descriptive name for the custom provider. */ name?: string; - /** The provider's OAuth authorization endpoint. */ + /** The provider's OAuth authorization endpoint. Must be a static URL: `${config.…}` placeholders are resolved against a provider's declared config fields, which custom providers cannot declare. */ authorizationUrl?: string; - /** The provider's OAuth token endpoint. */ + /** The provider's OAuth token endpoint. Must be a static URL: `${config.…}` placeholders are resolved against a provider's declared config fields, which custom providers cannot declare. */ tokenUrl?: string; - /** The endpoint used to refresh tokens, if different from the token endpoint. */ + /** The endpoint used to refresh tokens, if different from the token endpoint. Must be a static URL, like the other endpoints. */ refreshTokenUrl?: string | null; /** Whether PKCE is used during the authorization code flow. */ pkceEnabled?: boolean; diff --git a/src/pipes/interfaces/update-data-integration-api-key-options.interface.ts b/src/pipes/interfaces/update-data-integration-api-key-options.interface.ts index 8731fa0ed..354f59f00 100644 --- a/src/pipes/interfaces/update-data-integration-api-key-options.interface.ts +++ b/src/pipes/interfaces/update-data-integration-api-key-options.interface.ts @@ -1,12 +1,8 @@ // This file is auto-generated by oagen. Do not edit. -export interface UpdateDataIntegrationApiKeyOptions { +import type { DataIntegrationsUpsertApiKeyRequest } from './data-integrations-upsert-api-key-request.interface'; + +export interface UpdateDataIntegrationApiKeyOptions extends DataIntegrationsUpsertApiKeyRequest { /** The identifier of the integration. */ slug: string; - /** A [User](https://workos.com/docs/reference/authkit/user) identifier. */ - userId: string; - /** An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. */ - organizationId?: string; - /** The API key secret to store for this integration. */ - secret: string; } diff --git a/src/pipes/interfaces/update-data-integration-client-credentials-options.interface.ts b/src/pipes/interfaces/update-data-integration-client-credentials-options.interface.ts new file mode 100644 index 000000000..479ca69c3 --- /dev/null +++ b/src/pipes/interfaces/update-data-integration-client-credentials-options.interface.ts @@ -0,0 +1,8 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { DataIntegrationsUpsertClientCredentialsRequest } from './data-integrations-upsert-client-credentials-request.interface'; + +export interface UpdateDataIntegrationClientCredentialsOptions extends DataIntegrationsUpsertClientCredentialsRequest { + /** The identifier of the integration. */ + slug: string; +} diff --git a/src/pipes/interfaces/update-data-integration-options.interface.ts b/src/pipes/interfaces/update-data-integration-options.interface.ts index 50272096c..42fe0db6d 100644 --- a/src/pipes/interfaces/update-data-integration-options.interface.ts +++ b/src/pipes/interfaces/update-data-integration-options.interface.ts @@ -1,19 +1,8 @@ // This file is auto-generated by oagen. Do not edit. -import type { DataIntegrationCredentialsDto } from './data-integration-credentials-dto.interface'; -import type { UpdateCustomProviderDefinition } from './update-custom-provider-definition.interface'; +import type { UpdateDataIntegration } from './update-data-integration.interface'; -export interface UpdateDataIntegrationOptions { +export interface UpdateDataIntegrationOptions extends UpdateDataIntegration { /** The slug identifier of the data integration. */ slug: string; - /** An optional description of the Data Integration. */ - description?: string | null; - /** Whether the Data Integration is enabled. */ - enabled?: boolean; - /** The OAuth scopes to request for the Data Integration. Pass `null` to reset to the provider's configured scopes. */ - scopes?: string[] | null; - /** New credentials for the Data Integration. When provided, rotates the stored client secret. */ - credentials?: DataIntegrationCredentialsDto; - /** Updates to a custom provider's OAuth definition. Only valid for custom-provider integrations. */ - customProvider?: UpdateCustomProviderDefinition; } diff --git a/src/pipes/interfaces/update-data-integration.interface.ts b/src/pipes/interfaces/update-data-integration.interface.ts index 08ec172ed..54170dda8 100644 --- a/src/pipes/interfaces/update-data-integration.interface.ts +++ b/src/pipes/interfaces/update-data-integration.interface.ts @@ -1,9 +1,13 @@ // This file is auto-generated by oagen. Do not edit. import type { - DataIntegrationCredentialsDto, - DataIntegrationCredentialsDtoResponse, -} from './data-integration-credentials-dto.interface'; + DataIntegrationCredentialsInput, + DataIntegrationCredentialsInputResponse, +} from './data-integration-credentials-input.interface'; +import type { + ApiKeyInstallation, + ApiKeyInstallationResponse, +} from './api-key-installation.interface'; import type { UpdateCustomProviderDefinition, UpdateCustomProviderDefinitionResponse, @@ -16,8 +20,10 @@ export interface UpdateDataIntegration { enabled?: boolean; /** The OAuth scopes to request for the Data Integration. Pass `null` to reset to the provider's configured scopes. */ scopes?: string[] | null; - /** New credentials for the Data Integration. When provided, rotates the stored client secret. */ - credentials?: DataIntegrationCredentialsDto; + /** New OAuth credentials for the Data Integration. When provided, rotates the stored client secret. Mutually exclusive with `api_key`. */ + credentials?: DataIntegrationCredentialsInput; + /** An API key to install or rotate for a tenant on an `api_key` integration. Upserts the tenant installation identified by `user_id` (and optional `organization_id`). */ + apiKey?: ApiKeyInstallation; /** Updates to a custom provider's OAuth definition. Only valid for custom-provider integrations. */ customProvider?: UpdateCustomProviderDefinition; } @@ -26,6 +32,7 @@ export interface UpdateDataIntegrationResponse { description?: string | null; enabled?: boolean; scopes?: string[] | null; - credentials?: DataIntegrationCredentialsDtoResponse; + credentials?: DataIntegrationCredentialsInputResponse; + api_key?: ApiKeyInstallationResponse; custom_provider?: UpdateCustomProviderDefinitionResponse; } diff --git a/src/pipes/interfaces/update-organization-connected-account-options.interface.ts b/src/pipes/interfaces/update-organization-connected-account-options.interface.ts new file mode 100644 index 000000000..be67a4d68 --- /dev/null +++ b/src/pipes/interfaces/update-organization-connected-account-options.interface.ts @@ -0,0 +1,26 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { OrganizationConnectedAccountState } from './organization-connected-account-state.interface'; + +export interface UpdateOrganizationConnectedAccountOptions { + /** An [Organization](https://workos.com/docs/reference/organization) identifier. */ + organizationId: string; + /** The slug identifier of the provider (e.g., `github`, `slack`, `notion`). */ + slug: string; + /** Set to `true` to use the plural connection contract. When omitted or `false`, only the compatibility connection is considered. */ + supportsMultipleConnections?: boolean; + /** A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to select the connection to update. */ + connectedAccountId?: string; + /** The OAuth access token for the connected account. */ + accessToken?: string; + /** The OAuth refresh token for the connected account. */ + refreshToken?: string; + /** The ISO-8601 timestamp when the access token expires. Required when `access_token` is provided for tokens that expire. */ + expiresAt?: Date; + /** The OAuth scopes granted for this connection. */ + scopes?: string[]; + /** Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. */ + state?: OrganizationConnectedAccountState; + /** The [User](https://workos.com/docs/reference/authkit/user) identifier of the organization member on whose behalf the connected account is being imported or updated. The user must be an active member of the organization. */ + userId: string; +} diff --git a/src/pipes/interfaces/update-organization-data-integration-options.interface.ts b/src/pipes/interfaces/update-organization-data-integration-options.interface.ts new file mode 100644 index 000000000..fa1ffdbc0 --- /dev/null +++ b/src/pipes/interfaces/update-organization-data-integration-options.interface.ts @@ -0,0 +1,6 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { UpdateDataIntegrationOptions } from './update-data-integration-options.interface'; + +export type UpdateOrganizationDataIntegrationOptions = + UpdateDataIntegrationOptions; diff --git a/src/pipes/interfaces/update-user-connected-account-options.interface.ts b/src/pipes/interfaces/update-user-connected-account-options.interface.ts index 6ab1ab3af..ceeb581f5 100644 --- a/src/pipes/interfaces/update-user-connected-account-options.interface.ts +++ b/src/pipes/interfaces/update-user-connected-account-options.interface.ts @@ -1,6 +1,6 @@ // This file is auto-generated by oagen. Do not edit. -import type { ConnectedAccountState } from './connected-account-state.interface'; +import type { ConnectedAccountInputState } from './connected-account-input-state.interface'; export interface UpdateUserConnectedAccountOptions { /** A [User](https://workos.com/docs/reference/authkit/user) identifier. */ @@ -9,6 +9,10 @@ export interface UpdateUserConnectedAccountOptions { slug: string; /** An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter if the connection is scoped to an organization. */ organizationId?: string; + /** Set to `true` to use the plural connection contract. Defaults to the compatibility connection. */ + supportsMultipleConnections?: boolean; + /** Select the connected account to update. */ + connectedAccountId?: string; /** The OAuth access token for the connected account. */ accessToken?: string; /** The OAuth refresh token for the connected account. */ @@ -18,5 +22,5 @@ export interface UpdateUserConnectedAccountOptions { /** The OAuth scopes granted for this connection. */ scopes?: string[]; /** Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. */ - state?: ConnectedAccountState; + state?: ConnectedAccountInputState; } diff --git a/src/pipes/pipes-requests.spec.ts b/src/pipes/pipes-requests.spec.ts new file mode 100644 index 000000000..dee1e9119 --- /dev/null +++ b/src/pipes/pipes-requests.spec.ts @@ -0,0 +1,448 @@ +// @oagen-ignore-file +// Request/response regressions that the generated smoke tests do not cover. +import fetch from 'jest-fetch-mock'; +import type { DataIntegrationCredentialsResponse } from '../index'; +import { WorkOS } from '../workos'; +import { + fetchBody, + fetchOnce, + fetchSearchParams, + fetchURL, +} from '../common/utils/test-utils'; +import connectedAccountFixture from './fixtures/connected-account.json'; +import dataIntegrationFixture from './fixtures/data-integration.json'; +import installationFixture from './fixtures/data-integration-installation.json'; +import listDataIntegrationFixture from './fixtures/list-data-integration.json'; +import providersFixture from './fixtures/data-integrations-list-response.json'; + +const workos = new WorkOS('sk_test_example'); +const userId = 'user_123'; +const organizationId = 'org_123'; +const connectedAccountId = 'data_installation_123'; +const apiKey = { userId, organizationId, secret: 'test_api_key' }; +const serializedApiKey = { + user_id: userId, + organization_id: organizationId, + secret: 'test_api_key', +}; +const connectionOptions = { + userId, + organizationId, + connectedAccountId, + connectionOwner: 'organization' as const, +}; +const serializedConnectionOptions = { + user_id: userId, + organization_id: organizationId, + connected_account_id: connectedAccountId, + connection_owner: 'organization', +}; + +beforeEach(() => fetch.resetMocks()); + +describe('Pipes request contracts', () => { + it('preserves the ownership filter across automatic pagination', async () => { + fetchOnce(listDataIntegrationFixture); + fetchOnce({ + ...listDataIntegrationFixture, + list_metadata: { before: null, after: 'next' }, + }); + fetchOnce(listDataIntegrationFixture); + + const list = await workos.pipes.listDataIntegrations({ + ownership: 'organization', + }); + await list.autoPagination(); + + expect( + fetch.mock.calls.map(([url]) => + Object.fromEntries(new URL(String(url)).searchParams), + ), + ).toEqual([ + { ownership: 'organization', order: 'desc' }, + { ownership: 'organization', order: 'desc', limit: '100' }, + { ownership: 'organization', order: 'desc', limit: '100', after: 'next' }, + ]); + }); + + it('serializes integration ownership, auth methods, config, and OAuth credentials', async () => { + fetchOnce(dataIntegrationFixture); + await workos.pipes.createDataIntegration({ + provider: 'snowflake', + ownership: 'organization', + enabled: false, + scopes: null, + authMethods: ['oauth'], + config: { account: 'acme' }, + credentials: { + type: 'custom', + clientId: 'client', + clientSecret: 'secret', + }, + }); + + expect(fetchBody()).toEqual({ + provider: 'snowflake', + ownership: 'organization', + enabled: false, + scopes: null, + auth_methods: ['oauth'], + config: { account: 'acme' }, + credentials: { + type: 'custom', + client_id: 'client', + client_secret: 'secret', + }, + }); + }); + + it('does not invent auth methods or config when optional response fields are absent', async () => { + const { auth_methods, config, installation, ...response } = + dataIntegrationFixture; + fetchOnce(response); + const result = await workos.pipes.getDataIntegration({ slug: 'github' }); + expect(result.authMethods).toBeUndefined(); + expect(result.config).toBeUndefined(); + expect(result.installation).toBeNull(); + }); + + it('creates an API-key integration and deserializes its initial installation', async () => { + fetchOnce({ + ...dataIntegrationFixture, + credentials: null, + auth_methods: ['api_key'], + installation: installationFixture, + }); + const result = await workos.pipes.createDataIntegration({ + provider: 'custom-api', + authMethods: ['api_key'], + apiKey, + customProvider: { name: 'Custom API' }, + }); + + expect(fetchBody()).toEqual({ + provider: 'custom-api', + auth_methods: ['api_key'], + api_key: serializedApiKey, + custom_provider: { name: 'Custom API' }, + }); + expect(result.credentials).toBeNull(); + expect(result.installation).toEqual({ + id: installationFixture.id, + connectionRole: 'compatibility', + accountIdentifier: 'workspace_123', + accountDisplayName: 'Acme production', + userId: installationFixture.user_id, + organizationId: null, + apiKeyLast4: 'cdef', + }); + }); + + it.each([ + 'updateDataIntegration', + 'updateOrganizationDataIntegration', + ] as const)( + '%s serializes API-key rotation and explicit null/false values', + async (method) => { + fetchOnce({ ...dataIntegrationFixture, credentials: null }); + await workos.pipes[method]({ + slug: 'custom-api', + apiKey, + enabled: false, + scopes: null, + }); + expect(fetchBody()).toEqual({ + api_key: serializedApiKey, + enabled: false, + scopes: null, + }); + }, + ); + + it('authorizes an organization connection with installation config', async () => { + fetchOnce({ url: 'https://provider.example/authorize' }); + await workos.pipes.authorizeDataIntegration({ + slug: 'zendesk', + userId, + organizationId, + connectionOwner: 'organization', + config: { subdomain: 'acme' }, + returnTo: 'https://example.com/callback', + }); + expect(fetchBody()).toEqual({ + user_id: userId, + organization_id: organizationId, + connection_owner: 'organization', + config: { subdomain: 'acme' }, + return_to: 'https://example.com/callback', + }); + }); + + it('selects an organization connection when rotating an API key', async () => { + fetchOnce(connectedAccountFixture); + await workos.pipes.updateDataIntegrationApiKey({ + slug: 'custom-api', + ...connectionOptions, + secret: 'test_secret', + }); + expect(fetchBody()).toEqual({ + ...serializedConnectionOptions, + secret: 'test_secret', + }); + }); + + it('serializes client credentials and deserializes their connection metadata', async () => { + fetchOnce({ + ...connectedAccountFixture, + auth_method: 'client_credentials', + user_id: null, + organization_id: organizationId, + }); + const result = await workos.pipes.updateDataIntegrationClientCredentials({ + slug: 'salesforce', + ...connectionOptions, + clientId: 'client', + clientSecret: 'secret', + config: { salesforce_host: 'acme.my.salesforce.com' }, + }); + expect(fetchBody()).toEqual({ + ...serializedConnectionOptions, + client_id: 'client', + client_secret: 'secret', + config: { salesforce_host: 'acme.my.salesforce.com' }, + }); + expect(result).toMatchObject({ + authMethod: 'client_credentials', + userId: null, + organizationId, + clientId: connectedAccountFixture.client_id, + clientSecretLast4: connectedAccountFixture.client_secret_last_4, + config: connectedAccountFixture.config, + }); + }); + + it.each(['getAccessToken', 'createDataIntegrationCredential'] as const)( + '%s sends ownership, connection selection, and explicit false', + async (method) => { + fetchOnce({ active: false, error: 'not_installed' }); + const result = await workos.pipes[method]({ + provider: 'github', + slug: 'github', + ...connectionOptions, + supportsMultipleConnections: false, + }); + expect(fetchBody()).toEqual({ + ...serializedConnectionOptions, + supports_multiple_connections: false, + }); + expect(result).toEqual({ active: false, error: 'not_installed' }); + }, + ); + + describe.each([ + [ + 'getUserConnectedAccount', + '/user_management/users/user_123/connected_accounts/provider%2Fslug', + { organization_id: organizationId }, + ], + [ + 'updateUserConnectedAccount', + '/user_management/users/user_123/connected_accounts/provider%2Fslug', + { organization_id: organizationId }, + ], + [ + 'deleteUserConnectedAccount', + '/user_management/users/user_123/connected_accounts/provider%2Fslug', + { organization_id: organizationId }, + ], + [ + 'getOrganizationConnectedAccount', + '/organizations/org_123/connected_accounts/provider%2Fslug', + {}, + ], + [ + 'updateOrganizationConnectedAccount', + '/organizations/org_123/connected_accounts/provider%2Fslug', + {}, + ], + [ + 'deleteOrganizationConnectedAccount', + '/organizations/org_123/connected_accounts/provider%2Fslug', + {}, + ], + ] as const)('%s query parameters', (method, pathname, query) => { + it.each([true, false, undefined])( + 'serializes supportsMultipleConnections=%s', + async (supportsMultipleConnections) => { + fetchOnce(connectedAccountFixture); + await workos.pipes[method]({ + userId, + organizationId, + slug: 'provider/slug', + supportsMultipleConnections, + connectedAccountId, + }); + expect(new URL(String(fetchURL())).pathname).toBe(pathname); + expect(fetchSearchParams()).toEqual({ + ...query, + connected_account_id: connectedAccountId, + ...(supportsMultipleConnections !== undefined && { + supports_multiple_connections: String(supportsMultipleConnections), + }), + }); + }, + ); + }); + + it.each([ + 'createUserConnectedAccount', + 'updateUserConnectedAccount', + 'createOrganizationConnectedAccount', + 'updateOrganizationConnectedAccount', + ] as const)( + '%s serializes tokens and dates without leaking path/query fields', + async (method) => { + fetchOnce(connectedAccountFixture); + await workos.pipes[method]({ + userId, + organizationId, + slug: 'github', + accessToken: 'token', + refreshToken: 'refresh', + expiresAt: new Date('2026-12-31T00:00:00.000Z'), + scopes: ['repo'], + state: 'connected', + }); + expect(fetchBody()).toEqual({ + access_token: 'token', + refresh_token: 'refresh', + expires_at: '2026-12-31T00:00:00.000Z', + scopes: ['repo'], + state: 'connected', + ...(method.includes('Organization') && { user_id: userId }), + }); + }, + ); + + it.each(['listUserDataProviders', 'listOrganizationDataProviders'] as const)( + '%s forwards plural opt-in and deserializes every connected account', + async (method) => { + fetchOnce(providersFixture); + const result = await workos.pipes[method]({ + userId, + organizationId, + supportsMultipleConnections: true, + }); + expect(fetchSearchParams()).toEqual({ + supports_multiple_connections: 'true', + ...(method === 'listUserDataProviders' && { + organization_id: organizationId, + }), + }); + expect(result.data[0].connectionOwner).toBe('user'); + expect(result.data[0].connectedAccounts).toEqual([ + result.data[0].connectedAccount, + ]); + expect(result.data[0].connectedAccounts?.[0]).toMatchObject({ + connectionRole: 'compatibility', + accountIdentifier: 'workspace_123', + accountDisplayName: 'Acme production', + }); + }, + ); +}); + +describe('Pipes credential response variants', () => { + it.each(['oauth', 'client_credentials'] as const)( + 'deserializes %s access tokens', + async (authMethod) => { + const metadata = { instance_url: 'https://acme.my.salesforce.com' }; + fetchOnce({ + active: true, + credential: { + object: 'credential', + auth_method: authMethod, + value: 'token', + expires_at: null, + scopes: ['api'], + missing_scopes: [], + ...(authMethod === 'client_credentials' && { metadata }), + }, + }); + const result: DataIntegrationCredentialsResponse = + await workos.pipes.createDataIntegrationCredential({ + slug: 'provider', + userId, + }); + expect(result).toEqual({ + active: true, + credential: { + object: 'credential', + authMethod, + value: 'token', + expiresAt: null, + scopes: ['api'], + missingScopes: [], + ...(authMethod === 'client_credentials' && { metadata }), + }, + }); + if ( + result.active && + result.credential.authMethod === 'client_credentials' + ) { + expect(result.credential.metadata).toEqual(metadata); + } + }, + ); + + it('returns an API key without invented token fields', async () => { + fetchOnce({ + active: true, + credential: { + object: 'credential', + auth_method: 'api_key', + value: 'secret', + }, + }); + const result = await workos.pipes.createDataIntegrationCredential({ + slug: 'provider', + userId, + }); + expect(result).toEqual({ + active: true, + credential: { + object: 'credential', + authMethod: 'api_key', + value: 'secret', + }, + }); + }); + + it.each(['not_installed', 'needs_reauthorization'])( + 'returns an inactive %s response', + async (error) => { + fetchOnce({ active: false, error }); + const result = await workos.pipes.createDataIntegrationCredential({ + slug: 'provider', + userId, + }); + expect(result).toEqual({ active: false, error }); + }, + ); + + it.each([ + [{ active: 'unexpected' }, 'Unknown active: unexpected'], + [ + { active: true, credential: { auth_method: 'unexpected' } }, + 'Unknown auth_method: unexpected', + ], + ])('rejects an unknown discriminator in %j', async (response, message) => { + fetchOnce(response); + await expect( + workos.pipes.createDataIntegrationCredential({ + slug: 'provider', + userId, + }), + ).rejects.toThrow(message); + }); +}); diff --git a/src/pipes/pipes.spec.ts b/src/pipes/pipes.spec.ts index bc8d96c93..44226e050 100644 --- a/src/pipes/pipes.spec.ts +++ b/src/pipes/pipes.spec.ts @@ -25,6 +25,7 @@ function expectDataIntegration(result: any) { expect(result.id).toBe('data_integration_01EHZNVPK3SFK441A1RGBFSHRT'); expect(result.slug).toBe('github'); expect(result.integrationType).toBe('github'); + expect(result.ownership).toBe('user'); expect(result.description).toBe('Production GitHub app'); expect(result.enabled).toBe(true); expect(result.state).toBe('valid'); @@ -32,6 +33,9 @@ function expectDataIntegration(result: any) { expect(result.redirectUri).toBe( 'https://api.workos.com/data-integrations/github/dik_01EHZNVPK3SFK441A1RGBFSHRT/callback', ); + expect(result.authMethods).toEqual(['oauth']); + expect(result.installation).toBeNull(); + expect(result.config).toEqual({ account: 'myorg-myaccount' }); expect(result.createdAt.toISOString()).toBe('2026-01-15T12:00:00.000Z'); expect(result.updatedAt.toISOString()).toBe('2026-01-15T12:00:00.000Z'); } @@ -39,6 +43,9 @@ function expectDataIntegration(result: any) { function expectConnectedAccount(result: any) { expect(result.object).toBe('connected_account'); expect(result.id).toBe('data_installation_01EHZNVPK3SFK441A1RGBFSHRT'); + expect(result.connectionRole).toBe('compatibility'); + expect(result.accountIdentifier).toBe('workspace_123'); + expect(result.accountDisplayName).toBe('Acme production'); expect(result.userId).toBe('user_01EHZNVPK3SFK441A1RGBFSHRT'); expect(result.organizationId).toBeNull(); expect(result.scopes).toEqual(['repo', 'user:email']); @@ -47,6 +54,10 @@ function expectConnectedAccount(result: any) { expect(result.updatedAt).toBe('2024-01-16T14:20:00.000Z'); } +function expectDataIntegrationsListResponse(result: any) { + expect(result.object).toBe('list'); +} + describe('Pipes', () => { beforeEach(() => fetch.resetMocks()); @@ -56,6 +67,7 @@ describe('Pipes', () => { const { data, listMetadata } = await workos.pipes.listDataIntegrations({ order: 'desc', + ownership: 'user', }); expect(fetchMethod()).toBe('GET'); @@ -184,6 +196,32 @@ describe('Pipes', () => { }); }); + describe('updateDataIntegrationClientCredentials', () => { + it('sends the correct request and returns result', async () => { + fetchOnce(connectedAccountFixture); + + const result = await workos.pipes.updateDataIntegrationClientCredentials({ + slug: 'test_slug', + userId: 'user_id_01234', + clientId: 'client_id_01234', + clientSecret: 'test_client_secret', + }); + + expect(fetchMethod()).toBe('PUT'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/data-integrations/test_slug/client-credentials', + ); + expect(fetchBody()).toEqual( + expect.objectContaining({ + user_id: 'user_id_01234', + client_id: 'client_id_01234', + client_secret: 'test_client_secret', + }), + ); + expectConnectedAccount(result); + }); + }); + describe('createDataIntegrationCredential', () => { it('sends the correct request and returns result', async () => { fetchOnce(dataIntegrationCredentialsResponseFixture); @@ -204,6 +242,61 @@ describe('Pipes', () => { }); }); + describe('getOrganizationDataIntegration', () => { + it('returns the expected result', async () => { + fetchOnce(dataIntegrationFixture); + + const result = await workos.pipes.getOrganizationDataIntegration({ + slug: 'test_slug', + }); + + expect(fetchMethod()).toBe('GET'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/data-integrations/test_slug/organization', + ); + expectDataIntegration(result); + }); + }); + + describe('updateOrganizationDataIntegration', () => { + it('sends the correct request and returns result', async () => { + fetchOnce(dataIntegrationFixture); + + const result = await workos.pipes.updateOrganizationDataIntegration({ + slug: 'test_slug', + enabled: true, + credentials: { type: 'custom' }, + }); + + expect(fetchMethod()).toBe('PUT'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/data-integrations/test_slug/organization', + ); + expect(fetchBody()).toEqual( + expect.objectContaining({ + enabled: true, + credentials: { type: 'custom' }, + }), + ); + expectDataIntegration(result); + }); + }); + + describe('deleteOrganizationDataIntegration', () => { + it('sends a DELETE request', async () => { + fetchOnce({}, { status: 204 }); + + await workos.pipes.deleteOrganizationDataIntegration({ + slug: 'test_slug', + }); + + expect(fetchMethod()).toBe('DELETE'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/data-integrations/test_slug/organization', + ); + }); + }); + describe('getAccessToken', () => { it('sends the correct request and returns result', async () => { fetchOnce(dataIntegrationAccessTokenResponseFixture); @@ -224,6 +317,104 @@ describe('Pipes', () => { }); }); + describe('getOrganizationConnectedAccount', () => { + it('returns the expected result', async () => { + fetchOnce(connectedAccountFixture); + + const result = await workos.pipes.getOrganizationConnectedAccount({ + organizationId: 'test_organizationId', + slug: 'test_slug', + supportsMultipleConnections: true, + connectedAccountId: 'data_installation_01EHZNVPK3SFK441A1RGBFSHRT', + }); + + expect(fetchMethod()).toBe('GET'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/organizations/test_organizationId/connected_accounts/test_slug', + ); + expectConnectedAccount(result); + }); + }); + + describe('createOrganizationConnectedAccount', () => { + it('sends the correct request and returns result', async () => { + fetchOnce(connectedAccountFixture); + + const result = await workos.pipes.createOrganizationConnectedAccount({ + organizationId: 'test_organizationId', + slug: 'test_slug', + userId: 'user_id_01234', + }); + + expect(fetchMethod()).toBe('POST'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/organizations/test_organizationId/connected_accounts/test_slug', + ); + expect(fetchBody()).toEqual( + expect.objectContaining({ user_id: 'user_id_01234' }), + ); + expectConnectedAccount(result); + }); + }); + + describe('updateOrganizationConnectedAccount', () => { + it('sends the correct request and returns result', async () => { + fetchOnce(connectedAccountFixture); + + const result = await workos.pipes.updateOrganizationConnectedAccount({ + organizationId: 'test_organizationId', + slug: 'test_slug', + supportsMultipleConnections: true, + connectedAccountId: 'data_installation_01EHZNVPK3SFK441A1RGBFSHRT', + userId: 'user_id_01234', + }); + + expect(fetchMethod()).toBe('PUT'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/organizations/test_organizationId/connected_accounts/test_slug', + ); + expect(fetchBody()).toEqual( + expect.objectContaining({ user_id: 'user_id_01234' }), + ); + expectConnectedAccount(result); + }); + }); + + describe('deleteOrganizationConnectedAccount', () => { + it('sends a DELETE request', async () => { + fetchOnce({}, { status: 204 }); + + await workos.pipes.deleteOrganizationConnectedAccount({ + organizationId: 'test_organizationId', + slug: 'test_slug', + supportsMultipleConnections: true, + connectedAccountId: 'data_installation_01EHZNVPK3SFK441A1RGBFSHRT', + }); + + expect(fetchMethod()).toBe('DELETE'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/organizations/test_organizationId/connected_accounts/test_slug', + ); + }); + }); + + describe('listOrganizationDataProviders', () => { + it('returns the expected result', async () => { + fetchOnce(dataIntegrationsListResponseFixture); + + const result = await workos.pipes.listOrganizationDataProviders({ + organizationId: 'test_organizationId', + supportsMultipleConnections: true, + }); + + expect(fetchMethod()).toBe('GET'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/organizations/test_organizationId/data_providers', + ); + expectDataIntegrationsListResponse(result); + }); + }); + describe('getUserConnectedAccount', () => { it('returns the expected result', async () => { fetchOnce(connectedAccountFixture); @@ -232,6 +423,8 @@ describe('Pipes', () => { userId: 'test_userId', slug: 'test_slug', organizationId: 'org_01EHZNVPK3SFK441A1RGBFSHRT', + supportsMultipleConnections: true, + connectedAccountId: 'data_installation_01EHZNVPK3SFK441A1RGBFSHRT', }); expect(fetchMethod()).toBe('GET'); @@ -276,6 +469,8 @@ describe('Pipes', () => { userId: 'test_userId', slug: 'test_slug', organizationId: 'org_01EHZNVPK3SFK441A1RGBFSHRT', + supportsMultipleConnections: true, + connectedAccountId: 'data_installation_01EHZNVPK3SFK441A1RGBFSHRT', accessToken: 'test_access_token', refreshToken: 'test_refresh_token', }); @@ -302,6 +497,8 @@ describe('Pipes', () => { userId: 'test_userId', slug: 'test_slug', organizationId: 'org_01EHZNVPK3SFK441A1RGBFSHRT', + supportsMultipleConnections: true, + connectedAccountId: 'data_installation_01EHZNVPK3SFK441A1RGBFSHRT', }); expect(fetchMethod()).toBe('DELETE'); @@ -310,6 +507,8 @@ describe('Pipes', () => { ); expect(fetchSearchParams()).toEqual({ organization_id: 'org_01EHZNVPK3SFK441A1RGBFSHRT', + supports_multiple_connections: 'true', + connected_account_id: 'data_installation_01EHZNVPK3SFK441A1RGBFSHRT', }); expect(fetchSearchParams()).not.toHaveProperty('query'); }); @@ -322,13 +521,14 @@ describe('Pipes', () => { const result = await workos.pipes.listUserDataProviders({ userId: 'test_userId', organizationId: 'org_01EHZNVPK3SFK441A1RGBFSHRT', + supportsMultipleConnections: true, }); expect(fetchMethod()).toBe('GET'); expect(new URL(String(fetchURL())).pathname).toBe( '/user_management/users/test_userId/data_providers', ); - expect(result.object).toBe('list'); + expectDataIntegrationsListResponse(result); }); }); }); diff --git a/src/pipes/pipes.ts b/src/pipes/pipes.ts index eeadae230..991f120d3 100644 --- a/src/pipes/pipes.ts +++ b/src/pipes/pipes.ts @@ -1,7 +1,7 @@ // This file is auto-generated by oagen. Do not edit. import type { WorkOS } from '../workos'; -import type { PaginationOptions } from '../common/interfaces/pagination-options.interface'; +import type { ListDataIntegrationsOptions } from './interfaces/list-data-integrations-options.interface'; import { AutoPaginatable } from '../common/utils/pagination'; import { fetchAndDeserialize } from '../common/utils/fetch-and-deserialize'; import type { CreateDataIntegrationOptions } from './interfaces/create-data-integration-options.interface'; @@ -10,8 +10,17 @@ import type { UpdateDataIntegrationOptions } from './interfaces/update-data-inte import type { DeleteDataIntegrationOptions } from './interfaces/delete-data-integration-options.interface'; import type { UpdateDataIntegrationApiKeyOptions } from './interfaces/update-data-integration-api-key-options.interface'; import type { AuthorizeDataIntegrationOptions } from './interfaces/authorize-data-integration-options.interface'; +import type { UpdateDataIntegrationClientCredentialsOptions } from './interfaces/update-data-integration-client-credentials-options.interface'; import type { CreateDataIntegrationCredentialOptions } from './interfaces/create-data-integration-credential-options.interface'; +import type { GetOrganizationDataIntegrationOptions } from './interfaces/get-organization-data-integration-options.interface'; +import type { UpdateOrganizationDataIntegrationOptions } from './interfaces/update-organization-data-integration-options.interface'; +import type { DeleteOrganizationDataIntegrationOptions } from './interfaces/delete-organization-data-integration-options.interface'; import type { GetAccessTokenOptions } from './interfaces/get-access-token-options.interface'; +import type { GetOrganizationConnectedAccountOptions } from './interfaces/get-organization-connected-account-options.interface'; +import type { CreateOrganizationConnectedAccountOptions } from './interfaces/create-organization-connected-account-options.interface'; +import type { UpdateOrganizationConnectedAccountOptions } from './interfaces/update-organization-connected-account-options.interface'; +import type { DeleteOrganizationConnectedAccountOptions } from './interfaces/delete-organization-connected-account-options.interface'; +import type { ListOrganizationDataProvidersOptions } from './interfaces/list-organization-data-providers-options.interface'; import type { GetUserConnectedAccountOptions } from './interfaces/get-user-connected-account-options.interface'; import type { CreateUserConnectedAccountOptions } from './interfaces/create-user-connected-account-options.interface'; import type { UpdateUserConnectedAccountOptions } from './interfaces/update-user-connected-account-options.interface'; @@ -45,9 +54,11 @@ import type { CreateDataIntegrationResponse } from './interfaces/create-data-int import type { UpdateDataIntegrationResponse } from './interfaces/update-data-integration.interface'; import type { DataIntegrationsUpsertApiKeyRequestResponse } from './interfaces/data-integrations-upsert-api-key-request.interface'; import type { DataIntegrationsGetDataIntegrationAuthorizeUrlRequestResponse } from './interfaces/data-integrations-get-data-integration-authorize-url-request.interface'; +import type { DataIntegrationsUpsertClientCredentialsRequestResponse } from './interfaces/data-integrations-upsert-client-credentials-request.interface'; import type { DataIntegrationsVendCredentialsRequestResponse } from './interfaces/data-integrations-vend-credentials-request.interface'; import type { DataIntegrationsGetUserTokenRequestResponse } from './interfaces/data-integrations-get-user-token-request.interface'; -import type { ConnectedAccountDtoResponse } from './interfaces/connected-account-dto.interface'; +import type { OrganizationConnectedAccountResponse } from './interfaces/organization-connected-account.interface'; +import type { ConnectedAccountInputResponse } from './interfaces/connected-account-input.interface'; import { deserializeDataIntegration } from './serializers/data-integration.serializer'; import { deserializeConnectedAccount } from './serializers/connected-account.serializer'; import { deserializeDataIntegrationAuthorizeUrlResponse } from './serializers/data-integration-authorize-url-response.serializer'; @@ -58,9 +69,11 @@ import { serializeCreateDataIntegration } from './serializers/create-data-integr import { serializeUpdateDataIntegration } from './serializers/update-data-integration.serializer'; import { serializeDataIntegrationsUpsertApiKeyRequest } from './serializers/data-integrations-upsert-api-key-request.serializer'; import { serializeDataIntegrationsGetDataIntegrationAuthorizeUrlRequest } from './serializers/data-integrations-get-data-integration-authorize-url-request.serializer'; +import { serializeDataIntegrationsUpsertClientCredentialsRequest } from './serializers/data-integrations-upsert-client-credentials-request.serializer'; import { serializeDataIntegrationsVendCredentialsRequest } from './serializers/data-integrations-vend-credentials-request.serializer'; import { serializeDataIntegrationsGetUserTokenRequest } from './serializers/data-integrations-get-user-token-request.serializer'; -import { serializeConnectedAccountDto } from './serializers/connected-account-dto.serializer'; +import { serializeOrganizationConnectedAccount } from './serializers/organization-connected-account.serializer'; +import { serializeConnectedAccountInput } from './serializers/connected-account-input.serializer'; export class Pipes { constructor(private readonly workos: WorkOS) {} @@ -68,14 +81,16 @@ export class Pipes { /** * List data integrations * - * Lists the environment's data integrations configured with `custom` or `organization` credentials, including custom providers. + * Lists the environment's data integrations configured with `custom` or `organization` credentials, including custom providers and API key integrations. Both user-owned and organization-owned roots are returned, each as its own row with an `ownership`; filter with `ownership` to return only one kind. * @param options - Pagination and filter options. - * @returns {Promise>} + * @returns {Promise>} * @throws {UnauthorizedException} 401 + * @throws {NotFoundException} 404 + * @throws {UnprocessableEntityException} 422 */ async listDataIntegrations( - options?: PaginationOptions, - ): Promise> { + options?: ListDataIntegrationsOptions, + ): Promise> { const paginationOptions = options; return new AutoPaginatable( await fetchAndDeserialize( @@ -98,22 +113,31 @@ export class Pipes { /** * Create a data integration * - * Creates a data integration for a provider. Set `credentials.type` to `custom` to use your own OAuth app credentials, or `organization` to have each organization supply its own. For a built-in provider, pass its slug as `provider`. For a custom provider, pass a new slug plus a `custom_provider` definition. + * Creates a data integration for a provider. Set `credentials.type` to `custom` to use your own OAuth app credentials or `organization` to have each organization supply its own. Set `auth_methods` to `["api_key"]` to create an API key integration; you may optionally supply an `api_key` block to install a first tenant in the same call. Set `auth_methods` to `["client_credentials"]` to create a client-credentials integration; client credentials are installed per-tenant afterwards. Set `ownership` to `organization` to create the integration organizations connect to instead of the default user-owned one; a provider may have one of each. For a built-in provider, pass its slug as `provider`. For a custom provider, pass a new slug plus a `custom_provider` definition, or the slug of an existing custom provider (without `custom_provider`) to add the other ownership. * @param options - Object containing provider. * @param options.provider - The provider to create a Data Integration for. For a built-in provider use its slug (e.g. `github`, `slack`). For a custom provider, this is the new provider slug and `custom_provider` must be supplied. A custom provider slug cannot shadow an existing global provider slug. * @example "github" + * @param options.ownership - Who owns the Data Integration. `user` (the default) creates the integration users connect their own accounts to; `organization` creates the root organizations connect to. Ownership is fixed at creation, and one integration of each ownership may exist per provider. Independent of `credentials.type`. + * @example "user" * @param options.description - An optional description of the Data Integration. * @example "Production GitHub app" * @param options.enabled - Whether the Data Integration is enabled. Defaults to `false`. * @example true * @param options.scopes - The OAuth scopes to request for the Data Integration. Defaults to the provider's configured scopes when omitted. * @example ["repo","read:org"] - * @param options.credentials - The credentials to configure for the Data Integration. Required for both built-in and custom providers. + * @param options.authMethods - How accounts authenticate with the provider. Defaults to `["oauth"]`. Use `["api_key"]` to declare an API key integration; `credentials` is then not required and keys are supplied per-tenant (optionally via `api_key` on this request). Use `["client_credentials"]` to declare a client-credentials integration; `credentials` is likewise not required and client credentials are supplied per-tenant. + * @example ["oauth"] + * @param options.config - Provider-specific config values (e.g. a Snowflake `account`), keyed by the config field. Only fields the built-in provider declares are accepted. + * @example {"account":"myorg-myaccount"} + * @param options.credentials - The OAuth credentials to configure for the Data Integration. Required for OAuth integrations; omit when `auth_methods` is `["api_key"]`. + * @param options.apiKey - An optional API key to install for the first tenant on an `api_key` integration. Omit to declare a keyless integration; tenants can be added later via the per-installation API key path. * @param options.customProvider - The OAuth definition for a custom provider. Supply this to define a custom provider; omit it to create an integration for a built-in provider. * @returns {Promise} * @throws {BadRequestException} 400 * @throws {UnauthorizedException} 401 + * @throws {AuthorizationException} 403 * @throws {NotFoundException} 404 + * @throws {ConflictException} 409 * @throws {UnprocessableEntityException} 422 */ async createDataIntegration( @@ -130,7 +154,7 @@ export class Pipes { /** * Get a data integration * - * Retrieves a data integration by its slug. + * Retrieves the user-owned data integration by its slug. * @param options - The request options. * @param options.slug - The slug identifier of the data integration. * @example "github" @@ -151,7 +175,7 @@ export class Pipes { /** * Update a data integration * - * Updates the description, enabled state, or custom credentials of a data integration. For custom providers, `custom_provider` updates the OAuth definition. + * Updates the description, enabled state, or custom credentials of the user-owned data integration. For custom providers, `custom_provider` updates the OAuth definition. * @param options - The request body. * @param options.slug - The slug identifier of the data integration. * @example "github" @@ -161,12 +185,15 @@ export class Pipes { * @example true * @param options.scopes - The OAuth scopes to request for the Data Integration. Pass `null` to reset to the provider's configured scopes. * @example ["repo","read:org"] - * @param options.credentials - New credentials for the Data Integration. When provided, rotates the stored client secret. + * @param options.credentials - New OAuth credentials for the Data Integration. When provided, rotates the stored client secret. Mutually exclusive with `api_key`. + * @param options.apiKey - An API key to install or rotate for a tenant on an `api_key` integration. Upserts the tenant installation identified by `user_id` (and optional `organization_id`). * @param options.customProvider - Updates to a custom provider's OAuth definition. Only valid for custom-provider integrations. * @returns {Promise} * @throws {BadRequestException} 400 * @throws {UnauthorizedException} 401 + * @throws {AuthorizationException} 403 * @throws {NotFoundException} 404 + * @throws {ConflictException} 409 * @throws {UnprocessableEntityException} 422 */ async updateDataIntegration( @@ -186,7 +213,7 @@ export class Pipes { /** * Delete a data integration * - * Deletes a data integration and all of its connected installations. For a custom provider, also deletes the custom provider definition. + * Deletes the user-owned data integration and all of its connected installations. For a custom provider, the provider definition is deleted once no organization-owned root references it either. * @param options - The request options. * @param options.slug - The slug identifier of the data integration. * @example "github" @@ -204,14 +231,18 @@ export class Pipes { /** * Upsert an API key for a connected account * - * Creates or updates an API-key-based installation for the specified integration and user. If an installation already exists, the stored API key is rotated to the new value. + * Creates or updates an API-key-based installation for the specified integration, owned by the user or, when `connection_owner` is `organization`, shared by the organization. If an installation already exists, the stored API key is rotated to the new value. * @param options - Object containing userId, secret. * @param options.slug - The identifier of the integration. * @example "github" * @param options.userId - A [User](https://workos.com/docs/reference/authkit/user) identifier. * @example "user_01EHZNVPK3SFK441A1RGBFSHRT" - * @param options.organizationId - An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. + * @param options.organizationId - An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. Required when `connection_owner` is `organization`. * @example "org_01EHZNVPK3SFK441A1RGBFSHRT" + * @param options.connectedAccountId - A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to rotate a specific existing connection. + * @example "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" + * @param options.connectionOwner - Whose connection to create or rotate. `user` (the default) addresses the connection owned by `user_id`. `organization` addresses the connection shared by every member of `organization_id`; `user_id` then identifies the member performing the request and must be an active member of the organization. + * @example "user" * @param options.secret - The API key secret to store for this integration. * @example "sk-1234567890abcdef" * @returns {Promise} @@ -219,6 +250,7 @@ export class Pipes { * @throws {UnauthorizedException} 401 * @throws {AuthorizationException} 403 * @throws {NotFoundException} 404 + * @throws {ConflictException} 409 * @throws {UnprocessableEntityException} 422 */ async updateDataIntegrationApiKey( @@ -242,12 +274,16 @@ export class Pipes { * @param options - Object containing userId. * @param options.slug - The slug identifier of the provider (e.g., `github`, `slack`, `notion`). * @example "github" - * @param options.userId - The ID of the user to authorize. + * @param options.userId - The ID of the user to authorize. When `connection_owner` is `organization`, this is the user authorizing on behalf of the organization; they must be an active member of the organization and do not become the owner of the resulting connected account. * @example "user_01EHZNVPK3SFK441A1RGBFSHRT" - * @param options.organizationId - An organization ID to scope the authorization to a specific organization. + * @param options.organizationId - An organization ID to scope the authorization to a specific organization. Required when `connection_owner` is `organization`. * @example "org_01EHZNVPK3SFK441A1RGBFSHRT" + * @param options.connectionOwner - Who will own the connected account. `user` (the default) connects the user's own account. `organization` connects the organization's shared account and requires `organization_id`. + * @example "user" * @param options.returnTo - The URL to redirect the user to after authorization. * @example "https://example.com/callback" + * @param options.config - Connect-time config values for the provider-declared `installation`-scope fields (e.g. a Zendesk `subdomain`), keyed by the config field. Only fields the provider declares may be supplied, and required fields must be provided unless already pinned on the integration. + * @example {"subdomain":"acme"} * @returns {Promise} * @throws {BadRequestException} 400 * @throws {UnauthorizedException} 401 @@ -268,21 +304,59 @@ export class Pipes { return deserializeDataIntegrationAuthorizeUrlResponse(data); } + /** + * Upsert client credentials for a connected account + * + * Creates or updates a client-credentials-based installation for the specified integration, owned by the user or, when `connection_owner` is `organization`, shared by the organization. If an installation already exists, the stored client credentials are rotated to the new values. + * @param options - The request options. + * @param options.slug - The identifier of the integration. + * @example "salesforce" + * @returns {Promise} + * @throws {BadRequestException} 400 + * @throws {UnauthorizedException} 401 + * @throws {AuthorizationException} 403 + * @throws {NotFoundException} 404 + * @throws {ConflictException} 409 + * @throws {UnprocessableEntityException} 422 + */ + async updateDataIntegrationClientCredentials( + options: UpdateDataIntegrationClientCredentialsOptions, + ): Promise { + const { slug, ...payload } = options; + const { data } = await this.workos.put< + ConnectedAccountResponse, + DataIntegrationsUpsertClientCredentialsRequestResponse + >( + `/data-integrations/${encodeURIComponent(slug)}/client-credentials`, + serializeDataIntegrationsUpsertClientCredentialsRequest(payload), + ); + return deserializeConnectedAccount(data); + } + /** * Vend credentials for a connected account * - * Returns credentials for a user's connected account. Branches on the installation's `auth_method`: OAuth installations return an access token (refreshed if needed); API-key installations return the stored secret. + * Returns credentials for a user-owned or organization-owned connected account. OAuth and client-credentials installations return an access token; API-key installations return the stored secret. Client-credentials responses also include provider metadata. * @param options - Object containing userId. * @param options.slug - The identifier of the integration. * @example "github" - * @param options.userId - A [User](https://workos.com/docs/reference/authkit/user) identifier. + * @param options.userId - A [User](https://workos.com/docs/reference/authkit/user) identifier. When `connection_owner` is `organization`, this is the user the credentials are vended on behalf of; they must be an active member of the organization. * @example "user_01EHZNVPK3SFK441A1RGBFSHRT" - * @param options.organizationId - An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. + * @param options.organizationId - An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. Required when `connection_owner` is `organization`. * @example "org_01EHZNVPK3SFK441A1RGBFSHRT" + * @param options.connectedAccountId - A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to select a specific connection when the user has several for this provider. + * @example "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" + * @param options.connectionOwner - Which connection to vend from. `user` (the default) vends the user's own connection and requires `user_id`. `organization` vends the organization's shared connection and requires `organization_id`. + * @example "user" + * @param options.supportsMultipleConnections - Set to `true` to use the plural connection contract. If no `connected_account_id` is supplied and several connections match, the request returns `account_selection_required`. When omitted or `false`, only the compatibility connection is considered. + * @example true * @returns {Promise} * @throws {BadRequestException} 400 * @throws {UnauthorizedException} 401 + * @throws {AuthorizationException} 403 * @throws {NotFoundException} 404 + * @throws {ConflictException} 409 + * @throws {UnprocessableEntityException} 422 */ async createDataIntegrationCredential( options: CreateDataIntegrationCredentialOptions, @@ -298,6 +372,76 @@ export class Pipes { return deserializeDataIntegrationCredentialsResponse(data); } + /** + * Get an organization-owned data integration + * + * Retrieves the organization-owned data integration for a provider by its slug. The `/organization` suffix selects the environment-level organization-owned root for the provider; it does not name a particular organization. + * @param options - The request options. + * @param options.slug - The slug identifier of the data integration. + * @example "github" + * @returns {Promise} + * @throws {UnauthorizedException} 401 + * @throws {NotFoundException} 404 + */ + async getOrganizationDataIntegration( + options: GetOrganizationDataIntegrationOptions, + ): Promise { + const { slug } = options; + const { data } = await this.workos.get( + `/data-integrations/${encodeURIComponent(slug)}/organization`, + ); + return deserializeDataIntegration(data); + } + + /** + * Update an organization-owned data integration + * + * Updates the description, enabled state, or custom credentials of the organization-owned data integration for a provider. For custom providers, `custom_provider` updates the OAuth definition, which is shared with the user-owned root. The `/organization` suffix selects the environment-level organization-owned root for the provider; it does not name a particular organization. + * @param options - The request options. + * @param options.slug - The slug identifier of the data integration. + * @example "github" + * @returns {Promise} + * @throws {BadRequestException} 400 + * @throws {UnauthorizedException} 401 + * @throws {AuthorizationException} 403 + * @throws {NotFoundException} 404 + * @throws {ConflictException} 409 + * @throws {UnprocessableEntityException} 422 + */ + async updateOrganizationDataIntegration( + options: UpdateOrganizationDataIntegrationOptions, + ): Promise { + const { slug, ...payload } = options; + const { data } = await this.workos.put< + DataIntegrationResponse, + UpdateDataIntegrationResponse + >( + `/data-integrations/${encodeURIComponent(slug)}/organization`, + serializeUpdateDataIntegration(payload), + ); + return deserializeDataIntegration(data); + } + + /** + * Delete an organization-owned data integration + * + * Deletes the organization-owned data integration for a provider and all of its connected installations. For a custom provider, the provider definition is deleted once no user-owned root references it either. The `/organization` suffix selects the environment-level organization-owned root for the provider; it does not name a particular organization. + * @param options - The request options. + * @param options.slug - The slug identifier of the data integration. + * @example "github" + * @returns {Promise} + * @throws {UnauthorizedException} 401 + * @throws {NotFoundException} 404 + */ + async deleteOrganizationDataIntegration( + options: DeleteOrganizationDataIntegrationOptions, + ): Promise { + const { slug } = options; + await this.workos.delete( + `/data-integrations/${encodeURIComponent(slug)}/organization`, + ); + } + /** * Get an access token for a connected account * @@ -305,14 +449,22 @@ export class Pipes { * @param options - Object containing userId. * @param options.provider - The identifier of the integration. * @example "github" - * @param options.userId - A [User](https://workos.com/docs/reference/authkit/user) identifier. + * @param options.userId - A [User](https://workos.com/docs/reference/authkit/user) identifier. When `connection_owner` is `organization`, this is the user the credentials are vended on behalf of; they must be an active member of the organization. * @example "user_01EHZNVPK3SFK441A1RGBFSHRT" - * @param options.organizationId - An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. + * @param options.organizationId - An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. Required when `connection_owner` is `organization`. * @example "org_01EHZNVPK3SFK441A1RGBFSHRT" + * @param options.connectedAccountId - A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to select a specific connection when the user has several for this provider. + * @example "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" + * @param options.connectionOwner - Which connection to vend from. `user` (the default) vends the user's own connection and requires `user_id`. `organization` vends the organization's shared connection and requires `organization_id`. + * @example "user" + * @param options.supportsMultipleConnections - Set to `true` to use the plural connection contract. If no `connected_account_id` is supplied and several connections match, the request returns `account_selection_required`. When omitted or `false`, only the compatibility connection is considered. + * @example true * @returns {Promise} * @throws {BadRequestException} 400 * @throws {UnauthorizedException} 401 + * @throws {AuthorizationException} 403 * @throws {NotFoundException} 404 + * @throws {ConflictException} 409 * @throws {UnprocessableEntityException} 422 */ async getAccessToken( @@ -329,6 +481,190 @@ export class Pipes { return deserializeDataIntegrationAccessTokenResponse(data); } + /** + * Get an organization connected account + * + * Retrieves an organization's [connected account](https://workos.com/docs/reference/pipes/connected-account) for a specific provider. + * @param options - Additional query options. + * @param options.organizationId - An [Organization](https://workos.com/docs/reference/organization) identifier. + * @example "org_01EHZNVPK3SFK441A1RGBFSHRT" + * @param options.slug - The slug identifier of the provider (e.g., `github`, `slack`, `notion`). + * @example "github" + * @param options.supportsMultipleConnections - Set to `true` to use the plural connection contract. When omitted or `false`, only the compatibility connection is considered. + * @example true + * @param options.connectedAccountId - A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to select a specific connection when the organization has several for this provider. + * @example "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" + * @returns {Promise} + * @throws {BadRequestException} 400 + * @throws {UnauthorizedException} 401 + * @throws {NotFoundException} 404 + * @throws {ConflictException} 409 + */ + async getOrganizationConnectedAccount( + options: GetOrganizationConnectedAccountOptions, + ): Promise { + const { organizationId, slug } = options; + const { data } = await this.workos.get( + `/organizations/${encodeURIComponent( + organizationId, + )}/connected_accounts/${encodeURIComponent(slug)}`, + { + query: { + ...(options.supportsMultipleConnections !== undefined && { + supports_multiple_connections: options.supportsMultipleConnections, + }), + ...(options.connectedAccountId !== undefined && { + connected_account_id: options.connectedAccountId, + }), + }, + }, + ); + return deserializeConnectedAccount(data); + } + + /** + * Import an organization connected account + * + * Imports an organization-owned [connected account](https://workos.com/docs/reference/pipes/connected-account) by providing OAuth tokens directly. Use this to migrate existing connections or set up connections without going through the OAuth flow. + * @param options - The request options. + * @param options.organizationId - An [Organization](https://workos.com/docs/reference/organization) identifier. + * @example "org_01EHZNVPK3SFK441A1RGBFSHRT" + * @param options.slug - The slug identifier of the provider (e.g., `github`, `slack`, `notion`). + * @example "github" + * @returns {Promise} + * @throws {UnauthorizedException} 401 + * @throws {AuthorizationException} 403 + * @throws {NotFoundException} 404 + * @throws {ConflictException} 409 + * @throws {UnprocessableEntityException} 422 + */ + async createOrganizationConnectedAccount( + options: CreateOrganizationConnectedAccountOptions, + ): Promise { + const { organizationId, slug, ...payload } = options; + const { data } = await this.workos.post< + ConnectedAccountResponse, + OrganizationConnectedAccountResponse + >( + `/organizations/${encodeURIComponent( + organizationId, + )}/connected_accounts/${encodeURIComponent(slug)}`, + serializeOrganizationConnectedAccount(payload), + ); + return deserializeConnectedAccount(data); + } + + /** + * Update an organization connected account + * + * Updates an organization's [connected account](https://workos.com/docs/reference/pipes/connected-account) tokens, scopes, or state for a specific provider. + * @param options - Additional query options. + * @param options.organizationId - An [Organization](https://workos.com/docs/reference/organization) identifier. + * @example "org_01EHZNVPK3SFK441A1RGBFSHRT" + * @param options.slug - The slug identifier of the provider (e.g., `github`, `slack`, `notion`). + * @example "github" + * @param options.supportsMultipleConnections - Set to `true` to use the plural connection contract. When omitted or `false`, only the compatibility connection is considered. + * @example true + * @param options.connectedAccountId - A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to select the connection to update. + * @example "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" + * @returns {Promise} + * @throws {BadRequestException} 400 + * @throws {UnauthorizedException} 401 + * @throws {AuthorizationException} 403 + * @throws {NotFoundException} 404 + * @throws {ConflictException} 409 + * @throws {UnprocessableEntityException} 422 + */ + async updateOrganizationConnectedAccount( + options: UpdateOrganizationConnectedAccountOptions, + ): Promise { + const { + organizationId, + slug, + supportsMultipleConnections, + connectedAccountId, + ...payload + } = options; + const { data } = await this.workos.put< + ConnectedAccountResponse, + OrganizationConnectedAccountResponse + >( + `/organizations/${encodeURIComponent( + organizationId, + )}/connected_accounts/${encodeURIComponent(slug)}`, + serializeOrganizationConnectedAccount(payload), + { + query: { + supports_multiple_connections: supportsMultipleConnections, + connected_account_id: connectedAccountId, + }, + }, + ); + return deserializeConnectedAccount(data); + } + + /** + * Delete an organization connected account + * + * Disconnects the organization's account for the provider, including removing any stored access and refresh tokens. A member will need to reauthorize if the organization wants to reconnect. This does not revoke access on the provider side. + * @param options - Additional query options. + * @param options.organizationId - An [Organization](https://workos.com/docs/reference/organization) identifier. + * @example "org_01EHZNVPK3SFK441A1RGBFSHRT" + * @param options.slug - The slug identifier of the provider (e.g., `github`, `slack`, `notion`). + * @example "github" + * @param options.supportsMultipleConnections - Set to `true` to use the plural connection contract. When omitted or `false`, only the compatibility connection is considered. + * @example true + * @param options.connectedAccountId - A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to select the connection to delete. + * @example "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" + * @returns {Promise} + * @throws {BadRequestException} 400 + * @throws {UnauthorizedException} 401 + * @throws {NotFoundException} 404 + * @throws {ConflictException} 409 + */ + async deleteOrganizationConnectedAccount( + options: DeleteOrganizationConnectedAccountOptions, + ): Promise { + const { organizationId, slug } = options; + await this.workos.delete( + `/organizations/${encodeURIComponent(organizationId)}/connected_accounts/${encodeURIComponent(slug)}`, + { + supports_multiple_connections: options.supportsMultipleConnections, + connected_account_id: options.connectedAccountId, + }, + ); + } + + /** + * List providers for an organization + * + * Retrieves the organization-owned providers configured for your environment and the organization's [connected account](https://workos.com/docs/reference/pipes/connected-account) information for each. Providers owned by individual users are not included. + * @param options - Additional query options. + * @param options.organizationId - An [Organization](https://workos.com/docs/reference/organization) identifier to list providers and connected accounts for. + * @example "org_01EHZNVPK3SFK441A1RGBFSHRT" + * @param options.supportsMultipleConnections - Set to `true` to use the plural connection contract. When omitted or `false`, only the compatibility connection is considered. + * @example true + * @returns {Promise} + * @throws {UnauthorizedException} 401 + * @throws {NotFoundException} 404 + */ + async listOrganizationDataProviders( + options: ListOrganizationDataProvidersOptions, + ): Promise { + const { organizationId } = options; + const { data } = await this.workos.get( + `/organizations/${encodeURIComponent(organizationId)}/data_providers`, + { + query: { + ...(options.supportsMultipleConnections !== undefined && { + supports_multiple_connections: options.supportsMultipleConnections, + }), + }, + }, + ); + return deserializeDataIntegrationsListResponse(data); + } + /** * Get a connected account * @@ -340,21 +676,35 @@ export class Pipes { * @example "github" * @param options.organizationId - An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter if the connection is scoped to an organization. * @example "org_01EHZNVPK3SFK441A1RGBFSHRT" + * @param options.supportsMultipleConnections - Set to `true` to use the plural connection contract. When omitted or `false`, only the compatibility connection is considered. + * @example true + * @param options.connectedAccountId - A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to select a specific connection when the user has several for this provider. + * @example "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" * @returns {Promise} + * @throws {BadRequestException} 400 * @throws {UnauthorizedException} 401 * @throws {NotFoundException} 404 + * @throws {ConflictException} 409 */ async getUserConnectedAccount( options: GetUserConnectedAccountOptions, ): Promise { const { userId, slug } = options; const { data } = await this.workos.get( - `/user_management/users/${encodeURIComponent(userId)}/connected_accounts/${encodeURIComponent(slug)}`, + `/user_management/users/${encodeURIComponent( + userId, + )}/connected_accounts/${encodeURIComponent(slug)}`, { query: { ...(options.organizationId !== undefined && { organization_id: options.organizationId, }), + ...(options.supportsMultipleConnections !== undefined && { + supports_multiple_connections: options.supportsMultipleConnections, + }), + ...(options.connectedAccountId !== undefined && { + connected_account_id: options.connectedAccountId, + }), }, }, ); @@ -394,17 +744,13 @@ export class Pipes { const { userId, slug, organizationId, ...payload } = options; const { data } = await this.workos.post< ConnectedAccountResponse, - ConnectedAccountDtoResponse + ConnectedAccountInputResponse >( - `/user_management/users/${encodeURIComponent(userId)}/connected_accounts/${encodeURIComponent(slug)}`, - serializeConnectedAccountDto(payload), - { - query: { - ...(options.organizationId !== undefined && { - organization_id: options.organizationId, - }), - }, - }, + `/user_management/users/${encodeURIComponent( + userId, + )}/connected_accounts/${encodeURIComponent(slug)}`, + serializeConnectedAccountInput(payload), + { query: { organization_id: organizationId } }, ); return deserializeConnectedAccount(data); } @@ -420,6 +766,10 @@ export class Pipes { * @example "github" * @param options.organizationId - An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter if the connection is scoped to an organization. * @example "org_01EHZNVPK3SFK441A1RGBFSHRT" + * @param options.supportsMultipleConnections - Set to `true` to use the plural connection contract. When omitted or `false`, only the compatibility connection is considered. + * @example true + * @param options.connectedAccountId - A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to select the connection to update. + * @example "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" * @param options.accessToken - The OAuth access token for the connected account. * @example "gho_16C7e42F292c6912E7710c838347Ae178B4a" * @param options.refreshToken - The OAuth refresh token for the connected account. @@ -431,24 +781,36 @@ export class Pipes { * @param options.state - Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. * @example "connected" * @returns {Promise} + * @throws {BadRequestException} 400 * @throws {UnauthorizedException} 401 * @throws {NotFoundException} 404 + * @throws {ConflictException} 409 + * @throws {UnprocessableEntityException} 422 */ async updateUserConnectedAccount( options: UpdateUserConnectedAccountOptions, ): Promise { - const { userId, slug, organizationId, ...payload } = options; + const { + userId, + slug, + organizationId, + supportsMultipleConnections, + connectedAccountId, + ...payload + } = options; const { data } = await this.workos.put< ConnectedAccountResponse, - ConnectedAccountDtoResponse + ConnectedAccountInputResponse >( - `/user_management/users/${encodeURIComponent(userId)}/connected_accounts/${encodeURIComponent(slug)}`, - serializeConnectedAccountDto(payload), + `/user_management/users/${encodeURIComponent( + userId, + )}/connected_accounts/${encodeURIComponent(slug)}`, + serializeConnectedAccountInput(payload), { query: { - ...(options.organizationId !== undefined && { - organization_id: options.organizationId, - }), + organization_id: organizationId, + supports_multiple_connections: supportsMultipleConnections, + connected_account_id: connectedAccountId, }, }, ); @@ -458,7 +820,7 @@ export class Pipes { /** * Delete a connected account * - * Disconnects WorkOS's account for the user, including removing any stored access and refresh tokens. The user will need to reauthorize if they want to reconnect. This does not revoke access on the provider side. + * Disconnects WorkOS's account for the user, including removing any stored access and refresh tokens. The user will need to reauthorize if they want to reconnect. Access is not revoked on the provider side, except for the WorkOS OAuth provider, whose underlying AuthKit grant is revoked. * @param options - Additional query options. * @param options.userId - A [User](https://workos.com/docs/reference/authkit/user) identifier. * @example "user_01EHZNVPK3SFK441A1RGBFSHRT" @@ -466,9 +828,15 @@ export class Pipes { * @example "github" * @param options.organizationId - An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter if the connection is scoped to an organization. * @example "org_01EHZNVPK3SFK441A1RGBFSHRT" + * @param options.supportsMultipleConnections - Set to `true` to use the plural connection contract. When omitted or `false`, only the compatibility connection is considered. + * @example true + * @param options.connectedAccountId - A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to select the connection to delete. + * @example "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" * @returns {Promise} + * @throws {BadRequestException} 400 * @throws {UnauthorizedException} 401 * @throws {NotFoundException} 404 + * @throws {ConflictException} 409 */ async deleteUserConnectedAccount( options: DeleteUserConnectedAccountOptions, @@ -477,9 +845,9 @@ export class Pipes { await this.workos.delete( `/user_management/users/${encodeURIComponent(userId)}/connected_accounts/${encodeURIComponent(slug)}`, { - ...(options.organizationId !== undefined && { - organization_id: options.organizationId, - }), + organization_id: options.organizationId, + supports_multiple_connections: options.supportsMultipleConnections, + connected_account_id: options.connectedAccountId, }, ); } @@ -493,6 +861,8 @@ export class Pipes { * @example "user_01EHZNVPK3SFK441A1RGBFSHRT" * @param options.organizationId - An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to filter connections for a specific organization. * @example "org_01EHZNVPK3SFK441A1RGBFSHRT" + * @param options.supportsMultipleConnections - Set to `true` to use the plural connection contract. When omitted or `false`, only the compatibility connection is considered. + * @example true * @returns {Promise} * @throws {UnauthorizedException} 401 * @throws {NotFoundException} 404 @@ -508,6 +878,9 @@ export class Pipes { ...(options.organizationId !== undefined && { organization_id: options.organizationId, }), + ...(options.supportsMultipleConnections !== undefined && { + supports_multiple_connections: options.supportsMultipleConnections, + }), }, }, ); diff --git a/src/pipes/serializers/api-key-installation.serializer.ts b/src/pipes/serializers/api-key-installation.serializer.ts new file mode 100644 index 000000000..b766f2d37 --- /dev/null +++ b/src/pipes/serializers/api-key-installation.serializer.ts @@ -0,0 +1,14 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + ApiKeyInstallation, + ApiKeyInstallationResponse, +} from '../interfaces/api-key-installation.interface'; + +export const serializeApiKeyInstallation = ( + model: ApiKeyInstallation, +): ApiKeyInstallationResponse => ({ + secret: model.secret, + user_id: model.userId, + organization_id: model.organizationId, +}); diff --git a/src/pipes/serializers/connected-account-input.serializer.ts b/src/pipes/serializers/connected-account-input.serializer.ts new file mode 100644 index 000000000..0445408f1 --- /dev/null +++ b/src/pipes/serializers/connected-account-input.serializer.ts @@ -0,0 +1,17 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + ConnectedAccountInput, + ConnectedAccountInputResponse, +} from '../interfaces/connected-account-input.interface'; + +export const serializeConnectedAccountInput = ( + model: ConnectedAccountInput, +): ConnectedAccountInputResponse => ({ + access_token: model.accessToken, + refresh_token: model.refreshToken, + expires_at: + model.expiresAt != null ? model.expiresAt.toISOString() : undefined, + scopes: model.scopes, + state: model.state, +}); diff --git a/src/pipes/serializers/connected-account.serializer.ts b/src/pipes/serializers/connected-account.serializer.ts index b8a7d66cb..489615855 100644 --- a/src/pipes/serializers/connected-account.serializer.ts +++ b/src/pipes/serializers/connected-account.serializer.ts @@ -10,11 +10,17 @@ export const deserializeConnectedAccount = ( ): ConnectedAccount => ({ object: response.object, id: response.id, + connectionRole: response.connection_role, + accountIdentifier: response.account_identifier ?? null, + accountDisplayName: response.account_display_name ?? null, userId: response.user_id ?? null, organizationId: response.organization_id ?? null, scopes: response.scopes, authMethod: response.auth_method, apiKeyLast4: response.api_key_last_4 ?? null, + clientId: response.client_id ?? null, + clientSecretLast4: response.client_secret_last_4 ?? null, + config: response.config, state: response.state, createdAt: response.created_at, updatedAt: response.updated_at, diff --git a/src/pipes/serializers/create-data-integration.serializer.ts b/src/pipes/serializers/create-data-integration.serializer.ts index 030d38e21..d246aba87 100644 --- a/src/pipes/serializers/create-data-integration.serializer.ts +++ b/src/pipes/serializers/create-data-integration.serializer.ts @@ -4,19 +4,27 @@ import type { CreateDataIntegration, CreateDataIntegrationResponse, } from '../interfaces/create-data-integration.interface'; -import { serializeDataIntegrationCredentialsDto } from './data-integration-credentials-dto.serializer'; +import { serializeDataIntegrationCredentialsInput } from './data-integration-credentials-input.serializer'; +import { serializeApiKeyInstallation } from './api-key-installation.serializer'; import { serializeCustomProviderDefinition } from './custom-provider-definition.serializer'; export const serializeCreateDataIntegration = ( model: CreateDataIntegration, ): CreateDataIntegrationResponse => ({ provider: model.provider, + ownership: model.ownership, + auth_methods: model.authMethods, + config: model.config, + api_key: + model.apiKey != null + ? serializeApiKeyInstallation(model.apiKey) + : undefined, description: model.description, enabled: model.enabled, scopes: model.scopes, credentials: model.credentials != null - ? serializeDataIntegrationCredentialsDto(model.credentials) + ? serializeDataIntegrationCredentialsInput(model.credentials) : undefined, custom_provider: model.customProvider != null diff --git a/src/pipes/serializers/data-integration-credentials-input.serializer.ts b/src/pipes/serializers/data-integration-credentials-input.serializer.ts new file mode 100644 index 000000000..c7a5e6303 --- /dev/null +++ b/src/pipes/serializers/data-integration-credentials-input.serializer.ts @@ -0,0 +1,14 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + DataIntegrationCredentialsInput, + DataIntegrationCredentialsInputResponse, +} from '../interfaces/data-integration-credentials-input.interface'; + +export const serializeDataIntegrationCredentialsInput = ( + model: DataIntegrationCredentialsInput, +): DataIntegrationCredentialsInputResponse => ({ + type: model.type, + client_id: model.clientId, + client_secret: model.clientSecret, +}); diff --git a/src/pipes/serializers/data-integration-credentials-response-credential.serializer.ts b/src/pipes/serializers/data-integration-credentials-response-credential.serializer.ts index c24651c76..cd5ed84aa 100644 --- a/src/pipes/serializers/data-integration-credentials-response-credential.serializer.ts +++ b/src/pipes/serializers/data-integration-credentials-response-credential.serializer.ts @@ -1,4 +1,5 @@ -// This file is auto-generated by oagen. Do not edit. +// @oagen-ignore-file +// Hand-maintained to preserve the spec's nested credential discriminants. import type { DataIntegrationCredentialsResponseCredential, @@ -7,11 +8,29 @@ import type { export const deserializeDataIntegrationCredentialsResponseCredential = ( response: DataIntegrationCredentialsResponseCredentialResponse, -): DataIntegrationCredentialsResponseCredential => ({ - object: response.object, - authMethod: response.auth_method, - value: response.value, - expiresAt: response.expires_at ?? null, - scopes: response.scopes, - missingScopes: response.missing_scopes, -}); +): DataIntegrationCredentialsResponseCredential => { + switch (response.auth_method) { + case 'api_key': + return { + object: response.object, + authMethod: response.auth_method, + value: response.value, + }; + case 'oauth': + case 'client_credentials': + return { + object: response.object, + value: response.value, + expiresAt: response.expires_at, + scopes: response.scopes, + missingScopes: response.missing_scopes, + ...(response.auth_method === 'client_credentials' + ? { authMethod: response.auth_method, metadata: response.metadata } + : { authMethod: response.auth_method }), + }; + default: + throw new Error( + `Unknown auth_method: ${String((response as Record).auth_method)}`, + ); + } +}; diff --git a/src/pipes/serializers/data-integration-credentials-response.serializer.ts b/src/pipes/serializers/data-integration-credentials-response.serializer.ts index baccf1a73..d8d56ae20 100644 --- a/src/pipes/serializers/data-integration-credentials-response.serializer.ts +++ b/src/pipes/serializers/data-integration-credentials-response.serializer.ts @@ -1,4 +1,5 @@ -// This file is auto-generated by oagen. Do not edit. +// @oagen-ignore-file +// Hand-maintained to preserve the spec's nested credential discriminants. import type { DataIntegrationCredentialsResponse, @@ -8,13 +9,20 @@ import { deserializeDataIntegrationCredentialsResponseCredential } from './data- export const deserializeDataIntegrationCredentialsResponse = ( response: DataIntegrationCredentialsResponseWire, -): DataIntegrationCredentialsResponse => ({ - active: response.active, - credential: - response.credential != null - ? deserializeDataIntegrationCredentialsResponseCredential( +): DataIntegrationCredentialsResponse => { + switch (response.active) { + case true: + return { + active: true, + credential: deserializeDataIntegrationCredentialsResponseCredential( response.credential, - ) - : undefined, - error: response.error, -}); + ), + }; + case false: + return { active: false, error: response.error }; + default: + throw new Error( + `Unknown active: ${String((response as Record).active)}`, + ); + } +}; diff --git a/src/pipes/serializers/data-integration-installation.serializer.ts b/src/pipes/serializers/data-integration-installation.serializer.ts new file mode 100644 index 000000000..af7e95174 --- /dev/null +++ b/src/pipes/serializers/data-integration-installation.serializer.ts @@ -0,0 +1,18 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + DataIntegrationInstallation, + DataIntegrationInstallationResponse, +} from '../interfaces/data-integration-installation.interface'; + +export const deserializeDataIntegrationInstallation = ( + response: DataIntegrationInstallationResponse, +): DataIntegrationInstallation => ({ + id: response.id, + connectionRole: response.connection_role, + accountIdentifier: response.account_identifier ?? null, + accountDisplayName: response.account_display_name ?? null, + userId: response.user_id ?? null, + organizationId: response.organization_id ?? null, + apiKeyLast4: response.api_key_last_4 ?? null, +}); diff --git a/src/pipes/serializers/data-integration.serializer.ts b/src/pipes/serializers/data-integration.serializer.ts index ba6282f17..740b27f41 100644 --- a/src/pipes/serializers/data-integration.serializer.ts +++ b/src/pipes/serializers/data-integration.serializer.ts @@ -5,6 +5,7 @@ import type { DataIntegrationResponse, } from '../interfaces/data-integration.interface'; import { deserializeDataIntegrationCredential } from './data-integration-credential.serializer'; +import { deserializeDataIntegrationInstallation } from './data-integration-installation.serializer'; import { deserializeDataIntegrationCustomProvider } from './data-integration-custom-provider.serializer'; export const deserializeDataIntegration = ( @@ -14,12 +15,22 @@ export const deserializeDataIntegration = ( id: response.id, slug: response.slug, integrationType: response.integration_type, + ownership: response.ownership, description: response.description ?? null, enabled: response.enabled, state: response.state, scopes: response.scopes ?? null, redirectUri: response.redirect_uri, - credentials: deserializeDataIntegrationCredential(response.credentials), + authMethods: response.auth_methods, + credentials: + response.credentials != null + ? deserializeDataIntegrationCredential(response.credentials) + : null, + installation: + response.installation != null + ? deserializeDataIntegrationInstallation(response.installation) + : null, + config: response.config, customProvider: response.custom_provider != null ? deserializeDataIntegrationCustomProvider(response.custom_provider) diff --git a/src/pipes/serializers/data-integrations-get-data-integration-authorize-url-request.serializer.ts b/src/pipes/serializers/data-integrations-get-data-integration-authorize-url-request.serializer.ts index a3bb3f876..940eb4cb9 100644 --- a/src/pipes/serializers/data-integrations-get-data-integration-authorize-url-request.serializer.ts +++ b/src/pipes/serializers/data-integrations-get-data-integration-authorize-url-request.serializer.ts @@ -10,5 +10,7 @@ export const serializeDataIntegrationsGetDataIntegrationAuthorizeUrlRequest = ( ): DataIntegrationsGetDataIntegrationAuthorizeUrlRequestResponse => ({ user_id: model.userId, organization_id: model.organizationId, + connection_owner: model.connectionOwner, return_to: model.returnTo, + config: model.config, }); diff --git a/src/pipes/serializers/data-integrations-get-user-token-request.serializer.ts b/src/pipes/serializers/data-integrations-get-user-token-request.serializer.ts index 0a4208ee4..084198cc6 100644 --- a/src/pipes/serializers/data-integrations-get-user-token-request.serializer.ts +++ b/src/pipes/serializers/data-integrations-get-user-token-request.serializer.ts @@ -10,4 +10,7 @@ export const serializeDataIntegrationsGetUserTokenRequest = ( ): DataIntegrationsGetUserTokenRequestResponse => ({ user_id: model.userId, organization_id: model.organizationId, + connected_account_id: model.connectedAccountId, + connection_owner: model.connectionOwner, + supports_multiple_connections: model.supportsMultipleConnections, }); diff --git a/src/pipes/serializers/data-integrations-list-response-data-connected-account.serializer.ts b/src/pipes/serializers/data-integrations-list-response-data-connected-account.serializer.ts index 94b71ffd8..5800677c4 100644 --- a/src/pipes/serializers/data-integrations-list-response-data-connected-account.serializer.ts +++ b/src/pipes/serializers/data-integrations-list-response-data-connected-account.serializer.ts @@ -10,11 +10,17 @@ export const deserializeDataIntegrationsListResponseDataConnectedAccount = ( ): DataIntegrationsListResponseDataConnectedAccount => ({ object: response.object, id: response.id, + connectionRole: response.connection_role, + accountIdentifier: response.account_identifier ?? null, + accountDisplayName: response.account_display_name ?? null, userId: response.user_id ?? null, organizationId: response.organization_id ?? null, scopes: response.scopes, authMethod: response.auth_method, apiKeyLast4: response.api_key_last_4 ?? null, + clientId: response.client_id ?? null, + clientSecretLast4: response.client_secret_last_4 ?? null, + config: response.config, state: response.state, createdAt: response.created_at, updatedAt: response.updated_at, diff --git a/src/pipes/serializers/data-integrations-list-response-data.serializer.ts b/src/pipes/serializers/data-integrations-list-response-data.serializer.ts index b5999b675..3b432de0c 100644 --- a/src/pipes/serializers/data-integrations-list-response-data.serializer.ts +++ b/src/pipes/serializers/data-integrations-list-response-data.serializer.ts @@ -18,6 +18,7 @@ export const deserializeDataIntegrationsListResponseData = ( credentialsType: response.credentials_type, scopes: response.scopes ?? null, authMethods: response.auth_methods, + connectionOwner: response.connection_owner, ownership: response.ownership, createdAt: response.created_at, updatedAt: response.updated_at, @@ -27,4 +28,10 @@ export const deserializeDataIntegrationsListResponseData = ( response.connected_account, ) : null, + connectedAccounts: + response.connected_accounts != null + ? response.connected_accounts.map( + deserializeDataIntegrationsListResponseDataConnectedAccount, + ) + : undefined, }); diff --git a/src/pipes/serializers/data-integrations-upsert-api-key-request.serializer.ts b/src/pipes/serializers/data-integrations-upsert-api-key-request.serializer.ts index 20ce8dfba..a86c1529a 100644 --- a/src/pipes/serializers/data-integrations-upsert-api-key-request.serializer.ts +++ b/src/pipes/serializers/data-integrations-upsert-api-key-request.serializer.ts @@ -10,5 +10,7 @@ export const serializeDataIntegrationsUpsertApiKeyRequest = ( ): DataIntegrationsUpsertApiKeyRequestResponse => ({ user_id: model.userId, organization_id: model.organizationId, + connected_account_id: model.connectedAccountId, + connection_owner: model.connectionOwner, secret: model.secret, }); diff --git a/src/pipes/serializers/data-integrations-upsert-client-credentials-request.serializer.ts b/src/pipes/serializers/data-integrations-upsert-client-credentials-request.serializer.ts new file mode 100644 index 000000000..93d89b861 --- /dev/null +++ b/src/pipes/serializers/data-integrations-upsert-client-credentials-request.serializer.ts @@ -0,0 +1,18 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + DataIntegrationsUpsertClientCredentialsRequest, + DataIntegrationsUpsertClientCredentialsRequestResponse, +} from '../interfaces/data-integrations-upsert-client-credentials-request.interface'; + +export const serializeDataIntegrationsUpsertClientCredentialsRequest = ( + model: DataIntegrationsUpsertClientCredentialsRequest, +): DataIntegrationsUpsertClientCredentialsRequestResponse => ({ + user_id: model.userId, + organization_id: model.organizationId, + connected_account_id: model.connectedAccountId, + connection_owner: model.connectionOwner, + client_id: model.clientId, + client_secret: model.clientSecret, + config: model.config, +}); diff --git a/src/pipes/serializers/data-integrations-vend-credentials-request.serializer.ts b/src/pipes/serializers/data-integrations-vend-credentials-request.serializer.ts index 1454753b8..9a04ed7a4 100644 --- a/src/pipes/serializers/data-integrations-vend-credentials-request.serializer.ts +++ b/src/pipes/serializers/data-integrations-vend-credentials-request.serializer.ts @@ -10,4 +10,7 @@ export const serializeDataIntegrationsVendCredentialsRequest = ( ): DataIntegrationsVendCredentialsRequestResponse => ({ user_id: model.userId, organization_id: model.organizationId, + connected_account_id: model.connectedAccountId, + connection_owner: model.connectionOwner, + supports_multiple_connections: model.supportsMultipleConnections, }); diff --git a/src/pipes/serializers/index.ts b/src/pipes/serializers/index.ts index 6157e2e3c..b6bff7863 100644 --- a/src/pipes/serializers/index.ts +++ b/src/pipes/serializers/index.ts @@ -1,23 +1,29 @@ // This file is auto-generated by oagen. Do not edit. +export * from './api-key-installation.serializer'; export * from './connected-account.serializer'; export * from './connected-account-dto.serializer'; export * from './create-data-integration.serializer'; +export * from './connected-account-input.serializer'; export * from './custom-provider-definition.serializer'; export * from './data-integration.serializer'; export * from './data-integration-access-token-response-access-token.serializer'; export * from './data-integration-authorize-url-response.serializer'; export * from './data-integration-credential.serializer'; export * from './data-integration-credentials-dto.serializer'; +export * from './data-integration-credentials-input.serializer'; export * from './data-integration-credentials-response.serializer'; export * from './data-integration-credentials-response-credential.serializer'; export * from './data-integration-custom-provider.serializer'; +export * from './data-integration-installation.serializer'; export * from './data-integrations-get-data-integration-authorize-url-request.serializer'; export * from './data-integrations-get-user-token-request.serializer'; export * from './data-integrations-list-response.serializer'; export * from './data-integrations-list-response-data.serializer'; export * from './data-integrations-list-response-data-connected-account.serializer'; export * from './data-integrations-upsert-api-key-request.serializer'; +export * from './data-integrations-upsert-client-credentials-request.serializer'; export * from './data-integrations-vend-credentials-request.serializer'; +export * from './organization-connected-account.serializer'; export * from './update-custom-provider-definition.serializer'; export * from './update-data-integration.serializer'; diff --git a/src/pipes/serializers/organization-connected-account.serializer.ts b/src/pipes/serializers/organization-connected-account.serializer.ts new file mode 100644 index 000000000..71d4ba56e --- /dev/null +++ b/src/pipes/serializers/organization-connected-account.serializer.ts @@ -0,0 +1,18 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + OrganizationConnectedAccount, + OrganizationConnectedAccountResponse, +} from '../interfaces/organization-connected-account.interface'; + +export const serializeOrganizationConnectedAccount = ( + model: OrganizationConnectedAccount, +): OrganizationConnectedAccountResponse => ({ + access_token: model.accessToken, + refresh_token: model.refreshToken, + expires_at: + model.expiresAt != null ? model.expiresAt.toISOString() : undefined, + scopes: model.scopes, + state: model.state, + user_id: model.userId, +}); diff --git a/src/pipes/serializers/update-data-integration.serializer.ts b/src/pipes/serializers/update-data-integration.serializer.ts index e027765cd..2bc09f4de 100644 --- a/src/pipes/serializers/update-data-integration.serializer.ts +++ b/src/pipes/serializers/update-data-integration.serializer.ts @@ -4,7 +4,8 @@ import type { UpdateDataIntegration, UpdateDataIntegrationResponse, } from '../interfaces/update-data-integration.interface'; -import { serializeDataIntegrationCredentialsDto } from './data-integration-credentials-dto.serializer'; +import { serializeDataIntegrationCredentialsInput } from './data-integration-credentials-input.serializer'; +import { serializeApiKeyInstallation } from './api-key-installation.serializer'; import { serializeUpdateCustomProviderDefinition } from './update-custom-provider-definition.serializer'; export const serializeUpdateDataIntegration = ( @@ -13,9 +14,13 @@ export const serializeUpdateDataIntegration = ( description: model.description, enabled: model.enabled, scopes: model.scopes, + api_key: + model.apiKey != null + ? serializeApiKeyInstallation(model.apiKey) + : undefined, credentials: model.credentials != null - ? serializeDataIntegrationCredentialsDto(model.credentials) + ? serializeDataIntegrationCredentialsInput(model.credentials) : undefined, custom_provider: model.customProvider != null From 890013017244ed22f1a961c1ea663c35fa2ac21b Mon Sep 17 00:00:00 2001 From: Dan Dorman Date: Thu, 24 Sep 2026 14:31:28 -0600 Subject: [PATCH 2/5] fix(pipes): Preserve generation compatibility The merged credential schema can replace handwritten decoding, but regeneration must not remove published names or revive the DELETE query bug. Keep the compatibility boundaries explicit while the remaining emitter fixes are handled upstream. --- .oagen-manifest.json | 2 + docs/PIPES_COMPATIBILITY.md | 94 +++++++++ .../connected-account-dto.interface.ts | 3 +- ...eate-data-integration-options.interface.ts | 5 +- ...a-integration-credentials-dto.interface.ts | 3 +- ...edentials-response-credential.interface.ts | 55 +----- ...egration-credentials-response.interface.ts | 30 ++- ...integration-vended-credential.interface.ts | 62 ++++++ .../interfaces/get-access-token.interface.ts | 38 ---- src/pipes/interfaces/index.ts | 1 + src/pipes/pipes-compatibility.spec.ts | 185 ++++++++++++++++++ src/pipes/pipes.ts | 8 + .../connected-account-dto.serializer.ts | 19 +- ...-integration-credentials-dto.serializer.ts | 16 +- ...dentials-response-credential.serializer.ts | 36 +--- ...gration-credentials-response.serializer.ts | 15 +- ...ntegration-vended-credential.serializer.ts | 43 ++++ src/pipes/serializers/index.ts | 2 + 18 files changed, 449 insertions(+), 168 deletions(-) create mode 100644 docs/PIPES_COMPATIBILITY.md create mode 100644 src/pipes/interfaces/data-integration-vended-credential.interface.ts delete mode 100644 src/pipes/interfaces/get-access-token.interface.ts create mode 100644 src/pipes/pipes-compatibility.spec.ts create mode 100644 src/pipes/serializers/data-integration-vended-credential.serializer.ts diff --git a/.oagen-manifest.json b/.oagen-manifest.json index 0842af7b1..b9ea7acfd 100644 --- a/.oagen-manifest.json +++ b/.oagen-manifest.json @@ -141,6 +141,7 @@ "src/pipes/interfaces/data-integration-installation.interface.ts", "src/pipes/interfaces/data-integration-ownership.interface.ts", "src/pipes/interfaces/data-integration-state.interface.ts", + "src/pipes/interfaces/data-integration-vended-credential.interface.ts", "src/pipes/interfaces/data-integration.interface.ts", "src/pipes/interfaces/data-integrations-get-data-integration-authorize-url-request-connection-owner.interface.ts", "src/pipes/interfaces/data-integrations-get-data-integration-authorize-url-request.interface.ts", @@ -195,6 +196,7 @@ "src/pipes/serializers/data-integration-credentials-response.serializer.ts", "src/pipes/serializers/data-integration-custom-provider.serializer.ts", "src/pipes/serializers/data-integration-installation.serializer.ts", + "src/pipes/serializers/data-integration-vended-credential.serializer.ts", "src/pipes/serializers/data-integration.serializer.ts", "src/pipes/serializers/data-integrations-get-data-integration-authorize-url-request.serializer.ts", "src/pipes/serializers/data-integrations-get-user-token-request.serializer.ts", diff --git a/docs/PIPES_COMPATIBILITY.md b/docs/PIPES_COMPATIBILITY.md new file mode 100644 index 000000000..c9d2b0085 --- /dev/null +++ b/docs/PIPES_COMPATIBILITY.md @@ -0,0 +1,94 @@ +# Pipes compatibility notes + +## Public types and release review + +The credential-schema refactoring preserves the API's JSON contract, but the +expanded SDK types are not a blanket source-compatible replacement for older +versions: + +- `DataIntegration.credentials` can be `null` for API-key and client-credentials + integrations. Check for credentials before accessing `clientId` or + `redactedClientSecret`; do not substitute invented OAuth credentials. +- Credential responses distinguish active and inactive results. Typed response + fixtures must include `active` and the matching `credential` or `error`. +- API-key credentials have no OAuth expiry or scope fields. Code that assumes + every credential has a scope array needs to check `authMethod`, or use optional + access such as `result.credential?.scopes?.includes('repo')`. +- Authentication-method and connected-account-state unions have additional + values. Exhaustive consumer switches may need additional cases. +- Credential types that were interfaces are now union types. Custom types should + use an intersection rather than an interface extending the union. + +Existing reads such as `result.error` and `result.credential?.value` remain +supported without first narrowing on `active`. `CreateDataIntegrationOptions` +remains an interface that consumers can augment. + +The previous `DataIntegrationCredentialsResponseCredential` and +`DataIntegrationCredentialsResponseCredentialResponse` names remain exported as +aliases of `DataIntegrationVendedCredential` and its wire type. The existing +credential deserializer name also remains available. These are not removed or +renamed imports, even though the underlying response types are more precise. + +The new organization-integration methods are +`getOrganizationDataIntegration`, `updateOrganizationDataIntegration`, and +`deleteOrganizationDataIntegration`. No previously released method was renamed. + +Maintainers must resolve the remaining type-compatibility changes before deciding +on a release version. With this repository's default release-please versioning, +`!` or a `BREAKING CHANGE` footer in the commit that lands on `main` requests a +major release. Removing that metadata alone does not make these changes +compatible. Release-please owns the version and changelog updates. + +## Ownership values + +Provider-list responses retain the legacy `ownership` field: + +| Legacy provider `ownership` | Preferred `connectionOwner` | +| --------------------------- | --------------------------- | +| `userland_user` | `user` | +| `organization` | `organization` | + +Existing consumers do not need to rewrite the legacy response value. New code +should prefer `connectionOwner`, falling back to the mapping above when reading +an older response that omits it. + +Integration creation and listing use `ownership: 'user' | 'organization'`. +Connection authorization, credential vending, and credential rotation instead +use `connectionOwner`, which defaults to `user`. Organization-owned requests need +an `organizationId`; the supplied `userId` identifies the acting member rather +than making that member the owner. + +## Multiple connections are opt-in + +Omitting `supportsMultipleConnections`, or passing `false`, keeps the +compatibility-connection behavior. Opt in with `true` to work with multiple +connections: + +1. Use `listUserDataProviders` or `listOrganizationDataProviders` with + `supportsMultipleConnections: true` to obtain `connectedAccounts`. +2. Select a connection by its `id`, not its display name or account identifier. +3. Pass that ID as `connectedAccountId` on reads, updates, deletes, or credential + vending. Continue sending the plural opt-in where the operation supports it. + +The legacy `connectedAccount` field still represents only the compatibility +connection and may be `null` even when standard connections exist. With plural +opt-in, credential vending without an account selector can return HTTP 409 +`account_selection_required` when several connections match. + +## Generation boundaries + +`ConnectedAccountDto` and `DataIntegrationCredentialsDto` remain published +compatibility interfaces. Their legacy serializers delegate to the generated +`ConnectedAccountInput` and `DataIntegrationCredentialsInput` serializers. The +four legacy TypeScript files are explicitly protected with `@oagen-ignore-file`. +Their two JSON fixtures remain inputs to compatibility tests. + +Those legacy DTO files and fixtures intentionally stay outside +`.oagen-manifest.json`: the manifest records generated-file ownership, not every +SDK file. Adding obsolete generated paths would make them candidates for pruning +when the current spec no longer emits them. Preserve compatibility files rather +than deleting them to make the directory match the manifest. + +The credential response types retain hand-maintained compatibility fields and +metadata typing. The two direct-query DELETE methods are protected with +`@oagen-ignore` regions until the Node emitter's helper-signature fix is available. diff --git a/src/pipes/interfaces/connected-account-dto.interface.ts b/src/pipes/interfaces/connected-account-dto.interface.ts index 358361f4c..d01956f40 100644 --- a/src/pipes/interfaces/connected-account-dto.interface.ts +++ b/src/pipes/interfaces/connected-account-dto.interface.ts @@ -1,4 +1,5 @@ -// This file is auto-generated by oagen. Do not edit. +// @oagen-ignore-file +// Preserve the published DTO interface independently of generated input models. import type { ConnectedAccountInputState } from './connected-account-input-state.interface'; diff --git a/src/pipes/interfaces/create-data-integration-options.interface.ts b/src/pipes/interfaces/create-data-integration-options.interface.ts index 26122fb34..c2e61c62e 100644 --- a/src/pipes/interfaces/create-data-integration-options.interface.ts +++ b/src/pipes/interfaces/create-data-integration-options.interface.ts @@ -2,4 +2,7 @@ import type { CreateDataIntegration } from './create-data-integration.interface'; -export type CreateDataIntegrationOptions = CreateDataIntegration; +// Keep an interface so existing consumers can extend or augment it. +export interface CreateDataIntegrationOptions extends CreateDataIntegration { + provider: string; +} diff --git a/src/pipes/interfaces/data-integration-credentials-dto.interface.ts b/src/pipes/interfaces/data-integration-credentials-dto.interface.ts index e141828e5..9f62ccb71 100644 --- a/src/pipes/interfaces/data-integration-credentials-dto.interface.ts +++ b/src/pipes/interfaces/data-integration-credentials-dto.interface.ts @@ -1,4 +1,5 @@ -// This file is auto-generated by oagen. Do not edit. +// @oagen-ignore-file +// Preserve the published DTO interface independently of generated input models. import type { DataIntegrationCredentialsType } from './data-integration-credentials-type.interface'; diff --git a/src/pipes/interfaces/data-integration-credentials-response-credential.interface.ts b/src/pipes/interfaces/data-integration-credentials-response-credential.interface.ts index b701b4e4e..20d9e5d6d 100644 --- a/src/pipes/interfaces/data-integration-credentials-response-credential.interface.ts +++ b/src/pipes/interfaces/data-integration-credentials-response-credential.interface.ts @@ -1,52 +1,7 @@ // @oagen-ignore-file -// Hand-maintained to preserve the spec's nested credential discriminants. +// Preserve the published names for the newly named OpenAPI component. -/** A vended credential, discriminated by its authentication method. */ -export type DataIntegrationCredentialsResponseCredential = - | { - object: 'credential'; - authMethod: 'oauth'; - value: string; - expiresAt: string | null; - scopes: string[]; - missingScopes: string[]; - } - | { - object: 'credential'; - authMethod: 'api_key'; - value: string; - } - | { - object: 'credential'; - authMethod: 'client_credentials'; - value: string; - expiresAt: string | null; - scopes: string[]; - missingScopes: string[]; - /** Non-sensitive provider token response fields, such as an instance URL. */ - metadata: Record; - }; - -export type DataIntegrationCredentialsResponseCredentialResponse = - | { - object: 'credential'; - auth_method: 'oauth'; - value: string; - expires_at: string | null; - scopes: string[]; - missing_scopes: string[]; - } - | { - object: 'credential'; - auth_method: 'api_key'; - value: string; - } - | { - object: 'credential'; - auth_method: 'client_credentials'; - value: string; - expires_at: string | null; - scopes: string[]; - missing_scopes: string[]; - metadata: Record; - }; +export type { + DataIntegrationVendedCredential as DataIntegrationCredentialsResponseCredential, + DataIntegrationVendedCredentialResponse as DataIntegrationCredentialsResponseCredentialResponse, +} from './data-integration-vended-credential.interface'; diff --git a/src/pipes/interfaces/data-integration-credentials-response.interface.ts b/src/pipes/interfaces/data-integration-credentials-response.interface.ts index d90bf88fe..1f0e958c1 100644 --- a/src/pipes/interfaces/data-integration-credentials-response.interface.ts +++ b/src/pipes/interfaces/data-integration-credentials-response.interface.ts @@ -1,19 +1,33 @@ // @oagen-ignore-file -// Hand-maintained to preserve the spec's nested credential discriminants. +// Preserve legacy property access on the generated active/inactive union. import type { - DataIntegrationCredentialsResponseCredential, - DataIntegrationCredentialsResponseCredentialResponse, -} from './data-integration-credentials-response-credential.interface'; + DataIntegrationVendedCredential, + DataIntegrationVendedCredentialResponse, +} from './data-integration-vended-credential.interface'; import type { DataIntegrationCredentialsResponseError } from './data-integration-credentials-response-error.interface'; +// Keep absent fields readable without requiring consumers to narrow first. export type DataIntegrationCredentialsResponse = - | { active: true; credential: DataIntegrationCredentialsResponseCredential } - | { active: false; error: DataIntegrationCredentialsResponseError }; + | { + active: true; + credential: DataIntegrationVendedCredential; + error?: undefined; + } + | { + active: false; + error: DataIntegrationCredentialsResponseError; + credential?: undefined; + }; export type DataIntegrationCredentialsResponseWire = | { active: true; - credential: DataIntegrationCredentialsResponseCredentialResponse; + credential: DataIntegrationVendedCredentialResponse; + error?: undefined; } - | { active: false; error: DataIntegrationCredentialsResponseError }; + | { + active: false; + error: DataIntegrationCredentialsResponseError; + credential?: undefined; + }; diff --git a/src/pipes/interfaces/data-integration-vended-credential.interface.ts b/src/pipes/interfaces/data-integration-vended-credential.interface.ts new file mode 100644 index 000000000..ce0091b26 --- /dev/null +++ b/src/pipes/interfaces/data-integration-vended-credential.interface.ts @@ -0,0 +1,62 @@ +// @oagen-ignore-file +// Preserve legacy property access and metadata typing on the generated variants. + +/** A vended credential, discriminated by its authentication method. */ +export type DataIntegrationVendedCredential = + | { + object: 'credential'; + authMethod: 'oauth'; + value: string; + expiresAt: string | null; + scopes: string[]; + missingScopes: string[]; + metadata?: undefined; + } + | { + object: 'credential'; + authMethod: 'api_key'; + value: string; + expiresAt?: undefined; + scopes?: undefined; + missingScopes?: undefined; + metadata?: undefined; + } + | { + object: 'credential'; + authMethod: 'client_credentials'; + value: string; + expiresAt: string | null; + scopes: string[]; + missingScopes: string[]; + /** Non-sensitive provider token response fields, such as an instance URL. */ + metadata: Record; + }; + +export type DataIntegrationVendedCredentialResponse = + | { + object: 'credential'; + auth_method: 'oauth'; + value: string; + expires_at: string | null; + scopes: string[]; + missing_scopes: string[]; + metadata?: undefined; + } + | { + object: 'credential'; + auth_method: 'api_key'; + value: string; + expires_at?: undefined; + scopes?: undefined; + missing_scopes?: undefined; + metadata?: undefined; + } + | { + object: 'credential'; + auth_method: 'client_credentials'; + value: string; + expires_at: string | null; + scopes: string[]; + missing_scopes: string[]; + metadata: Record; + }; diff --git a/src/pipes/interfaces/get-access-token.interface.ts b/src/pipes/interfaces/get-access-token.interface.ts deleted file mode 100644 index cf2bee185..000000000 --- a/src/pipes/interfaces/get-access-token.interface.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { AccessToken, SerializedAccessToken } from './access-token.interface'; - -export interface GetAccessTokenOptions { - userId: string; - organizationId?: string | null; -} - -export interface SerializedGetAccessTokenOptions { - user_id: string; - organization_id?: string | null; -} - -export interface GetAccessTokenSuccessResponse { - active: true; - accessToken: AccessToken; -} - -export interface GetAccessTokenFailureResponse { - active: false; - error: 'not_installed' | 'needs_reauthorization'; -} - -export type GetAccessTokenResponse = - GetAccessTokenSuccessResponse | GetAccessTokenFailureResponse; - -export interface SerializedGetAccessTokenSuccessResponse { - active: true; - access_token: SerializedAccessToken; -} - -export interface SerializedGetAccessTokenFailureResponse { - active: false; - error: 'not_installed' | 'needs_reauthorization'; -} - -export type SerializedGetAccessTokenResponse = - | SerializedGetAccessTokenSuccessResponse - | SerializedGetAccessTokenFailureResponse; diff --git a/src/pipes/interfaces/index.ts b/src/pipes/interfaces/index.ts index e8ba841e1..b3b5a9e2a 100644 --- a/src/pipes/interfaces/index.ts +++ b/src/pipes/interfaces/index.ts @@ -40,6 +40,7 @@ export * from './data-integration-installation-connection-role.interface'; export * from './data-integration-installation.interface'; export * from './data-integration-ownership.interface'; export * from './data-integration-state.interface'; +export * from './data-integration-vended-credential.interface'; export * from './data-integration.interface'; export * from './data-integrations-get-data-integration-authorize-url-request-connection-owner.interface'; export * from './data-integrations-get-data-integration-authorize-url-request.interface'; diff --git a/src/pipes/pipes-compatibility.spec.ts b/src/pipes/pipes-compatibility.spec.ts new file mode 100644 index 000000000..1ada4a7be --- /dev/null +++ b/src/pipes/pipes-compatibility.spec.ts @@ -0,0 +1,185 @@ +// @oagen-ignore-file +// Preserve consumer-facing access patterns independently of generated tests. +import fetch from 'jest-fetch-mock'; +import type { + CreateDataIntegrationOptions, + ConnectedAccountDto, + DataIntegrationCredentialsDto, + DataIntegrationCredentialsResponseCredential, + DataIntegrationCredentialsResponseCredentialResponse, + DataIntegrationVendedCredential, + DataIntegrationVendedCredentialResponse, +} from '../index'; +import { + deserializeDataIntegrationAccessTokenResponse, + deserializeDataIntegrationCredentialsResponseCredential, + deserializeDataIntegrationVendedCredential, + serializeConnectedAccountDto, + serializeConnectedAccountInput, + serializeDataIntegrationCredentialsDto, + serializeDataIntegrationCredentialsInput, +} from './serializers'; +import { fetchBody, fetchOnce } from '../common/utils/test-utils'; +import { WorkOS } from '../workos'; +import dataIntegrationFixture from './fixtures/data-integration.json'; + +// Check declaration merging without adding test-only fields to the SDK types. +declare module './interfaces/create-data-integration-options.interface' { + interface CreateDataIntegrationOptions { + provider: string; + } +} + +const workos = new WorkOS('sk_test_example'); + +beforeEach(() => fetch.resetMocks()); + +describe('Pipes source compatibility', () => { + it('exports the access-token response deserializer', () => { + expect( + deserializeDataIntegrationAccessTokenResponse({ + active: false, + error: 'not_installed', + }), + ).toEqual({ active: false, error: 'not_installed' }); + }); + + it('preserves the connected-account DTO serializer as a compatibility alias', () => { + const input: ConnectedAccountDto = { + accessToken: 'test_token', + refreshToken: 'test_refresh', + expiresAt: new Date('2026-01-01T00:00:00.000Z'), + scopes: ['repo'], + state: 'connected', + }; + expect(serializeConnectedAccountDto(input)).toEqual({ + access_token: 'test_token', + refresh_token: 'test_refresh', + expires_at: '2026-01-01T00:00:00.000Z', + scopes: ['repo'], + state: 'connected', + }); + expect(serializeConnectedAccountDto).toBe(serializeConnectedAccountInput); + }); + + it('preserves the OAuth credentials DTO serializer as a compatibility alias', () => { + const input: DataIntegrationCredentialsDto = { + type: 'custom', + clientId: 'test_client', + clientSecret: 'test_secret', + }; + expect(serializeDataIntegrationCredentialsDto(input)).toEqual({ + type: 'custom', + client_id: 'test_client', + client_secret: 'test_secret', + }); + expect(serializeDataIntegrationCredentialsDto).toBe( + serializeDataIntegrationCredentialsInput, + ); + }); + + it('keeps the create options interface mergeable', async () => { + const options: CreateDataIntegrationOptions = { provider: 'github' }; + fetchOnce(dataIntegrationFixture); + await workos.pipes.createDataIntegration(options); + expect(fetchBody()).toEqual({ provider: 'github' }); + }); + + it('preserves the legacy credential type and serializer exports', () => { + const response: DataIntegrationCredentialsResponseCredentialResponse = { + object: 'credential', + auth_method: 'client_credentials', + value: 'token', + expires_at: null, + scopes: ['api'], + missing_scopes: [], + metadata: { instance_url: 'https://example.test' }, + }; + const vendedResponse: DataIntegrationVendedCredentialResponse = response; + const legacy: DataIntegrationCredentialsResponseCredential = + deserializeDataIntegrationVendedCredential(vendedResponse); + const vended: DataIntegrationVendedCredential = + deserializeDataIntegrationCredentialsResponseCredential(response); + const metadata: Record | undefined = vended.metadata; + + expect(vended).toStrictEqual(legacy); + expect(metadata).toEqual({ instance_url: 'https://example.test' }); + expect(deserializeDataIntegrationCredentialsResponseCredential).toBe( + deserializeDataIntegrationVendedCredential, + ); + }); + + it.each([ + { + response: { + active: true, + credential: { + object: 'credential', + auth_method: 'oauth', + value: 'token', + expires_at: null, + scopes: ['repo'], + missing_scopes: [], + }, + }, + value: 'token', + scopes: ['repo'], + expiry: null, + metadata: undefined, + }, + { + response: { + active: true, + credential: { + object: 'credential', + auth_method: 'api_key', + value: 'secret', + }, + }, + value: 'secret', + scopes: undefined, + expiry: undefined, + metadata: undefined, + }, + { + response: { + active: true, + credential: { + object: 'credential', + auth_method: 'client_credentials', + value: 'client_token', + expires_at: null, + scopes: ['api'], + missing_scopes: [], + metadata: { instance_url: 'https://example.test' }, + }, + }, + value: 'client_token', + scopes: ['api'], + expiry: null, + metadata: { instance_url: 'https://example.test' }, + }, + { + response: { active: false, error: 'not_installed' }, + value: 'not_installed', + scopes: undefined, + expiry: undefined, + metadata: undefined, + }, + ])( + 'allows legacy property access for $value', + async ({ response, value, scopes, expiry, metadata }) => { + fetchOnce(response); + const result = await workos.pipes.createDataIntegrationCredential({ + slug: 'github', + userId: 'user_123', + }); + + // These expressions must compile without checking active or authMethod. + expect(result.credential?.value ?? result.error).toBe(value); + expect(result.credential?.scopes).toEqual(scopes); + expect(result.credential?.expiresAt).toBe(expiry); + expect(result.credential?.metadata).toEqual(metadata); + }, + ); +}); diff --git a/src/pipes/pipes.ts b/src/pipes/pipes.ts index 991f120d3..7e7698ead 100644 --- a/src/pipes/pipes.ts +++ b/src/pipes/pipes.ts @@ -603,6 +603,8 @@ export class Pipes { return deserializeConnectedAccount(data); } + // @oagen-ignore-start + // Keep direct-query DELETE calls until the emitter fix is available. /** * Delete an organization connected account * @@ -635,6 +637,8 @@ export class Pipes { ); } + // @oagen-ignore-end + /** * List providers for an organization * @@ -817,6 +821,8 @@ export class Pipes { return deserializeConnectedAccount(data); } + // @oagen-ignore-start + // Keep direct-query DELETE calls until the emitter fix is available. /** * Delete a connected account * @@ -852,6 +858,8 @@ export class Pipes { ); } + // @oagen-ignore-end + /** * List providers for a user * diff --git a/src/pipes/serializers/connected-account-dto.serializer.ts b/src/pipes/serializers/connected-account-dto.serializer.ts index 0cb5fb241..a82c0f90e 100644 --- a/src/pipes/serializers/connected-account-dto.serializer.ts +++ b/src/pipes/serializers/connected-account-dto.serializer.ts @@ -1,17 +1,4 @@ -// This file is auto-generated by oagen. Do not edit. +// @oagen-ignore-file +// Preserve the published DTO serializer name without duplicating generated logic. -import type { - ConnectedAccountDto, - ConnectedAccountDtoResponse, -} from '../interfaces/connected-account-dto.interface'; - -export const serializeConnectedAccountDto = ( - model: ConnectedAccountDto, -): ConnectedAccountDtoResponse => ({ - access_token: model.accessToken, - refresh_token: model.refreshToken, - expires_at: - model.expiresAt != null ? model.expiresAt.toISOString() : undefined, - scopes: model.scopes, - state: model.state, -}); +export { serializeConnectedAccountInput as serializeConnectedAccountDto } from './connected-account-input.serializer'; diff --git a/src/pipes/serializers/data-integration-credentials-dto.serializer.ts b/src/pipes/serializers/data-integration-credentials-dto.serializer.ts index e0d056371..0ee3d2666 100644 --- a/src/pipes/serializers/data-integration-credentials-dto.serializer.ts +++ b/src/pipes/serializers/data-integration-credentials-dto.serializer.ts @@ -1,14 +1,4 @@ -// This file is auto-generated by oagen. Do not edit. +// @oagen-ignore-file +// Preserve the published DTO serializer name without duplicating generated logic. -import type { - DataIntegrationCredentialsDto, - DataIntegrationCredentialsDtoResponse, -} from '../interfaces/data-integration-credentials-dto.interface'; - -export const serializeDataIntegrationCredentialsDto = ( - model: DataIntegrationCredentialsDto, -): DataIntegrationCredentialsDtoResponse => ({ - type: model.type, - client_id: model.clientId, - client_secret: model.clientSecret, -}); +export { serializeDataIntegrationCredentialsInput as serializeDataIntegrationCredentialsDto } from './data-integration-credentials-input.serializer'; diff --git a/src/pipes/serializers/data-integration-credentials-response-credential.serializer.ts b/src/pipes/serializers/data-integration-credentials-response-credential.serializer.ts index cd5ed84aa..128db3b28 100644 --- a/src/pipes/serializers/data-integration-credentials-response-credential.serializer.ts +++ b/src/pipes/serializers/data-integration-credentials-response-credential.serializer.ts @@ -1,36 +1,4 @@ // @oagen-ignore-file -// Hand-maintained to preserve the spec's nested credential discriminants. +// Preserve the existing serializer export without duplicating generated logic. -import type { - DataIntegrationCredentialsResponseCredential, - DataIntegrationCredentialsResponseCredentialResponse, -} from '../interfaces/data-integration-credentials-response-credential.interface'; - -export const deserializeDataIntegrationCredentialsResponseCredential = ( - response: DataIntegrationCredentialsResponseCredentialResponse, -): DataIntegrationCredentialsResponseCredential => { - switch (response.auth_method) { - case 'api_key': - return { - object: response.object, - authMethod: response.auth_method, - value: response.value, - }; - case 'oauth': - case 'client_credentials': - return { - object: response.object, - value: response.value, - expiresAt: response.expires_at, - scopes: response.scopes, - missingScopes: response.missing_scopes, - ...(response.auth_method === 'client_credentials' - ? { authMethod: response.auth_method, metadata: response.metadata } - : { authMethod: response.auth_method }), - }; - default: - throw new Error( - `Unknown auth_method: ${String((response as Record).auth_method)}`, - ); - } -}; +export { deserializeDataIntegrationVendedCredential as deserializeDataIntegrationCredentialsResponseCredential } from './data-integration-vended-credential.serializer'; diff --git a/src/pipes/serializers/data-integration-credentials-response.serializer.ts b/src/pipes/serializers/data-integration-credentials-response.serializer.ts index d8d56ae20..15e296120 100644 --- a/src/pipes/serializers/data-integration-credentials-response.serializer.ts +++ b/src/pipes/serializers/data-integration-credentials-response.serializer.ts @@ -1,11 +1,10 @@ -// @oagen-ignore-file -// Hand-maintained to preserve the spec's nested credential discriminants. +// This file is auto-generated by oagen. Do not edit. import type { DataIntegrationCredentialsResponse, DataIntegrationCredentialsResponseWire, } from '../interfaces/data-integration-credentials-response.interface'; -import { deserializeDataIntegrationCredentialsResponseCredential } from './data-integration-credentials-response-credential.serializer'; +import { deserializeDataIntegrationVendedCredential } from './data-integration-vended-credential.serializer'; export const deserializeDataIntegrationCredentialsResponse = ( response: DataIntegrationCredentialsResponseWire, @@ -14,15 +13,19 @@ export const deserializeDataIntegrationCredentialsResponse = ( case true: return { active: true, - credential: deserializeDataIntegrationCredentialsResponseCredential( + credential: deserializeDataIntegrationVendedCredential( response.credential, ), }; case false: - return { active: false, error: response.error }; + return { + active: false, + error: response.error, + }; default: throw new Error( - `Unknown active: ${String((response as Record).active)}`, + 'Unknown active: ' + + String((response as Record).active), ); } }; diff --git a/src/pipes/serializers/data-integration-vended-credential.serializer.ts b/src/pipes/serializers/data-integration-vended-credential.serializer.ts new file mode 100644 index 000000000..e30a2b685 --- /dev/null +++ b/src/pipes/serializers/data-integration-vended-credential.serializer.ts @@ -0,0 +1,43 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + DataIntegrationVendedCredential, + DataIntegrationVendedCredentialResponse, +} from '../interfaces/data-integration-vended-credential.interface'; + +export const deserializeDataIntegrationVendedCredential = ( + response: DataIntegrationVendedCredentialResponse, +): DataIntegrationVendedCredential => { + switch (response.auth_method) { + case 'oauth': + return { + authMethod: 'oauth', + object: response.object, + value: response.value, + expiresAt: response.expires_at, + scopes: response.scopes, + missingScopes: response.missing_scopes, + }; + case 'api_key': + return { + authMethod: 'api_key', + object: response.object, + value: response.value, + }; + case 'client_credentials': + return { + authMethod: 'client_credentials', + object: response.object, + value: response.value, + expiresAt: response.expires_at, + scopes: response.scopes, + missingScopes: response.missing_scopes, + metadata: response.metadata, + }; + default: + throw new Error( + 'Unknown auth_method: ' + + String((response as Record).auth_method), + ); + } +}; diff --git a/src/pipes/serializers/index.ts b/src/pipes/serializers/index.ts index b6bff7863..77b6f597c 100644 --- a/src/pipes/serializers/index.ts +++ b/src/pipes/serializers/index.ts @@ -8,6 +8,7 @@ export * from './connected-account-input.serializer'; export * from './custom-provider-definition.serializer'; export * from './data-integration.serializer'; export * from './data-integration-access-token-response-access-token.serializer'; +export * from './data-integration-access-token-response.serializer'; export * from './data-integration-authorize-url-response.serializer'; export * from './data-integration-credential.serializer'; export * from './data-integration-credentials-dto.serializer'; @@ -16,6 +17,7 @@ export * from './data-integration-credentials-response.serializer'; export * from './data-integration-credentials-response-credential.serializer'; export * from './data-integration-custom-provider.serializer'; export * from './data-integration-installation.serializer'; +export * from './data-integration-vended-credential.serializer'; export * from './data-integrations-get-data-integration-authorize-url-request.serializer'; export * from './data-integrations-get-user-token-request.serializer'; export * from './data-integrations-list-response.serializer'; From e73e805abfa70b6782b5a37087c33f598afa00d1 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 24 Sep 2026 20:50:46 +0000 Subject: [PATCH 3/5] test(pipes): Cover plural connection lists and unmark alias files Add a provider-list regression with distinct compatibility and standard connections, plus a provider with only a standard connection, so the plural-list deserialization cannot silently drop peers. Remove the DataIntegrationCredentialsResponseCredential alias interface, serializer, and fixture from .oagen-manifest.json: they are hand-maintained compatibility shims (@oagen-ignore-file) whose paths the current spec no longer emits, so listing them as generated made them prune candidates. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .oagen-manifest.json | 3 -- docs/PIPES_COMPATIBILITY.md | 5 ++- src/pipes/pipes-requests.spec.ts | 67 ++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 4 deletions(-) diff --git a/.oagen-manifest.json b/.oagen-manifest.json index b9ea7acfd..7a0947d67 100644 --- a/.oagen-manifest.json +++ b/.oagen-manifest.json @@ -91,7 +91,6 @@ "src/pipes/fixtures/data-integration-authorize-url-response.json", "src/pipes/fixtures/data-integration-credential.json", "src/pipes/fixtures/data-integration-credentials-input.json", - "src/pipes/fixtures/data-integration-credentials-response-credential.json", "src/pipes/fixtures/data-integration-credentials-response.json", "src/pipes/fixtures/data-integration-custom-provider.json", "src/pipes/fixtures/data-integration-installation.json", @@ -132,7 +131,6 @@ "src/pipes/interfaces/data-integration-credential.interface.ts", "src/pipes/interfaces/data-integration-credentials-input-type.interface.ts", "src/pipes/interfaces/data-integration-credentials-input.interface.ts", - "src/pipes/interfaces/data-integration-credentials-response-credential.interface.ts", "src/pipes/interfaces/data-integration-credentials-response-error.interface.ts", "src/pipes/interfaces/data-integration-credentials-response.interface.ts", "src/pipes/interfaces/data-integration-custom-provider-authenticate-via.interface.ts", @@ -192,7 +190,6 @@ "src/pipes/serializers/data-integration-authorize-url-response.serializer.ts", "src/pipes/serializers/data-integration-credential.serializer.ts", "src/pipes/serializers/data-integration-credentials-input.serializer.ts", - "src/pipes/serializers/data-integration-credentials-response-credential.serializer.ts", "src/pipes/serializers/data-integration-credentials-response.serializer.ts", "src/pipes/serializers/data-integration-custom-provider.serializer.ts", "src/pipes/serializers/data-integration-installation.serializer.ts", diff --git a/docs/PIPES_COMPATIBILITY.md b/docs/PIPES_COMPATIBILITY.md index c9d2b0085..55fa4ca2d 100644 --- a/docs/PIPES_COMPATIBILITY.md +++ b/docs/PIPES_COMPATIBILITY.md @@ -90,5 +90,8 @@ when the current spec no longer emits them. Preserve compatibility files rather than deleting them to make the directory match the manifest. The credential response types retain hand-maintained compatibility fields and -metadata typing. The two direct-query DELETE methods are protected with +metadata typing. The `DataIntegrationCredentialsResponseCredential` alias files +(interface, serializer, and fixture) re-export the generated +`DataIntegrationVendedCredential` component under its published name and, like +the legacy DTOs, stay outside the manifest. The two direct-query DELETE methods are protected with `@oagen-ignore` regions until the Node emitter's helper-signature fix is available. diff --git a/src/pipes/pipes-requests.spec.ts b/src/pipes/pipes-requests.spec.ts index dee1e9119..99a7910c6 100644 --- a/src/pipes/pipes-requests.spec.ts +++ b/src/pipes/pipes-requests.spec.ts @@ -350,6 +350,73 @@ describe('Pipes request contracts', () => { }); }, ); + + it.each(['listUserDataProviders', 'listOrganizationDataProviders'] as const)( + '%s keeps standard connections alongside the compatibility connection', + async (method) => { + const [provider] = providersFixture.data; + const compatibilityAccount = provider.connected_account; + const standardAccount = { + ...compatibilityAccount, + id: 'data_installation_standard', + connection_role: 'standard', + account_identifier: 'workspace_456', + account_display_name: 'Acme staging', + }; + fetchOnce({ + ...providersFixture, + data: [ + { + ...provider, + connected_accounts: [compatibilityAccount, standardAccount], + }, + { + ...provider, + id: 'data_integration_standard_only', + connected_account: null, + connected_accounts: [standardAccount], + }, + ], + }); + + const result = await workos.pipes[method]({ + userId, + organizationId, + supportsMultipleConnections: true, + }); + + const [withPeers, standardOnly] = result.data; + expect(withPeers.connectedAccount?.id).toBe(compatibilityAccount.id); + expect( + withPeers.connectedAccounts?.map( + ({ id, connectionRole, accountIdentifier }) => ({ + id, + connectionRole, + accountIdentifier, + }), + ), + ).toEqual([ + { + id: compatibilityAccount.id, + connectionRole: 'compatibility', + accountIdentifier: 'workspace_123', + }, + { + id: 'data_installation_standard', + connectionRole: 'standard', + accountIdentifier: 'workspace_456', + }, + ]); + + expect(standardOnly.connectedAccount).toBeNull(); + expect(standardOnly.connectedAccounts).toHaveLength(1); + expect(standardOnly.connectedAccounts?.[0]).toMatchObject({ + id: 'data_installation_standard', + connectionRole: 'standard', + accountDisplayName: 'Acme staging', + }); + }, + ); }); describe('Pipes credential response variants', () => { From 74f19120a0a3a1ca2ea493620db4ae82ad25ba16 Mon Sep 17 00:00:00 2001 From: Dan Dorman Date: Fri, 25 Sep 2026 14:45:47 -0600 Subject: [PATCH 4/5] feat(pipes): Refresh config and connection APIs Pipes now returns provider routing config and distinguishes creating a connection from reauthorizing one. Expose that contract without losing the SDK's compatibility aliases, reviewed plural-list coverage, or the local DELETE and PUT serialization fixes. --- .oagen-manifest.json | 14 + docs/PIPES_COMPATIBILITY.md | 48 +++ .../interfaces/connected-account.interface.ts | 2 +- ...reate-connected-account-state.interface.ts | 9 + .../create-connected-account.interface.ts | 27 ++ ...a-integration-api-key-options.interface.ts | 18 + ...ion-client-credential-options.interface.ts | 22 ++ ...ion-connected-account-options.interface.ts | 2 + ...ation-connected-account-state.interface.ts | 9 + ...rganization-connected-account.interface.ts | 30 ++ ...ser-connected-account-options.interface.ts | 2 + ...integration-vended-credential.interface.ts | 12 +- ...tion-request-connection-owner.interface.ts | 9 + ...te-api-key-connection-request.interface.ts | 24 ++ ...tion-request-connection-owner.interface.ts | 10 + ...redentials-connection-request.interface.ts | 30 ++ ...sponse-data-connected-account.interface.ts | 2 +- ...ations-upsert-api-key-request.interface.ts | 6 +- ...rt-client-credentials-request.interface.ts | 6 +- src/pipes/interfaces/index.ts | 10 + ...ion-connected-account-options.interface.ts | 4 +- ...ser-connected-account-options.interface.ts | 4 +- src/pipes/pipes-api-updates.spec.ts | 309 ++++++++++++++++++ src/pipes/pipes.ts | 92 +++++- .../create-connected-account.serializer.ts | 18 + ...ganization-connected-account.serializer.ts | 19 ++ ...ntegration-vended-credential.serializer.ts | 3 + ...e-api-key-connection-request.serializer.ts | 16 + ...edentials-connection-request.serializer.ts | 19 ++ ...tions-upsert-api-key-request.serializer.ts | 4 +- ...t-client-credentials-request.serializer.ts | 4 +- src/pipes/serializers/index.ts | 4 + 32 files changed, 768 insertions(+), 20 deletions(-) create mode 100644 src/pipes/interfaces/create-connected-account-state.interface.ts create mode 100644 src/pipes/interfaces/create-connected-account.interface.ts create mode 100644 src/pipes/interfaces/create-data-integration-api-key-options.interface.ts create mode 100644 src/pipes/interfaces/create-data-integration-client-credential-options.interface.ts create mode 100644 src/pipes/interfaces/create-organization-connected-account-state.interface.ts create mode 100644 src/pipes/interfaces/create-organization-connected-account.interface.ts create mode 100644 src/pipes/interfaces/data-integrations-create-api-key-connection-request-connection-owner.interface.ts create mode 100644 src/pipes/interfaces/data-integrations-create-api-key-connection-request.interface.ts create mode 100644 src/pipes/interfaces/data-integrations-create-client-credentials-connection-request-connection-owner.interface.ts create mode 100644 src/pipes/interfaces/data-integrations-create-client-credentials-connection-request.interface.ts create mode 100644 src/pipes/pipes-api-updates.spec.ts create mode 100644 src/pipes/serializers/create-connected-account.serializer.ts create mode 100644 src/pipes/serializers/create-organization-connected-account.serializer.ts create mode 100644 src/pipes/serializers/data-integrations-create-api-key-connection-request.serializer.ts create mode 100644 src/pipes/serializers/data-integrations-create-client-credentials-connection-request.serializer.ts diff --git a/.oagen-manifest.json b/.oagen-manifest.json index 7a0947d67..1d4b9e11c 100644 --- a/.oagen-manifest.json +++ b/.oagen-manifest.json @@ -115,11 +115,17 @@ "src/pipes/interfaces/connected-account-input.interface.ts", "src/pipes/interfaces/connected-account-state.interface.ts", "src/pipes/interfaces/connected-account.interface.ts", + "src/pipes/interfaces/create-connected-account-state.interface.ts", + "src/pipes/interfaces/create-connected-account.interface.ts", + "src/pipes/interfaces/create-data-integration-api-key-options.interface.ts", "src/pipes/interfaces/create-data-integration-auth-methods.interface.ts", + "src/pipes/interfaces/create-data-integration-client-credential-options.interface.ts", "src/pipes/interfaces/create-data-integration-ownership.interface.ts", "src/pipes/interfaces/create-data-integration-token-options.interface.ts", "src/pipes/interfaces/create-data-integration.interface.ts", "src/pipes/interfaces/create-organization-connected-account-options.interface.ts", + "src/pipes/interfaces/create-organization-connected-account-state.interface.ts", + "src/pipes/interfaces/create-organization-connected-account.interface.ts", "src/pipes/interfaces/custom-provider-definition-authenticate-via.interface.ts", "src/pipes/interfaces/custom-provider-definition.interface.ts", "src/pipes/interfaces/data-integration-access-token-response-access-token.interface.ts", @@ -141,6 +147,10 @@ "src/pipes/interfaces/data-integration-state.interface.ts", "src/pipes/interfaces/data-integration-vended-credential.interface.ts", "src/pipes/interfaces/data-integration.interface.ts", + "src/pipes/interfaces/data-integrations-create-api-key-connection-request-connection-owner.interface.ts", + "src/pipes/interfaces/data-integrations-create-api-key-connection-request.interface.ts", + "src/pipes/interfaces/data-integrations-create-client-credentials-connection-request-connection-owner.interface.ts", + "src/pipes/interfaces/data-integrations-create-client-credentials-connection-request.interface.ts", "src/pipes/interfaces/data-integrations-get-data-integration-authorize-url-request-connection-owner.interface.ts", "src/pipes/interfaces/data-integrations-get-data-integration-authorize-url-request.interface.ts", "src/pipes/interfaces/data-integrations-get-user-token-request-connection-owner.interface.ts", @@ -184,6 +194,8 @@ "src/pipes/serializers/api-key-installation.serializer.ts", "src/pipes/serializers/connected-account-input.serializer.ts", "src/pipes/serializers/connected-account.serializer.ts", + "src/pipes/serializers/create-connected-account.serializer.ts", + "src/pipes/serializers/create-organization-connected-account.serializer.ts", "src/pipes/serializers/custom-provider-definition.serializer.ts", "src/pipes/serializers/data-integration-access-token-response-access-token.serializer.ts", "src/pipes/serializers/data-integration-access-token-response.serializer.ts", @@ -195,6 +207,8 @@ "src/pipes/serializers/data-integration-installation.serializer.ts", "src/pipes/serializers/data-integration-vended-credential.serializer.ts", "src/pipes/serializers/data-integration.serializer.ts", + "src/pipes/serializers/data-integrations-create-api-key-connection-request.serializer.ts", + "src/pipes/serializers/data-integrations-create-client-credentials-connection-request.serializer.ts", "src/pipes/serializers/data-integrations-get-data-integration-authorize-url-request.serializer.ts", "src/pipes/serializers/data-integrations-get-user-token-request.serializer.ts", "src/pipes/serializers/data-integrations-list-response-data-connected-account.serializer.ts", diff --git a/docs/PIPES_COMPATIBILITY.md b/docs/PIPES_COMPATIBILITY.md index 55fa4ca2d..335ec82ec 100644 --- a/docs/PIPES_COMPATIBILITY.md +++ b/docs/PIPES_COMPATIBILITY.md @@ -75,6 +75,47 @@ connection and may be `null` even when standard connections exist. With plural opt-in, credential vending without an account selector can return HTTP 409 `account_selection_required` when several connections match. +## Explicit connection creation and reauthorization + +Use `createDataIntegrationApiKey` or +`createDataIntegrationClientCredential` to POST an API-key or client-credentials +connection. Both require `connectionIntent: 'add'` and take no account selector. +POST requests use the SDK's existing idempotency-key handling for retries. + +The existing `updateDataIntegrationApiKey` and +`updateDataIntegrationClientCredentials` methods still use PUT. They keep +compatibility upsert behavior when intent and selector are omitted. To update +an exact connection, supply `connectedAccountId`; an explicit +`connectionIntent: 'reauthorize'` is optional, but requires that selector. +Do not send `add` intent to PUT. + +OAuth imports (`createUserConnectedAccount` and +`createOrganizationConnectedAccount`) accept `connectionIntent: 'add'` in the +body. Omitting it keeps compatibility behavior. Their update counterparts accept +`connectionIntent: 'reauthorize'` and the account selector in the query. +`supportsMultipleConnections` remains accepted on updates, but it does not select +the update target. + +Creating additional connections is still subject to API availability. The +current contract allows `add` for the owner's first connection and otherwise +returns HTTP 404 `multiple_connections_unavailable` until additional creation is +enabled. The SDK propagates that error; it does not fall back to rotating an +existing connection. + +## Provider configuration + +`createDataIntegrationCredential` preserves `credential.config` for OAuth, +API-key, and client-credentials results. It contains provider-declared, +non-secret integration- and installation-scope snapshot values plus current +defaults. It is separate from client-credentials token `metadata`. + +Connected-account detail and provider-list results also preserve `config` for +all authentication methods. Those maps contain stored, non-secret +installation-scope values, rather than the combined/defaulted credential config. +Secret and undeclared values are filtered by the API, not guessed or filtered by +the SDK. An empty map stays empty; missing config from older API responses stays +`undefined`. The legacy `getAccessToken` response is unchanged. + ## Generation boundaries `ConnectedAccountDto` and `DataIntegrationCredentialsDto` remain published @@ -95,3 +136,10 @@ metadata typing. The `DataIntegrationCredentialsResponseCredential` alias files `DataIntegrationVendedCredential` component under its published name and, like the legacy DTOs, stay outside the manifest. The two direct-query DELETE methods are protected with `@oagen-ignore` regions until the Node emitter's helper-signature fix is available. + +API-key and client-credentials PUT options also retain their published flat +interfaces and explicit snake_case serializers. The current emitter passes the +new union request bodies through unchanged, which would send camelCase keys from +the SDK. These interfaces, serializers, and method regions are protected until +that emission path is fixed. They remain in the manifest where the spec still +emits the corresponding paths. diff --git a/src/pipes/interfaces/connected-account.interface.ts b/src/pipes/interfaces/connected-account.interface.ts index b02fb249c..992b87629 100644 --- a/src/pipes/interfaces/connected-account.interface.ts +++ b/src/pipes/interfaces/connected-account.interface.ts @@ -29,7 +29,7 @@ export interface ConnectedAccount { clientId?: string | null; /** The last four characters of the client secret supplied for this connection, or `null` when it can't be read. Only present when `auth_method` is `client_credentials`. */ clientSecretLast4?: string | null; - /** The connection-level configuration values stored for this connection — the fields the provider declares at `installation` scope, excluding any it declares as secret. Only present when `auth_method` is `client_credentials`. */ + /** The connection-level configuration values stored for this connection — the fields the provider declares at `installation` scope, excluding secret and undeclared fields. Returned for every authentication method; empty when no stored values can be disclosed. */ config?: Record; /** * The state of the connected account: diff --git a/src/pipes/interfaces/create-connected-account-state.interface.ts b/src/pipes/interfaces/create-connected-account-state.interface.ts new file mode 100644 index 000000000..2b2e6d768 --- /dev/null +++ b/src/pipes/interfaces/create-connected-account-state.interface.ts @@ -0,0 +1,9 @@ +// This file is auto-generated by oagen. Do not edit. + +export const CreateConnectedAccountState = { + Connected: 'connected', + NeedsReauthorization: 'needs_reauthorization', +} as const; + +export type CreateConnectedAccountState = + (typeof CreateConnectedAccountState)[keyof typeof CreateConnectedAccountState]; diff --git a/src/pipes/interfaces/create-connected-account.interface.ts b/src/pipes/interfaces/create-connected-account.interface.ts new file mode 100644 index 000000000..ebfe19bbe --- /dev/null +++ b/src/pipes/interfaces/create-connected-account.interface.ts @@ -0,0 +1,27 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { CreateConnectedAccountState } from './create-connected-account-state.interface'; + +export interface CreateConnectedAccount { + /** The OAuth access token for the connected account. */ + accessToken?: string; + /** The OAuth refresh token for the connected account. */ + refreshToken?: string; + /** The ISO-8601 timestamp when the access token expires. Required when `access_token` is provided for tokens that expire. */ + expiresAt?: Date; + /** The OAuth scopes granted for this connection. */ + scopes?: string[]; + /** Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. */ + state?: CreateConnectedAccountState; + /** Set to `add` to create another connected account. Omit this field for permanent compatibility behavior. Creating an additional connection is not yet available: until it is, `add` succeeds only when the owner has no connection for this integration, which creates the compatibility connection, and otherwise returns 404 `multiple_connections_unavailable`. */ + connectionIntent?: 'add'; +} + +export interface CreateConnectedAccountResponse { + access_token?: string; + refresh_token?: string; + expires_at?: string; + scopes?: string[]; + state?: CreateConnectedAccountState; + connection_intent?: 'add'; +} diff --git a/src/pipes/interfaces/create-data-integration-api-key-options.interface.ts b/src/pipes/interfaces/create-data-integration-api-key-options.interface.ts new file mode 100644 index 000000000..b83f89860 --- /dev/null +++ b/src/pipes/interfaces/create-data-integration-api-key-options.interface.ts @@ -0,0 +1,18 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { DataIntegrationsCreateApiKeyConnectionRequestConnectionOwner } from './data-integrations-create-api-key-connection-request-connection-owner.interface'; + +export interface CreateDataIntegrationApiKeyOptions { + /** The identifier of the integration. */ + slug: string; + /** A [User](https://workos.com/docs/reference/authkit/user) identifier. */ + userId: string; + /** An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. Required when `connection_owner` is `organization`. */ + organizationId?: string; + /** Whose connection to create or rotate. `user` (the default) addresses the connection owned by `user_id`. `organization` addresses the connection shared by every member of `organization_id`; `user_id` then identifies the member performing the request and must be an active member of the organization. */ + connectionOwner?: DataIntegrationsCreateApiKeyConnectionRequestConnectionOwner; + /** The API key secret to store for this integration. */ + secret: string; + /** Must be `add`: this endpoint only creates another connection. The first connection for an owner shape fills the compatibility slot; later connections are standard. Creating an additional connection is not yet available: until it is, `add` succeeds only when the owner has no connection for this integration and otherwise returns 404 `multiple_connections_unavailable`. */ + connectionIntent: 'add'; +} diff --git a/src/pipes/interfaces/create-data-integration-client-credential-options.interface.ts b/src/pipes/interfaces/create-data-integration-client-credential-options.interface.ts new file mode 100644 index 000000000..4905f551e --- /dev/null +++ b/src/pipes/interfaces/create-data-integration-client-credential-options.interface.ts @@ -0,0 +1,22 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { DataIntegrationsCreateClientCredentialsConnectionRequestConnectionOwner } from './data-integrations-create-client-credentials-connection-request-connection-owner.interface'; + +export interface CreateDataIntegrationClientCredentialOptions { + /** The identifier of the integration. */ + slug: string; + /** A [User](https://workos.com/docs/reference/authkit/user) identifier. */ + userId: string; + /** An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. Required when `connection_owner` is `organization`. */ + organizationId?: string; + /** Whose connection to create or rotate. `user` (the default) addresses the connection owned by `user_id`. `organization` addresses the connection shared by every member of `organization_id`; `user_id` then identifies the member performing the request and must be an active member of the organization. */ + connectionOwner?: DataIntegrationsCreateClientCredentialsConnectionRequestConnectionOwner; + /** The OAuth client ID to store for this integration. */ + clientId: string; + /** The OAuth client secret to store for this integration. */ + clientSecret: string; + /** Provider-specific configuration values collected for this installation, keyed by the provider's config field descriptors. */ + config?: Record; + /** Must be `add`: this endpoint only creates another connection. The first connection for an owner shape fills the compatibility slot; later connections are standard. Creating an additional connection is not yet available: until it is, `add` succeeds only when the owner has no connection for this integration and otherwise returns 404 `multiple_connections_unavailable`. */ + connectionIntent: 'add'; +} diff --git a/src/pipes/interfaces/create-organization-connected-account-options.interface.ts b/src/pipes/interfaces/create-organization-connected-account-options.interface.ts index 85d6cdbc5..5eb78c611 100644 --- a/src/pipes/interfaces/create-organization-connected-account-options.interface.ts +++ b/src/pipes/interfaces/create-organization-connected-account-options.interface.ts @@ -19,4 +19,6 @@ export interface CreateOrganizationConnectedAccountOptions { state?: OrganizationConnectedAccountState; /** The [User](https://workos.com/docs/reference/authkit/user) identifier of the organization member on whose behalf the connected account is being imported or updated. The user must be an active member of the organization. */ userId: string; + /** Explicitly create a connection. Omit for compatibility behavior. Until additional connections are available, `add` returns 404 `multiple_connections_unavailable` if the owner already has a connection. */ + connectionIntent?: 'add'; } diff --git a/src/pipes/interfaces/create-organization-connected-account-state.interface.ts b/src/pipes/interfaces/create-organization-connected-account-state.interface.ts new file mode 100644 index 000000000..7a69c5ab1 --- /dev/null +++ b/src/pipes/interfaces/create-organization-connected-account-state.interface.ts @@ -0,0 +1,9 @@ +// This file is auto-generated by oagen. Do not edit. + +export const CreateOrganizationConnectedAccountState = { + Connected: 'connected', + NeedsReauthorization: 'needs_reauthorization', +} as const; + +export type CreateOrganizationConnectedAccountState = + (typeof CreateOrganizationConnectedAccountState)[keyof typeof CreateOrganizationConnectedAccountState]; diff --git a/src/pipes/interfaces/create-organization-connected-account.interface.ts b/src/pipes/interfaces/create-organization-connected-account.interface.ts new file mode 100644 index 000000000..64fc670b1 --- /dev/null +++ b/src/pipes/interfaces/create-organization-connected-account.interface.ts @@ -0,0 +1,30 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { CreateOrganizationConnectedAccountState } from './create-organization-connected-account-state.interface'; + +export interface CreateOrganizationConnectedAccount { + /** The OAuth access token for the connected account. */ + accessToken?: string; + /** The OAuth refresh token for the connected account. */ + refreshToken?: string; + /** The ISO-8601 timestamp when the access token expires. Required when `access_token` is provided for tokens that expire. */ + expiresAt?: Date; + /** The OAuth scopes granted for this connection. */ + scopes?: string[]; + /** Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. */ + state?: CreateOrganizationConnectedAccountState; + /** The [User](https://workos.com/docs/reference/authkit/user) identifier of the organization member on whose behalf the connected account is being imported or updated. The user must be an active member of the organization. */ + userId: string; + /** Set to `add` to create another connected account. Omit this field for permanent compatibility behavior. Creating an additional connection is not yet available: until it is, `add` succeeds only when the owner has no connection for this integration, which creates the compatibility connection, and otherwise returns 404 `multiple_connections_unavailable`. */ + connectionIntent?: 'add'; +} + +export interface CreateOrganizationConnectedAccountResponse { + access_token?: string; + refresh_token?: string; + expires_at?: string; + scopes?: string[]; + state?: CreateOrganizationConnectedAccountState; + user_id: string; + connection_intent?: 'add'; +} diff --git a/src/pipes/interfaces/create-user-connected-account-options.interface.ts b/src/pipes/interfaces/create-user-connected-account-options.interface.ts index cadacbdd7..28d84cb4f 100644 --- a/src/pipes/interfaces/create-user-connected-account-options.interface.ts +++ b/src/pipes/interfaces/create-user-connected-account-options.interface.ts @@ -19,4 +19,6 @@ export interface CreateUserConnectedAccountOptions { scopes?: string[]; /** Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. */ state?: ConnectedAccountInputState; + /** Explicitly create a connection. Omit for compatibility behavior. Until additional connections are available, `add` returns 404 `multiple_connections_unavailable` if the owner already has a connection. */ + connectionIntent?: 'add'; } diff --git a/src/pipes/interfaces/data-integration-vended-credential.interface.ts b/src/pipes/interfaces/data-integration-vended-credential.interface.ts index ce0091b26..5dda65862 100644 --- a/src/pipes/interfaces/data-integration-vended-credential.interface.ts +++ b/src/pipes/interfaces/data-integration-vended-credential.interface.ts @@ -1,12 +1,17 @@ // @oagen-ignore-file // Preserve legacy property access and metadata typing on the generated variants. -/** A vended credential, discriminated by its authentication method. */ +/** + * A vended credential, discriminated by its authentication method. + * `config` contains provider-declared, non-secret snapshot values with live + * defaults. It is optional for compatibility with older API responses. + */ export type DataIntegrationVendedCredential = | { object: 'credential'; authMethod: 'oauth'; value: string; + config?: Record; expiresAt: string | null; scopes: string[]; missingScopes: string[]; @@ -16,6 +21,7 @@ export type DataIntegrationVendedCredential = object: 'credential'; authMethod: 'api_key'; value: string; + config?: Record; expiresAt?: undefined; scopes?: undefined; missingScopes?: undefined; @@ -25,6 +31,7 @@ export type DataIntegrationVendedCredential = object: 'credential'; authMethod: 'client_credentials'; value: string; + config?: Record; expiresAt: string | null; scopes: string[]; missingScopes: string[]; @@ -37,6 +44,7 @@ export type DataIntegrationVendedCredentialResponse = object: 'credential'; auth_method: 'oauth'; value: string; + config?: Record; expires_at: string | null; scopes: string[]; missing_scopes: string[]; @@ -46,6 +54,7 @@ export type DataIntegrationVendedCredentialResponse = object: 'credential'; auth_method: 'api_key'; value: string; + config?: Record; expires_at?: undefined; scopes?: undefined; missing_scopes?: undefined; @@ -55,6 +64,7 @@ export type DataIntegrationVendedCredentialResponse = object: 'credential'; auth_method: 'client_credentials'; value: string; + config?: Record; expires_at: string | null; scopes: string[]; missing_scopes: string[]; diff --git a/src/pipes/interfaces/data-integrations-create-api-key-connection-request-connection-owner.interface.ts b/src/pipes/interfaces/data-integrations-create-api-key-connection-request-connection-owner.interface.ts new file mode 100644 index 000000000..cc41e086f --- /dev/null +++ b/src/pipes/interfaces/data-integrations-create-api-key-connection-request-connection-owner.interface.ts @@ -0,0 +1,9 @@ +// This file is auto-generated by oagen. Do not edit. + +export const DataIntegrationsCreateApiKeyConnectionRequestConnectionOwner = { + User: 'user', + Organization: 'organization', +} as const; + +export type DataIntegrationsCreateApiKeyConnectionRequestConnectionOwner = + (typeof DataIntegrationsCreateApiKeyConnectionRequestConnectionOwner)[keyof typeof DataIntegrationsCreateApiKeyConnectionRequestConnectionOwner]; diff --git a/src/pipes/interfaces/data-integrations-create-api-key-connection-request.interface.ts b/src/pipes/interfaces/data-integrations-create-api-key-connection-request.interface.ts new file mode 100644 index 000000000..16f3e411d --- /dev/null +++ b/src/pipes/interfaces/data-integrations-create-api-key-connection-request.interface.ts @@ -0,0 +1,24 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { DataIntegrationsCreateApiKeyConnectionRequestConnectionOwner } from './data-integrations-create-api-key-connection-request-connection-owner.interface'; + +export interface DataIntegrationsCreateApiKeyConnectionRequest { + /** A [User](https://workos.com/docs/reference/authkit/user) identifier. */ + userId: string; + /** An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. Required when `connection_owner` is `organization`. */ + organizationId?: string; + /** Whose connection to create or rotate. `user` (the default) addresses the connection owned by `user_id`. `organization` addresses the connection shared by every member of `organization_id`; `user_id` then identifies the member performing the request and must be an active member of the organization. */ + connectionOwner?: DataIntegrationsCreateApiKeyConnectionRequestConnectionOwner; + /** The API key secret to store for this integration. */ + secret: string; + /** Must be `add`: this endpoint only creates another connection. The first connection for an owner shape fills the compatibility slot; later connections are standard. Creating an additional connection is not yet available: until it is, `add` succeeds only when the owner has no connection for this integration and otherwise returns 404 `multiple_connections_unavailable`. */ + connectionIntent: 'add'; +} + +export interface DataIntegrationsCreateApiKeyConnectionRequestResponse { + user_id: string; + organization_id?: string; + connection_owner?: DataIntegrationsCreateApiKeyConnectionRequestConnectionOwner; + secret: string; + connection_intent: 'add'; +} diff --git a/src/pipes/interfaces/data-integrations-create-client-credentials-connection-request-connection-owner.interface.ts b/src/pipes/interfaces/data-integrations-create-client-credentials-connection-request-connection-owner.interface.ts new file mode 100644 index 000000000..79f0ed495 --- /dev/null +++ b/src/pipes/interfaces/data-integrations-create-client-credentials-connection-request-connection-owner.interface.ts @@ -0,0 +1,10 @@ +// This file is auto-generated by oagen. Do not edit. + +export const DataIntegrationsCreateClientCredentialsConnectionRequestConnectionOwner = + { + User: 'user', + Organization: 'organization', + } as const; + +export type DataIntegrationsCreateClientCredentialsConnectionRequestConnectionOwner = + (typeof DataIntegrationsCreateClientCredentialsConnectionRequestConnectionOwner)[keyof typeof DataIntegrationsCreateClientCredentialsConnectionRequestConnectionOwner]; diff --git a/src/pipes/interfaces/data-integrations-create-client-credentials-connection-request.interface.ts b/src/pipes/interfaces/data-integrations-create-client-credentials-connection-request.interface.ts new file mode 100644 index 000000000..ab1accd23 --- /dev/null +++ b/src/pipes/interfaces/data-integrations-create-client-credentials-connection-request.interface.ts @@ -0,0 +1,30 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { DataIntegrationsCreateClientCredentialsConnectionRequestConnectionOwner } from './data-integrations-create-client-credentials-connection-request-connection-owner.interface'; + +export interface DataIntegrationsCreateClientCredentialsConnectionRequest { + /** A [User](https://workos.com/docs/reference/authkit/user) identifier. */ + userId: string; + /** An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. Required when `connection_owner` is `organization`. */ + organizationId?: string; + /** Whose connection to create or rotate. `user` (the default) addresses the connection owned by `user_id`. `organization` addresses the connection shared by every member of `organization_id`; `user_id` then identifies the member performing the request and must be an active member of the organization. */ + connectionOwner?: DataIntegrationsCreateClientCredentialsConnectionRequestConnectionOwner; + /** The OAuth client ID to store for this integration. */ + clientId: string; + /** The OAuth client secret to store for this integration. */ + clientSecret: string; + /** Provider-specific configuration values collected for this installation, keyed by the provider's config field descriptors. */ + config?: Record; + /** Must be `add`: this endpoint only creates another connection. The first connection for an owner shape fills the compatibility slot; later connections are standard. Creating an additional connection is not yet available: until it is, `add` succeeds only when the owner has no connection for this integration and otherwise returns 404 `multiple_connections_unavailable`. */ + connectionIntent: 'add'; +} + +export interface DataIntegrationsCreateClientCredentialsConnectionRequestResponse { + user_id: string; + organization_id?: string; + connection_owner?: DataIntegrationsCreateClientCredentialsConnectionRequestConnectionOwner; + client_id: string; + client_secret: string; + config?: Record; + connection_intent: 'add'; +} diff --git a/src/pipes/interfaces/data-integrations-list-response-data-connected-account.interface.ts b/src/pipes/interfaces/data-integrations-list-response-data-connected-account.interface.ts index d533d4bd3..5cb37b258 100644 --- a/src/pipes/interfaces/data-integrations-list-response-data-connected-account.interface.ts +++ b/src/pipes/interfaces/data-integrations-list-response-data-connected-account.interface.ts @@ -29,7 +29,7 @@ export interface DataIntegrationsListResponseDataConnectedAccount { clientId?: string | null; /** The last four characters of the client secret supplied for this connection, or `null` when it can't be read. Only present when `auth_method` is `client_credentials`. */ clientSecretLast4?: string | null; - /** The connection-level configuration values stored for this connection — the fields the provider declares at `installation` scope, excluding any it declares as secret. Only present when `auth_method` is `client_credentials`. */ + /** The connection-level configuration values stored for this connection — the fields the provider declares at `installation` scope, excluding secret and undeclared fields. Returned for every authentication method; empty when no stored values can be disclosed. */ config?: Record; /** * The state of the connected account: diff --git a/src/pipes/interfaces/data-integrations-upsert-api-key-request.interface.ts b/src/pipes/interfaces/data-integrations-upsert-api-key-request.interface.ts index 2b4671d23..2167ae5ee 100644 --- a/src/pipes/interfaces/data-integrations-upsert-api-key-request.interface.ts +++ b/src/pipes/interfaces/data-integrations-upsert-api-key-request.interface.ts @@ -1,4 +1,5 @@ -// This file is auto-generated by oagen. Do not edit. +// @oagen-ignore-file +// Preserve the published flat options across the API's compatibility/reauthorize union. import type { DataIntegrationsUpsertApiKeyRequestConnectionOwner } from './data-integrations-upsert-api-key-request-connection-owner.interface'; @@ -9,6 +10,8 @@ export interface DataIntegrationsUpsertApiKeyRequest { organizationId?: string; /** A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to rotate a specific existing connection. */ connectedAccountId?: string; + /** Reauthorize the named connected account. Requires `connectedAccountId`; omit both to upsert the compatibility connection. Use POST to add a connection. */ + connectionIntent?: 'reauthorize'; /** Whose connection to create or rotate. `user` (the default) addresses the connection owned by `user_id`. `organization` addresses the connection shared by every member of `organization_id`; `user_id` then identifies the member performing the request and must be an active member of the organization. */ connectionOwner?: DataIntegrationsUpsertApiKeyRequestConnectionOwner; /** The API key secret to store for this integration. */ @@ -19,6 +22,7 @@ export interface DataIntegrationsUpsertApiKeyRequestResponse { user_id: string; organization_id?: string; connected_account_id?: string; + connection_intent?: 'reauthorize'; connection_owner?: DataIntegrationsUpsertApiKeyRequestConnectionOwner; secret: string; } diff --git a/src/pipes/interfaces/data-integrations-upsert-client-credentials-request.interface.ts b/src/pipes/interfaces/data-integrations-upsert-client-credentials-request.interface.ts index 200a80fd2..4f44e5ca1 100644 --- a/src/pipes/interfaces/data-integrations-upsert-client-credentials-request.interface.ts +++ b/src/pipes/interfaces/data-integrations-upsert-client-credentials-request.interface.ts @@ -1,4 +1,5 @@ -// This file is auto-generated by oagen. Do not edit. +// @oagen-ignore-file +// Preserve the published flat options across the API's compatibility/reauthorize union. import type { DataIntegrationsUpsertClientCredentialsRequestConnectionOwner } from './data-integrations-upsert-client-credentials-request-connection-owner.interface'; @@ -9,6 +10,8 @@ export interface DataIntegrationsUpsertClientCredentialsRequest { organizationId?: string; /** A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to rotate a specific existing connection. */ connectedAccountId?: string; + /** Reauthorize the named connected account. Requires `connectedAccountId`; omit both to upsert the compatibility connection. Use POST to add a connection. */ + connectionIntent?: 'reauthorize'; /** Whose connection to create or rotate. `user` (the default) addresses the connection owned by `user_id`. `organization` addresses the connection shared by every member of `organization_id`; `user_id` then identifies the member performing the request and must be an active member of the organization. */ connectionOwner?: DataIntegrationsUpsertClientCredentialsRequestConnectionOwner; /** The OAuth client ID to store for this integration. */ @@ -23,6 +26,7 @@ export interface DataIntegrationsUpsertClientCredentialsRequestResponse { user_id: string; organization_id?: string; connected_account_id?: string; + connection_intent?: 'reauthorize'; connection_owner?: DataIntegrationsUpsertClientCredentialsRequestConnectionOwner; client_id: string; client_secret: string; diff --git a/src/pipes/interfaces/index.ts b/src/pipes/interfaces/index.ts index b3b5a9e2a..df4827429 100644 --- a/src/pipes/interfaces/index.ts +++ b/src/pipes/interfaces/index.ts @@ -10,13 +10,19 @@ export * from './connected-account-input-state.interface'; export * from './connected-account-input.interface'; export * from './connected-account-state.interface'; export * from './connected-account.interface'; +export * from './create-connected-account-state.interface'; +export * from './create-connected-account.interface'; +export * from './create-data-integration-api-key-options.interface'; export * from './create-data-integration-auth-methods.interface'; +export * from './create-data-integration-client-credential-options.interface'; export * from './create-data-integration-credential-options.interface'; export * from './create-data-integration-options.interface'; export * from './create-data-integration-ownership.interface'; export * from './create-data-integration-token-options.interface'; export * from './create-data-integration.interface'; export * from './create-organization-connected-account-options.interface'; +export * from './create-organization-connected-account-state.interface'; +export * from './create-organization-connected-account.interface'; export * from './create-user-connected-account-options.interface'; export * from './custom-provider-definition-authenticate-via.interface'; export * from './custom-provider-definition.interface'; @@ -42,6 +48,10 @@ export * from './data-integration-ownership.interface'; export * from './data-integration-state.interface'; export * from './data-integration-vended-credential.interface'; export * from './data-integration.interface'; +export * from './data-integrations-create-api-key-connection-request-connection-owner.interface'; +export * from './data-integrations-create-api-key-connection-request.interface'; +export * from './data-integrations-create-client-credentials-connection-request-connection-owner.interface'; +export * from './data-integrations-create-client-credentials-connection-request.interface'; export * from './data-integrations-get-data-integration-authorize-url-request-connection-owner.interface'; export * from './data-integrations-get-data-integration-authorize-url-request.interface'; export * from './data-integrations-get-user-token-request-connection-owner.interface'; diff --git a/src/pipes/interfaces/update-organization-connected-account-options.interface.ts b/src/pipes/interfaces/update-organization-connected-account-options.interface.ts index be67a4d68..d0eb06ad3 100644 --- a/src/pipes/interfaces/update-organization-connected-account-options.interface.ts +++ b/src/pipes/interfaces/update-organization-connected-account-options.interface.ts @@ -7,10 +7,12 @@ export interface UpdateOrganizationConnectedAccountOptions { organizationId: string; /** The slug identifier of the provider (e.g., `github`, `slack`, `notion`). */ slug: string; - /** Set to `true` to use the plural connection contract. When omitted or `false`, only the compatibility connection is considered. */ + /** Accepted for compatibility; does not change update targeting. Supply `connectedAccountId` to select a connection. */ supportsMultipleConnections?: boolean; /** A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to select the connection to update. */ connectedAccountId?: string; + /** Set to `reauthorize` with `connectedAccountId` to update an exact connection. Omit both for compatibility behavior. */ + connectionIntent?: 'reauthorize'; /** The OAuth access token for the connected account. */ accessToken?: string; /** The OAuth refresh token for the connected account. */ diff --git a/src/pipes/interfaces/update-user-connected-account-options.interface.ts b/src/pipes/interfaces/update-user-connected-account-options.interface.ts index ceeb581f5..098fd7231 100644 --- a/src/pipes/interfaces/update-user-connected-account-options.interface.ts +++ b/src/pipes/interfaces/update-user-connected-account-options.interface.ts @@ -9,10 +9,12 @@ export interface UpdateUserConnectedAccountOptions { slug: string; /** An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter if the connection is scoped to an organization. */ organizationId?: string; - /** Set to `true` to use the plural connection contract. Defaults to the compatibility connection. */ + /** Accepted for compatibility; does not change update targeting. Supply `connectedAccountId` to select a connection. */ supportsMultipleConnections?: boolean; /** Select the connected account to update. */ connectedAccountId?: string; + /** Set to `reauthorize` with `connectedAccountId` to update an exact connection. Omit both for compatibility behavior. */ + connectionIntent?: 'reauthorize'; /** The OAuth access token for the connected account. */ accessToken?: string; /** The OAuth refresh token for the connected account. */ diff --git a/src/pipes/pipes-api-updates.spec.ts b/src/pipes/pipes-api-updates.spec.ts new file mode 100644 index 000000000..e8ddb567e --- /dev/null +++ b/src/pipes/pipes-api-updates.spec.ts @@ -0,0 +1,309 @@ +// @oagen-ignore-file +// Contracts added after the initial connection-sharing generation. +import fetch from 'jest-fetch-mock'; +import { WorkOS } from '../workos'; +import { + fetchBody, + fetchHeaders, + fetchMethod, + fetchOnce, + fetchSearchParams, + fetchURL, +} from '../common/utils/test-utils'; +import connectedAccountFixture from './fixtures/connected-account.json'; +import providersFixture from './fixtures/data-integrations-list-response.json'; + +const workos = new WorkOS('sk_test_example'); +const config = { account: 'acme', subdomain: 'support' }; +const owner = { + userId: 'user_123', + organizationId: 'org_123', + connectionOwner: 'organization' as const, +}; +const wireOwner = { + user_id: 'user_123', + organization_id: 'org_123', + connection_owner: 'organization', +}; + +beforeEach(() => fetch.resetMocks()); + +describe('Pipes provider config', () => { + it.each(['oauth', 'api_key', 'client_credentials'] as const)( + 'returns vended config for %s without conflating it with token metadata', + async (authMethod) => { + const metadata = { instance_url: 'https://token.example.test' }; + fetchOnce({ + active: true, + credential: { + object: 'credential', + auth_method: authMethod, + value: 'test_secret', + config, + ...(authMethod !== 'api_key' && { + expires_at: null, + scopes: ['read'], + missing_scopes: [], + }), + ...(authMethod === 'client_credentials' && { metadata }), + }, + }); + const result = await workos.pipes.createDataIntegrationCredential({ + slug: 'provider', + ...owner, + }); + const receivedConfig: Record | undefined = + result.credential?.config; + expect(receivedConfig).toEqual(config); + expect(result.credential?.metadata).toEqual( + authMethod === 'client_credentials' ? metadata : undefined, + ); + }, + ); + + it.each([{}, undefined])( + 'preserves empty versus absent vended config: %j', + async (responseConfig) => { + fetchOnce({ + active: true, + credential: { + object: 'credential', + auth_method: 'api_key', + value: 'key', + config: responseConfig, + }, + }); + const result = await workos.pipes.createDataIntegrationCredential({ + slug: 'provider', + ...owner, + }); + expect(result.credential?.config).toEqual(responseConfig); + }, + ); + + describe.each(['oauth', 'api_key', 'client_credentials'] as const)( + '%s connected accounts', + (authMethod) => { + it.each([ + 'getUserConnectedAccount', + 'getOrganizationConnectedAccount', + 'listUserDataProviders', + 'listOrganizationDataProviders', + ] as const)('%s retains connection config', async (method) => { + const account = { + ...connectedAccountFixture, + auth_method: authMethod, + config, + }; + fetchOnce( + method.startsWith('list') + ? { + object: 'list', + data: [ + { + ...providersFixture.data[0], + connected_account: account, + connected_accounts: [account], + }, + ], + } + : account, + ); + const options = { + userId: 'user_123', + organizationId: 'org_123', + slug: 'provider', + supportsMultipleConnections: true, + }; + const result = await workos.pipes[method](options); + if (result.object === 'list') { + expect(result.data[0].connectedAccount?.config).toEqual(config); + expect(result.data[0].connectedAccounts?.[0].config).toEqual(config); + } else { + expect(result.config).toEqual(config); + } + }); + }, + ); +}); + +describe('Pipes explicit connection creation', () => { + it('creates an API-key connection with POST and explicit add intent', async () => { + fetchOnce(connectedAccountFixture); + const result = await workos.pipes.createDataIntegrationApiKey({ + slug: 'provider/slug', + ...owner, + secret: 'test_key', + connectionIntent: 'add', + }); + expect(fetchMethod()).toBe('POST'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/data-integrations/provider%2Fslug/api-key', + ); + expect(fetchBody()).toEqual({ + ...wireOwner, + secret: 'test_key', + connection_intent: 'add', + }); + expect(fetchHeaders()).toHaveProperty( + 'Idempotency-Key', + expect.stringMatching(/^retry-/), + ); + expect(result.id).toBe(connectedAccountFixture.id); + }); + + it('creates a client-credentials connection with POST and installation config', async () => { + fetchOnce(connectedAccountFixture); + await workos.pipes.createDataIntegrationClientCredential({ + slug: 'provider/slug', + ...owner, + clientId: 'client', + clientSecret: 'secret', + config, + connectionIntent: 'add', + }); + expect(fetchMethod()).toBe('POST'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/data-integrations/provider%2Fslug/client-credentials', + ); + expect(fetchBody()).toEqual({ + ...wireOwner, + client_id: 'client', + client_secret: 'secret', + config, + connection_intent: 'add', + }); + expect(fetchHeaders()).toHaveProperty( + 'Idempotency-Key', + expect.stringMatching(/^retry-/), + ); + }); + + it('propagates the creation rollout error without falling back to a PUT', async () => { + fetchOnce( + { + code: 'multiple_connections_unavailable', + message: 'Additional connections are not yet available.', + }, + { status: 404 }, + ); + await expect( + workos.pipes.createDataIntegrationApiKey({ + slug: 'provider', + userId: 'user_123', + secret: 'test_key', + connectionIntent: 'add', + }), + ).rejects.toMatchObject({ + status: 404, + code: 'multiple_connections_unavailable', + }); + expect(fetch.mock.calls).toHaveLength(1); + expect(fetchMethod()).toBe('POST'); + }); + + it.each([ + 'createUserConnectedAccount', + 'createOrganizationConnectedAccount', + ] as const)('%s sends add intent in the import body', async (method) => { + fetchOnce(connectedAccountFixture); + await workos.pipes[method]({ + slug: 'provider', + userId: 'user_123', + organizationId: 'org_123', + accessToken: 'token', + expiresAt: new Date('2026-12-31T00:00:00.000Z'), + connectionIntent: 'add', + }); + expect(fetchMethod()).toBe('POST'); + expect(fetchBody()).toEqual({ + access_token: 'token', + expires_at: '2026-12-31T00:00:00.000Z', + connection_intent: 'add', + ...(method === 'createOrganizationConnectedAccount' && { + user_id: 'user_123', + }), + }); + expect(fetchSearchParams()).toEqual( + method === 'createUserConnectedAccount' + ? { organization_id: 'org_123' } + : {}, + ); + }); +}); + +describe('Pipes exact reauthorization', () => { + it('retains snake_case serialization for API-key PUT requests', async () => { + fetchOnce(connectedAccountFixture); + await workos.pipes.updateDataIntegrationApiKey({ + slug: 'provider', + ...owner, + secret: 'test_key', + connectedAccountId: 'account_123', + connectionIntent: 'reauthorize', + }); + expect(fetchMethod()).toBe('PUT'); + expect(fetchBody()).toEqual({ + ...wireOwner, + secret: 'test_key', + connected_account_id: 'account_123', + connection_intent: 'reauthorize', + }); + }); + + it('retains snake_case serialization for client-credentials PUT requests', async () => { + fetchOnce(connectedAccountFixture); + await workos.pipes.updateDataIntegrationClientCredentials({ + slug: 'provider', + ...owner, + clientId: 'client', + clientSecret: 'secret', + config, + connectedAccountId: 'account_123', + connectionIntent: 'reauthorize', + }); + expect(fetchMethod()).toBe('PUT'); + expect(fetchBody()).toEqual({ + ...wireOwner, + client_id: 'client', + client_secret: 'secret', + config, + connected_account_id: 'account_123', + connection_intent: 'reauthorize', + }); + }); + + it.each([ + 'updateUserConnectedAccount', + 'updateOrganizationConnectedAccount', + ] as const)( + '%s sends reauthorization intent in the query, not the body', + async (method) => { + fetchOnce(connectedAccountFixture); + await workos.pipes[method]({ + slug: 'provider', + userId: 'user_123', + organizationId: 'org_123', + accessToken: 'new_token', + connectedAccountId: 'account_123', + connectionIntent: 'reauthorize', + supportsMultipleConnections: false, + }); + expect(fetchMethod()).toBe('PUT'); + expect(fetchSearchParams()).toEqual({ + connected_account_id: 'account_123', + connection_intent: 'reauthorize', + supports_multiple_connections: 'false', + ...(method === 'updateUserConnectedAccount' && { + organization_id: 'org_123', + }), + }); + expect(fetchBody()).toEqual({ + access_token: 'new_token', + ...(method === 'updateOrganizationConnectedAccount' && { + user_id: 'user_123', + }), + }); + }, + ); +}); diff --git a/src/pipes/pipes.ts b/src/pipes/pipes.ts index 7e7698ead..99a88a9f2 100644 --- a/src/pipes/pipes.ts +++ b/src/pipes/pipes.ts @@ -5,6 +5,12 @@ import type { ListDataIntegrationsOptions } from './interfaces/list-data-integra import { AutoPaginatable } from '../common/utils/pagination'; import { fetchAndDeserialize } from '../common/utils/fetch-and-deserialize'; import type { CreateDataIntegrationOptions } from './interfaces/create-data-integration-options.interface'; +import type { CreateDataIntegrationApiKeyOptions } from './interfaces/create-data-integration-api-key-options.interface'; +import type { CreateDataIntegrationClientCredentialOptions } from './interfaces/create-data-integration-client-credential-options.interface'; +import type { DataIntegrationsCreateApiKeyConnectionRequestResponse } from './interfaces/data-integrations-create-api-key-connection-request.interface'; +import type { DataIntegrationsCreateClientCredentialsConnectionRequestResponse } from './interfaces/data-integrations-create-client-credentials-connection-request.interface'; +import type { CreateConnectedAccountResponse } from './interfaces/create-connected-account.interface'; +import type { CreateOrganizationConnectedAccountResponse } from './interfaces/create-organization-connected-account.interface'; import type { GetDataIntegrationOptions } from './interfaces/get-data-integration-options.interface'; import type { UpdateDataIntegrationOptions } from './interfaces/update-data-integration-options.interface'; import type { DeleteDataIntegrationOptions } from './interfaces/delete-data-integration-options.interface'; @@ -66,6 +72,10 @@ import { deserializeDataIntegrationCredentialsResponse } from './serializers/dat import { deserializeDataIntegrationAccessTokenResponse } from './serializers/data-integration-access-token-response.serializer'; import { deserializeDataIntegrationsListResponse } from './serializers/data-integrations-list-response.serializer'; import { serializeCreateDataIntegration } from './serializers/create-data-integration.serializer'; +import { serializeDataIntegrationsCreateApiKeyConnectionRequest } from './serializers/data-integrations-create-api-key-connection-request.serializer'; +import { serializeDataIntegrationsCreateClientCredentialsConnectionRequest } from './serializers/data-integrations-create-client-credentials-connection-request.serializer'; +import { serializeCreateConnectedAccount } from './serializers/create-connected-account.serializer'; +import { serializeCreateOrganizationConnectedAccount } from './serializers/create-organization-connected-account.serializer'; import { serializeUpdateDataIntegration } from './serializers/update-data-integration.serializer'; import { serializeDataIntegrationsUpsertApiKeyRequest } from './serializers/data-integrations-upsert-api-key-request.serializer'; import { serializeDataIntegrationsGetDataIntegrationAuthorizeUrlRequest } from './serializers/data-integrations-get-data-integration-authorize-url-request.serializer'; @@ -228,6 +238,31 @@ export class Pipes { await this.workos.delete(`/data-integrations/${encodeURIComponent(slug)}`); } + /** + * Create another API key connected account + * + * Requires `connectionIntent: 'add'` and does not accept an account selector. + * Until additional connections are available, this creates the first connection + * or returns 404 `multiple_connections_unavailable` if one already exists. + * Use `updateDataIntegrationApiKey` to rotate the compatibility connection or + * update an exact connection. + */ + async createDataIntegrationApiKey( + options: CreateDataIntegrationApiKeyOptions, + ): Promise { + const { slug, ...payload } = options; + const { data } = await this.workos.post< + ConnectedAccountResponse, + DataIntegrationsCreateApiKeyConnectionRequestResponse + >( + `/data-integrations/${encodeURIComponent(slug)}/api-key`, + serializeDataIntegrationsCreateApiKeyConnectionRequest(payload), + ); + return deserializeConnectedAccount(data); + } + + // @oagen-ignore-start + // Preserve serialization while the emitter passes union request bodies through unchanged. /** * Upsert an API key for a connected account * @@ -267,6 +302,8 @@ export class Pipes { return deserializeConnectedAccount(data); } + // @oagen-ignore-end + /** * Get authorization URL * @@ -304,6 +341,33 @@ export class Pipes { return deserializeDataIntegrationAuthorizeUrlResponse(data); } + /** + * Create another client credentials connected account + * + * Requires `connectionIntent: 'add'` and does not accept an account selector. + * Until additional connections are available, this creates the first connection + * or returns 404 `multiple_connections_unavailable` if one already exists. + * Use `updateDataIntegrationClientCredentials` to rotate the compatibility + * connection or update an exact connection. + */ + async createDataIntegrationClientCredential( + options: CreateDataIntegrationClientCredentialOptions, + ): Promise { + const { slug, ...payload } = options; + const { data } = await this.workos.post< + ConnectedAccountResponse, + DataIntegrationsCreateClientCredentialsConnectionRequestResponse + >( + `/data-integrations/${encodeURIComponent(slug)}/client-credentials`, + serializeDataIntegrationsCreateClientCredentialsConnectionRequest( + payload, + ), + ); + return deserializeConnectedAccount(data); + } + + // @oagen-ignore-start + // Preserve serialization while the emitter passes union request bodies through unchanged. /** * Upsert client credentials for a connected account * @@ -333,10 +397,12 @@ export class Pipes { return deserializeConnectedAccount(data); } + // @oagen-ignore-end + /** * Vend credentials for a connected account * - * Returns credentials for a user-owned or organization-owned connected account. OAuth and client-credentials installations return an access token; API-key installations return the stored secret. Client-credentials responses also include provider metadata. + * Returns credentials for a user-owned or organization-owned connected account. OAuth and client-credentials installations return an access token; API-key installations return the stored secret. Every active credential includes provider-declared, non-secret `config` from the connection snapshot with current defaults. Client-credentials responses also include provider token `metadata`. * @param options - Object containing userId. * @param options.slug - The identifier of the integration. * @example "github" @@ -525,7 +591,7 @@ export class Pipes { /** * Import an organization connected account * - * Imports an organization-owned [connected account](https://workos.com/docs/reference/pipes/connected-account) by providing OAuth tokens directly. Use this to migrate existing connections or set up connections without going through the OAuth flow. + * Imports an organization-owned [connected account](https://workos.com/docs/reference/pipes/connected-account) by providing OAuth tokens directly. Omit `connectionIntent` for compatibility behavior or set it to `add` to explicitly create a connection. Additional connection creation is subject to API availability. * @param options - The request options. * @param options.organizationId - An [Organization](https://workos.com/docs/reference/organization) identifier. * @example "org_01EHZNVPK3SFK441A1RGBFSHRT" @@ -544,12 +610,12 @@ export class Pipes { const { organizationId, slug, ...payload } = options; const { data } = await this.workos.post< ConnectedAccountResponse, - OrganizationConnectedAccountResponse + CreateOrganizationConnectedAccountResponse >( `/organizations/${encodeURIComponent( organizationId, )}/connected_accounts/${encodeURIComponent(slug)}`, - serializeOrganizationConnectedAccount(payload), + serializeCreateOrganizationConnectedAccount(payload), ); return deserializeConnectedAccount(data); } @@ -563,10 +629,10 @@ export class Pipes { * @example "org_01EHZNVPK3SFK441A1RGBFSHRT" * @param options.slug - The slug identifier of the provider (e.g., `github`, `slack`, `notion`). * @example "github" - * @param options.supportsMultipleConnections - Set to `true` to use the plural connection contract. When omitted or `false`, only the compatibility connection is considered. - * @example true + * @param options.supportsMultipleConnections - Accepted for compatibility; does not change update targeting. * @param options.connectedAccountId - A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to select the connection to update. * @example "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" + * @param options.connectionIntent - Set to `reauthorize` with `connectedAccountId`, or omit both to update the compatibility connection. * @returns {Promise} * @throws {BadRequestException} 400 * @throws {UnauthorizedException} 401 @@ -583,6 +649,7 @@ export class Pipes { slug, supportsMultipleConnections, connectedAccountId, + connectionIntent, ...payload } = options; const { data } = await this.workos.put< @@ -597,6 +664,7 @@ export class Pipes { query: { supports_multiple_connections: supportsMultipleConnections, connected_account_id: connectedAccountId, + connection_intent: connectionIntent, }, }, ); @@ -718,7 +786,7 @@ export class Pipes { /** * Import a connected account * - * Imports a [connected account](https://workos.com/docs/reference/pipes/connected-account) for a user by providing OAuth tokens directly. Use this to migrate existing connections or set up connections without going through the OAuth flow. + * Imports a [connected account](https://workos.com/docs/reference/pipes/connected-account) for a user by providing OAuth tokens directly. Omit `connectionIntent` for compatibility behavior or set it to `add` to explicitly create a connection. Additional connection creation is subject to API availability. * @param options - The request body. * @param options.userId - A [User](https://workos.com/docs/reference/authkit/user) identifier. * @example "user_01EHZNVPK3SFK441A1RGBFSHRT" @@ -748,12 +816,12 @@ export class Pipes { const { userId, slug, organizationId, ...payload } = options; const { data } = await this.workos.post< ConnectedAccountResponse, - ConnectedAccountInputResponse + CreateConnectedAccountResponse >( `/user_management/users/${encodeURIComponent( userId, )}/connected_accounts/${encodeURIComponent(slug)}`, - serializeConnectedAccountInput(payload), + serializeCreateConnectedAccount(payload), { query: { organization_id: organizationId } }, ); return deserializeConnectedAccount(data); @@ -770,10 +838,10 @@ export class Pipes { * @example "github" * @param options.organizationId - An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter if the connection is scoped to an organization. * @example "org_01EHZNVPK3SFK441A1RGBFSHRT" - * @param options.supportsMultipleConnections - Set to `true` to use the plural connection contract. When omitted or `false`, only the compatibility connection is considered. - * @example true + * @param options.supportsMultipleConnections - Accepted for compatibility; does not change update targeting. * @param options.connectedAccountId - A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to select the connection to update. * @example "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" + * @param options.connectionIntent - Set to `reauthorize` with `connectedAccountId`, or omit both to update the compatibility connection. * @param options.accessToken - The OAuth access token for the connected account. * @example "gho_16C7e42F292c6912E7710c838347Ae178B4a" * @param options.refreshToken - The OAuth refresh token for the connected account. @@ -800,6 +868,7 @@ export class Pipes { organizationId, supportsMultipleConnections, connectedAccountId, + connectionIntent, ...payload } = options; const { data } = await this.workos.put< @@ -815,6 +884,7 @@ export class Pipes { organization_id: organizationId, supports_multiple_connections: supportsMultipleConnections, connected_account_id: connectedAccountId, + connection_intent: connectionIntent, }, }, ); diff --git a/src/pipes/serializers/create-connected-account.serializer.ts b/src/pipes/serializers/create-connected-account.serializer.ts new file mode 100644 index 000000000..da315ae7e --- /dev/null +++ b/src/pipes/serializers/create-connected-account.serializer.ts @@ -0,0 +1,18 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + CreateConnectedAccount, + CreateConnectedAccountResponse, +} from '../interfaces/create-connected-account.interface'; + +export const serializeCreateConnectedAccount = ( + model: CreateConnectedAccount, +): CreateConnectedAccountResponse => ({ + access_token: model.accessToken, + refresh_token: model.refreshToken, + expires_at: + model.expiresAt != null ? model.expiresAt.toISOString() : undefined, + scopes: model.scopes, + state: model.state, + connection_intent: model.connectionIntent, +}); diff --git a/src/pipes/serializers/create-organization-connected-account.serializer.ts b/src/pipes/serializers/create-organization-connected-account.serializer.ts new file mode 100644 index 000000000..194561315 --- /dev/null +++ b/src/pipes/serializers/create-organization-connected-account.serializer.ts @@ -0,0 +1,19 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + CreateOrganizationConnectedAccount, + CreateOrganizationConnectedAccountResponse, +} from '../interfaces/create-organization-connected-account.interface'; + +export const serializeCreateOrganizationConnectedAccount = ( + model: CreateOrganizationConnectedAccount, +): CreateOrganizationConnectedAccountResponse => ({ + access_token: model.accessToken, + refresh_token: model.refreshToken, + expires_at: + model.expiresAt != null ? model.expiresAt.toISOString() : undefined, + scopes: model.scopes, + state: model.state, + user_id: model.userId, + connection_intent: model.connectionIntent, +}); diff --git a/src/pipes/serializers/data-integration-vended-credential.serializer.ts b/src/pipes/serializers/data-integration-vended-credential.serializer.ts index e30a2b685..b0041e5a1 100644 --- a/src/pipes/serializers/data-integration-vended-credential.serializer.ts +++ b/src/pipes/serializers/data-integration-vended-credential.serializer.ts @@ -17,12 +17,14 @@ export const deserializeDataIntegrationVendedCredential = ( expiresAt: response.expires_at, scopes: response.scopes, missingScopes: response.missing_scopes, + config: response.config, }; case 'api_key': return { authMethod: 'api_key', object: response.object, value: response.value, + config: response.config, }; case 'client_credentials': return { @@ -33,6 +35,7 @@ export const deserializeDataIntegrationVendedCredential = ( scopes: response.scopes, missingScopes: response.missing_scopes, metadata: response.metadata, + config: response.config, }; default: throw new Error( diff --git a/src/pipes/serializers/data-integrations-create-api-key-connection-request.serializer.ts b/src/pipes/serializers/data-integrations-create-api-key-connection-request.serializer.ts new file mode 100644 index 000000000..ed0a4597a --- /dev/null +++ b/src/pipes/serializers/data-integrations-create-api-key-connection-request.serializer.ts @@ -0,0 +1,16 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + DataIntegrationsCreateApiKeyConnectionRequest, + DataIntegrationsCreateApiKeyConnectionRequestResponse, +} from '../interfaces/data-integrations-create-api-key-connection-request.interface'; + +export const serializeDataIntegrationsCreateApiKeyConnectionRequest = ( + model: DataIntegrationsCreateApiKeyConnectionRequest, +): DataIntegrationsCreateApiKeyConnectionRequestResponse => ({ + user_id: model.userId, + organization_id: model.organizationId, + connection_owner: model.connectionOwner, + secret: model.secret, + connection_intent: model.connectionIntent, +}); diff --git a/src/pipes/serializers/data-integrations-create-client-credentials-connection-request.serializer.ts b/src/pipes/serializers/data-integrations-create-client-credentials-connection-request.serializer.ts new file mode 100644 index 000000000..165196687 --- /dev/null +++ b/src/pipes/serializers/data-integrations-create-client-credentials-connection-request.serializer.ts @@ -0,0 +1,19 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + DataIntegrationsCreateClientCredentialsConnectionRequest, + DataIntegrationsCreateClientCredentialsConnectionRequestResponse, +} from '../interfaces/data-integrations-create-client-credentials-connection-request.interface'; + +export const serializeDataIntegrationsCreateClientCredentialsConnectionRequest = + ( + model: DataIntegrationsCreateClientCredentialsConnectionRequest, + ): DataIntegrationsCreateClientCredentialsConnectionRequestResponse => ({ + user_id: model.userId, + organization_id: model.organizationId, + connection_owner: model.connectionOwner, + client_id: model.clientId, + client_secret: model.clientSecret, + config: model.config, + connection_intent: model.connectionIntent, + }); diff --git a/src/pipes/serializers/data-integrations-upsert-api-key-request.serializer.ts b/src/pipes/serializers/data-integrations-upsert-api-key-request.serializer.ts index a86c1529a..2ff646486 100644 --- a/src/pipes/serializers/data-integrations-upsert-api-key-request.serializer.ts +++ b/src/pipes/serializers/data-integrations-upsert-api-key-request.serializer.ts @@ -1,4 +1,5 @@ -// This file is auto-generated by oagen. Do not edit. +// @oagen-ignore-file +// Keep snake_case serialization for the legacy flat options and exact selectors. import type { DataIntegrationsUpsertApiKeyRequest, @@ -11,6 +12,7 @@ export const serializeDataIntegrationsUpsertApiKeyRequest = ( user_id: model.userId, organization_id: model.organizationId, connected_account_id: model.connectedAccountId, + connection_intent: model.connectionIntent, connection_owner: model.connectionOwner, secret: model.secret, }); diff --git a/src/pipes/serializers/data-integrations-upsert-client-credentials-request.serializer.ts b/src/pipes/serializers/data-integrations-upsert-client-credentials-request.serializer.ts index 93d89b861..cae4f813b 100644 --- a/src/pipes/serializers/data-integrations-upsert-client-credentials-request.serializer.ts +++ b/src/pipes/serializers/data-integrations-upsert-client-credentials-request.serializer.ts @@ -1,4 +1,5 @@ -// This file is auto-generated by oagen. Do not edit. +// @oagen-ignore-file +// Keep snake_case serialization for the legacy flat options and exact selectors. import type { DataIntegrationsUpsertClientCredentialsRequest, @@ -11,6 +12,7 @@ export const serializeDataIntegrationsUpsertClientCredentialsRequest = ( user_id: model.userId, organization_id: model.organizationId, connected_account_id: model.connectedAccountId, + connection_intent: model.connectionIntent, connection_owner: model.connectionOwner, client_id: model.clientId, client_secret: model.clientSecret, diff --git a/src/pipes/serializers/index.ts b/src/pipes/serializers/index.ts index 77b6f597c..7112ad6d8 100644 --- a/src/pipes/serializers/index.ts +++ b/src/pipes/serializers/index.ts @@ -3,7 +3,9 @@ export * from './api-key-installation.serializer'; export * from './connected-account.serializer'; export * from './connected-account-dto.serializer'; +export * from './create-connected-account.serializer'; export * from './create-data-integration.serializer'; +export * from './create-organization-connected-account.serializer'; export * from './connected-account-input.serializer'; export * from './custom-provider-definition.serializer'; export * from './data-integration.serializer'; @@ -18,6 +20,8 @@ export * from './data-integration-credentials-response-credential.serializer'; export * from './data-integration-custom-provider.serializer'; export * from './data-integration-installation.serializer'; export * from './data-integration-vended-credential.serializer'; +export * from './data-integrations-create-api-key-connection-request.serializer'; +export * from './data-integrations-create-client-credentials-connection-request.serializer'; export * from './data-integrations-get-data-integration-authorize-url-request.serializer'; export * from './data-integrations-get-user-token-request.serializer'; export * from './data-integrations-list-response.serializer'; From f294105ca9d1480d99f276007b5d58212cd66b0f Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 25 Sep 2026 21:10:25 +0000 Subject: [PATCH 5/5] docs(pipes): Clarify idempotency-key note for POST routes Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- docs/PIPES_COMPATIBILITY.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/PIPES_COMPATIBILITY.md b/docs/PIPES_COMPATIBILITY.md index 335ec82ec..32478a5f6 100644 --- a/docs/PIPES_COMPATIBILITY.md +++ b/docs/PIPES_COMPATIBILITY.md @@ -80,7 +80,8 @@ opt-in, credential vending without an account selector can return HTTP 409 Use `createDataIntegrationApiKey` or `createDataIntegrationClientCredential` to POST an API-key or client-credentials connection. Both require `connectionIntent: 'add'` and take no account selector. -POST requests use the SDK's existing idempotency-key handling for retries. +POST requests use the SDK's existing idempotency-key handling for retries once +the API honors `Idempotency-Key` on these routes. The existing `updateDataIntegrationApiKey` and `updateDataIntegrationClientCredentials` methods still use PUT. They keep