Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions src/common/interfaces/event.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import {
FeatureFlagResponse,
} from '../../feature-flags/interfaces';
import { Group, GroupResponse } from '../../groups/interfaces';
import { ConnectedAccountState } from '../../pipes/interfaces';
import {
VaultDataCreatedEventData,
VaultDataCreatedEventResponseData,
Expand Down Expand Up @@ -817,6 +818,98 @@ export interface GroupMemberRemovedEventResponse extends EventResponseBase {
data: GroupMemberEventResponseData;
}

export interface PipesConnectedAccount {
object: 'connected_account';
id: string;
dataIntegrationId: string;
providerSlug: string;
userId: string | null;
organizationId: string | null;
scopes: string[];
state: ConnectedAccountState;
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Outdated
createdAt: string;
updatedAt: string;
}

export interface PipesConnectedAccountResponse {
object: 'connected_account';
id: string;
data_integration_id: string;
provider_slug: string;
user_id: string | null;
organization_id: string | null;
scopes: string[];
state: ConnectedAccountState;
created_at: string;
updated_at: string;
}

export interface PipesConnectionFailed {
object: 'connection_failed';
dataIntegrationId: string;
providerSlug: string;
userId: string | null;
organizationId: string | null;
errorCode: string;
errorReason: string | null;
providerError: string | null;
providerErrorDescription: string | null;
createdAt: string;
}

export interface PipesConnectionFailedResponse {
object: 'connection_failed';
data_integration_id: string;
provider_slug: string;
user_id: string | null;
organization_id: string | null;
error_code: string;
error_reason: string | null;
provider_error: string | null;
provider_error_description: string | null;
created_at: string;
}

export interface PipesConnectedAccountConnectedEvent extends EventBase {
event: 'pipes.connected_account.connected';
data: PipesConnectedAccount;
}

export interface PipesConnectedAccountConnectedEventResponse extends EventResponseBase {
event: 'pipes.connected_account.connected';
data: PipesConnectedAccountResponse;
}

export interface PipesConnectedAccountConnectionFailedEvent extends EventBase {
event: 'pipes.connected_account.connection_failed';
data: PipesConnectionFailed;
}

export interface PipesConnectedAccountConnectionFailedEventResponse extends EventResponseBase {
event: 'pipes.connected_account.connection_failed';
data: PipesConnectionFailedResponse;
}

export interface PipesConnectedAccountDisconnectedEvent extends EventBase {
event: 'pipes.connected_account.disconnected';
data: PipesConnectedAccount;
}

export interface PipesConnectedAccountDisconnectedEventResponse extends EventResponseBase {
event: 'pipes.connected_account.disconnected';
data: PipesConnectedAccountResponse;
}

export interface PipesConnectedAccountReauthorizationNeededEvent extends EventBase {
event: 'pipes.connected_account.reauthorization_needed';
data: PipesConnectedAccount;
}

export interface PipesConnectedAccountReauthorizationNeededEventResponse extends EventResponseBase {
event: 'pipes.connected_account.reauthorization_needed';
data: PipesConnectedAccountResponse;
}

export interface VaultDataCreatedEvent extends EventBase {
event: 'vault.data.created';
data: VaultDataCreatedEventData;
Expand Down Expand Up @@ -993,6 +1086,10 @@ export type Event =
| GroupDeletedEvent
| GroupMemberAddedEvent
| GroupMemberRemovedEvent
| PipesConnectedAccountConnectedEvent
| PipesConnectedAccountConnectionFailedEvent
| PipesConnectedAccountDisconnectedEvent
| PipesConnectedAccountReauthorizationNeededEvent
| VaultDataCreatedEvent
| VaultDataUpdatedEvent
| VaultDataReadEvent
Expand Down Expand Up @@ -1075,6 +1172,10 @@ export type EventResponse =
| GroupDeletedEventResponse
| GroupMemberAddedEventResponse
| GroupMemberRemovedEventResponse
| PipesConnectedAccountConnectedEventResponse
| PipesConnectedAccountConnectionFailedEventResponse
| PipesConnectedAccountDisconnectedEventResponse
| PipesConnectedAccountReauthorizationNeededEventResponse
| VaultDataCreatedEventResponse
| VaultDataUpdatedEventResponse
| VaultDataReadEventResponse
Expand Down
54 changes: 53 additions & 1 deletion src/common/serializers/event.serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,15 @@ import { deserializeOrganizationDomain } from '../../organization-domains/serial
import { deserializeOrganizationMembership } from '../../user-management/serializers/organization-membership.serializer';
import { deserializeRoleEvent } from '../../user-management/serializers/role.serializer';
import { deserializeSession } from '../../user-management/serializers/session.serializer';
import { Event, EventBase, EventResponse } from '../interfaces';
import {
Event,
EventBase,
EventResponse,
PipesConnectedAccount,
PipesConnectedAccountResponse,
PipesConnectionFailed,
PipesConnectionFailedResponse,
} from '../interfaces';
import { deserializeAuthenticationRadarRiskDetectedEvent } from '../../user-management/serializers/authentication-radar-risk-event-serializer';
import { deserializeApiKey } from '../../api-keys/serializers/api-key.serializer';
import { deserializeOrganizationRoleEvent } from '../../authorization/serializers/organization-role.serializer';
Expand All @@ -41,6 +49,36 @@ import {
} from '../../vault/serializers/vault-event.serializer';
import { deserializeOrganizationDomainVerificationFailed } from '../../organization-domains/serializers/organization-domain-verification-failed.serializer';

const deserializePipesConnectedAccount = (
connectedAccount: PipesConnectedAccountResponse,
): PipesConnectedAccount => ({
object: connectedAccount.object,
id: connectedAccount.id,
dataIntegrationId: connectedAccount.data_integration_id,
providerSlug: connectedAccount.provider_slug,
userId: connectedAccount.user_id,
organizationId: connectedAccount.organization_id,
scopes: connectedAccount.scopes,
state: connectedAccount.state,
createdAt: connectedAccount.created_at,
updatedAt: connectedAccount.updated_at,
});

const deserializePipesConnectionFailed = (
connectionFailed: PipesConnectionFailedResponse,
): PipesConnectionFailed => ({
object: connectionFailed.object,
dataIntegrationId: connectionFailed.data_integration_id,
providerSlug: connectionFailed.provider_slug,
userId: connectionFailed.user_id,
organizationId: connectionFailed.organization_id,
errorCode: connectionFailed.error_code,
errorReason: connectionFailed.error_reason,
providerError: connectionFailed.provider_error,
providerErrorDescription: connectionFailed.provider_error_description,
createdAt: connectionFailed.created_at,
});

export const deserializeEvent = (event: EventResponse): Event => {
const eventBase: EventBase = {
id: event.id,
Expand Down Expand Up @@ -261,6 +299,20 @@ export const deserializeEvent = (event: EventResponse): Event => {
organizationMembershipId: event.data.organization_membership_id,
},
};
case 'pipes.connected_account.connected':
case 'pipes.connected_account.disconnected':
case 'pipes.connected_account.reauthorization_needed':
return {
...eventBase,
event: event.event,
data: deserializePipesConnectedAccount(event.data),
};
case 'pipes.connected_account.connection_failed':
return {
...eventBase,
event: event.event,
data: deserializePipesConnectionFailed(event.data),
};
case 'vault.data.created':
return {
...eventBase,
Expand Down
104 changes: 104 additions & 0 deletions src/events/events.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ import {
OrganizationDomainCreatedEventResponse,
OrganizationDomainVerificationFailedEvent,
OrganizationDomainVerificationFailedEventResponse,
PipesConnectedAccountConnectedEvent,
PipesConnectedAccountConnectedEventResponse,
PipesConnectedAccountConnectionFailedEvent,
PipesConnectedAccountConnectionFailedEventResponse,
VaultDataCreatedEvent,
VaultDataCreatedEventResponse,
VaultDataUpdatedEvent,
Expand Down Expand Up @@ -169,6 +173,106 @@ describe('Event', () => {
});
});

describe('Pipes connected account events', () => {
it('accepts event filters and deserializes payloads', async () => {
const connectedResponse: PipesConnectedAccountConnectedEventResponse = {
id: 'event_connected',
created_at: '2026-08-25T12:00:00.000Z',
context: { client_id: 'client_123' },
event: 'pipes.connected_account.connected',
data: {
object: 'connected_account',
id: 'data_installation_123',
data_integration_id: 'data_integration_123',
provider_slug: 'github',
user_id: 'user_123',
organization_id: null,
scopes: ['repo', 'user:email'],
state: 'connected',
created_at: '2026-08-25T11:00:00.000Z',
updated_at: '2026-08-25T12:00:00.000Z',
},
};
const connectionFailedResponse: PipesConnectedAccountConnectionFailedEventResponse =
{
id: 'event_connection_failed',
created_at: '2026-08-25T13:00:00.000Z',
event: 'pipes.connected_account.connection_failed',
data: {
object: 'connection_failed',
data_integration_id: 'data_integration_123',
provider_slug: 'github',
user_id: 'user_123',
organization_id: null,
error_code: 'authorization_code_exchange_error',
error_reason: 'The authorization code has expired.',
provider_error: 'access_denied',
provider_error_description:
'The user denied the authorization request.',
created_at: '2026-08-25T13:00:00.000Z',
},
};
const connected: PipesConnectedAccountConnectedEvent = {
id: 'event_connected',
createdAt: '2026-08-25T12:00:00.000Z',
context: { client_id: 'client_123' },
event: 'pipes.connected_account.connected',
data: {
object: 'connected_account',
id: 'data_installation_123',
dataIntegrationId: 'data_integration_123',
providerSlug: 'github',
userId: 'user_123',
organizationId: null,
scopes: ['repo', 'user:email'],
state: 'connected',
createdAt: '2026-08-25T11:00:00.000Z',
updatedAt: '2026-08-25T12:00:00.000Z',
},
};
const connectionFailed: PipesConnectedAccountConnectionFailedEvent = {
id: 'event_connection_failed',
createdAt: '2026-08-25T13:00:00.000Z',
context: undefined,
event: 'pipes.connected_account.connection_failed',
data: {
object: 'connection_failed',
dataIntegrationId: 'data_integration_123',
providerSlug: 'github',
userId: 'user_123',
organizationId: null,
errorCode: 'authorization_code_exchange_error',
errorReason: 'The authorization code has expired.',
providerError: 'access_denied',
providerErrorDescription:
'The user denied the authorization request.',
createdAt: '2026-08-25T13:00:00.000Z',
},
};

fetchOnce({
object: 'list',
data: [connectedResponse, connectionFailedResponse],
list_metadata: {},
});

const list = await workos.events.listEvents({
events: [
'pipes.connected_account.connected',
'pipes.connected_account.connection_failed',
'pipes.connected_account.disconnected',
'pipes.connected_account.reauthorization_needed',
],
});

expect(list).toEqual({
object: 'list',
data: [connected, connectionFailed],
listMetadata: {},
});
});
});

describe('feature flag events', () => {
it('deserializes flag.created events', async () => {
const flagCreatedResponse: FlagCreatedEventResponse = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ export const CreateWebhookEndpointEvents = {
PermissionDeleted: 'permission.deleted',
PermissionUpdated: 'permission.updated',
PipesConnectedAccountConnected: 'pipes.connected_account.connected',
PipesConnectedAccountConnectionFailed:
'pipes.connected_account.connection_failed',
PipesConnectedAccountDisconnected: 'pipes.connected_account.disconnected',
PipesConnectedAccountReauthorizationNeeded:
'pipes.connected_account.reauthorization_needed',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ export const UpdateWebhookEndpointEvents = {
PermissionDeleted: 'permission.deleted',
PermissionUpdated: 'permission.updated',
PipesConnectedAccountConnected: 'pipes.connected_account.connected',
PipesConnectedAccountConnectionFailed:
'pipes.connected_account.connection_failed',
PipesConnectedAccountDisconnected: 'pipes.connected_account.disconnected',
PipesConnectedAccountReauthorizationNeeded:
'pipes.connected_account.reauthorization_needed',
Expand Down