UNPKG

@backland/schema

Version:

TypeScript schema declaration and validation library with static type inference

476 lines (474 loc) 12.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getInnerType = getInnerType; exports.getQueryTemplates = getQueryTemplates; exports.getSchemaQueryTemplates = getSchemaQueryTemplates; exports.processField = processField; var _utils = require("@backland/utils"); var _graphql = require("graphql"); var _createGraphQLSchema = require("../createGraphQLSchema"); var _LiteralField = require("../fields/LiteralField"); function getSchemaQueryTemplates(schema, options = {}) { const { depthLimit = 5000, includeDeprecatedFields = true } = options; const query = schema.getQueryType(); const mutation = schema.getMutationType(); const subscription = schema.getSubscriptionType(); let fullQuery = ''; const queryByResolver = { mutation: {}, query: {}, subscription: {} }; const items = [query ? { kind: _createGraphQLSchema.resolverKinds.query, value: query } : undefined, mutation ? { kind: _createGraphQLSchema.resolverKinds.mutation, value: mutation } : undefined, subscription ? { kind: _createGraphQLSchema.resolverKinds.subscription, value: subscription } : undefined].filter(Boolean); items.forEach(item => { if (!item) return; const { value, kind } = item; Object.values(value.getFields()).forEach(graphQLField => { const item = getQueryTemplates({ depthLimit, graphQLField, includeDeprecatedFields, kind, queryKind: 'mainQuery' }); queryByResolver[kind][graphQLField.name] = item; fullQuery += `${item.fullQuery}`; }); }); return { fullQuery: fullQuery.trim(), queryByResolver }; } /** * Generate the query for the specified field * @param params */ function getQueryTemplates(params) { const { kind = 'query' } = params; const { name } = params.graphQLField; const { breadcrumb = [name], graphQLField, queryKind, includeDeprecatedFields = true, format = true } = params; const cache = {}; const { payload } = processField({ cache, graphQLField, includeDeprecatedFields }); const fieldStrings = fieldsToString({ breadcrumb, cache, field: payload, isTopQuery: false }); const innerQuery = payload.isObject ? `{${fieldStrings.query}}` : ''; let fullQuery = ''; let fieldQuery = `${name} ${fieldStrings.argsParsed.strings.innerArgsString} ${innerQuery}`; if (queryKind === 'mainQuery') { const { allArgs } = fieldStrings; const argValues = Object.values(allArgs); const topArgs = argValues.map(el => el.strings.topArgsStringPart).filter(Boolean); const innerArgs = argValues.map(el => el.strings.innerArgsStringPart).filter(Boolean); const topArgsString = topArgs.length ? `(${topArgs.join(',')})` : ''; const innerArgsString = innerArgs.length ? `(${innerArgs.join(',')})` : ''; fullQuery += `${kind} ${name}${topArgsString} { ${name} ${innerArgsString} ${innerQuery}}`; } else { fullQuery += `${kind} ${fieldQuery}`; } if (format) { fullQuery = prettifyQuery(fullQuery, 'mainQuery'); fieldQuery = prettifyQuery(fieldQuery, 'fieldQuery'); } fullQuery += `\n`; return { ...fieldStrings, fieldQuery, fields: payload, fullQuery }; } function getFieldPayload({ graphQLField, cache }) { const { args, type } = graphQLField; const { innerType } = getInnerType(type); const innerTypeString = innerType.toJSON(); const { hash: key, readableHash } = hashField(graphQLField); if (cache[key]) return cache[key]; const payload = cache[key] = { args, children: [], deprecationReason: (0, _utils.getByPath)(innerType, 'deprecationReason'), description: (0, _utils.getByPath)(innerType, 'description'), fields: {}, hash: key, innerTypeString: innerTypeString, isObject: false, isUnion: false, readableHash }; if ((0, _graphql.isObjectType)(innerType)) { payload.isObject = true; Object.entries(innerType.getFields()).forEach(([name, field]) => { payload.fields[name] = getFieldPayload({ cache, graphQLField: field }); }); } if ((0, _graphql.isUnionType)(innerType)) { const unionTypes = innerType.getTypes(); payload.isUnion = true; unionTypes.forEach(unionType => { const unionItem = getFieldPayload({ cache, graphQLField: { args: [], astNode: unionType.astNode, deprecationReason: (0, _utils.getByPath)(unionType, 'deprecationReason'), description: unionType.description, extensions: unionType.extensions, name: unionType.name, type: unionType } }); payload.children.push(unionItem); }); } return payload; } function processField(config) { const { includeDeprecatedFields, cache = {}, graphQLField } = config; const { innerType: type } = getInnerType(graphQLField.type); const { hash: key } = hashField(graphQLField); if (cache[key] !== undefined) { return { cache, payload: cache[key] }; } const payload = getFieldPayload({ cache, graphQLField }); if ((0, _graphql.isObjectType)(type)) { const gqlTypeFields = type.getFields(); const fields = Object.entries(gqlTypeFields).filter(([_, child]) => { return includeDeprecatedFields || !child.deprecationReason; }); fields.forEach(([_, field]) => { const item = getFieldPayload({ cache, graphQLField: field }); if (!item.isObject) { return; } processField({ cache, graphQLField: field, includeDeprecatedFields }); }); } return { cache, payload }; } function fieldsToString(config) { const { field, // cache, breadcrumb, fieldsToStringCache = {} } = config; const { fields, args, hash } = field; if (fieldsToStringCache[hash]) { return fieldsToStringCache[hash]; } const allArgs = config.allArgs || {}; const argsParsed = parseArgs({ args, breadcrumb }); allArgs[hash] = argsParsed; const self = { allArgs, argsParsed, query: '' }; fieldsToStringCache[hash] = self; if (field.isObject) { Object.entries(fields || {}).forEach(([name, field]) => { const _breadcrumb = [...breadcrumb, name]; if (!field.isObject && !field.isUnion) { const { argsParsed: { strings: { innerArgsString } } } = fieldsToString({ allArgs, breadcrumb: _breadcrumb, cache, field, fieldsToStringCache, isTopQuery: false }); self.query += ` ${name}${innerArgsString} `; return; } if (field.isObject) { const child = fieldsToString({ allArgs, breadcrumb: _breadcrumb, cache, field, fieldsToStringCache, isTopQuery: false }); const { argsParsed: { strings: { innerArgsString } } } = child; self.query += ` ${name}${innerArgsString} { `; self.query += child.query; self.query += ` } `; } if (field.isUnion) { let childQuery = ''; const argsStrings = []; const topArgsStrings = []; field.children.forEach(item => { const u_field = cache[item.hash]; const child = fieldsToString({ breadcrumb: _breadcrumb, cache, field: u_field, fieldsToStringCache, isTopQuery: false }); const { argsParsed: { strings: { innerArgsStringPart, topArgsStringPart } } } = child; argsStrings.push(innerArgsStringPart); topArgsStrings.push(topArgsStringPart); childQuery += `... on ${item.innerTypeString} { ${child.query} }`; }); self.query += ` ${name}${argsStrings ? `${argsStrings.join()}` : ''} { `; self.query += childQuery; self.query += ` } `; } }); } else { const { argsParsed: { strings: { innerArgsString } } } = fieldsToString({ allArgs, breadcrumb, cache, field, fieldsToStringCache, isTopQuery: false }); self.query += `${innerArgsString}`; } return self; } function parseArgs(config) { const { args, breadcrumb } = config; const vars = []; if (!(args !== null && args !== void 0 && args.length)) { return { strings: parsedArgsToString(vars), vars }; } let prefix = breadcrumb.length ? breadcrumb.join('_') + '_' : ''; args.forEach(arg => { const { type, description, deprecationReason, defaultValue, name } = arg; const varName = `$${prefix}${name}`; const comments = descriptionsToComments(deprecationReason, description); let _defaultValue = arg.defaultValue !== undefined ? _LiteralField.LiteralField.utils.serialize(defaultValue) : undefined; if (typeof defaultValue === 'string') { _defaultValue = JSON.stringify(_defaultValue); } vars.push({ comments, defaultValue: _defaultValue, name, type: type.toString(), varName }); }); return { strings: parsedArgsToString(vars), vars }; } function parsedArgsToString(parsed) { const innerParts = []; const topParts = []; parsed.forEach(({ type, defaultValue, name, varName, comments }) => { topParts.push(`${comments}${varName}: ${type}`); innerParts.push(`${comments}${name}: ${varName}`); if (defaultValue !== undefined) { topParts.push(`= ${defaultValue}`); } }); const topArgsStringPart = topParts.join(',').replace(',=', ' = '); const innerArgsStringPart = innerParts.join(',').replace(',=', ' = '); return { innerArgsString: innerArgsStringPart.length ? `(${innerArgsStringPart})` : '', innerArgsStringPart: innerArgsStringPart, topArgsString: topArgsStringPart.length ? `(${topArgsStringPart})` : '', topArgsStringPart }; } function getInnerType(graphqlType) { const wrappers = []; while ('ofType' in graphqlType) { if ((0, _graphql.isUnionType)(graphqlType)) { wrappers.push('union'); } if (!(0, _graphql.isNonNullType)(graphqlType)) { wrappers.push('optional'); } if ((0, _graphql.isListType)(graphqlType)) { wrappers.push('list'); } graphqlType = graphqlType.ofType; } const wrappersPath = wrappers.join(''); const innerTypeJSON = graphqlType.toJSON(); return { innerType: graphqlType, innerTypeJSON, wrappers, wrappersPath }; } function hashField(graphQLField) { const { wrappersPath, innerType } = getInnerType(graphQLField.type); let argString = ''; graphQLField.args.forEach(({ type, description, deprecationReason, defaultValue, name }) => { argString += [name, type.toString(), description, deprecationReason, defaultValue].filter(el => el !== null && el !== undefined).join(''); }); const suffix = `${wrappersPath}${argString}`; const hash = (0, _utils.hashString)(suffix); return { hash: `${innerType}${hash}`, readableHash: `${innerType}${suffix}` }; } function descriptionsToComments(...list) { const commentsList = [...list].filter(Boolean); let comments = ''; if (commentsList.length) { return `\n#${commentsList.join('\n#')}\n`; } return comments; } function prettifyQuery(value, queryKind) { const isMainQuery = queryKind === 'mainQuery'; value = value.trim(); try { value = (0, _utils.formatGraphQL)(isMainQuery ? value : `{${value}}`); } catch (e) { // throw new Error(`Failed to prettify:\n${value}\n\n\n${e.stack}`); } if (isMainQuery) return value; return value.trim() // .replace(/^{/, '').replace(/}$/, ''); } //# sourceMappingURL=getQueryTemplates.js.map