diff --git a/.changelog-pending/2026-09-25T17-40-23-0b0182d195a272be7528ca26c0ca7f936c8f789f.md b/.changelog-pending/2026-09-25T17-40-23-0b0182d195a272be7528ca26c0ca7f936c8f789f.md new file mode 100644 index 00000000..d257df61 --- /dev/null +++ b/.changelog-pending/2026-09-25T17-40-23-0b0182d195a272be7528ca26c0ca7f936c8f789f.md @@ -0,0 +1,54 @@ +* [#450](https://github.com/workos/workos-php/pull/450) feat(generated): regenerate from spec (1 change) + + **⚠️ Breaking** + * **[pipes](https://workos.com/docs/reference/pipes)**: + * Removed model `AddDataIntegrationsUpsertApiKeyRequest` + * Removed model `AddDataIntegrationsUpsertClientCredentialsRequest` + * Removed enum `AddDataIntegrationsUpsertApiKeyRequestConnectionOwner` + * Removed enum `AddDataIntegrationsUpsertClientCredentialsRequestConnectionOwner` + + **Features** + * **[pipes](https://workos.com/docs/reference/pipes)**: + * Added model `DataIntegrationsCreateApiKeyConnectionRequest` + * Added model `DataIntegrationsCreateClientCredentialsConnectionRequest` + * Added enum `DataIntegrationsCreateApiKeyConnectionRequestConnectionOwner` + * Added enum `DataIntegrationsCreateClientCredentialsConnectionRequestConnectionOwner` + * Added endpoint `POST /data-integrations/{slug}/api-key` + * Added endpoint `POST /data-integrations/{slug}/client-credentials` + * Added `config` to `DataIntegrationVendedCredential` + * Added model `DataIntegrationVendedCredential` + * Added model `CreateOrganizationConnectedAccount` + * Added model `CreateConnectedAccount` + * Added model `AddDataIntegrationsUpsertApiKeyRequest` + * Added model `ReauthorizeDataIntegrationsUpsertApiKeyRequest` + * Added model `DataIntegrationsUpsertApiKeyRequest2` + * Added model `AddDataIntegrationsUpsertClientCredentialsRequest` + * Added model `ReauthorizeDataIntegrationsUpsertClientCredentialsRequest` + * Added model `DataIntegrationsUpsertClientCredentialsRequest2` + * Added enum `CreateOrganizationConnectedAccountState` + * Added enum `CreateConnectedAccountState` + * Added enum `AddDataIntegrationsUpsertApiKeyRequestConnectionOwner` + * Added enum `ReauthorizeDataIntegrationsUpsertApiKeyRequestConnectionOwner` + * Added enum `DataIntegrationsUpsertApiKeyRequest2ConnectionOwner` + * Added enum `AddDataIntegrationsUpsertClientCredentialsRequestConnectionOwner` + * Added enum `ReauthorizeDataIntegrationsUpsertClientCredentialsRequestConnectionOwner` + * Added enum `DataIntegrationsUpsertClientCredentialsRequest2ConnectionOwner` + * Added parameter `OrganizationsDataProviders.updateOrganizationDataInstallation.connection_intent` + * Added parameter `UserManagementDataProviders.updateUserDataInstallation.connection_intent` + * Added model `OrganizationConnectedAccount` + * Added enum `OrganizationConnectedAccountState` + + **Fixes** + * **[pipes](https://workos.com/docs/reference/pipes)**: + * Changed request body for `Pipes.upsertApiKey` + * Changed request body for `Pipes.upsertClientCredentials` + * Changed errors for endpoint `POST /organizations/{organization_id}/connected_accounts/{slug}` + * Changed errors for endpoint `POST /user_management/users/{user_id}/connected_accounts/{slug}` + * Changed the type of `DataIntegrationCredentialsResponse.credential` + * Removed `connected_account_id` from `DataIntegrationsUpsertApiKeyRequest` + * Removed `connected_account_id` from `DataIntegrationsUpsertClientCredentialsRequest` + * Changed request body of `OrganizationsDataProviders.createOrganizationDataInstallation` from `OrganizationConnectedAccount` to `CreateOrganizationConnectedAccount` + * Changed request body of `UserManagementDataProviders.createUserDataInstallation` from `ConnectedAccountInput` to `CreateConnectedAccount` + * Changed request body of `OrganizationsDataProviders.createOrganizationDataInstallation` from `ConnectedAccountInput` to `OrganizationConnectedAccount` + * Changed request body of `OrganizationsDataProviders.updateOrganizationDataInstallation` from `ConnectedAccountInput` to `OrganizationConnectedAccount` + * Changed errors for endpoint `PUT /organizations/{organization_id}/connected_accounts/{slug}` diff --git a/.last-synced-sha b/.last-synced-sha index 89fbe35a..9b1ca4f2 100644 --- a/.last-synced-sha +++ b/.last-synced-sha @@ -1 +1 @@ -6f037208ce812063908f97744b4aaf4fdf570351 +e8f39fdd452f5ebca8bcdad9b8436f2bfb1d3ed7 diff --git a/.oagen-manifest.json b/.oagen-manifest.json index 07a40923..c159e14e 100644 --- a/.oagen-manifest.json +++ b/.oagen-manifest.json @@ -238,6 +238,7 @@ "lib/Resource/CreateAuthorizationPermission.php", "lib/Resource/CreateAuthorizationResource.php", "lib/Resource/CreateCORSOrigin.php", + "lib/Resource/CreateConnectedAccount.php", "lib/Resource/CreateConnection.php", "lib/Resource/CreateConnectionAttributeMaps.php", "lib/Resource/CreateConnectionKeyPair.php", @@ -256,6 +257,7 @@ "lib/Resource/CreateOAuthApplication.php", "lib/Resource/CreateObjectRequest.php", "lib/Resource/CreateOrganizationApiKey.php", + "lib/Resource/CreateOrganizationConnectedAccount.php", "lib/Resource/CreateOrganizationDomain.php", "lib/Resource/CreateOrganizationRole.php", "lib/Resource/CreatePasswordReset.php", @@ -294,6 +296,9 @@ "lib/Resource/DataIntegrationCustomProvider.php", "lib/Resource/DataIntegrationInstallation.php", "lib/Resource/DataIntegrationState.php", + "lib/Resource/DataIntegrationVendedCredential.php", + "lib/Resource/DataIntegrationsCreateApiKeyConnectionRequest.php", + "lib/Resource/DataIntegrationsCreateClientCredentialsConnectionRequest.php", "lib/Resource/DataIntegrationsGetDataIntegrationAuthorizeUrlRequest.php", "lib/Resource/DataIntegrationsGetUserTokenRequest.php", "lib/Resource/DataIntegrationsListResponse.php", @@ -301,7 +306,9 @@ "lib/Resource/DataIntegrationsListResponseDataConnectedAccount.php", "lib/Resource/DataIntegrationsListResponseDataOwnership.php", "lib/Resource/DataIntegrationsUpsertApiKeyRequest.php", + "lib/Resource/DataIntegrationsUpsertApiKeyRequest2.php", "lib/Resource/DataIntegrationsUpsertClientCredentialsRequest.php", + "lib/Resource/DataIntegrationsUpsertClientCredentialsRequest2.php", "lib/Resource/DataIntegrationsVendCredentialsRequest.php", "lib/Resource/DecryptRequest.php", "lib/Resource/DecryptResponse.php", @@ -447,6 +454,7 @@ "lib/Resource/OrganizationApiKeyWithValue.php", "lib/Resource/OrganizationApiKeyWithValueOwner.php", "lib/Resource/OrganizationAuthorizedConnectApplicationListData.php", + "lib/Resource/OrganizationConnectedAccount.php", "lib/Resource/OrganizationCreated.php", "lib/Resource/OrganizationCreatedData.php", "lib/Resource/OrganizationCreatedDataDomain.php", @@ -544,6 +552,8 @@ "lib/Resource/RadarStandaloneResponseVerdict.php", "lib/Resource/RadarStandaloneUpdateRadarAttemptRequest.php", "lib/Resource/RadarStandaloneUpdateRadarListRequest.php", + "lib/Resource/ReauthorizeDataIntegrationsUpsertApiKeyRequest.php", + "lib/Resource/ReauthorizeDataIntegrationsUpsertClientCredentialsRequest.php", "lib/Resource/RedirectUri.php", "lib/Resource/RedirectUriInput.php", "lib/Resource/RefreshTokenSessionAuthenticateRequest.php", @@ -944,6 +954,7 @@ "tests/Fixtures/create_authkit_oauth_resource.json", "tests/Fixtures/create_authorization_permission.json", "tests/Fixtures/create_authorization_resource.json", + "tests/Fixtures/create_connected_account.json", "tests/Fixtures/create_connection.json", "tests/Fixtures/create_connection_attribute_maps.json", "tests/Fixtures/create_connection_key_pair.json", @@ -963,6 +974,7 @@ "tests/Fixtures/create_oauth_application.json", "tests/Fixtures/create_object_request.json", "tests/Fixtures/create_organization_api_key.json", + "tests/Fixtures/create_organization_connected_account.json", "tests/Fixtures/create_organization_domain.json", "tests/Fixtures/create_organization_role.json", "tests/Fixtures/create_password_reset.json", @@ -991,13 +1003,18 @@ "tests/Fixtures/data_integration_credentials_response_credential.json", "tests/Fixtures/data_integration_custom_provider.json", "tests/Fixtures/data_integration_installation.json", + "tests/Fixtures/data_integration_vended_credential.json", + "tests/Fixtures/data_integrations_create_api_key_connection_request.json", + "tests/Fixtures/data_integrations_create_client_credentials_connection_request.json", "tests/Fixtures/data_integrations_get_data_integration_authorize_url_request.json", "tests/Fixtures/data_integrations_get_user_token_request.json", "tests/Fixtures/data_integrations_list_response.json", "tests/Fixtures/data_integrations_list_response_data.json", "tests/Fixtures/data_integrations_list_response_data_connected_account.json", "tests/Fixtures/data_integrations_upsert_api_key_request.json", + "tests/Fixtures/data_integrations_upsert_api_key_request_2.json", "tests/Fixtures/data_integrations_upsert_client_credentials_request.json", + "tests/Fixtures/data_integrations_upsert_client_credentials_request_2.json", "tests/Fixtures/data_integrations_vend_credentials_request.json", "tests/Fixtures/decrypt_request.json", "tests/Fixtures/decrypt_response.json", @@ -1167,6 +1184,7 @@ "tests/Fixtures/organization_api_key_with_value.json", "tests/Fixtures/organization_api_key_with_value_owner.json", "tests/Fixtures/organization_authorized_connect_application_list_data.json", + "tests/Fixtures/organization_connected_account.json", "tests/Fixtures/organization_created.json", "tests/Fixtures/organization_created_data.json", "tests/Fixtures/organization_created_data_domain.json", @@ -1248,6 +1266,8 @@ "tests/Fixtures/radar_standalone_response.json", "tests/Fixtures/radar_standalone_update_radar_attempt_request.json", "tests/Fixtures/radar_standalone_update_radar_list_request.json", + "tests/Fixtures/reauthorize_data_integrations_upsert_api_key_request.json", + "tests/Fixtures/reauthorize_data_integrations_upsert_client_credentials_request.json", "tests/Fixtures/redirect_uri.json", "tests/Fixtures/redirect_uri_input.json", "tests/Fixtures/refresh_token_session_authenticate_request.json", @@ -2447,6 +2467,14 @@ "GET /audit_logs/exports/{auditLogExportId}": { "sdkMethod": "getExport", "service": "auditLogs" + }, + "POST /data-integrations/{slug}/api-key": { + "sdkMethod": "createDataIntegrationApiKey", + "service": "pipes" + }, + "POST /data-integrations/{slug}/client-credentials": { + "sdkMethod": "createDataIntegrationClientCredential", + "service": "pipes" } } } diff --git a/lib/Resource/CreateConnectedAccount.php b/lib/Resource/CreateConnectedAccount.php new file mode 100644 index 00000000..4626e863 --- /dev/null +++ b/lib/Resource/CreateConnectedAccount.php @@ -0,0 +1,55 @@ +|null + */ + public ?array $scopes = null, + /** Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. */ + public ?PipeConnectedAccountState $state = null, + /** 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`. */ + public ?string $connectionIntent = null, + ) { + } + + public static function fromArray(array $data): self + { + return new self( + accessToken: $data['access_token'] ?? null, + refreshToken: $data['refresh_token'] ?? null, + expiresAt: isset($data['expires_at']) ? new \DateTimeImmutable($data['expires_at']) : null, + scopes: $data['scopes'] ?? null, + state: isset($data['state']) ? PipeConnectedAccountState::from($data['state']) : null, + connectionIntent: $data['connection_intent'] ?? null, + ); + } + + public function toArray(): array + { + return [ + 'access_token' => $this->accessToken, + 'refresh_token' => $this->refreshToken, + 'expires_at' => $this->expiresAt?->format(\DateTimeInterface::RFC3339_EXTENDED), + 'scopes' => $this->scopes, + 'state' => $this->state?->value, + 'connection_intent' => $this->connectionIntent, + ]; + } +} diff --git a/lib/Resource/CreateOrganizationConnectedAccount.php b/lib/Resource/CreateOrganizationConnectedAccount.php new file mode 100644 index 00000000..fe270ae1 --- /dev/null +++ b/lib/Resource/CreateOrganizationConnectedAccount.php @@ -0,0 +1,59 @@ +|null + */ + public ?array $scopes = null, + /** Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. */ + public ?PipeConnectedAccountState $state = null, + /** 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`. */ + public ?string $connectionIntent = null, + ) { + } + + public static function fromArray(array $data): self + { + return new self( + userId: $data['user_id'], + accessToken: $data['access_token'] ?? null, + refreshToken: $data['refresh_token'] ?? null, + expiresAt: isset($data['expires_at']) ? new \DateTimeImmutable($data['expires_at']) : null, + scopes: $data['scopes'] ?? null, + state: isset($data['state']) ? PipeConnectedAccountState::from($data['state']) : null, + connectionIntent: $data['connection_intent'] ?? null, + ); + } + + public function toArray(): array + { + return [ + 'user_id' => $this->userId, + 'access_token' => $this->accessToken, + 'refresh_token' => $this->refreshToken, + 'expires_at' => $this->expiresAt?->format(\DateTimeInterface::RFC3339_EXTENDED), + 'scopes' => $this->scopes, + 'state' => $this->state?->value, + 'connection_intent' => $this->connectionIntent, + ]; + } +} diff --git a/lib/Resource/DataIntegrationCredentialsResponse.php b/lib/Resource/DataIntegrationCredentialsResponse.php index 8deff39a..8e61e936 100644 --- a/lib/Resource/DataIntegrationCredentialsResponse.php +++ b/lib/Resource/DataIntegrationCredentialsResponse.php @@ -14,7 +14,7 @@ public function __construct( /** Indicates credentials are available. */ public ?bool $active = null, /** The credential object containing the vended secret. */ - public ?DataIntegrationCredentialsResponseCredential $credential = null, + public ?DataIntegrationVendedCredential $credential = null, /** * 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. @@ -28,7 +28,7 @@ public static function fromArray(array $data): self { return new self( active: $data['active'] ?? null, - credential: isset($data['credential']) ? DataIntegrationCredentialsResponseCredential::fromArray($data['credential']) : null, + credential: isset($data['credential']) ? DataIntegrationVendedCredential::fromArray($data['credential']) : null, error: isset($data['error']) ? DataIntegrationAccessTokenResponseError::from($data['error']) : null, ); } diff --git a/lib/Resource/DataIntegrationVendedCredential.php b/lib/Resource/DataIntegrationVendedCredential.php new file mode 100644 index 00000000..c6283bc5 --- /dev/null +++ b/lib/Resource/DataIntegrationVendedCredential.php @@ -0,0 +1,72 @@ +|null + */ + public ?array $scopes = null, + /** + * If the integration has requested scopes that aren't present on the access token, they're listed here. Present for `oauth` and `client_credentials` credentials; absent for `api_key`. + * @var array|null + */ + public ?array $missingScopes = null, + /** + * Provider-declared, non-secret config from the installation snapshot, with current defaults for unset fields. Includes both integration- and installation-scope fields; omits undeclared fields and fields marked secret. Use these values to address a per-tenant host, such as Snowflake's `account` or Zendesk's `subdomain`. Empty when no values are disclosable. Changes to integration or organization pins require reconnecting or explicitly rebinding the connection. Defaults are live and can differ from a cached token's audience until refresh or re-mint. + * @var array|null + */ + public ?array $config = null, + /** + * Non-sensitive fields captured from the provider token response (e.g. Salesforce `instance_url`), as configured for the provider. Only present for `client_credentials` credentials. + * @var array|null + */ + public ?array $metadata = null, + ) { + } + + public static function fromArray(array $data): self + { + return new self( + object: $data['object'] ?? null, + authMethod: $data['auth_method'] ?? null, + value: $data['value'] ?? null, + expiresAt: $data['expires_at'] ?? null, + scopes: $data['scopes'] ?? null, + missingScopes: $data['missing_scopes'] ?? null, + config: $data['config'] ?? null, + metadata: $data['metadata'] ?? null, + ); + } + + public function toArray(): array + { + return [ + 'object' => $this->object, + 'auth_method' => $this->authMethod, + 'value' => $this->value, + 'expires_at' => $this->expiresAt, + 'scopes' => $this->scopes, + 'missing_scopes' => $this->missingScopes, + 'config' => $this->config, + 'metadata' => $this->metadata, + ]; + } +} diff --git a/lib/Resource/DataIntegrationsCreateApiKeyConnectionRequest.php b/lib/Resource/DataIntegrationsCreateApiKeyConnectionRequest.php new file mode 100644 index 00000000..b7a36398 --- /dev/null +++ b/lib/Resource/DataIntegrationsCreateApiKeyConnectionRequest.php @@ -0,0 +1,48 @@ + $this->userId, + 'secret' => $this->secret, + 'connection_intent' => $this->connectionIntent, + 'organization_id' => $this->organizationId, + 'connection_owner' => $this->connectionOwner?->value, + ]; + } +} diff --git a/lib/Resource/DataIntegrationsCreateClientCredentialsConnectionRequest.php b/lib/Resource/DataIntegrationsCreateClientCredentialsConnectionRequest.php new file mode 100644 index 00000000..b20284f3 --- /dev/null +++ b/lib/Resource/DataIntegrationsCreateClientCredentialsConnectionRequest.php @@ -0,0 +1,59 @@ +|null + */ + public ?array $config = null, + ) { + } + + public static function fromArray(array $data): self + { + return new self( + userId: $data['user_id'], + clientId: $data['client_id'], + clientSecret: $data['client_secret'], + connectionIntent: $data['connection_intent'] ?? 'add', + organizationId: $data['organization_id'] ?? null, + connectionOwner: isset($data['connection_owner']) ? PipesOwnership::from($data['connection_owner']) : null, + config: $data['config'] ?? null, + ); + } + + public function toArray(): array + { + return [ + 'user_id' => $this->userId, + 'client_id' => $this->clientId, + 'client_secret' => $this->clientSecret, + 'connection_intent' => $this->connectionIntent, + 'organization_id' => $this->organizationId, + 'connection_owner' => $this->connectionOwner?->value, + 'config' => $this->config, + ]; + } +} diff --git a/lib/Resource/DataIntegrationsUpsertApiKeyRequest.php b/lib/Resource/DataIntegrationsUpsertApiKeyRequest.php index 376bee75..26953307 100644 --- a/lib/Resource/DataIntegrationsUpsertApiKeyRequest.php +++ b/lib/Resource/DataIntegrationsUpsertApiKeyRequest.php @@ -17,8 +17,6 @@ public function __construct( public string $secret, /** 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`. */ public ?string $organizationId = null, - /** A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to rotate a specific existing connection. */ - public ?string $connectedAccountId = null, /** 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. */ public ?PipesOwnership $connectionOwner = null, ) { @@ -30,7 +28,6 @@ public static function fromArray(array $data): self userId: $data['user_id'], secret: $data['secret'], organizationId: $data['organization_id'] ?? null, - connectedAccountId: $data['connected_account_id'] ?? null, connectionOwner: isset($data['connection_owner']) ? PipesOwnership::from($data['connection_owner']) : null, ); } @@ -41,7 +38,6 @@ public function toArray(): array 'user_id' => $this->userId, 'secret' => $this->secret, 'organization_id' => $this->organizationId, - 'connected_account_id' => $this->connectedAccountId, 'connection_owner' => $this->connectionOwner?->value, ]; } diff --git a/lib/Resource/DataIntegrationsUpsertApiKeyRequest2.php b/lib/Resource/DataIntegrationsUpsertApiKeyRequest2.php new file mode 100644 index 00000000..6db51e0e --- /dev/null +++ b/lib/Resource/DataIntegrationsUpsertApiKeyRequest2.php @@ -0,0 +1,48 @@ + $this->userId, + 'secret' => $this->secret, + 'connected_account_id' => $this->connectedAccountId, + 'organization_id' => $this->organizationId, + 'connection_owner' => $this->connectionOwner?->value, + ]; + } +} diff --git a/lib/Resource/DataIntegrationsUpsertClientCredentialsRequest.php b/lib/Resource/DataIntegrationsUpsertClientCredentialsRequest.php index 222c492c..55c26df4 100644 --- a/lib/Resource/DataIntegrationsUpsertClientCredentialsRequest.php +++ b/lib/Resource/DataIntegrationsUpsertClientCredentialsRequest.php @@ -19,8 +19,6 @@ public function __construct( public string $clientSecret, /** 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`. */ public ?string $organizationId = null, - /** A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to rotate a specific existing connection. */ - public ?string $connectedAccountId = null, /** 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. */ public ?PipesOwnership $connectionOwner = null, /** @@ -38,7 +36,6 @@ public static function fromArray(array $data): self clientId: $data['client_id'], clientSecret: $data['client_secret'], organizationId: $data['organization_id'] ?? null, - connectedAccountId: $data['connected_account_id'] ?? null, connectionOwner: isset($data['connection_owner']) ? PipesOwnership::from($data['connection_owner']) : null, config: $data['config'] ?? null, ); @@ -51,7 +48,6 @@ public function toArray(): array 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'organization_id' => $this->organizationId, - 'connected_account_id' => $this->connectedAccountId, 'connection_owner' => $this->connectionOwner?->value, 'config' => $this->config, ]; diff --git a/lib/Resource/DataIntegrationsUpsertClientCredentialsRequest2.php b/lib/Resource/DataIntegrationsUpsertClientCredentialsRequest2.php new file mode 100644 index 00000000..f7e621f1 --- /dev/null +++ b/lib/Resource/DataIntegrationsUpsertClientCredentialsRequest2.php @@ -0,0 +1,59 @@ +|null + */ + public ?array $config = null, + ) { + } + + public static function fromArray(array $data): self + { + return new self( + userId: $data['user_id'], + clientId: $data['client_id'], + clientSecret: $data['client_secret'], + connectedAccountId: $data['connected_account_id'], + organizationId: $data['organization_id'] ?? null, + connectionOwner: isset($data['connection_owner']) ? PipesOwnership::from($data['connection_owner']) : null, + config: $data['config'] ?? null, + ); + } + + public function toArray(): array + { + return [ + 'user_id' => $this->userId, + 'client_id' => $this->clientId, + 'client_secret' => $this->clientSecret, + 'connected_account_id' => $this->connectedAccountId, + 'organization_id' => $this->organizationId, + 'connection_owner' => $this->connectionOwner?->value, + 'config' => $this->config, + ]; + } +} diff --git a/lib/Resource/OrganizationConnectedAccount.php b/lib/Resource/OrganizationConnectedAccount.php new file mode 100644 index 00000000..d9fdcf32 --- /dev/null +++ b/lib/Resource/OrganizationConnectedAccount.php @@ -0,0 +1,55 @@ +|null + */ + public ?array $scopes = null, + /** Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. */ + public ?PipeConnectedAccountState $state = null, + ) { + } + + public static function fromArray(array $data): self + { + return new self( + userId: $data['user_id'], + accessToken: $data['access_token'] ?? null, + refreshToken: $data['refresh_token'] ?? null, + expiresAt: isset($data['expires_at']) ? new \DateTimeImmutable($data['expires_at']) : null, + scopes: $data['scopes'] ?? null, + state: isset($data['state']) ? PipeConnectedAccountState::from($data['state']) : null, + ); + } + + public function toArray(): array + { + return [ + 'user_id' => $this->userId, + 'access_token' => $this->accessToken, + 'refresh_token' => $this->refreshToken, + 'expires_at' => $this->expiresAt?->format(\DateTimeInterface::RFC3339_EXTENDED), + 'scopes' => $this->scopes, + 'state' => $this->state?->value, + ]; + } +} diff --git a/lib/Resource/ReauthorizeDataIntegrationsUpsertApiKeyRequest.php b/lib/Resource/ReauthorizeDataIntegrationsUpsertApiKeyRequest.php new file mode 100644 index 00000000..1f01d2dc --- /dev/null +++ b/lib/Resource/ReauthorizeDataIntegrationsUpsertApiKeyRequest.php @@ -0,0 +1,52 @@ + $this->userId, + 'secret' => $this->secret, + 'connection_intent' => $this->connectionIntent, + 'connected_account_id' => $this->connectedAccountId, + 'organization_id' => $this->organizationId, + 'connection_owner' => $this->connectionOwner?->value, + ]; + } +} diff --git a/lib/Resource/ReauthorizeDataIntegrationsUpsertClientCredentialsRequest.php b/lib/Resource/ReauthorizeDataIntegrationsUpsertClientCredentialsRequest.php new file mode 100644 index 00000000..ae1aede5 --- /dev/null +++ b/lib/Resource/ReauthorizeDataIntegrationsUpsertClientCredentialsRequest.php @@ -0,0 +1,63 @@ +|null + */ + public ?array $config = null, + ) { + } + + public static function fromArray(array $data): self + { + return new self( + userId: $data['user_id'], + clientId: $data['client_id'], + clientSecret: $data['client_secret'], + connectionIntent: $data['connection_intent'] ?? 'reauthorize', + connectedAccountId: $data['connected_account_id'], + organizationId: $data['organization_id'] ?? null, + connectionOwner: isset($data['connection_owner']) ? PipesOwnership::from($data['connection_owner']) : null, + config: $data['config'] ?? null, + ); + } + + public function toArray(): array + { + return [ + 'user_id' => $this->userId, + 'client_id' => $this->clientId, + 'client_secret' => $this->clientSecret, + 'connection_intent' => $this->connectionIntent, + 'connected_account_id' => $this->connectedAccountId, + 'organization_id' => $this->organizationId, + 'connection_owner' => $this->connectionOwner?->value, + 'config' => $this->config, + ]; + } +} diff --git a/lib/Service/Pipes.php b/lib/Service/Pipes.php index 3065e5f7..58021780 100644 --- a/lib/Service/Pipes.php +++ b/lib/Service/Pipes.php @@ -188,34 +188,57 @@ public function deleteDataIntegration( } /** - * Upsert an API key for a connected account + * Create another API key connected account * - * 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. + * Creates another API key-based connected account for the specified integration, owned by the user or, when `connection_owner` is `organization`, shared by the organization. Requires `connection_intent: add` and does not accept `connected_account_id`; use PUT to create or rotate the compatibility connection or to update an exact connection. Creating an additional connection is not yet available: until it is, this endpoint succeeds only when the owner has no connection for this integration, which creates the compatibility connection, and otherwise returns 404 `multiple_connections_unavailable`. * @param string $slug The identifier of the integration. * @param string $userId A [User](https://workos.com/docs/reference/authkit/user) identifier. * @param string|null $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`. - * @param string|null $connectedAccountId A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to rotate a specific existing connection. * @param \WorkOS\Resource\PipesOwnership|null $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. * @param string $secret The API key secret to store for this integration. + * @param string $connectionIntent 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`. * @return \WorkOS\Resource\ConnectedAccount * @throws \WorkOS\Exception\WorkOSException */ - public function updateDataIntegrationApiKey( + public function createDataIntegrationApiKey( string $slug, string $userId, string $secret, + string $connectionIntent, ?string $organizationId = null, - ?string $connectedAccountId = null, ?\WorkOS\Resource\PipesOwnership $connectionOwner = null, ?\WorkOS\RequestOptions $options = null, ): \WorkOS\Resource\ConnectedAccount { $body = array_filter([ 'user_id' => $userId, 'organization_id' => $organizationId, - 'connected_account_id' => $connectedAccountId, 'connection_owner' => $connectionOwner?->value, 'secret' => $secret, + 'connection_intent' => $connectionIntent, ], fn ($v) => $v !== null); + $response = $this->client->request( + method: 'POST', + path: 'data-integrations/' . rawurlencode($slug) . '/api-key', + body: $body, + options: $options, + ); + return ConnectedAccount::fromArray($response); + } + + /** + * Upsert an API key for a connected account + * + * 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. To create another connection, use POST. + * @param string $slug The identifier of the integration. + * @return \WorkOS\Resource\ConnectedAccount + * @throws \WorkOS\Exception\WorkOSException + */ + public function updateDataIntegrationApiKey( + string $slug, + ?\WorkOS\RequestOptions $options = null, + ): \WorkOS\Resource\ConnectedAccount { + $body = [ + ]; $response = $this->client->request( method: 'PUT', path: 'data-integrations/' . rawurlencode($slug) . '/api-key', @@ -264,27 +287,27 @@ public function authorizeDataIntegration( } /** - * Upsert client credentials for a connected account + * Create another client credentials 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. + * Creates another client credentials-based connected account for the specified integration, owned by the user or, when `connection_owner` is `organization`, shared by the organization. Requires `connection_intent: add` and does not accept `connected_account_id`; use PUT to create or rotate the compatibility connection or to update an exact connection. Creating an additional connection is not yet available: until it is, this endpoint succeeds only when the owner has no connection for this integration, which creates the compatibility connection, and otherwise returns 404 `multiple_connections_unavailable`. * @param string $slug The identifier of the integration. * @param string $userId A [User](https://workos.com/docs/reference/authkit/user) identifier. * @param string|null $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`. - * @param string|null $connectedAccountId A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to rotate a specific existing connection. * @param \WorkOS\Resource\PipesOwnership|null $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. * @param string $clientId The OAuth client ID to store for this integration. * @param string $clientSecret The OAuth client secret to store for this integration. * @param array|null $config Provider-specific configuration values collected for this installation, keyed by the provider's config field descriptors. + * @param string $connectionIntent 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`. * @return \WorkOS\Resource\ConnectedAccount * @throws \WorkOS\Exception\WorkOSException */ - public function updateDataIntegrationClientCredentials( + public function createDataIntegrationClientCredential( string $slug, string $userId, string $clientId, string $clientSecret, + string $connectionIntent, ?string $organizationId = null, - ?string $connectedAccountId = null, ?\WorkOS\Resource\PipesOwnership $connectionOwner = null, ?array $config = null, ?\WorkOS\RequestOptions $options = null, @@ -292,12 +315,35 @@ public function updateDataIntegrationClientCredentials( $body = array_filter([ 'user_id' => $userId, 'organization_id' => $organizationId, - 'connected_account_id' => $connectedAccountId, 'connection_owner' => $connectionOwner?->value, 'client_id' => $clientId, 'client_secret' => $clientSecret, 'config' => $config, + 'connection_intent' => $connectionIntent, ], fn ($v) => $v !== null); + $response = $this->client->request( + method: 'POST', + path: 'data-integrations/' . rawurlencode($slug) . '/client-credentials', + body: $body, + options: $options, + ); + return ConnectedAccount::fromArray($response); + } + + /** + * 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. To create another connection, use POST. + * @param string $slug The identifier of the integration. + * @return \WorkOS\Resource\ConnectedAccount + * @throws \WorkOS\Exception\WorkOSException + */ + public function updateDataIntegrationClientCredentials( + string $slug, + ?\WorkOS\RequestOptions $options = null, + ): \WorkOS\Resource\ConnectedAccount { + $body = [ + ]; $response = $this->client->request( method: 'PUT', path: 'data-integrations/' . rawurlencode($slug) . '/client-credentials', @@ -310,7 +356,7 @@ public function updateDataIntegrationClientCredentials( /** * 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'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. Every active credential includes `config`: provider-declared, non-secret values from the installation snapshot, with current provider defaults for unset fields. Editing integration or organization configuration does not change the snapshot; reconnect or explicitly rebind the connection to adopt those edits. Defaults remain live, so a changed default can appear in `config` before a cached token is refreshed or re-minted. Credentials that never refresh require a reconnect or rebind when a default changes their routing. * @param string $slug The identifier of the integration. * @param string $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. * @param string|null $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`. @@ -497,7 +543,7 @@ public function getOrganizationConnectedAccount( /** * 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 `connection_intent` to create only the compatibility connection, or set it to `add` to explicitly create another connection. This creation-only endpoint does not accept `connected_account_id` or reauthorization intent. * @param string $organizationId An [Organization](https://workos.com/docs/reference/organization) identifier. * @param string $slug The slug identifier of the provider (e.g., `github`, `slack`, `notion`). * @param string|null $accessToken The OAuth access token for the connected account. @@ -505,17 +551,21 @@ public function getOrganizationConnectedAccount( * @param \DateTimeImmutable|null $expiresAt The ISO-8601 timestamp when the access token expires. Required when `access_token` is provided for tokens that expire. * @param array|null $scopes The OAuth scopes granted for this connection. * @param \WorkOS\Resource\PipeConnectedAccountState|null $state Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. + * @param string $userId 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. + * @param string|null $connectionIntent 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`. * @return \WorkOS\Resource\ConnectedAccount * @throws \WorkOS\Exception\WorkOSException */ public function createOrganizationConnectedAccount( string $organizationId, string $slug, + string $userId, ?string $accessToken = null, ?string $refreshToken = null, ?\DateTimeImmutable $expiresAt = null, ?array $scopes = null, ?\WorkOS\Resource\PipeConnectedAccountState $state = null, + ?string $connectionIntent = null, ?\WorkOS\RequestOptions $options = null, ): \WorkOS\Resource\ConnectedAccount { $body = array_filter([ @@ -524,6 +574,8 @@ public function createOrganizationConnectedAccount( 'expires_at' => $expiresAt?->format(\DateTimeInterface::RFC3339_EXTENDED), 'scopes' => $scopes, 'state' => $state?->value, + 'user_id' => $userId, + 'connection_intent' => $connectionIntent, ], fn ($v) => $v !== null); $response = $this->client->request( method: 'POST', @@ -545,14 +597,17 @@ public function createOrganizationConnectedAccount( * @param \DateTimeImmutable|null $expiresAt The ISO-8601 timestamp when the access token expires. Required when `access_token` is provided for tokens that expire. * @param array|null $scopes The OAuth scopes granted for this connection. * @param \WorkOS\Resource\PipeConnectedAccountState|null $state Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. - * @param bool|null $supportsMultipleConnections Set to `true` to use the plural connection contract. When omitted or `false`, only the compatibility connection is considered. + * @param string $userId 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. + * @param bool|null $supportsMultipleConnections Accepted for compatibility; does not change update targeting. Omit intent and selector to update the compatibility connection, or supply `connected_account_id` to update an exact connection. * @param string|null $connectedAccountId A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to select the connection to update. + * @param string|null $connectionIntent Set to `reauthorize` with `connected_account_id` to update one exact connection. The intent may be omitted when supplying an ID. Omit both for permanent compatibility behavior. * @return \WorkOS\Resource\ConnectedAccount * @throws \WorkOS\Exception\WorkOSException */ public function updateOrganizationConnectedAccount( string $organizationId, string $slug, + string $userId, ?string $accessToken = null, ?string $refreshToken = null, ?\DateTimeImmutable $expiresAt = null, @@ -560,6 +615,7 @@ public function updateOrganizationConnectedAccount( ?\WorkOS\Resource\PipeConnectedAccountState $state = null, ?bool $supportsMultipleConnections = null, ?string $connectedAccountId = null, + ?string $connectionIntent = null, ?\WorkOS\RequestOptions $options = null, ): \WorkOS\Resource\ConnectedAccount { $body = array_filter([ @@ -568,6 +624,7 @@ public function updateOrganizationConnectedAccount( 'expires_at' => $expiresAt?->format(\DateTimeInterface::RFC3339_EXTENDED), 'scopes' => $scopes, 'state' => $state?->value, + 'user_id' => $userId, ], fn ($v) => $v !== null); $response = $this->client->request( method: 'PUT', @@ -679,6 +736,7 @@ public function getUserConnectedAccount( * @param \DateTimeImmutable|null $expiresAt The ISO-8601 timestamp when the access token expires. Required when `access_token` is provided for tokens that expire. * @param array|null $scopes The OAuth scopes granted for this connection. * @param \WorkOS\Resource\PipeConnectedAccountState|null $state Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. + * @param string|null $connectionIntent 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`. * @param string|null $organizationId An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter if the connection is scoped to an organization. * @return \WorkOS\Resource\ConnectedAccount * @throws \WorkOS\Exception\WorkOSException @@ -691,6 +749,7 @@ public function createUserConnectedAccount( ?\DateTimeImmutable $expiresAt = null, ?array $scopes = null, ?\WorkOS\Resource\PipeConnectedAccountState $state = null, + ?string $connectionIntent = null, ?string $organizationId = null, ?\WorkOS\RequestOptions $options = null, ): \WorkOS\Resource\ConnectedAccount { @@ -700,6 +759,7 @@ public function createUserConnectedAccount( 'expires_at' => $expiresAt?->format(\DateTimeInterface::RFC3339_EXTENDED), 'scopes' => $scopes, 'state' => $state?->value, + 'connection_intent' => $connectionIntent, ], fn ($v) => $v !== null); $response = $this->client->request( method: 'POST', @@ -722,8 +782,9 @@ public function createUserConnectedAccount( * @param array|null $scopes The OAuth scopes granted for this connection. * @param \WorkOS\Resource\PipeConnectedAccountState|null $state Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. * @param string|null $organizationId An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter if the connection is scoped to an organization. - * @param bool|null $supportsMultipleConnections Set to `true` to use the plural connection contract. When omitted or `false`, only the compatibility connection is considered. + * @param bool|null $supportsMultipleConnections Accepted for compatibility; does not change update targeting. Omit intent and selector to update the compatibility connection, or supply `connected_account_id` to update an exact connection. * @param string|null $connectedAccountId A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to select the connection to update. + * @param string|null $connectionIntent Set to `reauthorize` with `connected_account_id` to update one exact connection. The intent may be omitted when supplying an ID. Omit both for permanent compatibility behavior. * @return \WorkOS\Resource\ConnectedAccount * @throws \WorkOS\Exception\WorkOSException */ @@ -738,6 +799,7 @@ public function updateUserConnectedAccount( ?string $organizationId = null, ?bool $supportsMultipleConnections = null, ?string $connectedAccountId = null, + ?string $connectionIntent = null, ?\WorkOS\RequestOptions $options = null, ): \WorkOS\Resource\ConnectedAccount { $body = array_filter([ diff --git a/tests/Fixtures/create_connected_account.json b/tests/Fixtures/create_connected_account.json new file mode 100644 index 00000000..4b3e3862 --- /dev/null +++ b/tests/Fixtures/create_connected_account.json @@ -0,0 +1,11 @@ +{ + "access_token": "gho_16C7e42F292c6912E7710c838347Ae178B4a", + "refresh_token": "ghr_xxxxxxxxxxxxxxxxxxxx", + "expires_at": "2025-12-31T23:59:59.000Z", + "scopes": [ + "repo", + "user:email" + ], + "state": "connected", + "connection_intent": "add" +} diff --git a/tests/Fixtures/create_organization_connected_account.json b/tests/Fixtures/create_organization_connected_account.json new file mode 100644 index 00000000..41732c7a --- /dev/null +++ b/tests/Fixtures/create_organization_connected_account.json @@ -0,0 +1,12 @@ +{ + "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", + "connection_intent": "add" +} diff --git a/tests/Fixtures/data_integration_credentials_response.json b/tests/Fixtures/data_integration_credentials_response.json index 9efedd6d..bbac768e 100644 --- a/tests/Fixtures/data_integration_credentials_response.json +++ b/tests/Fixtures/data_integration_credentials_response.json @@ -9,7 +9,13 @@ "repo", "user:email" ], - "missing_scopes": [] + "missing_scopes": [], + "config": { + "account": "myorg-myaccount" + }, + "metadata": { + "instance_url": "https://acme.my.salesforce.com" + } }, "error": "not_installed" } diff --git a/tests/Fixtures/data_integration_vended_credential.json b/tests/Fixtures/data_integration_vended_credential.json new file mode 100644 index 00000000..c9958827 --- /dev/null +++ b/tests/Fixtures/data_integration_vended_credential.json @@ -0,0 +1,17 @@ +{ + "object": "credential", + "auth_method": "oauth", + "value": "gho_16C7e42F292c6912E7710c838347Ae178B4a", + "expires_at": "2025-12-31T23:59:59.000Z", + "scopes": [ + "repo", + "user:email" + ], + "missing_scopes": [], + "config": { + "account": "myorg-myaccount" + }, + "metadata": { + "instance_url": "https://acme.my.salesforce.com" + } +} diff --git a/tests/Fixtures/data_integrations_create_api_key_connection_request.json b/tests/Fixtures/data_integrations_create_api_key_connection_request.json new file mode 100644 index 00000000..18236684 --- /dev/null +++ b/tests/Fixtures/data_integrations_create_api_key_connection_request.json @@ -0,0 +1,7 @@ +{ + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_owner": "user", + "secret": "sk-1234567890abcdef", + "connection_intent": "add" +} diff --git a/tests/Fixtures/data_integrations_create_client_credentials_connection_request.json b/tests/Fixtures/data_integrations_create_client_credentials_connection_request.json new file mode 100644 index 00000000..dc67f1b1 --- /dev/null +++ b/tests/Fixtures/data_integrations_create_client_credentials_connection_request.json @@ -0,0 +1,11 @@ +{ + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_owner": "user", + "client_id": "3MVG9...", + "client_secret": "shhh-secret", + "config": { + "salesforce_host": "acme.my.salesforce.com" + }, + "connection_intent": "add" +} diff --git a/tests/Fixtures/data_integrations_upsert_api_key_request.json b/tests/Fixtures/data_integrations_upsert_api_key_request.json index 45952523..7e72130b 100644 --- a/tests/Fixtures/data_integrations_upsert_api_key_request.json +++ b/tests/Fixtures/data_integrations_upsert_api_key_request.json @@ -1,7 +1,6 @@ { "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", - "connected_account_id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT", "connection_owner": "user", "secret": "sk-1234567890abcdef" } diff --git a/tests/Fixtures/data_integrations_upsert_api_key_request_2.json b/tests/Fixtures/data_integrations_upsert_api_key_request_2.json new file mode 100644 index 00000000..5f088719 --- /dev/null +++ b/tests/Fixtures/data_integrations_upsert_api_key_request_2.json @@ -0,0 +1,7 @@ +{ + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_owner": "user", + "secret": "sk-1234567890abcdef", + "connected_account_id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" +} diff --git a/tests/Fixtures/data_integrations_upsert_client_credentials_request.json b/tests/Fixtures/data_integrations_upsert_client_credentials_request.json index 9497d49c..ca9f4824 100644 --- a/tests/Fixtures/data_integrations_upsert_client_credentials_request.json +++ b/tests/Fixtures/data_integrations_upsert_client_credentials_request.json @@ -1,7 +1,6 @@ { "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", - "connected_account_id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT", "connection_owner": "user", "client_id": "3MVG9...", "client_secret": "shhh-secret", diff --git a/tests/Fixtures/data_integrations_upsert_client_credentials_request_2.json b/tests/Fixtures/data_integrations_upsert_client_credentials_request_2.json new file mode 100644 index 00000000..6eabca26 --- /dev/null +++ b/tests/Fixtures/data_integrations_upsert_client_credentials_request_2.json @@ -0,0 +1,11 @@ +{ + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_owner": "user", + "client_id": "3MVG9...", + "client_secret": "shhh-secret", + "config": { + "salesforce_host": "acme.my.salesforce.com" + }, + "connected_account_id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" +} diff --git a/tests/Fixtures/organization_connected_account.json b/tests/Fixtures/organization_connected_account.json new file mode 100644 index 00000000..952a4cfa --- /dev/null +++ b/tests/Fixtures/organization_connected_account.json @@ -0,0 +1,11 @@ +{ + "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/tests/Fixtures/reauthorize_data_integrations_upsert_api_key_request.json b/tests/Fixtures/reauthorize_data_integrations_upsert_api_key_request.json new file mode 100644 index 00000000..186f69ab --- /dev/null +++ b/tests/Fixtures/reauthorize_data_integrations_upsert_api_key_request.json @@ -0,0 +1,8 @@ +{ + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_owner": "user", + "secret": "sk-1234567890abcdef", + "connection_intent": "reauthorize", + "connected_account_id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" +} diff --git a/tests/Fixtures/reauthorize_data_integrations_upsert_client_credentials_request.json b/tests/Fixtures/reauthorize_data_integrations_upsert_client_credentials_request.json new file mode 100644 index 00000000..92465540 --- /dev/null +++ b/tests/Fixtures/reauthorize_data_integrations_upsert_client_credentials_request.json @@ -0,0 +1,12 @@ +{ + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_owner": "user", + "client_id": "3MVG9...", + "client_secret": "shhh-secret", + "config": { + "salesforce_host": "acme.my.salesforce.com" + }, + "connection_intent": "reauthorize", + "connected_account_id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" +} diff --git a/tests/Service/PipesTest.php b/tests/Service/PipesTest.php index efbadb1c..1d91f96a 100644 --- a/tests/Service/PipesTest.php +++ b/tests/Service/PipesTest.php @@ -83,21 +83,36 @@ public function testDeleteDataIntegration(): void $this->assertStringEndsWith('data-integrations/test_slug', $request->getUri()->getPath()); } - public function testUpdateDataIntegrationApiKey(): void + public function testCreateDataIntegrationApiKey(): void { $fixture = $this->loadFixture('connected_account'); $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); - $result = $client->pipes()->updateDataIntegrationApiKey('test_slug', userId: 'test_value', secret: 'test_value'); + $result = $client->pipes()->createDataIntegrationApiKey('test_slug', userId: 'test_value', secret: 'test_value', connectionIntent: 'test_value'); $this->assertInstanceOf(\WorkOS\Resource\ConnectedAccount::class, $result); $this->assertSame($fixture['id'], $result->id); $this->assertSame($fixture['created_at'], $result->createdAt); $this->assertIsArray($result->toArray()); $request = $this->getLastRequest(); - $this->assertSame('PUT', $request->getMethod()); + $this->assertSame('POST', $request->getMethod()); $this->assertStringEndsWith('data-integrations/test_slug/api-key', $request->getUri()->getPath()); $body = json_decode((string) $request->getBody(), true); $this->assertSame('test_value', $body['user_id']); $this->assertSame('test_value', $body['secret']); + $this->assertArrayHasKey('connection_intent', $body); + } + + public function testUpdateDataIntegrationApiKey(): void + { + $fixture = $this->loadFixture('connected_account'); + $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); + $result = $client->pipes()->updateDataIntegrationApiKey('test_slug'); + $this->assertInstanceOf(\WorkOS\Resource\ConnectedAccount::class, $result); + $this->assertSame($fixture['id'], $result->id); + $this->assertSame($fixture['created_at'], $result->createdAt); + $this->assertIsArray($result->toArray()); + $request = $this->getLastRequest(); + $this->assertSame('PUT', $request->getMethod()); + $this->assertStringEndsWith('data-integrations/test_slug/api-key', $request->getUri()->getPath()); } public function testAuthorizeDataIntegration(): void @@ -115,22 +130,37 @@ public function testAuthorizeDataIntegration(): void $this->assertSame('test_value', $body['user_id']); } - public function testUpdateDataIntegrationClientCredentials(): void + public function testCreateDataIntegrationClientCredential(): void { $fixture = $this->loadFixture('connected_account'); $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); - $result = $client->pipes()->updateDataIntegrationClientCredentials('test_slug', userId: 'test_value', clientId: 'test_value', clientSecret: 'test_value'); + $result = $client->pipes()->createDataIntegrationClientCredential('test_slug', userId: 'test_value', clientId: 'test_value', clientSecret: 'test_value', connectionIntent: 'test_value'); $this->assertInstanceOf(\WorkOS\Resource\ConnectedAccount::class, $result); $this->assertSame($fixture['id'], $result->id); $this->assertSame($fixture['created_at'], $result->createdAt); $this->assertIsArray($result->toArray()); $request = $this->getLastRequest(); - $this->assertSame('PUT', $request->getMethod()); + $this->assertSame('POST', $request->getMethod()); $this->assertStringEndsWith('data-integrations/test_slug/client-credentials', $request->getUri()->getPath()); $body = json_decode((string) $request->getBody(), true); $this->assertSame('test_value', $body['user_id']); $this->assertSame('test_value', $body['client_id']); $this->assertSame('test_value', $body['client_secret']); + $this->assertArrayHasKey('connection_intent', $body); + } + + public function testUpdateDataIntegrationClientCredentials(): void + { + $fixture = $this->loadFixture('connected_account'); + $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); + $result = $client->pipes()->updateDataIntegrationClientCredentials('test_slug'); + $this->assertInstanceOf(\WorkOS\Resource\ConnectedAccount::class, $result); + $this->assertSame($fixture['id'], $result->id); + $this->assertSame($fixture['created_at'], $result->createdAt); + $this->assertIsArray($result->toArray()); + $request = $this->getLastRequest(); + $this->assertSame('PUT', $request->getMethod()); + $this->assertStringEndsWith('data-integrations/test_slug/client-credentials', $request->getUri()->getPath()); } public function testCreateDataIntegrationCredential(): void @@ -216,7 +246,7 @@ public function testCreateOrganizationConnectedAccount(): void { $fixture = $this->loadFixture('connected_account'); $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); - $result = $client->pipes()->createOrganizationConnectedAccount('test_organization_id', 'test_slug'); + $result = $client->pipes()->createOrganizationConnectedAccount('test_organization_id', 'test_slug', userId: 'test_value'); $this->assertInstanceOf(\WorkOS\Resource\ConnectedAccount::class, $result); $this->assertSame($fixture['id'], $result->id); $this->assertSame($fixture['created_at'], $result->createdAt); @@ -224,13 +254,15 @@ public function testCreateOrganizationConnectedAccount(): void $request = $this->getLastRequest(); $this->assertSame('POST', $request->getMethod()); $this->assertStringEndsWith('organizations/test_organization_id/connected_accounts/test_slug', $request->getUri()->getPath()); + $body = json_decode((string) $request->getBody(), true); + $this->assertSame('test_value', $body['user_id']); } public function testUpdateOrganizationConnectedAccount(): void { $fixture = $this->loadFixture('connected_account'); $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); - $result = $client->pipes()->updateOrganizationConnectedAccount('test_organization_id', 'test_slug'); + $result = $client->pipes()->updateOrganizationConnectedAccount('test_organization_id', 'test_slug', userId: 'test_value'); $this->assertInstanceOf(\WorkOS\Resource\ConnectedAccount::class, $result); $this->assertSame($fixture['id'], $result->id); $this->assertSame($fixture['created_at'], $result->createdAt); @@ -238,6 +270,8 @@ public function testUpdateOrganizationConnectedAccount(): void $request = $this->getLastRequest(); $this->assertSame('PUT', $request->getMethod()); $this->assertStringEndsWith('organizations/test_organization_id/connected_accounts/test_slug', $request->getUri()->getPath()); + $body = json_decode((string) $request->getBody(), true); + $this->assertSame('test_value', $body['user_id']); } public function testDeleteOrganizationConnectedAccount(): void