Skip to content

Commit cbcb5e1

Browse files
authored
[Feature] Introduces the ability to define groups for rollout (#14)
* Introduces the ability to define groups for rollout
1 parent 48fc04a commit cbcb5e1

12 files changed

+380
-24
lines changed

readme.md

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ ROLLOUT_TABLE=rollout
5757

5858
### Implementing Interfaces
5959

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

6263
```php
6364
<?php
@@ -76,8 +77,63 @@ class User implements Contract
7677
}
7778
```
7879

80+
#### Group
81+
Your rollout groups must implement the `\Jaspaul\LaravelRollout\Contracts\Group` interface.
82+
83+
```php
84+
<?php
85+
86+
use Jaspaul\LaravelRollout\Contracts\Group;
87+
88+
class BetaUsersGroup implements Group
89+
{
90+
/**
91+
* The name of the group.
92+
*
93+
* @return string
94+
*/
95+
public function getName(): string
96+
{
97+
return 'beta-users';
98+
}
99+
100+
/**
101+
* Defines the rule membership in the group.
102+
*
103+
* @return boolean
104+
*/
105+
public function hasMember($user = null): bool
106+
{
107+
if (!is_null($user)) {
108+
return $user->hasOptedIntoBeta();
109+
}
110+
111+
return false;
112+
}
113+
}
114+
```
115+
116+
and you should update your local `laravel-rollout.php` configuration to include the group in the groups array:
117+
118+
`laravel-rollout.php`
119+
```php
120+
return [
121+
...
122+
'groups' => [
123+
BetaUsersGroup::class
124+
],
125+
...
126+
]
127+
```
128+
79129
## Commands
80130

131+
### Add Group
132+
133+
`php artisan rollout:add-group {feature} {group}`
134+
135+
Swap `{feature}` with the name of the feature, and `{group}` with the name you defined in the group class.
136+
81137
### Add User
82138

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

121177
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.
122178

179+
### Remove Group
180+
181+
`php artisan rollout:remove-group {feature} {group}`
182+
183+
Swap `{feature}` with the name of the feature, and `{group}` with the name you defined in the group class.
184+
123185
### Remove User
124186

125187
`php artisan rollout:remove-user {feature} {user}`

resources/config/laravel-rollout.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22

33
return [
44
'storage' => env('ROLLOUT_STORAGE', 'cache'),
5-
'table' => env('ROLLOUT_TABLE', 'rollout')
5+
'table' => env('ROLLOUT_TABLE', 'rollout'),
6+
'groups' => [],
67
];

src/Console/AddGroupCommand.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace Jaspaul\LaravelRollout\Console;
4+
5+
class AddGroupCommand extends RolloutCommand
6+
{
7+
/**
8+
* The name and signature of the console command.
9+
*
10+
* @var string
11+
*/
12+
protected $signature = 'rollout:add-group {feature} {group}';
13+
14+
/**
15+
* The console command description.
16+
*
17+
* @var string
18+
*/
19+
protected $description = 'Enables the provided feature for the group.';
20+
21+
/**
22+
* Adds the provided group to the requested feature. Note this will create
23+
* the feature as a side effect.
24+
*
25+
* @return void
26+
*/
27+
public function handle()
28+
{
29+
$name = $this->argument('feature');
30+
$group = $this->argument('group');
31+
32+
$this->rollout->activateGroup($name, $group);
33+
34+
$this->renderFeatureAsTable($name);
35+
}
36+
}

src/Console/RemoveGroupCommand.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace Jaspaul\LaravelRollout\Console;
4+
5+
use Jaspaul\LaravelRollout\Helpers\User;
6+
7+
class RemoveGroupCommand extends RolloutCommand
8+
{
9+
/**
10+
* The name and signature of the console command.
11+
*
12+
* @var string
13+
*/
14+
protected $signature = 'rollout:remove-group {feature} {group}';
15+
16+
/**
17+
* The console command description.
18+
*
19+
* @var string
20+
*/
21+
protected $description = 'Removes the provided group from the feature.';
22+
23+
/**
24+
* Removes the provided user from the feature. Note this will create
25+
* the feature as a side effect.
26+
*
27+
* @return void
28+
*/
29+
public function handle()
30+
{
31+
$name = $this->argument('feature');
32+
$group = $this->argument('group');
33+
34+
$this->rollout->deactivateGroup($name, $group);
35+
36+
$this->renderFeatureAsTable($name);
37+
}
38+
}

src/Contracts/Group.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Jaspaul\LaravelRollout\Contracts;
4+
5+
use Opensoft\Rollout\RolloutUserInterface;
6+
7+
interface Group
8+
{
9+
/**
10+
* The name of the group.
11+
*
12+
* @return string
13+
*/
14+
public function getName(): string;
15+
16+
/**
17+
* Checks if the given user is a member of this group.
18+
*
19+
* @param mixed $user
20+
*
21+
* @return boolean
22+
*/
23+
public function hasMember($user = null): bool;
24+
}

src/FeaturePresenter.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,16 @@ public function getUsers() : string
108108
return implode(', ', $this->feature->getUsers());
109109
}
110110

