musora-content-services 1.0.19 → 1.0.20
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/docs/fonts/OpenSans-Semibold-webfont.eot +0 -0
- package/docs/fonts/OpenSans-Semibold-webfont.svg +0 -0
- package/docs/fonts/OpenSans-Semibold-webfont.ttf +0 -0
- package/docs/fonts/OpenSans-Semibold-webfont.woff +0 -0
- package/docs/fonts/OpenSans-SemiboldItalic-webfont.eot +0 -0
- package/docs/fonts/OpenSans-SemiboldItalic-webfont.svg +0 -0
- package/docs/fonts/OpenSans-SemiboldItalic-webfont.ttf +0 -0
- package/docs/fonts/OpenSans-SemiboldItalic-webfont.woff +0 -0
- package/package.json +1 -1
- package/publish.sh +0 -0
- package/src/contentTypeConfig.js +30 -0
- package/src/index.js +68 -188
- package/test/sanityQueryService.test.js +35 -0
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/package.json
CHANGED
package/publish.sh
CHANGED
|
File without changes
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
let contentTypeConfig = {
|
|
2
|
+
'song': {
|
|
3
|
+
'fields': [
|
|
4
|
+
'"artist_name": artist->name',
|
|
5
|
+
'soundslice',
|
|
6
|
+
'instrumentless'
|
|
7
|
+
],
|
|
8
|
+
'relationships': {
|
|
9
|
+
'artist': {
|
|
10
|
+
isOneToOne: true
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
'challenge':{
|
|
15
|
+
'fields':[
|
|
16
|
+
'enrollment_start_time',
|
|
17
|
+
'enrollment_end_time',
|
|
18
|
+
'registration_url',
|
|
19
|
+
'"lesson_count": child_count',
|
|
20
|
+
'"primary_cta_text": select(dateTime(published_on) > dateTime(now()) && dateTime(enrollment_start_time) > dateTime(now()) => "Notify Me", "Start Challenge")',
|
|
21
|
+
'challenge_state',
|
|
22
|
+
'challenge_state_text',
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
module.exports = {
|
|
29
|
+
contentTypeConfig
|
|
30
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const {contentTypeConfig} = require('../src/contentTypeConfig.js');
|
|
2
|
+
|
|
1
3
|
let globalConfig = {};
|
|
2
4
|
|
|
3
5
|
/**
|
|
@@ -11,7 +13,7 @@ let globalConfig = {};
|
|
|
11
13
|
* @param {string} config.version - The API version to use.
|
|
12
14
|
* @param {boolean} [config.debug=false] - Optional flag to enable debug mode, which logs the query and results.
|
|
13
15
|
* @param {boolean} [config.useCachedAPI=true] - Optional flag to disable cached API.
|
|
14
|
-
*
|
|
16
|
+
*
|
|
15
17
|
* @example
|
|
16
18
|
* // Initialize the Sanity service in your app.js
|
|
17
19
|
* initializeSanityService({
|
|
@@ -179,7 +181,7 @@ async function fetchRelatedSongs(brand, songId) {
|
|
|
179
181
|
* @param {Array<string>} [params.includedFields=[]] - The fields to include in the query.
|
|
180
182
|
* @param {string} [params.groupBy=""] - The field to group the results by.
|
|
181
183
|
* @returns {Promise<Object|null>} - The fetched song data or null if not found.
|
|
182
|
-
*
|
|
184
|
+
*
|
|
183
185
|
* @example
|
|
184
186
|
* fetchAllSongs('drumeo', {
|
|
185
187
|
* page: 2,
|
|
@@ -200,124 +202,7 @@ async function fetchAllSongs(brand, {
|
|
|
200
202
|
includedFields = [],
|
|
201
203
|
groupBy = ""
|
|
202
204
|
}) {
|
|
203
|
-
|
|
204
|
-
const start = (page - 1) * limit;
|
|
205
|
-
const end = start + limit;
|
|
206
|
-
|
|
207
|
-
// Construct the search filter
|
|
208
|
-
const searchFilter = searchTerm
|
|
209
|
-
? `&& (artist->name match "${searchTerm}*" || title match "${searchTerm}*")`
|
|
210
|
-
: "";
|
|
211
|
-
|
|
212
|
-
// Construct the included fields filter, replacing 'difficulty' with 'difficulty_string'
|
|
213
|
-
const includedFieldsFilter = includedFields.length > 0
|
|
214
|
-
? includedFields.map(field => {
|
|
215
|
-
let [key, value] = field.split(',');
|
|
216
|
-
if (key === 'difficulty') {
|
|
217
|
-
key = 'difficulty_string';
|
|
218
|
-
}
|
|
219
|
-
return `&& ${key} == "${value}"`;
|
|
220
|
-
}).join(' ')
|
|
221
|
-
: "";
|
|
222
|
-
|
|
223
|
-
// Determine the sort order
|
|
224
|
-
let sortOrder;
|
|
225
|
-
switch (sort) {
|
|
226
|
-
case "slug":
|
|
227
|
-
sortOrder = "artist->name asc";
|
|
228
|
-
break;
|
|
229
|
-
case "published_on":
|
|
230
|
-
sortOrder = "published_on desc";
|
|
231
|
-
break;
|
|
232
|
-
case "-published_on":
|
|
233
|
-
sortOrder = "published_on asc";
|
|
234
|
-
break;
|
|
235
|
-
case "-slug":
|
|
236
|
-
sortOrder = "artist->name desc";
|
|
237
|
-
break;
|
|
238
|
-
case "-popularity":
|
|
239
|
-
sortOrder = "popularity desc";
|
|
240
|
-
break;
|
|
241
|
-
default:
|
|
242
|
-
sortOrder = "published_on asc";
|
|
243
|
-
break;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// Determine the group by clause
|
|
247
|
-
let query = "";
|
|
248
|
-
if (groupBy === "artist") {
|
|
249
|
-
query = `
|
|
250
|
-
{
|
|
251
|
-
"total": count(*[_type == 'artist' && count(*[_type == 'song' && brand == '${brand}' && ^._id == artist._ref ]._id) > 0]),
|
|
252
|
-
"entity": *[_type == 'artist' && count(*[_type == 'song' && brand == '${brand}' && ^._id == artist._ref ]._id) > 0]
|
|
253
|
-
{
|
|
254
|
-
'id': _id,
|
|
255
|
-
'type': _type,
|
|
256
|
-
name,
|
|
257
|
-
'head_shot_picture_url': thumbnail_url.asset->url,
|
|
258
|
-
'all_lessons_count': count(*[_type == 'song' && brand == '${brand}' && ^._id == artist._ref ]._id),
|
|
259
|
-
'lessons': *[_type == 'song' && brand == '${brand}' && ^._id == artist._ref ]{
|
|
260
|
-
"id": railcontent_id,
|
|
261
|
-
"type": _type,
|
|
262
|
-
title,
|
|
263
|
-
"thumbnail_url": thumbnail.asset->url,
|
|
264
|
-
"artist_name": artist->name,
|
|
265
|
-
difficulty_string,
|
|
266
|
-
published_on,
|
|
267
|
-
soundslice,
|
|
268
|
-
instrumentless,
|
|
269
|
-
}[0...10]
|
|
270
|
-
}
|
|
271
|
-
|order(${sortOrder})
|
|
272
|
-
[${start}...${end}]
|
|
273
|
-
}`;
|
|
274
|
-
} else if (groupBy === "style") {
|
|
275
|
-
query = `
|
|
276
|
-
{
|
|
277
|
-
"total": count(*[_type == 'genre' && count(*[_type == 'song' && brand == '${brand}' && ^._id in genre[]._ref ]._id) > 0]),
|
|
278
|
-
"entity":
|
|
279
|
-
*[_type == 'genre' && count(*[_type == 'song' && brand == '${brand}' && ^._id in genre[]._ref ]._id)>0]
|
|
280
|
-
{
|
|
281
|
-
'id': _id,
|
|
282
|
-
'type': _type,
|
|
283
|
-
name,
|
|
284
|
-
'head_shot_picture_url': thumbnail_url.asset->url,
|
|
285
|
-
'all_lessons_count': count(*[_type == 'song' && brand == '${brand}' && ^._id in genre[]._ref ]._id),
|
|
286
|
-
'lessons': *[_type == 'song' && brand == '${brand}' && ^._id in genre[]._ref ]{
|
|
287
|
-
"id": railcontent_id,
|
|
288
|
-
"type": _type,
|
|
289
|
-
title,
|
|
290
|
-
"thumbnail_url": thumbnail.asset->url,
|
|
291
|
-
"artist_name": artist->name,
|
|
292
|
-
difficulty_string,
|
|
293
|
-
published_on,
|
|
294
|
-
soundslice,
|
|
295
|
-
instrumentless,
|
|
296
|
-
}[0...10]
|
|
297
|
-
}
|
|
298
|
-
|order(${sortOrder})
|
|
299
|
-
[${start}...${end}]
|
|
300
|
-
}`;
|
|
301
|
-
} else {
|
|
302
|
-
query = `
|
|
303
|
-
{
|
|
304
|
-
"entity": *[_type == 'song' && brand == "${brand}" ${searchFilter} ${includedFieldsFilter}] | order(${sortOrder}) [${start}...${end}] {
|
|
305
|
-
"id": railcontent_id,
|
|
306
|
-
"type": _type,
|
|
307
|
-
title,
|
|
308
|
-
"thumbnail_url": thumbnail.asset->url,
|
|
309
|
-
"artist_name": artist->name,
|
|
310
|
-
difficulty_string,
|
|
311
|
-
published_on,
|
|
312
|
-
soundslice,
|
|
313
|
-
instrumentless,
|
|
314
|
-
},
|
|
315
|
-
"total": count(*[_type == 'song' && brand == "${brand}" ${searchFilter} ${includedFieldsFilter}])
|
|
316
|
-
}
|
|
317
|
-
`;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
return fetchSanity(query, true);
|
|
205
|
+
return fetchAll(brand, 'song', {page, limit, searchTerm, sort, includedFields, groupBy});
|
|
321
206
|
}
|
|
322
207
|
|
|
323
208
|
/**
|
|
@@ -581,7 +466,7 @@ async function fetchByRailContentIds(ids) {
|
|
|
581
466
|
* @param {Array<string>} [params.includedFields=[]] - The fields to include in the query.
|
|
582
467
|
* @param {string} [params.groupBy=""] - The field to group the results by (e.g., 'artist', 'genre').
|
|
583
468
|
* @returns {Promise<Object|null>} - The fetched content data or null if not found.
|
|
584
|
-
*
|
|
469
|
+
*
|
|
585
470
|
* @example
|
|
586
471
|
* fetchAll('drumeo', 'song', {
|
|
587
472
|
* page: 2,
|
|
@@ -602,6 +487,9 @@ async function fetchAll(brand, type, {
|
|
|
602
487
|
includedFields = [],
|
|
603
488
|
groupBy = ""
|
|
604
489
|
}) {
|
|
490
|
+
let config = contentTypeConfig[type] ?? {};
|
|
491
|
+
let additionalFields = config?.fields ?? [];
|
|
492
|
+
let isGroupByOneToOne = (groupBy ? config?.relationships[groupBy]?.isOneToOne : false) ?? false;
|
|
605
493
|
const start = (page - 1) * limit;
|
|
606
494
|
const end = start + limit;
|
|
607
495
|
|
|
@@ -644,10 +532,20 @@ async function fetchAll(brand, type, {
|
|
|
644
532
|
break;
|
|
645
533
|
}
|
|
646
534
|
|
|
535
|
+
let defaultFields = ['railcontent_id',
|
|
536
|
+
'title',
|
|
537
|
+
'"image": thumbnail.asset->url',
|
|
538
|
+
'difficulty',
|
|
539
|
+
'difficulty_string',
|
|
540
|
+
'web_url_path',
|
|
541
|
+
'published_on'];
|
|
542
|
+
|
|
543
|
+
let fields = defaultFields.concat(additionalFields);
|
|
544
|
+
let fieldsString = fields.join(',');
|
|
545
|
+
|
|
647
546
|
// Determine the group by clause
|
|
648
547
|
let query = "";
|
|
649
|
-
|
|
650
|
-
if (groupBy !== "" && !manyReference) {
|
|
548
|
+
if (groupBy !== "" && isGroupByOneToOne) {
|
|
651
549
|
query = `
|
|
652
550
|
{
|
|
653
551
|
"total": count(*[_type == '${groupBy}' && count(*[_type == '${type}' && brand == '${brand}' && ^._id == ${groupBy}._ref ]._id) > 0]),
|
|
@@ -659,20 +557,14 @@ async function fetchAll(brand, type, {
|
|
|
659
557
|
'head_shot_picture_url': thumbnail_url.asset->url,
|
|
660
558
|
'all_lessons_count': count(*[_type == '${type}' && brand == '${brand}' && ^._id == ${groupBy}._ref ]._id),
|
|
661
559
|
'lessons': *[_type == '${type}' && brand == '${brand}' && ^._id == ${groupBy}._ref ]{
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
"image": thumbnail.asset->url,
|
|
665
|
-
difficulty,
|
|
666
|
-
difficulty_string,
|
|
667
|
-
web_url_path,
|
|
668
|
-
published_on,
|
|
669
|
-
${groupBy}
|
|
560
|
+
${fieldsString},
|
|
561
|
+
${groupBy}
|
|
670
562
|
}[0...10]
|
|
671
563
|
}
|
|
672
564
|
|order(${sortOrder})
|
|
673
565
|
[${start}...${end}]
|
|
674
566
|
}`;
|
|
675
|
-
} else if (groupBy !== ""
|
|
567
|
+
} else if (groupBy !== "") {
|
|
676
568
|
query = `
|
|
677
569
|
{
|
|
678
570
|
"total": count(*[_type == '${groupBy}' && count(*[_type == '${type}' && brand == '${brand}' && ^._id in ${groupBy}[]._ref]._id)>0]),
|
|
@@ -684,14 +576,8 @@ async function fetchAll(brand, type, {
|
|
|
684
576
|
'head_shot_picture_url': thumbnail_url.asset->url,
|
|
685
577
|
'all_lessons_count': count(*[_type == '${type}' && brand == '${brand}' && ^._id in ${groupBy}[]._ref ]._id),
|
|
686
578
|
'lessons': *[_type == '${type}' && brand == '${brand}' && ^._id in ${groupBy}[]._ref ]{
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
"image": thumbnail.asset->url,
|
|
690
|
-
difficulty,
|
|
691
|
-
difficulty_string,
|
|
692
|
-
web_url_path,
|
|
693
|
-
published_on,
|
|
694
|
-
${groupBy}
|
|
579
|
+
${fieldsString},
|
|
580
|
+
${groupBy}
|
|
695
581
|
}[0...10]
|
|
696
582
|
}
|
|
697
583
|
|order(${sortOrder})
|
|
@@ -701,20 +587,14 @@ async function fetchAll(brand, type, {
|
|
|
701
587
|
query = `
|
|
702
588
|
{
|
|
703
589
|
"entity": *[_type == '${type}' && brand == "${brand}" ${searchFilter} ${includedFieldsFilter}] | order(${sortOrder}) [${start}...${end}] {
|
|
704
|
-
|
|
705
|
-
title,
|
|
706
|
-
"image": thumbnail.asset->url,
|
|
707
|
-
difficulty,
|
|
708
|
-
difficulty_string,
|
|
709
|
-
web_url_path,
|
|
710
|
-
published_on
|
|
590
|
+
${fieldsString}
|
|
711
591
|
},
|
|
712
592
|
"total": count(*[_type == '${type}' && brand == "${brand}" ${searchFilter} ${includedFieldsFilter}])
|
|
713
593
|
}
|
|
714
594
|
`;
|
|
715
595
|
}
|
|
716
596
|
|
|
717
|
-
return fetchSanity(query,
|
|
597
|
+
return fetchSanity(query, true);
|
|
718
598
|
}
|
|
719
599
|
|
|
720
600
|
/**
|
|
@@ -739,14 +619,14 @@ async function fetchAll(brand, type, {
|
|
|
739
619
|
* .catch(error => console.error(error));
|
|
740
620
|
*/
|
|
741
621
|
async function fetchAllFilterOptions(
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
){
|
|
749
|
-
|
|
622
|
+
brand,
|
|
623
|
+
filters,
|
|
624
|
+
style,
|
|
625
|
+
artist,
|
|
626
|
+
contentType,
|
|
627
|
+
term
|
|
628
|
+
) {
|
|
629
|
+
const query = `
|
|
750
630
|
{
|
|
751
631
|
"meta": {
|
|
752
632
|
"totalResults": count(*[_type == '${contentType}' && brand == "${brand}" && ${style ? `'${style}' in genre[]->name` : `artist->name == '${artist}'`} ${filters}
|
|
@@ -772,7 +652,7 @@ async function fetchAllFilterOptions(
|
|
|
772
652
|
}
|
|
773
653
|
`;
|
|
774
654
|
|
|
775
|
-
|
|
655
|
+
return fetchSanity(query, false);
|
|
776
656
|
}
|
|
777
657
|
|
|
778
658
|
/**
|
|
@@ -852,7 +732,7 @@ async function fetchNextPreviousLesson(railcontentId) {
|
|
|
852
732
|
* Fetch the page data for a specific lesson by Railcontent ID.
|
|
853
733
|
* @param {string} railContentId - The Railcontent ID of the current lesson.
|
|
854
734
|
* @returns {Promise<Object|null>} - The fetched page data or null if found.
|
|
855
|
-
*
|
|
735
|
+
*
|
|
856
736
|
* @example
|
|
857
737
|
* fetchLessonContent('lesson123')
|
|
858
738
|
* .then(data => console.log(data))
|
|
@@ -932,7 +812,7 @@ async function fetchPackAll(railcontentId) {
|
|
|
932
812
|
* Fetch all children of a specific pack by Railcontent ID.
|
|
933
813
|
* @param {string} railcontentId - The Railcontent ID of the pack.
|
|
934
814
|
* @returns {Promise<Array<Object>|null>} - The fetched pack children data or null if not found.
|
|
935
|
-
*
|
|
815
|
+
*
|
|
936
816
|
* @example
|
|
937
817
|
* fetchPackChildren('pack123')
|
|
938
818
|
* .then(children => console.log(children))
|
|
@@ -956,38 +836,38 @@ async function fetchPackChildren(railcontentId) {
|
|
|
956
836
|
* .catch(error => console.error(error));
|
|
957
837
|
*/
|
|
958
838
|
async function fetchSanity(query, isList) {
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
839
|
+
// Check the config object before proceeding
|
|
840
|
+
if (!checkConfig(globalConfig)) {
|
|
841
|
+
return null;
|
|
842
|
+
}
|
|
963
843
|
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
844
|
+
if (globalConfig.debug) {
|
|
845
|
+
console.log("fetchSanity Query:", query);
|
|
846
|
+
}
|
|
967
847
|
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
848
|
+
const encodedQuery = encodeURIComponent(query);
|
|
849
|
+
const api = globalConfig.useCachedAPI ? 'apicdn' : 'api'
|
|
850
|
+
const url = `https://${globalConfig.projectId}.${api}.sanity.io/v${globalConfig.version}/data/query/${globalConfig.dataset}?query=${encodedQuery}`;
|
|
851
|
+
const headers = {
|
|
852
|
+
'Authorization': `Bearer ${globalConfig.token}`,
|
|
853
|
+
'Content-Type': 'application/json'
|
|
854
|
+
};
|
|
975
855
|
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
856
|
+
try {
|
|
857
|
+
const response = await fetch(url, {headers});
|
|
858
|
+
const result = await response.json();
|
|
859
|
+
if (result.result) {
|
|
860
|
+
if (globalConfig.debug) {
|
|
861
|
+
console.log("fetchSanity Results:", result);
|
|
862
|
+
}
|
|
863
|
+
return isList ? result.result : result.result[0];
|
|
864
|
+
} else {
|
|
865
|
+
throw new Error('No results found');
|
|
866
|
+
}
|
|
867
|
+
} catch (error) {
|
|
868
|
+
console.error('fetchSanity: Fetch error:', error);
|
|
869
|
+
return null;
|
|
870
|
+
}
|
|
991
871
|
}
|
|
992
872
|
|
|
993
873
|
|
|
@@ -79,6 +79,41 @@ describe('Sanity Queries', function () {
|
|
|
79
79
|
expect(response.railcontent_id).toBe(id);
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
+
test('fetchAllSongs', async () => {
|
|
83
|
+
const response = await fetchAllSongs('drumeo', {});
|
|
84
|
+
console.log(response);
|
|
85
|
+
expect(response.entity[0].soundslice).toBeDefined();
|
|
86
|
+
expect(response.entity[0].artist_name).toBeDefined();
|
|
87
|
+
expect(response.entity[0].instrumentless).toBeDefined();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test('fetchAllSongsGroupByArtist', async () => {
|
|
91
|
+
const response = await fetchAllSongs('drumeo', {groupBy:"artist"});
|
|
92
|
+
expect(response.entity[0].lessons[0].soundslice).toBeDefined();
|
|
93
|
+
expect(response.entity[0].lessons[0].artist_name).toBeDefined();
|
|
94
|
+
expect(response.entity[0].lessons[0].instrumentless).toBeDefined();
|
|
95
|
+
}, 100000);
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
test('fetchAllWorkouts', async () => {
|
|
99
|
+
const response = await fetchAll('drumeo', 'workout',{});
|
|
100
|
+
console.log(response);
|
|
101
|
+
expect(response.entity[0].railcontent_id).toBeDefined();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('fetchAllChallenges', async () => {
|
|
105
|
+
const response = await fetchAll('drumeo', 'challenge',{});
|
|
106
|
+
console.log(response);
|
|
107
|
+
expect(response.entity[0].registration_url).toBeDefined();
|
|
108
|
+
expect(response.entity[0].enrollment_start_time).toBeDefined();
|
|
109
|
+
expect(response.entity[0].enrollment_end_time).toBeDefined();
|
|
110
|
+
|
|
111
|
+
expect(response.entity[0].lesson_count).toBeDefined();
|
|
112
|
+
expect(response.entity[0].primary_cta_text).toBeDefined();
|
|
113
|
+
expect(response.entity[0].challenge_state).toBeDefined();
|
|
114
|
+
expect(response.entity[0].challenge_state_text).toBeDefined();
|
|
115
|
+
|
|
116
|
+
});
|
|
82
117
|
// test('fetchRelatedLessons', async () => {
|
|
83
118
|
// const id = 380094;
|
|
84
119
|
// const response = await fetchRelatedLessons(id, 'singeo', 'song');
|