mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-16 15:45:13 +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', () => {
|
it('should not use x-gitbook-prefix for http scheme', () => {
|
||||||
const securities: OpenAPIOperationData['securities'] = [
|
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', () => {
|
it('should return empty object if no visitor data matches prefill expression', () => {
|
||||||
const operation: OpenAPIOperationData = {
|
const operation: OpenAPIOperationData = {
|
||||||
path: '/orgs/<orgId>/spaces',
|
path: '/orgs/<orgId>/spaces',
|
||||||
@@ -167,7 +305,9 @@ describe('resolveTryItPrefillForOperation', () => {
|
|||||||
|
|
||||||
const prefillInputContext: PrefillInputContextData = {
|
const prefillInputContext: PrefillInputContextData = {
|
||||||
visitor: {
|
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
|
// Override env
|
||||||
const overrideEnvResult = resolveTryItPrefillForOperation({
|
const overrideEnvResult = resolveTryItPrefillForOperation({
|
||||||
operation,
|
operation,
|
||||||
prefillInputContext: { visitor: { claims: { api: { env: 'staging' } } } },
|
prefillInputContext: {
|
||||||
|
visitor: { claims: { api: { env: 'staging' } } },
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(overrideEnvResult).toEqual({
|
expect(overrideEnvResult).toEqual({
|
||||||
servers: [
|
servers: [
|
||||||
@@ -232,7 +374,9 @@ describe('resolveTryItPrefillForOperation', () => {
|
|||||||
// Override version
|
// Override version
|
||||||
const overrideVersionResult = resolveTryItPrefillForOperation({
|
const overrideVersionResult = resolveTryItPrefillForOperation({
|
||||||
operation,
|
operation,
|
||||||
prefillInputContext: { visitor: { claims: { api: { version: 'v2' } } } },
|
prefillInputContext: {
|
||||||
|
visitor: { claims: { api: { version: 'v2' } } },
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(overrideVersionResult).toEqual({
|
expect(overrideVersionResult).toEqual({
|
||||||
servers: [
|
servers: [
|
||||||
@@ -467,7 +611,10 @@ describe('resolveURLWithPrefillCodePlaceholdersFromServer', () => {
|
|||||||
const result = resolveURLWithPrefillCodePlaceholdersFromServer({
|
const result = resolveURLWithPrefillCodePlaceholdersFromServer({
|
||||||
url: 'https://{region}.example.com',
|
url: 'https://{region}.example.com',
|
||||||
variables: {
|
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}',
|
url: 'https://{region}.example.com/{version}',
|
||||||
'x-gitbook-prefill': '{{ user.baseUrl }}',
|
'x-gitbook-prefill': '{{ user.baseUrl }}',
|
||||||
variables: {
|
variables: {
|
||||||
region: { default: 'us-east-1', 'x-gitbook-prefill': '{{ user.region }}' },
|
region: {
|
||||||
|
default: 'us-east-1',
|
||||||
|
'x-gitbook-prefill': '{{ user.region }}',
|
||||||
|
},
|
||||||
version: { default: 'v1' },
|
version: { default: 'v1' },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -33,18 +33,18 @@ export function resolveTryItPrefillForOperation(args: {
|
|||||||
prefillInputContext,
|
prefillInputContext,
|
||||||
} = args;
|
} = args;
|
||||||
|
|
||||||
if (!prefillInputContext) {
|
const resolveTryItPrefillExpression = prefillInputContext
|
||||||
return {};
|
? (() => {
|
||||||
}
|
const runtime = new ExpressionRuntime();
|
||||||
|
return (expr: string) => {
|
||||||
const runtime = new ExpressionRuntime();
|
const parts = parseTemplate(expr);
|
||||||
const resolveTryItPrefillExpression = (expr: string) => {
|
if (!parts.length) {
|
||||||
const parts = parseTemplate(expr);
|
return undefined;
|
||||||
if (!parts.length) {
|
}
|
||||||
return undefined;
|
return runtime.evaluateTemplate(expr, prefillInputContext);
|
||||||
}
|
};
|
||||||
return runtime.evaluateTemplate(expr, prefillInputContext);
|
})()
|
||||||
};
|
: undefined;
|
||||||
|
|
||||||
const prefillAuth = securities
|
const prefillAuth = securities
|
||||||
? resolveTryItPrefillAuthForOperationSecurities({
|
? resolveTryItPrefillAuthForOperationSecurities({
|
||||||
@@ -54,7 +54,10 @@ export function resolveTryItPrefillForOperation(args: {
|
|||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const prefillServers = servers
|
const prefillServers = servers
|
||||||
? resolveTryItPrefillServersForOperationServers({ servers, resolveTryItPrefillExpression })
|
? resolveTryItPrefillServersForOperationServers({
|
||||||
|
servers,
|
||||||
|
resolveTryItPrefillExpression,
|
||||||
|
})
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -68,15 +71,17 @@ export function resolveTryItPrefillForOperation(args: {
|
|||||||
*/
|
*/
|
||||||
function resolveTryItPrefillAuthForOperationSecurities(args: {
|
function resolveTryItPrefillAuthForOperationSecurities(args: {
|
||||||
securities: OpenAPIOperationData['securities'];
|
securities: OpenAPIOperationData['securities'];
|
||||||
resolveTryItPrefillExpression: (expr: string) => string | undefined;
|
resolveTryItPrefillExpression?: (expr: string) => string | undefined;
|
||||||
}): ApiClientConfiguration['authentication'] | undefined {
|
}): ApiClientConfiguration['authentication'] | undefined {
|
||||||
const { securities, resolveTryItPrefillExpression } = args;
|
const { securities, resolveTryItPrefillExpression } = 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[PREFILL_CUSTOM_PROPERTY]
|
const resolvedPrefill = security[PREFILL_CUSTOM_PROPERTY]
|
||||||
? resolveTryItPrefillExpression(security[PREFILL_CUSTOM_PROPERTY])
|
? resolveTryItPrefillExpression?.(security[PREFILL_CUSTOM_PROPERTY])
|
||||||
: undefined;
|
: undefined;
|
||||||
|
const tryitPrefillAuthValue =
|
||||||
|
resolvedPrefill || security['x-gitbook-token-placeholder'] || undefined;
|
||||||
|
|
||||||
if (!tryitPrefillAuthValue) {
|
if (!tryitPrefillAuthValue) {
|
||||||
continue;
|
continue;
|
||||||
@@ -120,7 +125,7 @@ function resolveTryItPrefillAuthForOperationSecurities(args: {
|
|||||||
*/
|
*/
|
||||||
function resolveTryItPrefillServersForOperationServers(args: {
|
function resolveTryItPrefillServersForOperationServers(args: {
|
||||||
servers: OpenAPIOperationData['servers'];
|
servers: OpenAPIOperationData['servers'];
|
||||||
resolveTryItPrefillExpression: (expr: string) => string | undefined;
|
resolveTryItPrefillExpression?: (expr: string) => string | undefined;
|
||||||
}): ApiClientConfiguration['servers'] | undefined {
|
}): ApiClientConfiguration['servers'] | undefined {
|
||||||
const { servers, resolveTryItPrefillExpression } = args;
|
const { servers, resolveTryItPrefillExpression } = args;
|
||||||
const resolvedServers: ApiClientConfiguration['servers'] = [];
|
const resolvedServers: ApiClientConfiguration['servers'] = [];
|
||||||
@@ -128,9 +133,10 @@ function resolveTryItPrefillServersForOperationServers(args: {
|
|||||||
for (const server of servers) {
|
for (const server of servers) {
|
||||||
// Url-level prefill
|
// Url-level prefill
|
||||||
const tryItPrefillServerUrlExpr = server[PREFILL_CUSTOM_PROPERTY];
|
const tryItPrefillServerUrlExpr = server[PREFILL_CUSTOM_PROPERTY];
|
||||||
const tryItPrefillServerUrlValue = tryItPrefillServerUrlExpr
|
const tryItPrefillServerUrlValue =
|
||||||
? resolveTryItPrefillExpression(tryItPrefillServerUrlExpr)
|
tryItPrefillServerUrlExpr && resolveTryItPrefillExpression
|
||||||
: undefined;
|
? resolveTryItPrefillExpression(tryItPrefillServerUrlExpr)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
const variables: { [variable: string]: OpenAPIV3.ServerVariableObject } = server.variables
|
const variables: { [variable: string]: OpenAPIV3.ServerVariableObject } = server.variables
|
||||||
? { ...server.variables }
|
? { ...server.variables }
|
||||||
@@ -143,7 +149,7 @@ function resolveTryItPrefillServersForOperationServers(args: {
|
|||||||
variable;
|
variable;
|
||||||
|
|
||||||
const tryItPrefillVarValue = tryItPrefillVarExpr
|
const tryItPrefillVarValue = tryItPrefillVarExpr
|
||||||
? resolveTryItPrefillExpression(tryItPrefillVarExpr)
|
? resolveTryItPrefillExpression?.(tryItPrefillVarExpr)
|
||||||
: undefined;
|
: undefined;
|
||||||
variables[varName] = {
|
variables[varName] = {
|
||||||
...variableProps,
|
...variableProps,
|
||||||
@@ -266,5 +272,7 @@ function templatePartsToExpression(parts: ReturnType<typeof parseTemplate>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function toPrefillCodePlaceholder(expression: string, defaultValue?: string) {
|
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