MODCFIELDS-39 - Custom Field backend demo
A high-level overview of Custom Fields
What is the custom field?
Custom fields are a set of additional fields that module can define for it is own purposes. The custom fields integration will be covered below in this presentation.
Request dispatching
Usually, OKAPI
supports one-to-one connection between module and interface, but for special cases there is a way for multiple modules to implement the same interface by using a special header 'X-Okapi-Module-Id
'. To enable OKAPI
to dispatch the request to any number of modules with the same interface just use "interfaceType
": "multiple
" in the "Provides
" section of the ModuleDescriptor.json
file.
It is assumed that UI will send 'X-Okapi-Module-Id
' header along with request to define target module. To find out what modules provide an interface user is looking for, use next request
_/proxy/tenants/{tenant}/modules?provide={interface}
where {tenant} -
the name of the tenant and {interface}
- the interface name, in our case `custom-fields
`
In the response user receives a list with module ids
And the request to 'mod-users'
can be as follows.
where the 'X-Okapi-Module-Id
' header holds the version of the 'mod-users
' id.
To get more information about Multiple interfaces feature follow the link to OKAPI
documentation.
The following example shows the part of the ModuleDescriptor.json
file with the custom-fields interface section. The full version of ModuleDescriptor.json
file for 'mod-users
' module can be found via the link.
... { "id": "custom-fields", "version": "1.2", "interfaceType" : "multiple", "handlers": [ { "methods": ["GET"], "pathPattern": "/custom-fields", "permissionsRequired": ["user-settings.custom-fields.collection.get"] }, { "methods": ["POST"], "pathPattern": "/custom-fields", "permissionsRequired": ["user-settings.custom-fields.item.post"] }, { "methods": ["GET"], "pathPattern": "/custom-fields/{id}", "permissionsRequired": ["user-settings.custom-fields.item.get"] }, { "methods": ["PUT"], "pathPattern": "/custom-fields/{id}", "permissionsRequired": ["user-settings.custom-fields.item.put"] }, { "methods": ["PUT"], "pathPattern": "/custom-fields", "permissionsRequired": ["user-settings.custom-fields.collection.put"] }, { "methods": ["DELETE"], "pathPattern": "/custom-fields/{id}", "permissionsRequired": ["user-settings.custom-fields.item.delete"] }, { "methods": ["GET"], "pathPattern": "/custom-fields/{id}/stats", "permissionsRequired": ["user-settings.custom-fields.item.stats.get"] } ] } ...
Schema overview
The initial schema for custom-fields:
The current version of the custom-fields supports following types:
- RADIO_BUTTON
- SINGLE_CHECKBOX
- SINGLE_SELECT_DROPDOWN
- MULTI_SELECT_DROPDOWN
TEXTBOX_SHORT
TEXTBOX_LONG
Each type have allowed set of the fields that can be defined.
The full schema of customField.json
file can be found via the link.
Custom Fields validation
The validation of the custom fields is divided onto two parts: validation of the definition and validation of the values assigned to the custom field.
Definition validation
Each custom-field type has predefined set of allowed properties.
We also divided definition validation into two groups : common attributes validation and type-specific validation
The Common attributes validation includes following rules:
- name - required field, should not have length more than 65 characters
- helpText - required field, should not have length more than 100 characters
- entityType - required field
- isRepeatable - not required, if not present, will set to
false
- visible - not required, if not present, will set to
true
- required - not required, if not present, will set to
false
Also, custom-fields schema does not allow to have additional properties that are not allowed for custom field type, for instance, the following example will not pass definition validation
{ "visible": true, "required": true, "type": "SINGLE_SELECT_DROPDOWN", "name": "my single select field", "order": 5, "entityType": "user", "helpText": "Select your department", "selectField": { "multiSelect": false, "options": { "values": [ { "id": "opt_1", "value": "Engineering" }, { "value": "Architecture" } ], "sortingOrder": "ASC" } }, "checkboxField": {} }
The Type-specific validation includes following rules:
RADIO_BUTTON
- options - required field, max size is 5 values
- options - max option name length no more than 100 characters
- multiSelect - required field, should be
false
for RADIO_BUTTON type - defaults - not required, if present, should not have more than 1 value
SINGLE_CHECKBOX
- default - not required, if not present, will set to
false
SINGLE_SELECT_DROPDOWN
- options - required field, max size is 200 values
- options - max option name length no more than 100 characters
- multiSelect - required field, should be
false
for SINGLE_SELECT_DROPDOWN type - defaults - not required, if present, should not have more than 1 value
MULTI_SELECT_DROPDOWN
- options - required field, max size is 200 values
- options - max option name length no more than 100 characters
- multiSelect - required field, should be
true
for MULTI_SELECT_DROPDOWN type - defaults - not required, does not allow duplicates, if present, may have 0 .. n values, where n - values from options
Values validation
The proposed linkage between custom-field and assigned value is following:
"custom-fields": { "refId_1": ["Greek", "Latin"], "refId_2": "Yes" }
where the 'refId_1
' - the reference if of the custom field, created during the POST
For the text custom-field types backed has a restriction on a value length:
TEXTBOX_SHORT
- value - max length no longer than 150 characters
TEXTBOX_LONG
- value - max length no longer than 1500 characters
The other custom-field types validate if the custom field is allowed to have a specified value.
Below, you can find and example of assigning custom-field values to a user record
Custom fields Integration to a module
To enable custom-fields for particular module, please, follow the next steps
- add custom-fields dependency to a target module pom file
<dependency> <groupId>org.folio</groupId> <artifactId>mod-custom-fields</artifactId> <version>1.3.0</version> </dependency>
- Include custom-fields interfaces to target module
ModuleDescriptor.json
file.
{ "id": "custom-fields", "version": "1.2", "interfaceType" : "multiple", "handlers": [ { "methods": ["GET"], "pathPattern": "/custom-fields", "permissionsRequired": ["user-settings.custom-fields.collection.get"] }, { "methods": ["POST"], "pathPattern": "/custom-fields", "permissionsRequired": ["user-settings.custom-fields.item.post"] }, { "methods": ["GET"], "pathPattern": "/custom-fields/{id}", "permissionsRequired": ["user-settings.custom-fields.item.get"] }, { "methods": ["PUT"], "pathPattern": "/custom-fields/{id}", "permissionsRequired": ["user-settings.custom-fields.item.put"] }, { "methods": ["PUT"], "pathPattern": "/custom-fields", "permissionsRequired": ["user-settings.custom-fields.collection.put"] }, { "methods": ["DELETE"], "pathPattern": "/custom-fields/{id}", "permissionsRequired": ["user-settings.custom-fields.item.delete"] }, { "methods": ["GET"], "pathPattern": "/custom-fields/{id}/stats", "permissionsRequired": ["user-settings.custom-fields.item.stats.get"] } ] }
The permission name inside of the `permissionsRequired
` section can be modified to represent the module purpose.
The example of ModuleDescriptor.json
file for 'mod-users'
is available here
Add custom-fields script to create a table for storing module-specific custom fields and additional triggers.
"scripts" : [ { "run": "after", "snippetPath": "create_custom_fields_table.sql", "fromModuleVersion": "1.0" } ]
Use cases and demo
The API Postman collection was created to allow potential users to play around with custom-fields feature.
The provided API collection interacts with 'mod-users
'
Please note that environment variables file attached below refers to a local setup and may be changed to cooperate with any other environment such as stable env.
- Postman API collection - custom fields.postman_collection.json
- Postman environment variables - custom-fields.localhost.postman_environment.json
Concerns q/a
Question | Answer |
---|---|
how will the APIs handle custom fields (including mod-user-import)? | In general 'mod-user-import ' fetches the data(address types, custom fields definition and patron groups) from ' mod-users' , then validates it and if everything is ok returns successful response, if not, returns list of errors. |
how the custom fields can be updated programmatically? | you may use prepared collection of API request attached above |
why not just put all the custom field info into a single custom_fields table in its own schema rather than having the same table duplicated in multiple modules? | the custom-fields was not designed to be a module, it is rather a library and intended to be closer to the module data |
My main concern is that validation of custom fields not slow down bulk insert/update. It would be good if the people developing the custom field support have a test that does a load of about 50,000 users and see what kind off affect it has on performance. However, the problem at the moment is that there are no bulk APIs for insert/update for users. Using the single update HTTP methods may mask performance problems. | That is a nice idea, currently, we do not have such kind of test, at least our team did not create it, but in the future it would be really nice to have a JIRA issue for that purpose and make it into account in next planning iteration. |
Why does it use Okapi's multiple interfaces feature that requires clients (front-end, other scripts) to pass the complete module id in the X-Okapi-Module-Id header? Front-end and other scripts should work with only some interface version, not a complete module id. | FCFIELDS-4 Spike: Multiple interface, or module specific interfaces? |
Related links