musora-content-services 2.71.0 → 2.72.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/docs/ContentOrganization.html +2 -2
  3. package/docs/Forums.html +2 -2
  4. package/docs/Gamification.html +2 -2
  5. package/docs/TestUser.html +3 -3
  6. package/docs/UserManagementSystem.html +2 -2
  7. package/docs/api_types.js.html +2 -2
  8. package/docs/config.js.html +2 -2
  9. package/docs/content-org_content-org.js.html +2 -2
  10. package/docs/content-org_guided-courses.ts.html +2 -2
  11. package/docs/content-org_learning-paths.ts.html +94 -5
  12. package/docs/content-org_playlists-types.js.html +2 -2
  13. package/docs/content-org_playlists.js.html +2 -2
  14. package/docs/content.js.html +2 -2
  15. package/docs/forums_categories.ts.html +2 -2
  16. package/docs/forums_forums.ts.html +41 -41
  17. package/docs/forums_posts.ts.html +34 -27
  18. package/docs/forums_threads.ts.html +2 -2
  19. package/docs/gamification_awards.ts.html +2 -2
  20. package/docs/gamification_gamification.js.html +2 -2
  21. package/docs/global.html +3 -3
  22. package/docs/index.html +2 -2
  23. package/docs/liveTesting.ts.html +4 -3
  24. package/docs/module-Accounts.html +2 -2
  25. package/docs/module-Awards.html +2 -2
  26. package/docs/module-Config.html +2 -2
  27. package/docs/module-Content-Services-V2.html +2 -2
  28. package/docs/module-Forums.html +26 -26
  29. package/docs/module-GuidedCourses.html +2 -1198
  30. package/docs/module-Interests.html +2 -2
  31. package/docs/module-LearningPaths.html +1077 -0
  32. package/docs/module-Onboarding.html +2 -2
  33. package/docs/module-Payments.html +2 -2
  34. package/docs/module-Permissions.html +2 -2
  35. package/docs/module-Playlists.html +2 -2
  36. package/docs/module-Railcontent-Services.html +2 -2
  37. package/docs/module-Sanity-Services.html +32 -32
  38. package/docs/module-Sessions.html +2 -2
  39. package/docs/module-UserActivity.html +2 -2
  40. package/docs/module-UserChat.html +2 -2
  41. package/docs/module-UserManagement.html +2 -2
  42. package/docs/module-UserMemberships.html +2 -2
  43. package/docs/module-UserNotifications.html +2 -2
  44. package/docs/module-UserProfile.html +2 -2
  45. package/docs/railcontent.js.html +2 -2
  46. package/docs/sanity.js.html +5 -7
  47. package/docs/userActivity.js.html +2 -2
  48. package/docs/user_account.ts.html +2 -2
  49. package/docs/user_chat.js.html +2 -2
  50. package/docs/user_interests.js.html +2 -2
  51. package/docs/user_management.js.html +2 -2
  52. package/docs/user_memberships.ts.html +2 -2
  53. package/docs/user_notifications.js.html +2 -2
  54. package/docs/user_onboarding.ts.html +2 -2
  55. package/docs/user_payments.ts.html +2 -2
  56. package/docs/user_permissions.js.html +2 -2
  57. package/docs/user_profile.js.html +2 -2
  58. package/docs/user_sessions.js.html +2 -2
  59. package/docs/user_types.js.html +2 -2
  60. package/docs/user_user-management-system.js.html +2 -2
  61. package/package.json +1 -1
  62. package/src/contentTypeConfig.js +162 -105
  63. package/src/index.d.ts +2 -0
  64. package/src/index.js +2 -0
  65. package/src/lib/httpHelper.js +5 -0
  66. package/src/services/content-org/learning-paths.ts +92 -3
  67. package/src/services/forums/forums.ts +38 -38
  68. package/src/services/forums/posts.ts +32 -25
  69. package/test/initializeTests.js +21 -36
  70. package/test/learningPaths.test.js +19 -0
@@ -1,6 +1,6 @@
1
- import {HttpClient} from "../../infrastructure/http/HttpClient";
2
- import {PaginatedResponse} from "../api/types";
3
- import {ForumThread} from "./types";
1
+ import { HttpClient } from '../../infrastructure/http/HttpClient'
2
+ import { PaginatedResponse } from '../api/types'
3
+ import { ForumThread } from './types'
4
4
  import { globalConfig } from '../config.js'
5
5
 
6
6
  const baseUrl = `/api/forums`
@@ -13,39 +13,41 @@ const baseUrl = `/api/forums`
13
13
 
