Merge pull request #27836 from woocommerce/packages/api/add/repository-listing
@woocommerce/api: Added list() repository operation and classes for managing store settings
This commit is contained in:
commit
f9ef91adc1
File diff suppressed because it is too large
Load Diff
|
@ -37,9 +37,9 @@
|
|||
"@babel/polyfill": "7.10.4",
|
||||
"@babel/preset-env": "7.10.4",
|
||||
"@babel/register": "7.10.4",
|
||||
"@typescript-eslint/eslint-plugin": "3.1.0",
|
||||
"@typescript-eslint/experimental-utils": "^2.34.0",
|
||||
"@typescript-eslint/parser": "3.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "3.10.1",
|
||||
"@typescript-eslint/experimental-utils": "3.10.1",
|
||||
"@typescript-eslint/parser": "3.10.1",
|
||||
"@woocommerce/api": "file:tests/e2e/api",
|
||||
"@woocommerce/e2e-core-tests": "file:tests/e2e/core-tests",
|
||||
"@woocommerce/e2e-environment": "file:tests/e2e/env",
|
||||
|
@ -84,7 +84,7 @@
|
|||
"puppeteer": "^2.1.1",
|
||||
"stylelint": "12.0.1",
|
||||
"stylelint-config-wordpress": "16.0.0",
|
||||
"typescript": "3.9.5",
|
||||
"typescript": "3.9.7",
|
||||
"webpack": "4.44.1",
|
||||
"webpack-cli": "3.3.12",
|
||||
"wp-textdomain": "1.0.1"
|
||||
|
|
|
@ -14,7 +14,7 @@ module.exports = {
|
|||
'no-useless-constructor': 'off',
|
||||
'@typescript-eslint/no-useless-constructor': 2,
|
||||
},
|
||||
'plugins': [
|
||||
plugins: [
|
||||
'@typescript-eslint'
|
||||
],
|
||||
extends: [
|
||||
|
@ -22,17 +22,22 @@ module.exports = {
|
|||
],
|
||||
overrides: [
|
||||
{
|
||||
'files': [
|
||||
files: [
|
||||
'**/*.js',
|
||||
'**/*.ts'
|
||||
]
|
||||
],
|
||||
settings: {
|
||||
jsdoc: {
|
||||
mode: 'typescript',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
'files': [
|
||||
files: [
|
||||
'**/*.spec.ts',
|
||||
'**/*.test.ts'
|
||||
],
|
||||
'rules': {
|
||||
rules: {
|
||||
'no-console': 'off',
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,8 @@ httpClient.get( '/wc/v3/products' ).then( ( response ) => {
|
|||
response.headers;
|
||||
// Access the data from the response, in this case, the products.
|
||||
response.data;
|
||||
}, ( error ) => {
|
||||
// Handle errors that may have come up.
|
||||
} );
|
||||
```
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
verbose: true,
|
||||
rootDir: 'src',
|
||||
testEnvironment: 'node',
|
||||
testPathIgnorePatterns: [ '/node_modules/', '/dist/' ],
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -46,7 +46,7 @@
|
|||
"jest-mock-extended": "^1.0.10",
|
||||
"moxios": "0.4.0",
|
||||
"ts-jest": "25.5.0",
|
||||
"typescript": "3.8.3"
|
||||
"typescript": "3.9.7"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
|
|
@ -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,103 @@ class DummyModel extends Model {
|
|||
Object.assign( this, partial );
|
||||
}
|
||||
}
|
||||
type DummyModelParams = ModelRepositoryParams< DummyModel, never, { 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: 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: ListsModels< DummyModelParams > = new ModelRepository< DummyModelParams >(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
|
||||
expect( () => repository.list() ).toThrowError( /not supported/i );
|
||||
} );
|
||||
|
||||
it( 'should create', async () => {
|
||||
const model = new DummyModel();
|
||||
const callback = jest.fn().mockResolvedValue( model );
|
||||
const repository = new ModelRepository< DummyModel >( 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 );
|
||||
const repository: CreatesModels< DummyModelParams > = new ModelRepository< DummyModelParams >(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
|
||||
expect( () => repository.create( { name: 'test' } ) ).toThrowError( /not supported/i );
|
||||
} );
|
||||
|
@ -30,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, 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 );
|
||||
const repository: ReadsModels< DummyModelParams > = new ModelRepository< DummyModelParams >(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
|
||||
expect( () => repository.read( 1 ) ).toThrowError( /not supported/i );
|
||||
} );
|
||||
|
@ -46,30 +169,85 @@ describe( 'ModelRepository', () => {
|
|||
it( 'should update', async () => {
|
||||
const model = new DummyModel();
|
||||
const callback = jest.fn().mockResolvedValue( model );
|
||||
const repository = new ModelRepository< DummyModel >( 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 >( 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, callback );
|
||||
const repository: DeletesModels< DummyModelParams > = 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 );
|
||||
const repository: DeletesModels< DummyModelParams > = new ModelRepository< DummyModelParams >(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
|
||||
expect( () => repository.delete( 1 ) ).toThrowError( /not supported/i );
|
||||
} );
|
||||
|
|
|
@ -1,69 +1,247 @@
|
|||
import { Model } from '../models/model';
|
||||
import { Model, ModelID } from '../models/model';
|
||||
|
||||
/**
|
||||
* 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 = never,
|
||||
// @ts-ignore
|
||||
ParentID extends ModelID | ModelParentID = never,
|
||||
// @ts-ignore
|
||||
ListParams = never,
|
||||
// @ts-ignore
|
||||
UpdateParams extends keyof T = never,
|
||||
> {
|
||||
// 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 [ never ] ? C : P;
|
||||
|
||||
/**
|
||||
* A callback for listing models using a data source.
|
||||
*
|
||||
* @callback ListFn
|
||||
* @param {L} [params] The list parameters for the query.
|
||||
* @return {Promise.<Array.<T>>} Resolves to an array of created models.
|
||||
* @template {Model} T
|
||||
* @template L
|
||||
*/
|
||||
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 {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 ModelRepositoryParams > = (
|
||||
parent: ParentID< T >,
|
||||
params?: ListParams< T >
|
||||
) => Promise< ModelClass< T >[] >;
|
||||
|
||||
/**
|
||||
* A callback for creating a model using a data source.
|
||||
*
|
||||
* @callback CreateFn
|
||||
* @param {Object} properties The properties of the model to create.
|
||||
* @return {Promise.<Model>} Resolves to the created model.
|
||||
* @param {Partial.<T>} properties The properties of the model to create.
|
||||
* @return {Promise.<T>} Resolves to the created model.
|
||||
* @template {Model} T
|
||||
*/
|
||||
export type CreateFn< T > = ( 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.
|
||||
*
|
||||
* @callback ReadFn
|
||||
* @param {number|Object} id The ID or object used to find the model.
|
||||
* @return {Promise.<Model>} Resolves to the read model.
|
||||
* @param {ModelID} id The ID of the model.
|
||||
* @return {Promise.<T>} Resolves to the read model.
|
||||
* @template {Model} T
|
||||
*/
|
||||
export type ReadFn< IDParam, T > = ( id: IDParam ) => Promise< T >;
|
||||
export type ReadFn< T extends ModelRepositoryParams > = ( id: ModelID ) => Promise< ModelClass< T > >;
|
||||
|
||||
/**
|
||||
* A callback for reading a child model using a data source.
|
||||
*
|
||||
* @callback ReadChildFn
|
||||
* @param {P} parent The parent identifier for the model.
|
||||
* @param {ModelID} childID The ID of the model.
|
||||
* @return {Promise.<T>} Resolves to the read model.
|
||||
* @template {Model} T
|
||||
* @template {ModelParentID} P
|
||||
*/
|
||||
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 {number|Object} id The ID or object used to find the model.
|
||||
* @return {Promise.<Model>} Resolves to the updated model.
|
||||
* @param {ModelID} id The ID of the model.
|
||||
* @param {Partial.<T>} properties The properties to update.
|
||||
* @return {Promise.<T>} Resolves to the updated model.
|
||||
* @template {Model} T
|
||||
*/
|
||||
export type UpdateFn< IDParam, T > = ( id: IDParam, properties: Partial< T > ) => Promise< T >;
|
||||
export type UpdateFn< T extends ModelRepositoryParams > = (
|
||||
id: ModelID,
|
||||
properties: UpdateParams< T >,
|
||||
) => Promise< ModelClass< T > >;
|
||||
|
||||
/**
|
||||
* A callback for updating a child model using a data source.
|
||||
*
|
||||
* @callback UpdateChildFn
|
||||
* @param {P} parent The parent identifier for the model.
|
||||
* @param {ModelID} childID The ID of the model.
|
||||
* @param {Partial.<T>} properties The properties to update.
|
||||
* @return {Promise.<T>} Resolves to the updated model.
|
||||
* @template {Model} T
|
||||
* @template {ModelParentID} P
|
||||
*/
|
||||
export type UpdateChildFn< T extends ModelRepositoryParams > = (
|
||||
parent: ParentID< T >,
|
||||
childID: ModelID,
|
||||
properties: UpdateParams< T >,
|
||||
) => Promise< ModelClass< T > >;
|
||||
|
||||
/**
|
||||
* A callback for deleting a model from a data source.
|
||||
*
|
||||
* @callback DeleteFn
|
||||
* @param {number|Object} id The ID or object used to find the model.
|
||||
* @param {ModelID} id The ID of the model.
|
||||
* @return {Promise.<boolean>} Resolves to true once the model has been deleted.
|
||||
*/
|
||||
export type DeleteFn< IDParam > = ( id: IDParam ) => Promise< boolean >;
|
||||
export type DeleteFn = ( id: ModelID ) => Promise< boolean >;
|
||||
|
||||
/**
|
||||
* A callback for deleting a child model from a data source.
|
||||
*
|
||||
* @callback DeleteChildFn
|
||||
* @param {P} parent The parent identifier for the model.
|
||||
* @param {ModelID} childID The ID of the model.
|
||||
* @return {Promise.<boolean>} Resolves to true once the model has been deleted.
|
||||
* @template {ModelParentID} P
|
||||
*/
|
||||
export type DeleteChildFn< T extends ModelRepositoryParams > = ( parent: ParentID< T >, childID: ModelID ) => Promise< boolean >;
|
||||
|
||||
/**
|
||||
* An interface for repositories that can list models.
|
||||
*
|
||||
* @typedef ListsModels
|
||||
* @property {ListFn.<T,L>} list Lists models using the repository.
|
||||
* @template {Model} T
|
||||
* @template L
|
||||
*/
|
||||
export interface ListsModels< T extends ModelRepositoryParams > {
|
||||
list( params?: HasParent< T, never, ListParams< T > > ): Promise< ModelClass< T >[] >;
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface for repositories that can list child models.
|
||||
*
|
||||
* @typedef ListsChildModels
|
||||
* @property {ListChildFn.<T,P,L>} list Lists models using the repository.
|
||||
* @template {Model} T
|
||||
* @template {ModelParentID} P
|
||||
* @template L
|
||||
*/
|
||||
export interface ListsChildModels< T extends ModelRepositoryParams > {
|
||||
list(
|
||||
parent: HasParent< T, ParentID< T >, never >,
|
||||
params?: HasParent< T, ListParams< T >, never >,
|
||||
): Promise< ModelClass< T >[] >;
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface for repositories that can create models.
|
||||
*
|
||||
* @typedef CreatesModels
|
||||
* @property {CreateFn} create Creates a model using the repository.
|
||||
* @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 > >;
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface for repositories that can read models.
|
||||
*
|
||||
* @typedef ReadsModels
|
||||
* @property {ReadFn} read Reads a model using the repository.
|
||||
* @property {ReadFn.<T>} read Reads a model using the repository.
|
||||
* @template {Model} T
|
||||
*/
|
||||
export interface ReadsModels< T extends Model, IDParam = number > {
|
||||
read( id: IDParam ): Promise< T >;
|
||||
export interface ReadsModels< T extends ModelRepositoryParams > {
|
||||
read( id: HasParent< T, never, ModelID > ): Promise< ModelClass< T > >;
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface for repositories that can read models that are children.
|
||||
*
|
||||
* @typedef ReadsChildModels
|
||||
* @property {ReadChildFn.<T,P>} read Reads a model using the repository.
|
||||
* @template {Model} T
|
||||
* @template {ModelParentID} P
|
||||
*/
|
||||
export interface ReadsChildModels< T extends ModelRepositoryParams > {
|
||||
read(
|
||||
parent: HasParent< T, ParentID< T >, never >,
|
||||
childID: HasParent< T, ModelID, never >,
|
||||
): Promise< ModelClass< T > >;
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface for repositories that can update models.
|
||||
*
|
||||
* @typedef UpdatesModels
|
||||
* @property {UpdateFn} update Updates a model using the repository.
|
||||
* @property {UpdateFn.<T>} update Updates a model using the repository.
|
||||
* @template {Model} T
|
||||
*/
|
||||
export interface UpdatesModels< T extends Model, IDParam = number > {
|
||||
update( id: IDParam, properties: Partial< T > ): Promise< T >;
|
||||
export interface UpdatesModels< T extends ModelRepositoryParams > {
|
||||
update(
|
||||
id: HasParent< T, never, ModelID >,
|
||||
properties: HasParent< T, never, UpdateParams< T > >,
|
||||
): Promise< ModelClass< T > >;
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface for repositories that can update models.
|
||||
*
|
||||
* @typedef UpdatesChildModels
|
||||
* @property {UpdateChildFn.<T,P>} update Updates a model using the repository.
|
||||
* @template {Model} T
|
||||
* @template {ModelParentID} P
|
||||
*/
|
||||
export interface UpdatesChildModels< T extends ModelRepositoryParams > {
|
||||
update(
|
||||
parent: HasParent< T, ParentID< T >, never >,
|
||||
childID: HasParent< T, ModelID, never >,
|
||||
properties: HasParent< T, UpdateParams< T >, never >,
|
||||
): Promise< ModelClass< T > >;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -72,24 +250,54 @@ export interface UpdatesModels< T extends Model, IDParam = number > {
|
|||
* @typedef DeletesModels
|
||||
* @property {DeleteFn} delete Deletes a model using the repository.
|
||||
*/
|
||||
export interface DeletesModels< IDParam = number > {
|
||||
delete( id: IDParam ): Promise< boolean >;
|
||||
export interface DeletesModels< T extends ModelRepositoryParams > {
|
||||
delete( id: HasParent< T, never, ModelID > ): Promise< boolean >;
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface for repositories that can delete models.
|
||||
*
|
||||
* @typedef DeletesModels
|
||||
* @property {DeleteChildFn.<P>} delete Deletes a model using the repository.
|
||||
* @template {ModelParentID} P
|
||||
*/
|
||||
export interface DeletesChildModels< T extends ModelRepositoryParams > {
|
||||
delete(
|
||||
parent: HasParent< T, ParentID< T >, never >,
|
||||
childID: HasParent< T, ModelID, never >,
|
||||
): Promise< boolean >;
|
||||
}
|
||||
|
||||
/**
|
||||
* A class for performing CRUD operations on models using a number of internal hooks.
|
||||
* Note that if a model does not support a given operation then it will throw an
|
||||
* error when attempting to perform that action.
|
||||
*
|
||||
* @template {Model} T
|
||||
* @template {ModelParentID} P
|
||||
* @template {Object} L
|
||||
*/
|
||||
export class ModelRepository< T extends Model, IDParam = number > implements
|
||||
CreatesModels< T >,
|
||||
ReadsModels< T, IDParam >,
|
||||
UpdatesModels< T, IDParam >,
|
||||
DeletesModels< IDParam > {
|
||||
export class ModelRepository< T extends ModelRepositoryParams > implements
|
||||
ListsModels< T >,
|
||||
ListsChildModels< T >,
|
||||
ReadsModels< T >,
|
||||
ReadsChildModels< T >,
|
||||
UpdatesModels< T >,
|
||||
UpdatesChildModels< T >,
|
||||
DeletesModels< T >,
|
||||
DeletesChildModels< T > {
|
||||
/**
|
||||
* The hook used to list models.
|
||||
*
|
||||
* @type {ListFn.<T,P,L>|ListChildFn<T,P,L>}
|
||||
* @private
|
||||
*/
|
||||
private readonly listHook: HasParent< T, ListChildFn< T >, ListFn< T > > | null;
|
||||
|
||||
/**
|
||||
* The hook used to create models
|
||||
*
|
||||
* @type {CreateFn}
|
||||
* @type {CreateFn.<T>}
|
||||
* @private
|
||||
*/
|
||||
private readonly createHook: CreateFn< T > | null;
|
||||
|
@ -97,41 +305,44 @@ export class ModelRepository< T extends Model, IDParam = number > implements
|
|||
/**
|
||||
* The hook used to read models.
|
||||
*
|
||||
* @type {ReadFn}
|
||||
* @type {ReadFn.<T>|ReadChildFn.<T,P>}
|
||||
* @private
|
||||
*/
|
||||
private readonly readHook: ReadFn< IDParam, T > | null;
|
||||
private readonly readHook: HasParent< T, ReadChildFn< T >, ReadFn< T > > | null;
|
||||
|
||||
/**
|
||||
* The hook used to update models.
|
||||
*
|
||||
* @type {UpdateFn}
|
||||
* @type {UpdateFn.<T>|UpdateChildFn.<T,P>}
|
||||
* @private
|
||||
*/
|
||||
private readonly updateHook: UpdateFn< IDParam, T > | null;
|
||||
private readonly updateHook: HasParent< T, UpdateChildFn< T >, UpdateFn< T > > | null;
|
||||
|
||||
/**
|
||||
* The hook used to delete models.
|
||||
*
|
||||
* @type {DeleteFn}
|
||||
* @type {DeleteFn|DeleteChildFn.<P>}
|
||||
* @private
|
||||
*/
|
||||
private readonly deleteHook: DeleteFn< IDParam > | null;
|
||||
private readonly deleteHook: HasParent< T, DeleteChildFn< T >, DeleteFn > | null;
|
||||
|
||||
/**
|
||||
* Creates a new repository instance.
|
||||
*
|
||||
* @param {CreateFn|null} createHook The hook for model creation.
|
||||
* @param {ReadFn|null} readHook The hook for model reading.
|
||||
* @param {UpdateFn|null} updateHook The hook for model updating.
|
||||
* @param {DeleteFn|null} deleteHook The hook for model deletion.
|
||||
* @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>|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< T, ListChildFn< T >, ListFn< T > > | null,
|
||||
createHook: CreateFn< T > | null,
|
||||
readHook: ReadFn< IDParam, T > | null,
|
||||
updateHook: UpdateFn< IDParam, T > | null,
|
||||
deleteHook: DeleteFn< IDParam > | 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;
|
||||
this.readHook = readHook;
|
||||
this.updateHook = updateHook;
|
||||
|
@ -139,12 +350,39 @@ export class ModelRepository< T extends Model, IDParam = number > implements
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates the given model.
|
||||
* Lists models using the repository.
|
||||
*
|
||||
* @param {Object} properties The properties for the model we'd like to create.
|
||||
* @return {Promise.<Model>} A promise that resolves to the model after creation.
|
||||
* @param {L|P} [paramsOrParent] The params for the lookup or the parent to list if the model is a child.
|
||||
* @param {L} [params] The params when using the parent.
|
||||
* @return {Promise.<Array.<T>>} Resolves to the listed models.
|
||||
*/
|
||||
public create( properties: Partial< T > ): Promise< T > {
|
||||
public list(
|
||||
paramsOrParent?: HasParent< T, ParentID< T >, ListParams< T > >,
|
||||
params?: HasParent< T, ListParams< T >, never >,
|
||||
): Promise< ModelClass< T >[] > {
|
||||
if ( ! this.listHook ) {
|
||||
throw new Error( 'The \'list\' operation is not supported on this model.' );
|
||||
}
|
||||
|
||||
if ( params === undefined ) {
|
||||
return ( this.listHook as ListFn< T > )(
|
||||
paramsOrParent as ListParams< T >,
|
||||
);
|
||||
}
|
||||
|
||||
return ( this.listHook as ListChildFn< T > )(
|
||||
paramsOrParent as ParentID< T >,
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new model using the repository.
|
||||
*
|
||||
* @param {Partial.<T>} properties The properties to create the model with.
|
||||
* @return {Promise.<T>} Resolves to the created model.
|
||||
*/
|
||||
public create( properties: Partial< ModelClass< T > > ): Promise< ModelClass< T > > {
|
||||
if ( ! this.createHook ) {
|
||||
throw new Error( 'The \'create\' operation is not supported on this model.' );
|
||||
}
|
||||
|
@ -153,45 +391,87 @@ export class ModelRepository< T extends Model, IDParam = number > implements
|
|||
}
|
||||
|
||||
/**
|
||||
* Reads the given model.
|
||||
* Reads a model using the repository.
|
||||
*
|
||||
* @param {number|Object} id The identifier for the model to read.
|
||||
* @return {Promise.<Model>} A promise that resolves to the model.
|
||||
* @param {ModelID|P} idOrParent The ID of the model or its parent if the model is a child.
|
||||
* @param {ModelID} [childID] The ID of the model when using the parent.
|
||||
* @return {Promise.<T>} Resolves to the loaded model.
|
||||
*/
|
||||
public read( id: IDParam ): Promise< T > {
|
||||
public read(
|
||||
idOrParent: HasParent< T, ParentID< T >, ModelID >,
|
||||
childID?: HasParent< T, ModelID, never >,
|
||||
): Promise< ModelClass< T > > {
|
||||
if ( ! this.readHook ) {
|
||||
throw new Error( 'The \'read\' operation is not supported on this model.' );
|
||||
}
|
||||
|
||||
return this.readHook( id );
|
||||
if ( childID === undefined ) {
|
||||
return ( this.readHook as ReadFn< T > )(
|
||||
idOrParent as ModelID,
|
||||
);
|
||||
}
|
||||
|
||||
return ( this.readHook as ReadChildFn< T > )(
|
||||
idOrParent as ParentID< T >,
|
||||
childID,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the given model.
|
||||
* Updates the model's properties using the repository.
|
||||
*
|
||||
* @param {number|Object} id The identifier for the model to create.
|
||||
* @param {Object} properties The model properties that we'd like to update.
|
||||
* @return {Promise.<Model>} A promise that resolves to the model after updating.
|
||||
* @param {ModelID|P} idOrParent The ID of the model or its parent if the model is a child.
|
||||
* @param {Partial.<T>|ModelID} propertiesOrChildID The properties for the model or the ID when using the parent.
|
||||
* @param {Partial.<T>} [properties] The properties for child models.
|
||||
* @return {Promise.<T>} Resolves to the updated model.
|
||||
*/
|
||||
public update( id: IDParam, properties: Partial< T > ): Promise< T > {
|
||||
public update(
|
||||
idOrParent: HasParent< T, ParentID< T >, ModelID >,
|
||||
propertiesOrChildID: HasParent< T, ModelID, UpdateParams< T > >,
|
||||
properties?: HasParent< T, UpdateParams< T >, never >,
|
||||
): Promise< ModelClass< T > > {
|
||||
if ( ! this.updateHook ) {
|
||||
throw new Error( 'The \'update\' operation is not supported on this model.' );
|
||||
}
|
||||
|
||||
return this.updateHook( id, properties );
|
||||
if ( properties === undefined ) {
|
||||
return ( this.updateHook as UpdateFn< T > )(
|
||||
idOrParent as ModelID,
|
||||
propertiesOrChildID as UpdateParams< T >,
|
||||
);
|
||||
}
|
||||
|
||||
return ( this.updateHook as UpdateChildFn< T > )(
|
||||
idOrParent as ParentID< T >,
|
||||
propertiesOrChildID as ModelID,
|
||||
properties,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given model.
|
||||
* Deletes a model using the repository.
|
||||
*
|
||||
* @param {number|Object} id The identifier for the model to delete.
|
||||
* @return {Promise.<boolean>} A promise that resolves to "true" on success.
|
||||
* @param {ModelID|P} idOrParent The ID of the model or its parent if the model is a child.
|
||||
* @param {ModelID} [childID] The ID of the model when using the parent.
|
||||
* @return {Promise.<T>} Resolves to the loaded model.
|
||||
*/
|
||||
public delete( id: IDParam ): Promise< boolean > {
|
||||
public delete(
|
||||
idOrParent: HasParent< T, ParentID< T >, ModelID >,
|
||||
childID?: HasParent< T, ModelID, never >,
|
||||
): Promise< boolean > {
|
||||
if ( ! this.deleteHook ) {
|
||||
throw new Error( 'The \'delete\' operation is not supported on this model.' );
|
||||
}
|
||||
|
||||
return this.deleteHook( id );
|
||||
if ( childID === undefined ) {
|
||||
return ( this.deleteHook as DeleteFn )(
|
||||
idOrParent as ModelID,
|
||||
);
|
||||
}
|
||||
|
||||
return ( this.deleteHook as DeleteChildFn< T > )(
|
||||
idOrParent as ParentID< T >,
|
||||
childID,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
import axios, { AxiosInstance } from 'axios';
|
||||
import * as moxios from 'moxios';
|
||||
import { AxiosInterceptor } from '../axios-interceptor';
|
||||
|
||||
class TestInterceptor extends AxiosInterceptor {}
|
||||
|
||||
describe( 'AxiosInterceptor', () => {
|
||||
let interceptors: TestInterceptor[];
|
||||
let axiosInstance: AxiosInstance;
|
||||
|
||||
beforeEach( () => {
|
||||
axiosInstance = axios.create();
|
||||
moxios.install( axiosInstance );
|
||||
interceptors = [];
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
for ( const interceptor of interceptors ) {
|
||||
interceptor.stop( axiosInstance );
|
||||
}
|
||||
moxios.uninstall( axiosInstance );
|
||||
} );
|
||||
|
||||
it( 'should not break interceptor chaining for success', async () => {
|
||||
moxios.stubRequest( 'http://test.test', { status: 200 } );
|
||||
|
||||
interceptors.push( new TestInterceptor() );
|
||||
interceptors.push( new TestInterceptor() );
|
||||
interceptors.push( new TestInterceptor() );
|
||||
for ( const interceptor of interceptors ) {
|
||||
interceptor.start( axiosInstance );
|
||||
}
|
||||
|
||||
const response = await axiosInstance.get( 'http://test.test' );
|
||||
|
||||
expect( response.status ).toBe( 200 );
|
||||
} );
|
||||
|
||||
it( 'should not break interceptor chaining for errors', async () => {
|
||||
moxios.stubRequest( 'http://test.test', { status: 401 } );
|
||||
|
||||
interceptors.push( new TestInterceptor() );
|
||||
interceptors.push( new TestInterceptor() );
|
||||
interceptors.push( new TestInterceptor() );
|
||||
for ( const interceptor of interceptors ) {
|
||||
interceptor.start( axiosInstance );
|
||||
}
|
||||
|
||||
await expect( axiosInstance.get( 'http://test.test' ) ).rejects.toBeInstanceOf( Error );
|
||||
} );
|
||||
} );
|
|
@ -49,9 +49,7 @@ describe( 'AxiosResponseInterceptor', () => {
|
|||
responseText: JSON.stringify( { code: 'error_code', message: 'value' } ),
|
||||
} );
|
||||
|
||||
const response = await axiosInstance.get( 'http://test.test' );
|
||||
|
||||
expect( response ).toMatchObject( {
|
||||
await expect( axiosInstance.get( 'http://test.test' ) ).rejects.toMatchObject( {
|
||||
statusCode: 404,
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
import { buildURL } from '../utils';
|
||||
|
||||
describe( 'buildURL', () => {
|
||||
it( 'should use base when given no url', () => {
|
||||
const url = buildURL( { baseURL: 'http://test.test' } );
|
||||
expect( url ).toBe( 'http://test.test' );
|
||||
} );
|
||||
|
||||
it( 'should use url when given absolute', () => {
|
||||
const url = buildURL( { baseURL: 'http://test.test', url: 'http://override.test' } );
|
||||
expect( url ).toBe( 'http://override.test' );
|
||||
} );
|
||||
|
||||
it( 'should combine base and url', () => {
|
||||
const url = buildURL( { baseURL: 'http://test.test', url: 'yes/test' } );
|
||||
expect( url ).toBe( 'http://test.test/yes/test' );
|
||||
} );
|
||||
|
||||
it( 'should combine base and url with trailing/leading slashes', () => {
|
||||
const url = buildURL( { baseURL: 'http://test.test/////', url: '////yes/test' } );
|
||||
expect( url ).toBe( 'http://test.test/yes/test' );
|
||||
} );
|
||||
} );
|
|
@ -8,7 +8,7 @@ import { AxiosResponseInterceptor } from './axios-response-interceptor';
|
|||
*/
|
||||
export class AxiosClient implements HTTPClient {
|
||||
/**
|
||||
* An instance of the axios client for making HTTP requests.
|
||||
* An instance of the Axios client for making HTTP requests.
|
||||
*
|
||||
* @type {AxiosInstance}
|
||||
* @private
|
||||
|
@ -75,7 +75,7 @@ export class AxiosClient implements HTTPClient {
|
|||
* Performs a PUT request.
|
||||
*
|
||||
* @param {string} path The path we should send the request to.
|
||||
* @param {Object} data Any parameters that should be passed in the request.
|
||||
* @param {Object} data Any parameters that should be passed in the request.
|
||||
* @return {Promise.<HTTPResponse>} The response from the API.
|
||||
*/
|
||||
public put< T = any >(
|
||||
|
@ -89,7 +89,7 @@ export class AxiosClient implements HTTPClient {
|
|||
* Performs a PATCH request.
|
||||
*
|
||||
* @param {string} path The path we should query.
|
||||
* @param {*} data Any parameters that should be passed in the request.
|
||||
* @param {*} data Any parameters that should be passed in the request.
|
||||
* @return {Promise.<HTTPResponse>} The response from the API.
|
||||
*/
|
||||
public patch< T = any >(
|
||||
|
@ -103,7 +103,7 @@ export class AxiosClient implements HTTPClient {
|
|||
* Performs a DELETE request.
|
||||
*
|
||||
* @param {string} path The path we should send the request to.
|
||||
* @param {*} data Any parameters that should be passed in the request.
|
||||
* @param {*} data Any parameters that should be passed in the request.
|
||||
* @return {Promise.<HTTPResponse>} The response from the API.
|
||||
*/
|
||||
public delete< T = any >(
|
||||
|
|
|
@ -15,7 +15,7 @@ type ActiveInterceptor = {
|
|||
}
|
||||
|
||||
/**
|
||||
* A base class for encapsulating the start and stop functionality required by all axios interceptors.
|
||||
* A base class for encapsulating the start and stop functionality required by all Axios interceptors.
|
||||
*/
|
||||
export abstract class AxiosInterceptor {
|
||||
/**
|
||||
|
@ -61,7 +61,7 @@ export abstract class AxiosInterceptor {
|
|||
/**
|
||||
* An interceptor method for handling requests before they are made to the server.
|
||||
*
|
||||
* @param {AxiosRequestConfig} config The axios request options.
|
||||
* @param {AxiosRequestConfig} config The Axios request options.
|
||||
*/
|
||||
protected handleRequest( config: AxiosRequestConfig ): AxiosRequestConfig {
|
||||
return config;
|
||||
|
@ -70,7 +70,7 @@ export abstract class AxiosInterceptor {
|
|||
/**
|
||||
* An interceptor method for handling successful responses.
|
||||
*
|
||||
* @param {AxiosResponse} response The response from the axios client.
|
||||
* @param {*} response The response from the Axios client.
|
||||
*/
|
||||
protected onResponseSuccess( response: AxiosResponse ): any {
|
||||
return response;
|
||||
|
@ -79,9 +79,9 @@ export abstract class AxiosInterceptor {
|
|||
/**
|
||||
* An interceptor method for handling response failures.
|
||||
*
|
||||
* @param {*} error The error that occurred.
|
||||
* @param {Promise} error The error that occurred.
|
||||
*/
|
||||
protected onResponseRejected( error: any ): any {
|
||||
return error;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import type { AxiosRequestConfig } from 'axios';
|
|||
import * as createHmac from 'create-hmac';
|
||||
import * as OAuth from 'oauth-1.0a';
|
||||
import { AxiosInterceptor } from './axios-interceptor';
|
||||
import { buildURL } from './utils';
|
||||
|
||||
/**
|
||||
* A utility class for managing the lifecycle of an authentication interceptor.
|
||||
|
@ -43,7 +44,7 @@ export class AxiosOAuthInterceptor extends AxiosInterceptor {
|
|||
* @return {AxiosRequestConfig} The request with the additional authorization headers.
|
||||
*/
|
||||
protected handleRequest( request: AxiosRequestConfig ): AxiosRequestConfig {
|
||||
const url = ( request.baseURL || '' ) + ( request.url || '' );
|
||||
const url = buildURL( request );
|
||||
if ( url.startsWith( 'https' ) ) {
|
||||
request.auth = {
|
||||
username: this.oauth.consumer.key,
|
||||
|
|
|
@ -7,26 +7,21 @@ export class AxiosResponseInterceptor extends AxiosInterceptor {
|
|||
* Transforms the Axios response into our HTTP response.
|
||||
*
|
||||
* @param {AxiosResponse} response The response that we need to transform.
|
||||
* @return {Promise} A promise containing the HTTPResponse.
|
||||
* @return {HTTPResponse} The HTTP response.
|
||||
*/
|
||||
protected onResponseSuccess( response: AxiosResponse ): Promise< HTTPResponse > {
|
||||
return Promise.resolve< HTTPResponse >(
|
||||
new HTTPResponse( response.status, response.headers, response.data ),
|
||||
);
|
||||
protected onResponseSuccess( response: AxiosResponse ): HTTPResponse {
|
||||
return new HTTPResponse( response.status, response.headers, response.data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Axios throws HTTP errors so we need to eat those errors and pass them normally.
|
||||
*
|
||||
* @param {*} error The error that was caught.
|
||||
* @return {Promise} A promise containing the HTTPResponse.
|
||||
*/
|
||||
protected onResponseRejected( error: any ): Promise< HTTPResponse > {
|
||||
protected onResponseRejected( error: any ): never {
|
||||
// Convert HTTP response errors into a form that we can handle them with.
|
||||
if ( error.response ) {
|
||||
return Promise.resolve< HTTPResponse >(
|
||||
new HTTPResponse( error.response.status, error.response.headers, error.response.data ),
|
||||
);
|
||||
throw new HTTPResponse( error.response.status, error.response.headers, error.response.data );
|
||||
}
|
||||
|
||||
throw error;
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import { AxiosRequestConfig } from 'axios';
|
||||
|
||||
/**
|
||||
* Given an Axios request config this function generates the URL that Axios will
|
||||
* use to make the request.
|
||||
*
|
||||
* @param {AxiosRequestConfig} request The Axios request we're building the URL for.
|
||||
* @return {string} The merged URL.
|
||||
*/
|
||||
export function buildURL( request: AxiosRequestConfig ): string {
|
||||
const base = request.baseURL || '';
|
||||
if ( ! request.url ) {
|
||||
return base;
|
||||
}
|
||||
|
||||
// Axios ignores the base when the URL is absolute.
|
||||
const url = request.url;
|
||||
if ( ! base || url.match( /^([a-z][a-z\d+\-.]*:)?\/\/[^\/]/i ) ) {
|
||||
return url;
|
||||
}
|
||||
|
||||
// Remove trailing slashes from the base and leading slashes from the URL so we can combine them consistently.
|
||||
return base.replace( /\/+$/, '' ) + '/' + url.replace( /^\/+/, '' );
|
||||
}
|
|
@ -12,7 +12,7 @@ export class HTTPResponse< T = any > {
|
|||
/**
|
||||
* The headers from the response.
|
||||
*
|
||||
* @type {Object.<string, string|string[]>}
|
||||
* @type {Object.<string,string|string[]>}
|
||||
*/
|
||||
public readonly headers: any;
|
||||
|
||||
|
@ -27,7 +27,7 @@ export class HTTPResponse< T = any > {
|
|||
* Creates a new HTTP response instance.
|
||||
*
|
||||
* @param {number} statusCode The status code from the HTTP response.
|
||||
* @param {Object.<string, string|string[]>} headers The headers from the HTTP response.
|
||||
* @param {Object.<string,string|string[]>} headers The headers from the HTTP response.
|
||||
* @param {Object} data The data from the HTTP response.
|
||||
*/
|
||||
public constructor( statusCode: number, headers: any, data: T ) {
|
||||
|
@ -45,7 +45,7 @@ export interface HTTPClient {
|
|||
* Performs a GET request.
|
||||
*
|
||||
* @param {string} path The path we should send the request to.
|
||||
* @param {*} params Any parameters that should be passed in the request.
|
||||
* @param {*} params Any parameters that should be passed in the request.
|
||||
* @return {Promise.<HTTPResponse>} The response from the API.
|
||||
*/
|
||||
get< T = any >( path: string, params?: any ): Promise< HTTPResponse< T > >;
|
||||
|
@ -54,7 +54,7 @@ export interface HTTPClient {
|
|||
* Performs a POST request.
|
||||
*
|
||||
* @param {string} path The path we should send the request to.
|
||||
* @param {*} data Any parameters that should be passed in the request.
|
||||
* @param {*} data Any parameters that should be passed in the request.
|
||||
* @return {Promise.<HTTPResponse>} The response from the API.
|
||||
*/
|
||||
post< T = any >( path: string, data?: any ): Promise< HTTPResponse< T > >;
|
||||
|
@ -63,7 +63,7 @@ export interface HTTPClient {
|
|||
* Performs a PUT request.
|
||||
*
|
||||
* @param {string} path The path we should send the request to.
|
||||
* @param {*} data Any parameters that should be passed in the request.
|
||||
* @param {*} data Any parameters that should be passed in the request.
|
||||
* @return {Promise.<HTTPResponse>} The response from the API.
|
||||
*/
|
||||
put< T = any >( path: string, data?: any ): Promise< HTTPResponse< T > >;
|
||||
|
@ -72,7 +72,7 @@ export interface HTTPClient {
|
|||
* Performs a PATCH request.
|
||||
*
|
||||
* @param {string} path The path we should send the request to.
|
||||
* @param {*} data Any parameters that should be passed in the request.
|
||||
* @param {*} data Any parameters that should be passed in the request.
|
||||
* @return {Promise.<HTTPResponse>} The response from the API.
|
||||
*/
|
||||
patch< T = any >( path: string, data?: any ): Promise< HTTPResponse< T > >;
|
||||
|
@ -81,7 +81,7 @@ export interface HTTPClient {
|
|||
* Performs a DELETE request.
|
||||
*
|
||||
* @param {string} path The path we should send the request to.
|
||||
* @param {*} data Any parameters that should be passed in the request.
|
||||
* @param {*} data Any parameters that should be passed in the request.
|
||||
* @return {Promise.<HTTPResponse>} The response from the API.
|
||||
*/
|
||||
delete< T = any >( path: string, data?: any ): Promise< HTTPResponse< T > >;
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
export { HTTPClientFactory } from './http';
|
||||
export * from './models';
|
||||
export * from './services';
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
export { SimpleProduct } from './products/simple-product';
|
||||
|
||||
export { SettingGroup } from './settings/setting-group';
|
||||
export { Setting } from './settings/setting';
|
||||
|
|
|
@ -1,11 +1,19 @@
|
|||
/**
|
||||
* A base class for all models.
|
||||
* The ID of a model.
|
||||
*
|
||||
* @typedef ModelID
|
||||
* @alias string|number
|
||||
*/
|
||||
export type ModelID = string | number;
|
||||
|
||||
/**
|
||||
* The base class for all models.
|
||||
*/
|
||||
export abstract class Model {
|
||||
/**
|
||||
* The ID of the model if it exists.
|
||||
*
|
||||
* @type {number|null}
|
||||
* @type {string|number|null}
|
||||
*/
|
||||
public readonly id: number | null = null;
|
||||
public readonly id: ModelID | undefined;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,21 @@
|
|||
import { AbstractProduct } from './abstract-product';
|
||||
import { HTTPClient } from '../../http';
|
||||
import { CreatesModels } from '../../framework/model-repository';
|
||||
import { simpleProductRESTRepository } from '../../repositories/rest/products/simple-product';
|
||||
import { CreatesModels, ModelRepositoryParams } from '../../framework/model-repository';
|
||||
|
||||
/**
|
||||
* The parameters embedded in this generic can be used in the ModelRepository in order to give
|
||||
* type-safety in an incredibly granular way.
|
||||
*/
|
||||
export type SimpleProductRepositoryParams = ModelRepositoryParams< SimpleProduct, never, never, 'regularPrice' >;
|
||||
|
||||
/**
|
||||
* An interface for creating simple products using the repository.
|
||||
*
|
||||
* @typedef CreatesSimpleProducts
|
||||
* @alias CreatesModels.<SimpleProduct>
|
||||
*/
|
||||
export type CreatesSimpleProducts = CreatesModels< SimpleProductRepositoryParams >;
|
||||
|
||||
/**
|
||||
* A simple product object.
|
||||
|
@ -21,9 +35,8 @@ export class SimpleProduct extends AbstractProduct {
|
|||
* Creates a model repository configured for communicating via the REST API.
|
||||
*
|
||||
* @param {HTTPClient} httpClient The client for communicating via HTTP.
|
||||
* @return {CreatesModels} The created repository.
|
||||
*/
|
||||
public static restRepository( httpClient: HTTPClient ): CreatesModels< SimpleProduct > {
|
||||
public static restRepository( httpClient: HTTPClient ): ReturnType< typeof simpleProductRESTRepository > {
|
||||
return simpleProductRESTRepository( httpClient );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
import { Model, ModelID } from '../model';
|
||||
import { HTTPClient } from '../../http';
|
||||
import { settingGroupRESTRepository } from '../../repositories/rest/settings/setting-group';
|
||||
import { ListsModels, ModelRepositoryParams } from '../../framework/model-repository';
|
||||
|
||||
/**
|
||||
* The parameters embedded in this generic can be used in the ModelRepository in order to give
|
||||
* type-safety in an incredibly granular way.
|
||||
*/
|
||||
export type SettingGroupRepositoryParams = ModelRepositoryParams< SettingGroup >;
|
||||
|
||||
/**
|
||||
* An interface for listing setting groups using the repository.
|
||||
*
|
||||
* @typedef ListsSettingGroups
|
||||
* @alias ListsModels.<SettingGroup>
|
||||
*/
|
||||
export type ListsSettingGroups = ListsModels< SettingGroupRepositoryParams >;
|
||||
|
||||
/**
|
||||
* A settings group object.
|
||||
*/
|
||||
export class SettingGroup extends Model {
|
||||
/**
|
||||
* The label of the setting group.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly label: string = '';
|
||||
|
||||
/**
|
||||
* The description of the setting group.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly description: string = '';
|
||||
|
||||
/**
|
||||
* The ID of the group this is a child of.
|
||||
*
|
||||
* @type {ModelID|null}
|
||||
*/
|
||||
public readonly parentID: ModelID | null = null;
|
||||
|
||||
/**
|
||||
* Creates a new setting group instance with the given properties
|
||||
*
|
||||
* @param {Object} properties The properties to set in the object.
|
||||
*/
|
||||
public constructor( properties: Partial< SettingGroup > = {} ) {
|
||||
super();
|
||||
Object.assign( this, properties );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the repository for interacting with this type of model.
|
||||
*
|
||||
* @param {HTTPClient} httpClient The client for communicating via HTTP.
|
||||
*/
|
||||
public static restRepository( httpClient: HTTPClient ): ReturnType< typeof settingGroupRESTRepository > {
|
||||
return settingGroupRESTRepository( httpClient );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
import { Model, ModelID } from '../model';
|
||||
import { HTTPClient } from '../../http';
|
||||
import { settingRESTRepository } from '../../repositories/rest/settings/setting';
|
||||
import {
|
||||
ModelRepositoryParams,
|
||||
ListsChildModels,
|
||||
ReadsChildModels,
|
||||
UpdatesChildModels,
|
||||
} from '../../framework/model-repository';
|
||||
|
||||
/**
|
||||
* The parameters embedded in this generic can be used in the ModelRepository in order to give
|
||||
* type-safety in an incredibly granular way.
|
||||
*/
|
||||
export type SettingRepositoryParams = ModelRepositoryParams< Setting, ModelID, never, 'value' >;
|
||||
|
||||
/**
|
||||
* An interface for listing settings using the repository.
|
||||
*
|
||||
* @typedef ListsSettings
|
||||
* @alias ListsChildModels.<Setting,SettingParentID>
|
||||
*/
|
||||
export type ListsSettings = ListsChildModels< SettingRepositoryParams >;
|
||||
|
||||
/**
|
||||
* An interface for reading settings using the repository.
|
||||
*
|
||||
* @typedef ReadsSettings
|
||||
* @alias ReadsChildModels.<Setting,SettingParentID>
|
||||
*/
|
||||
export type ReadsSettings = ReadsChildModels< SettingRepositoryParams >;
|
||||
|
||||
/**
|
||||
* An interface for updating settings using the repository.
|
||||
*
|
||||
* @typedef UpdatesSettings
|
||||
* @alias UpdatesChildModels.<Setting,SettingParentID>
|
||||
*/
|
||||
export type UpdatesSettings = UpdatesChildModels< SettingRepositoryParams >;
|
||||
|
||||
/**
|
||||
* The default types of settings that are available.
|
||||
*/
|
||||
type SettingType = 'text' | 'select' | 'multiselect' | 'checkbox' | 'number';
|
||||
|
||||
/**
|
||||
* A setting object.
|
||||
*/
|
||||
export class Setting extends Model {
|
||||
/**
|
||||
* The label of the setting.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly label: string = '';
|
||||
|
||||
/**
|
||||
* The description of the setting.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly description: string = '';
|
||||
|
||||
/**
|
||||
* The type of the setting.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly type: string | SettingType = '';
|
||||
|
||||
/**
|
||||
* The options of the setting, if it has any.
|
||||
*
|
||||
* @type {Object.<string, string>|null}
|
||||
*/
|
||||
public readonly options: { [key: string]: string } | undefined;
|
||||
|
||||
/**
|
||||
* The default value for the setting.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly default: string = '';
|
||||
|
||||
/**
|
||||
* The current value of the setting.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
public readonly value: string = '';
|
||||
|
||||
/**
|
||||
* Creates a new setting instance with the given properties
|
||||
*
|
||||
* @param {Object} properties The properties to set in the object.
|
||||
*/
|
||||
public constructor( properties: Partial< Setting > = {} ) {
|
||||
super();
|
||||
Object.assign( this, properties );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the repository for interacting with this type of model.
|
||||
*
|
||||
* @param {HTTPClient} httpClient The client for communicating via HTTP.
|
||||
*/
|
||||
public static restRepository( httpClient: HTTPClient ): ReturnType< typeof settingRESTRepository > {
|
||||
return settingRESTRepository( httpClient );
|
||||
}
|
||||
}
|
|
@ -2,11 +2,10 @@ import { simpleProductRESTRepository } from '../simple-product';
|
|||
import { mock, MockProxy } from 'jest-mock-extended';
|
||||
import { HTTPClient, HTTPResponse } from '../../../../http';
|
||||
import { SimpleProduct } from '../../../../models';
|
||||
import { CreatesModels } from '../../../../framework/model-repository';
|
||||
|
||||
describe( 'simpleProductRESTRepository', () => {
|
||||
let httpClient: MockProxy< HTTPClient >;
|
||||
let repository: CreatesModels< SimpleProduct >;
|
||||
let repository: ReturnType< typeof simpleProductRESTRepository >;
|
||||
|
||||
beforeEach( () => {
|
||||
httpClient = mock< HTTPClient >();
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
import { HTTPClient } from '../../../http';
|
||||
import { CreateFn, CreatesModels, ModelRepository } from '../../../framework/model-repository';
|
||||
import { CreateFn, ModelRepository } from '../../../framework/model-repository';
|
||||
import { SimpleProduct } from '../../../models';
|
||||
import { CreatesSimpleProducts, SimpleProductRepositoryParams } from '../../../models/products/simple-product';
|
||||
|
||||
/**
|
||||
* Creates a callback for REST model creation.
|
||||
*
|
||||
* @param {HTTPClient} httpClient The HTTP client for requests.
|
||||
* @return {CreateFn} The callback for creating models via the REST API.
|
||||
*/
|
||||
function restCreate( httpClient: HTTPClient ): CreateFn< SimpleProduct > {
|
||||
function restCreate( httpClient: HTTPClient ): CreateFn< SimpleProductRepositoryParams > {
|
||||
return async ( properties ) => {
|
||||
const response = await httpClient.post(
|
||||
'/wc/v3/products',
|
||||
|
@ -31,10 +26,11 @@ function restCreate( httpClient: HTTPClient ): CreateFn< SimpleProduct > {
|
|||
* Creates a new ModelRepository instance for interacting with models via the REST API.
|
||||
*
|
||||
* @param {HTTPClient} httpClient The HTTP client for the REST requests to be made using.
|
||||
* @return {CreatesModels} A repository for interacting with models via the REST API.
|
||||
* @return {CreatesSimpleProducts} The created repository.
|
||||
*/
|
||||
export function simpleProductRESTRepository( httpClient: HTTPClient ): CreatesModels< SimpleProduct > {
|
||||
export function simpleProductRESTRepository( httpClient: HTTPClient ): CreatesSimpleProducts {
|
||||
return new ModelRepository(
|
||||
null,
|
||||
restCreate( httpClient ),
|
||||
null,
|
||||
null,
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
import { mock, MockProxy } from 'jest-mock-extended';
|
||||
import { HTTPClient, HTTPResponse } from '../../../../http';
|
||||
import { settingGroupRESTRepository } from '../setting-group';
|
||||
|
||||
describe( 'settingGroupRESTRepository', () => {
|
||||
let httpClient: MockProxy< HTTPClient >;
|
||||
let repository: ReturnType< typeof settingGroupRESTRepository >;
|
||||
|
||||
beforeEach( () => {
|
||||
httpClient = mock< HTTPClient >();
|
||||
repository = settingGroupRESTRepository( httpClient );
|
||||
} );
|
||||
|
||||
it( 'should list', async () => {
|
||||
httpClient.get.mockResolvedValue( new HTTPResponse(
|
||||
200,
|
||||
{},
|
||||
[
|
||||
{
|
||||
id: 'group_1',
|
||||
label: 'Test Group 1',
|
||||
},
|
||||
{
|
||||
id: 'group_2',
|
||||
label: 'Test Group 2',
|
||||
},
|
||||
],
|
||||
) );
|
||||
|
||||
const list = await repository.list();
|
||||
|
||||
expect( list ).toHaveLength( 2 );
|
||||
expect( list[ 0 ] ).toMatchObject( { id: 'group_1', label: 'Test Group 1' } );
|
||||
expect( list[ 1 ] ).toMatchObject( { id: 'group_2', label: 'Test Group 2' } );
|
||||
expect( httpClient.get ).toHaveBeenCalledWith( '/wc/v3/settings' );
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,73 @@
|
|||
import { mock, MockProxy } from 'jest-mock-extended';
|
||||
import { HTTPClient, HTTPResponse } from '../../../../http';
|
||||
import { settingRESTRepository } from '../setting';
|
||||
|
||||
describe( 'settingGroupRESTRepository', () => {
|
||||
let httpClient: MockProxy< HTTPClient >;
|
||||
let repository: ReturnType< typeof settingRESTRepository >;
|
||||
|
||||
beforeEach( () => {
|
||||
httpClient = mock< HTTPClient >();
|
||||
repository = settingRESTRepository( httpClient );
|
||||
} );
|
||||
|
||||
it( 'should list', async () => {
|
||||
httpClient.get.mockResolvedValue( new HTTPResponse(
|
||||
200,
|
||||
{},
|
||||
[
|
||||
{
|
||||
id: 'setting_1',
|
||||
label: 'Test Setting 1',
|
||||
},
|
||||
{
|
||||
id: 'setting_2',
|
||||
label: 'Test Setting 2',
|
||||
},
|
||||
],
|
||||
) );
|
||||
|
||||
const list = await repository.list( 'general' );
|
||||
|
||||
expect( list ).toHaveLength( 2 );
|
||||
expect( list[ 0 ] ).toMatchObject( { id: 'setting_1', label: 'Test Setting 1' } );
|
||||
expect( list[ 1 ] ).toMatchObject( { id: 'setting_2', label: 'Test Setting 2' } );
|
||||
expect( httpClient.get ).toHaveBeenCalledWith( '/wc/v3/settings/general' );
|
||||
} );
|
||||
|
||||
it( 'should read', async () => {
|
||||
httpClient.get.mockResolvedValue( new HTTPResponse(
|
||||
200,
|
||||
{},
|
||||
{
|
||||
id: 'setting_1',
|
||||
label: 'Test Setting',
|
||||
},
|
||||
) );
|
||||
|
||||
const read = await repository.read( 'general', 'setting_1' );
|
||||
|
||||
expect( read ).toMatchObject( { id: 'setting_1', label: 'Test Setting' } );
|
||||
expect( httpClient.get ).toHaveBeenCalledWith( '/wc/v3/settings/general/setting_1' );
|
||||
} );
|
||||
|
||||
it( 'should update', async () => {
|
||||
httpClient.patch.mockResolvedValue( new HTTPResponse(
|
||||
200,
|
||||
{},
|
||||
{
|
||||
id: 'setting_1',
|
||||
label: 'Test Setting',
|
||||
value: 'updated-value',
|
||||
},
|
||||
) );
|
||||
|
||||
const updated = await repository.update( 'general', 'setting_1', { value: 'test-value' } );
|
||||
|
||||
expect( updated ).toMatchObject( { id: 'setting_1', value: 'updated-value' } );
|
||||
expect( httpClient.patch ).toHaveBeenCalledWith(
|
||||
'/wc/v3/settings/general/setting_1',
|
||||
{ value: 'test-value' },
|
||||
);
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,38 @@
|
|||
import { HTTPClient } from '../../../http';
|
||||
import { ListFn, ModelRepository } from '../../../framework/model-repository';
|
||||
import { SettingGroup } from '../../../models';
|
||||
import { ListsSettingGroups, SettingGroupRepositoryParams } from '../../../models/settings/setting-group';
|
||||
|
||||
function restList( httpClient: HTTPClient ): ListFn< SettingGroupRepositoryParams > {
|
||||
return async () => {
|
||||
const response = await httpClient.get( '/wc/v3/settings' );
|
||||
|
||||
const list: SettingGroup[] = [];
|
||||
for ( const raw of response.data ) {
|
||||
list.push( new SettingGroup( {
|
||||
id: raw.id,
|
||||
label: raw.label,
|
||||
description: raw.description,
|
||||
parentID: raw.parent_id,
|
||||
} ) );
|
||||
}
|
||||
|
||||
return Promise.resolve( list );
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ModelRepository instance for interacting with models via the REST API.
|
||||
*
|
||||
* @param {HTTPClient} httpClient The HTTP client for the REST requests to be made using.
|
||||
* @return {ListsSettingGroups} The created repository.
|
||||
*/
|
||||
export function settingGroupRESTRepository( httpClient: HTTPClient ): ListsSettingGroups {
|
||||
return new ModelRepository(
|
||||
restList( httpClient ),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
import { HTTPClient } from '../../../http';
|
||||
import {
|
||||
ListChildFn,
|
||||
ModelRepository,
|
||||
ReadChildFn,
|
||||
UpdateChildFn,
|
||||
} from '../../../framework/model-repository';
|
||||
import { Setting } from '../../../models';
|
||||
import {
|
||||
ListsSettings,
|
||||
ReadsSettings,
|
||||
SettingRepositoryParams,
|
||||
UpdatesSettings,
|
||||
} from '../../../models/settings/setting';
|
||||
|
||||
function restList( httpClient: HTTPClient ): ListChildFn< SettingRepositoryParams > {
|
||||
return async ( parent ) => {
|
||||
const response = await httpClient.get( '/wc/v3/settings/' + parent );
|
||||
|
||||
const list: Setting[] = [];
|
||||
for ( const raw of response.data ) {
|
||||
list.push( new Setting( {
|
||||
id: raw.id,
|
||||
label: raw.label,
|
||||
description: raw.description,
|
||||
type: raw.type,
|
||||
options: raw.options,
|
||||
default: raw.default,
|
||||
value: raw.value,
|
||||
} ) );
|
||||
}
|
||||
|
||||
return Promise.resolve( list );
|
||||
};
|
||||
}
|
||||
|
||||
function restRead( httpClient: HTTPClient ): ReadChildFn< SettingRepositoryParams > {
|
||||
return async ( parent, id ) => {
|
||||
const response = await httpClient.get( '/wc/v3/settings/' + parent + '/' + id );
|
||||
|
||||
return Promise.resolve( new Setting( {
|
||||
id: response.data.id,
|
||||
label: response.data.label,
|
||||
description: response.data.description,
|
||||
type: response.data.type,
|
||||
options: response.data.options,
|
||||
default: response.data.default,
|
||||
value: response.data.value,
|
||||
} ) );
|
||||
};
|
||||
}
|
||||
|
||||
function restUpdate( httpClient: HTTPClient ): UpdateChildFn< SettingRepositoryParams > {
|
||||
return async ( parent, id, params ) => {
|
||||
const response = await httpClient.patch(
|
||||
'/wc/v3/settings/' + parent + '/' + id,
|
||||
params,
|
||||
);
|
||||
|
||||
return Promise.resolve( new Setting( {
|
||||
id: response.data.id,
|
||||
label: response.data.label,
|
||||
description: response.data.description,
|
||||
type: response.data.type,
|
||||
options: response.data.options,
|
||||
default: response.data.default,
|
||||
value: response.data.value,
|
||||
} ) );
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ModelRepository instance for interacting with models via the REST API.
|
||||
*
|
||||
* @param {HTTPClient} httpClient The HTTP client for the REST requests to be made using.
|
||||
* @return {ListsSettings|ReadsSettings|UpdatesSettings} The created repository.
|
||||
*/
|
||||
export function settingRESTRepository( httpClient: HTTPClient ): ListsSettings & ReadsSettings & UpdatesSettings {
|
||||
return new ModelRepository(
|
||||
restList( httpClient ),
|
||||
null,
|
||||
restRead( httpClient ),
|
||||
restUpdate( httpClient ),
|
||||
null,
|
||||
);
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
import { mock, MockProxy } from 'jest-mock-extended';
|
||||
import { UpdatesSettings } from '../../models/settings/setting';
|
||||
import { SettingService } from '../setting-service';
|
||||
|
||||
describe( 'SettingService', () => {
|
||||
let repository: MockProxy< UpdatesSettings >;
|
||||
let service: SettingService;
|
||||
|
||||
beforeEach( () => {
|
||||
repository = mock< UpdatesSettings >();
|
||||
service = new SettingService( repository );
|
||||
} );
|
||||
|
||||
it( 'should update address', async () => {
|
||||
const result = await service.updateStoreAddress(
|
||||
'line1',
|
||||
'line2',
|
||||
'New York',
|
||||
'US:NY',
|
||||
'12345',
|
||||
);
|
||||
|
||||
expect( result ).toBeTruthy();
|
||||
expect( repository.update ).toHaveBeenCalledTimes( 5 );
|
||||
expect( repository.update ).toHaveBeenCalledWith( 'general', 'woocommerce_store_address', { value: 'line1' } );
|
||||
expect( repository.update ).toHaveBeenCalledWith( 'general', 'woocommerce_store_address_2', { value: 'line2' } );
|
||||
expect( repository.update ).toHaveBeenCalledWith( 'general', 'woocommerce_store_city', { value: 'New York' } );
|
||||
expect( repository.update ).toHaveBeenCalledWith( 'general', 'woocommerce_default_country', { value: 'US:NY' } );
|
||||
expect( repository.update ).toHaveBeenCalledWith( 'general', 'woocommerce_store_postcode', { value: '12345' } );
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1 @@
|
|||
export { SettingService } from './setting-service';
|
|
@ -0,0 +1,45 @@
|
|||
import { Setting, UpdatesSettings } from '../models/settings/setting';
|
||||
|
||||
/**
|
||||
* A service that wraps setting changes in convenient methods.
|
||||
*/
|
||||
export class SettingService {
|
||||
/**
|
||||
* The repository that will be used to change the settings.
|
||||
*
|
||||
* @type {UpdatesSettings}
|
||||
* @private
|
||||
*/
|
||||
private readonly repository: UpdatesSettings;
|
||||
|
||||
/**
|
||||
* Creates a new service class for easily changing store settings.
|
||||
*
|
||||
* @param {UpdatesSettings} repository The repository that will be used to change the settings.
|
||||
*/
|
||||
public constructor( repository: UpdatesSettings ) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the address for the store.
|
||||
*
|
||||
* @param {string} address1 The first address line.
|
||||
* @param {string} address2 The second address line.
|
||||
* @param {string} city The city.
|
||||
* @param {string} country The country or country/state.
|
||||
* @param {string} postCode The postal code.
|
||||
* @return {Promise.<boolean>} Resolves to true if all of the settings are updated.
|
||||
*/
|
||||
public updateStoreAddress( address1: string, address2: string, city: string, country: string, postCode: string ): Promise< boolean > {
|
||||
const promises: Promise< Setting >[] = [];
|
||||
|
||||
promises.push( this.repository.update( 'general', 'woocommerce_store_address', { value: address1 } ) );
|
||||
promises.push( this.repository.update( 'general', 'woocommerce_store_address_2', { value: address2 } ) );
|
||||
promises.push( this.repository.update( 'general', 'woocommerce_store_city', { value: city } ) );
|
||||
promises.push( this.repository.update( 'general', 'woocommerce_default_country', { value: country } ) );
|
||||
promises.push( this.repository.update( 'general', 'woocommerce_store_postcode', { value: postCode } ) );
|
||||
|
||||
return Promise.all( promises ).then( () => true );
|
||||
}
|
||||
}
|
|
@ -2601,9 +2601,9 @@
|
|||
}
|
||||
},
|
||||
"@types/babel__generator": {
|
||||
"version": "7.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.1.tgz",
|
||||
"integrity": "sha512-bBKm+2VPJcMRVwNhxKu8W+5/zT7pwNEqeokFOmbvVSqGzFneNxYcEBro9Ac7/N9tlsaPYnZLK8J1LWKkMsLAew==",
|
||||
"version": "7.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz",
|
||||
"integrity": "sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==",
|
||||
"requires": {
|
||||
"@babel/types": "^7.0.0"
|
||||
}
|
||||
|
@ -2618,13 +2618,21 @@
|
|||
}
|
||||
},
|
||||
"@types/babel__traverse": {
|
||||
"version": "7.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.14.tgz",
|
||||
"integrity": "sha512-8w9szzKs14ZtBVuP6Wn7nMLRJ0D6dfB0VEBEyRgxrZ/Ln49aNMykrghM2FaNn4FJRzNppCSa0Rv9pBRM5Xc3wg==",
|
||||
"version": "7.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.15.tgz",
|
||||
"integrity": "sha512-Pzh9O3sTK8V6I1olsXpCfj2k/ygO2q1X0vhhnDrEQyYLHZesWz+zMZMVcwXLCYf0U36EtmyYaFGPfXlTtDHe3A==",
|
||||
"requires": {
|
||||
"@babel/types": "^7.3.0"
|
||||
}
|
||||
},
|
||||
"@types/cheerio": {
|
||||
"version": "0.22.22",
|
||||
"resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.22.tgz",
|
||||
"integrity": "sha512-05DYX4zU96IBfZFY+t3Mh88nlwSMtmmzSYaQkKN48T495VV1dkHSah6qYyDTN5ngaS0i0VonH37m+RuzSM0YiA==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/color-name": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
||||
|
@ -3760,20 +3768,20 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"es-abstract": {
|
||||
"version": "1.17.6",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
|
||||
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
|
||||
"version": "1.17.7",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
|
||||
"integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"es-to-primitive": "^1.2.1",
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.1",
|
||||
"is-callable": "^1.2.0",
|
||||
"is-regex": "^1.1.0",
|
||||
"object-inspect": "^1.7.0",
|
||||
"is-callable": "^1.2.2",
|
||||
"is-regex": "^1.1.1",
|
||||
"object-inspect": "^1.8.0",
|
||||
"object-keys": "^1.1.1",
|
||||
"object.assign": "^4.1.0",
|
||||
"object.assign": "^4.1.1",
|
||||
"string.prototype.trimend": "^1.0.1",
|
||||
"string.prototype.trimstart": "^1.0.1"
|
||||
}
|
||||
|
@ -3795,19 +3803,19 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"es-abstract": {
|
||||
"version": "1.17.6",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
|
||||
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
|
||||
"version": "1.17.7",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
|
||||
"integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
|
||||
"requires": {
|
||||
"es-to-primitive": "^1.2.1",
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.1",
|
||||
"is-callable": "^1.2.0",
|
||||
"is-regex": "^1.1.0",
|
||||
"object-inspect": "^1.7.0",
|
||||
"is-callable": "^1.2.2",
|
||||
"is-regex": "^1.1.1",
|
||||
"object-inspect": "^1.8.0",
|
||||
"object-keys": "^1.1.1",
|
||||
"object.assign": "^4.1.0",
|
||||
"object.assign": "^4.1.1",
|
||||
"string.prototype.trimend": "^1.0.1",
|
||||
"string.prototype.trimstart": "^1.0.1"
|
||||
}
|
||||
|
@ -3824,19 +3832,19 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"es-abstract": {
|
||||
"version": "1.17.6",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
|
||||
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
|
||||
"version": "1.17.7",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
|
||||
"integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
|
||||
"requires": {
|
||||
"es-to-primitive": "^1.2.1",
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.1",
|
||||
"is-callable": "^1.2.0",
|
||||
"is-regex": "^1.1.0",
|
||||
"object-inspect": "^1.7.0",
|
||||
"is-callable": "^1.2.2",
|
||||
"is-regex": "^1.1.1",
|
||||
"object-inspect": "^1.8.0",
|
||||
"object-keys": "^1.1.1",
|
||||
"object.assign": "^4.1.0",
|
||||
"object.assign": "^4.1.1",
|
||||
"string.prototype.trimend": "^1.0.1",
|
||||
"string.prototype.trimstart": "^1.0.1"
|
||||
}
|
||||
|
@ -3855,20 +3863,20 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"es-abstract": {
|
||||
"version": "1.17.6",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
|
||||
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
|
||||
"version": "1.17.7",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
|
||||
"integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"es-to-primitive": "^1.2.1",
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.1",
|
||||
"is-callable": "^1.2.0",
|
||||
"is-regex": "^1.1.0",
|
||||
"object-inspect": "^1.7.0",
|
||||
"is-callable": "^1.2.2",
|
||||
"is-regex": "^1.1.1",
|
||||
"object-inspect": "^1.8.0",
|
||||
"object-keys": "^1.1.1",
|
||||
"object.assign": "^4.1.0",
|
||||
"object.assign": "^4.1.1",
|
||||
"string.prototype.trimend": "^1.0.1",
|
||||
"string.prototype.trimstart": "^1.0.1"
|
||||
}
|
||||
|
@ -4322,9 +4330,9 @@
|
|||
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001137",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001137.tgz",
|
||||
"integrity": "sha512-54xKQZTqZrKVHmVz0+UvdZR6kQc7pJDgfhsMYDG19ID1BWoNnDMFm5Q3uSBSU401pBvKYMsHAt9qhEDcxmk8aw=="
|
||||
"version": "1.0.30001141",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001141.tgz",
|
||||
"integrity": "sha512-EHfInJHoQTmlMdVZrEc5gmwPc0zyN/hVufmGHPbVNQwlk7tJfCmQ2ysRZMY2MeleBivALUTyyxXnQjK18XrVpA=="
|
||||
},
|
||||
"capture-exit": {
|
||||
"version": "2.0.0",
|
||||
|
@ -4976,9 +4984,9 @@
|
|||
}
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.572",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.572.tgz",
|
||||
"integrity": "sha512-TKqdEukCCl7JC20SwEoWTbtnGt4YjfHWAv4tcNky0a9qGo0WdM+Lrd60tps+nkaJCmktKBJjr99fLtEBU1ipWQ=="
|
||||
"version": "1.3.576",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.576.tgz",
|
||||
"integrity": "sha512-uSEI0XZ//5ic+0NdOqlxp0liCD44ck20OAGyLMSymIWTEAtHKVJi6JM18acOnRgUgX7Q65QqnI+sNncNvIy8ew=="
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
|
@ -5088,10 +5096,11 @@
|
|||
}
|
||||
},
|
||||
"enzyme-to-json": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/enzyme-to-json/-/enzyme-to-json-3.5.0.tgz",
|
||||
"integrity": "sha512-clusXRsiaQhG7+wtyc4t7MU8N3zCOgf4eY9+CeSenYzKlFST4lxerfOvnWd4SNaToKhkuba+w6m242YpQOS7eA==",
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/enzyme-to-json/-/enzyme-to-json-3.6.1.tgz",
|
||||
"integrity": "sha512-15tXuONeq5ORoZjV/bUo2gbtZrN2IH+Z6DvL35QmZyKHgbY1ahn6wcnLd9Xv9OjiwbAXiiP8MRZwbZrCv1wYNg==",
|
||||
"requires": {
|
||||
"@types/cheerio": "^0.22.22",
|
||||
"lodash": "^4.17.15",
|
||||
"react-is": "^16.12.0"
|
||||
}
|
||||
|
@ -5105,20 +5114,20 @@
|
|||
}
|
||||
},
|
||||
"es-abstract": {
|
||||
"version": "1.18.0-next.0",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.0.tgz",
|
||||
"integrity": "sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ==",
|
||||
"version": "1.18.0-next.1",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
|
||||
"integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
|
||||
"requires": {
|
||||
"es-to-primitive": "^1.2.1",
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.1",
|
||||
"is-callable": "^1.2.0",
|
||||
"is-callable": "^1.2.2",
|
||||
"is-negative-zero": "^2.0.0",
|
||||
"is-regex": "^1.1.1",
|
||||
"object-inspect": "^1.8.0",
|
||||
"object-keys": "^1.1.1",
|
||||
"object.assign": "^4.1.0",
|
||||
"object.assign": "^4.1.1",
|
||||
"string.prototype.trimend": "^1.0.1",
|
||||
"string.prototype.trimstart": "^1.0.1"
|
||||
}
|
||||
|
@ -5894,19 +5903,19 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"es-abstract": {
|
||||
"version": "1.17.6",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
|
||||
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
|
||||
"version": "1.17.7",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
|
||||
"integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
|
||||
"requires": {
|
||||
"es-to-primitive": "^1.2.1",
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.1",
|
||||
"is-callable": "^1.2.0",
|
||||
"is-regex": "^1.1.0",
|
||||
"object-inspect": "^1.7.0",
|
||||
"is-callable": "^1.2.2",
|
||||
"is-regex": "^1.1.1",
|
||||
"object-inspect": "^1.8.0",
|
||||
"object-keys": "^1.1.1",
|
||||
"object.assign": "^4.1.0",
|
||||
"object.assign": "^4.1.1",
|
||||
"string.prototype.trimend": "^1.0.1",
|
||||
"string.prototype.trimstart": "^1.0.1"
|
||||
}
|
||||
|
@ -6404,20 +6413,20 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"es-abstract": {
|
||||
"version": "1.17.6",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
|
||||
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
|
||||
"version": "1.17.7",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
|
||||
"integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"es-to-primitive": "^1.2.1",
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.1",
|
||||
"is-callable": "^1.2.0",
|
||||
"is-regex": "^1.1.0",
|
||||
"object-inspect": "^1.7.0",
|
||||
"is-callable": "^1.2.2",
|
||||
"is-regex": "^1.1.1",
|
||||
"object-inspect": "^1.8.0",
|
||||
"object-keys": "^1.1.1",
|
||||
"object.assign": "^4.1.0",
|
||||
"object.assign": "^4.1.1",
|
||||
"string.prototype.trimend": "^1.0.1",
|
||||
"string.prototype.trimstart": "^1.0.1"
|
||||
}
|
||||
|
@ -9391,32 +9400,12 @@
|
|||
"integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA=="
|
||||
},
|
||||
"object-is": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz",
|
||||
"integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==",
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.3.tgz",
|
||||
"integrity": "sha512-teyqLvFWzLkq5B9ki8FVWA902UER2qkxmdA4nLf+wjOLAWgxzCWZNCxpDq9MvE8MmhWNr+I8w3BN49Vx36Y6Xg==",
|
||||
"requires": {
|
||||
"define-properties": "^1.1.3",
|
||||
"es-abstract": "^1.17.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"es-abstract": {
|
||||
"version": "1.17.6",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
|
||||
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
|
||||
"requires": {
|
||||
"es-to-primitive": "^1.2.1",
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.1",
|
||||
"is-callable": "^1.2.0",
|
||||
"is-regex": "^1.1.0",
|
||||
"object-inspect": "^1.7.0",
|
||||
"object-keys": "^1.1.1",
|
||||
"object.assign": "^4.1.0",
|
||||
"string.prototype.trimend": "^1.0.1",
|
||||
"string.prototype.trimstart": "^1.0.1"
|
||||
}
|
||||
}
|
||||
"es-abstract": "^1.18.0-next.1"
|
||||
}
|
||||
},
|
||||
"object-keys": {
|
||||
|
@ -9454,19 +9443,19 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"es-abstract": {
|
||||
"version": "1.17.6",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
|
||||
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
|
||||
"version": "1.17.7",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
|
||||
"integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
|
||||
"requires": {
|
||||
"es-to-primitive": "^1.2.1",
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.1",
|
||||
"is-callable": "^1.2.0",
|
||||
"is-regex": "^1.1.0",
|
||||
"object-inspect": "^1.7.0",
|
||||
"is-callable": "^1.2.2",
|
||||
"is-regex": "^1.1.1",
|
||||
"object-inspect": "^1.8.0",
|
||||
"object-keys": "^1.1.1",
|
||||
"object.assign": "^4.1.0",
|
||||
"object.assign": "^4.1.1",
|
||||
"string.prototype.trimend": "^1.0.1",
|
||||
"string.prototype.trimstart": "^1.0.1"
|
||||
}
|
||||
|
@ -9491,19 +9480,19 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"es-abstract": {
|
||||
"version": "1.17.6",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
|
||||
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
|
||||
"version": "1.17.7",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
|
||||
"integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
|
||||
"requires": {
|
||||
"es-to-primitive": "^1.2.1",
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.1",
|
||||
"is-callable": "^1.2.0",
|
||||
"is-regex": "^1.1.0",
|
||||
"object-inspect": "^1.7.0",
|
||||
"is-callable": "^1.2.2",
|
||||
"is-regex": "^1.1.1",
|
||||
"object-inspect": "^1.8.0",
|
||||
"object-keys": "^1.1.1",
|
||||
"object.assign": "^4.1.0",
|
||||
"object.assign": "^4.1.1",
|
||||
"string.prototype.trimend": "^1.0.1",
|
||||
"string.prototype.trimstart": "^1.0.1"
|
||||
}
|
||||
|
@ -9520,19 +9509,19 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"es-abstract": {
|
||||
"version": "1.17.6",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
|
||||
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
|
||||
"version": "1.17.7",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
|
||||
"integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
|
||||
"requires": {
|
||||
"es-to-primitive": "^1.2.1",
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.1",
|
||||
"is-callable": "^1.2.0",
|
||||
"is-regex": "^1.1.0",
|
||||
"object-inspect": "^1.7.0",
|
||||
"is-callable": "^1.2.2",
|
||||
"is-regex": "^1.1.1",
|
||||
"object-inspect": "^1.8.0",
|
||||
"object-keys": "^1.1.1",
|
||||
"object.assign": "^4.1.0",
|
||||
"object.assign": "^4.1.1",
|
||||
"string.prototype.trimend": "^1.0.1",
|
||||
"string.prototype.trimstart": "^1.0.1"
|
||||
}
|
||||
|
@ -9559,19 +9548,19 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"es-abstract": {
|
||||
"version": "1.17.6",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
|
||||
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
|
||||
"version": "1.17.7",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
|
||||
"integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
|
||||
"requires": {
|
||||
"es-to-primitive": "^1.2.1",
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.1",
|
||||
"is-callable": "^1.2.0",
|
||||
"is-regex": "^1.1.0",
|
||||
"object-inspect": "^1.7.0",
|
||||
"is-callable": "^1.2.2",
|
||||
"is-regex": "^1.1.1",
|
||||
"object-inspect": "^1.8.0",
|
||||
"object-keys": "^1.1.1",
|
||||
"object.assign": "^4.1.0",
|
||||
"object.assign": "^4.1.1",
|
||||
"string.prototype.trimend": "^1.0.1",
|
||||
"string.prototype.trimstart": "^1.0.1"
|
||||
}
|
||||
|
@ -10217,20 +10206,20 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"es-abstract": {
|
||||
"version": "1.17.6",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
|
||||
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
|
||||
"version": "1.17.7",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
|
||||
"integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"es-to-primitive": "^1.2.1",
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.1",
|
||||
"is-callable": "^1.2.0",
|
||||
"is-regex": "^1.1.0",
|
||||
"object-inspect": "^1.7.0",
|
||||
"is-callable": "^1.2.2",
|
||||
"is-regex": "^1.1.1",
|
||||
"object-inspect": "^1.8.0",
|
||||
"object-keys": "^1.1.1",
|
||||
"object.assign": "^4.1.0",
|
||||
"object.assign": "^4.1.1",
|
||||
"string.prototype.trimend": "^1.0.1",
|
||||
"string.prototype.trimstart": "^1.0.1"
|
||||
}
|
||||
|
@ -10976,20 +10965,20 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"es-abstract": {
|
||||
"version": "1.17.6",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
|
||||
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
|
||||
"version": "1.17.7",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
|
||||
"integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"es-to-primitive": "^1.2.1",
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.1",
|
||||
"is-callable": "^1.2.0",
|
||||
"is-regex": "^1.1.0",
|
||||
"object-inspect": "^1.7.0",
|
||||
"is-callable": "^1.2.2",
|
||||
"is-regex": "^1.1.1",
|
||||
"object-inspect": "^1.8.0",
|
||||
"object-keys": "^1.1.1",
|
||||
"object.assign": "^4.1.0",
|
||||
"object.assign": "^4.1.1",
|
||||
"string.prototype.trimend": "^1.0.1",
|
||||
"string.prototype.trimstart": "^1.0.1"
|
||||
}
|
||||
|
@ -11015,19 +11004,19 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"es-abstract": {
|
||||
"version": "1.17.6",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
|
||||
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
|
||||
"version": "1.17.7",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
|
||||
"integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
|
||||
"requires": {
|
||||
"es-to-primitive": "^1.2.1",
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.1",
|
||||
"is-callable": "^1.2.0",
|
||||
"is-regex": "^1.1.0",
|
||||
"object-inspect": "^1.7.0",
|
||||
"is-callable": "^1.2.2",
|
||||
"is-regex": "^1.1.1",
|
||||
"object-inspect": "^1.8.0",
|
||||
"object-keys": "^1.1.1",
|
||||
"object.assign": "^4.1.0",
|
||||
"object.assign": "^4.1.1",
|
||||
"string.prototype.trimend": "^1.0.1",
|
||||
"string.prototype.trimstart": "^1.0.1"
|
||||
}
|
||||
|
@ -11044,19 +11033,19 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"es-abstract": {
|
||||
"version": "1.17.6",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
|
||||
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
|
||||
"version": "1.17.7",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
|
||||
"integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
|
||||
"requires": {
|
||||
"es-to-primitive": "^1.2.1",
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.1",
|
||||
"is-callable": "^1.2.0",
|
||||
"is-regex": "^1.1.0",
|
||||
"object-inspect": "^1.7.0",
|
||||
"is-callable": "^1.2.2",
|
||||
"is-regex": "^1.1.1",
|
||||
"object-inspect": "^1.8.0",
|
||||
"object-keys": "^1.1.1",
|
||||
"object.assign": "^4.1.0",
|
||||
"object.assign": "^4.1.1",
|
||||
"string.prototype.trimend": "^1.0.1",
|
||||
"string.prototype.trimstart": "^1.0.1"
|
||||
}
|
||||
|
@ -11566,19 +11555,19 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"es-abstract": {
|
||||
"version": "1.17.6",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
|
||||
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
|
||||
"version": "1.17.7",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
|
||||
"integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
|
||||
"requires": {
|
||||
"es-to-primitive": "^1.2.1",
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.1",
|
||||
"is-callable": "^1.2.0",
|
||||
"is-regex": "^1.1.0",
|
||||
"object-inspect": "^1.7.0",
|
||||
"is-callable": "^1.2.2",
|
||||
"is-regex": "^1.1.1",
|
||||
"object-inspect": "^1.8.0",
|
||||
"object-keys": "^1.1.1",
|
||||
"object.assign": "^4.1.0",
|
||||
"object.assign": "^4.1.1",
|
||||
"string.prototype.trimend": "^1.0.1",
|
||||
"string.prototype.trimstart": "^1.0.1"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue