musora-content-services 1.0.116 → 1.0.118
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/.github/workflows/node.js.yml +0 -0
- package/CHANGELOG.md +4 -0
- package/README.md +0 -0
- package/babel.config.js +0 -0
- package/docs/config.js.html +0 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +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/docs/scripts/collapse.js +0 -0
- package/docs/scripts/commonNav.js +0 -0
- package/docs/scripts/linenumber.js +0 -0
- package/docs/scripts/nav.js +0 -0
- package/docs/scripts/polyfill.js +0 -0
- package/docs/scripts/prettify/Apache-License-2.0.txt +0 -0
- package/docs/scripts/prettify/lang-css.js +0 -0
- package/docs/scripts/prettify/prettify.js +0 -0
- package/docs/scripts/search.js +0 -0
- package/docs/styles/jsdoc.css +0 -0
- package/docs/styles/prettify.css +0 -0
- package/jest.config.js +0 -0
- package/jsdoc.json +0 -0
- package/link_mcs.sh +0 -0
- package/package.json +1 -1
- package/src/contentMetaData.js +1091 -0
- package/src/contentTypeConfig.js +45 -1414
- package/src/filterBuilder.js +0 -0
- package/src/index.d.ts +0 -0
- package/src/index.js +0 -0
- package/src/services/config.js +1 -0
- package/src/services/railcontent.js +24 -12
- package/src/services/sanity.js +118 -71
- package/src/services/userContext.js +0 -0
- package/test/localStorageMock.js +0 -0
- package/test/log.js +0 -0
- package/test/sanityQueryService.test.js +126 -6
- package/test/userContext.test.js +0 -0
- package/tools/generate-index.js +0 -0
package/src/filterBuilder.js
CHANGED
|
File without changes
|
package/src/index.d.ts
CHANGED
|
File without changes
|
package/src/index.js
CHANGED
|
File without changes
|
package/src/services/config.js
CHANGED
|
@@ -19,6 +19,7 @@ let globalConfig = {
|
|
|
19
19
|
* @param {string} config.sanityConfig.version - The API version to use.
|
|
20
20
|
* @param {boolean} [config.sanityConfig.debug=false] - Optional flag to enable debug mode.
|
|
21
21
|
* @param {boolean} [config.sanityConfig.useCachedAPI=true] - Optional flag to enable or disable the use of the cached API.
|
|
22
|
+
* @param {boolean} [config.sanityConfig.useDummyRailContentMethods=false] - Optional flag to use test harness for railcontent methods. Should only be used by jest tests.
|
|
22
23
|
* @param {Object} config.railcontentConfig - Configuration for user services.
|
|
23
24
|
* @param {string} config.railcontentConfig.token - The token for authenticating user-specific requests.
|
|
24
25
|
* @param {string} config.railcontentConfig.userId - The user ID for fetching user-specific data.
|
|
@@ -254,15 +254,6 @@ export async function fetchContentPageUserData(contentId) {
|
|
|
254
254
|
}
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
-
function fetchAbsolute(url, params) {
|
|
258
|
-
if(globalConfig.railcontentConfig.baseUrl) {
|
|
259
|
-
if (url.startsWith('/')) {
|
|
260
|
-
return fetch(globalConfig.railcontentConfig.baseUrl + url, params)
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
return fetch(url, params);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
257
|
export async function fetchUserContext() {
|
|
267
258
|
let url = `/content/user_data_all`;
|
|
268
259
|
const headers = {
|
|
@@ -270,7 +261,7 @@ export async function fetchUserContext() {
|
|
|
270
261
|
'X-CSRF-TOKEN': globalConfig.railcontentConfig.token
|
|
271
262
|
};
|
|
272
263
|
try {
|
|
273
|
-
const response = await
|
|
264
|
+
const response = await fetchAbsolute(url, {headers});
|
|
274
265
|
const result = await response.json();
|
|
275
266
|
if (result) {
|
|
276
267
|
console.log('fetchUserContext', result);
|
|
@@ -284,13 +275,25 @@ export async function fetchUserContext() {
|
|
|
284
275
|
}
|
|
285
276
|
}
|
|
286
277
|
|
|
278
|
+
export async function fetchUserPermissions() {
|
|
279
|
+
//TODO: Should be investigate why throw 500 errors on MA
|
|
280
|
+
return [];
|
|
281
|
+
let url = `/content/user_data_permissions`;
|
|
282
|
+
const headers = {
|
|
283
|
+
'Content-Type': 'application/json',
|
|
284
|
+
'X-CSRF-TOKEN': globalConfig.railcontentConfig.token
|
|
285
|
+
};
|
|
286
|
+
// in the case of an unauthorized user, we return empty permissions
|
|
287
|
+
return fetchHandler(url, 'get') ?? [];
|
|
288
|
+
}
|
|
289
|
+
|
|
287
290
|
export async function fetchHandler(url, method = "get") {
|
|
288
291
|
const headers = {
|
|
289
292
|
'Content-Type': 'application/json',
|
|
290
293
|
'X-CSRF-TOKEN': globalConfig.railcontentConfig.token
|
|
291
294
|
};
|
|
292
295
|
try {
|
|
293
|
-
const response = await
|
|
296
|
+
const response = await fetchAbsolute(url, {method, headers});
|
|
294
297
|
const result = await response.json();
|
|
295
298
|
if (result) {
|
|
296
299
|
return result;
|
|
@@ -299,8 +302,8 @@ export async function fetchHandler(url, method = "get") {
|
|
|
299
302
|
}
|
|
300
303
|
} catch (error) {
|
|
301
304
|
console.error('Fetch error:', error);
|
|
302
|
-
return null;
|
|
303
305
|
}
|
|
306
|
+
return null;
|
|
304
307
|
}
|
|
305
308
|
|
|
306
309
|
export async function fetchLikeContent(contentId) {
|
|
@@ -311,4 +314,13 @@ export async function fetchLikeContent(contentId) {
|
|
|
311
314
|
export async function fetchUnlikeContent(contentId) {
|
|
312
315
|
let url = `/content/${contentId}/unlike`;
|
|
313
316
|
return await fetchHandler(url, "post");
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function fetchAbsolute(url, params) {
|
|
320
|
+
if(globalConfig.railcontentConfig.baseUrl) {
|
|
321
|
+
if (url.startsWith('/')) {
|
|
322
|
+
return fetch(globalConfig.railcontentConfig.baseUrl + url, params)
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return fetch(url, params);
|
|
314
326
|
}
|
package/src/services/sanity.js
CHANGED
|
@@ -11,13 +11,17 @@ import {
|
|
|
11
11
|
getFieldsForContentType,
|
|
12
12
|
filtersToGroq,
|
|
13
13
|
getUpcomingEventsTypes,
|
|
14
|
-
getNewReleasesTypes,
|
|
15
14
|
showsTypes,
|
|
16
|
-
|
|
15
|
+
getNewReleasesTypes,
|
|
17
16
|
} from "../contentTypeConfig";
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
processMetadata,
|
|
20
|
+
} from "../contentMetaData";
|
|
21
|
+
|
|
18
22
|
import {globalConfig} from "./config";
|
|
19
23
|
|
|
20
|
-
import { fetchAllCompletedStates, fetchCurrentSongComplete } from './railcontent.js';
|
|
24
|
+
import { fetchUserPermissions, fetchAllCompletedStates, fetchCurrentSongComplete } from './railcontent.js';
|
|
21
25
|
import {arrayToStringRepresentation, FilterBuilder} from "../filterBuilder";
|
|
22
26
|
|
|
23
27
|
/**
|
|
@@ -495,58 +499,51 @@ export async function fetchAll(brand, type, {
|
|
|
495
499
|
|
|
496
500
|
// Determine the group by clause
|
|
497
501
|
let query = "";
|
|
502
|
+
let entityFieldsString = "";
|
|
503
|
+
let filter = "";
|
|
498
504
|
if (groupBy !== "" && isGroupByOneToOne) {
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
"total": count(*[_type == '${groupBy}' && count(*[brand == '${brand}' && ^._id == ${groupBy}._ref ${typeFilter} ${searchFilter} ${includedFieldsFilter} ${progressFilter}]._id) > 0]),
|
|
503
|
-
"entity": *[_type == '${groupBy}' && count(*[brand == '${brand}' && ^._id == ${groupBy}._ref ${typeFilter} ${searchFilter} ${includedFieldsFilter} ${progressFilter}]._id) > 0]
|
|
504
|
-
{
|
|
505
|
+
const webUrlPath = 'artists';
|
|
506
|
+
const lessonsFilter = `_type == '${type}' && brand == '${brand}' && ^._id == ${groupBy}._ref ${searchFilter} ${includedFieldsFilter} ${progressFilter}`;
|
|
507
|
+
entityFieldsString = `
|
|
505
508
|
'id': _id,
|
|
506
509
|
'type': _type,
|
|
507
510
|
name,
|
|
508
511
|
'head_shot_picture_url': thumbnail_url.asset->url,
|
|
509
512
|
'web_url_path': '/${brand}/${webUrlPath}/'+name+'?included_fieds[]=type,${type}',
|
|
510
|
-
'all_lessons_count': count(*[
|
|
511
|
-
'lessons': *[
|
|
513
|
+
'all_lessons_count': count(*[${lessonsFilter}]._id),
|
|
514
|
+
'lessons': *[${lessonsFilter}]{
|
|
512
515
|
${fieldsString},
|
|
513
516
|
${groupBy}
|
|
514
517
|
}[0...20]
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
[${start}...${end}]
|
|
518
|
-
}`;
|
|
518
|
+
`;
|
|
519
|
+
filter = `_type == '${groupBy}' && count(*[brand == '${brand}' && ^._id == ${groupBy}._ref ${typeFilter} ${searchFilter} ${includedFieldsFilter} ${progressFilter}]._id) > 0`;
|
|
519
520
|
} else if (groupBy !== "") {
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
"total": count(*[_type == '${groupBy}' && count(*[brand == '${brand}' && ^._id in ${groupBy}[]._ref ${typeFilter} ${searchFilter} ${includedFieldsFilter} ${progressFilter}]._id) > 0]),
|
|
524
|
-
"entity": *[_type == '${groupBy}' && count(*[brand == '${brand}' && ^._id in ${groupBy}[]._ref ${typeFilter} ${searchFilter} ${includedFieldsFilter} ${progressFilter}]._id) > 0]
|
|
525
|
-
{
|
|
521
|
+
const webUrlPath = (groupBy == 'genre')?'/genres':'';
|
|
522
|
+
const lessonsFilter = `brand == '${brand}' && ^._id in ${groupBy}[]._ref ${typeFilter} ${searchFilter} ${includedFieldsFilter} ${progressFilter}`;
|
|
523
|
+
entityFieldsString = `
|
|
526
524
|
'id': _id,
|
|
527
525
|
'type': _type,
|
|
528
526
|
name,
|
|
529
527
|
'head_shot_picture_url': thumbnail_url.asset->url,
|
|
530
528
|
'web_url_path': select(defined(web_url_path)=> web_url_path +'?included_fieds[]=type,${type}',!defined(web_url_path)=> '/${brand}${webUrlPath}/'+name+'/${webUrlPathType}'),
|
|
531
|
-
'all_lessons_count': count(*[
|
|
532
|
-
'lessons': *[
|
|
529
|
+
'all_lessons_count': count(*[${lessonsFilter}]._id),
|
|
530
|
+
'lessons': *[${lessonsFilter}]{
|
|
533
531
|
${fieldsString},
|
|
534
532
|
${groupBy}
|
|
535
|
-
}[0...20]
|
|
536
|
-
|
|
537
|
-
|order(${sortOrder})
|
|
538
|
-
[${start}...${end}]
|
|
539
|
-
}`;
|
|
533
|
+
}[0...20]`;
|
|
534
|
+
filter = `_type == '${groupBy}' && count(*[brand == '${brand}' && ^._id in ${groupBy}[]._ref ${typeFilter} ${searchFilter} ${includedFieldsFilter} ${progressFilter}]._id) > 0`;
|
|
540
535
|
} else {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
"entity": *[brand == "${brand}" ${typeFilter} ${searchFilter} ${includedFieldsFilter} ${progressFilter}] | order(${sortOrder}) [${start}...${end}] {
|
|
544
|
-
${fieldsString},
|
|
545
|
-
},
|
|
546
|
-
"total": count(*[brand == "${brand}" ${typeFilter} ${searchFilter} ${includedFieldsFilter} ${progressFilter}])
|
|
547
|
-
}
|
|
548
|
-
`;
|
|
536
|
+
filter = `brand == "${brand}" ${typeFilter} ${searchFilter} ${includedFieldsFilter} ${progressFilter}`
|
|
537
|
+
entityFieldsString = fieldsString;
|
|
549
538
|
}
|
|
539
|
+
query = buildEntityAndTotalQuery(
|
|
540
|
+
filter,
|
|
541
|
+
entityFieldsString,
|
|
542
|
+
{
|
|
543
|
+
sortOrder: sortOrder,
|
|
544
|
+
start: start,
|
|
545
|
+
end: end,
|
|
546
|
+
});
|
|
550
547
|
|
|
551
548
|
return fetchSanity(query, true);
|
|
552
549
|
}
|
|
@@ -597,13 +594,14 @@ export function getSortOrder(sort= '-published_on', groupBy)
|
|
|
597
594
|
*/
|
|
598
595
|
export async function fetchAllFilterOptions(
|
|
599
596
|
brand,
|
|
600
|
-
filters,
|
|
597
|
+
filters = [],
|
|
601
598
|
style,
|
|
602
599
|
artist,
|
|
603
600
|
contentType,
|
|
604
601
|
term,
|
|
605
602
|
progressIds = undefined
|
|
606
603
|
) {
|
|
604
|
+
filters = Array.isArray(filters) ? filters : [];
|
|
607
605
|
const includedFieldsFilter = filters?.length > 0 ? filtersToGroq(filters) : undefined;
|
|
608
606
|
|
|
609
607
|
const progressFilter = progressIds !== undefined ?
|
|
@@ -611,13 +609,16 @@ export async function fetchAllFilterOptions(
|
|
|
611
609
|
|
|
612
610
|
const commonFilter = `_type == '${contentType}' && brand == "${brand}"${style ? ` && '${style}' in genre[]->name` : ''}${artist ? ` && artist->name == '${artist}'` : ''} ${progressFilter} ${includedFieldsFilter ? includedFieldsFilter : ''}`;
|
|
613
611
|
const metaData = processMetadata(brand, contentType, true);
|
|
614
|
-
const allowableFilters =
|
|
612
|
+
const allowableFilters = metaData?.allowableFilters || [];
|
|
615
613
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
614
|
+
const dynamicFilterOptions = allowableFilters.map(filter => {
|
|
615
|
+
// Create a modified common filter for each allowable filter
|
|
616
|
+
let includedFieldsFilterWithoutSelectedOption = filters?.length > 0 ? filtersToGroq(filters, filter) : undefined;
|
|
617
|
+
const commonFilterWithoutSelectedOption = `_type == '${contentType}' && brand == "${brand}"${(style && filter !== "style") ? ` && '${style}' in genre[]->name` : ''}${(artist && filter !== "artist") ? ` && artist->name == '${artist}'` : ''} ${includedFieldsFilterWithoutSelectedOption ? includedFieldsFilterWithoutSelectedOption : ''}`;
|
|
618
|
+
|
|
619
|
+
// Call getFilterOptions with the modified common filter
|
|
620
|
+
return getFilterOptions(filter, commonFilterWithoutSelectedOption, contentType);
|
|
621
|
+
}).join(' ');
|
|
621
622
|
|
|
622
623
|
const query = `
|
|
623
624
|
{
|
|
@@ -970,7 +971,7 @@ export async function fetchAllPacks(brand, sort = "-published_on", searchTerm =
|
|
|
970
971
|
const query = buildQuery(
|
|
971
972
|
filter,
|
|
972
973
|
filterParams,
|
|
973
|
-
|
|
974
|
+
getFieldsForContentType('pack'),
|
|
974
975
|
{
|
|
975
976
|
logo_image_url: 'logo_image_url.asset->url',
|
|
976
977
|
sortOrder: sortOrder,
|
|
@@ -1245,13 +1246,13 @@ export async function fetchGenreLessons(brand, name, contentType, {
|
|
|
1245
1246
|
}
|
|
1246
1247
|
|
|
1247
1248
|
|
|
1248
|
-
|
|
1249
1249
|
/**
|
|
1250
|
-
* Fetch data from the Sanity API based on a provided query.
|
|
1251
1250
|
*
|
|
1252
1251
|
* @param {string} query - The GROQ query to execute against the Sanity API.
|
|
1253
1252
|
* @param {boolean} isList - Whether to return an array or a single result.
|
|
1254
|
-
* @
|
|
1253
|
+
* @param {Function} [customPostProcess=null] - custom post process callback
|
|
1254
|
+
* @param {boolean} [processNeedAccess=true] - execute the needs_access callback
|
|
1255
|
+
* @returns {Promise<*|null>} - A promise that resolves to the fetched data or null if an error occurs or no results are found.
|
|
1255
1256
|
*
|
|
1256
1257
|
* @example
|
|
1257
1258
|
* const query = `*[_type == "song"]{title, artist->name}`;
|
|
@@ -1259,7 +1260,13 @@ export async function fetchGenreLessons(brand, name, contentType, {
|
|
|
1259
1260
|
* .then(data => console.log(data))
|
|
1260
1261
|
* .catch(error => console.error(error));
|
|
1261
1262
|
*/
|
|
1262
|
-
|
|
1263
|
+
|
|
1264
|
+
export async function fetchSanity(query,
|
|
1265
|
+
isList,
|
|
1266
|
+
{ customPostProcess = null,
|
|
1267
|
+
processNeedAccess = true,} = {}
|
|
1268
|
+
) {
|
|
1269
|
+
|
|
1263
1270
|
// Check the config object before proceeding
|
|
1264
1271
|
if (!checkSanityConfig(globalConfig)) {
|
|
1265
1272
|
return null;
|
|
@@ -1287,7 +1294,9 @@ export async function fetchSanity(query, isList) {
|
|
|
1287
1294
|
if (globalConfig.sanityConfig.debug) {
|
|
1288
1295
|
console.log("fetchSanity Results:", result);
|
|
1289
1296
|
}
|
|
1290
|
-
|
|
1297
|
+
let results = isList ? result.result : result.result[0];
|
|
1298
|
+
results = processNeedAccess ? await needsAccessDecorator(results) : results;
|
|
1299
|
+
return customPostProcess ? customPostProcess(results) : results;
|
|
1291
1300
|
} else {
|
|
1292
1301
|
throw new Error('No results found');
|
|
1293
1302
|
}
|
|
@@ -1297,6 +1306,41 @@ export async function fetchSanity(query, isList) {
|
|
|
1297
1306
|
}
|
|
1298
1307
|
}
|
|
1299
1308
|
|
|
1309
|
+
async function needsAccessDecorator(results)
|
|
1310
|
+
{
|
|
1311
|
+
if (globalConfig.sanityConfig.useDummyRailContentMethods) return results;
|
|
1312
|
+
let userPermissions = await getUserPermissions();
|
|
1313
|
+
userPermissions = new Set(userPermissions);
|
|
1314
|
+
if (Array.isArray(results)) {
|
|
1315
|
+
results.forEach((result) => {
|
|
1316
|
+
result['need_access'] = doesUserNeedAccessToContent(result, userPermissions);
|
|
1317
|
+
});
|
|
1318
|
+
} else {
|
|
1319
|
+
results['need_access'] = doesUserNeedAccessToContent(results, userPermissions);
|
|
1320
|
+
}
|
|
1321
|
+
return results;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
function doesUserNeedAccessToContent(result, userPermissions)
|
|
1325
|
+
{
|
|
1326
|
+
const permissions = new Set(result.permission_id ?? []);
|
|
1327
|
+
if (permissions.length === 0) {
|
|
1328
|
+
return false;
|
|
1329
|
+
}
|
|
1330
|
+
for (let permission of permissions) {
|
|
1331
|
+
if (userPermissions.has(permission)) {
|
|
1332
|
+
return false;
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
return true;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
async function getUserPermissions()
|
|
1339
|
+
{
|
|
1340
|
+
return await fetchUserPermissions();
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
|
|
1300
1344
|
/**
|
|
1301
1345
|
* Fetch CatalogueMetadata from Sanity. This information may be duplicated in the contentTypeConfig.js.
|
|
1302
1346
|
* It's an ongoing discussion (Aug 2024), but it's been included here if necessary
|
|
@@ -1348,7 +1392,7 @@ export async function fetchShowsData(brand) {
|
|
|
1348
1392
|
}
|
|
1349
1393
|
|
|
1350
1394
|
/**
|
|
1351
|
-
* Fetch metadata from the
|
|
1395
|
+
* Fetch metadata from the contentMetaData.js based on brand and type.
|
|
1352
1396
|
*
|
|
1353
1397
|
* @param {string} brand - The brand for which to fetch metadata.
|
|
1354
1398
|
* @param {string} type - The type for which to fetch metadata.
|
|
@@ -1459,28 +1503,7 @@ function buildEntityAndTotalQuery(
|
|
|
1459
1503
|
}`;
|
|
1460
1504
|
return query;
|
|
1461
1505
|
}
|
|
1462
|
-
function processMetadata(brand, type, withFilters = false) {
|
|
1463
|
-
const metadataElement = contentMetadata[brand]?.[type];
|
|
1464
|
-
if (!metadataElement) {
|
|
1465
|
-
return null;
|
|
1466
|
-
}
|
|
1467
|
-
const processedData = {
|
|
1468
|
-
type,
|
|
1469
|
-
thumbnailUrl: metadataElement.thumbnailUrl || null,
|
|
1470
|
-
name: metadataElement.name || null,
|
|
1471
|
-
description: metadataElement.description || null
|
|
1472
|
-
};
|
|
1473
|
-
|
|
1474
|
-
if (withFilters) {
|
|
1475
|
-
Object.keys(metadataElement).forEach(key => {
|
|
1476
|
-
if ( !['thumbnailUrl', 'name', 'description'].includes(key) ) {
|
|
1477
|
-
processedData[key] = metadataElement[key];
|
|
1478
|
-
}
|
|
1479
|
-
});
|
|
1480
|
-
}
|
|
1481
1506
|
|
|
1482
|
-
return processedData;
|
|
1483
|
-
}
|
|
1484
1507
|
|
|
1485
1508
|
function getFilterOptions(option, commonFilter,contentType){
|
|
1486
1509
|
let filterGroq = '';
|
|
@@ -1540,6 +1563,30 @@ function getFilterOptions(option, commonFilter,contentType){
|
|
|
1540
1563
|
return filterGroq;
|
|
1541
1564
|
}
|
|
1542
1565
|
|
|
1566
|
+
function cleanUpGroq(query) {
|
|
1567
|
+
// Split the query into clauses based on the logical operators
|
|
1568
|
+
const clauses = query.split(/(\s*&&|\s*\|\|)/).map(clause => clause.trim());
|
|
1569
|
+
|
|
1570
|
+
// Filter out empty clauses
|
|
1571
|
+
const filteredClauses = clauses.filter(clause => clause.length > 0);
|
|
1572
|
+
|
|
1573
|
+
// Check if there are valid conditions in the clauses
|
|
1574
|
+
const hasConditions = filteredClauses.some(clause => !clause.match(/^\s*&&\s*|\s*\|\|\s*$/));
|
|
1575
|
+
|
|
1576
|
+
if (!hasConditions) {
|
|
1577
|
+
// If no valid conditions, return an empty string or the original query
|
|
1578
|
+
return '';
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
// Remove occurrences of '&& ()'
|
|
1582
|
+
const cleanedQuery = filteredClauses.join(' ')
|
|
1583
|
+
.replace(/&&\s*\(\)/g, '')
|
|
1584
|
+
.replace(/(\s*&&|\s*\|\|)(?=\s*[\s()]*$|(?=\s*&&|\s*\|\|))/g, '')
|
|
1585
|
+
.trim();
|
|
1586
|
+
|
|
1587
|
+
return cleanedQuery;
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1543
1590
|
|
|
1544
1591
|
|
|
1545
1592
|
|
|
File without changes
|
package/test/localStorageMock.js
CHANGED
|
File without changes
|
package/test/log.js
CHANGED
|
File without changes
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import {initializeService} from '../src/services/config.js';
|
|
2
|
+
import {getFieldsForContentType} from "../src/contentTypeConfig";
|
|
3
|
+
import {fetchSanity} from "../src/services/sanity";
|
|
2
4
|
import {log} from './log.js';
|
|
3
5
|
|
|
4
6
|
const {
|
|
@@ -14,6 +16,7 @@ const {
|
|
|
14
16
|
fetchByRailContentId,
|
|
15
17
|
fetchByRailContentIds,
|
|
16
18
|
fetchAll,
|
|
19
|
+
fetchAllOld,
|
|
17
20
|
fetchAllFilterOptions,
|
|
18
21
|
fetchFoundation,
|
|
19
22
|
fetchMethods,
|
|
@@ -39,6 +42,10 @@ const {
|
|
|
39
42
|
FilterBuilder,
|
|
40
43
|
} = require('../src/filterBuilder.js');
|
|
41
44
|
|
|
45
|
+
const {
|
|
46
|
+
processMetadata,
|
|
47
|
+
} = require('../src/contentMetaData.js');
|
|
48
|
+
|
|
42
49
|
describe('Sanity Queries', function () {
|
|
43
50
|
beforeEach(() => {
|
|
44
51
|
const config = {
|
|
@@ -48,7 +55,8 @@ describe('Sanity Queries', function () {
|
|
|
48
55
|
dataset: process.env.SANITY_DATASET,
|
|
49
56
|
useCachedAPI: process.env.SANITY_USE_CACHED_API === 'true' || true,
|
|
50
57
|
version: '2021-06-07',
|
|
51
|
-
debug: process.env.DEBUG === 'true' || false
|
|
58
|
+
debug: process.env.DEBUG === 'true' || false,
|
|
59
|
+
useDummyRailContentMethods: true,
|
|
52
60
|
}
|
|
53
61
|
};
|
|
54
62
|
initializeService(config);
|
|
@@ -61,6 +69,7 @@ describe('Sanity Queries', function () {
|
|
|
61
69
|
|
|
62
70
|
});
|
|
63
71
|
|
|
72
|
+
|
|
64
73
|
test('fetchArtists', async () => {
|
|
65
74
|
const response = await fetchArtists('drumeo');
|
|
66
75
|
const artistNames = response.map((x) => x.name);
|
|
@@ -72,9 +81,29 @@ describe('Sanity Queries', function () {
|
|
|
72
81
|
const response = await fetchSongArtistCount('drumeo');
|
|
73
82
|
log(response);
|
|
74
83
|
expect(response).toBeGreaterThan(1000);
|
|
84
|
+
}, 10000);
|
|
85
|
+
|
|
86
|
+
test('fetchSanity-WithPostProcess', async () => {
|
|
87
|
+
const id = 380094;
|
|
88
|
+
const query = `*[railcontent_id == ${id}]{
|
|
89
|
+
${getFieldsForContentType('song')}
|
|
90
|
+
}`
|
|
91
|
+
const newSlug = 'keysmash1';
|
|
92
|
+
const newField = 1
|
|
93
|
+
const postProcess = (result) => {
|
|
94
|
+
result['new_field'] = newField;
|
|
95
|
+
result['slug'] = newSlug;
|
|
96
|
+
return result;
|
|
97
|
+
};
|
|
98
|
+
const response = await fetchSanity(query, false, {customPostProcess: postProcess});
|
|
99
|
+
log(response);
|
|
100
|
+
expect(response.id).toBe(id);
|
|
101
|
+
expect(response.new_field).toBe(newField);
|
|
102
|
+
expect(response.slug).toBe(newSlug);
|
|
75
103
|
});
|
|
76
104
|
|
|
77
|
-
|
|
105
|
+
|
|
106
|
+
test('fetchSanityPostProcess', async () => {
|
|
78
107
|
const id = 380094;
|
|
79
108
|
const response = await fetchByRailContentId(id, "song");
|
|
80
109
|
expect(response.id).toBe(id);
|
|
@@ -85,6 +114,7 @@ describe('Sanity Queries', function () {
|
|
|
85
114
|
const response = await fetchChallengeOverview(id);
|
|
86
115
|
expect(response.lessons).toBeDefined();
|
|
87
116
|
expect(response.id).toBe(id);
|
|
117
|
+
|
|
88
118
|
});
|
|
89
119
|
|
|
90
120
|
test('fetchByRailContentIds', async () => {
|
|
@@ -327,6 +357,37 @@ describe('Sanity Queries', function () {
|
|
|
327
357
|
|
|
328
358
|
test('fetchAll-IncludedFields', async () => {
|
|
329
359
|
let response = await fetchAll('drumeo', 'instructor',{includedFields: ['is_active']});
|
|
360
|
+
console.log(response);
|
|
361
|
+
expect(response.entity.length).toBeGreaterThan(0);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
test('fetchAll-IncludedFields-multiple', async () => {
|
|
365
|
+
let response = await fetchAll('drumeo', 'course',{includedFields: ['essential,Dynamics','essential,Timing','difficulty,Beginner']});
|
|
366
|
+
log(response);
|
|
367
|
+
expect(response.entity.length).toBeGreaterThan(0);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
test('fetchAll-IncludedFields-playalong-multiple', async () => {
|
|
371
|
+
let response = await fetchAll('drumeo', 'play-along',{includedFields: ['bpm,91-120','bpm,181+','genre,Blues']});
|
|
372
|
+
log(response);
|
|
373
|
+
expect(response.entity.length).toBeGreaterThan(0);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
test('fetchAll-IncludedFields-rudiment-multiple-gear', async () => {
|
|
377
|
+
let response = await fetchAll('drumeo', 'rudiment',{includedFields: ['gear,Drum-Set','gear,Practice Pad']});
|
|
378
|
+
log(response);
|
|
379
|
+
expect(response.entity.length).toBeGreaterThan(0);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
test('fetchAll-IncludedFields-coaches-multiple-focus', async () => {
|
|
383
|
+
let response = await fetchAll('drumeo', 'instructor',{includedFields: ['focus,drumline','focus,recording']});
|
|
384
|
+
log(response);
|
|
385
|
+
expect(response.entity.length).toBeGreaterThan(0);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
test('fetchAll-IncludedFields-songs-multiple-instrumentless', async () => {
|
|
389
|
+
let response = await fetchAll('drumeo', 'song',{includedFields: ['instrumentless,true','instrumentless,false']});
|
|
390
|
+
log(response);
|
|
330
391
|
expect(response.entity.length).toBeGreaterThan(0);
|
|
331
392
|
});
|
|
332
393
|
|
|
@@ -518,7 +579,7 @@ describe('Filter Builder', function () {
|
|
|
518
579
|
}
|
|
519
580
|
|
|
520
581
|
test('fetchAllFilterOptions', async () => {
|
|
521
|
-
let response = await fetchAllFilterOptions('drumeo',
|
|
582
|
+
let response = await fetchAllFilterOptions('drumeo', [], '', '', 'song', '');
|
|
522
583
|
log(response);
|
|
523
584
|
expect(response.meta.filterOptions.difficulty).toBeDefined();
|
|
524
585
|
expect(response.meta.filterOptions.genre).toBeDefined();
|
|
@@ -527,14 +588,14 @@ describe('Filter Builder', function () {
|
|
|
527
588
|
});
|
|
528
589
|
|
|
529
590
|
test('fetchAllFilterOptions-Rudiment', async () => {
|
|
530
|
-
let response = await fetchAllFilterOptions('drumeo',
|
|
591
|
+
let response = await fetchAllFilterOptions('drumeo', [], '', '', 'rudiment', '');
|
|
531
592
|
log(response);
|
|
532
593
|
expect(response.meta.filterOptions.gear).toBeDefined();
|
|
533
594
|
expect(response.meta.filterOptions.genre).toBeDefined();
|
|
534
595
|
expect(response.meta.filterOptions.topic).toBeDefined();
|
|
535
596
|
});
|
|
536
597
|
test('fetchAllFilterOptions-PlayAlong', async () => {
|
|
537
|
-
let response = await fetchAllFilterOptions('drumeo',
|
|
598
|
+
let response = await fetchAllFilterOptions('drumeo', [], '', '', 'play-along', '');
|
|
538
599
|
log(response);
|
|
539
600
|
expect(response.meta.filterOptions.difficulty).toBeDefined();
|
|
540
601
|
expect(response.meta.filterOptions.genre).toBeDefined();
|
|
@@ -542,11 +603,70 @@ describe('Filter Builder', function () {
|
|
|
542
603
|
});
|
|
543
604
|
|
|
544
605
|
test('fetchAllFilterOptions-Coaches', async () => {
|
|
545
|
-
let response = await fetchAllFilterOptions('drumeo',
|
|
606
|
+
let response = await fetchAllFilterOptions('drumeo', [], '', '', 'instructor', '');
|
|
546
607
|
log(response);
|
|
547
608
|
expect(response.meta.filterOptions.focus).toBeDefined();
|
|
548
609
|
expect(response.meta.filterOptions.focus.length).toBeGreaterThan(0);
|
|
549
610
|
expect(response.meta.filterOptions.genre).toBeDefined();
|
|
550
611
|
expect(response.meta.filterOptions.genre.length).toBeGreaterThan(0);
|
|
551
612
|
});
|
|
613
|
+
|
|
614
|
+
test('fetchAllFilterOptions-filter-selected', async () => {
|
|
615
|
+
let response = await fetchAllFilterOptions('drumeo', ['theory,notation','theory,time signatures','creativity,Grooves','creativity,Fills & Chops','difficulty,Beginner','difficulty,Intermediate','difficulty,Expert'], '', '', 'course', '');
|
|
616
|
+
log(response);
|
|
617
|
+
expect(response.meta.filterOptions).toBeDefined();
|
|
618
|
+
});
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
describe('MetaData', function () {
|
|
622
|
+
|
|
623
|
+
test('customBrandTypeExists', async () => {
|
|
624
|
+
const metaData = processMetadata('guitareo', 'recording');
|
|
625
|
+
expect(metaData.type).toBe('recording');
|
|
626
|
+
expect(metaData.name).toBe('Archives');
|
|
627
|
+
expect(metaData.description).toBeDefined();
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
test('invalidContentType', async () => {
|
|
631
|
+
const metaData = processMetadata('guitareo', 'not a real type');
|
|
632
|
+
expect(metaData).toBeNull();
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
test('onlyCommon', async () => {
|
|
636
|
+
const guitareoMetaData = processMetadata('guitareo', 'challenge');
|
|
637
|
+
const drumeoMetaData = processMetadata('drumeo', 'challenge');
|
|
638
|
+
expect(guitareoMetaData).toStrictEqual(drumeoMetaData);
|
|
639
|
+
expect(guitareoMetaData.type).toBe('challenge');
|
|
640
|
+
expect(guitareoMetaData.name).toBe('Challenges');
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
test('withCommon', async () => {
|
|
644
|
+
const guitareoMetaData = processMetadata('guitareo', 'instructor');
|
|
645
|
+
const drumeoMetaData = processMetadata('drumeo', 'instructor');
|
|
646
|
+
expect(guitareoMetaData.description).not.toBe(drumeoMetaData.description);
|
|
647
|
+
guitareoMetaData.description = ''
|
|
648
|
+
drumeoMetaData.description = ''
|
|
649
|
+
expect(guitareoMetaData).toStrictEqual(drumeoMetaData);
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
test('withWithoutFilters', async () => {
|
|
653
|
+
let metaData = processMetadata('singeo', 'student-review', true);
|
|
654
|
+
expect(metaData.type).toBeDefined()
|
|
655
|
+
expect(metaData.name).toBeDefined()
|
|
656
|
+
expect(metaData.description).toBeDefined();
|
|
657
|
+
expect(metaData.thumbnailUrl).toBeDefined();
|
|
658
|
+
expect(metaData.tabs).toBeDefined();
|
|
659
|
+
metaData = processMetadata('singeo', 'student-review', false);
|
|
660
|
+
expect(metaData.type).toBeDefined()
|
|
661
|
+
expect(metaData.name).toBeDefined()
|
|
662
|
+
expect(metaData.description).toBeDefined();
|
|
663
|
+
expect(metaData.tabs).not.toBeDefined();
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
test('nulled', async () => {
|
|
667
|
+
let metaData = processMetadata('drumeo', 'student-review');
|
|
668
|
+
expect(metaData).toBeNull();
|
|
669
|
+
metaData = processMetadata('singeo', 'student-review');
|
|
670
|
+
expect(metaData).not.toBeNull();
|
|
671
|
+
});
|
|
552
672
|
});
|
package/test/userContext.test.js
CHANGED
|
File without changes
|
package/tools/generate-index.js
CHANGED
|
File without changes
|