mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-12 05:48:57 +00:00
Support x-gitbook-token-placeholder as TryIt prefill fallback for basic auth (#4105)
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@gitbook/react-openapi": patch
|
||||
---
|
||||
|
||||
Support x-gitbook-token-placeholder as TryIt prefill fallback for HTTP auth (basic and bearer)
|
||||
@@ -50,6 +50,71 @@ describe('getSecurityHeaders', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should use default placeholder for basic auth', () => {
|
||||
const securities: OpenAPIOperationData['securities'] = [
|
||||
[
|
||||
'basicAuth',
|
||||
{
|
||||
type: 'http',
|
||||
scheme: 'basic',
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
const result = getSecurityHeaders({
|
||||
securityRequirement: [{ basicAuth: [] }],
|
||||
securities,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
Authorization: 'Basic username:password',
|
||||
});
|
||||
});
|
||||
|
||||
it('should use x-gitbook-token-placeholder for basic auth', () => {
|
||||
const securities: OpenAPIOperationData['securities'] = [
|
||||
[
|
||||
'basicAuth',
|
||||
{
|
||||
type: 'http',
|
||||
scheme: 'basic',
|
||||
'x-gitbook-token-placeholder': 'admin:secret123',
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
const result = getSecurityHeaders({
|
||||
securityRequirement: [{ basicAuth: [] }],
|
||||
securities,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
Authorization: 'Basic admin:secret123',
|
||||
});
|
||||
});
|
||||
|
||||
it('should use x-gitbook-token-placeholder for bearer auth', () => {
|
||||
const securities: OpenAPIOperationData['securities'] = [
|
||||
[
|
||||
'bearerAuth',
|
||||
{
|
||||
type: 'http',
|
||||
scheme: 'bearer',
|
||||
'x-gitbook-token-placeholder': 'MY_CUSTOM_TOKEN',
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
const result = getSecurityHeaders({
|
||||
securityRequirement: [{ bearerAuth: [] }],
|
||||
securities,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
Authorization: 'Bearer MY_CUSTOM_TOKEN',
|
||||
});
|
||||
});
|
||||
|
||||
it('should not use x-gitbook-prefix for http scheme', () => {
|
||||
const securities: OpenAPIOperationData['securities'] = [
|
||||
[
|
||||
|
||||
@@ -118,6 +118,144 @@ describe('resolveTryItPrefillForOperation', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should use x-gitbook-token-placeholder as fallback for basic auth in try-it', () => {
|
||||
const operation: OpenAPIOperationData = {
|
||||
path: '/users',
|
||||
method: 'GET',
|
||||
operation: { summary: 'List users' },
|
||||
servers: [{ url: 'https://api.example.com' }],
|
||||
securities: [
|
||||
[
|
||||
'basicAuth',
|
||||
{
|
||||
type: 'http',
|
||||
scheme: 'basic',
|
||||
'x-gitbook-token-placeholder': 'admin:secret123',
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
|
||||
const prefillInputContext: PrefillInputContextData = {
|
||||
visitor: { claims: {} },
|
||||
};
|
||||
|
||||
const result = resolveTryItPrefillForOperation({
|
||||
operation,
|
||||
prefillInputContext,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
authentication: {
|
||||
securitySchemes: {
|
||||
basicAuth: { username: 'admin', password: 'secret123' },
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should use x-gitbook-token-placeholder as fallback for bearer auth in try-it', () => {
|
||||
const operation: OpenAPIOperationData = {
|
||||
path: '/users',
|
||||
method: 'GET',
|
||||
operation: { summary: 'List users' },
|
||||
servers: [{ url: 'https://api.example.com' }],
|
||||
securities: [
|
||||
[
|
||||
'bearerAuth',
|
||||
{
|
||||
type: 'http',
|
||||
scheme: 'bearer',
|
||||
'x-gitbook-token-placeholder': 'my-default-token',
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
|
||||
const prefillInputContext: PrefillInputContextData = {
|
||||
visitor: { claims: {} },
|
||||
};
|
||||
|
||||
const result = resolveTryItPrefillForOperation({
|
||||
operation,
|
||||
prefillInputContext,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
authentication: {
|
||||
securitySchemes: {
|
||||
bearerAuth: { token: 'my-default-token' },
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should fall back to x-gitbook-token-placeholder when x-gitbook-prefill exists but prefillInputContext is null', () => {
|
||||
const operation: OpenAPIOperationData = {
|
||||
path: '/users',
|
||||
method: 'GET',
|
||||
operation: { summary: 'List users' },
|
||||
servers: [{ url: 'https://api.example.com' }],
|
||||
securities: [
|
||||
[
|
||||
'basicAuth',
|
||||
{
|
||||
type: 'http',
|
||||
scheme: 'basic',
|
||||
'x-gitbook-prefill': '{{ visitor.claims.basicAuth }}',
|
||||
'x-gitbook-token-placeholder': 'admin:secret123',
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
|
||||
const result = resolveTryItPrefillForOperation({
|
||||
operation,
|
||||
prefillInputContext: null,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
authentication: {
|
||||
securitySchemes: {
|
||||
basicAuth: { username: 'admin', password: 'secret123' },
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should fall back to x-gitbook-token-placeholder when x-gitbook-prefill expression resolves to undefined', () => {
|
||||
const operation: OpenAPIOperationData = {
|
||||
path: '/users',
|
||||
method: 'GET',
|
||||
operation: { summary: 'List users' },
|
||||
servers: [{ url: 'https://api.example.com' }],
|
||||
securities: [
|
||||
[
|
||||
'bearerAuth',
|
||||
{
|
||||
type: 'http',
|
||||
scheme: 'bearer',
|
||||
'x-gitbook-prefill': '{{ visitor.claims.missing }}',
|
||||
'x-gitbook-token-placeholder': 'fallback-token',
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
|
||||
const result = resolveTryItPrefillForOperation({
|
||||
operation,
|
||||
prefillInputContext: { visitor: { claims: {} } },
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
authentication: {
|
||||
securitySchemes: {
|
||||
bearerAuth: { token: 'fallback-token' },
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should return empty object if no visitor data matches prefill expression', () => {
|
||||
const operation: OpenAPIOperationData = {
|
||||
path: '/orgs/<orgId>/spaces',
|
||||
@@ -167,7 +305,9 @@ describe('resolveTryItPrefillForOperation', () => {
|
||||
|
||||
const prefillInputContext: PrefillInputContextData = {
|
||||
visitor: {
|
||||
claims: { api: { endpointUrl: 'https://api.gitbook-staging.com/v1/' } },
|
||||
claims: {
|
||||
api: { endpointUrl: 'https://api.gitbook-staging.com/v1/' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -214,7 +354,9 @@ describe('resolveTryItPrefillForOperation', () => {
|
||||
// Override env
|
||||
const overrideEnvResult = resolveTryItPrefillForOperation({
|
||||
operation,
|
||||
prefillInputContext: { visitor: { claims: { api: { env: 'staging' } } } },
|
||||
prefillInputContext: {
|
||||
visitor: { claims: { api: { env: 'staging' } } },
|
||||
},
|
||||
});
|
||||
expect(overrideEnvResult).toEqual({
|
||||
servers: [
|
||||
@@ -232,7 +374,9 @@ describe('resolveTryItPrefillForOperation', () => {
|
||||
// Override version
|
||||
const overrideVersionResult = resolveTryItPrefillForOperation({
|
||||
operation,
|
||||
prefillInputContext: { visitor: { claims: { api: { version: 'v2' } } } },
|
||||
prefillInputContext: {
|
||||
visitor: { claims: { api: { version: 'v2' } } },
|
||||
},
|
||||
});
|
||||
expect(overrideVersionResult).toEqual({
|
||||
servers: [
|
||||
@@ -467,7 +611,10 @@ describe('resolveURLWithPrefillCodePlaceholdersFromServer', () => {
|
||||
const result = resolveURLWithPrefillCodePlaceholdersFromServer({
|
||||
url: 'https://{region}.example.com',
|
||||
variables: {
|
||||
region: { default: 'us-east-1', 'x-gitbook-prefill': '{{ user.region }}' },
|
||||
region: {
|
||||
default: 'us-east-1',
|
||||
'x-gitbook-prefill': '{{ user.region }}',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -492,7 +639,10 @@ describe('resolveURLWithPrefillCodePlaceholdersFromServer', () => {
|
||||
url: 'https://{region}.example.com/{version}',
|
||||
'x-gitbook-prefill': '{{ user.baseUrl }}',
|
||||
variables: {
|
||||
region: { default: 'us-east-1', 'x-gitbook-prefill': '{{ user.region }}' },
|
||||
region: {
|
||||
default: 'us-east-1',
|
||||
'x-gitbook-prefill': '{{ user.region }}',
|
||||
},
|
||||
version: { default: 'v1' },
|
||||
},
|
||||
});
|
||||
|
||||
@@ -33,18 +33,18 @@ export function resolveTryItPrefillForOperation(args: {
|
||||
prefillInputContext,
|
||||
} = args;
|
||||
|
||||
if (!prefillInputContext) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const runtime = new ExpressionRuntime();
|
||||
const resolveTryItPrefillExpression = (expr: string) => {
|
||||
const parts = parseTemplate(expr);
|
||||
if (!parts.length) {
|
||||
return undefined;
|
||||
}
|
||||
return runtime.evaluateTemplate(expr, prefillInputContext);
|
||||
};
|
||||
const resolveTryItPrefillExpression = prefillInputContext
|
||||
? (() => {
|
||||
const runtime = new ExpressionRuntime();
|
||||
return (expr: string) => {
|
||||
const parts = parseTemplate(expr);
|
||||
if (!parts.length) {
|
||||
return undefined;
|
||||
}
|
||||
return runtime.evaluateTemplate(expr, prefillInputContext);
|
||||
};
|
||||
})()
|
||||
: undefined;
|
||||
|
||||
const prefillAuth = securities
|
||||
? resolveTryItPrefillAuthForOperationSecurities({
|
||||
@@ -54,7 +54,10 @@ export function resolveTryItPrefillForOperation(args: {
|
||||
: undefined;
|
||||
|
||||
const prefillServers = servers
|
||||
? resolveTryItPrefillServersForOperationServers({ servers, resolveTryItPrefillExpression })
|
||||
? resolveTryItPrefillServersForOperationServers({
|
||||
servers,
|
||||
resolveTryItPrefillExpression,
|
||||
})
|
||||
: [];
|
||||
|
||||
return {
|
||||
@@ -68,15 +71,17 @@ export function resolveTryItPrefillForOperation(args: {
|
||||
*/
|
||||
function resolveTryItPrefillAuthForOperationSecurities(args: {
|
||||
securities: OpenAPIOperationData['securities'];
|
||||
resolveTryItPrefillExpression: (expr: string) => string | undefined;
|
||||
resolveTryItPrefillExpression?: (expr: string) => string | undefined;
|
||||
}): ApiClientConfiguration['authentication'] | undefined {
|
||||
const { securities, resolveTryItPrefillExpression } = args;
|
||||
const prefillAuthConfig: ApiClientConfiguration['authentication']['securitySchemes'] = {};
|
||||
|
||||
for (const [schemeName, security] of Object.values(securities)) {
|
||||
const tryitPrefillAuthValue = security[PREFILL_CUSTOM_PROPERTY]
|
||||
? resolveTryItPrefillExpression(security[PREFILL_CUSTOM_PROPERTY])
|
||||
const resolvedPrefill = security[PREFILL_CUSTOM_PROPERTY]
|
||||
? resolveTryItPrefillExpression?.(security[PREFILL_CUSTOM_PROPERTY])
|
||||
: undefined;
|
||||
const tryitPrefillAuthValue =
|
||||
resolvedPrefill || security['x-gitbook-token-placeholder'] || undefined;
|
||||
|
||||
if (!tryitPrefillAuthValue) {
|
||||
continue;
|
||||
@@ -120,7 +125,7 @@ function resolveTryItPrefillAuthForOperationSecurities(args: {
|
||||
*/
|
||||
function resolveTryItPrefillServersForOperationServers(args: {
|
||||
servers: OpenAPIOperationData['servers'];
|
||||
resolveTryItPrefillExpression: (expr: string) => string | undefined;
|
||||
resolveTryItPrefillExpression?: (expr: string) => string | undefined;
|
||||
}): ApiClientConfiguration['servers'] | undefined {
|
||||
const { servers, resolveTryItPrefillExpression } = args;
|
||||
const resolvedServers: ApiClientConfiguration['servers'] = [];
|
||||
@@ -128,9 +133,10 @@ function resolveTryItPrefillServersForOperationServers(args: {
|
||||
for (const server of servers) {
|
||||
// Url-level prefill
|
||||
const tryItPrefillServerUrlExpr = server[PREFILL_CUSTOM_PROPERTY];
|
||||
const tryItPrefillServerUrlValue = tryItPrefillServerUrlExpr
|
||||
? resolveTryItPrefillExpression(tryItPrefillServerUrlExpr)
|
||||
: undefined;
|
||||
const tryItPrefillServerUrlValue =
|
||||
tryItPrefillServerUrlExpr && resolveTryItPrefillExpression
|
||||
? resolveTryItPrefillExpression(tryItPrefillServerUrlExpr)
|
||||
: undefined;
|
||||
|
||||
const variables: { [variable: string]: OpenAPIV3.ServerVariableObject } = server.variables
|
||||
? { ...server.variables }
|
||||
@@ -143,7 +149,7 @@ function resolveTryItPrefillServersForOperationServers(args: {
|
||||
variable;
|
||||
|
||||
const tryItPrefillVarValue = tryItPrefillVarExpr
|
||||
? resolveTryItPrefillExpression(tryItPrefillVarExpr)
|
||||
? resolveTryItPrefillExpression?.(tryItPrefillVarExpr)
|
||||
: undefined;
|
||||
variables[varName] = {
|
||||
...variableProps,
|
||||
@@ -266,5 +272,7 @@ function templatePartsToExpression(parts: ReturnType<typeof parseTemplate>) {
|
||||
}
|
||||
|
||||
function toPrefillCodePlaceholder(expression: string, defaultValue?: string) {
|
||||
return `$$__X-GITBOOK-PREFILL[(${expression})${defaultValue ? ` ?? '${defaultValue}'` : ''}]__$$`;
|
||||
return `$$__X-GITBOOK-PREFILL[(${expression})${
|
||||
defaultValue ? ` ?? '${defaultValue}'` : ''
|
||||
}]__$$`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user