UNPKG

adminjs-graphql

Version:
256 lines 12.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GraphQLConnection = void 0; const graphql_1 = require("graphql"); const GraphQLClient_1 = require("./GraphQLClient"); const GraphQLProperty_1 = require("./GraphQLProperty"); /** * GraphQLConnection connects to a GraphQL API, and initializes a list of * configured resources with data from the remote API schema, so that they * can be used as AdminBro resources. */ class GraphQLConnection { constructor(resources, options, onError) { var _a, _b; this.resources = resources; this.onError = onError; this.tag = "GraphQLConnection"; this.name = (_a = options === null || options === void 0 ? void 0 : options.name) !== null && _a !== void 0 ? _a : "graphql"; const url = (_b = options === null || options === void 0 ? void 0 : options.url) !== null && _b !== void 0 ? _b : "http://localhost:3000/graphql"; this.client = new GraphQLClient_1.GraphQLClient(url, options === null || options === void 0 ? void 0 : options.agentOptions); this.headers = options === null || options === void 0 ? void 0 : options.headers; } get r() { return this.resources.reduce((map, resource) => { map[resource.id] = resource; return map; }, {}); } async init() { const fullSchema = await this.fetchSchema(); await this.inflateResources(fullSchema); } async fetchSchema() { const query = (0, graphql_1.getIntrospectionQuery)({ descriptions: false }); const result = await this.request(query); return (0, graphql_1.buildClientSchema)(result); } static graphQLTypeToPropertyType(graphQLType) { switch (graphQLType.name) { case "String": case "ID": return "string"; case "Float": return "float"; case "Int": return "number"; case "Bool": return "boolean"; case "Date": return "datetime"; default: return "mixed"; } } async inflateResources(schema) { this.resources .map((res) => res) .map((resource) => { var _a; const findMapping = resource.findOne(42); let parsed = typeof findMapping.query === "string" ? (0, graphql_1.parse)(findMapping.query) : findMapping.query; const typeInfo = new graphql_1.TypeInfo(schema); const path = []; resource.typeMap = new Map(); resource.connection = this; resource.tag = "GraphQLResource"; parsed = expandFragments(parsed); const operationDefinition = parsed.definitions.find((def) => def.kind === "OperationDefinition"); if (!operationDefinition) { throw new Error("Document without operation is not allowed"); } const toplevelSelections = operationDefinition.selectionSet.selections; if (toplevelSelections.length !== 1) { throw new Error("Top level selections must contain exactly one field"); } const topNode = operationDefinition; // Initialize with "root" object const objectStack = [[]]; const propertyMap = new Map(); (0, graphql_1.visit)(topNode, (0, graphql_1.visitWithTypeInfo)(typeInfo, { Field: { enter: (field) => { var _a, _b, _c, _d, _e; const parentType = typeInfo.getParentType(); if ((parentType === null || parentType === void 0 ? void 0 : parentType.name) === "Query" || (parentType === null || parentType === void 0 ? void 0 : parentType.name) === "Mutation") { return; } const fieldName = field.name.value; let graphQLType = typeInfo.getType(); if (!graphQLType) { throw new Error(`Unexpected empty type for field "${fieldName}" of resource "${resource.id}"`); } let isArray = false; let isRequired = false; while ((0, graphql_1.isWrappingType)(graphQLType)) { if (graphQLType instanceof graphql_1.GraphQLList) { isArray = true; } else if (graphQLType instanceof graphql_1.GraphQLNonNull && !isArray) { isRequired = true; } graphQLType = graphQLType.ofType; } const namedType = graphQLType; let enumValues; if (namedType instanceof graphql_1.GraphQLEnumType) { enumValues = namedType .getValues() .map((val) => val.value); } const parentPath = path.join("."); const propertyPath = [...path, fieldName].join("."); let propertyType; let referencing; if (namedType instanceof graphql_1.GraphQLObjectType) { const objectFields = namedType.getFields(); const selections = (_b = (_a = field.selectionSet) === null || _a === void 0 ? void 0 : _a.selections) !== null && _b !== void 0 ? _b : []; if (selections.length === 1 && selections[0].kind === "Field") { const fieldName = selections[0].name.value; const objectField = objectFields[fieldName]; if (!objectField) { throw new Error(`Field ${fieldName} is not in ${namedType.name}`); } const fieldType = objectField.type; if (typeIsID(fieldType)) { propertyType = "reference"; referencing = namedType.name; } } } if (!propertyType) { propertyType = (propertyPath in ((_c = resource.referenceFields) !== null && _c !== void 0 ? _c : {}) && "reference") || ((enumValues === null || enumValues === void 0 ? void 0 : enumValues.length) && "string") || GraphQLConnection.graphQLTypeToPropertyType(namedType); } const isSortable = resource.sortableFields ? resource.sortableFields.includes(propertyPath) : propertyType != "reference"; const parentProperty = propertyMap.get(parentPath); const useFullPath = !resource.makeSubproperties && !((parentProperty === null || parentProperty === void 0 ? void 0 : parentProperty.type()) === "mixed" && (parentProperty === null || parentProperty === void 0 ? void 0 : parentProperty.isArray())); const property = new GraphQLProperty_1.GraphQLPropertyAdapter({ path: useFullPath ? propertyPath : fieldName, type: propertyType, isId: namedType.name === "ID" && propertyType !== "reference", isSortable: isSortable, referencing: referencing !== null && referencing !== void 0 ? referencing : (_d = resource.referenceFields) === null || _d === void 0 ? void 0 : _d[propertyPath], enumValues, isArray, isRequired, }); objectStack[objectStack.length - 1].push(property); propertyMap.set(propertyPath, property); (_e = resource.typeMap) === null || _e === void 0 ? void 0 : _e.set(propertyPath, namedType); if (field.selectionSet) { path.push(fieldName); objectStack.push([]); } }, leave: (field) => { var _a; const parentType = (_a = typeInfo.getParentType()) === null || _a === void 0 ? void 0 : _a.name; if (parentType === "Query" || parentType === "Mutation") { return; } if (field.selectionSet) { path.pop(); const currentObject = objectStack.pop(); if (currentObject === undefined) { throw new Error("Unexpected empty object"); } const lastObject = objectStack[objectStack.length - 1]; const lastProperty = lastObject[lastObject.length - 1]; if (lastProperty && ((lastProperty.type() === "mixed" && lastProperty.isArray()) || resource.makeSubproperties)) { lastProperty.setSubProperties(currentObject); } else if (currentObject.length !== 1 || !currentObject[0].isId()) { lastObject.push(...currentObject); } } }, }, })); resource.properties = (_a = objectStack .pop()) === null || _a === void 0 ? void 0 : _a.filter((prop) => prop.type() !== "mixed" || prop.subProperties().length); }); } formatGraphQLErrors(errors) { return ("GraphQL request error: " + errors.map((error) => error.message).join(", ")); } async request(document, variables) { var _a, _b, _c, _d; try { const headers = (_a = this.headers) === null || _a === void 0 ? void 0 : _a.call(this); const response = await this.client.request(document, variables, headers); if ((_b = response.errors) === null || _b === void 0 ? void 0 : _b.length) { this.reportAndThrow(new Error(this.formatGraphQLErrors(response.errors)), response.errors); } return response.data; } catch (thrown) { let error = thrown; const axiosError = error; const graphQLErrors = (_d = (_c = axiosError.response) === null || _c === void 0 ? void 0 : _c.data) === null || _d === void 0 ? void 0 : _d.errors; if (graphQLErrors) { error = new Error(this.formatGraphQLErrors(graphQLErrors)); } this.reportAndThrow(error, graphQLErrors); } } reportAndThrow(error, originalErrors) { var _a; (_a = this.onError) === null || _a === void 0 ? void 0 : _a.call(this, error, originalErrors); throw error; } } exports.GraphQLConnection = GraphQLConnection; function expandFragments(node) { const fragmentDefinitions = node.definitions.filter((def) => def.kind === "FragmentDefinition"); return (0, graphql_1.visit)(node, { FragmentSpread: (spread) => { const fragment = fragmentDefinitions.find((def) => def.name.value === spread.name.value); if (!fragment) { throw new Error("Invalid spread reference"); } return fragment.selectionSet; }, }); } function typeIsID(fieldType) { while ((0, graphql_1.isWrappingType)(fieldType)) { fieldType = fieldType.ofType; } return fieldType === graphql_1.GraphQLID; } //# sourceMappingURL=GraphQLConnection.js.map