Skip to content

Commit bd55c04

Browse files
rsanteriobgibson
andauthored
feat:Change plugin metadata to be exposed as object (#51)
Co-authored-by: Brendan Gibson <[email protected]>
1 parent 3f454f9 commit bd55c04

File tree

11 files changed

+71
-97
lines changed

11 files changed

+71
-97
lines changed

docs/plugin-system.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Example of basic plugin HTML:
4444
});
4545
4646
Metaflow.subscribeToMetadata((message) => {
47-
console.log(`Metadata for ${resource.run_number} got updated! Metadata array for the task is ${JSON.stringify(message.data)}`);
47+
console.log(`Metadata for ${resource.run_number} got updated! Metadata object for the task is ${JSON.stringify(message.data)}`);
4848
});
4949
});
5050

plugin-api/Examples/react-plugin/app.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ function App() {
1919
<div>
2020
{metadata ? (
2121
<div>
22-
<div>{metadata.length}</div>
22+
<div>{JSON.stringify(metadata)}</div>
2323
</div>
2424
) : 'Waiting for data'}
2525
</div>

plugin-api/MetaflowPluginAPI.js

Lines changed: 26 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const VERSION_INFO = {
2-
api: "1.0.0",
2+
api: '1.1.0',
33
};
44

55
const Listeners = [];
@@ -15,7 +15,7 @@ const PluginInfo = {
1515
function messageHandler(event) {
1616
if (event.data && event.data.type) {
1717
switch (event.data.type) {
18-
case "ReadyToRender": {
18+
case 'ReadyToRender': {
1919
if (!initialised) {
2020
Metaflow.parameters = event.data.config;
2121
Metaflow.resource = event.data.resource;
@@ -27,13 +27,13 @@ function messageHandler(event) {
2727
}
2828
return;
2929
}
30-
case "DataUpdate": {
30+
case 'DataUpdate': {
3131
for (const listener of Listeners) {
3232
listener(event.data);
3333
}
3434
return;
3535
}
36-
case "EventUpdate": {
36+
case 'EventUpdate': {
3737
for (const listener of EventListeners) {
3838
listener(event.data);
3939
}
@@ -43,29 +43,29 @@ function messageHandler(event) {
4343
}
4444
}
4545

46-
window.addEventListener("message", messageHandler);
46+
window.addEventListener('message', messageHandler);
4747

4848
const Metaflow = {
4949
parameters: {},
5050
resource: {},
5151
/**
5252
* onReady function will be called with basic info like resource ids and custom server parameters.
53-
* @param {*} onready
53+
* @param {*} onready
5454
*/
5555
onReady(onready) {
5656
onReadyFn = onready;
5757
window.parent.postMessage(
5858
{
5959
name: window.name,
60-
type: "PluginRegisterEvent",
60+
type: 'PluginRegisterEvent',
6161
version: VERSION_INFO,
6262
},
63-
"*"
63+
'*',
6464
);
6565
},
6666
/**
6767
* Alias for onReady
68-
* @param {*} _settings Deprecated settings for register function
68+
* @param {*} _settings Deprecated settings for register function
6969
* @param {*} onready Callback to startup plugin.
7070
*/
7171
register(_settings, onready) {
@@ -79,21 +79,11 @@ const Metaflow = {
7979
*/
8080
setHeight(fixedHeight) {
8181
if (fixedHeight) {
82-
window.parent.postMessage(
83-
{ name: window.name, type: "PluginHeightCheck", height: fixedHeight },
84-
"*"
85-
);
82+
window.parent.postMessage({ name: window.name, type: 'PluginHeightCheck', height: fixedHeight }, '*');
8683
} else {
8784
const body = document.body;
88-
const height = Math.max(
89-
body.scrollHeight,
90-
body.offsetHeight,
91-
body.clientHeight
92-
);
93-
window.parent.postMessage(
94-
{ name: window.name, type: "PluginHeightCheck", height: height },
95-
"*"
96-
);
85+
const height = Math.max(body.scrollHeight, body.offsetHeight, body.clientHeight);
86+
window.parent.postMessage({ name: window.name, type: 'PluginHeightCheck', height: height }, '*');
9787
}
9888
},
9989
/**
@@ -103,10 +93,7 @@ const Metaflow = {
10393
*/
10494
subscribe(paths, fn) {
10595
Listeners.push(fn);
106-
window.parent.postMessage(
107-
{ name: window.name, type: "PluginSubscribeToData", paths: paths },
108-
"*"
109-
);
96+
window.parent.postMessage({ name: window.name, type: 'PluginSubscribeToData', paths: paths }, '*');
11097
},
11198
/**
11299
* Subscribe to events
@@ -115,21 +102,15 @@ const Metaflow = {
115102
*/
116103
on(events, fn) {
117104
EventListeners.push(fn);
118-
window.parent.postMessage(
119-
{ name: window.name, type: "PluginSubscribeToEvent", events: events },
120-
"*"
121-
);
105+
window.parent.postMessage({ name: window.name, type: 'PluginSubscribeToEvent', events: events }, '*');
122106
},
123107
/**
124108
* Call event with any name and payload. Other plugins or systems in app might subscribe to these events.
125109
* @param {string} event
126110
* @param {*} data
127111
*/
128112
call(event, data) {
129-
window.parent.postMessage(
130-
{ name: window.name, type: "PluginCallEvent", event: event, data: data },
131-
"*"
132-
);
113+
window.parent.postMessage({ name: window.name, type: 'PluginCallEvent', event: event, data: data }, '*');
133114
},
134115
/**
135116
* Send notification on main application
@@ -139,11 +120,11 @@ const Metaflow = {
139120
window.parent.postMessage(
140121
{
141122
name: window.name,
142-
type: "PluginCallEvent",
143-
event: "SEND_NOTIFICATION",
123+
type: 'PluginCallEvent',
124+
event: 'SEND_NOTIFICATION',
144125
data: message,
145126
},
146-
"*"
127+
'*',
147128
);
148129
},
149130
/**
@@ -154,40 +135,37 @@ const Metaflow = {
154135
window.parent.postMessage(
155136
{
156137
name: window.name,
157-
type: "PluginCallEvent",
158-
event: "UPDATE_PLUGIN",
138+
type: 'PluginCallEvent',
139+
event: 'UPDATE_PLUGIN',
159140
data: {
160141
slot: PluginInfo.slot,
161142
name: PluginInfo.manifest.name,
162143
visible: visible,
163144
},
164145
},
165-
"*"
146+
'*',
166147
);
167148
},
168149
//
169150
// Request to be removed?
170151
//
171152
remove(fn) {
172-
window.parent.postMessage(
173-
{ name: window.name, type: "PluginRemoveRequest" },
174-
"*"
175-
);
153+
window.parent.postMessage({ name: window.name, type: 'PluginRemoveRequest' }, '*');
176154
},
177155

178156
subscribeToMetadata(fn) {
179-
this.subscribe(["metadata"], fn);
157+
this.subscribe(['metadata'], (event) => fn(event.data));
180158
},
181159

182160
subscribeToRunMetadata(fn) {
183-
this.subscribe(["run-metadata"], fn);
161+
this.subscribe(['run-metadata'], (event) => fn(event.data));
184162
},
185163
};
186164

187-
if (typeof exports !== "undefined") {
165+
if (typeof exports !== 'undefined') {
188166
exports.Metaflow = Metaflow;
189167
}
190168

191-
if (typeof window !== "undefined") {
169+
if (typeof window !== 'undefined') {
192170
window.Metaflow = Metaflow;
193171
}

plugin-api/README.md

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,33 +10,14 @@ Plugins will use JS API to communicate with the host application. Plugins must c
1010
| -- | -- | -- |
1111
| onReady | (callback: (configuration, resource) => void) => void | Register callback function to be run when host and plugin iframe are ready. |
1212
| subscribe | (paths: string[], cb: (message: { path: string, data: any }) => void) => void | Subscribe to contextual data updates from application. Possible paths: 'metadata' or 'run-metadata' |
13-
| subscribeToMetadata | (callback: (message: { type: string, data: Metadata[] }) => void) => void | Subscribe to task metadata |
14-
| subscribeToRunMetadata | (callback: (message: { type: string, data: Metadata[] }) => void) => void | Subscribe to run metadata |
13+
| subscribeToMetadata | (callback: (message: Record<string,string>) => void) => void | Subscribe to task metadata |
14+
| subscribeToRunMetadata | (callback: (message: Record<string,string>) => void) => void | Subscribe to run metadata |
1515
| on | (events: string[], cb: (message: { type: string, data: any }) => void) => void | Subscribe to any event by event string. |
1616
| call | (event: string, data: any) => void | Call any custom event with string and any data. |
1717
| sendNotification | (message: string \| { type: string, message: string }) => void | Call notification API from host application. |
1818
| setHeight | (height: number \| undefined) => void | Update height of iframe container for plugin. |
1919
| setVisibility | (visibility: boolean) => void | Update visibility of the plugin. Note that if will stay in iframe even if visibility is set false. |
2020

21-
Type of metadata is
22-
23-
```typescript
24-
type Metadata = {
25-
flow_id: string;
26-
run_number: string;
27-
step_name: string;
28-
task_id: string;
29-
user_name: string;
30-
ts_epoch: number;
31-
tags?: string[];
32-
system_tags: string[];
33-
id: number;
34-
field_name: string;
35-
value: string;
36-
type: string;
37-
}
38-
```
39-
4021
## How to
4122

4223
Here are three different ways to use the plugin API file.

plugin-api/dev/index.plugin-dev.tsx

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,10 @@ const PLUGIN_DEFINITIONS = [
2828

2929
// Mock data as you need for your plugin. This data will be available in plugin with Metaflow.subscribe(['key-here'], callback)
3030
const DATA = {
31-
metadata: [
32-
{
33-
flow_id: 'PluginDevFlow',
34-
user_name: 'dev-user',
35-
ts_epoch: 1634036924920,
36-
tags: [],
37-
system_tags: ['metaflow-version:1'],
38-
id: 1,
39-
run_number: 1,
40-
step_name: 'start',
41-
task_id: '123',
42-
field_name: 'datafield',
43-
value: 'value',
44-
type: 'aws-batch-job-id',
45-
},
46-
],
31+
metadata: {
32+
field: 'value',
33+
is_plugin_test: 'true',
34+
},
4735
};
4836

4937
describe('Testing', () => {

plugin-api/dev/plugins/dev-plugin/index.html

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@
3737

3838
<p>
3939
Plugin contains at least one html file and manifest.json. HTML file contains everything plugin needs to render and
40-
manifest file contains configurations for the plugin. In development tooling basic information is mocked in
41-
test file instead.
40+
manifest file contains configurations for the plugin. In development tooling basic information is mocked in test
41+
file instead.
4242
</p>
4343

4444
<p>
@@ -54,7 +54,10 @@
5454
<p><b>Recommended workflow</b></p>
5555

5656
<ol>
57-
<li>Create new folder to <code>plugin-api/dev/plugins</code> and initialise it as repository to your version control.</li>
57+
<li>
58+
Create new folder to <code>plugin-api/dev/plugins</code> and initialise it as repository to your version
59+
control.
60+
</li>
5861
<li>Add <code>index.html</code> and <code>manifest.json</code></li>
5962
<li>
6063
Change <code>PLUGIN_DEFINITIONS[0].name from plugin-api/dev/index.plugin-dev.tsx</code> to be your folder name.
@@ -82,9 +85,8 @@
8285
document.getElementById('init').innerHTML = 'Plugin is up and ready!';
8386
document.getElementById('resource').innerHTML = 'Resource Info: ' + JSON.stringify(resource);
8487

85-
8688
Metaflow.subscribeToMetadata((message) => {
87-
document.getElementById('metadata').innerHTML = 'Metadata: ' + JSON.stringify(message.data, 0, 2);
89+
document.getElementById('metadata').innerHTML = 'Metadata: ' + JSON.stringify(message, 0, 2);
8890
Metaflow.setHeight();
8991
});
9092
});

src/components/Plugins/PluginManager.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ type PluginVersionInfo = {
4747
export type AllowedSlot = 'task-details' | 'run-header';
4848

4949
const SUPPORTED_PLUGIN_API_VERSION = '0.13.0';
50-
const RECOMMENDED_PLUGIN_API_VERSION = '1.0.0';
50+
const RECOMMENDED_PLUGIN_API_VERSION = '1.1.0';
5151
const ALLOWED_SLOTS: AllowedSlot[] = ['task-details', 'run-header'];
5252

5353
//

src/pages/Run/RunPage.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { FixedContent } from '../../components/Structure';
2222
import useTaskListSettings from '../../components/Timeline/useTaskListSettings';
2323
import useResource from '../../hooks/useResource';
2424
import { PluginsContext } from '../../components/Plugins/PluginManager';
25+
import { metadataToRecord } from '../../utils/metadata';
2526
import { GraphModel } from '../../components/DAG/DAGUtils';
2627

2728
//
@@ -82,7 +83,7 @@ const RunPage: React.FC<RunPageProps> = ({ run, params }) => {
8283
step_name: '_parameters',
8384
},
8485
onUpdate(items) {
85-
plContext.addDataToStore('run-metadata', items);
86+
plContext.addDataToStore('run-metadata', metadataToRecord(items));
8687
},
8788
});
8889

@@ -250,7 +251,7 @@ const RunPage: React.FC<RunPageProps> = ({ run, params }) => {
250251
)}
251252
</ErrorBoundary>
252253
),
253-
}
254+
},
254255
]}
255256
/>
256257
</RunPageContainer>

src/pages/Task/useTaskMetadata.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useContext, useEffect } from 'react';
22
import { PluginsContext } from '../../components/Plugins/PluginManager';
33
import useResource, { Resource } from '../../hooks/useResource';
44
import { Metadata } from '../../types';
5+
import { metadataToRecord } from '../../utils/metadata';
56

67
type TaskMetadataConfig = {
78
url: string;
@@ -55,11 +56,11 @@ function useTaskMetadata({ url, attemptId, paused }: TaskMetadataConfig): TaskMe
5556

5657
// Update metadata to plugin store
5758
useEffect(() => {
58-
addDataToStore('metadata', data);
59+
addDataToStore('metadata', metadataToRecord(data));
5960
}, [dataCount]); //eslint-disable-line
6061

6162
useEffect(() => {
62-
return () => addDataToStore('metadata', []);
63+
return () => addDataToStore('metadata', {});
6364
}, [url, attemptId]); //eslint-disable-line
6465

6566
return { data, taskMetadataResource, attemptMetadataResource };
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { metadataToRecord } from '../metadata';
2+
import { createMetadata } from '../testhelper';
3+
4+
describe('Metadata utils', () => {
5+
it('metadataToRecord()', () => {
6+
expect(metadataToRecord([])).to.eql({});
7+
expect(metadataToRecord([createMetadata({ field_name: 'test', value: '123' })])).to.eql({ test: '123' });
8+
expect(
9+
metadataToRecord([
10+
createMetadata({ field_name: 'test', value: '123' }),
11+
createMetadata({ field_name: 'second', value: 'row' }),
12+
createMetadata({ field_name: 'third', value: 'row' }),
13+
]),
14+
).to.eql({ test: '123', second: 'row', third: 'row' });
15+
});
16+
});

src/utils/metadata.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { Metadata } from '../types';
2+
3+
export function metadataToRecord(data: Metadata[]): Record<string, string> {
4+
return data.reduce((obj, metadata) => {
5+
return { ...obj, [metadata.field_name]: metadata.value };
6+
}, {});
7+
}

0 commit comments

Comments
 (0)