UNPKG

9.67 kBTypeScriptView Raw
1import { Observable, BehaviorSubject } from 'rxjs';
2import { HeadersCore, RequestInfoUtilities, InMemoryDbService, InMemoryBackendConfigArgs, ParsedRequestUrl, PassThruBackend, RequestCore, RequestInfo, ResponseOptions, UriInfo } from './interfaces';
3/**
4 * Base class for in-memory web api back-ends
5 * Simulate the behavior of a RESTy web api
6 * backed by the simple in-memory data store provided by the injected `InMemoryDbService` service.
7 * Conforms mostly to behavior described here:
8 * http://www.restapitutorial.com/lessons/httpmethods.html
9 */
10export declare abstract class BackendService {
11 protected inMemDbService: InMemoryDbService;
12 protected config: InMemoryBackendConfigArgs;
13 protected db: Object;
14 protected dbReadySubject: BehaviorSubject<boolean>;
15 private passThruBackend;
16 protected requestInfoUtils: RequestInfoUtilities;
17 constructor(inMemDbService: InMemoryDbService, config?: InMemoryBackendConfigArgs);
18 protected readonly dbReady: Observable<boolean>;
19 /**
20 * Process Request and return an Observable of Http Response object
21 * in the manner of a RESTy web api.
22 *
23 * Expect URI pattern in the form :base/:collectionName/:id?
24 * Examples:
25 * // for store with a 'customers' collection
26 * GET api/customers // all customers
27 * GET api/customers/42 // the character with id=42
28 * GET api/customers?name=^j // 'j' is a regex; returns customers whose name starts with 'j' or 'J'
29 * GET api/customers.json/42 // ignores the ".json"
30 *
31 * Also accepts direct commands to the service in which the last segment of the apiBase is the word "commands"
32 * Examples:
33 * POST commands/resetDb,
34 * GET/POST commands/config - get or (re)set the config
35 *
36 * HTTP overrides:
37 * If the injected inMemDbService defines an HTTP method (lowercase)
38 * The request is forwarded to that method as in
39 * `inMemDbService.get(requestInfo)`
40 * which must return either an Observable of the response type
41 * for this http library or null|undefined (which means "keep processing").
42 */
43 protected handleRequest(req: RequestCore): Observable<any>;
44 protected handleRequest_(req: RequestCore): Observable<any>;
45 /**
46 * Add configured delay to response observable unless delay === 0
47 */
48 protected addDelay(response: Observable<any>): Observable<any>;
49 /**
50 * Apply query/search parameters as a filter over the collection
51 * This impl only supports RegExp queries on string properties of the collection
52 * ANDs the conditions together
53 */
54 protected applyQuery(collection: any[], query: Map<string, string[]>): any[];
55 /**
56 * Get a method from the `InMemoryDbService` (if it exists), bound to that service
57 */
58 protected bind<T extends Function>(methodName: string): T;
59 protected bodify(data: any): any;
60 protected clone(data: any): any;
61 protected collectionHandler(reqInfo: RequestInfo): ResponseOptions;
62 /**
63 * Commands reconfigure the in-memory web api service or extract information from it.
64 * Commands ignore the latency delay and respond ASAP.
65 *
66 * When the last segment of the `apiBase` path is "commands",
67 * the `collectionName` is the command.
68 *
69 * Example URLs:
70 * commands/resetdb (POST) // Reset the "database" to its original state
71 * commands/config (GET) // Return this service's config object
72 * commands/config (POST) // Update the config (e.g. the delay)
73 *
74 * Usage:
75 * http.post('commands/resetdb', undefined);
76 * http.get('commands/config');
77 * http.post('commands/config', '{"delay":1000}');
78 */
79 protected commands(reqInfo: RequestInfo): Observable<any>;
80 protected createErrorResponseOptions(url: string, status: number, message: string): ResponseOptions;
81 /**
82 * Create standard HTTP headers object from hash map of header strings
83 * @param headers
84 */
85 protected abstract createHeaders(headers: {
86 [index: string]: string;
87 }): HeadersCore;
88 /**
89 * create the function that passes unhandled requests through to the "real" backend.
90 */
91 protected abstract createPassThruBackend(): PassThruBackend;
92 /**
93 * return a search map from a location query/search string
94 */
95 protected abstract createQueryMap(search: string): Map<string, string[]>;
96 /**
97 * Create a cold response Observable from a factory for ResponseOptions
98 * @param resOptionsFactory - creates ResponseOptions when observable is subscribed
99 * @param withDelay - if true (default), add simulated latency delay from configuration
100 */
101 protected createResponse$(resOptionsFactory: () => ResponseOptions, withDelay?: boolean): Observable<any>;
102 /**
103 * Create a Response observable from ResponseOptions observable.
104 */
105 protected abstract createResponse$fromResponseOptions$(resOptions$: Observable<ResponseOptions>): Observable<any>;
106 /**
107 * Create a cold Observable of ResponseOptions.
108 * @param resOptionsFactory - creates ResponseOptions when observable is subscribed
109 */
110 protected createResponseOptions$(resOptionsFactory: () => ResponseOptions): Observable<ResponseOptions>;
111 protected delete({ collection, collectionName, headers, id, url }: RequestInfo): ResponseOptions;
112 /**
113 * Find first instance of item in collection by `item.id`
114 * @param collection
115 * @param id
116 */
117 protected findById<T extends {
118 id: any;
119 }>(collection: T[], id: any): T;
120 /**
121 * Generate the next available id for item in this collection
122 * Use method from `inMemDbService` if it exists and returns a value,
123 * else delegates to `genIdDefault`.
124 * @param collection - collection of items with `id` key property
125 */
126 protected genId<T extends {
127 id: any;
128 }>(collection: T[], collectionName: string): any;
129 /**
130 * Default generator of the next available id for item in this collection
131 * This default implementation works only for numeric ids.
132 * @param collection - collection of items with `id` key property
133 * @param collectionName - name of the collection
134 */
135 protected genIdDefault<T extends {
136 id: any;
137 }>(collection: T[], collectionName: string): any;
138 protected get({ collection, collectionName, headers, id, query, url }: RequestInfo): ResponseOptions;
139 /** Get JSON body from the request object */
140 protected abstract getJsonBody(req: any): any;
141 /**
142 * Get location info from a url, even on server where `document` is not defined
143 */
144 protected getLocation(url: string): UriInfo;
145 /**
146 * get or create the function that passes unhandled requests
147 * through to the "real" backend.
148 */
149 protected getPassThruBackend(): PassThruBackend;
150 /**
151 * Get utility methods from this service instance.
152 * Useful within an HTTP method override
153 */
154 protected getRequestInfoUtils(): RequestInfoUtilities;
155 /**
156 * return canonical HTTP method name (lowercase) from the request object
157 * e.g. (req.method || 'get').toLowerCase();
158 * @param req - request object from the http call
159 *
160 */
161 protected abstract getRequestMethod(req: any): string;
162 protected indexOf(collection: any[], id: number): number;
163 /** Parse the id as a number. Return original value if not a number. */
164 protected parseId(collection: any[], collectionName: string, id: string): any;
165 /**
166 * return true if can determine that the collection's `item.id` is a number
167 * This implementation can't tell if the collection is empty so it assumes NO
168 * */
169 protected isCollectionIdNumeric<T extends {
170 id: any;
171 }>(collection: T[], collectionName: string): boolean;
172 /**
173 * Parses the request URL into a `ParsedRequestUrl` object.
174 * Parsing depends upon certain values of `config`: `apiBase`, `host`, and `urlRoot`.
175 *
176 * Configuring the `apiBase` yields the most interesting changes to `parseRequestUrl` behavior:
177 * When apiBase=undefined and url='http://localhost/api/collection/42'
178 * {base: 'api/', collectionName: 'collection', id: '42', ...}
179 * When apiBase='some/api/root/' and url='http://localhost/some/api/root/collection'
180 * {base: 'some/api/root/', collectionName: 'collection', id: undefined, ...}
181 * When apiBase='/' and url='http://localhost/collection'
182 * {base: '/', collectionName: 'collection', id: undefined, ...}
183 *
184 * The actual api base segment values are ignored. Only the number of segments matters.
185 * The following api base strings are considered identical: 'a/b' ~ 'some/api/' ~ `two/segments'
186 *
187 * To replace this default method, assign your alternative to your InMemDbService['parseRequestUrl']
188 */
189 protected parseRequestUrl(url: string): ParsedRequestUrl;
190 protected post({ collection, collectionName, headers, id, req, resourceUrl, url }: RequestInfo): ResponseOptions;
191 protected put({ collection, collectionName, headers, id, req, url }: RequestInfo): ResponseOptions;
192 protected removeById(collection: any[], id: number): boolean;
193 /**
194 * Tell your in-mem "database" to reset.
195 * returns Observable of the database because resetting it could be async
196 */
197 protected resetDb(reqInfo?: RequestInfo): Observable<boolean>;
198}