Slide 1

Slide 1 text

Dmitry Petrov [email protected] Handle Complex POST/PATCH requests in RESTful API

Slide 2

Slide 2 text

ProFIT Dmitry Petrov The system of process control printing Product Fulfillment and Information Tracking

Slide 3

Slide 3 text

ProFIT Dmitry Petrov Daily: ~ 1 000 orders ~1 000 000 print productions 1 hour of downtime ~ 25 000$ Product Fulfillment and Information Tracking

Slide 4

Slide 4 text

RESTful API Dmitry Petrov ~ 60 entity ~100 API endpoints Complex business logic RESTful API for ProFIT

Slide 5

Slide 5 text

Dmitry Petrov GET /api/orders/12/items/fg45sf54 The server response: { "id": "fg45sf54", "url": "http://localhost/api/orders/12/items/fg45sf54", "product": "business cards", "quantity": 1000, "previews": { "front": { "large": "http://localhost/large/front.jpg", "medium": "http://localhost/medium/front.jpg", "small": "http://localhost/small/front.jpg", }, "back": { "large": "http://localhost/large/back.jpg", "medium": "http://localhost/medium/back.jpg", "small": "http://localhost/small/back.jpg", } } } GET /api/orders/12 The server response: { "id": 12, "url": "http://localhost/api/orders/12", "client": { "firstname": "Dmitry", "lastname": "Petrov", "email": "", "phone": null, "address": { "country": "Russia", "city": "Saratov", "zip": 123456, "street": "Vavilova", "residentional": false } } } RESTful API, examples GET

Slide 6

Slide 6 text

Dmitry Petrov GET /api/product-box-types/12-type/associations The server response: [ { "id": 1, "product": "business_cards", "quantity": 1000 }, ...... ] GET /api/machines/KARAT+1/hot-folders The server response: [ { "path":"/home/somepath/", "types": [ "34-f-Type", "33-S-Type", ...... ] }, ...... ] GET /api/press-sheets/134/label The server response: { "label": "epl string" } RESTful API, examples GET

Slide 7

Slide 7 text

Dmitry Petrov POST http://localhost/api/press-sheets/12/transition Body of the request: { "transition": "start:printing:front", "note": null } POST http://localhost/api/orders, PUT http://localhost/api/orders/12 Body of the request: { "id": 12, "client": { "firstname": "Dmitry", "lastname": "Petrov", "email": "", "phone": null, "address": { "country": "Russia", "city": "Saratov", "zip": 123456, "street": "Vavilova", "residentional": false } } } RESTful API, examples POST / PUT

Slide 8

Slide 8 text

Dmitry Petrov PATCH http://localhost/api/orders/12 Body of the request: { "client": { "email": "", "phone": null } } PATCH http://localhost/api/orders/12 Body of the request: { "client": { "email": "" }, "address": { "street": "Vavilova", "residentional": true } } Object: { "id": 12, "client": { "firstname": "Dmitry", "lastname": "Petrov", "email": "[email protected]", "phone": "8-888-999", "address": { "country": "Russia", "city": "Saratov", "zip": 123456, "street": "Vavilova", "residentional": false } } } RESTful API, examples PATCH

Slide 9

Slide 9 text

Dmitry Petrov Become thoughtful . . .

Slide 10

Slide 10 text

DTO Dmitry Petrov Data Transfer Object DTO attribute1: String attribute2: String Assembler createDTO updateDomainObject serialize deserialize DomainObject1 attribute1: String DomainObject2 attribute2: String

Slide 11

Slide 11 text

Dmitry Petrov GET /api/orders/12 The server response: { "id": 12, "url": "http://localhost/orders/12", "client": { "firstname": "Dmitry", "lastname": "Petrov", "email": "", "phone": null, "address": { "country": "Russia", "city": "Saratov", "zip": 123456, "street": "Vavilova", "residentional": false } } } Examples of DTO

Slide 12

Slide 12 text

