@lagoshny/ngx-hateoas-client
Version:
This client used to develop `Angular 12+` applications working with RESTfulll server API with HAL/JSON response type (supports server implementation by Spring HATEOAS)
1 lines • 249 kB
Source Map (JSON)
{"version":3,"file":"lagoshny-ngx-hateoas-client.mjs","sources":["../../../projects/ngx-hateoas-client/src/lib/util/dependency-injector.ts","../../../projects/ngx-hateoas-client/src/lib/config/hateoas-configuration.interface.ts","../../../projects/ngx-hateoas-client/src/lib/model/declarations.ts","../../../projects/ngx-hateoas-client/src/lib/config/lib-config.ts","../../../projects/ngx-hateoas-client/src/lib/logger/console-logger.ts","../../../projects/ngx-hateoas-client/src/lib/model/resource-type.ts","../../../projects/ngx-hateoas-client/src/lib/logger/stage.enum.ts","../../../projects/ngx-hateoas-client/src/lib/logger/stage-logger.ts","../../../projects/ngx-hateoas-client/src/lib/util/validation.utils.ts","../../../projects/ngx-hateoas-client/src/lib/util/url.utils.ts","../../../projects/ngx-hateoas-client/src/lib/util/resource.utils.ts","../../../projects/ngx-hateoas-client/src/lib/service/internal/cache/model/cache-key.ts","../../../projects/ngx-hateoas-client/src/lib/util/cache.utils.ts","../../../projects/ngx-hateoas-client/src/lib/service/http-executor.ts","../../../projects/ngx-hateoas-client/src/lib/service/internal/cache/model/cached-resource.ts","../../../projects/ngx-hateoas-client/src/lib/service/internal/cache/resource-cache.service.ts","../../../projects/ngx-hateoas-client/src/lib/service/internal/resource-http.service.ts","../../../projects/ngx-hateoas-client/src/lib/model/resource/abstract-resource.ts","../../../projects/ngx-hateoas-client/src/lib/service/internal/resource-collection-http.service.ts","../../../projects/ngx-hateoas-client/src/lib/service/internal/paged-resource-collection-http.service.ts","../../../projects/ngx-hateoas-client/src/lib/model/resource/base-resource.ts","../../../projects/ngx-hateoas-client/src/lib/model/resource/resource.ts","../../../projects/ngx-hateoas-client/src/lib/model/resource/resource-collection.ts","../../../projects/ngx-hateoas-client/src/lib/model/resource/embedded-resource.ts","../../../projects/ngx-hateoas-client/src/lib/model/resource/paged-resource-collection.ts","../../../projects/ngx-hateoas-client/src/lib/config/ngx-hateoas-client-configuration.service.ts","../../../projects/ngx-hateoas-client/src/lib/service/internal/common-resource-http.service.ts","../../../projects/ngx-hateoas-client/src/lib/service/external/hateoas-resource.service.ts","../../../projects/ngx-hateoas-client/src/lib/service/external/hateoas-resource-operation.ts","../../../projects/ngx-hateoas-client/src/lib/model/decorators.ts","../../../projects/ngx-hateoas-client/src/lib/ngx-hateoas-client.module.ts","../../../projects/ngx-hateoas-client/src/public-api.ts","../../../projects/ngx-hateoas-client/src/lagoshny-ngx-hateoas-client.ts"],"sourcesContent":["import { Injector, Type } from '@angular/core';\r\n\r\n/**\r\n * Holds dependency injector to allow use ше in internal the lib classes.\r\n */\r\n/* tslint:disable:variable-name */\r\nexport class DependencyInjector {\r\n\r\n private static _injector: Injector = null;\r\n\r\n static get<T>(type: Type<T>): T {\r\n if (this._injector) {\r\n return this._injector.get(type);\r\n }\r\n throw new Error('You need initialize Injector');\r\n }\r\n\r\n static set injector(value: Injector) {\r\n this._injector = value;\r\n }\r\n\r\n}\r\n","/**\r\n * Describe all client configuration params.\r\n */\r\nimport { Resource } from '../model/resource/resource';\r\nimport { EmbeddedResource } from '../model/resource/embedded-resource';\r\nimport { CacheMode } from '../model/declarations';\r\n\r\nexport const DEFAULT_ROUTE_NAME = 'defaultRoute';\r\n\r\n/**\r\n * Used to specify additional {@link Resource} options.\r\n */\r\nexport interface ResourceOption {\r\n /**\r\n * Name of the route that configured in {@link HateoasConfiguration#http} as {@link MultipleResourceRoutes}.\r\n * Be default used route with name 'defaultRoute'.\r\n *\r\n * See more about this option in <a href=\"https://github.com/lagoshny/ngx-hateoas-client/blob/master/README.md#options\">documentation</a>.\r\n */\r\n routeName?: string;\r\n}\r\n\r\n/**\r\n * Resource route config that defined where from retrieve resources.\r\n * If you use this config, then a default route created with name 'defaultRoute' will be assigned to all resources.\r\n */\r\nexport interface ResourceRoute {\r\n /**\r\n * Root server url.\r\n *\r\n * For default Spring application it looks like: http://localhost:8080.\r\n */\r\n rootUrl: string;\r\n /**\r\n * Proxy url on which to send requests.\r\n * If passed then it uses to change rootUrl to proxyUrl when get relation link.\r\n *\r\n * For default Spring application it looks like: http://localhost:8080/api/v1.\r\n */\r\n proxyUrl?: string;\r\n}\r\n\r\n/**\r\n * Defines several resource routes.\r\n */\r\nexport interface MultipleResourceRoutes {\r\n /**\r\n * Each resource route is declared as {@link ResourceRoute} object with root and proxy url if need it.\r\n * Specified route name is used in {@link ResourceOption#routeName} to retrieve resource by this route.\r\n *\r\n * If you want to declare only one route, you need to use default route name as 'defaultRoute' or use simple {@link ResourceRoute} config.\r\n */\r\n [routeName: string]: ResourceRoute;\r\n}\r\n\r\nexport interface HateoasConfiguration {\r\n\r\n /**\r\n * Http options.\r\n * {@link ResourceRoute} declare common resource route that created with default name 'defaultRoute'.\r\n * {@link MultipleResourceRoutes} declare several resource routes,\r\n * to define default route in this case, use default route name 'defaultRoute'.\r\n */\r\n http: ResourceRoute | MultipleResourceRoutes;\r\n\r\n /**\r\n * Logging option.\r\n */\r\n logs?: {\r\n /**\r\n * Should print verbose logs to the console.\r\n */\r\n verboseLogs?: boolean;\r\n };\r\n\r\n /**\r\n * Cache options.\r\n */\r\n cache?: {\r\n /**\r\n * When {@code true} then cache will be used, {@code false} otherwise.\r\n */\r\n enabled: boolean;\r\n /**\r\n * Allows to adjust cache more granular using {@link CacheMode} modes.\r\n */\r\n mode?: CacheMode;\r\n /**\r\n * Time in milliseconds after which cache need to be expired.\r\n */\r\n lifeTime?: number;\r\n };\r\n\r\n /**\r\n * Declared resource/embedded resource types that will be used to create resources from server response that contains resources.\r\n */\r\n useTypes?: {\r\n resources: Array<new (...args: any[]) => Resource>;\r\n embeddedResources?: Array<new (...args: any[]) => EmbeddedResource>;\r\n };\r\n\r\n /**\r\n * {@code true} when running in production environment, {@code false} otherwise.\r\n */\r\n isProduction?: boolean;\r\n\r\n /**\r\n * Specifying format for some type values.\r\n */\r\n typesFormat?: {\r\n /**\r\n * This date format will be used when parse {@link Resource} properties.\r\n * If the property will be match to some one of specified formats, then the property type will be as Date object.\r\n * Otherwise, raw type will be used as default.\r\n */\r\n date?: {\r\n /**\r\n * Date pattern.\r\n * The {@link https://date-fns.org} lib is used to parse date with patterns, use patterns supported by this lib.\r\n * See more about supported formats <a href='https://date-fns.org/v2.28.0/docs/parse'>here</a>.\r\n */\r\n patterns: Array<string>;\r\n }\r\n };\r\n\r\n /**\r\n * Let to change default page params that is size = 20, page = 0.\r\n */\r\n pagination?: {\r\n defaultPage: {\r\n size: number;\r\n page?: number;\r\n }\r\n };\r\n\r\n /**\r\n * Additional configuration to specify settings for HAL format.\r\n */\r\n halFormat?: {\r\n json?: {\r\n /**\r\n * {@code true} when empty object {} should be converted to {@code null} value\r\n * {@code false} when empty object {} should be used as is\r\n */\r\n convertEmptyObjectToNull: boolean;\r\n },\r\n collections?: {\r\n /**\r\n * If {@code true}, then for empty collections, not required to specify _embedded property.\r\n * When {@code false} (be default), you need to specify empty _embedded property for empty collections.\r\n *\r\n * By default, Spring Data REST includes empty _embedded property for empty collections,\r\n * but when using Spring HATEOAS you need to do it manually.\r\n *\r\n * Recommending use Spring Data REST approach and return empty _embedded property for empty collection\r\n * for more predictable determine resource type algorithm.\r\n */\r\n embeddedOptional: boolean;\r\n }\r\n };\r\n\r\n}\r\n","import { Resource } from './resource/resource';\r\nimport { BaseResource } from './resource/base-resource';\r\nimport { EmbeddedResource } from './resource/embedded-resource';\r\nimport { HttpHeaders, HttpParams } from '@angular/common/http';\r\n\r\nexport const RESOURCE_NAME_PROP = '__resourceName__';\r\nexport const RESOURCE_OPTIONS_PROP = '__options__';\r\n\r\n/**\r\n * Resource link object.\r\n */\r\nexport interface Link {\r\n /**\r\n * Link name.\r\n */\r\n [key: string]: LinkData;\r\n}\r\n\r\nexport interface LinkData {\r\n /**\r\n * Link url.\r\n */\r\n href: string;\r\n /**\r\n * {@code true} if <b>href</b> has template, {@code false} otherwise.\r\n */\r\n templated?: boolean;\r\n}\r\n\r\n/**\r\n * Interface that allows to identify that object is resource when it is has a links object.\r\n */\r\nexport interface ResourceIdentifiable {\r\n\r\n /**\r\n * List of links related with the resource.\r\n */\r\n _links: Link;\r\n}\r\n\r\n/**\r\n * Http options that used by Angular HttpClient.\r\n */\r\nexport interface HttpClientOptions {\r\n headers?: HttpHeaders | {\r\n [header: string]: string | string[];\r\n };\r\n observe?: 'body' | 'response';\r\n params?: HttpParams;\r\n reportProgress?: boolean;\r\n responseType?: 'json';\r\n withCredentials?: boolean;\r\n}\r\n\r\n/**\r\n * Extend {@link GetOption} with page param.\r\n */\r\nexport interface PagedGetOption extends GetOption {\r\n pageParams?: PageParam;\r\n}\r\n\r\n/**\r\n * Contains options that can be applied to POST/PUT/PATCH/DELETE request.\r\n */\r\nexport interface RequestOption {\r\n params?: RequestParam;\r\n headers?: HttpHeaders | {\r\n [header: string]: string | string[];\r\n };\r\n observe?: 'body' | 'response';\r\n reportProgress?: boolean;\r\n withCredentials?: boolean;\r\n}\r\n\r\n/**\r\n * Contains additional options that can be applied to the GET request.\r\n */\r\nexport interface GetOption extends RequestOption {\r\n /**\r\n * Sorting options.\r\n */\r\n sort?: Sort;\r\n useCache?: boolean;\r\n}\r\n\r\n/**\r\n * Request params that will be applied to the result url as http request params.\r\n *\r\n * Should not contains params as: 'projection' and {@link PageParam} properties.\r\n * If want pass this params then use suitable properties from {@link GetOption} or {@link PagedGetOption},\r\n * otherwise exception will be thrown.\r\n */\r\nexport interface RequestParam {\r\n [paramName: string]: Resource | string | number | boolean | Array<string> | Array<number>;\r\n}\r\n\r\n/**\r\n * Page content params.\r\n */\r\nexport interface PageParam {\r\n /**\r\n * Number of page.\r\n */\r\n page?: number;\r\n\r\n /**\r\n * Page size.\r\n */\r\n size?: number;\r\n}\r\n\r\n/**\r\n * Page params with sort option.\r\n */\r\nexport interface SortedPageParam {\r\n /**\r\n * Page content params.\r\n */\r\n pageParams?: PageParam;\r\n /**\r\n * Sorting options.\r\n */\r\n sort?: Sort;\r\n}\r\n\r\nexport type SortOrder = 'DESC' | 'ASC';\r\n\r\nexport interface Sort {\r\n /**\r\n * Name of the property to sort.\r\n */\r\n [propertyToSort: string]: SortOrder;\r\n}\r\n\r\n/**\r\n * Page resource response from Spring application.\r\n */\r\nexport interface PageData {\r\n page: {\r\n size: number;\r\n totalElements: number;\r\n totalPages: number;\r\n number: number;\r\n };\r\n _links?: {\r\n first: {\r\n href: string\r\n };\r\n prev?: {\r\n href: string\r\n };\r\n self: {\r\n href: string\r\n };\r\n next?: {\r\n href: string\r\n };\r\n last: {\r\n href: string\r\n };\r\n };\r\n}\r\n\r\nexport enum Include {\r\n /**\r\n * Allows to include null values to request body\r\n */\r\n NULL_VALUES = 'NULL_VALUES',\r\n /**\r\n * Not replace related resources with their self links, instead pass them as JSON objects.\r\n */\r\n REL_RESOURCES_AS_OBJECTS = 'REL_RESOURCES_AS_OBJECTS'\r\n}\r\n\r\n/**\r\n * Include options that allow configure should include or not some specific values\r\n * (e.q. null values).\r\n */\r\nexport interface ValuesOption {\r\n include: Include | Include[];\r\n}\r\n\r\n/**\r\n * Request body object.\r\n */\r\nexport interface RequestBody<T> {\r\n /**\r\n * Any object that will be passed as request body.\r\n */\r\n body: T;\r\n /**\r\n * Use this param to influence on body values that you want include or not.\r\n */\r\n valuesOption?: ValuesOption;\r\n}\r\n\r\n/**\r\n * Supported http methods for custom query.\r\n */\r\nexport enum HttpMethod {\r\n GET = 'GET', POST = 'POST', PUT = 'PUT', PATCH = 'PATCH'\r\n}\r\n\r\ntype NonResourcePropertyType<T> = {\r\n [K in keyof T]: T[K] extends BaseResource ? never : K;\r\n}[keyof T];\r\n\r\n/**\r\n * Type that allowed represent resource relations as resource projection excluding {@link Resource},\r\n * {@link EmbeddedResource} props and methods from current type.\r\n */\r\nexport type ProjectionRelType<T extends BaseResource> =\r\n Pick<T, Exclude<keyof T, keyof Resource | keyof EmbeddedResource> & NonResourcePropertyType<T>>;\r\n\r\n/**\r\n * Additional cache modes.\r\n */\r\nexport enum CacheMode {\r\n /**\r\n * Default mode.\r\n * When cache enable, then all HTTP GET methods will use cache. Except methods where explicitly passed {useCache : false}.\r\n */\r\n ALWAYS = 'ALWAYS',\r\n\r\n /**\r\n * This is opposite option for ALWAYS mode.\r\n * When cache enable, that mode will NOT use cache by default on all HTTP GET methods.\r\n * Except methods where explicitly passed {useCache : true}.\r\n */\r\n ON_DEMAND = 'ON_DEMAND'\r\n}\r\n","import { DEFAULT_ROUTE_NAME, HateoasConfiguration } from './hateoas-configuration.interface';\r\nimport { CacheMode } from '../model/declarations';\r\n\r\n/**\r\n * Contains all configuration lib params.\r\n */\r\n// tslint:disable:no-string-literal\r\nexport class LibConfig {\r\n\r\n public static readonly DEFAULT_CONFIG: HateoasConfiguration = {\r\n http: {\r\n [DEFAULT_ROUTE_NAME]: {\r\n rootUrl: 'http://localhost:8080/api/v1'\r\n },\r\n },\r\n logs: {\r\n verboseLogs: false\r\n },\r\n cache: {\r\n enabled: true,\r\n mode: CacheMode.ALWAYS,\r\n lifeTime: 5 * 60 * 1000\r\n },\r\n useTypes: {\r\n resources: []\r\n },\r\n pagination: {\r\n defaultPage: {\r\n size: 20,\r\n page: 0\r\n }\r\n },\r\n halFormat: {\r\n json: {\r\n convertEmptyObjectToNull: true\r\n },\r\n collections: {\r\n embeddedOptional: false\r\n }\r\n },\r\n isProduction: false\r\n };\r\n\r\n private static config: HateoasConfiguration = LibConfig.DEFAULT_CONFIG;\r\n\r\n public static setConfig(hateoasConfiguration: HateoasConfiguration) {\r\n LibConfig.config = LibConfig.mergeConfigs(hateoasConfiguration);\r\n }\r\n\r\n public static getConfig(): HateoasConfiguration {\r\n return LibConfig.config;\r\n }\r\n\r\n public static mergeConfigs(config: HateoasConfiguration): HateoasConfiguration {\r\n return {\r\n ...LibConfig.DEFAULT_CONFIG,\r\n ...config,\r\n halFormat: {\r\n json: {\r\n ...LibConfig.DEFAULT_CONFIG.halFormat.json,\r\n ...config?.halFormat?.json\r\n },\r\n collections: {\r\n ...LibConfig.DEFAULT_CONFIG.halFormat.collections,\r\n ...config?.halFormat?.collections\r\n }\r\n },\r\n pagination: {\r\n defaultPage: {\r\n ...LibConfig.DEFAULT_CONFIG.pagination.defaultPage,\r\n ...config?.pagination?.defaultPage\r\n }\r\n },\r\n cache: {\r\n ...LibConfig.DEFAULT_CONFIG.cache,\r\n ...config?.cache\r\n },\r\n };\r\n }\r\n\r\n}\r\n","import { LibConfig } from '../config/lib-config';\r\nimport { camelCase, capitalize, isEmpty } from 'lodash-es';\r\n\r\n/* tslint:disable:variable-name no-console */\r\nexport class ConsoleLogger {\r\n\r\n public static info(message?: any, ...optionalParams: any[]): void {\r\n if (!this.isLoggingEnabled()) {\r\n return;\r\n }\r\n console.info(message, ...optionalParams);\r\n }\r\n\r\n public static warn(message?: any, ...optionalParams: any[]): void {\r\n if (!this.isLoggingEnabled()) {\r\n return;\r\n }\r\n console.warn(message, ...optionalParams);\r\n }\r\n\r\n public static error(message?: any, ...optionalParams: any[]): void {\r\n if (!this.isLoggingEnabled()) {\r\n return;\r\n }\r\n console.error(message, ...optionalParams);\r\n }\r\n\r\n /**\r\n * Log info messages in pretty format.\r\n *\r\n * @param message log message\r\n * @param params additional params for verbose log\r\n */\r\n public static prettyInfo(message: string, params?: object): void {\r\n if (!this.isLoggingEnabled()) {\r\n return;\r\n }\r\n\r\n let msg = `%c${ message }\\n`;\r\n const color = [\r\n 'color: #201AB3;'\r\n ];\r\n\r\n if (!isEmpty(params)) {\r\n for (const [key, value] of Object.entries(params)) {\r\n if (key.toLowerCase() === 'result') {\r\n msg += `%c${ capitalize(key) }: %c${ value }\\n`;\r\n color.push('color: #3AA6D0;', 'color: #00BA45;');\r\n } else {\r\n msg += `%c${ camelCase(key) }: %c${ value }\\n`;\r\n color.push('color: #3AA6D0;', 'color: default;');\r\n }\r\n }\r\n }\r\n\r\n ConsoleLogger.info(msg, ...color);\r\n }\r\n\r\n public static objectPrettyInfo(message: string, params?: object): void {\r\n if (!this.isLoggingEnabled()) {\r\n return;\r\n }\r\n\r\n const msg = `%c${ message }\\n`;\r\n const color = [\r\n 'color: #201AB3;'\r\n ];\r\n\r\n ConsoleLogger.info(msg, ...color, params);\r\n }\r\n\r\n /**\r\n * Log resource info messages in pretty format.\r\n *\r\n * @param message log message\r\n * @param resourceName resource name\r\n * @param params additional params for verbose log\r\n */\r\n public static resourcePrettyInfo(resourceName: string, message: string, params?: object): void {\r\n if (!this.isLoggingEnabled()) {\r\n return;\r\n }\r\n\r\n let msg = `%c${ resourceName } %c${ message }\\n`;\r\n const color = [\r\n 'color: #DA005C;',\r\n 'color: #201AB3;'\r\n ];\r\n\r\n if (!isEmpty(params)) {\r\n for (const [key, value] of Object.entries(params)) {\r\n if (key.toLowerCase() === 'result') {\r\n msg += `%c${ capitalize(key) }: %c${ value }\\n`;\r\n color.push('color: #3AA6D0;', 'color: #00BA45;');\r\n } else {\r\n msg += `%c${ camelCase(key) }: %c${ value }\\n`;\r\n color.push('color: #3AA6D0;', 'color: default;');\r\n }\r\n }\r\n }\r\n\r\n ConsoleLogger.info(msg, ...color);\r\n }\r\n\r\n /**\r\n * Log error messages in pretty format.\r\n *\r\n * @param message log message\r\n * @param params additional params for verbose log\r\n */\r\n public static prettyError(message: string, params?: object): void {\r\n if (!this.isLoggingEnabled()) {\r\n return;\r\n }\r\n\r\n let msg = `%c${ message }\\n`;\r\n const color = [\r\n 'color: #df004f;'\r\n ];\r\n\r\n if (!isEmpty(params)) {\r\n for (const [key, value] of Object.entries(params)) {\r\n if (key.toLowerCase() === 'error') {\r\n msg += `%c${ capitalize(key) }: %c${ value }\\n`;\r\n color.push('color: #df004f;', 'color: #ff0000;');\r\n } else {\r\n msg += `%c${ capitalize(key) }: %c${ value }\\n`;\r\n color.push('color: #3AA6D0;', 'color: #000;');\r\n }\r\n }\r\n }\r\n\r\n ConsoleLogger.error(msg, ...color);\r\n }\r\n\r\n /**\r\n * Log warn messages in pretty format.\r\n *\r\n * @param message log message\r\n * @param params additional params for verbose log\r\n */\r\n public static prettyWarn(message: string, params?: object): void {\r\n if (!this.isLoggingEnabled()) {\r\n return;\r\n }\r\n\r\n let msg = `%c${ message }\\n`;\r\n const color = [\r\n 'color: #ffbe00;'\r\n ];\r\n\r\n if (!isEmpty(params)) {\r\n for (const [key, value] of Object.entries(params)) {\r\n msg += `%c${ capitalize(key) }: %c${ value }\\n`;\r\n color.push('color: #3AA6D0;', 'color: #000;');\r\n }\r\n }\r\n\r\n ConsoleLogger.warn(msg, ...color);\r\n }\r\n\r\n private static isLoggingEnabled() {\r\n return (!LibConfig.getConfig().isProduction && LibConfig.getConfig().logs.verboseLogs)\r\n || LibConfig.getConfig().logs.verboseLogs;\r\n }\r\n\r\n}\r\n","import { isObject } from 'lodash-es';\r\nimport { LibConfig } from '../config/lib-config';\r\n\r\nexport function isEmbeddedResource(object: any) {\r\n // Embedded resource doesn't have self link in _links object\r\n return !isPagedResourceCollection(object) && !isResourceCollection(object)\r\n && isResourceObject(object) && !('self' in (object._links as object));\r\n}\r\n\r\nexport function isResource(object: any): boolean {\r\n return !isPagedResourceCollection(object) && !isResourceCollection(object)\r\n && isResourceObject(object) && ('self' in (object._links as object));\r\n}\r\n\r\nexport function isResourceCollection(object: any): boolean {\r\n const baseCondition = isObject(object) &&\r\n ('_links' in object) &&\r\n !('page' in object);\r\n if (!baseCondition) {\r\n return false;\r\n }\r\n\r\n if (LibConfig.getConfig().halFormat.collections.embeddedOptional) {\r\n return baseCondition && (Object.keys(object).length === 1 ||\r\n '_embedded' in object && Object.keys(object).length === 2);\r\n } else {\r\n return baseCondition && '_embedded' in object && Object.keys(object).length === 2;\r\n }\r\n}\r\n\r\nexport function isPagedResourceCollection(object: any): boolean {\r\n const baseCondition = isObject(object) &&\r\n ('_links' in object) &&\r\n ('page' in object);\r\n if (!baseCondition) {\r\n return false;\r\n }\r\n\r\n if (LibConfig.getConfig().halFormat.collections.embeddedOptional) {\r\n return baseCondition && (Object.keys(object).length === 2 ||\r\n '_embedded' in object && Object.keys(object).length === 3);\r\n } else {\r\n return baseCondition && '_embedded' in object && Object.keys(object).length === 3;\r\n }\r\n}\r\n\r\n/**\r\n * Check that passed object has links property.\r\n *\r\n * @param object which need to check links property\r\n */\r\nexport function isResourceObject(object: any) {\r\n return isObject(object) && ('_links' in object);\r\n}\r\n\r\n/**\r\n * Defining resource type bypassed object.\r\n *\r\n * @param object that presumably is one of resource type\r\n */\r\nexport function getResourceType(object: any): string {\r\n if (isEmbeddedResource(object)) {\r\n return 'EmbeddedResource';\r\n } else if (isResource(object)) {\r\n return 'Resource';\r\n } else if (isResourceCollection(object)) {\r\n return 'ResourceCollection';\r\n } else if (isPagedResourceCollection(object)) {\r\n return 'PagedResourceCollection';\r\n } else {\r\n return 'Unknown';\r\n }\r\n}\r\n","export enum Stage {\r\n BEGIN = 'BEGIN',\r\n PREPARE_URL = 'PREPARE_URL',\r\n CHECK_PARAMS = 'CHECK_PARAMS',\r\n PREPARE_PARAMS = 'PREPARE_PARAMS',\r\n INIT_RESOURCE = 'INIT_RESOURCE',\r\n RESOLVE_VALUES = 'RESOLVE_VALUES',\r\n CACHE_PUT = 'CACHE_PUT',\r\n CACHE_GET = 'CACHE_GET',\r\n CACHE_EVICT = 'CACHE_EVICT',\r\n CACHE_EVICT_ALL = 'CACHE_EVICT_ALL',\r\n HTTP_REQUEST = 'HTTP_REQUEST',\r\n HTTP_RESPONSE = 'HTTP_RESPONSE',\r\n END = 'END'\r\n}\r\n","import { ConsoleLogger } from './console-logger';\r\nimport { Stage } from './stage.enum';\r\nimport { LibConfig } from '../config/lib-config';\r\nimport { capitalize, isEmpty, isNil, isObject, isString } from 'lodash-es';\r\nimport { RESOURCE_NAME_PROP } from '../model/declarations';\r\n\r\n/**\r\n * Simplify logger calls.\r\n */\r\n\r\n/* tslint:disable:no-string-literal */\r\nexport class StageLogger {\r\n\r\n public static resourceBeginLog(resource: object | string, method: string, params?: object): void {\r\n if (!LibConfig.getConfig().logs.verboseLogs && !LibConfig.getConfig().isProduction) {\r\n return;\r\n }\r\n const paramToLog = this.prepareParams(params);\r\n\r\n let resourceName;\r\n if (isString(resource)) {\r\n resourceName = resource;\r\n } else if (!isNil(resource)) {\r\n resourceName = RESOURCE_NAME_PROP in resource ? resource[RESOURCE_NAME_PROP] : 'EmbeddedResource';\r\n } else {\r\n resourceName = 'NOT_DEFINED_RESOURCE_NAME';\r\n }\r\n ConsoleLogger.resourcePrettyInfo(`${ capitalize(resourceName) } ${ method }`,\r\n `STAGE ${ Stage.BEGIN }`, paramToLog);\r\n }\r\n\r\n public static resourceEndLog(resource: object | string, method: string, params: object): void {\r\n if (!LibConfig.getConfig().logs.verboseLogs && !LibConfig.getConfig().isProduction) {\r\n return;\r\n }\r\n const paramToLog = this.prepareParams(params);\r\n\r\n let resourceName;\r\n if (isString(resource)) {\r\n resourceName = resource;\r\n } else {\r\n resourceName = RESOURCE_NAME_PROP in resource ? resource[RESOURCE_NAME_PROP] : 'EmbeddedResource';\r\n }\r\n\r\n ConsoleLogger.resourcePrettyInfo(`${ capitalize(resourceName) } ${ method }`,\r\n `STAGE ${ Stage.END }`, paramToLog);\r\n }\r\n\r\n public static stageLog(stage: Stage, params: object): void {\r\n if (!LibConfig.getConfig().logs.verboseLogs && !LibConfig.getConfig().isProduction) {\r\n return;\r\n }\r\n const paramToLog = this.prepareParams(params);\r\n\r\n ConsoleLogger.prettyInfo(`STAGE ${ stage }`, paramToLog);\r\n }\r\n\r\n public static stageErrorLog(stage: Stage, params: object): void {\r\n if (LibConfig.getConfig().isProduction) {\r\n return;\r\n }\r\n const paramToLog = this.prepareParams(params);\r\n\r\n ConsoleLogger.prettyError(`STAGE ${ stage }`, paramToLog);\r\n }\r\n\r\n public static stageWarnLog(stage: Stage, params: object): void {\r\n if (LibConfig.getConfig().isProduction) {\r\n return;\r\n }\r\n const paramToLog = this.prepareParams(params);\r\n\r\n ConsoleLogger.prettyWarn(`STAGE ${ stage }`, paramToLog);\r\n }\r\n\r\n private static prepareParams(params: object) {\r\n const paramToLog = {};\r\n if (isEmpty(params)) {\r\n return paramToLog;\r\n }\r\n for (const [key, value] of Object.entries(params)) {\r\n if (!params.hasOwnProperty(key)) {\r\n continue;\r\n }\r\n if (isObject(value)) {\r\n paramToLog[key] = JSON.stringify(value, null, 2);\r\n } else {\r\n paramToLog[key] = value;\r\n }\r\n }\r\n return paramToLog;\r\n }\r\n}\r\n","import { StageLogger } from '../logger/stage-logger';\r\nimport { Stage } from '../logger/stage.enum';\r\nimport { isArray, isEmpty, isFunction, isNil, isObject, isPlainObject, isString } from 'lodash-es';\r\nimport { RESOURCE_NAME_PROP } from '../model/declarations';\r\n\r\nexport class ValidationUtils {\r\n\r\n /**\r\n * Checks that passed object with params has all valid params.\r\n * Params should not has null, undefined, empty object, empty string values.\r\n *\r\n * @param params object with params to check\r\n * @throws error if any params are not defined\r\n */\r\n public static validateInputParams(params: object): void {\r\n if (isNil(params)) {\r\n const errMsg = 'Passed params object is not valid';\r\n StageLogger.stageErrorLog(Stage.CHECK_PARAMS, {error: errMsg});\r\n throw new Error(errMsg);\r\n }\r\n\r\n const notValidParams = [];\r\n for (const [key, value] of Object.entries(params)) {\r\n // tslint:disable-next-line:no-string-literal\r\n if (isFunction(value) && isFunction(value.constructor) && !value[RESOURCE_NAME_PROP]) {\r\n throw new Error(`Resource '${ value.name }' has not 'resourceName' value. Set it with @HateoasResource decorator on '${ value.name }' class.`);\r\n }\r\n\r\n if (isNil(value)\r\n || (isString(value) && !value)\r\n || (isPlainObject(value) && isEmpty(value))\r\n || (isArray(value) && value.length === 0)) {\r\n\r\n let formattedValue = value;\r\n if (isObject(value)) {\r\n formattedValue = JSON.stringify(value, null, 2);\r\n }\r\n notValidParams.push(`'${ key } = ${ formattedValue }'`);\r\n }\r\n }\r\n if (notValidParams.length > 0) {\r\n const errMsg = `Passed param(s) ${ notValidParams.join(', ') } ${ notValidParams.length > 1 ? 'are' : 'is' } not valid`;\r\n StageLogger.stageErrorLog(Stage.CHECK_PARAMS, {error: errMsg});\r\n throw new Error(errMsg);\r\n }\r\n }\r\n\r\n}\r\n","import { HttpParams } from '@angular/common/http';\r\nimport { isResource } from '../model/resource-type';\r\nimport { Resource } from '../model/resource/resource';\r\nimport { GetOption, HttpClientOptions, LinkData, PagedGetOption, Sort } from '../model/declarations';\r\nimport { ValidationUtils } from './validation.utils';\r\nimport { LibConfig } from '../config/lib-config';\r\nimport { isArray, isEmpty, isNil, isObject, toString } from 'lodash-es';\r\nimport { UriTemplate } from 'uri-templates-es';\r\nimport { MultipleResourceRoutes, ResourceRoute } from '../config/hateoas-configuration.interface';\r\nimport { ConsoleLogger } from '../logger/console-logger';\r\n\r\nexport class UrlUtils {\r\n\r\n /**\r\n * Convert passed params to the {@link HttpParams}.\r\n *\r\n * @param options which need to convert\r\n * @param httpParams (optional) if passed then will be applied to this one, otherwise created a new one\r\n */\r\n public static convertToHttpParams(options: PagedGetOption, httpParams?: HttpParams): HttpParams {\r\n let resultParams = httpParams ? httpParams : new HttpParams();\r\n if (isEmpty(options) || isNil(options)) {\r\n return resultParams;\r\n }\r\n UrlUtils.checkDuplicateParams(options);\r\n\r\n if (isObject(options.params) && !isEmpty(options.params)) {\r\n for (const [key, value] of Object.entries(options.params)) {\r\n if (options.params.hasOwnProperty(key)) {\r\n if (isResource(value)) {\r\n // Append resource as resource link\r\n resultParams = resultParams.append(key, (value as Resource).getSelfLinkHref());\r\n } else if (isArray(options.params[key])) {\r\n // Append arrays params as repeated key with each value from array\r\n (options.params[key] as Array<any>).forEach((item) => {\r\n resultParams = resultParams.append(`${ key.toString() }`, item);\r\n });\r\n } else {\r\n // Else append simple param as is\r\n resultParams = resultParams.append(key, value.toString());\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (!isEmpty(options.pageParams)) {\r\n resultParams = resultParams.append('page', toString(options.pageParams.page));\r\n resultParams = resultParams.append('size', toString(options.pageParams.size));\r\n }\r\n if (!isEmpty(options.sort)) {\r\n resultParams = UrlUtils.generateSortParams(options.sort, resultParams);\r\n }\r\n\r\n return resultParams;\r\n }\r\n\r\n /**\r\n * Convert ngx-hateoas-client option to Angular HttpClient.\r\n * @param options ngx-hateoas-client options\r\n */\r\n public static convertToHttpOptions(options: PagedGetOption): HttpClientOptions {\r\n if (isEmpty(options) || isNil(options)) {\r\n return {};\r\n }\r\n\r\n return {\r\n params: UrlUtils.convertToHttpParams(options),\r\n headers: options.headers,\r\n observe: options.observe,\r\n reportProgress: options.reportProgress,\r\n withCredentials: options.withCredentials,\r\n };\r\n }\r\n\r\n /**\r\n * Generate link url.\r\n * If proxyUrl is not empty then relation url will be use proxy.\r\n *\r\n * @param relationLink resource link to which need to generate the url\r\n * @param options (optional) additional options that should be applied to the request\r\n * @throws error when required params are not valid\r\n */\r\n public static generateLinkUrl(relationLink: LinkData, options?: PagedGetOption): string {\r\n ValidationUtils.validateInputParams({relationLink, linkUrl: relationLink?.href});\r\n let url;\r\n if (options && !isEmpty(options)) {\r\n url = relationLink.templated ? UrlUtils.fillTemplateParams(relationLink.href, options) : relationLink.href;\r\n } else {\r\n url = relationLink.templated ? UrlUtils.removeTemplateParams(relationLink.href) : relationLink.href;\r\n }\r\n\r\n const route = UrlUtils.guessResourceRoute(url);\r\n if (route.proxyUrl) {\r\n return url.replace(route.rootUrl, route.proxyUrl);\r\n }\r\n return url;\r\n }\r\n\r\n /**\r\n * Return server api url based on proxy url when it is not empty or root url otherwise.\r\n *\r\n * @param routeName resource route name that configured in {@link MultipleResourceRoutes}.\r\n */\r\n public static getApiUrl(routeName: string): string {\r\n const route = UrlUtils.getRouteByName(routeName);\r\n if (route.proxyUrl) {\r\n return route.proxyUrl;\r\n } else {\r\n return route.rootUrl;\r\n }\r\n }\r\n\r\n /**\r\n * Try to determine resource route by passed resource url.\r\n * @param url resource url\r\n */\r\n public static guessResourceRoute(url: string): ResourceRoute {\r\n let resourceRoute: ResourceRoute;\r\n for (const [routeName] of Object.entries(UrlUtils.getRoutes())) {\r\n const route = UrlUtils.getRouteByName(routeName);\r\n const lowerCaseUrl = url.toLowerCase();\r\n if (lowerCaseUrl.includes(route.rootUrl.toLowerCase())\r\n || (!isEmpty(route.proxyUrl) && lowerCaseUrl.includes(route.proxyUrl.toLowerCase()))) {\r\n resourceRoute = route;\r\n break;\r\n }\r\n }\r\n\r\n if (isEmpty(resourceRoute)) {\r\n throw new Error(`Failed to determine resource route by url: ${ url }`);\r\n }\r\n\r\n return resourceRoute;\r\n }\r\n\r\n /**\r\n * Generate url use base and the resource name.\r\n *\r\n * @param baseUrl will be as first part as a result url\r\n * @param resourceName added to the base url through slash\r\n * @param query (optional) if passed then adds to end of the url\r\n * @throws error when required params are not valid\r\n */\r\n public static generateResourceUrl(baseUrl: string, resourceName: string, query?: string): string {\r\n ValidationUtils.validateInputParams({baseUrl, resourceName});\r\n\r\n let url = baseUrl;\r\n if (!url.endsWith('/')) {\r\n url = url.concat('/');\r\n }\r\n return url.concat(resourceName).concat(query ? `${ query.startsWith('/') ? query : '/' + query }` : '');\r\n }\r\n\r\n /**\r\n * Retrieve a resource name from resource url.\r\n *\r\n * @param url resource url\r\n */\r\n public static getResourceNameFromUrl(url: string): string {\r\n ValidationUtils.validateInputParams({url});\r\n const lowerCaseUrl = url.toLowerCase();\r\n const resourceRoute = UrlUtils.guessResourceRoute(url);\r\n\r\n let baseUrl;\r\n if (lowerCaseUrl.includes(resourceRoute.rootUrl)) {\r\n baseUrl = resourceRoute.rootUrl;\r\n } else if (!isEmpty(resourceRoute.proxyUrl) && lowerCaseUrl.includes(resourceRoute.proxyUrl)) {\r\n baseUrl = resourceRoute.proxyUrl;\r\n } else {\r\n throw new Error(`Failed to determine resource name from url: ${ url }, found resource route ${ JSON.stringify(resourceRoute) }`);\r\n }\r\n\r\n if (!baseUrl.endsWith('/')) {\r\n baseUrl = baseUrl.concat('/');\r\n }\r\n\r\n return url.toLowerCase().replace(`${ baseUrl }`, '').split('/')[0];\r\n }\r\n\r\n /**\r\n * Clear url from template params.\r\n *\r\n * @param url to be cleaned\r\n * @throws error when required params are not valid\r\n */\r\n public static removeTemplateParams(url: string): string {\r\n ValidationUtils.validateInputParams({url});\r\n\r\n return UrlUtils.fillTemplateParams(url, {});\r\n }\r\n\r\n /**\r\n * Clear all url params.\r\n *\r\n * @param url to clear params\r\n * @throws error when required params are not valid\r\n */\r\n public static clearUrlParams(url: string): string {\r\n ValidationUtils.validateInputParams({url});\r\n const srcUrl = new URL(url);\r\n\r\n return srcUrl.origin + srcUrl.pathname;\r\n }\r\n\r\n /**\r\n * Fill url template params.\r\n *\r\n * @param url to be filled\r\n * @param options contains params to apply to result url, if empty then template params will be cleared\r\n * @throws error when required params are not valid\r\n */\r\n public static fillTemplateParams(url: string, options: PagedGetOption): string {\r\n ValidationUtils.validateInputParams({url});\r\n UrlUtils.checkDuplicateParams(options);\r\n\r\n /*\r\n Sort params will be applied through Http request params not with template params\r\n because often sort params is not present in template params then sort params need to put through Http request params.\r\n But when sort params present in template params we need to avoid duplication sort params from Http request params\r\n and template params therefore we need to add it in one place.\r\n */\r\n const paramsWithoutSortParam = {\r\n ...options,\r\n ...options?.params,\r\n ...options?.pageParams,\r\n };\r\n return new UriTemplate(url).fill(isNil(paramsWithoutSortParam) ? {} : paramsWithoutSortParam);\r\n }\r\n\r\n public static fillDefaultPageDataIfNoPresent(options: PagedGetOption) {\r\n const pagedOptions = !isEmpty(options) ? options : {};\r\n if (isEmpty(pagedOptions.pageParams)) {\r\n pagedOptions.pageParams = LibConfig.getConfig().pagination.defaultPage;\r\n } else if (!pagedOptions.pageParams.size) {\r\n pagedOptions.pageParams.size = LibConfig.getConfig().pagination.defaultPage.size;\r\n } else if (!pagedOptions.pageParams.page) {\r\n pagedOptions.pageParams.page = LibConfig.getConfig().pagination.defaultPage.page;\r\n }\r\n\r\n return pagedOptions;\r\n }\r\n\r\n private static generateSortParams(sort: Sort, httpParams?: HttpParams): HttpParams {\r\n let resultParams = httpParams ? httpParams : new HttpParams();\r\n if (!isEmpty(sort)) {\r\n for (const [sortPath, sortOrder] of Object.entries(sort)) {\r\n resultParams = resultParams.append('sort', `${ sortPath },${ sortOrder }`);\r\n }\r\n }\r\n\r\n return resultParams;\r\n }\r\n\r\n private static checkDuplicateParams(options: GetOption): void {\r\n if (isEmpty(options) || isEmpty(options.params)) {\r\n return;\r\n }\r\n if ('page' in options.params || 'size' in options.params) {\r\n throw Error('Please, pass page params in page object key, not with params object!');\r\n }\r\n }\r\n\r\n public static getRouteByName(routeName: string): ResourceRoute {\r\n const route = LibConfig.getConfig().http[routeName];\r\n if (isEmpty(route)) {\r\n ConsoleLogger.error(`No Resource route found by name: '${ routeName }'. Check you configuration. Read more about this ...`, {\r\n availableRoutes: UrlUtils.getRoutes()\r\n });\r\n throw Error(`No Resource route found by name: '${ routeName }'.`);\r\n }\r\n\r\n return route;\r\n }\r\n\r\n public static getRoutes(): MultipleResourceRoutes {\r\n return LibConfig.getConfig().http as MultipleResourceRoutes;\r\n }\r\n\r\n}\r\n","import { BaseResource } from '../model/resource/base-resource';\r\nimport { isEmbeddedResource, isResource } from '../model/resource-type';\r\nimport { ResourceCollection } from '../model/resource/resource-collection';\r\nimport { PagedResourceCollection } from '../model/resource/paged-resource-collection';\r\nimport { GetOption, Include, Link, PageData, RequestBody } from '../model/declarations';\r\nimport { Resource } from '../model/resource/resource';\r\nimport { EmbeddedResource } from '../model/resource/embedded-resource';\r\nimport { UrlUtils } from './url.utils';\r\nimport { Stage } from '../logger/stage.enum';\r\nimport { StageLogger } from '../logger/stage-logger';\r\nimport { includes, isArray, isEmpty, isNil, isObject, isPlainObject } from 'lodash-es';\r\nimport { ConsoleLogger } from '../logger/console-logger';\r\nimport { LibConfig } from '../config/lib-config';\r\nimport { isMatch, parse } from 'date-fns';\r\n\r\n/* tslint:disable:no-string-literal */\r\nexport class ResourceUtils {\r\n\r\n public static RESOURCE_NAME_TYPE_MAP: Map<string, any> = new Map<string, any>();\r\n public static RESOURCE_NAME_PROJECTION_TYPE_MAP: Map<string, any> = new Map<string, any>();\r\n public static RESOURCE_PROJECTION_REL_NAME_TYPE_MAP: Map<string, any> = new Map<string, any>();\r\n public static EMBEDDED_RESOURCE_TYPE_MAP: Map<string, any> = new Map<string, any>();\r\n\r\n private static resourceType: new() => BaseResource;\r\n\r\n private static resourceCollectionType: new() => ResourceCollection<BaseResource>;\r\n\r\n private static pagedResourceCollectionType: new(collection: ResourceCollection<BaseResource>, pageData?: PageData)\r\n => PagedResourceCollection<BaseResource>;\r\n\r\n private static embeddedResourceType: new() => EmbeddedResource;\r\n\r\n public static useResourceType(type: new () => Resource) {\r\n this.resourceType = type;\r\n }\r\n\r\n public static useResourceCollectionType(type: new() => ResourceCollection<BaseResource>) {\r\n this.resourceCollectionType = type;\r\n }\r\n\r\n public static usePagedResourceCollectionType(type: new(collection: ResourceCollection<BaseResource>)\r\n => PagedResourceCollection<BaseResource>) {\r\n this.pagedResourceCollectionType = type;\r\n }\r\n\r\n public static useEmbeddedResourceType(type: new() => EmbeddedResource) {\r\n this.embeddedResourceType = type;\r\n }\r\n\r\n public static instantiateResource<T extends BaseResource>(payload: object, isProjection?: boolean): T {\r\n // @ts-ignore\r\n if (isEmpty(payload)\r\n || (!isObject(payload['_links']) || isEmpty(payload['_links']))) {\r\n ConsoleLogger.warn('Incorrect resource object! Returned \\'null\\' value, because it has not \\'_links\\' array. Check that server send right resource object.', {incorrectResource: payload});\r\n return null;\r\n }\r\n\r\n return this.createResource(this.resolvePayloadProperties(payload, isProjection), isProjection);\r\n }\r\n\r\n private static resolvePayloadProperties<T extends BaseResource>(payload: object, isProjection?: boolean): object {\r\n for (const key of Object.keys(payload)) {\r\n if (key === 'hibernateLazyInitializer') {\r\n delete payload[key];\r\n continue;\r\n }\r\n if (key === '_links') {\r\n payload[key] = payload[key];\r\n continue;\r\n }\r\n\r\n if (key === '_embedded' && isObject(payload[key])) {\r\n payload = {\r\n ...payload,\r\n ...this.resolvePayloadProperties(payload[key], isProjection)\r\n };\r\n delete payload['_embedded'];\r\n\r\n continue;\r\n }\r\n\r\n if (LibConfig.getConfig()?.typesFormat?.date?.patterns && !isEmpty(LibConfig.getConfig().typesFormat.date.patterns)) {\r\n for (const pattern of LibConfig.getConfig().typesFormat.date.patterns) {\r\n if (isMatch(payload[key], pattern)) {\r\n const valueAsDate = parse(payload[key], pattern, new Date());\r\n if (valueAsDate) {\r\n payload[key] = valueAsDate;\r\n break;\r\n }\r\n }\r\n }\r\n if (payload[key] instanceof Date) {\r\n continue;\r\n }\r\n }\r\n\r\n payload[key] = this.resolvePayloadType(key, payload[key], isProjection);\r\n }\r\n\r\n return payload;\r\n }\r\n\r\n private static resolvePayloadType<T extends BaseResource>(key: string, payload: object, isProjection?: boolean): object {\r\n if (isNil(payload)) {\r\n return payload;\r\n } else if (isArray(payload)) {\r\n for (let i = 0; i < payload.length; i++) {\r\n payload[i] = this.resolvePayloadType(key, payload[i], isProjection);\r\n }\r\n } else if (isProjection && isPlainObject(payload)) {\r\n // Need to check resource projection relation props because some inner props can be objects that can be also resources\r\n payload = this.resolvePayloadProperties(this.createResourceProjectionRel(key, payload), isProjection);\r\n } else if (isEmbeddedResource(payload) || ResourceUtils.EMBEDDED_RESOURCE_TYPE_MAP.get(key)) {\r\n // Need to check embedded resource props because some inner props can be objects that can be also resources\r\n payload = this.resolvePayloadProperties(this.createEmbeddedResource(key, payload), isProjection);\r\n } else if (isResource(payload)) {\r\n // Need to check resource props because some inner props can be objects that can be also resources\r\n payload = this.resolvePayloadProperties(this.createResource(payload), isProjection);\r\n }\r\n\r\n return payload;\r\n }\r\n\r\n private static createResource<T extends BaseResource>(payload: any, isProjection?: boolean): T {\r\n const resourceName = this.findResourceName(payload);\r\n let resourceClass;\r\n if (isProjection && !ResourceUtils.RESOURCE_NAME_PROJECTION_TYPE_MAP.get(resourceName)) {\r\n resourceClass = ResourceUtils.RESOURCE_NAME_TYPE_MAP.get(resourceName);\r\n ConsoleLogger.prettyWarn('Not found projection resource type when create resource projection: \\'' + resourceName + '\\' so used resource type: \\'' + (resourceClass ? resourceClass?.name : ' default Resource') + '\\'. \\n\\r' +\r\n 'It can be when you pass projection param as http request directly instead use projection type with @HateoasProjection.\\n\\r' +\r\n '\\n\\rSee more how to use @HateoasProjection here https://github.com/lagoshny/ngx-hateoas-client#resource-projection-support.');\r\n } else {\r\n resourceClass = isProjection\r\n ? ResourceUtils.RESOURCE_NAME_PROJECTION_TYPE_MAP.get(resourceName)\r\n : ResourceUtils.RESOURCE_NAME_TYPE_MAP.get(resourceName);\r\n }\r\n\r\n if (resourceClass) {\r\n return Object.assign(new (resourceClass)() as T, payload);\r\n } else {\r\n ConsoleLogger.prettyWarn('Not found resource type when create resource: \\'' + resourceName + '\\' so used default Resource type, for this can be some reasons: \\n\\r' +\r\n '1) You did not pass resource property name as \\'' + resourceName + '\\' with @HateoasResource decorator. \\n\\r' +\r\n '2) You did not declare resource type in configuration \"configuration.useTypes.resources\". \\n\\r' +\r\n '\\n\\rSee more about declare resource types here: https://github.com/lagoshny/ngx-hateoas-client#usetypes-params..');\r\n\r\n return Object.assign(new this.resourceType(), payload);\r\n }\r\n }\r\n\r\n private static createResourceProjectionRel<T extends Resource>(relationName: string, payload: any): T {\r\n const relationClass = ResourceUtils.RESOURCE_PROJECTION_REL_NAME_TYPE_MAP.get(relationName);\r\n if (relationClass) {\r\n return Object.assign(new (relationClass)() as T, payload);\r\n } else {\r\n ConsoleLogger.prettyWarn('Not found resource relation type when create relation: \\'' + relationName + '\\' so used default Resource type, for this can be some reasons: \\n\\r' +\r\n 'You did not pass relation type property with @ProjectionRel decorator on relation property \\'' + relationName + '\\'. \\n\\r' +\r\n '\\n\\rSee more how to use @ProjectionRel here https://github.com/lagoshny/ngx-hateoas-client#resource-projection-support.');\r\n\r\n return Object.assign(new this.resourceType(), payload);\r\n }\r\n }\r\n\r\n private static createEmbeddedResource<T extends BaseResource>(key: string, payload: any): T {\r\n const resourceClass = ResourceUtils.EMBEDDED_RESOURCE_TYPE_MAP.get(key);\r\n if (resourceClass) {\r\n return Object.assign(new (resourceClass)() as T, payload);\r\n } else {\r\n ConsoleLogger.prettyWarn('Not found embedded resource type when create resource: \\'' + key + '\\' so used default EmbeddedResource type, for this can be some reasons:. \\n\\r' +\r\n '1) You did not pass embedded resource property name as \\'' + key + '\\' with @HateoasEmbeddedResource decorator. \\n\\r' +\r\n '2) You did not declare embedded resource type in configuration \"configuration.useTypes.embeddedResources\". \\n\\r' +\r\n '\\n\\r See more about declare resource types here: https://github.com/lagoshny/ngx-hateoas-client#usetypes-params.');\r\n\r\n return Object.assign(new this.embeddedResourceType(), payload);\r\n }\r\n }\r\n\r\n public static instantiateResourceCollection<T extends ResourceCollection<BaseResource>>(payload: object, isProjection?: boolean): T {\r\n if (isEmpty(payload)\r\n || (!isObject(payload['_links']) || isEmpty(payload['_links']))\r\n