Skip to content

Commit

Permalink
Added new plugin (#114)
Browse files Browse the repository at this point in the history
* Added new plugin to header
  • Loading branch information
obgibson committed Jul 28, 2023
1 parent 01f90a3 commit 298e2dd
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 63 deletions.
40 changes: 20 additions & 20 deletions docs/plugin-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ This way uses mock data and does not require other services for development.
3. Open plugin development environment `yarn dev:plugin`. Cypress UI will open in browser in a while.
4. Click `index.plugin-dev.tsx` on left side of the screen. This is will show up the plugin example.
5. Open up `plugin-api/dev/plugins/dev-plugin/index.html` with your code editor and start developing your plugin here.
> Alternatively you can make a new folder (`plugin-api/dev/plugins/new-plugin`) and add `index.html` and `manifest.json` like in dev-plugin. Also make copy of `index.plugin-dev.tsx` and update `PLUGIN_DEFINITIONS`. This is recommended so you can easily initialize this new folder as a git repo.
> Alternatively you can make a new folder (`plugin-api/dev/plugins/new-plugin`) and add `index.html` and `manifest.json` like in dev-plugin. Also make copy of `index.plugin-dev.tsx` and update `PLUGIN_DEFINITIONS`. This is recommended so you can easily initialize this new folder as a git repo.
6. Before deployment add a copy of MetaflowPluginAPI.js with the plugin and make sure that index.html refers to it correctly.
7. See `Deployment guide` from below

Expand All @@ -20,14 +20,14 @@ This way requires the use of the UI application and Metaflow Metadata services.
1. Clone [Metaflow UI](https://github.com/Netflix/metaflow-ui)
2. Install dependencies `yarn install` and start up UI with `yarn start`
3. Clone [Metaflow Service](https://github.com/Netflix/metaflow-service)
4. Create a new folder to `services/ui_backend_service/plugins/installed/your-new-plugin` and add `index.html`, `manifest.json` and `MetaflowPluginAPI.js`.
4. Create a new folder to `services/ui_backend_service/plugins/installed/your-new-plugin` and add `index.html`, `manifest.json` and `MetaflowPluginAPI.js`.
5. Start up the backend service with docker-compose with [plugin configurations](https://github.com/Netflix/metaflow-service/blob/master/services/ui_backend_service/docs/plugins.md).

>`PLUGINS={"your-new-plugin": {}} docker-compose -f docker-compose.development.yml up`
> `PLUGINS={"your-new-plugin": {}} docker-compose -f docker-compose.development.yml up`
6. Run Metaflow runs with the new backend service. https://github.com/Netflix/metaflow-service
7. Open UI in browser `http://localhost:3000` and start developing the plugin. Plugin should show up in `run-header` or `task-details` depending on the manifest.json `slot` parameter.
8. See `Deployment guide` from below
7. Open UI in browser `http://localhost:3000` and start developing the plugin. Plugin should show up in `run-header`, `task-details`, or `header` depending on the manifest.json `slot` parameter.
8. See `Deployment guide` from below

# Deployment

Expand All @@ -36,34 +36,34 @@ Production stage plugins live on the server side. Detailed instructions at [Meta
## Way 1: GIT

1. Setup GIT repo for your plugin.
2. Configure `PLUGINS` variable at metaflow-service/ui_backend_service with following example.
> ```
> {
> "plugin-example": "[email protected]:User/plugin-repo.git"
> }
> ```
2. Configure `PLUGINS` variable at metaflow-service/ui_backend_service with following example.
> ```
> {
> "plugin-example": "[email protected]:User/plugin-repo.git"
> }
> ```
or (all possible settings are described in Metaflow Service docs)

>```
> ```
> {
> "plugin-example": {
> "repository": "path_to_your_repo",
> "ref": "1234f5a",
> "auth": { "user": "user", "password": "password" }
> }
> "auth": { "user": "user", "password": "password" }
> }
> }
>```
> ```
3. Start up the service and the plugin is fetched and installed.

## Way 2: Manual

1. Move plugin folder to `services/ui_backend_service/plugins/installed` on the backend service.
2. Configure `PLUGINS` variable at metaflow-service/ui_backend_service with
> ```
> {
> "plugin-folder-name": {}
> }
> ```
> ```
> {
> "plugin-folder-name": {}
> }
> ```
3. Start up the service
15 changes: 11 additions & 4 deletions docs/plugin-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,11 @@ Example of basic plugin HTML:
});
Metaflow.subscribeToMetadata((message) => {
console.log(`Metadata for ${resource.run_number} got updated! Metadata object for the task is ${JSON.stringify(message.data)}`);
console.log(
`Metadata for ${resource.run_number} got updated! Metadata object for the task is ${JSON.stringify(
message.data,
)}`,
);
});
});
Expand All @@ -64,17 +68,16 @@ Example of manifest.json
"name": "Hello world plugin",
"version": "0.0.1",
"entrypoint": "plugin.html",
"slot": "task-details",
"slot": "task-details",
"container": "collapsable"
}
```


This plugin is registered to be rendered in the task details section (path /FLOW_ID/RUN_NUMBER/STEP_NAME/TASK_ID in application). It subscribes to a custom event "customEvent" and is prepared to send another custom event "customEvent" on button click. It also subscribes for updates about task Metadata.

## Plugin slots

There are two implemented plugin slots, `run-header` and `task-details`. The desired slot must be defined in manifest.json file.
There are three implemented plugin slots, `run-header`, `task-details`, and `header`. The desired slot must be defined in manifest.json file.

### run-header

Expand All @@ -84,6 +87,10 @@ The `run-header` plugin will be rendered below run details in a collapsable elem

The `task-details` plugin will be rendered below task details in a collapsable element.

### header

The `header` plugin will be rendered below the Metaflow logo in the header.

## Plugin configurations

Plugins can be given custom parameters from the server-side. These parameters will be passed to the plugin with the `onReady` callback call.
Expand Down
37 changes: 18 additions & 19 deletions plugin-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ This folder contains the JS API for making UI plugins for Metaflow UI. There are

Plugins will use JS API to communicate with the host application. Plugins must call at least the `register` function from the API to get rendered.

| function | type | description |
| -- | -- | -- |
| onReady | (callback: (configuration, resource) => void) => void | Register callback function to be run when host and plugin iframe are ready. |
| function | type | description |
| ---------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- |
| onReady | (callback: (configuration, resource) => void) => void | Register callback function to be run when host and plugin iframe are ready. |
| subscribe | (paths: string[], cb: (message: { path: string, data: any }) => void) => void | Subscribe to contextual data updates from application. Possible paths: 'metadata' or 'run-metadata' |
| subscribeToMetadata | (callback: (message: Record<string,string>) => void) => void | Subscribe to task metadata |
| subscribeToRunMetadata | (callback: (message: Record<string,string>) => void) => void | Subscribe to run metadata |
| on | (events: string[], cb: (message: { type: string, data: any }) => void) => void | Subscribe to any event by event string. |
| call | (event: string, data: any) => void | Call any custom event with string and any data. |
| sendNotification | (message: string \| { type: string, message: string }) => void | Call notification API from host application. |
| setHeight | (height: number \| undefined) => void | Update height of iframe container for plugin. |
| setVisibility | (visibility: boolean) => void | Update visibility of the plugin. Note that if will stay in iframe even if visibility is set false. |
| subscribeToMetadata | (callback: (message: Record<string,string>) => void) => void | Subscribe to task metadata |
| subscribeToRunMetadata | (callback: (message: Record<string,string>) => void) => void | Subscribe to run metadata |
| on | (events: string[], cb: (message: { type: string, data: any }) => void) => void | Subscribe to any event by event string. |
| call | (event: string, data: any) => void | Call any custom event with string and any data. |
| sendNotification | (message: string \| { type: string, message: string }) => void | Call notification API from host application. |
| setHeight | (height: number \| undefined) => void | Update height of iframe container for plugin. |
| setVisibility | (visibility: boolean) => void | Update visibility of the plugin. Note that if will stay in iframe even if visibility is set false. |

## How to

Expand Down Expand Up @@ -48,14 +48,13 @@ Plugins requires some configuration to their manifest.json

```json
{
"name": "Plugin name", // Name will be visible in UI
"name": "Plugin name", // Name will be visible in UI
"version": "0.0.1",
"entrypoint": "index.html", // Name for HTML file used
"slot": "run-header", // Slot in UI where plugin is rendered. "run-header" or "task-details"
"visible": true, // (Optional) Define if plugin should be visible by default. Default: true
"container": "titled-container", // (Optional) Define what kind of container is used for plugin. "collapsable" or "titled-container". Default: "collapsable"
"containerProps": {}, // (Optional) Properties for container element. For example collapsable can take { "initialState": true } to be open by default. Default: null
"useApplicationStyles": false, // (Optional) Disable injecting basic styles from main application. Default: true

"entrypoint": "index.html", // Name for HTML file used
"slot": "run-header", // Slot in UI where plugin is rendered. "run-header", "task-details", or "header"
"visible": true, // (Optional) Define if plugin should be visible by default. Default: true
"container": "titled-container", // (Optional) Define what kind of container is used for plugin. "collapsable" or "titled-container". Default: "collapsable"
"containerProps": {}, // (Optional) Properties for container element. For example collapsable can take { "initialState": true } to be open by default. Default: null
"useApplicationStyles": false // (Optional) Disable injecting basic styles from main application. Default: true
}
```
```
13 changes: 8 additions & 5 deletions src/components/AppBar/__tests__/AppBar.test.cypress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,20 @@ import { BrowserRouter as Router, Route } from 'react-router-dom';
import { QueryParamProvider } from 'use-query-params';
import theme from '../../../theme';
import AppBar from '..';
import { PluginsProvider } from '../../Plugins/PluginManager';

