Het project is de OEAPI, een API structuur gebaseerd op REST die gebruikt wordt door instituten om op een universele manier informatie naar buiten te brengen.
De OU wilt dit implimenteren zodat sites zoals Studiekeuze123 actuele informatie van de OU op kan halen, zoals faculteiten en producten.
Eisen:
expand groter worden.Voor beveiliging worden JWT tokens gebruikt. Deze worden samen gebruikt met een secret key uit Azure. Deze secret wordt gebruikt om een nieuwe token op te kunnen halen, en het bepaald ook wat ze kunnen doen met de API. Bijvoorbeeld een Read token kan alleen maar dingen lezen, maar een ReadWrite kan lezen en schrijven.
Hiervoor wordt gebruik gemaakt van middleware. Dit is een tussenstukje wat altijd gebeurt voordat de call geaccepteerd wordt. Hier worden dingen zoals de token gecheckt voordat het bij de echte function komt.
Om te zorgen dat de OU aan de eisen van de OEAPI voldoet, moeten ook de goede foutmeldingen teruggegeven worden. Deze zijn in het volgende structuur:
{
"type": string <uri>,
"title": string,
"status": integer <int32>,
"detail": string or null,
"instance": string or null <uri>
}
De keys werken als volgt:
Mapping wordt gedaan in DevOps (voor nu, wordt misschien verandert naar Confluence).
Niet alle informatie kan ingevuld worden, maar de required informatie (naast de GUID, deze staat nog niet in de database) kan wel ingevuld worden. Voor mapping zelf wordt AutoMapper gebruikt.
Niet alle informatie hoeft naar buiten, dus ook niet alle endpoints zullen gebruikt worden. De endpoints die wel gebruikt worden zijn als volgt:
organisationType dus altijd faculty.programme---
config:
theme: 'dark'
---
sequenceDiagram
actor User
participant Backend@{"type": "boundary"}
participant PHOUNIX@{"type" : "database"}
Note over Backend,User: OEAPI architecture
critical Connect to DB
activate PHOUNIX
Backend->>PHOUNIX: Query data
option Success
PHOUNIX-->>Backend: Return data
deactivate PHOUNIX
option Failure
Backend-->>User: 500
end
critical Authorize
User->>Backend: JWT Token
Backend->>Backend: Check JWT
option Valid
Backend-->>User: Response
option Token invalid or null
Backend-->>User: 401
option Wrong role
Backend-->>User: 403
end
In het diagram hierboven zijn een aantal dingen te zien:
De user maakt een request via OEAPI. Het stuurt in de request Authorization header een JWT token mee. De backend zal deze bekijken. Als de token klopt en de rol van de token mag bij die endpoint, dan wordt alle benodigde informatie terug gestuurd.
Ook kan een specifieke versie van OEAPI of de consumer terug gevraagd worden. Dit gaat in de Content-Type header (GitHub).
Om de informatie op te kunnen halen, wordt er een query gestuurd naar de oracle database. Deze geeft de informatie terug op basis van de query message.
Binnen de periode van ~6 maanden zijn alle belangrijkste onderdelen en functies af binnnen de geplande tijd.
Eerst was ik gestart met de opzet van de OEAPI zelf. Dit was gedaan met een Dummy project die dan op de correcte manier vernoemt moet woorden naar OEAPI. Met dit dummy project komen pipelines, waarmee je de functions automatisch in Azure kunt zetten. Dit was ook mooi binnen de tijd gedaan, kostte maar 1 dag.
Daarna komt de API zelf. We waren eerst gestart met OEAPI versie 5. Ik had op dit moment nog geen connectie aan de databases die we gingen gebruiken, maar ik heb even rond gespeeld met de mapping om er een gevoel voor te krijgen.
Na een paar dagen waren we begonnen met het echte mappen, en om te bespreken welke database waar het beste zou passen. Ik heb hierbij een generiek object gemaakt om een connectie te maken aan elke database die we gingen gebruiken. Later (als mijn stage al is afgerond) wordt dit ook omgezet naar een centrale database, maar deze is op dit moment nog niet af.
Tijdens deze fase werd ook nog besproken of we nou versie 5 of versie 6 gingen gebruiken. versie 6 was op dit moment namelijk nog in beta, en we wisten niet zeker of het slim was om meteen naar een beta versie te gaan. Toch hebben we besloten om naar versie 6 te gaan, en dit bleek een goede keuze te zijn.
Ik ben in de volgende paar maanden bezig geweest met de echte OOAPI. Ik heb binnen de afgesproken tijden altijd alles afgekregen. Je kunt met de API de programmes, courses en organisation ophalen (ook de objecten die nested zijn). Ook is dit allemaal beveiligd met een JWT token en een secret die aangemaakt kan worden in Azure.
De functionaliteiten voldoen aan de eisen die gesteld werden.
Beveiliging is gedaan door middel van JWT tokens en secret keys.

