Skip to content

Commit 353508d

Browse files
authored
Merge pull request #52 from dneustadt/feature/feature-flags
2 parents 78a3774 + ca78338 commit 353508d

21 files changed

Lines changed: 325 additions & 7 deletions

File tree

CHANGELOG_de-DE.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@
77
* Positive Statusnachricht für den Produktivmodus Check hinzugefügt.
88
* Verbesserungen der Administration-Oberfläche.
99
* Max-Execution-Time Prüfung zum System Status hinzugefügt.
10+
11+
# 0.1.2
12+
13+
* Feature Flag Manager hinzugefügt

CHANGELOG_en-GB.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@
77
* Added positive status message for the production mode check.
88
* Administration view improvements.
99
* Added Max-Execution-Time check to system status.
10+
11+
# 0.1.2
12+
13+
* Added Feature Flag Manager

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ The current feature set consists of:
1616
- Shows the entries of /var/log/*.log files
1717
- Task Logging
1818
- Can be enabled with env `FROSH_TOOLS_TASK_LOGGING=1` in `.env`. This will create a log in `var/log/task_logging-xx.log`
19+
- Feature Flag Manager
20+
- Provides the ability to enable or disable feature flags
1921

2022
## Installation
2123

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "frosh/tools",
3-
"version": "0.1.1",
3+
"version": "0.1.2",
44
"description": "Provides some basic things for managing the Shopware Installation",
55
"type": "shopware-platform-plugin",
66
"license": "MIT",
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Frosh\Tools\Controller;
4+
5+
use Frosh\Tools\Components\Environment\EnvironmentManager;
6+
use Shopware\Core\Framework\Feature;
7+
use Shopware\Core\Framework\Routing\Annotation\RouteScope;
8+
use Shopware\Core\Framework\Routing\Exception\InvalidRequestParameterException;
9+
use Shopware\Core\Framework\Routing\Exception\MissingRequestParameterException;
10+
use Symfony\Component\HttpFoundation\JsonResponse;
11+
use Symfony\Component\HttpFoundation\Request;
12+
use Symfony\Component\HttpFoundation\Response;
13+
use Symfony\Component\HttpKernel\Exception\HttpException;
14+
use Symfony\Component\Routing\Annotation\Route;
15+
16+
/**
17+
* @RouteScope(scopes={"api"})
18+
* @Route(path="/api/_action/frosh-tools")
19+
*/
20+
class FeatureFlagController
21+
{
22+
private string $envPath;
23+
24+
public function __construct(string $projectDir)
25+
{
26+
$this->envPath = $projectDir . '/.env';
27+
}
28+
29+
/**
30+
* @Route(path="/feature-flag/list", methods={"GET"}, name="api.frosh.tools.feature-flag.list")
31+
*/
32+
public function list(): JsonResponse
33+
{
34+
$featureFlags = Feature::getRegisteredFeatures();
35+
36+
foreach (array_keys($featureFlags) as $featureFlag) {
37+
$featureFlags[$featureFlag]['flag'] = $featureFlag;
38+
$featureFlags[$featureFlag]['active'] = Feature::isActive($featureFlag);
39+
}
40+
41+
return new JsonResponse(array_values($featureFlags));
42+
}
43+
44+
/**
45+
* @Route(path="/feature-flag/toggle", methods={"POST"}, name="api.frosh.tools.feature-flag.toggle")
46+
*/
47+
public function toggle(Request $request): Response
48+
{
49+
$featureFlag = $request->get('flag');
50+
51+
if (!file_exists($this->envPath)) {
52+
throw new HttpException(
53+
Response::HTTP_INTERNAL_SERVER_ERROR,
54+
sprintf('File at %s does not exist', $this->envPath)
55+
);
56+
}
57+
58+
if (empty($featureFlag)) {
59+
throw new MissingRequestParameterException('flag');
60+
}
61+
62+
if (!Feature::has($featureFlag)) {
63+
throw new InvalidRequestParameterException('flag');
64+
}
65+
66+
$this->updateEnvFile($featureFlag);
67+
68+
return new Response('', Response::HTTP_NO_CONTENT);
69+
}
70+
71+
private function updateEnvFile(string $featureFlag): void
72+
{
73+
$manager = new EnvironmentManager();
74+
$file = $manager->read($this->envPath);
75+
76+
if (Feature::isActive($featureFlag)) {
77+
$file->set($featureFlag, '0');
78+
} else {
79+
$file->set($featureFlag, '1');
80+
}
81+
82+
$manager->save($this->envPath, $file);
83+
}
84+
}

src/Resources/app/administration/src/api/frosh-tools.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,31 @@ class FroshTools extends ApiService {
121121
return response;
122122
});
123123
}
124+
125+
getFeatureFlags() {
126+
const apiRoute = `${this.getApiBasePath()}/feature-flag/list`;
127+
return this.httpClient.get(
128+
apiRoute,
129+
{
130+
headers: this.getBasicHeaders()
131+
}
132+
).then((response) => {
133+
return ApiService.handleResponse(response);
134+
});
135+
}
136+
137+
toggleFeatureFlag(flag) {
138+
const apiRoute = `${this.getApiBasePath()}/feature-flag/toggle`;
139+
return this.httpClient.post(
140+
apiRoute,
141+
{ flag },
142+
{
143+
headers: this.getBasicHeaders()
144+
}
145+
).then((response) => {
146+
return ApiService.handleResponse(response);
147+
});
148+
}
124149
}
125150

