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