111+
/**
112+
* Returns a list of the groups that have this feature enabled.
113+
*
114+
* @return string
115+
*/
116+
public function getGroups() : string
117+
{
118+
return implode(', ', $this->feature->getGroups());
119+
}
120+
111121
/**
112122
* Returns an array representation of the feature presenter.
113123
*
@@ -120,7 +130,8 @@ public function toArray() : array
120130
'status' => $this->getDisplayStatus(),
121131
'request-parameter' => $this->getRequestParameter(),
122132
'percentage' => $this->getPercentage(),
123-
'users' => $this->getUsers()
133+
'users' => $this->getUsers(),
134+
'groups' => $this->getGroups(),
124135
];
125136
}
126137
}

src/ServiceProvider.php

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@
1212
use Jaspaul\LaravelRollout\Console\CreateCommand;
1313
use Jaspaul\LaravelRollout\Console\DeleteCommand;
1414
use Jaspaul\LaravelRollout\Console\AddUserCommand;
15+
use Jaspaul\LaravelRollout\Console\AddGroupCommand;
1516
use Jaspaul\LaravelRollout\Console\EveryoneCommand;
1617
use Illuminate\Contracts\Config\Repository as Config;
1718
use Jaspaul\LaravelRollout\Console\DeactivateCommand;
1819
use Jaspaul\LaravelRollout\Console\PercentageCommand;
1920
use Jaspaul\LaravelRollout\Console\RemoveUserCommand;
21+
use Jaspaul\LaravelRollout\Console\RemoveGroupCommand;
2022
use Illuminate\Support\ServiceProvider as IlluminateServiceProvider;
2123

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

47-
return new Rollout($driver);
49+
$this->loadGroups($rollout = new Rollout($driver), $config->get('laravel-rollout.groups'));
50+
51+
return $rollout;
4852
});
4953

5054
$this->commands([
55+
AddGroupCommand::class,
5156
AddUserCommand::class,
5257
CreateCommand::class,
5358
DeactivateCommand::class,
5459
DeleteCommand::class,
5560
EveryoneCommand::class,
5661
ListCommand::class,
5762
PercentageCommand::class,
63+
RemoveGroupCommand::class,
5864
RemoveUserCommand::class
5965
]);
6066
}
@@ -67,7 +73,8 @@ public function boot()
6773
public function register()
6874
{
6975
$this->mergeConfigFrom(
70-
__DIR__.'/../resources/config/laravel-rollout.php', 'laravel-rollout'
76+
__DIR__.'/../resources/config/laravel-rollout.php',
77+
'laravel-rollout'
7178
);
7279
}
7380

@@ -92,4 +99,19 @@ protected function loadMigrations()
9299
{
93100
$this->loadMigrationsFrom(__DIR__.'/../resources/migrations');
94101
}
102+
103+
/**
104+
* Loads our groups
105+
*
106+
* @return void
107+
*/
108+
protected function loadGroups(Rollout $rollout, array $groups)
109+
{
110+
foreach ($groups as $group) {
111+
$instance = resolve($group);
112+
$rollout->defineGroup($instance->getName(), function ($user = null) use ($instance) {
113+
return $instance->hasMember($user);
114+
});
115+
}
116+
}
95117
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
namespace Tests\Drivers;
4+
5+
use Tests\TestCase;
6+
use Opensoft\Rollout\Rollout;
7+
use Tests\Doubles\SampleGroup;
8+
use Illuminate\Support\Facades\Artisan;
9+
use Jaspaul\LaravelRollout\Helpers\User;
10+
use Jaspaul\LaravelRollout\Drivers\Cache;
11+
12+
class AddGroupCommandTest extends TestCase
13+
{
14+
/**
15+
* @test
16+
*/
17+
public function running_the_command_with_a_feature_will_create_the_corresponding_feature()
18+
{
19+
Artisan::call('rollout:add-group', [
20+
'feature' => 'derp',
21+
'group' => 'ballers'
22+
]);
23+
24+
$store = app()->make('cache.store')->getStore();
25+
26+
$this->assertEquals('derp', $store->get('rollout.feature:__features__'));
27+
$this->assertEquals('0||ballers|', $store->get('rollout.feature:derp'));
28+
}
29+
30+
/**
31+
* @test
32+
*/
33+
public function rollout_will_flag_a_user_as_active_if_the_group_returns_true()
34+
{
35+
config(['laravel-rollout.groups' => [SampleGroup::class]]);
36+
37+
$this->assertFalse(app()->make(Rollout::class)->isActive('derp', new User('id')));
38+
39+
Artisan::call('rollout:add-group', [
40+
'feature' => 'derp',
41+
'group' => 'sample-group'
42+
]);
43+
44+
$this->assertTrue(app()->make(Rollout::class)->isActive('derp', new User('id')));
45+
}
46+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace Tests\Drivers;
4+
5+
use Tests\TestCase;
6+
use Opensoft\Rollout\Rollout;
7+
use Illuminate\Support\Facades\Artisan;
8+
use Jaspaul\LaravelRollout\Helpers\User;
9+
use Jaspaul\LaravelRollout\Drivers\Cache;
10+
11+
class RemoveGroupCommandTest extends TestCase
12+
{
13+
/**
14+
* @test
15+
*/
16+
public function running_the_command_with_a_feature_will_remove_the_corresponding_user()
17+
{
18+
$store = app()->make('cache.store')->getStore();
19+
20+
$rollout = app()->make(Rollout::class);
21+
$rollout->activateGroup('derp', 'ballers');
22+
23+
$this->assertEquals('derp', $store->get('rollout.feature:__features__'));
24+
$this->assertEquals('0||ballers|', $store->get('rollout.feature:derp'));
25+
26+
Artisan::call('rollout:remove-group', [
27+
'feature' => 'derp',
28+
'group' => 'ballers'
29+
]);
30+
31+
$this->assertEquals('derp', $store->get('rollout.feature:__features__'));
32+
$this->assertEquals('0|||', $store->get('rollout.feature:derp'));
33+
}
34+
}

0 commit comments

Comments
 (0)