SlideShare una empresa de Scribd logo
1 de 80
Descargar para leer sin conexión
Using API platform to build ticketing
system
Antonio Perić-Mažar, Locastic
Paula Čučuk, Locastic
18.10.2019. - #sfday
Antonio
Perić-Mažar
CEO @ Locastic
Co-founder @ Litto
Co-founder @ Tinel Meetup
t: @antonioperic
m: antonio@locastic.com
Paula Čučuk
Lead Backend Developer @ Locastic
Partner @ Locastic
t: @paulala_14
m: paula@locastic.com
Locastic
Helping clients create web
and mobile apps since 2011
• UX/UI
• Mobile apps
• Web apps
• Training & Consulting
www.locastic.com
@locastic
• API Platform & Symfony
• Ticketing platform: GFNY (franchise business)
• ~ year and half in production
• ~ 60 000 tickets released & race results stored in DB
• ~ 20 000 users/racers,
• ~ 60 users with admin roles
• 48 events in 26 countries, users from 82 countries
• 8 different languages including Hebrew and Indonesian
Context & our
Experience
• Social network
• chat based
• matching similar to Tinder :)
• few CRM/ERP applications
Context & our
Experience
What is API platform ?
–Fabien Potencier (creator of Symfony), SymfonyCon 2017
“API Platform is the most advanced API platform,
in any framework or language.”
• full stack framework dedicated to API-Driven projects
• contains a PHP library to create a fully featured APIs supporting
industry standards (JSON-LD, Hydra, GraphQL, OpenAPI…)
• provides ambitious Javascript tooling to consume APIs in a snap
• Symfony official API stack (instead of FOSRestBundle)
• shipped with Docker and Kubernetes integration
API Platform
• creating, retrieving, updating and deleting (CRUD) resources
• data validation
• pagination
• filtering
• sorting
• hypermedia/HATEOAS and content negotiation support (JSON-LD
and Hydra, JSON:API, HAL…)
API Platform built-in
features:
• GraphQL support
• Nice UI and machine-readable documentations (Swagger UI/
OpenAPI, GraphiQL…)
• authentication (Basic HTP, cookies as well as JWT and OAuth through
extensions)
• CORS headers
• security checks and headers (tested against OWASP
recommendations)
API Platform built-in
features:
• invalidation-based HTTP caching
• and basically everything needed to build modern APIs.
API Platform built-in
features:
Creating Simple CRUD
in a minute
Create
Entity
Step One
<?php
// src/Entity/Greeting.php
namespace AppEntity;
class Greeting
{
private $id;
public $name = '';
public function getId(): int
{
return $this->id;
}
}
Create
Mapping
Step Two
# config/doctrine/Greeting.orm.yml
AppEntityGreeting:
type: entity
table: greeting
id:
id:
type: integer
generator: { strategy: AUTO }
fields:
name:
type: string
length: 100
Add
Validation
Step Three
# config/validator/greeting.yaml
AppEntityGreeting:
properties:
name:
- NotBlank: ~
Expose
Resource
Step Four # config/api_platform/resources.yaml
resources:
AppEntityGreeting: ~
Serialization Groups
User Management &
Security
• Avoid using FOSUserBundle
• not well suited with API
• Use Doctrine User Provider
• simple and easy to integrate
User management
// src/Entity/User.php
namespace AppEntity;
use SymfonyComponentSecurityCoreUserUserInterface;
use SymfonyComponentSerializerAnnotationGroups;
class User implements UserInterface
{
/**
* @Groups({"user-read"})
*/
private $id;
/**
* @Groups({"user-write", "user-read"})
*/
private $email;
/**
* @Groups({"user-read"})
*/
private $roles = [];
/**
* @Groups({"user-write"})
*/
private $plainPassword;
private $password;
… getters and setters …
}
// src/Entity/User.php
namespace AppEntity;
use SymfonyComponentSecurityCoreUserUserInterface;
use SymfonyComponentSerializerAnnotationGroups;
class User implements UserInterface
{
/**
* @Groups({"user-read"})
*/
private $id;
/**
* @Groups({"user-write", "user-read"})
*/
private $email;
/**
* @Groups({"user-read"})
*/
private $roles = [];
/**
* @Groups({"user-write"})
*/
private $plainPassword;
private $password;
… getters and setters …
}
# config/doctrine/User.orm.yml
AppEntityUser:
type: entity
table: users
repositoryClass: AppRepositoryUserRepository
id:
id:
type: integer
generator: { strategy: AUTO }
fields:
email:
type: string
length: 255
password:
type: string
length: 255
roles:
type: array
// src/Entity/User.php
namespace AppEntity;
use SymfonyComponentSecurityCoreUserUserInterface;
use SymfonyComponentSerializerAnnotationGroups;
class User implements UserInterface
{
/**
* @Groups({"user-read"})
*/
private $id;
/**
* @Groups({"user-write", "user-read"})
*/
private $email;
/**
* @Groups({"user-read"})
*/
private $roles = [];
/**
* @Groups({"user-write"})
*/
private $plainPassword;
private $password;
… getters and setters …
}
# config/doctrine/User.orm.yml
AppEntityUser:
type: entity
table: users
repositoryClass: AppRepositoryUserRepository
id:
id:
type: integer
generator: { strategy: AUTO }
fields:
email:
type: string
length: 255
password:
type: string
length: 255
roles:
type: array
# config/api_platform/resources.yaml
resources:
AppEntityUser:
attributes:
normalization_context:
groups: ['user-read']
denormalization_context:
groups: ['user-write']
use ApiPlatformCoreDataPersisterContextAwareDataPersisterInterface;
use AppEntityUser;
use DoctrineORMEntityManagerInterface;
use SymfonyComponentSecurityCoreEncoderUserPasswordEncoderInterface;
class UserDataPersister implements ContextAwareDataPersisterInterface
{
private $entityManager;
private $userPasswordEncoder;
public function __construct(EntityManagerInterface $entityManager, UserPasswordEncoderInterface $userPasswordEncoder)
{
$this->entityManager = $entityManager;
$this->userPasswordEncoder = $userPasswordEncoder;
}
public function supports($data, array $context = []): bool
{
return $data instanceof User;
}
public function persist($data, array $context = [])
{
/** @var User $data */
if ($data->getPlainPassword()) {
$data->setPassword(
$this->userPasswordEncoder->encodePassword($data, $data->getPlainPassword())
);
$data->eraseCredentials();
}
$this->entityManager->persist($data);
$this->entityManager->flush($data);
return $data;
}
public function remove($data, array $context = [])
{
$this->entityManager->remove($data);
$this->entityManager->flush();
}
• Lightweight and simple authentication system
• Stateless: token signed and verified server-side then stored client-
side and sent with each request in an Authorization header
• Store the token in the browser local storage
JSON Web Token (JWT)
• API Platform allows to easily add a JWT-based authentication to your
API using LexikJWTAuthenticationBundle.
• Maybe you want to use a refresh token to renew your JWT. In this
case you can check JWTRefreshTokenBundle.
User authentication
User security
checker
Security
<?php
namespace AppSecurity;
use AppExceptionAccountDeletedException;
use AppSecurityUser as AppUser;
use SymfonyComponentSecurityCoreExceptionAccountExpiredException;
use SymfonyComponentSecurityCoreExceptionCustomUserMessageAuthenticat
use SymfonyComponentSecurityCoreUserUserCheckerInterface;
use SymfonyComponentSecurityCoreUserUserInterface;
class UserChecker implements UserCheckerInterface
{
public function checkPreAuth(UserInterface $user)
{
if (!$user instanceof AppUser) {
return;
}
// user is deleted, show a generic Account Not Found message.
if ($user->isDeleted()) {
throw new AccountDeletedException();
}
}
public function checkPostAuth(UserInterface $user)
{
if (!$user instanceof AppUser) {
return;
}
// user account is expired, the user may be notified
if ($user->isExpired()) {
throw new AccountExpiredException('...');
}
}
}
User security
checker
Security
# config/packages/security.yaml
# ...
security:
firewalls:
main:
pattern: ^/
user_checker: AppSecurityUserChecker
# ...
Resource and
operation
level
Security
# api/config/api_platform/resources.yaml
AppEntityBook:
attributes:
security: 'is_granted("ROLE_USER")'
collectionOperations:
get: ~
post:
security: 'is_granted("ROLE_ADMIN")'
itemOperations:
get: ~
put:
security_: 'is_granted("ROLE_ADMIN")
or object.owner == user'
Resource and
operation
level using
Voters
Security
# api/config/api_platform/resources.yaml
AppEntityBook:
itemOperations:
get:
security_: 'is_granted('READ', object)'
put:
security_: 'is_granted('UPDATE', object)'
• A JWT is self-contained, meaning that we can trust into its payload
for processing the authentication. In a nutshell, there should be no
need for loading the user from the database when authenticating a
JWT Token, the database should be hit only once for delivering the
token.
• It means you will have to fetch the User entity from the database
yourself as needed (probably through the Doctrine EntityManager).
JWT tip
A database-less user
provider
JWT tip
A database-less user
provider
# config/packages/security.yaml
security:
providers:
jwt:
lexik_jwt: ~
security:
firewalls:
api:
provider: jwt
guard:
# ...
Creating
multi-language APIs
• Locastic Api Translation Bundle
• Translation bundle for ApiPlatform based on Sylius translation
• It requires two entities: Translatable & Translation entity
• Open source
• https://github.com/Locastic/ApiPlatformTranslationBundle
• https://locastic.com/blog/having-troubles-with-implementing-
translations-in-apiplatform/
Creating
multi-language APIs
POST
translation
example
Multi-language API
{
"datetime":"2017-10-10",
"translations": {
"en":{
"title":"test",
"content":"test",
"locale":"en"
},
"de":{
"title":"test de",
"content":"test de",
"locale":"de"
}
}
}
Get response by locale
GET /api/posts/1?locale=en

{
"@context": "/api/v1/contexts/Post",
"@id": "/api/v1/posts/1')",
"@type": "Post",
"id": 1,
"datetime":"2019-10-10",
"title":"Hello world",
"content":"Hello from Verona!"
}
Get response with all translations
GET /api/posts/1?groups[]=translations
{
"@context": "/api/v1/contexts/Post",
"@id": "/api/v1/posts/1')",
"@type": "Post",
"id": 1,
"datetime":"2019-10-10",
"translations": {
"en":{
"title":"Hello world",
"content":"Hello from Verona!",
"locale":"en"
},
"it":{
"title":"Ciao mondo",
"content":"Ciao da Verona!",
"locale":"it"
}
}
}
• Endpoint for creating new language
• Creates all Symfony translation files when new language is added
• Endpoint for editing each language translation files
Adding languages and
translations
dynamically
Manipulating
the Context
Context
namespace AppEntity;
use ApiPlatformCoreAnnotationApiResource;
use SymfonyComponentSerializerAnnotationGroups;
/**
* @ApiResource(
* normalizationContext={"groups"={"book:output"}},
* denormalizationContext={"groups"={"book:input"}}
* )
*/
class Book
{
// ...
/**
* This field can be managed only by an admin
*
* @var bool
*
* @Groups({"book:output", "admin:input"})
*/
public $active = false;
/**
* This field can be managed by any user
*
* @var string
*
* @Groups({"book:output", "book:input"})
*/
public $name;
// ...
}
Manipulating
the Context
Context
# api/config/services.yaml
services:
# ...
'AppSerializerBookContextBuilder':
decorates: 'api_platform.serializer.context_builder'
arguments: [ '@AppSerializerBookContextBuilder.inner' ]
autoconfigure: false
// api/src/Serializer/BookContextBuilder.php
namespace AppSerializer;
use ApiPlatformCoreSerializerSerializerContextBuilderInterface;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentSecurityCoreAuthorizationAuthorizationCheckerInterface;
use AppEntityBook;
final class BookContextBuilder implements SerializerContextBuilderInterface
{
private $decorated;
private $authorizationChecker;
public function __construct(SerializerContextBuilderInterface $decorated, AuthorizationCheckerInterface
$authorizationChecker)
{
$this->decorated = $decorated;
$this->authorizationChecker = $authorizationChecker;
}
public function createFromRequest(Request $request, bool $normalization, ?array $extractedAttributes = null): array
{
$context = $this->decorated->createFromRequest($request, $normalization, $extractedAttributes);
$resourceClass = $context['resource_class'] ?? null;
if ($resourceClass === Book::class && isset($context['groups']) && $this->authorizationChecker-
>isGranted('ROLE_ADMIN') && false === $normalization) {
$context['groups'][] = 'admin:input';
}
return $context;
}
}
Symfony Messanger
Component
• The Messenger component helps applications send and receive
messages to/from other applications or via message queues.
• Easy to implement
• Making async easy
• Many transports are supported to dispatch messages to async
consumers, including RabbitMQ, Apache Kafka, Amazon SQS and
Google Pub/Sub.
Symfony Messenger
• Allows to implement the Command Query Responsibility Segregation
(CQRS) pattern in a convenient way.
• It also makes it easy to send messages through the web API that will
be consumed asynchronously.
• Async import, export, image processing… any heavy work
Symfony Messenger &
API Platform
CQRS
Symfony Messenger & API Platform
AppEntityPasswordResetRequest:
collectionOperations:
post:
status: 202
itemOperations: []
attributes:
messenger: true
output: false
CQRS
Symfony Messenger & API Platform
<?php
namespace AppHandler;
use AppEntityPasswordResetRequest;
use SymfonyComponentMessengerHandlerMessageHandlerInterfac
final class PasswordResetRequestHandler implements MessageHand
{
public function __invoke(PasswordResetRequest $forgotPassw
{
// do some heavy things
}
}
<?php
namespace AppEntity;
final class PasswordResetRequest
{
public $email;
}
CQRS
/w DTO
Symfony Messenger & API Platform
AppEntityUser:
collectionOperations:
post:
status: 202
itemOperations: []
attributes:
messenger: “input”
input: “ResetPasswordRequest::class”
output: false
// api/src/Entity/User.php
namespace AppEntity;
use ApiPlatformCoreAnnotationApiResource;
use AppDtoResetPasswordRequest;
final class User
{
}
CQRS
/w DTO
Symfony Messenger & API Platform
// api/src/Handler/ResetPasswordRequestHandler.php
namespace AppHandler;
use AppDtoResetPasswordRequest;
use SymfonyComponentMessengerHandlerMessageHandlerInterface;
final class ResetPasswordRequestHandler implements MessageHandle
{
public function __invoke(ResetPasswordRequest $forgotPasswor
{
// do something with the resource
}
}
// api/src/Dto/ResetPasswordRequest.php
namespace AppDto;
use SymfonyComponentValidatorConstraints as Assert;
final class ResetPasswordRequest
{
public $var;
}
<?php
namespace AppDataPersister;
use ApiPlatformCoreDataPersisterContextAwareDataPersisterInterface;
use AppEntityImageMedia;
use DoctrineORMEntityManagerInterface;
use SymfonyComponentMessengerMessageBusInterface;
class ImageMediaDataPersister implements ContextAwareDataPersisterInterface
{
private $entityManager;
private $messageBus;
public function __construct(EntityManagerInterface $entityManager, MessageBusInterface $messageBus)
{
$this->entityManager = $entityManager;
$this->messageBus = $messageBus;
}
public function supports($data, array $context = []): bool
{
return $data instanceof ImageMedia;
}
public function persist($data, array $context = [])
{
$this->entityManager->persist($data);
$this->entityManager->flush($data);
$this->messageBus->dispatch(new ProcessImageMessage($data->getId()));
return $data;
}
public function remove($data, array $context = [])
{
$this->entityManager->remove($data);
$this->entityManager->flush();
$this->messageBus->dispatch(new DeleteImageMessage($data->getId()));
}
}
namespace AppEventSubscriber;
use ApiPlatformCoreEventListenerEventPriorities;
use AppEntityBook;
use SymfonyComponentEventDispatcherEventSubscriberInterface;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpKernelEventViewEvent;
use SymfonyComponentHttpKernelKernelEvents;
use SymfonyComponentMessengerMessageBusInterface;
final class BookMailSubscriber implements EventSubscriberInterface
{
private $messageBus;
public function __construct(MessageBusInterface $messageBus)
{
$this->messageBus = $messageBus;
}
public static function getSubscribedEvents()
{
return [
KernelEvents::VIEW => ['sendMail', EventPriorities::POST_WRITE],
];
}
public function sendMail(ViewEvent $event)
{
$book = $event->getControllerResult();
$method = $event->getRequest()->getMethod();
if (!$book instanceof Book || Request::METHOD_POST !== $method) {
return;
}
// send to all users 2M that new book has arrived
this->messageBus->dispatch(new SendEmailMessage(‘new-book’, $book->getTitle()));
}
}
• problem:
• different objects from source and in our database
• multiple sources of data (3rd party)
• DataTransform transforms from source object to our object
• exporting to CSV files
Using DTOs with import
and export
resources:
AppEntityOrder:
collectionOperations:
get: ~
exports:
method: POST
path: '/orders/export'
formats:
csv: ['text/csv']
pagination_enabled: false
output: "OrderExport::class"
normalization_context:
groups: ['order-export']
Real-time applications
with API platform
• Redis + NodeJS
• Pusher
• ReactPHP
• …
• but to be honest PHP is not build for realtime :)
Real-time applications
with API platform
• Fast, written in Go
• native browser support, no lib nor SDK required (built on top of HTTP and server-sent
events)
• compatible with all existing servers, even those who don't support persistent
connections (serverless architecture, PHP, FastCGI…)
• Automatic HTTP/2 and HTTPS (using Let's Encrypt) support
• CORS support, CSRF protection mechanism
• Cloud Native, follows the Twelve-Factor App methodology
• Open source (AGPL)
• …
Mercure
resources:
AppEntityGreeting:
attributes:
mercure: true
const eventSource = new EventSource('http://localhost:3000/hub?topic=' +
encodeURIComponent('http://example.com/greeting/1'));
eventSource.onmessage = event => {
// Will be called every time an update is published by the server
console.log(JSON.parse(event.data));
}
Testing
• Unit tests
• test your logic, refactor your code using SOLID priciples
• Integration tests
• validation
• 3rd party integrations
• database queries
• Functional tests
• response code, header and content (expected fields in expected format)
Type of tests
• Ask yourself: “Am I sure the code I tested works as it should?”
• 100% coverage doesn’t guarantee your code is fully tested and
working
• Write test first is just one of the approaches
• Legacy code:
• Start replicating bugs with tests before fixing them
• Test at least most important and critical parts
Testing tips and tricks
Handy testing tools
PHP Matcher
Library that enables you to check your response against patterns.
Faker
Library for generating random data
Postman tests
Newman + Postman
• Infection - tool for mutation testing
• PHPStan - focuses on finding errors in your code without actually
running it
• Continuous integration (CI) -  enables you to run your tests on git on
each commit
Tools for checking test
quality
Api Platform is awesome!
Conclusion
Thank you!
Questions?
Antonio Perić-Mažar
t: @antonioperic
m: antonio@locastic.com
Paula Čučuk
t: @paulala_14
m: paula@locastic.com

Más contenido relacionado

La actualidad más candente

Baruco 2014 - Rubymotion Workshop
Baruco 2014 - Rubymotion WorkshopBaruco 2014 - Rubymotion Workshop
Baruco 2014 - Rubymotion WorkshopBrian Sam-Bodden
 
LEARNING  iPAD STORYBOARDS IN OBJ-­‐C LESSON 1
LEARNING	 iPAD STORYBOARDS IN OBJ-­‐C LESSON 1LEARNING	 iPAD STORYBOARDS IN OBJ-­‐C LESSON 1
LEARNING  iPAD STORYBOARDS IN OBJ-­‐C LESSON 1Rich Helton
 
Cross-platform mobile apps with Apache Cordova
Cross-platform mobile apps with Apache CordovaCross-platform mobile apps with Apache Cordova
Cross-platform mobile apps with Apache CordovaIvano Malavolta
 
[JavaLand 2015] Developing JavaScript Mobile Apps Using Apache Cordova
[JavaLand 2015] Developing JavaScript Mobile Apps Using Apache Cordova[JavaLand 2015] Developing JavaScript Mobile Apps Using Apache Cordova
[JavaLand 2015] Developing JavaScript Mobile Apps Using Apache CordovaHazem Saleh
 
[JMaghreb 2014] Developing JavaScript Mobile Apps Using Apache Cordova
[JMaghreb 2014] Developing JavaScript Mobile Apps Using Apache Cordova[JMaghreb 2014] Developing JavaScript Mobile Apps Using Apache Cordova
[JMaghreb 2014] Developing JavaScript Mobile Apps Using Apache CordovaHazem Saleh
 
Design pattern in Symfony2 - Nanos gigantium humeris insidentes
Design pattern in Symfony2 - Nanos gigantium humeris insidentesDesign pattern in Symfony2 - Nanos gigantium humeris insidentes
Design pattern in Symfony2 - Nanos gigantium humeris insidentesGiulio De Donato
 
I pad uicatalog_lesson02
I pad uicatalog_lesson02I pad uicatalog_lesson02
I pad uicatalog_lesson02Rich Helton
 
Cross Platform Mobile Apps with the Ionic Framework
Cross Platform Mobile Apps with the Ionic FrameworkCross Platform Mobile Apps with the Ionic Framework
Cross Platform Mobile Apps with the Ionic FrameworkTroy Miles
 
PhoneGap: Accessing Device Capabilities
PhoneGap: Accessing Device CapabilitiesPhoneGap: Accessing Device Capabilities
PhoneGap: Accessing Device CapabilitiesIvano Malavolta
 
Building Mobile Friendly APIs in Rails
Building Mobile Friendly APIs in RailsBuilding Mobile Friendly APIs in Rails
Building Mobile Friendly APIs in RailsJim Jeffers
 
Automating the Gaps of Unit Testing Mobile Apps
Automating the Gaps of Unit Testing Mobile AppsAutomating the Gaps of Unit Testing Mobile Apps
Automating the Gaps of Unit Testing Mobile AppsGeoffrey Goetz
 
JavaScript on HP webOS: Enyo and Node.js
JavaScript on HP webOS: Enyo and Node.jsJavaScript on HP webOS: Enyo and Node.js
JavaScript on HP webOS: Enyo and Node.jsBen Combee
 
CapitolJS: Enyo, Node.js, & the State of webOS
CapitolJS: Enyo, Node.js, & the State of webOSCapitolJS: Enyo, Node.js, & the State of webOS
CapitolJS: Enyo, Node.js, & the State of webOSBen Combee
 
Accessibility: A Journey to Accessible Rich Components
Accessibility: A Journey to Accessible Rich ComponentsAccessibility: A Journey to Accessible Rich Components
Accessibility: A Journey to Accessible Rich ComponentsAchievers Tech
 
Learning C# iPad Programming
Learning C# iPad ProgrammingLearning C# iPad Programming
Learning C# iPad ProgrammingRich Helton
 
Understanding Identity in the World of Web APIs – Ronnie Mitra, API Architec...
Understanding Identity in the World of Web APIs – Ronnie Mitra,  API Architec...Understanding Identity in the World of Web APIs – Ronnie Mitra,  API Architec...
Understanding Identity in the World of Web APIs – Ronnie Mitra, API Architec...CA API Management
 
Documenting REST APIs
Documenting REST APIsDocumenting REST APIs
Documenting REST APIsTom Johnson
 

La actualidad más candente (20)

Baruco 2014 - Rubymotion Workshop
Baruco 2014 - Rubymotion WorkshopBaruco 2014 - Rubymotion Workshop
Baruco 2014 - Rubymotion Workshop
 
C# fundamentals Part 2
C# fundamentals Part 2C# fundamentals Part 2
C# fundamentals Part 2
 
LEARNING  iPAD STORYBOARDS IN OBJ-­‐C LESSON 1
LEARNING	 iPAD STORYBOARDS IN OBJ-­‐C LESSON 1LEARNING	 iPAD STORYBOARDS IN OBJ-­‐C LESSON 1
LEARNING  iPAD STORYBOARDS IN OBJ-­‐C LESSON 1
 
Cross-platform mobile apps with Apache Cordova
Cross-platform mobile apps with Apache CordovaCross-platform mobile apps with Apache Cordova
Cross-platform mobile apps with Apache Cordova
 
React Native
React NativeReact Native
React Native
 
[JavaLand 2015] Developing JavaScript Mobile Apps Using Apache Cordova
[JavaLand 2015] Developing JavaScript Mobile Apps Using Apache Cordova[JavaLand 2015] Developing JavaScript Mobile Apps Using Apache Cordova
[JavaLand 2015] Developing JavaScript Mobile Apps Using Apache Cordova
 
[JMaghreb 2014] Developing JavaScript Mobile Apps Using Apache Cordova
[JMaghreb 2014] Developing JavaScript Mobile Apps Using Apache Cordova[JMaghreb 2014] Developing JavaScript Mobile Apps Using Apache Cordova
[JMaghreb 2014] Developing JavaScript Mobile Apps Using Apache Cordova
 
Design pattern in Symfony2 - Nanos gigantium humeris insidentes
Design pattern in Symfony2 - Nanos gigantium humeris insidentesDesign pattern in Symfony2 - Nanos gigantium humeris insidentes
Design pattern in Symfony2 - Nanos gigantium humeris insidentes
 
I pad uicatalog_lesson02
I pad uicatalog_lesson02I pad uicatalog_lesson02
I pad uicatalog_lesson02
 
Cross Platform Mobile Apps with the Ionic Framework
Cross Platform Mobile Apps with the Ionic FrameworkCross Platform Mobile Apps with the Ionic Framework
Cross Platform Mobile Apps with the Ionic Framework
 
PhoneGap: Accessing Device Capabilities
PhoneGap: Accessing Device CapabilitiesPhoneGap: Accessing Device Capabilities
PhoneGap: Accessing Device Capabilities
 
Building Mobile Friendly APIs in Rails
Building Mobile Friendly APIs in RailsBuilding Mobile Friendly APIs in Rails
Building Mobile Friendly APIs in Rails
 
Swift meetup22june2015
Swift meetup22june2015Swift meetup22june2015
Swift meetup22june2015
 
Automating the Gaps of Unit Testing Mobile Apps
Automating the Gaps of Unit Testing Mobile AppsAutomating the Gaps of Unit Testing Mobile Apps
Automating the Gaps of Unit Testing Mobile Apps
 
JavaScript on HP webOS: Enyo and Node.js
JavaScript on HP webOS: Enyo and Node.jsJavaScript on HP webOS: Enyo and Node.js
JavaScript on HP webOS: Enyo and Node.js
 
CapitolJS: Enyo, Node.js, & the State of webOS
CapitolJS: Enyo, Node.js, & the State of webOSCapitolJS: Enyo, Node.js, & the State of webOS
CapitolJS: Enyo, Node.js, & the State of webOS
 
Accessibility: A Journey to Accessible Rich Components
Accessibility: A Journey to Accessible Rich ComponentsAccessibility: A Journey to Accessible Rich Components
Accessibility: A Journey to Accessible Rich Components
 
Learning C# iPad Programming
Learning C# iPad ProgrammingLearning C# iPad Programming
Learning C# iPad Programming
 
Understanding Identity in the World of Web APIs – Ronnie Mitra, API Architec...
Understanding Identity in the World of Web APIs – Ronnie Mitra,  API Architec...Understanding Identity in the World of Web APIs – Ronnie Mitra,  API Architec...
Understanding Identity in the World of Web APIs – Ronnie Mitra, API Architec...
 
Documenting REST APIs
Documenting REST APIsDocumenting REST APIs
Documenting REST APIs
 

Similar a Using API platform to build ticketing system (translations, time zones, ...) #sfday #verona

Introduction to Titanium and how to connect with a PHP backend
Introduction to Titanium and how to connect with a PHP backendIntroduction to Titanium and how to connect with a PHP backend
Introduction to Titanium and how to connect with a PHP backendJoseluis Laso
 
Building TweetEngine
Building TweetEngineBuilding TweetEngine
Building TweetEngineikailan
 
API Workshop: Deep dive into REST APIs
API Workshop: Deep dive into REST APIsAPI Workshop: Deep dive into REST APIs
API Workshop: Deep dive into REST APIsTom Johnson
 
Telerik AppBuilder Presentation for TelerikNEXT Conference
Telerik AppBuilder Presentation for TelerikNEXT ConferenceTelerik AppBuilder Presentation for TelerikNEXT Conference
Telerik AppBuilder Presentation for TelerikNEXT ConferenceJen Looper
 
Checkmarx meetup API Security - API Security in depth - Inon Shkedy
Checkmarx meetup API Security - API Security in depth - Inon ShkedyCheckmarx meetup API Security - API Security in depth - Inon Shkedy
Checkmarx meetup API Security - API Security in depth - Inon ShkedyAdar Weidman
 
Don't worry be API with Slim framework and Joomla
Don't worry be API with Slim framework and JoomlaDon't worry be API with Slim framework and Joomla
Don't worry be API with Slim framework and JoomlaPierre-André Vullioud
 
How to build Simple yet powerful API.pptx
How to build Simple yet powerful API.pptxHow to build Simple yet powerful API.pptx
How to build Simple yet powerful API.pptxChanna Ly
 
Modern Web Applications Utilizing HTML5 APIs
Modern Web Applications Utilizing HTML5 APIsModern Web Applications Utilizing HTML5 APIs
Modern Web Applications Utilizing HTML5 APIsIdo Green
 
Building APIs in an easy way using API Platform
Building APIs in an easy way using API PlatformBuilding APIs in an easy way using API Platform
Building APIs in an easy way using API PlatformAntonio Peric-Mazar
 
Google App Engine Overview - BarCamp Phnom Penh 2011
Google App Engine Overview - BarCamp Phnom Penh 2011Google App Engine Overview - BarCamp Phnom Penh 2011
Google App Engine Overview - BarCamp Phnom Penh 2011traactivity
 
API Security - OWASP top 10 for APIs + tips for pentesters
API Security - OWASP top 10 for APIs + tips for pentestersAPI Security - OWASP top 10 for APIs + tips for pentesters
API Security - OWASP top 10 for APIs + tips for pentestersInon Shkedy
 
Utilizing HTML5 APIs
Utilizing HTML5 APIsUtilizing HTML5 APIs
Utilizing HTML5 APIsIdo Green
 
Developer Tutorial: WebAuthn for Web & FIDO2 for Android
Developer Tutorial: WebAuthn for Web & FIDO2 for AndroidDeveloper Tutorial: WebAuthn for Web & FIDO2 for Android
Developer Tutorial: WebAuthn for Web & FIDO2 for AndroidFIDO Alliance
 
Creating REST Applications with the Slim Micro-Framework by Vikram Vaswani
Creating REST Applications with the Slim Micro-Framework by Vikram VaswaniCreating REST Applications with the Slim Micro-Framework by Vikram Vaswani
Creating REST Applications with the Slim Micro-Framework by Vikram Vaswanivvaswani
 
Developing Apps with Azure AD
Developing Apps with Azure ADDeveloping Apps with Azure AD
Developing Apps with Azure ADSharePointRadi
 
automation framework
automation frameworkautomation framework
automation frameworkANSHU GOYAL
 
FIDO2 Specifications Overview
FIDO2 Specifications OverviewFIDO2 Specifications Overview
FIDO2 Specifications OverviewFIDO Alliance
 
How React Native, Appium and me made each other shine @ContinuousDeliveryAmst...
How React Native, Appium and me made each other shine @ContinuousDeliveryAmst...How React Native, Appium and me made each other shine @ContinuousDeliveryAmst...
How React Native, Appium and me made each other shine @ContinuousDeliveryAmst...Wim Selles
 

Similar a Using API platform to build ticketing system (translations, time zones, ...) #sfday #verona (20)

Introduction to Titanium and how to connect with a PHP backend
Introduction to Titanium and how to connect with a PHP backendIntroduction to Titanium and how to connect with a PHP backend
Introduction to Titanium and how to connect with a PHP backend
 
Building TweetEngine
Building TweetEngineBuilding TweetEngine
Building TweetEngine
 
API Workshop: Deep dive into REST APIs
API Workshop: Deep dive into REST APIsAPI Workshop: Deep dive into REST APIs
API Workshop: Deep dive into REST APIs
 
Introduction to Google App Engine
Introduction to Google App EngineIntroduction to Google App Engine
Introduction to Google App Engine
 
Telerik AppBuilder Presentation for TelerikNEXT Conference
Telerik AppBuilder Presentation for TelerikNEXT ConferenceTelerik AppBuilder Presentation for TelerikNEXT Conference
Telerik AppBuilder Presentation for TelerikNEXT Conference
 
Checkmarx meetup API Security - API Security in depth - Inon Shkedy
Checkmarx meetup API Security - API Security in depth - Inon ShkedyCheckmarx meetup API Security - API Security in depth - Inon Shkedy
Checkmarx meetup API Security - API Security in depth - Inon Shkedy
 
Don't worry be API with Slim framework and Joomla
Don't worry be API with Slim framework and JoomlaDon't worry be API with Slim framework and Joomla
Don't worry be API with Slim framework and Joomla
 
How to build Simple yet powerful API.pptx
How to build Simple yet powerful API.pptxHow to build Simple yet powerful API.pptx
How to build Simple yet powerful API.pptx
 
Modern Web Applications Utilizing HTML5 APIs
Modern Web Applications Utilizing HTML5 APIsModern Web Applications Utilizing HTML5 APIs
Modern Web Applications Utilizing HTML5 APIs
 
Building APIs in an easy way using API Platform
Building APIs in an easy way using API PlatformBuilding APIs in an easy way using API Platform
Building APIs in an easy way using API Platform
 
Google App Engine Overview - BarCamp Phnom Penh 2011
Google App Engine Overview - BarCamp Phnom Penh 2011Google App Engine Overview - BarCamp Phnom Penh 2011
Google App Engine Overview - BarCamp Phnom Penh 2011
 
API Security - OWASP top 10 for APIs + tips for pentesters
API Security - OWASP top 10 for APIs + tips for pentestersAPI Security - OWASP top 10 for APIs + tips for pentesters
API Security - OWASP top 10 for APIs + tips for pentesters
 
Utilizing HTML5 APIs
Utilizing HTML5 APIsUtilizing HTML5 APIs
Utilizing HTML5 APIs
 
Developer Tutorial: WebAuthn for Web & FIDO2 for Android
Developer Tutorial: WebAuthn for Web & FIDO2 for AndroidDeveloper Tutorial: WebAuthn for Web & FIDO2 for Android
Developer Tutorial: WebAuthn for Web & FIDO2 for Android
 
Creating REST Applications with the Slim Micro-Framework by Vikram Vaswani
Creating REST Applications with the Slim Micro-Framework by Vikram VaswaniCreating REST Applications with the Slim Micro-Framework by Vikram Vaswani
Creating REST Applications with the Slim Micro-Framework by Vikram Vaswani
 
Hexagonal architecture
Hexagonal architectureHexagonal architecture
Hexagonal architecture
 
Developing Apps with Azure AD
Developing Apps with Azure ADDeveloping Apps with Azure AD
Developing Apps with Azure AD
 
automation framework
automation frameworkautomation framework
automation framework
 
FIDO2 Specifications Overview
FIDO2 Specifications OverviewFIDO2 Specifications Overview
FIDO2 Specifications Overview
 
How React Native, Appium and me made each other shine @ContinuousDeliveryAmst...
How React Native, Appium and me made each other shine @ContinuousDeliveryAmst...How React Native, Appium and me made each other shine @ContinuousDeliveryAmst...
How React Native, Appium and me made each other shine @ContinuousDeliveryAmst...
 

Más de Antonio Peric-Mazar

You call yourself a Senior Developer?
You call yourself a Senior Developer?You call yourself a Senior Developer?
You call yourself a Senior Developer?Antonio Peric-Mazar
 
Are you failing at being agile? #digitallabin
Are you failing at being agile? #digitallabinAre you failing at being agile? #digitallabin
Are you failing at being agile? #digitallabinAntonio Peric-Mazar
 
Symfony 4: A new way to develop applications #ipc19
Symfony 4: A new way to develop applications #ipc19Symfony 4: A new way to develop applications #ipc19
Symfony 4: A new way to develop applications #ipc19Antonio Peric-Mazar
 
A year with progressive web apps! #webinale
A year with progressive web apps! #webinaleA year with progressive web apps! #webinale
A year with progressive web apps! #webinaleAntonio Peric-Mazar
 
The UI is the THE application #dpc19
The UI is the THE application #dpc19The UI is the THE application #dpc19
The UI is the THE application #dpc19Antonio Peric-Mazar
 
Symfony 4: A new way to develop applications #phpsrb
 Symfony 4: A new way to develop applications #phpsrb Symfony 4: A new way to develop applications #phpsrb
Symfony 4: A new way to develop applications #phpsrbAntonio Peric-Mazar
 
A year with progressive web apps! #DevConMU
A year with progressive web apps! #DevConMUA year with progressive web apps! #DevConMU
A year with progressive web apps! #DevConMUAntonio Peric-Mazar
 
Service workers are your best friends
Service workers are your best friendsService workers are your best friends
Service workers are your best friendsAntonio Peric-Mazar
 
Building APIs in an easy way using API Platform
Building APIs in an easy way using API PlatformBuilding APIs in an easy way using API Platform
Building APIs in an easy way using API PlatformAntonio Peric-Mazar
 
Symfony4 - A new way of developing web applications
Symfony4 - A new way of developing web applicationsSymfony4 - A new way of developing web applications
Symfony4 - A new way of developing web applicationsAntonio Peric-Mazar
 
Build your business on top of Open Source
Build your business on top of Open SourceBuild your business on top of Open Source
Build your business on top of Open SourceAntonio Peric-Mazar
 
Lessons learned while developing with Sylius
Lessons learned while developing with SyliusLessons learned while developing with Sylius
Lessons learned while developing with SyliusAntonio Peric-Mazar
 
Drupal8 for Symfony developers - Dutch PHP
Drupal8 for Symfony developers - Dutch PHPDrupal8 for Symfony developers - Dutch PHP
Drupal8 for Symfony developers - Dutch PHPAntonio Peric-Mazar
 
Drupal8 for Symfony Developers (PHP Day Verona 2017)
Drupal8 for Symfony Developers (PHP Day Verona 2017)Drupal8 for Symfony Developers (PHP Day Verona 2017)
Drupal8 for Symfony Developers (PHP Day Verona 2017)Antonio Peric-Mazar
 
Maintainable + Extensible = Clean ... yes, Code!
Maintainable + Extensible = Clean ... yes, Code! Maintainable + Extensible = Clean ... yes, Code!
Maintainable + Extensible = Clean ... yes, Code! Antonio Peric-Mazar
 
A recipe for effective leadership
A recipe for effective leadershipA recipe for effective leadership
A recipe for effective leadershipAntonio Peric-Mazar
 
Building real time applications with Symfony2
Building real time applications with Symfony2Building real time applications with Symfony2
Building real time applications with Symfony2Antonio Peric-Mazar
 
Building Single Page Application (SPA) with Symfony2 and AngularJS
Building Single Page Application (SPA) with Symfony2 and AngularJSBuilding Single Page Application (SPA) with Symfony2 and AngularJS
Building Single Page Application (SPA) with Symfony2 and AngularJSAntonio Peric-Mazar
 

Más de Antonio Peric-Mazar (20)

You call yourself a Senior Developer?
You call yourself a Senior Developer?You call yourself a Senior Developer?
You call yourself a Senior Developer?
 
Are you failing at being agile? #digitallabin
Are you failing at being agile? #digitallabinAre you failing at being agile? #digitallabin
Are you failing at being agile? #digitallabin
 
Symfony 4: A new way to develop applications #ipc19
Symfony 4: A new way to develop applications #ipc19Symfony 4: A new way to develop applications #ipc19
Symfony 4: A new way to develop applications #ipc19
 
A year with progressive web apps! #webinale
A year with progressive web apps! #webinaleA year with progressive web apps! #webinale
A year with progressive web apps! #webinale
 
The UI is the THE application #dpc19
The UI is the THE application #dpc19The UI is the THE application #dpc19
The UI is the THE application #dpc19
 
Symfony 4: A new way to develop applications #phpsrb
 Symfony 4: A new way to develop applications #phpsrb Symfony 4: A new way to develop applications #phpsrb
Symfony 4: A new way to develop applications #phpsrb
 
A year with progressive web apps! #DevConMU
A year with progressive web apps! #DevConMUA year with progressive web apps! #DevConMU
A year with progressive web apps! #DevConMU
 
Service workers are your best friends
Service workers are your best friendsService workers are your best friends
Service workers are your best friends
 
Progressive Web Apps are here!
Progressive Web Apps are here!Progressive Web Apps are here!
Progressive Web Apps are here!
 
Building APIs in an easy way using API Platform
Building APIs in an easy way using API PlatformBuilding APIs in an easy way using API Platform
Building APIs in an easy way using API Platform
 
Symfony4 - A new way of developing web applications
Symfony4 - A new way of developing web applicationsSymfony4 - A new way of developing web applications
Symfony4 - A new way of developing web applications
 
Build your business on top of Open Source
Build your business on top of Open SourceBuild your business on top of Open Source
Build your business on top of Open Source
 
Lessons learned while developing with Sylius
Lessons learned while developing with SyliusLessons learned while developing with Sylius
Lessons learned while developing with Sylius
 
Drupal8 for Symfony developers - Dutch PHP
Drupal8 for Symfony developers - Dutch PHPDrupal8 for Symfony developers - Dutch PHP
Drupal8 for Symfony developers - Dutch PHP
 
Drupal8 for Symfony Developers (PHP Day Verona 2017)
Drupal8 for Symfony Developers (PHP Day Verona 2017)Drupal8 for Symfony Developers (PHP Day Verona 2017)
Drupal8 for Symfony Developers (PHP Day Verona 2017)
 
Drupal8 for Symfony Developers
Drupal8 for Symfony DevelopersDrupal8 for Symfony Developers
Drupal8 for Symfony Developers
 
Maintainable + Extensible = Clean ... yes, Code!
Maintainable + Extensible = Clean ... yes, Code! Maintainable + Extensible = Clean ... yes, Code!
Maintainable + Extensible = Clean ... yes, Code!
 
A recipe for effective leadership
A recipe for effective leadershipA recipe for effective leadership
A recipe for effective leadership
 
Building real time applications with Symfony2
Building real time applications with Symfony2Building real time applications with Symfony2
Building real time applications with Symfony2
 
Building Single Page Application (SPA) with Symfony2 and AngularJS
Building Single Page Application (SPA) with Symfony2 and AngularJSBuilding Single Page Application (SPA) with Symfony2 and AngularJS
Building Single Page Application (SPA) with Symfony2 and AngularJS
 

Último

%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in sowetomasabamasaba
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park masabamasaba
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...Health
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...masabamasaba
 
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...WSO2
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrainmasabamasaba
 
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...Jittipong Loespradit
 
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...masabamasaba
 
Artyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxArtyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxAnnaArtyushina1
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...masabamasaba
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplatePresentation.STUDIO
 
What Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the SituationWhat Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the SituationJuha-Pekka Tolvanen
 
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...masabamasaba
 
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...WSO2
 
WSO2Con204 - Hard Rock Presentation - Keynote
WSO2Con204 - Hard Rock Presentation - KeynoteWSO2Con204 - Hard Rock Presentation - Keynote
WSO2Con204 - Hard Rock Presentation - KeynoteWSO2
 
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...WSO2
 
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...WSO2
 
WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfonteinmasabamasaba
 
WSO2CON 2024 - How to Run a Security Program
WSO2CON 2024 - How to Run a Security ProgramWSO2CON 2024 - How to Run a Security Program
WSO2CON 2024 - How to Run a Security ProgramWSO2
 

Último (20)

%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
 
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
 
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
 
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
 
Artyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxArtyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptx
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation Template
 
What Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the SituationWhat Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the Situation
 
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
 
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...
 
WSO2Con204 - Hard Rock Presentation - Keynote
WSO2Con204 - Hard Rock Presentation - KeynoteWSO2Con204 - Hard Rock Presentation - Keynote
WSO2Con204 - Hard Rock Presentation - Keynote
 
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
 
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
 
WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
 
WSO2CON 2024 - How to Run a Security Program
WSO2CON 2024 - How to Run a Security ProgramWSO2CON 2024 - How to Run a Security Program
WSO2CON 2024 - How to Run a Security Program
 

Using API platform to build ticketing system (translations, time zones, ...) #sfday #verona

  • 1. Using API platform to build ticketing system Antonio Perić-Mažar, Locastic Paula Čučuk, Locastic 18.10.2019. - #sfday
  • 2. Antonio Perić-Mažar CEO @ Locastic Co-founder @ Litto Co-founder @ Tinel Meetup t: @antonioperic m: antonio@locastic.com
  • 3. Paula Čučuk Lead Backend Developer @ Locastic Partner @ Locastic t: @paulala_14 m: paula@locastic.com
  • 4. Locastic Helping clients create web and mobile apps since 2011 • UX/UI • Mobile apps • Web apps • Training & Consulting www.locastic.com @locastic
  • 5.
  • 6. • API Platform & Symfony • Ticketing platform: GFNY (franchise business) • ~ year and half in production • ~ 60 000 tickets released & race results stored in DB • ~ 20 000 users/racers, • ~ 60 users with admin roles • 48 events in 26 countries, users from 82 countries • 8 different languages including Hebrew and Indonesian Context & our Experience
  • 7. • Social network • chat based • matching similar to Tinder :) • few CRM/ERP applications Context & our Experience
  • 8. What is API platform ?
  • 9. –Fabien Potencier (creator of Symfony), SymfonyCon 2017 “API Platform is the most advanced API platform, in any framework or language.”
  • 10. • full stack framework dedicated to API-Driven projects • contains a PHP library to create a fully featured APIs supporting industry standards (JSON-LD, Hydra, GraphQL, OpenAPI…) • provides ambitious Javascript tooling to consume APIs in a snap • Symfony official API stack (instead of FOSRestBundle) • shipped with Docker and Kubernetes integration API Platform
  • 11. • creating, retrieving, updating and deleting (CRUD) resources • data validation • pagination • filtering • sorting • hypermedia/HATEOAS and content negotiation support (JSON-LD and Hydra, JSON:API, HAL…) API Platform built-in features:
  • 12. • GraphQL support • Nice UI and machine-readable documentations (Swagger UI/ OpenAPI, GraphiQL…) • authentication (Basic HTP, cookies as well as JWT and OAuth through extensions) • CORS headers • security checks and headers (tested against OWASP recommendations) API Platform built-in features:
  • 13. • invalidation-based HTTP caching • and basically everything needed to build modern APIs. API Platform built-in features:
  • 15. Create Entity Step One <?php // src/Entity/Greeting.php namespace AppEntity; class Greeting { private $id; public $name = ''; public function getId(): int { return $this->id; } }
  • 16. Create Mapping Step Two # config/doctrine/Greeting.orm.yml AppEntityGreeting: type: entity table: greeting id: id: type: integer generator: { strategy: AUTO } fields: name: type: string length: 100
  • 18. Expose Resource Step Four # config/api_platform/resources.yaml resources: AppEntityGreeting: ~
  • 19.
  • 20.
  • 21.
  • 22.
  • 25. • Avoid using FOSUserBundle • not well suited with API • Use Doctrine User Provider • simple and easy to integrate User management
  • 26. // src/Entity/User.php namespace AppEntity; use SymfonyComponentSecurityCoreUserUserInterface; use SymfonyComponentSerializerAnnotationGroups; class User implements UserInterface { /** * @Groups({"user-read"}) */ private $id; /** * @Groups({"user-write", "user-read"}) */ private $email; /** * @Groups({"user-read"}) */ private $roles = []; /** * @Groups({"user-write"}) */ private $plainPassword; private $password; … getters and setters … }
  • 27. // src/Entity/User.php namespace AppEntity; use SymfonyComponentSecurityCoreUserUserInterface; use SymfonyComponentSerializerAnnotationGroups; class User implements UserInterface { /** * @Groups({"user-read"}) */ private $id; /** * @Groups({"user-write", "user-read"}) */ private $email; /** * @Groups({"user-read"}) */ private $roles = []; /** * @Groups({"user-write"}) */ private $plainPassword; private $password; … getters and setters … } # config/doctrine/User.orm.yml AppEntityUser: type: entity table: users repositoryClass: AppRepositoryUserRepository id: id: type: integer generator: { strategy: AUTO } fields: email: type: string length: 255 password: type: string length: 255 roles: type: array
  • 28. // src/Entity/User.php namespace AppEntity; use SymfonyComponentSecurityCoreUserUserInterface; use SymfonyComponentSerializerAnnotationGroups; class User implements UserInterface { /** * @Groups({"user-read"}) */ private $id; /** * @Groups({"user-write", "user-read"}) */ private $email; /** * @Groups({"user-read"}) */ private $roles = []; /** * @Groups({"user-write"}) */ private $plainPassword; private $password; … getters and setters … } # config/doctrine/User.orm.yml AppEntityUser: type: entity table: users repositoryClass: AppRepositoryUserRepository id: id: type: integer generator: { strategy: AUTO } fields: email: type: string length: 255 password: type: string length: 255 roles: type: array # config/api_platform/resources.yaml resources: AppEntityUser: attributes: normalization_context: groups: ['user-read'] denormalization_context: groups: ['user-write']
  • 29. use ApiPlatformCoreDataPersisterContextAwareDataPersisterInterface; use AppEntityUser; use DoctrineORMEntityManagerInterface; use SymfonyComponentSecurityCoreEncoderUserPasswordEncoderInterface; class UserDataPersister implements ContextAwareDataPersisterInterface { private $entityManager; private $userPasswordEncoder; public function __construct(EntityManagerInterface $entityManager, UserPasswordEncoderInterface $userPasswordEncoder) { $this->entityManager = $entityManager; $this->userPasswordEncoder = $userPasswordEncoder; } public function supports($data, array $context = []): bool { return $data instanceof User; } public function persist($data, array $context = []) { /** @var User $data */ if ($data->getPlainPassword()) { $data->setPassword( $this->userPasswordEncoder->encodePassword($data, $data->getPlainPassword()) ); $data->eraseCredentials(); } $this->entityManager->persist($data); $this->entityManager->flush($data); return $data; } public function remove($data, array $context = []) { $this->entityManager->remove($data); $this->entityManager->flush(); }
  • 30. • Lightweight and simple authentication system • Stateless: token signed and verified server-side then stored client- side and sent with each request in an Authorization header • Store the token in the browser local storage JSON Web Token (JWT)
  • 31.
  • 32.
  • 33. • API Platform allows to easily add a JWT-based authentication to your API using LexikJWTAuthenticationBundle. • Maybe you want to use a refresh token to renew your JWT. In this case you can check JWTRefreshTokenBundle. User authentication
  • 34.
  • 35. User security checker Security <?php namespace AppSecurity; use AppExceptionAccountDeletedException; use AppSecurityUser as AppUser; use SymfonyComponentSecurityCoreExceptionAccountExpiredException; use SymfonyComponentSecurityCoreExceptionCustomUserMessageAuthenticat use SymfonyComponentSecurityCoreUserUserCheckerInterface; use SymfonyComponentSecurityCoreUserUserInterface; class UserChecker implements UserCheckerInterface { public function checkPreAuth(UserInterface $user) { if (!$user instanceof AppUser) { return; } // user is deleted, show a generic Account Not Found message. if ($user->isDeleted()) { throw new AccountDeletedException(); } } public function checkPostAuth(UserInterface $user) { if (!$user instanceof AppUser) { return; } // user account is expired, the user may be notified if ($user->isExpired()) { throw new AccountExpiredException('...'); } } }
  • 36. User security checker Security # config/packages/security.yaml # ... security: firewalls: main: pattern: ^/ user_checker: AppSecurityUserChecker # ...
  • 37. Resource and operation level Security # api/config/api_platform/resources.yaml AppEntityBook: attributes: security: 'is_granted("ROLE_USER")' collectionOperations: get: ~ post: security: 'is_granted("ROLE_ADMIN")' itemOperations: get: ~ put: security_: 'is_granted("ROLE_ADMIN") or object.owner == user'
  • 38. Resource and operation level using Voters Security # api/config/api_platform/resources.yaml AppEntityBook: itemOperations: get: security_: 'is_granted('READ', object)' put: security_: 'is_granted('UPDATE', object)'
  • 39. • A JWT is self-contained, meaning that we can trust into its payload for processing the authentication. In a nutshell, there should be no need for loading the user from the database when authenticating a JWT Token, the database should be hit only once for delivering the token. • It means you will have to fetch the User entity from the database yourself as needed (probably through the Doctrine EntityManager). JWT tip A database-less user provider
  • 40. JWT tip A database-less user provider # config/packages/security.yaml security: providers: jwt: lexik_jwt: ~ security: firewalls: api: provider: jwt guard: # ...
  • 42. • Locastic Api Translation Bundle • Translation bundle for ApiPlatform based on Sylius translation • It requires two entities: Translatable & Translation entity • Open source • https://github.com/Locastic/ApiPlatformTranslationBundle • https://locastic.com/blog/having-troubles-with-implementing- translations-in-apiplatform/ Creating multi-language APIs
  • 44. Get response by locale GET /api/posts/1?locale=en { "@context": "/api/v1/contexts/Post", "@id": "/api/v1/posts/1')", "@type": "Post", "id": 1, "datetime":"2019-10-10", "title":"Hello world", "content":"Hello from Verona!" }
  • 45. Get response with all translations GET /api/posts/1?groups[]=translations { "@context": "/api/v1/contexts/Post", "@id": "/api/v1/posts/1')", "@type": "Post", "id": 1, "datetime":"2019-10-10", "translations": { "en":{ "title":"Hello world", "content":"Hello from Verona!", "locale":"en" }, "it":{ "title":"Ciao mondo", "content":"Ciao da Verona!", "locale":"it" } } }
  • 46. • Endpoint for creating new language • Creates all Symfony translation files when new language is added • Endpoint for editing each language translation files Adding languages and translations dynamically
  • 47. Manipulating the Context Context namespace AppEntity; use ApiPlatformCoreAnnotationApiResource; use SymfonyComponentSerializerAnnotationGroups; /** * @ApiResource( * normalizationContext={"groups"={"book:output"}}, * denormalizationContext={"groups"={"book:input"}} * ) */ class Book { // ... /** * This field can be managed only by an admin * * @var bool * * @Groups({"book:output", "admin:input"}) */ public $active = false; /** * This field can be managed by any user * * @var string * * @Groups({"book:output", "book:input"}) */ public $name; // ... }
  • 48. Manipulating the Context Context # api/config/services.yaml services: # ... 'AppSerializerBookContextBuilder': decorates: 'api_platform.serializer.context_builder' arguments: [ '@AppSerializerBookContextBuilder.inner' ] autoconfigure: false
  • 49. // api/src/Serializer/BookContextBuilder.php namespace AppSerializer; use ApiPlatformCoreSerializerSerializerContextBuilderInterface; use SymfonyComponentHttpFoundationRequest; use SymfonyComponentSecurityCoreAuthorizationAuthorizationCheckerInterface; use AppEntityBook; final class BookContextBuilder implements SerializerContextBuilderInterface { private $decorated; private $authorizationChecker; public function __construct(SerializerContextBuilderInterface $decorated, AuthorizationCheckerInterface $authorizationChecker) { $this->decorated = $decorated; $this->authorizationChecker = $authorizationChecker; } public function createFromRequest(Request $request, bool $normalization, ?array $extractedAttributes = null): array { $context = $this->decorated->createFromRequest($request, $normalization, $extractedAttributes); $resourceClass = $context['resource_class'] ?? null; if ($resourceClass === Book::class && isset($context['groups']) && $this->authorizationChecker- >isGranted('ROLE_ADMIN') && false === $normalization) { $context['groups'][] = 'admin:input'; } return $context; } }
  • 51. • The Messenger component helps applications send and receive messages to/from other applications or via message queues. • Easy to implement • Making async easy • Many transports are supported to dispatch messages to async consumers, including RabbitMQ, Apache Kafka, Amazon SQS and Google Pub/Sub. Symfony Messenger
  • 52.
  • 53.
  • 54. • Allows to implement the Command Query Responsibility Segregation (CQRS) pattern in a convenient way. • It also makes it easy to send messages through the web API that will be consumed asynchronously. • Async import, export, image processing… any heavy work Symfony Messenger & API Platform
  • 55. CQRS Symfony Messenger & API Platform AppEntityPasswordResetRequest: collectionOperations: post: status: 202 itemOperations: [] attributes: messenger: true output: false
  • 56. CQRS Symfony Messenger & API Platform <?php namespace AppHandler; use AppEntityPasswordResetRequest; use SymfonyComponentMessengerHandlerMessageHandlerInterfac final class PasswordResetRequestHandler implements MessageHand { public function __invoke(PasswordResetRequest $forgotPassw { // do some heavy things } } <?php namespace AppEntity; final class PasswordResetRequest { public $email; }
  • 57. CQRS /w DTO Symfony Messenger & API Platform AppEntityUser: collectionOperations: post: status: 202 itemOperations: [] attributes: messenger: “input” input: “ResetPasswordRequest::class” output: false // api/src/Entity/User.php namespace AppEntity; use ApiPlatformCoreAnnotationApiResource; use AppDtoResetPasswordRequest; final class User { }
  • 58. CQRS /w DTO Symfony Messenger & API Platform // api/src/Handler/ResetPasswordRequestHandler.php namespace AppHandler; use AppDtoResetPasswordRequest; use SymfonyComponentMessengerHandlerMessageHandlerInterface; final class ResetPasswordRequestHandler implements MessageHandle { public function __invoke(ResetPasswordRequest $forgotPasswor { // do something with the resource } } // api/src/Dto/ResetPasswordRequest.php namespace AppDto; use SymfonyComponentValidatorConstraints as Assert; final class ResetPasswordRequest { public $var; }
  • 59. <?php namespace AppDataPersister; use ApiPlatformCoreDataPersisterContextAwareDataPersisterInterface; use AppEntityImageMedia; use DoctrineORMEntityManagerInterface; use SymfonyComponentMessengerMessageBusInterface; class ImageMediaDataPersister implements ContextAwareDataPersisterInterface { private $entityManager; private $messageBus; public function __construct(EntityManagerInterface $entityManager, MessageBusInterface $messageBus) { $this->entityManager = $entityManager; $this->messageBus = $messageBus; } public function supports($data, array $context = []): bool { return $data instanceof ImageMedia; } public function persist($data, array $context = []) { $this->entityManager->persist($data); $this->entityManager->flush($data); $this->messageBus->dispatch(new ProcessImageMessage($data->getId())); return $data; } public function remove($data, array $context = []) { $this->entityManager->remove($data); $this->entityManager->flush(); $this->messageBus->dispatch(new DeleteImageMessage($data->getId())); } }
  • 60. namespace AppEventSubscriber; use ApiPlatformCoreEventListenerEventPriorities; use AppEntityBook; use SymfonyComponentEventDispatcherEventSubscriberInterface; use SymfonyComponentHttpFoundationRequest; use SymfonyComponentHttpKernelEventViewEvent; use SymfonyComponentHttpKernelKernelEvents; use SymfonyComponentMessengerMessageBusInterface; final class BookMailSubscriber implements EventSubscriberInterface { private $messageBus; public function __construct(MessageBusInterface $messageBus) { $this->messageBus = $messageBus; } public static function getSubscribedEvents() { return [ KernelEvents::VIEW => ['sendMail', EventPriorities::POST_WRITE], ]; } public function sendMail(ViewEvent $event) { $book = $event->getControllerResult(); $method = $event->getRequest()->getMethod(); if (!$book instanceof Book || Request::METHOD_POST !== $method) { return; } // send to all users 2M that new book has arrived this->messageBus->dispatch(new SendEmailMessage(‘new-book’, $book->getTitle())); } }
  • 61. • problem: • different objects from source and in our database • multiple sources of data (3rd party) • DataTransform transforms from source object to our object • exporting to CSV files Using DTOs with import and export
  • 62. resources: AppEntityOrder: collectionOperations: get: ~ exports: method: POST path: '/orders/export' formats: csv: ['text/csv'] pagination_enabled: false output: "OrderExport::class" normalization_context: groups: ['order-export']
  • 64.
  • 65. • Redis + NodeJS • Pusher • ReactPHP • … • but to be honest PHP is not build for realtime :) Real-time applications with API platform
  • 66.
  • 67. • Fast, written in Go • native browser support, no lib nor SDK required (built on top of HTTP and server-sent events) • compatible with all existing servers, even those who don't support persistent connections (serverless architecture, PHP, FastCGI…) • Automatic HTTP/2 and HTTPS (using Let's Encrypt) support • CORS support, CSRF protection mechanism • Cloud Native, follows the Twelve-Factor App methodology • Open source (AGPL) • … Mercure
  • 68. resources: AppEntityGreeting: attributes: mercure: true const eventSource = new EventSource('http://localhost:3000/hub?topic=' + encodeURIComponent('http://example.com/greeting/1')); eventSource.onmessage = event => { // Will be called every time an update is published by the server console.log(JSON.parse(event.data)); }
  • 70.
  • 71. • Unit tests • test your logic, refactor your code using SOLID priciples • Integration tests • validation • 3rd party integrations • database queries • Functional tests • response code, header and content (expected fields in expected format) Type of tests
  • 72. • Ask yourself: “Am I sure the code I tested works as it should?” • 100% coverage doesn’t guarantee your code is fully tested and working • Write test first is just one of the approaches • Legacy code: • Start replicating bugs with tests before fixing them • Test at least most important and critical parts Testing tips and tricks
  • 74. PHP Matcher Library that enables you to check your response against patterns.
  • 77. • Infection - tool for mutation testing • PHPStan - focuses on finding errors in your code without actually running it • Continuous integration (CI) -  enables you to run your tests on git on each commit Tools for checking test quality
  • 78. Api Platform is awesome! Conclusion
  • 80. Questions? Antonio Perić-Mažar t: @antonioperic m: antonio@locastic.com Paula Čučuk t: @paulala_14 m: paula@locastic.com