@amityco/ts-sdk 0.0.1-beta.30 → 0.0.1-beta.32

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 (233) hide show
  1. package/dist/@types/core/cache.d.ts +3 -0
  2. package/dist/@types/core/cache.d.ts.map +1 -1
  3. package/dist/@types/core/errors.d.ts +2 -1
  4. package/dist/@types/core/errors.d.ts.map +1 -1
  5. package/dist/@types/core/events.d.ts +2 -0
  6. package/dist/@types/core/events.d.ts.map +1 -1
  7. package/dist/@types/core/live.d.ts +8 -4
  8. package/dist/@types/core/live.d.ts.map +1 -1
  9. package/dist/@types/core/query.d.ts +1 -1
  10. package/dist/@types/core/query.d.ts.map +1 -1
  11. package/dist/@types/domains/channel.d.ts +1 -0
  12. package/dist/@types/domains/channel.d.ts.map +1 -1
  13. package/dist/@types/domains/message.d.ts +13 -6
  14. package/dist/@types/domains/message.d.ts.map +1 -1
  15. package/dist/@types/domains/post.d.ts +10 -3
  16. package/dist/@types/domains/post.d.ts.map +1 -1
  17. package/dist/@types/domains/user.d.ts +1 -0
  18. package/dist/@types/domains/user.d.ts.map +1 -1
  19. package/dist/@types/index.d.ts +2 -2
  20. package/dist/@types/index.d.ts.map +1 -1
  21. package/dist/cache/api/backupCache.d.ts.map +1 -1
  22. package/dist/cache/api/isInTombstone.d.ts +18 -0
  23. package/dist/cache/api/isInTombstone.d.ts.map +1 -0
  24. package/dist/cache/api/pushToTombstone.d.ts +17 -0
  25. package/dist/cache/api/pushToTombstone.d.ts.map +1 -0
  26. package/dist/cache/api/tests/pushToCache.test.d.ts +2 -0
  27. package/dist/cache/api/tests/pushToCache.test.d.ts.map +1 -0
  28. package/dist/cache/utils/index.d.ts +17 -0
  29. package/dist/cache/utils/index.d.ts.map +1 -1
  30. package/dist/channel/api/addChannelMembers.d.ts.map +1 -1
  31. package/dist/channel/api/getChannel.d.ts.map +1 -1
  32. package/dist/channel/api/queryChannelMembers.d.ts +1 -1
  33. package/dist/channel/api/queryChannelMembers.d.ts.map +1 -1
  34. package/dist/channel/api/queryChannels.d.ts +1 -1
  35. package/dist/channel/api/queryChannels.d.ts.map +1 -1
  36. package/dist/channel/api/tests/addChannelMembers.test.d.ts +2 -0
  37. package/dist/channel/api/tests/addChannelMembers.test.d.ts.map +1 -0
  38. package/dist/channel/api/tests/getChannel.test.d.ts +2 -0
  39. package/dist/channel/api/tests/getChannel.test.d.ts.map +1 -0
  40. package/dist/channel/api/tests/queryChannelMembers.test.d.ts +2 -0
  41. package/dist/channel/api/tests/queryChannelMembers.test.d.ts.map +1 -0
  42. package/dist/channel/api/tests/queryChannels.test.d.ts +2 -0
  43. package/dist/channel/api/tests/queryChannels.test.d.ts.map +1 -0
  44. package/dist/channel/events/index.d.ts +2 -0
  45. package/dist/channel/events/index.d.ts.map +1 -1
  46. package/dist/channel/events/onChannelMemberAdded.d.ts.map +1 -1
  47. package/dist/channel/events/onChannelMemberBanned.d.ts +17 -0
  48. package/dist/channel/events/onChannelMemberBanned.d.ts.map +1 -0
  49. package/dist/channel/events/onChannelMemberUnbanned.d.ts +17 -0
  50. package/dist/channel/events/onChannelMemberUnbanned.d.ts.map +1 -0
  51. package/dist/channel/events/tests/onChannelMemberBanned.test.d.ts +2 -0
  52. package/dist/channel/events/tests/onChannelMemberBanned.test.d.ts.map +1 -0
  53. package/dist/channel/events/tests/onChannelMemberUnbanned.test.d.ts +2 -0
  54. package/dist/channel/events/tests/onChannelMemberUnbanned.test.d.ts.map +1 -0
  55. package/dist/channel/observers/liveChannelMembers.d.ts.map +1 -1
  56. package/dist/channel/observers/liveChannels.d.ts.map +1 -1
  57. package/dist/channel/{tests/utils → utils/tests}/prepareChannelPayload.test.d.ts +0 -0
  58. package/dist/channel/utils/tests/prepareChannelPayload.test.d.ts.map +1 -0
  59. package/dist/core/events.d.ts +3 -3
  60. package/dist/core/events.d.ts.map +1 -1
  61. package/dist/core/query/filtering.d.ts +11 -0
  62. package/dist/core/query/filtering.d.ts.map +1 -1
  63. package/dist/core/query/query.d.ts.map +1 -1
  64. package/dist/core/transports/http.d.ts +5 -0
  65. package/dist/core/transports/http.d.ts.map +1 -1
  66. package/dist/core/transports/test/http.test.d.ts +2 -0
  67. package/dist/core/transports/test/http.test.d.ts.map +1 -0
  68. package/dist/index.cjs.js +980 -534
  69. package/dist/index.esm.js +962 -526
  70. package/dist/index.umd.js +4 -4
  71. package/dist/message/api/createMessage.d.ts +4 -1
  72. package/dist/message/api/createMessage.d.ts.map +1 -1
  73. package/dist/message/api/deleteMessage.d.ts.map +1 -1
  74. package/dist/message/api/getMessage.d.ts +1 -1
  75. package/dist/message/api/getMessage.d.ts.map +1 -1
  76. package/dist/message/api/getMessages.d.ts.map +1 -1
  77. package/dist/message/api/queryMessages.d.ts.map +1 -1
  78. package/dist/message/api/tests/createMessage.test.d.ts +2 -0
  79. package/dist/message/api/tests/createMessage.test.d.ts.map +1 -0
  80. package/dist/message/api/tests/deleteMessage.test.d.ts +2 -0
  81. package/dist/message/api/tests/deleteMessage.test.d.ts.map +1 -0
  82. package/dist/message/api/tests/getMessage.test.d.ts +2 -0
  83. package/dist/message/api/tests/getMessage.test.d.ts.map +1 -0
  84. package/dist/message/api/tests/getMessages.test.d.ts +2 -0
  85. package/dist/message/api/tests/getMessages.test.d.ts.map +1 -0
  86. package/dist/message/api/tests/queryMessages.test.d.ts +2 -0
  87. package/dist/message/api/tests/queryMessages.test.d.ts.map +1 -0
  88. package/dist/message/api/tests/updateMessage.test.d.ts +2 -0
  89. package/dist/message/api/tests/updateMessage.test.d.ts.map +1 -0
  90. package/dist/message/api/updateMessage.d.ts.map +1 -1
  91. package/dist/message/events/index.d.ts +1 -0
  92. package/dist/message/events/index.d.ts.map +1 -1
  93. package/dist/message/events/onMessageFetched.d.ts +17 -0
  94. package/dist/message/events/onMessageFetched.d.ts.map +1 -0
  95. package/dist/message/observers/index.d.ts +2 -1
  96. package/dist/message/observers/index.d.ts.map +1 -1
  97. package/dist/message/observers/liveMessage.d.ts +18 -0
  98. package/dist/message/observers/liveMessage.d.ts.map +1 -0
  99. package/dist/message/observers/liveMessages.d.ts.map +1 -1
  100. package/dist/message/observers/observeMessage.d.ts.map +1 -1
  101. package/dist/message/tests/observers/liveMessage.test.d.ts +2 -0
  102. package/dist/message/tests/observers/liveMessage.test.d.ts.map +1 -0
  103. package/dist/message/{observers/tests → tests/observers}/liveMessages.test.d.ts +0 -0
  104. package/dist/message/tests/observers/liveMessages.test.d.ts.map +1 -0
  105. package/dist/post/api/deletePost.d.ts.map +1 -1
  106. package/dist/post/api/getPost.d.ts.map +1 -1
  107. package/dist/post/api/updatePost.d.ts +6 -1
  108. package/dist/post/api/updatePost.d.ts.map +1 -1
  109. package/dist/reaction/api/addReaction.d.ts.map +1 -1
  110. package/dist/reaction/api/removeReaction.d.ts.map +1 -1
  111. package/dist/reaction/api/tests/addReaction.test.d.ts +2 -0
  112. package/dist/reaction/api/tests/addReaction.test.d.ts.map +1 -0
  113. package/dist/reaction/api/tests/removeReaction.test.d.ts +2 -0
  114. package/dist/reaction/api/tests/removeReaction.test.d.ts.map +1 -0
  115. package/dist/user/api/getUser.d.ts +1 -1
  116. package/dist/user/api/getUser.d.ts.map +1 -1
  117. package/dist/user/api/getUsers.d.ts.map +1 -1
  118. package/dist/user/api/queryUsers.d.ts.map +1 -1
  119. package/dist/user/api/tests/queryUsers.test.d.ts +2 -0
  120. package/dist/user/api/tests/queryUsers.test.d.ts.map +1 -0
  121. package/dist/user/api/updateUser.d.ts.map +1 -1
  122. package/dist/user/events/index.d.ts +1 -0
  123. package/dist/user/events/index.d.ts.map +1 -1
  124. package/dist/user/events/onUserFetched.d.ts +17 -0
  125. package/dist/user/events/onUserFetched.d.ts.map +1 -0
  126. package/dist/user/observers/index.d.ts +1 -0
  127. package/dist/user/observers/index.d.ts.map +1 -1
  128. package/dist/user/observers/liveUser.d.ts +18 -0
  129. package/dist/user/observers/liveUser.d.ts.map +1 -0
  130. package/dist/user/observers/observeUser.d.ts.map +1 -1
  131. package/dist/user/tests/api/getUser.test.d.ts +2 -0
  132. package/dist/user/tests/api/getUser.test.d.ts.map +1 -0
  133. package/dist/user/tests/observers/liveUser.test.d.ts +2 -0
  134. package/dist/user/tests/observers/liveUser.test.d.ts.map +1 -0
  135. package/dist/utils/constants.d.ts +3 -0
  136. package/dist/utils/constants.d.ts.map +1 -1
  137. package/dist/utils/tests/dummy/channel.d.ts +15 -0
  138. package/dist/utils/tests/dummy/channel.d.ts.map +1 -1
  139. package/dist/utils/tests/dummy/message.d.ts +12 -1
  140. package/dist/utils/tests/dummy/message.d.ts.map +1 -1
  141. package/dist/utils/tests/dummy/post.d.ts +12 -1
  142. package/dist/utils/tests/dummy/post.d.ts.map +1 -1
  143. package/dist/utils/tests/dummy/user.d.ts +10 -0
  144. package/dist/utils/tests/dummy/user.d.ts.map +1 -1
  145. package/package.json +2 -1
  146. package/src/@types/core/cache.ts +4 -0
  147. package/src/@types/core/errors.ts +1 -0
  148. package/src/@types/core/events.ts +2 -0
  149. package/src/@types/core/live.ts +10 -4
  150. package/src/@types/core/query.ts +1 -1
  151. package/src/@types/domains/channel.ts +6 -0
  152. package/src/@types/domains/message.ts +13 -10
  153. package/src/@types/domains/post.ts +10 -3
  154. package/src/@types/domains/user.ts +8 -0
  155. package/src/@types/index.ts +2 -2
  156. package/src/cache/api/backupCache.ts +0 -1
  157. package/src/cache/api/index.ts +2 -1
  158. package/src/cache/api/isInTombstone.ts +41 -0
  159. package/src/cache/api/pushToTombstone.ts +33 -0
  160. package/src/cache/api/tests/pushToCache.test.ts +57 -0
  161. package/src/cache/utils/index.ts +22 -0
  162. package/src/channel/api/addChannelMembers.ts +9 -1
  163. package/src/channel/api/getChannel.ts +20 -5
  164. package/src/channel/api/queryChannelMembers.ts +53 -5
  165. package/src/channel/api/queryChannels.ts +31 -4
  166. package/src/channel/api/tests/addChannelMembers.test.ts +95 -0
  167. package/src/channel/api/tests/getChannel.test.ts +108 -0
  168. package/src/channel/api/tests/queryChannelMembers.test.ts +115 -0
  169. package/src/channel/api/tests/queryChannels.test.ts +121 -0
  170. package/src/channel/events/index.ts +5 -0
  171. package/src/channel/events/onChannelMemberAdded.ts +1 -1
  172. package/src/channel/events/onChannelMemberBanned.ts +36 -0
  173. package/src/channel/events/onChannelMemberUnbanned.ts +36 -0
  174. package/src/channel/events/tests/onChannelMemberBanned.test.ts +58 -0
  175. package/src/channel/events/tests/onChannelMemberUnbanned.test.ts +58 -0
  176. package/src/channel/observers/liveChannelMembers.ts +28 -4
  177. package/src/channel/observers/liveChannels.ts +7 -1
  178. package/src/channel/observers/tests/liveChannelMembers.test.ts +53 -11
  179. package/src/channel/observers/tests/liveChannels.test.ts +50 -21
  180. package/src/channel/{tests/utils → utils/tests}/prepareChannelPayload.test.ts +1 -1
  181. package/src/core/events.ts +2 -1
  182. package/src/core/query/filtering.ts +49 -0
  183. package/src/core/query/query.ts +9 -6
  184. package/src/core/tests/query/query.test.ts +12 -2
  185. package/src/core/transports/http.ts +3 -17
  186. package/src/core/transports/test/http.test.ts +83 -0
  187. package/src/message/api/createMessage.ts +15 -9
  188. package/src/message/api/deleteMessage.ts +4 -4
  189. package/src/message/api/getMessage.ts +32 -4
  190. package/src/message/api/getMessages.ts +6 -1
  191. package/src/message/api/queryMessages.ts +3 -0
  192. package/src/message/api/tests/createMessage.test.ts +99 -0
  193. package/src/message/api/tests/deleteMessage.test.ts +110 -0
  194. package/src/message/api/tests/getMessage.test.ts +121 -0
  195. package/src/message/api/tests/getMessages.test.ts +108 -0
  196. package/src/message/api/tests/queryMessages.test.ts +115 -0
  197. package/src/message/api/tests/updateMessage.test.ts +100 -0
  198. package/src/message/api/updateMessage.ts +2 -0
  199. package/src/message/events/index.ts +1 -0
  200. package/src/message/events/onMessageFetched.ts +30 -0
  201. package/src/message/observers/index.ts +2 -1
  202. package/src/message/observers/liveMessage.ts +100 -0
  203. package/src/message/observers/liveMessages.ts +3 -1
  204. package/src/message/observers/observeMessage.ts +4 -0
  205. package/src/message/tests/observers/liveMessage.test.ts +248 -0
  206. package/src/message/{observers/tests → tests/observers}/liveMessages.test.ts +29 -3
  207. package/src/post/api/deletePost.ts +4 -3
  208. package/src/post/api/getPost.ts +8 -4
  209. package/src/post/api/updatePost.ts +6 -1
  210. package/src/post/tests/api/queryPosts.test.ts +117 -18
  211. package/src/reaction/api/addReaction.ts +1 -4
  212. package/src/reaction/api/removeReaction.ts +1 -4
  213. package/src/reaction/api/tests/addReaction.test.ts +37 -0
  214. package/src/reaction/api/tests/removeReaction.test.ts +43 -0
  215. package/src/user/api/getUser.ts +32 -5
  216. package/src/user/api/getUsers.ts +3 -0
  217. package/src/user/api/queryUsers.ts +27 -5
  218. package/src/user/api/tests/queryUsers.test.ts +124 -0
  219. package/src/user/api/updateUser.ts +3 -0
  220. package/src/user/events/index.ts +1 -0
  221. package/src/user/events/onUserFetched.ts +19 -0
  222. package/src/user/observers/index.ts +1 -0
  223. package/src/user/observers/liveUser.ts +89 -0
  224. package/src/user/observers/observeUser.ts +2 -0
  225. package/src/user/tests/api/getUser.test.ts +112 -0
  226. package/src/user/tests/observers/liveUser.test.ts +224 -0
  227. package/src/utils/constants.ts +5 -0
  228. package/src/utils/tests/dummy/channel.ts +43 -0
  229. package/src/utils/tests/dummy/message.ts +16 -2
  230. package/src/utils/tests/dummy/post.ts +26 -3
  231. package/src/utils/tests/dummy/user.ts +11 -0
  232. package/dist/channel/tests/utils/prepareChannelPayload.test.d.ts.map +0 -1
  233. package/dist/message/observers/tests/liveMessages.test.d.ts.map +0 -1
