UNPKG

postgraphile

Version:

Automatic, high performance, and highly customizable GraphQL API for PostgreSQL

357 lines 16.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.V4Preset = exports.makeV4Preset = exports.PgV4SmartTagsPlugin = exports.PgV4SimpleSubscriptionsPlugin = exports.PgV4InflectionPlugin = exports.PgV4BuildPlugin = exports.PgV4BehaviorPlugin = void 0; exports.makeV4ErrorOutputPreset = makeV4ErrorOutputPreset; const tslib_1 = require("tslib"); require("../index.js"); const graphql_1 = require("grafast/graphql"); const grafserv_1 = require("grafserv"); const PgV4BehaviorPlugin_ts_1 = require("../plugins/PgV4BehaviorPlugin.js"); Object.defineProperty(exports, "PgV4BehaviorPlugin", { enumerable: true, get: function () { return PgV4BehaviorPlugin_ts_1.PgV4BehaviorPlugin; } }); const PgV4BuildPlugin_ts_1 = require("../plugins/PgV4BuildPlugin.js"); Object.defineProperty(exports, "PgV4BuildPlugin", { enumerable: true, get: function () { return PgV4BuildPlugin_ts_1.PgV4BuildPlugin; } }); const PgV4InflectionPlugin_ts_1 = require("../plugins/PgV4InflectionPlugin.js"); Object.defineProperty(exports, "PgV4InflectionPlugin", { enumerable: true, get: function () { return PgV4InflectionPlugin_ts_1.PgV4InflectionPlugin; } }); const PgV4SimpleSubscriptionsPlugin_ts_1 = require("../plugins/PgV4SimpleSubscriptionsPlugin.js"); Object.defineProperty(exports, "PgV4SimpleSubscriptionsPlugin", { enumerable: true, get: function () { return PgV4SimpleSubscriptionsPlugin_ts_1.PgV4SimpleSubscriptionsPlugin; } }); const PgV4SmartTagsPlugin_ts_1 = require("../plugins/PgV4SmartTagsPlugin.js"); Object.defineProperty(exports, "PgV4SmartTagsPlugin", { enumerable: true, get: function () { return PgV4SmartTagsPlugin_ts_1.PgV4SmartTagsPlugin; } }); const amber_ts_1 = tslib_1.__importDefault(require("./amber.js")); function isNotNullish(arg) { return arg != null; } const makeV4Plugin = (options) => { const { classicIds = false, defaultRole } = options; if (defaultRole) { throw new Error(`The 'defaultRole' V4 option is not currently supported in V5; please use the \`preset.grafast.context\` callback instead.`); } const simpleCollectionsBehavior = (() => { switch (options.simpleCollections) { case "both": { return ["connection", "resource:connection", "list", "resource:list"]; } case "only": { return ["-connection", "-resource:connection", "list", "resource:list"]; } case "omit": { return ["connection", "resource:connection", "-list", "-resource:list"]; } default: { return []; } } })(); return { name: "PostGraphileV4CompatibilityPlugin", version: "0.0.0", after: ["PgAttributesPlugin", "PgMutationUpdateDeletePlugin"], inflection: { ignoreReplaceIfNotExists: ["nodeIdFieldName"], replace: { ...(classicIds ? null : { // Undo rename of 'id' to 'rowId' _attributeName(previous, options, details) { const name = previous(details); if (!details.skipRowId && name === "row_id") { const { codec, attributeName } = details; const attribute = codec.attributes[attributeName]; const baseName = attribute.extensions?.tags?.name || attributeName; if (baseName.toLowerCase() === "id" && !codec.isAnonymous) { return "id"; } } return name; }, }), ...(classicIds || options.skipPlugins?.some((p) => p.name === "NodePlugin") ? null : { // Rename GraphQL Global Object Identification 'id' to 'nodeId' // NOTE: this would be better as `_id` in general, but V4 uses `nodeId` nodeIdFieldName() { return "nodeId"; }, }), }, }, schema: { // We could base this on the legacy relations setting; but how to set deprecated? globalBehavior(behavior) { return [ behavior, ...simpleCollectionsBehavior, "-singularRelation:resource:connection", "-singularRelation:resource:list", "condition:attribute:filterBy", "attribute:orderBy", "resource:connection:backwards", ]; }, entityBehavior: { pgResource: "delete:resource:select", pgCodecAttribute: { inferred(behavior, [codec, attributeName]) { const attribute = codec.attributes[attributeName]; const domainUnwrappedCodec = attribute.codec.domainOfCodec ?? attribute.codec; const innermostCodec = unwrapCodec(attribute.codec); const newBehavior = new Set([behavior]); if (innermostCodec.name === "tsvector" || innermostCodec.name === "tsquery") { // Undo force-enable of behavior globally newBehavior.add("-condition:attribute:filterBy"); newBehavior.add("-attribute:orderBy"); } if (domainUnwrappedCodec.arrayOfCodec || domainUnwrappedCodec.rangeOfCodec) { newBehavior.add("-attribute:orderBy"); } if (innermostCodec.isBinary) { newBehavior.add("-attribute:orderBy"); newBehavior.add("-condition:attribute:filterBy"); } return [...newBehavior]; }, }, }, }, }; }; /** bodySizeLimit to maxRequestLength */ function bsl2mrl(bsl) { if (typeof bsl !== "number") { throw new Error(`bodySizeLimit must now be a number of bytes, string formats are no longer supported`); } else { return bsl; } } /** * Extracts the requested fields from a pg error object, handling 'code' to * 'errcode' mapping. */ function pickPgError(err, inFields) { const result = Object.create(null); let fields; if (Array.isArray(inFields)) { fields = inFields; } else if (typeof inFields === "string") { fields = inFields.split(","); } else { throw new Error("Invalid argument to extendedErrors - expected array of strings"); } if (err && typeof err === "object") { fields.forEach((field) => { // pg places 'errcode' on the 'code' property if (typeof field !== "string") { throw new Error("Invalid argument to extendedErrors - expected array of strings"); } const errField = field === "errcode" ? "code" : field; result[field] = err[errField] != null ? String(err[errField]) : err[errField]; }); } return result; } /** * Given a GraphQLError, format it according to the rules described by the * Response Format, Errors section of the GraphQL Specification, plus it can * extract additional error codes from the postgres error, such as 'hint', * 'detail', 'errcode', 'where', etc. - see `extendedErrors` option. */ function extendedFormatError(error, fields) { if (!error) { throw new Error("Received null or undefined error."); } const originalError = error.originalError; const exceptionDetails = originalError && fields ? pickPgError(originalError, fields) : undefined; return { message: error.message, locations: error.locations, path: error.path, ...(exceptionDetails ? { // Reference: https://facebook.github.io/graphql/draft/#sec-Errors extensions: { ...originalError?.extensions, exception: exceptionDetails, }, } : null), }; } function makeV4ErrorOutputPreset(options) { const { extendedErrors, showErrorStack, handleErrors } = options; if (handleErrors) { if (extendedErrors || showErrorStack) { throw new Error(`handleErrors cannot be combined with extendedErrors / showErrorStack`); } return { grafserv: { maskError(error) { return handleErrors([error])[0]; }, }, }; } else if (extendedErrors || showErrorStack) { return { grafserv: { maskError(error) { const formattedError = extendedErrors && extendedErrors.length ? extendedFormatError(error, extendedErrors) : (0, graphql_1.formatError)(error); // If the user wants to see the error’s stack, let’s add it to the // formatted error. if (showErrorStack) { formattedError["stack"] = error.stack != null && showErrorStack === "json" ? error.stack.split("\n") : error.stack; } return formattedError; }, }, }; } else { return { grafserv: {} }; } } const makeV4Preset = (options = {}) => { const { pgUseCustomNetworkScalars, pgStrictFunctions, orderByNullsLast, ...otherGraphileBuildOptions } = options.graphileBuildOptions ?? {}; if (options.enableQueryBatching) { throw new Error(`As of PostGraphile v5, query batching is no longer supported. Query batching has not been standardized as part of the GraphQL-over-HTTP specification efforts, and the need for it has been significantly reduced with the ubiquity of HTTP2+ servers. Further, with incremental delivery (@stream/@defer) on the horizon, query batching will develop a lot of unnecessary complexity that handling at the network layer would bypass.`); } const graphqlPath = options.graphqlRoute ?? "/graphql"; const graphiqlPath = options.graphiqlRoute ?? "/graphiql"; const eventStreamPath = options.eventStreamRoute ?? `${graphqlPath}/stream`; const bodySizeLimit = options.bodySizeLimit; return { extends: [amber_ts_1.default, makeV4ErrorOutputPreset(options)], plugins: [ PgV4BuildPlugin_ts_1.PgV4BuildPlugin, PgV4InflectionPlugin_ts_1.PgV4InflectionPlugin, PgV4SmartTagsPlugin_ts_1.PgV4SmartTagsPlugin, PgV4BehaviorPlugin_ts_1.PgV4BehaviorPlugin, options.simpleSubscriptions ? PgV4SimpleSubscriptionsPlugin_ts_1.PgV4SimpleSubscriptionsPlugin : null, makeV4Plugin(options), ...(options.appendPlugins ? options.appendPlugins : []), ].filter(isNotNullish), disablePlugins: [ ...(options.disableDefaultMutations ? ["PgMutationCreatePlugin", "PgMutationUpdateDeletePlugin"] : []), ...(options.skipPlugins ? options.skipPlugins.map((p) => p.name) : []), ...(options.ignoreRBAC !== false ? ["PgRBACPlugin"] : []), ...(options.ignoreIndexes === false ? [] : ["PgIndexBehaviorsPlugin"]), ], schema: { pgMutationPayloadRelations: true, ...otherGraphileBuildOptions, ...{ simpleCollections: options.simpleCollections }, pgUseCustomNetworkScalars: pgUseCustomNetworkScalars ?? false, pgOrderByNullsLast: orderByNullsLast, pgV4UseTableNameForNodeIdentifier: true, pgForbidSetofFunctionsToReturnNull: options.setofFunctionsContainNulls === false, jsonScalarAsString: options.dynamicJson !== true, ...(options.jwtSecret != null ? { pgJwtSecret: options.jwtSecret, } : null), ...(options.retryOnInitFail ? { retryOnInitFail: options.retryOnInitFail } : null), exportSchemaSDLPath: options.exportGqlSchemaPath, exportSchemaIntrospectionResultPath: options.exportJsonSchemaPath, sortExport: options.sortExport, }, gather: { pgFakeConstraintsAutofixForeignKeyUniqueness: true, pgStrictFunctions, ...(options.jwtPgTypeIdentifier ? { pgJwtTypes: [options.jwtPgTypeIdentifier], } : null), }, grafast: { ...(options.additionalGraphQLContextFromRequest || options.pgSettings ? { async context(ctx) { const context = Object.create(null); const req = ctx.node?.req ?? ctx.ws?.request; const res = ctx.node?.res ?? ctx.ws?.request?.res; if (options.additionalGraphQLContextFromRequest) { if (!req || !res) { console.warn(`Could not determine req/res to use for additionalGraphQLContextFromRequest call.`); console.warn(ctx); } const addl = await options.additionalGraphQLContextFromRequest(req, res); Object.assign(context, addl); } if (options.pgSettings) { if (!req && typeof options.pgSettings === "function") { console.warn(`Could not determine req to use for pgSettings call.`); console.warn(ctx); } const pgSettings = typeof options.pgSettings === "function" ? await options.pgSettings(req) : options.pgSettings; Object.assign(context, { pgSettings }); } return context; }, } : null), ...(options.allowExplain ? { explain: true, } : null), }, grafserv: { graphqlPath, graphiqlPath, eventStreamPath, graphiql: options.graphiql ?? false, watch: options.watchPg, websockets: options.subscriptions, allowedRequestContentTypes: [ ...grafserv_1.DEFAULT_ALLOWED_REQUEST_CONTENT_TYPES, "application/x-www-form-urlencoded", ], ...(bodySizeLimit != null ? { maxRequestLength: bsl2mrl(bodySizeLimit) } : null), }, }; }; exports.makeV4Preset = makeV4Preset; /** * Unwraps domains, arrays and ranges to get the innermost codec. */ function unwrapCodec(codec) { if (codec.domainOfCodec) { return unwrapCodec(codec.domainOfCodec); } else if (codec.arrayOfCodec) { return unwrapCodec(codec.arrayOfCodec); } else if (codec.rangeOfCodec) { return unwrapCodec(codec.rangeOfCodec); } else { return codec; } } exports.V4Preset = (0, exports.makeV4Preset)(); exports.default = exports.V4Preset; //# sourceMappingURL=v4.js.map