Skip to content

Commit 060d051

Browse files
committed
Add enterprise connection test run resource
1 parent e69797a commit 060d051

7 files changed

Lines changed: 402 additions & 3 deletions

File tree

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
import type {
2+
ClerkResourceReloadParams,
3+
EnterpriseConnectionTestRunJSON,
4+
EnterpriseConnectionTestRunJSONSnapshot,
5+
EnterpriseConnectionTestRunLogResource,
6+
EnterpriseConnectionTestRunOauthPayloadJSON,
7+
EnterpriseConnectionTestRunOauthPayloadResource,
8+
EnterpriseConnectionTestRunParsedUserInfoJSON,
9+
EnterpriseConnectionTestRunParsedUserInfoResource,
10+
EnterpriseConnectionTestRunResource,
11+
EnterpriseConnectionTestRunSamlPayloadJSON,
12+
EnterpriseConnectionTestRunSamlPayloadResource,
13+
} from '@clerk/shared/types';
14+
15+
import { unixEpochToDate } from '../../utils/date';
16+
import { clerkUnsupportedReloadMethod } from '../errors';
17+
import { BaseResource } from './Base';
18+
19+
export class EnterpriseConnectionTestRun extends BaseResource implements EnterpriseConnectionTestRunResource {
20+
pathRoot = '/me';
21+
private enterpriseConnectionId = '';
22+
23+
id!: string;
24+
status!: string;
25+
connectionType!: 'saml' | 'oauth';
26+
parsedUserInfo: EnterpriseConnectionTestRunParsedUserInfoResource | null = null;
27+
logs: EnterpriseConnectionTestRunLogResource[] = [];
28+
saml: EnterpriseConnectionTestRunSamlPayloadResource | null = null;
29+
oauth: EnterpriseConnectionTestRunOauthPayloadResource | null = null;
30+
createdAt: Date | null = null;
31+
32+
constructor(
33+
data: EnterpriseConnectionTestRunJSON | EnterpriseConnectionTestRunJSONSnapshot,
34+
enterpriseConnectionId: string,
35+
) {
36+
super();
37+
this.enterpriseConnectionId = enterpriseConnectionId;
38+
this.fromJSON(data);
39+
}
40+
41+
protected path(): string {
42+
return `${this.pathRoot}/enterprise_connections/${this.enterpriseConnectionId}/test_runs/${this.id}`;
43+
}
44+
45+
reload(_?: ClerkResourceReloadParams): Promise<this> {
46+
clerkUnsupportedReloadMethod('EnterpriseConnectionTestRun');
47+
}
48+
49+
protected fromJSON(data: EnterpriseConnectionTestRunJSON | EnterpriseConnectionTestRunJSONSnapshot | null): this {
50+
if (!data) {
51+
return this;
52+
}
53+
54+
this.id = data.id;
55+
this.status = data.status;
56+
this.connectionType = data.connection_type;
57+
this.parsedUserInfo = parsedUserInfoFromJSON(data.parsed_user_info ?? null);
58+
this.logs = (data.logs ?? []).map(log => ({
59+
level: log.level,
60+
code: log.code,
61+
shortMessage: log.short_message,
62+
message: log.message,
63+
}));
64+
this.saml = samlPayloadFromJSON(data.saml ?? null);
65+
this.oauth = oauthPayloadFromJSON(data.oauth ?? null);
66+
this.createdAt = unixEpochToDate(data.created_at);
67+
68+
return this;
69+
}
70+
71+
public __internal_toSnapshot(): EnterpriseConnectionTestRunJSONSnapshot {
72+
return {
73+
object: 'enterprise_connection_test_run',
74+
id: this.id,
75+
status: this.status,
76+
connection_type: this.connectionType,
77+
parsed_user_info: parsedUserInfoToJSON(this.parsedUserInfo),
78+
logs: this.logs.map(log => ({
79+
level: log.level,
80+
code: log.code,
81+
short_message: log.shortMessage,
82+
message: log.message,
83+
})),
84+
saml: samlPayloadToJSON(this.saml),
85+
oauth: oauthPayloadToJSON(this.oauth),
86+
created_at: this.createdAt?.getTime() ?? 0,
87+
};
88+
}
89+
}
90+
91+
function parsedUserInfoFromJSON(
92+
data: EnterpriseConnectionTestRunParsedUserInfoJSON | null | undefined,
93+
): EnterpriseConnectionTestRunParsedUserInfoResource | null {
94+
if (!data) {
95+
return null;
96+
}
97+
98+
return {
99+
emailAddress: data.email_address,
100+
firstName: data.first_name,
101+
lastName: data.last_name,
102+
userId: data.user_id,
103+
};
104+
}
105+
106+
function parsedUserInfoToJSON(
107+
data: EnterpriseConnectionTestRunParsedUserInfoResource | null,
108+
): EnterpriseConnectionTestRunParsedUserInfoJSON | undefined {
109+
if (!data) {
110+
return undefined;
111+
}
112+
113+
return {
114+
email_address: data.emailAddress,
115+
first_name: data.firstName,
116+
last_name: data.lastName,
117+
user_id: data.userId,
118+
};
119+
}
120+
121+
function samlPayloadFromJSON(
122+
data: EnterpriseConnectionTestRunSamlPayloadJSON | null | undefined,
123+
): EnterpriseConnectionTestRunSamlPayloadResource | null {
124+
if (!data) {
125+
return null;
126+
}
127+
128+
return {
129+
samlRequest: data.saml_request,
130+
samlResponse: data.saml_response,
131+
relayState: data.relay_state,
132+
};
133+
}
134+
135+
function samlPayloadToJSON(
136+
data: EnterpriseConnectionTestRunSamlPayloadResource | null,
137+
): EnterpriseConnectionTestRunSamlPayloadJSON | undefined {
138+
if (!data) {
139+
return undefined;
140+
}
141+
return {
142+
saml_request: data.samlRequest,
143+
saml_response: data.samlResponse,
144+
relay_state: data.relayState,
145+
};
146+
}
147+
148+
function oauthPayloadFromJSON(
149+
data: EnterpriseConnectionTestRunOauthPayloadJSON | null | undefined,
150+
): EnterpriseConnectionTestRunOauthPayloadResource | null {
151+
if (!data) {
152+
return null;
153+
}
154+
return {
155+
idToken: data.id_token,
156+
accessToken: data.access_token,
157+
userInfo: data.user_info,
158+
};
159+
}
160+
161+
function oauthPayloadToJSON(
162+
data: EnterpriseConnectionTestRunOauthPayloadResource | null,
163+
): EnterpriseConnectionTestRunOauthPayloadJSON | undefined {
164+
if (!data) {
165+
return undefined;
166+
}
167+
return {
168+
id_token: data.idToken,
169+
access_token: data.accessToken,
170+
user_info: data.userInfo,
171+
};
172+
}

