musora-content-services 1.0.178 → 1.0.180
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 +6 -7
- package/src/filterBuilder.js +22 -2
- package/src/services/sanity.js +5 -2
- package/test/sanityQueryService.test.js +16 -12
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.180](https://github.com/railroadmedia/musora-content-services/compare/v1.0.179...v1.0.180) (2024-11-23)
|
|
6
|
+
|
|
7
|
+
### [1.0.179](https://github.com/railroadmedia/musora-content-services/compare/v1.0.178...v1.0.179) (2024-11-19)
|
|
8
|
+
|
|
5
9
|
### [1.0.178](https://github.com/railroadmedia/musora-content-services/compare/v1.0.177...v1.0.178) (2024-11-19)
|
|
6
10
|
|
|
7
11
|
### [1.0.177](https://github.com/railroadmedia/musora-content-services/compare/v1.0.176...v1.0.177) (2024-11-18)
|
package/package.json
CHANGED
package/src/contentTypeConfig.js
CHANGED
|
@@ -10,6 +10,7 @@ const DEFAULT_FIELDS = [
|
|
|
10
10
|
"difficulty",
|
|
11
11
|
"difficulty_string",
|
|
12
12
|
"web_url_path",
|
|
13
|
+
"'url': web_url_path",
|
|
13
14
|
"published_on",
|
|
14
15
|
"'type': _type",
|
|
15
16
|
"progress_percent",
|
|
@@ -86,7 +87,7 @@ let contentTypeConfig = {
|
|
|
86
87
|
'fields': [
|
|
87
88
|
'enrollment_start_time',
|
|
88
89
|
'enrollment_end_time',
|
|
89
|
-
'registration_url',
|
|
90
|
+
"'registration_url': '/' + brand + '/enrollment/' + slug.current",
|
|
90
91
|
'"lesson_count": child_count',
|
|
91
92
|
'"primary_cta_text": select(dateTime(published_on) > dateTime(now()) && dateTime(enrollment_start_time) > dateTime(now()) => "Notify Me", "Start Challenge")',
|
|
92
93
|
'challenge_state',
|
|
@@ -103,7 +104,7 @@ let contentTypeConfig = {
|
|
|
103
104
|
'"gold_award": gold_award.asset->url',
|
|
104
105
|
'"silver_award": silver_award.asset->url',
|
|
105
106
|
'"bronze_award": bronze_award.asset->url',
|
|
106
|
-
'
|
|
107
|
+
'is_solo',
|
|
107
108
|
`"lessons": child[]->{
|
|
108
109
|
"id": railcontent_id,
|
|
109
110
|
title,
|
|
@@ -196,7 +197,6 @@ let contentTypeConfig = {
|
|
|
196
197
|
'title',
|
|
197
198
|
'total_xp',
|
|
198
199
|
'"type": _type',
|
|
199
|
-
'"url": web_url_path',
|
|
200
200
|
'xp',
|
|
201
201
|
]
|
|
202
202
|
},
|
|
@@ -316,7 +316,6 @@ let contentTypeConfig = {
|
|
|
316
316
|
title,
|
|
317
317
|
"type": _type,
|
|
318
318
|
"description": ${descriptionField},
|
|
319
|
-
"url": web_url_path,
|
|
320
319
|
xp,
|
|
321
320
|
}`
|
|
322
321
|
]
|
|
@@ -345,12 +344,12 @@ let contentTypeConfig = {
|
|
|
345
344
|
'"coach_card_image": coach_card_image.asset->url',
|
|
346
345
|
'"coach_featured_image": coach_featured_image.asset->url',
|
|
347
346
|
'"coach_top_banner_image": coach_top_banner_image.asset->url',
|
|
348
|
-
'focus',
|
|
347
|
+
'"focus": focus[]->name',
|
|
349
348
|
'focus_text',
|
|
350
349
|
'forum_thread_id',
|
|
351
|
-
'long_bio',
|
|
350
|
+
'"long_bio": long_bio[0].children[0].text,',
|
|
352
351
|
'name',
|
|
353
|
-
'short_bio',
|
|
352
|
+
'"short_bio" : short_bio[0].children[0].text',
|
|
354
353
|
'bands',
|
|
355
354
|
'endorsements',
|
|
356
355
|
]
|
package/src/filterBuilder.js
CHANGED
|
@@ -4,6 +4,10 @@ import {fetchUserPermissions} from "./services/userPermissions";
|
|
|
4
4
|
export class FilterBuilder {
|
|
5
5
|
|
|
6
6
|
STATUS_SCHEDULED = 'scheduled';
|
|
7
|
+
STATUS_PUBLISHED = 'published';
|
|
8
|
+
STATUS_DRAFT = 'draft';
|
|
9
|
+
STATUS_ARCHIVED = 'archived';
|
|
10
|
+
STATUS_UNLISTED = 'unlisted';
|
|
7
11
|
|
|
8
12
|
constructor(
|
|
9
13
|
filter = '',
|
|
@@ -13,13 +17,18 @@ export class FilterBuilder {
|
|
|
13
17
|
pullFutureContent = false,
|
|
14
18
|
getFutureContentOnly = false,
|
|
15
19
|
getFutureScheduledContentsOnly = false,
|
|
16
|
-
|
|
20
|
+
bypassStatuses = false,
|
|
21
|
+
bypassPublishedDateRestriction = false,
|
|
22
|
+
isSingle = false
|
|
17
23
|
} = {}) {
|
|
18
24
|
this.availableContentStatuses = availableContentStatuses;
|
|
19
25
|
this.bypassPermissions = bypassPermissions;
|
|
26
|
+
this.bypassStatuses = bypassStatuses;
|
|
27
|
+
this.bypassPublishedDateRestriction = bypassPublishedDateRestriction;
|
|
20
28
|
this.pullFutureContent = pullFutureContent;
|
|
21
29
|
this.getFutureContentOnly = getFutureContentOnly;
|
|
22
30
|
this.getFutureScheduledContentsOnly = getFutureScheduledContentsOnly;
|
|
31
|
+
this.isSingle = isSingle;
|
|
23
32
|
this.filter = filter;
|
|
24
33
|
// this.debug = process.env.DEBUG === 'true' || false;
|
|
25
34
|
this.debug = false;
|
|
@@ -48,7 +57,17 @@ export class FilterBuilder {
|
|
|
48
57
|
|
|
49
58
|
_applyContentStatuses() {
|
|
50
59
|
// This must be run before _applyPublishDateRestrictions()
|
|
51
|
-
if
|
|
60
|
+
if(this.bypassStatuses) return this;
|
|
61
|
+
if (this.availableContentStatuses.length === 0) {
|
|
62
|
+
if (this.userData.isAdmin) {
|
|
63
|
+
this.availableContentStatuses = [this.STATUS_DRAFT, this.STATUS_SCHEDULED, this.STATUS_PUBLISHED, this.STATUS_ARCHIVED, this.STATUS_UNLISTED];
|
|
64
|
+
} else if(this.isSingle){
|
|
65
|
+
this.availableContentStatuses = [this.STATUS_SCHEDULED, this.STATUS_PUBLISHED, this.STATUS_UNLISTED, this.STATUS_ARCHIVED];
|
|
66
|
+
} else{
|
|
67
|
+
this.availableContentStatuses = [this.STATUS_SCHEDULED, this.STATUS_PUBLISHED];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
52
71
|
// I'm not sure if I'm 100% on this logic, but this is my intepretation of the ContentRepository logic
|
|
53
72
|
if (this.getFutureScheduledContentsOnly && this.availableContentStatuses.includes(this.STATUS_SCHEDULED)) {
|
|
54
73
|
// we must pull in future content here, otherwise we'll restrict on content this is published in the past and remove any scheduled content
|
|
@@ -77,6 +96,7 @@ export class FilterBuilder {
|
|
|
77
96
|
}
|
|
78
97
|
|
|
79
98
|
_applyPublishingDateRestrictions() {
|
|
99
|
+
if(this.bypassPublishedDateRestriction) return this;
|
|
80
100
|
const now = new Date().toISOString();
|
|
81
101
|
if (this.getFutureContentOnly) {
|
|
82
102
|
this._andWhere(`published_on >= '${now}'`);
|
package/src/services/sanity.js
CHANGED
|
@@ -510,6 +510,7 @@ export async function fetchAll(brand, type, {
|
|
|
510
510
|
let webUrlPathType = config?.slug ?? type;
|
|
511
511
|
const start = (page - 1) * limit;
|
|
512
512
|
const end = start + limit;
|
|
513
|
+
let bypassStatusAndPublishedValidation = (type == 'instructor');
|
|
513
514
|
|
|
514
515
|
// Construct the type filter
|
|
515
516
|
const typeFilter = type ? `&& _type == '${type}'` : "";
|
|
@@ -574,8 +575,10 @@ export async function fetchAll(brand, type, {
|
|
|
574
575
|
filter = `brand == "${brand}" ${typeFilter} ${searchFilter} ${includedFieldsFilter} ${progressFilter}`
|
|
575
576
|
entityFieldsString = fieldsString;
|
|
576
577
|
}
|
|
578
|
+
|
|
579
|
+
const filterWithRestrictions = await new FilterBuilder(filter,{bypassStatuses:bypassStatusAndPublishedValidation, bypassPermissions: bypassStatusAndPublishedValidation, bypassPublishedDateRestriction: bypassStatusAndPublishedValidation} ).buildFilter();
|
|
577
580
|
query = buildEntityAndTotalQuery(
|
|
578
|
-
|
|
581
|
+
filterWithRestrictions,
|
|
579
582
|
entityFieldsString,
|
|
580
583
|
{
|
|
581
584
|
sortOrder: sortOrder,
|
|
@@ -990,7 +993,7 @@ export async function fetchNextPreviousLesson(railcontentId) {
|
|
|
990
993
|
* .catch(error => console.error(error));
|
|
991
994
|
*/
|
|
992
995
|
export async function fetchLessonContent(railContentId) {
|
|
993
|
-
const filterParams = {};
|
|
996
|
+
const filterParams = {isSingle:true};
|
|
994
997
|
// Format changes made to the `fields` object may also need to be reflected in Musora-web-platform SanityGateway.php $fields object
|
|
995
998
|
// Currently only for challenges and challenge lessons
|
|
996
999
|
// If you're unsure, message Adrian, or just add them.
|
|
@@ -630,13 +630,16 @@ describe('Filter Builder', function () {
|
|
|
630
630
|
let finalFilter = await builder.buildFilter(filter);
|
|
631
631
|
let clauses = spliceFilterForAnds(finalFilter);
|
|
632
632
|
expect(clauses[0].phrase).toBe(filter);
|
|
633
|
-
expect(clauses[1].field).toBe('
|
|
633
|
+
expect(clauses[1].field).toBe('status');
|
|
634
|
+
expect(clauses[2].field).toBe('published_on');
|
|
634
635
|
|
|
635
636
|
builder = new FilterBuilder('', {bypassPermissions: true});
|
|
636
637
|
finalFilter = await builder.buildFilter(filter);
|
|
637
638
|
clauses = spliceFilterForAnds(finalFilter);
|
|
638
|
-
expect(clauses[0].field).toBe('
|
|
639
|
-
expect(clauses[0].operator).toBe('
|
|
639
|
+
expect(clauses[0].field).toBe('status');
|
|
640
|
+
expect(clauses[0].operator).toBe('in');
|
|
641
|
+
expect(clauses[1].field).toBe('published_on');
|
|
642
|
+
expect(clauses[1].operator).toBe('<=');
|
|
640
643
|
});
|
|
641
644
|
|
|
642
645
|
test('withOnlyFilterAvailableStatuses', async () => {
|
|
@@ -665,8 +668,6 @@ describe('Filter Builder', function () {
|
|
|
665
668
|
expect(clauses[1].operator).toBe('in');
|
|
666
669
|
// getFutureScheduledContentsOnly doesn't make a filter that's splicable, so we match on the more static string
|
|
667
670
|
const expected = "['published','unlisted'] || (status == 'scheduled' && published_on >=";
|
|
668
|
-
console.log(clauses[1].condition);
|
|
669
|
-
console.log(expected)
|
|
670
671
|
const isMatch = finalFilter.includes(expected);
|
|
671
672
|
expect(isMatch).toBeTruthy();
|
|
672
673
|
});
|
|
@@ -701,7 +702,9 @@ describe('Filter Builder', function () {
|
|
|
701
702
|
expect(isMatch).toBeFalsy();
|
|
702
703
|
const clauses = spliceFilterForAnds(finalFilter);
|
|
703
704
|
expect(clauses[0].field).toBe('railcontent_id');
|
|
704
|
-
expect(clauses[1].field).toBe('
|
|
705
|
+
expect(clauses[1].field).toBe('status');
|
|
706
|
+
expect(clauses[2].field).toBe('published_on');
|
|
707
|
+
|
|
705
708
|
});
|
|
706
709
|
|
|
707
710
|
|
|
@@ -717,10 +720,11 @@ describe('Filter Builder', function () {
|
|
|
717
720
|
let finalFilter = await builder.buildFilter();
|
|
718
721
|
let clauses = spliceFilterForAnds(finalFilter);
|
|
719
722
|
expect(clauses[0].phrase).toBe(filter);
|
|
720
|
-
|
|
721
|
-
expect(clauses[1].
|
|
722
|
-
expect(clauses[
|
|
723
|
-
|
|
723
|
+
expect(clauses[1].field).toBe('status');
|
|
724
|
+
expect(clauses[1].operator).toBe('in');
|
|
725
|
+
expect(clauses[2].field).toBe('published_on');
|
|
726
|
+
expect(clauses[2].operator).toBe('<=');
|
|
727
|
+
const restrictionDate = new Date(clauses[2].condition)
|
|
724
728
|
const now = new Date();
|
|
725
729
|
expect(now.getTime()).toBeLessThan(restrictionDate.getTime());
|
|
726
730
|
|
|
@@ -732,8 +736,8 @@ describe('Filter Builder', function () {
|
|
|
732
736
|
finalFilter = await builder.buildFilter();
|
|
733
737
|
clauses = spliceFilterForAnds(finalFilter);
|
|
734
738
|
expect(clauses[0].phrase).toBe(filter);
|
|
735
|
-
expect(clauses[
|
|
736
|
-
expect(clauses[
|
|
739
|
+
expect(clauses[2].field).toBe('published_on');
|
|
740
|
+
expect(clauses[2].operator).toBe('>=');
|
|
737
741
|
});
|
|
738
742
|
|
|
739
743
|
function spliceFilterForAnds(filter) {
|