14
14
  export async function getActiveDiscussions(brand: string, { page = 1, limit = 10 } = {}) {
15
15
  const httpClient = new HttpClient(globalConfig.baseUrl)
16
- const latestDiscussions = await httpClient.get<PaginatedResponse<ForumThread>>(`${baseUrl}/v1/threads/latest?brand=${brand}&page=${page}&limit=${limit}`)
16
+ const latestDiscussions = await httpClient.get<PaginatedResponse<ForumThread>>(
17
+ `${baseUrl}/v1/threads/latest?brand=${brand}&page=${page}&limit=${limit}`
18
+ )
17
19
 
18
20
  // Transform the response
19
- return transformLatestDiscussions(latestDiscussions);
21
+ return transformLatestDiscussions(latestDiscussions)
20
22
  }
21
23
 
22
24
  interface TransformedDiscussion {
23
- id: number;
24
- url: string;
25
- title: string;
26
- post: string;
25
+ id: number
26
+ url: string
27
+ title: string
28
+ post: string
27
29
  author: {
28
- id: number;
29
- name: string;
30
- avatar: string;
31
- };
30
+ id: number
31
+ name: string
32
+ avatar: string
33
+ }
32
34
  }
33
35
 
34
36
  interface TransformedResponse {
35
- data: TransformedDiscussion[];
36
- meta: PaginatedResponse<ForumThread>['meta'];
37
+ data: TransformedDiscussion[]
38
+ meta: PaginatedResponse<ForumThread>['meta']
37
39
  }
38
40
 
39
41
  // Helper function to strip HTML tags and decode entities
40
42
  function stripHtml(html: string): string {
41
43
  // Remove blockquotes and their content first
42
- let filtered = html.replace(/<blockquote[^>]*>.*?<\/blockquote>/gis, '');
44
+ let filtered = html.replace(/<blockquote[^>]*>[\s\S]*?<\/blockquote>/gi, '')
43
45
 
44
46
  // Remove iframes and their content
45
- filtered = filtered.replace(/<iframe[^>]*>.*?<\/iframe>/gis, '');
47
+ filtered = filtered.replace(/<iframe[^>]*>[\s\S]*?<\/iframe>/gi, '')
46
48
 
47
49
  // Remove remaining HTML tags
48
- const withoutTags = filtered.replace(/<[^>]*>/g, '');
50
+ const withoutTags = filtered.replace(/<[^>]*>/g, '')
49
51
 
50
52
  // Decode common HTML entities
51
53
  const decoded = withoutTags
@@ -54,35 +56,33 @@ function stripHtml(html: string): string {
54
56
  .replace(/&gt;/g, '>')
55
57
  .replace(/&quot;/g, '"')
56
58
  .replace(/&#39;/g, "'")
57
- .replace(/&nbsp;/g, ' ');
59
+ .replace(/&nbsp;/g, ' ')
58
60
 
59
- return decoded.trim();
61
+ return decoded.trim()
60
62
  }
61
63
 
62
- function transformLatestDiscussions(
63
- response: PaginatedResponse<ForumThread>
64
- ): TransformedResponse {
64
+ function transformLatestDiscussions(response: PaginatedResponse<ForumThread>): TransformedResponse {
65
65
  const transformedData = response.data
66
66
  .filter((thread) => thread.last_post && thread.author) // Filter out null posts/authors
67
67
  .map((thread) => {
68
- const postContent = stripHtml(thread.last_post!.content)
69
- const postId = thread.last_post!.id
68
+ const postContent = stripHtml(thread.last_post!.content)
69
+ const postId = thread.last_post!.id
70
70
 
71
- return {
72
- id: thread.id,
73
- url: `forums/post/${postId}`,
74
- title: thread.title,
75
- post: postContent,
76
- author: {
77
- id: thread.author!.id,
78
- name: thread.author!.display_name,
79
- avatar: thread.author!.profile_picture_url || '',
80
- },
81
- };
82
- });
71
+ return {
72
+ id: thread.id,
73
+ url: `forums/post/${postId}`,
74
+ title: thread.title,
75
+ post: postContent,
76
+ author: {
77
+ id: thread.author!.id,
78
+ name: thread.author!.display_name,
79
+ avatar: thread.author!.profile_picture_url || '',
80
+ },
81
+ }
82
+ })
83
83
 
84
84
  return {
85
85
  data: transformedData,
86
86
  meta: response.meta,
87
- };
87
+ }
88
88
  }
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { HttpClient } from '../../infrastructure/http/HttpClient'
5
5
  import { globalConfig } from '../config.js'
6
- import {ForumPost} from './types'
6
+ import { ForumPost } from './types'
7
7
  import { PaginatedResponse } from '../api/types'
8
8
 
9
9
  const baseUrl = `/api/forums`
