Zum Inhalt

Verwendung der Forms API#

Einführung#

Das Forms-Addon besteht aus einem i-doit-kompatiblen Add-on und einer Backend-Applikation. Das Backend ist für die Datenhaltung verantwortlich und wird über eine REST-API bedient.

Du kannst i-doit als Proxy verwenden, um mit dem Forms-Backend zu kommunizieren. Das setzt jedoch eine valide Benutzersession in i-doit voraus. Alternativ sprichst du die Forms-Backend-API direkt an. Dieses Dokument geht von einer direkten Kommunikation aus.

Keine serverseitige Validierung

Das Backend enthält keinerlei logische Validierungen -- diese Aufgabe übernimmt ausschließlich das Frontend. Bei einer direkten API-Nutzung verzichtest du vollständig auf Kontrollstrukturen. Beachte das bei der Nutzung.

Für die Nutzung von i-doit-Attributen setzt du zusätzlich die Forms-Addon-API voraus, um auf essentielle Attributinformationen zuzugreifen.

Authentifizierung#

Die Authentifizierung gegen das Forms-Backend erfolgt mit Benutzername und API-Schlüssel -- dieselben Konfigurationsparameter, die du in der i-doit-Konfiguration hinterlegt hast:

1
2
3
4
5
6
7
POST http://localhost:3000/login
Content-Type: application/json

{
"apikey": "APIKEY",
"name": "USERNAME"
}

Bei erfolgreicher Authentifizierung erhältst du einen JSON Web Token:

1
2
3
{
"access_token": "{JWT_TOKEN}"
}

Den Token gibst du fortan in jedem Request im Authorization-Header an, sofern der Endpunkt eine Authentifizierung voraussetzt. Beispiel:

1
2
GET http://localhost:3000/api/form
Authorization: bearer {JWT_TOKEN}

Ein Token ist standardmäßig 60 Minuten gültig. Nach Ablauf musst du dich erneut einloggen.

Endpunkte#

GET /api/form#

Dieser Endpunkt gibt eine Liste aller verfügbaren Formulare zurück. Darüber hinaus verfügt es über die Möglichkeit der Filterung und Sortierung.

1
2
3
GET http://localhost:3000/api/form
Accept: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MWU2YThlNmY1ZTMxYjI5NzAwOTMxOWEiLCJuYW1lIjoic2VsY3VrIiwic3ViIjoiJDJhJDEwJFJ4YlRybVpUVXlXc1NSQ2VZTFR6enVBZXJZTUF1dUlsNU5qOWt5RFN4WXlFL0NsdG1iLmY2IiwiaWF0IjoxNjU3MjkyNTAxLCJleHAiOjE2NTcyOTYxMDF9.yEZAjFAGpOCbDsJuI_vqot5J75MOE0bKPPn8osQS0Ik

GET /api/form/{ID}#

Abruf von spezifischen Formularen

1
2
GET http://localhost:3000/api/form/6245bf4f36f695945b3df9be
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MWU2YThlNmY1ZTMxYjI5NzAwOTMxOWEiLCJuYW1lIjoic2VsY3VrIiwic3ViIjoiJDJhJDEwJFJ4YlRybVpUVXlXc1NSQ2VZTFR6enVBZXJZTUF1dUlsNU5qOWt5RFN4WXlFL0NsdG1iLmY2IiwiaWF0IjoxNjU3MjkyNTAxLCJleHAiOjE2NTcyOTYxMDF9.yEZAjFAGpOCbDsJuI_vqot5J75MOE0bKPPn8osQS0Ik

