musora-content-services 2.3.26 → 2.4.0

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 (124) hide show
  1. package/.coderabbit.yaml +0 -0
  2. package/.editorconfig +0 -0
  3. package/.github/pull_request_template.md +0 -0
  4. package/.github/workflows/node.js.yml +0 -0
  5. package/.prettierignore +0 -0
  6. package/.prettierrc +0 -0
  7. package/.yarnrc.yml +1 -0
  8. package/CHANGELOG.md +7 -0
  9. package/README.md +0 -0
  10. package/babel.config.cjs +0 -0
  11. package/docs/Content-Organization.html +0 -0
  12. package/docs/ContentOrganization.html +1 -1
  13. package/docs/Gamification.html +1 -1
  14. package/docs/UserManagement.html +0 -0
  15. package/docs/UserManagementSystem.html +1 -1
  16. package/docs/api_types.js.html +1 -1
  17. package/docs/config.js.html +1 -1
  18. package/docs/content-org_content-org.js.html +1 -1
  19. package/docs/content-org_playlists-types.js.html +1 -1
  20. package/docs/content-org_playlists.js.html +1 -1
  21. package/docs/content.js.html +25 -10
  22. package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  23. package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  24. package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  25. package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  26. package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  27. package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  28. package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  29. package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  30. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  31. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +0 -0
  32. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  33. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  34. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  35. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  36. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +0 -0
  37. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  38. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  39. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  40. package/docs/gamification_awards.js.html +1 -1
  41. package/docs/gamification_gamification.js.html +1 -1
  42. package/docs/gamification_types.js.html +1 -1
  43. package/docs/global.html +1 -1
  44. package/docs/global.html#User +0 -0
  45. package/docs/index.html +1 -1
  46. package/docs/module-Awards.html +1 -1
  47. package/docs/module-Config.html +1 -1
  48. package/docs/module-Content-Services-V2.html +9 -8
  49. package/docs/module-Content-Services.html +763 -0
  50. package/docs/module-Permissions.html +1 -1
  51. package/docs/module-Playlists.html +1 -1
  52. package/docs/module-Railcontent-Services.html +1 -1
  53. package/docs/module-Sanity-Services.html +32 -32
  54. package/docs/module-Session-Management.html +0 -0
  55. package/docs/module-Sessions.html +1 -1
  56. package/docs/module-User-Activity.html +7 -7
  57. package/docs/module-User-Management.html +0 -0
  58. package/docs/module-User-Permissions.html +0 -0
  59. package/docs/module-UserManagement.html +1 -1
  60. package/docs/railcontent.js.html +1 -1
  61. package/docs/sanity.js.html +5 -3
  62. package/docs/scripts/collapse.js +0 -0
  63. package/docs/scripts/commonNav.js +0 -0
  64. package/docs/scripts/linenumber.js +0 -0
  65. package/docs/scripts/nav.js +0 -0
  66. package/docs/scripts/polyfill.js +0 -0
  67. package/docs/scripts/prettify/Apache-License-2.0.txt +0 -0
  68. package/docs/scripts/prettify/lang-css.js +0 -0
  69. package/docs/scripts/prettify/prettify.js +0 -0
  70. package/docs/scripts/search.js +0 -0
  71. package/docs/styles/jsdoc.css +0 -0
  72. package/docs/styles/prettify.css +0 -0
  73. package/docs/types.js.html +0 -0
  74. package/docs/userActivity.js.html +2 -4
  75. package/docs/user_management.js.html +1 -1
  76. package/docs/user_permissions.js.html +1 -1
  77. package/docs/user_sessions.js.html +1 -1
  78. package/docs/user_types.js.html +1 -1
  79. package/docs/user_user-management-system.js.html +1 -1
  80. package/docs/user_user-management.js.html +0 -0
  81. package/jest.config.js +0 -0
  82. package/jsdoc.json +0 -0
  83. package/link_mcs.sh +0 -0
  84. package/package.json +1 -1
  85. package/src/contentMetaData.js +29 -5
  86. package/src/contentTypeConfig.js +3 -2
  87. package/src/index.d.ts +2 -0
  88. package/src/index.js +2 -0
  89. package/src/infrastructure/http/HttpClient.ts +120 -0
  90. package/src/infrastructure/http/executors/FetchRequestExecutor.ts +45 -0
  91. package/src/infrastructure/http/index.ts +13 -0
  92. package/src/infrastructure/http/interfaces/HeaderProvider.ts +3 -0
  93. package/src/infrastructure/http/interfaces/HttpError.ts +7 -0
  94. package/src/infrastructure/http/interfaces/NetworkError.ts +6 -0
  95. package/src/infrastructure/http/interfaces/RequestExecutor.ts +5 -0
  96. package/src/infrastructure/http/interfaces/RequestOptions.ts +5 -0
  97. package/src/infrastructure/http/providers/DefaultHeaderProvider.ts +24 -0
  98. package/src/services/content.js +24 -9
  99. package/src/services/contentAggregator.js +18 -10
  100. package/src/services/contentProgress.js +0 -0
  101. package/src/services/railcontent.js +28 -0
  102. package/src/services/recommendations.js +21 -25
  103. package/src/services/userActivity.js +23 -0
  104. package/test/HttpClient.test.js +257 -0
  105. package/test/content.test.js +0 -0
  106. package/test/contentLikes.test.js +0 -0
  107. package/test/contentProgress.test.js +0 -0
  108. package/test/dataContext.test.js +0 -0
  109. package/test/forum.test.js +0 -0
  110. package/test/imageSRCBuilder.test.js +0 -0
  111. package/test/imageSRCVerify.test.js +0 -0
  112. package/test/initializeTests.js +0 -0
  113. package/test/lib/lastUpdated.test.js +0 -0
  114. package/test/live/contentProgressLive.test.js +0 -0
  115. package/test/live/railcontentLive.test.js +0 -0
  116. package/test/localStorageMock.js +0 -0
  117. package/test/log.js +0 -0
  118. package/test/mockData/mockData_fetchByRailContentIds_one_content.json +0 -0
  119. package/test/mockData/mockData_user_practices.json +0 -0
  120. package/test/sanityQueryService.test.js +0 -0
  121. package/test/streakMessage.test.js +0 -0
  122. package/test/user/permissions.test.js +0 -0
  123. package/test/userActivity.test.js +0 -0
  124. package/tools/generate-index.cjs +0 -0
