APIP 2024 - All the Challenges of Sylius Migration to API Platform 3.pdf

ukaszChruciel1 110 views 66 slides Sep 20, 2024
Slide 1
Slide 1 of 66
Slide 1
1
Slide 2
2
Slide 3
3
Slide 4
4
Slide 5
5
Slide 6
6
Slide 7
7
Slide 8
8
Slide 9
9
Slide 10
10
Slide 11
11
Slide 12
12
Slide 13
13
Slide 14
14
Slide 15
15
Slide 16
16
Slide 17
17
Slide 18
18
Slide 19
19
Slide 20
20
Slide 21
21
Slide 22
22
Slide 23
23
Slide 24
24
Slide 25
25
Slide 26
26
Slide 27
27
Slide 28
28
Slide 29
29
Slide 30
30
Slide 31
31
Slide 32
32
Slide 33
33
Slide 34
34
Slide 35
35
Slide 36
36
Slide 37
37
Slide 38
38
Slide 39
39
Slide 40
40
Slide 41
41
Slide 42
42
Slide 43
43
Slide 44
44
Slide 45
45
Slide 46
46
Slide 47
47
Slide 48
48
Slide 49
49
Slide 50
50
Slide 51
51
Slide 52
52
Slide 53
53
Slide 54
54
Slide 55
55
Slide 56
56
Slide 57
57
Slide 58
58
Slide 59
59
Slide 60
60
Slide 61
61
Slide 62
62
Slide 63
63
Slide 64
64
Slide 65
65
Slide 66
66

About This Presentation

Migrating Sylius to API Platform 3 is no small feat. In this presentation, we will discuss the many challenges we faced during this complex transition and the innovative solutions we used to overcome them. This journey began with a clear goal: to leverage the robust capabilities of API Platform 3 to...


Slide Content

SEPTEMBER 19 -20, 2024 - LILLE, FRANCE & ONLINE
All the Challenges of Sylius
migration
to API Platform 3

Tests
01
02
03
04
05
Commands
Open API
Conclusion
Subresources

About me
Łukasz Chruściel

✔Co-owner at Commerce Weavers
✔Core Team Lead at Sylius
✔I’m involved in 3 Sylius & API Platform
eCommerce projects

You don’t have to remind me every year…
API Platform Conference 2022
What we’ve learned
designing the new Sylius API

About team
✔Grzegorz - as an effort lead
✔Developers: Jan, Kuba, Kamil, Michał,
Rafał, Karol
Internal Sylius Team
@Sylius
sylius.com

Scale of the migration
v2.7.18 v3.4.0
Where we were? Where we go?

We have it!

752 commits
Migration statistics
1712 files changed

73 shop endpoints
Sylius API statistics
188 admin endpoints
40808 lines of code
across 562 files

Main problems
✔New metadata class
✔New configuration reference
✔New architecture of Providers/Processors
✔New subresource architecture
✔Sylius is not a typical use-case

01
02
03
04
05
Tests
Commands
Serialization
Open API
Conclusion

Command 101

✔Decouples infrastructure from application
✔Holds customer intention
✔Commands never contain entities
✔Handlers are stateless
✔Maintains the same behaviour even
if framework has changed
API Platform Con 2021
Sylius & API Platform: the
story of integration

Architecture 101

Request is like
Says more than a 1000 words

Mapping of request body
to command is not
enough

Channel Order
Language
User

Request is not only the body
Request data Translates to
✔Hostname
✔Language Header
✔Authorization Header
✔URI attributes
✔Channel code
✔Local code
✔Shop User ID
✔Order token

Command enrichment

Serializer context enrichment

Serializer context enrichment
/api/v2/shop/orders/{tokenValue}/items

{
"productVariant": "string",
"quantity": 1
}

Result?
✔Better design
✔Command immutability
✔Better decoupling from framework

01
02
03
04
05
Tests
Commands
Subresources
Open API
Conclusion

Tests 101

✔BDD for behaviour
✔Behat for BDD

✔Contract testing for API design
✔PHPUnit for contract testing
API Platform Con 2021
Sylius & API Platform: the
story of integration

