Last active
January 9, 2017 20:50
-
-
Save nathanbarrett/cfe31f4ad3e1235872f6dc3c4c901bf4 to your computer and use it in GitHub Desktop.
Angular 1.X helper service for helping with crud tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * A backend service for other services wanting crud operations | |
| * | |
| * @params | |
| * | |
| * @returns none | |
| */ | |
| (function () { | |
| 'use strict'; | |
| angular.module('app').service('CrudService', CrudService); | |
| CrudService.$inject = ['$http', '$log', '$timeout', '$q']; | |
| function CrudService ($http, $log, $timeout, $q){ | |
| var self = this; | |
| var logger; | |
| /**** Initializations ****/ | |
| self.$onInit(); | |
| /**** Methods ****/ | |
| /* | |
| * Initialization of the service | |
| * | |
| * @params none | |
| * | |
| * @returns none | |
| */ | |
| function $onInit () { | |
| self.config = { | |
| modelName: null, | |
| pluralModelName: null, | |
| defaultInclude: null, | |
| actionHttp: { | |
| create: { | |
| method: 'PUT', | |
| url: null | |
| }, | |
| createMany: { | |
| method: 'PUT', | |
| url: null | |
| }, | |
| get: { | |
| method: 'GET', | |
| url: null | |
| }, | |
| getMany: { | |
| method: 'GET', | |
| url: null | |
| }, | |
| update: { | |
| method: 'POST', | |
| url: null | |
| }, | |
| updateMany: { | |
| method: 'POST', | |
| url: null | |
| }, | |
| delete: { | |
| method: 'DELETE', | |
| url: null | |
| }, | |
| deleteMany: { | |
| method: 'DELETE', | |
| url: null | |
| } | |
| }, | |
| defaultWhere: null, | |
| testing: { | |
| enabled: false, | |
| delay: 500, | |
| generator: null | |
| }, | |
| logging: { | |
| events: false, | |
| errors: false | |
| }, | |
| responseDataKey: 'data', | |
| responseSuccessKey: 'success', | |
| responseSuccessValue: true, | |
| responseSuccessStrict: true, | |
| attachQueryItemsToRequestBody: true, // on put and post requests send 'include' and 'where' queries in the body vs the url | |
| idPlaceholder: ':id' | |
| }; | |
| if (window.errorLogger) { | |
| logger = window.errorLogger; | |
| } | |
| else { | |
| logger = { | |
| error: function (description, context) { | |
| $log.error(description, context); | |
| throw description; | |
| } | |
| }; | |
| } | |
| } | |
| /* | |
| * Helper for setting a config object | |
| * | |
| * @params object config | |
| * | |
| * @returns none | |
| */ | |
| self.setConfig = setConfig; | |
| function setConfig (config) { | |
| self.config = overrideValues(angular.copy(self.config), config); | |
| } | |
| /* | |
| * Sets the only required configuration item, modelName | |
| * | |
| * @params string modelName | |
| * | |
| * @returns none | |
| */ | |
| self.model = model; | |
| function model (modelName, pluralName) { | |
| self.config.modelName = modelName; | |
| if (pluralName) { | |
| self.config.pluralModelName = pluralName; | |
| } | |
| } | |
| /* | |
| * Fetches a model by id | |
| * | |
| * @params int id, (object|array) include | |
| * | |
| * @returns Promise<object response> | |
| */ | |
| self.get = get; | |
| function get (id, include) { | |
| if (angular.isArray(id) || angular.isObject(id)) { | |
| return self.getMany(id, include); | |
| } | |
| return call('get', id, include); | |
| } | |
| /* | |
| * Fetches many models, confined to ids if needed | |
| * | |
| * @params array ids | |
| * | |
| * @returns Promise<model> | |
| */ | |
| self.getMany = getMany; | |
| function getMany (ids, include, where) { | |
| if (!angular.isArray(ids)) { | |
| ids = []; | |
| } | |
| return call('getMany', ids, include, where); | |
| } | |
| /* | |
| * Creates a new model | |
| * | |
| * @params object data, include | |
| * | |
| * @returns Promise<model> | |
| */ | |
| self.create = create; | |
| function create (data, include) { | |
| return call ('create', data, include); | |
| } | |
| /* | |
| * Creates many new models | |
| * | |
| * @params object data, array include | |
| * | |
| * @returns Promise<Array<model>> | |
| */ | |
| self.createMany = createMany; | |
| function createMany (data, include, where) { | |
| return call ('createMany', data, include, where); | |
| } | |
| /* | |
| * Updates an existing model | |
| * | |
| * @params object data, array include | |
| * | |
| * @returns Promise<model> | |
| */ | |
| self.update = update; | |
| function update (data, include) { | |
| return call ('update', data, include); | |
| } | |
| /* | |
| * Updates many existing models | |
| * | |
| * @params object data, array include, array where | |
| * | |
| * @returns Promise<Array<model>> | |
| */ | |
| self.updateMany = updateMany; | |
| function updateMany (data, include, where) { | |
| return call ('updateMany', data, include, where); | |
| } | |
| /* | |
| * Deletes an existing model | |
| * | |
| * @params int id | |
| * | |
| * @returns Promise<model> | |
| */ | |
| self.delete = del; | |
| function del (id) { | |
| return call ('delete', id); | |
| } | |
| /* | |
| * Deletes many existing models | |
| * | |
| * @params array ids | |
| * | |
| * @returns Promise<Array<model>> | |
| */ | |
| self.deleteMany = deleteMany; | |
| function deleteMany (ids) { | |
| if (!angular.isArray(ids)) { | |
| ids = []; | |
| } | |
| return call ('deleteMany', ids); | |
| } | |
| /* | |
| * Private function that all of the calls go through | |
| * | |
| * @params string action, string method, (object|array) data, (object|array) include, (object|array) where | |
| * | |
| * @returns Promise<object response> | |
| */ | |
| function call (action, data, include, where) { | |
| if (!angular.isString(self.config.modelName) || self.config.modelName === '') { | |
| logger.error('You need to give a model name before making calls', {config: self.config, args: arguments}); | |
| } | |
| var callMethod = self.config.actionHttp[action].method; | |
| var callModelName = self.config.modelName; | |
| if (action.indexOf('Many') >= 0 && self.config.pluralModelName) { | |
| callModelName = self.config.pluralModelName; | |
| } | |
| var callId = null; | |
| if (angular.isNumber(data) || angular.isString(data) || angular.isArray(data)) { | |
| callId = data; | |
| } | |
| var httpConfig = { | |
| method: callMethod, | |
| url: generateUrl(action, callId, include, where) | |
| }; | |
| if ((angular.isObject(data) || angular.isArray(data)) && (callMethod === 'PUT' || callMethod === 'POST')) { | |
| httpConfig.data = data; | |
| } | |
| if (self.config.testing.enabled) { | |
| if (['create', 'createMany'].indexOf(action) >= 0) { | |
| var model = data; | |
| model.id = Math.ceil(Math.random() * 1000); | |
| } | |
| else { | |
| if (angular.isFunction(self.config.testing.model)) { | |
| model = self.config.testing.model(); | |
| } | |
| else { | |
| model = self.config.testing.model; | |
| } | |
| } | |
| var testingDefer = $q.defer(); | |
| $timeout(function () { | |
| testingDefer.resolve(model); | |
| }, self.config.testing.delay); | |
| return testingDefer.promise; | |
| } | |
| return $http(httpConfig) | |
| .then(function (response) { | |
| var data = response[self.config.responseDataKey]; | |
| if (self.config.responseSuccessKey && | |
| (self.config.responseSuccessStrict ? | |
| data[self.config.responseSuccessKey] !== self.config.responseSuccessValue : | |
| data[self.config.responseSuccessKey] != self.config.responseSuccessValue)) { | |
| logger.error(callModelName + ': non success response', {httpConfig: httpConfig, response: response}); | |
| return null; | |
| } | |
| if (self.config.logging.events) { | |
| console.group('Succesfully ' + getActionVerb(action) + ' ' + callModelName); | |
| console.info(callModelName + ':'); | |
| console.log(data[callModelName]); | |
| if (data.previous) { | |
| console.info('Previous ' + callModelName + ':'); | |
| console.log(data.previous); | |
| } | |
| console.info('response:'); | |
| console.log(response); | |
| console.groupEnd(); | |
| } | |
| return data[callModelName]; | |
| }, function (error) { | |
| if (self.config.logging.errors) { | |
| logger.error(callModelName + ': error response', {error: error, httpConfig: httpConfig}); | |
| } | |
| return error; | |
| }) | |
| } | |
| /* | |
| * Generates the call url given the parameters | |
| * | |
| * @params string action, include, where | |
| * | |
| * @returns string url | |
| */ | |
| function generateUrl (action, id, include, where) { | |
| var url = null; | |
| switch (action) { | |
| case 'create': | |
| if (self.config.actionHttp.create.url) { | |
| url = self.config.actionHttp.create.url; | |
| } | |
| else { | |
| url = '/' + self.config.modelName; | |
| } | |
| break; | |
| case 'createMany': | |
| if (self.config.actionHttp.createMany.url) { | |
| url = self.config.actionHttp.createMany.url; | |
| } | |
| else if (self.config.pluralModelName) { | |
| url = '/' + self.config.pluralModelName; | |
| } | |
| else { | |
| url = '/' + self.config.modelName + 's'; | |
| } | |
| break; | |
| case 'get': | |
| if (self.config.actionHttp.get.url) { | |
| url = injectIdIntoUrl(self.config.actionHttp.get.url, id); | |
| } | |
| else { | |
| url = '/' + self.config.modelName + '/' + id; | |
| } | |
| break; | |
| case 'getMany': | |
| if (self.config.actionHttp.getMany.url) { | |
| url = self.config.actionHttp.getMany.url; | |
| } | |
| else if (self.config.pluralModelName) { | |
| url = '/' + self.config.pluralModelName; | |
| } | |
| else { | |
| url = '/' + self.config.modelName + 's'; | |
| } | |
| break; | |
| case 'update': | |
| if (self.config.actionHttp.update.url) { | |
| url = injectIdIntoUrl(self.config.actionHttp.update.url, id); | |
| } | |
| else { | |
| url = '/' + self.config.modelName + '/' + id; | |
| } | |
| break; | |
| case 'updateMany': | |
| if (self.config.actionHttp.updateMany.url) { | |
| url = self.config.actionHttp.updateMany.url; | |
| } | |
| else if (self.config.pluralModelName) { | |
| url = '/' + self.config.pluralModelName; | |
| } | |
| else { | |
| url = '/' + self.config.modelName + 's'; | |
| } | |
| break; | |
| case 'delete': | |
| if (self.config.actionHttp.delete.url) { | |
| url = injectIdIntoUrl(self.config.actionHttp.delete.url, id); | |
| } | |
| else { | |
| url = '/' + self.config.modelName + '/' + id; | |
| } | |
| break; | |
| case 'deleteMany': | |
| if (self.config.actionHttp.deleteMany.url) { | |
| url = self.config.actionHttp.deleteMany.url; | |
| } | |
| else if (self.config.pluralModelName) { | |
| url = '/' + self.config.pluralModelName; | |
| } | |
| else { | |
| url = '/' + self.config.modelName + 's'; | |
| } | |
| break; | |
| default: | |
| logger.error('Not a defined action for crud service: ' + action, {args: arguments}); | |
| break; | |
| } | |
| var requestMethod = self.config.actionHttp[action].method; | |
| if (angular.isArray(id) && id.length > 0 && (requestMethod === 'DELETE' || requestMethod === 'GET')){ | |
| var idAppend = url.indexOf('?') >= 0 ? '&' : '?'; | |
| url = url + idAppend + 'ids=' + JSON.stringify(id); | |
| } | |
| if ((include || where) && !(self.config.attachQueryItemsToRequestBody && (requestMethod === 'POST' || requestMethod === 'PUT'))) { | |
| if (include) { | |
| var includeAppend = url.indexOf('?') >= 0 ? '&' : '?'; | |
| url = url + includeAppend + 'include=' + JSON.stringify(include); | |
| } | |
| if (where) { | |
| var whereAppend = url.indexOf('?') >= 0 ? '&' : '?'; | |
| url = url + whereAppend + 'where=' + JSON.stringify(where); | |
| } | |
| } | |
| return url; | |
| } | |
| /* | |
| * Finds any optional id placeholder in the given url and replaces it with the given id | |
| * | |
| * @params string url, int id | |
| * | |
| * @returns string injectedUrl | |
| */ | |
| function injectIdIntoUrl (url, id) { | |
| if (!id || url.indexOf(self.config.idPlaceholder) < 0 || angular.isArray(id)) return url; | |
| return url.replace(self.config.idPlaceholder, id); | |
| } | |
| /* | |
| * Overrides values in objectA with any matching values of object B | |
| * | |
| * @params object a, object b | |
| * | |
| * @returns object | |
| */ | |
| function overrideValues (a, b) { | |
| if (!angular.isObject(a) || !angular.isObject(b)) { | |
| return a; | |
| } | |
| angular.forEach(a, function (value, key) { | |
| if (angular.isObject(a[key])) { | |
| if (angular.isObject(b[key])) { | |
| a[key] = overrideValues(a[key], b[key]); | |
| } | |
| else { | |
| a[key] = b[key]; | |
| } | |
| } | |
| else if (b[key] !== undefined) { | |
| a[key] = b[key]; | |
| } | |
| }); | |
| return a; | |
| } | |
| function getActionVerb (action) { | |
| if (action === 'get') return 'got'; | |
| else if (action === 'getMany') return 'got many of'; | |
| else if (action.indexOf('Many') >= 0) { | |
| var baseAction = action.replace('Many', ''); | |
| return baseAction + 'd many of'; | |
| } | |
| else { | |
| return action + 'd'; | |
| } | |
| } | |
| } | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment