@knime/hub-features 1.22.4 → 1.24.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 +19 -0
- package/package.json +10 -5
- package/src/analytics/README.md +87 -0
- package/src/analytics/analytics.ts +76 -0
- package/src/analytics/index.ts +2 -0
- package/src/analytics/schema/schema.d.ts +129 -0
- package/src/analytics/schema/schema.json +472 -0
- package/src/analytics/types.ts +62 -0
- package/src/analytics/utils/eventIds.ts +35 -0
- package/src/analytics/utils/toSnakeCaseDeep.ts +33 -0
- package/src/authUtils/README.md +123 -0
- package/src/authUtils/client.ts +18 -0
- package/src/authUtils/index.ts +61 -0
- package/src/authUtils/logger.ts +1 -0
- package/src/authUtils/refresher.ts +65 -0
- package/src/authUtils/shared.ts +25 -0
- package/src/authUtils/types.ts +12 -0
- package/src/authUtils/useAuthState.ts +19 -0
- package/src/components/versions/composables/useVersionsApi.ts +55 -31
- package/src/embeddingSDK/types.ts +7 -2
- package/src/index.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @knime/hub-features
|
|
2
2
|
|
|
3
|
+
## 1.24.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 9bc7a76: Add authUtils package
|
|
8
|
+
- 182d2d7: Added analyticEvents package. See README for usage and feature overview
|
|
9
|
+
Changed embeddingSDK analytics property on the embedding context.
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Updated dependencies [c4e0bfd]
|
|
14
|
+
- @knime/components@1.45.10
|
|
15
|
+
|
|
16
|
+
## 1.23.0
|
|
17
|
+
|
|
18
|
+
### Minor Changes
|
|
19
|
+
|
|
20
|
+
- 368d638: Fix error when fetching versions if user that created the version is no longer available
|
|
21
|
+
|
|
3
22
|
## 1.22.4
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@knime/hub-features",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.24.0",
|
|
4
4
|
"description": "Vue components & composables for shared hub features",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,6 +26,9 @@
|
|
|
26
26
|
},
|
|
27
27
|
"./versions": {
|
|
28
28
|
"import": "./src/components/versions/index.ts"
|
|
29
|
+
},
|
|
30
|
+
"./analytics": {
|
|
31
|
+
"import": "./src/analytics/index.ts"
|
|
29
32
|
}
|
|
30
33
|
},
|
|
31
34
|
"dependencies": {
|
|
@@ -36,9 +39,9 @@
|
|
|
36
39
|
"lodash-es": "4.17.23",
|
|
37
40
|
"ofetch": "^1.4.1",
|
|
38
41
|
"typescript": "^5.9.3",
|
|
39
|
-
"@knime/components": "1.45.
|
|
40
|
-
"@knime/
|
|
41
|
-
"@knime/
|
|
42
|
+
"@knime/components": "1.45.10",
|
|
43
|
+
"@knime/styles": "1.15.0",
|
|
44
|
+
"@knime/utils": "1.10.1"
|
|
42
45
|
},
|
|
43
46
|
"peerDependencies": {
|
|
44
47
|
"consola": "3.x",
|
|
@@ -48,6 +51,7 @@
|
|
|
48
51
|
"@vitejs/plugin-vue": "^6.0.1",
|
|
49
52
|
"@vue/test-utils": "2.4.6",
|
|
50
53
|
"consola": "3.4.2",
|
|
54
|
+
"json-schema-to-typescript": "^15.0.4",
|
|
51
55
|
"vite": "^7.3.1",
|
|
52
56
|
"vite-svg-loader": "5.1.0",
|
|
53
57
|
"vue-tsc": "3.0.4"
|
|
@@ -57,6 +61,7 @@
|
|
|
57
61
|
},
|
|
58
62
|
"scripts": {
|
|
59
63
|
"type-check": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
|
|
60
|
-
"test:unit": "TZ='Europe/Berlin' vitest"
|
|
64
|
+
"test:unit": "TZ='Europe/Berlin' vitest",
|
|
65
|
+
"generate-analytics-events": "json2ts ./src/analyticsEvents/schema/schema.json > ./src/analyticsEvents/schema/schema.d.ts"
|
|
61
66
|
}
|
|
62
67
|
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# @knime/hub-features — analyticsEvents
|
|
2
|
+
|
|
3
|
+
## Preface
|
|
4
|
+
|
|
5
|
+
This package provides helpers related to analytics events. It provides 2 main behaviors:
|
|
6
|
+
|
|
7
|
+
- Provides a builder function that creates and normalizes event payloads
|
|
8
|
+
- Provides a sender function that forwards a given event payload to a third party API.
|
|
9
|
+
The implementation of the third-party API is up to the consumer as long as it satisfies the adapter contract
|
|
10
|
+
|
|
11
|
+
The main reason why these behaviors are split are for increased flexibility and to also enable this feature being used on a setup where the creation of the event is separated to the sending of the event. Like for example, on host/guest application combination where there's an iframe communication layer between both apps.
|
|
12
|
+
|
|
13
|
+
## Usage examples
|
|
14
|
+
|
|
15
|
+
### Building event payloads
|
|
16
|
+
|
|
17
|
+
As stated above, some applications might only care (or have the correct setup) to construct events based on different code paths and user interactions.
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { analyticsEvents } from "@knime/hub-features/analytics";
|
|
21
|
+
|
|
22
|
+
// Create a small factory bound to your context
|
|
23
|
+
const { newEvent } = analyticsEvents.eventBuilder({ jobId: "job-123" });
|
|
24
|
+
|
|
25
|
+
// Build an event. Event ids are strongly-typed, combining their special format
|
|
26
|
+
// and validating payloads, if required
|
|
27
|
+
const event = newEvent({ id: "kai_prompted::kaiqa_button_prompt" });
|
|
28
|
+
|
|
29
|
+
// once you have this event object, the application can decide what to do with it.
|
|
30
|
+
// Whether that is sending it through an iframe as a message or some other event bus. OR sending it directly if it's so desired (see Sending events section)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Keeping types in-sync
|
|
34
|
+
|
|
35
|
+
Due to the strict-type setup and the shape of the API of this package, reusing the type signature
|
|
36
|
+
of the function that creates the events can be tricky. However, this is helpful, for example, if you want to
|
|
37
|
+
wrap the create function with some other behavior of your own but keep the strong-type guarantees.
|
|
38
|
+
Below you can see an example for this use-case:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import {
|
|
42
|
+
analyticsEvents,
|
|
43
|
+
type CreateEventFn,
|
|
44
|
+
} from "@knime/hub-features/analytics";
|
|
45
|
+
|
|
46
|
+
const myCustomFunction: CreateEventFn = (...args) => {
|
|
47
|
+
const { newEvent } = analyticsEvents.eventBuilder(TheNeededContext);
|
|
48
|
+
|
|
49
|
+
// do something before
|
|
50
|
+
|
|
51
|
+
const event = newEvent(...args);
|
|
52
|
+
|
|
53
|
+
// do something with event
|
|
54
|
+
};
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
In this case, the type signature of `myCustomFunction` will match the one of `newEvent`, just like
|
|
58
|
+
in the previous example.
|
|
59
|
+
|
|
60
|
+
### Sending events
|
|
61
|
+
|
|
62
|
+
As stated in the preface, to send events, you need to provide an adapter
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
import {
|
|
66
|
+
analyticsEvents,
|
|
67
|
+
type AnalyticsAdapter,
|
|
68
|
+
} from "@knime/hub-features/analytics";
|
|
69
|
+
|
|
70
|
+
const myAdapter: AnalyticsAdapter = {
|
|
71
|
+
sendEvent({ event, metadata, idParser }) {
|
|
72
|
+
console.log("Parsed id", idParser(event.id));
|
|
73
|
+
console.log("Metadata", metadata);
|
|
74
|
+
console.log("Event", event)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// create the sender
|
|
79
|
+
const sender = analyticsEvents.eventSender(myAdapter);
|
|
80
|
+
|
|
81
|
+
// some event
|
|
82
|
+
const { newEvent } = analyticsEvents.eventBuilder(TheNeededContext);
|
|
83
|
+
const event = newEvent(...);
|
|
84
|
+
|
|
85
|
+
// then...
|
|
86
|
+
sender.send(event);
|
|
87
|
+
```
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AnalyticsAdapter,
|
|
3
|
+
AnalyticsPayload,
|
|
4
|
+
Context,
|
|
5
|
+
CreateEventFn,
|
|
6
|
+
Metadata,
|
|
7
|
+
} from "./types";
|
|
8
|
+
import { eventID } from "./utils/eventIds";
|
|
9
|
+
import { toSnakeCaseDeep } from "./utils/toSnakeCaseDeep";
|
|
10
|
+
|
|
11
|
+
const METADATA: Metadata = Object.freeze({
|
|
12
|
+
version: "v1.0",
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const analyticsEvents = Object.freeze({
|
|
16
|
+
METADATA,
|
|
17
|
+
/**
|
|
18
|
+
* This function creates an event builder, which lets you construct correct and strongly-typed
|
|
19
|
+
* event payloads according to the current schema used by this package
|
|
20
|
+
* @param context
|
|
21
|
+
* @returns
|
|
22
|
+
*/
|
|
23
|
+
eventBuilder: (context: Context) => {
|
|
24
|
+
const newEvent: CreateEventFn<AnalyticsPayload> = ({
|
|
25
|
+
id: eventId,
|
|
26
|
+
payload: eventData,
|
|
27
|
+
}) => {
|
|
28
|
+
// runtime check just in case - shouldn't be needed due to TS
|
|
29
|
+
if (!eventID(eventId).isValid()) {
|
|
30
|
+
throw new Error(`Implementation error: Invalid event id: ${eventId}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const uniqueEventId = crypto.randomUUID();
|
|
34
|
+
|
|
35
|
+
const data = (() => {
|
|
36
|
+
if (!eventData) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return toSnakeCaseDeep((eventData as unknown) ?? {});
|
|
41
|
+
})();
|
|
42
|
+
|
|
43
|
+
const event = {
|
|
44
|
+
id: `editor_${eventId}`,
|
|
45
|
+
data: {
|
|
46
|
+
...data,
|
|
47
|
+
// eslint-disable-next-line camelcase
|
|
48
|
+
job_id: context.jobId,
|
|
49
|
+
// eslint-disable-next-line camelcase
|
|
50
|
+
event_id: uniqueEventId,
|
|
51
|
+
timestamp: new Date().toISOString(),
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return event;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
return { newEvent };
|
|
59
|
+
},
|
|
60
|
+
/**
|
|
61
|
+
* This function creates an event sender which will use an internal implementation of an adapter
|
|
62
|
+
* to forward the event's data to an external third-party provider
|
|
63
|
+
* @returns
|
|
64
|
+
*/
|
|
65
|
+
eventSender: (adapter: AnalyticsAdapter) => {
|
|
66
|
+
const send = (event: AnalyticsPayload) => {
|
|
67
|
+
adapter.sendEvent({
|
|
68
|
+
event,
|
|
69
|
+
idParser: eventID(event.id).parse,
|
|
70
|
+
metadata: METADATA,
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
return { send };
|
|
75
|
+
},
|
|
76
|
+
});
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file was automatically generated by json-schema-to-typescript.
|
|
3
|
+
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
|
|
4
|
+
* and run json-schema-to-typescript to regenerate this file.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export type EmptyPayload = null;
|
|
8
|
+
export type NodeCreated_Base = NodeCreated_Common & NodeOrComponent;
|
|
9
|
+
export type NodeCreated_WithOptionalConnectedTo = NodeCreated_Common &
|
|
10
|
+
NodeOrComponent & {
|
|
11
|
+
connectedTo?: ConnectedTo_Basic;
|
|
12
|
+
};
|
|
13
|
+
export type NodeCreated_WithOptionalConnectedToPorts = NodeCreated_Common &
|
|
14
|
+
NodeOrComponent & {
|
|
15
|
+
connectedTo?: ConnectedTo_WithPorts;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export interface AnalyticsEventSchema {
|
|
19
|
+
version: "v1.0";
|
|
20
|
+
events: {
|
|
21
|
+
kai_prompted: {
|
|
22
|
+
kaiqa_button_: EmptyPayload;
|
|
23
|
+
kaibuild_button_: EmptyPayload;
|
|
24
|
+
qam_button_: KaiPromptedPayload;
|
|
25
|
+
};
|
|
26
|
+
layouteditor_opened: {
|
|
27
|
+
canvas_ctxmenu_openlayouteditor: EmptyPayload;
|
|
28
|
+
keyboard_shortcut_openlayouteditor: EmptyPayload;
|
|
29
|
+
wftoolbar_button_openlayouteditor: EmptyPayload;
|
|
30
|
+
};
|
|
31
|
+
annotation_created: {
|
|
32
|
+
canvas_ctxmenu_newannotation: EmptyPayload;
|
|
33
|
+
canvas_ctxmenu_kaiexplain: EmptyPayload;
|
|
34
|
+
};
|
|
35
|
+
workflow_saved: {
|
|
36
|
+
wftoolbar_button_save: WorkflowSavedPayload;
|
|
37
|
+
keyboard_shortcut_savewf: WorkflowSavedPayload;
|
|
38
|
+
};
|
|
39
|
+
connection_created: {
|
|
40
|
+
port_dragdrop_fwd: ConnectionCreatedPayload;
|
|
41
|
+
port_dragdrop_bwd: ConnectionCreatedPayload;
|
|
42
|
+
keyboard_shortcut_connectnodes: EmptyPayload;
|
|
43
|
+
keyboard_shortcut_connectflowvar: EmptyPayload;
|
|
44
|
+
canvas_ctxmenu_connectnodes: EmptyPayload;
|
|
45
|
+
canvas_ctxmenu_connectflowvar: EmptyPayload;
|
|
46
|
+
};
|
|
47
|
+
node_created: {
|
|
48
|
+
noderepo_dragdrop_: NodeCreated_Base;
|
|
49
|
+
noderepo_doubleclick_: NodeCreated_WithOptionalConnectedTo;
|
|
50
|
+
noderepo_keyboard_enter: NodeCreated_WithOptionalConnectedTo;
|
|
51
|
+
explorer_dragdrop_: NodeCreated_Base;
|
|
52
|
+
qam_click_: NodeCreated_WithOptionalConnectedToPorts;
|
|
53
|
+
qam_keyboard_enter: NodeCreated_WithOptionalConnectedToPorts;
|
|
54
|
+
kaiqa_dragdrop_: NodeCreated_Base;
|
|
55
|
+
kaiqa_keyboard_enter: NodeCreated_Base;
|
|
56
|
+
};
|
|
57
|
+
qam_opened: {
|
|
58
|
+
port_dragdrop_fwd: QAMOpened_Payload;
|
|
59
|
+
port_dragdrop_bwd: QAMOpened_Payload;
|
|
60
|
+
canvas_doubleclick_: EmptyPayload;
|
|
61
|
+
keyboard_shortcut_: QAMOpened_PartialPayload;
|
|
62
|
+
canvas_ctxmenu_quickaddnode: EmptyPayload;
|
|
63
|
+
};
|
|
64
|
+
node_searched: {
|
|
65
|
+
noderepo_type_: SearchPayload;
|
|
66
|
+
qam_type_: SearchPayload;
|
|
67
|
+
};
|
|
68
|
+
sidepanel_opened: {
|
|
69
|
+
sidepanel_click_info: EmptyPayload;
|
|
70
|
+
sidepanel_click_noderepo: EmptyPayload;
|
|
71
|
+
sidepanel_click_explorer: EmptyPayload;
|
|
72
|
+
sidepanel_click_kai: EmptyPayload;
|
|
73
|
+
sidepanel_click_monitor: EmptyPayload;
|
|
74
|
+
wftoolbar_dropdownmenu_versionhistory: EmptyPayload;
|
|
75
|
+
};
|
|
76
|
+
action_undone: {
|
|
77
|
+
wftoolbar_button_: EmptyPayload;
|
|
78
|
+
keyboard_shortcut_: EmptyPayload;
|
|
79
|
+
};
|
|
80
|
+
action_redone: {
|
|
81
|
+
wftoolbar_button_: EmptyPayload;
|
|
82
|
+
keyboard_shortcut_: EmptyPayload;
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
export interface KaiPromptedPayload {
|
|
87
|
+
nodeFactoryId?: string;
|
|
88
|
+
nodeType?: "node" | "component" | "metanode";
|
|
89
|
+
}
|
|
90
|
+
export interface WorkflowSavedPayload {
|
|
91
|
+
isAutosyncEnabled: boolean;
|
|
92
|
+
}
|
|
93
|
+
export interface ConnectionCreatedPayload {
|
|
94
|
+
fromNode: ConnectionCreated_NodePayload;
|
|
95
|
+
toNode: ConnectionCreated_NodePayload;
|
|
96
|
+
}
|
|
97
|
+
export interface ConnectionCreated_NodePayload {
|
|
98
|
+
nodeType: "node" | "component" | "metanode";
|
|
99
|
+
nodePortIndex: number;
|
|
100
|
+
nodePortId: string;
|
|
101
|
+
nodeFactoryId?: string;
|
|
102
|
+
}
|
|
103
|
+
export interface NodeCreated_Common {
|
|
104
|
+
nodeType: "node" | "component";
|
|
105
|
+
}
|
|
106
|
+
export interface NodeOrComponent {
|
|
107
|
+
nodeFactoryId?: string;
|
|
108
|
+
nodeHubId?: string;
|
|
109
|
+
}
|
|
110
|
+
export interface ConnectedTo_Basic {
|
|
111
|
+
nodeType: "node" | "component" | "metanode";
|
|
112
|
+
nodeFactoryId: string;
|
|
113
|
+
}
|
|
114
|
+
export interface ConnectedTo_WithPorts {
|
|
115
|
+
nodeType: "node" | "component" | "metanode";
|
|
116
|
+
nodeFactoryId: string;
|
|
117
|
+
nodePortIndex?: number;
|
|
118
|
+
nodePortId?: string;
|
|
119
|
+
}
|
|
120
|
+
export interface QAMOpened_Payload {
|
|
121
|
+
connectedTo: ConnectedTo_WithPorts;
|
|
122
|
+
}
|
|
123
|
+
export interface QAMOpened_PartialPayload {
|
|
124
|
+
connectedTo?: ConnectedTo_WithPorts;
|
|
125
|
+
}
|
|
126
|
+
export interface SearchPayload {
|
|
127
|
+
repoType: "node" | "component";
|
|
128
|
+
keyword: string;
|
|
129
|
+
}
|