SlideShare a Scribd company logo
1 of 14
Download to read offline
Restaurant Server
I skipped the server creation tutorial before as I felt it left the domain of Codename One too much and ventured into server programming. In this level we don’t skip such
things so I’m going to review the server code related to the restaurant app and why it was built like that.
Restaurant Server
✦ I will not go into all of the details because many
pieces are mundane and boring
✦ The server portion is relatively easier when we have a
client ready but there is a lot of hidden complexity
✦ This is especially true when it comes to security
which is often unintuitive
© Codename One 2017 all rights reserved
I find server programming to be boring. That’s not a bad thing, boring can be great when you need something you can rely on which is exactly what a good server should
be. I’ll try to focus on the interesting pieces only and I won’t even mention some of the entities or their relations as they are just “more of the same”. There are some great
Java EE and Spring Boot courses out there and I don’t think it’s our place to displace them.

Because we did the client first the server portion is mostly filling in the blanks, there are still some hidden complex nuggets but it’s mostly simple.

Security is one of the more challenging things about server programming, I’ll try to explain as much as I can but once you go into security there is always more to know…
It’s a specialty in its own right.
Standard Spring Boot project
✦We’ll create pretty much the same spring boot project
we created in the spring boot tutorial
✦I’ll go with a new Database URL to start fresh and
new project name “AppBackendServer”. The generic
name makes sense for the app maker…
✦My goal isn’t to win an “architecture award” but to
create the bare minimum server within reason
© Codename One 2017 all rights reserved
Our starting point will be the same spring boot project I did in the base course. 

I’ll use mysql again and create a blank database. We’ll use app backend server as the project name. The same database & project will be used for the app maker as well
as the app itself.

There are some nuances in Spring such as service components which are similar to sessions beans in Java EE. I’m not using any of those things which would make
sense in an architectural standpoint but aren’t really useful for our narrow use case.
<dependency>
<groupId>com.braintreepayments.gateway</groupId>
<artifactId>braintree-java</artifactId>
<version>2.71.0</version>
</dependency>
BrainTree Support - pom.xml
Lets start right away, assuming the project was created adding braintree support to the server side starts with editing the pom file with the right dependency. This will
allow us to compile the code that supports braintree
@Controller
@RequestMapping("/braintree")
public class BraintreePaymentService {
@Autowired
private RestaurantRepository restaurantRepository;
@Autowired
private OrderRepository orderRepository;
private WeakHashMap<String, BraintreeGateway> gatewayCache = new WeakHashMap<>();
private BraintreeGateway getGateway(String auth) {
BraintreeGateway bg = gatewayCache.get(auth);
if(bg != null) {
return bg;
}
RestaurantEntity r = restaurantRepository.findOne(auth);
if(r != null) {
bg = new BraintreeGateway(
Environment.SANDBOX,
r.getMerchantId(),
r.getPublicKey(),
r.getPrivateKey()
);
gatewayCache.put(auth, bg);
return bg;
}
return null;
}
BraintreePaymentService
The braintree service is pretty simple, it maps to the braintree URL. The first thing we do is provide access to the braintree gateway.
@Controller
@RequestMapping("/braintree")
public class BraintreePaymentService {
@Autowired
private RestaurantRepository restaurantRepository;
@Autowired
private OrderRepository orderRepository;
private WeakHashMap<String, BraintreeGateway> gatewayCache = new WeakHashMap<>();
private BraintreeGateway getGateway(String auth) {
BraintreeGateway bg = gatewayCache.get(auth);
if(bg != null) {
return bg;
}
RestaurantEntity r = restaurantRepository.findOne(auth);
if(r != null) {
bg = new BraintreeGateway(
Environment.SANDBOX,
r.getMerchantId(),
r.getPublicKey(),
r.getPrivateKey()
);
gatewayCache.put(auth, bg);
return bg;
}
return null;
}
BraintreePaymentService
Notice that the normal braintree API hardcodes all of these values but we load them dynamically and then place them in a cache for reuse. The reason for this is that the
braintree credentials are specific to restaurant and we will have multiple restaurants running on a single server
@RequestMapping(method=RequestMethod.GET)
public @ResponseBody String getToken(@RequestParam(name = "auth") String auth) {
return getGateway(auth).clientToken().generate();
}
@RequestMapping(method=RequestMethod.POST)
public @ResponseBody PurchaseResult purchase(@RequestBody(required=true) Order i) {
TransactionRequest request = new TransactionRequest()
.amount(i.calculateAmount())
.paymentMethodNonce(i.getNounce())
.options()
.submitForSettlement(true)
.done();
Result<Transaction> result = getGateway(i.getAuth()).transaction().sale(request);
if(result.isSuccess()) {
// Store entry and email restaurant
}
return new PurchaseResult(result.isSuccess(), result.getMessage());
}
}
BraintreePaymentService
The service methods themselves are pretty trivial
@RequestMapping(method=RequestMethod.GET)
public @ResponseBody String getToken(@RequestParam(name = "auth") String auth) {
return getGateway(auth).clientToken().generate();
}
@RequestMapping(method=RequestMethod.POST)
public @ResponseBody PurchaseResult purchase(@RequestBody(required=true) Order i) {
TransactionRequest request = new TransactionRequest()
.amount(i.calculateAmount())
.paymentMethodNonce(i.getNounce())
.options()
.submitForSettlement(true)
.done();
Result<Transaction> result = getGateway(i.getAuth()).transaction().sale(request);
if(result.isSuccess()) {
// Store entry and email restaurant
}
return new PurchaseResult(result.isSuccess(), result.getMessage());
}
}
BraintreePaymentService
The token is generated using the standard braintree API, there isn’t much here it just returns the string which we use in the client side code
@RequestMapping(method=RequestMethod.GET)
public @ResponseBody String getToken(@RequestParam(name = "auth") String auth) {
return getGateway(auth).clientToken().generate();
}
@RequestMapping(method=RequestMethod.POST)
public @ResponseBody PurchaseResult purchase(@RequestBody(required=true) Order i) {
TransactionRequest request = new TransactionRequest()
.amount(i.calculateAmount())
.paymentMethodNonce(i.getNounce())
.options()
.submitForSettlement(true)
.done();
Result<Transaction> result = getGateway(i.getAuth()).transaction().sale(request);
if(result.isSuccess()) {
// Store entry and email restaurant
}
return new PurchaseResult(result.isSuccess(), result.getMessage());
}
}
BraintreePaymentService
The interesting method is the purchase method where we create a purchase request and finally fill in the amount we want to pay.

