@postman-cse/onboarding-insights 1.0.4 → 2.0.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 +15 -13
- package/action.yml +3 -3
- package/dist/action.cjs +269 -43
- package/dist/cli.cjs +361 -135
- package/dist/index.cjs +273 -43
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -16,6 +16,8 @@ Part of the [Postman API Onboarding suite](https://github.com/postman-cs/postman
|
|
|
16
16
|
|
|
17
17
|
This action does **not** deploy the Insights agent, create workspaces, create environments, upload OpenAPI specs, or sync repo artifacts. It only links a service that Insights has already discovered.
|
|
18
18
|
|
|
19
|
+
> **Credential requirement for the Insights linking steps.** The service discovery and git-link calls run on Postman's `api-catalog` service and accept a service-account access token. The Insights acknowledgment and application-binding steps run on the `akita` (Insights) service, which authenticates against a **Postman user identity** and answers `401 "Postman User not found"` for a service-account token. To complete the full link, supply a credential pair that carries a Postman user identity (a user's access token and user PMAK). A service-account-only pair from `postman-resolve-service-token-action` is sufficient for discovery and git linking but cannot finish the Insights acknowledgment.
|
|
20
|
+
|
|
19
21
|
## Which action should I use?
|
|
20
22
|
|
|
21
23
|
| Need | Action |
|
|
@@ -40,12 +42,12 @@ jobs:
|
|
|
40
42
|
# ... deploy your service to Kubernetes ...
|
|
41
43
|
|
|
42
44
|
- id: postman_token
|
|
43
|
-
uses: postman-cs/postman-resolve-service-token-action@
|
|
45
|
+
uses: postman-cs/postman-resolve-service-token-action@v2
|
|
44
46
|
with:
|
|
45
47
|
postman-api-key: ${{ secrets.POSTMAN_API_KEY }}
|
|
46
48
|
postman-region: us
|
|
47
49
|
|
|
48
|
-
- uses: postman-cs/postman-insights-onboarding-action@
|
|
50
|
+
- uses: postman-cs/postman-insights-onboarding-action@v2
|
|
49
51
|
with:
|
|
50
52
|
project-name: core-payments
|
|
51
53
|
workspace-id: ${{ vars.POSTMAN_WORKSPACE_ID }}
|
|
@@ -70,12 +72,12 @@ jobs:
|
|
|
70
72
|
# ... deploy your service to Kubernetes ...
|
|
71
73
|
|
|
72
74
|
- id: postman_token
|
|
73
|
-
uses: postman-cs/postman-resolve-service-token-action@
|
|
75
|
+
uses: postman-cs/postman-resolve-service-token-action@v2
|
|
74
76
|
with:
|
|
75
77
|
postman-api-key: ${{ secrets.POSTMAN_API_KEY }}
|
|
76
78
|
postman-region: us
|
|
77
79
|
|
|
78
|
-
- uses: postman-cs/postman-insights-onboarding-action@
|
|
80
|
+
- uses: postman-cs/postman-insights-onboarding-action@v2
|
|
79
81
|
with:
|
|
80
82
|
project-name: core-payments
|
|
81
83
|
workspace-id: ${{ vars.POSTMAN_WORKSPACE_ID }}
|
|
@@ -99,12 +101,12 @@ jobs:
|
|
|
99
101
|
- uses: actions/checkout@v5
|
|
100
102
|
|
|
101
103
|
- id: postman_token
|
|
102
|
-
uses: postman-cs/postman-resolve-service-token-action@
|
|
104
|
+
uses: postman-cs/postman-resolve-service-token-action@v2
|
|
103
105
|
with:
|
|
104
106
|
postman-api-key: ${{ secrets.POSTMAN_API_KEY }}
|
|
105
107
|
postman-region: us
|
|
106
108
|
|
|
107
|
-
- uses: postman-cs/postman-bootstrap-action@
|
|
109
|
+
- uses: postman-cs/postman-bootstrap-action@v2
|
|
108
110
|
id: bootstrap
|
|
109
111
|
with:
|
|
110
112
|
project-name: core-payments
|
|
@@ -115,7 +117,7 @@ jobs:
|
|
|
115
117
|
|
|
116
118
|
# ... deploy service to Kubernetes ...
|
|
117
119
|
|
|
118
|
-
- uses: postman-cs/postman-repo-sync-action@
|
|
120
|
+
- uses: postman-cs/postman-repo-sync-action@v2
|
|
119
121
|
id: sync
|
|
120
122
|
with:
|
|
121
123
|
project-name: core-payments
|
|
@@ -129,7 +131,7 @@ jobs:
|
|
|
129
131
|
postman-api-key: ${{ secrets.POSTMAN_API_KEY }}
|
|
130
132
|
postman-access-token: ${{ steps.postman_token.outputs.token }}
|
|
131
133
|
|
|
132
|
-
- uses: postman-cs/postman-insights-onboarding-action@
|
|
134
|
+
- uses: postman-cs/postman-insights-onboarding-action@v2
|
|
133
135
|
with:
|
|
134
136
|
project-name: core-payments
|
|
135
137
|
workspace-id: ${{ steps.bootstrap.outputs.workspace-id }}
|
|
@@ -147,7 +149,7 @@ jobs:
|
|
|
147
149
|
The Insights agent takes time to discover services after pods start. The action polls the [API Catalog](https://learning.postman.com/docs/api-catalog/connect/insights/) discovered-services list until the service appears or the timeout is reached. For services that take longer to appear (cold cluster, large pod startup time), raise the timeout:
|
|
148
150
|
|
|
149
151
|
```yaml
|
|
150
|
-
- uses: postman-cs/postman-insights-onboarding-action@
|
|
152
|
+
- uses: postman-cs/postman-insights-onboarding-action@v2
|
|
151
153
|
with:
|
|
152
154
|
project-name: core-payments
|
|
153
155
|
workspace-id: ${{ vars.POSTMAN_WORKSPACE_ID }}
|
|
@@ -165,7 +167,7 @@ The Insights agent takes time to discover services after pods start. The action
|
|
|
165
167
|
Before any onboarding write, the action can verify that `postman-api-key` and `postman-access-token` resolve to the same parent organization. The default `warn` mode logs a note and continues. Set `credential-preflight` to `enforce` to fail fast on mismatched credentials:
|
|
166
168
|
|
|
167
169
|
```yaml
|
|
168
|
-
- uses: postman-cs/postman-insights-onboarding-action@
|
|
170
|
+
- uses: postman-cs/postman-insights-onboarding-action@v2
|
|
169
171
|
with:
|
|
170
172
|
project-name: core-payments
|
|
171
173
|
workspace-id: ${{ vars.POSTMAN_WORKSPACE_ID }}
|
|
@@ -201,13 +203,13 @@ See [CLI Usage](docs/cli.md) for provider auto-detection, output formats, and Gi
|
|
|
201
203
|
<!-- inputs-table:start -->
|
|
202
204
|
| Name | Description | Required | Default |
|
|
203
205
|
| --- | --- | --- | --- |
|
|
204
|
-
| `project-name` | Service name or
|
|
206
|
+
| `project-name` | Service name or Jira/Xray project key to match against the final discovered-service segment | Yes | |
|
|
205
207
|
| `workspace-id` | Postman workspace ID to link the discovered service to | Yes | |
|
|
206
208
|
| `environment-id` | Postman environment UID for the onboarding association | Yes | |
|
|
207
209
|
| `system-environment-id` | Postman system environment UUID for service-level Insights acknowledgment | No | |
|
|
208
|
-
| `cluster-name` | Insights cluster name.
|
|
210
|
+
| `cluster-name` | Insights cluster name. When set, matches {cluster-name}/{project-name} exactly in discovered services | No | |
|
|
209
211
|
| `repo-url` | Repository URL for Git onboarding. Auto-detected from CI context when omitted. | No | |
|
|
210
|
-
| `postman-access-token` |
|
|
212
|
+
| `postman-access-token` | Postman access token (x-access-token) for the Bifrost linking calls. The api-catalog discovery and git-link steps accept a service-account token (mint it with postman-resolve-service-token-action); the Insights (akita) acknowledgment and application-binding steps require a token carrying a Postman user identity — a service-account token answers 401 "Postman User not found" there. | Yes | |
|
|
211
213
|
| `postman-team-id` | Explicit Postman team ID for org-mode integration request headers. When omitted, x-entity-team-id is not sent. | No | |
|
|
212
214
|
| `github-token` | Optional GitHub token passed as git_api_key when repository auth is required by onboarding/git | No | |
|
|
213
215
|
| `postman-api-key` | Service-account Postman API key (PMAK-*) for the application binding call. Auto-created from postman-access-token when omitted or invalid after a clear 401/403 validation failure. | No | |
|
package/action.yml
CHANGED
|
@@ -6,7 +6,7 @@ branding:
|
|
|
6
6
|
color: orange
|
|
7
7
|
inputs:
|
|
8
8
|
project-name:
|
|
9
|
-
description: 'Service name or
|
|
9
|
+
description: 'Service name or Jira/Xray project key to match against the final discovered-service segment'
|
|
10
10
|
required: true
|
|
11
11
|
workspace-id:
|
|
12
12
|
description: 'Postman workspace ID to link the discovered service to'
|
|
@@ -18,13 +18,13 @@ inputs:
|
|
|
18
18
|
description: 'Postman system environment UUID for service-level Insights acknowledgment'
|
|
19
19
|
required: false
|
|
20
20
|
cluster-name:
|
|
21
|
-
description: 'Insights cluster name.
|
|
21
|
+
description: 'Insights cluster name. When set, matches {cluster-name}/{project-name} exactly in discovered services'
|
|
22
22
|
required: false
|
|
23
23
|
repo-url:
|
|
24
24
|
description: 'Repository URL for Git onboarding. Auto-detected from CI context when omitted.'
|
|
25
25
|
required: false
|
|
26
26
|
postman-access-token:
|
|
27
|
-
description: '
|
|
27
|
+
description: 'Postman access token (x-access-token) for the Bifrost linking calls. The api-catalog discovery and git-link steps accept a service-account token (mint it with postman-resolve-service-token-action); the Insights (akita) acknowledgment and application-binding steps require a token carrying a Postman user identity — a service-account token answers 401 "Postman User not found" there.'
|
|
28
28
|
required: true
|
|
29
29
|
postman-team-id:
|
|
30
30
|
description: 'Explicit Postman team ID for org-mode integration request headers. When omitted, x-entity-team-id is not sent.'
|
package/dist/action.cjs
CHANGED
|
@@ -12166,7 +12166,7 @@ var require_response = __commonJS({
|
|
|
12166
12166
|
var assert = require("node:assert");
|
|
12167
12167
|
var { types } = require("node:util");
|
|
12168
12168
|
var textEncoder = new TextEncoder("utf-8");
|
|
12169
|
-
var
|
|
12169
|
+
var Response2 = class _Response {
|
|
12170
12170
|
// Creates network error Response.
|
|
12171
12171
|
static error() {
|
|
12172
12172
|
const responseObject = fromInnerResponse(makeNetworkError(), "immutable");
|
|
@@ -12309,8 +12309,8 @@ var require_response = __commonJS({
|
|
|
12309
12309
|
return `Response ${nodeUtil.formatWithOptions(options, properties)}`;
|
|
12310
12310
|
}
|
|
12311
12311
|
};
|
|
12312
|
-
mixinBody(
|
|
12313
|
-
Object.defineProperties(
|
|
12312
|
+
mixinBody(Response2);
|
|
12313
|
+
Object.defineProperties(Response2.prototype, {
|
|
12314
12314
|
type: kEnumerableProperty,
|
|
12315
12315
|
url: kEnumerableProperty,
|
|
12316
12316
|
status: kEnumerableProperty,
|
|
@@ -12326,7 +12326,7 @@ var require_response = __commonJS({
|
|
|
12326
12326
|
configurable: true
|
|
12327
12327
|
}
|
|
12328
12328
|
});
|
|
12329
|
-
Object.defineProperties(
|
|
12329
|
+
Object.defineProperties(Response2, {
|
|
12330
12330
|
json: kEnumerableProperty,
|
|
12331
12331
|
redirect: kEnumerableProperty,
|
|
12332
12332
|
error: kEnumerableProperty
|
|
@@ -12459,7 +12459,7 @@ var require_response = __commonJS({
|
|
|
12459
12459
|
}
|
|
12460
12460
|
}
|
|
12461
12461
|
function fromInnerResponse(innerResponse, guard) {
|
|
12462
|
-
const response = new
|
|
12462
|
+
const response = new Response2(kConstruct);
|
|
12463
12463
|
response[kState] = innerResponse;
|
|
12464
12464
|
response[kHeaders] = new Headers3(kConstruct);
|
|
12465
12465
|
setHeadersList(response[kHeaders], innerResponse.headersList);
|
|
@@ -12527,7 +12527,7 @@ var require_response = __commonJS({
|
|
|
12527
12527
|
makeResponse,
|
|
12528
12528
|
makeAppropriateNetworkError,
|
|
12529
12529
|
filterResponse,
|
|
12530
|
-
Response,
|
|
12530
|
+
Response: Response2,
|
|
12531
12531
|
cloneResponse,
|
|
12532
12532
|
fromInnerResponse
|
|
12533
12533
|
};
|
|
@@ -15199,7 +15199,7 @@ var require_cache = __commonJS({
|
|
|
15199
15199
|
var { urlEquals, getFieldValues } = require_util5();
|
|
15200
15200
|
var { kEnumerableProperty, isDisturbed } = require_util();
|
|
15201
15201
|
var { webidl } = require_webidl();
|
|
15202
|
-
var { Response, cloneResponse, fromInnerResponse } = require_response();
|
|
15202
|
+
var { Response: Response2, cloneResponse, fromInnerResponse } = require_response();
|
|
15203
15203
|
var { Request, fromInnerRequest } = require_request2();
|
|
15204
15204
|
var { kState } = require_symbols2();
|
|
15205
15205
|
var { fetching } = require_fetch();
|
|
@@ -15726,7 +15726,7 @@ var require_cache = __commonJS({
|
|
|
15726
15726
|
converter: webidl.converters.DOMString
|
|
15727
15727
|
}
|
|
15728
15728
|
]);
|
|
15729
|
-
webidl.converters.Response = webidl.interfaceConverter(
|
|
15729
|
+
webidl.converters.Response = webidl.interfaceConverter(Response2);
|
|
15730
15730
|
webidl.converters["sequence<RequestInfo>"] = webidl.sequenceConverter(
|
|
15731
15731
|
webidl.converters.RequestInfo
|
|
15732
15732
|
);
|
|
@@ -21481,6 +21481,117 @@ function resolvePostmanEndpointProfile(stack, region = "us") {
|
|
|
21481
21481
|
return profile;
|
|
21482
21482
|
}
|
|
21483
21483
|
|
|
21484
|
+
// src/lib/postman/token-provider.ts
|
|
21485
|
+
var MintError = class extends Error {
|
|
21486
|
+
permanent;
|
|
21487
|
+
constructor(message, permanent) {
|
|
21488
|
+
super(message);
|
|
21489
|
+
this.name = "MintError";
|
|
21490
|
+
this.permanent = permanent;
|
|
21491
|
+
}
|
|
21492
|
+
};
|
|
21493
|
+
function extractAccessToken(payload) {
|
|
21494
|
+
if (!payload || typeof payload !== "object") return void 0;
|
|
21495
|
+
const record = payload;
|
|
21496
|
+
const direct = record.access_token;
|
|
21497
|
+
if (typeof direct === "string" && direct.trim()) return direct.trim();
|
|
21498
|
+
const session = record.session;
|
|
21499
|
+
if (session && typeof session === "object") {
|
|
21500
|
+
const token = session.token;
|
|
21501
|
+
if (typeof token === "string" && token.trim()) return token.trim();
|
|
21502
|
+
}
|
|
21503
|
+
return void 0;
|
|
21504
|
+
}
|
|
21505
|
+
var AccessTokenProvider = class {
|
|
21506
|
+
token;
|
|
21507
|
+
apiKey;
|
|
21508
|
+
apiBaseUrl;
|
|
21509
|
+
fetchImpl;
|
|
21510
|
+
maxAttempts;
|
|
21511
|
+
onToken;
|
|
21512
|
+
sleep;
|
|
21513
|
+
inflight;
|
|
21514
|
+
constructor(options) {
|
|
21515
|
+
this.token = String(options.accessToken || "").trim();
|
|
21516
|
+
this.apiKey = String(options.apiKey || "").trim();
|
|
21517
|
+
this.apiBaseUrl = String(
|
|
21518
|
+
options.apiBaseUrl || POSTMAN_ENDPOINT_PROFILES.prod.apiBaseUrl
|
|
21519
|
+
).replace(/\/+$/, "");
|
|
21520
|
+
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
21521
|
+
this.maxAttempts = Math.max(1, options.maxAttempts ?? 2);
|
|
21522
|
+
this.onToken = options.onToken;
|
|
21523
|
+
this.sleep = options.sleep;
|
|
21524
|
+
}
|
|
21525
|
+
current() {
|
|
21526
|
+
return this.token;
|
|
21527
|
+
}
|
|
21528
|
+
/** True when a PMAK is present, so an expired token can be re-minted. */
|
|
21529
|
+
canRefresh() {
|
|
21530
|
+
return Boolean(this.apiKey);
|
|
21531
|
+
}
|
|
21532
|
+
refresh() {
|
|
21533
|
+
this.inflight ??= this.mintWithRetry().finally(() => {
|
|
21534
|
+
this.inflight = void 0;
|
|
21535
|
+
});
|
|
21536
|
+
return this.inflight;
|
|
21537
|
+
}
|
|
21538
|
+
async mintWithRetry() {
|
|
21539
|
+
if (!this.apiKey) {
|
|
21540
|
+
throw new Error(
|
|
21541
|
+
"postman: the access token expired and cannot be refreshed because no postman-api-key is present. Service-account access tokens expire after about 1 to 1.5 hours. Re-mint a fresh token (postman-resolve-service-token-action) and re-run."
|
|
21542
|
+
);
|
|
21543
|
+
}
|
|
21544
|
+
const token = await retry(() => this.mintOnce(), {
|
|
21545
|
+
maxAttempts: this.maxAttempts,
|
|
21546
|
+
delayMs: 1e3,
|
|
21547
|
+
backoffMultiplier: 2,
|
|
21548
|
+
...this.sleep ? { sleep: this.sleep } : {},
|
|
21549
|
+
shouldRetry: (error2) => !(error2 instanceof MintError && error2.permanent)
|
|
21550
|
+
});
|
|
21551
|
+
this.token = token;
|
|
21552
|
+
this.onToken?.(token);
|
|
21553
|
+
return token;
|
|
21554
|
+
}
|
|
21555
|
+
async mintOnce() {
|
|
21556
|
+
const response = await this.fetchImpl(`${this.apiBaseUrl}/service-account-tokens`, {
|
|
21557
|
+
method: "POST",
|
|
21558
|
+
headers: {
|
|
21559
|
+
"Content-Type": "application/json",
|
|
21560
|
+
"x-api-key": this.apiKey
|
|
21561
|
+
},
|
|
21562
|
+
body: JSON.stringify({ apiKey: this.apiKey })
|
|
21563
|
+
});
|
|
21564
|
+
const body = await response.text().catch(() => "");
|
|
21565
|
+
if (!response.ok) {
|
|
21566
|
+
const status = response.status;
|
|
21567
|
+
if (status === 401 || status === 403) {
|
|
21568
|
+
throw new MintError(
|
|
21569
|
+
`postman: re-mint failed because the postman-api-key was rejected (PMAK rejected, HTTP ${status}); confirm it is a valid, enabled service-account PMAK for the intended team.`,
|
|
21570
|
+
true
|
|
21571
|
+
);
|
|
21572
|
+
}
|
|
21573
|
+
if (status === 400 && body.toLowerCase().includes("service accounts not enabled")) {
|
|
21574
|
+
throw new MintError(
|
|
21575
|
+
"postman: re-mint failed because service accounts are not enabled for this team; enable them in Team Settings or use a team where they are.",
|
|
21576
|
+
true
|
|
21577
|
+
);
|
|
21578
|
+
}
|
|
21579
|
+
throw new MintError(`postman: re-mint failed (service-account-tokens HTTP ${status}).`, false);
|
|
21580
|
+
}
|
|
21581
|
+
let parsed;
|
|
21582
|
+
try {
|
|
21583
|
+
parsed = JSON.parse(body);
|
|
21584
|
+
} catch {
|
|
21585
|
+
parsed = void 0;
|
|
21586
|
+
}
|
|
21587
|
+
const token = extractAccessToken(parsed);
|
|
21588
|
+
if (!token) {
|
|
21589
|
+
throw new MintError("postman: re-mint succeeded but no access token was returned.", false);
|
|
21590
|
+
}
|
|
21591
|
+
return token;
|
|
21592
|
+
}
|
|
21593
|
+
};
|
|
21594
|
+
|
|
21484
21595
|
// src/lib/bifrost-client.ts
|
|
21485
21596
|
var DEFAULT_BIFROST_BASE_URL = POSTMAN_ENDPOINT_PROFILES.prod.bifrostBaseUrl;
|
|
21486
21597
|
var BIFROST_PROXY_PATH = "/ws/proxy";
|
|
@@ -21488,8 +21599,11 @@ var DEFAULT_OBSERVABILITY_BASE_URL = POSTMAN_ENDPOINT_PROFILES.prod.observabilit
|
|
|
21488
21599
|
var DEFAULT_OBSERVABILITY_ENV = POSTMAN_ENDPOINT_PROFILES.prod.observabilityEnv;
|
|
21489
21600
|
var MAX_DISCOVERED_SERVICE_PAGES = 100;
|
|
21490
21601
|
var MAX_PROVIDER_SERVICE_PAGES = 100;
|
|
21602
|
+
function isExpiredAuthError(status, body) {
|
|
21603
|
+
return status === 401 || body.includes("UNAUTHENTICATED") || body.includes("authenticationError");
|
|
21604
|
+
}
|
|
21491
21605
|
var BifrostCatalogClient = class {
|
|
21492
|
-
|
|
21606
|
+
tokenProvider;
|
|
21493
21607
|
teamId;
|
|
21494
21608
|
apiKey;
|
|
21495
21609
|
fetchFn;
|
|
@@ -21498,11 +21612,14 @@ var BifrostCatalogClient = class {
|
|
|
21498
21612
|
observabilityBaseUrl;
|
|
21499
21613
|
observabilityEnv;
|
|
21500
21614
|
constructor(options) {
|
|
21501
|
-
this.
|
|
21615
|
+
this.tokenProvider = options.tokenProvider ?? new AccessTokenProvider({
|
|
21616
|
+
accessToken: options.accessToken,
|
|
21617
|
+
apiKey: options.apiKey
|
|
21618
|
+
});
|
|
21502
21619
|
this.teamId = options.teamId;
|
|
21503
21620
|
this.apiKey = options.apiKey;
|
|
21504
21621
|
this.fetchFn = options.fetchFn ?? globalThis.fetch;
|
|
21505
|
-
this.secretValues = [
|
|
21622
|
+
this.secretValues = [this.tokenProvider.current(), options.apiKey].filter(Boolean);
|
|
21506
21623
|
const base = (options.bifrostBaseUrl || DEFAULT_BIFROST_BASE_URL).replace(/\/+$/, "");
|
|
21507
21624
|
this.bifrostProxyUrl = `${base}${BIFROST_PROXY_PATH}`;
|
|
21508
21625
|
this.observabilityBaseUrl = (options.observabilityBaseUrl || DEFAULT_OBSERVABILITY_BASE_URL).replace(/\/+$/, "");
|
|
@@ -21514,6 +21631,11 @@ var BifrostCatalogClient = class {
|
|
|
21514
21631
|
this.secretValues.push(apiKey);
|
|
21515
21632
|
}
|
|
21516
21633
|
}
|
|
21634
|
+
registerAccessToken(token) {
|
|
21635
|
+
if (token && !this.secretValues.includes(token)) {
|
|
21636
|
+
this.secretValues.push(token);
|
|
21637
|
+
}
|
|
21638
|
+
}
|
|
21517
21639
|
/**
|
|
21518
21640
|
* Build Bifrost proxy headers.
|
|
21519
21641
|
* x-entity-team-id is ONLY included when teamId is present (org-mode tokens).
|
|
@@ -21521,7 +21643,7 @@ var BifrostCatalogClient = class {
|
|
|
21521
21643
|
*/
|
|
21522
21644
|
headers() {
|
|
21523
21645
|
const h = {
|
|
21524
|
-
"x-access-token": this.
|
|
21646
|
+
"x-access-token": this.tokenProvider.current(),
|
|
21525
21647
|
"Content-Type": "application/json"
|
|
21526
21648
|
};
|
|
21527
21649
|
if (this.teamId) {
|
|
@@ -21537,7 +21659,7 @@ var BifrostCatalogClient = class {
|
|
|
21537
21659
|
const session = getMemoizedSessionIdentity();
|
|
21538
21660
|
return {
|
|
21539
21661
|
operation,
|
|
21540
|
-
hasAccessToken: Boolean(this.
|
|
21662
|
+
hasAccessToken: Boolean(this.tokenProvider.current()),
|
|
21541
21663
|
sessionTeamId: session?.teamId,
|
|
21542
21664
|
sessionRoles: session?.roles,
|
|
21543
21665
|
sessionConsumerType: session?.consumerType,
|
|
@@ -21546,7 +21668,7 @@ var BifrostCatalogClient = class {
|
|
|
21546
21668
|
};
|
|
21547
21669
|
}
|
|
21548
21670
|
async proxyRequest(method, path6, body = {}, operation = "api-catalog request") {
|
|
21549
|
-
const
|
|
21671
|
+
const send2 = async () => this.fetchFn(this.bifrostProxyUrl, {
|
|
21550
21672
|
method: "POST",
|
|
21551
21673
|
headers: this.headers(),
|
|
21552
21674
|
body: JSON.stringify({
|
|
@@ -21556,6 +21678,39 @@ var BifrostCatalogClient = class {
|
|
|
21556
21678
|
body
|
|
21557
21679
|
})
|
|
21558
21680
|
});
|
|
21681
|
+
let response = await send2();
|
|
21682
|
+
if (!response.ok) {
|
|
21683
|
+
const bodyText = await response.text().catch(() => "");
|
|
21684
|
+
if (isExpiredAuthError(response.status, bodyText) && this.tokenProvider.canRefresh()) {
|
|
21685
|
+
try {
|
|
21686
|
+
const refreshed = await this.tokenProvider.refresh();
|
|
21687
|
+
this.registerAccessToken(refreshed);
|
|
21688
|
+
response = await send2();
|
|
21689
|
+
} catch {
|
|
21690
|
+
const httpErr = await HttpError.fromResponse(
|
|
21691
|
+
new Response(bodyText, { status: response.status, headers: response.headers }),
|
|
21692
|
+
{
|
|
21693
|
+
method: "POST",
|
|
21694
|
+
url: `bifrost:api-catalog:${method} ${path6}`,
|
|
21695
|
+
secretValues: this.secretValues
|
|
21696
|
+
}
|
|
21697
|
+
);
|
|
21698
|
+
const advised = adviseFromHttpError(httpErr, this.adviceContext(operation));
|
|
21699
|
+
throw advised ?? httpErr;
|
|
21700
|
+
}
|
|
21701
|
+
} else {
|
|
21702
|
+
const httpErr = await HttpError.fromResponse(
|
|
21703
|
+
new Response(bodyText, { status: response.status, headers: response.headers }),
|
|
21704
|
+
{
|
|
21705
|
+
method: "POST",
|
|
21706
|
+
url: `bifrost:api-catalog:${method} ${path6}`,
|
|
21707
|
+
secretValues: this.secretValues
|
|
21708
|
+
}
|
|
21709
|
+
);
|
|
21710
|
+
const advised = adviseFromHttpError(httpErr, this.adviceContext(operation));
|
|
21711
|
+
throw advised ?? httpErr;
|
|
21712
|
+
}
|
|
21713
|
+
}
|
|
21559
21714
|
if (!response.ok) {
|
|
21560
21715
|
const httpErr = await HttpError.fromResponse(response, {
|
|
21561
21716
|
method: "POST",
|
|
@@ -21578,7 +21733,7 @@ var BifrostCatalogClient = class {
|
|
|
21578
21733
|
return data;
|
|
21579
21734
|
}
|
|
21580
21735
|
async akitaProxyRequest(method, path6, body = {}, operation = "Insights request") {
|
|
21581
|
-
const
|
|
21736
|
+
const send2 = async () => this.fetchFn(this.bifrostProxyUrl, {
|
|
21582
21737
|
method: "POST",
|
|
21583
21738
|
headers: this.headers(),
|
|
21584
21739
|
body: JSON.stringify({
|
|
@@ -21588,8 +21743,30 @@ var BifrostCatalogClient = class {
|
|
|
21588
21743
|
body
|
|
21589
21744
|
})
|
|
21590
21745
|
});
|
|
21746
|
+
let response = await send2();
|
|
21591
21747
|
if (!response.ok) {
|
|
21592
21748
|
const text = await response.text().catch(() => "");
|
|
21749
|
+
if (isExpiredAuthError(response.status, text) && this.tokenProvider.canRefresh()) {
|
|
21750
|
+
try {
|
|
21751
|
+
const refreshed = await this.tokenProvider.refresh();
|
|
21752
|
+
this.registerAccessToken(refreshed);
|
|
21753
|
+
response = await send2();
|
|
21754
|
+
if (response.ok) {
|
|
21755
|
+
const data2 = await response.json();
|
|
21756
|
+
return { ok: true, status: response.status, data: data2, errorText: "" };
|
|
21757
|
+
}
|
|
21758
|
+
const retryText = await response.text().catch(() => "");
|
|
21759
|
+
const advised2 = adviseFromBifrostBody(response.status, retryText, this.adviceContext(operation));
|
|
21760
|
+
const errorText2 = advised2 ? retryText ? `${retryText}
|
|
21761
|
+
${advised2.message}` : advised2.message : retryText;
|
|
21762
|
+
return { ok: false, status: response.status, data: null, errorText: errorText2 };
|
|
21763
|
+
} catch {
|
|
21764
|
+
const advised2 = adviseFromBifrostBody(response.status, text, this.adviceContext(operation));
|
|
21765
|
+
const errorText2 = advised2 ? text ? `${text}
|
|
21766
|
+
${advised2.message}` : advised2.message : text;
|
|
21767
|
+
return { ok: false, status: response.status, data: null, errorText: errorText2 };
|
|
21768
|
+
}
|
|
21769
|
+
}
|
|
21593
21770
|
const advised = adviseFromBifrostBody(response.status, text, this.adviceContext(operation));
|
|
21594
21771
|
const errorText = advised ? text ? `${text}
|
|
21595
21772
|
${advised.message}` : advised.message : text;
|
|
@@ -21688,12 +21865,19 @@ ${advised.message}` : advised.message : text;
|
|
|
21688
21865
|
}
|
|
21689
21866
|
page++;
|
|
21690
21867
|
}
|
|
21691
|
-
|
|
21692
|
-
|
|
21693
|
-
|
|
21694
|
-
|
|
21695
|
-
|
|
21696
|
-
|
|
21868
|
+
if (clusterName) {
|
|
21869
|
+
const fullName = `${clusterName}/${projectName}`;
|
|
21870
|
+
const exactMatch = allServices.find((s) => s.name === fullName);
|
|
21871
|
+
return exactMatch?.id || null;
|
|
21872
|
+
}
|
|
21873
|
+
const finalSegmentMatch = allServices.find(
|
|
21874
|
+
(s) => getFinalServiceSegment(s.name) === projectName
|
|
21875
|
+
);
|
|
21876
|
+
if (finalSegmentMatch) return finalSegmentMatch.id;
|
|
21877
|
+
const bracketedMatch = allServices.find(
|
|
21878
|
+
(s) => getFinalServiceSegment(s.name).includes(`[${projectName}]`)
|
|
21879
|
+
);
|
|
21880
|
+
return bracketedMatch?.id || null;
|
|
21697
21881
|
}
|
|
21698
21882
|
async acknowledgeOnboarding(providerServiceId, workspaceId, systemEnvironmentId) {
|
|
21699
21883
|
const result = await this.akitaProxyRequest(
|
|
@@ -21723,6 +21907,14 @@ ${advised.message}` : advised.message : text;
|
|
|
21723
21907
|
throw new Error(`Workspace acknowledge failed: ${result.status} ${result.errorText}`);
|
|
21724
21908
|
}
|
|
21725
21909
|
}
|
|
21910
|
+
// PMAK-only by proven exception. A live probe against the observability
|
|
21911
|
+
// application-binding endpoint (POST /v2/agent/api-catalog/workspaces/:id/
|
|
21912
|
+
// applications) showed x-access-token is rejected identically to the x-api-key
|
|
21913
|
+
// control: both return 401 {"message":"Postman User not found"} for a
|
|
21914
|
+
// service-account credential, because the observability service has no
|
|
21915
|
+
// "Postman User" for a service account. The access token offers no improvement
|
|
21916
|
+
// over the API key here, so this route is not migrated to access-token-primary
|
|
21917
|
+
// (the suite-wide migration explicitly leaves probe-failed routes on PMAK).
|
|
21726
21918
|
async createApplication(workspaceId, systemEnv) {
|
|
21727
21919
|
const response = await this.fetchFn(
|
|
21728
21920
|
`${this.observabilityBaseUrl}/v2/agent/api-catalog/workspaces/${workspaceId}/applications`,
|
|
@@ -21790,7 +21982,17 @@ function findDiscoveredService(services, projectName, clusterName) {
|
|
|
21790
21982
|
const fullName = `${clusterName}/${projectName}`;
|
|
21791
21983
|
return services.find((s) => s.name === fullName);
|
|
21792
21984
|
}
|
|
21793
|
-
|
|
21985
|
+
const finalSegmentMatch = services.find(
|
|
21986
|
+
(service) => getFinalServiceSegment(service.name) === projectName
|
|
21987
|
+
);
|
|
21988
|
+
if (finalSegmentMatch) return finalSegmentMatch;
|
|
21989
|
+
return services.find(
|
|
21990
|
+
(service) => getFinalServiceSegment(service.name).includes(`[${projectName}]`)
|
|
21991
|
+
);
|
|
21992
|
+
}
|
|
21993
|
+
function getFinalServiceSegment(serviceName) {
|
|
21994
|
+
const lastSlash = serviceName.lastIndexOf("/");
|
|
21995
|
+
return lastSlash === -1 ? serviceName : serviceName.slice(lastSlash + 1);
|
|
21794
21996
|
}
|
|
21795
21997
|
|
|
21796
21998
|
// node_modules/@postman-cse/automation-telemetry-core/dist/ci-context.js
|
|
@@ -22047,7 +22249,7 @@ function resolveActionVersion(explicit) {
|
|
|
22047
22249
|
if (explicit) {
|
|
22048
22250
|
return explicit;
|
|
22049
22251
|
}
|
|
22050
|
-
return "
|
|
22252
|
+
return "2.0.0" ? "2.0.0" : "unknown";
|
|
22051
22253
|
}
|
|
22052
22254
|
function telemetryDisabled(env) {
|
|
22053
22255
|
const flag = String(env.POSTMAN_ACTIONS_TELEMETRY ?? "").trim().toLowerCase();
|
|
@@ -22436,19 +22638,39 @@ async function resolveApiKeyAndTeamId(inputs, client, reporter = core_exports) {
|
|
|
22436
22638
|
}
|
|
22437
22639
|
return { apiKey, teamId: resolvedTeamId, pmakIdentity };
|
|
22438
22640
|
}
|
|
22439
|
-
async function runCredentialPreflightForInputs(inputs, pmak, reporter, fetchImpl) {
|
|
22641
|
+
async function runCredentialPreflightForInputs(inputs, pmak, reporter, fetchImpl, liveAccessToken) {
|
|
22642
|
+
const accessToken = liveAccessToken ?? inputs.postmanAccessToken;
|
|
22440
22643
|
await runCredentialPreflight({
|
|
22441
22644
|
apiBaseUrl: inputs.postmanApiBase || DEFAULT_POSTMAN_API_BASE,
|
|
22442
22645
|
iapubBaseUrl: inputs.postmanIapubBase || DEFAULT_POSTMAN_IAPUB_BASE,
|
|
22443
22646
|
pmak,
|
|
22444
|
-
postmanAccessToken:
|
|
22647
|
+
postmanAccessToken: accessToken,
|
|
22445
22648
|
explicitTeamId: inputs.postmanTeamId || void 0,
|
|
22446
22649
|
mode: inputs.credentialPreflight,
|
|
22447
|
-
mask: createSecretMasker([inputs.postmanApiKey,
|
|
22650
|
+
mask: createSecretMasker([inputs.postmanApiKey, accessToken]),
|
|
22448
22651
|
log: reporter,
|
|
22449
22652
|
fetchImpl
|
|
22450
22653
|
});
|
|
22451
22654
|
}
|
|
22655
|
+
function createInsightsTokenProvider(inputs, reporter, apiKey = inputs.postmanApiKey) {
|
|
22656
|
+
return new AccessTokenProvider({
|
|
22657
|
+
accessToken: inputs.postmanAccessToken,
|
|
22658
|
+
apiKey,
|
|
22659
|
+
apiBaseUrl: inputs.postmanApiBase || DEFAULT_POSTMAN_API_BASE,
|
|
22660
|
+
onToken: (token) => reporter.setSecret(token)
|
|
22661
|
+
});
|
|
22662
|
+
}
|
|
22663
|
+
function createInsightsBifrostClient(inputs, tokenProvider, teamId, apiKey) {
|
|
22664
|
+
return new BifrostCatalogClient({
|
|
22665
|
+
tokenProvider,
|
|
22666
|
+
accessToken: tokenProvider.current(),
|
|
22667
|
+
teamId,
|
|
22668
|
+
apiKey,
|
|
22669
|
+
bifrostBaseUrl: inputs.postmanBifrostBase,
|
|
22670
|
+
observabilityBaseUrl: inputs.postmanObservabilityBase,
|
|
22671
|
+
observabilityEnv: inputs.postmanObservabilityEnv
|
|
22672
|
+
});
|
|
22673
|
+
}
|
|
22452
22674
|
async function runAction() {
|
|
22453
22675
|
const inputs = resolveInputs();
|
|
22454
22676
|
const planned = createPlannedOutputs(inputs);
|
|
@@ -22458,28 +22680,32 @@ async function runAction() {
|
|
|
22458
22680
|
setSecret(inputs.postmanAccessToken);
|
|
22459
22681
|
if (inputs.postmanApiKey) setSecret(inputs.postmanApiKey);
|
|
22460
22682
|
if (inputs.githubToken) setSecret(inputs.githubToken);
|
|
22461
|
-
const
|
|
22462
|
-
|
|
22463
|
-
|
|
22464
|
-
|
|
22465
|
-
|
|
22466
|
-
|
|
22467
|
-
|
|
22468
|
-
});
|
|
22683
|
+
const tokenProvider = createInsightsTokenProvider(inputs, core_exports);
|
|
22684
|
+
const preliminaryClient = createInsightsBifrostClient(
|
|
22685
|
+
inputs,
|
|
22686
|
+
tokenProvider,
|
|
22687
|
+
inputs.postmanTeamId,
|
|
22688
|
+
inputs.postmanApiKey
|
|
22689
|
+
);
|
|
22469
22690
|
const { apiKey, teamId, pmakIdentity } = await resolveApiKeyAndTeamId(inputs, preliminaryClient, core_exports);
|
|
22691
|
+
const activeTokenProvider = apiKey !== inputs.postmanApiKey ? new AccessTokenProvider({
|
|
22692
|
+
accessToken: tokenProvider.current(),
|
|
22693
|
+
apiKey,
|
|
22694
|
+
apiBaseUrl: inputs.postmanApiBase || DEFAULT_POSTMAN_API_BASE,
|
|
22695
|
+
onToken: (token) => setSecret(token)
|
|
22696
|
+
}) : tokenProvider;
|
|
22470
22697
|
const telemetry = createTelemetryContext({ action: "postman-insights-onboarding-action", logger: core_exports });
|
|
22471
22698
|
telemetry.setTeamId(inputs.postmanTeamId || pmakIdentity?.teamId);
|
|
22472
22699
|
let result;
|
|
22473
22700
|
try {
|
|
22474
|
-
await runCredentialPreflightForInputs(
|
|
22475
|
-
|
|
22476
|
-
|
|
22477
|
-
|
|
22478
|
-
|
|
22479
|
-
|
|
22480
|
-
|
|
22481
|
-
|
|
22482
|
-
});
|
|
22701
|
+
await runCredentialPreflightForInputs(
|
|
22702
|
+
inputs,
|
|
22703
|
+
pmakIdentity,
|
|
22704
|
+
core_exports,
|
|
22705
|
+
void 0,
|
|
22706
|
+
activeTokenProvider.current()
|
|
22707
|
+
);
|
|
22708
|
+
const client = createInsightsBifrostClient(inputs, activeTokenProvider, teamId, apiKey);
|
|
22483
22709
|
result = await runOnboarding(inputs, client, sleep, core_exports);
|
|
22484
22710
|
} catch (error2) {
|
|
22485
22711
|
const message = error2 instanceof Error ? error2.message : String(error2);
|