Passed
Push — master ( 2dbb0d...2ad290 )
by Xavier
01:17
created

Fetcher::fetch()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 0
1
<?php
2
3
namespace PubPeerFoundation\PublicationDataExtractor;
4
5
use Generator;
6
use Tightenco\Collect\Support\Arr;
7
use GuzzleHttp\Promise\EachPromise;
8
use Psr\Http\Message\ResponseInterface;
9
use GuzzleHttp\Exception\RequestException;
10
use GrahamCampbell\GuzzleFactory\GuzzleFactory;
11
use PubPeerFoundation\PublicationDataExtractor\Resources\Resource;
12
use PubPeerFoundation\PublicationDataExtractor\Identifiers\Identifier;
13
14
class Fetcher
15
{
16
    /**
17
     * @var Identifier
18
     */
19
    protected $identifier;
20
21
    /**
22
     * @var \GuzzleHttp\Client
23
     */
24
    protected $client;
25
26
    /**
27
     * @var array
28
     */
29
    protected $apiData = [];
30
31
    /**
32
     * @var array
33
     */
34
    protected $resources = [];
35
36
    /**
37
     * @var array
38
     */
39
    protected $resourcesToFetch = [];
40
41
    /**
42
     * @var array
43
     */
44
    protected $errors = [];
45
46
    /**
47
     * The Output object.
48
     *
49
     * @var Output
50
     */
51
    protected $output;
52
53
    /**
54
     * ApiDataFetcher constructor.
55
     *
56
     * @param Identifier $identifier
57
     */
58
    public function __construct(Identifier $identifier)
59
    {
60
        $this->identifier = $identifier;
61
        $this->resourcesToFetch = $identifier->getRelatedResources();
62
        $this->output = new Output();
63
        $this->client = GuzzleFactory::make(compact('headers'), 100);
64
    }
65
66
    /**
67
     * Fetch Data and return Output.
68
     *
69
     * @return Output
70
     */
71
    public function fetch(): Output
72
    {
73
        $this->fetchResources();
74
75
        $this->fetchComplementaryResources();
76
77
        $this->output->resetLists();
78
79
        return $this->output;
80
    }
81
82
    /**
83
     * Fetch resources.
84
     */
85
    protected function fetchResources(): void
86
    {
87
        (new EachPromise($this->getPromises(), [
88
            'concurrency' => 5,
89
            'fulfilled' => function (ResponseInterface $response, $index) {
90
                $this->apiData[] = $this->getResourceAtIndex($index)
91
                    ->getDataFrom((string) $response->getBody());
92
            },
93
            'rejected' => function (RequestException $exception, $index) {
94
                $resourceName = get_class_name($this->getResourceAtIndex($index));
95
                $this->errors[$resourceName] = $exception->getCode();
96
            },
97
        ]))->promise()->wait();
98
    }
99
100
    /**
101
     * Get list of API calls promises.
102
     *
103
     * @return Generator
104
     */
105
    protected function getPromises(): Generator
106
    {
107
        foreach ($this->resourcesToFetch as $resourceClass) {
108
            $resource = $this->instantiateResource($resourceClass);
109
110
            $promise = $this->client->requestAsync(
111
                'GET',
112
                $resource->getApiUrl(),
113
                $resource->getRequestOptions()
114
            );
115
116
            yield $promise;
117
        }
118
    }
119
120
    /**
121
     * Get errors as array.
122
     *
123
     * @return array
124
     */
125
    public function getErrors(): array
126
    {
127
        return $this->errors;
128
    }
129
130
    /**
131
     * Get resource instance from resources array.
132
     *
133
     * @param int $index
134
     *
135
     * @return resource
136
     */
137
    protected function getResourceAtIndex(int $index): Resource
138
    {
139
        return $this->resources[$index];
140
    }
141
142
    /**
143
     * Create and store an instance of the Resource class.
144
     *
145
     * @param string $resourceClass
146
     *
147
     * @return resource
148
     */
149
    protected function instantiateResource($resourceClass): Resource
150
    {
151
        return $this->resources[] = new $resourceClass($this->identifier, $this->output);
152
    }
153
154
    /**
155
     * Fetch again with new related Identifiers.
156
     */
157
    protected function fetchComplementaryResources()
158
    {
159
        $flatIdentifiers = Arr::flatten(Arr::pluck($this->apiData, 'identifiers'));
160
161
        foreach ($this->identifier->getComplementaryResources() as $key => $value) {
162
            if (false !== $valueKey = array_search($key, $flatIdentifiers)) {
163
                try {
164
                    $this->identifier = (new IdentifierResolver($flatIdentifiers[$valueKey - 1]))->handle();
165
166
                    $this->resourcesToFetch = [$value];
167
                    $this->resources = [];
168
169
                    $this->fetchResources();
170
                } catch (Exceptions\UnknownIdentifierException $e) {
171
                    continue;
172
                }
173
            }
174
        }
175
    }
176
}
177