@@ -21,10 +21,7 @@ export interface CreatePostParams {
21
21
  * @returns {Promise<ForumPost>} - A promise that resolves to the created post.
22
22
  * @throws {HttpError} - If the request fails.
23
23
  */
24
- export async function createPost(
25
- threadId: number,
26
- params: CreatePostParams
27
- ): Promise<ForumPost> {
24
+ export async function createPost(threadId: number, params: CreatePostParams): Promise<ForumPost> {
28
25
  const httpClient = new HttpClient(globalConfig.baseUrl)
29
26
  return httpClient.post<ForumPost>(`${baseUrl}/v1/threads/${threadId}/posts`, params)
30
27
  }
@@ -37,18 +34,14 @@ export async function createPost(
37
34
  * @returns {Promise<ForumPost>} - A promise that resolves to the updated post.
38
35
  * @throws {HttpError} - If the request fails.
39
36
  */
40
- export async function updatePost(
41
- postId: number,
42
- params: CreatePostParams
43
- ): Promise<ForumPost> {
37
+ export async function updatePost(postId: number, params: CreatePostParams): Promise<ForumPost> {
44
38
  const httpClient = new HttpClient(globalConfig.baseUrl)
45
39
  return httpClient.put<ForumPost>(`${baseUrl}/v1/posts/${postId}`, params)
46
40
  }
47
41
 
48
-
49
42
  export interface FetchPostParams {
50
- page?: number,
51
- limit?: number,
43
+ page?: number
44
+ limit?: number
52
45
  /** Sort order: "-published_on" (default), "published_on", or "mine". */
53
46
  sort?: '-published_on' | string
54
47
  }
@@ -67,9 +60,14 @@ export async function fetchPosts(
67
60
  params: FetchPostParams = {}
68
61
  ): Promise<PaginatedResponse<ForumPost>> {
69
62
  const httpClient = new HttpClient(globalConfig.baseUrl)
70
- const queryObj: Record<string, string> = { brand, ...Object.fromEntries(
71
- Object.entries(params).filter(([_, v]) => v !== undefined && v !== null).map(([k, v]) => [k, String(v)])
72
- )}
63
+ const queryObj: Record<string, string> = {
64
+ brand,
65
+ ...Object.fromEntries(
66
+ Object.entries(params)
67
+ .filter(([_, v]) => v !== undefined && v !== null)
68
+ .map(([k, v]) => [k, String(v)])
69
+ ),
70
+ }
73
71
  const query = new URLSearchParams(queryObj).toString()
74
72
 
75
73
  const url = `${baseUrl}/v1/threads/${threadId}/posts?${query}`
@@ -126,7 +124,7 @@ export async function deletePost(postId: number, brand: string): Promise<void> {
126
124
  export async function fetchCommunityGuidelines(brand: string): Promise<ForumPost[]> {
127
125
  const httpClient = new HttpClient(globalConfig.baseUrl)
128
126
  const url = `${baseUrl}/v1/rules?brand=${brand}`
129
- return httpClient.get<ForumPost[]>(url);
127
+ return httpClient.get<ForumPost[]>(url)
130
128
  }
131
129
 
132
130
  export interface SearchParams {
@@ -134,8 +132,8 @@ export interface SearchParams {
134
132
  limit?: number,
135
133
  category_id?: number,
136
134
  /** Sort order: "-published_on" (default), "published_on", or "mine". */
137
- sort?: '-published_on' | string,
138
- term: string
135
+ sort?: '-published_on' | string
136
+ term?: string
139
137
  }
140
138
 
141
139
  /**
@@ -151,9 +149,14 @@ export async function search(
151
149
  params: SearchParams
152
150
  ): Promise<PaginatedResponse<ForumPost>> {
153
151
  const httpClient = new HttpClient(globalConfig.baseUrl)
154
- const queryObj: Record<string, string> = { brand, ...Object.fromEntries(
155
- Object.entries(params).filter(([_, v]) => v !== undefined && v !== null).map(([k, v]) => [k, String(v)])
156
- )}
152
+ const queryObj: Record<string, string> = {
153
+ brand,
154
+ ...Object.fromEntries(
155
+ Object.entries(params)
156
+ .filter(([_, v]) => v !== undefined && v !== null)
157
+ .map(([k, v]) => [k, String(v)])
158
+ ),
159
+ }
157
160
  const query = new URLSearchParams(queryObj).toString()
158
161
 
159
162
  const url = `${baseUrl}/v1/search?${query}`
@@ -175,12 +178,16 @@ export async function jumpToPost(
175
178
  params: FetchPostParams = {}
176
179
  ): Promise<PaginatedResponse<ForumPost>> {
177
180
  const httpClient = new HttpClient(globalConfig.baseUrl)
178
- const queryObj: Record<string, string> = { brand, ...Object.fromEntries(
179
- Object.entries(params).filter(([_, v]) => v !== undefined && v !== null).map(([k, v]) => [k, String(v)])
180
- )}
181
+ const queryObj: Record<string, string> = {
182
+ brand,
183
+ ...Object.fromEntries(
184
+ Object.entries(params)
185
+ .filter(([_, v]) => v !== undefined && v !== null)
186
+ .map(([k, v]) => [k, String(v)])
187
+ ),
188
+ }
181
189
  const query = new URLSearchParams(queryObj).toString()
182
190
 
183
191
  const url = `${baseUrl}/v1/posts/${postId}/jump?${query}`
184
192
  return httpClient.get<PaginatedResponse<ForumPost>>(url)
185
193
  }
186
-
@@ -1,18 +1,32 @@
1
1
  import { globalConfig, initializeService } from '../src'
2
2
  import { LocalStorageMock } from './localStorageMock'
3
-
4
3
  const railContentModule = require('../src/services/railcontent.js')
5
4
  let token = null
6
5
  let userId = process.env.RAILCONTENT_USER_ID ?? null
7
6
 
8
7
  export async function initializeTestService(useLive = false, isAdmin = false) {
8
+ let token, userId
9
9
  if (useLive && !token && process.env.RAILCONTENT_BASE_URL) {
10
- let data = await fetchLoginToken(
11
- process.env.RAILCONTENT_EMAIL,
12
- process.env.RAILCONTENT_PASSWORD
13
- )
10
+ const baseUrl = `${process.env.RAILCONTENT_BASE_URL}/api/user-management-system`
11
+ const response = await fetch(`${baseUrl}/v1/sessions`, {
12
+ method: 'POST',
13
+ headers: {
14
+ 'X-Client-Platform': 'mobile',
15
+ 'Content-Type': 'application/json',
16
+ Authorization: null,
17
+ },
18
+ body: JSON.stringify({
19
+ email: process.env.RAILCONTENT_EMAIL,
20
+ password: process.env.RAILCONTENT_PASSWORD,
21
+ device_name: 'test',
22
+ device_token: '',
23
+ platform: '',
24
+ }),
25
+ })
26
+
27
+ let data = await response.json() // Parse the JSON body
14
28
  token = data['token']
15
- userId = data['userId']
29
+ userId = data['user']['id']
16
30
  }
17
31
 
18
32
  const config = {
@@ -25,42 +39,13 @@ export async function initializeTestService(useLive = false, isAdmin = false) {
25
39
  debug: process.env.DEBUG === 'true' || false,
26
40
  useDummyRailContentMethods: true,
27
41
  },
28
- sessionConfig: {
29
- token: token,
30
- userId: userId,
31
- authToken: token,
32
- },
42
+ sessionConfig: { token: token, userId: userId, authToken: token },
33
43
  baseUrl: process.env.RAILCONTENT_BASE_URL,
34
44
  localStorage: new LocalStorageMock(),
35
45
  isMA: true,
36
46
  }
37
47
  initializeService(config)
38
-
39
48
  let mock = jest.spyOn(railContentModule, 'fetchUserPermissionsData')
40
49
  let testData = { permissions: [78, 91, 92], isAdmin: isAdmin }
41
50
  mock.mockImplementation(() => testData)
42
51
  }
43
-
44
- async function fetchLoginToken(email, password) {
45
- try {
46
- const url = `${process.env.RAILCONTENT_BASE_URL}/user-management-system/login/token`
47
- const response = await fetch(url, {
48
- method: 'POST',
49
- headers: {
50
- Accept: 'application/json',
51
- 'Content-Type': 'application/json',
52
- },
53
- body: JSON.stringify({ email: email, password: password, device_name: 'test' }),
54
- })
55
- if (response.ok) {
56
- let data = await response.json()
57
- return { token: data.token, userId: data.user.id }
58
- } else {
59
- console.log('fetch error:', response.status)
60
- console.log(response)
61
- }
62
- } catch (error) {
63
- console.error('Fetch error:', error)
64
- }
65
- return null
66
- }
@@ -0,0 +1,19 @@
1
+ import { initializeTestService } from './initializeTests.js'
2
+ import { fetchLearningPathLessons } from '../src/services/content-org/learning-paths.ts'
3
+ import { fetchByRailContentId } from '../src/services/sanity.js'
4
+ import { contentStatusCompleted } from '../src/services/contentProgress.js'
5
+ describe('learning-paths', function () {
6
+ beforeEach(async () => {
7
+ await initializeTestService(true)
8
+ })
9
+
10
+ test('getLearningPathsV2Test', async () => {
11
+ const results = await fetchByRailContentId(417140, 'learning-path-v2')
12
+ })
13
+ test('getlearningPathLessonsTestNew', async () => {
14
+ await contentStatusCompleted(417105)
15
+ const userDate = new Date('2025-10-31')
16
+ const results = await fetchLearningPathLessons(422533, 'drumeo', userDate)
17
+ console.log(results)
18
+ })
19
+ })