musora-content-services 1.0.120 → 1.0.121

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/CHANGELOG.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [1.0.121](https://github.com/railroadmedia/musora-content-services/compare/v1.0.120...v1.0.121) (2024-10-01)
6
+
5
7
  ### [1.0.120](https://github.com/railroadmedia/musora-content-services/compare/v1.0.119...v1.0.120) (2024-09-30)
6
8
 
7
9
  ### [1.0.119](https://github.com/railroadmedia/musora-content-services/compare/v1.0.118...v1.0.119) (2024-09-27)
package/link_mcs.sh CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musora-content-services",
3
- "version": "1.0.120",
3
+ "version": "1.0.121",
4
4
  "description": "A package for Musoras content services ",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
package/src/index.d.ts CHANGED
@@ -5,6 +5,12 @@ import {
5
5
  initializeService
6
6
  } from './services/config.js';
7
7
 
8
+ import {
9
+ isContentLiked,
10
+ likeContent,
11
+ unlikeContent
12
+ } from './services/contentLikes.js';
13
+
8
14
  import {
9
15
  fetchAllCompletedStates,
10
16
  fetchCompletedContent,
@@ -12,10 +18,9 @@ import {
12
18
  fetchContentInProgress,
13
19
  fetchContentPageUserData,
14
20
  fetchHandler,
15
- fetchLikeContent,
16
21
  fetchSongsInProgress,
17
- fetchUnlikeContent,
18
- fetchUserContext,
22
+ fetchUserLikes,
23
+ fetchUserPermissions,
19
24
  fetchVimeoData
20
25
  } from './services/railcontent.js';
21
26
 
@@ -65,19 +70,8 @@ import {
65
70
  getSortOrder
66
71
  } from './services/sanity.js';
67
72
 
68
- import {
69
- clearCache,
70
- fetchContentData,
71
- init,
72
- likeContent,
73
- testClearLocal,
74
- unlikeContent,
75
- version
76
- } from './services/userContext.js';
77
-
78
73
  declare module 'musora-content-services' {
79
74
  export {
80
- clearCache,
81
75
  fetchAll,
82
76
  fetchAllCompletedStates,
83
77
  fetchAllFilterOptions,
@@ -94,7 +88,6 @@ declare module 'musora-content-services' {
94
88
  fetchCoachLessons,
95
89
  fetchCompletedContent,
96
90
  fetchCompletedState,
97
- fetchContentData,
98
91
  fetchContentInProgress,
99
92
  fetchContentPageUserData,
100
93
  fetchCourseOverview,
@@ -102,7 +95,6 @@ declare module 'musora-content-services' {
102
95
  fetchGenreLessons,
103
96
  fetchHandler,
104
97
  fetchLessonContent,
105
- fetchLikeContent,
106
98
  fetchLiveEvent,
107
99
  fetchMetadata,
108
100
  fetchMethod,
@@ -127,18 +119,16 @@ declare module 'musora-content-services' {
127
119
  fetchSongCount,
128
120
  fetchSongFilterOptions,
129
121
  fetchSongsInProgress,
130
- fetchUnlikeContent,
131
122
  fetchUpcomingEvents,
132
- fetchUserContext,
123
+ fetchUserLikes,
124
+ fetchUserPermissions,
133
125
  fetchVimeoData,
134
126
  fetchWorkouts,
135
127
  getSortOrder,
136
128
  globalConfig,
137
- init,
138
129
  initializeService,
130
+ isContentLiked,
139
131
  likeContent,
140
- testClearLocal,
141
132
  unlikeContent,
142
- version,
143
133
  }
144
134
  }
package/src/index.js CHANGED
@@ -5,6 +5,12 @@ import {
5
5
  initializeService
6
6
  } from './services/config.js';
7
7
 
8
+ import {
9
+ isContentLiked,
10
+ likeContent,
11
+ unlikeContent
12
+ } from './services/contentLikes.js';
13
+
8
14
  import {
9
15
  fetchAllCompletedStates,
10
16
  fetchCompletedContent,
@@ -12,10 +18,9 @@ import {
12
18
  fetchContentInProgress,
13
19
  fetchContentPageUserData,
14
20
  fetchHandler,
15
- fetchLikeContent,
16
21
  fetchSongsInProgress,
17
- fetchUnlikeContent,
18
- fetchUserContext,
22
+ fetchUserLikes,
23
+ fetchUserPermissions,
19
24
  fetchVimeoData
20
25
  } from './services/railcontent.js';
21
26
 
@@ -65,18 +70,7 @@ import {
65
70
  getSortOrder
66
71
  } from './services/sanity.js';
67
72
 
68
- import {
69
- clearCache,
70
- fetchContentData,
71
- init,
72
- likeContent,
73
- testClearLocal,
74
- unlikeContent,
75
- version
76
- } from './services/userContext.js';
77
-
78
73
  export {
79
- clearCache,
80
74
  fetchAll,
81
75
  fetchAllCompletedStates,
82
76
  fetchAllFilterOptions,
@@ -93,7 +87,6 @@ export {
93
87
  fetchCoachLessons,
94
88
  fetchCompletedContent,
95
89
  fetchCompletedState,
96
- fetchContentData,
97
90
  fetchContentInProgress,
98
91
  fetchContentPageUserData,
99
92
  fetchCourseOverview,
@@ -101,7 +94,6 @@ export {
101
94
  fetchGenreLessons,
102
95
  fetchHandler,
103
96
  fetchLessonContent,
104
- fetchLikeContent,
105
97
  fetchLiveEvent,
106
98
  fetchMetadata,
107
99
  fetchMethod,
@@ -126,17 +118,15 @@ export {
126
118
  fetchSongCount,
127
119
  fetchSongFilterOptions,
128
120
  fetchSongsInProgress,
129
- fetchUnlikeContent,
130
121
  fetchUpcomingEvents,
131
- fetchUserContext,
122
+ fetchUserLikes,
123
+ fetchUserPermissions,
132
124
  fetchVimeoData,
133
125
  fetchWorkouts,
134
126
  getSortOrder,
135
127
  globalConfig,
136
- init,
137
128
  initializeService,
129
+ isContentLiked,
138
130
  likeContent,
139
- testClearLocal,
140
131
  unlikeContent,
141
- version,
142
132
  };
@@ -4,7 +4,8 @@
4
4
 
5
5
  let globalConfig = {
6
6
  sanityConfig: {},
7
- railcontentConfig: {}
7
+ railcontentConfig: {},
8
+ localStorage: null
8
9
  };
9
10
 
10
11
  /**
@@ -24,6 +25,7 @@ let globalConfig = {
24
25
  * @param {string} config.railcontentConfig.token - The token for authenticating user-specific requests.
25
26
  * @param {string} config.railcontentConfig.userId - The user ID for fetching user-specific data.
26
27
  * @param {string} config.railcontentConfig.baseUrl - The url for the enviroment.
28
+ * @param {Object} config.localStorage - Cache to use for localStorage
27
29
 
28
30
  *
29
31
  * @example
@@ -41,12 +43,14 @@ let globalConfig = {
41
43
  * token: 'your-user-api-token',
42
44
  * userId: 'current-user-id',
43
45
  * baseUrl: 'https://web-staging-one.musora.com'
44
- * }
46
+ * },
47
+ * localStorage: localStorage
45
48
  * });
46
49
  */
47
50
  function initializeService(config) {
48
51
  globalConfig.sanityConfig = config.sanityConfig;
49
52
  globalConfig.railcontentConfig = config.railcontentConfig;
53
+ globalConfig.localStorage = config.localStorage;
50
54
  }
51
55
 
52
56
  // Export both the initialization function and the config object
@@ -0,0 +1,38 @@
1
+ import {fetchUserLikes, postContentLiked, postContentUnliked} from "./railcontent";
2
+ import {DataContext, ContentVersionKey} from "./dataContext";
3
+
4
+ export let dataContext = new DataContext(ContentVersionKey, fetchUserLikes);
5
+
6
+ export async function isContentLiked(contentId) {
7
+ let data = await dataContext.getData();
8
+ return data.includes(contentId);
9
+ }
10
+
11
+ export async function likeContent(contentId) {
12
+ await dataContext.update(
13
+ function (context) {
14
+ if (!context.data.includes(contentId)) {
15
+ context.data.push(contentId);
16
+ }
17
+ },
18
+ async function(){
19
+ return postContentLiked(contentId);
20
+ }
21
+ );
22
+ }
23
+
24
+ export async function unlikeContent(contentId) {
25
+ await dataContext.update(
26
+ function (context) {
27
+ if (context.data.includes(contentId)) {
28
+ const index = context.data.indexOf(contentId);
29
+ if (index > -1) { // only splice array when item is found
30
+ context.data.splice(index, 1); // 2nd parameter means remove one item only
31
+ }
32
+ }
33
+ },
34
+ async function(){
35
+ return postContentUnliked(contentId);
36
+ }
37
+ );
38
+ }
@@ -0,0 +1,88 @@
1
+ import {globalConfig} from "./config";
2
+
3
+ //These constants need to match MWP UserDataVersionKeyEnum enum
4
+ export const ContentVersionKey = 0;
5
+
6
+ let cache = null;
7
+
8
+ export class DataContext {
9
+ context = null;
10
+
11
+ constructor(dataVersionKey, fetchDataFunction) {
12
+ this.dataVersionKey = dataVersionKey;
13
+ this.fetchDataFunction = fetchDataFunction;
14
+ this.localStorageKey = `dataContext_${this.dataVersionKey}`;
15
+ this.localStorageLastUpdatedKey = `dataContext_${this.dataVersionKey}_lastUpdated`;
16
+ }
17
+
18
+ async getData() {
19
+ this.ensureLocalContextLoaded();
20
+ if (!this.context || this.shouldVerifyServerVerions()) {
21
+ let version = this.version();
22
+ let data = await this.fetchData(version);
23
+ if (data.version !== "No Change") {
24
+ this.context = data;
25
+ cache.setItem(this.localStorageKey, JSON.stringify(data));
26
+ }
27
+ cache.setItem(this.localStorageLastUpdatedKey, new Date().getTime());
28
+ }
29
+ return this.context.data;
30
+ }
31
+
32
+ async fetchData(version) {
33
+ return await this.fetchDataFunction(version);
34
+ }
35
+
36
+ ensureLocalContextLoaded() {
37
+ if (this.context) return;
38
+ this.verifyConfig();
39
+ let localData = cache.getItem(this.localStorageKey);
40
+ if (localData) {
41
+ this.context = JSON.parse(localData);
42
+ }
43
+ }
44
+
45
+ verifyConfig() {
46
+ if (!cache) {
47
+ cache = globalConfig.localStorage;
48
+ if (!cache) throw new Error('dataContext: LocalStorage cache not configured in musora content services initializeService.');
49
+ }
50
+ }
51
+
52
+ shouldVerifyServerVerions() {
53
+ let lastUpdated = cache.getItem(this.localStorageLastUpdatedKey);
54
+ if (!lastUpdated) return false;
55
+ const verifyServerTime = 10000; //10 s
56
+ return (new Date().getTime() - lastUpdated) > verifyServerTime;
57
+ }
58
+
59
+ clearCache() {
60
+ this.clearContext();
61
+ cache.removeItem(this.localStorageKey);
62
+ cache.removeItem(this.localStorageLastUpdatedKey);
63
+ }
64
+
65
+ clearContext() {
66
+ this.context = null;
67
+ }
68
+
69
+ async update(localUpdateFunction, serverUpdateFunction) {
70
+ this.ensureLocalContextLoaded();
71
+ if (this.context) {
72
+ localUpdateFunction(this.context);
73
+ this.context.version++;
74
+ let data = JSON.stringify(this.context);
75
+ cache.setItem(this.localStorageKey, data);
76
+ cache.setItem(this.localStorageLastUpdatedKey, new Date().getTime());
77
+ }
78
+ let response = await serverUpdateFunction();
79
+ if (response.version !== this.version()) {
80
+ this.clearCache();
81
+ }
82
+ }
83
+
84
+ version() {
85
+ return this.context?.version ?? -1;
86
+ }
87
+
88
+ }
@@ -2,7 +2,7 @@
2
2
  * @module Railcontent-Services
3
3
  */
4
4
 
5
- const { globalConfig } = require('./config');
5
+ const {globalConfig} = require('./config');
6
6
 
7
7
 
8
8
  /**
@@ -24,10 +24,11 @@ export async function fetchCompletedState(content_id) {
24
24
  };
25
25
 
26
26
  try {
27
- const response = await fetchAbsolute(url, { headers });
27
+ const response = await fetchAbsolute(url, {headers});
28
28
  const result = await response.json();
29
29
 
30
- if (result && result[content_id]) { return result[content_id]; // Return the correct object
30
+ if (result && result[content_id]) {
31
+ return result[content_id]; // Return the correct object
31
32
  } else {
32
33
  return null; // Handle unexpected structure
33
34
  }
@@ -56,7 +57,7 @@ export async function fetchVimeoData(vimeo_id) {
56
57
  };
57
58
 
58
59
  try {
59
- const response = await fetchAbsolute(url, { headers });
60
+ const response = await fetchAbsolute(url, {headers});
60
61
  const result = await response.json();
61
62
 
62
63
  if (result) {
@@ -91,9 +92,9 @@ export async function fetchAllCompletedStates(contentIds) {
91
92
  };
92
93
 
93
94
  try {
94
- const response = await fetchAbsolute(url, { headers });
95
+ const response = await fetchAbsolute(url, {headers});
95
96
  const result = await response.json();
96
- if(result){
97
+ if (result) {
97
98
  return result;
98
99
  } else {
99
100
  console.log('result not json');
@@ -123,9 +124,9 @@ export async function fetchSongsInProgress(brand) {
123
124
  };
124
125
 
125
126
  try {
126
- const response = await fetchAbsolute(url, { headers });
127
+ const response = await fetchAbsolute(url, {headers});
127
128
  const result = await response.json();
128
- if(result){
129
+ if (result) {
129
130
  //console.log('fetchSongsInProgress', result);
130
131
  return result;
131
132
  } else {
@@ -150,12 +151,12 @@ export async function fetchSongsInProgress(brand) {
150
151
  * .then(songs => console.log(songs))
151
152
  * .catch(error => console.error(error));
152
153
  */
153
- export async function fetchContentInProgress(type="all", brand, { page, limit } = {}) {
154
+ export async function fetchContentInProgress(type = "all", brand, {page, limit} = {}) {
154
155
  let url;
155
156
  const limitString = limit ? `&limit=${limit}` : '';
156
157
  const pageString = page ? `&page=${page}` : '';
157
158
 
158
- if(type === "all") {
159
+ if (type === "all") {
159
160
  url = `/content/in_progress/${globalConfig.railcontentConfig.userId}?brand=${brand}${limitString}${pageString}`;
160
161
  } else {
161
162
  url = `/content/in_progress/${globalConfig.railcontentConfig.userId}?content_type=${type}&brand=${brand}${limitString}${pageString}`;
@@ -165,9 +166,9 @@ export async function fetchContentInProgress(type="all", brand, { page, limit }
165
166
  'X-CSRF-TOKEN': globalConfig.railcontentConfig.token
166
167
  };
167
168
  try {
168
- const response = await fetchAbsolute(url, { headers });
169
+ const response = await fetchAbsolute(url, {headers});
169
170
  const result = await response.json();
170
- if(result){
171
+ if (result) {
171
172
  //console.log('contentInProgress', result);
172
173
  return result;
173
174
  } else {
@@ -192,12 +193,12 @@ export async function fetchContentInProgress(type="all", brand, { page, limit }
192
193
  * .then(songs => console.log(songs))
193
194
  * .catch(error => console.error(error));
194
195
  */
195
- export async function fetchCompletedContent(type="all", brand, { page, limit } = {}) {
196
+ export async function fetchCompletedContent(type = "all", brand, {page, limit} = {}) {
196
197
  let url;
197
198
  const limitString = limit ? `&limit=${limit}` : '';
198
199
  const pageString = page ? `&page=${page}` : '';
199
200
 
200
- if(type === "all") {
201
+ if (type === "all") {
201
202
  url = `/content/completed/${globalConfig.railcontentConfig.userId}?brand=${brand}${limitString}${pageString}`;
202
203
  } else {
203
204
  url = `/content/completed/${globalConfig.railcontentConfig.userId}?content_type=${type}&brand=${brand}${limitString}${pageString}`;
@@ -207,9 +208,9 @@ export async function fetchCompletedContent(type="all", brand, { page, limit } =
207
208
  'X-CSRF-TOKEN': globalConfig.railcontentConfig.token
208
209
  };
209
210
  try {
210
- const response = await fetchAbsolute(url, { headers });
211
+ const response = await fetchAbsolute(url, {headers});
211
212
  const result = await response.json();
212
- if(result){
213
+ if (result) {
213
214
  //console.log('completed content', result);
214
215
  return result;
215
216
  } else {
@@ -239,32 +240,11 @@ export async function fetchContentPageUserData(contentId) {
239
240
  'X-CSRF-TOKEN': globalConfig.railcontentConfig.token
240
241
  };
241
242
 
242
- try {
243
- const response = await fetchAbsolute(url, { headers });
244
- const result = await response.json();
245
- if(result){
246
- console.log('fetchContentPageUserData', result);
247
- return result;
248
- } else {
249
- console.log('result not json');
250
- }
251
- } catch (error) {
252
- console.error('Fetch error:', error);
253
- return null;
254
- }
255
- }
256
-
257
- export async function fetchUserContext() {
258
- let url = `/content/user_data_all`;
259
- const headers = {
260
- 'Content-Type': 'application/json',
261
- 'X-CSRF-TOKEN': globalConfig.railcontentConfig.token
262
- };
263
243
  try {
264
244
  const response = await fetchAbsolute(url, {headers});
265
245
  const result = await response.json();
266
246
  if (result) {
267
- console.log('fetchUserContext', result);
247
+ console.log('fetchContentPageUserData', result);
268
248
  return result;
269
249
  } else {
270
250
  console.log('result not json');
@@ -287,11 +267,16 @@ export async function fetchUserPermissions() {
287
267
  return fetchHandler(url, 'get') ?? [];
288
268
  }
289
269
 
290
- export async function fetchHandler(url, method = "get") {
291
- const headers = {
270
+ async function fetchDataHandler(url, dataVersion, method = "get") {
271
+ return fetchHandler(url, method, dataVersion);
272
+ }
273
+
274
+ export async function fetchHandler(url, method = "get", dataVersion = null) {
275
+ let headers = {
292
276
  'Content-Type': 'application/json',
293
- 'X-CSRF-TOKEN': globalConfig.railcontentConfig.token
277
+ 'X-CSRF-TOKEN': globalConfig.railcontentConfig.token,
294
278
  };
279
+ if (dataVersion) headers['Data-Version'] = dataVersion;
295
280
  try {
296
281
  const response = await fetchAbsolute(url, {method, headers});
297
282
  const result = await response.json();
@@ -306,18 +291,23 @@ export async function fetchHandler(url, method = "get") {
306
291
  return null;
307
292
  }
308
293
 
309
- export async function fetchLikeContent(contentId) {
310
- let url = `/content/${contentId}/like`;
294
+ export async function fetchUserLikes(currentVersion) {
295
+ let url = `/content/user/likes/all`;
296
+ return fetchDataHandler(url, currentVersion);
297
+ }
298
+
299
+ export async function postContentLiked(contentId) {
300
+ let url = `/content/user/likes/like/${contentId}`;
311
301
  return await fetchHandler(url, "post");
312
302
  }
313
303
 
314
- export async function fetchUnlikeContent(contentId) {
315
- let url = `/content/${contentId}/unlike`;
304
+ export async function postContentUnliked(contentId) {
305
+ let url = `/content/user/likes/unlike/${contentId}`;
316
306
  return await fetchHandler(url, "post");
317
307
  }
318
308
 
319
309
  function fetchAbsolute(url, params) {
320
- if(globalConfig.railcontentConfig.baseUrl) {
310
+ if (globalConfig.railcontentConfig.baseUrl) {
321
311
  if (url.startsWith('/')) {
322
312
  return fetch(globalConfig.railcontentConfig.baseUrl + url, params)
323
313
  }
@@ -0,0 +1,86 @@
1
+ import {isContentLiked, dataContext, likeContent, unlikeContent} from "../src/services/contentLikes";
2
+ import {LocalStorageMock} from "./localStorageMock";
3
+ import {initializeService} from "../src";
4
+
5
+ const railContentModule = require('../src/services/railcontent.js')
6
+
7
+ describe('commentLikesDataContext', function () {
8
+ let mock = null;
9
+ const testVersion = 1;
10
+
11
+ beforeEach(() => {
12
+ initializeService({localStorage: new LocalStorageMock()});
13
+ mock = jest.spyOn(dataContext, 'fetchData');
14
+ var json = JSON.parse(`{"version":${testVersion},"data":[308516,308515,308514,308518]}`);
15
+ mock.mockImplementation(() => json);
16
+ });
17
+
18
+ test('contentLiked', async () => {
19
+ let result = await isContentLiked(308516);
20
+ expect(result).toBe(true);
21
+ });
22
+
23
+ test('contentNotLiked', async () => {
24
+ let result = await isContentLiked(121111);
25
+ expect(result).toBe(false);
26
+ });
27
+
28
+ test('ensureOnlyOneServerFetchRequest', async () => {
29
+ dataContext.clearCache();
30
+ await isContentLiked(308516);
31
+ await isContentLiked(308514);
32
+ await isContentLiked(121111);
33
+ expect(dataContext.fetchData).toHaveBeenCalledTimes(1);
34
+ });
35
+
36
+ test('ensureDataPulledFromLocalCache', async () => {
37
+ dataContext.clearCache();
38
+ await isContentLiked(308516);
39
+ dataContext.clearContext();
40
+ await isContentLiked(308514);
41
+ expect(dataContext.fetchData).toHaveBeenCalledTimes(1);
42
+ });
43
+
44
+ test('likeContent', async () => {
45
+ mock = jest.spyOn(railContentModule, 'postContentLiked');
46
+ var json = JSON.parse(`{"version":${testVersion + 1}}`);
47
+ mock.mockImplementation(() => json);
48
+
49
+ dataContext.clearCache();
50
+ let isLiked = await isContentLiked(111111);
51
+ expect(isLiked).toBe(false);
52
+
53
+ await likeContent(111111);
54
+ isLiked = await isContentLiked(111111);
55
+ expect(isLiked).toBe(true);
56
+
57
+ dataContext.clearContext();
58
+ isLiked = await isContentLiked(111111);
59
+ expect(isLiked).toBe(true);
60
+
61
+ expect(dataContext.version()).toBe(testVersion + 1);
62
+ });
63
+
64
+
65
+ test('unlikeContent', async () => {
66
+ mock = jest.spyOn(railContentModule, 'postContentUnliked');
67
+ var json = JSON.parse(`{"version":${testVersion + 1}}`);
68
+ mock.mockImplementation(() => json);
69
+
70
+ dataContext.clearCache();
71
+ let isLiked = await isContentLiked(308516);
72
+ expect(isLiked).toBe(true);
73
+
74
+ await unlikeContent(308516);
75
+ console.log(dataContext.context);
76
+ isLiked = await isContentLiked(308516);
77
+ expect(isLiked).toBe(false);
78
+
79
+ dataContext.clearContext();
80
+ isLiked = await isContentLiked(308516);
81
+ expect(isLiked).toBe(false);
82
+
83
+ expect(dataContext.version()).toBe(testVersion + 1);
84
+ });
85
+
86
+ });
@@ -1,105 +0,0 @@
1
- import {fetchUserContext, fetchLikeContent, fetchUnlikeContent} from "./railcontent";
2
-
3
- const StorageKey = "userContext";
4
- let userContext = null;
5
- let cache = null;
6
-
7
- export function init(localCache) {
8
- cache = localCache;
9
- }
10
-
11
- export function version() {
12
- ensureLocalContextLoaded();
13
- return userContext.version;
14
- }
15
-
16
- async function getUserContext() {
17
- ensureLocalContextLoaded();
18
- if (userContext) {
19
- verifyContextIsValid();
20
- }
21
- if (!userContext) {
22
- await fetchFromServer();
23
- }
24
- return userContext;
25
- }
26
-
27
- function verifyContextIsValid() {
28
-
29
- }
30
-
31
- function ensureLocalContextLoaded() {
32
- if (userContext) return;
33
- let localData = cache.getItem(StorageKey);
34
- if (localData) {
35
- userContext = JSON.parse(localData);
36
- }
37
- }
38
-
39
- function updateLocalContext(contentId, updateFunction) {
40
- ensureLocalContextLoaded();
41
- if (userContext) {
42
- let contentData = userContext.data[contentId] ?? [];
43
- updateFunction(contentData);
44
- userContext.data[contentId] = contentData;
45
- userContext.version++;
46
- let data = JSON.stringify(userContext);
47
- cache.setItem(StorageKey, data);
48
- }
49
- }
50
-
51
- async function fetchFromServer() {
52
- let data = await fetchUserContext();
53
- userContext = JSON.parse(data);
54
- cache.setItem(StorageKey, data);
55
- }
56
-
57
-
58
- function transformData(data, contentId) {
59
- let transformed = [];
60
- transformed["contentId"] = contentId;
61
- transformed["liked"] = (data && data.l) ?? 0;
62
- return transformed;
63
- }
64
-
65
- export async function fetchContentData(contentId) {
66
- let userContext = await getUserContext();
67
- let data = userContext.data[contentId];
68
- data = transformData(data, contentId);
69
- return data;
70
- }
71
-
72
- export function clearCache() {
73
- userContext = null;
74
- cache.setItem(StorageKey, null);
75
- }
76
-
77
- export function testClearLocal() {
78
- userContext = null;
79
- }
80
-
81
- export async function likeContent(contentId) {
82
- updateLocalContext(contentId,
83
- function (contentData) {
84
- contentData.l = 1;
85
- }
86
- );
87
-
88
- let result = await fetchLikeContent(contentId);
89
- if (result.version !== userContext.version) {
90
- clearCache();
91
- }
92
- }
93
-
94
- export async function unlikeContent(contentId) {
95
- updateLocalContext(contentId,
96
- function (contentData) {
97
- contentData.l = 0;
98
- }
99
- );
100
-
101
- let result = await fetchUnlikeContent(contentId);
102
- if (result.version !== userContext.version) {
103
- clearCache();
104
- }
105
- }
@@ -1,86 +0,0 @@
1
- const railContentModule = require('../src/services/railcontent.js')
2
- const userContextModule = require('../src/services/userContext.js');
3
- import {LocalStorageMock} from "./localStorageMock";
4
-
5
- describe('userContext', function () {
6
- let mock = null;
7
- const testVersion = 1;
8
- beforeEach(() => {
9
- userContextModule.init(new LocalStorageMock());
10
- mock = jest.spyOn(railContentModule, 'fetchUserContext');
11
- var json = `{"version":${testVersion},"data":{"308516":{"l":1},"308515":{"p":100},"308514":{"p":13},"308518":{"p":100}}}`;
12
- mock.mockImplementation(() => json);
13
- });
14
-
15
- test('contentLiked', async () => {
16
- let contentData = await userContextModule.fetchContentData(308516);
17
- expect(contentData.liked).toBe(1);
18
- });
19
-
20
- test('contentDoesNotExist', async () => {
21
- //fetch content that does not exist
22
- let contentData = await userContextModule.fetchContentData(121111);
23
- expect(contentData.liked).toBe(0);
24
- });
25
-
26
- test('ensureOnlyOneServerFetchRequest', async () => {
27
- userContextModule.clearCache();
28
- await userContextModule.fetchContentData(308516);
29
- await userContextModule.fetchContentData(308514);
30
- expect(railContentModule.fetchUserContext).toHaveBeenCalledTimes(1);
31
- });
32
-
33
- test('ensureDataPulledFromLocalCache', async () => {
34
- userContextModule.clearCache();
35
- await userContextModule.fetchContentData(308516);
36
- userContextModule.testClearLocal();
37
- await userContextModule.fetchContentData(308514);
38
- expect(railContentModule.fetchUserContext).toHaveBeenCalledTimes(1);
39
- });
40
-
41
- // test('hashExpiration', async () => {
42
- // userContextModule.clearCache();
43
- // await userContextModule.fetchContentData(testHash308516);
44
- // let newHash = "8g9qg5wn3e5s5oi69q6g22et9w6g34t5";
45
- // await userContextModule.fetchContentData(newHash, 308516);
46
- // expect(railContentModule.fetchUserContext).toHaveBeenCalledTimes(2);
47
- // });
48
-
49
- test('likeContent', async () => {
50
- mock = jest.spyOn(railContentModule, 'fetchLikeContent');
51
- var json = JSON.parse(`{"version":${testVersion + 1}}`);
52
- mock.mockImplementation(() => json);
53
-
54
- userContextModule.clearCache();
55
- await userContextModule.fetchContentData(308515);
56
- await userContextModule.likeContent(308515);
57
- let contentData = await userContextModule.fetchContentData(308515);
58
- expect(contentData.liked).toBe(1);
59
-
60
- userContextModule.testClearLocal();
61
- contentData = await userContextModule.fetchContentData(308515);
62
- expect(contentData.liked).toBe(1);
63
-
64
- expect(userContextModule.version()).toBe(testVersion + 1);
65
- });
66
-
67
-
68
- test('unlikeContent', async () => {
69
- mock = jest.spyOn(railContentModule, 'fetchUnlikeContent');
70
- var json = JSON.parse(`{"version":${testVersion + 1}}`);
71
- mock.mockImplementation(() => json);
72
-
73
- userContextModule.clearCache();
74
- await userContextModule.fetchContentData(308516);
75
- await userContextModule.unlikeContent(308516);
76
- let contentData = await userContextModule.fetchContentData(308516);
77
- expect(contentData.liked).toBe(0);
78
-
79
- userContextModule.testClearLocal();
80
- contentData = await userContextModule.fetchContentData(308516);
81
- expect(contentData.liked).toBe(0);
82
-
83
- expect(userContextModule.version()).toBe(testVersion + 1);
84
- });
85
-
86
- });