Passed
Pull Request — master (#843)
by Stefano
02:42
created

ApiConfigTrait::readApiConfig()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 12
c 1
b 0
f 0
nc 3
nop 0
dl 0
loc 19
rs 9.8666
1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * BEdita, API-first content management framework
6
 * Copyright 2022 Atlas Srl, Chialab Srl
7
 *
8
 * This file is part of BEdita: you can redistribute it and/or modify
9
 * it under the terms of the GNU Lesser General Public License as published
10
 * by the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * See LICENSE.LGPL or <http://gnu.org/licenses/lgpl-3.0.html> for more details.
14
 */
15
namespace App\Utility;
16
17
use BEdita\SDK\BEditaClientException;
18
use BEdita\WebTools\ApiClientProvider;
19
use Cake\Cache\Cache;
20
use Cake\Collection\Collection;
21
use Cake\Core\Configure;
22
use Cake\Http\Exception\BadRequestException;
23
use Cake\Log\Log;
24
use Cake\Utility\Hash;
25
26
/**
27
 * Read and write configuration via API
28
 */
29
trait ApiConfigTrait
30
{
31
    /**
32
     * Cache config key
33
     *
34
     * @var string
35
     */
36
    protected static $cacheKey = 'api_config';
37
38
    /**
39
     * Allowed configuration keys from API
40
     *
41
     * @var array
42
     */
43
    protected static $configKeys = [
44
        'AlertMessage',
45
        'Export',
46
        'Modules',
47
        'Pagination',
48
        'Project',
49
        'Properties',
50
    ];
51
52
    /**
53
     * Read cached configuration items from API and update configuration
54
     * using `Configure::write`.
55
     *
56
     * @return void
57
     */
58
    protected function readApiConfig(): void
59
    {
60
        try {
61
            $configs = (array)Cache::remember(
62
                CacheTools::cacheKey(static::$cacheKey),
63
                function () {
64
                    return $this->fetchConfig();
65
                }
66
            );
67
        } catch (BEditaClientException $e) {
68
            Log::error($e->getMessage());
69
70
            return;
71
        }
72
73
        foreach ($configs as $config) {
74
            $content = (array)json_decode((string)Hash::get($config, 'attributes.content'), true);
75
            $key = (string)Hash::get($config, 'attributes.name');
76
            Configure::write($key, $content);
77
        }
78
    }
79
80
    /**
81
     * Fetch configurations from API
82
     *
83
     * @param null|string $key Configuration key to fetch, fetch all keys if null.
84
     * @return array
85
     */
86
    protected function fetchConfig(?string $key = null): array
87
    {
88
        $query = ['page_size' => 100];
89
        $response = (array)ApiClientProvider::getApiClient()->get('/config', $query);
90
        $collection = new Collection((array)Hash::get($response, 'data'));
91
92
        return (array)$collection->reject(function ($item) use ($key) {
93
            return !$this->isAppConfig($key, (array)$item);
94
        })->toArray();
95
    }
96
97
    /**
98
     * Check if a configuration is a valid application configuration.
99
     *
100
     * @param string|null $key Configuration key, if `null` consider any configuration key as valid
101
     * @param array $config Configuration data array from API.
102
     * @return bool
103
     */
104
    protected function isAppConfig(?string $key = null, array $config): bool
105
    {
106
        $attr = (array)Hash::get($config, 'attributes');
107
        if (
108
            (isset($attr['application_id']) && $attr['application_id'] === null) ||
109
            (isset($attr['context']) && $attr['context'] !== 'app') ||
110
            !in_array((string)Hash::get($attr, 'name'), static::$configKeys)
111
        ) {
112
            return false;
113
        }
114
115
        if ($key === null) {
116
            return true;
117
        }
118
119
        return $attr['name'] === $key;
120
    }
121
122
    /**
123
     * Get BEdita Manager application ID
124
     *
125
     * @return int
126
     */
127
    public function managerApplicationId(): int
128
    {
129
        $name = (string)Configure::read('ManagerAppName', 'manager');
130
        $filter = compact('name');
131
        $response = (array)ApiClientProvider::getApiClient()->get('/admin/applications', compact('filter'));
132
133
        return (int)Hash::get($response, 'data.0.id');
134
    }
135
136
    /**
137
     * Save configuration to API
138
     *
139
     * @param string $key Configuration key
140
     * @param array $data Configuration data
141
     * @return void
142
     * @throws \Cake\Http\Exception\BadRequestException
143
     */
144
    public function saveApiConfig(string $key, array $data): void
145
    {
146
        if (!in_array($key, static::$configKeys)) {
147
            throw new BadRequestException(__('Bad configuration key "{0}"', $key));
148
        }
149
        $items = array_values($this->fetchConfig($key));
150
        $config = (array)Hash::get($items, '0');
151
        $configId = Hash::get($config, 'id');
152
        $managerAppId = Hash::get($config, 'attributes.application_id');
153
        if (empty($managerAppId)) {
154
            $managerAppId = $this->managerApplicationId();
155
        }
156
        $endpoint = '/admin/config';
157
        $body = [
158
            'data' => [
159
                'type' => 'config',
160
                'attributes' => [
161
                    'name' => $key,
162
                    'context' => 'app',
163
                    'content' => json_encode($data),
164
                    'application_id' => $managerAppId,
165
                ],
166
            ],
167
        ];
168
169
        if (empty($configId)) {
170
            ApiClientProvider::getApiClient()->post($endpoint, json_encode($body));
171
        } else {
172
            $body['data']['id'] = (string)$configId;
173
            ApiClientProvider::getApiClient()->patch(sprintf('%s/%s', $endpoint, $configId), json_encode($body));
174
        }
175
        Cache::delete(CacheTools::cacheKey(static::$cacheKey));
176
        Cache::delete(CacheTools::cacheKey('properties'));
177
    }
178
}
179