@flowcore/cli 1.0.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +308 -0
  2. package/README.md +510 -0
  3. package/bin/dev.cmd +3 -0
  4. package/bin/dev.js +9 -0
  5. package/bin/run.cmd +3 -0
  6. package/bin/run.js +8 -0
  7. package/dist/base-command.d.ts +17 -0
  8. package/dist/base-command.js +32 -0
  9. package/dist/commands/config/set.d.ts +13 -0
  10. package/dist/commands/config/set.js +45 -0
  11. package/dist/commands/config/show.d.ts +6 -0
  12. package/dist/commands/config/show.js +11 -0
  13. package/dist/commands/login.d.ts +19 -0
  14. package/dist/commands/login.js +146 -0
  15. package/dist/commands/stream.d.ts +18 -0
  16. package/dist/commands/stream.js +220 -0
  17. package/dist/commands/whoami.d.ts +5 -0
  18. package/dist/commands/whoami.js +25 -0
  19. package/dist/index.d.ts +1 -0
  20. package/dist/index.js +1 -0
  21. package/dist/interfaces/source-event.interface.d.ts +18 -0
  22. package/dist/interfaces/source-event.interface.js +1 -0
  23. package/dist/utils/config.util.d.ts +31 -0
  24. package/dist/utils/config.util.js +108 -0
  25. package/dist/utils/graphql.util.d.ts +8 -0
  26. package/dist/utils/graphql.util.js +25 -0
  27. package/dist/utils/object-path.util.d.ts +1 -0
  28. package/dist/utils/object-path.util.js +17 -0
  29. package/dist/utils/queries/fetch-data-core.gql.d.ts +12 -0
  30. package/dist/utils/queries/fetch-data-core.gql.js +10 -0
  31. package/dist/utils/queries/fetch-event-type-range.gql.d.ts +21 -0
  32. package/dist/utils/queries/fetch-event-type-range.gql.js +19 -0
  33. package/dist/utils/queries/fetch-events.gql.d.ts +28 -0
  34. package/dist/utils/queries/fetch-events.gql.js +28 -0
  35. package/dist/utils/timebucket.util.d.ts +5 -0
  36. package/dist/utils/timebucket.util.js +11 -0
  37. package/dist/utils/validate-login.util.d.ts +24 -0
  38. package/dist/utils/validate-login.util.js +111 -0
  39. package/oclif.manifest.json +253 -0
  40. package/package.json +102 -0
