Add create product actions in products data store (#33278)

* Add create product actions in products data store

* Add changelog entry

* Yield to actions in generator

* Add mutable properties type
This commit is contained in:
Joshua T Flowers 2022-06-07 09:23:27 -04:00 committed by GitHub
parent 1bca9327ac
commit 17d9e8072d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 115 additions and 2 deletions

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Add create product actions in products data store #33278

View File

@ -155,3 +155,4 @@ export interface WCDataSelector {
// Other exports // Other exports
export { ActionDispatchers as PluginsStoreActions } from './plugins/actions'; export { ActionDispatchers as PluginsStoreActions } from './plugins/actions';
export { ActionDispatchers as ProductsStoreActions } from './products/actions';

View File

@ -1,4 +1,6 @@
export enum TYPES { export enum TYPES {
CREATE_PRODUCT_ERROR = 'CREATE_PRODUCT_ERROR',
CREATE_PRODUCT_SUCCESS = 'CREATE_PRODUCT_SUCCESS',
GET_PRODUCT_SUCCESS = 'GET_PRODUCT_SUCCESS', GET_PRODUCT_SUCCESS = 'GET_PRODUCT_SUCCESS',
GET_PRODUCT_ERROR = 'GET_PRODUCT_ERROR', GET_PRODUCT_ERROR = 'GET_PRODUCT_ERROR',
GET_PRODUCTS_SUCCESS = 'GET_PRODUCTS_SUCCESS', GET_PRODUCTS_SUCCESS = 'GET_PRODUCTS_SUCCESS',

View File

@ -1,8 +1,20 @@
/**
* External dependencies
*/
import { apiFetch } from '@wordpress/data-controls';
import { DispatchFromMap } from '@automattic/data-stores';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import TYPES from './action-types'; import TYPES from './action-types';
import { PartialProduct, ProductQuery } from './types'; import {
MutableProperties,
PartialProduct,
Product,
ProductQuery,
} from './types';
import { WC_PRODUCT_NAMESPACE } from './constants';
export function getProductSuccess( id: number, product: PartialProduct ) { export function getProductSuccess( id: number, product: PartialProduct ) {
return { return {
@ -23,6 +35,25 @@ export function getProductError(
}; };
} }
function createProductSuccess( id: number, product: PartialProduct ) {
return {
type: TYPES.CREATE_PRODUCT_SUCCESS as const,
id,
product,
};
}
export function createProductError(
query: Partial< Product >,
error: unknown
) {
return {
type: TYPES.CREATE_PRODUCT_ERROR as const,
query,
error,
};
}
export function getProductsSuccess( export function getProductsSuccess(
query: Partial< ProductQuery >, query: Partial< ProductQuery >,
products: PartialProduct[], products: PartialProduct[],
@ -69,7 +100,25 @@ export function getProductsTotalCountError(
}; };
} }
export function* createProduct( data: Pick< Product, MutableProperties > ) {
try {
const product: Product = yield apiFetch( {
path: WC_PRODUCT_NAMESPACE,
method: 'POST',
data,
} );
yield createProductSuccess( product.id, product );
return product;
} catch ( error ) {
yield createProductError( data, error );
throw error;
}
}
export type Actions = ReturnType< export type Actions = ReturnType<
| typeof createProductError
| typeof createProductSuccess
| typeof getProductSuccess | typeof getProductSuccess
| typeof getProductError | typeof getProductError
| typeof getProductsSuccess | typeof getProductsSuccess
@ -77,3 +126,7 @@ export type Actions = ReturnType<
| typeof getProductsTotalCountSuccess | typeof getProductsTotalCountSuccess
| typeof getProductsTotalCountError | typeof getProductsTotalCountError
>; >;
export type ActionDispatchers = DispatchFromMap< {
createProduct: typeof createProduct;
} >;

View File

@ -37,6 +37,7 @@ const reducer: Reducer< ProductState, Actions > = (
) => { ) => {
if ( payload && 'type' in payload ) { if ( payload && 'type' in payload ) {
switch ( payload.type ) { switch ( payload.type ) {
case TYPES.CREATE_PRODUCT_SUCCESS:
case TYPES.GET_PRODUCT_SUCCESS: case TYPES.GET_PRODUCT_SUCCESS:
const productData = state.data || {}; const productData = state.data || {};
return { return {
@ -88,6 +89,7 @@ const reducer: Reducer< ProductState, Actions > = (
case TYPES.GET_PRODUCT_ERROR: case TYPES.GET_PRODUCT_ERROR:
case TYPES.GET_PRODUCTS_ERROR: case TYPES.GET_PRODUCTS_ERROR:
case TYPES.GET_PRODUCTS_TOTAL_COUNT_ERROR: case TYPES.GET_PRODUCTS_TOTAL_COUNT_ERROR:
case TYPES.CREATE_PRODUCT_ERROR:
return { return {
...state, ...state,
errors: { errors: {

View File

@ -77,7 +77,16 @@ export const getProductsError = (
return state.errors[ resourceName ]; return state.errors[ resourceName ];
}; };
export const getCreateProductError = (
state: ProductState,
query: ProductQuery
) => {
const resourceName = getProductResourceName( query );
return state.errors[ resourceName ];
};
export type ProductsSelectors = { export type ProductsSelectors = {
getCreateProductError: WPDataSelector< typeof getCreateProductError >;
getProducts: WPDataSelector< typeof getProducts >; getProducts: WPDataSelector< typeof getProducts >;
getProductsTotalCount: WPDataSelector< typeof getProductsTotalCount >; getProductsTotalCount: WPDataSelector< typeof getProductsTotalCount >;
getProductsError: WPDataSelector< typeof getProductsError >; getProductsError: WPDataSelector< typeof getProductsError >;

View File

@ -8,7 +8,7 @@ import {
getTotalProductCountResourceName, getTotalProductCountResourceName,
} from '../utils'; } from '../utils';
import { Actions } from '../actions'; import { Actions } from '../actions';
import { PartialProduct, ProductQuery } from '../types'; import { PartialProduct, ProductQuery, Product } from '../types';
const defaultState: ProductState = { const defaultState: ProductState = {
products: {}, products: {},
@ -180,4 +180,34 @@ describe( 'products reducer', () => {
expect( state.errors[ resourceName ] ).toBe( error ); expect( state.errors[ resourceName ] ).toBe( error );
} ); } );
it( 'should handle CREATE_PRODUCT_SUCCESS', () => {
const update: PartialProduct = {
id: 2,
name: 'Off the hook!',
status: 'draft',
};
const state = reducer( defaultState, {
type: TYPES.CREATE_PRODUCT_SUCCESS,
id: update.id,
product: update,
} );
expect( state.data[ 2 ].name ).toEqual( update.name );
expect( state.data[ 2 ].status ).toEqual( update.status );
} );
it( 'should handle CREATE_PRODUCT_ERROR', () => {
const query: Partial< Product > = { name: 'Donkey Sauce' };
const resourceName = getProductResourceName( query );
const error = 'Baaam!';
const state = reducer( defaultState, {
type: TYPES.GET_PRODUCTS_ERROR,
query,
error,
} );
expect( state.errors[ resourceName ] ).toBe( error );
} );
} ); } );

View File

@ -40,6 +40,18 @@ export type Product<
sale_price: string; sale_price: string;
}; };
export type MutableProperties =
| 'name'
| 'slug'
| 'type'
| 'status'
| 'featured'
| 'description'
| 'short_description'
| 'sku'
| 'regular_price'
| 'sale_price';
export type PartialProduct = Partial< Product > & Pick< Product, 'id' >; export type PartialProduct = Partial< Product > & Pick< Product, 'id' >;
export type ProductQuery< export type ProductQuery<