musora-content-services 1.0.102 → 1.0.105

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.
@@ -150,15 +150,15 @@ export async function fetchSongsInProgress(brand) {
150
150
  * .then(songs => console.log(songs))
151
151
  * .catch(error => console.error(error));
152
152
  */
153
- export async function fetchContentInProgress(type="all", brand, {
154
- page = 1,
155
- limit = 10,
156
- } = {}) {
153
+ export async function fetchContentInProgress(type="all", brand, { page, limit } = {}) {
157
154
  let url;
155
+ const limitString = limit ? `&limit=${limit}` : '';
156
+ const pageString = page ? `&page=${page}` : '';
157
+
158
158
  if(type === "all") {
159
- url = `/content/in_progress/${globalConfig.railcontentConfig.userId}?brand=${brand}&limit=${limit}&page=${page}`;
159
+ url = `/content/in_progress/${globalConfig.railcontentConfig.userId}?brand=${brand}${limitString}${pageString}`;
160
160
  } else {
161
- url = `/content/in_progress/${globalConfig.railcontentConfig.userId}?content_type=${type}&brand=${brand}&limit=${limit}&page=${page}`;
161
+ url = `/content/in_progress/${globalConfig.railcontentConfig.userId}?content_type=${type}&brand=${brand}${limitString}${pageString}`;
162
162
  }
163
163
  const headers = {
164
164
  'Content-Type': 'application/json',
@@ -192,15 +192,15 @@ export async function fetchContentInProgress(type="all", brand, {
192
192
  * .then(songs => console.log(songs))
193
193
  * .catch(error => console.error(error));
194
194
  */
195
- export async function fetchCompletedContent(type="all", brand, {
196
- page = 1,
197
- limit = 10,
198
- } = {}) {
195
+ export async function fetchCompletedContent(type="all", brand, { page, limit } = {}) {
199
196
  let url;
197
+ const limitString = limit ? `&limit=${limit}` : '';
198
+ const pageString = page ? `&page=${page}` : '';
199
+
200
200
  if(type === "all") {
201
- url = `/content/completed/${globalConfig.railcontentConfig.userId}?brand=${brand}&limit=${limit}&page=${page}`;
201
+ url = `/content/completed/${globalConfig.railcontentConfig.userId}?brand=${brand}${limitString}${pageString}`;
202
202
  } else {
203
- url = `/content/completed/${globalConfig.railcontentConfig.userId}?content_type=${type}&brand=${brand}&limit=${limit}&page=${page}`;
203
+ url = `/content/completed/${globalConfig.railcontentConfig.userId}?content_type=${type}&brand=${brand}${limitString}${pageString}`;
204
204
  }
205
205
  const headers = {
206
206
  'Content-Type': 'application/json',
@@ -90,6 +90,7 @@ export async function fetchRelatedSongs(brand, songId) {
90
90
  "url": web_url_path,
91
91
  "published_on": published_on,
92
92
  status,
93
+ "image": thumbnail.asset->url,
93
94
  "fields": [
94
95
  {
95
96
  "key": "title",
@@ -108,10 +109,6 @@ export async function fetchRelatedSongs(brand, songId) {
108
109
  "value": soundslice[0].soundslice_length_in_second
109
110
  }
110
111
  ],
111
- "data": [{
112
- "key": "thumbnail_url",
113
- "value": thumbnail.asset->url
114
- }]
115
112
  }[0...10]),
116
113
  ...(*[_type == "song" && brand == "${brand}" && railcontent_id != ${songId} && references(^.genre[]->_id)]{
117
114
  "type": _type,
@@ -229,6 +226,27 @@ export async function fetchSongCount(brand) {
229
226
  return fetchSanity(query, true);
230
227
  }
231
228
 
229
+ /**
230
+ * Fetch the latest workouts for a specific brand, including completion status and progress.
231
+ * This function retrieves up to five of the latest workout content for a given brand, sorted in descending order by their publication date.
232
+ * It also includes completion status and progress percentage for each workout by fetching additional data about user progress.
233
+ *
234
+ * @param {string} brand - The brand for which to fetch workouts (e.g., 'drumeo', 'pianote').
235
+ * @returns {Promise<Array<Object>|null>} - A promise that resolves to an array of workout data objects with additional properties for completion status and progress percentage, or null if no workouts are found.
236
+ *
237
+ * @example
238
+ * fetchWorkouts('drumeo')
239
+ * .then(workouts => console.log(workouts))
240
+ * .catch(error => console.error(error));
241
+ */
242
+ export async function fetchWorkouts(brand) {
243
+ const fields = getFieldsForContentType('workout');
244
+ const query = `*[_type == 'workout' && brand == '${brand}'] [0...5] {
245
+ ${fields.toString()}
246
+ } | order(published_on desc)[0...5]`
247
+ return fetchSanity(query, true);
248
+ }
249
+
232
250
  /**
233
251
  * Fetch the latest new releases for a specific brand.
234
252
  * @param {string} brand - The brand for which to fetch new releases.
@@ -415,6 +433,7 @@ export async function fetchAll(brand, type, {
415
433
  let config = contentTypeConfig[type] ?? {};
416
434
  let additionalFields = config?.fields ?? [];
417
435
  let isGroupByOneToOne = (groupBy ? config?.relationships?.[groupBy]?.isOneToOne : false) ?? false;
436
+ let webUrlPathType = config?.slug ?? type;
418
437
  const start = (page - 1) * limit;
419
438
  const end = start + limit;
420
439
 
@@ -443,6 +462,7 @@ export async function fetchAll(brand, type, {
443
462
  // Determine the group by clause
444
463
  let query = "";
445
464
  if (groupBy !== "" && isGroupByOneToOne) {
465
+ let webUrlPath = 'artists';
446
466
  query = `
447
467
  {
448
468
  "total": count(*[_type == '${groupBy}' && count(*[_type == '${type}' && brand == '${brand}' && ^._id == ${groupBy}._ref ${searchFilter} ${includedFieldsFilter} ${progressFilter}]._id) > 0]),
@@ -452,7 +472,7 @@ export async function fetchAll(brand, type, {
452
472
  'type': _type,
453
473
  name,
454
474
  'head_shot_picture_url': thumbnail_url.asset->url,
455
- web_url_path,
475
+ 'web_url_path': '/${brand}/${webUrlPath}/'+name+'?included_fieds[]=type,${type}',
456
476
  'all_lessons_count': count(*[_type == '${type}' && brand == '${brand}' && ^._id == ${groupBy}._ref ${searchFilter} ${includedFieldsFilter} ${progressFilter}]._id),
457
477
  'lessons': *[_type == '${type}' && brand == '${brand}' && ^._id == ${groupBy}._ref ${searchFilter} ${includedFieldsFilter} ${progressFilter}]{
458
478
  ${fieldsString},
@@ -463,6 +483,7 @@ export async function fetchAll(brand, type, {
463
483
  [${start}...${end}]
464
484
  }`;
465
485
  } else if (groupBy !== "") {
486
+ let webUrlPath = (groupBy == 'genre')?'/genres':'';
466
487
  query = `
467
488
  {
468
489
  "total": count(*[_type == '${groupBy}' && count(*[_type == '${type}' && brand == '${brand}' && ^._id in ${groupBy}[]._ref ${searchFilter} ${includedFieldsFilter} ${progressFilter}]._id) > 0]),
@@ -472,7 +493,7 @@ export async function fetchAll(brand, type, {
472
493
  'type': _type,
473
494
  name,
474
495
  'head_shot_picture_url': thumbnail_url.asset->url,
475
- web_url_path,
496
+ 'web_url_path': select(defined(web_url_path)=> web_url_path +'?included_fieds[]=type,${type}',!defined(web_url_path)=> '/${brand}${webUrlPath}/'+name+'/${webUrlPathType}'),
476
497
  'all_lessons_count': count(*[_type == '${type}' && brand == '${brand}' && ^._id in ${groupBy}[]._ref ${searchFilter} ${includedFieldsFilter} ${progressFilter}]._id),
477
498
  'lessons': *[_type == '${type}' && brand == '${brand}' && ^._id in ${groupBy}[]._ref ${searchFilter} ${includedFieldsFilter} ${progressFilter}]{
478
499
  ${fieldsString},
@@ -658,6 +679,7 @@ export async function fetchMethod(brand, slug) {
658
679
  status,
659
680
  title,
660
681
  video,
682
+ length_in_seconds,
661
683
  "type": _type,
662
684
  "levels": child[]->
663
685
  {
@@ -712,7 +734,6 @@ export async function fetchMethodNextLesson(railcontentId, methodId) {
712
734
  return childIndex ? await fetchByRailContentId(childIndex) : null;
713
735
  }
714
736
 
715
-
716
737
  /**
717
738
  * Fetch the next lesson for a specific method by Railcontent ID.
718
739
  * @param {string} railcontentId - The Railcontent ID of the current lesson.
package/test/log.js ADDED
@@ -0,0 +1,5 @@
1
+ export function log(message, forceEnabled = false) {
2
+ if (process.env.DEBUG === 'true' || forceEnabled) {
3
+ console.log(message);
4
+ }
5
+ }
@@ -1,4 +1,5 @@
1
1
  import {initializeService} from '../src/services/config.js';
2
+ import {log} from './log.js';
2
3
 
3
4
  const {
4
5
  fetchSongById,
@@ -37,14 +38,14 @@ const {
37
38
 
38
39
  describe('Sanity Queries', function () {
39
40
  beforeEach(() => {
40
- const config = {
41
+ const config = {
41
42
  sanityConfig: {
42
43
  token: process.env.SANITY_API_TOKEN,
43
44
  projectId: process.env.SANITY_PROJECT_ID,
44
45
  dataset: process.env.SANITY_DATASET,
45
- useCachedAPI: process.env.SANITY_USE_CACHED_API || true,
46
+ useCachedAPI: process.env.SANITY_USE_CACHED_API === 'true' || true,
46
47
  version: '2021-06-07',
47
- debug: process.env.DEBUG || false
48
+ debug: process.env.DEBUG === 'true' || false
48
49
  }
49
50
  };
50
51
  initializeService(config);
@@ -66,7 +67,7 @@ describe('Sanity Queries', function () {
66
67
 
67
68
  test('fetchSongArtistCount', async () => {
68
69
  const response = await fetchSongArtistCount('drumeo');
69
- console.log(response);
70
+ log(response);
70
71
  expect(response).toBeGreaterThan(1000);
71
72
  });
72
73
 
@@ -95,7 +96,7 @@ describe('Sanity Queries', function () {
95
96
 
96
97
  test('fetchAllSongs', async () => {
97
98
  const response = await fetchAllSongs('drumeo', {});
98
- console.log(response);
99
+ log(response);
99
100
  expect(response.entity[0].soundslice).toBeDefined();
100
101
  expect(response.entity[0].artist_name).toBeDefined();
101
102
  expect(response.entity[0].instrumentless).toBeDefined();
@@ -111,20 +112,20 @@ describe('Sanity Queries', function () {
111
112
 
112
113
  test('fetchAllWorkouts', async () => {
113
114
  const response = await fetchAll('drumeo', 'workout',{});
114
- console.log(response);
115
+ log(response);
115
116
  expect(response.entity[0].id).toBeDefined();
116
117
  });
117
118
 
118
119
  test('fetchAllInstructorField', async () => {
119
120
  const response = await fetchAll('drumeo', 'quick-tips',{searchTerm: 'Domino Santantonio'});
120
- console.log(response);
121
+ log(response);
121
122
  expect(response.entity[0].id).toBeDefined();
122
123
  expect(response.entity[0].instructors).toBeTruthy();
123
124
  });
124
125
 
125
126
  test('fetchAllSortField', async () => {
126
127
  const response = await fetchAll('drumeo', 'rhythmic-adventures-of-captain-carson',{});
127
- console.log(response);
128
+ log(response);
128
129
  expect(response.entity[0].id).toBeDefined();
129
130
  expect(response.entity[0].sort).toBeDefined();
130
131
  });
@@ -132,7 +133,7 @@ describe('Sanity Queries', function () {
132
133
 
133
134
  test('fetchAllChallenges', async () => {
134
135
  const response = await fetchAll('drumeo', 'challenge',{});
135
- console.log(response);
136
+ log(response);
136
137
  expect(response.entity[0].registration_url).toBeDefined();
137
138
  expect(response.entity[0].enrollment_start_time).toBeDefined();
138
139
  expect(response.entity[0].enrollment_end_time).toBeDefined();
@@ -146,12 +147,12 @@ describe('Sanity Queries', function () {
146
147
 
147
148
  test('fetchAll-CustomFields', async () => {
148
149
  let response = await fetchAll('drumeo', 'challenge',{customFields:['garbage']});
149
- console.log(response);
150
+ log(response);
150
151
  expect(response.entity[0].garbage).toBeDefined();
151
152
  expect(response.entity[0].id).toBeDefined();
152
153
 
153
154
  response = await fetchAll('drumeo', 'challenge',{useDefaultFields: false, customFields:['garbage']});
154
- console.log(response);
155
+ log(response);
155
156
  expect(response.entity[0].garbage).toBeDefined();
156
157
  expect.not.objectContaining(response.entity[0].id);
157
158
  });
@@ -177,8 +178,8 @@ describe('Sanity Queries', function () {
177
178
  const id = 191338; ////https://web-staging-one.musora.com/admin/studio/publishing/structure/play-along;play-along_191338
178
179
  const expectedChildID = 191492;
179
180
  const response = await fetchChildren(id);
180
- console.log('num children', response.length);
181
- console.log(response);
181
+ log('num children', response.length);
182
+ log(response);
182
183
 
183
184
  expect(response.length > 0).toBeTruthy();
184
185
  const foundExpectedChild = response.some((child) => {
@@ -209,15 +210,14 @@ describe('Sanity Queries', function () {
209
210
  });
210
211
 
211
212
  test('fetchMethod', async () => {
212
- const response = await fetchMethod('drumeo', 'drumeo-method');
213
- //console.log(response);
213
+ const response = await fetchMethod('drumeo', 'drumeo-method');log(response);
214
214
  expect(response).toBeDefined();
215
215
  expect(response.levels.length).toBeGreaterThan(0);
216
216
  });
217
217
 
218
218
  test('fetchMethods', async () => {
219
219
  const response = await fetchMethods('drumeo');
220
- console.log(response);
220
+ log(response);
221
221
  expect(response.length).toBeGreaterThan(0);
222
222
  expect(response[0].type).toBe('learning-path');
223
223
  });
@@ -250,15 +250,14 @@ describe('Sanity Queries', function () {
250
250
  });
251
251
 
252
252
  test('fetchFoundation', async () => {
253
- const response = await fetchFoundation('foundations-2019');
254
- // console.log(response);
253
+ const response = await fetchFoundation('foundations-2019');log(response);
255
254
  expect(response.units.length).toBeGreaterThan(0);
256
255
  expect(response.type).toBe('foundation');
257
256
  });
258
257
 
259
258
  test('fetchPackAll', async () => {
260
259
  const response = await fetchPackAll(212899); //https://web-staging-one.musora.com/admin/studio/publishing/structure/pack;pack_212899%2Cinspect%3Don
261
- // console.log(response);
260
+ log(response);
262
261
  expect(response.slug).toBe('creative-control');
263
262
  });
264
263
 
@@ -266,8 +265,9 @@ describe('Sanity Queries', function () {
266
265
  let response = await fetchAllPacks('drumeo');
267
266
  response = await fetchAllPacks('drumeo', 'slug');
268
267
  const titles = response.map((doc) => doc.title);
269
- const sortedTitles = [...titles].sort((a, b) => a.localeCompare(b));
270
- // This fails for upper/lower case compare and I couldn't figure it out. Sanity sorts with case sensativity, and localeCompare should do the same but doesn't
268
+
269
+ const sortedTitles = [...titles].sort((a, b) => a === b ? 0 : a > b ? 1 : -1);
270
+
271
271
  expect(titles).toStrictEqual(sortedTitles);
272
272
  response = await fetchAllPacks('drumeo', 'slug', 'Creative Control');
273
273
  expect(response[0].id).toBe(212899);
@@ -292,4 +292,22 @@ describe('Sanity Queries', function () {
292
292
  const response = await fetchScheduledReleases('drumeo', {});
293
293
  expect(response.length).toBeGreaterThan(0);
294
294
  });
295
+
296
+ test('fetchAll-GroupBy-Genre', async () => {
297
+ let response = await fetchAll('drumeo', 'solo',{groupBy: 'genre'});
298
+ log(response);
299
+ expect(response.entity[0].web_url_path).toContain('/drumeo/genres/');
300
+ });
301
+
302
+ test('fetchAll-GroupBy-Artists', async () => {
303
+ let response = await fetchAll('drumeo', 'song',{groupBy: 'artist'});
304
+ log(response);
305
+ expect(response.entity[0].web_url_path).toContain('/drumeo/artists/');
306
+ });
307
+
308
+ test('fetchAll-GroupBy-Instructors', async () => {
309
+ let response = await fetchAll('drumeo', 'course',{groupBy: 'instructor'});
310
+ log(response);
311
+ expect(response.entity[0].web_url_path).toContain('/drumeo/coaches/');
312
+ });
295
313
  });