Implemented the factory & repository for simple products
This commit is contained in:
parent
6c230ca7b3
commit
a9ee9806a4
|
@ -1,2 +0,0 @@
|
|||
export { Product } from './product';
|
||||
export { SimpleProduct, registerSimpleProduct } from './simple-product';
|
|
@ -1,15 +0,0 @@
|
|||
import { Model } from './model';
|
||||
import { DeepPartial } from 'fishery';
|
||||
|
||||
/**
|
||||
* The base class for all product types.
|
||||
*/
|
||||
export abstract class Product extends Model {
|
||||
public readonly name: string = '';
|
||||
public readonly regularPrice: string = '';
|
||||
|
||||
protected constructor( partial: DeepPartial< Product > = {} ) {
|
||||
super( partial );
|
||||
Object.assign( this, partial );
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
import { DeepPartial } from 'fishery';
|
||||
import { Product } from './product';
|
||||
|
||||
/**
|
||||
* The simple product class.
|
||||
*/
|
||||
export class SimpleProduct extends Product {
|
||||
public constructor( partial: DeepPartial< SimpleProduct > = {} ) {
|
||||
super( partial );
|
||||
Object.assign( this, partial );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
import { Model } from '../../models/model';
|
||||
import { AsyncFactory } from '../async-factory';
|
||||
|
||||
class DummyModel extends Model {
|
||||
public name: string = '';
|
||||
|
||||
public constructor( partial?: Partial< DummyModel > ) {
|
||||
super();
|
||||
Object.assign( this, partial );
|
||||
}
|
||||
}
|
||||
|
||||
describe( 'AsyncFactory', () => {
|
||||
let factory: AsyncFactory< DummyModel >;
|
||||
|
||||
beforeEach( () => {
|
||||
let sequence = 1;
|
||||
|
||||
factory = new AsyncFactory< DummyModel >(
|
||||
( { params } ) => {
|
||||
const model = new DummyModel();
|
||||
model.name = params.name ?? '';
|
||||
return model;
|
||||
},
|
||||
( model ) => {
|
||||
return Promise.resolve( new DummyModel( { id: sequence++, name: model.name } ) );
|
||||
},
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should create', async () => {
|
||||
const model = await factory.create( { name: 'test-name' } );
|
||||
|
||||
expect( model ).toHaveProperty( 'id', 1 );
|
||||
expect( model ).toHaveProperty( 'name', 'test-name' );
|
||||
} );
|
||||
|
||||
it( 'should create many', async () => {
|
||||
const models = await factory.createList( 2, { name: 'test-name' } );
|
||||
|
||||
expect( models ).toHaveLength( 2 );
|
||||
expect( models[ 0 ] ).toHaveProperty( 'id', 1 );
|
||||
expect( models[ 0 ] ).toHaveProperty( 'name', 'test-name' );
|
||||
expect( models[ 1 ] ).toHaveProperty( 'id', 2 );
|
||||
expect( models[ 1 ] ).toHaveProperty( 'name', 'test-name' );
|
||||
} );
|
||||
} );
|
|
@ -4,11 +4,6 @@ import Mock = jest.Mock;
|
|||
|
||||
class DummyModel extends Model {
|
||||
public name: string = '';
|
||||
|
||||
public onCreated( data: any ): void {
|
||||
super.onCreated( data );
|
||||
this.name = data.name;
|
||||
}
|
||||
}
|
||||
|
||||
describe( 'ModelRepository', () => {
|
||||
|
@ -24,28 +19,28 @@ describe( 'ModelRepository', () => {
|
|||
} );
|
||||
|
||||
it( 'should create', async () => {
|
||||
mockCallback.mockReturnValue( Promise.resolve( dummyModel ) );
|
||||
mockCallback.mockResolvedValue( dummyModel );
|
||||
|
||||
await repository.create( dummyModel );
|
||||
expect( mockCallback ).toHaveBeenCalledWith( dummyModel );
|
||||
} );
|
||||
|
||||
it( 'should read', async () => {
|
||||
mockCallback.mockReturnValue( Promise.resolve( dummyModel ) );
|
||||
mockCallback.mockResolvedValue( dummyModel );
|
||||
|
||||
await repository.read( { id: 'test' } );
|
||||
expect( mockCallback ).toHaveBeenCalledWith( { id: 'test' } );
|
||||
await repository.read( { id: 1 } );
|
||||
expect( mockCallback ).toHaveBeenCalledWith( { id: 1 } );
|
||||
} );
|
||||
|
||||
it( 'should update', async () => {
|
||||
mockCallback.mockReturnValue( Promise.resolve( dummyModel ) );
|
||||
mockCallback.mockResolvedValue( dummyModel );
|
||||
|
||||
await repository.update( dummyModel );
|
||||
expect( mockCallback ).toHaveBeenCalledWith( dummyModel );
|
||||
} );
|
||||
|
||||
it( 'should delete', async () => {
|
||||
mockCallback.mockReturnValue( Promise.resolve( true ) );
|
||||
mockCallback.mockResolvedValue( true );
|
||||
|
||||
await repository.delete( dummyModel );
|
||||
expect( mockCallback ).toHaveBeenCalledWith( dummyModel );
|
||||
|
|
|
@ -1,25 +1,18 @@
|
|||
import { DeepPartial, Factory as BaseFactory, BuildOptions } from 'fishery';
|
||||
import { Repository } from './repository';
|
||||
import { BuildOptions, DeepPartial, Factory } from 'fishery';
|
||||
import { GeneratorFnOptions } from 'fishery/dist/types';
|
||||
import { Model } from '../models/model';
|
||||
|
||||
/**
|
||||
* A factory that can be used to create models using an adapter.
|
||||
*/
|
||||
export class RepositoryFactory< T extends Model, I = any > extends BaseFactory< T, I > {
|
||||
private repository: Repository< T > | null = null;
|
||||
|
||||
public constructor( generator: ( opts: GeneratorFnOptions< T, I > ) => T ) {
|
||||
super( generator );
|
||||
}
|
||||
export class AsyncFactory< T, I = any > extends Factory< T, I > {
|
||||
private readonly creator: ( model: T ) => Promise< T >;
|
||||
|
||||
/**
|
||||
* Sets the repository that the factory should use when creating data.
|
||||
* Creates a new factory instance.
|
||||
*
|
||||
* @param {Repository|null} repository The repository to set.
|
||||
* @param {Function} generator The factory's generator function.
|
||||
* @param {Function} creator The factory's creation function.
|
||||
*/
|
||||
public setRepository( repository: Repository< T > | null ): void {
|
||||
this.repository = repository;
|
||||
public constructor( generator: ( opts: GeneratorFnOptions< T, I > ) => T, creator: ( model: T ) => Promise< T > ) {
|
||||
super( generator );
|
||||
this.creator = creator;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,12 +23,8 @@ export class RepositoryFactory< T extends Model, I = any > extends BaseFactory<
|
|||
* @return {Promise} Resolves to the created model.
|
||||
*/
|
||||
public create( params?: DeepPartial< T >, options?: BuildOptions< T, I > ): Promise< T > {
|
||||
if ( ! this.repository ) {
|
||||
throw new Error( 'The factory has no repository to create using.' );
|
||||
}
|
||||
|
||||
const model = this.build( params, options );
|
||||
return this.repository.create( model );
|
||||
return this.creator( model );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -47,14 +36,10 @@ export class RepositoryFactory< T extends Model, I = any > extends BaseFactory<
|
|||
* @return {Promise} Resolves to the created models.
|
||||
*/
|
||||
public createList( number: number, params?: DeepPartial< T >, options?: BuildOptions< T, I > ): Promise< T[] > {
|
||||
if ( ! this.repository ) {
|
||||
throw new Error( 'The factory has no repository to create using.' );
|
||||
}
|
||||
|
||||
const models = this.buildList( number, params, options );
|
||||
const promises: Promise< T >[] = [];
|
||||
for ( const model of models ) {
|
||||
promises.push( this.repository.create( model ) );
|
||||
promises.push( this.create( model ) );
|
||||
}
|
||||
|
||||
return Promise.all( promises );
|
|
@ -9,7 +9,7 @@ type DeleteFn< T > = ( model: T ) => Promise< boolean >;
|
|||
* The standard parameters for reading a model.
|
||||
*/
|
||||
interface DefaultReadParams {
|
||||
id: string;
|
||||
id: number;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -30,7 +30,7 @@ export class AxiosClient implements HTTPClient {
|
|||
* @param {*} params Any parameters that should be passed in the request.
|
||||
* @return {Promise} Resolves to an HTTPResponse.
|
||||
*/
|
||||
public get< T >(
|
||||
public get< T = any >(
|
||||
path: string,
|
||||
params?: any,
|
||||
): Promise< HTTPResponse< T >> {
|
||||
|
@ -44,7 +44,7 @@ export class AxiosClient implements HTTPClient {
|
|||
* @param {*} data Any parameters that should be passed in the request.
|
||||
* @return {Promise} Resolves to an HTTPResponse.
|
||||
*/
|
||||
public post< T >(
|
||||
public post< T = any >(
|
||||
path: string,
|
||||
data?: any,
|
||||
): Promise< HTTPResponse< T >> {
|
||||
|
@ -58,7 +58,7 @@ export class AxiosClient implements HTTPClient {
|
|||
* @param {*} data Any parameters that should be passed in the request.
|
||||
* @return {Promise} Resolves to an HTTPResponse.
|
||||
*/
|
||||
public put< T >(
|
||||
public put< T = any >(
|
||||
path: string,
|
||||
data?: any,
|
||||
): Promise< HTTPResponse< T >> {
|
||||
|
@ -72,7 +72,7 @@ export class AxiosClient implements HTTPClient {
|
|||
* @param {*} data Any parameters that should be passed in the request.
|
||||
* @return {Promise} Resolves to an HTTPResponse.
|
||||
*/
|
||||
public patch< T >(
|
||||
public patch< T = any >(
|
||||
path: string,
|
||||
data?: any,
|
||||
): Promise< HTTPResponse< T >> {
|
||||
|
@ -86,7 +86,7 @@ export class AxiosClient implements HTTPClient {
|
|||
* @param {*} data Any parameters that should be passed in the request.
|
||||
* @return {Promise} Resolves to an HTTPResponse.
|
||||
*/
|
||||
public delete< T >(
|
||||
public delete< T = any >(
|
||||
path: string,
|
||||
data?: any,
|
||||
): Promise< HTTPResponse< T >> {
|
||||
|
|
|
@ -31,7 +31,7 @@ export interface HTTPClient {
|
|||
* @param {*} params Any parameters that should be passed in the request.
|
||||
* @return {Promise} Resolves to an HTTPResponse.
|
||||
*/
|
||||
get< T >( path: string, params?: any ): Promise< HTTPResponse< T > >;
|
||||
get< T = any >( path: string, params?: any ): Promise< HTTPResponse< T > >;
|
||||
|
||||
/**
|
||||
* Performs a POST request.
|
||||
|
@ -40,7 +40,7 @@ export interface HTTPClient {
|
|||
* @param {*} data Any parameters that should be passed in the request.
|
||||
* @return {Promise} Resolves to an HTTPResponse.
|
||||
*/
|
||||
post< T >( path: string, data?: any ): Promise< HTTPResponse< T > >;
|
||||
post< T = any >( path: string, data?: any ): Promise< HTTPResponse< T > >;
|
||||
|
||||
/**
|
||||
* Performs a PUT request.
|
||||
|
@ -49,7 +49,7 @@ export interface HTTPClient {
|
|||
* @param {*} data Any parameters that should be passed in the request.
|
||||
* @return {Promise} Resolves to an HTTPResponse.
|
||||
*/
|
||||
put< T >( path: string, data?: any ): Promise< HTTPResponse< T > >;
|
||||
put< T = any >( path: string, data?: any ): Promise< HTTPResponse< T > >;
|
||||
|
||||
/**
|
||||
* Performs a PATCH request.
|
||||
|
@ -58,7 +58,7 @@ export interface HTTPClient {
|
|||
* @param {*} data Any parameters that should be passed in the request.
|
||||
* @return {Promise} Resolves to an HTTPResponse.
|
||||
*/
|
||||
patch< T >( path: string, data?: any ): Promise< HTTPResponse< T > >;
|
||||
patch< T = any >( path: string, data?: any ): Promise< HTTPResponse< T > >;
|
||||
|
||||
/**
|
||||
* Performs a DELETE request.
|
||||
|
@ -67,5 +67,5 @@ export interface HTTPClient {
|
|||
* @param {*} data Any parameters that should be passed in the request.
|
||||
* @return {Promise} Resolves to an HTTPResponse.
|
||||
*/
|
||||
delete< T >( path: string, data?: any ): Promise< HTTPResponse< T > >;
|
||||
delete< T = any >( path: string, data?: any ): Promise< HTTPResponse< T > >;
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export { SimpleProduct } from './product/simple-product';
|
|
@ -2,13 +2,5 @@
|
|||
* A base class for all models.
|
||||
*/
|
||||
export abstract class Model {
|
||||
private _id: number | null = null;
|
||||
|
||||
public get id(): number | null {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public onCreated( data: any ): void {
|
||||
this._id = data.id;
|
||||
}
|
||||
public readonly id: number | null = null;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
import { SimpleProduct } from '../simple-product';
|
||||
import { mock, MockProxy } from 'jest-mock-extended';
|
||||
import { HTTPClient, HTTPResponse } from '../../../http';
|
||||
import { ModelRepository } from '../../../framework/model-repository';
|
||||
|
||||
describe( 'SimpleProduct', () => {
|
||||
describe( 'restRepository', () => {
|
||||
let httpClient: MockProxy< HTTPClient >;
|
||||
let repository: ModelRepository< SimpleProduct >;
|
||||
|
||||
beforeEach( () => {
|
||||
httpClient = mock< HTTPClient >();
|
||||
repository = SimpleProduct.restRepository( httpClient );
|
||||
} );
|
||||
|
||||
it( 'should create', async () => {
|
||||
httpClient.post.mockResolvedValue( new HTTPResponse( 200, {}, { id: 2 } ) );
|
||||
|
||||
const created = await repository.create( new SimpleProduct( { name: 'test' } ) );
|
||||
|
||||
expect( created ).toHaveProperty( 'id', 2 );
|
||||
expect( httpClient.post ).toHaveBeenCalledWith(
|
||||
'/wc/v3/products',
|
||||
{
|
||||
name: 'test',
|
||||
},
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should read', async () => {
|
||||
httpClient.get.mockResolvedValue(
|
||||
new HTTPResponse( 200, {}, {
|
||||
id: 12,
|
||||
name: 'test-name',
|
||||
} ),
|
||||
);
|
||||
|
||||
const read = await repository.read( { id: 12 } );
|
||||
|
||||
expect( read ).toHaveProperty( 'id', 12 );
|
||||
expect( httpClient.get ).toHaveBeenCalledWith( '/wc/v3/products/12' );
|
||||
} );
|
||||
|
||||
it( 'should update', async () => {
|
||||
httpClient.put.mockResolvedValue( new HTTPResponse( 200, {}, { id: 1 } ) );
|
||||
|
||||
const updated = await repository.update( new SimpleProduct( { id: 1, name: 'test' } ) );
|
||||
|
||||
expect( updated ).toHaveProperty( 'id', 1 );
|
||||
expect( httpClient.put ).toHaveBeenCalledWith(
|
||||
'/wc/v3/products/1',
|
||||
{
|
||||
id: 1,
|
||||
name: 'test',
|
||||
},
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should delete', async () => {
|
||||
httpClient.delete.mockResolvedValue( new HTTPResponse( 200, {}, {} ) );
|
||||
|
||||
const response = await repository.delete( new SimpleProduct( { id: 123 } ) );
|
||||
|
||||
expect( response ).toBeTruthy();
|
||||
expect( httpClient.delete ).toHaveBeenCalledWith( '/wc/v3/products/123' );
|
||||
} );
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,9 @@
|
|||
import { Model } from '../model';
|
||||
|
||||
/**
|
||||
* The base class for all product types.
|
||||
*/
|
||||
export abstract class AbstractProduct extends Model {
|
||||
public readonly name: string = '';
|
||||
public readonly regularPrice: string = '';
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
import { AbstractProduct } from './abstract-product';
|
||||
import * as faker from 'faker/locale/en';
|
||||
import { HTTPClient } from '../../http';
|
||||
import { ModelRepository } from '../../framework/model-repository';
|
||||
import { AsyncFactory } from '../../framework/async-factory';
|
||||
|
||||
/**
|
||||
* The simple product class.
|
||||
*/
|
||||
export class SimpleProduct extends AbstractProduct {
|
||||
public constructor( partial: Partial< SimpleProduct > = {} ) {
|
||||
super();
|
||||
Object.assign( this, partial );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a model repository configured for communicating via the REST API.
|
||||
*
|
||||
* @param {HTTPClient} httpClient The client for communicating via HTTP.
|
||||
* @return {ModelRepository} The created repository.
|
||||
*/
|
||||
public static restRepository( httpClient: HTTPClient ): ModelRepository< SimpleProduct > {
|
||||
return new ModelRepository(
|
||||
async ( model ) => {
|
||||
const response = await httpClient.post(
|
||||
'/wc/v3/products',
|
||||
{
|
||||
name: model.name,
|
||||
},
|
||||
);
|
||||
|
||||
return Promise.resolve( new SimpleProduct( {
|
||||
id: response.data.id,
|
||||
name: response.data.name,
|
||||
} ) );
|
||||
},
|
||||
async ( params ) => {
|
||||
const response = await httpClient.get( '/wc/v3/products/' + params.id );
|
||||
|
||||
const model = new SimpleProduct(
|
||||
{
|
||||
id: response.data.id,
|
||||
name: response.data.name,
|
||||
},
|
||||
);
|
||||
return Promise.resolve( model );
|
||||
},
|
||||
async ( model ) => {
|
||||
return httpClient.put(
|
||||
'/wc/v3/products/' + model.id,
|
||||
{
|
||||
id: model.id,
|
||||
name: model.name,
|
||||
},
|
||||
).then( () => model );
|
||||
},
|
||||
async ( model ) => {
|
||||
return httpClient.delete( '/wc/v3/products/' + model.id ).then( () => true );
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new factory instance.
|
||||
*
|
||||
* @param {ModelRepository} repository The repository to use for creation.
|
||||
* @return {AsyncFactory} The new factory instance.
|
||||
*/
|
||||
public static factory( repository: ModelRepository< SimpleProduct > ): AsyncFactory< SimpleProduct > {
|
||||
return new AsyncFactory< SimpleProduct >(
|
||||
( { params } ) => {
|
||||
return new SimpleProduct( {
|
||||
name: params.name ?? faker.commerce.productName(),
|
||||
regularPrice: params.regularPrice ?? faker.commerce.price(),
|
||||
} );
|
||||
},
|
||||
repository.create,
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue