@flowcore/cli 1.0.2 → 1.1.0
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/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,13 @@
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
|
|
10
|
+
## [1.1.0](https://github.com/flowcore-io/flowcore-cli/compare/v1.0.2...v1.1.0) (2024-01-09)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* added json output ([c97b83e](https://github.com/flowcore-io/flowcore-cli/commit/c97b83e5f3f5d6172ce76e70d6331bee497b44db))
|
|
16
|
+
|
|
10
17
|
## [1.0.2](https://github.com/flowcore-io/flowcore-cli/compare/v1.0.1...v1.0.2) (2024-01-09)
|
|
11
18
|
|
|
12
19
|
|
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ $ npm install -g @flowcore/cli
|
|
|
17
17
|
$ flowcore COMMAND
|
|
18
18
|
running command...
|
|
19
19
|
$ flowcore (--version)
|
|
20
|
-
@flowcore/cli/1.0
|
|
20
|
+
@flowcore/cli/1.1.0 linux-x64 node-v20.10.0
|
|
21
21
|
$ flowcore --help [COMMAND]
|
|
22
22
|
USAGE
|
|
23
23
|
$ flowcore COMMAND
|
|
@@ -103,7 +103,7 @@ EXAMPLES
|
|
|
103
103
|
$ flowcore config set -l https://auth.flowcore.io/realms/flowcore/.well-known/openid-configuration -c my-client-id -p
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
-
_See code: [src/commands/config/set.ts](https://github.com/flowcore-io/flowcore-cli/blob/v1.0
|
|
106
|
+
_See code: [src/commands/config/set.ts](https://github.com/flowcore-io/flowcore-cli/blob/v1.1.0/src/commands/config/set.ts)_
|
|
107
107
|
|
|
108
108
|
## `flowcore config show`
|
|
109
109
|
|
|
@@ -123,7 +123,7 @@ EXAMPLES
|
|
|
123
123
|
$ flowcore config show
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
-
_See code: [src/commands/config/show.ts](https://github.com/flowcore-io/flowcore-cli/blob/v1.0
|
|
126
|
+
_See code: [src/commands/config/show.ts](https://github.com/flowcore-io/flowcore-cli/blob/v1.1.0/src/commands/config/show.ts)_
|
|
127
127
|
|
|
128
128
|
## `flowcore help [COMMANDS]`
|
|
129
129
|
|
|
@@ -166,7 +166,7 @@ EXAMPLES
|
|
|
166
166
|
$ flowcore login --port 8080
|
|
167
167
|
```
|
|
168
168
|
|
|
169
|
-
_See code: [src/commands/login.ts](https://github.com/flowcore-io/flowcore-cli/blob/v1.0
|
|
169
|
+
_See code: [src/commands/login.ts](https://github.com/flowcore-io/flowcore-cli/blob/v1.1.0/src/commands/login.ts)_
|
|
170
170
|
|
|
171
171
|
## `flowcore plugins`
|
|
172
172
|
|
|
@@ -449,14 +449,15 @@ Stream events from a datacore running on the Flowcore Platform
|
|
|
449
449
|
|
|
450
450
|
```
|
|
451
451
|
USAGE
|
|
452
|
-
$ flowcore stream STREAM [--profile <value>] [-d <value>] [-l] [-o http|log] [-s <value>]
|
|
452
|
+
$ flowcore stream STREAM [--profile <value>] [-d <value>] [-j] [-l] [-o http|log] [-s <value>]
|
|
453
453
|
|
|
454
454
|
ARGUMENTS
|
|
455
455
|
STREAM stream url to connect to
|
|
456
456
|
|
|
457
457
|
FLAGS
|
|
458
458
|
-d, --destination=<value> [default: http://localhost:3000/transform] Destination to send events to
|
|
459
|
-
-
|
|
459
|
+
-j, --json Output json only
|
|
460
|
+
-l, --[no-]live Change to live mode when reaching last time bucket
|
|
460
461
|
-o, --output=<option> [default: http] Output format
|
|
461
462
|
<options: http|log>
|
|
462
463
|
-s, --start=<value> Start time bucket to stream from, example: (1y, 1m, 1d, 1h)
|
|
@@ -469,7 +470,7 @@ EXAMPLES
|
|
|
469
470
|
$ flowcore stream https://staging.flowcore.io/flowcore/flowcore-platform/organization.0/event.organization.subscription.updated-requested.0.stream
|
|
470
471
|
```
|
|
471
472
|
|
|
472
|
-
_See code: [src/commands/stream.ts](https://github.com/flowcore-io/flowcore-cli/blob/v1.0
|
|
473
|
+
_See code: [src/commands/stream.ts](https://github.com/flowcore-io/flowcore-cli/blob/v1.1.0/src/commands/stream.ts)_
|
|
473
474
|
|
|
474
475
|
## `flowcore version`
|
|
475
476
|
|
|
@@ -506,5 +507,5 @@ DESCRIPTION
|
|
|
506
507
|
Check what user you are logged in as
|
|
507
508
|
```
|
|
508
509
|
|
|
509
|
-
_See code: [src/commands/whoami.ts](https://github.com/flowcore-io/flowcore-cli/blob/v1.0
|
|
510
|
+
_See code: [src/commands/whoami.ts](https://github.com/flowcore-io/flowcore-cli/blob/v1.1.0/src/commands/whoami.ts)_
|
|
510
511
|
<!-- commandsstop -->
|
|
@@ -7,6 +7,7 @@ export default class Stream extends BaseCommand<typeof Stream> {
|
|
|
7
7
|
static examples: string[];
|
|
8
8
|
static flags: {
|
|
9
9
|
destination: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
10
|
+
json: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
10
11
|
live: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
11
12
|
output: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
12
13
|
start: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
package/dist/commands/stream.js
CHANGED
|
@@ -23,14 +23,15 @@ export default class Stream extends BaseCommand {
|
|
|
23
23
|
];
|
|
24
24
|
static flags = {
|
|
25
25
|
destination: Flags.string({ char: 'd', default: "http://localhost:3000/transform", description: 'Destination to send events to' }),
|
|
26
|
-
|
|
26
|
+
json: Flags.boolean({ char: 'j', description: 'Output json only' }),
|
|
27
|
+
live: Flags.boolean({ allowNo: true, char: 'l', default: true, description: 'Change to live mode when reaching last time bucket' }),
|
|
27
28
|
output: Flags.string({ char: 'o', default: 'http', description: 'Output format', options: ['http', 'log'] }),
|
|
28
29
|
start: Flags.string({ char: 's', description: 'Start time bucket to stream from, example: (1y, 1m, 1d, 1h)' }),
|
|
29
30
|
};
|
|
30
31
|
async run() {
|
|
31
32
|
const config = this.cliConfiguration.getConfig();
|
|
32
33
|
const loginValidator = new ValidateLogin(config.login.url);
|
|
33
|
-
const validateLogin = await loginValidator.validate(config, this.cliConfiguration);
|
|
34
|
+
const validateLogin = await loginValidator.validate(config, this.cliConfiguration, !this.flags.json);
|
|
34
35
|
const { api, auth } = config;
|
|
35
36
|
if (validateLogin.status !== LOGIN_CODES.LOGIN_SUCCESS || !auth) {
|
|
36
37
|
ux.error(`Not logged in, please run ${ux.colorize("yellow", "flowcore login")} first`);
|
|
@@ -39,7 +40,7 @@ export default class Stream extends BaseCommand {
|
|
|
39
40
|
ux.error("No api url configured");
|
|
40
41
|
}
|
|
41
42
|
const graphqlClient = new QueryGraphQL(api.url, auth.accessToken, async () => {
|
|
42
|
-
if (!await loginValidator.isExpired(config, this.cliConfiguration)) {
|
|
43
|
+
if (!await loginValidator.isExpired(config, this.cliConfiguration, !this.flags.json)) {
|
|
43
44
|
return false;
|
|
44
45
|
}
|
|
45
46
|
return config.auth.accessToken;
|
|
@@ -65,23 +66,23 @@ export default class Stream extends BaseCommand {
|
|
|
65
66
|
let firstTimeBucket = eventRangeRequest.datacore.flowtypes.find(ft => ft.aggregator === aggregator)?.events.find(e => e.name === eventType)?.catalog.range.firstTimeBucket;
|
|
66
67
|
if (!firstTimeBucket && !flags.start) {
|
|
67
68
|
firstTimeBucket = createTimebucket(dayjs());
|
|
68
|
-
|
|
69
|
+
!this.flags.json && this.warn(`First time bucket not found, setting to current time (${ux.colorize("yellowBright", firstTimeBucket)})`);
|
|
69
70
|
}
|
|
70
71
|
if (flags.start) {
|
|
71
72
|
firstTimeBucket = this.setTimeBucket(dayjs(), flags.start);
|
|
72
|
-
|
|
73
|
+
!this.flags.json && this.log(ux.colorize("green", `Setting first time bucket to ${ux.colorize("yellow", flags.start)} ago (${ux.colorize("yellowBright", firstTimeBucket)})`));
|
|
73
74
|
}
|
|
74
75
|
if (!firstTimeBucket) {
|
|
75
76
|
firstTimeBucket = this.setTimeBucket(dayjs(), "1d");
|
|
76
|
-
|
|
77
|
+
!this.flags.json && this.log(ux.colorize("green", `Setting first time bucket to ${ux.colorize("yellow", "1d")} ago (${ux.colorize("yellowBright", firstTimeBucket)})`));
|
|
77
78
|
}
|
|
78
79
|
let lastTimeBucket = eventRangeRequest.datacore.flowtypes.find(ft => ft.aggregator === aggregator)?.events.find(e => e.name === eventType)?.catalog.range.lastTimeBucket;
|
|
79
80
|
if (!lastTimeBucket) {
|
|
80
81
|
lastTimeBucket = createTimebucket(dayjs());
|
|
81
|
-
this.warn(`Last time bucket not found, setting to current time (${ux.colorize("yellowBright", lastTimeBucket)})`);
|
|
82
|
+
!this.flags.json && this.warn(`Last time bucket not found, setting to current time (${ux.colorize("yellowBright", lastTimeBucket)})`);
|
|
82
83
|
}
|
|
83
84
|
const observer = new Subject();
|
|
84
|
-
this.log(ux.colorize("blackBright", `Starting to stream events for ${dataCoreId} - ${aggregator} - ${eventType}, press ${ux.colorize("whiteBright", "ctrl+c")} to stop`));
|
|
85
|
+
!this.flags.json && this.log(ux.colorize("blackBright", `Starting to stream events for ${dataCoreId} - ${aggregator} - ${eventType}, press ${ux.colorize("whiteBright", "ctrl+c")} to stop`));
|
|
85
86
|
// eslint-disable-next-line no-void
|
|
86
87
|
void this.streanEvents(dataCoreId, aggregator, eventType, firstTimeBucket, lastTimeBucket, graphqlClient, observer, flags.live);
|
|
87
88
|
await this.processEvents(observer, flags.destination, flags.output);
|
|
@@ -89,13 +90,14 @@ export default class Stream extends BaseCommand {
|
|
|
89
90
|
async processEvents(observer, destination, output) {
|
|
90
91
|
let done = false;
|
|
91
92
|
let events = [];
|
|
93
|
+
const warn = this.flags.json ? () => { } : this.warn.bind(this);
|
|
92
94
|
observer.subscribe({
|
|
93
95
|
complete() {
|
|
94
|
-
|
|
96
|
+
warn("Stream completed");
|
|
95
97
|
done = true;
|
|
96
98
|
},
|
|
97
99
|
error(error) {
|
|
98
|
-
|
|
100
|
+
warn(`Stream error occurred with error ${error}`);
|
|
99
101
|
done = true;
|
|
100
102
|
},
|
|
101
103
|
next(value) {
|
|
@@ -109,10 +111,13 @@ export default class Stream extends BaseCommand {
|
|
|
109
111
|
while (!processingQueue.isEmpty()) {
|
|
110
112
|
const event = processingQueue.dequeue();
|
|
111
113
|
if (output === "log") {
|
|
112
|
-
|
|
114
|
+
this.flags.json ? this.log(JSON.stringify({
|
|
115
|
+
event,
|
|
116
|
+
type: "input",
|
|
117
|
+
}, null, 2)) : this.log(JSON.stringify(event));
|
|
113
118
|
continue;
|
|
114
119
|
}
|
|
115
|
-
this.
|
|
120
|
+
this.log(`Sending event to ${destination}: ${event.eventId}`);
|
|
116
121
|
try {
|
|
117
122
|
// eslint-disable-next-line no-await-in-loop
|
|
118
123
|
const result = await fetch(destination, {
|
|
@@ -123,15 +128,19 @@ export default class Stream extends BaseCommand {
|
|
|
123
128
|
method: "POST",
|
|
124
129
|
});
|
|
125
130
|
if (!result.ok) {
|
|
126
|
-
this.warn(`Error sending event to ${destination}: ${result.statusText}`);
|
|
131
|
+
!this.flags.json && this.warn(`Error sending event to ${destination}: ${result.statusText}`);
|
|
127
132
|
}
|
|
128
133
|
if (result.body) {
|
|
129
|
-
|
|
130
|
-
|
|
134
|
+
this.flags.json ? this.log(JSON.stringify({
|
|
135
|
+
// eslint-disable-next-line no-await-in-loop
|
|
136
|
+
event: (await result.json()),
|
|
137
|
+
type: "output",
|
|
138
|
+
// eslint-disable-next-line no-await-in-loop
|
|
139
|
+
}, null, 2)) : this.log(`Response from ${destination}: ${JSON.stringify(await result.json())}`);
|
|
131
140
|
}
|
|
132
141
|
}
|
|
133
142
|
catch (error) {
|
|
134
|
-
this.warn(`Error sending event to ${destination}: ${error}`);
|
|
143
|
+
!this.flags.json && this.warn(`Error sending event to ${destination}: ${error}`);
|
|
135
144
|
}
|
|
136
145
|
}
|
|
137
146
|
if (events.length === 0) {
|
|
@@ -159,7 +168,7 @@ export default class Stream extends BaseCommand {
|
|
|
159
168
|
return createTimebucket(date.subtract(Number.parseInt(parts[1], 10), "hour"));
|
|
160
169
|
}
|
|
161
170
|
default: {
|
|
162
|
-
|
|
171
|
+
ux.error("Invalid start time bucket");
|
|
163
172
|
}
|
|
164
173
|
}
|
|
165
174
|
}
|
|
@@ -171,11 +180,15 @@ export default class Stream extends BaseCommand {
|
|
|
171
180
|
// eslint-disable-next-line no-constant-condition
|
|
172
181
|
while (true) {
|
|
173
182
|
const timeBucket = createTimebucket(startTimeBucket);
|
|
174
|
-
|
|
183
|
+
if (!this.flags.json) {
|
|
184
|
+
ux.action.start(`Fetching events for ${startTimeBucket}`);
|
|
185
|
+
}
|
|
175
186
|
let cursor = null;
|
|
176
187
|
let found = false;
|
|
177
188
|
do {
|
|
178
|
-
|
|
189
|
+
if (!this.flags.json) {
|
|
190
|
+
ux.action.status = `(${timeBucket}), cursor: ${cursor}`;
|
|
191
|
+
}
|
|
179
192
|
// eslint-disable-next-line no-await-in-loop
|
|
180
193
|
await graphqlClient.validateToken();
|
|
181
194
|
try {
|
|
@@ -199,7 +212,7 @@ export default class Stream extends BaseCommand {
|
|
|
199
212
|
if (error instanceof Error && error.message.includes("Token is expired")) {
|
|
200
213
|
continue;
|
|
201
214
|
}
|
|
202
|
-
|
|
215
|
+
this.warn(`Error fetching events: ${error}`);
|
|
203
216
|
}
|
|
204
217
|
} while (cursor);
|
|
205
218
|
if (dayjs(timeBucket, TIME_BUCKET_HOUR_PATTERN).isBefore(currentTime, "hour")) {
|
|
@@ -208,11 +221,13 @@ export default class Stream extends BaseCommand {
|
|
|
208
221
|
if (timeBucket === lastTimeBucket) {
|
|
209
222
|
if (!live) {
|
|
210
223
|
observer.complete();
|
|
211
|
-
|
|
224
|
+
if (!this.flags.json) {
|
|
225
|
+
ux.action.stop("Fetched all events, stopping");
|
|
226
|
+
}
|
|
212
227
|
return;
|
|
213
228
|
}
|
|
214
229
|
if (!liveMode) {
|
|
215
|
-
|
|
230
|
+
!this.flags.json && this.warn("Reached last time bucket, switching to live mode");
|
|
216
231
|
startTimeBucket = createTimebucketDayjs(currentTime);
|
|
217
232
|
liveMode = true;
|
|
218
233
|
}
|
|
@@ -14,9 +14,9 @@ export declare enum LOGIN_CODES {
|
|
|
14
14
|
export declare class ValidateLogin {
|
|
15
15
|
private readonly url;
|
|
16
16
|
constructor(url: string);
|
|
17
|
-
isExpired(config: CliConfig, client: CliConfiguration): Promise<boolean>;
|
|
17
|
+
isExpired(config: CliConfig, client: CliConfiguration, displayProgress?: boolean): Promise<boolean>;
|
|
18
18
|
tryRefreshToken(config: CliConfig, client: CliConfiguration): Promise<CliConfig>;
|
|
19
|
-
validate(config: CliConfig, client: CliConfiguration): Promise<{
|
|
19
|
+
validate(config: CliConfig, client: CliConfiguration, displayProgress?: boolean): Promise<{
|
|
20
20
|
status: LOGIN_CODES;
|
|
21
21
|
} | UserInfo & {
|
|
22
22
|
status: LOGIN_CODES;
|
|
@@ -11,16 +11,20 @@ export class ValidateLogin {
|
|
|
11
11
|
constructor(url) {
|
|
12
12
|
this.url = url;
|
|
13
13
|
}
|
|
14
|
-
async isExpired(config, client) {
|
|
14
|
+
async isExpired(config, client, displayProgress = true) {
|
|
15
15
|
const decoded = jwtDecode(config.auth?.accessToken ?? "");
|
|
16
16
|
if (decoded.exp === undefined) {
|
|
17
17
|
throw new Error("Invalid id token");
|
|
18
18
|
}
|
|
19
19
|
if (decoded.exp * 1000 < Date.now() - 5000) {
|
|
20
20
|
try {
|
|
21
|
-
|
|
21
|
+
if (displayProgress) {
|
|
22
|
+
ux.action.start("Refreshing token");
|
|
23
|
+
}
|
|
22
24
|
config = await this.tryRefreshToken(config, client);
|
|
23
|
-
|
|
25
|
+
if (displayProgress) {
|
|
26
|
+
ux.action.stop("Refreshed token");
|
|
27
|
+
}
|
|
24
28
|
return true;
|
|
25
29
|
}
|
|
26
30
|
catch (error) {
|
|
@@ -61,7 +65,7 @@ export class ValidateLogin {
|
|
|
61
65
|
});
|
|
62
66
|
return client.getConfig();
|
|
63
67
|
}
|
|
64
|
-
async validate(config, client) {
|
|
68
|
+
async validate(config, client, displayProgress = true) {
|
|
65
69
|
const response = await fetch(this.url);
|
|
66
70
|
const json = await response.json();
|
|
67
71
|
if (json.userinfo_endpoint === undefined) {
|
|
@@ -78,7 +82,7 @@ export class ValidateLogin {
|
|
|
78
82
|
};
|
|
79
83
|
}
|
|
80
84
|
try {
|
|
81
|
-
await this.isExpired(config, client);
|
|
85
|
+
await this.isExpired(config, client, displayProgress);
|
|
82
86
|
}
|
|
83
87
|
catch {
|
|
84
88
|
return {
|
package/oclif.manifest.json
CHANGED
|
@@ -71,11 +71,18 @@
|
|
|
71
71
|
"multiple": false,
|
|
72
72
|
"type": "option"
|
|
73
73
|
},
|
|
74
|
+
"json": {
|
|
75
|
+
"char": "j",
|
|
76
|
+
"description": "Output json only",
|
|
77
|
+
"name": "json",
|
|
78
|
+
"allowNo": false,
|
|
79
|
+
"type": "boolean"
|
|
80
|
+
},
|
|
74
81
|
"live": {
|
|
75
82
|
"char": "l",
|
|
76
83
|
"description": "Change to live mode when reaching last time bucket",
|
|
77
84
|
"name": "live",
|
|
78
|
-
"allowNo":
|
|
85
|
+
"allowNo": true,
|
|
79
86
|
"type": "boolean"
|
|
80
87
|
},
|
|
81
88
|
"output": {
|
|
@@ -249,5 +256,5 @@
|
|
|
249
256
|
]
|
|
250
257
|
}
|
|
251
258
|
},
|
|
252
|
-
"version": "1.0
|
|
259
|
+
"version": "1.1.0"
|
|
253
260
|
}
|
package/package.json
CHANGED
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"prestart": "npm run build",
|
|
87
87
|
"update-schema": "rover graph introspect https://graph.api.staging.flowcore.io/graphql -o schema.gql"
|
|
88
88
|
},
|
|
89
|
-
"version": "1.0
|
|
89
|
+
"version": "1.1.0",
|
|
90
90
|
"bugs": "https://github.com/flowcore-io/flowcore-cli/issues",
|
|
91
91
|
"keywords": [
|
|
92
92
|
"flowcore",
|