musora-content-services 1.0.70 → 1.0.72
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.
- package/CHANGELOG.md +4 -0
- package/package.json +1 -1
- package/src/contentTypeConfig.js +32 -1
- package/src/index.js +2 -0
- package/src/services/sanity.js +91 -39
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [1.0.72](https://github.com/railroadmedia/musora-content-services/compare/v1.0.71...v1.0.72) (2024-08-29)
|
|
6
|
+
|
|
7
|
+
### [1.0.71](https://github.com/railroadmedia/musora-content-services/compare/v1.0.70...v1.0.71) (2024-08-28)
|
|
8
|
+
|
|
5
9
|
### [1.0.70](https://github.com/railroadmedia/musora-content-services/compare/v1.0.69...v1.0.70) (2024-08-27)
|
|
6
10
|
|
|
7
11
|
### [1.0.69](https://github.com/railroadmedia/musora-content-services/compare/v1.0.68...v1.0.69) (2024-08-27)
|
package/package.json
CHANGED
package/src/contentTypeConfig.js
CHANGED
|
@@ -85,6 +85,11 @@ let contentTypeConfig = {
|
|
|
85
85
|
'xp',
|
|
86
86
|
]
|
|
87
87
|
},
|
|
88
|
+
'workout': {
|
|
89
|
+
'fields': [
|
|
90
|
+
artistOrInstructorNameAsArray(),
|
|
91
|
+
]
|
|
92
|
+
},
|
|
88
93
|
'quick-tips': {
|
|
89
94
|
'fields': [
|
|
90
95
|
'"instructors": instructor[]->name'
|
|
@@ -220,6 +225,7 @@ let contentTypeConfig = {
|
|
|
220
225
|
'"lesson_count": child_count',
|
|
221
226
|
'xp',
|
|
222
227
|
`"description": ${descriptionField}`,
|
|
228
|
+
'"instructors": instructor[]->name'
|
|
223
229
|
],
|
|
224
230
|
}
|
|
225
231
|
}
|
|
@@ -237,12 +243,37 @@ function getFieldsForContentType(contentType, asQueryString=true) {
|
|
|
237
243
|
const fields = contentType ? DEFAULT_FIELDS.concat(contentTypeConfig?.[contentType]?.fields ?? []) : DEFAULT_FIELDS;
|
|
238
244
|
return asQueryString ? fields.toString() + ',' : fields;
|
|
239
245
|
}
|
|
246
|
+
/**
|
|
247
|
+
* Takes the included fields array and returns a string that can be used in a groq query.
|
|
248
|
+
* @param {Array<string>} filters - An array of strings that represent applied filters. This should be in the format of a key,value array. eg. ['difficulty,Intermediate', 'genre,rock']
|
|
249
|
+
* @returns {string} - A string that can be used in a groq query
|
|
250
|
+
*/
|
|
251
|
+
function filtersToGroq(filters) {
|
|
252
|
+
const groq = filters.map(field => {
|
|
253
|
+
let [key, value] = field.split(',');
|
|
254
|
+
switch (key) {
|
|
255
|
+
case 'difficulty':
|
|
256
|
+
return `&& difficulty_string == "${value}"`;
|
|
257
|
+
case 'genre':
|
|
258
|
+
return `&& genre[]->name match "${value}"`;
|
|
259
|
+
case 'topic':
|
|
260
|
+
return `&& topic[]->name match "${value}"`;
|
|
261
|
+
case 'instrumentless':
|
|
262
|
+
return `&& instrumentless == ${value}`;
|
|
263
|
+
default:
|
|
264
|
+
return `&& ${key} == "${value}"`;
|
|
265
|
+
}
|
|
266
|
+
}).join(' ');
|
|
267
|
+
return groq;
|
|
268
|
+
}
|
|
240
269
|
|
|
241
270
|
module.exports = {
|
|
242
271
|
contentTypeConfig,
|
|
272
|
+
descriptionField,
|
|
243
273
|
artistOrInstructorName,
|
|
244
274
|
artistOrInstructorNameAsArray,
|
|
245
275
|
getFieldsForContentType,
|
|
246
276
|
DEFAULT_FIELDS,
|
|
247
|
-
assignmentsField
|
|
277
|
+
assignmentsField,
|
|
278
|
+
filtersToGroq,
|
|
248
279
|
}
|
package/src/index.js
CHANGED
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
fetchChildren,
|
|
30
30
|
fetchParentByRailContentId,
|
|
31
31
|
fetchMethodPreviousNextLesson,
|
|
32
|
+
fetchLiveEvent,
|
|
32
33
|
} from './services/sanity.js';
|
|
33
34
|
|
|
34
35
|
import {
|
|
@@ -71,4 +72,5 @@ export {
|
|
|
71
72
|
fetchChildren,
|
|
72
73
|
fetchParentByRailContentId,
|
|
73
74
|
fetchMethodPreviousNextLesson,
|
|
75
|
+
fetchLiveEvent,
|
|
74
76
|
}
|
package/src/services/sanity.js
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module Sanity-Services
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
artistOrInstructorName,
|
|
6
|
+
artistOrInstructorNameAsArray,
|
|
7
|
+
assignmentsField,
|
|
8
|
+
descriptionField,
|
|
9
|
+
contentTypeConfig,
|
|
10
|
+
DEFAULT_FIELDS,
|
|
11
|
+
getFieldsForContentType,
|
|
12
|
+
filtersToGroq
|
|
13
|
+
} from "../contentTypeConfig";
|
|
5
14
|
import {globalConfig} from "./config";
|
|
6
15
|
|
|
7
16
|
import { fetchAllCompletedStates, fetchCurrentSongComplete } from './railcontent.js';
|
|
@@ -232,18 +241,9 @@ export async function fetchSongCount(brand) {
|
|
|
232
241
|
* .catch(error => console.error(error));
|
|
233
242
|
*/
|
|
234
243
|
export async function fetchWorkouts(brand) {
|
|
244
|
+
const fields = getFieldsForContentType('workout');
|
|
235
245
|
const query = `*[_type == 'workout' && brand == '${brand}'] [0...5] {
|
|
236
|
-
|
|
237
|
-
title,
|
|
238
|
-
"image": thumbnail.asset->url,
|
|
239
|
-
"artist_name": instructor[0]->name,
|
|
240
|
-
"artists": instructor[]->name,
|
|
241
|
-
difficulty,
|
|
242
|
-
difficulty_string,
|
|
243
|
-
length_in_seconds,
|
|
244
|
-
published_on,
|
|
245
|
-
"type": _type,
|
|
246
|
-
web_url_path,
|
|
246
|
+
${fields.toString()}
|
|
247
247
|
} | order(published_on desc)[0...5]`
|
|
248
248
|
return fetchSanity(query, true);
|
|
249
249
|
}
|
|
@@ -301,8 +301,7 @@ export async function fetchUpcomingEvents(brand) {
|
|
|
301
301
|
};
|
|
302
302
|
const typesString = arrayJoinWithQuotes(liveTypes[brand] ?? liveTypes['default']);
|
|
303
303
|
const now = getSanityDate(new Date());
|
|
304
|
-
|
|
305
|
-
const query = `*[_type in [${typesString}] && brand == '${brand}' && published_on > '${now}']{
|
|
304
|
+
const query = `*[_type in [${typesString}] && brand == '${brand}' && published_on > '${now}' && status == 'scheduled']{
|
|
306
305
|
"id": railcontent_id,
|
|
307
306
|
title,
|
|
308
307
|
"image": thumbnail.asset->url,
|
|
@@ -404,13 +403,7 @@ export async function fetchAll(brand, type, {
|
|
|
404
403
|
|
|
405
404
|
// Construct the included fields filter, replacing 'difficulty' with 'difficulty_string'
|
|
406
405
|
const includedFieldsFilter = includedFields.length > 0
|
|
407
|
-
? includedFields
|
|
408
|
-
let [key, value] = field.split(',');
|
|
409
|
-
if (key === 'difficulty') {
|
|
410
|
-
key = 'difficulty_string';
|
|
411
|
-
}
|
|
412
|
-
return `&& ${key} == "${value}"`;
|
|
413
|
-
}).join(' ')
|
|
406
|
+
? filtersToGroq(includedFields)
|
|
414
407
|
: "";
|
|
415
408
|
|
|
416
409
|
// Determine the sort order
|
|
@@ -524,16 +517,9 @@ export async function fetchAllFilterOptions(
|
|
|
524
517
|
contentType,
|
|
525
518
|
term
|
|
526
519
|
) {
|
|
527
|
-
const
|
|
528
|
-
let [key, value] = field.split(',');
|
|
529
|
-
if (key === 'difficulty') {
|
|
530
|
-
key = 'difficulty_string';
|
|
531
|
-
}
|
|
532
|
-
return `&& ${key} == "${value}"`;
|
|
533
|
-
}).join(' ')
|
|
534
|
-
: undefined;
|
|
520
|
+
const includedFieldsFilter = filters?.length > 0 ? filtersToGroq(filters) : undefined;
|
|
535
521
|
|
|
536
|
-
const commonFilter = `_type == '${contentType}' && brand == "${brand}"${style ? ` && '${style}' in genre[]->name` : ''}${artist ? ` && artist->name == '${artist}'` : ''} ${
|
|
522
|
+
const commonFilter = `_type == '${contentType}' && brand == "${brand}"${style ? ` && '${style}' in genre[]->name` : ''}${artist ? ` && artist->name == '${artist}'` : ''} ${includedFieldsFilter ? includedFieldsFilter : ''}`;
|
|
537
523
|
const query = `
|
|
538
524
|
{
|
|
539
525
|
"meta": {
|
|
@@ -569,6 +555,8 @@ export async function fetchAllFilterOptions(
|
|
|
569
555
|
*/
|
|
570
556
|
export async function fetchChildren(railcontentId, contentType) {
|
|
571
557
|
const query = `*[railcontent_id == ${railcontentId}]{
|
|
558
|
+
title,
|
|
559
|
+
|
|
572
560
|
'children': child[]->{
|
|
573
561
|
${getFieldsForContentType(contentType)}
|
|
574
562
|
},
|
|
@@ -600,11 +588,8 @@ export async function fetchParentByRailContentId(railcontentId) {
|
|
|
600
588
|
* @returns {Promise<Object|null>} - The fetched methods data or null if not found.
|
|
601
589
|
*/
|
|
602
590
|
export async function fetchMethods(brand) {
|
|
603
|
-
//TODOS
|
|
604
|
-
//ADD INSTRUCTORS AND POSITION
|
|
605
591
|
const query = `*[_type == 'learning-path' && brand == '${brand}'] {
|
|
606
|
-
${ getFieldsForContentType() }
|
|
607
|
-
"position": count(*[_type == 'learning-path' && brand == '${brand}' && (published_on < ^.published_on || (published_on == ^.published_on && _id < ^._id))]) + 1
|
|
592
|
+
${ getFieldsForContentType() }
|
|
608
593
|
} | order(published_on asc)`
|
|
609
594
|
return fetchSanity(query, true);
|
|
610
595
|
}
|
|
@@ -617,7 +602,7 @@ export async function fetchMethods(brand) {
|
|
|
617
602
|
*/
|
|
618
603
|
export async function fetchMethod(brand, slug) {
|
|
619
604
|
const query = `*[_type == 'learning-path' && brand == "${brand}" && slug.current == "${slug}"] {
|
|
620
|
-
"description":
|
|
605
|
+
"description": ${descriptionField},
|
|
621
606
|
"instructors":instructor[]->name,
|
|
622
607
|
published_on,
|
|
623
608
|
"id": railcontent_id,
|
|
@@ -638,7 +623,7 @@ export async function fetchMethod(brand, slug) {
|
|
|
638
623
|
"instructor": instructor[]->{name},
|
|
639
624
|
title,
|
|
640
625
|
"type": _type,
|
|
641
|
-
"description":
|
|
626
|
+
"description": ${descriptionField},
|
|
642
627
|
"url": web_url_path,
|
|
643
628
|
xp,
|
|
644
629
|
}
|
|
@@ -652,7 +637,16 @@ return fetchSanity(query, false);
|
|
|
652
637
|
* @returns {Promise<Object|null>} - The fetched next lesson data or null if not found.
|
|
653
638
|
*/
|
|
654
639
|
export async function fetchMethodChildren(railcontentId) {
|
|
655
|
-
|
|
640
|
+
const query = `*[railcontent_id == ${railcontentId}]{
|
|
641
|
+
child_count,
|
|
642
|
+
"description": ${descriptionField},
|
|
643
|
+
title,
|
|
644
|
+
xp,
|
|
645
|
+
'children': child[]->{
|
|
646
|
+
${getFieldsForContentType('method')}
|
|
647
|
+
},
|
|
648
|
+
}[0..1]`;
|
|
649
|
+
return fetchSanity(query, true);
|
|
656
650
|
}
|
|
657
651
|
|
|
658
652
|
/**
|
|
@@ -758,8 +752,23 @@ export async function fetchNextPreviousLesson(railcontentId) {
|
|
|
758
752
|
* .catch(error => console.error(error));
|
|
759
753
|
*/
|
|
760
754
|
export async function fetchLessonContent(railContentId) {
|
|
761
|
-
const query = `*[railcontent_id == ${railContentId} ]
|
|
762
|
-
|
|
755
|
+
const query = `*[railcontent_id == ${railContentId} ]{
|
|
756
|
+
title,
|
|
757
|
+
published_on,
|
|
758
|
+
"type":_type,
|
|
759
|
+
"resources": resource,
|
|
760
|
+
difficulty,
|
|
761
|
+
difficulty_string,
|
|
762
|
+
brand,
|
|
763
|
+
soundslice,
|
|
764
|
+
instrumentless,
|
|
765
|
+
railcontent_id,
|
|
766
|
+
"id":railcontent_id,
|
|
767
|
+
slug, artist->,
|
|
768
|
+
"thumbnail_url":thumbnail.asset->url,
|
|
769
|
+
"url": web_url_path,
|
|
770
|
+
soundslice_slug,
|
|
771
|
+
description,
|
|
763
772
|
"chapters": chapter[]{
|
|
764
773
|
chapter_description,
|
|
765
774
|
chapter_timecode,
|
|
@@ -820,6 +829,49 @@ export async function fetchPackAll(railcontentId) {
|
|
|
820
829
|
return fetchSanity(query, true);
|
|
821
830
|
}
|
|
822
831
|
|
|
832
|
+
export async function fetchLiveEvent(brand) {
|
|
833
|
+
//calendarIDs taken from addevent.php
|
|
834
|
+
// TODO import instructor calendars to Sanity
|
|
835
|
+
let defaultCalendarID = '';
|
|
836
|
+
switch(brand) {
|
|
837
|
+
case ('drumeo'):
|
|
838
|
+
defaultCalendarID = 'GP142387';
|
|
839
|
+
break;
|
|
840
|
+
case ('pianote'):
|
|
841
|
+
defaultCalendarID = 'be142408';
|
|
842
|
+
break;
|
|
843
|
+
case ('guitareo'):
|
|
844
|
+
defaultCalendarID = 'IJ142407';
|
|
845
|
+
break;
|
|
846
|
+
case ('singeo'):
|
|
847
|
+
defaultCalendarID = 'bk354284';
|
|
848
|
+
break;
|
|
849
|
+
default:
|
|
850
|
+
break;
|
|
851
|
+
}
|
|
852
|
+
let dateTemp = new Date();
|
|
853
|
+
dateTemp.setDate(dateTemp.getDate() - 1);
|
|
854
|
+
|
|
855
|
+
// See LiveStreamEventService.getCurrentOrNextLiveEvent for some nice complicated logic which I don't think is actually importart
|
|
856
|
+
// this has some +- on times
|
|
857
|
+
// But this query just finds the first scheduled event (sorted by start_time) that ends after now()
|
|
858
|
+
const query = `*[status == 'scheduled' && defined(live_event_start_time) && published_on > '${getSanityDate(dateTemp)}' && live_event_end_time >= '${getSanityDate(new Date())}']{
|
|
859
|
+
'slug': slug.current,
|
|
860
|
+
'id': railcontent_id,
|
|
861
|
+
live_event_start_time,
|
|
862
|
+
live_event_end_time,
|
|
863
|
+
railcontent_id,
|
|
864
|
+
published_on,
|
|
865
|
+
'event_coach_url' : instructor[0]->web_url_path,
|
|
866
|
+
'event_coach_calendar_id': coalesce(calendar_id, '${defaultCalendarID}'),
|
|
867
|
+
title,
|
|
868
|
+
"image": thumbnail.asset->url,
|
|
869
|
+
"instructors": instructor[]->name,
|
|
870
|
+
'videoId': coalesce(live_event_youtube_id, video.external_id),
|
|
871
|
+
} | order(live_event_start_time)[0...1]`;
|
|
872
|
+
return await fetchSanity(query, false);
|
|
873
|
+
}
|
|
874
|
+
|
|
823
875
|
/**
|
|
824
876
|
* Fetch all children of a specific pack by Railcontent ID.
|
|
825
877
|
* @param {string} railcontentId - The Railcontent ID of the pack.
|