@commercetools/cli-topic-integration-layer 0.0.0 → 0.2.2
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/LICENSE +21 -0
- package/README.md +64 -0
- package/dist/commands/integration-layer/config/get.d.ts +9 -0
- package/dist/commands/integration-layer/config/get.js +22 -0
- package/dist/commands/integration-layer/config/get.js.map +1 -0
- package/dist/commands/integration-layer/config/list.d.ts +6 -0
- package/dist/commands/integration-layer/config/list.js +20 -0
- package/dist/commands/integration-layer/config/list.js.map +1 -0
- package/dist/commands/integration-layer/config/set.d.ts +13 -0
- package/dist/commands/integration-layer/config/set.js +29 -0
- package/dist/commands/integration-layer/config/set.js.map +1 -0
- package/dist/commands/integration-layer/config/unset.d.ts +9 -0
- package/dist/commands/integration-layer/config/unset.js +18 -0
- package/dist/commands/integration-layer/config/unset.js.map +1 -0
- package/dist/commands/integration-layer/extension/build.d.ts +12 -0
- package/dist/commands/integration-layer/extension/build.js +40 -0
- package/dist/commands/integration-layer/extension/build.js.map +1 -0
- package/dist/commands/integration-layer/extension/delete.d.ts +9 -0
- package/dist/commands/integration-layer/extension/delete.js +39 -0
- package/dist/commands/integration-layer/extension/delete.js.map +1 -0
- package/dist/commands/integration-layer/extension/invoke.d.ts +13 -0
- package/dist/commands/integration-layer/extension/invoke.js +89 -0
- package/dist/commands/integration-layer/extension/invoke.js.map +1 -0
- package/dist/commands/integration-layer/extension/push.d.ts +28 -0
- package/dist/commands/integration-layer/extension/push.js +217 -0
- package/dist/commands/integration-layer/extension/push.js.map +1 -0
- package/dist/commands/integration-layer/extension/serve.d.ts +37 -0
- package/dist/commands/integration-layer/extension/serve.js +528 -0
- package/dist/commands/integration-layer/extension/serve.js.map +1 -0
- package/dist/commands/integration-layer/extension/status.d.ts +6 -0
- package/dist/commands/integration-layer/extension/status.js +28 -0
- package/dist/commands/integration-layer/extension/status.js.map +1 -0
- package/dist/commands/integration-layer/extension/validate.d.ts +15 -0
- package/dist/commands/integration-layer/extension/validate.js +98 -0
- package/dist/commands/integration-layer/extension/validate.js.map +1 -0
- package/dist/commands/integration-layer/hello.d.ts +6 -0
- package/dist/commands/integration-layer/hello.js +10 -0
- package/dist/commands/integration-layer/hello.js.map +1 -0
- package/dist/commands/integration-layer/init.d.ts +13 -0
- package/dist/commands/integration-layer/init.js +279 -0
- package/dist/commands/integration-layer/init.js.map +1 -0
- package/dist/commands/integration-layer/schema/fetch.d.ts +9 -0
- package/dist/commands/integration-layer/schema/fetch.js +27 -0
- package/dist/commands/integration-layer/schema/fetch.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/awaitBundleState.d.ts +33 -0
- package/dist/lib/awaitBundleState.js +77 -0
- package/dist/lib/awaitBundleState.js.map +1 -0
- package/dist/lib/base.d.ts +50 -0
- package/dist/lib/base.js +85 -0
- package/dist/lib/base.js.map +1 -0
- package/dist/lib/ilClient.d.ts +93 -0
- package/dist/lib/ilClient.js +120 -0
- package/dist/lib/ilClient.js.map +1 -0
- package/dist/lib/sourceRevision.d.ts +39 -0
- package/dist/lib/sourceRevision.js +95 -0
- package/dist/lib/sourceRevision.js.map +1 -0
- package/dist/lib/tooling/apiExtension.d.ts +31 -0
- package/dist/lib/tooling/apiExtension.js +39 -0
- package/dist/lib/tooling/apiExtension.js.map +1 -0
- package/dist/lib/tooling/build.d.ts +18 -0
- package/dist/lib/tooling/build.js +54 -0
- package/dist/lib/tooling/build.js.map +1 -0
- package/dist/lib/tooling/compose.d.ts +28 -0
- package/dist/lib/tooling/compose.js +48 -0
- package/dist/lib/tooling/compose.js.map +1 -0
- package/dist/lib/tooling/extensions.d.ts +77 -0
- package/dist/lib/tooling/extensions.js +209 -0
- package/dist/lib/tooling/extensions.js.map +1 -0
- package/dist/lib/tooling/gateway.d.ts +21 -0
- package/dist/lib/tooling/gateway.js +65 -0
- package/dist/lib/tooling/gateway.js.map +1 -0
- package/dist/lib/tooling/loadBundle.d.ts +10 -0
- package/dist/lib/tooling/loadBundle.js +27 -0
- package/dist/lib/tooling/loadBundle.js.map +1 -0
- package/dist/lib/tooling/staticAnalysis.d.ts +11 -0
- package/dist/lib/tooling/staticAnalysis.js +167 -0
- package/dist/lib/tooling/staticAnalysis.js.map +1 -0
- package/dist/lib/tooling/validateBundle.d.ts +19 -0
- package/dist/lib/tooling/validateBundle.js +144 -0
- package/dist/lib/tooling/validateBundle.js.map +1 -0
- package/package.json +47 -5
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { AuthCommand, CtpAuthorizedClient } from "@commercetools/cli-plugin-auth";
|
|
2
|
+
/** The resolved, ready-to-use integration-layer context: where to call and as whom. */
|
|
3
|
+
export interface IlContext {
|
|
4
|
+
/** The integration-layer edge base URL, trailing slashes trimmed. */
|
|
5
|
+
baseUrl: string;
|
|
6
|
+
projectKey: string;
|
|
7
|
+
/** The logged-in principal's commercetools access token (the `manage_project` bearer). */
|
|
8
|
+
token: string;
|
|
9
|
+
}
|
|
10
|
+
/** The subset of parsed flags {@link IntegrationLayerCommand.resolveIlContext} reads. */
|
|
11
|
+
export interface IlFlagValues {
|
|
12
|
+
"integration-layer-url"?: string;
|
|
13
|
+
"project-key"?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Derive the integration-layer EXTENSIONS edge base URL from the authenticated
|
|
17
|
+
* principal's commercetools region. The extensions edge — the connector-backend pod
|
|
18
|
+
* that serves the CLI's `/<project>/subgraph` + `/<project>/extension/*` routes —
|
|
19
|
+
* follows the same host convention as the commercetools API itself
|
|
20
|
+
* (`<svc>.<region>.commercetools.com`, cf. the auth client's
|
|
21
|
+
* `auth.<region>.commercetools.com`), so a login in region `eu-central-1.aws`
|
|
22
|
+
* resolves to `https://extensions.integration-layer.eu-central-1.aws.commercetools.com`
|
|
23
|
+
* (the production host). `--integration-layer-url` / `INTEGRATION_LAYER_URL` stays
|
|
24
|
+
* the explicit override — e.g. to point at a local edge or a staging edge on the
|
|
25
|
+
* escemo/stage zone, which don't follow the production convention. Returns undefined
|
|
26
|
+
* only when the region is absent, so resolveIlContext then fails loudly.
|
|
27
|
+
*/
|
|
28
|
+
export declare function edgeUrlForRegion(region: string): string | undefined;
|
|
29
|
+
export declare abstract class IntegrationLayerCommand extends AuthCommand {
|
|
30
|
+
/**
|
|
31
|
+
* Whether the command needs a logged-in principal. True for every command that
|
|
32
|
+
* calls the integration layer; a command that only conditionally reaches it (e.g.
|
|
33
|
+
* `serve` in standalone mode) sets this false and relies on {@link resolveIlContext}
|
|
34
|
+
* to fail loudly if the network path is actually taken.
|
|
35
|
+
*/
|
|
36
|
+
protected authorized: boolean;
|
|
37
|
+
static baseFlags: {
|
|
38
|
+
"integration-layer-url": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
39
|
+
"project-key": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
40
|
+
};
|
|
41
|
+
protected init(): Promise<void>;
|
|
42
|
+
/** The logged-in commercetools principal, or an `AuthorizationError` pointing at `auth login`. */
|
|
43
|
+
protected requirePrincipal(): CtpAuthorizedClient;
|
|
44
|
+
/**
|
|
45
|
+
* Resolve the integration-layer base URL + project key from the login context and
|
|
46
|
+
* take the principal's access token as the bearer. Fails loudly (no defensive
|
|
47
|
+
* defaults) when a required value is absent.
|
|
48
|
+
*/
|
|
49
|
+
protected resolveIlContext(flags: IlFlagValues): Promise<IlContext>;
|
|
50
|
+
}
|
package/dist/lib/base.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Base class for the topic's commands that reach the integration layer's
|
|
2
|
+
// `manage_project` boundary. It extends the host CLI's `AuthCommand`
|
|
3
|
+
// (`@commercetools/cli-plugin-auth`), so the bearer is the token
|
|
4
|
+
// `commercetools auth login` already persisted to `~/.commercetools/credentials`
|
|
5
|
+
// (loaded into the security context by the auth plugin's prerun hook) — no separate
|
|
6
|
+
// credential handling, exactly as the `connect` topic does.
|
|
7
|
+
//
|
|
8
|
+
// `@commercetools/cli-plugin-auth` is a PEER dependency: at runtime the plugin uses
|
|
9
|
+
// the HOST CLI's copy, so the static auth manager/repository the host wires up are
|
|
10
|
+
// the same singletons this class reads (a bundled second copy would see an empty
|
|
11
|
+
// security context and reject every logged-in caller).
|
|
12
|
+
import { Flags } from "@oclif/core";
|
|
13
|
+
import { AuthCommand, AuthorizationError, CtpAuthorizedClient, } from "@commercetools/cli-plugin-auth";
|
|
14
|
+
/**
|
|
15
|
+
* Derive the integration-layer EXTENSIONS edge base URL from the authenticated
|
|
16
|
+
* principal's commercetools region. The extensions edge — the connector-backend pod
|
|
17
|
+
* that serves the CLI's `/<project>/subgraph` + `/<project>/extension/*` routes —
|
|
18
|
+
* follows the same host convention as the commercetools API itself
|
|
19
|
+
* (`<svc>.<region>.commercetools.com`, cf. the auth client's
|
|
20
|
+
* `auth.<region>.commercetools.com`), so a login in region `eu-central-1.aws`
|
|
21
|
+
* resolves to `https://extensions.integration-layer.eu-central-1.aws.commercetools.com`
|
|
22
|
+
* (the production host). `--integration-layer-url` / `INTEGRATION_LAYER_URL` stays
|
|
23
|
+
* the explicit override — e.g. to point at a local edge or a staging edge on the
|
|
24
|
+
* escemo/stage zone, which don't follow the production convention. Returns undefined
|
|
25
|
+
* only when the region is absent, so resolveIlContext then fails loudly.
|
|
26
|
+
*/
|
|
27
|
+
export function edgeUrlForRegion(region) {
|
|
28
|
+
const trimmed = region.trim();
|
|
29
|
+
if (!trimmed)
|
|
30
|
+
return undefined;
|
|
31
|
+
return `https://extensions.integration-layer.${trimmed}.commercetools.com`;
|
|
32
|
+
}
|
|
33
|
+
export class IntegrationLayerCommand extends AuthCommand {
|
|
34
|
+
/**
|
|
35
|
+
* Whether the command needs a logged-in principal. True for every command that
|
|
36
|
+
* calls the integration layer; a command that only conditionally reaches it (e.g.
|
|
37
|
+
* `serve` in standalone mode) sets this false and relies on {@link resolveIlContext}
|
|
38
|
+
* to fail loudly if the network path is actually taken.
|
|
39
|
+
*/
|
|
40
|
+
authorized = true;
|
|
41
|
+
static baseFlags = {
|
|
42
|
+
"integration-layer-url": Flags.string({
|
|
43
|
+
description: "integration-layer extensions edge base URL (also settable via INTEGRATION_LAYER_URL); overrides the URL derived from your login region",
|
|
44
|
+
env: "INTEGRATION_LAYER_URL",
|
|
45
|
+
helpGroup: "INTEGRATION LAYER",
|
|
46
|
+
}),
|
|
47
|
+
"project-key": Flags.string({
|
|
48
|
+
description: "override the logged-in project key",
|
|
49
|
+
helpGroup: "INTEGRATION LAYER",
|
|
50
|
+
}),
|
|
51
|
+
};
|
|
52
|
+
async init() {
|
|
53
|
+
await super.init();
|
|
54
|
+
if (this.authorized)
|
|
55
|
+
this.requirePrincipal();
|
|
56
|
+
}
|
|
57
|
+
/** The logged-in commercetools principal, or an `AuthorizationError` pointing at `auth login`. */
|
|
58
|
+
requirePrincipal() {
|
|
59
|
+
const principal = this.getAuthentication()?.getPrincipal();
|
|
60
|
+
if (principal instanceof CtpAuthorizedClient)
|
|
61
|
+
return principal;
|
|
62
|
+
throw new AuthorizationError("not logged in — run `commercetools auth login` first");
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Resolve the integration-layer base URL + project key from the login context and
|
|
66
|
+
* take the principal's access token as the bearer. Fails loudly (no defensive
|
|
67
|
+
* defaults) when a required value is absent.
|
|
68
|
+
*/
|
|
69
|
+
async resolveIlContext(flags) {
|
|
70
|
+
const principal = this.requirePrincipal();
|
|
71
|
+
const token = principal.getAccessToken().getTokenValue();
|
|
72
|
+
const projectKey = flags["project-key"] ?? principal.getProjectKey();
|
|
73
|
+
if (!projectKey) {
|
|
74
|
+
throw new Error("no project key — log in with `commercetools auth login --project-key <key>` or pass --project-key");
|
|
75
|
+
}
|
|
76
|
+
const baseUrl = flags["integration-layer-url"] ?? edgeUrlForRegion(principal.getRegion());
|
|
77
|
+
if (!baseUrl) {
|
|
78
|
+
throw new Error("could not resolve the integration layer URL: pass --integration-layer-url or set " +
|
|
79
|
+
"INTEGRATION_LAYER_URL (the extensions edge base, e.g. " +
|
|
80
|
+
"https://extensions.integration-layer.eu-central-1.aws.commercetools.com)");
|
|
81
|
+
}
|
|
82
|
+
return { baseUrl: baseUrl.replace(/\/+$/, ""), projectKey, token };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=base.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../src/lib/base.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,qEAAqE;AACrE,iEAAiE;AACjE,iFAAiF;AACjF,oFAAoF;AACpF,4DAA4D;AAC5D,EAAE;AACF,oFAAoF;AACpF,mFAAmF;AACnF,iFAAiF;AACjF,uDAAuD;AAEvD,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,gCAAgC,CAAC;AAiBxC;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,OAAO,wCAAwC,OAAO,oBAAoB,CAAC;AAC7E,CAAC;AAED,MAAM,OAAgB,uBAAwB,SAAQ,WAAW;IAC/D;;;;;OAKG;IACO,UAAU,GAAG,IAAI,CAAC;IAE5B,MAAM,CAAU,SAAS,GAAG;QAC1B,uBAAuB,EAAE,KAAK,CAAC,MAAM,CAAC;YACpC,WAAW,EACT,wIAAwI;YAC1I,GAAG,EAAE,uBAAuB;YAC5B,SAAS,EAAE,mBAAmB;SAC/B,CAAC;QACF,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC;YAC1B,WAAW,EAAE,oCAAoC;YACjD,SAAS,EAAE,mBAAmB;SAC/B,CAAC;KACH,CAAC;IAEiB,KAAK,CAAC,IAAI;QAC3B,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,IAAI,IAAI,CAAC,UAAU;YAAE,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAC/C,CAAC;IAED,kGAAkG;IACxF,gBAAgB;QACxB,MAAM,SAAS,GAAG,IAAI,CAAC,iBAAiB,EAAE,EAAE,YAAY,EAAE,CAAC;QAC3D,IAAI,SAAS,YAAY,mBAAmB;YAAE,OAAO,SAAS,CAAC;QAC/D,MAAM,IAAI,kBAAkB,CAAC,sDAAsD,CAAC,CAAC;IACvF,CAAC;IAED;;;;OAIG;IACO,KAAK,CAAC,gBAAgB,CAAC,KAAmB;QAClD,MAAM,SAAS,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAG,SAAS,CAAC,cAAc,EAAE,CAAC,aAAa,EAAE,CAAC;QAEzD,MAAM,UAAU,GAAG,KAAK,CAAC,aAAa,CAAC,IAAI,SAAS,CAAC,aAAa,EAAE,CAAC;QACrE,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CACb,mGAAmG,CACpG,CAAC;QACJ,CAAC;QAED,MAAM,OAAO,GAAG,KAAK,CAAC,uBAAuB,CAAC,IAAI,gBAAgB,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC,CAAC;QAC1F,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CACb,mFAAmF;gBACjF,wDAAwD;gBACxD,0EAA0E,CAC7E,CAAC;QACJ,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;IACrE,CAAC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/** A single {key,value,secret} entry of a project's extension config (secret values redacted on read). */
|
|
2
|
+
export interface ConfigEntry {
|
|
3
|
+
key: string;
|
|
4
|
+
value: string | null;
|
|
5
|
+
secret: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Lifecycle state of a stored bundle version.
|
|
9
|
+
*
|
|
10
|
+
* pending — stored and being handed out, but nothing has confirmed it loads.
|
|
11
|
+
* running — an extension loaded it and published its schema. Once a version
|
|
12
|
+
* reaches this it is never demoted.
|
|
13
|
+
* failed — it could not be loaded (`reason` says why). The integration layer
|
|
14
|
+
* stops handing it out and the version beneath it takes over.
|
|
15
|
+
*/
|
|
16
|
+
export type ExtensionState = "pending" | "running" | "failed";
|
|
17
|
+
/** The stored bundle's metadata (integration layer `ExtensionMeta`). */
|
|
18
|
+
export interface ExtensionMeta {
|
|
19
|
+
projectKey: string;
|
|
20
|
+
length: number;
|
|
21
|
+
uploadedAt: number;
|
|
22
|
+
version: number;
|
|
23
|
+
updatedBy?: string;
|
|
24
|
+
filename?: string;
|
|
25
|
+
/**
|
|
26
|
+
* The integrator's own version-control revision the stored bundle was built from,
|
|
27
|
+
* as reported on upload. Absent for a bundle uploaded without one (e.g. by hand in
|
|
28
|
+
* the Merchant Center).
|
|
29
|
+
*/
|
|
30
|
+
sourceRevision?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Lifecycle state of THIS version. Absent when talking to an integration layer
|
|
33
|
+
* that predates bundle state — callers must treat `undefined` as "this deployment
|
|
34
|
+
* can't tell me", not as a failure.
|
|
35
|
+
*/
|
|
36
|
+
state?: ExtensionState;
|
|
37
|
+
/** Why this version failed to load; only ever set alongside state "failed". */
|
|
38
|
+
reason?: string;
|
|
39
|
+
/**
|
|
40
|
+
* The version actually in use, which is NOT always `version`: when the newest
|
|
41
|
+
* upload failed to load, the integration layer serves the one beneath it and this
|
|
42
|
+
* reports that older one. `null` when nothing is in use. Returned by the meta read
|
|
43
|
+
* only, not by an upload's response.
|
|
44
|
+
*
|
|
45
|
+
* It carries the source revision as well as the number, because the number is the
|
|
46
|
+
* integration layer's own counter — the commit is what identifies the build to
|
|
47
|
+
* whoever pushed it.
|
|
48
|
+
*/
|
|
49
|
+
served?: {
|
|
50
|
+
version: number;
|
|
51
|
+
sourceRevision?: string;
|
|
52
|
+
} | null;
|
|
53
|
+
}
|
|
54
|
+
export interface BreakingChange {
|
|
55
|
+
type: string;
|
|
56
|
+
description: string;
|
|
57
|
+
}
|
|
58
|
+
export interface RemoteValidationResult {
|
|
59
|
+
valid: boolean;
|
|
60
|
+
composes: boolean;
|
|
61
|
+
compositionErrors: string[];
|
|
62
|
+
breakingChanges: BreakingChange[];
|
|
63
|
+
/** False when no published baseline was available, so the breaking check was skipped. */
|
|
64
|
+
comparedToPublished: boolean;
|
|
65
|
+
}
|
|
66
|
+
/** GET the project's raw core-subgraph SDL — the compose input (`GET …/subgraph`). */
|
|
67
|
+
export declare function fetchSubgraphSdl(baseUrl: string, projectKey: string, token: string): Promise<string>;
|
|
68
|
+
/** POST a candidate SDL to the composition + breaking-change gate (`POST …/extension/validate`). */
|
|
69
|
+
export declare function remoteValidate(baseUrl: string, projectKey: string, token: string, typeDefs: string): Promise<RemoteValidationResult>;
|
|
70
|
+
/**
|
|
71
|
+
* Upload the built bundle, replacing the project's stored one (`PUT …/extension/bundle`).
|
|
72
|
+
*
|
|
73
|
+
* `sourceRevision` (optional) records which of the integrator's revisions this build
|
|
74
|
+
* came from; the integration layer stores it against the revision, shows it in the
|
|
75
|
+
* Merchant Center, and hands it to the connector.
|
|
76
|
+
*/
|
|
77
|
+
export declare function pushBundle(baseUrl: string, projectKey: string, token: string, bundle: string, filename: string, sourceRevision?: string): Promise<ExtensionMeta>;
|
|
78
|
+
/** The stored bundle's metadata, or null if none is stored (`GET …/extension/bundle/meta`). */
|
|
79
|
+
export declare function fetchExtensionMeta(baseUrl: string, projectKey: string, token: string): Promise<ExtensionMeta | null>;
|
|
80
|
+
/** Remove the project's extension subgraph from Hive (`DELETE …/extension/subgraph`). */
|
|
81
|
+
export declare function deleteExtensionSubgraph(baseUrl: string, projectKey: string, token: string): Promise<void>;
|
|
82
|
+
/** List the project's extension config entries, secret values redacted (`GET …/extension/config`). */
|
|
83
|
+
export declare function listConfig(baseUrl: string, projectKey: string, token: string): Promise<ConfigEntry[]>;
|
|
84
|
+
/**
|
|
85
|
+
* Upsert/delete config entries and return the resulting (redacted) list
|
|
86
|
+
* (`PATCH …/extension/config`). An entry with `value: null` deletes that key. PATCH
|
|
87
|
+
* (not PUT) so a single set/unset touches one key without clobbering the rest.
|
|
88
|
+
*/
|
|
89
|
+
export declare function patchConfig(baseUrl: string, projectKey: string, token: string, entries: Array<{
|
|
90
|
+
key: string;
|
|
91
|
+
value: string | null;
|
|
92
|
+
secret?: boolean;
|
|
93
|
+
}>): Promise<ConfigEntry[]>;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// Low-level HTTP client for the integration layer's per-project manage-surface
|
|
2
|
+
// routes. Every call is parameterised by (baseUrl, projectKey, token) — the command
|
|
3
|
+
// layer resolves those (`IntegrationLayerCommand.resolveIlContext`, whose token is the
|
|
4
|
+
// `commercetools auth login` principal's) and this module just speaks the wire
|
|
5
|
+
// protocol. The route bodies these mirror live in the integration
|
|
6
|
+
// layer's `routes/manage.ts`; the request/response shapes here are extracted from the
|
|
7
|
+
// `ee-ext` tooling's `remoteValidate.ts` / `push.ts` (which read them from env) and
|
|
8
|
+
// the remaining manage routes (config, status, delete, subgraph).
|
|
9
|
+
function apiRoot(baseUrl, projectKey) {
|
|
10
|
+
return `${baseUrl.replace(/\/+$/, "")}/${encodeURIComponent(projectKey)}`;
|
|
11
|
+
}
|
|
12
|
+
function bearer(token) {
|
|
13
|
+
return { Authorization: `Bearer ${token}` };
|
|
14
|
+
}
|
|
15
|
+
/** GET the project's raw core-subgraph SDL — the compose input (`GET …/subgraph`). */
|
|
16
|
+
export async function fetchSubgraphSdl(baseUrl, projectKey, token) {
|
|
17
|
+
const url = `${apiRoot(baseUrl, projectKey)}/subgraph`;
|
|
18
|
+
const res = await fetch(url, { headers: { accept: "text/plain", ...bearer(token) } });
|
|
19
|
+
const text = await res.text();
|
|
20
|
+
if (!res.ok) {
|
|
21
|
+
throw new Error(`could not fetch core-subgraph SDL (${res.status}) from ${url}: ${text}`);
|
|
22
|
+
}
|
|
23
|
+
return text;
|
|
24
|
+
}
|
|
25
|
+
/** POST a candidate SDL to the composition + breaking-change gate (`POST …/extension/validate`). */
|
|
26
|
+
export async function remoteValidate(baseUrl, projectKey, token, typeDefs) {
|
|
27
|
+
const url = `${apiRoot(baseUrl, projectKey)}/extension/validate`;
|
|
28
|
+
const res = await fetch(url, {
|
|
29
|
+
method: "POST",
|
|
30
|
+
headers: { "Content-Type": "application/json", ...bearer(token) },
|
|
31
|
+
body: JSON.stringify({ typeDefs }),
|
|
32
|
+
});
|
|
33
|
+
const text = await res.text();
|
|
34
|
+
if (!res.ok) {
|
|
35
|
+
throw new Error(`schema validation request failed (${res.status}): ${text}`);
|
|
36
|
+
}
|
|
37
|
+
return JSON.parse(text);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Upload the built bundle, replacing the project's stored one (`PUT …/extension/bundle`).
|
|
41
|
+
*
|
|
42
|
+
* `sourceRevision` (optional) records which of the integrator's revisions this build
|
|
43
|
+
* came from; the integration layer stores it against the revision, shows it in the
|
|
44
|
+
* Merchant Center, and hands it to the connector.
|
|
45
|
+
*/
|
|
46
|
+
export async function pushBundle(baseUrl, projectKey, token, bundle, filename, sourceRevision) {
|
|
47
|
+
const url = `${apiRoot(baseUrl, projectKey)}/extension/bundle`;
|
|
48
|
+
const res = await fetch(url, {
|
|
49
|
+
method: "PUT",
|
|
50
|
+
headers: {
|
|
51
|
+
"Content-Type": "text/javascript",
|
|
52
|
+
// The route requires the original filename (percent-encoded; it carries the
|
|
53
|
+
// module's type). The bundle is plain CommonJS, so name it `.cjs`.
|
|
54
|
+
"x-extension-filename": encodeURIComponent(filename),
|
|
55
|
+
// Percent-encoded for the same reason as the filename: keep an arbitrary value
|
|
56
|
+
// header-safe. Omitted entirely when there's no revision to report — the route
|
|
57
|
+
// treats a missing header as "no revision", not an error.
|
|
58
|
+
...(sourceRevision
|
|
59
|
+
? { "x-extension-source-revision": encodeURIComponent(sourceRevision) }
|
|
60
|
+
: {}),
|
|
61
|
+
...bearer(token),
|
|
62
|
+
},
|
|
63
|
+
body: bundle,
|
|
64
|
+
});
|
|
65
|
+
const text = await res.text();
|
|
66
|
+
if (!res.ok) {
|
|
67
|
+
throw new Error(`PUT extension/bundle failed (${res.status}): ${text}`);
|
|
68
|
+
}
|
|
69
|
+
return JSON.parse(text);
|
|
70
|
+
}
|
|
71
|
+
/** The stored bundle's metadata, or null if none is stored (`GET …/extension/bundle/meta`). */
|
|
72
|
+
export async function fetchExtensionMeta(baseUrl, projectKey, token) {
|
|
73
|
+
const url = `${apiRoot(baseUrl, projectKey)}/extension/bundle/meta`;
|
|
74
|
+
const res = await fetch(url, { headers: { accept: "application/json", ...bearer(token) } });
|
|
75
|
+
if (res.status === 404)
|
|
76
|
+
return null;
|
|
77
|
+
const text = await res.text();
|
|
78
|
+
if (!res.ok) {
|
|
79
|
+
throw new Error(`GET extension/bundle/meta failed (${res.status}): ${text}`);
|
|
80
|
+
}
|
|
81
|
+
return JSON.parse(text);
|
|
82
|
+
}
|
|
83
|
+
/** Remove the project's extension subgraph from Hive (`DELETE …/extension/subgraph`). */
|
|
84
|
+
export async function deleteExtensionSubgraph(baseUrl, projectKey, token) {
|
|
85
|
+
const url = `${apiRoot(baseUrl, projectKey)}/extension/subgraph`;
|
|
86
|
+
const res = await fetch(url, { method: "DELETE", headers: { ...bearer(token) } });
|
|
87
|
+
if (!res.ok && res.status !== 204) {
|
|
88
|
+
const text = await res.text();
|
|
89
|
+
throw new Error(`DELETE extension/subgraph failed (${res.status}): ${text}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/** List the project's extension config entries, secret values redacted (`GET …/extension/config`). */
|
|
93
|
+
export async function listConfig(baseUrl, projectKey, token) {
|
|
94
|
+
const url = `${apiRoot(baseUrl, projectKey)}/extension/config`;
|
|
95
|
+
const res = await fetch(url, { headers: { accept: "application/json", ...bearer(token) } });
|
|
96
|
+
const text = await res.text();
|
|
97
|
+
if (!res.ok) {
|
|
98
|
+
throw new Error(`GET extension/config failed (${res.status}): ${text}`);
|
|
99
|
+
}
|
|
100
|
+
return JSON.parse(text);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Upsert/delete config entries and return the resulting (redacted) list
|
|
104
|
+
* (`PATCH …/extension/config`). An entry with `value: null` deletes that key. PATCH
|
|
105
|
+
* (not PUT) so a single set/unset touches one key without clobbering the rest.
|
|
106
|
+
*/
|
|
107
|
+
export async function patchConfig(baseUrl, projectKey, token, entries) {
|
|
108
|
+
const url = `${apiRoot(baseUrl, projectKey)}/extension/config`;
|
|
109
|
+
const res = await fetch(url, {
|
|
110
|
+
method: "PATCH",
|
|
111
|
+
headers: { "Content-Type": "application/json", ...bearer(token) },
|
|
112
|
+
body: JSON.stringify(entries),
|
|
113
|
+
});
|
|
114
|
+
const text = await res.text();
|
|
115
|
+
if (!res.ok) {
|
|
116
|
+
throw new Error(`PATCH extension/config failed (${res.status}): ${text}`);
|
|
117
|
+
}
|
|
118
|
+
return JSON.parse(text);
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=ilClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ilClient.js","sourceRoot":"","sources":["../../src/lib/ilClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Longest revision the integration layer stores — it truncates past this. Applied
|
|
3
|
+
* here too so what the CLI prints is what actually gets stored, rather than a value
|
|
4
|
+
* the server silently shortens.
|
|
5
|
+
*/
|
|
6
|
+
export declare const MAX_SOURCE_REVISION_LENGTH = 200;
|
|
7
|
+
/**
|
|
8
|
+
* Reduce a revision to printable ASCII, length-capped — matching the integration
|
|
9
|
+
* layer's own rule, so the CLI never reports a value different from the stored one.
|
|
10
|
+
* (The server sanitizes regardless; it doesn't trust its callers. This is about
|
|
11
|
+
* telling the user the truth.)
|
|
12
|
+
*/
|
|
13
|
+
export declare function sanitizeSourceRevision(value: string | undefined): string | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* The git revision describing `cwd`'s working copy, or null when it isn't a git
|
|
16
|
+
* checkout.
|
|
17
|
+
*
|
|
18
|
+
* `git describe --tags --always` gives the most human-meaningful identifier
|
|
19
|
+
* available, in decreasing order of usefulness: an exact tag (`v1.4.2`), a tag plus
|
|
20
|
+
* distance and commit (`v1.4.2-3-g1a2b3c4d`), or — with no tags at all — the bare
|
|
21
|
+
* commit (`1a2b3c4d`). That single command therefore covers "commit hash or tag"
|
|
22
|
+
* without the caller choosing.
|
|
23
|
+
*
|
|
24
|
+
* A `-dirty` suffix is appended from `git status --porcelain -- .` rather than
|
|
25
|
+
* `describe --dirty`, because `--dirty` ignores UNTRACKED files — and an untracked
|
|
26
|
+
* source file that the entry point imports is bundled into the artifact, so a build
|
|
27
|
+
* containing it is genuinely not the tagged commit. Scoped to `-- .` so it reflects
|
|
28
|
+
* the directory being pushed, not unrelated edits elsewhere in a monorepo.
|
|
29
|
+
*/
|
|
30
|
+
export declare function detectGitRevision(cwd: string): Promise<string | null>;
|
|
31
|
+
/**
|
|
32
|
+
* The source revision to record for this push: the explicit value if the caller gave
|
|
33
|
+
* one, else whatever the working copy says, else nothing.
|
|
34
|
+
*
|
|
35
|
+
* Nothing is a legitimate outcome — a bundle built outside version control has no
|
|
36
|
+
* revision, and inventing one ("unknown", a timestamp) would put a value in the
|
|
37
|
+
* Merchant Center and in every connector log line that means nothing.
|
|
38
|
+
*/
|
|
39
|
+
export declare function resolveSourceRevision(explicit: string | undefined, cwd: string): Promise<string | undefined>;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// Where the bundle being pushed came from — the integrator's OWN version-control
|
|
2
|
+
// revision, recorded against the stored bundle by the integration layer.
|
|
3
|
+
//
|
|
4
|
+
// The point is provenance: with this, the Merchant Center shows which revision a
|
|
5
|
+
// project is running, the connector stamps it on every log line, and
|
|
6
|
+
// `{ _extensionBundle { sourceRevision } }` answers it over GraphQL. So the default
|
|
7
|
+
// has to be zero-effort — `extension push` reads the working copy and reports what
|
|
8
|
+
// it finds, with no flag.
|
|
9
|
+
//
|
|
10
|
+
// GIT is auto-detected because that's what the examples and (nearly) every
|
|
11
|
+
// integrator use. Any other VCS — hg, svn, Perforce, a CI build id — is supported
|
|
12
|
+
// through `--source-revision` / `EXTENSION_SOURCE_REVISION` rather than a detector
|
|
13
|
+
// per system: the integration layer treats the value as an OPAQUE string, so
|
|
14
|
+
// whatever identifies a revision to the integrator is the right thing to send.
|
|
15
|
+
import { execFile } from "node:child_process";
|
|
16
|
+
import { promisify } from "node:util";
|
|
17
|
+
const exec = promisify(execFile);
|
|
18
|
+
/**
|
|
19
|
+
* Longest revision the integration layer stores — it truncates past this. Applied
|
|
20
|
+
* here too so what the CLI prints is what actually gets stored, rather than a value
|
|
21
|
+
* the server silently shortens.
|
|
22
|
+
*/
|
|
23
|
+
export const MAX_SOURCE_REVISION_LENGTH = 200;
|
|
24
|
+
/**
|
|
25
|
+
* Reduce a revision to printable ASCII, length-capped — matching the integration
|
|
26
|
+
* layer's own rule, so the CLI never reports a value different from the stored one.
|
|
27
|
+
* (The server sanitizes regardless; it doesn't trust its callers. This is about
|
|
28
|
+
* telling the user the truth.)
|
|
29
|
+
*/
|
|
30
|
+
export function sanitizeSourceRevision(value) {
|
|
31
|
+
if (!value)
|
|
32
|
+
return undefined;
|
|
33
|
+
const cleaned = value
|
|
34
|
+
.replace(/[^\x20-\x7e]/g, "")
|
|
35
|
+
.trim()
|
|
36
|
+
.slice(0, MAX_SOURCE_REVISION_LENGTH);
|
|
37
|
+
return cleaned || undefined;
|
|
38
|
+
}
|
|
39
|
+
// Run a git command in `cwd`, or return null if git isn't available, this isn't a
|
|
40
|
+
// repository, or the command has nothing to say. Provenance is a nice-to-have on a
|
|
41
|
+
// push: never fail the push over it.
|
|
42
|
+
async function git(args, cwd) {
|
|
43
|
+
try {
|
|
44
|
+
const { stdout } = await exec("git", args, { cwd, windowsHide: true });
|
|
45
|
+
const trimmed = stdout.trim();
|
|
46
|
+
return trimmed || null;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The git revision describing `cwd`'s working copy, or null when it isn't a git
|
|
54
|
+
* checkout.
|
|
55
|
+
*
|
|
56
|
+
* `git describe --tags --always` gives the most human-meaningful identifier
|
|
57
|
+
* available, in decreasing order of usefulness: an exact tag (`v1.4.2`), a tag plus
|
|
58
|
+
* distance and commit (`v1.4.2-3-g1a2b3c4d`), or — with no tags at all — the bare
|
|
59
|
+
* commit (`1a2b3c4d`). That single command therefore covers "commit hash or tag"
|
|
60
|
+
* without the caller choosing.
|
|
61
|
+
*
|
|
62
|
+
* A `-dirty` suffix is appended from `git status --porcelain -- .` rather than
|
|
63
|
+
* `describe --dirty`, because `--dirty` ignores UNTRACKED files — and an untracked
|
|
64
|
+
* source file that the entry point imports is bundled into the artifact, so a build
|
|
65
|
+
* containing it is genuinely not the tagged commit. Scoped to `-- .` so it reflects
|
|
66
|
+
* the directory being pushed, not unrelated edits elsewhere in a monorepo.
|
|
67
|
+
*/
|
|
68
|
+
export async function detectGitRevision(cwd) {
|
|
69
|
+
// Cheapest possible "is this a git checkout?" probe — everything below is
|
|
70
|
+
// pointless outside one, and this keeps a non-git working copy silent rather than
|
|
71
|
+
// noisy.
|
|
72
|
+
if (!(await git(["rev-parse", "--git-dir"], cwd)))
|
|
73
|
+
return null;
|
|
74
|
+
const described = await git(["describe", "--tags", "--always", "--abbrev=12"], cwd);
|
|
75
|
+
// A repository with no commits yet describes to nothing — there is no revision to
|
|
76
|
+
// report, so report none.
|
|
77
|
+
if (!described)
|
|
78
|
+
return null;
|
|
79
|
+
const dirt = await git(["status", "--porcelain", "--", "."], cwd);
|
|
80
|
+
return dirt ? `${described}-dirty` : described;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The source revision to record for this push: the explicit value if the caller gave
|
|
84
|
+
* one, else whatever the working copy says, else nothing.
|
|
85
|
+
*
|
|
86
|
+
* Nothing is a legitimate outcome — a bundle built outside version control has no
|
|
87
|
+
* revision, and inventing one ("unknown", a timestamp) would put a value in the
|
|
88
|
+
* Merchant Center and in every connector log line that means nothing.
|
|
89
|
+
*/
|
|
90
|
+
export async function resolveSourceRevision(explicit, cwd) {
|
|
91
|
+
if (explicit !== undefined)
|
|
92
|
+
return sanitizeSourceRevision(explicit);
|
|
93
|
+
return sanitizeSourceRevision((await detectGitRevision(cwd)) ?? undefined);
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=sourceRevision.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sourceRevision.js","sourceRoot":"","sources":["../../src/lib/sourceRevision.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,yEAAyE;AACzE,EAAE;AACF,iFAAiF;AACjF,qEAAqE;AACrE,oFAAoF;AACpF,mFAAmF;AACnF,0BAA0B;AAC1B,EAAE;AACF,2EAA2E;AAC3E,kFAAkF;AAClF,mFAAmF;AACnF,6EAA6E;AAC7E,+EAA+E;AAE/E,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAEjC;;;;GAIG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,GAAG,CAAC;AAE9C;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAyB;IAC9D,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,OAAO,GAAG,KAAK;SAClB,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC;SAC5B,IAAI,EAAE;SACN,KAAK,CAAC,CAAC,EAAE,0BAA0B,CAAC,CAAC;IACxC,OAAO,OAAO,IAAI,SAAS,CAAC;AAC9B,CAAC;AAED,kFAAkF;AAClF,mFAAmF;AACnF,qCAAqC;AACrC,KAAK,UAAU,GAAG,CAAC,IAAc,EAAE,GAAW;IAC5C,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QACvE,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;QAC9B,OAAO,OAAO,IAAI,IAAI,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,GAAW;IACjD,0EAA0E;IAC1E,kFAAkF;IAClF,SAAS;IACT,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,WAAW,EAAE,WAAW,CAAC,EAAE,GAAG,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAE/D,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,CAAC,EAAE,GAAG,CAAC,CAAC;IACpF,kFAAkF;IAClF,0BAA0B;IAC1B,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAE5B,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;IAClE,OAAO,IAAI,CAAC,CAAC,CAAC,GAAG,SAAS,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;AACjD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,QAA4B,EAC5B,GAAW;IAEX,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,sBAAsB,CAAC,QAAQ,CAAC,CAAC;IACpE,OAAO,sBAAsB,CAAC,CAAC,MAAM,iBAAiB,CAAC,GAAG,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC;AAC7E,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ExtensionAction, ExtensionInput } from '@commercetools/platform-sdk';
|
|
2
|
+
export type { ExtensionInput };
|
|
3
|
+
export type ApiExtensionAction = Extract<ExtensionAction, 'Create' | 'Update'>;
|
|
4
|
+
export interface ExtensionContext {
|
|
5
|
+
now(): number;
|
|
6
|
+
config: Readonly<Record<string, string>>;
|
|
7
|
+
}
|
|
8
|
+
export type ApiExtensionInput = ExtensionInput;
|
|
9
|
+
export interface ApiExtensionError {
|
|
10
|
+
code: string;
|
|
11
|
+
message: string;
|
|
12
|
+
}
|
|
13
|
+
export type ApiExtensionResult = void | {
|
|
14
|
+
actions?: unknown[];
|
|
15
|
+
errors?: ApiExtensionError[];
|
|
16
|
+
};
|
|
17
|
+
export interface ApiExtensionDefinition {
|
|
18
|
+
key: string;
|
|
19
|
+
resourceTypeId: string;
|
|
20
|
+
actions: ApiExtensionAction[];
|
|
21
|
+
condition?: string;
|
|
22
|
+
handler: (input: ApiExtensionInput, ctx: ExtensionContext) => ApiExtensionResult | Promise<ApiExtensionResult>;
|
|
23
|
+
}
|
|
24
|
+
/** Identity helper — declare an API-Extension handler, type-checked against the SDK payload. */
|
|
25
|
+
export declare function defineApiExtension(definition: ApiExtensionDefinition): ApiExtensionDefinition;
|
|
26
|
+
/** Approve the write unchanged. */
|
|
27
|
+
export declare function approve(): ApiExtensionResult;
|
|
28
|
+
/** Block the write with a validation error (commercetools returns it to the caller). */
|
|
29
|
+
export declare function block(code: string, message: string): ApiExtensionResult;
|
|
30
|
+
/** Apply commercetools update actions to the triggering resource. */
|
|
31
|
+
export declare function update(actions: unknown[]): ApiExtensionResult;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Authoring API for commercetools API Extensions.
|
|
2
|
+
//
|
|
3
|
+
// Copied verbatim from commercetools/integration-layer-extension-examples
|
|
4
|
+
// (packages/tooling/src/apiExtension.ts). An API Extension is the commercetools
|
|
5
|
+
// platform feature: a synchronous HTTP callback commercetools makes before
|
|
6
|
+
// persisting a cart/order/… create or update, which can approve, modify, or BLOCK
|
|
7
|
+
// the write. This is a DIFFERENT thing from a GraphQL schema extension (typeDefs +
|
|
8
|
+
// resolvers), which adds fields to the graph. A bundle may export `apiExtensions`,
|
|
9
|
+
// `typeDefs`/`resolvers`, or both.
|
|
10
|
+
//
|
|
11
|
+
// A handler is a plain function `(input, ctx) => result`. Use `defineApiExtension`
|
|
12
|
+
// for type checking and the `approve` / `block` / `update` helpers to return intent
|
|
13
|
+
// instead of hand-rolling the wire shape. The runtime (the sandbox) maps the result
|
|
14
|
+
// to commercetools' response contract.
|
|
15
|
+
//
|
|
16
|
+
// The RECEIVED payload is the commercetools SDK's own `ExtensionInput` — you don't
|
|
17
|
+
// define the resource types. Its `resource` is the discriminated `Reference` union,
|
|
18
|
+
// so narrowing on `input.resource.typeId === 'cart'` gives you `resource.obj` typed
|
|
19
|
+
// as the real `Cart` (and so on). These are type-only imports (erased by the
|
|
20
|
+
// bundler), so they add nothing to the pushed bundle. The SDK does NOT model the
|
|
21
|
+
// RESPONSE envelope (approve/block/actions is an HTTP contract, not a resource), so
|
|
22
|
+
// `ApiExtensionResult` / `ApiExtensionError` below stay local.
|
|
23
|
+
/** Identity helper — declare an API-Extension handler, type-checked against the SDK payload. */
|
|
24
|
+
export function defineApiExtension(definition) {
|
|
25
|
+
return definition;
|
|
26
|
+
}
|
|
27
|
+
/** Approve the write unchanged. */
|
|
28
|
+
export function approve() {
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
/** Block the write with a validation error (commercetools returns it to the caller). */
|
|
32
|
+
export function block(code, message) {
|
|
33
|
+
return { errors: [{ code, message }] };
|
|
34
|
+
}
|
|
35
|
+
/** Apply commercetools update actions to the triggering resource. */
|
|
36
|
+
export function update(actions) {
|
|
37
|
+
return { actions };
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=apiExtension.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiExtension.js","sourceRoot":"","sources":["../../../src/lib/tooling/apiExtension.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,EAAE;AACF,0EAA0E;AAC1E,gFAAgF;AAChF,2EAA2E;AAC3E,kFAAkF;AAClF,mFAAmF;AACnF,mFAAmF;AACnF,mCAAmC;AACnC,EAAE;AACF,mFAAmF;AACnF,oFAAoF;AACpF,oFAAoF;AACpF,uCAAuC;AACvC,EAAE;AACF,mFAAmF;AACnF,oFAAoF;AACpF,oFAAoF;AACpF,6EAA6E;AAC7E,iFAAiF;AACjF,oFAAoF;AACpF,+DAA+D;AA+C/D,gGAAgG;AAChG,MAAM,UAAU,kBAAkB,CAAC,UAAkC;IACnE,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,OAAO;IACrB,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,KAAK,CAAC,IAAY,EAAE,OAAe;IACjD,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;AACzC,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,MAAM,CAAC,OAAkB;IACvC,OAAO,EAAE,OAAO,EAAE,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Peer modules the runtime owns — bundled never, resolved at load time. */
|
|
2
|
+
export declare const HOST_PROVIDED_EXTERNALS: string[];
|
|
3
|
+
/** The extension source of the example the tool is invoked inside (cwd-relative). */
|
|
4
|
+
export declare function defaultEntry(): string;
|
|
5
|
+
/** The bundle output for the example the tool is invoked inside (cwd-relative). */
|
|
6
|
+
export declare function defaultOutfile(): string;
|
|
7
|
+
export interface BuildResult {
|
|
8
|
+
/** Absolute path to the bundled artifact. */
|
|
9
|
+
outfile: string;
|
|
10
|
+
/**
|
|
11
|
+
* The author's own source files that went into the bundle (entry + local
|
|
12
|
+
* imports), absolute paths. npm packages are excluded — these feed the static
|
|
13
|
+
* analyzer, which only checks code the author wrote.
|
|
14
|
+
*/
|
|
15
|
+
sourceFiles: string[];
|
|
16
|
+
}
|
|
17
|
+
/** Bundle `entry` into a single self-contained CJS file at `outfile`. */
|
|
18
|
+
export declare function buildBundle(entry?: string, outfile?: string): Promise<BuildResult>;
|