@postman-cse/onboarding-bootstrap 0.9.1 → 0.11.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.
@@ -9,6 +9,13 @@ export interface GovernanceAssociation {
9
9
  systemEnvId: string;
10
10
  }
11
11
 
12
+ export interface SpecificationCollectionLink {
13
+ collectionId: string;
14
+ syncOptions?: {
15
+ syncExamples: boolean;
16
+ };
17
+ }
18
+
12
19
  export interface InternalIntegrationAdapterOptions {
13
20
  accessToken: string;
14
21
  backend: string;
@@ -32,6 +39,14 @@ export interface InternalIntegrationAdapter {
32
39
  workspaceId: string,
33
40
  repoUrl: string
34
41
  ): Promise<void>;
42
+ linkCollectionsToSpecification(
43
+ specificationId: string,
44
+ collections: SpecificationCollectionLink[]
45
+ ): Promise<void>;
46
+ syncCollection(
47
+ specificationId: string,
48
+ collectionId: string
49
+ ): Promise<void>;
35
50
  }
36
51
 
37
52
  class BifrostInternalIntegrationAdapter implements InternalIntegrationAdapter {
@@ -53,6 +68,33 @@ class BifrostInternalIntegrationAdapter implements InternalIntegrationAdapter {
53
68
  ).replace(/\/+$/, '');
54
69
  }
55
70
 
