From e29561064e0200a59fbfe9b10c38714697519338 Mon Sep 17 00:00:00 2001 From: Mihael Safaric Date: Tue, 17 Sep 2024 16:47:21 +0200 Subject: [PATCH] Improve handling Pagination --- projects/ngx-hal/package.json | 2 +- .../ngx-hal/src/lib/classes/hal-document.ts | 12 +- .../classes/hal-storage/etag-hal-storage.ts | 32 +- .../hal-storage/hal-storage-factory.ts | 11 +- .../lib/classes/hal-storage/hal-storage.ts | 34 +- .../classes/hal-storage/simple-hal-storage.ts | 9 +- .../src/lib/decorators/attribute.decorator.ts | 10 +- .../decorators/datastore-config.decorator.ts | 7 +- .../src/lib/decorators/has-many.decorator.ts | 7 +- .../src/lib/decorators/has-one.decorator.ts | 7 +- .../decorators/header-attribute.decorator.ts | 7 +- .../src/lib/decorators/link.decorator.ts | 5 +- .../lib/decorators/model-config.decorator.ts | 9 +- .../interfaces/attribute-options.interface.ts | 5 +- .../interfaces/datastore-options.interface.ts | 10 +- .../interfaces/has-many-options.interface.ts | 5 +- .../interfaces/has-one-options.interface.ts | 5 +- .../header-attribute-options.interface.ts | 5 +- .../lib/interfaces/model-options.interface.ts | 7 +- projects/ngx-hal/src/lib/models/hal.model.ts | 45 ++- .../services/datastore/datastore.service.ts | 329 +++++++++--------- .../services/model-service/model.service.ts | 18 +- .../lib/types/hal-document-construtor.type.ts | 5 +- .../src/lib/types/model-constructor.type.ts | 7 +- .../ngx-hal/src/lib/types/pagination.type.ts | 3 - 25 files changed, 321 insertions(+), 275 deletions(-) delete mode 100644 projects/ngx-hal/src/lib/types/pagination.type.ts diff --git a/projects/ngx-hal/package.json b/projects/ngx-hal/package.json index 9c11fb8..813a792 100644 --- a/projects/ngx-hal/package.json +++ b/projects/ngx-hal/package.json @@ -1,6 +1,6 @@ { "name": "ngx-hal", - "version": "3.1.1", + "version": "4.0.0-beta", "description": "Angular library for supporting HAL format APIs", "author": "Infinum ", "license": "MIT", diff --git a/projects/ngx-hal/src/lib/classes/hal-document.ts b/projects/ngx-hal/src/lib/classes/hal-document.ts index 81d84c4..8e6a511 100644 --- a/projects/ngx-hal/src/lib/classes/hal-document.ts +++ b/projects/ngx-hal/src/lib/classes/hal-document.ts @@ -17,16 +17,16 @@ import { RequestOptions } from '../types/request-options.type'; import { RelationshipRequestDescriptor } from '../types/relationship-request-descriptor.type'; import { generateUUID } from '../helpers/uuid/uuid.helper'; -export class HalDocument { +export class HalDocument, P extends Pagination> { public models: Array; - public pagination: Pagination; + public pagination: P; public uniqueModelIdentificator: string; constructor( private rawResource: RawHalResource, private rawResponse: HttpResponse, - private modelClass: ModelConstructor, - private datastore: DatastoreService, + private modelClass: ModelConstructor, + private datastore: DatastoreService

, ) { this.parseRawResources(rawResource); this.generateUniqueModelIdentificator(); @@ -50,7 +50,7 @@ export class HalDocument { includeRelationships: Array = [], requestOptions: RequestOptions = {}, subsequentRequestsOptions: RequestOptions = {}, - ): Observable> { + ): Observable> { requestOptions.params = requestOptions.params || {}; if (pageNumber || pageNumber === 0) { @@ -82,7 +82,7 @@ export class HalDocument { }); } - private generatePagination(pagination: RawHalResource): Pagination { + private generatePagination(pagination: RawHalResource): P { if (!this.datastore.paginationClass) { return null; } diff --git a/projects/ngx-hal/src/lib/classes/hal-storage/etag-hal-storage.ts b/projects/ngx-hal/src/lib/classes/hal-storage/etag-hal-storage.ts index cee819f..a61801c 100644 --- a/projects/ngx-hal/src/lib/classes/hal-storage/etag-hal-storage.ts +++ b/projects/ngx-hal/src/lib/classes/hal-storage/etag-hal-storage.ts @@ -1,22 +1,23 @@ -import { HalModel } from '../../models/hal.model'; -import { HalDocument } from './../hal-document'; import { HttpResponse } from '@angular/common/http'; +import { HalModel } from '../../models/hal.model'; import { RequestOptions } from '../../types/request-options.type'; -import { HalStorage } from './hal-storage'; import { setRequestHeader } from '../../utils/set-request-header/set-request-header.util'; +import { Pagination } from '../pagination'; +import { HalDocument } from './../hal-document'; +import { HalStorage } from './hal-storage'; -export interface EtagStorageModel { - model: T | HalDocument; +export interface EtagStorageModel, P extends Pagination> { + model: T | HalDocument; etag: string; } -export class EtagHalStorage extends HalStorage { - public save( - model: T | HalDocument, +export class EtagHalStorage

extends HalStorage

{ + public save>( + model: T | HalDocument, response?: HttpResponse, alternateUniqueIdentificators: Array = [], - ): Array> { - const storedModels: Array> = []; + ): Array> { + const storedModels: Array> = []; const identificators: Array = [].concat(alternateUniqueIdentificators); identificators.push(model.uniqueModelIdentificator); @@ -33,8 +34,8 @@ export class EtagHalStorage extends HalStorage { return storedModels; } - public get(uniqueModelIdentificator: string): T | HalDocument { - const localModel: EtagStorageModel = this.getRawStorageModel(uniqueModelIdentificator); + public get>(uniqueModelIdentificator: string): T | HalDocument { + const localModel: EtagStorageModel = this.getRawStorageModel(uniqueModelIdentificator); return localModel ? localModel.model : undefined; } @@ -42,7 +43,8 @@ export class EtagHalStorage extends HalStorage { uniqueModelIdentificator: string, requestOptions: RequestOptions, ): void { - const storageModel: EtagStorageModel = this.getRawStorageModel(uniqueModelIdentificator); + const storageModel: EtagStorageModel = + this.getRawStorageModel(uniqueModelIdentificator); if (!storageModel) { return; @@ -57,9 +59,9 @@ export class EtagHalStorage extends HalStorage { } } - protected getRawStorageModel( + protected getRawStorageModel>( uniqueModelIdentificator: string, - ): EtagStorageModel { + ): EtagStorageModel { return this.internalStorage[uniqueModelIdentificator]; } diff --git a/projects/ngx-hal/src/lib/classes/hal-storage/hal-storage-factory.ts b/projects/ngx-hal/src/lib/classes/hal-storage/hal-storage-factory.ts index ae0fcde..ad33457 100644 --- a/projects/ngx-hal/src/lib/classes/hal-storage/hal-storage-factory.ts +++ b/projects/ngx-hal/src/lib/classes/hal-storage/hal-storage-factory.ts @@ -2,14 +2,15 @@ import { CacheStrategy } from '../../enums/cache-strategy.enum'; import { SimpleHalStorage } from '../../classes/hal-storage/simple-hal-storage'; import { EtagHalStorage } from '../../classes/hal-storage/etag-hal-storage'; import { HalStorage } from './hal-storage'; +import { Pagination } from '../pagination'; -export type HalStorageType = SimpleHalStorage | EtagHalStorage; +export type HalStorageType

= SimpleHalStorage

| EtagHalStorage

; -export function createHalStorage( +export function createHalStorage

( cacheStrategy: CacheStrategy = CacheStrategy.NONE, - storageInstance: HalStorage, -): HalStorageType { - let storage: HalStorageType; + storageInstance: HalStorage

, +): HalStorageType

{ + let storage: HalStorageType

; switch (cacheStrategy) { case CacheStrategy.NONE: diff --git a/projects/ngx-hal/src/lib/classes/hal-storage/hal-storage.ts b/projects/ngx-hal/src/lib/classes/hal-storage/hal-storage.ts index 65e51c6..8e1b29a 100644 --- a/projects/ngx-hal/src/lib/classes/hal-storage/hal-storage.ts +++ b/projects/ngx-hal/src/lib/classes/hal-storage/hal-storage.ts @@ -1,22 +1,28 @@ -import { HalModel } from '../../models/hal.model'; -import { HalDocument } from './../hal-document'; import { HttpResponse } from '@angular/common/http'; -import { RequestOptions } from '../../types/request-options.type'; import { Observable } from 'rxjs'; +import { HalModel } from '../../models/hal.model'; import { ModelConstructor, ModelConstructorFn } from '../../types/model-constructor.type'; +import { RequestOptions } from '../../types/request-options.type'; +import { Pagination } from '../pagination'; +import { HalDocument } from './../hal-document'; -export abstract class HalStorage { +export abstract class HalStorage

{ protected internalStorage: { [K: string]: any } = {}; - public abstract save( - model: T | HalDocument, + public abstract save>( + model: T | HalDocument, response?: HttpResponse, alternateUniqueIdentificators?: Array, ): void; - public abstract get(uniqueModelIdentificator: string): T | HalDocument; + public abstract get>( + uniqueModelIdentificator: string, + ): T | HalDocument; - public saveAll(models: Array, savePartialModels: boolean = false): void { + public saveAll>( + models: Array, + savePartialModels: boolean = false, + ): void { models.forEach((model: T) => { if (savePartialModels || !this.get(model.uniqueModelIdentificator)) { this.save(model); @@ -24,7 +30,7 @@ export abstract class HalStorage { }); } - public remove(model: HalModel): void { + public remove(model: HalModel

): void { delete this.internalStorage[model.uniqueModelIdentificator]; } @@ -35,13 +41,13 @@ export abstract class HalStorage { // noop } - public makeGetRequestWrapper?( + public makeGetRequestWrapper?>( urls: { originalUrl: string; cleanUrl: string; urlWithParams: string }, - cachedResource: T | HalDocument, - originalGetRequest$: Observable>, + cachedResource: T | HalDocument, + originalGetRequest$: Observable>, requestOptions: RequestOptions, - modelClass: ModelConstructor | ModelConstructorFn, + modelClass: ModelConstructor | ModelConstructorFn, isSingleResource: boolean, storePartialModels?: boolean, - ): Observable>; + ): Observable>; } diff --git a/projects/ngx-hal/src/lib/classes/hal-storage/simple-hal-storage.ts b/projects/ngx-hal/src/lib/classes/hal-storage/simple-hal-storage.ts index 4270baa..d6df02f 100644 --- a/projects/ngx-hal/src/lib/classes/hal-storage/simple-hal-storage.ts +++ b/projects/ngx-hal/src/lib/classes/hal-storage/simple-hal-storage.ts @@ -2,10 +2,11 @@ import { HttpResponse } from '@angular/common/http'; import { HalModel } from '../../models/hal.model'; import { HalDocument } from './../hal-document'; import { HalStorage } from './hal-storage'; +import { Pagination } from '../pagination'; -export class SimpleHalStorage extends HalStorage { - public save( - model: T | HalDocument, +export class SimpleHalStorage

extends HalStorage

{ + public save>( + model: T | HalDocument, response?: HttpResponse, alternateUniqueIdentificators: Array = [], ): void { @@ -17,7 +18,7 @@ export class SimpleHalStorage extends HalStorage { }); } - public get(uniqueModelIdentificator: string): T | HalDocument { + public get>(uniqueModelIdentificator: string): T | HalDocument { return this.internalStorage[uniqueModelIdentificator]; } } diff --git a/projects/ngx-hal/src/lib/decorators/attribute.decorator.ts b/projects/ngx-hal/src/lib/decorators/attribute.decorator.ts index 95a25f5..8db9979 100644 --- a/projects/ngx-hal/src/lib/decorators/attribute.decorator.ts +++ b/projects/ngx-hal/src/lib/decorators/attribute.decorator.ts @@ -1,3 +1,4 @@ +import { Pagination } from '../classes/pagination'; import { ATTRIBUTE_PROPERTIES_METADATA_KEY } from '../constants/metadata.constant'; import { ModelProperty as ModelPropertyEnum } from '../enums/model-property.enum'; import { getObjProperty } from '../helpers/metadata/metadata.helper'; @@ -10,9 +11,12 @@ import { AttributeModelProperty } from '../interfaces/model-property.interface'; import { HalModel } from '../models/hal.model'; import { deepmergeWrapper } from '../utils/deepmerge-wrapper'; -export function Attribute(options: AttributeOptions = {}) { - return (model: HalModel, propertyName: string) => { - const attributeOptions: AttributeOptions = deepmergeWrapper(DEFAULT_ATTRIBUTE_OPTIONS, options); +export function Attribute

(options: AttributeOptions

= {}) { + return (model: HalModel

, propertyName: string) => { + const attributeOptions: AttributeOptions

= deepmergeWrapper( + DEFAULT_ATTRIBUTE_OPTIONS, + options, + ); const existingAttributeProperties: Array = getObjProperty( model, ATTRIBUTE_PROPERTIES_METADATA_KEY, diff --git a/projects/ngx-hal/src/lib/decorators/datastore-config.decorator.ts b/projects/ngx-hal/src/lib/decorators/datastore-config.decorator.ts index ca56dc1..39e1ed4 100644 --- a/projects/ngx-hal/src/lib/decorators/datastore-config.decorator.ts +++ b/projects/ngx-hal/src/lib/decorators/datastore-config.decorator.ts @@ -1,10 +1,11 @@ -import { DatastoreOptions } from '../interfaces/datastore-options.interface'; +import { Pagination } from '../classes/pagination'; import { HAL_DATASTORE_DOCUMENT_CLASS_METADATA_KEY } from '../constants/metadata.constant'; +import { setObjProperty } from '../helpers/metadata/metadata.helper'; +import { DatastoreOptions } from '../interfaces/datastore-options.interface'; import { DEFAULT_NETWORK_CONFIG, NetworkConfig } from '../interfaces/network-config.interface'; import { deepmergeWrapper } from '../utils/deepmerge-wrapper'; -import { setObjProperty } from '../helpers/metadata/metadata.helper'; -export function DatastoreConfig(config: DatastoreOptions) { +export function DatastoreConfig

(config: DatastoreOptions

) { return function (target: any) { const networkConfig = deepmergeWrapper( DEFAULT_NETWORK_CONFIG, diff --git a/projects/ngx-hal/src/lib/decorators/has-many.decorator.ts b/projects/ngx-hal/src/lib/decorators/has-many.decorator.ts index 1cca174..0b2266f 100644 --- a/projects/ngx-hal/src/lib/decorators/has-many.decorator.ts +++ b/projects/ngx-hal/src/lib/decorators/has-many.decorator.ts @@ -1,3 +1,4 @@ +import { Pagination } from '../classes/pagination'; import { HAS_MANY_PROPERTIES_METADATA_KEY } from '../constants/metadata.constant'; import { ModelProperty } from '../enums/model-property.enum'; import { getObjProperty } from '../helpers/metadata/metadata.helper'; @@ -7,9 +8,9 @@ import { HasManyModelProperty } from '../interfaces/model-property.interface'; import { HalModel } from '../models/hal.model'; import { deepmergeWrapper } from '../utils/deepmerge-wrapper'; -export function HasMany(options: HasManyOptions) { - return (model: HalModel, propertyName: string) => { - const hasManyOptions: HasManyOptions = deepmergeWrapper(DEFAULT_HAS_MANY_OPTIONS, options); +export function HasMany

(options: HasManyOptions

) { + return (model: HalModel

, propertyName: string) => { + const hasManyOptions: HasManyOptions

= deepmergeWrapper(DEFAULT_HAS_MANY_OPTIONS, options); const existingHasManyProperties: Array = getObjProperty( model, diff --git a/projects/ngx-hal/src/lib/decorators/has-one.decorator.ts b/projects/ngx-hal/src/lib/decorators/has-one.decorator.ts index 2c322a8..acbfbfc 100644 --- a/projects/ngx-hal/src/lib/decorators/has-one.decorator.ts +++ b/projects/ngx-hal/src/lib/decorators/has-one.decorator.ts @@ -1,3 +1,4 @@ +import { Pagination } from '../classes/pagination'; import { HAS_ONE_PROPERTIES_METADATA_KEY } from '../constants/metadata.constant'; import { ModelProperty } from '../enums/model-property.enum'; import { getObjProperty } from '../helpers/metadata/metadata.helper'; @@ -7,9 +8,9 @@ import { HasOneModelProperty } from '../interfaces/model-property.interface'; import { HalModel } from '../models/hal.model'; import { deepmergeWrapper } from '../utils/deepmerge-wrapper'; -export function HasOne(options: HasOneOptions) { - return (model: HalModel, propertyName: string) => { - const hasOneOptions: HasOneOptions = deepmergeWrapper(DEFAULT_HAS_ONE_OPTIONS, options); +export function HasOne

(options: HasOneOptions

) { + return (model: HalModel

, propertyName: string) => { + const hasOneOptions: HasOneOptions

= deepmergeWrapper(DEFAULT_HAS_ONE_OPTIONS, options); const existingHasOneProperties: Array = getObjProperty( model, diff --git a/projects/ngx-hal/src/lib/decorators/header-attribute.decorator.ts b/projects/ngx-hal/src/lib/decorators/header-attribute.decorator.ts index c367c5e..e72e5a4 100644 --- a/projects/ngx-hal/src/lib/decorators/header-attribute.decorator.ts +++ b/projects/ngx-hal/src/lib/decorators/header-attribute.decorator.ts @@ -1,3 +1,4 @@ +import { Pagination } from '../classes/pagination'; import { HEADER_ATTRIBUTE_PROPERTIES_METADATA_KEY } from '../constants/metadata.constant'; import { ModelProperty as ModelPropertyEnum } from '../enums/model-property.enum'; import { getObjProperty } from '../helpers/metadata/metadata.helper'; @@ -13,9 +14,9 @@ import { import { HalModel } from '../models/hal.model'; import { deepmergeWrapper } from '../utils/deepmerge-wrapper'; -export function HeaderAttribute(options: HeaderAttributeOptions = {}) { - return (model: HalModel, propertyName: string) => { - const headerAttributeOptions: HeaderAttributeOptions = deepmergeWrapper( +export function HeaderAttribute

(options: HeaderAttributeOptions

= {}) { + return (model: HalModel

, propertyName: string) => { + const headerAttributeOptions: HeaderAttributeOptions

= deepmergeWrapper( DEFAULT_HEADER_ATTRIBUTE_OPTIONS, options, ); diff --git a/projects/ngx-hal/src/lib/decorators/link.decorator.ts b/projects/ngx-hal/src/lib/decorators/link.decorator.ts index 9039c42..5412d34 100644 --- a/projects/ngx-hal/src/lib/decorators/link.decorator.ts +++ b/projects/ngx-hal/src/lib/decorators/link.decorator.ts @@ -1,3 +1,4 @@ +import { Pagination } from '../classes/pagination'; import { LINK_PROPERTIES_METADATA_KEY } from '../constants/metadata.constant'; import { ModelProperty } from '../enums/model-property.enum'; import { getObjProperty } from '../helpers/metadata/metadata.helper'; @@ -6,8 +7,8 @@ import { LinkRelationshipOptions } from '../interfaces/link-relationship-options import { LinkProperty } from '../interfaces/model-property.interface'; import { HalModel } from '../models/hal.model'; -export function Link(options: LinkRelationshipOptions = {}) { - return (model: HalModel, propertyName: string) => { +export function Link

(options: LinkRelationshipOptions = {}) { + return (model: HalModel

, propertyName: string) => { const existingLinkProperties: Array = getObjProperty( model, LINK_PROPERTIES_METADATA_KEY, diff --git a/projects/ngx-hal/src/lib/decorators/model-config.decorator.ts b/projects/ngx-hal/src/lib/decorators/model-config.decorator.ts index c46502b..44d9dbc 100644 --- a/projects/ngx-hal/src/lib/decorators/model-config.decorator.ts +++ b/projects/ngx-hal/src/lib/decorators/model-config.decorator.ts @@ -1,11 +1,12 @@ -import { ModelOptions, DEFAULT_MODEL_OPTIONS } from '../interfaces/model-options.interface'; +import { Pagination } from '../classes/pagination'; import { HAL_MODEL_DOCUMENT_CLASS_METADATA_KEY } from '../constants/metadata.constant'; -import { deepmergeWrapper } from '../utils/deepmerge-wrapper'; import { setObjProperty } from '../helpers/metadata/metadata.helper'; +import { DEFAULT_MODEL_OPTIONS, ModelOptions } from '../interfaces/model-options.interface'; +import { deepmergeWrapper } from '../utils/deepmerge-wrapper'; -export function ModelConfig(config: ModelOptions) { +export function ModelConfig

(config: ModelOptions

) { return function (target: any) { - const configValue = deepmergeWrapper(DEFAULT_MODEL_OPTIONS, config); + const configValue = deepmergeWrapper>(DEFAULT_MODEL_OPTIONS, config); Object.defineProperty(target.prototype, 'config', { value: configValue, writable: true, diff --git a/projects/ngx-hal/src/lib/interfaces/attribute-options.interface.ts b/projects/ngx-hal/src/lib/interfaces/attribute-options.interface.ts index 883416a..930843d 100644 --- a/projects/ngx-hal/src/lib/interfaces/attribute-options.interface.ts +++ b/projects/ngx-hal/src/lib/interfaces/attribute-options.interface.ts @@ -1,7 +1,8 @@ +import { Pagination } from '../classes/pagination'; import { ModelConstructor, ModelConstructorFn } from '../types/model-constructor.type'; -export interface AttributeOptions { - useClass?: string | ModelConstructor | ModelConstructorFn; +export interface AttributeOptions

{ + useClass?: string | ModelConstructor | ModelConstructorFn; transformResponseValue?: (rawAttribute: any) => any; transformBeforeSave?: (raw: any) => any; externalName?: string; diff --git a/projects/ngx-hal/src/lib/interfaces/datastore-options.interface.ts b/projects/ngx-hal/src/lib/interfaces/datastore-options.interface.ts index e39c63a..5d6f7ba 100644 --- a/projects/ngx-hal/src/lib/interfaces/datastore-options.interface.ts +++ b/projects/ngx-hal/src/lib/interfaces/datastore-options.interface.ts @@ -1,14 +1,14 @@ import { NetworkConfig } from './network-config.interface'; import { HalModel } from '../models/hal.model'; import { HalDocumentConstructor } from '../types/hal-document-construtor.type'; -import { PaginationConstructor } from '../types/pagination.type'; import { CacheStrategy } from '../enums/cache-strategy.enum'; import { HalStorage } from '../classes/hal-storage/hal-storage'; +import { Pagination } from '../classes/pagination'; -export interface DatastoreOptions { +export interface DatastoreOptions

{ network?: NetworkConfig; - halDocumentClass?: HalDocumentConstructor; - paginationClass?: PaginationConstructor; + halDocumentClass?: HalDocumentConstructor, P>; + paginationClass?: { new (...args): P }; cacheStrategy?: CacheStrategy; - storage?: HalStorage; + storage?: HalStorage

; } diff --git a/projects/ngx-hal/src/lib/interfaces/has-many-options.interface.ts b/projects/ngx-hal/src/lib/interfaces/has-many-options.interface.ts index 5f2929f..ba08233 100644 --- a/projects/ngx-hal/src/lib/interfaces/has-many-options.interface.ts +++ b/projects/ngx-hal/src/lib/interfaces/has-many-options.interface.ts @@ -1,7 +1,8 @@ +import { Pagination } from '../classes/pagination'; import { ModelConstructor, ModelConstructorFn } from '../types/model-constructor.type'; -export interface HasManyOptions { - itemsType: string | ModelConstructor | ModelConstructorFn; +export interface HasManyOptions

{ + itemsType: string | ModelConstructor | ModelConstructorFn; includeInPayload?: boolean; externalName?: string; } diff --git a/projects/ngx-hal/src/lib/interfaces/has-one-options.interface.ts b/projects/ngx-hal/src/lib/interfaces/has-one-options.interface.ts index 3f99528..41d1744 100644 --- a/projects/ngx-hal/src/lib/interfaces/has-one-options.interface.ts +++ b/projects/ngx-hal/src/lib/interfaces/has-one-options.interface.ts @@ -1,9 +1,10 @@ +import { Pagination } from '../classes/pagination'; import { ModelConstructor, ModelConstructorFn } from '../types/model-constructor.type'; -export interface HasOneOptions { +export interface HasOneOptions

{ externalName?: string; includeInPayload?: boolean; - propertyClass: string | ModelConstructor | ModelConstructorFn; + propertyClass: string | ModelConstructor | ModelConstructorFn; } export const DEFAULT_HAS_ONE_OPTIONS = { diff --git a/projects/ngx-hal/src/lib/interfaces/header-attribute-options.interface.ts b/projects/ngx-hal/src/lib/interfaces/header-attribute-options.interface.ts index 9054eab..bd70443 100644 --- a/projects/ngx-hal/src/lib/interfaces/header-attribute-options.interface.ts +++ b/projects/ngx-hal/src/lib/interfaces/header-attribute-options.interface.ts @@ -1,7 +1,8 @@ +import { Pagination } from '../classes/pagination'; import { ModelConstructor, ModelConstructorFn } from '../types/model-constructor.type'; -export interface HeaderAttributeOptions { - useClass?: boolean | ModelConstructor | ModelConstructorFn; +export interface HeaderAttributeOptions

{ + useClass?: boolean | ModelConstructor | ModelConstructorFn; transformResponseValue?: (rawAttribute: any) => any; transformBeforeSave?: (raw: any) => any; externalName?: string; diff --git a/projects/ngx-hal/src/lib/interfaces/model-options.interface.ts b/projects/ngx-hal/src/lib/interfaces/model-options.interface.ts index 487a37c..d898035 100644 --- a/projects/ngx-hal/src/lib/interfaces/model-options.interface.ts +++ b/projects/ngx-hal/src/lib/interfaces/model-options.interface.ts @@ -1,15 +1,16 @@ import { HalDocumentConstructor } from '../types/hal-document-construtor.type'; import { HalModel } from '../models/hal.model'; import { NetworkConfig } from './network-config.interface'; +import { Pagination } from '../classes/pagination'; -export class ModelOptions { +export class ModelOptions

{ type: string; endpoint?: string; - halDocumentClass?: HalDocumentConstructor; + halDocumentClass?: HalDocumentConstructor, P>; networkConfig?: NetworkConfig; } -export const DEFAULT_MODEL_OPTIONS: ModelOptions = { +export const DEFAULT_MODEL_OPTIONS: ModelOptions = { type: '', }; diff --git a/projects/ngx-hal/src/lib/models/hal.model.ts b/projects/ngx-hal/src/lib/models/hal.model.ts index 891a1bb..1c07b6f 100644 --- a/projects/ngx-hal/src/lib/models/hal.model.ts +++ b/projects/ngx-hal/src/lib/models/hal.model.ts @@ -50,9 +50,13 @@ import { isFunction } from '../helpers/is-function/is-function.helper'; import { ModelEndpoints } from '../interfaces/model-endpoints.interface'; import { map } from 'rxjs/operators'; import { getArrayObjProperty, getObjProperty } from '../helpers/metadata/metadata.helper'; +import { Pagination } from '../classes/pagination'; -export abstract class HalModel { - private config: ModelOptions = this['config'] || DEFAULT_MODEL_OPTIONS; +export abstract class HalModel< + P extends Pagination, + Datastore extends DatastoreService

= DatastoreService

, +> { + private config: ModelOptions

= this['config'] || DEFAULT_MODEL_OPTIONS; private temporarySelfLink: string = null; private localModelIdentificator: string; private internalHasManyDocumentIdentificators: { [K: string]: string } = {}; @@ -104,7 +108,10 @@ export abstract class HalModel(): HalDocumentConstructor { + public getHalDocumentClass, P extends Pagination>(): HalDocumentConstructor< + T, + P + > { return getObjProperty(this, HAL_MODEL_DOCUMENT_CLASS_METADATA_KEY, null); } @@ -283,7 +290,7 @@ export abstract class HalModel { + this[propertyName].forEach((model: HalModel

) => { if (model && model.selfLink) { hasManyPropertyLinks.push({ href: model.selfLink, @@ -364,7 +371,7 @@ export abstract class HalModel(relationshipName: string): T | HalDocument { + public getRelationship>(relationshipName: string): T | HalDocument { const property: ModelProperty = this.getPropertyData(relationshipName); const isHasOneProperty: boolean = property.type === ModelPropertyEnum.HasOne; @@ -417,7 +424,7 @@ export abstract class HalModel(value: T) { + set>(value: T) { if (isHalModelInstance(value) || !value) { this.replaceRelationshipModel(property.externalName, value); } else { @@ -436,7 +443,7 @@ export abstract class HalModel = this.getHasManyRelationship(property); + const halDocument: HalDocument, P> = this.getHasManyRelationship(property); if (!halDocument) { return; @@ -444,8 +451,10 @@ export abstract class HalModel(value: Array) { - const existingHalDocument: HalDocument = this.getHasManyRelationship(property); + set>(value: Array) { + const existingHalDocument: HalDocument, P> = this.getHasManyRelationship( + property, + ); if (existingHalDocument) { existingHalDocument.models = value; @@ -505,7 +514,7 @@ export abstract class HalModel(property: ModelProperty): T { + private getHasOneRelationship>(property: ModelProperty): T { const relationshipLinks: RawHalLink = this.links[property.externalName]; if (!relationshipLinks) { @@ -517,7 +526,9 @@ export abstract class HalModel(property: ModelProperty): HalDocument { + private getHasManyRelationship>( + property: ModelProperty, + ): HalDocument { const uniqueRelationshipIdentificator: string = this.hasManyDocumentIdentificators[property.externalName]; @@ -525,9 +536,9 @@ export abstract class HalModel = this.datastore.storage.get( + const halDocument: HalDocument = this.datastore.storage.get( uniqueRelationshipIdentificator, - ) as HalDocument; + ) as HalDocument; if (!halDocument) { console.warn(`Has many relationship ${property.name} is not fetched.`); @@ -551,7 +562,7 @@ export abstract class HalModel( + private replaceRelationshipModel>( relationshipName: string, relationshipModel: T, ): void { @@ -579,15 +590,15 @@ export abstract class HalModel): boolean { return property.type === ModelPropertyEnum.HasOne; } - private isHasManyProperty(property: ModelOptions): boolean { + private isHasManyProperty(property: ModelOptions

): boolean { return property.type === ModelPropertyEnum.HasMany; } - public populateModelMetadata(sourceModel: K) { + public populateModelMetadata>(sourceModel: K) { this.resource = sourceModel.resource; this.rawResponse = sourceModel.rawResponse; this.parseAttributes(this.resource); diff --git a/projects/ngx-hal/src/lib/services/datastore/datastore.service.ts b/projects/ngx-hal/src/lib/services/datastore/datastore.service.ts index b1e9e15..02d108e 100644 --- a/projects/ngx-hal/src/lib/services/datastore/datastore.service.ts +++ b/projects/ngx-hal/src/lib/services/datastore/datastore.service.ts @@ -15,7 +15,6 @@ import { RawHalResource } from '../../interfaces/raw-hal-resource.interface'; import { ModelProperty, AttributeModelProperty } from '../../interfaces/model-property.interface'; import { ModelProperty as ModelPropertyEnum } from '../../enums/model-property.enum'; import { RawHalLink } from '../../interfaces/raw-hal-link.interface'; -import { PaginationConstructor } from '../../types/pagination.type'; import { getResponseHeader } from '../../utils/get-response-headers/get-response-header.util'; import { CacheStrategy } from '../../enums/cache-strategy.enum'; import { createHalStorage } from '../../classes/hal-storage/hal-storage-factory'; @@ -39,25 +38,29 @@ import { isString } from '../../utils/is-string/is-string.util'; import { isFunction } from '../../helpers/is-function/is-function.helper'; import { populateTemplatedUrl } from '../../helpers/populate-templated-url/populate-templated-url.helper'; import { getObjProperty } from '../../helpers/metadata/metadata.helper'; +import { Pagination } from '../../classes/pagination'; @Injectable() -export class DatastoreService { +export class DatastoreService

{ public networkConfig: NetworkConfig = this['networkConfig'] || DEFAULT_NETWORK_CONFIG; private _cacheStrategy: CacheStrategy; // tslint:disable-next-line - private _storage: HalStorage; // set by Config decorator + private _storage: HalStorage

; // set by Config decorator private internalStorage = createHalStorage(this.cacheStrategy, this.halStorage); protected httpParamsOptions?: object; - public paginationClass: PaginationConstructor; + public paginationClass: { new (...args): P }; public modelTypes = []; constructor(public http: HttpClient) {} - private getHalDocumentClass(): HalDocumentConstructor { + private getHalDocumentClass< + T extends HalModel

, + P extends Pagination, + >(): HalDocumentConstructor { return getObjProperty(this, HAL_DATASTORE_DOCUMENT_CLASS_METADATA_KEY, null) || HalDocument; } - public buildUrl(model?: HalModel): string { + public buildUrl(model?: HalModel

): string { const hostUrl: string = this.buildHostUrl(model); const urlParts: Array = [hostUrl, model ? model.endpoint : null]; @@ -69,22 +72,22 @@ export class DatastoreService { return urlParts.filter((urlPart) => urlPart).join('/'); } - public createHalDocument( + public createHalDocument>( rawResource: RawHalResource, - modelClass: ModelConstructor | ModelConstructorFn, + modelClass: ModelConstructor | ModelConstructorFn, rawResponse?: HttpResponse, - ): HalDocument { - const propertyClass: ModelConstructor = isFunction(modelClass) - ? (modelClass as ModelConstructorFn)(rawResource) - : (modelClass as ModelConstructor); + ): HalDocument { + const propertyClass: ModelConstructor = isFunction(modelClass) + ? (modelClass as ModelConstructorFn)(rawResource) + : (modelClass as ModelConstructor); const representantiveModel: T = new propertyClass({}, this); const halDocumentClass = - representantiveModel.getHalDocumentClass() || this.getHalDocumentClass(); + representantiveModel.getHalDocumentClass() || this.getHalDocumentClass(); return new halDocumentClass(rawResource, rawResponse, propertyClass, this); } - public findOne( - modelClass: ModelConstructor, + public findOne>( + modelClass: ModelConstructor, modelId: string, includeRelationships: Array = [], requestOptions: RequestOptions = {}, @@ -110,7 +113,7 @@ export class DatastoreService { ); } - public fetchModelRelationships( + public fetchModelRelationships>( model: T, relationshipNames: RelationshipRequestDescriptor | Array, requestOptions: RequestOptions = {}, @@ -132,7 +135,7 @@ export class DatastoreService { return combineLatest(relationships$).pipe(map(() => model)); } - private fetchRelationships( + private fetchRelationships>( model: T, relationshipDescriptors: Array, requestOptions: RequestOptions = {}, // "global" options for all requests @@ -162,7 +165,7 @@ export class DatastoreService { // Checks if the relationship is already embdedded inside the emdedded property, or // as a part of attribute properties const embeddedRelationship: RawHalResource = model.getEmbeddedResource(relationshipName); - let fetchedModels: T | HalDocument; + let fetchedModels: T | HalDocument; if (embeddedRelationship) { fetchedModels = this.processRawResource( @@ -261,69 +264,69 @@ export class DatastoreService { ); } - private handleGetRequestWithRelationships( + private handleGetRequestWithRelationships>( url: string, requestsOptions: RequestsOptions, - modelClass: ModelConstructor | ModelConstructorFn, + modelClass: ModelConstructor | ModelConstructorFn, isSingleResource: true, includeRelationships: Array, ): Observable; - private handleGetRequestWithRelationships( + private handleGetRequestWithRelationships>( url: string, requestsOptions: RequestsOptions, - modelClass: ModelConstructor | ModelConstructorFn, + modelClass: ModelConstructor | ModelConstructorFn, isSingleResource: false, includeRelationships: Array, - ): Observable>; - private handleGetRequestWithRelationships( + ): Observable>; + private handleGetRequestWithRelationships>( url: string, requestsOptions: RequestsOptions, - modelClass: ModelConstructor | ModelConstructorFn, + modelClass: ModelConstructor | ModelConstructorFn, isSingleResource: boolean, includeRelationships: Array, - ): Observable>; - private handleGetRequestWithRelationships( + ): Observable>; + private handleGetRequestWithRelationships>( url: string, requestsOptions: RequestsOptions, - modelClass: ModelConstructor | ModelConstructorFn, + modelClass: ModelConstructor | ModelConstructorFn, isSingleResource: true, includeRelationships: Array, - fetchedModels: T | HalDocument, + fetchedModels: T | HalDocument, ): Observable; - private handleGetRequestWithRelationships( + private handleGetRequestWithRelationships>( url: string, requestsOptions: RequestsOptions, - modelClass: ModelConstructor | ModelConstructorFn, + modelClass: ModelConstructor | ModelConstructorFn, isSingleResource: boolean, includeRelationships: Array, fetchedModels: T, ): Observable; - private handleGetRequestWithRelationships( + private handleGetRequestWithRelationships>( url: string, requestsOptions: RequestsOptions, - modelClass: ModelConstructor | ModelConstructorFn, + modelClass: ModelConstructor | ModelConstructorFn, isSingleResource: boolean, includeRelationships: Array, - fetchedModels: HalDocument, - ): Observable>; - private handleGetRequestWithRelationships( + fetchedModels: HalDocument, + ): Observable>; + private handleGetRequestWithRelationships>( url: string, requestsOptions: RequestsOptions, - modelClass: ModelConstructor | ModelConstructorFn, + modelClass: ModelConstructor | ModelConstructorFn, isSingleResource: boolean, includeRelationships: Array, - fetchedModels: T | HalDocument, + fetchedModels: T | HalDocument, storePartialModels?: boolean, - ): Observable>; - private handleGetRequestWithRelationships( + ): Observable>; + private handleGetRequestWithRelationships>( url: string, requestsOptions: RequestsOptions, - modelClass: ModelConstructor | ModelConstructorFn, + modelClass: ModelConstructor | ModelConstructorFn, isSingleResource: boolean, includeRelationships: Array = [], - fetchedModels: T | HalDocument = null, + fetchedModels: T | HalDocument = null, storePartialModels?: boolean, - ): Observable> { + ): Observable> { let models$; if (fetchedModels) { @@ -340,10 +343,10 @@ export class DatastoreService { if (includeRelationships.length) { return models$.pipe( - flatMap((model: T | HalDocument) => { + flatMap((model: T | HalDocument) => { const models: Array = isSingleResource ? ([model] as Array) - : (model as HalDocument).models; + : (model as HalDocument).models; const relationshipCalls: Array> = this.triggerFetchingModelRelationships( models, @@ -363,14 +366,14 @@ export class DatastoreService { return models$; } - private makeGetRequestWrapper( + private makeGetRequestWrapper>( url: string, requestsOptions: RequestsOptions, - modelClass: ModelConstructor | ModelConstructorFn, + modelClass: ModelConstructor | ModelConstructorFn, isSingleResource: boolean, storePartialModels?: boolean, - ): Observable | T> { - const originalGetRequest$: Observable> = this.makeGetRequest( + ): Observable | T> { + const originalGetRequest$: Observable> = this.makeGetRequest( url, requestsOptions.mainRequest, modelClass, @@ -400,7 +403,7 @@ export class DatastoreService { return originalGetRequest$; } - private triggerFetchingModelRelationships( + private triggerFetchingModelRelationships>( models: Array, includeRelationships: Array, requestOptions?: RequestOptions, @@ -419,75 +422,75 @@ export class DatastoreService { return modelRelationshipCalls; } - public find( - modelClass: ModelConstructor, + public find>( + modelClass: ModelConstructor, params: object | { [param: string]: string | string[] } | HttpParams, ): Observable>; - public find( - modelClass: ModelConstructor, + public find>( + modelClass: ModelConstructor, params: object | { [param: string]: string | string[] } | HttpParams, includeMeta: false, ): Observable>; - public find( - modelClass: ModelConstructor, + public find>( + modelClass: ModelConstructor, params: object | { [param: string]: string | string[] } | HttpParams, includeMeta: false, includeRelationships: Array, ): Observable>; - public find( - modelClass: ModelConstructor, + public find>( + modelClass: ModelConstructor, params: object | { [param: string]: string | string[] } | HttpParams, includeMeta: true, includeRelationships: Array, - ): Observable>; - public find( - modelClass: ModelConstructor, + ): Observable>; + public find>( + modelClass: ModelConstructor, params: object | { [param: string]: string | string[] } | HttpParams, includeMeta: true, includeRelationships: Array, requestOptions: RequestOptions, - ): Observable>; - public find( - modelClass: ModelConstructor, + ): Observable>; + public find>( + modelClass: ModelConstructor, params: object | { [param: string]: string | string[] } | HttpParams, includeMeta: true, includeRelationships: Array, requestOptions: RequestOptions, customUrl?: string, - ): Observable>; - public find( - modelClass: ModelConstructor, + ): Observable>; + public find>( + modelClass: ModelConstructor, params: object | { [param: string]: string | string[] } | HttpParams, includeMeta: false, includeRelationships: Array, requestOptions: RequestOptions, ): Observable>; - public find( - modelClass: ModelConstructor, + public find>( + modelClass: ModelConstructor, params: object | { [param: string]: string | string[] } | HttpParams, includeMeta: boolean, includeRelationships: Array, requestOptions: RequestOptions, - ): Observable | HalDocument>; - public find( - modelClass: ModelConstructor, + ): Observable | HalDocument>; + public find>( + modelClass: ModelConstructor, params: object | { [param: string]: string | string[] } | HttpParams, includeMeta: boolean, includeRelationships: Array, requestOptions: RequestOptions, customUrl: string, - ): Observable | HalDocument>; - public find( - modelClass: ModelConstructor, + ): Observable | HalDocument>; + public find>( + modelClass: ModelConstructor, params: object | { [param: string]: string | string[] } | HttpParams, includeMeta: boolean, includeRelationships: Array, requestOptions: RequestOptions, customUrl: string, subsequentRequestsOptions: RequestOptions, - ): Observable | HalDocument>; - public find( - modelClass: ModelConstructor, + ): Observable | HalDocument>; + public find>( + modelClass: ModelConstructor, params: object | { [param: string]: string | string[] } | HttpParams, includeMeta: true, includeRelationships: Array, @@ -495,9 +498,9 @@ export class DatastoreService { customUrl: string, subsequentRequestsOptions: RequestOptions, storePartialModels?: boolean, - ): Observable>; - public find( - modelClass: ModelConstructor, + ): Observable>; + public find>( + modelClass: ModelConstructor, params: object | { [param: string]: string | string[] } | HttpParams, includeMeta: false, includeRelationships: Array, @@ -506,8 +509,8 @@ export class DatastoreService { subsequentRequestsOptions: RequestOptions, storePartialModels?: boolean, ): Observable; - public find( - modelClass: ModelConstructor, + public find>( + modelClass: ModelConstructor, params: object | { [param: string]: string | string[] } | HttpParams, includeMeta: boolean, includeRelationships: Array, @@ -515,9 +518,9 @@ export class DatastoreService { customUrl: string, subsequentRequestsOptions: RequestOptions, storePartialModels?: boolean, - ): Observable | HalDocument>; - public find( - modelClass: ModelConstructor, + ): Observable | HalDocument>; + public find>( + modelClass: ModelConstructor, params: object | { [param: string]: string | string[] } | HttpParams = {}, includeMeta: boolean = false, includeRelationships: Array = [], @@ -525,7 +528,7 @@ export class DatastoreService { customUrl?: string, subsequentRequestsOptions: RequestOptions = {}, storePartialModels: boolean = false, - ): Observable | Array> { + ): Observable | Array> { const url: string = customUrl || this.buildModelUrl(modelClass); const subsequentOptions: RequestOptions = deepmergeWrapper({}, subsequentRequestsOptions); @@ -553,7 +556,7 @@ export class DatastoreService { null, storePartialModels, ).pipe( - flatMap((halDocument: HalDocument) => { + flatMap((halDocument: HalDocument) => { if (relationshipDescriptors.length) { return of(halDocument); } @@ -569,13 +572,13 @@ export class DatastoreService { }), ); }), - map((halDocument: HalDocument) => (includeMeta ? halDocument : halDocument.models)), + map((halDocument: HalDocument) => (includeMeta ? halDocument : halDocument.models)), ); } - public save( + public save>( model: T, - modelClass: ModelConstructor, + modelClass: ModelConstructor, requestOptions?: RequestOptions, saveOptions: CustomOptions = {}, ): Observable { @@ -630,7 +633,7 @@ export class DatastoreService { ); } - private updateModelWithChangedProperties(model: T, payload: object) { + private updateModelWithChangedProperties>(model: T, payload: object) { Object.keys(payload).forEach((externalPropertyName: string) => { const property: AttributeModelProperty = model.getPropertyData(externalPropertyName); @@ -644,7 +647,7 @@ export class DatastoreService { }); } - public update( + public update>( model: T, requestOptions?: RequestOptions, updateOptions: CustomOptions = {}, @@ -677,7 +680,7 @@ export class DatastoreService { ); } - public delete( + public delete>( model: T, requestOptions?: RequestOptions, updateOptions: CustomOptions = {}, @@ -700,59 +703,59 @@ export class DatastoreService { return this.internalStorage; } - public request( + public request>( method: string, url: string, requestOptions: RequestOptions, - modelClass: ModelConstructor, + modelClass: ModelConstructor, singleResource: false, - ): Observable>; - public request( + ): Observable>; + public request>( method: string, url: string, requestOptions: RequestOptions, - modelClass: ModelConstructor, + modelClass: ModelConstructor, singleResource: false, includeNetworkConfig: false, - ): Observable>; - public request( + ): Observable>; + public request>( method: string, url: string, requestOptions: RequestOptions, - modelClass: ModelConstructor, + modelClass: ModelConstructor, singleResource: false, includeNetworkConfig: true, - ): Observable>; - public request( + ): Observable>; + public request>( method: string, url: string, requestOptions: RequestOptions, - modelClass: ModelConstructor, + modelClass: ModelConstructor, singleResource: true, ): Observable; - public request( + public request>( method: string, url: string, requestOptions: RequestOptions, - modelClass: ModelConstructor, + modelClass: ModelConstructor, singleResource: boolean, - ): Observable | T>; - public request( + ): Observable | T>; + public request>( method: string, url: string, requestOptions: RequestOptions, - modelClass: ModelConstructor, + modelClass: ModelConstructor, singleResource: boolean, includeNetworkConfig?: boolean, - ): Observable | T>; - public request( + ): Observable | T>; + public request>( method: string, url: string, requestOptions: RequestOptions, - modelClass: ModelConstructor, + modelClass: ModelConstructor, singleResource: boolean, includeNetworkConfig: boolean = true, - ): Observable | T> { + ): Observable | T> { const customUrl: string = includeNetworkConfig ? `${this.buildHostUrl(new modelClass({}, this))}/${url}` : url; @@ -765,32 +768,32 @@ export class DatastoreService { } } - private makeGetRequest( + private makeGetRequest>( url: string, requestOptions: RequestOptions, - modelClass: ModelConstructor | ModelConstructorFn, + modelClass: ModelConstructor | ModelConstructorFn, singleResource: false, - ): Observable>; - private makeGetRequest( + ): Observable>; + private makeGetRequest>( url: string, requestOptions: RequestOptions, - modelClass: ModelConstructor | ModelConstructorFn, + modelClass: ModelConstructor | ModelConstructorFn, singleResource: true, ): Observable; - private makeGetRequest( + private makeGetRequest>( url: string, requestOptions: RequestOptions, - modelClass: ModelConstructor | ModelConstructorFn, + modelClass: ModelConstructor | ModelConstructorFn, singleResource: boolean, storePartialModels?: boolean, - ): Observable | T>; - private makeGetRequest( + ): Observable | T>; + private makeGetRequest>( url: string, requestOptions: RequestOptions, - modelClass: ModelConstructor | ModelConstructorFn, + modelClass: ModelConstructor | ModelConstructorFn, singleResource: boolean, storePartialModels?: boolean, - ): Observable | T> { + ): Observable | T> { const { cleanUrl, requestOptions: options, @@ -823,7 +826,7 @@ export class DatastoreService { ); } - public head(url: string, requestOptions: RequestOptions): Observable { + public head>(url: string, requestOptions: RequestOptions): Observable { const { cleanUrl, requestOptions: options } = this.extractRequestInfo(url, requestOptions); return this.http.head(cleanUrl, options as any); @@ -880,7 +883,7 @@ export class DatastoreService { return params; } - private makePostRequest( + private makePostRequest>( url: string, payload: object, requestOptions?: RequestOptions, @@ -892,7 +895,7 @@ export class DatastoreService { return this.http.post(cleanUrl, payload, options as { [K: string]: any }); } - private makePutRequest( + private makePutRequest>( url: string, payload: object, requestOptions?: RequestOptions, @@ -904,7 +907,7 @@ export class DatastoreService { return this.http.put(cleanUrl, payload, options as { [K: string]: any }); } - private makePatchRequest( + private makePatchRequest>( url: string, payload: object, requestOptions?: RequestOptions, @@ -916,7 +919,7 @@ export class DatastoreService { return this.http.patch(cleanUrl, payload, options as { [K: string]: any }); } - private makeDeleteRequest( + private makeDeleteRequest>( url: string, requestOptions?: RequestOptions, ): Observable { @@ -927,51 +930,55 @@ export class DatastoreService { return this.http.delete(cleanUrl, options as { [K: string]: any }); } - private processRawResource( + private processRawResource>( rawResource: RawHalResource, - modelClass: ModelConstructor | ModelConstructorFn, + modelClass: ModelConstructor | ModelConstructorFn, isSingleResource: false, response: HttpResponse, - ): HalDocument; - private processRawResource( + ): HalDocument; + private processRawResource>( rawResource: RawHalResource, - modelClass: ModelConstructor | ModelConstructorFn, + modelClass: ModelConstructor | ModelConstructorFn, isSingleResource: true, response: HttpResponse, ): T; - private processRawResource( + private processRawResource>( rawResource: RawHalResource, - modelClass: ModelConstructor | ModelConstructorFn, + modelClass: ModelConstructor | ModelConstructorFn, isSingleResource: boolean, response: HttpResponse, - ): T | HalDocument; - private processRawResource( + ): T | HalDocument; + private processRawResource>( rawResource: RawHalResource, - modelClass: ModelConstructor | ModelConstructorFn, + modelClass: ModelConstructor | ModelConstructorFn, isSingleResource: boolean, response: HttpResponse, url?: string, savePartialModels?: boolean, - ): T | HalDocument; - private processRawResource( + ): T | HalDocument; + private processRawResource>( rawResource: RawHalResource, - modelClass: ModelConstructor | ModelConstructorFn, + modelClass: ModelConstructor | ModelConstructorFn, isSingleResource: boolean, response: HttpResponse, url?: string, savePartialModels?: boolean, - ): T | HalDocument { + ): T | HalDocument { if (isSingleResource) { - const propertyClass: ModelConstructor = isFunction(modelClass) - ? (modelClass as ModelConstructorFn)(rawResource) - : (modelClass as ModelConstructor); + const propertyClass: ModelConstructor = isFunction(modelClass) + ? (modelClass as ModelConstructorFn)(rawResource) + : (modelClass as ModelConstructor); const model: T = new propertyClass(rawResource, this, response); this.populateResourceWithRelationshipIndentificators(model); this.storage.save(model, response, [url]); return model; } - const halDocument: HalDocument = this.createHalDocument(rawResource, modelClass, response); + const halDocument: HalDocument = this.createHalDocument( + rawResource, + modelClass, + response, + ); this.storage.saveAll(halDocument.models, savePartialModels); @@ -983,7 +990,7 @@ export class DatastoreService { return halDocument; } - private buildModelUrl(modelClass: ModelConstructor, modelId?: string): string { + private buildModelUrl(modelClass: ModelConstructor, P>, modelId?: string): string { const model = new modelClass({}, this); if (modelId && model.modelEndpoints?.singleResourceEndpoint) { @@ -1000,16 +1007,16 @@ export class DatastoreService { return response.body; } - private populateResourceWithRelationshipIndentificators(model: T): void { + private populateResourceWithRelationshipIndentificators>(model: T): void { const localResource: T = this.storage.get(model.uniqueModelIdentificator); if (localResource) { model.hasManyDocumentIdentificators = localResource.hasManyDocumentIdentificators; } } - private fetchEmbeddedListItems( - halDocument: HalDocument, - modelClass: ModelConstructor | ModelConstructorFn, + private fetchEmbeddedListItems>( + halDocument: HalDocument, + modelClass: ModelConstructor | ModelConstructorFn, includeRelationships: Array = [], requestOptions: RequestOptions = {}, ): Observable> { @@ -1057,13 +1064,11 @@ export class DatastoreService { return combineLatest(modelCalls); } - private buildHostUrl(model?: HalModel): string { - // tslint:disable-next-line:max-line-length + private buildHostUrl(model?: HalModel

): string { const baseUrl: string = model && model.networkConfig && model.networkConfig.baseUrl ? model.networkConfig.baseUrl : this.networkConfig.baseUrl; - // tslint:disable-next-line:max-line-length const networkEndpoint: string = model && model.networkConfig && model.networkConfig.endpoint ? model.networkConfig.endpoint @@ -1072,7 +1077,7 @@ export class DatastoreService { return [baseUrl, networkEndpoint].filter((urlPart) => urlPart).join('/'); } - private defaultUrlBuildFunction(model: T, urlFromModel: string): string { + private defaultUrlBuildFunction>(model: T, urlFromModel: string): string { if (model.isSaved && model.selfLink) { return model.selfLink; } @@ -1094,12 +1099,12 @@ export class DatastoreService { return this._cacheStrategy; } - private get halStorage(): HalStorage { + private get halStorage(): HalStorage

{ return this._storage; } - public findModelClassByType(modelType: string): ModelConstructor { - const modelClass: ModelConstructor = this.modelTypes.find( + public findModelClassByType>(modelType: string): ModelConstructor { + const modelClass: ModelConstructor = this.modelTypes.find( (modelClass) => modelClass.modelType === modelType, ); @@ -1112,8 +1117,8 @@ export class DatastoreService { return modelClass; } - public createModel( - modelClass: ModelConstructor, + public createModel>( + modelClass: ModelConstructor, recordData: object = {}, ): T { const rawRecordData: object = Object.assign({}, recordData); diff --git a/projects/ngx-hal/src/lib/services/model-service/model.service.ts b/projects/ngx-hal/src/lib/services/model-service/model.service.ts index a7c18db..d18b0c5 100644 --- a/projects/ngx-hal/src/lib/services/model-service/model.service.ts +++ b/projects/ngx-hal/src/lib/services/model-service/model.service.ts @@ -6,9 +6,13 @@ import { RequestOptions } from '../../types/request-options.type'; import { HalDocument } from '../../classes/hal-document'; import { ModelConstructor } from '../../types/model-constructor.type'; import { RelationshipRequestDescriptor } from '../../types/relationship-request-descriptor.type'; +import { Pagination } from '../../classes/pagination'; -export abstract class ModelService { - constructor(protected datastore: DatastoreService, private modelClass: ModelConstructor) {} +export abstract class ModelService, P extends Pagination> { + constructor( + protected datastore: DatastoreService

, + private modelClass: ModelConstructor, + ) {} public findOne( modelId: string, @@ -37,7 +41,7 @@ export abstract class ModelService { public find( params: object | { [param: string]: string | string[] } | HttpParams, includeMeta: true, - ): Observable>; + ): Observable>; public find( params: object | { [param: string]: string | string[] } | HttpParams, includeMeta: false, @@ -47,7 +51,7 @@ export abstract class ModelService { params: object | { [param: string]: string | string[] } | HttpParams, includeMeta: true, includeRelationships: Array, - ): Observable>; + ): Observable>; public find( params: object | { [param: string]: string | string[] } | HttpParams, includeMeta: false, @@ -59,7 +63,7 @@ export abstract class ModelService { includeMeta: true, includeRelationships: Array, requestOptions: RequestOptions, - ): Observable>; + ): Observable>; public find( params: object | { [param: string]: string | string[] } | HttpParams, includeMeta: true, @@ -68,7 +72,7 @@ export abstract class ModelService { subsequentRequestsOptions: RequestOptions, customUrl?: string, storePartialModels?: boolean, - ): Observable>; + ): Observable>; public find( params: object | { [param: string]: string | string[] } | HttpParams, includeMeta: false, @@ -86,7 +90,7 @@ export abstract class ModelService { subsequentRequestsOptions: RequestOptions = {}, customUrl?: string, storePartialModels?: boolean, - ): Observable | Array> { + ): Observable | Array> { return this.datastore.find( this.modelClass, params, diff --git a/projects/ngx-hal/src/lib/types/hal-document-construtor.type.ts b/projects/ngx-hal/src/lib/types/hal-document-construtor.type.ts index d9b88c3..92b6bea 100644 --- a/projects/ngx-hal/src/lib/types/hal-document-construtor.type.ts +++ b/projects/ngx-hal/src/lib/types/hal-document-construtor.type.ts @@ -1,6 +1,7 @@ import { HalModel } from '../models/hal.model'; import { HalDocument } from '../classes/hal-document'; +import { Pagination } from '../classes/pagination'; -export type HalDocumentConstructor = { - new (...args): HalDocument; +export type HalDocumentConstructor, P extends Pagination> = { + new (...args): HalDocument; }; diff --git a/projects/ngx-hal/src/lib/types/model-constructor.type.ts b/projects/ngx-hal/src/lib/types/model-constructor.type.ts index b1c0c6b..0f1f6c6 100644 --- a/projects/ngx-hal/src/lib/types/model-constructor.type.ts +++ b/projects/ngx-hal/src/lib/types/model-constructor.type.ts @@ -1,4 +1,7 @@ +import { Pagination } from '../classes/pagination'; import { HalModel } from '../models/hal.model'; -export type ModelConstructor = { new (...args): T }; -export type ModelConstructorFn = (rawPropertyValue: any) => ModelConstructor; +export type ModelConstructor, P extends Pagination> = { new (...args): T }; +export type ModelConstructorFn, P extends Pagination> = ( + rawPropertyValue: any, +) => ModelConstructor; diff --git a/projects/ngx-hal/src/lib/types/pagination.type.ts b/projects/ngx-hal/src/lib/types/pagination.type.ts deleted file mode 100644 index ef8d5d8..0000000 --- a/projects/ngx-hal/src/lib/types/pagination.type.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { Pagination } from '../classes/pagination'; - -export type PaginationConstructor = { new (...args): Pagination };