modules/recommendations.js

  1. /* eslint-disable object-curly-newline, no-param-reassign */
  2. const EventDispatcher = require('../utils/event-dispatcher');
  3. const helpers = require('../utils/helpers');
  4. // Create URL from supplied parameters
  5. function createRecommendationsUrl(podId, parameters, options) {
  6. const { apiKey, version, serviceUrl, sessionId, userId, clientId, segments } = options;
  7. let queryParams = { c: version };
  8. queryParams.key = apiKey;
  9. queryParams.i = clientId;
  10. queryParams.s = sessionId;
  11. // Validate pod identifier is provided
  12. if (!podId || typeof podId !== 'string') {
  13. throw new Error('podId is a required parameter of type string');
  14. }
  15. // Pull user segments from options
  16. if (segments && segments.length) {
  17. queryParams.us = segments;
  18. }
  19. // Pull user id from options and ensure string
  20. if (userId) {
  21. queryParams.ui = String(userId);
  22. }
  23. if (parameters) {
  24. const {
  25. numResults,
  26. itemIds,
  27. section,
  28. term,
  29. filters,
  30. variationsMap,
  31. hiddenFields,
  32. preFilterExpression,
  33. } = parameters;
  34. // Pull num results number from parameters
  35. if (!helpers.isNil(numResults)) {
  36. queryParams.num_results = numResults;
  37. }
  38. // Pull item ids from parameters
  39. if (itemIds) {
  40. queryParams.item_id = itemIds;
  41. }
  42. // Pull section from parameters
  43. if (section) {
  44. queryParams.section = section;
  45. }
  46. // Pull term from parameters
  47. if (term) {
  48. queryParams.term = term;
  49. }
  50. // Pull filters from parameters
  51. if (filters) {
  52. queryParams.filters = filters;
  53. }
  54. // Pull hidden fields from parameters
  55. if (hiddenFields) {
  56. if (queryParams.fmt_options) {
  57. queryParams.fmt_options.hidden_fields = hiddenFields;
  58. } else {
  59. queryParams.fmt_options = { hidden_fields: hiddenFields };
  60. }
  61. }
  62. // Pull variations map from parameters
  63. if (variationsMap) {
  64. queryParams.variations_map = JSON.stringify(variationsMap);
  65. }
  66. // Pull pre_filter_expression from parameters
  67. if (preFilterExpression) {
  68. queryParams.pre_filter_expression = JSON.stringify(preFilterExpression);
  69. }
  70. }
  71. queryParams = helpers.cleanParams(queryParams);
  72. const queryString = helpers.stringify(queryParams);
  73. return `${serviceUrl}/recommendations/v1/pods/${helpers.encodeURIComponentRFC3986(helpers.trimNonBreakingSpaces(podId))}?${queryString}`;
  74. }
  75. /**
  76. * Interface to recommendations related API calls
  77. *
  78. * @module recommendations
  79. * @inner
  80. * @returns {object}
  81. */
  82. class Recommendations {
  83. constructor(options) {
  84. this.options = options || {};
  85. this.eventDispatcher = new EventDispatcher(options.eventDispatcher);
  86. }
  87. /**
  88. * Get recommendations for supplied pod identifier
  89. *
  90. * @function getRecommendations
  91. * @description Retrieve recommendation results from Constructor.io API
  92. * @param {string} podId - Pod identifier
  93. * @param {object} [parameters] - Additional parameters to refine results
  94. * @param {string|array} [parameters.itemIds] - Item ID(s) to retrieve recommendations for (strategy specific)
  95. * @param {number} [parameters.numResults] - The number of results to return
  96. * @param {string} [parameters.section] - The section to return results from
  97. * @param {string} [parameters.term] - The term to use to refine results (strategy specific)
  98. * @param {object} [parameters.filters] - Key / value mapping of filters used to refine results
  99. * @param {object} [parameters.variationsMap] - The variations map object to aggregate variations. Please refer to https://docs.constructor.com/reference/shared-variations-mapping for details
  100. * @param {object} [parameters.preFilterExpression] - Faceting expression to scope search results. Please refer to https://docs.constructor.com/reference/configuration-collections
  101. * @param {string[]} [parameters.hiddenFields] - Hidden metadata fields to return
  102. * @param {object} [networkParameters] - Parameters relevant to the network request
  103. * @param {number} [networkParameters.timeout] - Request timeout (in milliseconds)
  104. * @returns {Promise}
  105. * @see https://docs.constructor.com/reference/recommendations-recommendation-results
  106. * @example
  107. * constructorio.recommendations.getRecommendations('t-shirt-best-sellers', {
  108. * numResults: 5,
  109. * filters: {
  110. * size: 'medium'
  111. * },
  112. * });
  113. */
  114. getRecommendations(podId, parameters, networkParameters = {}) {
  115. let requestUrl;
  116. const { fetch } = this.options;
  117. let signal;
  118. if (typeof AbortController === 'function') {
  119. const controller = new AbortController();
  120. signal = controller && controller.signal;
  121. // Handle network timeout if specified
  122. helpers.applyNetworkTimeout(this.options, networkParameters, controller);
  123. }
  124. try {
  125. requestUrl = createRecommendationsUrl(podId, parameters, this.options);
  126. } catch (e) {
  127. return Promise.reject(e);
  128. }
  129. return fetch(requestUrl, { signal })
  130. .then(helpers.convertResponseToJson)
  131. .then((json) => {
  132. if (json.response && json.response.results) {
  133. if (json.result_id) {
  134. // Append `result_id` to each result item
  135. json.response.results.forEach((result) => {
  136. // eslint-disable-next-line no-param-reassign
  137. result.result_id = json.result_id;
  138. });
  139. }
  140. this.eventDispatcher.queue('recommendations.getRecommendations.completed', json);
  141. return json;
  142. }
  143. throw new Error('getRecommendations response data is malformed');
  144. });
  145. }
  146. }
  147. module.exports = Recommendations;