Skip to content

Commit 3bbd9fd

Browse files
authored
Add Message Actions API support (#174)
* feat: add message actions Add Message Actions API support which allow to: add, remove and fetch previously added actions. feat: modify fetch messages endpoint to fetch message actions Add new arguments to fetch messages function which allow to fetch previously added actions and message metadata. feat: add new event handler for message actions Add new handler which can be used to track message actions addition / removal events.
1 parent 4c4ade4 commit 3bbd9fd

File tree

19 files changed

+1437
-316
lines changed

19 files changed

+1437
-316
lines changed

.pubnub.yml

Lines changed: 291 additions & 283 deletions
Large diffs are not rendered by default.

src/core/components/endpoint.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ export default function(modules, endpoint, ...args) {
138138
let callInstance;
139139
let networkingParams = { url,
140140
operation: endpoint.getOperation(),
141-
timeout: endpoint.getRequestTimeout(modules)
141+
timeout: endpoint.getRequestTimeout(modules),
142+
headers: endpoint.getRequestHeaders ? endpoint.getRequestHeaders() : {}
142143
};
143144

144145
outgoingParams.uuid = config.UUID;

src/core/components/listener_manager.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
MessageAnnouncement,
44
StatusAnnouncement,
55
SignalAnnouncement,
6+
MessageActionAnnouncement,
67
ObjectAnnouncement,
78
CallbackStruct,
89
PresenceAnnouncement,
@@ -58,6 +59,12 @@ export default class {
5859
});
5960
}
6061