126151
export default FroshTools;
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import template from './template.html.twig';
2+
import './style.scss';
3+
4+
const { Component, Mixin } = Shopware;
5+
6+
Component.register('frosh-tools-tab-feature-flags', {
7+
template,
8+
9+
inject: ['froshToolsService'],
10+
11+
mixins: [
12+
Mixin.getByName('notification')
13+
],
14+
15+
data() {
16+
return {
17+
featureFlags: null,
18+
isLoading: true,
19+
}
20+
},
21+
22+
async created() {
23+
this.createdComponent();
24+
},
25+
26+
computed: {
27+
columns() {
28+
return [
29+
{
30+
property: 'flag',
31+
label: 'frosh-tools.tabs.feature-flags.flag',
32+
rawData: true,
33+
},
34+
{
35+
property: 'active',
36+
label: 'frosh-tools.active',
37+
rawData: true,
38+
},
39+
{
40+
property: 'description',
41+
label: 'frosh-tools.tabs.feature-flags.description',
42+
rawData: true,
43+
},
44+
{
45+
property: 'major',
46+
label: 'frosh-tools.tabs.feature-flags.major',
47+
rawData: true,
48+
},
49+
{
50+
property: 'default',
51+
label: 'frosh-tools.tabs.feature-flags.default',
52+
rawData: true,
53+
},
54+
];
55+
},
56+
},
57+
58+
methods: {
59+
async createdComponent() {
60+
this.isLoading = true;
61+
this.featureFlags = await this.froshToolsService.getFeatureFlags();
62+
this.isLoading = false;
63+
},
64+
65+
async toggle(flag) {
66+
this.isLoading = true;
67+
await this.froshToolsService.toggleFeatureFlag(flag)
68+
.then(async () => {
69+
this.featureFlags = await this.froshToolsService.getFeatureFlags();
70+
window.location.reload();
71+
})
72+
.catch((error) => {
73+
this.createNotificationError({
74+
message: error?.response?.data?.errors[0]?.detail
75+
});
76+
this.isLoading = false;
77+
});
78+
}
79+
}
80+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.frosh-tools-tab-feature-flags__feature-flags-card {
2+
.sw-card__content {
3+
padding: 0;
4+
5+
.sw-label {
6+
text-align: center;
7+
}
8+
}
9+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<sw-card-view>
2+
<sw-card
3+
class="frosh-tools-tab-feature-flags__feature-flags-card"
4+
:title="$tc('frosh-tools.tabs.feature-flags.title')"
5+
:isLoading="isLoading"
6+
:large="true"
7+
>
8+
<sw-data-grid
9+
:showSelection="false"
10+
:dataSource="featureFlags"
11+
:columns="columns"
12+
>
13+
<template #column-active="{ item }">
14+
<sw-icon
15+
v-if="item.active"
16+
color="#37d046"
17+
name="small-default-checkmark-line-medium"
18+
small
19+
/>
20+
<sw-icon
21+
v-else
22+
color="#de294c"
23+
name="small-default-x-line-medium"
24+
small
25+
/>
26+
</template>
27+
28+
<template #column-major="{ item }">
29+
<sw-icon
30+
v-if="item.major"
31+
color="#37d046"
32+
name="small-default-checkmark-line-medium"
33+
small
34+
/>
35+
<sw-icon
36+
v-else
37+
color="#de294c"
38+
name="small-default-x-line-medium"
39+
small
40+
/>
41+
</template>
42+
43+
<template #column-default="{ item }">
44+
<sw-icon
45+
v-if="item.default"
46+
color="#37d046"
47+
name="small-default-checkmark-line-medium"
48+
small
49+
/>
50+
<sw-icon
51+
v-else
52+
color="#de294c"
53+
name="small-default-x-line-medium"
54+
small
55+
/>
56+
</template>
57+
58+
<template #actions="{ item }">
59+
<sw-context-menu-item variant="danger" @click="toggle(item.flag)">
60+
<template v-if="item.active">
61+
{{ $tc('frosh-tools.tabs.feature-flags.deactivate') }}
62+
</template>
63+
<template v-else>
64+
{{ $tc('frosh-tools.tabs.feature-flags.activate') }}
65+
</template>
66+
</sw-context-menu-item>
67+
</template>
68+
</sw-data-grid>
69+
</sw-card>
70+
</sw-card-view>

src/Resources/app/administration/src/module/frosh-tools/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import './component/frosh-tools-tab-scheduled';
55
import './component/frosh-tools-tab-elasticsearch';
66
import './component/frosh-tools-tab-logs';
77
import './component/frosh-tools-tab-files';
8+
import './component/frosh-tools-tab-feature-flags';
89
import './page/index';
910

1011
Shopware.Module.register('frosh-tools', {
@@ -70,6 +71,13 @@ Shopware.Module.register('frosh-tools', {
7071
parentPath: 'frosh.tools.index'
7172
}
7273
},
74+
featureflags: {
75+
component: 'frosh-tools-tab-feature-flags',
76+
path: 'feature-flags',
77+
meta: {
78+
parentPath: 'frosh.tools.index'
79+
}
80+
},
7381
}
7482
},
7583
},

0 commit comments

Comments
 (0)