This isn’t obvious from the code here but we have a small security issue in this code…
@RequestMapping(method=RequestMethod.GET)
public @ResponseBody String getToken(@RequestParam(name = "auth") String auth) {
return getGateway(auth).clientToken().generate();
}
@RequestMapping(method=RequestMethod.POST)
public @ResponseBody PurchaseResult purchase(@RequestBody(required=true) Order i) {
TransactionRequest request = new TransactionRequest()
.amount(i.calculateAmount())
.paymentMethodNonce(i.getNounce())
.options()
.submitForSettlement(true)
.done();
Result<Transaction> result = getGateway(i.getAuth()).transaction().sale(request);
if(result.isSuccess()) {
// Store entry and email restaurant
}
return new PurchaseResult(result.isSuccess(), result.getMessage());
}
}
BraintreePaymentService
Right here… Calculate amount runs on the order which we received from the client. This means that the prices of the elements in the order were also received from the
client.

Our client is theoretically trustworthy but if someone reverse engineers the communication protocol or the client they could just change the prices of the dishes to give
themselves a discount. However, if we just take the dish ID and calculate the price on the server this will make no difference…

This doesn’t even have to be malicious, if a price was updated and a local copy of the app still has the old price because a user hasn’t launched the app in a year. It’s
important to make these calculations in the server always. In the final version of the server this is fixed but I kept it for now in the interest of simplicity and also to
demonstrate how nuanced security issues can be.
@Entity
@Table(name="Restaurant")
public class RestaurantEntity {
@Id
private String id;
private String name;
private String merchantId;
private String publicKey;
private String privateKey;
private double currentVersionIOS = 1.0;
private double lastSupportedVersionIOS = 0;
private double currentVersionAnd = 1.0;
private double lastSupportedVersionAnd = 0;
private double currentVersionUWP = 1.0;
private double lastSupportedVersionUWP = 0;
private long dishListUpdateTimestamp = 1;
private String restaurantEmail;
@OneToMany
private Set<DishEntity> dishes;
@OneToMany
private Set<CategoryEntity> categories;
public RestaurantEntity() {
id = UUID.randomUUID().toString();
}
RestaurantEntity
This is the basic restaurant entity. It allows us to save the details we need of the restaurant. We’ll add a lot more into this class but for now all we need are these things. 

Notice that instead of using a numeric auto generated id I chose to use a string which I’ve set to a universal unique identifier in the constructor. 

Numeric id’s are sometimes problematic as one can potentially guess the id’s and scan your database. E.g. if someone notices that we have an API called listDishes that
accepts the restaurant id this might be a problem. He could scrape the API for all the dishes of all the restaurants by scanning integer values.

Scanning UUID values will take centuries or even eons.
@Entity
@Table(name="Purchases")
public class OrderEntity {
@Id
private String id;
@OneToMany
private Set<OrderLineEntity> dishQuanity;
private Date date;
private String notes;
public OrderEntity() {
id = UUID.randomUUID().toString();
}
OrderEntity vs. dao Order
public class Order {
private String id;
private Date date;
private boolean purchased;
private Map<Dish, Integer> dishQuantity = new
HashMap<>();
private String notes;
private String nounce;
private String auth;
public BigDecimal calculateAmount() {
BigDecimal b = BigDecimal.ZERO;
for(Map.Entry<Dish, Integer> d :
dishQuantity.entrySet()) {
b = b.add(new BigDecimal(
d.getValue() *
d.getKey().
getPrice()));
}
return b;
}
When looking at the code you will see some duplication. In this example we can see the order entity next to the order DAO. 

You will notice that these values are very similar to one another, a DAO transfers data from the client or within the server and an entity stores the data. They are often
mirror images of one another but not always. In this case we can see that the DAO contains things that aren’t a part of the order.

Also the structure is different since the entity needs to map to storage structure and the DAO is designed for our convenience. This is a common practice that saves the
need to pass entities around and break the separation of the tiers within the application. It allows us to have some flexibility with our data.
@Controller
@RequestMapping("/ver")
public class VersionService {
@Autowired
private RestaurantRepository restaurantRepository;
@RequestMapping(method=RequestMethod.GET)
public @ResponseBody AppVersion getKey(@RequestParam(value="os", required=true) String os,
@RequestParam(value="appId", required=true) String appId) {
RestaurantEntity r = restaurantRepository.findOne(appId);
switch(os) {
case "ios":
return new AppVersion(r.getCurrentVersionIOS(), r.getLastSupportedVersionIOS());
case "and":
return new AppVersion(r.getCurrentVersionAnd(), r.getLastSupportedVersionAnd());
case "win":
return new AppVersion(r.getCurrentVersionUWP(), r.getLastSupportedVersionUWP());
}
return null;
}
}
VersionService
The versioning service allows us to detect if a new version of the app is available and effectively allows us to disable outdated versions. This is important as we might be
stuck with an out of date protocol that we wish to retire or we might need to change the server address.
Thank You
Thanks for watching. I hope you found this informative

More Related Content

Similar to Restaurant Server - Transcript.pdf

Esposito Ajax Remote
Esposito Ajax RemoteEsposito Ajax Remote
Esposito Ajax Remote
ask bills
 

Similar to Restaurant Server - Transcript.pdf (20)

Server side rendering with React and Symfony
Server side rendering with React and SymfonyServer side rendering with React and Symfony
Server side rendering with React and Symfony
 
Esposito Ajax Remote
Esposito Ajax RemoteEsposito Ajax Remote
Esposito Ajax Remote
 
SpringOne Tour: Spring Recipes: A Collection of Common-Sense Solutions
SpringOne Tour: Spring Recipes: A Collection of Common-Sense SolutionsSpringOne Tour: Spring Recipes: A Collection of Common-Sense Solutions
SpringOne Tour: Spring Recipes: A Collection of Common-Sense Solutions
 
How to perform debounce in react
How to perform debounce in reactHow to perform debounce in react
How to perform debounce in react
 
Jakość dostarczanego oprogramowania oparta o testy
Jakość dostarczanego oprogramowania oparta o testyJakość dostarczanego oprogramowania oparta o testy
Jakość dostarczanego oprogramowania oparta o testy
 
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
 
Angular resolver tutorial
Angular resolver tutorialAngular resolver tutorial
Angular resolver tutorial
 
Testing C# and ASP.net using Ruby
Testing C# and ASP.net using RubyTesting C# and ASP.net using Ruby
Testing C# and ASP.net using Ruby
 
Itb 2021 - Bulding Quick APIs by Gavin Pickin
Itb 2021 - Bulding Quick APIs by Gavin PickinItb 2021 - Bulding Quick APIs by Gavin Pickin
Itb 2021 - Bulding Quick APIs by Gavin Pickin
 
Apache Aries Blog Sample
Apache Aries Blog SampleApache Aries Blog Sample
Apache Aries Blog Sample
 
WCF - In a Week
WCF - In a WeekWCF - In a Week
WCF - In a Week
 
Salesforce and sap integration
Salesforce and sap integrationSalesforce and sap integration
Salesforce and sap integration
 
REST API for your WP7 App
REST API for your WP7 AppREST API for your WP7 App
REST API for your WP7 App
 
Testing ASP.net Web Applications using Ruby
Testing ASP.net Web Applications using RubyTesting ASP.net Web Applications using Ruby
Testing ASP.net Web Applications using Ruby
 
EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...
EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...
EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...
 
Server Side Swift - AppBuilders 2017
Server Side Swift - AppBuilders 2017Server Side Swift - AppBuilders 2017
Server Side Swift - AppBuilders 2017
 
Dependency injection - the right way
Dependency injection - the right wayDependency injection - the right way
Dependency injection - the right way
 
ATAGTR2017 CDC Tests - Integration Tests cant be made simpler than this!
ATAGTR2017 CDC Tests - Integration Tests cant be made simpler than this!ATAGTR2017 CDC Tests - Integration Tests cant be made simpler than this!
ATAGTR2017 CDC Tests - Integration Tests cant be made simpler than this!
 
Data models in Angular 1 & 2
Data models in Angular 1 & 2Data models in Angular 1 & 2
Data models in Angular 1 & 2
 
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
 

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

Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
Joaquim Jorge
 

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
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
Evaluating the top large language models.pdf
Evaluating the top large language models.pdfEvaluating the top large language models.pdf
Evaluating the top large language models.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)
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
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
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
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...
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
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
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL 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
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
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
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 

Restaurant Server - Transcript.pdf

