Skip to content

Commit

Permalink
feature toggle bundle
Browse files Browse the repository at this point in the history
  • Loading branch information
JoshuaEstes committed Oct 23, 2024
1 parent b0c2cd7 commit 6cfea5f
Show file tree
Hide file tree
Showing 13 changed files with 344 additions and 4 deletions.
19 changes: 16 additions & 3 deletions bard.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
"packages": [
{
"path": "src/SonsOfPHP/Bard",
"repository": "git@github.com:SonsOfPHP/bard.git"
"repository": "git@github.com:SonsOfPHP/bard.git",
"config": {
"merge": false
}
},
{
"path": "src/SonsOfPHP/Component/Assert",
Expand Down Expand Up @@ -47,7 +50,10 @@
},
{
"path": "src/SonsOfPHP/Bundle/Cqrs",
"repository": "git@github.com:SonsOfPHP/cqrs-bundle.git"
"repository": "git@github.com:SonsOfPHP/cqrs-bundle.git",
"config": {
"merge": false
}
},
{
"path": "src/SonsOfPHP/Bridge/Symfony/Cqrs",
Expand Down Expand Up @@ -188,6 +194,13 @@
{
"path": "src/SonsOfPHP/Contract/Version",
"repository": "git@github.com:SonsOfPHP/version-contract.git"
},
{
"path": "src/SonsOfPHP/Bundle/FeatureToggleBundle",
"repository": "git@github.com:SonsOfPHP/feature-toggle-bundle.git",
"config": {
"merge": false
}
}
]
}
}
19 changes: 19 additions & 0 deletions src/SonsOfPHP/Bundle/FeatureToggleBundle/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Copyright 2022 to Present Joshua Estes

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
23 changes: 23 additions & 0 deletions src/SonsOfPHP/Bundle/FeatureToggleBundle/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<div align="center">
<img src="https://raw.githubusercontent.com/SonsOfPHP/.github/main/assets/top-rocker.png" />
</div>
<br/>
<div align="center">
<a href="https://codecov.io/github/SonsOfPHP/sonsofphp"><img src="https://codecov.io/github/SonsOfPHP/sonsofphp/graph/badge.svg?token=VZ2FVOUKUW" /></a>
<img src="https://img.shields.io/packagist/l/sonsofphp/sonsofphp" />
<img src="https://img.shields.io/packagist/v/sonsofphp/sonsofphp" />
</div>

# Sons of PHP

Sons of PHP is builds reusable components and tools using PHP.

* Documentation can be found at [docs.SonsOfPHP.com][docs]
* Please [Report Issues][issues] and send [Pull Requests][pull-requests] in the [Mother Repository][mother-repo]
* You can get more help by posting questions in the [GitHub Discussions][discussions]

[mother-repo]: <https://github.com/SonsOfPHP/sonsofphp> "Sons of PHP Mother Repository"
[discussions]: https://github.com/orgs/SonsOfPHP/discussions
[issues]: https://github.com/SonsOfPHP/sonsofphp/issues
[pull-requests]: https://github.com/SonsOfPHP/sonsofphp/pulls
[docs]: https://docs.sonsofphp.com
56 changes: 56 additions & 0 deletions src/SonsOfPHP/Bundle/FeatureToggleBundle/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"name": "sonsofphp/feature-toggle-bundle",
"type": "symfony-bundle",
"description": "Sons of PHP | Feature Toggle Bundle",
"keywords": [
"sonsofphp",
"sons of php",
"feature",
"toggle",
"feature toggle",
"symfony"
],
"homepage": "https://github.com/SonsOfPHP/feature-toggle-bundle",
"license": "MIT",
"authors": [
{
"name": "Joshua Estes",
"email": "joshua@sonsofphp.com"
}
],
"support": {
"issues": "https://github.com/SonsOfPHP/sonsofphp/issues",
"forum": "https://github.com/orgs/SonsOfPHP/discussions",
"docs": "https://docs.sonsofphp.com/symfony-bundles/feature-toggle"
},
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/JoshuaEstes"
},
{
"type": "tidelift",
"url": "https://tidelift.com/subscription/pkg/packagist-sonsofphp-sonsofphp"
}
],
"require": {
"php": ">=8.2",
"sonsofphp/feature-toggle": "^0.3@dev"
},
"autoload": {
"psr-4": {
"SonsOfPHP\\Bundle\\FeatureToggleBundle\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"SonsOfPHP\\Bundle\\FeatureToggleBundle\\Tests\\": "tests/"
}
},
"extra": {
"sort-packages": true,
"branch-alias": {
"dev-main": "0.3.x-dev"
}
}
}
40 changes: 40 additions & 0 deletions src/SonsOfPHP/Bundle/FeatureToggleBundle/config/services.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
services:
SonsOfPHP\Bundle\FeatureToggleBundle\Command\DebugCommand:
arguments: ['@sons_of_php.feature_toggle.provider']
tags:
- { name: 'console.command' }