@@ -36,10 +36,7 @@ export const removeReaction = async (
36
36
  data: { referenceId, referenceType, reactionName },
37
37
  });
38
38
 
39
- const model = await fetchReference(referenceType, referenceId);
40
- dispatchReactable(referenceType, model.data);
41
-
42
- return !model.data.myReactions?.includes(reactionName) ?? false;
39
+ return true;
43
40
  };
44
41
 
45
42
  /**
@@ -0,0 +1,37 @@
1
+ import { disableCache, enableCache, pullFromCache, pushToCache } from '~/cache/api';
2
+ import { onMessageUpdated } from '~/message/events';
3
+ import { addReaction } from '~/reaction/api';
4
+ import { client, message11 as message, pause } from '~/utils/tests';
5
+
6
+ describe('addReaction', () => {
7
+ const cacheKey = ['message', 'get', message.messageId];
8
+
9
+ beforeEach(enableCache);
10
+
11
+ afterEach(disableCache);
12
+
13
+ test('should optimistically add a reaction', async () => {
14
+ const callback = jest.fn();
15
+ pushToCache(cacheKey, message);
16
+ onMessageUpdated(callback);
17
+
18
+ const success = await addReaction.optimistically('message', message.messageId, 'like');
19
+
20
+ expect(success).toEqual(true);
21
+ expect(pullFromCache(cacheKey)?.data).toMatchObject({
22
+ myReactions: ['like'],
23
+ reactions: { like: 1 },
24
+ reactionsCount: 1,
25
+ });
26
+ await pause();
27
+ expect(callback).toBeCalledTimes(1);
28
+ });
29
+
30
+ test('should add a reaction', async () => {
31
+ client.http.post = jest.fn().mockResolvedValue({ data: { addedId: 'reactionId' } });
32
+
33
+ const success = await addReaction('message', message.messageId, 'like');
34
+
35
+ expect(success).toEqual(true);
36
+ });
37
+ });
@@ -0,0 +1,43 @@
1
+ import { disableCache, enableCache, pullFromCache, pushToCache } from '~/cache/api';
2
+ import { onMessageUpdated } from '~/message/events';
3
+ import { removeReaction } from '~/reaction/api';
4
+ import { client, message11 as message, message11, pause } from '~/utils/tests';
5
+
6
+ describe('removeReaction', () => {
7
+ const message = {
8
+ ...message11,
9
+ myReactions: ['like'],
10
+ reactions: { like: 1 },
11
+ reactionsCount: 1,
12
+ };
13
+ const cacheKey = ['message', 'get', message.messageId];
14
+
15
+ beforeEach(enableCache);
16
+
17
+ afterEach(disableCache);
18
+
19
+ test('should optimistically add a reaction', async () => {
20
+ const callback = jest.fn();
21
+ pushToCache(cacheKey, message);
22
+ onMessageUpdated(callback);
23
+
24
+ const success = await removeReaction.optimistically('message', message.messageId, 'like');
25
+
26
+ expect(success).toEqual(true);
27
+ expect(pullFromCache(cacheKey)?.data).toMatchObject({
28
+ myReactions: [],
29
+ reactions: { like: 0 },
30
+ reactionsCount: 0,
31
+ });
32
+ await pause();
33
+ expect(callback).toBeCalledTimes(1);
34
+ });
35
+
36
+ test('should add a reaction', async () => {
37
+ client.http.delete = jest.fn().mockResolvedValue({ data: { removedId: 'reactionId' } });
38
+
39
+ const success = await removeReaction('message', message.messageId, 'like');
40
+
41
+ expect(success).toEqual(true);
42
+ });
43
+ });
@@ -2,6 +2,11 @@ import { getActiveClient } from '~/client/api';
2
2
 
3
3
  import { pullFromCache } from '~/cache/api';
4
4
  import { ingestInCache } from '~/cache/api/ingestInCache';
5
+ import { fireEvent } from '~/core/events';
6
+
7
+ import { isInTombstone } from '~/cache/api/isInTombstone';
8
+ import { checkIfShouldGoesToTombstone } from '~/cache/utils';
9
+ import { pushToTombstone } from '~/cache/api/pushToTombstone';
5
10
 
6
11
  /**
7
12
  * ```js
@@ -17,20 +22,42 @@ import { ingestInCache } from '~/cache/api/ingestInCache';
17
22
  * @category User API
18
23
  * @async
19
24
  */
