Source: cache/index.js

  1. /**
  2. * @module cache
  3. */
  4. const NodeCache = require("node-cache");
  5. const log = require("../../util/log");
  6. const linkedinApi = require("../linkedin_learning");
  7. const fs = require('fs');
  8. /**
  9. * The main server cache.
  10. */
  11. const modulesCache = new NodeCache({
  12. checkperiod: 0,
  13. });
  14. /**
  15. * Get a cached object.
  16. * @param {String} key
  17. * @return {Object} cached object
  18. */
  19. const get = (key) => {
  20. return modulesCache.get(key);
  21. };
  22. /**
  23. * Cache an object.
  24. * @param {String} key
  25. * @param {String} value
  26. */
  27. const set = (key, value) => {
  28. modulesCache.set(key, value);
  29. };
  30. /**
  31. * Return true if key exists in cache.
  32. * @param {String} key
  33. * @return {Boolean} true if exists
  34. */
  35. const has = (key) => {
  36. return modulesCache.has(key);
  37. };
  38. /**
  39. * Remove an object from the cache.
  40. * @param {String} key
  41. */
  42. const del = (key) => {
  43. modulesCache.del(key);
  44. };
  45. /**
  46. * Flush the cache.
  47. */
  48. const flush = () => {
  49. log.info("About to flush CACHE, current stats: %s", modulesCache.getStats());
  50. modulesCache.flushAll();
  51. log.info("Flushed CACHE, stats reset: %s", modulesCache.getStats());
  52. };
  53. /**
  54. * Log the cache stats.
  55. */
  56. const logStats = () => {
  57. log.info("CACHE stats: %s", modulesCache.getStats());
  58. };
  59. /**
  60. * Update all cached learning objects from LinkedIn Learning API.
  61. * Log the results and write out failed objects to a separate log file.
  62. */
  63. const updateAllFromAPI = async () => {
  64. log.info("Attempting to update CACHE from LinkedIn-L API..");
  65. const courses = [];
  66. const videos = [];
  67. let successCount = 0;
  68. let errorCount = 0;
  69. const objectsWithoutURN = {
  70. courses: [],
  71. videos: [],
  72. };
  73. for await (const key of modulesCache.keys()) {
  74. // check if object has an LinkedIn Learning API ID
  75. const val = modulesCache.get(key);
  76. if (/^urn:li:/.test(val.urn)) {
  77. const learningObj = await linkedinApi.fetchLearningObject(val.urn);
  78. if (learningObj) {
  79. ++successCount;
  80. // update object fields
  81. val.title = learningObj.title.value;
  82. val.hyperlink = learningObj.details.urls.webLaunch;
  83. val.longDescription = learningObj.details.descriptionIncludingHtml ?
  84. learningObj.details.descriptionIncludingHtml.value : null;
  85. val.shortDescription = learningObj.details.shortDescriptionIncludingHtml ?
  86. learningObj.details.shortDescriptionIncludingHtml.value : null;
  87. val.picture = learningObj.details.images.primary ?
  88. learningObj.details.images.primary : null;
  89. val.length = learningObj.details.timeToComplete ?
  90. learningObj.details.timeToComplete.duration : null;
  91. // store updated object
  92. modulesCache.set(key, val);
  93. } else {
  94. ++errorCount;
  95. }
  96. // add object to collections despite update
  97. if (/^course-/.test(key)) {
  98. courses.push(modulesCache.get(key));
  99. } else if (/^video-/.test(key)) {
  100. videos.push(modulesCache.get(key));
  101. }
  102. } else {
  103. // log discarded object for not having a URN
  104. if (/^course-/.test(key)) {
  105. objectsWithoutURN.courses.push(val);
  106. } else if (/^video-/.test(key)) {
  107. objectsWithoutURN.videos.push(val);
  108. }
  109. modulesCache.del(key);
  110. }
  111. }
  112. // update collections
  113. modulesCache.set("courses", courses);
  114. modulesCache.set("videos", videos);
  115. errorCount === 0 ?
  116. log.info("CACHE update from LinkedIn-L API finished without errors: %s objects fetched successfully", successCount) :
  117. log.warn("CACHE update from LinkedIn-L API had at least one error: %s succeeded, %s failed", successCount, errorCount);
  118. logStats();
  119. fs.writeFileSync(`./log/objects_without_urn_${(new Date()).toUTCString()}`, JSON.stringify(objectsWithoutURN));
  120. };
  121. /**
  122. * Update a single cached object from LinkedIn Learning API.
  123. * @param {String} key
  124. */
  125. const updateFromAPI = async (key) => {
  126. log.info("Attempting to update CACHE(%s) from LinkedIn-L API..", key);
  127. try {
  128. const val = modulesCache.get(key);
  129. const learningObj = await linkedinApi.fetchLearningObject(val.urn);
  130. if (learningObj) {
  131. // update object fields
  132. val.title = learningObj.title.value;
  133. val.hyperlink = learningObj.details.urls.webLaunch;
  134. val.longDescription = learningObj.details.descriptionIncludingHtml ?
  135. learningObj.details.descriptionIncludingHtml.value : null;
  136. val.shortDescription = learningObj.details.shortDescriptionIncludingHtml ?
  137. learningObj.details.shortDescriptionIncludingHtml.value : null;
  138. val.picture = learningObj.details.images.primary ?
  139. learningObj.details.images.primary : null;
  140. val.length = learningObj.details.timeToComplete ?
  141. learningObj.details.timeToComplete.duration : null;
  142. // store updated object
  143. modulesCache.set(key, val);
  144. // add object to collections if updated
  145. if (/^course-/.test(key)) {
  146. const courses = modulesCache.get("courses");
  147. courses.push(modulesCache.get(key));
  148. modulesCache.set("courses", courses);
  149. } else if (/^video-/.test(key)) {
  150. const videos = modulesCache.get("videos");
  151. videos.push(modulesCache.get(key));
  152. modulesCache.set("videos", videos);
  153. }
  154. }
  155. log.info("Successfully updated CACHE(%s) from LinkedIn-L API..", key);
  156. } catch (error) {
  157. log.error("Failed to update CACHE(%s from LinkedIn-L API.., err: " + error.message, key);
  158. }
  159. };
  160. module.exports = {
  161. get,
  162. set,
  163. has,
  164. del,
  165. flush,
  166. logStats,
  167. updateAllFromAPI,
  168. updateFromAPI,
  169. };