SlideShare a Scribd company logo
1 of 26
Download to read offline
Creating an Uber Clone - Part XXVI
Before we proceed into the actual driver app work & push notification we need to implement all the infrastructure in the server side. I also need to implement some
changes we need in the protocol.
private String hailingFrom;
private String hailingTo;
private String pushToken;
public RideDAO getRideDao() {
return new RideDAO(id, givenName, hailingFrom, hailingTo);
}
public UserDAO getDao() {
return new UserDAO(id, givenName, surname, phone, email,
facebookId, googleId, driver, car, currentRating,
latitude, longitude, direction, pushToken);
}
public UserDAO getPartialDao() {
return new UserDAO(id, givenName, surname, null, null,
null, null, driver, car, currentRating, latitude,
longitude, direction, pushToken);
}
User (Server)
Lets start by looking at the User class. I had to add 3 new fields and modify/add some methods.

hailingFrom & hailingTo allow us to communicate our trip details with the driver community
private String hailingFrom;
private String hailingTo;
private String pushToken;
public RideDAO getRideDao() {
return new RideDAO(id, givenName, hailingFrom, hailingTo);
}
public UserDAO getDao() {
return new UserDAO(id, givenName, surname, phone, email,
facebookId, googleId, driver, car, currentRating,
latitude, longitude, direction, pushToken);
}
public UserDAO getPartialDao() {
return new UserDAO(id, givenName, surname, null, null,
null, null, driver, car, currentRating, latitude,
longitude, direction, pushToken);
}
User (Server)
I need the pushToken of drivers so we can hail them directly from the app
private String hailingFrom;
private String hailingTo;
private String pushToken;
public RideDAO getRideDao() {
return new RideDAO(id, givenName, hailingFrom, hailingTo);
}
public UserDAO getDao() {
return new UserDAO(id, givenName, surname, phone, email,
facebookId, googleId, driver, car, currentRating,
latitude, longitude, direction, pushToken);
}
public UserDAO getPartialDao() {
return new UserDAO(id, givenName, surname, null, null,
null, null, driver, car, currentRating, latitude,
longitude, direction, pushToken);
}
User (Server)
I'll discuss the `RideDAO` class soon, it allows us to send details about the trip to drivers
private String hailingFrom;
private String hailingTo;
private String pushToken;
public RideDAO getRideDao() {
return new RideDAO(id, givenName, hailingFrom, hailingTo);
}
public UserDAO getDao() {
return new UserDAO(id, givenName, surname, phone, email,
facebookId, googleId, driver, car, currentRating,
latitude, longitude, direction, pushToken);
}
public UserDAO getPartialDao() {
return new UserDAO(id, givenName, surname, null, null,
null, null, driver, car, currentRating, latitude,
longitude, direction, pushToken);
}
User (Server)
Not much of a change but I added the pushToken to the UserDAO factory methods
public class RideDAO implements Serializable {
private long userId;
private String name;
private String from;
private String destination;
public RideDAO() {
}
public RideDAO(long userId, String name, String from, String destination) {
this.userId = userId;
this.name = name;
if(this.name == null) {
this.name = "[Unnamed User]";
}
this.from = from;
this.destination = destination;
}
// getters and setters trimmed out
}
RideDAO
The RideDAO class is a pretty trivial object. There's no point in enumerating the details of this class as it's pretty simple. The main usage for this is in the new RideService
which I will get to soon but first we need to discuss the Ride class.
@Entity
public class Ride {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@ManyToOne
private User passenger;
@ManyToOne
private User driver;
@OneToMany
@OrderBy("time ASC")
private Set<Waypoint> route;
private BigDecimal cost;
private String currency;
private boolean finished;
private boolean started;
// trimmed out constructors, getters and setters
}
Ride
Ride isn't as simple as RideDAO despite their common name. It contains far more information. Currently we don't use all of that but the fact that it's logged will let you
provide all of that information within the app or a management app easily.

The Ride class is a JPA entity similar to the User class.

I used an auto-increment value for the id instead of a random string. I wanted to keep things simple but notice this can expose a security vulnerability of scanning for
rides…
@Entity
public class Ride {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@ManyToOne
private User passenger;
@ManyToOne
private User driver;
@OneToMany
@OrderBy("time ASC")
private Set<Waypoint> route;
private BigDecimal cost;
private String currency;
private boolean finished;
private boolean started;
// trimmed out constructors, getters and setters
}
Ride
The passenger & driver are relational database references to the respective database objects representing each one of them
@Entity
public class Ride {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@ManyToOne
private User passenger;
@ManyToOne
private User driver;
@OneToMany
@OrderBy("time ASC")
private Set<Waypoint> route;
private BigDecimal cost;
private String currency;
private boolean finished;
private boolean started;
// trimmed out constructors, getters and setters
}
Ride
The route itself is a set of waypoints sorted by the time associated with the given waypoint. We'll discuss waypoints soon enough but technically it's just a set of
coordinates
@Entity
public class Ride {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@ManyToOne
private User passenger;
@ManyToOne
private User driver;
@OneToMany
@OrderBy("time ASC")
private Set<Waypoint> route;
private BigDecimal cost;
private String currency;
private boolean finished;
private boolean started;
// trimmed out constructors, getters and setters
}
Ride
I really oversimplified the cost field. It should work for sum and currency but it's usually not as simple as that. It's important to use something like BigDecimal and not
double when dealing with financial numbers as double is built for scientific usage and has rounding errors
@Entity
public class Ride {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@ManyToOne
private User passenger;
@ManyToOne
private User driver;
@OneToMany
@OrderBy("time ASC")
private Set<Waypoint> route;
private BigDecimal cost;
private String currency;
private boolean finished;
private boolean started;
// trimmed out constructors, getters and setters
}
Ride
We have two boolean flags, a ride is started once a passenger is picked up. It’s finished once he is dropped off or if the ride was canceled
public interface RideRepository extends CrudRepository<Ride, Long> {
@Query("select b from Ride b where b.finished = false and b.driver.id = ?1")
public List<Ride> findByNotFinishedUser(long id);
}
RideRepository
The companion CRUD RideRepository is pretty standard with one big exception. I added a special case finder that lets us locate the User that is currently hailing a car.
Notice the syntax b.driver.id = ?1 which points through the relation to the driver object.
@Entity
public class Waypoint {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private long time;
private double latitude;
private double longitude;
private float direction;
// trimmed out constructors, getters and setters
}
Waypoint
The Waypoint entity referenced from the Ride entity is pretty trivial. Notice we still need a unique id for a waypoint even if we don't actually use it in code...

The interesting part here is the time value which is the value of System.currentTimeMillis(). This allows us to build a path based on the time sequence. It will also allow us
to reconstruct a trip and generate additional details such as speed/cost if we wish to do that in the future.