20
- export const getUser = async (userId: Amity.User['userId']): Promise<Amity.Cached<Amity.User>> => {
25
+ export const getUser = async (
26
+ userId: Amity.User['userId'],
27
+ isLive = false,
28
+ ): Promise<Amity.Cached<Amity.User>> => {
21
29
  const client = getActiveClient();
22
30
  client.log('user/getUser', userId);
23
31
 
24
- // API-FIX: endpoint should not be /list, parameters should be querystring.
25
- const { data } = await client.http.get<Amity.UserPayload>(
26
- `/api/v3/users/${encodeURIComponent(userId)}`,
27
- );
32
+ if (!isLive) {
33
+ console.log('getUser will be deprecated on 25th November 2022, please use liveUser instead.');
34
+ }
35
+
36
+ isInTombstone('user', userId);
37
+
38
+ let data: Amity.UserPayload;
39
+
40
+ try {
41
+ const response = await client.http.get<Amity.UserPayload>(
42
+ `/api/v3/users/${encodeURIComponent(userId)}`,
43
+ );
44
+
45
+ data = response.data;
46
+ } catch (error) {
47
+ if (checkIfShouldGoesToTombstone((error as Amity.ErrorResponse)?.code)) {
48
+ pushToTombstone('user', userId);
49
+ }
50
+
51
+ throw error;
52
+ }
28
53
 
29
54
  const cachedAt = client.cache && Date.now();
30
55
  if (client.cache) ingestInCache(data, { cachedAt });
31
56
 
32
57
  const { users } = data;
33
58
 
59
+ fireEvent('user.fetched', data);
60
+
34
61
  return {
35
62
  data: users.find(user => user.userId === userId)!,
36
63
  cachedAt,
@@ -2,6 +2,7 @@ import { getActiveClient } from '~/client/api';
2
2
 
3
3
  import { pullFromCache } from '~/cache/api';
4
4
  import { ingestInCache } from '~/cache/api/ingestInCache';
5
+ import { fireEvent } from '~/core/events';
5
6
 
6
7
  /**
7
8
  * ```js
@@ -33,6 +34,8 @@ export const getUsers = async (
33
34
  const cachedAt = client.cache && Date.now();
34
35
  if (client.cache) ingestInCache(data, { cachedAt });
35
36
 
37
+ fireEvent('user.fetched', data);
38
+
36
39
  return {
37
40
  data: data.users,
38
41
  cachedAt,
@@ -5,6 +5,7 @@ import { toPage, toToken } from '~/core/query';
5
5
  import { pullFromCache, pushToCache } from '~/cache/api';
6
6
  import { ingestInCache } from '~/cache/api/ingestInCache';
7
7
  import { getResolver } from '~/core/model';
8
+ import { fireEvent } from '~/core/events';
8
9
 
9
10
  /**
10
11
  * ```js
@@ -32,9 +33,14 @@ export const queryUsers = async (
32
33
  const client = getActiveClient();
33
34
  client.log('user/queryUsers', query);
34
35
 
35
- const { page, displayName, filter = 'all', sortBy = 'displayName', ...params } = query;
36
+ const {
37
+ page = { limit: 10 },
38
+ displayName,
39
+ filter = 'all',
40
+ sortBy = 'displayName',
41
+ ...params
42
+ } = query;
36
43
 
37
- // API-FIX: parameters should be querystring.
38
44
  const { data } = await client.http.get<Amity.UserPayload & Amity.Pagination>(`/api/v3/users`, {
39
45
  params: {
40
46
  ...params,
@@ -56,10 +62,16 @@ export const queryUsers = async (
56
62
  if (client.cache) {
57
63
  ingestInCache(payload as Amity.UserPayload, { cachedAt });
58
64
 
59
- const cacheKey = ['user', 'query', query as Amity.Serializable];
65
+ /*
66
+ * using query as cache key over params because if the keyword, filter, sort
67
+ * change the API will NOT cache results, when it should
68
+ */
69
+ const cacheKey = ['user', 'query', { ...query, options: { ...page } } as Amity.Serializable];
60
70
  pushToCache(cacheKey, { users: users.map(getResolver('user')), paging });
61
71
  }
62
72
 
73
+ fireEvent('user.fetched', data);
74
+
63
75
  const prevPage = toPage(paging.previous);
64
76
  const nextPage = toPage(paging.next);
65
77
 
@@ -88,9 +100,19 @@ queryUsers.locally = (
88
100
 
89
101
  if (!client.cache) return;
90
102
 
91
- const queryKey = ['user', 'query', query as Amity.Serializable];
103
+ const { page = { limit: 10 } } = query ?? {};
104
+
105
+ const cacheKey = [
106
+ 'user',
107
+ 'query',
108
+ {
109
+ ...query,
110
+ options: { ...page },
111
+ } as Amity.Serializable,
112
+ ];
113
+
92
114
  const { data, cachedAt } =
93
- pullFromCache<{ users: Pick<Amity.User, 'userId'>[] } & Amity.Pagination>(queryKey) ?? {};
115
+ pullFromCache<{ users: Pick<Amity.User, 'userId'>[] } & Amity.Pagination>(cacheKey) ?? {};
94
116
 
95
117
  const users: Amity.User[] =
96
118
  data?.users
@@ -0,0 +1,124 @@
1
+ import { getResolver } from '~/core/model';
2
+
3
+ import { disableCache, enableCache, pushToCache } from '~/cache/api';
4
+ import { ingestInCache } from '~/cache/api/ingestInCache';
5
+
6
+ import { client, userQueryResponse } from '~/utils/tests';
7
+
8
+ import { queryUsers } from '../queryUsers';
9
+
10
+ describe('queryUsers', () => {
11
+ test('it should return users', async () => {
12
+ const { users: expected } = userQueryResponse.data;
13
+ client.http.get = jest.fn().mockResolvedValue(userQueryResponse);
14
+
15
+ const { data } = await queryUsers();
16
+
17
+ expect(data).toEqual(expected);
18
+ });
19
+
20
+ test('it should throw error', async () => {
21
+ client.http.get = jest.fn().mockRejectedValueOnce(new Error('error'));
22
+
23
+ await expect(queryUsers()).rejects.toThrow('error');
24
+ });
25
+
26
+ test('it should update cache upon fetching data from server', async () => {
27
+ enableCache();
28
+
29
+ client.http.get = jest.fn().mockResolvedValue(userQueryResponse);
30
+
31
+ const { data: expected } = await queryUsers();
32
+ const { data: received } = queryUsers.locally()!;
33
+
34
+ expect(received).toBeDefined();
35
+ expect(received).toEqual(expected);
36
+
37
+ disableCache();
38
+ });
39
+ });
40
+
41
+ describe('queryUsers.locally', () => {
42
+ const cacheKey = [
43
+ 'user',
44
+ 'query',
45
+ { options: { limit: 10, after: undefined } } as Amity.Serializable,
46
+ ];
47
+
48
+ test('it should fetch query locally if present in cache', () => {
49
+ enableCache();
50
+
51
+ const { data } = userQueryResponse;
52
+ const { users: expected, paging } = data;
53
+
54
+ ingestInCache(data as Amity.UserPayload);
55
+
56
+ pushToCache(cacheKey, { users: expected.map(getResolver('user')), paging });
57
+
58
+ const { data: received } = queryUsers.locally()!;
59
+
60
+ expect(received).toBeDefined();
61
+ expect(received).toEqual(expected);
62
+
63
+ disableCache();
64
+ });
65
+
66
+ test('it should return undefined if only partial data in cache', () => {
67
+ enableCache();
68
+
69
+ const { data } = userQueryResponse;
70
+ const { users, paging } = data;
71
+
72
+ // ingest incomplete data
73
+ ingestInCache({
74
+ ...data,
75
+ users: [data.users[0]],
76
+ } as Amity.UserPayload);
77
+
78
+ pushToCache(cacheKey, {
79
+ users: users.map(getResolver('user')),
80
+ paging,
81
+ });
82
+
83
+ const received = queryUsers.locally()!;
84
+
85
+ expect(received).toBeUndefined();
86
+
87
+ disableCache();
88
+ });
89
+
90
+ // added because of comment above cache key in queryUsers
91
+ test('it should return undefined if params do not match', () => {
92
+ enableCache();
93
+
94
+ const { data } = userQueryResponse;
95
+ const { users: expected, paging } = data;
96
+
97
+ ingestInCache(data as Amity.UserPayload);
98
+
99
+ pushToCache(cacheKey, { users: expected.map(getResolver('user')), paging });
100
+
101
+ const { data: received } = queryUsers.locally()!;
102
+ const received2 = queryUsers.locally({ displayName: 'test' })!;
103
+
104
+ expect(received).toBeDefined();
105
+ expect(received).toEqual(expected);
106
+ expect(received2).toBeUndefined();
107
+
108
+ disableCache();
109
+ });
110
+
111
+ test('it should return undefined if data not in cache', () => {
112
+ enableCache();
113
+
114
+ const received = queryUsers.locally();
115
+
116
+ expect(received).toBeUndefined();
117
+
118
+ disableCache();
119
+ });
120
+
121
+ test('it should return undefined if cache disabled', () => {
122
+ expect(queryUsers.locally()).toBeUndefined();
123
+ });
124
+ });
@@ -1,5 +1,6 @@
1
1
  import { getActiveClient } from '~/client/api';
2
2
 
3
+ import { fireEvent } from '~/core/events';
3
4
  import { ingestInCache } from '~/cache/api/ingestInCache';
4
5
 
5
6
  /**
@@ -45,6 +46,8 @@ export const updateUser = async (
45
46
 
46
47
  const { users } = data;
47
48
 
49
+ fireEvent('user.updated', data);
50
+
48
51
  return {
49
52
  data: users.find(user => user.userId === userId)!,
50
53
  cachedAt,
@@ -1,3 +1,4 @@
1
+ export * from './onUserFetched';
1
2
  export * from './onUserUpdated';
2
3
  export * from './onUserDeleted';
3
4
 
@@ -0,0 +1,19 @@
1
+ import { createUserEventSubscriber } from './utils';
2
+
3
+ /**
4
+ * ```js
5
+ * import { onUserFetched } from '@amityco/ts-sdk'
6
+ * const dispose = onUserFetched(user => {
7
+ * // ...
8
+ * })
9
+ * ```
10
+ *
11
+ * Fired when a {@link Amity.User} has been fetched
12
+ *
13
+ * @param callback The function to call when the event was fired
14
+ * @returns an {@link Amity.Unsubscriber} function to stop listening
15
+ *
16
+ * @category User Events
17
+ */
18
+ export const onUserFetched = (callback: Amity.Listener<Amity.User>) =>
19
+ createUserEventSubscriber('user.fetched', callback);
@@ -1 +1,2 @@
1
+ export { liveUser } from './liveUser';
1
2
  export { observeUser } from './observeUser';
@@ -0,0 +1,89 @@
1
+ import { getActiveClient } from '~/client/api';
2
+ import { createQuery, runQuery } from '~/core/query';
3
+
4
+ import { LIVE_OBJECT_ENABLE_CACHE_MESSAGE } from '~/utils/constants';
5
+
6
+ import {
7
+ onUserFetched,
8
+ onUserDeleted,
9
+ onUserUpdated,
10
+ onUserFlagCleared,
11
+ onUserFlagged,
12
+ onUserUnflagged,
13
+ } from '../events';
14
+ import { getUser } from '../api';
15
+
16
+ /**
17
+ * ```js
18
+ * import { liveUser } from '@amityco/ts-sdk'
19
+ *
20
+ * let users = []
21
+ * const unsub = liveUser(userId, response => merge(users, response.data))
22
+ * ```
23
+ *
24
+ * Observe all mutation on a given {@link Amity.User}
25
+ *
26
+ * @param userId the ID of the user to observe
27
+ * @param callback the function to call when new data are available
28
+ * @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the users
29
+ *
30
+ * @category Message Live Object
31
+ */
32
+ export const liveUser = (
33
+ userId: Amity.User['userId'],
34
+ callback: Amity.LiveObjectCallback<Amity.User>,
35
+ ): Amity.Unsubscriber => {
36
+ const { log, cache } = getActiveClient();
37
+ let user: Amity.User;
38
+
39
+ if (!cache) {
40
+ console.log(LIVE_OBJECT_ENABLE_CACHE_MESSAGE);
41
+ }
42
+
43
+ const timestamp = Date.now();
44
+ log(`liveUser(tmpid: ${timestamp}) > listen`);
45
+
46
+ const disposers: Amity.Unsubscriber[] = [];
47
+
48
+ const dispatcher = (data: Amity.LiveObject<Amity.User>) => {
49
+ // we check updatedAt to see if the current snapshot is new so
50
+ // we won't send old data back, only fresh new data
51
+ if (user && data.data?.updatedAt === user?.updatedAt && data.origin !== 'server') return;
52
+
53
+ callback(data);
54
+ user = data.data;
55
+ };
56
+
57
+ const realtimeRouter = (user: Amity.User, action: Amity.UserActionType) => {
58
+ dispatcher({ loading: false, data: user, origin: 'event' });
59
+ };
60
+
61
+ const onFetch = () => {
62
+ const query = createQuery(getUser, userId, true);
63
+
64
+ runQuery(query, ({ loading, data, error, origin }) => {
65
+ dispatcher({ loading, data, origin, error });
66
+
67
+ if (error) {
68
+ // Terminate observe functionality on error
69
+ disposers.forEach(fn => fn());
70
+ }
71
+ });
72
+ };
73
+
74
+ disposers.push(
75
+ onUserFetched(user => realtimeRouter(user, 'onFetch')),
76
+ onUserUpdated(user => realtimeRouter(user, 'onUpdate')),
77
+ onUserDeleted(user => realtimeRouter(user, 'onDelete')),
78
+ onUserFlagged(user => realtimeRouter(user, 'onFlagged')),
79
+ onUserUnflagged(user => realtimeRouter(user, 'onUnflagged')),
80
+ onUserFlagCleared(user => realtimeRouter(user, 'onFlagCleared')),
81
+ );
82
+
83
+ onFetch();
84
+
85
+ return () => {
86
+ log(`liveUser(tmpid: ${timestamp}) > dispose`);
87
+ disposers.forEach(fn => fn());
88
+ };
89
+ };
@@ -37,6 +37,8 @@ export const observeUser = <
37
37
  const timestamp = Date.now();
38
38
  log(`observeUser(tmpid: ${timestamp}) > listen`);
39
39
 
40
+ console.log('observeUser will be deprecated on 25th November 2022, please use liveUser instead.');
41
+
40
42
  // wrapper function to make sure
41
43
  const router = (result: Amity.Snapshot<Amity.User>, action: Events[number]) => {
42
44
  // filter function
@@ -0,0 +1,112 @@
1
+ import { ASCApiError } from '~/core/errors';
2
+ import { disableCache, enableCache, pushToCache } from '~/cache/api';
3
+ import { client, user11 } from '~/utils/tests';
4
+
5
+ import { pushToTombstone } from '~/cache/api/pushToTombstone';
6
+ import { isInTombstone } from '~/cache/api/isInTombstone';
7
+
8
+ import { getUser } from '../../api/getUser';
9
+
10
+ const user = user11;
11
+ const getResolvedUserValue = () => ({
12
+ data: {
13
+ users: [user],
14
+ files: [],
15
+ },
16
+ });
17
+
18
+ describe('getUser', () => {
19
+ test('should return user data', async () => {
20
+ client.http.get = jest.fn().mockResolvedValue(getResolvedUserValue());
21
+
22
+ const { data } = await getUser(user.userId);
23
+
24
+ expect(data).toStrictEqual(user);
25
+ });
26
+
27
+ test('it should update cache after fetch from server', async () => {
28
+ enableCache();
29
+ client.http.get = jest.fn().mockResolvedValue(getResolvedUserValue());
30
+ await getUser(user.userId);
31
+
32
+ expect(getUser.locally(user.userId)).toBeDefined();
33
+
34
+ disableCache();
35
+ });
36
+
37
+ test('should return an error', async () => {
38
+ client.http.get = jest.fn().mockRejectedValueOnce(new Error('error'));
39
+
40
+ await expect(getUser(user.userId)).rejects.toThrow('error');
41
+ });
42
+
43
+ test('should return an error if user in cache but api throws not found', async () => {
44
+ enableCache();
45
+ pushToCache(['user', 'get', user.userId], user, { cachedAt: Date.now() });
46
+
47
+ client.http.get = jest
48
+
49
+ .fn()
50
+ .mockRejectedValueOnce(
51
+ new ASCApiError('not found!', Amity.ServerError.ITEM_NOT_FOUND, Amity.ErrorLevel.ERROR),
52
+ );
53
+
54
+ await expect(getUser(user.userId)).rejects.toThrow();
55
+
56
+ expect(getUser.locally(user.userId)).toBeUndefined();
57
+ disableCache();
58
+ });
59
+ });
60
+
61
+ describe('getUser locally', () => {
62
+ test('should return data from cache', () => {
63
+ enableCache();
64
+
65
+ const cachedAt = Date.now();
66
+
67
+ pushToCache(['user', 'get', user.userId], user, { cachedAt });
68
+
69
+ const data = getUser.locally(user.userId);
70
+
71
+ expect(data).toBeDefined();
72
+ expect(data?.data).toEqual(user);
73
+ expect(data?.cachedAt).toEqual(cachedAt);
74
+
75
+ disableCache();
76
+ });
77
+
78
+ test('should return undefined if user not in cache', () => {
79
+ expect(getUser.locally('non-existent-user')).toBeUndefined();
80
+ });
81
+
82
+ test('should return undefined if cache not enabled', () => {
83
+ expect(getUser.locally(user.userId)).toBeUndefined();
84
+ });
85
+ });
86
+
87
+ describe('getUser tombostone', () => {
88
+ test('it should add user to tombostone if 404', async () => {
89
+ enableCache();
90
+ client.http.get = jest
91
+ .fn()
92
+ .mockRejectedValueOnce(
93
+ new ASCApiError('not found!', Amity.ServerError.ITEM_NOT_FOUND, Amity.ErrorLevel.ERROR),
94
+ );
95
+
96
+ await expect(getUser(user.userId)).rejects.toThrow();
97
+ expect(() => {
98
+ isInTombstone('user', user.userId);
99
+ }).toThrow();
100
+
101
+ disableCache();
102
+ });
103
+
104
+ test('it should throw error if user not found in tombostone', async () => {
105
+ enableCache();
106
+
107
+ pushToTombstone('user', user.userId);
108
+ await expect(getUser(user.userId)).rejects.toThrow();
109
+
110
+ disableCache();
111
+ });
112
+ });