musora-content-services 2.79.0 → 2.80.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/.coderabbit.yaml +0 -0
  2. package/.editorconfig +0 -0
  3. package/.github/pull_request_template.md +0 -0
  4. package/.github/workflows/conventional-commits.yaml +0 -0
  5. package/.github/workflows/docs.js.yml +0 -0
  6. package/.github/workflows/node.js.yml +0 -0
  7. package/.prettierignore +0 -0
  8. package/.prettierrc +0 -0
  9. package/CHANGELOG.md +14 -0
  10. package/README.md +0 -0
  11. package/babel.config.cjs +0 -0
  12. package/docs/Content.html +269 -0
  13. package/docs/ContentOrganization.html +2 -2
  14. package/docs/Forums.html +2 -2
  15. package/docs/Gamification.html +2 -2
  16. package/docs/TestUser.html +2 -2
  17. package/docs/UserManagementSystem.html +2 -2
  18. package/docs/api_types.js.html +2 -2
  19. package/docs/config.js.html +2 -2
  20. package/docs/content-org_content-org.js.html +2 -2
  21. package/docs/content-org_guided-courses.ts.html +2 -2
  22. package/docs/content-org_learning-paths.ts.html +124 -24
  23. package/docs/content-org_playlists-types.js.html +2 -2
  24. package/docs/content-org_playlists.js.html +2 -2
  25. package/docs/content.js.html +2 -2
  26. package/docs/content_artist.ts.html +212 -0
  27. package/docs/content_content.ts.html +77 -0
  28. package/docs/content_genre.ts.html +211 -0
  29. package/docs/content_instructor.ts.html +203 -0
  30. package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  31. package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  32. package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  33. package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  34. package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  35. package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  36. package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  37. package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  38. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  39. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +0 -0
  40. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  41. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  42. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  43. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  44. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +0 -0
  45. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  46. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  47. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  48. package/docs/forums_categories.ts.html +3 -3
  49. package/docs/forums_discussions.js.html +0 -0
  50. package/docs/forums_forum.js.html +0 -0
  51. package/docs/forums_forums.ts.html +2 -2
  52. package/docs/forums_posts.ts.html +22 -3
  53. package/docs/forums_threads.ts.html +16 -3
  54. package/docs/gamification_awards.js.html +0 -0
  55. package/docs/gamification_awards.ts.html +2 -2
  56. package/docs/gamification_gamification.js.html +2 -2
  57. package/docs/gamification_types.js.html +0 -0
  58. package/docs/global.html +2 -2
  59. package/docs/index.html +2 -2
  60. package/docs/liveTesting.ts.html +2 -2
  61. package/docs/module-Accounts.html +10 -10
  62. package/docs/module-Artist.html +991 -0
  63. package/docs/module-Awards.html +2 -2
  64. package/docs/module-Categories.html +0 -0
  65. package/docs/module-Config.html +2 -2
  66. package/docs/module-Content-Services-V2.html +2 -2
  67. package/docs/module-ForumCategories.html +0 -0
  68. package/docs/module-ForumDiscussions.html +0 -0
  69. package/docs/module-Forums.html +731 -89
  70. package/docs/module-Genre.html +981 -0
  71. package/docs/module-GuidedCourses.html +2 -2
  72. package/docs/module-Instructor.html +929 -0
  73. package/docs/module-Interests.html +2 -2
  74. package/docs/module-LearningPaths.html +640 -12
  75. package/docs/module-Onboarding.html +2 -2
  76. package/docs/module-Payments.html +2 -2
  77. package/docs/module-Permissions.html +2 -2
  78. package/docs/module-Playlists.html +2 -2
  79. package/docs/module-ProgressRow.html +2 -2
  80. package/docs/module-Railcontent-Services.html +2 -2
  81. package/docs/module-Sanity-Services.html +327 -1855
  82. package/docs/module-Sessions.html +2 -2
  83. package/docs/module-Threads.html +0 -0
  84. package/docs/module-UserActivity.html +2 -2
  85. package/docs/module-UserChat.html +2 -2
  86. package/docs/module-UserManagement.html +2 -2
  87. package/docs/module-UserMemberships.html +2 -2
  88. package/docs/module-UserNotifications.html +2 -2
  89. package/docs/module-UserProfile.html +2 -2
  90. package/docs/progress-row_method-card.js.html +8 -7
  91. package/docs/railcontent.js.html +2 -2
  92. package/docs/sanity.js.html +109 -268
  93. package/docs/scripts/collapse.js +0 -0
  94. package/docs/scripts/commonNav.js +0 -0
  95. package/docs/scripts/linenumber.js +0 -0
  96. package/docs/scripts/nav.js +0 -0
  97. package/docs/scripts/polyfill.js +0 -0
  98. package/docs/scripts/prettify/Apache-License-2.0.txt +0 -0
  99. package/docs/scripts/prettify/lang-css.js +0 -0
  100. package/docs/scripts/prettify/prettify.js +0 -0
  101. package/docs/scripts/search.js +0 -0
  102. package/docs/styles/jsdoc.css +0 -0
  103. package/docs/styles/prettify.css +0 -0
  104. package/docs/userActivity.js.html +2 -2
  105. package/docs/user_account.ts.html +5 -11
  106. package/docs/user_chat.js.html +2 -2
  107. package/docs/user_interests.js.html +2 -2
  108. package/docs/user_management.js.html +2 -2
  109. package/docs/user_memberships.js.html +0 -0
  110. package/docs/user_memberships.ts.html +2 -2
  111. package/docs/user_notifications.js.html +2 -2
  112. package/docs/user_onboarding.ts.html +2 -2
  113. package/docs/user_payments.ts.html +2 -2
  114. package/docs/user_permissions.js.html +2 -2
  115. package/docs/user_profile.js.html +2 -2
  116. package/docs/user_sessions.js.html +2 -2
  117. package/docs/user_types.js.html +2 -2
  118. package/docs/user_user-management-system.js.html +2 -2
  119. package/jest.config.js +0 -0
  120. package/jsdoc.json +1 -0
  121. package/package.json +1 -1
  122. package/src/contentMetaData.js +0 -0
  123. package/src/contentTypeConfig.js +2 -2
  124. package/src/filterBuilder.js +0 -0
  125. package/src/index.d.ts +28 -5
  126. package/src/index.js +28 -5
  127. package/src/infrastructure/http/HttpClient.ts +0 -0
  128. package/src/infrastructure/http/executors/FetchRequestExecutor.ts +0 -0
  129. package/src/infrastructure/http/index.ts +0 -0
  130. package/src/infrastructure/http/interfaces/HeaderProvider.ts +0 -0
  131. package/src/infrastructure/http/interfaces/HttpError.ts +0 -0
  132. package/src/infrastructure/http/interfaces/NetworkError.ts +0 -0
  133. package/src/infrastructure/http/interfaces/RequestExecutor.ts +0 -0
  134. package/src/infrastructure/http/interfaces/RequestOptions.ts +0 -0
  135. package/src/infrastructure/http/providers/DefaultHeaderProvider.ts +0 -0
  136. package/src/lib/httpHelper.js +0 -0
  137. package/src/lib/lastUpdated.js +0 -0
  138. package/src/services/api/types.js +0 -0
  139. package/src/services/api/types.ts +0 -0
  140. package/src/services/config.js +0 -0
  141. package/src/services/content/artist.ts +139 -0
  142. package/src/services/content/content.ts +38 -0
  143. package/src/services/content/genre.ts +139 -0
  144. package/src/services/content/instructor.ts +131 -0
  145. package/src/services/content-org/content-org.js +0 -0
  146. package/src/services/content-org/guided-courses.ts +0 -0
  147. package/src/services/content-org/learning-paths.ts +35 -21
  148. package/src/services/content-org/playlists-types.js +0 -0
  149. package/src/services/content-org/playlists.js +0 -0
  150. package/src/services/content.js +0 -0
  151. package/src/services/contentLikes.js +0 -0
  152. package/src/services/contentProgress.js +0 -0
  153. package/src/services/dataContext.js +0 -0
  154. package/src/services/dateUtils.js +0 -0
  155. package/src/services/eventsAPI.js +0 -0
  156. package/src/services/forums/forums.ts +0 -0
  157. package/src/services/forums/posts.ts +0 -0
  158. package/src/services/forums/threads.ts +0 -0
  159. package/src/services/forums/types.ts +0 -0
  160. package/src/services/gamification/awards.ts +0 -0
  161. package/src/services/gamification/gamification.js +0 -0
  162. package/src/services/imageSRCBuilder.js +0 -0
  163. package/src/services/imageSRCVerify.js +0 -0
  164. package/src/services/liveTesting.ts +0 -0
  165. package/src/services/progress-row/method-card.js +0 -0
  166. package/src/services/railcontent.js +0 -0
  167. package/src/services/recommendations.js +0 -0
  168. package/src/services/sanity.js +105 -266
  169. package/src/services/types.js +0 -0
  170. package/src/services/user/account.ts +3 -9
  171. package/src/services/user/chat.js +0 -0
  172. package/src/services/user/interests.js +0 -0
  173. package/src/services/user/management.js +0 -0
  174. package/src/services/user/memberships.ts +0 -0
  175. package/src/services/user/notifications.js +0 -0
  176. package/src/services/user/onboarding.ts +0 -0
  177. package/src/services/user/payments.ts +0 -0
  178. package/src/services/user/permissions.js +0 -0
  179. package/src/services/user/profile.js +0 -0
  180. package/src/services/user/sessions.js +0 -0
  181. package/src/services/user/types.js +0 -0
  182. package/src/services/user/user-management-system.js +0 -0
  183. package/src/services/userActivity.js +0 -0
  184. package/test/HttpClient.test.js +0 -0
  185. package/test/content.test.js +0 -0
  186. package/test/contentLikes.test.js +0 -0
  187. package/test/contentProgress.test.js +0 -0
  188. package/test/dataContext.test.js +0 -0
  189. package/test/forum.test.js +0 -0
  190. package/test/imageSRCBuilder.test.js +0 -0
  191. package/test/imageSRCVerify.test.js +0 -0
  192. package/test/initializeTests.js +0 -0
  193. package/test/learningPaths.test.js +0 -0
  194. package/test/lib/lastUpdated.test.js +0 -0
  195. package/test/live/contentProgressLive.test.js +0 -0
  196. package/test/live/railcontentLive.test.js +0 -0
  197. package/test/localStorageMock.js +0 -0
  198. package/test/log.js +0 -0
  199. package/test/mockData/mockData_fetchByRailContentIds_one_content.json +0 -0
  200. package/test/mockData/mockData_progress_content.json +0 -0
  201. package/test/mockData/mockData_sanity_progress_content.json +0 -0
  202. package/test/mockData/mockData_user_practices.json +0 -0
  203. package/test/notifications.test.js +0 -0
  204. package/test/progressRows.test.js +0 -0
  205. package/test/sanityQueryService.test.js +0 -0
  206. package/test/streakMessage.test.js +0 -0
  207. package/test/user/permissions.test.js +0 -0
  208. package/test/userActivity.test.js +0 -0
  209. package/tools/generate-index.cjs +0 -0
  210. package/.claude/settings.local.json +0 -14
  211. package/.yarnrc.yml +0 -1
