@flowcore/cli 1.0.2 → 1.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/CHANGELOG.md CHANGED
@@ -7,6 +7,20 @@
7
7
 
8
8
 
9
9
 
10
+ ## [1.1.1](https://github.com/flowcore-io/flowcore-cli/compare/v1.1.0...v1.1.1) (2024-01-09)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * made sure to process last events in queue on no-live ([b8daffd](https://github.com/flowcore-io/flowcore-cli/commit/b8daffd667e2b1102e24f5b867a589c9e1939375))
16
+
17
+ ## [1.1.0](https://github.com/flowcore-io/flowcore-cli/compare/v1.0.2...v1.1.0) (2024-01-09)
18
+
19
+
20
+ ### Features
21
+
22
+ * added json output ([c97b83e](https://github.com/flowcore-io/flowcore-cli/commit/c97b83e5f3f5d6172ce76e70d6331bee497b44db))
23
+
10
24
  ## [1.0.2](https://github.com/flowcore-io/flowcore-cli/compare/v1.0.1...v1.0.2) (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.2 linux-x64 node-v20.10.0
20
+ @flowcore/cli/1.1.1 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.2/src/commands/config/set.ts)_
106
+ _See code: [src/commands/config/set.ts](https://github.com/flowcore-io/flowcore-cli/blob/v1.1.1/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.2/src/commands/config/show.ts)_
126
+ _See code: [src/commands/config/show.ts](https://github.com/flowcore-io/flowcore-cli/blob/v1.1.1/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.2/src/commands/login.ts)_
169
+ _See code: [src/commands/login.ts](https://github.com/flowcore-io/flowcore-cli/blob/v1.1.1/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
- -l, --live Change to live mode when reaching last time bucket
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.2/src/commands/stream.ts)_
473
+ _See code: [src/commands/stream.ts](https://github.com/flowcore-io/flowcore-cli/blob/v1.1.1/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.2/src/commands/whoami.ts)_
510
+ _See code: [src/commands/whoami.ts](https://github.com/flowcore-io/flowcore-cli/blob/v1.1.1/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>;
@@ -14,5 +15,5 @@ export default class Stream extends BaseCommand<typeof Stream> {
14
15
  run(): Promise<void>;
15
16
  private processEvents;
16
17
  private setTimeBucket;
17
- private streanEvents;
18
+ private streamEvents;
18
19
  }
@@ -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
- live: Flags.boolean({ char: 'l', default: true, description: 'Change to live mode when reaching last time bucket' }),
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,37 +66,38 @@ 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
- ux.warn(`First time bucket not found, setting to current time (${ux.colorize("yellowBright", firstTimeBucket)})`);
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
- ux.log(ux.colorize("green", `Setting first time bucket to ${ux.colorize("yellow", flags.start)} ago (${ux.colorize("yellowBright", firstTimeBucket)})`));
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
- ux.log(ux.colorize("green", `Setting first time bucket to ${ux.colorize("yellow", "1d")} ago (${ux.colorize("yellowBright", firstTimeBucket)})`));
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
- void this.streanEvents(dataCoreId, aggregator, eventType, firstTimeBucket, lastTimeBucket, graphqlClient, observer, flags.live);
87
+ void this.streamEvents(dataCoreId, aggregator, eventType, firstTimeBucket, lastTimeBucket, graphqlClient, observer, flags.live);
87
88
  await this.processEvents(observer, flags.destination, flags.output);
88
89
  }
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
- ux.warn("Stream completed");
96
+ warn("Stream completed");
95
97
  done = true;
96
98
  },
97
99
  error(error) {
98
- ux.warn(`Stream error occurred with error ${error}`);
100
+ warn(`Stream error occurred with error ${error}`);
99
101
  done = true;
100
102
  },
101
103
  next(value) {
@@ -103,16 +105,20 @@ export default class Stream extends BaseCommand {
103
105
  }
104
106
  });
105
107
  // eslint-disable-next-line no-unmodified-loop-condition
106
- while (!done) {
108
+ while (!done || events.length > 0) {
107
109
  const processingQueue = Queue.fromArray(events);
108
110
  events = [];
109
111
  while (!processingQueue.isEmpty()) {
110
112
  const event = processingQueue.dequeue();
111
113
  if (output === "log") {
112
- ux.log(JSON.stringify(event));
114
+ ux.info(JSON.stringify(event));
115
+ this.flags.json ? this.log(JSON.stringify({
116
+ event,
117
+ type: "input",
118
+ }, null, 2)) : this.log(JSON.stringify(event));
113
119
  continue;
114
120
  }
115
- this.debug(`Sending event to ${destination}: ${event.eventId}`);
121
+ this.log(`Sending event to ${destination}: ${event.eventId}`);
116
122
  try {
117
123
  // eslint-disable-next-line no-await-in-loop
118
124
  const result = await fetch(destination, {
@@ -123,15 +129,19 @@ export default class Stream extends BaseCommand {
123
129
  method: "POST",
124
130
  });
125
131
  if (!result.ok) {
126
- this.warn(`Error sending event to ${destination}: ${result.statusText}`);
132
+ !this.flags.json && this.warn(`Error sending event to ${destination}: ${result.statusText}`);
127
133
  }
128
134
  if (result.body) {
129
- // eslint-disable-next-line no-await-in-loop
130
- ux.debug(`Response from ${destination}: ${await result.json()}`);
135
+ this.flags.json ? this.log(JSON.stringify({
136
+ // eslint-disable-next-line no-await-in-loop
137
+ event: (await result.json()),
138
+ type: "output",
139
+ // eslint-disable-next-line no-await-in-loop
140
+ }, null, 2)) : this.log(`Response from ${destination}: ${JSON.stringify(await result.json())}`);
131
141
  }
132
142
  }
133
143
  catch (error) {
134
- this.warn(`Error sending event to ${destination}: ${error}`);
144
+ !this.flags.json && this.warn(`Error sending event to ${destination}: ${error}`);
135
145
  }
136
146
  }
137
147
  if (events.length === 0) {
@@ -159,11 +169,11 @@ export default class Stream extends BaseCommand {
159
169
  return createTimebucket(date.subtract(Number.parseInt(parts[1], 10), "hour"));
160
170
  }
161
171
  default: {
162
- this.error("Invalid start time bucket");
172
+ ux.error("Invalid start time bucket");
163
173
  }
164
174
  }
165
175
  }
166
- async streanEvents(dataCoreId, aggregator, eventType, firstTimeBucket, lastTimeBucket, graphqlClient, observer, live) {
176
+ async streamEvents(dataCoreId, aggregator, eventType, firstTimeBucket, lastTimeBucket, graphqlClient, observer, live) {
167
177
  let startTimeBucket = dayjs(firstTimeBucket, TIME_BUCKET_HOUR_PATTERN);
168
178
  const currentTime = dayjs.utc();
169
179
  let liveMode = false;
@@ -171,11 +181,15 @@ export default class Stream extends BaseCommand {
171
181
  // eslint-disable-next-line no-constant-condition
172
182
  while (true) {
173
183
  const timeBucket = createTimebucket(startTimeBucket);
174
- ux.action.start(`Fetching events for ${startTimeBucket}`);
184
+ if (!this.flags.json) {
185
+ ux.action.start(`Fetching events for ${startTimeBucket}`);
186
+ }
175
187
  let cursor = null;
176
188
  let found = false;
177
189
  do {
178
- ux.action.status = `(${timeBucket}), cursor: ${cursor}`;
190
+ if (!this.flags.json) {
191
+ ux.action.status = `(${timeBucket}), cursor: ${cursor}`;
192
+ }
179
193
  // eslint-disable-next-line no-await-in-loop
180
194
  await graphqlClient.validateToken();
181
195
  try {
@@ -199,7 +213,7 @@ export default class Stream extends BaseCommand {
199
213
  if (error instanceof Error && error.message.includes("Token is expired")) {
200
214
  continue;
201
215
  }
202
- ux.warn(`Error fetching events: ${error}`);
216
+ this.warn(`Error fetching events: ${error}`);
203
217
  }
204
218
  } while (cursor);
205
219
  if (dayjs(timeBucket, TIME_BUCKET_HOUR_PATTERN).isBefore(currentTime, "hour")) {
@@ -208,11 +222,13 @@ export default class Stream extends BaseCommand {
208
222
  if (timeBucket === lastTimeBucket) {
209
223
  if (!live) {
210
224
  observer.complete();
211
- ux.action.stop("Fetched all events, stopping");
225
+ if (!this.flags.json) {
226
+ ux.action.stop("Fetched all events, stopping");
227
+ }
212
228
  return;
213
229
  }
214
230
  if (!liveMode) {
215
- ux.warn("Reached last time bucket, switching to live mode");
231
+ !this.flags.json && this.warn("Reached last time bucket, switching to live mode");
216
232
  startTimeBucket = createTimebucketDayjs(currentTime);
217
233
  liveMode = true;
218
234
  }
@@ -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
- ux.action.start("Refreshing token");
21
+ if (displayProgress) {
22
+ ux.action.start("Refreshing token");
23
+ }
22
24
  config = await this.tryRefreshToken(config, client);
23
- ux.action.stop("Refreshed token");
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 {
@@ -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": false,
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.2"
259
+ "version": "1.1.1"
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.2",
89
+ "version": "1.1.1",
90
90
  "bugs": "https://github.com/flowcore-io/flowcore-cli/issues",
91
91
  "keywords": [
92
92
  "flowcore",