From dfc0dfab3a0a29252b1dd97a3daa19862915cbf9 Mon Sep 17 00:00:00 2001 From: Christof Hullaert Date: Thu, 7 Dec 2017 23:27:02 +0100 Subject: [PATCH 1/6] choose of id property on item by defineId definition --- .../hero-in-mem-data-override-id.service.ts | 73 ++++++ src/in-mem/backend.service.ts | 109 ++++++--- src/in-mem/http-backend.service.spec.ts | 224 +++++++++++------- 3 files changed, 277 insertions(+), 129 deletions(-) create mode 100644 src/app/hero-in-mem-data-override-id.service.ts diff --git a/src/app/hero-in-mem-data-override-id.service.ts b/src/app/hero-in-mem-data-override-id.service.ts new file mode 100644 index 0000000..88cfd95 --- /dev/null +++ b/src/app/hero-in-mem-data-override-id.service.ts @@ -0,0 +1,73 @@ +/** + * This is an example of a Hero-oriented InMemoryDbService. + * + * For demonstration purposes, it can return the database + * synchronously as an object (default), + * as an observable, or as a promise. + * + * Add the following line to `AppModule.imports` + * InMemoryWebApiModule.forRoot(HeroInMemDataService) // or HeroInMemDataOverrideService + */ +import { Injectable } from '@angular/core'; +import { InMemoryDbService, RequestInfo } from '../in-mem/interfaces'; + +// tslint:disable:no-unused-variable +import { Observable } from 'rxjs/Observable'; +import { of } from 'rxjs/observable/of'; +import 'rxjs/add/operator/delay'; +// tslint:enable:no-unused-variable + +@Injectable() +export class HeroInMemDataOverrideIdService implements InMemoryDbService { + defineId(collectionName: string) { + return 'uuid'; + } + createDb(reqInfo?: RequestInfo) { + + const heroes = [ + { uuid: '00000000-0000-0000-0000-000000000001', name: 'Windstorm' }, + { uuid: '00000000-0000-0000-0000-000000000002', name: 'Bombasto' }, + { uuid: '00000000-0000-0000-0000-000000000003', name: 'Magneta' }, + { uuid: '00000000-0000-0000-0000-000000000004', name: 'Tornado' } + ]; + + const nobodies: any[] = []; + + // entities with string ids that look like numbers + const stringers = [ + { uuid: '00000000-0000-0000-0000-000000000010', name: 'Bob String' }, + { uuid: '00000000-0000-0000-0000-000000000020', name: 'Jill String' } + ]; + + // default returnType + let returnType = 'object'; + // let returnType = 'observable'; + // let returnType = 'promise'; + + // demonstrate POST commands/resetDb + // this example clears the collections if the request body tells it to do so + if (reqInfo) { + const body = reqInfo.utils.getJsonBody(reqInfo.req) || {}; + if (body.clear === true) { + heroes.length = 0; + nobodies.length = 0; + stringers.length = 0; + } + + // 'returnType` can be 'object' | 'observable' | 'promise' + returnType = body.returnType || 'object'; + } + const db = { heroes, nobodies, stringers }; + + switch (returnType) { + case ('observable'): + return of(db).delay(10); + case ('promise'): + return new Promise(resolve => { + setTimeout(() => resolve(db), 10); + }); + default: + return db; + } + } +} diff --git a/src/in-mem/backend.service.ts b/src/in-mem/backend.service.ts index 2d24a50..5831911 100644 --- a/src/in-mem/backend.service.ts +++ b/src/in-mem/backend.service.ts @@ -28,6 +28,10 @@ import { UriInfo } from './interfaces'; +export interface IDictionary { + [index: string]: string; +} + /** * Base class for in-memory web api back-ends * Simulate the behavior of a RESTy web api @@ -36,6 +40,7 @@ import { * http://www.restapitutorial.com/lessons/httpmethods.html */ export abstract class BackendService { + protected fieldIds = {} as IDictionary; protected config: InMemoryBackendConfigArgs = new InMemoryBackendConfig(); protected db: Object; protected dbReadySubject: BehaviorSubject; @@ -211,28 +216,28 @@ export abstract class BackendService { protected collectionHandler(reqInfo: RequestInfo): ResponseOptions { // const req = reqInfo.req; - let resOptions: ResponseOptions; - switch (reqInfo.method) { - case 'get': - resOptions = this.get(reqInfo); - break; - case 'post': - resOptions = this.post(reqInfo); - break; - case 'put': - resOptions = this.put(reqInfo); - break; - case 'delete': - resOptions = this.delete(reqInfo); - break; - default: - resOptions = this.createErrorResponseOptions(reqInfo.url, STATUS.METHOD_NOT_ALLOWED, 'Method not allowed'); - break; - } + let resOptions: ResponseOptions; + switch (reqInfo.method) { + case 'get': + resOptions = this.get(reqInfo); + break; + case 'post': + resOptions = this.post(reqInfo); + break; + case 'put': + resOptions = this.put(reqInfo); + break; + case 'delete': + resOptions = this.delete(reqInfo); + break; + default: + resOptions = this.createErrorResponseOptions(reqInfo.url, STATUS.METHOD_NOT_ALLOWED, 'Method not allowed'); + break; + } - // If `inMemDbService.responseInterceptor` exists, let it morph the response options - const interceptor = this.bind('responseInterceptor'); - return interceptor ? interceptor(resOptions, reqInfo) : resOptions; + // If `inMemDbService.responseInterceptor` exists, let it morph the response options + const interceptor = this.bind('responseInterceptor'); + return interceptor ? interceptor(resOptions, reqInfo) : resOptions; } /** @@ -272,7 +277,7 @@ export abstract class BackendService { resOptions.status = STATUS.OK; resOptions.body = this.clone(this.config); - // any other HTTP method is assumed to be a config update + // any other HTTP method is assumed to be a config update } else { const body = this.getJsonBody(reqInfo.req); Object.assign(this.config, body); @@ -381,7 +386,32 @@ export abstract class BackendService { * @param id */ protected findById(collection: T[], id: any): T { - return collection.find((item: T) => item.id === id); + return collection.find((item: T) => this.getItemId(item) === id); + } + + /** + * define for your in-mem database what is the name of your id field + * return the name of the id field + */ + protected defineId(collectionName = 'default_id'): string { + const defineId = this.bind('defineId'); + if (defineId) { + const id = defineId(collectionName); + if ((id !== undefined) && id !== '') { return id; } + } + return 'id'; + } + + protected getItemId(item: T, collectionName = 'default_id'): any { + return item[this.fieldIds[collectionName]]; + } + + protected setItemId(item: T, id: any, collectionName = 'default_id'): any { + return item[this.fieldIds[collectionName]] = id; + } + + protected setFieldId(id: any, collectionName = 'default_id') { + this.fieldIds[collectionName] = this.defineId(collectionName); } /** @@ -414,7 +444,7 @@ export abstract class BackendService { let maxId = 0; collection.reduce((prev: any, item: any) => { - maxId = Math.max(maxId, typeof item.id === 'number' ? item.id : maxId); + maxId = Math.max(maxId, typeof this.getItemId(item) === 'number' ? this.getItemId(item) : maxId); }, undefined); return maxId + 1; } @@ -493,7 +523,7 @@ export abstract class BackendService { protected abstract getRequestMethod(req: any): string; protected indexOf(collection: any[], id: number) { - return collection.findIndex((item: any) => item.id === id); + return collection.findIndex((item: any) => this.getItemId(item) === id); } /** Parse the id as a number. Return original value if not a number. */ @@ -588,9 +618,9 @@ export abstract class BackendService { const item = this.getJsonBody(req); // tslint:disable-next-line:triple-equals - if (item.id == undefined) { + if (this.getItemId(item) == undefined) { try { - item.id = id || this.genId(collection, collectionName); + this.setItemId(item, id || this.genId(collection, collectionName)); } catch (err) { const emsg: string = err.message || ''; if (/id type is non-numeric/.test(emsg)) { @@ -603,10 +633,10 @@ export abstract class BackendService { } } - if (id && id !== item.id) { + if (id && id !== this.getItemId(item)) { return this.createErrorResponseOptions(url, STATUS.BAD_REQUEST, `Request id does not match item.id`); } else { - id = item.id; + id = this.getItemId(item); } const existingIx = this.indexOf(collection, id); const body = this.bodify(item); @@ -621,8 +651,8 @@ export abstract class BackendService { } else { collection[existingIx] = item; return this.config.post204 ? - { headers, status: STATUS.NO_CONTENT } : // successful; no content - { headers, body, status: STATUS.OK }; // successful; return entity + { headers, status: STATUS.NO_CONTENT } : // successful; no content + { headers, body, status: STATUS.OK }; // successful; return entity } } @@ -631,14 +661,14 @@ export abstract class BackendService { protected put({ collection, collectionName, headers, id, req, url }: RequestInfo): ResponseOptions { const item = this.getJsonBody(req); // tslint:disable-next-line:triple-equals - if (item.id == undefined) { + if (this.getItemId(item) == undefined) { return this.createErrorResponseOptions(url, STATUS.NOT_FOUND, `Missing '${collectionName}' id`); } - if (id && id !== item.id) { + if (id && id !== this.getItemId(item)) { return this.createErrorResponseOptions(url, STATUS.BAD_REQUEST, `Request for '${collectionName}' id does not match item.id`); } else { - id = item.id; + id = this.getItemId(item); } const existingIx = this.indexOf(collection, id); const body = this.bodify(item); @@ -646,8 +676,8 @@ export abstract class BackendService { if (existingIx > -1) { collection[existingIx] = item; return this.config.put204 ? - { headers, status: STATUS.NO_CONTENT } : // successful; no content - { headers, body, status: STATUS.OK }; // successful; return entity + { headers, status: STATUS.NO_CONTENT } : // successful; no content + { headers, body, status: STATUS.OK }; // successful; return entity } else if (this.config.put404) { // item to update not found; use POST to create new item for this id. return this.createErrorResponseOptions(url, STATUS.NOT_FOUND, @@ -676,12 +706,15 @@ export abstract class BackendService { this.dbReadySubject.next(false); const db = this.inMemDbService.createDb(reqInfo); const db$ = db instanceof Observable ? db : - isPromise(db) ? fromPromise(db) : - of(db); + isPromise(db) ? fromPromise(db) : + of(db); first.call(db$).subscribe((d: {}) => { this.db = d; this.dbReadySubject.next(true); }); + + this.setFieldId(this.defineId()); + return this.dbReady; } diff --git a/src/in-mem/http-backend.service.spec.ts b/src/in-mem/http-backend.service.spec.ts index b07e740..7265167 100644 --- a/src/in-mem/http-backend.service.spec.ts +++ b/src/in-mem/http-backend.service.spec.ts @@ -18,6 +18,7 @@ import { HttpHeroService } from '../app/http-hero.service'; import { HeroInMemDataService } from '../app/hero-in-mem-data.service'; import { HeroInMemDataOverrideService } from '../app/hero-in-mem-data-override.service'; +import { HeroInMemDataOverrideIdService } from '../app/hero-in-mem-data-override-id.service'; import { HeroServiceCoreSpec } from '../app/hero.service.spec'; class Nobody { id: string; name: string; } @@ -43,14 +44,14 @@ describe('Http Backend Service', () => { it('can get heroes', async(() => { http.get('api/heroes') - .map(res => res.json() as Hero[]) - .subscribe( + .map(res => res.json() as Hero[]) + .subscribe( heroes => { // console.log(heroes); expect(heroes.length).toBeGreaterThan(0, 'should have heroes'); }, failure - ); + ); })); it('GET should be a "cold" observable', async(() => { @@ -64,31 +65,31 @@ describe('Http Backend Service', () => { expect(spy).not.toHaveBeenCalled(); get$.map(res => res.json() as Hero[]) - .subscribe( + .subscribe( heroes => { expect(spy).toHaveBeenCalled(); expect(heroes.length).toBeGreaterThan(0, 'should have heroes'); }, failure - ); + ); })); it('can get heroes (w/ a different base path)', async(() => { http.get('some-base-path/heroes') - .map(res => res.json() as Hero[]) - .subscribe( + .map(res => res.json() as Hero[]) + .subscribe( heroes => { // console.log(heroes); expect(heroes.length).toBeGreaterThan(0, 'should have heroes'); }, failure - ); + ); })); it('should 404 when GET unknown collection', async(() => { const url = 'api/unknown-collection'; http.get(url) - .subscribe( + .subscribe( _ => { console.log(_); fail(`should not have found data for '${url}'`); @@ -96,69 +97,69 @@ describe('Http Backend Service', () => { err => { expect(err.status).toBe(404, 'should have 404 status'); } - ); + ); })); it('should return the hero w/id=1 for GET app/heroes/1', async(() => { http.get('api/heroes/1') - .map(res => res.json() as Hero) - .subscribe( + .map(res => res.json() as Hero) + .subscribe( hero => { expect(hero).toBeDefined('should find hero with id=1'); }, failure - ); + ); })); // test where id is string that looks like a number it('should return the stringer w/id="10" for GET app/stringers/10', async(() => { http.get('api/stringers/10') - .map(res => res.json() as { id: string, name: string }) - .subscribe( + .map(res => res.json() as { id: string, name: string }) + .subscribe( hero => { expect(hero).toBeDefined('should find string with id="10"'); }, failure - ); + ); })); it('should return 1-item array for GET app/heroes/?id=1', async(() => { http.get('api/heroes/?id=1') - .map(res => res.json() as Hero[]) - .subscribe( + .map(res => res.json() as Hero[]) + .subscribe( heroes => { expect(heroes.length).toBe(1, 'should find one hero w/id=1'); }, failure - ); + ); })); it('should return 1-item array for GET app/heroes?id=1', async(() => { http.get('api/heroes?id=1') - .map(res => res.json() as Hero[]) - .subscribe( + .map(res => res.json() as Hero[]) + .subscribe( heroes => { expect(heroes.length).toBe(1, 'should find one hero w/id=1'); }, failure - ); + ); })); it('should return undefined for GET app/heroes?id=not-found-id', async(() => { http.get('api/heroes?id=123456') - .map(res => res.json() as Hero[]) - .subscribe( + .map(res => res.json() as Hero[]) + .subscribe( heroes => { expect(heroes.length).toBe(0); }, failure - ); + ); })); it('should return 404 for GET app/heroes/not-found-id', async(() => { const url = 'api/heroes/123456'; http.get(url) - .subscribe( + .subscribe( _ => { console.log(_); fail(`should not have found data for '${url}'`); @@ -166,18 +167,18 @@ describe('Http Backend Service', () => { err => { expect(err.status).toBe(404, 'should have 404 status'); } - ); + ); })); it('can get nobodies (empty collection)', async(() => { http.get('api/nobodies') - .map(res => res.json()) - .subscribe( + .map(res => res.json()) + .subscribe( nobodies => { expect(nobodies.length).toBe(0, 'should have no nobodies'); }, failure - ); + ); })); it('can add a nobody with an id to empty nobodies collection', async(() => { @@ -188,12 +189,12 @@ describe('Http Backend Service', () => { .concatMap(() => http.get('api/nobodies')) .map(res => res.json()) .subscribe( - nobodies => { - expect(nobodies.length).toBe(1, 'should a nobody'); - expect(nobodies[0].name).toBe('Noman', 'should be "Noman"'); - expect(nobodies[0].id).toBe(id, 'should preserve the submitted, ' + id); - }, - failure + nobodies => { + expect(nobodies.length).toBe(1, 'should a nobody'); + expect(nobodies[0].name).toBe('Noman', 'should be "Noman"'); + expect(nobodies[0].id).toBe(id, 'should preserve the submitted, ' + id); + }, + failure ); })); @@ -230,18 +231,18 @@ describe('Http Backend Service', () => { // Add a nobody so that we have one http.post('api/nobodies', { id: 42, name: 'Noman' }) - // Reset database with "clear" option - .concatMap(() => http.post('commands/resetDb', { clear: true })) - // get the number of heroes and nobodies - .concatMap(() => sizes$) - .subscribe( + // Reset database with "clear" option + .concatMap(() => http.post('commands/resetDb', { clear: true })) + // get the number of heroes and nobodies + .concatMap(() => sizes$) + .subscribe( sizes => { expect(sizes.heroes).toBe(0, 'reset should have cleared the heroes'); expect(sizes.nobodies).toBe(0, 'reset should have cleared the nobodies'); expect(sizes.stringers).toBe(0, 'reset should have cleared the stringers'); }, failure - ); + ); } }); @@ -264,44 +265,44 @@ describe('Http Backend Service', () => { it('can get heroes', async(() => { http.get('api/heroes') - .map(res => res.json() as Hero[]) - .subscribe( + .map(res => res.json() as Hero[]) + .subscribe( heroes => { // console.log(heroes); expect(heroes.length).toBeGreaterThan(0, 'should have heroes'); }, failure - ); + ); })); it('can translate `foo/heroes` to `heroes` via `parsedRequestUrl` override', async(() => { http.get('api/foo/heroes') - .map(res => res.json() as Hero[]) - .subscribe( + .map(res => res.json() as Hero[]) + .subscribe( heroes => { // console.log(heroes); expect(heroes.length).toBeGreaterThan(0, 'should have heroes'); }, failure - ); + ); })); it('can get villains', async(() => { http.get('api/villains') - .map(res => res.json() as Hero[]) - .subscribe( + .map(res => res.json() as Hero[]) + .subscribe( villains => { // console.log(villains); expect(villains.length).toBeGreaterThan(0, 'should have villains'); }, failure - ); + ); })); it('should 404 when POST to villains', async(() => { const url = 'api/villains'; http.post(url, {id: 42, name: 'Dr. Evil'}) - .subscribe( + .subscribe( _ => { console.log(_); fail(`should not have POSTed data for '${url}'`); @@ -309,13 +310,13 @@ describe('Http Backend Service', () => { err => { expect(err.status).toBe(404, 'should have 404 status'); } - ); + ); })); it('should 404 when GET unknown collection', async(() => { const url = 'api/unknown-collection'; http.get(url) - .subscribe( + .subscribe( _ => { console.log(_); fail(`should not have found data for '${url}'`); @@ -323,21 +324,21 @@ describe('Http Backend Service', () => { err => { expect(err.status).toBe(404, 'should have 404 status'); } - ); + ); })); it('should use genId override to add new hero, "Maxinius"', async(() => { http.post('api/heroes', { name: 'Maxinius' }) - .concatMap(() => http.get('api/heroes?name=Maxi')) - .map(res => res.json()) - .subscribe( + .concatMap(() => http.get('api/heroes?name=Maxi')) + .map(res => res.json()) + .subscribe( heroes => { expect(heroes.length).toBe(1, 'should have found "Maxinius"'); expect(heroes[0].name).toBe('Maxinius'); expect(heroes[0].id).toBeGreaterThan(1000); }, failure - ); + ); })); it('should use genId override guid generator for a new nobody without an id', async(() => { @@ -345,12 +346,12 @@ describe('Http Backend Service', () => { .concatMap(() => http.get('api/nobodies')) .map(res => res.json()) .subscribe( - nobodies => { - expect(nobodies.length).toBe(1, 'should a nobody'); - expect(nobodies[0].name).toBe('Noman', 'should be "Noman"'); - expect(typeof nobodies[0].id).toBe('string', 'should create a string (guid) id'); - }, - failure + nobodies => { + expect(nobodies.length).toBe(1, 'should a nobody'); + expect(nobodies[0].name).toBe('Noman', 'should be "Noman"'); + expect(typeof nobodies[0].id).toBe('string', 'should create a string (guid) id'); + }, + failure ); })); @@ -376,11 +377,11 @@ describe('Http Backend Service', () => { // Add a nobody so that we have one http.post('api/nobodies', { id: 42, name: 'Noman' }) - // Reset database with "clear" option - .concatMap(() => http.post('commands/resetDb', { clear: true })) - // count all the collections - .concatMap(() => sizes$) - .subscribe( + // Reset database with "clear" option + .concatMap(() => http.post('commands/resetDb', { clear: true })) + // count all the collections + .concatMap(() => sizes$) + .subscribe( sizes => { expect(sizes.heroes).toBe(0, 'reset should have cleared the heroes'); expect(sizes.nobodies).toBe(0, 'reset should have cleared the nobodies'); @@ -388,10 +389,51 @@ describe('Http Backend Service', () => { expect(sizes.villains).toBeGreaterThan(0, 'reset should NOT clear villains'); }, failure - ); + ); }; }); + //////////////// + describe('raw Angular Http w/ override id service', () => { + + let http: Http; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + HttpModule, + HttpInMemoryWebApiModule.forRoot(HeroInMemDataOverrideIdService, { delay }) + ] + }); + + http = TestBed.get(Http); + }); + + it('can get heroes', async(() => { + http.get('api/heroes') + .map(res => res.json() as Hero[]) + .subscribe( + heroes => { + // console.log(heroes); + expect(heroes.length).toBeGreaterThan(0, 'should have heroes'); + }, + failure + ); + })); + + it('can get heroes by uuid', async(() => { + http.get('api/heroes/00000000-0000-0000-0000-000000000002') + .map(res => res.json() as Hero) + .subscribe( + hero => { + // console.log(heroes); + expect(hero.name).toEqual('Bombasto', 'should find hero Bombasto'); + }, + failure + ); + })); + }); + //////////////// describe('Http HeroService', () => { @@ -441,13 +483,13 @@ describe('Http Backend Service', () => { it('can get heroes (no passthru)', async(() => { http.get('api/heroes') - .map(res => res.json() as Hero[]) - .subscribe( + .map(res => res.json() as Hero[]) + .subscribe( heroes => { - expect(createPassThruBackend).not.toHaveBeenCalled(); - expect(heroes.length).toBeGreaterThan(0, 'should have heroes'); - }, - failure + expect(createPassThruBackend).not.toHaveBeenCalled(); + expect(heroes.length).toBeGreaterThan(0, 'should have heroes'); + }, + failure ); })); @@ -465,11 +507,11 @@ describe('Http Backend Service', () => { http.get('api/passthru') .map(res => res.json() as any[]) .subscribe( - passthru => { - console.log('GET passthru data', passthru); - expect(passthru.length).toBeGreaterThan(0, 'should have passthru data'); - }, - failure + passthru => { + console.log('GET passthru data', passthru); + expect(passthru.length).toBeGreaterThan(0, 'should have passthru data'); + }, + failure ); })); @@ -483,12 +525,12 @@ describe('Http Backend Service', () => { http.post('api/passthru', { name: 'Dude' }) .map(res => res.json() as any) .subscribe( - passthru => { - console.log('POST passthru data', passthru); - expect(passthru).toBeDefined('should have passthru data'); - expect(passthru.id).toBe(42, 'passthru object should have id 42'); - }, - failure + passthru => { + console.log('POST passthru data', passthru); + expect(passthru).toBeDefined('should have passthru data'); + expect(passthru.id).toBe(42, 'passthru object should have id 42'); + }, + failure ); })); }); @@ -510,13 +552,13 @@ describe('Http Backend Service', () => { it('can get heroes (encapsulated)', async(() => { http.get('api/heroes') - .map(res => res.json().data as Hero[]) // unwrap data object - .subscribe( + .map(res => res.json().data as Hero[]) // unwrap data object + .subscribe( heroes => { expect(heroes.length).toBeGreaterThan(0, 'should have data.heroes'); }, failure - ); + ); })); }); From ee3cfb7ff64d18fda066f44b48441f0fb0af2533 Mon Sep 17 00:00:00 2001 From: Christof Hullaert Date: Thu, 7 Dec 2017 23:36:01 +0100 Subject: [PATCH 2/6] add comments on method --- src/in-mem/backend.service.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/in-mem/backend.service.ts b/src/in-mem/backend.service.ts index 5831911..9b65d22 100644 --- a/src/in-mem/backend.service.ts +++ b/src/in-mem/backend.service.ts @@ -402,14 +402,30 @@ export abstract class BackendService { return 'id'; } + /** + * return the key (by id property name) of item for a collection name + * @param item + * @param collectionName + */ protected getItemId(item: T, collectionName = 'default_id'): any { return item[this.fieldIds[collectionName]]; } + /** + * set the key (by id property name) of a item for a collection + * @param item + * @param id + * @param collectionName + */ protected setItemId(item: T, id: any, collectionName = 'default_id'): any { return item[this.fieldIds[collectionName]] = id; } + /** + * set the property name of the key of a collection + * @param id + * @param collectionName + */ protected setFieldId(id: any, collectionName = 'default_id') { this.fieldIds[collectionName] = this.defineId(collectionName); } From 5e5f0f698268ce095a24ecd33b26a5f0d1ec4fc5 Mon Sep 17 00:00:00 2001 From: Christof Hullaert Date: Thu, 7 Dec 2017 23:44:59 +0100 Subject: [PATCH 3/6] remove indentation --- src/in-mem/backend.service.ts | 54 +++++++++++++++++------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/in-mem/backend.service.ts b/src/in-mem/backend.service.ts index 9b65d22..310bb2b 100644 --- a/src/in-mem/backend.service.ts +++ b/src/in-mem/backend.service.ts @@ -215,29 +215,29 @@ export abstract class BackendService { } protected collectionHandler(reqInfo: RequestInfo): ResponseOptions { - // const req = reqInfo.req; - let resOptions: ResponseOptions; - switch (reqInfo.method) { - case 'get': - resOptions = this.get(reqInfo); - break; - case 'post': - resOptions = this.post(reqInfo); - break; - case 'put': - resOptions = this.put(reqInfo); - break; - case 'delete': - resOptions = this.delete(reqInfo); - break; - default: - resOptions = this.createErrorResponseOptions(reqInfo.url, STATUS.METHOD_NOT_ALLOWED, 'Method not allowed'); - break; - } + // const req = reqInfo.req; + let resOptions: ResponseOptions; + switch (reqInfo.method) { + case 'get': + resOptions = this.get(reqInfo); + break; + case 'post': + resOptions = this.post(reqInfo); + break; + case 'put': + resOptions = this.put(reqInfo); + break; + case 'delete': + resOptions = this.delete(reqInfo); + break; + default: + resOptions = this.createErrorResponseOptions(reqInfo.url, STATUS.METHOD_NOT_ALLOWED, 'Method not allowed'); + break; + } - // If `inMemDbService.responseInterceptor` exists, let it morph the response options - const interceptor = this.bind('responseInterceptor'); - return interceptor ? interceptor(resOptions, reqInfo) : resOptions; + // If `inMemDbService.responseInterceptor` exists, let it morph the response options + const interceptor = this.bind('responseInterceptor'); + return interceptor ? interceptor(resOptions, reqInfo) : resOptions; } /** @@ -277,7 +277,7 @@ export abstract class BackendService { resOptions.status = STATUS.OK; resOptions.body = this.clone(this.config); - // any other HTTP method is assumed to be a config update + // any other HTTP method is assumed to be a config update } else { const body = this.getJsonBody(reqInfo.req); Object.assign(this.config, body); @@ -692,8 +692,8 @@ export abstract class BackendService { if (existingIx > -1) { collection[existingIx] = item; return this.config.put204 ? - { headers, status: STATUS.NO_CONTENT } : // successful; no content - { headers, body, status: STATUS.OK }; // successful; return entity + { headers, status: STATUS.NO_CONTENT } : // successful; no content + { headers, body, status: STATUS.OK }; // successful; return entity } else if (this.config.put404) { // item to update not found; use POST to create new item for this id. return this.createErrorResponseOptions(url, STATUS.NOT_FOUND, @@ -722,8 +722,8 @@ export abstract class BackendService { this.dbReadySubject.next(false); const db = this.inMemDbService.createDb(reqInfo); const db$ = db instanceof Observable ? db : - isPromise(db) ? fromPromise(db) : - of(db); + isPromise(db) ? fromPromise(db) : + of(db); first.call(db$).subscribe((d: {}) => { this.db = d; this.dbReadySubject.next(true); From 0f67092488340d7252977fc351d5b4d0110052df Mon Sep 17 00:00:00 2001 From: Christof Hullaert Date: Thu, 7 Dec 2017 23:46:42 +0100 Subject: [PATCH 4/6] remove indentation --- src/in-mem/backend.service.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/in-mem/backend.service.ts b/src/in-mem/backend.service.ts index 310bb2b..6803a8a 100644 --- a/src/in-mem/backend.service.ts +++ b/src/in-mem/backend.service.ts @@ -215,7 +215,7 @@ export abstract class BackendService { } protected collectionHandler(reqInfo: RequestInfo): ResponseOptions { - // const req = reqInfo.req; + // const req = reqInfo.req; let resOptions: ResponseOptions; switch (reqInfo.method) { case 'get': @@ -692,8 +692,8 @@ export abstract class BackendService { if (existingIx > -1) { collection[existingIx] = item; return this.config.put204 ? - { headers, status: STATUS.NO_CONTENT } : // successful; no content - { headers, body, status: STATUS.OK }; // successful; return entity + { headers, status: STATUS.NO_CONTENT } : // successful; no content + { headers, body, status: STATUS.OK }; // successful; return entity } else if (this.config.put404) { // item to update not found; use POST to create new item for this id. return this.createErrorResponseOptions(url, STATUS.NOT_FOUND, From f4dde7992c7735dfe8f5f680852420060c7f854b Mon Sep 17 00:00:00 2001 From: Christof Hullaert Date: Thu, 7 Dec 2017 23:54:22 +0100 Subject: [PATCH 5/6] use of old indentation --- src/in-mem/http-backend.service.spec.ts | 248 ++++++++++++------------ 1 file changed, 124 insertions(+), 124 deletions(-) diff --git a/src/in-mem/http-backend.service.spec.ts b/src/in-mem/http-backend.service.spec.ts index 7265167..473c870 100644 --- a/src/in-mem/http-backend.service.spec.ts +++ b/src/in-mem/http-backend.service.spec.ts @@ -44,14 +44,14 @@ describe('Http Backend Service', () => { it('can get heroes', async(() => { http.get('api/heroes') - .map(res => res.json() as Hero[]) - .subscribe( + .map(res => res.json() as Hero[]) + .subscribe( heroes => { // console.log(heroes); expect(heroes.length).toBeGreaterThan(0, 'should have heroes'); }, failure - ); + ); })); it('GET should be a "cold" observable', async(() => { @@ -65,31 +65,31 @@ describe('Http Backend Service', () => { expect(spy).not.toHaveBeenCalled(); get$.map(res => res.json() as Hero[]) - .subscribe( + .subscribe( heroes => { expect(spy).toHaveBeenCalled(); expect(heroes.length).toBeGreaterThan(0, 'should have heroes'); }, failure - ); + ); })); it('can get heroes (w/ a different base path)', async(() => { http.get('some-base-path/heroes') - .map(res => res.json() as Hero[]) - .subscribe( + .map(res => res.json() as Hero[]) + .subscribe( heroes => { // console.log(heroes); expect(heroes.length).toBeGreaterThan(0, 'should have heroes'); }, failure - ); + ); })); it('should 404 when GET unknown collection', async(() => { const url = 'api/unknown-collection'; http.get(url) - .subscribe( + .subscribe( _ => { console.log(_); fail(`should not have found data for '${url}'`); @@ -97,69 +97,69 @@ describe('Http Backend Service', () => { err => { expect(err.status).toBe(404, 'should have 404 status'); } - ); + ); })); it('should return the hero w/id=1 for GET app/heroes/1', async(() => { http.get('api/heroes/1') - .map(res => res.json() as Hero) - .subscribe( + .map(res => res.json() as Hero) + .subscribe( hero => { expect(hero).toBeDefined('should find hero with id=1'); }, failure - ); + ); })); // test where id is string that looks like a number it('should return the stringer w/id="10" for GET app/stringers/10', async(() => { http.get('api/stringers/10') - .map(res => res.json() as { id: string, name: string }) - .subscribe( + .map(res => res.json() as { id: string, name: string }) + .subscribe( hero => { expect(hero).toBeDefined('should find string with id="10"'); }, failure - ); + ); })); it('should return 1-item array for GET app/heroes/?id=1', async(() => { http.get('api/heroes/?id=1') - .map(res => res.json() as Hero[]) - .subscribe( + .map(res => res.json() as Hero[]) + .subscribe( heroes => { expect(heroes.length).toBe(1, 'should find one hero w/id=1'); }, failure - ); + ); })); it('should return 1-item array for GET app/heroes?id=1', async(() => { http.get('api/heroes?id=1') - .map(res => res.json() as Hero[]) - .subscribe( + .map(res => res.json() as Hero[]) + .subscribe( heroes => { expect(heroes.length).toBe(1, 'should find one hero w/id=1'); }, failure - ); + ); })); it('should return undefined for GET app/heroes?id=not-found-id', async(() => { http.get('api/heroes?id=123456') - .map(res => res.json() as Hero[]) - .subscribe( + .map(res => res.json() as Hero[]) + .subscribe( heroes => { expect(heroes.length).toBe(0); }, failure - ); + ); })); it('should return 404 for GET app/heroes/not-found-id', async(() => { const url = 'api/heroes/123456'; http.get(url) - .subscribe( + .subscribe( _ => { console.log(_); fail(`should not have found data for '${url}'`); @@ -167,18 +167,18 @@ describe('Http Backend Service', () => { err => { expect(err.status).toBe(404, 'should have 404 status'); } - ); + ); })); it('can get nobodies (empty collection)', async(() => { http.get('api/nobodies') - .map(res => res.json()) - .subscribe( + .map(res => res.json()) + .subscribe( nobodies => { expect(nobodies.length).toBe(0, 'should have no nobodies'); }, failure - ); + ); })); it('can add a nobody with an id to empty nobodies collection', async(() => { @@ -189,12 +189,12 @@ describe('Http Backend Service', () => { .concatMap(() => http.get('api/nobodies')) .map(res => res.json()) .subscribe( - nobodies => { - expect(nobodies.length).toBe(1, 'should a nobody'); - expect(nobodies[0].name).toBe('Noman', 'should be "Noman"'); - expect(nobodies[0].id).toBe(id, 'should preserve the submitted, ' + id); - }, - failure + nobodies => { + expect(nobodies.length).toBe(1, 'should a nobody'); + expect(nobodies[0].name).toBe('Noman', 'should be "Noman"'); + expect(nobodies[0].id).toBe(id, 'should preserve the submitted, ' + id); + }, + failure ); })); @@ -231,18 +231,18 @@ describe('Http Backend Service', () => { // Add a nobody so that we have one http.post('api/nobodies', { id: 42, name: 'Noman' }) - // Reset database with "clear" option - .concatMap(() => http.post('commands/resetDb', { clear: true })) - // get the number of heroes and nobodies - .concatMap(() => sizes$) - .subscribe( + // Reset database with "clear" option + .concatMap(() => http.post('commands/resetDb', { clear: true })) + // get the number of heroes and nobodies + .concatMap(() => sizes$) + .subscribe( sizes => { expect(sizes.heroes).toBe(0, 'reset should have cleared the heroes'); expect(sizes.nobodies).toBe(0, 'reset should have cleared the nobodies'); expect(sizes.stringers).toBe(0, 'reset should have cleared the stringers'); }, failure - ); + ); } }); @@ -265,44 +265,44 @@ describe('Http Backend Service', () => { it('can get heroes', async(() => { http.get('api/heroes') - .map(res => res.json() as Hero[]) - .subscribe( + .map(res => res.json() as Hero[]) + .subscribe( heroes => { // console.log(heroes); expect(heroes.length).toBeGreaterThan(0, 'should have heroes'); }, failure - ); + ); })); it('can translate `foo/heroes` to `heroes` via `parsedRequestUrl` override', async(() => { http.get('api/foo/heroes') - .map(res => res.json() as Hero[]) - .subscribe( + .map(res => res.json() as Hero[]) + .subscribe( heroes => { // console.log(heroes); expect(heroes.length).toBeGreaterThan(0, 'should have heroes'); }, failure - ); + ); })); it('can get villains', async(() => { http.get('api/villains') - .map(res => res.json() as Hero[]) - .subscribe( + .map(res => res.json() as Hero[]) + .subscribe( villains => { // console.log(villains); expect(villains.length).toBeGreaterThan(0, 'should have villains'); }, failure - ); + ); })); it('should 404 when POST to villains', async(() => { const url = 'api/villains'; http.post(url, {id: 42, name: 'Dr. Evil'}) - .subscribe( + .subscribe( _ => { console.log(_); fail(`should not have POSTed data for '${url}'`); @@ -310,13 +310,13 @@ describe('Http Backend Service', () => { err => { expect(err.status).toBe(404, 'should have 404 status'); } - ); + ); })); it('should 404 when GET unknown collection', async(() => { const url = 'api/unknown-collection'; http.get(url) - .subscribe( + .subscribe( _ => { console.log(_); fail(`should not have found data for '${url}'`); @@ -324,21 +324,21 @@ describe('Http Backend Service', () => { err => { expect(err.status).toBe(404, 'should have 404 status'); } - ); + ); })); it('should use genId override to add new hero, "Maxinius"', async(() => { http.post('api/heroes', { name: 'Maxinius' }) - .concatMap(() => http.get('api/heroes?name=Maxi')) - .map(res => res.json()) - .subscribe( + .concatMap(() => http.get('api/heroes?name=Maxi')) + .map(res => res.json()) + .subscribe( heroes => { expect(heroes.length).toBe(1, 'should have found "Maxinius"'); expect(heroes[0].name).toBe('Maxinius'); expect(heroes[0].id).toBeGreaterThan(1000); }, failure - ); + ); })); it('should use genId override guid generator for a new nobody without an id', async(() => { @@ -346,12 +346,12 @@ describe('Http Backend Service', () => { .concatMap(() => http.get('api/nobodies')) .map(res => res.json()) .subscribe( - nobodies => { - expect(nobodies.length).toBe(1, 'should a nobody'); - expect(nobodies[0].name).toBe('Noman', 'should be "Noman"'); - expect(typeof nobodies[0].id).toBe('string', 'should create a string (guid) id'); - }, - failure + nobodies => { + expect(nobodies.length).toBe(1, 'should a nobody'); + expect(nobodies[0].name).toBe('Noman', 'should be "Noman"'); + expect(typeof nobodies[0].id).toBe('string', 'should create a string (guid) id'); + }, + failure ); })); @@ -377,11 +377,11 @@ describe('Http Backend Service', () => { // Add a nobody so that we have one http.post('api/nobodies', { id: 42, name: 'Noman' }) - // Reset database with "clear" option - .concatMap(() => http.post('commands/resetDb', { clear: true })) - // count all the collections - .concatMap(() => sizes$) - .subscribe( + // Reset database with "clear" option + .concatMap(() => http.post('commands/resetDb', { clear: true })) + // count all the collections + .concatMap(() => sizes$) + .subscribe( sizes => { expect(sizes.heroes).toBe(0, 'reset should have cleared the heroes'); expect(sizes.nobodies).toBe(0, 'reset should have cleared the nobodies'); @@ -389,51 +389,51 @@ describe('Http Backend Service', () => { expect(sizes.villains).toBeGreaterThan(0, 'reset should NOT clear villains'); }, failure - ); + ); }; }); //////////////// describe('raw Angular Http w/ override id service', () => { - let http: Http; + let http: Http; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ - HttpModule, - HttpInMemoryWebApiModule.forRoot(HeroInMemDataOverrideIdService, { delay }) - ] + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + HttpModule, + HttpInMemoryWebApiModule.forRoot(HeroInMemDataOverrideIdService, { delay }) + ] + }); + + http = TestBed.get(Http); }); - http = TestBed.get(Http); + it('can get heroes', async(() => { + http.get('api/heroes') + .map(res => res.json() as Hero[]) + .subscribe( + heroes => { + // console.log(heroes); + expect(heroes.length).toBeGreaterThan(0, 'should have heroes'); + }, + failure + ); + })); + + it('can get heroes by uuid', async(() => { + http.get('api/heroes/00000000-0000-0000-0000-000000000002') + .map(res => res.json() as Hero) + .subscribe( + hero => { + // console.log(heroes); + expect(hero.name).toEqual('Bombasto', 'should find hero Bombasto'); + }, + failure + ); + })); }); - it('can get heroes', async(() => { - http.get('api/heroes') - .map(res => res.json() as Hero[]) - .subscribe( - heroes => { - // console.log(heroes); - expect(heroes.length).toBeGreaterThan(0, 'should have heroes'); - }, - failure - ); - })); - - it('can get heroes by uuid', async(() => { - http.get('api/heroes/00000000-0000-0000-0000-000000000002') - .map(res => res.json() as Hero) - .subscribe( - hero => { - // console.log(heroes); - expect(hero.name).toEqual('Bombasto', 'should find hero Bombasto'); - }, - failure - ); - })); - }); - //////////////// describe('Http HeroService', () => { @@ -483,13 +483,13 @@ describe('Http Backend Service', () => { it('can get heroes (no passthru)', async(() => { http.get('api/heroes') - .map(res => res.json() as Hero[]) - .subscribe( + .map(res => res.json() as Hero[]) + .subscribe( heroes => { - expect(createPassThruBackend).not.toHaveBeenCalled(); - expect(heroes.length).toBeGreaterThan(0, 'should have heroes'); - }, - failure + expect(createPassThruBackend).not.toHaveBeenCalled(); + expect(heroes.length).toBeGreaterThan(0, 'should have heroes'); + }, + failure ); })); @@ -507,11 +507,11 @@ describe('Http Backend Service', () => { http.get('api/passthru') .map(res => res.json() as any[]) .subscribe( - passthru => { - console.log('GET passthru data', passthru); - expect(passthru.length).toBeGreaterThan(0, 'should have passthru data'); - }, - failure + passthru => { + console.log('GET passthru data', passthru); + expect(passthru.length).toBeGreaterThan(0, 'should have passthru data'); + }, + failure ); })); @@ -525,12 +525,12 @@ describe('Http Backend Service', () => { http.post('api/passthru', { name: 'Dude' }) .map(res => res.json() as any) .subscribe( - passthru => { - console.log('POST passthru data', passthru); - expect(passthru).toBeDefined('should have passthru data'); - expect(passthru.id).toBe(42, 'passthru object should have id 42'); - }, - failure + passthru => { + console.log('POST passthru data', passthru); + expect(passthru).toBeDefined('should have passthru data'); + expect(passthru.id).toBe(42, 'passthru object should have id 42'); + }, + failure ); })); }); @@ -552,13 +552,13 @@ describe('Http Backend Service', () => { it('can get heroes (encapsulated)', async(() => { http.get('api/heroes') - .map(res => res.json().data as Hero[]) // unwrap data object - .subscribe( + .map(res => res.json().data as Hero[]) // unwrap data object + .subscribe( heroes => { expect(heroes.length).toBeGreaterThan(0, 'should have data.heroes'); }, failure - ); + ); })); }); From 0bafb61c5e8f33d85985a94c19409d8b51565054 Mon Sep 17 00:00:00 2001 From: Christof Hullaert Date: Fri, 8 Dec 2017 00:10:45 +0100 Subject: [PATCH 6/6] fix comments --- src/in-mem/backend.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/in-mem/backend.service.ts b/src/in-mem/backend.service.ts index 6803a8a..1dc2094 100644 --- a/src/in-mem/backend.service.ts +++ b/src/in-mem/backend.service.ts @@ -412,7 +412,7 @@ export abstract class BackendService { } /** - * set the key (by id property name) of a item for a collection + * set the key by property name of a item for a collection * @param item * @param id * @param collectionName