SonsOfPHP\Bundle\FeatureToggleBundle\Twig\Extension\FeatureToggleExtension:
tags:
- { name: 'twig.extension' }

SonsOfPHP\Bundle\FeatureToggleBundle\Twig\Runtime\FeatureToggleExtensionRuntime:
arguments: ['@sons_of_php.feature_toggle.provider']
tags:
- { name: 'twig.runtime' }

SonsOfPHP\Component\FeatureToggle\Toggle\AlwaysDisabledToggle:
tags:
- { name: 'sons_of_php.feature_toggle.toggle' }

sons_of_php.feature_toggle.toggle.disabled:
alias: SonsOfPHP\Component\FeatureToggle\Toggle\AlwaysDisabledToggle
public: true

SonsOfPHP\Component\FeatureToggle\Toggle\AlwaysEnabledToggle:
tags:
- { name: 'sons_of_php.feature_toggle.toggle' }

sons_of_php.feature_toggle.toggle.enabled:
alias: SonsOfPHP\Component\FeatureToggle\Toggle\AlwaysEnabledToggle
public: true

# The provider contains all the different features and their toggles Default
# provider should be a chain provider so that users can define additional
# providers
SonsOfPHP\Contract\FeatureToggle\FeatureToggleProviderInterface:
class: SonsOfPHP\Component\FeatureToggle\Provider\InMemoryFeatureToggleProvider

sons_of_php.feature_toggle.provider:
alias: SonsOfPHP\Contract\FeatureToggle\FeatureToggleProviderInterface
public: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

declare(strict_types=1);

namespace SonsOfPHP\Bundle\FeatureToggleBundle\Attribute;

