What's going on guys? In this video we
are going to use goroutines and channels to build an API mashup pulling data
from two different APIs.... I created a Golang cheat sheet that includes some
examples on goroutines and channels and also some examples on interfaces,structs,
error handling and a lot more! you can download the golang cheat sheet from
the link in the description below (pragmaticreviews.com) Remember to share, like, and subscribe to
the channel and let's get started.... so this is going to be the result after the
mashup of the two APIs that we're going to integrate using goroutines and
channels so these four attributes: the identifier, the brand, the model and the
year are going to be extracted from this endpoint: https://myfakeapi.com/api/cars/1
and these are the attributes that we are going to
extract the identifier, the brand, the model and the year and these three
attributes: the first name, the last name and the email we are going to extract
those from this endpoint: https://myfakeapi.com/api/users/1
and we're going to extract this first name attribute, the last name and
the email...okay so I already created the server I'm using the HTTP router that we
created on a previous video and I've added this endpoint that is going to use this
resource or this URI and it's going to use this function within the car details
controller that is actually this one and what it's going to do is it's going to
delegate to the car details service and it's going to call this function that is
going to return an structure that we are going to define that is going to
basically represent this output that we are going to return when our API is
invoked specifically we are using the Chi router and here we have the
controller where we are passing the service as a parameter... let's get started
working on the data structures that are going to represent not only the output
after the mashup of the external api's but also the data structures that are
going to represent the attributes that we want to extract from those external
api's so first let's start working on these four attributes that are part of
the first API so I'm going to create a new file here this is going to be car.go
the package is going to be entity and the struct is going to be a
car struct and if we go back to the response of this external API we're
going to see that we have a car and within this element we have the
attributes that we want to extract so we need to create another structure let's
call it CarData (type CarData struct) and here we have to add the four
elements: the identifier it's gonna be an int here we define the attribute name.... and we also need to add the rest of the attributes the brand, the model
.... it's gonna be ID it's gonna be brand, it's gonna be model
sorry this gonna be a string it's gonna be the string as well and the attribute in
this case is car_model and then we have the year that's going to be
an int and in this case the attribute name is car_model_year like this okay
so this is the structure and we need to embed the structure here so this is the
structure that we just created and now we need to add this wrapper element so
we need to embed this struct within this one...in this case the field name is going to be Car like in here okay now we have the
struct that is going to represent the output of the first API now let's create
the structure that is going to represent this output (the second API) so let's call it owner.go the package is going to be entity... type owner struct and here we need to do
something similar to the car because we have this element that is wrapping the
attributes that we need so we're going to create another struct here there's
going to be OwnerData and this is the struct that is going to
include the attributes that we need to map so we have first name, last name and
email so this is firstname string and field name is first_name
like this the second one is going to be last name and this third one is going to
be the email and that's the field that we need to map okay and here we need to
embed this struct that we created and it's going to be User like this let's
copy that and we have our structure created for the output of this second
API and now we are going to create the structure for this output for the mashup
API that we are implementing so we are going to call it CarDetails car-details.go
and here the package is going to be entity "...type CarDetails..." and here
we are going to have all these elements right we can grab this one from here I forgot to add "struct" here okay
we have ID, brand, model and here ID, brand, model here and the fields we want these
fields we want ID, we want brand and this is going to be model and it's going
to be model year and we need to add the details of the owner of the car let's say
and we can extract those from here and for this last three attributes we want
to assign owner_firstname owner_lastname and owner_email like this okay now we have all the data structures that
we need and let's move on to the service so this service is going to return
entity and the struct that we already created that is CarDetails and I need to import the entity package "import entity" okay
and the interface is going to also return that type okay this method what
is going to do is it's going to create a Goroutine to get data from the first API that is this one..... another Goroutine to get the
data from the second API which URL is this one and then we are going to create
two channels "create channel to get the data.... "create channel this should be
userChannel, sorry, carChannel the first one....and then we
are going to create the ownerChannel to get data from endpoint 1 and this is going to get the information from the
endpoint 2... so let's say goroutine the goroutine is actually going to call
endpoint 1 and this goroutine is going to call endpoint 2 so these are going to be
2 asynchronous operations that we want to create a (to run on a new) thread so they can run in
the background and once we get the result we're going to receive that
result here in the channels that we are going to create so this is pretty much
what this method is going to do so let's create a service to call the endpoint
number 1 so this is going to be the car service that is going to extract the
information from this endpoint so let's go to the service folder and let's
create a new file let's call it... car-service.go the package is gonna be service and we
are going to create an interface here car service and the only method that we are
going to declare here is going to be the fetch data method okay and we need to
create a structure to implement that interface let's call it fetch car data
service like that I forgot to add the struct here okay
and we need to create our constructor function it's going to be new car
service and it's going to return a reference to this struct and I need to
declare the type here that is CarService okay and now I need to implement actually
this fetch data function so func it's gonna be....I need to pass the
struct here to make it implement this function it's going to be fetch
data and here what we're going to use is an HTTP client that is going to call
that it's going to invoke this URL so I'm going to copy this URL and I'm going
to create a constant here car service URL like that
and here I need to use I'm going to create a client that's gonna be http.Client{}
...like that I'm going to print just to show that I'm calling that
endpoint fetching the URL and I'm going to pass the URL car service URL this
is fmt.Printf... like that and here what I need to do is I need to
call the external API and here I need to handle the response and I'm going to
ignore the error and I need to use client that get and I need to pass the
URL that is this one okay and after that what I need to do is I need to write the
response to the channel and this is going to be done once I declare that
channel the channel needs to be shared between this car detail service and
this car service and this is pretty much what we need to do to get the data from
the first API that we want to mashup and now I'm going to create another
service that is going to be pretty similar to this one it's going to be the
owner service and pretty much the only thing that is going
change is the URL so I'm going to copy this one and I'm going to adapt it, I'm going to rename it... this is going to be... owner-service.go let's rename this we're
going to use the same fetch data function this is going to be owner
service URL it's gonna be users let's check yes "..../users/1" and it's going
to be fetch owner data service let's copy this let's copy this interface here and
this is going to be new owner service and I need to copy the structure here to
make it implement this function and here I need to change the URL for this one and
here and I think that's pretty much for this owner service so we have the car
service that is going to get the data from this endpoint and then we have this
owner service that's going to fetch the data from this other endpoint
okay now I need a reference to those services that I just created because I
need to use those services here okay I'm going to add those variables
to create those references so first I'm going to use the car service it's going
to be a car service interface and I need to use the constructor new car service to get a
reference to that service and I'm gonna need the owner service that is going to implement the owner
service interface and I need to create a new instance by using the new owner
service function like that and also I need two channels here so I need a car
data channel here I'm going to receive the HTTP response so I need to use the make
function and here I'm going to declare the channel like this chan and it's going
to be a reference to *http.Response like that and something similar to the
other API so this one is going to be owner data channel where I'm going to
receive the data related to the owner of the car okay so this is pretty much what
we need to start working on our get details function so here I'm going to
use a Goroutine this is gonna be go using this keyword I'm going to create a
new thread and that thread is going to run this method carService.FetchData() and something similar for the owner service in another go routine ownerService.FetchData()... okay and now let's go to the car service and here I need to
write a response to the channel the channel is car data channel and I can
write to that channel like this and I pass the response so once I get the response from
this API that is actually this URL I pass or I write the response to this car data Channel and something similar is going to happen here on the
owner service once I get the response from this endpoint I'm going to write
the response to the owner data channel like this I'm going to remove the "todo"
here and I'm going to remove the "todo" here okay let's go back to the car
details service I'm going to create a function that is going to read the
information from the channel and it's going to decode that data from JSON
to the "object" that we need so let's do that actually we need to create two
functions one for the car data and the other one for the owner data so the
first one is going to be func getCarData() and here I'm going to
return entity.Car or an error in case we have any errors so first I'm
going to read the information from the channel and I can do that like this I
create this variable ":=" ...and here I specify what is the channel that
I want to read from in this case it's gonna be car data Channel and I'm going to
create a variable here that's going to be the car entity.Car and now I'm going
to decode the response that I received here from the channel so it's going to
be Shea sound I knew the coder and here I need to pass
r1.Body like that and here going to call the encode function, sorry!
the decode function because I receive a JSON here...decode function and I need to pass a reference like this to the car variable where I'm going to create the object okay and
here I need to handle this error in case we have any errors decoding the body and
here we are going to check if err is not Nil I'm going to print an error... ...let's use the Error() function here I forgot to add the function print like that and I'm going
to return car and error and if we don't get any errors I just return car and
nil... like that and the other function to get the owner data it's
gonna be pretty similar so I'm going to copy this and I'm going to adapt it for
the owner data so this is gonna be getOwnerData() and this is gonna be
entity.Owner like that and here the channel is going to be owner data channel like that and here I'm going to create a variable that is
going to be owner entity.Owner and here is not car, is owner like that and here I'm going to return the owner and the same if we don't get
any errors I'm going to return the owner and nil as the error
okay now let's use these functions from here so here I'm going to receive the
car and an error I'm going to ignore the error and here I'm going to use the get
card data function and something similar for the owner owner and I'm going to
ignore the error and I need to use here the get owner data function let's check if
everything is okay........ yeah I think we are good okay let's move on
I'm going to remove these comments and now I need to return this structure okay let's
grab the attributes from here okay the identifier is going to be
car.ID the brand is going to be card.Brand the model is going to be car.Model the year is going to be car.Year okay and the first name is gonna be
owner.FirstName the last name is going to be owner.LastName like
that and the email is going to be owner.Email okay I think we're good
okay I'm going to create a test really quick here so this is gonna be car-details -service_test.go I'm going to use the same service package
and here I'm going to create a function that is going to be TestGetDetails() I'm going to pass t and the reference to the testing library *testing.T like that car detail service like that and I need to use the
constructor here new car Details service and here I expect to get the car details and I need to use the getDetails function from the car details
service so this is gonna be... carDetailsService.GetDetails() like that I'm
going to it put this in a variable here yes like that and here I'm going to
add some assertions first I need to import the testify assertion library I'm
going to do that so this is.... "github.com/stretcher/testify/assert" so here we can use assert.NotNil() and I'm going to check that the car details are not
empty and I'm going to check a couple of elements so I'm going to use equal ....
assert.Equal here I need to pass the test the expected value in this case I'm
going to check that the identifier is 1 and here I need to pass carDetails.ID and something similar for a couple of attributes let's say brand and the year
sorry the year so here I expect the brand to be Mitsubishi and the year I expect it expect it to be
2002 like that okay let's run this test okay like that okay let's run this again
and the test is passing okay let's quickly debug it to see just
to check what is the information that we get here so I'm going to click here on "debug test" and if we go here to the car details we're gonna see that we get the identifier as 1 the
brand as Mitsubishi the model as Montero the year as 2002 ,first name
last name, and email and are basically the information that we got from here
right the identifier, the brand, the model and the year and the same for the rest of the information the first name like in here, the last name like in here and the email the same as this one
let's run this "go run *.go" okay and now we have our server let's go back so we have
our server running on this port so I'm going to create that request
http://localhost:8000/carDetails so if we run this we should get a response that
we expect... yes we get the id, brand, the model, the year, the owner first
name the owner last name the owner email and this is the same structure that we
defined here at the very beginning of the video ID, brand, model, year, owner first name, owner last name okay that's pretty much all I have for today thank
you for watching...remember to download the free Cheat Sheet for Golang (pragmaticreviews.com).... and I
see you guys in the next video, take care, bye!