Notice that there is also a WaypointRepository interface. I’m skipping it as it contains no actual code
@Service
public class RideService {
@Autowired
private UserRepository users;
@Autowired
private RideRepository rides;
@Transactional
public UserDAO hailCar(String token, boolean h, String from, String to) {
User u = users.findByAuthToken(token).get(0);
if(h) {
if(u.getAssignedUser() != null) {
long driverId = u.getAssignedUser();
u.setAssignedUser(null);
users.save(u);
User driver = users.findOne(driverId);
return driver.getPartialDao();
}
} else {
u.setAssignedUser(null);
}
u.setHailing(h);
u.setHailingFrom(from);
u.setHailingTo(to);
users.save(u);
return null;
}
RideService
The RideService class serves the same purpose as the UserService class focusing on rides and driver related features. I could have just stuck all of this logic into one
huge class but separating functionality to different service classes based on logic makes sense.

We manipulate both the rides and users CRUD objects from this class
@Service
public class RideService {
@Autowired
private UserRepository users;
@Autowired
private RideRepository rides;
@Transactional
public UserDAO hailCar(String token, boolean h, String from, String to) {
User u = users.findByAuthToken(token).get(0);
if(h) {
if(u.getAssignedUser() != null) {
long driverId = u.getAssignedUser();
u.setAssignedUser(null);
users.save(u);
User driver = users.findOne(driverId);
return driver.getPartialDao();
}
} else {
u.setAssignedUser(null);
}
u.setHailing(h);
u.setHailingFrom(from);
u.setHailingTo(to);
users.save(u);
return null;
}
RideService
Hailing is a transactional method, this means that all operations within the method will either succeed or fail depending on the outcome. This is important to prevent an
inconsistent state in the database
@Service
public class RideService {
@Autowired
private UserRepository users;
@Autowired
private RideRepository rides;
@Transactional
public UserDAO hailCar(String token, boolean h, String from, String to) {
User u = users.findByAuthToken(token).get(0);
if(h) {
if(u.getAssignedUser() != null) {
long driverId = u.getAssignedUser();
u.setAssignedUser(null);
users.save(u);
User driver = users.findOne(driverId);
return driver.getPartialDao();
}
} else {
u.setAssignedUser(null);
}
u.setHailing(h);
u.setHailingFrom(from);
u.setHailingTo(to);
users.save(u);
return null;
}
RideService
This method can be invoked to start and stop hailing. In this case we use the assigned user property to detect if a driver accepted the ride. If so we return the driver data
to the client
users.save(u);
return null;
}
public RideDAO getRideData(long userId) {
User u = users.findOne(userId);
if(u == null) {
return null;
}
return u.getRideDao();
}
@Transactional
public long acceptRide(String token, long userId) {
User driver = users.findByAuthToken(token).get(0);
User passenger = users.findOne(userId);
if(!passenger.isHailing()) {
throw new RuntimeException("Not hailing");
}
passenger.setHailing(false);
passenger.setAssignedUser(driver.getId());
driver.setAssignedUser(userId);
users.save(driver);
users.save(passenger);
Ride r = new Ride();
r.setDriver(driver);
r.setPassenger(passenger);
rides.save(r);
RideService
When a driver gets a notification of a ride he invokes this method to get back the data about the ride
users.save(u);
return null;
}
public RideDAO getRideData(long userId) {
User u = users.findOne(userId);
if(u == null) {
return null;
}
return u.getRideDao();
}
@Transactional
public long acceptRide(String token, long userId) {
User driver = users.findByAuthToken(token).get(0);
User passenger = users.findOne(userId);
if(!passenger.isHailing()) {
throw new RuntimeException("Not hailing");
}
passenger.setHailing(false);
passenger.setAssignedUser(driver.getId());
driver.setAssignedUser(userId);
users.save(driver);
users.save(passenger);
Ride r = new Ride();
r.setDriver(driver);
r.setPassenger(passenger);
rides.save(r);
RideService
If the driver wishes to accept the ride he invokes this transactional method. The method accepts the token from the driver and the id of the user hailing the ride. It creates
a new Ride entity and returns its ID, from this point on we need to refer to the Ride id and not the user id or token
User driver = users.findByAuthToken(token).get(0);
User passenger = users.findOne(userId);
if(!passenger.isHailing()) {
throw new RuntimeException("Not hailing");
}
passenger.setHailing(false);
passenger.setAssignedUser(driver.getId());
driver.setAssignedUser(userId);
users.save(driver);
users.save(passenger);
Ride r = new Ride();
r.setDriver(driver);
r.setPassenger(passenger);
rides.save(r);
return r.getId();
}
public void startRide(long rideId) {
Ride current = rides.findOne(rideId);
current.setStarted(true);
rides.save(current);
}
public void finishRide(long rideId) {
Ride current = rides.findOne(rideId);
current.setFinished(true);
rides.save(current);
}
}
RideService
Start ride and finish ride are invoked by the driver when he picks up the passenger and when he drops him off. Normally, finish ride should also handle elements like
billing etc. but I won't go into that now
@Controller
@RequestMapping("/ride")
public class RideWebservice {
@Autowired
private RideService rides;
@RequestMapping(method=RequestMethod.GET,value = "/get")
public @ResponseBody RideDAO getRideData(long id) {
return rides.getRideData(id);
}
@RequestMapping(method=RequestMethod.GET,value="/accept")
public @ResponseBody String acceptRide(@RequestParam(name="token", required = true) String token,
@RequestParam(name="userId", required = true) long userId) {
long val = rides.acceptRide(token, userId);
return "" + val;
}
@RequestMapping(method=RequestMethod.POST,value="/start")
public @ResponseBody String startRide(@RequestParam(name="id", required = true) long rideId) {
rides.startRide(rideId);
return "OK";
}
@RequestMapping(method=RequestMethod.POST,value="/finish")
public @ResponseBody String finishRide(@RequestParam(name="id", required = true) long rideId) {
rides.finishRide(rideId);
return "OK";
}
}
RideWebservice
The next step is bridging this to the user through a webservice...

The RideWebservice class exposes the RideService calls almost verbatim to the client.

The get call fetches the RideDAO for the given user id
@Controller
@RequestMapping("/ride")
public class RideWebservice {
@Autowired
private RideService rides;
@RequestMapping(method=RequestMethod.GET,value = "/get")
public @ResponseBody RideDAO getRideData(long id) {
return rides.getRideData(id);
}
@RequestMapping(method=RequestMethod.GET,value="/accept")
public @ResponseBody String acceptRide(@RequestParam(name="token", required = true) String token,
@RequestParam(name="userId", required = true) long userId) {
long val = rides.acceptRide(token, userId);
return "" + val;
}
@RequestMapping(method=RequestMethod.POST,value="/start")
public @ResponseBody String startRide(@RequestParam(name="id", required = true) long rideId) {
rides.startRide(rideId);
return "OK";
}
@RequestMapping(method=RequestMethod.POST,value="/finish")
public @ResponseBody String finishRide(@RequestParam(name="id", required = true) long rideId) {
rides.finishRide(rideId);
return "OK";
}
}
RideWebservice
Start and finish rides are again very simple with only one argument which is the ride id
public void updatePushToken(String token, String pushToken) {
User u = users.findByAuthToken(token).get(0);
u.setPushToken(pushToken);
users.save(u);
}
UserService
We also have to add some minor changes to the UserService and LocationService classes. Lets start with the UserService class. Drivers need a push token so we can
hail them. This is always set outside of the user creation code for two reasons. 

The first time around the user is created but the push key isn't there yet (it arrives asynchronously)

Push is re-registered in every launch and refreshed, there is no reason to update the entire object for that
@RequestMapping(method = RequestMethod.GET,value = "/setPushToken")
public @ResponseBody String updatePushToken(
@RequestParam(name="token", required = true) String token,
@RequestParam(name="pushToken", required = true) String
pushToken) {
users.updatePushToken(token, pushToken);
return "OK";
}
UserWebservice
The UserWebservice class needs to mirror these changes obviously...

There isn't much here we just added a new setPushToken URL and we accept this update.
public class LocationService {
@Autowired
private UserRepository users;
@Autowired
private RideRepository rides;
@Autowired
private WaypointRepository waypoints;
public void updateUserLocation(String token,double lat,double lon,float dir) {
List<User> us = users.findByAuthToken(token);
User u = us.get(0);
u.setLatitude(lat);
u.setLongitude(lat);
u.setDirection(dir);
users.save(u);
if(u.isDriver() && u.getAssignedUser() != null) {
List<Ride> r = rides.findByNotFinishedUser(u.getId());
if(r != null && !r.isEmpty()) {
Ride ride = r.get(0);
if(ride.isStarted() && !ride.isFinished()) {
Set<Waypoint> route = ride.getRoute();
Waypoint newPosition = new Waypoint(
System.currentTimeMillis(), lat, lon, dir);
waypoints.save(newPosition);
route.add(newPosition);
ride.setRoute(route);
rides.save(ride);
LocationService
The LocationService needs a bit more work.

Every time we update a users location we check if he's a driver on a ride
public class LocationService {
@Autowired
private UserRepository users;
@Autowired
private RideRepository rides;
@Autowired
private WaypointRepository waypoints;
public void updateUserLocation(String token,double lat,double lon,float dir) {
List<User> us = users.findByAuthToken(token);
User u = us.get(0);
u.setLatitude(lat);
u.setLongitude(lat);
u.setDirection(dir);
users.save(u);
if(u.isDriver() && u.getAssignedUser() != null) {
List<Ride> r = rides.findByNotFinishedUser(u.getId());
if(r != null && !r.isEmpty()) {
Ride ride = r.get(0);
if(ride.isStarted() && !ride.isFinished()) {
Set<Waypoint> route = ride.getRoute();
Waypoint newPosition = new Waypoint(
System.currentTimeMillis(), lat, lon, dir);
waypoints.save(newPosition);
route.add(newPosition);
ride.setRoute(route);
rides.save(ride);
LocationService
Assuming we have a Ride object we check if this is currently an ongoing ride that wasn't finished
public class LocationService {
@Autowired
private UserRepository users;
@Autowired
private RideRepository rides;
@Autowired
private WaypointRepository waypoints;
public void updateUserLocation(String token,double lat,double lon,float dir) {
List<User> us = users.findByAuthToken(token);
User u = us.get(0);
u.setLatitude(lat);
u.setLongitude(lat);
u.setDirection(dir);
users.save(u);
if(u.isDriver() && u.getAssignedUser() != null) {
List<Ride> r = rides.findByNotFinishedUser(u.getId());
if(r != null && !r.isEmpty()) {
Ride ride = r.get(0);
if(ride.isStarted() && !ride.isFinished()) {
Set<Waypoint> route = ride.getRoute();
Waypoint newPosition = new Waypoint(
System.currentTimeMillis(), lat, lon, dir);
waypoints.save(newPosition);
route.add(newPosition);
ride.setRoute(route);
rides.save(ride);
LocationService
If so we add a waypoint to the ride and update it so we can later on inspect the path of the Ride. 

This pretty much tracks rides seamlessly. If we wanted to be really smart we could detect the driver and user position to detect them traveling together and automatically
handle the ride. There are obviously problems with this as it means a user can't order a cab for someone else but it might be an interesting feature since we have two
close data points…

More Related Content

Similar to Creating an Uber Clone - Part XXVI - Transcript.pdf

Introducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and serverIntroducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and server
Spike Brehm
 
Js in Automotive - JS.everywhere(2013)
Js in Automotive - JS.everywhere(2013)Js in Automotive - JS.everywhere(2013)
Js in Automotive - JS.everywhere(2013)
Alexandre Morgaut
 
Android Best Practices
Android Best PracticesAndroid Best Practices
Android Best Practices
Yekmer Simsek
 
For the following questions, you will implement the data structure to.pdf
For the following questions, you will implement the data structure to.pdfFor the following questions, you will implement the data structure to.pdf
For the following questions, you will implement the data structure to.pdf
arjunhassan8
 
Sahana introduction to the code v2
Sahana   introduction to the code v2Sahana   introduction to the code v2
Sahana introduction to the code v2
AidIQ
 

Similar to Creating an Uber Clone - Part XXVI - Transcript.pdf (20)

Creating an Uber Clone - Part XIII - Transcript.pdf
Creating an Uber Clone - Part XIII - Transcript.pdfCreating an Uber Clone - Part XIII - Transcript.pdf
Creating an Uber Clone - Part XIII - Transcript.pdf
 
Introducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and serverIntroducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and server
 
Js in Automotive - JS.everywhere(2013)
Js in Automotive - JS.everywhere(2013)Js in Automotive - JS.everywhere(2013)
Js in Automotive - JS.everywhere(2013)
 
Introduction to angular js
Introduction to angular jsIntroduction to angular js
Introduction to angular js
 
Creating an Uber Clone - Part XVII - Transcript.pdf
Creating an Uber Clone - Part XVII - Transcript.pdfCreating an Uber Clone - Part XVII - Transcript.pdf
Creating an Uber Clone - Part XVII - Transcript.pdf
 
Creating an Uber Clone - Part XXXI - Transcript.pdf
Creating an Uber Clone - Part XXXI - Transcript.pdfCreating an Uber Clone - Part XXXI - Transcript.pdf
Creating an Uber Clone - Part XXXI - Transcript.pdf
 
Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics
 
TripThru_API_Doc_v1
TripThru_API_Doc_v1TripThru_API_Doc_v1
TripThru_API_Doc_v1
 
Angular directive filter and routing
Angular directive filter and routingAngular directive filter and routing
Angular directive filter and routing
 
Android Best Practices
Android Best PracticesAndroid Best Practices
Android Best Practices
 
For the following questions, you will implement the data structure to.pdf
For the following questions, you will implement the data structure to.pdfFor the following questions, you will implement the data structure to.pdf
For the following questions, you will implement the data structure to.pdf
 
A gently introduction to AngularJS
A gently introduction to AngularJSA gently introduction to AngularJS
A gently introduction to AngularJS
 
4.Spring IoC&DI(Spring Ioc실습_어노테이션 기반)
4.Spring IoC&DI(Spring Ioc실습_어노테이션 기반)4.Spring IoC&DI(Spring Ioc실습_어노테이션 기반)
4.Spring IoC&DI(Spring Ioc실습_어노테이션 기반)
 
Angular routing
Angular routingAngular routing
Angular routing
 
Angular js routing options
Angular js routing optionsAngular js routing options
Angular js routing options
 
Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...
Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...
Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...
 
Domain Driven Design 101
Domain Driven Design 101Domain Driven Design 101
Domain Driven Design 101
 
Creating an Uber Clone - Part XXVII - Transcript.pdf
Creating an Uber Clone - Part XXVII - Transcript.pdfCreating an Uber Clone - Part XXVII - Transcript.pdf
Creating an Uber Clone - Part XXVII - Transcript.pdf
 
Clean Architecture @ Taxibeat
Clean Architecture @ TaxibeatClean Architecture @ Taxibeat
Clean Architecture @ Taxibeat
 
Sahana introduction to the code v2
Sahana   introduction to the code v2Sahana   introduction to the code v2
Sahana introduction to the code v2
 

More from ShaiAlmog1

More from ShaiAlmog1 (20)

The Duck Teaches Learn to debug from the masters. Local to production- kill ...
The Duck Teaches  Learn to debug from the masters. Local to production- kill ...The Duck Teaches  Learn to debug from the masters. Local to production- kill ...
The Duck Teaches Learn to debug from the masters. Local to production- kill ...
 
create-netflix-clone-06-client-ui.pdf
create-netflix-clone-06-client-ui.pdfcreate-netflix-clone-06-client-ui.pdf
create-netflix-clone-06-client-ui.pdf
 
create-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-01-introduction_transcript.pdfcreate-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-01-introduction_transcript.pdf
 
create-netflix-clone-02-server_transcript.pdf
create-netflix-clone-02-server_transcript.pdfcreate-netflix-clone-02-server_transcript.pdf
create-netflix-clone-02-server_transcript.pdf
 
create-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdfcreate-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdf
 
create-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdfcreate-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdf
 
create-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdfcreate-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdf
 
create-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdfcreate-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdf
 
create-netflix-clone-04-server-continued.pdf
create-netflix-clone-04-server-continued.pdfcreate-netflix-clone-04-server-continued.pdf
create-netflix-clone-04-server-continued.pdf
 
create-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-05-client-model_transcript.pdfcreate-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-05-client-model_transcript.pdf
 
create-netflix-clone-03-server_transcript.pdf
create-netflix-clone-03-server_transcript.pdfcreate-netflix-clone-03-server_transcript.pdf
create-netflix-clone-03-server_transcript.pdf
 
create-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdfcreate-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdf
 
create-netflix-clone-05-client-model.pdf
create-netflix-clone-05-client-model.pdfcreate-netflix-clone-05-client-model.pdf
create-netflix-clone-05-client-model.pdf
 
Creating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdfCreating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdf
 
Creating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdfCreating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdf
 
Creating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdfCreating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdf
 
Creating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdfCreating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdf
 
Creating a Whatsapp Clone - Part IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdfCreating a Whatsapp Clone - Part IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdf
 
Creating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part IV.pdfCreating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part IV.pdf
 
Creating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdfCreating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdf
 

Recently uploaded

Recently uploaded (20)

TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdf
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 

Creating an Uber Clone - Part XXVI - Transcript.pdf

  • 1. Creating an Uber Clone - Part XXVI Before we proceed into the actual driver app work & push notification we need to implement all the infrastructure in the server side. I also need to implement some changes we need in the protocol.
  • 2. private String hailingFrom; private String hailingTo; private String pushToken; public RideDAO getRideDao() { return new RideDAO(id, givenName, hailingFrom, hailingTo); } public UserDAO getDao() { return new UserDAO(id, givenName, surname, phone, email, facebookId, googleId, driver, car, currentRating, latitude, longitude, direction, pushToken); } public UserDAO getPartialDao() { return new UserDAO(id, givenName, surname, null, null, null, null, driver, car, currentRating, latitude, longitude, direction, pushToken); } User (Server) Lets start by looking at the User class. I had to add 3 new fields and modify/add some methods. hailingFrom & hailingTo allow us to communicate our trip details with the driver community
  • 3. private String hailingFrom; private String hailingTo; private String pushToken; public RideDAO getRideDao() { return new RideDAO(id, givenName, hailingFrom, hailingTo); } public UserDAO getDao() { return new UserDAO(id, givenName, surname, phone, email, facebookId, googleId, driver, car, currentRating, latitude, longitude, direction, pushToken); } public UserDAO getPartialDao() { return new UserDAO(id, givenName, surname, null, null, null, null, driver, car, currentRating, latitude, longitude, direction, pushToken); } User (Server) I need the pushToken of drivers so we can hail them directly from the app
  • 4. private String hailingFrom; private String hailingTo; private String pushToken; public RideDAO getRideDao() { return new RideDAO(id, givenName, hailingFrom, hailingTo); } public UserDAO getDao() { return new UserDAO(id, givenName, surname, phone, email, facebookId, googleId, driver, car, currentRating, latitude, longitude, direction, pushToken); } public UserDAO getPartialDao() { return new UserDAO(id, givenName, surname, null, null, null, null, driver, car, currentRating, latitude, longitude, direction, pushToken); } User (Server) I'll discuss the `RideDAO` class soon, it allows us to send details about the trip to drivers
  • 5. private String hailingFrom; private String hailingTo; private String pushToken; public RideDAO getRideDao() { return new RideDAO(id, givenName, hailingFrom, hailingTo); } public UserDAO getDao() { return new UserDAO(id, givenName, surname, phone, email, facebookId, googleId, driver, car, currentRating, latitude, longitude, direction, pushToken); } public UserDAO getPartialDao() { return new UserDAO(id, givenName, surname, null, null, null, null, driver, car, currentRating, latitude, longitude, direction, pushToken); } User (Server) Not much of a change but I added the pushToken to the UserDAO factory methods
  • 6. public class RideDAO implements Serializable { private long userId; private String name; private String from; private String destination; public RideDAO() { } public RideDAO(long userId, String name, String from, String destination) { this.userId = userId; this.name = name; if(this.name == null) { this.name = "[Unnamed User]"; } this.from = from; this.destination = destination; } // getters and setters trimmed out } RideDAO The RideDAO class is a pretty trivial object. There's no point in enumerating the details of this class as it's pretty simple. The main usage for this is in the new RideService which I will get to soon but first we need to discuss the Ride class.
  • 7. @Entity public class Ride { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Long id; @ManyToOne private User passenger; @ManyToOne private User driver; @OneToMany @OrderBy("time ASC") private Set<Waypoint> route; private BigDecimal cost; private String currency; private boolean finished; private boolean started; // trimmed out constructors, getters and setters } Ride Ride isn't as simple as RideDAO despite their common name. It contains far more information. Currently we don't use all of that but the fact that it's logged will let you provide all of that information within the app or a management app easily. The Ride class is a JPA entity similar to the User class. I used an auto-increment value for the id instead of a random string. I wanted to keep things simple but notice this can expose a security vulnerability of scanning for rides…
  • 8. @Entity public class Ride { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Long id; @ManyToOne private User passenger; @ManyToOne private User driver; @OneToMany @OrderBy("time ASC") private Set<Waypoint> route; private BigDecimal cost; private String currency; private boolean finished; private boolean started; // trimmed out constructors, getters and setters } Ride The passenger & driver are relational database references to the respective database objects representing each one of them
  • 9. @Entity public class Ride { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Long id; @ManyToOne private User passenger; @ManyToOne private User driver; @OneToMany @OrderBy("time ASC") private Set<Waypoint> route; private BigDecimal cost; private String currency; private boolean finished; private boolean started; // trimmed out constructors, getters and setters } Ride The route itself is a set of waypoints sorted by the time associated with the given waypoint. We'll discuss waypoints soon enough but technically it's just a set of coordinates
  • 10. @Entity public class Ride { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Long id; @ManyToOne private User passenger; @ManyToOne private User driver; @OneToMany @OrderBy("time ASC") private Set<Waypoint> route; private BigDecimal cost; private String currency; private boolean finished; private boolean started; // trimmed out constructors, getters and setters } Ride I really oversimplified the cost field. It should work for sum and currency but it's usually not as simple as that. It's important to use something like BigDecimal and not double when dealing with financial numbers as double is built for scientific usage and has rounding errors
  • 11. @Entity public class Ride { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Long id; @ManyToOne private User passenger; @ManyToOne private User driver; @OneToMany @OrderBy("time ASC") private Set<Waypoint> route; private BigDecimal cost; private String currency; private boolean finished; private boolean started; // trimmed out constructors, getters and setters } Ride We have two boolean flags, a ride is started once a passenger is picked up. It’s finished once he is dropped off or if the ride was canceled
  • 12. public interface RideRepository extends CrudRepository<Ride, Long> { @Query("select b from Ride b where b.finished = false and b.driver.id = ?1") public List<Ride> findByNotFinishedUser(long id); } RideRepository The companion CRUD RideRepository is pretty standard with one big exception. I added a special case finder that lets us locate the User that is currently hailing a car. Notice the syntax b.driver.id = ?1 which points through the relation to the driver object.
  • 13. @Entity public class Waypoint { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Long id; private long time; private double latitude; private double longitude; private float direction; // trimmed out constructors, getters and setters } Waypoint The Waypoint entity referenced from the Ride entity is pretty trivial. Notice we still need a unique id for a waypoint even if we don't actually use it in code... The interesting part here is the time value which is the value of System.currentTimeMillis(). This allows us to build a path based on the time sequence. It will also allow us to reconstruct a trip and generate additional details such as speed/cost if we wish to do that in the future. Notice that there is also a WaypointRepository interface. I’m skipping it as it contains no actual code
  • 14. @Service public class RideService { @Autowired private UserRepository users; @Autowired private RideRepository rides; @Transactional public UserDAO hailCar(String token, boolean h, String from, String to) { User u = users.findByAuthToken(token).get(0); if(h) { if(u.getAssignedUser() != null) { long driverId = u.getAssignedUser(); u.setAssignedUser(null); users.save(u); User driver = users.findOne(driverId); return driver.getPartialDao(); } } else { u.setAssignedUser(null); } u.setHailing(h); u.setHailingFrom(from); u.setHailingTo(to); users.save(u); return null; } RideService The RideService class serves the same purpose as the UserService class focusing on rides and driver related features. I could have just stuck all of this logic into one huge class but separating functionality to different service classes based on logic makes sense. We manipulate both the rides and users CRUD objects from this class
  • 15. @Service public class RideService { @Autowired private UserRepository users; @Autowired private RideRepository rides; @Transactional public UserDAO hailCar(String token, boolean h, String from, String to) { User u = users.findByAuthToken(token).get(0); if(h) { if(u.getAssignedUser() != null) { long driverId = u.getAssignedUser(); u.setAssignedUser(null); users.save(u); User driver = users.findOne(driverId); return driver.getPartialDao(); } } else { u.setAssignedUser(null); } u.setHailing(h); u.setHailingFrom(from); u.setHailingTo(to); users.save(u); return null; } RideService Hailing is a transactional method, this means that all operations within the method will either succeed or fail depending on the outcome. This is important to prevent an inconsistent state in the database
  • 16. @Service public class RideService { @Autowired private UserRepository users; @Autowired private RideRepository rides; @Transactional public UserDAO hailCar(String token, boolean h, String from, String to) { User u = users.findByAuthToken(token).get(0); if(h) { if(u.getAssignedUser() != null) { long driverId = u.getAssignedUser(); u.setAssignedUser(null); users.save(u); User driver = users.findOne(driverId); return driver.getPartialDao(); } } else { u.setAssignedUser(null); } u.setHailing(h); u.setHailingFrom(from); u.setHailingTo(to); users.save(u); return null; } RideService This method can be invoked to start and stop hailing. In this case we use the assigned user property to detect if a driver accepted the ride. If so we return the driver data to the client
  • 17. users.save(u); return null; } public RideDAO getRideData(long userId) { User u = users.findOne(userId); if(u == null) { return null; } return u.getRideDao(); } @Transactional public long acceptRide(String token, long userId) { User driver = users.findByAuthToken(token).get(0); User passenger = users.findOne(userId); if(!passenger.isHailing()) { throw new RuntimeException("Not hailing"); } passenger.setHailing(false); passenger.setAssignedUser(driver.getId()); driver.setAssignedUser(userId); users.save(driver); users.save(passenger); Ride r = new Ride(); r.setDriver(driver); r.setPassenger(passenger); rides.save(r); RideService When a driver gets a notification of a ride he invokes this method to get back the data about the ride
  • 18. users.save(u); return null; } public RideDAO getRideData(long userId) { User u = users.findOne(userId); if(u == null) { return null; } return u.getRideDao(); } @Transactional public long acceptRide(String token, long userId) { User driver = users.findByAuthToken(token).get(0); User passenger = users.findOne(userId); if(!passenger.isHailing()) { throw new RuntimeException("Not hailing"); } passenger.setHailing(false); passenger.setAssignedUser(driver.getId()); driver.setAssignedUser(userId); users.save(driver); users.save(passenger); Ride r = new Ride(); r.setDriver(driver); r.setPassenger(passenger); rides.save(r); RideService If the driver wishes to accept the ride he invokes this transactional method. The method accepts the token from the driver and the id of the user hailing the ride. It creates a new Ride entity and returns its ID, from this point on we need to refer to the Ride id and not the user id or token
  • 19. User driver = users.findByAuthToken(token).get(0); User passenger = users.findOne(userId); if(!passenger.isHailing()) { throw new RuntimeException("Not hailing"); } passenger.setHailing(false); passenger.setAssignedUser(driver.getId()); driver.setAssignedUser(userId); users.save(driver); users.save(passenger); Ride r = new Ride(); r.setDriver(driver); r.setPassenger(passenger); rides.save(r); return r.getId(); } public void startRide(long rideId) { Ride current = rides.findOne(rideId); current.setStarted(true); rides.save(current); } public void finishRide(long rideId) { Ride current = rides.findOne(rideId); current.setFinished(true); rides.save(current); } } RideService Start ride and finish ride are invoked by the driver when he picks up the passenger and when he drops him off. Normally, finish ride should also handle elements like billing etc. but I won't go into that now
  • 20. @Controller @RequestMapping("/ride") public class RideWebservice { @Autowired private RideService rides; @RequestMapping(method=RequestMethod.GET,value = "/get") public @ResponseBody RideDAO getRideData(long id) { return rides.getRideData(id); } @RequestMapping(method=RequestMethod.GET,value="/accept") public @ResponseBody String acceptRide(@RequestParam(name="token", required = true) String token, @RequestParam(name="userId", required = true) long userId) { long val = rides.acceptRide(token, userId); return "" + val; } @RequestMapping(method=RequestMethod.POST,value="/start") public @ResponseBody String startRide(@RequestParam(name="id", required = true) long rideId) { rides.startRide(rideId); return "OK"; } @RequestMapping(method=RequestMethod.POST,value="/finish") public @ResponseBody String finishRide(@RequestParam(name="id", required = true) long rideId) { rides.finishRide(rideId); return "OK"; } } RideWebservice The next step is bridging this to the user through a webservice... The RideWebservice class exposes the RideService calls almost verbatim to the client. The get call fetches the RideDAO for the given user id
  • 21. @Controller @RequestMapping("/ride") public class RideWebservice { @Autowired private RideService rides; @RequestMapping(method=RequestMethod.GET,value = "/get") public @ResponseBody RideDAO getRideData(long id) { return rides.getRideData(id); } @RequestMapping(method=RequestMethod.GET,value="/accept") public @ResponseBody String acceptRide(@RequestParam(name="token", required = true) String token, @RequestParam(name="userId", required = true) long userId) { long val = rides.acceptRide(token, userId); return "" + val; } @RequestMapping(method=RequestMethod.POST,value="/start") public @ResponseBody String startRide(@RequestParam(name="id", required = true) long rideId) { rides.startRide(rideId); return "OK"; } @RequestMapping(method=RequestMethod.POST,value="/finish") public @ResponseBody String finishRide(@RequestParam(name="id", required = true) long rideId) { rides.finishRide(rideId); return "OK"; } } RideWebservice Start and finish rides are again very simple with only one argument which is the ride id
  • 22. public void updatePushToken(String token, String pushToken) { User u = users.findByAuthToken(token).get(0); u.setPushToken(pushToken); users.save(u); } UserService We also have to add some minor changes to the UserService and LocationService classes. Lets start with the UserService class. Drivers need a push token so we can hail them. This is always set outside of the user creation code for two reasons. The first time around the user is created but the push key isn't there yet (it arrives asynchronously) Push is re-registered in every launch and refreshed, there is no reason to update the entire object for that
  • 23. @RequestMapping(method = RequestMethod.GET,value = "/setPushToken") public @ResponseBody String updatePushToken( @RequestParam(name="token", required = true) String token, @RequestParam(name="pushToken", required = true) String pushToken) { users.updatePushToken(token, pushToken); return "OK"; } UserWebservice The UserWebservice class needs to mirror these changes obviously... There isn't much here we just added a new setPushToken URL and we accept this update.
  • 24. public class LocationService { @Autowired private UserRepository users; @Autowired private RideRepository rides; @Autowired private WaypointRepository waypoints; public void updateUserLocation(String token,double lat,double lon,float dir) { List<User> us = users.findByAuthToken(token); User u = us.get(0); u.setLatitude(lat); u.setLongitude(lat); u.setDirection(dir); users.save(u); if(u.isDriver() && u.getAssignedUser() != null) { List<Ride> r = rides.findByNotFinishedUser(u.getId()); if(r != null && !r.isEmpty()) { Ride ride = r.get(0); if(ride.isStarted() && !ride.isFinished()) { Set<Waypoint> route = ride.getRoute(); Waypoint newPosition = new Waypoint( System.currentTimeMillis(), lat, lon, dir); waypoints.save(newPosition); route.add(newPosition); ride.setRoute(route); rides.save(ride); LocationService The LocationService needs a bit more work. Every time we update a users location we check if he's a driver on a ride
  • 25. public class LocationService { @Autowired private UserRepository users; @Autowired private RideRepository rides; @Autowired private WaypointRepository waypoints; public void updateUserLocation(String token,double lat,double lon,float dir) { List<User> us = users.findByAuthToken(token); User u = us.get(0); u.setLatitude(lat); u.setLongitude(lat); u.setDirection(dir); users.save(u); if(u.isDriver() && u.getAssignedUser() != null) { List<Ride> r = rides.findByNotFinishedUser(u.getId()); if(r != null && !r.isEmpty()) { Ride ride = r.get(0); if(ride.isStarted() && !ride.isFinished()) { Set<Waypoint> route = ride.getRoute(); Waypoint newPosition = new Waypoint( System.currentTimeMillis(), lat, lon, dir); waypoints.save(newPosition); route.add(newPosition); ride.setRoute(route); rides.save(ride); LocationService Assuming we have a Ride object we check if this is currently an ongoing ride that wasn't finished
  • 26. public class LocationService { @Autowired private UserRepository users; @Autowired private RideRepository rides; @Autowired private WaypointRepository waypoints; public void updateUserLocation(String token,double lat,double lon,float dir) { List<User> us = users.findByAuthToken(token); User u = us.get(0); u.setLatitude(lat); u.setLongitude(lat); u.setDirection(dir); users.save(u); if(u.isDriver() && u.getAssignedUser() != null) { List<Ride> r = rides.findByNotFinishedUser(u.getId()); if(r != null && !r.isEmpty()) { Ride ride = r.get(0); if(ride.isStarted() && !ride.isFinished()) { Set<Waypoint> route = ride.getRoute(); Waypoint newPosition = new Waypoint( System.currentTimeMillis(), lat, lon, dir); waypoints.save(newPosition); route.add(newPosition); ride.setRoute(route); rides.save(ride); LocationService If so we add a waypoint to the ride and update it so we can later on inspect the path of the Ride. This pretty much tracks rides seamlessly. If we wanted to be really smart we could detect the driver and user position to detect them traveling together and automatically handle the ride. There are obviously problems with this as it means a user can't order a cab for someone else but it might be an interesting feature since we have two close data points…