Dmitry Petrov { "transition": "start:printing:front", "note": null } { "label": "epl string" } [ { "path":"/home/somepath/", "types": [ "34-f-Type", ...... ] }, ...... ] Examples of DTO

Slide 13

Slide 13 text

Dmitry Petrov Reducing the number of queries Independence from API "Makes you think" The benefits of using DTO pattern

Slide 14

Slide 14 text

Dmitry Petrov FOSRestBundle JMSSerializerBundle LiipHelloBundle FOSCommentBundle Popular bundles and examples

Slide 15

Slide 15 text

Dmitry Petrov GET /api/orders/12 The server response: { "id": 12, "url": "http://localhost/api/orders/12", "client": { "firstname": "Dmitry", "lastname": "Petrov", "email": "", "phone": null, "address": { "country": "Russia", "city": "Saratov", "zip": 123456, "street": "Vavilova", "residentional": false } } } JMSSerializerBundle & GET method

Slide 16

Slide 16 text

Dmitry Petrov POST /api/orders, Body of the request: { "id": 12, "client": { "firstname": "Dmitry", "lastname": "Petrov", "email": "", "phone": null, "address": { "country": "Russia", "city": "Saratov", "zip": 123456, "street": "Vavilova", "residentional": false } } } JMSSerializerBundle & POST method

Slide 17

Slide 17 text

Dmitry Petrov JMSSerializerBundle & PATCH method

Slide 18

Slide 18 text

Dmitry Petrov $this->deserialize($request, 'Rest\OrderDTO', 'json'); JMSSerializerBundle

Slide 19

Slide 19 text

MERGE Dmitry Petrov $this->merge($oldDTO, $newDTO); JMSSerializerBundle

Slide 20

Slide 20 text

Dmitry Petrov Merging DTO

Slide 21

Slide 21 text

Dmitry Petrov PATCH /api/orders/12 Request: { "client": { "email": "", "phone": null } } JMSSerializerBundle & PATCH method

Slide 22

Slide 22 text

Problems / Disadvantages Dmitry Petrov GET - serialization of null values PATCH - deserialized into an object PATCH - merge null values MERGE - a lot of useless code RESTful API, JMSSerializerBundle

Slide 23

Slide 23 text

Dmitry Petrov SimpleThingsFormSerializerBundle July 15, 2012 SimpleThingsFormSerializerBundle

Slide 24

Slide 24 text

Dmitry Petrov SimpleThingsFormSerializerBundle

Slide 25

Slide 25 text

Dmitry Petrov GET /api/orders/12 The server response: { "id": "12", "url": "http://localhost/orders/12", "client": { "firstname": "Dmitry", "lastname": "Petrov", "email": "", "phone": "", "address": { "country": "Russia", "city": "Saratov", "zip": "123456", "street": "Vavilova", "residentional": "false" } } } SimpleThingsFormSerializerBundle

Slide 26

Slide 26 text

Problems / Disadvantages Dmitry Petrov Converting data into string Lack of support PATCH method (v. 2.0) The ideological aversion The dirty mix *Type and *DTO SimpleThingsFormSerializerBundle

Slide 27

Slide 27 text

Dmitry Petrov Отпуск

Slide 28

Slide 28 text

Dmitry Petrov Отпуск

Slide 29

Slide 29 text

Dmitry Petrov Отпуск

Slide 30

Slide 30 text

Requirements Dmitry Petrov (Un)Serialization of objects Preservation of the type in data Metadata cache Reinvent the wheel

Slide 31

Slide 31 text

Assumptions Dmitry Petrov The output format is json Metadata is stored in yml There are get/set methods Reinvent the wheel

Slide 32

Slide 32 text

After 36 hours... train Saratov - Kiev is comming 30 hours Dmitry Petrov SimpleSerializer SimpleSerializerBundle Details can be found on the habr Reinvent the wheel

Slide 33

Slide 33 text

Benefits Dmitry Petrov This is library Separation of serialization rules and format Absence voiced disadvantages "Inteligent" deserialization SimpleSerializer

Slide 34

