google cloud endpoints - javacro conference · motivation • dead simple web backends for multi-...

Post on 20-Apr-2020

3 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Google Cloud EndpointsEndpoints

Tomislav Čoh, Calyx d.o.o.

Summary

• Introduction

• Motivation

• Architecture• Architecture

• Demo app "Re-gifter"

• Conclusion

Introduction

• Experimental App Engine feature

• REST & RPC APIs for app backend

• Utilizes internal Google infrastructure• Utilizes internal Google infrastructure

• API explorer

• Client library generation:o Java / Android

o Objective C / iOS

o Javascript

Motivation

• Dead simple Web backends for multi-platform clients

• Utilize the power of App EngineDatastoreo Datastore

o Blobstore

o Image service

o Google OAuth2 integration

• Rich testing environment

• Enforced scalability

• Pay for what you use

Architecture

Demo appDemo appre-gifter.appspot.com

Development process• Project setup• Design data model• Develop business logic• Test business logic• Test business logic• Annotate endpoints• Test on local server• Deploy• Test deployed• Generate client libraries• Add authentication

Project setup

• Install Google Plugin for Eclipse

• Create Web Application project

• Disable DataNucleus Enhancer•o Google->App Engine->Orm

• Install Lombok

• Add Objectify to classpath

• Add Guava to classpath

• Add Lombok to classpath

• Setup Objectify

web.xml:

...<filter>

<filter-name>ObjectifyFilter</filter-name><filter-class>com.googlecode.objectify.ObjectifyFilter</filter-class>

</filter><filter-mapping><filter-name>ObjectifyFilter</filter-name>

