From 13c6a4d55301dfd3862e02523d8a557705343957 Mon Sep 17 00:00:00 2001 From: Professor Raghunath Date: Tue, 3 May 2016 07:06:20 -0400 Subject: [PATCH] Adding all files --- .gitignore | 3 +- package.json | 110 +++++++++++----------- src/base.model.js | 44 +++++++++ src/base.model.spec.js | 87 ++++++++++++++++++ src/store.config.js | 24 +++++ src/store.config.spec.js | 22 +++++ src/store.service.js | 188 ++++++++++++++++++++++++++++++++++++++ src/store.service.spec.js | 137 +++++++++++++++++++++++++++ 8 files changed, 558 insertions(+), 57 deletions(-) create mode 100644 src/base.model.js create mode 100644 src/base.model.spec.js create mode 100644 src/store.config.js create mode 100644 src/store.config.spec.js create mode 100644 src/store.service.js create mode 100644 src/store.service.spec.js diff --git a/.gitignore b/.gitignore index d9e8a39..a38b658 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,6 @@ typings Thumbs.db .DS_Store -src/**/*.js *.map *.d.ts -.idea \ No newline at end of file +.idea diff --git a/package.json b/package.json index ef1f299..eb8e490 100644 --- a/package.json +++ b/package.json @@ -1,57 +1,57 @@ { - "name": "ng2-data", - "version": "0.0.25", - "description": "Convential HTTP client for Model Data in Angular 2", - "repository": { - "type": "git", - "url": "git+https://github.com/raghunat/ng2-data.git" - }, - "main": "src/store.service", - "scripts": { - "start": "concurrently \"npm run tsc:w\" \"npm run lite\" ", - "tsc": "tsc", - "tsc:w": "tsc -w", - "lite": "lite-server", - "typings": "typings", - "postinstall": "typings install", - "test": "./node_modules/karma/bin/karma start" - }, - "keywords": [ - "angular", - "angular2", - "data", - "orm", - "http" - ], - "author": "raghunat", - "license": "MIT", - "bugs": { - "url": "https://github.com/raghunat/ng2-data/issues" - }, - "typings": "./ng2-data.d.ts", - "homepage": "https://github.com/raghunat/ng2-data#readme", - "dependencies": { - "angular2": "2.0.0-beta.9", - "es6-promise": "^3.0.2", - "es6-shim": "^0.33.3", - "reflect-metadata": "0.1.2", - "rxjs": "5.0.0-beta.2", - "zone.js": "0.5.15" - }, - "devDependencies": { - "body-parser": "^1.15.0", - "concurrently": "^2.0.0", - "cors": "^2.7.1", - "express": "^4.13.4", - "glob": "^7.0.3", - "jasmine-core": "^2.4.1", - "jasmine-spec-reporter": "^2.4.0", - "karma": "^0.13.22", - "karma-chrome-launcher": "^0.2.2", - "karma-jasmine": "^0.3.7", - "karma-mocha-reporter": "^2.0.0", - "lite-server": "^2.1.0", - "systemjs": "~0.19.24", - "typescript": "~1.7.5" - } + "name": "ng2-data", + "version": "0.0.26", + "description": "Convential HTTP client for Model Data in Angular 2", + "repository": { + "type": "git", + "url": "git+https://github.com/raghunat/ng2-data.git" + }, + "main": "src/store.service", + "scripts": { + "start": "concurrently \"npm run tsc:w\" \"npm run lite\" ", + "tsc": "tsc", + "tsc:w": "tsc -w", + "lite": "lite-server", + "typings": "typings", + "postinstall": "typings install", + "test": "./node_modules/karma/bin/karma start" + }, + "keywords": [ + "angular", + "angular2", + "data", + "orm", + "http" + ], + "author": "raghunat", + "license": "MIT", + "bugs": { + "url": "https://github.com/raghunat/ng2-data/issues" + }, + "typings": "./ng2-data.d.ts", + "homepage": "https://github.com/raghunat/ng2-data#readme", + "dependencies": { + "angular2": "2.0.0-beta.9", + "es6-promise": "^3.0.2", + "es6-shim": "^0.33.3", + "reflect-metadata": "0.1.2", + "rxjs": "5.0.0-beta.2", + "zone.js": "0.5.15" + }, + "devDependencies": { + "body-parser": "^1.15.0", + "concurrently": "^2.0.0", + "cors": "^2.7.1", + "express": "^4.13.4", + "glob": "^7.0.3", + "jasmine-core": "^2.4.1", + "jasmine-spec-reporter": "^2.4.0", + "karma": "^0.13.22", + "karma-chrome-launcher": "^0.2.2", + "karma-jasmine": "^0.3.7", + "karma-mocha-reporter": "^2.0.0", + "lite-server": "^2.1.0", + "systemjs": "~0.19.24", + "typescript": "~1.7.5" + } } diff --git a/src/base.model.js b/src/base.model.js new file mode 100644 index 0000000..53032e8 --- /dev/null +++ b/src/base.model.js @@ -0,0 +1,44 @@ +System.register([], function(exports_1) { + var BaseModel; + return { + setters:[], + execute: function() { + BaseModel = (function () { + function BaseModel(params, store) { + if (params === void 0) { params = {}; } + this.store = store; + Object.assign(this, params); + } + BaseModel.prototype.stripForRequest = function () { + var _this = this; + var obj = {}; + Object.keys(this).forEach(function (k) { + switch (k) { + case '_model': + case 'store': + return; + default: + obj[k] = _this[k]; + return; + } + }); + return obj; + }; + BaseModel.prototype.save = function () { + if (this.id !== undefined) { + return this.store.update(this._model, this.id, this.stripForRequest()); + } + else { + return this.store.create(this._model, this.stripForRequest()); + } + }; + BaseModel.prototype.destroy = function () { + return this.store.destroy(this._model, this.id); + }; + return BaseModel; + })(); + exports_1("BaseModel", BaseModel); + } + } +}); +//# sourceMappingURL=base.model.js.map \ No newline at end of file diff --git a/src/base.model.spec.js b/src/base.model.spec.js new file mode 100644 index 0000000..8b95ed2 --- /dev/null +++ b/src/base.model.spec.js @@ -0,0 +1,87 @@ +System.register(['angular2/testing', 'angular2/core', 'angular2/http', 'angular2/http/testing', './store.service', './base.model'], function(exports_1) { + var testing_1, core_1, http_1, testing_2, store_service_1, base_model_1; + var MockStore; + return { + setters:[ + function (testing_1_1) { + testing_1 = testing_1_1; + }, + function (core_1_1) { + core_1 = core_1_1; + }, + function (http_1_1) { + http_1 = http_1_1; + }, + function (testing_2_1) { + testing_2 = testing_2_1; + }, + function (store_service_1_1) { + store_service_1 = store_service_1_1; + }, + function (base_model_1_1) { + base_model_1 = base_model_1_1; + }], + execute: function() { + MockStore = (function () { + function MockStore() { + } + MockStore.prototype.destroy = function () { + return { + subscribe: function (cb) { + return cb('Response'); + } + }; + }; + MockStore.prototype.create = function () { + return { + subscribe: function (cb) { + return cb({ + id: 1 + }); + } + }; + }; + MockStore.prototype.update = function () { + return { + subscribe: function (cb) { + return cb({ + id: 1 + }); + } + }; + }; + return MockStore; + })(); + testing_1.describe('Base Model', function () { + testing_1.beforeEachProviders(function () { return [core_1.provide(store_service_1.StoreService, { useClass: MockStore }), http_1.HTTP_PROVIDERS, core_1.provide(http_1.XHRBackend, { useClass: testing_2.MockBackend })]; }); + testing_1.it('should delete a user', testing_1.inject([http_1.XHRBackend, store_service_1.StoreService], function (mockBackend, store) { + var user = new base_model_1.BaseModel({ + _model: 'user', + id: 1 + }, store); + user.destroy().subscribe(function (res) { + testing_1.expect(res).toBeDefined(); + }); + })); + testing_1.it('should save a new user', testing_1.inject([http_1.XHRBackend, store_service_1.StoreService], function (mockBackend, store) { + var user = new base_model_1.BaseModel({ + _model: 'user', + }, store); + user.save().subscribe(function (user) { + testing_1.expect(user.id).toBe(1); + }); + })); + testing_1.it('should save an existing user', testing_1.inject([http_1.XHRBackend, store_service_1.StoreService], function (mockBackend, store) { + var user = new base_model_1.BaseModel({ + _model: 'user', + id: 1 + }, store); + user.save().subscribe(function (user) { + testing_1.expect(user.id).toBe(1); + }); + })); + }); + } + } +}); +//# sourceMappingURL=base.model.spec.js.map \ No newline at end of file diff --git a/src/store.config.js b/src/store.config.js new file mode 100644 index 0000000..3650786 --- /dev/null +++ b/src/store.config.js @@ -0,0 +1,24 @@ +System.register([], function(exports_1) { + var StoreConfig; + return { + setters:[], + execute: function() { + /** + * Input Class + */ + StoreConfig = (function () { + function StoreConfig(config) { + if (config === void 0) { config = {}; } + config = config || {}; + // TODO + // Sanitze inputs + // bulk assign + Object.assign(this, config); + } + return StoreConfig; + })(); + exports_1("StoreConfig", StoreConfig); + } + } +}); +//# sourceMappingURL=store.config.js.map \ No newline at end of file diff --git a/src/store.config.spec.js b/src/store.config.spec.js new file mode 100644 index 0000000..50911be --- /dev/null +++ b/src/store.config.spec.js @@ -0,0 +1,22 @@ +System.register(['angular2/testing', './store.config'], function(exports_1) { + var testing_1, store_config_1; + return { + setters:[ + function (testing_1_1) { + testing_1 = testing_1_1; + }, + function (store_config_1_1) { + store_config_1 = store_config_1_1; + }], + execute: function() { + testing_1.describe('StoreConfig', function () { + testing_1.it('should be a basic config class', testing_1.inject([], function () { + testing_1.expect(store_config_1.StoreConfig).toBeDefined(); + var config = new store_config_1.StoreConfig({ baseUri: 'someuri' }); + testing_1.expect(config.baseUri).toBe('someuri'); + })); + }); + } + } +}); +//# sourceMappingURL=store.config.spec.js.map \ No newline at end of file diff --git a/src/store.service.js b/src/store.service.js new file mode 100644 index 0000000..43bda3d --- /dev/null +++ b/src/store.service.js @@ -0,0 +1,188 @@ +System.register(['angular2/core', 'angular2/http', 'rxjs/Rx', './base.model'], function(exports_1) { + var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + var core_1, http_1, base_model_1; + var instance, StoreService; + return { + setters:[ + function (core_1_1) { + core_1 = core_1_1; + }, + function (http_1_1) { + http_1 = http_1_1; + }, + function (_1) {}, + function (base_model_1_1) { + base_model_1 = base_model_1_1; + }], + execute: function() { + instance = null; + StoreService = (function () { + function StoreService(http) { + this.http = http; + } + StoreService.prototype.init = function (config) { + StoreService.config = config; + }; + StoreService.prototype.log = function (msg, data) { + if (window['NG2_DATA_LOG']) { + console.log('[ng2-data]', msg, data); + } + }; + StoreService.prototype.generateRequestOptions = function (url, method, queryParameters, body) { + this.log('Generating headers with', arguments); + var options = new http_1.RequestOptions(); + if (StoreService.customGenerateOptions) { + var createdHeaders = StoreService.customGenerateOptions(url, method, queryParameters, body); + var newHeaders = new http_1.Headers(); + Object.keys(createdHeaders).forEach(function (k) { + newHeaders.set(k, createdHeaders[k]); + }); + options.headers = newHeaders; + } + this.log('Generated The Following headers', options); + return options; + }; + StoreService.prototype.generateRequestQuery = function (url, method, queryParameters, body) { + this.log('Generating query parameters with', arguments); + var query = {}; + if (StoreService.customGenerateQuery) { + Object.assign(query, StoreService.customGenerateQuery(url, method, queryParameters, body)); + } + else { + Object.assign(query, queryParameters); + } + this.log('Generated The following query parameters', query); + return query; + }; + StoreService.prototype.extendHeaders = function (fn) { + StoreService.customGenerateOptions = fn; + }; + StoreService.prototype.extendQueryParameters = function (fn) { + StoreService.customGenerateQuery = fn; + }; + StoreService.prototype.simplePluralize = function (noun) { + switch (noun[noun.length - 1]) { + case 's': + case 'x': + return noun + 'es'; + case 'y': + switch (noun[noun.length - 2]) { + case 'a': + case 'e': + case 'i': + case 'o': + case 'u': + return noun + 's'; + default: + return noun.substring(0, noun.length - 1) + 'ies'; + } + default: + return noun + 's'; + } + }; + StoreService.prototype.modelize = function (model) { + var _this = this; + return function (m) { + m._model = model; + return new base_model_1.BaseModel(m, _this); + }; + }; + StoreService.prototype.buildUri = function (model) { + return StoreService.config.baseUri + "/" + this.simplePluralize(model); + }; + StoreService.prototype.makeRequest = function (method, uri, params, body) { + var _this = this; + if (body === void 0) { body = null; } + // build query string + var queryParams = new http_1.URLSearchParams(); + // build query + params = this.generateRequestQuery(uri, method, params, body); + // url-ify + Object.keys(params).forEach(function (k) { + _this.log(k, {}); + queryParams.set(k, (typeof params[k] !== 'string' ? JSON.stringify(params[k]) : params[k]).replace(/\{/g, '%7B').replace(/\}/g, '%7D')); + }); + // Create request options + var options = this.generateRequestOptions(uri, method, params, body); + // Add query string + options.search = queryParams; + this.log('Performing request with the following options', options); + // Switch api based on method + switch (method) { + case 'get': + case 'delete': + return this.http[method](uri, options); + default: + return this.http[method](uri, JSON.stringify(body), options); + } + }; + StoreService.prototype.rawRequest = function (method, route, params, body) { + return this.http[method](StoreService.config.baseUri + "/" + route, params, body); + }; + /** + * GET /model + */ + StoreService.prototype.find = function (model, params) { + var _this = this; + if (params === void 0) { params = {}; } + return this.makeRequest('get', this.buildUri(model), params).map(function (r) { return r.json()[_this.simplePluralize(model)]; }).map(function (array) { + var results = []; + array.forEach(function (i) { + i._model = model; + results.push(new base_model_1.BaseModel(i, _this)); + }); + return results; + }); + }; + /** + * GET /model/:id + */ + StoreService.prototype.findOne = function (model, id, params) { + if (params === void 0) { params = {}; } + return this.makeRequest('get', this.buildUri(model) + "/" + id, params).map(function (r) { return r.json()[model]; }).map(this.modelize(model)); + }; + /** + * POST /model + */ + StoreService.prototype.create = function (model, body, params) { + if (params === void 0) { params = {}; } + var data = {}; + data[model] = body; + return this.makeRequest('post', this.buildUri(model), params, data).map(function (r) { return r.json()[model]; }).map(this.modelize(model)); + }; + /** + * PUT /model/:id + */ + StoreService.prototype.update = function (model, id, body, params) { + if (params === void 0) { params = {}; } + var data = {}; + data[model] = body; + return this.makeRequest('put', this.buildUri(model) + "/" + id, params, data).map(function (r) { return r.json()[model]; }).map(this.modelize(model)); + }; + /** + * DELETE /model/:id + */ + StoreService.prototype.destroy = function (model, id, body, params) { + if (body === void 0) { body = null; } + if (params === void 0) { params = {}; } + return this.makeRequest('delete', this.buildUri(model) + "/" + id, params, body).map(function (r) { return r.json(); }); + }; + StoreService = __decorate([ + core_1.Injectable(), + __metadata('design:paramtypes', [http_1.Http]) + ], StoreService); + return StoreService; + })(); + exports_1("StoreService", StoreService); + } + } +}); +//# sourceMappingURL=store.service.js.map \ No newline at end of file diff --git a/src/store.service.spec.js b/src/store.service.spec.js new file mode 100644 index 0000000..c81457b --- /dev/null +++ b/src/store.service.spec.js @@ -0,0 +1,137 @@ +System.register(['angular2/testing', 'angular2/core', './store.service', './store.config', 'angular2/http', 'angular2/http/testing'], function(exports_1) { + var testing_1, core_1, store_service_1, store_config_1, http_1, testing_2, http_2; + return { + setters:[ + function (testing_1_1) { + testing_1 = testing_1_1; + }, + function (core_1_1) { + core_1 = core_1_1; + }, + function (store_service_1_1) { + store_service_1 = store_service_1_1; + }, + function (store_config_1_1) { + store_config_1 = store_config_1_1; + }, + function (http_1_1) { + http_1 = http_1_1; + http_2 = http_1_1; + }, + function (testing_2_1) { + testing_2 = testing_2_1; + }], + execute: function() { + testing_1.describe('StoreService Service', function () { + testing_1.beforeEachProviders(function () { return [store_service_1.StoreService, http_1.HTTP_PROVIDERS, core_1.provide(http_1.XHRBackend, { useClass: testing_2.MockBackend })]; }); + testing_1.it('should find users', testing_1.inject([http_1.XHRBackend, store_service_1.StoreService], function (mockBackend, store) { + // prep + mockBackend.connections.subscribe(function (connection) { + connection.mockRespond(new http_1.Response(new http_2.ResponseOptions({ + body: { + users: [{ + id: 1, + name: 'stephen' + }] + } + }))); + }); + // test + store.init(new store_config_1.StoreConfig({ baseUri: 'http://localhost' })); + store.find('user').subscribe(function (users) { + testing_1.expect(users[0].id).toBe(1); + testing_1.expect(users[0].name).toBe('stephen'); + }); + })); + testing_1.it('should find a specific user', testing_1.inject([http_1.XHRBackend, store_service_1.StoreService], function (mockBackend, store) { + // prep + mockBackend.connections.subscribe(function (connection) { + connection.mockRespond(new http_1.Response(new http_2.ResponseOptions({ + body: { + user: { + id: 1, + name: 'stephen' + } + } + }))); + }); + // test + store.init(new store_config_1.StoreConfig({ baseUri: 'http://localhost' })); + store.findOne('user', 1).subscribe(function (user) { + testing_1.expect(user.id).toBe(1); + testing_1.expect(user.name).toBe('stephen'); + }); + })); + testing_1.it('should create a user', testing_1.inject([http_1.XHRBackend, store_service_1.StoreService], function (mockBackend, store) { + // prep + mockBackend.connections.subscribe(function (connection) { + connection.mockRespond(new http_1.Response(new http_2.ResponseOptions({ + body: { + user: { + id: 1, + name: 'stephen' + } + } + }))); + }); + // test + store.init(new store_config_1.StoreConfig({ baseUri: 'http://localhost' })); + store.create('user', { name: 'stephen' }).subscribe(function (user) { + testing_1.expect(user.id).toBe(1); + testing_1.expect(user.name).toBe('stephen'); + }); + })); + testing_1.it('should update a user', testing_1.inject([http_1.XHRBackend, store_service_1.StoreService], function (mockBackend, store) { + // prep + mockBackend.connections.subscribe(function (connection) { + connection.mockRespond(new http_1.Response(new http_2.ResponseOptions({ + body: { + user: { + id: 1, + name: 'stephenA' + } + } + }))); + }); + // test + store.init(new store_config_1.StoreConfig({ baseUri: 'http://localhost' })); + store.update('user', 1, { name: 'stephen' }).subscribe(function (user) { + testing_1.expect(user.id).toBe(1); + testing_1.expect(user.name).toBe('stephenA'); + }); + })); + testing_1.it('should destroy a user', testing_1.inject([http_1.XHRBackend, store_service_1.StoreService], function (mockBackend, store) { + // prep + mockBackend.connections.subscribe(function (connection) { + connection.mockRespond(new http_1.Response(new http_2.ResponseOptions({ + body: { + statusCode: 200 + } + }))); + }); + // test + store.init(new store_config_1.StoreConfig({ baseUri: 'http://localhost' })); + store.destroy('user', 1).subscribe(function (res) { + testing_1.expect(res.statusCode).toBe(200); + }); + })); + testing_1.it('can pluralize nouns', testing_1.inject([http_1.XHRBackend, store_service_1.StoreService], function (mockBackend, store) { + var dictionary = { + 'product': 'products', + 'sequence': 'sequences', + 'repo': 'repos', + 'testsuite': 'testsuites', + 'bug': 'bugs', + 'company': 'companies', + 'fix': 'fixes' + }; + var singulars = Object.keys(dictionary); + var expected = Object.keys(dictionary).map(function (key) { return dictionary[key]; }); + var actuals = singulars.map(store.simplePluralize); + testing_1.expect(expected).toEqual(actuals); + })); + }); + } + } +}); +//# sourceMappingURL=store.service.spec.js.map \ No newline at end of file