De teruggave voor elk endpoint klopt. Bijvoorbeeld een 401 omdat de pagesize niet klopte:
{
"type": "https://api.example.org/problems/invalid-parameter",
"title": "Invalid request parameters",
"status": 400,
"detail": "The query parameter 'pageSize' must be one of: 10, 20, 50, 100, 250.",
"instance": "http://localhost:7033/api/courses?pageSize=11&pageNumber=1&expand=organisation"
}
Of een 200 als je de courses ophaalt (zonder expand parameter, maar 1 object voor leesbaarheid.):
{
"pageSize": 10,
"pageNumber": 1,
"hasPreviousPage": false,
"hasNextPage": true,
"totalPages": 338,
"items": [
{
"courseId": "ed53c788-73de-4154-8826-5af7060ae90b",
"primaryCode": {
"codeType": "product_id",
"code": "GM0502"
},
"name": [
{
"language": "nl",
"value": " Kwaliteit, beleid en governance in de gezondheidszorg"
}
],
"description": [
{
"language": "nl",
"value": " Kwaliteit, beleid en governance in de gezondheidszorg"
}
],
"studyLoad": {
"studyLoadUnit": "ects",
"value": null
},
"duration": null,
"level": "0",
"organisationId": "081c074a-ad6b-4eb7-8166-3205e9717934",
"validFrom": "2022-05-16T00:00:00",
"validTo": "0001-01-01T00:00:00"
},
]
En dan ook met de expand:
{
"pageSize": 10,
"pageNumber": 1,
"hasPreviousPage": false,
"hasNextPage": true,
"totalPages": 338,
"items": [
{
"courseId": "ed53c788-73de-4154-8826-5af7060ae90b",
"primaryCode": {
"codeType": "product_id",
"code": "GM0502"
},
"name": [
{
"language": "nl",
"value": " Kwaliteit, beleid en governance in de gezondheidszorg"
}
],
"description": [
{
"language": "nl",
"value": " Kwaliteit, beleid en governance in de gezondheidszorg"
}
],
"studyLoad": {
"studyLoadUnit": "ects",
"value": null
},
"duration": null,
"level": "0",
"organisation": {
"organisationId": "081c074a-ad6b-4eb7-8166-3205e9717934",
"primaryCode": {
"codeType": "organisation_id",
"code": "19"
},
"organisationType": "faculty",
"name": [
{
"language": "nl",
"value": "Psychologie"
}
],
"shortName": "Psychologie"
},
"organisationId": "081c074a-ad6b-4eb7-8166-3205e9717934",
"validFrom": "2022-05-16T00:00:00",
"validTo": "0001-01-01T00:00:00"
},
]

De correcte endpoints (en alleen de endpoints die we gaan gebruiken) staan er in, behalve de offering objecten omdat deze nog gemapt moeten worden.

De code bestaat grotendeels uit generieke objecten. Dit betekent dat er weinig of geen dubbele code is. Producten is hierbij een goed voorbeeld. Omdat programmes en courses eigenlijk hetzelfde zijn in de database (allebij producten met een andere type), heb ik een generiek object voor deze gemaakt, namelijk ProductService.
Normaal gesproken zou een endpoint er ongeveer zo uitzien (simpel gemaakt voor leesbaarheid):
// We zetten de parameters neer
[OpenApiParameter(...)]
// We laten zien wat de response is
[OpenApiResponseWithBody(...)]
[Function("GetOrganisations")]
[Authorize("Reader")] // Deze behoort niet tot OpenApi, maar is een eigen object voor authorization.
public async Task<HttpResponseData> GetOrganisations(
[HttpTrigger(AuthorizationLevel.Function, "get", Route="organisations")]
HttpRequestData req)
{
// Hier pakken we de parameters en maken wij het reponse object aan.
}
Het bovenstaande is voor organisations. Bij programmes en courses is het anders. Als ze beide toch bijna hetzelfde doen, is het beter om alles via een generiek object te doen:
// Hier hetzelfde als bij organisations
// En in de function:
{
response = await _productService.GetProducts<ProgrammeDto>(type=4, query=query)
}
Hier hebben we 1 object, _productService die alle informatie ophaalt en mapt. Bij courses gaat dit bijna precies hetzelfde, maar dan met CourseDto en type=0. Hierdoor voorkomen we dubbele code, maar voldoen we toch aan de eisen van de OEAPI.
Generieke objecten komen erg vaak voor in de code. Ook wordt dit gebruikt voor het database object, en het object om enums te checken.
Ook wordt er veel aan beveiliging gedaan, en DTO's zijn een goed voorbeeld. Als je niet alle informatie naar buiten wilt brengen, wordt een Data Transfer Object gebruikt, een DTO. We zetten in deze DTO alleen de informatie die nodig is, zodat niet alles doorgegeven hoeft te worden.
Omdat dit een eenmans project is, wordt versiebeheer minder gebruikt dan gewoonlijk, maar het wordt alsnog op een goede manier toegepast.
Elke dag (of elke opdracht) wordt alles gecommit en wordt het gepusht naar de remote. Bij een commit moet altijd een goede beschrijving staan over wat er gedaan is in een korte omschrijving. Dit zorgt ervoor dat er een mooie geschiedenis komt van de commits, en als er iets mis gaat weet je naar welke commit je terug moet gaan.
Bij het algemene project wordt maar 1 branch gebruikt, de develop branch. Hierop wordt alles gerealiseerd. Maar soms is het ook nodig om verschillende dingen te testen over hoe en wat. Een voorbeeld hiervan is het database object. Dit object en het project zelf moesten door elkaar heen gemaakt worden, dus we hebben hiervoor een nieuwe branch gemaakt. Hetzelfde met de JWT tokens. Het was nog niet helemaal zeker hoe en wat, dus heb ik een nieuwe branch gemaakt zodat dit nagekeken kon worden zonder dat het in de weg zat van het echte project.