musora-content-services 1.0.7

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.
Files changed (67) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +39 -0
  3. package/docs/data/search.json +1 -0
  4. package/docs/fonts/Inconsolata-Regular.ttf +0 -0
  5. package/docs/fonts/OpenSans-Bold-webfont.eot +0 -0
  6. package/docs/fonts/OpenSans-Bold-webfont.svg +1830 -0
  7. package/docs/fonts/OpenSans-Bold-webfont.woff +0 -0
  8. package/docs/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
  9. package/docs/fonts/OpenSans-BoldItalic-webfont.svg +1830 -0
  10. package/docs/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
  11. package/docs/fonts/OpenSans-Italic-webfont.eot +0 -0
  12. package/docs/fonts/OpenSans-Italic-webfont.svg +1830 -0
  13. package/docs/fonts/OpenSans-Italic-webfont.woff +0 -0
  14. package/docs/fonts/OpenSans-Light-webfont.eot +0 -0
  15. package/docs/fonts/OpenSans-Light-webfont.svg +1831 -0
  16. package/docs/fonts/OpenSans-Light-webfont.woff +0 -0
  17. package/docs/fonts/OpenSans-LightItalic-webfont.eot +0 -0
  18. package/docs/fonts/OpenSans-LightItalic-webfont.svg +1835 -0
  19. package/docs/fonts/OpenSans-LightItalic-webfont.woff +0 -0
  20. package/docs/fonts/OpenSans-Regular-webfont.eot +0 -0
  21. package/docs/fonts/OpenSans-Regular-webfont.svg +1831 -0
  22. package/docs/fonts/OpenSans-Regular-webfont.woff +0 -0
  23. package/docs/fonts/OpenSans-Regular.ttf +0 -0
  24. package/docs/fonts/OpenSans-Semibold-webfont.eot +0 -0
  25. package/docs/fonts/OpenSans-Semibold-webfont.svg +1830 -0
  26. package/docs/fonts/OpenSans-Semibold-webfont.ttf +0 -0
  27. package/docs/fonts/OpenSans-Semibold-webfont.woff +0 -0
  28. package/docs/fonts/OpenSans-SemiboldItalic-webfont.eot +0 -0
  29. package/docs/fonts/OpenSans-SemiboldItalic-webfont.svg +1830 -0
  30. package/docs/fonts/OpenSans-SemiboldItalic-webfont.ttf +0 -0
  31. package/docs/fonts/OpenSans-SemiboldItalic-webfont.woff +0 -0
  32. package/docs/fonts/WorkSans-Bold.ttf +0 -0
  33. package/docs/global.html +4773 -0
  34. package/docs/index.html +93 -0
  35. package/docs/index.js.html +766 -0
  36. package/docs/scripts/core.js +726 -0
  37. package/docs/scripts/core.min.js +23 -0
  38. package/docs/scripts/linenumber.js +25 -0
  39. package/docs/scripts/prettify/Apache-License-2.0.txt +202 -0
  40. package/docs/scripts/prettify/lang-css.js +2 -0
  41. package/docs/scripts/prettify/prettify.js +28 -0
  42. package/docs/scripts/resize.js +90 -0
  43. package/docs/scripts/search.js +265 -0
  44. package/docs/scripts/search.min.js +6 -0
  45. package/docs/scripts/third-party/Apache-License-2.0.txt +202 -0
  46. package/docs/scripts/third-party/fuse.js +9 -0
  47. package/docs/scripts/third-party/hljs-line-num-original.js +369 -0
  48. package/docs/scripts/third-party/hljs-line-num.js +1 -0
  49. package/docs/scripts/third-party/hljs-original.js +5171 -0
  50. package/docs/scripts/third-party/hljs.js +1 -0
  51. package/docs/scripts/third-party/popper.js +5 -0
  52. package/docs/scripts/third-party/tippy.js +1 -0
  53. package/docs/scripts/third-party/tocbot.js +672 -0
  54. package/docs/scripts/third-party/tocbot.min.js +1 -0
  55. package/docs/styles/clean-jsdoc-theme-base.css +1159 -0
  56. package/docs/styles/clean-jsdoc-theme-dark.css +412 -0
  57. package/docs/styles/clean-jsdoc-theme-light.css +482 -0
  58. package/docs/styles/clean-jsdoc-theme-scrollbar.css +30 -0
  59. package/docs/styles/clean-jsdoc-theme-without-scrollbar.min.css +1 -0
  60. package/docs/styles/clean-jsdoc-theme.min.css +1 -0
  61. package/docs/styles/jsdoc-default.css +692 -0
  62. package/docs/styles/prettify-jsdoc.css +111 -0
  63. package/docs/styles/prettify-tomorrow.css +132 -0
  64. package/jsdoc.json +13 -0
  65. package/package.json +27 -0
  66. package/publish.sh +21 -0
  67. package/src/index.js +758 -0
