/**
* Orders resources
*
* @module resources/orders
* @copyright 2015–2020 RewardOps Inc.
*/
const config = require('../config');
const api = require('../api');
const { cancelOrderSchema } = require('../schemas/cancel-order');
/**
* Higher order function for creating a `getOrderSummary` function in context.
*
* @param {module:resources/orders~OrderContext} orderContext Order context object
*
* @returns {module:resources/orders~GetSummaryFunc} `getSummary` orders function in context
*
* @see {@link module:resources/orders~GetSummaryFunc} for examples.
*
* @protected
*/
const getOrderSummary = orderContext => {
return function getSummary(params, callback) {
const options = {
path: `/${orderContext.contextTypeName}/${orderContext.contextId}/orders/summary`,
config: config.getAll(),
params,
};
if (typeof params !== 'object') {
if (arguments.length === 1 && typeof params === 'function') {
callback = params;
}
callback(Error('A params object is required'));
return;
}
options.params = params;
api.get(options, callback);
};
};
/**
* Get an order summary JSON objects function.
*
* @typedef module:resources/orders~GetSummaryFunc
*
* @property {object} params [Request]{@link https://github.com/request/request} params to pass to the API call
* @property {module:api~requestCallback} callback Callback that handles the response
*
* @example
* ro.program(12).orders.getSummary({},
* (error, responseBody, response) => {
* if (error) {
* console.log(error);
* } else {
* console.log(result);
* }
* });
*/
/**
* Higher order function for creating a `getAll` orders function in context.
*
* @param {module:resources/orders~OrderContext} orderContext Order context object
*
* @returns {module:resources/orders~GetAllFunc} `getAll` orders function in context
*
* @see {@link module:resources/orders~GetAllFunc} for examples.
*
* @protected
*/
const getAllOrders = orderContext => {
return function getAll(memberId, params, callback) {
const options = {
path: `/${orderContext.contextTypeName}/${orderContext.contextId}/orders`,
config: config.getAll(),
};
/*
* If called with three arguments,
* set options.params to the second
* argument (`params`).
*
* Otherwise (i.e., called with just
* two arguments), set the callback as
* the second argument.
*/
if (arguments.length === 3) {
options.params = params;
} else {
options.params = {};
callback = params;
}
options.params.member_id = memberId;
api.get(options, callback);
};
};
/**
* Get an array of order JSON objects function.
*
* @typedef module:resources/orders~GetAllFunc
*
* @property {number} memberId Member ID
* @property {object} [params] [Request]{@link https://github.com/request/request} params to pass to the API call
* @property {module:api~requestCallback} callback Callback that handles the response
*
* @example
* // Get an array of order details for the program with id 12 and member with ID 'abc123'
* ro.program(12).orders.getAll('abc123', (error, responseBody, response) => {
* if (error) {
* console.log(error);
* } else {
* console.log(result);
* }
* });
*/
/**
* Higher order function for creating a `get` order function in context.
*
* @param {module:resources/orders~OrderContext} orderContext Order context object
*
* @returns {module:resources/orders~GetFunc} `get` order function in context
*
* @see {@link module:resources/orders~GetFunc} for examples.
*
* @protected
*/
const getOrder = orderContext => {
return function get(orderId, params, callback) {
const options = {
path: `/${orderContext.contextTypeName}/${orderContext.contextId}/orders/${orderId}`,
config: config.getAll(),
};
/*
* If called with three arguments,
* set options.params to the second
* argument (`params`).
*
* Otherwise (i.e., called with just
* two arguments), set the callback as
* the second argument.
*/
if (arguments.length === 3) {
options.params = params;
} else {
callback = params;
}
api.get(options, callback);
};
};
/**
* Get an order JSON object function.
*
* @typedef module:resources/orders~GetFunc
*
* @property {string} orderId Order ID
* @property {object} [params] [Request]{@link https://github.com/request/request} params to pass to the API call
* @property {module:api~requestCallback} callback Callback that handles the response
*
* @example
* // Gets JSON for the order with ID 938
* ro.program(12).orders.get(938, (error, responseBody, response) => {
* if (error) {
* console.log(error);
* } else {
* console.log(result);
* }
* });
*/
/**
* Higher order function for creating a `create` order function in context.
*
* **NOTE: If the program is configured to use {@link module:config~DefaultConfig geographic-specific PII storage},
* the SDK handles the {@link module:resources/order-recipients~storeOrderRecipient storeOrderRecipient} call under
* the hood by making this function an alias for {@link module:resources/order-recipients~StoreOrderRecipientFunc}.**
*
* @param {module:resources/orders~OrderContext} orderContext Order context object.
* **NOTE: If using geographic-specific PII storage, use
* {@link module:resources/order-recipients~V5OrderContext V5OrderContext} type as the parametre instead.**
*
* @returns {module:resources/orders~CreateFunc} `create` order function in context
*
* @see {@link module:resources/orders~CreateFunc} for examples.
*
* @protected
*/
const createOrder = orderContext => {
return function create(params, callback) {
let error;
let errorMessage;
// Argument validations.
if (typeof params !== 'object') {
// Validate that `params` exists and is an object.
errorMessage = 'A params object is required';
} else if (config.get('apiVersion') === 'v3' && typeof params.reward_id !== 'number') {
// Validate that `params.reward_id` is a number.
// NOTE: This is only required in RewardOps API v3
errorMessage = 'reward_id must be a number';
} else if (config.get('apiVersion') === 'v4' && (!params.member || typeof params.member !== 'object')) {
// Validate that `params.member` is an object.
// NOTE: This is only required in RewardOps API v4
errorMessage = 'must pass a member object in the params object to `orders.create()`';
} else if (config.get('apiVersion') === 'v4' && !Array.isArray(params.items)) {
// Validate that `params.items` is an array.
// NOTE: This is only required in RewardOps API v4
errorMessage = 'must pass an items array in the params object to `orders.create()`';
}
if (errorMessage) {
error = new Error();
error.message = errorMessage;
if (arguments.length === 1 && typeof params === 'function') {
callback = params;
}
callback(error);
return;
}
const options = {
path: `/${orderContext.contextTypeName}/${orderContext.contextId}/orders`,
config: config.getAll(),
params,
};
api.post(options, callback);
};
};
/**
* Create an order function.
*
* NOTE: The `params` object is required and must include a `reward_id` and a `member` object.
*
* @typedef module:resources/orders~CreateFunc
*
* @property {object} params [Request]{@link https://github.com/request/request} params to pass to the create order API call.
* @property {module:api~requestCallback} callback Callback that handles the response.
*
* @example
* // Create new order for program `12`, with reward `45231` for member 'jb0987'
* const program = ro.program(12);
* program.order.create(
* {
* reward_id: 45231,
* member: {
* id: 'jb0987',
* full_name: 'Jolanta Banicki',
* email: 'jolanta.b@example.com',
* },
* },
* (error, responseBody, response) => {
* if (error) {
* console.log(error);
* } else {
* console.log(result);
* }
* }
* );
*/
/**
* Higher order function for creating a `update` order function in context.
*
* @param {module:resources/orders~OrderContext} orderContext Order context object
*
* @returns {module:resources/orders~UpdateFunc} `update` order function in context
*
* @see {@link module:resources/orders~UpdateFunc} for examples.
*
* @protected
*/
const updateOrder = orderContext => {
return function update(orderExternalId, params, callback) {
let error;
let errorMessage;
// Argument validations.
if (typeof orderExternalId !== 'string') {
errorMessage = 'must pass an order (external) ID as the first argument to `orders.update()`';
} else if (typeof params !== 'object') {
errorMessage = 'A params object is required';
}
if (errorMessage) {
error = new Error();
error.message = errorMessage;
if (arguments.length === 1 && typeof orderExternalId === 'function') {
callback = orderExternalId;
} else if (arguments.length === 2 && typeof params === 'function') {
callback = params;
}
callback(error);
return;
}
const options = {
path: `/${orderContext.contextTypeName}/${orderContext.contextId}/orders/${orderExternalId}`,
config: config.getAll(),
params,
};
api.patch(options, callback);
};
};
/**
* Update an order function.
*
* @typedef module:resources/orders~UpdateFunc
*
* @property {string} orderExternalId External order ID.
* @property {object} [params] [Request]{@link https://github.com/request/request} params to pass to the API call.
* @property {module:api~requestCallback} callback Callback that handles the response.
*
* @example
* // Make a patch request to the API for the order with the (external) ID of 'abc123' for program 12
* ro.program(12).order.update('abc123', {
* // params
* }, (error, data) => {
* // ...
* });
*/
/**
* Higher order function for creating a `updateOrderItems` function in context.
*
* @param {module:resources/orders~OrderContext} orderContext Order context object
*
* @returns {module:resources/orders~UpdateFunc} `updateOrderItems` function in context
*
* @see {@link module:resources/orders~UpdateFunc} for examples.
*
* @protected
*/
const updateOrderItemsInContext = orderContext => {
return function updateOrderItems(externalOrderId, params, callback) {
let error;
let errorMessage;
// Argument validations.
if (typeof externalOrderId !== 'string') {
errorMessage = 'must pass an order (external) ID as the first argument to `orders.updateOrderItems()`';
} else if (typeof params !== 'object') {
errorMessage = 'A params object is required';
}
if (errorMessage) {
error = new Error();
error.message = errorMessage;
if (arguments.length === 1 && typeof externalOrderId === 'function') {
callback = externalOrderId;
} else if (arguments.length === 2 && typeof params === 'function') {
callback = params;
}
callback(error);
return;
}
const options = {
path: `/${orderContext.contextTypeName}/${orderContext.contextId}/orders/${externalOrderId}/order_items`,
config: config.getAll(),
params,
};
api.patch(options, callback);
};
};
/**
* Cancel an order function.
*
* @typedef module:resources/orders~CancelFunc
*
* @property {string} orderId RewardOps order ID.
* @property {string} refundReasonDescription Reason for cancelling the order.
* @property {object} [params] [Request]{@link https://github.com/request/request} params to pass to the API call.
* @property {module:api~requestCallback} callback Callback that handles the response.
*
* @example
* // Make a post request to the API for the order with the ID of 'abc123' for program 12
* ro.program(12).order.cancel(
* 'abc123',
* 'reason',
* {
* // params
* },
* (error, data) => {
* // ...
* }
* );
*/
/**
* Higher order function for creating a `cancelOrder` function in context.
*
* @param {module:resources/orders~OrderContext} orderContext Order context object
*
* @returns {module:resources/orders~CancelFunc} `cancelOrder` function in context
*
* @see {@link module:resources/orders~CancelFunc} for examples.
*
* @protected
*/
const cancelOrder = orderContext => {
return function cancel(orderId, refundReasonDescription, params, callback) {
let error;
let errorMessage;
try {
cancelOrderSchema.validateSync(params);
} catch (err) {
errorMessage = err;
}
// Argument validations.
if (typeof params !== 'object') {
// Validate that `params` exists and is an object.
errorMessage = 'A params object is required';
} else if (config.get('apiVersion') !== 'v4') {
errorMessage = 'cancel order is only supported in v4';
} else if (!orderId || typeof orderId !== 'string') {
// Validate that `orderId` exists.
errorMessage = 'must pass an orderId to `orders.cancel()`';
} else if (!refundReasonDescription || typeof refundReasonDescription !== 'string') {
// Validate that `refundReasonDescription` exists.
errorMessage = 'must pass an refundReasonDescription param to `orders.cancel()`';
}
if (errorMessage) {
error = new Error();
error.message = errorMessage;
if (arguments.length === 1 && typeof params === 'function') {
callback = params;
}
callback(error);
return;
}
const options = {
path: `/${orderContext.contextTypeName}/${orderContext.contextId}/orders/${orderId}/refunds?refund_reason_description=${refundReasonDescription}`,
config: config.getAll(),
params,
};
api.post(options, callback);
};
};
/**
* Function for updating all the items in an order.
*
* @typedef module:resources/orders~UpdateOrderItemsFunc
*
* @property {(string)} externalOrderId External order ID.
* @property {object} [params] [Request]{@link https://github.com/request/request} params to pass to the API call
* @property {module:api~requestCallback} callback Callback that handles the response
*
* @example
* // Make a patch request to the API for the order on an orderItem level with the (external) ID of 'abc123' for program 12
* ro.program(12).order.updateOrderItems('abc123', {
* // params
* }, (error, data) => {
* // ...
* });
*/
/**
* Factory for creating `orders` methods and properties.
*
* @param {string} contextTypeName The type of the parent context ('programs')
* @param {number} contextId The ID of the order's parent program
*
* @returns {module:resources/orders~Orders} Orders object
*
* @example
* // Used in the context of a program
* const orders = ro.program(12).orders;
*
* @protected
*/
function ordersFactory(contextTypeName, contextId) {
const orderContext = {
contextTypeName,
contextId,
};
if (contextTypeName === 'programs') {
return {
...orderContext,
get: getOrder(orderContext),
getAll: getAllOrders(orderContext),
getSummary: getOrderSummary(orderContext),
create: createOrder(orderContext),
update: updateOrder(orderContext),
updateOrderItems: updateOrderItemsInContext(orderContext),
cancel: cancelOrder(orderContext),
};
}
throw Error('Can only create an orders object for programs');
}
/**
* Orders methods object
*
* @typedef module:resources/orders~Orders
*
* @property {string} contextTypeName The type of the parent context ('programs')
* @property {number} contextId The ID of the order's parent program
* @property {module:resources/orders~GetFunc} get Get order function
* @property {module:resources/orders~GetAllFunc} getAll Get all orders function
* @property {module:resources/orders~GetSummaryFunc} getSummary Get summary function
* @property {module:resources/orders~CreateFunc} create Create order function
* @property {module:resources/orders~UpdateFunc} update Update order function
* @property {module:resources/orders~UpdateOrderItemsFunc} updateOrderItems Update order items function
*/
/**
* Order options object
*
* @typedef module:resources/orders~OrderContext
*
* @property {string} contextTypeName The type of the parent context ('programs')
* @property {number} contextId The ID of the order's parent program
*/
module.exports = ordersFactory;
Source