VNG-Realisatie / gemma-zaken

Samen ontwikkelen van API's voor Zaakgericht werken
https://vng-realisatie.github.io/gemma-zaken/
Other
41 stars 26 forks source link

String velden - null of lege string of ... #2447

Open basretera opened 1 month ago

basretera commented 1 month ago

In onze testen met andere leveranciers lopen we tegen een issue aan m.b.t. optionele string velden. Via dit issue wil ik nogmaals vragen om een uitspraak te doen in hoe we hiermee om moeten gaan aangezien de standaard niet eenduidig is. Dit issue is in navolging op issue #1901 en issue #2090 aan. De standaard is gewoonweg niet duidelijk genoeg hierin. Wat moeten we nu doen met niet verplichte (string) velden? Opties die er zijn:

  1. Het veld weglaten in de request body;
  2. Opnemen maar met de waarde null; of
  3. Een lege waarde "" meegeven.

Optie 3 vind ik totaal niet fraai, maar het belangrijkste is dat er duidelijkheid komt. De voorgaande issues staan inmiddels 1,5 jaar open en iedere leverancier maakt hier zijn eigen keuzes in die mogelijkerwijs niet stroken met elkaar. Graag op korte termijn een uitspraak hierover zodat het voor alle leveranciers duidelijk is.

basretera commented 1 month ago

Dit issue ligt alweer 7 dagen zonder enige reactie. Dit moet echt prioriteit krijgen. We lopen in onze testen hier zwaar tegenaan. Hier moet echt snel duidelijkheid over komen!

HenriKorver commented 1 month ago

Ik begrijp het issue niet goed. Kun je een concreet voorbeeld geven waar precies de problemen optreden?

basretera commented 1 month ago

image

In de specificatie van deze optionele stringvelden staat veelal bovenstaande (voorbeeld) gespecificeerd. Namelijk dat een veld een stringwaarde moet bevatten <= x karakters. Wat wordt hiermee bedoeld? Moeten deze optionele velden dan met een lege stringwaarde "" aangeboden worden? Logischer zou zijn dat deze een null-waarde zouden kunnen hebben. Null-waardes lijkt me toch een beetje de algehele standaard. Maar op andere plekken (zie hieronder) staat weer gespecificeerd string or null voor een vergelijkbaar veld.

image

En mag je optionele velden helemaal weglaten? Of moet je ze wel altijd teruggeven/meesturen maar dan inderdaad met een null-waarde of lege string-waarde ""?

Kijk ook zeker naar de lopende discussies in de gerelateerde issues.

HenriKorver commented 1 month ago

Namelijk dat een veld een stringwaarde moet bevatten <= x karakters. Wat wordt hiermee bedoeld?

Bijvoorbeeld <= 80 betekent dat de string niet langer mag zijn dan 80. Dus de string mag wel een lengte 0 hebben, oftewel leeg zijn.

basretera commented 1 month ago

Dat snap ik natuurlijk ook. Maar daar gaat het niet om.

HenriKorver commented 1 month ago

En mag je optionele velden helemaal weglaten? Of moet je ze wel altijd teruggeven/meesturen maar dan inderdaad met een null-waarde of lege string-waarde ""?

In principe mag je zelf bepalen of je lege attributen die niet required zijn wel of niet opneemt in een bericht. In een request bericht laat ik de lege attributen altijd weg als ze niet verplicht zijn, lijkt me logisch want waarom zou ik de moeite doen. Echter, bij een respons-bericht vind ik het wel weer prettig als ook de attributen met een lege waarde worden teruggegeven, want dan zie ik namelijk in één oog opslag welke attributen wel of niet gevuld zijn. De referentie-implementatie doet het ook zo.

basretera commented 1 month ago

Dank voor je antwoord, maar het geeft nog steeds geen antwoord op de meest prangende vraag: moet een leeg veld als lege string "" of als null-waarde in een request meegegeven en in een response teruggegeven worden?

HenriKorver commented 1 month ago

