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:
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:
cat .env
# Windows: type .env
On my machine I see the following:
IP_ADDRESS=192.168.1.94
We will use the above to modify the config.py
file.
# 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()
# 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
.
# 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.
# 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:
# 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
tovin
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.
# 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
# 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:
# 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:
# 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:
# 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:
# 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.