UNPKG

shyft

Version:

Model driven GraphQL API framework

413 lines (412 loc) 24.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; 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 __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.transformFilterLevel = exports.splitAttributeAndFilterOperator = exports.generateFilterInput = void 0; var graphql_1 = require("graphql"); var _ = __importStar(require("lodash")); var ProtocolGraphQL_1 = require("./ProtocolGraphQL"); var Entity_1 = require("../engine/entity/Entity"); var constants_1 = require("../engine/constants"); var ComplexDataType_1 = require("../engine/datatype/ComplexDataType"); var util_1 = require("../engine/util"); var ViewEntity_1 = require("../engine/entity/ViewEntity"); var ShadowEntity_1 = require("../engine/entity/ShadowEntity"); var dataTypes_1 = require("../engine/datatype/dataTypes"); var registry_1 = require("./registry"); var io_1 = require("./io"); var AND_OPERATOR = 'AND'; var OR_OPERATOR = 'OR'; var logicalKeysMap = (_a = {}, _a[AND_OPERATOR] = '$and', _a[OR_OPERATOR] = '$or', _a); var DEEP_FILTER_OPERATOR = 'filter'; var PRE_FILTER_OPERATOR = 'pre_filter'; var generateFilterInput = function (entity, graphRegistry) { var protocolConfiguration = ProtocolGraphQL_1.ProtocolGraphQL.getProtocolConfiguration(); var typeNamePluralListName = registry_1.getRegisteredEntity(entity.name).typeNamePluralPascalCase; var storageType = entity.storageType; var filterInputTypeName = protocolConfiguration.generateFilterInputTypeName(entity); var preFilters = entity.getPreFilters(); var entityFilterType = new graphql_1.GraphQLInputObjectType({ name: filterInputTypeName, description: "Filter **`" + typeNamePluralListName + "`** by various criteria", fields: function () { var _a; var fields = (_a = {}, _a[AND_OPERATOR] = { description: "Combine **`" + filterInputTypeName + "`** by a logical **AND**", type: new graphql_1.GraphQLList(new graphql_1.GraphQLNonNull(entityFilterType)), }, _a[OR_OPERATOR] = { description: "Combine **`" + filterInputTypeName + "`** by a logical **OR**", type: new graphql_1.GraphQLList(new graphql_1.GraphQLNonNull(entityFilterType)), }, _a); _.forEach(entity.getAttributes(), function (attribute) { var attributeType = attribute.type; var isPrimary = attribute.primary; if (attribute.hidden) { return; } if (attribute.mutationInput) { return; } var gqlFieldName = registry_1.getRegisteredEntityAttribute(entity.name, attribute.name).fieldName; // it's a reference if (Entity_1.isEntity(attributeType)) { var targetEntity = attributeType; var targetTypeName = registry_1.getRegisteredEntity(targetEntity.name).typeName; var targetRegistryType = graphRegistry.types[targetTypeName]; var targetConnectionArgs = targetRegistryType.connectionArgs; var fieldName = gqlFieldName + "__" + DEEP_FILTER_OPERATOR; fields[fieldName] = targetConnectionArgs.filter; var primaryAttribute = targetEntity.getPrimaryAttribute(); attributeType = primaryAttribute.type; } else if (ShadowEntity_1.isShadowEntity(attributeType)) { var targetEntity = attributeType; var primaryAttribute = targetEntity.getPrimaryAttribute(); attributeType = primaryAttribute.type; } var fieldType = ProtocolGraphQL_1.ProtocolGraphQL.convertToProtocolDataType(attributeType, entity.name, true); var storageDataType = storageType.convertToStorageDataType(attributeType); if (!ComplexDataType_1.isComplexDataType(attributeType)) { fields[gqlFieldName] = { type: fieldType, }; } storageDataType.capabilities.map(function (capability) { var fieldName = gqlFieldName + "__" + capability; var field = {}; if (constants_1.storageDataTypeCapabilities[capability] === constants_1.storageDataTypeCapabilityType.STRING) { // object types and list types can only accept a string as filter field.type = ProtocolGraphQL_1.ProtocolGraphQL.convertToProtocolDataType(dataTypes_1.DataTypeString, entity.name, true); } else if (constants_1.storageDataTypeCapabilities[capability] === constants_1.storageDataTypeCapabilityType.VALUE) { field.type = fieldType; } else if (constants_1.storageDataTypeCapabilities[capability] === constants_1.storageDataTypeCapabilityType.LIST) { field.type = new graphql_1.GraphQLList(new graphql_1.GraphQLNonNull(fieldType)); } else if (constants_1.storageDataTypeCapabilities[capability] === constants_1.storageDataTypeCapabilityType.BOOLEAN) { field.type = ProtocolGraphQL_1.ProtocolGraphQL.convertToProtocolDataType(dataTypes_1.DataTypeBoolean, entity.name, true); } fields[fieldName] = field; }); if (isPrimary && preFilters) { var preFilterInputTypeName = protocolConfiguration.generateFilterPreFilterInputTypeName(entity); var fieldName = gqlFieldName + "__" + PRE_FILTER_OPERATOR; var preFilterFieldType = new graphql_1.GraphQLInputObjectType({ name: preFilterInputTypeName, description: "Filter **`" + typeNamePluralListName + "`** by a custom pre-filter", fields: function () { var preFilterFields = {}; Object.keys(preFilters).map(function (preFilterName) { var preFilter = preFilters[preFilterName]; if (preFilter.input) { var preFilterParamsInputTypeName = protocolConfiguration.generateFilterPreFilterParamsInputTypeName(entity, preFilterName); var preFilterAttributesType = void 0; if (util_1.isFunction(preFilter.input)) { var preFilterAttributesTypeFn = preFilter.input; var generatedType = preFilterAttributesTypeFn({ setup: { name: preFilterParamsInputTypeName, description: preFilterParamsInputTypeName, }, }); preFilterAttributesType = generatedType.getAttributes(); } else { preFilterAttributesType = preFilter.input; } var preFilterInputType = io_1.generateDataInput(preFilterParamsInputTypeName, preFilterAttributesType, false); preFilterFields[preFilterName] = { type: preFilterInputType, }; } else { preFilterFields[preFilterName] = { type: graphql_1.GraphQLBoolean, }; } }); return preFilterFields; }, }); fields[fieldName] = { type: preFilterFieldType, description: "Filter **`" + typeNamePluralListName + "`** by a custom pre-filter", }; } }); return fields; }, }); return { type: entityFilterType, description: 'Filter list by various criteria', }; }; exports.generateFilterInput = generateFilterInput; var splitAttributeAndFilterOperator = function (str) { var ret; if (typeof str === 'string') { var splitPos = str.lastIndexOf('__'); if (splitPos > 0) { var operator = str.substr(splitPos + 2); var attributeName = str.substring(0, splitPos); if (operator.length > 0 && attributeName.length > 0) { ret = { operator: operator, attributeName: attributeName, }; } } else { ret = { attributeName: str, }; } } if (!ret) { throw new Error("invalid filter '" + str + "'"); } return ret; }; exports.splitAttributeAndFilterOperator = splitAttributeAndFilterOperator; var deepFilterResolver = function (entity, filter, context, path) { return __awaiter(void 0, void 0, void 0, function () { var storageType, transformedFilter, data, ids; return __generator(this, function (_a) { switch (_a.label) { case 0: storageType = entity.storageType; return [4 /*yield*/, exports.transformFilterLevel(entity, filter, entity.getAttributes(), context, path)]; case 1: transformedFilter = _a.sent(); return [4 /*yield*/, storageType.find(entity, { all: true, skipPermissions: true, filter: transformedFilter, }, context)]; case 2: data = (_a.sent()).data; ids = data.map(function (_a) { var id = _a.id; return id; }); if (ids.length < 1) { return [2 /*return*/, null]; } return [2 /*return*/, ids]; } }); }); }; var transformFilterLevel = function (entity, filters, attributes, context, path) { if (filters === void 0) { filters = {}; } return __awaiter(void 0, void 0, void 0, function () { var ret, hasFilter, errorLocation, filterKeys; return __generator(this, function (_a) { switch (_a.label) { case 0: ret = {}; hasFilter = {}; if (path && !util_1.isArray(path, true)) { throw new Error('optional path in transformFilterLevel() needs to be an array'); } errorLocation = path ? " at '" + path.join('.') + "'" : ''; if (!Entity_1.isEntity(entity) && !ViewEntity_1.isViewEntity(entity)) { throw new Error('transformFilterLevel() expects an entity or view entity'); } if (!util_1.isMap(filters)) { throw new Error("filter" + errorLocation + " needs to be an object of filter criteria"); } if (!util_1.isMap(attributes, true)) { throw new Error('transformFilterLevel() expects an attribute map'); } filterKeys = Object.keys(filters); return [4 /*yield*/, Promise.all(filterKeys.map(function (filter) { return __awaiter(void 0, void 0, void 0, function () { var value, logicalKey, newPath_1, _a, _b, _c, operator, attributeName, attribute, attributesNames, realAttributeName, newPath, targetEntity, resolvedList, preFilters, usedPreFilters, usedPreFilter, preFilter, preFilterFn, preFilterAttributes, resolvedList, operatorKey; return __generator(this, function (_d) { switch (_d.label) { case 0: value = filters[filter]; if (!(filter === AND_OPERATOR || filter === OR_OPERATOR)) return [3 /*break*/, 2]; logicalKey = logicalKeysMap[filter]; newPath_1 = path ? path.slice() : []; _a = ret; _b = logicalKey; return [4 /*yield*/, Promise.all(value.map(function (newFilter, idx) { return __awaiter(void 0, void 0, void 0, function () { var idxPath; return __generator(this, function (_a) { switch (_a.label) { case 0: idxPath = newPath_1.slice(); idxPath.push(filter + "[" + idx + "]"); return [4 /*yield*/, exports.transformFilterLevel(entity, newFilter, attributes, context, idxPath)]; case 1: return [2 /*return*/, _a.sent()]; } }); }); }))]; case 1: _a[_b] = _d.sent(); return [2 /*return*/]; case 2: _c = exports.splitAttributeAndFilterOperator(filter), operator = _c.operator, attributeName = _c.attributeName; attributesNames = Object.keys(attributes); attributesNames.map(function (name) { var gqlFieldName = registry_1.getRegisteredEntityAttribute(entity.name, attributes[name].name).fieldName; if (attributeName === gqlFieldName) { attribute = attributes[name]; } }); if (!attribute) { throw new Error("Unknown attribute name '" + attributeName + "' used in filter" + errorLocation); } hasFilter[attributeName] = hasFilter[attributeName] || {}; if (operator) { if (operator === DEEP_FILTER_OPERATOR) { hasFilter[attributeName].hasDeepFilter = true; } else { hasFilter[attributeName].hasComplexFilter = true; } } else { hasFilter[attributeName].hasExactMatchFilter = true; } realAttributeName = attribute.name; if (hasFilter[attributeName].hasDeepFilter && (hasFilter[attributeName].hasComplexFilter || hasFilter[attributeName].hasExactMatchFilter)) { throw new Error("Cannot combine 'filter' operator with other operators on attribute '" + attributeName + "' used in filter" + errorLocation); } if (hasFilter[attributeName].hasExactMatchFilter && (hasFilter[attributeName].hasComplexFilter || hasFilter[attributeName].hasDeepFilter)) { throw new Error("Cannot combine 'exact match' operator with other operators on attribute '" + attributeName + "' used in filter" + errorLocation); } if (!operator) return [3 /*break*/, 10]; ret[realAttributeName] = ret[realAttributeName] || {}; if (!(operator === DEEP_FILTER_OPERATOR)) return [3 /*break*/, 4]; newPath = path ? path.slice() : []; newPath.push(filter); targetEntity = attributes[attributeName].type; return [4 /*yield*/, deepFilterResolver(targetEntity, value, context, newPath)]; case 3: resolvedList = _d.sent(); if (resolvedList) { ret[realAttributeName].$in = resolvedList; } else { ret[realAttributeName].$noResult = true; } return [3 /*break*/, 9]; case 4: if (!(operator === PRE_FILTER_OPERATOR)) return [3 /*break*/, 8]; preFilters = entity.getPreFilters(); usedPreFilters = Object.keys(value).filter(function (preFilterName) { var preFilterValue = value[preFilterName]; return util_1.isMap(preFilterValue) || preFilterValue === true; }); if (!(usedPreFilters.length > 1)) return [3 /*break*/, 5]; throw new Error('Multiple preFilters cannot be combined'); case 5: if (!(usedPreFilters.length === 1)) return [3 /*break*/, 7]; usedPreFilter = usedPreFilters[0]; preFilter = preFilters[usedPreFilter]; if (!preFilter) { throw new Error("No preFilter named '" + usedPreFilter + "' found"); } preFilterFn = preFilter.resolve; preFilterAttributes = value[usedPreFilter]; return [4 /*yield*/, preFilterFn(context, preFilterAttributes)]; case 6: resolvedList = _d.sent(); if (resolvedList) { ret[realAttributeName].$in = resolvedList; } else { ret[realAttributeName].$noResult = true; } _d.label = 7; case 7: return [3 /*break*/, 9]; case 8: operatorKey = "$" + operator; ret[realAttributeName][operatorKey] = value; _d.label = 9; case 9: return [3 /*break*/, 11]; case 10: ret[realAttributeName] = value; _d.label = 11; case 11: return [2 /*return*/]; } }); }); }))]; case 1: _a.sent(); return [2 /*return*/, ret]; } }); }); }; exports.transformFilterLevel = transformFilterLevel;