@@ -0,0 +1,257 @@
1
+ import { HttpClient } from '../src/infrastructure/http/HttpClient'
2
+ import { HeaderProvider } from '../src/infrastructure/http/interfaces/HeaderProvider'
3
+ import { RequestExecutor } from '../src/infrastructure/http/interfaces/RequestExecutor'
4
+
5
+ describe('HttpClient', () => {
6
+ let httpClient
7
+ let mockHeaderProvider
8
+ let mockRequestExecutor
9
+ const baseUrl = 'https://api.example.com'
10
+ const token = 'test-token'
11
+ const headers = { 'Content-Type': 'application/json', Accept: 'application/json' }
12
+ const responseData = { success: true }
13
+
14
+ beforeEach(() => {
15
+ // Mock HeaderProvider
16
+ mockHeaderProvider = {
17
+ getHeaders: jest.fn().mockReturnValue(headers),
18
+ }
19
+
20
+ // Mock RequestExecutor
21
+ mockRequestExecutor = {
22
+ execute: jest.fn().mockResolvedValue(responseData),
23
+ }
24
+
25
+ // Create HttpClient instance with mocks
26
+ httpClient = new HttpClient(baseUrl, token, mockHeaderProvider, mockRequestExecutor)
27
+ })
28
+
29
+ describe('Constructor', () => {
30
+ test('should initialize with default parameters when not provided', () => {
31
+ const client = new HttpClient(baseUrl)
32
+ expect(client).toBeInstanceOf(HttpClient)
33
+ })
34
+
35
+ test('should initialize with provided parameters', () => {
36
+ expect(httpClient).toBeInstanceOf(HttpClient)
37
+ })
38
+ })
39
+
40
+ describe('setToken', () => {
41
+ test('should update the token', () => {
42
+ const newToken = 'new-test-token'
43
+ httpClient.setToken(newToken)
44
+
45
+ // Verify token was updated by making a request and checking if the new token is used
46
+ httpClient.get('/test')
47
+
48
+ expect(mockRequestExecutor.execute).toHaveBeenCalledWith(
49
+ expect.anything(),
50
+ expect.objectContaining({
51
+ headers: expect.objectContaining({
52
+ Authorization: `Bearer ${newToken}`,
53
+ }),
54
+ })
55
+ )
56
+ })
57
+ })
58
+
59
+ describe('HTTP Methods', () => {
60
+ test('get should make a GET request', async () => {
61
+ const url = '/test'
62
+ await httpClient.get(url)
63
+
64
+ expect(mockHeaderProvider.getHeaders).toHaveBeenCalled()
65
+ expect(mockRequestExecutor.execute).toHaveBeenCalledWith(
66
+ `${baseUrl}${url}`,
67
+ expect.objectContaining({
68
+ method: 'get',
69
+ headers: expect.objectContaining({
70
+ Authorization: `Bearer ${token}`,
71
+ }),
72
+ })
73
+ )
74
+ })
75
+
76
+ test('post should make a POST request with data', async () => {
77
+ const url = '/test'
78
+ const data = { name: 'test' }
79
+ await httpClient.post(url, data)
80
+
81
+ expect(mockHeaderProvider.getHeaders).toHaveBeenCalled()
82
+ expect(mockRequestExecutor.execute).toHaveBeenCalledWith(
83
+ `${baseUrl}${url}`,
84
+ expect.objectContaining({
85
+ method: 'post',
86
+ headers: expect.objectContaining({
87
+ Authorization: `Bearer ${token}`,
88
+ }),
89
+ body: JSON.stringify(data),
90
+ })
91
+ )
92
+ })
93
+
94
+ test('put should make a PUT request with data', async () => {
95
+ const url = '/test'
96
+ const data = { name: 'test' }
97
+ await httpClient.put(url, data)
98
+
99
+ expect(mockHeaderProvider.getHeaders).toHaveBeenCalled()
100
+ expect(mockRequestExecutor.execute).toHaveBeenCalledWith(
101
+ `${baseUrl}${url}`,
102
+ expect.objectContaining({
103
+ method: 'put',
104
+ headers: expect.objectContaining({
105
+ Authorization: `Bearer ${token}`,
106
+ }),
107
+ body: JSON.stringify(data),
108
+ })
109
+ )
110
+ })
111
+
112
+ test('patch should make a PATCH request with data', async () => {
113
+ const url = '/test'
114
+ const data = { name: 'test' }
115
+ await httpClient.patch(url, data)
116
+
117
+ expect(mockHeaderProvider.getHeaders).toHaveBeenCalled()
118
+ expect(mockRequestExecutor.execute).toHaveBeenCalledWith(
119
+ `${baseUrl}${url}`,
120
+ expect.objectContaining({
121
+ method: 'patch',
122
+ headers: expect.objectContaining({
123
+ Authorization: `Bearer ${token}`,
124
+ }),
125
+ body: JSON.stringify(data),
126
+ })
127
+ )
128
+ })
129
+
130
+ test('delete should make a DELETE request', async () => {
131
+ const url = '/test'
132
+ await httpClient.delete(url)
133
+
134
+ expect(mockHeaderProvider.getHeaders).toHaveBeenCalled()
135
+ expect(mockRequestExecutor.execute).toHaveBeenCalledWith(
136
+ `${baseUrl}${url}`,
137
+ expect.objectContaining({
138
+ method: 'delete',
139
+ headers: expect.objectContaining({
140
+ Authorization: `Bearer ${token}`,
141
+ }),
142
+ })
143
+ )
144
+ })
145
+ })
146
+
147
+ describe('URL Resolution', () => {
148
+ test('should prepend baseUrl to relative URLs', async () => {
149
+ const relativeUrl = '/api/endpoint'
150
+ await httpClient.get(relativeUrl)
151
+
152
+ expect(mockRequestExecutor.execute).toHaveBeenCalledWith(
153
+ `${baseUrl}${relativeUrl}`,
154
+ expect.anything()
155
+ )
156
+ })
157
+
158
+ test('should not modify absolute URLs', async () => {
159
+ const absoluteUrl = 'https://another-api.example.com/endpoint'
160
+ await httpClient.get(absoluteUrl)
161
+
162
+ expect(mockRequestExecutor.execute).toHaveBeenCalledWith(absoluteUrl, expect.anything())
163
+ })
164
+ })
165
+
166
+ describe('Data Version Header', () => {
167
+ beforeEach(() => {
168
+ // Reset all mocks and create a fresh HttpClient for each test
169
+ jest.resetAllMocks()
170
+ mockHeaderProvider = {
171
+ getHeaders: jest.fn().mockReturnValue({ ...headers }),
172
+ }
173
+ mockRequestExecutor = {
174
+ execute: jest.fn().mockResolvedValue(responseData),
175
+ }
176
+ httpClient = new HttpClient(baseUrl, token, mockHeaderProvider, mockRequestExecutor)
177
+ })
178
+
179
+ test('should add Data-Version header when provided', async () => {
180
+ const url = '/test'
181
+ const dataVersion = '1.2.3'
182
+
183
+ await httpClient.get(url, dataVersion)
184
+
185
+ // Check the actual calls that were made
186
+ expect(mockRequestExecutor.execute.mock.calls.length).toBe(1)
187
+
188
+ const requestOptions = mockRequestExecutor.execute.mock.calls[0][1]
189
+ expect(requestOptions.headers['Data-Version']).toBe(dataVersion)
190
+ })
191
+
192
+ test('should not add Data-Version header when not provided', async () => {
193
+ // Log what's happening for debugging
194
+ console.log('Starting test: should not add Data-Version header when not provided')
195
+
196
+ const url = '/test'
197
+
198
+ // Make sure we're using exact null here, not undefined
199
+ const dataVersion = null
200
+
201
+ // Create separate mocks for this test to avoid state bleeding
202
+ const testHeaderProvider = {
203
+ getHeaders: jest.fn().mockReturnValue({
204
+ 'Content-Type': 'application/json',
205
+ Accept: 'application/json',
206
+ }),
207
+ }
208
+
209
+ const testRequestExecutor = {
210
+ execute: jest.fn().mockResolvedValue(responseData),
211
+ }
212
+
213
+ // Create a completely fresh HttpClient instance
214
+ const testClient = new HttpClient(baseUrl, token, testHeaderProvider, testRequestExecutor)
215
+
216
+ await testClient.get(url, dataVersion)
217
+
218
+ console.log('Headers used in request:', testRequestExecutor.execute.mock.calls[0][1].headers)
219
+
220
+ // Direct check on the mock to validate Data-Version absence
221
+ const actualHeaders = testRequestExecutor.execute.mock.calls[0][1].headers
222
+ const hasDataVersion = Object.prototype.hasOwnProperty.call(actualHeaders, 'Data-Version')
223
+ expect(hasDataVersion).toBe(false)
224
+ })
225
+ })
226
+
227
+ describe('Error Handling', () => {
228
+ test('should properly handle HTTP errors', async () => {
229
+ const httpError = {
230
+ status: 404,
231
+ statusText: 'Not Found',
232
+ body: { message: 'Resource not found' },
233
+ url: `${baseUrl}/test`,
234
+ method: 'get',
235
+ }
236
+
237
+ mockRequestExecutor.execute.mockRejectedValueOnce(httpError)
238
+
239
+ await expect(httpClient.get('/test')).rejects.toEqual(httpError)
240
+ })
241
+
242
+ test('should properly handle network errors', async () => {
243
+ const networkError = new Error('Network error')
244
+
245
+ mockRequestExecutor.execute.mockImplementationOnce(() => {
246
+ throw networkError
247
+ })
248
+
249
+ await expect(httpClient.get('/test')).rejects.toMatchObject({
250
+ message: 'Network error',
251
+ url: '/test',
252
+ method: 'get',
253
+ originalError: networkError,
254
+ })
255
+ })
256
+ })
257
+ })
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/test/log.js CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes