@lukoweb/apitogo 0.2.50 → 0.2.51
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/dist/cli/cli.js +34 -18
- package/dist/declarations/lib/plugins/openapi/util/createHttpSnippet.d.ts +2 -0
- package/package.json +1 -1
- package/src/config/v2/full-server.ts +1 -1
- package/src/config/v2/gateway-reference.ts +15 -2
- package/src/config/v2/init.ts +1 -1
- package/src/config/v2/starter-landing.ts +13 -6
- package/src/config/v2/wrapper.ts +8 -0
- package/src/lib/plugins/openapi/util/createHttpSnippet.ts +147 -135
package/dist/cli/cli.js
CHANGED
|
@@ -6268,7 +6268,7 @@ title: Welcome
|
|
|
6268
6268
|
|
|
6269
6269
|
# ${name}
|
|
6270
6270
|
|
|
6271
|
-
|
|
6271
|
+
Welcome to the ${name} developer portal. Here you will find guides, the API reference, and everything you need to get an API key and make your first call.
|
|
6272
6272
|
`;
|
|
6273
6273
|
}
|
|
6274
6274
|
async function docsFromPaths(dir, requested) {
|
|
@@ -7657,7 +7657,8 @@ async function removeServicesFromManifest(state, names) {
|
|
|
7657
7657
|
// src/config/v2/gateway-reference.ts
|
|
7658
7658
|
import { readFile as readFile6 } from "node:fs/promises";
|
|
7659
7659
|
import { parse } from "yaml";
|
|
7660
|
-
var DEFAULT_KEY_HEADER = "
|
|
7660
|
+
var DEFAULT_KEY_HEADER = "X-API-Key";
|
|
7661
|
+
var KEY_HEADER_EXTENSION = "x-apitogo-key-header";
|
|
7661
7662
|
var METHODS = [
|
|
7662
7663
|
"get",
|
|
7663
7664
|
"put",
|
|
@@ -7770,10 +7771,13 @@ function buildReferenceSpec(specText, options) {
|
|
|
7770
7771
|
if (Object.keys(components).length > 0) doc.components = components;
|
|
7771
7772
|
else delete doc.components;
|
|
7772
7773
|
doc.security = Object.keys(requirement).length > 0 ? [requirement] : [];
|
|
7773
|
-
for (const [, item, method] of operationsOf(paths)) {
|
|
7774
|
+
for (const [path97, item, method] of operationsOf(paths)) {
|
|
7774
7775
|
const operation = item[method];
|
|
7775
7776
|
if (Array.isArray(operation.security) && operation.security.length > 0)
|
|
7776
7777
|
delete operation.security;
|
|
7778
|
+
if (options.apiKeyRequired && !options.publicOperations.has(actionKey(method, path97)))
|
|
7779
|
+
operation[KEY_HEADER_EXTENSION] = options.apiKeyHeader;
|
|
7780
|
+
else delete operation[KEY_HEADER_EXTENSION];
|
|
7777
7781
|
}
|
|
7778
7782
|
return JSON.parse(hideOrigin(JSON.stringify(doc), options));
|
|
7779
7783
|
}
|
|
@@ -8089,6 +8093,8 @@ function planPortal(profile, manifest) {
|
|
|
8089
8093
|
apiBaseUrl: plans.apiBaseUrl,
|
|
8090
8094
|
keysApiBaseUrl: plans.keysApiBaseUrl,
|
|
8091
8095
|
billing: plans.billing,
|
|
8096
|
+
...gateway?.keyHeaderName ? { keyHeader: gateway.keyHeaderName } : {},
|
|
8097
|
+
...gateway?.baseUrl && serviceReady(gateway) ? { gatewayBaseUrl: gateway.baseUrl } : {},
|
|
8092
8098
|
links: {
|
|
8093
8099
|
// Hosted docs are linked once their pages are known (see `buildPortalWorkspace`): there is no page to
|
|
8094
8100
|
// name before that, and a guessed one was a 404 on every portal whose first page is not called `introduction`.
|
|
@@ -8630,7 +8636,7 @@ var FULL_SPEC = (name) => `openapi: 3.1.0
|
|
|
8630
8636
|
info:
|
|
8631
8637
|
title: ${JSON.stringify(name)}
|
|
8632
8638
|
version: 1.0.0
|
|
8633
|
-
description: A
|
|
8639
|
+
description: A sample API with a greeting and a list of items.
|
|
8634
8640
|
paths:
|
|
8635
8641
|
/health:
|
|
8636
8642
|
get:
|
|
@@ -8719,10 +8725,14 @@ components:
|
|
|
8719
8725
|
// src/config/v2/starter-landing.ts
|
|
8720
8726
|
function starterLandingPage(options) {
|
|
8721
8727
|
const name = JSON.stringify(options.name);
|
|
8722
|
-
const plansImport = options.withPlans ? 'import {
|
|
8723
|
-
const plansHook = options.withPlans ? " // The plans of the project's catalog: the same ones the pricing page shows.\n const plans = usePlansPreview();\n\n" : "";
|
|
8728
|
+
const plansImport = options.withPlans ? 'import {\n useGatewayCall,\n usePlansPreview,\n} from "@lukoweb/apitogo-plugin-dev-portal-billing";\n' : "";
|
|
8729
|
+
const plansHook = options.withPlans ? " // The plans of the project's catalog: the same ones the pricing page shows.\n const plans = usePlansPreview();\n // The real gateway address and key header, so the sample works when it is pasted with a key.\n const gateway = useGatewayCall();\n\n" : "";
|
|
8730
|
+
const sampleUrl = options.withPlans ? '${gateway.baseUrl ?? "https://YOUR-GATEWAY"}' : "https://YOUR-GATEWAY";
|
|
8731
|
+
const sampleHeader = options.withPlans ? "${gateway.keyHeader}" : "X-API-Key";
|
|
8724
8732
|
const plansSection = options.withPlans ? " <PlansPreviewSection plans={plans} />\n" : "";
|
|
8725
|
-
return
|
|
8733
|
+
return `// Your portal's home page. Edit this file (apitogo/portal/landing/LandingPage.tsx) to make it yours: everything
|
|
8734
|
+
// on it is public, so keep notes for yourself in comments like this one.
|
|
8735
|
+
import {
|
|
8726
8736
|
ApiShowcaseSection,
|
|
8727
8737
|
FeaturesSection,
|
|
8728
8738
|
HeroSection,
|
|
@@ -8752,8 +8762,8 @@ ${plansHook} return (
|
|
|
8752
8762
|
{
|
|
8753
8763
|
filename: "Call the API",
|
|
8754
8764
|
language: "bash",
|
|
8755
|
-
code: \`$ curl
|
|
8756
|
-
-H "
|
|
8765
|
+
code: \`$ curl ${sampleUrl}/health \\\\
|
|
8766
|
+
-H "${sampleHeader}: YOUR_API_KEY"
|
|
8757
8767
|
# { "status": "ok" }\`,
|
|
8758
8768
|
},
|
|
8759
8769
|
]}
|
|
@@ -8763,7 +8773,7 @@ ${plansHook} return (
|
|
|
8763
8773
|
label: "What you get",
|
|
8764
8774
|
title: "A developer portal that is ready on day one",
|
|
8765
8775
|
description:
|
|
8766
|
-
"
|
|
8776
|
+
"Docs, a live API reference, self-service API keys and clear plans, all in one place.",
|
|
8767
8777
|
}}
|
|
8768
8778
|
features={[
|
|
8769
8779
|
{
|
|
@@ -10771,7 +10781,7 @@ import {
|
|
|
10771
10781
|
// package.json
|
|
10772
10782
|
var package_default = {
|
|
10773
10783
|
name: "@lukoweb/apitogo",
|
|
10774
|
-
version: "0.2.
|
|
10784
|
+
version: "0.2.51",
|
|
10775
10785
|
type: "module",
|
|
10776
10786
|
sideEffects: [
|
|
10777
10787
|
"**/*.css",
|
|
@@ -16080,7 +16090,7 @@ async function onlinePlansRules(resolved, projectId, api) {
|
|
|
16080
16090
|
const convert = (item) => issue3(
|
|
16081
16091
|
"PLANS_PLAN_INVALID",
|
|
16082
16092
|
`plans.file ${item.field}`,
|
|
16083
|
-
|
|
16093
|
+
withPlanName(item.message, item.sku)
|
|
16084
16094
|
);
|
|
16085
16095
|
return {
|
|
16086
16096
|
errors: answer.errors.map(convert),
|
|
@@ -16094,6 +16104,7 @@ async function onlinePlansRules(resolved, projectId, api) {
|
|
|
16094
16104
|
};
|
|
16095
16105
|
}
|
|
16096
16106
|
}
|
|
16107
|
+
var withPlanName = (message, sku) => !sku || message.toLowerCase().startsWith(`plan "${sku.toLowerCase()}"`) ? message : `plan ${sku}: ${message}`;
|
|
16097
16108
|
|
|
16098
16109
|
// src/cli/check/v2.ts
|
|
16099
16110
|
async function isConfigV2(dir) {
|
|
@@ -19096,10 +19107,13 @@ export default function ${pageComponentName(slug)}() {
|
|
|
19096
19107
|
<Typography>
|
|
19097
19108
|
<Heading level={1}>{title}</Heading>
|
|
19098
19109
|
<p>${text}</p>
|
|
19099
|
-
|
|
19100
|
-
|
|
19101
|
-
|
|
19102
|
-
|
|
19110
|
+
{/* A note for you while you run the portal locally (\`apitogo dev\`); the deployed portal does not show it. */}
|
|
19111
|
+
{import.meta.env.DEV && (
|
|
19112
|
+
<Callout type="info" title="Where this page lives">
|
|
19113
|
+
Edit <code>${file}</code>. Files whose name starts with <code>_</code> are not pages, so you can keep
|
|
19114
|
+
shared components next to your pages.
|
|
19115
|
+
</Callout>
|
|
19116
|
+
)}
|
|
19103
19117
|
</Typography>
|
|
19104
19118
|
</section>
|
|
19105
19119
|
);
|
|
@@ -21330,7 +21344,7 @@ function buildPostPublishChecklist(args) {
|
|
|
21330
21344
|
if (args.gatewayUrl) {
|
|
21331
21345
|
items.push({
|
|
21332
21346
|
id: "test_gateway",
|
|
21333
|
-
label: "Call the API gateway with
|
|
21347
|
+
label: "Call the API gateway with X-API-Key",
|
|
21334
21348
|
required: false,
|
|
21335
21349
|
completed: false,
|
|
21336
21350
|
url: args.gatewayUrl
|
|
@@ -26619,7 +26633,9 @@ async function deployGateway(resolved, projectId, deps, options = {}) {
|
|
|
26619
26633
|
baseUrl,
|
|
26620
26634
|
capabilities: capabilities.capabilities,
|
|
26621
26635
|
openapiUrl: status2.openApiPath ?? void 0,
|
|
26622
|
-
openapiHash: status2.openApiHash ?? status2.specSha256 ?? void 0
|
|
26636
|
+
openapiHash: status2.openApiHash ?? status2.specSha256 ?? void 0,
|
|
26637
|
+
// The portal's keys page and API page show this header (API-120).
|
|
26638
|
+
keyHeaderName: resolved.gateway?.policies.access?.apiKeyHeader ?? DEFAULT_KEY_HEADER
|
|
26623
26639
|
})
|
|
26624
26640
|
);
|
|
26625
26641
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { HTTPSnippet } from "@zudoku/httpsnippet";
|
|
2
2
|
import type { OperationsFragmentFragment } from "../graphql/graphql.js";
|
|
3
|
+
export declare const KEY_HEADER_EXTENSION = "x-apitogo-key-header";
|
|
4
|
+
export declare const KEY_PLACEHOLDER = "YOUR_API_KEY";
|
|
3
5
|
export declare const createHttpSnippet: ({ operation, selectedServer, exampleBody, }: {
|
|
4
6
|
operation: OperationsFragmentFragment;
|
|
5
7
|
selectedServer: string;
|
package/package.json
CHANGED
|
@@ -105,7 +105,7 @@ export const FULL_SPEC = (name: string) => `openapi: 3.1.0
|
|
|
105
105
|
info:
|
|
106
106
|
title: ${JSON.stringify(name)}
|
|
107
107
|
version: 1.0.0
|
|
108
|
-
description: A
|
|
108
|
+
description: A sample API with a greeting and a list of items.
|
|
109
109
|
paths:
|
|
110
110
|
/health:
|
|
111
111
|
get:
|
|
@@ -18,7 +18,13 @@ export type ReferenceOptions = {
|
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
/** Header of the API key when the gateway does not say otherwise (same as the platform's OpenAPI export). */
|
|
21
|
-
export const DEFAULT_KEY_HEADER = "
|
|
21
|
+
export const DEFAULT_KEY_HEADER = "X-API-Key";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Operation extension naming the header a protected operation's key goes in. The API page's code samples read it
|
|
25
|
+
* (`createHttpSnippet`), so they show the header with a placeholder key.
|
|
26
|
+
*/
|
|
27
|
+
export const KEY_HEADER_EXTENSION = "x-apitogo-key-header";
|
|
22
28
|
|
|
23
29
|
const METHODS = [
|
|
24
30
|
"get",
|
|
@@ -175,10 +181,17 @@ export function buildReferenceSpec(
|
|
|
175
181
|
else delete doc.components;
|
|
176
182
|
// One requirement with both schemes means both are needed together.
|
|
177
183
|
doc.security = Object.keys(requirement).length > 0 ? [requirement] : [];
|
|
178
|
-
for (const [, item, method] of operationsOf(paths)) {
|
|
184
|
+
for (const [path, item, method] of operationsOf(paths)) {
|
|
179
185
|
const operation = item[method] as Json;
|
|
180
186
|
if (Array.isArray(operation.security) && operation.security.length > 0)
|
|
181
187
|
delete operation.security;
|
|
188
|
+
// The page's code samples add this header, so a copied sample works once the key is filled in (API-120).
|
|
189
|
+
if (
|
|
190
|
+
options.apiKeyRequired &&
|
|
191
|
+
!options.publicOperations.has(actionKey(method, path))
|
|
192
|
+
)
|
|
193
|
+
operation[KEY_HEADER_EXTENSION] = options.apiKeyHeader;
|
|
194
|
+
else delete operation[KEY_HEADER_EXTENSION];
|
|
182
195
|
}
|
|
183
196
|
|
|
184
197
|
return JSON.parse(hideOrigin(JSON.stringify(doc), options)) as Json;
|
package/src/config/v2/init.ts
CHANGED
|
@@ -59,7 +59,7 @@ title: Welcome
|
|
|
59
59
|
|
|
60
60
|
# ${name}
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
Welcome to the ${name} developer portal. Here you will find guides, the API reference, and everything you need to get an API key and make your first call.
|
|
63
63
|
`;
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -9,15 +9,22 @@ export function starterLandingPage(options: {
|
|
|
9
9
|
}): string {
|
|
10
10
|
const name = JSON.stringify(options.name);
|
|
11
11
|
const plansImport = options.withPlans
|
|
12
|
-
? 'import {
|
|
12
|
+
? 'import {\n useGatewayCall,\n usePlansPreview,\n} from "@lukoweb/apitogo-plugin-dev-portal-billing";\n'
|
|
13
13
|
: "";
|
|
14
14
|
const plansHook = options.withPlans
|
|
15
|
-
? " // The plans of the project's catalog: the same ones the pricing page shows.\n const plans = usePlansPreview();\n\n"
|
|
15
|
+
? " // The plans of the project's catalog: the same ones the pricing page shows.\n const plans = usePlansPreview();\n // The real gateway address and key header, so the sample works when it is pasted with a key.\n const gateway = useGatewayCall();\n\n"
|
|
16
16
|
: "";
|
|
17
|
+
// Without the plans service the page cannot learn the gateway, so the sample names the platform's default header.
|
|
18
|
+
const sampleUrl = options.withPlans
|
|
19
|
+
? '${gateway.baseUrl ?? "https://YOUR-GATEWAY"}'
|
|
20
|
+
: "https://YOUR-GATEWAY";
|
|
21
|
+
const sampleHeader = options.withPlans ? "${gateway.keyHeader}" : "X-API-Key";
|
|
17
22
|
const plansSection = options.withPlans
|
|
18
23
|
? " <PlansPreviewSection plans={plans} />\n"
|
|
19
24
|
: "";
|
|
20
|
-
return
|
|
25
|
+
return `// Your portal's home page. Edit this file (apitogo/portal/landing/LandingPage.tsx) to make it yours: everything
|
|
26
|
+
// on it is public, so keep notes for yourself in comments like this one.
|
|
27
|
+
import {
|
|
21
28
|
ApiShowcaseSection,
|
|
22
29
|
FeaturesSection,
|
|
23
30
|
HeroSection,
|
|
@@ -47,8 +54,8 @@ ${plansHook} return (
|
|
|
47
54
|
{
|
|
48
55
|
filename: "Call the API",
|
|
49
56
|
language: "bash",
|
|
50
|
-
code: \`$ curl
|
|
51
|
-
-H "
|
|
57
|
+
code: \`$ curl ${sampleUrl}/health \\\\
|
|
58
|
+
-H "${sampleHeader}: YOUR_API_KEY"
|
|
52
59
|
# { "status": "ok" }\`,
|
|
53
60
|
},
|
|
54
61
|
]}
|
|
@@ -58,7 +65,7 @@ ${plansHook} return (
|
|
|
58
65
|
label: "What you get",
|
|
59
66
|
title: "A developer portal that is ready on day one",
|
|
60
67
|
description:
|
|
61
|
-
"
|
|
68
|
+
"Docs, a live API reference, self-service API keys and clear plans, all in one place.",
|
|
62
69
|
}}
|
|
63
70
|
features={[
|
|
64
71
|
{
|
package/src/config/v2/wrapper.ts
CHANGED
|
@@ -77,6 +77,10 @@ export type PortalPlan = {
|
|
|
77
77
|
billing?: string;
|
|
78
78
|
/** Where the account pages link to: only places this portal has (hosted docs, an external docs site, the API page). */
|
|
79
79
|
links?: { docs?: string; api?: string };
|
|
80
|
+
/** The header API users send their key in, from the gateway entry of services.json (API-120). */
|
|
81
|
+
keyHeader?: string;
|
|
82
|
+
/** The gateway's address, for code samples on the portal's pages. */
|
|
83
|
+
gatewayBaseUrl?: string;
|
|
80
84
|
};
|
|
81
85
|
/** Account pages. */
|
|
82
86
|
userPanel: boolean;
|
|
@@ -259,6 +263,10 @@ export function planPortal(
|
|
|
259
263
|
apiBaseUrl: plans.apiBaseUrl,
|
|
260
264
|
keysApiBaseUrl: plans.keysApiBaseUrl,
|
|
261
265
|
billing: plans.billing,
|
|
266
|
+
...(gateway?.keyHeaderName ? { keyHeader: gateway.keyHeaderName } : {}),
|
|
267
|
+
...(gateway?.baseUrl && serviceReady(gateway)
|
|
268
|
+
? { gatewayBaseUrl: gateway.baseUrl }
|
|
269
|
+
: {}),
|
|
262
270
|
links: {
|
|
263
271
|
// Hosted docs are linked once their pages are known (see `buildPortalWorkspace`): there is no page to
|
|
264
272
|
// name before that, and a guessed one was a 404 on every portal whose first page is not called `introduction`.
|
|
@@ -1,135 +1,147 @@
|
|
|
1
|
-
import { HTTPSnippet } from "@zudoku/httpsnippet";
|
|
2
|
-
import type { OperationsFragmentFragment } from "../graphql/graphql.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
case "
|
|
109
|
-
converted = snippet.convert("
|
|
110
|
-
break;
|
|
111
|
-
case "
|
|
112
|
-
converted = snippet.convert("
|
|
113
|
-
break;
|
|
114
|
-
case "
|
|
115
|
-
converted = snippet.convert("
|
|
116
|
-
break;
|
|
117
|
-
case "
|
|
118
|
-
converted = snippet.convert("
|
|
119
|
-
break;
|
|
120
|
-
case "
|
|
121
|
-
converted = snippet.convert("
|
|
122
|
-
break;
|
|
123
|
-
case "
|
|
124
|
-
converted = snippet.convert("
|
|
125
|
-
break;
|
|
126
|
-
case "
|
|
127
|
-
converted = snippet.convert("
|
|
128
|
-
break;
|
|
129
|
-
|
|
130
|
-
converted = snippet.convert("
|
|
131
|
-
break;
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
1
|
+
import { HTTPSnippet } from "@zudoku/httpsnippet";
|
|
2
|
+
import type { OperationsFragmentFragment } from "../graphql/graphql.js";
|
|
3
|
+
|
|
4
|
+
/** Operation extension with the header a key goes in (same name as `KEY_HEADER_EXTENSION` in the CLI's config). */
|
|
5
|
+
export const KEY_HEADER_EXTENSION = "x-apitogo-key-header";
|
|
6
|
+
|
|
7
|
+
/** What a code sample shows where the visitor's key goes. */
|
|
8
|
+
export const KEY_PLACEHOLDER = "YOUR_API_KEY";
|
|
9
|
+
|
|
10
|
+
const toFormDataParams = (text?: string) => {
|
|
11
|
+
const stringify = (v: unknown) =>
|
|
12
|
+
typeof v === "string" ? v : JSON.stringify(v);
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const obj = text && JSON.parse(text);
|
|
16
|
+
if (typeof obj !== "object" || !obj) return [];
|
|
17
|
+
|
|
18
|
+
return Object.entries(obj).flatMap(([name, value]) => {
|
|
19
|
+
const values = Array.isArray(value) ? value : [value];
|
|
20
|
+
return values.map((v) => ({ name, value: stringify(v) }));
|
|
21
|
+
});
|
|
22
|
+
} catch {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const createHttpSnippet = ({
|
|
28
|
+
operation,
|
|
29
|
+
selectedServer,
|
|
30
|
+
exampleBody,
|
|
31
|
+
}: {
|
|
32
|
+
operation: OperationsFragmentFragment;
|
|
33
|
+
selectedServer: string;
|
|
34
|
+
exampleBody: {
|
|
35
|
+
mimeType: string;
|
|
36
|
+
text?: string;
|
|
37
|
+
};
|
|
38
|
+
}) => {
|
|
39
|
+
const isMultipart =
|
|
40
|
+
exampleBody.mimeType === "multipart/form-data" ||
|
|
41
|
+
exampleBody.mimeType === "application/x-www-form-urlencoded";
|
|
42
|
+
|
|
43
|
+
const postData = isMultipart
|
|
44
|
+
? {
|
|
45
|
+
mimeType: exampleBody.mimeType,
|
|
46
|
+
params: toFormDataParams(exampleBody.text),
|
|
47
|
+
}
|
|
48
|
+
: exampleBody;
|
|
49
|
+
|
|
50
|
+
const keyHeader = operation.extensions?.[KEY_HEADER_EXTENSION];
|
|
51
|
+
|
|
52
|
+
return new HTTPSnippet({
|
|
53
|
+
method: operation.method.toUpperCase(),
|
|
54
|
+
url:
|
|
55
|
+
selectedServer + operation.path.replaceAll("{", ":").replaceAll("}", ""),
|
|
56
|
+
postData,
|
|
57
|
+
headers: [
|
|
58
|
+
// The gateway's key header, on operations that need a key (set by the portal build, API-120).
|
|
59
|
+
...(typeof keyHeader === "string" && keyHeader
|
|
60
|
+
? [{ name: keyHeader, value: KEY_PLACEHOLDER }]
|
|
61
|
+
: []),
|
|
62
|
+
...(exampleBody.text
|
|
63
|
+
? [{ name: "Content-Type", value: exampleBody.mimeType }]
|
|
64
|
+
: []),
|
|
65
|
+
...(operation.parameters
|
|
66
|
+
?.filter((p) => p.in === "header" && p.required === true)
|
|
67
|
+
.map((p) => ({
|
|
68
|
+
name: p.name,
|
|
69
|
+
value:
|
|
70
|
+
p.schema?.default ??
|
|
71
|
+
p.examples?.find((x) => x.value)?.value ??
|
|
72
|
+
(p.schema?.type === "string"
|
|
73
|
+
? "<string>"
|
|
74
|
+
: p.schema?.type === "number" || p.schema?.type === "integer"
|
|
75
|
+
? "<number>"
|
|
76
|
+
: p.schema?.type === "boolean"
|
|
77
|
+
? "<bool>"
|
|
78
|
+
: "<value>"),
|
|
79
|
+
})) ?? []),
|
|
80
|
+
],
|
|
81
|
+
queryString:
|
|
82
|
+
operation.parameters
|
|
83
|
+
?.filter((p) => p.in === "query" && p.required === true)
|
|
84
|
+
.map((p) => ({
|
|
85
|
+
name: p.name,
|
|
86
|
+
value:
|
|
87
|
+
p.schema?.default ??
|
|
88
|
+
p.examples?.find((x) => x.value)?.value ??
|
|
89
|
+
(p.schema?.type === "string"
|
|
90
|
+
? "<string>"
|
|
91
|
+
: p.schema?.type === "number" || p.schema?.type === "integer"
|
|
92
|
+
? "<number>"
|
|
93
|
+
: p.schema?.type === "boolean"
|
|
94
|
+
? "<bool>"
|
|
95
|
+
: "<value>"),
|
|
96
|
+
})) ?? [],
|
|
97
|
+
httpVersion: "",
|
|
98
|
+
cookies: [],
|
|
99
|
+
headersSize: 0,
|
|
100
|
+
bodySize: 0,
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export const getConverted = (snippet: HTTPSnippet, option: string) => {
|
|
105
|
+
// biome-ignore lint/suspicious/noExplicitAny: Allow any type
|
|
106
|
+
let converted: any;
|
|
107
|
+
switch (option) {
|
|
108
|
+
case "shell":
|
|
109
|
+
converted = snippet.convert("shell", "curl");
|
|
110
|
+
break;
|
|
111
|
+
case "js":
|
|
112
|
+
converted = snippet.convert("javascript", "fetch");
|
|
113
|
+
break;
|
|
114
|
+
case "python":
|
|
115
|
+
converted = snippet.convert("python", "requests");
|
|
116
|
+
break;
|
|
117
|
+
case "java":
|
|
118
|
+
converted = snippet.convert("java", "okhttp");
|
|
119
|
+
break;
|
|
120
|
+
case "go":
|
|
121
|
+
converted = snippet.convert("go", "native");
|
|
122
|
+
break;
|
|
123
|
+
case "csharp":
|
|
124
|
+
converted = snippet.convert("csharp", "httpclient");
|
|
125
|
+
break;
|
|
126
|
+
case "kotlin":
|
|
127
|
+
converted = snippet.convert("kotlin", "okhttp");
|
|
128
|
+
break;
|
|
129
|
+
case "objc":
|
|
130
|
+
converted = snippet.convert("objc", "nsurlsession");
|
|
131
|
+
break;
|
|
132
|
+
case "php":
|
|
133
|
+
converted = snippet.convert("php", "http2");
|
|
134
|
+
break;
|
|
135
|
+
case "ruby":
|
|
136
|
+
converted = snippet.convert("ruby");
|
|
137
|
+
break;
|
|
138
|
+
case "swift":
|
|
139
|
+
converted = snippet.convert("swift");
|
|
140
|
+
break;
|
|
141
|
+
default:
|
|
142
|
+
converted = snippet.convert("shell");
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return converted ? converted[0] : "";
|
|
147
|
+
};
|