Die Rückgabe sieht beispielhaft, wie folgt aus:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
{
"_id": "62864bbe2eb3cb20879f9405",
"data": [],
"shape": {
"root": {
    "children": [
    "page"
    ],
    "config": {
    "type": "RootForm",
    "props": {
        "name": "Form with dependency",
        "page": "page",
        "user": {
        "name": "admin ",
        "email": "",
        "id": "9"
        },
        "classification": "C__OBJTYPE__SERVER"
    }
    }
},
"page": {
    "children": [
    "Attribute1652968394906021170002572071178",
    "Attribute1652968394927036554591972381756",
    "Attribute1653044325681014562436038700444",
    "Attribute165304432570104575271313868843",
    "Attribute1653044340696047510895312740087"
    ],
    "config": {
    "type": "FormPage"
    }
},
"Attribute1652968394906021170002572071178": {
    "children": [],
    "config": {
    "type": "Attribute",
    "props": {
        "attribute": "C__CATG__MODEL.manufacturer",
        "defaultLabel": "Manufacturer (Model)",
        "label": "",
        "required": false,
        "isSystemRequired": false,
        "categoryLabel": "Model",
        "type": "dialog_plus",
        "hidden": false,
        "defaultValue": "5"
    }
    }
},
"Attribute1652968394927036554591972381756": {
    "children": [],
    "config": {
    "type": "Attribute",
    "props": {
        "attribute": "C__CATG__MODEL.title",
        "defaultLabel": "Model (Model)",
        "label": "",
        "required": false,
        "isSystemRequired": false,
        "categoryLabel": "Model",
        "type": "dialog_plus",
        "parent": "C__CATG__MODEL.manufacturer",
        "hidden": false,
        "defaultValue": null
    }
    }
},
"Attribute1653044325681014562436038700444": {
    "children": [],
    "config": {
    "type": "Attribute",
    "props": {
        "attribute": "C__CATG__CONTRACT_ASSIGNMENT.connected_contract",
        "defaultLabel": "Assigned contract (Contract assignment)",
        "label": "",
        "required": false,
        "isSystemRequired": false,
        "categoryLabel": "Contract assignment",
        "type": "object_browser"
    }
    }
},
"Attribute165304432570104575271313868843": {
    "children": [],
    "config": {
    "type": "Attribute",
    "props": {
        "attribute": "C__CATG__CONTRACT_ASSIGNMENT.contract_start",
        "defaultLabel": "Contract begin (Contract assignment)",
        "label": "",
        "required": false,
        "isSystemRequired": false,
        "categoryLabel": "Contract assignment",
        "type": "date",
        "parent": "C__CATG__CONTRACT_ASSIGNMENT.connected_contract"
    }
    }
},
"Attribute1653044340696047510895312740087": {
    "children": [],
    "config": {
    "type": "Attribute",
    "props": {
        "attribute": "C__CATG__CONTRACT_ASSIGNMENT.contract_end",
        "defaultLabel": "Contract end (Contract assignment)",
        "label": "",
        "required": false,
        "isSystemRequired": false,
        "categoryLabel": "Contract assignment",
        "type": "date",
        "parent": "C__CATG__CONTRACT_ASSIGNMENT.connected_contract"
    }
    }
}
},
"instance": "61e6a8e6f5e31b297009319a",
"published": true,
"updated": "2022-05-20T11:09:49.194Z",
"created": "2022-05-19T13:53:02.143Z",
"name": "Form with dependency",
"__v": 0,
"identifier": "form-with-dependency-62864bbe"
}

DELETE /api/form/{ID}#

Über das DELETE-Verb löschst du ein bestimmtes Formular. Die Rückgabe enthält die Formular-Daten.

1
2
DELETE http://localhost:3000/api/form/6245bf4f36f695945b3df9be
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MWU2YThlNmY1ZTMxYjI5NzAwOTMxOWEiLCJuYW1lIjoic2VsY3VrIiwic3ViIjoiJDJhJDEwJFJ4YlRybVpUVXlXc1NSQ2VZTFR6enVBZXJZTUF1dUlsNU5qOWt5RFN4WXlFL0NsdG1iLmY2IiwiaWF0IjoxNjU3MjkyNTAxLCJleHAiOjE2NTcyOTYxMDF9.yEZAjFAGpOCbDsJuI_vqot5J75MOE0bKPPn8osQS0Ik

PUT /api/form/{ID}#

Über diesen Endpunkt bearbeitest du ein Formular. Im JSON-Body gibst du lediglich die zu ändernden Attribute an.

POST /api/form#

Über diesen Endpunkt erstellst du neue Formulare. Die Definition erfolgt über JSON mit folgender Struktur:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
// Name des Forms
"name": "My Form",
// Struktur
"shape": {
...
},
// Veröffentlichungsstatus
"published": false
}

Die Struktur des Formulars gibst du unter shape an. Sie bildet eine normalisierte und hierarchische Struktur ab. Auf der ersten Ebene enthält sie alle verfügbaren Knoten-Elemente: Überschriften, Texte, Trennlinien, i-doit-Kategorie-Attribute und Seiten.

