UNPKG

graphql-compose-connection

Version:

Plugin for `graphql-compose` which provide a connection resolver for types.

310 lines 13.7 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.findSortConfig = exports.emptyConnection = exports.prepareLimitSkipFallback = exports.prepareRawQuery = exports.preparePageInfo = exports.prepareConnectionResolver = void 0; const graphql_compose_1 = require("graphql-compose"); const connectionType_1 = require("./types/connectionType"); const sortInputType_1 = require("./types/sortInputType"); const cursor_1 = require("./cursor"); function prepareConnectionResolver(tc, opts) { if (!(tc instanceof graphql_compose_1.ObjectTypeComposer)) { throw new Error(`First arg for prepareConnectionResolver() should be instance of ObjectTypeComposer but received: ${graphql_compose_1.inspect(tc)}`); } if (!opts.countResolver || !(opts.countResolver instanceof graphql_compose_1.Resolver)) { throw new Error(`Option 'opts.countResolver' must be a Resolver instance. Received ${graphql_compose_1.inspect(opts.countResolver)}`); } const countResolver = opts.countResolver; const countResolve = countResolver.getResolve(); if (!opts.findManyResolver || !(opts.findManyResolver instanceof graphql_compose_1.Resolver)) { throw new Error(`Option 'opts.findManyResolver' must be a Resolver instance. Received ${graphql_compose_1.inspect(opts.findManyResolver)}`); } const findManyResolver = opts.findManyResolver; const findManyResolve = findManyResolver.getResolve(); const additionalArgs = {}; if (findManyResolver.hasArg('filter')) { const filter = findManyResolver.getArg('filter'); if (filter) { additionalArgs.filter = filter; } } const sortEnumType = sortInputType_1.prepareSortType(tc, opts); const firstField = sortEnumType.getFieldNames()[0]; const defaultValue = firstField && sortEnumType.getField(firstField).value; const resolverName = opts.name || 'connection'; return tc.schemaComposer.createResolver({ type: connectionType_1.prepareConnectionType(tc, resolverName, opts.edgeTypeName, opts.edgeFields), name: resolverName, kind: 'query', args: Object.assign(Object.assign({ first: { type: 'Int', description: 'Forward pagination argument for returning at most first edges', }, after: { type: 'String', description: 'Forward pagination argument for returning at most first edges', }, last: { type: 'Int', description: 'Backward pagination argument for returning at most last edges', }, before: { type: 'String', description: 'Backward pagination argument for returning at most last edges', } }, additionalArgs), { sort: { type: sortEnumType, defaultValue, description: 'Sort argument for data ordering', } }), resolve(resolveParams) { var _a; return __awaiter(this, void 0, void 0, function* () { let countPromise; let findManyPromise; const { projection = {}, args, rawQuery } = resolveParams; const findManyParams = Object.assign({}, resolveParams); let first = parseInt(args.first, 10) || 0; if (first < 0) { throw new Error('Argument `first` should be non-negative number.'); } const last = parseInt(args.last, 10) || 0; if (last < 0) { throw new Error('Argument `last` should be non-negative number.'); } const countParams = Object.assign(Object.assign({}, resolveParams), { rawQuery, args: { filter: Object.assign({}, resolveParams.args.filter), } }); if (projection.count) { countPromise = countResolve(countParams); } else if (!first && last) { countPromise = countResolve(countParams); } else { countPromise = Promise.resolve(0); } if (projection === null || projection === void 0 ? void 0 : projection.edges) { const { edges } = projection, projectionWithoutEdges = __rest(projection, ["edges"]); const extraProjection = {}; if (opts.edgeFields) { Object.keys(opts.edgeFields).forEach((extraKey) => { if (projection.edges[extraKey]) { extraProjection[extraKey] = projection.edges[extraKey]; } }); } findManyParams.projection = Object.assign(Object.assign(Object.assign({}, projectionWithoutEdges), (_a = projection === null || projection === void 0 ? void 0 : projection.edges) === null || _a === void 0 ? void 0 : _a.node), extraProjection); } else { findManyParams.projection = Object.assign({}, projection); } const sortConfig = findSortConfig(opts.sort, args.sort); if (sortConfig) { prepareRawQuery(resolveParams, sortConfig); } if (!first && last) { const filteredCountParams = Object.assign(Object.assign({}, resolveParams), { args: { filter: Object.assign({}, resolveParams.args.filter), } }); first = yield countResolve(filteredCountParams); first = parseInt(first, 10) || 0; } let limit = last || first || opts.defaultLimit || 20; let skip = last > 0 ? first - last : 0; let prepareCursorData; if (sortConfig) { findManyParams.rawQuery = resolveParams.rawQuery; sortConfig.cursorFields.forEach((fieldName) => { findManyParams.projection[fieldName] = true; }); prepareCursorData = (record) => { const result = {}; sortConfig.cursorFields.forEach((fieldName) => { result[fieldName] = record[fieldName]; }); return result; }; } else { [limit, skip] = prepareLimitSkipFallback(resolveParams, limit, skip); let skipIdx = -1; prepareCursorData = () => { skipIdx += 1; return skip + skipIdx; }; } findManyParams.args.limit = limit + 1; if (skip > 0) { findManyParams.args.skip = skip; } resolveParams.findManyResolveParams = findManyParams; resolveParams.countResolveParams = countParams; if (projection.count && Object.keys(projection).length === 1) { findManyPromise = Promise.resolve([]); } else { findManyPromise = findManyResolve(findManyParams); } return Promise.all([findManyPromise, countPromise]) .then(([recordList, count]) => { const edges = recordList.map((record) => { const edge = { cursor: cursor_1.dataToCursor(prepareCursorData(record)), node: opts.edgeFields ? record.node : record, }; if (opts.edgeFields) { Object.keys(opts.edgeFields).forEach((field) => { edge[field] = record[field]; }); } return edge; }); return [edges, count]; }) .then(([edges, count]) => { const result = emptyConnection(); result.edges = edges.length > limit ? edges.slice(0, limit) : edges; result.pageInfo = preparePageInfo(edges, args, limit, skip); result.count = count; return result; }); }); }, }); } exports.prepareConnectionResolver = prepareConnectionResolver; function preparePageInfo(edges, args, limit, skip) { const pageInfo = { startCursor: '', endCursor: '', hasPreviousPage: false, hasNextPage: false, }; const hasExtraRecords = edges.length > limit; if (edges.length > 0 && limit > 0) { pageInfo.startCursor = edges[0].cursor; if (hasExtraRecords) { pageInfo.endCursor = edges[limit - 1].cursor; } else { pageInfo.endCursor = edges[edges.length - 1].cursor; } pageInfo.hasPreviousPage = skip > 0 || !!args.after; pageInfo.hasNextPage = hasExtraRecords || !!args.before; } return pageInfo; } exports.preparePageInfo = preparePageInfo; function prepareRawQuery(rp, sortConfig) { var _a, _b; if (!rp.rawQuery) { rp.rawQuery = {}; } const beginCursorData = cursor_1.cursorToData((_a = rp === null || rp === void 0 ? void 0 : rp.args) === null || _a === void 0 ? void 0 : _a.after); if (beginCursorData) { const r = sortConfig.afterCursorQuery(rp.rawQuery, beginCursorData, rp); if (r !== undefined) { rp.rawQuery = r; } } const endCursorData = cursor_1.cursorToData((_b = rp === null || rp === void 0 ? void 0 : rp.args) === null || _b === void 0 ? void 0 : _b.before); if (endCursorData) { const r = sortConfig.beforeCursorQuery(rp.rawQuery, endCursorData, rp); if (r !== undefined) { rp.rawQuery = r; } } } exports.prepareRawQuery = prepareRawQuery; function prepareLimitSkipFallback(rp, limit, skip) { var _a, _b; let newLimit = limit; let newSkip = skip; let beforeSkip = 0; let afterSkip = 0; if ((_a = rp === null || rp === void 0 ? void 0 : rp.args) === null || _a === void 0 ? void 0 : _a.before) { const tmp = cursor_1.cursorToData(rp.args.before); if (Number.isInteger(tmp)) { beforeSkip = parseInt(tmp, 10); } } if ((_b = rp === null || rp === void 0 ? void 0 : rp.args) === null || _b === void 0 ? void 0 : _b.after) { const tmp = cursor_1.cursorToData(rp.args.after); if (Number.isInteger(tmp)) { afterSkip = parseInt(tmp, 10) + 1; } } if (beforeSkip && afterSkip) { const rangeLimit = beforeSkip - afterSkip; if (rangeLimit < 0) { newLimit = 0; newSkip = skip + afterSkip; } else if (rangeLimit < limit) { newLimit = rangeLimit; newSkip = skip + beforeSkip - rangeLimit; } else { newSkip = skip + afterSkip; } } else if (beforeSkip) { newSkip = beforeSkip - limit; if (newSkip < 0) { newSkip = 0; newLimit = limit; if (newLimit > beforeSkip) { newLimit = beforeSkip; } } } else if (afterSkip) { newSkip = afterSkip; } return [newLimit, newSkip]; } exports.prepareLimitSkipFallback = prepareLimitSkipFallback; function emptyConnection() { return { count: 0, edges: [], pageInfo: { startCursor: '', endCursor: '', hasPreviousPage: false, hasNextPage: false, }, }; } exports.emptyConnection = emptyConnection; function findSortConfig(configs, val) { for (const k in configs) { if (configs[k].value === val) { return configs[k]; } } const valStringified = JSON.stringify(val); for (const k in configs) { if (JSON.stringify(configs[k].value) === valStringified) { return configs[k]; } } return undefined; } exports.findSortConfig = findSortConfig; //# sourceMappingURL=connection.js.map