@@ -0,0 +1,131 @@
1
+ /**
2
+ * @module Instructor
3
+ */
4
+ import { FilterBuilder } from '../../filterBuilder.js'
5
+ import { filtersToGroq, getFieldsForContentType } from '../../contentTypeConfig.js'
6
+ import { buildEntityAndTotalQuery, fetchSanity, getSortOrder } from '../sanity.js'
7
+ import { Lesson } from './content'
8
+
9
+ export interface Instructor {
10
+ lessonCount: number
11
+ slug: string
12
+ name: string
13
+ short_bio: string
14
+ thumbnail: string
15
+ }
16
+
17
+ /**
18
+ * Fetch all instructor with lessons available for a specific brand.
19
+ *
20
+ * @param {string} brand - The brand for which to fetch instructors.
21
+ * @returns {Promise<Instructor[]>} - A promise that resolves to an array of instructor objects.
22
+ *
23
+ * @example
24
+ * fetchInstructors('drumeo')
25
+ * .then(instructors => console.log(instructors))
26
+ * .catch(error => console.error(error));
27
+ */
28
+ export async function fetchInstructors(brand: string): Promise<Instructor[]> {
29
+ const filter = await new FilterBuilder(`brand == "${brand}" && references(^._id)`, {
30
+ bypassPermissions: true,
31
+ }).buildFilter()
32
+
33
+ const query = `
34
+ *[_type == "instructor"] {
35
+ name,
36
+ "slug": slug.current,
37
+ "lessonsCount": count(*[${filter}])
38
+ }[lessonsCount > 0] |order(lower(name)) `
39
+ return fetchSanity(query, true, { processNeedAccess: false, processPageType: false })
40
+ }
41
+
42
+ /**
43
+ * Fetch a single instructor by their name
44
+ *
45
+ * @param {string} slug - The slug of the instructor to fetch.
46
+ * @param {string} [brand] - The brand for which to fetch the instructor. Lesson count will be filtered by this brand if provided.
47
+ * @returns {Promise<Instructor[]>} - A promise that resolves to an instructor object or null if not found.
48
+ *
49
+ * @example
50
+ * fetchInstructorBySlug('66samus', 'drumeo')
51
+ * .then(instructor => console.log(instructor))
52
+ * .catch(error => console.error(error));
53
+ */
54
+ export async function fetchInstructorBySlug(
55
+ slug: string,
56
+ brand?: string
57
+ ): Promise<Instructor | null> {
58
+ const brandFilter = brand ? `brand == "${brand}" && ` : ''
59
+ const filter = await new FilterBuilder(`${brandFilter} references(^._id)`, {
60
+ bypassPermissions: true,
61
+ }).buildFilter()
62
+
63
+ const query = `
64
+ *[_type == "instructor" && slug.current == '${slug}'][0] {
65
+ name,
66
+ "slug": slug.current,
67
+ short_bio,
68
+ 'thumbnail': thumbnail_url.asset->url,
69
+ "lessonsCount": count(*[${filter}])
70
+ }`
71
+ return fetchSanity(query, true, { processNeedAccess: false, processPageType: false })
72
+ }
73
+
74
+ export interface FetchInstructorLessonsOptions {
75
+ sortOrder?: string
76
+ searchTerm?: string
77
+ page?: number
78
+ limit?: number
79
+ includedFields?: Array<string>
80
+ }
81
+
82
+ export interface InstructorLessonsResponse {
83
+ entity: Lesson[]
84
+ total: number
85
+ }
86
+
87
+ /**
88
+ * Fetch the data needed for the instructor screen.
89
+ * @param {string} brand - The brand for which to fetch instructor lessons
90
+ * @param {string} slug - The slug of the instructor
91
+ *
92
+ * @param {FetchInstructorLessonsOptions} options - Parameters for pagination, filtering and sorting.
93
+ * @param {string} [options.sortOrder="-published_on"] - The field to sort the lessons by.
94
+ * @param {string} [options.searchTerm=""] - The search term to filter content by title.
95
+ * @param {number} [options.page=1] - The page number for pagination.
96
+ * @param {number} [options.limit=10] - The number of items per page.
97
+ * @param {Array<string>} [options.includedFields=[]] - Additional filters to apply to the query in the format of a key,value array. eg. ['difficulty,Intermediate', 'genre,rock'].
98
+ *
99
+ * @returns {Promise<InstructorLessonsResponse>} - The lessons for the instructor or null if not found.
100
+ * @example
101
+ * fetchInstructorLessons('instructor123')
102
+ * .then(lessons => console.log(lessons))
103
+ * .catch(error => console.error(error));
104
+ */
105
+ export async function fetchInstructorLessons(
106
+ slug: string,
107
+ brand: string,
108
+ {
109
+ sortOrder = '-published_on',
110
+ searchTerm = '',
111
+ page = 1,
112
+ limit = 20,
113
+ includedFields = [],
114
+ }: FetchInstructorLessonsOptions = {}
115
+ ): Promise<InstructorLessonsResponse> {
116
+ const fieldsString = getFieldsForContentType() as string
117
+ const start = (page - 1) * limit
118
+ const end = start + limit
119
+ const searchFilter = searchTerm ? `&& title match "${searchTerm}*"` : ''
120
+ const includedFieldsFilter = includedFields.length > 0 ? filtersToGroq(includedFields) : ''
121
+ const filter = `brand == '${brand}' ${searchFilter} ${includedFieldsFilter} && references(*[_type=='instructor' && slug.current == '${slug}']._id)`
122
+ const filterWithRestrictions = await new FilterBuilder(filter).buildFilter()
123
+
124
+ sortOrder = getSortOrder(sortOrder, brand)
125
+ const query = buildEntityAndTotalQuery(filterWithRestrictions, fieldsString, {
126
+ sortOrder: sortOrder,
127
+ start: start,
128
+ end: end,
129
+ })
130
+ return fetchSanity(query, true)
131
+ }
File without changes
File without changes
@@ -101,21 +101,28 @@ export async function resetAllLearningPaths() {
101
101
  * @returns {Promise<Object>} Learning path with enriched lesson data
102
102
  */
103
103
  export async function getEnrichedLearningPath(learningPathId) {
104
- //TODO BEH-1410: refactor/cleanup
105
- let learningPath = await fetchByRailContentId(learningPathId, 'learning-path-v2')
106
- learningPath.children = mapContentToParent(
107
- learningPath.children,
104
+ // TODO: replace addNextLesson with addNaviageTo
105
+ const response = (await addContextToContent(
106
+ fetchByRailContentId,
107
+ learningPathId,
108
+ 'learning-path-v2',
109
+ {
110
+ dataField: 'children',
111
+ dataField_includeParent: true,
112
+ addProgressStatus: true,
113
+ addProgressPercentage: true,
114
+ addProgressTimestamp: true,
115
+ addNextLesson: true,
116
+ }
117
+ )) as any
118
+ if (!response) return response
119
+
120
+ response.children = mapContentToParent(
121
+ response.children,
108
122
  'learning-path-lesson-v2',
109
123
  learningPathId
110
124
  )
111
-
112
- learningPath.children = await addContextToContent(() => learningPath.children, {
113
- addProgressStatus: true,
114
- addProgressPercentage: true,
115
- addProgressTimestamp: true,
116
- })
117
- learningPath = await addContextToContent(() => learningPath, { addNextLesson: true })
118
- return learningPath
125
+ return response
119
126
  }
120
127
 
121
128
  /**
@@ -234,7 +241,7 @@ interface completeMethodIntroVideo {
234
241
  }
235
242
  /**
236
243
  * Handles completion of method intro video and other related actions.
237
- * @param introVideoId - The intro video content ID.
244
+ * @param introVideoId - The method intro video content ID.
238
245
  * @param brand
239
246
  * @returns {Promise<Array>} response - The response object.
240
247
  * @returns {Promise<Object|null>} response.intro_video_response - The intro video completion response or null if already completed.
@@ -245,8 +252,11 @@ export async function completeMethodIntroVideo(introVideoId: number, brand: stri
245
252
 
246
253
  response.intro_video_response = await completeIfNotCompleted(introVideoId)
247
254
 
255
+ const methodStructure = await fetchMethodV2Structure(brand)
256
+ const learningPathId = methodStructure.learningPaths[0].id
257
+
248
258
  const url: string = `${LEARNING_PATHS_PATH}/start`
249
- const body = { brand: brand }
259
+ const body = { learning_path_id: learningPathId, brand: brand }
250
260
  response.active_path_response = await fetchHandler(url, 'POST', null, body)
251
261
 
252
262
  return response
@@ -254,13 +264,18 @@ export async function completeMethodIntroVideo(introVideoId: number, brand: stri
254
264
 
255
265
  interface completeLearningPathIntroVideo {
256
266
  intro_video_response: Object | null,
257
- learning_path_reset_response: void | Object[] | Object
267
+ learning_path_reset_response: void | null,
268
+ lesson_import_response: Object[] | null
258
269
  }
259
270
  /**
260
271
  * Handles completion of learning path intro video and other related actions.
261
- * @param introVideoId
262
- * @param learningPathId
263
- * @param lessonsToImport
272
+ * @param introVideoId - The learning path intro video content ID.
273
+ * @param learningPathId - The content_id of the learning path that this learning path intro video belongs to.
274
+ * @param lessonsToImport - content ids for all lessons with progress found during intro video progress check. empty if user chose not to keep learning path progress.
275
+ * @returns {Promise<Array>} response - The response object.
276
+ * @returns {Promise<Object|null>} response.intro_video_response - The intro video completion response or null if already completed.
277
+ * @returns {Promise<void>} response.learning_path_reset_response - The reset learning path response.
278
+ * @returns {Promise<Object[]>} response.lesson_import_response - The responses for completing each content_id within the learning path.
264
279
  */
265
280
  export async function completeLearningPathIntroVideo(introVideoId: number, learningPathId: number, lessonsToImport: number[] | null) {
266
281
  let response = {} as completeLearningPathIntroVideo
@@ -268,14 +283,13 @@ export async function completeLearningPathIntroVideo(introVideoId: number, learn
268
283
  response.intro_video_response = await completeIfNotCompleted(introVideoId)
269
284
 
270
285
  if (!lessonsToImport) {
271
- // reset progress within the learning path
286
+ // returns nothing now, but it will when watermelon comes 'round
272
287
  response.learning_path_reset_response = await contentStatusReset(learningPathId)
273
288
  } else {
274
- response.learning_path_reset_response = null
275
289
 
276
290
  // todo: add collection context + optimize with bulk calls with watermelon
277
291
  for (const contentId of lessonsToImport) {
278
- response.learning_path_reset_response[contentId] = await contentStatusCompleted(contentId)
292
+ response.lesson_import_response[contentId] = await contentStatusCompleted(contentId)
279
293
  }
280
294
  }
281
295
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes