├── .browserslistrc ├── .circleci └── config.yml ├── .editorconfig ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── README.md ├── angular.json ├── e2e ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts └── tsconfig.json ├── karma.conf.js ├── package-lock.json ├── package.json ├── projects └── ngx-resource │ ├── core │ ├── README.md │ ├── karma.conf.js │ ├── ng-package.json │ ├── package.json │ ├── src │ │ ├── lib │ │ │ ├── Declarations.ts │ │ │ ├── Resource.ts │ │ │ ├── ResourceAction.ts │ │ │ ├── ResourceCommon │ │ │ │ ├── ResourceCRUD.ts │ │ │ │ ├── ResourceCRUDBase.ts │ │ │ │ ├── ResourceCRUDObservable.ts │ │ │ │ └── ResourceCRUDPromise.ts │ │ │ ├── ResourceGlobalConfig.ts │ │ │ ├── ResourceHandler.ts │ │ │ ├── ResourceHandlerNoop.ts │ │ │ ├── ResourceHelper.ts │ │ │ ├── ResourceModel.ts │ │ │ ├── ResourceModule.ts │ │ │ └── ResourceParams.ts │ │ ├── public-api.ts │ │ └── test.ts │ ├── tsconfig.lib.json │ ├── tsconfig.lib.prod.json │ ├── tsconfig.spec.json │ └── tslint.json │ ├── handler-cordova-advanced-http │ ├── README.md │ ├── karma.conf.js │ ├── ng-package.json │ ├── package.json │ ├── src │ │ ├── lib │ │ │ ├── ResourceHandlerCordovaAdvancedHttp.ts │ │ │ └── ResourceModule.ts │ │ ├── public-api.ts │ │ └── test.ts │ ├── tsconfig.lib.json │ ├── tsconfig.lib.prod.json │ ├── tsconfig.spec.json │ └── tslint.json │ ├── handler-fetch │ ├── README.md │ ├── karma.conf.js │ ├── ng-package.json │ ├── package.json │ ├── src │ │ ├── lib │ │ │ ├── ResourceHandlerFetch.ts │ │ │ └── ResourceModule.ts │ │ ├── public-api.ts │ │ └── test.ts │ ├── tsconfig.lib.json │ ├── tsconfig.lib.prod.json │ ├── tsconfig.spec.json │ └── tslint.json │ └── handler-ngx-http │ ├── README.md │ ├── karma.conf.js │ ├── ng-package.json │ ├── package.json │ ├── src │ ├── lib │ │ ├── ResourceHandlerHttpClient.ts │ │ └── ResourceModule.ts │ ├── public-api.ts │ └── test.ts │ ├── tsconfig.lib.json │ ├── tsconfig.lib.prod.json │ ├── tsconfig.spec.json │ └── tslint.json ├── proxy.conf.json ├── server ├── package-lock.json └── package.json ├── src ├── app │ ├── app.component.html │ ├── app.component.scss │ ├── app.component.spec.ts │ ├── app.component.ts │ ├── app.module.ts │ ├── components │ │ └── crud-obs-test │ │ │ ├── crud-obs-test.component.html │ │ │ ├── crud-obs-test.component.scss │ │ │ ├── crud-obs-test.component.spec.ts │ │ │ └── crud-obs-test.component.ts │ └── resources │ │ └── crud-obs-test │ │ ├── crud-obs-test.resource.spec.ts │ │ └── crud-obs-test.resource.ts ├── assets │ └── .gitkeep ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── main.ts ├── polyfills.ts ├── styles.scss └── test.ts ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.spec.json └── tslint.json /.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | - image: circleci/node:lts 11 | 12 | # Specify service dependencies here if necessary 13 | # CircleCI maintains a library of pre-built images 14 | # documented at https://circleci.com/docs/2.0/circleci-images/ 15 | # - image: circleci/mongo:3.4.4 16 | 17 | working_directory: ~/repo 18 | 19 | steps: 20 | - checkout 21 | 22 | # Download and cache dependencies 23 | - restore_cache: 24 | keys: 25 | - v1-dependencies-{{ checksum "package.json" }} 26 | # fallback to using the latest cache if no exact match is found 27 | - v1-dependencies- 28 | 29 | - run: 30 | name: Npm Install 31 | command: npm install 32 | 33 | - save_cache: 34 | paths: 35 | - node_modules 36 | key: v1-dependencies-{{ checksum "package.json" }} 37 | 38 | - run: 39 | name: Test "@ngx-resource/core" 40 | command: npm run core.lint 41 | 42 | - run: 43 | name: Test "@ngx-resource/handler-ngx-http" 44 | command: npm run http.lint 45 | 46 | - run: 47 | name: Test "@ngx-resource/handler-cordova-advanced-http" 48 | command: npm run cordova.lint 49 | 50 | - run: 51 | name: Test "@ngx-resource/handler-fetch" 52 | command: npm run fetch.lint 53 | 54 | - run: 55 | name: Build "@ngx-resource/core" 56 | command: npm run core.build 57 | 58 | - run: 59 | name: Build "@ngx-resource/handler-ngx-http" 60 | command: npm run http.build 61 | 62 | - run: 63 | name: Build "@ngx-resource/handler-cordova-advanced-http" 64 | command: npm run cordova.build 65 | 66 | - run: 67 | name: Build "@ngx-resource/handler-fetch" 68 | command: npm run fetch.build 69 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | insert_final_newline = false 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Misc 3 | ################# 4 | **/.DS_Store 5 | nbproject 6 | manifest.mf 7 | build.xml 8 | node_modules/* 9 | npm-debug.log 10 | *.js 11 | !config/* 12 | !karma.conf.js 13 | !webpack.config.js 14 | *.map 15 | *.d.ts 16 | !make.js 17 | !examples/**/*.js 18 | coverage 19 | *.metadata.json 20 | bundles 21 | dist 22 | server/node_modules 23 | 24 | ################# 25 | ## JetBrains 26 | ################# 27 | .idea 28 | *.iml 29 | .project 30 | .settings 31 | 32 | ############ 33 | ## Windows 34 | ############ 35 | 36 | # Windows image file caches 37 | Thumbs.db 38 | 39 | # Folder config file 40 | Desktop.ini 41 | 42 | ############ 43 | ## Mac 44 | ############ 45 | 46 | # Mac crap 47 | .DS_Store 48 | 49 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Misc 3 | ################# 4 | **/.DS_Store 5 | nbproject 6 | manifest.mf 7 | build.xml 8 | node_modules/* 9 | npm-debug.log 10 | *.ts 11 | !*.d.ts 12 | tests 13 | examples 14 | config 15 | .github 16 | coverage 17 | !*.metadata.json 18 | !bundles/*.js 19 | 20 | ################# 21 | ## JetBrains 22 | ################# 23 | .idea 24 | .project 25 | .settings 26 | 27 | ############ 28 | ## Windows 29 | ############ 30 | 31 | # Windows image file caches 32 | Thumbs.db 33 | 34 | # Folder config file 35 | Desktop.ini 36 | 37 | ############ 38 | ## Mac 39 | ############ 40 | 41 | # Mac crap 42 | .DS_Store 43 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 7.1.0 2 | * Unnecessary to provide `ResourceHandler` in the resource constructor 3 | (@Optional). 4 | 5 | ## 7.0.0 6 | 7 | ### Breaking changes: 8 | #### `ResourceGlobalConfig` 9 | * removed `asPromise` and `asResourceResponse` 10 | * added `returnAs` with enum type `ResourceActionReturnType`; Default is `ResourceActionReturnType.Observable` 11 | 12 | #### `IResourceParams` and `IResourceAction` 13 | * removed `asPromise` and `asResourceResponse` 14 | * added `returnAs` with enum type `ResourceActionReturnType`; Default is `ResourceActionReturnType.Observable` 15 | 16 | ### Deprecation 17 | * `IResourceMethod` -> `IResourceMethodPromise` 18 | * `IResourceMethodStrict` -> `IResourceMethodPromiseStrict` 19 | * `IResourceMethodFull` -> `IResourceMethodPromiseFull` 20 | * `IResourceMethodStrictFull` -> `IResourceMethodPromiseStrictFull` 21 | * `ResourceCRUD` -> `ResourceCRUDPromise` 22 | 23 | ### New 24 | * `ResourceCRUDObservable` - same as ResourceCRUDPromise, but typed to Observable 25 | 26 | ## 5.4.4 27 | 28 | ### Bug fix 29 | 30 | * Fixes Angular Universal compatibility 31 | 32 | ## 5.4.3 33 | 34 | ### Bug fix 35 | 36 | * Remove `id` from body of delete request 37 | 38 | ## 5.4.2 39 | 40 | ### Improvement 41 | 42 | * Added new query mapping method `ResourceQueryMappingMethod.None` in order 43 | to have query params as is (not converted) 44 | 45 | ## 5.4.1 46 | 47 | ### Improvement 48 | 49 | * Added fourth optional generic type for query result 50 | `ResourceCRUD` 51 | 52 | ## 5.4.0 53 | 54 | ### Improvement 55 | 56 | * Added `patch` method to CRUD resource 57 | 58 | ## 5.3.0 59 | 60 | ### Improvement 61 | 62 | * Implemented new ResourceAction flag `asResourceResponse` which will 63 | make reshource method to return IResourceResponse object instead of just body. 64 | 65 | * Interfaces for auto complition 66 | * `IResourceMethodStrictFull` 67 | * `IResourceMethodFull` 68 | * `IResourceMethodResultStrictFull` 69 | * `IResourceMethodResultFull` 70 | 71 | ## 5.2.2 72 | 73 | ### Bugs fixed 74 | 75 | * Remove body from DELETE requests #20 76 | 77 | ## 5.2.0 78 | 79 | ### Improvement 80 | 81 | * Added static fields to `ResourceModel` in order to define custom resource method names 82 | ``` typescript 83 | protected static methodQuery: string = 'query'; 84 | protected static methodGet: string = 'get'; 85 | protected static methodCreate: string = 'create'; 86 | protected static methodUpdate: string = 'update'; 87 | protected static methodRemove: string = 'remove'; 88 | ``` 89 | 90 | ## 5.1.0 91 | 92 | ### Improvement 93 | 94 | * Added static methods `ResourceModel` in order to not inject resources (need to inject once to create instance into 95 | your `AppComponent` (first loaded component) 96 | * `get(id: string): Promise` 97 | * `query(query?: any): Promise` 98 | * `remove(id: string): Promise` 99 | 100 | ## 5.0.0 101 | 102 | ### Breaking changes 103 | 104 | * Use npm `@ngx-resource/core` instead of `rest-core` 105 | * All `Rest` names and file refactored to `Resource` 106 | 107 | ## 0.2.0 108 | 109 | ### Bug Fixes 110 | 111 | * Default query parameter build method set to `RestGlobalConfig` 112 | 113 | ### Improvements 114 | 115 | * Added flag `queryMappingMethod` to `RestParams` and `RestAction` to define 116 | query parameter build method per Rest class or per method. 117 | 118 | ### Breaking Changes 119 | 120 | * `getParamsMappingType` property renamed to `queryMappingMethod` in `RestGlobalConfig` 121 | * `RestGetParamsMappingType` enum renamed to `RestQueryMappingMethod` 122 | 123 | ## 0.1.2 Release 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Unfortunately I don't have time to maintain the library. So PRs are more than welcome. 2 |