Es ist sicherzustellen, dass die Knoten-IDs (root, SEITE_1, SEITE_2,…) einzigartig sind und keine Wiederholungen aufweisen.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"shape": {
// Einstiegsknoten
"root": {
    ..config..
},
// Seite 1
"SEITE_1": {
    ..config..
},
// Seite 2
"SEITE_2": {
    ..config..
},
// Überschrift 1
"ÜBERSCHRIFT_1": {
    ..config..
},
// TEXT 1
"TEXT_1": {
    ..config..
}
}
}

Die verfügbaren Konfigurationsparameter eines Knotens sind typabhängig. Die allgemeine Datenstruktur sieht so aus:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"KNOTEN_1": {
// Welche Knoten sind in "KNOTEN_1" enthalten - Angabe der Knoten-ID
"children": [
    "KNOTEN_1.1",
    "KNOTEN_1.2"
],
// Konfigurationsparameter zu "KNOTEN_1"
"config": {
    // Typ des Knotens
    "type": "KNOTEN_TYP",
    // Direkte Konfigurationsparameter
    "props": {
    "parameter_1": "",
    "parameter_2": "",
[
...
]
}
}
}
}

Besonders wichtig ist der Knoten root. Er stellt den Einstiegspunkt dar, auf dessen Basis du das Formular hierarchisch aufbaust.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
                    ┌──────ROOT──────┐
                    │                │
                    │                │
                    ▼                ▼
          ┌─────────Seite 1───┐      Seite 2────────────┬────────┬───────────┐
          │           │       │            │            │        │           │
          │           │       │            │            │        │           │
          │           │       │            │            │        │           │
          │           │       │            ▼            │        │           │
          │           │       │         Attribut 2      │        │           │
          ▼           │       ▼                         │        │           │
     Überschrift 1    │    Text 1                       ▼        │           │
                      │                             Attribut 3   │           │
                      │                                          │           │
                      │                                          ▼           │
                      ▼                                      Attribut 4      │
                 Attribut 1                                                  │
                                                                             ▼
                                                                        Text 2

Im Folgenden siehst du eine vollständige Struktur eines Formulars, das aus zwei Seiten ("SEITE_1" und "SEITE_2") besteht. Jede Seite beinhaltet eine Überschrift und eine Textbeschreibung.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
{
"shape": {
// Root-Knoten
// Enthält Meta-Informationen sowie die Basisstruktur
"root": {
    // Beinhaltet die IDs der Pages
    "children": [
    "SEITE_1",
    "SEITE_2"
    ],
    // Meta-Informationen
    "config": {
    // Knoten-Type - Muss zwingend als RootForm angegeben werden
    "type": "RootForm",
    "props": {
        // Name des Formulars
        "name": "My Form",
        // Initial zu ladende Page
        "page": "SEITE_1",
        // Benutzerinformationen basierend auf dem i-doit Benutzer
        "user": {
        "name": "admin",
        "email": "admin@i-doit.com",
        "id": "9"
        },
        // Zugrundeliegender Objekttyp
        "classification": "C__OBJTYPE__SERVER"
    }
    }
},
// Definition der 1. Seite
"SEITE_1": {
    // Seite beinhaltet "ÜBERSCHRIFT_1" und "TEXT_1"
    "children": [
    "ÜBERSCHRIFT_1",
    "TEXT_1"
    ],
    "config": {
    // Gibt an, dass es sich bei diesem Knoten um eine Seite handelt
    "type": "FormPage",
    "props": {}
    }
},
// Analoger Aufbau wie bereits "SEITE_1"
"SEITE_2": {
    // Beinhaltet "ÜBERSCHRIFT_2" und "TEXT_2"
    "children": [
    "ÜBERSCHRIFT_2",
    "TEXT_2"
    ],
    "config": {
    "type": "FormPage",
    "props": {}
    }
},
// ...
"ÜBERSCHRIFT_1": {
    "children": [],
    "config": {
    "type": "Headline",
    "props": {
        "label": "Page 1 Headline",
        "size": "h1"
    }
    }
},
"TEXT_1": {
    "children": [],
    "config": {
    "type": "Text",
    "props": {
        "text": "Page 1 Text",
        "placeholder": "Enter your text"
    }
    }
},
"ÜBERSCHRIFT_2": {
    "children": [],
    "config": {
    "type": "Headline",
    "props": {
        "label": "Page 2 Headline",
        "size": "h1"
    }
    }
},
"TEXT_2": {
    "children": [],
    "config": {
    "type": "Text",
    "props": {
        "text": "Page 2 Text",
        "placeholder": "Enter your text"
    }
    }
}
}
}

