diff --git a/src/pipes/fixtures/connected-account.json b/src/pipes/fixtures/connected-account.json index 47260b9ed..9bb04f217 100644 --- a/src/pipes/fixtures/connected-account.json +++ b/src/pipes/fixtures/connected-account.json @@ -4,6 +4,8 @@ "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", "organization_id": null, "scopes": ["repo", "user:email"], + "auth_method": "oauth", + "api_key_last_4": null, "state": "connected", "created_at": "2024-01-16T14:20:00.000Z", "updated_at": "2024-01-16T14:20:00.000Z" diff --git a/src/pipes/fixtures/data-integration-access-token-response.json b/src/pipes/fixtures/data-integration-access-token-response.json index 2b0c3da0c..14f59204b 100644 --- a/src/pipes/fixtures/data-integration-access-token-response.json +++ b/src/pipes/fixtures/data-integration-access-token-response.json @@ -6,5 +6,6 @@ "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-response-credential.json b/src/pipes/fixtures/data-integration-credentials-response-credential.json new file mode 100644 index 000000000..ce1353452 --- /dev/null +++ b/src/pipes/fixtures/data-integration-credentials-response-credential.json @@ -0,0 +1,8 @@ +{ + "object": "credential", + "auth_method": "oauth", + "value": "gho_16C7e42F292c6912E7710c838347Ae178B4a", + "expires_at": "2025-12-31T23:59:59.000Z", + "scopes": ["repo", "user:email"], + "missing_scopes": [] +} diff --git a/src/pipes/fixtures/data-integration-credentials-response.json b/src/pipes/fixtures/data-integration-credentials-response.json new file mode 100644 index 000000000..ab0ad7979 --- /dev/null +++ b/src/pipes/fixtures/data-integration-credentials-response.json @@ -0,0 +1,12 @@ +{ + "active": true, + "credential": { + "object": "credential", + "auth_method": "oauth", + "value": "gho_16C7e42F292c6912E7710c838347Ae178B4a", + "expires_at": "2025-12-31T23:59:59.000Z", + "scopes": ["repo", "user:email"], + "missing_scopes": [] + }, + "error": "not_installed" +} 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 17097fac7..997f762c3 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 @@ -4,6 +4,8 @@ "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", "organization_id": "test_organizationId", "scopes": ["repo", "user:email"], + "auth_method": "oauth", + "api_key_last_4": null, "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 8a79e6da9..6e3d95f66 100644 --- a/src/pipes/fixtures/data-integrations-list-response-data.json +++ b/src/pipes/fixtures/data-integrations-list-response-data.json @@ -7,6 +7,7 @@ "integration_type": "test_integrationType", "credentials_type": "test_credentialsType", "scopes": ["repo", "user:email"], + "auth_methods": ["oauth"], "ownership": "userland_user", "created_at": "test_createdAt", "updated_at": "test_updatedAt", @@ -16,6 +17,8 @@ "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", "organization_id": "test_organizationId", "scopes": ["repo", "user:email"], + "auth_method": "oauth", + "api_key_last_4": null, "state": "connected", "created_at": "test_createdAt", "updated_at": "test_updatedAt", diff --git a/src/pipes/fixtures/data-integrations-list-response.json b/src/pipes/fixtures/data-integrations-list-response.json index b0ab94489..1b98060ac 100644 --- a/src/pipes/fixtures/data-integrations-list-response.json +++ b/src/pipes/fixtures/data-integrations-list-response.json @@ -10,6 +10,7 @@ "integration_type": "test_integrationType", "credentials_type": "test_credentialsType", "scopes": ["repo", "user:email"], + "auth_methods": ["oauth"], "ownership": "userland_user", "created_at": "test_createdAt", "updated_at": "test_updatedAt", @@ -19,6 +20,8 @@ "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", "organization_id": "test_organizationId", "scopes": ["repo", "user:email"], + "auth_method": "oauth", + "api_key_last_4": null, "state": "connected", "created_at": "test_createdAt", "updated_at": "test_updatedAt", diff --git a/src/pipes/fixtures/data-integrations-upsert-api-key-request.json b/src/pipes/fixtures/data-integrations-upsert-api-key-request.json new file mode 100644 index 000000000..bf0bd81be --- /dev/null +++ b/src/pipes/fixtures/data-integrations-upsert-api-key-request.json @@ -0,0 +1,5 @@ +{ + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "secret": "sk-1234567890abcdef" +} diff --git a/src/pipes/fixtures/data-integrations-vend-credentials-request.json b/src/pipes/fixtures/data-integrations-vend-credentials-request.json new file mode 100644 index 000000000..ef8c44380 --- /dev/null +++ b/src/pipes/fixtures/data-integrations-vend-credentials-request.json @@ -0,0 +1,4 @@ +{ + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT" +} diff --git a/src/pipes/interfaces/connected-account-auth-method.interface.ts b/src/pipes/interfaces/connected-account-auth-method.interface.ts new file mode 100644 index 000000000..e64ca1398 --- /dev/null +++ b/src/pipes/interfaces/connected-account-auth-method.interface.ts @@ -0,0 +1,9 @@ +// This file is auto-generated by oagen. Do not edit. + +export const ConnectedAccountAuthMethod = { + OAuth: 'oauth', + ApiKey: 'api_key', +} as const; + +export type ConnectedAccountAuthMethod = + (typeof ConnectedAccountAuthMethod)[keyof typeof ConnectedAccountAuthMethod]; diff --git a/src/pipes/interfaces/connected-account.interface.ts b/src/pipes/interfaces/connected-account.interface.ts index b027f50ea..5d180b7b8 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 { ConnectedAccountAuthMethod } from './connected-account-auth-method.interface'; import type { ConnectedAccountState } from './connected-account-state.interface'; export interface ConnectedAccount { @@ -13,6 +14,10 @@ export interface ConnectedAccount { 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. */ + authMethod?: ConnectedAccountAuthMethod; + /** The last four characters of the API key, or `null` for OAuth connections. */ + apiKeyLast4?: string | null; /** * The state of the connected account: * - `connected`: The connection is active and tokens are valid. @@ -32,6 +37,8 @@ export interface ConnectedAccountResponse { user_id: string | null; organization_id: string | null; scopes: string[]; + auth_method?: ConnectedAccountAuthMethod; + api_key_last_4?: string | null; state: ConnectedAccountState; created_at: string; updated_at: string; diff --git a/src/pipes/interfaces/create-data-integration-credential-options.interface.ts b/src/pipes/interfaces/create-data-integration-credential-options.interface.ts new file mode 100644 index 000000000..2a4b53dbd --- /dev/null +++ b/src/pipes/interfaces/create-data-integration-credential-options.interface.ts @@ -0,0 +1,10 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface CreateDataIntegrationCredentialOptions { + /** 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/data-integration-credentials-response-credential.interface.ts b/src/pipes/interfaces/data-integration-credentials-response-credential.interface.ts new file mode 100644 index 000000000..59290742e --- /dev/null +++ b/src/pipes/interfaces/data-integration-credentials-response-credential.interface.ts @@ -0,0 +1,26 @@ +// This file is auto-generated by oagen. Do not edit. + +/** 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[]; +} + +export interface DataIntegrationCredentialsResponseCredentialResponse { + object: 'credential'; + auth_method: 'oauth'; + value: string; + expires_at: string | null; + scopes: string[]; + missing_scopes: string[]; +} diff --git a/src/pipes/interfaces/data-integration-credentials-response-error.interface.ts b/src/pipes/interfaces/data-integration-credentials-response-error.interface.ts new file mode 100644 index 000000000..28da6b1c9 --- /dev/null +++ b/src/pipes/interfaces/data-integration-credentials-response-error.interface.ts @@ -0,0 +1,9 @@ +// This file is auto-generated by oagen. Do not edit. + +export const DataIntegrationCredentialsResponseError = { + NotInstalled: 'not_installed', + NeedsReauthorization: 'needs_reauthorization', +} as const; + +export type DataIntegrationCredentialsResponseError = + (typeof DataIntegrationCredentialsResponseError)[keyof typeof DataIntegrationCredentialsResponseError]; diff --git a/src/pipes/interfaces/data-integration-credentials-response.interface.ts b/src/pipes/interfaces/data-integration-credentials-response.interface.ts new file mode 100644 index 000000000..458d23a82 --- /dev/null +++ b/src/pipes/interfaces/data-integration-credentials-response.interface.ts @@ -0,0 +1,26 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + DataIntegrationCredentialsResponseCredential, + DataIntegrationCredentialsResponseCredentialResponse, +} 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 interface DataIntegrationCredentialsResponseWire { + active?: true; + credential?: DataIntegrationCredentialsResponseCredentialResponse; + error?: DataIntegrationCredentialsResponseError; +} 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 new file mode 100644 index 000000000..dfe4175b9 --- /dev/null +++ b/src/pipes/interfaces/data-integrations-list-response-data-auth-methods.interface.ts @@ -0,0 +1,9 @@ +// This file is auto-generated by oagen. Do not edit. + +export const DataIntegrationsListResponseDataAuthMethods = { + OAuth: 'oauth', + ApiKey: 'api_key', +} as const; + +export type DataIntegrationsListResponseDataAuthMethods = + (typeof DataIntegrationsListResponseDataAuthMethods)[keyof typeof 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 new file mode 100644 index 000000000..892bf63e8 --- /dev/null +++ b/src/pipes/interfaces/data-integrations-list-response-data-connected-account-auth-method.interface.ts @@ -0,0 +1,9 @@ +// This file is auto-generated by oagen. Do not edit. + +export const DataIntegrationsListResponseDataConnectedAccountAuthMethod = { + OAuth: 'oauth', + ApiKey: 'api_key', +} as const; + +export type DataIntegrationsListResponseDataConnectedAccountAuthMethod = + (typeof DataIntegrationsListResponseDataConnectedAccountAuthMethod)[keyof typeof DataIntegrationsListResponseDataConnectedAccountAuthMethod]; 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 3912b16bd..fd7da74b4 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 { 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'; export interface DataIntegrationsListResponseDataConnectedAccount { @@ -13,6 +14,10 @@ export interface DataIntegrationsListResponseDataConnectedAccount { 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. */ + authMethod?: DataIntegrationsListResponseDataConnectedAccountAuthMethod; + /** The last four characters of the API key, or `null` for OAuth connections. */ + apiKeyLast4?: string | null; /** * The state of the connected account: * - `connected`: The connection is active and tokens are valid. @@ -37,6 +42,8 @@ export interface DataIntegrationsListResponseDataConnectedAccountResponse { user_id: string | null; organization_id: string | null; scopes: string[]; + auth_method?: DataIntegrationsListResponseDataConnectedAccountAuthMethod; + api_key_last_4?: string | null; state: DataIntegrationsListResponseDataConnectedAccountState; created_at: string; updated_at: string; 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 ba78aeb69..8d3206c5d 100644 --- a/src/pipes/interfaces/data-integrations-list-response-data.interface.ts +++ b/src/pipes/interfaces/data-integrations-list-response-data.interface.ts @@ -4,6 +4,7 @@ import type { DataIntegrationsListResponseDataConnectedAccount, DataIntegrationsListResponseDataConnectedAccountResponse, } from './data-integrations-list-response-data-connected-account.interface'; +import type { DataIntegrationsListResponseDataAuthMethods } from './data-integrations-list-response-data-auth-methods.interface'; import type { DataIntegrationsListResponseDataOwnership } from './data-integrations-list-response-data-ownership.interface'; export interface DataIntegrationsListResponseData { @@ -23,6 +24,8 @@ 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. */ + authMethods?: DataIntegrationsListResponseDataAuthMethods[]; /** Whether the provider is owned by a user or organization. */ ownership: DataIntegrationsListResponseDataOwnership; /** The timestamp when the provider was created. */ @@ -42,6 +45,7 @@ export interface DataIntegrationsListResponseDataResponse { integration_type: string; credentials_type: string; scopes: string[] | null; + auth_methods?: DataIntegrationsListResponseDataAuthMethods[]; ownership: DataIntegrationsListResponseDataOwnership; created_at: string; updated_at: string; 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 new file mode 100644 index 000000000..81d215a35 --- /dev/null +++ b/src/pipes/interfaces/data-integrations-upsert-api-key-request.interface.ts @@ -0,0 +1,16 @@ +// This file is auto-generated by oagen. Do not edit. + +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. */ + organizationId?: string; + /** The API key secret to store for this integration. */ + secret: string; +} + +export interface DataIntegrationsUpsertApiKeyRequestResponse { + user_id: string; + organization_id?: string; + secret: string; +} diff --git a/src/pipes/interfaces/data-integrations-vend-credentials-request.interface.ts b/src/pipes/interfaces/data-integrations-vend-credentials-request.interface.ts new file mode 100644 index 000000000..eccd207a6 --- /dev/null +++ b/src/pipes/interfaces/data-integrations-vend-credentials-request.interface.ts @@ -0,0 +1,13 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface DataIntegrationsVendCredentialsRequest { + /** 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; +} + +export interface DataIntegrationsVendCredentialsRequestResponse { + user_id: string; + organization_id?: string; +} diff --git a/src/pipes/interfaces/get-access-token-options.interface.ts b/src/pipes/interfaces/get-access-token-options.interface.ts new file mode 100644 index 000000000..f5d18d739 --- /dev/null +++ b/src/pipes/interfaces/get-access-token-options.interface.ts @@ -0,0 +1,10 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface GetAccessTokenOptions { + /** 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/index.ts b/src/pipes/interfaces/index.ts index 46441c501..e573b302b 100644 --- a/src/pipes/interfaces/index.ts +++ b/src/pipes/interfaces/index.ts @@ -2,21 +2,30 @@ export * from './access-token.interface'; export * from './authorize-data-integration-options.interface'; +export * from './connected-account-auth-method.interface'; export * from './connected-account-state.interface'; export * from './connected-account.interface'; +export * from './create-data-integration-credential-options.interface'; export * from './create-data-integration-token-options.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-authorize-url-response.interface'; +export * from './data-integration-credentials-response-credential.interface'; +export * from './data-integration-credentials-response-error.interface'; export * from './data-integrations-get-data-integration-authorize-url-request.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-state.interface'; export * from './data-integrations-list-response-data-connected-account.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.interface'; +export * from './data-integrations-vend-credentials-request.interface'; export * from './delete-user-connected-account-options.interface'; -export * from './get-access-token.interface'; +export * from './get-access-token-options.interface'; export * from './get-user-connected-account-options.interface'; export * from './list-user-data-providers-options.interface'; +export * from './update-data-integration-api-key-options.interface'; 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 new file mode 100644 index 000000000..8731fa0ed --- /dev/null +++ b/src/pipes/interfaces/update-data-integration-api-key-options.interface.ts @@ -0,0 +1,12 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface UpdateDataIntegrationApiKeyOptions { + /** 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/pipes.spec.ts b/src/pipes/pipes.spec.ts index 5f0ece960..2a8de4c25 100644 --- a/src/pipes/pipes.spec.ts +++ b/src/pipes/pipes.spec.ts @@ -9,16 +9,52 @@ import { } from '../common/utils/test-utils'; import { WorkOS } from '../workos'; +import connectedAccountFixture from './fixtures/connected-account.json'; import dataIntegrationAuthorizeUrlResponseFixture from './fixtures/data-integration-authorize-url-response.json'; +import dataIntegrationCredentialsResponseFixture from './fixtures/data-integration-credentials-response.json'; import dataIntegrationAccessTokenResponseFixture from './fixtures/data-integration-access-token-response.json'; -import connectedAccountFixture from './fixtures/connected-account.json'; import dataIntegrationsListResponseFixture from './fixtures/data-integrations-list-response.json'; const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU'); +function expectConnectedAccount(result: any) { + expect(result.object).toBe('connected_account'); + expect(result.id).toBe('data_installation_01EHZNVPK3SFK441A1RGBFSHRT'); + expect(result.userId).toBe('user_01EHZNVPK3SFK441A1RGBFSHRT'); + expect(result.organizationId).toBeNull(); + expect(result.scopes).toEqual(['repo', 'user:email']); + expect(result.state).toBe('connected'); + expect(result.createdAt).toBe('2024-01-16T14:20:00.000Z'); + expect(result.updatedAt).toBe('2024-01-16T14:20:00.000Z'); +} + describe('Pipes', () => { beforeEach(() => fetch.resetMocks()); + describe('updateDataIntegrationApiKey', () => { + it('sends the correct request and returns result', async () => { + fetchOnce(connectedAccountFixture); + + const result = await workos.pipes.updateDataIntegrationApiKey({ + slug: 'test_slug', + userId: 'user_id_01234', + secret: 'test_secret', + }); + + expect(fetchMethod()).toBe('PUT'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/data-integrations/test_slug/api-key', + ); + expect(fetchBody()).toEqual( + expect.objectContaining({ + user_id: 'user_id_01234', + secret: 'test_secret', + }), + ); + expectConnectedAccount(result); + }); + }); + describe('authorizeDataIntegration', () => { it('sends the correct request and returns result', async () => { fetchOnce(dataIntegrationAuthorizeUrlResponseFixture); @@ -39,58 +75,45 @@ describe('Pipes', () => { 'https://api.workos.com/data-integrations/q2czJKmVAraSBg8xFpT7M9uR/authorize-redirect', ); }); - - it('throws when the API responds with an error', async () => { - fetchOnce({ message: 'Bad Request' }, { status: 400 }); - - await expect( - workos.pipes.authorizeDataIntegration({ - slug: 'test_slug', - userId: 'user_id_01234', - }), - ).rejects.toThrow(); - }); }); - describe('getAccessToken', () => { + describe('createDataIntegrationCredential', () => { it('sends the correct request and returns result', async () => { - fetchOnce(dataIntegrationAccessTokenResponseFixture); + fetchOnce(dataIntegrationCredentialsResponseFixture); - const result = await workos.pipes.getAccessToken({ - provider: 'test_provider', + const result = await workos.pipes.createDataIntegrationCredential({ + slug: 'test_slug', userId: 'user_id_01234', }); expect(fetchMethod()).toBe('POST'); expect(new URL(String(fetchURL())).pathname).toBe( - '/data-integrations/test_provider/token', + '/data-integrations/test_slug/credentials', ); expect(fetchBody()).toEqual( expect.objectContaining({ user_id: 'user_id_01234' }), ); expect(result).toBeDefined(); }); + }); - it('returns the active=false response variant', async () => { - fetchOnce({ active: false, error: 'not_installed' }); + describe('getAccessToken', () => { + it('sends the correct request and returns result', async () => { + fetchOnce(dataIntegrationAccessTokenResponseFixture); const result = await workos.pipes.getAccessToken({ provider: 'test_provider', userId: 'user_id_01234', }); - expect(result.active).toBe(false); - }); - - it('throws when the API responds with an error', async () => { - fetchOnce({ message: 'Bad Request' }, { status: 400 }); - - await expect( - workos.pipes.getAccessToken({ - provider: 'test_provider', - userId: 'user_id_01234', - }), - ).rejects.toThrow(); + expect(fetchMethod()).toBe('POST'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/data-integrations/test_provider/token', + ); + expect(fetchBody()).toEqual( + expect.objectContaining({ user_id: 'user_id_01234' }), + ); + expect(result).toBeDefined(); }); }); @@ -108,26 +131,7 @@ describe('Pipes', () => { expect(new URL(String(fetchURL())).pathname).toBe( '/user_management/users/test_userId/connected_accounts/test_slug', ); - expect(result.object).toBe('connected_account'); - expect(result.id).toBe('data_installation_01EHZNVPK3SFK441A1RGBFSHRT'); - expect(result.userId).toBe('user_01EHZNVPK3SFK441A1RGBFSHRT'); - expect(result.organizationId).toBeNull(); - expect(result.scopes).toEqual(['repo', 'user:email']); - expect(result.state).toBe('connected'); - expect(result.createdAt).toBe('2024-01-16T14:20:00.000Z'); - expect(result.updatedAt).toBe('2024-01-16T14:20:00.000Z'); - }); - - it('throws when the API responds with an error', async () => { - fetchOnce({ message: 'Bad Request' }, { status: 400 }); - - await expect( - workos.pipes.getUserConnectedAccount({ - userId: 'test_userId', - slug: 'test_slug', - organizationId: 'org_01EHZNVPK3SFK441A1RGBFSHRT', - }), - ).rejects.toThrow(); + expectConnectedAccount(result); }); }); @@ -146,18 +150,6 @@ describe('Pipes', () => { '/user_management/users/test_userId/connected_accounts/test_slug', ); }); - - it('throws when the API responds with an error', async () => { - fetchOnce({ message: 'Bad Request' }, { status: 400 }); - - await expect( - workos.pipes.deleteUserConnectedAccount({ - userId: 'test_userId', - slug: 'test_slug', - organizationId: 'org_01EHZNVPK3SFK441A1RGBFSHRT', - }), - ).rejects.toThrow(); - }); }); describe('listUserDataProviders', () => { @@ -175,16 +167,5 @@ describe('Pipes', () => { ); expect(result.object).toBe('list'); }); - - it('throws when the API responds with an error', async () => { - fetchOnce({ message: 'Bad Request' }, { status: 400 }); - - await expect( - workos.pipes.listUserDataProviders({ - userId: 'test_userId', - organizationId: 'org_01EHZNVPK3SFK441A1RGBFSHRT', - }), - ).rejects.toThrow(); - }); }); }); diff --git a/src/pipes/pipes.ts b/src/pipes/pipes.ts index 5a2beec79..bee9465c3 100644 --- a/src/pipes/pipes.ts +++ b/src/pipes/pipes.ts @@ -1,46 +1,91 @@ // This file is auto-generated by oagen. Do not edit. import type { WorkOS } from '../workos'; +import type { UpdateDataIntegrationApiKeyOptions } from './interfaces/update-data-integration-api-key-options.interface'; import type { AuthorizeDataIntegrationOptions } from './interfaces/authorize-data-integration-options.interface'; -import type { GetAccessTokenOptions } from './interfaces/get-access-token.interface'; +import type { CreateDataIntegrationCredentialOptions } from './interfaces/create-data-integration-credential-options.interface'; +import type { GetAccessTokenOptions } from './interfaces/get-access-token-options.interface'; import type { GetUserConnectedAccountOptions } from './interfaces/get-user-connected-account-options.interface'; import type { DeleteUserConnectedAccountOptions } from './interfaces/delete-user-connected-account-options.interface'; import type { ListUserDataProvidersOptions } from './interfaces/list-user-data-providers-options.interface'; +import type { + ConnectedAccount, + ConnectedAccountResponse, +} from './interfaces/connected-account.interface'; import type { DataIntegrationAuthorizeUrlResponse, DataIntegrationAuthorizeUrlResponseWire, } from './interfaces/data-integration-authorize-url-response.interface'; +import type { + DataIntegrationCredentialsResponse, + DataIntegrationCredentialsResponseWire, +} from './interfaces/data-integration-credentials-response.interface'; import type { DataIntegrationAccessTokenResponse, DataIntegrationAccessTokenResponseWire, } from './interfaces/data-integration-access-token-response.interface'; -import type { - ConnectedAccount, - ConnectedAccountResponse, -} from './interfaces/connected-account.interface'; import type { DataIntegrationsListResponse, DataIntegrationsListResponseWire, } from './interfaces/data-integrations-list-response.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 { DataIntegrationsVendCredentialsRequestResponse } from './interfaces/data-integrations-vend-credentials-request.interface'; import type { DataIntegrationsGetUserTokenRequestResponse } from './interfaces/data-integrations-get-user-token-request.interface'; +import { deserializeConnectedAccount } from './serializers/connected-account.serializer'; import { deserializeDataIntegrationAuthorizeUrlResponse } from './serializers/data-integration-authorize-url-response.serializer'; +import { deserializeDataIntegrationCredentialsResponse } from './serializers/data-integration-credentials-response.serializer'; import { deserializeDataIntegrationAccessTokenResponse } from './serializers/data-integration-access-token-response.serializer'; -import { deserializeConnectedAccount } from './serializers/connected-account.serializer'; import { deserializeDataIntegrationsListResponse } from './serializers/data-integrations-list-response.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 { serializeDataIntegrationsVendCredentialsRequest } from './serializers/data-integrations-vend-credentials-request.serializer'; import { serializeDataIntegrationsGetUserTokenRequest } from './serializers/data-integrations-get-user-token-request.serializer'; export class Pipes { constructor(private readonly workos: WorkOS) {} + /** + * 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. + * @param options - The request options. + * @param options.slug - The identifier of the integration. + * @example "github" + * @returns {Promise} + * @throws {BadRequestException} 400 + * @throws {UnauthorizedException} 401 + * @throws {AuthorizationException} 403 + * @throws {NotFoundException} 404 + * @throws {UnprocessableEntityException} 422 + */ + async updateDataIntegrationApiKey( + options: UpdateDataIntegrationApiKeyOptions, + ): Promise { + const { slug, ...payload } = options; + const { data } = await this.workos.put< + ConnectedAccountResponse, + DataIntegrationsUpsertApiKeyRequestResponse + >( + `/data-integrations/${encodeURIComponent(slug)}/api-key`, + serializeDataIntegrationsUpsertApiKeyRequest(payload), + ); + return deserializeConnectedAccount(data); + } + /** * Get authorization URL * * Generates an OAuth authorization URL to initiate the connection flow for a user. Redirect the user to the returned URL to begin the OAuth flow with the third-party provider. - * @param options - The request options. + * @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. + * @example "user_01EHZNVPK3SFK441A1RGBFSHRT" + * @param options.organizationId - An organization ID to scope the authorization to a specific organization. + * @example "org_01EHZNVPK3SFK441A1RGBFSHRT" + * @param options.returnTo - The URL to redirect the user to after authorization. + * @example "https://example.com/callback" * @returns {Promise} * @throws {BadRequestException} 400 * @throws {UnauthorizedException} 401 @@ -61,6 +106,32 @@ export class Pipes { return deserializeDataIntegrationAuthorizeUrlResponse(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. + * @param options - The request options. + * @param options.slug - The identifier of the integration. + * @example "github" + * @returns {Promise} + * @throws {BadRequestException} 400 + * @throws {UnauthorizedException} 401 + * @throws {NotFoundException} 404 + */ + async createDataIntegrationCredential( + options: CreateDataIntegrationCredentialOptions, + ): Promise { + const { slug, ...payload } = options; + const { data } = await this.workos.post< + DataIntegrationCredentialsResponseWire, + DataIntegrationsVendCredentialsRequestResponse + >( + `/data-integrations/${encodeURIComponent(slug)}/credentials`, + serializeDataIntegrationsVendCredentialsRequest(payload), + ); + return deserializeDataIntegrationCredentialsResponse(data); + } + /** * Get an access token for a connected account * @@ -76,9 +147,10 @@ export class Pipes { * @throws {BadRequestException} 400 * @throws {UnauthorizedException} 401 * @throws {NotFoundException} 404 + * @throws {UnprocessableEntityException} 422 */ async getAccessToken( - options: GetAccessTokenOptions & { provider: string }, + options: GetAccessTokenOptions, ): Promise { const { provider, ...payload } = options; const { data } = await this.workos.post< @@ -155,7 +227,7 @@ export class Pipes { } /** - * List providers + * List providers for a user * * Retrieves a list of available providers and the user's connection status for each. Returns all providers configured for your environment, along with the user's [connected account](https://workos.com/docs/reference/pipes/connected-account) information where applicable. * @param options - Additional query options. diff --git a/src/pipes/serializers/connected-account.serializer.ts b/src/pipes/serializers/connected-account.serializer.ts index 0844158d5..b8a7d66cb 100644 --- a/src/pipes/serializers/connected-account.serializer.ts +++ b/src/pipes/serializers/connected-account.serializer.ts @@ -13,6 +13,8 @@ export const deserializeConnectedAccount = ( userId: response.user_id ?? null, organizationId: response.organization_id ?? null, scopes: response.scopes, + authMethod: response.auth_method, + apiKeyLast4: response.api_key_last_4 ?? null, state: response.state, createdAt: response.created_at, updatedAt: response.updated_at, diff --git a/src/pipes/serializers/data-integration-credentials-response-credential.serializer.ts b/src/pipes/serializers/data-integration-credentials-response-credential.serializer.ts new file mode 100644 index 000000000..c24651c76 --- /dev/null +++ b/src/pipes/serializers/data-integration-credentials-response-credential.serializer.ts @@ -0,0 +1,17 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + DataIntegrationCredentialsResponseCredential, + DataIntegrationCredentialsResponseCredentialResponse, +} from '../interfaces/data-integration-credentials-response-credential.interface'; + +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, +}); diff --git a/src/pipes/serializers/data-integration-credentials-response.serializer.ts b/src/pipes/serializers/data-integration-credentials-response.serializer.ts new file mode 100644 index 000000000..baccf1a73 --- /dev/null +++ b/src/pipes/serializers/data-integration-credentials-response.serializer.ts @@ -0,0 +1,20 @@ +// 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'; + +export const deserializeDataIntegrationCredentialsResponse = ( + response: DataIntegrationCredentialsResponseWire, +): DataIntegrationCredentialsResponse => ({ + active: response.active, + credential: + response.credential != null + ? deserializeDataIntegrationCredentialsResponseCredential( + response.credential, + ) + : undefined, + error: response.error, +}); 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..e0b9d5612 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 @@ -9,5 +9,5 @@ export const serializeDataIntegrationsGetUserTokenRequest = ( model: DataIntegrationsGetUserTokenRequest, ): DataIntegrationsGetUserTokenRequestResponse => ({ user_id: model.userId, - organization_id: model.organizationId, + organization_id: model.organizationId ?? null, }); 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 ba6a6316d..94b71ffd8 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 @@ -13,6 +13,8 @@ export const deserializeDataIntegrationsListResponseDataConnectedAccount = ( userId: response.user_id ?? null, organizationId: response.organization_id ?? null, scopes: response.scopes, + authMethod: response.auth_method, + apiKeyLast4: response.api_key_last_4 ?? null, 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 10e30dacf..b5999b675 100644 --- a/src/pipes/serializers/data-integrations-list-response-data.serializer.ts +++ b/src/pipes/serializers/data-integrations-list-response-data.serializer.ts @@ -17,6 +17,7 @@ export const deserializeDataIntegrationsListResponseData = ( integrationType: response.integration_type, credentialsType: response.credentials_type, scopes: response.scopes ?? null, + authMethods: response.auth_methods, ownership: response.ownership, createdAt: response.created_at, updatedAt: response.updated_at, 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 new file mode 100644 index 000000000..20ce8dfba --- /dev/null +++ b/src/pipes/serializers/data-integrations-upsert-api-key-request.serializer.ts @@ -0,0 +1,14 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + DataIntegrationsUpsertApiKeyRequest, + DataIntegrationsUpsertApiKeyRequestResponse, +} from '../interfaces/data-integrations-upsert-api-key-request.interface'; + +export const serializeDataIntegrationsUpsertApiKeyRequest = ( + model: DataIntegrationsUpsertApiKeyRequest, +): DataIntegrationsUpsertApiKeyRequestResponse => ({ + user_id: model.userId, + organization_id: model.organizationId, + secret: model.secret, +}); diff --git a/src/pipes/serializers/data-integrations-vend-credentials-request.serializer.ts b/src/pipes/serializers/data-integrations-vend-credentials-request.serializer.ts new file mode 100644 index 000000000..1454753b8 --- /dev/null +++ b/src/pipes/serializers/data-integrations-vend-credentials-request.serializer.ts @@ -0,0 +1,13 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + DataIntegrationsVendCredentialsRequest, + DataIntegrationsVendCredentialsRequestResponse, +} from '../interfaces/data-integrations-vend-credentials-request.interface'; + +export const serializeDataIntegrationsVendCredentialsRequest = ( + model: DataIntegrationsVendCredentialsRequest, +): DataIntegrationsVendCredentialsRequestResponse => ({ + user_id: model.userId, + organization_id: model.organizationId, +}); diff --git a/src/pipes/serializers/index.ts b/src/pipes/serializers/index.ts index 40baf6493..d14594c28 100644 --- a/src/pipes/serializers/index.ts +++ b/src/pipes/serializers/index.ts @@ -1,11 +1,14 @@ // This file is auto-generated by oagen. Do not edit. export * from './connected-account.serializer'; -export * from './data-integration-access-token-response.serializer'; export * from './data-integration-access-token-response-access-token.serializer'; export * from './data-integration-authorize-url-response.serializer'; +export * from './data-integration-credentials-response.serializer'; +export * from './data-integration-credentials-response-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'; 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-vend-credentials-request.serializer';