Skip to content

Repair the broken client

To get bob working again we could, of course, use the hypermedia approach. However, in this demo we will continue with the "memorize endpoints" approach.

Planning the work

The problem, of course, is all resources used to be located under a single BASE_API_URL, which was configured in config.py. They have all been relocated so we will need to update that file.

Let's look again at the client class diagram:

Each of the <<api>> stereotyped methods are subject to change.

So we will change these files/methods, in this order:

  • config.py
  • venue.py
    • select_venue()
    • get_events()
    • add_event()
  • event.py
    • view_attendees()
    • register()

config.py

Looking at the v2 docker-compose.yml we see that the new single-resource components are found in the following locations:

yaml
name: event-buddy

services:
  venues-service:
    ...  
      HY_BASE_URL: http://$IP_ADDRESS:2113
    ...
      
  registrations-service:
    ...
      HY_BASE_URL: http://$IP_ADDRESS:2114
    ...
      
  events-service:
    ...
      HY_BASE_URL: http://$IP_ADDRESS:2115
    ...

  accounts-service:
    ...
      HY_BASE_URL: http://$IP_ADDRESS:2116
    ...

To find the value of $IP_ADDRESS, change into /server/v2/ then:

bash
cat .env
# Windows: type .env

On my machine I see the following:

text
IP_ADDRESS=192.168.1.94

We will use the above to modify the config.py file.

python
# config.py
VENUES_BASE_API_URL = 'http://192.168.1.94:2113'
REGISTRATIONS_BASE_API_URL = 'http://192.168.1.94:2114'
EVENTS_BASE_API_URL = 'http://192.168.1.94:2115'
ACCOUNTS_BASE_API_URL = 'http://192.168.1.94:2116'

Note

You can also use localhost instead of IP for bob-v2. The docker-compose.yml needs IP addresses because in order for the components to maintain their relationships with each other, they need URLs that resolve to the other components' services. A component, running inside a docker container, will resolve localhost to itself - breaking the domain model.

venue.py - select_venue()

python
# venue.py
from config import BASE_URL_API
...
...
@staticmethod
def select_venue():
    url = Api.url_join(BASE_API_URL, '/venues')
    result = requests.get(url, headers=Api.get_headers())

This is a simple fix. The BASE_API_URL is currently configured to point at what is now the Gateway (http://localhost:2112). We need to replace that with VENUES_BASE_API_URL.

python
# venue.py
from config import VENUES_BASE_API_URL
...
...
@staticmethod
def select_venue():
    url = Api.url_join(VENUES_BASE_API_URL, '/venues')
    result = requests.get(url, headers=Api.get_headers())

venue.py - get_events()

This fix is a bit more challenging.

python
# venue.py
from config import VENUES_BASE_API_URL
...
...
def get_events(self):
    url = Api.url_join(BASE_API_URL, f'/venues/{self["_id"]}/events')
    result = requests.get(url, headers=Api.get_headers())

Put your backend developer hat on for a moment.

When all resources were served by the same component:

  • to GET all venues, the path is /venues
  • to GET a single venue, the path is /venues/{venue_id}
  • so it made sense if you want to GET the events for a venue, the path should be /venues/{venue_id}/events

However, events are now served by the events-service component. The above logic no longer applies (what does GET /venues mean from the events-service point of view?)

So the backend team created this route: /events/venue/{venue_id} i.e. GET the events for venue ID.

This means the above needs to become:

python
# venue.py
from config import VENUES_BASE_API_URL, EVENTS_BASE_API_URL
...
...
def get_events(self):
    url = Api.url_join(EVENTS_BASE_API_URL, f'/events/venue/{self["_id"]}')
    result = requests.get(url, headers=Api.get_headers())

Side note

Even the assumption that _id is part of a URL is subject to future change. You will recognize the IDs used in Event Buddy as ObjectIDs from MongoDB. We had a backend that served vehicle data. The initial version of that service used ObjectIDs in a similar fashion to Event Buddy. URLs contained a vehicle's _id.

A request was made by the BI team, who were analyzing server logs. Server logs are full of data like GET /vehicles/67c761f3ed56f8e9e1210b9f They wanted to harvest this information and correlate to vehicle VINs.

It was easy to change _id to vin in constructing these URLs because our hypermedia aware clients didn't care how URLs were constructed. Now server logs are full of data like this instead: GET /vehicles/SCA1S684X4UX07444 i.e. the vehicle VIN instead of a random generated ID.

venue.py - add_event()

This change is identical to the previous - it uses the same resource with a POST instead of a GET.

python
# venue.py
from config import VENUES_BASE_URL_API, EVENTS_BASE_URL_API
...
...
def add_event(self, event_data):
    data = json.dumps(event_data)
    url = Api.url_join(BASE_API_URL, f'/venues/{self["_id"]}/events')
    result = requests.post(url, data=data, headers=Api.get_headers())

becomes

python
# venue.py
from config import VENUES_BASE_URL_API, EVENTS_BASE_URL_API
...
...
def add_event(self, event_data):
    data = json.dumps(event_data)
    url = Api.url_join(EVENTS_BASE_API_URL, f'/events/venue/{self["_id"]}')
    result = requests.post(url, data=data, headers=Api.get_headers())

event.py - view_attendees()

This change is similar to the last couple of changes:

python
# venue.py
from config import BASE_URL_API
...
...
def view_attendees(self):
    url = Api.url_join(BASE_API_URL, f'/events/{self["_id"]}/registrations')

Registrations have moved, with similar change to the path URL design as we saw with events/venues.

So the above becomes GET registrations for event ID:

python
# venue.py
from config import REGISTRATIONS_BASE_API_URL
...
...
def view_attendees(self):
    url = Api.url_join(REGISTRATIONS_BASE_API_URL, f'/registrations/event/{self["_id"]}')

event.py - register()

Finally, although we haven't yet seen event registration in action, we can see what needs to change here:

python
# venue.py
from config import REGISTRATIONS_BASE_URL_API
...
...
def register(self, username=None):
    user_spec = json.dumps({'username': username}) if username else {}
    url = Api.url_join(BASE_API_URL, f'/events/{self["_id"]}/register')

Without going into details, the above becomes:

python
# venue.py
from config import REGISTRATIONS_BASE_API_URL, EVENTS_BASE_API_URL
...
...
def register(self, username=None):
    user_spec = json.dumps({'username': username}) if username else {}
    url = Api.url_join(EVENTS_BASE_API_URL, f'/events/{self["_id"]}/register')

Note

As mentioned before, we will look at registering to attend an event in a future discussion.

That completes all the changes - until the backend team gets some more ideas.

bob-v2

If you were not following along making the changes to the bob client, you can still give bob-v2 a try.

All of the above changes can be found in the clients/bob-v2 folder. With Event Buddy v2 running, it will work as expected - but will fail if v1 is running.

As we have seen, alice works with both versions - and versions yet to come.

Released under the MIT License.

Released under the MIT License.