Typen#

Im folgenden Abschnitt findest du alle verfügbaren Knotentypen und deren Konfigurationen.

Trennlinie#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
"Dividingline165731154526401147116484517714": {
"children": [],
"config": {
    // Type des Knotens: Trennlinie
    "type": "Dividing line",
    "props": {
    // Dicke der Trennlinie
    "height": 1
    }
}
}
}

Text#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{
"Text165729439981501359363935038671": {
"children": [],
"config": {
    // Type des Knotens: Text
    "type": "Text",
    "props": {
    // Anzuzeigender Text
    "text": "Page 2 Text",
    // Platzhalter: Wird angezeigt, sofern der Inhalt im Frontend geleert wird
    "placeholder": "Enter your text",
    // Sichtbarkeit: Soll das Element versteckt werden?
    "hidden": true
    }
}
}
}

Überschrift#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
"Headline165729439333006517788648825422": {
"children": [],
"config": {
    // Type des Knotens: Überschrift
    "type": "Headline",
    "props": {
    // Inhalt der Überschrift
    "label": "Page 2 Headline",
    // Größe: Mögliche Werte sind h1, h2 oder h3
    "size": "h1"
    }
}
}
}

i-doit Kategorieattribute#

Dieser Typ ist komplexer als die vorherigen und enthält daher mehr Konfigurationsparameter. Die Parameter unterscheiden sich je nach Typ des Kategorieattributs.

Die grundlegende Datenstruktur wird am Beispiel des Attributs Allgemein > Bezeichnung beschrieben:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
"Attribute165731273460305039947937820184": {
"children": [],
"config": {
    // Type des Knotens: Kategorieattribut
    "type": "Attribute",
    "props": {
    // Attribut-ID: Zusammensetzung aus der Konstante der Kategorie und des technischen Attributidentifikators
    "attribute": "C__CATG__GLOBAL.title",
    // Standardmäßige Bezeichnung des Feldes im Formular, sofern "label" nicht angegeben wurde
    "defaultLabel": "Bezeichnung (Allgemein)",
    // Feldbezeichnung
    "label": "BEZEICHNUNG",
    // Handelt es sich um ein Pflichtfeld?
    "required": false,
    // Wird das Attribut i-doit-seitig vorausgesetzt? Nativ oder aber über die Validierungskonfiguration.
    "isSystemRequired": false,
    // Label der Kategorie
    "categoryLabel": "Allgemein",
    // Attributtyp
    "type": "text",
    // Ist das Feld sichtbar?
    "hidden": false,
    // Standardwert
    "defaultValue": "Vorausgefüllter Wert"
    }
}
}
}

Metainformationen zu Attributen#

Einige Konfigurationsparameter werden von der Forms-Addon-API vorgegeben. Sie bestehen aus statischen oder dynamischen Werten:

  • attribute*: statischer Wert
  • type* : statischer Wert, gibt den Attributtypen wieder
  • isSystemRequired : variabler Wert, wird auf Basis der Validierung oder nativ berechnet

Dieser Parameter überschreibt required, sofern das Attribut system-seitig vorausgesetzt wird.

  • defaultValue : variabler Wert, wird vorausgefüllt, sofern der Objekttyp ein Default-Template adressiert, welches ein Wert für das Attribut definiert

Eine fehlerhafte Angabe von isSystemRequired wird zwangsläufig in einen Fehler beim Submit des Formulars führen, sofern required nicht true ist. defaultValue ist nicht zwingend notwendig, sofern die Werte aus dem Default-Template nicht berücksichtigt werden sollen.

All diese Informationen ermittelst du über die Forms-Addon-API:

1
GET https://idoit-instance/forms/api/attribute?category=C__CATG__GLOBAL,C__CATG__ACCOUNTING&class=C__OBJTYPE__SERVER

