musora-content-services 1.3.19 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +16 -0
- package/CHANGELOG.md +1 -3
- package/docs/config.js.html +14 -5
- package/docs/content.js.html +425 -0
- package/docs/global.html +3026 -0
- package/docs/index.html +2 -2
- package/docs/module-Config.html +60 -7
- package/docs/module-Content-Services-V2.html +2433 -0
- package/docs/module-Railcontent-Services.html +522 -2
- package/docs/module-Sanity-Services.html +57 -43
- package/docs/module-Session-Management.html +575 -0
- package/docs/module-User-Permissions.html +406 -0
- package/docs/railcontent.js.html +42 -5
- package/docs/sanity.js.html +290 -103
- package/docs/user_permissions.js.html +110 -0
- package/docs/user_sessions.js.html +139 -0
- package/docs/user_types.js.html +188 -0
- package/jsdoc.json +2 -0
- package/package.json +1 -1
- package/publish.sh +2 -2
- package/src/contentMetaData.js +307 -1088
- package/src/contentTypeConfig.js +108 -4
- package/src/filterBuilder.js +6 -6
- package/src/index.d.ts +61 -6
- package/src/index.js +61 -6
- package/src/{services → lib}/lastUpdated.js +17 -1
- package/src/services/config.js +0 -0
- package/src/services/content.js +371 -0
- package/src/services/dataContext.js +0 -0
- package/src/services/forum.js +57 -0
- package/src/services/railcontent.js +124 -11
- package/src/services/recommendations.js +19 -0
- package/src/services/sanity.js +278 -104
- package/src/services/{userPermissions.js → user/permissions.js} +16 -2
- package/src/services/user/sessions.js +67 -0
- package/src/services/user/types.js +116 -0
- package/src/services/userActivity.js +32 -0
- package/test/content.test.js +116 -0
- package/test/contentLikes.test.js +0 -0
- package/test/contentProgress.test.js +83 -5
- package/test/forum.test.js +18 -0
- package/test/initializeTests.js +6 -1
- package/test/{lastUpdated.test.js → lib/lastUpdated.test.js} +2 -5
- package/test/sanityQueryService.test.js +66 -18
- package/test/{userPermissions.test.js → user/permissions.test.js} +3 -3
- package/tools/generate-index.cjs +16 -3
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module Content-Services-V2
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
fetchAll,
|
|
7
|
+
fetchByRailContentIds,
|
|
8
|
+
fetchMetadata,
|
|
9
|
+
fetchRecent,
|
|
10
|
+
fetchTabData,
|
|
11
|
+
fetchNewReleases,
|
|
12
|
+
fetchUpcomingEvents,
|
|
13
|
+
fetchScheduledReleases,
|
|
14
|
+
fetchReturning,
|
|
15
|
+
fetchLeaving, fetchScheduledAndNewReleases
|
|
16
|
+
} from './sanity.js'
|
|
17
|
+
import {TabResponseType, Tabs, capitalizeFirstLetter} from '../contentMetaData.js'
|
|
18
|
+
import {getAllStartedOrCompleted} from "./contentProgress";
|
|
19
|
+
import {fetchHandler} from "./railcontent";
|
|
20
|
+
import {recommendations} from "./recommendations";
|
|
21
|
+
|
|
22
|
+
export async function getLessonContentRows (brand='drumeo', pageName = 'lessons') {
|
|
23
|
+
let recentContentIds = await fetchRecent(brand, pageName, { progress: 'recent' });
|
|
24
|
+
recentContentIds = recentContentIds.map(item => item.id);
|
|
25
|
+
|
|
26
|
+
let contentRows = await getContentRows(brand, pageName);
|
|
27
|
+
contentRows = Array.isArray(contentRows) ? contentRows : [];
|
|
28
|
+
contentRows.unshift({
|
|
29
|
+
id: 'recent',
|
|
30
|
+
title: 'Recent ' + capitalizeFirstLetter(pageName),
|
|
31
|
+
content: recentContentIds || []
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const results = await Promise.all(
|
|
35
|
+
contentRows.map(async (row) => {
|
|
36
|
+
if (row.content.length == 0){
|
|
37
|
+
return { id: row.id, title: row.title, items: [] }
|
|
38
|
+
}
|
|
39
|
+
const data = await fetchByRailContentIds(row.content)
|
|
40
|
+
return { id: row.id, title: row.title, items: data }
|
|
41
|
+
})
|
|
42
|
+
)
|
|
43
|
+
return results
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Get data that should be displayed for a specific tab with pagination
|
|
48
|
+
* @param {string} brand - The brand for which to fetch data.
|
|
49
|
+
* @param {string} pageName - The page name (e.g., 'lessons', 'songs','challenges).
|
|
50
|
+
* @param {string} tabName - The name for the selected tab. Should be same name received from fetchMetadata (e.g., 'Individuals', 'Collections','For You').
|
|
51
|
+
* @param {Object} params - Parameters for pagination, sorting, and filter.
|
|
52
|
+
* @param {number} [params.page=1] - The page number for pagination.
|
|
53
|
+
* @param {number} [params.limit=10] - The number of items per page.
|
|
54
|
+
* @param {string} [params.sort="-published_on"] - The field to sort the data by.
|
|
55
|
+
* @param {Array<string>} [params.selectedFilters=[]] - The selected filter.
|
|
56
|
+
* @returns {Promise<Object|null>} - The fetched content data or null if not found.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* getTabResults('drumeo', 'lessons','Singles', {
|
|
60
|
+
* page: 2,
|
|
61
|
+
* limit: 20,
|
|
62
|
+
* sort: '-popularity',
|
|
63
|
+
* includedFields: ['difficulty,Intermediate'],
|
|
64
|
+
* })
|
|
65
|
+
* .then(content => console.log(content))
|
|
66
|
+
* .catch(error => console.error(error));
|
|
67
|
+
*/
|
|
68
|
+
export async function getTabResults(brand, pageName, tabName, {
|
|
69
|
+
page = 1,
|
|
70
|
+
limit = 10,
|
|
71
|
+
sort = 'recommended',
|
|
72
|
+
selectedFilters = []
|
|
73
|
+
} = {}) {
|
|
74
|
+
|
|
75
|
+
// Extract and handle 'progress' filter separately
|
|
76
|
+
const progressFilter = selectedFilters.find(f => f.startsWith('progress,')) || 'progress,all';
|
|
77
|
+
const progressValue = progressFilter.split(',')[1].toLowerCase();
|
|
78
|
+
const filteredSelectedFilters = selectedFilters.filter(f => !f.startsWith('progress,'));
|
|
79
|
+
|
|
80
|
+
// Prepare included fields
|
|
81
|
+
const mergedIncludedFields = [...filteredSelectedFilters, `tab,${tabName.toLowerCase()}`];
|
|
82
|
+
|
|
83
|
+
// Fetch data
|
|
84
|
+
const results = tabName === Tabs.ForYou.name
|
|
85
|
+
? { entity: await getLessonContentRows(brand, pageName) }
|
|
86
|
+
: await fetchTabData(brand, pageName, { page, limit, sort, includedFields: mergedIncludedFields, progress: progressValue });
|
|
87
|
+
|
|
88
|
+
// Fetch metadata
|
|
89
|
+
const metaData = await fetchMetadata(brand, pageName);
|
|
90
|
+
|
|
91
|
+
// Process filters
|
|
92
|
+
const filters = (metaData.filters ?? []).map(filter => ({
|
|
93
|
+
...filter,
|
|
94
|
+
items: filter.items.map(item => {
|
|
95
|
+
const value = item.value.split(',')[1];
|
|
96
|
+
return {
|
|
97
|
+
...item,
|
|
98
|
+
selected: selectedFilters.includes(`${filter.key},${value}`) ||
|
|
99
|
+
(filter.key === 'progress' && value === 'all' && !selectedFilters.some(f => f.startsWith('progress,')))
|
|
100
|
+
};
|
|
101
|
+
})
|
|
102
|
+
}));
|
|
103
|
+
|
|
104
|
+
// Process sort options
|
|
105
|
+
const sortOptions = {
|
|
106
|
+
title: metaData.sort?.title ?? 'Sort By',
|
|
107
|
+
type: metaData.sort?.type ?? 'radio',
|
|
108
|
+
items: (metaData.sort?.items ?? []).map(option => ({
|
|
109
|
+
...option,
|
|
110
|
+
selected: option.value === sort
|
|
111
|
+
}))
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
type: tabName === Tabs.ForYou.name ? TabResponseType.SECTIONS : TabResponseType.CATALOG,
|
|
116
|
+
data: results.entity,
|
|
117
|
+
meta: { filters, sort: sortOptions }
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Fetches recent content for a given brand and page with pagination.
|
|
123
|
+
*
|
|
124
|
+
* @param {string} brand - The brand for which to fetch data.
|
|
125
|
+
* @param {string} pageName - The page name (e.g., 'all', 'incomplete', 'completed').
|
|
126
|
+
* @param {string} [tabName='all'] - The tab name (defaults to 'all' for recent content).
|
|
127
|
+
* @param {Object} params - Parameters for pagination and sorting.
|
|
128
|
+
* @param {number} [params.page=1] - The page number for pagination.
|
|
129
|
+
* @param {number} [params.limit=10] - The number of items per page.
|
|
130
|
+
* @param {string} [params.sort="-published_on"] - The field to sort the data by.
|
|
131
|
+
* @returns {Promise<Object>} - The fetched content data.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* getRecent('drumeo', 'lessons', 'all', {
|
|
135
|
+
* page: 2,
|
|
136
|
+
* limit: 15,
|
|
137
|
+
* sort: '-popularity'
|
|
138
|
+
* })
|
|
139
|
+
* .then(content => console.log(content))
|
|
140
|
+
* .catch(error => console.error(error));
|
|
141
|
+
*/
|
|
142
|
+
export async function getRecent(brand, pageName, tabName = 'all', {
|
|
143
|
+
page = 1,
|
|
144
|
+
limit = 10,
|
|
145
|
+
sort = '-published_on',
|
|
146
|
+
} = {}) {
|
|
147
|
+
const progress = tabName.toLowerCase() == 'all' ? 'recent':tabName.toLowerCase();
|
|
148
|
+
const recentContentIds = await fetchRecent(brand, pageName, { page:page, limit:limit, progress: progress });
|
|
149
|
+
const metaData = await fetchMetadata(brand, 'recent');
|
|
150
|
+
return {
|
|
151
|
+
type: TabResponseType.CATALOG,
|
|
152
|
+
data: recentContentIds,
|
|
153
|
+
meta: { tabs: metaData.tabs }
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Fetches content rows for a given brand and page with optional filtering by content row id.
|
|
159
|
+
*
|
|
160
|
+
* @param {string} brand - The brand for which to fetch content rows.
|
|
161
|
+
* @param {string} pageName - The page name (e.g., 'lessons', 'songs', 'challenges').
|
|
162
|
+
* @param {string} [contentRowId] - The specific content row ID to fetch.
|
|
163
|
+
* @param {Object} params - Parameters for pagination.
|
|
164
|
+
* @param {number} [params.page=1] - The page number for pagination.
|
|
165
|
+
* @param {number} [params.limit=10] - The maximum number of content items per row.
|
|
166
|
+
* @returns {Promise<Object>} - The fetched content rows.
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* getContentRows('drumeo', 'lessons', 'Your-Daily-Warmup', {
|
|
170
|
+
* page: 1,
|
|
171
|
+
* limit: 5
|
|
172
|
+
* })
|
|
173
|
+
* .then(content => console.log(content))
|
|
174
|
+
* .catch(error => console.error(error));
|
|
175
|
+
*/
|
|
176
|
+
export async function getContentRows(brand, pageName, contentRowId , {
|
|
177
|
+
page = 1,
|
|
178
|
+
limit = 10,
|
|
179
|
+
} = {}) {
|
|
180
|
+
const contentRow = contentRowId ? `&content_row_id=${contentRowId}` : ''
|
|
181
|
+
const url = `/api/content/v1/rows?brand=${brand}&page_name=${pageName}${contentRow}&page=${page}&limit=${limit}`;
|
|
182
|
+
return await fetchHandler(url, 'get', null);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Fetches new and upcoming releases for a given brand with pagination options.
|
|
187
|
+
*
|
|
188
|
+
* @param {string} brand - The brand for which to fetch new and upcoming releases.
|
|
189
|
+
* @param {Object} [params={}] - Pagination parameters.
|
|
190
|
+
* @param {number} [params.page=1] - The page number for pagination.
|
|
191
|
+
* @param {number} [params.limit=10] - The maximum number of content items to fetch.
|
|
192
|
+
* @returns {Promise<{ data: Object[] } | null>} - A promise that resolves to the fetched content data or `null` if no data is found.
|
|
193
|
+
*
|
|
194
|
+
* @example
|
|
195
|
+
* // Fetch the first page with 10 results
|
|
196
|
+
* getNewAndUpcoming('drumeo')
|
|
197
|
+
* .then(response => console.log(response))
|
|
198
|
+
* .catch(error => console.error(error));
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* // Fetch the second page with 20 results
|
|
202
|
+
* getNewAndUpcoming('drumeo', { page: 2, limit: 20 })
|
|
203
|
+
* .then(response => console.log(response))
|
|
204
|
+
* .catch(error => console.error(error));
|
|
205
|
+
*/
|
|
206
|
+
export async function getNewAndUpcoming(brand, {
|
|
207
|
+
page = 1,
|
|
208
|
+
limit = 10,
|
|
209
|
+
} = {}) {
|
|
210
|
+
|
|
211
|
+
const data = await fetchScheduledAndNewReleases(brand, {page: page, limit: limit});
|
|
212
|
+
if (!data) {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
data: data,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Fetches scheduled content rows for a given brand with optional filtering by content row ID.
|
|
223
|
+
*
|
|
224
|
+
* @param {string} brand - The brand for which to fetch content rows.
|
|
225
|
+
* @param {string} [contentRowId=null] - The specific content row ID to fetch (optional).
|
|
226
|
+
* @param {Object} [params={}] - Pagination parameters.
|
|
227
|
+
* @param {number} [params.page=1] - The page number for pagination.
|
|
228
|
+
* @param {number} [params.limit=10] - The maximum number of content items per row.
|
|
229
|
+
* @returns {Promise<Object>} - A promise that resolves to the fetched content rows.
|
|
230
|
+
*
|
|
231
|
+
* @example
|
|
232
|
+
* // Fetch all sections with default pagination
|
|
233
|
+
* getScheduleContentRows('drumeo')
|
|
234
|
+
* .then(content => console.log(content))
|
|
235
|
+
* .catch(error => console.error(error));
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* // Fetch only the 'New-Releases' section with custom pagination
|
|
239
|
+
* getScheduleContentRows('drumeo', 'New-Releases', { page: 1, limit: 30 })
|
|
240
|
+
* .then(content => console.log(content))
|
|
241
|
+
* .catch(error => console.error(error));
|
|
242
|
+
*
|
|
243
|
+
* @example
|
|
244
|
+
* // Fetch only the 'Live-Streams' section with unlimited results
|
|
245
|
+
* getScheduleContentRows('drumeo', 'Live-Streams')
|
|
246
|
+
* .then(content => console.log(content))
|
|
247
|
+
* .catch(error => console.error(error));
|
|
248
|
+
*/
|
|
249
|
+
export async function getScheduleContentRows(brand, contentRowId = null, { page = 1, limit = 10 } = {}) {
|
|
250
|
+
const sections = {
|
|
251
|
+
'New-Releases': {
|
|
252
|
+
title: 'New Releases',
|
|
253
|
+
fetchMethod: fetchNewReleases
|
|
254
|
+
},
|
|
255
|
+
'Live-Streams': {
|
|
256
|
+
title: 'Live Streams',
|
|
257
|
+
fetchMethod: fetchUpcomingEvents
|
|
258
|
+
},
|
|
259
|
+
'Upcoming-Releases': {
|
|
260
|
+
title: 'Upcoming Releases',
|
|
261
|
+
fetchMethod: fetchScheduledReleases
|
|
262
|
+
},
|
|
263
|
+
'Returning-Soon': {
|
|
264
|
+
title: 'Returning Soon',
|
|
265
|
+
fetchMethod: fetchReturning
|
|
266
|
+
},
|
|
267
|
+
'Leaving-Soon': {
|
|
268
|
+
title: 'Leaving Soon',
|
|
269
|
+
fetchMethod: fetchLeaving
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
if (contentRowId) {
|
|
274
|
+
if (!sections[contentRowId]) {
|
|
275
|
+
return null; // Return null if the requested section does not exist
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const items = await sections[contentRowId].fetchMethod(brand, { page, limit });
|
|
279
|
+
|
|
280
|
+
// Fetch only the requested section
|
|
281
|
+
const result = {
|
|
282
|
+
id: contentRowId,
|
|
283
|
+
title: sections[contentRowId].title,
|
|
284
|
+
// TODO: Remove content after FE/MA updates the existing code to use items
|
|
285
|
+
content: items,
|
|
286
|
+
items: items
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
return {
|
|
290
|
+
type: TabResponseType.CATALOG,
|
|
291
|
+
data: result,
|
|
292
|
+
meta: {}
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// If no specific contentRowId, fetch all sections
|
|
297
|
+
const results = await Promise.all(
|
|
298
|
+
Object.entries(sections).map(async ([id, section]) => {
|
|
299
|
+
// Apply special pagination rules
|
|
300
|
+
const isNewReleases = id === 'New-Releases';
|
|
301
|
+
const pagination = isNewReleases ? { page: 1, limit: 30 } : { page: 1, limit: Number.MAX_SAFE_INTEGER };
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
id,
|
|
305
|
+
title: section.title,
|
|
306
|
+
content: await section.fetchMethod(brand, pagination)
|
|
307
|
+
};
|
|
308
|
+
})
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
return {
|
|
312
|
+
type: TabResponseType.SECTIONS,
|
|
313
|
+
data: results,
|
|
314
|
+
meta: {}
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Fetches recommended content for a given brand with pagination support.
|
|
320
|
+
*
|
|
321
|
+
* @param {string} brand - The brand for which to fetch recommended content.
|
|
322
|
+
* @param {Object} [params={}] - Pagination parameters.
|
|
323
|
+
* @param {number} [params.page=1] - The page number for pagination.
|
|
324
|
+
* @param {number} [params.limit=10] - The maximum number of recommended content items per page.
|
|
325
|
+
* @returns {Promise<Object>} - A promise that resolves to an object containing recommended content.
|
|
326
|
+
*
|
|
327
|
+
* @example
|
|
328
|
+
* // Fetch recommended content for a brand with default pagination
|
|
329
|
+
* getRecommendedForYou('drumeo')
|
|
330
|
+
* .then(content => console.log(content))
|
|
331
|
+
* .catch(error => console.error(error));
|
|
332
|
+
*
|
|
333
|
+
* @example
|
|
334
|
+
* // Fetch recommended content for a brand with custom pagination
|
|
335
|
+
* getRecommendedForYou('drumeo', { page: 2, limit: 5 })
|
|
336
|
+
* .then(content => console.log(content))
|
|
337
|
+
* .catch(error => console.error(error));
|
|
338
|
+
*/
|
|
339
|
+
export async function getRecommendedForYou(brand, rowId = null, {
|
|
340
|
+
page = 1,
|
|
341
|
+
limit = 10,
|
|
342
|
+
} = {}) {
|
|
343
|
+
const requiredItems = page * limit;
|
|
344
|
+
const data = await recommendations(brand, {limit: requiredItems});
|
|
345
|
+
if (!data || !data.length) {
|
|
346
|
+
return { id: 'recommended', title: 'Recommended For You', items: [] };
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Apply pagination before calling fetchByRailContentIds
|
|
350
|
+
const startIndex = (page - 1) * limit;
|
|
351
|
+
const paginatedData = data.slice(startIndex, startIndex + limit);
|
|
352
|
+
|
|
353
|
+
const contents = await fetchByRailContentIds(paginatedData);
|
|
354
|
+
const result = {
|
|
355
|
+
id: 'recommended',
|
|
356
|
+
title: 'Recommended For You',
|
|
357
|
+
items: contents
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
if (rowId) {
|
|
361
|
+
return {
|
|
362
|
+
type: TabResponseType.CATALOG,
|
|
363
|
+
data: contents,
|
|
364
|
+
meta: {}
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return { id: 'recommended', title: 'Recommended For You', items: contents }
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
|
|
File without changes
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module Forum-V2
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export async function getActiveDiscussions(brand, { page = 1, limit = 10 } = {}) {
|
|
7
|
+
// Dummy data TODO: BE endpoint call
|
|
8
|
+
const results = {
|
|
9
|
+
entity: [
|
|
10
|
+
{
|
|
11
|
+
id: 11,
|
|
12
|
+
url: 'https://forum.example.com/post/11',
|
|
13
|
+
post: "<p><strong>Lorem ipsum</strong> dolor sit amet, <em>consectetur adipiscing elit</em>. <a href='#'>Click here</a> for more info.</p>",
|
|
14
|
+
author: {
|
|
15
|
+
id: 123,
|
|
16
|
+
name: 'John Doe',
|
|
17
|
+
avatar: 'https://d3fzm1tzeyr5n3.cloudfront.net/profile_picture_url/5f6abe99-f1ed-49ec-aff4-893c017ed1aa-1681577292-565638.jpg'
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: 12,
|
|
22
|
+
url: 'https://forum.example.com/post/12',
|
|
23
|
+
post: "<p>This is a <span style='color: red;'>sample forum post</span> to <strong>test</strong> data structure. <ul><li>Point 1</li><li>Point 2</li></ul></p>",
|
|
24
|
+
author: {
|
|
25
|
+
id: 124,
|
|
26
|
+
name: 'Jane Smith',
|
|
27
|
+
avatar: 'https://d3fzm1tzeyr5n3.cloudfront.net/profile_picture_url/5f6abe99-f1ed-49ec-aff4-893c017ed1aa-1681577292-565638.jpg'
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: 13,
|
|
32
|
+
url: 'https://forum.example.com/post/13',
|
|
33
|
+
post: "<blockquote>Another test post with some <i>dummy text</i> content.</blockquote>",
|
|
34
|
+
author: {
|
|
35
|
+
id: 125,
|
|
36
|
+
name: 'Alice Johnson',
|
|
37
|
+
avatar: 'https://d3fzm1tzeyr5n3.cloudfront.net/profile_picture_url/5f6abe99-f1ed-49ec-aff4-893c017ed1aa-1681577292-565638.jpg'
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: 14,
|
|
42
|
+
url: 'https://forum.example.com/post/14',
|
|
43
|
+
post: "<h2>Final example post</h2><p>To complete the dataset, here is a <strong>bold</strong> statement and an image: <img src='https://example.com/image.jpg' alt='Example Image'></p>",
|
|
44
|
+
author: {
|
|
45
|
+
id: 126,
|
|
46
|
+
name: 'Bob Williams',
|
|
47
|
+
avatar: 'https://d3fzm1tzeyr5n3.cloudfront.net/profile_picture_url/5f6abe99-f1ed-49ec-aff4-893c017ed1aa-1681577292-565638.jpg'
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
data: results.entity,
|
|
55
|
+
meta: {}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -290,6 +290,10 @@ async function patchDataHandler(url, data) {
|
|
|
290
290
|
return fetchHandler(url, 'patch', null, data)
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
+
async function deleteDataHandler(url, data) {
|
|
294
|
+
return fetchHandler(url, 'delete')
|
|
295
|
+
}
|
|
296
|
+
|
|
293
297
|
export async function fetchHandler(url, method = 'get', dataVersion = null, body = null) {
|
|
294
298
|
let headers = {
|
|
295
299
|
'Content-Type': 'application/json',
|
|
@@ -645,7 +649,7 @@ export async function fetchUserPlaylists(
|
|
|
645
649
|
const content = content_id ? `&content_id=${content_id}` : ''
|
|
646
650
|
const categoryString =
|
|
647
651
|
categories && categories.length ? categories.map((cat) => `categories[]=${cat}`).join('&') : ''
|
|
648
|
-
url = `/playlists/all?brand=${brand}${limitString}${pageString}${sortString}${searchFilter}${content}${categoryString ? `&${categoryString}` : ''}`
|
|
652
|
+
url = `/api/content/v1/user/playlists/all?brand=${brand}${limitString}${pageString}${sortString}${searchFilter}${content}${categoryString ? `&${categoryString}` : ''}`
|
|
649
653
|
return await fetchHandler(url)
|
|
650
654
|
}
|
|
651
655
|
|
|
@@ -771,7 +775,7 @@ export async function updatePlaylist(playlistId, updatedData) {
|
|
|
771
775
|
* .catch(error => console.error('Error creating playlist:', error));
|
|
772
776
|
*/
|
|
773
777
|
export async function createPlaylist(playlistData) {
|
|
774
|
-
const url = `/playlists/playlist`
|
|
778
|
+
const url = `/api/content/v1/user/playlists/playlist`
|
|
775
779
|
return await fetchHandler(url, 'POST', null, playlistData)
|
|
776
780
|
}
|
|
777
781
|
|
|
@@ -1037,7 +1041,7 @@ export async function postContentReset(contentId) {
|
|
|
1037
1041
|
* });
|
|
1038
1042
|
*/
|
|
1039
1043
|
export async function addItemToPlaylist(payload) {
|
|
1040
|
-
const url = `/playlists/add-item`
|
|
1044
|
+
const url = `/api/content/v1/user/playlists/add-item`
|
|
1041
1045
|
return await fetchHandler(url, 'POST', null, payload)
|
|
1042
1046
|
}
|
|
1043
1047
|
|
|
@@ -1206,28 +1210,28 @@ export async function setStudentViewForUser(userId, enable) {
|
|
|
1206
1210
|
* @returns {Promise<Object|null>} - A promise that resolves to an comment object
|
|
1207
1211
|
*/
|
|
1208
1212
|
export async function fetchTopComment(railcontentId) {
|
|
1209
|
-
const url = `/api/content/v1/comments/${railcontentId}/top`
|
|
1213
|
+
const url = `/api/content/v1/comments/content/${railcontentId}/top`
|
|
1210
1214
|
return await fetchHandler(url)
|
|
1211
1215
|
}
|
|
1212
1216
|
|
|
1213
1217
|
|
|
1214
1218
|
/**
|
|
1215
1219
|
*
|
|
1216
|
-
* @param railcontentId
|
|
1217
|
-
* @param page
|
|
1218
|
-
* @param limit
|
|
1220
|
+
* @param {int} railcontentId
|
|
1221
|
+
* @param {int} page
|
|
1222
|
+
* @param {int} limit
|
|
1219
1223
|
* @returns {Promise<*|null>}
|
|
1220
1224
|
*/
|
|
1221
1225
|
export async function fetchComments(railcontentId, page = 1, limit = 20) {
|
|
1222
|
-
const url = `/api/content/v1/comments/${railcontentId}/all?page=${page}&limit=${limit}`
|
|
1226
|
+
const url = `/api/content/v1/comments/content/${railcontentId}/all?page=${page}&limit=${limit}`
|
|
1223
1227
|
return await fetchHandler(url)
|
|
1224
1228
|
}
|
|
1225
1229
|
|
|
1226
1230
|
/**
|
|
1227
1231
|
*
|
|
1228
|
-
* @param commentId
|
|
1229
|
-
* @param page
|
|
1230
|
-
* @param limit
|
|
1232
|
+
* @param {int} commentId
|
|
1233
|
+
* @param {int} page
|
|
1234
|
+
* @param {int} limit
|
|
1231
1235
|
* @returns {Promise<*|null>}
|
|
1232
1236
|
*/
|
|
1233
1237
|
export async function fetchCommentRelies(commentId, page = 1, limit = 20) {
|
|
@@ -1235,6 +1239,115 @@ export async function fetchCommentRelies(commentId, page = 1, limit = 20) {
|
|
|
1235
1239
|
return await fetchHandler(url)
|
|
1236
1240
|
}
|
|
1237
1241
|
|
|
1242
|
+
/**
|
|
1243
|
+
* @param {int} commentId
|
|
1244
|
+
* @returns {Promise<*|null>}
|
|
1245
|
+
*/
|
|
1246
|
+
export async function deleteComment(commentId) {
|
|
1247
|
+
const url = `/api/content/v1/comments/${commentId}`
|
|
1248
|
+
return await fetchHandler(url, "DELETE")
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
/**
|
|
1252
|
+
* @param {int} commentId
|
|
1253
|
+
* @param {string} comment
|
|
1254
|
+
* @returns {Promise<*|null>}
|
|
1255
|
+
*/
|
|
1256
|
+
export async function replyToComment(commentId, comment) {
|
|
1257
|
+
const data = {'comment' : comment}
|
|
1258
|
+
const url = `/api/content/v1/comments/${commentId}/reply`
|
|
1259
|
+
return await postDataHandler(url, data)
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
/**
|
|
1263
|
+
* @param {int} railcontentId
|
|
1264
|
+
* @param {string} comment
|
|
1265
|
+
* @returns {Promise<*|null>}
|
|
1266
|
+
*/
|
|
1267
|
+
export async function createComment(railcontentId, comment) {
|
|
1268
|
+
const data = {
|
|
1269
|
+
'comment' : comment,
|
|
1270
|
+
'content_id' : railcontentId
|
|
1271
|
+
}
|
|
1272
|
+
const url = `/api/content/v1/comments/store`
|
|
1273
|
+
return await postDataHandler(url, data)
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
/**
|
|
1277
|
+
* @param {int} commentId
|
|
1278
|
+
* @returns {Promise<*|null>}
|
|
1279
|
+
*/
|
|
1280
|
+
export async function assignModeratorToComment(commentId) {
|
|
1281
|
+
const url = `/api/content/v1/comments/${commentId}/assign_moderator`
|
|
1282
|
+
return await postDataHandler(url)
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
/**
|
|
1286
|
+
* @param {int} commentId
|
|
1287
|
+
* @returns {Promise<*|null>}
|
|
1288
|
+
*/
|
|
1289
|
+
export async function unassignModeratorToComment(commentId) {
|
|
1290
|
+
const url = `/api/content/v1/comments/${commentId}/unassign_moderator`
|
|
1291
|
+
return await postDataHandler(url)
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
/**
|
|
1295
|
+
* @param {int} commentId
|
|
1296
|
+
* @returns {Promise<*|null>}
|
|
1297
|
+
*/
|
|
1298
|
+
export async function likeComment(commentId) {
|
|
1299
|
+
const url = `/api/content/v1/comments/${commentId}/like`
|
|
1300
|
+
return await postDataHandler(url)
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
/**
|
|
1304
|
+
* @param {int} commentId
|
|
1305
|
+
* @returns {Promise<*|null>}
|
|
1306
|
+
*/
|
|
1307
|
+
export async function unlikeComment(commentId) {
|
|
1308
|
+
const url = `/api/content/v1/comments/${commentId}/like`
|
|
1309
|
+
return await deleteDataHandler(url)
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
/**
|
|
1313
|
+
* @param {int} commentId
|
|
1314
|
+
* @returns {Promise<*|null>}
|
|
1315
|
+
*/
|
|
1316
|
+
export async function closeComment(commentId) {
|
|
1317
|
+
const url = `/api/content/v1/comments/${commentId}`
|
|
1318
|
+
const data = {
|
|
1319
|
+
'conversation_status' : 'closed'
|
|
1320
|
+
}
|
|
1321
|
+
return await patchDataHandler(url, data)
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
/**
|
|
1325
|
+
* @param {int} commentId
|
|
1326
|
+
* @returns {Promise<*|null>}
|
|
1327
|
+
*/
|
|
1328
|
+
export async function openComment(commentId) {
|
|
1329
|
+
const url = `/api/content/v1/comments/${commentId}`
|
|
1330
|
+
const data = {
|
|
1331
|
+
'conversation_status' : 'open'
|
|
1332
|
+
}
|
|
1333
|
+
return await patchDataHandler(url, data)
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
/**
|
|
1337
|
+
* @param {int} commentId
|
|
1338
|
+
* @param {string} comment
|
|
1339
|
+
* @returns {Promise<*|null>}
|
|
1340
|
+
*/
|
|
1341
|
+
export async function editComment(commentId, comment) {
|
|
1342
|
+
const url = `/api/content/v1/comments/${commentId}`
|
|
1343
|
+
const data = {
|
|
1344
|
+
'comment' : comment
|
|
1345
|
+
}
|
|
1346
|
+
return await patchDataHandler(url, data)
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
|
|
1350
|
+
|
|
1238
1351
|
function fetchAbsolute(url, params) {
|
|
1239
1352
|
if (globalConfig.railcontentConfig.authToken) {
|
|
1240
1353
|
params.headers['Authorization'] = `Bearer ${globalConfig.railcontentConfig.authToken}`
|
|
@@ -109,6 +109,25 @@ export async function rankItems(brand, content_ids) {
|
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
export async function recommendations(brand, {
|
|
113
|
+
page = 1,
|
|
114
|
+
limit = 10,
|
|
115
|
+
} = {}) {
|
|
116
|
+
let data = {
|
|
117
|
+
'brand': brand,
|
|
118
|
+
'user_id': globalConfig.railcontentConfig.userId,
|
|
119
|
+
'num_recommendations': limit
|
|
120
|
+
}
|
|
121
|
+
const url = `/recommendations/`
|
|
122
|
+
try {
|
|
123
|
+
const response = await fetchHandler(url, 'POST', data)
|
|
124
|
+
return response['recommendations']
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.error('Fetch error:', error)
|
|
127
|
+
return null
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
112
131
|
async function fetchHandler(url, method = 'get', body = null) {
|
|
113
132
|
|
|
114
133
|
let headers = {
|