UNPKG

graphql-from-swagger

Version:

<a href="http://badge.fury.io/js/graphql-from-swagger"><img src="https://badge.fury.io/js/graphql-from-swagger.svg"></a>

322 lines (286 loc) 10.7 kB
import fetch from 'node-fetch'; import path from 'path'; import fs from 'fs'; import YAML from 'yamljs'; import { getArgsStringFromOperationId, indent, getRelativePath, getInstanceNameFromClass, checkUrlForm, replaceOddChars, getGQLTypeNameFromURL, } from './utils'; export async function createRESTDataSource( swaggerPaths: Array<string>, typesFiles: Array<string>, restDataSourceOutputFiles: Array<string>, ): Promise<Array<string>> { const totalLength = swaggerPaths.length; const currentDir = process.cwd(); const result: Array<string> = []; for (let i = 0; i < totalLength; i++) { const imports = []; imports.push(`import { RESTDataSource } from 'apollo-datasource-rest';`); imports.push(`import { URLSearchParams } from 'url';`); let swaggerString: string; if (checkUrlForm(swaggerPaths[i])) { swaggerString = await (await fetch(swaggerPaths[i])).text(); } else { swaggerString = await fs.readFileSync(path.join(currentDir, swaggerPaths[i]), 'utf-8'); } let swaggerJSON: { [key: string]: any }; if (swaggerPaths[i].endsWith('.json')) { swaggerJSON = JSON.parse(swaggerString); } else if (swaggerPaths[i].endsWith('.yaml')) { swaggerJSON = YAML.parse(swaggerString); } else { throw new Error('Swagger is Invalid!'); } const className = path.basename(restDataSourceOutputFiles[i], '.ts'); const classSentences = []; let hostUrl = ''; if (swaggerJSON.host) { hostUrl = swaggerJSON.host.startsWith('http') ? swaggerJSON.host : 'http://' + swaggerJSON.host; if (swaggerJSON.basePath) { hostUrl += swaggerJSON.basePath; } } else if (swaggerJSON.servers && swaggerJSON.servers.length > 0 && swaggerJSON.servers[0].url) { hostUrl = swaggerJSON.servers[0].url.startsWith('http') ? swaggerJSON.servers[0].url : 'http://' + swaggerJSON.servers[0].url; } else { throw new Error('hostUrl is not exist!'); } classSentences.push( ` export class ${className} extends RESTDataSource { constructor() { super(); this.baseURL = '${hostUrl}'; } `.trim(), ); const functions: string[] = []; const argTypes: string[] = []; for (const endpoint in swaggerJSON.paths) { for (const method in swaggerJSON.paths[endpoint]) { if (method === 'parameters') continue; const field = swaggerJSON.paths[endpoint][method]; const operationId = field.operationId ? replaceOddChars(field.operationId) : getGQLTypeNameFromURL(method, endpoint); const parameters: { [key: string]: string[] } = {}; if (field.parameters) { for (const parameter of field.parameters) { if (parameters[parameter.in] !== undefined) { parameters[parameter.in].push(parameter.name); } else { parameters[parameter.in] = [parameter.name]; } } } const args = Object.keys(parameters).reduce((result: string[], param) => { if (parameters[param] === undefined) return result; return [...result, ...parameters[param]]; }, []); const func: string[] = []; func.push(''); func.push(...getCommentBlocks(field)); const argumentsString = args.length ? `{ ${args.join(', ')} }: ${getArgsStringFromOperationId(operationId, method)}` : ''; if (args.length) argTypes.push(`${getArgsStringFromOperationId(operationId, method)}`); func.push(indent(`async ${operationId}(${argumentsString}) {`)); if (parameters['query']) func.push(indent(`const queries = { ${parameters['query'].join(', ')} };`, 2)); const queryString = parameters['query'] ? `?\${new URLSearchParams(queries as { [key: string]: any })}` : ''; let bodyString = parameters['body'] && method !== 'get' ? `, { ${parameters['body'].join(', ')} }` : ''; let requestInitString = ''; let headerStrings = []; const isXWwwFormUrlEncoded = field.consumes && field.consumes.includes('application/x-www-form-urlencoded'); if (isXWwwFormUrlEncoded) { headerStrings.push(`'Content-Type': 'application/x-www-form-urlencoded'`); bodyString = parameters['formData'] && method !== 'get' ? `, { ${parameters['formData'].join(', ')} }` : ''; } if (parameters['header']) { headerStrings.push(...parameters['header']); } if (headerStrings && headerStrings.length > 0) { requestInitString = `, { headers: { ${headerStrings.join(',\n') + ','} }, }`; } func.push(indent(`return this.${method}(\`${endpoint.replace('{', '${')}${queryString}\`${bodyString}${requestInitString});`, 2)); func.push(indent(`}`)); functions.push(func.join('\n')); } } imports.push( ` import { ${argTypes.join(',\n ')} } from '${getRelativePath(restDataSourceOutputFiles[i], typesFiles[i])}'; `.trim(), ); result.push([...imports, '', ...classSentences, ...functions, '}'].join('\n')); } return result; } function getCommentBlocks(field: { [key: string]: any }): string[] { const blocks: string[] = []; blocks.push(indent(`/**`)); if (field.deprecated) blocks.push(indent(` * @deprecated`)); if (field.tags && field.tags.length > 0) blocks.push(indent(` * @tags ${field.tags.join(', ')}`)); if (field.summary) blocks.push(indent(` * @summary ${field.summary}`)); if (field.description) blocks.push(indent(` * @description ${field.description}`)); if (field.parameters) { for (const parameter of field.parameters) { let type; if (parameter.type) { if (parameter.type !== 'array') { type = parameter.type; } else { type = parameter.items.type + '[]'; } } else if (parameter.schema) { if (parameter.schema.$ref) { type = parameter.schema.$ref.split('#/definitions/').pop(); } else if (parameter.schema.items && parameter.schema.type === 'array') { if (parameter.schema.items.type) { type = parameter.schema.items.type + '[]'; } else if (parameter.schema.items.$ref) { type = parameter.schema.items.$ref.split('#/definitions/').pop() + '[]'; } } } blocks.push( indent( ` * @param${type ? ` {${type}}` : ''} ${parameter.name} ${parameter.description ? '- ' + parameter.description : ''} @${ parameter.in } ${parameter.required ? '@required' : ''}`, ), ); } } blocks.push(indent(` */`)); return blocks; } export async function createResolvers( swaggerPaths: Array<string>, typesFiles: Array<string>, restDataSourceFiles: Array<string>, resolversOutputFiles: Array<string>, ): Promise<Array<string>> { const totalLength = swaggerPaths.length; const currentDir = process.cwd(); const result: Array<string> = []; for (let i = 0; i < totalLength; i++) { const imports = []; const queries = []; const mutations = []; const resolvers = []; const argTypes = []; const typeDefs = []; let swaggerString: string; if (checkUrlForm(swaggerPaths[i])) { swaggerString = await (await fetch(swaggerPaths[i])).text(); } else { swaggerString = await fs.readFileSync(path.join(currentDir, swaggerPaths[i]), 'utf-8'); } let swaggerJSON: { [key: string]: any }; if (swaggerPaths[i].endsWith('.json')) { swaggerJSON = JSON.parse(swaggerString); } else if (swaggerPaths[i].endsWith('.yaml')) { swaggerJSON = YAML.parse(swaggerString); } else { throw new Error('Swagger is Invalid!'); } const className = path.basename(restDataSourceFiles[i], '.ts'); for (const endpoint in swaggerJSON.paths) { for (const method in swaggerJSON.paths[endpoint]) { if (method === 'parameters') continue; const field = swaggerJSON.paths[endpoint][method]; const operationId = field.operationId ? replaceOddChars(field.operationId) : getGQLTypeNameFromURL(method, endpoint); const parameters: { [key: string]: string[] } = {}; if (field.parameters) { for (const parameter of field.parameters) { if (parameters[parameter.in] !== undefined) { parameters[parameter.in].push(parameter.name); } else { parameters[parameter.in] = [parameter.name]; } } } const args = Object.keys(parameters).reduce((result: string[], param) => { if (parameters[param] === undefined) return result; return [...result, ...parameters[param]]; }, []); if (args.length) argTypes.push(`${getArgsStringFromOperationId(operationId, method)}`); if (method === 'get') { queries.push( indent( ` ${operationId}: (parent: Query, args: ${ args.length ? getArgsStringFromOperationId(operationId, method) : 'null' }, context: Context, info: GraphQLResolveInfo) => { return context.dataSources.${getInstanceNameFromClass(className)}.${operationId}(${args.length ? 'args' : ''}); }, `.trim(), 2, ), ); } else { mutations.push( indent( ` ${operationId}: (parent: Mutation, args: ${ args.length ? getArgsStringFromOperationId(operationId, method) : 'null' }, context: Context, info: GraphQLResolveInfo) => { return context.dataSources.${getInstanceNameFromClass(className)}.${operationId}(${args.length ? 'args' : ''}); }, `.trim(), 2, ), ); } } } resolvers.push( ` export const resolvers = { Query: { ${queries.join('\n')} }, Mutation: { ${mutations.join('\n')} } } `.trim(), ); imports.push(`import { GraphQLResolveInfo } from 'graphql';`); imports.push( ` import { ${['Query', 'Mutation', ...argTypes].join(',\n ')} } from '${getRelativePath(resolversOutputFiles[i], typesFiles[i])}'; `.trim(), ); imports.push( ` import { ${className} } from '${getRelativePath(resolversOutputFiles[i], restDataSourceFiles[i])}'; `.trim(), ); typeDefs.push( ` type DataSources = { ${getInstanceNameFromClass(className)}: ${className}; }; `.trim(), ); typeDefs.push( ` type Context = { dataSources: DataSources; }; `.trim(), ); result.push([...imports, '', ...typeDefs, '', resolvers].join('\n')); } return result; }