Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
Google Cloud Endpoints: Building Third-Party APIs on Google AppEngine
Google Cloud Endpoints
Third-partyAPIson GoogleAppEngine
Roman "sgzmd" Kirillov - Google
Developing on the Google Cloud Platform with Java - 2013-07-10
Hello World
Some introductions are in order
#GoogleCloudPlatform
If you have a question, raise a hand.
There will be dedicated Q&A time at the end of the lecture
All source code will be provided in the form of a GitHub link
·
·
·
3/39
Today's plan
Does everyone know what a RESTful web API is?
#GoogleCloudPlatform
A little bit of theory. Why another solution for APIs?
Building an API on AppEngine: what does it take?
Building a simple client for our API
·
·
·
4/39
What are RESTful APIs (quick recap)
#GoogleCloudPlatform
A web service, which uses HTTP as a transport
Central idea: there are resources which we want to export
Resources are sent back and forth using their representation
REST = representational state transfer
Four main operations, mapped to HTTP methods:
·
·
·
·
·
GET – read or list the data
POST – add new data
PUT – update existing data
DELETE – as follows from the name
·
·
·
·
See also: wiki:Representation State Transfer
5/39
REST API example
(really primitive one)
Say, we have a book:
We want to:
#GoogleCloudPlatform
{
"name": "TheHitchhiker'sGuidetotheGalaxy"
}
JSON
Add it to the book collection
Retrieve it from there
·
·
6/39
REST API example
Adding new data
#GoogleCloudPlatform
curl -d "{'name': 'The Hitchhiker's Guide to the Galaxy'}" 
https://sample-restful-api.appspot.com/_ah/api/bookendpoint/v1/book 
-X POST 
-H "Accept: application/json" 
-H "Content-type: application/json"
{
"key": {
"kind": "Book",
"id": "42",
},
"name": "The Hitchhiker's Guide to the Galaxy",
}
Sending data to the URL using HTTP POST
Request and response are in JSON
·
·
7/39
REST API example
Listing the data
Note: /bookendpoint/v1/bookis a collection URL and can be used to list the data. If we want to
read the data about specific book, we would use Element URL, like /bookendpoint/v1/book/42
#GoogleCloudPlatform
curl https://sample-restful-api.appspot.com/_ah/api/bookendpoint/v1/book 
-X GET -H "Accept: application/json"
{
"items": [
{
"key": {
"kind": "Book",
"id": "42",
},
"name": "The Hitchhiker's Guide to the Galaxy",
},
],
}
8/39
Better example: home monitoring
(part of a real open source project!)
UI mocks
What do we want to get in the end
#GoogleCloudPlatform 10/39
Implementation outline
How do we get there
#GoogleCloudPlatform
Defining and implementing a model of our application
Defining an interface of the API
Implementing API endpoint
Generating client libraries
Implementing a client in JS
Implementing an Android client
·
·
·
·
·
·
11/39
Application data model
#GoogleCloudPlatform
We will define a POJO and spice it up with some JDO
Using JDO isn't mandatory – there are other options available
JDO is closest to what you see in AppEngine for Python
·
·
·
12/39
Data model
Defining data object for Sensor
#GoogleCloudPlatform
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Sensor {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
// Uniquely identifies sensor in home network
@Persistent @Unique private String networkId;
// Sensor's data is being used
@Persistent private Boolean active;
// Last time sensor fired
@Persistent private Long lastActive = 0L;
// Motion, temperature, etc.
@Persistent private SensorType sensorType;
}
JAVA
13/39
Data model
Defining data object for Room
...and this is pretty much it for the data.
#GoogleCloudPlatform
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Room {
private static final Instant NEVER = new Instant(0);
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
@Persistent
private String name;
@Persistent
@Element(dependent = "true")
private List<Sensor> sensors;
}
JAVA
14/39
UI mocks
#GoogleCloudPlatform 15/39
API interface
Think of this as (almost) pseudocode
#GoogleCloudPlatform
classApiBackend{
List<Room>listRooms(){}
RoomaddRoom(Roomroom){}
voiddeleteRoom(RoomIdroomId){}
RoomupdateRoom(RoomIdroomId,Roomroom){}
RoomaddSensor(RoomIdroomId,Sensorsensor){}
voidsensorUpdated(SensorNetworkIdnetworkId){}
voidarm(RoomIdroomId){}
voiddisarm(RoomIdroomId){}
voidarm(){}
voiddisarm(){}
}
JAVA
16/39
API implementation
Code for API will look much like any other Java code using JDO. In fact, you
can use your existing backend code.
#GoogleCloudPlatform
publicRoomupdateRoom(
Named("room")LongroomId,RoomupdatedRoom){
PersistenceManagerpm=getPM();
try{
Roomroom=(Room)pm.getObjectById(
Room.class,roomId);
room.updateFrom(updatedRoom);
returnroom;
}finally{
pm.close();
}
}
JAVA
17/39
Adding API annotations
Converting AppEngine backend to REST web service
And this is pretty much it for API implementation.
#GoogleCloudPlatform
@ApiMethod(
name="updateRoom",
httpMethod="PUT",
path="rooms/{room}")
publicRoomupdateRoom(
@Named("room")LongroomId,
RoomupdatedRoom){
//...
}
JAVA
18/39
Let's take it for a ride
Note: you can do the same thing against API running locally.
#GoogleCloudPlatform
roman $ export ENDPOINT="https://cloud-endpoints-example.appspot.com/_ah/api"
roman $ curl "$ENDPOINT/monitoring/v1/rooms" 
-X POST 
-H "Accept: application/json" 
-H "Content-type: application/json" 
-d "{'name': 'Bedroom'}"
{
"key": {
"kind": "Room",
"appId": "s~cloud-endpoints-example",
"id": "1001",
"complete": true
},
"name": "Bedroom",
}
19/39
Building our first client
In HTML code:
In JavaScript code:
#GoogleCloudPlatform
Loading Google JavaScript client
Initialising your client object
Using the API.
·
·
·
<script src="https://apis.google.com/js/client.js?onload=init"></script>
function init() {
// change to local API endpoint for local testing
var ROOT = 'https://cloud-endpoints-example.appspot.com/_ah/api';
gapi.client.load('monitoring', 'v1', reloadAllData, ROOT);
}
20/39
Building a first client
Calling the API: adding a new room
#GoogleCloudPlatform
varaddRoom=function(name){
//buildingJSONobjecttobesenttotheAPI
varroom={
'name':name
};
//callingtheAPI
gapi.client.monitoring.addRoom(room).execute(function(resp){
//processingtheresponse
if(resp){
reloadAllData(true);
}
});
};
JAVASCRIPT
21/39
Building a first client
cloud-endpoints-js-client.appspot.com – try it yourself.
#GoogleCloudPlatform 22/39
Stepping it up: Android client
A bit more fiddly than JavaScript client. In this section:
#GoogleCloudPlatform
Generating an Android client library
Adding a whole bunch of jars to your project
Creating a service object
Calling the API
·
·
·
·
23/39
Generating Android client library
From your project's WEB-INF directory, do:
You mostly care about this generated stuff:
Copy *jar* to your project's 'libs', and link to generated source.
#GoogleCloudPlatform
roman$~/bin/appengine-java-sdk-1.8.0/bin/endpoints.sh
get-client-libcom.sgzmd.examples.cloudendpoints.ApiBackend
cloud-endpoints-example-monitoring-v1-*-sources.jar*
monitoring-v1-generated-source/
·
·
24/39
Adding jars to Android project
From 'libs' subdirectory of the generated client, copy:
#GoogleCloudPlatform
google-api-client-1.12.0-beta.jar
google-api-client-android-1.12.0-beta.jar
google-http-client-1.12.0-beta.jar
google-http-client-android-1.12.0-beta.jar
google-http-client-gson-1.12.0-beta.jar
google-oauth-client-1.12.0-beta.jar
gson-2.1.jar
guava-jdk5-13.0.jar
jsr305-1.3.9.jar
·
·
·
·
·
·
·
·
·
Check developers.google.com/appengine/docs/java/endpoints/consume_android
25/39
Creating service object and calling API
Service object is your interface to the API
#GoogleCloudPlatform
public class MonitoringProvider {
private static final Monitoring MONITORING = new Monitoring(
AndroidHttp.newCompatibleTransport(),
new GsonFactory(),
null);
public static Monitoring get() {
return MONITORING;
}
}
// somewhere in your activity - calling ListRooms method
new AsyncTask<Void, Void, List<Room>>() {
@Override
protected List<Room> doInBackground(Void... params) {
return MonitoringProvider.get().listRooms().execute().getItems();
}
}.execute();
JAVA
26/39
Creating service object and calling API
Service object is your interface to the API
#GoogleCloudPlatform
// 'enabled' is a checkbox view in list item
enabled.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override public void onCheckedChanged(
CompoundButton buttonView, final boolean isChecked) {
new AsyncTask<Void, Void, Void>() { // network is prohibited on UI thread
@Override protected Void doInBackground(Void... params) {
try {
MonitoringProvider.get() // getting instance of service object
.arm(isChecked) // creating a request
.setSensor(sensor.getId()) // setting request parameters
.setRoom(sensor.getRoomId())
.execute(); // executing the request
} catch (IOException e) {
// your probably would want some better error handling here
Throwables.propagate(e);
}
}
}.execute();
}
});
JAVA
27/39
Better example: home monitoring
#GoogleCloudPlatform 28/39
Staying safe: authentication
(do we still have any time left?)
#GoogleCloudPlatform
Creating an API project and application keys
Modifying backend code to use authentication
Preparing your Android project to use authentication
Modifying Android code
Will involve few steps, can be somewhat fiddly, but ultimately is fairly
straightforward.
·
·
·
·
29/39
Creating API project in Google API console
Configuring new API client
"Create an OAuth 2.0 client ID..."
#GoogleCloudPlatform
New Client configuration
Installed Application
Android
Use same SHA1 you currently use to sign your Android apps. Debug key
will work, too.
You will also need a Web app key. Use localhost if you don't have a real
web app.
·
·
·
·
·
31/39
Configuring new API client
Configuring new API client
Final result should look like that:
You'll need both keys for Android app to work, but it's OK to re-use your
normal web application key for that purpose.
Backend changes
#GoogleCloudPlatform
@Api(
name = "monitoring",
version = "v2",
clientIds = {"your-android-key.googleusercontent.com"},
audiences = {"your-web-key.googleusercontent.com"})
class ApiBackend {
// ...
@ApiMethod(name = "listRooms", httpMethod = "GET", path = "rooms")
public List<Room> listRooms(User user) throws OAuthRequestException {
checkAuth(user);
// ...
}
private void checkAuth(User user) throws OAuthRequestException {
if (user == null) {
throw new OAuthRequestException("This method requires authentication");
}
}
JAVA
Updating @Api annotation for your API class. Note, that I use version v2 which can co-exist with v1
in the same application.
Adding an extra parameter to every method to be auth protected
·
·
34/39
Updating Android project
Optional step if running on Android emulator: make sure your Android Target is created using
Google APIs AVD and not just plain Android.
Note: due to variety of Android devices, it is always a good idea not to assume that Google Play
services are present on the device and check it at runtime.
#GoogleCloudPlatform
Ensure you have Google Play services installed in SDK Manager
Copy google-play-services.jar to libs directory of your project
Add following two lines to your AndroidManifest.xml:
·
·
·
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
35/39
Modify Android client code
Without going into too many details ...
Look through RoomListActivity.java – it's mostly about Auth.
#GoogleCloudPlatform
Verify Google Play Services are present on the device
Create GoogleAccountCredential using your web app key
Start Account Picker Intent to choose an account
In onActivityResult save it to Shared Preferences
Update created Credential with the account name
Use it to construct your service object
·
·
·
·
·
·
36/39
Code, docs and links
#GoogleCloudPlatform
github.com/sigizmund/cloud-endpoints-java-sample
cloud-endpoints-js-client.appspot.com
developers.google.com/appengine/docs/java/endpoints
·
·
·
37/39
<Thank You!>
g+ google.com/+RomanKirillov
twitter @sgzmd
github github.com/sigizmund
Google Cloud Endpoints: Building Third-Party APIs on Google AppEngine

More Related Content

Google Cloud Endpoints: Building Third-Party APIs on Google AppEngine

