proca 0.1.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 ADDED
@@ -0,0 +1,214 @@
1
+ Access the proca api
2
+
3
+ ## usage
4
+
5
+ ### global installation
6
+ <!-- usage -->
7
+ ```sh-session
8
+ $ npm install -g proca
9
+ $ proca COMMAND
10
+ running command...
11
+ $ proca (--version)
12
+ proca/0.1.1 linux-x64 node-v20.12.2
13
+ $ proca --help [COMMAND]
14
+ USAGE
15
+ $ proca COMMAND
16
+ ...
17
+ ```
18
+ <!-- usagestop -->
19
+
20
+ ### local development
21
+
22
+ ```sh-session
23
+ $ git clone https://github.com/fixthestatusquo/proca-cli.git
24
+ $ git install
25
+ $ cd proca-cli
26
+ $./proca-cli --help
27
+ ...
28
+ ```
29
+
30
+ ### TOPICS
31
+ - campaign Handle campaigns
32
+ - org
33
+ - config create setting to access the server authentication
34
+
35
+ ### TODO TOPICS
36
+
37
+ - widget
38
+ - actions
39
+ - service
40
+ - target
41
+
42
+ # Commands
43
+ <!-- commands -->
44
+ * [`proca campaign get`](#proca-campaign-get)
45
+ * [`proca campaign list [TITLE]`](#proca-campaign-list-title)
46
+ * [`proca config add [ENVIRONMENT]`](#proca-config-add-environment)
47
+ * [`proca config setup [ENVIRONMENT]`](#proca-config-setup-environment)
48
+ * [`proca org get`](#proca-org-get)
49
+
50
+ ## `proca campaign get`
51
+
52
+ view a campaign
53
+
54
+ ```
55
+ USAGE
56
+ $ proca campaign get [--simplify [--json | --csv | --table]] [-i <organisation name>]
57
+ [-n <campaign name>] [--config] [--stats] [--locale <value>]
58
+
59
+ FLAGS
60
+ -i, --id=<organisation name> id of the campaign
61
+ -n, --name=<campaign name> name of the campaign
62
+ --[no-]config display the config
63
+ --locale=<value> display a locale
64
+ --[no-]stats display the stats
65
+
66
+ OUTPUT FLAGS
67
+ --csv Format output as csv
68
+ --json Format output as json
69
+ --simplify flatten and filter to output only the most important attributes
70
+ --table Format output as table [default]
71
+
72
+ DESCRIPTION
73
+ view a campaign
74
+
75
+ EXAMPLES
76
+ $ proca campaign get -i 42
77
+ ```
78
+
79
+ ## `proca campaign list [TITLE]`
80
+
81
+ list all the campaigns
82
+
83
+ ```
84
+ USAGE
85
+ $ proca campaign list [TITLE] [--simplify [--json | --csv | --table]] [-o <organisation
86
+ name> | -t <campaign title>] [--stats]
87
+
88
+ ARGUMENTS
89
+ TITLE name of the campaign, % for wildchar
90
+
91
+ FLAGS
92
+ -o, --org=<organisation name> campaigns of the organisation (coordinator or partner)
93
+ -t, --title=<campaign title> name of the campaign, % for wildchar
94
+ --[no-]stats display the stats
95
+
96
+ OUTPUT FLAGS
97
+ --csv Format output as csv
98
+ --json Format output as json
99
+ --simplify flatten and filter to output only the most important attributes
100
+ --table Format output as table [default]
101
+
102
+ DESCRIPTION
103
+ list all the campaigns
104
+
105
+ EXAMPLES
106
+ $ proca campaign list %pizza%
107
+ ```
108
+
109
+ ## `proca config add [ENVIRONMENT]`
110
+
111
+ create setting to access the server authentication
112
+
113
+ ```
114
+ USAGE
115
+ $ proca config add [ENVIRONMENT] --token <API-token> [--simplify [--json | --csv |
116
+ --table]] [--force] [--url <url>] [--n8n <n8n api>] [--supabase <url>] [--supabase-anon-key <value>]
117
+ [--supabase-secrey-key <value>]
118
+
119
+ ARGUMENTS
120
+ ENVIRONMENT [default: default] environment
121
+
122
+ FLAGS
123
+ --force write over an existing configuration
124
+ --n8n=<n8n api> api access on the n8n server
125
+ --supabase=<url> url of the supabase
126
+ --supabase-anon-key=<value> anonymous key
127
+ --supabase-secrey-key=<value> secret service key
128
+ --token=<API-token> (required) user token on proca server
129
+ --url=<url> [default: https://api.proca.app/api] url of the proca server api
130
+
131
+ OUTPUT FLAGS
132
+ --csv Format output as csv
133
+ --json Format output as json
134
+ --simplify flatten and filter to output only the most important attributes
135
+ --table Format output as table [default]
136
+
137
+ DESCRIPTION
138
+ create setting to access the server authentication
139
+
140
+ ALIASES
141
+ $ proca config setup
142
+
143
+ EXAMPLES
144
+ $ proca config add --user=xavier@example.org --token=API-12345789
145
+ ```
146
+
147
+ ## `proca config setup [ENVIRONMENT]`
148
+
149
+ create setting to access the server authentication
150
+
151
+ ```
152
+ USAGE
153
+ $ proca config setup [ENVIRONMENT] --token <API-token> [--simplify [--json | --csv |
154
+ --table]] [--force] [--url <url>] [--n8n <n8n api>] [--supabase <url>] [--supabase-anon-key <value>]
155
+ [--supabase-secrey-key <value>]
156
+
157
+ ARGUMENTS
158
+ ENVIRONMENT [default: default] environment
159
+
160
+ FLAGS
161
+ --force write over an existing configuration
162
+ --n8n=<n8n api> api access on the n8n server
163
+ --supabase=<url> url of the supabase
164
+ --supabase-anon-key=<value> anonymous key
165
+ --supabase-secrey-key=<value> secret service key
166
+ --token=<API-token> (required) user token on proca server
167
+ --url=<url> [default: https://api.proca.app/api] url of the proca server api
168
+
169
+ OUTPUT FLAGS
170
+ --csv Format output as csv
171
+ --json Format output as json
172
+ --simplify flatten and filter to output only the most important attributes
173
+ --table Format output as table [default]
174
+
175
+ DESCRIPTION
176
+ create setting to access the server authentication
177
+
178
+ ALIASES
179
+ $ proca config setup
180
+
181
+ EXAMPLES
182
+ $ proca config setup --user=xavier@example.org --token=API-12345789
183
+ ```
184
+
185
+ ## `proca org get`
186
+
187
+ view a org
188
+
189
+ ```
190
+ USAGE
191
+ $ proca org get [--simplify [--json | --csv | --table]] [-n <org name>] [--config]
192
+ [--keys] [--campaigns] [--widgets] [--users]
193
+
194
+ FLAGS
195
+ -n, --name=<org name> name of the org
196
+ --[no-]campaigns
197
+ --[no-]config display the config
198
+ --[no-]keys
199
+ --[no-]users
200
+ --[no-]widgets
201
+
202
+ OUTPUT FLAGS
203
+ --csv Format output as csv
204
+ --json Format output as json
205
+ --simplify flatten and filter to output only the most important attributes
206
+ --table Format output as table [default]
207
+
208
+ DESCRIPTION
209
+ view a org
210
+
211
+ EXAMPLES
212
+ $ proca org get <name of the ngo>
213
+ ```
214
+ <!-- commandsstop -->
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "proca",
3
+ "description": "Access the proca api",
4
+ "version": "0.1.1",
5
+ "author": "=Xavier",
6
+ "bin": {
7
+ "proca": "./proca-cli"
8
+ },
9
+ "bugs": "https://github.com/fixthestatusquo/git@github.com:fixthestatusquo/proca-cli.git/issues",
10
+ "dependencies": {
11
+ "@oclif/core": "^4",
12
+ "@oclif/plugin-help": "^6",
13
+ "@oclif/plugin-plugins": "^5",
14
+ "@urql/exchange-auth": "^2.2.0",
15
+ "dotenv": "^16.4.5",
16
+ "easy-table": "^1.2.0",
17
+ "fast-csv": "^5.0.1",
18
+ "urql": "^4.1.0"
19
+ },
20
+ "devDependencies": {
21
+ "@biomejs/biome": "^1.9.3",
22
+ "@graphql-codegen/cli": "^2.x.x",
23
+ "@graphql-codegen/introspection": "^2.x.x",
24
+ "@graphql-codegen/typescript-operations": "^4.2.3",
25
+ "@graphql-codegen/typescript-urql": "^4.0.0",
26
+ "@oclif/plugin-autocomplete": "^3.2.5",
27
+ "@oclif/plugin-not-found": "^3.2.22",
28
+ "lefthook": "^1.7.18",
29
+ "oclif": "^4"
30
+ },
31
+ "engines": {
32
+ "node": ">=18.0.0"
33
+ },
34
+ "files": ["/proca-cli", "/src", "/theme.json"],
35
+ "homepage": "https://github.com/fixthestatusquo/git@github.com:fixthestatusquo/proca-cli.git",
36
+ "keywords": ["oclif"],
37
+ "imports": {
38
+ "#src/*": "./src/*"
39
+ },
40
+ "license": "aGPL3",
41
+ "main": "src/index.js",
42
+ "oclif": {
43
+ "repository": "github:fixthestatusquo/proca-cli",
44
+ "bin": "proca",
45
+ "theme": "theme.json",
46
+ "topicSeparator": " ",
47
+ "commands": "./src/commands",
48
+ "topics": {
49
+ "widget": {
50
+ "description": "Manage widgets"
51
+ },
52
+ "campaign": {
53
+ "description": "Handle campaigns"
54
+ }
55
+ },
56
+ "plugins": []
57
+ },
58
+ "repository": "git@github.com:fixthestatusquo/proca-cli.git",
59
+ "scripts": {
60
+ "check": "npx @biomejs/biome check --write",
61
+ "format": "npx @biomejs/biome format --write",
62
+ "lint": "npx @biomejs/biome lint --write",
63
+ "prepack": "oclif manifest && oclif readme",
64
+ "version": "oclif readme && git add README.md"
65
+ }
66
+ }
package/proca-cli ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env -S node --trace-warnings
2
+ const url = require('url');
3
+
4
+ (async () => {
5
+ const metaUrl = url.pathToFileURL(__filename).href;
6
+ const { execute } = await import('@oclif/core');
7
+ await execute({ dir: metaUrl });
8
+ })();
@@ -0,0 +1,121 @@
1
+ import { Args, Flags } from "@oclif/core";
2
+ import { error, stdout, ux } from "@oclif/core/ux";
3
+ import Command from "#src/procaCommand.mjs";
4
+ import {
5
+ FragmentOrg,
6
+ FragmentStats,
7
+ FragmentSummary,
8
+ } from "#src/queries/campaign.mjs";
9
+ import { gql, query } from "#src/urql.mjs";
10
+
11
+ export default class CampaignGet extends Command {
12
+ actionTypes = new Set();
13
+
14
+ static args = {};
15
+
16
+ static description = "view a campaign";
17
+
18
+ static examples = ["<%= config.bin %> <%= command.id %> -i 42"];
19
+
20
+ static flags = {
21
+ // flag with no value (-f, --force)
22
+ ...super.globalFlags,
23
+ id: Flags.string({
24
+ char: "i",
25
+ parse: (input) => {
26
+ return Number.parseInt(input, 10);
27
+ },
28
+ description: "id of the campaign",
29
+ exactlyOne: ["id", "name"],
30
+ helpValue: "<organisation name>",
31
+ }),
32
+ name: Flags.string({
33
+ char: "n",
34
+ description: "name of the campaign",
35
+ helpValue: "<campaign name>",
36
+ }),
37
+ config: Flags.boolean({
38
+ description: "display the config",
39
+ default: false,
40
+ allowNo: true,
41
+ }),
42
+ stats: Flags.boolean({
43
+ description: "display the stats",
44
+ default: true,
45
+ allowNo: true,
46
+ }),
47
+ locale: Flags.string({
48
+ description: "display a locale",
49
+ }),
50
+ };
51
+
52
+ Get = async (id, name) => {
53
+ const GetCampaignDocument = gql`
54
+ query GetCampaign($id: Int, $name: String, $withStats: Boolean = false) {
55
+ campaign (name: $name, id: $id) {
56
+ ...Summary
57
+ ...Org
58
+ config
59
+ ...Stats @include(if: $withStats)
60
+ }
61
+ }
62
+ ${FragmentStats}
63
+ ${FragmentSummary}
64
+ ${FragmentOrg}
65
+ `;
66
+ const result = await query(GetCampaignDocument, {
67
+ id: id,
68
+ name: name,
69
+ withStats: this.flags.stats,
70
+ });
71
+ return result.campaign;
72
+ };
73
+
74
+ simplify = (d) => {
75
+ const result = {
76
+ id: d.id,
77
+ Name: d.name,
78
+ Title: d.title,
79
+ Org: d.org.name,
80
+ Status: d.status,
81
+ locales: d.config.locales && Object.keys(d.config.locales).join(" "),
82
+ journey: d.config.journey?.join(" → "),
83
+ };
84
+ if (this.flags.stats) {
85
+ result["#Supporters"] = d.stats.supporterCount;
86
+
87
+ this.actionTypes.forEach((type) => {
88
+ const action = d.stats.actionCount.find(
89
+ (action) => action.actionType === type,
90
+ );
91
+ if (action) result[`#${type}`] = action.count;
92
+ });
93
+ }
94
+ return result;
95
+ };
96
+
97
+ table = (r) => {
98
+ r.config = JSON.parse(r.config);
99
+ super.table(this.simplify(r), null, null);
100
+ if (this.flags.locale) {
101
+ this.prettyJson(r.config.locales[this.flags.locale]);
102
+ }
103
+ if (this.flags.config) {
104
+ r.config.locales = undefined;
105
+ this.prettyJson(r.config);
106
+ }
107
+ };
108
+
109
+ async run() {
110
+ const { args, flags } = await this.parse();
111
+
112
+ const data = await this.Get(flags.id, flags.name);
113
+ if (this.flags.stats) {
114
+ data.stats.actionCount.forEach((d) => {
115
+ //skip share_confirmed?
116
+ this.actionTypes.add(d.actionType);
117
+ });
118
+ }
119
+ return this.output(data);
120
+ }
121
+ }
@@ -0,0 +1,165 @@
1
+ import { Args, Flags } from "@oclif/core";
2
+ import { error, stdout, ux } from "@oclif/core/ux";
3
+ import Command from "#src/procaCommand.mjs";
4
+ import {
5
+ FragmentOrg,
6
+ FragmentStats,
7
+ FragmentSummary,
8
+ } from "#src/queries/campaign.mjs";
9
+ import { gql, query } from "#src/urql.mjs";
10
+
11
+ export default class CampaignList extends Command {
12
+ actionTypes = new Set();
13
+
14
+ static args = {
15
+ title: Args.string({ description: "name of the campaign, % for wildchar" }),
16
+ };
17
+
18
+ static description = "list all the campaigns";
19
+
20
+ static examples = ["<%= config.bin %> <%= command.id %> %pizza%"];
21
+
22
+ static flags = {
23
+ // flag with no value (-f, --force)
24
+ ...super.globalFlags,
25
+ org: Flags.string({
26
+ char: "o",
27
+ description: "campaigns of the organisation (coordinator or partner)",
28
+ exclusive: ["title"],
29
+ helpValue: "<organisation name>",
30
+ }),
31
+ title: Flags.string({
32
+ char: "t",
33
+ description: "name of the campaign, % for wildchar",
34
+ exclusive: ["org"],
35
+ helpValue: "<campaign title>",
36
+ }),
37
+ stats: Flags.boolean({
38
+ description: "display the stats",
39
+ default: true,
40
+ allowNo: true,
41
+ }),
42
+ };
43
+
44
+ OrgSearch = async (name) => {
45
+ const SearchCampaignsDocument = gql`
46
+ query SearchCampaigns($org: String!, $withStats: Boolean = false) {
47
+ org (name:$org) {
48
+ campaigns {
49
+ ...Summary
50
+ ...Org
51
+ ...Stats @include(if: $withStats)
52
+ }
53
+ }
54
+ }
55
+ ${FragmentStats}
56
+ ${FragmentOrg}
57
+ ${FragmentSummary}
58
+ `;
59
+ const result = await query(SearchCampaignsDocument, {
60
+ org: name,
61
+ withStats: this.flags.stats,
62
+ });
63
+ return result.org.campaigns;
64
+ //return result.campaigns.map (d => {d.config = JSON.parse(d.config); return d});
65
+ };
66
+
67
+ Search = async (title) => {
68
+ const SearchCampaignsDocument = gql`
69
+ query SearchCampaigns($title: String!, $withStats: Boolean = false) {
70
+ campaigns(title: $title) {
71
+ ...Summary
72
+ ...Org
73
+ ...Stats @include(if: $withStats)
74
+ }
75
+ }
76
+ ${FragmentStats}
77
+ ${FragmentOrg}
78
+ ${FragmentSummary}
79
+ `;
80
+ const result = await query(SearchCampaignsDocument, {
81
+ title: title,
82
+ withStats: this.flags.stats,
83
+ });
84
+ return result.campaigns;
85
+ //return result.campaigns.map (d => {d.config = JSON.parse(d.config); return d});
86
+ };
87
+
88
+ simplify = (d, context) => {
89
+ const result = {
90
+ id: d.id,
91
+ Name: d.name,
92
+ Title: d.title,
93
+ Org: d.org.name,
94
+ Status: d.status,
95
+ };
96
+ if (this.flags.stats) {
97
+ result["#Supporters"] = d.stats.supporterCount;
98
+
99
+ this.actionTypes.forEach((type) => {
100
+ const action = d.stats.actionCount.find(
101
+ (action) => action.actionType === type,
102
+ );
103
+ if (action) result[`#${type}`] = action.count;
104
+ });
105
+ }
106
+ return result;
107
+ };
108
+
109
+ table = (r) => {
110
+ super.table(
111
+ r,
112
+ (d, cell) => {
113
+ for (const [key, value] of Object.entries(this.simplify(d))) {
114
+ cell(key, value);
115
+ }
116
+ },
117
+ (table) => table.sort(["ID"]).toString(),
118
+ );
119
+ };
120
+
121
+ async run() {
122
+ const { args, flags } = await this.parse(CampaignList);
123
+ let data = [];
124
+
125
+ if (args.title && flags.title) {
126
+ throw new Error(
127
+ `${this.id} EITHER [title of the campaign] OR --title [title of the campaign]`,
128
+ );
129
+ }
130
+ if (args.title) {
131
+ flags.title = args.title;
132
+ }
133
+
134
+ if (!flags.title && !flags.org) {
135
+ throw new Error(
136
+ `${this.id} -t [title of the campaign] or -o [organisation]`,
137
+ );
138
+ }
139
+
140
+ if (flags.title) {
141
+ data = await this.Search(flags.title);
142
+ if (this.flags.stats) {
143
+ data.forEach((d) => {
144
+ d.stats.actionCount.forEach((d) => {
145
+ //skip share_confirmed?
146
+ this.actionTypes.add(d.actionType);
147
+ });
148
+ });
149
+ }
150
+ }
151
+
152
+ if (flags.org) {
153
+ data = await this.OrgSearch(flags.org);
154
+ if (this.flags.stats) {
155
+ data.forEach((d) => {
156
+ d.stats.actionCount.forEach((d) => {
157
+ //skip share_confirmed?
158
+ this.actionTypes.add(d.actionType);
159
+ });
160
+ });
161
+ }
162
+ }
163
+ return this.output(data);
164
+ }
165
+ }
@@ -0,0 +1,19 @@
1
+ query SearchCampaigns($name: String!) {
2
+ campaigns(name: $name) {
3
+ id
4
+ name
5
+ title
6
+ config
7
+ }
8
+ }
9
+
10
+ query GetOrgCampaigns($org: String!) {
11
+ org(name: $org) {
12
+ campaigns {
13
+ id
14
+ name
15
+ title
16
+ config
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,108 @@
1
+ import { Args, Flags } from "@oclif/core";
2
+ import { error, stdout, ux } from "@oclif/core/ux";
3
+ import { get as getConfig, getFilename, write } from "#src/config.mjs";
4
+ import Command from "#src/procaCommand.mjs";
5
+
6
+ export default class CampaignList extends Command {
7
+ static enableJsonFlag = true;
8
+ static aliases = ["config:setup"];
9
+ static deprecateAliases = true;
10
+
11
+ static args = {
12
+ environment: Args.string({
13
+ description: "environment",
14
+ default: "default",
15
+ }),
16
+ };
17
+
18
+ static description = "create setting to access the server authentication";
19
+
20
+ static examples = [
21
+ "<%= config.bin %> <%= command.id %> --user=xavier@example.org --token=API-12345789",
22
+ ];
23
+
24
+ static flags = {
25
+ // flag with no value (-f, --force)
26
+ ...super.globalFlags,
27
+ force: Flags.boolean({
28
+ description: "write over an existing configuration",
29
+ default: false,
30
+ helpValue: "(default false)",
31
+ }),
32
+ url: Flags.string({
33
+ description: "url of the proca server api",
34
+ default: "https://api.proca.app/api",
35
+ helpValue: "<url>",
36
+ }),
37
+ token: Flags.string({
38
+ description: "user token on proca server",
39
+ helpValue: "<API-token>",
40
+ required: true,
41
+ }),
42
+ n8n: Flags.string({
43
+ description: "api access on the n8n server",
44
+ helpValue: "<n8n api>",
45
+ }),
46
+ supabase: Flags.string({
47
+ description: "url of the supabase",
48
+ helpValue: "<url>",
49
+ }),
50
+ "supabase-anon-key": Flags.string({
51
+ description: "anonymous key",
52
+ }),
53
+ "supabase-secrey-key": Flags.string({
54
+ description: "secret service key",
55
+ }),
56
+ };
57
+
58
+ format = (obj) => {
59
+ const content = ["# generated by proca-api"];
60
+ for (const [key, value] of Object.entries(obj)) {
61
+ if (value) {
62
+ content.push(`${key}='${value.replace(/'/g, "''")}'`);
63
+ } else {
64
+ content.push(`#${key}= `);
65
+ }
66
+ }
67
+ return content.join("\n");
68
+ };
69
+
70
+ generate = function () {
71
+ console.log(this.flags);
72
+
73
+ const mapping = {
74
+ REACT_APP_NAME: "proca",
75
+ REACT_APP_API_URL: this.flags.url,
76
+ PROCA_TOKEN: this.flags.token,
77
+ N8N_TOKEN: this.flags.n8n,
78
+ REACT_APP_SUPABASE_URL: this.flags.supabase,
79
+ REACT_APP_SUPABASE_ANON_KEY: this.flags.supabase_anon_key,
80
+ SUPABASE_SECRET_KEY: this.flags.supabase_secret_key,
81
+ };
82
+
83
+ return this.format(mapping);
84
+ };
85
+
86
+ async run() {
87
+ const { args, flags } = await this.parse();
88
+ const file = getFilename(this.config.configDir);
89
+ let exists = true;
90
+ const userConfig = getConfig({
91
+ folder: this.config.configDir,
92
+ onMissing: (missingFile) => {
93
+ exists = false;
94
+ },
95
+ });
96
+ if (exists && !this.flags.force) {
97
+ this.error("config file exists already", {
98
+ code: "CONFIG_ERR",
99
+ _ref: "README.md#",
100
+ suggestions: [
101
+ `edit ${file}`,
102
+ "add --force flag\nWARNING, it will delete the existing file",
103
+ ],
104
+ });
105
+ }
106
+ write(file, this.generate());
107
+ }
108
+ }