8000 choose of id property on item by defineId definition · cHullaert/in-memory-web-api@dfc0dfa · GitHub
[go: up one dir, main page]

Skip to content

Commit dfc0dfa

Browse files
committed
choose of id property on item by defineId definition
1 parent 7fc1ca0 commit dfc0dfa

File tree

3 files changed

+277
-129
lines changed

3 files changed

+277
-129
lines changed
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* This is an example of a Hero-oriented InMemoryDbService.
3+
*
4+
* For demonstration purposes, it can return the database
5+
* synchronously as an object (default),
6+
* as an observable, or as a promise.
7+
*
8+
* Add the following line to `AppModule.imports`
9+
* InMemoryWebApiModule.forRoot(HeroInMemDataService) // or HeroInMemDataOverrideService
10+
*/
11+
import { Injectable } from '@angular/core';
12+
import { InMemoryDbService, RequestInfo } from '../in-mem/interfaces';
13+
14+
// tslint:disable:no-unused-variable
15+
import { Observable } from 'rxjs/Observable';
16+
import { of } from 'rxjs/observable/of';
17+
import 'rxjs/add/operator/delay';
18+
// tslint:enable:no-unused-variable
19+
20+
@Injectable()
21+
export class HeroInMemDataOverrideIdService implements InMemoryDbService {
22+
defineId(collectionName: string) {
23+
return 'uuid';
24+
}
25+
createDb(reqInfo?: RequestInfo) {
26+
27+
const heroes = [
28+
{ uuid: '00000000-0000-0000-0000-000000000001', name: 'Windstorm' },
29+
{ uuid: '00000000-0000-0000-0000-000000000002', name: 'Bombasto' },
30+
{ uuid: '00000000-0000-0000-0000-000000000003', name: 'Magneta' },
31+
{ uuid: '00000000-0000-0000-0000-000000000004', name: 'Tornado' }
32+
];
33+
34+
const nobodies: any[] = [];
35+
36+
// entities with string ids that look like numbers
37+
const stringers = [
38+
{ uuid: '00000000-0000-0000-0000-000000000010', name: 'Bob String' },
39+
{ uuid: '00000000-0000-0000-0000-000000000020', name: 'Jill String' }
40+
];
41< 67E6 code class="diff-text syntax-highlighted-line addition">+
42+
// default returnType
43+
let returnType = 'object';
44+
// let returnType = 'observable';
45+
// let returnType = 'promise';
46+
47+
// demonstrate POST commands/resetDb
48+
// this example clears the collections if the request body tells it to do so
49+
if (reqInfo) {
50+
const body = reqInfo.utils.getJsonBody(reqInfo.req) || {};
51+
if (body.clear === true) {
52+
heroes.length = 0;
53+
nobodies.length = 0;
54+
stringers.length = 0;
55+
}
56+
57+
// 'returnType` can be 'object' | 'observable' | 'promise'
58+
returnType = body.returnType || 'object';
59+
}
60+
const db = { heroes, nobodies, stringers };
61+
62+
switch (returnType) {
63+
case ('observable'):
64+
return of(db).delay(10);
65+
case ('promise'):
66+
return new Promise(resolve => {
67+
setTimeout(() => resolve(db), 10);
68+
});
69+
default:
70+
return db;
71+
}
72+
}
73+
}

src/in-mem/backend.service.ts

Lines changed: 71 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ import {
2828
UriInfo
2929
} from './interfaces';
3030

