/**
* @module resource-handler-spec
*/
'use strict';
var createPathPartSetter = require( './path-part-setter' ).create;
/** @private */
function addLevelOption( levelsObj, level, obj ) {
levelsObj[ level ] = levelsObj[ level ] || [];
levelsObj[ level ].push( obj );
}
/**
* Assign a setter function for the provided node to the provided route
* handler object setters dictionary (mutates handler by reference).
*
* @private
* @param {Object} handler A route handler definition object
* @param {Object} node A route hierarchy level node object
*/
function assignSetterFnForNode( handler, node ) {
var setterFn;
// For each node, add its handler to the relevant "level" representation
addLevelOption( handler._levels, node.level, {
component: node.component,
validate: node.validate,
methods: node.methods
});
// First level is set implicitly, no dedicated setter needed
if ( node.level > 0 ) {
setterFn = createPathPartSetter( node );
node.names.forEach(function( name ) {
// Convert from snake_case to camelCase
var setterFnName = name
.replace( /[_-]+\w/g, function( match ) {
return match.replace( /[_-]+/, '' ).toUpperCase();
});
// Don't overwrite previously-set methods
if ( ! handler._setters[ setterFnName ] ) {
handler._setters[ setterFnName ] = setterFn;
}
});
}
}
/**
* Walk the tree of a specific resource node to create the setter methods
*
* The API we want to produce from the node tree looks like this:
*
* wp.posts(); /wp/v2/posts
* wp.posts().id( 7 ); /wp/v2/posts/7
* wp.posts().id( 7 ).revisions(); /wp/v2/posts/7/revisions
* wp.posts().id( 7 ).revisions( 8 ); /wp/v2/posts/7/revisions/8
*
* ^ That last one's the tricky one: we can deduce that this parameter is "id", but
* that param will already be taken by the post ID, so sub-collections have to be
* set up as `.revisions()` to get the collection, and `.revisions( id )` to get a
* specific resource.
*
* @private
* @param {Object} node A node object
* @param {Object} [node.children] An object of child nodes
* // @returns {isLeaf} A boolean indicating whether the processed node is a leaf
*/
function extractSetterFromNode( handler, node ) {
assignSetterFnForNode( handler, node );
if ( node.children ) {
// Recurse down to this node's children
Object.keys( node.children ).forEach(function( key ) {
extractSetterFromNode( handler, node.children[ key ] );
});
}
}
/**
* Create a node handler specification object from a route definition object
*
* @name create
* @param {object} routeDefinition A route definition object
* @param {string} resource The string key of the resource for which to create a handler
* @returns {object} A handler spec object with _path, _levels and _setters properties
*/
function createNodeHandlerSpec( routeDefinition, resource ) {
var handler = {
// A "path" is an ordered (by key) set of values composed into the final URL
_path: {
'0': resource
},
// A "level" is a level-keyed object representing the valid options for
// one level of the resource URL
_levels: {},
// Objects that hold methods and properties which will be copied to
// instances of this endpoint's handler
_setters: {},
// Arguments (query parameters) that may be set in GET requests to endpoints
// nested within this resource route tree, used to determine the mixins to
// add to the request handler
_getArgs: routeDefinition._getArgs
};
// Walk the tree
Object.keys( routeDefinition ).forEach(function( routeDefProp ) {
if ( routeDefProp !== '_getArgs' ) {
extractSetterFromNode( handler, routeDefinition[ routeDefProp ] );
}
});
return handler;
}
module.exports = {
create: createNodeHandlerSpec
};