@amityco/ts-sdk 0.0.1-beta.30 → 0.0.1-beta.31
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/dist/@types/core/cache.d.ts +3 -0
- package/dist/@types/core/cache.d.ts.map +1 -1
- package/dist/@types/core/errors.d.ts +2 -1
- package/dist/@types/core/errors.d.ts.map +1 -1
- package/dist/@types/core/events.d.ts +2 -0
- package/dist/@types/core/events.d.ts.map +1 -1
- package/dist/@types/core/live.d.ts +8 -4
- package/dist/@types/core/live.d.ts.map +1 -1
- package/dist/@types/core/query.d.ts +1 -1
- package/dist/@types/core/query.d.ts.map +1 -1
- package/dist/@types/domains/channel.d.ts +1 -0
- package/dist/@types/domains/channel.d.ts.map +1 -1
- package/dist/@types/domains/message.d.ts +13 -6
- package/dist/@types/domains/message.d.ts.map +1 -1
- package/dist/@types/domains/post.d.ts +10 -3
- package/dist/@types/domains/post.d.ts.map +1 -1
- package/dist/@types/domains/user.d.ts +1 -0
- package/dist/@types/domains/user.d.ts.map +1 -1
- package/dist/@types/index.d.ts +2 -2
- package/dist/@types/index.d.ts.map +1 -1
- package/dist/cache/api/isInTombstone.d.ts +18 -0
- package/dist/cache/api/isInTombstone.d.ts.map +1 -0
- package/dist/cache/api/pushToTombstone.d.ts +17 -0
- package/dist/cache/api/pushToTombstone.d.ts.map +1 -0
- package/dist/cache/utils/index.d.ts +17 -0
- package/dist/cache/utils/index.d.ts.map +1 -1
- package/dist/channel/api/queryChannelMembers.d.ts +1 -1
- package/dist/channel/api/queryChannelMembers.d.ts.map +1 -1
- package/dist/channel/api/queryChannels.d.ts +1 -1
- package/dist/channel/api/queryChannels.d.ts.map +1 -1
- package/dist/channel/api/tests/queryChannelMembers.test.d.ts +2 -0
- package/dist/channel/api/tests/queryChannelMembers.test.d.ts.map +1 -0
- package/dist/channel/api/tests/queryChannels.test.d.ts +2 -0
- package/dist/channel/api/tests/queryChannels.test.d.ts.map +1 -0
- package/dist/channel/events/index.d.ts +2 -0
- package/dist/channel/events/index.d.ts.map +1 -1
- package/dist/channel/events/onChannelMemberBanned.d.ts +17 -0
- package/dist/channel/events/onChannelMemberBanned.d.ts.map +1 -0
- package/dist/channel/events/onChannelMemberUnbanned.d.ts +17 -0
- package/dist/channel/events/onChannelMemberUnbanned.d.ts.map +1 -0
- package/dist/channel/events/tests/onChannelMemberBanned.test.d.ts +2 -0
- package/dist/channel/events/tests/onChannelMemberBanned.test.d.ts.map +1 -0
- package/dist/channel/events/tests/onChannelMemberUnbanned.test.d.ts +2 -0
- package/dist/channel/events/tests/onChannelMemberUnbanned.test.d.ts.map +1 -0
- package/dist/channel/observers/liveChannelMembers.d.ts.map +1 -1
- package/dist/channel/observers/liveChannels.d.ts.map +1 -1
- package/dist/channel/{tests/utils → utils/tests}/prepareChannelPayload.test.d.ts +0 -0
- package/dist/channel/utils/tests/prepareChannelPayload.test.d.ts.map +1 -0
- package/dist/core/events.d.ts +3 -3
- package/dist/core/events.d.ts.map +1 -1
- package/dist/core/query/filtering.d.ts +11 -0
- package/dist/core/query/filtering.d.ts.map +1 -1
- package/dist/core/query/query.d.ts.map +1 -1
- package/dist/index.cjs.js +963 -507
- package/dist/index.esm.js +946 -499
- package/dist/index.umd.js +4 -4
- package/dist/message/api/createMessage.d.ts +4 -1
- package/dist/message/api/createMessage.d.ts.map +1 -1
- package/dist/message/api/deleteMessage.d.ts.map +1 -1
- package/dist/message/api/getMessage.d.ts +1 -1
- package/dist/message/api/getMessage.d.ts.map +1 -1
- package/dist/message/api/getMessages.d.ts.map +1 -1
- package/dist/message/api/queryMessages.d.ts.map +1 -1
- package/dist/message/api/tests/createMessage.test.d.ts +2 -0
- package/dist/message/api/tests/createMessage.test.d.ts.map +1 -0
- package/dist/message/api/updateMessage.d.ts.map +1 -1
- package/dist/message/events/index.d.ts +1 -0
- package/dist/message/events/index.d.ts.map +1 -1
- package/dist/message/events/onMessageFetched.d.ts +17 -0
- package/dist/message/events/onMessageFetched.d.ts.map +1 -0
- package/dist/message/observers/index.d.ts +2 -1
- package/dist/message/observers/index.d.ts.map +1 -1
- package/dist/message/observers/liveMessage.d.ts +18 -0
- package/dist/message/observers/liveMessage.d.ts.map +1 -0
- package/dist/message/observers/liveMessages.d.ts.map +1 -1
- package/dist/message/observers/observeMessage.d.ts.map +1 -1
- package/dist/message/tests/api/getMessage.test.d.ts +2 -0
- package/dist/message/tests/api/getMessage.test.d.ts.map +1 -0
- package/dist/message/tests/observers/liveMessage.test.d.ts +2 -0
- package/dist/message/tests/observers/liveMessage.test.d.ts.map +1 -0
- package/dist/message/{observers/tests → tests/observers}/liveMessages.test.d.ts +0 -0
- package/dist/message/tests/observers/liveMessages.test.d.ts.map +1 -0
- package/dist/post/api/deletePost.d.ts.map +1 -1
- package/dist/post/api/getPost.d.ts.map +1 -1
- package/dist/post/api/updatePost.d.ts +6 -1
- package/dist/post/api/updatePost.d.ts.map +1 -1
- package/dist/user/api/getUser.d.ts +1 -1
- package/dist/user/api/getUser.d.ts.map +1 -1
- package/dist/user/api/getUsers.d.ts.map +1 -1
- package/dist/user/api/queryUsers.d.ts.map +1 -1
- package/dist/user/api/updateUser.d.ts.map +1 -1
- package/dist/user/events/index.d.ts +1 -0
- package/dist/user/events/index.d.ts.map +1 -1
- package/dist/user/events/onUserFetched.d.ts +17 -0
- package/dist/user/events/onUserFetched.d.ts.map +1 -0
- package/dist/user/observers/index.d.ts +1 -0
- package/dist/user/observers/index.d.ts.map +1 -1
- package/dist/user/observers/liveUser.d.ts +18 -0
- package/dist/user/observers/liveUser.d.ts.map +1 -0
- package/dist/user/observers/observeUser.d.ts.map +1 -1
- package/dist/user/tests/api/getUser.test.d.ts +2 -0
- package/dist/user/tests/api/getUser.test.d.ts.map +1 -0
- package/dist/user/tests/observers/liveUser.test.d.ts +2 -0
- package/dist/user/tests/observers/liveUser.test.d.ts.map +1 -0
- package/dist/utils/constants.d.ts +3 -0
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/tests/dummy/channel.d.ts +15 -0
- package/dist/utils/tests/dummy/channel.d.ts.map +1 -1
- package/dist/utils/tests/dummy/message.d.ts +12 -1
- package/dist/utils/tests/dummy/message.d.ts.map +1 -1
- package/dist/utils/tests/dummy/user.d.ts +10 -0
- package/dist/utils/tests/dummy/user.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/@types/core/cache.ts +4 -0
- package/src/@types/core/errors.ts +1 -0
- package/src/@types/core/events.ts +2 -0
- package/src/@types/core/live.ts +10 -4
- package/src/@types/core/query.ts +1 -1
- package/src/@types/domains/channel.ts +6 -0
- package/src/@types/domains/message.ts +13 -10
- package/src/@types/domains/post.ts +10 -3
- package/src/@types/domains/user.ts +8 -0
- package/src/@types/index.ts +2 -2
- package/src/cache/api/index.ts +2 -1
- package/src/cache/api/isInTombstone.ts +41 -0
- package/src/cache/api/pushToTombstone.ts +33 -0
- package/src/cache/utils/index.ts +22 -0
- package/src/channel/api/queryChannelMembers.ts +53 -5
- package/src/channel/api/queryChannels.ts +31 -4
- package/src/channel/api/tests/queryChannelMembers.test.ts +115 -0
- package/src/channel/api/tests/queryChannels.test.ts +101 -0
- package/src/channel/events/index.ts +5 -0
- package/src/channel/events/onChannelMemberBanned.ts +36 -0
- package/src/channel/events/onChannelMemberUnbanned.ts +36 -0
- package/src/channel/events/tests/onChannelMemberBanned.test.ts +58 -0
- package/src/channel/events/tests/onChannelMemberUnbanned.test.ts +58 -0
- package/src/channel/observers/liveChannelMembers.ts +28 -4
- package/src/channel/observers/liveChannels.ts +7 -1
- package/src/channel/observers/tests/liveChannelMembers.test.ts +53 -11
- package/src/channel/observers/tests/liveChannels.test.ts +50 -20
- package/src/channel/{tests/utils → utils/tests}/prepareChannelPayload.test.ts +1 -1
- package/src/core/events.ts +1 -0
- package/src/core/query/filtering.ts +49 -0
- package/src/core/query/query.ts +9 -6
- package/src/core/tests/query/query.test.ts +12 -2
- package/src/message/api/createMessage.ts +15 -9
- package/src/message/api/deleteMessage.ts +2 -1
- package/src/message/api/getMessage.ts +32 -4
- package/src/message/api/getMessages.ts +6 -1
- package/src/message/api/queryMessages.ts +3 -0
- package/src/message/api/tests/createMessage.test.ts +76 -0
- package/src/message/api/updateMessage.ts +2 -0
- package/src/message/events/index.ts +1 -0
- package/src/message/events/onMessageFetched.ts +30 -0
- package/src/message/observers/index.ts +2 -1
- package/src/message/observers/liveMessage.ts +100 -0
- package/src/message/observers/liveMessages.ts +3 -1
- package/src/message/observers/observeMessage.ts +4 -0
- package/src/message/tests/api/getMessage.test.ts +96 -0
- package/src/message/tests/observers/liveMessage.test.ts +248 -0
- package/src/message/{observers/tests → tests/observers}/liveMessages.test.ts +29 -3
- package/src/post/api/deletePost.ts +4 -3
- package/src/post/api/getPost.ts +8 -4
- package/src/post/api/updatePost.ts +6 -1
- package/src/user/api/getUser.ts +34 -6
- package/src/user/api/getUsers.ts +3 -0
- package/src/user/api/queryUsers.ts +3 -0
- package/src/user/api/updateUser.ts +3 -0
- package/src/user/events/index.ts +1 -0
- package/src/user/events/onUserFetched.ts +19 -0
- package/src/user/observers/index.ts +1 -0
- package/src/user/observers/liveUser.ts +89 -0
- package/src/user/observers/observeUser.ts +2 -0
- package/src/user/tests/api/getUser.test.ts +112 -0
- package/src/user/tests/observers/liveUser.test.ts +224 -0
- package/src/utils/constants.ts +5 -0
- package/src/utils/tests/dummy/channel.ts +43 -0
- package/src/utils/tests/dummy/message.ts +16 -2
- package/src/utils/tests/dummy/user.ts +11 -0
- package/dist/channel/tests/utils/prepareChannelPayload.test.d.ts.map +0 -1
- package/dist/message/observers/tests/liveMessages.test.d.ts.map +0 -1
package/src/@types/index.ts
CHANGED
|
@@ -21,10 +21,8 @@ import './domains/reaction';
|
|
|
21
21
|
|
|
22
22
|
import './domains/group';
|
|
23
23
|
import './domains/channel';
|
|
24
|
-
import './domains/message';
|
|
25
24
|
|
|
26
25
|
import './domains/category';
|
|
27
|
-
import './domains/post';
|
|
28
26
|
import './domains/comment';
|
|
29
27
|
|
|
30
28
|
import './domains/stream';
|
|
@@ -36,3 +34,5 @@ import './domains/poll';
|
|
|
36
34
|
export * from './domains/community';
|
|
37
35
|
export * from './domains/content';
|
|
38
36
|
export * from './domains/file';
|
|
37
|
+
export * from './domains/message';
|
|
38
|
+
export * from './domains/post';
|
package/src/cache/api/index.ts
CHANGED
|
@@ -18,6 +18,7 @@ export * from './upsertInCache';
|
|
|
18
18
|
export * from './dropFromCache';
|
|
19
19
|
|
|
20
20
|
/*
|
|
21
|
-
* ingestInCache and syncInCache
|
|
21
|
+
* ingestInCache and syncInCache and isInTombstone
|
|
22
|
+
* and pushToTombstone are not exported ;
|
|
22
23
|
* we don't want it as part of the sdk's public api.
|
|
23
24
|
*/
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { getActiveClient } from '~/client/api/activeClient';
|
|
2
|
+
|
|
3
|
+
import { ASCApiError } from '~/core/errors';
|
|
4
|
+
import { pullFromCache } from '~/cache/api';
|
|
5
|
+
import { isFresh, queryOptions } from '~/core/query';
|
|
6
|
+
import { CACHE_KEY_TOMBSTONE, CACHE_LIFESPAN_TOMBSTONE } from '~/cache/utils';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* ```js
|
|
10
|
+
* import { isInTombstone } from '@amityco/ts-sdk'
|
|
11
|
+
* const user = isInTombstone(["message", "messageId"])
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* Checks if the {@link Amity.TombstoneCacheOptions} exists
|
|
15
|
+
* in cache and it's not expired means it's in tombstone
|
|
16
|
+
* and we throw an Error
|
|
17
|
+
*
|
|
18
|
+
* @param model the model to check
|
|
19
|
+
* @param modelId the object id to check
|
|
20
|
+
* @returns the matching cache entry, or undefined.
|
|
21
|
+
*
|
|
22
|
+
* @category Cache API
|
|
23
|
+
*/
|
|
24
|
+
export const isInTombstone = (model: Amity.Domain, modelId: string) => {
|
|
25
|
+
const { log, cache } = getActiveClient();
|
|
26
|
+
const key = [model, CACHE_KEY_TOMBSTONE, modelId];
|
|
27
|
+
|
|
28
|
+
if (!cache) return;
|
|
29
|
+
log('cache/api/isInTombstone', key);
|
|
30
|
+
|
|
31
|
+
const isInTombstone = pullFromCache<Amity.TombstoneCacheOptions>(key);
|
|
32
|
+
const { lifeSpan } = queryOptions('cache_then_server', CACHE_LIFESPAN_TOMBSTONE);
|
|
33
|
+
|
|
34
|
+
if (isInTombstone && isFresh(isInTombstone.data, lifeSpan)) {
|
|
35
|
+
throw new ASCApiError(
|
|
36
|
+
'Item not found!',
|
|
37
|
+
Amity.ServerError.ITEM_NOT_FOUND,
|
|
38
|
+
Amity.ErrorLevel.ERROR,
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { getActiveClient } from '~/client/api/activeClient';
|
|
2
|
+
|
|
3
|
+
import { dropFromCache, pushToCache } from '~/cache/api';
|
|
4
|
+
|
|
5
|
+
import { CACHE_KEY_GET, CACHE_KEY_TOMBSTONE } from '~/cache/utils';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* ```js
|
|
9
|
+
* import { pushToTombstone } from '@amityco/ts-sdk'
|
|
10
|
+
* pushToTombstone(["message", "messageId"], { cachedAt: Date.now() })
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* Removes an existing {@link Amity.CacheEntry} from the {@link Amity.Client}'s
|
|
14
|
+
* {@link Amity.Cache} from a given {@link Amity.CacheKey}
|
|
15
|
+
*
|
|
16
|
+
* @param model the model to remove and push to Tombstone
|
|
17
|
+
* @param modelId the related model id
|
|
18
|
+
*
|
|
19
|
+
* @category Cache API
|
|
20
|
+
* @hidden
|
|
21
|
+
*/
|
|
22
|
+
export const pushToTombstone = (model: Amity.Domain, modelId: string) => {
|
|
23
|
+
const { log, cache } = getActiveClient();
|
|
24
|
+
|
|
25
|
+
const cacheKey = [model, CACHE_KEY_TOMBSTONE, modelId];
|
|
26
|
+
const data: Pick<Amity.CacheOptions, 'cachedAt'> = { cachedAt: Date.now() };
|
|
27
|
+
|
|
28
|
+
if (!cache) return;
|
|
29
|
+
log('cache/api/pushToTombstone', { cacheKey, data });
|
|
30
|
+
|
|
31
|
+
dropFromCache([model, CACHE_KEY_GET, modelId], true);
|
|
32
|
+
pushToCache(cacheKey, data);
|
|
33
|
+
};
|
package/src/cache/utils/index.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
// cache constants
|
|
2
|
+
export const CACHE_KEY_GET = 'get';
|
|
3
|
+
export const CACHE_KEY_QUERY = 'query';
|
|
4
|
+
export const CACHE_KEY_TOMBSTONE = 'dead';
|
|
5
|
+
export const CACHE_KEY_COLLECTION = 'collection';
|
|
6
|
+
|
|
7
|
+
export const CACHE_LIFESPAN = 1 * 60 * 1000; // 1 minute
|
|
8
|
+
export const CACHE_LIFESPAN_TOMBSTONE = 3 * 60 * 1000; // 3 minutes
|
|
9
|
+
|
|
1
10
|
/**
|
|
2
11
|
* Shallow-clones an object and sort its keys
|
|
3
12
|
*
|
|
@@ -72,3 +81,16 @@ export const partialMatch = (predicate: any, candidate: any): boolean => {
|
|
|
72
81
|
*/
|
|
73
82
|
export const isCached = <T extends unknown>(obj?: any): obj is Amity.Cached<T> =>
|
|
74
83
|
['data', 'cachedAt'].every(prop => obj?.hasOwnProperty(prop));
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* checks if passed error code is included in
|
|
87
|
+
* Tombstone errors list
|
|
88
|
+
*
|
|
89
|
+
* @param errorCode as {@link Amity.ServerError}
|
|
90
|
+
* @returns success boolean if the given errorCode
|
|
91
|
+
* is included in Tombstone errors list
|
|
92
|
+
*
|
|
93
|
+
* @category Cache
|
|
94
|
+
*/
|
|
95
|
+
export const checkIfShouldGoesToTombstone = (errorCode: Amity.ServerError): boolean =>
|
|
96
|
+
[Amity.ServerError.ITEM_NOT_FOUND, Amity.ServerError.FORBIDDEN].includes(errorCode);
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { getActiveClient } from '~/client/api';
|
|
2
2
|
|
|
3
3
|
import { toPage, toToken } from '~/core/query';
|
|
4
|
+
import { getResolver } from '~/core/model';
|
|
5
|
+
|
|
6
|
+
import { pullFromCache, pushToCache } from '~/cache/api';
|
|
4
7
|
import { ingestInCache } from '~/cache/api/ingestInCache';
|
|
5
8
|
|
|
6
9
|
import { prepareChannelPayload } from '../utils';
|
|
@@ -25,11 +28,11 @@ export const queryChannelMembers = async (
|
|
|
25
28
|
const client = getActiveClient();
|
|
26
29
|
client.log('channel/queryChannelMembers', query);
|
|
27
30
|
|
|
28
|
-
const { page
|
|
31
|
+
const { page = { limit: 10 }, ...params } = query ?? {};
|
|
29
32
|
|
|
30
33
|
const { data: queryPayload } = await client.http.get<
|
|
31
34
|
Amity.ChannelMembershipPayload & Amity.Pagination
|
|
32
|
-
>(`/api/v4/channels/${encodeURIComponent(channelId)}/users`, {
|
|
35
|
+
>(`/api/v4/channels/${encodeURIComponent(params.channelId)}/users`, {
|
|
33
36
|
params: {
|
|
34
37
|
...params,
|
|
35
38
|
options: {
|
|
@@ -47,6 +50,19 @@ export const queryChannelMembers = async (
|
|
|
47
50
|
const cachedAt = client.cache && Date.now();
|
|
48
51
|
if (client.cache) {
|
|
49
52
|
ingestInCache(data as Amity.ChannelMembershipPayload);
|
|
53
|
+
|
|
54
|
+
const cacheKey = [
|
|
55
|
+
'channelUsers',
|
|
56
|
+
'query',
|
|
57
|
+
{ ...params, options: { ...page } } as Amity.Serializable,
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
pushToCache(cacheKey, {
|
|
61
|
+
channelUsers: channelUsers.map(({ channelId, userId }) =>
|
|
62
|
+
getResolver('channelUsers')({ channelId, userId }),
|
|
63
|
+
),
|
|
64
|
+
paging,
|
|
65
|
+
});
|
|
50
66
|
}
|
|
51
67
|
|
|
52
68
|
const nextPage = toPage(paging.next);
|
|
@@ -68,9 +84,41 @@ export const queryChannelMembers = async (
|
|
|
68
84
|
*
|
|
69
85
|
* @category Post API
|
|
70
86
|
*/
|
|
71
|
-
queryChannelMembers.locally = (
|
|
87
|
+
queryChannelMembers.locally = (
|
|
88
|
+
query: Parameters<typeof queryChannelMembers>[0],
|
|
89
|
+
): Amity.Cached<Amity.Paged<Amity.Membership<'channel'>>> | undefined => {
|
|
72
90
|
const client = getActiveClient();
|
|
73
|
-
client.log('channel/queryChannelMembers', query);
|
|
91
|
+
client.log('channel/queryChannelMembers.locally', query);
|
|
92
|
+
|
|
93
|
+
if (!client.cache) return;
|
|
94
|
+
|
|
95
|
+
const { page = { limit: 10 }, ...params } = query;
|
|
96
|
+
|
|
97
|
+
const cacheKey = [
|
|
98
|
+
'channelUsers',
|
|
99
|
+
'query',
|
|
100
|
+
{ ...params, options: { ...page } } as Amity.Serializable,
|
|
101
|
+
];
|
|
102
|
+
|
|
103
|
+
const { data, cachedAt } =
|
|
104
|
+
pullFromCache<
|
|
105
|
+
{
|
|
106
|
+
channelUsers: Pick<Amity.Membership<'channel'>, 'channelId' | 'userId'>[];
|
|
107
|
+
} & Amity.Pagination
|
|
108
|
+
>(cacheKey) ?? {};
|
|
109
|
+
|
|
110
|
+
if (!data?.channelUsers.length) return;
|
|
111
|
+
|
|
112
|
+
const channelUsers: Amity.Membership<'channel'>[] = data.channelUsers
|
|
113
|
+
// ck -> cacheKey, abbrevated to avoid confusion from the above cache key
|
|
114
|
+
.map(ck => pullFromCache<Amity.Membership<'channel'>>(['channelUsers', 'get', ck])!)
|
|
115
|
+
.filter(Boolean)
|
|
116
|
+
.map(({ data }) => data);
|
|
117
|
+
|
|
118
|
+
const prevPage = toPage(data?.paging.previous);
|
|
119
|
+
const nextPage = toPage(data?.paging.next);
|
|
74
120
|
|
|
75
|
-
|
|
121
|
+
return channelUsers.length === data?.channelUsers?.length
|
|
122
|
+
? { data: channelUsers, cachedAt, prevPage, nextPage }
|
|
123
|
+
: undefined;
|
|
76
124
|
};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { getActiveClient } from '~/client/api';
|
|
2
2
|
|
|
3
3
|
import { toPage, toToken } from '~/core/query';
|
|
4
|
-
import { pushToCache } from '~/cache/api';
|
|
5
|
-
import { ingestInCache } from '~/cache/api/ingestInCache';
|
|
6
4
|
import { getResolver } from '~/core/model';
|
|
7
5
|
|
|
6
|
+
import { pushToCache, pullFromCache } from '~/cache/api';
|
|
7
|
+
import { ingestInCache } from '~/cache/api/ingestInCache';
|
|
8
|
+
|
|
8
9
|
import { prepareChannelPayload } from '../utils';
|
|
9
10
|
|
|
10
11
|
/**
|
|
@@ -86,9 +87,35 @@ export const queryChannels = async (
|
|
|
86
87
|
*
|
|
87
88
|
* @category Channel API
|
|
88
89
|
*/
|
|
89
|
-
queryChannels.locally = (
|
|
90
|
+
queryChannels.locally = (
|
|
91
|
+
query?: Parameters<typeof queryChannels>[0],
|
|
92
|
+
): Amity.Cached<Amity.Paged<Amity.Channel>> | undefined => {
|
|
90
93
|
const client = getActiveClient();
|
|
91
94
|
client.log('channel/queryChannels.locally', query);
|
|
92
95
|
|
|
93
|
-
|
|
96
|
+
const { page = { limit: 10 }, ...params } = query ?? {};
|
|
97
|
+
|
|
98
|
+
const cacheKey = ['channel', 'query', { ...params, options: { ...page } } as Amity.Serializable];
|
|
99
|
+
|
|
100
|
+
const { data, cachedAt } =
|
|
101
|
+
pullFromCache<
|
|
102
|
+
{
|
|
103
|
+
channels: Pick<Amity.Channel, 'channelId'>[];
|
|
104
|
+
} & Amity.Pagination
|
|
105
|
+
>(cacheKey) ?? {};
|
|
106
|
+
|
|
107
|
+
if (!data?.channels.length) return;
|
|
108
|
+
|
|
109
|
+
const channels: Amity.Channel[] = data.channels
|
|
110
|
+
// ck -> cacheKey, abbrevated to avoid confusion from the above cache key
|
|
111
|
+
.map(ck => pullFromCache<Amity.Channel>(['channel', 'get', ck])!)
|
|
112
|
+
.filter(Boolean)
|
|
113
|
+
.map(({ data }) => data);
|
|
114
|
+
|
|
115
|
+
const prevPage = toPage(data?.paging.previous);
|
|
116
|
+
const nextPage = toPage(data?.paging.next);
|
|
117
|
+
|
|
118
|
+
return channels.length === data?.channels?.length
|
|
119
|
+
? { data: channels, cachedAt, prevPage, nextPage }
|
|
120
|
+
: undefined;
|
|
94
121
|
};
|
|
@@ -0,0 +1,115 @@
|
|
|
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 { prepareChannelPayload } from '~/channel/utils';
|
|
7
|
+
import { client, channelUserQueryResponse } from '~/utils/tests';
|
|
8
|
+
|
|
9
|
+
import { queryChannelMembers } from '../queryChannelMembers';
|
|
10
|
+
|
|
11
|
+
describe('queryChannelMembers', () => {
|
|
12
|
+
const { channelId } = channelUserQueryResponse.data.channels[0];
|
|
13
|
+
|
|
14
|
+
test('it should return channel members', async () => {
|
|
15
|
+
const expected = channelUserQueryResponse.data.channelUsers;
|
|
16
|
+
client.http.get = jest.fn().mockResolvedValue(channelUserQueryResponse);
|
|
17
|
+
|
|
18
|
+
const { data } = await queryChannelMembers({ channelId });
|
|
19
|
+
|
|
20
|
+
expect(data).toEqual(expected);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('it should throw error', async () => {
|
|
24
|
+
client.http.get = jest.fn().mockRejectedValueOnce(new Error('error'));
|
|
25
|
+
|
|
26
|
+
await expect(queryChannelMembers({ channelId })).rejects.toThrow('error');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('it should update cache upon fetching data from server', async () => {
|
|
30
|
+
enableCache();
|
|
31
|
+
client.http.get = jest.fn().mockResolvedValue(channelUserQueryResponse);
|
|
32
|
+
|
|
33
|
+
const { data: got } = await queryChannelMembers({ channelId });
|
|
34
|
+
const { data } = queryChannelMembers.locally({ channelId })!;
|
|
35
|
+
|
|
36
|
+
expect(data).toBeDefined();
|
|
37
|
+
expect(got).toEqual(data);
|
|
38
|
+
|
|
39
|
+
disableCache();
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('queryChannelMembers.locally', () => {
|
|
44
|
+
const { channelId } = channelUserQueryResponse.data.channels[0];
|
|
45
|
+
const cacheKey = [
|
|
46
|
+
'channelUsers',
|
|
47
|
+
'query',
|
|
48
|
+
{ channelId, options: { limit: 10, after: undefined } },
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
test('it should fetch query locally if present in cache', () => {
|
|
52
|
+
enableCache();
|
|
53
|
+
|
|
54
|
+
const { paging, ...payload } = channelUserQueryResponse.data;
|
|
55
|
+
const data = prepareChannelPayload(payload);
|
|
56
|
+
const { channelUsers } = data;
|
|
57
|
+
|
|
58
|
+
ingestInCache(data as Amity.ChannelMembershipPayload);
|
|
59
|
+
|
|
60
|
+
pushToCache(cacheKey, {
|
|
61
|
+
channelUsers: channelUsers.map(({ channelId, userId }) =>
|
|
62
|
+
getResolver('channelUsers')({ channelId, userId }),
|
|
63
|
+
),
|
|
64
|
+
paging,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const { data: got } = queryChannelMembers.locally({ channelId })!;
|
|
68
|
+
|
|
69
|
+
expect(got).toBeDefined();
|
|
70
|
+
expect(got).toEqual(channelUsers);
|
|
71
|
+
|
|
72
|
+
disableCache();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('it should return undefined if only partial data in cache', () => {
|
|
76
|
+
enableCache();
|
|
77
|
+
|
|
78
|
+
const { paging, ...payload } = channelUserQueryResponse.data;
|
|
79
|
+
const data = prepareChannelPayload(payload);
|
|
80
|
+
const { channelUsers } = data;
|
|
81
|
+
|
|
82
|
+
// ingest incomplete data
|
|
83
|
+
ingestInCache({
|
|
84
|
+
...data,
|
|
85
|
+
channelUsers: [data.channelUsers[0]],
|
|
86
|
+
} as Amity.ChannelMembershipPayload);
|
|
87
|
+
|
|
88
|
+
pushToCache(cacheKey, {
|
|
89
|
+
channelUsers: channelUsers.map(({ channelId, userId }) =>
|
|
90
|
+
getResolver('channelUsers')({ channelId, userId }),
|
|
91
|
+
),
|
|
92
|
+
paging,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const got = queryChannelMembers.locally({ channelId })!;
|
|
96
|
+
|
|
97
|
+
expect(got).toBeUndefined();
|
|
98
|
+
|
|
99
|
+
disableCache();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('it should return undefined if data not in cache', () => {
|
|
103
|
+
enableCache();
|
|
104
|
+
|
|
105
|
+
const got = queryChannelMembers.locally({ channelId });
|
|
106
|
+
|
|
107
|
+
expect(got).toBeUndefined();
|
|
108
|
+
|
|
109
|
+
disableCache();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('it should return undefined if cache disabled', () => {
|
|
113
|
+
expect(queryChannelMembers.locally({ channelId })).toBeUndefined();
|
|
114
|
+
});
|
|
115
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
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 { prepareChannelPayload } from '~/channel/utils';
|
|
7
|
+
import { client, channelQueryResponse } from '~/utils/tests';
|
|
8
|
+
|
|
9
|
+
import { queryChannels } from '../queryChannels';
|
|
10
|
+
|
|
11
|
+
describe('queryChannels', () => {
|
|
12
|
+
test('it should return channels', async () => {
|
|
13
|
+
const expected = prepareChannelPayload(channelQueryResponse.data).channels;
|
|
14
|
+
client.http.get = jest.fn().mockResolvedValue(channelQueryResponse);
|
|
15
|
+
|
|
16
|
+
const { data } = await queryChannels();
|
|
17
|
+
|
|
18
|
+
expect(data).toEqual(expected);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('it should throw error', async () => {
|
|
22
|
+
client.http.get = jest.fn().mockRejectedValueOnce(new Error('error'));
|
|
23
|
+
|
|
24
|
+
await expect(queryChannels()).rejects.toThrow('error');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('it should update cache upon fetching data from server', async () => {
|
|
28
|
+
enableCache();
|
|
29
|
+
client.http.get = jest.fn().mockResolvedValue(channelQueryResponse);
|
|
30
|
+
|
|
31
|
+
const { data: got } = await queryChannels();
|
|
32
|
+
const { data } = queryChannels.locally()!;
|
|
33
|
+
|
|
34
|
+
expect(data).toBeDefined();
|
|
35
|
+
expect(got).toEqual(data);
|
|
36
|
+
|
|
37
|
+
disableCache();
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('queryChannels.locally', () => {
|
|
42
|
+
const cacheKey = ['channel', 'query', { options: { limit: 10, after: undefined } }];
|
|
43
|
+
|
|
44
|
+
test('it should fetch query locally if present in cache', () => {
|
|
45
|
+
enableCache();
|
|
46
|
+
|
|
47
|
+
const { paging, ...payload } = channelQueryResponse.data;
|
|
48
|
+
const data = prepareChannelPayload(payload);
|
|
49
|
+
const { channels } = data;
|
|
50
|
+
|
|
51
|
+
ingestInCache(data as Amity.ChannelMembershipPayload);
|
|
52
|
+
|
|
53
|
+
pushToCache(cacheKey, { channels: channels.map(getResolver('channel')), paging });
|
|
54
|
+
|
|
55
|
+
const { data: got } = queryChannels.locally()!;
|
|
56
|
+
|
|
57
|
+
expect(got).toBeDefined();
|
|
58
|
+
expect(got).toEqual(channels);
|
|
59
|
+
|
|
60
|
+
disableCache();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('it should return undefined if only partial data in cache', () => {
|
|
64
|
+
enableCache();
|
|
65
|
+
|
|
66
|
+
const { paging, ...payload } = channelQueryResponse.data;
|
|
67
|
+
const data = prepareChannelPayload(payload);
|
|
68
|
+
const { channels } = data;
|
|
69
|
+
|
|
70
|
+
// ingest incomplete data
|
|
71
|
+
ingestInCache({
|
|
72
|
+
...data,
|
|
73
|
+
channels: [data.channels[0]],
|
|
74
|
+
} as Amity.ChannelPayload);
|
|
75
|
+
|
|
76
|
+
pushToCache(cacheKey, {
|
|
77
|
+
channels: channels.map(getResolver('channel')),
|
|
78
|
+
paging,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const got = queryChannels.locally()!;
|
|
82
|
+
|
|
83
|
+
expect(got).toBeUndefined();
|
|
84
|
+
|
|
85
|
+
disableCache();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('it should return undefined if data not in cache', () => {
|
|
89
|
+
enableCache();
|
|
90
|
+
|
|
91
|
+
const got = queryChannels.locally();
|
|
92
|
+
|
|
93
|
+
expect(got).toBeUndefined();
|
|
94
|
+
|
|
95
|
+
disableCache();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('it should return undefined if cache disabled', () => {
|
|
99
|
+
expect(queryChannels.locally()).toBeUndefined();
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
export * from './onChannelCreated';
|
|
2
2
|
export * from './onChannelUpdated';
|
|
3
3
|
export * from './onChannelDeleted';
|
|
4
|
+
|
|
4
5
|
export * from './onChannelJoined';
|
|
5
6
|
export * from './onChannelLeft';
|
|
7
|
+
|
|
6
8
|
export * from './onChannelMemberAdded';
|
|
7
9
|
export * from './onChannelMemberRemoved';
|
|
10
|
+
|
|
11
|
+
export * from './onChannelMemberBanned';
|
|
12
|
+
export * from './onChannelMemberUnbanned';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { getActiveClient } from '~/client/api';
|
|
2
|
+
import { createEventSubscriber } from '~/core/events';
|
|
3
|
+
|
|
4
|
+
import { ingestInCache } from '~/cache/api/ingestInCache';
|
|
5
|
+
|
|
6
|
+
import { prepareChannelPayload } from '../utils';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* ```js
|
|
10
|
+
* import { onChannelMemberBanned } from '@amityco/ts-sdk'
|
|
11
|
+
* const dispose = onChannelMemberBanned((channel, member) => {
|
|
12
|
+
* // ...
|
|
13
|
+
* })
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* Fired when any {@link Amity.User} has been banned from any {@link Amity.Channel}
|
|
17
|
+
*
|
|
18
|
+
* @param callback The function to call when the event was fired
|
|
19
|
+
* @returns an {@link Amity.Unsubscriber} function to stop listening
|
|
20
|
+
*
|
|
21
|
+
* @category Channel Events
|
|
22
|
+
*/
|
|
23
|
+
export const onChannelMemberBanned = (
|
|
24
|
+
callback: (channel: Amity.Channel, member: Amity.Membership<'channel'>) => void,
|
|
25
|
+
) => {
|
|
26
|
+
const client = getActiveClient();
|
|
27
|
+
|
|
28
|
+
const filter = (payload: Amity.ChannelMembershipPayload) => {
|
|
29
|
+
const data = prepareChannelPayload(payload);
|
|
30
|
+
|
|
31
|
+
ingestInCache(data);
|
|
32
|
+
callback(data.channels[0], data.channelUsers.find(cu => cu.membership === 'banned')!);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return createEventSubscriber(client, 'onChannelMemberBanned', 'v3.channel.didBan', filter);
|
|
36
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { getActiveClient } from '~/client/api';
|
|
2
|
+
import { createEventSubscriber } from '~/core/events';
|
|
3
|
+
|
|
4
|
+
import { ingestInCache } from '~/cache/api/ingestInCache';
|
|
5
|
+
|
|
6
|
+
import { prepareChannelPayload } from '../utils';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* ```js
|
|
10
|
+
* import { onChannelMemberUnbanned } from '@amityco/ts-sdk'
|
|
11
|
+
* const dispose = onChannelMemberUnbanned((channel, member) => {
|
|
12
|
+
* // ...
|
|
13
|
+
* })
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* Fired when any {@link Amity.User} has been unbanned from any {@link Amity.Channel}
|
|
17
|
+
*
|
|
18
|
+
* @param callback The function to call when the event was fired
|
|
19
|
+
* @returns an {@link Amity.Unsubscriber} function to stop listening
|
|
20
|
+
*
|
|
21
|
+
* @category Channel Events
|
|
22
|
+
*/
|
|
23
|
+
export const onChannelMemberUnbanned = (
|
|
24
|
+
callback: (channel: Amity.Channel, member: Amity.Membership<'channel'>) => void,
|
|
25
|
+
) => {
|
|
26
|
+
const client = getActiveClient();
|
|
27
|
+
|
|
28
|
+
const filter = (payload: Amity.ChannelMembershipPayload) => {
|
|
29
|
+
const data = prepareChannelPayload(payload);
|
|
30
|
+
|
|
31
|
+
ingestInCache(data);
|
|
32
|
+
callback(data.channels[0], data.channelUsers.find(cu => cu.membership === 'none')!);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return createEventSubscriber(client, 'onChannelMemberBanned', 'v3.channel.didUnban', filter);
|
|
36
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { disableCache, enableCache } from '~/cache/api';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
client,
|
|
5
|
+
connectClient,
|
|
6
|
+
disconnectClient,
|
|
7
|
+
channel11,
|
|
8
|
+
channelUser1,
|
|
9
|
+
channelUser5,
|
|
10
|
+
} from '~/utils/tests';
|
|
11
|
+
|
|
12
|
+
import { onChannelMemberBanned } from '../onChannelMemberBanned';
|
|
13
|
+
|
|
14
|
+
describe('onChannelMemberBanned event', () => {
|
|
15
|
+
beforeAll(async () => {
|
|
16
|
+
await connectClient();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterAll(async () => {
|
|
20
|
+
await disconnectClient();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
beforeEach(async () => {
|
|
24
|
+
enableCache();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
afterEach(disableCache);
|
|
28
|
+
|
|
29
|
+
const channelPayload: Amity.ChannelMembershipPayload = {
|
|
30
|
+
channelUsers: [channelUser1, channelUser5],
|
|
31
|
+
channels: [channel11],
|
|
32
|
+
files: [],
|
|
33
|
+
users: [],
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
test('it should call callback with channel and banned member', () => {
|
|
37
|
+
const callback = jest.fn();
|
|
38
|
+
|
|
39
|
+
const unsub = onChannelMemberBanned(callback);
|
|
40
|
+
client.emitter.emit('v3.channel.didBan', channelPayload);
|
|
41
|
+
|
|
42
|
+
unsub();
|
|
43
|
+
|
|
44
|
+
expect(callback).toHaveBeenCalled();
|
|
45
|
+
expect(callback).toHaveBeenCalledWith(channel11, channelUser5);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('it should return an unsubscriber', () => {
|
|
49
|
+
const callback = jest.fn();
|
|
50
|
+
|
|
51
|
+
const unsub = onChannelMemberBanned(callback);
|
|
52
|
+
unsub();
|
|
53
|
+
|
|
54
|
+
client.emitter.emit('v3.channel.didBan', channelPayload);
|
|
55
|
+
|
|
56
|
+
expect(callback).not.toHaveBeenCalled();
|
|
57
|
+
});
|
|
58
|
+
});
|