Skip to content

Commit e83fdaa

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

14 files changed

+1482
-0
lines changed

SUMMARY.md

Lines changed: 12 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)
@@ -478,7 +479,18 @@
478479
* [Admin text editor evaluation](resources/references/adr/2023-03-27-admin-text-editor-evaluation.md)
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)
482+
* [New language inheritance mechanism for opensearch](resources/references/adr/2023-04-11-new-language-inheritance-mechanism-for-opensearch.md)
481483
* [Jest test files should be javascript only](resources/references/adr/2023-04-14-jest-test-files-should-be-javascript-only.md)
484+
* [Optimise cart cleanup](resources/references/adr/2023-05-09-optimise-cart-cleanup.md)
485+
* [Experimental features](resources/references/adr/2023-05-10-experimental-features.md)
486+
* [Stock API](resources/references/adr/2023-05-15-stock-api.md)
487+
* [Php enums](resources/references/adr/2023-05-16-php-enums.md)
488+
* [Symfony dependency management](resources/references/adr/2023-05-16-symfony-dependency-management.md)
489+
* [Switch to uuidv7](resources/references/adr/2023-05-22-switch-to-uuidv7.md)
490+
* [Exception log levels](resources/references/adr/2023-05-25-exception-log-levels.md)
491+
* [Store API to app server](resources/references/adr/2023-06-27-store-api-to-app-server.md)
492+
* [Default handle for non specified salutations](resources/references/adr/2023-06-28-default-handle-for-non-specified-salutations.md)
493+
* [Flow builder preview](resources/references/adr/2023-07-13-flow-builder-preview.md)
482494
* [YYYY MM DD template](resources/references/adr/YYYY-MM-DD-template.md)
483495

484496
* [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,

0 commit comments

Comments
 (0)