postgraphile
Version:
Automatic, high performance, and highly customizable GraphQL API for PostgreSQL
357 lines • 16.1 kB
JavaScript
;
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