71
+ private async proxyRequest(
72
+ service: string,
73
+ method: string,
74
+ requestPath: string,
75
+ body?: unknown
76
+ ): Promise<Response> {
77
+ const url = 'https://bifrost-premium-https-v4.gw.postman.com/ws/proxy';
78
+ const headers: Record<string, string> = {
79
+ 'Content-Type': 'application/json',
80
+ 'x-access-token': this.accessToken
81
+ };
82
+ if (this.teamId) {
83
+ headers['x-entity-team-id'] = this.teamId;
84
+ }
85
+
86
+ return this.fetchImpl(url, {
87
+ method: 'POST',
88
+ headers,
89
+ body: JSON.stringify({
90
+ service,
91
+ method,
92
+ path: requestPath,
93
+ ...(body !== undefined ? { body } : {})
94
+ })
95
+ });
96
+ }
97
+
56
98
  async assignWorkspaceToGovernanceGroup(
57
99
  workspaceId: string,
58
100
  domain: string,
@@ -168,14 +210,6 @@ class BifrostInternalIntegrationAdapter implements InternalIntegrationAdapter {
168
210
  workspaceId: string,
169
211
  repoUrl: string
170
212
  ): Promise<void> {
171
- const url = 'https://bifrost-premium-https-v4.gw.postman.com/ws/proxy';
172
- const headers: Record<string, string> = {
173
- 'Content-Type': 'application/json',
174
- 'x-access-token': this.accessToken
175
- };
176
- if (this.teamId) {
177
- headers['x-entity-team-id'] = this.teamId;
178
- }
179
213
  const payload = {
180
214
  service: 'workspaces',
181
215
  method: 'POST',
@@ -187,11 +221,12 @@ class BifrostInternalIntegrationAdapter implements InternalIntegrationAdapter {
187
221
  }
188
222
  };
189
223
 
190
- const response = await this.fetchImpl(url, {
191
- method: 'POST',
192
- headers,
193
- body: JSON.stringify(payload)
194
- });
224
+ const response = await this.proxyRequest(
225
+ payload.service,
226
+ payload.method,
227
+ payload.path,
228
+ payload.body
229
+ );
195
230
 
196
231
  if (response.ok) return;
197
232
 
@@ -215,31 +250,82 @@ class BifrostInternalIntegrationAdapter implements InternalIntegrationAdapter {
215
250
 
216
251
  throw await HttpError.fromResponse(response, {
217
252
  method: 'POST',
218
- requestHeaders: headers,
253
+ requestHeaders: {
254
+ 'Content-Type': 'application/json',
255
+ 'x-access-token': this.accessToken,
256
+ ...(this.teamId ? { 'x-entity-team-id': this.teamId } : {})
257
+ },
219
258
  secretValues: [this.accessToken],
220
- url
259
+ url: 'https://bifrost-premium-https-v4.gw.postman.com/ws/proxy'
221
260
  });
222
261
  }
223
262
 
224
- private async getWorkspaceGitRepoUrl(workspaceId: string): Promise<string | null> {
225
- const url = 'https://bifrost-premium-https-v4.gw.postman.com/ws/proxy';
226
- const headers: Record<string, string> = {
227
- 'Content-Type': 'application/json',
228
- 'x-access-token': this.accessToken
229
- };
230
- if (this.teamId) {
231
- headers['x-entity-team-id'] = this.teamId;
263
+ async linkCollectionsToSpecification(
264
+ specificationId: string,
265
+ collections: SpecificationCollectionLink[]
266
+ ): Promise<void> {
267
+ if (collections.length === 0) {
268
+ return;
232
269
  }
233
270
 
234
- const response = await this.fetchImpl(url, {
271
+ const response = await this.proxyRequest(
272
+ 'specification',
273
+ 'put',
274
+ `/specifications/${specificationId}/collections`,
275
+ collections.map((collection) => ({
276
+ collectionId: collection.collectionId,
277
+ ...(collection.syncOptions ? { syncOptions: collection.syncOptions } : {})
278
+ }))
279
+ );
280
+
281
+ if (response.ok) {
282
+ return;
283
+ }
284
+
285
+ throw await HttpError.fromResponse(response, {
235
286
  method: 'POST',
236
- headers,
237
- body: JSON.stringify({
238
- service: 'workspaces',
239
- method: 'GET',
240
- path: `/workspaces/${workspaceId}/filesystem`
241
- })
287
+ requestHeaders: {
288
+ 'Content-Type': 'application/json',
289
+ 'x-access-token': this.accessToken,
290
+ ...(this.teamId ? { 'x-entity-team-id': this.teamId } : {})
291
+ },
292
+ secretValues: [this.accessToken],
293
+ url: 'https://bifrost-premium-https-v4.gw.postman.com/ws/proxy'
242
294
  });
295
+ }
296
+
297
+ async syncCollection(
298
+ specificationId: string,
299
+ collectionId: string
300
+ ): Promise<void> {
301
+ const response = await this.proxyRequest(
302
+ 'specification',
303
+ 'post',
304
+ `/specifications/${specificationId}/collections/${collectionId}/sync`
305
+ );
306
+
307
+ if (response.ok) {
308
+ return;
309
+ }
310
+
311
+ throw await HttpError.fromResponse(response, {
312
+ method: 'POST',
313
+ requestHeaders: {
314
+ 'Content-Type': 'application/json',
315
+ 'x-access-token': this.accessToken,
316
+ ...(this.teamId ? { 'x-entity-team-id': this.teamId } : {})
317
+ },
318
+ secretValues: [this.accessToken],
319
+ url: 'https://bifrost-premium-https-v4.gw.postman.com/ws/proxy'
320
+ });
321
+ }
322
+
323
+ private async getWorkspaceGitRepoUrl(workspaceId: string): Promise<string | null> {
324
+ const response = await this.proxyRequest(
325
+ 'workspaces',
326
+ 'GET',
327
+ `/workspaces/${workspaceId}/filesystem`
328
+ );
243
329
 
244
330
  if (response.status === 404) return null;
245
331
  if (!response.ok) return null;
@@ -110,6 +110,21 @@ export class PostmanAssetsClient {
110
110
  return undefined;
111
111
  }
112
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
+
113
128
  private async request(
114
129
  path: string,
115
130
  init: RequestInit = {}
@@ -144,13 +159,14 @@ export class PostmanAssetsClient {
144
159
  }
145
160
  }
146
161
 
147
- async createWorkspace(name: string, about: string): Promise<{ id: string }> {
162
+ async createWorkspace(name: string, about: string, targetTeamId?: number): Promise<{ id: string }> {
148
163
  return retry(async () => {
149
164
  const payload = {
150
165
  workspace: {
151
166
  about,
152
167
  name,
153
- type: 'team'
168
+ type: 'team',
169
+ ...(targetTeamId != null && !Number.isNaN(targetTeamId) ? { teamId: targetTeamId } : {})
154
170
  }
155
171
  };
156
172
  let created: FetchResult;
@@ -162,7 +178,9 @@ export class PostmanAssetsClient {
162
178
  } catch (err) {
163
179
  if (err instanceof Error && err.message.includes('Only personal workspaces')) {
164
180
  throw new Error(
165
- 'Workspace creation failed: Your team may have Org Mode enabled. Org-level workspaces require a different API request schema which is currently unsupported in this alpha version.'
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.'
166
184
  );
167
185
  }
168
186
  throw err;
@@ -184,7 +202,12 @@ export class PostmanAssetsClient {
184
202
  return {
185
203
  id: workspaceId
186
204
  };
187
- }, { maxAttempts: 3, delayMs: 2000 });
205
+ }, {
206
+ maxAttempts: 3,
207
+ delayMs: 2000,
208
+ shouldRetry: (err) =>
209
+ !(err instanceof Error && err.message.includes('workspace-team-id'))
210
+ });
188
211
  }
189
212
 
190
213
  async listWorkspaces(): Promise<Array<{ id: string; name: string; type: string }>> {