@@ -0,0 +1,21 @@
1
+ export type FetchEventTypeRangeResponse = {
2
+ datacore: {
3
+ flowtypes: {
4
+ aggregator: string;
5
+ events: {
6
+ catalog: {
7
+ range: {
8
+ firstTimeBucket: string;
9
+ lastTimeBucket: string;
10
+ };
11
+ };
12
+ name: string;
13
+ }[];
14
+ }[];
15
+ };
16
+ };
17
+ export type FetchEventTypeRangeQueryInput = {
18
+ aggregator: string;
19
+ dataCoreId: string;
20
+ };
21
+ export declare const FETCH_EVENT_TYPE_RANGE_GQL_QUERY: string;
@@ -0,0 +1,19 @@
1
+ import { gql } from "graphql-request";
2
+ export const FETCH_EVENT_TYPE_RANGE_GQL_QUERY = gql `
3
+ query FLOWCORE_CLI_FETCH_EVENT_TYPE_RANGE($dataCoreId: ID!, $aggregator: String!) {
4
+ datacore(search: {id: $dataCoreId}) {
5
+ flowtypes(search: {aggregator: $aggregator }) {
6
+ aggregator
7
+ events {
8
+ name
9
+ catalog {
10
+ range {
11
+ firstTimeBucket
12
+ lastTimeBucket
13
+ }
14
+ }
15
+ }
16
+ }
17
+ }
18
+ }
19
+ `;
@@ -0,0 +1,28 @@
1
+ export type FetchEventsResponse = {
2
+ datacore: {
3
+ fetchEvents: {
4
+ cursor: string;
5
+ events: {
6
+ aggregator: string;
7
+ dataCore: string;
8
+ eventId: string;
9
+ eventType: string;
10
+ metadata: string;
11
+ payload: string;
12
+ timeBucket: string;
13
+ validTime: string;
14
+ }[];
15
+ };
16
+ };
17
+ };
18
+ export type FetchEventsQueryInput = {
19
+ afterEventId?: string;
20
+ aggregator: string;
21
+ beforeEventId?: string;
22
+ cursor?: string;
23
+ dataCoreId: string;
24
+ eventType: string;
25
+ pageSize?: number;
26
+ timeBucket: string;
27
+ };
28
+ export declare const FETCH_EVENTS_GQL_QUERY: string;
@@ -0,0 +1,28 @@
1
+ import { gql } from "graphql-request";
2
+ export const FETCH_EVENTS_GQL_QUERY = gql `
3
+ query FLOWCORE_CLI_FETCH_EVENTS($dataCoreId: ID!, $aggregator: String!, $eventType: String!, $timeBucket: String!, $cursor: String, $afterEventId: String, $beforeEventId: String, $pageSize: Int) {
4
+ datacore(search: {id: $dataCoreId}) {
5
+ fetchEvents(input: {
6
+ aggregator: $aggregator,
7
+ eventTypes: [$eventType],
8
+ timeBucket: $timeBucket
9
+ cursor: $cursor
10
+ afterEventId: $afterEventId
11
+ beforeEventId: $beforeEventId
12
+ pageSize: $pageSize
13
+ }) {
14
+ events {
15
+ eventId
16
+ timeBucket
17
+ eventType
18
+ aggregator
19
+ dataCore
20
+ metadata
21
+ payload
22
+ validTime
23
+ }
24
+ cursor
25
+ }
26
+ }
27
+ }
28
+ `;
@@ -0,0 +1,5 @@
1
+ import { Dayjs } from "dayjs";
2
+ export declare const TIME_BUCKET_PATTERN = "YYYYMMDDHHmmss";
3
+ export declare const TIME_BUCKET_HOUR_PATTERN = "YYYYMMDDHH";
4
+ export declare const createTimebucket: (date: Dayjs) => string;
5
+ export declare const createTimebucketDayjs: (date: Dayjs) => Dayjs;
@@ -0,0 +1,11 @@
1
+ import { TimeBucket } from "@flowcore/time-bucket";
2
+ export const TIME_BUCKET_PATTERN = "YYYYMMDDHHmmss";
3
+ export const TIME_BUCKET_HOUR_PATTERN = "YYYYMMDDHH";
4
+ export const createTimebucket = (date) => {
5
+ const timeBucket = TimeBucket.create(TIME_BUCKET_HOUR_PATTERN, TIME_BUCKET_PATTERN);
6
+ return timeBucket(date).format(TIME_BUCKET_PATTERN);
7
+ };
8
+ export const createTimebucketDayjs = (date) => {
9
+ const timeBucket = TimeBucket.create(TIME_BUCKET_HOUR_PATTERN, TIME_BUCKET_PATTERN);
10
+ return timeBucket(date);
11
+ };
@@ -0,0 +1,24 @@
1
+ import { CliConfig, CliConfiguration } from "./config.util.js";
2
+ export type UserInfo = {
3
+ family_name: string;
4
+ flowcore_user_id: string;
5
+ given_name: string;
6
+ name: string;
7
+ preferred_username: string;
8
+ };
9
+ export declare enum LOGIN_CODES {
10
+ LOGIN_EXPIRED = 2,
11
+ LOGIN_FAILED = 0,
12
+ LOGIN_SUCCESS = 1
13
+ }
14
+ export declare class ValidateLogin {
15
+ private readonly url;
16
+ constructor(url: string);
17
+ isExpired(config: CliConfig, client: CliConfiguration): Promise<boolean>;
18
+ tryRefreshToken(config: CliConfig, client: CliConfiguration): Promise<CliConfig>;
19
+ validate(config: CliConfig, client: CliConfiguration): Promise<{
20
+ status: LOGIN_CODES;
21
+ } | UserInfo & {
22
+ status: LOGIN_CODES;
23
+ }>;
24
+ }
@@ -0,0 +1,111 @@
1
+ import { ux } from "@oclif/core";
2
+ import { jwtDecode } from "jwt-decode";
3
+ export var LOGIN_CODES;
4
+ (function (LOGIN_CODES) {
5
+ LOGIN_CODES[LOGIN_CODES["LOGIN_EXPIRED"] = 2] = "LOGIN_EXPIRED";
6
+ LOGIN_CODES[LOGIN_CODES["LOGIN_FAILED"] = 0] = "LOGIN_FAILED";
7
+ LOGIN_CODES[LOGIN_CODES["LOGIN_SUCCESS"] = 1] = "LOGIN_SUCCESS";
8
+ })(LOGIN_CODES || (LOGIN_CODES = {}));
9
+ export class ValidateLogin {
10
+ url;
11
+ constructor(url) {
12
+ this.url = url;
13
+ }
14
+ async isExpired(config, client) {
15
+ const decoded = jwtDecode(config.auth?.accessToken ?? "");
16
+ if (decoded.exp === undefined) {
17
+ throw new Error("Invalid id token");
18
+ }
19
+ if (decoded.exp * 1000 < Date.now() - 5000) {
20
+ try {
21
+ ux.action.start("Refreshing token");
22
+ config = await this.tryRefreshToken(config, client);
23
+ ux.action.stop("Refreshed token");
24
+ return true;
25
+ }
26
+ catch (error) {
27
+ ux.error(`Failed to refresh token: ${error}`);
28
+ }
29
+ }
30
+ return false;
31
+ }
32
+ async tryRefreshToken(config, client) {
33
+ if (config.auth?.refreshToken === undefined) {
34
+ throw new Error("No refresh token");
35
+ }
36
+ const response = await fetch(this.url);
37
+ const json = await response.json();
38
+ if (json.token_endpoint === undefined) {
39
+ throw new Error("No token_endpoint in openid configuration");
40
+ }
41
+ const body = new URLSearchParams();
42
+ body.append("grant_type", "refresh_token");
43
+ body.append("refresh_token", config.auth.refreshToken);
44
+ body.append("client_id", config.login.clientId);
45
+ body.append("client_secret", config.login.clientSecret ?? "");
46
+ const result = await fetch(json.token_endpoint, {
47
+ body,
48
+ method: "POST",
49
+ });
50
+ if (result.status !== 200) {
51
+ throw new Error("Failed to refresh token");
52
+ }
53
+ const resultJson = await result.json();
54
+ client.setConfig({
55
+ auth: {
56
+ ...config.auth,
57
+ accessToken: resultJson.access_token,
58
+ idToken: resultJson.id_token,
59
+ refreshToken: resultJson.refresh_token,
60
+ }
61
+ });
62
+ return client.getConfig();
63
+ }
64
+ async validate(config, client) {
65
+ const response = await fetch(this.url);
66
+ const json = await response.json();
67
+ if (json.userinfo_endpoint === undefined) {
68
+ throw new Error("No userinfo_endpoint in openid configuration");
69
+ }
70
+ if (config.auth === undefined) {
71
+ return {
72
+ status: LOGIN_CODES.LOGIN_FAILED,
73
+ };
74
+ }
75
+ if (config.auth.accessToken === undefined) {
76
+ return {
77
+ status: LOGIN_CODES.LOGIN_FAILED,
78
+ };
79
+ }
80
+ try {
81
+ await this.isExpired(config, client);
82
+ }
83
+ catch {
84
+ return {
85
+ status: LOGIN_CODES.LOGIN_EXPIRED,
86
+ };
87
+ }
88
+ try {
89
+ const result = await fetch(json.userinfo_endpoint, {
90
+ headers: {
91
+ "Authorization": `Bearer ${config?.auth?.accessToken}`,
92
+ }
93
+ });
94
+ if (result.status !== 200) {
95
+ return {
96
+ status: LOGIN_CODES.LOGIN_FAILED,
97
+ };
98
+ }
99
+ return {
100
+ ...await result.json(),
101
+ status: LOGIN_CODES.LOGIN_SUCCESS,
102
+ };
103
+ }
104
+ catch (error) {
105
+ console.error(error);
106
+ return {
107
+ status: LOGIN_CODES.LOGIN_FAILED,
108
+ };
109
+ }
110
+ }
111
+ }
@@ -0,0 +1,253 @@
1
+ {
2
+ "commands": {
3
+ "login": {
4
+ "aliases": [],
5
+ "args": {},
6
+ "description": "login to the Flowcore Platform",
7
+ "examples": [
8
+ "<%= config.bin %> <%= command.id %>",
9
+ "<%= config.bin %> <%= command.id %> --port 8080"
10
+ ],
11
+ "flags": {
12
+ "profile": {
13
+ "description": "Specify the configuration profile to use",
14
+ "name": "profile",
15
+ "hasDynamicHelp": false,
16
+ "multiple": false,
17
+ "type": "option"
18
+ },
19
+ "port": {
20
+ "char": "p",
21
+ "description": "port to listen for the callback",
22
+ "name": "port",
23
+ "default": 3000,
24
+ "hasDynamicHelp": false,
25
+ "multiple": false,
26
+ "type": "option"
27
+ }
28
+ },
29
+ "hasDynamicHelp": false,
30
+ "hiddenAliases": [],
31
+ "id": "login",
32
+ "pluginAlias": "@flowcore/cli",
33
+ "pluginName": "@flowcore/cli",
34
+ "pluginType": "core",
35
+ "strict": true,
36
+ "enableJsonFlag": false,
37
+ "isESM": true,
38
+ "relativePath": [
39
+ "dist",
40
+ "commands",
41
+ "login.js"
42
+ ]
43
+ },
44
+ "stream": {
45
+ "aliases": [],
46
+ "args": {
47
+ "STREAM": {
48
+ "description": "stream url to connect to",
49
+ "name": "STREAM",
50
+ "required": true
51
+ }
52
+ },
53
+ "description": "Stream events from a datacore running on the Flowcore Platform",
54
+ "examples": [
55
+ "<%= config.bin %> <%= command.id %> https://staging.flowcore.io/flowcore/flowcore-platform/organization.0/event.organization.subscription.updated-requested.0.stream"
56
+ ],
57
+ "flags": {
58
+ "profile": {
59
+ "description": "Specify the configuration profile to use",
60
+ "name": "profile",
61
+ "hasDynamicHelp": false,
62
+ "multiple": false,
63
+ "type": "option"
64
+ },
65
+ "destination": {
66
+ "char": "d",
67
+ "description": "Destination to send events to",
68
+ "name": "destination",
69
+ "default": "http://localhost:3000/transform",
70
+ "hasDynamicHelp": false,
71
+ "multiple": false,
72
+ "type": "option"
73
+ },
74
+ "live": {
75
+ "char": "l",
76
+ "description": "Change to live mode when reaching last time bucket",
77
+ "name": "live",
78
+ "allowNo": false,
79
+ "type": "boolean"
80
+ },
81
+ "output": {
82
+ "char": "o",
83
+ "description": "Output format",
84
+ "name": "output",
85
+ "default": "http",
86
+ "hasDynamicHelp": false,
87
+ "multiple": false,
88
+ "options": [
89
+ "http",
90
+ "log"
91
+ ],
92
+ "type": "option"
93
+ },
94
+ "start": {
95
+ "char": "s",
96
+ "description": "Start time bucket to stream from, example: (1y, 1m, 1d, 1h)",
97
+ "name": "start",
98
+ "hasDynamicHelp": false,
99
+ "multiple": false,
100
+ "type": "option"
101
+ }
102
+ },
103
+ "hasDynamicHelp": false,
104
+ "hiddenAliases": [],
105
+ "id": "stream",
106
+ "pluginAlias": "@flowcore/cli",
107
+ "pluginName": "@flowcore/cli",
108
+ "pluginType": "core",
109
+ "strict": true,
110
+ "enableJsonFlag": false,
111
+ "isESM": true,
112
+ "relativePath": [
113
+ "dist",
114
+ "commands",
115
+ "stream.js"
116
+ ]
117
+ },
118
+ "whoami": {
119
+ "aliases": [],
120
+ "args": {},
121
+ "description": "Check what user you are logged in as",
122
+ "flags": {
123
+ "profile": {
124
+ "description": "Specify the configuration profile to use",
125
+ "name": "profile",
126
+ "hasDynamicHelp": false,
127
+ "multiple": false,
128
+ "type": "option"
129
+ }
130
+ },
131
+ "hasDynamicHelp": false,
132
+ "hiddenAliases": [],
133
+ "id": "whoami",
134
+ "pluginAlias": "@flowcore/cli",
135
+ "pluginName": "@flowcore/cli",
136
+ "pluginType": "core",
137
+ "strict": true,
138
+ "enableJsonFlag": false,
139
+ "isESM": true,
140
+ "relativePath": [
141
+ "dist",
142
+ "commands",
143
+ "whoami.js"
144
+ ]
145
+ },
146
+ "config:set": {
147
+ "aliases": [],
148
+ "args": {},
149
+ "description": "Configure the cli",
150
+ "examples": [
151
+ "<%= config.bin %> <%= command.id %> -l https://auth.flowcore.io/realms/flowcore/.well-known/openid-configuration -c my-client-id -s my-client-secret",
152
+ "<%= config.bin %> <%= command.id %> -u https://graph.api.flowcore.io/graphql",
153
+ "<%= config.bin %> <%= command.id %> -l https://auth.flowcore.io/realms/flowcore/.well-known/openid-configuration -c my-client-id -p"
154
+ ],
155
+ "flags": {
156
+ "profile": {
157
+ "description": "Specify the configuration profile to use",
158
+ "name": "profile",
159
+ "hasDynamicHelp": false,
160
+ "multiple": false,
161
+ "type": "option"
162
+ },
163
+ "clientId": {
164
+ "char": "c",
165
+ "description": "client id to use for the login",
166
+ "name": "clientId",
167
+ "hasDynamicHelp": false,
168
+ "multiple": false,
169
+ "type": "option"
170
+ },
171
+ "clientSecret": {
172
+ "char": "n",
173
+ "description": "name to print",
174
+ "name": "clientSecret",
175
+ "hasDynamicHelp": false,
176
+ "multiple": false,
177
+ "type": "option"
178
+ },
179
+ "loginUrl": {
180
+ "char": "l",
181
+ "description": "url to discover the openid configuration",
182
+ "name": "loginUrl",
183
+ "hasDynamicHelp": false,
184
+ "multiple": false,
185
+ "type": "option"
186
+ },
187
+ "port": {
188
+ "char": "p",
189
+ "description": "prompt for port to listen for the callback",
190
+ "name": "port",
191
+ "allowNo": false,
192
+ "type": "boolean"
193
+ },
194
+ "url": {
195
+ "char": "u",
196
+ "description": "url to the flowcore platform api",
197
+ "name": "url",
198
+ "hasDynamicHelp": false,
199
+ "multiple": false,
200
+ "type": "option"
201
+ }
202
+ },
203
+ "hasDynamicHelp": false,
204
+ "hiddenAliases": [],
205
+ "id": "config:set",
206
+ "pluginAlias": "@flowcore/cli",
207
+ "pluginName": "@flowcore/cli",
208
+ "pluginType": "core",
209
+ "strict": true,
210
+ "enableJsonFlag": false,
211
+ "isESM": true,
212
+ "relativePath": [
213
+ "dist",
214
+ "commands",
215
+ "config",
216
+ "set.js"
217
+ ]
218
+ },
219
+ "config:show": {
220
+ "aliases": [],
221
+ "args": {},
222
+ "description": "Show the configured login url",
223
+ "examples": [
224
+ "<%= config.bin %> <%= command.id %>"
225
+ ],
226
+ "flags": {
227
+ "profile": {
228
+ "description": "Specify the configuration profile to use",
229
+ "name": "profile",
230
+ "hasDynamicHelp": false,
231
+ "multiple": false,
232
+ "type": "option"
233
+ }
234
+ },
235
+ "hasDynamicHelp": false,
236
+ "hiddenAliases": [],
237
+ "id": "config:show",
238
+ "pluginAlias": "@flowcore/cli",
239
+ "pluginName": "@flowcore/cli",
240
+ "pluginType": "core",
241
+ "strict": true,
242
+ "enableJsonFlag": false,
243
+ "isESM": true,
244
+ "relativePath": [
245
+ "dist",
246
+ "commands",
247
+ "config",
248
+ "show.js"
249
+ ]
250
+ }
251
+ },
252
+ "version": "1.0.1"
253
+ }
package/package.json ADDED
@@ -0,0 +1,102 @@
1
+ {
2
+ "author": "Flowcore",
3
+ "bin": {
4
+ "flowcore": "./bin/run.js"
5
+ },
6
+ "dependencies": {
7
+ "@datastructures-js/queue": "^4.2.3",
8
+ "@flowcore/time-bucket": "^1.1.0",
9
+ "@oclif/core": "^3",
10
+ "@oclif/plugin-autocomplete": "^3.0.2",
11
+ "@oclif/plugin-help": "^5",
12
+ "@oclif/plugin-not-found": "^3.0.4",
13
+ "@oclif/plugin-plugins": "^4",
14
+ "@oclif/plugin-version": "^2.0.8",
15
+ "axios": "^1.6.2",
16
+ "cross-fetch": "^4.0.0",
17
+ "dayjs": "^1.11.10",
18
+ "enquirer": "^2.4.1",
19
+ "express": "^4.18.2",
20
+ "graphql": "^16.8.1",
21
+ "graphql-request": "^6.1.0",
22
+ "jwt-decode": "^4.0.0",
23
+ "lodash": "^4.17.21",
24
+ "merge": "^2.1.1",
25
+ "open": "^9.1.0",
26
+ "openid-client": "^5.6.1",
27
+ "rxjs": "^7.8.1",
28
+ "session": "^0.1.0"
29
+ },
30
+ "description": "Flowcore CLI for interacting with the Flowcore platform",
31
+ "devDependencies": {
32
+ "@oclif/prettier-config": "^0.2.1",
33
+ "@oclif/test": "^3",
34
+ "@types/chai": "^4",
35
+ "@types/express": "^4.17.21",
36
+ "@types/lodash": "^4.14.202",
37
+ "@types/mocha": "^10",
38
+ "@types/node": "^18",
39
+ "chai": "^4",
40
+ "eslint": "^8",
41
+ "eslint-config-oclif": "^5",
42
+ "eslint-config-oclif-typescript": "^3",
43
+ "eslint-config-prettier": "^9.0.0",
44
+ "mocha": "^10",
45
+ "oclif": "^4.0.4",
46
+ "shx": "^0.3.4",
47
+ "ts-node": "^10.9.1",
48
+ "typescript": "^5"
49
+ },
50
+ "engines": {
51
+ "node": ">=18.0.0"
52
+ },
53
+ "files": [
54
+ "/bin",
55
+ "/dist",
56
+ "/oclif.manifest.json"
57
+ ],
58
+ "homepage": "https://github.com/flowcore-io/flowcore-cli",
59
+ "license": "MIT",
60
+ "main": "",
61
+ "name": "@flowcore/cli",
62
+ "oclif": {
63
+ "bin": "flowcore",
64
+ "dirname": "flowcore",
65
+ "commands": "./dist/commands",
66
+ "plugins": [
67
+ "@oclif/plugin-help",
68
+ "@oclif/plugin-version",
69
+ "@oclif/plugin-plugins",
70
+ "@oclif/plugin-autocomplete",
71
+ "@oclif/plugin-not-found"
72
+ ],
73
+ "topicSeparator": " "
74
+ },
75
+ "repository": "flowcore-io/flowcore-cli",
76
+ "scripts": {
77
+ "build": "shx rm -rf dist && tsc -b",
78
+ "lint": "eslint . --ext .ts",
79
+ "postpack": "shx rm -f oclif.manifest.json",
80
+ "posttest": "npm run lint",
81
+ "prepack": "npm run build && oclif manifest && oclif readme",
82
+ "prepare": "npm run build",
83
+ "test": "mocha --forbid-only \"test/**/*.test.ts\"",
84
+ "version": "oclif readme && git add README.md",
85
+ "start": "./bin/run.js",
86
+ "prestart": "npm run build",
87
+ "update-schema": "rover graph introspect https://graph.api.staging.flowcore.io/graphql -o schema.gql"
88
+ },
89
+ "version": "1.0.1",
90
+ "bugs": "https://github.com/flowcore-io/flowcore-cli/issues",
91
+ "keywords": [
92
+ "flowcore",
93
+ "cli",
94
+ "flowcore-cli",
95
+ "streaming",
96
+ "events",
97
+ "data"
98
+ ],
99
+ "types": "dist/index.d.ts",
100
+ "exports": "./lib/index.js",
101
+ "type": "module"
102
+ }