mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-19 17:15:24 +00:00
Adapt OpenAPI code samples to prefill API key using visitor data (#3666)
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"@gitbook/react-openapi": patch
|
||||||
|
"gitbook": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Adapt OpenAPI code samples to prefill API key using visitor data
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import type { DocumentBlockCode, JSONDocument } from '@gitbook/api';
|
import type { JSONDocument } from '@gitbook/api';
|
||||||
import { useId } from 'react';
|
import { useId } from 'react';
|
||||||
|
|
||||||
import { CodeBlock } from './CodeBlock';
|
import { CodeBlock } from './CodeBlock';
|
||||||
|
import { convertCodeStringToBlock } from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plain code block with syntax highlighting.
|
* Plain code block with syntax highlighting.
|
||||||
@@ -11,31 +12,7 @@ export function PlainCodeBlock(props: { code: string; syntax: string }) {
|
|||||||
const { code, syntax } = props;
|
const { code, syntax } = props;
|
||||||
const id = useId();
|
const id = useId();
|
||||||
|
|
||||||
const block: DocumentBlockCode = {
|
const block = convertCodeStringToBlock({ key: id, code, syntax });
|
||||||
key: id,
|
|
||||||
object: 'block',
|
|
||||||
type: 'code',
|
|
||||||
data: {
|
|
||||||
syntax,
|
|
||||||
},
|
|
||||||
nodes: code.split('\n').map((line) => ({
|
|
||||||
object: 'block',
|
|
||||||
type: 'code-line',
|
|
||||||
data: {},
|
|
||||||
nodes: [
|
|
||||||
{
|
|
||||||
object: 'text',
|
|
||||||
leaves: [
|
|
||||||
{
|
|
||||||
object: 'leaf',
|
|
||||||
text: line,
|
|
||||||
marks: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
|
|
||||||
const document: JSONDocument = {
|
const document: JSONDocument = {
|
||||||
object: 'document',
|
object: 'document',
|
||||||
|
|||||||
@@ -0,0 +1,219 @@
|
|||||||
|
import { describe, expect, it } from 'bun:test';
|
||||||
|
import { convertCodeStringToBlock } from './utils';
|
||||||
|
|
||||||
|
describe('convertCodeStringToBlock', () => {
|
||||||
|
it('converts plain code string without placeholders', () => {
|
||||||
|
const result = convertCodeStringToBlock({
|
||||||
|
key: 'test1',
|
||||||
|
code: 'console.log("hello");',
|
||||||
|
syntax: 'javascript',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
key: 'test1',
|
||||||
|
object: 'block',
|
||||||
|
type: 'code',
|
||||||
|
data: { syntax: 'javascript' },
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
object: 'block',
|
||||||
|
type: 'code-line',
|
||||||
|
data: {},
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
object: 'text',
|
||||||
|
leaves: [{ object: 'leaf', text: 'console.log("hello");', marks: [] }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('converts a single placeholder into an expression node', () => {
|
||||||
|
const result = convertCodeStringToBlock({
|
||||||
|
key: 'test2',
|
||||||
|
code: 'const config = { API_KEY: "$$__X-GITBOOK-PREFILL[visitor.claims.apiKey ?? "YOUR_API_KEY"]__$$" }',
|
||||||
|
syntax: 'javascript',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
key: 'test2',
|
||||||
|
object: 'block',
|
||||||
|
type: 'code',
|
||||||
|
data: { syntax: 'javascript' },
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
object: 'block',
|
||||||
|
type: 'code-line',
|
||||||
|
data: {},
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
object: 'text',
|
||||||
|
leaves: [
|
||||||
|
{ object: 'leaf', text: 'const config = { API_KEY: "', marks: [] },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: 'inline',
|
||||||
|
type: 'expression',
|
||||||
|
data: { expression: 'visitor.claims.apiKey ?? "YOUR_API_KEY"' },
|
||||||
|
isVoid: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: 'text',
|
||||||
|
leaves: [{ object: 'leaf', text: '" }', marks: [] }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles multiple placeholders in one line', () => {
|
||||||
|
const result = convertCodeStringToBlock({
|
||||||
|
key: 'test3',
|
||||||
|
code: 'let a = $$__X-GITBOOK-PREFILL[valA]__$$, b = $$__X-GITBOOK-PREFILL[valB]__$$;',
|
||||||
|
syntax: 'javascript',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
key: 'test3',
|
||||||
|
object: 'block',
|
||||||
|
type: 'code',
|
||||||
|
data: { syntax: 'javascript' },
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
object: 'block',
|
||||||
|
type: 'code-line',
|
||||||
|
data: {},
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
object: 'text',
|
||||||
|
leaves: [{ object: 'leaf', text: 'let a = ', marks: [] }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: 'inline',
|
||||||
|
type: 'expression',
|
||||||
|
data: { expression: 'valA' },
|
||||||
|
isVoid: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: 'text',
|
||||||
|
leaves: [{ object: 'leaf', text: ', b = ', marks: [] }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: 'inline',
|
||||||
|
type: 'expression',
|
||||||
|
data: { expression: 'valB' },
|
||||||
|
isVoid: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: 'text',
|
||||||
|
leaves: [{ object: 'leaf', text: ';', marks: [] }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles multiple placeholders across different lines', () => {
|
||||||
|
const result = convertCodeStringToBlock({
|
||||||
|
key: 'test4',
|
||||||
|
code: [
|
||||||
|
'const name = "$$__X-GITBOOK-PREFILL[visitor.claims.userName]__$$";',
|
||||||
|
'const age = $$__X-GITBOOK-PREFILL[visitor.claims.userAge]__$$;',
|
||||||
|
'console.log(name, age);',
|
||||||
|
].join('\n'),
|
||||||
|
syntax: 'javascript',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
key: 'test4',
|
||||||
|
object: 'block',
|
||||||
|
type: 'code',
|
||||||
|
data: { syntax: 'javascript' },
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
object: 'block',
|
||||||
|
type: 'code-line',
|
||||||
|
data: {},
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
object: 'text',
|
||||||
|
leaves: [{ object: 'leaf', text: 'const name = "', marks: [] }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: 'inline',
|
||||||
|
type: 'expression',
|
||||||
|
data: { expression: 'visitor.claims.userName' },
|
||||||
|
isVoid: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: 'text',
|
||||||
|
leaves: [{ object: 'leaf', text: '";', marks: [] }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: 'block',
|
||||||
|
type: 'code-line',
|
||||||
|
data: {},
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
object: 'text',
|
||||||
|
leaves: [{ object: 'leaf', text: 'const age = ', marks: [] }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: 'inline',
|
||||||
|
type: 'expression',
|
||||||
|
data: { expression: 'visitor.claims.userAge' },
|
||||||
|
isVoid: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: 'text',
|
||||||
|
leaves: [{ object: 'leaf', text: ';', marks: [] }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: 'block',
|
||||||
|
type: 'code-line',
|
||||||
|
data: {},
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
object: 'text',
|
||||||
|
leaves: [
|
||||||
|
{ object: 'leaf', text: 'console.log(name, age);', marks: [] },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an empty code block for empty string', () => {
|
||||||
|
const result = convertCodeStringToBlock({
|
||||||
|
key: 'test5',
|
||||||
|
code: '',
|
||||||
|
syntax: 'javascript',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
key: 'test5',
|
||||||
|
object: 'block',
|
||||||
|
type: 'code',
|
||||||
|
data: { syntax: 'javascript' },
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
object: 'block',
|
||||||
|
type: 'code-line',
|
||||||
|
data: {},
|
||||||
|
nodes: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import type { DocumentBlockCode, DocumentBlockCodeLine } from '@gitbook/api';
|
||||||
|
|
||||||
|
const PREFILL_WITH_EXPR_REGEX = /\$\$__X-GITBOOK-PREFILL\[(.+?)\]__\$\$/g;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a raw code string into a `DocumentBlockCode` object representation.
|
||||||
|
*
|
||||||
|
* Any placeholder of the form `$$__X-GITBOOK-PREFILL[<expression>]__$$` inside the code
|
||||||
|
* string is transformed into a DocumentInlineExpression node with its `data.expression` set to the
|
||||||
|
* extracted `<expression>`.
|
||||||
|
*/
|
||||||
|
export function convertCodeStringToBlock(args: {
|
||||||
|
key: string;
|
||||||
|
code: string;
|
||||||
|
syntax: string;
|
||||||
|
}): DocumentBlockCode {
|
||||||
|
const { key, code, syntax } = args;
|
||||||
|
const lines = code.split('\n').map<DocumentBlockCodeLine>((line) => {
|
||||||
|
const nodes: DocumentBlockCodeLine['nodes'] = [];
|
||||||
|
let lastIndex = 0;
|
||||||
|
|
||||||
|
for (const match of line.matchAll(PREFILL_WITH_EXPR_REGEX)) {
|
||||||
|
const [placeholder, expression] = match;
|
||||||
|
const start = match.index ?? 0;
|
||||||
|
|
||||||
|
if (start > lastIndex) {
|
||||||
|
nodes.push({
|
||||||
|
object: 'text',
|
||||||
|
leaves: [{ object: 'leaf', text: line.slice(lastIndex, start), marks: [] }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expression) {
|
||||||
|
nodes.push({
|
||||||
|
object: 'inline',
|
||||||
|
type: 'expression',
|
||||||
|
data: { expression },
|
||||||
|
isVoid: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
lastIndex = start + placeholder.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastIndex < line.length) {
|
||||||
|
nodes.push({
|
||||||
|
object: 'text',
|
||||||
|
leaves: [{ object: 'leaf', text: line.slice(lastIndex), marks: [] }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
object: 'block',
|
||||||
|
type: 'code-line',
|
||||||
|
data: {},
|
||||||
|
nodes,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
key,
|
||||||
|
object: 'block',
|
||||||
|
type: 'code',
|
||||||
|
data: { syntax },
|
||||||
|
nodes: lines,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -454,7 +454,7 @@ export function serveVisitorClaimsDataRequest(request: NextRequest, siteRequestU
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!visitorToken && !Object.keys(unsignedClaims).length) {
|
if (!visitorToken && !Object.keys(unsignedClaims).length) {
|
||||||
return NextResponse.json({});
|
return NextResponse.json({ visitor: { claims: { unsigned: {} } } });
|
||||||
}
|
}
|
||||||
|
|
||||||
const visitorClaims = {
|
const visitorClaims = {
|
||||||
|
|||||||
@@ -5,12 +5,16 @@ import {
|
|||||||
} from './OpenAPICodeSampleInteractive';
|
} from './OpenAPICodeSampleInteractive';
|
||||||
import { OpenAPICodeSampleBody } from './OpenAPICodeSampleSelector';
|
import { OpenAPICodeSampleBody } from './OpenAPICodeSampleSelector';
|
||||||
import { ScalarApiButton } from './ScalarApiButton';
|
import { ScalarApiButton } from './ScalarApiButton';
|
||||||
import { type CodeSampleGenerator, codeSampleGenerators } from './code-samples';
|
import { type CodeSampleGenerator, codeSampleGenerators, parseHostAndPath } from './code-samples';
|
||||||
import { type OpenAPIContext, getOpenAPIClientContext } from './context';
|
import { type OpenAPIContext, getOpenAPIClientContext } from './context';
|
||||||
import { generateMediaTypeExamples, generateSchemaExample } from './generateSchemaExample';
|
import { generateMediaTypeExamples, generateSchemaExample } from './generateSchemaExample';
|
||||||
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
||||||
import type { OpenAPIOperationData } from './types';
|
import type { OpenAPIOperationData } from './types';
|
||||||
import { getDefaultServerURL } from './util/server';
|
import { getDefaultServerURL } from './util/server';
|
||||||
|
import {
|
||||||
|
resolvePrefillCodePlaceholderFromSecurityScheme,
|
||||||
|
resolveURLWithPrefillCodePlaceholdersFromServer,
|
||||||
|
} from './util/tryit-prefill';
|
||||||
import { checkIsReference, extractOperationSecurityInfo } from './utils';
|
import { checkIsReference, extractOperationSecurityInfo } from './utils';
|
||||||
|
|
||||||
const CUSTOM_CODE_SAMPLES_KEYS = ['x-custom-examples', 'x-code-samples', 'x-codeSamples'] as const;
|
const CUSTOM_CODE_SAMPLES_KEYS = ['x-custom-examples', 'x-code-samples', 'x-codeSamples'] as const;
|
||||||
@@ -100,10 +104,15 @@ function generateCodeSamples(props: {
|
|||||||
? data.operation.requestBody
|
? data.operation.requestBody
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const url =
|
const defaultServerUrl = getDefaultServerURL(data.servers);
|
||||||
getDefaultServerURL(data.servers) +
|
let serverUrlPath = defaultServerUrl ? parseHostAndPath(defaultServerUrl).path : '';
|
||||||
data.path +
|
serverUrlPath = serverUrlPath === '/' ? '' : serverUrlPath;
|
||||||
(searchParams.size ? `?${searchParams.toString()}` : '');
|
const serverUrl = data.servers[0]
|
||||||
|
? resolveURLWithPrefillCodePlaceholdersFromServer(data.servers[0], defaultServerUrl)
|
||||||
|
: defaultServerUrl;
|
||||||
|
const serverUrlOrigin = serverUrl.replaceAll(serverUrlPath, '');
|
||||||
|
const path =
|
||||||
|
serverUrlPath + data.path + (searchParams.size ? `?${searchParams.toString()}` : '');
|
||||||
|
|
||||||
const genericHeaders = {
|
const genericHeaders = {
|
||||||
...getSecurityHeaders({
|
...getSecurityHeaders({
|
||||||
@@ -124,7 +133,7 @@ function generateCodeSamples(props: {
|
|||||||
mediaType,
|
mediaType,
|
||||||
element: context.renderCodeBlock({
|
element: context.renderCodeBlock({
|
||||||
code: generator.generate({
|
code: generator.generate({
|
||||||
url,
|
url: { origin: serverUrlOrigin, path },
|
||||||
method: data.method,
|
method: data.method,
|
||||||
body: undefined,
|
body: undefined,
|
||||||
headers: mediaTypeHeaders,
|
headers: mediaTypeHeaders,
|
||||||
@@ -137,7 +146,7 @@ function generateCodeSamples(props: {
|
|||||||
example,
|
example,
|
||||||
element: context.renderCodeBlock({
|
element: context.renderCodeBlock({
|
||||||
code: generator.generate({
|
code: generator.generate({
|
||||||
url,
|
url: { origin: serverUrlOrigin, path },
|
||||||
method: data.method,
|
method: data.method,
|
||||||
body: example.value,
|
body: example.value,
|
||||||
headers: mediaTypeHeaders,
|
headers: mediaTypeHeaders,
|
||||||
@@ -174,7 +183,7 @@ function generateCodeSamples(props: {
|
|||||||
label: generator.label,
|
label: generator.label,
|
||||||
body: context.renderCodeBlock({
|
body: context.renderCodeBlock({
|
||||||
code: generator.generate({
|
code: generator.generate({
|
||||||
url,
|
url: { origin: serverUrlOrigin, path },
|
||||||
method: data.method,
|
method: data.method,
|
||||||
body: undefined,
|
body: undefined,
|
||||||
headers: genericHeaders,
|
headers: genericHeaders,
|
||||||
@@ -306,13 +315,17 @@ function getSecurityHeaders(args: {
|
|||||||
switch (security.type) {
|
switch (security.type) {
|
||||||
case 'http': {
|
case 'http': {
|
||||||
let scheme = security.scheme;
|
let scheme = security.scheme;
|
||||||
let format = security.bearerFormat ?? 'YOUR_SECRET_TOKEN';
|
const format = resolvePrefillCodePlaceholderFromSecurityScheme({
|
||||||
|
security: security,
|
||||||
|
defaultPlaceholderValue: scheme?.includes('basic')
|
||||||
|
? 'username:password'
|
||||||
|
: 'YOUR_SECRET_TOKEN',
|
||||||
|
});
|
||||||
|
|
||||||
if (scheme?.includes('bearer')) {
|
if (scheme?.includes('bearer')) {
|
||||||
scheme = 'Bearer';
|
scheme = 'Bearer';
|
||||||
} else if (scheme?.includes('basic')) {
|
} else if (scheme?.includes('basic')) {
|
||||||
scheme = 'Basic';
|
scheme = 'Basic';
|
||||||
format = 'username:password';
|
|
||||||
} else if (scheme?.includes('token')) {
|
} else if (scheme?.includes('token')) {
|
||||||
scheme = 'Token';
|
scheme = 'Token';
|
||||||
}
|
}
|
||||||
@@ -324,14 +337,18 @@ function getSecurityHeaders(args: {
|
|||||||
if (security.in !== 'header') {
|
if (security.in !== 'header') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const name = security.name ?? 'Authorization';
|
const name = security.name ?? 'Authorization';
|
||||||
headers[name] = 'YOUR_API_KEY';
|
headers[name] = resolvePrefillCodePlaceholderFromSecurityScheme({
|
||||||
|
security: security,
|
||||||
|
defaultPlaceholderValue: 'YOUR_API_KEY',
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'oauth2': {
|
case 'oauth2': {
|
||||||
headers.Authorization = 'Bearer YOUR_OAUTH2_TOKEN';
|
headers.Authorization = `Bearer ${resolvePrefillCodePlaceholderFromSecurityScheme({
|
||||||
|
security: security,
|
||||||
|
defaultPlaceholderValue: 'YOUR_OAUTH2_TOKEN',
|
||||||
|
})}`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
@@ -339,7 +356,6 @@ function getSecurityHeaders(args: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import { OpenAPICopyButton } from './OpenAPICopyButton';
|
|||||||
import { OpenAPISchemaName } from './OpenAPISchemaName';
|
import { OpenAPISchemaName } from './OpenAPISchemaName';
|
||||||
import type { OpenAPIClientContext } from './context';
|
import type { OpenAPIClientContext } from './context';
|
||||||
import { t } from './translate';
|
import { t } from './translate';
|
||||||
import type { OpenAPIOperationData, OpenAPISecurityWithRequired } from './types';
|
import type { OpenAPISecuritySchemeWithRequired } from './types';
|
||||||
|
import type { OpenAPIOperationData } from './types';
|
||||||
import { createStateKey, extractOperationSecurityInfo, resolveDescription } from './utils';
|
import { createStateKey, extractOperationSecurityInfo, resolveDescription } from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,7 +63,10 @@ export function OpenAPISecurities(props: {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLabelForType(security: OpenAPISecurityWithRequired, context: OpenAPIClientContext) {
|
function getLabelForType(
|
||||||
|
security: OpenAPISecuritySchemeWithRequired,
|
||||||
|
context: OpenAPIClientContext
|
||||||
|
) {
|
||||||
switch (security.type) {
|
switch (security.type) {
|
||||||
case 'apiKey':
|
case 'apiKey':
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ it('should parse host and path on url strings properly', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const exampleEndpointUrl = new URL('https://example.com/path');
|
||||||
|
|
||||||
describe('curL code sample generator', () => {
|
describe('curL code sample generator', () => {
|
||||||
const generator = codeSampleGenerators.find((g) => g.id === 'curl');
|
const generator = codeSampleGenerators.find((g) => g.id === 'curl');
|
||||||
|
|
||||||
@@ -58,7 +60,7 @@ describe('curL code sample generator', () => {
|
|||||||
it('should format application/x-www-form-urlencoded body properly', () => {
|
it('should format application/x-www-form-urlencoded body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
},
|
},
|
||||||
@@ -77,7 +79,7 @@ describe('curL code sample generator', () => {
|
|||||||
it('should format application/json body properly', () => {
|
it('should format application/json body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
@@ -96,7 +98,7 @@ describe('curL code sample generator', () => {
|
|||||||
it('should format application/xml body properly', () => {
|
it('should format application/xml body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/xml',
|
'Content-Type': 'application/xml',
|
||||||
},
|
},
|
||||||
@@ -113,7 +115,7 @@ describe('curL code sample generator', () => {
|
|||||||
it('should convert json to xml body properly', () => {
|
it('should convert json to xml body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/xml',
|
'Content-Type': 'application/xml',
|
||||||
},
|
},
|
||||||
@@ -130,7 +132,7 @@ describe('curL code sample generator', () => {
|
|||||||
it('should format application/graphql body properly', () => {
|
it('should format application/graphql body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/graphql',
|
'Content-Type': 'application/graphql',
|
||||||
},
|
},
|
||||||
@@ -147,7 +149,7 @@ describe('curL code sample generator', () => {
|
|||||||
it('should format text/csv body properly', () => {
|
it('should format text/csv body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'text/csv',
|
'Content-Type': 'text/csv',
|
||||||
},
|
},
|
||||||
@@ -164,7 +166,7 @@ describe('curL code sample generator', () => {
|
|||||||
it('should format application/pdf body properly', () => {
|
it('should format application/pdf body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/pdf',
|
'Content-Type': 'application/pdf',
|
||||||
},
|
},
|
||||||
@@ -181,7 +183,7 @@ describe('curL code sample generator', () => {
|
|||||||
it('should format text/plain body properly', () => {
|
it('should format text/plain body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'text/plain',
|
'Content-Type': 'text/plain',
|
||||||
},
|
},
|
||||||
@@ -198,7 +200,7 @@ describe('curL code sample generator', () => {
|
|||||||
it('should format multipart/form-data body properly', () => {
|
it('should format multipart/form-data body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'multipart/form-data',
|
'Content-Type': 'multipart/form-data',
|
||||||
},
|
},
|
||||||
@@ -223,7 +225,7 @@ describe('javascript code sample generator', () => {
|
|||||||
it('should format application/x-www-form-urlencoded body properly', () => {
|
it('should format application/x-www-form-urlencoded body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
},
|
},
|
||||||
@@ -242,7 +244,7 @@ describe('javascript code sample generator', () => {
|
|||||||
it('should format application/json body properly', () => {
|
it('should format application/json body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
@@ -261,7 +263,7 @@ describe('javascript code sample generator', () => {
|
|||||||
it('should format application/xml body properly', () => {
|
it('should format application/xml body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/xml',
|
'Content-Type': 'application/xml',
|
||||||
},
|
},
|
||||||
@@ -278,7 +280,7 @@ describe('javascript code sample generator', () => {
|
|||||||
it('should convert json to xml body properly', () => {
|
it('should convert json to xml body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/xml',
|
'Content-Type': 'application/xml',
|
||||||
},
|
},
|
||||||
@@ -295,7 +297,7 @@ describe('javascript code sample generator', () => {
|
|||||||
it('should format application/graphql body properly', () => {
|
it('should format application/graphql body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/graphql',
|
'Content-Type': 'application/graphql',
|
||||||
},
|
},
|
||||||
@@ -312,7 +314,7 @@ describe('javascript code sample generator', () => {
|
|||||||
it('should format text/csv body properly', () => {
|
it('should format text/csv body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'text/csv',
|
'Content-Type': 'text/csv',
|
||||||
},
|
},
|
||||||
@@ -329,7 +331,7 @@ describe('javascript code sample generator', () => {
|
|||||||
it('should format application/pdf body properly', () => {
|
it('should format application/pdf body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/pdf',
|
'Content-Type': 'application/pdf',
|
||||||
},
|
},
|
||||||
@@ -346,7 +348,7 @@ describe('javascript code sample generator', () => {
|
|||||||
it('should format text/plain body properly', () => {
|
it('should format text/plain body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'text/plain',
|
'Content-Type': 'text/plain',
|
||||||
},
|
},
|
||||||
@@ -363,7 +365,7 @@ describe('javascript code sample generator', () => {
|
|||||||
it('should format multipart/form-data body properly', () => {
|
it('should format multipart/form-data body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'multipart/form-data',
|
'Content-Type': 'multipart/form-data',
|
||||||
},
|
},
|
||||||
@@ -388,7 +390,7 @@ describe('python code sample generator', () => {
|
|||||||
it('should format application/x-www-form-urlencoded body properly', () => {
|
it('should format application/x-www-form-urlencoded body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
},
|
},
|
||||||
@@ -407,7 +409,7 @@ describe('python code sample generator', () => {
|
|||||||
it('should format application/json body properly', () => {
|
it('should format application/json body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
@@ -429,7 +431,7 @@ describe('python code sample generator', () => {
|
|||||||
it('should format application/xml body properly', () => {
|
it('should format application/xml body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/xml',
|
'Content-Type': 'application/xml',
|
||||||
},
|
},
|
||||||
@@ -446,7 +448,7 @@ describe('python code sample generator', () => {
|
|||||||
it('should convert json to xml body properly', () => {
|
it('should convert json to xml body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/xml',
|
'Content-Type': 'application/xml',
|
||||||
},
|
},
|
||||||
@@ -463,7 +465,7 @@ describe('python code sample generator', () => {
|
|||||||
it('should format application/graphql body properly', () => {
|
it('should format application/graphql body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/graphql',
|
'Content-Type': 'application/graphql',
|
||||||
},
|
},
|
||||||
@@ -480,7 +482,7 @@ describe('python code sample generator', () => {
|
|||||||
it('should format text/csv body properly', () => {
|
it('should format text/csv body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'text/csv',
|
'Content-Type': 'text/csv',
|
||||||
},
|
},
|
||||||
@@ -497,7 +499,7 @@ describe('python code sample generator', () => {
|
|||||||
it('should format application/pdf body properly', () => {
|
it('should format application/pdf body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/pdf',
|
'Content-Type': 'application/pdf',
|
||||||
},
|
},
|
||||||
@@ -514,7 +516,7 @@ describe('python code sample generator', () => {
|
|||||||
it('should format text/plain body properly', () => {
|
it('should format text/plain body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'text/plain',
|
'Content-Type': 'text/plain',
|
||||||
},
|
},
|
||||||
@@ -531,7 +533,7 @@ describe('python code sample generator', () => {
|
|||||||
it('should format multipart/form-data body properly', () => {
|
it('should format multipart/form-data body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'multipart/form-data',
|
'Content-Type': 'multipart/form-data',
|
||||||
},
|
},
|
||||||
@@ -556,7 +558,7 @@ describe('http code sample generator', () => {
|
|||||||
it('should format application/x-www-form-urlencoded body properly', () => {
|
it('should format application/x-www-form-urlencoded body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
},
|
},
|
||||||
@@ -575,7 +577,7 @@ describe('http code sample generator', () => {
|
|||||||
it('should format application/json body properly', () => {
|
it('should format application/json body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
@@ -594,7 +596,7 @@ describe('http code sample generator', () => {
|
|||||||
it('should format application/xml body properly', () => {
|
it('should format application/xml body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/xml',
|
'Content-Type': 'application/xml',
|
||||||
},
|
},
|
||||||
@@ -611,7 +613,7 @@ describe('http code sample generator', () => {
|
|||||||
it('should convert json to xml body properly', () => {
|
it('should convert json to xml body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/xml',
|
'Content-Type': 'application/xml',
|
||||||
},
|
},
|
||||||
@@ -628,7 +630,7 @@ describe('http code sample generator', () => {
|
|||||||
it('should format application/graphql body properly', () => {
|
it('should format application/graphql body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/graphql',
|
'Content-Type': 'application/graphql',
|
||||||
},
|
},
|
||||||
@@ -645,7 +647,7 @@ describe('http code sample generator', () => {
|
|||||||
it('should format text/csv body properly', () => {
|
it('should format text/csv body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'text/csv',
|
'Content-Type': 'text/csv',
|
||||||
},
|
},
|
||||||
@@ -662,7 +664,7 @@ describe('http code sample generator', () => {
|
|||||||
it('should format application/pdf body properly', () => {
|
it('should format application/pdf body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/pdf',
|
'Content-Type': 'application/pdf',
|
||||||
},
|
},
|
||||||
@@ -679,7 +681,7 @@ describe('http code sample generator', () => {
|
|||||||
it('should format text/plain body properly', () => {
|
it('should format text/plain body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'text/plain',
|
'Content-Type': 'text/plain',
|
||||||
},
|
},
|
||||||
@@ -696,7 +698,7 @@ describe('http code sample generator', () => {
|
|||||||
it('should format multipart/form-data body properly', () => {
|
it('should format multipart/form-data body properly', () => {
|
||||||
const input: CodeSampleInput = {
|
const input: CodeSampleInput = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://example.com/path',
|
url: { origin: exampleEndpointUrl.origin, path: exampleEndpointUrl.pathname },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'multipart/form-data',
|
'Content-Type': 'multipart/form-data',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,7 +16,10 @@ import { stringifyOpenAPI } from './stringifyOpenAPI';
|
|||||||
|
|
||||||
export interface CodeSampleInput {
|
export interface CodeSampleInput {
|
||||||
method: string;
|
method: string;
|
||||||
url: string;
|
url: {
|
||||||
|
origin: string;
|
||||||
|
path: string;
|
||||||
|
};
|
||||||
headers?: Record<string, string>;
|
headers?: Record<string, string>;
|
||||||
body?: any;
|
body?: any;
|
||||||
}
|
}
|
||||||
@@ -33,9 +36,7 @@ export const codeSampleGenerators: CodeSampleGenerator[] = [
|
|||||||
id: 'http',
|
id: 'http',
|
||||||
label: 'HTTP',
|
label: 'HTTP',
|
||||||
syntax: 'http',
|
syntax: 'http',
|
||||||
generate: ({ method, url, headers = {}, body }: CodeSampleInput) => {
|
generate: ({ method, url: { origin, path }, headers = {}, body }: CodeSampleInput) => {
|
||||||
const { host, path } = parseHostAndPath(url);
|
|
||||||
|
|
||||||
if (body) {
|
if (body) {
|
||||||
// if we had a body add a content length header
|
// if we had a body add a content length header
|
||||||
const bodyContent = body ? stringifyOpenAPI(body) : '';
|
const bodyContent = body ? stringifyOpenAPI(body) : '';
|
||||||
@@ -69,7 +70,7 @@ export const codeSampleGenerators: CodeSampleGenerator[] = [
|
|||||||
const bodyString = body ? `\n${body}` : '';
|
const bodyString = body ? `\n${body}` : '';
|
||||||
|
|
||||||
const httpRequest = `${method.toUpperCase()} ${decodeURI(path)} HTTP/1.1
|
const httpRequest = `${method.toUpperCase()} ${decodeURI(path)} HTTP/1.1
|
||||||
Host: ${host}
|
Host: ${origin.replaceAll(/https*:\/\//g, '')}
|
||||||
${headerString}${bodyString}`;
|
${headerString}${bodyString}`;
|
||||||
|
|
||||||
return httpRequest;
|
return httpRequest;
|
||||||
@@ -79,7 +80,7 @@ ${headerString}${bodyString}`;
|
|||||||
id: 'curl',
|
id: 'curl',
|
||||||
label: 'cURL',
|
label: 'cURL',
|
||||||
syntax: 'bash',
|
syntax: 'bash',
|
||||||
generate: ({ method, url, headers, body }) => {
|
generate: ({ method, url: { origin, path }, headers, body }) => {
|
||||||
const separator = ' \\\n';
|
const separator = ' \\\n';
|
||||||
|
|
||||||
const lines: string[] = ['curl -L'];
|
const lines: string[] = ['curl -L'];
|
||||||
@@ -88,7 +89,7 @@ ${headerString}${bodyString}`;
|
|||||||
lines.push(`--request ${method.toUpperCase()}`);
|
lines.push(`--request ${method.toUpperCase()}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
lines.push(`--url '${url}'`);
|
lines.push(`--url '${origin}${path}'`);
|
||||||
|
|
||||||
if (body) {
|
if (body) {
|
||||||
const bodyContent = BodyGenerators.getCurlBody(body, headers);
|
const bodyContent = BodyGenerators.getCurlBody(body, headers);
|
||||||
@@ -120,7 +121,7 @@ ${headerString}${bodyString}`;
|
|||||||
id: 'javascript',
|
id: 'javascript',
|
||||||
label: 'JavaScript',
|
label: 'JavaScript',
|
||||||
syntax: 'javascript',
|
syntax: 'javascript',
|
||||||
generate: ({ method, url, headers, body }) => {
|
generate: ({ method, url: { origin, path }, headers, body }) => {
|
||||||
let code = '';
|
let code = '';
|
||||||
|
|
||||||
if (body) {
|
if (body) {
|
||||||
@@ -134,7 +135,7 @@ ${headerString}${bodyString}`;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
code += `const response = await fetch('${url}', {
|
code += `const response = await fetch('${origin}${path}', {
|
||||||
method: '${method.toUpperCase()}',\n`;
|
method: '${method.toUpperCase()}',\n`;
|
||||||
|
|
||||||
if (headers && Object.keys(headers).length > 0) {
|
if (headers && Object.keys(headers).length > 0) {
|
||||||
@@ -155,7 +156,7 @@ ${headerString}${bodyString}`;
|
|||||||
id: 'python',
|
id: 'python',
|
||||||
label: 'Python',
|
label: 'Python',
|
||||||
syntax: 'python',
|
syntax: 'python',
|
||||||
generate: ({ method, url, headers, body }) => {
|
generate: ({ method, url: { origin, path }, headers, body }) => {
|
||||||
const contentType = headers?.['Content-Type'];
|
const contentType = headers?.['Content-Type'];
|
||||||
let code = `${isJSON(contentType) ? 'import json\n' : ''}import requests\n\n`;
|
let code = `${isJSON(contentType) ? 'import json\n' : ''}import requests\n\n`;
|
||||||
|
|
||||||
@@ -171,7 +172,7 @@ ${headerString}${bodyString}`;
|
|||||||
}
|
}
|
||||||
|
|
||||||
code += `response = requests.${method.toLowerCase()}(\n`;
|
code += `response = requests.${method.toLowerCase()}(\n`;
|
||||||
code += indent(`"${url}",\n`, 4);
|
code += indent(`"${origin}${path}",\n`, 4);
|
||||||
|
|
||||||
if (headers && Object.keys(headers).length > 0) {
|
if (headers && Object.keys(headers).length > 0) {
|
||||||
code += indent(`headers=${stringifyOpenAPI(headers)},\n`, 4);
|
code += indent(`headers=${stringifyOpenAPI(headers)},\n`, 4);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export type OpenAPIServerWithCustomProperties = Omit<OpenAPIV3.ServerObject, 'va
|
|||||||
};
|
};
|
||||||
} & OpenAPICustomPrefillProperties;
|
} & OpenAPICustomPrefillProperties;
|
||||||
|
|
||||||
export type OpenAPISecurityWithRequired = OpenAPIV3.SecuritySchemeObject &
|
export type OpenAPISecuritySchemeWithRequired = OpenAPIV3.SecuritySchemeObject &
|
||||||
OpenAPICustomPrefillProperties & { required?: boolean };
|
OpenAPICustomPrefillProperties & { required?: boolean };
|
||||||
|
|
||||||
export interface OpenAPIOperationData extends OpenAPICustomSpecProperties {
|
export interface OpenAPIOperationData extends OpenAPICustomSpecProperties {
|
||||||
@@ -31,7 +31,7 @@ export interface OpenAPIOperationData extends OpenAPICustomSpecProperties {
|
|||||||
operation: OpenAPIV3.OperationObject<OpenAPICustomOperationProperties>;
|
operation: OpenAPIV3.OperationObject<OpenAPICustomOperationProperties>;
|
||||||
|
|
||||||
/** Securities that should be used for this operation */
|
/** Securities that should be used for this operation */
|
||||||
securities: [string, OpenAPISecurityWithRequired][];
|
securities: [string, OpenAPISecuritySchemeWithRequired][];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OpenAPIWebhookData extends OpenAPICustomSpecProperties {
|
export interface OpenAPIWebhookData extends OpenAPICustomSpecProperties {
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import { describe, expect, it } from 'bun:test';
|
import { describe, expect, it } from 'bun:test';
|
||||||
import type { PrefillInputContextData } from '../OpenAPIPrefillContextProvider';
|
import type { PrefillInputContextData } from '../OpenAPIPrefillContextProvider';
|
||||||
import type { OpenAPIOperationData } from '../types';
|
import type { OpenAPIOperationData } from '../types';
|
||||||
import { resolveTryItPrefillForOperation } from './tryit-prefill';
|
import {
|
||||||
|
resolvePrefillCodePlaceholderFromSecurityScheme,
|
||||||
|
resolveTryItPrefillForOperation,
|
||||||
|
resolveURLWithPrefillCodePlaceholdersFromServer,
|
||||||
|
} from './tryit-prefill';
|
||||||
|
|
||||||
describe('resolveTryItPrefillForOperation', () => {
|
describe('resolveTryItPrefillForOperation', () => {
|
||||||
describe('prefill authentication info', () => {
|
describe('prefill authentication info', () => {
|
||||||
@@ -309,3 +313,166 @@ describe('resolveTryItPrefillForOperation', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('resolvePrefillCodePlaceholderFromSecurityScheme (integration style)', () => {
|
||||||
|
it('should return placeholder for bearer token scheme', () => {
|
||||||
|
const result = resolvePrefillCodePlaceholderFromSecurityScheme({
|
||||||
|
security: {
|
||||||
|
type: 'http',
|
||||||
|
scheme: 'bearer',
|
||||||
|
'x-gitbook-prefill': '{{ visitor.claims.apiToken }}',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBe('$$__X-GITBOOK-PREFILL[(visitor.claims.apiToken)]__$$');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return placeholder for basic auth scheme', () => {
|
||||||
|
const result = resolvePrefillCodePlaceholderFromSecurityScheme({
|
||||||
|
security: {
|
||||||
|
type: 'http',
|
||||||
|
scheme: 'basic',
|
||||||
|
'x-gitbook-prefill': '{{ visitor.claims.basicAuth }}',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBe('$$__X-GITBOOK-PREFILL[(visitor.claims.basicAuth)]__$$');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should build placeholder for apiKey scheme', () => {
|
||||||
|
const result = resolvePrefillCodePlaceholderFromSecurityScheme({
|
||||||
|
security: {
|
||||||
|
type: 'apiKey',
|
||||||
|
in: 'header',
|
||||||
|
name: 'X-API-KEY',
|
||||||
|
'x-gitbook-prefill': '{{ visitor.claims.apiKey }}',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBe('$$__X-GITBOOK-PREFILL[(visitor.claims.apiKey)]__$$');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return placeholder with default value if provided', () => {
|
||||||
|
const result = resolvePrefillCodePlaceholderFromSecurityScheme({
|
||||||
|
security: {
|
||||||
|
type: 'http',
|
||||||
|
scheme: 'bearer',
|
||||||
|
'x-gitbook-prefill': '{{ visitor.claims.missing }}',
|
||||||
|
},
|
||||||
|
defaultPlaceholderValue: 'YOUR_API_TOKEN',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBe(
|
||||||
|
`$$__X-GITBOOK-PREFILL[(visitor.claims.missing) ?? 'YOUR_API_TOKEN']__$$`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should concatenate text and expression in prefill', () => {
|
||||||
|
const result = resolvePrefillCodePlaceholderFromSecurityScheme({
|
||||||
|
security: {
|
||||||
|
type: 'http',
|
||||||
|
scheme: 'bearer',
|
||||||
|
'x-gitbook-prefill': 'Bearer {{ visitor.claims.apiToken }}',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBe('$$__X-GITBOOK-PREFILL[("Bearer " + visitor.claims.apiToken)]__$$');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle multiple expressions in prefill', () => {
|
||||||
|
const result = resolvePrefillCodePlaceholderFromSecurityScheme({
|
||||||
|
security: {
|
||||||
|
type: 'http',
|
||||||
|
scheme: 'basic',
|
||||||
|
'x-gitbook-prefill': '{{ visitor.claims.username }}:{{ visitor.claims.password }}',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBe(
|
||||||
|
'$$__X-GITBOOK-PREFILL[(visitor.claims.username + ":" + visitor.claims.password)]__$$'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return empty default value if no prefill property exists', () => {
|
||||||
|
const result = resolvePrefillCodePlaceholderFromSecurityScheme({
|
||||||
|
security: {
|
||||||
|
type: 'http',
|
||||||
|
scheme: 'bearer',
|
||||||
|
},
|
||||||
|
defaultPlaceholderValue: 'YOUR_API_TOKEN',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBe('YOUR_API_TOKEN');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return empty string if no prefill property exists', () => {
|
||||||
|
const result = resolvePrefillCodePlaceholderFromSecurityScheme({
|
||||||
|
security: {
|
||||||
|
type: 'http',
|
||||||
|
scheme: 'bearer',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBe('');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('resolveURLWithPrefillCodePlaceholdersFromServer', () => {
|
||||||
|
it('should return a simple URL when no prefills are present', () => {
|
||||||
|
const result = resolveURLWithPrefillCodePlaceholdersFromServer({
|
||||||
|
url: 'https://api.example.com/v1',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBe('https://api.example.com/v1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should replace a variable with its default when no prefill is set', () => {
|
||||||
|
const result = resolveURLWithPrefillCodePlaceholdersFromServer({
|
||||||
|
url: 'https://{region}.example.com',
|
||||||
|
variables: {
|
||||||
|
region: { default: 'us-east-1' },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBe('https://us-east-1.example.com');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a placeholder for variable-level prefill only', () => {
|
||||||
|
const result = resolveURLWithPrefillCodePlaceholdersFromServer({
|
||||||
|
url: 'https://{region}.example.com',
|
||||||
|
variables: {
|
||||||
|
region: { default: 'us-east-1', 'x-gitbook-prefill': '{{ user.region }}' },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBe(
|
||||||
|
`$$__X-GITBOOK-PREFILL[(\`https://\${(user.region ?? 'us-east-1')}.example.com\`)]__$$`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should wrap full URL when URL-level prefill exists', () => {
|
||||||
|
const result = resolveURLWithPrefillCodePlaceholdersFromServer({
|
||||||
|
url: 'https://api.example.com/v1',
|
||||||
|
'x-gitbook-prefill': '{{ user.baseUrl }}',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBe(
|
||||||
|
"$$__X-GITBOOK-PREFILL[(user.baseUrl ?? 'https://api.example.com/v1')]__$$"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should combine variable-level and URL-level prefills correctly', () => {
|
||||||
|
const result = resolveURLWithPrefillCodePlaceholdersFromServer({
|
||||||
|
url: 'https://{region}.example.com/{version}',
|
||||||
|
'x-gitbook-prefill': '{{ user.baseUrl }}',
|
||||||
|
variables: {
|
||||||
|
region: { default: 'us-east-1', 'x-gitbook-prefill': '{{ user.region }}' },
|
||||||
|
version: { default: 'v1' },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBe(
|
||||||
|
"$$__X-GITBOOK-PREFILL[(user.baseUrl ?? `https://${(user.region ?? 'us-east-1')}.example.com/v1`)]__$$"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
import { ExpressionRuntime, parseTemplate } from '@gitbook/expr';
|
import { ExpressionRuntime, type TemplatePart, parseTemplate } from '@gitbook/expr';
|
||||||
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
|
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
|
||||||
import type { ApiClientConfiguration } from '@scalar/types';
|
import type { ApiClientConfiguration } from '@scalar/types';
|
||||||
import type { PrefillInputContextData } from '../OpenAPIPrefillContextProvider';
|
import type { PrefillInputContextData } from '../OpenAPIPrefillContextProvider';
|
||||||
import type { OpenAPIOperationData } from '../types';
|
import type {
|
||||||
|
OpenAPIOperationData,
|
||||||
|
OpenAPISecuritySchemeWithRequired,
|
||||||
|
OpenAPIServerWithCustomProperties,
|
||||||
|
} from '../types';
|
||||||
|
|
||||||
export interface TryItPrefillConfiguration {
|
export interface TryItPrefillConfiguration {
|
||||||
authentication?: ApiClientConfiguration['authentication'];
|
authentication?: ApiClientConfiguration['authentication'];
|
||||||
servers?: ApiClientConfiguration['servers'];
|
servers?: ApiClientConfiguration['servers'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const PREFILL_CUSTOM_PROPERTY = 'x-gitbook-prefill';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the Scalar API client prefill configuration for a given OpenAPI operation.
|
* Resolve the Scalar API client prefill configuration for a given OpenAPI operation.
|
||||||
*/
|
*/
|
||||||
@@ -68,8 +74,8 @@ function resolveTryItPrefillAuthForOperationSecurities(args: {
|
|||||||
const prefillAuthConfig: ApiClientConfiguration['authentication']['securitySchemes'] = {};
|
const prefillAuthConfig: ApiClientConfiguration['authentication']['securitySchemes'] = {};
|
||||||
|
|
||||||
for (const [schemeName, security] of Object.values(securities)) {
|
for (const [schemeName, security] of Object.values(securities)) {
|
||||||
const tryitPrefillAuthValue = security['x-gitbook-prefill']
|
const tryitPrefillAuthValue = security[PREFILL_CUSTOM_PROPERTY]
|
||||||
? resolveTryItPrefillExpression(security['x-gitbook-prefill'])
|
? resolveTryItPrefillExpression(security[PREFILL_CUSTOM_PROPERTY])
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
if (!tryitPrefillAuthValue) {
|
if (!tryitPrefillAuthValue) {
|
||||||
@@ -121,7 +127,7 @@ function resolveTryItPrefillServersForOperationServers(args: {
|
|||||||
|
|
||||||
for (const server of servers) {
|
for (const server of servers) {
|
||||||
// Url-level prefill
|
// Url-level prefill
|
||||||
const tryItPrefillServerUrlExpr = server['x-gitbook-prefill'];
|
const tryItPrefillServerUrlExpr = server[PREFILL_CUSTOM_PROPERTY];
|
||||||
const tryItPrefillServerUrlValue = tryItPrefillServerUrlExpr
|
const tryItPrefillServerUrlValue = tryItPrefillServerUrlExpr
|
||||||
? resolveTryItPrefillExpression(tryItPrefillServerUrlExpr)
|
? resolveTryItPrefillExpression(tryItPrefillServerUrlExpr)
|
||||||
: undefined;
|
: undefined;
|
||||||
@@ -133,7 +139,8 @@ function resolveTryItPrefillServersForOperationServers(args: {
|
|||||||
// Variable-level prefill
|
// Variable-level prefill
|
||||||
if (server.variables) {
|
if (server.variables) {
|
||||||
for (const [varName, variable] of Object.entries(server.variables)) {
|
for (const [varName, variable] of Object.entries(server.variables)) {
|
||||||
const { 'x-gitbook-prefill': tryItPrefillVarExpr, ...variableProps } = variable;
|
const { [PREFILL_CUSTOM_PROPERTY]: tryItPrefillVarExpr, ...variableProps } =
|
||||||
|
variable;
|
||||||
|
|
||||||
const tryItPrefillVarValue = tryItPrefillVarExpr
|
const tryItPrefillVarValue = tryItPrefillVarExpr
|
||||||
? resolveTryItPrefillExpression(tryItPrefillVarExpr)
|
? resolveTryItPrefillExpression(tryItPrefillVarExpr)
|
||||||
@@ -158,3 +165,102 @@ function resolveTryItPrefillServersForOperationServers(args: {
|
|||||||
|
|
||||||
return resolvedServers.length > 0 ? resolvedServers : undefined;
|
return resolvedServers.length > 0 ? resolvedServers : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a X-GITBOOK-PREFILL placeholder based on the prefill custom property in the provided security scheme.
|
||||||
|
*/
|
||||||
|
export function resolvePrefillCodePlaceholderFromSecurityScheme(args: {
|
||||||
|
security: OpenAPISecuritySchemeWithRequired;
|
||||||
|
defaultPlaceholderValue?: string;
|
||||||
|
}) {
|
||||||
|
const { security, defaultPlaceholderValue } = args;
|
||||||
|
const prefillExprParts = extractPrefillExpressionPartsFromSecurityScheme(security);
|
||||||
|
|
||||||
|
if (prefillExprParts.length === 0) {
|
||||||
|
return defaultPlaceholderValue ?? '';
|
||||||
|
}
|
||||||
|
const prefillExpr = templatePartsToExpression(prefillExprParts);
|
||||||
|
|
||||||
|
return toPrefillCodePlaceholder(prefillExpr, defaultPlaceholderValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractPrefillExpressionPartsFromSecurityScheme(
|
||||||
|
security: OpenAPISecuritySchemeWithRequired
|
||||||
|
): TemplatePart[] {
|
||||||
|
const expression = security[PREFILL_CUSTOM_PROPERTY];
|
||||||
|
|
||||||
|
if (!expression || expression.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseTemplate(expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a server URL with X-GITBOOK-PREFILL placeholders based on the prefill custom properties in the provided security scheme.
|
||||||
|
*/
|
||||||
|
export function resolveURLWithPrefillCodePlaceholdersFromServer(
|
||||||
|
server: OpenAPIServerWithCustomProperties,
|
||||||
|
defaultServerUrl?: string
|
||||||
|
): string {
|
||||||
|
const serverVariables = server.variables ?? {};
|
||||||
|
const variableExprs: Record<string, string> = {};
|
||||||
|
let hasVariablePrefill = false;
|
||||||
|
|
||||||
|
for (const [name, variable] of Object.entries(serverVariables ?? {})) {
|
||||||
|
if (variable[PREFILL_CUSTOM_PROPERTY]) {
|
||||||
|
hasVariablePrefill = true;
|
||||||
|
const exprString = templatePartsToExpression(
|
||||||
|
parseTemplate(variable[PREFILL_CUSTOM_PROPERTY])
|
||||||
|
);
|
||||||
|
variableExprs[name] = `(${exprString} ?? '${variable.default ?? ''}')`;
|
||||||
|
} else {
|
||||||
|
variableExprs[name] = String(variable.default) ?? '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let interpolatedUrl = server.url ?? '';
|
||||||
|
interpolatedUrl = interpolatedUrl.replace(/{([^}]+)}/g, (_, varName: string) => {
|
||||||
|
const expr = variableExprs[varName];
|
||||||
|
if (serverVariables[varName]?.[PREFILL_CUSTOM_PROPERTY]) {
|
||||||
|
return `\${${expr ?? `'${varName}'`}}`;
|
||||||
|
}
|
||||||
|
return expr ?? `{${varName}}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const interpolatedUrlTemplate = hasVariablePrefill ? `\`${interpolatedUrl}\`` : interpolatedUrl;
|
||||||
|
|
||||||
|
const urlLevelExpr = server[PREFILL_CUSTOM_PROPERTY];
|
||||||
|
if (urlLevelExpr) {
|
||||||
|
const exprString = templatePartsToExpression(parseTemplate(urlLevelExpr));
|
||||||
|
const defaultValue = hasVariablePrefill
|
||||||
|
? interpolatedUrlTemplate
|
||||||
|
: `'${interpolatedUrlTemplate}'`;
|
||||||
|
return toPrefillCodePlaceholder(`${exprString} ?? ${defaultValue}`, defaultServerUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasVariablePrefill) {
|
||||||
|
return toPrefillCodePlaceholder(interpolatedUrlTemplate, defaultServerUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
return interpolatedUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
function templatePartsToExpression(parts: ReturnType<typeof parseTemplate>) {
|
||||||
|
return parts
|
||||||
|
.map((part) => {
|
||||||
|
switch (part.type) {
|
||||||
|
case 'text':
|
||||||
|
return `"${part.value}"`;
|
||||||
|
case 'expression':
|
||||||
|
return part.value;
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.join(' + ');
|
||||||
|
}
|
||||||
|
|
||||||
|
function toPrefillCodePlaceholder(expression: string, defaultValue?: string) {
|
||||||
|
return `$$__X-GITBOOK-PREFILL[(${expression})${defaultValue ? ` ?? '${defaultValue}'` : ''}]__$$`;
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { AnyObject, OpenAPIV3, OpenAPIV3_1 } from '@gitbook/openapi-parser'
|
|||||||
import type { OpenAPIUniversalContext } from './context';
|
import type { OpenAPIUniversalContext } from './context';
|
||||||
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
||||||
import { tString } from './translate';
|
import { tString } from './translate';
|
||||||
import type { OpenAPIOperationData, OpenAPISecurityWithRequired } from './types';
|
import type { OpenAPIOperationData, OpenAPISecuritySchemeWithRequired } from './types';
|
||||||
|
|
||||||
export function checkIsReference(input: unknown): input is OpenAPIV3.ReferenceObject {
|
export function checkIsReference(input: unknown): input is OpenAPIV3.ReferenceObject {
|
||||||
return typeof input === 'object' && !!input && '$ref' in input;
|
return typeof input === 'object' && !!input && '$ref' in input;
|
||||||
@@ -258,7 +258,7 @@ export function getSchemaTitle(schema: OpenAPIV3.SchemaObject): string {
|
|||||||
export type OperationSecurityInfo = {
|
export type OperationSecurityInfo = {
|
||||||
key: string;
|
key: string;
|
||||||
label: string;
|
label: string;
|
||||||
schemes: OpenAPISecurityWithRequired[];
|
schemes: OpenAPISecuritySchemeWithRequired[];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user