import { snakeCase, clone } from 'lodash'
import PathBuilder from './path-builder'
import { MissingRequiredParameterError } from './exceptions'
class RouteBuilder {
/**
* RailsRanger object constructor
* @constructor
*/
constructor () {
this.pathBuilder = new PathBuilder()
this.chainedPaths = []
}
/**
* Defines a namespace to be used in the next route of the chain
* @param {string} resource - the name of the resource to be used as namespace
* @param {integer} id - the ID of the resource, can be left empty
* @example
* const routes = new RouteBuilder
* routes.resource('users', 1).list('blogPosts')
* //=> { path: '/users/1/blog_posts', params: {} }
*/
resource (resource, id = null) {
const snakedResource = snakeCase(resource)
if (id) {
return this.namespace(`${snakedResource}/:id`, { id })
} else {
return this.namespace(snakedResource)
}
}
/**
* Defines a namespace to be used in the next route of the chain
* @param {string} namespace - The path fragment to be used as the namespace
* @param {object} params - The parameters to be interpolated into the path, can be left empty
* @example
* const routes = new RouteBuilder
* routes.namespace('admin').list('blogPosts')
* //=> { path: '/admin/blog_posts', params: {} }
*/
namespace (namespace, params = {}) {
const newInstance = clone(this)
// Duplicates the chainedPaths as a new object
newInstance.chainedPaths = clone(this.chainedPaths)
// Process the given namespace interpolating params on the path
// Ex:
// 'users/:id' with params { id: 1 } becomes 'users/1'
const pathAndParams = this.pathBuilder._paramsToPath({ path: namespace, params })
// Pushes the new namespace to the chainedPaths
newInstance.chainedPaths.push(pathAndParams['path'])
return newInstance
}
/**
* Returns a path and params to the index action of a given resource
* @param {string} resource - A resource name
* @param {object} params - Any parameters for the request
* @returns {Promise}
* @example
* const routes = new RouteBuilder
* routes.index('users')
* //=> { path: '/users', params: {} }
*/
index (resource, params) {
const path = snakeCase(resource)
return this._buildPath('get', path, params)
}
/**
* An alias for the {@link RouteBuilder#index} function
*/
list (...args) {
return this.index(...args)
}
/**
* Returns a path and params to the show action of a given resource
* @param {string} resource - A resource name
* @param {object} params - Any parameters for the request
* @param {number|string} params.id - The id of the resource
* @returns {Promise}
* @example
* const routes = new RouteBuilder
* routes.show('users', { id: 1 })
* //=> { path: '/users/1', params: {} }
*/
show (resource, params) {
this._validateIdPresence(params)
const path = `${snakeCase(resource)}/:id`
return this._buildPath('get', path, params)
}
/**
* Returns a path and params to the destroy action of a given resource
* @param {string} resource - A resource name
* @param {object} params - Any parameters for the request
* @param {number|string} params.id - The id of the resource
* @returns {Promise}
* @example
* const routes = new RouteBuilder
* routes.destroy('users', { id: 1 })
* //=> { path: '/users/1', params: {} }
*/
destroy (resource, params) {
this._validateIdPresence(params)
const path = `${snakeCase(resource)}/:id`
return this._buildPath('delete', path, params)
}
/**
* Returns a path and params to the create action of a given resource
* @param {string} resource - A resource name
* @param {object} params - Any parameters for the request
* @returns {Promise}
* @example
* const routes = new RouteBuilder
* routes.create('users', { email: 'john@doe.com' })
* //=> { path: '/users', params: { email: 'john@doe.com' } }
*/
create (resource, params) {
const path = snakeCase(resource)
return this._buildPath('post', path, params)
}
/**
* Returns a path and params to the update action of a given resource
* @param {string} resource - A resource name
* @param {object} params - Any parameters for the request
* @param {number|string} params.id - The id of the resource
* @returns {Promise}
* @example
* const routes = new RouteBuilder
* routes.update('users', { id: 1, email: 'john@doe.com' })
* //=> { path: '/users/1', params: { email: 'john@doe.com' } }
*/
update (resource, params) {
this._validateIdPresence(params)
const path = `${snakeCase(resource)}/:id`
return this._buildPath('patch', path, params)
}
/**
* Returns a path and params to the new action of a given resource
* @param {string} resource - A resource name
* @param {object} params - Any parameters for the request
* @returns {Promise}
* @example
* const routes = new RouteBuilder
* routes.new('users')
* //=> { path: '/users', params: {} }
*/
new (resource, params) {
const path = `${snakeCase(resource)}/new`
return this._buildPath('get', path, params)
}
/**
* Returns a path and params to the edit action of a given resource
* @param {string} resource - A resource name
* @param {object} params - Any parameters for the request
* @param {number|string} params.id - The id of the resource
* @returns {Promise}
* @example
* const routes = new RouteBuilder
* routes.edit('users', { id: 1 })
* //=> { path: '/users/1', params: {} }
*/
edit (resource, params) {
this._validateIdPresence(params)
const path = `${snakeCase(resource)}/:id/edit`
return this._buildPath('get', path, params)
}
/**
* Returns a path and params to the specified GET request
* @param {string} path - A path for the request
* @param {object} params - Any parameters for the request
* @param {number|string} params.id - The id of the resource
* @returns {Promise}
* @example
* const routes = new RouteBuilder
* routes.get('users')
* //=> { path: '/users', params: {} }
*/
get (path, params) {
return this._buildPath('get', path, params)
}
/**
* Returns a path and params to the specified POST request
* @param {string} path - A path for the request
* @param {object} params - Any parameters for the request
* @param {number|string} params.id - The id of the resource
* @returns {Promise}
* @example
* const routes = new RouteBuilder
* routes.post('users', { id: 1 })
* //=> { path: '/users', params: { id: 1 } }
*/
post (path, params) {
return this._buildPath('post', path, params)
}
/**
* Returns a path and params to the specified PATCH request
* @param {string} path - A path for the request
* @param {object} params - Any parameters for the request
* @param {number|string} params.id - The id of the resource
* @returns {Promise}
* @example
* const routes = new RouteBuilder
* routes.patch('users', { id: 1 })
* //=> { path: '/users', params: { id: 1 } }
*/
patch (path, params) {
return this._buildPath('patch', path, params)
}
/**
* Returns a path and params to the specified PUT request
* @param {string} path - A path for the request
* @param {object} params - Any parameters for the request
* @param {number|string} params.id - The id of the resource
* @returns {Promise}
* @example
* const routes = new RouteBuilder
* routes.put('users', { id: 1 })
* //=> { path: '/users', params: { id: 1 } }
*/
put (path, params) {
return this._buildPath('put', path, params)
}
/**
* Returns a path and params to the specified DELETE request
* @param {string} path - A path for the request
* @param {object} params - Any parameters for the request
* @param {number|string} params.id - The id of the resource
* @returns {Promise}
* @example
* const routes = new RouteBuilder
* routes.delete('users', { id: 1 })
* //=> { path: '/users?id=1', params: {} }
*/
delete (path, params) {
return this._buildPath('delete', path, params)
}
/**
* Private functions
*/
_validateIdPresence (params) {
if (!params.id) {
throw new MissingRequiredParameterError('id')
}
}
_buildPath (method, path, params) {
const pathWithNestedResources = this._mergeChainPaths(path)
return this.pathBuilder[method](pathWithNestedResources, params)
}
_mergeChainPaths (mainPath) {
if (this.chainedPaths === []) { return mainPath }
const chainPath = this.chainedPaths.reduce((mergedPath, path) => mergedPath + path + '/', '')
return chainPath + mainPath
}
}
export default RouteBuilder