Test Failed
Branch feature/dynamodb (3e8935)
by Csaba
02:57
created

Resource::throwAwsPostError()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
c 0
b 0
f 0
rs 9.4285
cc 2
eloc 8
nc 2
nop 1
1
<?php
2
namespace Fathomminds\Rest\Database\DynamoDb;
3
4
use Aws\Sdk;
5
use Aws\DynamoDb\DynamoDbClient as Client;
6
use Aws\DynamoDb\Marshaler;
7
use Fathomminds\Rest\Exceptions\RestException;
8
use Fathomminds\Rest\Contracts\IResource;
9
use Fathomminds\Rest\Helpers\Uuid;
10
use Aws\DynamoDb\Exception\DynamoDbException;
11
12
class Resource implements IResource
13
{
14
    protected $client;
15
    protected $databaseName;
16
    protected $collection;
17
    protected $fullTableName;
18
    protected $resourceName;
19
    protected $primaryKey;
20
    protected $marshaler;
21
22
    public function __construct($resourceName, $primaryKey, Client $client = null, $databaseName = null)
23
    {
24
        $this->resourceName = $resourceName;
25
        $this->primaryKey = $primaryKey;
26
        $this->client = $client === null ? $this->createClient() : $client;
27
        $this->databaseName = $databaseName === null ? $this->getFullDatabaseName() : $databaseName;
28
        $this->fullTableName = $this->databaseName . '-' . $this->resourceName;
29
        $this->marshaler = new Marshaler;
30
    }
31
32
    private function createClient()
33
    {
34
        $sdk = new Sdk([
35
            'region'   => getenv('AWS_SDK_REGION'),
36
            'version'  => getenv('AWS_SDK_VERSION'),
37
            'http' => [
38
                'verify' => getenv('AWS_SDK_HTTP_VERIFY') === 'false' ? false : getenv('AWS_SDK_HTTP_VERIFY'),
39
            ]
40
        ]);
41
        return $sdk->createDynamoDb();
42
    }
43
44
    private function getFullDatabaseName()
45
    {
46
        return getenv('AWS_DYNAMODB_NAMESPACE') . '-' . getenv('AWS_DYNAMODB_DATABASE');
47
    }
48
49
    public function get($resourceId = null)
50
    {
51
        if ($resourceId !== null) {
52
            return $this->getOne($resourceId);
53
        }
54
        return $this->getAll();
55
    }
56
57
    protected function getOne($resourceId)
58
    {
59
        $query = $this->createGetQuery($resourceId);
60
        $res = $this->client->getItem($query);
61
        return $this->unmarshalItem($res['Item']);
62
    }
63
64
    protected function createGetQuery($resourceId)
65
    {
66
        $query = [
67
            'TableName' => $this->fullTableName,
68
            'Key' => $this->marshalItem([$this->primaryKey => $resourceId]),
69
        ];
70
        return $query;
71
    }
72
73
    protected function getAll()
74
    {
75
        $query = $this->createScanQuery();
76
        $res = $this->client->scan($query);
77
        return $this->unmarshalBatch($res['Items']);
78
    }
79
80
    protected function createScanQuery()
81
    {
82
        $query = [
83
            'TableName' => $this->fullTableName,
84
        ];
85
        return $query;
86
    }
87
88
    protected function throwAwsPostError($ex)
89
    {
90
        switch ($ex->getAwsErrorCode()) {
91
            case 'ConditionalCheckFailedException':
92
                throw new RestException(
93
                    'Primary key collision',
94
                    ['exception' => $ex]
95
                );
96
                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
97
        }
98
        throw new RestException($ex->getMessage(), ['exception' => $ex]);
99
    }
100
101
    public function post($newResource)
102
    {
103
        try {
104
            $query = $this->createPostQuery($newResource);
105
            $this->client->putItem($query);
106
            return $newResource;
107
        } catch (DynamoDbException $ex) {
108
            $this->throwAwsPostError($ex);
109
        } catch (\Exception $ex) {
110
            throw new RestException($ex->getMessage(), ['exception' => $ex]);
111
        }
112
    }
113
114
    protected function createPostQuery($newResource)
115
    {
116
        $query = [
117
            'TableName' => $this->fullTableName,
118
            'Item' => $this->marshalItem($newResource),
119
            'ConditionExpression' => 'attribute_not_exists(#pk)',
120
            "ExpressionAttributeNames" => ["#pk" => $this->primaryKey],
121
        ];
122
        return $query;
123
    }
124
125
    protected function throwAwsPutError($ex)
126
    {
127
        switch ($ex->getAwsErrorCode()) {
128
            case 'ConditionalCheckFailedException':
129
                throw new RestException(
130
                    'Resource does not exist',
131
                    ['exception' => $ex]
132
                );
133
                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
134
        }
135
        throw new RestException($ex->getMessage(), ['exception' => $ex]);
136
    }
137
138
    public function put($resourceId, $newResource)
139
    {
140
        try {
141
            $newResource->{$this->primaryKey} = $resourceId;
142
            $query = $this->createPutQuery($newResource);
143
            $res = $this->client->putItem($query);
144
            return $newResource;
145
        } catch (DynamoDbException $ex) {
146
            $this->throwAwsPutError($ex);
147
        } catch (\Exception $ex) {
148
            throw new RestException($ex->getMessage(), ['result'=>empty($res)?null:$res]);
149
        }
150
    }
151
152
    protected function createPutQuery($newResource)
153
    {
154
        $query = [
155
            'TableName' => $this->fullTableName,
156
            'Item' => $this->marshalItem($newResource),
157
            'ConditionExpression' => 'attribute_exists(#pk)',
158
            "ExpressionAttributeNames" => ["#pk" => $this->primaryKey],
159
        ];
160
        return $query;
161
    }
162
163
    public function delete($resourceId)
164
    {
165
        try {
166
            $query = $this->createDeleteQuery($resourceId);
167
            $res = $this->client->deleteItem($query);
168
            return $resourceId;
169
        } catch (\Exception $ex) {
170
            throw new RestException($ex->getMessage(), ['result'=>empty($res)?null:$res]);
171
        }
172
    }
173
174
    protected function createDeleteQuery($resourceId)
175
    {
176
        $query = [
177
            'TableName' => $this->fullTableName,
178
            'Key' => $this->marshalItem([$this->primaryKey => $resourceId]),
179
        ];
180
        return $query;
181
    }
182
183
    protected function marshalItem($resource)
184
    {
185
        return $this->marshaler->marshalItem($resource);
186
    }
187
188
    protected function unmarshalItem($item, $mapAsObject = true)
189
    {
190
        if ($item === null) {
191
            return new \StdClass;
192
        }
193
        return $this->marshaler->unmarshalItem($item, $mapAsObject);
194
    }
195
196
    protected function unmarshalBatch($itemList, $mapAsObject = true)
197
    {
198
        if ($itemList === null) {
199
            return [];
200
        }
201
        $list = [];
202
        foreach ($itemList as $item) {
203
            $list[] = $this->unmarshalItem($item, $mapAsObject);
204
        }
205
        return $list;
206
    }
207
}
208