musora-content-services 2.93.1 → 2.94.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.
- package/.claude/settings.local.json +14 -0
- package/.coderabbit.yaml +0 -0
- package/.editorconfig +0 -0
- package/.github/pull_request_template.md +0 -0
- package/.github/workflows/conventional-commits.yaml +0 -0
- package/.github/workflows/node.js.yml +0 -0
- package/.prettierignore +0 -0
- package/.prettierrc +0 -0
- package/.yarnrc.yml +1 -0
- package/CHANGELOG.md +24 -0
- package/README.md +0 -0
- package/jest.config.js +0 -0
- package/jsdoc.json +0 -0
- package/package.json +1 -1
- package/src/contentMetaData.js +0 -0
- package/src/filterBuilder.js +0 -0
- package/src/index.d.ts +4 -0
- package/src/index.js +4 -0
- package/src/infrastructure/http/HttpClient.ts +0 -0
- package/src/infrastructure/http/executors/FetchRequestExecutor.ts +0 -0
- package/src/infrastructure/http/index.ts +0 -0
- package/src/infrastructure/http/interfaces/HeaderProvider.ts +0 -0
- package/src/infrastructure/http/interfaces/HttpError.ts +0 -0
- package/src/infrastructure/http/interfaces/NetworkError.ts +0 -0
- package/src/infrastructure/http/interfaces/RequestExecutor.ts +0 -0
- package/src/infrastructure/http/interfaces/RequestOptions.ts +0 -0
- package/src/infrastructure/http/providers/DefaultHeaderProvider.ts +0 -0
- package/src/lib/httpHelper.js +0 -0
- package/src/lib/lastUpdated.js +0 -0
- package/src/lib/sanity/query.ts +0 -0
- package/src/services/api/types.js +0 -0
- package/src/services/api/types.ts +0 -0
- package/src/services/config.js +1 -1
- package/src/services/content/content.ts +0 -0
- package/src/services/content-org/content-org.js +0 -0
- package/src/services/content-org/guided-courses.ts +0 -0
- package/src/services/content-org/learning-paths.ts +31 -18
- package/src/services/content-org/playlists-types.js +0 -0
- package/src/services/content-org/playlists.js +0 -0
- package/src/services/content.js +0 -0
- package/src/services/contentAggregator.js +0 -0
- package/src/services/contentLikes.js +0 -0
- package/src/services/contentProgress.js +50 -13
- package/src/services/dataContext.js +0 -0
- package/src/services/dateUtils.js +0 -0
- package/src/services/eventsAPI.js +0 -0
- package/src/services/forums/categories.ts +0 -0
- package/src/services/forums/forums.ts +0 -0
- package/src/services/forums/posts.ts +0 -0
- package/src/services/forums/threads.ts +0 -0
- package/src/services/forums/types.ts +0 -0
- package/src/services/gamification/awards.ts +0 -0
- package/src/services/gamification/gamification.js +0 -0
- package/src/services/imageSRCBuilder.js +0 -0
- package/src/services/imageSRCVerify.js +0 -0
- package/src/services/liveTesting.ts +0 -0
- package/src/services/permissions/PermissionsAdapter.ts +0 -0
- package/src/services/permissions/PermissionsAdapterFactory.ts +0 -0
- package/src/services/permissions/PermissionsV1Adapter.ts +0 -0
- package/src/services/permissions/README.md +0 -0
- package/src/services/permissions/index.ts +0 -0
- package/src/services/progress-row/method-card.js +2 -1
- package/src/services/railcontent.js +0 -0
- package/src/services/recommendations.js +0 -0
- package/src/services/reporting/README.md +0 -0
- package/src/services/reporting/types.ts +0 -0
- package/src/services/sanity.js +27 -3
- package/src/services/sentry/.indexignore +0 -0
- package/src/services/sentry/index.ts +0 -0
- package/src/services/sync/.indexignore +0 -0
- package/src/services/sync/adapters/factory.ts +0 -0
- package/src/services/sync/adapters/lokijs.ts +0 -0
- package/src/services/sync/adapters/sqlite.ts +0 -0
- package/src/services/sync/concurrency-safety.ts +0 -0
- package/src/services/sync/context/index.ts +0 -0
- package/src/services/sync/context/providers/base.ts +0 -0
- package/src/services/sync/context/providers/connectivity.ts +0 -0
- package/src/services/sync/context/providers/durability.ts +0 -0
- package/src/services/sync/context/providers/index.ts +0 -0
- package/src/services/sync/context/providers/session.ts +0 -0
- package/src/services/sync/context/providers/tabs.ts +0 -0
- package/src/services/sync/context/providers/visibility.ts +0 -0
- package/src/services/sync/database/factory.ts +0 -0
- package/src/services/sync/errors/index.ts +0 -0
- package/src/services/sync/index.ts +0 -0
- package/src/services/sync/models/Base.ts +0 -0
- package/src/services/sync/models/ContentLike.ts +0 -0
- package/src/services/sync/models/ContentProgress.ts +1 -1
- package/src/services/sync/models/Practice.ts +0 -0
- package/src/services/sync/models/PracticeDayNote.ts +0 -0
- package/src/services/sync/models/index.ts +0 -0
- package/src/services/sync/repositories/base.ts +0 -0
- package/src/services/sync/repositories/content-likes.ts +0 -0
- package/src/services/sync/repositories/content-progress.ts +34 -7
- package/src/services/sync/repositories/index.ts +0 -0
- package/src/services/sync/repositories/practice-day-notes.ts +0 -0
- package/src/services/sync/repositories/practices.ts +0 -0
- package/src/services/sync/resolver.ts +0 -0
- package/src/services/sync/run-scope.ts +0 -0
- package/src/services/sync/schema/index.ts +0 -0
- package/src/services/sync/serializers/index.ts +0 -0
- package/src/services/sync/serializers/model.ts +0 -0
- package/src/services/sync/serializers/raw.ts +0 -0
- package/src/services/sync/store/push-coalescer.ts +0 -0
- package/src/services/sync/store-configs.ts +0 -0
- package/src/services/sync/strategies/base.ts +0 -0
- package/src/services/sync/strategies/index.ts +0 -0
- package/src/services/sync/strategies/initial.ts +0 -0
- package/src/services/sync/strategies/polling.ts +0 -0
- package/src/services/sync/utils/event-emitter.ts +0 -0
- package/src/services/sync/utils/index.ts +0 -0
- package/src/services/sync/utils/throttle.ts +0 -0
- package/src/services/sync/utils/timers.ts +0 -0
- package/src/services/types.js +0 -0
- package/src/services/user/account.ts +0 -0
- package/src/services/user/chat.js +0 -0
- package/src/services/user/interests.js +0 -0
- package/src/services/user/management.js +0 -0
- package/src/services/user/memberships.ts +0 -0
- package/src/services/user/notifications.js +0 -0
- package/src/services/user/payments.ts +0 -0
- package/src/services/user/permissions.js +0 -0
- package/src/services/user/profile.js +0 -0
- package/src/services/user/sessions.js +0 -0
- package/src/services/user/types.d.ts +0 -0
- package/src/services/user/types.js +0 -0
- package/src/services/user/user-management-system.js +0 -0
- package/src/services/userActivity.js +3 -2
- package/test/HttpClient.test.js +0 -0
- package/test/content.test.js +0 -0
- package/test/contentLikes.test.js +0 -0
- package/test/contentProgress.test.js +0 -0
- package/test/dataContext.test.js +0 -0
- package/test/forum.test.js +0 -0
- package/test/imageSRCBuilder.test.js +0 -0
- package/test/imageSRCVerify.test.js +0 -0
- package/test/initializeTests.js +0 -0
- package/test/learningPaths.test.js +0 -0
- package/test/lib/lastUpdated.test.js +0 -0
- package/test/live/contentProgressLive.test.js +0 -0
- package/test/live/railcontentLive.test.js +0 -0
- package/test/localStorageMock.js +0 -0
- package/test/log.js +0 -0
- package/test/mockData/mockData_fetchByRailContentIds_one_content.json +0 -0
- package/test/mockData/mockData_progress_content.json +0 -0
- package/test/mockData/mockData_sanity_progress_content.json +0 -0
- package/test/mockData/mockData_user_practices.json +0 -0
- package/test/notifications.test.js +0 -0
- package/test/progressRows.test.js +0 -0
- package/test/reporting.test.js +132 -0
- package/test/sanityQueryService.test.js +0 -0
- package/test/streakMessage.test.js +0 -0
- package/test/user/permissions.test.js +0 -0
- package/test/userActivity.test.js +0 -0
- package/tools/generate-index.cjs +0 -0
|
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
|
|
@@ -115,10 +115,10 @@ export default class ProgressRepository extends SyncRepository<ContentProgress>
|
|
|
115
115
|
return await this.queryAll(...clauses)
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
|
|
118
|
+
recordProgress(contentId: number, collection: { type: COLLECTION_TYPE; id: number } | null, progressPct: number, resumeTime?: number) {
|
|
119
119
|
const id = ProgressRepository.generateId(contentId, collection)
|
|
120
120
|
|
|
121
|
-
return this.
|
|
121
|
+
return this.upsertOne(id, (r) => {
|
|
122
122
|
r.content_id = contentId
|
|
123
123
|
r.collection_type = collection?.type ?? null
|
|
124
124
|
r.collection_id = collection?.id ?? null
|
|
@@ -132,12 +132,16 @@ export default class ProgressRepository extends SyncRepository<ContentProgress>
|
|
|
132
132
|
})
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
|
|
136
|
-
|
|
135
|
+
recordProgresses(
|
|
136
|
+
contentIds: number[],
|
|
137
|
+
collection: { type: COLLECTION_TYPE; id: number } | null,
|
|
138
|
+
progressPct: number
|
|
139
|
+
) {
|
|
140
|
+
return this.upsertSome(
|
|
137
141
|
Object.fromEntries(
|
|
138
|
-
|
|
139
|
-
ProgressRepository.generateId(contentId,
|
|
140
|
-
(r) => {
|
|
142
|
+
contentIds.map((contentId) => [
|
|
143
|
+
ProgressRepository.generateId(contentId, collection),
|
|
144
|
+
(r: ContentProgress) => {
|
|
141
145
|
r.content_id = contentId
|
|
142
146
|
r.collection_type = collection?.type ?? null
|
|
143
147
|
r.collection_id = collection?.id ?? null
|
|
@@ -150,6 +154,29 @@ export default class ProgressRepository extends SyncRepository<ContentProgress>
|
|
|
150
154
|
)
|
|
151
155
|
}
|
|
152
156
|
|
|
157
|
+
recordProgressesTentative(
|
|
158
|
+
contentProgresses: Record<string, number>, // Accept plain object
|
|
159
|
+
collection: { type: COLLECTION_TYPE; id: number } | null
|
|
160
|
+
) {
|
|
161
|
+
const data = Object.fromEntries(
|
|
162
|
+
Object.entries(contentProgresses).map(([contentId, progressPct]) => {
|
|
163
|
+
const generatedId = ProgressRepository.generateId(Number(contentId), collection)
|
|
164
|
+
console.log('Processing:', { contentId, progressPct, generatedId, collection })
|
|
165
|
+
return [
|
|
166
|
+
generatedId,
|
|
167
|
+
(record: ContentProgress) => {
|
|
168
|
+
record.content_id = Number(contentId)
|
|
169
|
+
record.collection_type = collection?.type ?? null
|
|
170
|
+
record.collection_id = collection?.id ?? null
|
|
171
|
+
record.state = progressPct === 100 ? STATE.COMPLETED : STATE.STARTED
|
|
172
|
+
record.progress_percent = progressPct
|
|
173
|
+
},
|
|
174
|
+
]
|
|
175
|
+
})
|
|
176
|
+
)
|
|
177
|
+
return this.upsertSomeTentative(data)
|
|
178
|
+
}
|
|
179
|
+
|
|
153
180
|
eraseProgress(contentId: number, collection: { type: COLLECTION_TYPE; id: number } | null) {
|
|
154
181
|
return this.deleteOne(ProgressRepository.generateId(contentId, collection))
|
|
155
182
|
}
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/services/types.js
CHANGED
|
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
|
|
File without changes
|
|
File without changes
|
|
@@ -40,6 +40,7 @@ import dayjs from 'dayjs'
|
|
|
40
40
|
import { addContextToContent } from './contentAggregator.js'
|
|
41
41
|
import { getMethodCard } from './progress-row/method-card.js'
|
|
42
42
|
import { db, Q } from './sync'
|
|
43
|
+
import {COLLECTION_TYPE} from "./sync/models/ContentProgress";
|
|
43
44
|
|
|
44
45
|
const DATA_KEY_PRACTICES = 'practices'
|
|
45
46
|
|
|
@@ -1039,7 +1040,7 @@ export async function getProgressRows({ brand = 'drumeo', limit = 8 } = {}) {
|
|
|
1039
1040
|
switch (item.type) {
|
|
1040
1041
|
case 'playlist':
|
|
1041
1042
|
return processPlaylistItem(item)
|
|
1042
|
-
case
|
|
1043
|
+
case COLLECTION_TYPE.LEARNING_PATH:
|
|
1043
1044
|
case 'method':
|
|
1044
1045
|
return item
|
|
1045
1046
|
default:
|
|
@@ -1282,7 +1283,7 @@ function mergeAndSortItems(items, limit) {
|
|
|
1282
1283
|
|
|
1283
1284
|
export function findIncompleteLesson(progressOnItems, currentContentId, contentType) {
|
|
1284
1285
|
const ids = Object.keys(progressOnItems).map(Number)
|
|
1285
|
-
if (contentType === 'guided-course' || contentType ===
|
|
1286
|
+
if (contentType === 'guided-course' || contentType === COLLECTION_TYPE.LEARNING_PATH) {
|
|
1286
1287
|
// Return first incomplete lesson
|
|
1287
1288
|
return ids.find((id) => progressOnItems[id] !== 'completed') || ids.at(0)
|
|
1288
1289
|
}
|
package/test/HttpClient.test.js
CHANGED
|
File without changes
|
package/test/content.test.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/test/dataContext.test.js
CHANGED
|
File without changes
|
package/test/forum.test.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/test/initializeTests.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/test/localStorageMock.js
CHANGED
|
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
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Reporting Service
|
|
3
|
+
*
|
|
4
|
+
* Note: These tests use mocked HTTP calls. For integration tests,
|
|
5
|
+
* ensure the backend API is running at the configured baseUrl.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { initializeTestService } from './initializeTests.js'
|
|
9
|
+
import {
|
|
10
|
+
reportContent,
|
|
11
|
+
reportForumPost,
|
|
12
|
+
submitReport
|
|
13
|
+
} from '../src/services/reporting/reporting.ts'
|
|
14
|
+
|
|
15
|
+
// Mock HttpClient to avoid actual API calls in tests
|
|
16
|
+
jest.mock('../src/infrastructure/http/HttpClient', () => {
|
|
17
|
+
return {
|
|
18
|
+
HttpClient: jest.fn().mockImplementation(() => ({
|
|
19
|
+
post: jest.fn().mockResolvedValue({
|
|
20
|
+
report_id: 12345,
|
|
21
|
+
message: 'Report submitted successfully',
|
|
22
|
+
is_duplicate: false
|
|
23
|
+
})
|
|
24
|
+
}))
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
describe('Reporting Service', () => {
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
initializeTestService()
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
describe('reportContent', () => {
|
|
34
|
+
test('should submit a content report with video_not_working category', async () => {
|
|
35
|
+
const result = await reportContent({
|
|
36
|
+
contentId: 123,
|
|
37
|
+
category: 'video_not_working',
|
|
38
|
+
description: 'Video freezes at 2:30'
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
expect(result).toBeDefined()
|
|
42
|
+
expect(result.report_id).toBe(12345)
|
|
43
|
+
expect(result.message).toBe('Report submitted successfully')
|
|
44
|
+
expect(result.is_duplicate).toBe(false)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test('should submit a content report without description', async () => {
|
|
48
|
+
const result = await reportContent({
|
|
49
|
+
contentId: 456,
|
|
50
|
+
category: 'incorrect_content'
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
expect(result).toBeDefined()
|
|
54
|
+
expect(result.report_id).toBeDefined()
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test('should submit a content report with other category and description', async () => {
|
|
58
|
+
const result = await reportContent({
|
|
59
|
+
contentId: 789,
|
|
60
|
+
category: 'other',
|
|
61
|
+
description: 'Audio quality is very poor'
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
expect(result).toBeDefined()
|
|
65
|
+
expect(result.report_id).toBeDefined()
|
|
66
|
+
expect(result.message).toBeDefined()
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
describe('reportForumPost', () => {
|
|
71
|
+
test('should submit a forum post report with required brand', async () => {
|
|
72
|
+
const result = await reportForumPost({
|
|
73
|
+
postId: 555,
|
|
74
|
+
brand: 'drumeo',
|
|
75
|
+
category: 'spam'
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
expect(result).toBeDefined()
|
|
79
|
+
expect(result.report_id).toBeDefined()
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
test('should submit a forum post report with description', async () => {
|
|
83
|
+
const result = await reportForumPost({
|
|
84
|
+
postId: 666,
|
|
85
|
+
brand: 'pianote',
|
|
86
|
+
category: 'harassment',
|
|
87
|
+
description: 'User is harassing other members'
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
expect(result).toBeDefined()
|
|
91
|
+
expect(result.report_id).toBeDefined()
|
|
92
|
+
expect(result.message).toBeDefined()
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
describe('submitReport', () => {
|
|
97
|
+
test('should submit a generic report for content', async () => {
|
|
98
|
+
const result = await submitReport({
|
|
99
|
+
reportableType: 'content',
|
|
100
|
+
reportableId: 999,
|
|
101
|
+
category: 'technical_issue',
|
|
102
|
+
description: 'Page not loading'
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
expect(result).toBeDefined()
|
|
106
|
+
expect(result.report_id).toBeDefined()
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
test('should submit a generic report for playlist', async () => {
|
|
110
|
+
const result = await submitReport({
|
|
111
|
+
reportableType: 'playlist',
|
|
112
|
+
reportableId: 777,
|
|
113
|
+
category: 'incorrect_content'
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
expect(result).toBeDefined()
|
|
117
|
+
expect(result.report_id).toBeDefined()
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
test('should submit a generic report for comment', async () => {
|
|
121
|
+
const result = await submitReport({
|
|
122
|
+
reportableType: 'comment',
|
|
123
|
+
reportableId: 888,
|
|
124
|
+
category: 'inappropriate',
|
|
125
|
+
description: 'Comment contains offensive language'
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
expect(result).toBeDefined()
|
|
129
|
+
expect(result.report_id).toBeDefined()
|
|
130
|
+
})
|
|
131
|
+
})
|
|
132
|
+
})
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/tools/generate-index.cjs
CHANGED
|
File without changes
|