musora-content-services 1.3.19 → 2.0.2
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/.editorconfig +16 -0
- package/CHANGELOG.md +1 -3
- package/docs/config.js.html +14 -5
- package/docs/content.js.html +425 -0
- package/docs/global.html +3026 -0
- package/docs/index.html +2 -2
- package/docs/module-Config.html +60 -7
- package/docs/module-Content-Services-V2.html +2433 -0
- package/docs/module-Railcontent-Services.html +522 -2
- package/docs/module-Sanity-Services.html +57 -43
- package/docs/module-Session-Management.html +575 -0
- package/docs/module-User-Permissions.html +406 -0
- package/docs/railcontent.js.html +42 -5
- package/docs/sanity.js.html +290 -103
- package/docs/user_permissions.js.html +110 -0
- package/docs/user_sessions.js.html +139 -0
- package/docs/user_types.js.html +188 -0
- package/jsdoc.json +2 -0
- package/package.json +1 -1
- package/publish.sh +2 -2
- package/src/contentMetaData.js +307 -1088
- package/src/contentTypeConfig.js +108 -4
- package/src/filterBuilder.js +6 -6
- package/src/index.d.ts +61 -6
- package/src/index.js +61 -6
- package/src/{services → lib}/lastUpdated.js +17 -1
- package/src/services/config.js +0 -0
- package/src/services/content.js +371 -0
- package/src/services/dataContext.js +0 -0
- package/src/services/forum.js +57 -0
- package/src/services/railcontent.js +124 -11
- package/src/services/recommendations.js +19 -0
- package/src/services/sanity.js +278 -104
- package/src/services/{userPermissions.js → user/permissions.js} +16 -2
- package/src/services/user/sessions.js +67 -0
- package/src/services/user/types.js +116 -0
- package/src/services/userActivity.js +32 -0
- package/test/content.test.js +116 -0
- package/test/contentLikes.test.js +0 -0
- package/test/contentProgress.test.js +83 -5
- package/test/forum.test.js +18 -0
- package/test/initializeTests.js +6 -1
- package/test/{lastUpdated.test.js → lib/lastUpdated.test.js} +2 -5
- package/test/sanityQueryService.test.js +66 -18
- package/test/{userPermissions.test.js → user/permissions.test.js} +3 -3
- package/tools/generate-index.cjs +16 -3
package/src/services/sanity.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import {
|
|
5
5
|
artistOrInstructorName,
|
|
6
|
-
artistOrInstructorNameAsArray,
|
|
7
6
|
assignmentsField,
|
|
8
7
|
descriptionField,
|
|
9
8
|
resourcesField,
|
|
@@ -23,14 +22,13 @@ import { processMetadata, typeWithSortOrder } from '../contentMetaData.js'
|
|
|
23
22
|
import { globalConfig } from './config.js'
|
|
24
23
|
|
|
25
24
|
import {
|
|
26
|
-
fetchAllCompletedStates,
|
|
27
25
|
fetchCompletedChallenges,
|
|
28
26
|
fetchOwnedChallenges,
|
|
29
27
|
fetchNextContentDataForParent,
|
|
30
28
|
fetchHandler,
|
|
31
29
|
} from './railcontent.js'
|
|
32
30
|
import { arrayToStringRepresentation, FilterBuilder } from '../filterBuilder.js'
|
|
33
|
-
import { fetchUserPermissions } from './
|
|
31
|
+
import { fetchUserPermissions } from './user/permissions.js'
|
|
34
32
|
import { getAllCompleted, getAllStarted, getAllStartedOrCompleted } from './contentProgress.js'
|
|
35
33
|
|
|
36
34
|
/**
|
|
@@ -73,15 +71,22 @@ export async function fetchSongById(documentId) {
|
|
|
73
71
|
* @number contentPerPage
|
|
74
72
|
* @returns {Promise<Object|null>}
|
|
75
73
|
*/
|
|
76
|
-
export async function fetchLeaving(
|
|
77
|
-
|
|
78
|
-
{ pageNumber = 1, contentPerPage = 20 } = {}) {
|
|
79
|
-
const nextQuarter = getNextAndPreviousQuarterDates()['next'];
|
|
74
|
+
export async function fetchLeaving(brand, { pageNumber = 1, contentPerPage = 20 } = {}) {
|
|
75
|
+
const nextQuarter = getNextAndPreviousQuarterDates()['next']
|
|
80
76
|
const filterString = `brand == '${brand}' && quarter_removed == '${nextQuarter}'`
|
|
81
|
-
const startEndOrder = getQueryFromPage(pageNumber, contentPerPage)
|
|
82
|
-
const sortOrder = {
|
|
83
|
-
|
|
84
|
-
|
|
77
|
+
const startEndOrder = getQueryFromPage(pageNumber, contentPerPage)
|
|
78
|
+
const sortOrder = {
|
|
79
|
+
sortOrder: 'published_on desc, id desc',
|
|
80
|
+
start: startEndOrder['start'],
|
|
81
|
+
end: startEndOrder['end'],
|
|
82
|
+
}
|
|
83
|
+
const query = await buildQuery(
|
|
84
|
+
filterString,
|
|
85
|
+
{ pullFutureContent: false, availableContentStatuses: ['published'] },
|
|
86
|
+
getFieldsForContentType(),
|
|
87
|
+
sortOrder
|
|
88
|
+
)
|
|
89
|
+
return fetchSanity(query, true)
|
|
85
90
|
}
|
|
86
91
|
|
|
87
92
|
/**
|
|
@@ -92,16 +97,23 @@ export async function fetchLeaving(
|
|
|
92
97
|
* @number contentPerPage
|
|
93
98
|
* @returns {Promise<Object|null>}
|
|
94
99
|
*/
|
|
95
|
-
export async function fetchReturning(
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
100
|
+
export async function fetchReturning(brand, { pageNumber = 1, contentPerPage = 20 } = {}) {
|
|
101
|
+
const nextQuarter = getNextAndPreviousQuarterDates()['next']
|
|
102
|
+
const filterString = `brand == '${brand}' && quarter_published == '${nextQuarter}'`
|
|
103
|
+
const startEndOrder = getQueryFromPage(pageNumber, contentPerPage)
|
|
104
|
+
const sortOrder = {
|
|
105
|
+
sortOrder: 'published_on desc, id desc',
|
|
106
|
+
start: startEndOrder['start'],
|
|
107
|
+
end: startEndOrder['end'],
|
|
108
|
+
}
|
|
109
|
+
const query = await buildQuery(
|
|
110
|
+
filterString,
|
|
111
|
+
{ pullFutureContent: true, availableContentStatuses: ['draft'] },
|
|
112
|
+
getFieldsForContentType('returning'),
|
|
113
|
+
sortOrder
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
return fetchSanity(query, true)
|
|
105
117
|
}
|
|
106
118
|
|
|
107
119
|
/**
|
|
@@ -112,14 +124,21 @@ export async function fetchReturning(
|
|
|
112
124
|
* @number contentPerPage
|
|
113
125
|
* @returns {Promise<Object|null>}
|
|
114
126
|
*/
|
|
115
|
-
export async function fetchComingSoon(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
127
|
+
export async function fetchComingSoon(brand, { pageNumber = 1, contentPerPage = 20 } = {}) {
|
|
128
|
+
const filterString = `brand == '${brand}' && _type == 'song'`
|
|
129
|
+
const startEndOrder = getQueryFromPage(pageNumber, contentPerPage)
|
|
130
|
+
const sortOrder = {
|
|
131
|
+
sortOrder: 'published_on desc, id desc',
|
|
132
|
+
start: startEndOrder['start'],
|
|
133
|
+
end: startEndOrder['end'],
|
|
134
|
+
}
|
|
135
|
+
const query = await buildQuery(
|
|
136
|
+
filterString,
|
|
137
|
+
{ getFutureContentOnly: true },
|
|
138
|
+
getFieldsForContentType(),
|
|
139
|
+
sortOrder
|
|
140
|
+
)
|
|
141
|
+
return fetchSanity(query, true)
|
|
123
142
|
}
|
|
124
143
|
|
|
125
144
|
/**
|
|
@@ -128,47 +147,47 @@ export async function fetchComingSoon(
|
|
|
128
147
|
* @returns {number[]}
|
|
129
148
|
*/
|
|
130
149
|
function getQueryFromPage(pageNumber, contentPerPage) {
|
|
131
|
-
const start = contentPerPage*(pageNumber-1)
|
|
132
|
-
const end = contentPerPage*pageNumber
|
|
133
|
-
let result = []
|
|
134
|
-
result['start'] = start
|
|
135
|
-
result['end'] = end
|
|
136
|
-
return result
|
|
150
|
+
const start = contentPerPage * (pageNumber - 1)
|
|
151
|
+
const end = contentPerPage * pageNumber
|
|
152
|
+
let result = []
|
|
153
|
+
result['start'] = start
|
|
154
|
+
result['end'] = end
|
|
155
|
+
return result
|
|
137
156
|
}
|
|
138
157
|
|
|
139
158
|
/**
|
|
140
159
|
* returns array of next and previous quarter dates as strings
|
|
141
160
|
*
|
|
142
|
-
* @returns {
|
|
161
|
+
* @returns {string[]}
|
|
143
162
|
*/
|
|
144
163
|
function getNextAndPreviousQuarterDates() {
|
|
145
|
-
const january = 1
|
|
146
|
-
const april = 4
|
|
147
|
-
const july = 7
|
|
148
|
-
const october = 10
|
|
149
|
-
const month = new Date().getMonth()
|
|
150
|
-
let year = new Date().getFullYear()
|
|
151
|
-
let nextQuarter = ''
|
|
152
|
-
let prevQuarter = ''
|
|
164
|
+
const january = 1
|
|
165
|
+
const april = 4
|
|
166
|
+
const july = 7
|
|
167
|
+
const october = 10
|
|
168
|
+
const month = new Date().getMonth()
|
|
169
|
+
let year = new Date().getFullYear()
|
|
170
|
+
let nextQuarter = ''
|
|
171
|
+
let prevQuarter = ''
|
|
153
172
|
if (month < april) {
|
|
154
|
-
nextQuarter = `${year}-0${april}-01
|
|
155
|
-
prevQuarter = `${year}-0${january}-01
|
|
173
|
+
nextQuarter = `${year}-0${april}-01`
|
|
174
|
+
prevQuarter = `${year}-0${january}-01`
|
|
156
175
|
} else if (month < july) {
|
|
157
|
-
nextQuarter = `${year}-0${july}-01
|
|
158
|
-
prevQuarter = `${year}-0${april}-01
|
|
176
|
+
nextQuarter = `${year}-0${july}-01`
|
|
177
|
+
prevQuarter = `${year}-0${april}-01`
|
|
159
178
|
} else if (month < october) {
|
|
160
|
-
nextQuarter = `${year}-${october}-01
|
|
161
|
-
prevQuarter = `${year}-0${july}-01
|
|
179
|
+
nextQuarter = `${year}-${october}-01`
|
|
180
|
+
prevQuarter = `${year}-0${july}-01`
|
|
162
181
|
} else {
|
|
163
|
-
prevQuarter = `${year}-${october}-01
|
|
164
|
-
year
|
|
165
|
-
nextQuarter = `${year}-0${january}-01
|
|
182
|
+
prevQuarter = `${year}-${october}-01`
|
|
183
|
+
year++
|
|
184
|
+
nextQuarter = `${year}-0${january}-01`
|
|
166
185
|
}
|
|
167
186
|
|
|
168
|
-
let result = []
|
|
169
|
-
result['next'] = nextQuarter
|
|
170
|
-
result['previous'] = prevQuarter
|
|
171
|
-
return result
|
|
187
|
+
let result = []
|
|
188
|
+
result['next'] = nextQuarter
|
|
189
|
+
result['previous'] = prevQuarter
|
|
190
|
+
return result
|
|
172
191
|
}
|
|
173
192
|
|
|
174
193
|
/**
|
|
@@ -202,8 +221,8 @@ export async function fetchArtists(brand) {
|
|
|
202
221
|
*/
|
|
203
222
|
export async function fetchSongArtistCount(brand) {
|
|
204
223
|
const filter = await new FilterBuilder(
|
|
205
|
-
|
|
206
|
-
|
|
224
|
+
`_type == "song" && brand == "${brand}" && references(^._id)`,
|
|
225
|
+
{ bypassPermissions: true }
|
|
207
226
|
).buildFilter()
|
|
208
227
|
const query = `
|
|
209
228
|
count(*[_type == "artist"]{
|
|
@@ -213,13 +232,13 @@ export async function fetchSongArtistCount(brand) {
|
|
|
213
232
|
return fetchSanity(query, true, { processNeedAccess: false })
|
|
214
233
|
}
|
|
215
234
|
|
|
216
|
-
export async function fetchPlayAlongsCount(
|
|
217
|
-
|
|
218
|
-
includedFields,
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
222
|
-
|
|
235
|
+
export async function fetchPlayAlongsCount(
|
|
236
|
+
brand,
|
|
237
|
+
{ searchTerm, includedFields, progressIds, progress }
|
|
238
|
+
) {
|
|
239
|
+
const searchFilter = searchTerm
|
|
240
|
+
? `&& (artist->name match "${searchTerm}*" || instructor[]->name match "${searchTerm}*" || title match "${searchTerm}*" || name match "${searchTerm}*")`
|
|
241
|
+
: ''
|
|
223
242
|
|
|
224
243
|
// Construct the included fields filter, replacing 'difficulty' with 'difficulty_string'
|
|
225
244
|
const includedFieldsFilter = includedFields.length > 0 ? filtersToGroq(includedFields) : ''
|
|
@@ -327,7 +346,8 @@ export async function fetchNewReleases(
|
|
|
327
346
|
const start = (page - 1) * limit
|
|
328
347
|
const end = start + limit
|
|
329
348
|
const sortOrder = getSortOrder(sort, brand)
|
|
330
|
-
const
|
|
349
|
+
const nextQuarter = getNextAndPreviousQuarterDates()['next']
|
|
350
|
+
const filter = `_type in ${typesString} && brand == '${brand}' && show_in_new_feed == true && (!defined(quarter_published) || quarter_published != '${nextQuarter}')`
|
|
331
351
|
const fields = `
|
|
332
352
|
"id": railcontent_id,
|
|
333
353
|
title,
|
|
@@ -489,7 +509,7 @@ export async function fetchByRailContentId(id, contentType) {
|
|
|
489
509
|
*/
|
|
490
510
|
export async function fetchByRailContentIds(ids, contentType = undefined) {
|
|
491
511
|
if (!ids) {
|
|
492
|
-
return []
|
|
512
|
+
return []
|
|
493
513
|
}
|
|
494
514
|
const idsString = ids.join(',')
|
|
495
515
|
|
|
@@ -590,6 +610,8 @@ export async function fetchAll(
|
|
|
590
610
|
if (type === 'archives') {
|
|
591
611
|
typeFilter = `&& status == "archived"`
|
|
592
612
|
bypassStatusAndPublishedValidation = true
|
|
613
|
+
} else if (type === 'lessons' || type === 'songs') {
|
|
614
|
+
typeFilter = ``
|
|
593
615
|
} else if (type === 'pack') {
|
|
594
616
|
typeFilter = `&& (_type == 'pack' || _type == 'semester-pack')`
|
|
595
617
|
} else {
|
|
@@ -857,6 +879,14 @@ async function getProgressFilter(progress, progressIds) {
|
|
|
857
879
|
const ids = await getAllStartedOrCompleted()
|
|
858
880
|
return `&& !(railcontent_id in [${ids.join(',')}])`
|
|
859
881
|
}
|
|
882
|
+
case 'recent': {
|
|
883
|
+
const ids = await getAllStartedOrCompleted()
|
|
884
|
+
return `&& (railcontent_id in [${ids.join(',')}])`
|
|
885
|
+
}
|
|
886
|
+
case 'incomplete': {
|
|
887
|
+
const ids = await getAllStarted()
|
|
888
|
+
return `&& railcontent_id in [${ids.join(',')}]`
|
|
889
|
+
}
|
|
860
890
|
default:
|
|
861
891
|
throw new Error(`'${progress}' progress option not implemented`)
|
|
862
892
|
}
|
|
@@ -932,6 +962,13 @@ export async function fetchAllFilterOptions(
|
|
|
932
962
|
coachId,
|
|
933
963
|
includeTabs = false
|
|
934
964
|
) {
|
|
965
|
+
if (contentType == 'lessons' || contentType == 'songs') {
|
|
966
|
+
const metaData = processMetadata(brand, contentType, true)
|
|
967
|
+
return {
|
|
968
|
+
meta: metaData,
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
|
|
935
972
|
if (coachId && contentType !== 'coach-lessons') {
|
|
936
973
|
throw new Error(
|
|
937
974
|
`Invalid contentType: '${contentType}' for coachId. It must be 'coach-lessons'.`
|
|
@@ -1210,26 +1247,26 @@ export async function fetchLessonContent(railContentId) {
|
|
|
1210
1247
|
// Format changes made to the `fields` object may also need to be reflected in Musora-web-platform SanityGateway.php $fields object
|
|
1211
1248
|
// Currently only for challenges and challenge lessons
|
|
1212
1249
|
// If you're unsure, message Adrian, or just add them.
|
|
1213
|
-
const fields = `title,
|
|
1250
|
+
const fields = `title,
|
|
1214
1251
|
published_on,
|
|
1215
|
-
"type":_type,
|
|
1252
|
+
"type":_type,
|
|
1216
1253
|
"resources": ${resourcesField},
|
|
1217
|
-
difficulty,
|
|
1218
|
-
difficulty_string,
|
|
1219
|
-
brand,
|
|
1254
|
+
difficulty,
|
|
1255
|
+
difficulty_string,
|
|
1256
|
+
brand,
|
|
1220
1257
|
status,
|
|
1221
|
-
soundslice,
|
|
1222
|
-
instrumentless,
|
|
1223
|
-
railcontent_id,
|
|
1224
|
-
"id":railcontent_id,
|
|
1258
|
+
soundslice,
|
|
1259
|
+
instrumentless,
|
|
1260
|
+
railcontent_id,
|
|
1261
|
+
"id":railcontent_id,
|
|
1225
1262
|
slug, artist->,
|
|
1226
|
-
"thumbnail_url":thumbnail.asset->url,
|
|
1227
|
-
"url": web_url_path,
|
|
1263
|
+
"thumbnail_url":thumbnail.asset->url,
|
|
1264
|
+
"url": web_url_path,
|
|
1228
1265
|
soundslice_slug,
|
|
1229
1266
|
"description": description[0].children[0].text,
|
|
1230
1267
|
"chapters": chapter[]{
|
|
1231
1268
|
chapter_description,
|
|
1232
|
-
chapter_timecode,
|
|
1269
|
+
chapter_timecode,
|
|
1233
1270
|
"chapter_thumbnail_url": chapter_thumbnail_url.asset->url
|
|
1234
1271
|
},
|
|
1235
1272
|
"instructors":instructor[]->name,
|
|
@@ -1237,7 +1274,7 @@ export async function fetchLessonContent(railContentId) {
|
|
|
1237
1274
|
"id":railcontent_id,
|
|
1238
1275
|
name,
|
|
1239
1276
|
short_bio,
|
|
1240
|
-
"biography": short_bio[0].children[0].text,
|
|
1277
|
+
"biography": short_bio[0].children[0].text,
|
|
1241
1278
|
web_url_path,
|
|
1242
1279
|
"coach_card_image": coach_card_image.asset->url,
|
|
1243
1280
|
"coach_profile_image":thumbnail_url.asset->url
|
|
@@ -1264,16 +1301,16 @@ export async function fetchLessonContent(railContentId) {
|
|
|
1264
1301
|
isSingle: true,
|
|
1265
1302
|
})
|
|
1266
1303
|
const chapterProcess = (result) => {
|
|
1267
|
-
const chapters = result.chapters ?? []
|
|
1268
|
-
if(chapters.length == 0) return result
|
|
1304
|
+
const chapters = result.chapters ?? []
|
|
1305
|
+
if (chapters.length == 0) return result
|
|
1269
1306
|
result.chapters = chapters.map((chapter, index) => ({
|
|
1270
1307
|
...chapter,
|
|
1271
|
-
chapter_thumbnail_url: `https://musora-web-platform.s3.amazonaws.com/chapters/${result.brand}/Chapter${index + 1}.jpg
|
|
1272
|
-
}))
|
|
1308
|
+
chapter_thumbnail_url: `https://musora-web-platform.s3.amazonaws.com/chapters/${result.brand}/Chapter${index + 1}.jpg`,
|
|
1309
|
+
}))
|
|
1273
1310
|
return result
|
|
1274
1311
|
}
|
|
1275
1312
|
|
|
1276
|
-
return fetchSanity(query, false, {customPostProcess:chapterProcess})
|
|
1313
|
+
return fetchSanity(query, false, { customPostProcess: chapterProcess })
|
|
1277
1314
|
}
|
|
1278
1315
|
|
|
1279
1316
|
/**
|
|
@@ -1353,7 +1390,7 @@ export async function fetchPackAll(railcontentId, type = 'pack') {
|
|
|
1353
1390
|
return fetchByRailContentId(railcontentId, type)
|
|
1354
1391
|
}
|
|
1355
1392
|
|
|
1356
|
-
export async function fetchLiveEvent(brand) {
|
|
1393
|
+
export async function fetchLiveEvent(brand, forcedContentId = null) {
|
|
1357
1394
|
//calendarIDs taken from addevent.php
|
|
1358
1395
|
// TODO import instructor calendars to Sanity
|
|
1359
1396
|
let defaultCalendarID = ''
|
|
@@ -1381,7 +1418,9 @@ export async function fetchLiveEvent(brand) {
|
|
|
1381
1418
|
// See LiveStreamEventService.getCurrentOrNextLiveEvent for some nice complicated logic which I don't think is actually importart
|
|
1382
1419
|
// this has some +- on times
|
|
1383
1420
|
// But this query just finds the first scheduled event (sorted by start_time) that ends after now()
|
|
1384
|
-
const query =
|
|
1421
|
+
const query =
|
|
1422
|
+
forcedContentId !== null
|
|
1423
|
+
? `*[railcontent_id == ${forcedContentId} ]{
|
|
1385
1424
|
'slug': slug.current,
|
|
1386
1425
|
'id': railcontent_id,
|
|
1387
1426
|
live_event_start_time,
|
|
@@ -1392,13 +1431,36 @@ export async function fetchLiveEvent(brand) {
|
|
|
1392
1431
|
'event_coach_url' : instructor[0]->web_url_path,
|
|
1393
1432
|
'event_coach_calendar_id': coalesce(calendar_id, '${defaultCalendarID}'),
|
|
1394
1433
|
title,
|
|
1395
|
-
"
|
|
1434
|
+
"thumbnail": thumbnail.asset->url,
|
|
1435
|
+
${artistOrInstructorName()},
|
|
1436
|
+
difficulty_string,
|
|
1437
|
+
"instructors": instructor[]->{
|
|
1438
|
+
name,
|
|
1439
|
+
web_url_path,
|
|
1440
|
+
},
|
|
1441
|
+
'videoId': coalesce(live_event_youtube_id, video.external_id),
|
|
1442
|
+
} | order(live_event_start_time)[0...1]`
|
|
1443
|
+
: `*[status == 'scheduled' && brand == '${brand}' && defined(live_event_start_time) && live_event_start_time <= '${getSanityDate(startDateTemp, false)}' && live_event_end_time >= '${getSanityDate(endDateTemp, false)}']{
|
|
1444
|
+
'slug': slug.current,
|
|
1445
|
+
'id': railcontent_id,
|
|
1446
|
+
live_event_start_time,
|
|
1447
|
+
live_event_end_time,
|
|
1448
|
+
live_event_youtube_id,
|
|
1449
|
+
railcontent_id,
|
|
1450
|
+
published_on,
|
|
1451
|
+
'event_coach_url' : instructor[0]->web_url_path,
|
|
1452
|
+
'event_coach_calendar_id': coalesce(calendar_id, '${defaultCalendarID}'),
|
|
1453
|
+
title,
|
|
1454
|
+
"thumbnail": thumbnail.asset->url,
|
|
1455
|
+
${artistOrInstructorName()},
|
|
1456
|
+
difficulty_string,
|
|
1396
1457
|
"instructors": instructor[]->{
|
|
1397
1458
|
name,
|
|
1398
1459
|
web_url_path,
|
|
1399
1460
|
},
|
|
1400
1461
|
'videoId': coalesce(live_event_youtube_id, video.external_id),
|
|
1401
1462
|
} | order(live_event_start_time)[0...1]`
|
|
1463
|
+
|
|
1402
1464
|
return await fetchSanity(query, false, { processNeedAccess: false })
|
|
1403
1465
|
}
|
|
1404
1466
|
|
|
@@ -1561,10 +1623,10 @@ export async function fetchArtistLessons(
|
|
|
1561
1623
|
progressIds !== undefined ? `&& railcontent_id in [${progressIds.join(',')}]` : ''
|
|
1562
1624
|
const now = getSanityDate(new Date())
|
|
1563
1625
|
const query = `{
|
|
1564
|
-
"entity":
|
|
1626
|
+
"entity":
|
|
1565
1627
|
*[_type == 'artist' && name == '${name}']
|
|
1566
|
-
{'type': _type, name, 'thumbnail_url':thumbnail_url.asset->url,
|
|
1567
|
-
'lessons_count': count(*[${addType} brand == '${brand}' && references(^._id)]),
|
|
1628
|
+
{'type': _type, name, 'thumbnail_url':thumbnail_url.asset->url,
|
|
1629
|
+
'lessons_count': count(*[${addType} brand == '${brand}' && references(^._id)]),
|
|
1568
1630
|
'lessons': *[${addType} brand == '${brand}' && references(^._id) && (status in ['published'] || (status == 'scheduled' && defined(published_on) && published_on >= '${now}')) ${searchFilter} ${includedFieldsFilter} ${progressFilter}]{${fieldsString}}
|
|
1569
1631
|
[${start}...${end}]}
|
|
1570
1632
|
|order(${sortOrder})
|
|
@@ -1615,10 +1677,10 @@ export async function fetchGenreLessons(
|
|
|
1615
1677
|
progressIds !== undefined ? `&& railcontent_id in [${progressIds.join(',')}]` : ''
|
|
1616
1678
|
const now = getSanityDate(new Date())
|
|
1617
1679
|
const query = `{
|
|
1618
|
-
"entity":
|
|
1680
|
+
"entity":
|
|
1619
1681
|
*[_type == 'genre' && name == '${name}']
|
|
1620
|
-
{'type': _type, name, 'thumbnail_url':thumbnail_url.asset->url,
|
|
1621
|
-
'lessons_count': count(*[${addType} brand == '${brand}' && references(^._id)]),
|
|
1682
|
+
{'type': _type, name, 'thumbnail_url':thumbnail_url.asset->url,
|
|
1683
|
+
'lessons_count': count(*[${addType} brand == '${brand}' && references(^._id)]),
|
|
1622
1684
|
'lessons': *[${addType} brand == '${brand}' && references(^._id) && (status in ['published'] || (status == 'scheduled' && defined(published_on) && published_on >= '${now}')) ${searchFilter} ${includedFieldsFilter} ${progressFilter}]{${fieldsString}}
|
|
1623
1685
|
[${start}...${end}]}
|
|
1624
1686
|
|order(${sortOrder})
|
|
@@ -1638,8 +1700,8 @@ export async function fetchTopLevelParentId(railcontentId) {
|
|
|
1638
1700
|
'parents': *[^._id in child[]._ref ${statusFilter}]{
|
|
1639
1701
|
railcontent_id,
|
|
1640
1702
|
'parents': *[^._id in child[]._ref ${statusFilter}]{
|
|
1641
|
-
railcontent_id,
|
|
1642
|
-
}
|
|
1703
|
+
railcontent_id,
|
|
1704
|
+
}
|
|
1643
1705
|
}
|
|
1644
1706
|
}
|
|
1645
1707
|
}
|
|
@@ -1673,8 +1735,8 @@ export async function fetchHierarchy(railcontentId) {
|
|
|
1673
1735
|
railcontent_id,
|
|
1674
1736
|
'assignments': assignment[]{railcontent_id},
|
|
1675
1737
|
'children': child[${childrenFilter}]->{
|
|
1676
|
-
railcontent_id,
|
|
1677
|
-
}
|
|
1738
|
+
railcontent_id,
|
|
1739
|
+
}
|
|
1678
1740
|
}
|
|
1679
1741
|
}
|
|
1680
1742
|
},
|
|
@@ -1890,6 +1952,7 @@ export async function fetchShowsData(brand) {
|
|
|
1890
1952
|
|
|
1891
1953
|
/**
|
|
1892
1954
|
* Fetch metadata from the contentMetaData.js based on brand and type.
|
|
1955
|
+
* For v2 you need to provide page type('lessons' or 'songs') in type parameter
|
|
1893
1956
|
*
|
|
1894
1957
|
* @param {string} brand - The brand for which to fetch metadata.
|
|
1895
1958
|
* @param {string} type - The type for which to fetch metadata.
|
|
@@ -1974,7 +2037,7 @@ function checkSanityConfig(config) {
|
|
|
1974
2037
|
function buildRawQuery(
|
|
1975
2038
|
filter = '',
|
|
1976
2039
|
fields = '...',
|
|
1977
|
-
{ sortOrder = 'published_on desc', start = 0, end = 10, isSingle = false}
|
|
2040
|
+
{ sortOrder = 'published_on desc', start = 0, end = 10, isSingle = false }
|
|
1978
2041
|
) {
|
|
1979
2042
|
const sortString = sortOrder ? `order(${sortOrder})` : ''
|
|
1980
2043
|
const countString = isSingle ? '[0...1]' : `[${start}...${end}]`
|
|
@@ -1988,10 +2051,10 @@ async function buildQuery(
|
|
|
1988
2051
|
baseFilter = '',
|
|
1989
2052
|
filterParams = { pullFutureContent: false },
|
|
1990
2053
|
fields = '...',
|
|
1991
|
-
{ sortOrder = 'published_on desc', start = 0, end = 10, isSingle = false}
|
|
2054
|
+
{ sortOrder = 'published_on desc', start = 0, end = 10, isSingle = false }
|
|
1992
2055
|
) {
|
|
1993
2056
|
const filter = await new FilterBuilder(baseFilter, filterParams).buildFilter()
|
|
1994
|
-
return buildRawQuery(filter, fields, { sortOrder, start, end, isSingle})
|
|
2057
|
+
return buildRawQuery(filter, fields, { sortOrder, start, end, isSingle })
|
|
1995
2058
|
}
|
|
1996
2059
|
|
|
1997
2060
|
function buildEntityAndTotalQuery(
|
|
@@ -2002,7 +2065,7 @@ function buildEntityAndTotalQuery(
|
|
|
2002
2065
|
const sortString = sortOrder ? `order(${sortOrder})` : ''
|
|
2003
2066
|
const countString = isSingle ? '[0...1]' : `[${start}...${end}]`
|
|
2004
2067
|
const query = `{
|
|
2005
|
-
"entity": *[${filter}] | ${sortString}${countString}
|
|
2068
|
+
"entity": *[${filter}] | ${sortString}${countString}
|
|
2006
2069
|
{
|
|
2007
2070
|
${fields}
|
|
2008
2071
|
},
|
|
@@ -2017,7 +2080,7 @@ function getFilterOptions(option, commonFilter, contentType, brand) {
|
|
|
2017
2080
|
|
|
2018
2081
|
switch (option) {
|
|
2019
2082
|
case 'difficulty':
|
|
2020
|
-
filterGroq = `
|
|
2083
|
+
filterGroq = `
|
|
2021
2084
|
"difficulty": [
|
|
2022
2085
|
{"type": "All", "count": count(*[${commonFilter} && difficulty_string == "All"])},
|
|
2023
2086
|
{"type": "Introductory", "count": count(*[${commonFilter} && (difficulty_string == "Novice" || difficulty_string == "Introductory")])},
|
|
@@ -2104,3 +2167,114 @@ function cleanUpGroq(query) {
|
|
|
2104
2167
|
|
|
2105
2168
|
return cleanedQuery
|
|
2106
2169
|
}
|
|
2170
|
+
|
|
2171
|
+
// V2 methods
|
|
2172
|
+
|
|
2173
|
+
export async function fetchTabData(
|
|
2174
|
+
brand,
|
|
2175
|
+
pageName,
|
|
2176
|
+
{
|
|
2177
|
+
page = 1,
|
|
2178
|
+
limit = 10,
|
|
2179
|
+
sort = '-published_on',
|
|
2180
|
+
includedFields = [],
|
|
2181
|
+
progressIds = undefined,
|
|
2182
|
+
progress = 'all',
|
|
2183
|
+
} = {}
|
|
2184
|
+
) {
|
|
2185
|
+
const start = (page - 1) * limit
|
|
2186
|
+
const end = start + limit
|
|
2187
|
+
|
|
2188
|
+
// Construct the included fields filter, replacing 'difficulty' with 'difficulty_string'
|
|
2189
|
+
const includedFieldsFilter =
|
|
2190
|
+
includedFields.length > 0 ? filtersToGroq(includedFields, [], pageName) : ''
|
|
2191
|
+
|
|
2192
|
+
// limits the results to supplied progressIds for started & completed filters
|
|
2193
|
+
const progressFilter = await getProgressFilter(progress, progressIds)
|
|
2194
|
+
|
|
2195
|
+
// Determine the sort order
|
|
2196
|
+
const sortOrder = getSortOrder(sort, brand, '')
|
|
2197
|
+
|
|
2198
|
+
let fields = DEFAULT_FIELDS
|
|
2199
|
+
let fieldsString = fields.join(',')
|
|
2200
|
+
|
|
2201
|
+
// Determine the group by clause
|
|
2202
|
+
let query = ''
|
|
2203
|
+
let entityFieldsString = ''
|
|
2204
|
+
let filter = ''
|
|
2205
|
+
|
|
2206
|
+
filter = `brand == "${brand}" ${includedFieldsFilter} ${progressFilter}`
|
|
2207
|
+
const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
|
|
2208
|
+
entityFieldsString = ` ${fieldsString},
|
|
2209
|
+
'lesson_count': coalesce(count(child[${childrenFilter}]->), 0) ,
|
|
2210
|
+
'length_in_seconds': coalesce(
|
|
2211
|
+
math::sum(
|
|
2212
|
+
select(
|
|
2213
|
+
child[${childrenFilter}]->length_in_seconds
|
|
2214
|
+
)
|
|
2215
|
+
),
|
|
2216
|
+
length_in_seconds
|
|
2217
|
+
),`
|
|
2218
|
+
|
|
2219
|
+
const filterWithRestrictions = await new FilterBuilder(filter, {}).buildFilter()
|
|
2220
|
+
query = buildEntityAndTotalQuery(filterWithRestrictions, entityFieldsString, {
|
|
2221
|
+
sortOrder: sortOrder,
|
|
2222
|
+
start: start,
|
|
2223
|
+
end: end,
|
|
2224
|
+
})
|
|
2225
|
+
|
|
2226
|
+
return fetchSanity(query, true)
|
|
2227
|
+
}
|
|
2228
|
+
|
|
2229
|
+
export async function fetchRecent(
|
|
2230
|
+
brand,
|
|
2231
|
+
pageName,
|
|
2232
|
+
{ page = 1, limit = 10, sort = '-published_on', includedFields = [], progress = 'recent' } = {}
|
|
2233
|
+
) {
|
|
2234
|
+
const mergedIncludedFields = [...includedFields, `tab,all`]
|
|
2235
|
+
const results = await fetchTabData(brand, pageName, {
|
|
2236
|
+
page,
|
|
2237
|
+
limit,
|
|
2238
|
+
sort,
|
|
2239
|
+
includedFields: mergedIncludedFields,
|
|
2240
|
+
progress: progress.toLowerCase(),
|
|
2241
|
+
})
|
|
2242
|
+
return results.entity
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
export async function fetchScheduledAndNewReleases(
|
|
2246
|
+
brand,
|
|
2247
|
+
{ page = 1, limit = 20, sort = '-published_on' } = {}
|
|
2248
|
+
) {
|
|
2249
|
+
const upcomingTypes = getUpcomingEventsTypes(brand)
|
|
2250
|
+
const newTypes = getNewReleasesTypes(brand)
|
|
2251
|
+
|
|
2252
|
+
const scheduledTypes = merge(upcomingTypes, newTypes)
|
|
2253
|
+
const typesString = arrayJoinWithQuotes(scheduledTypes)
|
|
2254
|
+
const now = getSanityDate(new Date())
|
|
2255
|
+
|
|
2256
|
+
const start = (page - 1) * limit
|
|
2257
|
+
const end = start + limit
|
|
2258
|
+
const sortOrder = getSortOrder(sort, brand)
|
|
2259
|
+
|
|
2260
|
+
const query = `
|
|
2261
|
+
*[_type in [${typesString}] && brand == '${brand}' && ((status in ['published','scheduled'] && published_on > '${now}')||(show_in_new_feed == true)) ]
|
|
2262
|
+
[${start}...${end}]
|
|
2263
|
+
| order(published_on asc) {
|
|
2264
|
+
"id": railcontent_id,
|
|
2265
|
+
title,
|
|
2266
|
+
"image": thumbnail.asset->url,
|
|
2267
|
+
${artistOrInstructorName()},
|
|
2268
|
+
"artists": instructor[]->name,
|
|
2269
|
+
difficulty,
|
|
2270
|
+
difficulty_string,
|
|
2271
|
+
length_in_seconds,
|
|
2272
|
+
published_on,
|
|
2273
|
+
"type": _type,
|
|
2274
|
+
show_in_new_feed,
|
|
2275
|
+
web_url_path,
|
|
2276
|
+
"permission_id": permission[]->railcontent_id
|
|
2277
|
+
}`
|
|
2278
|
+
|
|
2279
|
+
return fetchSanity(query, true)
|
|
2280
|
+
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @module User-Permissions
|
|
3
|
+
*/
|
|
4
|
+
import { setLastUpdatedTime, wasLastUpdateOlderThanXSeconds } from '../../lib/lastUpdated.js'
|
|
5
|
+
import { fetchUserPermissionsData } from '../railcontent.js'
|
|
6
|
+
import './types'
|
|
3
7
|
|
|
4
8
|
/**
|
|
5
9
|
* Exported functions that are excluded from index generation.
|
|
@@ -10,6 +14,11 @@ const excludeFromGeneratedIndex = []
|
|
|
10
14
|
let userPermissionsPromise = null
|
|
11
15
|
let lastUpdatedKey = `userPermissions_lastUpdated`
|
|
12
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Fetches the user permissions data.
|
|
19
|
+
*
|
|
20
|
+
* @returns {Promise<UserPermissions>} - The user permissions data.
|
|
21
|
+
*/
|
|
13
22
|
export async function fetchUserPermissions() {
|
|
14
23
|
if (!userPermissionsPromise || wasLastUpdateOlderThanXSeconds(10, lastUpdatedKey)) {
|
|
15
24
|
userPermissionsPromise = fetchUserPermissionsData()
|
|
@@ -19,6 +28,11 @@ export async function fetchUserPermissions() {
|
|
|
19
28
|
return await userPermissionsPromise
|
|
20
29
|
}
|
|
21
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Resets the user permissions data.
|
|
33
|
+
*
|
|
34
|
+
* @returns {Promise<void>}
|
|
35
|
+
*/
|
|
22
36
|
export async function reset() {
|
|
23
37
|
userPermissionsPromise = null
|
|
24
38
|
}
|