UNPKG

@themost/web

Version:

MOST Web Framework 2.0 - Web Server Module

1,122 lines (1,088 loc) 56 kB
// noinspection ES6ConvertVarToLetConst /** * @license * MOST Web Framework 2.0 Codename Blueshift * Copyright (c) 2017, THEMOST LP All rights reserved * * Use of this source code is governed by an BSD-3-Clause license that can be * found in the LICENSE file at https://themost.io/license */ // var LangUtils = require('@themost/common/utils').LangUtils; var DataQueryable = require('@themost/data/data-queryable').DataQueryable; var HttpNotFoundError = require('@themost/common/errors').HttpNotFoundError; var HttpForbiddenError = require('@themost/common/errors').HttpForbiddenError; var HttpMethodNotAllowedError = require('@themost/common/errors').HttpMethodNotAllowedError; var HttpBadRequestError = require('@themost/common/errors').HttpBadRequestError; var parseBoolean = require('@themost/common/utils').LangUtils.parseBoolean; var pluralize = require('pluralize'); var _ = require('lodash'); var httpRoute = require('../decorators').httpRoute; var httpGet = require('../decorators').httpGet; var httpPost = require('../decorators').httpPost; var httpPut = require('../decorators').httpPut; var httpPatch = require('../decorators').httpPatch; var httpDelete = require('../decorators').httpDelete; var httpAction = require('../decorators').httpAction; var httpController = require('../decorators').httpController; var defineDecorator = require('../decorators').defineDecorator; var HttpBaseController = require('./base'); var ODataModelBuilder = require('@themost/data/odata').ODataModelBuilder; var EdmMapping = require('@themost/data/odata').EdmMapping; var EdmType = require('@themost/data/odata').EdmType; var DefaultTopQueryOption = 50; var Args = require('@themost/common').Args; /** * @type {Array<HttpRouteConfiguration>} */ var SERVICE_DEFAULT_ROUTES = require('../resources/service-controller-routes'); /** * @this HttpServiceController * @param {HttpContext} context * @param {*} value * @param {string} type * @private */ function mapPrimitiveInstance(context, value, type) { if (context) { var contextLink = this.getBuilder().getContextLink(context); if (contextLink) { return { "@odata.context": contextLink + '#' + type, "value": value }; } } return { "value": value }; } /** * @classdesc HttpBaseController class describes a base controller. * @class * @param {HttpContext} context * @constructor */ function HttpServiceController(context) { HttpServiceController.super_.bind(this)(context); } LangUtils.inherits(HttpServiceController, HttpBaseController); defineDecorator(HttpServiceController, 'constructor', httpController('api')); HttpServiceController.prototype.getMetadata = function() { var self = this; return this.getBuilder().getEdmDocument().then(function (result) { return Promise.resolve(self.xml(result.outerXML())); }); }; defineDecorator(HttpServiceController.prototype, 'getMetadata', httpGet()); defineDecorator(HttpServiceController.prototype, 'getMetadata', httpAction('metadata')); HttpServiceController.prototype.getIndex = function() { var self = this; return self.getBuilder().getEdm().then(function (result) { if (typeof self.getBuilder().getContextLink === 'function') { return Promise.resolve({ "@odata.context": self.getBuilder().getContextLink(self.context), value: result.entityContainer.entitySet }); } return Promise.resolve({ value: result.entityContainer.entitySet }); }); }; //apply descriptors defineDecorator(HttpServiceController.prototype, 'getIndex', httpGet()); defineDecorator(HttpServiceController.prototype, 'getIndex', httpAction("index")); /** * * @param {string} entitySet */ HttpServiceController.prototype.getItems = function(entitySet) { var self = this; var context = self.context; try { //get entity set var thisEntitySet = this.getBuilder().getEntitySet(entitySet); if (thisEntitySet == null) { return this.next(); } /** * @type {DataModel} */ var model = context.model(thisEntitySet.entityType.name); if (model == null) { return Promise.reject(new HttpNotFoundError("Entity not found")); } //set default $top property if (!context.params.hasOwnProperty('$top')) { Object.assign(context.params, { $top: DefaultTopQueryOption }); } //parse query filter and return a DataQueryable return model.filter(context.params).then(function(query) { var count = parseBoolean(self.context.params.$count); if (count) { //get items with count return query.getList().then(function(result) { //and finally return json result return Promise.resolve(thisEntitySet.mapInstanceSet(context,result)); }); } else { //get items return query.getItems().then(function(result) { //and finally return json result return Promise.resolve(thisEntitySet.mapInstanceSet(context,result)); }); } }); } catch (err) { return Promise.reject(err); } }; defineDecorator(HttpServiceController.prototype, 'getItems', httpGet()); defineDecorator(HttpServiceController.prototype, 'getItems', httpAction("items")); /** * * @param {string} entitySet */ HttpServiceController.prototype.postItems = function(entitySet) { var self = this; var context = self.context; try { //get entity set var thisEntitySet = this.getBuilder().getEntitySet(entitySet); if (thisEntitySet == null) { return this.next(); } /** * @type {DataModel} */ var model = context.model(thisEntitySet.entityType.name); if (model == null) { return Promise.reject(new HttpNotFoundError("Entity not found")); } // noinspection JSUnresolvedVariable var body = context.request.body; return model.save(body).then(function () { if (Array.isArray(body)) { return Promise.resolve(thisEntitySet.mapInstanceSet(context,body)); } else { return Promise.resolve(thisEntitySet.mapInstance(context,body)); } }); } catch (err) { return Promise.reject(err); } }; defineDecorator(HttpServiceController.prototype, 'postItems', httpPut()); defineDecorator(HttpServiceController.prototype, 'postItems', httpPost()); defineDecorator(HttpServiceController.prototype, 'postItems', httpAction("items")); /** * * @param {string} entitySet */ HttpServiceController.prototype.deleteItems = function(entitySet) { var self = this; var context = self.context; try { //get entity set var thisEntitySet = this.getBuilder().getEntitySet(entitySet); if (thisEntitySet == null) { return this.next(); } /** * @type {DataModel} */ var model = context.model(thisEntitySet.entityType.name); if (model == null) { return Promise.reject(new HttpNotFoundError("Entity not found")); } // noinspection JSUnresolvedVariable var body = context.request.body; return model.remove(body).then(function () { if (Array.isArray(body)) { return Promise.resolve(thisEntitySet.mapInstanceSet(context,body)); } else { return Promise.resolve(thisEntitySet.mapInstance(context,body)); } }); } catch (err) { return Promise.reject(err); } }; defineDecorator(HttpServiceController.prototype, 'deleteItems', httpDelete()); defineDecorator(HttpServiceController.prototype, 'deleteItems', httpAction("items")); /** * @param {*} id * @param {string} entitySet */ HttpServiceController.prototype.getItem = function(entitySet, id) { var self = this; var context = self.context; var model; try { //get entity set var thisEntitySet = this.getBuilder().getEntitySet(entitySet); if (thisEntitySet == null) { return this.next(); } else { model = context.model(thisEntitySet.entityType.name); // validate model if (model == null) { return Promise.reject(new HttpNotFoundError('Entity not found')); } if (id == null) { // search route parameters // noinspection JSUnresolvedVariable if (context.request.route && context.request.route.params && context.request.route.params.$filter) { return model.filter({ "$filter": context.request.route.params.$filter }).then(function(query) { return query.select(model.primaryKey).value().then(function (value) { if (value == null) { return Promise.reject(new HttpNotFoundError()); } return self.getItem(entitySet, value); }); }); } // id parameter cannot be resolved return Promise.reject(new HttpBadRequestError('Object identifier request parameter is missing.')); } } return model.filter({ "$select": context.params.$select, "$expand": context.params.$expand }).then(function(query) { return query.where(model.primaryKey).equal(id).getItem().then(function (result) { if (result == null) { return Promise.reject(new HttpNotFoundError()); } return Promise.resolve(thisEntitySet.mapInstance(context,result)); }); }); } catch (err) { return Promise.reject(err); } }; defineDecorator(HttpServiceController.prototype, 'getItem', httpGet()); defineDecorator(HttpServiceController.prototype, 'getItem', httpAction("item")); /** * * @param {string} entitySet * @param {*} id */ HttpServiceController.prototype.patchItem = function(entitySet, id) { var self = this; var context = self.context; var model; try { //get entity set var thisEntitySet = this.getBuilder().getEntitySet(entitySet); if (thisEntitySet == null) { return this.next(); } else { model = context.model(thisEntitySet.entityType.name); // validate model if (model == null) { return Promise.reject(new HttpNotFoundError('Entity not found')); } if (id == null) { // noinspection JSUnresolvedVariable if (context.request.route && context.request.route.params && context.request.route.params.$filter) { return model.filter({ "$filter": context.request.route.params.$filter }).then(function(query) { return query.select(model.primaryKey).value().then(function (value) { if (value == null) { return Promise.reject(new HttpNotFoundError('The requested object cannot be found or is inaccessible.')); } return self.patchItem(entitySet, value); }); }); } // id parameter cannot be resolved return Promise.reject(new HttpBadRequestError('Object identifier request parameter is missing.')); } } return model.where(model.primaryKey).equal(id).select("id").getItem().then(function (result) { if (result == null) { return Promise.reject(new HttpNotFoundError('The requested object cannot be found or is inaccessible.')); } // noinspection JSUnresolvedVariable var body = Object.assign(context.request.body, result); // save item return model.save(body).then(function () { return Promise.resolve(thisEntitySet.mapInstance(context,body)); }); }); } catch (err) { return Promise.reject(err); } }; defineDecorator(HttpServiceController.prototype, 'patchItem', httpPatch()); defineDecorator(HttpServiceController.prototype, 'patchItem', httpAction("item")); /** * * @param {string} entitySet * @param {*} id */ HttpServiceController.prototype.deleteItem = function(entitySet, id) { var self = this; var context = self.context; var model; try { //get entity set var thisEntitySet = this.getBuilder().getEntitySet(entitySet); if (thisEntitySet == null) { return this.next(); } // get model model = context.model(thisEntitySet.entityType.name); // validate model if (model == null) { return Promise.reject(new HttpNotFoundError('Entity not found')); } // validate object identifier if (typeof id === 'undefined') { // noinspection JSUnresolvedVariable if (context.request.route && context.request.route.params && context.request.route.params.$filter) { return model.filter({ "$filter":context.request.route.params.$filter }).then(function(query) { return query.select(model.primaryKey).value().then(function (value) { if (value == null) { return Promise.reject(new HttpNotFoundError('The requested object cannot be found or is inaccessible.')); } return self.deleteItem(entitySet, value); }); }); } // id parameter cannot be resolved return Promise.reject(new HttpBadRequestError('Object identifier request parameter is missing.')); } return model.where(model.primaryKey).equal(id).count().then(function (exists) { if (!exists) { return Promise.reject(new HttpNotFoundError()); } var obj = {}; obj[model.primaryKey] = id; return model.remove(obj).then(function () { return Promise.resolve(obj); }); }); } catch (err) { return Promise.reject(err); } }; defineDecorator(HttpServiceController.prototype, 'deleteItem', httpDelete()); defineDecorator(HttpServiceController.prototype, 'deleteItem', httpAction("item")); /** * * @param {string} entitySet */ HttpServiceController.prototype.postItem = function(entitySet) { var self = this; var context = self.context; try { //get entity set var thisEntitySet = this.getBuilder().getEntitySet(entitySet); if (thisEntitySet == null) { return this.next(); } /** * @type {DataModel} */ var model = context.model(thisEntitySet.entityType.name); if (model == null) { return Promise.reject(new HttpNotFoundError('Entity not found')); } // get request body // noinspection JSUnresolvedVariable var body = context.request.body; // save object return model.save(body).then(function () { if (Array.isArray(body)) { return Promise.resolve(thisEntitySet.mapInstanceSet(context,body)); } else { return Promise.resolve(thisEntitySet.mapInstance(context,body)); } }); } catch (err) { return Promise.reject(err); } }; defineDecorator(HttpServiceController.prototype, 'postItem', httpPost()); defineDecorator(HttpServiceController.prototype, 'postItem', httpPut()); defineDecorator(HttpServiceController.prototype, 'postItem', httpAction('item')); /** * @param {DataQueryable} target * @param {DataQueryable} source */ function extendQueryable(target, source) { if (source.query.$select) { target.query.$select = source.query.$select; } if (source.$view) { target.$view = source.$view; } if (source.$expand) { target.$expand=( target.$expand || []).concat(source.$expand); } if (source.query.$expand) { var targetExpand = []; if (Array.isArray(target.query.$expand)) { targetExpand = target.query.$expand; } else if (typeof target.query.$expand === 'object') { targetExpand.push(target.query.$expand); } var sourceExpand = [].concat(source.query.$expand); var res = sourceExpand.filter(function (x) { return typeof targetExpand.find(function (y) { return y.$entity.name === x.$entity.name; }) === 'undefined'; }); target.query.$expand= targetExpand.concat(res); } if (source.query.$group) { target.query.$group = source.query.$group; } if (source.query.$order) { target.query.$order = source.query.$order; } if (source.query.$prepared) { target.query.$where = source.query.$prepared; } if (source.query.$skip) { target.query.$skip = source.query.$skip; } if (source.query.$take) { target.query.$take = source.query.$take; } return target; } /** * * @param {string} entitySet * @param {string} navigationProperty * @param {*} id */ HttpServiceController.prototype.getNavigationProperty = function(entitySet, navigationProperty, id) { var self = this; var context = self.context; var model; try { //get entity set var thisEntitySet = this.getBuilder().getEntitySet(entitySet); if (thisEntitySet == null) { return this.next(); } /** * @type {DataModel} */ model = context.model(thisEntitySet.entityType.name); if (model == null) { return Promise.reject(new HttpNotFoundError('Entity not found')); } // validate object identifier if (id == null) { // noinspection JSUnresolvedVariable if (context.request.route && context.request.route.params && context.request.route.params.$filter) { return model.filter({ "$filter": context.request.route.params.$filter }).then(function(query) { return query.select(model.primaryKey).value().then(function (value) { if ( value == null) { return Promise.reject(new HttpNotFoundError('The requested object cannot be found or is inaccessible.')); } return self.getNavigationProperty(entitySet, navigationProperty, value); }); }); } return Promise.reject(new HttpForbiddenError()); } return model.where(model.primaryKey).equal(id).select(model.primaryKey).getTypedItem() .then(function(obj) { if (obj == null) { return Promise.reject(new HttpNotFoundError('The requested object cannot be found or is inaccessible.')); } //check if entity set has a function with the same name var func = thisEntitySet.entityType.hasFunction(navigationProperty); if (func) { var returnsCollection = typeof func.returnCollectionType === 'string'; var returnModel = context.model(func.returnType || func.returnCollectionType); //find method var memberFunc = EdmMapping.hasOwnFunction(obj, func.name); if (memberFunc) { var funcParameters = func.parameters.filter(function(x) { return x.name !== 'bindingParameter'; }).map(function(x) { return LangUtils.parseValue(context.params[x.name]); }); // get entity type var returnEntityType = self.getBuilder().getEntity(func.returnType || func.returnCollectionType); return Promise.resolve(memberFunc.apply(obj, funcParameters)).then(function(result) { if (result instanceof DataQueryable) { if (returnModel == null) { return Promise.reject(new HttpNotFoundError("Result Entity not found")); } //if the return value is a single instance if (!returnsCollection) { //pass context parameters (only $select and $expand) var params = _.pick(context.params, [ "$select", "$expand" ]); //filter with parameters return returnModel.filter(params).then(function(q) { // extend data queryable var q1 = extendQueryable(result, q); //get item return q1.getItem().then(function(result) { //return result return Promise.resolve(returnEntityType.mapInstance(context,result)); }); }); } //else if the return value is a collection return returnModel.filter( Object.assign({ "$top": DefaultTopQueryOption }, context.params)).then(function(q) { var count = context.params.hasOwnProperty('$inlinecount') ? parseBoolean(context.params.$inlinecount) : (context.params.hasOwnProperty('$count') ? parseBoolean(context.params.$count) : false); var q1 = extendQueryable(result, q); if (count) { return q1.getList().then(function(result) { return Promise.resolve(returnEntityType.mapInstanceSet(context,result)); }); } return q1.getItems().then(function(result) { return Promise.resolve(returnEntityType.mapInstanceSet(context,result)); }); }); } // if return entity type is defined if (returnEntityType) { if (returnsCollection) { // map collection return Promise.resolve(returnEntityType.mapInstanceSet(context, result)); } // or map object return Promise.resolve(returnEntityType.mapInstance(context, result)); } // otherwise return value return Promise.resolve(mapPrimitiveInstance.bind(self)(context, result, func.returnType || func.returnCollectionType)); }); } } //get primary key var key = obj[model.primaryKey]; //get mapping var mapping = model.inferMapping(navigationProperty); //get count parameter var count = context.params.hasOwnProperty('$inlinecount') ? parseBoolean(context.params.$inlinecount) : (context.params.hasOwnProperty('$count') ? parseBoolean(context.params.$count) : false); if (mapping == null) { //try to find associated model //get singular model name var otherModelName = pluralize.singular(navigationProperty); //search for model with this name var otherModel = self.context.model(otherModelName); if (otherModel) { var otherFields = otherModel.attributes.filter(function(x) { return x.type === model.name; }); if (otherFields.length>1) { return Promise.reject(new HttpMethodNotAllowedError('Multiple associations found')); } else if (otherFields.length === 1) { var otherField = otherFields[0]; mapping = otherModel.inferMapping(otherField.name); if (mapping && mapping.associationType === 'junction') { var attr; //search model for attribute that has an association of type junction with child model if (mapping.parentModel === otherModel.name) { attr = otherModel.attributes.find(function(x) { return x.name === otherField.name; }); } else { attr = model.attributes.find(function(x) { return x.type === otherModel.name; }); } if (attr) { model = attr.name; mapping = model.inferMapping(attr.name); } } } } if (mapping == null) { return Promise.reject(new HttpNotFoundError("Association not found")); } } if (mapping.associationType === 'junction') { /** * @type {DataQueryable} */ var junction = obj.property(navigationProperty); return junction.model.filter(self.context.params).then(function (q) { //merge properties if (q.query.$select) { junction.query.$select = q.query.$select; } if (q.$expand) { junction.$expand = q.$expand; } if (q.query.$group) { junction.query.$group = q.query.$group; } if (q.query.$order) { junction.query.$order = q.query.$order; } if (q.query.$prepared) { junction.query.$where = q.query.$prepared; } if (q.query.$skip) { junction.query.$skip = q.query.$skip; } if (q.query.$take) { junction.query.$take = q.query.$take; } var otherEntitySet = self.getBuilder().getEntityTypeEntitySet(junction.model.name); if (count) { return junction.getList().then(function (result) { if (typeof otherEntitySet === 'undefined') { return Promise.resolve(thisEntitySet.mapInstanceSet(context,result)); } return Promise.resolve(otherEntitySet.mapInstanceSet(context,result)); }); } else { return junction.getItems().then(function (result) { if (typeof otherEntitySet === 'undefined') { return Promise.resolve(thisEntitySet.mapInstanceSet(context,result)); } return Promise.resolve(otherEntitySet.mapInstanceSet(context,result)); }); } }); } else if (mapping.parentModel === model.name && mapping.associationType === 'association') { //get associated model var associatedModel = self.context.model(mapping.childModel); if (associatedModel == null) { return Promise.reject(new HttpNotFoundError('Associated model not found')); } var associatedEntitySet = self.getBuilder().getEntityTypeEntitySet(associatedModel.name); return associatedModel.filter( Object.assign({ "$top": DefaultTopQueryOption },context.params)).then(function(q) { if (count) { return q.where(mapping.childField).equal(key).getList().then(function (result) { return Promise.resolve(associatedEntitySet.mapInstanceSet(context,result)); }); } else { // get navigation property var property = thisEntitySet.getEntityTypeNavigationProperty(navigationProperty, true); if (property == null) { return Promise.reject(Object.assign(new HttpBadRequestError('Invalid navigation property.'), { entitySet: thisEntitySet.name, navigationProperty: navigationProperty })); } // if navigation property type is a collection of objects if (EdmType.IsCollection(property.type)) { return q.where(mapping.childField).equal(key).getItems().then(function (result) { return Promise.resolve(associatedEntitySet.mapInstanceSet(context,result)); }); } else { // else send return q.where(mapping.childField).equal(key).getItem().then(function (result) { return Promise.resolve(thisEntitySet.mapInstanceProperty(context, navigationProperty, result)); }); } } }); } else if (mapping.childModel === model.name && mapping.associationType === 'association') { //get associated model var parentModel = self.context.model(mapping.parentModel); if (parentModel == null) { return Promise.reject(new HttpNotFoundError('Parent associated model not found')); } return model.where(model.primaryKey).equal(obj.id) .select(model.primaryKey,navigationProperty) .expand(navigationProperty).getItem() .then(function(result) { var parentEntitySet = self.getBuilder().getEntityTypeEntitySet(parentModel.name); return Promise.resolve(parentEntitySet.mapInstance(context,result[navigationProperty])); }); } else { return Promise.reject(new HttpNotFoundError()); } }); } catch (err) { return Promise.reject(err); } }; defineDecorator(HttpServiceController.prototype, 'getNavigationProperty', httpGet()); defineDecorator(HttpServiceController.prototype, 'getNavigationProperty', httpAction("navigationProperty")); /** * * @param {string} entitySet * @param {string} entityFunction * @param {*} id */ HttpServiceController.prototype.getEntityFunction = function(entitySet, entityFunction, id) { var self = this; var context = self.context; /** * get current model builder * @type {ODataModelBuilder} */ var builder = context.getApplication().getStrategy(ODataModelBuilder); // get entity set var thisEntitySet = this.getBuilder().getEntitySet(entitySet); if (thisEntitySet == null) { return this.next(); } // get underlying model var thisModel = context.model(thisEntitySet.entityType.name); if (thisModel == null) { return Promise.reject(new HttpNotFoundError('Entity not found')); } // if id is undefined if (id == null) { return this.next(); } // get entity function var func = thisEntitySet.entityType.hasFunction(entityFunction); if (func) { return thisModel.where(thisModel.primaryKey).equal(id).select(thisModel.primaryKey).getTypedItem().then(function (obj) { if (typeof obj === 'undefined') { return Promise.reject(new HttpNotFoundError("Entity type action cannot be empty")); } //check if entity set has a function with the same name var memberFunc = EdmMapping.hasOwnAction(obj, func.name); if (memberFunc) { var functionParameters = func.parameters.filter(function (x) { return x.name !== 'bindingParameter'; }).map(function (x) { return context.getParam(x.name); }); return Promise.resolve(memberFunc.apply(obj, functionParameters)).then(function (result) { // check if action returns a collection of object var returnsCollection = typeof func.returnCollectionType === 'string'; var returnEntitySet; if (returnsCollection) { returnEntitySet = builder.getEntityTypeEntitySet(func.returnCollectionType); } if (result instanceof DataQueryable) { if (returnsCollection) { // call DataModel.getItems() instead of DataModel.getList() // an action that returns a collection of objects must always return a native array (without paging parameters) return result.getItems().then(function (finalResult) { if (returnEntitySet) { return Promise.resolve(returnEntitySet.mapInstanceSet(context, finalResult)); } return Promise.resolve(finalResult); }); } else { // otherwise call DataModel.getItem() to get only the first item of the result set return result.getItem().then(function (finalResult) { return Promise.resolve(finalResult); }); } } if (typeof result === 'undefined') { // return no content return Promise.resolve(); } // return result as native object if (returnsCollection && returnEntitySet) { return Promise.resolve(returnEntitySet.mapInstanceSet(context, result)); } return Promise.resolve(result); }); } return Promise.reject(new HttpBadRequestError('Member function cannot be found')); }); } return this.next(); }; defineDecorator(HttpServiceController.prototype, 'getEntityFunction', httpGet()); defineDecorator(HttpServiceController.prototype, 'getEntityFunction', httpAction("entityFunction")); /** * * @param {string} entitySet * @param {string} entitySetFunction * @param {string=} navigationProperty */ HttpServiceController.prototype.getEntitySetFunction = function(entitySet, entitySetFunction, navigationProperty) { var self = this; var context = self.context; var thisEntitySet = this.getBuilder().getEntitySet(entitySet); if (thisEntitySet == null) { return this.next(); } var model = context.model(thisEntitySet.entityType.name); if (model == null) { return Promise.reject(new HttpNotFoundError('Entity not found')); } var func = thisEntitySet.entityType.collection.hasFunction(entitySetFunction); if (func) { //get data object class var DataObjectClass = model.getDataObjectType(); var staticFunc = EdmMapping.hasOwnFunction(DataObjectClass,entitySetFunction); if (staticFunc) { return Promise.resolve(staticFunc(context)).then(function(result) { var returnsCollection = typeof func.returnCollectionType === 'string'; // get return entity set var returnEntitySet = self.getBuilder().getEntityTypeEntitySet(func.returnType || func.returnCollectionType); // get model var returnModel = context.model(func.returnType || func.returnCollectionType); // get return type var returnEntityType = self.getBuilder().getEntity(func.returnType || func.returnCollectionType); if (result instanceof DataQueryable) { // if model is null throw error if (returnModel == null) { return Promise.reject(new HttpNotFoundError('Result Entity not found')); } if (!returnsCollection) { //pass context parameters (if navigationProperty is empty) var params = {}; if (navigationProperty == null) { params = _.pick(context.params, [ "$select", "$expand" ]); } return returnModel.filter(params).then(function(q) { //do not add context params var q1 = extendQueryable(result, q); return q1.getItem().then(function(result) { if (result == null) { return Promise.reject(new HttpNotFoundError()); } if (typeof navigationProperty === 'string') { return self.getNavigationProperty(returnEntityType.name,navigationProperty, result[returnModel.primaryKey]) } return Promise.resolve(returnEntityType.mapInstance(context,result)); }); }); } if (typeof navigationProperty !== 'undefined') { return Promise.reject(new HttpBadRequestError()); } return returnModel.filter(params)( Object.assign({ "$top":DefaultTopQueryOption },context.params)).then(function(q) { var count = context.params.hasOwnProperty('$inlinecount') ? parseBoolean(context.params.$inlinecount) : (context.params.hasOwnProperty('$count') ? parseBoolean(context.params.$count) : false); var q1 = extendQueryable(result, q); if (count) { return q1.getList().then(function(result) { return Promise.resolve(returnEntityType.mapInstanceSet(context,result)); }); } return q1.getItems().then(function(result) { return Promise.resolve(returnEntityType.mapInstanceSet(context,result)); }); }); } if (navigationProperty == null) { // if entity set is not defined if (returnEntityType) { // map result if (returnsCollection) { // for collection return Promise.resolve(returnEntityType.mapInstanceSet(context,result)); } else { // for object return Promise.resolve(returnEntityType.mapInstance(context,result)); } } else { // return native object return Promise.resolve(mapPrimitiveInstance.bind(self)(context, result, func.returnType || func.returnCollectionType)); } } if (returnEntitySet == null) { return Promise.reject(new HttpNotFoundError('Result EntitySet not found')); } return self.getNavigationProperty(returnEntitySet.name,navigationProperty, result[returnModel.primaryKey]) }); } } return this.next(); }; defineDecorator(HttpServiceController.prototype, 'getEntitySetFunction', httpGet()); defineDecorator(HttpServiceController.prototype, 'getEntitySetFunction', httpAction("entitySetFunction")); defineDecorator(HttpServiceController.prototype, 'getEntitySetFunction', httpRoute(':entitySet/:entitySetFunction/?', 'json', 8)); /** * * @param {string} entitySet * @param {string} entityAction * @param {*} id */ HttpServiceController.prototype.postEntityAction = function(entitySet, entityAction, id) { var self = this; var context = self.context; var thisEntitySet = this.getBuilder().getEntitySet(entitySet); if (thisEntitySet == null) { return Promise.reject(new HttpNotFoundError("Entity set not found")); } /** * get current data model * @type {DataModel} */ var thisModel = context.model(thisEntitySet.entityType.name); if (typeof thisModel === 'undefined') { return Promise.reject(new HttpNotFoundError("Entity type not found")); } if (typeof entityAction === 'undefined') { return Promise.reject(new HttpNotFoundError("Entity type action cannot be empty")); } // validate entity type function var action = thisEntitySet.entityType.hasAction(entityAction); if (typeof action === 'undefined') { return Promise.reject(new HttpNotFoundError("Entity type action not found")); } // get typed item return thisModel.where(thisModel.primaryKey).equal(id).select(thisModel.primaryKey).getTypedItem().then(function (obj) { if (typeof obj === 'undefined') { return Promise.reject(new HttpNotFoundError("Entity type action cannot be empty")); } //check if entity set has a function with the same name var memberFunc = EdmMapping.hasOwnAction(obj, action.name); if (memberFunc) { var actionParameters = []; var parameters = action.parameters.filter(function (x) { return x.name !== 'bindingParameter'; }); // if action has only one parameter and this parameter has fromBody flag if (parameters.length === 1 && parameters[0].fromBody) { // noinspection JSUnresolvedVariable actionParameters.push(context.request.body); } else { // add other parameters by getting request body attributes parameters.forEach(function (x) { // noinspection JSUnresolvedVariable actionParameters.push(context.request.body[x.name]); }); } // get result entity type var returnEntityType = self.getBuilder().getEntity(action.returnType || action.returnCollectionType); return Promise.resolve(memberFunc.apply(obj, actionParameters)).then(function (result) { // check if action returns a collection of object var returnsCollection = typeof action.returnCollectionType === 'string'; if (result instanceof DataQueryable) { if (returnsCollection) { // an action that returns a collection of objects must always return a native array (without paging parameters) return result.getItems().then(function (finalResult) { return Promise.resolve(returnEntityType.mapInstanceSet(context, finalResult)); }); } else { // otherwise call DataModel.getItem() to get only the first item of the result set return result.getItem().then(function (finalResult) { return Promise.resolve(returnEntityType.mapInstance(context, finalResult)); }); } } // if return entity type is defined if (returnEntityType) { if (returnsCollection) { // map collection return Promise.resolve(returnEntityType.mapInstanceSet(context, result)); } // or map object return Promise.resolve(returnEntityType.mapInstance(context, result)); } // otherwise return value return Promise.resolve(mapPrimitiveInstance.bind(self)(context, result, action.returnType || action.returnCollectionType)); }); } // entity type does not have an instance method with the given name, continue return Promise.reject(new HttpNotFoundError()); }).catch(function (err) { return Promise.reject(err); }); }; defineDecorator(HttpServiceController.prototype, 'postEntityAction', httpPost()); defineDecorator(HttpServiceController.prototype, 'postEntityAction', httpAction("entityAction")); /** * * @param {string} entitySet * @param {string} entitySetAction */ HttpServiceController.prototype.postEntitySetAction = function(entitySet, entitySetAction) { var self = this; var context = self.context; var thisEntitySet = this.getBuilder().getEntitySet(entitySet); if (thisEntitySet == null) { return Promise.reject(new HttpNotFoundError("Entity set not found")); } /** * get current data model * @type {DataModel} */ var thisModel = context.model(thisEntitySet.entityType.name); if (typeof thisModel === 'undefined') { return Promise.reject(new HttpNotFoundError("Entity type not found")); } if (typeof entitySetAction === 'undefined') { return Promise.reject(new HttpNotFoundError("Entity type action cannot be empty")); } var action = thisEntitySet.entityType.collection.hasAction(entitySetAction); if (action) { //get data object class var DataObjectClass = thisModel.getDataObjectType(); var actionFunc = EdmMapping.hasOwnAction(DataObjectClass, entitySetAction); if (typeof actionFunc !== 'function') { return Promise.reject(new HttpBadRequestError('Invalid entity set configuration. The specified action cannot be found')); } var actionParameters = []; var parameters = action.parameters.filter(function (x) { return x.name !== 'bindingParameter'; }); // if parameters must be included in body if (parameters.length) { // validate request body // noinspection JSUnresolvedVariable if (typeof context.request.body === 'undefined') { // throw bad request if body is missing return Promise.reject(new HttpBadRequestError('Request body cannot be empty')); } } // add context as the first parameter actionParameters.push(context); // if action has only one parameter and this parameter has fromBody flag if (parameters.length === 1 && parameters[0].fromBody) { // noinspection JSUnresolvedVariable actionParameters.push(context.request.body); } else { // add other parameters by getting request body attributes parameters