Model.service.js 11 KB
(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);