proca 2.3.1 → 2.5.1
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/README.md +58 -11
- package/package.json +2 -1
- package/src/commands/action/add.mjs +14 -21
- package/src/commands/campaign/get.mjs +1 -1
- package/src/commands/campaign/list.mjs +3 -3
- package/src/commands/org/email.mjs +7 -0
- package/src/commands/service/add.mjs +14 -5
- package/src/commands/user/join.mjs +1 -0
- package/src/commands/widget/external/cron.js +63 -0
- package/src/commands/widget/external/index.mjs +56 -33
- package/src/gitCommand.mjs +173 -0
- package/src/index.d.mts +1 -0
- package/src/procaCommand.mjs +26 -7
package/README.md
CHANGED
|
@@ -111,6 +111,7 @@ you should also use the local proca-api in your [widget generator](https://githu
|
|
|
111
111
|
* [`proca widget add`](#proca-widget-add)
|
|
112
112
|
* [`proca widget delete`](#proca-widget-delete)
|
|
113
113
|
* [`proca widget external`](#proca-widget-external)
|
|
114
|
+
* [`proca widget external cron`](#proca-widget-external-cron)
|
|
114
115
|
* [`proca widget get`](#proca-widget-get)
|
|
115
116
|
* [`proca widget list`](#proca-widget-list)
|
|
116
117
|
* [`proca widget rebuild`](#proca-widget-rebuild)
|
|
@@ -124,8 +125,8 @@ you should also use the local proca-api in your [widget generator](https://githu
|
|
|
124
125
|
USAGE
|
|
125
126
|
$ proca action add [ID_NAME_DXID...] -i <value> --firstname <value> --email <value>
|
|
126
127
|
[--json | --csv | --markdown] [--env <value>] [--simplify] [-n <the_short_name>] [-x <value>] [--testing] [--optin]
|
|
127
|
-
[--action_type <value>] [--lastname <value>] [--street <value>] [--locality <value>] [--region <value>] [--
|
|
128
|
-
<value>] [--utm <value>] [--target <value>] [--subject <value>] [--body <value>]
|
|
128
|
+
[--action_type <value>] [--lastname <value>] [--street <value>] [--locality <value>] [--region <value>] [--postcode
|
|
129
|
+
<value>] [--country <value>] [--utm <value>] [--target <value>] [--subject <value>] [--body <value>]
|
|
129
130
|
|
|
130
131
|
FLAGS
|
|
131
132
|
-i, --id=<value> (required) widget's id
|
|
@@ -141,6 +142,7 @@ FLAGS
|
|
|
141
142
|
--locality=<value>
|
|
142
143
|
--[no-]optin Whether the user opts in (default: false). Use --optin to enable or --no-optin to
|
|
143
144
|
explicitly disable.
|
|
145
|
+
--postcode=<value>
|
|
144
146
|
--region=<value>
|
|
145
147
|
--street=<value>
|
|
146
148
|
--subject=<value> [mtt] subject of the email
|
|
@@ -470,8 +472,8 @@ list all the campaigns
|
|
|
470
472
|
|
|
471
473
|
```
|
|
472
474
|
USAGE
|
|
473
|
-
$ proca campaign list [--json | --csv | --markdown] [--env <value>] [--simplify]
|
|
474
|
-
<name of the organisation>] [-t <campaign title>...] [--stats]
|
|
475
|
+
$ proca campaign list [NAME] [--json | --csv | --markdown] [--env <value>] [--simplify]
|
|
476
|
+
[-n <name of the organisation>] [-t <campaign title>...] [--stats]
|
|
475
477
|
|
|
476
478
|
FLAGS
|
|
477
479
|
-n, --name=<name of the organisation> name (technical short name, also called slug)
|
|
@@ -1160,6 +1162,15 @@ OUTPUT FLAGS
|
|
|
1160
1162
|
|
|
1161
1163
|
DESCRIPTION
|
|
1162
1164
|
Set email service and supporter confirmation for an org
|
|
1165
|
+
|
|
1166
|
+
EXAMPLES
|
|
1167
|
+
$ proca org email myorg --mailer=ses --from=hello@example.com
|
|
1168
|
+
|
|
1169
|
+
$ proca org email myorg --mailer=mailjet
|
|
1170
|
+
|
|
1171
|
+
$ proca org email myorg --supporter-confirm --supporter-confirm-template=confirm_v2
|
|
1172
|
+
|
|
1173
|
+
$ proca org email myorg --no-supporter-confirm
|
|
1163
1174
|
```
|
|
1164
1175
|
|
|
1165
1176
|
## `proca org get`
|
|
@@ -1222,7 +1233,7 @@ EXAMPLES
|
|
|
1222
1233
|
$ proca org logo <name of the ngo>
|
|
1223
1234
|
```
|
|
1224
1235
|
|
|
1225
|
-
_See code: [src/commands/org/logo.ts](https://github.com/fixthestatusquo/proca-cli/blob/v2.
|
|
1236
|
+
_See code: [src/commands/org/logo.ts](https://github.com/fixthestatusquo/proca-cli/blob/v2.5.1/src/commands/org/logo.ts)_
|
|
1226
1237
|
|
|
1227
1238
|
## `proca org user get`
|
|
1228
1239
|
|
|
@@ -1545,7 +1556,7 @@ _See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/
|
|
|
1545
1556
|
|
|
1546
1557
|
## `proca service add`
|
|
1547
1558
|
|
|
1548
|
-
Set service, usually email backend for an org
|
|
1559
|
+
Set service, usually email backend for an org. the specific meaning of each param is dependant on the service.
|
|
1549
1560
|
|
|
1550
1561
|
```
|
|
1551
1562
|
USAGE
|
|
@@ -1559,7 +1570,7 @@ FLAGS
|
|
|
1559
1570
|
--host=<value> server of the service
|
|
1560
1571
|
--password=<value> credential of the account on the service
|
|
1561
1572
|
--path=<value> path on the service
|
|
1562
|
-
--type=<option> (required)
|
|
1573
|
+
--type=<option> (required) type of the service
|
|
1563
1574
|
<options: mailjet|ses|stripe|test_stripe|preview|webhook|supabase|smtp>
|
|
1564
1575
|
--user=<value> credential of the account on the service
|
|
1565
1576
|
|
|
@@ -1570,7 +1581,15 @@ OUTPUT FLAGS
|
|
|
1570
1581
|
--[no-]simplify flatten and filter to output only the most important attributes, mostly relevant for json
|
|
1571
1582
|
|
|
1572
1583
|
DESCRIPTION
|
|
1573
|
-
Set service, usually email backend for an org
|
|
1584
|
+
Set service, usually email backend for an org. the specific meaning of each param is dependant on the service.
|
|
1585
|
+
If a service from that type exists, it will replace it
|
|
1586
|
+
|
|
1587
|
+
EXAMPLES
|
|
1588
|
+
$ proca service add -o example_org --type system
|
|
1589
|
+
|
|
1590
|
+
$ proca service add -o example_org --host=tls://mail.example.org:587 --user=login --password "secret" --type smtp
|
|
1591
|
+
|
|
1592
|
+
$ proca service add -o example_org --host=ssl://mail.example.org:465 --user=login --password "secret" --type smtp
|
|
1574
1593
|
```
|
|
1575
1594
|
|
|
1576
1595
|
## `proca service list`
|
|
@@ -1728,14 +1747,14 @@ let a user join an organisation with a role
|
|
|
1728
1747
|
```
|
|
1729
1748
|
USAGE
|
|
1730
1749
|
$ proca user join [ID_NAME_DXID] [--json | --csv | --markdown] [--env <value>]
|
|
1731
|
-
[--simplify] [-n <org>] [
|
|
1750
|
+
[--simplify] [-n <org>] [-r owner|campaigner|coordinator|translator] [-u <user email>]
|
|
1732
1751
|
|
|
1733
1752
|
FLAGS
|
|
1734
1753
|
-n, --name=<org> name (technical short name, also called slug)
|
|
1754
|
+
-r, --role=<option> [default: campaigner] permission level in that org
|
|
1755
|
+
<options: owner|campaigner|coordinator|translator>
|
|
1735
1756
|
-u, --user=<user email> email, default current user
|
|
1736
1757
|
--env=<value> [default: default] allow to switch between configurations (server or users)
|
|
1737
|
-
--role=<option> [default: campaigner] permission level in that org
|
|
1738
|
-
<options: owner|campaigner|coordinator|translator>
|
|
1739
1758
|
|
|
1740
1759
|
OUTPUT FLAGS
|
|
1741
1760
|
--csv Format output as csv
|
|
@@ -1970,6 +1989,34 @@ EXAMPLES
|
|
|
1970
1989
|
$ proca widget external --url https://mitmachen.wwf.de/node/506/polling
|
|
1971
1990
|
```
|
|
1972
1991
|
|
|
1992
|
+
## `proca widget external cron`
|
|
1993
|
+
|
|
1994
|
+
Pull all external counters and save it into a widget extra Supporter. symlink the widget json into config/counter
|
|
1995
|
+
|
|
1996
|
+
```
|
|
1997
|
+
USAGE
|
|
1998
|
+
$ proca widget external cron [--json | --csv | --markdown] [--env <value>] [--simplify]
|
|
1999
|
+
[--dry-run]
|
|
2000
|
+
|
|
2001
|
+
FLAGS
|
|
2002
|
+
--dry-run just fetch, don't update
|
|
2003
|
+
--env=<value> [default: default] allow to switch between configurations (server or users)
|
|
2004
|
+
|
|
2005
|
+
OUTPUT FLAGS
|
|
2006
|
+
--csv Format output as csv
|
|
2007
|
+
--json Format output as json
|
|
2008
|
+
--markdown Format output as markdown table
|
|
2009
|
+
--[no-]simplify flatten and filter to output only the most important attributes, mostly relevant for json
|
|
2010
|
+
|
|
2011
|
+
DESCRIPTION
|
|
2012
|
+
Pull all external counters and save it into a widget extra Supporter. symlink the widget json into config/counter
|
|
2013
|
+
|
|
2014
|
+
EXAMPLES
|
|
2015
|
+
$ proca widget external cron
|
|
2016
|
+
```
|
|
2017
|
+
|
|
2018
|
+
_See code: [src/commands/widget/external/cron.ts](https://github.com/fixthestatusquo/proca-cli/blob/v2.5.1/src/commands/widget/external/cron.ts)_
|
|
2019
|
+
|
|
1973
2020
|
## `proca widget get`
|
|
1974
2021
|
|
|
1975
2022
|
view a widget
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "proca",
|
|
3
3
|
"description": "Access the proca api",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.5.1",
|
|
5
5
|
"author": "Xavier",
|
|
6
6
|
"bin": {
|
|
7
7
|
"proca": "proca-cli"
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"merge-anything": "^6.0.6",
|
|
22
22
|
"object-path": "^0.11.8",
|
|
23
23
|
"prompts": "^2.4.2",
|
|
24
|
+
"simple-git": "^3.33.0",
|
|
24
25
|
"typescript": "^5.7.3",
|
|
25
26
|
"urql": "^4.1.0"
|
|
26
27
|
},
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { error, stdout, ux } from "@oclif/core/ux";
|
|
1
|
+
import { Flags } from "@oclif/core";
|
|
3
2
|
import Command from "#src/procaCommand.mjs";
|
|
4
3
|
import { gql, mutation } from "#src/urql.mjs";
|
|
5
|
-
|
|
4
|
+
|
|
5
|
+
export const addAction = async (params) => {
|
|
6
|
+
const d = new ActionAdd([]);
|
|
7
|
+
if (params.utm) {
|
|
8
|
+
const [campaign, source, medium] = params.utm.split(".");
|
|
9
|
+
params.tracking = { source, medium, campaign };
|
|
10
|
+
} else params.tracking = {};
|
|
11
|
+
const data = await d.create(params);
|
|
12
|
+
return data;
|
|
13
|
+
};
|
|
6
14
|
|
|
7
15
|
export default class ActionAdd extends Command {
|
|
8
16
|
static examples = [
|
|
@@ -44,6 +52,7 @@ export default class ActionAdd extends Command {
|
|
|
44
52
|
street: Flags.string(),
|
|
45
53
|
locality: Flags.string(),
|
|
46
54
|
region: Flags.string(),
|
|
55
|
+
postcode: Flags.string(),
|
|
47
56
|
country: Flags.string({
|
|
48
57
|
description: "2-letter country iso code",
|
|
49
58
|
parse: async (input) => {
|
|
@@ -116,7 +125,6 @@ export default class ActionAdd extends Command {
|
|
|
116
125
|
};
|
|
117
126
|
values.action.actionType = "mail2target";
|
|
118
127
|
}
|
|
119
|
-
console.log(values.action.mtt);
|
|
120
128
|
|
|
121
129
|
const query = gql`
|
|
122
130
|
mutation (
|
|
@@ -139,11 +147,9 @@ export default class ActionAdd extends Command {
|
|
|
139
147
|
firstName
|
|
140
148
|
}
|
|
141
149
|
}`;
|
|
142
|
-
|
|
143
150
|
const result = await mutation(query, values);
|
|
144
151
|
|
|
145
|
-
|
|
146
|
-
return result;
|
|
152
|
+
return result.addActionContact;
|
|
147
153
|
};
|
|
148
154
|
|
|
149
155
|
parseUnknownFlags = (argv) => {
|
|
@@ -151,20 +157,6 @@ export default class ActionAdd extends Command {
|
|
|
151
157
|
const chars = def.char ? [def.char] : [];
|
|
152
158
|
return [key, ...chars];
|
|
153
159
|
});
|
|
154
|
-
/* doesn't work static=false has no effect const unknownFlags = Object.fromEntries(
|
|
155
|
-
argv
|
|
156
|
-
.filter(arg =>
|
|
157
|
-
(/^--?\w+=/.test(arg)) // --key=val or -x=val
|
|
158
|
-
)
|
|
159
|
-
.map(arg => {
|
|
160
|
-
const keyval = arg.replace(/^-+/, '').split('=')
|
|
161
|
-
return [keyval[0], keyval[1]]
|
|
162
|
-
})
|
|
163
|
-
.filter(([key]) => !knownFlags.includes(key))
|
|
164
|
-
)
|
|
165
|
-
*/
|
|
166
|
-
|
|
167
|
-
// Extract key=val style positional args (e.g. foo=bar)
|
|
168
160
|
const kvArgs = Object.fromEntries(
|
|
169
161
|
argv
|
|
170
162
|
.filter((arg) => !arg.startsWith("-") && arg.includes("="))
|
|
@@ -175,6 +167,7 @@ export default class ActionAdd extends Command {
|
|
|
175
167
|
|
|
176
168
|
return kvArgs;
|
|
177
169
|
};
|
|
170
|
+
|
|
178
171
|
async run() {
|
|
179
172
|
const { args, flags } = await this.parse(ActionAdd, {
|
|
180
173
|
context: { strict: false /* this does not work*/ },
|
|
@@ -62,6 +62,7 @@ export default class CampaignGet extends Command {
|
|
|
62
62
|
name: name,
|
|
63
63
|
withStats: this.flags.stats,
|
|
64
64
|
});
|
|
65
|
+
result.campaign.config = JSON.parse(result.campaign.config);
|
|
65
66
|
return result.campaign;
|
|
66
67
|
};
|
|
67
68
|
|
|
@@ -108,7 +109,6 @@ export default class CampaignGet extends Command {
|
|
|
108
109
|
};
|
|
109
110
|
|
|
110
111
|
table = (r) => {
|
|
111
|
-
r.config = JSON.parse(r.config);
|
|
112
112
|
super.table(r, null, null);
|
|
113
113
|
if (this.flags.locale) {
|
|
114
114
|
this.prettyJson(r.config?.locales[this.flags.locale]);
|
|
@@ -18,11 +18,11 @@ export default class CampaignList extends Command {
|
|
|
18
18
|
|
|
19
19
|
static description = "list all the campaigns";
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
static args = this.namearg();
|
|
22
22
|
|
|
23
23
|
static flags = {
|
|
24
24
|
// flag with no value (-f, --force)
|
|
25
|
-
...this.flagify({ name: "name of the organisation" }),
|
|
25
|
+
...this.flagify({ name: "name of the organisation", char: "o" }),
|
|
26
26
|
title: Flags.string({
|
|
27
27
|
char: "t",
|
|
28
28
|
description: "name of the campaign",
|
|
@@ -111,7 +111,7 @@ export default class CampaignList extends Command {
|
|
|
111
111
|
|
|
112
112
|
if (!flags.title && !flags.name) {
|
|
113
113
|
throw new Error(
|
|
114
|
-
`${this.id} -t [title of the campaign] or -
|
|
114
|
+
`${this.id} -t [title of the campaign] or -n [organisation]`,
|
|
115
115
|
);
|
|
116
116
|
}
|
|
117
117
|
|
|
@@ -18,6 +18,13 @@ export default class OrgEmail extends Command {
|
|
|
18
18
|
static description =
|
|
19
19
|
"Set email service and supporter confirmation for an org";
|
|
20
20
|
|
|
21
|
+
static examples = [
|
|
22
|
+
"<%= config.bin %> <%= command.id %> myorg --mailer=ses --from=hello@example.com",
|
|
23
|
+
"<%= config.bin %> <%= command.id %> myorg --mailer=mailjet",
|
|
24
|
+
"<%= config.bin %> <%= command.id %> myorg --supporter-confirm --supporter-confirm-template=confirm_v2",
|
|
25
|
+
"<%= config.bin %> <%= command.id %> myorg --no-supporter-confirm",
|
|
26
|
+
];
|
|
27
|
+
|
|
21
28
|
static args = this.multiid();
|
|
22
29
|
|
|
23
30
|
static flags = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Flags } from "@oclif/core";
|
|
2
2
|
import Command from "#src/procaCommand.mjs";
|
|
3
|
-
import { gql, mutation
|
|
3
|
+
import { gql, mutation } from "#src/urql.mjs";
|
|
4
4
|
|
|
5
5
|
const SERVICE_NAMES = [
|
|
6
6
|
"MAILJET",
|
|
@@ -14,9 +14,19 @@ const SERVICE_NAMES = [
|
|
|
14
14
|
"SMTP",
|
|
15
15
|
].map((d) => d.toLowerCase());
|
|
16
16
|
|
|
17
|
-
export default class
|
|
17
|
+
export default class ServiceAdd extends Command {
|
|
18
18
|
static description =
|
|
19
|
-
"Set service, usually email backend for an org
|
|
19
|
+
"Set service, usually email backend for an org. the specific meaning of each param is dependant on the service. \nIf a service from that type exists, it will replace it";
|
|
20
|
+
|
|
21
|
+
// examples to add to help
|
|
22
|
+
// <%= config.bin %> resolves to the executable name
|
|
23
|
+
// <%= command.id %> resolves to the command name
|
|
24
|
+
static examples = [
|
|
25
|
+
// Examples can be simple strings
|
|
26
|
+
"<%= config.bin %> <%= command.id %> -o example_org --type system",
|
|
27
|
+
'<%= config.bin %> <%= command.id %> -o example_org --host=tls://mail.example.org:587 --user=login --password "secret" --type smtp',
|
|
28
|
+
'<%= config.bin %> <%= command.id %> -o example_org --host=ssl://mail.example.org:465 --user=login --password "secret" --type smtp',
|
|
29
|
+
];
|
|
20
30
|
|
|
21
31
|
static flags = {
|
|
22
32
|
...super.globalFlags,
|
|
@@ -29,7 +39,6 @@ export default class OrgEmail extends Command {
|
|
|
29
39
|
description: "type of the service",
|
|
30
40
|
options: SERVICE_NAMES,
|
|
31
41
|
required: true,
|
|
32
|
-
default: "system",
|
|
33
42
|
}),
|
|
34
43
|
user: Flags.string({
|
|
35
44
|
description: "credential of the account on the service",
|
|
@@ -37,7 +46,7 @@ export default class OrgEmail extends Command {
|
|
|
37
46
|
password: Flags.string({
|
|
38
47
|
description: "credential of the account on the service",
|
|
39
48
|
}),
|
|
40
|
-
host: Flags.
|
|
49
|
+
host: Flags.url({
|
|
41
50
|
description: "server of the service",
|
|
42
51
|
}),
|
|
43
52
|
path: Flags.string({
|
|
@@ -14,6 +14,7 @@ export default class UserJoinOrg extends Command {
|
|
|
14
14
|
// flag with no value (-f, --force)
|
|
15
15
|
...this.flagify({ multiid: false, name: "org", char: "o" }),
|
|
16
16
|
role: Flags.string({
|
|
17
|
+
char: "r",
|
|
17
18
|
description: "permission level in that org",
|
|
18
19
|
default: "campaigner",
|
|
19
20
|
options: ["owner", "campaigner", "coordinator", "translator"],
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { Flags } from "@oclif/core";
|
|
4
|
+
import { update } from "#src/commands/widget/external/index.mjs";
|
|
5
|
+
import Command from "#src/procaCommand.mjs";
|
|
6
|
+
|
|
7
|
+
export default class CounterExternal extends Command {
|
|
8
|
+
static description =
|
|
9
|
+
"Pull all external counters and save it into a widget extra Supporter. symlink the widget json into config/counter";
|
|
10
|
+
|
|
11
|
+
static examples = ["<%= config.bin %> <%= command.id %>"];
|
|
12
|
+
|
|
13
|
+
static flags = {
|
|
14
|
+
"dry-run": Flags.boolean({
|
|
15
|
+
description: "just fetch, don't update",
|
|
16
|
+
}),
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
async monitored() {
|
|
20
|
+
const dir = path.join(this.config.procaConfig.folder, "/counter");
|
|
21
|
+
try {
|
|
22
|
+
await fs.access(dir);
|
|
23
|
+
} catch {
|
|
24
|
+
this.error(`create the folder ${dir}`);
|
|
25
|
+
}
|
|
26
|
+
const entries = await fs.readdir(dir);
|
|
27
|
+
const results = await Promise.all(
|
|
28
|
+
entries.map(async (entry) => {
|
|
29
|
+
const file = entry.split(".");
|
|
30
|
+
if (file.length !== 2) {
|
|
31
|
+
this.warn(`invalid file ${entry}`);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (file[1] !== "json") {
|
|
35
|
+
this.warn(`should be a json file ${entry}`);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const content = JSON.parse(
|
|
39
|
+
await fs.readFile(path.join(dir, entry), "utf-8"),
|
|
40
|
+
);
|
|
41
|
+
return {
|
|
42
|
+
id: Number.parseInt(file[0]),
|
|
43
|
+
name: content.filename,
|
|
44
|
+
url: content.component.counter?.url,
|
|
45
|
+
path: content.component.counter?.path,
|
|
46
|
+
};
|
|
47
|
+
}),
|
|
48
|
+
);
|
|
49
|
+
return results.flat();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async run() {
|
|
53
|
+
const { flags } = await this.parse(CounterExternal);
|
|
54
|
+
const widgets = await this.monitored();
|
|
55
|
+
if (flags["dry-run"]) return this.output(widgets);
|
|
56
|
+
const updated = await Promise.all(
|
|
57
|
+
widgets.map(async (widget) => {
|
|
58
|
+
return await update(widget);
|
|
59
|
+
}),
|
|
60
|
+
);
|
|
61
|
+
return this.output(updated);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -1,8 +1,19 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
1
|
import { Flags } from "@oclif/core";
|
|
3
2
|
import oPath from "object-path";
|
|
4
3
|
import { updateCounter } from "#src/commands/widget/update/external.mjs";
|
|
5
|
-
import Command from "#src/
|
|
4
|
+
import Command from "#src/gitCommand.mjs";
|
|
5
|
+
|
|
6
|
+
export const update = async (config) => {
|
|
7
|
+
const d = new CounterExternal([]);
|
|
8
|
+
// const config = await d.getCounterConfig({id});
|
|
9
|
+
if (!config) {
|
|
10
|
+
console.warn("missing config");
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
const counter = await d.fetchCounter(config);
|
|
14
|
+
await updateCounter(config.id, counter);
|
|
15
|
+
return { name: config.name, counter, id: config.id };
|
|
16
|
+
};
|
|
6
17
|
|
|
7
18
|
export default class CounterExternal extends Command {
|
|
8
19
|
static description =
|
|
@@ -41,47 +52,59 @@ export default class CounterExternal extends Command {
|
|
|
41
52
|
}),
|
|
42
53
|
};
|
|
43
54
|
|
|
44
|
-
async fetchCounter({ url, path, timeout, "dry-run": verbose }) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (!response.ok) {
|
|
53
|
-
this.error(`API request failed with status ${response.status}`, {
|
|
54
|
-
exit: 1,
|
|
55
|
+
async fetchCounter({ url, path, timeout = 10000, "dry-run": verbose }) {
|
|
56
|
+
try {
|
|
57
|
+
const response = await fetch(url, {
|
|
58
|
+
signal: AbortSignal.timeout(timeout),
|
|
59
|
+
headers: {
|
|
60
|
+
"User-Agent": "proca/451.42",
|
|
61
|
+
},
|
|
55
62
|
});
|
|
56
|
-
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
this.error(`API request failed with status ${response.status}`, {
|
|
65
|
+
exit: 1,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
57
68
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
69
|
+
const data = await response.json();
|
|
70
|
+
if (verbose) {
|
|
71
|
+
this.log(JSON.stringify(data, null, 2));
|
|
72
|
+
}
|
|
73
|
+
const counter = oPath.get(data, path);
|
|
74
|
+
if (
|
|
75
|
+
Number.isNaN(Number.parseFloat(counter)) ||
|
|
76
|
+
!Number.isFinite(counter)
|
|
77
|
+
) {
|
|
78
|
+
this.error(`Could not extract value from ${counter} at ${path}`, {
|
|
79
|
+
exit: 1,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return Number.parseFloat(counter);
|
|
83
|
+
} catch (err) {
|
|
84
|
+
if (err.name === "TimeoutError") {
|
|
85
|
+
console.error("Request timed out after", timeout, "ms");
|
|
86
|
+
} else if (err.cause?.code === "ETIMEDOUT") {
|
|
87
|
+
console.error("Network timeout — server unreachable:", url);
|
|
88
|
+
} else {
|
|
89
|
+
throw err;
|
|
90
|
+
}
|
|
61
91
|
}
|
|
62
|
-
const counter = oPath.get(data, path);
|
|
63
|
-
if (Number.isNaN(Number.parseFloat(counter)) || !Number.isFinite(counter)) {
|
|
64
|
-
this.error(`Could not extract value from ${counter} at ${path}`, {
|
|
65
|
-
exit: 1,
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
return Number.parseFloat(counter);
|
|
69
92
|
}
|
|
70
93
|
|
|
71
|
-
async getCounterConfig(
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return
|
|
94
|
+
async getCounterConfig() {
|
|
95
|
+
const data = await this.read();
|
|
96
|
+
if (!data.component.counter)
|
|
97
|
+
this.error(
|
|
98
|
+
"missing config.component.counter {url, path} in ${this.getFile()}",
|
|
99
|
+
);
|
|
100
|
+
return data.component.counter;
|
|
78
101
|
}
|
|
79
102
|
|
|
80
103
|
async run() {
|
|
81
104
|
const { flags } = await this.parse(CounterExternal);
|
|
82
105
|
let counter = undefined;
|
|
83
|
-
if (!flags.url) {
|
|
84
|
-
const config = await this.getCounterConfig(
|
|
106
|
+
if (!flags.url && !flags.total) {
|
|
107
|
+
const config = await this.getCounterConfig();
|
|
85
108
|
flags.url = config.url;
|
|
86
109
|
flags.path = config.path;
|
|
87
110
|
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
//import { load as loadConfig } from "proca/src/config.mjs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import simpleGit from "simple-git";
|
|
6
|
+
import ProcaCommand from "#src/procaCommand.mjs";
|
|
7
|
+
export { Args, Flags } from "#src/index.mjs";
|
|
8
|
+
|
|
9
|
+
export class ProcaGitCommand extends ProcaCommand {
|
|
10
|
+
fileName = undefined;
|
|
11
|
+
newFile = undefined;
|
|
12
|
+
|
|
13
|
+
static extension = "json";
|
|
14
|
+
|
|
15
|
+
initGit = () => {
|
|
16
|
+
if (!fs.existsSync(path.join(this.config.procaConfig.folder, "/.git"))) {
|
|
17
|
+
this.warn("config not on git");
|
|
18
|
+
}
|
|
19
|
+
return simpleGit({ baseDir: this.config.procaConfig.folder });
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
commit = async () => this.git.commit(this.gitMessage(), [this.getFile()]);
|
|
23
|
+
|
|
24
|
+
checkFile = (fileName) => {
|
|
25
|
+
if (fileName.toString().includes("..")) {
|
|
26
|
+
this.error("invalid filename ", fileName);
|
|
27
|
+
}
|
|
28
|
+
return fileName;
|
|
29
|
+
};
|
|
30
|
+
// mkdir -p
|
|
31
|
+
mkdirp = () => {
|
|
32
|
+
fs.mkdirSync(this.getFile(), { recursive: true });
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
getFolder = () => {
|
|
36
|
+
const mapping = { widget: "./", campaign: "campaign/", org: "org/" };
|
|
37
|
+
const type = this.id.split(":")[0];
|
|
38
|
+
const folder = mapping[type];
|
|
39
|
+
if (!folder) this.error(`no folder defined for ${type}`);
|
|
40
|
+
return folder;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
setFile = (name, ext = "json") => {
|
|
44
|
+
if (!name) {
|
|
45
|
+
const folder = this.getFolder();
|
|
46
|
+
if (folder === "./") {
|
|
47
|
+
//we are dealing with widgets
|
|
48
|
+
name = `${folder}${this._flags.id}`;
|
|
49
|
+
} else {
|
|
50
|
+
name = `${folder}${this._flags.name}`;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
this.fileName = path.join(
|
|
54
|
+
this.config.procaConfig.folder,
|
|
55
|
+
`${this.checkFile(name)}.${ext}`,
|
|
56
|
+
);
|
|
57
|
+
return this.fileName;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
getFile = () => {
|
|
61
|
+
if (!this.fileName) this.error("you need to setFile first");
|
|
62
|
+
return this.fileName;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
gitMessage = (_obj) => `proca cli ${this.id}`;
|
|
66
|
+
|
|
67
|
+
fileExists = () => {
|
|
68
|
+
return fs.existsSync(this.getFile());
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
uncommited = async ({ file, commit = false, exit = true }) => {
|
|
72
|
+
if (!this.git) return false;
|
|
73
|
+
if (!file) file = this.getFile();
|
|
74
|
+
const status = await this.git.status();
|
|
75
|
+
const relative = path.relative(this.config.procaConfig.folder, file);
|
|
76
|
+
|
|
77
|
+
const hasChanges = status.modified.includes(relative);
|
|
78
|
+
if (!hasChanges) return false;
|
|
79
|
+
if (commit) {
|
|
80
|
+
this.git.commit(this.gitMessage(data, file));
|
|
81
|
+
}
|
|
82
|
+
if (exit) {
|
|
83
|
+
this.log(await this.diff(file));
|
|
84
|
+
this.error(`Your local changes to ${file} would be overwritten`, {
|
|
85
|
+
code: "git error",
|
|
86
|
+
exit: 1,
|
|
87
|
+
suggestions: ["run with --auto to commit the changes before pulling"],
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return true;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
read = async ({ file = this.getFile(), commit } = {}) => {
|
|
94
|
+
try {
|
|
95
|
+
let data;
|
|
96
|
+
if (!file) file = getFile();
|
|
97
|
+
const ext = this.constructor.extension;
|
|
98
|
+
console.log("read file", file, ext);
|
|
99
|
+
if (ext === "json") {
|
|
100
|
+
data = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
101
|
+
} else {
|
|
102
|
+
data = fs.readFileSync(file, "utf8");
|
|
103
|
+
}
|
|
104
|
+
if (commit && this.git) {
|
|
105
|
+
const status = await this.git.status();
|
|
106
|
+
const hasChanges = status.modified.includes(file);
|
|
107
|
+
if (hasChanges) {
|
|
108
|
+
this.git.commit(this.gitMessage(data, file));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return data;
|
|
112
|
+
} catch (e) {
|
|
113
|
+
console.error(`no local copy of ${file}: ${e.message}`);
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
stringify = (obj) =>
|
|
119
|
+
`${JSON.stringify(
|
|
120
|
+
obj,
|
|
121
|
+
(_key, value) => (value === null ? undefined : value),
|
|
122
|
+
2,
|
|
123
|
+
)}\n`;
|
|
124
|
+
|
|
125
|
+
diff = async (file) => {
|
|
126
|
+
const diff = await this.git.diff([file || this.getFile()]);
|
|
127
|
+
const colorDiff = diff
|
|
128
|
+
.split("\n")
|
|
129
|
+
.map((line) => {
|
|
130
|
+
if (line.startsWith("+")) return chalk.green(line);
|
|
131
|
+
if (line.startsWith("-")) return chalk.red(line);
|
|
132
|
+
if (line.startsWith("@@")) return chalk.cyan(line);
|
|
133
|
+
return line;
|
|
134
|
+
})
|
|
135
|
+
.join("\n");
|
|
136
|
+
return colorDiff;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
save = async (json) => {
|
|
140
|
+
let needToAdd = false;
|
|
141
|
+
const file = this.getFile();
|
|
142
|
+
if (this.git && !this.fileExists(file)) {
|
|
143
|
+
needToAdd = true;
|
|
144
|
+
}
|
|
145
|
+
this.uncommited({ file }); //exit if unsaved, better to always stash?
|
|
146
|
+
fs.writeFileSync(file, this.stringify(json));
|
|
147
|
+
if (this.git) {
|
|
148
|
+
if (needToAdd) {
|
|
149
|
+
await this.git.add(file);
|
|
150
|
+
}
|
|
151
|
+
const diff = await this.diff(file);
|
|
152
|
+
const r = await this.git.commit(this.gitMessage(json), file);
|
|
153
|
+
return { ...r, ...r.summary, diff };
|
|
154
|
+
}
|
|
155
|
+
return { file, git: false };
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
parse = async () => {
|
|
159
|
+
const r = await super.parse();
|
|
160
|
+
if (this._flags.git === false) {
|
|
161
|
+
this.git = undefined;
|
|
162
|
+
}
|
|
163
|
+
this._flags && this.setFile(null, this.constructor.extension);
|
|
164
|
+
return r;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
async init() {
|
|
168
|
+
await super.init();
|
|
169
|
+
this.git = this.initGit();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export default ProcaGitCommand;
|
package/src/index.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { run, Args, Flags } from "@oclif/core";
|
package/src/procaCommand.mjs
CHANGED
|
@@ -10,7 +10,8 @@ class ProcaCommand extends Command {
|
|
|
10
10
|
static enableJsonFlag = true;
|
|
11
11
|
procaConfig = { url: "https://api.proca.app/api" };
|
|
12
12
|
format = "human"; // the default formatting
|
|
13
|
-
flags = {};
|
|
13
|
+
flags = {}; // the definition
|
|
14
|
+
_flags = undefined; // the cli flags
|
|
14
15
|
|
|
15
16
|
static baseFlags = {
|
|
16
17
|
env: Flags.string({
|
|
@@ -50,6 +51,17 @@ class ProcaCommand extends Command {
|
|
|
50
51
|
return args;
|
|
51
52
|
}
|
|
52
53
|
|
|
54
|
+
static namearg() {
|
|
55
|
+
const args = {
|
|
56
|
+
name: Args.string({
|
|
57
|
+
ignoreStdin: true,
|
|
58
|
+
hidden: true,
|
|
59
|
+
description: "convenience, but try to use -n <name> instead",
|
|
60
|
+
}),
|
|
61
|
+
};
|
|
62
|
+
return args;
|
|
63
|
+
}
|
|
64
|
+
|
|
53
65
|
static flagify({ multiid = false, name = false, char } = {}) {
|
|
54
66
|
const flags = Object.assign({}, ProcaCommand.baseFlags);
|
|
55
67
|
if (name || multiid) {
|
|
@@ -86,9 +98,14 @@ class ProcaCommand extends Command {
|
|
|
86
98
|
|
|
87
99
|
async parse() {
|
|
88
100
|
const parsed = await super.parse();
|
|
101
|
+
|
|
102
|
+
if (parsed.args.name)
|
|
103
|
+
parsed.flags.name = ProcaCommand.safeName(parsed.args.name);
|
|
104
|
+
|
|
89
105
|
if (this.ctor.args.id_name_dxid === undefined) {
|
|
90
106
|
return parsed;
|
|
91
107
|
}
|
|
108
|
+
|
|
92
109
|
const maybe = parsed.args.id_name_dxid;
|
|
93
110
|
if (maybe) {
|
|
94
111
|
const identified = [
|
|
@@ -106,9 +123,11 @@ class ProcaCommand extends Command {
|
|
|
106
123
|
if (d) parsed.flags.id = d;
|
|
107
124
|
else parsed.flags.name = ProcaCommand.safeName(maybe);
|
|
108
125
|
}
|
|
126
|
+
|
|
109
127
|
if (parsed.flags.dxid) {
|
|
110
128
|
parsed.flags.id = dxid(parsed.flags.dxid);
|
|
111
129
|
}
|
|
130
|
+
|
|
112
131
|
const identified = [
|
|
113
132
|
parsed.flags.name,
|
|
114
133
|
parsed.flags.id,
|
|
@@ -120,7 +139,7 @@ class ProcaCommand extends Command {
|
|
|
120
139
|
code: 1,
|
|
121
140
|
});
|
|
122
141
|
}
|
|
123
|
-
|
|
142
|
+
this._flags = parsed.flags;
|
|
124
143
|
return parsed;
|
|
125
144
|
}
|
|
126
145
|
|
|
@@ -132,16 +151,16 @@ class ProcaCommand extends Command {
|
|
|
132
151
|
};
|
|
133
152
|
async init() {
|
|
134
153
|
await super.init();
|
|
154
|
+
this.debug = debug("proca");
|
|
155
|
+
initHook({ config: this.config });
|
|
156
|
+
this.procaConfig = this.config.procaConfig; // set up from the hooks/init
|
|
157
|
+
// await this.config.runHook('init', { config: this.config });
|
|
135
158
|
const { flags } = await this.parse();
|
|
136
159
|
this.flags = flags;
|
|
137
160
|
if (flags.json) this.format = "json";
|
|
138
161
|
if (flags.csv) this.format = "csv";
|
|
139
162
|
if (flags.markdown) this.format = "markdown";
|
|
140
163
|
|
|
141
|
-
this.debug = debug("proca");
|
|
142
|
-
initHook({ config: this.config });
|
|
143
|
-
this.procaConfig = this.config.procaConfig; // set up from the hooks/init
|
|
144
|
-
// await this.config.runHook('init', { config: this.config });
|
|
145
164
|
createClient(this.procaConfig);
|
|
146
165
|
}
|
|
147
166
|
|
|
@@ -192,7 +211,7 @@ class ProcaCommand extends Command {
|
|
|
192
211
|
for (const [key, value] of Object.entries(d)) {
|
|
193
212
|
if (key === "__typename") continue;
|
|
194
213
|
if (key === "config" && typeof value === "string") continue; // it's just a giant mess if not processed, let's skipt
|
|
195
|
-
if (value === null) continue;
|
|
214
|
+
if (value === null || value === "") continue;
|
|
196
215
|
if (typeof value === "string" || typeof value === "number") {
|
|
197
216
|
r[key] = value;
|
|
198
217
|
continue;
|