Skip to content

Commit

Permalink
[Feature] Introduces the ability to define groups for rollout (#14)
Browse files Browse the repository at this point in the history
* Introduces the ability to define groups for rollout
  • Loading branch information
Jaspaul authored Aug 24, 2018
1 parent 48fc04a commit cbcb5e1
Show file tree
Hide file tree
Showing 12 changed files with 380 additions and 24 deletions.
64 changes: 63 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ ROLLOUT_TABLE=rollout

### Implementing Interfaces

Your rollout users must implement the `\Jaspaul\LaravelRollout\Helpers\User` interface. Often this will be your main user object:
##### User
Your rollout users must implement the `\Jaspaul\LaravelRollout\Contracts\User` interface. Often this will be your main user object:

```php
<?php
Expand All @@ -76,8 +77,63 @@ class User implements Contract
}
```

#### Group
Your rollout groups must implement the `\Jaspaul\LaravelRollout\Contracts\Group` interface.

```php
<?php

use Jaspaul\LaravelRollout\Contracts\Group;

class BetaUsersGroup implements Group
{
/**
* The name of the group.
*
* @return string
*/
public function getName(): string
{
return 'beta-users';
}

/**
* Defines the rule membership in the group.
*
* @return boolean
*/
public function hasMember($user = null): bool
{
if (!is_null($user)) {
return $user->hasOptedIntoBeta();
}

return false;
}
}
```

and you should update your local `laravel-rollout.php` configuration to include the group in the groups array:

`laravel-rollout.php`
```php
return [
...
'groups' => [
BetaUsersGroup::class
],
...
]
```

## Commands

### Add Group

`php artisan rollout:add-group {feature} {group}`

Swap `{feature}` with the name of the feature, and `{group}` with the name you defined in the group class.

### Add User

`php artisan rollout:add-user {feature} {user}`
Expand Down Expand Up @@ -120,6 +176,12 @@ Swap `{feature}` with the name of the feature you'd like to rollout to 100% of y

Swap `{feature}` with the name of the feature you'd like to rollout, and `{percentage}` with the percentage of users to rollout the feature to.

### Remove Group

`php artisan rollout:remove-group {feature} {group}`

Swap `{feature}` with the name of the feature, and `{group}` with the name you defined in the group class.

### Remove User

`php artisan rollout:remove-user {feature} {user}`
Expand Down
3 changes: 2 additions & 1 deletion resources/config/laravel-rollout.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@

return [
'storage' => env('ROLLOUT_STORAGE', 'cache'),
'table' => env('ROLLOUT_TABLE', 'rollout')
'table' => env('ROLLOUT_TABLE', 'rollout'),
'groups' => [],
];
36 changes: 36 additions & 0 deletions src/Console/AddGroupCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace Jaspaul\LaravelRollout\Console;

class AddGroupCommand extends RolloutCommand
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'rollout:add-group {feature} {group}';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Enables the provided feature for the group.';

/**
* Adds the provided group to the requested feature. Note this will create
* the feature as a side effect.
*
* @return void
*/
public function handle()
{
$name = $this->argument('feature');
$group = $this->argument('group');

$this->rollout->activateGroup($name, $group);

$this->renderFeatureAsTable($name);
}
}
38 changes: 38 additions & 0 deletions src/Console/RemoveGroupCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace Jaspaul\LaravelRollout\Console;

use Jaspaul\LaravelRollout\Helpers\User;

class RemoveGroupCommand extends RolloutCommand
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'rollout:remove-group {feature} {group}';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Removes the provided group from the feature.';

/**
* Removes the provided user from the feature. Note this will create
* the feature as a side effect.
*
* @return void
*/
public function handle()
{
$name = $this->argument('feature');
$group = $this->argument('group');

$this->rollout->deactivateGroup($name, $group);

$this->renderFeatureAsTable($name);
}
}
24 changes: 24 additions & 0 deletions src/Contracts/Group.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Jaspaul\LaravelRollout\Contracts;

use Opensoft\Rollout\RolloutUserInterface;

interface Group
{
/**
* The name of the group.
*
* @return string
*/
public function getName(): string;

/**
* Checks if the given user is a member of this group.
*
* @param mixed $user
*
* @return boolean
*/
public function hasMember($user = null): bool;
}
13 changes: 12 additions & 1 deletion src/FeaturePresenter.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,16 @@ public function getUsers() : string
return implode(', ', $this->feature->getUsers());
}

/**
* Returns a list of the groups that have this feature enabled.
*
* @return string
*/
public function getGroups() : string
{
return implode(', ', $this->feature->getGroups());
}

