rosscado / raceready

Created for toolchain: https://console.bluemix.net/devops/toolchains/0fc11092-0119-4ab8-a5f0-6cbf19d20e03?env_id=ibm%3Ayp%3Aeu-gb
0 stars 0 forks source link

Worked Example: Lucan GP #57

Open rosscado opened 6 years ago

rosscado commented 6 years ago

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

rosscado commented 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.

rosscado commented 6 years ago

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.

rosscado commented 6 years ago

Club Entities

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}
rosscado commented 6 years ago

Event Media

Add a field media to event entities to hold promotional media like posters.

media:
   poster:

Event Posters

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.

rosscado commented 6 years ago

Handicapping

The stage entity's handicapped field is not defined in the model. It should be a boolean field with a default value of false.

Conflicting Information

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

Conflict Resolution

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.

rosscado commented 6 years ago

Courses without Circuits

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.

rosscado commented 6 years ago

Updating Entities: All or Nothing?

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?

YAML Fragments

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:

Determination: PUT All

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.

rosscado commented 6 years ago

Representing Relationships: Nested or Linked Entities?

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}?

HATEOAS

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:

The 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.

Object Links

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}

Pattern

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: