musora-content-services 1.2.5 → 1.3.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/.prettierignore +5 -0
- package/.prettierrc +8 -0
- package/.yarnrc.yml +1 -0
- package/CHANGELOG.md +11 -0
- package/README.md +0 -0
- package/babel.config.cjs +1 -1
- package/docs/config.js.html +0 -0
- package/docs/index.html +0 -0
- package/docs/module-Config.html +0 -0
- package/docs/module-Railcontent-Services.html +0 -0
- package/docs/module-Sanity-Services.html +0 -0
- package/docs/railcontent.js.html +0 -0
- package/docs/sanity.js.html +0 -0
- package/jest.config.js +9 -10
- package/jsdoc.json +17 -12
- package/package.json +2 -1
- package/src/contentMetaData.js +1190 -1131
- package/src/contentTypeConfig.js +492 -387
- package/src/filterBuilder.js +163 -145
- package/src/index.d.ts +227 -237
- package/src/index.js +226 -236
- package/src/services/config.js +12 -12
- package/src/services/contentLikes.js +33 -32
- package/src/services/contentProgress.js +233 -200
- package/src/services/dataContext.js +99 -93
- package/src/services/lastUpdated.js +7 -7
- package/src/services/railcontent.js +368 -364
- package/src/services/sanity.js +983 -955
- package/src/services/userPermissions.js +12 -14
- package/test/contentLikes.test.js +89 -86
- package/test/contentProgress.test.js +229 -236
- package/test/initializeTests.js +54 -51
- package/test/lastUpdated.test.js +20 -18
- package/test/live/contentProgressLive.test.js +135 -137
- package/test/live/railcontentLive.test.js +12 -14
- package/test/localStorageMock.js +16 -16
- package/test/log.js +5 -5
- package/test/sanityQueryService.test.js +857 -821
- package/test/userPermissions.test.js +15 -15
- package/tools/generate-index.cjs +108 -111
package/src/services/sanity.js
CHANGED
|
@@ -2,45 +2,42 @@
|
|
|
2
2
|
* @module Sanity-Services
|
|
3
3
|
*/
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
} from
|
|
5
|
+
artistOrInstructorName,
|
|
6
|
+
artistOrInstructorNameAsArray,
|
|
7
|
+
assignmentsField,
|
|
8
|
+
descriptionField,
|
|
9
|
+
resourcesField,
|
|
10
|
+
contentTypeConfig,
|
|
11
|
+
DEFAULT_FIELDS,
|
|
12
|
+
getFieldsForContentType,
|
|
13
|
+
filtersToGroq,
|
|
14
|
+
getUpcomingEventsTypes,
|
|
15
|
+
showsTypes,
|
|
16
|
+
getNewReleasesTypes,
|
|
17
|
+
coachLessonsTypes,
|
|
18
|
+
} from '../contentTypeConfig.js'
|
|
19
|
+
|
|
20
|
+
import { processMetadata, typeWithSortOrder } from '../contentMetaData.js'
|
|
21
|
+
|
|
22
|
+
import { globalConfig } from './config.js'
|
|
19
23
|
|
|
20
24
|
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
import {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
fetchOwnedChallenges,
|
|
31
|
-
fetchNextContentDataForParent,
|
|
32
|
-
fetchHandler,
|
|
33
|
-
} from './railcontent.js';
|
|
34
|
-
import {arrayToStringRepresentation, FilterBuilder} from "../filterBuilder.js";
|
|
35
|
-
import {fetchUserPermissions} from "./userPermissions.js";
|
|
36
|
-
import {getAllCompleted, getAllStarted, getAllStartedOrCompleted} from "./contentProgress.js";
|
|
25
|
+
fetchAllCompletedStates,
|
|
26
|
+
fetchCompletedChallenges,
|
|
27
|
+
fetchOwnedChallenges,
|
|
28
|
+
fetchNextContentDataForParent,
|
|
29
|
+
fetchHandler,
|
|
30
|
+
} from './railcontent.js'
|
|
31
|
+
import { arrayToStringRepresentation, FilterBuilder } from '../filterBuilder.js'
|
|
32
|
+
import { fetchUserPermissions } from './userPermissions.js'
|
|
33
|
+
import { getAllCompleted, getAllStarted, getAllStartedOrCompleted } from './contentProgress.js'
|
|
37
34
|
|
|
38
35
|
/**
|
|
39
36
|
* Exported functions that are excluded from index generation.
|
|
40
37
|
*
|
|
41
38
|
* @type {string[]}
|
|
42
39
|
*/
|
|
43
|
-
const excludeFromGeneratedIndex = ['handleCustomFetchAll']
|
|
40
|
+
const excludeFromGeneratedIndex = ['handleCustomFetchAll']
|
|
44
41
|
|
|
45
42
|
/**
|
|
46
43
|
* Fetch a song by its document ID from Sanity.
|
|
@@ -54,16 +51,17 @@ const excludeFromGeneratedIndex = ['handleCustomFetchAll'];
|
|
|
54
51
|
* .catch(error => console.error(error));
|
|
55
52
|
*/
|
|
56
53
|
export async function fetchSongById(documentId) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
54
|
+
const fields = getFieldsForContentType('song')
|
|
55
|
+
const filterParams = {}
|
|
56
|
+
const query = await buildQuery(
|
|
57
|
+
`_type == "song" && railcontent_id == ${documentId}`,
|
|
58
|
+
filterParams,
|
|
59
|
+
fields,
|
|
60
|
+
{
|
|
61
|
+
isSingle: true,
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
return fetchSanity(query, false)
|
|
67
65
|
}
|
|
68
66
|
|
|
69
67
|
/**
|
|
@@ -78,13 +76,16 @@ export async function fetchSongById(documentId) {
|
|
|
78
76
|
* .catch(error => console.error(error));
|
|
79
77
|
*/
|
|
80
78
|
export async function fetchArtists(brand) {
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
const filter = await new FilterBuilder(
|
|
80
|
+
`_type == "song" && brand == "${brand}" && references(^._id)`,
|
|
81
|
+
{ bypassPermissions: true }
|
|
82
|
+
).buildFilter()
|
|
83
|
+
const query = `
|
|
83
84
|
*[_type == "artist"]{
|
|
84
85
|
name,
|
|
85
86
|
"lessonsCount": count(*[${filter}])
|
|
86
|
-
}[lessonsCount > 0]
|
|
87
|
-
|
|
87
|
+
}[lessonsCount > 0] |order(lower(name)) `
|
|
88
|
+
return fetchSanity(query, true, { processNeedAccess: false })
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
/**
|
|
@@ -93,13 +94,21 @@ export async function fetchArtists(brand) {
|
|
|
93
94
|
* @returns {Promise<int|null>} - The fetched count of artists.
|
|
94
95
|
*/
|
|
95
96
|
export async function fetchSongArtistCount(brand) {
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
const filter = await new FilterBuilder(
|
|
98
|
+
`_type == "song" && brand == "${brand}" && references(^._id)`,
|
|
99
|
+
{ bypassPermissions: true }
|
|
100
|
+
).buildFilter()
|
|
101
|
+
const query = `
|
|
102
|
+
count(*[_type == "artist"]{
|
|
103
|
+
name,
|
|
104
|
+
"lessonsCount": count(*[${filter}])
|
|
105
|
+
}[lessonsCount > 0])`
|
|
106
|
+
return fetchSanity(query, true, { processNeedAccess: false })
|
|
98
107
|
}
|
|
99
108
|
|
|
100
109
|
export async function fetchPlayAlongsCount(brand) {
|
|
101
|
-
|
|
102
|
-
|
|
110
|
+
const query = `count(*[brand == '${brand}' && _type == "play-along"]) `
|
|
111
|
+
return fetchSanity(query, true, { processNeedAccess: false })
|
|
103
112
|
}
|
|
104
113
|
|
|
105
114
|
/**
|
|
@@ -115,8 +124,8 @@ export async function fetchPlayAlongsCount(brand) {
|
|
|
115
124
|
* .catch(error => console.error(error));
|
|
116
125
|
*/
|
|
117
126
|
export async function fetchRelatedSongs(brand, songId) {
|
|
118
|
-
|
|
119
|
-
|
|
127
|
+
const now = getSanityDate(new Date())
|
|
128
|
+
const query = `
|
|
120
129
|
*[_type == "song" && railcontent_id == ${songId}]{
|
|
121
130
|
"entity": array::unique([
|
|
122
131
|
...(*[_type == "song" && brand == "${brand}" && railcontent_id != ${songId} && references(^.artist->_id)
|
|
@@ -177,10 +186,10 @@ export async function fetchRelatedSongs(brand, songId) {
|
|
|
177
186
|
}]
|
|
178
187
|
}[0...10])
|
|
179
188
|
])[0...10]
|
|
180
|
-
}
|
|
189
|
+
}`
|
|
181
190
|
|
|
182
|
-
|
|
183
|
-
|
|
191
|
+
// Fetch the related songs data
|
|
192
|
+
return fetchSanity(query, false)
|
|
184
193
|
}
|
|
185
194
|
|
|
186
195
|
/**
|
|
@@ -188,14 +197,17 @@ export async function fetchRelatedSongs(brand, songId) {
|
|
|
188
197
|
* @param {string} brand - The brand for which to fetch new releases.
|
|
189
198
|
* @returns {Promise<Object|null>} - The fetched new releases data or null if not found.
|
|
190
199
|
*/
|
|
191
|
-
export async function fetchNewReleases(
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
200
|
+
export async function fetchNewReleases(
|
|
201
|
+
brand,
|
|
202
|
+
{ page = 1, limit = 20, sort = '-published_on' } = {}
|
|
203
|
+
) {
|
|
204
|
+
const newTypes = getNewReleasesTypes(brand)
|
|
205
|
+
const typesString = arrayToStringRepresentation(newTypes)
|
|
206
|
+
const start = (page - 1) * limit
|
|
207
|
+
const end = start + limit
|
|
208
|
+
const sortOrder = getSortOrder(sort, brand)
|
|
209
|
+
const filter = `_type in ${typesString} && brand == '${brand}' && show_in_new_feed == true`
|
|
210
|
+
const fields = `
|
|
199
211
|
"id": railcontent_id,
|
|
200
212
|
title,
|
|
201
213
|
"image": thumbnail.asset->url,
|
|
@@ -208,21 +220,16 @@ export async function fetchNewReleases(brand, {page = 1, limit = 20, sort = "-pu
|
|
|
208
220
|
"type": _type,
|
|
209
221
|
web_url_path,
|
|
210
222
|
"permission_id": permission[]->railcontent_id,
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
start,
|
|
220
|
-
end: end,
|
|
221
|
-
});
|
|
222
|
-
return fetchSanity(query, true);
|
|
223
|
+
`
|
|
224
|
+
const filterParams = { allowsPullSongsContent: false }
|
|
225
|
+
const query = await buildQuery(filter, filterParams, fields, {
|
|
226
|
+
sortOrder: sortOrder,
|
|
227
|
+
start,
|
|
228
|
+
end: end,
|
|
229
|
+
})
|
|
230
|
+
return fetchSanity(query, true)
|
|
223
231
|
}
|
|
224
232
|
|
|
225
|
-
|
|
226
233
|
/**
|
|
227
234
|
* Fetch upcoming events for a specific brand.
|
|
228
235
|
*
|
|
@@ -237,13 +244,13 @@ export async function fetchNewReleases(brand, {page = 1, limit = 20, sort = "-pu
|
|
|
237
244
|
* .then(events => console.log(events))
|
|
238
245
|
* .catch(error => console.error(error));
|
|
239
246
|
*/
|
|
240
|
-
export async function fetchUpcomingEvents(brand, {page = 1, limit = 10} = {}) {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
+
export async function fetchUpcomingEvents(brand, { page = 1, limit = 10 } = {}) {
|
|
248
|
+
const liveTypes = getUpcomingEventsTypes(brand)
|
|
249
|
+
const typesString = arrayToStringRepresentation(liveTypes)
|
|
250
|
+
const now = getSanityDate(new Date())
|
|
251
|
+
const start = (page - 1) * limit
|
|
252
|
+
const end = start + limit
|
|
253
|
+
const fields = `
|
|
247
254
|
"id": railcontent_id,
|
|
248
255
|
title,
|
|
249
256
|
"image": thumbnail.asset->url,
|
|
@@ -255,17 +262,17 @@ export async function fetchUpcomingEvents(brand, {page = 1, limit = 10} = {}) {
|
|
|
255
262
|
published_on,
|
|
256
263
|
"type": _type,
|
|
257
264
|
web_url_path,
|
|
258
|
-
"permission_id": permission[]->railcontent_id
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
265
|
+
"permission_id": permission[]->railcontent_id,`
|
|
266
|
+
const query = buildRawQuery(
|
|
267
|
+
`_type in ${typesString} && brand == '${brand}' && published_on > '${now}' && status == 'scheduled'`,
|
|
268
|
+
fields,
|
|
269
|
+
{
|
|
270
|
+
sortOrder: 'published_on asc',
|
|
271
|
+
start: start,
|
|
272
|
+
end: end,
|
|
273
|
+
}
|
|
274
|
+
)
|
|
275
|
+
return fetchSanity(query, true)
|
|
269
276
|
}
|
|
270
277
|
|
|
271
278
|
/**
|
|
@@ -282,16 +289,16 @@ export async function fetchUpcomingEvents(brand, {page = 1, limit = 10} = {}) {
|
|
|
282
289
|
* .then(content => console.log(content))
|
|
283
290
|
* .catch(error => console.error(error));
|
|
284
291
|
*/
|
|
285
|
-
export async function fetchScheduledReleases(brand, {page = 1, limit = 10}) {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
292
|
+
export async function fetchScheduledReleases(brand, { page = 1, limit = 10 }) {
|
|
293
|
+
const upcomingTypes = getUpcomingEventsTypes(brand)
|
|
294
|
+
const newTypes = getNewReleasesTypes(brand)
|
|
295
|
+
|
|
296
|
+
const scheduledTypes = merge(upcomingTypes, newTypes)
|
|
297
|
+
const typesString = arrayJoinWithQuotes(scheduledTypes)
|
|
298
|
+
const now = getSanityDate(new Date())
|
|
299
|
+
const start = (page - 1) * limit
|
|
300
|
+
const end = start + limit
|
|
301
|
+
const query = `*[_type in [${typesString}] && brand == '${brand}' && status in ['published','scheduled'] && published_on > '${now}']{
|
|
295
302
|
"id": railcontent_id,
|
|
296
303
|
title,
|
|
297
304
|
"image": thumbnail.asset->url,
|
|
@@ -304,8 +311,8 @@ export async function fetchScheduledReleases(brand, {page = 1, limit = 10}) {
|
|
|
304
311
|
"type": _type,
|
|
305
312
|
web_url_path,
|
|
306
313
|
"permission_id": permission[]->railcontent_id,
|
|
307
|
-
} | order(published_on asc)[${start}...${end}]
|
|
308
|
-
|
|
314
|
+
} | order(published_on asc)[${start}...${end}]`
|
|
315
|
+
return fetchSanity(query, true)
|
|
309
316
|
}
|
|
310
317
|
|
|
311
318
|
/**
|
|
@@ -321,9 +328,9 @@ export async function fetchScheduledReleases(brand, {page = 1, limit = 10}) {
|
|
|
321
328
|
* .catch(error => console.error(error));
|
|
322
329
|
*/
|
|
323
330
|
export async function fetchByRailContentId(id, contentType) {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
331
|
+
const fields = getFieldsForContentType(contentType)
|
|
332
|
+
const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
|
|
333
|
+
const entityFieldsString = ` ${fields}
|
|
327
334
|
'child_count': coalesce(count(child[${childrenFilter}]->), 0) ,
|
|
328
335
|
"lessons": child[${childrenFilter}]->{
|
|
329
336
|
"id": railcontent_id,
|
|
@@ -339,17 +346,17 @@ export async function fetchByRailContentId(id, contentType) {
|
|
|
339
346
|
)
|
|
340
347
|
),
|
|
341
348
|
length_in_seconds
|
|
342
|
-
)
|
|
349
|
+
),`
|
|
343
350
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
+
const query = buildRawQuery(
|
|
352
|
+
`railcontent_id == ${id} && _type == '${contentType}'`,
|
|
353
|
+
entityFieldsString,
|
|
354
|
+
{
|
|
355
|
+
isSingle: true,
|
|
356
|
+
}
|
|
357
|
+
)
|
|
351
358
|
|
|
352
|
-
|
|
359
|
+
return fetchSanity(query, false)
|
|
353
360
|
}
|
|
354
361
|
|
|
355
362
|
/**
|
|
@@ -365,25 +372,25 @@ export async function fetchByRailContentId(id, contentType) {
|
|
|
365
372
|
* .catch(error => console.error(error));
|
|
366
373
|
*/
|
|
367
374
|
export async function fetchByRailContentIds(ids, contentType = undefined) {
|
|
368
|
-
|
|
375
|
+
const idsString = ids.join(',')
|
|
369
376
|
|
|
370
|
-
|
|
377
|
+
const query = `*[railcontent_id in [${idsString}]]{
|
|
371
378
|
${getFieldsForContentType(contentType)}
|
|
372
379
|
}`
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
const sortFuction = function compare(a,b){
|
|
376
|
-
const indexA = ids.indexOf(a['id']);
|
|
377
|
-
const indexB = ids.indexOf(b['id'])
|
|
378
|
-
if(indexA === indexB) return 0;
|
|
379
|
-
if(indexA > indexB) return 1;
|
|
380
|
-
return -1;
|
|
381
|
-
}
|
|
380
|
+
const results = await fetchSanity(query, true)
|
|
382
381
|
|
|
383
|
-
|
|
384
|
-
const
|
|
382
|
+
const sortFuction = function compare(a, b) {
|
|
383
|
+
const indexA = ids.indexOf(a['id'])
|
|
384
|
+
const indexB = ids.indexOf(b['id'])
|
|
385
|
+
if (indexA === indexB) return 0
|
|
386
|
+
if (indexA > indexB) return 1
|
|
387
|
+
return -1
|
|
388
|
+
}
|
|
385
389
|
|
|
386
|
-
|
|
390
|
+
// Sort results to match the order of the input IDs
|
|
391
|
+
const sortedResults = results.sort(sortFuction)
|
|
392
|
+
|
|
393
|
+
return sortedResults
|
|
387
394
|
}
|
|
388
395
|
|
|
389
396
|
/**
|
|
@@ -418,87 +425,96 @@ export async function fetchByRailContentIds(ids, contentType = undefined) {
|
|
|
418
425
|
* .then(content => console.log(content))
|
|
419
426
|
* .catch(error => console.error(error));
|
|
420
427
|
*/
|
|
421
|
-
export async function fetchAll(
|
|
428
|
+
export async function fetchAll(
|
|
429
|
+
brand,
|
|
430
|
+
type,
|
|
431
|
+
{
|
|
422
432
|
page = 1,
|
|
423
433
|
limit = 10,
|
|
424
|
-
searchTerm =
|
|
425
|
-
sort =
|
|
434
|
+
searchTerm = '',
|
|
435
|
+
sort = '-published_on',
|
|
426
436
|
includedFields = [],
|
|
427
|
-
groupBy =
|
|
437
|
+
groupBy = '',
|
|
428
438
|
progressIds = undefined,
|
|
429
439
|
useDefaultFields = true,
|
|
430
440
|
customFields = [],
|
|
431
|
-
progress =
|
|
432
|
-
} = {}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
?
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
441
|
+
progress = 'all',
|
|
442
|
+
} = {}
|
|
443
|
+
) {
|
|
444
|
+
let customResults = await handleCustomFetchAll(brand, type, {
|
|
445
|
+
page,
|
|
446
|
+
limit,
|
|
447
|
+
searchTerm,
|
|
448
|
+
sort,
|
|
449
|
+
includedFields,
|
|
450
|
+
groupBy,
|
|
451
|
+
progressIds,
|
|
452
|
+
useDefaultFields,
|
|
453
|
+
customFields,
|
|
454
|
+
progress,
|
|
455
|
+
})
|
|
456
|
+
if (customResults) {
|
|
457
|
+
return customResults
|
|
458
|
+
}
|
|
459
|
+
let config = contentTypeConfig[type] ?? {}
|
|
460
|
+
let additionalFields = config?.fields ?? []
|
|
461
|
+
let isGroupByOneToOne = (groupBy ? config?.relationships?.[groupBy]?.isOneToOne : false) ?? false
|
|
462
|
+
let webUrlPathType = config?.slug ?? type
|
|
463
|
+
const start = (page - 1) * limit
|
|
464
|
+
const end = start + limit
|
|
465
|
+
let bypassStatusAndPublishedValidation =
|
|
466
|
+
type == 'instructor' || groupBy == 'artist' || groupBy == 'genre' || groupBy == 'instructor'
|
|
467
|
+
let bypassPermissions = bypassStatusAndPublishedValidation
|
|
468
|
+
// Construct the type filter
|
|
469
|
+
let typeFilter
|
|
470
|
+
|
|
471
|
+
if (type === 'archives') {
|
|
472
|
+
typeFilter = `&& status == "archived"`
|
|
473
|
+
bypassStatusAndPublishedValidation = true
|
|
474
|
+
} else if (type === 'pack') {
|
|
475
|
+
typeFilter = `&& (_type == 'pack' || _type == 'semester-pack')`
|
|
476
|
+
} else {
|
|
477
|
+
typeFilter = type
|
|
478
|
+
? `&& _type == '${type}'`
|
|
479
|
+
: progress === 'in progress' || progress === 'completed'
|
|
480
|
+
? " && (_type != 'challenge-part' && _type != 'challenge')"
|
|
481
|
+
: ''
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Construct the search filter
|
|
485
|
+
const searchFilter = searchTerm
|
|
486
|
+
? groupBy !== ''
|
|
487
|
+
? `&& (^.name match "${searchTerm}*" || title match "${searchTerm}*")`
|
|
488
|
+
: `&& (artist->name match "${searchTerm}*" || instructor[]->name match "${searchTerm}*" || title match "${searchTerm}*" || name match "${searchTerm}*")`
|
|
489
|
+
: ''
|
|
490
|
+
|
|
491
|
+
// Construct the included fields filter, replacing 'difficulty' with 'difficulty_string'
|
|
492
|
+
const includedFieldsFilter = includedFields.length > 0 ? filtersToGroq(includedFields) : ''
|
|
493
|
+
|
|
494
|
+
// limits the results to supplied progressIds for started & completed filters
|
|
495
|
+
const progressFilter = await getProgressFilter(progress, progressIds)
|
|
496
|
+
|
|
497
|
+
// Determine the sort order
|
|
498
|
+
const sortOrder = getSortOrder(sort, brand, groupBy)
|
|
499
|
+
|
|
500
|
+
let fields = useDefaultFields
|
|
501
|
+
? customFields.concat(DEFAULT_FIELDS, additionalFields)
|
|
502
|
+
: customFields
|
|
503
|
+
let fieldsString = fields.join(',')
|
|
504
|
+
|
|
505
|
+
let customFilter = ''
|
|
506
|
+
if (type == 'instructor') {
|
|
507
|
+
customFilter = '&& coach_card_image != null'
|
|
508
|
+
}
|
|
509
|
+
// Determine the group by clause
|
|
510
|
+
let query = ''
|
|
511
|
+
let entityFieldsString = ''
|
|
512
|
+
let filter = ''
|
|
513
|
+
if (groupBy !== '' && isGroupByOneToOne) {
|
|
514
|
+
const webUrlPath = 'artists'
|
|
515
|
+
const lessonsFilter = `_type == '${type}' && brand == '${brand}' && ^._id == ${groupBy}._ref ${searchFilter} ${includedFieldsFilter} ${progressFilter} ${customFilter}`
|
|
516
|
+
const lessonsFilterWithRestrictions = await new FilterBuilder(lessonsFilter).buildFilter()
|
|
517
|
+
entityFieldsString = `
|
|
502
518
|
'id': railcontent_id,
|
|
503
519
|
'type': _type,
|
|
504
520
|
name,
|
|
@@ -509,16 +525,16 @@ export async function fetchAll(brand, type, {
|
|
|
509
525
|
${fieldsString},
|
|
510
526
|
${groupBy}
|
|
511
527
|
}[0...20]
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
528
|
+
`
|
|
529
|
+
filter = `_type == '${groupBy}' && count(*[${lessonsFilterWithRestrictions}]._id) > 0`
|
|
530
|
+
} else if (groupBy !== '') {
|
|
531
|
+
const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
|
|
516
532
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
533
|
+
const webUrlPath = groupBy == 'genre' ? '/genres' : ''
|
|
534
|
+
const lessonsFilter = `brand == '${brand}' && ^._id in ${groupBy}[]._ref ${typeFilter} ${searchFilter} ${includedFieldsFilter} ${progressFilter} ${customFilter}`
|
|
535
|
+
const lessonsFilterWithRestrictions = await new FilterBuilder(lessonsFilter).buildFilter()
|
|
520
536
|
|
|
521
|
-
|
|
537
|
+
entityFieldsString = `
|
|
522
538
|
'id': railcontent_id,
|
|
523
539
|
'type': _type,
|
|
524
540
|
name,
|
|
@@ -529,12 +545,12 @@ export async function fetchAll(brand, type, {
|
|
|
529
545
|
${fieldsString},
|
|
530
546
|
'lesson_count': coalesce(count(child[${childrenFilter}]->), 0) ,
|
|
531
547
|
${groupBy}
|
|
532
|
-
}[0...20]
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
548
|
+
}[0...20]`
|
|
549
|
+
filter = `_type == '${groupBy}' && count(*[${lessonsFilterWithRestrictions}]._id) > 0`
|
|
550
|
+
} else {
|
|
551
|
+
filter = `brand == "${brand}" ${typeFilter} ${searchFilter} ${includedFieldsFilter} ${progressFilter} ${customFilter}`
|
|
552
|
+
const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
|
|
553
|
+
entityFieldsString = ` ${fieldsString},
|
|
538
554
|
'lesson_count': coalesce(count(child[${childrenFilter}]->), 0) ,
|
|
539
555
|
'length_in_seconds': coalesce(
|
|
540
556
|
math::sum(
|
|
@@ -543,24 +559,21 @@ export async function fetchAll(brand, type, {
|
|
|
543
559
|
)
|
|
544
560
|
),
|
|
545
561
|
length_in_seconds
|
|
546
|
-
)
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
});
|
|
562
|
-
|
|
563
|
-
return fetchSanity(query, true);
|
|
562
|
+
),`
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
const filterWithRestrictions = await new FilterBuilder(filter, {
|
|
566
|
+
bypassStatuses: bypassStatusAndPublishedValidation,
|
|
567
|
+
bypassPermissions: bypassPermissions,
|
|
568
|
+
bypassPublishedDateRestriction: bypassStatusAndPublishedValidation,
|
|
569
|
+
}).buildFilter()
|
|
570
|
+
query = buildEntityAndTotalQuery(filterWithRestrictions, entityFieldsString, {
|
|
571
|
+
sortOrder: sortOrder,
|
|
572
|
+
start: start,
|
|
573
|
+
end: end,
|
|
574
|
+
})
|
|
575
|
+
|
|
576
|
+
return fetchSanity(query, true)
|
|
564
577
|
}
|
|
565
578
|
|
|
566
579
|
/**
|
|
@@ -580,83 +593,110 @@ export async function fetchAll(brand, type, {
|
|
|
580
593
|
* @param {string} [params.progress="all"] - An string representing which progress filter to use ("all", "in progress", "complete", "not started").
|
|
581
594
|
* @returns {Promise<Object|null>} - The fetched content data or null if not found.
|
|
582
595
|
*/
|
|
583
|
-
async function handleCustomFetchAll(
|
|
596
|
+
async function handleCustomFetchAll(
|
|
597
|
+
brand,
|
|
598
|
+
type,
|
|
599
|
+
{
|
|
584
600
|
page = 1,
|
|
585
601
|
limit = 10,
|
|
586
|
-
searchTerm =
|
|
587
|
-
sort =
|
|
602
|
+
searchTerm = '',
|
|
603
|
+
sort = '-published_on',
|
|
588
604
|
includedFields = [],
|
|
589
|
-
groupBy =
|
|
605
|
+
groupBy = '',
|
|
590
606
|
progressIds = undefined,
|
|
591
607
|
useDefaultFields = true,
|
|
592
608
|
customFields = [],
|
|
593
|
-
progress =
|
|
594
|
-
} = {}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
609
|
+
progress = 'all',
|
|
610
|
+
} = {}
|
|
611
|
+
) {
|
|
612
|
+
if (type === 'challenge') {
|
|
613
|
+
if (groupBy === 'completed') {
|
|
614
|
+
const completedIds = await fetchCompletedChallenges(brand, page, limit)
|
|
615
|
+
return fetchAll(brand, type, {
|
|
616
|
+
page,
|
|
617
|
+
limit,
|
|
618
|
+
searchTerm,
|
|
619
|
+
sort,
|
|
620
|
+
includedFields,
|
|
621
|
+
groupBy: '',
|
|
622
|
+
progressIds: completedIds,
|
|
623
|
+
useDefaultFields,
|
|
624
|
+
customFields,
|
|
625
|
+
progress,
|
|
626
|
+
})
|
|
627
|
+
} else if (groupBy === 'owned') {
|
|
628
|
+
const ownedIds = await fetchOwnedChallenges(brand, page, limit)
|
|
629
|
+
return fetchAll(brand, type, {
|
|
630
|
+
page,
|
|
631
|
+
limit,
|
|
632
|
+
searchTerm,
|
|
633
|
+
sort,
|
|
634
|
+
includedFields,
|
|
635
|
+
groupBy: '',
|
|
636
|
+
progressIds: ownedIds,
|
|
637
|
+
useDefaultFields,
|
|
638
|
+
customFields,
|
|
639
|
+
progress,
|
|
640
|
+
})
|
|
641
|
+
} else if (groupBy === 'difficulty_string') {
|
|
642
|
+
return fetchChallengesByDifficulty(
|
|
643
|
+
brand,
|
|
644
|
+
type,
|
|
645
|
+
page,
|
|
646
|
+
limit,
|
|
647
|
+
searchTerm,
|
|
648
|
+
sort,
|
|
649
|
+
includedFields,
|
|
650
|
+
groupBy,
|
|
651
|
+
progressIds,
|
|
652
|
+
useDefaultFields,
|
|
653
|
+
customFields,
|
|
654
|
+
progress
|
|
655
|
+
)
|
|
629
656
|
}
|
|
630
|
-
|
|
657
|
+
}
|
|
658
|
+
return null
|
|
631
659
|
}
|
|
632
660
|
|
|
633
|
-
async function fetchChallengesByDifficulty(
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
661
|
+
async function fetchChallengesByDifficulty(
|
|
662
|
+
brand,
|
|
663
|
+
type,
|
|
664
|
+
page,
|
|
665
|
+
limit,
|
|
666
|
+
searchTerm,
|
|
667
|
+
sort,
|
|
668
|
+
includedFields,
|
|
669
|
+
groupBy,
|
|
670
|
+
progressIds,
|
|
671
|
+
useDefaultFields,
|
|
672
|
+
customFields,
|
|
673
|
+
progress
|
|
674
|
+
) {
|
|
675
|
+
let config = contentTypeConfig['challenge'] ?? {}
|
|
676
|
+
let additionalFields = config?.fields ?? []
|
|
643
677
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
678
|
+
// Construct the search filter
|
|
679
|
+
const searchFilter = searchTerm
|
|
680
|
+
? groupBy !== ''
|
|
681
|
+
? `&& (^.name match "${searchTerm}*" || title match "${searchTerm}*")`
|
|
682
|
+
: `&& (artist->name match "${searchTerm}*" || instructor[]->name match "${searchTerm}*" || title match "${searchTerm}*" || name match "${searchTerm}*")`
|
|
683
|
+
: ''
|
|
648
684
|
|
|
649
|
-
|
|
650
|
-
|
|
685
|
+
// Construct the included fields filter, replacing 'difficulty' with 'difficulty_string'
|
|
686
|
+
const includedFieldsFilter = includedFields.length > 0 ? filtersToGroq(includedFields) : ''
|
|
651
687
|
|
|
652
|
-
|
|
653
|
-
|
|
688
|
+
// limits the results to supplied progressIds for started & completed filters
|
|
689
|
+
const progressFilter = await getProgressFilter(progress, progressIds)
|
|
654
690
|
|
|
691
|
+
let fields = useDefaultFields
|
|
692
|
+
? customFields.concat(DEFAULT_FIELDS, additionalFields)
|
|
693
|
+
: customFields
|
|
694
|
+
let fieldsString = fields.join(',')
|
|
655
695
|
|
|
656
|
-
|
|
657
|
-
|
|
696
|
+
const lessonsFilter = `_type == 'challenge' && brand == '${brand}' && ^.name == difficulty_string ${searchFilter} ${includedFieldsFilter} ${progressFilter}`
|
|
697
|
+
const lessonsFilterWithRestrictions = await new FilterBuilder(lessonsFilter).buildFilter()
|
|
658
698
|
|
|
659
|
-
|
|
699
|
+
const query = `{
|
|
660
700
|
"entity": [
|
|
661
701
|
{"name": "All"},
|
|
662
702
|
{"name": "Novice"},
|
|
@@ -674,62 +714,61 @@ async function fetchChallengesByDifficulty(brand, type, page, limit, searchTerm,
|
|
|
674
714
|
}[0...20]
|
|
675
715
|
},
|
|
676
716
|
"total": 0
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
717
|
+
}`
|
|
718
|
+
let data = await fetchSanity(query, true)
|
|
719
|
+
data.entity = data.entity.filter(function (difficulty) {
|
|
720
|
+
return difficulty.lessons.length > 0
|
|
721
|
+
})
|
|
722
|
+
return data
|
|
683
723
|
}
|
|
684
724
|
|
|
685
725
|
async function getProgressFilter(progress, progressIds) {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
return `&& railcontent_id in [${ids.join(',')}]`;
|
|
697
|
-
}
|
|
698
|
-
case "not started": {
|
|
699
|
-
const ids = await getAllStartedOrCompleted();
|
|
700
|
-
return `&& !(railcontent_id in [${ids.join(',')}])`;
|
|
701
|
-
}
|
|
702
|
-
default:
|
|
703
|
-
throw new Error(`'${progress}' progress option not implemented`);
|
|
726
|
+
switch (progress) {
|
|
727
|
+
case 'all':
|
|
728
|
+
return progressIds !== undefined ? `&& railcontent_id in [${progressIds.join(',')}]` : ''
|
|
729
|
+
case 'in progress': {
|
|
730
|
+
const ids = await getAllStarted()
|
|
731
|
+
return `&& railcontent_id in [${ids.join(',')}]`
|
|
732
|
+
}
|
|
733
|
+
case 'completed': {
|
|
734
|
+
const ids = await getAllCompleted()
|
|
735
|
+
return `&& railcontent_id in [${ids.join(',')}]`
|
|
704
736
|
}
|
|
737
|
+
case 'not started': {
|
|
738
|
+
const ids = await getAllStartedOrCompleted()
|
|
739
|
+
return `&& !(railcontent_id in [${ids.join(',')}])`
|
|
740
|
+
}
|
|
741
|
+
default:
|
|
742
|
+
throw new Error(`'${progress}' progress option not implemented`)
|
|
743
|
+
}
|
|
705
744
|
}
|
|
706
745
|
|
|
707
746
|
export function getSortOrder(sort = '-published_on', brand, groupBy) {
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
747
|
+
// Determine the sort order
|
|
748
|
+
let sortOrder = ''
|
|
749
|
+
const isDesc = sort.startsWith('-')
|
|
750
|
+
sort = isDesc ? sort.substring(1) : sort
|
|
751
|
+
switch (sort) {
|
|
752
|
+
case 'slug':
|
|
753
|
+
sortOrder = groupBy ? 'name' : 'title'
|
|
754
|
+
break
|
|
755
|
+
case 'name':
|
|
756
|
+
sortOrder = sort
|
|
757
|
+
break
|
|
758
|
+
case 'popularity':
|
|
759
|
+
if (groupBy == 'artist' || groupBy == 'genre') {
|
|
760
|
+
sortOrder = isDesc ? `coalesce(popularity.${brand}, -1)` : 'popularity'
|
|
761
|
+
} else {
|
|
762
|
+
sortOrder = isDesc ? 'coalesce(popularity, -1)' : 'popularity'
|
|
763
|
+
}
|
|
764
|
+
break
|
|
765
|
+
case 'published_on':
|
|
766
|
+
default:
|
|
767
|
+
sortOrder = 'published_on'
|
|
768
|
+
break
|
|
769
|
+
}
|
|
770
|
+
sortOrder += isDesc ? ' desc' : ' asc'
|
|
771
|
+
return sortOrder
|
|
733
772
|
}
|
|
734
773
|
|
|
735
774
|
/**
|
|
@@ -764,42 +803,48 @@ export function getSortOrder(sort = '-published_on', brand, groupBy) {
|
|
|
764
803
|
* .catch(error => console.error(error));
|
|
765
804
|
*/
|
|
766
805
|
export async function fetchAllFilterOptions(
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
806
|
+
brand,
|
|
807
|
+
filters = [],
|
|
808
|
+
style,
|
|
809
|
+
artist,
|
|
810
|
+
contentType,
|
|
811
|
+
term,
|
|
812
|
+
progressIds,
|
|
813
|
+
coachId,
|
|
814
|
+
includeTabs = false
|
|
776
815
|
) {
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
816
|
+
if (coachId && contentType !== 'coach-lessons') {
|
|
817
|
+
throw new Error(
|
|
818
|
+
`Invalid contentType: '${contentType}' for coachId. It must be 'coach-lessons'.`
|
|
819
|
+
)
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
const includedFieldsFilter = filters?.length ? filtersToGroq(filters) : undefined
|
|
823
|
+
const progressFilter = progressIds ? `&& railcontent_id in [${progressIds.join(',')}]` : ''
|
|
824
|
+
const isAdmin = (await fetchUserPermissions()).isAdmin
|
|
825
|
+
|
|
826
|
+
const constructCommonFilter = (excludeFilter) => {
|
|
827
|
+
const filterWithoutOption = excludeFilter
|
|
828
|
+
? filtersToGroq(filters, excludeFilter)
|
|
829
|
+
: includedFieldsFilter
|
|
830
|
+
const statusFilter = ' && status == "published"'
|
|
831
|
+
const includeStatusFilter = !isAdmin && !['instructor', 'artist', 'genre'].includes(contentType)
|
|
832
|
+
|
|
833
|
+
return coachId
|
|
834
|
+
? `brand == '${brand}' && status == "published" && references(*[_type=='instructor' && railcontent_id == ${coachId}]._id) ${filterWithoutOption || ''} ${term ? ` && (title match "${term}" || album match "${term}" || artist->name match "${term}" || genre[]->name match "${term}")` : ''}`
|
|
835
|
+
: `_type == '${contentType}' && brand == "${brand}"${includeStatusFilter ? statusFilter : ''}${style && excludeFilter !== 'style' ? ` && '${style}' in genre[]->name` : ''}${artist && excludeFilter !== 'artist' ? ` && artist->name == '${artist}'` : ''} ${progressFilter} ${filterWithoutOption || ''} ${term ? ` && (title match "${term}" || album match "${term}" || artist->name match "${term}" || genre[]->name match "${term}")` : ''}`
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
const metaData = processMetadata(brand, contentType, true)
|
|
839
|
+
const allowableFilters = metaData?.allowableFilters || []
|
|
840
|
+
const tabs = metaData?.tabs || []
|
|
841
|
+
const catalogName = metaData?.shortname || metaData?.name
|
|
842
|
+
|
|
843
|
+
const dynamicFilterOptions = allowableFilters
|
|
844
|
+
.map((filter) => getFilterOptions(filter, constructCommonFilter(filter), contentType, brand))
|
|
845
|
+
.join(' ')
|
|
846
|
+
|
|
847
|
+
const query = `
|
|
803
848
|
{
|
|
804
849
|
"meta": {
|
|
805
850
|
"totalResults": count(*[${constructCommonFilter()}
|
|
@@ -808,11 +853,11 @@ export async function fetchAllFilterOptions(
|
|
|
808
853
|
${dynamicFilterOptions}
|
|
809
854
|
}
|
|
810
855
|
}
|
|
811
|
-
}
|
|
856
|
+
}`
|
|
812
857
|
|
|
813
|
-
|
|
858
|
+
const results = await fetchSanity(query, true, { processNeedAccess: false })
|
|
814
859
|
|
|
815
|
-
|
|
860
|
+
return includeTabs ? { ...results, tabs, catalogName } : results
|
|
816
861
|
}
|
|
817
862
|
|
|
818
863
|
/**
|
|
@@ -821,17 +866,17 @@ export async function fetchAllFilterOptions(
|
|
|
821
866
|
* @returns {Promise<Object|null>} - The fetched foundation data or null if not found.
|
|
822
867
|
*/
|
|
823
868
|
export async function fetchFoundation(slug) {
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
869
|
+
const filterParams = {}
|
|
870
|
+
const query = await buildQuery(
|
|
871
|
+
`_type == 'foundation' && slug.current == "${slug}"`,
|
|
872
|
+
filterParams,
|
|
873
|
+
getFieldsForContentType('foundation'),
|
|
874
|
+
{
|
|
875
|
+
sortOrder: 'published_on asc',
|
|
876
|
+
isSingle: true,
|
|
877
|
+
}
|
|
878
|
+
)
|
|
879
|
+
return fetchSanity(query, false)
|
|
835
880
|
}
|
|
836
881
|
|
|
837
882
|
/**
|
|
@@ -841,9 +886,9 @@ export async function fetchFoundation(slug) {
|
|
|
841
886
|
* @returns {Promise<Object|null>} - The fetched methods data or null if not found.
|
|
842
887
|
*/
|
|
843
888
|
export async function fetchMethod(brand, slug) {
|
|
844
|
-
|
|
889
|
+
const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
|
|
845
890
|
|
|
846
|
-
|
|
891
|
+
const query = `*[_type == 'learning-path' && brand == "${brand}" && slug.current == "${slug}"] {
|
|
847
892
|
"description": ${descriptionField},
|
|
848
893
|
"instructors":instructor[]->name,
|
|
849
894
|
published_on,
|
|
@@ -880,7 +925,7 @@ export async function fetchMethod(brand, slug) {
|
|
|
880
925
|
total_xp
|
|
881
926
|
}
|
|
882
927
|
} | order(published_on asc)`
|
|
883
|
-
|
|
928
|
+
return fetchSanity(query, false)
|
|
884
929
|
}
|
|
885
930
|
|
|
886
931
|
/**
|
|
@@ -889,9 +934,9 @@ export async function fetchMethod(brand, slug) {
|
|
|
889
934
|
* @returns {Promise<Object|null>} - The fetched next lesson data or null if not found.
|
|
890
935
|
*/
|
|
891
936
|
export async function fetchMethodChildren(railcontentId) {
|
|
892
|
-
|
|
937
|
+
const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
|
|
893
938
|
|
|
894
|
-
|
|
939
|
+
const query = `*[railcontent_id == ${railcontentId}]{
|
|
895
940
|
"child_count":coalesce(count(child[${childrenFilter}]->), 0),
|
|
896
941
|
"id": railcontent_id,
|
|
897
942
|
"description": ${descriptionField},
|
|
@@ -909,8 +954,8 @@ export async function fetchMethodChildren(railcontentId) {
|
|
|
909
954
|
'children': child[(${childrenFilter})]->{
|
|
910
955
|
${getFieldsForContentType('method')}
|
|
911
956
|
},
|
|
912
|
-
}[0..1]
|
|
913
|
-
|
|
957
|
+
}[0..1]`
|
|
958
|
+
return fetchSanity(query, true)
|
|
914
959
|
}
|
|
915
960
|
|
|
916
961
|
/**
|
|
@@ -924,21 +969,21 @@ export async function fetchMethodChildren(railcontentId) {
|
|
|
924
969
|
* .catch(error => console.error(error));
|
|
925
970
|
*/
|
|
926
971
|
export async function fetchMethodPreviousNextLesson(railcontentId, methodId) {
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
972
|
+
const sortedChildren = await fetchMethodChildrenIds(methodId)
|
|
973
|
+
const index = sortedChildren.indexOf(Number(railcontentId))
|
|
974
|
+
let nextId = sortedChildren[index + 1]
|
|
975
|
+
let previousId = sortedChildren[index - 1]
|
|
976
|
+
let ids = []
|
|
977
|
+
if (nextId) ids.push(nextId)
|
|
978
|
+
if (previousId) ids.push(previousId)
|
|
979
|
+
let nextPrev = await fetchByRailContentIds(ids)
|
|
980
|
+
const nextLesson = nextPrev.find((elem) => {
|
|
981
|
+
return elem['id'] === nextId
|
|
982
|
+
})
|
|
983
|
+
const prevLesson = nextPrev.find((elem) => {
|
|
984
|
+
return elem['id'] === previousId
|
|
985
|
+
})
|
|
986
|
+
return { nextLesson, prevLesson }
|
|
942
987
|
}
|
|
943
988
|
|
|
944
989
|
/**
|
|
@@ -947,9 +992,9 @@ export async function fetchMethodPreviousNextLesson(railcontentId, methodId) {
|
|
|
947
992
|
* @returns {Promise<Array<Object>|null>} - The fetched children data or null if not found.
|
|
948
993
|
*/
|
|
949
994
|
export async function fetchMethodChildrenIds(railcontentId) {
|
|
950
|
-
|
|
995
|
+
const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
|
|
951
996
|
|
|
952
|
-
|
|
997
|
+
const query = `*[ railcontent_id == ${railcontentId}]{
|
|
953
998
|
'children': child[${childrenFilter}]-> {
|
|
954
999
|
'id': railcontent_id,
|
|
955
1000
|
'type' : _type,
|
|
@@ -962,22 +1007,22 @@ export async function fetchMethodChildrenIds(railcontentId) {
|
|
|
962
1007
|
}
|
|
963
1008
|
}
|
|
964
1009
|
}
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
|
|
1010
|
+
}`
|
|
1011
|
+
let allChildren = await fetchSanity(query, false)
|
|
1012
|
+
return getChildrenToDepth(allChildren, 4)
|
|
968
1013
|
}
|
|
969
1014
|
|
|
970
1015
|
function getChildrenToDepth(parent, depth = 1) {
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1016
|
+
let allChildrenIds = []
|
|
1017
|
+
if (parent && parent['children'] && depth > 0) {
|
|
1018
|
+
parent['children'].forEach((child) => {
|
|
1019
|
+
if (!child['children']) {
|
|
1020
|
+
allChildrenIds.push(child['id'])
|
|
1021
|
+
}
|
|
1022
|
+
allChildrenIds = allChildrenIds.concat(getChildrenToDepth(child, depth - 1))
|
|
1023
|
+
})
|
|
1024
|
+
}
|
|
1025
|
+
return allChildrenIds
|
|
981
1026
|
}
|
|
982
1027
|
|
|
983
1028
|
/**
|
|
@@ -986,31 +1031,31 @@ function getChildrenToDepth(parent, depth = 1) {
|
|
|
986
1031
|
* @returns {Promise<Object|null>} - The fetched next and previous lesson data or null if found.
|
|
987
1032
|
*/
|
|
988
1033
|
export async function fetchNextPreviousLesson(railcontentId) {
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1034
|
+
const document = await fetchLessonContent(railcontentId)
|
|
1035
|
+
if (document.parent_content_data && document.parent_content_data.length > 0) {
|
|
1036
|
+
const lastElement = document.parent_content_data[document.parent_content_data.length - 1]
|
|
1037
|
+
const results = await fetchMethodPreviousNextLesson(railcontentId, lastElement.id)
|
|
1038
|
+
return results
|
|
1039
|
+
}
|
|
1040
|
+
const processedData = processMetadata(document.brand, document.type, true)
|
|
1041
|
+
let sortBy = processedData?.sortBy ?? 'published_on'
|
|
1042
|
+
const isDesc = sortBy.startsWith('-')
|
|
1043
|
+
sortBy = isDesc ? sortBy.substring(1) : sortBy
|
|
1044
|
+
let sortValue = document[sortBy]
|
|
1045
|
+
if (sortValue == null) {
|
|
1046
|
+
sortBy = 'railcontent_id'
|
|
1047
|
+
sortValue = document['railcontent_id']
|
|
1048
|
+
}
|
|
1049
|
+
const isNumeric = !isNaN(sortValue)
|
|
1050
|
+
let prevComparison = isNumeric ? `${sortBy} <= ${sortValue}` : `${sortBy} <= "${sortValue}"`
|
|
1051
|
+
let nextComparison = isNumeric ? `${sortBy} >= ${sortValue}` : `${sortBy} >= "${sortValue}"`
|
|
1052
|
+
const fields = getFieldsForContentType(document.type)
|
|
1053
|
+
const query = `{
|
|
1009
1054
|
"prevLesson": *[brand == "${document.brand}" && status == "${document.status}" && _type == "${document.type}" && ${prevComparison} && railcontent_id != ${railcontentId}] | order(${sortBy} desc){${fields}}[0...1][0],
|
|
1010
1055
|
"nextLesson": *[brand == "${document.brand}" && status == "${document.status}" && _type == "${document.type}" && ${nextComparison} && railcontent_id != ${railcontentId}] | order(${sortBy} asc){${fields}}[0...1][0]
|
|
1011
|
-
}
|
|
1056
|
+
}`
|
|
1012
1057
|
|
|
1013
|
-
|
|
1058
|
+
return await fetchSanity(query, true)
|
|
1014
1059
|
}
|
|
1015
1060
|
|
|
1016
1061
|
/**
|
|
@@ -1023,12 +1068,12 @@ export async function fetchNextPreviousLesson(railcontentId) {
|
|
|
1023
1068
|
* .catch(error => console.error(error));
|
|
1024
1069
|
*/
|
|
1025
1070
|
export async function jumpToContinueContent(railcontentId) {
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1071
|
+
const nextContent = await fetchNextContentDataForParent(railcontentId)
|
|
1072
|
+
if (!nextContent || !nextContent.id) {
|
|
1073
|
+
return null
|
|
1074
|
+
}
|
|
1075
|
+
let next = await fetchByRailContentId(nextContent.id, nextContent.type)
|
|
1076
|
+
return { next }
|
|
1032
1077
|
}
|
|
1033
1078
|
|
|
1034
1079
|
/**
|
|
@@ -1042,11 +1087,11 @@ export async function jumpToContinueContent(railcontentId) {
|
|
|
1042
1087
|
* .catch(error => console.error(error));
|
|
1043
1088
|
*/
|
|
1044
1089
|
export async function fetchLessonContent(railContentId) {
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1090
|
+
const filterParams = { isSingle: true, pullFutureContent: true }
|
|
1091
|
+
// Format changes made to the `fields` object may also need to be reflected in Musora-web-platform SanityGateway.php $fields object
|
|
1092
|
+
// Currently only for challenges and challenge lessons
|
|
1093
|
+
// If you're unsure, message Adrian, or just add them.
|
|
1094
|
+
const fields = `title,
|
|
1050
1095
|
published_on,
|
|
1051
1096
|
"type":_type,
|
|
1052
1097
|
"resources": ${resourcesField},
|
|
@@ -1094,16 +1139,11 @@ export async function fetchLessonContent(railContentId) {
|
|
|
1094
1139
|
"type": *[railcontent_id == ^.id][0]._type,
|
|
1095
1140
|
},
|
|
1096
1141
|
sort,
|
|
1097
|
-
xp
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
{
|
|
1103
|
-
isSingle: true,
|
|
1104
|
-
}
|
|
1105
|
-
);
|
|
1106
|
-
return fetchSanity(query, false);
|
|
1142
|
+
xp`
|
|
1143
|
+
const query = await buildQuery(`railcontent_id == ${railContentId}`, filterParams, fields, {
|
|
1144
|
+
isSingle: true,
|
|
1145
|
+
})
|
|
1146
|
+
return fetchSanity(query, false)
|
|
1107
1147
|
}
|
|
1108
1148
|
|
|
1109
1149
|
/**
|
|
@@ -1113,14 +1153,22 @@ export async function fetchLessonContent(railContentId) {
|
|
|
1113
1153
|
* @returns {Promise<Array<Object>|null>} - The fetched related lessons data or null if not found.
|
|
1114
1154
|
*/
|
|
1115
1155
|
export async function fetchRelatedLessons(railContentId, brand) {
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1156
|
+
const filterSameTypeAndSortOrder = await new FilterBuilder(
|
|
1157
|
+
`_type==^._type && _type in ${JSON.stringify(typeWithSortOrder)} && brand == "${brand}" && railcontent_id !=${railContentId}`
|
|
1158
|
+
).buildFilter()
|
|
1159
|
+
const filterSameType = await new FilterBuilder(
|
|
1160
|
+
`_type==^._type && !(_type in ${JSON.stringify(typeWithSortOrder)}) && !(defined(parent_type)) && brand == "${brand}" && railcontent_id !=${railContentId}`
|
|
1161
|
+
).buildFilter()
|
|
1162
|
+
const filterSongSameArtist = await new FilterBuilder(
|
|
1163
|
+
`_type=="song" && _type==^._type && brand == "${brand}" && references(^.artist->_id) && railcontent_id !=${railContentId}`
|
|
1164
|
+
).buildFilter()
|
|
1165
|
+
const filterSongSameGenre = await new FilterBuilder(
|
|
1166
|
+
`_type=="song" && _type==^._type && brand == "${brand}" && references(^.genre[]->_id) && railcontent_id !=${railContentId}`
|
|
1167
|
+
).buildFilter()
|
|
1168
|
+
const filterNeighbouringSiblings = await new FilterBuilder(`references(^._id)`).buildFilter()
|
|
1169
|
+
const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
|
|
1170
|
+
|
|
1171
|
+
const query = `*[railcontent_id == ${railContentId} && brand == "${brand}" && (!defined(permission) || references(*[_type=='permission']._id))]{
|
|
1124
1172
|
_type, parent_type, railcontent_id,
|
|
1125
1173
|
"related_lessons" : array::unique([
|
|
1126
1174
|
...(*[${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}),
|
|
@@ -1129,8 +1177,8 @@ export async function fetchRelatedLessons(railContentId, brand) {
|
|
|
1129
1177
|
...(*[${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]),
|
|
1130
1178
|
...(*[${filterSameType}]{_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])
|
|
1131
1179
|
,
|
|
1132
|
-
])[0...10]}
|
|
1133
|
-
|
|
1180
|
+
])[0...10]}`
|
|
1181
|
+
return fetchSanity(query, false)
|
|
1134
1182
|
}
|
|
1135
1183
|
|
|
1136
1184
|
/**
|
|
@@ -1142,26 +1190,27 @@ export async function fetchRelatedLessons(railContentId, brand) {
|
|
|
1142
1190
|
* @param {number} [params.limit=10] - The number of items per page.
|
|
1143
1191
|
* @returns {Promise<Array<Object>|null>} - The fetched pack content data or null if not found.
|
|
1144
1192
|
*/
|
|
1145
|
-
export async function fetchAllPacks(
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1193
|
+
export async function fetchAllPacks(
|
|
1194
|
+
brand,
|
|
1195
|
+
sort = '-published_on',
|
|
1196
|
+
searchTerm = '',
|
|
1197
|
+
page = 1,
|
|
1198
|
+
limit = 10
|
|
1199
|
+
) {
|
|
1200
|
+
const sortOrder = getSortOrder(sort, brand)
|
|
1201
|
+
const filter = `(_type == 'pack' || _type == 'semester-pack') && brand == '${brand}' && title match "${searchTerm}*"`
|
|
1202
|
+
const filterParams = {}
|
|
1203
|
+
const fields = getFieldsForContentType('pack')
|
|
1204
|
+
const start = (page - 1) * limit
|
|
1205
|
+
const end = start + limit
|
|
1206
|
+
|
|
1207
|
+
const query = await buildQuery(filter, filterParams, getFieldsForContentType('pack'), {
|
|
1208
|
+
logo_image_url: 'logo_image_url.asset->url',
|
|
1209
|
+
sortOrder: sortOrder,
|
|
1210
|
+
start,
|
|
1211
|
+
end,
|
|
1212
|
+
})
|
|
1213
|
+
return fetchSanity(query, true)
|
|
1165
1214
|
}
|
|
1166
1215
|
|
|
1167
1216
|
/**
|
|
@@ -1170,38 +1219,38 @@ export async function fetchAllPacks(brand, sort = "-published_on", searchTerm =
|
|
|
1170
1219
|
* @returns {Promise<Array<Object>|null>} - The fetched pack content data or null if not found.
|
|
1171
1220
|
*/
|
|
1172
1221
|
export async function fetchPackAll(railcontentId, type = 'pack') {
|
|
1173
|
-
|
|
1222
|
+
return fetchByRailContentId(railcontentId, type)
|
|
1174
1223
|
}
|
|
1175
1224
|
|
|
1176
1225
|
export async function fetchLiveEvent(brand) {
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1226
|
+
//calendarIDs taken from addevent.php
|
|
1227
|
+
// TODO import instructor calendars to Sanity
|
|
1228
|
+
let defaultCalendarID = ''
|
|
1229
|
+
switch (brand) {
|
|
1230
|
+
case 'drumeo':
|
|
1231
|
+
defaultCalendarID = 'GP142387'
|
|
1232
|
+
break
|
|
1233
|
+
case 'pianote':
|
|
1234
|
+
defaultCalendarID = 'be142408'
|
|
1235
|
+
break
|
|
1236
|
+
case 'guitareo':
|
|
1237
|
+
defaultCalendarID = 'IJ142407'
|
|
1238
|
+
break
|
|
1239
|
+
case 'singeo':
|
|
1240
|
+
defaultCalendarID = 'bk354284'
|
|
1241
|
+
break
|
|
1242
|
+
default:
|
|
1243
|
+
break
|
|
1244
|
+
}
|
|
1245
|
+
let startDateTemp = new Date()
|
|
1246
|
+
let endDateTemp = new Date()
|
|
1247
|
+
startDateTemp = new Date(startDateTemp.setMinutes(startDateTemp.getMinutes() + 15))
|
|
1248
|
+
endDateTemp = new Date(endDateTemp.setMinutes(endDateTemp.getMinutes() - 15))
|
|
1249
|
+
|
|
1250
|
+
// See LiveStreamEventService.getCurrentOrNextLiveEvent for some nice complicated logic which I don't think is actually importart
|
|
1251
|
+
// this has some +- on times
|
|
1252
|
+
// But this query just finds the first scheduled event (sorted by start_time) that ends after now()
|
|
1253
|
+
const query = `*[status == 'scheduled' && brand == '${brand}' && defined(live_event_start_time) && live_event_start_time <= '${getSanityDate(startDateTemp, false)}' && live_event_end_time >= '${getSanityDate(endDateTemp, false)}']{
|
|
1205
1254
|
'slug': slug.current,
|
|
1206
1255
|
'id': railcontent_id,
|
|
1207
1256
|
live_event_start_time,
|
|
@@ -1218,8 +1267,8 @@ export async function fetchLiveEvent(brand) {
|
|
|
1218
1267
|
web_url_path,
|
|
1219
1268
|
},
|
|
1220
1269
|
'videoId': coalesce(live_event_youtube_id, video.external_id),
|
|
1221
|
-
} | order(live_event_start_time)[0...1]
|
|
1222
|
-
|
|
1270
|
+
} | order(live_event_start_time)[0...1]`
|
|
1271
|
+
return await fetchSanity(query, false, { processNeedAccess: false })
|
|
1223
1272
|
}
|
|
1224
1273
|
|
|
1225
1274
|
/**
|
|
@@ -1233,10 +1282,10 @@ export async function fetchLiveEvent(brand) {
|
|
|
1233
1282
|
* .catch(error => console.error(error));
|
|
1234
1283
|
*/
|
|
1235
1284
|
export async function fetchPackData(id) {
|
|
1236
|
-
|
|
1237
|
-
${getFieldsForContentType(
|
|
1238
|
-
} [0...1]
|
|
1239
|
-
|
|
1285
|
+
const query = `*[railcontent_id == ${id}]{
|
|
1286
|
+
${getFieldsForContentType('pack')}
|
|
1287
|
+
} [0...1]`
|
|
1288
|
+
return fetchSanity(query, false)
|
|
1240
1289
|
}
|
|
1241
1290
|
|
|
1242
1291
|
/**
|
|
@@ -1256,34 +1305,26 @@ export async function fetchPackData(id) {
|
|
|
1256
1305
|
* .then(lessons => console.log(lessons))
|
|
1257
1306
|
* .catch(error => console.error(error));
|
|
1258
1307
|
*/
|
|
1259
|
-
export async function fetchCoachLessons(
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
fieldsString,
|
|
1280
|
-
{
|
|
1281
|
-
sortOrder: sortOrder,
|
|
1282
|
-
start: start,
|
|
1283
|
-
end: end,
|
|
1284
|
-
},
|
|
1285
|
-
);
|
|
1286
|
-
return fetchSanity(query, true);
|
|
1308
|
+
export async function fetchCoachLessons(
|
|
1309
|
+
brand,
|
|
1310
|
+
id,
|
|
1311
|
+
{ sortOrder = '-published_on', searchTerm = '', page = 1, limit = 20, includedFields = [] } = {}
|
|
1312
|
+
) {
|
|
1313
|
+
const fieldsString = getFieldsForContentType()
|
|
1314
|
+
const start = (page - 1) * limit
|
|
1315
|
+
const end = start + limit
|
|
1316
|
+
const searchFilter = searchTerm ? `&& title match "${searchTerm}*"` : ''
|
|
1317
|
+
const includedFieldsFilter = includedFields.length > 0 ? filtersToGroq(includedFields) : ''
|
|
1318
|
+
const filter = `brand == '${brand}' ${searchFilter} ${includedFieldsFilter} && references(*[_type=='instructor' && railcontent_id == ${id}]._id)`
|
|
1319
|
+
const filterWithRestrictions = await new FilterBuilder(filter).buildFilter()
|
|
1320
|
+
|
|
1321
|
+
sortOrder = getSortOrder(sortOrder, brand)
|
|
1322
|
+
const query = buildEntityAndTotalQuery(filterWithRestrictions, fieldsString, {
|
|
1323
|
+
sortOrder: sortOrder,
|
|
1324
|
+
start: start,
|
|
1325
|
+
end: end,
|
|
1326
|
+
})
|
|
1327
|
+
return fetchSanity(query, true)
|
|
1287
1328
|
}
|
|
1288
1329
|
|
|
1289
1330
|
/**
|
|
@@ -1297,15 +1338,15 @@ export async function fetchCoachLessons(brand, id, {
|
|
|
1297
1338
|
* .catch(error => console.error(error));
|
|
1298
1339
|
*/
|
|
1299
1340
|
export async function fetchParentForDownload(id) {
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1341
|
+
const query = buildRawQuery(
|
|
1342
|
+
`railcontent_id == ${id}`,
|
|
1343
|
+
getFieldsForContentType('parent-download'),
|
|
1344
|
+
{
|
|
1345
|
+
isSingle: true,
|
|
1346
|
+
}
|
|
1347
|
+
)
|
|
1348
|
+
|
|
1349
|
+
return fetchSanity(query, false)
|
|
1309
1350
|
}
|
|
1310
1351
|
|
|
1311
1352
|
/**
|
|
@@ -1319,33 +1360,24 @@ export async function fetchParentForDownload(id) {
|
|
|
1319
1360
|
* .then(lessons => console.log(lessons))
|
|
1320
1361
|
* .catch(error => console.error(error));
|
|
1321
1362
|
*/
|
|
1322
|
-
export async function fetchByReference(
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
filterWithRestrictions,
|
|
1341
|
-
fieldsString,
|
|
1342
|
-
{
|
|
1343
|
-
sortOrder: getSortOrder(sortOrder, brand),
|
|
1344
|
-
start: start,
|
|
1345
|
-
end: end,
|
|
1346
|
-
},
|
|
1347
|
-
);
|
|
1348
|
-
return fetchSanity(query, true);
|
|
1363
|
+
export async function fetchByReference(
|
|
1364
|
+
brand,
|
|
1365
|
+
{ sortOrder = '-published_on', searchTerm = '', page = 1, limit = 20, includedFields = [] } = {}
|
|
1366
|
+
) {
|
|
1367
|
+
const fieldsString = getFieldsForContentType()
|
|
1368
|
+
const start = (page - 1) * limit
|
|
1369
|
+
const end = start + limit
|
|
1370
|
+
const searchFilter = searchTerm ? `&& title match "${searchTerm}*"` : ''
|
|
1371
|
+
const includedFieldsFilter = includedFields.length > 0 ? includedFields.join(' && ') : ''
|
|
1372
|
+
|
|
1373
|
+
const filter = `brand == '${brand}' ${searchFilter} && references(*[${includedFieldsFilter}]._id)`
|
|
1374
|
+
const filterWithRestrictions = await new FilterBuilder(filter).buildFilter()
|
|
1375
|
+
const query = buildEntityAndTotalQuery(filterWithRestrictions, fieldsString, {
|
|
1376
|
+
sortOrder: getSortOrder(sortOrder, brand),
|
|
1377
|
+
start: start,
|
|
1378
|
+
end: end,
|
|
1379
|
+
})
|
|
1380
|
+
return fetchSanity(query, true)
|
|
1349
1381
|
}
|
|
1350
1382
|
|
|
1351
1383
|
/**
|
|
@@ -1367,29 +1399,37 @@ export async function fetchByReference(brand, {
|
|
|
1367
1399
|
* .then(lessons => console.log(lessons))
|
|
1368
1400
|
* .catch(error => console.error(error));
|
|
1369
1401
|
*/
|
|
1370
|
-
export async function fetchArtistLessons(
|
|
1402
|
+
export async function fetchArtistLessons(
|
|
1403
|
+
brand,
|
|
1404
|
+
name,
|
|
1405
|
+
contentType,
|
|
1406
|
+
{
|
|
1371
1407
|
sort = '-published_on',
|
|
1372
1408
|
searchTerm = '',
|
|
1373
1409
|
page = 1,
|
|
1374
1410
|
limit = 10,
|
|
1375
1411
|
includedFields = [],
|
|
1376
1412
|
progressIds = undefined,
|
|
1377
|
-
} = {}
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1413
|
+
} = {}
|
|
1414
|
+
) {
|
|
1415
|
+
const fieldsString = DEFAULT_FIELDS.join(',')
|
|
1416
|
+
const start = (page - 1) * limit
|
|
1417
|
+
const end = start + limit
|
|
1418
|
+
const searchFilter = searchTerm ? `&& title match "${searchTerm}*"` : ''
|
|
1419
|
+
const sortOrder = getSortOrder(sort, brand)
|
|
1420
|
+
const addType =
|
|
1421
|
+
contentType && Array.isArray(contentType)
|
|
1422
|
+
? `_type in ['${contentType.join("', '")}'] &&`
|
|
1423
|
+
: contentType
|
|
1424
|
+
? `_type == '${contentType}' && `
|
|
1425
|
+
: ''
|
|
1426
|
+
const includedFieldsFilter = includedFields.length > 0 ? filtersToGroq(includedFields) : ''
|
|
1427
|
+
|
|
1428
|
+
// limits the results to supplied progressIds for started & completed filters
|
|
1429
|
+
const progressFilter =
|
|
1430
|
+
progressIds !== undefined ? `&& railcontent_id in [${progressIds.join(',')}]` : ''
|
|
1431
|
+
const now = getSanityDate(new Date())
|
|
1432
|
+
const query = `{
|
|
1393
1433
|
"entity":
|
|
1394
1434
|
*[_type == 'artist' && name == '${name}']
|
|
1395
1435
|
{'type': _type, name, 'thumbnail_url':thumbnail_url.asset->url,
|
|
@@ -1397,8 +1437,8 @@ export async function fetchArtistLessons(brand, name, contentType, {
|
|
|
1397
1437
|
'lessons': *[${addType} brand == '${brand}' && references(^._id) && (status in ['published'] || (status == 'scheduled' && defined(published_on) && published_on >= '${now}')) ${searchFilter} ${includedFieldsFilter} ${progressFilter}]{${fieldsString}}
|
|
1398
1438
|
[${start}...${end}]}
|
|
1399
1439
|
|order(${sortOrder})
|
|
1400
|
-
}
|
|
1401
|
-
|
|
1440
|
+
}`
|
|
1441
|
+
return fetchSanity(query, true)
|
|
1402
1442
|
}
|
|
1403
1443
|
|
|
1404
1444
|
/**
|
|
@@ -1419,27 +1459,31 @@ export async function fetchArtistLessons(brand, name, contentType, {
|
|
|
1419
1459
|
* .then(lessons => console.log(lessons))
|
|
1420
1460
|
* .catch(error => console.error(error));
|
|
1421
1461
|
*/
|
|
1422
|
-
export async function fetchGenreLessons(
|
|
1462
|
+
export async function fetchGenreLessons(
|
|
1463
|
+
brand,
|
|
1464
|
+
name,
|
|
1465
|
+
contentType,
|
|
1466
|
+
{
|
|
1423
1467
|
sort = '-published_on',
|
|
1424
1468
|
searchTerm = '',
|
|
1425
1469
|
page = 1,
|
|
1426
1470
|
limit = 10,
|
|
1427
1471
|
includedFields = [],
|
|
1428
1472
|
progressIds = undefined,
|
|
1429
|
-
} = {}
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1473
|
+
} = {}
|
|
1474
|
+
) {
|
|
1475
|
+
const fieldsString = DEFAULT_FIELDS.join(',')
|
|
1476
|
+
const start = (page - 1) * limit
|
|
1477
|
+
const end = start + limit
|
|
1478
|
+
const searchFilter = searchTerm ? `&& title match "${searchTerm}*"` : ''
|
|
1479
|
+
const sortOrder = getSortOrder(sort, brand)
|
|
1480
|
+
const addType = contentType ? `_type == '${contentType}' && ` : ''
|
|
1481
|
+
const includedFieldsFilter = includedFields.length > 0 ? filtersToGroq(includedFields) : ''
|
|
1482
|
+
// limits the results to supplied progressIds for started & completed filters
|
|
1483
|
+
const progressFilter =
|
|
1484
|
+
progressIds !== undefined ? `&& railcontent_id in [${progressIds.join(',')}]` : ''
|
|
1485
|
+
const now = getSanityDate(new Date())
|
|
1486
|
+
const query = `{
|
|
1443
1487
|
"entity":
|
|
1444
1488
|
*[_type == 'genre' && name == '${name}']
|
|
1445
1489
|
{'type': _type, name, 'thumbnail_url':thumbnail_url.asset->url,
|
|
@@ -1447,14 +1491,14 @@ export async function fetchGenreLessons(brand, name, contentType, {
|
|
|
1447
1491
|
'lessons': *[${addType} brand == '${brand}' && references(^._id) && (status in ['published'] || (status == 'scheduled' && defined(published_on) && published_on >= '${now}')) ${searchFilter} ${includedFieldsFilter} ${progressFilter}]{${fieldsString}}
|
|
1448
1492
|
[${start}...${end}]}
|
|
1449
1493
|
|order(${sortOrder})
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1494
|
+
}`
|
|
1495
|
+
return fetchSanity(query, true)
|
|
1452
1496
|
}
|
|
1453
1497
|
|
|
1454
1498
|
export async function fetchTopLevelParentId(railcontentId) {
|
|
1455
|
-
|
|
1499
|
+
const statusFilter = "&& status in ['scheduled', 'published', 'archived', 'unlisted']"
|
|
1456
1500
|
|
|
1457
|
-
|
|
1501
|
+
const query = `*[railcontent_id == ${railcontentId}]{
|
|
1458
1502
|
railcontent_id,
|
|
1459
1503
|
'parents': *[^._id in child[]._ref ${statusFilter}]{
|
|
1460
1504
|
railcontent_id,
|
|
@@ -1468,24 +1512,24 @@ export async function fetchTopLevelParentId(railcontentId) {
|
|
|
1468
1512
|
}
|
|
1469
1513
|
}
|
|
1470
1514
|
}
|
|
1471
|
-
}
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
}
|
|
1515
|
+
}`
|
|
1516
|
+
let response = await fetchSanity(query, false, { processNeedAccess: false })
|
|
1517
|
+
if (!response) return null
|
|
1518
|
+
let currentLevel = response
|
|
1519
|
+
for (let i = 0; i < 4; i++) {
|
|
1520
|
+
if (currentLevel['parents'].length > 0) {
|
|
1521
|
+
currentLevel = currentLevel['parents'][0]
|
|
1522
|
+
} else {
|
|
1523
|
+
return currentLevel['railcontent_id']
|
|
1481
1524
|
}
|
|
1482
|
-
|
|
1525
|
+
}
|
|
1526
|
+
return null
|
|
1483
1527
|
}
|
|
1484
1528
|
|
|
1485
1529
|
export async function fetchHierarchy(railcontentId) {
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1530
|
+
let topLevelId = await fetchTopLevelParentId(railcontentId)
|
|
1531
|
+
const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
|
|
1532
|
+
const query = `*[railcontent_id == ${topLevelId}]{
|
|
1489
1533
|
railcontent_id,
|
|
1490
1534
|
'assignments': assignment[]{railcontent_id},
|
|
1491
1535
|
'children': child[${childrenFilter}]->{
|
|
@@ -1503,42 +1547,40 @@ export async function fetchHierarchy(railcontentId) {
|
|
|
1503
1547
|
}
|
|
1504
1548
|
}
|
|
1505
1549
|
},
|
|
1506
|
-
}
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1550
|
+
}`
|
|
1551
|
+
let response = await fetchSanity(query, false, { processNeedAccess: false })
|
|
1552
|
+
if (!response) return null
|
|
1553
|
+
let data = {
|
|
1554
|
+
topLevelId: topLevelId,
|
|
1555
|
+
parents: {},
|
|
1556
|
+
children: {},
|
|
1557
|
+
}
|
|
1558
|
+
populateHierarchyLookups(response, data, null)
|
|
1559
|
+
return data
|
|
1516
1560
|
}
|
|
1517
1561
|
|
|
1518
|
-
|
|
1519
1562
|
function populateHierarchyLookups(currentLevel, data, parentId) {
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
}
|
|
1529
|
-
} else {
|
|
1530
|
-
data.children[contentId] = [];
|
|
1531
|
-
}
|
|
1532
|
-
|
|
1533
|
-
let assignments = currentLevel['assignments'];
|
|
1534
|
-
if (assignments) {
|
|
1535
|
-
let assignmentIds = assignments.map(assignment => assignment['railcontent_id']);
|
|
1536
|
-
data.children[contentId] = (data.children[contentId] ?? []).concat(assignmentIds);
|
|
1537
|
-
assignmentIds.forEach(assignmentId => {
|
|
1538
|
-
data.parents[assignmentId] = contentId;
|
|
1539
|
-
});
|
|
1563
|
+
let contentId = currentLevel['railcontent_id']
|
|
1564
|
+
let children = currentLevel['children']
|
|
1565
|
+
|
|
1566
|
+
data.parents[contentId] = parentId
|
|
1567
|
+
if (children) {
|
|
1568
|
+
data.children[contentId] = children.map((child) => child['railcontent_id'])
|
|
1569
|
+
for (let i = 0; i < children.length; i++) {
|
|
1570
|
+
populateHierarchyLookups(children[i], data, contentId)
|
|
1540
1571
|
}
|
|
1541
|
-
|
|
1572
|
+
} else {
|
|
1573
|
+
data.children[contentId] = []
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
let assignments = currentLevel['assignments']
|
|
1577
|
+
if (assignments) {
|
|
1578
|
+
let assignmentIds = assignments.map((assignment) => assignment['railcontent_id'])
|
|
1579
|
+
data.children[contentId] = (data.children[contentId] ?? []).concat(assignmentIds)
|
|
1580
|
+
assignmentIds.forEach((assignmentId) => {
|
|
1581
|
+
data.parents[assignmentId] = contentId
|
|
1582
|
+
})
|
|
1583
|
+
}
|
|
1542
1584
|
}
|
|
1543
1585
|
|
|
1544
1586
|
/**
|
|
@@ -1548,27 +1590,28 @@ function populateHierarchyLookups(currentLevel, data, parentId) {
|
|
|
1548
1590
|
* @returns {Promise<Object|null>} - A promise that resolves to an object containing the data
|
|
1549
1591
|
*/
|
|
1550
1592
|
export async function fetchCommentModContentData(ids) {
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1593
|
+
const idsString = ids.join(',')
|
|
1594
|
+
const fields = `"id": railcontent_id, "type": _type, title, "url": web_url_path, "parent": *[^._id in child[]._ref]{"id": railcontent_id, title}`
|
|
1595
|
+
const query = await buildQuery(
|
|
1596
|
+
`railcontent_id in [${idsString}]`,
|
|
1597
|
+
{ bypassPermissions: true },
|
|
1598
|
+
fields,
|
|
1599
|
+
{ end: 50 }
|
|
1600
|
+
)
|
|
1601
|
+
let data = await fetchSanity(query, true)
|
|
1602
|
+
let mapped = {}
|
|
1603
|
+
data.forEach(function (content) {
|
|
1604
|
+
mapped[content.id] = {
|
|
1605
|
+
id: content.id,
|
|
1606
|
+
type: content.type,
|
|
1607
|
+
title: content.title,
|
|
1608
|
+
url: content.url,
|
|
1609
|
+
parentTitle: content.parent[0]?.title ?? null,
|
|
1610
|
+
}
|
|
1611
|
+
})
|
|
1612
|
+
return mapped
|
|
1569
1613
|
}
|
|
1570
1614
|
|
|
1571
|
-
|
|
1572
1615
|
/**
|
|
1573
1616
|
*
|
|
1574
1617
|
* @param {string} query - The GROQ query to execute against the Sanity API.
|
|
@@ -1584,110 +1627,110 @@ export async function fetchCommentModContentData(ids) {
|
|
|
1584
1627
|
* .catch(error => console.error(error));
|
|
1585
1628
|
*/
|
|
1586
1629
|
|
|
1587
|
-
export async function fetchSanity(
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
processNeedAccess = true,
|
|
1592
|
-
} = {}
|
|
1630
|
+
export async function fetchSanity(
|
|
1631
|
+
query,
|
|
1632
|
+
isList,
|
|
1633
|
+
{ customPostProcess = null, processNeedAccess = true } = {}
|
|
1593
1634
|
) {
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1635
|
+
// Check the config object before proceeding
|
|
1636
|
+
if (!checkSanityConfig(globalConfig)) {
|
|
1637
|
+
return null
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
if (globalConfig.sanityConfig.debug) {
|
|
1641
|
+
console.log('fetchSanity Query:', query)
|
|
1642
|
+
}
|
|
1643
|
+
const perspective = globalConfig.sanityConfig.perspective ?? 'published'
|
|
1644
|
+
const api = globalConfig.sanityConfig.useCachedAPI ? 'apicdn' : 'api'
|
|
1645
|
+
const url = `https://${globalConfig.sanityConfig.projectId}.${api}.sanity.io/v${globalConfig.sanityConfig.version}/data/query/${globalConfig.sanityConfig.dataset}?perspective=${perspective}`
|
|
1646
|
+
const headers = {
|
|
1647
|
+
Authorization: `Bearer ${globalConfig.sanityConfig.token}`,
|
|
1648
|
+
'Content-Type': 'application/json',
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
try {
|
|
1652
|
+
const method = 'post'
|
|
1653
|
+
const options = {
|
|
1654
|
+
method,
|
|
1655
|
+
headers,
|
|
1656
|
+
body: JSON.stringify({ query: query }),
|
|
1597
1657
|
}
|
|
1598
1658
|
|
|
1599
|
-
|
|
1600
|
-
|
|
1659
|
+
let promisesResult = await Promise.all([
|
|
1660
|
+
fetch(url, options),
|
|
1661
|
+
processNeedAccess ? fetchUserPermissions() : null,
|
|
1662
|
+
])
|
|
1663
|
+
const response = promisesResult[0]
|
|
1664
|
+
const userPermissions = promisesResult[1]?.permissions
|
|
1665
|
+
const isAdmin = promisesResult[1]?.isAdmin
|
|
1666
|
+
|
|
1667
|
+
if (!response.ok) {
|
|
1668
|
+
throw new Error(`Sanity API error: ${response.status} - ${response.statusText}`)
|
|
1601
1669
|
}
|
|
1602
|
-
const
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
headers,
|
|
1615
|
-
body: JSON.stringify({'query': query})
|
|
1616
|
-
};
|
|
1617
|
-
|
|
1618
|
-
let promisesResult = await Promise.all([
|
|
1619
|
-
fetch(url, options),
|
|
1620
|
-
processNeedAccess ? fetchUserPermissions() : null
|
|
1621
|
-
]);
|
|
1622
|
-
const response = promisesResult[0];
|
|
1623
|
-
const userPermissions = promisesResult[1]?.permissions;
|
|
1624
|
-
const isAdmin = promisesResult[1]?.isAdmin;
|
|
1625
|
-
|
|
1626
|
-
if (!response.ok) {
|
|
1627
|
-
throw new Error(`Sanity API error: ${response.status} - ${response.statusText}`);
|
|
1628
|
-
}
|
|
1629
|
-
const result = await response.json();
|
|
1630
|
-
if (result.result) {
|
|
1631
|
-
if (globalConfig.sanityConfig.debug) {
|
|
1632
|
-
console.log("fetchSanity Results:", result);
|
|
1633
|
-
}
|
|
1634
|
-
let results = isList ? result.result : result.result[0];
|
|
1635
|
-
results = processNeedAccess ? await needsAccessDecorator(results, userPermissions, isAdmin) : results;
|
|
1636
|
-
return customPostProcess ? customPostProcess(results) : results;
|
|
1637
|
-
} else {
|
|
1638
|
-
throw new Error('No results found');
|
|
1639
|
-
}
|
|
1640
|
-
} catch (error) {
|
|
1641
|
-
console.error('fetchSanity: Fetch error:', error);
|
|
1642
|
-
return null;
|
|
1670
|
+
const result = await response.json()
|
|
1671
|
+
if (result.result) {
|
|
1672
|
+
if (globalConfig.sanityConfig.debug) {
|
|
1673
|
+
console.log('fetchSanity Results:', result)
|
|
1674
|
+
}
|
|
1675
|
+
let results = isList ? result.result : result.result[0]
|
|
1676
|
+
results = processNeedAccess
|
|
1677
|
+
? await needsAccessDecorator(results, userPermissions, isAdmin)
|
|
1678
|
+
: results
|
|
1679
|
+
return customPostProcess ? customPostProcess(results) : results
|
|
1680
|
+
} else {
|
|
1681
|
+
throw new Error('No results found')
|
|
1643
1682
|
}
|
|
1683
|
+
} catch (error) {
|
|
1684
|
+
console.error('fetchSanity: Fetch error:', error)
|
|
1685
|
+
return null
|
|
1686
|
+
}
|
|
1644
1687
|
}
|
|
1645
1688
|
|
|
1646
1689
|
function needsAccessDecorator(results, userPermissions, isAdmin) {
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
});
|
|
1662
|
-
} else {
|
|
1663
|
-
result['need_access'] = doesUserNeedAccessToContent(result, userPermissions, isAdmin);
|
|
1664
|
-
}
|
|
1665
|
-
});
|
|
1666
|
-
} else if (results.related_lessons && Array.isArray(results.related_lessons)) {
|
|
1667
|
-
results.related_lessons.forEach((result) => {
|
|
1668
|
-
result['need_access'] = doesUserNeedAccessToContent(result, userPermissions, isAdmin);
|
|
1690
|
+
if (globalConfig.sanityConfig.useDummyRailContentMethods) return results
|
|
1691
|
+
|
|
1692
|
+
userPermissions = new Set(userPermissions)
|
|
1693
|
+
|
|
1694
|
+
if (Array.isArray(results)) {
|
|
1695
|
+
results.forEach((result) => {
|
|
1696
|
+
result['need_access'] = doesUserNeedAccessToContent(result, userPermissions, isAdmin)
|
|
1697
|
+
})
|
|
1698
|
+
} else if (results.entity && Array.isArray(results.entity)) {
|
|
1699
|
+
// Group By
|
|
1700
|
+
results.entity.forEach((result) => {
|
|
1701
|
+
if (result.lessons) {
|
|
1702
|
+
result.lessons.forEach((lesson) => {
|
|
1703
|
+
lesson['need_access'] = doesUserNeedAccessToContent(lesson, userPermissions, isAdmin) // Updated to check lesson access
|
|
1669
1704
|
})
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1705
|
+
} else {
|
|
1706
|
+
result['need_access'] = doesUserNeedAccessToContent(result, userPermissions, isAdmin)
|
|
1707
|
+
}
|
|
1708
|
+
})
|
|
1709
|
+
} else if (results.related_lessons && Array.isArray(results.related_lessons)) {
|
|
1710
|
+
results.related_lessons.forEach((result) => {
|
|
1711
|
+
result['need_access'] = doesUserNeedAccessToContent(result, userPermissions, isAdmin)
|
|
1712
|
+
})
|
|
1713
|
+
} else {
|
|
1714
|
+
results['need_access'] = doesUserNeedAccessToContent(results, userPermissions, isAdmin)
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
return results
|
|
1675
1718
|
}
|
|
1676
1719
|
|
|
1677
1720
|
function doesUserNeedAccessToContent(result, userPermissions, isAdmin) {
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1721
|
+
if (isAdmin ?? false) {
|
|
1722
|
+
return false
|
|
1723
|
+
}
|
|
1724
|
+
const permissions = new Set(result?.permission_id ?? [])
|
|
1725
|
+
if (permissions.size === 0) {
|
|
1726
|
+
return false
|
|
1727
|
+
}
|
|
1728
|
+
for (let permission of permissions) {
|
|
1729
|
+
if (userPermissions.has(permission)) {
|
|
1730
|
+
return false
|
|
1684
1731
|
}
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
return false;
|
|
1688
|
-
}
|
|
1689
|
-
}
|
|
1690
|
-
return true;
|
|
1732
|
+
}
|
|
1733
|
+
return true
|
|
1691
1734
|
}
|
|
1692
1735
|
|
|
1693
1736
|
/**
|
|
@@ -1703,15 +1746,15 @@ function doesUserNeedAccessToContent(result, userPermissions, isAdmin) {
|
|
|
1703
1746
|
* .catch(error => console.error(error));
|
|
1704
1747
|
*/
|
|
1705
1748
|
export async function fetchShowsData(brand) {
|
|
1706
|
-
|
|
1707
|
-
|
|
1749
|
+
let shows = showsTypes[brand] ?? []
|
|
1750
|
+
const showsInfo = []
|
|
1708
1751
|
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1752
|
+
shows.forEach((type) => {
|
|
1753
|
+
const processedData = processMetadata(brand, type)
|
|
1754
|
+
if (processedData) showsInfo.push(processedData)
|
|
1755
|
+
})
|
|
1713
1756
|
|
|
1714
|
-
|
|
1757
|
+
return showsInfo
|
|
1715
1758
|
}
|
|
1716
1759
|
|
|
1717
1760
|
/**
|
|
@@ -1728,135 +1771,122 @@ export async function fetchShowsData(brand) {
|
|
|
1728
1771
|
* .catch(error => console.error(error));
|
|
1729
1772
|
*/
|
|
1730
1773
|
export async function fetchMetadata(brand, type) {
|
|
1731
|
-
|
|
1732
|
-
|
|
1774
|
+
const processedData = processMetadata(brand, type, true)
|
|
1775
|
+
return processedData ? processedData : {}
|
|
1733
1776
|
}
|
|
1734
1777
|
|
|
1735
1778
|
export async function fetchChatAndLiveEnvent(brand, forcedId = null) {
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1779
|
+
const liveEvent =
|
|
1780
|
+
forcedId !== null ? await fetchByRailContentIds([forcedId]) : [await fetchLiveEvent(brand)]
|
|
1781
|
+
if (liveEvent.length === 0 || (liveEvent.length === 1 && liveEvent[0] === undefined)) {
|
|
1782
|
+
return null
|
|
1783
|
+
}
|
|
1784
|
+
let url = `/content/live-chat?brand=${brand}`
|
|
1785
|
+
const chatData = await fetchHandler(url)
|
|
1786
|
+
const mergedData = { ...chatData, ...liveEvent[0] }
|
|
1787
|
+
return mergedData
|
|
1744
1788
|
}
|
|
1745
1789
|
|
|
1746
|
-
|
|
1747
1790
|
//Helper Functions
|
|
1748
1791
|
function arrayJoinWithQuotes(array, delimiter = ',') {
|
|
1749
|
-
|
|
1750
|
-
|
|
1792
|
+
const wrapped = array.map((value) => `'${value}'`)
|
|
1793
|
+
return wrapped.join(delimiter)
|
|
1751
1794
|
}
|
|
1752
1795
|
|
|
1753
1796
|
function getSanityDate(date, roundToHourForCaching = true) {
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1797
|
+
if (roundToHourForCaching) {
|
|
1798
|
+
// We need to set the published on filter date to be a round time so that it doesn't bypass the query cache
|
|
1799
|
+
// with every request by changing the filter date every second. I've set it to one minute past the current hour
|
|
1800
|
+
// because publishing usually publishes content on the hour exactly which means it should still skip the cache
|
|
1801
|
+
// when the new content is available.
|
|
1802
|
+
// Round to the start of the current hour
|
|
1803
|
+
const roundedDate = new Date(
|
|
1804
|
+
date.getFullYear(),
|
|
1805
|
+
date.getMonth(),
|
|
1806
|
+
date.getDate(),
|
|
1807
|
+
date.getHours()
|
|
1808
|
+
)
|
|
1809
|
+
|
|
1810
|
+
return roundedDate.toISOString()
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
return date.toISOString()
|
|
1766
1814
|
}
|
|
1767
1815
|
|
|
1768
1816
|
const merge = (a, b, predicate = (a, b) => a === b) => {
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1817
|
+
const c = [...a] // copy to avoid side effects
|
|
1818
|
+
// add all items from B to copy C if they're not already present
|
|
1819
|
+
b.forEach((bItem) => (c.some((cItem) => predicate(bItem, cItem)) ? null : c.push(bItem)))
|
|
1820
|
+
return c
|
|
1773
1821
|
}
|
|
1774
1822
|
|
|
1775
1823
|
function checkSanityConfig(config) {
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1824
|
+
if (!config.sanityConfig.token) {
|
|
1825
|
+
console.warn('fetchSanity: The "token" property is missing in the config object.')
|
|
1826
|
+
return false
|
|
1827
|
+
}
|
|
1828
|
+
if (!config.sanityConfig.projectId) {
|
|
1829
|
+
console.warn('fetchSanity: The "projectId" property is missing in the config object.')
|
|
1830
|
+
return false
|
|
1831
|
+
}
|
|
1832
|
+
if (!config.sanityConfig.dataset) {
|
|
1833
|
+
console.warn('fetchSanity: The "dataset" property is missing in the config object.')
|
|
1834
|
+
return false
|
|
1835
|
+
}
|
|
1836
|
+
if (!config.sanityConfig.version) {
|
|
1837
|
+
console.warn('fetchSanity: The "version" property is missing in the config object.')
|
|
1838
|
+
return false
|
|
1839
|
+
}
|
|
1840
|
+
return true
|
|
1793
1841
|
}
|
|
1794
1842
|
|
|
1795
|
-
|
|
1796
1843
|
function buildRawQuery(
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
sortOrder = 'published_on desc',
|
|
1801
|
-
start = 0,
|
|
1802
|
-
end = 10,
|
|
1803
|
-
isSingle = false,
|
|
1804
|
-
}
|
|
1844
|
+
filter = '',
|
|
1845
|
+
fields = '...',
|
|
1846
|
+
{ sortOrder = 'published_on desc', start = 0, end = 10, isSingle = false }
|
|
1805
1847
|
) {
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1848
|
+
const sortString = sortOrder ? `order(${sortOrder})` : ''
|
|
1849
|
+
const countString = isSingle ? '[0...1]' : `[${start}...${end}]`
|
|
1850
|
+
const query = `*[${filter}]{
|
|
1809
1851
|
${fields}
|
|
1810
1852
|
} | ${sortString}${countString}`
|
|
1811
|
-
|
|
1853
|
+
return query
|
|
1812
1854
|
}
|
|
1813
1855
|
|
|
1814
|
-
|
|
1815
1856
|
async function buildQuery(
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
sortOrder = 'published_on desc',
|
|
1821
|
-
start = 0,
|
|
1822
|
-
end = 10,
|
|
1823
|
-
isSingle = false,
|
|
1824
|
-
},
|
|
1857
|
+
baseFilter = '',
|
|
1858
|
+
filterParams = { pullFutureContent: false },
|
|
1859
|
+
fields = '...',
|
|
1860
|
+
{ sortOrder = 'published_on desc', start = 0, end = 10, isSingle = false }
|
|
1825
1861
|
) {
|
|
1826
|
-
|
|
1827
|
-
|
|
1862
|
+
const filter = await new FilterBuilder(baseFilter, filterParams).buildFilter()
|
|
1863
|
+
return buildRawQuery(filter, fields, { sortOrder, start, end, isSingle })
|
|
1828
1864
|
}
|
|
1829
1865
|
|
|
1830
1866
|
function buildEntityAndTotalQuery(
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
sortOrder = 'published_on desc',
|
|
1835
|
-
start = 0,
|
|
1836
|
-
end = 10,
|
|
1837
|
-
isSingle = false,
|
|
1838
|
-
},
|
|
1867
|
+
filter = '',
|
|
1868
|
+
fields = '...',
|
|
1869
|
+
{ sortOrder = 'published_on desc', start = 0, end = 10, isSingle = false }
|
|
1839
1870
|
) {
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1871
|
+
const sortString = sortOrder ? `order(${sortOrder})` : ''
|
|
1872
|
+
const countString = isSingle ? '[0...1]' : `[${start}...${end}]`
|
|
1873
|
+
const query = `{
|
|
1843
1874
|
"entity": *[${filter}] | ${sortString}${countString}
|
|
1844
1875
|
{
|
|
1845
1876
|
${fields}
|
|
1846
1877
|
},
|
|
1847
1878
|
"total": 0
|
|
1848
|
-
}
|
|
1849
|
-
|
|
1879
|
+
}`
|
|
1880
|
+
return query
|
|
1850
1881
|
}
|
|
1851
1882
|
|
|
1852
|
-
|
|
1853
1883
|
function getFilterOptions(option, commonFilter, contentType, brand) {
|
|
1854
|
-
|
|
1855
|
-
|
|
1884
|
+
let filterGroq = ''
|
|
1885
|
+
const types = Array.from(new Set([...coachLessonsTypes, ...showsTypes[brand]]))
|
|
1856
1886
|
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1887
|
+
switch (option) {
|
|
1888
|
+
case 'difficulty':
|
|
1889
|
+
filterGroq = `
|
|
1860
1890
|
"difficulty": [
|
|
1861
1891
|
{"type": "All", "count": count(*[${commonFilter} && difficulty_string == "All"])},
|
|
1862
1892
|
{"type": "Introductory", "count": count(*[${commonFilter} && difficulty_string == "Introductory"])},
|
|
@@ -1864,84 +1894,82 @@ function getFilterOptions(option, commonFilter, contentType, brand) {
|
|
|
1864
1894
|
{"type": "Intermediate", "count": count(*[${commonFilter} && difficulty_string == "Intermediate" ])},
|
|
1865
1895
|
{"type": "Advanced", "count": count(*[${commonFilter} && difficulty_string == "Advanced" ])},
|
|
1866
1896
|
{"type": "Expert", "count": count(*[${commonFilter} && difficulty_string == "Expert" ])}
|
|
1867
|
-
][count > 0]
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1897
|
+
][count > 0],`
|
|
1898
|
+
break
|
|
1899
|
+
case 'type':
|
|
1900
|
+
const typesString = types
|
|
1901
|
+
.map((t) => {
|
|
1902
|
+
return `{"type": "${t}"}`
|
|
1903
|
+
})
|
|
1904
|
+
.join(', ')
|
|
1905
|
+
filterGroq = `"type": [${typesString}]{type, 'count': count(*[_type == ^.type && ${commonFilter}])}[count > 0],`
|
|
1906
|
+
break
|
|
1907
|
+
case 'genre':
|
|
1908
|
+
case 'essential':
|
|
1909
|
+
case 'focus':
|
|
1910
|
+
case 'theory':
|
|
1911
|
+
case 'topic':
|
|
1912
|
+
case 'lifestyle':
|
|
1913
|
+
case 'creativity':
|
|
1914
|
+
filterGroq = `
|
|
1883
1915
|
"${option}": *[_type == '${option}' ${contentType ? ` && '${contentType}' in filter_types` : ''} ] {
|
|
1884
1916
|
"type": name,
|
|
1885
1917
|
"count": count(*[${commonFilter} && references(^._id)])
|
|
1886
|
-
}[count > 0]
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1918
|
+
}[count > 0],`
|
|
1919
|
+
break
|
|
1920
|
+
case 'instrumentless':
|
|
1921
|
+
filterGroq = `
|
|
1890
1922
|
"${option}": [
|
|
1891
1923
|
{"type": "Full Song Only", "count": count(*[${commonFilter} && instrumentless == false ])},
|
|
1892
1924
|
{"type": "Instrument Removed", "count": count(*[${commonFilter} && instrumentless == true ])}
|
|
1893
|
-
][count > 0]
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1925
|
+
][count > 0],`
|
|
1926
|
+
break
|
|
1927
|
+
case 'gear':
|
|
1928
|
+
filterGroq = `
|
|
1897
1929
|
"${option}": [
|
|
1898
1930
|
{"type": "Practice Pad", "count": count(*[${commonFilter} && gear match 'Practice Pad' ])},
|
|
1899
1931
|
{"type": "Drum-Set", "count": count(*[${commonFilter} && gear match 'Drum-Set'])}
|
|
1900
|
-
][count > 0]
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1932
|
+
][count > 0],`
|
|
1933
|
+
break
|
|
1934
|
+
case 'bpm':
|
|
1935
|
+
filterGroq = `
|
|
1904
1936
|
"${option}": [
|
|
1905
1937
|
{"type": "50-90", "count": count(*[${commonFilter} && bpm > 50 && bpm < 91])},
|
|
1906
1938
|
{"type": "91-120", "count": count(*[${commonFilter} && bpm > 90 && bpm < 121])},
|
|
1907
1939
|
{"type": "121-150", "count": count(*[${commonFilter} && bpm > 120 && bpm < 151])},
|
|
1908
1940
|
{"type": "151-180", "count": count(*[${commonFilter} && bpm > 150 && bpm < 181])},
|
|
1909
1941
|
{"type": "180+", "count": count(*[${commonFilter} && bpm > 180])},
|
|
1910
|
-
][count > 0]
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1942
|
+
][count > 0],`
|
|
1943
|
+
break
|
|
1944
|
+
default:
|
|
1945
|
+
filterGroq = ''
|
|
1946
|
+
break
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
return filterGroq
|
|
1918
1950
|
}
|
|
1919
1951
|
|
|
1920
1952
|
function cleanUpGroq(query) {
|
|
1921
|
-
|
|
1922
|
-
|
|
1953
|
+
// Split the query into clauses based on the logical operators
|
|
1954
|
+
const clauses = query.split(/(\s*&&|\s*\|\|)/).map((clause) => clause.trim())
|
|
1923
1955
|
|
|
1924
|
-
|
|
1925
|
-
|
|
1956
|
+
// Filter out empty clauses
|
|
1957
|
+
const filteredClauses = clauses.filter((clause) => clause.length > 0)
|
|
1926
1958
|
|
|
1927
|
-
|
|
1928
|
-
|
|
1959
|
+
// Check if there are valid conditions in the clauses
|
|
1960
|
+
const hasConditions = filteredClauses.some((clause) => !clause.match(/^\s*&&\s*|\s*\|\|\s*$/))
|
|
1929
1961
|
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1962
|
+
if (!hasConditions) {
|
|
1963
|
+
// If no valid conditions, return an empty string or the original query
|
|
1964
|
+
return ''
|
|
1965
|
+
}
|
|
1934
1966
|
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1967
|
+
// Remove occurrences of '&& ()'
|
|
1968
|
+
const cleanedQuery = filteredClauses
|
|
1969
|
+
.join(' ')
|
|
1970
|
+
.replace(/&&\s*\(\)/g, '')
|
|
1971
|
+
.replace(/(\s*&&|\s*\|\|)(?=\s*[\s()]*$|(?=\s*&&|\s*\|\|))/g, '')
|
|
1972
|
+
.trim()
|
|
1940
1973
|
|
|
1941
|
-
|
|
1974
|
+
return cleanedQuery
|
|
1942
1975
|
}
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|