Opsætning og brug af REST API

Formål

Denne vejledning henvender sig til eksterne parter, der ønsker at anvende Plandata.dk's REST-snitflade for system-til-system-indberetning af planer til Plandata.dk.

Det eksterne REST API er gjort tilgængeligt i produktionsmiljøet i januar 2024, og det er udviklet til at erstatte den ældre SOAP-service, som planlægges udfaset i løbet af 2024.

I udviklingen af REST-snitfladen har der været særligt fokus på følgende forbedringer ift. den ældre SOAP-snitflade.

  • Validering: Alle kald til REST API'et der ændrer på en plan, bliver tjekket op imod det samme sæt af valideringsregler, som anvendes ved indberetning igennem Plandata.dk's web-baserede brugerflade (https://indberet.plandata.dk). Dette sikrer, at succesfuld indberetning af planer igennem REST API'et ikke vil udløse fejl, når de samme planer efterfølgende tilgås i bl.a. web-brugerfladen, og der sikres desuden en højere datakvalitet.
  • Autentificering og Rollestyring: Plandata.dk's REST API bruger en moderne JWT-baseret autentificeringsmetode, som sikrer at kommunale plandata-indberettere kun har adgang til at ændre i planer i deres egen kommune. Authentificeringen af en brugers adgang til REST API'et forholder sig desuden til samme MitID-rettigheder, som den web-baserede brugerflade, hvilket betyder, at enhver bruger med adgang til web-brugerfladen vil have en tilsvarende adgang igennem REST API'et og omvendt.

Miljøer

 

PREPROD

https://indberetpreprod.plandata.dk/plandata-api

Testmiljø der benytter sig af NemLog-ins Devtest4 miljø, og er infrastrukturmæssig en spejlning af produktionsmiljøet.

Kan anvendes til ekstern test.

PROD

https://indberet.plandata.dk/plandata-api

Produktionsmiljø

Anvendes til indberetning af planer.

Autentificering

Både ift. at tilgå REST API'et i produktionsmiljøet og i pre-produktionsmiljøet (vores eksternt tilgængelige testmiljø), skal der logges ind med en bruger, som kan autentificeres i Digitaliseringsstyrelsens system for NemLog-in. Med en autentificeret bruger kan der hentes en access token, som giver en tidsbegrænset adgang for at indberetning til Plandata.dk.

PRODUKTION

For at anvende Plandata REST API i produktionsmiljøet, skal man anvende et MitID Erhverv fra den relevante kommune, som har tilknyttet de nødvendige rettigheder. Det er samme rettigheder, som anvendes til indberetning via Plandata.dk's egen web-brugerflade, https://indberet.plandata.dk.

PRE-PRODUKTION

For at anvende Plandata.dk's REST API i pre-produktionsmiljøet skal der oprettes en testbruger i Digitaliseringsstyrelsens system. Der findes flere muligheder for, hvordan test-MitID'et kan sættes op. Disse muligheder gennemgås i næste afsnit.

Det anbefales, at pre-produktionsmiljøet anvendes til at afprøve tredjepartsløsningers brug af REST API'et, inden der indberettes til produktionsmiljøet. På testmiljøet er der som udgangspunkt ikke den samme kommune-specifikke begrænsning ift. adgang til indberetning af planer.

Administrering af testbrugere i PREPROD

For at bruge Plandata REST API i pre-produktionsmiljøet findes der 3 muligheder for at få adgang til en testbruger:

  1. Henvend jer til Plandata-supporten mhp. at vi opretter en testbruger til jer, som tilknyttes vores Plandata's egen test-brugerorganisation.
    1. Denne testbruger vil få privilegier tilknyttet et test-CVR, som er whitelistet i vores system. Dette gør at brugeren har adgang til alle kommuner og giver mulighed for at komme hurtigt igang med at teste
    2. Denne mulighed reflekterer ikke til fulde, hvordan brugerorganisation og privileger for indberettere vil se ud i Plandata REST API i produktionsmiljøet, fordi der i produktion ikke vil være adgang til alle kommuner. Hvis opsætning fsva. adgangen til specifikke kommuner skal afspejle produktionsmiljøet, anbefales i stedet opsætning i Mulighed 3.
  1. Lav selv jeres egen test-brugerorganisation og derefter testbrugere i Digitaliseringsstyrelsens "devtest4-miljø" for NemLog-in.
    1. En ny brugerorganisation oprettes her: https://testportal.test-devtest4-nemlog-in.dk/BO. Når brugerorganisationen er oprettet, skal I henvende jer til Plandata-supporten mhp. at få whitelistet test-brugerorganisationens CVR-nr, således der åbnes for indberetning til Plandata.dk.
    2. Første led i opsætningen af denne mulighed tager længere tid end Mulighed 1, men derfra får I selv fuld kontrol over, hvilke brugere der tilknyttes jeres test-organisation.
    3. Ligesom for Mulighed 1, så kan Mulighed 2 give adgang til alle kommuner, hvorved den bliver mere fleksibel til testformål, men til gengæld ikke til fulde afspejler opsætningen, som den vil se ud i produktionsmiljøet. Hvis opsætning fsva. adgangen til specifikke kommuner skal afspejle produktionsmiljøet, anbefales i stedet opsætning i Mulighed 3.
  1. Anmod om, at NemLog-in-administratoren for en specifik kommune opretter testorganisation og testbrugere til jer i Digitaliseringsstyrelsens "devtest4-miljø". Denne bruger kan derved få privilegier tilknyttet den specifikke kommune. Dette reflekterer opsætningen og proces ift. hvordan det vil fungere i PROD, men placerer til gengæld opgaven for oprettelse og vedligehold af testbrugere hos de enkelte kommuner.

Hentning af token

I det følgende beskrives det, hvordan en bruger autentificerer sig mod Token Service for at hente en access token i forskellige plandata miljøer. Denne access token bruges som autentificering ved kald til Plandata REST API. En access token er gyldig i 1 time, hvorefter fortsat indberetningsarbejde vil kræve fornyelse af token.

PREPROD

  1. Tilgå OpenID Connect (OIDC) siden for PREPROD på https://indberetpreprod.plandata.dk/plandata-api/oidcClient/index
  2. I toppen, klik "Login/forny session":
  3. Brugeren bliver redirected til NemLog-in.
  4. I toppen, vælg "Test login":
  5. Udfyld med brugernavn og adgangskode for jeres testbrugere i devtest4 (se afsnit Administrering af testbrugere i PREPROD).
  6. Brugeren bliver herefter viderestillet tilbage til OIDC-siden (se længere nede).

PROD

  1. Tilgå OpenID Connect (OIDC) siden for PROD på https://indberet.plandata.dk/plandata-api/oidcClient/index
    1. Denne side er pt. kun åben for interne adresser.
  2. I toppen, klik "Login/forny session":
  3. Brugeren bliver viderestillet til MitID, hvor brugeren skal logge ind med sit certifikat.
  4. Brugeren bliver herefter viderestillet tilbage til OIDC-siden (se længere nede).

OIDC siden efter autentificering

Hvis alt er gået godt så skal teksten efter status være: "Bruger er logget ind". I toppen vises også JSON data for brugerens profil, inklusive brugerens navn og en liste af privilegier (hvis brugeren har nogle tildelt):

Brugerens access token kan nu hentes ved at klikke på Vis access token:

Brug af token

For at autentificere kald til Plandata REST API, skal 'Authorization' header sættes til 'Bearer <token>', hvor <token> er access token kopieret fra OIDC klienten, f.eks:

{'Authorization': 'Bearer eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJBQUl...'}

Grundlæggende struktur i REST API

REST-snitfladen er dokumenteret ved brug af Swagger, som også giver mulighed for at afprøve hvert endpoint.

Der er separate endpoints for hver plantype for hhv. indberetning, opdatering, statusskifte, hent, valideringstjek m.m. I denne sammenhæng betragtes eksempelvis lokalplaner, delområder og byggefelter i teknisk forstand som forskellige, men relaterede plantyper.

Til forskel fra SOAP-servicen, så er det igennem REST API'et ikke muligt i ét kald at indberette en plan, der på forhånd er beriget med alle oplysninger og data. I stedet oprettes eksempelvis en lokalplan først som næsten "tom skal" i status "kladde til forslag", hvorefter den kan beriges med data. Når kladden er klar og overholder valideringskrav, så kan den i et separat kald skifte status til at blive et offentliggjort planforslag. Undervejs er det desuden muligt at foretage valideringstjek af sin plan, så man kan tjekke, om alt er, som det skal være uden at risikere, at en plan risikeres. Flowet er illusteret i figuren herunder.

Swagger - API Dokumentation

Swagger er et værktøj for automatisk at dokumentere alle endepunkter, deres input/outputs og forventede HTTP statuskoder i Plandata REST API. Nedenstående tabel samler links til vores swagger-sider i forskellige miljøer. Data kan enten vises i en brugervenlig grænseflade (Swagger UI) eller som JSON data.

Miljø

UI

JSON

PREPROD

https://indberetpreprod.plandata.dk/plandata-api/swagger/

https://indberetpreprod.plandata.dk/plandata-api/swagger/v2/swagger.json

PROD

https://indberet.plandata.dk/plandata-api/swagger/

https://indberet.plandata.dk/plandata-api/swagger/v2/swagger.json

API standarder

Nedenstående beskriver de standarder, vi overholder for endepunkterne. Dette kan også ses i Swagger.

HTTP Statuskoder

2xx

Kode

Betydning

Bruges til

200

OK

Succesfuldt kald der ikke passer ind i andre statusser, f.eks. hentning af data.

201

Created

Succesfuldt indberetning/oprettelse af data der resulterer i at ny data bliver oprettet.

204

No Content

Returneres når data slettes succesfuldt.

4xx

Kode

Betydning

Bruges til

400

Bad Request

Hvis input data ikke opfylder alle krav, f.eks. hvis et request mangler data, eller hvis planen ikke opfylder alle valideringsregler.

401

Unauthorized

Returneres når der mangler information for autentificering, f.eks. hvis headeren "Authorization" mangler.

403

Forbidden

Returneres når brugeren er korrekt autentificeret, men ikke har de nødvendige privilegier til at udføre den forsøgte handling.

5xx

Kode

Betydelse

Bruges til

503

Service Unavailable

Hvis forbindelsen til integrationslaget (eller anden kritisk integration) er nede. 

HTTP Metoder

Følgende tabel beskriver hvilke metoder som bruges i Plandata REST API, og hvornår de skal bruges. Dette fremgår også af Swagger-dokumentationen.

Metode

Bruges til

GET

Hentning af data

POST

Indberetning/Oprettelse af ny data

PUT

Opdatering af eksisterende data

DELETE

Sletning af data

Endepunktsmønster

Alle endepunkter skal følger nedenstående mønster:

/<ressourcetype>/<action>

  • <ressourcetype> skal erstattes med navnet for den ressource man vil tilgå, f.eks. lokalplanlandzonetilladelse eller hoeringspart
  • <action> skal erstattes med den action man vil udføre, f.eks. hentLokalplanopretHoeringspart eller sletByggefelt.

Inputformat

Input data skal vare i JSON/GeoJSON-format.

Geometri

Geometri kan angives for en plan via felterne geometri og projection.

Feltet projection specificerer den projection som geometriens koordinater er specifierede i, og skal altid være "EPSG:25832".

Feltet geometri specificerer koordinater og type af geometri i gyldigt GeoJSON-format, mere info her: https://en.wikipedia.org/wiki/GeoJSONTypen skal altid være "MultiPolygon". Nedenstående er et eksempel på en gyldig GeoJSON-streng:

{
    "geometri": "{\"type\":\"MultiPolygon\",\"coordinates\":[[[[724500.0,6169049.2],[725016.5,6171885.9],[725507.7,6171885.4],[725075.8,6169235.5],[724923.4,6169108.5],[724500.0,6169049.2]]]]}",
    "projection": "EPSG:25832"
}

Datoer

Alle datofelter skal følge ISO 8601 format, dvs. "yyyy-mm-dd". Eksempel:

{
    "aflysningsdato": "2020-01-01",
    "datoer": {
        "vedtagelsesdato": "2022-01-13"
    }
}

Outputformat

Output data returneres i JSON/GeoJSON-format. Hvis alt er vellykket returneres hvad der er blev oprettet, opdateret eller slettet af brugerens kald. Hvis noget gik galt, returneres et JSON-response med information om hvad der gik galt. Dataen skal have følgende struktur:

{
    "besked": "Noget gik galt...",
    "fejlType": "VALIDERINGSFEJL",
    "fejl": [
        "Parameteren 'plantype' er forkert..."
    ]
}

fejlType

Tilgængelige fejltyper:

Navn

Beskrivelse

VALIDERINGSFEJL

Noget i input data indeholder en eller flere constraint eller valideringsfejl.

DESERIALISERINGSFEJL

Input JSON-data kunne ikke læses til intern datamodel korrekt.

INTEGRATIONSFEJL

Noget i forbindelsen mellem komponenter gik galt.

PDK_FEJL

PDK (plan-backend) kastede en håndterbar fejl.

RESSOURCE_IKKE_FUNDET

Den efterspurgte ressource eksisterer ikke.

ADGANGSFEJL

Der mangler nødvendigt privilegie for at udføre handlingen.

ANDET

  • Øvrige fejl.

Eksempler

Hent Lokalplan

Eksempel på hent af en lokalplan, der er i forslags-status i Københavns Kommune (kommunekode 101). I eksemplet er det specificeret, at geometri skal ekskluderes mhp. at holde response mere overskueligt. Parameteren maxPageSize er også indstillet til at begrænse antallet af returnerede planer til 10.

curl --location 'http://s9internpreprod.nonprod.es.local/plandata-api/lokalplan/hent?planstatus=FORSLAG&kommunekode=101&maxPageSize=10&excludeColumns=geometri' \
--header 'Authorization: Bearer <token>'

Response

Status: 200 OK

[
    {
        "plannavn": "Testnavn",
        "planId": 11231635,
        "plantype": "LOKALPLAN",
        "planstatus": "FORSLAG",
        "plannummer": "123",
        "kommunekode": 101,
        "dokumentUrl": null,
        "geometri": null,
        "projection": "EPSG:25832",
        "aflysningsdato": null,
        "ikrafttraedelsesdato": null,
        "gyldighedsdatoFra": null,
        "gyldighedsdatoTil": null,
        "opdateringstidspunkt": "2023-03-02T04:28:53.896",
        "versionsnr": 1,
        "datoer": {
            "forslagsDato": "2023-03-02",
            "hoeringsperiodeStartDato": "2023-03-02",
            "hoeringsperiodeSlutDato": "2023-04-27",
            "oprindeligHoeringsperiodeSlutDato": "2023-04-27",
            "vedtagelsesdato": null,
            "indberetningsdato": null
        },
        "aktuel": true,
        "plandistrikt": "distrikt1",
        "regulering": {
            "zone": null,
            "zoneReguleresIkke": true,
            "anvendelseReguleresIkke": false,
            "kompleksplan": false,
            "kompleksplanBeskrivelse": null,
            "generelAnvendelseskategori": null,
            "specifikOmfangAnvendelser": [],
            "minTilladtMiljoeklasse": null,
            "maxTilladtMiljoeklasse": null,
            "bygningsbevaringsbestemmelser": {
                "bygningsbevaringsbestemmelser": false,
                "bygningsbevaringsbestemmelserFritekst": null
            },
            "skilteOgFacader": false,
            "megawatt": null,
            "vindmoelleBeskrivelse": null,
            "saerligeForhold": null,
            "saerligeForholdPaavirkerNedbrydning": false,
            "omfangsbestemmelser": [],
            "omfangsbestemmelserReguleresIkke": false,
            "udstykning": {
                "udstykningsbestemmelser": []
            },
            "udstykningReguleresIkke": false,
            "notat": {
                "generelleanvendelsesbestemmelser": null,
                "omraadetsAnvendelse": null,
                "bebyggelsensOmfangOgUdformning": null,
                "opholdsOgFriarealer": null,
                "miljoeforhold": null,
                "infrastruktur": null,
                "zonenotat": null,
                "lokalplanermmIndenforrammen": null,
                "notatgenerelt": null
            }
        },
        "tvaergaaendeBestemmelser": []
    },
    {
        ...
    },
    {
        ...
    }
]

Opret Lokalplan

Opret en ny Lokalplan i status Kladde til forslag i København (kommunekode 101). Her specificeres også plannummer og plannavn.

curl --location 'http://s9internpreprod.nonprod.es.local/plandata-api/lokalplan/opret' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <token>' \
--data '{
    "kommunekode": 101,
    "planstatus": "KLADDE_TIL_FORSLAG",
    "plannummer": "123-abc",
    "plannavn": "et fint navn"
}'

Response

Status: 201 CREATED

{
    "plannavn": "et fint navn",
    "planId": 11264604,
    "plantype": "LOKALPLAN",
    "planstatus": "KLADDE_TIL_FORSLAG",
    "plannummer": "123-abc",
    "kommunekode": 101,
    "dokumentUrl": null,
    "geometri": null,
    "projection": "EPSG:25832",
    "aflysningsdato": null,
    "ikrafttraedelsesdato": null,
    "gyldighedsdatoFra": null,
    "gyldighedsdatoTil": null,
    "opdateringstidspunkt": "2023-09-14T15:52:57.532",
    "versionsnr": 1,
    "datoer": {
        "forslagsDato": null,
        "hoeringsperiodeStartDato": null,
        "hoeringsperiodeSlutDato": null,
        "oprindeligHoeringsperiodeSlutDato": null,
        "vedtagelsesdato": null,
        "indberetningsdato": null
    },
    "aktuel": true,
    "plandistrikt": null,
    "regulering": {
        "zone": null,
        "zoneReguleresIkke": false,
        "anvendelseReguleresIkke": false,
        "kompleksplan": false,
        "kompleksplanBeskrivelse": null,
        "generelAnvendelseskategori": null,
        "specifikOmfangAnvendelser": [],
        "minTilladtMiljoeklasse": null,
        "maxTilladtMiljoeklasse": null,
        "bygningsbevaringsbestemmelser": {
            "bygningsbevaringsbestemmelser": false,
            "bygningsbevaringsbestemmelserFritekst": null
        },
        "skilteOgFacader": false,
        "megawatt": null,
        "vindmoelleBeskrivelse": null,
        "saerligeForhold": null,
        "saerligeForholdPaavirkerNedbrydning": false,
        "omfangsbestemmelser": [],
        "omfangsbestemmelserReguleresIkke": false,
        "udstykning": {
            "udstykningsbestemmelser": []
        },
        "udstykningReguleresIkke": false,
        "notat": {
            "generelleanvendelsesbestemmelser": null,
            "omraadetsAnvendelse": null,
            "bebyggelsensOmfangOgUdformning": null,
            "opholdsOgFriarealer": null,
            "miljoeforhold": null,
            "infrastruktur": null,
            "zonenotat": null,
            "lokalplanermmIndenforrammen": null,
            "notatgenerelt": null
        }
    },
    "tvaergaaendeBestemmelser": []
}