In de meeste gevallen zijn de attributen zo gespecificeerd dat er maar één waarde voor het leeg zijn mogelijk is, dus of null of "" (lege string), maar niet beide. Dus daar speelt dit probleem niet. Helaas zijn er een paar gevallen waarbij dit niet goed is gegaan zoals bij het attribuut "processobjectaard". In dat geval moet de API beide waarden ondersteunen (null en lege string). Het is niet fraai maar het werkt wel.

Bij het toevoegen van nieuwe attributen in toekomstige versies moeten we goed in de gaten houden dat we slechts één leeg-waarde toelaten. De vraag is of we in een volgende major release ook alle oude attributen waar dit probleem speelt willen fixen (lees: beperken tot één leeg-waarde) want dat zal mogelijk tot veel backwards compatibiliteits problemen kunnen leiden.

basretera commented 1 month ago

Laatste vraag: Kun jij mij uitleggen waarom er gekozen is voor een lege string "" waarde i.p.v. null? Wat veel logischer lijkt voor mij. Is dat in REST API standaarden gebruikelijker of wat is de reden?

HenriKorver commented 1 month ago

Ik weet niet wat de exacte beweegredenen waren om te kiezen voor een lege string "" i.p.v. null en ik weet ook niet wat de beweegredenen waren om daar weer soms van af te wijken zoals bij het attribuut 'processobjectaard'. Dat was voor mijn tijd. Het niet consequent zijn is een groter probleem dan de waarom-vraag denk ik.

Persoonlijk vind ik het mooier als attributen van het type "string" een lege string "" als lege waarde teruggeven en niet null. Het is namelijk een lege waarde die het datatype string al van zichzelf heeft en je hoeft dan geen nieuw concept zoals null te introduceren. Hetzelfde verhaal voor attributen van het type "array": die geeft je de lege lijst [] terug en niet de waarde null. Immers het datatype array heeft intrinsiek een eigen lege waarde namelijk de lege lijst [] net zoals string ook zijn eigen lege waarde heeft namelijk de lege string "". Het mooie is dan ook dat je in de respons-berichten kunt zien van welke type het lege attribuut is ("" -> string, [] -> array). Dit vind ik fijner dan dat er null had gestaan.

{
    "count": 1,
    "next": null,
    "previous": null,
    "results": [
        {
            "url": "https://zaken-api.vng.cloud/api/v1/zaken/01a50969-359c-4a0e-95ca-79293914534b",
            "uuid": "01a50969-359c-4a0e-95ca-79293914534b",
            "identificatie": "ZAAK-2019-0000000002",
            "bronorganisatie": "000000000",
            "omschrijving": "aangepast",
            "toelichting": "string",
            "zaaktype": "https://catalogi-api.vng.cloud/api/v1/zaaktypen/f0cdd277-c62d-4464-a0a7-ddffd6d29f6b",
            "registratiedatum": "2019-04-09",
            "verantwoordelijkeOrganisatie": "000000000",
            "startdatum": "2019-04-09",
            "einddatum": null,
            "einddatumGepland": "2019-04-20",
            "uiterlijkeEinddatumAfdoening": "2019-04-09",
            "publicatiedatum": "2019-04-09",
            "communicatiekanaal": "",
            "productenOfDiensten": [],
            "vertrouwelijkheidaanduiding": "openbaar",
            "betalingsindicatie": "geheel",
            "betalingsindicatieWeergave": "De met de zaak gemoeide kosten zijn geheel betaald.",
            "laatsteBetaaldatum": null,
            "zaakgeometrie": {
                "type": "Point",
                "coordinates": [
                    53.0,
                    5.0
                ]
            },
            "verlenging": {
                "reden": "",
                "duur": null
            },
            "opschorting": {
                "indicatie": true,
                "reden": "string"
            },
            "selectielijstklasse": "https://referentielijsten-api.vng.cloud/api/v1/resultaten/fb65ae34-e820-4ace-815a-cf5da7d04a12",
            "hoofdzaak": null,
            "deelzaken": [],
            "relevanteAndereZaken": [],
            "eigenschappen": [
                "https://zaken-api.vng.cloud/api/v1/zaken/01a50969-359c-4a0e-95ca-79293914534b/zaakeigenschappen/e5235521-e735-40a3-8005-2b9953757c2c"
            ],
            "rollen": [],
            "status": null,
            "zaakinformatieobjecten": [],
            "zaakobjecten": [],
            "kenmerken": [],
            "archiefnominatie": null,
            "archiefstatus": "nog_te_archiveren",
            "archiefactiedatum": null,
            "resultaat": null,
            "opdrachtgevendeOrganisatie": "",
            "processobjectaard": null,
            "startdatumBewaartermijn": null,
            "processobject": {
                "datumkenmerk": "",
                "identificatie": "",
                "objecttype": "",
                "registratie": ""
            }
        }
    ]
}