describe('AppBar test', () => {
it('AppBar basic', () => {
cy.viewport(1000, 600);
mount(
<ThemeProvider theme={theme}>
<Router>
<QueryParamProvider ReactRouterRoute={Route}>
<AppBar />
</QueryParamProvider>
</Router>
<PluginsProvider>
<Router>
<QueryParamProvider ReactRouterRoute={Route}>
<AppBar />
</QueryParamProvider>
</Router>
</PluginsProvider>
</ThemeProvider>,
);
// test all of the AppBars child components render
Expand Down
16 changes: 10 additions & 6 deletions src/components/AppBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Breadcrumb from '../Breadcrumb';
import { ItemRow } from '../Structure';
import HelpMenu from '../HelpMenu';
import ConnectionStatus from '../ConnectionStatus';
import PluginGroup from '../Plugins/PluginGroup';

//
// Main application bar which is always shown on top of the page
Expand All @@ -24,6 +25,9 @@ const AppBar: React.FC = () => {
<Breadcrumb />
<HelpMenu />
</ItemRow>
<ItemRow pad="lg">
<PluginGroup id="header" title="Extensions" slot="header" />
</ItemRow>
</Wrapper>
);
};
Expand All @@ -39,15 +43,16 @@ const Wrapper = styled.header`
display: flex;
align-items: center;
justify-content: flex-start;
position: fixed;
position: sticky;
top: 0;
left: 0;
right: 0;
height: ${(p) => p.theme.layout.appbarHeight}rem;
min-height: ${(p) => p.theme.layout.appbarHeight}rem;
margin: 0 auto;
padding: 0 ${(p) => p.theme.layout.pagePaddingX}rem;
background: ${(p) => p.theme.color.bg.white};
z-index: 999;
flex-direction: column;
`;

const Logo = styled.img`
Expand All @@ -59,9 +64,8 @@ const LogoLink = styled(Link)`
`;

const ConnectionStatusWrapper = styled.div`
position: absolute;
top: 0;
right: 0;
padding: 0 ${(p) => p.theme.layout.pagePaddingX}rem;
display: flex;
justify-content: flex-end;
width: 100%;
padding-top: ${(p) => p.theme.spacer.md}rem;
`;
1 change: 1 addition & 0 deletions src/components/ConnectionStatus/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ const Wrapper = styled.div<{ status: RealtimeStatus }>`
&:hover ${Text} {
opacity: 1;
}
height: 2rem;
`;

const StatusColorIndicator = styled.div<{ status: RealtimeStatus }>`
Expand Down
2 changes: 1 addition & 1 deletion src/components/Plugins/PluginGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type Props = {
resourceParams?: Record<string, string>;
};

const VALID_CONTAINERS = ['collapsable', 'titled-container'];
const VALID_CONTAINERS = ['collapsable', 'titled-container', 'none'];

//
// Renders list of plugin to iframe in collapsable element.
Expand Down
4 changes: 2 additions & 2 deletions src/components/Plugins/PluginManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ type PluginVersionInfo = {
// Constants. Plugin will not render if it doesn't satisfy SUPPORTED_PLUGIN_API_VERSION.
//

export type AllowedSlot = 'task-details' | 'run-header';
export type AllowedSlot = 'task-details' | 'run-header' | 'header';

const SUPPORTED_PLUGIN_API_VERSION = '0.13.0';
const RECOMMENDED_PLUGIN_API_VERSION = '1.1.0';
const ALLOWED_SLOTS: AllowedSlot[] = ['task-details', 'run-header'];
const ALLOWED_SLOTS: AllowedSlot[] = ['task-details', 'run-header', 'header'];

//
// Utils
Expand Down
6 changes: 0 additions & 6 deletions src/components/Structure/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,6 @@ export const Page = styled.div`
min-height: 100vh;
display: flex;
flex-direction: column;
&:before {
display: block;
content: '';
height: ${(p) => p.theme.layout.appbarHeight}rem;
}
`;

export const Section = styled.section`
Expand Down

0 comments on commit 298e2dd

Please sign in to comment.