Creating a trip

This tutorial is intended for developers who are involved in the development of software to create and manage Trips. The software should typically provide a user interface which makes it able to create simple or more complex trips (from a simple button to a complex form). The software should provide contineous feedback about the state of the trips it manages as well.
At the other side of the line, there should be a Transporter who is responsible for responding to the trips, and to carry out trips according to the trips. How to handle trips is described in detail in the tutorial Handling a trip

Creating a trip is one of the easiest real-world examples of using CCP. The tutorial assumes the reader has basic knowledge about CCP, OData and REST based communication in general.

Adding a trip to the system is as easy as a HTTP POST to /Trips. The payload of de request consist of the trip properties in one of the supported formats. CCP supports three OData wire-formats: Atom and two JSON variants (verbose and light). The provided examples are in the JSON light format for readability.

Versioning

The examples use OData version "3.0" and an API version of "40.0", the most current versions at the time of writing.

Tools

The tutorial is platform, language, framework and IDE independent. For testing purposes or to simply follow along the tutorial you can use a HTTP request tools like HttpRequester for Firefox, Postman for Google Chrome, Fiddler or any other REST client.

Authentication

In this example we use Basic Authentication. The username and password should be combined like username:password, for example test@test.com:test123. This string should be converted to bytes (using UTF8-encoding) and converted back to string using Base64, and preceded by the term "Basic ". When applied to the example, it should look like "Basic dGVzdEB0ZXN0LmNvbTp0ZXN0MTIz". In every request to CCP, the header "Authorization" should be present having this value. which you will see in the request headers. The following roles have rights to create a trip: Transporter, Contractor, Agent and Traveler.

Our first Trip

Let’s create our first Trip. There are quite a lot of properties in the Trip entity. Most of these are optional and mostly used in a specific context. For our first Trip we keep things simple. We create a trip where we want an immediate pickup at our home address of "Wilhelminapark 36 TILBURG" without a destination address.

First we need to add the appropriate headers to the request

POST /CCPService/DataServiceCCP.svc/Trips HTTP/1.1
Authorization: Basic dGVzdEB0ZXN0LmNvbTp0ZXN0MTIz
Accept: application/json
Content-Type: application/json
DataServiceVersion: 3.0
MaxDataServiceVersion: 3.0
CabmanCloudPlatformVersion: 40.0
MaxCabmanCloudPlatformVersion: 40.0

Then add the fields in json format to the body

{
  "TripDateTimeTypeName" : "Immediately",
  "TripScopeName" : "To",
  "VehicleKindName" : "Taxi",
  "StatusName" : "New",
  "PickUpAddress" : {
    "Street" : "Wilhelminapark",
    "Number" : 36,
    "Town" : "TILBURG"
  }
}

There are a few extra fields required for our simple trip to complete successfully. These are the fields:

  • StatusName. Just fill it with a value of "New".
  • TripScopeName. Fill it with either "To" or "Return".
  • VehicleKindName. Fill it with either "Taxi" or "Van". CCP needs to know if the user wants a normal taxi or a larger vehicle.
  • PickupAddress. An AddressFragment complex type used in many entities.

Execute the request. The result should have an HTTP responsecode 201 (created) and the body should contain the created trip. The following headers are also part of the response:

CabmanCloudPlatformVersion: 40.0
Cache-Control: no-cache
Content-Length: 1155
Content-Type: application/json;odata=minimalmetadata;streaming=true;charset=utf-8
DataServiceVersion: 3.0;
Date: Wed, 03 Dec 2014 14:43:54 GMT
Location: https://www.cabmanonline.nl/CCPService/DataServiceCCP.svc/Trips(3198)
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Content-Type-Options: nosniff
X-Powered-By: ASP.NET

Most headers are self-explanatory and shows information about the server and the size and content-type of the returned data. The most interesting header is de location header. This header contains the fully qualified URL that points to the new resource. You can use this URL to request or update the trip we just created.
If you don't want the newly created trip to be returned in the response (e.g. to conserve bandwidth), just add HTTP request header "Prefer" with the value "return-no-content" to POST-request. In other cases the response body should look like:

