Skip to content

Commit 24379e2

Browse files
committed
Add enterprise connection test run resource
1 parent 5996211 commit 24379e2

8 files changed

Lines changed: 406 additions & 81 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(data: EnterpriseConnectionTestRunJSON, enterpriseConnectionId: string) {
33+
super();
34+
this.enterpriseConnectionId = enterpriseConnectionId;
35+
this.fromJSON(data);
36+
}
37+
38+
protected path(): string {
39+
return `${this.pathRoot}/enterprise_connections/${this.enterpriseConnectionId}/test_runs/${this.id}`;
40+
}
41+
42+
reload(_?: ClerkResourceReloadParams): Promise<this> {
43+
clerkUnsupportedReloadMethod('EnterpriseConnectionTestRun');
44+
}
45+
46+
protected fromJSON(data: EnterpriseConnectionTestRunJSON | null): this {
47+
if (!data) {
48+
return this;
49+
}
50+
51+
this.id = data.id;
52+
this.status = data.status;
53+
this.connectionType = data.connection_type;
54+
this.parsedUserInfo = parsedUserInfoFromJSON(data.parsed_user_info ?? null);
55+
this.saml = samlPayloadFromJSON(data.saml ?? null);
56+
this.oauth = oauthPayloadFromJSON(data.oauth ?? null);
57+
this.createdAt = unixEpochToDate(data.created_at);
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+
65+
return this;
66+
}
67+
68+
public __internal_toSnapshot(): EnterpriseConnectionTestRunJSONSnapshot {
69+
return {
70+
object: 'enterprise_connection_test_run',
71+
id: this.id,
72+
status: this.status,
73+
connection_type: this.connectionType,
74+
parsed_user_info: parsedUserInfoToJSON(this.parsedUserInfo),
75+
saml: samlPayloadToJSON(this.saml),
76+
oauth: oauthPayloadToJSON(this.oauth),
77+
logs: this.logs.map(log => ({
78+
level: log.level,
79+
code: log.code,
80+
short_message: log.shortMessage,
81+
message: log.message,
82+
})),
83+
created_at: this.createdAt?.getTime() ?? 0,
84+
};
85+
}
86+
}
87+
88+
function parsedUserInfoFromJSON(
89+
data: EnterpriseConnectionTestRunParsedUserInfoJSON | null | undefined,
90+
): EnterpriseConnectionTestRunParsedUserInfoResource | null {
91+
if (!data) {
92+
return null;
93+
}
94+
95+
return {
96+
emailAddress: data.email_address,
97+
firstName: data.first_name,
98+
lastName: data.last_name,
99+
userId: data.user_id,
100+
};
101+
}
102+
103+
function parsedUserInfoToJSON(
104+
data: EnterpriseConnectionTestRunParsedUserInfoResource | null,
105+
): EnterpriseConnectionTestRunParsedUserInfoJSON | null {
106+
if (!data) {
107+
return null;
108+
}
109+
110+
return {
111+
email_address: data.emailAddress,
112+
first_name: data.firstName,
113+
last_name: data.lastName,
114+
user_id: data.userId,
115+
};
116+
}
117+
118+
function samlPayloadFromJSON(
119+
data: EnterpriseConnectionTestRunSamlPayloadJSON | null | undefined,
120+
): EnterpriseConnectionTestRunSamlPayloadResource | null {
121+
if (!data) {
122+
return null;
123+
}
124+
125+
return {
126+
samlRequest: data.saml_request,
127+
samlResponse: data.saml_response,
128+
relayState: data.relay_state,
129+
};
130+
}
131+
132+
function samlPayloadToJSON(
133+
data: EnterpriseConnectionTestRunSamlPayloadResource | null,
134+
): EnterpriseConnectionTestRunSamlPayloadJSON | null {
135+
if (!data) {
136+
return null;
137+
}
138+
139+
return {
140+
saml_request: data.samlRequest,
141+
saml_response: data.samlResponse,
142+
relay_state: data.relayState,
143+
};
144+
}
145+
146+
function oauthPayloadFromJSON(
147+
data: EnterpriseConnectionTestRunOauthPayloadJSON | null | undefined,
148+
): EnterpriseConnectionTestRunOauthPayloadResource | null {
149+
if (!data) {
150+
return null;
151+
}
152+
153+
return {
154+
idToken: data.id_token,
155+
accessToken: data.access_token,
156+
userInfo: data.user_info,
157+
};
158+
}
159+
160+
function oauthPayloadToJSON(
161+
data: EnterpriseConnectionTestRunOauthPayloadResource | null,
162+
): EnterpriseConnectionTestRunOauthPayloadJSON | null {
163+
if (!data) {
164+
return null;
165+
}
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: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { getFullName } from '@clerk/shared/internal/clerk-js/user';
2+
import { deepCamelToSnake } from '@clerk/shared/underscore';
23
import type {
34
BackupCodeJSON,
45
BackupCodeResource,
@@ -14,7 +15,14 @@ import type {
1415
EnterpriseConnectionResource,
1516
ExternalAccountJSON,
1617
ExternalAccountResource,
18+
ClerkPaginatedResponse,
1719
CreateMeEnterpriseConnectionParams,
20+
EnterpriseConnectionTestRunInitJSON,
21+
EnterpriseConnectionTestRunJSON,
22+
EnterpriseConnectionTestRunsPaginatedJSON,
23+
EnterpriseConnectionTestRunInitResource,
24+
EnterpriseConnectionTestRunResource,
25+
GetEnterpriseConnectionTestRunsParams,
1826
GetEnterpriseConnectionsParams,
1927
UpdateMeEnterpriseConnectionParams,
2028
GetOrganizationMemberships,
@@ -38,10 +46,7 @@ import type {
3846
} from '@clerk/shared/types';
3947

4048
import { unixEpochToDate } from '../../utils/date';
41-
import {
42-
buildCreateMeEnterpriseConnectionBody,
43-
buildUpdateMeEnterpriseConnectionBody,
44-
} from '../../utils/meEnterpriseConnectionBody';
49+
import { convertPageToOffsetSearchParams } from '../../utils/convertPageToOffsetSearchParams';
4550
import { normalizeUnsafeMetadata } from '../../utils/resourceParams';
4651
import { eventBus, events } from '../events';
4752
import { addPaymentMethod, getPaymentMethods, initializePaymentMethod } from '../modules/billing';
@@ -52,6 +57,7 @@ import {
5257
EmailAddress,
5358
EnterpriseAccount,
5459
EnterpriseConnection,
60+
EnterpriseConnectionTestRun,
5561
ExternalAccount,
5662
Image,
5763
OrganizationMembership,
@@ -329,7 +335,7 @@ export class User extends BaseResource implements UserResource {
329335
await BaseResource._fetch<EnterpriseConnectionJSON>({
330336
path: `${this.path()}/enterprise_connections`,
331337
method: 'POST',
332-
body: buildCreateMeEnterpriseConnectionBody(params) as any,
338+
body: deepCamelToSnake(params) as any,
333339
})
334340
)?.response as unknown as EnterpriseConnectionJSON;
335341

@@ -344,7 +350,7 @@ export class User extends BaseResource implements UserResource {
344350
await BaseResource._fetch<EnterpriseConnectionJSON>({
345351
path: `${this.path()}/enterprise_connections/${enterpriseConnectionId}`,
346352
method: 'PATCH',
347-
body: buildUpdateMeEnterpriseConnectionBody(params) as any,
353+
body: deepCamelToSnake(params) as any,
348354
})
349355
)?.response as unknown as EnterpriseConnectionJSON;
350356

@@ -362,6 +368,47 @@ export class User extends BaseResource implements UserResource {
362368
return new DeletedObject(json);
363369
};
364370

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

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

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,73 @@ 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+
object: 'enterprise_connection_test_run' as const,
299+
id: 'run_1',
300+
status: 'success',
301+
connection_type: 'saml' as const,
302+
created_at: 1700000000000,
303+
},
304+
],
305+
total_count: 1,
306+
};
307+
308+
// @ts-ignore
309+
BaseResource._fetch = vi.fn().mockReturnValue(Promise.resolve({ response: paginated }));
310+
311+
const user = new User({
312+
email_addresses: [],
313+
phone_numbers: [],
314+
web3_wallets: [],
315+
external_accounts: [],
316+
} as unknown as UserJSON);
317+
318+
const result = await user.getEnterpriseConnectionTestRuns('ec_123', {
319+
initialPage: 1,
320+
pageSize: 10,
321+
status: ['pending', 'success'],
322+
});
323+
324+
// @ts-ignore
325+
const call = BaseResource._fetch.mock.calls[0][0];
326+
expect(call.method).toBe('GET');
327+
expect(call.path).toBe('/me/enterprise_connections/ec_123/test_runs');
328+
expect(call.search.get('limit')).toBe('10');
329+
expect(call.search.get('offset')).toBe('0');
330+
expect(call.search.get('paginated')).toBe('true');
331+
expect(call.search.getAll('status')).toEqual(['pending', 'success']);
332+
333+
expect(result.total_count).toBe(1);
334+
expect(result.data).toHaveLength(1);
335+
expect(result.data[0].id).toBe('run_1');
336+
expect(result.data[0].connectionType).toBe('saml');
337+
});
338+
272339
it('creates a web3 wallet', async () => {
273340
const targetWeb3Wallet = '0x0000000000000000000000000000000000000000';
274341
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)