• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

acdh-oeaw / arche-diss-cache / #64

25 Oct 2024 06:41AM UTC coverage: 80.93% (+0.09%) from 80.841%
#64

push

php-coveralls

zozlak
Service::processException() extracted as a public method allowing reuse

9 of 10 new or added lines in 1 file covered. (90.0%)

174 of 215 relevant lines covered (80.93%)

3.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.11
/src/acdhOeaw/arche/lib/dissCache/Service.php
1
<?php
2

3
/*
4
 * The MIT License
5
 *
6
 * Copyright 2024 zozlak.
7
 *
8
 * Permission is hereby granted, free of charge, to any person obtaining a copy
9
 * of this software and associated documentation files (the "Software"), to deal
10
 * in the Software without restriction, including without limitation the rights
11
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
 * copies of the Software, and to permit persons to whom the Software is
13
 * furnished to do so, subject to the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be included in
16
 * all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
 * THE SOFTWARE.
25
 */
26

27
namespace acdhOeaw\arche\lib\dissCache;
28

29
use zozlak\logging\Log;
30
use acdhOeaw\arche\lib\RepoDb;
31
use acdhOeaw\arche\lib\SearchConfig;
32
use acdhOeaw\arche\lib\RepoResourceInterface;
33
use acdhOeaw\arche\lib\exception\NotFound;
34

35
/**
36
 * Wrapper handling boilerplate code around the ARCHE microservice initialization
37
 * 
38
 * Requires following config file structure:
39
 * ```
40
 * dissCacheService:
41
 *   db: sqlite:/some/path
42
 *   log:
43
 *     file: /some/path
44
 *     level: debug
45
 *    ttl:
46
 *      resource: 3600
47
 *      response: 31536000
48
 *    repoDb:
49
 *    - repoConfigPath.yaml
50
 *    allowedNmsp:
51
 *    - https://one
52
 *    - https://another
53
 *    metadataMode: parents
54
 *    parentProperty: ""
55
 *    resourceProperties: []
56
 *    relativesProperties: []
57
 * ```
58
 * @author zozlak
59
 */
60
class Service {
61

62
    private object $config;
63
    private Log $log;
64

65
    /**
66
     * 
67
     * @var callable
68
     */
69
    private $clbck;
70

71
    public function __construct(string $confFile) {
72
        $this->config = json_decode(json_encode(yaml_parse_file($confFile)));
1✔
73

74
        $logId     = sprintf("%08d", rand(0, 99999999));
1✔
75
        $tmpl      = "{TIMESTAMP}:$logId:{LEVEL}\t{MESSAGE}";
1✔
76
        $logCfg    = $this->config->dissCacheService->log;
1✔
77
        $this->log = new Log($logCfg->file, $logCfg->level, $tmpl);
1✔
78
    }
79

80
    /**
81
     * 
82
     * @param callable $clbck response cache miss handler with signature
83
     *   `fn(acdhOeaw\arche\lib\RepoResourceInterface $res, array $param)`
84
     *   where the `$param` will passed from the `serveRequest()` method 
85
     *   `$param` parameter.
86
     */
87
    public function setCallback(callable $clbck): void {
88
        $this->clbck = $clbck;
1✔
89
    }
90

91
    public function getConfig(): object {
92
        return $this->config;
×
93
    }
94

95
    public function getLog(): Log {
96
        return $this->log;
×
97
    }
98

99
    /**
100
     * 
101
     * @param array<mixed> $param
102
     */
103
    public function serveRequest(string $id, array $param): ResponseCacheItem {
104
        try {
105
            $t0  = microtime(true);
1✔
106
            $cfg = $this->config->dissCacheService;
1✔
107

108
            if (empty($id)) {
1✔
109
                $id = 'no identifer provided';
1✔
110
            }
111
            $this->log->info("Getting response for $id");
1✔
112
            $allowed = false;
1✔
113
            foreach ($cfg->allowedNmsp as $i) {
1✔
114
                if (str_starts_with($id, $i)) {
1✔
115
                    $allowed = true;
1✔
116
                    break;
1✔
117
                }
118
            }
119
            if (!$allowed) {
1✔
120
                throw new ServiceException("Requested resource $id not in allowed namespace", 400);
1✔
121
            }
122

123
            $cache = new CachePdo($cfg->db);
1✔
124

125
            $repos = [];
1✔
126
            foreach ($cfg->repoDb ?? [] as $i) {
1✔
127
                $repos[] = new RepoWrapperRepoInterface(RepoDb::factory($i), true);
×
128
            }
129
            $repos[] = new RepoWrapperGuzzle(false);
1✔
130

131
            $sc                         = new SearchConfig();
1✔
132
            $sc->metadataMode           = $cfg->metadataMode ?? RepoResourceInterface::META_RESOURCE;
1✔
133
            $sc->metadataParentProperty = $cfg->parentProperty ?? '';
1✔
134
            $sc->resourceProperties     = $cfg->resourceProperties ?? [];
1✔
135
            $sc->relativesProperties    = $cfg->relativesProperties ?? [];
1✔
136

137
            $cache = new ResponseCache($cache, $this->clbck, $cfg->ttl->resource, $cfg->ttl->response, $repos, $sc, $this->log);
1✔
138

139
            $response = $cache->getResponse($param, $id);
1✔
140
            $this->log->info("Ended in " . round(microtime(true) - $t0, 3) . " s");
1✔
141
            return $response;
1✔
142
        } catch (\Throwable $e) {
1✔
143
            return $this->processException($e);
1✔
144
        }
145
    }
146

147
    public function processException(\Throwable $e): ResponseCacheItem {
148
        $code              = $e->getCode();
1✔
149
        $ordinaryException = $e instanceof ServiceException || $e instanceof NotFound;
1✔
150

151
        $logMsg = "$code: " . $e->getMessage() . ($ordinaryException ? '' : "\n" . $e->getFile() . ":" . $e->getLine() . "\n" . $e->getTraceAsString());
1✔
152
        $this->log->error($logMsg);
1✔
153

154
        if ($code < 400 || $code >= 500) {
1✔
NEW
155
            $code = 500;
×
156
        }
157
        $body    = $ordinaryException ? $e->getMessage() . "\n" : "Internal Server Error\n";
1✔
158
        $headers = $e instanceof ServiceException ? $e->getHeaders() : [];
1✔
159
        return new ResponseCacheItem($body, $code, $headers, false);
1✔
160
    }
161
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc