@postman-cse/onboarding-bootstrap 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,786 +0,0 @@
1
- import { HttpError } from '../http-error.js';
2
- import { retry } from '../retry.js';
3
- import { createSecretMasker, type SecretMasker } from '../secrets.js';
4
-
5
- type EnvironmentValue = {
6
- key: string;
7
- type: string;
8
- value: string;
9
- };
10
-
11
- type FetchResult = Record<string, any> | null;
12
-
13
- export interface PostmanAssetsClientOptions {
14
- apiKey: string;
15
- baseUrl?: string;
16
- fetchImpl?: typeof fetch;
17
- secretMasker?: SecretMasker;
18
- }
19
-
20
- export function normalizeGitRepoUrl(url: string | null | undefined): string {
21
- const raw = String(url || '').trim();
22
- if (!raw) return '';
23
-
24
- // git@<host>:<owner>/<repo>.git -> https://<host>/<owner>/<repo>
25
- const sshMatch = raw.match(/^git@([^:]+):(.+)$/i);
26
- if (sshMatch?.[1] && sshMatch?.[2]) {
27
- return normalizeGitRepoUrl(`https://${sshMatch[1]}/${sshMatch[2]}`);
28
- }
29
-
30
- try {
31
- const parsed = new URL(raw);
32
- const host = parsed.hostname.toLowerCase();
33
- const parts = parsed.pathname
34
- .replace(/^\/+|\/+$/g, '')
35
- .replace(/\.git$/i, '')
36
- .split('/')
37
- .filter(Boolean);
38
- if (parts.length < 2) return raw.replace(/\.git$/i, '').replace(/\/+$/g, '').toLowerCase();
39
- return `https://${host}/${parts[0].toLowerCase()}/${parts[1].toLowerCase()}`;
40
- } catch {
41
- return raw.replace(/\.git$/i, '').replace(/\/+$/g, '').toLowerCase();
42
- }
43
- }
44
-
45
- function extractGitRepoUrl(value: unknown): string | null {
46
- if (!value) return null;
47
- if (typeof value === 'string') {
48
- const normalized = normalizeGitRepoUrl(value);
49
- // Accept any URL that looks like a hosted git repo (github.com, gitlab.com, or self-hosted)
50
- if (/^https:\/\/[^/]+\/[^/]+\/[^/]+$/.test(normalized)) return normalized;
51
- return null;
52
- }
53
- if (Array.isArray(value)) {
54
- for (const item of value) {
55
- const repoUrl = extractGitRepoUrl(item);
56
- if (repoUrl) return repoUrl;
57
- }
58
- return null;
59
- }
60
- if (typeof value === 'object') {
61
- const record = value as Record<string, unknown>;
62
- const preferredKeys = ['repo', 'repository', 'repoUrl', 'repo_url', 'remoteUrl', 'remote_url', 'origin'];
63
- for (const key of preferredKeys) {
64
- const repoUrl = extractGitRepoUrl(record[key]);
65
- if (repoUrl) return repoUrl;
66
- }
67
- for (const nested of Object.values(record)) {
68
- const repoUrl = extractGitRepoUrl(nested);
69
- if (repoUrl) return repoUrl;
70
- }
71
- }
72
- return null;
73
- }
74
-
75
- export class PostmanAssetsClient {
76
- private readonly apiKey: string;
77
- private readonly baseUrl: string;
78
- private readonly fetchImpl: typeof fetch;
79
- private readonly secretMasker: SecretMasker;
80
-
81
- constructor(options: PostmanAssetsClientOptions) {
82
- this.apiKey = String(options.apiKey || '').trim();
83
- this.baseUrl = String(options.baseUrl || 'https://api.getpostman.com').replace(
84
- /\/+$/,
85
- ''
86
- );
87
- this.fetchImpl = options.fetchImpl ?? fetch;
88
- this.secretMasker =
89
- options.secretMasker ?? createSecretMasker([this.apiKey]);
90
- }
91
-
92
- getBaseUrl(): string {
93
- return this.baseUrl;
94
- }
95
-
96
- async getMe(): Promise<Record<string, unknown> | null> {
97
- return this.request('/me', { method: 'GET' }) as Promise<Record<string, unknown> | null>;
98
- }
99
-
100
- async getAutoDerivedTeamId(): Promise<string | undefined> {
101
- try {
102
- const data = await this.getMe();
103
- const user = data?.user;
104
- if (user && typeof user === 'object' && 'teamId' in user && user.teamId) {
105
- return String(user.teamId);
106
- }
107
- } catch (e) {
108
- // ignore
109
- }
110
- return undefined;
111
- }
112
-
113
- async getTeams(): Promise<Array<{ id: number; name: string; handle: string; organizationId?: number }>> {
114
- const data = await this.request('/teams');
115
- const teams = data?.data ?? [];
116
- return Array.isArray(teams)
117
- ? teams
118
- .filter((t: any) => t?.id && t?.name)
119
- .map((t: any) => ({
120
- id: Number(t.id),
121
- name: String(t.name),
122
- handle: String(t.handle || ''),
123
- ...(t.organizationId != null ? { organizationId: Number(t.organizationId) } : {})
124
- }))
125
- : [];
126
- }
127
-
128
- private async request(
129
- path: string,
130
- init: RequestInit = {}
131
- ): Promise<FetchResult> {
132
- const url = path.startsWith('http') ? path : `${this.baseUrl}${path}`;
133
- const response = await this.fetchImpl(url, {
134
- ...init,
135
- headers: {
136
- 'Content-Type': 'application/json',
137
- 'X-Api-Key': this.apiKey,
138
- ...(init.headers || {})
139
- }
140
- });
141
-
142
- if (!response.ok) {
143
- throw await HttpError.fromResponse(response, {
144
- method: init.method || 'GET',
145
- requestHeaders: {
146
- 'Content-Type': 'application/json',
147
- 'X-Api-Key': this.apiKey,
148
- ...(init.headers || {})
149
- },
150
- secretValues: [this.apiKey],
151
- url
152
- });
153
- }
154
-
155
- try {
156
- return (await response.json()) as Record<string, any>;
157
- } catch {
158
- return null;
159
- }
160
- }
161
-
162
- async createWorkspace(name: string, about: string, targetTeamId?: number): Promise<{ id: string }> {
163
- return retry(async () => {
164
- const payload = {
165
- workspace: {
166
- about,
167
- name,
168
- type: 'team',
169
- ...(targetTeamId != null && !Number.isNaN(targetTeamId) ? { teamId: targetTeamId } : {})
170
- }
171
- };
172
- let created: FetchResult;
173
- try {
174
- created = await this.request('/workspaces', {
175
- method: 'POST',
176
- body: JSON.stringify(payload)
177
- });
178
- } catch (err) {
179
- if (err instanceof Error && err.message.includes('Only personal workspaces')) {
180
- throw new Error(
181
- 'Workspace creation failed: This may be an Org-mode account that requires a workspace-team-id input. ' +
182
- 'The Postman API does not allow creating team workspaces at the organization level. ' +
183
- 'Use the workspace-team-id input to specify which sub-team should own this workspace.'
184
- );
185
- }
186
- throw err;
187
- }
188
-
189
- const workspaceId = created?.workspace?.id;
190
- if (!workspaceId) {
191
- throw new Error('Workspace create did not return an id');
192
- }
193
-
194
- const workspace = await this.request(`/workspaces/${workspaceId}`);
195
- if (workspace?.workspace?.visibility !== 'team') {
196
- await this.request(`/workspaces/${workspaceId}`, {
197
- method: 'PUT',
198
- body: JSON.stringify(payload)
199
- });
200
- }
201
-
202
- return {
203
- id: workspaceId
204
- };
205
- }, {
206
- maxAttempts: 3,
207
- delayMs: 2000,
208
- shouldRetry: (err) =>
209
- !(err instanceof Error && err.message.includes('workspace-team-id'))
210
- });
211
- }
212
-
213
- async listWorkspaces(): Promise<Array<{ id: string; name: string; type: string }>> {
214
- const data = await this.request('/workspaces');
215
- const workspaces = data?.workspaces ?? [];
216
- return Array.isArray(workspaces)
217
- ? workspaces
218
- .filter((w: any) => w?.id && w?.name)
219
- .map((w: any) => ({
220
- id: String(w.id),
221
- name: String(w.name),
222
- type: String(w.type ?? 'team')
223
- }))
224
- : [];
225
- }
226
-
227
- async findWorkspacesByName(name: string): Promise<Array<{ id: string; name: string }>> {
228
- const workspaces = await this.listWorkspaces();
229
- return workspaces
230
- .filter((w) => w.name === name)
231
- .sort((a, b) => a.id.localeCompare(b.id))
232
- .map((w) => ({ id: w.id, name: w.name }));
233
- }
234
-
235
- async getWorkspaceGitRepoUrl(
236
- workspaceId: string,
237
- teamId: string,
238
- accessToken: string
239
- ): Promise<string | null> {
240
- const url = 'https://bifrost-premium-https-v4.gw.postman.com/ws/proxy';
241
- const headers: Record<string, string> = {
242
- 'x-access-token': accessToken,
243
- 'Content-Type': 'application/json'
244
- };
245
- if (teamId) {
246
- headers['x-entity-team-id'] = teamId;
247
- }
248
-
249
- const response = await this.fetchImpl(url, {
250
- method: 'POST',
251
- headers,
252
- body: JSON.stringify({
253
- service: 'workspaces',
254
- method: 'GET',
255
- path: `/workspaces/${workspaceId}/filesystem`
256
- })
257
- });
258
-
259
- if (response.status === 404) return null;
260
- if (!response.ok) {
261
- const body = await response.text();
262
- throw new Error(`Bifrost workspace lookup failed: ${response.status} - ${body}`);
263
- }
264
-
265
- const body = await response.text();
266
- if (!body.trim()) return null;
267
-
268
- try {
269
- return extractGitRepoUrl(JSON.parse(body));
270
- } catch {
271
- return extractGitRepoUrl(body);
272
- }
273
- }
274
-
275
- async inviteRequesterToWorkspace(
276
- workspaceId: string,
277
- email: string
278
- ): Promise<void> {
279
- const users = await this.request('/users');
280
- const user = users?.data?.find((entry: any) => entry.email === email);
281
- if (!user?.id) {
282
- return;
283
- }
284
-
285
- await this.request(`/workspaces/${workspaceId}/roles`, {
286
- method: 'PATCH',
287
- body: JSON.stringify({
288
- roles: [
289
- {
290
- op: 'add',
291
- path: '/user',
292
- value: [{ id: user.id, role: 2 }]
293
- }
294
- ]
295
- })
296
- });
297
- }
298
-
299
- async addAdminsToWorkspace(
300
- workspaceId: string,
301
- adminIds: string
302
- ): Promise<void> {
303
- const ids = String(adminIds || '')
304
- .split(',')
305
- .map((entry) => entry.trim())
306
- .filter(Boolean);
307
- if (ids.length === 0) {
308
- return;
309
- }
310
-
311
- await this.request(`/workspaces/${workspaceId}/roles`, {
312
- method: 'PATCH',
313
- body: JSON.stringify({
314
- roles: [
315
- {
316
- op: 'add',
317
- path: '/user',
318
- value: ids.map((id) => ({
319
- id: Number.parseInt(id, 10),
320
- role: 3
321
- }))
322
- }
323
- ]
324
- })
325
- });
326
- }
327
-
328
- async uploadSpec(
329
- workspaceId: string,
330
- projectName: string,
331
- specContent: string
332
- ): Promise<string> {
333
- const response = await this.request(`/specs?workspaceId=${workspaceId}`, {
334
- method: 'POST',
335
- body: JSON.stringify({
336
- name: projectName,
337
- type: 'OPENAPI:3.0',
338
- files: [{ path: 'index.yaml', content: specContent }]
339
- })
340
- });
341
-
342
- const specId = String(response?.id || '').trim();
343
- if (!specId) {
344
- throw new Error('Spec upload did not return an ID');
345
- }
346
-
347
- await retry(async () => {
348
- const verified = await this.request(`/specs/${specId}?workspaceId=${workspaceId}`);
349
- if (verified?.id !== specId) {
350
- throw new Error(`Spec preflight response did not contain expected id ${specId}`);
351
- }
352
- }, { maxAttempts: 3, delayMs: 2000 });
353
-
354
- return specId;
355
- }
356
-
357
-
358
- async updateSpec(
359
- specId: string,
360
- specContent: string,
361
- _workspaceId?: string
362
- ): Promise<void> {
363
- // Postman Spec Hub uses PATCH /specs/{specId}/files/{filePath} for updates.
364
- // PUT /specs/{specId} is not a valid endpoint and returns 404.
365
- await this.request(`/specs/${specId}/files/index.yaml`, {
366
- method: 'PATCH',
367
- body: JSON.stringify({ content: specContent })
368
- });
369
- }
370
-
371
- async getSpecContent(specId: string): Promise<string | undefined> {
372
- try {
373
- const result = await this.request(`/specs/${specId}/files/index.yaml`);
374
- return typeof result?.content === 'string' ? result.content : undefined;
375
- } catch {
376
- return undefined;
377
- }
378
- }
379
-
380
- async generateCollection(
381
- specId: string,
382
- projectName: string,
383
- prefix: string
384
- ): Promise<string> {
385
- const payload = {
386
- name: `${prefix} ${projectName}`,
387
- options: {
388
- requestNameSource: 'Fallback'
389
- }
390
- };
391
-
392
- const extractUid = (data: any): string | undefined =>
393
- data?.details?.resources?.[0]?.id ||
394
- data?.collection?.id ||
395
- data?.collection?.uid ||
396
- data?.resource?.uid ||
397
- data?.resource?.id ||
398
- undefined;
399
-
400
- return retry(
401
- async () => {
402
- const maxLockedRetries = 5;
403
- let response: FetchResult = null;
404
-
405
- for (let lockedAttempt = 0; ; lockedAttempt += 1) {
406
- try {
407
- response = await this.request(`/specs/${specId}/generations/collection`, {
408
- method: 'POST',
409
- body: JSON.stringify(payload)
410
- });
411
- break;
412
- } catch (error) {
413
- const message = this.secretMasker(
414
- error instanceof Error ? error.message : String(error)
415
- );
416
- const isLocked = message.includes('423');
417
- if (!isLocked || lockedAttempt >= maxLockedRetries) {
418
- throw error;
419
- }
420
- await new Promise((resolve) => {
421
- setTimeout(resolve, 5000 * Math.pow(2, lockedAttempt));
422
- });
423
- }
424
- }
425
-
426
- const directUid = extractUid(response);
427
- if (directUid) {
428
- return directUid;
429
- }
430
-
431
- let taskUrl =
432
- response?.url ||
433
- response?.task_url ||
434
- response?.taskUrl ||
435
- response?.links?.task;
436
- if (!taskUrl) {
437
- const taskId = response?.taskId || response?.task?.id || response?.id;
438
- if (!taskId) {
439
- throw new Error(
440
- `Collection generation did not return a task URL or ID for ${prefix}`
441
- );
442
- }
443
- taskUrl = `/specs/${specId}/tasks/${taskId}`;
444
- }
445
-
446
- for (let attempt = 0; attempt < 45; attempt += 1) {
447
- await new Promise((resolve) => {
448
- setTimeout(resolve, 2000);
449
- });
450
- const task = await this.request(taskUrl);
451
- const status = String(task?.status || task?.task?.status || '').toLowerCase();
452
- if (status === 'completed') {
453
- const taskUid = extractUid(task);
454
- if (!taskUid) {
455
- throw new Error(`Task completed but no UID found for ${prefix}`);
456
- }
457
- return taskUid;
458
- }
459
- if (status === 'failed') {
460
- throw new Error(`Task failed for ${prefix}`);
461
- }
462
- }
463
-
464
- throw new Error(`Collection generation timed out for ${prefix}`);
465
- },
466
- {
467
- maxAttempts: 4,
468
- delayMs: 2000
469
- }
470
- );
471
- }
472
-
473
- async tagCollection(collectionUid: string, tags: string[]): Promise<void> {
474
- const normalized = tags
475
- .map((entry) =>
476
- String(entry || '')
477
- .toLowerCase()
478
- .trim()
479
- .replace(/[^a-z0-9-]+/g, '-')
480
- .replace(/^-+|-+$/g, '')
481
- )
482
- .filter((entry) => /^[a-z][a-z0-9-]*[a-z0-9]$/.test(entry));
483
-
484
- if (normalized.length === 0) {
485
- throw new Error(`No valid tag slugs to apply for collection ${collectionUid}`);
486
- }
487
-
488
- await this.request(`/collections/${collectionUid}/tags`, {
489
- method: 'PUT',
490
- body: JSON.stringify({
491
- tags: normalized.map((slug) => ({ slug }))
492
- })
493
- });
494
- }
495
-
496
- async injectTests(collectionUid: string, type: 'contract' | 'smoke'): Promise<void> {
497
- const collectionResponse = await this.request(`/collections/${collectionUid}`);
498
- const collection = collectionResponse?.collection;
499
- if (!collection) {
500
- throw new Error(`Failed to fetch collection ${collectionUid}`);
501
- }
502
-
503
- const smokeTests = [
504
- "// [Smoke] Auto-generated test assertions",
505
- "",
506
- "pm.test('Status code is successful (2xx)', function () {",
507
- " pm.response.to.be.success;",
508
- "});",
509
- "",
510
- "pm.test('Response time is acceptable', function () {",
511
- " var threshold = parseInt(pm.environment.get('RESPONSE_TIME_THRESHOLD') || '2000', 10);",
512
- " pm.expect(pm.response.responseTime).to.be.below(threshold);",
513
- "});",
514
- "",
515
- "pm.test('Response body is not empty', function () {",
516
- " if (pm.response.code !== 204) {",
517
- " var body = pm.response.text();",
518
- " pm.expect(body.length).to.be.above(0);",
519
- " }",
520
- "});"
521
- ];
522
- const contractTests = [
523
- "// [Contract] Auto-generated contract test assertions",
524
- "",
525
- "pm.test('Status code is successful (2xx)', function () {",
526
- " pm.response.to.be.success;",
527
- "});",
528
- "",
529
- "pm.test('Response time is acceptable', function () {",
530
- " var threshold = parseInt(pm.environment.get('RESPONSE_TIME_THRESHOLD') || '2000', 10);",
531
- " pm.expect(pm.response.responseTime).to.be.below(threshold);",
532
- "});",
533
- "",
534
- "pm.test('Response body is not empty', function () {",
535
- " if (pm.response.code !== 204) {",
536
- " var body = pm.response.text();",
537
- " pm.expect(body.length).to.be.above(0);",
538
- " }",
539
- "});",
540
- "",
541
- "pm.test('Content-Type is application/json', function () {",
542
- " if (pm.response.code !== 204) {",
543
- " pm.response.to.have.header('Content-Type');",
544
- " pm.expect(pm.response.headers.get('Content-Type')).to.include('application/json');",
545
- " }",
546
- "});",
547
- "",
548
- "pm.test('Response is valid JSON', function () {",
549
- " if (pm.response.code !== 204) {",
550
- " pm.response.to.be.json;",
551
- " }",
552
- "});",
553
- "",
554
- "// Validate required fields from response schema",
555
- "pm.test('Required fields are present', function () {",
556
- " if (pm.response.code === 204) return;",
557
- " var jsonData = pm.response.json();",
558
- " pm.expect(jsonData).to.be.an('object');",
559
- " var keys = Object.keys(jsonData);",
560
- " if (keys.length === 1 && Array.isArray(jsonData[keys[0]])) {",
561
- " pm.expect(jsonData[keys[0]]).to.be.an('array');",
562
- " }",
563
- "});",
564
- "",
565
- "// Validate response field types (non-null required fields)",
566
- "pm.test('Field types are correct', function () {",
567
- " if (pm.response.code === 204) return;",
568
- " var jsonData = pm.response.json();",
569
- " Object.keys(jsonData).forEach(function(key) {",
570
- " pm.expect(jsonData[key]).to.not.be.undefined;",
571
- " });",
572
- "});",
573
- "",
574
- "(function() {",
575
- " var status = pm.response.code;",
576
- " if (status === 204) return; ",
577
- " try {",
578
- " var body = pm.response.json();",
579
- " pm.test('Response body matches expected structure', function () {",
580
- " pm.expect(typeof body).to.equal('object');",
581
- " if (status >= 400) {",
582
- " pm.expect(body).to.have.property('error');",
583
- " pm.expect(body).to.have.property('message');",
584
- " }",
585
- " });",
586
- " } catch (e) {}",
587
- "})();"
588
- ];
589
-
590
- const scriptsToInject = type === 'smoke' ? smokeTests : contractTests;
591
- const request0Item = {
592
- name: '00 - Resolve Secrets',
593
- request: {
594
- auth: {
595
- type: 'awsv4',
596
- awsv4: [
597
- { key: 'accessKey', value: '{{AWS_ACCESS_KEY_ID}}' },
598
- { key: 'secretKey', value: '{{AWS_SECRET_ACCESS_KEY}}' },
599
- { key: 'region', value: '{{AWS_REGION}}' },
600
- { key: 'service', value: 'secretsmanager' }
601
- ]
602
- },
603
- method: 'POST',
604
- header: [
605
- { key: 'X-Amz-Target', value: 'secretsmanager.GetSecretValue' },
606
- { key: 'Content-Type', value: 'application/x-amz-json-1.1' }
607
- ],
608
- body: {
609
- mode: 'raw',
610
- raw: '{"SecretId": "{{AWS_SECRET_NAME}}"}'
611
- },
612
- url: {
613
- raw: 'https://secretsmanager.{{AWS_REGION}}.amazonaws.com',
614
- protocol: 'https',
615
- host: ['secretsmanager', '{{AWS_REGION}}', 'amazonaws', 'com']
616
- }
617
- },
618
- event: [
619
- {
620
- listen: 'test',
621
- script: {
622
- exec: [
623
- 'if (pm.environment.get("CI") === "true") { return; }',
624
- 'const body = pm.response.json();',
625
- 'if (body.SecretString) {',
626
- ' const secrets = JSON.parse(body.SecretString);',
627
- ' Object.entries(secrets).forEach(([k, v]) => pm.collectionVariables.set(k, v));',
628
- '}'
629
- ]
630
- }
631
- }
632
- ]
633
- };
634
-
635
- const injectScripts = (itemNode: any) => {
636
- if (itemNode.name === '00 - Resolve Secrets') {
637
- return;
638
- }
639
-
640
- if (itemNode.request) {
641
- itemNode.event = (itemNode.event || []).filter(
642
- (entry: any) => entry.listen !== 'test'
643
- );
644
- itemNode.event.push({
645
- listen: 'test',
646
- script: {
647
- type: 'text/javascript',
648
- exec: scriptsToInject
649
- }
650
- });
651
- }
652
- if (Array.isArray(itemNode.item)) {
653
- itemNode.item.forEach(injectScripts);
654
- }
655
- };
656
-
657
- if (Array.isArray(collection.item)) {
658
- // Remove any existing secrets resolver to prevent duplicates on reruns
659
- collection.item = collection.item.filter(
660
- (entry: any) => entry.name !== '00 - Resolve Secrets'
661
- );
662
- collection.item.forEach(injectScripts);
663
- } else {
664
- collection.item = [];
665
- }
666
-
667
- collection.item.unshift(request0Item);
668
-
669
- await this.request(`/collections/${collectionUid}`, {
670
- method: 'PUT',
671
- body: JSON.stringify({ collection })
672
- });
673
- }
674
-
675
- async createEnvironment(
676
- workspaceId: string,
677
- name: string,
678
- values: EnvironmentValue[]
679
- ): Promise<string> {
680
- const response = await this.request(`/environments?workspace=${workspaceId}`, {
681
- method: 'POST',
682
- body: JSON.stringify({
683
- environment: {
684
- name,
685
- values
686
- }
687
- })
688
- });
689
-
690
- const uid = String(response?.environment?.uid || '').trim();
691
- if (!uid) {
692
- throw new Error('Environment create did not return a UID');
693
- }
694
- return uid;
695
- }
696
-
697
- async updateEnvironment(
698
- uid: string,
699
- name: string,
700
- values: EnvironmentValue[]
701
- ): Promise<void> {
702
- await this.request(`/environments/${uid}`, {
703
- method: 'PUT',
704
- body: JSON.stringify({
705
- environment: {
706
- name,
707
- values
708
- }
709
- })
710
- });
711
- }
712
-
713
- async createMonitor(
714
- workspaceId: string,
715
- name: string,
716
- collectionUid: string,
717
- environmentUid: string
718
- ): Promise<string> {
719
- const response = await this.request(`/monitors?workspace=${workspaceId}`, {
720
- method: 'POST',
721
- body: JSON.stringify({
722
- monitor: {
723
- name,
724
- collection: collectionUid,
725
- environment: environmentUid,
726
- schedule: {
727
- cron: '*/5 * * * *',
728
- timezone: 'UTC'
729
- }
730
- }
731
- })
732
- });
733
-
734
- const uid = String(response?.monitor?.uid || '').trim();
735
- if (!uid) {
736
- throw new Error('Monitor create did not return a UID');
737
- }
738
- return uid;
739
- }
740
-
741
- async createMock(
742
- workspaceId: string,
743
- name: string,
744
- collectionUid: string,
745
- environmentUid: string
746
- ): Promise<{ uid: string; url: string }> {
747
- const response = await this.request(`/mocks?workspace=${workspaceId}`, {
748
- method: 'POST',
749
- body: JSON.stringify({
750
- mock: {
751
- name,
752
- collection: collectionUid,
753
- environment: environmentUid,
754
- private: false
755
- }
756
- })
757
- });
758
-
759
- const uid = String(response?.mock?.uid || '').trim();
760
- if (!uid) {
761
- throw new Error('Mock create did not return a UID');
762
- }
763
-
764
- return {
765
- uid,
766
- url:
767
- String(response?.mock?.mockUrl || '').trim() ||
768
- String(response?.mock?.config?.serverResponseId || '').trim()
769
- };
770
- }
771
-
772
- async getCollection(uid: string): Promise<any> {
773
- const response = await this.request(`/collections/${uid}`);
774
- return response?.collection;
775
- }
776
-
777
- async getEnvironment(uid: string): Promise<any> {
778
- const response = await this.request(`/environments/${uid}`);
779
- return response?.environment;
780
- }
781
-
782
- async getEnvironments(workspaceId: string): Promise<any[]> {
783
- const response = await this.request(`/environments?workspace=${workspaceId}`);
784
- return response?.environments || [];
785
- }
786
- }