-
Notifications
You must be signed in to change notification settings - Fork 89
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update router.ts - adding operationId (#1349)
* Update router.ts - adding operationId https://swagger.io/specification/#fixed-fields-8 operationId Unique string used to identify the operation. The id MUST be unique among all operations described in the API. The operationId value is case-sensitive. Tools and libraries MAY use the operationId to uniquely identify an operation, therefore, it is RECOMMENDED to follow common programming naming conventions. * Use operationName --------- Co-authored-by: Arda TANRIKULU <[email protected]>
- Loading branch information
1 parent
d4f9437
commit f21c5ad
Showing
53 changed files
with
2,542 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { type DocumentNode, type OperationDefinitionNode, type VariableDefinitionNode } from 'graphql'; | ||
export type OperationInfo = { | ||
operation: OperationDefinitionNode; | ||
variables: ReadonlyArray<VariableDefinitionNode>; | ||
name: string; | ||
} | undefined; | ||
export declare function getOperationInfo(doc: DocumentNode): OperationInfo; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getOperationInfo = void 0; | ||
const graphql_1 = require("graphql"); | ||
function getOperationInfo(doc) { | ||
const op = (0, graphql_1.getOperationAST)(doc, null); | ||
if (!op) { | ||
return; | ||
} | ||
return { | ||
operation: op, | ||
name: op.name.value, | ||
variables: op.variableDefinitions || [], | ||
}; | ||
} | ||
exports.getOperationInfo = getOperationInfo; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export declare function convertName(name: string): string; | ||
export declare function isNil<T>(val: T): boolean; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.isNil = exports.convertName = void 0; | ||
const param_case_1 = require("param-case"); | ||
function convertName(name) { | ||
return (0, param_case_1.paramCase)(name); | ||
} | ||
exports.convertName = convertName; | ||
function isNil(val) { | ||
return val == null; | ||
} | ||
exports.isNil = isNil; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import type { SofaConfig } from './sofa'; | ||
export { OpenAPI } from './open-api'; | ||
export declare function useSofa(config: SofaConfig): import("fets").Router<any, {}, { | ||
[TKey: string]: never; | ||
}>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.useSofa = exports.OpenAPI = void 0; | ||
const router_1 = require("./router"); | ||
const sofa_1 = require("./sofa"); | ||
var open_api_1 = require("./open-api"); | ||
Object.defineProperty(exports, "OpenAPI", { enumerable: true, get: function () { return open_api_1.OpenAPI; } }); | ||
function useSofa(config) { | ||
return (0, router_1.createRouter)((0, sofa_1.createSofa)(config)); | ||
} | ||
exports.useSofa = useSofa; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export declare const logger: { | ||
error: (...args: any[]) => void; | ||
warn: (...args: any[]) => void; | ||
info: (...args: any[]) => void; | ||
debug: (...args: any[]) => void; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.logger = void 0; | ||
const tslib_1 = require("tslib"); | ||
const ansi_colors_1 = tslib_1.__importDefault(require("ansi-colors")); | ||
const levels = ['error', 'warn', 'info', 'debug']; | ||
const toLevel = (string) => levels.includes(string) ? string : null; | ||
const currentLevel = globalThis.process?.env?.SOFA_DEBUG | ||
? 'debug' | ||
: toLevel(globalThis.process?.env?.SOFA_LOGGER_LEVEL) ?? 'info'; | ||
const log = (level, color, args) => { | ||
if (levels.indexOf(level) <= levels.indexOf(currentLevel)) { | ||
console.log(`${color(level)}:`, ...args); | ||
} | ||
}; | ||
exports.logger = { | ||
error: (...args) => { | ||
log('error', ansi_colors_1.default.red, args); | ||
}, | ||
warn: (...args) => { | ||
log('warn', ansi_colors_1.default.yellow, args); | ||
}, | ||
info: (...args) => { | ||
log('info', ansi_colors_1.default.green, args); | ||
}, | ||
debug: (...args) => { | ||
log('debug', ansi_colors_1.default.blue, args); | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { GraphQLSchema } from 'graphql'; | ||
import { RouteInfo } from '../types'; | ||
import { OpenAPIV3 } from 'openapi-types'; | ||
export declare function OpenAPI({ schema, info, servers, components, security, tags, customScalars, }: { | ||
schema: GraphQLSchema; | ||
info: OpenAPIV3.InfoObject; | ||
servers?: OpenAPIV3.ServerObject[]; | ||
components?: Record<string, any>; | ||
security?: OpenAPIV3.SecurityRequirementObject[]; | ||
tags?: OpenAPIV3.TagObject[]; | ||
/** | ||
* Override mapping of custom scalars to OpenAPI | ||
* @example | ||
* ```js | ||
* { | ||
* Date: { type: "string", format: "date" } | ||
* } | ||
* ``` | ||
*/ | ||
customScalars?: Record<string, any>; | ||
}): { | ||
addRoute(info: RouteInfo, config?: { | ||
basePath?: string; | ||
}): void; | ||
get(): OpenAPIV3.Document<{}>; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.OpenAPI = void 0; | ||
const graphql_1 = require("graphql"); | ||
const types_1 = require("./types"); | ||
const operations_1 = require("./operations"); | ||
const utils_1 = require("./utils"); | ||
function OpenAPI({ schema, info, servers, components, security, tags, customScalars = {}, }) { | ||
const types = schema.getTypeMap(); | ||
const swagger = { | ||
openapi: '3.0.0', | ||
info, | ||
servers, | ||
tags: [], | ||
paths: {}, | ||
components: { | ||
schemas: {}, | ||
}, | ||
}; | ||
for (const typeName in types) { | ||
const type = types[typeName]; | ||
if (((0, graphql_1.isObjectType)(type) || (0, graphql_1.isInputObjectType)(type)) && | ||
!(0, graphql_1.isIntrospectionType)(type)) { | ||
swagger.components.schemas[typeName] = (0, types_1.buildSchemaObjectFromType)(type, { | ||
customScalars, | ||
}); | ||
} | ||
} | ||
if (components) { | ||
swagger.components = { ...components, ...swagger.components }; | ||
} | ||
if (security) { | ||
swagger.security = security; | ||
} | ||
if (tags) { | ||
swagger.tags = tags; | ||
} | ||
return { | ||
addRoute(info, config) { | ||
const basePath = config?.basePath || ''; | ||
const path = basePath + | ||
(0, utils_1.normalizePathParamForOpenAPI)(info.path); | ||
if (!swagger.paths[path]) { | ||
swagger.paths[path] = {}; | ||
} | ||
const pathsObj = swagger.paths[path]; | ||
pathsObj[info.method.toLowerCase()] = (0, operations_1.buildPathFromOperation)({ | ||
url: path, | ||
operation: info.document, | ||
schema, | ||
useRequestBody: ['POST', 'PUT', 'PATCH'].includes(info.method), | ||
tags: info.tags || [], | ||
description: info.description || '', | ||
customScalars, | ||
}); | ||
}, | ||
get() { | ||
return swagger; | ||
}, | ||
}; | ||
} | ||
exports.OpenAPI = OpenAPI; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { DocumentNode, GraphQLSchema, OperationDefinitionNode, TypeNode, VariableDefinitionNode } from 'graphql'; | ||
import { OpenAPIV3 } from 'openapi-types'; | ||
export declare function buildPathFromOperation({ url, schema, operation, useRequestBody, tags, description, customScalars, }: { | ||
url: string; | ||
schema: GraphQLSchema; | ||
operation: DocumentNode; | ||
useRequestBody: boolean; | ||
tags?: string[]; | ||
description?: string; | ||
customScalars: Record<string, any>; | ||
}): OpenAPIV3.OperationObject; | ||
export declare function resolveRequestBody(variables: ReadonlyArray<VariableDefinitionNode> | undefined, schema: GraphQLSchema, operation: OperationDefinitionNode, opts: { | ||
customScalars: Record<string, any>; | ||
enumTypes: Record<string, any>; | ||
}): {}; | ||
export declare function resolveParamSchema(type: TypeNode, opts: { | ||
customScalars: Record<string, any>; | ||
enumTypes: Record<string, any>; | ||
}): any; | ||
export declare function resolveResponse({ schema, operation, opts, }: { | ||
schema: GraphQLSchema; | ||
operation: OperationDefinitionNode; | ||
opts: { | ||
customScalars: Record<string, any>; | ||
enumTypes: Record<string, any>; | ||
}; | ||
}): any; | ||
export declare function isInPath(url: string, param: string): boolean; | ||
export declare function resolveVariableDescription(schema: GraphQLSchema, operation: OperationDefinitionNode, variable: VariableDefinitionNode): string | undefined; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.resolveVariableDescription = exports.isInPath = exports.resolveResponse = exports.resolveParamSchema = exports.resolveRequestBody = exports.buildPathFromOperation = void 0; | ||
const graphql_1 = require("graphql"); | ||
const ast_1 = require("../ast"); | ||
const utils_1 = require("./utils"); | ||
const types_1 = require("./types"); | ||
const title_case_1 = require("title-case"); | ||
function buildPathFromOperation({ url, schema, operation, useRequestBody, tags, description, customScalars, }) { | ||
const info = (0, ast_1.getOperationInfo)(operation); | ||
const enumTypes = resolveEnumTypes(schema); | ||
const summary = resolveDescription(schema, info.operation); | ||
const variables = info.operation.variableDefinitions; | ||
const pathParams = variables?.filter((variable) => isInPath(url, variable.variable.name.value)); | ||
const bodyParams = variables?.filter((variable) => !isInPath(url, variable.variable.name.value)); | ||
return { | ||
tags, | ||
description, | ||
summary, | ||
operationId: info.name, | ||
...(useRequestBody | ||
? { | ||
parameters: resolveParameters(url, pathParams, schema, info.operation, { customScalars, enumTypes }), | ||
requestBody: { | ||
content: { | ||
'application/json': { | ||
schema: resolveRequestBody(bodyParams, schema, info.operation, { customScalars, enumTypes }), | ||
}, | ||
}, | ||
}, | ||
} | ||
: { | ||
parameters: resolveParameters(url, variables, schema, info.operation, { customScalars, enumTypes }), | ||
}), | ||
responses: { | ||
200: { | ||
description: summary, | ||
content: { | ||
'application/json': { | ||
schema: resolveResponse({ | ||
schema, | ||
operation: info.operation, | ||
opts: { customScalars, enumTypes }, | ||
}), | ||
}, | ||
}, | ||
}, | ||
}, | ||
}; | ||
} | ||
exports.buildPathFromOperation = buildPathFromOperation; | ||
function resolveEnumTypes(schema) { | ||
const enumTypes = Object.values(schema.getTypeMap()) | ||
.filter(graphql_1.isEnumType); | ||
return Object.fromEntries(enumTypes.map((type) => [ | ||
type.name, | ||
{ | ||
type: 'string', | ||
enum: type.getValues().map((value) => value.name), | ||
}, | ||
])); | ||
} | ||
function resolveParameters(url, variables, schema, operation, opts) { | ||
if (!variables) { | ||
return []; | ||
} | ||
return variables.map((variable) => { | ||
return { | ||
in: isInPath(url, variable.variable.name.value) ? 'path' : 'query', | ||
name: variable.variable.name.value, | ||
required: variable.type.kind === graphql_1.Kind.NON_NULL_TYPE, | ||
schema: resolveParamSchema(variable.type, opts), | ||
description: resolveVariableDescription(schema, operation, variable), | ||
}; | ||
}); | ||
} | ||
function resolveRequestBody(variables, schema, operation, opts) { | ||
if (!variables) { | ||
return {}; | ||
} | ||
const properties = {}; | ||
const required = []; | ||
variables.forEach((variable) => { | ||
if (variable.type.kind === graphql_1.Kind.NON_NULL_TYPE) { | ||
required.push(variable.variable.name.value); | ||
} | ||
properties[variable.variable.name.value] = { | ||
...resolveParamSchema(variable.type, opts), | ||
description: resolveVariableDescription(schema, operation, variable), | ||
}; | ||
}); | ||
return { | ||
type: 'object', | ||
properties, | ||
...(required.length ? { required } : {}), | ||
}; | ||
} | ||
exports.resolveRequestBody = resolveRequestBody; | ||
// array -> [type] | ||
// type -> $ref | ||
// scalar -> swagger primitive | ||
function resolveParamSchema(type, opts) { | ||
if (type.kind === graphql_1.Kind.NON_NULL_TYPE) { | ||
return resolveParamSchema(type.type, opts); | ||
} | ||
if (type.kind === graphql_1.Kind.LIST_TYPE) { | ||
return { | ||
type: 'array', | ||
items: resolveParamSchema(type.type, opts), | ||
}; | ||
} | ||
const primitive = (0, utils_1.mapToPrimitive)(type.name.value); | ||
return (primitive || | ||
opts.customScalars[type.name.value] || | ||
opts.enumTypes[type.name.value] || { $ref: (0, utils_1.mapToRef)(type.name.value) }); | ||
} | ||
exports.resolveParamSchema = resolveParamSchema; | ||
function resolveResponse({ schema, operation, opts, }) { | ||
const operationType = operation.operation; | ||
const rootField = operation.selectionSet.selections[0]; | ||
if (rootField.kind === graphql_1.Kind.FIELD) { | ||
if (operationType === 'query') { | ||
const queryType = schema.getQueryType(); | ||
const field = queryType.getFields()[rootField.name.value]; | ||
return (0, types_1.resolveFieldType)(field.type, opts); | ||
} | ||
if (operationType === 'mutation') { | ||
const mutationType = schema.getMutationType(); | ||
const field = mutationType.getFields()[rootField.name.value]; | ||
return (0, types_1.resolveFieldType)(field.type, opts); | ||
} | ||
} | ||
} | ||
exports.resolveResponse = resolveResponse; | ||
function isInPath(url, param) { | ||
return url.includes(`:${param}`) || url.includes(`{${param}}`); | ||
} | ||
exports.isInPath = isInPath; | ||
function getOperationFieldNode(schema, operation) { | ||
const selection = operation.selectionSet.selections[0]; | ||
const fieldName = selection.name.value; | ||
const typeDefinition = schema.getType((0, title_case_1.titleCase)(operation.operation)); | ||
if (!typeDefinition) { | ||
return undefined; | ||
} | ||
const definitionNode = typeDefinition.astNode || (0, graphql_1.parse)((0, graphql_1.printType)(typeDefinition)).definitions[0]; | ||
if (!isObjectTypeDefinitionNode(definitionNode)) { | ||
return undefined; | ||
} | ||
return definitionNode.fields.find((field) => field.name.value === fieldName); | ||
} | ||
function resolveDescription(schema, operation) { | ||
const fieldNode = getOperationFieldNode(schema, operation); | ||
return fieldNode?.description?.value || ''; | ||
} | ||
function resolveVariableDescription(schema, operation, variable) { | ||
const fieldNode = getOperationFieldNode(schema, operation); | ||
const argument = fieldNode?.arguments?.find((arg) => arg.name.value === variable.variable.name.value); | ||
return argument?.description?.value; | ||
} | ||
exports.resolveVariableDescription = resolveVariableDescription; | ||
function isObjectTypeDefinitionNode(node) { | ||
return node.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { GraphQLObjectType, GraphQLInputObjectType, GraphQLType } from 'graphql'; | ||
export declare function buildSchemaObjectFromType(type: GraphQLObjectType | GraphQLInputObjectType, opts: { | ||
customScalars: Record<string, any>; | ||
}): any; | ||
export declare function resolveFieldType(type: GraphQLType, opts: { | ||
customScalars: Record<string, any>; | ||
}): any; |
Oops, something went wrong.