musora-content-services 1.1.3 → 1.2.1

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 (67) hide show
  1. package/.github/workflows/node.js.yml +0 -0
  2. package/CHANGELOG.md +16 -0
  3. package/README.md +0 -0
  4. package/babel.config.cjs +0 -0
  5. package/docs/config.js.html +0 -0
  6. package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  7. package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  8. package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  9. package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  10. package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  11. package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  12. package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  13. package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  14. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  15. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +0 -0
  16. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  17. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  18. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  19. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  20. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +0 -0
  21. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  22. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  23. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  24. package/docs/index.html +0 -0
  25. package/docs/module-Config.html +0 -0
  26. package/docs/module-Railcontent-Services.html +0 -0
  27. package/docs/module-Sanity-Services.html +0 -0
  28. package/docs/railcontent.js.html +0 -0
  29. package/docs/sanity.js.html +0 -0
  30. package/docs/scripts/collapse.js +0 -0
  31. package/docs/scripts/commonNav.js +0 -0
  32. package/docs/scripts/linenumber.js +0 -0
  33. package/docs/scripts/nav.js +0 -0
  34. package/docs/scripts/polyfill.js +0 -0
  35. package/docs/scripts/prettify/Apache-License-2.0.txt +0 -0
  36. package/docs/scripts/prettify/lang-css.js +0 -0
  37. package/docs/scripts/prettify/prettify.js +0 -0
  38. package/docs/scripts/search.js +0 -0
  39. package/docs/styles/jsdoc.css +0 -0
  40. package/docs/styles/prettify.css +0 -0
  41. package/jest.config.js +0 -0
  42. package/jsdoc.json +0 -0
  43. package/link_mcs.sh +0 -0
  44. package/package.json +1 -1
  45. package/src/contentMetaData.js +0 -0
  46. package/src/filterBuilder.js +7 -5
  47. package/src/index.d.ts +0 -0
  48. package/src/index.js +0 -0
  49. package/src/services/config.js +0 -0
  50. package/src/services/contentLikes.js +0 -0
  51. package/src/services/contentProgress.js +0 -0
  52. package/src/services/dataContext.js +0 -0
  53. package/src/services/lastUpdated.js +0 -0
  54. package/src/services/railcontent.js +0 -0
  55. package/src/services/sanity.js +56 -14
  56. package/src/services/userPermissions.js +0 -0
  57. package/test/contentLikes.test.js +0 -0
  58. package/test/contentProgress.test.js +0 -0
  59. package/test/initializeTests.js +0 -0
  60. package/test/lastUpdated.test.js +0 -0
  61. package/test/live/contentProgressLive.test.js +0 -0
  62. package/test/live/railcontentLive.test.js +0 -0
  63. package/test/localStorageMock.js +0 -0
  64. package/test/log.js +0 -0
  65. package/test/sanityQueryService.test.js +3 -3
  66. package/test/userPermissions.test.js +0 -0
  67. package/tools/generate-index.js +0 -0
