@postman-cse/onboarding-insights 0.7.2 → 0.9.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.
- package/README.md +14 -4
- package/action.yml +7 -6
- package/dist/cli.cjs +162 -64
- package/dist/index.cjs +154 -71
- package/package.json +14 -3
package/README.md
CHANGED
|
@@ -122,7 +122,7 @@ Output is JSON to stdout. Use `--result-json` to write the same payload to a fil
|
|
|
122
122
|
|
|
123
123
|
```yaml
|
|
124
124
|
onboarding:
|
|
125
|
-
image: node:
|
|
125
|
+
image: node:24
|
|
126
126
|
script:
|
|
127
127
|
- npm install -g postman-insights-onboarding-action
|
|
128
128
|
- postman-insights-onboard --project-name af-cards-activation --workspace-id "$WORKSPACE_ID" --environment-id "$ENVIRONMENT_ID" --postman-access-token "$POSTMAN_ACCESS_TOKEN" --postman-api-key "$POSTMAN_API_KEY" --cluster-name "$CLUSTER_NAME" --repo-url "$CI_PROJECT_URL" --poll-timeout-seconds 180 --result-json insights-result.json --dotenv-path insights.env
|
|
@@ -131,7 +131,7 @@ onboarding:
|
|
|
131
131
|
### Bitbucket Pipelines
|
|
132
132
|
|
|
133
133
|
```yaml
|
|
134
|
-
image: node:
|
|
134
|
+
image: node:24
|
|
135
135
|
|
|
136
136
|
pipelines:
|
|
137
137
|
default:
|
|
@@ -166,8 +166,7 @@ steps:
|
|
|
166
166
|
| `environment-id` | Yes | | Postman environment UID for the onboarding association. |
|
|
167
167
|
| `system-environment-id` | No | | Postman system environment UUID for service-level Insights acknowledgment. Falls back to the value from the discovered service record. |
|
|
168
168
|
| `cluster-name` | No | | Insights cluster name. When set, the action matches `{cluster-name}/{project-name}` exactly. When omitted, falls back to suffix matching. |
|
|
169
|
-
| `
|
|
170
|
-
| `git-repository-name` | No | `project-name` | GitHub repository name. Defaults to the project name. |
|
|
169
|
+
| `repo-url` | No | Auto-detected from CI when available | Repository URL used for Git onboarding. |
|
|
171
170
|
| `postman-access-token` | Yes | | Postman session token for Bifrost API calls. See [Obtaining postman-access-token](#obtaining-postman-access-token-open-alpha). |
|
|
172
171
|
| `postman-team-id` | No | | Explicit Postman team ID for org-mode Bifrost requests. When omitted, the action leaves `x-entity-team-id` unset so Bifrost resolves team context from the access token. |
|
|
173
172
|
| `github-token` | No | ambient `GITHUB_TOKEN` env when exported by the workflow | Optional GitHub token passed as `git_api_key` only when repository auth is required by the onboarding endpoint. |
|
|
@@ -238,6 +237,17 @@ The action calls the following API endpoints in order:
|
|
|
238
237
|
7. **Workspace acknowledge** -- `POST /v2/workspaces/{id}/onboarding/acknowledge` (Bifrost akita) to activate the Insights project.
|
|
239
238
|
8. **Team verification token** -- `GET /v2/workspaces/{id}/team-verification-token` (Bifrost akita) to retrieve the DaemonSet telemetry token.
|
|
240
239
|
|
|
240
|
+
## Contract smoke monitoring
|
|
241
|
+
|
|
242
|
+
This repo includes `.github/workflows/contract-smoke.yml`, a scheduled live contract check for the upstream APIs used by Insights onboarding.
|
|
243
|
+
|
|
244
|
+
Configure these repository secrets before enabling the workflow:
|
|
245
|
+
|
|
246
|
+
- `SMOKE_ORG_API_KEY`
|
|
247
|
+
- `SMOKE_ORG_ACCESS_TOKEN`
|
|
248
|
+
|
|
249
|
+
The smoke workflow verifies `/me`, `/teams`, `iapub.postman.co/api/sessions/current`, and Bifrost API key creation so auth or payload drift shows up in CI before it hits production onboarding runs.
|
|
250
|
+
|
|
241
251
|
## Local development
|
|
242
252
|
|
|
243
253
|
```bash
|
package/action.yml
CHANGED
|
@@ -16,11 +16,8 @@ inputs:
|
|
|
16
16
|
cluster-name:
|
|
17
17
|
description: 'Insights cluster name. Matches {cluster}/{project-name} in discovered services'
|
|
18
18
|
required: false
|
|
19
|
-
|
|
20
|
-
description: '
|
|
21
|
-
required: false
|
|
22
|
-
git-repository-name:
|
|
23
|
-
description: 'GitHub repository name. Defaults to project-name'
|
|
19
|
+
repo-url:
|
|
20
|
+
description: 'Repository URL for Git onboarding. Auto-detected from CI context when omitted.'
|
|
24
21
|
required: false
|
|
25
22
|
postman-access-token:
|
|
26
23
|
description: 'Postman access token for Bifrost API calls'
|
|
@@ -42,6 +39,10 @@ inputs:
|
|
|
42
39
|
description: 'Seconds between discovery polling attempts'
|
|
43
40
|
required: false
|
|
44
41
|
default: '10'
|
|
42
|
+
postman-stack:
|
|
43
|
+
description: 'Postman stack profile.'
|
|
44
|
+
required: false
|
|
45
|
+
default: 'prod'
|
|
45
46
|
outputs:
|
|
46
47
|
discovered-service-id:
|
|
47
48
|
description: 'Numeric ID from the API Catalog discovered-services list'
|
|
@@ -56,5 +57,5 @@ outputs:
|
|
|
56
57
|
status:
|
|
57
58
|
description: 'Onboarding result: success, not-found, or error'
|
|
58
59
|
runs:
|
|
59
|
-
using: '
|
|
60
|
+
using: 'node24'
|
|
60
61
|
main: 'dist/index.cjs'
|
package/dist/cli.cjs
CHANGED
|
@@ -27347,9 +27347,6 @@ function redactSecrets(input, secretValues, replacement = REDACTED) {
|
|
|
27347
27347
|
return sanitized.split(secret).join(replacement);
|
|
27348
27348
|
}, source);
|
|
27349
27349
|
}
|
|
27350
|
-
function createSecretMasker(secretValues, replacement = REDACTED) {
|
|
27351
|
-
return (input) => redactSecrets(input, secretValues, replacement);
|
|
27352
|
-
}
|
|
27353
27350
|
function headerEntries(headers) {
|
|
27354
27351
|
if (headers instanceof Headers) {
|
|
27355
27352
|
return Array.from(headers.entries());
|
|
@@ -27476,20 +27473,58 @@ async function retry(operation, options = {}) {
|
|
|
27476
27473
|
throw new Error("Retry exhausted without returning or throwing");
|
|
27477
27474
|
}
|
|
27478
27475
|
|
|
27476
|
+
// src/lib/postman/base-urls.ts
|
|
27477
|
+
var POSTMAN_ENDPOINT_PROFILES = {
|
|
27478
|
+
prod: {
|
|
27479
|
+
apiBaseUrl: "https://api.getpostman.com",
|
|
27480
|
+
bifrostBaseUrl: "https://bifrost-premium-https-v4.gw.postman.com",
|
|
27481
|
+
observabilityBaseUrl: "https://api.observability.postman.com",
|
|
27482
|
+
observabilityEnv: "production"
|
|
27483
|
+
},
|
|
27484
|
+
beta: {
|
|
27485
|
+
apiBaseUrl: "https://api.getpostman-beta.com",
|
|
27486
|
+
bifrostBaseUrl: "https://bifrost-https-v4.gw.postman-beta.com",
|
|
27487
|
+
observabilityBaseUrl: "https://api.observability.postman-beta.com",
|
|
27488
|
+
observabilityEnv: "beta"
|
|
27489
|
+
}
|
|
27490
|
+
};
|
|
27491
|
+
function parsePostmanStack(value) {
|
|
27492
|
+
const normalized = String(value || "prod").trim().toLowerCase();
|
|
27493
|
+
if (normalized === "prod" || normalized === "beta") {
|
|
27494
|
+
return normalized;
|
|
27495
|
+
}
|
|
27496
|
+
throw new Error(`Unsupported postman-stack "${value}". Supported values: prod, beta`);
|
|
27497
|
+
}
|
|
27498
|
+
function resolvePostmanEndpointProfile(stack) {
|
|
27499
|
+
return POSTMAN_ENDPOINT_PROFILES[stack];
|
|
27500
|
+
}
|
|
27501
|
+
|
|
27479
27502
|
// src/lib/bifrost-client.ts
|
|
27480
|
-
var
|
|
27503
|
+
var DEFAULT_BIFROST_BASE_URL = POSTMAN_ENDPOINT_PROFILES.prod.bifrostBaseUrl;
|
|
27504
|
+
var BIFROST_PROXY_PATH = "/ws/proxy";
|
|
27505
|
+
var DEFAULT_OBSERVABILITY_BASE_URL = POSTMAN_ENDPOINT_PROFILES.prod.observabilityBaseUrl;
|
|
27506
|
+
var DEFAULT_OBSERVABILITY_ENV = POSTMAN_ENDPOINT_PROFILES.prod.observabilityEnv;
|
|
27507
|
+
var MAX_DISCOVERED_SERVICE_PAGES = 100;
|
|
27508
|
+
var MAX_PROVIDER_SERVICE_PAGES = 100;
|
|
27481
27509
|
var BifrostCatalogClient = class {
|
|
27482
27510
|
accessToken;
|
|
27483
27511
|
teamId;
|
|
27484
27512
|
apiKey;
|
|
27485
27513
|
fetchFn;
|
|
27486
27514
|
secretValues;
|
|
27515
|
+
bifrostProxyUrl;
|
|
27516
|
+
observabilityBaseUrl;
|
|
27517
|
+
observabilityEnv;
|
|
27487
27518
|
constructor(options) {
|
|
27488
27519
|
this.accessToken = options.accessToken;
|
|
27489
27520
|
this.teamId = options.teamId;
|
|
27490
27521
|
this.apiKey = options.apiKey;
|
|
27491
27522
|
this.fetchFn = options.fetchFn ?? globalThis.fetch;
|
|
27492
27523
|
this.secretValues = [options.accessToken, options.apiKey].filter(Boolean);
|
|
27524
|
+
const base = (options.bifrostBaseUrl || DEFAULT_BIFROST_BASE_URL).replace(/\/+$/, "");
|
|
27525
|
+
this.bifrostProxyUrl = `${base}${BIFROST_PROXY_PATH}`;
|
|
27526
|
+
this.observabilityBaseUrl = (options.observabilityBaseUrl || DEFAULT_OBSERVABILITY_BASE_URL).replace(/\/+$/, "");
|
|
27527
|
+
this.observabilityEnv = options.observabilityEnv || DEFAULT_OBSERVABILITY_ENV;
|
|
27493
27528
|
}
|
|
27494
27529
|
setApiKey(apiKey) {
|
|
27495
27530
|
this.apiKey = apiKey;
|
|
@@ -27513,7 +27548,7 @@ var BifrostCatalogClient = class {
|
|
|
27513
27548
|
return h;
|
|
27514
27549
|
}
|
|
27515
27550
|
async proxyRequest(method, path7, body = {}) {
|
|
27516
|
-
const response = await this.fetchFn(
|
|
27551
|
+
const response = await this.fetchFn(this.bifrostProxyUrl, {
|
|
27517
27552
|
method: "POST",
|
|
27518
27553
|
headers: this.headers(),
|
|
27519
27554
|
body: JSON.stringify({
|
|
@@ -27538,7 +27573,7 @@ var BifrostCatalogClient = class {
|
|
|
27538
27573
|
return data;
|
|
27539
27574
|
}
|
|
27540
27575
|
async akitaProxyRequest(method, path7, body = {}) {
|
|
27541
|
-
const response = await this.fetchFn(
|
|
27576
|
+
const response = await this.fetchFn(this.bifrostProxyUrl, {
|
|
27542
27577
|
method: "POST",
|
|
27543
27578
|
headers: this.headers(),
|
|
27544
27579
|
body: JSON.stringify({
|
|
@@ -27560,16 +27595,23 @@ var BifrostCatalogClient = class {
|
|
|
27560
27595
|
async () => {
|
|
27561
27596
|
const allItems = [];
|
|
27562
27597
|
let cursor = null;
|
|
27563
|
-
|
|
27564
|
-
|
|
27598
|
+
const seenCursors = /* @__PURE__ */ new Set();
|
|
27599
|
+
for (let page = 0; page < MAX_DISCOVERED_SERVICE_PAGES; page += 1) {
|
|
27565
27600
|
const cursorParam = cursor ? `&cursor=${encodeURIComponent(cursor)}` : "";
|
|
27566
27601
|
const data = await this.proxyRequest(
|
|
27567
27602
|
"GET",
|
|
27568
27603
|
`/api/v1/onboarding/discovered-services?status=discovered${cursorParam}`
|
|
27569
27604
|
);
|
|
27570
27605
|
allItems.push(...data.items || []);
|
|
27571
|
-
|
|
27572
|
-
|
|
27606
|
+
if (data.total && allItems.length >= data.total) {
|
|
27607
|
+
break;
|
|
27608
|
+
}
|
|
27609
|
+
const nextCursor = data.nextCursor || null;
|
|
27610
|
+
if (!nextCursor || seenCursors.has(nextCursor)) {
|
|
27611
|
+
break;
|
|
27612
|
+
}
|
|
27613
|
+
seenCursors.add(nextCursor);
|
|
27614
|
+
cursor = nextCursor;
|
|
27573
27615
|
}
|
|
27574
27616
|
return allItems;
|
|
27575
27617
|
},
|
|
@@ -27616,8 +27658,7 @@ var BifrostCatalogClient = class {
|
|
|
27616
27658
|
const allServices = [];
|
|
27617
27659
|
let page = 1;
|
|
27618
27660
|
const pageSize = 100;
|
|
27619
|
-
let
|
|
27620
|
-
while (hasMore) {
|
|
27661
|
+
for (let pageCount = 0; pageCount < MAX_PROVIDER_SERVICE_PAGES; pageCount += 1) {
|
|
27621
27662
|
const result = await this.akitaProxyRequest(
|
|
27622
27663
|
"GET",
|
|
27623
27664
|
`/v2/api-catalog/services?status=discovered&populate_endpoints=false&populate_discovery_metadata=true&page=${page}&page_size=${pageSize}`
|
|
@@ -27625,7 +27666,12 @@ var BifrostCatalogClient = class {
|
|
|
27625
27666
|
if (!result.ok || !result.data) return null;
|
|
27626
27667
|
const services = result.data.services || [];
|
|
27627
27668
|
allServices.push(...services);
|
|
27628
|
-
|
|
27669
|
+
if (result.data.total && allServices.length >= result.data.total) {
|
|
27670
|
+
break;
|
|
27671
|
+
}
|
|
27672
|
+
if (services.length < pageSize) {
|
|
27673
|
+
break;
|
|
27674
|
+
}
|
|
27629
27675
|
page++;
|
|
27630
27676
|
}
|
|
27631
27677
|
const fullName = clusterName ? `${clusterName}/${projectName}` : projectName;
|
|
@@ -27662,12 +27708,12 @@ var BifrostCatalogClient = class {
|
|
|
27662
27708
|
}
|
|
27663
27709
|
async createApplication(workspaceId, systemEnv) {
|
|
27664
27710
|
const response = await this.fetchFn(
|
|
27665
|
-
|
|
27711
|
+
`${this.observabilityBaseUrl}/v2/agent/api-catalog/workspaces/${workspaceId}/applications`,
|
|
27666
27712
|
{
|
|
27667
27713
|
method: "POST",
|
|
27668
27714
|
headers: {
|
|
27669
27715
|
"x-api-key": this.apiKey,
|
|
27670
|
-
"x-postman-env":
|
|
27716
|
+
"x-postman-env": this.observabilityEnv,
|
|
27671
27717
|
"Content-Type": "application/json"
|
|
27672
27718
|
},
|
|
27673
27719
|
body: JSON.stringify({ system_env: systemEnv })
|
|
@@ -27691,7 +27737,7 @@ var BifrostCatalogClient = class {
|
|
|
27691
27737
|
return result.data.team_verification_token || null;
|
|
27692
27738
|
}
|
|
27693
27739
|
async createApiKey(name) {
|
|
27694
|
-
const response = await this.fetchFn(
|
|
27740
|
+
const response = await this.fetchFn(this.bifrostProxyUrl, {
|
|
27695
27741
|
method: "POST",
|
|
27696
27742
|
headers: this.headers(),
|
|
27697
27743
|
body: JSON.stringify({
|
|
@@ -27731,8 +27777,15 @@ var POLL_TIMEOUT_DEFAULT = 120;
|
|
|
27731
27777
|
var POLL_INTERVAL_MIN = 2;
|
|
27732
27778
|
var POLL_INTERVAL_MAX = 60;
|
|
27733
27779
|
var POLL_INTERVAL_DEFAULT = 10;
|
|
27734
|
-
|
|
27735
|
-
|
|
27780
|
+
var PROD_ENDPOINTS = resolvePostmanEndpointProfile("prod");
|
|
27781
|
+
var DEFAULT_POSTMAN_API_BASE = PROD_ENDPOINTS.apiBaseUrl;
|
|
27782
|
+
var DEFAULT_POSTMAN_BIFROST_BASE = PROD_ENDPOINTS.bifrostBaseUrl;
|
|
27783
|
+
var DEFAULT_POSTMAN_OBSERVABILITY_BASE = PROD_ENDPOINTS.observabilityBaseUrl;
|
|
27784
|
+
function trimTrailingSlash(value) {
|
|
27785
|
+
return value.replace(/\/+$/, "");
|
|
27786
|
+
}
|
|
27787
|
+
async function validateApiKey(apiKey, apiBase = DEFAULT_POSTMAN_API_BASE) {
|
|
27788
|
+
const res = await fetch(`${trimTrailingSlash(apiBase)}/me`, {
|
|
27736
27789
|
method: "GET",
|
|
27737
27790
|
headers: { "x-api-key": apiKey }
|
|
27738
27791
|
});
|
|
@@ -27746,6 +27799,23 @@ async function validateApiKey(apiKey) {
|
|
|
27746
27799
|
const teamId = data?.user?.teamId ? String(data.user.teamId) : void 0;
|
|
27747
27800
|
return { valid: true, teamId };
|
|
27748
27801
|
}
|
|
27802
|
+
async function getTeams(apiKey, apiBase = DEFAULT_POSTMAN_API_BASE) {
|
|
27803
|
+
try {
|
|
27804
|
+
const res = await fetch(`${trimTrailingSlash(apiBase)}/teams`, {
|
|
27805
|
+
method: "GET",
|
|
27806
|
+
headers: { "x-api-key": apiKey }
|
|
27807
|
+
});
|
|
27808
|
+
if (!res.ok) return [];
|
|
27809
|
+
const data = await res.json();
|
|
27810
|
+
return (data?.data ?? []).filter((t) => t?.id && t?.name).map((t) => ({
|
|
27811
|
+
id: Number(t.id),
|
|
27812
|
+
name: String(t.name),
|
|
27813
|
+
...t.organizationId != null ? { organizationId: Number(t.organizationId) } : {}
|
|
27814
|
+
}));
|
|
27815
|
+
} catch {
|
|
27816
|
+
return [];
|
|
27817
|
+
}
|
|
27818
|
+
}
|
|
27749
27819
|
function clamp(value, min, max, fallback) {
|
|
27750
27820
|
const parsed = Number.isFinite(value) ? value : fallback;
|
|
27751
27821
|
return Math.min(max, Math.max(min, parsed));
|
|
@@ -27758,33 +27828,42 @@ function resolveInputs(env = process.env) {
|
|
|
27758
27828
|
if (!postmanAccessToken) throw new Error("postman-access-token is required");
|
|
27759
27829
|
const postmanApiKey = get("postman-api-key");
|
|
27760
27830
|
const postmanTeamId = get("postman-team-id") || env.POSTMAN_TEAM_ID?.trim() || "";
|
|
27761
|
-
const workspaceId = get("workspace-id");
|
|
27762
|
-
if (!workspaceId)
|
|
27763
|
-
|
|
27764
|
-
|
|
27765
|
-
|
|
27766
|
-
|
|
27767
|
-
const
|
|
27768
|
-
|
|
27831
|
+
const workspaceId = get("workspace-id") || env.POSTMAN_WORKSPACE_ID?.trim() || "";
|
|
27832
|
+
if (!workspaceId) {
|
|
27833
|
+
throw new Error(
|
|
27834
|
+
"workspace-id is required. Provide it as an input, or set the POSTMAN_WORKSPACE_ID environment variable."
|
|
27835
|
+
);
|
|
27836
|
+
}
|
|
27837
|
+
const environmentId = get("environment-id") || env.POSTMAN_ENVIRONMENT_ID?.trim() || "";
|
|
27838
|
+
if (!environmentId) {
|
|
27839
|
+
throw new Error(
|
|
27840
|
+
"environment-id is required. Provide it as an input, or set the POSTMAN_ENVIRONMENT_ID environment variable."
|
|
27841
|
+
);
|
|
27842
|
+
}
|
|
27769
27843
|
const detectedRepoUrl = (env.GITHUB_SERVER_URL && env.GITHUB_REPOSITORY ? `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}` : "") || env.CI_PROJECT_URL || env.BITBUCKET_GIT_HTTP_ORIGIN || env.BUILD_REPOSITORY_URI || "";
|
|
27770
|
-
const repoUrl = get("repo-url", detectedRepoUrl
|
|
27844
|
+
const repoUrl = get("repo-url", detectedRepoUrl);
|
|
27771
27845
|
const rawTimeout = parseInt(get("poll-timeout-seconds", String(POLL_TIMEOUT_DEFAULT)), 10);
|
|
27772
27846
|
const rawInterval = parseInt(get("poll-interval-seconds", String(POLL_INTERVAL_DEFAULT)), 10);
|
|
27847
|
+
const postmanStack = parsePostmanStack(get("postman-stack"));
|
|
27848
|
+
const endpointProfile = resolvePostmanEndpointProfile(postmanStack);
|
|
27773
27849
|
return {
|
|
27774
27850
|
projectName,
|
|
27775
27851
|
workspaceId,
|
|
27776
27852
|
environmentId,
|
|
27777
27853
|
systemEnvironmentId: get("system-environment-id", ""),
|
|
27778
27854
|
clusterName: get("cluster-name", ""),
|
|
27779
|
-
gitOwner,
|
|
27780
|
-
gitRepositoryName,
|
|
27781
27855
|
repoUrl,
|
|
27782
27856
|
postmanAccessToken,
|
|
27783
27857
|
postmanApiKey,
|
|
27784
27858
|
postmanTeamId,
|
|
27785
27859
|
githubToken: get("github-token", env.GITHUB_TOKEN || ""),
|
|
27786
27860
|
pollTimeoutSeconds: clamp(rawTimeout, POLL_TIMEOUT_MIN, POLL_TIMEOUT_MAX, POLL_TIMEOUT_DEFAULT),
|
|
27787
|
-
pollIntervalSeconds: clamp(rawInterval, POLL_INTERVAL_MIN, POLL_INTERVAL_MAX, POLL_INTERVAL_DEFAULT)
|
|
27861
|
+
pollIntervalSeconds: clamp(rawInterval, POLL_INTERVAL_MIN, POLL_INTERVAL_MAX, POLL_INTERVAL_DEFAULT),
|
|
27862
|
+
postmanStack,
|
|
27863
|
+
postmanApiBase: endpointProfile.apiBaseUrl,
|
|
27864
|
+
postmanBifrostBase: endpointProfile.bifrostBaseUrl,
|
|
27865
|
+
postmanObservabilityBase: endpointProfile.observabilityBaseUrl,
|
|
27866
|
+
postmanObservabilityEnv: endpointProfile.observabilityEnv
|
|
27788
27867
|
};
|
|
27789
27868
|
}
|
|
27790
27869
|
function createPlannedOutputs(inputs) {
|
|
@@ -27888,15 +27967,12 @@ async function resolveApiKeyAndTeamId(inputs, client, reporter = core_exports) {
|
|
|
27888
27967
|
let apiKey = inputs.postmanApiKey;
|
|
27889
27968
|
const teamId = inputs.postmanTeamId;
|
|
27890
27969
|
let keyValid = false;
|
|
27970
|
+
const apiBase = inputs.postmanApiBase || DEFAULT_POSTMAN_API_BASE;
|
|
27891
27971
|
if (apiKey) {
|
|
27892
|
-
|
|
27893
|
-
|
|
27894
|
-
|
|
27895
|
-
|
|
27896
|
-
reporter.warning("Provided postman-api-key is invalid or expired.");
|
|
27897
|
-
}
|
|
27898
|
-
} catch (error2) {
|
|
27899
|
-
throw error2;
|
|
27972
|
+
const result = await validateApiKey(apiKey, apiBase);
|
|
27973
|
+
keyValid = result.valid;
|
|
27974
|
+
if (!keyValid) {
|
|
27975
|
+
reporter.warning("Provided postman-api-key is invalid or expired.");
|
|
27900
27976
|
}
|
|
27901
27977
|
}
|
|
27902
27978
|
if (!keyValid) {
|
|
@@ -27907,12 +27983,42 @@ async function resolveApiKeyAndTeamId(inputs, client, reporter = core_exports) {
|
|
|
27907
27983
|
client.setApiKey(apiKey);
|
|
27908
27984
|
reporter.info("New API key created successfully.");
|
|
27909
27985
|
}
|
|
27910
|
-
|
|
27911
|
-
|
|
27986
|
+
let resolvedTeamId = teamId;
|
|
27987
|
+
if (!resolvedTeamId && apiKey) {
|
|
27988
|
+
try {
|
|
27989
|
+
const teams = await getTeams(apiKey, apiBase);
|
|
27990
|
+
if (teams.length > 1 && teams.every((t) => t.organizationId == null)) {
|
|
27991
|
+
reporter.warning(
|
|
27992
|
+
"GET /teams returned multiple teams but none include organizationId. Org-mode auto-detection may be degraded due to an upstream API change. Set postman-team-id explicitly if Bifrost calls fail."
|
|
27993
|
+
);
|
|
27994
|
+
}
|
|
27995
|
+
const isOrgMode = teams.some((t) => t.organizationId != null);
|
|
27996
|
+
if (isOrgMode) {
|
|
27997
|
+
if (teams.length === 1) {
|
|
27998
|
+
resolvedTeamId = String(teams[0].id);
|
|
27999
|
+
reporter.info(
|
|
28000
|
+
`Org-mode account detected. Using sub-team ${teams[0].id} (${teams[0].name ?? "unknown"}) for Bifrost calls.`
|
|
28001
|
+
);
|
|
28002
|
+
} else {
|
|
28003
|
+
const meResult = await validateApiKey(apiKey, apiBase);
|
|
28004
|
+
const meTeamId = meResult.teamId ? parseInt(meResult.teamId, 10) : NaN;
|
|
28005
|
+
if (!Number.isNaN(meTeamId) && teams.some((t) => t.id === meTeamId)) {
|
|
28006
|
+
resolvedTeamId = String(meTeamId);
|
|
28007
|
+
reporter.info(
|
|
28008
|
+
`Org-mode account detected. Using sub-team ${meTeamId} (from /me) for Bifrost calls.`
|
|
28009
|
+
);
|
|
28010
|
+
}
|
|
28011
|
+
}
|
|
28012
|
+
}
|
|
28013
|
+
} catch {
|
|
28014
|
+
}
|
|
28015
|
+
}
|
|
28016
|
+
if (resolvedTeamId) {
|
|
28017
|
+
reporter.info(`Using postman-team-id for Bifrost headers: ${resolvedTeamId}`);
|
|
27912
28018
|
} else {
|
|
27913
|
-
reporter.info("No
|
|
28019
|
+
reporter.info("No postman-team-id resolved; omitting x-entity-team-id so Bifrost resolves team from the access token.");
|
|
27914
28020
|
}
|
|
27915
|
-
return { apiKey, teamId };
|
|
28021
|
+
return { apiKey, teamId: resolvedTeamId };
|
|
27916
28022
|
}
|
|
27917
28023
|
async function runAction() {
|
|
27918
28024
|
const inputs = resolveInputs();
|
|
@@ -27920,11 +28026,6 @@ async function runAction() {
|
|
|
27920
28026
|
for (const [key, value] of Object.entries(planned)) {
|
|
27921
28027
|
setOutput(key, value);
|
|
27922
28028
|
}
|
|
27923
|
-
const maskSecret = createSecretMasker([
|
|
27924
|
-
inputs.postmanAccessToken,
|
|
27925
|
-
inputs.postmanApiKey,
|
|
27926
|
-
inputs.githubToken
|
|
27927
|
-
].filter(Boolean));
|
|
27928
28029
|
setSecret(inputs.postmanAccessToken);
|
|
27929
28030
|
if (inputs.postmanApiKey) setSecret(inputs.postmanApiKey);
|
|
27930
28031
|
if (inputs.githubToken) setSecret(inputs.githubToken);
|
|
@@ -27932,14 +28033,18 @@ async function runAction() {
|
|
|
27932
28033
|
accessToken: inputs.postmanAccessToken,
|
|
27933
28034
|
teamId: inputs.postmanTeamId,
|
|
27934
28035
|
apiKey: inputs.postmanApiKey,
|
|
27935
|
-
|
|
28036
|
+
bifrostBaseUrl: inputs.postmanBifrostBase,
|
|
28037
|
+
observabilityBaseUrl: inputs.postmanObservabilityBase,
|
|
28038
|
+
observabilityEnv: inputs.postmanObservabilityEnv
|
|
27936
28039
|
});
|
|
27937
28040
|
const { apiKey, teamId } = await resolveApiKeyAndTeamId(inputs, preliminaryClient, core_exports);
|
|
27938
28041
|
const client = new BifrostCatalogClient({
|
|
27939
28042
|
accessToken: inputs.postmanAccessToken,
|
|
27940
28043
|
teamId,
|
|
27941
28044
|
apiKey,
|
|
27942
|
-
|
|
28045
|
+
bifrostBaseUrl: inputs.postmanBifrostBase,
|
|
28046
|
+
observabilityBaseUrl: inputs.postmanObservabilityBase,
|
|
28047
|
+
observabilityEnv: inputs.postmanObservabilityEnv
|
|
27943
28048
|
});
|
|
27944
28049
|
let result;
|
|
27945
28050
|
try {
|
|
@@ -28014,15 +28119,14 @@ function parseCliArgs(argv, env = process.env) {
|
|
|
28014
28119
|
"environment-id",
|
|
28015
28120
|
"system-environment-id",
|
|
28016
28121
|
"cluster-name",
|
|
28017
|
-
"git-owner",
|
|
28018
|
-
"git-repository-name",
|
|
28019
28122
|
"repo-url",
|
|
28020
28123
|
"postman-access-token",
|
|
28021
28124
|
"postman-api-key",
|
|
28022
28125
|
"postman-team-id",
|
|
28023
28126
|
"github-token",
|
|
28024
28127
|
"poll-timeout-seconds",
|
|
28025
|
-
"poll-interval-seconds"
|
|
28128
|
+
"poll-interval-seconds",
|
|
28129
|
+
"postman-stack"
|
|
28026
28130
|
];
|
|
28027
28131
|
const inputEnv = { ...env };
|
|
28028
28132
|
for (const name of inputNames) {
|
|
@@ -28078,31 +28182,25 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
28078
28182
|
if (inputs.githubToken) {
|
|
28079
28183
|
reporter.setSecret(inputs.githubToken);
|
|
28080
28184
|
}
|
|
28081
|
-
const preliminaryMaskSecret = createSecretMasker([
|
|
28082
|
-
inputs.postmanAccessToken,
|
|
28083
|
-
inputs.postmanApiKey,
|
|
28084
|
-
inputs.githubToken
|
|
28085
|
-
]);
|
|
28086
28185
|
const preliminaryClient = new BifrostCatalogClient({
|
|
28087
28186
|
accessToken: inputs.postmanAccessToken,
|
|
28088
28187
|
teamId: inputs.postmanTeamId,
|
|
28089
28188
|
apiKey: inputs.postmanApiKey,
|
|
28090
|
-
|
|
28189
|
+
bifrostBaseUrl: inputs.postmanBifrostBase,
|
|
28190
|
+
observabilityBaseUrl: inputs.postmanObservabilityBase,
|
|
28191
|
+
observabilityEnv: inputs.postmanObservabilityEnv
|
|
28091
28192
|
});
|
|
28092
28193
|
const { apiKey, teamId } = await resolveApiKeyAndTeamId(inputs, preliminaryClient, reporter);
|
|
28093
28194
|
if (apiKey) {
|
|
28094
28195
|
reporter.setSecret(apiKey);
|
|
28095
28196
|
}
|
|
28096
|
-
const maskSecret = createSecretMasker([
|
|
28097
|
-
inputs.postmanAccessToken,
|
|
28098
|
-
inputs.githubToken,
|
|
28099
|
-
apiKey
|
|
28100
|
-
]);
|
|
28101
28197
|
const client = new BifrostCatalogClient({
|
|
28102
28198
|
accessToken: inputs.postmanAccessToken,
|
|
28103
28199
|
teamId,
|
|
28104
28200
|
apiKey,
|
|
28105
|
-
|
|
28201
|
+
bifrostBaseUrl: inputs.postmanBifrostBase,
|
|
28202
|
+
observabilityBaseUrl: inputs.postmanObservabilityBase,
|
|
28203
|
+
observabilityEnv: inputs.postmanObservabilityEnv
|
|
28106
28204
|
});
|
|
28107
28205
|
const result = await (runtime.executeOnboarding ?? runOnboarding)(
|
|
28108
28206
|
inputs,
|
package/dist/index.cjs
CHANGED
|
@@ -25012,9 +25012,11 @@ ${captureLines}` : capture.stack;
|
|
|
25012
25012
|
// src/index.ts
|
|
25013
25013
|
var index_exports = {};
|
|
25014
25014
|
__export(index_exports, {
|
|
25015
|
+
DEFAULT_POSTMAN_API_BASE: () => DEFAULT_POSTMAN_API_BASE,
|
|
25016
|
+
DEFAULT_POSTMAN_BIFROST_BASE: () => DEFAULT_POSTMAN_BIFROST_BASE,
|
|
25017
|
+
DEFAULT_POSTMAN_OBSERVABILITY_BASE: () => DEFAULT_POSTMAN_OBSERVABILITY_BASE,
|
|
25015
25018
|
createPlannedOutputs: () => createPlannedOutputs,
|
|
25016
|
-
|
|
25017
|
-
deriveTeamIdFromSession: () => deriveTeamIdFromSession,
|
|
25019
|
+
getTeams: () => getTeams,
|
|
25018
25020
|
resolveApiKeyAndTeamId: () => resolveApiKeyAndTeamId,
|
|
25019
25021
|
resolveInputs: () => resolveInputs,
|
|
25020
25022
|
runOnboarding: () => runOnboarding,
|
|
@@ -27347,9 +27349,6 @@ function redactSecrets(input, secretValues, replacement = REDACTED) {
|
|
|
27347
27349
|
return sanitized.split(secret).join(replacement);
|
|
27348
27350
|
}, source);
|
|
27349
27351
|
}
|
|
27350
|
-
function createSecretMasker(secretValues, replacement = REDACTED) {
|
|
27351
|
-
return (input) => redactSecrets(input, secretValues, replacement);
|
|
27352
|
-
}
|
|
27353
27352
|
function headerEntries(headers) {
|
|
27354
27353
|
if (headers instanceof Headers) {
|
|
27355
27354
|
return Array.from(headers.entries());
|
|
@@ -27476,20 +27475,58 @@ async function retry(operation, options = {}) {
|
|
|
27476
27475
|
throw new Error("Retry exhausted without returning or throwing");
|
|
27477
27476
|
}
|
|
27478
27477
|
|
|
27478
|
+
// src/lib/postman/base-urls.ts
|
|
27479
|
+
var POSTMAN_ENDPOINT_PROFILES = {
|
|
27480
|
+
prod: {
|
|
27481
|
+
apiBaseUrl: "https://api.getpostman.com",
|
|
27482
|
+
bifrostBaseUrl: "https://bifrost-premium-https-v4.gw.postman.com",
|
|
27483
|
+
observabilityBaseUrl: "https://api.observability.postman.com",
|
|
27484
|
+
observabilityEnv: "production"
|
|
27485
|
+
},
|
|
27486
|
+
beta: {
|
|
27487
|
+
apiBaseUrl: "https://api.getpostman-beta.com",
|
|
27488
|
+
bifrostBaseUrl: "https://bifrost-https-v4.gw.postman-beta.com",
|
|
27489
|
+
observabilityBaseUrl: "https://api.observability.postman-beta.com",
|
|
27490
|
+
observabilityEnv: "beta"
|
|
27491
|
+
}
|
|
27492
|
+
};
|
|
27493
|
+
function parsePostmanStack(value) {
|
|
27494
|
+
const normalized = String(value || "prod").trim().toLowerCase();
|
|
27495
|
+
if (normalized === "prod" || normalized === "beta") {
|
|
27496
|
+
return normalized;
|
|
27497
|
+
}
|
|
27498
|
+
throw new Error(`Unsupported postman-stack "${value}". Supported values: prod, beta`);
|
|
27499
|
+
}
|
|
27500
|
+
function resolvePostmanEndpointProfile(stack) {
|
|
27501
|
+
return POSTMAN_ENDPOINT_PROFILES[stack];
|
|
27502
|
+
}
|
|
27503
|
+
|
|
27479
27504
|
// src/lib/bifrost-client.ts
|
|
27480
|
-
var
|
|
27505
|
+
var DEFAULT_BIFROST_BASE_URL = POSTMAN_ENDPOINT_PROFILES.prod.bifrostBaseUrl;
|
|
27506
|
+
var BIFROST_PROXY_PATH = "/ws/proxy";
|
|
27507
|
+
var DEFAULT_OBSERVABILITY_BASE_URL = POSTMAN_ENDPOINT_PROFILES.prod.observabilityBaseUrl;
|
|
27508
|
+
var DEFAULT_OBSERVABILITY_ENV = POSTMAN_ENDPOINT_PROFILES.prod.observabilityEnv;
|
|
27509
|
+
var MAX_DISCOVERED_SERVICE_PAGES = 100;
|
|
27510
|
+
var MAX_PROVIDER_SERVICE_PAGES = 100;
|
|
27481
27511
|
var BifrostCatalogClient = class {
|
|
27482
27512
|
accessToken;
|
|
27483
27513
|
teamId;
|
|
27484
27514
|
apiKey;
|
|
27485
27515
|
fetchFn;
|
|
27486
27516
|
secretValues;
|
|
27517
|
+
bifrostProxyUrl;
|
|
27518
|
+
observabilityBaseUrl;
|
|
27519
|
+
observabilityEnv;
|
|
27487
27520
|
constructor(options) {
|
|
27488
27521
|
this.accessToken = options.accessToken;
|
|
27489
27522
|
this.teamId = options.teamId;
|
|
27490
27523
|
this.apiKey = options.apiKey;
|
|
27491
27524
|
this.fetchFn = options.fetchFn ?? globalThis.fetch;
|
|
27492
27525
|
this.secretValues = [options.accessToken, options.apiKey].filter(Boolean);
|
|
27526
|
+
const base = (options.bifrostBaseUrl || DEFAULT_BIFROST_BASE_URL).replace(/\/+$/, "");
|
|
27527
|
+
this.bifrostProxyUrl = `${base}${BIFROST_PROXY_PATH}`;
|
|
27528
|
+
this.observabilityBaseUrl = (options.observabilityBaseUrl || DEFAULT_OBSERVABILITY_BASE_URL).replace(/\/+$/, "");
|
|
27529
|
+
this.observabilityEnv = options.observabilityEnv || DEFAULT_OBSERVABILITY_ENV;
|
|
27493
27530
|
}
|
|
27494
27531
|
setApiKey(apiKey) {
|
|
27495
27532
|
this.apiKey = apiKey;
|
|
@@ -27513,7 +27550,7 @@ var BifrostCatalogClient = class {
|
|
|
27513
27550
|
return h;
|
|
27514
27551
|
}
|
|
27515
27552
|
async proxyRequest(method, path6, body = {}) {
|
|
27516
|
-
const response = await this.fetchFn(
|
|
27553
|
+
const response = await this.fetchFn(this.bifrostProxyUrl, {
|
|
27517
27554
|
method: "POST",
|
|
27518
27555
|
headers: this.headers(),
|
|
27519
27556
|
body: JSON.stringify({
|
|
@@ -27538,7 +27575,7 @@ var BifrostCatalogClient = class {
|
|
|
27538
27575
|
return data;
|
|
27539
27576
|
}
|
|
27540
27577
|
async akitaProxyRequest(method, path6, body = {}) {
|
|
27541
|
-
const response = await this.fetchFn(
|
|
27578
|
+
const response = await this.fetchFn(this.bifrostProxyUrl, {
|
|
27542
27579
|
method: "POST",
|
|
27543
27580
|
headers: this.headers(),
|
|
27544
27581
|
body: JSON.stringify({
|
|
@@ -27560,16 +27597,23 @@ var BifrostCatalogClient = class {
|
|
|
27560
27597
|
async () => {
|
|
27561
27598
|
const allItems = [];
|
|
27562
27599
|
let cursor = null;
|
|
27563
|
-
|
|
27564
|
-
|
|
27600
|
+
const seenCursors = /* @__PURE__ */ new Set();
|
|
27601
|
+
for (let page = 0; page < MAX_DISCOVERED_SERVICE_PAGES; page += 1) {
|
|
27565
27602
|
const cursorParam = cursor ? `&cursor=${encodeURIComponent(cursor)}` : "";
|
|
27566
27603
|
const data = await this.proxyRequest(
|
|
27567
27604
|
"GET",
|
|
27568
27605
|
`/api/v1/onboarding/discovered-services?status=discovered${cursorParam}`
|
|
27569
27606
|
);
|
|
27570
27607
|
allItems.push(...data.items || []);
|
|
27571
|
-
|
|
27572
|
-
|
|
27608
|
+
if (data.total && allItems.length >= data.total) {
|
|
27609
|
+
break;
|
|
27610
|
+
}
|
|
27611
|
+
const nextCursor = data.nextCursor || null;
|
|
27612
|
+
if (!nextCursor || seenCursors.has(nextCursor)) {
|
|
27613
|
+
break;
|
|
27614
|
+
}
|
|
27615
|
+
seenCursors.add(nextCursor);
|
|
27616
|
+
cursor = nextCursor;
|
|
27573
27617
|
}
|
|
27574
27618
|
return allItems;
|
|
27575
27619
|
},
|
|
@@ -27616,8 +27660,7 @@ var BifrostCatalogClient = class {
|
|
|
27616
27660
|
const allServices = [];
|
|
27617
27661
|
let page = 1;
|
|
27618
27662
|
const pageSize = 100;
|
|
27619
|
-
let
|
|
27620
|
-
while (hasMore) {
|
|
27663
|
+
for (let pageCount = 0; pageCount < MAX_PROVIDER_SERVICE_PAGES; pageCount += 1) {
|
|
27621
27664
|
const result = await this.akitaProxyRequest(
|
|
27622
27665
|
"GET",
|
|
27623
27666
|
`/v2/api-catalog/services?status=discovered&populate_endpoints=false&populate_discovery_metadata=true&page=${page}&page_size=${pageSize}`
|
|
@@ -27625,7 +27668,12 @@ var BifrostCatalogClient = class {
|
|
|
27625
27668
|
if (!result.ok || !result.data) return null;
|
|
27626
27669
|
const services = result.data.services || [];
|
|
27627
27670
|
allServices.push(...services);
|
|
27628
|
-
|
|
27671
|
+
if (result.data.total && allServices.length >= result.data.total) {
|
|
27672
|
+
break;
|
|
27673
|
+
}
|
|
27674
|
+
if (services.length < pageSize) {
|
|
27675
|
+
break;
|
|
27676
|
+
}
|
|
27629
27677
|
page++;
|
|
27630
27678
|
}
|
|
27631
27679
|
const fullName = clusterName ? `${clusterName}/${projectName}` : projectName;
|
|
@@ -27662,12 +27710,12 @@ var BifrostCatalogClient = class {
|
|
|
27662
27710
|
}
|
|
27663
27711
|
async createApplication(workspaceId, systemEnv) {
|
|
27664
27712
|
const response = await this.fetchFn(
|
|
27665
|
-
|
|
27713
|
+
`${this.observabilityBaseUrl}/v2/agent/api-catalog/workspaces/${workspaceId}/applications`,
|
|
27666
27714
|
{
|
|
27667
27715
|
method: "POST",
|
|
27668
27716
|
headers: {
|
|
27669
27717
|
"x-api-key": this.apiKey,
|
|
27670
|
-
"x-postman-env":
|
|
27718
|
+
"x-postman-env": this.observabilityEnv,
|
|
27671
27719
|
"Content-Type": "application/json"
|
|
27672
27720
|
},
|
|
27673
27721
|
body: JSON.stringify({ system_env: systemEnv })
|
|
@@ -27691,7 +27739,7 @@ var BifrostCatalogClient = class {
|
|
|
27691
27739
|
return result.data.team_verification_token || null;
|
|
27692
27740
|
}
|
|
27693
27741
|
async createApiKey(name) {
|
|
27694
|
-
const response = await this.fetchFn(
|
|
27742
|
+
const response = await this.fetchFn(this.bifrostProxyUrl, {
|
|
27695
27743
|
method: "POST",
|
|
27696
27744
|
headers: this.headers(),
|
|
27697
27745
|
body: JSON.stringify({
|
|
@@ -27731,21 +27779,15 @@ var POLL_TIMEOUT_DEFAULT = 120;
|
|
|
27731
27779
|
var POLL_INTERVAL_MIN = 2;
|
|
27732
27780
|
var POLL_INTERVAL_MAX = 60;
|
|
27733
27781
|
var POLL_INTERVAL_DEFAULT = 10;
|
|
27734
|
-
|
|
27735
|
-
|
|
27736
|
-
|
|
27737
|
-
|
|
27738
|
-
|
|
27739
|
-
|
|
27740
|
-
if (!res.ok) return void 0;
|
|
27741
|
-
const data = await res.json();
|
|
27742
|
-
if (data?.user?.teamId) return String(data.user.teamId);
|
|
27743
|
-
} catch {
|
|
27744
|
-
}
|
|
27745
|
-
return void 0;
|
|
27782
|
+
var PROD_ENDPOINTS = resolvePostmanEndpointProfile("prod");
|
|
27783
|
+
var DEFAULT_POSTMAN_API_BASE = PROD_ENDPOINTS.apiBaseUrl;
|
|
27784
|
+
var DEFAULT_POSTMAN_BIFROST_BASE = PROD_ENDPOINTS.bifrostBaseUrl;
|
|
27785
|
+
var DEFAULT_POSTMAN_OBSERVABILITY_BASE = PROD_ENDPOINTS.observabilityBaseUrl;
|
|
27786
|
+
function trimTrailingSlash(value) {
|
|
27787
|
+
return value.replace(/\/+$/, "");
|
|
27746
27788
|
}
|
|
27747
|
-
async function validateApiKey(apiKey) {
|
|
27748
|
-
const res = await fetch(
|
|
27789
|
+
async function validateApiKey(apiKey, apiBase = DEFAULT_POSTMAN_API_BASE) {
|
|
27790
|
+
const res = await fetch(`${trimTrailingSlash(apiBase)}/me`, {
|
|
27749
27791
|
method: "GET",
|
|
27750
27792
|
headers: { "x-api-key": apiKey }
|
|
27751
27793
|
});
|
|
@@ -27759,18 +27801,22 @@ async function validateApiKey(apiKey) {
|
|
|
27759
27801
|
const teamId = data?.user?.teamId ? String(data.user.teamId) : void 0;
|
|
27760
27802
|
return { valid: true, teamId };
|
|
27761
27803
|
}
|
|
27762
|
-
async function
|
|
27804
|
+
async function getTeams(apiKey, apiBase = DEFAULT_POSTMAN_API_BASE) {
|
|
27763
27805
|
try {
|
|
27764
|
-
const res = await fetch(
|
|
27806
|
+
const res = await fetch(`${trimTrailingSlash(apiBase)}/teams`, {
|
|
27765
27807
|
method: "GET",
|
|
27766
|
-
headers: { "x-
|
|
27808
|
+
headers: { "x-api-key": apiKey }
|
|
27767
27809
|
});
|
|
27768
|
-
if (!res.ok) return
|
|
27810
|
+
if (!res.ok) return [];
|
|
27769
27811
|
const data = await res.json();
|
|
27770
|
-
|
|
27812
|
+
return (data?.data ?? []).filter((t) => t?.id && t?.name).map((t) => ({
|
|
27813
|
+
id: Number(t.id),
|
|
27814
|
+
name: String(t.name),
|
|
27815
|
+
...t.organizationId != null ? { organizationId: Number(t.organizationId) } : {}
|
|
27816
|
+
}));
|
|
27771
27817
|
} catch {
|
|
27818
|
+
return [];
|
|
27772
27819
|
}
|
|
27773
|
-
return void 0;
|
|
27774
27820
|
}
|
|
27775
27821
|
function clamp(value, min, max, fallback) {
|
|
27776
27822
|
const parsed = Number.isFinite(value) ? value : fallback;
|
|
@@ -27784,33 +27830,42 @@ function resolveInputs(env = process.env) {
|
|
|
27784
27830
|
if (!postmanAccessToken) throw new Error("postman-access-token is required");
|
|
27785
27831
|
const postmanApiKey = get("postman-api-key");
|
|
27786
27832
|
const postmanTeamId = get("postman-team-id") || env.POSTMAN_TEAM_ID?.trim() || "";
|
|
27787
|
-
const workspaceId = get("workspace-id");
|
|
27788
|
-
if (!workspaceId)
|
|
27789
|
-
|
|
27790
|
-
|
|
27791
|
-
|
|
27792
|
-
|
|
27793
|
-
const
|
|
27794
|
-
|
|
27833
|
+
const workspaceId = get("workspace-id") || env.POSTMAN_WORKSPACE_ID?.trim() || "";
|
|
27834
|
+
if (!workspaceId) {
|
|
27835
|
+
throw new Error(
|
|
27836
|
+
"workspace-id is required. Provide it as an input, or set the POSTMAN_WORKSPACE_ID environment variable."
|
|
27837
|
+
);
|
|
27838
|
+
}
|
|
27839
|
+
const environmentId = get("environment-id") || env.POSTMAN_ENVIRONMENT_ID?.trim() || "";
|
|
27840
|
+
if (!environmentId) {
|
|
27841
|
+
throw new Error(
|
|
27842
|
+
"environment-id is required. Provide it as an input, or set the POSTMAN_ENVIRONMENT_ID environment variable."
|
|
27843
|
+
);
|
|
27844
|
+
}
|
|
27795
27845
|
const detectedRepoUrl = (env.GITHUB_SERVER_URL && env.GITHUB_REPOSITORY ? `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}` : "") || env.CI_PROJECT_URL || env.BITBUCKET_GIT_HTTP_ORIGIN || env.BUILD_REPOSITORY_URI || "";
|
|
27796
|
-
const repoUrl = get("repo-url", detectedRepoUrl
|
|
27846
|
+
const repoUrl = get("repo-url", detectedRepoUrl);
|
|
27797
27847
|
const rawTimeout = parseInt(get("poll-timeout-seconds", String(POLL_TIMEOUT_DEFAULT)), 10);
|
|
27798
27848
|
const rawInterval = parseInt(get("poll-interval-seconds", String(POLL_INTERVAL_DEFAULT)), 10);
|
|
27849
|
+
const postmanStack = parsePostmanStack(get("postman-stack"));
|
|
27850
|
+
const endpointProfile = resolvePostmanEndpointProfile(postmanStack);
|
|
27799
27851
|
return {
|
|
27800
27852
|
projectName,
|
|
27801
27853
|
workspaceId,
|
|
27802
27854
|
environmentId,
|
|
27803
27855
|
systemEnvironmentId: get("system-environment-id", ""),
|
|
27804
27856
|
clusterName: get("cluster-name", ""),
|
|
27805
|
-
gitOwner,
|
|
27806
|
-
gitRepositoryName,
|
|
27807
27857
|
repoUrl,
|
|
27808
27858
|
postmanAccessToken,
|
|
27809
27859
|
postmanApiKey,
|
|
27810
27860
|
postmanTeamId,
|
|
27811
27861
|
githubToken: get("github-token", env.GITHUB_TOKEN || ""),
|
|
27812
27862
|
pollTimeoutSeconds: clamp(rawTimeout, POLL_TIMEOUT_MIN, POLL_TIMEOUT_MAX, POLL_TIMEOUT_DEFAULT),
|
|
27813
|
-
pollIntervalSeconds: clamp(rawInterval, POLL_INTERVAL_MIN, POLL_INTERVAL_MAX, POLL_INTERVAL_DEFAULT)
|
|
27863
|
+
pollIntervalSeconds: clamp(rawInterval, POLL_INTERVAL_MIN, POLL_INTERVAL_MAX, POLL_INTERVAL_DEFAULT),
|
|
27864
|
+
postmanStack,
|
|
27865
|
+
postmanApiBase: endpointProfile.apiBaseUrl,
|
|
27866
|
+
postmanBifrostBase: endpointProfile.bifrostBaseUrl,
|
|
27867
|
+
postmanObservabilityBase: endpointProfile.observabilityBaseUrl,
|
|
27868
|
+
postmanObservabilityEnv: endpointProfile.observabilityEnv
|
|
27814
27869
|
};
|
|
27815
27870
|
}
|
|
27816
27871
|
function createPlannedOutputs(inputs) {
|
|
@@ -27914,15 +27969,12 @@ async function resolveApiKeyAndTeamId(inputs, client, reporter = core_exports) {
|
|
|
27914
27969
|
let apiKey = inputs.postmanApiKey;
|
|
27915
27970
|
const teamId = inputs.postmanTeamId;
|
|
27916
27971
|
let keyValid = false;
|
|
27972
|
+
const apiBase = inputs.postmanApiBase || DEFAULT_POSTMAN_API_BASE;
|
|
27917
27973
|
if (apiKey) {
|
|
27918
|
-
|
|
27919
|
-
|
|
27920
|
-
|
|
27921
|
-
|
|
27922
|
-
reporter.warning("Provided postman-api-key is invalid or expired.");
|
|
27923
|
-
}
|
|
27924
|
-
} catch (error2) {
|
|
27925
|
-
throw error2;
|
|
27974
|
+
const result = await validateApiKey(apiKey, apiBase);
|
|
27975
|
+
keyValid = result.valid;
|
|
27976
|
+
if (!keyValid) {
|
|
27977
|
+
reporter.warning("Provided postman-api-key is invalid or expired.");
|
|
27926
27978
|
}
|
|
27927
27979
|
}
|
|
27928
27980
|
if (!keyValid) {
|
|
@@ -27933,12 +27985,42 @@ async function resolveApiKeyAndTeamId(inputs, client, reporter = core_exports) {
|
|
|
27933
27985
|
client.setApiKey(apiKey);
|
|
27934
27986
|
reporter.info("New API key created successfully.");
|
|
27935
27987
|
}
|
|
27936
|
-
|
|
27937
|
-
|
|
27988
|
+
let resolvedTeamId = teamId;
|
|
27989
|
+
if (!resolvedTeamId && apiKey) {
|
|
27990
|
+
try {
|
|
27991
|
+
const teams = await getTeams(apiKey, apiBase);
|
|
27992
|
+
if (teams.length > 1 && teams.every((t) => t.organizationId == null)) {
|
|
27993
|
+
reporter.warning(
|
|
27994
|
+
"GET /teams returned multiple teams but none include organizationId. Org-mode auto-detection may be degraded due to an upstream API change. Set postman-team-id explicitly if Bifrost calls fail."
|
|
27995
|
+
);
|
|
27996
|
+
}
|
|
27997
|
+
const isOrgMode = teams.some((t) => t.organizationId != null);
|
|
27998
|
+
if (isOrgMode) {
|
|
27999
|
+
if (teams.length === 1) {
|
|
28000
|
+
resolvedTeamId = String(teams[0].id);
|
|
28001
|
+
reporter.info(
|
|
28002
|
+
`Org-mode account detected. Using sub-team ${teams[0].id} (${teams[0].name ?? "unknown"}) for Bifrost calls.`
|
|
28003
|
+
);
|
|
28004
|
+
} else {
|
|
28005
|
+
const meResult = await validateApiKey(apiKey, apiBase);
|
|
28006
|
+
const meTeamId = meResult.teamId ? parseInt(meResult.teamId, 10) : NaN;
|
|
28007
|
+
if (!Number.isNaN(meTeamId) && teams.some((t) => t.id === meTeamId)) {
|
|
28008
|
+
resolvedTeamId = String(meTeamId);
|
|
28009
|
+
reporter.info(
|
|
28010
|
+
`Org-mode account detected. Using sub-team ${meTeamId} (from /me) for Bifrost calls.`
|
|
28011
|
+
);
|
|
28012
|
+
}
|
|
28013
|
+
}
|
|
28014
|
+
}
|
|
28015
|
+
} catch {
|
|
28016
|
+
}
|
|
28017
|
+
}
|
|
28018
|
+
if (resolvedTeamId) {
|
|
28019
|
+
reporter.info(`Using postman-team-id for Bifrost headers: ${resolvedTeamId}`);
|
|
27938
28020
|
} else {
|
|
27939
|
-
reporter.info("No
|
|
28021
|
+
reporter.info("No postman-team-id resolved; omitting x-entity-team-id so Bifrost resolves team from the access token.");
|
|
27940
28022
|
}
|
|
27941
|
-
return { apiKey, teamId };
|
|
28023
|
+
return { apiKey, teamId: resolvedTeamId };
|
|
27942
28024
|
}
|
|
27943
28025
|
async function runAction() {
|
|
27944
28026
|
const inputs = resolveInputs();
|
|
@@ -27946,11 +28028,6 @@ async function runAction() {
|
|
|
27946
28028
|
for (const [key, value] of Object.entries(planned)) {
|
|
27947
28029
|
setOutput(key, value);
|
|
27948
28030
|
}
|
|
27949
|
-
const maskSecret = createSecretMasker([
|
|
27950
|
-
inputs.postmanAccessToken,
|
|
27951
|
-
inputs.postmanApiKey,
|
|
27952
|
-
inputs.githubToken
|
|
27953
|
-
].filter(Boolean));
|
|
27954
28031
|
setSecret(inputs.postmanAccessToken);
|
|
27955
28032
|
if (inputs.postmanApiKey) setSecret(inputs.postmanApiKey);
|
|
27956
28033
|
if (inputs.githubToken) setSecret(inputs.githubToken);
|
|
@@ -27958,14 +28035,18 @@ async function runAction() {
|
|
|
27958
28035
|
accessToken: inputs.postmanAccessToken,
|
|
27959
28036
|
teamId: inputs.postmanTeamId,
|
|
27960
28037
|
apiKey: inputs.postmanApiKey,
|
|
27961
|
-
|
|
28038
|
+
bifrostBaseUrl: inputs.postmanBifrostBase,
|
|
28039
|
+
observabilityBaseUrl: inputs.postmanObservabilityBase,
|
|
28040
|
+
observabilityEnv: inputs.postmanObservabilityEnv
|
|
27962
28041
|
});
|
|
27963
28042
|
const { apiKey, teamId } = await resolveApiKeyAndTeamId(inputs, preliminaryClient, core_exports);
|
|
27964
28043
|
const client = new BifrostCatalogClient({
|
|
27965
28044
|
accessToken: inputs.postmanAccessToken,
|
|
27966
28045
|
teamId,
|
|
27967
28046
|
apiKey,
|
|
27968
|
-
|
|
28047
|
+
bifrostBaseUrl: inputs.postmanBifrostBase,
|
|
28048
|
+
observabilityBaseUrl: inputs.postmanObservabilityBase,
|
|
28049
|
+
observabilityEnv: inputs.postmanObservabilityEnv
|
|
27969
28050
|
});
|
|
27970
28051
|
let result;
|
|
27971
28052
|
try {
|
|
@@ -27996,9 +28077,11 @@ runAction().catch((error2) => {
|
|
|
27996
28077
|
});
|
|
27997
28078
|
// Annotate the CommonJS export names for ESM import in node:
|
|
27998
28079
|
0 && (module.exports = {
|
|
28080
|
+
DEFAULT_POSTMAN_API_BASE,
|
|
28081
|
+
DEFAULT_POSTMAN_BIFROST_BASE,
|
|
28082
|
+
DEFAULT_POSTMAN_OBSERVABILITY_BASE,
|
|
27999
28083
|
createPlannedOutputs,
|
|
28000
|
-
|
|
28001
|
-
deriveTeamIdFromSession,
|
|
28084
|
+
getTeams,
|
|
28002
28085
|
resolveApiKeyAndTeamId,
|
|
28003
28086
|
resolveInputs,
|
|
28004
28087
|
runOnboarding,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@postman-cse/onboarding-insights",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "GitHub Action to onboard Postman Insights discovered services to API Catalog workspaces.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -11,9 +11,14 @@
|
|
|
11
11
|
"action.yml",
|
|
12
12
|
"dist"
|
|
13
13
|
],
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=24"
|
|
16
|
+
},
|
|
14
17
|
"scripts": {
|
|
15
|
-
"build": "rm -rf dist && esbuild src/index.ts --bundle --platform=node --target=
|
|
18
|
+
"build": "rm -rf dist && esbuild src/index.ts --bundle --platform=node --target=node24 --format=cjs --outfile=dist/index.cjs && esbuild src/cli.ts --bundle --platform=node --target=node24 --format=cjs --outfile=dist/cli.cjs",
|
|
16
19
|
"check:dist": "npm run build && git diff --exit-code -- dist",
|
|
20
|
+
"lint": "eslint .",
|
|
21
|
+
"lint:fix": "eslint . --fix",
|
|
17
22
|
"test": "vitest run",
|
|
18
23
|
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
19
24
|
},
|
|
@@ -31,12 +36,18 @@
|
|
|
31
36
|
"@actions/core": "^3.0.0"
|
|
32
37
|
},
|
|
33
38
|
"overrides": {
|
|
34
|
-
"undici": ">=6.24.0"
|
|
39
|
+
"undici": ">=6.24.0",
|
|
40
|
+
"picomatch": ">=4.0.4"
|
|
35
41
|
},
|
|
36
42
|
"devDependencies": {
|
|
43
|
+
"@commitlint/cli": "^20.5.0",
|
|
44
|
+
"@commitlint/config-conventional": "^20.5.0",
|
|
45
|
+
"@eslint/js": "^10.0.1",
|
|
37
46
|
"@types/node": "^25.3.5",
|
|
38
47
|
"esbuild": "^0.27.3",
|
|
48
|
+
"eslint": "^10.1.0",
|
|
39
49
|
"typescript": "^5.9.3",
|
|
50
|
+
"typescript-eslint": "^8.57.2",
|
|
40
51
|
"vitest": "^4.0.18",
|
|
41
52
|
"yaml": "^2.8.2"
|
|
42
53
|
},
|