This repository has been archived by the owner on Jul 12, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 145
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
data: Add fresh-data and replace orders in table
This PR adds fresh-data with a WooCommerce API spec to fulfill order information. It then replaces the existing selectors for the orders table with the new selectors as a proof-of-concept.
- Loading branch information
1 parent
cf0491e
commit d6aefc5
Showing
13 changed files
with
347 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/** @format */ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { SECOND, MINUTE } from '@fresh-data/framework'; | ||
|
||
export const NAMESPACE = '/wc/v3'; | ||
|
||
export const DEFAULT_REQUIREMENT = { | ||
timeout: 5 * SECOND, | ||
freshness: 5 * MINUTE, | ||
}; | ||
|
||
// WordPress & WooCommerce both set a hard limit of 100 for the per_page parameter | ||
export const MAX_PER_PAGE = 100; | ||
|
||
export const QUERY_DEFAULTS = { | ||
pageSize: 25, | ||
period: 'month', | ||
compare: 'previous_year', | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/** @format */ | ||
/** | ||
* Internal dependencies | ||
*/ | ||
import operations from './operations'; | ||
import selectors from './selectors'; | ||
|
||
export default { | ||
operations, | ||
selectors, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
/** @format */ | ||
/** | ||
* External dependencies | ||
*/ | ||
import apiFetch from '@wordpress/api-fetch'; | ||
|
||
/** | ||
* WooCommerce dependencies | ||
*/ | ||
import { stringifyQuery } from '@woocommerce/navigation'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { isResourcePrefix, getResourceIdentifier, getResourceName } from '../utils'; | ||
import { NAMESPACE } from '../constants'; | ||
|
||
function read( resourceNames, fetch = apiFetch ) { | ||
return [ ...readOrders( resourceNames, fetch ), ...readOrderQueries( resourceNames, fetch ) ]; | ||
} | ||
|
||
function readOrderQueries( resourceNames, fetch ) { | ||
const filteredNames = resourceNames.filter( name => isResourcePrefix( name, 'order-query' ) ); | ||
|
||
return filteredNames.map( resourceName => { | ||
const query = getResourceIdentifier( resourceName ); | ||
const url = `${ NAMESPACE }/orders${ stringifyQuery( query ) }`; | ||
|
||
return fetch( { path: url } ) | ||
.then( orders => { | ||
const ids = orders.map( order => order.id ); | ||
const orderResources = orders.reduce( ( resources, order ) => { | ||
resources[ getResourceName( 'order', order.id ) ] = { data: order }; | ||
return resources; | ||
}, {} ); | ||
|
||
return { | ||
[ resourceName ]: { data: ids }, | ||
...orderResources, | ||
}; | ||
} ) | ||
.catch( error => { | ||
return { [ resourceName ]: { error } }; | ||
} ); | ||
} ); | ||
} | ||
|
||
function readOrders( resourceNames, fetch ) { | ||
const filteredNames = resourceNames.filter( name => isResourcePrefix( name, 'order' ) ); | ||
return filteredNames.map( resourceName => readOrder( resourceName, fetch ) ); | ||
} | ||
|
||
function readOrder( resourceName, fetch ) { | ||
const id = getResourceIdentifier( resourceName ); | ||
const url = `${ NAMESPACE }/orders/${ id }`; | ||
|
||
return fetch( { path: url } ) | ||
.then( order => { | ||
return { [ resourceName ]: { data: order } }; | ||
} ) | ||
.catch( error => { | ||
return { [ resourceName ]: { error } }; | ||
} ); | ||
} | ||
|
||
export default { | ||
read, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/** @format */ | ||
/** | ||
* Internal dependencies | ||
*/ | ||
import { getResourceName } from '../utils'; | ||
import { DEFAULT_REQUIREMENT } from '../constants'; | ||
|
||
const getOrders = ( getResource, requireResource ) => ( | ||
query = {}, | ||
requirement = DEFAULT_REQUIREMENT | ||
) => { | ||
const resourceName = getResourceName( 'order-query', query ); | ||
const ids = requireResource( requirement, resourceName ).data || []; | ||
const orders = ids.map( id => getResource( getResourceName( 'order', id ) ).data || {} ); | ||
return orders; | ||
}; | ||
|
||
const isGetOrdersRequesting = getResource => ( query = {} ) => { | ||
const resourceName = getResourceName( 'order-query', query ); | ||
const { lastRequested, lastReceived } = getResource( resourceName ); | ||
return lastRequested && lastRequested > lastReceived; | ||
}; | ||
|
||
const isGetOrdersError = getResource => ( query = {} ) => { | ||
const resourceName = getResourceName( 'order-query', query ); | ||
return getResource( resourceName ).error; | ||
}; | ||
|
||
export default { | ||
getOrders, | ||
isGetOrdersRequesting, | ||
isGetOrdersError, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
/** @format */ | ||
|
||
export function getResourceName( prefix, identifier ) { | ||
const identifierString = JSON.stringify( identifier, Object.keys( identifier ).sort() ); | ||
return `${ prefix }:${ identifierString }`; | ||
} | ||
|
||
export function isResourcePrefix( resourceName, prefix ) { | ||
const resourcePrefix = resourceName.substring( 0, resourceName.indexOf( ':' ) ); | ||
return resourcePrefix === prefix; | ||
} | ||
|
||
export function getResourceIdentifier( resourceName ) { | ||
const identifierString = resourceName.substring( resourceName.indexOf( ':' ) + 1 ); | ||
return JSON.parse( identifierString ); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/** @format */ | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import orders from './orders'; | ||
|
||
function createWcApiSpec() { | ||
return { | ||
selectors: { | ||
...orders.selectors, | ||
}, | ||
operations: { | ||
read( resourceNames ) { | ||
return [ ...orders.operations.read( resourceNames ) ]; | ||
}, | ||
}, | ||
}; | ||
} | ||
|
||
export default createWcApiSpec(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/** @format */ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { ApiClient } from '@fresh-data/framework'; | ||
import { createStore as createReduxStore } from 'redux'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import reducer from './reducer'; | ||
|
||
function createStore( name ) { | ||
const devTools = window.__REDUX_DEVTOOLS_EXTENSION__; | ||
|
||
return createReduxStore( reducer, devTools && devTools( { name: name, instanceId: name } ) ); | ||
} | ||
|
||
function createDataHandlers( store ) { | ||
return { | ||
dataRequested: resourceNames => { | ||
store.dispatch( { | ||
type: 'FRESH_DATA_REQUESTED', | ||
resourceNames, | ||
time: new Date(), | ||
} ); | ||
}, | ||
dataReceived: resources => { | ||
store.dispatch( { | ||
type: 'FRESH_DATA_RECEIVED', | ||
resources, | ||
time: new Date(), | ||
} ); | ||
}, | ||
}; | ||
} | ||
|
||
function createApiClient( name, apiSpec ) { | ||
const store = createStore( name ); | ||
const dataHandlers = createDataHandlers( store ); | ||
const apiClient = new ApiClient( apiSpec ); | ||
apiClient.setDataHandlers( dataHandlers ); | ||
|
||
const storeChanged = () => { | ||
apiClient.setState( store.getState() ); | ||
}; | ||
store.subscribe( storeChanged ); | ||
|
||
return apiClient; | ||
} | ||
|
||
export default createApiClient; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
/** @format */ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { createStore } from 'redux'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import reducer from './reducer'; | ||
|
||
export default name => { | ||
const devTools = window.__REDUX_DEVTOOLS_EXTENSION__; | ||
|
||
return createStore( reducer, devTools && devTools( { name: name, instanceId: name } ) ); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/** @format */ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { registerGenericStore } from '@wordpress/data'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import createApiClient from './create-api-client'; | ||
import wcApiSpec from '../wc-api-spec'; | ||
|
||
function createWcApiStore() { | ||
const apiClient = createApiClient( 'wc-api', wcApiSpec ); | ||
|
||
function getComponentSelectors( component ) { | ||
const componentRequirements = []; | ||
const apiSelectors = apiClient.getSelectors( componentRequirements ); | ||
|
||
apiClient.clearComponentRequirements( component ); | ||
|
||
return Object.keys( apiSelectors ).reduce( ( componentSelectors, selectorName ) => { | ||
componentSelectors[ selectorName ] = ( ...args ) => { | ||
const result = apiSelectors[ selectorName ]( ...args ); | ||
apiClient.setComponentRequirements( component, componentRequirements ); | ||
return result; | ||
}; | ||
return componentSelectors; | ||
}, {} ); | ||
} | ||
|
||
return { | ||
getSelectors( context ) { | ||
const component = context && context.component ? context.component : context; | ||
return getComponentSelectors( component ); | ||
}, | ||
getActions() { | ||
// TODO: Add mutations here. | ||
return {}; | ||
}, | ||
subscribe: apiClient.subscribe, | ||
}; | ||
} | ||
|
||
registerGenericStore( 'wc-api', createWcApiStore() ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
/** @format */ | ||
|
||
const defaultState = { | ||
resources: {}, | ||
}; | ||
|
||
/** | ||
* Primary reducer for fresh-data apiclient data. | ||
* @param {Object} state The base state for fresh-data. | ||
* @param {Object} action Action object to be processed. | ||
* @return {Object} The new state, or the previous state if this action doesn't belong to fresh-data. | ||
*/ | ||
export default function reducer( state = defaultState, action ) { | ||
switch ( action.type ) { | ||
case 'FRESH_DATA_REQUESTED': | ||
return reduceRequested( state, action ); | ||
case 'FRESH_DATA_RECEIVED': | ||
return reduceReceived( state, action ); | ||
default: | ||
return state; | ||
} | ||
} | ||
|
||
export function reduceRequested( state, action ) { | ||
const newResources = action.resourceNames.reduce( ( resources, name ) => { | ||
resources[ name ] = { lastRequested: action.time }; | ||
return resources; | ||
}, {} ); | ||
return reduceResources( state, newResources ); | ||
} | ||
|
||
export function reduceReceived( state, action ) { | ||
const newResources = Object.keys( action.resources ).reduce( ( resources, name ) => { | ||
const resource = { ...action.resources[ name ] }; | ||
if ( resource.data ) { | ||
resource.lastReceived = action.time; | ||
} | ||
if ( resource.error ) { | ||
resource.lastError = action.time; | ||
} | ||
resources[ name ] = resource; | ||
return resources; | ||
}, {} ); | ||
return reduceResources( state, newResources ); | ||
} | ||
|
||
export function reduceResources( state, newResources ) { | ||
const updatedResources = Object.keys( newResources ).reduce( | ||
( resources, resourceName ) => { | ||
const resource = resources[ resourceName ]; | ||
const newResource = newResources[ resourceName ]; | ||
resources[ resourceName ] = { ...resource, ...newResource }; | ||
return resources; | ||
}, | ||
{ ...state.resources } | ||
); | ||
|
||
return { ...state, resources: updatedResources }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters