feat: put the access codes behind a feature flag
This commit is contained in:
1 parent
afa3f17d77
commit
ed5826128e
15 files changed
+248
-1
No files matched your search
@@ -77,3 +77,14 @@ EP_OAUTH2_URL_ACCESS_TOKEN=https://myep.ddev.site/token
|
||||
EP_OAUTH2_URL_USERINFO=https://myep.ddev.site/api/userinfo
|
||||
EP_OAUTH2_SCOPES=email,profile,roles
|
||||
###< app/myep-oauth2 ###
|
||||
|
||||
###> app/features ###
|
||||
# Feature flags, read by config/packages/flagception.yaml. Off here and switched on per
|
||||
# deployment in .env.local, so a feature reaches production when its environment says so rather
|
||||
# than when it is merged.
|
||||
#
|
||||
# Door, lift and barrier codes under /access-codes and their admin table. Off until the codes
|
||||
# have actually been entered: an empty list of codes is worse than no menu entry, because it
|
||||
# reads as "there are none" to somebody standing at the door.
|
||||
FEATURE_ACCESS_CODES=false
|
||||
###< app/features ###
|
||||
@@ -8,3 +8,6 @@ APP_SECRET=44660e8fcd421376ebe0f7a7d55e179c
|
||||
|
||||
# ddev serves the application under this name; anything else is refused.
|
||||
SYMFONY_TRUSTED_HOSTS='^ephub\.ddev\.site$'
|
||||
|
||||
# On for local development, so a fresh checkout sees the feature the branch is about.
|
||||
FEATURE_ACCESS_CODES=true
|
||||
@@ -3,3 +3,7 @@ KERNEL_CLASS='App\Kernel'
|
||||
# Fixed and committed so the test suite is reproducible, and never served: .env.test is excluded
|
||||
# from the deploy rsync and production takes APP_SECRET from its own uncommitted .env.local.
|
||||
APP_SECRET='$ecretf0rt3st'
|
||||
|
||||
# The access-code suite tests the feature, not the gate in front of it, so it runs with the flag
|
||||
# on.
|
||||
FEATURE_ACCESS_CODES=true
|
||||
@@ -10,6 +10,7 @@
|
||||
"doctrine/doctrine-bundle": "^3.3",
|
||||
"doctrine/doctrine-migrations-bundle": "^4.0",
|
||||
"doctrine/orm": "^3.7",
|
||||
"flagception/flagception-bundle": "^6.0",
|
||||
"knplabs/knp-paginator-bundle": "^6.10",
|
||||
"league/oauth2-client": "^2.9",
|
||||
"phpdocumentor/reflection-docblock": "^6.0",
|
||||
|
||||
Generated
+134
-1
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "5651480dee3b63391faefc9646fe4a95",
|
||||
"content-hash": "2aef6c416eb2231831a8f94f753f2aa3",
|
||||
"packages": [
|
||||
{
|
||||
"name": "composer/semver",
|
||||
@@ -1269,6 +1269,139 @@
|
||||
],
|
||||
"time": "2025-03-06T22:45:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "flagception/flagception",
|
||||
"version": "2.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/playox/flagception-sdk.git",
|
||||
"reference": "6934e73ea1ab43611a43bf85922349a83561177b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/playox/flagception-sdk/zipball/6934e73ea1ab43611a43bf85922349a83561177b",
|
||||
"reference": "6934e73ea1ab43611a43bf85922349a83561177b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2||^8.0",
|
||||
"psr/cache": "^1.0||^2.0||^3.0",
|
||||
"symfony/expression-language": ">=2.7"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-coveralls/php-coveralls": "^2.4.3",
|
||||
"phpunit/phpunit": "*",
|
||||
"squizlabs/php_codesniffer": "^3.5"
|
||||
},
|
||||
"suggest": {
|
||||
"flagception/database-activator": "Fetch feature flags from database."
|
||||
},
|
||||
"type": "lib",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Flagception\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michel Chowanski",
|
||||
"email": "[email protected]"
|
||||
}
|
||||
],
|
||||
"description": "Feature toggle on steroids.",
|
||||
"keywords": [
|
||||
"feature",
|
||||
"feature-flags",
|
||||
"feature-toggle",
|
||||
"flagception",
|
||||
"flags",
|
||||
"rollout",
|
||||
"testing",
|
||||
"toggle"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/playox/flagception-sdk/issues",
|
||||
"source": "https://github.com/playox/flagception-sdk/tree/2.0.0"
|
||||
},
|
||||
"time": "2025-02-26T16:11:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "flagception/flagception-bundle",
|
||||
"version": "6.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/playox/flagception-bundle.git",
|
||||
"reference": "48f8fd0c8386aa3f4e66ba7882e24dc3ffcf8997"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/playox/flagception-bundle/zipball/48f8fd0c8386aa3f4e66ba7882e24dc3ffcf8997",
|
||||
"reference": "48f8fd0c8386aa3f4e66ba7882e24dc3ffcf8997",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"flagception/flagception": "^2.0",
|
||||
"php": "^8.0",
|
||||
"symfony/config": "^4.4 | ^5.0 | ^6.0 | ^7.0 | ^8.0",
|
||||
"symfony/dependency-injection": "^4.4 | ^5.0 | ^6.0 | ^7.0 | ^8.0",
|
||||
"symfony/http-kernel": "^4.4 | ^5.0 | ^6.0 | ^7.0 | ^8.0",
|
||||
"symfony/yaml": "^4.4 | ^5.0 | ^6.0 | ^7.0 | ^8.0",
|
||||
"twig/twig": "^1.18|^2.0|^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"flagception/database-activator": "^2.0",
|
||||
"php-coveralls/php-coveralls": "^2.0",
|
||||
"squizlabs/php_codesniffer": "^3.3.1",
|
||||
"symfony/phpunit-bridge": "^5.0 | ^6.0 | ^7.0 | ^8.0",
|
||||
"symfony/twig-bridge": "^4.4 | ^5.0 | ^6.0 | ^7.0 | ^8.0"
|
||||
},
|
||||
"suggest": {
|
||||
"flagception/database-activator": "Fetch feature flags from a database."
|
||||
},
|
||||
"type": "symfony-bundle",
|
||||
"extra": {
|
||||
"thanks": {
|
||||
"url": "https://github.com/playox/flagception-sdk",
|
||||
"name": "flagception/flagception"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Flagception\\Bundle\\FlagceptionBundle\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michel Chowanski",
|
||||
"email": "[email protected]"
|
||||
}
|
||||
],
|
||||
"description": "Feature toggle bundle on steroids.",
|
||||
"keywords": [
|
||||
"bundle",
|
||||
"feature",
|
||||
"feature-flags",
|
||||
"feature-toggle",
|
||||
"flagception",
|
||||
"flags",
|
||||
"rollout",
|
||||
"symfony",
|
||||
"toggle"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/playox/flagception-bundle/issues",
|
||||
"source": "https://github.com/playox/flagception-bundle/tree/6.1.1"
|
||||
},
|
||||
"time": "2026-06-01T10:02:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/guzzle",
|
||||
"version": "7.15.5",
|
||||
|
||||
@@ -14,4 +14,5 @@ return [
|
||||
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
||||
Symfony\UX\Icons\UXIconsBundle::class => ['all' => true],
|
||||
Knp\Bundle\PaginatorBundle\KnpPaginatorBundle::class => ['all' => true],
|
||||
Flagception\Bundle\FlagceptionBundle\FlagceptionBundle::class => ['all' => true],
|
||||
];
|
||||
@@ -0,0 +1,10 @@
|
||||
# See full configuration at https://github.com/bestit/flagception-bundle
|
||||
#
|
||||
# Features are off unless an environment says otherwise: a deployment that knows nothing about a
|
||||
# flag gets the state the feature had before it existed. Switching one on is an .env change, not
|
||||
# a release.
|
||||
flagception:
|
||||
features:
|
||||
access_codes:
|
||||
default: false
|
||||
env: FEATURE_ACCESS_CODES
|
||||
@@ -1513,6 +1513,61 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
||||
* convert_exception?: bool|Param, // Default: false
|
||||
* remove_first_page_param?: bool|Param, // Default: false
|
||||
* }
|
||||
* @psalm-type FlagceptionConfig = array{
|
||||
* features?: array<string, array{ // Default: []
|
||||
* default?: scalar|Param|null, // Default: false
|
||||
* env?: scalar|Param|null, // Default: false
|
||||
* cookie?: bool|Param, // Default: true
|
||||
* constraint?: scalar|Param|null, // Default: false
|
||||
* }>,
|
||||
* routing_metadata?: array{
|
||||
* enable?: bool|Param, // Default: true
|
||||
* },
|
||||
* activators?: array{
|
||||
* array?: array{
|
||||
* enable?: bool|Param, // Default: true
|
||||
* priority?: int|Param, // Default: 255
|
||||
* },
|
||||
* constraint?: array{
|
||||
* enable?: bool|Param, // Default: true
|
||||
* priority?: int|Param, // Default: 210
|
||||
* },
|
||||
* environment?: array{
|
||||
* enable?: bool|Param, // Default: true
|
||||
* priority?: int|Param, // Default: 230
|
||||
* },
|
||||
* cookie?: array{
|
||||
* enable?: bool|Param, // Default: false
|
||||
* priority?: int|Param, // Default: 200
|
||||
* name?: scalar|Param|null, // Default: "flagception"
|
||||
* separator?: scalar|Param|null, // Default: ","
|
||||
* mode?: "whitelist"|"blacklist"|Param, // Default: "whitelist"
|
||||
* },
|
||||
* database?: array{
|
||||
* enable?: bool|Param, // Default: false
|
||||
* priority?: int|Param, // Default: 220
|
||||
* url?: scalar|Param|null, // Connection string for the database
|
||||
* dbal?: scalar|Param|null, // Service with dbal instance
|
||||
* credentials?: array{
|
||||
* dbname?: scalar|Param|null,
|
||||
* user?: scalar|Param|null,
|
||||
* password?: scalar|Param|null,
|
||||
* host?: scalar|Param|null,
|
||||
* driver?: scalar|Param|null,
|
||||
* },
|
||||
* options?: array{
|
||||
* db_table?: scalar|Param|null, // Default: "flagception_features"
|
||||
* db_column_feature?: scalar|Param|null, // Default: "feature"
|
||||
* db_column_state?: scalar|Param|null, // Default: "state"
|
||||
* },
|
||||
* cache?: array{
|
||||
* enable?: bool|Param, // Default: false
|
||||
* pool?: scalar|Param|null, // Default: "cache.app"
|
||||
* lifetime?: int|Param, // Default: 3600
|
||||
* },
|
||||
* },
|
||||
* },
|
||||
* }
|
||||
* @psalm-type ConfigType = array{
|
||||
* imports?: ImportsConfig,
|
||||
* parameters?: ParametersConfig,
|
||||
@@ -1527,6 +1582,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
||||
* monolog?: MonologConfig,
|
||||
* ux_icons?: UxIconsConfig,
|
||||
* knp_paginator?: KnpPaginatorConfig,
|
||||
* flagception?: FlagceptionConfig,
|
||||
* "when@dev"?: array{
|
||||
* imports?: ImportsConfig,
|
||||
* parameters?: ParametersConfig,
|
||||
@@ -1544,6 +1600,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
||||
* maker?: MakerConfig,
|
||||
* ux_icons?: UxIconsConfig,
|
||||
* knp_paginator?: KnpPaginatorConfig,
|
||||
* flagception?: FlagceptionConfig,
|
||||
* },
|
||||
* "when@prod"?: array{
|
||||
* imports?: ImportsConfig,
|
||||
@@ -1559,6 +1616,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
||||
* monolog?: MonologConfig,
|
||||
* ux_icons?: UxIconsConfig,
|
||||
* knp_paginator?: KnpPaginatorConfig,
|
||||
* flagception?: FlagceptionConfig,
|
||||
* },
|
||||
* "when@test"?: array{
|
||||
* imports?: ImportsConfig,
|
||||
@@ -1575,6 +1633,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
||||
* monolog?: MonologConfig,
|
||||
* ux_icons?: UxIconsConfig,
|
||||
* knp_paginator?: KnpPaginatorConfig,
|
||||
* flagception?: FlagceptionConfig,
|
||||
* },
|
||||
* ...<string, ExtensionType|array{ // extra keys must follow the when@%env% pattern or match an extension alias
|
||||
* imports?: ImportsConfig,
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace App\Controller\AccessCode;
|
||||
use App\Controller\Traits\CurrentUserTrait;
|
||||
use App\Entity\AccessCode;
|
||||
use App\Repository\AccessCodeRepository;
|
||||
use Flagception\Bundle\FlagceptionBundle\Attribute\Feature;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
@@ -23,6 +24,7 @@ use Symfony\Component\Routing\Attribute\Route;
|
||||
* has been replaced is gone the day its successor takes over. Both would only be one more number
|
||||
* on the screen for somebody to mistype.
|
||||
*/
|
||||
#[Feature('access_codes')]
|
||||
final class IndexController extends AbstractController
|
||||
{
|
||||
use CurrentUserTrait;
|
||||
|
||||
@@ -11,12 +11,14 @@ use App\Htmx\HxRedirectResponse;
|
||||
use App\Security\Role;
|
||||
use App\Service\AuditLogger;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Flagception\Bundle\FlagceptionBundle\Attribute\Feature;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
#[Feature('access_codes')]
|
||||
#[IsGranted(Role::ADMIN)]
|
||||
final class CreateController extends AbstractController
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@ use App\Htmx\HxRedirectResponse;
|
||||
use App\Security\Role;
|
||||
use App\Service\AuditLogger;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Flagception\Bundle\FlagceptionBundle\Attribute\Feature;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
@@ -21,6 +22,7 @@ use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
* confirmation, which is there because a deleted code is not recoverable from the screen that
|
||||
* deleted it.
|
||||
*/
|
||||
#[Feature('access_codes')]
|
||||
#[IsGranted(Role::ADMIN)]
|
||||
final class DeleteController extends AbstractController
|
||||
{
|
||||
|
||||
@@ -10,12 +10,14 @@ use App\Htmx\HxRedirectResponse;
|
||||
use App\Security\Role;
|
||||
use App\Service\AuditLogger;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Flagception\Bundle\FlagceptionBundle\Attribute\Feature;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
#[Feature('access_codes')]
|
||||
#[IsGranted(Role::ADMIN)]
|
||||
final class EditController extends AbstractController
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@ use App\Controller\Traits\CurrentUserTrait;
|
||||
use App\Entity\AccessCode;
|
||||
use App\Repository\AccessCodeRepository;
|
||||
use App\Security\Role;
|
||||
use Flagception\Bundle\FlagceptionBundle\Attribute\Feature;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
@@ -20,6 +21,7 @@ use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
* Which is which is a question about today rather than a stored flag, and it is answered here
|
||||
* because it is a question about the whole list: a template cannot look back over its own rows.
|
||||
*/
|
||||
#[Feature('access_codes')]
|
||||
#[IsGranted(Role::ADMIN)]
|
||||
final class IndexController extends AbstractController
|
||||
{
|
||||
|
||||
@@ -35,6 +35,18 @@
|
||||
"migrations/.gitignore"
|
||||
]
|
||||
},
|
||||
"flagception/flagception-bundle": {
|
||||
"version": "6.1",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes-contrib",
|
||||
"branch": "main",
|
||||
"version": "3.0",
|
||||
"ref": "9f045b9e854039c20e9ecf54aa8d9868c1b841cd"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/flagception.yaml"
|
||||
]
|
||||
},
|
||||
"knplabs/knp-paginator-bundle": {
|
||||
"version": "v6.11.0"
|
||||
},
|
||||
|
||||
@@ -49,6 +49,8 @@
|
||||
<p class="text-sm text-gray-500 pb-4">Alle Buchungen verwalten.</p>
|
||||
</a>
|
||||
|
||||
{# The route 404s while the feature is off, so the card has to go with it. #}
|
||||
{% if feature('access_codes') %}
|
||||
<a href="{{ path('app_access_code') }}"
|
||||
class="group flex flex-col rounded-md border border-gray-200 p-6 hover:border-primary hover:bg-gray-50">
|
||||
<div class="flex items-center gap-3 pb-2">
|
||||
@@ -57,6 +59,7 @@
|
||||
</div>
|
||||
<p class="text-sm text-gray-500 pb-4">Codes für Türen, Aufzug und Schranke.</p>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{#
|
||||
The header is deliberately not navigation, so the dashboard is the only place an
|
||||
|
||||
Reference in new issue
Block a user