UNPKG

jaydata-dynamic-metadata

Version:

OData v4 metadata to JayData context util

606 lines 29.2 kB
"use strict"; var annotations_1 = require("./annotations"); var dts_1 = require("./dts"); var containsField = function (obj, field, cb) { // if (field in (obj || {})) { // cb(obj[field]) // } if (obj && field in obj && typeof obj[field] !== "undefined") { cb(obj[field]); } }; var parsebool = function (b, d) { if ("boolean" === typeof b) { return b; } switch (b) { case "true": return true; case "false": return false; default: return d; } }; var _collectionRegex = /^Collection\((.*)\)$/; var dtsTypeMapping = { 'Edm.Boolean': 'boolean', 'Edm.Binary': 'Uint8Array', 'Edm.DateTime': 'Date', 'Edm.DateTimeOffset': 'Date', 'Edm.Time': 'string', 'Edm.Duration': 'string', 'Edm.TimeOfDay': 'string', 'Edm.Date': 'string', 'Edm.Decimal': 'string', 'Edm.Single': 'number', 'Edm.Float': 'number', 'Edm.Double': 'number', 'Edm.Guid': 'string', 'Edm.Int16': 'number', 'Edm.Int32': 'number', 'Edm.Int64': 'string', 'Edm.Byte': 'number', 'Edm.SByte': 'number', 'Edm.String': 'string', 'Edm.GeographyPoint': '$data.Geography', 'Edm.GeographyLineString': '$data.GeographyLineString', 'Edm.GeographyPolygon': '$data.GeographyPolygon', 'Edm.GeographyMultiPoint': '$data.GeographyMultiPoint', 'Edm.GeographyMultiPolygon': '$data.GeographyMultiPolygon', 'Edm.GeographyMultiLineString': '$data.GeographyMultiLineString', 'Edm.GeographyCollection': '$data.GeographyCollection', 'Edm.GeometryPoint': '$data.Geometry', 'Edm.GeometryLineString': '$data.GeometryLineString', 'Edm.GeometryPolygon': '$data.GeometryPolygon', 'Edm.GeometryMultiPoint': '$data.GeometryMultiPoint', 'Edm.GeometryMultiPolygon': '$data.GeometryMultiPolygon', 'Edm.GeometryMultiLineString': '$data.GeometryMultiLineString', 'Edm.GeometryCollection': '$data.GeometryCollection' }; var Metadata = (function () { function Metadata($data, options, metadata) { this.$data = $data; this.options = options || {}; this.metadata = metadata; this.options.container = this.$data.Container; //this.options.container || $data.createContainer() this.options.baseType = this.options.baseType || '$data.Entity'; this.options.entitySetType = this.options.entitySetType || '$data.EntitySet'; this.options.contextType = this.options.contextType || '$data.EntityContext'; this.options.collectionBaseType = this.options.collectionBaseType || 'Array'; this.annotationHandler = new annotations_1.Annotations(); this.storedTypes = {}; } Metadata.prototype._getMaxValue = function (maxValue) { if ("number" === typeof maxValue) return maxValue; if ("max" === maxValue) return Number.MAX_VALUE; return parseInt(maxValue); }; Metadata.prototype.createTypeDefinition = function (propertySchema, definition) { var _this = this; containsField(propertySchema, "type", function (v) { var match = _collectionRegex.exec(v); if (match) { definition.type = _this.options.collectionBaseType; definition.elementType = match[1]; } else { definition.type = v; } }); }; Metadata.prototype.createReturnTypeDefinition = function (propertySchema, definition) { containsField(propertySchema, "type", function (v) { var match = _collectionRegex.exec(v); if (match) { definition.returnType = '$data.Queryable'; definition.elementType = match[1]; } else { definition.returnType = v; } }); }; Metadata.prototype.createProperty = function (entityFullName, entitySchema, propertySchema) { var _this = this; var self = this; if (!propertySchema) { propertySchema = entitySchema; entitySchema = undefined; } var definition = {}; this.createTypeDefinition(propertySchema, definition); containsField(propertySchema, "nullable", function (v) { definition.nullable = parsebool(v, true), definition.required = parsebool(v, true) === false; }); containsField(propertySchema, "maxLength", function (v) { definition.maxLength = _this._getMaxValue(v); }); containsField(entitySchema, "key", function (keys) { if (keys.propertyRefs.some(function (pr) { return pr.name === propertySchema.name; })) { definition.key = true; } }); containsField(propertySchema, "annotations", function (v) { _this.annotationHandler.processEntityPropertyAnnotations(entityFullName, propertySchema.name, v); }); return { name: propertySchema.name, definition: definition }; }; Metadata.prototype.createNavigationProperty = function (entityFullName, entitySchema, propertySchema) { var _this = this; if (!propertySchema) { propertySchema = entitySchema; entitySchema = undefined; } var definition = {}; this.createTypeDefinition(propertySchema, definition); containsField(propertySchema, "nullable", function (v) { definition.nullable = parsebool(v, true), definition.required = parsebool(v, true) === false; }); containsField(propertySchema, "partner", function (p) { definition.inverseProperty = p; }); if (!definition.inverseProperty) { definition.inverseProperty = '$$unbound'; } containsField(propertySchema, "referentialConstraints", function (p) { if (p.length) { definition.keys = p.map(function (r) { return r.property; }); definition.foreignKeys = p.map(function (r) { return r.referencedProperty; }); } }); containsField(propertySchema, "annotations", function (v) { _this.annotationHandler.processEntityPropertyAnnotations(entityFullName, propertySchema.name, v); }); return { name: propertySchema.name, definition: definition }; }; Metadata.prototype.createEntityDefinition = function (entitySchema, entityFullName) { var props = (entitySchema.properties || []).map(this.createProperty.bind(this, entityFullName, entitySchema)); var navigationProps = (entitySchema.navigationProperties || []).map(this.createNavigationProperty.bind(this, entityFullName, entitySchema)); props = props.concat(navigationProps); var result = props.reduce(function (p, c) { p[c.name] = c.definition; return p; }, {}); return result; }; Metadata.prototype.createEntityType = function (entitySchema, namespace) { var _this = this; var baseType = (entitySchema.baseType ? entitySchema.baseType : this.options.baseType); var entityFullName = namespace + "." + entitySchema.name; var definition = this.createEntityDefinition(entitySchema, entityFullName); var staticDefinition = {}; containsField(entitySchema, "openType", function (v) { if (parsebool(v, false)) { staticDefinition.openType = { value: true }; } }); containsField(entitySchema, "annotations", function (v) { _this.annotationHandler.processEntityAnnotations(entityFullName, v); }); return { namespace: namespace, typeName: entityFullName, baseType: baseType, params: [entityFullName, this.options.container, definition, staticDefinition], definition: definition, type: 'entity' }; }; Metadata.prototype.createEnumOption = function (enumFullName, entitySchema, propertySchema, i) { var _this = this; if (!propertySchema) { propertySchema = entitySchema; entitySchema = undefined; } var definition = { name: propertySchema.name, index: i }; containsField(propertySchema, "value", function (value) { var v = +value; if (!isNaN(v)) { definition.value = v; } }); containsField(propertySchema, "annotations", function (v) { _this.annotationHandler.processEntityPropertyAnnotations(enumFullName, propertySchema.name, v, true); }); return definition; }; Metadata.prototype.createEnumDefinition = function (enumSchema, enumFullName) { var props = (enumSchema.members || []).map(this.createEnumOption.bind(this, enumFullName, enumSchema)); return props; }; Metadata.prototype.createEnumType = function (enumSchema, namespace) { var _this = this; var self = this; var enumFullName = namespace + "." + enumSchema.name; var definition = this.createEnumDefinition(enumSchema, enumFullName); containsField(enumSchema, "annotations", function (v) { _this.annotationHandler.processEntityAnnotations(enumFullName, v, true); }); return { namespace: namespace, typeName: enumFullName, baseType: '$data.Enum', params: [enumFullName, this.options.container, enumSchema.underlyingType, definition], definition: definition, type: 'enum' }; }; Metadata.prototype.createEntitySetProperty = function (entitySetSchema, contextSchema) { var _this = this; //var c = this.options.container var t = entitySetSchema.entityType; //c.classTypes[c.classNames[entitySetSchema.entityType]] // || entitySetSchema.entityType var prop = { name: entitySetSchema.name, definition: { type: this.options.entitySetType, elementType: t } }; containsField(entitySetSchema, "annotations", function (v) { _this.annotationHandler.processEntitySetAnnotations(t, v); }); return prop; }; Metadata.prototype.indexBy = function (fieldName, pick) { return [function (p, c) { p[c[fieldName]] = c[pick]; return p; }, {}]; }; Metadata.prototype.createContextDefinition = function (contextSchema, namespace) { var _this = this; var props = (contextSchema.entitySets || []).map(function (es) { return _this.createEntitySetProperty(es, contextSchema); }); var result = props.reduce.apply(props, this.indexBy("name", "definition")); return result; }; Metadata.prototype.createContextType = function (contextSchema, namespace) { if (Array.isArray(contextSchema)) { throw new Error("Array type is not supported here"); } var definition = this.createContextDefinition(contextSchema, namespace); var baseType = this.options.contextType; var typeName = namespace + "." + contextSchema.name; var contextImportMethods = []; contextSchema.actionImports && contextImportMethods.push.apply(contextImportMethods, contextSchema.actionImports); contextSchema.functionImports && contextImportMethods.push.apply(contextImportMethods, contextSchema.functionImports); return { namespace: namespace, typeName: typeName, baseType: baseType, params: [typeName, this.options.container, definition], definition: definition, type: 'context', contextImportMethods: contextImportMethods }; }; Metadata.prototype.createMethodParameter = function (parameter, definition) { var paramDef = { name: parameter.name }; this.createTypeDefinition(parameter, paramDef); definition.params.push(paramDef); }; Metadata.prototype.applyBoundMethod = function (actionInfo, ns, typeDefinitions, type) { var _this = this; var definition = { type: type, namespace: ns, returnType: null, params: [] }; containsField(actionInfo, "returnType", function (value) { _this.createReturnTypeDefinition(value, definition); }); var parameters = [].concat(actionInfo.parameters); parameters.forEach(function (p) { return _this.createMethodParameter(p, definition); }); if (parsebool(actionInfo.isBound, false)) { var bindingParameter_1 = definition.params.shift(); if (bindingParameter_1.type === this.options.collectionBaseType) { var filteredContextDefinitions = typeDefinitions.filter(function (d) { return d.namespace === ns && d.type === 'context'; }); filteredContextDefinitions.forEach(function (ctx) { for (var setName in ctx.definition) { var set = ctx.definition[setName]; if (set.elementType === bindingParameter_1.elementType) { set.actions = set.actions || {}; set.actions[actionInfo.name] = definition; } } }); } else { var filteredTypeDefinitions = typeDefinitions.filter(function (d) { return d.typeName === bindingParameter_1.type && d.type === 'entity'; }); filteredTypeDefinitions.forEach(function (t) { t.definition[actionInfo.name] = definition; }); } } else { delete definition.namespace; var methodFullName_1 = ns + '.' + actionInfo.name; var filteredContextDefinitions = typeDefinitions.filter(function (d) { return d.type === 'context'; }); filteredContextDefinitions.forEach(function (ctx) { ctx.contextImportMethods.forEach(function (methodImportInfo) { if (methodImportInfo.action === methodFullName_1 || methodImportInfo.function === methodFullName_1) { ctx.definition[actionInfo.name] = definition; } }); }); } }; Metadata.prototype.processMetadata = function (createdTypes) { var _this = this; var types = createdTypes || []; var typeDefinitions = []; var serviceMethods = []; containsField(this.metadata, "references", function (references) { references.forEach(function (ref) { containsField(ref, "includes", function (includes) { includes.forEach(function (include) { _this.annotationHandler.addInclude(include); }); }); }); }); var dtsModules = {}; types.dts = '/*//////////////////////////////////////////////////////////////////////////////////////\n' + '////// Autogenerated by JaySvcUtil http://JayData.org for more info /////////\n' + '////// OData V4 TypeScript /////////\n' + '//////////////////////////////////////////////////////////////////////////////////////*/\n\n'; types.dts += dts_1.JayData.src + '\n\n'; //types.dts += 'declare module Edm {\n' + Object.keys(dtsTypeMapping).map(t => ' type ' + t.split('.')[1] + ' = ' + dtsTypeMapping[t] + ';').join('\n') + '\n}\n\n'; var self = this; this.metadata.dataServices.schemas.forEach(function (schema) { var ns = schema.namespace; dtsModules[ns] = ['declare module ' + ns + ' {', '}']; if (schema.enumTypes) { var enumTypes = schema.enumTypes.map(function (ct) { return _this.createEnumType(ct, ns); }); typeDefinitions.push.apply(typeDefinitions, enumTypes); } if (schema.complexTypes) { var complexTypes = schema.complexTypes.map(function (ct) { return _this.createEntityType(ct, ns); }); typeDefinitions.push.apply(typeDefinitions, complexTypes); } if (schema.entityTypes) { var entityTypes = schema.entityTypes.map(function (et) { return _this.createEntityType(et, ns); }); typeDefinitions.push.apply(typeDefinitions, entityTypes); } if (schema.actions) { serviceMethods.push.apply(serviceMethods, schema.actions.map(function (m) { return function (defs) { return _this.applyBoundMethod(m, ns, defs, '$data.ServiceAction'); }; })); } if (schema.functions) { serviceMethods.push.apply(serviceMethods, schema.functions.map(function (m) { return function (defs) { return _this.applyBoundMethod(m, ns, defs, '$data.ServiceFunction'); }; })); } if (schema.entityContainer) { var contexts = schema.entityContainer.map(function (ctx) { return _this.createContextType(ctx, self.options.namespace || ns); }); typeDefinitions.push.apply(typeDefinitions, contexts); } //console.log('annotations', schema) containsField(schema, 'annotations', function (annotations) { annotations.forEach(function (annot) { containsField(annot, "target", function (target) { containsField(annot, "annotations", function (v) { _this.annotationHandler.processSchemaAnnotations(target, v, annot.qualifier); }); }); }); }); }); serviceMethods.forEach(function (m) { return m(typeDefinitions); }); var contextFullName; types.src = '(function(mod) {\n' + ' if (typeof exports == "object" && typeof module == "object") return mod(exports, require("jaydata/core")); // CommonJS\n' + ' if (typeof define == "function" && define.amd) return define(["exports", "jaydata/core"], mod); // AMD\n' + ' mod($data.generatedContext || ($data.generatedContext = {}), $data); // Plain browser env\n' + '})(function(exports, $data) {\n\n' + 'exports.$data = $data;\n\n' + 'var types = {};\n\n'; typeDefinitions = this.orderTypeDefinitions(typeDefinitions); types.push.apply(types, typeDefinitions.map(function (d) { _this.annotationHandler.preProcessAnnotation(d); _this.storeExportable(d.params[0]); var dtsm = dtsModules[d.namespace]; if (!dtsm) { dtsm = dtsModules[d.namespace] = ['declare module ' + d.namespace + ' {', '}']; } var dtsPart = []; var srcPart = ''; if (d.baseType == '$data.Enum') { dtsPart.push(' export enum ' + d.typeName.split('.').pop() + ' {'); if (d.params[3] && Object.keys(d.params[3]).length > 0) { Object.keys(d.params[3]).forEach(function (dp) { return dtsPart.push(' ' + d.params[3][dp].name + ','); }); } srcPart += 'types["' + d.params[0] + '"] = $data.createEnum("' + d.params[0] + '", [\n' + Object.keys(d.params[3]).map(function (dp) { return ' ' + _this._createPropertyDefString(d.params[3][dp]); }).join(',\n') + '\n]);\n\n'; } else { dtsPart.push(' export class ' + d.typeName.split('.').pop() + ' extends ' + d.baseType + ' {'); if (d.baseType == self.options.contextType) { dtsPart.push(' onReady(): Promise<' + d.typeName.split('.').pop() + '>;'); dtsPart.push(''); } else { dtsPart.push(' constructor();'); var ctr = ' constructor(initData: { '; if (d.params[2] && Object.keys(d.params[2]).length > 0) { ctr += Object.keys(d.params[2]).map(function (dp) { return dp + '?: ' + (d.params[2][dp].type == 'Array' ? d.params[2][dp].elementType + '[]' : d.params[2][dp].type); }).join('; '); } ctr += ' });'; dtsPart.push(ctr); dtsPart.push(''); } var typeName = d.baseType; if (d.baseType == _this.options.contextType) { srcPart += 'exports.type = '; contextFullName = d.typeName; } srcPart += 'types["' + d.params[0] + '"] = ' + (typeName == _this.options.baseType || typeName == _this.options.contextType ? ('$data("' + typeName + '")') : 'types["' + typeName + '"]') + '.extend("' + d.params[0] + '", '; if (d.params[2] && Object.keys(d.params[2]).length > 0) { srcPart += '{\n' + Object.keys(d.params[2]).map(function (dp) { return ' ' + dp + ': ' + _this._createPropertyDefString(d.params[2][dp]); }).join(',\n') + '\n}'; if (d.baseType == _this.options.contextType) { Object.keys(d.params[2]).forEach(function (dp) { return dtsPart.push(' ' + dp + ': ' + _this._typeToTS(d.params[2][dp].type, d.params[2][dp].elementType, d.params[2][dp]) + ';'); }); } else { Object.keys(d.params[2]).forEach(function (dp) { return dtsPart.push(' ' + dp + ': ' + _this._typeToTS(d.params[2][dp].type, d.params[2][dp].elementType, d.params[2][dp]) + ';'); }); } } else srcPart += 'null'; if (d.params[3] && Object.keys(d.params[3]).length > 0) { srcPart += ', {\n' + Object.keys(d.params[3]).map(function (dp) { return ' ' + dp + ': ' + _this._createPropertyDefString(d.params[3][dp]); }).join(',\n') + '\n}'; } srcPart += ');\n\n'; } types.src += srcPart; dtsPart.push(' }'); dtsm.splice(1, 0, dtsPart.join('\n')); if (_this.options.debug) console.log('Type generated:', d.params[0]); if (_this.options.generateTypes !== false) { var baseType = _this.options.container.resolveType(d.baseType); var type = baseType.extend.apply(baseType, d.params); _this.annotationHandler.addAnnotation(type); return type; } })); this.addExportables(types); types.src += 'var ctxType = exports.type;\n' + 'exports.factory = function(config){\n' + ' if (ctxType){\n' + ' var cfg = $data.typeSystem.extend({\n' + ' name: "oData",\n' + ' oDataServiceHost: "' + (this.options.url && this.options.url.replace('/$metadata', '') || '') + '",\n' + ' withCredentials: ' + (this.options.withCredentials || false) + ',\n' + ' maxDataServiceVersion: "' + (this.options.maxDataServiceVersion || '4.0') + '"\n' + ' }, config);\n' + ' return new ctxType(cfg);\n' + ' }else{\n' + ' return null;\n' + ' }\n' + '};\n\n'; if (this.options.autoCreateContext) { var contextName = typeof this.options.autoCreateContext == 'string' ? this.options.autoCreateContext : 'context'; types.src += 'exports["' + contextName + '"] = exports.factory();\n\n'; } types.src += this.annotationHandler.annotationsText(); types.src += '});'; // declare modules types.dts += Object.keys(dtsModules) .filter(function (m) { return dtsModules[m] && dtsModules[m].length > 2; }) .map(function (m) { return dtsModules[m].join('\n\n'); }) .join('\n\n'); // export modules types.dts += Object.keys(dtsModules) .filter(function (m) { return dtsModules[m] && dtsModules[m].length > 2; }) .map(function (m) { return m.split(".")[0]; }) .filter(function (v, i, a) { return a.indexOf(v) === i; }) // distinct .map(function (m) { return '\n\nexport {' + m + ' as ' + m + '}'; }) .join(''); if (contextFullName) { var mod = ['\n\nexport var type: typeof ' + contextFullName + ';', 'export var factory: (config:any) => ' + contextFullName + ';']; if (this.options.autoCreateContext) { var contextName = typeof this.options.autoCreateContext == 'string' ? this.options.autoCreateContext : 'context'; mod.push('export var ' + contextName + ': ' + contextFullName + ';'); } types.dts += mod.join('\n'); } if (this.options.generateTypes === false) { types.length = 0; } return types; }; Metadata.prototype._createPropertyDefString = function (definition) { if (definition.concurrencyMode) { return JSON.stringify(definition).replace('"concurrencyMode":"fixed"}', '"concurrencyMode":$data.ConcurrencyMode.Fixed}'); } else { return JSON.stringify(definition); } }; Metadata.prototype._typeToTS = function (type, elementType, definition) { var _this = this; if (type == this.options.entitySetType) { return '$data.EntitySet<typeof ' + elementType + ', ' + elementType + '>'; } else if (type == '$data.Queryable') { return '$data.Queryable<' + elementType + '>'; } else if (type == this.options.collectionBaseType) { return elementType + '[]'; } else if (type == '$data.ServiceAction') { return '{ (' + (definition.params.length > 0 ? definition.params.map(function (p) { return p.name + ': ' + _this._typeToTS(p.type, p.elementType, p); }).join(', ') : '') + '): Promise<void>; }'; } else if (type == '$data.ServiceFunction') { var t = this._typeToTS(definition.returnType, definition.elementType, definition); if (t.indexOf('$data.Queryable') < 0) t = 'Promise<' + t + '>'; return '{ (' + (definition.params.length > 0 ? definition.params.map(function (p) { return p.name + ': ' + _this._typeToTS(p.type, p.elementType, p); }).join(', ') : '') + '): ' + t + '; }'; } else return type; }; Metadata.prototype.orderTypeDefinitions = function (typeDefinitions) { var contextTypes = typeDefinitions.filter(function (t) { return t.type === 'context'; }); var ordered = []; var dependants = [].concat(typeDefinitions.filter(function (t) { return t.type !== 'context'; })); var addedTypes; var baseType = this.options.baseType; var dependantCount = Number.MAX_VALUE; while (dependants.length) { var dependantItems = [].concat(dependants); dependants.length = 0; dependantItems.forEach(function (typeDef) { if (dependantCount === dependantItems.length || typeDef.type !== "entity" || typeDef.baseType === baseType || ordered.some(function (t) { return t.typeName === typeDef.baseType; })) { ordered.push(typeDef); } else { dependants.push(typeDef); } }); dependantCount = dependantItems.length; } return ordered.concat(contextTypes); }; Metadata.prototype.storeExportable = function (typesStr) { var typesArr = typesStr.split("."); var container = this.storedTypes; typesArr.forEach(function (current) { if (typesArr[typesArr.length - 1] === current) { container[current] = "@@" + typesStr + "@@"; } else { if (!container[current]) { container[current] = {}; } container = container[current]; } }); }; Metadata.prototype.addExportables = function (meta) { for (var key in this.storedTypes) { var types = "exports." + key + " = " + JSON.stringify(this.storedTypes[key], null, 2) .replace(/"@@/g, "types[\"") .replace(/@@"/g, "\"]") + ";\n\n"; meta.src += types; } }; return Metadata; }()); exports.Metadata = Metadata; //# sourceMappingURL=metadata.js.map