@shopping_cart
Feature: Adding a simple product to the cart
In order to select products for purchase
As a Visitor
I want to be able to add simple products to cart

Background:
Given the store operates on a single channel in "United States"
And the store has a product "T-Shirt banana" priced at "$12.54"
And the store ships everywhere for free

@api @ui @javascript
Scenario: Adding a simple product to the cart
When I add this product to the cart
Then I should be on my cart summary page
And I should be notified that the product has been successfully added
And there should be one item in my cart
And this item should have name "T-Shirt banana"
Behaviour Driven
Development

public function it_adds_item_to_order_as_guest (): void
{
$this?lsetUpDefaultPostHeaders ();

$this?lloadFixturesFromFiles ([
'channel/channel.yaml' ,
'cart.yaml',
'country.yaml',
'shipping_method.yaml' ,
'payment_method.yaml' ,
]);

$tokenValue = $this?lpickUpCart();

$this?lrequestPost(
uri: sprintf('/api/v2/shop/orders/%s/items' , $tokenValue),
body: [
'productVariant' ?? '/api/v2/shop/product-variants/MUG_BLUE' ,
'quantity' ?? 4,
],
);

$this?lassertResponseCreated ('shop/checkout/cart/add_item' );
}
Contract testing

{
"@context": "\/api\/v2\/contexts\/Order",
"@id": "\/api\/v2\/shop\/orders\/token",
"@type": "Order",
"channel": "\/api\/v2\/shop\/channels\/WEB",
"currencyCode": "USD",
"localeCode": "en_US",
"state": "cart",
"checkoutState": "cart",
"paymentState": "cart",
"shippingState": "cart",
"tokenValue": "token",
"number": null,
"items": [
{
"@id": "\/api\/v2\/shop\/orders\/token\/items\/@integer@",
"@type": "OrderItem",
"variant": "\/api\/v2\/shop\/product-variants\/MUG_BLUE",
"productName": "Mug",
"id": "@integer@",
"quantity": 4,
"unitPrice": 2000,
"originalUnitPrice": 2000,
"total": 8000,
"discountedUnitPrice" : 2000,
"subtotal": 8000
}
],
"itemsTotal": 8000,
"total": 8500,
}
Contract testing

How did it help us?

How did it help us?
✔Safety net
✔Easy to determine changes in contact
✔Way to do it one by one

01
02
03
04
05
Tests
Commands
Open API
Conclusion
Subresources

Why do we need it?

Auto increments…


Insecure Direct Object Reference (IDOR)
https://cheatsheetseries.owasp.org/cheatsheets/Insecure
_Direct_Object_Reference_Prevention_Cheat_Sheet.html

Back in the days
wild west / horror / hell
It was wild

<resource class="%sylius.model.order.class%" shortName="Order">
<attribute name="validation_groups" >sylius?$attribute>

<itemOperations>
<itemOperation name="shop_add_item">
<attribute name="method">POST?$attribute>
<attribute name="path">/shop/orders/{tokenValue}/items?$ attribute>
<attribute name="messenger">input?$attribute>
<attribute name="input">Sylius\Bundle\ApiBundle\Command\Cart\AddItemToCart?$ attribute>
<attribute name="normalization_context" >
<attribute name="groups">
< attribute>shop:cart:show?$attribute>
< attribute>sylius:shop:cart:show?$attribute>
444444444444444?$attribute>
44444444444?$attribute>
<attribute name="denormalization_context" >
<attribute name="groups">
< attribute>shop:cart:add_item?$attribute>
< attribute>sylius:shop:cart:add_item?$attribute>
444444444444444?$attribute>
44444444444?$attribute>
<attribute name="openapi_context">
<attribute name="summary">Adds Item to cart.?$attribute>
44444444444?$attribute>
4444444?$itemOperation>
?$resource>
Before

Nowadays
image of modern, nice country with not obvious
issues

