8000 feat: refactor to agnostic base backend service class · cHullaert/in-memory-web-api@c734deb · GitHub
[go: up one dir, main page]

Skip to content

Commit c734deb

Browse files
committed
feat: refactor to agnostic base backend service class
1 parent cb4cb82 commit c734deb

40 files changed

+2867
-1542
lines changed

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ karma.conf.js
1010
rollup.config.js
1111
src
1212
tsconfig.json
13+
tsconfig-ngc.json
1314
tslint.json
1415
yarn.lock

CHANGELOG.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,16 @@ We do try to tell you about such changes in this `CHANGELOG.md`
1010
and we fix bugs as fast as we can.
1111

1212
<a id="0.4.0"></a>
13-
## 0.4.0 (2017-09-04)
13+
## 0.4.0 (2017-09-06)
14+
**Theme: Support `HttpClient` and add tests**.
1415
See PR #130.
15-
* Added support for `HttpClient`
16-
* Added tests
16+
17+
BREAKING CHANGES: Massive refactoring.
18+
Many low-level and customization options have changed.
19+
Apps that stuck with defaults should be (mostly) OK.
20+
21+
* added support for `HttpClient`
22+
* added tests
1723
* refactor existing code to support tests
1824
* correct bugs and clarify choices as result of test
1925
* add some configuration options

