Creating Custom Widgets
Cloudify enables you to create your own widgets to help you to orchestrate your applications in the Cloud.
Widget Development Methods
Widgets can be written using two different methods:
Using the React utility is the recommended method, and it requires a build operation. You must use the build system described in Widget building section.
Plain JavaScript that enables attachment of an HTML template file. The callbacks for this method are described later in this topic. You must create widget package yourself. No ES6 is supported in that method.
Widget Structure
A widget is made up of these files:
widget.js
‑ Holds the widget’s definition (Required)widget.png
‑ The preview image of the widget in the widgets catalog (Required)backend.js
- A widget backend that allows widget frontend code to use backend services (Optional)widget.html
‑ A widget template file that is relevant only when you are writing a widget using plain JavaScript with an HTML template (Optional)widget.css
‑ The CSS file that the widget uses (Optional)
If you want to use the React utility (recommended), you must put your widget.js
file (and optionally backend.js
file) into the src
directory along with any other required files. In the widget.js
file, you can use import
to include the additional files. You can split the widget into a number of files. You can also use ES6 features.
Using this method, the file system will look as follows:
/widgets
/blueprint
/src
widget.js
backend.js
widget.js (generated by build system)
backend.js (generated by build system)
widget.html
widget.png
widget.css
If you want to use plain JavaScript and you want to create a widget named blueprint
, the widget library looks like this:
/widgets
/blueprint
widget.js
backend.js
widget.html
widget.png
widget.css
Widget Definition
Each widget.js
file must have a call to the Stage.defineWidget
global function.
Example
The following code demonstrates how easy it is to create a simple widget.
You can copy this code and put it in a widget.js
file to produce a fully working widget. See the previous section for file structure guidelines.
Stage.defineWidget({
id: 'sampleWidget',
name: 'A basic example',
description: 'This widget displays "Hello World!" text',
initialWidth: 4,
initialHeight: 2,
showHeader: false,
showBorder: false,
initialConfiguration: [],
isReact: true,
categories: [Stage.GenericConfig.CATEGORY.OTHERS],
permission: Stage.GenericConfig.CUSTOM_WIDGET_PERMISSIONS.CUSTOM_ALL,
render: function(widget,data,error,toolbox) {
return (
<span>Hello World!</span>
);
}
});
Widget Settings
As seen in the example above, there are a number of configuration fields that you can provide when you design a widget.
The Stage.defineWidget
function receives a settings object with the options described in this table.
Option | Type | Required | Default | Description |
---|---|---|---|---|
categories |
array | No | OTHERS |
This property specifies in which categories this widget shall be visible. It may take an array containing one or more of the values defined in Stage.GenericConfig.CATEGORY object: BLUEPRINTS (‘Blueprints’ category), DEPLOYMENTS (‘Deployments’), BUTTONS_AND_FILTERS (‘Buttons and Filters’), CHARTS_AND_STATISTICS (‘Charts and Statistics’), EXECUTIONS_NODES (‘Executions/Nodes’), SYSTEM_RESOURCES (‘System Resources’), OTHERS (‘Others’), ALL (‘All’). |
color |
string | No | blue |
The color of the widget. One of the following: red , orange , yellow , olive , green , teal , blue , violet , purple , pink , brown , grey or black . |
description |
string | No | - | Description of the widget that is displayed in the Add Widget dialog. |
fetchUrl |
string/object | No | - | If fetchUrl exists, the data from the URL is fetched by the application and passed to the render and postRender methods. To fetch multiple URLs, you must pass an object where the key is a name you select for this data, and the value is the URL. It is important to note that the render is called once before the data is fetched (to enable information about loading or partial data can be displayed) and once after the data is fetched. |
hasStyle |
boolean | No | false |
Whether to use style.css file. File must be present in widget’s main directory. |
hasTemplate |
boolean | No | false |
Whether to use widget.html file as template. File must be present in widget’s main directory. |
id |
string | Yes | - | The ID of the widget definition. Must match the name of the directory into which it is placed. |
initialConfiguration |
array | No | [] |
A list of widget configuration options. The options are displayed when a user clicks the Configure icon in the top-right corner of the widget in edit mode. It can also be accessed in the widget, to determine the current selected configuration. |
initialHeight |
string | No | 12 |
The default height of the widget when added to a page. |
initialWidth |
string | No | 3 |
The default width of the widget when added to a page. |
isReact |
boolean | Yes | - | Set as true when writing a React widget. |
name |
string | Yes | - | The display name of the widget that is displayed in the Add Widget dialog. It is also used as the default widget name. |
showBorder |
boolean | No | true |
Whether to display widget’s border. |
showHeader |
boolean | No | true |
Whether to display a header. If a header is not displayed, a user cannot change the widget name. |
permission |
string | Yes | - | This property specifies which user may later access and view this widget. It may take one of the following three values defined in Stage.GenericConfig.CUSTOM_WIDGET_PERMISSIONS object: CUSTOM_ADMIN_ONLY (applies for ‘sys_admin’ and ‘manager’ roles), CUSTOM_SYS_ADMIN_ONLY (applies for ‘sys_admin’ only, CUSTOM_ALL (applies to all user-roles). |
initialConfiguration
initialConfiguration
supports 4 generic pre-made config fields (see fetchUrl
for example):
Stage.GenericConfig.POLLING_TIME_CONFIG(int)
- How often to refresh the data (in seconds)Stage.GenericConfig.PAGE_SIZE_CONFIG(int)
- The initial number of rows (page size) for widgets presenting data in tabular view. If value not provided, then page size is set to 5Stage.GenericConfig.SORT_COLUMN_CONFIG(string)
- Column name to sort byStage.GenericConfig.SORT_ASCENDING_CONFIG(boolean)
- Change sorting order (true=ascending)
In addition to listed above, you can create your own configuration fields. Example of configuration with user-defined fields:
initialConfiguration: [
Stage.GenericConfig.PAGE_SIZE_CONFIG(3),
{id: 'username', name: 'Fetch with', placeHolder:"GitHub user", description:"...", default:"cloudify-examples", type: Stage.Basic.GenericField.STRING_TYPE},
{id: 'filter', name: 'Filter', placeHolder:"GitHub filter", description:"...", default:"blueprint in:name NOT local", type: Stage.Basic.GenericField.STRING_TYPE},
{id: "displayStyle",name: "Display style", items: [{name:'Table', value:'table'}, {name:'Catalog', value:'catalog'}], default: "catalog", type: Stage.Basic.GenericField.LIST_TYPE},
Stage.GenericConfig.SORT_COLUMN_CONFIG('created_at'),
Stage.GenericConfig.SORT_ASCENDING_CONFIG(false)
]
You can find descriptions of the fields in the GenericField component documentation).
Configuration fields values can be fetched in render
method using widget.configuration
object. See Accessing data in render() for details.
fetchUrl
There are two primary ways of pulling data from remote sources: fetchUrl
and fetchData()
.
fetchUrl
is an object member and may be defined either as a string or an object with multiple string properties (property:URL) where each property represents a separate URL to query.
A single URL’s results will be available directly in the data object.
In case fetchUrl
is defined with multiple URLs, the results will be accessible by property name of this URL (i.e. data.nodes).
Single URL Example
fetchUrl: 'localhost:50123/public/nodes'
// ...
render: function(widget,data,error,toolbox) {
let your_data = data;
//...
}
Mulitple URL Example
fetchUrl: {
nodes: '[manager]/nodes?_include=id,deployment_id,blueprint_id,type,type_hierarchy,number_of_instances,host_id,relationships,created_by[params:blueprint_id,deployment_id,gridParams]',
nodeInstances: '[manager]/node-instances?_include=id,node_id,deployment_id,state,relationships,runtime_properties[params:deployment_id]',
deployments: '[manager]/deployments?_include=id,groups[params:blueprint_id,id]'
}
// ...
render: function(widget,data,error,toolbox) {
let nodes = data.nodes.items;
let deployments = data.nodeInstances.items;
//...
}
As seen in the example above, URLs provided in fetchUrl
can be parametrized with several special tokens:
fetchUrl: '[manager]/executions?is_system_workflow=false[params]'
- The
[manager]
token will be replaced with the current Cloudify manager’s IP address (or proxy, if applicable). - The
[params]
token, on the other hand, is quite special. This placeholder can be expanded into a number of things depending on usage:[params]
alone anywhere in the URL will be expanded to default pagination parameters (_size
,_offset
,_sort
) if available (seeinitialConfiguration
). This mode is inclusive - all params available in the widget will be appended to URL.[params:param_name1(,param_name2)]
will be replaced with “¶mName1:paramValue1” in the URL. Please note that this can be used both to selectively pick pagination parameter as well as custom parameters (seefetchParams()
). This mode is exclusive - parameters not specified explicitly will be skipped. When using selective param picking ([params:param_name]
) you can use a pre-definedgridParams
tag to include all pagination parameters (_size
,_offset
,_sort
) instead of specifying explicitly each of the three.
fetchUrl - Inclusive Params
The following example illustrates fetchUrl with both tokens along with resulting URL:
initialConfiguration: [
Stage.GenericConfig.POLLING_TIME_CONFIG(60),
Stage.GenericConfig.PAGE_SIZE_CONFIG(),
Stage.GenericConfig.SORT_COLUMN_CONFIG('column_name'),
Stage.GenericConfig.SORT_ASCENDING_CONFIG(false)
],
fetchUrl: {
nodes: '[manager]/nodes[params]'
},
fetchParams: function(widget, toolbox) {
return {
sampleFuncParam: 'dummy'
}
}
Result URL: http://localhost:3000/sp/?su=/api/v3.1/nodes?&_sort=-column_name&_size=5&_offset=0&sampleFuncParam=dummy
This url can be divided into 3 separate parts:
Field | Example | Description |
---|---|---|
manager address | http://localhost:3000/sp/?su=/api/v3.1/ | The internal value of Cloudify manager [manager] |
endpoint name | nodes? | Remaining part of the REST endpoint address |
generic params | &_sort=-column_name&_size=5&_offset=0 | Parameters that were implicitly added to request. These parameters are inferred from the GenericConfig objects in initialConfiguration and are responsible for pagination of the results. It is possible to omit them by explicitly specifying param names to be used like so [params:my-param] . Alternatively, gridParams (sort, size, offset) can be simply removed from initialConfiguration . |
custom params | &sampleFuncParam=dummy | Custom parameters can be defined in fetchParams() function. Each custom parameter must be returned as a property of an Object returned by fetchParams() function. |
fetchUrl - Exclusive Params
The same URL, this time with explicit param names (and the gridParams
tag):
initialConfiguration: [
Stage.GenericConfig.POLLING_TIME_CONFIG(60),
Stage.GenericConfig.PAGE_SIZE_CONFIG(),
Stage.GenericConfig.SORT_COLUMN_CONFIG('column_name'),
Stage.GenericConfig.SORT_ASCENDING_CONFIG(false)
],
fetchUrl: {
nodes: '[manager]/nodes[params:sampleFuncParam,gridParams]'
// which is essentially the same as
// nodes: '[manager]/nodes[params:sampleFuncParam,_size,_offset_,_sort]'
},
fetchParams: function(widget, toolbox) {
return {
sampleFuncParam: 'dummy'
}
}
Result URL: http://localhost:3000/sp/?su=/api/v3.1/nodes?&sampleFuncParam=dummy&_sort=-column_name&_size=5&_offset=0
Widget Functions
The following functions are available for widgets.
init()
Called when the widget definition is loaded, which occurs after the system is loaded. Can be used to define certain elements, for example classes and objects that will be used in the widget definition.
render(widget, data, error, toolbox)
Called each time that the widget needs to draw itself. This might occur when the page is loaded, widget data is changed, context data is changed, widget data is fetched, and so on. render
parameters are:
- The widget object itself
- The fetched data, either using
fetchUrl
orfetchData
. The data isnull
iffetchData
orfetchUrl
is not specified. The data will also passnull
to therender
method until data is fetched. If you are expecting data, you can render a “loading” indicator. - The error if data fetching failed
- The toolbox object.
render()
is focal to the appearance of the widget as the return value of this function will be rendered to UI by React engine.
As such it is important to understand how to build widgets. The following example illustrates the simplest usage:
render: function(widget,data,error,toolbox) {
return (
<span>Hello World!</span>
);
}
Please note that render()
may only return a single DOM node (refer to JSX spec for more detail).
In order to render more than one HTML element they must be wrapped in a parent element (a div
is usually a good choice):
render: function(widget,data,error,toolbox) {
return (
<div>
<span>Hello World!</span>
<p>Writing Cloudify UI widgets is <strong>super</strong> easy</p>
</div>
);
}
Using Ready Components In render()
Although using plain HTML tags gives you extreme flexibility, usually it is much quicker to design your widget with the use of Cloudify UI ready-made components.
These components were designed with UI uniformity and ease-of-use in mind, and as are very easy to learn and use.
The following example illustrates how to use a KeyIndicator
component:
render: function(widget,data,error,toolbox) {
let {KeyIndicator} = Stage.Basic;
return (
<div>
<KeyIndicator title='User Stars' icon='star' number={3} />
</div>
);
}
Notice that the KeyIndicator
component is imported into the widget. It is defined in the render method as:
let {KeyIndicator} = Stage.Basic;
You can also import multiple components in the same line, for example:
let {KeyIndicator, Checkmark} = Stage.Basic;
Other components are available in the Stage.Basic library.
Accessing Data In render()
There can be several independent data sources for your widget. Two most commonly used are the configuration
and data
objects.
The following example illustrates how to access both of them:
Stage.defineWidget({
id: 'sampleWidget',
name: 'A basic example',
description: 'This widget polls data from two different sources',
initialWidth: 2,
initialHeight: 2,
showHeader: false,
showBorder: false,
isReact: true,
initialConfiguration: [
{id: 'confText', name: 'Conf Item', placeHolder: 'Configuration text item', default: 'Conf text', type: Stage.Basic.GenericField.STRING_TYPE}
],
permission: Stage.GenericConfig.CUSTOM_WIDGET_PERMISSIONS.CUSTOM_ALL,
fetchData(widget, toolbox){
return Promise.resolve({fetchedText: 'Fetched text'});
},
render: function(widget,data,error,toolbox) {
let {Loading} = Stage.Basic
if (_.isEmpty(data)) // Make sure the data is already fetched, if not show a loading spinner
return (<Loading message='Loading data...'></Loading>)
else
return (
<div>
<p>confItem value: {widget.configuration.confText}</p>
<p>fetchedText value: {data.fetchedText}</p>
</div>
);
}
});
The above widget prints will display two lines containing the strings defined in the data sources: “Conf text” and “Fetched Text”. Please note how the widget makes sure data has been loaded has completed before rendering it. Skipping this check would result in an error in browser console.
initialConfiguration, as the name suggests is only used if there are no user defined values for these properties. A user can change them by entering the ‘Edit Mode’ where he can modify widget’s configuration. From that point, the current widget will use the value provided by the user. To reset it to it’s default value, the widget must be removed and re-added to the workbench.
Moreover, please remember to remove and re-add the widget to the dashboard if changing the initialConfiguration
field. It is only loaded for newly ‘mounted’ widgets.
postRender(el, widget, data, toolbox)
Non-React widgets only.
PostRender is called immediately after the widget has been made visible in the UI.
This function has access to the same objects as the render
function with one addition - the el
object containing a reference to the widget’s container (parent) object.
fetchData(widget, toolbox)
An alternative to using fetchUrl
is the fetchData()
function. It provides greater flexibility when you need to pre-process your results or chain them into nested Promises (ie. Pull a list of URLs and resolve each of those URLs).
The return value for fetchData() is expected to be a promise. As such if you would like to return a primitive value you would need to wrap it in a promise:
fetchData(widget, toolbox){
return Promise.resolve({key:value});
}
Please note that should the result be a single primitive value you still need to return it as a property of an Object, since referencing the Object directly is illegal in React. With this in mind, the following example would not work:
// THIS WILL NOT WORK
fetchData(widget, toolbox){ return 10; }
render(widget,data,error,toolbox){
return (
<div>
{data} // This will produce a runtime error
</div>
)
}
Instead, you can return the int
value as a property of the object like so:
fetchData(widget, toolbox){ return {myInt: 10}; }
render(widget,data,error, toolbox) {
return (
<div>
{data.myInt} // OK
</div>
)
}
Note: fetchUrl
and fetchData()
are mutually exclusive, that is if you define fetchUrl in your widget, then fetchData()
definition will be ignored.
fetchParams(widget, toolbox)
fetchParams()
function delivers parameters to fetchData()
function which can be applied with [params]
wildcard.
Example:
fetchParams: function(widget, toolbox) {
let deploymentId = toolbox.getContext().getValue('deploymentId');
return {deployment_id: deploymentId};
}
Widget Building
When you use the React utility to develop the widget, you must use the Widget build system to generate the widget package.
Development Tools
The widget development tools include built-in features, widget objects, functions, templating mechanism and available libraries.
Widget Object
The widget
object has the following attributes:
Attribute | Description |
---|---|
id |
The ID of the widget |
name |
The display name of the widget. (The widget definition name is the default name for the widget, but a user can change it) |
height |
The height of the widget on the page |
width |
The width of the widget on the page |
x |
The x location of the widget on the page |
y |
The y location of the widget on the page |
definition |
The widget definition object as it was passed to defineWidget method. All widget definitions are contained in the widget definition object. The only additional field that the widget can access is template , which is fetched from the HTML and added to the widget definition. |
Toolbox Object
The toolbox
object provides the widget with tools to communicate with the application and other widgets. It also provides generic tools that the widget might require.
The toolbox provides access to the following tools:
getEventBus()
Used to register (listen to) and trigger events. The event bus is used to enable a widget to broadcast an event, usually a change that it made that will affect others. For example, if a blueprints widget creates a new deployment, other widgets need to be aware that the the deployment list has changed. The listening widgets then call a refresh
. Event bus
supports the following methods:
on (event, callback, context)
trigger (event)
off (event, offCallback)
For example:
componentDidMount() {
this.props.toolbox.getEventBus().on('deployments:refresh',this._refreshData,this);
}
componentWillUnmount() {
this.props.toolbox.getEventBus().off('deployments:refresh',this._refreshData);
}
_deleteDeployment() {
// Do somehting...
actions.doDelete(deploymentToDelete).then(()=>{
this.props.toolbox.getEventBus().trigger('deployments:refresh');
}).catch((err)=>{
// Handle errors...
});
}
getManager()
Returns manager object. Used to read current manager’s properties. Available calls:
getIp()
getCurrentUsername()
getManagerUrl(url, data)
getApiVersion()
getSelectedTenant()
doGetFull(url, params, parseResponse, fullData, size)
getExternal()
Used to access the connected Cloudify Manager. The Manager provides access to the Manager REST API. The URL is the service URL, without the /api/vX.X
doGet(url,params)
doPost(url,params,data)
doDelete(url,params,data)
doPut(url,params,data)
doUpload(url,params,file,method)
It also exposes a method that only constructs the URL. Use this with caution because some request headers require being passed to the Manager.
getManagerUrl(url,data)
For example,
return this.toolbox.getManager().doDelete('/deployments/${blueprint.id}');
doUpload(blueprintName, blueprintFileName, file) {
return this.toolbox.getManager().doUpload('/blueprints/${blueprintName}',
_.isEmpty(blueprintFileName)
? null
: {application_file_name: blueprintFileName+'.yaml'},
file);
}
Please note that it is recommended to use fetchData()
instead of doGet(URL, params)
since fetchData()
not only utilizes doGet()
but also gives easy access to helper params.
getInternal()
Same as getExternal()
, but on a secured connection. All headers are appended with an ‘Authentication-Token’.
getWidgetBackend()
Same as getInternal()
, but it allows you to call previously defined widget backend endpoints (see Widget backend section for details).
getNewManager(ip)
Returns a manager object connected on the specified IP. May be needed in order to join a different manager (eg. for cluster joining).
getContext()
A widget context gives access to the application context. Using the context we can pass arguments between widgets, for example when a blueprint is selected, set the context to the selected blueprint, and all the widgets that can filter by blueprint can read this value and filter accordingly.
The context supports these methods:
- setValue(key,value)
- getValue(key) - returns value
getConfig()
Returns global widget configuration as defined in conf/widgets.json.
refresh()
If we did some actions in the widget that will require fetching the data again (for example we added a record) we can ask the app to refresh only this widget by calling refresh().
loading(boolean)
Will show/hide a loading spinner in widget header. Not allowed in render() and postRender() methods as it changes store’s state leading to render() and postRender() re-run.
drillDown(widget,defaultTemplate,drilldownContext)
When you drill down to a page, you must pass the drilldown page template name. When a widget is on a page and you use the drilldown action (for example, in a link click event to a button) for the first time to access the page, the app creates a new page based on the passed template. When this page is created, the user can edit it like any other page. Each time the user accesses to this page, the existing page is shown.
You can also pass a drilldownContext to the drilldown page. This context is saved on the URL and is available through the app context. This value is persistent, so if a user drills down to a page and then refreshes the page, the context is saved. For example, with the selected deployment in drilldown deployment page.
For example when selecting a deployment we drill down to a deployment page. It looks like this:
_selectDeployment(item) {
this.props.toolbox.drillDown(this.props.widget,'deployment',{deploymentId: item.id});
}
You can see an example of the deployment template in the Cloudify UI repository in the /pages/deployment.json file.
Drilldown Page Templates
Drilldown page templates are defined in the /templates/pages directory. Each file contains one page template configuration.
Page template configuration has a name which is the default page name, and list of widgets. Each widget will have the following fields
field | description |
---|---|
name | Widget default name |
definition | The ID of the widget to use |
width | The initial width of the widget on the page |
height | The initial height of the widget on the page |
x | The initial x location of the widget on the page |
y | The initial y location of the widget on the page |
configuration | The initial configuration of the widget (Optional) |
If x and/or y are not defined the page will be auto arranged (not recommended)
For example:
{
"name": "template-name",
"widgets": [
{
"name": "Deployments",
"definition": "deployments",
"width": 12,
"height": 24,
"x": 7,
"y": 35,
"configuration":{
"displayStyle":"list"
}
},
...
]
}
External Libraries
The external libraries available to a widget are: moment
, jQuery
and Lodash
.
moment - Date/Time parsing utility. Moment documentation
for example:
var formattedData = Object.assign({},data,{
items: _.map (data.items,(item)=>{
return Object.assign({},item,{
created_at: moment(item.created_at,'YYYY-MM-DD HH:mm:ss.SSSSS').format('DD-MM-YYYY HH:mm'),
updated_at: moment(item.updated_at,'YYYY-MM-DD HH:mm:ss.SSSSS').format('DD-MM-YYYY HH:mm'),
})
})
});
jQuery - Feature-rich JS library. jQuery API
for example:
postRender: function(el,widget,data,toolbox) {
$(el).find('.ui.dropdown').dropdown({
onChange: (value, text, $choice) => {
context.setValue('selectedValue',value);
}
});
})
Lodash - Modern JavaScript utility library delivering modularity, performance & extras. Lodash documentation
for example:
_.each(items, (item)=>{
//...
});
Widget Backend
With widget backend support user can create HTTP endpoints in UI backend. They allow to define specific actions when endpoint is called. There can be used helper services not available in widget frontend.
Security Aspects
- Endpoint is accessible only from the widget which created that endpoint.
- Access to external libraries can be limited to preconfigured set of libraries.
Defining Endpoints
To create endpoint per widget you need to create backend.js
file with at least one endpoint definition. That file must be placed in widget main folder similarly to widget.js
file.
backend.js
file structure
Example of backend.js
file is presented below:
module.exports = function(r) {
r.register('manager', 'GET', (req, res, next, helper) => {
var _ = require('lodash');
var jsonBody = require('body/json');
var url = req.query.endpoint;
var params = _.omit(req.query, 'endpoint');
var headers = req.headers;
jsonBody(req, res, function (error, body) {
helper.Manager.doPost(url, params, body, headers)
.then((data) => res.send(data))
.catch(next);
})
});
}
backend.js
file should export a function taking one argument (r
in example). This function’s body contains calls to register method (r.register
in example). Each call registers HTTP endpoint in the backend.
Syntax of register
method:
function register(name, method, body)
where
name
- String with HTTP endpoint name on which service will be registered,method
- String with HTTP endpoint method on which service will be registered,body
- Function (function(req, res, next, helper)
) to be called on request to this endpoint, where:req, res, next
- Part of middleware function (see Using middleware @ ExpressJS for details)helper
- JSON object containing Helper services.
Helper Services
In this section helper services, which can be used from helper
object in endpoints body are described.
Manager
Available methods:
call(method, url, params, data, headers={})
- Performs HTTP request to Cloudify ManagerdoGet(url, params, headers)
- Performs HTTP GET request to Cloudify ManagerdoPost(url, params, data, headers)
- Performs HTTP POST request to Cloudify ManagerdoDelete(url, params, data, headers)
- Performs HTTP DELETE request to Cloudify ManagerdoPut(url, params, data, headers)
- Performs HTTP PUT request to Cloudify ManagerdoPatch(url, params, data, headers)
- Performs HTTP PATCH request to Cloudify Manager
where:
method
- HTTP methods (allowed methods: ‘GET’, ‘POST’, ‘DELETE’, ‘PUT’, ‘PATCH’)url
- Manager REST API URL (eg.blueprints
, see Cloudify REST API documentation for details)params
- JSON object with URL parameters (key is parameter name, value is parameter value, eg.{param1: 'value1', param2: 'value2'}
) (Optional)data
- JSON object with request body (Optional)headers
- JSON object with request headers (Optional)
Request
Available methods:
call(method, url, params, data, parseResponse=true, headers={})
- Performs HTTP requestdoGet(url, params, parseResponse, headers)
- Performs HTTP GET requestdoPost(url, params, data, parseResponse, headers)
- Performs HTTP POST requestdoDelete(url, params, data, parseResponse, headers)
- Performs HTTP DELETE requestdoPut(url, params, data, parseResponse, headers)
- Performs HTTP PUT requestdoPatch(url, params, data, parseResponse, headers)
- Performs HTTP PATCH request
where:
method
- HTTP methods (allowed methods: ‘GET’, ‘POST’, ‘DELETE’, ‘PUT’, ‘PATCH’)url
- HTTP URL (eg.http://example.com
)params
- JSON object with URL parameters (key - parameter name, value - parameter value, eg.{param1: 'value1', param2: 'value2'}
) (Optional)data
- JSON object with request body (Optional)parseResponse
- boolean value informing if response shall be parsed as JSON (Optional)headers
- JSON object with request headers (Optional)
Calling Endpoints
Previously defined endpoints can be accessed in widget’s frontend using toolbox.getWidgetBackend()
method (see getWidgetBackend() for details).
Example of calling endpoint status with GET method widget.js
:
Stage.defineWidget({
// ... all stuff necessary to define widget ...
fetchData: function(widget, toolbox, params) {
return toolbox.getWidgetBackend().doGet('status')
.then((data) => Promise.resolve({status: data}))
.catch((error) => Promise.reject('Error fetching status. Error: ' + error));
},
render: function(widget,data,error,toolbox) {
var status = data ? data.status : 'unknown';
return (
<p>
Status: <b>{status}</b>
</p>
);
}
});
The status endpoint for GET method must be defined in backend.js
file:
module.exports = function(r) {
r.register('status', 'GET', (req, res, next, helper) => {
res.send('OK');
});
}
Widget Template
The widget template is an html file written with lodash template engine.
Widget template if fetched when the widget definition is loaded, and its passed to the render function. To access it use widget.definition.template.
To render the template using the built in lodash templates engine use _.template(widget.definition.template)(data);
, where ‘data’ is any context you want to pass on to the template.
For example, a simple render function will look like this:
render: function(widget,data,toolbox) {
if (!widget.definition.template) {
return 'missing template';
}
return _.template(widget.definition.template)();
}
Useful Links
- Cloudify UI @ GitHub - Git repository with Cloudify UI source code
- Cloudify UI Widgets Boilerplate @ GitHub - Git repository containing widget development environment
- Widget Components API Reference - Auto-generated documentation of Cloudify built-in React components which can be used in custom widgets