vng-realisatie / api-kennisbank Goto Github PK
View Code? Open in Web Editor NEWAfspraken, werkinstructies, tutorials, enz. voor de co-creatie en het beheer van API standaarden
Home Page: https://vng-realisatie.github.io/API-Kennisbank/
Afspraken, werkinstructies, tutorials, enz. voor de co-creatie en het beheer van API standaarden
Home Page: https://vng-realisatie.github.io/API-Kennisbank/
Wanneer er voor een begindatum of einddatum al een functioneel gegeven bestaat, gebruiken we die. Denk aan datumOntbindingHuwelijk of datumAanvangAdreshouding.
We maken in de Regels bij activiteiten API gebruik van het Generieke datatype 'GeoJsonGeometrie'.
Daarvoor heb jij in het conceptualschema ook een entry met dezelfde naam gemaakt.
Bij gebruik levert dat in de yaml de volgende referentie op:
In het schema 'https://raw.githubusercontent.com/VNG-Realisatie/API-Kennisbank/master/common/Generieke-Datatypen-Gemeenten.yaml' heb je daarvoor ook een component opgenomen echter met een naam die niet overeen komt met de in EA en het conceptualschema gehanteerde naam nl. 'GeoJSONGeometrie'.
Daardoor krijg ik in Swagger een foutmelding. We moeten dus een van de volgende correcties uitvoeren:
We hernoemen het component in 'https://raw.githubusercontent.com/VNG-Realisatie/API-Kennisbank/master/common/Generieke-Datatypen-Gemeenten.yaml' naar 'GeoJsonGeometrie'. Dit heeft als nadeel dat het yamlschema in de GitHub voor de Regels bij activiteiten API meteen invalid wordt en ook aangepast moet worden.
We hernoemen het component in EA en in het conceptualschema naar 'GeoJSONGeometrie', waarmee het meteen opgelost is. Nadeel daarvan is wel dat we afwijken van de wijze waarop het in de API voor de omgevingsdocumenten is gedaan.
Wat heeft de voorkeur?
De lengte van enumeratiewaarden wordt beperkt. Bijvoorbeeld "Opstalhouder Nutsvoorzieningen op gedeelte van perceel" krijgt als code "nutsvoorzieningen_gedeelte".
Indien een object een groepattribuutsoort heeft (een groep van bij elkaar behorende attribuutsoorten), al dan niet herhalend d.w.z. kardinaliteit nul of meer (een ‘lijst’ van waarden van de attributen van het groepattribuut), dan wordt het groepsattribuut inline (binnen de resource voor het object) ontsloten. Het krijgt dus geen eigen resource, het heeft immers geen zelfstandig bestaansrecht. Het heeft alleen bestaansrecht als eigenschap van het object, net zoals attribuutsoorten. Typische voorbeelden zijn (zaak-)kenmerken en (zaak-)eigenschappen waarin Zaak het hoofdobject betreft, en Kenmerk en Eigenschap de groepattributen.
Voorbeeld:
{
"url": "https://example.com/zrc/api/v1/zaken/6232ba6a-beee-4357-b054-6bde6d1ded6c",
"zaaktype": "https://example.com/ztc/api/v1/zaaktypen/6232ba6a-beee-4357-b054-6bde6d1ded6c"
// ...
"kenmerken": [
{
"kenmerk": "test",
"bron": "http://www.example.com/"
}
]
}
Dus niet M en V, maar man en vrouw.
Ratio Een developer moet bij het coderen begrijpen wat de code betekent, om fouten in de implementatie te voorkomen. Kanttekening De lengte van de enumeratiewaarden zal zoveel mogelijk beperkt moeten worden (Zie de design rule over lengtes van enumeratiewaarden #38).
Om relaties zowel naar een andere resource (zoals status) als naar een genest attribuut (zoals kenmerken) te gebruiken binnen query parameters, wordt de __ (dubbele underscore) gebruikt tussen de attribuutnamen.
Voorbeelden
Voor filteren:
?zaak__kenmerken=test
?zaak__status=http://example.com
Voor de fields query parameter:
?fields=zaak__kenmerken
?fields=zaak__status
Wanneer er een gegevenswoordenboek (gegevenscatalogus, informatiemodel) bestaat, gebruiken we voor corresponderende resources of voor corresponderende properties in een resource de naam zoals die in het gegevenswoordenbook staat, met inachtneming van de Design Rules voor naamgeving, zoals gebruik (Upper)snakeCase.
Van de naam in het gegevenswoordenboek kan worden afgeweken in o.a. de volgende situaties:
Advies op basis van #36 . Deze Design Rule is opgesplitst.
JBo: Overnemen
HK: Overnemen
RM: Discussie
MV: Overnemen
GJ: Bespreken
JBi: Idem DD1.6
Bijvoorbeeld in de resource van een ingeschreven natuurlijk persoon bij de BRP-bevraging kunnen de relaties partner(schap), ouders en kinderen embed worden opgenomen met gebruik van de expand-parameter. Dit betekent dat het mogelijk is in één aanroep de ingeschreven persoon te krijgen, met daarbij gegevens over de relaties met eventuele partner(s), ouders en kinderen.
Wanneer een gerelateerde resource expand wordt, wordt de gehele sub-resource teruggegeven, tenzij in de expand parameter alleen een deel van de gerelateerde resource gevraagd is.
Gegevens uit een andere bron/registratie (bijvoorbeeld het BAG-adres van een persoon) kunnen niet embed worden. Deze worden alleen als link opgenomen.
Ratio : We willen "tight coupling" met andere bronnen voorkomen. Over de domeinen heen wordt alleen met links verwezen.
Kanttekening Er zijn grofweg twee categoriën Hal-links waar we gebruik van maken. Links naar resources binnen het eigen domein en links naar resources die in een ander domein beheerd worden. Om discoverability te bereiken, worden voor beide categorieën de Hal-link opgenomen naar de gerelateerde resource.
Schema componenten voor dynamische domeinwaarden (referentielijsten zoals "Tabel 32 Nationaliteitentabel") en enumeraties krijgen respectievelijk extensie "Tabel" en "Enum" zonder de toevoeging van underscores.
Wanneer een relatie-property (niet zijnde een link of embedded) alleen de identificatie van een gerelateerde resource bevat en geen andere properties, wordt als naam van de property de naam van de resource plus het woord 'Identificatie' gebruikt.
Bijvoorbeeld: maakt deel uit van + pand + Identificatie = pandIdentificatie
Indien een object een groepattribuutsoort heeft (een groep van bij elkaar behorende attribuutsoorten), al dan niet herhalend d.w.z. kardinaliteit nul of meer (een ‘lijst’ van waarden van de attributen van het groepattribuut), dan wordt het groepattribuut inline (binnen de resource voor het object) ontsloten. Het krijgt dus geen eigen resource, het heeft immers geen zelfstandig bestaansrecht. Het heeft alleen bestaansrecht als eigenschap van het object, net zoals attribuutsoorten. Typische voorbeelden zijn (zaak-)kenmerken en (zaak-)eigenschappen waarin Zaak het hoofdobject betreft, en Kenmerk en Eigenschap de groepattributen.
Voorbeeld:
{
"url": "https://example.com/zrc/api/v1/zaken/6232ba6a-beee-4357-b054-6bde6d1ded6c",
"zaaktype": "https://example.com/ztc/api/v1/zaaktypen/6232ba6a-beee-4357-b054-6bde6d1ded6c"
// ...
"kenmerken": [
{
"kenmerk": "test",
"bron": "http://www.example.com/"
}
]
}
Bij het gebruik van 'allOf' staat de component die hergebruikt wordt altijd eerst, en staan de toegevoegde properties als tweede.
Voorbeeld van het correct gebruik van 'allOf':
allOf:
- $ref: "#/components/schemas/Naam"
- description: "Gegevens over de naam van de persoon"
properties:
aanhef:
type: "string"
Voorbeeld van foutief gebruik van 'allOf':
NaamPersoon:
allOf:
- description: "Gegevens over de naam van de persoon"
properties:
aanhef:
type: "string"
- $ref: "#/components/schemas/Naam"
Ratio
Afwijken van deze regel leidt tot problemen bij het genereren van code uit de API specificaties.
In deze Kennisbank staan in de aangehaalde landelijke lijst met Design Rules en Principles staan een aantal onvolkomenheden.
Ik wil deze aanpassing met alle plezier even uitvoeren maar wil dan graag op de bovengenoemde punten 3, 4 en 5 een antwoord hebben.
Bij gebruik van allOf is er altijd exact één component waarnaar gerefereerd wordt en één gedefinieerd object met ten minste één property.
Voorbeeld van het foutief gebruik van allOf:
NaamPersoon:
allOf:
- $ref: "#/components/schemas/Naam"
- $ref: "#/components/schemas/Aanschrijfwijze"
- description: "Gegevens over de naam van de persoon"
properties:
aanhef:
type: "string"
Er wordt hier uit twee componenten overgeërfd wat niet correct is.
Voorbeeld van het foutief gebruik van allOf:
NaamPersoon:
allOf:
- $ref: "#/components/schemas/Naam"
- description: "Gegevens over de naam van de persoon"
NaamPersoon heeft geen eigen properties wat niet correct is.
Voorbeeld van correct gebruik van allOf:
Naam:
type: "object"
properties:
geslachtsnaam:
type: "string"
description: |
De achternaam van een persoon.
example: "Vries"
voorletters:
type: "string"
description: |
De voorletters van de persoon, afgeleid van de voornamen.
example: "P.J."
NaamPersoon:
allOf:
- $ref: "#/components/schemas/Naam"
- description: "Gegevens over de naam van de persoon"
properties:
aanhef:
type: "string"
aanschrijfwijze:
type: "string"
Ratio
Afwijken van deze regel leidt tot problemen bij het genereren van code uit de API specificaties.
De identificatie van een resource zit, als die is opgenomen in de resource en gebruikt wordt als path-parameter van het resource-endpoint, altijd op het hoogste niveau van de resource in de vorm en inhoud zoals die wordt opgenomen in de uri (path-parameter) van de resource.
De API levert alleen gegevens terug waar de vragende organisatie voor geautoriseerd is. Er worden geen aparte endpoints of resources gedefinieerd per autorisatieprofiel.
De bronhouder die gegevens ter beschikking stelt is verantwoordelijk voor het leveren van alleen gegevens waarvoor de juiste autorisatie bestaat. Bij de bronhouder gaat het om autorisatie op niveau van de vragende organisatie (gemeente). De gemeente is er verantwoordelijk voor de resultaten van de API aanvraag te filteren op autorisatie van de betreffende gebruiker.
Ratio
Uitgangspunt in de architectuur is gedelegeerde autorisatie.
We nemen geen reden op over het leeg/afwezig zijn van een waarde van een gegeven (zoals met StUF:noValue werd gedaan), tenzij duidelijk is dat er bij de gebruikers behoefte is om dit te weten.
Ratio: Deze reden werd opgenomen vanuit synchronisatie-overwegingen. In principe willen we REST/Json API's niet inzetten voor synchronisatie-doeleinden.
Noot: Tekst is aangepast n.a.v. opmerking van RM.
Voor een query-parameter waarin een entry uit een waardelijst of een landelijke tabel als selectie-criterium wordt gebruikt wordt de code van de entry gebruikt.
Ratio: De omschrijving is human readable tekst. Daar kunnen verschillen in staan, bijvoorbeeld hoofd- of kleine letters. Voor een computer een verschil, voor een mens niet. Daarnaast levert het gebruik van codes ook kortere URL's op.
Indien een parameter een element betreft dat geen onderdeel van de op te vragen resource is, maar onderdeel van een gerelateerde resource, een subresource of een gegevensgroep, dan wordt de elementnaam voorafgegaan door de betreffende resourcenaam of gegevensgroepnaam en vervolgens twee underscores.
Bijvoorbeeld:
Een 0-op-N of 1-op-N relatie tussen objecttypen A en B in een informatiemodel zoals RGBZ of ImZTC kan eenvoudig vertaald worden naar een REST API door een hyperlink op te nemen in de resource dat meerdere keren kan voorkomen in de relatie, B in dit geval. Het veld met de hyperlink in resource B verwijst naar de gerelateerde resource A.
Dit is het principe van ‘linked data’.
Bijvoorbeeld in het RGBZ kan een zaak kan nul of meer statussen hebben en een status kan niet bestaan zonder een zaak, oftewel er is sprake van een 1-op-N relatie tussen Zaak en Status. De resource Status bevat naast zijn eigen gegevens het (toegevoegde) veld "zaak" dat de url naar de resource van de gerelateerde zaak bevat.
Opmerking: We gaan er gemakshalve vanuit dat 0-op-N of 1-op-N relaties geen eigen gegevens hebben. In het RGBZ komt dit in ieder geval niet voor.
Wanneer de gerelateerde resource één keer kan voorkomen wordt de resourcenaam omgezet naar enkelvoud. Wanneer de relatie meerdere keren kan voorkomen, wordt de resourcenaam in meervoud gebruikt.
Bij gebruik van de resourcenaam als propertynaam wordt lowerCamelCase toegepast (zie DD1.2).
Wanneer de resourcenaam niet voldoende beschrijvend is voor de betekenis van de relatie ("adres" is "verblijfplaats" van een persoon), of wanneer de resourcenaam niet onderscheidend is (ouders, partners en kinderen zijn allen relaties naar ingeschrevenpersonen) kan gekozen worden om bijvoorbeeld de relatienaam te gebruiken, of bijvoorbeeld de relatienaam gevolgd door de resourcenaam. Zo nodig wordt dit ingekort of aangepast om tot een bondige en duidelijke naam te komen.
Bijvoorbeeld:
woonplaats (resource "woonplaatsen")
openbareRuimte (resource "openbareruimten")
verblijfplaats (resource "adressen")
kinderen (resource "ingeschrevenpersonen" en relatie "heeft kinderen")
We nemen geen waarden op met een speciale betekenis die afwijkt van de normale betekenis van het gegeven.
bijvoorbeeld datum "0000-00-00" om aan te geven dat een datum onbekend is
bijvoorbeeld landcode "0000" om aan te geven dat het land onbekend is
Ratio: Als er informatie beschikbaar is moet die als zondanig onderkend en gemodelleerd worden.
Deze beslissing komt voort uit issue #166 en is bijgesteld op basis van user story issue #1677 n.a.v. user story issue #979.
Tussen twee componenten A en B (met component A de component waar in het IM de relatie naar component B loopt), wordt de relatie in beide componenten bewaard. Eventuele metagegevens (extra informatie op de relatie) komt in component A te liggen, en component B bevat enkel de relatieinformatie zelf.
Dit is een technische oplossing zodat beide componenten de relaties kunnen opvragen zonder alle (mogelijks honderden) componenten af te moeten lopen om te vragen of ze een relatie hebben.
Een consumer maakt 1x een relatie aan, tussen component A en B, met metagegevens over de relatie in component A. Component A is vervolgens verantwoordelijk om de relatie in component B aan te maken.
We stemmen de objectTypes af zodat die mappen op de namen van resources om generieke synchronisatieimplementaties mogelijk te maken.
Component A moet aan de volgende spelregels voldoen:
de naam van de resource is {resourceA}{resourceB}, in component A
de resource accepteert een URL-veld met de naam resourceB
Component B moet dan aan de volgende spelregels voldoen:
de naam van de resource is object{resourceB} in component B
de resource accepteert een URL-veld met de naam object
de resource heeft een enumeratie objectType met een waarde voor resourceA
Deze spelregels en interactie worden actief getest in de integratietests om compliancy af te kunnen dwingen.
Een concreet voorbeeld hiervan is een INFORMATIEOBJECT in het DRC en een ZAAK in het ZRC:
De consumer maakt in het ZRC een relatie (met polymorfe relatieinformatie):
POST https://zrc.nl/api/v1/zaakinformatieobjecten
{
"informatieobject": "https://drc.nl/api/v1/enkelvoudigeinformatieobjecten/1234",
"zaak": "https://zrc.nl/api/v1/zaken/456789",
"titel": "",
"beschrijving": "",
"registratiedatum": "2018-09-19T17:57:08+0200"
}
Het ZRC doet vervolgens een request naar het DRC (op basis van de URL van object):
POST https://drc.nl/api/v1/objectinformatieobjecten
{
"informatieobject": "https://drc.nl/api/v1/enkelvoudigeinformatieobjecten/1234",
"object": "https://zrc.nl/api/v1/zaken/456789",
"objectType": "zaak",
}
Wanneer de resourcenaam niet voldoende beschrijvend is voor de betekenis van de relatie ("adres" is "verblijfplaats" van een persoon), of wanneer de resourcenaam niet onderscheidend is (ouders, partners en kinderen zijn allen relaties naar ingeschrevenpersonen) kan gekozen worden om bijvoorbeeld de relatienaam te gebruiken, of bijvoorbeeld de relatienaam gevolgd door de resourcenaam. Zo nodig wordt dit ingekort of aangepast om tot een bondige en duidelijke naam te komen. Bijvoorbeeld: woonplaats (resource "woonplaatsen") openbareRuimte (resource "openbareruimten") verblijfplaats (resource "adressen") kinderen (resource "ingeschrevenpersonen" en relatie "heeft kinderen")
Wanneer in een API (comfort)gegevens worden opgenomen die authentiek zijn opgeslagen in een andere bron, worden deze bij voorkeur gemodelleerd op dezelfde manier als in die bron. Wanneer mogelijk wordt hergebruik gemaakt van de API specificatie van die andere bron (via $ref).
Bijvoorbeeld het adres (BAG) van een ingeschreven persoon (BRP) of vestiging (HR). Bijvoorbeeld de naam en geboortedatum (BRP) van een eigenaar in BRK of functionaris in HR.
Vanuit andere registraties bestaan er relaties naar ingeschreven natuurlijk personen. Een persoon kan bijvoorbeeld zakelijk gerechtigde zijn van een Kadastraal object of functionaris zijn van een bedrijf. Deze inverse relaties uit een andere bron worden niet opgenomen bij de ingeschreven natuurlijk persoon. Wanneer er functionele behoefte is aan deze gegevens moeten deze bij de betreffende bron (bijvoorbeeld BRK of HR) worden opgevraagd.
Ratio : We bevragen gegevens bij de bron. Een bron kan alleen gegevens leveren die ze zelf heeft.
Bij verschillende operaties zijn verschillende foutcodes van toepassing. Zo zal bij het bevragen van een collectie geen 404 optreden als er geen resultaat is, maar wordt er een lege collectie geretourneerd. Dan hoeft de 404 foutcode ook niet gespecificeerd te worden. Als "best practice" worden de volgende foutcodes bij per operatie gedefinieerd.
Bij een get operatie, zonder pad-parameter in de url, die een array als response oplevert:
Bij een get operatie waarbij een pad-parameter in de url is opgenomen:
Als voor een einddatum geen functioneel duidende naam is (bv. datumOntbindingHuwelijk) neem dan voor einddatums altijd expliciet in de naam de string "tot" of "totEnMet" op.
Ratio : Het is niet eenduidig of een einddatum een "tot"-datum is of een "totEnMet"-datum is. Dat is afhankelijk van de functionele context. Door deze postfix te gebruiken maak je het expliciet.
Voeg in common/Generieke-Datatypen-Gemeenten.yaml de datatypen
toe.
Check daarnaast of het datatype 'DatumOnvolledig' de datatypes 'DMO' en 'DTMO' helemaal afdekt. Voeg ook deze toe indien dit niet het geval is.
Binnen Haal Centraal passen we op de meeste repositories een aantal actions toe:
Het lijkt mij goed om deze actions in principe en mogelijk in aangepaste vorm ook toe te passen op andere repositories van VNG-R waarbinnen een openapi.yaml te vinden is.
In principe omdat er natuurlijk altijd redenen te bedenken zijn waarom je een action juist niet wil toepassen. In aangepaste vorm omdat er binnen Haal Centraal natuurlijk wel een paar ontwerp-keuzes zijn gemaakt die niet voor alle andere repositories gelden waardoor de validatie regels zoals die voor Haal Centraal gelden niet 1 op 1 ook voor de andere repositories gelden.
Overigens kan dat laatste natuurlijk ook tussen de andere repositories onderling spelen.
Voor een goede inrichting van deze validatie regels stel ik de volgende structuur voor:
Meer informatie over de werking van de in dit issue bedoelde actions vind je binnen de daarvoor bedoelde folder in deze kennisbank.
We vermijden het gebruik van afkortingen in propertynamen. Propertynamen moeten zoveel mogelijk zelfverklarend zijn.
Reactie op: 156b9c9#diff-0384e80de7a6ad576d2a7c8fd97d8872R48-R54
Polymorphisme
M.b.t. Polymorphisme bestaat er een verschil in aanpak tussen de projecten.
Project Beargumentatie ZGW Binnen de ZGW API's wordt polymorphisme vermeden wat wordt doorgetrokken naar > het gegevensmodel. De attributen in een superclass worden opgenomen in de subclasses en de superclass wordt in het gegevensmodel verwijderd. De beargumentatie voor deze keuze is dat polymorphisme de complexiteit verhoogt. HaalCentraal Binnen HaalCentraal is het gebruikt van superclasses een common practice o.a. omdat daarmee de werkelijkheid beter weergegeven kan worden.
Het stuk over polymorphisme vind ik wat vaag en het lijkt incorrect. Ik denk dat het ook geen stukje op zichzelf moet zijn maar als een keuze voor een bepaald probleem, namelijk:
Een supertype (IM-term) of abstracte klasse (dev-term, deze gebruik ik even) kan op verschillende manieren worden vertaald naar een API-specificatie in termen van resources. Neem als voorbeeld Persoon
als abstracte klasse en Docent
en Student
als concrete klasses die overerven van Persoon
(het zijn immers beide personen maar gedeelde en specifieke attributen). Je kan nu 3 dingen doen:
Persoon
, Docent
en Student
. Je kan nooit een Persoon
alleen aanmaken, maar als je een Student
aanmaakt, moet je ook een Persoon
aanmaken (en deze koppelen).Student
en Docent
op als concrete resources. Deze resources bevatten beide de attributen uit Persoon
.Persoon
op als resource, en op basis van een type
-attribuut (discriminator met de keuzes student
en docent
) wordt bepaald wat de specifieke attributen er in deze resource zitten.(bovenstaande ook als PR ingestuurd)
Gebruik polymorphisme in ZGW
ZGW gebruikt polymorphisme vooral voor de verwijzing naar een externe API/object waarbij het object kan verschillen (bijv. zaakobjecten of betrokkenen). Hierdoor krijg je 3 attributen:
objectUrl
-- link naar het object in de andere API (leeg indien er geen API beschikbaar is voor dit object)objectType
-- type van het object (tevens discriminator)objectIdentificatie
-- op basis van objectType
is dit een polymorpisch attribuut waar de gegevens in kunnen worden opgenomen.Verder ben ik van mening dat polymorphisme (optie 3) vooral complexiteit en onvoorspelbaarheid met zich meeneemt. Je verwacht typisch dat een object er altijd hetzelfde uitziet, maar opeens introduceer je variabele attributen. Dat is lastig en daarom gebruiken we het verder niet vaak. Volgens mij HaalCentraal ook niet. Dat staat dan niet goed in je tutorial
Optie 1 ben ik geen fan van omdat je dan 2 calls nodig hebt voor het aanmaken van 1 object. Het is als het ware een samengesteld object.
Optie 2 blijft het eenvoudigst in alle gevallen. Echter, als er (m2m/o2m) relaties zijn met de abstracte klasse
en dus per definitie ook met de concrete klasses
dan kan dit leiden tot veel extra resources om deze relatie vorm te geven (net zoals in databases).
Een N-op-M relatie tussen objecttypen A en B wordt vertaald als een nieuwe resource R die fungeert als een kruistabel zoals in databases.
De resource R bevat de volgende velden:
Het voordeel om een N-op-M relatie als resource te vertalen is dat je POST en DELETE operaties kunt uitvoeren om relaties toe te voegen of te verwijderen. Als je dat niet doet en je de relaties bijvoorbeeld bijhoudt bij één of beide gerelateerde resources, dan belandt je al snel in een minder handige situatie waarbij je de PATCH (of PUT)-operaties moet gebruiken om het lijstje met hyperlinks naar de gerelateerde resources bij te werken.
Bijvoorbeeld in het RGBZ is de relatie tussen Zaak en Informatieobject N-op-M. Deze relatie is vertaald naar de resource ZaakInformatieObject met de volgende velden:
In geval van de ZGW API’s is de relatie tussen zaken en informatieobjecten gedistribueerd over meerdere ZRC- en DRC-instanties en daarmee een speciaal geval. Zie Many-to-many relaties verspreid over API’s (#64) voor de designkeuzes hierbij.
Wanneer de gerelateerde resource één keer kan voorkomen wordt de resourcenaam omgezet naar enkelvoud. Wanneer de relatie meerdere keren kan voorkomen, wordt de resourcenaam in meervoud gebruikt.
(Deze issue is gemaakt n.a.v. het opsplitsen van #36
Voor developers die geen HAL links willen gebruiken wordt tevens de identificatie van de gerelateerde resource opgenomen in de content van de opgevraagde resource.
N.a.v. het toepassen van /components/requestBodies in de Logging-API ben ik er nog even ingedoken omdat ik bang was dat ik het in Open RaadsInformatie fout gedaan had.
Het valt mee. Ik heb het anders gedaan, maar dat is ook een valide werkwijze.
Ik schets even de situatie ter leering ende vermaeck
Keuze in Logging API was de volgende (zeer ingekort voor de leesbaarheid.
paths:
/verwerkingsacties
post:
tags:
- verwerkingsacties
summary: Maak een VERWERKINGSACTIE aan.
operationId: verwerkingsactie_create
description: Maak een VERWERKINGSACTIE aan.
requestBody:
$ref: '#/components/requestBodies/Verwerkingsactie'
components:
requestBodies:
Verwerkingsactie:
content:
application/json:
schema:
$ref: '#/components/schemas/Verwerkingsactie'
required: true
schemas:
Verwerkingsactie:
title: Verwerkingsactie
description: Een verwerkingsactie is een operatie die wordt uitgevoerd door een geautomatiseerd systeem waarbij er (persoons)gegevens verwerkt worden. Een verwerkingsactie wordt uitgevoerd als onderdeel van (een handeling van) een verwerking.
type: object
properties:
url:
description: URL-referentie naar deze resource. Deze link wordt automatisch aangemaakt door het log.
type: string
actieId:
type: string
In Open RaadsInformatie heb ik de requestbody op de volgende manier gebruikt.
paths:
/agendapunten
post:
operationId: PostAgendapunt
summary: Opslaan agendapunt
tags:
- Agendapunten
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Agendapunt'
required: true
components:
schemas:
Agendapunt:
type: "object"
description: |
Een onderdeel van de agenda dat als één geheel wordt behandeld. Een AGENDAPUNT kan weer onderverdeeld zijn naar subAGENDAPUNTen.
agendapuntKenmerk:
type: "string"
agendapuntnummer:
type: "string"
Misschien moeten we hier ook een check op doen wat dit voor code-generatie betekent. Mijn vermoeden is dat de manier waarop jij dit hebt opgelost vriendelijker is voor code-generatoren dan hoe ik het heb opgelost.
De naam van een pull request moet voldoen aan de volgende conventie:
fixit [nummer]
nummer is daarbij het nummer van het issue waar het pull request invulling aan geeft.
Als queryparameters voor het historisch opvragen gebruiken we "peildatum", "datumVan" en "datumTotEnMet"
We gebruiken geen oneOf of anyOf constructies voor polymorfe gegevens.
Alhoewel de oneOf constructie een valide OAS3 constructie is levert deze bij het genereren van code problemen op. Deze constructie wordt ontweken door twee mogelijke alternatieven.
De subtypes worden samengevoegd tot 1 object. Deze keuze is logisch als de subtypes grotendeels overlappen en er geen strijdigheid is tussen de verschillende mogelijke types.
De subtypes worden volledig opgenomen als property van een object, met daarin de eigenschappen van dat type. In het response is altijd maar 1 van deze properties gevuld. Deze keuze is logisch als de subtypes weing gemeenschappelijke properties hebben.
In beide gevallen nemen we ook een type property (discriminator) op waaruit de gebruiker kan opmaken welk type het betreft.
Ratio: Diverse code-generatoren gaan dus niet goed om met deze constructie en genereren foutieve code.
Kanttekening : Mochten code-generatoren in de toekomst wel goed met deze constructie om kunnen gaan dan is het het overwegen waard om deze constructie aan te passen bij de eerstvolgende major (breaking) change.
In github_tutorial.md wordt in de uitleg van de term branch de opmerking
In Git worden branches dus op een andere manier gebruikt dan in SVN
geplaatst. Dit klopt echter niet. Ook in SVN kan het de bedoeling zijn een branch weer te mergen met de trunk. Dat we dit niet doen i.h.k.v. de Nieuwe aanpak heeft zo zijn reden.
Ook wil ik een aantal andere kleine tekstuele correcties aanbrengen.
Wanneer er meerdere componenten zijn met meerdere dezelfde properties, moet er hergebruik worden gemaakt via een 'allOf' constructie. Dit sluit aan op object oriëntatie in de verschillende programmeeromgevingen.
Bijvoorbeeld een woonadres en een postadres zijn identiek, behalve dat postadres ook een postbusnummer kent. Dan is postadres een extensie op woonadres.
Bijvoorbeeld een natuurlijk persoon en een niet-natuurlijk persoon hebben beide een naam en een adres, maar beide hebben ook eigen gegevens (natuurlijk persoon heeft geboortedatum, niet-natuurlijk persoon heeft eigenaar), dan zijn beide een extensie op een bovenliggende component "Persoon".
Bijvoorbeeld bij een zakelijkGerechtigde worden alleen minimale identificerende gegevens van een persoon opgenomen (alleen naam en identificatie), maar bij de persoon (resource) worden meer eigenschappen van de persoon opgenomen (zoals adres). Dan gebruikt zakelijkGerechtigde het component "persoonBeperkt" en is de uitgebreide persoon een extensie hierop.
Noot: Onderstaande tekst aangepast n.a.v. opmerking RM :
Wanneer een gegeven in het informatiemodel gedefinieerd is als een enumeratie, maar de enumeratiewaarde wordt door gebruikers van de API alleen gebruikt om als tekst te tonen (bevraging-endpoints) aan eindgebruikers (mensen), definieer het gegeven dan als string (niet als enumeratie) in de API.
Wanneer de enumeratiewaarde gebruikt wordt in code (algoritmes), definieer dan een betekenisvolle maar bondige code.
Ratio : Het opnemen van een enumeratie in de API-definitie betekent dat voor het kunnen tonen van een nieuwe of gewijzigde waarde in de enumeratie een nieuwe versie van de API moet worden uitgebracht. Voor bevraging-API's is dat een ongewenste vorm van tight coupling
.
Als het gegeven echter wordt gebruikt in code (algoritme) dan is het van belang dat er niet afgeweken kan worden van de bekende waarden en is een definitie als enumeratie op zijn plaats.
Wanneer het gevuld zijn van een datum functionele betekenis heeft, ook wanneer deze volledig onbekend is, wordt een indicator opgenomen om dit aan te geven.
Indien in het onderstaande voorbeeld er helemaal niets bekend is van de overlijdensdatum, maar er is wel bekend dat de persoon overleden is, dan kan dat nergens uit worden afgeleid. Uit de reponse kan je niet opmaken of de overlijdensdatum leeg is omdat iemand niet is overleden of omdat de overlijdensdatum niet bekend is.
Persoon:
type: object
description: "Natuurlijkpersoon of NietNatuurlijkPersoon"
properties:
identificatie:
type: string
datumOverleden:
$ref: "#/components/schemas/TypePersoonEnum"type: string
voorletters:
type: string
Door voor een dergelijke situatie een boolean op te nemen waar de betreffende informatie wordt gerepresenteerd wordt duidelijk wat de situatie is zonder dat er invalide waardes hoeven te worden opgenomen in de datum-velden.
Persoon:
type: object
description: "Natuurlijkpersoon of NietNatuurlijkPersoon"
properties:
identificatie:
type: string
datumOverleden:
$ref: "#/components/schemas/TypePersoonEnum"type: string
indicatieOverleden:
type: boolean
voorletters:
type: string
Ratio : Bijvoorbeeld of een persoon overleden is kan niet worden afgeleid uit het bestaan van een overlijdensdatum wanneer die datum onbekend is. Daarvoor kan een boolean "indicatieOverleden" worden gedefinieerd.
Eigenschappen die functioneel alleen de waarde Ja/aan/waar of Nee/uit/onwaar kunnen hebben, worden gedefinieerd als boolean. We gebruiken dus geen enumeratie zoals [J,N] voor dit soort situaties.
Als er tussen twee componenten (A en B) in 2 verschillende API-specificaties een relatie kan bestaan (waarbij component A het component is van waaruit in het IM de relatie naar component B loopt), wordt het vastleggen van die relatie in beide componenten mogelijk gemaakt. Eventuele metagegevens (extra informatie op de relatie) worden daarbij bij component A gespecificeerd terwijl bij component B enkel de informatie over de relatie zelf wordt gespecificeerd.
Ratio: Met deze technische oplossing wordt voorkomen dat voor het achterhalen van de relaties die een component heeft alle (mogelijk honderden) componenten in een andere API moeten worden afgelopen.
Een consumer maakt 1x een relatie aan, tussen component A en B, met de metagegevens over de relatie in component A. component A is vervolgens verantwoordelijk om de relatie in component B, en dus in een andere API, aan te maken.
We stemmen de objectTypes af zodat die mappen op de namen van resources om generieke synchronisatieimplementaties mogelijk te maken.
Component A moet aan de volgende spelregels voldoen:
Component B moet dan aan de volgende spelregels voldoen:
Deze spelregels en interactie worden actief getest in de integratietests om compliancy af te kunnen dwingen.
Een concreet voorbeeld hiervan is een INFORMATIEOBJECT in het DRC en een ZAAK in het ZRC:
De consumer maakt in het ZRC een relatie (met polymorfe relatieinformatie):
POST https://zrc.nl/api/v1/zaakinformatieobjecten
{
"informatieobject": "https://drc.nl/api/v1/enkelvoudigeinformatieobjecten/1234",
"zaak": "https://zrc.nl/api/v1/zaken/456789",
"titel": "",
"beschrijving": "",
"registratiedatum": "2018-09-19T17:57:08+0200"
}
Het ZRC doet vervolgens een request naar het DRC (op basis van de URL van object):
POST https://drc.nl/api/v1/objectinformatieobjecten
{
"informatieobject": "https://drc.nl/api/v1/enkelvoudigeinformatieobjecten/1234",
"object": "https://zrc.nl/api/v1/zaken/456789",
"objectType": "zaak",
}
Prefixes die slaan op de eigen resource, worden niet gebruikt. Voorbeeld: Attribuutsoort informatieobjecttype.informatieobjecttype-omschrijving in RGBZ2 krijgt de naam “omschrijving” in de API, aangezien het een veld is binnen de resource informatieobjecttype. Dit komt overeen met de in RGBZ genoemde XML-tag.
{
"url": "https://example.com/drc/api/v1/informatieobjecttypen/8534ba6a-bcde-4387-b054-6bde6d1ded8f",
"omschrijving": "Document"
// ...
}
Ratio:
Voor developers zijn kortere namen binnen een object prettiger en overzichtelijker.
We nemen bij een property een description op die semantisch overeenkomt met de beschrijving in het gegevenswoordenboek. Deze kan ingekort, vereenvoudigd, of uitgebreid zijn, maar mag de betekenis van het gegeven niet laten afwijken van de betekenis van het corresponderende gegeven in het gegevenswoordenboek.
De description kan worden weggelaten wanneer evident is dat de gebruikers van de API uit de propertynaam weten wat bedoeld wordt (bijvoorbeeld huisnummer).
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.