Open rosscado opened 6 years ago
In this use case the circuit is not explicitly stated in the event information. If it is to be included in the event it will need to be inferred some other way, e.g. by searching Strava activities for previous editions.
We will represent this in the test case by creating the event without a circuit initially, and perhaps adding one later.
In this case the organising club/contacts information is on the calendar page and not on the event details page, so that will be an important source of information too when parsing events automatically.
I need to separate the club entity from the POST event request, since we won't be recreating clubs on every event. This will then require a means of looking up the club and linking the event to it. E.g.
club:
id: Lucan Cycling Road Club
...
event:
organised_by:
club_id: Lucan Cycling Road Club
Or
club_id = GET /api/clubs/?title=Lucan Cycling Road Club
event:
organised_by:
club_id: {club_id}
Add a field media
to event entities to hold promotional media like posters.
media:
poster:
Many of the better promoted events have poster images. These can be highly informative and will be important to display to athletes, but they will be impossible to parse programmatically.
The poster
field could be a URL to the online image, or an embedded image document itself. Saving the image within the application would be more resilient but would consume much more data.
The stage entity's handicapped
field is not defined in the model.
It should be a boolean field with a default value of false
.
This use case has a conflict between the event details page and the event poster. The former indicates 3 races: A+/A1/A2, A3/J, A4 The latter indicates 2 races: A+/A1/A2/A3/J (handicapped), A4
In this case the conflict is resolved in favour of the event poster. In an automated system it would probably require a user to flag the event has having a discrepancy/error and an administrator to review and correct it. I will represent the conflict in the test case by initially creating the event as according to the event details page and subsequently update it according to the event poster.
In this use case as in many the event details page indicates the distance of the races but not the route/course/circuit.
We will infer the circuit later but must be able to capture the presented information too. Even if it turns out later to have a discrepancy with the circuit (e.g. if distance_km % circuit.distance_km != 0
) that's information we would want to represent too.
Requirement:
Add a distance_km
field to the Course
model.
Should POST updates for entities include all entity data or just that which is changing? The former is more verbose and harder to write in test cases, but the latter begs the question of how to signal that a field has been removed? And what about fields that are required in POST requests but haven't changed in the PUT request?
Wrt writing yaml testcases there's an elegant solution: include the POST request payload in PUT requests as a reference to a yaml fragment, and override the changed properties.
request:
url: "{api.server}/api/events/{event_id}"
json:
<<: *initial_event_info
changed_properties:
For now we will assume that PUT requests contain all the state of the entity being updated. Tavern testcases should be made more succinct by using yaml fragments.
How should the API model represent referenced entities, especially entities that are shared?
For example, a single Circuit
entity may be referenced by many Event
entities. Should the Event
nest the Circuit
as a circuit: {CIRCUIT}
field or point to the external object as circuit_id: {circuit_uuid}
?
According to strict hypermedia representational principles like HATEOAS relationships between resources should be represented as url-addressable links
.
title: Lucan GP
id: {id}
links:
self:
href: /events/{id}
rel: self
organising_club:
- href: /clubs/lrcc
rel: club
races:
- href: /stages/{headline_race_id}
rel: race
- href: /stages/{a4_race_id}
rel: race
The hypermedia principle theoretically supports programmatic navigation by clients by providing them with the complete set of permissible operations in every REST API response. E.g. GET /event/{id}
is responded to with:
links.{relationship}.href
) and actions (HTTP methods)
PUT or DELETE self
GET, PUT, or DELETE racesThe hypermedia principle extends from the web paradigm where servers have little control over clients; They know little more than that their clients are web browsers that may navigate across the network of resources linked to in the server's responses.
In most practical applications, and certainly in this project's case, there is much more control over the client(s) and its requirements. For us there will be one client initially (in support of a web application with possible mobile application clients added later), a JavaScript library rendering application resources in a web UI.
A compromise approach that adopts advantages of both hypermedia and domain-application principles is to federate resources and include links to each. 1
GET /events/{id}
title: Lucan GP
id: {id}
href: /events/{id}
organised_by:
club:
title: Lucan Road Cycling Club
href: /clubs/lrcc
races:
- href: /events/{id}/races/{stage_id_a123}
- href: /events/{id}/races/{stage_id_a4W}
All complex reusable objects are represented as URL addressable resources.
When a resource A
has a relationship to another resource B
, A
contains only B
's href
and other top-level attributes. A
does not embed the complex nested object unless the client requests it with query parameter embed=(field, ...)
.
A
model
id: {id}
href: {self_url}
title:
b:
href: {b_url}
id:
title:
B
model
id:
title:
c:
href:
Create another worked example test case to shake out any further requirements and evaluate the enhancements delivered to support the Laragh Classic use case.
Cycling Ireland Event URL: https://cyclingireland.azolve.com/Workbench.mvc/public/eventDetails?EventId=1074983