Support x-gitbook-token-placeholder as TryIt prefill fallback for basic auth (#4105)

This commit is contained in:
Nolann B.
2026-03-11 20:35:13 +01:00
committed by GitHub
parent 4787b7e910
commit 9dd2db6678
4 changed files with 255 additions and 27 deletions
+5
View File
@@ -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}'` : ''
}]__$$`;
}