In mijn ogen is de definitie van het attribuut "processobjectaard" een designfout en had er een lege string "" teruggegeven moeten worden in plaats van null. Want alle andere attributen van het type string geven wel een lege string "" terug zoals datumkenmerk, identificatie, objecttype , etc.

sergei-maertens commented 2 weeks ago

OpenAPI maakt gebruik van JsonSchema (ze zijn niet 100% gelijk, maar bewegen wel die kant op), dus om deze vraag te beantwoorden moet je naar de details van JsonSchema kijken. Een eenvoudig voorbeeld:

type: object
required:
  - property1
properties:
  property1:
    type: string
    maxLength: 80
  property2:
    type:
      - string
      - 'null'
    maxLength: 20

Extra validates zijn altijd bovenop het type waarop de validatie betrekking heeft. maxLength heeft geen betekenis voor null, en is dus niet relevant. Een property die niet required is, mag enkel als null meegestuurd worden als het type van de property null toestaat.

In OpenAPI 3.1 gebruiken ze deze array voor types, voor eerdere versie is hier de nullable: true schema key beschikbaar (even uit het hoofd).

De volgende ("lege") data objecten zijn dus wel geldig voor het schema hierboven:

{"property1": ""}
{"property1": "", "property2": ""}
{"property1": "", "property2": null}

Dit is een onderwerp waar veel verwarring over bestaat, maar het is wel gespecifieerd in de onderliggende JsonSchema specificatie.

sergei-maertens commented 2 weeks ago

@HenriKorver

Persoonlijk vind ik het mooier als attributen van het type "string" een lege string "" als lege waarde teruggeven en niet null. Het is namelijk een lege waarde die het datatype string al van zichzelf heeft en je hoeft dan geen nieuw concept zoals null te introduceren. Hetzelfde verhaal voor attributen van het type "array": die geeft je de lege lijst [] terug en niet de waarde null. Immers het datatype array heeft intrinsiek een eigen lege waarde namelijk de lege lijst [] net zoals string ook zijn eigen lege waarde heeft namelijk de lege string "".

Hier zit wel nuance die relevant kan zijn! null is een weergave dat er niets gezet is, het is onbekende informatie. Terwijl een lege array [] gewoon kan betekenen dat er wel iets gezet is, maar er is geen relevante informatie. null lees je typisch als "onbepaald", in plaats van "leeg". Tri-state logica kent bijvoorbeeld true | false | null om dit weer te geven.

basretera commented 2 weeks ago

@sergei-maertens Dank voor je heldere uiteenzetting!

erikhoevenberg commented 3 days ago

Bovenstaande maakt het een en ander duidelijk maar ik heb nog een vervolg vraag. Is "" een geldige waarde voor een url/datum/duration? Voor enum kan ik afleiden dat dit niet het geval is omdat er soms expliciet een "Empty Enum" optie staat gespecifieerd.

sergei-maertens commented 3 days ago

Voor de zekerheid heb ik het zelf even getoetst op https://www.jsonschemavalidator.net/, met schema:

{
  "type": "string",
  "format": "date-time"
}

en waarde:

""

en deze is inderdaad niet geldig. Daarom zou je dus typisch wel null toelaten om een onbekende waarde te communiceren.

Alledrie de voorbeelden die je noemt zijn ongeldig met lege-string.