(function (window, angular) { var module = angular.module('framework.services.model', []); module.provider('Model', modelsProvider); //module.service('DataContext', dataService); function modelsProvider() { var schemas = { }; this.registerSchema = registerSchema; this.$get = buildService; function registerSchema(name, path, schema) { schemas[name] = [path, schema]; return this; } buildService.$inject = ['$config', '$http', '$q']; function buildService($config, $http, $q) { var models = {}; angular.forEach(schemas, function (value, key) { var modelName = key; var modelPath = value[0]; var modelSchema = value[1]; models[modelName] = BuildConstructor(modelPath, modelSchema); }); return models; function Entity(apiEndpoint, schema, values) { this.$$apiLocation = apiEndpoint; this.$$schema = schema; this.$$originalValues = {}; this.$$changedValues = {}; this.$$foreignModels = {}; this.$getChanges = getChanges; this.$validate = validateEntity; this.$save = saveEntity; this.$update = updateEntity; this.$patch = patchEntity; this.$delete = deleteEntity; this.$commit = commitEntity; this.$rollback = rollbackEntity; angular.forEach(schema, function (value, key) { var fieldName = key, fieldDef = value; if (values[fieldName] !== undefined) { if (typeof fieldDef.type === 'string' && values[fieldName] !== null) { this.$$originalValues[fieldName] = new models[fieldDef.type](values[fieldName]); this.$$foreignModels[fieldName] = fieldDef.type; } else { this.$$originalValues[fieldName] = values[fieldName]; } } else { this.$$originalValues[fieldName] = fieldDef.defaultValue; } Object.defineProperty(this, fieldName, { configurable: false, enumerable: true, get: fieldDef.calculated || function () { return this.$$changedValues.hasOwnProperty(fieldName) ? this.$$changedValues[fieldName] : this.$$originalValues[fieldName]; }, set: function (value) { if (angular.isUndefined(fieldDef.editable) || fieldDef.editable) { this.$$changedValues[fieldName] = value; //CHECK: Cast to correct type?? } } }); }, this); function getChanges() { return this.$$changedValues; } function validateEntity() { } //TODO function saveEntity() { return $http.post($config.API_ENDPOINT + this.$$apiLocation, this); } function updateEntity() { var config = { params: { id: this.Id } }; return $http.put($config.API_ENDPOINT + this.$$apiLocation, this, config); } function patchEntity(skipSubObjects) { var self = this; var delta = angular.extend({ Id: this.Id }, this.$getChanges()); var config = { params: { id: this.Id } }; var request = $http.patch($config.API_ENDPOINT + this.$$apiLocation, delta, config); request.success(function () { self.$commit(true); }); return request; } function deleteEntity() { var config = { params: { id: this.Id } }; return $http.delete($config.API_ENDPOINT + this.$$apiLocation, config); } function commitEntity(skipSubObjects) { angular.forEach(this.$$changedValues, function (value, key) { var fieldName = key, fieldValue = value; this.$$originalValues[fieldName] = fieldValue }, this); this.$$changedValues = {}; if (skipSubObjects) return; angular.forEach(this.$$foreignModels, function (value, key) { this[key].$commit(); }, this); } function rollbackEntity(skipSubObjects) { this.$$changedValues = {}; if (skipSubObjects) return; angular.forEach(this.$$foreignModels, function (value, key) { this[key].$rollback(); }, this); } } function Query(apiEndpoint, ctor) { var includes = []; var selects = []; var filters = []; var order = []; var modelCtor = ctor; this.include = include; this.select = select; this.filter = filter; this.where = filter; this.parseAs = parseAs; this.execute = execute; this.orderBy = orderBy; function parseAs(parser) { modelCtor = parser; } function include() { if (arguments.length === 2) { if (angular.isObject(arguments[1])) { //TODO: Process subquery } else if (angular.isFunction(arguments[1])) { //TODO: Process subquery } } else { [].push.apply(includes, arguments); } return this; } function orderBy(prop) { this.order.push(prop); //todo add dir and aliases } function select() { [].push.apply(selects, arguments); modelCtor = null; return this; } function filter(key, op, value) { if (arguments.length === 1) { filters.push(key); } else if (arguments.length === 3) { switch (op) { case '&&': op = 'and'; break; case '||': op = 'or'; break; case '=': case '==': case '===': op = 'eq'; break; case '!=': case '!==': case '!===': op = 'ne'; break; case '>': op = 'gt'; break; case '>=': op = 'ge'; break; case '<': op = 'lt'; break; case '<=': op = 'le'; break; default: break; } filters.push(key + ' ' + op + ' ' + value); } return this; } function execute() { var config = {}; config.params = _buildParams(); apiCall = $config.API_ENDPOINT + apiEndpoint; return $http.get(apiCall, config).then(function (response) { if (!modelCtor) { return response.data; } else { if (angular.isArray(response.data)) { var results = []; angular.forEach(response.data, function (d) { results.push(new modelCtor(d)); }); return results; } else { return new modelCtor(response.data); } } }); } function _buildParams() { var params = {}; if (includes.length) { params.$expand = includes.join(','); } if (filters.length) { params.$filter = filters.join(' and '); } if (selects.length) { params.$select = selects.join(','); } return params; } } function ModelCache() { } function BuildConstructor(apiEndpoint, schema) { function ModelCtor(values) { var values = values || {}; this.base = Entity; this.base(apiEndpoint, schema, values); } ModelCtor.prototype = new Entity; ModelCtor.$schema = schema; ModelCtor.query = query; ModelCtor.getById = getById; var passon = ModelCtor; function query() { return new Query(apiEndpoint, passon); } function getById(id) { return new Query(apiEndpoint) .filter('Id', 'eq', id); } return ModelCtor; } } } dataService.$inject = ['$config', '$http', '$q', 'Models']; function dataService($config, $http, $q, Models) { var entityIdMaps = {}; var entityArrays = {}; } })(window, angular);