  • 2. Google Cloud Endpoints Third-partyAPIson GoogleAppEngine Roman "sgzmd" Kirillov - Google Developing on the Google Cloud Platform with Java - 2013-07-10
  • 3. Hello World Some introductions are in order #GoogleCloudPlatform If you have a question, raise a hand. There will be dedicated Q&A time at the end of the lecture All source code will be provided in the form of a GitHub link · · · 3/39
  • 4. Today's plan Does everyone know what a RESTful web API is? #GoogleCloudPlatform A little bit of theory. Why another solution for APIs? Building an API on AppEngine: what does it take? Building a simple client for our API · · · 4/39
  • 5. What are RESTful APIs (quick recap) #GoogleCloudPlatform A web service, which uses HTTP as a transport Central idea: there are resources which we want to export Resources are sent back and forth using their representation REST = representational state transfer Four main operations, mapped to HTTP methods: · · · · · GET – read or list the data POST – add new data PUT – update existing data DELETE – as follows from the name · · · · See also: wiki:Representation State Transfer 5/39
  • 6. REST API example (really primitive one) Say, we have a book: We want to: #GoogleCloudPlatform { "name": "TheHitchhiker'sGuidetotheGalaxy" } JSON Add it to the book collection Retrieve it from there · · 6/39
  • 7. REST API example Adding new data #GoogleCloudPlatform curl -d "{'name': 'The Hitchhiker's Guide to the Galaxy'}" https://sample-restful-api.appspot.com/_ah/api/bookendpoint/v1/book -X POST -H "Accept: application/json" -H "Content-type: application/json" { "key": { "kind": "Book", "id": "42", }, "name": "The Hitchhiker's Guide to the Galaxy", } Sending data to the URL using HTTP POST Request and response are in JSON · · 7/39
  • 8. REST API example Listing the data Note: /bookendpoint/v1/bookis a collection URL and can be used to list the data. If we want to read the data about specific book, we would use Element URL, like /bookendpoint/v1/book/42 #GoogleCloudPlatform curl https://sample-restful-api.appspot.com/_ah/api/bookendpoint/v1/book -X GET -H "Accept: application/json" { "items": [ { "key": { "kind": "Book", "id": "42", }, "name": "The Hitchhiker's Guide to the Galaxy", }, ], } 8/39
  • 9. Better example: home monitoring (part of a real open source project!)
  • 10. UI mocks What do we want to get in the end #GoogleCloudPlatform 10/39
  • 11. Implementation outline How do we get there #GoogleCloudPlatform Defining and implementing a model of our application Defining an interface of the API Implementing API endpoint Generating client libraries Implementing a client in JS Implementing an Android client · · · · · · 11/39
  • 12. Application data model #GoogleCloudPlatform We will define a POJO and spice it up with some JDO Using JDO isn't mandatory – there are other options available JDO is closest to what you see in AppEngine for Python · · · 12/39
  • 13. Data model Defining data object for Sensor #GoogleCloudPlatform @PersistenceCapable(identityType = IdentityType.APPLICATION) public class Sensor { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; // Uniquely identifies sensor in home network @Persistent @Unique private String networkId; // Sensor's data is being used @Persistent private Boolean active; // Last time sensor fired @Persistent private Long lastActive = 0L; // Motion, temperature, etc. @Persistent private SensorType sensorType; } JAVA 13/39
  • 14. Data model Defining data object for Room ...and this is pretty much it for the data. #GoogleCloudPlatform @PersistenceCapable(identityType = IdentityType.APPLICATION) public class Room { private static final Instant NEVER = new Instant(0); @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; @Persistent private String name; @Persistent @Element(dependent = "true") private List<Sensor> sensors; } JAVA 14/39
  • 16. API interface Think of this as (almost) pseudocode #GoogleCloudPlatform classApiBackend{ List<Room>listRooms(){} RoomaddRoom(Roomroom){} voiddeleteRoom(RoomIdroomId){} RoomupdateRoom(RoomIdroomId,Roomroom){} RoomaddSensor(RoomIdroomId,Sensorsensor){} voidsensorUpdated(SensorNetworkIdnetworkId){} voidarm(RoomIdroomId){} voiddisarm(RoomIdroomId){} voidarm(){} voiddisarm(){} } JAVA 16/39
  • 17. API implementation Code for API will look much like any other Java code using JDO. In fact, you can use your existing backend code. #GoogleCloudPlatform publicRoomupdateRoom( Named("room")LongroomId,RoomupdatedRoom){ PersistenceManagerpm=getPM(); try{ Roomroom=(Room)pm.getObjectById( Room.class,roomId); room.updateFrom(updatedRoom); returnroom; }finally{ pm.close(); } } JAVA 17/39
  • 18. Adding API annotations Converting AppEngine backend to REST web service And this is pretty much it for API implementation. #GoogleCloudPlatform @ApiMethod( name="updateRoom", httpMethod="PUT", path="rooms/{room}") publicRoomupdateRoom( @Named("room")LongroomId, RoomupdatedRoom){ //... } JAVA 18/39
  • 19. Let's take it for a ride Note: you can do the same thing against API running locally. #GoogleCloudPlatform roman $ export ENDPOINT="https://cloud-endpoints-example.appspot.com/_ah/api" roman $ curl "$ENDPOINT/monitoring/v1/rooms" -X POST -H "Accept: application/json" -H "Content-type: application/json" -d "{'name': 'Bedroom'}" { "key": { "kind": "Room", "appId": "s~cloud-endpoints-example", "id": "1001", "complete": true }, "name": "Bedroom", } 19/39
  • 20. Building our first client In HTML code: In JavaScript code: #GoogleCloudPlatform Loading Google JavaScript client Initialising your client object Using the API. · · · <script src="https://apis.google.com/js/client.js?onload=init"></script> function init() { // change to local API endpoint for local testing var ROOT = 'https://cloud-endpoints-example.appspot.com/_ah/api'; gapi.client.load('monitoring', 'v1', reloadAllData, ROOT); } 20/39
  • 21. Building a first client Calling the API: adding a new room #GoogleCloudPlatform varaddRoom=function(name){ //buildingJSONobjecttobesenttotheAPI varroom={ 'name':name }; //callingtheAPI gapi.client.monitoring.addRoom(room).execute(function(resp){ //processingtheresponse if(resp){ reloadAllData(true); } }); }; JAVASCRIPT 21/39
  • 22. Building a first client cloud-endpoints-js-client.appspot.com – try it yourself. #GoogleCloudPlatform 22/39
  • 23. Stepping it up: Android client A bit more fiddly than JavaScript client. In this section: #GoogleCloudPlatform Generating an Android client library Adding a whole bunch of jars to your project Creating a service object Calling the API · · · · 23/39
  • 24. Generating Android client library From your project's WEB-INF directory, do: You mostly care about this generated stuff: Copy *jar* to your project's 'libs', and link to generated source. #GoogleCloudPlatform roman$~/bin/appengine-java-sdk-1.8.0/bin/endpoints.sh get-client-libcom.sgzmd.examples.cloudendpoints.ApiBackend cloud-endpoints-example-monitoring-v1-*-sources.jar* monitoring-v1-generated-source/ · · 24/39
  • 25. Adding jars to Android project From 'libs' subdirectory of the generated client, copy: #GoogleCloudPlatform google-api-client-1.12.0-beta.jar google-api-client-android-1.12.0-beta.jar google-http-client-1.12.0-beta.jar google-http-client-android-1.12.0-beta.jar google-http-client-gson-1.12.0-beta.jar google-oauth-client-1.12.0-beta.jar gson-2.1.jar guava-jdk5-13.0.jar jsr305-1.3.9.jar · · · · · · · · · Check developers.google.com/appengine/docs/java/endpoints/consume_android 25/39
  • 26. Creating service object and calling API Service object is your interface to the API #GoogleCloudPlatform public class MonitoringProvider { private static final Monitoring MONITORING = new Monitoring( AndroidHttp.newCompatibleTransport(), new GsonFactory(), null); public static Monitoring get() { return MONITORING; } } // somewhere in your activity - calling ListRooms method new AsyncTask<Void, Void, List<Room>>() { @Override protected List<Room> doInBackground(Void... params) { return MonitoringProvider.get().listRooms().execute().getItems(); } }.execute(); JAVA 26/39
  • 27. Creating service object and calling API Service object is your interface to the API #GoogleCloudPlatform // 'enabled' is a checkbox view in list item enabled.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged( CompoundButton buttonView, final boolean isChecked) { new AsyncTask<Void, Void, Void>() { // network is prohibited on UI thread @Override protected Void doInBackground(Void... params) { try { MonitoringProvider.get() // getting instance of service object .arm(isChecked) // creating a request .setSensor(sensor.getId()) // setting request parameters .setRoom(sensor.getRoomId()) .execute(); // executing the request } catch (IOException e) { // your probably would want some better error handling here Throwables.propagate(e); } } }.execute(); } }); JAVA 27/39
  • 28. Better example: home monitoring #GoogleCloudPlatform 28/39
  • 29. Staying safe: authentication (do we still have any time left?) #GoogleCloudPlatform Creating an API project and application keys Modifying backend code to use authentication Preparing your Android project to use authentication Modifying Android code Will involve few steps, can be somewhat fiddly, but ultimately is fairly straightforward. · · · · 29/39
  • 30. Creating API project in Google API console
  • 31. Configuring new API client "Create an OAuth 2.0 client ID..." #GoogleCloudPlatform New Client configuration Installed Application Android Use same SHA1 you currently use to sign your Android apps. Debug key will work, too. You will also need a Web app key. Use localhost if you don't have a real web app. · · · · · 31/39
  • 33. Configuring new API client Final result should look like that: You'll need both keys for Android app to work, but it's OK to re-use your normal web application key for that purpose.
  • 34. Backend changes #GoogleCloudPlatform @Api( name = "monitoring", version = "v2", clientIds = {"your-android-key.googleusercontent.com"}, audiences = {"your-web-key.googleusercontent.com"}) class ApiBackend { // ... @ApiMethod(name = "listRooms", httpMethod = "GET", path = "rooms") public List<Room> listRooms(User user) throws OAuthRequestException { checkAuth(user); // ... } private void checkAuth(User user) throws OAuthRequestException { if (user == null) { throw new OAuthRequestException("This method requires authentication"); } } JAVA Updating @Api annotation for your API class. Note, that I use version v2 which can co-exist with v1 in the same application. Adding an extra parameter to every method to be auth protected · · 34/39
  • 35. Updating Android project Optional step if running on Android emulator: make sure your Android Target is created using Google APIs AVD and not just plain Android. Note: due to variety of Android devices, it is always a good idea not to assume that Google Play services are present on the device and check it at runtime. #GoogleCloudPlatform Ensure you have Google Play services installed in SDK Manager Copy google-play-services.jar to libs directory of your project Add following two lines to your AndroidManifest.xml: · · · <uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.USE_CREDENTIALS" /> 35/39
  • 36. Modify Android client code Without going into too many details ... Look through RoomListActivity.java – it's mostly about Auth. #GoogleCloudPlatform Verify Google Play Services are present on the device Create GoogleAccountCredential using your web app key Start Account Picker Intent to choose an account In onActivityResult save it to Shared Preferences Update created Credential with the account name Use it to construct your service object · · · · · · 36/39
  • 37. Code, docs and links #GoogleCloudPlatform github.com/sigizmund/cloud-endpoints-java-sample cloud-endpoints-js-client.appspot.com developers.google.com/appengine/docs/java/endpoints · · · 37/39
  • 38. <Thank You!> g+ google.com/+RomanKirillov twitter @sgzmd github github.com/sigizmund