final class AsFeature {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace SonsOfPHP\Bundle\FeatureToggleBundle\Command;

use SonsOfPHP\Component\FeatureToggle\Feature;
use SonsOfPHP\Contract\FeatureToggle\FeatureToggleProviderInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

class DebugCommand extends Command
{
public function __construct(

Check warning on line 16 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Command/DebugCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Command/DebugCommand.php#L16

Added line #L16 was not covered by tests
private readonly FeatureToggleProviderInterface $provider,
) {
parent::__construct();

Check warning on line 19 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Command/DebugCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Command/DebugCommand.php#L19

Added line #L19 was not covered by tests
}

protected function configure(): void

Check warning on line 22 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Command/DebugCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Command/DebugCommand.php#L22

Added line #L22 was not covered by tests
{
$this
->setName('debug:features')
->setDescription('Debug feature toggles')
;

Check warning on line 27 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Command/DebugCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Command/DebugCommand.php#L24-L27

Added lines #L24 - L27 were not covered by tests
}

protected function execute(InputInterface $input, OutputInterface $output): int

Check warning on line 30 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Command/DebugCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Command/DebugCommand.php#L30

Added line #L30 was not covered by tests
{
$symfonyStyle = new SymfonyStyle($input, $output);

Check warning on line 32 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Command/DebugCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Command/DebugCommand.php#L32

Added line #L32 was not covered by tests

$header = ['Key', 'Toggle'];
$rows = [];
$toggle = new \ReflectionProperty(Feature::class, 'toggle');
foreach ($this->provider->all() as $key => $feature) {
$rows[] = [$key, $toggle->getValue($feature)::class];

Check warning on line 38 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Command/DebugCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Command/DebugCommand.php#L34-L38

Added lines #L34 - L38 were not covered by tests
}

$symfonyStyle->table($header, $rows);

Check warning on line 41 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Command/DebugCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Command/DebugCommand.php#L41

Added line #L41 was not covered by tests

return Command::SUCCESS;

Check warning on line 43 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Command/DebugCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Command/DebugCommand.php#L43

Added line #L43 was not covered by tests
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace SonsOfPHP\Bundle\FeatureToggleBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

final class FeaturePass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void

Check warning on line 13 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/DependencyInjection/Compiler/FeaturePass.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/DependencyInjection/Compiler/FeaturePass.php#L13

Added line #L13 was not covered by tests
{
if (!$container->has('sons_of_php.feature_toggle.provider')) {
return;

Check warning on line 16 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/DependencyInjection/Compiler/FeaturePass.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/DependencyInjection/Compiler/FeaturePass.php#L15-L16

Added lines #L15 - L16 were not covered by tests
}

$provider = $container->findDefinition('sons_of_php.feature_toggle.provider');
$features = $container->findTaggedServiceIds('sons_of_php.feature_toggle.feature');

Check warning on line 20 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/DependencyInjection/Compiler/FeaturePass.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/DependencyInjection/Compiler/FeaturePass.php#L19-L20

Added lines #L19 - L20 were not covered by tests

foreach (array_keys($features) as $id) {
$provider->addMethodCall('add', [new Reference($id)]);

Check warning on line 23 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/DependencyInjection/Compiler/FeaturePass.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/DependencyInjection/Compiler/FeaturePass.php#L22-L23

Added lines #L22 - L23 were not covered by tests
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

declare(strict_types=1);

namespace SonsOfPHP\Bundle\FeatureToggleBundle;

use SonsOfPHP\Bundle\FeatureToggleBundle\Attribute\AsFeature;
use SonsOfPHP\Bundle\FeatureToggleBundle\DependencyInjection\Compiler\FeaturePass;
use SonsOfPHP\Component\FeatureToggle\Feature;
use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;

class SonsOfPHPFeatureToggleBundle extends AbstractBundle
{
public function configure(DefinitionConfigurator $definition): void

Check warning on line 18 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/SonsOfPHPFeatureToggleBundle.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/SonsOfPHPFeatureToggleBundle.php#L18

Added line #L18 was not covered by tests
{
/**
* @todo provider: sons_of_php.feature_toggle.provider
* features:
* key:
* toggle: sons_of_php.feature_toggle.toggle.enabled
* another_key:
* toggle: sons_of_php.feature_toggle.toggle.disabled
*/
$definition->rootNode()->children()

Check failure on line 28 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/SonsOfPHPFeatureToggleBundle.php

View workflow job for this annotation

GitHub Actions / Psalm (8.2)

UndefinedMethod

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/SonsOfPHPFeatureToggleBundle.php:28:34: UndefinedMethod: Method Symfony\Component\Config\Definition\Builder\NodeDefinition::children does not exist (see https://psalm.dev/022)
// @todo
//->scalarNode('provider')
// ->defaultValue('sons_of_php.feature_toggle.provider')
//->end() // provider
->arrayNode('features')
->info('Features contains a list of features by "key" and the toggle the feature uses')
->useAttributeAsKey('key')
->arrayPrototype()
->children()
->scalarNode('toggle')
->info('Can be "enabled", "disabled", or a service')
->isRequired()
->cannotBeEmpty()
->defaultValue('sons_of_php.feature_toggle.toggle.enabled')
->end()
->end()
->end()
->end() // features
->end();

Check warning on line 47 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/SonsOfPHPFeatureToggleBundle.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/SonsOfPHPFeatureToggleBundle.php#L28-L47

Added lines #L28 - L47 were not covered by tests
}

public function build(ContainerBuilder $container): void

Check warning on line 50 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/SonsOfPHPFeatureToggleBundle.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/SonsOfPHPFeatureToggleBundle.php#L50

Added line #L50 was not covered by tests
{
$container->registerForAutoconfiguration(AsFeature::class)->addTag('sons_of_php.feature_toggle.feature');
$container->addCompilerPass(new FeaturePass());

Check warning on line 53 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/SonsOfPHPFeatureToggleBundle.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/SonsOfPHPFeatureToggleBundle.php#L52-L53

Added lines #L52 - L53 were not covered by tests
}

public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void

Check warning on line 56 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/SonsOfPHPFeatureToggleBundle.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/SonsOfPHPFeatureToggleBundle.php#L56

Added line #L56 was not covered by tests
{
$container->import('../config/services.yaml');

Check warning on line 58 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/SonsOfPHPFeatureToggleBundle.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/SonsOfPHPFeatureToggleBundle.php#L58

Added line #L58 was not covered by tests

foreach ($config['features'] as $key => $value) {
$value['toggle'] = match ($value['toggle']) {
'enabled' => 'sons_of_php.feature_toggle.toggle.enabled',
'disabled' => 'sons_of_php.feature_toggle.toggle.disabled',
default => $value['toggle'],
};

Check warning on line 65 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/SonsOfPHPFeatureToggleBundle.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/SonsOfPHPFeatureToggleBundle.php#L60-L65

Added lines #L60 - L65 were not covered by tests

$feature = $container->services()->set('sons_of_php.feature_toggle.feature.' . $key, Feature::class);
$feature->arg(0, $key);
$feature->arg(1, new Reference($value['toggle']));
$feature->tag('sons_of_php.feature_toggle.feature');

Check warning on line 70 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/SonsOfPHPFeatureToggleBundle.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/SonsOfPHPFeatureToggleBundle.php#L67-L70

Added lines #L67 - L70 were not covered by tests
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace SonsOfPHP\Bundle\FeatureToggleBundle\Twig\Extension;

use SonsOfPHP\Bundle\FeatureToggleBundle\Twig\Runtime\FeatureToggleExtensionRuntime;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

final class FeatureToggleExtension extends AbstractExtension
{
public function getFunctions(): array

Check warning on line 13 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Twig/Extension/FeatureToggleExtension.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Twig/Extension/FeatureToggleExtension.php#L13

Added line #L13 was not covered by tests
{
return [
new TwigFunction('is_feature_enabled', [FeatureToggleExtensionRuntime::class, 'isEnabled']),
];

Check warning on line 17 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Twig/Extension/FeatureToggleExtension.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Twig/Extension/FeatureToggleExtension.php#L15-L17

Added lines #L15 - L17 were not covered by tests
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace SonsOfPHP\Bundle\FeatureToggleBundle\Twig\Runtime;

use SonsOfPHP\Contract\FeatureToggle\FeatureToggleProviderInterface;
use Twig\Extension\RuntimeExtensionInterface;

final readonly class FeatureToggleExtensionRuntime implements RuntimeExtensionInterface
{
public function __construct(

Check warning on line 12 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Twig/Runtime/FeatureToggleExtensionRuntime.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Twig/Runtime/FeatureToggleExtensionRuntime.php#L12

Added line #L12 was not covered by tests
private FeatureToggleProviderInterface $provider,
) {}

Check warning on line 14 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Twig/Runtime/FeatureToggleExtensionRuntime.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Twig/Runtime/FeatureToggleExtensionRuntime.php#L14

Added line #L14 was not covered by tests

public function isEnabled(string $key): bool

Check warning on line 16 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Twig/Runtime/FeatureToggleExtensionRuntime.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Twig/Runtime/FeatureToggleExtensionRuntime.php#L16

Added line #L16 was not covered by tests
{
return $this->provider->get($key)->isEnabled();

Check warning on line 18 in src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Twig/Runtime/FeatureToggleExtensionRuntime.php

View check run for this annotation

Codecov / codecov/patch

src/SonsOfPHP/Bundle/FeatureToggleBundle/src/Twig/Runtime/FeatureToggleExtensionRuntime.php#L18

Added line #L18 was not covered by tests
}
}
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ public function get(string $key): FeatureInterface;
* When the $key is invalid. MUST support keys consisting of the
* characters A-Z, a-z, 0-9, _, and . in any order in UTF-8 encoding and
* a length of up to 64 characters
*
*/
public function has(string $key): bool;

Expand Down

0 comments on commit 6cfea5f

Please sign in to comment.