File without changes
package/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
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.2.1](https://github.com/railroadmedia/musora-content-services/compare/v1.2.0...v1.2.1) (2025-01-16)
6
+
7
+ ## [1.2.0](https://github.com/railroadmedia/musora-content-services/compare/v1.0.233...v1.2.0) (2025-01-14)
8
+
9
+
10
+ ### Features
11
+
12
+ * add es6 exports ([f16d26f](https://github.com/railroadmedia/musora-content-services/commit/f16d26ffd26cc0febdb4050b5fd33bcd06c78c54))
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * babel blocking tests ([b219af7](https://github.com/railroadmedia/musora-content-services/commit/b219af7097927bd6452af533f9ec0722cbba9c29))
18
+ * jest esm support and security vulnerability ([1aa7f91](https://github.com/railroadmedia/musora-content-services/commit/1aa7f9181247e26f91f038edefd5b3e50b276661))
19
+ * merge conflict ([c7714b9](https://github.com/railroadmedia/musora-content-services/commit/c7714b932585ae0a862370027be57f285878033c))
20
+
5
21
  ### [1.1.3](https://github.com/railroadmedia/musora-content-services/compare/v1.1.2...v1.1.3) (2025-01-08)
6
22
 
7
23
  ### [1.1.2](https://github.com/railroadmedia/musora-content-services/compare/v1.1.1...v1.1.2) (2025-01-08)
package/README.md CHANGED
File without changes
package/babel.config.cjs CHANGED
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
package/docs/index.html CHANGED
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
package/jest.config.js CHANGED
File without changes
package/jsdoc.json CHANGED
File without changes
package/link_mcs.sh CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musora-content-services",
3
- "version": "1.1.3",
3
+ "version": "1.2.1",
4
4
  "description": "A package for Musoras content services ",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
File without changes
@@ -22,7 +22,8 @@ export class FilterBuilder {
22
22
  bypassStatuses = false,
23
23
  bypassPublishedDateRestriction = false,
24
24
  isSingle = false,
25
- allowsPullSongsContent = true
25
+ allowsPullSongsContent = true,
26
+ isChildrenFilter = false
26
27
  } = {}) {
27
28
  this.availableContentStatuses = availableContentStatuses;
28
29
  this.bypassPermissions = bypassPermissions;
@@ -36,6 +37,7 @@ export class FilterBuilder {
36
37
  this.filter = filter;
37
38
  // this.debug = process.env.DEBUG === 'true' || false;
38
39
  this.debug = false;
40
+ this.prefix = isChildrenFilter ? '@->' : '';
39
41
  }
40
42
 
41
43
 
@@ -81,10 +83,10 @@ export class FilterBuilder {
81
83
  const now = new Date().toISOString();
82
84
  let statuses = [...this.availableContentStatuses];
83
85
  statuses.splice(statuses.indexOf(this.STATUS_SCHEDULED), 1);
84
- this._andWhere(`(status in ${arrayToStringRepresentation(statuses)} || (status == '${this.STATUS_SCHEDULED}' && defined(published_on) && published_on >= '${now}'))`)
86
+ this._andWhere(`(${this.prefix}status in ${arrayToStringRepresentation(statuses)} || (${this.prefix}status == '${this.STATUS_SCHEDULED}' && defined(${this.prefix}published_on) && ${this.prefix}published_on >= '${now}'))`)
85
87
 
86
88
  } else {
87
- this._andWhere(`status in ${arrayToStringRepresentation(this.availableContentStatuses)}`);
89
+ this._andWhere(`${this.prefix}status in ${arrayToStringRepresentation(this.availableContentStatuses)}`);
88
90
  }
89
91
  return this;
90
92
  }
@@ -117,9 +119,9 @@ export class FilterBuilder {
117
119
  now = roundedDate.toISOString();
118
120
 
119
121
  if (this.getFutureContentOnly) {
120
- this._andWhere(`published_on >= '${now}'`);
122
+ this._andWhere(`${this.prefix}published_on >= '${now}'`);
121
123
  } else if (!this.pullFutureContent) {
122
- this._andWhere(`published_on <= '${now}'`);
124
+ this._andWhere(`${this.prefix}published_on <= '${now}'`);
123
125
  } else {
124
126
  // const date = new Date();
125
127
  // const theFuture = new Date(date.setMonth(date.getMonth() + 18));
package/src/index.d.ts CHANGED
File without changes
package/src/index.js CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -422,10 +422,29 @@ export async function fetchScheduledReleases(brand, {page = 1, limit = 10}) {
422
422
  * .catch(error => console.error(error));
423
423
  */
424
424
  export async function fetchByRailContentId(id, contentType) {
425
+ const fields = getFieldsForContentType(contentType);
426
+ const childrenFilter = await new FilterBuilder(``, {isChildrenFilter: true} ).buildFilter();
427
+ const entityFieldsString = ` ${fields}
428
+ 'child_count': coalesce(count(child[${childrenFilter}]->), 0) ,
429
+ "lessons": child[${childrenFilter}]->{
430
+ "id": railcontent_id,
431
+ title,
432
+ "image": thumbnail.asset->url,
433
+ "instructors": instructor[]->name,
434
+ length_in_seconds,
435
+ },
436
+ 'length_in_seconds': coalesce(
437
+ math::sum(
438
+ select(
439
+ child[${childrenFilter}]->length_in_seconds
440
+ )
441
+ ),
442
+ length_in_seconds
443
+ ),`;
425
444
 
426
445
  const query = buildRawQuery(
427
446
  `railcontent_id == ${id} && _type == '${contentType}'`,
428
- getFieldsForContentType(contentType),
447
+ entityFieldsString,
429
448
  {
430
449
  isSingle: true,
431
450
  },
@@ -595,6 +614,8 @@ export async function fetchAll(brand, type, {
595
614
  `;
596
615
  filter = `_type == '${groupBy}' && count(*[${lessonsFilterWithRestrictions}]._id) > 0`;
597
616
  } else if (groupBy !== "") {
617
+ const childrenFilter = await new FilterBuilder(``, {isChildrenFilter: true} ).buildFilter();
618
+
598
619
  const webUrlPath = (groupBy == 'genre') ? '/genres' : '';
599
620
  const lessonsFilter = `brand == '${brand}' && ^._id in ${groupBy}[]._ref ${typeFilter} ${searchFilter} ${includedFieldsFilter} ${progressFilter} ${customFilter}`;
600
621
  const lessonsFilterWithRestrictions = await new FilterBuilder(lessonsFilter).buildFilter();
@@ -608,12 +629,23 @@ export async function fetchAll(brand, type, {
608
629
  'all_lessons_count': count(*[${lessonsFilterWithRestrictions}]._id),
609
630
  'lessons': *[${lessonsFilterWithRestrictions}]{
610
631
  ${fieldsString},
632
+ 'lesson_count': coalesce(count(child[${childrenFilter}]->), 0) ,
611
633
  ${groupBy}
612
634
  }[0...20]`;
613
635
  filter = `_type == '${groupBy}' && count(*[${lessonsFilterWithRestrictions}]._id) > 0`;
614
636
  } else {
615
637
  filter = `brand == "${brand}" ${typeFilter} ${searchFilter} ${includedFieldsFilter} ${progressFilter} ${customFilter}`
616
- entityFieldsString = fieldsString;
638
+ const childrenFilter = await new FilterBuilder(``, {isChildrenFilter: true} ).buildFilter();
639
+ entityFieldsString = ` ${fieldsString},
640
+ 'lesson_count': coalesce(count(child[${childrenFilter}]->), 0) ,
641
+ 'length_in_seconds': coalesce(
642
+ math::sum(
643
+ select(
644
+ child[${childrenFilter}]->length_in_seconds
645
+ )
646
+ ),
647
+ length_in_seconds
648
+ ),`;
617
649
  }
618
650
 
619
651
  const filterWithRestrictions = await new FilterBuilder(filter, {
@@ -965,6 +997,8 @@ export async function fetchFoundation(slug) {
965
997
  * @returns {Promise<Object|null>} - The fetched methods data or null if not found.
966
998
  */
967
999
  export async function fetchMethod(brand, slug) {
1000
+ const childrenFilter = await new FilterBuilder(``, {isChildrenFilter: true} ).buildFilter();
1001
+
968
1002
  const query = `*[_type == 'learning-path' && brand == "${brand}" && slug.current == "${slug}"] {
969
1003
  "description": ${descriptionField},
970
1004
  "instructors":instructor[]->name,
@@ -984,7 +1018,7 @@ export async function fetchMethod(brand, slug) {
984
1018
  } | order(length(url)),
985
1019
  "type": _type,
986
1020
  "permission_id": permission[]->railcontent_id,
987
- "levels": child[]->
1021
+ "levels": child[${childrenFilter}]->
988
1022
  {
989
1023
  "id": railcontent_id,
990
1024
  published_on,
@@ -1011,8 +1045,10 @@ export async function fetchMethod(brand, slug) {
1011
1045
  * @returns {Promise<Object|null>} - The fetched next lesson data or null if not found.
1012
1046
  */
1013
1047
  export async function fetchMethodChildren(railcontentId) {
1048
+ const childrenFilter = await new FilterBuilder(``, {isChildrenFilter: true} ).buildFilter();
1049
+
1014
1050
  const query = `*[railcontent_id == ${railcontentId}]{
1015
- child_count,
1051
+ "child_count":coalesce(count(child[${childrenFilter}]->), 0),
1016
1052
  "id": railcontent_id,
1017
1053
  "description": ${descriptionField},
1018
1054
  "thumbnail_url": thumbnail.asset->url,
@@ -1020,12 +1056,13 @@ export async function fetchMethodChildren(railcontentId) {
1020
1056
  xp,
1021
1057
  total_xp,
1022
1058
  parent_content_data,
1059
+ "resources": ${resourcesField},
1023
1060
  "breadcrumbs_data": parent_content_data[] {
1024
1061
  "id": id,
1025
1062
  "title": *[railcontent_id == ^.id][0].title,
1026
1063
  "url": *[railcontent_id == ^.id][0].web_url_path
1027
1064
  } | order(length(url)),
1028
- 'children': child[]->{
1065
+ 'children': child[(${childrenFilter})]->{
1029
1066
  ${getFieldsForContentType('method')}
1030
1067
  },
1031
1068
  }[0..1]`;
@@ -1079,14 +1116,16 @@ export async function fetchMethodPreviousNextLesson(railcontentId, methodId) {
1079
1116
  * @returns {Promise<Array<Object>|null>} - The fetched children data or null if not found.
1080
1117
  */
1081
1118
  export async function fetchMethodChildrenIds(railcontentId) {
1119
+ const childrenFilter = await new FilterBuilder(``, {isChildrenFilter: true} ).buildFilter();
1120
+
1082
1121
  const query = `*[ railcontent_id == ${railcontentId}]{
1083
- 'children': child[]-> {
1122
+ 'children': child[${childrenFilter}]-> {
1084
1123
  'id': railcontent_id,
1085
1124
  'type' : _type,
1086
- 'children': child[]-> {
1125
+ 'children': child[${childrenFilter}]-> {
1087
1126
  'id': railcontent_id,
1088
1127
  'type' : _type,
1089
- 'children': child[]-> {
1128
+ 'children': child[${childrenFilter}]-> {
1090
1129
  'id': railcontent_id,
1091
1130
  'type' : _type,
1092
1131
  }
@@ -1248,11 +1287,12 @@ export async function fetchRelatedLessons(railContentId, brand) {
1248
1287
  const filterSongSameArtist = await new FilterBuilder(`_type=="song" && _type==^._type && brand == "${brand}" && references(^.artist->_id) && railcontent_id !=${railContentId}`).buildFilter();
1249
1288
  const filterSongSameGenre = await new FilterBuilder(`_type=="song" && _type==^._type && brand == "${brand}" && references(^.genre[]->_id) && railcontent_id !=${railContentId}`).buildFilter();
1250
1289
  const filterNeighbouringSiblings = await new FilterBuilder(`references(^._id)`).buildFilter();
1290
+ const childrenFilter = await new FilterBuilder(``, {isChildrenFilter: true} ).buildFilter();
1251
1291
 
1252
1292
  const query = `*[railcontent_id == ${railContentId} && brand == "${brand}" && (!defined(permission) || references(*[_type=='permission']._id))]{
1253
1293
  _type, parent_type, railcontent_id,
1254
1294
  "related_lessons" : array::unique([
1255
- ...(*[${filterNeighbouringSiblings}][0].child[]->{_id, "id":railcontent_id, published_on, "instructor": instructor[0]->name, title, "thumbnail_url":thumbnail.asset->url, length_in_seconds, web_url_path, "type": _type, difficulty, difficulty_string, railcontent_id, artist->,"permission_id": permission[]->railcontent_id,_type}),
1295
+ ...(*[${filterNeighbouringSiblings}][0].child[${childrenFilter}]->{_id, "id":railcontent_id, published_on, "instructor": instructor[0]->name, title, "thumbnail_url":thumbnail.asset->url, length_in_seconds, web_url_path, "type": _type, difficulty, difficulty_string, railcontent_id, artist->,"permission_id": permission[]->railcontent_id,_type}),
1256
1296
  ...(*[${filterSongSameArtist}]{_id, "id":railcontent_id, published_on, "instructor": instructor[0]->name, title, "thumbnail_url":thumbnail.asset->url, length_in_seconds, web_url_path, "type": _type, difficulty, difficulty_string, railcontent_id, artist->,"permission_id": permission[]->railcontent_id,_type}|order(published_on desc, title asc)[0...10]),
1257
1297
  ...(*[${filterSongSameGenre}]{_id, "id":railcontent_id, published_on, "instructor": instructor[0]->name, title, "thumbnail_url":thumbnail.asset->url, length_in_seconds, web_url_path, "type": _type, difficulty, difficulty_string, railcontent_id, artist->,"permission_id": permission[]->railcontent_id,_type}|order(published_on desc, title asc)[0...10]),
1258
1298
  ...(*[${filterSameTypeAndSortOrder}]{_id, "id":railcontent_id, published_on, "instructor": instructor[0]->name, title, "thumbnail_url":thumbnail.asset->url, length_in_seconds, web_url_path, "type": _type, difficulty, difficulty_string, railcontent_id, artist->,"permission_id": permission[]->railcontent_id,_type, sort}|order(sort asc, title asc)[0...10]),
@@ -1456,10 +1496,11 @@ export async function fetchCoachLessons(brand, id, {
1456
1496
  ? filtersToGroq(includedFields)
1457
1497
  : "";
1458
1498
  const filter = `brand == '${brand}' ${searchFilter} ${includedFieldsFilter} && references(*[_type=='instructor' && railcontent_id == ${id}]._id)`;
1499
+ const filterWithRestrictions = await new FilterBuilder(filter).buildFilter();
1459
1500
 
1460
1501
  sortOrder = getSortOrder(sortOrder, brand);
1461
1502
  const query = buildEntityAndTotalQuery(
1462
- filter,
1503
+ filterWithRestrictions,
1463
1504
  fieldsString,
1464
1505
  {
1465
1506
  sortOrder: sortOrder,
@@ -1682,19 +1723,20 @@ export async function fetchTopLevelParentId(railcontentId) {
1682
1723
 
1683
1724
  export async function fetchHierarchy(railcontentId) {
1684
1725
  let topLevelId = await fetchTopLevelParentId(railcontentId);
1726
+ const childrenFilter = await new FilterBuilder(``, {isChildrenFilter: true} ).buildFilter();
1685
1727
  const query = `*[railcontent_id == ${topLevelId}]{
1686
1728
  railcontent_id,
1687
1729
  'assignments': assignment[]{railcontent_id},
1688
- 'children': child[]->{
1730
+ 'children': child[${childrenFilter}]->{
1689
1731
  railcontent_id,
1690
1732
  'assignments': assignment[]{railcontent_id},
1691
- 'children': child[]->{
1733
+ 'children': child[${childrenFilter}]->{
1692
1734
  railcontent_id,
1693
1735
  'assignments': assignment[]{railcontent_id},
1694
- 'children': child[]->{
1736
+ 'children': child[${childrenFilter}]->{
1695
1737
  railcontent_id,
1696
1738
  'assignments': assignment[]{railcontent_id},
1697
- 'children': child[]->{
1739
+ 'children': child[${childrenFilter}]->{
1698
1740
  railcontent_id,
1699
1741
  }
1700
1742
  }
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/test/log.js CHANGED
File without changes
@@ -607,12 +607,12 @@ describe('Sanity Queries', function () {
607
607
  });
608
608
 
609
609
  test('fetchMethodNextPreviousLesson-Last', async () => {
610
- const id = 396234;
611
- const methodId = 396229;
610
+ const id = 260171;
611
+ const methodId = 259060;
612
612
  const response = await fetchMethodPreviousNextLesson(id, methodId);
613
613
  log(response);
614
614
  expect(response.prevLesson).toBeDefined();
615
- expect(response.prevLesson.id).toBe(396233);
615
+ expect(response.prevLesson.id).toBe(260170);
616
616
  expect(response.prevLesson.type).toBe('course-part');
617
617
  expect(response.nextLesson).not.toBeDefined();
618
618
  });
File without changes
File without changes