Handling CaFa JSON configurations
CAFA is storage for layered custom configurations, more info.
Goerp has an option to change or create json typed values in the CAFA entry. Using xPath, template creators can define specific location from the json and modify one field. All implementation options will create field if it is not existing in json. Also, defining new type for the field will update the type in json as well, so be careful.
The jsonLookup
function accepts single json object and path as a parameters. Path supports
standard xpath queries, more info.
CAFA key definition
"my-app::Company::level_1::sample::draft-sample-v1"
, where:
::
, - separatormy-app
, - application parameterCompany
, - level parameterlevel_1
, - level_id parametersample
, - type parameterdraft-sample-v1
, - name parameter
Single input sample
Single input (model JsonConfigurationSingleInput
) form is always scoped around one
configuration entry, meaning that when submitting the form, only one config entry gets processed and
updated in the database. It is more performant in comparison with Bulk input saving.
This option should be used when page content consists of one or few configuration entries which have
to describe many fields of each entry. Also, this option is simpler to read (and write) from a code
perspective. We need to provide .Key and .Value only once in the form and then
repeat .FieldPath
, .FieldValue
and .FieldType
for every field from json
object (sample have 4 fields from one json object):
<!-- Would be simpler to store reusable parameters into variables -->
{{ $key10 := "my-app::Company::::sample::draft-sample-v1" }}
{{ $v10 := index.Data.CaFaApi.ConfigurationMap $key10 }}
<!-- Single input form -->
<form method="post">
<!-- Form control parameter allows to link this form to the specific model -->
<input type="hidden" name="postActionEntity" value="JsonConfigurationSingleInput">
<!-- fields related to config entry -->
<input type="hidden" name="CaFaApi.JsonConfigurationSingleInput.Key" value="{{ $key10 }}">
<input type="hidden" name="CaFaApi.JsonConfigurationSingleInput.Value" value="{{ $v10 }}">
<!-- multiple fields from config json value -->
{{ $path101 := "content.node-obj.child" }}
<input type="hidden" name="CaFaApi.JsonConfigurationSingleInput.FieldPath" value={{ $path101 }}>
<label for="formInputValue101">{{ $path101 }}</label>
<input type="text" id="formInputValue101"
name="CaFaApi.JsonConfigurationSingleInput.FieldValue"
value="{{ jsonLookup $v10 $path101 }}">
<input type="hidden" name="CaFaApi.JsonConfigurationSingleInput.FieldType"
value="{{ jsonType $v10 $path101 }}">
{{ $path102 := "content.node-bool" }}
<input type="hidden" name="CaFaApi.JsonConfigurationSingleInput.FieldPath"
value={{ $path102 }}>
<label for="formInputValue102">{{ $path102 }}</label>
<input type="text" id="formInputValue102"
name="CaFaApi.JsonConfigurationSingleInput.FieldValue"
value="{{ jsonLookup $v10 $path102 }}">
<input type="hidden" name="CaFaApi.JsonConfigurationSingleInput.FieldType"
value="{{ jsonType $v10 $path102 }}">
{{ $path103 := "content.node-num" }}
<input type="hidden" name="CaFaApi.JsonConfigurationSingleInput.FieldPath"
value={{ $path103 }}>
<label for="formInputValue103">{{ $path103 }}</label>
<input type="text" id="formInputValue103"
name="CaFaApi.JsonConfigurationSingleInput.FieldValue"
value="{{ jsonLookup $v10 $path103 }}">
<input type="hidden" name="CaFaApi.JsonConfigurationSingleInput.FieldType"
value="{{ jsonType $v10 $path103 }}">
{{ $path104 := "status" }}
<input type="hidden" name="CaFaApi.JsonConfigurationSingleInput.FieldPath"
value={{ $path104 }}>
<label for="formInputValue104">{{ $path104 }}</label>
<input type="text" id="formInputValue104"
name="CaFaApi.JsonConfigurationSingleInput.FieldValue"
value="{{ jsonLookup $v10 $path104 }}">
<input type="hidden" name="CaFaApi.JsonConfigurationSingleInput.FieldType"
value="{{ jsonType $v10 $path104 }}">
<input type="submit" value="Submit draft-sample-v1">
</form>
Bulk input sample
Single input (model JsonConfigurationSingleInput
) form is scoped around multiple
configuration entries, meaning that when submitting the form, then every entry would be processed
and updated in the database in the loop (uniqueness defined by $key
’s). It is less
performant in comparison with Single input saving (later will be processed asynchronously, meaning
without performance loss, but for now prefer using single input option).
This option should be used when page content consists of many entries having few field inputs for
each. Also, this option is harder to read (and write) from a code perspective. We need to
provide .Key
, .Value
, .FieldPath
, .FieldValue
and .FieldType
for every field from json objects. We have here 6 entries, please note that final field declaration
assumes that field may not exist and type can be selected.
<form method="post" id="cafa-conf-single-edit-form">
<input type="hidden" name="postActionEntity" value="JsonConfigurationBulkInput">
{{ $key := "my-app::Company::::sample::draft-sample-v1" }}
{{ $v := index .Data.CaFaApi.ConfigurationMap $key }}
<div class="flex-row">
{{ $path := "content.node-obj.child" }}
<input type="hidden" name="CaFaApi.JsonConfigurationBulkInput.Key" value="{{ $key }}">
<input type="hidden" name="CaFaApi.JsonConfigurationBulkInput.Value" value="{{ $v }}">
<input type="hidden" name="CaFaApi.JsonConfigurationBulkInput.FieldPath" value="{{ $path }}">
<p>(Config object) {{ $key }}</p>
<div class="form-field">
<label for="formInputValue1">{{ $path }}:</label>
<input type="text" id="formInputValue1"
name="CaFaApi.JsonConfigurationBulkInput.FieldValue"
value="{{ jsonLookup $v $path }}">
</div>
<div class="form-field">
<input type="text" name="CaFaApi.JsonConfigurationBulkInput.FieldType"
value="{{ jsonType $v $path }}" readonly>
</div>
</div>
<div class="flex-row">
{{ $path4 := "status" }}
<input type="hidden" name="CaFaApi.JsonConfigurationBulkInput.Key" value="{{ $key }}">
<input type="hidden" name="CaFaApi.JsonConfigurationBulkInput.Value" value="{{ $v }}">
<input type="hidden" name="CaFaApi.JsonConfigurationBulkInput.FieldPath" value="{{ $path4 }}">
<p>(Config object) {{ $key }}</p>
<div class="form-field">
<label for="formInputValue4">{{ $path4 }}:</label>
<input type="text" id="formInputValue4"
name="CaFaApi.JsonConfigurationBulkInput.FieldValue"
value="{{ jsonLookup $v $path4 }}">
</div>
<div class="form-field">
<input type="text" name="CaFaApi.JsonConfigurationBulkInput.FieldType"
value="{{ jsonType $v $path4 }}" readonly>
</div>
</div>
{{ $key2 := "my-app::Company::::sample::draft-sample-v2" }}
{{ $v2 := index .Data.CaFaApi.ConfigurationMap $key2 }}
<div class="flex-row">
{{ $path2 := "content.node-bool" }}
<input type="hidden" name="CaFaApi.JsonConfigurationBulkInput.Key" value="{{ $key2 }}">
<input type="hidden" name="CaFaApi.JsonConfigurationBulkInput.Value" value="{{ $v2 }}">
<input type="hidden" name="CaFaApi.JsonConfigurationBulkInput.FieldPath" value={{ $path2 }}>
<p>(Config object) {{ $key2 }}</p>
<div class="form-field">
<label for="formInputValue2">{{$path2}}:</label>
<input type="text" id="formInputValue2"
name="CaFaApi.JsonConfigurationBulkInput.FieldValue"
value="{{ jsonLookup $v2 $path2 }}">
</div>
<div class="form-field">
<input type="text" name="CaFaApi.JsonConfigurationBulkInput.FieldType"
value="{{ jsonType $v2 $path2 }}" readonly>
</div>
</div>
<div class="flex-row">
{{ $path3 := "status" }}
<input type="hidden" name="CaFaApi.JsonConfigurationBulkInput.Key" value="{{ $key2 }}">
<input type="hidden" name="CaFaApi.JsonConfigurationBulkInput.Value" value="{{ $v2 }}">
<input type="hidden" name="CaFaApi.JsonConfigurationBulkInput.FieldPath" value={{ $path3 }}>
<p>(Config object) {{ $key2 }}</p>
<div class="form-field">
<label for="formInputValue3">{{ $path3 }}:</label>
<input type="text" id="formInputValue3"
name="CaFaApi.JsonConfigurationBulkInput.FieldValue"
value="{{ jsonLookup $v2 $path3 }}">
</div>
<div class="form-field">
<input type="text" name="CaFaApi.JsonConfigurationBulkInput.FieldType"
value="{{ jsonType $v2 $path3 }}" readonly>
</div>
</div>
<div class="flex-row">
{{ $path5 := "content.node-num" }}
<input type="hidden" name="CaFaApi.JsonConfigurationBulkInput.Key" value="{{ $key2 }}">
<input type="hidden" name="CaFaApi.JsonConfigurationBulkInput.Value" value="{{ $v2 }}">
<input type="hidden" name="CaFaApi.JsonConfigurationBulkInput.FieldPath" value={{ $path5 }}>
<p>(Config object) {{ $key2 }}</p>
<div class="form-field">
<label for="formInputValue5">{{ $path5 }}:</label>
<input type="text" id="formInputValue5"
name="CaFaApi.JsonConfigurationBulkInput.FieldValue"
value="{{ jsonLookup $v2 $path5 }}">
</div>
<div class="form-field">
<input type="text" name="CaFaApi.JsonConfigurationBulkInput.FieldType"
value="{{ jsonType $v2 $path5 }}" readonly>
</div>
</div>
<div class="flex-row">
<!-- if path doesn't exist, then it will be added (injected) into the json -->
{{ $path6 := "not-exist" }}
<input type="hidden" name="CaFaApi.JsonConfigurationBulkInput.Key" value="{{ $key2 }}">
<input type="hidden" name="CaFaApi.JsonConfigurationBulkInput.Value" value="{{ $v2 }}">
<input type="hidden" name="CaFaApi.JsonConfigurationBulkInput.FieldPath" value={{ $path6 }}>
<p>(Config object) {{ $key2 }}</p>
<div class="form-field">
<label for="formInputValue6">{{ $path6 }}:</label>
<input type="text" id="formInputValue6"
name="CaFaApi.JsonConfigurationBulkInput.FieldValue"
value="{{ jsonLookup $v2 $path6 }}">
</div>
<div class="form-field">
<label for="formInputValue7">Type:</label>
{{ $t := jsonType $v2 $path6 }}
<!-- All available json types listed here -->
<select name="CaFaApi.JsonConfigurationBulkInput.FieldType" id="formInputValue7">
<option value="Boolean" {{ if eq $t "Boolean"}} selected {{ end }}>Boolean</option>
<option value="String" {{ if eq $t "String"}} selected {{ end }}>String</option>
<option value="Number" {{ if eq $t "Number"}} selected {{ end }}>Number</option>
<option value="JSON" {{ if eq $t "JSON"}} selected {{ end }}>JSON</option>
</select>
</div>
</div>
<input type="submit" value="SAVE">
</form>
Test data for samples
Sample code uses some fake data. To make it work properly, please populate your test account with this fake data (or replace all parameters with your data structure). Just insert this data into CAFA storage using any rest client.
[
{
"application": "my-app",
"level": "Company",
"level_id": "",
"type": "sample",
"name": "draft-sample-v1",
"value": {
"content": {
"node-bool": true,
"node-num": 112,
"node-obj": {
"child": "nested content"
}
},
"status": "draft"
},
"added": 0,
"addedby_id": 0,
"changed": 0,
"changedby_id": 0
},
{
"application": "my-app",
"level": "Company",
"level_id": "",
"type": "sample",
"name": "draft-sample-v2",
"value": {
"content": {
"node-bool": false,
"node-num": 123777,
"node-obj": {
"child": "nested content two"
}
},
"status": "draft"
},
"added": 0,
"addedby_id": 0,
"changed": 0,
"changedby_id": 0
}
]