Completed
Push — master ( d61801...46edea )
by Biao
05:21
created

Apollo::pullBatch()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 7
eloc 13
c 2
b 0
f 0
nc 7
nop 3
dl 0
loc 20
rs 8.8333
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 Apollo
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\Apollo: $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
20
    protected $releaseKeys   = [];
21
    protected $notifications = [];
22
23
    protected $watching = true;
24
25
    public function __construct(array $settings)
26
    {
27
        $this->server = $settings['server'];
28
        $this->appId = $settings['app_id'];
29
        if (isset($settings['cluster'])) {
30
            $this->cluster = $settings['cluster'];
31
        }
32
        if (isset($settings['namespaces'])) {
33
            $this->namespaces = $settings['namespaces'];
34
        }
35
        if (isset($settings['client_ip'])) {
36
            $this->clientIp = $settings['client_ip'];
37
        }
38
        if (isset($settings['pull_timeout'])) {
39
            $this->pullTimeout = $settings['pull_timeout'];
40
        }
41
    }
42
43
    public static function createFromEnv()
44
    {
45
        $envs = getenv();
46
        if (!isset($envs['APOLLO_SERVER'], $envs['APOLLO_APP_ID'])) {
47
            throw new \InvalidArgumentException('Missing environment variable APOLLO_SERVER & APOLLO_APP_ID');
48
        }
49
        $options = [
50
            'server'       => $envs['APOLLO_SERVER'],
51
            'app_id'       => $envs['APOLLO_APP_ID'],
52
            'cluster'      => isset($envs['APOLLO_CLUSTER']) ? $envs['APOLLO_CLUSTER'] : null,
53
            'namespaces'   => isset($envs['APOLLO_NAMESPACES']) ? explode(',', $envs['APOLLO_NAMESPACES']) : null,
54
            'client_ip'    => isset($envs['APOLLO_CLIENT_IP']) ? $envs['APOLLO_CLIENT_IP'] : null,
55
            'pull_timeout' => isset($envs['APOLLO_PULL_TIMEOUT']) ? $envs['APOLLO_PULL_TIMEOUT'] : null,
56
        ];
57
        return new static($options);
58
    }
59
60
61
    public function pullBatch(array $namespaces, $withReleaseKey = false, array $options = [])
62
    {
63
        $configs = [];
64
        $uri = sprintf('%s/configs/%s/%s/', $this->server, $this->appId, $this->cluster);
65
        foreach ($namespaces as $namespace) {
66
            $url = $uri . $namespace . '?' . http_build_query([
67
                    'releaseKey' => $withReleaseKey && isset($this->releaseKeys[$namespace]) ? $this->releaseKeys[$namespace] : null,
68
                    'ip'         => $this->clientIp,
69
                ]);
70
            $timeout = isset($options['timeout']) ? $options['timeout'] : $this->pullTimeout;
71
            $response = $this->httpGet($url, compact('timeout'));
72
            if ($response['statusCode'] === 200) {
73
                $configs[$namespace] = json_decode($response['body'], true);
74
                $this->releaseKeys[$namespace] = $configs[$namespace]['releaseKey'];
75
            } elseif ($response['statusCode'] === 304) {
76
                // ignore 304
77
            }
78
79
        }
80
        return $configs;
81
    }
82
83
    public function pullAll($withReleaseKey = false, array $options = [])
84
    {
85
        return $this->pullBatch($this->namespaces, $withReleaseKey, $options);
86
    }
87
88
    public function pullAllAndSave($filepath, $keepOld = false, array $options = [])
89
    {
90
        $all = $this->pullAll(false, $options);
91
        if (count($all) !== count($this->namespaces)) {
92
            throw new \RuntimeException('Incomplete Apollo configuration list');
93
        }
94
        $configs = [];
95
        foreach ($all as $namespace => $config) {
96
            $configs[] = '# Namespace: ' . $config['namespaceName'];
97
            ksort($config['configurations']);
98
            foreach ($config['configurations'] as $key => $value) {
99
                $configs[] = sprintf('%s=%s', $key, $value);
100
            }
101
        }
102
        if (empty($configs)) {
103
            throw new \RuntimeException('Empty Apollo configuration list');
104
        }
105
        if ($keepOld && file_exists($filepath)) {
106
            rename($filepath, $filepath . '.' . date('YmdHis'));
107
        }
108
        $fileContent = implode(PHP_EOL, $configs);
109
        if (Context::inCoroutine()) {
110
            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

110
            Coroutine::/** @scrutinizer ignore-call */ 
111
                       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...
111
        } else {
112
            file_put_contents($filepath, $fileContent);
113
        }
114
        return $configs;
115
    }
116
117
    public function startWatchNotification(callable $callback, array $options = [])
118
    {
119
        if (!isset($options['timeout']) || $options['timeout'] < 60) {
120
            $options['timeout'] = 70;
121
        }
122
        $this->watching = true;
123
        $this->notifications = [];
124
        foreach ($this->namespaces as $namespace) {
125
            $this->notifications[$namespace] = ['namespaceName' => $namespace, 'notificationId' => -1];
126
        }
127
        while ($this->watching) {
128
            $url = sprintf('%s/notifications/v2?%s',
129
                $this->server,
130
                http_build_query([
131
                    'appId'         => $this->appId,
132
                    'cluster'       => $this->cluster,
133
                    'notifications' => json_encode(array_values($this->notifications)),
134
                ])
135
            );
136
            $response = $this->httpGet($url, $options);
137
138
            if ($response['statusCode'] === 200) {
139
                $notifications = json_decode($response['body'], true);
140
                if (empty($notifications)) {
141
                    continue;
142
                }
143
                $callback($notifications);
144
                array_walk($notifications, function (&$notification) {
145
                    unset($notification['messages']);
146
                });
147
                $this->notifications = array_merge($this->notifications, array_column($notifications, null, 'namespaceName'));
148
            } elseif ($response['statusCode'] === 304) {
149
                // ignore 304
150
            }
151
        }
152
    }
153
154
    public function stopWatchNotification()
155
    {
156
        $this->watching = false;
157
    }
158
}
159