Slide 34 text

Dmitry Petrov SimpleSerializer & POST / PATCH

Slide 35

Slide 35 text

Dmitry Petrov SimpleSerializer & POST method

Slide 36

Slide 36 text

What? Where? When? Dmitry Petrov Parameters of requests Data transfer objects Business logic RESTful API, validation

Slide 37

Slide 37 text

Parameters of requests Dmitry Petrov /api/orders/12 /api/boxes/BOX-1-1 /api/orders?valid=true RESTful API, validation

Slide 38

Slide 38 text

Routing requirements Dmitry Petrov RESTful API, validation

Slide 39

Slide 39 text

Dmitry Petrov ParameterChecker RESTful API, validation

Slide 40

Slide 40 text

Dmitry Petrov RESTful API, validation

Slide 41

Slide 41 text

Dmitry Petrov RESTful API, validation

Slide 42

Slide 42 text

Dmitry Petrov AbstractRestController RESTful API, validation

Slide 43

Slide 43 text

Dmitry Petrov PATCH /api/orders/12 Body of the request: { "client": { "email": "", "comment": "I'm hacker" } } Object: { "id": 12, "client": { "firstname": "Dmitry", "lastname": "Petrov", "email": "[email protected]", "phone": "8-888-999", "address": { "country": "Russia", "city": "Saratov", "zip": 123456, "street": "Vavilova", "residentional": false } } } RESTful API, validation

Slide 44

Slide 44 text

Dmitry Petrov POST /api/press-sheets/12/transition Body of the request: { "transition": "start:printing:front", "note": null, "comment": "I'm hacker" } POST /api/press-sheets/12/transition Body of the request: { "transition": "start:printing:front", "comment": "I'm hacker" } Object: { "transition": "start:printing:front", "note": null } RESTful API, validation

Slide 45

Slide 45 text

Dmitry Petrov How, where and when to handle these situations? RESTful API, validation

Slide 46

Slide 46 text

Dmitry Petrov REST APIs with Symfony2: The Right Way

Slide 47

Slide 47 text

Dmitry Petrov Disadvantages Conventionalism Duplication of code Only works as filter REST APIs with Symfony2: The Right Way

Slide 48

Slide 48 text

Dmitry Petrov "Inteligent" deserialization 3 modes of deserialization: Strict, Medium strict, Non-strict + Support groups SimpleSerializer

Slide 49

Slide 49 text

Dmitry Petrov RESTful API, handle of DTO

Slide 50

Slide 50 text

Dmitry Petrov RESTful API, handle of domain object

Slide 51

Slide 51 text

Behat, PHPUnit Dmitry Petrov Controllers Data access layer Service layer RESTful API, testing

Slide 52

Slide 52 text

Dmitry Petrov RESTful API, Behat scenario

Slide 53

Slide 53 text

Problems Dmitry Petrov Run time: Behat ~ 90 minutes PHPUnit ~ 5 minutes RESTful API, testing

Slide 54

Slide 54 text

WSSE Dmitry Petrov Atom Authentication How to create a custom Authentication Provider EscapeWSSEAuthenticationBundle (v. 2.0) MopaWSSEAuthenticationBundle (v. 2.1) RESTful API, authentication

Slide 55

Slide 55 text

WSSE Header Dmitry Petrov X-WSSE: UsernameToken Username="bob", PasswordDigest="quR/EWLAV4xLf9Zqyw4pDmfV9OY=", Nonce="d36e316282959a9ed4c89851497a717f", Created="2003-12-15T14:43:07Z" RESTful API, authentication

Slide 56

Slide 56 text

Password digest Dmitry Petrov Base64 (SHA1 (Nonce + CreationTimestamp + Password)) RESTful API, authentication

Slide 57

Slide 57 text

Dmitry Petrov RESTful API, The End

Slide 58

Slide 58 text

Any questions? Dmitry Petrov @old_fightmaster RESTful API https://github.com/opensoft https://github.com/fightmaster Special thanks to ProFIT team