musora-content-services 1.0.7 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/README.md +8 -1
- package/docs/global.html +1350 -972
- package/docs/index.html +8 -3
- package/docs/index.js.html +286 -93
- package/jest.config.js +198 -0
- package/package.json +4 -2
- package/src/index.js +190 -49
- package/test/sanityQueryService.test.js +88 -0
package/jest.config.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* For a detailed explanation regarding each configuration property, visit:
|
|
3
|
+
* https://jestjs.io/docs/configuration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** @type {import('jest').Config} */
|
|
7
|
+
const config = {
|
|
8
|
+
// All imported modules in your tests should be mocked automatically
|
|
9
|
+
// automock: false,
|
|
10
|
+
|
|
11
|
+
// Stop running tests after `n` failures
|
|
12
|
+
// bail: 0,
|
|
13
|
+
|
|
14
|
+
// The directory where Jest should store its cached dependency information
|
|
15
|
+
// cacheDirectory: "/tmp/jest_rs",
|
|
16
|
+
|
|
17
|
+
// Automatically clear mock calls, instances, contexts and results before every test
|
|
18
|
+
clearMocks: true,
|
|
19
|
+
|
|
20
|
+
// Indicates whether the coverage information should be collected while executing the test
|
|
21
|
+
collectCoverage: true,
|
|
22
|
+
|
|
23
|
+
// An array of glob patterns indicating a set of files for which coverage information should be collected
|
|
24
|
+
// collectCoverageFrom: undefined,
|
|
25
|
+
|
|
26
|
+
// The directory where Jest should output its coverage files
|
|
27
|
+
coverageDirectory: "coverage",
|
|
28
|
+
|
|
29
|
+
// An array of regexp pattern strings used to skip coverage collection
|
|
30
|
+
// coveragePathIgnorePatterns: [
|
|
31
|
+
// "/node_modules/"
|
|
32
|
+
// ],
|
|
33
|
+
|
|
34
|
+
// Indicates which provider should be used to instrument code for coverage
|
|
35
|
+
// coverageProvider: "babel",
|
|
36
|
+
|
|
37
|
+
// A list of reporter names that Jest uses when writing coverage reports
|
|
38
|
+
// coverageReporters: [
|
|
39
|
+
// "json",
|
|
40
|
+
// "text",
|
|
41
|
+
// "lcov",
|
|
42
|
+
// "clover"
|
|
43
|
+
// ],
|
|
44
|
+
|
|
45
|
+
// An object that configures minimum threshold enforcement for coverage results
|
|
46
|
+
// coverageThreshold: undefined,
|
|
47
|
+
|
|
48
|
+
// A path to a custom dependency extractor
|
|
49
|
+
// dependencyExtractor: undefined,
|
|
50
|
+
|
|
51
|
+
// Make calling deprecated APIs throw helpful error messages
|
|
52
|
+
// errorOnDeprecated: false,
|
|
53
|
+
|
|
54
|
+
// The default configuration for fake timers
|
|
55
|
+
// fakeTimers: {
|
|
56
|
+
// "enableGlobally": false
|
|
57
|
+
// },
|
|
58
|
+
|
|
59
|
+
// Force coverage collection from ignored files using an array of glob patterns
|
|
60
|
+
// forceCoverageMatch: [],
|
|
61
|
+
|
|
62
|
+
// A path to a module which exports an async function that is triggered once before all test suites
|
|
63
|
+
// globalSetup: undefined,
|
|
64
|
+
|
|
65
|
+
// A path to a module which exports an async function that is triggered once after all test suites
|
|
66
|
+
// globalTeardown: undefined,
|
|
67
|
+
|
|
68
|
+
// A set of global variables that need to be available in all test environments
|
|
69
|
+
// globals: {},
|
|
70
|
+
|
|
71
|
+
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
|
|
72
|
+
// maxWorkers: "50%",
|
|
73
|
+
|
|
74
|
+
// An array of directory names to be searched recursively up from the requiring module's location
|
|
75
|
+
// moduleDirectories: [
|
|
76
|
+
// "node_modules"
|
|
77
|
+
// ],
|
|
78
|
+
|
|
79
|
+
// An array of file extensions your modules use
|
|
80
|
+
// moduleFileExtensions: [
|
|
81
|
+
// "js",
|
|
82
|
+
// "mjs",
|
|
83
|
+
// "cjs",
|
|
84
|
+
// "jsx",
|
|
85
|
+
// "ts",
|
|
86
|
+
// "tsx",
|
|
87
|
+
// "json",
|
|
88
|
+
// "node"
|
|
89
|
+
// ],
|
|
90
|
+
|
|
91
|
+
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
|
|
92
|
+
// moduleNameMapper: {},
|
|
93
|
+
|
|
94
|
+
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
|
95
|
+
// modulePathIgnorePatterns: [],
|
|
96
|
+
|
|
97
|
+
// Activates notifications for test results
|
|
98
|
+
// notify: false,
|
|
99
|
+
|
|
100
|
+
// An enum that specifies notification mode. Requires { notify: true }
|
|
101
|
+
// notifyMode: "failure-change",
|
|
102
|
+
|
|
103
|
+
// A preset that is used as a base for Jest's configuration
|
|
104
|
+
// preset: undefined,
|
|
105
|
+
|
|
106
|
+
// Run tests from one or more projects
|
|
107
|
+
// projects: undefined,
|
|
108
|
+
|
|
109
|
+
// Use this configuration option to add custom reporters to Jest
|
|
110
|
+
// reporters: undefined,
|
|
111
|
+
|
|
112
|
+
// Automatically reset mock state before every test
|
|
113
|
+
// resetMocks: false,
|
|
114
|
+
|
|
115
|
+
// Reset the module registry before running each individual test
|
|
116
|
+
// resetModules: false,
|
|
117
|
+
|
|
118
|
+
// A path to a custom resolver
|
|
119
|
+
// resolver: undefined,
|
|
120
|
+
|
|
121
|
+
// Automatically restore mock state and implementation before every test
|
|
122
|
+
// restoreMocks: false,
|
|
123
|
+
|
|
124
|
+
// The root directory that Jest should scan for tests and modules within
|
|
125
|
+
// rootDir: undefined,
|
|
126
|
+
|
|
127
|
+
// A list of paths to directories that Jest should use to search for files in
|
|
128
|
+
// roots: [
|
|
129
|
+
// "<rootDir>"
|
|
130
|
+
// ],
|
|
131
|
+
|
|
132
|
+
// Allows you to use a custom runner instead of Jest's default test runner
|
|
133
|
+
// runner: "jest-runner",
|
|
134
|
+
|
|
135
|
+
// The paths to modules that run some code to configure or set up the testing environment before each test
|
|
136
|
+
// setupFiles: [],
|
|
137
|
+
|
|
138
|
+
// A list of paths to modules that run some code to configure or set up the testing framework before each test
|
|
139
|
+
setupFilesAfterEnv: ["dotenv/config"],
|
|
140
|
+
|
|
141
|
+
// The number of seconds after which a test is considered as slow and reported as such in the results.
|
|
142
|
+
// slowTestThreshold: 5,
|
|
143
|
+
|
|
144
|
+
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
|
|
145
|
+
// snapshotSerializers: [],
|
|
146
|
+
|
|
147
|
+
// The test environment that will be used for testing
|
|
148
|
+
// testEnvironment: "jest-environment-node",
|
|
149
|
+
|
|
150
|
+
// Options that will be passed to the testEnvironment
|
|
151
|
+
// testEnvironmentOptions: {},
|
|
152
|
+
|
|
153
|
+
// Adds a location field to test results
|
|
154
|
+
// testLocationInResults: false,
|
|
155
|
+
|
|
156
|
+
// The glob patterns Jest uses to detect test files
|
|
157
|
+
// testMatch: [
|
|
158
|
+
// "**/__tests__/**/*.[jt]s?(x)",
|
|
159
|
+
// "**/?(*.)+(spec|test).[tj]s?(x)"
|
|
160
|
+
// ],
|
|
161
|
+
|
|
162
|
+
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
|
|
163
|
+
// testPathIgnorePatterns: [
|
|
164
|
+
// "/node_modules/"
|
|
165
|
+
// ],
|
|
166
|
+
|
|
167
|
+
// The regexp pattern or array of patterns that Jest uses to detect test files
|
|
168
|
+
// testRegex: [],
|
|
169
|
+
|
|
170
|
+
// This option allows the use of a custom results processor
|
|
171
|
+
// testResultsProcessor: undefined,
|
|
172
|
+
|
|
173
|
+
// This option allows use of a custom test runner
|
|
174
|
+
// testRunner: "jest-circus/runner",
|
|
175
|
+
|
|
176
|
+
// A map from regular expressions to paths to transformers
|
|
177
|
+
// transform: undefined,
|
|
178
|
+
|
|
179
|
+
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
|
180
|
+
// transformIgnorePatterns: [
|
|
181
|
+
// "/node_modules/",
|
|
182
|
+
// "\\.pnp\\.[^\\/]+$"
|
|
183
|
+
// ],
|
|
184
|
+
|
|
185
|
+
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
|
186
|
+
// unmockedModulePathPatterns: undefined,
|
|
187
|
+
|
|
188
|
+
// Indicates whether each individual test should be reported during the run
|
|
189
|
+
// verbose: undefined,
|
|
190
|
+
|
|
191
|
+
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
|
|
192
|
+
// watchPathIgnorePatterns: [],
|
|
193
|
+
|
|
194
|
+
// Whether to use watchman for file crawling
|
|
195
|
+
// watchman: true,
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
module.exports = config;
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "musora-content-services",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "A package for Musoras content services ",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"release": "standard-version",
|
|
8
8
|
"doc": "jsdoc -c jsdoc.json --verbose",
|
|
9
|
-
"test": "
|
|
9
|
+
"test": "jest --setupFiles dotenv/config"
|
|
10
10
|
},
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
},
|
|
20
20
|
"homepage": "https://github.com/railroadmedia/musora-content-services#readme",
|
|
21
21
|
"devDependencies": {
|
|
22
|
+
"dotenv": "^16.4.5",
|
|
23
|
+
"jest": "^29.7.0",
|
|
22
24
|
"jsdoc": "^4.0.3",
|
|
23
25
|
"minami": "^1.2.3",
|
|
24
26
|
"standard-version": "^9.5.0",
|
package/src/index.js
CHANGED
|
@@ -10,7 +10,7 @@ let globalConfig = {};
|
|
|
10
10
|
* @param {string} config.dataset - The dataset name in Sanity.
|
|
11
11
|
* @param {string} config.version - The API version to use.
|
|
12
12
|
* @param {boolean} [config.debug=false] - Optional flag to enable debug mode, which logs the query and results.
|
|
13
|
-
*
|
|
13
|
+
* @param {boolean} [config.useCachedAPI=true] - Optional flag to disable cached API. *
|
|
14
14
|
* @example
|
|
15
15
|
* // Initialize the Sanity service in your app.js
|
|
16
16
|
* initializeSanityService({
|
|
@@ -19,6 +19,7 @@ let globalConfig = {};
|
|
|
19
19
|
* dataset: 'your-dataset-name',
|
|
20
20
|
* version: '2021-06-07',
|
|
21
21
|
* debug: true // Optional: Enable debug mode
|
|
22
|
+
* useCachedAPI: true // Optional: Use cached API
|
|
22
23
|
* });
|
|
23
24
|
*/
|
|
24
25
|
function initializeSanityService(config) {
|
|
@@ -39,6 +40,7 @@ async function fetchSongById(documentId) {
|
|
|
39
40
|
'album',
|
|
40
41
|
'instrumentless',
|
|
41
42
|
'soundslice',
|
|
43
|
+
'railcontent_id',
|
|
42
44
|
'"resources": resource[]{resource_url, resource_name}',
|
|
43
45
|
];
|
|
44
46
|
|
|
@@ -46,7 +48,7 @@ async function fetchSongById(documentId) {
|
|
|
46
48
|
*[_type == "song" && railcontent_id == ${documentId}]{
|
|
47
49
|
${fields.join(', ')}
|
|
48
50
|
}`;
|
|
49
|
-
return fetchSanity(query);
|
|
51
|
+
return fetchSanity(query, false);
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
/**
|
|
@@ -63,6 +65,16 @@ async function fetchArtists(brand) {
|
|
|
63
65
|
return fetchSanity(query, true);
|
|
64
66
|
}
|
|
65
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Fetch current number of artists for songs within a brand.
|
|
70
|
+
* @param {string} brand - The current brand.
|
|
71
|
+
* @returns {Promise<int|null>} - The fetched count of artists.
|
|
72
|
+
*/
|
|
73
|
+
async function fetchSongArtistCount(brand) {
|
|
74
|
+
const query = `count(*[_type == 'artist']{'lessonsCount': count(*[_type == 'song' && brand == '${brand}' && references(^._id)]._id)}[lessonsCount > 0])`;
|
|
75
|
+
return fetchSanity(query, false);
|
|
76
|
+
}
|
|
77
|
+
|
|
66
78
|
/**
|
|
67
79
|
* Fetch related songs for a specific brand and song ID.
|
|
68
80
|
* @param {string} brand - The brand for which to fetch related songs.
|
|
@@ -134,7 +146,7 @@ async function fetchRelatedSongs(brand, songId) {
|
|
|
134
146
|
])[0...10]
|
|
135
147
|
}`;
|
|
136
148
|
|
|
137
|
-
return fetchSanity(query);
|
|
149
|
+
return fetchSanity(query, true);
|
|
138
150
|
}
|
|
139
151
|
|
|
140
152
|
/**
|
|
@@ -149,7 +161,14 @@ async function fetchRelatedSongs(brand, songId) {
|
|
|
149
161
|
* @param {string} [params.groupBy=""] - The field to group the results by.
|
|
150
162
|
* @returns {Promise<Object|null>} - The fetched song data or null if not found.
|
|
151
163
|
*/
|
|
152
|
-
async function fetchAllSongs(brand, {
|
|
164
|
+
async function fetchAllSongs(brand, {
|
|
165
|
+
page = 1,
|
|
166
|
+
limit = 10,
|
|
167
|
+
searchTerm = "",
|
|
168
|
+
sort = "-published_on",
|
|
169
|
+
includedFields = [],
|
|
170
|
+
groupBy = ""
|
|
171
|
+
}) {
|
|
153
172
|
console.log('groupBy', groupBy)
|
|
154
173
|
const start = (page - 1) * limit;
|
|
155
174
|
const end = start + limit;
|
|
@@ -267,7 +286,7 @@ async function fetchAllSongs(brand, { page = 1, limit = 10, searchTerm = "", sor
|
|
|
267
286
|
`;
|
|
268
287
|
}
|
|
269
288
|
|
|
270
|
-
return fetchSanity(query);
|
|
289
|
+
return fetchSanity(query, false);
|
|
271
290
|
}
|
|
272
291
|
|
|
273
292
|
/**
|
|
@@ -275,7 +294,7 @@ async function fetchAllSongs(brand, { page = 1, limit = 10, searchTerm = "", sor
|
|
|
275
294
|
* @param {string} brand - The brand for which to fetch filter options.
|
|
276
295
|
* @returns {Promise<Object|null>} - The fetched filter options or null if not found.
|
|
277
296
|
*/
|
|
278
|
-
async function
|
|
297
|
+
async function fetchSongFilterOptions(brand) {
|
|
279
298
|
const query = `
|
|
280
299
|
{
|
|
281
300
|
"difficulty": [
|
|
@@ -296,7 +315,7 @@ async function fetchFilterOptions(brand) {
|
|
|
296
315
|
}
|
|
297
316
|
`;
|
|
298
317
|
|
|
299
|
-
return fetchSanity(query);
|
|
318
|
+
return fetchSanity(query, false);
|
|
300
319
|
}
|
|
301
320
|
|
|
302
321
|
/**
|
|
@@ -306,7 +325,7 @@ async function fetchFilterOptions(brand) {
|
|
|
306
325
|
*/
|
|
307
326
|
async function fetchSongCount(brand) {
|
|
308
327
|
const query = `count(*[_type == 'song' && brand == "${brand}"])`;
|
|
309
|
-
return fetchSanity(query);
|
|
328
|
+
return fetchSanity(query, false);
|
|
310
329
|
}
|
|
311
330
|
|
|
312
331
|
/**
|
|
@@ -326,7 +345,7 @@ async function fetchWorkouts(brand) {
|
|
|
326
345
|
web_url_path,
|
|
327
346
|
published_on
|
|
328
347
|
} | order(published_on desc)[0...5]`
|
|
329
|
-
return fetchSanity(query);
|
|
348
|
+
return fetchSanity(query, true);
|
|
330
349
|
}
|
|
331
350
|
|
|
332
351
|
/**
|
|
@@ -354,7 +373,7 @@ async function fetchNewReleases(brand) {
|
|
|
354
373
|
web_url_path,
|
|
355
374
|
published_on
|
|
356
375
|
} | order(published_on desc)[0...5]`
|
|
357
|
-
return fetchSanity(query);
|
|
376
|
+
return fetchSanity(query, true);
|
|
358
377
|
}
|
|
359
378
|
|
|
360
379
|
/**
|
|
@@ -384,7 +403,7 @@ async function fetchUpcomingEvents(brand) {
|
|
|
384
403
|
web_url_path,
|
|
385
404
|
published_on
|
|
386
405
|
} | order(published_on asc)[0...5]`;
|
|
387
|
-
return fetchSanity(query);
|
|
406
|
+
return fetchSanity(query, true);
|
|
388
407
|
}
|
|
389
408
|
|
|
390
409
|
/**
|
|
@@ -393,7 +412,7 @@ async function fetchUpcomingEvents(brand) {
|
|
|
393
412
|
* @returns {Promise<Object|null>} - The fetched content data or null if not found.
|
|
394
413
|
*/
|
|
395
414
|
async function fetchByRailContentId(id) {
|
|
396
|
-
const query = `*[railcontent_id
|
|
415
|
+
const query = `*[railcontent_id == ${id}]{
|
|
397
416
|
railcontent_id,
|
|
398
417
|
title,
|
|
399
418
|
"image": thumbnail.asset->url,
|
|
@@ -404,7 +423,7 @@ async function fetchByRailContentId(id) {
|
|
|
404
423
|
web_url_path,
|
|
405
424
|
published_on
|
|
406
425
|
}`
|
|
407
|
-
return fetchSanity(query);
|
|
426
|
+
return fetchSanity(query, false);
|
|
408
427
|
}
|
|
409
428
|
|
|
410
429
|
/**
|
|
@@ -425,7 +444,7 @@ async function fetchByRailContentIds(ids) {
|
|
|
425
444
|
web_url_path,
|
|
426
445
|
published_on
|
|
427
446
|
}`
|
|
428
|
-
return fetchSanity(query);
|
|
447
|
+
return fetchSanity(query, true);
|
|
429
448
|
}
|
|
430
449
|
|
|
431
450
|
/**
|
|
@@ -441,7 +460,14 @@ async function fetchByRailContentIds(ids) {
|
|
|
441
460
|
* @param {string} [params.groupBy=""] - The field to group the results by (e.g., 'artist', 'genre').
|
|
442
461
|
* @returns {Promise<Object|null>} - The fetched content data or null if not found.
|
|
443
462
|
*/
|
|
444
|
-
async function fetchAll(brand, type, {
|
|
463
|
+
async function fetchAll(brand, type, {
|
|
464
|
+
page = 1,
|
|
465
|
+
limit = 10,
|
|
466
|
+
searchTerm = "",
|
|
467
|
+
sort = "-published_on",
|
|
468
|
+
includedFields = [],
|
|
469
|
+
groupBy = ""
|
|
470
|
+
}) {
|
|
445
471
|
const start = (page - 1) * limit;
|
|
446
472
|
const end = start + limit;
|
|
447
473
|
|
|
@@ -486,7 +512,8 @@ async function fetchAll(brand, type, { page = 1, limit = 10, searchTerm = "", so
|
|
|
486
512
|
|
|
487
513
|
// Determine the group by clause
|
|
488
514
|
let query = "";
|
|
489
|
-
|
|
515
|
+
let manyReference = true; //TODO: define whether reference is one to one or one to many
|
|
516
|
+
if (groupBy !== "" && !manyReference) {
|
|
490
517
|
query = `
|
|
491
518
|
{
|
|
492
519
|
"total": count(*[_type == '${groupBy}' && count(*[_type == '${type}' && brand == '${brand}' && ^._id == ${groupBy}._ref ]._id) > 0]),
|
|
@@ -511,6 +538,31 @@ async function fetchAll(brand, type, { page = 1, limit = 10, searchTerm = "", so
|
|
|
511
538
|
|order(${sortOrder})
|
|
512
539
|
[${start}...${end}]
|
|
513
540
|
}`;
|
|
541
|
+
} else if (groupBy !== "" && manyReference) {
|
|
542
|
+
query = `
|
|
543
|
+
{
|
|
544
|
+
"total": count(*[_type == '${groupBy}' && count(*[_type == '${type}' && brand == '${brand}' && ^._id in ${groupBy}[]._ref]._id)>0]),
|
|
545
|
+
"entity": *[_type == '${groupBy}' && count(*[_type == '${type}' && brand == '${brand}' && ^._id in ${groupBy}[]._ref]._id) > 0]
|
|
546
|
+
{
|
|
547
|
+
'id': _id,
|
|
548
|
+
'type': _type,
|
|
549
|
+
name,
|
|
550
|
+
'head_shot_picture_url': thumbnail_url.asset->url,
|
|
551
|
+
'all_lessons_count': count(*[_type == '${type}' && brand == '${brand}' && ^._id in ${groupBy}[]._ref ]._id),
|
|
552
|
+
'lessons': *[_type == '${type}' && brand == '${brand}' && ^._id in ${groupBy}[]._ref ]{
|
|
553
|
+
railcontent_id,
|
|
554
|
+
title,
|
|
555
|
+
"image": thumbnail.asset->url,
|
|
556
|
+
difficulty,
|
|
557
|
+
difficulty_string,
|
|
558
|
+
web_url_path,
|
|
559
|
+
published_on,
|
|
560
|
+
${groupBy}
|
|
561
|
+
}[0...10]
|
|
562
|
+
}
|
|
563
|
+
|order(${sortOrder})
|
|
564
|
+
[${start}...${end}]
|
|
565
|
+
}`;
|
|
514
566
|
} else {
|
|
515
567
|
query = `
|
|
516
568
|
{
|
|
@@ -528,7 +580,65 @@ async function fetchAll(brand, type, { page = 1, limit = 10, searchTerm = "", so
|
|
|
528
580
|
`;
|
|
529
581
|
}
|
|
530
582
|
|
|
531
|
-
return fetchSanity(query);
|
|
583
|
+
return fetchSanity(query, false);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Fetches all available filter options based on various criteria such as brand, filters, style, artist, content type, and search term.
|
|
588
|
+
*
|
|
589
|
+
* This function constructs a query to retrieve the total number of results and filter options such as difficulty, instrument type, and genre.
|
|
590
|
+
* The filter options are dynamically generated based on the provided filters, style, artist, and content type.
|
|
591
|
+
*
|
|
592
|
+
* @param {string} brand - The brand for which to fetch the filter options.
|
|
593
|
+
* @param {string} filters - Additional filters to apply to the query, typically in the format of Sanity GROQ queries.
|
|
594
|
+
* @param {string} [style] - Optional style or genre to filter the results. If provided, the query will check if the style exists in the genre array.
|
|
595
|
+
* @param {string} [artist] - Optional artist name to filter the results. If provided, the query will check if the artist's name matches.
|
|
596
|
+
* @param {string} contentType - The content type to fetch (e.g., 'song', 'lesson').
|
|
597
|
+
* @param {string} [term] - Optional search term to match against various fields such as title, album, artist name, and genre.
|
|
598
|
+
*
|
|
599
|
+
* @returns {Promise<Object|null>} - A promise that resolves to an object containing the total results and filter options, or null if the query fails.
|
|
600
|
+
*
|
|
601
|
+
* @example
|
|
602
|
+
* // Example usage:
|
|
603
|
+
* fetchAllFilterOptions('myBrand', '', 'Rock', 'John Doe', 'song', 'Love')
|
|
604
|
+
* .then(options => console.log(options))
|
|
605
|
+
* .catch(error => console.error(error));
|
|
606
|
+
*/
|
|
607
|
+
async function fetchAllFilterOptions(
|
|
608
|
+
brand,
|
|
609
|
+
filters,
|
|
610
|
+
style,
|
|
611
|
+
artist,
|
|
612
|
+
contentType,
|
|
613
|
+
term
|
|
614
|
+
){
|
|
615
|
+
const query = `
|
|
616
|
+
{
|
|
617
|
+
"meta": {
|
|
618
|
+
"totalResults": count(*[_type == '${contentType}' && brand == "${brand}" && ${style ? `'${style}' in genre[]->name` : `artist->name == '${artist}'`} ${filters}
|
|
619
|
+
${term ? `&& (title match "${term}" || album match "${term}" || artist->name match "${term}" || genre[]->name match "${term}")` : ''}]),
|
|
620
|
+
"filterOptions": {
|
|
621
|
+
"difficulty": [
|
|
622
|
+
{"type": "Introductory", "count": count(*[_type == '${contentType}' && brand == '${brand}' && ${style ? `'${style}' in genre[]->name` : `artist->name == '${artist}'`} && difficulty_string == "Introductory" ${filters}])},
|
|
623
|
+
{"type": "Beginner", "count": count(*[_type == '${contentType}' && brand == '${brand}' && ${style ? `'${style}' in genre[]->name` : `artist->name == '${artist}'`} && difficulty_string == "Beginner" ${filters}])},
|
|
624
|
+
{"type": "Intermediate", "count": count(*[_type == '${contentType}' && brand == '${brand}' && ${style ? `'${style}' in genre[]->name` : `artist->name == '${artist}'`} && difficulty_string == "Intermediate" ${filters}])},
|
|
625
|
+
{"type": "Advanced", "count": count(*[_type == '${contentType}' && brand == '${brand}' && ${style ? `'${style}' in genre[]->name` : `artist->name == '${artist}'`} && difficulty_string == "Advanced" ${filters}])},
|
|
626
|
+
{"type": "Expert", "count": count(*[_type == '${contentType}' && brand == '${brand}' && ${style ? `'${style}' in genre[]->name` : `artist->name == '${artist}'`} && difficulty_string == "Expert" ${filters}])}
|
|
627
|
+
][count > 0],
|
|
628
|
+
"instrumentless": [
|
|
629
|
+
{"type": "Full Song Only", "count": count(*[_type == '${contentType}' && brand == '${brand}' && ${style ? `'${style}' in genre[]->name` : `artist->name == '${artist}'`} && instrumentless == false ${filters}])},
|
|
630
|
+
{"type": "Instrument Removed", "count": count(*[_type == '${contentType}' && brand == '${brand}' && ${style ? `'${style}' in genre[]->name` : `artist->name == '${artist}'`} && instrumentless == true ${filters}])}
|
|
631
|
+
][count > 0],
|
|
632
|
+
"genre": *[_type == 'genre' && '${contentType}' in filter_types] {
|
|
633
|
+
"type": name,
|
|
634
|
+
"count": count(*[_type == '${contentType}' && brand == "${brand}" && ${style ? `'${style}' in genre[]->name` : `artist->name == '${artist}'`} && references(^._id)])
|
|
635
|
+
}[count > 0]
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
`;
|
|
640
|
+
|
|
641
|
+
return fetchSanity(query, false);
|
|
532
642
|
}
|
|
533
643
|
|
|
534
644
|
/**
|
|
@@ -549,7 +659,7 @@ async function fetchChildren(railcontentId) {
|
|
|
549
659
|
web_url_path,
|
|
550
660
|
published_on
|
|
551
661
|
} | order(published_on asc)`
|
|
552
|
-
return fetchSanity(query);
|
|
662
|
+
return fetchSanity(query, true);
|
|
553
663
|
}
|
|
554
664
|
|
|
555
665
|
/**
|
|
@@ -570,7 +680,7 @@ async function fetchMethodNextLesson(railcontentId) {
|
|
|
570
680
|
web_url_path,
|
|
571
681
|
published_on
|
|
572
682
|
}`
|
|
573
|
-
return fetchSanity(query);
|
|
683
|
+
return fetchSanity(query, false);
|
|
574
684
|
}
|
|
575
685
|
|
|
576
686
|
/**
|
|
@@ -601,35 +711,61 @@ async function fetchNextPreviousLesson(railcontentId) {
|
|
|
601
711
|
web_url_path,
|
|
602
712
|
published_on
|
|
603
713
|
}`
|
|
604
|
-
return fetchSanity(query);
|
|
714
|
+
return fetchSanity(query, false);
|
|
605
715
|
}
|
|
606
716
|
|
|
607
717
|
/**
|
|
608
|
-
* Fetch
|
|
609
|
-
* @param {string}
|
|
610
|
-
* @
|
|
718
|
+
* Fetch the page data for a specific lesson by Railcontent ID.
|
|
719
|
+
* @param {string} railContentId - The Railcontent ID of the current lesson.
|
|
720
|
+
* @returns {Promise<Object|null>} - The fetched page data or null if found.
|
|
721
|
+
*/
|
|
722
|
+
async function fetchLessonContent(railContentId) {
|
|
723
|
+
const query = `*[railcontent_id == ${railContentId} ]
|
|
724
|
+
{title, published_on,"type":_type, "resources": resource, difficulty, difficulty_string, brand, soundslice, instrumentless, railcontent_id, "id":railcontent_id, slug, artist->,"thumbnail_url":thumbnail.asset->url, "url": web_url_path, soundslice_slug,description,
|
|
725
|
+
"chapters": chapter[]{
|
|
726
|
+
chapter_description,
|
|
727
|
+
chapter_timecode,
|
|
728
|
+
"chapter_thumbnail_url": chapter_thumbnail_url.asset->url
|
|
729
|
+
},
|
|
730
|
+
"coaches": instructor[]-> {
|
|
731
|
+
name,
|
|
732
|
+
"id":_id,
|
|
733
|
+
"coach_profile_image":thumbnail_url.asset->url
|
|
734
|
+
},
|
|
735
|
+
"instructors":instructor[]->name,
|
|
736
|
+
instructor[]->,
|
|
737
|
+
"assignments":assignment[]{
|
|
738
|
+
"id": railcontent_id,
|
|
739
|
+
"soundslice_slug": assignment_soundslice,
|
|
740
|
+
"title": assignment_title,
|
|
741
|
+
"sheet_music_image_url": assignment_sheet_music_image,
|
|
742
|
+
"timecode": assignment_timecode,
|
|
743
|
+
"description": assignment_description
|
|
744
|
+
},
|
|
745
|
+
video}`
|
|
746
|
+
return fetchSanity(query, false);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Fetch related lessons for a specific lesson by RailContent ID and type.
|
|
751
|
+
* @param {string} railContentId - The RailContent ID of the current lesson.
|
|
752
|
+
* @param {string} brand - The current brand.
|
|
611
753
|
* @returns {Promise<Array<Object>|null>} - The fetched related lessons data or null if not found.
|
|
612
754
|
*/
|
|
613
|
-
async function fetchRelatedLessons(
|
|
614
|
-
let sort = 'published_on'
|
|
615
|
-
if (type == 'rhythmic-adventures-of-captain-carson' ||
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
}
|
|
755
|
+
async function fetchRelatedLessons(railContentId, brand) {
|
|
756
|
+
// let sort = 'published_on'
|
|
757
|
+
// if (type == 'rhythmic-adventures-of-captain-carson' ||
|
|
758
|
+
// type == 'diy-drum-experiments' ||
|
|
759
|
+
// type == 'in-rhythm') {
|
|
760
|
+
// sort = 'sort';
|
|
761
|
+
// }
|
|
620
762
|
//TODO: Implement $this->contentService->getFiltered
|
|
621
|
-
const query = `*[
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
difficulty,
|
|
628
|
-
difficulty_string,
|
|
629
|
-
web_url_path,
|
|
630
|
-
published_on
|
|
631
|
-
} | order(published_on asc)[0...5]`
|
|
632
|
-
return fetchSanity(query);
|
|
763
|
+
const query = `*[railcontent_id == ${railContentId} && brand == "${brand}" && references(*[_type=='permission']._id)]{
|
|
764
|
+
"related_lessons" : array::unique([
|
|
765
|
+
...(*[_type=="song" && brand == "${brand}" && references(^.artist->_id)]{_id, "id":railcontent_id, published_on, title, "thumbnail_url":thumbnail.asset->url, difficulty_string, railcontent_id, artist->}[0...11]),
|
|
766
|
+
...(*[_type=="song" && brand == "${brand}" && references(^.genre[]->_id)]{_id, "id":railcontent_id, published_on, title, "thumbnail_url":thumbnail.asset->url, difficulty_string, railcontent_id, artist->}[0...11])
|
|
767
|
+
])|order(published_on, railcontent_id)[0...11]}`;
|
|
768
|
+
return fetchSanity(query, false);
|
|
633
769
|
}
|
|
634
770
|
|
|
635
771
|
/**
|
|
@@ -650,7 +786,7 @@ async function fetchPackAll(railcontentId) {
|
|
|
650
786
|
web_url_path,
|
|
651
787
|
published_on
|
|
652
788
|
} | order(published_on asc)[0...5]`
|
|
653
|
-
return fetchSanity(query);
|
|
789
|
+
return fetchSanity(query, true);
|
|
654
790
|
}
|
|
655
791
|
|
|
656
792
|
/**
|
|
@@ -665,9 +801,10 @@ async function fetchPackChildren(railcontentId) {
|
|
|
665
801
|
/**
|
|
666
802
|
* Fetch data from the Sanity API based on a provided query.
|
|
667
803
|
* @param {string} query - The GROQ query to execute against the Sanity API.
|
|
804
|
+
* @param {boolean} isList - Whether to return an array or single result
|
|
668
805
|
* @returns {Promise<Object|null>} - The first result from the query, or null if an error occurs or no results are found.
|
|
669
806
|
*/
|
|
670
|
-
async function fetchSanity(query, isList
|
|
807
|
+
async function fetchSanity(query, isList) {
|
|
671
808
|
// Check the config object before proceeding
|
|
672
809
|
if (!checkConfig(globalConfig)) {
|
|
673
810
|
return null;
|
|
@@ -678,18 +815,19 @@ async function fetchSanity(query, isList = false) {
|
|
|
678
815
|
}
|
|
679
816
|
|
|
680
817
|
const encodedQuery = encodeURIComponent(query);
|
|
681
|
-
const
|
|
818
|
+
const api = globalConfig.useCachedAPI ? 'apicdn' : 'api'
|
|
819
|
+
const url = `https://${globalConfig.projectId}.${api}.sanity.io/v${globalConfig.version}/data/query/${globalConfig.dataset}?query=${encodedQuery}`;
|
|
682
820
|
const headers = {
|
|
683
821
|
'Authorization': `Bearer ${globalConfig.token}`,
|
|
684
822
|
'Content-Type': 'application/json'
|
|
685
823
|
};
|
|
686
824
|
|
|
687
825
|
try {
|
|
688
|
-
const response = await fetch(url, {
|
|
826
|
+
const response = await fetch(url, {headers});
|
|
689
827
|
const result = await response.json();
|
|
690
828
|
if (result.result) {
|
|
691
829
|
if (globalConfig.debug) {
|
|
692
|
-
console.log("fetchSanity Results:", result
|
|
830
|
+
console.log("fetchSanity Results:", result);
|
|
693
831
|
}
|
|
694
832
|
return isList ? result.result : result.result[0];
|
|
695
833
|
} else {
|
|
@@ -734,13 +872,14 @@ function checkConfig(config) {
|
|
|
734
872
|
|
|
735
873
|
|
|
736
874
|
//Main
|
|
737
|
-
|
|
875
|
+
module.exports = {
|
|
738
876
|
initializeSanityService,
|
|
739
877
|
fetchSongById,
|
|
740
878
|
fetchArtists,
|
|
879
|
+
fetchSongArtistCount,
|
|
741
880
|
fetchRelatedSongs,
|
|
742
881
|
fetchAllSongs,
|
|
743
|
-
|
|
882
|
+
fetchSongFilterOptions,
|
|
744
883
|
fetchSongCount,
|
|
745
884
|
fetchWorkouts,
|
|
746
885
|
fetchNewReleases,
|
|
@@ -748,11 +887,13 @@ export {
|
|
|
748
887
|
fetchByRailContentId,
|
|
749
888
|
fetchByRailContentIds,
|
|
750
889
|
fetchAll,
|
|
890
|
+
fetchAllFilterOptions,
|
|
751
891
|
fetchMethodNextLesson,
|
|
752
892
|
fetchMethodChildren,
|
|
753
893
|
fetchNextPreviousLesson,
|
|
754
894
|
fetchRelatedLessons,
|
|
755
895
|
fetchPackAll,
|
|
756
896
|
fetchPackChildren,
|
|
897
|
+
fetchLessonContent
|
|
757
898
|
};
|
|
758
899
|
|