Adjusted the ModelRepository to make repository methods more type-safe

This commit is contained in:
Christopher Allford 2020-10-01 14:31:55 -07:00
parent f2dda16c40
commit 787040db4c
7 changed files with 327 additions and 137 deletions

View File

@ -1,5 +1,17 @@
import { Model } from '../../models/model';
import { ModelRepository } from '../model-repository';
import {
CreatesModels,
DeletesChildModels,
DeletesModels,
ListsChildModels,
ListsModels,
ModelRepository,
ModelRepositoryParams,
ReadsChildModels,
ReadsModels,
UpdatesChildModels,
UpdatesModels,
} from '../model-repository';
class DummyModel extends Model {
public name: string = '';
@ -9,20 +21,59 @@ class DummyModel extends Model {
Object.assign( this, partial );
}
}
type DummyModelParams = ModelRepositoryParams< DummyModel, undefined, { search: string }, 'name' >
class DummyChildModel extends Model {
public childName: string = '';
public constructor( partial?: Partial< DummyModel > ) {
super();
Object.assign( this, partial );
}
}
type DummyChildParams = ModelRepositoryParams< DummyChildModel, { parent: string }, { childSearch: string }, 'childName' >
describe( 'ModelRepository', () => {
it( 'should list', async () => {
const model = new DummyModel();
const callback = jest.fn().mockResolvedValue( [ model ] );
const repository = new ModelRepository< DummyModel, { search: string } >( callback, null, null, null, null );
const repository: ListsModels< DummyModelParams > = new ModelRepository< DummyModelParams >(
callback,
null,
null,
null,
null,
);
const listed = await repository.list( { search: 'test' } );
expect( listed ).toContain( model );
expect( callback ).toHaveBeenCalledWith( { search: 'test' } );
} );
it( 'should list child', async () => {
const model = new DummyChildModel();
const callback = jest.fn().mockResolvedValue( [ model ] );
const repository: ListsChildModels< DummyChildParams > = new ModelRepository< DummyChildParams >(
callback,
null,
null,
null,
null,
);
const listed = await repository.list( { parent: 'test' }, { childSearch: 'test' } );
expect( listed ).toContain( model );
expect( callback ).toHaveBeenCalledWith( { parent: 'test' }, { childSearch: 'test' } );
} );
it( 'should throw error on list without callback', () => {
const repository = new ModelRepository< DummyModel >( null, null, null, null, null );
const repository: ListsModels< DummyModelParams > = new ModelRepository< DummyModelParams >(
null,
null,
null,
null,
null,
);
expect( () => repository.list() ).toThrowError( /not supported/i );
} );
@ -30,15 +81,43 @@ describe( 'ModelRepository', () => {
it( 'should create', async () => {
const model = new DummyModel();
const callback = jest.fn().mockResolvedValue( model );
const repository = new ModelRepository< DummyModel >( null, callback, null, null, null );
const repository: CreatesModels< DummyModelParams > = new ModelRepository< DummyModelParams >(
null,
callback,
null,
null,
null,
);
const created = await repository.create( { name: 'test' } );
expect( created ).toBe( model );
expect( callback ).toHaveBeenCalledWith( { name: 'test' } );
} );
it( 'should create child', async () => {
const model = new DummyChildModel();
const callback = jest.fn().mockResolvedValue( model );
const repository: CreatesModels< DummyChildParams > = new ModelRepository< DummyChildParams >(
null,
callback,
null,
null,
null,
);
const created = await repository.create( { childName: 'test' } );
expect( created ).toBe( model );
expect( callback ).toHaveBeenCalledWith( { childName: 'test' } );
} );
it( 'should throw error on create without callback', () => {
const repository = new ModelRepository< DummyModel >( null, null, null, null, null );
const repository: CreatesModels< DummyModelParams > = new ModelRepository< DummyModelParams >(
null,
null,
null,
null,
null,
);
expect( () => repository.create( { name: 'test' } ) ).toThrowError( /not supported/i );
} );
@ -46,15 +125,43 @@ describe( 'ModelRepository', () => {
it( 'should read', async () => {
const model = new DummyModel();
const callback = jest.fn().mockResolvedValue( model );
const repository = new ModelRepository< DummyModel >( null, null, callback, null, null );
const repository: ReadsModels< DummyModelParams > = new ModelRepository< DummyModelParams >(
null,
null,
callback,
null,
null,
);
const created = await repository.read( 1 );
expect( created ).toBe( model );
expect( callback ).toHaveBeenCalledWith( 1 );
} );
it( 'should read child', async () => {
const model = new DummyChildModel();
const callback = jest.fn().mockResolvedValue( model );
const repository: ReadsChildModels< DummyChildParams > = new ModelRepository< DummyChildParams >(
null,
null,
callback,
null,
null,
);
const created = await repository.read( { parent: 'yes' }, 1 );
expect( created ).toBe( model );
expect( callback ).toHaveBeenCalledWith( { parent: 'yes' }, 1 );
} );
it( 'should throw error on read without callback', () => {
const repository = new ModelRepository< DummyModel >( null, null, null, null, null );
const repository: ReadsModels< DummyModelParams > = new ModelRepository< DummyModelParams >(
null,
null,
null,
null,
null,
);
expect( () => repository.read( 1 ) ).toThrowError( /not supported/i );
} );
@ -62,30 +169,85 @@ describe( 'ModelRepository', () => {
it( 'should update', async () => {
const model = new DummyModel();
const callback = jest.fn().mockResolvedValue( model );
const repository = new ModelRepository< DummyModel, void, 'name' >( null, null, null, callback, null );
const repository: UpdatesModels< DummyModelParams > = new ModelRepository< DummyModelParams >(
null,
null,
null,
callback,
null,
);
const updated = await repository.update( 1, { name: 'new-name' } );
expect( updated ).toBe( model );
expect( callback ).toHaveBeenCalledWith( 1, { name: 'new-name' } );
} );
it( 'should update child', async () => {
const model = new DummyChildModel();
const callback = jest.fn().mockResolvedValue( model );
const repository: UpdatesChildModels< DummyChildParams > = new ModelRepository< DummyChildParams >(
null,
null,
null,
callback,
null,
);
const updated = await repository.update( { parent: 'test' }, 1, { childName: 'new-name' } );
expect( updated ).toBe( model );
expect( callback ).toHaveBeenCalledWith( { parent: 'test' }, 1, { childName: 'new-name' } );
} );
it( 'should throw error on update without callback', () => {
const repository = new ModelRepository< DummyModel, void, 'name' >( null, null, null, null, null );
const repository: UpdatesModels< DummyModelParams > = new ModelRepository< DummyModelParams >(
null,
null,
null,
null,
null,
);
expect( () => repository.update( 1, { name: 'new-name' } ) ).toThrowError( /not supported/i );
} );
it( 'should delete', async () => {
const callback = jest.fn().mockResolvedValue( true );
const repository = new ModelRepository< DummyModel >( null, null, null, null, callback );
const repository: DeletesModels = new ModelRepository< DummyModelParams >(
null,
null,
null,
null,
callback,
);
const success = await repository.delete( 1 );
expect( success ).toBe( true );
expect( callback ).toHaveBeenCalledWith( 1 );
} );
it( 'should delete child', async () => {
const callback = jest.fn().mockResolvedValue( true );
const repository: DeletesChildModels< DummyChildParams > = new ModelRepository< DummyChildParams >(
null,
null,
null,
null,
callback,
);
const success = await repository.delete( { parent: 'yes' }, 1 );
expect( success ).toBe( true );
expect( callback ).toHaveBeenCalledWith( { parent: 'yes' }, 1 );
} );
it( 'should throw error on delete without callback', () => {
const repository = new ModelRepository< DummyModel >( null, null, null, null, null );
const repository: DeletesModels = new ModelRepository< DummyModelParams >(
null,
null,
null,
null,
null,
);
expect( () => repository.delete( 1 ) ).toThrowError( /not supported/i );
} );

View File

@ -1,8 +1,43 @@
import { Model, ModelID, ModelParentID } from '../models/model';
import { Model, ModelID } from '../models/model';
// Helpers for making types easier to work with.
type UpdateParams< T extends Model, U > = [ U ] extends [ keyof T ] ? Pick< T, U > : undefined;
type HasParent< P, T, F > = [ P ] extends [ ModelParentID ] ? T : F;
/**
* An interface for describing the shape of parent identifiers for repositories.
*
* @typedef ModelParentID
* @alias Object.<string,ModelID>
*/
interface ModelParentID {
[ key: number ]: ModelID
}
/**
* This type describes the structure of different kinds of data that is extracted
* for use in the repository to provide type-safety to repository actions.
*/
export interface ModelRepositoryParams<
T extends Model = any,
// @ts-ignore
ParentID extends ModelParentID | undefined = any,
// @ts-ignore
ListParams = any,
// @ts-ignore
UpdateParams extends keyof T = any,
> {
// Since TypeScript's type system is structural we need to add something to this type to prevent
// it from matching with everything else (since it is an empty interface).
thisTypeIsDeclarativeOnly: string;
}
/**
* These helpers will extract information about a model from its repository params to be used in the repository.
*/
type ModelClass< T extends ModelRepositoryParams > = [ T ] extends [ ModelRepositoryParams< infer X > ] ? X : never;
type ParentID< T extends ModelRepositoryParams > = [ T ] extends [ ModelRepositoryParams< any, infer X > ] ? X : never;
type ListParams< T extends ModelRepositoryParams > = [ T ] extends [ ModelRepositoryParams< any, any, infer X > ] ? X : never;
type UpdateParams< T extends ModelRepositoryParams > = [ T ] extends [ ModelRepositoryParams< infer C, any, any, infer X > ] ?
( [ X ] extends [ keyof C ] ? Pick< C, X > : never ) :
never;
type HasParent< T extends ModelRepositoryParams, P, C > = [ ParentID< T > ] extends [ ModelParentID ] ? P : C;
/**
* A callback for listing models using a data source.
@ -13,20 +48,23 @@ type HasParent< P, T, F > = [ P ] extends [ ModelParentID ] ? T : F;
* @template {Model} T
* @template L
*/
export type ListFn< T extends Model, L > = ( params?: L ) => Promise< T[] >;
export type ListFn< T extends ModelRepositoryParams > = ( params?: ListParams< T > ) => Promise< ModelClass< T >[] >;
/**
* A callback for listing child models using a data source.
*
* @callback ListChildFn
* @param {Partial.<T>} parent The parent identifier for the model.
* @param {P} parent The parent identifier for the model.
* @param {L} [params] The list parameters for the query.
* @return {Promise.<Array.<T>>} Resolves to an array of created models.
* @template {Model} T
* @template {ModelParentID} P
* @template L
*/
export type ListChildFn< T extends Model, P extends ModelParentID | undefined, L > = ( parent: P, params?: L ) => Promise< T[] >;
export type ListChildFn< T extends ModelRepositoryParams > = (
parent: ParentID< T >,
params?: ListParams< T >
) => Promise< ModelClass< T >[] >;
/**
* A callback for creating a model using a data source.
@ -36,7 +74,7 @@ export type ListChildFn< T extends Model, P extends ModelParentID | undefined, L
* @return {Promise.<T>} Resolves to the created model.
* @template {Model} T
*/
export type CreateFn< T extends Model > = ( properties: Partial< T > ) => Promise< T >;
export type CreateFn< T extends ModelRepositoryParams > = ( properties: Partial< ModelClass< T > > ) => Promise< ModelClass< T > >;
/**
* A callback for reading a model using a data source.
@ -46,7 +84,7 @@ export type CreateFn< T extends Model > = ( properties: Partial< T > ) => Promis
* @return {Promise.<T>} Resolves to the read model.
* @template {Model} T
*/
export type ReadFn< T extends Model > = ( id: ModelID ) => Promise< T >;
export type ReadFn< T extends ModelRepositoryParams > = ( id: ModelID ) => Promise< ModelClass< T > >;
/**
* A callback for reading a child model using a data source.
@ -58,22 +96,21 @@ export type ReadFn< T extends Model > = ( id: ModelID ) => Promise< T >;
* @template {Model} T
* @template {ModelParentID} P
*/
export type ReadChildFn< T extends Model, P extends ModelParentID | undefined > = ( parent: P, childID: ModelID ) => Promise< T >;
export type ReadChildFn< T extends ModelRepositoryParams > = ( parent: ParentID< T >, childID: ModelID ) => Promise< ModelClass< T > >;
/**
* A callback for updating a model using a data source.
*
* @callback UpdateFn
* @param {ModelID} id The ID of the model.
* @param {Pick.<T,U>} properties The properties to update.
* @param {Partial.<T>} properties The properties to update.
* @return {Promise.<T>} Resolves to the updated model.
* @template {Model} T
* @template {string} U
*/
export type UpdateFn< T extends Model, U extends keyof T | undefined > = (
export type UpdateFn< T extends ModelRepositoryParams > = (
id: ModelID,
properties: UpdateParams< T, U >,
) => Promise< T >;
properties: UpdateParams< T >,
) => Promise< ModelClass< T > >;
/**
* A callback for updating a child model using a data source.
@ -81,17 +118,16 @@ export type UpdateFn< T extends Model, U extends keyof T | undefined > = (
* @callback UpdateChildFn
* @param {P} parent The parent identifier for the model.
* @param {ModelID} childID The ID of the model.
* @param {Pick.<T,U>} properties The properties to update.
* @param {Partial.<T>} properties The properties to update.
* @return {Promise.<T>} Resolves to the updated model.
* @template {Model} T
* @template {ModelParentID} P
* @template {string} U
*/
export type UpdateChildFn< T extends Model, P extends ModelParentID | undefined, U extends keyof T | undefined > = (
parent: P,
export type UpdateChildFn< T extends ModelRepositoryParams > = (
parent: ParentID< T >,
childID: ModelID,
properties: UpdateParams< T, U >,
) => Promise< T >;
properties: UpdateParams< T >,
) => Promise< ModelClass< T > >;
/**
* A callback for deleting a model from a data source.
@ -111,7 +147,7 @@ export type DeleteFn = ( id: ModelID ) => Promise< boolean >;
* @return {Promise.<boolean>} Resolves to true once the model has been deleted.
* @template {ModelParentID} P
*/
export type DeleteChildFn< P extends ModelParentID | undefined > = ( parent: P, childID: ModelID ) => Promise< boolean >;
export type DeleteChildFn< T extends ModelRepositoryParams > = ( parent: ParentID< T >, childID: ModelID ) => Promise< boolean >;
/**
* An interface for repositories that can list models.
@ -121,8 +157,8 @@ export type DeleteChildFn< P extends ModelParentID | undefined > = ( parent: P,
* @template {Model} T
* @template L
*/
export interface ListsModels< T extends Model, L = undefined > {
list( params?: L ): Promise< T[] >;
export interface ListsModels< T extends ModelRepositoryParams > {
list( params?: ListParams< T > ): Promise< ModelClass< T >[] >;
}
/**
@ -134,8 +170,8 @@ export interface ListsModels< T extends Model, L = undefined > {
* @template {ModelParentID} P
* @template L
*/
export interface ListsChildModels< T extends Model, P extends ModelParentID | undefined, L = undefined > {
list( parent: P, params?: L ): Promise< T[] >;
export interface ListsChildModels< T extends ModelRepositoryParams > {
list( parent: ParentID< T >, params?: ListParams< T > ): Promise< ModelClass< T >[] >;
}
/**
@ -145,8 +181,8 @@ export interface ListsChildModels< T extends Model, P extends ModelParentID | un
* @property {CreateFn.<T>} create Creates a model using the repository.
* @template {Model} T
*/
export interface CreatesModels< T extends Model > {
create( properties: Partial< T > ): Promise< T >;
export interface CreatesModels< T extends ModelRepositoryParams > {
create( properties: Partial< ModelClass< T > > ): Promise< ModelClass< T > >;
}
/**
@ -156,8 +192,8 @@ export interface CreatesModels< T extends Model > {
* @property {ReadFn.<T>} read Reads a model using the repository.
* @template {Model} T
*/
export interface ReadsModels< T extends Model > {
read( id: ModelID ): Promise< T >;
export interface ReadsModels< T extends ModelRepositoryParams > {
read( id: ModelID ): Promise< ModelClass< T > >;
}
/**
@ -168,33 +204,31 @@ export interface ReadsModels< T extends Model > {
* @template {Model} T
* @template {ModelParentID} P
*/
export interface ReadsChildModels< T extends Model, P extends ModelParentID | undefined > {
read( parent: P, childID: ModelID ): Promise< T >;
export interface ReadsChildModels< T extends ModelRepositoryParams > {
read( parent: ParentID< ModelRepositoryParams >, childID: ModelID ): Promise< ModelClass< T > >;
}
/**
* An interface for repositories that can update models.
*
* @typedef UpdatesModels
* @property {UpdateFn.<T,U>} update Updates a model using the repository.
* @property {UpdateFn.<T>} update Updates a model using the repository.
* @template {Model} T
* @template {string} U
*/
export interface UpdatesModels< T extends Model, U extends keyof T | undefined > {
update( id: ModelID, properties: UpdateParams< T, U > ): Promise< T >;
export interface UpdatesModels< T extends ModelRepositoryParams > {
update( id: ModelID, properties: UpdateParams< T > ): Promise< ModelClass< T > >;
}
/**
* An interface for repositories that can update models.
*
* @typedef UpdatesChildModels
* @property {UpdateChildFn.<T,P,U>} update Updates a model using the repository.
* @property {UpdateChildFn.<T,P>} update Updates a model using the repository.
* @template {Model} T
* @template {ModelParentID} P
* @template {string} U
*/
export interface UpdatesChildModels< T extends Model, P extends ModelParentID | undefined, U extends keyof T | undefined > {
update( parent: P, childID: ModelID, properties: UpdateParams< T, U > ): Promise< T >;
export interface UpdatesChildModels< T extends ModelRepositoryParams > {
update( parent: ParentID< T >, childID: ModelID, properties: UpdateParams< T > ): Promise< ModelClass< T > >;
}
/**
@ -214,8 +248,8 @@ export interface DeletesModels {
* @property {DeleteChildFn.<P>} delete Deletes a model using the repository.
* @template {ModelParentID} P
*/
export interface DeletesChildModels< P extends ModelParentID | undefined > {
delete( parent: P, childID: ModelID ): Promise< boolean >;
export interface DeletesChildModels< T extends ModelRepositoryParams > {
delete( parent: ParentID< T >, childID: ModelID ): Promise< boolean >;
}
/**
@ -224,31 +258,25 @@ export interface DeletesChildModels< P extends ModelParentID | undefined > {
* error when attempting to perform that action.
*
* @template {Model} T
* @template {Object|undefined} L
* @template {string|undefined} U
* @template {ModelParentID|undefined} P
* @template {ModelParentID} P
* @template {Object} L
*/
export class ModelRepository<
T extends Model,
L = undefined,
U extends keyof T | undefined = undefined,
P extends ModelParentID | undefined = undefined
> implements
ListsModels< T, L >,
ListsChildModels< T, P, L >,
export class ModelRepository< T extends ModelRepositoryParams > implements
ListsModels< T >,
ListsChildModels< T >,
ReadsModels< T >,
ReadsChildModels< T, HasParent< P, P, undefined > >,
UpdatesModels< T, U >,
UpdatesChildModels< T, HasParent< P, P, undefined >, U >,
ReadsChildModels< T >,
UpdatesModels< T >,
UpdatesChildModels< T >,
DeletesModels,
DeletesChildModels< HasParent< P, P, undefined > > {
DeletesChildModels< T > {
/**
* The hook used to list models.
*
* @type {ListFn.<T,P,L>|ListChildFn<T,P,L>}
* @private
*/
private readonly listHook: HasParent< P, ListChildFn< T, P, L >, ListFn< T, L > > | null;
private readonly listHook: HasParent< T, ListChildFn< T >, ListFn< T > > | null;
/**
* The hook used to create models
@ -264,15 +292,15 @@ export class ModelRepository<
* @type {ReadFn.<T>|ReadChildFn.<T,P>}
* @private
*/
private readonly readHook: HasParent< P, ReadChildFn< T, P >, ReadFn< T > > | null;
private readonly readHook: HasParent< T, ReadChildFn< T >, ReadFn< T > > | null;
/**
* The hook used to update models.
*
* @type {UpdateFn.<T,U>|UpdateChildFn.<T,P,U>}
* @type {UpdateFn.<T>|UpdateChildFn.<T,P>}
* @private
*/
private readonly updateHook: HasParent< P, UpdateChildFn< T, P, U >, UpdateFn< T, U > > | null;
private readonly updateHook: HasParent< T, UpdateChildFn< T >, UpdateFn< T > > | null;
/**
* The hook used to delete models.
@ -280,7 +308,7 @@ export class ModelRepository<
* @type {DeleteFn|DeleteChildFn.<P>}
* @private
*/
private readonly deleteHook: HasParent< P, DeleteChildFn< P >, DeleteFn > | null;
private readonly deleteHook: HasParent< T, DeleteChildFn< T >, DeleteFn > | null;
/**
* Creates a new repository instance.
@ -288,15 +316,15 @@ export class ModelRepository<
* @param {ListFn.<T,L>|ListChildFn<T,P,L>} listHook The hook for model listing.
* @param {CreateFn.<T>|null} createHook The hook for model creation.
* @param {ReadFn.<T>|ReadChildFn.<T,P>|null} readHook The hook for model reading.
* @param {UpdateFn.<T,U>|UpdateChildFn.<T,P,U>|null} updateHook The hook for model updating.
* @param {UpdateFn.<T>|UpdateChildFn.<T,P>|null} updateHook The hook for model updating.
* @param {DeleteFn|DeleteChildFn.<P>|null} deleteHook The hook for model deletion.
*/
public constructor(
listHook: HasParent< P, ListChildFn< T, P, L >, ListFn< T, L > > | null,
listHook: HasParent< T, ListChildFn< T >, ListFn< T > > | null,
createHook: CreateFn< T > | null,
readHook: HasParent< P, ReadChildFn< T, P >, ReadFn< T > > | null,
updateHook: HasParent< P, UpdateChildFn< T, P, U >, UpdateFn< T, U > > | null,
deleteHook: HasParent< P, DeleteChildFn< P >, DeleteFn > | null,
readHook: HasParent< T, ReadChildFn< T >, ReadFn< T > > | null,
updateHook: HasParent< T, UpdateChildFn< T >, UpdateFn< T > > | null,
deleteHook: HasParent< T, DeleteChildFn< T >, DeleteFn > | null,
) {
this.listHook = listHook;
this.createHook = createHook;
@ -312,19 +340,22 @@ export class ModelRepository<
* @param {L} [params] The params when using the parent.
* @return {Promise.<Array.<T>>} Resolves to the listed models.
*/
public list( paramsOrParent?: L | P, params?: L ): Promise< T[] > {
public list(
paramsOrParent?: ListParams< T > | ParentID< T >,
params?: ListParams< T >,
): Promise< ModelClass< T >[] > {
if ( ! this.listHook ) {
throw new Error( 'The \'create\' operation is not supported on this model.' );
}
if ( params === undefined ) {
return ( this.listHook as ListFn< T, L > )(
paramsOrParent as L,
return ( this.listHook as ListFn< T > )(
paramsOrParent as ListParams< T >,
);
}
return ( this.listHook as ListChildFn< T, P, L > )(
paramsOrParent as P,
return ( this.listHook as ListChildFn< T > )(
paramsOrParent as ParentID< T >,
params,
);
}
@ -335,7 +366,7 @@ export class ModelRepository<
* @param {Partial.<T>} properties The properties to create the model with.
* @return {Promise.<T>} Resolves to the created model.
*/
public create( properties: Partial< T > ): Promise< T > {
public create( properties: Partial< ModelClass< T > > ): Promise< ModelClass< T > > {
if ( ! this.createHook ) {
throw new Error( 'The \'create\' operation is not supported on this model.' );
}
@ -351,9 +382,9 @@ export class ModelRepository<
* @return {Promise.<T>} Resolves to the loaded model.
*/
public read(
idOrParent: ModelID | P | undefined,
idOrParent: ModelID | ParentID< T >,
childID?: ModelID,
): Promise< T > {
): Promise< ModelClass< T > > {
if ( ! this.readHook ) {
throw new Error( 'The \'read\' operation is not supported on this model.' );
}
@ -364,8 +395,8 @@ export class ModelRepository<
);
}
return ( this.readHook as ReadChildFn< T, P > )(
idOrParent as P,
return ( this.readHook as ReadChildFn< T > )(
idOrParent as ParentID< T >,
childID,
);
}
@ -379,23 +410,23 @@ export class ModelRepository<
* @return {Promise.<T>} Resolves to the updated model.
*/
public update(
idOrParent: ModelID | P | undefined,
propertiesOrChildID: UpdateParams< T, U > | ModelID,
properties?: UpdateParams< T, U >,
): Promise< T > {
idOrParent: ModelID | ParentID< T >,
propertiesOrChildID: UpdateParams< T > | ModelID,
properties?: UpdateParams< T >,
): Promise< ModelClass< T > > {
if ( ! this.updateHook ) {
throw new Error( 'The \'update\' operation is not supported on this model.' );
}
if ( properties === undefined ) {
return ( this.updateHook as UpdateFn< T, U > )(
return ( this.updateHook as UpdateFn< T > )(
idOrParent as ModelID,
propertiesOrChildID as UpdateParams< T, U >,
propertiesOrChildID as UpdateParams< T >,
);
}
return ( this.updateHook as UpdateChildFn< T, P, U > )(
idOrParent as P,
return ( this.updateHook as UpdateChildFn< T > )(
idOrParent as ParentID< T >,
propertiesOrChildID as ModelID,
properties,
);
@ -408,7 +439,10 @@ export class ModelRepository<
* @param {ModelID} [childID] The ID of the model when using the parent.
* @return {Promise.<T>} Resolves to the loaded model.
*/
public delete( idOrParent: ModelID | P | undefined, childID?: ModelID ): Promise< boolean > {
public delete(
idOrParent: ModelID | ParentID< T >,
childID?: ModelID,
): Promise< boolean > {
if ( ! this.deleteHook ) {
throw new Error( 'The \'delete\' operation is not supported on this model.' );
}
@ -419,8 +453,8 @@ export class ModelRepository<
);
}
return ( this.deleteHook as DeleteChildFn< P > )(
idOrParent as P,
return ( this.deleteHook as DeleteChildFn< T > )(
idOrParent as ParentID< T >,
childID,
);
}

View File

@ -6,16 +6,6 @@
*/
export type ModelID = string | number;
/**
* An interface for describing the shape of parent identifiers.
*
* @typedef ModelParentID
* @alias Object.<string,ModelID>
*/
export interface ModelParentID {
[ key: number ]: ModelID
}
/**
* The base class for all models.
*/

View File

@ -1,4 +1,4 @@
import { Model, ModelID, ModelParentID } from '../model';
import { Model } from '../model';
import { HTTPClient } from '../../http';
import { settingRESTRepository } from '../../repositories/rest/settings/setting';
@ -7,16 +7,6 @@ import { settingRESTRepository } from '../../repositories/rest/settings/setting'
*/
type SettingType = 'text' | 'select' | 'multiselect' | 'checkbox' | 'number';
/**
* An interface describing the shape of setting parent data.
*
* @typedef SettingParentID
* @property {ModelID} settingGroupID The ID of the setting group for the setting.
*/
export interface SettingParentID extends ModelParentID {
settingGroupID: ModelID;
}
/**
* A setting object.
*/

View File

@ -1,8 +1,10 @@
import { HTTPClient } from '../../../http';
import { CreateFn, CreatesModels, ModelRepository } from '../../../framework/model-repository';
import { CreateFn, CreatesModels, ModelRepository, ModelRepositoryParams } from '../../../framework/model-repository';
import { SimpleProduct } from '../../../models';
function restCreate( httpClient: HTTPClient ): CreateFn< SimpleProduct > {
type SimpleProductParams = ModelRepositoryParams< SimpleProduct, never, never, 'regularPrice' >;
function restCreate( httpClient: HTTPClient ): CreateFn< SimpleProductParams > {
return async ( properties ) => {
const response = await httpClient.post(
'/wc/v3/products',
@ -27,7 +29,7 @@ function restCreate( httpClient: HTTPClient ): CreateFn< SimpleProduct > {
* @param {HTTPClient} httpClient The HTTP client for the REST requests to be made using.
* @return {CreatesModels.<SimpleProduct>} A repository for interacting with models via the REST API.
*/
export function simpleProductRESTRepository( httpClient: HTTPClient ): CreatesModels< SimpleProduct > {
export function simpleProductRESTRepository( httpClient: HTTPClient ): CreatesModels< SimpleProductParams > {
return new ModelRepository(
null,
restCreate( httpClient ),

View File

@ -1,8 +1,10 @@
import { HTTPClient } from '../../../http';
import { ListFn, ListsModels, ModelRepository } from '../../../framework/model-repository';
import { SettingGroup } from '../../../models/settings/setting-group';
import { ListFn, ListsModels, ModelRepository, ModelRepositoryParams } from '../../../framework/model-repository';
import { SettingGroup } from '../../../models';
function restList( httpClient: HTTPClient ): ListFn< SettingGroup, void > {
type SettingGroupParams = ModelRepositoryParams< SettingGroup, never, never, never >;
function restList( httpClient: HTTPClient ): ListFn< SettingGroupParams > {
return async () => {
const response = await httpClient.get( '/wc/v3/settings' );
@ -26,8 +28,8 @@ function restList( httpClient: HTTPClient ): ListFn< SettingGroup, void > {
* @param {HTTPClient} httpClient The HTTP client for the REST requests to be made using.
* @return {ListsModels.<SettingGroup>} A repository for interacting with models via the REST API.
*/
export function settingGroupRESTRepository( httpClient: HTTPClient ): ListsModels< SettingGroup > {
return new ModelRepository(
export function settingGroupRESTRepository( httpClient: HTTPClient ): ListsModels< SettingGroupParams > {
return new ModelRepository< SettingGroupParams >(
restList( httpClient ),
null,
null,

View File

@ -3,13 +3,23 @@ import {
ListChildFn,
ListsChildModels,
ModelRepository,
ModelRepositoryParams,
ReadChildFn,
ReadsChildModels,
UpdateChildFn, UpdatesChildModels,
UpdateChildFn,
UpdatesChildModels,
} from '../../../framework/model-repository';
import { Setting, SettingParentID } from '../../../models/settings/setting';
import { Setting } from '../../../models';
function restList( httpClient: HTTPClient ): ListChildFn< Setting, SettingParentID, undefined > {
/**
* @typedef SettingParentID
* @property {string} settingGroupID The ID of the setting group we're a child of.
*/
type SettingParentID = { settingGroupID: string };
type SettingParams = ModelRepositoryParams< Setting, SettingParentID, never, 'value' >;
function restList( httpClient: HTTPClient ): ListChildFn< SettingParams > {
return async ( parent ) => {
const response = await httpClient.get( '/wc/v3/settings/' + parent.settingGroupID );
@ -30,7 +40,7 @@ function restList( httpClient: HTTPClient ): ListChildFn< Setting, SettingParent
};
}
function restRead( httpClient: HTTPClient ): ReadChildFn< Setting, SettingParentID > {
function restRead( httpClient: HTTPClient ): ReadChildFn< SettingParams > {
return async ( parent, id ) => {
const response = await httpClient.get( '/wc/v3/settings/' + parent.settingGroupID + '/' + id );
@ -46,7 +56,7 @@ function restRead( httpClient: HTTPClient ): ReadChildFn< Setting, SettingParent
};
}
function restUpdate( httpClient: HTTPClient ): UpdateChildFn< Setting, SettingParentID, 'value' > {
function restUpdate( httpClient: HTTPClient ): UpdateChildFn< SettingParams > {
return async ( parent, id, params ) => {
const response = await httpClient.patch(
'/wc/v3/settings/' + parent.settingGroupID + '/' + id,
@ -76,10 +86,10 @@ function restUpdate( httpClient: HTTPClient ): UpdateChildFn< Setting, SettingPa
* } A repository for interacting with models via the REST API.
*/
export function settingRESTRepository( httpClient: HTTPClient ):
ListsChildModels< Setting, SettingParentID > &
ReadsChildModels< Setting, SettingParentID > &
UpdatesChildModels< Setting, SettingParentID, 'value' > {
return new ModelRepository< Setting, undefined, 'value', SettingParentID >(
ListsChildModels< SettingParams > &
ReadsChildModels< SettingParams > &
UpdatesChildModels< SettingParams > {
return new ModelRepository< SettingParams >(
restList( httpClient ),
null,
restRead( httpClient ),