Completed
Push — master ( 3a2992...6aee26 )
by Biao
04:48
created

Client::pullBatch()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 13
nc 7
nop 3
dl 0
loc 20
rs 8.8333
c 0
b 0
f 0
1
<?php
2
3
namespace Hhxsv5\LaravelS\Components\Apollo;
4
5
use Hhxsv5\LaravelS\Components\HttpClient\SimpleHttpTrait;
6
use Hhxsv5\LaravelS\Swoole\Coroutine\Context;
7
use Swoole\Coroutine;
8
9
class Client
10
{
11
    use SimpleHttpTrait;
0 ignored issues
show
introduced by
The trait Hhxsv5\LaravelS\Componen...pClient\SimpleHttpTrait requires some properties which are not provided by Hhxsv5\LaravelS\Components\Apollo\Client: $statusCode, $body, $errMsg, $errCode, $headers
Loading history...
12
13
    protected $server;
14
    protected $appId;
15
    protected $cluster      = 'default';
16
    protected $namespaces   = ['application'];
17
    protected $clientIp;
18
    protected $pullTimeout  = 5;
19
    protected $backupOldEnv = false;
20
21
    protected $releaseKeys   = [];
22
    protected $notifications = [];
23
24
    protected $watching = true;
25
26
    public function __construct(array $settings)
27
    {
28
        $this->server = $settings['server'];
29
        $this->appId = $settings['app_id'];
30
        if (isset($settings['cluster'])) {
31
            $this->cluster = $settings['cluster'];
32
        }
33
        if (isset($settings['namespaces'])) {
34
            if (empty($settings['namespaces'])) {
35
                throw new \InvalidArgumentException('Empty Apollo namespace');
36
            }
37
            $this->namespaces = $settings['namespaces'];
38
        }
39
        if (isset($settings['client_ip'])) {
40
            $this->clientIp = $settings['client_ip'];
41
        } else {
42
            $this->clientIp = current(swoole_get_local_ip()) ?: null;
43
        }
44
        if (isset($settings['pull_timeout'])) {
45
            $this->pullTimeout = (int)$settings['pull_timeout'];
46
        }
47
        if (isset($settings['backup_old_env'])) {
48
            $this->backupOldEnv = (bool)$settings['backup_old_env'];
49
        }
50
    }
51
52
    public static function createFromEnv()
53
    {
54
        if (!getenv('APOLLO_SERVER') || !getenv('APOLLO_APP_ID')) {
55
            throw new \InvalidArgumentException('Missing environment variable APOLLO_SERVER & APOLLO_APP_ID');
56
        }
57
        $options = [
58
            'server'         => getenv('APOLLO_SERVER'),
59
            'app_id'         => getenv('APOLLO_APP_ID'),
60
            'cluster'        => getenv('APOLLO_CLUSTER') ?: null,
61
            'namespaces'     => explode(',', getenv('APOLLO_NAMESPACES')) ?: null,
62
            'client_ip'      => getenv('APOLLO_CLIENT_IP') ?: null,
63
            'pull_timeout'   => getenv('APOLLO_PULL_TIMEOUT') ?: null,
64
            'backup_old_env' => getenv('APOLLO_BACKUP_OLD_ENV') ?: false,
65
        ];
66
        return new static($options);
67
    }
68
69
70
    public function pullBatch(array $namespaces, $withReleaseKey = false, array $options = [])
71
    {
72
        $configs = [];
73
        $uri = sprintf('%s/configs/%s/%s/', $this->server, $this->appId, $this->cluster);
74
        foreach ($namespaces as $namespace) {
75
            $url = $uri . $namespace . '?' . http_build_query([
76
                    'releaseKey' => $withReleaseKey && isset($this->releaseKeys[$namespace]) ? $this->releaseKeys[$namespace] : null,
77
                    'ip'         => $this->clientIp,
78
                ]);
79
            $timeout = isset($options['timeout']) ? $options['timeout'] : $this->pullTimeout;
80
            $response = $this->httpGet($url, compact('timeout'));
81
            if ($response['statusCode'] === 200) {
82
                $configs[$namespace] = json_decode($response['body'], true);
83
                $this->releaseKeys[$namespace] = $configs[$namespace]['releaseKey'];
84
            } elseif ($response['statusCode'] === 304) {
85
                // ignore 304
86
            }
87
88
        }
89
        return $configs;
90
    }
91
92
    public function pullAll($withReleaseKey = false, array $options = [])
93
    {
94
        return $this->pullBatch($this->namespaces, $withReleaseKey, $options);
95
    }
96
97
    public function pullAllAndSave($filepath, array $options = [])
98
    {
99
        $all = $this->pullAll(false, $options);
100
        if (count($all) !== count($this->namespaces)) {
101
            throw new \RuntimeException('Incomplete Apollo configuration list');
102
        }
103
        $configs = [];
104
        foreach ($all as $namespace => $config) {
105
            $configs[] = '# Namespace: ' . $config['namespaceName'];
106
            ksort($config['configurations']);
107
            foreach ($config['configurations'] as $key => $value) {
108
                $configs[] = sprintf('%s=%s', $key, $value);
109
            }
110
        }
111
        if (empty($configs)) {
112
            throw new \RuntimeException('Empty Apollo configuration list');
113
        }
114
        if ($this->backupOldEnv && file_exists($filepath)) {
115
            rename($filepath, $filepath . '.' . date('YmdHis'));
116
        }
117
        $fileContent = implode(PHP_EOL, $configs);
118
        if (Context::inCoroutine()) {
119
            Coroutine::writeFile($filepath, $fileContent);
0 ignored issues
show
Bug introduced by
The call to Swoole\Coroutine::writeFile() has too few arguments starting with flags. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

119
            Coroutine::/** @scrutinizer ignore-call */ 
120
                       writeFile($filepath, $fileContent);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
120
        } else {
121
            file_put_contents($filepath, $fileContent);
122
        }
123
        return $configs;
124
    }
125
126
    public function startWatchNotification(callable $callback, array $options = [])
127
    {
128
        if (!isset($options['timeout']) || $options['timeout'] < 60) {
129
            $options['timeout'] = 70;
130
        }
131
        $this->watching = true;
132
        $this->notifications = [];
133
        foreach ($this->namespaces as $namespace) {
134
            $this->notifications[$namespace] = ['namespaceName' => $namespace, 'notificationId' => -1];
135
        }
136
        while ($this->watching) {
137
            $url = sprintf('%s/notifications/v2?%s',
138
                $this->server,
139
                http_build_query([
140
                    'appId'         => $this->appId,
141
                    'cluster'       => $this->cluster,
142
                    'notifications' => json_encode(array_values($this->notifications)),
143
                ])
144
            );
145
            $response = $this->httpGet($url, $options);
146
147
            if ($response['statusCode'] === 200) {
148
                $notifications = json_decode($response['body'], true);
149
                if (empty($notifications)) {
150
                    continue;
151
                }
152
                if (!empty($this->notifications) && current($this->notifications)['notificationId'] !== -1) { // Ignore the first pull
153
                    $callback($notifications);
154
                }
155
                array_walk($notifications, function (&$notification) {
156
                    unset($notification['messages']);
157
                });
158
                $this->notifications = array_merge($this->notifications, array_column($notifications, null, 'namespaceName'));
159
            } elseif ($response['statusCode'] === 304) {
160
                // ignore 304
161
            }
162
        }
163
    }
164
165
    public function stopWatchNotification()
166
    {
167
        $this->watching = false;
168
    }
169
}
170