@@ -0,0 +1,766 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <title>index.js - Documentation</title>
7
+
8
+ <script src="scripts/prettify/prettify.js"></script>
9
+ <script src="scripts/prettify/lang-css.js"></script>
10
+ <!--[if lt IE 9]>
11
+ <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
12
+ <![endif]-->
13
+ <link type="text/css" rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
14
+ <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
15
+ <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
16
+ </head>
17
+ <body>
18
+
19
+ <input type="checkbox" id="nav-trigger" class="nav-trigger" />
20
+ <label for="nav-trigger" class="navicon-button x">
21
+ <div class="navicon"></div>
22
+ </label>
23
+
24
+ <label for="nav-trigger" class="overlay"></label>
25
+
26
+ <nav>
27
+ <li class="nav-link nav-home-link"><a href="index.html">Home</a></li><li class="nav-heading"><a href="global.html">Globals</a></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#fetchAll">fetchAll</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#fetchAllSongs">fetchAllSongs</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#fetchArtists">fetchArtists</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#fetchByRailContentId">fetchByRailContentId</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#fetchByRailContentIds">fetchByRailContentIds</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#fetchChildren">fetchChildren</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#fetchFilterOptions">fetchFilterOptions</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#fetchMethodChildren">fetchMethodChildren</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#fetchMethodNextLesson">fetchMethodNextLesson</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#fetchNewReleases">fetchNewReleases</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#fetchNextPreviousLesson">fetchNextPreviousLesson</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#fetchPackAll">fetchPackAll</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#fetchPackChildren">fetchPackChildren</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#fetchRelatedLessons">fetchRelatedLessons</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#fetchRelatedSongs">fetchRelatedSongs</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#fetchSanity">fetchSanity</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#fetchSongById">fetchSongById</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#fetchSongCount">fetchSongCount</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#fetchUpcomingEvents">fetchUpcomingEvents</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#fetchWorkouts">fetchWorkouts</a></span></li>
28
+ </nav>
29
+
30
+ <div id="main">
31
+
32
+ <h1 class="page-title">index.js</h1>
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+ <section>
41
+ <article>
42
+ <pre class="prettyprint source linenums"><code>
43
+
44
+ /**
45
+ * Fetch a song by its document ID from Sanity.
46
+ * @param {Object} config - Configuration object containing token, projectId, dataset, version, and debug.
47
+ * @param {string} documentId - The ID of the document to fetch.
48
+ * @returns {Promise&lt;Object|null>} - The fetched song data or null if not found.
49
+ */
50
+ export async function fetchSongById(config, documentId) {
51
+ const fields = [
52
+ 'title',
53
+ '"thumbnail_url": thumbnail.asset->url',
54
+ '"style": genre[0]->name',
55
+ '"artist": artist->name',
56
+ 'album',
57
+ 'instrumentless',
58
+ 'soundslice',
59
+ '"resources": resource[]{resource_url, resource_name}',
60
+ ];
61
+
62
+ const query = `
63
+ *[_type == "song" &amp;&amp; railcontent_id == ${documentId}]{
64
+ ${fields.join(', ')}
65
+ }`;
66
+ return fetchSanity(config, query);
67
+ }
68
+
69
+ /**
70
+ * Fetch all artists with lessons available for a specific brand.
71
+ * @param {Object} config - Configuration object containing token, projectId, dataset, version, and debug.
72
+ * @param {string} brand - The brand for which to fetch artists.
73
+ * @returns {Promise&lt;Object|null>} - The fetched artist data or null if not found.
74
+ */
75
+ export async function fetchArtists(config, brand) {
76
+ const query = `
77
+ *[_type == "artist"]{
78
+ name,
79
+ "lessonsCount": count(*[_type == "song" &amp;&amp; brand == "${brand}" &amp;&amp; references(^._id)])
80
+ }[lessonsCount > 0]`;
81
+ return fetchSanity(config, query, true);
82
+ }
83
+
84
+ /**
85
+ * Fetch related songs for a specific brand and song ID.
86
+ * @param {Object} config - Configuration object containing token, projectId, dataset, version, and debug.
87
+ * @param {string} brand - The brand for which to fetch related songs.
88
+ * @param {string} songId - The ID of the song to find related songs for.
89
+ * @returns {Promise&lt;Object|null>} - The fetched related songs data or null if not found.
90
+ */
91
+ export async function fetchRelatedSongs(config, brand, songId) {
92
+ const query = `
93
+ *[_type == "song" &amp;&amp; railcontent_id == ${songId}]{
94
+ "data": array::unique([
95
+ ...(*[_type == "song" &amp;&amp; brand == "${brand}" &amp;&amp; railcontent_id != ${songId} &amp;&amp; references(^.artist->_id)]{
96
+ "type": _type,
97
+ "id": railcontent_id,
98
+ "url": web_url_path,
99
+ "published_on": published_on,
100
+ status,
101
+ "fields": [
102
+ {
103
+ "key": "title",
104
+ "value": title
105
+ },
106
+ {
107
+ "key": "artist",
108
+ "value": artist->name
109
+ },
110
+ {
111
+ "key": "difficulty",
112
+ "value": difficulty
113
+ },
114
+ {
115
+ "key": "length_in_seconds",
116
+ "value": soundslice[0].soundslice_length_in_second
117
+ }
118
+ ],
119
+ "data": [{
120
+ "key": "thumbnail_url",
121
+ "value": thumbnail.asset->url
122
+ }]
123
+ }[0...10]),
124
+ ...(*[_type == "song" &amp;&amp; brand == "${brand}" &amp;&amp; railcontent_id != ${songId} &amp;&amp; references(^.genre[]->_id)]{
125
+ "type": _type,
126
+ "id": railcontent_id,
127
+ "url": web_url_path,
128
+ "published_on": published_on,
129
+ status,
130
+ "fields": [
131
+ {
132
+ "key": "title",
133
+ "value": title
134
+ },
135
+ {
136
+ "key": "artist",
137
+ "value": artist->name
138
+ },
139
+ {
140
+ "key": "difficulty",
141
+ "value": difficulty
142
+ },
143
+ {
144
+ "key": "length_in_seconds",
145
+ "value": soundslice[0].soundslice_length_in_second
146
+ }
147
+ ],
148
+ "data": [{
149
+ "key": "thumbnail_url",
150
+ "value": thumbnail.asset->url
151
+ }]
152
+ }[0...10])
153
+ ])[0...10]
154
+ }`;
155
+
156
+ return fetchSanity(config, query);
157
+ }
158
+
159
+ /**
160
+ * Fetch all songs for a specific brand with pagination and search options.
161
+ * @param {Object} config - Configuration object containing token, projectId, dataset, version, and debug.
162
+ * @param {string} brand - The brand for which to fetch songs.
163
+ * @param {Object} params - Parameters for pagination, filtering, and sorting.
164
+ * @param {number} [params.page=1] - The page number for pagination.
165
+ * @param {number} [params.limit=10] - The number of songs per page.
166
+ * @param {string} [params.searchTerm=""] - The search term to filter songs by title or artist.
167
+ * @param {string} [params.sort="-published_on"] - The field to sort the songs by.
168
+ * @param {Array&lt;string>} [params.includedFields=[]] - The fields to include in the query.
169
+ * @param {string} [params.groupBy=""] - The field to group the results by.
170
+ * @returns {Promise&lt;Object|null>} - The fetched song data or null if not found.
171
+ */
172
+ export async function fetchAllSongs(config, brand, { page = 1, limit = 10, searchTerm = "", sort = "-published_on", includedFields = [] , groupBy = "" }) {
173
+ console.log('groupBy', groupBy)
174
+ const start = (page - 1) * limit;
175
+ const end = start + limit;
176
+
177
+ // Construct the search filter
178
+ const searchFilter = searchTerm
179
+ ? `&amp;&amp; (artist->name match "${searchTerm}*" || title match "${searchTerm}*")`
180
+ : "";
181
+
182
+ // Construct the included fields filter, replacing 'difficulty' with 'difficulty_string'
183
+ const includedFieldsFilter = includedFields.length > 0
184
+ ? includedFields.map(field => {
185
+ let [key, value] = field.split(',');
186
+ if (key === 'difficulty') {
187
+ key = 'difficulty_string';
188
+ }
189
+ return `&amp;&amp; ${key} == "${value}"`;
190
+ }).join(' ')
191
+ : "";
192
+
193
+ // Determine the sort order
194
+ let sortOrder;
195
+ switch (sort) {
196
+ case "slug":
197
+ sortOrder = "artist->name asc";
198
+ break;
199
+ case "published_on":
200
+ sortOrder = "published_on desc";
201
+ break;
202
+ case "-published_on":
203
+ sortOrder = "published_on asc";
204
+ break;
205
+ case "-slug":
206
+ sortOrder = "artist->name desc";
207
+ break;
208
+ case "-popularity":
209
+ sortOrder = "popularity desc";
210
+ break;
211
+ default:
212
+ sortOrder = "published_on asc";
213
+ break;
214
+ }
215
+
216
+ // Determine the group by clause
217
+ let query = "";
218
+ if (groupBy === "artist") {
219
+ query = `
220
+ {
221
+ "total": count(*[_type == 'artist' &amp;&amp; count(*[_type == 'song' &amp;&amp; brand == '${brand}' &amp;&amp; ^._id == artist._ref ]._id) > 0]),
222
+ "entity": *[_type == 'artist' &amp;&amp; count(*[_type == 'song' &amp;&amp; brand == '${brand}' &amp;&amp; ^._id == artist._ref ]._id) > 0]
223
+ {
224
+ 'id': _id,
225
+ 'type': _type,
226
+ name,
227
+ 'head_shot_picture_url': thumbnail_url.asset->url,
228
+ 'all_lessons_count': count(*[_type == 'song' &amp;&amp; brand == '${brand}' &amp;&amp; ^._id == artist._ref ]._id),
229
+ 'lessons': *[_type == 'song' &amp;&amp; brand == '${brand}' &amp;&amp; ^._id == artist._ref ]{
230
+ railcontent_id,
231
+ title,
232
+ "image": thumbnail.asset->url,
233
+ "artist_name": artist->name,
234
+ artist,
235
+ difficulty,
236
+ difficulty_string,
237
+ web_url_path,
238
+ published_on
239
+ }[0...10]
240
+ }
241
+ |order(${sortOrder})
242
+ [${start}...${end}]
243
+ }`;
244
+ } else if (groupBy === "genre") {
245
+ query = `
246
+ {
247
+ "total": count(*[_type == 'genre' &amp;&amp; count(*[_type == 'song' &amp;&amp; brand == '${brand}' &amp;&amp; ^._id in genre[]._ref ]._id) > 0]),
248
+ "entity":
249
+ *[_type == 'genre' &amp;&amp; count(*[_type == 'song' &amp;&amp; brand == '${brand}' &amp;&amp; ^._id in genre[]._ref ]._id)>0]
250
+ {
251
+ 'id': _id,
252
+ 'type': _type,
253
+ name,
254
+ 'head_shot_picture_url': thumbnail_url.asset->url,
255
+ 'all_lessons_count': count(*[_type == 'song' &amp;&amp; brand == '${brand}' &amp;&amp; ^._id in genre[]._ref ]._id),
256
+ 'lessons': *[_type == 'song' &amp;&amp; brand == '${brand}' &amp;&amp; ^._id in genre[]._ref ]{
257
+ railcontent_id,
258
+ title,
259
+ "image": thumbnail.asset->url,
260
+ "artist_name": artist->name,
261
+ artist,
262
+ difficulty,
263
+ difficulty_string,
264
+ web_url_path,
265
+ published_on
266
+ }[0...10]
267
+ }
268
+ |order(${sortOrder})
269
+ [${start}...${end}]
270
+ }`;
271
+ } else {
272
+ query = `
273
+ {
274
+ "entity": *[_type == 'song' &amp;&amp; brand == "${brand}" ${searchFilter} ${includedFieldsFilter}] | order(${sortOrder}) [${start}...${end}] {
275
+ railcontent_id,
276
+ title,
277
+ "image": thumbnail.asset->url,
278
+ "artist_name": artist->name,
279
+ artist,
280
+ difficulty,
281
+ difficulty_string,
282
+ web_url_path,
283
+ published_on
284
+ },
285
+ "total": count(*[_type == 'song' &amp;&amp; brand == "${brand}" ${searchFilter} ${includedFieldsFilter}])
286
+ }
287
+ `;
288
+ }
289
+
290
+ return fetchSanity(config, query);
291
+ }
292
+
293
+ /**
294
+ * Fetch filter options for a specific brand.
295
+ * @param {Object} config - Configuration object containing token, projectId, dataset, version, and debug.
296
+ * @param {string} brand - The brand for which to fetch filter options.
297
+ * @returns {Promise&lt;Object|null>} - The fetched filter options or null if not found.
298
+ */
299
+ export async function fetchFilterOptions(config, brand) {
300
+ const query = `
301
+ {
302
+ "difficulty": [
303
+ {"type": "Introductory", "count": count(*[_type == 'song' &amp;&amp; brand == ${brand} &amp;&amp; difficulty_string == "Introductory"]._id)},
304
+ {"type": "Beginner", "count": count(*[_type == 'song' &amp;&amp; brand == ${brand} &amp;&amp; difficulty_string == "Beginner"]._id)},
305
+ {"type": "Intermediate", "count": count(*[_type == 'song' &amp;&amp; brand == ${brand} &amp;&amp; difficulty_string == "Intermediate"]._id)},
306
+ {"type": "Advanced", "count": count(*[_type == 'song' &amp;&amp; brand == ${brand} &amp;&amp; difficulty_string == "Advanced"]._id)},
307
+ {"type": "Expert", "count": count(*[_type == 'song' &amp;&amp; brand == ${brand} &amp;&amp; difficulty_string == "Expert"]._id)}
308
+ ],
309
+ "genre": *[_type == 'genre' &amp;&amp; 'song' in filter_types] {
310
+ "type": name,
311
+ "count": count(*[_type == 'song' &amp;&amp; brand == ${brand} &amp;&amp; references(^._id)]._id)
312
+ },
313
+ "instrumentless": [
314
+ {"type": "Full Song Only", "count": count(*[_type == 'song' &amp;&amp; brand == ${brand} &amp;&amp; instrumentless == false]._id)},
315
+ {"type": "Instrument Removed", "count": count(*[_type == 'song' &amp;&amp; brand == ${brand} &amp;&amp; instrumentless == true]._id)}
316
+ ]
317
+ }
318
+ `;
319
+
320
+ return fetchSanity(config, query);
321
+ }
322
+
323
+ /**
324
+ * Fetch the total count of songs for a specific brand.
325
+ * @param {Object} config - Configuration object containing token, projectId, dataset, version, and debug.
326
+ * @param {string} brand - The brand for which to fetch the song count.
327
+ * @returns {Promise&lt;number|null>} - The total count of songs or null if an error occurs.
328
+ */
329
+ export async function fetchSongCount(config, brand) {
330
+ const query = `count(*[_type == 'song' &amp;&amp; brand == "${brand}"])`;
331
+ return fetchSanity(config, query);
332
+ }
333
+
334
+ /**
335
+ * Fetch the latest workouts for the home page of a specific brand.
336
+ * @param {Object} config - Configuration object containing token, projectId, dataset, version, and debug.
337
+ * @param {string} brand - The brand for which to fetch workouts.
338
+ * @returns {Promise&lt;Object|null>} - The fetched workout data or null if not found.
339
+ */
340
+ export async function fetchWorkouts(config, brand) {
341
+ const query = `*[_type == 'workout' &amp;&amp; brand == '${brand}'] [0...5] {
342
+ railcontent_id,
343
+ title,
344
+ "image": thumbnail.asset->url,
345
+ "artist_name": artist->name,
346
+ artist,
347
+ difficulty,
348
+ difficulty_string,
349
+ web_url_path,
350
+ published_on
351
+ } | order(published_on desc)[0...5]`
352
+ return fetchSanity(config, query);
353
+ }
354
+
355
+ /**
356
+ * Fetch the latest new releases for a specific brand.
357
+ * @param {Object} config - Configuration object containing token, projectId, dataset, version, and debug.
358
+ * @param {string} brand - The brand for which to fetch new releases.
359
+ * @returns {Promise&lt;Object|null>} - The fetched new releases data or null if not found.
360
+ */
361
+ export async function fetchNewReleases(config, brand) {
362
+ const newTypes = {
363
+ 'drumeo': ["drum-fest-international-2022", "spotlight", "the-history-of-electronic-drums", "backstage-secrets", "quick-tips", "question-and-answer", "student-collaborations", "live-streams", "live", "podcasts", "solos", "boot-camps", "gear-guides", "performances", "in-rhythm", "challenges", "on-the-road", "diy-drum-experiments", "rhythmic-adventures-of-captain-carson", "study-the-greats", "rhythms-from-another-planet", "tama-drums", "paiste-cymbals", "behind-the-scenes", "exploring-beats", "sonor-drums", "course", "play-along", "student-focus", "coach-stream", "learning-path-level", "unit", "quick-tips", "live", "question-and-answer", "student-review", "boot-camps", "song", "chords-and-scales", "pack", "podcasts", "workout", "challenge", "challenge-part"],
364
+ 'pianote': ["student-review", "student-reviews", "question-and-answer", "course", "play-along", "student-focus", "coach-stream", "learning-path-level", "unit", "quick-tips", "live", "question-and-answer", "student-review", "boot-camps", "song", "chords-and-scales", "pack", "podcasts", "workout", "challenge", "challenge-part"],
365
+ 'guitareo': ["student-review", "student-reviews", "question-and-answer", "archives", "recording", "course", "play-along", "student-focus", "coach-stream", "learning-path-level", "unit", "quick-tips", "live", "question-and-answer", "student-review", "boot-camps", "song", "chords-and-scales", "pack", "podcasts", "workout", "challenge", "challenge-part"],
366
+ 'singeo': ["student-review", "student-reviews", "question-and-answer", "course", "play-along", "student-focus", "coach-stream", "learning-path-level", "unit", "quick-tips", "live", "question-and-answer", "student-review", "boot-camps", "song", "chords-and-scales", "pack", "podcasts", "workout", "challenge", "challenge-part"],
367
+ 'default': ["student-review", "student-reviews", "question-and-answer", "course", "play-along", "student-focus", "coach-stream", "learning-path-level", "unit", "quick-tips", "live", "question-and-answer", "student-review", "boot-camps", "song", "chords-and-scales", "pack", "podcasts", "workout", "challenge", "challenge-part"]
368
+ };
369
+ const typesString = arrayJoinWithQuotes(newTypes[brand] ?? newTypes['default']);
370
+ const query = `*[_type in [${typesString}] &amp;&amp; brand == '${brand}'] | order(releaseDate desc) [0...5] {
371
+ railcontent_id,
372
+ title,
373
+ "image": thumbnail.asset->url,
374
+ "artist_name": artist->name,
375
+ artist,
376
+ difficulty,
377
+ difficulty_string,
378
+ web_url_path,
379
+ published_on
380
+ } | order(published_on desc)[0...5]`
381
+ return fetchSanity(config, query);
382
+ }
383
+
384
+ /**
385
+ * Fetch upcoming events for a specific brand.
386
+ * @param {Object} config - Configuration object containing token, projectId, dataset, version, and debug.
387
+ * @param {string} brand - The brand for which to fetch upcoming events.
388
+ * @returns {Promise&lt;Object|null>} - The fetched upcoming events data or null if not found.
389
+ */
390
+ export async function fetchUpcomingEvents(config, brand) {
391
+ const liveTypes = {
392
+ 'drumeo': ["drum-fest-international-2022", "spotlight", "the-history-of-electronic-drums", "backstage-secrets", "quick-tips", "question-and-answer", "student-collaborations", "live-streams", "live", "podcasts", "solos", "boot-camps", "gear-guides", "performances", "in-rhythm", "challenges", "on-the-road", "diy-drum-experiments", "rhythmic-adventures-of-captain-carson", "study-the-greats", "rhythms-from-another-planet", "tama-drums", "paiste-cymbals", "behind-the-scenes", "exploring-beats", "sonor-drums", "student-focus", "coach-stream", "live", "question-and-answer", "student-review", "boot-camps", "recording", "pack-bundle-lesson"],
393
+ 'pianote': ["student-review", "student-reviews", "question-and-answer", "student-focus", "coach-stream", "live", "question-and-answer", "student-review", "boot-camps", "recording", "pack-bundle-lesson"],
394
+ 'guitareo': ["student-review", "student-reviews", "question-and-answer", "archives", "recording", "student-focus", "coach-stream", "live", "question-and-answer", "student-review", "boot-camps", "recording", "pack-bundle-lesson"],
395
+ 'singeo': ["student-review", "student-reviews", "question-and-answer", "student-focus", "coach-stream", "live", "question-and-answer", "student-review", "boot-camps", "recording", "pack-bundle-lesson"],
396
+ 'default': ["student-review", "student-reviews", "question-and-answer", "student-focus", "coach-stream", "live", "question-and-answer", "student-review", "boot-camps", "recording", "pack-bundle-lesson"]
397
+ };
398
+ const typesString = arrayJoinWithQuotes(liveTypes[brand] ?? liveTypes['default']);
399
+ const now = getSanityDate(new Date());
400
+ //TODO: status = 'scheduled' is this handled in sanity?
401
+ const query = `*[_type in [${typesString}] &amp;&amp; brand == '${brand}' &amp;&amp; published_on > '${now}']{
402
+ railcontent_id,
403
+ title,
404
+ "image": thumbnail.asset->url,
405
+ "artist_name": artist->name,
406
+ artist,
407
+ difficulty,
408
+ difficulty_string,
409
+ web_url_path,
410
+ published_on
411
+ } | order(published_on asc)[0...5]`;
412
+ return fetchSanity(config, query);
413
+ }
414
+
415
+ /**
416
+ * Fetch content by a specific Railcontent ID.
417
+ * @param {Object} config - Configuration object containing token, projectId, dataset, version, and debug.
418
+ * @param {string} id - The Railcontent ID of the content to fetch.
419
+ * @returns {Promise&lt;Object|null>} - The fetched content data or null if not found.
420
+ */
421
+ export async function fetchByRailContentId(config, id) {
422
+ const query = `*[railcontent_id = ${id}]{
423
+ railcontent_id,
424
+ title,
425
+ "image": thumbnail.asset->url,
426
+ "artist_name": artist->name,
427
+ artist,
428
+ difficulty,
429
+ difficulty_string,
430
+ web_url_path,
431
+ published_on
432
+ }`
433
+ return fetchSanity(config, query);
434
+ }
435
+
436
+ /**
437
+ * Fetch content by an array of Railcontent IDs.
438
+ * @param {Object} config - Configuration object containing token, projectId, dataset, version, and debug.
439
+ * @param {Array&lt;string>} ids - The array of Railcontent IDs of the content to fetch.
440
+ * @returns {Promise&lt;Array&lt;Object>|null>} - The fetched content data or null if not found.
441
+ */
442
+ export async function fetchByRailContentIds(config, ids) {
443
+ const idsString = ids.join(',');
444
+ const query = `*[railcontent_id in [${idsString}]]{
445
+ railcontent_id,
446
+ title,
447
+ "image": thumbnail.asset->url,
448
+ "artist_name": artist->name,
449
+ artist,
450
+ difficulty,
451
+ difficulty_string,
452
+ web_url_path,
453
+ published_on
454
+ }`
455
+ return fetchSanity(config, query);
456
+ }
457
+
458
+ /**
459
+ * Fetch all content for a specific brand and type with pagination, search, and grouping options.
460
+ * @param {Object} config - Configuration object containing token, projectId, dataset, version, and debug.
461
+ * @param {string} brand - The brand for which to fetch content.
462
+ * @param {string} type - The content type to fetch (e.g., 'song', 'artist').
463
+ * @param {Object} params - Parameters for pagination, filtering, sorting, and grouping.
464
+ * @param {number} [params.page=1] - The page number for pagination.
465
+ * @param {number} [params.limit=10] - The number of items per page.
466
+ * @param {string} [params.searchTerm=""] - The search term to filter content by title or artist.
467
+ * @param {string} [params.sort="-published_on"] - The field to sort the content by.
468
+ * @param {Array&lt;string>} [params.includedFields=[]] - The fields to include in the query.
469
+ * @param {string} [params.groupBy=""] - The field to group the results by (e.g., 'artist', 'genre').
470
+ * @returns {Promise&lt;Object|null>} - The fetched content data or null if not found.
471
+ */
472
+ export async function fetchAll(config, brand, type, { page = 1, limit = 10, searchTerm = "", sort = "-published_on", includedFields = [], groupBy = "" }) {
473
+ const start = (page - 1) * limit;
474
+ const end = start + limit;
475
+
476
+ // Construct the search filter
477
+ const searchFilter = searchTerm
478
+ ? `&amp;&amp; (artist->name match "${searchTerm}*" || title match "${searchTerm}*")`
479
+ : "";
480
+
481
+ // Construct the included fields filter, replacing 'difficulty' with 'difficulty_string'
482
+ const includedFieldsFilter = includedFields.length > 0
483
+ ? includedFields.map(field => {
484
+ let [key, value] = field.split(',');
485
+ if (key === 'difficulty') {
486
+ key = 'difficulty_string';
487
+ }
488
+ return `&amp;&amp; ${key} == "${value}"`;
489
+ }).join(' ')
490
+ : "";
491
+
492
+ // Determine the sort order
493
+ let sortOrder;
494
+ switch (sort) {
495
+ case "slug":
496
+ sortOrder = "artist->name asc";
497
+ break;
498
+ case "published_on":
499
+ sortOrder = "published_on desc";
500
+ break;
501
+ case "-published_on":
502
+ sortOrder = "published_on asc";
503
+ break;
504
+ case "-slug":
505
+ sortOrder = "artist->name desc";
506
+ break;
507
+ case "-popularity":
508
+ sortOrder = "popularity desc";
509
+ break;
510
+ default:
511
+ sortOrder = "published_on asc";
512
+ break;
513
+ }
514
+
515
+ // Determine the group by clause
516
+ let query = "";
517
+ if (groupBy !== "") {
518
+ query = `
519
+ {
520
+ "total": count(*[_type == '${groupBy}' &amp;&amp; count(*[_type == '${type}' &amp;&amp; brand == '${brand}' &amp;&amp; ^._id == ${groupBy}._ref ]._id) > 0]),
521
+ "entity": *[_type == '${groupBy}' &amp;&amp; count(*[_type == '${type}' &amp;&amp; brand == '${brand}' &amp;&amp; ^._id == ${groupBy}._ref ]._id) > 0]
522
+ {
523
+ 'id': _id,
524
+ 'type': _type,
525
+ name,
526
+ 'head_shot_picture_url': thumbnail_url.asset->url,
527
+ 'all_lessons_count': count(*[_type == '${type}' &amp;&amp; brand == '${brand}' &amp;&amp; ^._id == ${groupBy}._ref ]._id),
528
+ 'lessons': *[_type == '${type}' &amp;&amp; brand == '${brand}' &amp;&amp; ^._id == ${groupBy}._ref ]{
529
+ railcontent_id,
530
+ title,
531
+ "image": thumbnail.asset->url,
532
+ difficulty,
533
+ difficulty_string,
534
+ web_url_path,
535
+ published_on,
536
+ ${groupBy}
537
+ }[0...10]
538
+ }
539
+ |order(${sortOrder})
540
+ [${start}...${end}]
541
+ }`;
542
+ } else {
543
+ query = `
544
+ {
545
+ "entity": *[_type == '${type}' &amp;&amp; brand == "${brand}" ${searchFilter} ${includedFieldsFilter}] | order(${sortOrder}) [${start}...${end}] {
546
+ railcontent_id,
547
+ title,
548
+ "image": thumbnail.asset->url,
549
+ difficulty,
550
+ difficulty_string,
551
+ web_url_path,
552
+ published_on
553
+ },
554
+ "total": count(*[_type == '${type}' &amp;&amp; brand == "${brand}" ${searchFilter} ${includedFieldsFilter}])
555
+ }
556
+ `;
557
+ }
558
+
559
+ return fetchSanity(config, query);
560
+ }
561
+
562
+ /**
563
+ * Fetch children content by Railcontent ID.
564
+ * @param {Object} config - Configuration object containing token, projectId, dataset, version, and debug.
565
+ * @param {string} railcontentId - The Railcontent ID of the parent content.
566
+ * @returns {Promise&lt;Array&lt;Object>|null>} - The fetched children content data or null if not found.
567
+ */
568
+ export async function fetchChildren(config, railcontentId) {
569
+ //TODO: Implement getByParentId include sum XP
570
+ const query = `*[_railcontent_id == ${railcontentId}]{
571
+ railcontent_id,
572
+ title,
573
+ "image": thumbnail.asset->url,
574
+ "artist_name": artist->name,
575
+ artist,
576
+ difficulty,
577
+ difficulty_string,
578
+ web_url_path,
579
+ published_on
580
+ } | order(published_on asc)`
581
+ return fetchSanity(config, query);
582
+ }
583
+
584
+ /**
585
+ * Fetch the next lesson for a specific method by Railcontent ID.
586
+ * @param {Object} config - Configuration object containing token, projectId, dataset, version, and debug.
587
+ * @param {string} railcontentId - The Railcontent ID of the current lesson.
588
+ * @returns {Promise&lt;Object|null>} - The fetched next lesson data or null if not found.
589
+ */
590
+ export async function fetchMethodNextLesson(config, railcontentId) {
591
+ //TODO: Implement getNextContentForParentContentForUser
592
+ const query = `*[_railcontent_id == ${railcontentId}]{
593
+ railcontent_id,
594
+ title,
595
+ "image": thumbnail.asset->url,
596
+ "artist_name": artist->name,
597
+ artist,
598
+ difficulty,
599
+ difficulty_string,
600
+ web_url_path,
601
+ published_on
602
+ }`
603
+ return fetchSanity(config, query);
604
+ }
605
+
606
+ /**
607
+ * Fetch all children of a specific method by Railcontent ID.
608
+ * @param {Object} config - Configuration object containing token, projectId, dataset, version, and debug.
609
+ * @param {string} railcontentId - The Railcontent ID of the method.
610
+ * @returns {Promise&lt;Array&lt;Object>|null>} - The fetched children data or null if not found.
611
+ */
612
+ export async function fetchMethodChildren(config, railcontentId) {
613
+ //TODO: Implement getByParentId include sum XP
614
+ return fetchChildren(railcontentId);
615
+ }
616
+
617
+ /**
618
+ * Fetch the next and previous lessons for a specific lesson by Railcontent ID.
619
+ * @param {Object} config - Configuration object containing token, projectId, dataset, version, and debug.
620
+ * @param {string} railcontentId - The Railcontent ID of the current lesson.
621
+ * @returns {Promise&lt;Object|null>} - The fetched next and previous lesson data or null if found.
622
+ */
623
+ export async function fetchNextPreviousLesson(config, railcontentId) {
624
+ //TODO: Implement getTypeNeighbouringSiblings/getNextAndPreviousLessons
625
+ const query = `*[_railcontent_id == ${railcontentId}]{
626
+ railcontent_id,
627
+ title,
628
+ "image": thumbnail.asset->url,
629
+ "artist_name": artist->name,
630
+ artist,
631
+ difficulty,
632
+ difficulty_string,
633
+ web_url_path,
634
+ published_on
635
+ }`
636
+ return fetchSanity(config, query);
637
+ }
638
+
639
+ /**
640
+ * Fetch related lessons for a specific lesson by Railcontent ID and type.
641
+ * @param {Object} config - Configuration object containing token, projectId, dataset, version, and debug.
642
+ * @param {string} railcontentId - The Railcontent ID of the current lesson.
643
+ * @param {string} type - The type of related lessons to fetch.
644
+ * @returns {Promise&lt;Array&lt;Object>|null>} - The fetched related lessons data or null if not found.
645
+ */
646
+ export async function fetchRelatedLessons(config, railcontentId, type) {
647
+ let sort = 'published_on'
648
+ if (type == 'rhythmic-adventures-of-captain-carson' ||
649
+ type == 'diy-drum-experiments' ||
650
+ type == 'in-rhythm') {
651
+ sort = 'sort';
652
+ }
653
+ //TODO: Implement $this->contentService->getFiltered
654
+ const query = `*[_railcontent_id == ${railcontentId}]{
655
+ railcontent_id,
656
+ title,
657
+ "image": thumbnail.asset->url,
658
+ "artist_name": artist->name,
659
+ artist,
660
+ difficulty,
661
+ difficulty_string,
662
+ web_url_path,
663
+ published_on
664
+ } | order(published_on asc)[0...5]`
665
+ return fetchSanity(config, query);
666
+ }
667
+
668
+ /**
669
+ * Fetch all content for a specific pack by Railcontent ID.
670
+ * @param {Object} config - Configuration object containing token, projectId, dataset, version, and debug.
671
+ * @param {string} railcontentId - The Railcontent ID of the pack.
672
+ * @returns {Promise&lt;Array&lt;Object>|null>} - The fetched pack content data or null if not found.
673
+ */
674
+ export async function fetchPackAll(config, railcontentId) {
675
+ //TODO: Implement getPacks
676
+ const query = `*[_railcontent_id == ${railcontentId}]{
677
+ railcontent_id,
678
+ title,
679
+ "image": thumbnail.asset->url,
680
+ "artist_name": artist->name,
681
+ artist,
682
+ difficulty,
683
+ difficulty_string,
684
+ web_url_path,
685
+ published_on
686
+ } | order(published_on asc)[0...5]`
687
+ return fetchSanity(config, query);
688
+ }
689
+
690
+ /**
691
+ * Fetch all children of a specific pack by Railcontent ID.
692
+ * @param {Object} config - Configuration object containing token, projectId, dataset, version, and debug.
693
+ * @param {string} railcontentId - The Railcontent ID of the pack.
694
+ * @returns {Promise&lt;Array&lt;Object>|null>} - The fetched pack children data or null if not found.
695
+ */
696
+ export async function fetchPackChildren(config, railcontentId) {
697
+ return fetchChildren(railcontentId, 'pack');
698
+ }
699
+
700
+ /**
701
+ * Fetch data from the Sanity API based on a provided query.
702
+ * @param {Object} config - Configuration object containing the Sanity API settings.
703
+ * @param {string} config.token - The API token for authenticating with Sanity.
704
+ * @param {string} config.projectId - The project ID in Sanity.
705
+ * @param {string} config.dataset - The dataset name in Sanity.
706
+ * @param {string} config.version - The API version to use.
707
+ * @param {boolean} [config.debug=false] - Flag to enable debug mode, which logs the query and results.
708
+ * @param {string} query - The GROQ query to execute against the Sanity API.
709
+ * @returns {Promise&lt;Object|null>} - The first result from the query, or null if an error occurs or no results are found.
710
+ */
711
+ async function fetchSanity({ token, projectId, dataset, version, debug = false }, query, isList = false) {
712
+ if (debug) {
713
+ console.log("fetchSanity Query:", query);
714
+ }
715
+ const encodedQuery = encodeURIComponent(query);
716
+ const url = `https://${projectId}.apicdn.sanity.io/v${version}/data/query/${dataset}?query=${encodedQuery}`;
717
+ const headers = {
718
+ 'Authorization': `Bearer ${token}`,
719
+ 'Content-Type': 'application/json'
720
+ };
721
+
722
+ try {
723
+ const response = await fetch(url, {headers});
724
+ const result = await response.json();
725
+ if (result.result) {
726
+ if (debug) {
727
+ console.log("fetchSanity Results:", result.result);
728
+ }
729
+ return isList ? result.result : result.result[0];
730
+ } else {
731
+ throw new Error('No results found');
732
+ }
733
+ } catch (error) {
734
+ console.error('sanityQueryService: Fetch error:', error);
735
+ return null;
736
+ }
737
+ }
738
+
739
+ function arrayJoinWithQuotes(array, delimiter = ',') {
740
+ const wrapped = array.map(value => `'${value}'`);
741
+ return wrapped.join(delimiter)
742
+ }
743
+
744
+ function getSanityDate(date) {
745
+ return date.toISOString();
746
+ }
747
+
748
+ </code></pre>
749
+ </article>
750
+ </section>
751
+
752
+
753
+
754
+
755
+ </div>
756
+
757
+ <br class="clear">
758
+
759
+ <footer>
760
+ Generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.3</a> on Fri Aug 09 2024 20:57:53 GMT+0000 (Coordinated Universal Time) using the Minami theme.
761
+ </footer>
762
+
763
+ <script>prettyPrint();</script>
764
+ <script src="scripts/linenumber.js"></script>
765
+ </body>
766
+ </html>