Passed
Push — develop ( 500084...288399 )
by Stan
09:28
created

HistogramSamplesBuilder::group()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 21
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 13
nc 1
nop 0
dl 0
loc 21
ccs 11
cts 11
cp 1
crap 2
rs 9.8333
c 0
b 0
f 0
1
<?php
2
3
namespace Krenor\Prometheus\Storage\Builders;
4
5
use InvalidArgumentException;
6
use Krenor\Prometheus\Metrics\Histogram;
7
use Tightenco\Collect\Support\Collection;
8
use Krenor\Prometheus\Contracts\SamplesBuilder;
9
10
class HistogramSamplesBuilder extends SamplesBuilder
11
{
12
    /**
13
     * @var Histogram
14
     */
15
    protected $metric;
16
17
    /**
18
     * HistogramSamplesBuilder constructor.
19
     *
20
     * @param Histogram $histogram
21
     * @param Collection $items
22
     */
23
    public function __construct(Histogram $histogram, Collection $items)
24
    {
25 10
        parent::__construct($histogram, $items);
26
    }
27 10
28 10
    /**
29
     * {@inheritdoc}
30
     */
31
    protected function parse(): Collection
32
    {
33 1
        $name = $this->metric->key();
34
        $buckets = $this->metric->buckets()->push('+Inf');
35 1
36
        return parent
37
            ::parse()
38
            ->groupBy(function ($data) {
39
                return json_encode($data['labels']);
40
            })->flatMap(function (Collection $data) use ($name, $buckets) {
41 10
                $labels = $data->first()['labels'];
42
43 10
                /** @var $sum Collection */
44
                /** @var $observations Collection */
45
                [$sum, $observations] = $data->partition('bucket', null);
46 10
47
                if ($sum->count() !== 1) {
48 10
                    throw new InvalidArgumentException('Sum of bucket observations missing.');
49 1
                }
50
51
                return $this
52 9
                    ->pad($this->complete($buckets, $observations), new Collection)
53
                    ->map(function (array $items) use ($name, $labels) {
54
                        $items['name'] = "{$name}_bucket";
55 9
                        $items['labels'] = $labels + [
56 9
                            'le' => $items['bucket'],
57 9
                        ];
58
59 9
                        return $items;
60
                    })->push([
61 9
                        'name'   => "{$name}_count",
62 9
                        'value'  => $observations->sum('value'),
63 10
                        'labels' => $labels,
64
                    ])->push([
65
                        'name'   => "{$name}_sum",
66
                        'value'  => $sum->first()['value'],
67
                        'labels' => $labels,
68
                    ]);
69 9
            });
70
    }
71
72 9
    /**
73
     * @param Collection $buckets
74 9
     * @param Collection $observations
75 9
     *
76
     * @return Collection
77
     */
78 9
    private function complete(Collection $buckets, Collection $observations): Collection
79 9
    {
80
        $missing = $buckets
81
            ->diff($observations->pluck('bucket'))
82 9
            ->map(function ($bucket) {
83 9
                return compact('bucket') + ['value' => 0];
84
            });
85
86
        return $observations
87
            ->reject(function (array $observation) use ($buckets) {
88
                return !$buckets->contains($observation['bucket']);
89
            })->merge($missing)
90
            ->sort(function (array $left, array $right) {
91
                // Due to http://php.net/manual/en/language.types.string.php#language.types.string.conversion the
92 9
                // bucket containing "+Inf" will be cast to 0. Sorting regularly would end up with it incorrectly
93
                // sitting at the very first spot instead of where it belongs - at the end.
94
                if ($left['bucket'] === '+Inf') {
95 9
                    return 1;
96
                }
97 9
98 9
                if ($right['bucket'] === '+Inf') {
99
                    return -1;
100
                }
101
102 7
                return $left['bucket'] <=> $right['bucket'];
103 9
            })->values();
104 9
    }
105 9
106
    /**
107
     * @param Collection $data
108
     * @param Collection $result
109
     * @param int $sum
110
     * @param int $i
111 9
     *
112
     * @return Collection
113
     */
114
    private function pad(Collection $data, Collection $result, int $sum = 0, int $i = 0): Collection
115
    {
116
        if ($i >= $data->count()) {
117 9
            return $result;
118 6
        }
119
120
        $value = $data[$i]['value'] + $sum;
121 9
        $bucket = $data[$i]['bucket'];
122 9
123
        $result->push(compact('bucket', 'value'));
124
125 9
        return $this->pad($data, $result, $value, ++$i);
126 9
    }
127
}
128