62+
announceMessageAction(announce: MessageActionAnnouncement) {
63+
this._listeners.forEach((listener) => {
64+
if (listener.messageAction) listener.messageAction(announce);
65+
});
66+
}
67+
6168
announceUser(announce: ObjectAnnouncement) {
6269
this._listeners.forEach((listener) => {
6370
if (listener.user) listener.user(announce);

src/core/components/subscription_manager.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import ReconnectionManager from '../components/reconnection_manager';
66
import DedupingManager from '../components/deduping_manager';
77
import utils from '../utils';
88
import {
9+
MessageActionAnnouncement,
910
MessageAnnouncement,
1011
SignalAnnouncement,
1112
ObjectAnnouncement,
@@ -673,6 +674,25 @@ export default class {
673674
} else if (message.payload.type === 'membership') {
674675
this._listenerManager.announceMembership(announce);
675676
}
677+
} else if (message.messageType === 3) {
678+
// this is a message action
679+
let announce: MessageActionAnnouncement = {};
680+
announce.channel = channel;
681+
announce.subscription = subscriptionMatch;
682+
announce.timetoken = publishMetaData.publishTimetoken;
683+
announce.publisher = message.issuingClientId;
684+
685+
announce.data = {
686+
messageTimetoken: message.payload.data.messageTimetoken,
687+
actionTimetoken: message.payload.data.actionTimetoken,
688+
type: message.payload.data.type,
689+
uuid: message.issuingClientId,
690+
value: message.payload.data.value,
691+
};
692+
693+
announce.event = message.payload.event;
694+
695+
this._listenerManager.announceMessageAction(announce);
676696
} else {
677697
let announce: MessageAnnouncement = {};
678698
announce.channel = null;

src/core/constants/operations.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ export default {
1313
PNPublishOperation: 'PNPublishOperation',
1414
PNSignalOperation: 'PNSignalOperation',
1515

16+
// Actions API
17+
PNAddMessageActionOperation: 'PNAddActionOperation',
18+
PNRemoveMessageActionOperation: 'PNRemoveMessageActionOperation',
19+
PNGetMessageActionsOperation: 'PNGetMessageActionsOperation',
20+
1621
// Objects API
1722

1823
PNCreateUserOperation: 'PNCreateUserOperation',
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/* @flow */
2+
3+
import {
4+
AddMessageActionInput,
5+
AddMessageActionResponse,
6+
ModulesInject,
7+
} from '../../flow_interfaces';
8+
import operationConstants from '../../constants/operations';
9+
10+
11+
export function getOperation(): string {
12+
return operationConstants.PNAddMessageActionOperation;
13+
}
14+
15+
export function validateParams(
16+
{ config }: ModulesInject,
17+
incomingParams: AddMessageActionInput
18+
) {
19+
let { action, channel, messageTimetoken } = incomingParams;
20+
21+
if (!messageTimetoken) return 'Missing message timetoken';
22+
if (!config.subscribeKey) return 'Missing Subscribe Key';
23+
if (!channel) return 'Missing message channel';
24+
if (!action) return 'Missing Action';
25+
if (!action.value) return 'Missing Action.value';
26+
if (!action.type) return 'Missing Action.type';
27+
if (action.type.length > 15) return 'Action.type value exceed maximum length of 15';
28+
}
29+
30+
export function usePost() {
31+
return true;
32+
}
33+
34+
export function postURL(
35+
{ config }: ModulesInject,
36+
incomingParams: AddMessageActionInput
37+
): string {
38+
let { channel, messageTimetoken } = incomingParams;
39+
40+
return `/v1/message-actions/${config.subscribeKey}/channel/${channel}/message/${messageTimetoken}`;
41+
}
42+
43+
export function getRequestTimeout({ config }: ModulesInject) {
44+
return config.getTransactionTimeout();
45+
}
46+
47+
export function getRequestHeaders(): Object {
48+
return { 'Content-Type': 'application/json' };
49+
}
50+
51+
export function isAuthSupported() {
52+
return true;
53+
}
54+
55+
export function prepareParams(): Object {
56+
return {};
57+
}
58+
59+
export function postPayload(
60+
modules: ModulesInject,
61+
incomingParams: AddMessageActionInput
62+
): string {
63+
return incomingParams.action;
64+
}
65+
66+
export function handleResponse(
67+
modules: ModulesInject,
68+
addMessageActionResponse: Object
69+
): AddMessageActionResponse {
70+
return { data: addMessageActionResponse.data };
71+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/* @flow */
2+
3+
import {
4+
GetMessageActionsInput,
5+
GetMessageActionsResponse,
6+
ModulesInject,
7+
} from '../../flow_interfaces';
8+
import operationConstants from '../../constants/operations';
9+
10+
11+
export function getOperation(): string {
12+
return operationConstants.PNGetMessageActionsOperation;
13+
}
14+
15+
export function validateParams(
16+
{ config }: ModulesInject,
17+
incomingParams: GetMessageActionsInput
18+
) {
19+
let { channel } = incomingParams;
20+
21+
if (!config.subscribeKey) return 'Missing Subscribe Key';
22+
if (!channel) return 'Missing message channel';
23+
}
24+
25+
26+
export function getURL(
27+
{ config }: ModulesInject,
28+
incomingParams: GetMessageActionsInput
29+
): string {
30+
let { channel } = incomingParams;
31+
32+
return `/v1/message-actions/${config.subscribeKey}/channel/${channel}`;
33+
}
34+
35+
export function getRequestTimeout({ config }: ModulesInject) {
36+
return config.getTransactionTimeout();
37+
}
38+
39+
export function isAuthSupported() {
40+
return true;
41+
}
42+
43+
export function prepareParams(
44+
modules: ModulesInject,
45+
incomingParams: GetMessageActionsInput
46+
): Object {
47+
const { limit, start, end } = incomingParams;
48+
let outgoingParams: Object = {};
49+
50+
if (limit) outgoingParams.limit = limit;
51+
if (start) outgoingParams.start = start;
52+
if (end) outgoingParams.end = end;
53+
54+
return outgoingParams;
55+
}
56+
57+
export function handleResponse(
58+
modules: ModulesInject,
59+
getMessageActionsResponse: Object
60+
): GetMessageActionsResponse {
61+
/** @type GetMessageActionsResponse */
62+
let response = { data: getMessageActionsResponse.data };
63+
64+
if (response.data.length) {
65+
response.end = response.data[response.data.length - 1].actionTimetoken;
66+
response.start = response.data[0].actionTimetoken;
67+
}
68+
69+
return response;
70+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/* @flow */
2+
3+
import {
4+
RemoveMessageActionInput,
5+
RemoveMessageActionResponse,
6+
ModulesInject,
7+
} from '../../flow_interfaces';
8+
import operationConstants from '../../constants/operations';
9+
10+
11+
export function getOperation(): string {
12+
return operationConstants.PNRemoveMessageActionOperation;
13+
}
14+
15+
export function validateParams(
16+
{ config }: ModulesInject,
17+
incomingParams: RemoveMessageActionInput
18+
) {
19+
let { channel, actionTimetoken, messageTimetoken } = incomingParams;
20+
21+
if (!messageTimetoken) return 'Missing message timetoken';
22+
if (!actionTimetoken) return 'Missing action timetoken';
23+
if (!config.subscribeKey) return 'Missing Subscribe Key';
24+
if (!channel) return 'Missing message channel';
25+
}
26+
27+
export function useDelete() {
28+
return true;
29+
}
30+
31+
export function getURL(
32+
{ config }: ModulesInject,
33+
incomingParams: RemoveMessageActionInput
34+
): string {
35+
let { channel, actionTimetoken, messageTimetoken } = incomingParams;
36+
37+
return `/v1/message-actions/${config.subscribeKey}/channel/${channel}/message/${messageTimetoken}/action/${actionTimetoken}`;
38+
}
39+
40+
export function getRequestTimeout({ config }: ModulesInject) {
41+
return config.getTransactionTimeout();
42+
}
43+
44+
export function isAuthSupported() {
45+
return true;
46+
}
47+
48+
export function prepareParams(): Object {
49+
return {};
50+
}
51+
52+
export function handleResponse(
53+
modules: ModulesInject,
54+
removeMessageActionResponse: Object
55+
): RemoveMessageActionResponse {
56+
return { data: removeMessageActionResponse.data };
57+
}

src/core/endpoints/fetch_messages.js

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,22 +29,27 @@ export function validateParams(
2929
modules: ModulesInject,
3030
incomingParams: FetchMessagesArguments
3131
) {
32-
let { channels } = incomingParams;
32+
let { channels, includeMessageActions = false } = incomingParams;
3333
let { config } = modules;
3434

3535
if (!channels || channels.length === 0) return 'Missing channels';
3636
if (!config.subscribeKey) return 'Missing Subscribe Key';
37+
38+
if (includeMessageActions && channels.length > 1) {
39+
throw new TypeError('History can return actions data for a single channel only. Either pass a single channel or disable the includeMessageActions flag.');
40+
}
3741
}
3842

3943
export function getURL(
4044
modules: ModulesInject,
4145
incomingParams: FetchMessagesArguments
4246
): string {
43-
let { channels = [] } = incomingParams;
47+
let { channels = [], includeMessageActions = false } = incomingParams;
4448
let { config } = modules;
49+
const endpoint = !includeMessageActions ? 'history' : 'history-with-actions';
4550

4651
let stringifiedChannels = channels.length > 0 ? channels.join(',') : ',';
47-
return `/v3/history/sub-key/${
52+
return `/v3/${endpoint}/sub-key/${
4853
config.subscribeKey
4954
}/channel/${utils.encodeString(stringifiedChannels)}`;
5055
}
@@ -61,13 +66,20 @@ export function prepareParams(
6166
modules: ModulesInject,
6267
incomingParams: FetchMessagesArguments
6368
): Object {
64-
const { start, end, count, stringifiedTimeToken = false } = incomingParams;
69+
const {
70+
start,
71+
end,
72+
count,
73+
stringifiedTimeToken = false,
74+
includeMeta = false,
75+
} = incomingParams;
6576
let outgoingParams: Object = {};
6677

6778
if (count) outgoingParams.max = count;
6879
if (start) outgoingParams.start = start;
6980
if (end) outgoingParams.end = end;
7081
if (stringifiedTimeToken) outgoingParams.string_message_token = 'true';
82+
if (includeMeta) outgoingParams.include_meta = 'true';
7183

7284
return outgoingParams;
7385
}
@@ -89,6 +101,14 @@ export function handleResponse(
89101
announce.subscription = null;
90102
announce.timetoken = messageEnvelope.timetoken;
91103
announce.message = __processMessage(modules, messageEnvelope.message);
104+
105+
if (messageEnvelope.actions) {
106+
announce.data = messageEnvelope.actions;
107+
}
108+
if (messageEnvelope.meta) {
109+
announce.meta = messageEnvelope.meta;
110+
}
111+
92112
response.channels[channelName].push(announce);
93113
});
94114
});

src/core/endpoints/history/get_history.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ export function prepareParams(
6464
reverse,
6565
count = 100,
6666
stringifiedTimeToken = false,
67+
includeMeta = false,
6768
} = incomingParams;
6869
let outgoingParams: Object = {
6970
include_token: 'true',
@@ -74,6 +75,7 @@ export function prepareParams(
7475
if (end) outgoingParams.end = end;
7576
if (stringifiedTimeToken) outgoingParams.string_message_token = 'true';
7677
if (reverse != null) outgoingParams.reverse = reverse.toString();
78+
if (includeMeta) outgoingParams.include_meta = 'true';
7779

7880
return outgoingParams;
7981
}
@@ -95,6 +97,10 @@ export function handleResponse(
9597
entry: __processMessage(modules, serverHistoryItem.message),
9698
};
9799

100+
if (serverHistoryItem.meta) {
101+
item.meta = serverHistoryItem.meta;
102+
}
103+
98104
response.messages.push(item);
99105
});
100106
}

0 commit comments

Comments
 (0)