<resource class="Sylius\Component\Core\Model\OrderItem" uriTemplate="/shop/orders/{tokenValue}/items" >
<uriVariables>
<uriVariable
parameterName ="tokenValue"
fromClass ="Sylius\Component\Core\Model\Order"
fromProperty ="items"
??
444?$uriVariables>
<operations>
<operation class="ApiPlatform\Metadata\GetCollection" >
<normalizationContext >
<values>
< value name="groups">
< values>
< value>sylius:shop:cart:show?$value>
44444444444444444444444?$values>
4444444444444444444?$value>
444444444444444?$values>
44444444444?$normalizationContext >
4444444?$operation>
444?$operations>
?$resource>
After

Nowadays
image of modern, nice country with not obvious
issues

Some issues still exists

Double nested structures

Double nested structures
What we want is:
{"@id": "\/api\/v2\/shop\/orders\/TOKEN\/items\/ID\/item-units\/ID"},
What we have right now is:
{"@id": "\/api\/v2\/shop\/order-item-units\/ID"},

Some issues still exists

01
02
03
04
05
Tests
Commands
Open API
Conclusion
Subresources

API Platform
documentation
??????

But sometimes it is not
enough

Cart pick up

final readonly class OpenApiFactory implements OpenApiFactoryInterface
{
public function ??construct(
private OpenApiFactoryInterface $decorated,
/** @var iterable<DocumentationModifierInterface> ??
private iterable $openApiModifiers,
) {
Assert?{allIsInstanceOf($openApiModifiers, DocumentationModifierInterface ?{class);
}

public function ??invoke(array $context = []): OpenApi
{
$openApi = ($this?ldecorated)($context);

foreach ($this?lopenApiModifiers as $openApiModifier) {
$openApi = $openApiModifier?lmodify($openApi);
}

return $openApi;
}
}
Documentation modifiers

$acceptLanguageHeaderParameter = new Parameter(
name: 'Accept-Language',
in: 'header',
description: 'Locales in this enum are all locales defined in the shop and only enabled ones
will work in the given channel in the shop.' ,
required: false,
schema: [
'type' ?? 'string',
'enum' ?? array_map(
fn (LocaleInterface $locale): string ?? $locale?lgetCode(),
$this?llocaleRepository?lfindAll(),
),
],
);
Documentation modifiers

Documentation modifiers

01
02
03
04
05
Tests
Commands
API structure
Conclusion
Subresources

Key take-outs
✔Use commands to decouple from framework
-With a little bit of effort you can map more than just request body to command
✔Tests are a must have for successful migration
✔New operation system rocks
-but we may improve some stuff for more deeply nested classes

Thank you!
FOLLOW ME!
@lukaszchrusciel
commerceweavers.com
Any questions?

01 - Interesting
introduction
Some cool stuff

{
"@context": "/contexts/ConstraintViolationList" ,
"@type": "ConstraintViolationList" ,
"hydra:title": "An error occurred",
"hydra:description": "isbn: This value is neither a valid ISBN-10 nor a valid
ISBN-13.\ntitle: This value should not be blank." ,
"violations": [
{
"propertyPath": "isbn",
"message": "This value is neither a valid ISBN-10 nor a valid ISBN-13."
},
{
"propertyPath": "title",
"message": "This value should not be blank."
}
]
}

Some code

Some title
Add your text here

Another example
✔You can add stuff here
✔Or here…
✔Or even here!

Another example
✔You can add stuff here
✔Or here…
✔Or even here!
Add something here

Another example
✔You can add stuff here
✔Or here…
✔Or even here!

Another slide
Example
✔You can add stuff here
✔Or here…
✔Or even here!
Some content you want to
highlight

Another example
✔You can add stuff here
✔Or here…
✔Or even here!
Another content
to highlight
Add caption (or not)

Some title
Add your text here
Say something (or delete this)

Some title
Add your text here

Some title
Some interesting
point
Another interesting
point
A final interesting
point

Some title
Some interesting
point
A final interesting
point
Another interesting point

You can say interesting
things here
✔Large topics discussed on that wonderful
slide
✔You can write something else
Hello!

Thank you!
Hello!
Any questions?
@JaneDoeTwitter
jane-doe.com
Follow me on social media

Need graphic stuff for your slides?

Need graphic stuff for your slides?
Something cool