Completed
Push — master ( 4314d2...3ee107 )
by Artem
03:45
created

SyncUsers::formatModes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 7
rs 10
1
<?php
2
3
namespace Slides\Connector\Auth\Commands;
4
5
use Slides\Connector\Auth\AuthService;
6
use Slides\Connector\Auth\Sync\Syncable;
7
use Slides\Connector\Auth\Client as AuthClient;
8
use Slides\Connector\Auth\Sync\User as SyncUser;
9
use Illuminate\Support\Facades\Auth;
10
11
/**
12
 * Class SyncUsers
13
 *
14
 * @package Slides\Connector\Auth\Commands
15
 */
16
class SyncUsers extends \Illuminate\Console\Command
17
{
18
    /**
19
     * The name and signature of the console command.
20
     *
21
     * @var string
22
     */
23
    protected $signature = 'connector:sync-users
24
                            {--passwords= : Allow syncing passwords (can rewrite remotely and locally) }';
25
26
    /**
27
     * The console command description.
28
     *
29
     * @var string
30
     */
31
    protected $description = 'Synchronize users with the remote authentication service';
32
33
    /**
34
     * @var AuthClient
35
     */
36
    protected $authClient;
37
38
    /**
39
     * @var AuthService
40
     */
41
    protected $authService;
42
43
    /**
44
     * Whether remote and local passwords can be overwritten.
45
     *
46
     * @var bool
47
     */
48
    private $passwords;
49
50
    /**
51
     * SyncUsers constructor.
52
     *
53
     * @param AuthService $authService
54
     */
55
    public function __construct(AuthService $authService)
56
    {
57
        $this->authService = $authService;
58
59
        parent::__construct();
60
    }
61
62
    /**
63
     * Execute the console command.
64
     *
65
     * @return void
66
     */
67
    public function handle()
68
    {
69
        $this->passwords = $this->hasOption('passwords');
70
        $this->authClient = new AuthClient();
71
72
        /** @var \Illuminate\Database\Eloquent\Collection $users */
73
        $users = Auth::getProvider()->createModel()
74
            ->newQuery()
75
            ->get();
76
77
        if($users->isEmpty()) {
78
            $this->info('No users found.');
79
            return;
80
        }
81
82
        if(!$this->confirm('There are ' . $users->count() . ' users to sync. Continue?')) {
83
            return;
84
        }
85
86
        $response = $this->authClient->request('sync', [
87
            'users' => $this->formatUsers($users),
88
            'modes' => $this->formatModes()
89
        ]);
90
91
        $difference = $response['difference'];
92
        $remoteStats = $response['stats'];
93
94
        $this->writeStats('Remote affection', array_keys($remoteStats), array_values($remoteStats));
95
96
        if(!count($difference)) {
97
            $this->info('There are no changes to apply locally.');
98
        }
99
        else {
100
            $this->info('Remote changes detected, applying...');
101
102
            $localStats = $this->applyDifference($difference);
103
104
            $this->writeStats('Local affection', array_keys($localStats), array_values($localStats));
105
        }
106
    }
107
108
    /**
109
     * Format users
110
     *
111
     * @param Syncable[]|\Illuminate\Database\Eloquent\Collection $users
112
     *
113
     * @return array
114
     */
115
    protected function formatUsers($users): array
116
    {
117
        return $users
118
            ->map(function(Syncable $user) {
119
                return [
120
                    'id' => $user->retrieveId(),
121
                    'remoteId' => $user->retrieveRemoteId(),
122
                    'name' => $user->retrieveName(),
123
                    'email' => $user->retrieveEmail(),
124
                    'password' => $user->retrievePassword(),
125
                    'created_at' => $user->retrieveCreatedAt()->toDateTimeString(),
126
                    'updated_at' => $user->retrieveUpdatedAt()->toDateTimeString()
127
                ];
128
            })
129
            ->toArray();
130
    }
131
132
    /**
133
     * Apply the difference
134
     *
135
     * @param array $users
136
     *
137
     * @return array
138
     */
139
    protected function applyDifference(array $users): array
140
    {
141
        $stats = ['created' => 0, 'updated' => 0, 'deleted' => 0];
142
143
        foreach ($this->loadUsers($users) as $user) {
144
            switch ($user->getRemoteAction()) {
145
                case 'create': {
146
                    $this->createUser($user);
147
                    $stats['created']++;
148
                    break;
149
                }
150
                case 'update': {
151
                    $this->updateUser($user);
152
                    $stats['updated']++;
153
                    break;
154
                }
155
                case 'delete': {
156
                    $this->deleteUser($user);
157
                    $stats['deleted']++;
158
                }
159
            }
160
        }
161
162
        return $stats;
163
    }
164
165
    /**
166
     * Write stats
167
     *
168
     * @param string $title
169
     * @param array $keys
170
     * @param array $values
171
     */
172
    private function writeStats(string $title, array $keys, array $values)
173
    {
174
        $this->output->title($title);
175
        $this->output->table($keys, array($values));
176
    }
177
178
    /**
179
     * Create a local user
180
     *
181
     * @param SyncUser $user
182
     *
183
     * @throws
184
     */
185
    private function createUser(SyncUser $user)
186
    {
187
        $email = $user->getEmail();
188
189
        // If a user already exists, skip the process
190
        if(Auth::getProvider()->retrieveByCredentials(['email' => $email])) {
191
            $this->warn("User with email {$email} already exists, unable to create");
192
193
            return;
194
        }
195
196
        $this->authService->handle(AuthService::HANDLER_USER_SYNC_CREATE, ['remote' => $user]);
197
    }
198
199
    /**
200
     * Update a local user
201
     *
202
     * @param SyncUser $user
203
     *
204
     * @throws
205
     */
206
    private function updateUser(SyncUser $user)
207
    {
208
        $email = $user->getEmail();
209
210
        // If a user doesn't exist, skip the process
211
        if(!$model = Auth::getProvider()->retrieveByCredentials(['email' => $email])) {
212
            $this->warn("User with email {$email} doesn't exist, unable to update");
213
214
            return;
215
        }
216
217
        if(!$this->passwords) {
218
            $user->resetPassword();
219
        }
220
221
        $this->authService->handle(AuthService::HANDLER_USER_SYNC_UPDATE, [
222
            'remote' => $user,
223
            'local' => $model
224
        ]);
225
    }
226
227
    /**
228
     * Delete a local user
229
     *
230
     * @param SyncUser $user
231
     *
232
     * @throws
233
     */
234
    private function deleteUser(SyncUser $user)
235
    {
236
        $email = $user->getEmail();
237
238
        // If a user doesn't exist, skip the process
239
        if(!$model = Auth::getProvider()->retrieveByCredentials(['email' => $email])) {
240
            $this->warn("User with email {$email} doesn't exist, unable to delete");
241
242
            return;
243
        }
244
245
        $this->authService->handle(AuthService::HANDLER_USER_SYNC_DELETE, ['remote' => $user, 'local' => $model]);
246
    }
247
248
    /**
249
     * Parse user into entities
250
     *
251
     * @param array $users
252
     *
253
     * @return SyncUser[]|array
254
     */
255
    protected function loadUsers(array $users): array
256
    {
257
        return array_map(function(array $user) {
258
            return new SyncUser(
259
                array_get($user, 'id'),
260
                array_get($user, 'name'),
261
                array_get($user, 'email'),
262
                array_get($user, 'password'),
263
                array_get($user, 'updated_at'),
264
                array_get($user, 'created_at'),
265
                array_get($user, 'action')
266
            );
267
        }, $users);
268
    }
269
270
    /**
271
     * Format the modes.
272
     *
273
     * @return array
274
     */
275
    protected function formatModes(): array
276
    {
277
        $modes = [
278
            'passwords' => $this->passwords
279
        ];
280
281
        return array_keys(array_filter($modes));
282
    }
283
}