...
We use these as a place to hold other configurable fields, where we know things such as what type of data we're expecting. The urlLink will be used to display a link at the bottom of the simpleSearch widget, which the user can configure to take them to the same query in the SASQ screen of whatever app is relevant. The numberOfRows is how we specify a maximum number of rows for the fetch to return. The way that SimpleSearch type is set up is that these can either be set to be user-configurable or not in the WidgetDefinition. For example the ERM Agreements WidgetDefinition entry for this section See below for how this would be configured in a definition.
WidgetDefinition
A WidgetDefinition ready to be accepted into the database for this type might look like the following:
Code Block |
---|
"configurableProperties": { "urlLinktype": { "configurablename": true"SimpleSearch", "version": "v1.0.0" }, "numberOfRowsversion": { "v1.0.0", "configurablename":"ERM falseAgreements", "defValuedefinition": 5{ ... } } |
Here you can see that urlLink is set to be configurable, whereas numberOfRows is non-configruable, and has a defaultValue. This will remove that entry on the dynamic widget form. The Type schema is set up to enforce defValue if configurable is set to false. In the case where a defValue is set and configurable is set to true this will manifest as a default value in the initial form creation.
WidgetDefinition
A WidgetDefinition ready to be accepted into the database for this type might look like the following:
Code Block |
---|
{
"type": {
"name": "SimpleSearch",
"version": "v1.0.0"
},
"version": "v1.0.0",
"name":"ERM Agreements",
"definition": {
...
}
} |
(This top level shape will be shared by definitions for ALL widget types)
We will get to the definition in a moment, but first notice that we use text fields here for the "type" field. We will then use these to attempt to resolve which type this definition conforms to (If in fact after resolution we do not find the relevant type, or the "definition" field fails validation then this will error out).
We also have a "version" field. This will be used to resolve definitions in case breaking changes are made to a widgetDefinition, eg columns removed or API paths changing. The idea is that widgets created using the old version of the widgetDefinition will not immediately fail.
(The concept of what we should do here, perhaps rendering a red "attention needed" widget in place of out of date ones or a simple wanring MessageBanner, will be later determined by user use cases and stories)
The definition section is what contains the meat of the WidgetDefinition, the actual JSON conforming to the Type.
...
(This top level shape will be shared by definitions for ALL widget types)
We will get to the definition in a moment, but first notice that we use text fields here for the "type" field. We will then use these to attempt to resolve which type this definition conforms to (If in fact after resolution we do not find the relevant type, or the "definition" field fails validation then this will error out).
We also have a "version" field. This will be used to resolve definitions in case breaking changes are made to a widgetDefinition, eg columns removed or API paths changing. The idea is that widgets created using the old version of the widgetDefinition will not immediately fail.
(The concept of what we should do here, perhaps rendering a red "attention needed" widget in place of out of date ones or a simple wanring MessageBanner, will be later determined by user use cases and stories)
The definition section is what contains the meat of the WidgetDefinition, the actual JSON conforming to the Type.
Code Block |
---|
{ "baseUrl":"/erm/sas", "results": { "columns": [ { "name":"agreementName", "label": "Agreement name", "accessPath":"name", "valueType": "String" }, { "name":"startDate", "label": "AgreementStart namedate", "accessPath":"namestartDate", "valueType": "StringDate" }, { "name":"startDateendDate", "label": "StartEnd date", "accessPath":"startDateendDate", "valueType": "Date" }, { "name":"endDateagreementStatus", "label": "End dateStatus", "accessPath":"endDateagreementStatus.value", "valueType": "DateString" }, { "name":"agreementStatus", "label": "Status", "accessPath":"agreementStatus.value", "valueType": "String" } ] ] }, "filters": { "columns": [ { "name":"agreementName", "label": "Agreement name", "filterPath":"name", "valueType": "String", "comparators": ["=~", "!~"] }, { "name":"agreementStatus", "label": "Agreement status", "filterPath":"agreementStatus.value", "valueType": "Enum", "enumValues": [ {"value": "active", "label": "Active"}, {"value": "closed", "label": "Closed"}, {"value": "in_negotiation", "label": "In negotiation"} ], "comparators": ["==", "!="] }, { "name":"startDate", "label": "Start date", "filterPath":"startDate", "valueType": "Date", "comparators": ["==", "!=", ">", ">=", "<", "<="] }, { "name":"endDate", "label": "End date", "filterPath":"endDate", "valueType": "Date", "comparators": ["==", "!=", ">", ">=", "<", "<=", "isNull", "isNotNull"] }, { "name":"internalContact", "label": "Internal contact", "filterPath":"contacts.user", "valueType": "UUID", "comparators": ["==", "!="] } ] }, "sort": { "columns": [ { "name":"id", "sortPath":"id", "sortTypes": ["asc"] }, { "name":"agreementName", "sortPath":"name", "sortTypes": ["asc", "desc"] }, { "name":"startDate", "sortPath":"startDate", "sortTypes": ["asc", "desc"] }, { ":"startDate", "sortTypes": ["asc", "desc"] }, { "name":"endDate", "sortPath":"endDate", "sortTypes": ["asc", "desc"] } ] }, "configurableProperties": { "urlLink": { "configurable": true }, "numberOfRows": { "configurable": false, "defValue": 5 } } } |
There's a lot of information there, so I'm going to break down some of the headlines.
Results
This section is all about display in the final table. To that end the value types allowed are all from the following list
Code Block |
---|
["String", "Integer", "Float", "Boolean", "Date"] |
Notice for example that the valueType for agreementStatus is "String", whereas later on the same path is described as an "Enum". We will look more at the filters later, but for right now all you need to know is that a valueType String means that the resulting field will be displayed as simple text in the resulting table.
Likewise we use "Boolean" and "Date" types to drive display of either a FormattedUTCDate, or a tick/cross for true/false.
These columns are then used to populate a component which allows us to pick one or several of these to form the columns for our display table in the output. The label will be the default label displayed for that column, although this can be overwritten.
Filters
This section drives both how we dynamically create the filter sections as well as how we build the filter part of the final query. As mentioned above lets look first at the agreementStatus entry:
Code Block |
---|
{ "name":"endDateagreementStatus", "sortPathlabel": "endDateAgreement status", "sortTypesfilterPath": ["ascagreementStatus.value", "descvalueType"] } ] }: "Enum", "configurablePropertiesenumValues": {[ {"urlLinkvalue": { "configurable"active", "label": true "Active"}, {"numberOfRowsvalue": { "configurableclosed": false, "defValuelabel": 5 } } } |
There's a lot of information there, so I'm going to break down some of the headlines.
Results
This section is all about display in the final table. To that end the value types allowed are all from the following list
Code Block |
---|
["String", "Integer", "Float", "Boolean", "Date"] |
Notice for example that the valueType for agreementStatus is "String", whereas later on the same path is described as an "Enum". We will look more at the filters later, but for right now all you need to know is that a valueType String means that the resulting field will be displayed as simple text in the resulting table.
Likewise we use "Boolean" and "Date" types to drive display of either a FormattedUTCDate, or a tick/cross for true/false.
These columns are then used to populate a component which allows us to pick one or several of these to form the columns for our display table in the output. The label will be the default label displayed for that column, although this can be overwritten.
Filters
This section drives both how we dynamically create the filter sections as well as how we build the filter part of the final query. As mentioned above lets look first at the agreementStatus entry:
Code Block |
---|
{ "name":"agreementStatus", "label": "Agreement status", "filterPath":"agreementStatus.value", "valueType": "Enum", "Closed"}, {"value": "in_negotiation", "label": "In negotiation"} ], "comparators": ["==", "!="] } |
As before we have a name and label, used to help the user while they build their widget configuration in the form. Here we have specified that the agreement status has type "Enum". This is because agreementStatus is not a string, but actually a closed list of possible values. This setup will mean that the user will see a dropdown list of the specified values.
Note |
---|
At some point we may offer a more complicated WidgetType (or version of SimpleSearch) that can look these values up at the point where the user is building their widget. |
Since we're just saving this to a JSON string eventually it wouldn't break anything to specify this as a "String" type. However that would necessitate that your users know each of the possible values for this field, as well as any differences between the filter value and how it's displayed to them usually. In fact it wouldn't break anything to put type Date, but it would then render a datePicker component and return strings of form "2021-03-19", so wouldn't likely be so useful.
However there's nothing stopping you from having an "Enum" for n set Dates in a date field, say:
Code Block |
---|
"enumValues": [ {"value": "active2020-12-25", "label": "Active"}, {"value": "closed", "label": "Closed"Christmas 2020"}, {"value": "in_negotiation2019-12-25", "label": "InChristmas negotiation2019"} ], {"comparatorsvalue": ["=="2018-12-25", "!=label"] } |
As before we have a name and label, used to help the user while they build their widget configuration in the form. Here we have specified that the agreement status has type "Enum". This is because agreementStatus is not a string, but actually a closed list of possible values. This setup will mean that the user will see a dropdown list of the specified values.
Note |
---|
At some point we may offer a more complicated WidgetType (or version of SimpleSearch) that can look these values up at the point where the user is building their widget. |
...
Code Block |
---|
"enumValues": [
{"value": "2020-12-25", "label": "Christmas 2020"},
{"value": "2019-12-25", "label": "Christmas 2019"},
{"value": "2018-12-25", "label": "Christmas 2018"},
], |
The point of stating all this is just to illustrate that the actual type of the underlying data isn't what you're specifying here, it's all relating to the configurable form and display.
The final part of this section is the comparators. For SimpleSearch these can be chosen from the list (although this is at present not enforced):
Code Block |
---|
["==", "!=", "=~", "!~", ">", ">=", "<", "<=", "isNull", "isNotNull"] |
since those are the comparators understood by KIWT style endpoints. Obviously not all of these make sense in all circumstances, but the Definition does not care, and will dutifully offer all of the comparators specified without question, even though "status <= 'abc'" is clearly nonsense.
Sort
The sort block is the simplest of the three,
Code Block |
---|
{
"name":"endDate",
"sortPath":"endDate",
"sortTypes": ["asc", "desc"]
} |
...
: "Christmas 2018"},
], |
The point of stating all this is just to illustrate that the actual type of the underlying data isn't what you're specifying here, it's all relating to the configurable form and display.
The final part of this section is the comparators. For SimpleSearch these can be chosen from the list (although this is at present not enforced):
Code Block |
---|
["==", "!=", "=~", "!~", ">", ">=", "<", "<=", "isNull", "isNotNull"] |
since those are the comparators understood by KIWT style endpoints. Obviously not all of these make sense in all circumstances, but the Definition does not care, and will dutifully offer all of the comparators specified without question, even though "status <= 'abc'" is clearly nonsense.
Sort
The sort block is the simplest of the three,
Code Block |
---|
{
"name":"endDate",
"sortPath":"endDate",
"sortTypes": ["asc", "desc"]
} |
This simply needs the path, and whether we're offering ascending/descending options. If there is only one sortType for a single sortEntry then no options are offered to the user in the form and that single value is used instead.
Configurable Properties
These are a bit different from the above, instead being for individual configurable fields with specific purposes. The way that SimpleSearch type is set up is that these can either be set to be user-configurable or not in the WidgetDefinition.
Code Block |
---|
"configurableProperties": {
"urlLink": {
"configurable": true
},
"numberOfRows": {
"configurable": false,
"defValue": 5
}
} |
Here you can see that urlLink is set to be configurable, whereas numberOfRows is non-configruable, and has a defaultValue. This will remove that entry on the dynamic widget form. The Type schema is set up to enforce defValue if configurable is set to false. In the case where a defValue is set and configurable is set to true this will manifest as a default value in the initial form creation.