ut-model
is a utility library for creating common functionality like:
- create, edit, delete, browse, report and other generic pages
- mocks for back end API
- mocks for back end orchestrator
- backend validations
- automated test steps
It is intended to define a standard design for UI pages, back end APIs and other structures, which enable fast creation of default functionality, which can be customized only if needed. At some point it will be enough to call it with minimum parameters and get a lot of default functionality:
import model from 'ut-model';
export default model.component([
({joi}) => {
subject: 'model',
object: 'tree'
}
]);
ut-model
exposes several functions, which expect as their first parameter
an array of model definition functions. These functions receive as parameter
an object with the properties:
joi
- the joi validation api
Then these functions return an object with the following properties:
-
subject
- name of the module to create pages/mocks for -
object
- name of the object to create pages/mocks for -
objectTitle
- title of the object to use in the pages, the default is capitalizedobject
-
keyField
- name of the key field in thesubject.object
table, the default is${object}Id
-
nameField
- the name of the field, which represents the object name or a similar concept. This field is rendered as a link in thebrowse
page. The default is${object}Name
-
tenantField
- the name of the field, which represents the tenant (if any). The default isbusinessUnitId
. -
methods
- an object with the following properties:fetch
- the name of the method used for fetching a list of objects, the default is${subject}.${object}.fetch
,add
- the name of the method used for creating a new object, the default is${subject}.${object}.add
,delete
- the name of the method used for deleting an object, the default is${subject}.${object}.delete
,get
- the name of the method used for retrieving a single object, the default is${subject}.${object}.get
,edit
- the name of the method used for modifying a single object, the default is${subject}.${object}.edit
-
schema
- an object, which describes the API parameters in the form of JSON schema with some extra properties. The important properties are:title
- the text to show for the field in a table header, form label, etc.filter
- a boolean, that activates the filtering by this field in thebrowse
page. The default isfalse
.sort
- a boolean, that activates the sorting by this field in thebrowse
page. The default isfalse
.validation
- joi validation object, used to validate the field in thesubject.object.open
,subject.object.new
pages.widget
- configuration for the widget used in forms or tables
-
cards
- an object, which describes named groups of properties. Cards are an abstraction for some visual components which involve a list of properties, such as:- columns in a table
- fields in a form
- parameters in a report
The
cards
object maps card names to objects, having the following properties: label
- Optional property, which defines the text to be shown, for specific visual component caseswidgets
- Array of strings, representing the property names. The array may include other arrays, to describe specific layout designs, such as: groups of columns, vertical layouts nested in horizontal ones and so on. Refer to the storybook for examples.className
- Controls the layout properties of the visual component. For example in a form, the default layout is to display cards in two columns. To change it to three columns for big screens ( > 1200px as per primeflex), pass'lg:col-6 xl:col-4'
forclassName
For more information check the component Editor in
ut-prime
. -
layouts
- an object, which describes named groups of cards. Each property of this object describes one layout as an array of card names or as a tree structure. Layouts are an abstraction for configuring the layout of predefined page designs like:- page for browsing a list of objects (
subject.object.browse
) - pages for creation or editing a single object (
subject.object.new
andsubject.object.open
) - pages for reports (
subject.object.report
)
When the layout is described as a tree structure, it usually describes some tabbed layout with one or more levels of nesting. The tree structure follows the PrimeReact MenuModel API, with one additional property
cards
, which is the array of card names. Check the storybook for examples. - page for browsing a list of objects (
-
browser
- an object, which configures thebrowse
page. It accepts the following properties:-
title
- Title of the browse page. The default is${objectTitle} list
, -
navigator
- A boolean, which indicates if a navigator for the tenants will be shown. The default is false. -
navigatorFetchMethod
- The name of the method used to fetch data for the navigator component. The default iscustomerOrganizationGraphFetch
. -
fetch
- an optional function, that adapts the parameters of the standard fetch function, to a back end, that does not follow the standard parameters. By default the fetch method parameter is an object with the following properties:[object]
- this represents the filter to be applied. The name of the property is based on the name of the passedobject
to the function.orderBy
- defines the sorting of the result, through an array of{field, dir}
objects, corresponding to thecore.orderByTT
table type. The property dir is eitherASC
orDESC
.paging
- defines the paging of the result as an object{pageNumber, pageSize}
, corresponding to thecore.pagingTT
table type.
If the back end expects different properties, this function is called and must return the expected parameters, based on the standard ones. For example, if the back end expects the filter in a property named
filterBy
, instead of a property namedsomeObject
, the following can be passed:require('ut-model').component(() => ({ browser: { fetch: ({ someObject: filterBy, orderBy, paging }) => ({ filterBy, orderBy, paging }) } }))
-
resultSet
- The name of the property, in which the fetched data is returned. By default the fetch method is expected to return an object with the shape{ [resultSet], pagination: { recordsTotal } }
. The propertyresultSet
contains an array of objects, representing the fetched data. By defaultresultSet
equals the object name passed in theobject
parameter. -
create
- By default thebrowse
page includes one button for creation of new objects. If the creation of new objects requires more than one button, this parameter enables defining of the buttons and the parameters to pass to the object creation page. Thecreate
parameter accepts an array of objects with the following properties:type
- this is passed as parameter to the object creation pagepermission
- the permission to check...params
- the rest of the properties are passed to the actions parameter of the<Explorer>
component.
-
delete
- By default thebrowse
page includes a button for deletion of objects by calling the${subject}.${object}.delete
back end method. This parameter allows to reshape the parameters expected by the back end. Thedelete
function receives an array of objects selected for deletion in thebrowse
page and must return the expected parameter, as per the back end API. By default it is expected that the back end accepts a parameter with property named after the key field, which is of type core.arrayNumberList. This corresponds to the followingdelete
function, which is the default:require('ut-model').component(() => ({ browser: { delete: instances => ({ [keyField]: instances.map(instance => ({ value: instance[keyField] })) }) } }))
-
-
editor
- an object, which configures the editor in thesubject.object.open
andsubject.object.add
pages. It accepts the following properties:typeField
- Sometimes object edition page has different layout, depending on the type of object. In such cases, this parameter defines the name of the property, that defines the object type. Based on object type, for examplefoo
, the layout is picked from thelayouts
parameter, from a property namededitFoo
, i.e. the capitalized type prefixed withedit
.
The module exposes the following functions:
-
component
- a function, which returns and array of functions, which define UI pages and components, such as:editor
- an editor componentsubject.object.browse
- a page for browsing the objectssubject.object.new
- a page for creation of new objectssubject.object.open
- a page for editing (or viewing) existing objects
The
component()
function is intended to be used in thebrowser
layer:import model from 'ut-model' export default () => function utUser() { return { config: require('./config'), browser: () => [ ...model.component(models) ] }; };
-
backendMock
- a function, which returns mocks for backend APIs:subject.object.fetch
subject.object.add
subject.object.delete
subject.object.get
subject.object.edit
These mock functions allow easier creation of Storybook stories and UI unit tests. The
backendMock
function accepts as second parameter a callback function, which must return an object which maps mock names (in the formsubjectObject
) to objects with the following properties:objects
- an optional array of objects to be used as mock data.fetch
- an optional function, which reshapes the structure of the parameters and result of thefetch
method of the actual back end APIs to the standard structure expected by the mock. This is only needed in cases the back end differs from the standard fetch API shape, which is:{[object], orderBy, paging}
- for the parameters{[object], pagination}
- for the result The passedfetch
function receives a single parameterfilter
, which is the default mock function for filtering, i.e. a function expecting the default parameter and result structures above. The passedfetch
function could reshape the non-standard back end parameters, before callingfilter
and then reshape the result to the shape of the non-standard back end result. For example, if the real back end expects a property namedfilterBy
, instead ofsomeObject
the following can be passed tomock
:
require('ut-model')(() => ({ subject: 'subject', object: 'object', ...rest }), () => ({ subjectObject: { fetch: filter => ({ filterBy, orderBy, paging }) => filter({ someObject: filterBy, orderBy, paging }) } }))
Note: usually the
fetch
function passed tomock
does the opposite of thefetch
function passed inbrowser.fetch
and when one of these is passed, the other one needs to be passed too.