packages/clerk-js/src/core/resources/User.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,14 @@ import type {
1414
EnterpriseConnectionResource,
1515
ExternalAccountJSON,
1616
ExternalAccountResource,
17+
ClerkPaginatedResponse,
1718
CreateMeEnterpriseConnectionParams,
19+
EnterpriseConnectionTestRunInitJSON,
20+
EnterpriseConnectionTestRunJSON,
21+
EnterpriseConnectionTestRunsPaginatedJSON,
22+
EnterpriseConnectionTestRunInitResource,
23+
EnterpriseConnectionTestRunResource,
24+
GetEnterpriseConnectionTestRunsParams,
1825
GetEnterpriseConnectionsParams,
1926
UpdateMeEnterpriseConnectionParams,
2027
GetOrganizationMemberships,
@@ -38,6 +45,7 @@ import type {
3845
} from '@clerk/shared/types';
3946

4047
import { unixEpochToDate } from '../../utils/date';
48+
import { convertPageToOffsetSearchParams } from '../../utils/convertPageToOffsetSearchParams';
4149
import {
4250
buildCreateMeEnterpriseConnectionBody,
4351
buildUpdateMeEnterpriseConnectionBody,
@@ -52,6 +60,7 @@ import {
5260
EmailAddress,
5361
EnterpriseAccount,
5462
EnterpriseConnection,
63+
EnterpriseConnectionTestRun,
5564
ExternalAccount,
5665
Image,
5766
OrganizationMembership,
@@ -362,6 +371,47 @@ export class User extends BaseResource implements UserResource {
362371
return new DeletedObject(json);
363372
};
364373

374+
createEnterpriseConnectionTestRun = async (
375+
enterpriseConnectionId: string,
376+
): Promise<EnterpriseConnectionTestRunInitResource> => {
377+
const json = (
378+
await BaseResource._fetch<EnterpriseConnectionTestRunInitJSON>({
379+
path: `${this.path()}/enterprise_connections/${enterpriseConnectionId}/test_runs`,
380+
method: 'POST',
381+
})
382+
)?.response as unknown as EnterpriseConnectionTestRunInitJSON;
383+
384+
return { url: json.url };
385+
};
386+
387+
getEnterpriseConnectionTestRuns = async (
388+
enterpriseConnectionId: string,
389+
params?: GetEnterpriseConnectionTestRunsParams,
390+
): Promise<ClerkPaginatedResponse<EnterpriseConnectionTestRunResource>> => {
391+
const { status, ...rest } = params || {};
392+
const search = convertPageToOffsetSearchParams({ ...rest, paginated: true });
393+
if (status?.length) {
394+
for (const s of status) {
395+
search.append('status', s);
396+
}
397+
}
398+
399+
const res = await BaseResource._fetch({
400+
path: `${this.path()}/enterprise_connections/${enterpriseConnectionId}/test_runs`,
401+
method: 'GET',
402+
search,
403+
});
404+
405+
const payload = res?.response as unknown as EnterpriseConnectionTestRunsPaginatedJSON | undefined;
406+
407+
return {
408+
total_count: payload?.total_count ?? 0,
409+
data: (payload?.data ?? []).map(
410+
(row: EnterpriseConnectionTestRunJSON) => new EnterpriseConnectionTestRun(row, enterpriseConnectionId),
411+
),
412+
};
413+
};
414+
365415
initializePaymentMethod: typeof initializePaymentMethod = params => {
366416
return initializePaymentMethod(params);
367417
};

packages/clerk-js/src/core/resources/__tests__/User.test.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,72 @@ describe('User', () => {
269269
expect(result.deleted).toBe(true);
270270
});
271271

272+
it('creates an enterprise connection test run', async () => {
273+
// @ts-ignore
274+
BaseResource._fetch = vi.fn().mockReturnValue(Promise.resolve({ response: { url: 'https://example.com/test' } }));
275+
276+
const user = new User({
277+
email_addresses: [],
278+
phone_numbers: [],
279+
web3_wallets: [],
280+
external_accounts: [],
281+
} as unknown as UserJSON);
282+
283+
const init = await user.createEnterpriseConnectionTestRun('ec_123');
284+
285+
// @ts-ignore
286+
expect(BaseResource._fetch).toHaveBeenCalledWith({
287+
method: 'POST',
288+
path: '/me/enterprise_connections/ec_123/test_runs',
289+
});
290+
291+
expect(init.url).toBe('https://example.com/test');
292+
});
293+
294+
it('lists enterprise connection test runs', async () => {
295+
const paginated = {
296+
data: [
297+
{
298+
id: 'run_1',
299+
status: 'success',
300+
connection_type: 'saml' as const,
301+
created_at: 1700000000000,
302+
},
303+
],
304+
total_count: 1,
305+
};
306+
307+
// @ts-ignore
308+
BaseResource._fetch = vi.fn().mockReturnValue(Promise.resolve({ response: paginated }));
309+
310+
const user = new User({
311+
email_addresses: [],
312+
phone_numbers: [],
313+
web3_wallets: [],
314+
external_accounts: [],
315+
} as unknown as UserJSON);
316+
317+
const result = await user.getEnterpriseConnectionTestRuns('ec_123', {
318+
initialPage: 1,
319+
pageSize: 10,
320+
status: ['pending', 'success'],
321+
});
322+
323+
// @ts-ignore
324+
const call = BaseResource._fetch.mock.calls[0][0];
325+
expect(call.method).toBe('GET');
326+
expect(call.path).toBe('/me/enterprise_connections/ec_123/test_runs');
327+
expect(call.search.get('limit')).toBe('10');
328+
expect(call.search.get('offset')).toBe('0');
329+
expect(call.search.get('paginated')).toBe('true');
330+
expect(call.search.getAll('status')).toEqual(['pending', 'success']);
331+
332+
expect(result.total_count).toBe(1);
333+
expect(result.data).toHaveLength(1);
334+
expect(result.data[0].id).toBe('run_1');
335+
expect(result.data[0].connectionType).toBe('saml');
336+
});
337+
272338
it('creates a web3 wallet', async () => {
273339
const targetWeb3Wallet = '0x0000000000000000000000000000000000000000';
274340
const web3WalletJSON = {

packages/clerk-js/src/core/resources/internal.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export * from './DisplayConfig';
1717
export * from './EmailAddress';
1818
export * from './EnterpriseAccount';
1919
export * from './EnterpriseConnection';
20+
export * from './EnterpriseConnectionTestRun';
2021
export * from './Environment';
2122
export * from './ExternalAccount';
2223
export * from './Feature';

0 commit comments

Comments
 (0)