{
  "odata.metadata" : "https://www.cabmanonline.nl/CCPService/DataServiceCCP.svc/$metadata#Trips/@Element",
  "TripId" : 3198,
  "PlannedPickupTime" : "2014-12-03T15:43:54.6982187+01:00",
  "PlannedDropOffTime" : null,
  "TripDateTimeTypeName" : "Immediately",
  "StatusName" : "New",
  "LastUpdated" : "2014-12-03T14:43:54.9482187Z",
  "TravelerCount" : 1,
  "Deleted" : false,
  "PhoneNumber" : null,
  "EmailAddress" : null,
  "NotificationLanguage" : null,
  "Remark" : null,
  "Reference" : null,
  "FlightNumber" : null,
  "FlightDateTime": null,
  "FlightOrigin": null,
  "PaymentTypeName" : null,
  "DayCode" : "D0001",
  "Amount" : null,
  "PlannedDistance" : null,
  "PlannedDuration" : null,
  "VehicleKindName" : "Taxi",
  "TravelerId" : null,
  "ContractorId" : null,
  "AgentId" : null,
  "TransporterId" : 1,
  "AssignedTransporterId" : null,
  "PickUpLocationId" : null,
  "DropOffLocationId" : null,
  "PickUpAddress" : {
    "Street" : "Wilhelminapark",
    "Number" : 36,
    "Addition" : null,
    "PostalCode" : null,
    "State" : null,
    "Town" : "TILBURG",
    "CountryId" : null,
    "Coordinates" : {
      "Latitude" : null,
      "Longitude" : null,
      "FormatName" : null
    }
  },
  "DropOffAddress" : {
    "Street" : null,
    "Number" : null,
    "Addition" : null,
    "PostalCode" : null,
    "State" : null,
    "Town" : null,
    "CountryId" : null,
    "Coordinates" : {
      "Latitude" : null,
      "Longitude" : null,
      "FormatName" : null
    }
  }
}

Locating the trip in Cabman Online shows the same information

The response has a number of extra fields that are automatically filled by CCP. The most important ones are "TripId", "DayCode" and "TransporterId". "TripId" contains the unique identification CCP has assigned to the trip. After the POST the Trip can be requested using a GET on /Trips(<tripid>). In our example the "TripId" is 3198 so the request would be /Trips(3198). "DayCode" is a property that is automatically generated by the server. This property can be used as an identification that is easier to remember. Finally "TransporterId" contains the id of the transporter that owns the trip. Even if other roles create a trip, the corresponding "TransporterId" is assigned. This way the trip can be routed to the right transporter inside CCP. Also note that the TripDateTime field is automatically filled with the time that the trip is created in CCP. This is because the TripDateTimeTypeName property had a value of "Immediately".

Canceling or modifying a trip

It can happen that a trip has to be cancelled or details have to be changed on the trip, after the trip has been created. The following rules apply:

  • As long as the StatusName of the trip is "New", the trip can be updated of cancelled without further consequences. Examples how to execute a request to update or cancel a trip are detailed below.
    The software that is used to let the Transporter Accept or Reject the trip should always present a refreshed trip, so the possible changes are reflected as well.
  • Trips that have the StatusName "Declined" cannot be updated nor cancelled.
  • Trips that have the StatusName "Confirmed" can only be modified if none the possible TripRules are being violated. The Transporter who will execute the Trip can specify one or more TripRules to limit the possibities of updating or cancelling. For more details about TripRules, see the tutorial Handling a trip
  • It is not allowed to modify the StatusName-field of the Trip. The StatusName changes automatically as a result of the Actions that will be executed.

Update example

Let’s assume it’s allowed to change the trip (no TripRules are being violated). From our POST we received the unique TripId of our trip so we can use that to update the entity. There are two ways to update an entity using OData. PUT and MERGE/PATCH. The main difference is that PUT is a replacement update (should contain all fields) and MERGE/PATCH a differential update (containing just the modified fields). MERGE/PATCH is recommended because the request will be more compact, and it avoids the risk of reverting changes to properties that were updated in the meantime.

NOTE: There can be a problem with using MERGE or PATCH while inside a corporate firewall. Some firewalls and proxy servers block or filter these type of requests. To circumvent this behavior you can wrap a MERGE/PATCH inside a POST request using a special header to let the server know it’s not a POST but a PATCH. The header is "X-HTTP-Method" and the value is the verb you want to proxy through the POST. In this case it’s PATCH or MERGE. PATCH is the preferred verb.

In this example we’ll update a field in the trip. We’ll change the TravelerCount property from 1 to 2. The following request is sent to the server. As you can see the URI is not /Trips but pointed directly at the specific trip 3198.