3 | 4 | | @ngx-resource/core | @ngx-resource/handler-ngx-http | @ngx-resource/handler-cordova-advanced-http | @ngx-resource/handler-ngx-http-legacy | 5 | | --- | --- | --- | --- | 6 | | [![npm version](https://img.shields.io/npm/v/%40ngx-resource%2Fcore.svg)](https://www.npmjs.com/package/@ngx-resource/core) | [![npm version](https://img.shields.io/npm/v/%40ngx-resource%2Fhandler-ngx-http.svg)](https://www.npmjs.com/package/@ngx-resource/handler-ngx-http) | [![npm version](https://img.shields.io/npm/v/%40ngx-resource%2Fhandler-cordova-advanced-http.svg)](https://www.npmjs.com/package/@ngx-resource/handler-cordova-advanced-http) | [![npm version](https://img.shields.io/npm/v/%40ngx-resource%2Fhandler-ngx-http-legacy.svg)](https://www.npmjs.com/package/@ngx-resource/handler-ngx-http-legacy) | 7 | 8 | # @ngx-resource/core 9 | Resource Core is an evolution of ngx-resource lib which provides flexibility for developers. Each developer can implement their own request handlers to easily customize the behavior. 10 | In fact, `@ngx-resource/core` is an abstract common library which uses `ResourceHandler` to make requests, so it's even possible to use the lib on node.js server side with typescript. You just need to implement a `ResourceHandler` for it. 11 | 12 | All my examples will be based on angular 4.4.4+ 13 | 14 | ### Known ResourceHandlers 15 | * `@ngx-resource/handler-ngx-http`. Based on `HttpClient` from `@angular/common/http`. Includes `ResourceModule.forRoot`. 16 | * `@ngx-resource/handler-ngx-http-legacy`. Based on `Http` from `@angular/http`. Includes `ResourceModule.forRoot`. 17 | * `@ngx-resource/handler-cordova-advanced-http`. Based on [Cordova Plugin Advanced HTTP](`https://github.com/silkimen/cordova-plugin-advanced-http`). 18 | * `@ngx-resource/handler-fetch`. Besed on [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). Not yet created. 19 | 20 | 21 | ## Creation of Resource class 22 | 23 | ```typescript 24 | @Injectable() 25 | @ResourceParams({ 26 | // IResourceParams 27 | pathPrefix: '/auth' 28 | }) 29 | export class MyAuthResource extends Resource { 30 | 31 | @ResourceAction({ 32 | // IResourceAction 33 | method: ResourceRequestMethod.Post, 34 | path: '/login' 35 | }) 36 | login: IResourceMethod<{login: string, password: string}, IReturnData>; // will make an post request to /auth/login 37 | 38 | @ResourceAction({ 39 | // IResourceAction 40 | //method: ResourceRequestMethod.Get is by default 41 | path: '/logout' 42 | }) 43 | logout: IResourceMethod; 44 | 45 | constructor(handler: ResourceHandler) { 46 | super(handler); 47 | } 48 | 49 | } 50 | 51 | @Injectable() 52 | @ResourceParams({ 53 | // IResourceParams 54 | pathPrefix: '/user' 55 | }) 56 | export class UserResource extends Resource { 57 | 58 | @ResourceAction({ 59 | path: '/{!id}' 60 | }) 61 | getUser: IResourceMethodPromise<{id: string}, IUser>; // will call /user/id 62 | 63 | @ResourceAction({ 64 | method: ResourceRequestMethod.Post 65 | }) 66 | createUser: IResourceMethodPromiseStrict; 67 | 68 | @ResoucreAction({ 69 | path: '/test/data', 70 | asResourceResponse: true 71 | }) 72 | testUserRequest: IResourceMethodPromiseFull<{id: string}, IUser>; // will call /test/data and receive repsponse object with headers, status and body 73 | 74 | constructor(restHandler: ResourceHandler) { 75 | super(restHandler); 76 | } 77 | 78 | } 79 | 80 | // Using created Resource 81 | @Injectable 82 | export class MyService { 83 | 84 | private user: IUser = null; 85 | 86 | constructor(private myResource: MyAuthResource, private userResource: UserResource) {} 87 | 88 | doLogin(login: string, password: string): Promise { 89 | return this.myResource.login({login, password}); 90 | } 91 | 92 | doLogout(): Promise { 93 | return this.myResource.logout(); 94 | } 95 | 96 | async loginAndLoadUser(login: string, password: string, userId: string): Promise { 97 | await this.doLogin(login, password); 98 | this.user = await this.userResource.getUser({id: userId}); 99 | } 100 | 101 | } 102 | 103 | ``` 104 | 105 | Final url is generated by concatination of `$getUrl`, `$getPathPrefix` and `$getPath` methods of `Resource` base class. 106 | 107 | ### [IResourceParams](https://github.com/troyanskiy/ngx-resource-core/blob/master/src/Declarations.ts#L2-L23) 108 | 109 | Is used by `ResourceParams` decorator for class decoration 110 | 111 | List of params: 112 | * `url?: string;` - url of the api server; *default `''`* 113 | * `pathPrefix?: string;` - path prefix of the api; *default `''`* 114 | * `path?: string;` - path of the api; *default `''`* 115 | * `headers?: any;` - headers; *default `{}`* 116 | * `body?: any;` - default body; *default `null`* 117 | * `params?: any;` - default url params; *default `null`* 118 | * `query?: any;` - defualt query params; *default `null`* 119 | * `rootNode?: string;` - key to assign all body; *default `null`* 120 | * `removeTrailingSlash?: boolean;` - *default `true`* 121 | * `addTimestamp?: boolean | string;` - *default `false`* 122 | * `withCredentials?: boolean;` - *default `false`* 123 | * `lean?: boolean;` - do no add `$` properties on result. Used only with `toPromise: false` *default `false`* 124 | * `mutateBody?: boolean;` - if need to mutate provided body with response body. *default `false`* 125 | * `returnAs?: ResourceActionReturnType;` - what type response should be returned by action/method . *default `ResourceActionReturnType.Observable`* 126 | * `keepEmptyBody?: boolean;` - if need to keep empty body object `{}` 127 | * `requestBodyType?: ResourceRequestBodyType;` - request body type. *default: will be detected automatically*. 128 | Check for possible body types in the sources of [ResourceRequestBodyType](https://github.com/troyanskiy/ngx-resource-core/blob/master/src/Declarations.ts#L114-L122). Type detection algorithm [check here](https://github.com/troyanskiy/ngx-resource-core/blob/master/src/ResourceHelper.ts#L12-L34). 129 | * `responseBodyType?: ResourceResponseBodyType;` - response body type. *default: `ResourceResponseBodyType.JSON`* Possible body type can be checked here [ResourceResponseBodyType](https://github.com/troyanskiy/ngx-resource-core/blob/master/src/Declarations.ts#L124-L129). 130 | 131 | ### [IResourceAction](https://github.com/troyanskiy/ngx-resource-core/blob/master/src/Declarations.ts#L2-L31) 132 | 133 | Is used by `ResourceAction` decorator for methods. 134 | 135 | List of params (is all of the above) plus the following: 136 | * `method?: ResourceRequestMethod;` - method of request. *Default `ResourceRequestMethod.Get`*. All possible methods listed in [ResourceRequestMethod](https://github.com/troyanskiy/ngx-resource-core/blob/master/src/Declarations.ts#L131-L139) 137 | * `expectJsonArray?: boolean;` - if expected to receive an array. The field is used only with `toPromise: false`. *Default `false`*. 138 | * `resultFactory?: IResourceResultFactory;` - custom method to create result object. *Default: `returns {}`* 139 | * `map?: IResourceResponseMap;` - custom data mapping method. *Default: `returns without any changes`* 140 | * `filter?: IResourceResponseFilter;` - custom data filtering method. *Default: `returns true`* 141 | 142 | ### [ResourceGlobalConfig](https://github.com/troyanskiy/ngx-resource-core/blob/master/src/ResourceGlobalConfig.ts) 143 | Mainly used to set defaults. 144 | 145 | 146 | ## Models 147 | An object with built-in in methods to save, update, and delete a model. 148 | Here is an example of a `User` model. 149 | 150 | Note: UserResource should be injected at the beginning in order to use static 151 | model method like `User.get()`, `User.query()`, `User.remove()` 152 | 153 | ```typescript 154 | 155 | export interface IPaginationQuery { 156 | page?: number; 157 | perPage?: number; 158 | } 159 | 160 | export interface IGroupQuery extends IPaginationQuery { 161 | title?: string; 162 | } 163 | 164 | export interface IUserQuery extends IPaginationQuery { 165 | firstName?: string; 166 | lastName?: string; 167 | groupId?: number; 168 | } 169 | 170 | export interface IUser { 171 | id: number; 172 | userName: string; 173 | firstName: string; 174 | lastName: string; 175 | groupId: string; 176 | } 177 | 178 | export class GroupResource extends ResourceCRUDPromise { 179 | 180 | constructor(restHandler: ResourceHandler) { 181 | super(restHandler); 182 | } 183 | 184 | $resultFactory(data: any, options: IResourceActionInner = {}): any { 185 | return new Group(data); 186 | } 187 | 188 | } 189 | 190 | export class Group extends ResourceModel { 191 | 192 | readonly $resource = GroupResource; 193 | 194 | id: number; 195 | title: string; 196 | 197 | constructor(data?: IGroup) { 198 | super(); 199 | if (data) { 200 | this.$setData(data); 201 | } 202 | } 203 | 204 | $setData(data: IGroup) { 205 | this.id = data.id; 206 | this.title = data.title; 207 | } 208 | 209 | } 210 | 211 | export class UserResource extends ResourceCRUDPromise { 212 | 213 | constructor(restHandler: ResourceHandler) { 214 | super(restHandler); 215 | } 216 | 217 | $resultFactory(data: any, options: IResourceActionInner = {}): any { 218 | return new User(data); 219 | } 220 | 221 | } 222 | 223 | export class User extends ResourceModel implements IUser { 224 | 225 | readonly $resource = UserResource; 226 | 227 | id: number; 228 | userName: string; 229 | firstName: string; 230 | lastName: string; 231 | groupId: string; 232 | 233 | fullName: string; // generated from first name and last name 234 | 235 | constructor(data?: IUser) { 236 | super(); 237 | if (data) { 238 | this.$setData(data); 239 | } 240 | } 241 | 242 | $setData(data: IUser): this { 243 | Object.assign(this, data); 244 | this.fullName = `${this.firstName} ${this.lastName}`; 245 | return this; 246 | } 247 | 248 | toJSON() { 249 | // here i'm using lodash lib pick method. 250 | return _.pick(this, ['id', 'firstName', 'lastName', 'groupId']); 251 | } 252 | 253 | } 254 | 255 | 256 | // example of using the staff 257 | async someMethodToCreateGroupAndUser() { 258 | 259 | // Creation a group 260 | const group = new Group(); 261 | group.title = 'My group'; 262 | 263 | // Saving the group 264 | await group.$save(); 265 | 266 | // Creating an user 267 | const user = new User({ 268 | userName: 'troyanskiy', 269 | firstName: 'firstName', 270 | lastName: 'lastName', 271 | groupId: group.id 272 | }); 273 | 274 | // Saving the user 275 | await user.$save(); 276 | 277 | 278 | // Query data from server 279 | 280 | const user1 = await this.userResource.get('1'); 281 | 282 | // or 283 | 284 | const user2: User = await User.get('id'); 285 | 286 | } 287 | 288 | ``` 289 | 290 | 291 | ## QueryParams Conversion 292 | 293 | You can define the way query params are converted. 294 | Set the global config at the root of your app. 295 | 296 | `ResourceGlobalConfig.queryMappingMethod = ResourceQueryMappingMethod.` 297 | 298 | ``` 299 | { 300 | a: [{ b:1, c: [2, 3] }] 301 | } 302 | ``` 303 | 304 | With `` being one of the following: 305 | 306 | #### Plain (default) 307 | No conversion at all 308 | 309 | Output: `?a=[Object object]` 310 | 311 | #### Bracket 312 | All array elements will be indexed 313 | 314 | Output: `?a[0][b]=10383&a[0][c][0]=2&a[0][c][1]=3` 315 | 316 | #### JQueryParamsBracket 317 | Implements the standard $.params way of converting 318 | 319 | Output: `?a[0][b]=10383&a[0][c][]=2&a[0][c][]=3` 320 | 321 | 322 | 323 | # @ngx-resource/handler-ngx-http 324 | 325 | It's implementation of `ResourceHandler` which uses Angular `HttpClient` 326 | 327 | # If you are using Angular 5, please use @ngx-resource/handler-ngx-http 5.x 328 | 329 | ## How to install and setup it 330 | ```bash 331 | & npm i --save @ngx-resource/core @ngx-resource/handler-ngx-http 332 | ``` 333 | 334 | In you app module 335 | ```typescript 336 | 337 | // AoT requires an exported function for factories 338 | export function myHandlerFactory(http: HttpClient) { 339 | return new MyResourceHandler(http); 340 | } 341 | 342 | @NgModule({ 343 | imports: [ 344 | BrowserModule, 345 | BrowserAnimationsModule, 346 | HttpClientModule, 347 | 348 | // Default ResourceHandler uses class `ResourceHandlerHttpClient` 349 | ResourceModule.forRoot() 350 | 351 | // Or set you own handler 352 | //ResourceModule.forRoot({ 353 |    //  handler: { provide: ResourceHandler, useFactory: (myHandlerFactory), deps: [HttpClient] } 354 | //}) 355 | ], 356 | declarations: [...], 357 | bootstrap: [...], 358 | entryComponents: [...], 359 | providers: [...] 360 | }) 361 | export class AppModule { 362 | } 363 | ``` 364 | 365 | ## [Docs about @ngx-resource/core](https://github.com/troyanskiy/ngx-resource-core/blob/master/README.md) 366 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "ngx-resource-mono": { 7 | "projectType": "application", 8 | "schematics": { 9 | "@schematics/angular:component": { 10 | "style": "scss" 11 | } 12 | }, 13 | "root": "", 14 | "sourceRoot": "src", 15 | "prefix": "app", 16 | "architect": { 17 | "build": { 18 | "builder": "@angular-devkit/build-angular:browser", 19 | "options": { 20 | "outputPath": "dist/ngx-resource-mono", 21 | "index": "src/index.html", 22 | "main": "src/main.ts", 23 | "polyfills": "src/polyfills.ts", 24 | "tsConfig": "tsconfig.app.json", 25 | "aot": true, 26 | "assets": [ 27 | "src/favicon.ico", 28 | "src/assets" 29 | ], 30 | "styles": [ 31 | "src/styles.scss" 32 | ], 33 | "scripts": [] 34 | }, 35 | "configurations": { 36 | "production": { 37 | "fileReplacements": [ 38 | { 39 | "replace": "src/environments/environment.ts", 40 | "with": "src/environments/environment.prod.ts" 41 | } 42 | ], 43 | "optimization": true, 44 | "outputHashing": "all", 45 | "sourceMap": false, 46 | "namedChunks": false, 47 | "extractLicenses": true, 48 | "vendorChunk": false, 49 | "buildOptimizer": true, 50 | "budgets": [ 51 | { 52 | "type": "initial", 53 | "maximumWarning": "2mb", 54 | "maximumError": "5mb" 55 | }, 56 | { 57 | "type": "anyComponentStyle", 58 | "maximumWarning": "6kb" 59 | } 60 | ] 61 | } 62 | } 63 | }, 64 | "serve": { 65 | "builder": "@angular-devkit/build-angular:dev-server", 66 | "options": { 67 | "browserTarget": "ngx-resource-mono:build", 68 | "proxyConfig": "proxy.conf.json" 69 | }, 70 | "configurations": { 71 | "production": { 72 | "browserTarget": "ngx-resource-mono:build:production" 73 | } 74 | } 75 | }, 76 | "extract-i18n": { 77 | "builder": "@angular-devkit/build-angular:extract-i18n", 78 | "options": { 79 | "browserTarget": "ngx-resource-mono:build" 80 | } 81 | }, 82 | "test": { 83 | "builder": "@angular-devkit/build-angular:karma", 84 | "options": { 85 | "main": "src/test.ts", 86 | "polyfills": "src/polyfills.ts", 87 | "tsConfig": "tsconfig.spec.json", 88 | "karmaConfig": "karma.conf.js", 89 | "assets": [ 90 | "src/favicon.ico", 91 | "src/assets" 92 | ], 93 | "styles": [ 94 | "src/styles.scss" 95 | ], 96 | "scripts": [] 97 | } 98 | }, 99 | "lint": { 100 | "builder": "@angular-devkit/build-angular:tslint", 101 | "options": { 102 | "tsConfig": [ 103 | "tsconfig.app.json", 104 | "tsconfig.spec.json", 105 | "e2e/tsconfig.json" 106 | ], 107 | "exclude": [ 108 | "**/node_modules/**" 109 | ] 110 | } 111 | }, 112 | "e2e": { 113 | "builder": "@angular-devkit/build-angular:protractor", 114 | "options": { 115 | "protractorConfig": "e2e/protractor.conf.js", 116 | "devServerTarget": "ngx-resource-mono:serve" 117 | }, 118 | "configurations": { 119 | "production": { 120 | "devServerTarget": "ngx-resource-mono:serve:production" 121 | } 122 | } 123 | } 124 | } 125 | }, 126 | "@ngx-resource/core": { 127 | "projectType": "library", 128 | "root": "projects/ngx-resource/core", 129 | "sourceRoot": "projects/ngx-resource/core/src", 130 | "prefix": "lib", 131 | "architect": { 132 | "build": { 133 | "builder": "@angular-devkit/build-angular:ng-packagr", 134 | "options": { 135 | "tsConfig": "projects/ngx-resource/core/tsconfig.lib.json", 136 | "project": "projects/ngx-resource/core/ng-package.json" 137 | } 138 | , "configurations": { 139 | "production": { 140 | "tsConfig": "projects/ngx-resource/core/tsconfig.lib.prod.json" 141 | } 142 | } 143 | }, 144 | "test": { 145 | "builder": "@angular-devkit/build-angular:karma", 146 | "options": { 147 | "main": "projects/ngx-resource/core/src/test.ts", 148 | "tsConfig": "projects/ngx-resource/core/tsconfig.spec.json", 149 | "karmaConfig": "projects/ngx-resource/core/karma.conf.js" 150 | } 151 | }, 152 | "lint": { 153 | "builder": "@angular-devkit/build-angular:tslint", 154 | "options": { 155 | "tsConfig": [ 156 | "projects/ngx-resource/core/tsconfig.lib.json", 157 | "projects/ngx-resource/core/tsconfig.spec.json" 158 | ], 159 | "exclude": [ 160 | "**/node_modules/**" 161 | ] 162 | } 163 | } 164 | } 165 | }, 166 | "@ngx-resource/handler-ngx-http": { 167 | "projectType": "library", 168 | "root": "projects/ngx-resource/handler-ngx-http", 169 | "sourceRoot": "projects/ngx-resource/handler-ngx-http/src", 170 | "prefix": "lib", 171 | "architect": { 172 | "build": { 173 | "builder": "@angular-devkit/build-angular:ng-packagr", 174 | "options": { 175 | "tsConfig": "projects/ngx-resource/handler-ngx-http/tsconfig.lib.json", 176 | "project": "projects/ngx-resource/handler-ngx-http/ng-package.json" 177 | } 178 | , "configurations": { 179 | "production": { 180 | "tsConfig": "projects/ngx-resource/handler-ngx-http/tsconfig.lib.prod.json" 181 | } 182 | } 183 | }, 184 | "test": { 185 | "builder": "@angular-devkit/build-angular:karma", 186 | "options": { 187 | "main": "projects/ngx-resource/handler-ngx-http/src/test.ts", 188 | "tsConfig": "projects/ngx-resource/handler-ngx-http/tsconfig.spec.json", 189 | "karmaConfig": "projects/ngx-resource/handler-ngx-http/karma.conf.js" 190 | } 191 | }, 192 | "lint": { 193 | "builder": "@angular-devkit/build-angular:tslint", 194 | "options": { 195 | "tsConfig": [ 196 | "projects/ngx-resource/handler-ngx-http/tsconfig.lib.json", 197 | "projects/ngx-resource/handler-ngx-http/tsconfig.spec.json" 198 | ], 199 | "exclude": [ 200 | "**/node_modules/**" 201 | ] 202 | } 203 | } 204 | } 205 | }, 206 | "@ngx-resource/handler-cordova-advanced-http": { 207 | "projectType": "library", 208 | "root": "projects/ngx-resource/handler-cordova-advanced-http", 209 | "sourceRoot": "projects/ngx-resource/handler-cordova-advanced-http/src", 210 | "prefix": "lib", 211 | "architect": { 212 | "build": { 213 | "builder": "@angular-devkit/build-angular:ng-packagr", 214 | "options": { 215 | "tsConfig": "projects/ngx-resource/handler-cordova-advanced-http/tsconfig.lib.json", 216 | "project": "projects/ngx-resource/handler-cordova-advanced-http/ng-package.json" 217 | } 218 | , "configurations": { 219 | "production": { 220 | "tsConfig": "projects/ngx-resource/handler-cordova-advanced-http/tsconfig.lib.prod.json" 221 | } 222 | } 223 | }, 224 | "test": { 225 | "builder": "@angular-devkit/build-angular:karma", 226 | "options": { 227 | "main": "projects/ngx-resource/handler-cordova-advanced-http/src/test.ts", 228 | "tsConfig": "projects/ngx-resource/handler-cordova-advanced-http/tsconfig.spec.json", 229 | "karmaConfig": "projects/ngx-resource/handler-cordova-advanced-http/karma.conf.js" 230 | } 231 | }, 232 | "lint": { 233 | "builder": "@angular-devkit/build-angular:tslint", 234 | "options": { 235 | "tsConfig": [ 236 | "projects/ngx-resource/handler-cordova-advanced-http/tsconfig.lib.json", 237 | "projects/ngx-resource/handler-cordova-advanced-http/tsconfig.spec.json" 238 | ], 239 | "exclude": [ 240 | "**/node_modules/**" 241 | ] 242 | } 243 | } 244 | } 245 | }, 246 | "@ngx-resource/handler-fetch": { 247 | "projectType": "library", 248 | "root": "projects/ngx-resource/handler-fetch", 249 | "sourceRoot": "projects/ngx-resource/handler-fetch/src", 250 | "prefix": "lib", 251 | "architect": { 252 | "build": { 253 | "builder": "@angular-devkit/build-angular:ng-packagr", 254 | "options": { 255 | "tsConfig": "projects/ngx-resource/handler-fetch/tsconfig.lib.json", 256 | "project": "projects/ngx-resource/handler-fetch/ng-package.json" 257 | } 258 | , "configurations": { 259 | "production": { 260 | "tsConfig": "projects/ngx-resource/handler-fetch/tsconfig.lib.prod.json" 261 | } 262 | } 263 | }, 264 | "test": { 265 | "builder": "@angular-devkit/build-angular:karma", 266 | "options": { 267 | "main": "projects/ngx-resource/handler-fetch/src/test.ts", 268 | "tsConfig": "projects/ngx-resource/handler-fetch/tsconfig.spec.json", 269 | "karmaConfig": "projects/ngx-resource/handler-fetch/karma.conf.js" 270 | } 271 | }, 272 | "lint": { 273 | "builder": "@angular-devkit/build-angular:tslint", 274 | "options": { 275 | "tsConfig": [ 276 | "projects/ngx-resource/handler-fetch/tsconfig.lib.json", 277 | "projects/ngx-resource/handler-fetch/tsconfig.spec.json" 278 | ], 279 | "exclude": [ 280 | "**/node_modules/**" 281 | ] 282 | } 283 | } 284 | } 285 | } 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('Welcome to ngx-resource-mono!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('app-root h1')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es2018", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, './coverage/ngx-resource-mono'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-resource-mono", 3 | "version": "10.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e", 11 | "core.lint": "ng lint @ngx-resource/core", 12 | "core.test": "npm run core.lint && ng test @ngx-resource/core --watch=false --browsers=ChromeHeadless --codeCoverage=true", 13 | "core.build": "ng build @ngx-resource/core --ts-config ./projects/ngx-resource/core/tsconfig.lib.prod.json", 14 | "core.publish": "npm publish dist/ngx-resource/core --access public", 15 | "core.full": "npm run core.test && npm run core.build && npm run core.publish", 16 | 17 | "http.lint": "ng lint @ngx-resource/handler-ngx-http", 18 | "http.test": "npm run http.lint && ng test @ngx-resource/handler-ngx-http --watch=false --browsers=ChromeHeadless --codeCoverage=true", 19 | "http.build": "ng build @ngx-resource/handler-ngx-http --ts-config ./projects/ngx-resource/handler-ngx-http/tsconfig.lib.prod.json", 20 | "http.publish": "npm publish dist/ngx-resource/handler-ngx-http --access public", 21 | "http.full": "npm run http.test && npm run http.build && npm run http.publish", 22 | 23 | "cordova.lint": "ng lint @ngx-resource/handler-cordova-advanced-http", 24 | "cordova.test": "npm run cordova.lint && ng test @ngx-resource/handler-cordova-advanced-http --watch=false --browsers=ChromeHeadless --codeCoverage=true", 25 | "cordova.build": "ng build @ngx-resource/handler-cordova-advanced-http --ts-config ./projects/ngx-resource/handler-cordova-advanced-http/tsconfig.lib.prod.json", 26 | "cordova.publish": "npm publish dist/ngx-resource/handler-cordova-advanced-http --access public", 27 | "cordova.full": "npm run cordova.test && npm run cordova.build && npm run cordova.publish", 28 | 29 | "fetch.lint": "ng lint @ngx-resource/handler-fetch", 30 | "fetch.test": "npm run fetch.lint && ng test @ngx-resource/handler-fetch --watch=false --browsers=ChromeHeadless --codeCoverage=true", 31 | "fetch.build": "ng build @ngx-resource/handler-fetch --ts-config ./projects/ngx-resource/handler-fetch/tsconfig.lib.prod.json", 32 | "fetch.publish": "npm publish dist/ngx-resource/handler-fetch --tag beta --access public", 33 | "fetch.full": "npm run fetch.test && npm run fetch.build && npm run fetch.publish", 34 | 35 | "ngrx.lint": "ng lint @ngx-resource/ngrx", 36 | "ngrx.test": "npm run ngrx.lint && ng test @ngx-resource/ngrx --watch=false --browsers=ChromeHeadless --codeCoverage=true", 37 | "ngrx.build": "ng build @ngx-resource/ngrx", 38 | "ngrx.publish": "npm publish dist/ngx-resource/ngrx --tag beta --access public", 39 | "ngrx.full": "npm run ngrx.test && npm run ngrx.build && npm run ngrx.publish", 40 | "all.build": "npm run core.build && npm run http.build && npm run cordova.build", 41 | "all.publish": "npm run core.publish && npm run http.publish && npm run cordova.publish" 42 | }, 43 | "private": true, 44 | "dependencies": { 45 | "@angular/animations": "^15.1.5", 46 | "@angular/common": "^15.1.5", 47 | "@angular/compiler": "^15.1.5", 48 | "@angular/core": "^15.1.5", 49 | "@angular/forms": "^15.1.5", 50 | "@angular/platform-browser": "^15.1.5", 51 | "@angular/platform-browser-dynamic": "^15.1.5", 52 | "@angular/router": "^15.1.5", 53 | "rxjs": "~7.5.0", 54 | "tslib": "^2.5.0", 55 | "zone.js": "~0.11.8" 56 | }, 57 | "devDependencies": { 58 | "@angular-devkit/build-angular": "^15.1.6", 59 | "@angular-devkit/core": "^15.1.6", 60 | "@angular-devkit/schematics": "^15.1.6", 61 | "@angular/cli": "^15.1.6", 62 | "@angular/compiler-cli": "^15.1.5", 63 | "@angular/language-service": "^15.1.5", 64 | "@types/jasmine": "~3.6.0", 65 | "@types/jasminewd2": "~2.0.3", 66 | "@types/node": "^12.11.1", 67 | "codelyzer": "^6.0.0", 68 | "jasmine-core": "~3.6.0", 69 | "jasmine-spec-reporter": "~5.0.0", 70 | "karma": "~6.4.1", 71 | "karma-chrome-launcher": "~3.1.0", 72 | "karma-coverage-istanbul-reporter": "~3.0.2", 73 | "karma-jasmine": "~4.0.0", 74 | "karma-jasmine-html-reporter": "^1.5.0", 75 | "ng-packagr": "^15.1.2", 76 | "protractor": "~7.0.0", 77 | "ts-node": "~8.3.0", 78 | "tslint": "~6.1.0", 79 | "typescript": "~4.9.5" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /projects/ngx-resource/core/README.md: -------------------------------------------------------------------------------- 1 | [![npm version](https://img.shields.io/npm/v/%40ngx-resource%2Fcore.svg)](https://www.npmjs.com/package/@ngx-resource/core) 2 | 3 | # @ngx-resource/core 4 | Resource Core is an evolution of ngx-resource lib which provides flexibility for developers. Each developer can implement their own request handlers to easily customize the behavior. 5 | In fact, `@ngx-resource/core` is an abstract common library which uses `ResourceHandler` to make requests, so it's even possible to use the lib on node.js server side with typescript. You just need to implement a `ResourceHandler` for it. 6 | 7 | All my examples will be based on angular 4.4.4+ 8 | 9 | ### Known ResourceHandlers 10 | * `@ngx-resource/handler-ngx-http`. Based on `HttpClient` from `@angular/common/http`. Includes `ResourceModule.forRoot`. 11 | * `@ngx-resource/handler-ngx-http-legacy`. Based on `Http` from `@angular/http`. Includes `ResourceModule.forRoot`. 12 | * `@ngx-resource/handler-cordova-advanced-http`. Based on [Cordova Plugin Advanced HTTP](`https://github.com/silkimen/cordova-plugin-advanced-http`). 13 | * `@ngx-resource/handler-fetch`. Besed on [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). Not yet created. 14 | 15 | 16 | ## Creation of Resource class 17 | 18 | ```typescript 19 | @Injectable() 20 | @ResourceParams({ 21 | // IResourceParams 22 | pathPrefix: '/auth' 23 | }) 24 | export class MyAuthResource extends Resource { 25 | 26 | @ResourceAction({ 27 | // IResourceAction 28 | method: ResourceRequestMethod.Post, 29 | path: '/login' 30 | }) 31 | login: IResourceMethod<{login: string, password: string}, IReturnData>; // will make an post request to /auth/login 32 | 33 | @ResourceAction({ 34 | // IResourceAction 35 | //method: ResourceRequestMethod.Get is by default 36 | path: '/logout' 37 | }) 38 | logout: IResourceMethod; 39 | 40 | constructor(handler: ResourceHandler) { 41 | super(handler); 42 | } 43 | 44 | } 45 | 46 | @Injectable() 47 | @ResourceParams({ 48 | // IResourceParams 49 | pathPrefix: '/user' 50 | }) 51 | export class UserResource extends Resource { 52 | 53 | @ResourceAction({ 54 | path: '/{!id}' 55 | }) 56 | getUser: IResourceMethodPromise<{id: string}, IUser>; // will call /user/id 57 | 58 | @ResourceAction({ 59 | method: ResourceRequestMethod.Post 60 | }) 61 | createUser: IResourceMethodPromiseStrict; 62 | 63 | @ResoucreAction({ 64 | path: '/test/data', 65 | asResourceResponse: true 66 | }) 67 | testUserRequest: IResourceMethodPromiseFull<{id: string}, IUser>; // will call /test/data and receive repsponse object with headers, status and body 68 | 69 | constructor(restHandler: ResourceHandler) { 70 | super(restHandler); 71 | } 72 | 73 | } 74 | 75 | // Using created Resource 76 | @Injectable 77 | export class MyService { 78 | 79 | private user: IUser = null; 80 | 81 | constructor(private myResource: MyAuthResource, private userResource: UserResource) {} 82 | 83 | doLogin(login: string, password: string): Promise { 84 | return this.myResource.login({login, password}); 85 | } 86 | 87 | doLogout(): Promise { 88 | return this.myResource.logout(); 89 | } 90 | 91 | async loginAndLoadUser(login: string, password: string, userId: string): Promise { 92 | await this.doLogin(login, password); 93 | this.user = await this.userResource.getUser({id: userId}); 94 | } 95 | 96 | } 97 | 98 | ``` 99 | 100 | Final url is generated by concatination of `$getUrl`, `$getPathPrefix` and `$getPath` methods of `Resource` base class. 101 | 102 | ### [IResourceParams](https://github.com/troyanskiy/ngx-resource-core/blob/master/src/Declarations.ts#L2-L23) 103 | 104 | Is used by `ResourceParams` decorator for class decoration 105 | 106 | List of params: 107 | * `url?: string;` - url of the api server; *default `''`* 108 | * `pathPrefix?: string;` - path prefix of the api; *default `''`* 109 | * `path?: string;` - path of the api; *default `''`* 110 | * `headers?: any;` - headers; *default `{}`* 111 | * `body?: any;` - default body; *default `null`* 112 | * `params?: any;` - default url params; *default `null`* 113 | * `query?: any;` - defualt query params; *default `null`* 114 | * `rootNode?: string;` - key to assign all body; *default `null`* 115 | * `removeTrailingSlash?: boolean;` - *default `true`* 116 | * `addTimestamp?: boolean | string;` - *default `false`* 117 | * `withCredentials?: boolean;` - *default `false`* 118 | * `lean?: boolean;` - do no add `$` properties on result. Used only with `toPromise: false` *default `false`* 119 | * `mutateBody?: boolean;` - if need to mutate provided body with response body. *default `false`* 120 | * `returnAs?: ResourceActionReturnType;` - what type response should be returned by action/method . *default `ResourceActionReturnType.Observable`* 121 | * `keepEmptyBody?: boolean;` - if need to keep empty body object `{}` 122 | * `requestBodyType?: ResourceRequestBodyType;` - request body type. *default: will be detected automatically*. 123 | Check for possible body types in the sources of [ResourceRequestBodyType](https://github.com/troyanskiy/ngx-resource-core/blob/master/src/Declarations.ts#L114-L122). Type detection algorithm [check here](https://github.com/troyanskiy/ngx-resource-core/blob/master/src/ResourceHelper.ts#L12-L34). 124 | * `responseBodyType?: ResourceResponseBodyType;` - response body type. *default: `ResourceResponseBodyType.JSON`* Possible body type can be checked here [ResourceResponseBodyType](https://github.com/troyanskiy/ngx-resource-core/blob/master/src/Declarations.ts#L124-L129). 125 | 126 | ### [IResourceAction](https://github.com/troyanskiy/ngx-resource-core/blob/master/src/Declarations.ts#L2-L31) 127 | 128 | Is used by `ResourceAction` decorator for methods. 129 | 130 | List of params (is all of the above) plus the following: 131 | * `method?: ResourceRequestMethod;` - method of request. *Default `ResourceRequestMethod.Get`*. All possible methods listed in [ResourceRequestMethod](https://github.com/troyanskiy/ngx-resource-core/blob/master/src/Declarations.ts#L131-L139) 132 | * `expectJsonArray?: boolean;` - if expected to receive an array. The field is used only with `toPromise: false`. *Default `false`*. 133 | * `resultFactory?: IResourceResultFactory;` - custom method to create result object. *Default: `returns {}`* 134 | * `map?: IResourceResponseMap;` - custom data mapping method. *Default: `returns without any changes`* 135 | * `filter?: IResourceResponseFilter;` - custom data filtering method. *Default: `returns true`* 136 | 137 | ### [ResourceGlobalConfig](https://github.com/troyanskiy/ngx-resource-core/blob/master/src/ResourceGlobalConfig.ts) 138 | Mainly used to set defaults. 139 | 140 | 141 | ## Models 142 | An object with built-in in methods to save, update, and delete a model. 143 | Here is an example of a `User` model. 144 | 145 | Note: UserResource should be injected at the beginning in order to use static 146 | model method like `User.get()`, `User.query()`, `User.remove()` 147 | 148 | ```typescript 149 | 150 | export interface IPaginationQuery { 151 | page?: number; 152 | perPage?: number; 153 | } 154 | 155 | export interface IGroupQuery extends IPaginationQuery { 156 | title?: string; 157 | } 158 | 159 | export interface IUserQuery extends IPaginationQuery { 160 | firstName?: string; 161 | lastName?: string; 162 | groupId?: number; 163 | } 164 | 165 | export interface IUser { 166 | id: number; 167 | userName: string; 168 | firstName: string; 169 | lastName: string; 170 | groupId: string; 171 | } 172 | 173 | export class GroupResource extends ResourceCRUDPromise { 174 | 175 | constructor(restHandler: ResourceHandler) { 176 | super(restHandler); 177 | } 178 | 179 | $resultFactory(data: any, options: IResourceActionInner = {}): any { 180 | return new Group(data); 181 | } 182 | 183 | } 184 | 185 | export class Group extends ResourceModel { 186 | 187 | readonly $resource = GroupResource; 188 | 189 | id: number; 190 | title: string; 191 | 192 | constructor(data?: IGroup) { 193 | super(); 194 | if (data) { 195 | this.$setData(data); 196 | } 197 | } 198 | 199 | $setData(data: IGroup) { 200 | this.id = data.id; 201 | this.title = data.title; 202 | } 203 | 204 | } 205 | 206 | export class UserResource extends ResourceCRUDPromise { 207 | 208 | constructor(restHandler: ResourceHandler) { 209 | super(restHandler); 210 | } 211 | 212 | $resultFactory(data: any, options: IResourceActionInner = {}): any { 213 | return new User(data); 214 | } 215 | 216 | } 217 | 218 | export class User extends ResourceModel implements IUser { 219 | 220 | readonly $resource = UserResource; 221 | 222 | id: number; 223 | userName: string; 224 | firstName: string; 225 | lastName: string; 226 | groupId: string; 227 | 228 | fullName: string; // generated from first name and last name 229 | 230 | constructor(data?: IUser) { 231 | super(); 232 | if (data) { 233 | this.$setData(data); 234 | } 235 | } 236 | 237 | $setData(data: IUser): this { 238 | Object.assign(this, data); 239 | this.fullName = `${this.firstName} ${this.lastName}`; 240 | return this; 241 | } 242 | 243 | toJSON() { 244 | // here i'm using lodash lib pick method. 245 | return _.pick(this, ['id', 'firstName', 'lastName', 'groupId']); 246 | } 247 | 248 | } 249 | 250 | 251 | // example of using the staff 252 | async someMethodToCreateGroupAndUser() { 253 | 254 | // Creation a group 255 | const group = new Group(); 256 | group.title = 'My group'; 257 | 258 | // Saving the group 259 | await group.$save(); 260 | 261 | // Creating an user 262 | const user = new User({ 263 | userName: 'troyanskiy', 264 | firstName: 'firstName', 265 | lastName: 'lastName', 266 | groupId: group.id 267 | }); 268 | 269 | // Saving the user 270 | await user.$save(); 271 | 272 | 273 | // Query data from server 274 | 275 | const user1 = await this.userResource.get('1'); 276 | 277 | // or 278 | 279 | const user2: User = await User.get('id'); 280 | 281 | } 282 | 283 | ``` 284 | 285 | 286 | ## QueryParams Conversion 287 | 288 | You can define the way query params are converted. 289 | Set the global config at the root of your app. 290 | 291 | `ResourceGlobalConfig.queryMappingMethod = ResourceQueryMappingMethod.` 292 | 293 | ``` 294 | { 295 | a: [{ b:1, c: [2, 3] }] 296 | } 297 | ``` 298 | 299 | With `` being one of the following: 300 | 301 | #### Plain (default) 302 | No conversion at all 303 | 304 | Output: `?a=[Object object]` 305 | 306 | #### Bracket 307 | All array elements will be indexed 308 | 309 | Output: `?a[0][b]=10383&a[0][c][0]=2&a[0][c][1]=3` 310 | 311 | #### JQueryParamsBracket 312 | Implements the standard $.params way of converting 313 | 314 | Output: `?a[0][b]=10383&a[0][c][]=2&a[0][c][]=3` 315 | 316 | -------------------------------------------------------------------------------- /projects/ngx-resource/core/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../../coverage/ngx-resource/core'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /projects/ngx-resource/core/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../../dist/ngx-resource/core", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | } 7 | } -------------------------------------------------------------------------------- /projects/ngx-resource/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ngx-resource/core", 3 | "version": "15.0.0", 4 | "description": "Core of resource library", 5 | "author": "Roman Rosluk ", 6 | "license": "MIT", 7 | "bugs": { 8 | "url": "https://github.com/troyanskiy/ngx-resource/issues" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/troyanskiy/ngx-resource.git" 13 | }, 14 | "keywords": [ 15 | "ngx-resource", 16 | "ngx-resource-core", 17 | "ngx-resource-rest", 18 | "ng2-resource", 19 | "ng2-resource-rest", 20 | "Angular2", 21 | "Resource" 22 | ], 23 | "dependencies": { 24 | "tslib": "^2.0.0" 25 | }, 26 | "peerDependencies": { 27 | "rxjs": ">=6.0.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /projects/ngx-resource/core/src/lib/Declarations.ts: -------------------------------------------------------------------------------- 1 | import { Observable, Subscription } from 'rxjs'; 2 | import { Provider } from '@angular/core'; 3 | 4 | export type TTypePromiseNull = T | Promise | null; 5 | 6 | export interface IResourceModuleConfig { 7 | handler?: Provider; 8 | } 9 | 10 | export interface IResourceParamsBase { 11 | url?: string; 12 | pathPrefix?: string; 13 | path?: string; 14 | headers?: any; 15 | body?: any; 16 | params?: any; 17 | query?: any; 18 | } 19 | 20 | export interface IResourceParams extends IResourceParamsBase { 21 | rootNode?: string; 22 | removeTrailingSlash?: boolean; 23 | addTimestamp?: boolean | string; 24 | withCredentials?: boolean; 25 | lean?: boolean; 26 | mutateBody?: boolean; 27 | returnAs?: ResourceActionReturnType; 28 | keepEmptyBody?: boolean; 29 | requestBodyType?: ResourceRequestBodyType; 30 | responseBodyType?: ResourceResponseBodyType; 31 | queryMappingMethod?: ResourceQueryMappingMethod; 32 | 33 | [prop: string]: any; 34 | } 35 | 36 | export interface IResourceAction extends IResourceParams { 37 | method?: ResourceRequestMethod; // get default 38 | expectJsonArray?: boolean; 39 | resultFactory?: TResourceResultFactory; 40 | map?: TResourceResponseMap; 41 | filter?: TResourceResponseFilter; 42 | } 43 | 44 | export type TResourceResponseMap = (item: any, options: IResourceActionInner) => any; 45 | 46 | export type TResourceResponseFilter = (item: any, options: IResourceActionInner) => boolean; 47 | 48 | export type TResourceResultFactory = (item: any, options: IResourceActionInner) => any; 49 | 50 | export interface IResourceActionAttributes { 51 | body?: any; 52 | query?: any; 53 | params?: any; 54 | 55 | onSuccess?(data: any): any; 56 | 57 | onError?(data: any): any; 58 | } 59 | 60 | export interface IResourceActionInner { 61 | actionAttributes?: IResourceActionAttributes; 62 | actionOptions?: IResourceAction; 63 | resolvedOptions?: IResourceParamsBase; 64 | 65 | queryMappingMethod?: ResourceQueryMappingMethod; 66 | 67 | usedInPath?: { [key: string]: boolean }; 68 | mainObservable?: Observable; 69 | subscription?: Subscription | null; 70 | promise?: Promise; 71 | isModel?: boolean; 72 | 73 | requestOptions?: IResourceRequest; 74 | 75 | returnData?: any; 76 | } 77 | 78 | export interface IResourceRequest { 79 | method?: ResourceRequestMethod; 80 | headers?: any; 81 | url?: string; 82 | withCredentials?: boolean; 83 | body?: any; 84 | query?: { [prop: string]: string }; 85 | responseBodyType?: ResourceResponseBodyType; 86 | requestBodyType?: ResourceRequestBodyType; 87 | } 88 | 89 | export interface IResourceHandlerResponse { 90 | promise?: Promise; 91 | observable?: Observable; 92 | 93 | abort?(): void; 94 | } 95 | 96 | export interface IResourceResponse { 97 | status: number; 98 | headers?: any; 99 | body?: B; 100 | } 101 | 102 | export interface IResourceMethodStrictBase { 103 | (body: IB, 104 | query: IQ, 105 | params: IP, 106 | onSuccess?: (data: O) => any, 107 | onError?: (err: IResourceResponse) => any): R; 108 | 109 | (body: IB, 110 | query: IQ, 111 | onSuccess?: (data: O) => any, 112 | onError?: (err: IResourceResponse) => any): R; 113 | 114 | (body: IB, 115 | onSuccess?: (data: O) => any, 116 | onError?: (err: IResourceResponse) => any): R; 117 | 118 | (onSuccess?: (data: O) => any, 119 | onError?: (err: IResourceResponse) => any): R; 120 | } 121 | 122 | export interface IResourceMethodBase 123 | extends IResourceMethodStrictBase { 124 | } 125 | 126 | /** 127 | * @deprecated use IResourceMethodPromiseStrict instead 128 | */ 129 | export interface IResourceMethodStrict 130 | extends IResourceMethodStrictBase> { 131 | } 132 | 133 | /** 134 | * @deprecated use IResourceMethodPromise instead 135 | */ 136 | export interface IResourceMethod 137 | extends IResourceMethodBase> { 138 | } 139 | 140 | export interface IResourceMethodPromiseStrict 141 | extends IResourceMethodStrictBase> { 142 | } 143 | 144 | export interface IResourceMethodPromise 145 | extends IResourceMethodBase> { 146 | } 147 | 148 | export interface IResourceMethodObservableStrict 149 | extends IResourceMethodStrictBase> { 150 | } 151 | 152 | export interface IResourceMethodObservable 153 | extends IResourceMethodBase> { 154 | } 155 | 156 | 157 | export interface IResourceMethodResultStrict 158 | extends IResourceMethodStrictBase> { 159 | } 160 | 161 | export interface IResourceMethodResult 162 | extends IResourceMethodBase> { 163 | } 164 | 165 | 166 | // As IResourceResponse 167 | 168 | /** 169 | * @deprecated use IResourceMethodPromiseStrictFull instead 170 | */ 171 | export interface IResourceMethodStrictFull 172 | extends IResourceMethodStrictBase>> { 173 | } 174 | 175 | /** 176 | * @deprecated use IResourceMethodPromiseFull instead 177 | */ 178 | export interface IResourceMethodFull 179 | extends IResourceMethodBase>> { 180 | } 181 | 182 | export interface IResourceMethodPromiseStrictFull 183 | extends IResourceMethodStrictBase>> { 184 | } 185 | 186 | export interface IResourceMethodPromiseFull 187 | extends IResourceMethodBase>> { 188 | } 189 | 190 | export interface IResourceMethodObservableStrictFull 191 | extends IResourceMethodStrictBase>> { 192 | } 193 | 194 | export interface IResourceMethodObservableFull 195 | extends IResourceMethodBase>> { 196 | } 197 | 198 | 199 | export interface IResourceMethodResultStrictFull 200 | extends IResourceMethodStrictBase>> { 201 | } 202 | 203 | export interface IResourceMethodResultFull 204 | extends IResourceMethodBase>> { 205 | } 206 | 207 | 208 | export type ResourceResult = R & { 209 | $resolved?: boolean; 210 | $promise?: Promise; 211 | $abort?(): void; 212 | }; 213 | 214 | 215 | export enum ResourceRequestBodyType { 216 | NONE = 0, 217 | JSON = 1, 218 | FORM = 2, 219 | FORM_DATA = 3, 220 | TEXT = 4, 221 | BLOB = 5, 222 | ARRAY_BUFFER = 6 223 | } 224 | 225 | export enum ResourceResponseBodyType { 226 | Text = 1, 227 | Json = 2, 228 | ArrayBuffer = 3, 229 | Blob = 4 230 | } 231 | 232 | export enum ResourceRequestMethod { 233 | Get = 1, 234 | Post = 2, 235 | Put = 3, 236 | Delete = 4, 237 | Options = 5, 238 | Head = 6, 239 | Patch = 7 240 | } 241 | 242 | export enum ResourceQueryMappingMethod { 243 | Plain = 1, 244 | Bracket = 2, 245 | JQueryParamsBracket = 3, 246 | 247 | None = 99 248 | } 249 | 250 | export enum ResourceActionReturnType { 251 | Promise = 'promise', 252 | Observable = 'observable', 253 | Resource = 'resource' 254 | } 255 | -------------------------------------------------------------------------------- /projects/ngx-resource/core/src/lib/Resource.ts: -------------------------------------------------------------------------------- 1 | import { Observable, of } from 'rxjs'; 2 | import { catchError, map, switchMap } from 'rxjs/operators'; 3 | 4 | import { 5 | IResourceAction, 6 | IResourceActionInner, 7 | IResourceResponse, 8 | ResourceActionReturnType, 9 | ResourceQueryMappingMethod, 10 | ResourceRequestBodyType, 11 | ResourceRequestMethod 12 | } from './Declarations'; 13 | import { ResourceGlobalConfig } from './ResourceGlobalConfig'; 14 | import { ResourceHelper } from './ResourceHelper'; 15 | import { ResourceHandler } from './ResourceHandler'; 16 | import { ResourceModule } from './ResourceModule'; 17 | import { Optional, Type } from '@angular/core'; 18 | 19 | export class Resource { 20 | 21 | protected get requestHandler(): ResourceHandler { 22 | 23 | if (!this._requestHandler) { 24 | 25 | if (!ResourceModule.injector) { 26 | throw new Error('ResourceModule.injector is missing'); 27 | } 28 | 29 | this._requestHandler = ResourceModule.injector.get(ResourceHandler as Type); 30 | 31 | if (!this._requestHandler) { 32 | throw new Error('ResourceHandler provider is missing'); 33 | } 34 | 35 | } 36 | 37 | return this._requestHandler; 38 | 39 | } 40 | 41 | private $url: string | null = null; 42 | private $pathPrefix: string | null = null; 43 | private $path: string | null = null; 44 | private $headers: {} | null = null; 45 | private $body: {} | null = null; 46 | private $params: {} | null = null; 47 | private $query: {} | null = null; 48 | 49 | // tslint:disable-next-line:variable-name 50 | private _requestHandler: ResourceHandler | null = null; 51 | 52 | constructor(@Optional() requestHandler?: ResourceHandler) { 53 | 54 | if (requestHandler) { 55 | this._requestHandler = requestHandler; 56 | } 57 | 58 | (this.constructor as any).instance = this; 59 | 60 | } 61 | 62 | /** 63 | * Used to get url 64 | */ 65 | $getUrl(actionOptions: IResourceAction = {}): string | Promise { 66 | return this.$url || actionOptions.url || ResourceGlobalConfig.url || ''; 67 | } 68 | 69 | $setUrl(url: string) { 70 | this.$url = url; 71 | } 72 | 73 | /** 74 | * Used to get path prefix 75 | */ 76 | $getPathPrefix(actionOptions: IResourceAction = {}): string | Promise { 77 | return this.$pathPrefix || actionOptions.pathPrefix || ResourceGlobalConfig.pathPrefix || ''; 78 | } 79 | 80 | $setPathPrefix(path: string) { 81 | this.$pathPrefix = path; 82 | } 83 | 84 | /** 85 | * Used to get path 86 | */ 87 | $getPath(actionOptions: IResourceAction = {}): string | Promise { 88 | return this.$path || actionOptions.path || ResourceGlobalConfig.path || ''; 89 | } 90 | 91 | $setPath(path: string) { 92 | this.$path = path; 93 | } 94 | 95 | /** 96 | * Get headers. 97 | */ 98 | $getHeaders(actionOptions: IResourceAction = {}): any | Promise { 99 | return this.$headers || actionOptions.headers || ResourceGlobalConfig.headers || {}; 100 | } 101 | 102 | $setHeaders(headers: any) { 103 | this.$headers = headers; 104 | } 105 | 106 | /** 107 | * Get body 108 | */ 109 | $getBody(actionOptions: IResourceAction = {}): any | Promise { 110 | return this.$body || actionOptions.body || ResourceGlobalConfig.body || {}; 111 | } 112 | 113 | $setBody(body: any) { 114 | this.$body = body; 115 | } 116 | 117 | /** 118 | * Get path params 119 | */ 120 | $getParams(actionOptions: IResourceAction = {}): any | Promise { 121 | return this.$params || actionOptions.params || ResourceGlobalConfig.params || {}; 122 | } 123 | 124 | $setParams(params: any) { 125 | this.$params = params; 126 | } 127 | 128 | /** 129 | * Get query params 130 | */ 131 | $getQuery(actionOptions: IResourceAction = {}): any | Promise { 132 | return this.$query || actionOptions.query || ResourceGlobalConfig.query || {}; 133 | } 134 | 135 | $setQuery(query: any) { 136 | this.$query = query; 137 | } 138 | 139 | /** 140 | * Used to filter received data. 141 | * Is applied on each element of array or object 142 | */ 143 | $filter(data: any, options: IResourceActionInner = {}): boolean { 144 | return true; 145 | } 146 | 147 | /** 148 | * Used to map received data 149 | * Is applied on each element of array or object 150 | */ 151 | $map(data: any, options: IResourceActionInner = {}): any { 152 | return data; 153 | } 154 | 155 | /** 156 | * Used to create result object 157 | * Is applied on each element of array or object 158 | */ 159 | $resultFactory(data: any, options: IResourceActionInner = {}): any { 160 | return data == null ? {} : data; 161 | } 162 | 163 | $restAction(options: IResourceActionInner) { 164 | 165 | this.$_setResourceActionInnerDefaults(options); 166 | this.$_setResourceActionOptionDefaults(options); 167 | 168 | if (!options.actionOptions) { 169 | throw new Error('Action options are not set'); 170 | } 171 | 172 | const actionOptions = options.actionOptions; 173 | 174 | if (!actionOptions.resultFactory) { 175 | throw new Error('Action options resultFactory is not set'); 176 | } 177 | 178 | if (!options.actionAttributes) { 179 | throw new Error('Action attributes is not set'); 180 | } 181 | 182 | 183 | if (actionOptions.mutateBody || options.isModel) { 184 | options.returnData = options.actionAttributes.body; 185 | } 186 | 187 | if (actionOptions.returnAs === ResourceActionReturnType.Resource) { 188 | options.returnData = actionOptions.expectJsonArray ? [] : actionOptions.resultFactory.call(this, null, options); 189 | } 190 | 191 | if (this.$_canSetInternalData(options)) { 192 | ResourceHelper.defineReturnDataPropertiesResolvedAbort(options.returnData); 193 | } 194 | 195 | options.mainObservable = this.$_createMainObservable(options); 196 | 197 | if (this.$_canSetInternalData(options)) { 198 | ResourceHelper.defineReturnDataPropertiesPromise(options.returnData, this.$_createPromise(options)); 199 | } 200 | 201 | switch (actionOptions.returnAs) { 202 | case ResourceActionReturnType.Observable: 203 | return options.mainObservable; 204 | 205 | case ResourceActionReturnType.Promise: 206 | return this.$_createPromise(options); 207 | 208 | default: 209 | return options.returnData; 210 | 211 | } 212 | 213 | } 214 | 215 | /** 216 | * Converts observable to promise and ads abort method 217 | */ 218 | protected $_createPromise(options: IResourceActionInner): Promise { 219 | 220 | if (!options.promise) { 221 | options.promise = new Promise((resolve, reject) => { 222 | 223 | if (!options.mainObservable) { 224 | reject(new Error('$_createPromise options.mainObservable missing')); 225 | 226 | return; 227 | } 228 | 229 | options.subscription = options.mainObservable.subscribe( 230 | resolve, 231 | error => { 232 | reject(error); 233 | options.subscription = null; 234 | }, 235 | () => { 236 | options.subscription = null; 237 | } 238 | ); 239 | 240 | ResourceHelper.createNewAbortMethod(options); 241 | 242 | }); 243 | } 244 | 245 | return options.promise; 246 | 247 | } 248 | 249 | 250 | /** 251 | * Creates main request observable 252 | */ 253 | protected $_createMainObservable(options: IResourceActionInner): Observable { 254 | 255 | const requestPreparationPromise = this.$_setResolvedOptions(options) 256 | .then((o: IResourceActionInner) => this.$_createRequestOptions(o)); 257 | 258 | return of(requestPreparationPromise) 259 | .pipe( 260 | switchMap((oPromise: Promise) => { 261 | return oPromise 262 | .then((o: IResourceActionInner) => { 263 | 264 | if (!o.requestOptions) { 265 | throw new Error('IResourceActionInner miss request options'); 266 | } 267 | 268 | const handlerResp = this.requestHandler.handle(o.requestOptions); 269 | 270 | if (o.returnData && this.$_canSetInternalData(options)) { 271 | o.returnData.$abort = handlerResp.abort; 272 | } 273 | 274 | if (handlerResp.observable) { 275 | return handlerResp.observable as any; 276 | } 277 | 278 | return handlerResp.promise; 279 | 280 | }); 281 | }), 282 | switchMap(s => s instanceof Observable ? s : of(s)), 283 | map((resp: IResourceResponse) => this.$handleSuccessResponse(options, resp)), 284 | catchError((resp: IResourceResponse) => { 285 | throw this.$handleErrorResponse(options, resp); 286 | }) 287 | ); 288 | } 289 | 290 | /** 291 | * Success response handler 292 | */ 293 | protected $handleSuccessResponse(options: IResourceActionInner, resp: IResourceResponse): any { 294 | 295 | let body = resp.body; 296 | 297 | if (Array.isArray(body)) { 298 | body = this.$prepareSuccessResponseBodyArray(body, options); 299 | } else { 300 | body = this.$prepareSuccessResponseBodyObject(body, options); 301 | } 302 | 303 | if (this.$_canSetInternalData(options)) { 304 | options.returnData.$resolved = true; 305 | } 306 | 307 | if (options.actionOptions && options.actionOptions.asResourceResponse) { 308 | resp.body = body; 309 | body = resp; 310 | } 311 | 312 | if (options.actionAttributes && options.actionAttributes.onSuccess) { 313 | options.actionAttributes.onSuccess(body); 314 | } 315 | 316 | return body; 317 | } 318 | 319 | /** 320 | * Prepare success response body as array 321 | */ 322 | protected $prepareSuccessResponseBodyArray(body: any[], options: IResourceActionInner): any[] { 323 | 324 | if (!options.actionOptions) { 325 | throw new Error('$prepareSuccessResponseBodyArray options.actionOptions missing'); 326 | } 327 | 328 | const actionOptions = options.actionOptions; 329 | 330 | body = body 331 | .filter((item: any) => { 332 | return actionOptions.filter ? actionOptions.filter.call(this, item, options) : true; 333 | }) 334 | .map((item: any) => { 335 | 336 | if (actionOptions.map) { 337 | item = actionOptions.map.call(this, item, options); 338 | } 339 | 340 | return actionOptions.resultFactory ? actionOptions.resultFactory.call(this, item, options) : item; 341 | }); 342 | 343 | if (options.returnData) { 344 | Array.prototype.push.apply(options.returnData, body); 345 | body = options.returnData; 346 | } 347 | 348 | return body; 349 | 350 | } 351 | 352 | /** 353 | * Prepare success response body as object 354 | */ 355 | protected $prepareSuccessResponseBodyObject(body: any, options: IResourceActionInner): any { 356 | 357 | if (!options.actionOptions) { 358 | throw new Error('$prepareSuccessResponseBodyObject options.actionOptions missing'); 359 | } 360 | 361 | const actionOptions = options.actionOptions; 362 | 363 | if (actionOptions.filter && !actionOptions.filter.call(this, body, options)) { 364 | return null; 365 | } 366 | 367 | if (actionOptions.map) { 368 | body = actionOptions.map.call(this, body, options); 369 | } 370 | 371 | let newBody = options.returnData; 372 | 373 | if (newBody) { 374 | if (typeof newBody.$setData === 'function') { 375 | newBody.$setData(body); 376 | } else { 377 | Object.assign(newBody, body); 378 | } 379 | } else { 380 | newBody = actionOptions.resultFactory ? actionOptions.resultFactory.call(this, body, options) : body; 381 | } 382 | 383 | body = newBody; 384 | 385 | // If it's model 386 | if (body.$resource) { 387 | body.$resolved = true; 388 | body.$promise = options.mainObservable; 389 | body.$abort = () => true; 390 | } 391 | 392 | return body; 393 | 394 | } 395 | 396 | /** 397 | * Handle error 398 | */ 399 | protected $handleErrorResponse(options: IResourceActionInner, resp: IResourceResponse): any { 400 | 401 | if (options.returnData && this.$_canSetInternalData(options)) { 402 | options.returnData.$resolved = true; 403 | } 404 | 405 | if (options.actionAttributes && options.actionAttributes.onError) { 406 | options.actionAttributes.onError(resp); 407 | } 408 | 409 | throw resp; 410 | } 411 | 412 | /** 413 | * Sets request options url 414 | */ 415 | protected $setRequestOptionsUrl(options: IResourceActionInner): void { 416 | 417 | const requestOptions = ResourceHelper.getRequestOptionsOrThrow(options); 418 | const resolvedOptions = ResourceHelper.getResolvedOptionsOrThrow(options); 419 | const actionAttributes = ResourceHelper.getActionAttributesOrThrow(options); 420 | 421 | if (!requestOptions.url) { 422 | requestOptions.url = 423 | (resolvedOptions.url || '') + 424 | (resolvedOptions.pathPrefix || '') + 425 | (resolvedOptions.path || ''); 426 | } 427 | 428 | 429 | options.usedInPath = {}; 430 | 431 | ResourceHelper.setRequestOptionsUrlParams( 432 | requestOptions, 433 | resolvedOptions, 434 | actionAttributes, 435 | options.usedInPath 436 | ); 437 | 438 | // Removing double slashed from final url 439 | requestOptions.url = requestOptions.url.replace(/\/\/+/g, '/'); 440 | if (requestOptions.url.startsWith('http')) { 441 | requestOptions.url = requestOptions.url.replace(':/', '://'); 442 | } 443 | 444 | // Remove trailing slash 445 | if (options.actionOptions && options.actionOptions.removeTrailingSlash) { 446 | while (requestOptions.url[requestOptions.url.length - 1] === '/') { 447 | requestOptions.url = requestOptions.url.substr(0, requestOptions.url.length - 1); 448 | } 449 | } 450 | 451 | } 452 | 453 | protected $setRequestOptionsBody(options: IResourceActionInner): void { 454 | 455 | const actionOptions = ResourceHelper.getActionOptionsOrThrow(options); 456 | const actionAttributes = ResourceHelper.getActionAttributesOrThrow(options); 457 | const requestOptions = ResourceHelper.getRequestOptionsOrThrow(options); 458 | 459 | let body = actionAttributes.body; 460 | 461 | if (!body) { 462 | return; 463 | } 464 | 465 | const realBodyType = ResourceHelper.getRealTypeOf(body); 466 | 467 | let bodyOk: boolean = realBodyType === actionOptions.requestBodyType; 468 | 469 | if ( 470 | !bodyOk && 471 | realBodyType === ResourceRequestBodyType.JSON && 472 | actionOptions.requestBodyType === ResourceRequestBodyType.FORM_DATA) { 473 | 474 | body = ResourceHelper.createRequestOptionsFormDataBody(body, actionOptions); 475 | bodyOk = true; 476 | 477 | } 478 | 479 | if (!bodyOk) { 480 | throw new Error('Can not convert body'); 481 | } 482 | 483 | if (!(body instanceof FormData)) { 484 | // Add root node if needed 485 | if (actionOptions.rootNode) { 486 | const newBody: any = {}; 487 | newBody[actionOptions.rootNode] = body; 488 | body = newBody; 489 | } 490 | 491 | 492 | if ((actionOptions.requestBodyType === ResourceRequestBodyType.NONE || 493 | (actionOptions.requestBodyType === ResourceRequestBodyType.JSON && 494 | typeof body === 'object' && Object.keys(body).length === 0) 495 | ) && !actionOptions.keepEmptyBody) { 496 | return; 497 | } 498 | 499 | } 500 | 501 | requestOptions.body = body; 502 | 503 | } 504 | 505 | protected $setRequestOptionsQuery(options: IResourceActionInner): void { 506 | 507 | const actionAttributes = ResourceHelper.getActionAttributesOrThrow(options); 508 | const resolvedOptions = ResourceHelper.getResolvedOptionsOrThrow(options); 509 | const requestOptions = ResourceHelper.getRequestOptionsOrThrow(options); 510 | const actionOptions = ResourceHelper.getActionOptionsOrThrow(options); 511 | 512 | options.usedInPath = options.usedInPath || {}; 513 | 514 | let oq = actionAttributes.query || {}; 515 | if (resolvedOptions.query) { 516 | oq = {...resolvedOptions.query, ...oq}; 517 | } 518 | 519 | if (oq) { 520 | requestOptions.query = {}; 521 | Object.keys(oq).forEach((key: string) => { 522 | // tslint:disable-next-line: no-non-null-assertion 523 | if (oq.hasOwnProperty(key) && !options.usedInPath![key]) { 524 | this.$appendQueryParams(requestOptions.query as any, key, oq[key], options.queryMappingMethod); 525 | } 526 | }); 527 | } 528 | 529 | if (actionOptions.addTimestamp) { 530 | 531 | requestOptions.query = requestOptions.query || {}; 532 | 533 | this.$appendQueryParams( 534 | requestOptions.query, 535 | actionOptions.addTimestamp as string, 536 | Date.now().toString(10), 537 | options.queryMappingMethod); 538 | } 539 | 540 | } 541 | 542 | protected $appendQueryParams(query: { [prop: string]: string | any[] }, 543 | key: string, 544 | value: any, 545 | queryMappingMethod?: ResourceQueryMappingMethod): void { 546 | 547 | ResourceHelper.appendQueryParams(query, key, value, queryMappingMethod); 548 | 549 | } 550 | 551 | protected $_setResourceActionInnerDefaults(options: IResourceActionInner) { 552 | 553 | const actionOptions = ResourceHelper.getActionOptionsOrThrow(options); 554 | const actionAttributes = ResourceHelper.getActionAttributesOrThrow(options); 555 | 556 | // Setting default request method 557 | if (!actionOptions.method) { 558 | actionOptions.method = ResourceRequestMethod.Get; 559 | } 560 | 561 | 562 | if (actionAttributes.body) { 563 | 564 | // Setting default request content type 565 | if (!actionOptions.requestBodyType) { 566 | actionOptions.requestBodyType = ResourceHelper.getRealTypeOf(actionAttributes.body); 567 | } 568 | 569 | 570 | // Setting params and query if needed 571 | if (actionOptions.requestBodyType === ResourceRequestBodyType.JSON && 572 | typeof actionAttributes.body === 'object' && !Array.isArray(actionAttributes.body)) { 573 | 574 | if (!actionAttributes.params) { 575 | actionAttributes.params = actionAttributes.body; 576 | } 577 | 578 | options.isModel = !!actionAttributes.body.$resource; 579 | 580 | } 581 | 582 | } 583 | 584 | actionAttributes.params = actionAttributes.params || {}; 585 | 586 | if (!actionAttributes.query && actionOptions.method === ResourceRequestMethod.Get) { 587 | actionAttributes.query = actionAttributes.params; 588 | } 589 | 590 | options.queryMappingMethod = actionOptions.queryMappingMethod || ResourceGlobalConfig.queryMappingMethod; 591 | 592 | } 593 | 594 | // tslint:disable-next-line: cognitive-complexity 595 | protected $_setResourceActionOptionDefaults(options: IResourceActionInner) { 596 | 597 | const actionOptions = ResourceHelper.getActionOptionsOrThrow(options); 598 | 599 | if (ResourceHelper.isNullOrUndefined(actionOptions.filter)) { 600 | actionOptions.filter = this.$filter; 601 | } 602 | 603 | if (ResourceHelper.isNullOrUndefined(actionOptions.map)) { 604 | actionOptions.map = this.$map; 605 | } 606 | 607 | if (ResourceHelper.isNullOrUndefined(actionOptions.resultFactory)) { 608 | actionOptions.resultFactory = this.$resultFactory; 609 | } 610 | 611 | if (ResourceHelper.isNullOrUndefined(actionOptions.removeTrailingSlash)) { 612 | actionOptions.removeTrailingSlash = ResourceGlobalConfig.removeTrailingSlash; 613 | } 614 | 615 | if (ResourceHelper.isNullOrUndefined(actionOptions.withCredentials)) { 616 | actionOptions.withCredentials = ResourceGlobalConfig.withCredentials; 617 | } 618 | 619 | if (ResourceHelper.isNullOrUndefined(actionOptions.returnAs)) { 620 | actionOptions.returnAs = ResourceGlobalConfig.returnAs; 621 | } 622 | 623 | if (ResourceHelper.isNullOrUndefined(actionOptions.responseBodyType)) { 624 | actionOptions.responseBodyType = ResourceGlobalConfig.responseBodyType; 625 | } 626 | 627 | if (ResourceHelper.isNullOrUndefined(actionOptions.lean)) { 628 | actionOptions.lean = !!ResourceGlobalConfig.lean; 629 | 630 | if (actionOptions.mutateBody 631 | && actionOptions.returnAs === ResourceActionReturnType.Resource 632 | && ResourceHelper.isNullOrUndefined(actionOptions.lean)) { 633 | 634 | actionOptions.lean = true; 635 | 636 | } 637 | } 638 | 639 | if (ResourceHelper.isNullOrUndefined(actionOptions.addTimestamp)) { 640 | actionOptions.addTimestamp = ResourceGlobalConfig.addTimestamp; 641 | 642 | if (actionOptions.addTimestamp && typeof actionOptions.addTimestamp !== 'string') { 643 | actionOptions.addTimestamp = 'ts'; 644 | } 645 | } 646 | } 647 | 648 | protected $_setResolvedOptions(options: IResourceActionInner): Promise { 649 | return Promise.all([ 650 | this.$getUrl(options.actionOptions), 651 | this.$getPathPrefix(options.actionOptions), 652 | this.$getPath(options.actionOptions), 653 | this.$getHeaders(options.actionOptions), 654 | this.$getBody(options.actionOptions), 655 | this.$getParams(options.actionOptions), 656 | this.$getQuery(options.actionOptions) 657 | ]) 658 | .then(([url, pathPrefix, path, headers, body, params, query]: any[]) => { 659 | 660 | options.resolvedOptions = { 661 | url, 662 | pathPrefix, 663 | path, 664 | headers, 665 | body, 666 | params, 667 | query 668 | }; 669 | 670 | return options; 671 | }); 672 | } 673 | 674 | protected $_createRequestOptions(options: IResourceActionInner): IResourceActionInner | Promise { 675 | 676 | const actionOptions = ResourceHelper.getActionOptionsOrThrow(options); 677 | const resolvedOptions = ResourceHelper.getResolvedOptionsOrThrow(options); 678 | 679 | options.requestOptions = {}; 680 | 681 | // Step 1 set main 682 | options.requestOptions.method = actionOptions.method; 683 | options.requestOptions.headers = resolvedOptions.headers; 684 | options.requestOptions.withCredentials = actionOptions.withCredentials; 685 | options.requestOptions.responseBodyType = actionOptions.responseBodyType; 686 | options.requestOptions.requestBodyType = actionOptions.requestBodyType; 687 | 688 | // Step 2 create url 689 | this.$setRequestOptionsUrl(options); 690 | 691 | // Step 3 create body 692 | this.$setRequestOptionsBody(options); 693 | 694 | // Step 4 set query params 695 | this.$setRequestOptionsQuery(options); 696 | 697 | return options; 698 | } 699 | 700 | protected $_canSetInternalData(options: IResourceActionInner): boolean { 701 | const actionOptions = ResourceHelper.getActionOptionsOrThrow(options); 702 | 703 | return !!(options.returnData && (!actionOptions.lean || options.isModel)); 704 | } 705 | 706 | } 707 | -------------------------------------------------------------------------------- /projects/ngx-resource/core/src/lib/ResourceAction.ts: -------------------------------------------------------------------------------- 1 | import { Resource } from './Resource'; 2 | import { IResourceAction, ResourceRequestMethod } from './Declarations'; 3 | 4 | 5 | export function ResourceAction(methodOptions?: IResourceAction) { 6 | 7 | methodOptions = methodOptions || {}; 8 | 9 | if (methodOptions.method === undefined) { 10 | methodOptions.method = ResourceRequestMethod.Get; 11 | } 12 | 13 | // tslint:disable-next-line: only-arrow-functions 14 | return function(target: Resource, propertyKey: string) { 15 | 16 | (target as any)[propertyKey] = function(...args: any[]): any { 17 | 18 | const callbacks: any = args.filter((arg: any) => typeof arg === 'function'); 19 | const data: any = args.filter((arg: any) => typeof arg !== 'function'); 20 | 21 | const [body, query, params] = data; 22 | const [onSuccess, onError] = callbacks; 23 | 24 | const actionOptions: IResourceAction = {...this.getResourceOptions(), ...methodOptions}; 25 | 26 | return this.$restAction({actionAttributes: {body, query, params, onSuccess, onError}, actionOptions}); 27 | 28 | }; 29 | 30 | }; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /projects/ngx-resource/core/src/lib/ResourceCommon/ResourceCRUD.ts: -------------------------------------------------------------------------------- 1 | import { ResourceActionReturnType } from '../Declarations'; 2 | import { ResourceCRUDBase } from './ResourceCRUDBase'; 3 | 4 | 5 | /** 6 | * @deprecated use ResourceCRUDPromise or ResourceCRUDObservable instead 7 | */ 8 | export abstract class ResourceCRUD 9 | extends ResourceCRUDBase, Promise, Promise> { 11 | 12 | protected readonly $crudReturnAs = ResourceActionReturnType.Promise; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /projects/ngx-resource/core/src/lib/ResourceCommon/ResourceCRUDBase.ts: -------------------------------------------------------------------------------- 1 | import { Resource } from '../Resource'; 2 | import { IResourceAction, IResourceResponse, ResourceActionReturnType, ResourceRequestMethod } from '../Declarations'; 3 | 4 | 5 | export abstract class ResourceCRUDBase extends Resource { 6 | 7 | protected readonly abstract $crudReturnAs: ResourceActionReturnType; 8 | 9 | query(query?: TQuery, 10 | onSuccess?: (data: TQueryResult) => any, 11 | onError?: (err: IResourceResponse) => any): TRetQuery { 12 | 13 | return this.$restAction({ 14 | actionAttributes: { 15 | body: query, 16 | onSuccess, 17 | onError 18 | }, 19 | actionOptions: this.$_crudBaseGetActionOptions() 20 | }); 21 | 22 | } 23 | 24 | get(data: { id: any }, 25 | onSuccess?: (data: TFull) => any, 26 | onError?: (err: IResourceResponse) => any): TRetFull { 27 | 28 | return this.$restAction({ 29 | actionAttributes: { 30 | body: data, 31 | onSuccess, 32 | onError 33 | }, 34 | actionOptions: this.$_crudBaseGetActionOptions({ 35 | path: '/{!id}' 36 | }) 37 | }); 38 | 39 | } 40 | 41 | save(data: TFull, 42 | onSuccess?: (data: TFull) => any, 43 | onError?: (err: IResourceResponse) => any): TRetFull { 44 | 45 | return this.$restAction({ 46 | actionAttributes: { 47 | body: data, 48 | onSuccess, 49 | onError 50 | }, 51 | actionOptions: this.$_crudBaseGetActionOptions({ 52 | method: ResourceRequestMethod.Post, 53 | }) 54 | }); 55 | 56 | } 57 | 58 | update(data: TFull, 59 | onSuccess?: (data: TFull) => any, 60 | onError?: (err: IResourceResponse) => any): TRetFull { 61 | 62 | return this.$restAction({ 63 | actionAttributes: { 64 | body: data, 65 | onSuccess, 66 | onError 67 | }, 68 | actionOptions: this.$_crudBaseGetActionOptions({ 69 | method: ResourceRequestMethod.Put, 70 | path: '/{!id}' 71 | }) 72 | }); 73 | 74 | } 75 | 76 | remove(data: { id: any }, 77 | onSuccess?: (data: any) => any, 78 | onError?: (err: IResourceResponse) => any): TRetAny { 79 | 80 | return this.$restAction({ 81 | actionAttributes: { 82 | body: data, 83 | onSuccess, 84 | onError 85 | }, 86 | actionOptions: this.$_crudBaseGetActionOptions({ 87 | method: ResourceRequestMethod.Delete, 88 | path: '/{!id}' 89 | }) 90 | }); 91 | 92 | } 93 | 94 | patch(data: { id: any } & Partial, 95 | onSuccess?: (data: TFull) => any, 96 | onError?: (err: IResourceResponse) => any): TRetFull { 97 | 98 | return this.$restAction({ 99 | actionAttributes: { 100 | body: data, 101 | onSuccess, 102 | onError 103 | }, 104 | actionOptions: this.$_crudBaseGetActionOptions({ 105 | method: ResourceRequestMethod.Patch, 106 | path: '/{!id}' 107 | }) 108 | }); 109 | 110 | } 111 | 112 | // Alias to save 113 | create(data: TFull, callback?: (res: TFull) => any): TRetFull { 114 | return this.save(data, callback); 115 | } 116 | 117 | 118 | private $_crudBaseGetActionOptions(actionOptions: IResourceAction = {}): IResourceAction { 119 | return { 120 | ...(this as any).getResourceOptions(), 121 | method: ResourceRequestMethod.Get, 122 | returnAs: this.$crudReturnAs, 123 | ...actionOptions 124 | }; 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /projects/ngx-resource/core/src/lib/ResourceCommon/ResourceCRUDObservable.ts: -------------------------------------------------------------------------------- 1 | import { ResourceActionReturnType } from '../Declarations'; 2 | import { ResourceCRUDBase } from './ResourceCRUDBase'; 3 | import { Observable } from 'rxjs'; 4 | 5 | 6 | export abstract class ResourceCRUDObservable 7 | extends ResourceCRUDBase, Observable, Observable> { 9 | 10 | protected readonly $crudReturnAs = ResourceActionReturnType.Observable; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /projects/ngx-resource/core/src/lib/ResourceCommon/ResourceCRUDPromise.ts: -------------------------------------------------------------------------------- 1 | import { ResourceActionReturnType } from '../Declarations'; 2 | import { ResourceCRUDBase } from './ResourceCRUDBase'; 3 | 4 | 5 | export abstract class ResourceCRUDPromise 6 | extends ResourceCRUDBase, Promise, Promise> { 8 | 9 | protected readonly $crudReturnAs = ResourceActionReturnType.Promise; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /projects/ngx-resource/core/src/lib/ResourceGlobalConfig.ts: -------------------------------------------------------------------------------- 1 | import { ResourceActionReturnType, ResourceQueryMappingMethod, ResourceResponseBodyType, TTypePromiseNull } from './Declarations'; 2 | 3 | export class ResourceGlobalConfig { 4 | static url: TTypePromiseNull = null; 5 | static pathPrefix: TTypePromiseNull = null; 6 | static path: TTypePromiseNull = null; 7 | static headers: TTypePromiseNull = null; 8 | static body: TTypePromiseNull = null; 9 | static params: TTypePromiseNull = null; 10 | static query: TTypePromiseNull = null; 11 | 12 | static removeTrailingSlash = true; 13 | static addTimestamp: boolean | string = false; 14 | static withCredentials = false; 15 | static lean: boolean | null = null; 16 | static returnAs: ResourceActionReturnType = ResourceActionReturnType.Observable; 17 | static responseBodyType: ResourceResponseBodyType = ResourceResponseBodyType.Json; 18 | 19 | 20 | static queryMappingMethod: ResourceQueryMappingMethod = ResourceQueryMappingMethod.Plain; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /projects/ngx-resource/core/src/lib/ResourceHandler.ts: -------------------------------------------------------------------------------- 1 | import { IResourceHandlerResponse, IResourceRequest } from './Declarations'; 2 | 3 | export abstract class ResourceHandler { 4 | abstract handle(req: IResourceRequest): IResourceHandlerResponse; 5 | } 6 | -------------------------------------------------------------------------------- /projects/ngx-resource/core/src/lib/ResourceHandlerNoop.ts: -------------------------------------------------------------------------------- 1 | import { IResourceHandlerResponse, IResourceRequest } from './Declarations'; 2 | import { ResourceHandler } from './ResourceHandler'; 3 | 4 | export class ResourceHandlerNoop extends ResourceHandler { 5 | handle(req: IResourceRequest): IResourceHandlerResponse { 6 | throw new Error('ResourceHandler is not provided'); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /projects/ngx-resource/core/src/lib/ResourceHelper.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IResourceAction, 3 | IResourceActionAttributes, 4 | IResourceActionInner, 5 | IResourceParamsBase, 6 | IResourceRequest, 7 | ResourceQueryMappingMethod, 8 | ResourceRequestBodyType 9 | } from './Declarations'; 10 | 11 | export class ResourceHelper { 12 | 13 | static cleanDataFields: string[] = [ 14 | '$resolved', 15 | '$promise', 16 | '$abort', 17 | '$resource' 18 | ]; 19 | 20 | 21 | private static isBrowser: boolean | null = null; 22 | 23 | static isRunningInBrowser(): boolean { 24 | 25 | if (this.isBrowser !== null) { 26 | return this.isBrowser; 27 | } 28 | 29 | try { 30 | this.isBrowser = !!window; 31 | } catch (e) { 32 | this.isBrowser = false; 33 | } 34 | 35 | return this.isBrowser; 36 | } 37 | 38 | static getRealTypeOf(data: any): ResourceRequestBodyType { 39 | if (!data) { 40 | return ResourceRequestBodyType.NONE; 41 | } 42 | 43 | if (this.isRunningInBrowser()) { 44 | if (FormData && data instanceof FormData) { 45 | return ResourceRequestBodyType.FORM_DATA; 46 | } 47 | 48 | if (Blob && data instanceof Blob) { 49 | return ResourceRequestBodyType.BLOB; 50 | } 51 | } 52 | 53 | if (data instanceof ArrayBuffer) { 54 | return ResourceRequestBodyType.ARRAY_BUFFER; 55 | } 56 | 57 | if (['string', 'number'].indexOf(typeof data) > -1) { 58 | return ResourceRequestBodyType.TEXT; 59 | } 60 | 61 | return ResourceRequestBodyType.JSON; 62 | } 63 | 64 | static defaults(dst: any, src: any): any { 65 | 66 | if (!dst) { 67 | dst = {}; 68 | } 69 | 70 | Object.keys(src) 71 | .forEach((key: string) => { 72 | if (dst[key] === undefined) { 73 | dst[key] = src[key]; 74 | } 75 | }); 76 | 77 | return dst; 78 | 79 | } 80 | 81 | static isNullOrUndefined(value: any): boolean { 82 | return value === null || value === undefined; 83 | } 84 | 85 | static cleanData(obj: any): any { 86 | 87 | if (Array.isArray(obj)) { 88 | return this.cleanDataArray(obj); 89 | } else { 90 | return this.cleanDataObject(obj); 91 | } 92 | 93 | } 94 | 95 | static cleanDataArray(obj: any[]): any[] { 96 | 97 | obj = obj.filter(value => typeof value !== 'function'); 98 | 99 | return obj; 100 | 101 | } 102 | 103 | static cleanDataObject(obj: any): any { 104 | const cleanedObj: any = {}; 105 | 106 | for (const propName in obj) { 107 | 108 | if (typeof obj[propName] !== 'function' && this.cleanDataFields.indexOf(propName) === -1) { 109 | cleanedObj[propName] = obj[propName]; 110 | } 111 | 112 | } 113 | 114 | return cleanedObj; 115 | } 116 | 117 | static defineReturnDataPropertiesResolvedAbort(returnData: any) { 118 | Object.defineProperty(returnData, '$resolved', { 119 | enumerable: false, 120 | configurable: true, 121 | writable: true, 122 | value: false 123 | }); 124 | 125 | Object.defineProperty(returnData, '$abort', { 126 | enumerable: false, 127 | configurable: true, 128 | writable: true, 129 | value: () => { 130 | // does nothing for now 131 | } 132 | }); 133 | } 134 | 135 | static defineReturnDataPropertiesPromise(returnData: any, value: any) { 136 | Object.defineProperty(returnData, '$promise', { 137 | enumerable: false, 138 | configurable: true, 139 | writable: true, 140 | value 141 | }); 142 | } 143 | 144 | static getRequestOptionsOrThrow(options: IResourceActionInner): IResourceRequest { 145 | // tslint:disable-next-line: prefer-immediate-return 146 | const value = this.getResourceActionInnerOrThrow(options, 'requestOptions'); 147 | 148 | return value; 149 | } 150 | 151 | // tslint:disable-next-line: no-identical-functions 152 | static getResolvedOptionsOrThrow(options: IResourceActionInner): IResourceParamsBase { 153 | // tslint:disable-next-line: prefer-immediate-return 154 | const value = this.getResourceActionInnerOrThrow(options, 'resolvedOptions'); 155 | 156 | return value; 157 | } 158 | 159 | // tslint:disable-next-line: no-identical-functions 160 | static getActionAttributesOrThrow(options: IResourceActionInner): IResourceActionAttributes { 161 | // tslint:disable-next-line: prefer-immediate-return 162 | const value = this.getResourceActionInnerOrThrow(options, 'actionAttributes'); 163 | 164 | return value; 165 | } 166 | 167 | // tslint:disable-next-line: no-identical-functions 168 | static getActionOptionsOrThrow(options: IResourceActionInner): IResourceAction { 169 | // tslint:disable-next-line: prefer-immediate-return 170 | const value = this.getResourceActionInnerOrThrow(options, 'actionOptions'); 171 | 172 | return value; 173 | } 174 | 175 | static setRequestOptionsUrlParams(requestOptions: IResourceRequest, 176 | resolvedOptions: IResourceParamsBase, 177 | actionAttributes: IResourceActionAttributes, 178 | usedInPath: { [key: string]: boolean }) { 179 | 180 | if (!requestOptions.url) { 181 | throw new Error('setRequestOptionsUrlParams options.requestOptions.url missing'); 182 | } 183 | 184 | const params = this.defaults(actionAttributes.params, resolvedOptions.params); 185 | const pathParams = requestOptions.url.match(/{([^}]*)}/g) || []; 186 | 187 | for (const pathParam of pathParams) { 188 | 189 | let pathKey = pathParam.substr(1, pathParam.length - 2); 190 | const isMandatory = pathKey[0] === '!'; 191 | if (isMandatory) { 192 | pathKey = pathKey.substr(1); 193 | } 194 | 195 | const onlyPathParam = pathKey[0] === ':'; 196 | if (onlyPathParam) { 197 | pathKey = pathKey.substr(1); 198 | } 199 | 200 | if (actionAttributes.query && actionAttributes.query === actionAttributes.params) { 201 | usedInPath[pathKey] = true; 202 | } 203 | 204 | const value = params[pathKey]; 205 | 206 | if (onlyPathParam) { 207 | delete params[pathKey]; 208 | } 209 | 210 | // Replacing in the url 211 | requestOptions.url = this.setRequestOptionsUrlParamsNewUrl(value, isMandatory, pathParam, requestOptions); 212 | 213 | } 214 | } 215 | 216 | static setRequestOptionsUrlParamsNewUrl(value: any, 217 | isMandatory: boolean, 218 | pathParam: string, 219 | requestOptions: IResourceRequest): string { 220 | 221 | if (!requestOptions.url) { 222 | throw new Error('setRequestOptionsUrlParamsNewUrl requestOptions.url missing'); 223 | } 224 | 225 | if (this.isNullOrUndefined(value)) { 226 | if (isMandatory) { 227 | const consoleMsg = `Mandatory ${pathParam} path parameter is missing`; 228 | console.warn(consoleMsg); 229 | 230 | throw new Error(consoleMsg); 231 | } 232 | 233 | return requestOptions.url.substr(0, requestOptions.url.indexOf(pathParam)); 234 | 235 | } 236 | 237 | return requestOptions.url.replace(pathParam, value); 238 | } 239 | 240 | static createRequestOptionsFormDataBody(body: any, actionOptions: IResourceAction): FormData { 241 | 242 | const newBody = new FormData(); 243 | 244 | Object.keys(body).forEach((key: string) => { 245 | 246 | const value = body[key]; 247 | 248 | if (body.hasOwnProperty(key) && typeof value !== 'function') { 249 | 250 | const isArrayOfFiles = value instanceof Array && value.reduce((acc, elem) => acc && elem instanceof File, true); 251 | 252 | if (isArrayOfFiles) { 253 | value.forEach((f: File, index: number) => { 254 | newBody.append(`${key}[${index}]`, f, f.name); 255 | }); 256 | } else if (value instanceof File) { 257 | newBody.append(key, value, value.name); 258 | } else if (!actionOptions.rootNode) { 259 | newBody.append(key, value); 260 | } 261 | } 262 | 263 | }); 264 | 265 | if (actionOptions.rootNode) { 266 | newBody.append(actionOptions.rootNode, JSON.stringify(body)); 267 | } 268 | 269 | return newBody; 270 | 271 | } 272 | 273 | static appendQueryParams(query: { [prop: string]: string | any[] }, 274 | key: string, 275 | value: any, 276 | queryMappingMethod?: ResourceQueryMappingMethod): void { 277 | 278 | if (value instanceof Date) { 279 | query[key] = value.toISOString(); 280 | 281 | return; 282 | } 283 | 284 | if (typeof value === 'object') { 285 | 286 | switch (queryMappingMethod) { 287 | 288 | case ResourceQueryMappingMethod.Plain: 289 | this.appendQueryParamsMappingMethodPlain(query, key, value); 290 | 291 | return; 292 | 293 | case ResourceQueryMappingMethod.Bracket: 294 | /// Convert object and arrays to query params 295 | this.appendQueryParamsMappingMethodBracket(query, key, value, queryMappingMethod); 296 | 297 | return; 298 | 299 | case ResourceQueryMappingMethod.JQueryParamsBracket: 300 | /// Convert object and arrays to query params according to $.params 301 | this.appendQueryParamsMappingMethodJQueryParamsBracket(query, key, value, queryMappingMethod); 302 | 303 | return; 304 | 305 | } 306 | 307 | } 308 | 309 | query[key] = value; 310 | 311 | } 312 | 313 | static appendQueryParamsMappingMethodPlain(query: { [prop: string]: any }, key: string, value: any) { 314 | 315 | if (Array.isArray(value)) { 316 | query[key] = value.join(','); 317 | } else { 318 | 319 | if (value && typeof value === 'object') { 320 | /// Convert dates to ISO format string 321 | if (value instanceof Date) { 322 | value = value.toISOString(); 323 | } else { 324 | value = JSON.stringify(value); 325 | } 326 | } 327 | 328 | query[key] = value; 329 | } 330 | } 331 | 332 | static appendQueryParamsMappingMethodBracket(query: { [prop: string]: any }, 333 | key: string, 334 | value: any, 335 | queryMappingMethod: ResourceQueryMappingMethod) { 336 | 337 | for (const k in value) { 338 | if (value.hasOwnProperty(k)) { 339 | this.appendQueryParams(query, `${key}[${k}]`, value[k], queryMappingMethod); 340 | } 341 | } 342 | 343 | } 344 | 345 | static appendQueryParamsMappingMethodJQueryParamsBracket(query: { [prop: string]: any }, 346 | key: string, 347 | value: any, 348 | queryMappingMethod: ResourceQueryMappingMethod) { 349 | 350 | for (const k in value) { 351 | if (value.hasOwnProperty(k)) { 352 | let path = `${key}[${k}]`; 353 | 354 | if (Array.isArray(value) && typeof value[k] !== 'object') { 355 | path = `${key}[]`; 356 | } 357 | this.appendQueryParams(query, path, value[k], queryMappingMethod); 358 | } 359 | } 360 | 361 | } 362 | 363 | /** 364 | * Creates new abort method from subscription 365 | */ 366 | static createNewAbortMethod(options: IResourceActionInner) { 367 | if (options.returnData && options.subscription) { 368 | 369 | const abort = options.returnData.abort; 370 | 371 | options.returnData.abort = () => { 372 | 373 | if (abort) { 374 | abort(); 375 | } 376 | 377 | if (options.subscription) { 378 | options.subscription.unsubscribe(); 379 | options.subscription = null; 380 | } 381 | 382 | }; 383 | } 384 | } 385 | 386 | 387 | private static getResourceActionInnerOrThrow(options: IResourceActionInner, param: string): any { 388 | if (options[param]) { 389 | return options[param]; 390 | } 391 | 392 | throw new Error('getResourceActionInnerOrThrow options.' + param + ' missing'); 393 | } 394 | 395 | } 396 | -------------------------------------------------------------------------------- /projects/ngx-resource/core/src/lib/ResourceModel.ts: -------------------------------------------------------------------------------- 1 | import { ResourceHelper } from './ResourceHelper'; 2 | import { ResourceCRUDPromise } from './ResourceCommon/ResourceCRUDPromise'; 3 | 4 | export abstract class ResourceModel { 5 | 6 | static resourceInstance: ResourceCRUDPromise | null = null; 7 | 8 | protected static methodQuery = 'query'; 9 | protected static methodGet = 'get'; 10 | protected static methodCreate = 'create'; 11 | protected static methodUpdate = 'update'; 12 | protected static methodRemove = 'remove'; 13 | 14 | 15 | abstract readonly $resource: any; 16 | 17 | $resolved = true; 18 | $promise: Promise | null = null; 19 | $abort: () => void; 20 | 21 | $idField = 'id'; 22 | 23 | static get(id: string | number): Promise { 24 | // tslint:disable-next-line: prefer-immediate-return 25 | const p = this.getInstance()[this.methodGet]({id}); 26 | 27 | return p; 28 | } 29 | 30 | static query(query?: any): Promise { 31 | // tslint:disable-next-line: prefer-immediate-return 32 | const p = this.getInstance()[this.methodQuery](query); 33 | 34 | return p; 35 | } 36 | 37 | static remove(id: string | number): Promise { 38 | // tslint:disable-next-line: prefer-immediate-return 39 | const p = this.getInstance()[this.methodRemove]({id}); 40 | 41 | return p; 42 | } 43 | 44 | private static getInstance(): any { 45 | if (!this.resourceInstance) { 46 | 47 | const model: ResourceModel = (new (this as any)()); 48 | 49 | if (!model.$resource) { 50 | throw new Error('Your resource is not defined'); 51 | } 52 | 53 | if (!model.$resource.instance) { 54 | throw new Error('Your resource is not created (inject it somewhere)'); 55 | } 56 | 57 | this.resourceInstance = (new (this as any)()).$resource.instance; 58 | } 59 | 60 | return this.resourceInstance; 61 | } 62 | 63 | 64 | public $setData(data: any) { 65 | Object.assign(this, data); 66 | 67 | return this; 68 | } 69 | 70 | public $save(query?: any, params?: any) { 71 | 72 | if (this.isNew()) { 73 | return this.$create(query, params); 74 | } else { 75 | return this.$update(query, params); 76 | } 77 | 78 | } 79 | 80 | public $create(query?: any, params?: any) { 81 | return this.$executeResourceMethod((this.constructor as any).methodCreate, query, params); 82 | } 83 | 84 | public $update(query?: any, params?: any) { 85 | return this.$executeResourceMethod((this.constructor as any).methodUpdate, query, params); 86 | } 87 | 88 | public $remove(query?: any, params?: any) { 89 | return this.$executeResourceMethod((this.constructor as any).methodRemove, query, params); 90 | } 91 | 92 | public toJSON(): any { 93 | return ResourceHelper.cleanData(this); 94 | } 95 | 96 | protected isNew(): boolean { 97 | return !(this as any)[this.$idField]; 98 | } 99 | 100 | protected $getResourceWithMethodCheck(methodName: string): any { 101 | 102 | if (!this.$resource) { 103 | console.error(`Your Resource is not defined`); 104 | 105 | return null; 106 | } 107 | 108 | const restInstance = this.$resource.instance; 109 | 110 | if (!restInstance) { 111 | console.error(`Your Resource is not defined or not created`); 112 | 113 | return null; 114 | } 115 | 116 | if (!restInstance[methodName]) { 117 | console.error(`Your Resource has no implemented ${methodName} method.`); 118 | 119 | return null; 120 | } 121 | 122 | return restInstance; 123 | 124 | } 125 | 126 | protected $executeResourceMethod(methodName: string, query?: any, params?: any) { 127 | 128 | const resource = this.$getResourceWithMethodCheck(methodName); 129 | 130 | if (resource) { 131 | resource[methodName](this, query, params); 132 | } 133 | 134 | return this; 135 | } 136 | 137 | 138 | } 139 | -------------------------------------------------------------------------------- /projects/ngx-resource/core/src/lib/ResourceModule.ts: -------------------------------------------------------------------------------- 1 | import { Injector, ModuleWithProviders, NgModule } from '@angular/core'; 2 | import { ResourceHandlerNoop } from './ResourceHandlerNoop'; 3 | import { ResourceHandler } from './ResourceHandler'; 4 | import { IResourceModuleConfig } from './Declarations'; 5 | 6 | @NgModule() 7 | export class ResourceModule { 8 | 9 | static injector: Injector | null = null; 10 | 11 | /** 12 | * For root 13 | */ 14 | static forRoot(config: IResourceModuleConfig = {}): ModuleWithProviders { 15 | return { 16 | ngModule: ResourceModule, 17 | providers: [ 18 | config.handler || {provide: ResourceHandler, useClass: ResourceHandlerNoop} 19 | ] 20 | }; 21 | } 22 | 23 | /** 24 | * For child 25 | */ 26 | // tslint:disable-next-line: no-identical-functions 27 | static forChild(config: IResourceModuleConfig = {}): ModuleWithProviders { 28 | return { 29 | ngModule: ResourceModule, 30 | providers: [ 31 | config.handler || {provide: ResourceHandler, useClass: ResourceHandlerNoop} 32 | ] 33 | }; 34 | } 35 | 36 | constructor(injector: Injector) { 37 | ResourceModule.injector = injector; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /projects/ngx-resource/core/src/lib/ResourceParams.ts: -------------------------------------------------------------------------------- 1 | import { IResourceParams } from './Declarations'; 2 | 3 | export function ResourceParams(params: IResourceParams = {}) { 4 | 5 | // tslint:disable-next-line: only-arrow-functions 6 | return function(target: any) { 7 | 8 | target.prototype.getResourceOptions = () => params; 9 | 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /projects/ngx-resource/core/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of core 3 | */ 4 | 5 | export * from './lib/Declarations'; 6 | export * from './lib/Resource'; 7 | export * from './lib/ResourceAction'; 8 | export * from './lib/ResourceGlobalConfig'; 9 | export * from './lib/ResourceHandler'; 10 | export * from './lib/ResourceHandlerNoop'; 11 | export * from './lib/ResourceHelper'; 12 | export * from './lib/ResourceModel'; 13 | export * from './lib/ResourceParams'; 14 | export * from './lib/ResourceCommon/ResourceCRUDBase'; 15 | export * from './lib/ResourceCommon/ResourceCRUD'; 16 | export * from './lib/ResourceCommon/ResourceCRUDPromise'; 17 | export * from './lib/ResourceCommon/ResourceCRUDObservable'; 18 | export * from './lib/ResourceModule'; 19 | -------------------------------------------------------------------------------- /projects/ngx-resource/core/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone'; 4 | import 'zone.js/dist/zone-testing'; 5 | import { getTestBed } from '@angular/core/testing'; 6 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; 7 | 8 | // First, initialize the Angular testing environment. 9 | getTestBed().initTestEnvironment( 10 | BrowserDynamicTestingModule, 11 | platformBrowserDynamicTesting() 12 | ); 13 | -------------------------------------------------------------------------------- /projects/ngx-resource/core/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../out-tsc/lib", 5 | "declarationMap": true, 6 | "declaration": true, 7 | "inlineSources": true, 8 | "types": [], 9 | "lib": [ 10 | "dom", 11 | "es2018" 12 | ] 13 | }, 14 | "angularCompilerOptions": { 15 | "skipTemplateCodegen": true, 16 | "strictMetadataEmit": true, 17 | "fullTemplateTypeCheck": true, 18 | "strictInjectionParameters": true, 19 | "enableResourceInlining": true 20 | }, 21 | "exclude": [ 22 | "src/test.ts", 23 | "**/*.spec.ts" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /projects/ngx-resource/core/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.lib.json", 3 | "compilerOptions": { 4 | "declarationMap": false 5 | }, 6 | "angularCompilerOptions": { 7 | "enableIvy": true, 8 | "compilationMode": "partial" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /projects/ngx-resource/core/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts" 12 | ], 13 | "include": [ 14 | "**/*.spec.ts", 15 | "**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /projects/ngx-resource/core/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "lib", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "lib", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-cordova-advanced-http/README.md: -------------------------------------------------------------------------------- 1 | # @ngx-resource/handler-cordova-advanced-http 2 | 3 | The library is based on https://github.com/silkimen/cordova-plugin-advanced-http 4 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-cordova-advanced-http/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../../coverage/ngx-resource/handler-cordova-advanced-http'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-cordova-advanced-http/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../../dist/ngx-resource/handler-cordova-advanced-http", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | } 7 | } -------------------------------------------------------------------------------- /projects/ngx-resource/handler-cordova-advanced-http/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ngx-resource/handler-cordova-advanced-http", 3 | "version": "15.0.0", 4 | "description": "Resource Handler based on cordova-advanced-http", 5 | "author": "Roman Rosluk ", 6 | "license": "MIT", 7 | "bugs": { 8 | "url": "https://github.com/troyanskiy/ngx-resource/issues" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/troyanskiy/ngx-resource.git" 13 | }, 14 | "keywords": [ 15 | "ngx-resource", 16 | "ngx-resource-rest", 17 | "ngx-resource-handler", 18 | "ngx-resource-handler-resource-cordova-advanced-http", 19 | "ng2-resource", 20 | "ng2-resource-rest", 21 | "ng2", 22 | "ng2-rest", 23 | "Angular2", 24 | "resource-cordova-advanced-http" 25 | ], 26 | "dependencies": { 27 | "tslib": "^2.0.0" 28 | }, 29 | "peerDependencies": { 30 | "@ngx-resource/core": "^15.0.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-cordova-advanced-http/src/lib/ResourceHandlerCordovaAdvancedHttp.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IResourceHandlerResponse, 3 | IResourceRequest, 4 | IResourceResponse, 5 | ResourceHandler, 6 | ResourceRequestBodyType, 7 | ResourceRequestMethod, 8 | ResourceResponseBodyType 9 | } from '@ngx-resource/core'; 10 | 11 | 12 | declare const cordova: any; 13 | 14 | export class ResourceHandlerCordovaAdvancedHttp extends ResourceHandler { 15 | 16 | private http: any = null; 17 | 18 | private methodWithBody = ['post', 'put', 'patch']; 19 | 20 | private initDeferResolve: any = null; 21 | private initDeferPromise: Promise | null = null; 22 | 23 | constructor() { 24 | super(); 25 | 26 | this.initDeferPromise = new Promise((resolve) => { 27 | this.initDeferResolve = resolve; 28 | }); 29 | 30 | this.initHttp(); 31 | } 32 | 33 | initHttp(http?: any) { 34 | 35 | if (http) { 36 | this.http = http; 37 | this.resolveDeferInit(); 38 | } else { 39 | this.initHttpPlugin(); 40 | } 41 | 42 | } 43 | 44 | handle(req: IResourceRequest): IResourceHandlerResponse { 45 | 46 | if (this.initDeferPromise) { 47 | return { 48 | promise: this.initDeferPromise 49 | .then(() => this.handle(req)) 50 | .then((r: IResourceHandlerResponse) => r.promise || {status: 0}) 51 | }; 52 | } 53 | 54 | 55 | if (!this.http) { 56 | return this.createErrorResponse('Http is not defined'); 57 | } 58 | 59 | if (req.requestBodyType === void 0) { 60 | req.requestBodyType = ResourceRequestBodyType.FORM_DATA; 61 | } 62 | 63 | this.setDataSerializer(req.requestBodyType); 64 | 65 | const methodName = this.mapMethodName(req.method); 66 | 67 | if (methodName === null) { 68 | return this.createErrorResponse('Request method is not supported'); 69 | } 70 | 71 | let url = req.url || ''; 72 | let second = req.body; 73 | 74 | if (req.query) { 75 | this.prepareQuery(req.query); 76 | } 77 | 78 | if (this.methodWithBody.indexOf(methodName) > -1) { 79 | if (req.query) { 80 | url = this.createUrlWithQuery(url, req.query); 81 | } 82 | } else { 83 | second = req.query; 84 | } 85 | 86 | 87 | const promise = new Promise((resolve, reject) => { 88 | try { 89 | this.http[methodName](url, second, req.headers, resolve, reject); 90 | } catch (e) { 91 | console.error(`Http plugin call failed with: ${e.message}`, e); 92 | reject({status: -1, headers: [{error: e.message}]}); 93 | } 94 | }) 95 | .catch((resp: any) => this.createResponse(resp, req, true)) 96 | .then((resp: any) => this.createResponse(resp, req)); 97 | 98 | return {promise}; 99 | 100 | } 101 | 102 | private initHttpPlugin() { 103 | if (this.tryToSetPlugin()) { 104 | this.resolveDeferInit(); 105 | } else { 106 | document.addEventListener('deviceready', () => { 107 | if (!this.tryToSetPlugin()) { 108 | console.warn('Can not set http plugin after device ready'); 109 | } 110 | this.resolveDeferInit(); 111 | }, false); 112 | } 113 | } 114 | 115 | private tryToSetPlugin(): boolean { 116 | 117 | if (!this.http && cordova && cordova.plugin && cordova.plugin.http) { 118 | this.http = cordova.plugin.http; 119 | } 120 | 121 | return !!this.http; 122 | } 123 | 124 | private resolveDeferInit() { 125 | this.initDeferResolve(); 126 | this.initDeferResolve = null; 127 | this.initDeferPromise = null; 128 | } 129 | 130 | private createResponse(resp: any, req: IResourceRequest, isError: boolean = false): Promise { 131 | 132 | return new Promise((resolve, reject) => { 133 | 134 | const ret: IResourceResponse = { 135 | status: resp.status, 136 | body: resp.data || resp.error, 137 | headers: resp.headers 138 | }; 139 | 140 | this.prepareResponseBody(ret, req, isError, resolve, reject); 141 | 142 | if (isError) { 143 | reject(ret); 144 | } else { 145 | resolve(ret); 146 | } 147 | 148 | }); 149 | 150 | } 151 | 152 | 153 | private createErrorResponse(msg: string) { 154 | return { 155 | promise: Promise.reject(new Error(msg)) 156 | }; 157 | } 158 | 159 | private setDataSerializer(requestBodyType: ResourceRequestBodyType) { 160 | switch (requestBodyType) { 161 | case ResourceRequestBodyType.JSON: 162 | this.http.setDataSerializer('json'); 163 | break; 164 | 165 | case ResourceRequestBodyType.FORM_DATA: 166 | this.http.setDataSerializer('urlencoded'); 167 | break; 168 | 169 | case ResourceRequestBodyType.TEXT: 170 | this.http.setDataSerializer('utf8'); 171 | break; 172 | 173 | default: 174 | return this.createErrorResponse('Supported only json, FormData or text types'); 175 | } 176 | } 177 | 178 | private mapMethodName(method?: ResourceRequestMethod): string | null { 179 | 180 | switch (method) { 181 | case ResourceRequestMethod.Post: 182 | return 'post'; 183 | 184 | case ResourceRequestMethod.Get: 185 | return 'get'; 186 | 187 | case ResourceRequestMethod.Put: 188 | return 'put'; 189 | 190 | case ResourceRequestMethod.Patch: 191 | return 'patch'; 192 | 193 | case ResourceRequestMethod.Delete: 194 | return 'delete'; 195 | 196 | case ResourceRequestMethod.Head: 197 | return 'head'; 198 | 199 | default: 200 | return null; 201 | 202 | } 203 | } 204 | 205 | private prepareQuery(query: any) { 206 | for (const key in query) { 207 | if (query.hasOwnProperty(key)) { 208 | query[key] = query[key].toString(); 209 | } 210 | } 211 | } 212 | 213 | private createUrlWithQuery(url: string, query: { [key: string]: string }): string { 214 | const params: string = Object.keys(query) 215 | .map((key: string) => `${key}=${query[key]}`) 216 | .join('&'); 217 | 218 | return url + (url.indexOf('?') === -1 ? '?' : '&') + encodeURI(params); 219 | } 220 | 221 | private prepareResponseBody(ret: IResourceResponse, 222 | req: IResourceRequest, 223 | isError: boolean = false, 224 | resolve: any, 225 | reject: any) { 226 | 227 | if (ret.body) { 228 | 229 | switch (req.responseBodyType) { 230 | 231 | case ResourceResponseBodyType.Json: 232 | try { 233 | ret.body = JSON.parse(ret.body); 234 | } catch (e) { 235 | // noop 236 | } 237 | 238 | break; 239 | 240 | case ResourceResponseBodyType.Blob: 241 | ret.body = new Blob([ret.body], {type: 'text/plain'}); 242 | 243 | break; 244 | 245 | case ResourceResponseBodyType.ArrayBuffer: 246 | const fileReader = new FileReader(); 247 | 248 | fileReader.onload = function() { 249 | ret.body = this.result; 250 | 251 | if (isError) { 252 | reject(ret); 253 | } else { 254 | resolve(ret); 255 | } 256 | }; 257 | 258 | fileReader.onerror = () => { 259 | reject({ 260 | status: 0, 261 | body: null, 262 | headers: {} 263 | }); 264 | }; 265 | 266 | fileReader.readAsArrayBuffer( 267 | new Blob([ret.body], { 268 | type: 'text/plain' 269 | }) 270 | ); 271 | 272 | return; 273 | 274 | } 275 | } 276 | } 277 | 278 | } 279 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-cordova-advanced-http/src/lib/ResourceModule.ts: -------------------------------------------------------------------------------- 1 | import { ModuleWithProviders, NgModule } from '@angular/core'; 2 | import { IResourceModuleConfig, ResourceHandler, ResourceModule as ResourceModuleCore } from '@ngx-resource/core'; 3 | import { ResourceHandlerCordovaAdvancedHttp } from './ResourceHandlerCordovaAdvancedHttp'; 4 | 5 | 6 | @NgModule() 7 | export class ResourceModule extends ResourceModuleCore { 8 | 9 | /** 10 | * For root 11 | */ 12 | static forRoot(config: IResourceModuleConfig = {}): ModuleWithProviders { 13 | return ResourceModuleCore.forRoot({ 14 | handler: config.handler || {provide: ResourceHandler, useClass: ResourceHandlerCordovaAdvancedHttp} 15 | }); 16 | } 17 | 18 | /** 19 | * For child 20 | */ 21 | static forChild(config: IResourceModuleConfig = {}): ModuleWithProviders { 22 | return ResourceModuleCore.forChild({ 23 | handler: config.handler || {provide: ResourceHandler, useClass: ResourceHandlerCordovaAdvancedHttp} 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-cordova-advanced-http/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of handler-cordova-advanced-http 3 | */ 4 | 5 | export * from './lib/ResourceHandlerCordovaAdvancedHttp'; 6 | export * from './lib/ResourceModule'; 7 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-cordova-advanced-http/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone'; 4 | import 'zone.js/dist/zone-testing'; 5 | import { getTestBed } from '@angular/core/testing'; 6 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; 7 | 8 | // First, initialize the Angular testing environment. 9 | getTestBed().initTestEnvironment( 10 | BrowserDynamicTestingModule, 11 | platformBrowserDynamicTesting() 12 | ); 13 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-cordova-advanced-http/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../out-tsc/lib", 5 | "declarationMap": true, 6 | "declaration": true, 7 | "inlineSources": true, 8 | "types": [], 9 | "lib": [ 10 | "dom", 11 | "es2018" 12 | ] 13 | }, 14 | "angularCompilerOptions": { 15 | "skipTemplateCodegen": true, 16 | "strictMetadataEmit": true, 17 | "fullTemplateTypeCheck": true, 18 | "strictInjectionParameters": true, 19 | "enableResourceInlining": true 20 | }, 21 | "exclude": [ 22 | "src/test.ts", 23 | "**/*.spec.ts" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-cordova-advanced-http/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.lib.json", 3 | "compilerOptions": { 4 | "declarationMap": false 5 | }, 6 | "angularCompilerOptions": { 7 | "enableIvy": true, 8 | "compilationMode": "partial" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-cordova-advanced-http/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts" 12 | ], 13 | "include": [ 14 | "**/*.spec.ts", 15 | "**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-cordova-advanced-http/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "lib", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "lib", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-fetch/README.md: -------------------------------------------------------------------------------- 1 | # @ngx-resource/handler-fetch 2 | 3 | It's implementation of `ResourceHandler` which uses `fetch` 4 | 5 | ## How to install and setup it 6 | ```bash 7 | & npm i --save @ngx-resource/core @ngx-resource/handler-ngx-http @ngx-resource/handler-fetch 8 | ``` 9 | 10 | In you app module 11 | ```typescript 12 | 13 | // AoT requires an exported function for factories 14 | export function fetchHandlerFactory() { 15 | return new ResourceHandlerFetch(); 16 | } 17 | 18 | @NgModule({ 19 | imports: [ 20 | BrowserModule, 21 | BrowserAnimationsModule, 22 | 23 | ResourceModule.forRoot({ 24 |      handler: { provide: ResourceHandler, useFactory: (fetchHandlerFactory) } 25 | }) 26 | ], 27 | declarations: [...], 28 | bootstrap: [...], 29 | entryComponents: [...], 30 | providers: [...] 31 | }) 32 | export class AppModule { 33 | } 34 | ``` 35 | 36 | ## [Docs about @ngx-resource/core](https://github.com/troyanskiy/ngx-resource/blob/master/README.md) 37 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-fetch/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../../coverage/ngx-resource/handler-fetch'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-fetch/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../../dist/ngx-resource/handler-fetch", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | } 7 | } -------------------------------------------------------------------------------- /projects/ngx-resource/handler-fetch/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ngx-resource/handler-fetch", 3 | "version": "15.0.0", 4 | "description": "Resource handler for fetch", 5 | "author": "Roman Rosluk ", 6 | "license": "MIT", 7 | "bugs": { 8 | "url": "https://github.com/troyanskiy/ngx-resource/issues" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/troyanskiy/ngx-resource.git" 13 | }, 14 | "keywords": [ 15 | "ngx-resource", 16 | "handler-fetch", 17 | "ngx-resource-rest", 18 | "ngx-resource-handler", 19 | "ngx-resource-handler-fetch", 20 | "Resource" 21 | ], 22 | "dependencies": { 23 | "tslib": "^2.0.0" 24 | }, 25 | "peerDependencies": { 26 | "@ngx-resource/core": "^15.0.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-fetch/src/lib/ResourceHandlerFetch.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IResourceHandlerResponse, 3 | IResourceRequest, 4 | ResourceHandler, 5 | ResourceRequestMethod, 6 | ResourceResponseBodyType 7 | } from '@ngx-resource/core'; 8 | 9 | export const REQUEST_METHOD_MAP = { 10 | [ResourceRequestMethod.Get]: 'GET', 11 | [ResourceRequestMethod.Post]: 'POST', 12 | [ResourceRequestMethod.Put]: 'PUT', 13 | [ResourceRequestMethod.Patch]: 'PATCH', 14 | [ResourceRequestMethod.Delete]: 'DELETE', 15 | [ResourceRequestMethod.Options]: 'OPTIONS', 16 | [ResourceRequestMethod.Head]: 'HEAD', 17 | }; 18 | 19 | export class ResourceHandlerFetch extends ResourceHandler { 20 | 21 | handle(req: IResourceRequest): IResourceHandlerResponse { 22 | 23 | const url = this.createUrl(req); 24 | const requestInit = this.createRequestInit(req); 25 | 26 | return { 27 | promise: fetch(url, requestInit).then(async (res: Response) => { 28 | return { 29 | headers: res.headers, 30 | status: res.status, 31 | body: await this.getBody(req, res) 32 | }; 33 | }), 34 | }; 35 | } 36 | 37 | private createUrl(req: IResourceRequest): string { 38 | 39 | let {url} = req; 40 | 41 | if (typeof url !== 'string') { 42 | throw new Error('Url us missing'); 43 | } 44 | 45 | if (req.query) { 46 | 47 | const queryGroups: string[] = []; 48 | 49 | for (const key in req.query) { 50 | if (req.query.hasOwnProperty(key)) { 51 | queryGroups.push('key=' + encodeURIComponent(req.query[key])); 52 | } 53 | } 54 | 55 | if (queryGroups.length) { 56 | url += (url.indexOf('?') > -1 ? '&' : '?') + queryGroups.join('&'); 57 | } 58 | 59 | } 60 | 61 | return url; 62 | } 63 | 64 | private createRequestInit(req: IResourceRequest): RequestInit { 65 | const requestInit: RequestInit = {}; 66 | 67 | req.method = req.method || ResourceRequestMethod.Get; 68 | 69 | requestInit.method = REQUEST_METHOD_MAP[req.method]; 70 | requestInit.headers = req.headers; 71 | 72 | if (req.method !== ResourceRequestMethod.Get && req.body) { 73 | requestInit.body = req.body; 74 | } 75 | 76 | return requestInit; 77 | } 78 | 79 | private getBody(req: IResourceRequest, res: Response): any { 80 | switch (req.responseBodyType) { 81 | case ResourceResponseBodyType.ArrayBuffer: 82 | return res.arrayBuffer(); 83 | 84 | case ResourceResponseBodyType.Blob: 85 | return res.blob(); 86 | 87 | case ResourceResponseBodyType.Json: 88 | return res.json(); 89 | 90 | case ResourceResponseBodyType.Text: 91 | default: 92 | return res.text(); 93 | 94 | } 95 | } 96 | 97 | 98 | } 99 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-fetch/src/lib/ResourceModule.ts: -------------------------------------------------------------------------------- 1 | import { ModuleWithProviders, NgModule } from '@angular/core'; 2 | import { IResourceModuleConfig, ResourceHandler, ResourceModule as ResourceModuleCore } from '@ngx-resource/core'; 3 | import { ResourceHandlerFetch } from './ResourceHandlerFetch'; 4 | 5 | 6 | @NgModule() 7 | export class ResourceModule extends ResourceModuleCore { 8 | 9 | /** 10 | * For root 11 | */ 12 | static forRoot(config: IResourceModuleConfig = {}): ModuleWithProviders { 13 | return ResourceModuleCore.forRoot({ 14 | handler: config.handler || {provide: ResourceHandler, useClass: ResourceHandlerFetch} 15 | }); 16 | } 17 | 18 | /** 19 | * For child 20 | */ 21 | static forChild(config: IResourceModuleConfig = {}): ModuleWithProviders { 22 | return ResourceModuleCore.forChild({ 23 | handler: config.handler || {provide: ResourceHandler, useClass: ResourceHandlerFetch} 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-fetch/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of handler-fetch 3 | */ 4 | 5 | export * from './lib/ResourceHandlerFetch'; 6 | export * from './lib/ResourceModule'; 7 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-fetch/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone'; 4 | import 'zone.js/dist/zone-testing'; 5 | import { getTestBed } from '@angular/core/testing'; 6 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; 7 | 8 | // First, initialize the Angular testing environment. 9 | getTestBed().initTestEnvironment( 10 | BrowserDynamicTestingModule, 11 | platformBrowserDynamicTesting() 12 | ); 13 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-fetch/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../out-tsc/lib", 5 | "declarationMap": true, 6 | "declaration": true, 7 | "inlineSources": true, 8 | "types": [], 9 | "lib": [ 10 | "dom", 11 | "es2018" 12 | ] 13 | }, 14 | "angularCompilerOptions": { 15 | "skipTemplateCodegen": true, 16 | "strictMetadataEmit": true, 17 | "fullTemplateTypeCheck": true, 18 | "strictInjectionParameters": true, 19 | "enableResourceInlining": true 20 | }, 21 | "exclude": [ 22 | "src/test.ts", 23 | "**/*.spec.ts" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-fetch/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.lib.json", 3 | "compilerOptions": { 4 | "declarationMap": false 5 | }, 6 | "angularCompilerOptions": { 7 | "enableIvy": false 8 | } 9 | } -------------------------------------------------------------------------------- /projects/ngx-resource/handler-fetch/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts" 12 | ], 13 | "include": [ 14 | "**/*.spec.ts", 15 | "**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-fetch/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "lib", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "lib", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-ngx-http/README.md: -------------------------------------------------------------------------------- 1 | # @ngx-resource/handler-ngx-http 2 | 3 | It's implementation of `ResourceHandler` which uses Angular `HttpClient` 4 | 5 | # If you are using Angular 5, please use @ngx-resource/handler-ngx-http 5.x 6 | 7 | ## How to install and setup it 8 | ```bash 9 | & npm i --save @ngx-resource/core @ngx-resource/handler-ngx-http 10 | ``` 11 | 12 | In you app module 13 | ```typescript 14 | 15 | // AoT requires an exported function for factories 16 | export function myHandlerFactory(http: HttpClient) { 17 | return new MyResourceHandler(http); 18 | } 19 | 20 | @NgModule({ 21 | imports: [ 22 | BrowserModule, 23 | BrowserAnimationsModule, 24 | HttpClientModule, 25 | 26 | // Default ResourceHandler uses class `ResourceHandlerHttpClient` 27 | ResourceModule.forRoot() 28 | 29 | // Or set you own handler 30 | //ResourceModule.forRoot({ 31 |    //  handler: { provide: ResourceHandler, useFactory: (myHandlerFactory), deps: [HttpClient] } 32 | //}) 33 | ], 34 | declarations: [...], 35 | bootstrap: [...], 36 | entryComponents: [...], 37 | providers: [...] 38 | }) 39 | export class AppModule { 40 | } 41 | ``` 42 | 43 | ## [Docs about @ngx-resource/core](https://github.com/troyanskiy/ngx-resource-core/blob/master/README.md) 44 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-ngx-http/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../../coverage/ngx-resource/handler-ngx-http'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-ngx-http/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../../dist/ngx-resource/handler-ngx-http", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | } 7 | } -------------------------------------------------------------------------------- /projects/ngx-resource/handler-ngx-http/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ngx-resource/handler-ngx-http", 3 | "version": "15.0.0", 4 | "description": "Resource handler for angular", 5 | "author": "Roman Rosluk ", 6 | "license": "MIT", 7 | "bugs": { 8 | "url": "https://github.com/troyanskiy/ngx-resource/issues" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/troyanskiy/ngx-resource.git" 13 | }, 14 | "keywords": [ 15 | "ngx-resource", 16 | "ngx-resource-rest", 17 | "ngx-resource-handler", 18 | "ngx-resource-handler-ngx-http", 19 | "ngx-resource-handler-http", 20 | "ng2-resource", 21 | "ng2-resource-rest", 22 | "ng2", 23 | "ng2-rest", 24 | "Angular2", 25 | "Resource" 26 | ], 27 | "dependencies": { 28 | "tslib": "^2.0.0" 29 | }, 30 | "peerDependencies": { 31 | "@angular/common": ">=15.0.0", 32 | "@angular/core": ">=15.0.0", 33 | "@ngx-resource/core": "^15.0.0", 34 | "rxjs": ">=6.0.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-ngx-http/src/lib/ResourceHandlerHttpClient.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams, HttpRequest, HttpResponse } from '@angular/common/http'; 3 | import { catchError, last, map } from 'rxjs/operators'; 4 | import { 5 | IResourceHandlerResponse, 6 | IResourceRequest, 7 | IResourceResponse, 8 | ResourceHandler, 9 | ResourceRequestMethod, 10 | ResourceResponseBodyType 11 | } from '@ngx-resource/core'; 12 | 13 | @Injectable() 14 | export class ResourceHandlerHttpClient extends ResourceHandler { 15 | 16 | constructor(private http: HttpClient) { 17 | super(); 18 | } 19 | 20 | handle(req: IResourceRequest): IResourceHandlerResponse { 21 | 22 | const request = this.prepareRequest(req); 23 | 24 | return { 25 | observable: this.http.request(request) 26 | .pipe( 27 | last(), 28 | map((resp: HttpResponse) => this.handleResponse(req, resp)), 29 | catchError((resp: HttpErrorResponse) => { 30 | throw this.handleResponse(req, resp); 31 | }) 32 | ) 33 | }; 34 | 35 | } 36 | 37 | 38 | protected prepareRequest(req: IResourceRequest): HttpRequest { 39 | 40 | let method = 'GET'; 41 | 42 | switch (req.method) { 43 | 44 | case ResourceRequestMethod.Get: 45 | method = 'GET'; 46 | break; 47 | 48 | case ResourceRequestMethod.Post: 49 | method = 'POST'; 50 | break; 51 | 52 | case ResourceRequestMethod.Put: 53 | method = 'PUT'; 54 | break; 55 | 56 | case ResourceRequestMethod.Delete: 57 | method = 'DELETE'; 58 | break; 59 | 60 | case ResourceRequestMethod.Head: 61 | method = 'HEAD'; 62 | break; 63 | 64 | case ResourceRequestMethod.Options: 65 | method = 'OPTIONS'; 66 | break; 67 | 68 | case ResourceRequestMethod.Patch: 69 | method = 'PATCH'; 70 | 71 | } 72 | 73 | const init: IHttpRequestInit = { 74 | withCredentials: req.withCredentials 75 | }; 76 | 77 | switch (req.responseBodyType) { 78 | 79 | case ResourceResponseBodyType.Json: 80 | init.responseType = 'json'; 81 | break; 82 | 83 | case ResourceResponseBodyType.ArrayBuffer: 84 | init.responseType = 'arraybuffer'; 85 | break; 86 | 87 | case ResourceResponseBodyType.Blob: 88 | init.responseType = 'blob'; 89 | break; 90 | 91 | default: 92 | init.responseType = 'text'; 93 | 94 | } 95 | 96 | if (req.headers) { 97 | init.headers = new HttpHeaders(req.headers); 98 | } 99 | 100 | if (req.query) { 101 | init.params = new HttpParams({fromObject: req.query}); 102 | } 103 | 104 | return new HttpRequest(method, req.url || '', req.body, init); 105 | 106 | } 107 | 108 | protected handleResponse(req: IResourceRequest, response: HttpResponse | HttpErrorResponse): IResourceResponse { 109 | 110 | const headers: any = {}; 111 | const keys = response.headers.keys(); 112 | keys.forEach((key: string) => { 113 | headers[key] = response.headers.getAll(key); 114 | }); 115 | 116 | return { 117 | status: response.status, 118 | body: (response as HttpResponse).body || (response as HttpErrorResponse).error, 119 | headers 120 | }; 121 | } 122 | 123 | } 124 | 125 | export type THttpRequestInitResponseType = 'arraybuffer' | 'blob' | 'json' | 'text'; 126 | 127 | export interface IHttpRequestInit { 128 | headers?: HttpHeaders; 129 | params?: HttpParams; 130 | responseType?: THttpRequestInitResponseType; 131 | withCredentials?: boolean; 132 | } 133 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-ngx-http/src/lib/ResourceModule.ts: -------------------------------------------------------------------------------- 1 | import { ModuleWithProviders, NgModule } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { IResourceModuleConfig, ResourceHandler, ResourceModule as ResourceModuleCore } from '@ngx-resource/core'; 4 | import { ResourceHandlerHttpClient } from './ResourceHandlerHttpClient'; 5 | 6 | @NgModule() 7 | export class ResourceModule extends ResourceModuleCore { 8 | 9 | /** 10 | * For root 11 | */ 12 | static forRoot(config: IResourceModuleConfig = {}): ModuleWithProviders { 13 | return ResourceModuleCore.forRoot({ 14 | handler: config.handler || {provide: ResourceHandler, useClass: ResourceHandlerHttpClient, deps: [HttpClient]} 15 | }); 16 | } 17 | 18 | /** 19 | * For child 20 | */ 21 | static forChild(config: IResourceModuleConfig = {}): ModuleWithProviders { 22 | return ResourceModuleCore.forChild({ 23 | handler: config.handler || {provide: ResourceHandler, useClass: ResourceHandlerHttpClient, deps: [HttpClient]} 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-ngx-http/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of handler-ngx-http 3 | */ 4 | 5 | export * from './lib/ResourceHandlerHttpClient'; 6 | export * from './lib/ResourceModule'; 7 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-ngx-http/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone'; 4 | import 'zone.js/dist/zone-testing'; 5 | import { getTestBed } from '@angular/core/testing'; 6 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; 7 | 8 | // First, initialize the Angular testing environment. 9 | getTestBed().initTestEnvironment( 10 | BrowserDynamicTestingModule, 11 | platformBrowserDynamicTesting() 12 | ); 13 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-ngx-http/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../out-tsc/lib", 5 | "declarationMap": true, 6 | "declaration": true, 7 | "inlineSources": true, 8 | "types": [], 9 | "lib": [ 10 | "dom", 11 | "es2018" 12 | ] 13 | }, 14 | "angularCompilerOptions": { 15 | "skipTemplateCodegen": true, 16 | "strictMetadataEmit": true, 17 | "fullTemplateTypeCheck": true, 18 | "strictInjectionParameters": true, 19 | "enableResourceInlining": true 20 | }, 21 | "exclude": [ 22 | "src/test.ts", 23 | "**/*.spec.ts" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-ngx-http/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.lib.json", 3 | "compilerOptions": { 4 | "declarationMap": false 5 | }, 6 | "angularCompilerOptions": { 7 | "enableIvy": true, 8 | "compilationMode": "partial" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-ngx-http/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts" 12 | ], 13 | "include": [ 14 | "**/*.spec.ts", 15 | "**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /projects/ngx-resource/handler-ngx-http/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "lib", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "lib", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /proxy.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "/api": { 3 | "target": "http://127.0.0.1:3222", 4 | "secure": false 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /server/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.7", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 10 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 11 | "requires": { 12 | "mime-types": "~2.1.24", 13 | "negotiator": "0.6.2" 14 | } 15 | }, 16 | "array-flatten": { 17 | "version": "1.1.1", 18 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 19 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 20 | }, 21 | "basic-auth": { 22 | "version": "2.0.1", 23 | "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", 24 | "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", 25 | "requires": { 26 | "safe-buffer": "5.1.2" 27 | } 28 | }, 29 | "body-parser": { 30 | "version": "1.19.0", 31 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 32 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 33 | "requires": { 34 | "bytes": "3.1.0", 35 | "content-type": "~1.0.4", 36 | "debug": "2.6.9", 37 | "depd": "~1.1.2", 38 | "http-errors": "1.7.2", 39 | "iconv-lite": "0.4.24", 40 | "on-finished": "~2.3.0", 41 | "qs": "6.7.0", 42 | "raw-body": "2.4.0", 43 | "type-is": "~1.6.17" 44 | } 45 | }, 46 | "bytes": { 47 | "version": "3.1.0", 48 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 49 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" 50 | }, 51 | "content-disposition": { 52 | "version": "0.5.3", 53 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 54 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 55 | "requires": { 56 | "safe-buffer": "5.1.2" 57 | } 58 | }, 59 | "content-type": { 60 | "version": "1.0.4", 61 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 62 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 63 | }, 64 | "cookie": { 65 | "version": "0.4.0", 66 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 67 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" 68 | }, 69 | "cookie-signature": { 70 | "version": "1.0.6", 71 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 72 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 73 | }, 74 | "debug": { 75 | "version": "2.6.9", 76 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 77 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 78 | "requires": { 79 | "ms": "2.0.0" 80 | } 81 | }, 82 | "depd": { 83 | "version": "1.1.2", 84 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 85 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 86 | }, 87 | "destroy": { 88 | "version": "1.0.4", 89 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 90 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 91 | }, 92 | "ee-first": { 93 | "version": "1.1.1", 94 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 95 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 96 | }, 97 | "encodeurl": { 98 | "version": "1.0.2", 99 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 100 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 101 | }, 102 | "escape-html": { 103 | "version": "1.0.3", 104 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 105 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 106 | }, 107 | "etag": { 108 | "version": "1.8.1", 109 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 110 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 111 | }, 112 | "express": { 113 | "version": "4.17.1", 114 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 115 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 116 | "requires": { 117 | "accepts": "~1.3.7", 118 | "array-flatten": "1.1.1", 119 | "body-parser": "1.19.0", 120 | "content-disposition": "0.5.3", 121 | "content-type": "~1.0.4", 122 | "cookie": "0.4.0", 123 | "cookie-signature": "1.0.6", 124 | "debug": "2.6.9", 125 | "depd": "~1.1.2", 126 | "encodeurl": "~1.0.2", 127 | "escape-html": "~1.0.3", 128 | "etag": "~1.8.1", 129 | "finalhandler": "~1.1.2", 130 | "fresh": "0.5.2", 131 | "merge-descriptors": "1.0.1", 132 | "methods": "~1.1.2", 133 | "on-finished": "~2.3.0", 134 | "parseurl": "~1.3.3", 135 | "path-to-regexp": "0.1.7", 136 | "proxy-addr": "~2.0.5", 137 | "qs": "6.7.0", 138 | "range-parser": "~1.2.1", 139 | "safe-buffer": "5.1.2", 140 | "send": "0.17.1", 141 | "serve-static": "1.14.1", 142 | "setprototypeof": "1.1.1", 143 | "statuses": "~1.5.0", 144 | "type-is": "~1.6.18", 145 | "utils-merge": "1.0.1", 146 | "vary": "~1.1.2" 147 | } 148 | }, 149 | "finalhandler": { 150 | "version": "1.1.2", 151 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 152 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 153 | "requires": { 154 | "debug": "2.6.9", 155 | "encodeurl": "~1.0.2", 156 | "escape-html": "~1.0.3", 157 | "on-finished": "~2.3.0", 158 | "parseurl": "~1.3.3", 159 | "statuses": "~1.5.0", 160 | "unpipe": "~1.0.0" 161 | } 162 | }, 163 | "forwarded": { 164 | "version": "0.1.2", 165 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 166 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 167 | }, 168 | "fresh": { 169 | "version": "0.5.2", 170 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 171 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 172 | }, 173 | "http-errors": { 174 | "version": "1.7.2", 175 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 176 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 177 | "requires": { 178 | "depd": "~1.1.2", 179 | "inherits": "2.0.3", 180 | "setprototypeof": "1.1.1", 181 | "statuses": ">= 1.5.0 < 2", 182 | "toidentifier": "1.0.0" 183 | } 184 | }, 185 | "iconv-lite": { 186 | "version": "0.4.24", 187 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 188 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 189 | "requires": { 190 | "safer-buffer": ">= 2.1.2 < 3" 191 | } 192 | }, 193 | "inherits": { 194 | "version": "2.0.3", 195 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 196 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 197 | }, 198 | "ipaddr.js": { 199 | "version": "1.9.0", 200 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", 201 | "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" 202 | }, 203 | "media-typer": { 204 | "version": "0.3.0", 205 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 206 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 207 | }, 208 | "merge-descriptors": { 209 | "version": "1.0.1", 210 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 211 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 212 | }, 213 | "methods": { 214 | "version": "1.1.2", 215 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 216 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 217 | }, 218 | "mime": { 219 | "version": "1.6.0", 220 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 221 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 222 | }, 223 | "mime-db": { 224 | "version": "1.40.0", 225 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", 226 | "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" 227 | }, 228 | "mime-types": { 229 | "version": "2.1.24", 230 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", 231 | "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", 232 | "requires": { 233 | "mime-db": "1.40.0" 234 | } 235 | }, 236 | "morgan": { 237 | "version": "1.9.1", 238 | "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", 239 | "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", 240 | "requires": { 241 | "basic-auth": "~2.0.0", 242 | "debug": "2.6.9", 243 | "depd": "~1.1.2", 244 | "on-finished": "~2.3.0", 245 | "on-headers": "~1.0.1" 246 | } 247 | }, 248 | "ms": { 249 | "version": "2.0.0", 250 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 251 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 252 | }, 253 | "negotiator": { 254 | "version": "0.6.2", 255 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 256 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 257 | }, 258 | "on-finished": { 259 | "version": "2.3.0", 260 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 261 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 262 | "requires": { 263 | "ee-first": "1.1.1" 264 | } 265 | }, 266 | "on-headers": { 267 | "version": "1.0.2", 268 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", 269 | "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" 270 | }, 271 | "parseurl": { 272 | "version": "1.3.3", 273 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 274 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 275 | }, 276 | "path-to-regexp": { 277 | "version": "0.1.7", 278 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 279 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 280 | }, 281 | "proxy-addr": { 282 | "version": "2.0.5", 283 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", 284 | "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", 285 | "requires": { 286 | "forwarded": "~0.1.2", 287 | "ipaddr.js": "1.9.0" 288 | } 289 | }, 290 | "qs": { 291 | "version": "6.7.0", 292 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 293 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" 294 | }, 295 | "range-parser": { 296 | "version": "1.2.1", 297 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 298 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 299 | }, 300 | "raw-body": { 301 | "version": "2.4.0", 302 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 303 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 304 | "requires": { 305 | "bytes": "3.1.0", 306 | "http-errors": "1.7.2", 307 | "iconv-lite": "0.4.24", 308 | "unpipe": "1.0.0" 309 | } 310 | }, 311 | "safe-buffer": { 312 | "version": "5.1.2", 313 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 314 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 315 | }, 316 | "safer-buffer": { 317 | "version": "2.1.2", 318 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 319 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 320 | }, 321 | "send": { 322 | "version": "0.17.1", 323 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 324 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 325 | "requires": { 326 | "debug": "2.6.9", 327 | "depd": "~1.1.2", 328 | "destroy": "~1.0.4", 329 | "encodeurl": "~1.0.2", 330 | "escape-html": "~1.0.3", 331 | "etag": "~1.8.1", 332 | "fresh": "0.5.2", 333 | "http-errors": "~1.7.2", 334 | "mime": "1.6.0", 335 | "ms": "2.1.1", 336 | "on-finished": "~2.3.0", 337 | "range-parser": "~1.2.1", 338 | "statuses": "~1.5.0" 339 | }, 340 | "dependencies": { 341 | "ms": { 342 | "version": "2.1.1", 343 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 344 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 345 | } 346 | } 347 | }, 348 | "serve-static": { 349 | "version": "1.14.1", 350 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 351 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 352 | "requires": { 353 | "encodeurl": "~1.0.2", 354 | "escape-html": "~1.0.3", 355 | "parseurl": "~1.3.3", 356 | "send": "0.17.1" 357 | } 358 | }, 359 | "setprototypeof": { 360 | "version": "1.1.1", 361 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 362 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 363 | }, 364 | "statuses": { 365 | "version": "1.5.0", 366 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 367 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 368 | }, 369 | "toidentifier": { 370 | "version": "1.0.0", 371 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 372 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" 373 | }, 374 | "type-is": { 375 | "version": "1.6.18", 376 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 377 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 378 | "requires": { 379 | "media-typer": "0.3.0", 380 | "mime-types": "~2.1.24" 381 | } 382 | }, 383 | "unpipe": { 384 | "version": "1.0.0", 385 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 386 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 387 | }, 388 | "utils-merge": { 389 | "version": "1.0.1", 390 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 391 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 392 | }, 393 | "vary": { 394 | "version": "1.1.2", 395 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 396 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 397 | } 398 | } 399 | } 400 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "body-parser": "^1.19.0", 13 | "express": "^4.17.1", 14 | "morgan": "^1.9.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troyanskiy/ngx-resource/6b62040e0a34674d5f4537fea6fd029baad06dbb/src/app/app.component.scss -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, waitForAsync } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | 4 | describe('AppComponent', () => { 5 | beforeEach(waitForAsync(() => { 6 | TestBed.configureTestingModule({ 7 | declarations: [ 8 | AppComponent 9 | ], 10 | }).compileComponents(); 11 | })); 12 | 13 | it('should create the app', () => { 14 | const fixture = TestBed.createComponent(AppComponent); 15 | const app = fixture.debugElement.componentInstance; 16 | expect(app).toBeTruthy(); 17 | }); 18 | 19 | it(`should have as title 'ngx-resource-mono'`, () => { 20 | const fixture = TestBed.createComponent(AppComponent); 21 | const app = fixture.debugElement.componentInstance; 22 | expect(app.title).toEqual('ngx-resource-mono'); 23 | }); 24 | 25 | it('should render title in a h1 tag', () => { 26 | const fixture = TestBed.createComponent(AppComponent); 27 | fixture.detectChanges(); 28 | const compiled = fixture.debugElement.nativeElement; 29 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to ngx-resource-mono!'); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.scss'] 7 | }) 8 | export class AppComponent { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { AppComponent } from './app.component'; 5 | import { HttpClientModule } from '@angular/common/http'; 6 | import { ResourceModule } from '@ngx-resource/handler-ngx-http'; 7 | import { CrudObsTestComponent } from './components/crud-obs-test/crud-obs-test.component'; 8 | 9 | @NgModule({ 10 | declarations: [ 11 | AppComponent, 12 | CrudObsTestComponent 13 | ], 14 | imports: [ 15 | BrowserModule, 16 | HttpClientModule, 17 | ResourceModule.forRoot() 18 | ], 19 | providers: [], 20 | bootstrap: [AppComponent] 21 | }) 22 | export class AppModule { } 23 | -------------------------------------------------------------------------------- /src/app/components/crud-obs-test/crud-obs-test.component.html: -------------------------------------------------------------------------------- 1 |

crud-obs-test tests!

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
MethodRes Async
QueryLoaded {{list.length}}
Get OneLoaded {{one.title}}
16 | 17 | 18 | 19 | Loading stuff... 20 | 21 | -------------------------------------------------------------------------------- /src/app/components/crud-obs-test/crud-obs-test.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troyanskiy/ngx-resource/6b62040e0a34674d5f4537fea6fd029baad06dbb/src/app/components/crud-obs-test/crud-obs-test.component.scss -------------------------------------------------------------------------------- /src/app/components/crud-obs-test/crud-obs-test.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { CrudObsTestComponent } from './crud-obs-test.component'; 4 | 5 | describe('CrudObsTestComponent', () => { 6 | let component: CrudObsTestComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ CrudObsTestComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(CrudObsTestComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/components/crud-obs-test/crud-obs-test.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { CrudObsTestResource } from '../../resources/crud-obs-test/crud-obs-test.resource'; 3 | import { Observable } from 'rxjs'; 4 | 5 | @Component({ 6 | selector: 'app-crud-obs-test', 7 | templateUrl: './crud-obs-test.component.html', 8 | styleUrls: ['./crud-obs-test.component.scss'] 9 | }) 10 | export class CrudObsTestComponent implements OnInit { 11 | 12 | query$: Observable; 13 | getOne$: Observable; 14 | 15 | constructor(private testResource: CrudObsTestResource) { 16 | this.query$ = this.testResource.query(); 17 | this.getOne$ = this.testResource.get({id: Math.ceil(Math.random() * 50)}); 18 | } 19 | 20 | ngOnInit() { 21 | 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/app/resources/crud-obs-test/crud-obs-test.resource.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { CrudObsTestResource } from './crud-obs-test.resource'; 4 | 5 | describe('CrudObsTestResource', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: CrudObsTestResource = TestBed.get(CrudObsTestResource); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/app/resources/crud-obs-test/crud-obs-test.resource.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { ResourceCRUDObservable, ResourceParams } from '@ngx-resource/core'; 3 | 4 | @Injectable({ 5 | providedIn: 'root' 6 | }) 7 | @ResourceParams({ 8 | pathPrefix: '/api/crud-test' 9 | }) 10 | export class CrudObsTestResource extends ResourceCRUDObservable { 11 | // TODO: add explicit constructor 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troyanskiy/ngx-resource/6b62040e0a34674d5f4537fea6fd029baad06dbb/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troyanskiy/ngx-resource/6b62040e0a34674d5f4537fea6fd029baad06dbb/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NgxResourceMono 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags.ts'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | 61 | /*************************************************************************************************** 62 | * APPLICATION IMPORTS 63 | */ 64 | -------------------------------------------------------------------------------- /src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | table { 4 | 5 | tr.deleted td { 6 | background: rgba(255, 0, 0, 0.46); 7 | } 8 | 9 | 10 | 11 | th,td { 12 | border: 1px solid #000; 13 | 14 | &.yes { 15 | background: green; 16 | font-weight: bold; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | // First, initialize the Angular testing environment. 11 | getTestBed().initTestEnvironment( 12 | BrowserDynamicTestingModule, 13 | platformBrowserDynamicTesting() 14 | ); 15 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": [ 8 | "src/main.ts", 9 | "src/polyfills.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "module": "es2020", 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "importHelpers": true, 13 | "strictNullChecks": true, 14 | "target": "ES2022", 15 | "typeRoots": [ 16 | "node_modules/@types" 17 | ], 18 | "lib": [ 19 | "es2018", 20 | "dom" 21 | ], 22 | "paths": { 23 | "@ngx-resource/core": [ 24 | "dist/ngx-resource/core" 25 | ], 26 | "@ngx-resource/core/*": [ 27 | "dist/ngx-resource/core/*" 28 | ], 29 | "@ngx-resource/handler-ngx-http": [ 30 | "dist/ngx-resource/handler-ngx-http" 31 | ], 32 | "@ngx-resource/handler-ngx-http/*": [ 33 | "dist/ngx-resource/handler-ngx-http/*" 34 | ], 35 | "@ngx-resource/handler-ngx-http-legacy": [ 36 | "dist/ngx-resource/handler-ngx-http-legacy" 37 | ], 38 | "@ngx-resource/handler-ngx-http-legacy/*": [ 39 | "dist/ngx-resource/handler-ngx-http-legacy/*" 40 | ], 41 | "@ngx-resource/handler-cordova-advanced-http": [ 42 | "dist/ngx-resource/handler-cordova-advanced-http" 43 | ], 44 | "@ngx-resource/handler-cordova-advanced-http/*": [ 45 | "dist/ngx-resource/handler-cordova-advanced-http/*" 46 | ], 47 | "@ngx-resource/handler-fetch": [ 48 | "dist/ngx-resource/handler-fetch" 49 | ], 50 | "@ngx-resource/handler-fetch/*": [ 51 | "dist/ngx-resource/handler-fetch/*" 52 | ] 53 | }, 54 | "useDefineForClassFields": false 55 | } 56 | } -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:recommended" 4 | ], 5 | "rules": { 6 | "array-type": false, 7 | "arrow-parens": false, 8 | "deprecation": { 9 | "severity": "warn" 10 | }, 11 | "component-class-suffix": true, 12 | "contextual-lifecycle": true, 13 | "directive-class-suffix": true, 14 | "directive-selector": [ 15 | true, 16 | "attribute", 17 | "app", 18 | "camelCase" 19 | ], 20 | "component-selector": [ 21 | true, 22 | "element", 23 | "app", 24 | "kebab-case" 25 | ], 26 | "import-blacklist": [ 27 | true, 28 | "rxjs/Rx" 29 | ], 30 | "interface-name": false, 31 | "max-classes-per-file": false, 32 | "max-line-length": [ 33 | true, 34 | 140 35 | ], 36 | "member-access": false, 37 | "member-ordering": [ 38 | true, 39 | { 40 | "order": [ 41 | "static-field", 42 | "instance-field", 43 | "static-method", 44 | "instance-method" 45 | ] 46 | } 47 | ], 48 | "no-consecutive-blank-lines": false, 49 | "no-console": [ 50 | true, 51 | "debug", 52 | "info", 53 | "time", 54 | "timeEnd", 55 | "trace" 56 | ], 57 | "no-empty": false, 58 | "no-inferrable-types": [ 59 | true, 60 | "ignore-params" 61 | ], 62 | "no-non-null-assertion": true, 63 | "no-redundant-jsdoc": true, 64 | "no-switch-case-fall-through": true, 65 | "no-var-requires": false, 66 | "object-literal-key-quotes": [ 67 | true, 68 | "as-needed" 69 | ], 70 | "object-literal-sort-keys": false, 71 | "ordered-imports": false, 72 | "quotemark": [ 73 | true, 74 | "single" 75 | ], 76 | "trailing-comma": false, 77 | "no-conflicting-lifecycle": true, 78 | "no-host-metadata-property": true, 79 | "no-input-rename": true, 80 | "no-inputs-metadata-property": true, 81 | "no-output-native": true, 82 | "no-output-on-prefix": true, 83 | "no-output-rename": true, 84 | "no-outputs-metadata-property": true, 85 | "template-banana-in-box": true, 86 | "template-no-negated-async": true, 87 | "use-lifecycle-interface": true, 88 | "use-pipe-transform-interface": true 89 | }, 90 | "rulesDirectory": [ 91 | "codelyzer" 92 | ], 93 | 94 | "newline-before-return": true 95 | } 96 | --------------------------------------------------------------------------------