shyft
Version:
Model driven GraphQL API framework
413 lines (412 loc) • 24.6 kB
JavaScript
;
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;