Passed
Push — develop ( d48ecd...e7a3d3 )
by Csaba
41s
created

RestObject   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 181
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 100%

Importance

Changes 5
Bugs 0 Features 0
Metric Value
dl 0
loc 181
c 5
b 0
f 0
wmc 29
lcom 1
cbo 7
ccs 106
cts 106
cp 1
rs 10

12 Methods

Rating   Name   Duplication   Size   Complexity  
A find() 0 9 2
A validateUniqueFields() 0 9 2
A allUniqueFieldHasIndex() 0 13 1
A scanUniqueFields() 0 17 4
A generateScan() 0 18 2
A generateScanFilter() 0 20 3
A queryUniqueFields() 0 18 4
A generateQueries() 0 10 3
A generateQuery() 0 21 2
A generatePromises() 0 9 2
A generatePromise() 0 16 3
A query() 0 4 1
1
<?php
2
namespace Fathomminds\Rest\Database\DynamoDb;
3
4
use Aws\DynamoDb\Marshaler;
5
use Fathomminds\Rest\Exceptions\RestException;
6
use Fathomminds\Rest\Objects\RestObject as CoreRestObject;
7
use GuzzleHttp\Promise as PromiseFunctions;
8
use GuzzleHttp\Promise\Promise as Promise;
9
10
class RestObject extends CoreRestObject
11
{
12
    protected $primaryKey = '_id';
13
    protected $databaseClass = Database::class;
14
15 2
    public function find($client = null)
16
    {
17 2
        if ($client === null) {
18 1
            $client = $this->getClient();
19
        }
20 2
        return (new Finder($client))
21 2
          ->database($this->getDatabaseName())
22 2
          ->from($this->getResourceName());
23
    }
24
25 5
    public function validateUniqueFields()
26
    {
27 5
        $uniqueFields = $this->getUniqueFields();
28 5
        $uniqueFields = array_diff($uniqueFields, [$this->primaryKey]); //Secondary indexes only
29 5
        if ($this->allUniqueFieldHasIndex($uniqueFields)) {
30 3
            return $this->queryUniqueFields($uniqueFields);
31
        }
32 2
        return $this->scanUniqueFields($uniqueFields);
33
    }
34
35 5
    protected function allUniqueFieldHasIndex($fields)
36
    {
37 5
        $indexes = array_keys($this->indexNames);
38 5
        $existingFields = array_keys(get_object_vars($this->resource));
39 5
        $uniqueFields = array_diff(
40
            array_intersect($fields, $existingFields), //Is unique and value is set
41 5
            [$this->primaryKey] //Collect only secondary indexes
42
        );
43 5
        $indexedFields = array_intersect($uniqueFields, $indexes); //Is unique and value is set and is indexed
44 5
        sort($uniqueFields);
45 5
        sort($indexedFields);
46 5
        return $uniqueFields===$indexedFields;
47
    }
48
49 2
    protected function scanUniqueFields($fields)
50
    {
51 2
        $client = $this->getClient();
52 2
        $query = new Scan($client, $this->generateScan($fields));
53 2
        while ($res = $query->next()) {
54 2
            if ($res !== null && $res['Count'] !== 0) {
55 1
                throw new RestException(
56 1
                    'Unique constraint violation',
57
                    [
58 1
                        'resourceName' => $this->resourceName,
59 1
                        'confilct' => $res,
60 1
                        'mode' => 'scan',
61
                    ]
62
                );
63
            }
64
        }
65 1
    }
66
67 2
    protected function generateScan($fields)
68
    {
69 2
        $filter = $this->generateScanFilter($fields);
70 2
        if (property_exists($this->resource, $this->primaryKey)) {
71 2
            $marshaler = new Marshaler;
72 2
            $filter['FilterExpression'] = '('.$filter['FilterExpression'].') AND #pk<>:pk';
73 2
            $filter['ExpressionAttributeNames']['#pk'] = $this->primaryKey;
74 2
            $filter['ExpressionAttributeValues'][':pk'] = $marshaler->marshalValue(
75 2
                $this->resource->{$this->primaryKey}
76
            );
77
        }
78
        return [
79 2
            'TableName' => $this->database->getDatabaseName() . '-' . $this->resourceName,
80 2
            'FilterExpression' => $filter['FilterExpression'],
81 2
            'ExpressionAttributeNames' => $filter['ExpressionAttributeNames'],
82 2
            'ExpressionAttributeValues' => $filter['ExpressionAttributeValues'],
83
        ];
84
    }
85
86 2
    protected function generateScanFilter($fields)
87
    {
88 2
        $marshaler = new Marshaler;
89
        $ret = [
90 2
            'FilterExpression' => '',
91
            'ExpressionAttributeNames' => [],
92
            'ExpressionAttributeValues' => [],
93
        ];
94 2
        foreach ($fields as $field) {
95 2
            if (property_exists($this->resource, $field)) {
96 2
                $ret['FilterExpression'] .= '#'.$field.'=:'.$field.' OR ';
97 2
                $ret['ExpressionAttributeNames']['#'.$field] = $field;
98 2
                $ret['ExpressionAttributeValues'][':'.$field] = $marshaler->marshalValue(
99 2
                    $this->resource->{$field}
100
                );
101
            }
102
        }
103 2
        $ret['FilterExpression'] = trim($ret['FilterExpression'], ' OR ');
104 2
        return $ret;
105
    }
106
107 3
    protected function queryUniqueFields($fields)
108
    {
109 3
        $queries = $this->generateQueries($fields);
110 3
        $promises = $this->generatePromises($queries);
111 3
        $results = PromiseFunctions\unwrap($promises);
112 3
        foreach ($results as $result) {
113 2
            if ($result !== null && $result['Count'] !== 0) {
114 1
                throw new RestException(
115 1
                    'Unique constraint violation',
116
                    [
117 1
                        'resourceName' => $this->resourceName,
118 1
                        'confilct' => $result,
119 2
                        'mode' => 'query',
120
                    ]
121
                );
122
            }
123
        }
124 2
    }
125
126 3
    protected function generateQueries($fields)
127
    {
128 3
        $queries = [];
129 3
        foreach ($fields as $field) {
130 2
            if (property_exists($this->resource, $field)) {
131 2
                $queries[] = $this->generateQuery($field);
132
            }
133
        }
134 3
        return $queries;
135
    }
136
137 2
    protected function generateQuery($field)
138
    {
139 2
        $marshaler = new Marshaler;
140
        $query = [
141 2
            'TableName' => $this->database->getDatabaseName() . '-' . $this->resourceName,
142 2
            'KeyConditionExpression' => '#'.$field.'=:'.$field,
143 2
            'IndexName' => $this->indexNames[$field],
144 2
            'ExpressionAttributeNames' => ['#'.$field => $field],
145 2
            'ExpressionAttributeValues' => [':'.$field =>  $marshaler->marshalValue(
146 2
                $this->resource->{$field}
147
            )],
148
        ];
149 2
        if (property_exists($this->resource, $this->primaryKey)) {
150 2
            $query['FilterExpression'] = '#pk<>:pk';
151 2
            $query['ExpressionAttributeNames']['#pk'] = $this->primaryKey;
152 2
            $query['ExpressionAttributeValues'][':pk'] = $marshaler->marshalValue(
153 2
                $this->resource->{$this->primaryKey}
154
            );
155
        }
156 2
        return $query;
157
    }
158
159 3
    protected function generatePromises($queries)
160
    {
161 3
        $client = $this->getClient();
162 3
        $promises = [];
163 3
        foreach ($queries as $query) {
164 2
            $promises[] = $this->generatePromise($client, $query);
165
        }
166 3
        return $promises;
167
    }
168
169 2
    protected function generatePromise($client, $request)
170
    {
171 2
        $promise = new Promise(
172 2
            function () use (&$promise, $client, $request) {
173 2
                $query = new Query($client, $request);
174 2
                while ($res = $query->next()) {
175 2
                    if ($res['Count'] !== 0) {
176 1
                        $promise->resolve($res);
177 1
                        return;
178
                    }
179
                }
180 1
                $promise->resolve(null);
181 2
            }
182
        );
183 2
        return $promise;
184
    }
185
186 1
    public function query()
187
    {
188 1
        return null;
189
    }
190
}
191