PATCH /CCPService/DataServiceCCP.svc/Trips(3198) HTTP/1.1
Host: www.cabmanonline.nl
Authorization: Basic dGVzdEB0ZXN0LmNvbTp0ZXN0MTIz
Accept: application/json
Content-Type: application/json
DataServiceVersion: 3.0
MaxDataServiceVersion: 3.0
CabmanCloudPlatformVersion: 40.0
MaxCabmanCloudPlatformVersion: 40.0
{
  "TravelerCount": 2
}

When successful, the server returns a HTTP status code 204 (no content) and without anything in the body. When we request the trip using a GET you can see that the TravelerCount has changed to 2 and the other fields are not changed. The only other field that’s changed is "LastUpdated". This field is very important when synchronizing trips with your own data-store. This is outside the scope of this tutorial. There are other tutorials that use synchronization. Also note that the coordinates are calculated for our pickup address. This is done automatically by CCP.

{
  "odata.metadata" : "https://www.cabmanonline.nl/CCPService/DataServiceCCP.svc/$metadata#Trips/@Element",
  "TripId" : 3198,
  "PlannedPickupTime" : "2014-12-03T15:43:54.697",
  "PlannedDropOffTime" : null,
  "TripDateTimeTypeName" : "Immediately",
  "StatusName" : "New",
  "LastUpdated" : "2014-12-03T16:13:13.23",
  "TravelerCount" : 2,
  "Deleted" : false,
  "PhoneNumber" : null,
  "EmailAddress" : null,
  "NotificationLanguage" : null,
  "Remark" : null,
  "CostCenterId" : null,
  "Reference" : null,
  "FlightNumber" : null,
  "FlightDateTime": null,
  "FlightOrigin": null,
  "PaymentTypeName" : null,
  "DayCode" : "D0001",
  "Amount" : null,
  "PlannedDistance" : null,
  "PlannedDuration" : null,
  "VehicleKindName" : "Taxi",
  "TravelerId" : null,
  "ContractorId" : null,
  "AgentId" : null,
  "TransporterId" : 1,
  "AssignedTransporterId" : null,
  "PickUpLocationId" : null,
  "DropOffLocationId" : null,
  "PickUpAddress" : {
    "Street" : "Wilhelminapark",
    "Number" : 36,
    "Addition" : null,
    "PostalCode" : null,
    "State" : null,
    "Town" : "TILBURG",
    "CountryId" : null,
    "Coordinates" : {
      "Latitude" : 51.565360000000005,
      "Longitude" : 5.0777600000000005,
      "FormatName" : "GeoDecimal"
    }
  },
  "DropOffAddress" : {
    "Street" : null,
    "Number" : null,
    "Addition" : null,
    "PostalCode" : null,
    "State" : null,
    "Town" : null,
    "CountryId" : null,
    "Coordinates" : {
      "Latitude" : null,
      "Longitude" : null,
      "FormatName" : null
    }
  }
}

Cancel example

CCP has an Action for cancelling a trip: CancelTrip. When looking at the reference of this function this needs to be in a POST request and has 1 parameter called tripId.

To cancel our trip we need the same headers as when creating the trip

POST /CCPService/DataServiceCCP.svc/CancelTrip HTTP/1.1
Host: www.cabmanonline.nl
Authorization: Basic dGVzdEB0ZXN0LmNvbTp0ZXN0MTIz
Accept: application/json
Content-Type: application/json
DataServiceVersion: 3.0
MaxDataServiceVersion: 3.0
CabmanCloudPlatformVersion: 40.0
MaxCabmanCloudPlatformVersion: 40.0

Include the following body

{
  "tripId": 3198
}

Execute the request and the server responds with a HTTP result 204 (no content). This confirms that the trip has been canceled. If it’s not allowed to cancel the trip, an error is returned by use of the standard http response codes depending on the type of error (response codes 300 and up).

What happens next?

After creating the trip, it needs to be processed by the Transporter, either by accepting the Trip or rejecting the trip. This process is described in the next tutorial Handling a trip.

For the creator of the Trip it might be interesting to follow these events:

  • When a Trip is rejected, its StatusName changes to "Declined". This is a final state, the Trip cannot be altered anymore.
  • When a Trip is accepted, its StatusName changes to "Confirmed", and one or more Trips are being created. Those Trips and their underlaying Movements are interesting to use as feedback to the user, as they contain a StatusName, a PlannedDistance [km] the planned pickup- and dropofftime, etc. Especially the "InTransit"-state of Trip is interesting, which indicates that the driver is on its way.

Email notifications

Users can fill in the fields EmailAddress and NotificationLanguage and they will receive an email in the given language ("en","nl" or "de") when the state of their trip changes.