Skip to content

Commit 7ab2db0

Browse files
author
shopwareBot
committed
[create-pull-request] automated change
1 parent 83c0737 commit 7ab2db0

7 files changed

+635
-0
lines changed

SUMMARY.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,7 @@
452452
* [Tax providers](resources/references/adr/2022-04-28-tax-providers.md)
453453
* [Remove static analysis with psalm](resources/references/adr/2022-05-12-remove-static-analysis-with-psalm.md)
454454
* [Rule condition field abstraction](resources/references/adr/2022-05-23-rule-condition-field-abstraction.md)
455+
* [Integrate app into flow event](resources/references/adr/2022-06-17-integrate-app-into-flow-event.md)
455456
* [Add typescript support for storefront js](resources/references/adr/2022-06-24-add-typescript-support-for-storefront-js.md)
456457
* [Providing the admin extension sdk](resources/references/adr/2022-06-27-providing-the-admin-extension-sdk.md)
457458
* [Blog concept](resources/references/adr/2022-07-19-blog-concept.md)
@@ -479,6 +480,10 @@
479480
* [Mocking repositories](resources/references/adr/2023-04-01-mocking-repositories.md)
480481
* [Disable css autoprefixer](resources/references/adr/2023-04-03-disable-css-autoprefixer.md)
481482
* [Jest test files should be javascript only](resources/references/adr/2023-04-14-jest-test-files-should-be-javascript-only.md)
483+
* [Optimise cart cleanup](resources/references/adr/2023-05-09-optimise-cart-cleanup.md)
484+
* [Stock API](resources/references/adr/2023-05-15-stock-api.md)
485+
* [Switch to uuidv7](resources/references/adr/2023-05-22-switch-to-uuidv7.md)
486+
* [Exception log levels](resources/references/adr/2023-05-25-exception-log-levels.md)
482487
* [YYYY MM DD template](resources/references/adr/YYYY-MM-DD-template.md)
483488

484489
* [App Reference](resources/references/app-reference/README.md)

resources/references/adr/2022-02-24-domain-exceptions.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ Until now, we have implemented many different exception classes in Shopware to m
1818
However, this pattern is very cumbersome for developers to maintain properly, which is why we often fall back on the old \RuntimeException.
1919
Another disadvantage of this pattern is that the system is overwhelmed with exception classes and therefore the overview of possible exceptions suffers.
2020

