Files
elixforms-schema-bulk-updater/save-metadata-schema-automatic.md
T
2026-08-24 09:45:51 +02:00

779 lines
32 KiB
Markdown

# Automatic bulk update of schema metadata
## Schema
### Schema metadata
ID: 616
Name: WFTemplate
1. Call to `https://console-unipr.elixforms.it/AJSRV/metadata/list?AJL=it&ID=616&ACL=true`
2. Response is a JSON:
```json
{
"objectList": [
{
"id": 616,
"title": "WFTemplate",
"dataId": 0,
"tag": "",
"objectId": 0,
"sections": [
{
"title": "Template",
"key": "SEC_0006",
"orderKey": "ORD001",
"fields": [
{
"type": "STRING",
"key": "COL0014",
"title": "Titolo template",
"searchable": false,
"valueList": [
{
"key": "",
"value": ""
}
],
"itemList": null,
"toManyItems": false,
"attributes": [],
"orderKey": "ORD001",
"defaultValue": null,
"required": false
},
{
"type": "HTML",
"key": "COL0009",
"title": "Template",
"searchable": false,
"valueList": [
{
"key": "",
"value": ""
}
],
"itemList": null,
"toManyItems": false,
"attributes": [],
"orderKey": "ORD002",
"defaultValue": null,
"required": false
}
],
"attributes": []
},
{
"title": "Metadata",
"key": "SEC_0007",
"orderKey": "ORD002",
"fields": [
{
"type": "STRING",
"key": "COL0010",
"title": "Tag azione",
"searchable": false,
"valueList": [
{
"key": "",
"value": ""
}
],
"itemList": null,
"toManyItems": false,
"attributes": [],
"orderKey": "ORD001",
"defaultValue": null,
"required": false
},
{
"type": "STRING",
"key": "COL0011",
"title": "Tag procedimento",
"searchable": false,
"valueList": [
{
"key": "",
"value": ""
}
],
"itemList": null,
"toManyItems": false,
"attributes": [],
"orderKey": "ORD002",
"defaultValue": null,
"required": false
},
{
"type": "STRING",
"key": "COL0012",
"title": "Tag modulo",
"searchable": false,
"valueList": [
{
"key": "",
"value": ""
}
],
"itemList": null,
"toManyItems": false,
"attributes": [],
"orderKey": "ORD003",
"defaultValue": null,
"required": false
}
],
"attributes": []
}
],
"categories": [],
"aclTable": [
{
"objectType": "SCHEMA",
"objectId": 616,
"permissionType": "VIEW",
"groupId": 0,
"allow": true
},
{
"objectType": "SCHEMA",
"objectId": 616,
"permissionType": "INSERT",
"groupId": 0,
"allow": true
},
{
"objectType": "SCHEMA",
"objectId": 616,
"permissionType": "UPDATE",
"groupId": 0,
"allow": true
},
{
"objectType": "SCHEMA",
"objectId": 616,
"permissionType": "DELETE",
"groupId": 0,
"allow": true
}
],
"master": false,
"attributes": []
}
]
}
```
3. `objectList.sections` is an array containing the sections defined in the schema
4. in each element of `objectList.sections` there is the `fields` array property, containing the fields definitions
5. Each one of the fields (use `key` and `title` properties) can be used as a filter (the `searchable: false` properties are just plain ignored...)
--------------------------------------------------------------------------------
### Schema structure
(taken from schema attributes)
```yaml
- sectionId: SEC_0006
sectionOrderKey: ORD001
sectionColumns:
- ID: COL0014
Name: Titolo template
orderKey: ORD001
- ID: COL0009
Name: Template
orderKey: ORD002
- sectionId: SEC_0007
sectionOrderKey: ORD002
sectionColumns:
- ID: COL0010
Name: Tag azione
orderKey: ORD001
- ID: COL0011
Name: Tag procedimento
orderKey: ORD002
- ID: COL0012
Name: Tag modulo
orderKey: ORD003
```
--------------------------------------------------------------------------------
### Schema search
Results in table format!
1. ID object (17460)
2. ID object (73743)
3. Titolo template (depDUSIC_Delibera)
4. Tag azione (liqc__op_a_generaDelibera__depDUSIC)
5. Tag procedimento (procedimento_liqc)
6. Template (...omitted...)
7. Tag modulo (RequestForm_RIPARTIZIONE_CCT_DOCENTE)
Column order in results is based ON WHAT!? Mmmh... orderKey ASC, Name ASC? Nope...
First metadata ordering is based on column's OrderKey.
Second metadata ordering? Not section's orderKey (COL0011 and COL0009 should be inverted), so... column id DESCENDING? If so, WHY!?
Anyway, column ordering in results table seems to be given by the `search` response content (see below).
| Order | Name | Column | OrderKey | sectionOrderKey |
| ----- | ---------------- | ------- | -------- | --------------- |
| 1 | Titolo template | COL0014 | ORD001 | ORD001 |
| 2 | Tag azione | COL0010 | ORD001 | ORD002 |
| 3 | Tag procedimento | COL0011 | ORD002 | ORD002 |
| 4 | Template | COL0009 | ORD002 | ORD001 |
| 5 | Tag modulo | COL0012 | ORD003 | ORD002 |
### Schema save
```json
{
"errors": {
"objectList": []
},
"result": {
"objectList": [
{
"id": 616,
"title": "WFTemplate",
"dataId": 81825,
"tag": "",
"objectId": 19519,
"sections": [
{
"title": "Template",
"key": "SEC_0006",
"orderKey": "ORD001",
"fields": [
{
"type": "STRING",
"key": "COL0014",
"title": "Titolo template",
"searchable": false,
"valueList": [
{
"key": "",
"value": "depCLA_Delibera"
}
],
"itemList": null,
"toManyItems": false,
"attributes": [],
"orderKey": "ORD001",
"defaultValue": null,
"required": false
},
{
"type": "HTML",
"key": "COL0009",
"title": "Template",
"searchable": false,
"valueList": [
{
"key": "",
"value": "<!DOCTYPE html>\n<html>\n[EFTL]...[/EFTL]\n</html>"
}
],
"itemList": null,
"toManyItems": false,
"attributes": [],
"orderKey": "ORD002",
"defaultValue": null,
"required": false
}
],
"attributes": []
},
{
"title": "Metadata",
"key": "SEC_0007",
"orderKey": "ORD002",
"fields": [
{
"type": "STRING",
"key": "COL0010",
"title": "Tag azione",
"searchable": false,
"valueList": [
{
"key": "",
"value": "liqc__op_a_generaDelibera__depCLA"
}
],
"itemList": null,
"toManyItems": false,
"attributes": [],
"orderKey": "ORD001",
"defaultValue": null,
"required": false
},
{
"type": "STRING",
"key": "COL0011",
"title": "Tag procedimento",
"searchable": false,
"valueList": [
{
"key": "",
"value": "procedimento_liqc"
}
],
"itemList": null,
"toManyItems": false,
"attributes": [],
"orderKey": "ORD002",
"defaultValue": null,
"required": false
},
{
"type": "STRING",
"key": "COL0012",
"title": "Tag modulo",
"searchable": false,
"valueList": [
{
"key": "",
"value": "RequestForm_RIPARTIZIONE_CCT_DOCENTE"
}
],
"itemList": null,
"toManyItems": false,
"attributes": [],
"orderKey": "ORD003",
"defaultValue": null,
"required": false
}
],
"attributes": []
}
],
"categories": [],
"aclTable": [],
"master": false,
"attributes": []
}
]
}
}
```
--------------------------------------------------------------------------------
## HTTP sequences
### Search by schema
1. Select schema from dropdown
2. Select tab "Cerca", optionally insert filters
3. Click on "Cerca" and obtain results
See file `search-schema.har`
Sequence is:
1. Call `https://console-unipr.elixforms.it/AJSRV/metadata/search?SEARCH_IN%5B%5D=616&PAGE=1&AJL=it&PAGESIZE=80&SECURE=true`
1. With filters: `https://console-unipr.elixforms.it/AJSRV/metadata/search?SEARCH_IN%5B%5D=616&S_616_NEW_COL0014=antani&S_616_NEW_COL0010=tapioca&PAGE=1&AJL=it&PAGESIZE=80&SECURE=true`
2. `search` route returns a JSON
```json
{
"totalRecords": 56,
"pageSize": 80,
"numPages": 1,
"currentPage": 1,
"columns": [
{
"key": "ID_OBJECT",
"value": "ID_OBJECT"
},
{
"key": "S_616_ID_GENERICSCHEMA",
"value": "S_616_ID_GENERICSCHEMA"
},
{
"key": "S_616_COL0014",
"value": "Titolo template"
},
{
"key": "S_616_COL0010",
"value": "Tag azione"
},
{
"key": "S_616_COL0011",
"value": "Tag procedimento"
},
{
"key": "S_616_COL0009",
"value": "Template"
},
{
"key": "S_616_COL0012",
"value": "Tag modulo"
}
],
"data": [
[
"17460",
"73743",
"depDUSIC_Delibera",
"liqc__op_a_generaDelibera__depDUSIC",
"procedimento_liqc",
"<!DOCTYPE html>\n<html>\n[EFTL]\n.........[/EFTL]\n</html>",
"RequestForm_RIPARTIZIONE_CCT_DOCENTE"
],
[
"17463",
"73782",
"depDUSIC_Provvedimento",
"liqc__op_a_inviaPerFirmaRemotaProvvedimento__depDUSIC",
"procedimento_liqc",
"<!DOCTYPE html>\n<html>\n[EFTL]\n.........[/EFTL]\n</html>",
"RequestForm_RIPARTIZIONE_CCT_DOCENTE"
],
[
"19494",
"81800",
"depGSPI_Delibera",
"liqc__op_a_generaDelibera__depGSPI",
"procedimento_liqc",
"<!DOCTYPE html>\n<html>\n[EFTL]\n.........[/EFTL]\n</html>",
"RequestForm_RIPARTIZIONE_CCT_DOCENTE"
],
[
"19495",
"81801",
"depGSPI_Provvedimento",
"liqc__op_a_inviaPerFirmaRemotaProvvedimento__depGSPI",
"procedimento_liqc",
"<!DOCTYPE html>\n<html>\n[EFTL]\n.........[/EFTL]\n</html>",
"RequestForm_RIPARTIZIONE_CCT_DOCENTE"
],
/* other elements */
[
"35397",
"168258",
"Titolo template (WFTemplate)",
"azione_ACQUISTI_generaPdfDetermina",
"edilizia_rda_manutenzione_wf",
"Template (WFTemplate)",
"RequestForm_EDILIZIA_RDA_MANUTENZIONE"
]
],
"extraData": {}
}
```
3. FOR EACH of the results, `objectTitle` route is called TWICE! These are NOT really needed in the process:
- `https://console-unipr.elixforms.it/AJSRV/metadata/objectTitle?ATTR_NAME=master_field&ATTR_VALUE=true&TYPE=SCHEMADATA&OBJECT_ID=17460`
- `https://console-unipr.elixforms.it/AJSRV/metadata/objectTitle?ATTR_NAME=master_field&ATTR_VALUE=true&TYPE=GENERICSCHEMA&OBJECT_ID=73743`
4. Each one return a JSON, do not know what it's for:
```json
{"objectList":[]}
```
### HTTP requests
**Schema data**:
https://console-unipr.elixforms.it/AJSRV/metadata/list?AJL=it&ID=616&ACL=true
**Search**:
```http
GET https://console-unipr.elixforms.it/AJSRV/metadata/search?SEARCH_IN%5B%5D=616&PAGE=1&AJL=it&PAGESIZE=80&SECURE=true HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, br, zstd
Cookie: ISIPSESSION=047b770fac5627c03c94030ca0c3b45c; AJSRV=BTN_USER_1787207482458_8771527838545232265; ISIPSESSION_TRACK=99b2488e73af7c61f6e5affceeb32502; JSESSIONID=e3c44b958c4f282660a5fc8ed994
X-Requested-With: XMLHttpRequest
```
## User Workflow
Routes:
- **list**: GET `https://console-unipr.elixforms.it/AJSRV/metadata/list?AJL=it&ID=616&ACL=true` (ID is dynamic)
- **search**: GET `https://console-unipr.elixforms.it/AJSRV/metadata/search?SEARCH_IN%5B%5D=616&S_616_NEW_COL0014=antani&S_616_NEW_COL0010=tapioca&PAGE=1&AJL=it&PAGESIZE=80&SECURE=true` (some parameters are dynamic)
- **save**: POST `https://console-unipr.elixforms.it/AJSRV/metadata/save`
Sequence:
1. Obtain the cookie value by logging manually to elixForms
2. Ask the user to input the cookie value and use that as `Cookie` header in the following requests
3. Ask the user for the following values:
1. Schema ID (e.g. 616)
4. Use the `list` route to obtain schema data (columns that can be used as a filter, see [Schema data](#schema-data))
5. Present the user with the schema details, such as
1. ID: `objectList.id`
2. Title: `objectList.title`
3. Sections: one for each `objectList.sections`, ordered by orderKey ASC, showing:
1. key: `objectList.sections[*].key`
2. title: `objectList.sections[*].title`
3. Fields: for each element in fields, ordered by orderKey ASC, showing:
1. key: `objectList.sections[*].fields[*].key`
2. title: `objectList.sections[*].fields[*].title`
3. type: `objectList.sections[*].fields[*].type`
6. Ask confirmation that the schema is the right one, then, for each one of the fields (consider them globally, independent of section), ask the user for a value:
1. If no value, or empty string, is given, the field will not be used as a filter
2. If a non-empty value is given, that value will be used as a filter for the subsequent search; the query string values will be:
- `SEARCH_IN[]`: the schema ID
- for each of the given values, build a query param such as `S_${schemaId}_NEW_${fieldKey}` and assign to it the value given by the user
- `PAGE`: 1
- `AJL`: it
- `PAGESIZE`: 80
- `SECURE`: true
7. Call the `search` route with:
- the query params defined above
- the `Cookie` header given by the user
- the `x-requested-with` header with value `XMLHttpRequest`
8. The search route will give a JSON like the following:
```json
{
"totalRecords": 56,
"pageSize": 80,
"numPages": 1,
"currentPage": 1,
"columns": [
{
"key": "ID_OBJECT",
"value": "ID_OBJECT"
},
{
"key": "S_616_ID_GENERICSCHEMA",
"value": "S_616_ID_GENERICSCHEMA"
},
{
"key": "S_616_COL0014",
"value": "Titolo template"
},
{
"key": "S_616_COL0010",
"value": "Tag azione"
},
{
"key": "S_616_COL0011",
"value": "Tag procedimento"
},
{
"key": "S_616_COL0009",
"value": "Template"
},
{
"key": "S_616_COL0012",
"value": "Tag modulo"
}
],
"data": [
[
"17460",
"73743",
"depDUSIC_Delibera",
"liqc__op_a_generaDelibera__depDUSIC",
"procedimento_liqc",
"<!DOCTYPE html>\n<html>\n[EFTL]\n.........[/EFTL]\n</html>",
"RequestForm_RIPARTIZIONE_CCT_DOCENTE"
],
[
"17463",
"73782",
"depDUSIC_Provvedimento",
"liqc__op_a_inviaPerFirmaRemotaProvvedimento__depDUSIC",
"procedimento_liqc",
"<!DOCTYPE html>\n<html>\n[EFTL]\n.........[/EFTL]\n</html>",
"RequestForm_RIPARTIZIONE_CCT_DOCENTE"
],
[
"19494",
"81800",
"depGSPI_Delibera",
"liqc__op_a_generaDelibera__depGSPI",
"procedimento_liqc",
"<!DOCTYPE html>\n<html>\n[EFTL]\n.........[/EFTL]\n</html>",
"RequestForm_RIPARTIZIONE_CCT_DOCENTE"
],
[
"19495",
"81801",
"depGSPI_Provvedimento",
"liqc__op_a_inviaPerFirmaRemotaProvvedimento__depGSPI",
"procedimento_liqc",
"<!DOCTYPE html>\n<html>\n[EFTL]\n.........[/EFTL]\n</html>",
"RequestForm_RIPARTIZIONE_CCT_DOCENTE"
],
/* other elements */
[
"35397",
"168258",
"Titolo template (WFTemplate)",
"azione_ACQUISTI_generaPdfDetermina",
"edilizia_rda_manutenzione_wf",
"Template (WFTemplate)",
"RequestForm_EDILIZIA_RDA_MANUTENZIONE"
]
],
"extraData": {}
}
```
1. The `columns` array property contains key-value pairs that define the order in which the schema columns will be shown, based on the order in the array (use order-maintaining structures)
2. Note that:
- pair with `key` having `ID_OBJECT` doesn't match any of the schema fields
- there is a pair with `key` composed like `S_${schemaId}_ID_GENERICSCHEMA`, this one also doesn't match any of the schema field
- other pairs should have their `key` composed like `S_${schemaId}_${columnId}`
- the `value` property is a string that will be used as column header
3. The `data` array contains one element for each of the results row
4. Each one of the `data` elements are string arrays: their content is ordered as the columns described above, and each value is corresponding to the columns
9. Show the obtained results (store them in memory, they will be used later) and ask confirmation to proceed (exit if no cofirmation is given)
10. Based on the response of the `list` route, ask the user which one field must be updated with a new value; empty value means exit
11. Ask for the user for the new value to be used for that field; empty value means empty string, don't exit
12. Ask for a final confirmation that the rows shown in step 9 will be irremediably replaced!
13. Make a backup of the `search` route response anyway, appending date in yyyyMMddHHmmss format
14. Now, for each results obtained in step 9:
1. Build a request with multipart/form-data with a random boundary string and add the relative `Content-Type` header
2. Add to the form a name-value JSON element, where `name` is composed as `S_${schemaId}_${ID_OBJECT}_${columnId}` and `value` is the value given by the user
3. Compute and add a `Content-Length` header
4. Add a `Cookie` header given by the user
5. Add a `x-requested-with` header with value `XMLHttpRequest`
6. Call the `save` route `POST` request `https://console-unipr.elixforms.it/AJSRV/metadata/save`
7. The response will be a JSON such as:
```json
{
"errors": {
"objectList": []
},
"result": {
"objectList": [
{
"id": 616,
"title": "WFTemplate",
"dataId": 81825,
"tag": "",
"objectId": 19519,
"sections": [
{
"title": "Template",
"key": "SEC_0006",
"orderKey": "ORD001",
"fields": [
{
"type": "STRING",
"key": "COL0014",
"title": "Titolo template",
"searchable": false,
"valueList": [
{
"key": "",
"value": "depCLA_Delibera"
}
],
"itemList": null,
"toManyItems": false,
"attributes": [],
"orderKey": "ORD001",
"defaultValue": null,
"required": false
},
{
"type": "HTML",
"key": "COL0009",
"title": "Template",
"searchable": false,
"valueList": [
{
"key": "",
"value": "<!DOCTYPE html>\n<html>\n[EFTL]...[/EFTL]\n</html>"
}
],
"itemList": null,
"toManyItems": false,
"attributes": [],
"orderKey": "ORD002",
"defaultValue": null,
"required": false
}
],
"attributes": []
},
{
"title": "Metadata",
"key": "SEC_0007",
"orderKey": "ORD002",
"fields": [
{
"type": "STRING",
"key": "COL0010",
"title": "Tag azione",
"searchable": false,
"valueList": [
{
"key": "",
"value": "liqc__op_a_generaDelibera__depCLA"
}
],
"itemList": null,
"toManyItems": false,
"attributes": [],
"orderKey": "ORD001",
"defaultValue": null,
"required": false
},
{
"type": "STRING",
"key": "COL0011",
"title": "Tag procedimento",
"searchable": false,
"valueList": [
{
"key": "",
"value": "procedimento_liqc"
}
],
"itemList": null,
"toManyItems": false,
"attributes": [],
"orderKey": "ORD002",
"defaultValue": null,
"required": false
},
{
"type": "STRING",
"key": "COL0012",
"title": "Tag modulo",
"searchable": false,
"valueList": [
{
"key": "",
"value": "RequestForm_RIPARTIZIONE_CCT_DOCENTE"
}
],
"itemList": null,
"toManyItems": false,
"attributes": [],
"orderKey": "ORD003",
"defaultValue": null,
"required": false
}
],
"attributes": []
}
],
"categories": [],
"aclTable": [],
"master": false,
"attributes": []
}
]
}
}
```
8. Show the result of the operation and, possibly the new value of the chosen field, taken from this JSON
15. Optionally, askl the user if they want to re-run the `search` route to show updates
16. Exit