Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit e675a6a

Browse files
committed
feat: support HttpClient
- more refactoring - can substitute your own id generator, `geniD` - parseUrl -> parseRequestUrl - refactored parseuri -> parseUri as independent function - moved interceptorArgs into RequestInfo
1 parent c734deb commit e675a6a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+2090
-1521
lines changed

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ BREAKING CHANGES: Massive refactoring.
1818
Many low-level and customization options have changed.
1919
Apps that stuck with defaults should be (mostly) OK.
2020

21-
* added support for `HttpClient`
21+
* added support for `HttpClient` -> renaming of backend service classes
2222
* added tests
2323
* refactor existing code to support tests
2424
* correct bugs and clarify choices as result of test
@@ -30,6 +30,9 @@ Apps that stuck with defaults should be (mostly) OK.
3030
so you can optionally reset the database dynamically
3131
to arbitrary initial states (issue #128)
3232
* when HTTP method interceptor returns null/undefined, continue with service's default processing (pr #120)
33+
* can substitute your own id generator, `geniD`
34+
* parseUrl -> parseRequestUrl
35+
* utility methods exposed in `RequestInfo.utils`
3336
* reorganize files into src/app and src/in-mem
3437
* adjust gulp tasks accordingly
3538

README.md

Lines changed: 110 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,32 @@
11
# Angular in-memory-web-api
22
[![Build Status][travis-badge]][travis-badge-url]
33

4-
An in-memory web api for Angular demos and tests.
4+
An in-memory web api for Angular demos and tests
5+
that emulates CRUD operations over a RESTy API.
6+
7+
It intercepts Angular `Http` and `HttpClient` requests that would otherwise go to the remote server and redirects them to an in-memory data store that you control.
8+
9+
## Use cases
10+
11+
* Demo apps that need to simulate CRUD data persistence operations without a real server.
12+
You won't have to build and start a test server.
13+
14+
* Whip up prototypes and proofs of concept.
15+
16+
* Share examples with the community in a web coding environment such as Plunker or CodePen.
17+
Create Angular issues and StackOverflow answers supported by live code.
18+
19+
* Simulate operations against data collections that aren't yet implemented on your dev/test server.
20+
You can pass requests thru to the dev/test server for collections that are supported.
21+
22+
* Write unit test apps that read and write data.
23+
Avoid the hassle of intercepting multiple http calls and manufacturing sequences of responses.
24+
The in-memory data store resets for each test so there is no cross-test data pollution.
25+
26+
* End-to-end tests. If you can toggle the app into test mode
27+
using the in-mem web api, you won't disturb the real database.
28+
This can be especially useful for CI (continuous integration) builds.
529

6-
It intercepts Angular `Http` requests that would otherwise go to the remote server
7-
via the Angular `XHRBackend` service
830

931
>**LIMITATIONS**
1032
>
@@ -35,7 +57,7 @@ Examples:
3557
```
3658
// for requests to an `api` base URL that gets heroes from a 'heroes' collection
3759
GET api/heroes // all heroes
38-
GET api/heroes/42 // the character with id=42
60+
GET api/heroes/42 // the hero with id=42
3961
GET api/heroes?name=^j // 'j' is a regex; returns heroes whose name starting with 'j' or 'J'
4062
GET api/heroes.json/42 // ignores the ".json"
4163
```
@@ -86,17 +108,16 @@ export class InMemHeroService implements InMemoryDbService {
86108

87109
>This library _currently_ assumes that every collection has a primary key called `id`.
88110
89-
Register this module and your service implementation in `AppModule.imports`
111+
Register this module and your data store service implementation in `AppModule.imports`
90112
calling the `forRoot` static method with this service class and optional configuration object:
91113
```ts
92-
// other imports
93-
import { HttpModule } from '@angular/http';
114+
import { HttpClientModule } from '@angular/common/http';
94115
import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
95116

96-
import { InMemHeroService } from '../app/hero-data';
117+
import { InMemHeroService } from '../app/hero.service';
97118
@NgModule({
98119
imports: [
99-
HttpModule,
120+
HttpClientModule,
100121
InMemoryWebApiModule.forRoot(InMemHeroService),
101122
...
102123
],
@@ -105,12 +126,37 @@ import { InMemHeroService } from '../app/hero-data';
105126
export class AppModule { ... }
106127
```
107128

108-
See examples in the Angular.io such as the
109-
[Server Communication](https://angular.io/docs/ts/latest/guide/server-communication.html) and
110-
[Tour of Heroes](https://angular.io/docs/ts/latest/tutorial/toh-pt6.html) chapters.
129+
**_Notes_**
130+
131+
* Always import the `InMemoryWebApiModule` _after_ the `HttpClientModule` to ensure that
132+
the in-memory backed provider supersedes the Angular version.
133+
134+
* You can setup the in-memory web api within a lazy loaded feature module by calling the `.forFeature` method as you would `.forRoot`.
111135

112-
>Always import the `InMemoryWebApiModule` _after_ the `HttpModule` to ensure that
113-
the `XHRBackend` provider of the `InMemoryWebApiModule` supersedes all others.
136+
* You can still use the in-memory web api with the older `Http` module.
137+
138+
```ts
139+
import { HttpModule } from '@angular/http';
140+
import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
141+
142+
import { InMemHeroService } from '../app/hero.service';
143+
@NgModule({
144+
imports: [
145+
HttpModule,
146+
InMemoryWebApiModule.forRoot(InMemHeroService),
147+
...
148+
],
149+
...
150+
})
151+
export class AppModule { ... }
152+
```
153+
154+
### Examples
155+
The tests (`src/app/*.spec.ts` files) in the [github repo](https://github.com/angular/in-memory-web-api/tree/master/src/app) are a good place to learn how to setup and use this in-memory web api library.
156+
157+
See also the example source code in the official Angular.io documentation such as the
158+
[HttpClient](https://angular.io/guide/http) guide and the
159+
[Tour of Heroes](https://angular.io/tutorial/toh-pt6).
114160

115161
# Bonus Features
116162
Some features are not readily apparent in the basic usage example.
@@ -129,7 +175,7 @@ Here's how it reasons:
129175
1. If it looks like a [command](#commands), process as a command
130176
2. If the [HTTP method is overridden](#method-override)
131177
3. If the resource name (after the api base path) matches one of the configured collections, process that
132-
4. If not but the `Config.passThruUnknownUrl` flag is `true`, try to [pass the request along to a real _XHRBackend_](#passthru).
178+
4. If not but the `Config.passThruUnknownUrl` flag is `true`, try to [pass the request along to a real _XHR_](#passthru).
133179
5. Return a 404.
134180

135181
See the `handleRequest` method implementation for details.
@@ -158,57 +204,77 @@ The following example matches all names start with the letter 'j' or 'J' in the
158204
Set `config.caseSensitiveSearch = true` if needed.
159205

160206
<a id="passthru"></a>
161-
## Pass thru to a live _XHRBackend_
207+
## Pass thru to a live server
162208

163209
If an existing, running remote server should handle requests for collections
164210
that are not in the in-memory database, set `Config.passThruUnknownUrl: true`.
165-
This service will forward unrecognized requests via a base version of the Angular `XHRBackend`.
211+
Then this service will forward unrecognized requests to the remote server
212+
via the Angular default `XHR` backend (it depends on whether your using `Http` or `HttpClient`).
166213

167-
## _parseUrl_ and your override
214+
## _parseRequestUrl_ and your override
168215

169-
The `parseUrl` parses the request URL into a `ParsedUrl` object.
170-
`ParsedUrl` is a public interface whose properties guide the in-memory web api
216+
The `parseRequestUrl` parses the request URL into a `ParsedRequestUrl` object.
217+
`ParsedRequestUrl` is a public interface whose properties guide the in-memory web api
171218
as it processes the request.
172219

173-
### Default _parseUrl_
220+
### Default _parseRequestUrl_
174221

175222
Default parsing depends upon certain values of `config`: `apiBase`, `host`, and `urlRoot`.
176223
Read the source code for the complete story.
177224

178-
Configuring the `apiBase` yields the most interesting changes to `parseUrl` behavior:
225+
Configuring the `apiBase` yields the most interesting changes to `parseRequestUrl` behavior:
179226

180227
* For `apiBase=undefined` and `url='http://localhost/api/customers/42'`
181228
```
182-
{base: 'api/', collectionName: 'customers', id: '42', ...}
229+
{apiBase: 'api/', collectionName: 'customers', id: '42', ...}
183230
```
184231
185232
* For `apiBase='some/api/root/'` and `url='http://localhost/some/api/root/customers'`
186233
```
187-
{base: 'some/api/root/', collectionName: 'customers', id: undefined, ...}
234+
{ apiBase: 'some/api/root/', collectionName: 'customers', id: undefined, ... }
188235
```
189236
190237
* For `apiBase='/'` and `url='http://localhost/customers'`
191238
```
192-
{base: '/', collectionName: 'customers', id: undefined, ...}
239+
{ apiBase: '/', collectionName: 'customers', id: undefined, ... }
193240
```
194241
195242
**The actual api base segment values are ignored**. Only the number of segments matters.
196243
The following api base strings are considered identical: 'a/b' ~ 'some/api/' ~ `two/segments'
197244
198245
This means that URLs that work with the in-memory web api may be rejected by the real server.
199246
200-
### Custom _parseUrl_
247+
### Custom _parseRequestUrl_
248+
249+
You can override the default parser by implementing a `parseRequestUrl` method in your `InMemoryDbService`.
201250
202-
You can override the default by implementing a `parseUrl` method in your `InMemoryDbService`.
203-
Such a method must take the incoming request URL string and return a `ParsedUrl` object.
251+
The service calls your method with two arguments.
252+
1. `url` - the request URL string
253+
1. `requestInfoUtils` - utility methods in a `RequestInfoUtilities` object, including the default parser.
254+
Note that some values have not yet been set as they depend on the outcome of parsing.
204255
205-
Assign your alternative to `InMemDbService['parseUrl']`
256+
Your method must either return a `ParsedRequestUrl` object or null|undefined,
257+
in which case the service uses the default parser.
258+
In this way you can intercept and parse some URLs and leave the others to the default parser.
259+
260+
### Custom _genId_
261+
262+
Collection items are presumed to have a primary key property called `id`.
263+
When you can specify the id when you add a new item;
264+
the service does not check for uniqueness.
265+
266+
If you do not specify the `id`, the service generates one via the `genId` method.
267+
You can override the default generator with a `genId` method in your `InMemoryDbService`.
268+
Your method receives the new item's collection and should return the generated id.
269+
If your generator returns null|undefined, the service uses the default generator.
206270
207271
## _responseInterceptor_
208272
209-
You can morph the response returned by the default HTTP methods, called by `collectionHandler`,
210-
to suit your needs by adding a `responseInterceptor` method to your `InMemoryDbService` class.
211-
The `collectionHandler` calls your interceptor like this:
273+
You can morph the response returned by the services default HTTP methods.
274+
A typical reason to intercept is to add a header that your application is expecting.
275+
276+
To intercept responses, add a `responseInterceptor` method to your `InMemoryDbService` class.
277+
The service calls your interceptor like this:
212278
```ts
213279
responseOptions = this.responseInterceptor(responseOptions, requestInfo);
214280
```
@@ -240,8 +306,16 @@ requestInfo: RequestInfo; // parsed request
240306
db: Object; // the current in-mem database collections
241307
config: InMemoryBackendConfigArgs; // the current config
242308
passThruBackend: ConnectionBackend; // pass through backend, if it exists
309+
310+
/**
311+
* Create a cold response Observable from a factory for ResponseOptions
312+
* the same way that the in-mem backend service does.
313+
* @param resOptionsFactory - creates ResponseOptions when observable is subscribed
314+
* @param withDelay - if true (default), add simulated latency delay from configuration
315+
*/
316+
createResponse$: (resOptionsFactory: () => ResponseOptions) => Observable<any>;
243317
```
244-
## Examples
318+
## In-memory Web Api Examples
245319

246320
The file `src/app/hero-in-mem-data.service.ts` is an example of a Hero-oriented `InMemoryDbService`,
247321
such as you might see in an HTTP sample in the Angular documentation.
@@ -251,8 +325,8 @@ To try it, add the following line to `AppModule.imports`
251325
InMemoryWebApiModule.forRoot(HeroInMemDataService)
252326
```
253327

254-
See the `src/app/hero-in-mem-data-override.service.ts` class that demonstrates overriding
255-
the `parseUrl` method. It also has a "cold" HTTP GET interceptor.
328+
For examples of overriding service methods,
329+
see the `src/app/hero-in-mem-data-override.service.ts` class.
256330

257331
Add the following line to `AppModule.imports` to see this version of the data service in action:
258332
```ts
@@ -288,7 +362,7 @@ compiling your application project.
288362

289363
- `gulp clean` - clear out all generated `text`
290364

291-
- `npm run tsc` to confirm the project compiles w/o error (sanity check)
365+
- `npm run build` to confirm the project compiles w/o error (sanity check)
292366

293367
- `npm test` to build and run tests (see "Testing" below)
294368

0 commit comments

Comments
 (0)