/**
* Returns an array representation of the feature presenter.
*
Expand All @@ -120,7 +130,8 @@ public function toArray() : array
'status' => $this->getDisplayStatus(),
'request-parameter' => $this->getRequestParameter(),
'percentage' => $this->getPercentage(),
'users' => $this->getUsers()
'users' => $this->getUsers(),
'groups' => $this->getGroups(),
];
}
}
26 changes: 24 additions & 2 deletions src/ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
use Jaspaul\LaravelRollout\Console\CreateCommand;
use Jaspaul\LaravelRollout\Console\DeleteCommand;
use Jaspaul\LaravelRollout\Console\AddUserCommand;
use Jaspaul\LaravelRollout\Console\AddGroupCommand;
use Jaspaul\LaravelRollout\Console\EveryoneCommand;
use Illuminate\Contracts\Config\Repository as Config;
use Jaspaul\LaravelRollout\Console\DeactivateCommand;
use Jaspaul\LaravelRollout\Console\PercentageCommand;
use Jaspaul\LaravelRollout\Console\RemoveUserCommand;
use Jaspaul\LaravelRollout\Console\RemoveGroupCommand;
use Illuminate\Support\ServiceProvider as IlluminateServiceProvider;

class ServiceProvider extends IlluminateServiceProvider
Expand Down Expand Up @@ -44,17 +46,21 @@ public function boot()
$driver = new Cache($app->make('cache.store'));
}

return new Rollout($driver);
$this->loadGroups($rollout = new Rollout($driver), $config->get('laravel-rollout.groups'));

return $rollout;
});

$this->commands([
AddGroupCommand::class,
AddUserCommand::class,
CreateCommand::class,
DeactivateCommand::class,
DeleteCommand::class,
EveryoneCommand::class,
ListCommand::class,
PercentageCommand::class,
RemoveGroupCommand::class,
RemoveUserCommand::class
]);
}
Expand All @@ -67,7 +73,8 @@ public function boot()
public function register()
{
$this->mergeConfigFrom(
__DIR__.'/../resources/config/laravel-rollout.php', 'laravel-rollout'
__DIR__.'/../resources/config/laravel-rollout.php',
'laravel-rollout'
);
}

Expand All @@ -92,4 +99,19 @@ protected function loadMigrations()
{
$this->loadMigrationsFrom(__DIR__.'/../resources/migrations');
}

/**
* Loads our groups
*
* @return void
*/
protected function loadGroups(Rollout $rollout, array $groups)
{
foreach ($groups as $group) {
$instance = resolve($group);
$rollout->defineGroup($instance->getName(), function ($user = null) use ($instance) {
return $instance->hasMember($user);
});
}
}
}
46 changes: 46 additions & 0 deletions tests/Console/AddGroupCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace Tests\Drivers;

use Tests\TestCase;
use Opensoft\Rollout\Rollout;
use Tests\Doubles\SampleGroup;
use Illuminate\Support\Facades\Artisan;
use Jaspaul\LaravelRollout\Helpers\User;
use Jaspaul\LaravelRollout\Drivers\Cache;

class AddGroupCommandTest extends TestCase
{
/**
* @test
*/
public function running_the_command_with_a_feature_will_create_the_corresponding_feature()
{
Artisan::call('rollout:add-group', [
'feature' => 'derp',
'group' => 'ballers'
]);

$store = app()->make('cache.store')->getStore();

$this->assertEquals('derp', $store->get('rollout.feature:__features__'));
$this->assertEquals('0||ballers|', $store->get('rollout.feature:derp'));
}

/**
* @test
*/
public function rollout_will_flag_a_user_as_active_if_the_group_returns_true()
{
config(['laravel-rollout.groups' => [SampleGroup::class]]);

$this->assertFalse(app()->make(Rollout::class)->isActive('derp', new User('id')));

Artisan::call('rollout:add-group', [
'feature' => 'derp',
'group' => 'sample-group'
]);

$this->assertTrue(app()->make(Rollout::class)->isActive('derp', new User('id')));
}
}
34 changes: 34 additions & 0 deletions tests/Console/RemoveGroupCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace Tests\Drivers;

use Tests\TestCase;
use Opensoft\Rollout\Rollout;
use Illuminate\Support\Facades\Artisan;
use Jaspaul\LaravelRollout\Helpers\User;
use Jaspaul\LaravelRollout\Drivers\Cache;

class RemoveGroupCommandTest extends TestCase
{
/**
* @test
*/
public function running_the_command_with_a_feature_will_remove_the_corresponding_user()
{
$store = app()->make('cache.store')->getStore();

$rollout = app()->make(Rollout::class);
$rollout->activateGroup('derp', 'ballers');

$this->assertEquals('derp', $store->get('rollout.feature:__features__'));
$this->assertEquals('0||ballers|', $store->get('rollout.feature:derp'));

Artisan::call('rollout:remove-group', [
'feature' => 'derp',
'group' => 'ballers'
]);

$this->assertEquals('derp', $store->get('rollout.feature:__features__'));
$this->assertEquals('0|||', $store->get('rollout.feature:derp'));
}
}
Loading

0 comments on commit cbcb5e1

Please sign in to comment.