backend.service.d.ts

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import { Observable } from 'rxjs/Observable';
2+
import 'rxjs/add/operator/delay';
3+
import { HeadersCore, InMemoryDbService, InMemoryBackendConfigArgs, ParsedUrl, PassThruBackend, RequestCore, RequestInfo, ResponseOptions } from './interfaces';
4+
/**
5+
* Base class for in-memory web api back-ends
6+
* Simulate the behavior of a RESTy web api
7+
* backed by the simple in-memory data store provided by the injected InMemoryDataService service.
8+
* Conforms mostly to behavior described here:
9+
* http://www.restapitutorial.com/lessons/httpmethods.html
10+
*/
11+
export declare abstract class BackendService {
12+
protected inMemDbService: InMemoryDbService;
13+
protected passThruBackend: PassThruBackend;
14+
protected config: InMemoryBackendConfigArgs;
15+
protected db: Object;
16+
constructor(inMemDbService: InMemoryDbService, config?: InMemoryBackendConfigArgs);
17+
/**
18+
* Process Request and return an Observable of Http Response object
19+
* in the manner of a RESTy web api.
20+
*
21+
* Expect URI pattern in the form :base/:collectionName/:id?
22+
* Examples:
23+
* // for store with a 'customers' collection
24+
* GET api/customers // all customers
25+
* GET api/customers/42 // the character with id=42
26+
* GET api/customers?name=^j // 'j' is a regex; returns customers whose name starts with 'j' or 'J'
27+
* GET api/customers.json/42 // ignores the ".json"
28+
*
29+
* Also accepts direct commands to the service in which the last segment of the apiBase is the word "commands"
30+
* Examples:
31+
* POST commands/resetDb,
32+
* GET/POST commands/config - get or (re)set the config
33+
*
34+
* HTTP overrides:
35+
* If the injected inMemDbService defines an HTTP method (lowercase)
36+
* The request is forwarded to that method as in
37+
* `inMemDbService.get(httpMethodInterceptorArgs)`
38+
* which must return either an Observable of the response type
39+
* for this http library or null|undefined (which means "keep processing").
40+
*/
41+
protected handleRequest(req: RequestCore): Observable<any>;
42+
/**
43+
* return canonical HTTP method name (lowercase) from the request object
44+
* e.g. (req.method || 'get').toLowerCase();
45+
* @param req - request object from the http call
46+
*
47+
*/
48+
protected abstract getRequestMethod(req: any): string;
49+
/**
50+
* Add configured delay to response observable unless delay === 0
51+
*/
52+
protected addDelay(response: Observable<any>): Observable<any>;
53+
/**
54+
* Apply query/search parameters as a filter over the collection
55+
* This impl only supports RegExp queries on string properties of the collection
56+
* ANDs the conditions together
57+
*/
58+
protected applyQuery(collection: any[], query: Map<string, string[]>): any[];
59+
protected bodify(data: any): any;
60+
protected clone(data: any): any;
61+
protected collectionHandler(reqInfo: RequestInfo): ResponseOptions;
62+
/**
63+
* When the last segment of the `base` path is "commands", the `collectionName` is the command
64+
* Example URLs:
65+
* commands/resetdb // Reset the "database" to its original state
66+
* commands/config (GET) // Return this service's config object
67+
* commands/config (!GET) // Update the config (e.g. delay)
68+
*
69+
* Commands are "hot", meaning they are always executed immediately
70+
* whether or not someone subscribes to the returned observable
71+
*
72+
* Usage:
73+
* http.post('commands/resetdb', undefined);
74+
* http.get('commands/config');
75+
* http.post('commands/config', '{"delay":1000}');
76+
*/
77+
protected commands(reqInfo: RequestInfo): Observable<any>;
78+
protected createErrorResponseOptions(url: string, status: number, message: string): ResponseOptions;
79+
/**
80+
* Create standard HTTP headers object from hash map of header strings
81+
* @param headers
82+
*/
83+
protected abstract createHeaders(headers: {
84+
[index: string]: string;
85+
}): HeadersCore;
86+
/**
87+
* return a search map from a location search string
88+
*/
89+
protected abstract createQuery(search: string): Map<string, string[]>;
90+
/**
91+
* Create an Observable response from response options.
92+
*/
93+
protected abstract createResponse$(resOptions$: Observable<ResponseOptions>): Observable<any>;
94+
/**
95+
* Create an Observable of ResponseOptions.
96+
*/
97+
protected createResponseOptions$(resOptionsFactory: () => ResponseOptions): Observable<ResponseOptions>;
98+
protected delete({id, collection, collectionName, headers, url}: RequestInfo): ResponseOptions;
99+
/**
100+
*
101+
* @param collection
102+
* @param id
103+
*/
104+
protected findById<T extends {
105+
id: any;
106+
}>(collection: T[], id: any): T;
107+
/**
108+
* Generate the next available id for item in this collection
109+
* @param collection - collection of items with `id` key property
110+
* This default implementation assumes integer ids.
111+
*/
112+
protected genId<T extends {
113+
id: any;
114+
}>(collection: T[]): any;
115+
protected get({id, query, collection, collectionName, headers, url}: RequestInfo): ResponseOptions;
116+
protected abstract getJsonBody(req: any): any;
117+
protected getLocation(href: string): {
118+
host: any;
119+
protocol: any;
120+
port: any;
121+
pathname: any;
122+
search: string;
123+
};
124+
protected parseuri(str: string): any;
125+
protected indexOf(collection: any[], id: number): number;
126+
protected parseId(collection: {
127+
id: any;
128+
}[], id: string): any;
129+
/**
130+
* Parses the request URL into a `ParsedUrl` object.
131+
* Parsing depends upon certain values of `config`: `apiBase`, `host`, and `urlRoot`.
132+
*
133+
* Configuring the `apiBase` yields the most interesting changes to `parseUrl` behavior:
134+
* When apiBase=undefined and url='http://localhost/api/collection/42'
135+
* {base: 'api/', collectionName: 'collection', id: '42', ...}
136+
* When apiBase='some/api/root/' and url='http://localhost/some/api/root/collection'
137+
* {base: 'some/api/root/', collectionName: 'collection', id: undefined, ...}
138+
* When apiBase='/' and url='http://localhost/collection'
139+
* {base: '/', collectionName: 'collection', id: undefined, ...}
140+
*
141+
* The actual api base segment values are ignored. Only the number of segments matters.
142+
* The following api base strings are considered identical: 'a/b' ~ 'some/api/' ~ `two/segments'
143+
*
144+
* To replace this default method, assign your alternative to your InMemDbService['parseUrl']
145+
*/
146+
protected parseUrl(url: string): ParsedUrl;
147+
protected post({collection, headers, id, req, resourceUrl, url}: RequestInfo): ResponseOptions;
148+
protected put({id, collection, collectionName, headers, req, url}: RequestInfo): ResponseOptions;
149+
protected removeById(collection: any[], id: number): boolean;
150+
/**
151+
* Reset the "database" to its original state
152+
*/
153+
protected resetDb(req?: {}): void;
154+
/**
155+
* Sets the function that passes unhandled requests
156+
* through to the "real" backend if
157+
* config.passThruUnknownUrl is true.
158+
*/
159+
protected abstract setPassThruBackend(): void;
160+
}

0 commit comments

Comments
 (0)
0