- Cisco Observability Platform
- Guides
- Access Management
- Knowledge Store
- Introduction
- Define a Knowledge Type
- Define a Knowledge Object
- Use References in Knowledge Types and Objects
- Manage Secrets in the Knowledge Store
- Create a Knowledge Object
- List all Knowledge Objects of a Given Type
- Fetch a Knowledge Object by ID
- Update a Knowledge Object
- Delete a Knowledge Object
- Create a Patch
- Solutions
- Introduction
- Create a Solution
- Add a Knowledge Type to a Solution
- Add a Knowledge Object to a Solution
- Tag a Solution
- Validate a Solution
- Fork a Solution
- Deploy a Solution to Your Tenant
- Subscribe to a Solution
- Check the Solution Upload and Subscription Status
- Update the Manifest File of a Solution
- Deploy a Solution to the Cisco Observability Platform Exchange
- Bump the Solution Version
- List the Available Solutions
- List All Files in a Solution
- Delete the Knowledge Types and Objects of a Solution
- Delete a Solution
- Check the Solution Deletion Status
- Troubleshoot Solutions
- Data Modeling
- Solution Services
- Data Ingestion
- Data Queries
- Codex
- Health Rules
- Actions
- UI Enhancements
- Example Solutions
- Reference
- Cisco Observability Platform CLI
- Cisco Observability Platform REST APIs
- Alphabetical Reference
- Introduction
- actions:httpactiontemplate
- codex:workflow
- dashui:entityPage
- dashui:entityPagePropSet
- dashui:entityPresentation
- dashui:form
- dashui:navPresentation
- dashui:settingsConfig
- dashui:template
- dashui:templatePropsExtension
- fmm:adConfigOobTemplate
- fmm:associationDeclaration
- fmm:associationDerivation
- fmm:attributePromotion
- fmm:enrichment
- fmm:entity
- fmm:entityGrouping
- fmm:entityPriority
- fmm:extension
- fmm:event
- fmm:extensionDerivation
- fmm:metric
- fmm:metricAggregation
- fmm:metricAttributeMapping
- fmm:metricDerivation
- fmm:metricMapping
- fmm:namespace
- fmm:resourceMapping
- fmm:sourceMapping
- fmm:tagPropagation
- healthrule:healthRuleScopeOverrides
- healthrule:healthRuleTemplate
- iam:Permission
- iam:Role
- iam:RoleToPermissionMapping
- iam:SolutionPermissions
- logs:dataMaskingExpression
- logs:dataMaskingRule
- logs:dataMaskingRuleV1
- logs:logParsingRule
- solutionsecret:solutionSecret
- zodiac:egressHosts
- zodiac:function
- zodiac:secret
- zodiac:solutionCron
- zodiac:subscriptionCronConfig
- Community and Support
This documentation and the Cisco Observability Platform functionalities it describes are subject to change. Data saved on the platform may disappear and APIs may change without notice.
Introduction to DashUI
DashUI does not require writing, building, or deploying Javascript to create User Interfaces. This is unlike conventional UI frameworks such as React. It allows the composition of pages through configuration files in JSON format. This UI framework is tightly integrated with the Flexible Meta Model (FMM) and Unified Query Engine (UQE). Therefore, developers do not need to write queries and complex data binding.
User Interfaces are plugged together from atomic widgets, and the framework infers the necessary queries to the backend to populate them with data. It bundles and optimizes the data requests of the individual widgets so that the number of roundtrips and redundant data requests is minimized.
Because of the modular system, you can combine atomic widgets to form reusable, composite building blocks, which you can use to build pages. You can render the pages in the live UI once the corresponding configuration objects (called templates) are stored in the central Knowledge Store. An online authoring tool even allows rendering templates using actual data while being developed.
One feature of DashUI is the ability to extend templates across solutions. This allows solution developers to surface their content on existing pages. For example, they can achieve this by adding a column to a table or adding new pages to the navigation structure.
Entity Centric Pages
Similar to the data model, the user interface of Cisco Observability Platform applications is mostly entity-centric. The Observe page initially shows the entities of all domains to which a Tenant is subscribed. Each domain has a section that contains one group of bubbles for every entity type (the bubbles themself represent the entity instances grouped by health, indicated by the color of the respective bubble). When users click on such a bubble or an entire group of bubbles, they navigate to an Entity Centric Page (ECP), allowing them to examine this particular set of entities.
ECP shows a selection of entities (also called the scope of the ECP), which depends on the navigation history. For example, if you click on the green bubble in the services group of the APM section, you have selected the set of all healthy services. However, if you first click on the set of unhealthy service instances and then on the red services bubble in the Relationship Map, you navigate to an ECP displaying only the unhealthy services related to the unhealthy service instances in the previous ECP.
The scope can include:
- A set of entities (List view).
- A single entity (Detail view).
- A grouped set of entities (grouped list view).
Depending on the entity type and the nature of the scope (set/single/grouped), the structure of the ECP will be different. That is, a set of services is represented differently than a single service instance or a set of backends.
In addition to the structure, the content of the ECP will also differ depending on the scope, specifically on filters and traversals in the navigation history (similar to the previous example of unhealthy services related to a set of unhealthy service instances). It also differs on additional filters set by the user in the filter bar.
It is important to understand that the Relationship Map behaves like the rest of the ECP. It is not an absolute navigation tool but allows you to navigate to entities related to the currently selected (and filtered) set of entities.
Such a set of entities is referred as a topology context. The scope consists of a topology context in addition to grouping information.
Custom Slots for Entity Centric Pages
Using a CustomSlot
allows you to delegate template selection to the entity page mechanism. This creates scope dependent content. CustomSlots
are defined similar to an entity page, the only difference is that slots are defined with a custom slot name, instead of a scopeType
. To use a CustomSlot
, the slot name must be prefixed with the solution.
When you use a CustomSlot
, you can determine the rendering target in two ways:
- By using the global scope.
- By using the static reference of entities consisting of an
entityType
, anentity ID
, or an array with a combination of the two.
Scenario for using a CustomSlot
:
Solution A has a detailed entity page, and Solution B wants to add their content to that page. In order for Solution B to do this, Solution B can extend the template that is used to render the page. However, the template might be used in other places where Solution B's content should not appear. Therefore, the best way for Solution B to add content to that page is by using a CustomSlot
.
The following is an example of a CustomSlot
in a template:
Copy{
"kind": "template",
"name": "foo:bar",
"view": "default",
"target": "*",
"element": {
"instanceOf": "entityPage",
"slot": "dashui:myCustomSlot"
"entityRefs": ["k8s:pod"]
}
}
Macros for Entity Centric Pages
You can add macros
to a List view of an ECP. The DashUI solution provides a CustomSlot
, which uses the template dashui:macroList
. If you want to add a macro
to a page, then you use this slot with your entityPagePropSets
.
Defintions
Name | Description |
---|---|
dashui:macroList |
The template that provides a scroll box container and exposes props for macros . If no props or macros are provided, then nothing is rendered when this custom slot is called. This macro slot should not be overwritten. |
The following is an example of dashui:macroList
:
Copy{
"kind": "entityPage",
"scopeType": "dashui:macroList",
"entityType": "*",
"template": "dashui:macroList"
}
The following is the associated template with the macro
slot:
Copy{
"kind": "template",
"name": "dashui:macroList",
"props": {
"macros": "[]"
},
"propDefs": {
"macros": {
"reduce": {
"type": "concat",
"sort": "index"
}
}
},
"preprocess": "$count($props.macros) > 0 ? $ ~> | elements | { elements: $props.macros.element } | : $ ~> | $ | { instanceOf: empty } |",
"element": {
"instanceOf": "buttonScroll",
"elements": {
"instanceOf": "html",
"style": {
"display": "flex",
"gap": 12,
"alignItems": "stretch",
"padding": "12px 1px"
}
}
}
}
You can call the macro
slot the same way you call other template elements. The ecpList
has the macros
property that can be initialized using the left and right bars. The following is an example of calling the macro
slot:
Copy{
"kind": "template",
"name": "spacefleet:spacecraftEcpList",
"view": "default",
"target": "*",
"element": {
"instanceOf": "ocpList",
"left": {
"instanceOf": "spacefleet:spacecraftEcpRelationshipMap"
},
"right": {
"instanceOf": "spacefleet:spacecraftEcpListInspector"
},
"macro": {
"instanceOf": "entityPage",
"slot": "dashui:macroList"
},
"elements": "PLACEHOLDER"
}
}
To add a PropSet
to the Custom slot, you must define a new dashui:entityPagePropSet
and target the macro
slot dashui:macroList
. For the example below, the entity type in entityRef
is specified.
Copy{
"kind": "dashui:entityPagePropSet",
"id": "listMacroProps",
"slot": "dashui:macroList",
"entityRef": "spacefleet:spacecraft",
"enabled": true,
"props": {
"macros": [
{
"index": 1,
"element": {
"instanceOf": "macro",
"type": "scope",
"title": "My Test Macro",
"description": "This is an example",
"commands": [
"..."
],
},
},
],
},
}
Configurable Filter
You can use the DashUI filter for the following:
- To add or remove the filter bar from ECP pages, and the Observe page using the template.
- To configure your own filter keys such as
attributes
,tags
,type
,isActive
, and you can populate those key values. - To configure operators that you want to hide or expose.
The following is an example of how to add or remove the filter bar from ECP pages and from the Observe page:
Copy{
"kind": "template",
"name": "k8s:clusterEcpList",
"view": "default",
"target": "*",
"element": {
"instanceOf": "ocpList",
"left": { "instanceOf": "k8s:clusterEcpRelationshipMap" },
"right": { "instanceOf": "k8s:clusterEcpListInspector" },
"filterInput": {
"instanceOf": "empty"
},
"elements": [
{
"instanceOf": "card",
"props": {
"style": {
"width": "100%",
"height": "calc(100% - 298px)",
"padding": 0
}
},
"elements": [{ "instanceOf": "k8s:clusterGridTable" }]
}
]
}
},
The following is an example of how to implement your own filter keys :
Copyimport { buildInstantiator, defineInstantiatorFactory } from '@appd/config-driven';
import { FilterInput } from '@/modules/shared';
interface ScopeFilterElement {
excludeKeys?: string[];
excludeOperators?: string[];
attributeOperators?: string[];
tagOperators?: string[];
typeOperators?: string[];
}
export const scopeFilter = defineInstantiatorFactory(({ element }) => {
const { excludeKeys, excludeOperators, attributeOperators, tagOperators, typeOperators } = element as ScopeFilterElement;
const customOperators = {
attributes: attributeOperators,
tags: tagOperators,
types: typeOperators,
};
return Promise.resolve(
buildInstantiator(() => <FilterInput customOperators={customOperators} hiddenSubjects={excludeKeys} excludeOperators={excludeOperators} />),
);
});
The following is an example of excluding the aws.region
tags for clusterECPList
using the ~
operator:
Copy{
"kind": "template",
"name": "k8s:clusterEcpList",
"view": "default",
"target": "*",
"element": {
"instanceOf": "ocpList",
"left": { "instanceOf": "k8s:clusterEcpRelationshipMap" },
"right": { "instanceOf": "k8s:clusterEcpListInspector" },
"filterInput": {
"instanceOf": "scopeFilter",
"excludeKeys": ["tags(aws.region)"],
"excludeOperators": ["~"]
},
"elements": [
{
"instanceOf": "card",
"props": {
"style": {
"width": "100%",
"height": "calc(100% - 298px)",
"padding": 0
}
},
"elements": [{ "instanceOf": "k8s:clusterGridTable" }]
}
]
}
},
Configurable Filter Using scopeGroupBy
The scopeGroupBy
building block renders the group by using the input component.
Properties | Description |
---|---|
groupingCategories |
This is the array listing each grouping category to be enabled. By default, globalTags are enabled. |
excludeKeys |
This is an array of tag keys that are excluded. For example, if you want exclude the tag aws.region , then use excludeKeys=["aws.region"] . |
customKeys |
This is an array of tag keys that can be included. For example, if you want to include tags(customKey1) and tags(customKey2) , then use customKeys=["customKey1", "customKey2"] . |
The following is an example format of the scopeGroupBy
building block:
Copy{
"instanceOf": "scopeGroupBy"
}
The following is an example using a sub-set of grouping categories:
Copy{
"instanceOf": "scopeGroupBy",
"groupingCategories": ["regularTags"]
}
The following is an example using excludeKeys
and customKeys
:
Copy"groupByInput": {
"instanceOf": "scopeGroupBy",
"customKeys": ["customKey1", "customKey2"],
"excludeKeys": ["aws.region"]
},
The following is an example of removing groupBy
from a page:
Copy"groupByInput": {
"instanceOf": "empty"
},
Templates
As the underlying data model of a domain is not hard-coded but modeled, the Entity Centric Pages visualizing this data are configured as JSON artifacts, which are named UI templates.
Templates:
- Are stored in the Knowledge Store as part of a solution.
- Define the arrangement, configuration, and data binding of their components.
- May contain complete pages with a pre-built design, composite components, atomic components, styling (such as fonts, colors, theme effects, background styles, and icons), and more.
- Can be nested. Hence, users can use a template within another template.
- Streamline the process of UI development by providing working units that can be composed without writing JavaScript code.
You write a template to apply to a topology context with a specific entity type, not arbitrary selection criteria. It does not contain an absolute query but instead displays the content it gets from the topology context. However, it does rely on the entity type and cardinality of that topology context for the attributes, metrics, and associations that it can render.
Therefore, a template consists of header information and template element. The header information contains the name and expected type of the topology context (target
), whereas the template element contains the actual configuration data.
The following is an example of a simple template to render a single entity of type service:
Copy{
"kind": "template",
"target": "apm:service",
"element": {
"instanceOf": "string",
"path": "attributes(service.name)"
}
}
kind
refers to the document type of the template (as required by the Knowledge Store).target
specifies that it applies to entities of the typeapm:service
.element
represents the actual template content. Template elements are always configurations of building blocks. The type of building block is indicated by the keyinstanceOf
(here, it'sstring
). Building blocks can either be atomic or other templates. All other keys in the element reflect the respective building block’s configuration parameters. The building block typestring
has a parameterpath
which contains the UQL fragment required to get the content rendered from the entity in the topology context. The only attribute required here isservice.name
. A building block of typestring
is an active building block. It does not require any explicit data binding information beyond thepath
. It knows how to collect the information specified bypath
from its topology context. On the other hand, passive building blocks have no notion of the topology context. These are just generic rendering components. To make them display data, you need to add explicit data binding and data transformation toelement
.
Limitation: Adding explicit data binding and data transformation to a template's building block is not yet supported.
The following is an example of a template representing a whole ECP list view:
Copy{
"kind": "template",
"name": "apm:service-ecpList",
"target": "apm:service",
"element": {
"instanceOf": "ecpList",
"elements": [
{
"instanceOf": "card",
"props": {
"style": {
"border": "solid 1px #464957",
"borderRadius": "5px",
"backgroundColor": "rgb(38, 41, 57)",
"padding": 0
},
"elements": [
{
"instanceOf": "apm:service-ecpListVisualizationTab"
}
]
}
}
]
}
}
The building block used at the top level is ecpList
. This renders the structure of an ECP (including relationship map and inspector panel). However, it receives the elements that need to be rendered in the center as part of its configuration.
In this case, there is only one element, a card that provides a colored background. You can configure the card to contain further child elements. In the template, the name apm:service-ecpListVisualizationTab
is the child element.
Instead of using a reference to a template, you can embed the element
of the referred template to the same effect. Using named templates instead of inlining the respective elements has two advantages. A named template can be:
- Reused in many other templates with no redundancy
- Addressed by template extensions
Template Extensions
Every Solution can define its templates to render Entity Centric Pages for the new entity types. But how can you enable users to navigate to your new entities from the ECPs of related entities in other domains?
The navigation to related entities is defined in the Relationship Map template of each entity type. But as a solution developer, you are not allowed to modify the templates of other solutions. However, you are allowed to extend templates of foreign solutions.
A template extension describes modifications to a specific template. For example, extensions can add elements to templates, such as columns to a table or links to a relationship map. When a template is loaded, all its extensions from subscribed solutions are applied (in no particular order).
Parameterized Templates expose explicit extension points (props
) that can be set by a template extension (templatePropsExtension
). The template itself then contains the logic that modifies itself according to the input from one or more extensions. Using this extension mechanism is safe because:
- The extension does not need to know the internal structure of the template. Hence, it can be changed at any time without risk.
- The internal structure of the extended template cannot be modified in an uncontrolled manner.
If the template that you are extending does not expose props, you can use the templateExtension
artifact. This requires knowledge of the internal structure of the template. This flavor of template extension allows the addition or removal of elements to/from arrays indicated by a path expression.
Using templateExtension
is not recommended because such extensions break when the author of the extended template changes the inner structure.
Forms
In order to create and edit configurations, corresponding Forms need to be generated. Forms are based on the schema of the knowledge type, and the configuration is a JSON object, and consists of different element
types, where the type
indicates the element type.
Defintions
Name | Description |
---|---|
Target object | The knowledge object that results from filling in the form . Usually this is a Knowledge Store, but it can be any knowledge object described by a schema. |
Target object type | A knowledge object that describes the target object. |
Object schema | The JSON schema of an object, or sub-object that is created or edited. In the case of the target object, this schema is part of the target object type in the Knowledge Store. |
Form configuration | The JSON artifact that describes a form , which is generated when creating, or editing a target object. |
Form element | An element within the form configuration. For example, the configuration of a control. |
Structure of Forms
A Form consists of header data, and a tree of elements
, which follows this syntax:
Copy{
"kind": "form",
"id": "namespace:name",
"@schema": "some-knowledge-type",
"elements": []
}
The id
with the namespace
is a unique identifier of the Form, and the schema
refers to the schema of the object that the Form produces. This is shown as an inlined JSON schema object, or as a reference in the form of a string that contains the id
of a knowledge type. If the elements
field is omitted, then the entire Form is generated from the schema of the ZeroConfig
algorithm, which follows this syntax:
Copy{
"kind": "form",
"id": "dashui:demoTeamExampleE2e",
"@schema": "dashuiMockSolution:demoTeam",
"description": "e2e test"
}
Form Elements
An array of elements
contains objects with a type
that specifies the element type, and additional type-specific properties, such as style. Element
types include: form.Control
, form.Repeater
, form.Dictionary
, form.Group
, form.Switch
, form.Html
, form.CodeSnippet
, ContainerCard
, form.Template
, form.Wizard
. The common property for form.Control
, form.Group
, and form.Repeater
is @path
. The @path
specifies the field, or sub-object in the target that the element
configures, which must be consistent with the object schema.
Control Element
The form.Control
element creates an interactive control for entering Form data. The type of control can be specified by the parameter control, or inferred from the schema of the schema object field specified in the @path
. The value of @path
indicates the location of the field in the schema of the Group
. @Path
segments are separated by /
characters. Valid values for form.Control
include:
Value | Detail |
---|---|
checkbox |
• description : string • defaultValue : boolean • disabled : boolean • label : string |
multiselect |
• disabled : boolean • label : string • searchable : boolean • secondaryHint : string • hint : string • tooltip : string • optionLabelPath : string • optionValuePath : string |
number |
• acceptFloat : boolean • disabled : boolean • secondaryHint : string • hint : string • tooltip : string |
password |
• disabled : boolean • hint : string • secondaryHint : string • tooltip : string |
radio |
• optionDescriptionPath : string • optionLabelPath : string • optionValuePath : string |
rowSelect |
• rows • columns • height : numberals for the grid • hideCheckbox : boolean • multiselect : boolean |
select |
• disabled : boolean • label : string • searchable : boolean • secondaryHint : string • hint : string • tooltip : string • optionLabelPath : string • optionValuePath : string |
text |
• disabled : boolean • readOnly : boolean • secondaryHint : string • hint : string • tooltip : string |
textarea |
• disabled : boolean • readOnly : boolean • secondaryHint : string • hint : string • tooltip : string • rows : number |
currency |
• maximumFractionDigits : number • label : string • required : boolean • disabled : boolean • secondaryHint : string • hint : string • tooltip : string |
toggle |
• disabled : boolean • label : string • showDescription : boolean |
The Control element follows this syntax:
Copy{
"@type": "form.Control",
"control": "text",
"@path": "/foo",
"label": "Example"
}
Repeater Element
The form.Repeater
element allows you to populate an array by adding rows, or sub-forms to create array elements. Apart from @path
, form.Repeater
has the property, itemElements
. itemElements
is the child form element that is used to input each child element of that array.
The Repeater element follows this syntax:
Copy{
"@type": "form.Repeater",
"@path": "/foo",
"itemElements": [
{
"@type": "form.Html",
"text": "Repeater Example",
"tag": "h2"
},
{
"@type": "form.Control",
"control": "text",
"@path": "/text",
"label": "Text Example"
}
]
}
Dictionary Element
The form.Dictionary
element is similar to the form.Repeater
element. The only difference is that form.Dictionary
has the value object type
, instead of array type
. The autoGeneratedKey
is used for auto-generating the key by the values, which is the default behavior. The
itemElements
is the child form element that is used to input each child element of the array.
The Dictionary element follows this syntax:
Copy{
"@type": "form.Dictionary",
"@path": "/repeater",
"autoGeneratedKey": false,
"itemElements": [
{
"@type": "form.Control",
"@path": "/...",
"control": "text",
"label": "..."
},
]
}
Group Element
The form.Group
element has two properties, schema
and elements
. A schema
is either an object, or a string identifying the knowledge object. elements
include child elements of the group
. If there are no elements
provided, then the Form will use ZeroConfig
automatically.
The Group element follows this syntax:
Copy{
"@type": "form.Group",
"@schema": {
"properties": {
"foo": {
"type": "string"
},
"bar": {
"type": "string"
}
},
"type": "object"
},
"elements": [
{ "@type": "form.Control", "@path": "/foo", "control": "..." },
{ "@type": "form.Control", "@path": "/bar", "control": "..." }
]
}
Limitation: form.Group does not support multiple groups with the same @path.
Wizard Element
The form.Wizard
element guides you through a complex process using a step-by-step approach. Each step
in the Wizard represents a part of the process, and you can choose if one step
needs to be concluded before proceeding to the next.
The Wizard element can be configured to have its own Submit or Cancel button that performs the configured operation. By default, these buttons are hidden as the actions they represent are executed by the Container
element. However, you can hide the Container
options and use the Wizard's action options. This provides the flexibility to either use the default actions provided by the Container
, or to use the actions within the Wizard's own interface.
Wizard Actions | Detail |
---|---|
steps |
Step has the following parameters: • name (optional): string • description (optional): string • stepElements (optional): Array<FormElement> FormElement • required (optional): boolean • default : false |
size |
• size (optional): string • COMPACT • NORMAL |
blockOnError |
• blockOnError (optional): boolean • default : true • If an error occurrs in the current step , then navigation to the previous and next step will be blocked until it's resolved. |
cancelLabel |
• cancelLabel (optional): string • boolean |
nextStepLabel |
• nextStepLabel (optional): string • boolean |
previousStepLabel |
• previousStepLabel (optional): string • boolean |
submitLabel |
• submitLabel (optional): string • boolean |
Copy{
"@type": "form.Wizard",
"@path": "/wizard",
"steps": [
{
"name": "Step 1",
"description": "This is a simple example",
"required": true,
"elements": [
{
"@type": "form.Control",
"control": "text",
"@path": "/text",
"label": "Text Example"
},
{
"@type": "form.Control",
"control": "text",
"@path": "/text2",
"label": "Text Example 2"
}
]
}
]
}
Switch Element
The form.Switch
element is used to select elements depending on the value of an expression. $by
contains a JSONata expression that evaluates a string. The result of the expression evaluation is compared with the entries from Cases. Cases is a dictionary that maps the possible results of $by
to corresponding Form elements. The default
, which is optional, is the Form element chosen if no key in the hashmap matches the result of $by
. After evaluating the $by
expression, the corresponding element takes the place of the Switch element.
The Switch element follows this syntax:
Copy{
"@type": "form.Switch",
"$by": "$parent().entity",
"cases": {
"Cluster": {
"@type": "form.Control",
"@path": "/value",
"control": "text",
"label": "New Cluster Name"
},
"Namespace": {
"@type": "form.Control",
"@path": "/value",
"control": "text",
"label": "New Namespace Name"
},
"Pod": {
"@type": "form.Control",
"@path": "/value",
"control": "text",
"label": "New Pod Name"
}
},
"default": {
"@type": "form.Html",
"tag": "span",
"text": "No entity type was selected"
}
}
HTML Element
The form.Html
element contains these properties: tag
, elements
, text
.
tag
: The HTML element type, such asdiv
orimg
.elements
: Contains new Form elements, which can be omitted.text
: Identifies if the HTML element should contain text. This applies only ifelements
is omitted.
The HTML element follows this syntax:
Copy{
"@type": "form.Html",
"text": "Example Headline",
"tag": "h2"
}
{
"@type": "form.Html",
"tag": "div",
"style": { },
"elements": [
{
"@type": "form.Control",
"control": "..."
},
{ }
]
}
CodeSnippet Element
The form.CodeSnippet
element is used to display code. The form.CodeSnippet
element contains these properties: label
, maxRows
, showActions
, showRows
, showActionsOnHoverOnly
, value
.
label
: A string headline for aCodeSnippet
block.maxRows
: The number limit that the rows display.showActions
: A boolean which controls whethercopy
, andfold
actions ofCodeSnippet
are displayed or hidden.showRows
: A boolean which controls whether row numbers are displayed or hidden.showActionsOnHoverOnly
: A boolean which controls whether actions are displayed when hovering, or whether they are always displayed.value
: The value shown in the code snippet. This can also be a JSONata expression to display fetched data.
The CodeSnippet element follows this syntax:
Copy{
"@type": "form.CodeSnippet",
"value": "Foo\nBar\nBaz\nQux\nFooo\nBarr\nBazz\nQuxx",
"label": "Display Actions only on Hover",
"maxRows": "3",
"showRows": true,
"showActions": true,
"showActionsOnHoverOnly": true
}
{
"@type": "form.CodeSnippet",
"@fetch": {
"clusters": {
"type": "uqe",
"query": "SINCE now-1h FROM clusterList: entities(k8s:cluster) FETCH id: id, name: clusterList.attributes(k8s.cluster.name)",
"url": "/api/fso/monitoring/v1/query/execute"
}
},
"$value": "'You can select from a list of Cluster:\n\n' & $reduce($var('clusters').rows.id, function($v, $i) { $v & '\n' & $i }) & '\n\nPlease make sure to select the correct cluster.'",
"label": "Display Actions only on Hover",
"showActions": true
}
ContainerCard Element
The ContainerCard
element can be used to wrap the form within a Card which has predefined styles, such as padding and background.
Value | Detail |
---|---|
elements |
This defines any FormElement . |
title |
The title of the Card. |
style |
This allows you to customize the style of the Card. |
The ContainerCard
element follows this syntax:
Copy{
"@type": "form.ContainerCard",
"title": "Demo Title",
"style": {},
"elements": {
}
}
Template Element
The template
element can be used to provide instructions on how to interact with forms
, and uses UIK components without needing to style components. The benefit of using a template
is that you can use the entire feature set of v1plus
. For example, if you want to navigate to another Form page to create a new entity, then you can use the v1plus
action; navigate.settings
. A template
can either be a string, or an inline template.
navigate.settings
includes these actions:
Action | Detail |
---|---|
settingsId |
(Required): string ID of the settingsConfig to navigate to. |
objectId |
(Optional): string ID of the object that is edited. This is only required if you want to navigate to an existing object edit page. |
params |
(Optional): Record URL search parameter object that is processed, and attached to the URL as search string . |
section |
(Optional): string only required if a settings page is part of a custom section in the navigation. This defaults to Modules . |
create |
(Optional): boolean flag that must be set to true if the navigation leads to creating a page. |
The Template element follows this syntax:
Copy[
{
"@type": "form.Template",
"template": "my:template"
},
{
"@type": "form.Template",
"template": {
"element": [
{
"@type": "uik.Button",
"children": "Navigate to Form CREATE",
"icon": "Navigation.DisclosureRight",
"onClick": {
"@action": "navigate.settings",
"payload": {
"settingsId": "dashuiplayground:demoSettingsConfig",
"create": true
}
},
"type": "PRIMARY"
}
]
}
}
]
Data Handling for Forms
All Form elements require data handling for populating selection controls, validating input, and controlling the sequence of steps. This is accomplished by the actions: @read
, fetch
, JSONata expressions, and validations. For each of these actions, a corresponding object is defined. Those blocks complement the scope of the Form element with variables that can be used in JSONata expressions.
@read
Action: This reads fields from the target object in its current state of completion, and assigns them to variable names in the local scope.
Copy{
"@type": "...",
"@read": {
"$country": "country",
"$province": "$.province",
"$state": "$root().state"
}
}
@fetch
Action: This allows you to retrieve information from an API, and write it into the scope.@fetch
is a dictionary where the variable name is the key, and the value is an object with the following properties.
JSON Example:
The example below calls the end point /api/provinces
and passes the value of country
as the request parameter country
and the string name
as the request parameter fields
.
Copy{
"@type": "...",
"@fetch": {
"provinces": {
"params": {
"$country": "$var('country')",
"fields": "name"
},
"url": "/api/provinces",
"type": "json"
}
}
}
UQE Example:
If the @fetch
block contains a dictionary where the type set is as UQE
, then you can use the UQE syntax to fetch data.
Copy{
"@type": "form.Control",
"fetch": {
"pods": {
"$skip": "$not($exists($var('cluster')))",
"query": "${\"SINCE now-1h FROM entities(\" & $var('cluster') & \").out.to(k8s:namespace).out.to(k8s:workload).out.to(k8s:pod) FETCH id: id\"}",
"type": "uqe"
}
},
"$options": "$var('pods').rows.id",
}
Knowledge Store Example:
If the @fetch
block contains a dictionary where the type is set as objstore
, then you can interact with the Knowledge Store API.
Copy{
"@type": "form.Control",
"@fetch": {
"exampleFetch": {
"type": "objstore",
"layerType": "LOCALUSER",
"fullyQualifiedTypeName": "dashui:template",
"query": {
"order": "asc"
}
}
},
"@path": "/example",
"control": "select",
"label": "Select an existing dashui:template from your localuser store",
"$options": "$var('exampleFetch').id"
}
If you want to use the result of the first selection in the next Control, then fetch the level above using $var
.
- JSONata Action: To use JSON in a value, you must flag the key with
"$" - $key: "EXPRESSION"
. The$
signals that the value contains a JSONata expression. If a key is flagged with a$
, then the value is a string JSONata expression. The following custom functions can be used inside JSONata expressions:$var('KEY')
: To access@read
bindings or prior@fetch
results.$parent().KEY
: To access values written against@path
:/foo
of the parent element.$root().KEY
: To access values written against@path
:/foo
of the root element.$mode()
: To access if the Form object is in create mode or edit mode. This can also be used for disabling a control.$baseURL()
: This allows you to accessglobalThis.document.UR
in the Form, and can not be used in@fetch
.$format()
: This allows you to expose the$format
functionality of UIK in the JSONata of FormUI.$format
follows this structure:$format(typeName, value, params) => string
.- Available
$format
functions include:currency
,date
,date.long
,date.longWithWeekday
,date.short
,date.shortWithFullYear
,dateTime
,dateTime.long
,dateTime.longWithWeekday
,dateTime.short
,dateTime.shortWithFullYear
,duration
,duration.withSeconds
,number
,number.bitRateBinary
,number.bitRateDecimal
,number.bytesBinary
,number.bytesDecimal
,number.long
,number.percent
,number.short
,time
,time.long12
,time.long24
,time.short12
,time.short24
.
Copy{
"control": "text",
"@type": "form.Control",
"@path": "...",
"$disabled": "$mode() = 'EDIT' ? true : false "
}
- Validate Action:
The values
Control
,Group
andRepeater
are validated against the schema with the Ajv JSON schema validator, a third party library. In the example below, theminLength
initems
creates the validation.
Copy{
"properties": {
"repeaterDemo": {
"type": "array",
"items": {
"type": "string",
"minLength": 3
}
}
},
"type": "object"
}