@trycourier/cli 2.7.1 → 3.1.4
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/bin/.gitkeep +0 -0
- package/install.js +115 -0
- package/package.json +22 -87
- package/LICENSE +0 -21
- package/README.md +0 -105
- package/dist/bulk.d.ts +0 -15
- package/dist/bulk.js +0 -53
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js +0 -24
- package/dist/commands/AudienceSearch.d.ts +0 -3
- package/dist/commands/AudienceSearch.js +0 -113
- package/dist/commands/AutomationInvokeBulk.d.ts +0 -3
- package/dist/commands/AutomationInvokeBulk.js +0 -139
- package/dist/commands/Config.d.ts +0 -4
- package/dist/commands/Config.js +0 -45
- package/dist/commands/DigestFlush.d.ts +0 -5
- package/dist/commands/DigestFlush.js +0 -28
- package/dist/commands/Help.d.ts +0 -16
- package/dist/commands/Help.js +0 -38
- package/dist/commands/Inbox/ArchiveAll.d.ts +0 -8
- package/dist/commands/Inbox/ArchiveAll.js +0 -180
- package/dist/commands/Inbox/ArchiveAllBulk.d.ts +0 -5
- package/dist/commands/Inbox/ArchiveAllBulk.js +0 -99
- package/dist/commands/Inbox/MarkAllRead.d.ts +0 -5
- package/dist/commands/Inbox/MarkAllRead.js +0 -110
- package/dist/commands/MessagesSearch.d.ts +0 -3
- package/dist/commands/MessagesSearch.js +0 -120
- package/dist/commands/NotYetImplemented.d.ts +0 -3
- package/dist/commands/NotYetImplemented.js +0 -5
- package/dist/commands/Send.d.ts +0 -5
- package/dist/commands/Send.js +0 -144
- package/dist/commands/Templates/List.d.ts +0 -3
- package/dist/commands/Templates/List.js +0 -114
- package/dist/commands/TenantsBulk.d.ts +0 -3
- package/dist/commands/TenantsBulk.js +0 -171
- package/dist/commands/TenantsGetMembership.d.ts +0 -3
- package/dist/commands/TenantsGetMembership.js +0 -127
- package/dist/commands/TenantsMembershipBulk.d.ts +0 -3
- package/dist/commands/TenantsMembershipBulk.js +0 -203
- package/dist/commands/Track.d.ts +0 -5
- package/dist/commands/Track.js +0 -40
- package/dist/commands/TrackBulk.d.ts +0 -3
- package/dist/commands/TrackBulk.js +0 -89
- package/dist/commands/TranslationsDownload.d.ts +0 -10
- package/dist/commands/TranslationsDownload.js +0 -30
- package/dist/commands/TranslationsUpload.d.ts +0 -9
- package/dist/commands/TranslationsUpload.js +0 -37
- package/dist/commands/Upgrade.d.ts +0 -3
- package/dist/commands/Upgrade.js +0 -38
- package/dist/commands/UserToken.d.ts +0 -3
- package/dist/commands/UserToken.js +0 -78
- package/dist/commands/UsersBulk.d.ts +0 -3
- package/dist/commands/UsersBulk.js +0 -205
- package/dist/commands/UsersGet.d.ts +0 -5
- package/dist/commands/UsersGet.js +0 -37
- package/dist/commands/UsersPreferences.d.ts +0 -3
- package/dist/commands/UsersPreferences.js +0 -73
- package/dist/commands/UsersSet.d.ts +0 -5
- package/dist/commands/UsersSet.js +0 -34
- package/dist/commands/UsersTokensBulk.d.ts +0 -3
- package/dist/commands/UsersTokensBulk.js +0 -215
- package/dist/commands/WhoAmI.d.ts +0 -3
- package/dist/commands/WhoAmI.js +0 -27
- package/dist/components/Context.d.ts +0 -55
- package/dist/components/Context.js +0 -94
- package/dist/components/Elemental.d.ts +0 -13
- package/dist/components/Elemental.js +0 -23
- package/dist/components/KVP.d.ts +0 -11
- package/dist/components/KVP.js +0 -12
- package/dist/components/Request.d.ts +0 -23
- package/dist/components/Request.js +0 -17
- package/dist/components/Response.d.ts +0 -16
- package/dist/components/Response.js +0 -29
- package/dist/components/Router.d.ts +0 -4
- package/dist/components/Router.js +0 -60
- package/dist/components/SdkResponse.d.ts +0 -9
- package/dist/components/SdkResponse.js +0 -21
- package/dist/components/Spinner.d.ts +0 -6
- package/dist/components/Spinner.js +0 -41
- package/dist/components/Table.d.ts +0 -19
- package/dist/components/Table.js +0 -62
- package/dist/components/UhOh.d.ts +0 -5
- package/dist/components/UhOh.js +0 -10
- package/dist/components/Version.d.ts +0 -3
- package/dist/components/Version.js +0 -53
- package/dist/constants.d.ts +0 -8
- package/dist/constants.js +0 -8
- package/dist/lib/api.d.ts +0 -17
- package/dist/lib/api.js +0 -39
- package/dist/lib/args.d.ts +0 -2
- package/dist/lib/args.js +0 -13
- package/dist/lib/courier.d.ts +0 -3
- package/dist/lib/courier.js +0 -7
- package/dist/lib/delay.d.ts +0 -2
- package/dist/lib/delay.js +0 -7
- package/dist/lib/load-env.d.ts +0 -2
- package/dist/lib/load-env.js +0 -16
- package/dist/lib/uuid.d.ts +0 -2
- package/dist/lib/uuid.js +0 -4
- package/dist/mappings.d.ts +0 -6
- package/dist/mappings.js +0 -559
- package/dist/version.d.ts +0 -2
- package/dist/version.js +0 -2
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import { Inbox } from '@trycourier/client-graphql';
|
|
2
|
-
import { Box, Text } from 'ink';
|
|
3
|
-
import React, { useEffect, useState } from 'react';
|
|
4
|
-
import { useBoolean } from 'usehooks-ts';
|
|
5
|
-
import { useCliContext } from '../../components/Context.js';
|
|
6
|
-
import Spinner from '../../components/Spinner.js';
|
|
7
|
-
import UhOh from '../../components/UhOh.js';
|
|
8
|
-
import uuid from '../../lib/uuid.js';
|
|
9
|
-
const MarkAllRead = ({}) => {
|
|
10
|
-
const [error, setError] = useState();
|
|
11
|
-
const [jwt, setJwt] = useState('');
|
|
12
|
-
const [unread, setUnread] = useState();
|
|
13
|
-
const running = useBoolean(true);
|
|
14
|
-
const { getJWT, parsedParams } = useCliContext();
|
|
15
|
-
const [response, setResponse] = useState();
|
|
16
|
-
const { tenant, tag, _: [user_id, ...args], } = parsedParams;
|
|
17
|
-
const handleError = (text) => {
|
|
18
|
-
setError(text + '\n' + JSON.stringify({ tenant, tag, user_id, args }));
|
|
19
|
-
};
|
|
20
|
-
useEffect(() => {
|
|
21
|
-
runJwt();
|
|
22
|
-
}, [user_id]);
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
if (jwt?.length) {
|
|
25
|
-
runMarkRead();
|
|
26
|
-
}
|
|
27
|
-
}, [jwt]);
|
|
28
|
-
useEffect(() => {
|
|
29
|
-
if (error?.length) {
|
|
30
|
-
running.setFalse();
|
|
31
|
-
}
|
|
32
|
-
else if (response) {
|
|
33
|
-
running.setFalse();
|
|
34
|
-
}
|
|
35
|
-
}, [error, response]);
|
|
36
|
-
const runJwt = async () => {
|
|
37
|
-
try {
|
|
38
|
-
if (user_id) {
|
|
39
|
-
const jwt = await getJWT(user_id, [
|
|
40
|
-
'inbox:read:messages',
|
|
41
|
-
'inbox:write:events',
|
|
42
|
-
]);
|
|
43
|
-
if (jwt.token) {
|
|
44
|
-
setJwt(jwt.token);
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
handleError('No JWT token returned.');
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
handleError('No user ID provided.');
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
catch (e) {
|
|
55
|
-
handleError(e instanceof Error ? e.message : String(e) || 'An error occurred.');
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
const runMarkRead = async () => {
|
|
59
|
-
if (jwt) {
|
|
60
|
-
const gql_client = Inbox({
|
|
61
|
-
authorization: jwt,
|
|
62
|
-
userId: user_id,
|
|
63
|
-
clientSourceId: uuid(),
|
|
64
|
-
});
|
|
65
|
-
try {
|
|
66
|
-
const res = await gql_client.getInboxCount({
|
|
67
|
-
status: 'unread',
|
|
68
|
-
});
|
|
69
|
-
if (res) {
|
|
70
|
-
if (typeof res.count === 'number') {
|
|
71
|
-
setUnread(res?.count || 0);
|
|
72
|
-
const res2 = await gql_client.markAllRead();
|
|
73
|
-
if (res2) {
|
|
74
|
-
setResponse(res2);
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
handleError('No response from markAllRead');
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
handleError('No count returned from getInboxCount');
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
handleError('No response from getInboxCount');
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
catch (e) {
|
|
89
|
-
handleError(e instanceof Error ? e.message : String(e) || 'An error occurred.');
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
handleError('No JWT generated, but GQL client called.');
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
if (error?.length) {
|
|
97
|
-
return React.createElement(UhOh, { text: error });
|
|
98
|
-
}
|
|
99
|
-
else if (running.value) {
|
|
100
|
-
return React.createElement(Spinner, { text: "Marking all read" });
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
return (React.createElement(Box, null,
|
|
104
|
-
React.createElement(Text, null,
|
|
105
|
-
"Marked ",
|
|
106
|
-
unread,
|
|
107
|
-
" messages as read")));
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
export default MarkAllRead;
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import { Alert } from '@inkjs/ui';
|
|
2
|
-
import { stringify } from 'csv-stringify/sync';
|
|
3
|
-
import fs from 'fs/promises';
|
|
4
|
-
import _ from 'lodash';
|
|
5
|
-
import { DateTime } from 'luxon';
|
|
6
|
-
import React, { useEffect, useState } from 'react';
|
|
7
|
-
import { useBoolean, useCounter } from 'usehooks-ts';
|
|
8
|
-
import { useCliContext } from '../components/Context.js';
|
|
9
|
-
import Spinner from '../components/Spinner.js';
|
|
10
|
-
import UhOh from '../components/UhOh.js';
|
|
11
|
-
const FILENAME = 'messages';
|
|
12
|
-
const MessagesSearch = () => {
|
|
13
|
-
const { parsedParams, courier } = useCliContext();
|
|
14
|
-
const processing = useBoolean(true);
|
|
15
|
-
const running = useBoolean(true);
|
|
16
|
-
const counter = useCounter(0);
|
|
17
|
-
const [messages, setMessages] = useState([]);
|
|
18
|
-
const [error, setError] = useState();
|
|
19
|
-
const { user, from, tag, status, enqueued_after, maxPages, json, csv, webhook, filename, template, tenant, } = parsedParams;
|
|
20
|
-
let searchParams = {};
|
|
21
|
-
if (user)
|
|
22
|
-
searchParams['recipient'] = String(user);
|
|
23
|
-
if (enqueued_after || from)
|
|
24
|
-
searchParams['enqueued_after'] = enqueued_after ?? from;
|
|
25
|
-
if (tag)
|
|
26
|
-
searchParams['tag'] = tag;
|
|
27
|
-
if (status)
|
|
28
|
-
searchParams['status'] = status;
|
|
29
|
-
if (template)
|
|
30
|
-
searchParams['notification'] = template;
|
|
31
|
-
if (tenant)
|
|
32
|
-
searchParams['tenant_id'] = template;
|
|
33
|
-
const out_file = (filename?.length
|
|
34
|
-
? filename.substring(0, filename.includes('.') ? filename.lastIndexOf('.') : filename.length)
|
|
35
|
-
: FILENAME) + (csv ? '.csv' : '.json');
|
|
36
|
-
const MAX_PAGES = Number(maxPages) || 10;
|
|
37
|
-
useEffect(() => {
|
|
38
|
-
if (!processing.value) {
|
|
39
|
-
if (json || csv || webhook?.length) {
|
|
40
|
-
runExport();
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
running.setFalse();
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}, [processing.value]);
|
|
47
|
-
useEffect(() => {
|
|
48
|
-
getMessages();
|
|
49
|
-
}, []);
|
|
50
|
-
const getMessages = async (cursor, count = 0) => {
|
|
51
|
-
counter.increment();
|
|
52
|
-
const r = await courier.messages.list({
|
|
53
|
-
...searchParams,
|
|
54
|
-
cursor,
|
|
55
|
-
});
|
|
56
|
-
setMessages(p => [...p, ...r.results]);
|
|
57
|
-
if (r.paging.more && count < MAX_PAGES) {
|
|
58
|
-
await getMessages(r.paging.cursor, count + 1);
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
processing.setFalse();
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
const runExport = async () => {
|
|
65
|
-
const flat = flattenData(messages);
|
|
66
|
-
if (csv) {
|
|
67
|
-
await fs.writeFile(out_file, stringify(flat, { header: true }));
|
|
68
|
-
}
|
|
69
|
-
else if (json) {
|
|
70
|
-
await fs.writeFile(out_file, JSON.stringify(flat, null, 2), {
|
|
71
|
-
encoding: 'utf-8',
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
if (webhook?.length) {
|
|
75
|
-
try {
|
|
76
|
-
await fetch(webhook, {
|
|
77
|
-
method: 'POST',
|
|
78
|
-
headers: {
|
|
79
|
-
'Content-Type': 'application/json',
|
|
80
|
-
},
|
|
81
|
-
body: JSON.stringify(flat),
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
catch (e) {
|
|
85
|
-
setError(e instanceof Error ? e.message : String(e));
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
running.setFalse();
|
|
89
|
-
};
|
|
90
|
-
if (error?.length) {
|
|
91
|
-
return React.createElement(UhOh, { text: error });
|
|
92
|
-
}
|
|
93
|
-
else if (running.value) {
|
|
94
|
-
return React.createElement(Spinner, { text: `Fetching messages - page ${counter.count}` });
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
return (React.createElement(React.Fragment, null,
|
|
98
|
-
React.createElement(Alert, { variant: "success", title: `Finished ${counter.count} pages` }, csv || json
|
|
99
|
-
? `Output ${messages.length} messages to ${out_file}`
|
|
100
|
-
: JSON.stringify(messages, null, 2))));
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
const flattenData = (data) => {
|
|
104
|
-
return data.map(row => {
|
|
105
|
-
return Object.keys(row).reduce((p, key) => {
|
|
106
|
-
const v = _.get(row, [key]);
|
|
107
|
-
if (typeof v === 'number') {
|
|
108
|
-
p[key] = DateTime.fromMillis(v, { zone: 'utc' }).toISO();
|
|
109
|
-
}
|
|
110
|
-
else if (typeof v === 'object') {
|
|
111
|
-
p[key] = JSON.stringify(v);
|
|
112
|
-
}
|
|
113
|
-
else if (v) {
|
|
114
|
-
p[key] = v;
|
|
115
|
-
}
|
|
116
|
-
return p;
|
|
117
|
-
}, {});
|
|
118
|
-
});
|
|
119
|
-
};
|
|
120
|
-
export default MessagesSearch;
|
package/dist/commands/Send.d.ts
DELETED
package/dist/commands/Send.js
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useState } from 'react';
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import UhOh from '../components/UhOh.js';
|
|
4
|
-
import Request from '../components/Request.js';
|
|
5
|
-
import Response from '../components/Response.js';
|
|
6
|
-
import Elemental from '../components/Elemental.js';
|
|
7
|
-
import api from '../lib/api.js';
|
|
8
|
-
import { Box, Text } from 'ink';
|
|
9
|
-
import { useCliContext } from '../components/Context.js';
|
|
10
|
-
import lodash from 'lodash';
|
|
11
|
-
const constructPayload = (params) => {
|
|
12
|
-
const to = [];
|
|
13
|
-
const { _, user, list, audience, email, tel, apn, fcm, title, body, message, template, channel, channels, all, elemental, mock, tenant, 'include-children': includeChildren, 'tenant-context': tenantContext, includeChildren: _includeChildren, tenantContext: _tenantContext, ...data } = params;
|
|
14
|
-
if (user) {
|
|
15
|
-
to.push({ user_id: String(user) });
|
|
16
|
-
}
|
|
17
|
-
if (list) {
|
|
18
|
-
to.push({ list_id: list });
|
|
19
|
-
}
|
|
20
|
-
if (audience) {
|
|
21
|
-
to.push({ audience_id: audience });
|
|
22
|
-
}
|
|
23
|
-
if (tenant) {
|
|
24
|
-
to.push({ tenant_id: tenant });
|
|
25
|
-
if (includeChildren) {
|
|
26
|
-
to.push({ include_children: true });
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
if (email) {
|
|
30
|
-
to.push({ email: email });
|
|
31
|
-
}
|
|
32
|
-
if (tel) {
|
|
33
|
-
to.push({ phone_number: tel });
|
|
34
|
-
}
|
|
35
|
-
if (apn) {
|
|
36
|
-
to.push({ apn: { token: apn } });
|
|
37
|
-
}
|
|
38
|
-
if (fcm) {
|
|
39
|
-
to.push({ firebaseToken: fcm });
|
|
40
|
-
}
|
|
41
|
-
if (to.length && tenantContext) {
|
|
42
|
-
to.forEach((_t, i) => {
|
|
43
|
-
lodash.set(to, [i, 'context', 'tenant_id'], tenantContext);
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
let contentElemental = {
|
|
47
|
-
title: undefined,
|
|
48
|
-
body: undefined,
|
|
49
|
-
};
|
|
50
|
-
if (title) {
|
|
51
|
-
contentElemental.title = title;
|
|
52
|
-
}
|
|
53
|
-
if (body) {
|
|
54
|
-
contentElemental.body = body;
|
|
55
|
-
}
|
|
56
|
-
else if (message) {
|
|
57
|
-
contentElemental.body = message;
|
|
58
|
-
}
|
|
59
|
-
let routing = {
|
|
60
|
-
channels: [],
|
|
61
|
-
method: params.all ? 'all' : 'single',
|
|
62
|
-
};
|
|
63
|
-
if (channel) {
|
|
64
|
-
routing.channels = channel.split(',');
|
|
65
|
-
}
|
|
66
|
-
else if (channels) {
|
|
67
|
-
routing.channels = channels.split(',');
|
|
68
|
-
}
|
|
69
|
-
if (email && email.length) {
|
|
70
|
-
routing.channels.push('email');
|
|
71
|
-
}
|
|
72
|
-
if (tel && tel.length) {
|
|
73
|
-
routing.channels.push('sms');
|
|
74
|
-
}
|
|
75
|
-
if ((apn && apn.length) || (fcm && fcm.length)) {
|
|
76
|
-
routing.channels.push('push');
|
|
77
|
-
}
|
|
78
|
-
if (template) {
|
|
79
|
-
return {
|
|
80
|
-
type: 'template',
|
|
81
|
-
message: {
|
|
82
|
-
to: to.length === 1 ? to[0] : to,
|
|
83
|
-
template,
|
|
84
|
-
routing: routing.channels.length ? routing : undefined,
|
|
85
|
-
data: data ? data : undefined,
|
|
86
|
-
},
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
return {
|
|
91
|
-
type: 'elemental',
|
|
92
|
-
message: {
|
|
93
|
-
to: to.length === 1 ? to[0] : to,
|
|
94
|
-
content: contentElemental,
|
|
95
|
-
routing: routing.channels.length ? routing : undefined,
|
|
96
|
-
data: data ? data : undefined,
|
|
97
|
-
},
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
export default ({ params }) => {
|
|
102
|
-
const { apikey, url } = useCliContext();
|
|
103
|
-
const [resp, setResp] = useState();
|
|
104
|
-
if (!params.body && !params.template && !params.elemental) {
|
|
105
|
-
return (React.createElement(UhOh, { text: "You must specify a message body, template, or Elemental file path." }));
|
|
106
|
-
}
|
|
107
|
-
let payload = constructPayload(params);
|
|
108
|
-
if (Array.isArray(payload.message.to) && !payload.message.to.length) {
|
|
109
|
-
return React.createElement(UhOh, { text: "You must specify a recipient." });
|
|
110
|
-
}
|
|
111
|
-
if (payload.type === 'elemental' &&
|
|
112
|
-
!params.elemental &&
|
|
113
|
-
(!payload.message.content.body || !payload.message.content.body?.length)) {
|
|
114
|
-
throw new Error('You must specify a body for the message.');
|
|
115
|
-
}
|
|
116
|
-
if (params.elemental && payload.type === 'elemental') {
|
|
117
|
-
if (!fs.existsSync(params.elemental)) {
|
|
118
|
-
throw new Error('Invalid file path to Elemental document.');
|
|
119
|
-
}
|
|
120
|
-
payload.message.content = JSON.parse(fs.readFileSync(params.elemental, 'utf8'));
|
|
121
|
-
}
|
|
122
|
-
const request = {
|
|
123
|
-
method: 'POST',
|
|
124
|
-
url: '/send',
|
|
125
|
-
body: {
|
|
126
|
-
message: payload.message,
|
|
127
|
-
},
|
|
128
|
-
};
|
|
129
|
-
useEffect(() => {
|
|
130
|
-
if (params.mock) {
|
|
131
|
-
setResp({ res: { status: 999, statusText: 'MOCKED' } });
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
api(request, url, apikey).then(res => setResp(res));
|
|
135
|
-
}, []);
|
|
136
|
-
return (React.createElement(Box, { flexDirection: "column" },
|
|
137
|
-
params.elemental ? (React.createElement(React.Fragment, null,
|
|
138
|
-
React.createElement(Box, { borderStyle: "bold", borderColor: "white" },
|
|
139
|
-
React.createElement(Text, null, " Elemental")),
|
|
140
|
-
React.createElement(Box, null, payload.type === 'elemental' ? (React.createElement(Elemental, { elemental: payload.message.content })) : null))) : null,
|
|
141
|
-
React.createElement(React.Fragment, null,
|
|
142
|
-
React.createElement(Request, { request: request, response: resp }),
|
|
143
|
-
React.createElement(Response, { response: resp }))));
|
|
144
|
-
};
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useState } from 'react';
|
|
2
|
-
import { useCliContext } from '../../components/Context.js';
|
|
3
|
-
import Table from '../../components/Table.js';
|
|
4
|
-
import { useBoolean } from 'usehooks-ts';
|
|
5
|
-
import Spinner from '../../components/Spinner.js';
|
|
6
|
-
import { Text } from 'ink';
|
|
7
|
-
import _ from 'lodash';
|
|
8
|
-
import { DateTime } from 'luxon';
|
|
9
|
-
import fs from 'fs/promises';
|
|
10
|
-
import { stringify } from 'csv-stringify/sync';
|
|
11
|
-
import UhOh from '../../components/UhOh.js';
|
|
12
|
-
const MAX_PAGES = 100;
|
|
13
|
-
const FILENAME = 'templates';
|
|
14
|
-
const TemplatesList = () => {
|
|
15
|
-
const [templates, setTemplates] = useState([]);
|
|
16
|
-
const { courier, parsedParams } = useCliContext();
|
|
17
|
-
const running = useBoolean(true);
|
|
18
|
-
const processing = useBoolean(true);
|
|
19
|
-
const [error, setError] = useState();
|
|
20
|
-
const { csv, filename, json, webhook } = parsedParams;
|
|
21
|
-
const out_file = filename || FILENAME + (csv ? '.csv' : '.json');
|
|
22
|
-
useEffect(() => {
|
|
23
|
-
getTemplates();
|
|
24
|
-
}, []);
|
|
25
|
-
const getTemplates = async () => {
|
|
26
|
-
let cursor;
|
|
27
|
-
let page = -1;
|
|
28
|
-
while (page < MAX_PAGES) {
|
|
29
|
-
page++;
|
|
30
|
-
const templates = await courier.notifications.list({ cursor });
|
|
31
|
-
setTemplates(p => [...p, ...templates.results]);
|
|
32
|
-
if (templates.paging.more) {
|
|
33
|
-
cursor = templates.paging.cursor;
|
|
34
|
-
}
|
|
35
|
-
else {
|
|
36
|
-
break;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
processing.setFalse();
|
|
40
|
-
};
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
if (!processing.value) {
|
|
43
|
-
runExport();
|
|
44
|
-
}
|
|
45
|
-
}, [processing.value, templates]);
|
|
46
|
-
const runExport = async () => {
|
|
47
|
-
if (csv) {
|
|
48
|
-
await fs.writeFile(out_file, stringify(flattenData(templates), { header: true }));
|
|
49
|
-
}
|
|
50
|
-
else if (json) {
|
|
51
|
-
await fs.writeFile(out_file, JSON.stringify(flattenData(templates), null, 2), {
|
|
52
|
-
encoding: 'utf-8',
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
if (webhook?.length) {
|
|
56
|
-
try {
|
|
57
|
-
await fetch(webhook, {
|
|
58
|
-
method: 'POST',
|
|
59
|
-
headers: {
|
|
60
|
-
'Content-Type': 'application/json',
|
|
61
|
-
},
|
|
62
|
-
body: JSON.stringify(flattenData(templates)),
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
catch (e) {
|
|
66
|
-
setError(e instanceof Error ? e.message : String(e));
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
running.setFalse();
|
|
70
|
-
};
|
|
71
|
-
if (running.value) {
|
|
72
|
-
return React.createElement(Spinner, { text: "Retrieving notification templates" });
|
|
73
|
-
}
|
|
74
|
-
else if (error?.length) {
|
|
75
|
-
return React.createElement(UhOh, { text: error });
|
|
76
|
-
}
|
|
77
|
-
else if (json || csv) {
|
|
78
|
-
return React.createElement(Text, null,
|
|
79
|
-
"Saved to ",
|
|
80
|
-
out_file);
|
|
81
|
-
}
|
|
82
|
-
else if (webhook?.length) {
|
|
83
|
-
return React.createElement(Text, null,
|
|
84
|
-
"Webhook sent to ",
|
|
85
|
-
webhook);
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
return (React.createElement(Table, { data: templates.map(({ tags, routing, created_at, updated_at, ...rest }) => {
|
|
89
|
-
return {
|
|
90
|
-
...rest,
|
|
91
|
-
tags: _.map(tags?.data, t => t.name).join(', '),
|
|
92
|
-
routing: JSON.stringify(routing, null, 2),
|
|
93
|
-
created_at: DateTime.fromMillis(created_at, {
|
|
94
|
-
zone: 'utc',
|
|
95
|
-
}).toRelative(),
|
|
96
|
-
updated_at: DateTime.fromMillis(updated_at, {
|
|
97
|
-
zone: 'utc',
|
|
98
|
-
}).toRelative(),
|
|
99
|
-
};
|
|
100
|
-
}) }));
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
const flattenData = (data) => {
|
|
104
|
-
return data.map(({ tags, routing, created_at, updated_at, ...rest }) => {
|
|
105
|
-
return {
|
|
106
|
-
...rest,
|
|
107
|
-
tags: _.map(tags?.data, t => t.name).join(', '),
|
|
108
|
-
routing: JSON.stringify(routing, null, 2),
|
|
109
|
-
created_at: DateTime.fromMillis(created_at, { zone: 'utc' }).toISO(),
|
|
110
|
-
updated_at: DateTime.fromMillis(updated_at, { zone: 'utc' }).toISO(),
|
|
111
|
-
};
|
|
112
|
-
});
|
|
113
|
-
};
|
|
114
|
-
export default TemplatesList;
|
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
import { ProgressBar } from '@inkjs/ui';
|
|
2
|
-
import { Box, Text } from 'ink';
|
|
3
|
-
import _ from 'lodash';
|
|
4
|
-
import React, { useEffect, useState } from 'react';
|
|
5
|
-
import { useBoolean, useCounter } from 'usehooks-ts';
|
|
6
|
-
import getDb from '../bulk.js';
|
|
7
|
-
import { useCliContext } from '../components/Context.js';
|
|
8
|
-
import Spinner from '../components/Spinner.js';
|
|
9
|
-
import UhOh from '../components/UhOh.js';
|
|
10
|
-
import delay from '../lib/delay.js';
|
|
11
|
-
const tenantToModifed = (t) => {
|
|
12
|
-
const modified = {
|
|
13
|
-
...t,
|
|
14
|
-
default_preferences: _.get(t, ['default_preferences', 'items'], []).reduce((p, { id, ...r }) => {
|
|
15
|
-
_.set(p, [id], r);
|
|
16
|
-
return p;
|
|
17
|
-
}, {}),
|
|
18
|
-
};
|
|
19
|
-
return modified;
|
|
20
|
-
};
|
|
21
|
-
const modifiedToTenant = (t) => {
|
|
22
|
-
const tenant = {
|
|
23
|
-
...t,
|
|
24
|
-
default_preferences: {
|
|
25
|
-
items: Object.entries(t.default_preferences).map(([id, r]) => {
|
|
26
|
-
return { id, ...r };
|
|
27
|
-
}),
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
return tenant;
|
|
31
|
-
};
|
|
32
|
-
export default () => {
|
|
33
|
-
const { parsedParams, courier } = useCliContext();
|
|
34
|
-
const [error, setError] = useState();
|
|
35
|
-
const processing = useBoolean(false);
|
|
36
|
-
const [data, setData] = useState();
|
|
37
|
-
const [data_errors, setDataErrors] = useState([]);
|
|
38
|
-
const counter = useCounter(0);
|
|
39
|
-
const filename = String(_.get(parsedParams, ['_', 0], ''));
|
|
40
|
-
const { db, filetype, sql } = getDb(filename);
|
|
41
|
-
const merge = Boolean(parsedParams['merge']);
|
|
42
|
-
useEffect(() => {
|
|
43
|
-
if (filetype) {
|
|
44
|
-
getData();
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
setError('File type not supported.');
|
|
48
|
-
}
|
|
49
|
-
}, []);
|
|
50
|
-
useEffect(() => {
|
|
51
|
-
if (data) {
|
|
52
|
-
processData();
|
|
53
|
-
}
|
|
54
|
-
}, [data]);
|
|
55
|
-
const getData = () => {
|
|
56
|
-
processing.setTrue();
|
|
57
|
-
db.all(sql, (err, result) => {
|
|
58
|
-
if (err) {
|
|
59
|
-
setError(err.message);
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
setData(result);
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
};
|
|
66
|
-
const processData = async () => {
|
|
67
|
-
if (data?.length) {
|
|
68
|
-
for (let i = 0; i < data.length; i++) {
|
|
69
|
-
let { tenant_id, parent_tenant_id, name, brand_id, user_profile = {}, default_preferences = {}, properties = {}, ...rest } = data[i] || {};
|
|
70
|
-
tenant_id = tenant_id ? String(tenant_id) : undefined;
|
|
71
|
-
parent_tenant_id = parent_tenant_id
|
|
72
|
-
? String(parent_tenant_id)
|
|
73
|
-
: undefined;
|
|
74
|
-
brand_id = brand_id ? String(brand_id) : undefined;
|
|
75
|
-
name = name ? String(name) : tenant_id;
|
|
76
|
-
if (!tenant_id) {
|
|
77
|
-
setDataErrors(p => [...p, `tenant_id not found in index ${i}`]);
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
try {
|
|
81
|
-
// handle rest, all should go into properties unlness they are user_profile or default_preferences
|
|
82
|
-
Object.entries(rest).forEach(([key, value]) => {
|
|
83
|
-
if (key.startsWith('user_profile.')) {
|
|
84
|
-
_.set(user_profile, key.slice('user_profile.'.length - 1), value);
|
|
85
|
-
}
|
|
86
|
-
else if (key.startsWith('default_preferences.')) {
|
|
87
|
-
_.set(default_preferences, key.slice('default_preferences.'.length - 1), value);
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
_.set(properties, key, value);
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
let curr;
|
|
94
|
-
let next = {
|
|
95
|
-
parent_tenant_id,
|
|
96
|
-
name,
|
|
97
|
-
brand_id,
|
|
98
|
-
user_profile,
|
|
99
|
-
default_preferences,
|
|
100
|
-
properties,
|
|
101
|
-
};
|
|
102
|
-
if (merge) {
|
|
103
|
-
try {
|
|
104
|
-
curr = await courier.tenants.get(tenant_id);
|
|
105
|
-
}
|
|
106
|
-
catch (e) {
|
|
107
|
-
curr = undefined;
|
|
108
|
-
}
|
|
109
|
-
finally {
|
|
110
|
-
if (curr) {
|
|
111
|
-
const modified = tenantToModifed(curr);
|
|
112
|
-
next = {
|
|
113
|
-
...curr,
|
|
114
|
-
...next,
|
|
115
|
-
user_profile: {
|
|
116
|
-
...next.user_profile,
|
|
117
|
-
...curr.user_profile,
|
|
118
|
-
},
|
|
119
|
-
};
|
|
120
|
-
next = {
|
|
121
|
-
...next,
|
|
122
|
-
default_preferences: {
|
|
123
|
-
...modified?.default_preferences,
|
|
124
|
-
...next.default_preferences,
|
|
125
|
-
},
|
|
126
|
-
user_profile: {
|
|
127
|
-
...modified?.user_profile,
|
|
128
|
-
...next.user_profile,
|
|
129
|
-
},
|
|
130
|
-
properties: {
|
|
131
|
-
...modified?.properties,
|
|
132
|
-
...next.properties,
|
|
133
|
-
},
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
await delay(2000);
|
|
139
|
-
await courier.tenants.createOrReplace(tenant_id, modifiedToTenant(next));
|
|
140
|
-
counter.increment();
|
|
141
|
-
}
|
|
142
|
-
catch (err) {
|
|
143
|
-
setDataErrors(p => [
|
|
144
|
-
...p,
|
|
145
|
-
`tenant_id (${tenant_id}) failed to update in index ${i}: ${String(err)}`,
|
|
146
|
-
]);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
processing.setFalse();
|
|
152
|
-
};
|
|
153
|
-
if (!filename?.length) {
|
|
154
|
-
return React.createElement(UhOh, { text: "You must specify a filename." });
|
|
155
|
-
}
|
|
156
|
-
else if (error?.length) {
|
|
157
|
-
return React.createElement(UhOh, { text: error });
|
|
158
|
-
}
|
|
159
|
-
else if (data && processing.value) {
|
|
160
|
-
return (React.createElement(React.Fragment, null,
|
|
161
|
-
React.createElement(ProgressBar, { value: Math.floor(((counter.count + 1) / data.length) * 100) }),
|
|
162
|
-
React.createElement(Spinner, { text: `Completed Rows: ${counter.count} / ${data.length}` })));
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
165
|
-
return (React.createElement(Box, { flexDirection: "column", marginY: 1 },
|
|
166
|
-
React.createElement(Text, { color: 'green' }, `Completed Rows: ${counter.count} / ${data?.length || 0}`),
|
|
167
|
-
data_errors.map((err, i) => {
|
|
168
|
-
return React.createElement(UhOh, { key: i, text: err });
|
|
169
|
-
})));
|
|
170
|
-
}
|
|
171
|
-
};
|