@flowcore/cli 1.0.1 → 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 +14 -0
- package/README.md +9 -8
- package/dist/commands/stream.d.ts +1 -0
- package/dist/commands/stream.js +43 -22
- package/dist/utils/validate-login.util.d.ts +2 -2
- package/dist/utils/validate-login.util.js +9 -5
- package/oclif.manifest.json +9 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,20 @@
|
|
|
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
|
+
|
|
17
|
+
## [1.0.2](https://github.com/flowcore-io/flowcore-cli/compare/v1.0.1...v1.0.2) (2024-01-09)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
* dont set start if it is defined ([72a275d](https://github.com/flowcore-io/flowcore-cli/commit/72a275d4b63195a4e594581550df8bd1fa6a6c8b))
|
|
23
|
+
|
|
10
24
|
## [1.0.1](https://github.com/flowcore-io/flowcore-cli/compare/v1.0.0...v1.0.1) (2024-01-09)
|
|
11
25
|
|
|
12
26
|
|
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,17 +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)})`);
|
|
70
|
+
}
|
|
71
|
+
if (flags.start) {
|
|
72
|
+
firstTimeBucket = this.setTimeBucket(dayjs(), flags.start);
|
|
73
|
+
!this.flags.json && this.log(ux.colorize("green", `Setting first time bucket to ${ux.colorize("yellow", flags.start)} ago (${ux.colorize("yellowBright", firstTimeBucket)})`));
|
|
74
|
+
}
|
|
75
|
+
if (!firstTimeBucket) {
|
|
76
|
+
firstTimeBucket = this.setTimeBucket(dayjs(), "1d");
|
|
77
|
+
!this.flags.json && this.log(ux.colorize("green", `Setting first time bucket to ${ux.colorize("yellow", "1d")} ago (${ux.colorize("yellowBright", firstTimeBucket)})`));
|
|
69
78
|
}
|
|
70
|
-
firstTimeBucket = this.setTimeBucket(dayjs(), flags.start || "1d");
|
|
71
|
-
ux.log(ux.colorize("green", `Setting first time bucket to ${ux.colorize("yellow", flags.start || "1d")} ago (${ux.colorize("yellowBright", firstTimeBucket)})`));
|
|
72
79
|
let lastTimeBucket = eventRangeRequest.datacore.flowtypes.find(ft => ft.aggregator === aggregator)?.events.find(e => e.name === eventType)?.catalog.range.lastTimeBucket;
|
|
73
80
|
if (!lastTimeBucket) {
|
|
74
81
|
lastTimeBucket = createTimebucket(dayjs());
|
|
75
|
-
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)})`);
|
|
76
83
|
}
|
|
77
84
|
const observer = new Subject();
|
|
78
|
-
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`));
|
|
79
86
|
// eslint-disable-next-line no-void
|
|
80
87
|
void this.streanEvents(dataCoreId, aggregator, eventType, firstTimeBucket, lastTimeBucket, graphqlClient, observer, flags.live);
|
|
81
88
|
await this.processEvents(observer, flags.destination, flags.output);
|
|
@@ -83,13 +90,14 @@ export default class Stream extends BaseCommand {
|
|
|
83
90
|
async processEvents(observer, destination, output) {
|
|
84
91
|
let done = false;
|
|
85
92
|
let events = [];
|
|
93
|
+
const warn = this.flags.json ? () => { } : this.warn.bind(this);
|
|
86
94
|
observer.subscribe({
|
|
87
95
|
complete() {
|
|
88
|
-
|
|
96
|
+
warn("Stream completed");
|
|
89
97
|
done = true;
|
|
90
98
|
},
|
|
91
99
|
error(error) {
|
|
92
|
-
|
|
100
|
+
warn(`Stream error occurred with error ${error}`);
|
|
93
101
|
done = true;
|
|
94
102
|
},
|
|
95
103
|
next(value) {
|
|
@@ -103,10 +111,13 @@ export default class Stream extends BaseCommand {
|
|
|
103
111
|
while (!processingQueue.isEmpty()) {
|
|
104
112
|
const event = processingQueue.dequeue();
|
|
105
113
|
if (output === "log") {
|
|
106
|
-
|
|
114
|
+
this.flags.json ? this.log(JSON.stringify({
|
|
115
|
+
event,
|
|
116
|
+
type: "input",
|
|
117
|
+
}, null, 2)) : this.log(JSON.stringify(event));
|
|
107
118
|
continue;
|
|
108
119
|
}
|
|
109
|
-
this.
|
|
120
|
+
this.log(`Sending event to ${destination}: ${event.eventId}`);
|
|
110
121
|
try {
|
|
111
122
|
// eslint-disable-next-line no-await-in-loop
|
|
112
123
|
const result = await fetch(destination, {
|
|
@@ -117,15 +128,19 @@ export default class Stream extends BaseCommand {
|
|
|
117
128
|
method: "POST",
|
|
118
129
|
});
|
|
119
130
|
if (!result.ok) {
|
|
120
|
-
this.warn(`Error sending event to ${destination}: ${result.statusText}`);
|
|
131
|
+
!this.flags.json && this.warn(`Error sending event to ${destination}: ${result.statusText}`);
|
|
121
132
|
}
|
|
122
133
|
if (result.body) {
|
|
123
|
-
|
|
124
|
-
|
|
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())}`);
|
|
125
140
|
}
|
|
126
141
|
}
|
|
127
142
|
catch (error) {
|
|
128
|
-
this.warn(`Error sending event to ${destination}: ${error}`);
|
|
143
|
+
!this.flags.json && this.warn(`Error sending event to ${destination}: ${error}`);
|
|
129
144
|
}
|
|
130
145
|
}
|
|
131
146
|
if (events.length === 0) {
|
|
@@ -153,7 +168,7 @@ export default class Stream extends BaseCommand {
|
|
|
153
168
|
return createTimebucket(date.subtract(Number.parseInt(parts[1], 10), "hour"));
|
|
154
169
|
}
|
|
155
170
|
default: {
|
|
156
|
-
|
|
171
|
+
ux.error("Invalid start time bucket");
|
|
157
172
|
}
|
|
158
173
|
}
|
|
159
174
|
}
|
|
@@ -165,11 +180,15 @@ export default class Stream extends BaseCommand {
|
|
|
165
180
|
// eslint-disable-next-line no-constant-condition
|
|
166
181
|
while (true) {
|
|
167
182
|
const timeBucket = createTimebucket(startTimeBucket);
|
|
168
|
-
|
|
183
|
+
if (!this.flags.json) {
|
|
184
|
+
ux.action.start(`Fetching events for ${startTimeBucket}`);
|
|
185
|
+
}
|
|
169
186
|
let cursor = null;
|
|
170
187
|
let found = false;
|
|
171
188
|
do {
|
|
172
|
-
|
|
189
|
+
if (!this.flags.json) {
|
|
190
|
+
ux.action.status = `(${timeBucket}), cursor: ${cursor}`;
|
|
191
|
+
}
|
|
173
192
|
// eslint-disable-next-line no-await-in-loop
|
|
174
193
|
await graphqlClient.validateToken();
|
|
175
194
|
try {
|
|
@@ -193,7 +212,7 @@ export default class Stream extends BaseCommand {
|
|
|
193
212
|
if (error instanceof Error && error.message.includes("Token is expired")) {
|
|
194
213
|
continue;
|
|
195
214
|
}
|
|
196
|
-
|
|
215
|
+
this.warn(`Error fetching events: ${error}`);
|
|
197
216
|
}
|
|
198
217
|
} while (cursor);
|
|
199
218
|
if (dayjs(timeBucket, TIME_BUCKET_HOUR_PATTERN).isBefore(currentTime, "hour")) {
|
|
@@ -202,11 +221,13 @@ export default class Stream extends BaseCommand {
|
|
|
202
221
|
if (timeBucket === lastTimeBucket) {
|
|
203
222
|
if (!live) {
|
|
204
223
|
observer.complete();
|
|
205
|
-
|
|
224
|
+
if (!this.flags.json) {
|
|
225
|
+
ux.action.stop("Fetched all events, stopping");
|
|
226
|
+
}
|
|
206
227
|
return;
|
|
207
228
|
}
|
|
208
229
|
if (!liveMode) {
|
|
209
|
-
|
|
230
|
+
!this.flags.json && this.warn("Reached last time bucket, switching to live mode");
|
|
210
231
|
startTimeBucket = createTimebucketDayjs(currentTime);
|
|
211
232
|
liveMode = true;
|
|
212
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",
|