Skip to content

Commit b5a4467

Browse files
committed
fix(events): handle domain verification failed
The domain verfication failed events have a slightly different schema to other organization domain events. This was causing the data to be parsed incorrectly in the list events api resulting in empty `data` properties. I've added the required interfaces, serializers, and deserializers to capture the difference and parse the events successfully. I've also added some unit tests for the serialization.
1 parent 6ff6197 commit b5a4467

5 files changed

Lines changed: 192 additions & 31 deletions

File tree

src/common/interfaces/event.interface.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ import {
7474
VaultDekDecryptedEventData,
7575
VaultDekDecryptedEventResponseData,
7676
} from '../../vault/interfaces/vault-event.interface';
77+
import {
78+
OrganizationDomainVerificationFailed,
79+
OrganizationDomainVerificationFailedResponse
80+
} from "../../organization-domains/interfaces/organization-domain-verification-failed.interface";
7781

7882
export interface EventBase {
7983
id: string;
@@ -654,12 +658,12 @@ export interface OrganizationDomainVerifiedEventResponse extends EventResponseBa
654658

655659
export interface OrganizationDomainVerificationFailedEvent extends EventBase {
656660
event: 'organization_domain.verification_failed';
657-
data: OrganizationDomain;
661+
data: OrganizationDomainVerificationFailed;
658662
}
659663

660664
export interface OrganizationDomainVerificationFailedEventResponse extends EventResponseBase {
661665
event: 'organization_domain.verification_failed';
662-
data: OrganizationDomainResponse;
666+
data: OrganizationDomainVerificationFailedResponse;
663667
}
664668

665669
export interface OrganizationDomainCreatedEvent extends EventBase {

src/common/serializers/event.serializer.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ import {
3737
deserializeVaultDekReadEvent,
3838
deserializeVaultDekDecryptedEvent,
3939
} from '../../vault/serializers/vault-event.serializer';
40+
import {
41+
deserializeOrganizationDomainVerificationFailed
42+
} from "../../organization-domains/serializers/organization-domain-verification-failed.serializer";
4043

4144
export const deserializeEvent = (event: EventResponse): Event => {
4245
const eventBase: EventBase = {
@@ -209,8 +212,13 @@ export const deserializeEvent = (event: EventResponse): Event => {
209212
event: event.event,
210213
data: deserializeOrganization(event.data),
211214
};
212-
case 'organization_domain.verified':
213215
case 'organization_domain.verification_failed':
216+
return {
217+
...eventBase,
218+
event: event.event,
219+
data: deserializeOrganizationDomainVerificationFailed(event.data),
220+
};
221+
case 'organization_domain.verified':
214222
case 'organization_domain.created':
215223
case 'organization_domain.updated':
216224
case 'organization_domain.deleted':

src/events/events.spec.ts

Lines changed: 147 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,39 @@
11
import fetch from 'jest-fetch-mock';
2-
import { fetchOnce, fetchSearchParams } from '../common/utils/test-utils';
2+
import {fetchOnce, fetchSearchParams} from '../common/utils/test-utils';
33
import {
4-
DsyncUserUpdatedEvent,
5-
DsyncUserUpdatedEventResponse,
6-
Event,
7-
EventResponse,
8-
FlagCreatedEvent,
9-
FlagCreatedEventResponse,
10-
ListResponse,
11-
VaultDataCreatedEvent,
12-
VaultDataCreatedEventResponse,
13-
VaultDataUpdatedEvent,
14-
VaultDataUpdatedEventResponse,
15-
VaultDataReadEvent,
16-
VaultDataReadEventResponse,
17-
VaultDataDeletedEvent,
18-
VaultDataDeletedEventResponse,
19-
VaultNamesListedEvent,
20-
VaultNamesListedEventResponse,
21-
VaultMetadataReadEvent,
22-
VaultMetadataReadEventResponse,
23-
VaultKekCreatedEvent,
24-
VaultKekCreatedEventResponse,
25-
VaultDekReadEvent,
26-
VaultDekReadEventResponse,
27-
VaultDekDecryptedEvent,
28-
VaultDekDecryptedEventResponse,
4+
DsyncUserUpdatedEvent,
5+
DsyncUserUpdatedEventResponse,
6+
Event,
7+
EventResponse,
8+
FlagCreatedEvent,
9+
FlagCreatedEventResponse,
10+
ListResponse,
11+
OrganizationDomainCreatedEvent,
12+
OrganizationDomainCreatedEventResponse,
13+
OrganizationDomainVerificationFailedEvent,
14+
OrganizationDomainVerificationFailedEventResponse,
15+
VaultDataCreatedEvent,
16+
VaultDataCreatedEventResponse,
17+
VaultDataDeletedEvent,
18+
VaultDataDeletedEventResponse,
19+
VaultDataReadEvent,
20+
VaultDataReadEventResponse,
21+
VaultDataUpdatedEvent,
22+
VaultDataUpdatedEventResponse,
23+
VaultDekDecryptedEvent,
24+
VaultDekDecryptedEventResponse,
25+
VaultDekReadEvent,
26+
VaultDekReadEventResponse,
27+
VaultKekCreatedEvent,
28+
VaultKekCreatedEventResponse,
29+
VaultMetadataReadEvent,
30+
VaultMetadataReadEventResponse,
31+
VaultNamesListedEvent,
32+
VaultNamesListedEventResponse,
2933
} from '../common/interfaces';
30-
import { WorkOS } from '../workos';
31-
import { ConnectionType } from '../sso/interfaces';
34+
import {WorkOS} from '../workos';
35+
import {ConnectionType} from '../sso/interfaces';
36+
import {OrganizationDomainState, OrganizationDomainVerificationStrategy} from "../organization-domains/interfaces";
3237

3338
describe('Event', () => {
3439
beforeEach(() => fetch.resetMocks());
@@ -693,5 +698,119 @@ describe('Event', () => {
693698
});
694699
});
695700
});
701+
702+
describe('organization domain events', () => {
703+
it('deserializes organization_domain.created events', async () => {
704+
const response: OrganizationDomainCreatedEventResponse = {
705+
"event": "organization_domain.created",
706+
"id": "event_01DOMAINCREATED001",
707+
"data": {
708+
"id": "org_domain_01TESTDOMAIN",
709+
"state": OrganizationDomainState.Pending,
710+
"domain": "example.com",
711+
"object": "organization_domain",
712+
"created_at": "2026-04-06T06:24:06.749Z",
713+
"updated_at": "2026-04-06T06:24:06.749Z",
714+
"organization_id": "org_01TESTORGANIZATION",
715+
"verification_strategy": OrganizationDomainVerificationStrategy.Manual,
716+
},
717+
"context": {},
718+
"created_at": "2026-04-06T06:24:06.776Z"
719+
};
720+
721+
const expected: OrganizationDomainCreatedEvent = {
722+
"event": "organization_domain.created",
723+
"id": "event_01DOMAINCREATED001",
724+
"data": {
725+
"id": "org_domain_01TESTDOMAIN",
726+
"state": OrganizationDomainState.Pending,
727+
"domain": "example.com",
728+
"object": "organization_domain",
729+
"createdAt": "2026-04-06T06:24:06.749Z",
730+
"updatedAt": "2026-04-06T06:24:06.749Z",
731+
"organizationId": "org_01TESTORGANIZATION",
732+
"verificationStrategy": OrganizationDomainVerificationStrategy.Manual,
733+
},
734+
"context": {},
735+
"createdAt": "2026-04-06T06:24:06.776Z"
736+
};
737+
738+
fetchOnce({
739+
object: 'list',
740+
data: [response],
741+
list_metadata: {},
742+
});
743+
744+
const list = await workos.events.listEvents({
745+
events: ['organization_domain.created'],
746+
});
747+
748+
expect(list).toEqual({
749+
object: 'list',
750+
data: [expected],
751+
listMetadata: {},
752+
});
753+
});
754+
755+
it('deserializes organization_domain.verification_failed events', async () => {
756+
const response: OrganizationDomainVerificationFailedEventResponse = {
757+
"event": "organization_domain.verification_failed",
758+
"id": "event_01DOMAIN0002",
759+
"data": {
760+
"reason": "domain_verification_period_expired",
761+
"organization_domain": {
762+
"id": "org_domain_0TESTDOMAIN",
763+
"state": OrganizationDomainState.Failed,
764+
"domain": "example.com",
765+
"object": "organization_domain",
766+
"created_at": "2026-03-07T02:24:56.621Z",
767+
"updated_at": "2026-04-06T02:25:00.494Z",
768+
"organization_id": "org_01TESTORGANIZATION",
769+
"verification_token": "FAKETOKEN",
770+
"verification_strategy": OrganizationDomainVerificationStrategy.Dns
771+
}
772+
},
773+
"context": {},
774+
"created_at": "2026-04-06T02:26:05.430Z"
775+
};
776+
777+
const expected: OrganizationDomainVerificationFailedEvent = {
778+
"event": "organization_domain.verification_failed",
779+
"id": "event_01DOMAIN0002",
780+
"data": {
781+
"reason": "domain_verification_period_expired",
782+
"organizationDomain": {
783+
"id": "org_domain_0TESTDOMAIN",
784+
"state": OrganizationDomainState.Failed,
785+
"domain": "example.com",
786+
"object": "organization_domain",
787+
"createdAt": "2026-03-07T02:24:56.621Z",
788+
"updatedAt": "2026-04-06T02:25:00.494Z",
789+
"organizationId": "org_01TESTORGANIZATION",
790+
"verificationToken": "FAKETOKEN",
791+
"verificationStrategy": OrganizationDomainVerificationStrategy.Dns
792+
}
793+
},
794+
"context": {},
795+
"createdAt": "2026-04-06T02:26:05.430Z"
796+
};
797+
798+
fetchOnce({
799+
object: 'list',
800+
data: [response],
801+
list_metadata: {},
802+
});
803+
804+
const list = await workos.events.listEvents({
805+
events: ['organization_domain.verification_failed'],
806+
});
807+
808+
expect(list).toEqual({
809+
object: 'list',
810+
data: [expected],
811+
listMetadata: {},
812+
});
813+
});
814+
});
696815
});
697816
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import {OrganizationDomain, OrganizationDomainResponse} from "./organization-domain.interface";
2+
3+
export interface OrganizationDomainVerificationFailed {
4+
reason: string;
5+
organizationDomain: OrganizationDomain;
6+
}
7+
8+
export interface OrganizationDomainVerificationFailedResponse {
9+
reason: string;
10+
organization_domain: OrganizationDomainResponse;
11+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import {
2+
OrganizationDomainVerificationFailed,
3+
OrganizationDomainVerificationFailedResponse
4+
} from "../interfaces/organization-domain-verification-failed.interface";
5+
import {deserializeOrganizationDomain, serializeOrganizationDomain} from "./organization-domain.serializer";
6+
7+
export const deserializeOrganizationDomainVerificationFailed = (
8+
organizationDomainVerificationFailed: OrganizationDomainVerificationFailedResponse,
9+
): OrganizationDomainVerificationFailed => ({
10+
reason: organizationDomainVerificationFailed.reason,
11+
organizationDomain: deserializeOrganizationDomain(organizationDomainVerificationFailed.organization_domain),
12+
});
13+
14+
export const serializeOrganizationDomainVerificationFailed = (
15+
organizationDomainVerificationFailed: OrganizationDomainVerificationFailed,
16+
): OrganizationDomainVerificationFailedResponse => ({
17+
reason: organizationDomainVerificationFailed.reason,
18+
organization_domain: serializeOrganizationDomain(organizationDomainVerificationFailed.organizationDomain)
19+
});

0 commit comments

Comments
 (0)