21+
Domain exceptions should be specific in 99% of cases, otherwise, they are no longer clearly identifiable and traceable. If we want to add a generic exception like EntityNotFound exceptions everywhere, it will not help API consumer to identify the root cause.
22+
Therefore, it is for a good reason that there're similar exceptions occur again in many places. If there's something goes wrong from anywhere, there should be an unique code for it.
23+
In good software, you have a unique code for each error. This code is then listed in a code list that is publicly available.
24+
For each code, there is then a clear documentation, of when and where it occurs and how to fix it.
25+
2126
## Solution
2227
With the following pattern I would like to achieve the following goals:
2328
- Developers can **no longer** just throw any **\RuntimeException** that can't be traced.
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
---
2+
title: Integrate an app into the flow event
3+
date: 2022-10-11
4+
area: business-ops
5+
tags: [flow, app]
6+
---
7+
8+
# Integrate an app into the flow event
9+
10+
{% hint style="info" %}
11+
This document represents an architecture decision record (ADR) and has been mirrored from the ADR section in our Shopware 6 repository.
12+
You can find the original version [here](https://github.com/shopware/platform/blob/trunk/adr/2022-06-17-integrate-app-into-flow-event.md)
13+
{% endhint %}
14+
15+
# 2022-10-11 - Integrate an app into the flow event
16+
17+
## Context
18+
Currently, apps can not extend the list of available events in the flow builder.
19+
20+
## Decision
21+
We update the flow builder so the apps can expand the list of available trigger events in the flow builder UI.
22+
23+
### Flow aware
24+
We define flow aware classes to detect which data will be available in the event and a function to get them.
25+
26+
**Problems:**
27+
* We are unsure which data will the app event provide
28+
29+
**Solution:**
30+
* We create an interface CustomAppAware that will use as implementation for the custom event from the app.
31+
32+
**Example pseudocode**
33+
```php
34+
interface CustomAppAware
35+
{
36+
public const APP_DATA = 'customAppData';
37+
38+
public function getCustomAppData(): array;
39+
}
40+
```
41+
42+
### Flow storer
43+
Flow data storer saves the data from the event as the [StorableFlow](../../adr/admin/flow-builder/2022-07-21-adding-the-StorableFlow-to-implement-DelayAction-in-Flow-Builder.md), and we use them in flow actions.
44+
45+
**Problems:**
46+
* Currently, we keep the event data in the core but do not store any personalized event data from the application.
47+
48+
**Solution:**
49+
* We create a CustomAppStorer, which is used to store the data from custom app event.
50+
* When the API triggers a custom trigger, the data in the body will be stored in FlowStore by their keys.
51+
52+
*Example to define data from the API:*
53+
```json
54+
{
55+
"customerId": "d20e4d60e35e4afdb795c767eee08fec",
56+
"salesChannelId": "55cb094fd1794d489c63975a6b4b5b90",
57+
"shopName": "Shopware's Shop",
58+
"url": "https://shopware.com"
59+
}
60+
```
61+
62+
*After that, at actions we can get data thought FlowStorer.*
63+
```php
64+
$salesChanelId = $flow->getData(MailAware::SALES_CHANNEL_ID));
65+
$customer = $flow->getData(CustomerAware::CUSTOMER_ID));
66+
```
67+
68+
*Or we can use the data when defining the email template.*
69+
```html
70+
<h3>Welcome to {{ shopName }}</h3>
71+
<h1>Visit us at: {{ url }} </h1>
72+
```
73+
74+
**Example pseudocode**
75+
```php
76+
class CustomAppStore extends FlowStorer
77+
{
78+
public function store(FlowEventAware $event, array $stored): array
79+
{
80+
//check if $event is an instance of CustomAppAware
81+
foreach ($event->getCustomAppData() as $key => $data) {
82+
$stored[ScalarValuesAware::STORE_VALUES][$key] = $data;
83+
$stored[$key] = $data;
84+
}
85+
}
86+
87+
public function restore(StorableFlow $storable): void
88+
{
89+
return;
90+
}
91+
}
92+
```
93+
94+
### Flow Events
95+
Events must implement FlowEventAware to be able to available in the flow builder triggers.
96+
97+
**Problems:**
98+
* We do not possess any `FlowEventAware` event instances that app developers can utilize for custom triggers to be dispatched or triggered from an app.
99+
100+
**Solution:**
101+
* We create a new CustomAppEvent class that can be triggered by the App system.
102+
103+
**Example pseudocode**
104+
```php
105+
class CustomAppEvent extends Event implements CustomAppAware, FlowEventAware
106+
{
107+
private string $name;
108+
109+
private array $data;
110+
111+
// __construct()
112+
//getters
113+
}
114+
```
115+
116+
### BusinessEventCollector
117+
BusinessEventCollector collects events that implemented FlowEventAware and output to flow builder.
118+
119+
**Problems:**
120+
* We currently collect events that implemented FlowEventAware. So the collector does not contain the events from the activated app.
121+
122+
**Solution:**
123+
* We will collect all `CustomAppEvent` events from activated apps.
124+
125+
**Example pseudocode**
126+
```php
127+
public function collect(Context $context): BusinessEventCollectorResponse
128+
{
129+
//fetch app event
130+
$this->fetchAppEvents(new BusinessEventCollectorResponse)
131+
}
132+
133+
private function fetchAppEvents(BusinessEventCollectorResponse $result): BusinessEventCollectorResponse
134+
{
135+
//check valid app events from the database
136+
return $this->createCustomAppEvent();
137+
}
138+
139+
private function createCustomAppEvent(): CustomAppEvent
140+
{
141+
// return new CustomAppEvent
142+
}
143+
```
144+
145+
### Trigger app custom events API
146+
We will provide an APIs to trigger CustomAppEvent.
147+
148+
**Problems:**
149+
* Currently, the events are provided and triggered from the core when the user performs specific actions from the storefront or admin, like checkout order or user recovery. 3rd parties can not add custom triggers and trigger them by themself.
150+
151+
**Solution:**
152+
* We will provide an API. The app calls the API to trigger the custom event and needs to provide the event name and the data. The API will create a CustomAppEvent object and dispatch it with the information provided.
153+
154+
**Example pseudocode**
155+
```php
156+
/**
157+
* @Since("6.5.2.0")
158+
*/
159+
#[Route(path: '/api/_action/trigger-event/{eventName}', name: 'api.action.trigger_event', methods: ['POST'])]
160+
public function flowCustomTrigger(string $eventName, Request $request, Context $context): JsonResponse
161+
{
162+
$data = $request->request->all();
163+
164+
$criteria = new Criteria([$data['flowAppEventId']])
165+
$criteria->addFilter(new EqualsFilter('appId', $data['flowId']));
166+
$criteria->addFilter(new EqualsFilter('app.active', 1));
167+
168+
$flowEvent = $flowAppEventRepository->search($criteria);
169+
//return http status code 404 if $flowEvent is empty
170+
171+
$this->eventDispatcher->dispatch(new CustomAppEvent($flowEvent->getName(), $data));
172+
//return http status code 200 and success message
173+
}
174+
175+
```
176+
177+
## Defining an App flow event in Xml
178+
The flow events are configured in a `<appRoot>/src/Resources/flow.xml` file. We can store the following information for a flow event, Also, we can define more than one event in one app:
179+
180+
1. `<name>` - The technical name - is unique and should be prefixed with the app vendor prefix, used when dispatching CustomAppEvent.php.
181+
2. `<aware>` - Use for deciding what flow actions will be allowed to show after the event.
182+
183+
- The list of aware supported following:
184+
- `orderAware`
185+
- `customerAware`
186+
- `mailAware`
187+
- `userAware`
188+
- `salesChannelAware`
189+
- `productAware`
190+
- `customerGroupAware`
191+
192+
- _Example:_
193+
194+
_`<aware>orderAware</aware>`_
195+
196+
_We will have a list of actions related to Order that can be selected at the flow below:_
197+
198+
- action.add.order.tag,
199+
- action.remove.order.tag,
200+
- action.generate.document,
201+
- action.grant.download.access,
202+
- action.set.order.state,
203+
- action.add.order.affiliate.and.campaign.code,
204+
- action.set.order.custom.field,
205+
- action.stop.flow
206+
207+
_`<aware>customerAware</aware>`_
208+
209+
_We will have a list of actions related to Customer that can be selected at the flow below:_
210+
211+
- action.add.customer.tag
212+
- action.remove.customer.tag
213+
- action.change.customer.group
214+
- action.change.customer.status
215+
- action.set.customer.custom.field
216+
- action.add.customer.affiliate.and.campaign.code
217+
- action.stop.flow
218+
219+
A complete XML structure looks like this:
220+
```xml
221+
<flow-extensions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://test-flow.com/flow-1.0.xsd">
222+
<flow-events>
223+
<flow-event>
224+
<name>swag.before.open.the.doors</name>
225+
<aware>customerAware</aware>
226+
<aware>orderAware</aware>
227+
</flow-event>
228+
<flow-event>
229+
...
230+
</flow-event>
231+
</flow-events>
232+
</flow-extensions>
233+
```
234+
235+
## Defining translated
236+
We support defining translation for custom trigger events to show in the trigger tree and the trigger's name in the flow list.
237+
238+
* We will create the snippet file in folder `<appRoot>/src/Resources/app/administration/snippet/`. The structure of the snippet should follow some principles below:
239+
* `sw-flow-custom-event` is a fixed key instance for snippets using at the trigger event.
240+
* `event-tree` is a fixed key. The keys are defined inside this key based on the specified trigger name at `name` in `flow.xml` used to translate in trigger tree.
241+
* `flow-list` is a fixed key, The keys defined inside the key based on the trigger name defined at `name` in `flow.xml` used to translate in the trigger tree.
242+
**Example pseudocode**
243+
```json
244+
{
245+
"sw-flow-custom-event": {
246+
"event-tree": {
247+
"swag": "Swag",
248+
"before": "Before",
249+
"openTheDoors": "Open the doors"
250+
},
251+
"flow-list": {
252+
"swag_before_open_the_doors": "Before open the doors"
253+
}
254+
}
255+
}
256+
```
257+
258+
## Database migration
259+
* We will create a new table `app_flow_event` to save defined data from the `<appRoot>/src/Resources/flow.xml` file.
260+
* The table will have columns like bellow:
261+
* `id` BINARY(16) NOT NULL,
262+
* `app_id` BINARY(16) NOT NULL,
263+
* `name` VARCHAR(255) NOT NULL UNIQUE,
264+
* `aware` JSON NOT NULL,
265+
* `created_at` DATETIME(3) NOT NULL,
266+
* `updated_at` DATETIME(3) NULL,
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
title: Optimize cart cleanup
3+
date: 2023-05-09
4+
area: core
5+
tags: [performance]
6+
---
7+
8+
# Optimize cart cleanup
9+
10+
{% hint style="info" %}
11+
This document represents an architecture decision record (ADR) and has been mirrored from the ADR section in our Shopware 6 repository.
12+
You can find the original version [here](https://github.com/shopware/platform/blob/trunk/adr/2023-05-09-optimise-cart-cleanup.md)
13+
{% endhint %}
14+
15+
## Context
16+
17+
The existing SQL snippet to delete the outdated cart entries doesn't use any database index to narrow down entries that can be deleted.
18+
On high traffic shops this leads to SQL query times larger than 30 seconds to find and remove these database entries.
19+
20+
Running
21+
```
22+
EXPLAIN DELETE FROM cart
23+
WHERE (updated_at IS NULL AND created_at <= '2023-02-01')
24+
OR (updated_at IS NOT NULL AND updated_at <= '2023-02-01') LIMIT 1000;
25+
```
26+
shows that the original sql query doesn't use an index (`possible_keys` = `NULL`)
27+
28+
## Decision
29+
30+
Reorder the query parameters so that the relevant cart entries can be narrowed down by an indexed field.
31+
32+
Testing the new SQL snippet by running
33+
```
34+
EXPLAIN DELETE FROM cart
35+
WHERE created_at <= '2023-02-01'
36+
AND (updated_at IS NULL OR updated_at <= '2023-02-01') LIMIT 1000;
37+
```
38+
shows that the new query uses an index (`possible_keys` = `idx.cart.created_at`).
39+
40+
41+
## Consequences
42+
43+
The logic stays the same but the amount of time needed to find the record drops
44+
dramatically, so the change results in a better performance during cart cleanup.

0 commit comments

Comments
 (0)