  • 1. Restaurant Server I skipped the server creation tutorial before as I felt it left the domain of Codename One too much and ventured into server programming. In this level we don’t skip such things so I’m going to review the server code related to the restaurant app and why it was built like that.
  • 2. Restaurant Server ✦ I will not go into all of the details because many pieces are mundane and boring ✦ The server portion is relatively easier when we have a client ready but there is a lot of hidden complexity ✦ This is especially true when it comes to security which is often unintuitive © Codename One 2017 all rights reserved I find server programming to be boring. That’s not a bad thing, boring can be great when you need something you can rely on which is exactly what a good server should be. I’ll try to focus on the interesting pieces only and I won’t even mention some of the entities or their relations as they are just “more of the same”. There are some great Java EE and Spring Boot courses out there and I don’t think it’s our place to displace them. Because we did the client first the server portion is mostly filling in the blanks, there are still some hidden complex nuggets but it’s mostly simple. Security is one of the more challenging things about server programming, I’ll try to explain as much as I can but once you go into security there is always more to know… It’s a specialty in its own right.
  • 3. Standard Spring Boot project ✦We’ll create pretty much the same spring boot project we created in the spring boot tutorial ✦I’ll go with a new Database URL to start fresh and new project name “AppBackendServer”. The generic name makes sense for the app maker… ✦My goal isn’t to win an “architecture award” but to create the bare minimum server within reason © Codename One 2017 all rights reserved Our starting point will be the same spring boot project I did in the base course. I’ll use mysql again and create a blank database. We’ll use app backend server as the project name. The same database & project will be used for the app maker as well as the app itself. There are some nuances in Spring such as service components which are similar to sessions beans in Java EE. I’m not using any of those things which would make sense in an architectural standpoint but aren’t really useful for our narrow use case.
  • 4. <dependency> <groupId>com.braintreepayments.gateway</groupId> <artifactId>braintree-java</artifactId> <version>2.71.0</version> </dependency> BrainTree Support - pom.xml Lets start right away, assuming the project was created adding braintree support to the server side starts with editing the pom file with the right dependency. This will allow us to compile the code that supports braintree
  • 5. @Controller @RequestMapping("/braintree") public class BraintreePaymentService { @Autowired private RestaurantRepository restaurantRepository; @Autowired private OrderRepository orderRepository; private WeakHashMap<String, BraintreeGateway> gatewayCache = new WeakHashMap<>(); private BraintreeGateway getGateway(String auth) { BraintreeGateway bg = gatewayCache.get(auth); if(bg != null) { return bg; } RestaurantEntity r = restaurantRepository.findOne(auth); if(r != null) { bg = new BraintreeGateway( Environment.SANDBOX, r.getMerchantId(), r.getPublicKey(), r.getPrivateKey() ); gatewayCache.put(auth, bg); return bg; } return null; } BraintreePaymentService The braintree service is pretty simple, it maps to the braintree URL. The first thing we do is provide access to the braintree gateway.
  • 6. @Controller @RequestMapping("/braintree") public class BraintreePaymentService { @Autowired private RestaurantRepository restaurantRepository; @Autowired private OrderRepository orderRepository; private WeakHashMap<String, BraintreeGateway> gatewayCache = new WeakHashMap<>(); private BraintreeGateway getGateway(String auth) { BraintreeGateway bg = gatewayCache.get(auth); if(bg != null) { return bg; } RestaurantEntity r = restaurantRepository.findOne(auth); if(r != null) { bg = new BraintreeGateway( Environment.SANDBOX, r.getMerchantId(), r.getPublicKey(), r.getPrivateKey() ); gatewayCache.put(auth, bg); return bg; } return null; } BraintreePaymentService Notice that the normal braintree API hardcodes all of these values but we load them dynamically and then place them in a cache for reuse. The reason for this is that the braintree credentials are specific to restaurant and we will have multiple restaurants running on a single server
  • 7. @RequestMapping(method=RequestMethod.GET) public @ResponseBody String getToken(@RequestParam(name = "auth") String auth) { return getGateway(auth).clientToken().generate(); } @RequestMapping(method=RequestMethod.POST) public @ResponseBody PurchaseResult purchase(@RequestBody(required=true) Order i) { TransactionRequest request = new TransactionRequest() .amount(i.calculateAmount()) .paymentMethodNonce(i.getNounce()) .options() .submitForSettlement(true) .done(); Result<Transaction> result = getGateway(i.getAuth()).transaction().sale(request); if(result.isSuccess()) { // Store entry and email restaurant } return new PurchaseResult(result.isSuccess(), result.getMessage()); } } BraintreePaymentService The service methods themselves are pretty trivial
  • 8. @RequestMapping(method=RequestMethod.GET) public @ResponseBody String getToken(@RequestParam(name = "auth") String auth) { return getGateway(auth).clientToken().generate(); } @RequestMapping(method=RequestMethod.POST) public @ResponseBody PurchaseResult purchase(@RequestBody(required=true) Order i) { TransactionRequest request = new TransactionRequest() .amount(i.calculateAmount()) .paymentMethodNonce(i.getNounce()) .options() .submitForSettlement(true) .done(); Result<Transaction> result = getGateway(i.getAuth()).transaction().sale(request); if(result.isSuccess()) { // Store entry and email restaurant } return new PurchaseResult(result.isSuccess(), result.getMessage()); } } BraintreePaymentService The token is generated using the standard braintree API, there isn’t much here it just returns the string which we use in the client side code
  • 9. @RequestMapping(method=RequestMethod.GET) public @ResponseBody String getToken(@RequestParam(name = "auth") String auth) { return getGateway(auth).clientToken().generate(); } @RequestMapping(method=RequestMethod.POST) public @ResponseBody PurchaseResult purchase(@RequestBody(required=true) Order i) { TransactionRequest request = new TransactionRequest() .amount(i.calculateAmount()) .paymentMethodNonce(i.getNounce()) .options() .submitForSettlement(true) .done(); Result<Transaction> result = getGateway(i.getAuth()).transaction().sale(request); if(result.isSuccess()) { // Store entry and email restaurant } return new PurchaseResult(result.isSuccess(), result.getMessage()); } } BraintreePaymentService The interesting method is the purchase method where we create a purchase request and finally fill in the amount we want to pay. This isn’t obvious from the code here but we have a small security issue in this code…
  • 10. @RequestMapping(method=RequestMethod.GET) public @ResponseBody String getToken(@RequestParam(name = "auth") String auth) { return getGateway(auth).clientToken().generate(); } @RequestMapping(method=RequestMethod.POST) public @ResponseBody PurchaseResult purchase(@RequestBody(required=true) Order i) { TransactionRequest request = new TransactionRequest() .amount(i.calculateAmount()) .paymentMethodNonce(i.getNounce()) .options() .submitForSettlement(true) .done(); Result<Transaction> result = getGateway(i.getAuth()).transaction().sale(request); if(result.isSuccess()) { // Store entry and email restaurant } return new PurchaseResult(result.isSuccess(), result.getMessage()); } } BraintreePaymentService Right here… Calculate amount runs on the order which we received from the client. This means that the prices of the elements in the order were also received from the client. Our client is theoretically trustworthy but if someone reverse engineers the communication protocol or the client they could just change the prices of the dishes to give themselves a discount. However, if we just take the dish ID and calculate the price on the server this will make no difference… This doesn’t even have to be malicious, if a price was updated and a local copy of the app still has the old price because a user hasn’t launched the app in a year. It’s important to make these calculations in the server always. In the final version of the server this is fixed but I kept it for now in the interest of simplicity and also to demonstrate how nuanced security issues can be.
  • 11. @Entity @Table(name="Restaurant") public class RestaurantEntity { @Id private String id; private String name; private String merchantId; private String publicKey; private String privateKey; private double currentVersionIOS = 1.0; private double lastSupportedVersionIOS = 0; private double currentVersionAnd = 1.0; private double lastSupportedVersionAnd = 0; private double currentVersionUWP = 1.0; private double lastSupportedVersionUWP = 0; private long dishListUpdateTimestamp = 1; private String restaurantEmail; @OneToMany private Set<DishEntity> dishes; @OneToMany private Set<CategoryEntity> categories; public RestaurantEntity() { id = UUID.randomUUID().toString(); } RestaurantEntity This is the basic restaurant entity. It allows us to save the details we need of the restaurant. We’ll add a lot more into this class but for now all we need are these things. Notice that instead of using a numeric auto generated id I chose to use a string which I’ve set to a universal unique identifier in the constructor. Numeric id’s are sometimes problematic as one can potentially guess the id’s and scan your database. E.g. if someone notices that we have an API called listDishes that accepts the restaurant id this might be a problem. He could scrape the API for all the dishes of all the restaurants by scanning integer values. Scanning UUID values will take centuries or even eons.
  • 12. @Entity @Table(name="Purchases") public class OrderEntity { @Id private String id; @OneToMany private Set<OrderLineEntity> dishQuanity; private Date date; private String notes; public OrderEntity() { id = UUID.randomUUID().toString(); } OrderEntity vs. dao Order public class Order { private String id; private Date date; private boolean purchased; private Map<Dish, Integer> dishQuantity = new HashMap<>(); private String notes; private String nounce; private String auth; public BigDecimal calculateAmount() { BigDecimal b = BigDecimal.ZERO; for(Map.Entry<Dish, Integer> d : dishQuantity.entrySet()) { b = b.add(new BigDecimal( d.getValue() * d.getKey(). getPrice())); } return b; } When looking at the code you will see some duplication. In this example we can see the order entity next to the order DAO. You will notice that these values are very similar to one another, a DAO transfers data from the client or within the server and an entity stores the data. They are often mirror images of one another but not always. In this case we can see that the DAO contains things that aren’t a part of the order. Also the structure is different since the entity needs to map to storage structure and the DAO is designed for our convenience. This is a common practice that saves the need to pass entities around and break the separation of the tiers within the application. It allows us to have some flexibility with our data.
  • 13. @Controller @RequestMapping("/ver") public class VersionService { @Autowired private RestaurantRepository restaurantRepository; @RequestMapping(method=RequestMethod.GET) public @ResponseBody AppVersion getKey(@RequestParam(value="os", required=true) String os, @RequestParam(value="appId", required=true) String appId) { RestaurantEntity r = restaurantRepository.findOne(appId); switch(os) { case "ios": return new AppVersion(r.getCurrentVersionIOS(), r.getLastSupportedVersionIOS()); case "and": return new AppVersion(r.getCurrentVersionAnd(), r.getLastSupportedVersionAnd()); case "win": return new AppVersion(r.getCurrentVersionUWP(), r.getLastSupportedVersionUWP()); } return null; } } VersionService The versioning service allows us to detect if a new version of the app is available and effectively allows us to disable outdated versions. This is important as we might be stuck with an out of date protocol that we wish to retire or we might need to change the server address.
  • 14. Thank You Thanks for watching. I hope you found this informative