ngx-hal
Version:
Angular library for supporting HAL format APIs
1 lines • 158 kB
Source Map (JSON)
{"version":3,"file":"ngx-hal.mjs","sources":["../../../projects/ngx-hal/src/lib/ngx-hal.module.ts","../../../projects/ngx-hal/src/lib/constants/metadata.constant.ts","../../../projects/ngx-hal/src/lib/interfaces/network-config.interface.ts","../../../projects/ngx-hal/src/lib/utils/deepmerge-wrapper.ts","../../../projects/ngx-hal/src/lib/helpers/metadata/metadata.helper.ts","../../../projects/ngx-hal/src/lib/decorators/datastore-config.decorator.ts","../../../projects/ngx-hal/src/lib/decorators/model-service-config.decorator.ts","../../../projects/ngx-hal/src/lib/interfaces/model-options.interface.ts","../../../projects/ngx-hal/src/lib/decorators/model-config.decorator.ts","../../../projects/ngx-hal/src/lib/enums/model-property.enum.ts","../../../projects/ngx-hal/src/lib/helpers/update-property-metadata/update-property-metadata.helper.ts","../../../projects/ngx-hal/src/lib/interfaces/attribute-options.interface.ts","../../../projects/ngx-hal/src/lib/decorators/attribute.decorator.ts","../../../projects/ngx-hal/src/lib/interfaces/header-attribute-options.interface.ts","../../../projects/ngx-hal/src/lib/decorators/header-attribute.decorator.ts","../../../projects/ngx-hal/src/lib/interfaces/has-many-options.interface.ts","../../../projects/ngx-hal/src/lib/decorators/has-many.decorator.ts","../../../projects/ngx-hal/src/lib/interfaces/has-one-options.interface.ts","../../../projects/ngx-hal/src/lib/decorators/has-one.decorator.ts","../../../projects/ngx-hal/src/lib/decorators/link.decorator.ts","../../../projects/ngx-hal/src/lib/constants/hal.constant.ts","../../../projects/ngx-hal/src/lib/constants/general.constant.ts","../../../projects/ngx-hal/src/lib/utils/is-array/is-array.util.ts","../../../projects/ngx-hal/src/lib/helpers/uuid/uuid.helper.ts","../../../projects/ngx-hal/src/lib/classes/hal-document.ts","../../../projects/ngx-hal/src/lib/utils/get-response-headers/get-response-header.util.ts","../../../projects/ngx-hal/src/lib/helpers/is-hal-model-instance.ts/is-hal-model-instance.helper.ts","../../../projects/ngx-hal/src/lib/utils/ensure-relationship-descriptors/ensure-relationship-descriptors.util.ts","../../../projects/ngx-hal/src/lib/utils/remove-query-params/remove-query-params.util.ts","../../../projects/ngx-hal/src/lib/utils/set-request-header/set-request-header.util.ts","../../../projects/ngx-hal/src/lib/utils/is-string/is-string.util.ts","../../../projects/ngx-hal/src/lib/models/simple-hal.model.ts","../../../projects/ngx-hal/src/lib/helpers/is-simple-hal-model-instance.ts/is-simple-hal-model-instance.helper.ts","../../../projects/ngx-hal/src/lib/helpers/is-function/is-function.helper.ts","../../../projects/ngx-hal/src/lib/models/hal.model.ts","../../../projects/ngx-hal/src/lib/classes/pagination.ts","../../../projects/ngx-hal/src/lib/classes/hal-storage/hal-storage.ts","../../../projects/ngx-hal/src/lib/classes/hal-storage/etag-hal-storage.ts","../../../projects/ngx-hal/src/lib/classes/hal-storage/simple-hal-storage.ts","../../../projects/ngx-hal/src/lib/enums/cache-strategy.enum.ts","../../../projects/ngx-hal/src/lib/interfaces/model-service-options.interface.ts","../../../projects/ngx-hal/src/lib/constants/request.constant.ts","../../../projects/ngx-hal/src/lib/classes/hal-storage/hal-storage-factory.ts","../../../projects/ngx-hal/src/lib/helpers/make-query-params-string/make-query-params-string.helper.ts","../../../projects/ngx-hal/src/lib/utils/get-query-params/get-query-params.util.ts","../../../projects/ngx-hal/src/lib/helpers/make-http-params/make-http-params.helper.ts","../../../projects/ngx-hal/src/lib/helpers/populate-templated-url/populate-templated-url.helper.ts","../../../projects/ngx-hal/src/lib/services/datastore/datastore.service.ts","../../../projects/ngx-hal/src/lib/services/model-service/model.service.ts","../../../projects/ngx-hal/src/lib/helpers/array-attribute-class/array-attribute-class.ts","../../../projects/ngx-hal/src/ngx-hal.ts"],"sourcesContent":["import { NgModule } from '@angular/core';\n\n@NgModule({\n\tdeclarations: [],\n\timports: [],\n\texports: [],\n})\nexport class NgxHalModule {}\n","export const ATTRIBUTE_PROPERTIES_METADATA_KEY = 'attributeProperties';\nexport const HEADER_ATTRIBUTE_PROPERTIES_METADATA_KEY = 'headerAttributeProperties';\nexport const HAS_MANY_PROPERTIES_METADATA_KEY = 'hasManyProperties';\nexport const HAS_ONE_PROPERTIES_METADATA_KEY = 'hasOneProperties';\nexport const LINK_PROPERTIES_METADATA_KEY = 'linkProperties';\nexport const HAL_DATASTORE_DOCUMENT_CLASS_METADATA_KEY = 'halDatastoreDocumentClass';\nexport const HAL_MODEL_DOCUMENT_CLASS_METADATA_KEY = 'halModelDocumentClass';\n","import { RequestOptions } from '../types/request-options.type';\n\nexport interface NetworkConfig {\n\tbaseUrl?: string;\n\tendpoint?: string;\n\tglobalRequestOptions?: RequestOptions;\n}\n\nexport const DEFAULT_NETWORK_CONFIG: NetworkConfig = {\n\tbaseUrl: '/',\n\tendpoint: '',\n\tglobalRequestOptions: {},\n};\n","import deepmerge from 'deepmerge';\n\nexport function deepmergeWrapper<T = { [K: string]: any }>(...args): T {\n\tconst ensuredArgs = args.map((arg: any) => arg || {});\n\treturn deepmerge.all(ensuredArgs) as unknown as T;\n}\n","export function getObjProperty(obj, propertyKey: string, defaultValue: any = []) {\n\tconst objClass = getClass(obj);\n\n\tif (!Object.prototype.hasOwnProperty.call(objClass, propertyKey)) {\n\t\tsetObjProperty(objClass, propertyKey, defaultValue);\n\t}\n\n\tconst x = objClass[propertyKey];\n\treturn x;\n}\n\nexport function setObjProperty(objClass, propertyKey, value) {\n\tObject.defineProperty(objClass, propertyKey, {\n\t\tconfigurable: true,\n\t\tenumerable: false,\n\t\tvalue,\n\t});\n}\n\n// Returns a new array, therefore, pushing into it won't affect the class metadata\nexport function getArrayObjProperty<T>(obj, propertyKey: string): Array<T> {\n\tconst objClass = getClass(obj);\n\tconst parentClass = Object.getPrototypeOf(objClass);\n\tconst isTopLevelClass = parentClass === Object.getPrototypeOf(Function);\n\n\tlet parentMeta: Array<T> = [];\n\n\tif (!isTopLevelClass) {\n\t\tparentMeta = getArrayObjProperty(parentClass, propertyKey);\n\t}\n\tconst meta: Array<T> = getObjProperty(obj, propertyKey);\n\tconst finalMeta = [].concat(meta, parentMeta);\n\n\treturn finalMeta;\n}\n\nfunction getClass<T>(obj: T): T {\n\treturn (typeof obj === 'function' ? obj : obj.constructor) as unknown as T;\n}\n","import { DatastoreOptions } from '../interfaces/datastore-options.interface';\nimport { HAL_DATASTORE_DOCUMENT_CLASS_METADATA_KEY } from '../constants/metadata.constant';\nimport { DEFAULT_NETWORK_CONFIG, NetworkConfig } from '../interfaces/network-config.interface';\nimport { deepmergeWrapper } from '../utils/deepmerge-wrapper';\nimport { setObjProperty } from '../helpers/metadata/metadata.helper';\n\nexport function DatastoreConfig(config: DatastoreOptions) {\n\treturn function (target: any) {\n\t\tconst networkConfig = deepmergeWrapper<NetworkConfig>(\n\t\t\tDEFAULT_NETWORK_CONFIG,\n\t\t\tconfig.network || {},\n\t\t);\n\t\tObject.defineProperty(target.prototype, 'paginationClass', {\n\t\t\tvalue: config.paginationClass,\n\t\t});\n\t\tObject.defineProperty(target.prototype, '_cacheStrategy', {\n\t\t\tvalue: config.cacheStrategy,\n\t\t});\n\t\tObject.defineProperty(target.prototype, '_storage', {\n\t\t\tvalue: config.storage,\n\t\t});\n\t\tObject.defineProperty(target.prototype, 'networkConfig', {\n\t\t\tvalue: networkConfig,\n\t\t\twritable: true,\n\t\t});\n\t\tsetObjProperty(target, HAL_DATASTORE_DOCUMENT_CLASS_METADATA_KEY, config.halDocumentClass);\n\t\treturn target;\n\t};\n}\n","import { ModelServiceOptions } from '../interfaces/model-service-options.interface';\n\nexport function ModelServiceConfig(config: ModelServiceOptions) {\n\treturn function (target: any) {\n\t\treturn target;\n\t};\n}\n","import { HalDocumentConstructor } from '../types/hal-document-construtor.type';\nimport { HalModel } from '../models/hal.model';\nimport { NetworkConfig } from './network-config.interface';\n\nexport class ModelOptions {\n\ttype: string;\n\tendpoint?: string;\n\thalDocumentClass?: HalDocumentConstructor<HalModel>;\n\tnetworkConfig?: NetworkConfig;\n}\n\nexport const DEFAULT_MODEL_OPTIONS: ModelOptions = {\n\ttype: '',\n};\n\nexport const DEFAULT_MODEL_TYPE = '__DEFAULT_MODEL_TYPE__';\n","import { ModelOptions, DEFAULT_MODEL_OPTIONS } from '../interfaces/model-options.interface';\nimport { HAL_MODEL_DOCUMENT_CLASS_METADATA_KEY } from '../constants/metadata.constant';\nimport { deepmergeWrapper } from '../utils/deepmerge-wrapper';\nimport { setObjProperty } from '../helpers/metadata/metadata.helper';\n\nexport function ModelConfig(config: ModelOptions) {\n\treturn function (target: any) {\n\t\tconst configValue = deepmergeWrapper<ModelOptions>(DEFAULT_MODEL_OPTIONS, config);\n\t\tObject.defineProperty(target.prototype, 'config', {\n\t\t\tvalue: configValue,\n\t\t\twritable: true,\n\t\t});\n\t\tsetObjProperty(target, HAL_MODEL_DOCUMENT_CLASS_METADATA_KEY, config.halDocumentClass);\n\t\treturn target;\n\t};\n}\n","export enum ModelProperty {\n\tAttribute = 'Attribute',\n\tHasMany = 'HasMany',\n\tHasOne = 'HasOne',\n\tHeaderAttribute = 'HeaderAttribute',\n\tLink = 'Link',\n}\n","import { ModelProperty } from '../../interfaces/model-property.interface';\n\n// Modifies the original array\nexport function updatePropertyMetadata<T extends ModelProperty>(\n\tmodelProperties: Array<T>,\n\tnewModelProperty: T,\n): Array<T> {\n\tconst existingProperty: T = modelProperties.find((property: T) => {\n\t\treturn property.name === newModelProperty.name;\n\t});\n\n\tif (existingProperty) {\n\t\tconst indexOfExistingProperty: number = modelProperties.indexOf(existingProperty);\n\t\tmodelProperties[indexOfExistingProperty] = newModelProperty;\n\t} else {\n\t\tmodelProperties.push(newModelProperty);\n\t}\n\n\treturn modelProperties;\n}\n","import { ModelConstructor, ModelConstructorFn } from '../types/model-constructor.type';\n\nexport interface AttributeOptions {\n\tuseClass?: string | ModelConstructor<any> | ModelConstructorFn<any>;\n\ttransformResponseValue?: (rawAttribute: any) => any;\n\ttransformBeforeSave?: (raw: any) => any;\n\texternalName?: string;\n\texcludeFromPayload?: boolean;\n}\n\nexport const DEFAULT_ATTRIBUTE_OPTIONS = {\n\texcludeFromPayload: false,\n\tuseClass: false,\n};\n","import { ATTRIBUTE_PROPERTIES_METADATA_KEY } from '../constants/metadata.constant';\nimport { ModelProperty as ModelPropertyEnum } from '../enums/model-property.enum';\nimport { getObjProperty } from '../helpers/metadata/metadata.helper';\nimport { updatePropertyMetadata } from '../helpers/update-property-metadata/update-property-metadata.helper';\nimport {\n\tAttributeOptions,\n\tDEFAULT_ATTRIBUTE_OPTIONS,\n} from '../interfaces/attribute-options.interface';\nimport { AttributeModelProperty } from '../interfaces/model-property.interface';\nimport { HalModel } from '../models/hal.model';\nimport { deepmergeWrapper } from '../utils/deepmerge-wrapper';\n\nexport function Attribute(options: AttributeOptions = {}) {\n\treturn (model: HalModel, propertyName: string) => {\n\t\tconst attributeOptions: AttributeOptions = deepmergeWrapper(DEFAULT_ATTRIBUTE_OPTIONS, options);\n\t\tconst existingAttributeProperties: Array<AttributeModelProperty> = getObjProperty(\n\t\t\tmodel,\n\t\t\tATTRIBUTE_PROPERTIES_METADATA_KEY,\n\t\t\t[],\n\t\t);\n\n\t\tconst attributeProperty: AttributeModelProperty = {\n\t\t\ttype: ModelPropertyEnum.Attribute,\n\t\t\ttransformResponseValue: attributeOptions.transformResponseValue,\n\t\t\ttransformBeforeSave: attributeOptions.transformBeforeSave,\n\t\t\tname: propertyName,\n\t\t\texternalName: options.externalName || propertyName,\n\t\t\texcludeFromPayload: options.excludeFromPayload,\n\t\t};\n\n\t\tif (attributeOptions.useClass) {\n\t\t\tattributeProperty.propertyClass = attributeOptions.useClass;\n\t\t}\n\n\t\tupdatePropertyMetadata(existingAttributeProperties, attributeProperty);\n\t};\n}\n","import { ModelConstructor, ModelConstructorFn } from '../types/model-constructor.type';\n\nexport interface HeaderAttributeOptions {\n\tuseClass?: boolean | ModelConstructor<any> | ModelConstructorFn<any>;\n\ttransformResponseValue?: (rawAttribute: any) => any;\n\ttransformBeforeSave?: (raw: any) => any;\n\texternalName?: string;\n}\n\nexport const DEFAULT_HEADER_ATTRIBUTE_OPTIONS = {\n\tuseClass: false,\n};\n","import { HEADER_ATTRIBUTE_PROPERTIES_METADATA_KEY } from '../constants/metadata.constant';\nimport { ModelProperty as ModelPropertyEnum } from '../enums/model-property.enum';\nimport { getObjProperty } from '../helpers/metadata/metadata.helper';\nimport { updatePropertyMetadata } from '../helpers/update-property-metadata/update-property-metadata.helper';\nimport {\n\tDEFAULT_HEADER_ATTRIBUTE_OPTIONS,\n\tHeaderAttributeOptions,\n} from '../interfaces/header-attribute-options.interface';\nimport {\n\tAttributeModelProperty,\n\tHeaderAttributeModelProperty,\n} from '../interfaces/model-property.interface';\nimport { HalModel } from '../models/hal.model';\nimport { deepmergeWrapper } from '../utils/deepmerge-wrapper';\n\nexport function HeaderAttribute(options: HeaderAttributeOptions = {}) {\n\treturn (model: HalModel, propertyName: string) => {\n\t\tconst headerAttributeOptions: HeaderAttributeOptions = deepmergeWrapper(\n\t\t\tDEFAULT_HEADER_ATTRIBUTE_OPTIONS,\n\t\t\toptions,\n\t\t);\n\n\t\tconst existingHeaderAttributeProperties: Array<AttributeModelProperty> = getObjProperty(\n\t\t\tmodel,\n\t\t\tHEADER_ATTRIBUTE_PROPERTIES_METADATA_KEY,\n\t\t\t[],\n\t\t);\n\n\t\tconst attributeProperty: HeaderAttributeModelProperty = {\n\t\t\ttype: ModelPropertyEnum.HeaderAttribute,\n\t\t\ttransformResponseValue: headerAttributeOptions.transformResponseValue,\n\t\t\ttransformBeforeSave: headerAttributeOptions.transformBeforeSave,\n\t\t\tname: propertyName,\n\t\t\texternalName: options.externalName || propertyName,\n\t\t};\n\n\t\tif (headerAttributeOptions.useClass) {\n\t\t\tattributeProperty.propertyClass = headerAttributeOptions.useClass;\n\t\t}\n\n\t\tupdatePropertyMetadata(existingHeaderAttributeProperties, attributeProperty);\n\t};\n}\n","import { ModelConstructor, ModelConstructorFn } from '../types/model-constructor.type';\n\nexport interface HasManyOptions {\n\titemsType: string | ModelConstructor<any> | ModelConstructorFn<any>;\n\tincludeInPayload?: boolean;\n\texternalName?: string;\n}\n\nexport const DEFAULT_HAS_MANY_OPTIONS = {\n\tincludeInPayload: false,\n};\n","import { HAS_MANY_PROPERTIES_METADATA_KEY } from '../constants/metadata.constant';\nimport { ModelProperty } from '../enums/model-property.enum';\nimport { getObjProperty } from '../helpers/metadata/metadata.helper';\nimport { updatePropertyMetadata } from '../helpers/update-property-metadata/update-property-metadata.helper';\nimport { DEFAULT_HAS_MANY_OPTIONS, HasManyOptions } from '../interfaces/has-many-options.interface';\nimport { HasManyModelProperty } from '../interfaces/model-property.interface';\nimport { HalModel } from '../models/hal.model';\nimport { deepmergeWrapper } from '../utils/deepmerge-wrapper';\n\nexport function HasMany(options: HasManyOptions) {\n\treturn (model: HalModel, propertyName: string) => {\n\t\tconst hasManyOptions: HasManyOptions = deepmergeWrapper(DEFAULT_HAS_MANY_OPTIONS, options);\n\n\t\tconst existingHasManyProperties: Array<HasManyModelProperty> = getObjProperty(\n\t\t\tmodel,\n\t\t\tHAS_MANY_PROPERTIES_METADATA_KEY,\n\t\t\t[],\n\t\t);\n\n\t\tconst hasManyProperty: HasManyModelProperty = {\n\t\t\tincludeInPayload: hasManyOptions.includeInPayload,\n\t\t\tname: propertyName,\n\t\t\tpropertyClass: hasManyOptions.itemsType,\n\t\t\ttype: ModelProperty.HasMany,\n\t\t\texternalName: options.externalName || propertyName,\n\t\t};\n\n\t\tupdatePropertyMetadata(existingHasManyProperties, hasManyProperty);\n\t};\n}\n","import { ModelConstructor, ModelConstructorFn } from '../types/model-constructor.type';\n\nexport interface HasOneOptions {\n\texternalName?: string;\n\tincludeInPayload?: boolean;\n\tpropertyClass: string | ModelConstructor<any> | ModelConstructorFn<any>;\n}\n\nexport const DEFAULT_HAS_ONE_OPTIONS = {\n\tincludeInPayload: false,\n};\n","import { HAS_ONE_PROPERTIES_METADATA_KEY } from '../constants/metadata.constant';\nimport { ModelProperty } from '../enums/model-property.enum';\nimport { getObjProperty } from '../helpers/metadata/metadata.helper';\nimport { updatePropertyMetadata } from '../helpers/update-property-metadata/update-property-metadata.helper';\nimport { DEFAULT_HAS_ONE_OPTIONS, HasOneOptions } from '../interfaces/has-one-options.interface';\nimport { HasOneModelProperty } from '../interfaces/model-property.interface';\nimport { HalModel } from '../models/hal.model';\nimport { deepmergeWrapper } from '../utils/deepmerge-wrapper';\n\nexport function HasOne(options: HasOneOptions) {\n\treturn (model: HalModel, propertyName: string) => {\n\t\tconst hasOneOptions: HasOneOptions = deepmergeWrapper(DEFAULT_HAS_ONE_OPTIONS, options);\n\n\t\tconst existingHasOneProperties: Array<HasOneModelProperty> = getObjProperty(\n\t\t\tmodel,\n\t\t\tHAS_ONE_PROPERTIES_METADATA_KEY,\n\t\t\t[],\n\t\t);\n\n\t\tconst hasOneProperty: HasOneModelProperty = {\n\t\t\tincludeInPayload: hasOneOptions.includeInPayload,\n\t\t\tname: propertyName,\n\t\t\tpropertyClass: hasOneOptions.propertyClass,\n\t\t\ttype: ModelProperty.HasOne,\n\t\t\texternalName: options.externalName || propertyName,\n\t\t};\n\n\t\tupdatePropertyMetadata(existingHasOneProperties, hasOneProperty);\n\t};\n}\n","import { LINK_PROPERTIES_METADATA_KEY } from '../constants/metadata.constant';\nimport { ModelProperty } from '../enums/model-property.enum';\nimport { getObjProperty } from '../helpers/metadata/metadata.helper';\nimport { updatePropertyMetadata } from '../helpers/update-property-metadata/update-property-metadata.helper';\nimport { LinkRelationshipOptions } from '../interfaces/link-relationship-options.interface';\nimport { LinkProperty } from '../interfaces/model-property.interface';\nimport { HalModel } from '../models/hal.model';\n\nexport function Link(options: LinkRelationshipOptions = {}) {\n\treturn (model: HalModel, propertyName: string) => {\n\t\tconst existingLinkProperties: Array<LinkProperty> = getObjProperty(\n\t\t\tmodel,\n\t\t\tLINK_PROPERTIES_METADATA_KEY,\n\t\t\t[],\n\t\t);\n\n\t\tconst linkProperty: LinkProperty = {\n\t\t\tname: propertyName,\n\t\t\ttype: ModelProperty.Link,\n\t\t\texternalName: options.externalName || propertyName,\n\t\t};\n\n\t\tupdatePropertyMetadata(existingLinkProperties, linkProperty);\n\t};\n}\n","export const EMBEDDED_PROPERTY_NAME = '_embedded';\nexport const LINKS_PROPERTY_NAME = '_links';\nexport const SELF_PROPERTY_NAME = 'self';\n","export const LOCAL_MODEL_ID_PREFIX = 'local-MODEL-identificator';\nexport const LOCAL_DOCUMENT_ID_PREFIX = 'local-document-identificator';\n","export function isArray(item: any): boolean {\n\treturn Array.isArray(item);\n}\n","export function generateUUID() {\n\treturn `${Math.floor(Math.random() * 1e10)}-${Date.now()}`;\n}\n","import { HttpResponse } from '@angular/common/http';\nimport { Observable } from 'rxjs';\nimport { RawHalResource } from '../interfaces/raw-hal-resource.interface';\nimport {\n\tLINKS_PROPERTY_NAME,\n\tEMBEDDED_PROPERTY_NAME,\n\tSELF_PROPERTY_NAME,\n} from '../constants/hal.constant';\nimport { HalModel } from '../models/hal.model';\nimport { Pagination } from './pagination';\nimport { ModelConstructor } from '../types/model-constructor.type';\nimport { DatastoreService } from '../services/datastore/datastore.service';\nimport { isArray } from '../utils/is-array/is-array.util';\nimport { RawHalLink } from '../interfaces/raw-hal-link.interface';\nimport { RawHalLinks } from '../interfaces/raw-hal-links.interface';\nimport { RequestOptions } from '../types/request-options.type';\nimport { RelationshipRequestDescriptor } from '../types/relationship-request-descriptor.type';\nimport { generateUUID } from '../helpers/uuid/uuid.helper';\n\nexport class HalDocument<T extends HalModel> {\n\tpublic models: Array<T>;\n\tpublic pagination: Pagination;\n\tpublic uniqueModelIdentificator: string;\n\n\tconstructor(\n\t\tprivate rawResource: RawHalResource,\n\t\tprivate rawResponse: HttpResponse<any>,\n\t\tprivate modelClass: ModelConstructor<T>,\n\t\tprivate datastore: DatastoreService,\n\t) {\n\t\tthis.parseRawResources(rawResource);\n\t\tthis.generateUniqueModelIdentificator();\n\t}\n\n\tpublic get hasEmbeddedItems(): boolean {\n\t\tconst listPropertyName: string = this.getListPropertyName(this.rawResource);\n\t\treturn (\n\t\t\tthis.rawResource[EMBEDDED_PROPERTY_NAME] &&\n\t\t\tthis.rawResource[EMBEDDED_PROPERTY_NAME][listPropertyName]\n\t\t);\n\t}\n\n\tpublic get itemLinks(): Array<RawHalLink> {\n\t\tconst listPropertyName: string = this.getListPropertyName(this.rawResource);\n\t\treturn (this.links[listPropertyName] as any) || [];\n\t}\n\n\tpublic getPage(\n\t\tpageNumber: number,\n\t\tincludeRelationships: Array<string | RelationshipRequestDescriptor> = [],\n\t\trequestOptions: RequestOptions = {},\n\t\tsubsequentRequestsOptions: RequestOptions = {},\n\t): Observable<HalDocument<T>> {\n\t\trequestOptions.params = requestOptions.params || {};\n\n\t\tif (pageNumber || pageNumber === 0) {\n\t\t\trequestOptions.params['page'] = pageNumber;\n\t\t}\n\n\t\tconst relationshipUrl: string = this.links[SELF_PROPERTY_NAME].href;\n\n\t\treturn this.datastore.find(\n\t\t\tthis.modelClass,\n\t\t\t{},\n\t\t\ttrue,\n\t\t\tincludeRelationships,\n\t\t\trequestOptions,\n\t\t\trelationshipUrl,\n\t\t\tsubsequentRequestsOptions,\n\t\t);\n\t}\n\n\tprivate parseRawResources(resources: RawHalResource): void {\n\t\tconst items: Array<RawHalResource> = this.getRawResourcesFromResponse(resources);\n\t\tthis.models = this.generateModels(items);\n\t\tthis.pagination = this.generatePagination(resources);\n\t}\n\n\tprivate generateModels(resources: Array<RawHalResource>): Array<T> {\n\t\treturn resources.map((resource: RawHalResource) => {\n\t\t\treturn new this.modelClass(resource, this.datastore, this.rawResponse);\n\t\t});\n\t}\n\n\tprivate generatePagination(pagination: RawHalResource): Pagination {\n\t\tif (!this.datastore.paginationClass) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn new this.datastore.paginationClass(pagination);\n\t}\n\n\tprivate getRawResourcesFromResponse(resources: RawHalResource): Array<RawHalResource> {\n\t\tconst listPropertyName: string = this.getListPropertyName(resources);\n\n\t\tif (!resources[EMBEDDED_PROPERTY_NAME]) {\n\t\t\treturn [];\n\t\t}\n\n\t\treturn resources[EMBEDDED_PROPERTY_NAME][listPropertyName] || [];\n\t}\n\n\tprivate getListPropertyName(listResponse: RawHalResource): string {\n\t\tconst links = listResponse[LINKS_PROPERTY_NAME];\n\n\t\tconst embdedded: object = this.rawResource[EMBEDDED_PROPERTY_NAME];\n\t\tconst fallbackListPropertyName = embdedded\n\t\t\t? Object.keys(embdedded)[0]\n\t\t\t: 'noListPropertyPresent';\n\n\t\treturn (\n\t\t\tObject.keys(links || {}).find((propertyName: string) => {\n\t\t\t\treturn isArray(links[propertyName]);\n\t\t\t}) || fallbackListPropertyName\n\t\t);\n\t}\n\n\tpublic get selfLink(): string {\n\t\treturn this.links && this.links[SELF_PROPERTY_NAME]\n\t\t\t? this.links[SELF_PROPERTY_NAME].href\n\t\t\t: null;\n\t}\n\n\tprivate get links(): RawHalLinks {\n\t\treturn this.rawResource[LINKS_PROPERTY_NAME];\n\t}\n\n\tprivate generateUniqueModelIdentificator(): void {\n\t\tthis.uniqueModelIdentificator = generateUUID();\n\t}\n}\n","import { HttpResponse, HttpHeaders } from '@angular/common/http';\n\nexport function getResponseHeader(response: HttpResponse<any>, headerName: string): any {\n\tconst emptyHeaders: HttpHeaders = new HttpHeaders();\n\tconst headers: HttpHeaders = response ? response.headers || emptyHeaders : emptyHeaders;\n\treturn headers.get(headerName);\n}\n","import { HalModel } from '../../models/hal.model';\n\nexport function isHalModelInstance(classInstance: any): boolean {\n\tif (!classInstance) {\n\t\treturn false;\n\t}\n\n\tif (classInstance instanceof HalModel) {\n\t\treturn true;\n\t}\n\n\treturn isHalModelInstance(classInstance.prototype);\n}\n","import { RelationshipRequestDescriptor } from '../../types/relationship-request-descriptor.type';\n\nexport function ensureRelationshipRequestDescriptors(\n\trelationships: Array<string | RelationshipRequestDescriptor>,\n): Array<RelationshipRequestDescriptor> {\n\treturn relationships.map((relationshipDescriptor: string | RelationshipRequestDescriptor) => {\n\t\tif (typeof relationshipDescriptor === 'string') {\n\t\t\treturn { name: relationshipDescriptor };\n\t\t}\n\n\t\treturn relationshipDescriptor;\n\t});\n}\n","export function removeQueryParams(uri: string): string {\n\tconst splittedUri: Array<string> = hasOnlyTemplatedQueryParameters(uri)\n\t\t? uri.split('{?')\n\t\t: uri.split('?');\n\n\tif (splittedUri.length > 1) {\n\t\tsplittedUri.pop();\n\t}\n\n\treturn splittedUri.join('');\n}\n\nfunction hasOnlyTemplatedQueryParameters(uri: string): boolean {\n\treturn uri.indexOf('{?') !== -1;\n}\n","import { HttpHeaders } from '@angular/common/http';\nimport { PlainHeaders } from '../../types/request-options.type';\n\nexport function setRequestHeader<HttpHeaders>(\n\tinitialHeaders: HttpHeaders,\n\theaderName: string,\n\theaderValue: string | Array<string>,\n): HttpHeaders;\nexport function setRequestHeader<PlainHeaders>(\n\tinitialHeaders: PlainHeaders,\n\theaderName: string,\n\theaderValue: string | Array<string>,\n): PlainHeaders;\nexport function setRequestHeader(\n\tinitialHeaders: HttpHeaders | PlainHeaders,\n\theaderName: string,\n\theaderValue: string | Array<string>,\n): HttpHeaders | PlainHeaders {\n\tif (initialHeaders instanceof HttpHeaders) {\n\t\treturn setHttpRequestHeader(initialHeaders, headerName, headerValue);\n\t}\n\n\treturn setObjectRequestHeader(initialHeaders, headerName, headerValue);\n}\n\nfunction setHttpRequestHeader(\n\tinitialHeaders: HttpHeaders,\n\theaderName: string,\n\theaderValue: string | Array<string>,\n): HttpHeaders {\n\tif (headerValue !== undefined && headerValue !== null) {\n\t\treturn initialHeaders.append(headerName, headerValue);\n\t}\n\n\treturn initialHeaders;\n}\n\nfunction setObjectRequestHeader(\n\tinitialHeaders: PlainHeaders,\n\theaderName: string,\n\theaderValue: string | Array<string>,\n): PlainHeaders {\n\tconst headers: PlainHeaders = {};\n\n\tObject.assign(headers, initialHeaders);\n\n\tif (headerValue !== undefined && headerValue !== null) {\n\t\theaders[headerName] = headerValue;\n\t}\n\n\treturn headers;\n}\n","export function isString(item: any): boolean {\n\treturn typeof item === 'string' || item instanceof String;\n}\n","export class SimpleHalModel {}\n","import { SimpleHalModel } from '../../models/simple-hal.model';\n\nexport function isSimpleHalModelInstance(classInstance: any): boolean {\n\tif (!classInstance) {\n\t\treturn false;\n\t}\n\n\tif (classInstance instanceof SimpleHalModel) {\n\t\treturn true;\n\t}\n\n\treturn isSimpleHalModelInstance(classInstance.prototype);\n}\n","import { isHalModelInstance } from '../is-hal-model-instance.ts/is-hal-model-instance.helper';\nimport { isSimpleHalModelInstance } from '../is-simple-hal-model-instance.ts/is-simple-hal-model-instance.helper';\n\nexport function isFunction(functionToCheck) {\n\treturn (\n\t\ttypeof functionToCheck === 'function' &&\n\t\t!isHalModelInstance(functionToCheck) &&\n\t\t!isSimpleHalModelInstance(functionToCheck)\n\t);\n}\n","import { Observable } from 'rxjs';\nimport { HttpResponse } from '@angular/common/http';\nimport {\n\tModelOptions,\n\tDEFAULT_MODEL_OPTIONS,\n\tDEFAULT_MODEL_TYPE,\n} from '../interfaces/model-options.interface';\nimport { RawHalResource } from '../interfaces/raw-hal-resource.interface';\nimport {\n\tATTRIBUTE_PROPERTIES_METADATA_KEY,\n\tHAL_MODEL_DOCUMENT_CLASS_METADATA_KEY,\n\tHAS_ONE_PROPERTIES_METADATA_KEY,\n\tHAS_MANY_PROPERTIES_METADATA_KEY,\n\tHEADER_ATTRIBUTE_PROPERTIES_METADATA_KEY,\n\tLINK_PROPERTIES_METADATA_KEY,\n} from '../constants/metadata.constant';\nimport { HalDocumentConstructor } from '../types/hal-document-construtor.type';\nimport {\n\tModelProperty,\n\tAttributeModelProperty,\n\tHasOneModelProperty,\n\tHasManyModelProperty,\n\tHeaderAttributeModelProperty,\n\tLinkProperty,\n} from '../interfaces/model-property.interface';\nimport {\n\tLINKS_PROPERTY_NAME,\n\tSELF_PROPERTY_NAME,\n\tEMBEDDED_PROPERTY_NAME,\n} from '../constants/hal.constant';\nimport { LOCAL_DOCUMENT_ID_PREFIX, LOCAL_MODEL_ID_PREFIX } from '../constants/general.constant';\nimport { DatastoreService } from '../services/datastore/datastore.service';\nimport { RawHalLink } from '../interfaces/raw-hal-link.interface';\nimport { RawHalLinks } from '../interfaces/raw-hal-links.interface';\nimport { HalDocument } from '../classes/hal-document';\nimport { NetworkConfig } from '../interfaces/network-config.interface';\nimport { generateUUID } from '../helpers/uuid/uuid.helper';\nimport { getResponseHeader } from '../utils/get-response-headers/get-response-header.util';\nimport { isHalModelInstance } from '../helpers/is-hal-model-instance.ts/is-hal-model-instance.helper';\nimport { PlainHeaders, RequestOptions } from '../types/request-options.type';\nimport { ModelProperty as ModelPropertyEnum } from '../enums/model-property.enum';\nimport { GeneratePayloadOptions } from '../interfaces/generate-payload-options.interface';\nimport { CustomOptions } from '../interfaces/custom-options.interface';\nimport { ensureRelationshipRequestDescriptors } from '../utils/ensure-relationship-descriptors/ensure-relationship-descriptors.util';\nimport { RelationshipRequestDescriptor } from '../types/relationship-request-descriptor.type';\nimport { removeQueryParams } from '../utils/remove-query-params/remove-query-params.util';\nimport { setRequestHeader } from '../utils/set-request-header/set-request-header.util';\nimport { isString } from '../utils/is-string/is-string.util';\nimport { isFunction } from '../helpers/is-function/is-function.helper';\nimport { ModelEndpoints } from '../interfaces/model-endpoints.interface';\nimport { map } from 'rxjs/operators';\nimport { getArrayObjProperty, getObjProperty } from '../helpers/metadata/metadata.helper';\n\nexport abstract class HalModel<Datastore extends DatastoreService = DatastoreService> {\n\tprivate config: ModelOptions = this['config'] || DEFAULT_MODEL_OPTIONS;\n\tprivate temporarySelfLink: string = null;\n\tprivate localModelIdentificator: string;\n\tprivate internalHasManyDocumentIdentificators: { [K: string]: string } = {};\n\tpublic static readonly modelType: string = DEFAULT_MODEL_TYPE;\n\n\tconstructor(\n\t\tprotected resource: RawHalResource = {},\n\t\tprotected datastore: Datastore,\n\t\tpublic rawResponse?: HttpResponse<any>,\n\t) {\n\t\tthis.setLocalModelIdentificator();\n\t\tthis.parseAttributes(resource);\n\t\tthis.parseHeaderAttributes(rawResponse);\n\t\tthis.initializeHasOneProperties();\n\t\tthis.initializeHasManyProperties();\n\t\tthis.extractEmbeddedProperties(resource);\n\t}\n\n\tpublic get uniqueModelIdentificator(): string {\n\t\treturn this.getUniqueModelIdentificator();\n\t}\n\n\tprotected getUniqueModelIdentificator(): string {\n\t\treturn this.selfLink || this.localModelIdentificator;\n\t}\n\n\tpublic get id(): string {\n\t\tif (!this.selfLink) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst selfLink: string = removeQueryParams(this.selfLink);\n\t\treturn selfLink.split('/').pop();\n\t}\n\n\tpublic get endpoint(): string {\n\t\treturn this.config.endpoint || 'unknownModelEndpoint';\n\t}\n\n\tpublic get modelEndpoints(): ModelEndpoints {\n\t\treturn null;\n\t}\n\n\tpublic get networkConfig(): NetworkConfig {\n\t\treturn this.config.networkConfig;\n\t}\n\n\tpublic get type(): string {\n\t\treturn this.config.type;\n\t}\n\n\tpublic getHalDocumentClass<T extends this>(): HalDocumentConstructor<T> {\n\t\treturn getObjProperty(this, HAL_MODEL_DOCUMENT_CLASS_METADATA_KEY, null);\n\t}\n\n\tpublic getRelationshipUrl(relationshipName: string): string {\n\t\tconst property: ModelProperty = this.getPropertyData(relationshipName);\n\n\t\tif (!property) {\n\t\t\tconsole.warn(`Relationship with the name ${relationshipName} is not defined on the model.`);\n\t\t\treturn;\n\t\t}\n\n\t\tconst fieldName: string = property.externalName || relationshipName;\n\t\tconst url = this.links[fieldName] ? this.links[fieldName].href : '';\n\n\t\tif (!url || url.startsWith(LOCAL_MODEL_ID_PREFIX) || url.startsWith(LOCAL_DOCUMENT_ID_PREFIX)) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn url;\n\t}\n\n\tpublic getPropertyData(propertyName: string): ModelProperty {\n\t\tconst attributeProperty = this.attributeProperties.find(\n\t\t\t(property: ModelProperty) => property.name === propertyName,\n\t\t);\n\t\tconst hasOneProperty = this.hasOneProperties.find(\n\t\t\t(property: ModelProperty) => property.name === propertyName,\n\t\t);\n\t\tconst hasManyProperty = this.hasManyProperties.find(\n\t\t\t(property: ModelProperty) => property.name === propertyName,\n\t\t);\n\t\tconst linkProperty = this.linkProperties.find(\n\t\t\t(property: ModelProperty) => property.name === propertyName,\n\t\t);\n\t\treturn attributeProperty || hasOneProperty || hasManyProperty || linkProperty;\n\t}\n\n\tpublic getEmbeddedResource(resourceName: string): RawHalResource | undefined {\n\t\tconst property: ModelProperty = this.getPropertyData(resourceName);\n\n\t\tif (this.resource[property.externalName]) {\n\t\t\treturn this.resource[property.externalName];\n\t\t}\n\n\t\tif (!this.resource[EMBEDDED_PROPERTY_NAME]) {\n\t\t\treturn;\n\t\t}\n\n\t\treturn this.resource[EMBEDDED_PROPERTY_NAME][property.externalName];\n\t}\n\n\tpublic save(\n\t\trequestOptions?: RequestOptions,\n\t\toptions: CustomOptions<this> = {},\n\t): Observable<this> {\n\t\tconst modelClass = Object.getPrototypeOf(this).constructor;\n\t\treturn this.datastore.save(this, modelClass, requestOptions, options);\n\t}\n\n\tpublic update(\n\t\trequestOptions?: RequestOptions,\n\t\toptions: CustomOptions<this> = {},\n\t): Observable<this> {\n\t\treturn this.datastore.update(this, requestOptions, options);\n\t}\n\n\tpublic delete(\n\t\trequestOptions?: RequestOptions,\n\t\toptions: CustomOptions<this> = {},\n\t): Observable<void> {\n\t\treturn this.datastore.delete(this, requestOptions, options);\n\t}\n\n\tpublic refetch(\n\t\tincludeRelationships?: Array<string | RelationshipRequestDescriptor>,\n\t\trequestOptions?: RequestOptions,\n\t): Observable<this> {\n\t\tconst modelClass = Object.getPrototypeOf(this).constructor;\n\t\treturn this.datastore\n\t\t\t.findOne(modelClass, undefined, includeRelationships, requestOptions, this.selfLink)\n\t\t\t.pipe(\n\t\t\t\tmap((fetchedModel: this) => {\n\t\t\t\t\tthis.populateModelMetadata(fetchedModel);\n\t\t\t\t\treturn this;\n\t\t\t\t}),\n\t\t\t);\n\t}\n\n\tpublic generatePayload(options: GeneratePayloadOptions = {}): object {\n\t\tconst attributePropertiesPayload: object = this.getAttributePropertiesPayload(options);\n\t\tconst relationshipsPayload: object = this.generateRelationshipsPayload(options);\n\t\tconst hasRelationshipLinks: boolean = Boolean(Object.keys(relationshipsPayload).length);\n\n\t\tconst payload = { ...attributePropertiesPayload };\n\n\t\tif (hasRelationshipLinks) {\n\t\t\tpayload[LINKS_PROPERTY_NAME] = relationshipsPayload;\n\t\t}\n\n\t\treturn payload;\n\t}\n\n\t// Used only when HalModels or HalDocument are passed when creating a new model\n\tprivate extractEmbeddedProperties(rawResource: RawHalResource): void {\n\t\tconst embeddedProperties: object = rawResource[EMBEDDED_PROPERTY_NAME] || {};\n\n\t\tObject.keys(embeddedProperties).forEach((propertyName: string) => {\n\t\t\tconst property: ModelProperty = this.getPropertyData(propertyName);\n\t\t\tconst isRelationshipProperty: boolean =\n\t\t\t\tproperty && (this.isHasOneProperty(property) || this.isHasManyProperty(property));\n\t\t\tconst propertyValue = embeddedProperties[propertyName];\n\t\t\tconst isHalModelOrDocument: boolean =\n\t\t\t\tisHalModelInstance(propertyValue) || propertyValue instanceof HalDocument;\n\n\t\t\tif (isRelationshipProperty && isHalModelOrDocument) {\n\t\t\t\tthis[property.name] = propertyValue;\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate getAttributePropertiesPayload(payloadOptions: GeneratePayloadOptions = {}): object {\n\t\tconst { specificFields, changedPropertiesOnly } = payloadOptions;\n\n\t\treturn this.attributeProperties.reduce((payload: object, property: AttributeModelProperty) => {\n\t\t\tconst propertyName: string = property.name;\n\t\t\tconst isPropertyExcludedFromPaylaod: boolean = property.excludeFromPayload;\n\t\t\tconst isSpecificFieldsSpecified: boolean = specificFields && Boolean(specificFields.length);\n\t\t\tconst isSpecificFieldsConditionSatisfied: boolean =\n\t\t\t\t!isSpecificFieldsSpecified || specificFields.indexOf(propertyName) !== -1;\n\n\t\t\tif (isPropertyExcludedFromPaylaod || !isSpecificFieldsConditionSatisfied) {\n\t\t\t\treturn payload;\n\t\t\t}\n\n\t\t\tconst externalPropertyName: string = property.externalName;\n\t\t\tconst propertyPayload: object = property.transformBeforeSave\n\t\t\t\t? property.transformBeforeSave(this[propertyName])\n\t\t\t\t: this[propertyName];\n\n\t\t\tif (changedPropertiesOnly) {\n\t\t\t\tconst isPropertyChanged: boolean = propertyPayload !== this.resource[propertyName];\n\n\t\t\t\tif (isPropertyChanged) {\n\t\t\t\t\tpayload[externalPropertyName] = propertyPayload;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tpayload[externalPropertyName] = propertyPayload;\n\t\t\t}\n\n\t\t\treturn payload;\n\t\t}, {});\n\t}\n\n\tprivate generateHasOnePropertyPayload(property: HasOneModelProperty): object {\n\t\tconst payload: object = {};\n\n\t\tconst propertyName: string = property.name;\n\t\tconst externalPropertyName: string = property.externalName;\n\n\t\tif (!this[propertyName].selfLink) {\n\t\t\treturn payload;\n\t\t}\n\n\t\tpayload[externalPropertyName] = {\n\t\t\thref: this[propertyName].selfLink,\n\t\t};\n\n\t\treturn payload;\n\t}\n\n\tprivate generateHasManyPropertyPayload(property: HasManyModelProperty): object {\n\t\tconst payload: object = {};\n\t\tconst hasManyPropertyLinks = [];\n\n\t\tconst propertyName: string = property.name;\n\t\tconst externalPropertyName: string = property.externalName;\n\n\t\t// TODO check if this[propertyName] is an array of models or just a HalDocument\n\t\tthis[propertyName].forEach((model: HalModel) => {\n\t\t\tif (model && model.selfLink) {\n\t\t\t\thasManyPropertyLinks.push({\n\t\t\t\t\thref: model.selfLink,\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\n\t\tif (hasManyPropertyLinks.length) {\n\t\t\tpayload[externalPropertyName] = hasManyPropertyLinks;\n\t\t}\n\n\t\treturn payload;\n\t}\n\n\tprivate generateRelationshipsPayload(payloadOptions: GeneratePayloadOptions = {}): object {\n\t\tconst { specificFields } = payloadOptions;\n\t\tconst isSpecificFieldsSpecified: boolean = specificFields && Boolean(specificFields.length);\n\n\t\treturn [...this.hasOneProperties, ...this.hasManyProperties]\n\t\t\t.filter((property: HasOneModelProperty) => property.includeInPayload)\n\t\t\t.filter(\n\t\t\t\t(property: HasOneModelProperty) =>\n\t\t\t\t\t!isSpecificFieldsSpecified || specificFields.indexOf(property.name) !== -1,\n\t\t\t)\n\t\t\t.reduce((payload: object, property: HasOneModelProperty) => {\n\t\t\t\tconst propertyName: string = property.name;\n\n\t\t\t\tif (!this[propertyName]) {\n\t\t\t\t\treturn payload;\n\t\t\t\t}\n\n\t\t\t\tconst isHasOneProperty: boolean = property.type === ModelPropertyEnum.HasOne;\n\t\t\t\tlet propertyPayload: object;\n\n\t\t\t\tif (isHasOneProperty) {\n\t\t\t\t\tpropertyPayload = this.generateHasOnePropertyPayload(property);\n\t\t\t\t} else {\n\t\t\t\t\tpropertyPayload = this.generateHasManyPropertyPayload(property);\n\t\t\t\t}\n\n\t\t\t\tObject.assign(payload, propertyPayload);\n\n\t\t\t\treturn payload;\n\t\t\t}, {});\n\t}\n\n\tpublic generateHeaders(): PlainHeaders {\n\t\treturn this.headerAttributeProperties.reduce(\n\t\t\t(headers: PlainHeaders, property: HeaderAttributeModelProperty) => {\n\t\t\t\tconst externalPropertyName: string = property.externalName;\n\t\t\t\tconst propertyName: string = property.name;\n\t\t\t\tconst propertyValue = property.transformBeforeSave\n\t\t\t\t\t? property.transformBeforeSave(this[propertyName])\n\t\t\t\t\t: this[propertyName];\n\n\t\t\t\treturn setRequestHeader(headers, externalPropertyName, propertyValue);\n\t\t\t},\n\t\t\t{},\n\t\t);\n\t}\n\n\tpublic get isSaved(): boolean {\n\t\treturn Boolean(this.id);\n\t}\n\n\tpublic fetchRelationships(\n\t\trelationships:\n\t\t\t| string\n\t\t\t| RelationshipRequestDescriptor\n\t\t\t| Array<string | RelationshipRequestDescriptor>,\n\t\trequestOptions: RequestOptions = {},\n\t): Observable<this> {\n\t\tconst relationshipsArray: Array<string | RelationshipRequestDescriptor> = [].concat(\n\t\t\trelationships,\n\t\t);\n\t\tconst relationshipDescriptors: Array<RelationshipRequestDescriptor> =\n\t\t\tensureRelationshipRequestDescriptors(relationshipsArray);\n\t\treturn this.datastore.fetchModelRelationships(this, relationshipDescriptors, requestOptions);\n\t}\n\n\tpublic getRelationship<T extends HalModel>(relationshipName: string): T | HalDocument<T> {\n\t\tconst property: ModelProperty = this.getPropertyData(relationshipName);\n\n\t\tconst isHasOneProperty: boolean = property.type === ModelPropertyEnum.HasOne;\n\n\t\tif (isHasOneProperty) {\n\t\t\treturn this.getHasOneRelationship(property) as T;\n\t\t} else if (this.isHasManyProperty) {\n\t\t\treturn this.getHasManyRelationship(property);\n\t\t}\n\t}\n\n\tprivate get attributeProperties(): Array<AttributeModelProperty> {\n\t\treturn this.getPropertiesMetadata(ATTRIBUTE_PROPERTIES_METADATA_KEY);\n\t}\n\n\tprivate get headerAttributeProperties(): Array<HeaderAttributeModelProperty> {\n\t\treturn this.getPropertiesMetadata(HEADER_ATTRIBUTE_PROPERTIES_METADATA_KEY);\n\t}\n\n\tprivate get hasOneProperties(): Array<HasOneModelProperty> {\n\t\treturn this.getPropertiesMetadata(HAS_ONE_PROPERTIES_METADATA_KEY);\n\t}\n\n\tprivate get hasManyProperties(): Array<HasManyModelProperty> {\n\t\treturn this.getPropertiesMetadata(HAS_MANY_PROPERTIES_METADATA_KEY);\n\t}\n\n\tprivate get linkProperties(): Array<LinkProperty> {\n\t\treturn this.getPropertiesMetadata(LINK_PROPERTIES_METADATA_KEY);\n\t}\n\n\tprivate getPropertiesMetadata<T extends ModelProperty>(propertyKey: string): Array<T> {\n\t\tconst propertiesMetadata: Array<T> = getArrayObjProperty(this, propertyKey);\n\n\t\tconst uniqueMetadata: Array<T> = [];\n\n\t\tpropertiesMetadata.forEach((property: T) => {\n\t\t\tif (uniqueMetadata.map((metadata: T) => metadata.name).indexOf(property.name) === -1) {\n\t\t\t\tuniqueMetadata.push(property);\n\t\t\t}\n\t\t});\n\n\t\treturn uniqueMetadata;\n\t}\n\n\tprivate initializeHasOneProperties(): void {\n\t\tthis.hasOneProperties.forEach((property: ModelProperty) => {\n\t\t\tObject.defineProperty(this, property.name, {\n\t\t\t\tconfigurable: true,\n\t\t\t\tget() {\n\t\t\t\t\treturn this.getHasOneRelationship(property);\n\t\t\t\t},\n\t\t\t\tset<T extends HalModel>(value: T) {\n\t\t\t\t\tif (isHalModelInstance(value) || !value) {\n\t\t\t\t\t\tthis.replaceRelationshipModel(property.externalName, value);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`Only HalModel instances can be assigned to property: ${property.name}. This will become an error in the next ngx-hal release`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\t// throw new Error(`Only HalModel instances can be assigned to property: ${property.name}`);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate initializeHasManyProperties(): void {\n\t\tthis.hasManyProperties.forEach((property: ModelProperty) => {\n\t\t\tObject.defineProperty(this, property.name, {\n\t\t\t\tconfigurable: true,\n\t\t\t\tget() {\n\t\t\t\t\tconst halDocument: HalDocument<HalModel> = this.getHasManyRelationship(property);\n\n\t\t\t\t\tif (!halDocument) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn halDocument.models;\n\t\t\t\t},\n\t\t\t\tset<T extends HalModel>(value: Array<T>) {\n\t\t\t\t\tconst existingHalDocument: HalDocument<HalModel> = this.getHasManyRelationship(property);\n\n\t\t\t\t\tif (existingHalDocument) {\n\t\t\t\t\t\texistingHalDocument.models = value;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst halDocumentRaw = {\n\t\t\t\t\t\t\tmodels: value,\n\t\t\t\t\t\t\tuniqueModelIdentificator: `${LOCAL_DOCUMENT_ID_PREFIX}-${generateUUID()}`,\n\t\t\t\t\t\t};\n\t\t\t\t\t\tthis.updateHasManyDocumentIdentificator(\n\t\t\t\t\t\t\tproperty,\n\t\t\t\t\t\t\thalDocumentRaw.uniqueModelIdentificator,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis.datastore.storage.save(halDocumentRaw);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate setProperty(\n\t\tmodelProperty: AttributeModelProperty | HeaderAttributeModelProperty,\n\t\trawPropertyValue: any,\n\t): void {\n\t\tconst propertyValue = modelProperty.transformResponseValue\n\t\t\t? modelProperty.transformResponseValue(rawPropertyValue)\n\t\t\t: rawPropertyValue;\n\n\t\tif (isString(modelProperty.propertyClass)) {\n\t\t\tconst propertyClass = this.datastore.findModelClassByType(modelProperty.propertyClass);\n\t\t\tthis[modelProperty.name] = new propertyClass(propertyValue);\n\t\t} else if (isFunction(modelProperty.propertyClass)) {\n\t\t\tconst propertyClass = modelProperty.propertyClass(propertyValue);\n\t\t\tthis[modelProperty.name] = new propertyClass(propertyValue);\n\t\t} else if (modelProperty.propertyClass) {\n\t\t\tthis[modelProperty.name] = new modelProperty.propertyClass(propertyValue);\n\t\t} else {\n\t\t\tthis[modelProperty.name] = propertyValue;\n\t\t}\n\t}\n\n\tprivate parseAttributes(resource: RawHalResource): void {\n\t\tthis.attributeProperties.forEach((attributeProperty: AttributeModelProperty) => {\n\t\t\tconst rawPropertyValue: any = resource[attributeProperty.externalName];\n\t\t\tthis.setProperty(attributeProperty, rawPropertyValue);\n\t\t});\n\t}\n\n\tprivate parseHeaderAttributes(response: HttpResponse<any>): void {\n\t\tthis.headerAttributeProperties.forEach(\n\t\t\t(headerAttributeProperty: HeaderAttributeModelProperty) => {\n\t\t\t\tconst rawPropertyValue: any = getResponseHeader(\n\t\t\t\t\tresponse,\n\t\t\t\t\theaderAttributeProperty.externalName,\n\t\t\t\t);\n\t\t\t\tthis.setProperty(headerAttributeProperty, rawPropertyValue);\n\t\t\t},\n\t\t);\n\t}\n\n\tprivate getHasOneRelationship<T extends HalModel>(property: ModelProperty): T {\n\t\tconst relationshipLinks: RawHalLink = this.links[property.externalName];\n\n\t\tif (!relationshipLinks) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst modelIdentificator: string = relationshipLinks.href;\n\n\t\treturn this.datastore.storage.get(modelIdentificator);\n\t}\n\n\tprivate getHasManyRelationship<T extends HalModel>(property: ModelProperty): HalDocument<T> {\n\t\tconst uniqueRelationshipIdentificator: string =\n\t\t\tthis.hasManyDocumentIdentificators[property.externalName];\n\n\t\tif (!uniqueRelationshipIdentificator) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst halDocument: HalDocument<T> = this.datastore.storage.get(\n\t\t\tuniqueRelationshipIdentificator,\n\t\t) as HalDocument<T>;\n\n\t\tif (!halDocument) {\n\t\t\tconsole.warn(`Has many relationship ${property.name} is not fetched.`);\n\t\t\treturn;\n\t\t}\n\n\t\treturn halDocument;\n\t}\n\n\tpublic get links(): RawHalLinks | { [relationshipName: string]: RawHalLink } {\n\t\treturn this.resource[LINKS_PROPERTY_NAME] || {};\n\t}\n\n\tpublic get selfLink(): string {\n\t\treturn this.links && this.links[SELF_PROPERTY_NAME]\n\t\t\t? this.links[SELF_PROPERTY_NAME].href\n\t\t\t: this.temporarySelfLink;\n\t}\n\n\tpublic set selfLink(link: string) {\n\t\tthis.temporarySelfLink = link;\n\t}\n\n\tprivate replaceRelationshipModel<T extends HalModel>(\n\t\trelationshipName: string,\n\t\trelationshipModel: T,\n\t): void {\n\t\tthis.resource[LINKS_PROPERTY_NAME] = this.resource[LINKS_PROPERTY_NAME] || {\n\t\t\tself: null,\n\t\t};\n\n\t\tlet relationshipLink = null;\n\t\tif (relationshipModel) {\n\t\t\trelationshipLink = {\n\t\t\t\thref: relationshipModel.uniqueModelIdentificator || relationshipModel.selfLink,\n\t\t\t};\n\t\t}\n\n\t\tthis.resource[LINKS_PROPERTY_NAME][relationshipName] = relationshipLink;\n\n\t\t// Save the model to the storage if it's not already there\n\t\tif (!this[relationshipName] && relationshipModel) {\n\t\t\t// TODO should the model be removed from the storage if relationshipModel does not exist?\n\t\t\tthis.datastore.storage.save(relationshipModel);\n\t\t}\n\t}\n\n\tprivate setLocalModelIdentificator(): void {\n\t\tthis.localModelIdentificator = `${LOCAL_MODEL_ID_PREFIX}-${generateUUID()}`;\n\t}\n\n\tprivate isHasOneProperty(property: ModelOptions): boolean {\n\t\treturn property.type === ModelPropertyEnum.HasOne;\n\t}\n\n\tprivate isHasManyProperty(property: ModelOptions): boolean {\n\t\treturn property.type === ModelPropertyEnum.HasMany;\n\t}\n\n\tpublic populateModelMetadata<K extends HalModel>(sourceModel: K) {\n\t\tthis.resource = sourceModel.resource;\n\t\tthis.rawResponse = sourceModel.rawResponse;\n\t\tthis.parseAttributes(this.resource);\n\t\tthis.parseHeaderAttributes(this.rawResponse);\n\t\tthis.extractEmbeddedProperties(this.resource);\n\t}\n\n\tpublic updateHasManyDocumentIdentificator(\n\t\tproperty: HasManyModelProperty,\n\t\tidentificator: string,\n\t): void {\n\t\tthis.hasManyDocumentIdentificators[property.externalName] = identificator;\n\t}\n\n\tpublic set hasManyDocumentIdentificators(hasManyDocumentIdentificators: { [K: string]: string }) {\n\t\tthis.internalHasManyDocumentIdentificators = Object.assign({}, hasManyDocumentIdentificators);\n\t}\n\n\tpublic get hasManyDocumentIdentificators(): { [K: string]: string } {\n\t\treturn this.internalHasManyDocumentIdentificators;\n\t}\n}\n","import { RawHalResource } from '../interfaces/raw-hal-resource.interface';\n\nexport abstract class Pagination {\n\tconstructor(protected rawResource: RawHalResource = {}) {}\n}\n","import { HalModel } from