category enthält eine kommaseparierte Liste an Kategoriekonstanten. class enthält die Objekttypkonstante und wird benötigt, um defaultValue zu ermitteln. Beispielhafte Rückgabe:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
[
{
"id": "C__CATG__GLOBAL.title",
"title": "Bezeichnung",
"type": "text",
"isSystemRequired": false,
"defaultValue": "SERVER_TEMPLATE",
"parent": null,
"children": null
},
{
"id": "C__CATG__GLOBAL.purpose",
"title": "Einsatzzweck",
"type": "dialog_plus",
"isSystemRequired": false,
"defaultValue": "7",
"parent": null,
"children": null
},
{
"id": "C__CATG__GLOBAL.category",
"title": "Kategorie",
"type": "dialog_plus",
"isSystemRequired": false,
"defaultValue": "2",
"parent": null,
"children": null
},
{
"id": "C__CATG__GLOBAL.sysid",
"title": "SYS-ID",
"type": "text",
"isSystemRequired": false,
"defaultValue": "SYSID_1655124042",
"parent": null,
"children": null
},
{
"id": "C__CATG__GLOBAL.cmdb_status",
"title": "CMDB-Status",
"type": "dialog",
"isSystemRequired": false,
"defaultValue": "6",
"parent": null,
"children": null
},
{
"id": "C__CATG__GLOBAL.tag",
"title": "Tags",
"type": "multiselect",
"isSystemRequired": false,
"defaultValue": null,
"parent": null,
"children": null
},
{
"id": "C__CATG__GLOBAL.description",
"title": "Beschreibung",
"type": "commentary",
"isSystemRequired": false,
"defaultValue": "<p>Standardbeschreibung des Default-Templates<\/p>",
"parent": null,
"children": null
}
]

Sonderfall: Feldabhängigkeiten#

i-doit verfügt über Attribute, die eine Abhängigkeit zu einem anderen Attribut innerhalb der Kategorie aufweisen. Ein einfaches Beispiel ist Modell > Modell, das von Modell > Hersteller abhängt.

Das Forms-Addon-Frontend enthält Kontrollmechanismen, die erkennen, wenn du Modell > Modell (Child) ohne Modell > Hersteller nutzt, und das Parentattribut automatisch hinzufügen.

Diese Attribute identifizierst du anhand ihrer Metadaten: Das Childattribut enthält eine parent- und das Parentattribut eine children-Information:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
GET https://idoit-instance/forms/api/attribute?category=C__CATG__MODEL&class=C__OBJTYPE__SERVER

[
{
"id": "C__CATG__MODEL.manufacturer",
"title": "Hersteller",
"type": "dialog_plus",
"isSystemRequired": false,
"defaultValue": "-1",
"parent": null,
"children": [
    {
    "id": "C__CATG__MODEL.title",
    "title": "Modell",
    "type": "dialog_plus",
    "isSystemRequired": false,
    "parent": "C__CATG__MODEL.manufacturer"
    }
]
},
{
"id": "C__CATG__MODEL.title",
"title": "Modell",
"type": "dialog_plus",
"isSystemRequired": false,
"defaultValue": "-1",
"parent": {
    "id": "C__CATG__MODEL.manufacturer",
    "title": "Hersteller",
    "type": "dialog_plus",
    "isSystemRequired": false
},
"children": null
},
[...]
]

Die Definition der Knoten für solche Abhängigkeitsstrukturen sieht hingegen wie folgt aus:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{
// Hersteller: Enthält keinerlei Informationen zur Abhängigkeit
"Attribute165731798525604834639173302364": {
"children": [],
"config": {
    "type": "Attribute",
    "props": {
    "attribute": "C__CATG__MODEL.manufacturer",
    "defaultLabel": "Hersteller (Modell)",
    "label": "",
    "required": false,
    "isSystemRequired": false,
    "categoryLabel": "Modell",
    "type": "dialog_plus"
    }
}
},
// Modell: Parameter "parent" adressiert "Hersteller", um die Abhängigkeit zu definieren
"Attribute165731798528807005258890749957": {
"children": [],
"config": {
    "type": "Attribute",
    "props": {
    "attribute": "C__CATG__MODEL.title",
    "defaultLabel": "Modell (Modell)",
    "label": "",
    "required": false,
    "isSystemRequired": false,
    "categoryLabel": "Modell",
    "type": "dialog_plus",
    "parent": "C__CATG__MODEL.manufacturer",
    "defaultValue": null,
    "hidden": false
    }
}
}
}

Siehe auch#