31+
export interface IDictionary {
32+
[index: string]: string;
33+
}
34+
3135
/**
3236
* Base clas F438 s for in-memory web api back-ends
3337
* Simulate the behavior of a RESTy web api
@@ -36,6 +40,7 @@ import {
3640
* http://www.restapitutorial.com/lessons/httpmethods.html
3741
*/
3842
export abstract class BackendService {
43+
protected fieldIds = {} as IDictionary;
3944
protected config: InMemoryBackendConfigArgs = new InMemoryBackendConfig();
4045
protected db: Object;
4146
protected dbReadySubject: BehaviorSubject<boolean>;
@@ -211,28 +216,28 @@ export abstract class BackendService {
211216

212217
protected collectionHandler(reqInfo: RequestInfo): ResponseOptions {
213218
// const req = reqInfo.req;
214-
let resOptions: ResponseOptions;
215-
switch (reqInfo.method) {
216-
case 'get':
217-
resOptions = this.get(reqInfo);
218-
break;
219-
case 'post':
220-
resOptions = this.post(reqInfo);
221-
break;
222-
case 'put':
223-
resOptions = this.put(reqInfo);
224-
break;
225-
case 'delete':
226-
resOptions = this.delete(reqInfo);
227-
break;
228-
default:
229-
resOptions = this.createErrorResponseOptions(reqInfo.url, STATUS.METHOD_NOT_ALLOWED, 'Method not allowed');
230-
break;
231-
}
219+
let resOptions: ResponseOptions;
220+
switch (reqInfo.method) {
221+
case 'get':
222+
resOptions = this.get(reqInfo);
223+
break;
224+
case 'post':
225+
resOptions = this.post(reqInfo);
226+
break;
227+
case 'put':
228+
resOptions = this.put(reqInfo);
229+
break;
230+
case 'delete':
231+
resOptions = this.delete(reqInfo);
232+
break;
233+
default:
234+
resOptions = this.createErrorResponseOptions(reqInfo.url, STATUS.METHOD_NOT_ALLOWED, 'Method not allowed');
235+
break;
236+
}
232237

233-
// If `inMemDbService.responseInterceptor` exists, let it morph the response options
234-
const interceptor = this.bind('responseInterceptor');
235-
return interceptor ? interceptor(resOptions, reqInfo) : resOptions;
238+
// If `inMemDbService.responseInterceptor` exists, let it morph the response options
239+
const interceptor = this.bind('responseInterceptor');
240+
return interceptor ? interceptor(resOptions, reqInfo) : resOptions;
236241
}
237242

238243
/**
@@ -272,7 +277,7 @@ export abstract class BackendService {
272277
resOptions.status = STATUS.OK;
273278
resOptions.body = this.clone(this.config);
274279

275-
// any other HTTP method is assumed to be a config update
280+
// any other HTTP method is assumed to be a config update
276281
} else {
277282
const body = this.getJsonBody(reqInfo.req);
278283
Object.assign(this.config, body);
@@ -381,7 +386,32 @@ export abstract class BackendService {
381386
* @param id
382387
*/
383388
protected findById<T extends { id: any }>(collection: T[], id: any): T {
384-
return collection.find((item: T) => item.id === id);
389+
return collection.find((item: T) => this.getItemId(item) === id);
390+
}
391+
392+
/**
393+
* define for your in-mem database what is the name of your id field
394+
* return the name of the id field
395+
*/
396+
protected defineId(collectionName = 'default_id'): string {
397+
const defineId = this.bind('defineId');
398+
if (defineId) {
399+
const id = defineId(collectionName);
400+
if ((id !== undefined) && id !== '') { return id; }
401+
}
402+
return 'id';
403+
}
404+
405+
protected getItemId<T extends { id: any }>(item: T, collectionName = 'default_id'): any {
406+
return item[this.fieldIds[collectionName]];
407+
}
408+
409+
protected setItemId<T extends { id: any }>(item: T, id: any, collectionName = 'default_id'): any {
410+
return item[this.fieldIds[collectionName]] = id;
411+
}
412+
413+
protected setFieldId(id: any, collectionName = 'default_id') {
414+
this.fieldIds[collectionName] = this.defineId(collectionName);
385415
}
386416

387417
/**
@@ -414,7 +444,7 @@ export abstract class BackendService {
414444

415445
let maxId = 0;
416446
collection.reduce((prev: any, item: any) => {
417-
maxId = Math.max(maxId, typeof item.id === 'number' ? item.id : maxId);
447+
maxId = Math.max(maxId, typeof this.getItemId(item) === 'number' ? this.getItemId(item) : maxId);
418448
}, undefined);
419449
return maxId + 1;
420450
}
@@ -493,7 +523,7 @@ export abstract class BackendService {
493523
protected abstract getRequestMethod(req: any): string;
494524

495525
protected indexOf(collection: any[], id: number) {
496-
return collection.findIndex((item: any) => item.id === id);
526+
return collection.findIndex((item: any) => this.getItemId(item) === id);
497527
}
498528

499529
/** Parse the id as a number. Return original value if not a number. */
@@ -588,9 +618,9 @@ export abstract class BackendService {
588618
const item = this.getJsonBody(req);
589619

590620
// tslint:disable-next-line:triple-equals
591-
if (item.id == undefined) {
621+
if (this.getItemId(item) == undefined) {
592622
try {
593-
item.id = id || this.genId(collection, collectionName);
623+
this.setItemId(item, id || this.genId(collection, collectionName));
594624
} catch (err) {
595625
const emsg: string = err.message || '';
596626
if (/id type is non-numeric/.test(emsg)) {
@@ -603,10 +633,10 @@ export abstract class BackendService {
603633
}
604634
}
605635

606-
if (id && id !== item.id) {
636+
if (id && id !== this.getItemId(item)) {
607637
return this.createErrorResponseOptions(url, STATUS.BAD_REQUEST, `Request id does not match item.id`);
608638
} else {
609-
id = item.id;
639+
id = this.getItemId(item);
610640
}
611641
const existingIx = this.indexOf(collection, id);
612642
const body = this.bodify(item);
@@ -621,8 +651,8 @@ export abstract class BackendService {
621651
} else {
622652
collection[existingIx] = item;
623653
return this.config.post204 ?
624-
{ headers, status: STATUS.NO_CONTENT } : // successful; no content
625-
{ headers, body, status: STATUS.OK }; // successful; return entity
654+
{ headers, status: STATUS.NO_CONTENT } : // successful; no content
655+
{ headers, body, status: STATUS.OK }; // successful; return entity
626656
}
627657
}
628658

@@ -631,23 +661,23 @@ export abstract class BackendService {
631661
protected put({ collection, collectionName, headers, id, req, url }: RequestInfo): ResponseOptions {
632662
const item = this.getJsonBody(req);
633663
// tslint:disable-next-line:triple-equals
634-
if (item.id == undefined) {
664+
if (this.getItemId(item) == undefined) {
635665
return this.createErrorResponseOptions(url, STATUS.NOT_FOUND, `Missing '${collectionName}' id`);
636666
}
637-
if (id && id !== item.id) {
667+
if (id && id !== this.getItemId(item)) {
638668
return this.createErrorResponseOptions(url, STATUS.BAD_REQUEST,
639669
`Request for '${collectionName}' id does not match item.id`);
640670
} else {
641-
id = item.id;
671+
id = this.getItemId(item);
642672
}
643673
const existingIx = this.indexOf(collection, id);
644674
const body = this.bodify(item);
645675

646676
if (existingIx > -1) {
647677
collection[existingIx] = item;
648678
return this.config.put204 ?
649-
{ headers, status: STATUS.NO_CONTENT } : // successful; no content
650-
{ headers, body, status: STATUS.OK }; // successful; return entity
679+
{ headers, status: STATUS.NO_CONTENT } : // successful; no content
680+
{ headers, body, status: STATUS.OK }; // successful; return entity
651681
} else if (this.config.put404) {
652682
// item to update not found; use POST to create new item for this id.
653683
return this.createErrorResponseOptions(url, STATUS.NOT_FOUND,
@@ -676,12 +706,15 @@ export abstract class BackendService {
676706
this.dbReadySubject.next(false);
677707
const db = this.inMemDbService.createDb(reqInfo);
678708
const db$ = db instanceof Observable ? db :
679-
isPromise(db) ? fromPromise(db) :
680-
of(db);
709+
isPromise(db) ? fromPromise(db) :
710+
of(db);
681711
first.call(db$).subscribe((d: {}) => {
682712
this.db = d;
683713
this.dbReadySubject.next(true);
684714
});
715+
716+
this.setFieldId(this.defineId());
717+
685718
return this.dbReady;
686719
}
687720

0 commit comments

Comments
 (0)
0