├── .editorconfig ├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── backend.service.d.ts ├── backend.service.js ├── backend.service.js.map ├── backend.service.metadata.json ├── bundles └── in-memory-web-api.umd.js ├── delay-response.d.ts ├── delay-response.js ├── delay-response.js.map ├── delay-response.metadata.json ├── gulpfile.js ├── http-client-backend.service.d.ts ├── http-client-backend.service.js ├── http-client-backend.service.js.map ├── http-client-backend.service.metadata.json ├── http-client-in-memory-web-api.module.d.ts ├── http-client-in-memory-web-api.module.js ├── http-client-in-memory-web-api.module.js.map ├── http-client-in-memory-web-api.module.metadata.json ├── http-client-in-memory-web-api.module.ngfactory.d.ts ├── http-client-in-memory-web-api.module.ngfactory.js ├── http-client-in-memory-web-api.module.ngfactory.js.map ├── http-status-codes.d.ts ├── http-status-codes.js ├── http-status-codes.js.map ├── http-status-codes.metadata.json ├── in-memory-web-api.module.d.ts ├── in-memory-web-api.module.js ├── in-memory-web-api.module.js.map ├── in-memory-web-api.module.metadata.json ├── in-memory-web-api.module.ngfactory.d.ts ├── in-memory-web-api.module.ngfactory.js ├── in-memory-web-api.module.ngfactory.js.map ├── index.d.ts ├── index.js ├── index.js.map ├── index.metadata.json ├── interfaces.d.ts ├── interfaces.js ├── interfaces.js.map ├── interfaces.metadata.json ├── karma-test-shim.js ├── karma.conf.js ├── package-lock.json ├── package.json ├── rollup.config.js ├── src ├── app │ ├── hero-in-mem-data-override.service.ts │ ├── hero-in-mem-data.service.ts │ ├── hero.service.spec.ts │ ├── hero.service.ts │ ├── hero.ts │ └── http-client-hero.service.ts ├── in-mem │ ├── backend.service.ngsummary.json │ ├── backend.service.ts │ ├── delay-response.ngsummary.json │ ├── delay-response.ts │ ├── http-client-backend.service.ngsummary.json │ ├── http-client-backend.service.spec.ts │ ├── http-client-backend.service.ts │ ├── http-client-in-memory-web-api.module.ngsummary.json │ ├── http-client-in-memory-web-api.module.ts │ ├── http-status-codes.ngsummary.json │ ├── http-status-codes.ts │ ├── in-memory-web-api.module.ngsummary.json │ ├── in-memory-web-api.module.ts │ ├── index.ngsummary.json │ ├── index.ts │ ├── interfaces.ngsummary.json │ └── interfaces.ts ├── systemjs-angular-loader.js ├── systemjs.config.js └── testing │ ├── index.ts │ └── jasmine-ajax.spec.ts ├── tsconfig-ngc.json ├── tsconfig.json └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | 12 | [*.md] 13 | max_line_length = 0 14 | trim_trailing_whitespace = false 15 | 16 | # Indentation override 17 | #[lib/**.js] 18 | #[{package.json,.travis.yml}] 19 | #[**/**.js] 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | aot 2 | in-memory-web-api 3 | node_modules 4 | typings 5 | npm-debug.log 6 | src/**/*.d.ts 7 | src/**/*.js 8 | src/**/*.js.map 9 | src/**/*.metadata.json 10 | yarn.lock 11 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .editorconfig 2 | .gitignore 3 | .npmignore 4 | .travis.yml 5 | aot 6 | gulpfile.js 7 | in-memory-web-api 8 | karma-test-shim.js 9 | karma.conf.js 10 | rollup.config.js 11 | src 12 | tsconfig.json 13 | tsconfig-ngc.json 14 | tslint.json 15 | yarn.lock 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: required 3 | language: node_js 4 | node_js: 5 | - "8" 6 | os: 7 | - linux 8 | env: 9 | global: 10 | - DBUS_SESSION_BUS_ADDRESS=/dev/null 11 | - DISPLAY=:99.0 12 | - CHROME_BIN=chromium-browser 13 | before_script: 14 | - sh -e /etc/init.d/xvfb start 15 | install: 16 | - npm install -g gulp 17 | - npm install 18 | script: 19 | - npm run lint 20 | - npm run tsc 21 | - gulp build 22 | - npm run test:once 23 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # "angular-in-memory-web-api" versions 2 | >This in-memory-web-api exists primarily to support the Angular documentation. 3 | It is not supposed to emulate every possible real world web API and is not intended for production use. 4 | > 5 | >Most importantly, it is ***always experimental***. 6 | 7 | We will make breaking changes and we won't feel bad about it 8 | because this is a development tool, not a production product. 9 | We do try to tell you about such changes in this `CHANGELOG.md` 10 | and we fix bugs as fast as we can. 11 | 12 | 13 | ## 0.10.0 (2020-05-13) 14 | 15 | * update to support Angular v10. 16 | * no functional changes. 17 | 18 | 19 | ## 0.9.0 (2019-06-20) 20 | 21 | * update to support Angular version 8.x and forward 22 | * no functional changes 23 | 24 | 25 | ## 0.8.0 (2018-12-06) 26 | 27 | * remove `@angular/http` support 28 | * no functional changes 29 | 30 | **BREAKING CHANGE** 31 | This version no longer supports any functionality for `@angular/http`. Please use 32 | `@angular/common/http` instead. 33 | 34 | 35 | ## 0.7.0 (2018-10-31) 36 | 37 | * update to support Angular v7. 38 | * no functional changes 39 | 40 | 41 | ## 0.6.1 (2018-05-04) 42 | 43 | * update to Angular and RxJS v6 releases 44 | 45 | 46 | ## 0.6.0 (2018-03-22) 47 | 48 | *Migrate to Angular v6 and RxJS v6 (rc and beta)* 49 | 50 | Note that this release is pinned to Angular "^6.0.0-rc.0" and RxJS "^6.0.0-beta.1". 51 | Will likely update again when they are official. 52 | 53 | **BREAKING CHANGE** 54 | This version depends on RxJS v6 and is not backward compatible with earlier RxJS versions. 55 | 56 | 57 | ## 0.5.4 (2018-03-09) 58 | 59 | Simulated HTTP error responses were not delaying the prescribed time when using RxJS `delay()` 60 | because it was short-circuited by the ErrorResponse. 61 | New `delayResponse` function does it right. 62 | Should not break you unless you incorrectly expected no delay for errors. 63 | 64 | Also, this library no longer calls RxJS `delay()` which may make testing with it easier 65 | (Angular TestBed does not handle RxJS `delay()` well because that operator uses `interval()`). 66 | 67 | Also fixes type error (issue #180). 68 | 69 | 70 | ## 0.5.3 (2018-01-06) 71 | Can make use of `HttpParams` which yields a `request.urlWithParams`. 72 | Added supporting `HeroService.searchHeroes(term: string)` and test. 73 | 74 | 75 | ## 0.5.2 (2017-12-10) 76 | No longer modify the request data coming from client. Fixes #164 77 | 78 | 79 | ## 0.5.1 (2017-10-21) 80 | Support Angular v5. 81 | 82 | 83 | ## 0.5.0 (2017-10-05) 84 | **BREAKING CHANGE**: HTTP response data no longer wrapped in object w/ `data` property by default. 85 | 86 | In this release, the `dataEncapsulation` configuration default changed from `false` to `true`. The HTTP response body holds the data values directly rather than an object that encapsulates those values, `{data: ...}`. This is a **breaking change that affects almost all existing apps!** 87 | 88 | Changing the default to `false` is a **breaking change**. Pre-existing apps that did not set this property explicitly will be broken because they expect encapsulation and are probably mapping 89 | the HTTP response results from the `data` property like this: 90 | ``` 91 | .map(data => data.data as Hero[]) 92 | ``` 93 | **To migrate, simply remove that line everywhere.** 94 | 95 | If you would rather keep the web api's encapsulation, `{data: ...}`, set `dataEncapsulation` to `true` during configuration as in the following example: 96 | ``` 97 | HttpClientInMemoryWebApiModule.forRoot(HeroInMemDataService, { dataEncapsulation: true }) 98 | ``` 99 | 100 | We made this change because 101 | 102 | 1. Almost everyone seems to hate the encapsulation 103 | 104 | 2. Encapsulation requires mapping to get the desired data out. With old `Http` that isn't _too_ bad because you needed to map to get data anyway (`res => res.json()`). But it is really ugly for `HttpClient` because you can't use the type HTTP method type parameter (e.g., `get`) and you have to map out of the data property (`.map(data => data.data as Hero[]`). That extra step requires explanations that distract from learning `HttpClient` itself. 105 | Now you just write `http.get()` and you’ve got data (please add error handling). 106 | 107 | 3. While you could have turned off encapsulation with configuration as of v.0.4, to do so took yet another step that you’d have to discover and explain. A big reason for the in-mem web api is to make it easy to introduce and demonstrate HTTP operations in Angular. The _out-of-box_ experience is more important than avoiding a breaking change. 108 | 109 | 4. The [security flaw](http://stackoverflow.com/questions/3503102/what-are-top-level-json-arrays-and-why-are-they-a-security-risk) 110 | that prompted encapsulation seems to have been mitigated by all (almost all?) the browsers that can run an Angular (v2+) app. We don’t think it’s needed anymore. 111 | 112 | 5. A most real world APIs today will not encapsulate; they’ll return the data in the body without extra ceremony. 113 | 114 | 115 | ## 0.4.6 (2017-09-13) 116 | - improves README 117 | - updates v0.4.0 entry in the CHANGELOG to describe essential additions to SystemJS configuration. 118 | - no important functional changes. 119 | 120 | 121 | ## 0.4.5 (2017-09-11) 122 | Feature - offer separate `HttpClientInMemoryWebApiModule` and `HttpInMemoryWebApiModule`. 123 | 124 | closes #140 125 | 126 | 127 | ## 0.4.4 (2017-09-11) 128 | closes #136 129 | 130 | A **breaking change** if you expected `genId` to generate ids for a collection 131 | with non-numeric `item.id`. 132 | 133 | 134 | ## 0.4.3 (2017-09-11) 135 | Refactoring for clarity and to correctly reflect intent. 136 | A **breaking change** only if your customizations depend directly and explicitly on `RequestInfo` or the `get`, `delete`, `post`, or `put` methods. 137 | 138 | - replace all `switchMap` with `concatMap` because, in all previous uses of `switchMap`, 139 | I really meant to wait for the source observable to complete _before_ beginning the inner observable whereas `switchMap` starts the inner observable right away. 140 | 141 | - restored `collection` to the `RequestInfo` interface and set it in `handleRequest_` 142 | - `get`, `delete`, `post`, and `put` methods get the `collection` from `requestInfo`; simplifies their signatures to one parameter. 143 | 144 | 145 | ## 0.4.2 (2017-09-08) 146 | - Postpones the in-memory database initialization (via `resetDb`) until the first HTTP request. 147 | 148 | - Your `createDb` method _can_ be asynchronous. 149 | You may return the database object (synchronous), an observable of it, or a promise of it. Issue #113. 150 | 151 | - fixed some rare race conditions. 152 | 153 | 154 | ## 0.4.1 (2017-09-08) 155 | **Support PassThru.** 156 | 157 | The passthru feature was broken by 0.4.0 158 | - add passthru to both `Http` and `HttpClient` 159 | - test passThru feature with jasmine-ajax mock-ajax plugin 160 | to intercept Angular's attempt to call browser's XHR 161 | - update devDependency packages 162 | - update karma.conf with jasmine-ajax plugin 163 | 164 | 165 | ## 0.4.0 (2017-09-07) 166 | **Theme: Support `HttpClient` and add tests**. 167 | See PR #130. 168 | 169 | The 0.4.0 release was a major overhaul of this library. 170 | 171 | You don't have to change your existing application _code_ if your app uses this library without customizations. 172 | 173 | But this release's **breaking changes** affect developers who used the customization features or loaded application files with SystemJS. 174 | 175 | **BREAKING CHANGES**: Massive refactoring. 176 | Many low-level and customization options have changed. 177 | Apps that stuck with defaults should be (mostly) OK. 178 | 179 | If you're loading application files with **SystemJS** (as you would in a plunker), see the [instructions below](#v-0-4-systemjs). 180 | 181 | * added support for `HttpClient` -> renaming of backend service classes 182 | * added tests 183 | * refactor existing code to support tests 184 | * correct bugs and clarify choices as result of test 185 | * add some configuration options 186 | - dataEncapsulation (issue #112, pr#123) 187 | - post409 188 | - put404b 189 | * `POST commands/resetDb` passes the request to your `resetDb` method 190 | so you can optionally reset the database dynamically 191 | to arbitrary initial states (issue #128) 192 | * when HTTP method interceptor returns null/undefined, continue with service's default processing (pr #120) 193 | * can substitute your own id generator, `geniD` 194 | * parseUrl -> parseRequestUrl 195 | * utility methods exposed in `RequestInfo.utils` 196 | * reorganize files into src/app and src/in-mem 197 | * adjust gulp tasks accordingly 198 | 199 | --- 200 | 201 | 202 | ### Plunkers and SystemJS 203 | 204 | If you’re loading application files with **SystemJS** (as you would in a plunker), you’ll have to configure it to load Angular’s `umd.js` for `HttpModule` and the `tslib` package. 205 | 206 | To see how, look in the `map` section of the 207 | [`src/systemjs.config.js` for this project](https://github.com/angular/in-memory-web-api/blob/master/src/systemjs.config.js) for the following two _additional_ lines : 208 | 209 | ``` 210 | '@angular/common/http': 'npm:@angular/common/bundles/common-http.umd.js', 211 | ... 212 | 'tslib': 'npm:tslib/tslib.js', 213 | 214 | ``` 215 | 216 | You've already made these changes if you are using `HttpClient` today. 217 | 218 | If you’re sticking with the original Angular `Http` module, you _must make this change anyway!_ Your app will break as soon as you run `npm install` and it installs >=v0.4.0. 219 | 220 | If you're using webpack (as CLI devs do), you don't have to worry about this stuff because webpack bundles the dependencies for you. 221 | 222 | --- 223 | 224 | 225 | ## 0.3.2 (2017-05-02) 226 | * Bug fixes PRs #91, 95, 106 227 | 228 | 229 | ## 0.3.1 (2017-03-08) 230 | * Now runs in node so can use in "universal" demos. 231 | See PR #102. 232 | 233 | 234 | ## 0.3.0 (2017-02-27) 235 | * Support Angular version 4 236 | 237 | 238 | ## 0.2.4 (2017-01-02) 239 | * Remove reflect-matadata and zone.js as peerDependencies 240 | 241 | 242 | ## 0.2.3 (2016-12-28) 243 | * Unpin RxJs 244 | 245 | 246 | ## 0.2.2 (2016-12-20) 247 | * Update to Angular 2.4.0 248 | 249 | 250 | ## 0.2.1 (2016-12-14) 251 | * Fixed regression in handling commands, introduced in 0.2.0 252 | * Improved README 253 | 254 | 255 | ## 0.2.0 (2016-12-11) 256 | 257 | * BREAKING CHANGE: The observables returned by the `handleCollections` methods that process requests against the supplied in-mem-db collections are now "cold". 258 | That means that requests aren't processed until something subscribes to the observable ... just like real-world `Http` calls. 259 | 260 | Previously, these request were "hot" meaning that the operation was performed immediately 261 | (e.g., an in-memory collection was updated) and _then_ we returned an `Observable`. 262 | That was a mistake! Fixing that mistake _might_ break your app which is why bumped the _minor_ version number from 1 to 2. 263 | 264 | We hope _very few apps are broken by this change_. Most will have subscribed anyway. 265 | But any app that called an `http` method with fire-and-forget ... and didn't subscribe ... 266 | expecting the database to be updated (for example) will discover that the operation did ***not*** happen. 267 | 268 | * BREAKING CHANGE: `createErrorResponse` now requires the `Request` object as its first parameter 269 | so it can prepare a proper error message. 270 | For example, a 404 `errorResponse.toString()` now shows the request URL. 271 | 272 | * Commands remain "hot" — processed immediately — as they should be. 273 | 274 | * The `HTTP GET` interceptor in example `hero-data.service` shows how to create your own "cold" observable. 275 | 276 | * While you can still specify the `inMemDbService['responseInterceptor']` to morph the response options, 277 | the previously exported `responseInterceptor` function no longer exists as it served no useful purpose. 278 | Added the `ResponseInterceptor` _type_ to remind you of the signature to implement. 279 | 280 | * Allows objects with `id===0` (issue #56) 281 | 282 | * The default `parseUrl` method is more flexible, thanks in part to the new `config.apiBase` property. 283 | See the ReadMe to learn more. 284 | 285 | * Added `config.post204` and `config.put204` to control whether PUT and POST return the saved entity. 286 | It is `true` by default which means they do not return the entity (`status=204`) — the same behavior as before. (issue #74) 287 | 288 | * `response.url` is set to `request.url` when this service itself creates the response. 289 | 290 | * A few new methods (e.g., `emitResponse`) to assist in HTTP method interceptors. 291 | 292 |
293 | 294 | 295 | ## 0.1.17 (2016-12-07) 296 | * Update to Angular 2.2.0. 297 | 298 | 299 | ## 0.1.16 (2016-11-20) 300 | * Swap `"lib": [ "es2015", "dom" ]` in `tsconfig.json` for @types/core-js` in `package.json` issue #288 301 | 302 | 303 | ## 0.1.15 (2016-11-14) 304 | * Update to Angular 2.2.0. 305 | 306 | 307 | ## 0.1.14 (2016-10-29) 308 | * Add `responseInterceptor` for [issue #61](https://github.com/angular/in-memory-web-api/issues/61) 309 | 310 | 311 | ## 0.1.13 (2016-10-20) 312 | * Update README for 0.1.11 breaking change: npm publish as `esm` and a `umd` bundle 313 | 314 | Going to `umd` changes your `systemjs.config` and the way you import the library. 315 | 316 | In `systemjs.config.js` you should change the mapping to: 317 | ``` 318 | 'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js' 319 | ``` 320 | then delete from `packages`: 321 | ``` 322 | 'angular-in-memory-web-api': { 323 | main: './index.js', 324 | defaultExtension: 'js' 325 | } 326 | ``` 327 | You must ES import the in-mem module (typically in `AppModule`) like this: 328 | ``` 329 | import { InMemoryWebApiModule } from 'angular-in-memory-web-api'; 330 | ``` 331 | 332 | ## 0.1.12 (2016-10-19) 333 | * exclude travis.yml and rollup.config.js from npm package 334 | 335 | 336 | ## 0.1.11 (2016-10-19) 337 | * BREAKING CHANGE: npm publish as `esm` and a `umd` bundle. 338 | Does not change the API but does change the way you register and import the 339 | in-mem module. Documented in later release, v.0.1.13 340 | 341 | 342 | ## 0.1.10 (2016-10-19) 343 | * Catch a `handleRequest` error and return as a failed server response. 344 | 345 | 346 | ## 0.1.9 (2016-10-18) 347 | * Restore delay option, issue #53. 348 | 349 | 350 | ## 0.1.7 (2016-10-12) 351 | * Angular 2.1.x support. 352 | 353 | 354 | ## 0.1.6 (2016-10-09) 355 | * Do not add delay to observable if delay value === 0 (issue #47) 356 | * Can override `parseUrl` method in your db service class (issue #46, #35) 357 | * README.md explains `parseUrl` override. 358 | * Exports functions helpful for custom HTTP Method Interceptors 359 | * `createErrorResponse` 360 | * `createObservableResponse` 361 | * `setStatusText` 362 | * Added `examples/hero-data.service.ts` to show overrides (issue #44) 363 | 364 | 365 | ## 0.1.5 (2016-10-03) 366 | * project.json license changed again to match angular.io package.json 367 | 368 | 369 | ## 0.1.4 (2016-10-03) 370 | * project.json license is "MIT" 371 | 372 | 373 | ## 0.1.3 (2016-09-29) 374 | * Fix typos 375 | 376 | 377 | ## 0.1.2 (2016-09-29) 378 | * AoT support from Tor PR #36 379 | * Update npm packages 380 | * `parseId` fix from PR #33 381 | 382 | 383 | ## 0.1.1 (2016-09-26) 384 | * Exclude src folder and its TS files from npm package 385 | 386 | 387 | ## 0.1.0 (2016-09-25) 388 | * Renamed package to "angular-in-memory-web-api" 389 | * Added "passThruUnknownUrl" options 390 | * Simplified `forRoot` and made it acceptable to AoT 391 | * Support case sensitive search (PR #16) 392 | 393 | # "angular2-in-memory-web-api" versions 394 | The last npm package named "angular2-in-memory-web-api" was v.0.0.21 395 | 396 | 397 | ## 0.0.21 (2016-09-25) 398 | * Add source maps (PR #14) 399 | 400 | 401 | ## 0.0.20 (2016-09-15) 402 | * Angular 2.0.0 403 | * Typescript 2.0.2 404 | 405 | 406 | ## 0.0.19 (2016-09-13) 407 | * RC7 408 | 409 | 410 | ## 0.0.18 (2016-08-31) 411 | * RC6 (doesn't work with older versions) 412 | 413 | 414 | ## 0.0.17 (2016-08-19) 415 | * fix `forRoot` type constraint 416 | * clarify `forRoot` param 417 | 418 | 419 | ## 0.0.16 (2016-08-19) 420 | * No longer exports `HttpModule` 421 | * Can specify configuration options in 2nd param of `forRoot` 422 | * jsDocs for `forRoot` 423 | 424 | 425 | ## 0.0.15 (2016-08-09) 426 | * RC5 427 | * Support for NgModules 428 | 429 | 430 | ## 0.0.14 (2016-06-30) 431 | * RC4 432 | 433 | 434 | ## 0.0.13 (2016-06-21) 435 | * RC3 436 | 437 | 438 | ## 0.0.12 (2016-06-15) 439 | * RC2 440 | 441 | 442 | ## 0.0.11 (2016-05-27) 443 | * add RegExp query support 444 | * find-by-id is sensitive to string ids that look like numbers 445 | 446 | 447 | ## 0.0.10 (2016-05-21) 448 | * added "main:index.js" to package.json 449 | * updated to typings v.1.0.4 (a breaking release) 450 | * dependencies -> peerDependencies|devDependencies 451 | * no es6-shim dependency. 452 | * use core-js as devDependency. 453 | 454 | 455 | ## 0.0.9 (2016-05-19) 456 | * renamed the barrel core.js -> index.js 457 | 458 | 459 | ## 0.0.8 (2016-05-19) 460 | * systemjs -> commonjs 461 | * replace es6-shim typings w/ core-js typings 462 | 463 | 464 | ## 0.0.7 (2016-05-03) 465 | * RC1 466 | * update to 2.0.0-rc.1 467 | 468 | 469 | ## 0.0.6 (2016-05-03) 470 | * RC0 471 | * update to 2.0.0-rc.0 472 | 473 | 474 | ## 0.0.5 (2016-05-01) 475 | * PROVISIONAL - refers to @angular packages 476 | * update to 0.0.0-5 477 | 478 | 479 | ## 0.0.4 (2016-04-30) 480 | * PROVISIONAL - refers to @angular packages 481 | * update to 0.0.0-3 482 | * rxjs: "5.0.0-beta.6" 483 | 484 | 485 | ## 0.0.3 (2016-04-29) 486 | * PROVISIONAL - refers to @angular packages 487 | * update to 0.0.0-2 488 | 489 | 490 | ## 0.0.2 (2016-04-27) 491 | * PROVISIONAL - refers to @angular packages 492 | 493 | 494 | ## 0.0.1 (2016-04-27) 495 | * DO NOT USE. Not adapted to new package system. 496 | * Initial cut for Angular 2 repackaged 497 | * target forthcoming Angular 2 RC 498 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2016 Google, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular in-memory-web-api 2 | [![Build Status][travis-badge]][travis-badge-url] 3 | 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 | See [Austin McDaniel's article](https://medium.com/@amcdnl/mocking-with-angular-more-than-just-unit-testing-cbb7908c9fcc) 10 | for a quick introduction. 11 | 12 | ### _It used to work and now it doesn't :-(_ 13 | 14 | Perhaps you installed a new version of this library? Check the 15 | [CHANGELOG.md](https://github.com/angular/in-memory-web-api/blob/master/CHANGELOG.md) 16 | for breaking changes that may have affected your app. 17 | 18 | If that doesn't explain it, create an 19 | [issue on github](https://github.com/angular/in-memory-web-api/issues), 20 | preferably with a small repro. 21 | 22 | ## Use cases 23 | 24 | * Demo apps that need to simulate CRUD data persistence operations without a real server. 25 | You won't have to build and start a test server. 26 | 27 | * Whip up prototypes and proofs of concept. 28 | 29 | * Share examples with the community in a web coding environment such as Plunker or CodePen. 30 | Create Angular issues and StackOverflow answers supported by live code. 31 | 32 | * Simulate operations against data collections that aren't yet implemented on your dev/test server. 33 | You can pass requests thru to the dev/test server for collections that are supported. 34 | 35 | * Write unit test apps that read and write data. 36 | Avoid the hassle of intercepting multiple http calls and manufacturing sequences of responses. 37 | The in-memory data store resets for each test so there is no cross-test data pollution. 38 | 39 | * End-to-end tests. If you can toggle the app into test mode 40 | using the in-memory web api, you won't disturb the real database. 41 | This can be especially useful for CI (continuous integration) builds. 42 | 43 | 44 | >**LIMITATIONS** 45 | > 46 | >The _in-memory-web-api_ exists primarily to support the 47 | [Angular documentation](https://angular.io/docs/ts/latest/ "Angular documentation web site"). 48 | It is not supposed to emulate every possible real world web API and is not intended for production use. 49 | > 50 | >Most importantly, it is ***always experimental***. 51 | We will make breaking changes and we won't feel bad about it 52 | because this is a development tool, not a production product. 53 | We do try to tell you about such changes in the `CHANGELOG.md` 54 | and we fix bugs as fast as we can. 55 | 56 | ## HTTP request handling 57 | This in-memory web api service processes an HTTP request and 58 | returns an `Observable` of HTTP `Response` object 59 | in the manner of a RESTy web api. 60 | It natively handles URI patterns in the form `:base/:collectionName/:id?` 61 | 62 | Examples: 63 | ```ts 64 | // for requests to an `api` base URL that gets heroes from a 'heroes' collection 65 | GET api/heroes // all heroes 66 | GET api/heroes/42 // the hero with id=42 67 | GET api/heroes?name=^j // 'j' is a regex; returns heroes whose name starting with 'j' or 'J' 68 | GET api/heroes.json/42 // ignores the ".json" 69 | ``` 70 | 71 | The in-memory web api service processes these requests against a "database" - a set of named collections - that you define during setup. 72 | 73 | ## Basic setup 74 | 75 | 76 | 77 | Create an `InMemoryDataService` class that implements `InMemoryDbService`. 78 | 79 | At minimum it must implement `createDb` which 80 | creates a "database" hash whose keys are collection names 81 | and whose values are arrays of collection objects to return or update. 82 | For example: 83 | ```ts 84 | import { InMemoryDbService } from 'angular-in-memory-web-api'; 85 | 86 | export class InMemHeroService implements InMemoryDbService { 87 | createDb() { 88 | let heroes = [ 89 | { id: 1, name: 'Windstorm' }, 90 | { id: 2, name: 'Bombasto' }, 91 | { id: 3, name: 'Magneta' }, 92 | { id: 4, name: 'Tornado' } 93 | ]; 94 | return {heroes}; 95 | } 96 | } 97 | ``` 98 | 99 | **Notes** 100 | 101 | * The in-memory web api library _currently_ assumes that every collection has a primary key called `id`. 102 | 103 | * The `createDb` method can be synchronous or asynchronous. 104 | It would have to be asynchronous if you initialized your in-memory database service from a JSON file. 105 | Return the database _object_, an _observable_ of that object, or a _promise_ of that object. The tests include an example of all three. 106 | 107 | * The in-memory web api calls your `InMemoryDbService` data service class's `createDb` method on two occasions. 108 | 109 | 1. when it handles the _first_ HTTP request 110 | 1. when it receives a `resetdb` [command](#commands). 111 | 112 | In the command case, the service passes in a `RequestInfo` object, 113 | enabling the `createDb` logic to adjust its behavior per the client request. See the tests for examples. 114 | 115 | ### Import the in-memory web api module 116 | 117 | Register your data store service implementation with the `HttpClientInMemoryWebApiModule` 118 | in your root `AppModule.imports` 119 | calling the `forRoot` static method with this service class and an optional configuration object: 120 | ```ts 121 | import { HttpClientModule } from '@angular/common/http'; 122 | import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api'; 123 | 124 | import { InMemHeroService } from '../app/hero.service'; 125 | 126 | @NgModule({ 127 | imports: [ 128 | HttpClientModule, 129 | HttpClientInMemoryWebApiModule.forRoot(InMemHeroService), 130 | ... 131 | ], 132 | ... 133 | }) 134 | export class AppModule { ... } 135 | ``` 136 | 137 | **_Notes_** 138 | 139 | * Always import the `HttpClientInMemoryWebApiModule` _after_ the `HttpClientModule` 140 | to ensure that the in-memory backend provider supersedes the Angular version. 141 | 142 | * You can setup the in-memory web api within a lazy loaded feature module by calling the `.forFeature` method as you would `.forRoot`. 143 | 144 | * In production, you want HTTP requests to go to the real server and probably have no need for the _in-memory_ provider. 145 | CLI-based apps can exclude the provider in production builds like this: 146 | ```ts 147 | imports: [ 148 | HttpClientModule, 149 | environment.production ? 150 | [] : HttpClientInMemoryWebApiModule.forRoot(InMemHeroService) 151 | ... 152 | ] 153 | ``` 154 | 155 | # Examples 156 | The tests (`src/app/*.spec.ts` files) in the 157 | [github repository](https://github.com/angular/in-memory-web-api/tree/master/src/app) 158 | are a good place to learn how to setup and use this in-memory web api library. 159 | 160 | See also the example source code in the official Angular.io documentation such as the 161 | [HttpClient](https://angular.io/guide/http) guide and the 162 | [Tour of Heroes](https://angular.io/tutorial/toh-pt6). 163 | 164 | # Advanced Features 165 | Some features are not readily apparent in the basic usage described above. 166 | 167 | ## Configuration arguments 168 | 169 | The `InMemoryBackendConfigArgs` defines a set of options. Add them as the second `forRoot` argument: 170 | ```ts 171 | InMemoryWebApiModule.forRoot(InMemHeroService, { delay: 500 }), 172 | ``` 173 | 174 | **Read the `InMemoryBackendConfigArgs` interface to learn about these options**. 175 | 176 | 177 | ## Request evaluation order 178 | This service can evaluate requests in multiple ways depending upon the configuration. 179 | Here's how it reasons: 180 | 1. If it looks like a [command](#commands), process as a command 181 | 2. If the [HTTP method is overridden](#method-override), try the override. 182 | 3. If the resource name (after the api base path) matches one of the configured collections, process that 183 | 4. If not but the `Config.passThruUnknownUrl` flag is `true`, try to [pass the request along to a real _XHR_](#passthru). 184 | 5. Return a 404. 185 | 186 | See the `handleRequest` method implementation for details. 187 | 188 | ## Default delayed response 189 | 190 | By default this service adds a 500ms delay 191 | to all data requests to simulate round-trip latency. 192 | 193 | >[Command requests](#commands) have zero added delay as they concern 194 | in-memory service configuration and do not emulate real data requests. 195 | 196 | You can change or eliminate the latency by setting a different `delay` value: 197 | ```ts 198 | InMemoryWebApiModule.forRoot(InMemHeroService, { delay: 0 }), // no delay 199 | InMemoryWebApiModule.forRoot(InMemHeroService, { delay: 1500 }), // 1.5 second delay 200 | ``` 201 | 202 | ## Simple query strings 203 | Pass custom filters as a regex pattern via query string. 204 | The query string defines which property and value to match. 205 | 206 | Format: `/app/heroes/?propertyName=regexPattern` 207 | 208 | The following example matches all names start with the letter 'j' or 'J' in the heroes collection. 209 | 210 | `/app/heroes/?name=^j` 211 | 212 | >Search pattern matches are case insensitive by default. 213 | Set `config.caseSensitiveSearch = true` if needed. 214 | 215 | 216 | ## Pass thru to a live server 217 | 218 | If an existing, running remote server should handle requests for collections 219 | that are not in the in-memory database, set `Config.passThruUnknownUrl: true`. 220 | Then this service will forward unrecognized requests to the remote server 221 | via the Angular default `XHR` backend (it depends on whether your using `Http` or `HttpClient`). 222 | 223 | 224 | ## Commands 225 | 226 | The client may issue a command request to get configuration state 227 | from the in-memory web api service, reconfigure it, 228 | or reset the in-memory database. 229 | 230 | When the last segment of the _api base path_ is "commands", the `collectionName` is treated as the _command_. 231 | 232 | Example URLs: 233 | ```sh 234 | commands/resetdb // Reset the "database" to its original state 235 | commands/config // Get or update this service's config object 236 | ``` 237 | 238 | Usage: 239 | ```sh 240 | http.post('commands/resetdb', undefined); 241 | http.get('commands/config'); 242 | http.post('commands/config', '{"delay":1000}'); 243 | ``` 244 | 245 | Command requests do not simulate real remote data access. 246 | They ignore the latency delay and respond as quickly as possible. 247 | 248 | The `resetDb` command 249 | calls your `InMemoryDbService` data service's [`createDb` method](#createDb) with the `RequestInfo` object, 250 | enabling the `createDb` logic to adjust its behavior per the client request. 251 | 252 | In the following example, the client includes a reset option in the command request body: 253 | ```ts 254 | http 255 | // Reset the database collections with the `clear` option 256 | .post('commands/resetDb', { clear: true })) 257 | 258 | // when command finishes, get heroes 259 | .concatMap( 260 | ()=> http.get('api/heroes') 261 | .map(data => data.data as Hero[]) 262 | ) 263 | 264 | // execute the request sequence and 265 | // do something with the heroes 266 | .subscribe(...) 267 | ``` 268 | 269 | See the tests for other examples. 270 | 271 | ## _parseRequestUrl_ 272 | 273 | The `parseRequestUrl` parses the request URL into a `ParsedRequestUrl` object. 274 | `ParsedRequestUrl` is a public interface whose properties guide the in-memory web api 275 | as it processes the request. 276 | 277 | ### Default _parseRequestUrl_ 278 | 279 | Default parsing depends upon certain values of `config`: `apiBase`, `host`, and `urlRoot`. 280 | Read the source code for the complete story. 281 | 282 | Configuring the `apiBase` yields the most interesting changes to `parseRequestUrl` behavior: 283 | 284 | * For `apiBase=undefined` and `url='http://localhost/api/customers/42'` 285 | ```ts 286 | {apiBase: 'api/', collectionName: 'customers', id: '42', ...} 287 | ``` 288 | 289 | * For `apiBase='some/api/root/'` and `url='http://localhost/some/api/root/customers'` 290 | ```ts 291 | { apiBase: 'some/api/root/', collectionName: 'customers', id: undefined, ... } 292 | ``` 293 | 294 | * For `apiBase='/'` and `url='http://localhost/customers'` 295 | ```ts 296 | { apiBase: '/', collectionName: 'customers', id: undefined, ... } 297 | ``` 298 | 299 | **The actual api base segment values are ignored**. Only the number of segments matters. 300 | The following api base strings are considered identical: 'a/b' ~ 'some/api/' ~ `two/segments' 301 | 302 | This means that URLs that work with the in-memory web api may be rejected by the real server. 303 | 304 | ### Custom _parseRequestUrl_ 305 | 306 | You can override the default parser by implementing a `parseRequestUrl` method in your `InMemoryDbService`. 307 | 308 | The service calls your method with two arguments. 309 | 1. `url` - the request URL string 310 | 1. `requestInfoUtils` - utility methods in a `RequestInfoUtilities` object, including the default parser. 311 | Note that some values have not yet been set as they depend on the outcome of parsing. 312 | 313 | Your method must either return a `ParsedRequestUrl` object or `null`|`undefined`, 314 | in which case the service uses the default parser. 315 | In this way you can intercept and parse some URLs and leave the others to the default parser. 316 | 317 | ## Custom _genId_ 318 | 319 | Collection items are presumed to have a primary key property called `id`. 320 | 321 | You can specify the `id` while adding a new item. 322 | The service will blindly use that `id`; it does not check for uniqueness. 323 | 324 | If you do not specify the `id`, the service generates one via the `genId` method. 325 | 326 | You can override the default id generator with a method called `genId` in your `InMemoryDbService`. 327 | Your method receives the new item's collection and collection name. 328 | It should return the generated id. 329 | If your generator returns `null`|`undefined`, the service uses the default generator. 330 | 331 | ## _responseInterceptor_ 332 | 333 | You can change the response returned by the service's default HTTP methods. 334 | A typical reason to intercept is to add a header that your application is expecting. 335 | 336 | To intercept responses, add a `responseInterceptor` method to your `InMemoryDbService` class. 337 | The service calls your interceptor like this: 338 | ```ts 339 | responseOptions = this.responseInterceptor(responseOptions, requestInfo); 340 | ``` 341 | 342 | 343 | ## HTTP method interceptors 344 | 345 | You may have HTTP requests that the in-memory web api can't handle properly. 346 | 347 | You can override any HTTP method by implementing a method 348 | of that name in your `InMemoryDbService`. 349 | 350 | Your method's name must be the same as the HTTP method name but **all lowercase**. 351 | The in-memory web api calls it with a `RequestInfo` object that contains request data and utility methods. 352 | 353 | For example, if you implemented a `get` method, the web api would be called like this: 354 | `yourInMemDbService["get"](requestInfo)`. 355 | 356 | Your custom HTTP method must return either: 357 | 358 | * `Observable` - you handled the request and the response is available from this 359 | observable. It _should be "cold"_. 360 | 361 | * `null`/`undefined` - you decided not to intervene, 362 | perhaps because you wish to intercept only certain paths for the given HTTP method. 363 | The service continues with its default processing of the HTTP request. 364 | 365 | The `RequestInfo` is an interface defined in `src/in-mem/interfaces.ts`. 366 | Its members include: 367 | ```ts 368 | req: Request; // the request object from the client 369 | collectionName: string; // calculated from the request url 370 | collection: any[]; // the corresponding collection (if found) 371 | id: any; // the item `id` (if specified) 372 | url: string; // the url in the request 373 | utils: RequestInfoUtilities; // helper functions 374 | ``` 375 | The functions in `utils` can help you analyze the request 376 | and compose a response. 377 | 378 | ## In-memory Web Api Examples 379 | 380 | The [github repository](https://github.com/angular/in-memory-web-api/tree/master/src/app) 381 | demonstrates library usage with tested examples. 382 | 383 | The `HeroInMemDataService` class (in `src/app/hero-in-mem-data.service.ts`) is a Hero-oriented `InMemoryDbService` 384 | such as you might see in an HTTP sample in the Angular documentation. 385 | 386 | The `HeroInMemDataOverrideService` class (in `src/app/hero-in-mem-data-override.service.ts`) 387 | demonstrates a few ways to override methods of the base `HeroInMemDataService`. 388 | 389 | The tests ([see below](#testing)) exercise these examples. 390 | 391 | # Build Instructions 392 | 393 | Follow these steps for updating the library. 394 | 395 | - `gulp bump` - up the package version number. 396 | 397 | - update `CHANGELOG.md` to record the change. Call out _breaking changes_. 398 | 399 | - update `README.md` if usage or interfaces change. 400 | 401 | - consider updating the dependency versions in `package.json`. 402 | 403 | - `npm install` the new package(s) if you did. 404 | 405 | - `npm list --depth=0` to make sure they really did install! 406 | 407 | - `gulp clean` to delete all generated files. 408 | 409 | - `npm test` to dev-build and run tests (see ["Testing"](#testing) below). 410 | 411 | - `gulp build` to build for distribution. 412 | 413 | - git add, commit, and push. 414 | 415 | - `npm publish` 416 | 417 | - Confirm that angular.io docs samples still work 418 | 419 | - Add two tags to the release commit in github 420 | - the version number 421 | - 'latest' 422 | 423 | [travis-badge]: https://travis-ci.org/angular/in-memory-web-api.svg?branch=master 424 | [travis-badge-url]: https://travis-ci.org/angular/in-memory-web-api 425 | 426 | ## Testing 427 | 428 | The "app" for this repo is not a real app. 429 | It's an Angular data service (`HeroService`) and a bunch of tests. 430 | 431 | >Note that the `tsconfig.json` produces a `commonjs` module. 432 | That's what _Angular specs require_. 433 | But when building for an app, it should be a `es2015` module, 434 | as is the `tsconfig-ngc.json` for AOT-ready version of this library. 435 | 436 | These tests are a work-in-progress, as tests often are. 437 | 438 | The `src/` folder is divided into 439 | - `app/` - the test "app" and its tests 440 | - `in-mem/` - the source code for the in-memory web api library 441 | 442 | >A real app would reference the in-memory web api node module; 443 | these tests reference the library source files. 444 | 445 | The `karma-test-shim.js` adds the `in-mem` folder to the list of folders that SystemJS should resolve. 446 | 447 | ## Rollup 448 | 449 | The gulp "umd" task runs rollup for tree-shaking. 450 | 451 | I don't remember if it ever worked without a lot of warnings. 452 | The `v0.4.x` release updated to `rollup@0.49` which required updates to the `rollup.config.js`. 453 | 454 | Still weirdly runs `cjs` rollup config first that I can’t find (which produces numerous warnings) before doing the right thing and running the `umd` config. 455 | 456 | Also does not work if you follow instructions and use the `output` property of `rollup.config.js`; does work when configure it “wrong” and put the options in the root. 457 | 458 | Ignoring these issues for now. 459 | 460 | -------------------------------------------------------------------------------- /backend.service.d.ts: -------------------------------------------------------------------------------- 1 | import { Observable, BehaviorSubject } from 'rxjs'; 2 | import { 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 | */ 10 | export declare abstract class BackendService { 11 | protected inMemDbService: InMemoryDbService; 12 | protected config: InMemoryBackendConfigArgs; 13 | protected db: Object; 14 | protected dbReadySubject: BehaviorSubject; 15 | private passThruBackend; 16 | protected requestInfoUtils: RequestInfoUtilities; 17 | constructor(inMemDbService: InMemoryDbService, config?: InMemoryBackendConfigArgs); 18 | protected readonly dbReady: Observable; 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; 44 | protected handleRequest_(req: RequestCore): Observable; 45 | /** 46 | * Add configured delay to response observable unless delay === 0 47 | */ 48 | protected addDelay(response: Observable): Observable; 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): any[]; 55 | /** 56 | * Get a method from the `InMemoryDbService` (if it exists), bound to that service 57 | */ 58 | protected bind(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; 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; 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; 102 | /** 103 | * Create a Response observable from ResponseOptions observable. 104 | */ 105 | protected abstract createResponse$fromResponseOptions$(resOptions$: Observable): Observable; 106 | /** 107 | * Create a cold Observable of ResponseOptions. 108 | * @param resOptionsFactory - creates ResponseOptions when observable is subscribed 109 | */ 110 | protected createResponseOptions$(resOptionsFactory: () => ResponseOptions): Observable; 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(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(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(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(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; 198 | } 199 | -------------------------------------------------------------------------------- /backend.service.metadata.json: -------------------------------------------------------------------------------- 1 | [{"__symbolic":"module","version":4,"metadata":{"BackendService":{"__symbolic":"class","members":{"__ctor__":[{"__symbolic":"constructor","parameters":[{"__symbolic":"reference","module":"./interfaces","name":"InMemoryDbService","line":37,"character":30},{"__symbolic":"reference","module":"./interfaces","name":"InMemoryBackendConfigArgs","line":38,"character":12}]}],"handleRequest":[{"__symbolic":"method"}],"handleRequest_":[{"__symbolic":"method"}],"addDelay":[{"__symbolic":"method"}],"applyQuery":[{"__symbolic":"method"}],"bind":[{"__symbolic":"method"}],"bodify":[{"__symbolic":"method"}],"clone":[{"__symbolic":"method"}],"collectionHandler":[{"__symbolic":"method"}],"commands":[{"__symbolic":"method"}],"createErrorResponseOptions":[{"__symbolic":"method"}],"createHeaders":[{"__symbolic":"method"}],"createPassThruBackend":[{"__symbolic":"method"}],"createQueryMap":[{"__symbolic":"method"}],"createResponse$":[{"__symbolic":"method"}],"createResponse$fromResponseOptions$":[{"__symbolic":"method"}],"createResponseOptions$":[{"__symbolic":"method"}],"delete":[{"__symbolic":"method"}],"findById":[{"__symbolic":"method"}],"genId":[{"__symbolic":"method"}],"genIdDefault":[{"__symbolic":"method"}],"get":[{"__symbolic":"method"}],"getJsonBody":[{"__symbolic":"method"}],"getLocation":[{"__symbolic":"method"}],"getPassThruBackend":[{"__symbolic":"method"}],"getRequestInfoUtils":[{"__symbolic":"method"}],"getRequestMethod":[{"__symbolic":"method"}],"indexOf":[{"__symbolic":"method"}],"parseId":[{"__symbolic":"method"}],"isCollectionIdNumeric":[{"__symbolic":"method"}],"parseRequestUrl":[{"__symbolic":"method"}],"post":[{"__symbolic":"method"}],"put":[{"__symbolic":"method"}],"removeById":[{"__symbolic":"method"}],"resetDb":[{"__symbolic":"method"}]}}}}] -------------------------------------------------------------------------------- /delay-response.d.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs'; 2 | /** adds specified delay (in ms) to both next and error channels of the response observable */ 3 | export declare function delayResponse(response$: Observable, delayMs: number): Observable; 4 | -------------------------------------------------------------------------------- /delay-response.js: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs'; 2 | // Replaces use of RxJS delay. See v0.5.4. 3 | /** adds specified delay (in ms) to both next and error channels of the response observable */ 4 | export function delayResponse(response$, delayMs) { 5 | return new Observable(function (observer) { 6 | var completePending = false; 7 | var nextPending = false; 8 | var subscription = response$.subscribe(function (value) { 9 | nextPending = true; 10 | setTimeout(function () { 11 | observer.next(value); 12 | if (completePending) { 13 | observer.complete(); 14 | } 15 | }, delayMs); 16 | }, function (error) { return setTimeout(function () { return observer.error(error); }, delayMs); }, function () { 17 | completePending = true; 18 | if (!nextPending) { 19 | observer.complete(); 20 | } 21 | }); 22 | return function () { 23 | return subscription.unsubscribe(); 24 | }; 25 | }); 26 | } 27 | //# sourceMappingURL=delay-response.js.map -------------------------------------------------------------------------------- /delay-response.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"delay-response.js","sourceRoot":"","sources":["delay-response.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAElC,0CAA0C;AAC1C,8FAA8F;AAC9F,MAAM,UAAU,aAAa,CAAI,SAAwB,EAAE,OAAe;IACxE,OAAO,IAAI,UAAU,CAAI,UAAA,QAAQ;QAC/B,IAAI,eAAe,GAAG,KAAK,CAAC;QAC5B,IAAI,WAAW,GAAG,KAAK,CAAC;QACxB,IAAM,YAAY,GAAG,SAAS,CAAC,SAAS,CACtC,UAAA,KAAK;YACD,WAAW,GAAG,IAAI,CAAC;YACnB,UAAU,CAAC;gBACX,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACrB,IAAI,eAAe,EAAE;oBACnB,QAAQ,CAAC,QAAQ,EAAE,CAAC;iBACrB;YACH,CAAC,EAAE,OAAO,CAAC,CAAC;QACd,CAAC,EACD,UAAA,KAAK,IAAI,OAAA,UAAU,CAAC,cAAM,OAAA,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,EAArB,CAAqB,EAAE,OAAO,CAAC,EAAhD,CAAgD,EACzD;YACE,eAAe,GAAG,IAAI,CAAC;YACvB,IAAI,CAAC,WAAW,EAAE;gBAChB,QAAQ,CAAC,QAAQ,EAAE,CAAC;aACrB;QACH,CAAC,CACF,CAAC;QACF,OAAO;YACL,OAAO,YAAY,CAAC,WAAW,EAAE,CAAC;QACpC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { Observable } from 'rxjs';\n\n// Replaces use of RxJS delay. See v0.5.4.\n/** adds specified delay (in ms) to both next and error channels of the response observable */\nexport function delayResponse(response$: Observable, delayMs: number): Observable {\n return new Observable(observer => {\n let completePending = false;\n let nextPending = false;\n const subscription = response$.subscribe(\n value => {\n nextPending = true;\n setTimeout(() => {\n observer.next(value);\n if (completePending) {\n observer.complete();\n }\n }, delayMs);\n },\n error => setTimeout(() => observer.error(error), delayMs),\n () => {\n completePending = true;\n if (!nextPending) {\n observer.complete();\n }\n }\n );\n return () => {\n return subscription.unsubscribe();\n };\n });\n}\n"]} -------------------------------------------------------------------------------- /delay-response.metadata.json: -------------------------------------------------------------------------------- 1 | [{"__symbolic":"module","version":4,"metadata":{"delayResponse":{"__symbolic":"function","parameters":["response$","delayMs"],"value":{"__symbolic":"new","expression":{"__symbolic":"reference","module":"rxjs","name":"Observable","line":5,"character":13},"arguments":[{"__symbolic":"error","message":"Lambda not supported","line":5,"character":27}]}}}}] -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var $ = require('gulp-load-plugins')({lazy: true}); 3 | var args = require('yargs').argv; 4 | var cp = require('child_process'); 5 | var del = require('del'); 6 | var rollup = require('rollup-stream'); 7 | var source = require('vinyl-source-stream'); 8 | 9 | var inMemSrc = './src/in-mem/'; 10 | var jsCopySrc = ['*.js', '*.js.map', '*.d.ts', '*.metadata.json'].map(ext => inMemSrc + ext); 11 | 12 | gulp.task('help', $.taskListing.withFilters(function (taskName) { 13 | var isSubTask = taskName.substr(0, 1) == "_"; 14 | return isSubTask; 15 | }, function (taskName) { 16 | var shouldRemove = taskName === 'default'; 17 | return shouldRemove; 18 | })); 19 | 20 | gulp.task('default', gulp.series('help')); 21 | 22 | gulp.task('clean', function() { 23 | return Promise.all([ 24 | clean([ 25 | 'aot/**/*.*', 26 | 'src/**/*.d.ts', 27 | 'src/**/*.js.map', 28 | 'src/**/*.metadata.json', 29 | 'src/**/*.ngfactory.ts', 30 | 'src/**/*.ngsummary.json' 31 | ]), 32 | clean([ 33 | 'src/app/**.*js', 34 | 'src/in-mem/**.*js', 35 | 'src/in-mem/node_modules/**/*.*', 36 | ]), 37 | clean([ /* root-level copies of in-mem web api files */ 38 | './backend.service.*', 39 | './http-status-codes.*', 40 | './http-backend.service.*', 41 | './http-client-backend.service.*', 42 | './interfaces.*', 43 | './http-in-memory-web-api.module.*', 44 | './http-client-in-memory-web-api.module.*', 45 | './in-memory-web-api.module.*', 46 | './index.*', 47 | './bundles/in-memory-web-api.umd.js' 48 | ]) 49 | ]) 50 | .then(() => console.log('Cleaned successfully')); 51 | }); 52 | 53 | gulp.task('ngc', gulp.series('clean', function(done) { 54 | runNgc('src/in-mem/', done); 55 | })); 56 | 57 | // Uses rollup-stream plugin https://www.npmjs.com/package/rollup-stream 58 | gulp.task('umd', gulp.series('ngc', function() { 59 | return rollup('rollup.config.js') 60 | .pipe(source('in-memory-web-api.umd.js')) 61 | .pipe(gulp.dest('./bundles')); 62 | })); 63 | 64 | gulp.task('build', gulp.series('umd', function(){ 65 | return gulp 66 | .src(jsCopySrc) 67 | .pipe(gulp.dest('./')); 68 | })); 69 | 70 | /** 71 | * Bump the version 72 | * --type=pre will bump the prerelease version *.*.*-x 73 | * --type=patch or no flag will bump the patch version *.*.x 74 | * --type=minor will bump the minor version *.x.* 75 | * --type=major will bump the major version x.*.* 76 | * --version=1.2.3 will bump to a specific version and ignore other flags 77 | */ 78 | gulp.task('bump', function() { 79 | var msg = 'Bumping versions'; 80 | var type = args.type; 81 | var version = args.ver; 82 | var options = {}; 83 | if (version) { 84 | options.version = version; 85 | msg += ' to ' + version; 86 | } else { 87 | options.type = type; 88 | msg += ' for a ' + type; 89 | } 90 | log(msg); 91 | 92 | return gulp 93 | .src('package.json') 94 | // .pipe($.print()) 95 | .pipe($.bump(options)) 96 | .pipe(gulp.dest('./')); 97 | }); 98 | ////////// 99 | 100 | function clean(path) { 101 | log('Cleaning: ' + path); 102 | return del(path, {dryRun:false}) 103 | .then(function(paths) { 104 | console.log('Deleted files and folders:\n', paths.join('\n')); 105 | }); 106 | } 107 | 108 | function log(msg) { 109 | if (typeof(msg) === 'object') { 110 | for (var item in msg) { 111 | if (msg.hasOwnProperty(item)) { 112 | console.log(msg[item]); 113 | } 114 | } 115 | } else { 116 | console.log(msg); 117 | } 118 | } 119 | function runNgc(directory, done) { 120 | directory = directory || './'; 121 | //var ngcjs = path.join(process.cwd(), 'node_modules/typescript/bin/tsc'); 122 | //ngcjs = path.join(process.cwd(), 'node_modules/.bin/ngc'); 123 | var ngcjs = './node_modules/@angular/compiler-cli/src/main.js'; 124 | var childProcess = cp.spawn('node', [ngcjs, '-p', './tsconfig-ngc.json'], { cwd: process.cwd() }); 125 | childProcess.stdout.on('data', function (data) { 126 | console.log(data.toString()); 127 | }); 128 | childProcess.stderr.on('data', function (data) { 129 | console.log(data.toString()); 130 | }); 131 | childProcess.on('close', function (code) { 132 | if (code !== 0) { 133 | throw(new Error('ngc compilation exited with an error code')) 134 | } else { 135 | done(); 136 | } 137 | }); 138 | } 139 | -------------------------------------------------------------------------------- /http-client-backend.service.d.ts: -------------------------------------------------------------------------------- 1 | import { HttpBackend, HttpEvent, HttpHeaders, HttpRequest, HttpResponse, HttpXhrBackend, XhrFactory } from '@angular/common/http'; 2 | import { Observable } from 'rxjs'; 3 | import { InMemoryBackendConfigArgs, InMemoryDbService, ResponseOptions } from './interfaces'; 4 | import { BackendService } from './backend.service'; 5 | /** 6 | * For Angular `HttpClient` simulate the behavior of a RESTy web api 7 | * backed by the simple in-memory data store provided by the injected `InMemoryDbService`. 8 | * Conforms mostly to behavior described here: 9 | * http://www.restapitutorial.com/lessons/httpmethods.html 10 | * 11 | * ### Usage 12 | * 13 | * Create an in-memory data store class that implements `InMemoryDbService`. 14 | * Call `config` static method with this service class and optional configuration object: 15 | * ``` 16 | * // other imports 17 | * import { HttpClientModule } from '@angular/common/http'; 18 | * import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api'; 19 | * 20 | * import { InMemHeroService, inMemConfig } from '../api/in-memory-hero.service'; 21 | * @NgModule({ 22 | * imports: [ 23 | * HttpModule, 24 | * HttpClientInMemoryWebApiModule.forRoot(InMemHeroService, inMemConfig), 25 | * ... 26 | * ], 27 | * ... 28 | * }) 29 | * export class AppModule { ... } 30 | * ``` 31 | */ 32 | export declare class HttpClientBackendService extends BackendService implements HttpBackend { 33 | private xhrFactory; 34 | constructor(inMemDbService: InMemoryDbService, config: InMemoryBackendConfigArgs, xhrFactory: XhrFactory); 35 | handle(req: HttpRequest): Observable>; 36 | protected getJsonBody(req: HttpRequest): any; 37 | protected getRequestMethod(req: HttpRequest): string; 38 | protected createHeaders(headers: { 39 | [index: string]: string; 40 | }): HttpHeaders; 41 | protected createQueryMap(search: string): Map; 42 | protected createResponse$fromResponseOptions$(resOptions$: Observable): Observable>; 43 | protected createPassThruBackend(): HttpXhrBackend; 44 | } 45 | -------------------------------------------------------------------------------- /http-client-backend.service.js: -------------------------------------------------------------------------------- 1 | var __extends = (this && this.__extends) || (function () { 2 | var extendStatics = function (d, b) { 3 | extendStatics = Object.setPrototypeOf || 4 | ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 5 | function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 6 | return extendStatics(d, b); 7 | }; 8 | return function (d, b) { 9 | extendStatics(d, b); 10 | function __() { this.constructor = d; } 11 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 12 | }; 13 | })(); 14 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 15 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 16 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 17 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 18 | return c > 3 && r && Object.defineProperty(target, key, r), r; 19 | }; 20 | var __metadata = (this && this.__metadata) || function (k, v) { 21 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 22 | }; 23 | var __param = (this && this.__param) || function (paramIndex, decorator) { 24 | return function (target, key) { decorator(target, key, paramIndex); } 25 | }; 26 | import { Inject, Injectable, Optional } from '@angular/core'; 27 | import { HttpHeaders, HttpParams, HttpResponse, HttpXhrBackend, XhrFactory } from '@angular/common/http'; 28 | import { map } from 'rxjs/operators'; 29 | import { STATUS } from './http-status-codes'; 30 | import { InMemoryBackendConfig, InMemoryBackendConfigArgs, InMemoryDbService } from './interfaces'; 31 | import { BackendService } from './backend.service'; 32 | /** 33 | * For Angular `HttpClient` simulate the behavior of a RESTy web api 34 | * backed by the simple in-memory data store provided by the injected `InMemoryDbService`. 35 | * Conforms mostly to behavior described here: 36 | * http://www.restapitutorial.com/lessons/httpmethods.html 37 | * 38 | * ### Usage 39 | * 40 | * Create an in-memory data store class that implements `InMemoryDbService`. 41 | * Call `config` static method with this service class and optional configuration object: 42 | * ``` 43 | * // other imports 44 | * import { HttpClientModule } from '@angular/common/http'; 45 | * import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api'; 46 | * 47 | * import { InMemHeroService, inMemConfig } from '../api/in-memory-hero.service'; 48 | * @NgModule({ 49 | * imports: [ 50 | * HttpModule, 51 | * HttpClientInMemoryWebApiModule.forRoot(InMemHeroService, inMemConfig), 52 | * ... 53 | * ], 54 | * ... 55 | * }) 56 | * export class AppModule { ... } 57 | * ``` 58 | */ 59 | var HttpClientBackendService = /** @class */ (function (_super) { 60 | __extends(HttpClientBackendService, _super); 61 | function HttpClientBackendService(inMemDbService, config, xhrFactory) { 62 | var _this = _super.call(this, inMemDbService, config) || this; 63 | _this.xhrFactory = xhrFactory; 64 | return _this; 65 | } 66 | HttpClientBackendService.prototype.handle = function (req) { 67 | try { 68 | return this.handleRequest(req); 69 | } 70 | catch (error) { 71 | var err = error.message || error; 72 | var resOptions_1 = this.createErrorResponseOptions(req.url, STATUS.INTERNAL_SERVER_ERROR, "" + err); 73 | return this.createResponse$(function () { return resOptions_1; }); 74 | } 75 | }; 76 | //// protected overrides ///// 77 | HttpClientBackendService.prototype.getJsonBody = function (req) { 78 | return req.body; 79 | }; 80 | HttpClientBackendService.prototype.getRequestMethod = function (req) { 81 | return (req.method || 'get').toLowerCase(); 82 | }; 83 | HttpClientBackendService.prototype.createHeaders = function (headers) { 84 | return new HttpHeaders(headers); 85 | }; 86 | HttpClientBackendService.prototype.createQueryMap = function (search) { 87 | var map = new Map(); 88 | if (search) { 89 | var params_1 = new HttpParams({ fromString: search }); 90 | params_1.keys().forEach(function (p) { return map.set(p, params_1.getAll(p)); }); 91 | } 92 | return map; 93 | }; 94 | HttpClientBackendService.prototype.createResponse$fromResponseOptions$ = function (resOptions$) { 95 | return resOptions$.pipe(map(function (opts) { return new HttpResponse(opts); })); 96 | }; 97 | HttpClientBackendService.prototype.createPassThruBackend = function () { 98 | try { 99 | return new HttpXhrBackend(this.xhrFactory); 100 | } 101 | catch (ex) { 102 | ex.message = 'Cannot create passThru404 backend; ' + (ex.message || ''); 103 | throw ex; 104 | } 105 | }; 106 | HttpClientBackendService = __decorate([ 107 | Injectable(), 108 | __param(1, Inject(InMemoryBackendConfig)), __param(1, Optional()), 109 | __metadata("design:paramtypes", [InMemoryDbService, 110 | InMemoryBackendConfigArgs, 111 | XhrFactory]) 112 | ], HttpClientBackendService); 113 | return HttpClientBackendService; 114 | }(BackendService)); 115 | export { HttpClientBackendService }; 116 | //# sourceMappingURL=http-client-backend.service.js.map -------------------------------------------------------------------------------- /http-client-backend.service.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"http-client-backend.service.js","sourceRoot":"","sources":["http-client-backend.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAGL,WAAW,EACX,UAAU,EAEV,YAAY,EACZ,cAAc,EACd,UAAU,EACX,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAErC,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAE7C,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,iBAAiB,EAElB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH;IAA8C,4CAAc;IAE1D,kCACE,cAAiC,EACU,MAAiC,EACpE,UAAsB;QAHhC,YAKE,kBAAM,cAAc,EAAE,MAAM,CAAC,SAC9B;QAHS,gBAAU,GAAV,UAAU,CAAY;;IAGhC,CAAC;IAED,yCAAM,GAAN,UAAO,GAAqB;QAC1B,IAAI;YACF,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;SAEhC;QAAC,OAAO,KAAK,EAAE;YACd,IAAM,GAAG,GAAG,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC;YACnC,IAAM,YAAU,GAAG,IAAI,CAAC,0BAA0B,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,qBAAqB,EAAE,KAAG,GAAK,CAAC,CAAC;YACpG,OAAO,IAAI,CAAC,eAAe,CAAC,cAAM,OAAA,YAAU,EAAV,CAAU,CAAC,CAAC;SAC/C;IACH,CAAC;IAED,+BAA+B;IAErB,8CAAW,GAArB,UAAsB,GAAqB;QACzC,OAAO,GAAG,CAAC,IAAI,CAAC;IAClB,CAAC;IAES,mDAAgB,GAA1B,UAA2B,GAAqB;QAC9C,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;IAC7C,CAAC;IAES,gDAAa,GAAvB,UAAwB,OAAqC;QAC3D,OAAO,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;IAES,iDAAc,GAAxB,UAAyB,MAAc;QACrC,IAAM,GAAG,GAAG,IAAI,GAAG,EAAoB,CAAC;QACxC,IAAI,MAAM,EAAE;YACV,IAAM,QAAM,GAAG,IAAI,UAAU,CAAC,EAAC,UAAU,EAAE,MAAM,EAAC,CAAC,CAAC;YACpD,QAAM,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,UAAA,CAAC,IAAI,OAAA,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,QAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAA5B,CAA4B,CAAC,CAAC;SAC1D;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAES,sEAAmC,GAA7C,UAA8C,WAAwC;QACpF,OAAO,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,UAAC,IAAsB,IAAK,OAAA,IAAI,YAAY,CAAM,IAAI,CAAC,EAA3B,CAA2B,CAAC,CAAC,CAAC;IACxF,CAAC;IAES,wDAAqB,GAA/B;QACE,IAAI;YACF,OAAO,IAAI,cAAc,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;SAC5C;QAAC,OAAO,EAAE,EAAE;YACX,EAAE,CAAC,OAAO,GAAG,qCAAqC,GAAG,CAAC,EAAE,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;YACxE,MAAM,EAAE,CAAC;SACV;IACH,CAAC;IAvDU,wBAAwB;QADpC,UAAU,EAAE;QAKR,WAAA,MAAM,CAAC,qBAAqB,CAAC,CAAA,EAAE,WAAA,QAAQ,EAAE,CAAA;yCAD1B,iBAAiB;YACkB,yBAAyB;YACxD,UAAU;OALrB,wBAAwB,CAwDpC;IAAD,+BAAC;CAAA,AAxDD,CAA8C,cAAc,GAwD3D;SAxDY,wBAAwB","sourcesContent":["import { Inject, Injectable, Optional } from '@angular/core';\nimport {\n HttpBackend,\n HttpEvent,\n HttpHeaders,\n HttpParams,\n HttpRequest,\n HttpResponse, HttpResponseBase,\n HttpXhrBackend,\n XhrFactory\n} from '@angular/common/http';\n\nimport { Observable } from 'rxjs';\nimport { map } from 'rxjs/operators';\n\nimport { STATUS } from './http-status-codes';\n\nimport {\n InMemoryBackendConfig,\n InMemoryBackendConfigArgs,\n InMemoryDbService,\n ResponseOptions\n} from './interfaces';\n\nimport { BackendService } from './backend.service';\n\n/**\n * For Angular `HttpClient` simulate the behavior of a RESTy web api\n * backed by the simple in-memory data store provided by the injected `InMemoryDbService`.\n * Conforms mostly to behavior described here:\n * http://www.restapitutorial.com/lessons/httpmethods.html\n *\n * ### Usage\n *\n * Create an in-memory data store class that implements `InMemoryDbService`.\n * Call `config` static method with this service class and optional configuration object:\n * ```\n * // other imports\n * import { HttpClientModule } from '@angular/common/http';\n * import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';\n *\n * import { InMemHeroService, inMemConfig } from '../api/in-memory-hero.service';\n * @NgModule({\n * imports: [\n * HttpModule,\n * HttpClientInMemoryWebApiModule.forRoot(InMemHeroService, inMemConfig),\n * ...\n * ],\n * ...\n * })\n * export class AppModule { ... }\n * ```\n */\n@Injectable()\nexport class HttpClientBackendService extends BackendService implements HttpBackend {\n\n constructor(\n inMemDbService: InMemoryDbService,\n @Inject(InMemoryBackendConfig) @Optional() config: InMemoryBackendConfigArgs,\n private xhrFactory: XhrFactory\n ) {\n super(inMemDbService, config);\n }\n\n handle(req: HttpRequest): Observable> {\n try {\n return this.handleRequest(req);\n\n } catch (error) {\n const err = error.message || error;\n const resOptions = this.createErrorResponseOptions(req.url, STATUS.INTERNAL_SERVER_ERROR, `${err}`);\n return this.createResponse$(() => resOptions);\n }\n }\n\n //// protected overrides /////\n\n protected getJsonBody(req: HttpRequest): any {\n return req.body;\n }\n\n protected getRequestMethod(req: HttpRequest): string {\n return (req.method || 'get').toLowerCase();\n }\n\n protected createHeaders(headers: { [index: string]: string; }): HttpHeaders {\n return new HttpHeaders(headers);\n }\n\n protected createQueryMap(search: string): Map {\n const map = new Map();\n if (search) {\n const params = new HttpParams({fromString: search});\n params.keys().forEach(p => map.set(p, params.getAll(p)));\n }\n return map;\n }\n\n protected createResponse$fromResponseOptions$(resOptions$: Observable): Observable> {\n return resOptions$.pipe(map((opts: HttpResponseBase) => new HttpResponse(opts)));\n }\n\n protected createPassThruBackend() {\n try {\n return new HttpXhrBackend(this.xhrFactory);\n } catch (ex) {\n ex.message = 'Cannot create passThru404 backend; ' + (ex.message || '');\n throw ex;\n }\n }\n}\n"]} -------------------------------------------------------------------------------- /http-client-backend.service.metadata.json: -------------------------------------------------------------------------------- 1 | [{"__symbolic":"module","version":4,"metadata":{"HttpClientBackendService":{"__symbolic":"class","extends":{"__symbolic":"reference","module":"./backend.service","name":"BackendService","line":54,"character":46},"decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Injectable","line":53,"character":1}}],"members":{"__ctor__":[{"__symbolic":"constructor","parameterDecorators":[null,[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Inject","line":58,"character":5},"arguments":[{"__symbolic":"reference","module":"./interfaces","name":"InMemoryBackendConfig","line":58,"character":12}]},{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Optional","line":58,"character":36}}],null],"parameters":[{"__symbolic":"reference","module":"./interfaces","name":"InMemoryDbService","line":57,"character":20},{"__symbolic":"reference","module":"./interfaces","name":"InMemoryBackendConfigArgs","line":58,"character":55},{"__symbolic":"reference","module":"@angular/common/http","name":"XhrFactory","line":59,"character":24}]}],"handle":[{"__symbolic":"method"}],"getJsonBody":[{"__symbolic":"method"}],"getRequestMethod":[{"__symbolic":"method"}],"createHeaders":[{"__symbolic":"method"}],"createQueryMap":[{"__symbolic":"method"}],"createResponse$fromResponseOptions$":[{"__symbolic":"method"}],"createPassThruBackend":[{"__symbolic":"method"}]}}}}] -------------------------------------------------------------------------------- /http-client-in-memory-web-api.module.d.ts: -------------------------------------------------------------------------------- 1 | import { ModuleWithProviders, Type } from '@angular/core'; 2 | import { HttpBackend, XhrFactory } from '@angular/common/http'; 3 | import { InMemoryBackendConfigArgs, InMemoryBackendConfig, InMemoryDbService } from './interfaces'; 4 | export declare function httpClientInMemBackendServiceFactory(dbService: InMemoryDbService, options: InMemoryBackendConfig, xhrFactory: XhrFactory): HttpBackend; 5 | export declare class HttpClientInMemoryWebApiModule { 6 | /** 7 | * Redirect the Angular `HttpClient` XHR calls 8 | * to in-memory data store that implements `InMemoryDbService`. 9 | * with class that implements InMemoryDbService and creates an in-memory database. 10 | * 11 | * Usually imported in the root application module. 12 | * Can import in a lazy feature module too, which will shadow modules loaded earlier 13 | * 14 | * @param {Type} dbCreator - Class that creates seed data for in-memory database. Must implement InMemoryDbService. 15 | * @param {InMemoryBackendConfigArgs} [options] 16 | * 17 | * @example 18 | * HttpInMemoryWebApiModule.forRoot(dbCreator); 19 | * HttpInMemoryWebApiModule.forRoot(dbCreator, {useValue: {delay:600}}); 20 | */ 21 | static forRoot(dbCreator: Type, options?: InMemoryBackendConfigArgs): ModuleWithProviders; 22 | /** 23 | * 24 | * Enable and configure the in-memory web api in a lazy-loaded feature module. 25 | * Same as `forRoot`. 26 | * This is a feel-good method so you can follow the Angular style guide for lazy-loaded modules. 27 | */ 28 | static forFeature(dbCreator: Type, options?: InMemoryBackendConfigArgs): ModuleWithProviders; 29 | } 30 | -------------------------------------------------------------------------------- /http-client-in-memory-web-api.module.js: -------------------------------------------------------------------------------- 1 | ////// HttpClient-Only version //// 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | import { NgModule } from '@angular/core'; 9 | import { HttpBackend, XhrFactory } from '@angular/common/http'; 10 | import { InMemoryBackendConfig, InMemoryDbService } from './interfaces'; 11 | import { HttpClientBackendService } from './http-client-backend.service'; 12 | // Internal - Creates the in-mem backend for the HttpClient module 13 | // AoT requires factory to be exported 14 | export function httpClientInMemBackendServiceFactory(dbService, options, xhrFactory) { 15 | var backend = new HttpClientBackendService(dbService, options, xhrFactory); 16 | return backend; 17 | } 18 | var HttpClientInMemoryWebApiModule = /** @class */ (function () { 19 | function HttpClientInMemoryWebApiModule() { 20 | } 21 | HttpClientInMemoryWebApiModule_1 = HttpClientInMemoryWebApiModule; 22 | /** 23 | * Redirect the Angular `HttpClient` XHR calls 24 | * to in-memory data store that implements `InMemoryDbService`. 25 | * with class that implements InMemoryDbService and creates an in-memory database. 26 | * 27 | * Usually imported in the root application module. 28 | * Can import in a lazy feature module too, which will shadow modules loaded earlier 29 | * 30 | * @param {Type} dbCreator - Class that creates seed data for in-memory database. Must implement InMemoryDbService. 31 | * @param {InMemoryBackendConfigArgs} [options] 32 | * 33 | * @example 34 | * HttpInMemoryWebApiModule.forRoot(dbCreator); 35 | * HttpInMemoryWebApiModule.forRoot(dbCreator, {useValue: {delay:600}}); 36 | */ 37 | HttpClientInMemoryWebApiModule.forRoot = function (dbCreator, options) { 38 | return { 39 | ngModule: HttpClientInMemoryWebApiModule_1, 40 | providers: [ 41 | { provide: InMemoryDbService, useClass: dbCreator }, 42 | { provide: InMemoryBackendConfig, useValue: options }, 43 | { provide: HttpBackend, 44 | useFactory: httpClientInMemBackendServiceFactory, 45 | deps: [InMemoryDbService, InMemoryBackendConfig, XhrFactory] } 46 | ] 47 | }; 48 | }; 49 | /** 50 | * 51 | * Enable and configure the in-memory web api in a lazy-loaded feature module. 52 | * Same as `forRoot`. 53 | * This is a feel-good method so you can follow the Angular style guide for lazy-loaded modules. 54 | */ 55 | HttpClientInMemoryWebApiModule.forFeature = function (dbCreator, options) { 56 | return HttpClientInMemoryWebApiModule_1.forRoot(dbCreator, options); 57 | }; 58 | var HttpClientInMemoryWebApiModule_1; 59 | HttpClientInMemoryWebApiModule = HttpClientInMemoryWebApiModule_1 = __decorate([ 60 | NgModule({}) 61 | ], HttpClientInMemoryWebApiModule); 62 | return HttpClientInMemoryWebApiModule; 63 | }()); 64 | export { HttpClientInMemoryWebApiModule }; 65 | //# sourceMappingURL=http-client-in-memory-web-api.module.js.map -------------------------------------------------------------------------------- /http-client-in-memory-web-api.module.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"http-client-in-memory-web-api.module.js","sourceRoot":"","sources":["http-client-in-memory-web-api.module.ts"],"names":[],"mappings":"AAAA,mCAAmC;;;;;;;AAEnC,OAAO,EAAE,QAAQ,EAA6B,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAE/D,OAAO,EAEL,qBAAqB,EACrB,iBAAiB,EAClB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAEzE,kEAAkE;AAClE,sCAAsC;AACtC,MAAM,UAAU,oCAAoC,CAClD,SAA4B,EAC5B,OAA8B,EAC9B,UAAsB;IAEtB,IAAM,OAAO,GAAQ,IAAI,wBAAwB,CAAC,SAAS,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;IAClF,OAAO,OAAO,CAAC;AACjB,CAAC;AAGD;IAAA;IAwCA,CAAC;uCAxCY,8BAA8B;IACzC;;;;;;;;;;;;;;MAcE;IACK,sCAAO,GAAd,UAAe,SAAkC,EAAE,OAAmC;QAEpF,OAAO;YACL,QAAQ,EAAE,gCAA8B;YACxC,SAAS,EAAE;gBACT,EAAE,OAAO,EAAE,iBAAiB,EAAG,QAAQ,EAAE,SAAS,EAAE;gBACpD,EAAE,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,OAAO,EAAE;gBAErD,EAAE,OAAO,EAAE,WAAW;oBACpB,UAAU,EAAE,oCAAoC;oBAChD,IAAI,EAAE,CAAC,iBAAiB,EAAE,qBAAqB,EAAE,UAAU,CAAC,EAAC;aAChE;SACF,CAAC;IACJ,CAAC;IACC;;;;;KAKC;IACI,yCAAU,GAAjB,UAAkB,SAAkC,EAAE,OAAmC;QAEvF,OAAO,gCAA8B,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACpE,CAAC;;IAvCU,8BAA8B;QAD1C,QAAQ,CAAC,EAAE,CAAC;OACA,8BAA8B,CAwC1C;IAAD,qCAAC;CAAA,AAxCD,IAwCC;SAxCY,8BAA8B","sourcesContent":["////// HttpClient-Only version ////\n\nimport { NgModule, ModuleWithProviders, Type } from '@angular/core';\nimport { HttpBackend, XhrFactory } from '@angular/common/http';\n\nimport {\n InMemoryBackendConfigArgs,\n InMemoryBackendConfig,\n InMemoryDbService\n} from './interfaces';\n\nimport { HttpClientBackendService } from './http-client-backend.service';\n\n// Internal - Creates the in-mem backend for the HttpClient module\n// AoT requires factory to be exported\nexport function httpClientInMemBackendServiceFactory(\n dbService: InMemoryDbService,\n options: InMemoryBackendConfig,\n xhrFactory: XhrFactory,\n): HttpBackend {\n const backend: any = new HttpClientBackendService(dbService, options, xhrFactory);\n return backend;\n}\n\n@NgModule({})\nexport class HttpClientInMemoryWebApiModule {\n /**\n * Redirect the Angular `HttpClient` XHR calls\n * to in-memory data store that implements `InMemoryDbService`.\n * with class that implements InMemoryDbService and creates an in-memory database.\n *\n * Usually imported in the root application module.\n * Can import in a lazy feature module too, which will shadow modules loaded earlier\n *\n * @param {Type} dbCreator - Class that creates seed data for in-memory database. Must implement InMemoryDbService.\n * @param {InMemoryBackendConfigArgs} [options]\n *\n * @example\n * HttpInMemoryWebApiModule.forRoot(dbCreator);\n * HttpInMemoryWebApiModule.forRoot(dbCreator, {useValue: {delay:600}});\n */\n static forRoot(dbCreator: Type, options?: InMemoryBackendConfigArgs):\n ModuleWithProviders {\n return {\n ngModule: HttpClientInMemoryWebApiModule,\n providers: [\n { provide: InMemoryDbService, useClass: dbCreator },\n { provide: InMemoryBackendConfig, useValue: options },\n\n { provide: HttpBackend,\n useFactory: httpClientInMemBackendServiceFactory,\n deps: [InMemoryDbService, InMemoryBackendConfig, XhrFactory]}\n ]\n };\n }\n /**\n *\n * Enable and configure the in-memory web api in a lazy-loaded feature module.\n * Same as `forRoot`.\n * This is a feel-good method so you can follow the Angular style guide for lazy-loaded modules.\n */\n static forFeature(dbCreator: Type, options?: InMemoryBackendConfigArgs):\n ModuleWithProviders {\n return HttpClientInMemoryWebApiModule.forRoot(dbCreator, options);\n }\n}\n"]} -------------------------------------------------------------------------------- /http-client-in-memory-web-api.module.metadata.json: -------------------------------------------------------------------------------- 1 | [{"__symbolic":"module","version":4,"metadata":{"httpClientInMemBackendServiceFactory":{"__symbolic":"function"},"HttpClientInMemoryWebApiModule":{"__symbolic":"class","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"NgModule","line":24,"character":1},"arguments":[{}]}],"statics":{"forRoot":{"__symbolic":"function","parameters":["dbCreator","options"],"value":{"ngModule":{"__symbolic":"reference","name":"HttpClientInMemoryWebApiModule"},"providers":[{"provide":{"__symbolic":"reference","module":"./interfaces","name":"InMemoryDbService","line":46,"character":19},"useClass":{"__symbolic":"reference","name":"dbCreator"}},{"provide":{"__symbolic":"reference","module":"./interfaces","name":"InMemoryBackendConfig","line":47,"character":19},"useValue":{"__symbolic":"reference","name":"options"}},{"provide":{"__symbolic":"reference","module":"@angular/common/http","name":"HttpBackend","line":49,"character":19},"useFactory":{"__symbolic":"reference","name":"httpClientInMemBackendServiceFactory"},"deps":[{"__symbolic":"reference","module":"./interfaces","name":"InMemoryDbService","line":51,"character":17},{"__symbolic":"reference","module":"./interfaces","name":"InMemoryBackendConfig","line":51,"character":36},{"__symbolic":"reference","module":"@angular/common/http","name":"XhrFactory","line":51,"character":59}]}]}},"forFeature":{"__symbolic":"function","parameters":["dbCreator","options"],"value":{"__symbolic":"call","expression":{"__symbolic":"select","expression":{"__symbolic":"reference","name":"HttpClientInMemoryWebApiModule"},"member":"forRoot"},"arguments":[{"__symbolic":"reference","name":"dbCreator"},{"__symbolic":"reference","name":"options"}]}}}}}}] -------------------------------------------------------------------------------- /http-client-in-memory-web-api.module.ngfactory.d.ts: -------------------------------------------------------------------------------- 1 | import * as i0 from '@angular/core'; 2 | import * as i1 from './http-client-in-memory-web-api.module'; 3 | export declare const HttpClientInMemoryWebApiModuleNgFactory: i0.NgModuleFactory; 4 | -------------------------------------------------------------------------------- /http-client-in-memory-web-api.module.ngfactory.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview This file was generated by the Angular template compiler. Do not edit. 3 | * 4 | * @suppress {suspiciousCode,uselessCode,missingProperties,missingOverride,checkTypes} 5 | * tslint:disable 6 | */ 7 | import * as i0 from "@angular/core"; 8 | import * as i1 from "./http-client-in-memory-web-api.module"; 9 | var HttpClientInMemoryWebApiModuleNgFactory = i0.ɵcmf(i1.HttpClientInMemoryWebApiModule, [], function (_l) { return i0.ɵmod([i0.ɵmpd(512, i0.ComponentFactoryResolver, i0.ɵCodegenComponentFactoryResolver, [[8, []], [3, i0.ComponentFactoryResolver], i0.NgModuleRef]), i0.ɵmpd(1073742336, i1.HttpClientInMemoryWebApiModule, i1.HttpClientInMemoryWebApiModule, [])]); }); 10 | export { HttpClientInMemoryWebApiModuleNgFactory as HttpClientInMemoryWebApiModuleNgFactory }; 11 | //# sourceMappingURL=http-client-in-memory-web-api.module.ngfactory.js.map -------------------------------------------------------------------------------- /http-client-in-memory-web-api.module.ngfactory.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"http-client-in-memory-web-api.module.ngfactory.js","sourceRoot":"","sources":["http-client-in-memory-web-api.module.ngfactory.ts"],"names":[],"mappings":"","sourcesContent":["import * as i0 from '@angular/core';\nimport * as i1 from './http-client-in-memory-web-api.module';\nexport const HttpClientInMemoryWebApiModuleNgFactory:i0.NgModuleFactory = (null as any);\nvar _decl0_0:i0.TemplateRef = ((null as any));\nvar _decl0_1:i0.ElementRef = ((null as any));\n"]} -------------------------------------------------------------------------------- /http-status-codes.d.ts: -------------------------------------------------------------------------------- 1 | export declare const STATUS: { 2 | CONTINUE: number; 3 | SWITCHING_PROTOCOLS: number; 4 | OK: number; 5 | CREATED: number; 6 | ACCEPTED: number; 7 | NON_AUTHORITATIVE_INFORMATION: number; 8 | NO_CONTENT: number; 9 | RESET_CONTENT: number; 10 | PARTIAL_CONTENT: number; 11 | MULTIPLE_CHOICES: number; 12 | MOVED_PERMANTENTLY: number; 13 | FOUND: number; 14 | SEE_OTHER: number; 15 | NOT_MODIFIED: number; 16 | USE_PROXY: number; 17 | TEMPORARY_REDIRECT: number; 18 | BAD_REQUEST: number; 19 | UNAUTHORIZED: number; 20 | PAYMENT_REQUIRED: number; 21 | FORBIDDEN: number; 22 | NOT_FOUND: number; 23 | METHOD_NOT_ALLOWED: number; 24 | NOT_ACCEPTABLE: number; 25 | PROXY_AUTHENTICATION_REQUIRED: number; 26 | REQUEST_TIMEOUT: number; 27 | CONFLICT: number; 28 | GONE: number; 29 | LENGTH_REQUIRED: number; 30 | PRECONDITION_FAILED: number; 31 | PAYLOAD_TO_LARGE: number; 32 | URI_TOO_LONG: number; 33 | UNSUPPORTED_MEDIA_TYPE: number; 34 | RANGE_NOT_SATISFIABLE: number; 35 | EXPECTATION_FAILED: number; 36 | IM_A_TEAPOT: number; 37 | UPGRADE_REQUIRED: number; 38 | INTERNAL_SERVER_ERROR: number; 39 | NOT_IMPLEMENTED: number; 40 | BAD_GATEWAY: number; 41 | SERVICE_UNAVAILABLE: number; 42 | GATEWAY_TIMEOUT: number; 43 | HTTP_VERSION_NOT_SUPPORTED: number; 44 | PROCESSING: number; 45 | MULTI_STATUS: number; 46 | IM_USED: number; 47 | PERMANENT_REDIRECT: number; 48 | UNPROCESSABLE_ENTRY: number; 49 | LOCKED: number; 50 | FAILED_DEPENDENCY: number; 51 | PRECONDITION_REQUIRED: number; 52 | TOO_MANY_REQUESTS: number; 53 | REQUEST_HEADER_FIELDS_TOO_LARGE: number; 54 | UNAVAILABLE_FOR_LEGAL_REASONS: number; 55 | VARIANT_ALSO_NEGOTIATES: number; 56 | INSUFFICIENT_STORAGE: number; 57 | NETWORK_AUTHENTICATION_REQUIRED: number; 58 | }; 59 | export declare const STATUS_CODE_INFO: { 60 | '100': { 61 | 'code': number; 62 | 'text': string; 63 | 'description': string; 64 | 'spec_title': string; 65 | 'spec_href': string; 66 | }; 67 | '101': { 68 | 'code': number; 69 | 'text': string; 70 | 'description': string; 71 | 'spec_title': string; 72 | 'spec_href': string; 73 | }; 74 | '200': { 75 | 'code': number; 76 | 'text': string; 77 | 'description': string; 78 | 'spec_title': string; 79 | 'spec_href': string; 80 | }; 81 | '201': { 82 | 'code': number; 83 | 'text': string; 84 | 'description': string; 85 | 'spec_title': string; 86 | 'spec_href': string; 87 | }; 88 | '202': { 89 | 'code': number; 90 | 'text': string; 91 | 'description': string; 92 | 'spec_title': string; 93 | 'spec_href': string; 94 | }; 95 | '203': { 96 | 'code': number; 97 | 'text': string; 98 | 'description': string; 99 | 'spec_title': string; 100 | 'spec_href': string; 101 | }; 102 | '204': { 103 | 'code': number; 104 | 'text': string; 105 | 'description': string; 106 | 'spec_title': string; 107 | 'spec_href': string; 108 | }; 109 | '205': { 110 | 'code': number; 111 | 'text': string; 112 | 'description': string; 113 | 'spec_title': string; 114 | 'spec_href': string; 115 | }; 116 | '206': { 117 | 'code': number; 118 | 'text': string; 119 | 'description': string; 120 | 'spec_title': string; 121 | 'spec_href': string; 122 | }; 123 | '300': { 124 | 'code': number; 125 | 'text': string; 126 | 'description': string; 127 | 'spec_title': string; 128 | 'spec_href': string; 129 | }; 130 | '301': { 131 | 'code': number; 132 | 'text': string; 133 | 'description': string; 134 | 'spec_title': string; 135 | 'spec_href': string; 136 | }; 137 | '302': { 138 | 'code': number; 139 | 'text': string; 140 | 'description': string; 141 | 'spec_title': string; 142 | 'spec_href': string; 143 | }; 144 | '303': { 145 | 'code': number; 146 | 'text': string; 147 | 'description': string; 148 | 'spec_title': string; 149 | 'spec_href': string; 150 | }; 151 | '304': { 152 | 'code': number; 153 | 'text': string; 154 | 'description': string; 155 | 'spec_title': string; 156 | 'spec_href': string; 157 | }; 158 | '305': { 159 | 'code': number; 160 | 'text': string; 161 | 'description': string; 162 | 'spec_title': string; 163 | 'spec_href': string; 164 | }; 165 | '307': { 166 | 'code': number; 167 | 'text': string; 168 | 'description': string; 169 | 'spec_title': string; 170 | 'spec_href': string; 171 | }; 172 | '400': { 173 | 'code': number; 174 | 'text': string; 175 | 'description': string; 176 | 'spec_title': string; 177 | 'spec_href': string; 178 | }; 179 | '401': { 180 | 'code': number; 181 | 'text': string; 182 | 'description': string; 183 | 'spec_title': string; 184 | 'spec_href': string; 185 | }; 186 | '402': { 187 | 'code': number; 188 | 'text': string; 189 | 'description': string; 190 | 'spec_title': string; 191 | 'spec_href': string; 192 | }; 193 | '403': { 194 | 'code': number; 195 | 'text': string; 196 | 'description': string; 197 | 'spec_title': string; 198 | 'spec_href': string; 199 | }; 200 | '404': { 201 | 'code': number; 202 | 'text': string; 203 | 'description': string; 204 | 'spec_title': string; 205 | 'spec_href': string; 206 | }; 207 | '405': { 208 | 'code': number; 209 | 'text': string; 210 | 'description': string; 211 | 'spec_title': string; 212 | 'spec_href': string; 213 | }; 214 | '406': { 215 | 'code': number; 216 | 'text': string; 217 | 'description': string; 218 | 'spec_title': string; 219 | 'spec_href': string; 220 | }; 221 | '407': { 222 | 'code': number; 223 | 'text': string; 224 | 'description': string; 225 | 'spec_title': string; 226 | 'spec_href': string; 227 | }; 228 | '408': { 229 | 'code': number; 230 | 'text': string; 231 | 'description': string; 232 | 'spec_title': string; 233 | 'spec_href': string; 234 | }; 235 | '409': { 236 | 'code': number; 237 | 'text': string; 238 | 'description': string; 239 | 'spec_title': string; 240 | 'spec_href': string; 241 | }; 242 | '410': { 243 | 'code': number; 244 | 'text': string; 245 | 'description': string; 246 | 'spec_title': string; 247 | 'spec_href': string; 248 | }; 249 | '411': { 250 | 'code': number; 251 | 'text': string; 252 | 'description': string; 253 | 'spec_title': string; 254 | 'spec_href': string; 255 | }; 256 | '412': { 257 | 'code': number; 258 | 'text': string; 259 | 'description': string; 260 | 'spec_title': string; 261 | 'spec_href': string; 262 | }; 263 | '413': { 264 | 'code': number; 265 | 'text': string; 266 | 'description': string; 267 | 'spec_title': string; 268 | 'spec_href': string; 269 | }; 270 | '414': { 271 | 'code': number; 272 | 'text': string; 273 | 'description': string; 274 | 'spec_title': string; 275 | 'spec_href': string; 276 | }; 277 | '415': { 278 | 'code': number; 279 | 'text': string; 280 | 'description': string; 281 | 'spec_title': string; 282 | 'spec_href': string; 283 | }; 284 | '416': { 285 | 'code': number; 286 | 'text': string; 287 | 'description': string; 288 | 'spec_title': string; 289 | 'spec_href': string; 290 | }; 291 | '417': { 292 | 'code': number; 293 | 'text': string; 294 | 'description': string; 295 | 'spec_title': string; 296 | 'spec_href': string; 297 | }; 298 | '418': { 299 | 'code': number; 300 | 'text': string; 301 | 'description': string; 302 | 'spec_title': string; 303 | 'spec_href': string; 304 | }; 305 | '426': { 306 | 'code': number; 307 | 'text': string; 308 | 'description': string; 309 | 'spec_title': string; 310 | 'spec_href': string; 311 | }; 312 | '500': { 313 | 'code': number; 314 | 'text': string; 315 | 'description': string; 316 | 'spec_title': string; 317 | 'spec_href': string; 318 | }; 319 | '501': { 320 | 'code': number; 321 | 'text': string; 322 | 'description': string; 323 | 'spec_title': string; 324 | 'spec_href': string; 325 | }; 326 | '502': { 327 | 'code': number; 328 | 'text': string; 329 | 'description': string; 330 | 'spec_title': string; 331 | 'spec_href': string; 332 | }; 333 | '503': { 334 | 'code': number; 335 | 'text': string; 336 | 'description': string; 337 | 'spec_title': string; 338 | 'spec_href': string; 339 | }; 340 | '504': { 341 | 'code': number; 342 | 'text': string; 343 | 'description': string; 344 | 'spec_title': string; 345 | 'spec_href': string; 346 | }; 347 | '505': { 348 | 'code': number; 349 | 'text': string; 350 | 'description': string; 351 | 'spec_title': string; 352 | 'spec_href': string; 353 | }; 354 | '102': { 355 | 'code': number; 356 | 'text': string; 357 | 'description': string; 358 | 'spec_title': string; 359 | 'spec_href': string; 360 | }; 361 | '207': { 362 | 'code': number; 363 | 'text': string; 364 | 'description': string; 365 | 'spec_title': string; 366 | 'spec_href': string; 367 | }; 368 | '226': { 369 | 'code': number; 370 | 'text': string; 371 | 'description': string; 372 | 'spec_title': string; 373 | 'spec_href': string; 374 | }; 375 | '308': { 376 | 'code': number; 377 | 'text': string; 378 | 'description': string; 379 | 'spec_title': string; 380 | 'spec_href': string; 381 | }; 382 | '422': { 383 | 'code': number; 384 | 'text': string; 385 | 'description': string; 386 | 'spec_title': string; 387 | 'spec_href': string; 388 | }; 389 | '423': { 390 | 'code': number; 391 | 'text': string; 392 | 'description': string; 393 | 'spec_title': string; 394 | 'spec_href': string; 395 | }; 396 | '424': { 397 | 'code': number; 398 | 'text': string; 399 | 'description': string; 400 | 'spec_title': string; 401 | 'spec_href': string; 402 | }; 403 | '428': { 404 | 'code': number; 405 | 'text': string; 406 | 'description': string; 407 | 'spec_title': string; 408 | 'spec_href': string; 409 | }; 410 | '429': { 411 | 'code': number; 412 | 'text': string; 413 | 'description': string; 414 | 'spec_title': string; 415 | 'spec_href': string; 416 | }; 417 | '431': { 418 | 'code': number; 419 | 'text': string; 420 | 'description': string; 421 | 'spec_title': string; 422 | 'spec_href': string; 423 | }; 424 | '451': { 425 | 'code': number; 426 | 'text': string; 427 | 'description': string; 428 | 'spec_title': string; 429 | 'spec_href': string; 430 | }; 431 | '506': { 432 | 'code': number; 433 | 'text': string; 434 | 'description': string; 435 | 'spec_title': string; 436 | 'spec_href': string; 437 | }; 438 | '507': { 439 | 'code': number; 440 | 'text': string; 441 | 'description': string; 442 | 'spec_title': string; 443 | 'spec_href': string; 444 | }; 445 | '511': { 446 | 'code': number; 447 | 'text': string; 448 | 'description': string; 449 | 'spec_title': string; 450 | 'spec_href': string; 451 | }; 452 | }; 453 | /** 454 | * get the status text from StatusCode 455 | */ 456 | export declare function getStatusText(status: number): any; 457 | /** 458 | * Returns true if the the Http Status Code is 200-299 (success) 459 | */ 460 | export declare function isSuccess(status: number): boolean; 461 | -------------------------------------------------------------------------------- /in-memory-web-api.module.d.ts: -------------------------------------------------------------------------------- 1 | import { ModuleWithProviders, Type } from '@angular/core'; 2 | import { InMemoryBackendConfigArgs, InMemoryDbService } from './interfaces'; 3 | export declare class InMemoryWebApiModule { 4 | /** 5 | * Redirect BOTH Angular `Http` and `HttpClient` XHR calls 6 | * to in-memory data store that implements `InMemoryDbService`. 7 | * with class that implements InMemoryDbService and creates an in-memory database. 8 | * 9 | * Usually imported in the root application module. 10 | * Can import in a lazy feature module too, which will shadow modules loaded earlier 11 | * 12 | * @param {Type} dbCreator - Class that creates seed data for in-memory database. Must implement InMemoryDbService. 13 | * @param {InMemoryBackendConfigArgs} [options] 14 | * 15 | * @example 16 | * InMemoryWebApiModule.forRoot(dbCreator); 17 | * InMemoryWebApiModule.forRoot(dbCreator, {useValue: {delay:600}}); 18 | */ 19 | static forRoot(dbCreator: Type, options?: InMemoryBackendConfigArgs): ModuleWithProviders; 20 | /** 21 | * 22 | * Enable and configure the in-memory web api in a lazy-loaded feature module. 23 | * Same as `forRoot`. 24 | * This is a feel-good method so you can follow the Angular style guide for lazy-loaded modules. 25 | */ 26 | static forFeature(dbCreator: Type, options?: InMemoryBackendConfigArgs): ModuleWithProviders; 27 | } 28 | -------------------------------------------------------------------------------- /in-memory-web-api.module.js: -------------------------------------------------------------------------------- 1 | ////// For apps with both Http and HttpClient //// 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | import { NgModule } from '@angular/core'; 9 | import { HttpBackend, XhrFactory } from '@angular/common/http'; 10 | import { InMemoryBackendConfig, InMemoryDbService } from './interfaces'; 11 | import { httpClientInMemBackendServiceFactory } from './http-client-in-memory-web-api.module'; 12 | var InMemoryWebApiModule = /** @class */ (function () { 13 | function InMemoryWebApiModule() { 14 | } 15 | InMemoryWebApiModule_1 = InMemoryWebApiModule; 16 | /** 17 | * Redirect BOTH Angular `Http` and `HttpClient` XHR calls 18 | * to in-memory data store that implements `InMemoryDbService`. 19 | * with class that implements InMemoryDbService and creates an in-memory database. 20 | * 21 | * Usually imported in the root application module. 22 | * Can import in a lazy feature module too, which will shadow modules loaded earlier 23 | * 24 | * @param {Type} dbCreator - Class that creates seed data for in-memory database. Must implement InMemoryDbService. 25 | * @param {InMemoryBackendConfigArgs} [options] 26 | * 27 | * @example 28 | * InMemoryWebApiModule.forRoot(dbCreator); 29 | * InMemoryWebApiModule.forRoot(dbCreator, {useValue: {delay:600}}); 30 | */ 31 | InMemoryWebApiModule.forRoot = function (dbCreator, options) { 32 | return { 33 | ngModule: InMemoryWebApiModule_1, 34 | providers: [ 35 | { provide: InMemoryDbService, useClass: dbCreator }, 36 | { provide: InMemoryBackendConfig, useValue: options }, 37 | { provide: HttpBackend, 38 | useFactory: httpClientInMemBackendServiceFactory, 39 | deps: [InMemoryDbService, InMemoryBackendConfig, XhrFactory] } 40 | ] 41 | }; 42 | }; 43 | /** 44 | * 45 | * Enable and configure the in-memory web api in a lazy-loaded feature module. 46 | * Same as `forRoot`. 47 | * This is a feel-good method so you can follow the Angular style guide for lazy-loaded modules. 48 | */ 49 | InMemoryWebApiModule.forFeature = function (dbCreator, options) { 50 | return InMemoryWebApiModule_1.forRoot(dbCreator, options); 51 | }; 52 | var InMemoryWebApiModule_1; 53 | InMemoryWebApiModule = InMemoryWebApiModule_1 = __decorate([ 54 | NgModule({}) 55 | ], InMemoryWebApiModule); 56 | return InMemoryWebApiModule; 57 | }()); 58 | export { InMemoryWebApiModule }; 59 | //# sourceMappingURL=in-memory-web-api.module.js.map -------------------------------------------------------------------------------- /in-memory-web-api.module.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"in-memory-web-api.module.js","sourceRoot":"","sources":["in-memory-web-api.module.ts"],"names":[],"mappings":"AAAA,kDAAkD;;;;;;;AAElD,OAAO,EAAY,QAAQ,EAA6B,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAE/D,OAAO,EAEL,qBAAqB,EACrB,iBAAiB,EAClB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,oCAAoC,EAAE,MAAM,wCAAwC,CAAC;AAG9F;IAAA;IAwCA,CAAC;6BAxCY,oBAAoB;IAC/B;;;;;;;;;;;;;;MAcE;IACK,4BAAO,GAAd,UAAe,SAAkC,EAAE,OAAmC;QACpF,OAAO;YACL,QAAQ,EAAE,sBAAoB;YAC9B,SAAS,EAAE;gBACT,EAAE,OAAO,EAAE,iBAAiB,EAAG,QAAQ,EAAE,SAAS,EAAE;gBACpD,EAAE,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,OAAO,EAAE;gBAGrD,EAAE,OAAO,EAAE,WAAW;oBACpB,UAAU,EAAE,oCAAoC;oBAChD,IAAI,EAAE,CAAC,iBAAiB,EAAE,qBAAqB,EAAE,UAAU,CAAC,EAAC;aAChE;SACF,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACI,+BAAU,GAAjB,UAAkB,SAAkC,EAAE,OAAmC;QACvF,OAAO,sBAAoB,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC1D,CAAC;;IAvCU,oBAAoB;QADhC,QAAQ,CAAC,EAAE,CAAC;OACA,oBAAoB,CAwChC;IAAD,2BAAC;CAAA,AAxCD,IAwCC;SAxCY,oBAAoB","sourcesContent":["////// For apps with both Http and HttpClient ////\n\nimport { Injector, NgModule, ModuleWithProviders, Type } from '@angular/core';\nimport { HttpBackend, XhrFactory } from '@angular/common/http';\n\nimport {\n InMemoryBackendConfigArgs,\n InMemoryBackendConfig,\n InMemoryDbService\n} from './interfaces';\n\nimport { httpClientInMemBackendServiceFactory } from './http-client-in-memory-web-api.module';\n\n@NgModule({})\nexport class InMemoryWebApiModule {\n /**\n * Redirect BOTH Angular `Http` and `HttpClient` XHR calls\n * to in-memory data store that implements `InMemoryDbService`.\n * with class that implements InMemoryDbService and creates an in-memory database.\n *\n * Usually imported in the root application module.\n * Can import in a lazy feature module too, which will shadow modules loaded earlier\n *\n * @param {Type} dbCreator - Class that creates seed data for in-memory database. Must implement InMemoryDbService.\n * @param {InMemoryBackendConfigArgs} [options]\n *\n * @example\n * InMemoryWebApiModule.forRoot(dbCreator);\n * InMemoryWebApiModule.forRoot(dbCreator, {useValue: {delay:600}});\n */\n static forRoot(dbCreator: Type, options?: InMemoryBackendConfigArgs): ModuleWithProviders {\n return {\n ngModule: InMemoryWebApiModule,\n providers: [\n { provide: InMemoryDbService, useClass: dbCreator },\n { provide: InMemoryBackendConfig, useValue: options },\n\n\n { provide: HttpBackend,\n useFactory: httpClientInMemBackendServiceFactory,\n deps: [InMemoryDbService, InMemoryBackendConfig, XhrFactory]}\n ]\n };\n }\n\n /**\n *\n * Enable and configure the in-memory web api in a lazy-loaded feature module.\n * Same as `forRoot`.\n * This is a feel-good method so you can follow the Angular style guide for lazy-loaded modules.\n */\n static forFeature(dbCreator: Type, options?: InMemoryBackendConfigArgs): ModuleWithProviders {\n return InMemoryWebApiModule.forRoot(dbCreator, options);\n }\n}\n"]} -------------------------------------------------------------------------------- /in-memory-web-api.module.metadata.json: -------------------------------------------------------------------------------- 1 | [{"__symbolic":"module","version":4,"metadata":{"InMemoryWebApiModule":{"__symbolic":"class","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"NgModule","line":13,"character":1},"arguments":[{}]}],"statics":{"forRoot":{"__symbolic":"function","parameters":["dbCreator","options"],"value":{"ngModule":{"__symbolic":"reference","name":"InMemoryWebApiModule"},"providers":[{"provide":{"__symbolic":"reference","module":"./interfaces","name":"InMemoryDbService","line":34,"character":19},"useClass":{"__symbolic":"reference","name":"dbCreator"}},{"provide":{"__symbolic":"reference","module":"./interfaces","name":"InMemoryBackendConfig","line":35,"character":19},"useValue":{"__symbolic":"reference","name":"options"}},{"provide":{"__symbolic":"reference","module":"@angular/common/http","name":"HttpBackend","line":38,"character":19},"useFactory":{"__symbolic":"reference","module":"./http-client-in-memory-web-api.module","name":"httpClientInMemBackendServiceFactory","line":39,"character":22},"deps":[{"__symbolic":"reference","module":"./interfaces","name":"InMemoryDbService","line":40,"character":17},{"__symbolic":"reference","module":"./interfaces","name":"InMemoryBackendConfig","line":40,"character":36},{"__symbolic":"reference","module":"@angular/common/http","name":"XhrFactory","line":40,"character":59}]}]}},"forFeature":{"__symbolic":"function","parameters":["dbCreator","options"],"value":{"__symbolic":"call","expression":{"__symbolic":"select","expression":{"__symbolic":"reference","name":"InMemoryWebApiModule"},"member":"forRoot"},"arguments":[{"__symbolic":"reference","name":"dbCreator"},{"__symbolic":"reference","name":"options"}]}}}}}}] -------------------------------------------------------------------------------- /in-memory-web-api.module.ngfactory.d.ts: -------------------------------------------------------------------------------- 1 | import * as i0 from '@angular/core'; 2 | import * as i1 from './in-memory-web-api.module'; 3 | export declare const InMemoryWebApiModuleNgFactory: i0.NgModuleFactory; 4 | -------------------------------------------------------------------------------- /in-memory-web-api.module.ngfactory.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview This file was generated by the Angular template compiler. Do not edit. 3 | * 4 | * @suppress {suspiciousCode,uselessCode,missingProperties,missingOverride,checkTypes} 5 | * tslint:disable 6 | */ 7 | import * as i0 from "@angular/core"; 8 | import * as i1 from "./in-memory-web-api.module"; 9 | var InMemoryWebApiModuleNgFactory = i0.ɵcmf(i1.InMemoryWebApiModule, [], function (_l) { return i0.ɵmod([i0.ɵmpd(512, i0.ComponentFactoryResolver, i0.ɵCodegenComponentFactoryResolver, [[8, []], [3, i0.ComponentFactoryResolver], i0.NgModuleRef]), i0.ɵmpd(1073742336, i1.InMemoryWebApiModule, i1.InMemoryWebApiModule, [])]); }); 10 | export { InMemoryWebApiModuleNgFactory as InMemoryWebApiModuleNgFactory }; 11 | //# sourceMappingURL=in-memory-web-api.module.ngfactory.js.map -------------------------------------------------------------------------------- /in-memory-web-api.module.ngfactory.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"in-memory-web-api.module.ngfactory.js","sourceRoot":"","sources":["in-memory-web-api.module.ngfactory.ts"],"names":[],"mappings":"","sourcesContent":["import * as i0 from '@angular/core';\nimport * as i1 from './in-memory-web-api.module';\nexport const InMemoryWebApiModuleNgFactory:i0.NgModuleFactory = (null as any);\nvar _decl0_0:i0.TemplateRef = ((null as any));\nvar _decl0_1:i0.ElementRef = ((null as any));\n"]} -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | export * from './backend.service'; 2 | export * from './http-status-codes'; 3 | export * from './http-client-backend.service'; 4 | export * from './in-memory-web-api.module'; 5 | export * from './http-client-in-memory-web-api.module'; 6 | export * from './interfaces'; 7 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | export * from './backend.service'; 2 | export * from './http-status-codes'; 3 | export * from './http-client-backend.service'; 4 | export * from './in-memory-web-api.module'; 5 | export * from './http-client-in-memory-web-api.module'; 6 | export * from './interfaces'; 7 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wCAAwC,CAAC;AACvD,cAAc,cAAc,CAAC","sourcesContent":["export * from './backend.service';\nexport * from './http-status-codes';\nexport * from './http-client-backend.service';\nexport * from './in-memory-web-api.module';\nexport * from './http-client-in-memory-web-api.module';\nexport * from './interfaces';\n"]} -------------------------------------------------------------------------------- /index.metadata.json: -------------------------------------------------------------------------------- 1 | [{"__symbolic":"module","version":4,"metadata":{},"exports":[{"from":"./backend.service"},{"from":"./http-status-codes"},{"from":"./http-client-backend.service"},{"from":"./in-memory-web-api.module"},{"from":"./http-client-in-memory-web-api.module"},{"from":"./interfaces"}]}] -------------------------------------------------------------------------------- /interfaces.d.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs'; 2 | /** 3 | * Minimum definition needed by base class 4 | */ 5 | export interface HeadersCore { 6 | set(name: string, value: string): void | any; 7 | } 8 | /** 9 | * Interface for a class that creates an in-memory database 10 | * 11 | * Its `createDb` method creates a hash of named collections that represents the database 12 | * 13 | * For maximum flexibility, the service may define HTTP method overrides. 14 | * Such methods must match the spelling of an HTTP method in lower case (e.g, "get"). 15 | * If a request has a matching method, it will be called as in 16 | * `get(info: requestInfo, db: {})` where `db` is the database object described above. 17 | */ 18 | export declare abstract class InMemoryDbService { 19 | /** 20 | * Creates an in-memory "database" hash whose keys are collection names 21 | * and whose values are arrays of collection objects to return or update. 22 | * 23 | * returns Observable of the database because could have to create it asynchronously. 24 | * 25 | * This method must be safe to call repeatedly. 26 | * Each time it should return a new object with new arrays containing new item objects. 27 | * This condition allows the in-memory backend service to mutate the collections 28 | * and their items without touching the original source data. 29 | * 30 | * The in-mem backend service calls this method without a value the first time. 31 | * The service calls it with the `RequestInfo` when it receives a POST `commands/resetDb` request. 32 | * Your InMemoryDbService can adjust its behavior accordingly. 33 | */ 34 | abstract createDb(reqInfo?: RequestInfo): {} | Observable<{}> | Promise<{}>; 35 | } 36 | /** 37 | * Interface for InMemoryBackend configuration options 38 | */ 39 | export declare abstract class InMemoryBackendConfigArgs { 40 | /** 41 | * The base path to the api, e.g, 'api/'. 42 | * If not specified than `parseRequestUrl` assumes it is the first path segment in the request. 43 | */ 44 | apiBase?: string; 45 | /** 46 | * false (default) if search match should be case insensitive 47 | */ 48 | caseSensitiveSearch?: boolean; 49 | /** 50 | * false (default) put content directly inside the response body. 51 | * true: encapsulate content in a `data` property inside the response body, `{ data: ... }`. 52 | */ 53 | dataEncapsulation?: boolean; 54 | /** 55 | * delay (in ms) to simulate latency 56 | */ 57 | delay?: number; 58 | /** 59 | * false (default) should 204 when object-to-delete not found; true: 404 60 | */ 61 | delete404?: boolean; 62 | /** 63 | * host for this service, e.g., 'localhost' 64 | */ 65 | host?: string; 66 | /** 67 | * false (default) should pass unrecognized request URL through to original backend; true: 404 68 | */ 69 | passThruUnknownUrl?: boolean; 70 | /** 71 | * true (default) should NOT return the item (204) after a POST. false: return the item (200). 72 | */ 73 | post204?: boolean; 74 | /** 75 | * false (default) should NOT update existing item with POST. false: OK to update. 76 | */ 77 | post409?: boolean; 78 | /** 79 | * true (default) should NOT return the item (204) after a POST. false: return the item (200). 80 | */ 81 | put204?: boolean; 82 | /** 83 | * false (default) if item not found, create as new item; false: should 404. 84 | */ 85 | put404?: boolean; 86 | /** 87 | * root path _before_ any API call, e.g., '' 88 | */ 89 | rootPath?: string; 90 | } 91 | /** 92 | * InMemoryBackendService configuration options 93 | * Usage: 94 | * InMemoryWebApiModule.forRoot(InMemHeroService, {delay: 600}) 95 | * 96 | * or if providing separately: 97 | * provide(InMemoryBackendConfig, {useValue: {delay: 600}}), 98 | */ 99 | export declare class InMemoryBackendConfig implements InMemoryBackendConfigArgs { 100 | constructor(config?: InMemoryBackendConfigArgs); 101 | } 102 | /** Return information (UriInfo) about a URI */ 103 | export declare function parseUri(str: string): UriInfo; 104 | /** 105 | * 106 | * Interface for the result of the `parseRequestUrl` method: 107 | * Given URL "http://localhost:8080/api/customers/42?foo=1 the default implementation returns 108 | * base: 'api/' 109 | * collectionName: 'customers' 110 | * id: '42' 111 | * query: this.createQuery('foo=1') 112 | * resourceUrl: 'http://localhost/api/customers/' 113 | */ 114 | export interface ParsedRequestUrl { 115 | apiBase: string; 116 | collectionName: string; 117 | id: string; 118 | query: Map; 119 | resourceUrl: string; 120 | } 121 | export interface PassThruBackend { 122 | /** 123 | * Handle an HTTP request and return an Observable of HTTP response 124 | * Both the request type and the response type are determined by the supporting HTTP library. 125 | */ 126 | handle(req: any): Observable; 127 | } 128 | export declare function removeTrailingSlash(path: string): string; 129 | /** 130 | * Minimum definition needed by base class 131 | */ 132 | export interface RequestCore { 133 | url: string; 134 | urlWithParams?: string; 135 | } 136 | /** 137 | * Interface for object w/ info about the current request url 138 | * extracted from an Http Request. 139 | * Also holds utility methods and configuration data from this service 140 | */ 141 | export interface RequestInfo { 142 | req: RequestCore; 143 | apiBase: string; 144 | collectionName: string; 145 | collection: any; 146 | headers: HeadersCore; 147 | method: string; 148 | id: any; 149 | query: Map; 150 | resourceUrl: string; 151 | url: string; 152 | utils: RequestInfoUtilities; 153 | } 154 | /** 155 | * Interface for utility methods from this service instance. 156 | * Useful within an HTTP method override 157 | */ 158 | export interface RequestInfoUtilities { 159 | /** 160 | * Create a cold response Observable from a factory for ResponseOptions 161 | * the same way that the in-mem backend service does. 162 | * @param resOptionsFactory - creates ResponseOptions when observable is subscribed 163 | * @param withDelay - if true (default), add simulated latency delay from configuration 164 | */ 165 | createResponse$: (resOptionsFactory: () => ResponseOptions) => Observable; 166 | /** 167 | * Find first instance of item in collection by `item.id` 168 | * @param collection 169 | * @param id 170 | */ 171 | findById(collection: T[], id: any): T; 174 | /** return the current, active configuration which is a blend of defaults and overrides */ 175 | getConfig(): InMemoryBackendConfigArgs; 176 | /** Get the in-mem service's copy of the "database" */ 177 | getDb(): {}; 178 | /** Get JSON body from the request object */ 179 | getJsonBody(req: any): any; 180 | /** Get location info from a url, even on server where `document` is not defined */ 181 | getLocation(url: string): UriInfo; 182 | /** Get (or create) the "real" backend */ 183 | getPassThruBackend(): PassThruBackend; 184 | /** 185 | * return true if can determine that the collection's `item.id` is a number 186 | * */ 187 | isCollectionIdNumeric(collection: T[], collectionName: string): boolean; 190 | /** 191 | * Parses the request URL into a `ParsedRequestUrl` object. 192 | * Parsing depends upon certain values of `config`: `apiBase`, `host`, and `urlRoot`. 193 | */ 194 | parseRequestUrl(url: string): ParsedRequestUrl; 195 | } 196 | /** 197 | * Provide a `responseInterceptor` method of this type in your `inMemDbService` to 198 | * morph the response options created in the `collectionHandler`. 199 | */ 200 | export declare type ResponseInterceptor = (res: ResponseOptions, ri: RequestInfo) => ResponseOptions; 201 | export interface ResponseOptions { 202 | /** 203 | * String, Object, ArrayBuffer or Blob representing the body of the {@link Response}. 204 | */ 205 | body?: string | Object | ArrayBuffer | Blob; 206 | /** 207 | * Response headers 208 | */ 209 | headers?: HeadersCore; 210 | /** 211 | * Http {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html status code} 212 | * associated with the response. 213 | */ 214 | status?: number; 215 | /** 216 | * Status text for the status code 217 | */ 218 | statusText?: string; 219 | /** 220 | * request url 221 | */ 222 | url?: string; 223 | } 224 | /** Interface of information about a Uri */ 225 | export interface UriInfo { 226 | source: string; 227 | protocol: string; 228 | authority: string; 229 | userInfo: string; 230 | user: string; 231 | password: string; 232 | host: string; 233 | port: string; 234 | relative: string; 235 | path: string; 236 | directory: string; 237 | file: string; 238 | query: string; 239 | anchor: string; 240 | } 241 | -------------------------------------------------------------------------------- /interfaces.js: -------------------------------------------------------------------------------- 1 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 2 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 3 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 4 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 5 | return c > 3 && r && Object.defineProperty(target, key, r), r; 6 | }; 7 | var __metadata = (this && this.__metadata) || function (k, v) { 8 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 9 | }; 10 | import { Injectable } from '@angular/core'; 11 | /** 12 | * Interface for a class that creates an in-memory database 13 | * 14 | * Its `createDb` method creates a hash of named collections that represents the database 15 | * 16 | * For maximum flexibility, the service may define HTTP method overrides. 17 | * Such methods must match the spelling of an HTTP method in lower case (e.g, "get"). 18 | * If a request has a matching method, it will be called as in 19 | * `get(info: requestInfo, db: {})` where `db` is the database object described above. 20 | */ 21 | var InMemoryDbService = /** @class */ (function () { 22 | function InMemoryDbService() { 23 | } 24 | return InMemoryDbService; 25 | }()); 26 | export { InMemoryDbService }; 27 | /** 28 | * Interface for InMemoryBackend configuration options 29 | */ 30 | var InMemoryBackendConfigArgs = /** @class */ (function () { 31 | function InMemoryBackendConfigArgs() { 32 | } 33 | return InMemoryBackendConfigArgs; 34 | }()); 35 | export { InMemoryBackendConfigArgs }; 36 | ///////////////////////////////// 37 | /** 38 | * InMemoryBackendService configuration options 39 | * Usage: 40 | * InMemoryWebApiModule.forRoot(InMemHeroService, {delay: 600}) 41 | * 42 | * or if providing separately: 43 | * provide(InMemoryBackendConfig, {useValue: {delay: 600}}), 44 | */ 45 | var InMemoryBackendConfig = /** @class */ (function () { 46 | function InMemoryBackendConfig(config) { 47 | if (config === void 0) { config = {}; } 48 | Object.assign(this, { 49 | // default config: 50 | caseSensitiveSearch: false, 51 | dataEncapsulation: false, 52 | delay: 500, 53 | delete404: false, 54 | passThruUnknownUrl: false, 55 | post204: true, 56 | post409: false, 57 | put204: true, 58 | put404: false, 59 | apiBase: undefined, 60 | host: undefined, 61 | rootPath: undefined // default value is actually set in InMemoryBackendService ctor 62 | }, config); 63 | } 64 | InMemoryBackendConfig = __decorate([ 65 | Injectable(), 66 | __metadata("design:paramtypes", [InMemoryBackendConfigArgs]) 67 | ], InMemoryBackendConfig); 68 | return InMemoryBackendConfig; 69 | }()); 70 | export { InMemoryBackendConfig }; 71 | /** Return information (UriInfo) about a URI */ 72 | export function parseUri(str) { 73 | // Adapted from parseuri package - http://blog.stevenlevithan.com/archives/parseuri 74 | // tslint:disable-next-line:max-line-length 75 | var URL_REGEX = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/; 76 | var m = URL_REGEX.exec(str); 77 | var uri = { 78 | source: '', 79 | protocol: '', 80 | authority: '', 81 | userInfo: '', 82 | user: '', 83 | password: '', 84 | host: '', 85 | port: '', 86 | relative: '', 87 | path: '', 88 | directory: '', 89 | file: '', 90 | query: '', 91 | anchor: '' 92 | }; 93 | var keys = Object.keys(uri); 94 | var i = keys.length; 95 | while (i--) { 96 | uri[keys[i]] = m[i] || ''; 97 | } 98 | return uri; 99 | } 100 | export function removeTrailingSlash(path) { 101 | return path.replace(/\/$/, ''); 102 | } 103 | //# sourceMappingURL=interfaces.js.map -------------------------------------------------------------------------------- /interfaces.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"interfaces.js","sourceRoot":"","sources":["interfaces.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAU3C;;;;;;;;;EASE;AACF;IAAA;IAiBA,CAAC;IAAD,wBAAC;AAAD,CAAC,AAjBD,IAiBC;;AAED;;EAEE;AACF;IAAA;IAmDA,CAAC;IAAD,gCAAC;AAAD,CAAC,AAnDD,IAmDC;;AAED,iCAAiC;AACjC;;;;;;;EAOE;AAEF;IACE,+BAAY,MAAsC;QAAtC,uBAAA,EAAA,WAAsC;QAChD,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;YAClB,kBAAkB;YAClB,mBAAmB,EAAE,KAAK;YAC1B,iBAAiB,EAAE,KAAK;YACxB,KAAK,EAAE,GAAG;YACV,SAAS,EAAE,KAAK;YAChB,kBAAkB,EAAE,KAAK;YACzB,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,IAAI;YACZ,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,SAAS;YAClB,IAAI,EAAE,SAAS;YACf,QAAQ,EAAE,SAAS,CAAC,+DAA+D;SACpF,EAAE,MAAM,CAAC,CAAC;IACb,CAAC;IAjBU,qBAAqB;QADjC,UAAU,EAAE;yCAES,yBAAyB;OADlC,qBAAqB,CAkBjC;IAAD,4BAAC;CAAA,AAlBD,IAkBC;SAlBY,qBAAqB;AAoBlC,gDAAgD;AAChD,MAAM,UAAU,QAAQ,CAAC,GAAW;IAClC,mFAAmF;IACnF,2CAA2C;IAC3C,IAAM,SAAS,GAAG,kMAAkM,CAAC;IACrN,IAAM,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAM,GAAG,GAAY;QACnB,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE,EAAE;QACZ,SAAS,EAAE,EAAE;QACb,QAAQ,EAAE,EAAE;QACZ,IAAI,EAAE,EAAE;QACR,QAAQ,EAAE,EAAE;QACZ,IAAI,EAAE,EAAE;QACR,IAAI,EAAE,EAAE;QACR,QAAQ,EAAE,EAAE;QACZ,IAAI,EAAE,EAAE;QACR,SAAS,EAAE,EAAE;QACb,IAAI,EAAE,EAAE;QACR,KAAK,EAAE,EAAE;QACT,MAAM,EAAE,EAAE;KACX,CAAC;IACF,IAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;IAEpB,OAAO,CAAC,EAAE,EAAE;QAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;KAAE;IAC1C,OAAO,GAAG,CAAC;AACb,CAAC;AA4BD,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACjC,CAAC","sourcesContent":["import { Injectable } from '@angular/core';\nimport { Observable } from 'rxjs';\n\n/**\n * Minimum definition needed by base class\n */\nexport interface HeadersCore {\n set(name: string, value: string): void | any;\n}\n\n/**\n* Interface for a class that creates an in-memory database\n*\n* Its `createDb` method creates a hash of named collections that represents the database\n*\n* For maximum flexibility, the service may define HTTP method overrides.\n* Such methods must match the spelling of an HTTP method in lower case (e.g, \"get\").\n* If a request has a matching method, it will be called as in\n* `get(info: requestInfo, db: {})` where `db` is the database object described above.\n*/\nexport abstract class InMemoryDbService {\n /**\n * Creates an in-memory \"database\" hash whose keys are collection names\n * and whose values are arrays of collection objects to return or update.\n *\n * returns Observable of the database because could have to create it asynchronously.\n *\n * This method must be safe to call repeatedly.\n * Each time it should return a new object with new arrays containing new item objects.\n * This condition allows the in-memory backend service to mutate the collections\n * and their items without touching the original source data.\n *\n * The in-mem backend service calls this method without a value the first time.\n * The service calls it with the `RequestInfo` when it receives a POST `commands/resetDb` request.\n * Your InMemoryDbService can adjust its behavior accordingly.\n */\n abstract createDb(reqInfo?: RequestInfo): {} | Observable<{}> | Promise<{}>;\n}\n\n/**\n* Interface for InMemoryBackend configuration options\n*/\nexport abstract class InMemoryBackendConfigArgs {\n /**\n * The base path to the api, e.g, 'api/'.\n * If not specified than `parseRequestUrl` assumes it is the first path segment in the request.\n */\n apiBase?: string;\n /**\n * false (default) if search match should be case insensitive\n */\n caseSensitiveSearch?: boolean;\n /**\n * false (default) put content directly inside the response body.\n * true: encapsulate content in a `data` property inside the response body, `{ data: ... }`.\n */\n dataEncapsulation?: boolean;\n /**\n * delay (in ms) to simulate latency\n */\n delay?: number;\n /**\n * false (default) should 204 when object-to-delete not found; true: 404\n */\n delete404?: boolean;\n /**\n * host for this service, e.g., 'localhost'\n */\n host?: string;\n /**\n * false (default) should pass unrecognized request URL through to original backend; true: 404\n */\n passThruUnknownUrl?: boolean;\n /**\n * true (default) should NOT return the item (204) after a POST. false: return the item (200).\n */\n post204?: boolean;\n /**\n * false (default) should NOT update existing item with POST. false: OK to update.\n */\n post409?: boolean;\n /**\n * true (default) should NOT return the item (204) after a POST. false: return the item (200).\n */\n put204?: boolean;\n /**\n * false (default) if item not found, create as new item; false: should 404.\n */\n put404?: boolean;\n /**\n * root path _before_ any API call, e.g., ''\n */\n rootPath?: string;\n}\n\n/////////////////////////////////\n/**\n* InMemoryBackendService configuration options\n* Usage:\n* InMemoryWebApiModule.forRoot(InMemHeroService, {delay: 600})\n*\n* or if providing separately:\n* provide(InMemoryBackendConfig, {useValue: {delay: 600}}),\n*/\n@Injectable()\nexport class InMemoryBackendConfig implements InMemoryBackendConfigArgs {\n constructor(config: InMemoryBackendConfigArgs = {}) {\n Object.assign(this, {\n // default config:\n caseSensitiveSearch: false,\n dataEncapsulation: false, // do NOT wrap content within an object with a `data` property\n delay: 500, // simulate latency by delaying response\n delete404: false, // don't complain if can't find entity to delete\n passThruUnknownUrl: false, // 404 if can't process URL\n post204: true, // don't return the item after a POST\n post409: false, // don't update existing item with that ID\n put204: true, // don't return the item after a PUT\n put404: false, // create new item if PUT item with that ID not found\n apiBase: undefined, // assumed to be the first path segment\n host: undefined, // default value is actually set in InMemoryBackendService ctor\n rootPath: undefined // default value is actually set in InMemoryBackendService ctor\n }, config);\n }\n}\n\n/** Return information (UriInfo) about a URI */\nexport function parseUri(str: string): UriInfo {\n // Adapted from parseuri package - http://blog.stevenlevithan.com/archives/parseuri\n // tslint:disable-next-line:max-line-length\n const URL_REGEX = /^(?:(?![^:@]+:[^:@\\/]*@)([^:\\/?#.]+):)?(?:\\/\\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\\/?#]*)(?::(\\d*))?)(((\\/(?:[^?#](?![^?#\\/]*\\.[^?#\\/.]+(?:[?#]|$)))*\\/?)?([^?#\\/]*))(?:\\?([^#]*))?(?:#(.*))?)/;\n const m = URL_REGEX.exec(str);\n const uri: UriInfo = {\n source: '',\n protocol: '',\n authority: '',\n userInfo: '',\n user: '',\n password: '',\n host: '',\n port: '',\n relative: '',\n path: '',\n directory: '',\n file: '',\n query: '',\n anchor: ''\n };\n const keys = Object.keys(uri);\n let i = keys.length;\n\n while (i--) { uri[keys[i]] = m[i] || ''; }\n return uri;\n}\n\n/**\n *\n * Interface for the result of the `parseRequestUrl` method:\n * Given URL \"http://localhost:8080/api/customers/42?foo=1 the default implementation returns\n * base: 'api/'\n * collectionName: 'customers'\n * id: '42'\n * query: this.createQuery('foo=1')\n * resourceUrl: 'http://localhost/api/customers/'\n */\nexport interface ParsedRequestUrl {\n apiBase: string; // the slash-terminated \"base\" for api requests (e.g. `api/`)\n collectionName: string; // the name of the collection of data items (e.g.,`customers`)\n id: string; // the (optional) id of the item in the collection (e.g., `42`)\n query: Map; // the query parameters;\n resourceUrl: string; // the effective URL for the resource (e.g., 'http://localhost/api/customers/')\n}\n\nexport interface PassThruBackend {\n /**\n * Handle an HTTP request and return an Observable of HTTP response\n * Both the request type and the response type are determined by the supporting HTTP library.\n */\n handle(req: any): Observable;\n}\n\nexport function removeTrailingSlash(path: string) {\n return path.replace(/\\/$/, '');\n}\n\n/**\n * Minimum definition needed by base class\n */\nexport interface RequestCore {\n url: string; // request URL\n urlWithParams?: string; // request URL with query parameters added by `HttpParams`\n}\n\n/**\n* Interface for object w/ info about the current request url\n* extracted from an Http Request.\n* Also holds utility methods and configuration data from this service\n*/\nexport interface RequestInfo {\n req: RequestCore; // concrete type depends upon the Http library\n apiBase: string;\n collectionName: string;\n collection: any;\n headers: HeadersCore;\n method: string;\n id: any;\n query: Map;\n resourceUrl: string;\n url: string; // request URL\n utils: RequestInfoUtilities;\n}\n\n/**\n * Interface for utility methods from this service instance.\n * Useful within an HTTP method override\n */\nexport interface RequestInfoUtilities {\n /**\n * Create a cold response Observable from a factory for ResponseOptions\n * the same way that the in-mem backend service does.\n * @param resOptionsFactory - creates ResponseOptions when observable is subscribed\n * @param withDelay - if true (default), add simulated latency delay from configuration\n */\n createResponse$: (resOptionsFactory: () => ResponseOptions) => Observable;\n\n /**\n * Find first instance of item in collection by `item.id`\n * @param collection\n * @param id\n */\n findById(collection: T[], id: any): T;\n\n /** return the current, active configuration which is a blend of defaults and overrides */\n getConfig(): InMemoryBackendConfigArgs;\n\n /** Get the in-mem service's copy of the \"database\" */\n getDb(): {};\n\n /** Get JSON body from the request object */\n getJsonBody(req: any): any;\n\n /** Get location info from a url, even on server where `document` is not defined */\n getLocation(url: string): UriInfo;\n\n /** Get (or create) the \"real\" backend */\n getPassThruBackend(): PassThruBackend;\n\n /**\n * return true if can determine that the collection's `item.id` is a number\n * */\n isCollectionIdNumeric(collection: T[], collectionName: string): boolean;\n\n /**\n * Parses the request URL into a `ParsedRequestUrl` object.\n * Parsing depends upon certain values of `config`: `apiBase`, `host`, and `urlRoot`.\n */\n parseRequestUrl(url: string): ParsedRequestUrl;\n}\n\n/**\n * Provide a `responseInterceptor` method of this type in your `inMemDbService` to\n * morph the response options created in the `collectionHandler`.\n */\nexport type ResponseInterceptor = (res: ResponseOptions, ri: RequestInfo) => ResponseOptions;\n\nexport interface ResponseOptions {\n /**\n * String, Object, ArrayBuffer or Blob representing the body of the {@link Response}.\n */\n body?: string | Object | ArrayBuffer | Blob;\n\n /**\n * Response headers\n */\n headers?: HeadersCore;\n\n /**\n * Http {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html status code}\n * associated with the response.\n */\n status?: number;\n\n /**\n * Status text for the status code\n */\n statusText?: string;\n /**\n * request url\n */\n url?: string;\n}\n\n/** Interface of information about a Uri */\nexport interface UriInfo {\n source: string;\n protocol: string;\n authority: string;\n userInfo: string;\n user: string;\n password: string;\n host: string;\n port: string;\n relative: string;\n path: string;\n directory: string;\n file: string;\n query: string;\n anchor: string;\n}\n"]} -------------------------------------------------------------------------------- /interfaces.metadata.json: -------------------------------------------------------------------------------- 1 | [{"__symbolic":"module","version":4,"metadata":{"HeadersCore":{"__symbolic":"interface"},"InMemoryDbService":{"__symbolic":"class","members":{"createDb":[{"__symbolic":"method"}]}},"InMemoryBackendConfigArgs":{"__symbolic":"class"},"InMemoryBackendConfig":{"__symbolic":"class","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Injectable","line":104,"character":1}}],"members":{"__ctor__":[{"__symbolic":"constructor","parameters":[{"__symbolic":"reference","name":"InMemoryBackendConfigArgs"}]}]}},"parseUri":{"__symbolic":"function"},"ParsedRequestUrl":{"__symbolic":"interface"},"PassThruBackend":{"__symbolic":"interface"},"removeTrailingSlash":{"__symbolic":"function","parameters":["path"],"value":{"__symbolic":"call","expression":{"__symbolic":"select","expression":{"__symbolic":"reference","name":"path"},"member":"replace"},"arguments":[{"__symbolic":"error","message":"Expression form not supported","line":181,"character":22},""]}},"RequestCore":{"__symbolic":"interface"},"RequestInfo":{"__symbolic":"interface"},"RequestInfoUtilities":{"__symbolic":"interface"},"ResponseInterceptor":{"__symbolic":"interface"},"ResponseOptions":{"__symbolic":"interface"},"UriInfo":{"__symbolic":"interface"}}}] -------------------------------------------------------------------------------- /karma-test-shim.js: -------------------------------------------------------------------------------- 1 | // /*global jasmine, __karma__, window*/ 2 | Error.stackTraceLimit = 0; // "No stacktrace"" is usually best for app testing. 3 | 4 | // Uncomment to get full stacktrace output. Sometimes helpful, usually not. 5 | // Error.stackTraceLimit = Infinity; // 6 | 7 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000; 8 | 9 | // builtPaths: root paths for output ("built") files 10 | // get from karma.config.js, then prefix with '/src/' (default is 'app/') 11 | var builtPaths = (__karma__.config.builtPaths || ['src/']) 12 | .map(function(p) { return '/base/'+p;}); 13 | 14 | __karma__.loaded = function () { }; 15 | 16 | function isJsFile(path) { 17 | return path.slice(-3) == '.js'; 18 | } 19 | 20 | function isSpecFile(path) { 21 | return /\.spec\.(.*\.)?js$/.test(path); 22 | } 23 | 24 | // Is a "built" file if is JavaScript file in one of the "built" folders 25 | function isBuiltFile(path) { 26 | return isJsFile(path) && 27 | builtPaths.reduce(function(keep, bp) { 28 | return keep || (path.substr(0, bp.length) === bp); 29 | }, false); 30 | } 31 | 32 | var allSpecFiles = Object.keys(window.__karma__.files) 33 | .filter(isSpecFile) 34 | .filter(isBuiltFile); 35 | 36 | System.config({ 37 | baseURL: 'base/src', 38 | // add packages for application folders other than `app/` 39 | packages: { 40 | // a testing folder 41 | 'testing': { main: 'index.js', defaultExtension: 'js' }, 42 | // other app folders 43 | 'in-mem': { main: 'index.js', defaultExtension: 'js' } 44 | }, 45 | 46 | // Assume npm: is set in `paths` in systemjs.config 47 | // Map the angular testing umd bundles 48 | map: { 49 | '@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js', 50 | '@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js', 51 | '@angular/common/http/testing': 'npm:@angular/common/bundles/common-http-testing.umd.js', 52 | '@angular/compiler/testing': 'npm:@angular/compiler/bundles/compiler-testing.umd.js', 53 | '@angular/platform-browser/testing': 'npm:@angular/platform-browser/bundles/platform-browser-testing.umd.js', 54 | '@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js', 55 | '@angular/router/testing': 'npm:@angular/router/bundles/router-testing.umd.js', 56 | '@angular/forms/testing': 'npm:@angular/forms/bundles/forms-testing.umd.js', 57 | }, 58 | }); 59 | 60 | System.import('systemjs.config.js') 61 | .then(initTestBed) 62 | .then(initTesting); 63 | 64 | function initTestBed(){ 65 | return Promise.all([ 66 | System.import('@angular/core/testing'), 67 | System.import('@angular/platform-browser-dynamic/testing') 68 | ]) 69 | 70 | .then(function (providers) { 71 | var coreTesting = providers[0]; 72 | var browserTesting = providers[1]; 73 | 74 | coreTesting.TestBed.initTestEnvironment( 75 | browserTesting.BrowserDynamicTestingModule, 76 | browserTesting.platformBrowserDynamicTesting()); 77 | }) 78 | } 79 | 80 | // Import all spec files and start karma 81 | function initTesting () { 82 | return Promise.all( 83 | allSpecFiles.map(function (moduleName) { 84 | return System.import(moduleName); 85 | }) 86 | ) 87 | .then(__karma__.start, __karma__.error); 88 | } 89 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // #docregion 2 | module.exports = function(config) { 3 | 4 | var appBase = 'src/'; // transpiled app JS and map files 5 | var appAssets = '/base/app/'; // component assets fetched by Angular's compiler 6 | 7 | // Testing helpers (optional) are conventionally in a folder called `testing` 8 | var testingBase = 'src/testing/'; // transpiled test JS and map files 9 | var testingSrcBase = 'src/testing/'; // test source TS files 10 | 11 | config.set({ 12 | basePath: '', 13 | frameworks: ['jasmine-ajax', 'jasmine'], 14 | 15 | plugins: [ 16 | require('karma-jasmine-ajax'), 17 | require('karma-jasmine'), 18 | require('karma-chrome-launcher'), 19 | require('karma-jasmine-html-reporter') 20 | ], 21 | 22 | client: { 23 | builtPaths: [appBase, testingBase], // add more spec base paths as needed 24 | clearContext: false // leave Jasmine Spec Runner output visible in browser 25 | }, 26 | 27 | customLaunchers: { 28 | // From the CLI. Not used here but interesting 29 | // chrome setup for travis CI using chromium 30 | Chrome_travis_ci: { 31 | base: 'Chrome', 32 | flags: ['--no-sandbox'] 33 | } 34 | }, 35 | 36 | files: [ 37 | // System.js for module loading 38 | 'node_modules/systemjs/dist/system.src.js', 39 | 40 | // Polyfills 41 | 'node_modules/core-js/client/shim.js', 42 | 43 | // zone.js 44 | 'node_modules/zone.js/dist/zone.js', 45 | 'node_modules/zone.js/dist/long-stack-trace-zone.js', 46 | 'node_modules/zone.js/dist/proxy.js', 47 | 'node_modules/zone.js/dist/sync-test.js', 48 | 'node_modules/zone.js/dist/jasmine-patch.js', 49 | 'node_modules/zone.js/dist/async-test.js', 50 | 'node_modules/zone.js/dist/fake-async-test.js', 51 | 52 | // RxJs 53 | { pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false }, 54 | { pattern: 'node_modules/rxjs/**/*.js.map', included: false, watched: false }, 55 | 56 | // tslib (TS helper fns such as `__extends`) 57 | { pattern: 'node_modules/tslib/**/*.js', included: false, watched: false }, 58 | { pattern: 'node_modules/tslib/**/*.js.map', included: false, watched: false }, 59 | 60 | // Paths loaded via module imports: 61 | // Angular itself 62 | { pattern: 'node_modules/@angular/**/*.js', included: false, watched: false }, 63 | { pattern: 'node_modules/@angular/**/*.js.map', included: false, watched: false }, 64 | 65 | { pattern: appBase + '/systemjs.config.js', included: false, watched: false }, 66 | 67 | 'karma-test-shim.js', // optionally extend SystemJS mapping e.g., with barrels 68 | 69 | // transpiled application & spec code paths loaded via module imports 70 | { pattern: appBase + '**/*.js', included: false, watched: true }, 71 | { pattern: testingBase + '**/*.js', included: false, watched: true }, 72 | 73 | 74 | // Asset (HTML & CSS) paths loaded via Angular's component compiler 75 | // (these paths need to be rewritten, see proxies section) 76 | { pattern: appBase + '**/*.html', included: false, watched: true }, 77 | { pattern: appBase + '**/*.css', included: false, watched: true }, 78 | 79 | // Paths for debugging with source maps in dev tools 80 | { pattern: appBase + '**/*.ts', included: false, watched: false }, 81 | { pattern: appBase + '**/*.js.map', included: false, watched: false }, 82 | { pattern: testingSrcBase + '**/*.ts', included: false, watched: false }, 83 | { pattern: testingBase + '**/*.js.map', included: false, watched: false} 84 | ], 85 | 86 | // Proxied base paths for loading assets 87 | proxies: { 88 | // required for modules fetched by SystemJS 89 | '/base/src/node_modules/': '/base/node_modules/' 90 | }, 91 | 92 | exclude: [], 93 | preprocessors: {}, 94 | reporters: ['progress', 'kjhtml'], 95 | 96 | port: 9876, 97 | colors: true, 98 | logLevel: config.LOG_INFO, 99 | autoWatch: true, 100 | browsers: ['Chrome'], 101 | singleRun: false 102 | }) 103 | } 104 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-in-memory-web-api", 3 | "version": "0.10.0", 4 | "description": "An in-memory web api for Angular demos and tests", 5 | "main": "bundles/in-memory-web-api.umd.js", 6 | "module": "index.js", 7 | "scripts": { 8 | "build": "tsc", 9 | "build:watch": "tsc -w", 10 | "build:lib": "gulp build", 11 | "lint": "tslint ./src/*.ts -t verbose -e ./src/*.d.ts", 12 | "start": "concurrently \"npm run build:watch\" \"npm run serve\"", 13 | "pretest": "npm run build", 14 | "test": "concurrently \"npm run build:watch\" \"karma start karma.conf.js\"", 15 | "pretest:once": "npm run build", 16 | "test:once": "karma start karma.conf.js --single-run", 17 | "tsc": "tsc", 18 | "tsc:w": "tsc -w" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/angular/in-memory-web-api.git" 23 | }, 24 | "keywords": [], 25 | "author": "", 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/angular/in-memory-web-api/issues" 29 | }, 30 | "homepage": "https://github.com/angular/in-memory-web-api#readme", 31 | "peerDependencies": { 32 | "@angular/common": ">=8.0.0", 33 | "@angular/core": ">=8.0.0", 34 | "rxjs": "^6.0.0" 35 | }, 36 | "devDependencies": { 37 | "@angular/animations": "^8.0.0", 38 | "@angular/common": "^8.0.0", 39 | "@angular/compiler": "^8.0.0", 40 | "@angular/compiler-cli": "^8.0.0", 41 | "@angular/core": "^8.0.0", 42 | "@angular/platform-browser": "^8.0.0", 43 | "@angular/platform-browser-dynamic": "^8.0.0", 44 | "@angular/platform-server": "^8.0.0", 45 | "@types/jasmine": "2.8.7", 46 | "@types/jasminewd2": "~2.0.3", 47 | "@types/jasmine-ajax": "^3.1.37", 48 | "@types/node": "^8.10.0", 49 | "canonical-path": "0.0.2", 50 | "concurrently": "^3.0.0", 51 | "core-js": "^2.4.1", 52 | "del": "^2.2.2", 53 | "gulp": "^4.0.2", 54 | "gulp-bump": "^3.1.3", 55 | "gulp-load-plugins": "^2.0.3", 56 | "gulp-task-listing": "^1.1.0", 57 | "http-server": "^0.9.0", 58 | "jasmine-ajax": "^3.3.1", 59 | "jasmine-core": "~2.8.0", 60 | "jasmine-spec-reporter": "~4.2.1", 61 | "karma": "~2.0.0", 62 | "karma-chrome-launcher": "~2.2.0", 63 | "karma-cli": "~1.0.1", 64 | "karma-coverage-istanbul-reporter": "~2.0.0", 65 | "karma-jasmine": "~1.1.1", 66 | "karma-jasmine-html-reporter": "^0.2.2", 67 | "karma-jasmine-ajax": "^0.1.13", 68 | "karma-phantomjs-launcher": "^1.0.4", 69 | "karma-sourcemap-loader": "^0.3.7", 70 | "karma-webpack": "^3.0.0", 71 | "lite-server": "^2.3.0", 72 | "lodash": "^4.17.10", 73 | "rimraf": "^2.6.2", 74 | "rollup": "^0.58.2", 75 | "rollup-stream": "^1.24.1", 76 | "rxjs": "^6.2.0", 77 | "systemjs": "0.21.3", 78 | "tslint": "^5.10.0", 79 | "typescript": "3.4.5", 80 | "vinyl-source-stream": "^2.0.0", 81 | "webpack": "2.2.1", 82 | "yargs": "^11.0.0", 83 | "zone.js": "^0.9.1" 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | const globals = { 2 | '@angular/core': 'ng.core', 3 | '@angular/common/http': 'ng.common.http', 4 | 'rxjs': 'rxjs', 5 | 'rxjs/operators': 'rxjs.operators' 6 | }; 7 | 8 | export default { 9 | input: './src/in-mem/index.js', 10 | file: './bundles/in-memory-web-api.umd.js', 11 | format: 'umd', 12 | name: 'ng.inMemoryWebApi', 13 | sourcemap: true, 14 | globals, 15 | external: Object.keys(globals) 16 | } 17 | -------------------------------------------------------------------------------- /src/app/hero-in-mem-data-override.service.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This is an example of a Hero-oriented InMemoryDbService with method overrides. 3 | */ 4 | import { Injectable } from '@angular/core'; 5 | 6 | // tslint:disable-next-line:no-unused-variable 7 | import { Observable } from 'rxjs'; 8 | 9 | import { ParsedRequestUrl, RequestInfo, RequestInfoUtilities, ResponseOptions } from '../in-mem/interfaces'; 10 | 11 | import { getStatusText, STATUS } from '../in-mem/http-status-codes'; 12 | 13 | import { HeroInMemDataService } from './hero-in-mem-data.service'; 14 | 15 | const villains = [ 16 | // deliberately using string ids that look numeric 17 | {id: 100, name: 'Snidley Wipsnatch'}, 18 | {id: 101, name: 'Boris Badenov'}, 19 | {id: 103, name: 'Natasha Fatale'} 20 | ]; 21 | 22 | // Pseudo guid generator 23 | function guid() { 24 | function s4() { 25 | return Math.floor((1 + Math.random()) * 0x10000) 26 | .toString(16) 27 | .substring(1); 28 | } 29 | return s4() + s4() + '-' + s4() + '-' + s4() + '-' + 30 | s4() + '-' + s4() + s4() + s4(); 31 | } 32 | 33 | @Injectable() 34 | export class HeroInMemDataOverrideService extends HeroInMemDataService { 35 | 36 | // Overrides id generator and delivers next available `id`, starting with 1001. 37 | genId(collection: T[], collectionName: string): any { 38 | if (collectionName === 'nobodies') { 39 | console.log('genId override for \'nobodies\''); 40 | return guid(); 41 | } else if (collection) { 42 | console.log(`genId override for '${collectionName}'`); 43 | return 1 + collection.reduce((prev, curr) => Math.max(prev, curr.id || 0), 1000); 44 | } 45 | } 46 | 47 | // HTTP GET interceptor 48 | get(reqInfo: RequestInfo) { 49 | const collectionName = reqInfo.collectionName; 50 | if (collectionName === 'villains') { 51 | return this.getVillains(reqInfo); 52 | } 53 | return undefined; // let the default GET handle all others 54 | } 55 | 56 | // HTTP GET interceptor handles requests for villains 57 | private getVillains(reqInfo: RequestInfo) { 58 | return reqInfo.utils.createResponse$(() => { 59 | console.log('HTTP GET override'); 60 | 61 | const collection = villains.slice(); 62 | const dataEncapsulation = reqInfo.utils.getConfig().dataEncapsulation; 63 | const id = reqInfo.id; 64 | 65 | // tslint:disable-next-line:triple-equals 66 | const data = id == undefined ? collection : reqInfo.utils.findById(collection, id); 67 | 68 | const options: ResponseOptions = data ? 69 | { 70 | body: dataEncapsulation ? { data } : data, 71 | status: STATUS.OK 72 | } : 73 | { 74 | body: { error: `'Villains' with id='${id}' not found` }, 75 | status: STATUS.NOT_FOUND 76 | }; 77 | return this.finishOptions(options, reqInfo); 78 | }); 79 | } 80 | 81 | // parseRequestUrl override 82 | // Do this to manipulate the request URL or the parsed result 83 | // into something your data store can handle. 84 | // This example turns a request for `/foo/heroes` into just `/heroes`. 85 | // It leaves other URLs untouched and forwards to the default parser. 86 | // It also logs the result of the default parser. 87 | parseRequestUrl(url: string, utils: RequestInfoUtilities): ParsedRequestUrl { 88 | const newUrl = url.replace(/\/foo\/heroes/, '/heroes'); 89 | // console.log('newUrl', newUrl); 90 | const parsed = utils.parseRequestUrl(newUrl); 91 | console.log(`parseRequestUrl override of '${url}':`, parsed); 92 | return parsed; 93 | } 94 | 95 | // intercept ResponseOptions from default HTTP method handlers 96 | // add a response header and report interception to console.log 97 | responseInterceptor(resOptions: ResponseOptions, reqInfo: RequestInfo) { 98 | 99 | resOptions.headers = resOptions.headers.set('x-test', 'test-header'); 100 | const method = reqInfo.method.toUpperCase(); 101 | const body = JSON.stringify(resOptions); 102 | console.log(`responseInterceptor: ${method} ${reqInfo.req.url}: \n${body}`); 103 | 104 | return resOptions; 105 | } 106 | 107 | /////////// helpers /////////////// 108 | 109 | private finishOptions(options: ResponseOptions, {headers, url}: RequestInfo) { 110 | options.statusText = getStatusText(options.status); 111 | options.headers = headers; 112 | options.url = url; 113 | return options; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/app/hero-in-mem-data.service.ts: -------------------------------------------------------------------------------- 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, of } from 'rxjs'; 16 | import { delay } from 'rxjs/operators'; 17 | // tslint:enable:no-unused-variable 18 | 19 | @Injectable() 20 | export class HeroInMemDataService implements InMemoryDbService { 21 | createDb(reqInfo?: RequestInfo) { 22 | 23 | const heroes = [ 24 | { id: 1, name: 'Windstorm' }, 25 | { id: 2, name: 'Bombasto' }, 26 | { id: 3, name: 'Magneta' }, 27 | { id: 4, name: 'Tornado' } 28 | ]; 29 | 30 | const nobodies: any[] = [ ]; 31 | 32 | // entities with string ids that look like numbers 33 | const stringers = [ 34 | { id: '10', name: 'Bob String'}, 35 | { id: '20', name: 'Jill String'} 36 | ]; 37 | 38 | // default returnType 39 | let returnType = 'object'; 40 | // let returnType = 'observable'; 41 | // let returnType = 'promise'; 42 | 43 | // demonstrate POST commands/resetDb 44 | // this example clears the collections if the request body tells it to do so 45 | if (reqInfo) { 46 | const body = reqInfo.utils.getJsonBody(reqInfo.req) || {}; 47 | if (body.clear === true) { 48 | heroes.length = 0; 49 | nobodies.length = 0; 50 | stringers.length = 0; 51 | } 52 | 53 | // 'returnType` can be 'object' | 'observable' | 'promise' 54 | returnType = body.returnType || 'object'; 55 | } 56 | const db = { heroes, nobodies, stringers }; 57 | 58 | switch (returnType) { 59 | case ('observable'): 60 | return of(db).pipe(delay(10)); 61 | case ('promise'): 62 | return new Promise(resolve => { 63 | setTimeout(() => resolve(db), 10); 64 | }); 65 | default: 66 | return db; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/app/hero.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, TestBed } from '@angular/core/testing'; 2 | 3 | import { concatMap, tap, map } from 'rxjs/operators'; 4 | 5 | import { failure } from '../testing'; 6 | 7 | import { Hero } from './hero'; 8 | import { HeroService } from './hero.service'; 9 | 10 | /** 11 | * Common tests for the HeroService, whether implemented with Http or HttpClient 12 | * Assumes that TestBed has been configured appropriately before created and run. 13 | * 14 | * Tests with extended test expirations accommodate the default (simulated) latency delay. 15 | * Ideally configured for short or no delay. 16 | */ 17 | export class HeroServiceCoreSpec { 18 | 19 | run() { 20 | 21 | describe('HeroService core', () => { 22 | 23 | let heroService: HeroService; 24 | 25 | beforeEach(function() { 26 | heroService = TestBed.get(HeroService); 27 | }); 28 | 29 | it('can get heroes', async(() => { 30 | heroService.getHeroes() 31 | .subscribe( 32 | heroes => { 33 | // console.log(heroes); 34 | expect(heroes.length).toBeGreaterThan(0, 'should have heroes'); 35 | }, 36 | failure 37 | ); 38 | })); 39 | 40 | it('can get hero w/ id=1', async(() => { 41 | heroService.getHero(1) 42 | .subscribe( 43 | hero => { 44 | // console.log(hero); 45 | expect(hero.name).toBe('Windstorm'); 46 | }, 47 | () => fail('getHero failed') 48 | ); 49 | })); 50 | 51 | it('should 404 when hero id not found', async(() => { 52 | const id = 123456; 53 | heroService.getHero(id) 54 | .subscribe( 55 | () => fail(`should not have found hero for id='${id}'`), 56 | err => { 57 | expect(err.status).toBe(404, 'should have 404 status'); 58 | } 59 | ); 60 | })); 61 | 62 | it('can add a hero', async(() => { 63 | heroService.addHero('FunkyBob').pipe( 64 | tap(hero => { 65 | // console.log(hero); 66 | expect(hero.name).toBe('FunkyBob'); 67 | }), 68 | // Get the new hero by its generated id 69 | concatMap(hero => heroService.getHero(hero.id)), 70 | ).subscribe( 71 | hero => { 72 | expect(hero.name).toBe('FunkyBob'); 73 | }, 74 | err => failure('re-fetch of new hero failed') 75 | ); 76 | }), 10000); 77 | 78 | it('can delete a hero', async(() => { 79 | const id = 1; 80 | heroService.deleteHero(id) 81 | .subscribe( 82 | (_: {}) => { 83 | expect(_).toBeDefined(); 84 | }, 85 | failure 86 | ); 87 | })); 88 | 89 | it('should allow delete of non-existent hero', async(() => { 90 | const id = 123456; 91 | heroService.deleteHero(id) 92 | .subscribe( 93 | (_: {}) => { 94 | expect(_).toBeDefined(); 95 | }, 96 | failure 97 | ); 98 | })); 99 | 100 | it('can search for heroes by name containing "a"', async(() => { 101 | heroService.searchHeroes('a') 102 | .subscribe( 103 | (heroes: Hero[]) => { 104 | expect(heroes.length).toBe(3, 'should find 3 heroes with letter "a"'); 105 | }, 106 | failure 107 | ); 108 | })); 109 | 110 | it('can update existing hero', async(() => { 111 | const id = 1; 112 | heroService.getHero(id).pipe( 113 | concatMap(hero => { 114 | hero.name = 'Thunderstorm'; 115 | return heroService.updateHero(hero); 116 | }), 117 | concatMap(() => { 118 | return heroService.getHero(id); 119 | }) 120 | ).subscribe( 121 | hero => { 122 | console.log(hero); 123 | expect(hero.name).toBe('Thunderstorm'); 124 | }, 125 | err => fail('re-fetch of updated hero failed') 126 | ); 127 | }), 10000); 128 | 129 | it('should create new hero when try to update non-existent hero', async(() => { 130 | const falseHero = new Hero(12321, 'DryMan'); 131 | 132 | heroService.updateHero(falseHero) 133 | .subscribe( 134 | hero => { 135 | expect(hero.name).toBe(falseHero.name); 136 | }, 137 | failure 138 | ); 139 | })); 140 | 141 | }); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/app/hero.service.ts: -------------------------------------------------------------------------------- 1 | import { Hero } from './hero'; 2 | import { Observable } from 'rxjs'; 3 | 4 | export abstract class HeroService { 5 | heroesUrl = 'api/heroes'; // URL to web api 6 | 7 | abstract getHeroes (): Observable; 8 | abstract getHero(id: number): Observable; 9 | abstract addHero (name: string): Observable; 10 | abstract deleteHero (hero: Hero | number): Observable; 11 | abstract searchHeroes(term: string): Observable; 12 | abstract updateHero (hero: Hero): Observable; 13 | } 14 | -------------------------------------------------------------------------------- /src/app/hero.ts: -------------------------------------------------------------------------------- 1 | export class Hero { 2 | constructor(public id = 0, public name = '') { } 3 | clone() { return new Hero(this.id, this.name); } 4 | } 5 | -------------------------------------------------------------------------------- /src/app/http-client-hero.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable }from '@angular/core'; 2 | import { HttpClient, HttpHeaders, HttpParams }from '@angular/common/http'; 3 | 4 | import { Observable, throwError } from 'rxjs'; 5 | import { tap, catchError, map } from 'rxjs/operators'; 6 | 7 | import { Hero } from './hero'; 8 | import { HeroService } from './hero.service'; 9 | 10 | const cudOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' })}; 11 | 12 | @Injectable() 13 | export class HttpClientHeroService extends HeroService { 14 | 15 | constructor (private http: HttpClient) { 16 | super(); 17 | } 18 | 19 | getHeroes (): Observable { 20 | return this.http.get(this.heroesUrl).pipe( 21 | // tap(data => console.log(data)), // eyeball results in the console 22 | catchError(this.handleError) 23 | ); 24 | } 25 | 26 | // This get-by-id will 404 when id not found 27 | getHero(id: number): Observable { 28 | const url = `${this.heroesUrl}/${id}`; 29 | return this.http.get(url).pipe( 30 | catchError(this.handleError) 31 | ); 32 | } 33 | 34 | // This get-by-id does not 404; returns undefined when id not found 35 | // getHero(id: number): Observable { 36 | // const url = `${this._heroesUrl}/?id=${id}`; 37 | // return this.http.get(url) 38 | // .map(heroes => heroes[0] as Hero) 39 | // .catch(this.handleError); 40 | // } 41 | 42 | addHero (name: string): Observable { 43 | const hero = { name }; 44 | 45 | return this.http.post(this.heroesUrl, hero, cudOptions).pipe( 46 | catchError(this.handleError) 47 | ); 48 | } 49 | 50 | deleteHero (hero: Hero | number): Observable { 51 | const id = typeof hero === 'number' ? hero : hero.id; 52 | const url = `${this.heroesUrl}/${id}`; 53 | 54 | return this.http.delete(url, cudOptions).pipe( 55 | catchError(this.handleError) 56 | ); 57 | } 58 | 59 | searchHeroes(term: string): Observable { 60 | term = term.trim(); 61 | // add safe, encoded search parameter if term is present 62 | const options = term ? 63 | { params: new HttpParams().set('name', term) } : {}; 64 | 65 | return this.http.get(this.heroesUrl, options).pipe( 66 | catchError(this.handleError) 67 | ); 68 | } 69 | 70 | updateHero (hero: Hero): Observable { 71 | return this.http.put(this.heroesUrl, hero, cudOptions).pipe( 72 | catchError(this.handleError) 73 | ); 74 | } 75 | 76 | private handleError (error: any) { 77 | // In a real world app, we might send the error to remote logging infrastructure 78 | // and reformat for user consumption 79 | console.error(error); // log to console instead 80 | return throwError(error); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/in-mem/backend.service.ngsummary.json: -------------------------------------------------------------------------------- 1 | {"moduleName":null,"summaries":[{"symbol":{"__symbol":0,"members":[]},"metadata":{"__symbolic":"class","members":{"__ctor__":[{"__symbolic":"constructor","parameters":[{"__symbol":1,"members":[]},{"__symbol":2,"members":[]}]}],"handleRequest":[{"__symbolic":"method"}],"handleRequest_":[{"__symbolic":"method"}],"addDelay":[{"__symbolic":"method"}],"applyQuery":[{"__symbolic":"method"}],"bind":[{"__symbolic":"method"}],"bodify":[{"__symbolic":"method"}],"clone":[{"__symbolic":"method"}],"collectionHandler":[{"__symbolic":"method"}],"commands":[{"__symbolic":"method"}],"createErrorResponseOptions":[{"__symbolic":"method"}],"createHeaders":[{"__symbolic":"method"}],"createPassThruBackend":[{"__symbolic":"method"}],"createQueryMap":[{"__symbolic":"method"}],"createResponse$":[{"__symbolic":"method"}],"createResponse$fromResponseOptions$":[{"__symbolic":"method"}],"createResponseOptions$":[{"__symbolic":"method"}],"delete":[{"__symbolic":"method"}],"findById":[{"__symbolic":"method"}],"genId":[{"__symbolic":"method"}],"genIdDefault":[{"__symbolic":"method"}],"get":[{"__symbolic":"method"}],"getJsonBody":[{"__symbolic":"method"}],"getLocation":[{"__symbolic":"method"}],"getPassThruBackend":[{"__symbolic":"method"}],"getRequestInfoUtils":[{"__symbolic":"method"}],"getRequestMethod":[{"__symbolic":"method"}],"indexOf":[{"__symbolic":"method"}],"parseId":[{"__symbolic":"method"}],"isCollectionIdNumeric":[{"__symbolic":"method"}],"parseRequestUrl":[{"__symbolic":"method"}],"post":[{"__symbolic":"method"}],"put":[{"__symbolic":"method"}],"removeById":[{"__symbolic":"method"}],"resetDb":[{"__symbolic":"method"}]}}}],"symbols":[{"__symbol":0,"name":"BackendService","filePath":"./backend.service"},{"__symbol":1,"name":"InMemoryDbService","filePath":"./interfaces"},{"__symbol":2,"name":"InMemoryBackendConfigArgs","filePath":"./interfaces"}]} -------------------------------------------------------------------------------- /src/in-mem/delay-response.ngsummary.json: -------------------------------------------------------------------------------- 1 | {"moduleName":null,"summaries":[{"symbol":{"__symbol":0,"members":[]},"metadata":{"__symbolic":"function","parameters":["response$","delayMs"],"value":{"__symbolic":"new","expression":{"__symbol":1,"members":[]},"arguments":[{"__symbolic":"error","message":"Lambda not supported","fileName":"src\\in-mem\\delay-response.ts"}]}}}],"symbols":[{"__symbol":0,"name":"delayResponse","filePath":"./delay-response"},{"__symbol":1,"name":"Observable","filePath":"rxjs"}]} -------------------------------------------------------------------------------- /src/in-mem/delay-response.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs'; 2 | 3 | // Replaces use of RxJS delay. See v0.5.4. 4 | /** adds specified delay (in ms) to both next and error channels of the response observable */ 5 | export function delayResponse(response$: Observable, delayMs: number): Observable { 6 | return new Observable(observer => { 7 | let completePending = false; 8 | let nextPending = false; 9 | const subscription = response$.subscribe( 10 | value => { 11 | nextPending = true; 12 | setTimeout(() => { 13 | observer.next(value); 14 | if (completePending) { 15 | observer.complete(); 16 | } 17 | }, delayMs); 18 | }, 19 | error => setTimeout(() => observer.error(error), delayMs), 20 | () => { 21 | completePending = true; 22 | if (!nextPending) { 23 | observer.complete(); 24 | } 25 | } 26 | ); 27 | return () => { 28 | return subscription.unsubscribe(); 29 | }; 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /src/in-mem/http-client-backend.service.ngsummary.json: -------------------------------------------------------------------------------- 1 | {"moduleName":null,"summaries":[{"symbol":{"__symbol":0,"members":[]},"metadata":{"__symbolic":"class","extends":{"__symbol":1,"members":[]},"members":{"__ctor__":[{"__symbolic":"constructor","parameterDecorators":[null,[{"__symbolic":"call","expression":{"__symbol":2,"members":[]},"arguments":[{"__symbol":3,"members":[]}]},{"__symbolic":"call","expression":{"__symbol":4,"members":[]}}],null],"parameters":[{"__symbol":5,"members":[]},{"__symbol":6,"members":[]},{"__symbol":7,"members":[]}]}],"handle":[{"__symbolic":"method"}],"getJsonBody":[{"__symbolic":"method"}],"getRequestMethod":[{"__symbolic":"method"}],"createHeaders":[{"__symbolic":"method"}],"createQueryMap":[{"__symbolic":"method"}],"createResponse$fromResponseOptions$":[{"__symbolic":"method"}],"createPassThruBackend":[{"__symbolic":"method"}]}},"type":{"summaryKind":3,"type":{"reference":{"__symbol":0,"members":[]},"diDeps":[{"isAttribute":false,"isHost":false,"isSelf":false,"isSkipSelf":false,"isOptional":false,"token":{"identifier":{"reference":{"__symbol":5,"members":[]}}}},{"isAttribute":false,"isHost":false,"isSelf":false,"isSkipSelf":false,"isOptional":true,"token":{"identifier":{"reference":{"__symbol":3,"members":[]}}}},{"isAttribute":false,"isHost":false,"isSelf":false,"isSkipSelf":false,"isOptional":false,"token":{"identifier":{"reference":{"__symbol":7,"members":[]}}}}],"lifecycleHooks":[]}}}],"symbols":[{"__symbol":0,"name":"HttpClientBackendService","filePath":"./http-client-backend.service"},{"__symbol":1,"name":"BackendService","filePath":"./backend.service"},{"__symbol":2,"name":"Inject","filePath":"@angular/core"},{"__symbol":3,"name":"InMemoryBackendConfig","filePath":"./interfaces"},{"__symbol":4,"name":"Optional","filePath":"@angular/core"},{"__symbol":5,"name":"InMemoryDbService","filePath":"./interfaces"},{"__symbol":6,"name":"InMemoryBackendConfigArgs","filePath":"./interfaces"},{"__symbol":7,"name":"XhrFactory","filePath":"@angular/common/http/http"}]} -------------------------------------------------------------------------------- /src/in-mem/http-client-backend.service.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Injectable, Optional } from '@angular/core'; 2 | import { 3 | HttpBackend, 4 | HttpEvent, 5 | HttpHeaders, 6 | HttpParams, 7 | HttpRequest, 8 | HttpResponse, HttpResponseBase, 9 | HttpXhrBackend, 10 | XhrFactory 11 | } from '@angular/common/http'; 12 | 13 | import { Observable } from 'rxjs'; 14 | import { map } from 'rxjs/operators'; 15 | 16 | import { STATUS } from './http-status-codes'; 17 | 18 | import { 19 | InMemoryBackendConfig, 20 | InMemoryBackendConfigArgs, 21 | InMemoryDbService, 22 | ResponseOptions 23 | } from './interfaces'; 24 | 25 | import { BackendService } from './backend.service'; 26 | 27 | /** 28 | * For Angular `HttpClient` simulate the behavior of a RESTy web api 29 | * backed by the simple in-memory data store provided by the injected `InMemoryDbService`. 30 | * Conforms mostly to behavior described here: 31 | * http://www.restapitutorial.com/lessons/httpmethods.html 32 | * 33 | * ### Usage 34 | * 35 | * Create an in-memory data store class that implements `InMemoryDbService`. 36 | * Call `config` static method with this service class and optional configuration object: 37 | * ``` 38 | * // other imports 39 | * import { HttpClientModule } from '@angular/common/http'; 40 | * import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api'; 41 | * 42 | * import { InMemHeroService, inMemConfig } from '../api/in-memory-hero.service'; 43 | * @NgModule({ 44 | * imports: [ 45 | * HttpModule, 46 | * HttpClientInMemoryWebApiModule.forRoot(InMemHeroService, inMemConfig), 47 | * ... 48 | * ], 49 | * ... 50 | * }) 51 | * export class AppModule { ... } 52 | * ``` 53 | */ 54 | @Injectable() 55 | export class HttpClientBackendService extends BackendService implements HttpBackend { 56 | 57 | constructor( 58 | inMemDbService: InMemoryDbService, 59 | @Inject(InMemoryBackendConfig) @Optional() config: InMemoryBackendConfigArgs, 60 | private xhrFactory: XhrFactory 61 | ) { 62 | super(inMemDbService, config); 63 | } 64 | 65 | handle(req: HttpRequest): Observable> { 66 | try { 67 | return this.handleRequest(req); 68 | 69 | } catch (error) { 70 | const err = error.message || error; 71 | const resOptions = this.createErrorResponseOptions(req.url, STATUS.INTERNAL_SERVER_ERROR, `${err}`); 72 | return this.createResponse$(() => resOptions); 73 | } 74 | } 75 | 76 | //// protected overrides ///// 77 | 78 | protected getJsonBody(req: HttpRequest): any { 79 | return req.body; 80 | } 81 | 82 | protected getRequestMethod(req: HttpRequest): string { 83 | return (req.method || 'get').toLowerCase(); 84 | } 85 | 86 | protected createHeaders(headers: { [index: string]: string; }): HttpHeaders { 87 | return new HttpHeaders(headers); 88 | } 89 | 90 | protected createQueryMap(search: string): Map { 91 | const map = new Map(); 92 | if (search) { 93 | const params = new HttpParams({fromString: search}); 94 | params.keys().forEach(p => map.set(p, params.getAll(p))); 95 | } 96 | return map; 97 | } 98 | 99 | protected createResponse$fromResponseOptions$(resOptions$: Observable): Observable> { 100 | return resOptions$.pipe(map((opts: HttpResponseBase) => new HttpResponse(opts))); 101 | } 102 | 103 | protected createPassThruBackend() { 104 | try { 105 | return new HttpXhrBackend(this.xhrFactory); 106 | } catch (ex) { 107 | ex.message = 'Cannot create passThru404 backend; ' + (ex.message || ''); 108 | throw ex; 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/in-mem/http-client-in-memory-web-api.module.ngsummary.json: -------------------------------------------------------------------------------- 1 | {"moduleName":null,"summaries":[{"symbol":{"__symbol":0,"members":[]},"metadata":{"__symbolic":"function"}},{"symbol":{"__symbol":1,"members":[]},"metadata":{"__symbolic":"class","statics":{"forRoot":{"__symbolic":"function","parameters":["dbCreator","options"],"value":{"ngModule":{"__symbol":1,"members":[]},"providers":[{"provide":{"__symbol":2,"members":[]},"useClass":{"__symbolic":"reference","name":"dbCreator"}},{"provide":{"__symbol":3,"members":[]},"useValue":{"__symbolic":"reference","name":"options"}},{"provide":{"__symbol":4,"members":[]},"useFactory":{"__symbol":0,"members":[]},"deps":[{"__symbol":2,"members":[]},{"__symbol":3,"members":[]},{"__symbol":5,"members":[]}]}]}},"forFeature":{"__symbolic":"function","parameters":["dbCreator","options"],"value":{"__symbolic":"call","expression":{"__symbolic":"select","expression":{"__symbol":1,"members":[]},"member":"forRoot"},"arguments":[{"__symbolic":"reference","name":"dbCreator"},{"__symbolic":"reference","name":"options"}]}}}},"type":{"summaryKind":2,"type":{"reference":{"__symbol":1,"members":[]},"diDeps":[],"lifecycleHooks":[]},"entryComponents":[],"providers":[],"modules":[{"reference":{"__symbol":1,"members":[]},"diDeps":[],"lifecycleHooks":[]}],"exportedDirectives":[],"exportedPipes":[]}}],"symbols":[{"__symbol":0,"name":"httpClientInMemBackendServiceFactory","filePath":"./http-client-in-memory-web-api.module"},{"__symbol":1,"name":"HttpClientInMemoryWebApiModule","filePath":"./http-client-in-memory-web-api.module"},{"__symbol":2,"name":"InMemoryDbService","filePath":"./interfaces"},{"__symbol":3,"name":"InMemoryBackendConfig","filePath":"./interfaces"},{"__symbol":4,"name":"HttpBackend","filePath":"@angular/common/http/http"},{"__symbol":5,"name":"XhrFactory","filePath":"@angular/common/http/http"}]} -------------------------------------------------------------------------------- /src/in-mem/http-client-in-memory-web-api.module.ts: -------------------------------------------------------------------------------- 1 | ////// HttpClient-Only version //// 2 | 3 | import { NgModule, ModuleWithProviders, Type } from '@angular/core'; 4 | import { HttpBackend, XhrFactory } from '@angular/common/http'; 5 | 6 | import { 7 | InMemoryBackendConfigArgs, 8 | InMemoryBackendConfig, 9 | InMemoryDbService 10 | } from './interfaces'; 11 | 12 | import { HttpClientBackendService } from './http-client-backend.service'; 13 | 14 | // Internal - Creates the in-mem backend for the HttpClient module 15 | // AoT requires factory to be exported 16 | export function httpClientInMemBackendServiceFactory( 17 | dbService: InMemoryDbService, 18 | options: InMemoryBackendConfig, 19 | xhrFactory: XhrFactory, 20 | ): HttpBackend { 21 | const backend: any = new HttpClientBackendService(dbService, options, xhrFactory); 22 | return backend; 23 | } 24 | 25 | @NgModule({}) 26 | export class HttpClientInMemoryWebApiModule { 27 | /** 28 | * Redirect the Angular `HttpClient` XHR calls 29 | * to in-memory data store that implements `InMemoryDbService`. 30 | * with class that implements InMemoryDbService and creates an in-memory database. 31 | * 32 | * Usually imported in the root application module. 33 | * Can import in a lazy feature module too, which will shadow modules loaded earlier 34 | * 35 | * @param {Type} dbCreator - Class that creates seed data for in-memory database. Must implement InMemoryDbService. 36 | * @param {InMemoryBackendConfigArgs} [options] 37 | * 38 | * @example 39 | * HttpInMemoryWebApiModule.forRoot(dbCreator); 40 | * HttpInMemoryWebApiModule.forRoot(dbCreator, {useValue: {delay:600}}); 41 | */ 42 | static forRoot(dbCreator: Type, options?: InMemoryBackendConfigArgs): 43 | ModuleWithProviders { 44 | return { 45 | ngModule: HttpClientInMemoryWebApiModule, 46 | providers: [ 47 | { provide: InMemoryDbService, useClass: dbCreator }, 48 | { provide: InMemoryBackendConfig, useValue: options }, 49 | 50 | { provide: HttpBackend, 51 | useFactory: httpClientInMemBackendServiceFactory, 52 | deps: [InMemoryDbService, InMemoryBackendConfig, XhrFactory]} 53 | ] 54 | }; 55 | } 56 | /** 57 | * 58 | * Enable and configure the in-memory web api in a lazy-loaded feature module. 59 | * Same as `forRoot`. 60 | * This is a feel-good method so you can follow the Angular style guide for lazy-loaded modules. 61 | */ 62 | static forFeature(dbCreator: Type, options?: InMemoryBackendConfigArgs): 63 | ModuleWithProviders { 64 | return HttpClientInMemoryWebApiModule.forRoot(dbCreator, options); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/in-mem/in-memory-web-api.module.ngsummary.json: -------------------------------------------------------------------------------- 1 | {"moduleName":null,"summaries":[{"symbol":{"__symbol":0,"members":[]},"metadata":{"__symbolic":"class","statics":{"forRoot":{"__symbolic":"function","parameters":["dbCreator","options"],"value":{"ngModule":{"__symbol":0,"members":[]},"providers":[{"provide":{"__symbol":1,"members":[]},"useClass":{"__symbolic":"reference","name":"dbCreator"}},{"provide":{"__symbol":2,"members":[]},"useValue":{"__symbolic":"reference","name":"options"}},{"provide":{"__symbol":3,"members":[]},"useFactory":{"__symbol":4,"members":[]},"deps":[{"__symbol":1,"members":[]},{"__symbol":2,"members":[]},{"__symbol":5,"members":[]}]}]}},"forFeature":{"__symbolic":"function","parameters":["dbCreator","options"],"value":{"__symbolic":"call","expression":{"__symbolic":"select","expression":{"__symbol":0,"members":[]},"member":"forRoot"},"arguments":[{"__symbolic":"reference","name":"dbCreator"},{"__symbolic":"reference","name":"options"}]}}}},"type":{"summaryKind":2,"type":{"reference":{"__symbol":0,"members":[]},"diDeps":[],"lifecycleHooks":[]},"entryComponents":[],"providers":[],"modules":[{"reference":{"__symbol":0,"members":[]},"diDeps":[],"lifecycleHooks":[]}],"exportedDirectives":[],"exportedPipes":[]}}],"symbols":[{"__symbol":0,"name":"InMemoryWebApiModule","filePath":"./in-memory-web-api.module"},{"__symbol":1,"name":"InMemoryDbService","filePath":"./interfaces"},{"__symbol":2,"name":"InMemoryBackendConfig","filePath":"./interfaces"},{"__symbol":3,"name":"HttpBackend","filePath":"@angular/common/http/http"},{"__symbol":4,"name":"httpClientInMemBackendServiceFactory","filePath":"./http-client-in-memory-web-api.module"},{"__symbol":5,"name":"XhrFactory","filePath":"@angular/common/http/http"}]} -------------------------------------------------------------------------------- /src/in-mem/in-memory-web-api.module.ts: -------------------------------------------------------------------------------- 1 | ////// For apps with both Http and HttpClient //// 2 | 3 | import { Injector, NgModule, ModuleWithProviders, Type } from '@angular/core'; 4 | import { HttpBackend, XhrFactory } from '@angular/common/http'; 5 | 6 | import { 7 | InMemoryBackendConfigArgs, 8 | InMemoryBackendConfig, 9 | InMemoryDbService 10 | } from './interfaces'; 11 | 12 | import { httpClientInMemBackendServiceFactory } from './http-client-in-memory-web-api.module'; 13 | 14 | @NgModule({}) 15 | export class InMemoryWebApiModule { 16 | /** 17 | * Redirect BOTH Angular `Http` and `HttpClient` XHR calls 18 | * to in-memory data store that implements `InMemoryDbService`. 19 | * with class that implements InMemoryDbService and creates an in-memory database. 20 | * 21 | * Usually imported in the root application module. 22 | * Can import in a lazy feature module too, which will shadow modules loaded earlier 23 | * 24 | * @param {Type} dbCreator - Class that creates seed data for in-memory database. Must implement InMemoryDbService. 25 | * @param {InMemoryBackendConfigArgs} [options] 26 | * 27 | * @example 28 | * InMemoryWebApiModule.forRoot(dbCreator); 29 | * InMemoryWebApiModule.forRoot(dbCreator, {useValue: {delay:600}}); 30 | */ 31 | static forRoot(dbCreator: Type, options?: InMemoryBackendConfigArgs): ModuleWithProviders { 32 | return { 33 | ngModule: InMemoryWebApiModule, 34 | providers: [ 35 | { provide: InMemoryDbService, useClass: dbCreator }, 36 | { provide: InMemoryBackendConfig, useValue: options }, 37 | 38 | 39 | { provide: HttpBackend, 40 | useFactory: httpClientInMemBackendServiceFactory, 41 | deps: [InMemoryDbService, InMemoryBackendConfig, XhrFactory]} 42 | ] 43 | }; 44 | } 45 | 46 | /** 47 | * 48 | * Enable and configure the in-memory web api in a lazy-loaded feature module. 49 | * Same as `forRoot`. 50 | * This is a feel-good method so you can follow the Angular style guide for lazy-loaded modules. 51 | */ 52 | static forFeature(dbCreator: Type, options?: InMemoryBackendConfigArgs): ModuleWithProviders { 53 | return InMemoryWebApiModule.forRoot(dbCreator, options); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/in-mem/index.ngsummary.json: -------------------------------------------------------------------------------- 1 | {"moduleName":null,"summaries":[{"symbol":{"__symbol":0,"members":[]},"metadata":{"__symbol":1,"members":[]}},{"symbol":{"__symbol":2,"members":[]},"metadata":{"__symbol":3,"members":[]}},{"symbol":{"__symbol":4,"members":[]},"metadata":{"__symbol":5,"members":[]}},{"symbol":{"__symbol":6,"members":[]},"metadata":{"__symbol":7,"members":[]}},{"symbol":{"__symbol":8,"members":[]},"metadata":{"__symbol":9,"members":[]}},{"symbol":{"__symbol":10,"members":[]},"metadata":{"__symbol":11,"members":[]}},{"symbol":{"__symbol":12,"members":[]},"metadata":{"__symbol":13,"members":[]}},{"symbol":{"__symbol":14,"members":[]},"metadata":{"__symbol":15,"members":[]}},{"symbol":{"__symbol":16,"members":[]},"metadata":{"__symbol":17,"members":[]}},{"symbol":{"__symbol":18,"members":[]},"metadata":{"__symbol":19,"members":[]}},{"symbol":{"__symbol":20,"members":[]},"metadata":{"__symbol":21,"members":[]}},{"symbol":{"__symbol":22,"members":[]},"metadata":{"__symbol":23,"members":[]}},{"symbol":{"__symbol":24,"members":[]},"metadata":{"__symbol":25,"members":[]}},{"symbol":{"__symbol":26,"members":[]},"metadata":{"__symbol":27,"members":[]}},{"symbol":{"__symbol":28,"members":[]},"metadata":{"__symbol":29,"members":[]}},{"symbol":{"__symbol":30,"members":[]},"metadata":{"__symbol":31,"members":[]}},{"symbol":{"__symbol":32,"members":[]},"metadata":{"__symbol":33,"members":[]}},{"symbol":{"__symbol":34,"members":[]},"metadata":{"__symbol":35,"members":[]}},{"symbol":{"__symbol":36,"members":[]},"metadata":{"__symbol":37,"members":[]}},{"symbol":{"__symbol":38,"members":[]},"metadata":{"__symbol":39,"members":[]}},{"symbol":{"__symbol":40,"members":[]},"metadata":{"__symbol":41,"members":[]}},{"symbol":{"__symbol":42,"members":[]},"metadata":{"__symbol":43,"members":[]}},{"symbol":{"__symbol":44,"members":[]},"metadata":{"__symbol":45,"members":[]}}],"symbols":[{"__symbol":0,"name":"BackendService","filePath":"./index"},{"__symbol":1,"name":"BackendService","filePath":"./backend.service"},{"__symbol":2,"name":"STATUS","filePath":"./index"},{"__symbol":3,"name":"STATUS","filePath":"./http-status-codes"},{"__symbol":4,"name":"STATUS_CODE_INFO","filePath":"./index"},{"__symbol":5,"name":"STATUS_CODE_INFO","filePath":"./http-status-codes"},{"__symbol":6,"name":"getStatusText","filePath":"./index"},{"__symbol":7,"name":"getStatusText","filePath":"./http-status-codes"},{"__symbol":8,"name":"isSuccess","filePath":"./index"},{"__symbol":9,"name":"isSuccess","filePath":"./http-status-codes"},{"__symbol":10,"name":"HttpClientBackendService","filePath":"./index"},{"__symbol":11,"name":"HttpClientBackendService","filePath":"./http-client-backend.service"},{"__symbol":12,"name":"InMemoryWebApiModule","filePath":"./index"},{"__symbol":13,"name":"InMemoryWebApiModule","filePath":"./in-memory-web-api.module"},{"__symbol":14,"name":"httpClientInMemBackendServiceFactory","filePath":"./index"},{"__symbol":15,"name":"httpClientInMemBackendServiceFactory","filePath":"./http-client-in-memory-web-api.module"},{"__symbol":16,"name":"HttpClientInMemoryWebApiModule","filePath":"./index"},{"__symbol":17,"name":"HttpClientInMemoryWebApiModule","filePath":"./http-client-in-memory-web-api.module"},{"__symbol":18,"name":"HeadersCore","filePath":"./index"},{"__symbol":19,"name":"HeadersCore","filePath":"./interfaces"},{"__symbol":20,"name":"InMemoryDbService","filePath":"./index"},{"__symbol":21,"name":"InMemoryDbService","filePath":"./interfaces"},{"__symbol":22,"name":"InMemoryBackendConfigArgs","filePath":"./index"},{"__symbol":23,"name":"InMemoryBackendConfigArgs","filePath":"./interfaces"},{"__symbol":24,"name":"InMemoryBackendConfig","filePath":"./index"},{"__symbol":25,"name":"InMemoryBackendConfig","filePath":"./interfaces"},{"__symbol":26,"name":"parseUri","filePath":"./index"},{"__symbol":27,"name":"parseUri","filePath":"./interfaces"},{"__symbol":28,"name":"ParsedRequestUrl","filePath":"./index"},{"__symbol":29,"name":"ParsedRequestUrl","filePath":"./interfaces"},{"__symbol":30,"name":"PassThruBackend","filePath":"./index"},{"__symbol":31,"name":"PassThruBackend","filePath":"./interfaces"},{"__symbol":32,"name":"removeTrailingSlash","filePath":"./index"},{"__symbol":33,"name":"removeTrailingSlash","filePath":"./interfaces"},{"__symbol":34,"name":"RequestCore","filePath":"./index"},{"__symbol":35,"name":"RequestCore","filePath":"./interfaces"},{"__symbol":36,"name":"RequestInfo","filePath":"./index"},{"__symbol":37,"name":"RequestInfo","filePath":"./interfaces"},{"__symbol":38,"name":"RequestInfoUtilities","filePath":"./index"},{"__symbol":39,"name":"RequestInfoUtilities","filePath":"./interfaces"},{"__symbol":40,"name":"ResponseInterceptor","filePath":"./index"},{"__symbol":41,"name":"ResponseInterceptor","filePath":"./interfaces"},{"__symbol":42,"name":"ResponseOptions","filePath":"./index"},{"__symbol":43,"name":"ResponseOptions","filePath":"./interfaces"},{"__symbol":44,"name":"UriInfo","filePath":"./index"},{"__symbol":45,"name":"UriInfo","filePath":"./interfaces"}]} -------------------------------------------------------------------------------- /src/in-mem/index.ts: -------------------------------------------------------------------------------- 1 | export * from './backend.service'; 2 | export * from './http-status-codes'; 3 | export * from './http-client-backend.service'; 4 | export * from './in-memory-web-api.module'; 5 | export * from './http-client-in-memory-web-api.module'; 6 | export * from './interfaces'; 7 | -------------------------------------------------------------------------------- /src/in-mem/interfaces.ngsummary.json: -------------------------------------------------------------------------------- 1 | {"moduleName":null,"summaries":[{"symbol":{"__symbol":0,"members":[]},"metadata":{"__symbolic":"interface"}},{"symbol":{"__symbol":1,"members":[]},"metadata":{"__symbolic":"class","members":{"createDb":[{"__symbolic":"method"}]}}},{"symbol":{"__symbol":2,"members":[]},"metadata":{"__symbolic":"class"}},{"symbol":{"__symbol":3,"members":[]},"metadata":{"__symbolic":"class","members":{"__ctor__":[{"__symbolic":"constructor","parameters":[{"__symbol":2,"members":[]}]}]}},"type":{"summaryKind":3,"type":{"reference":{"__symbol":3,"members":[]},"diDeps":[{"isAttribute":false,"isHost":false,"isSelf":false,"isSkipSelf":false,"isOptional":false,"token":{"identifier":{"reference":{"__symbol":2,"members":[]}}}}],"lifecycleHooks":[]}}},{"symbol":{"__symbol":4,"members":[]},"metadata":{"__symbolic":"function"}},{"symbol":{"__symbol":5,"members":[]},"metadata":{"__symbolic":"interface"}},{"symbol":{"__symbol":6,"members":[]},"metadata":{"__symbolic":"interface"}},{"symbol":{"__symbol":7,"members":[]},"metadata":{"__symbolic":"function","parameters":["path"],"value":{"__symbolic":"call","expression":{"__symbolic":"select","expression":{"__symbolic":"reference","name":"path"},"member":"replace"},"arguments":[{"__symbolic":"error","message":"Expression form not supported","fileName":"src\\in-mem\\interfaces.ts"},""]}}},{"symbol":{"__symbol":8,"members":[]},"metadata":{"__symbolic":"interface"}},{"symbol":{"__symbol":9,"members":[]},"metadata":{"__symbolic":"interface"}},{"symbol":{"__symbol":10,"members":[]},"metadata":{"__symbolic":"interface"}},{"symbol":{"__symbol":11,"members":[]},"metadata":{"__symbolic":"interface"}},{"symbol":{"__symbol":12,"members":[]},"metadata":{"__symbolic":"interface"}},{"symbol":{"__symbol":13,"members":[]},"metadata":{"__symbolic":"interface"}}],"symbols":[{"__symbol":0,"name":"HeadersCore","filePath":"./interfaces"},{"__symbol":1,"name":"InMemoryDbService","filePath":"./interfaces"},{"__symbol":2,"name":"InMemoryBackendConfigArgs","filePath":"./interfaces"},{"__symbol":3,"name":"InMemoryBackendConfig","filePath":"./interfaces"},{"__symbol":4,"name":"parseUri","filePath":"./interfaces"},{"__symbol":5,"name":"ParsedRequestUrl","filePath":"./interfaces"},{"__symbol":6,"name":"PassThruBackend","filePath":"./interfaces"},{"__symbol":7,"name":"removeTrailingSlash","filePath":"./interfaces"},{"__symbol":8,"name":"RequestCore","filePath":"./interfaces"},{"__symbol":9,"name":"RequestInfo","filePath":"./interfaces"},{"__symbol":10,"name":"RequestInfoUtilities","filePath":"./interfaces"},{"__symbol":11,"name":"ResponseInterceptor","filePath":"./interfaces"},{"__symbol":12,"name":"ResponseOptions","filePath":"./interfaces"},{"__symbol":13,"name":"UriInfo","filePath":"./interfaces"}]} -------------------------------------------------------------------------------- /src/in-mem/interfaces.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Observable } from 'rxjs'; 3 | 4 | /** 5 | * Minimum definition needed by base class 6 | */ 7 | export interface HeadersCore { 8 | set(name: string, value: string): void | any; 9 | } 10 | 11 | /** 12 | * Interface for a class that creates an in-memory database 13 | * 14 | * Its `createDb` method creates a hash of named collections that represents the database 15 | * 16 | * For maximum flexibility, the service may define HTTP method overrides. 17 | * Such methods must match the spelling of an HTTP method in lower case (e.g, "get"). 18 | * If a request has a matching method, it will be called as in 19 | * `get(info: requestInfo, db: {})` where `db` is the database object described above. 20 | */ 21 | export abstract class InMemoryDbService { 22 | /** 23 | * Creates an in-memory "database" hash whose keys are collection names 24 | * and whose values are arrays of collection objects to return or update. 25 | * 26 | * returns Observable of the database because could have to create it asynchronously. 27 | * 28 | * This method must be safe to call repeatedly. 29 | * Each time it should return a new object with new arrays containing new item objects. 30 | * This condition allows the in-memory backend service to mutate the collections 31 | * and their items without touching the original source data. 32 | * 33 | * The in-mem backend service calls this method without a value the first time. 34 | * The service calls it with the `RequestInfo` when it receives a POST `commands/resetDb` request. 35 | * Your InMemoryDbService can adjust its behavior accordingly. 36 | */ 37 | abstract createDb(reqInfo?: RequestInfo): {} | Observable<{}> | Promise<{}>; 38 | } 39 | 40 | /** 41 | * Interface for InMemoryBackend configuration options 42 | */ 43 | export abstract class InMemoryBackendConfigArgs { 44 | /** 45 | * The base path to the api, e.g, 'api/'. 46 | * If not specified than `parseRequestUrl` assumes it is the first path segment in the request. 47 | */ 48 | apiBase?: string; 49 | /** 50 | * false (default) if search match should be case insensitive 51 | */ 52 | caseSensitiveSearch?: boolean; 53 | /** 54 | * false (default) put content directly inside the response body. 55 | * true: encapsulate content in a `data` property inside the response body, `{ data: ... }`. 56 | */ 57 | dataEncapsulation?: boolean; 58 | /** 59 | * delay (in ms) to simulate latency 60 | */ 61 | delay?: number; 62 | /** 63 | * false (default) should 204 when object-to-delete not found; true: 404 64 | */ 65 | delete404?: boolean; 66 | /** 67 | * host for this service, e.g., 'localhost' 68 | */ 69 | host?: string; 70 | /** 71 | * false (default) should pass unrecognized request URL through to original backend; true: 404 72 | */ 73 | passThruUnknownUrl?: boolean; 74 | /** 75 | * true (default) should NOT return the item (204) after a POST. false: return the item (200). 76 | */ 77 | post204?: boolean; 78 | /** 79 | * false (default) should NOT update existing item with POST. false: OK to update. 80 | */ 81 | post409?: boolean; 82 | /** 83 | * true (default) should NOT return the item (204) after a POST. false: return the item (200). 84 | */ 85 | put204?: boolean; 86 | /** 87 | * false (default) if item not found, create as new item; false: should 404. 88 | */ 89 | put404?: boolean; 90 | /** 91 | * root path _before_ any API call, e.g., '' 92 | */ 93 | rootPath?: string; 94 | } 95 | 96 | ///////////////////////////////// 97 | /** 98 | * InMemoryBackendService configuration options 99 | * Usage: 100 | * InMemoryWebApiModule.forRoot(InMemHeroService, {delay: 600}) 101 | * 102 | * or if providing separately: 103 | * provide(InMemoryBackendConfig, {useValue: {delay: 600}}), 104 | */ 105 | @Injectable() 106 | export class InMemoryBackendConfig implements InMemoryBackendConfigArgs { 107 | constructor(config: InMemoryBackendConfigArgs = {}) { 108 | Object.assign(this, { 109 | // default config: 110 | caseSensitiveSearch: false, 111 | dataEncapsulation: false, // do NOT wrap content within an object with a `data` property 112 | delay: 500, // simulate latency by delaying response 113 | delete404: false, // don't complain if can't find entity to delete 114 | passThruUnknownUrl: false, // 404 if can't process URL 115 | post204: true, // don't return the item after a POST 116 | post409: false, // don't update existing item with that ID 117 | put204: true, // don't return the item after a PUT 118 | put404: false, // create new item if PUT item with that ID not found 119 | apiBase: undefined, // assumed to be the first path segment 120 | host: undefined, // default value is actually set in InMemoryBackendService ctor 121 | rootPath: undefined // default value is actually set in InMemoryBackendService ctor 122 | }, config); 123 | } 124 | } 125 | 126 | /** Return information (UriInfo) about a URI */ 127 | export function parseUri(str: string): UriInfo { 128 | // Adapted from parseuri package - http://blog.stevenlevithan.com/archives/parseuri 129 | // tslint:disable-next-line:max-line-length 130 | const URL_REGEX = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/; 131 | const m = URL_REGEX.exec(str); 132 | const uri: UriInfo = { 133 | source: '', 134 | protocol: '', 135 | authority: '', 136 | userInfo: '', 137 | user: '', 138 | password: '', 139 | host: '', 140 | port: '', 141 | relative: '', 142 | path: '', 143 | directory: '', 144 | file: '', 145 | query: '', 146 | anchor: '' 147 | }; 148 | const keys = Object.keys(uri); 149 | let i = keys.length; 150 | 151 | while (i--) { uri[keys[i]] = m[i] || ''; } 152 | return uri; 153 | } 154 | 155 | /** 156 | * 157 | * Interface for the result of the `parseRequestUrl` method: 158 | * Given URL "http://localhost:8080/api/customers/42?foo=1 the default implementation returns 159 | * base: 'api/' 160 | * collectionName: 'customers' 161 | * id: '42' 162 | * query: this.createQuery('foo=1') 163 | * resourceUrl: 'http://localhost/api/customers/' 164 | */ 165 | export interface ParsedRequestUrl { 166 | apiBase: string; // the slash-terminated "base" for api requests (e.g. `api/`) 167 | collectionName: string; // the name of the collection of data items (e.g.,`customers`) 168 | id: string; // the (optional) id of the item in the collection (e.g., `42`) 169 | query: Map; // the query parameters; 170 | resourceUrl: string; // the effective URL for the resource (e.g., 'http://localhost/api/customers/') 171 | } 172 | 173 | export interface PassThruBackend { 174 | /** 175 | * Handle an HTTP request and return an Observable of HTTP response 176 | * Both the request type and the response type are determined by the supporting HTTP library. 177 | */ 178 | handle(req: any): Observable; 179 | } 180 | 181 | export function removeTrailingSlash(path: string) { 182 | return path.replace(/\/$/, ''); 183 | } 184 | 185 | /** 186 | * Minimum definition needed by base class 187 | */ 188 | export interface RequestCore { 189 | url: string; // request URL 190 | urlWithParams?: string; // request URL with query parameters added by `HttpParams` 191 | } 192 | 193 | /** 194 | * Interface for object w/ info about the current request url 195 | * extracted from an Http Request. 196 | * Also holds utility methods and configuration data from this service 197 | */ 198 | export interface RequestInfo { 199 | req: RequestCore; // concrete type depends upon the Http library 200 | apiBase: string; 201 | collectionName: string; 202 | collection: any; 203 | headers: HeadersCore; 204 | method: string; 205 | id: any; 206 | query: Map; 207 | resourceUrl: string; 208 | url: string; // request URL 209 | utils: RequestInfoUtilities; 210 | } 211 | 212 | /** 213 | * Interface for utility methods from this service instance. 214 | * Useful within an HTTP method override 215 | */ 216 | export interface RequestInfoUtilities { 217 | /** 218 | * Create a cold response Observable from a factory for ResponseOptions 219 | * the same way that the in-mem backend service does. 220 | * @param resOptionsFactory - creates ResponseOptions when observable is subscribed 221 | * @param withDelay - if true (default), add simulated latency delay from configuration 222 | */ 223 | createResponse$: (resOptionsFactory: () => ResponseOptions) => Observable; 224 | 225 | /** 226 | * Find first instance of item in collection by `item.id` 227 | * @param collection 228 | * @param id 229 | */ 230 | findById(collection: T[], id: any): T; 231 | 232 | /** return the current, active configuration which is a blend of defaults and overrides */ 233 | getConfig(): InMemoryBackendConfigArgs; 234 | 235 | /** Get the in-mem service's copy of the "database" */ 236 | getDb(): {}; 237 | 238 | /** Get JSON body from the request object */ 239 | getJsonBody(req: any): any; 240 | 241 | /** Get location info from a url, even on server where `document` is not defined */ 242 | getLocation(url: string): UriInfo; 243 | 244 | /** Get (or create) the "real" backend */ 245 | getPassThruBackend(): PassThruBackend; 246 | 247 | /** 248 | * return true if can determine that the collection's `item.id` is a number 249 | * */ 250 | isCollectionIdNumeric(collection: T[], collectionName: string): boolean; 251 | 252 | /** 253 | * Parses the request URL into a `ParsedRequestUrl` object. 254 | * Parsing depends upon certain values of `config`: `apiBase`, `host`, and `urlRoot`. 255 | */ 256 | parseRequestUrl(url: string): ParsedRequestUrl; 257 | } 258 | 259 | /** 260 | * Provide a `responseInterceptor` method of this type in your `inMemDbService` to 261 | * morph the response options created in the `collectionHandler`. 262 | */ 263 | export type ResponseInterceptor = (res: ResponseOptions, ri: RequestInfo) => ResponseOptions; 264 | 265 | export interface ResponseOptions { 266 | /** 267 | * String, Object, ArrayBuffer or Blob representing the body of the {@link Response}. 268 | */ 269 | body?: string | Object | ArrayBuffer | Blob; 270 | 271 | /** 272 | * Response headers 273 | */ 274 | headers?: HeadersCore; 275 | 276 | /** 277 | * Http {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html status code} 278 | * associated with the response. 279 | */ 280 | status?: number; 281 | 282 | /** 283 | * Status text for the status code 284 | */ 285 | statusText?: string; 286 | /** 287 | * request url 288 | */ 289 | url?: string; 290 | } 291 | 292 | /** Interface of information about a Uri */ 293 | export interface UriInfo { 294 | source: string; 295 | protocol: string; 296 | authority: string; 297 | userInfo: string; 298 | user: string; 299 | password: string; 300 | host: string; 301 | port: string; 302 | relative: string; 303 | path: string; 304 | directory: string; 305 | file: string; 306 | query: string; 307 | anchor: string; 308 | } 309 | -------------------------------------------------------------------------------- /src/systemjs-angular-loader.js: -------------------------------------------------------------------------------- 1 | var templateUrlRegex = /templateUrl\s*:(\s*['"`](.*?)['"`]\s*)/gm; 2 | var stylesRegex = /styleUrls *:(\s*\[[^\]]*?\])/g; 3 | var stringRegex = /(['`"])((?:[^\\]\\\1|.)*?)\1/g; 4 | 5 | module.exports.translate = function(load){ 6 | if (load.source.indexOf('moduleId') != -1) return load; 7 | 8 | var url = document.createElement('a'); 9 | url.href = load.address; 10 | 11 | var basePathParts = url.pathname.split('/'); 12 | 13 | basePathParts.pop(); 14 | var basePath = basePathParts.join('/'); 15 | 16 | var baseHref = document.createElement('a'); 17 | baseHref.href = this.baseURL; 18 | baseHref = baseHref.pathname; 19 | 20 | if (!baseHref.startsWith('/base/')) { // it is not karma 21 | basePath = basePath.replace(baseHref, ''); 22 | } 23 | 24 | load.source = load.source 25 | .replace(templateUrlRegex, function(match, quote, url){ 26 | var resolvedUrl = url; 27 | 28 | if (url.startsWith('.')) { 29 | resolvedUrl = basePath + url.substr(1); 30 | } 31 | 32 | return 'templateUrl: "' + resolvedUrl + '"'; 33 | }) 34 | .replace(stylesRegex, function(match, relativeUrls) { 35 | var urls = []; 36 | 37 | while ((match = stringRegex.exec(relativeUrls)) !== null) { 38 | if (match[2].startsWith('.')) { 39 | urls.push('"' + basePath + match[2].substr(1) + '"'); 40 | } else { 41 | urls.push('"' + match[2] + '"'); 42 | } 43 | } 44 | 45 | return "styleUrls: [" + urls.join(', ') + "]"; 46 | }); 47 | 48 | return load; 49 | }; 50 | -------------------------------------------------------------------------------- /src/systemjs.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * System configuration for Angular samples 3 | * Adjust as necessary for your application needs. 4 | */ 5 | (function (global) { 6 | System.config({ 7 | paths: { 8 | // paths serve as alias 9 | 'npm:': 'node_modules/' 10 | }, 11 | // map tells the System loader where to look for things 12 | map: { 13 | // our app is within the app folder 14 | 'app': 'app', 15 | 16 | // angular bundles 17 | '@angular/animations': 'npm:@angular/animations/bundles/animations.umd.js', 18 | '@angular/animations/browser': 'npm:@angular/animations/bundles/animations-browser.umd.js', 19 | '@angular/core': 'npm:@angular/core/bundles/core.umd.js', 20 | '@angular/common': 'npm:@angular/common/bundles/common.umd.js', 21 | '@angular/common/http': 'npm:@angular/common/bundles/common-http.umd.js', 22 | '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js', 23 | '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js', 24 | '@angular/platform-browser/animations': 'npm:@angular/platform-browser/bundles/platform-browser-animations.umd.js', 25 | '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js', 26 | '@angular/router': 'npm:@angular/router/bundles/router.umd.js', 27 | '@angular/router/upgrade': 'npm:@angular/router/bundles/router-upgrade.umd.js', 28 | '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js', 29 | '@angular/upgrade': 'npm:@angular/upgrade/bundles/upgrade.umd.js', 30 | '@angular/upgrade/static': 'npm:@angular/upgrade/bundles/upgrade-static.umd.js', 31 | 32 | // other libraries 33 | 'rxjs': 'npm:rxjs', 34 | 'tslib': 'npm:tslib/tslib.js', 35 | 'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js' 36 | }, 37 | // packages tells the System loader how to load when no filename and/or no extension 38 | packages: { 39 | 'app': { 40 | main: './main.js', 41 | defaultExtension: 'js', 42 | meta: { 43 | './*.js': { 44 | loader: 'systemjs-angular-loader.js' 45 | } 46 | } 47 | }, 48 | 'rxjs': { 49 | main: 'index.js' 50 | }, 51 | 'rxjs/operators': { 52 | main: 'index.js' 53 | } 54 | } 55 | }); 56 | })(this); 57 | -------------------------------------------------------------------------------- /src/testing/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Fail a Jasmine test such that it displays the error object, 3 | * typically passed in the error path of an Observable.subscribe() 4 | * @param err - the error object 5 | */ 6 | export function failure(err: any) { 7 | fail(JSON.stringify(err)); 8 | } 9 | -------------------------------------------------------------------------------- /src/testing/jasmine-ajax.spec.ts: -------------------------------------------------------------------------------- 1 | // Confirm that the jasmine ajax testing library works in this project stand-alone. 2 | // These tests are copied from the documentation 3 | // https://jasmine.github.io/edge/ajax.html 4 | // See karma.conf and package.json changes related to using this jasmine plugin 5 | // https://www.npmjs.com/package/karma-jasmine-ajax 6 | 7 | describe('Jasmine ajax mocking proof-of-life', () => { 8 | 9 | describe('suite wide usage', () => { 10 | 11 | beforeEach(function() { 12 | jasmine.Ajax.install(); 13 | }); 14 | 15 | afterEach(function() { 16 | jasmine.Ajax.uninstall(); 17 | }); 18 | 19 | it('specifying response when you need it', () => { 20 | const doneFn = jasmine.createSpy('success'); 21 | 22 | const xhr = new XMLHttpRequest(); 23 | xhr.onreadystatechange = function(args) { 24 | if (this.readyState === this.DONE) { 25 | doneFn(this.responseText); 26 | } 27 | }; 28 | 29 | xhr.open('GET', '/some/cool/url'); 30 | xhr.send(); 31 | 32 | expect(jasmine.Ajax.requests.mostRecent().url).toBe('/some/cool/url'); 33 | expect(doneFn).not.toHaveBeenCalled(); 34 | 35 | jasmine.Ajax.requests.mostRecent().respondWith({ 36 | 'status': 200, 37 | 'contentType': 'text/plain', 38 | 'responseText': 'awesome response' 39 | }); 40 | 41 | expect(doneFn).toHaveBeenCalledWith('awesome response'); 42 | }); 43 | 44 | it('allows responses to be setup ahead of time', () => { 45 | const doneFn = jasmine.createSpy('success'); 46 | 47 | jasmine.Ajax.stubRequest('/another/url').andReturn({ 48 | 'responseText': 'immediate response' 49 | }); 50 | const xhr = new XMLHttpRequest(); 51 | xhr.onreadystatechange = function(args) { 52 | if (this.readyState === this.DONE) { 53 | doneFn(this.responseText); 54 | } 55 | }; 56 | 57 | xhr.open('GET', '/another/url'); 58 | xhr.send(); 59 | 60 | expect(doneFn).toHaveBeenCalledWith('immediate response'); 61 | }); 62 | 63 | }); 64 | 65 | it('allows use in a single spec', () => { 66 | const doneFn = jasmine.createSpy('success'); 67 | jasmine.Ajax.withMock(function() { 68 | const xhr = new XMLHttpRequest(); 69 | xhr.onreadystatechange = function(args) { 70 | if (this.readyState === this.DONE) { 71 | doneFn(this.responseText); 72 | } 73 | }; 74 | 75 | xhr.open('GET', '/some/cool/url'); 76 | xhr.send(); 77 | 78 | expect(doneFn).not.toHaveBeenCalled(); 79 | 80 | jasmine.Ajax.requests.mostRecent().respondWith({ 81 | 'status': 200, 82 | 'responseText': 'in spec response' 83 | }); 84 | 85 | expect(doneFn).toHaveBeenCalledWith('in spec response'); 86 | }); 87 | }); 88 | 89 | it('allows use in a single spec (json response)', () => { 90 | const doneFn = jasmine.createSpy('success'); 91 | jasmine.Ajax.withMock(function() { 92 | const xhr = new XMLHttpRequest(); 93 | xhr.onreadystatechange = function(args) { 94 | if (this.readyState === this.DONE) { 95 | doneFn(this.response); 96 | } 97 | }; 98 | 99 | xhr.open('GET', '/some/cool/url'); 100 | xhr.send(); 101 | 102 | expect(doneFn).not.toHaveBeenCalled(); 103 | 104 | const data = { data: [{ id: 42, name: 'Dude' }] }; 105 | const expectedResponse = JSON.stringify(data); 106 | 107 | jasmine.Ajax.requests.mostRecent().respondWith({ 108 | 'status': 200, 109 | 'contentType': 'application/json', 110 | 'response': expectedResponse 111 | }); 112 | 113 | expect(doneFn).toHaveBeenCalledWith(expectedResponse); 114 | }); 115 | }); 116 | 117 | }); 118 | -------------------------------------------------------------------------------- /tsconfig-ngc.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "es2015", 5 | "moduleResolution": "node", 6 | "sourceMap": true, 7 | "inlineSources": true, 8 | "declaration": true, 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "lib": [ "es2015", "dom" ], 12 | "noImplicitAny": true, 13 | "suppressImplicitAnyIndexErrors": true 14 | }, 15 | "files": [ 16 | "src/in-mem/index.ts" 17 | ], 18 | "angularCompilerOptions": { 19 | "genDir": "aot", 20 | "skipMetadataEmit" : false 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "sourceMap": true, 7 | "inlineSources": true, 8 | "declaration": true, 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "lib": [ "es2015", "dom" ], 12 | "noImplicitAny": true, 13 | "suppressImplicitAnyIndexErrors": true, 14 | "strictPropertyInitialization": false 15 | }, 16 | "exclude": [ 17 | "node_modules", 18 | "typings/main", 19 | "typings/main.d.ts", 20 | "**/*.d.ts" 21 | ], 22 | "angularCompilerOptions": { 23 | "genDir": "aot", 24 | "skipMetadataEmit" : false 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "class-name": true, 4 | "comment-format": [ 5 | true, 6 | "check-space" 7 | ], 8 | "curly": true, 9 | "eofline": true, 10 | "forin": true, 11 | "indent": [ 12 | true, 13 | "spaces" 14 | ], 15 | "label-position": true, 16 | "label-undefined": true, 17 | "max-line-length": [ 18 | true, 19 | 140 20 | ], 21 | "member-access": false, 22 | "member-ordering": [ 23 | true, 24 | "static-before-instance", 25 | "variables-before-functions" 26 | ], 27 | "no-arg": true, 28 | "no-bitwise": true, 29 | "no-console": [ 30 | true, 31 | "debug", 32 | "info", 33 | "time", 34 | "timeEnd", 35 | "trace" 36 | ], 37 | "no-construct": true, 38 | "no-debugger": true, 39 | "no-duplicate-key": true, 40 | "no-duplicate-variable": true, 41 | "no-empty": false, 42 | "no-eval": true, 43 | "no-inferrable-types": true, 44 | "no-shadowed-variable": true, 45 | "no-string-literal": false, 46 | "no-switch-case-fall-through": true, 47 | "no-trailing-whitespace": true, 48 | "no-unused-expression": true, 49 | "no-unused-variable": true, 50 | "no-unreachable": true, 51 | "no-use-before-declare": true, 52 | "no-var-keyword": true, 53 | "object-literal-sort-keys": false, 54 | "one-line": [ 55 | true, 56 | "check-open-brace", 57 | "check-catch", 58 | "check-else", 59 | "check-whitespace" 60 | ], 61 | "quotemark": [ 62 | true, 63 | "single" 64 | ], 65 | "radix": true, 66 | "semicolon": [ 67 | "always" 68 | ], 69 | "triple-equals": [ 70 | true, 71 | "allow-null-check" 72 | ], 73 | "typedef-whitespace": [ 74 | true, 75 | { 76 | "call-signature": "nospace", 77 | "index-signature": "nospace", 78 | "parameter": "nospace", 79 | "property-declaration": "nospace", 80 | "variable-declaration": "nospace" 81 | } 82 | ], 83 | "variable-name": false, 84 | "whitespace": [ 85 | true, 86 | "check-branch", 87 | "check-decl", 88 | "check-operator", 89 | "check-separator", 90 | "check-type" 91 | ] 92 | } 93 | } 94 | --------------------------------------------------------------------------------