public class OfyService {

static {factory().register(Gift.class);

}

public static final Objectify ofy() {return ObjectifyService.ofy();

}

Project setup

<filter-name>ObjectifyFilter</filter-name><url-pattern>/*</url-pattern>

</filter-mapping>...

}

public static final ObjectifyFactory factory() {return ObjectifyService.factory();

}

}

Design data model

• Schemaless datastore• Ancestor paths and entity groups• Datastore Java API•

o Datastore low-level APIo JDO/JPAo Objectifyo Twigo Slim3

@Entity@NoArgsConstructor@RequiredArgsConstructorpublic class Gift {

@Id@Getterprivate Long id;

@Parent@Setterprivate Key recipient;

Design data model

@Getter@NonNullprivate String giver;

@Getter@NonNullprivate String description;

}

Develop business logic

• Find gifts by recipient

• Insert a gift

public class Regifter {

public List<Gift> findGiftsByRecipient(String recipientEmail) {Key recipientKey = KeyFactory.createKey("Person", recipientEmail);return ofy().load().type(Gift.class).ancestor(recipientKey).list();

}

public Gift insertGift(String recipientEmail, Gift gift) {Key recipientKey = KeyFactory.createKey("Person", recipientEmail);gift.setRecipient(recipientKey);

Develop business logic

gift.setRecipient(recipientKey);ofy().save().entity(gift).now();return gift;

}

}

Test

• Create test project• Add Re-gifter project to classpath• Project dependencies:o JUnit4

•o JUnit4o ${SDK_ROOT}/lib/impl/appengine-api.jaro ${SDK_ROOT}/lib/impl/appengine-api-labs.jaro ${SDK_ROOT}/lib/impl/appengine-api-stubs.jaro ${SDK_ROOT}/lib/testing/appengine-testing.jar

• Test business logic

public class RegifterTest {private final LocalServiceTestHelper helper = new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig().setDefaultHighRepJobPolicyUnappliedJobPercentage(100));

private Regifter mRegifter;

@Beforepublic void setUp() {

helper.setUp();mRegifter = new Regifter();

}@Afterpublic void tearDown() {

helper.tearDown();}@Testpublic void allInOneTest() {

Gift gift = new Gift("jerry@gmail.com", "Cash");

Test

Gift gift = new Gift("jerry@gmail.com", "Cash");

Long giftId = mRegifter.insertGift("elaine@gmail.com", gift).getId();

List<Gift> gifts = mRegifter.findGiftsByRecipient("elaine@gmail.com");assertEquals(gifts.size(), 1);Gift loadedGift = gifts.get(0);assertEquals(gift.getDescription(), loadedGift.getDescription());assertNotNull(loadedGift.getId());assertEquals(giftId, loadedGift.getId());

}}

Annotate endpoints

• Expose REST & RPC API

• @Api - endpoint configuration

• @ApiMethod - method configuration• @ApiMethod - method configuration

@Api(name = "regifter", version = "v1")public class Regifter {

@ApiMethod(name = "gifts.list", path = "receiver/{receiverEmail}/gift", httpMethod = HttpMethod.GET)public List<Gift> findGiftsByRecipient(@Named("receiverEmail") String recipientEmail) {

Key recipientKey = KeyFactory.createKey("Person", recipientEmail);return ofy().load().type(Gift.class).ancestor(recipientKey).list();

}

@ApiMethod(name = "gifts.insert", path = "receiver/{receiverEmail}/gift", httpMethod = HttpMethod.POST)public Gift insertGift(@Named("receiverEmail") String recipientEmail, Gift gift) {

Key recipientKey = KeyFactory.createKey("Person", recipientEmail);gift.setRecipient(recipientKey);

Annotate endpoints

gift.setRecipient(recipientKey);ofy().save().entity(gift).now();return gift;

}

}

Test on local server

• Run project in Eclipse

• http://localhost:8888/_ah/api/regifter/v1/

• Mocked App Engine environment• Mocked App Engine environmento jetty server instance

o in-memory datastore

o mocked app engine services

$ curl --header "Content-Type: application/json" -X POST -d '{"giver": "cosmo@gmail.com", "description":

"Fusilli Jerry"}' http://localhost:8888/_ah/api/regifter/v1/receiver/jerry@gmail.com/gift

{

"id" : "1",

"giver" : "cosmo@gmail.com",

"description" : "Fusilli Jerry"

}

$ curl --header "Content-Type: application/json" -X POST -d '{"giver": "jerry@gmail.com", "description":

"Cash"}' http://localhost:8888/_ah/api/regifter/v1/receiver/elaine@gmail.com/gift

{

"id" : "1",

"giver" : "jerry@gmail.com",

"description" : "Cash"

}

$ curl --header "Content-Type: application/json"

http://localhost:8888/_ah/api/regifter/v1/receiver/jerry@gmail.com/gift

{

"items" : [ {

"id" : "1",

Test on local server

"id" : "1",

"giver" : "cosmo@gmail.com",

"description" : "Fusilli Jerry"

} ]

}

$ curl --header "Content-Type: application/json"

http://localhost:8888/_ah/api/regifter/v1/receiver/elaine@gmail.com/gift

{

"items" : [ {

"id" : "1",

"giver" : "jerry@gmail.com",

"description" : "Cash"

} ]

}

Deploy

• https://appengine.google.com

• Set application id

• Authorize Google Plugin for Eclipse• Authorize Google Plugin for Eclipse

• Right Click->Google->Deploy to App Engine

• ...

• Profit

Test deployed

• API Explorer

• https://re-gifter.appspot.com/_ah/api/explorer

• API discovery• API discovery

• Test OAuth2 authorized APIs

Test deployed

Generate client libraries• Right Click->Google->Generate Cloud Endpoint Client Library

• App Engine Connected Android Project

o Google Cloud Endpoints

o Google Cloud Messagingo Google Cloud Messaging

Android:Regifter.Builder builder = new Regifter.Builder(AndroidHttp.newCompatibleTransport(), new

GsonFactory(), null);

Regifter mRegifter = builder.build();

Gift fusilliJerry = new Gift();

fusilliJerry.setGiver("cosmo@gmail.com");

fusilliJerry.setDescription("Fusilli Jerry");

fusillyJerry = mRegifter.gifts().insert("jerry@gmail.com", fusilliJerry).execute();

JavaScript:<script>function onClientLoad() {

var ROOT = 'https://re-gifter.appspot.com/_ah/api';

gapi.client.load('regifter', 'v1', onLoad, ROOT);

Generate client libraries

gapi.client.load('regifter', 'v1', onLoad, ROOT);

}

function onLoad() {

gapi.client.regifter.gifts.insert('jerry@gmail.com', {giver: 'cosmo@gmail.com',

description: 'Fusilly Jerry'}).execute(onGiftInserted);

}

function onGiftInserted(response) {

alert('Gift given!');

}

</script>

<script src="https://apis.google.com/js/client.js?onload=onClientLoad"></script>

Add Authentication

• Create API Projecto https://code.google.com/apis/console

• Specify client ids• Specify client idso Browser, Server, Android, iOS

• Modify annotations

• Add User parameter to authorized methods

@Api(name = "regifter", version = "v1", clientIds = Constant.API_EXPLORER_CLIENT_ID)public class Regifter {

@ApiMethod(name = "gifts.list", path = "receiver/me/gift", httpMethod = HttpMethod.GET)public List<Gift> findGiftsByRecipient(User user) {

Key recipientKey = KeyFactory.createKey("Person", user.getEmail());return ofy().load().type(Gift.class).ancestor(recipientKey).list();

}

@ApiMethod(name = "gifts.insert", path = "receiver/me/gift", httpMethod = HttpMethod.POST)public Gift insertGift(User user, Gift gift) {

Key recipientKey = KeyFactory.createKey("Person", user.getEmail());gift.setRecipient(recipientKey);

Add authentication

gift.setRecipient(recipientKey);ofy().save().entity(gift).now();return gift;

}

}

Add authentication

ConclusionConclusionPros and Cons

The good parts

• Simple

• Minimal configuration

• Lightweight• Lightweight

• Integrated services

• No server maintenance

• Deploy in seconds

• Scalability implied

Drawbacks and limitations

• Experimentalo Bugs

o Breaking changes

•• Unintuitive error reporting in GPE

• Cold start delay

• Framework integration discouraged

• Third party libraries

• No custom domain for endpointso CORS browser support when using REST

• SQL support experimental

• JDO/JPA incompleteo no many-to-many owned relationships

o no join, aggregation, polymorphic queries

• Data migrationo Immutable keys

o New indices affect only new entities

Drawbacks and limitations

Thank you!Thank you!Questions?

top related