Passed
Pull Request — main (#10)
by Artem
02:10
created

Content::history()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 5
rs 10
1
<?php
2
declare(strict_types=1);
3
4
namespace CloudPlayDev\ConfluenceClient\Api;
5
6
use CloudPlayDev\ConfluenceClient\Entity\AbstractContent;
7
use CloudPlayDev\ConfluenceClient\Entity\ContentHistory;
8
use CloudPlayDev\ConfluenceClient\Entity\ContentSearchResult;
9
use CloudPlayDev\ConfluenceClient\Entity\ContentBody;
10
use CloudPlayDev\ConfluenceClient\Entity\Hydratable;
11
use CloudPlayDev\ConfluenceClient\Exception\ConfluencePhpClientException;
12
use Http\Client\Exception as HttpClientException;
13
use JsonException;
14
use Psr\Http\Message\ResponseInterface;
15
use Webmozart\Assert\Assert;
16
use function count;
17
use function in_array;
18
19
/**
20
 * @see https://docs.atlassian.com/atlassian-confluence/REST/6.6.0/#content
21
 */
22
class Content extends AbstractApi
23
{
24
    /**
25
     * ContentType for confluence attachments
26
     */
27
    public const CONTENT_TYPE_ATTACHMENT = 'attachment';
28
29
    /**
30
     * ContentType for confluence comments
31
     */
32
    public const CONTENT_TYPE_COMMENT = 'comment';
33
34
    /**
35
     * ContentType for confluence page content
36
     */
37
    public const CONTENT_TYPE_PAGE = 'page';
38
39
    /**
40
     * ContentType for confluence global content
41
     */
42
    public const CONTENT_TYPE_GLOBAL = 'global';
43
44
    /**
45
     * default value for expand query parameter
46
     */
47
    private const DEFAULT_EXPAND = 'space,version,body.storage,container';
48
    private const DEFAULT_HISTORY_EXPAND = 'content,content.space,content.version,content.body.storage,content.container';
49
50
    /**
51
     * @see https://docs.atlassian.com/atlassian-confluence/REST/6.6.0/#content-getContent
52
     * @throws ConfluencePhpClientException
53
     * @throws HttpClientException
54
     * @throws JsonException
55
     */
56
    public function get(int $contentId, ?int $version = null): ?AbstractContent
57
    {
58
        $fetchUri = ['content', $contentId];
59
        $parameter = ['expand' => self::DEFAULT_EXPAND];
60
61
        if ($version !== null) {
62
            $fetchUri[] = 'version';
63
            $fetchUri[] = $version;
64
65
            $parameter = ['expand' => self::DEFAULT_HISTORY_EXPAND];
66
        }
67
68
        $response = $this->httpGet(self::getRestfulUri(...$fetchUri), $parameter);
69
70
        return $this->hydrateResponse($response, AbstractContent::class);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->hydrateRes...AbstractContent::class) returns the type CloudPlayDev\ConfluenceClient\Entity\Hydratable which includes types incompatible with the type-hinted return CloudPlayDev\ConfluenceC...ty\AbstractContent|null.
Loading history...
71
    }
72
73
74
    /**
75
     * @see https://docs.atlassian.com/atlassian-confluence/REST/6.6.0/#content-getContent
76
     * @param array{title?: string, spaceKey?: string, type?: string, id?: int|string} $searchParameter
77
     * @return ContentSearchResult
78
     * @throws ConfluencePhpClientException
79
     */
80
    public function find(array $searchParameter): ContentSearchResult
81
    {
82
        $allowedSearchParameter = ['title', 'spaceKey', 'type', 'id'];
83
        $queryParameter = array_filter($searchParameter, static function (string $searchKey) use ($allowedSearchParameter) {
84
            return in_array($searchKey, $allowedSearchParameter, true);
85
        }, ARRAY_FILTER_USE_KEY);
86
87
        $queryParameter['expand'] = self::DEFAULT_EXPAND;
88
89
        $searchResponse = $this->httpGet('content', $queryParameter);
90
91
        return $this->hydrateResponse($searchResponse, ContentSearchResult::class);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->hydrateRes...entSearchResult::class) returns the type CloudPlayDev\ConfluenceClient\Entity\Hydratable which includes types incompatible with the type-hinted return CloudPlayDev\ConfluenceC...ity\ContentSearchResult.
Loading history...
92
    }
93
94
    /**
95
     * @see https://docs.atlassian.com/atlassian-confluence/REST/6.6.0/#content-update
96
     * @param AbstractContent $content
97
     * @return AbstractContent
98
     * @throws JsonException
99
     * @throws HttpClientException
100
     */
101
    public function update(AbstractContent $content): AbstractContent
102
    {
103
        $contentId = $content->getId();
104
        Assert::integer($contentId, 'The content can only be changed when it has already been created. To do this, use the "create" method.');
105
106
        $data = [
107
            'id' => $contentId,
108
            'type' => $content->getType(),
109
            'title' => $content->getTitle(),
110
            'space' => ['key' => $content->getSpace()],
111
            'body' => [
112
                'storage' => [
113
                    'value' => $content->getContent(),
114
                    'representation' => 'storage',
115
                ],
116
            ],
117
            'version' => ['number' => $content->getVersion() + 1]
118
        ];
119
120
        return $this->hydrateResponse(
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->hydrateRes...AbstractContent::class) returns the type CloudPlayDev\ConfluenceClient\Entity\Hydratable which includes types incompatible with the type-hinted return CloudPlayDev\ConfluenceC...\Entity\AbstractContent.
Loading history...
121
            $this->httpPut(self::getRestfulUri('content', $contentId), $data),
122
            AbstractContent::class
123
        );
124
    }
125
126
127
    /**
128
     * @see https://docs.atlassian.com/atlassian-confluence/REST/6.6.0/#content-createContent
129
     * @param AbstractContent $content
130
     * @return AbstractContent
131
     * @throws ConfluencePhpClientException
132
     * @throws HttpClientException
133
     * @throws JsonException
134
     */
135
    public function create(AbstractContent $content): AbstractContent
136
    {
137
        Assert::null($content->getId(), 'Only pages not already saved can be created.');
138
139
        $data = [
140
            'type' => $content->getType(),
141
            'title' => $content->getTitle(),
142
            'space' => ['key' => $content->getSpace()],
143
            'body' => [
144
                'storage' => [
145
                    'value' => $content->getContent(),
146
                    'representation' => 'storage',
147
                ],
148
            ],
149
        ];
150
151
        if (count($content->getAncestors()) > 0) {
152
            $ancestorsData = array_map(static function (int $id) {
153
                return ['id' => $id];
154
            }, $content->getAncestors());
155
156
            $data['ancestors'] = $ancestorsData;
157
        }
158
159
        /* attach content to content */
160
        if (null !== $content->getContainerId()) {
161
            $data['container'] = [
162
                'id' => $content->getContainerId(),
163
                'type' => $content->getContainerType(),
164
            ];
165
        }
166
167
        $response = $this->httpPost(self::getRestfulUri('content'), [], $data);
168
        return $this->hydrateResponse($response, AbstractContent::class);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->hydrateRes...AbstractContent::class) returns the type CloudPlayDev\ConfluenceClient\Entity\Hydratable which includes types incompatible with the type-hinted return CloudPlayDev\ConfluenceC...\Entity\AbstractContent.
Loading history...
169
170
    }
171
172
    /**
173
     * @see https://docs.atlassian.com/atlassian-confluence/REST/6.6.0/#content-delete
174
     * @param AbstractContent $content
175
     * @return ResponseInterface
176
     */
177
    public function delete(AbstractContent $content): ResponseInterface
178
    {
179
        $contentId = $content->getId();
180
        Assert::integer($contentId, 'The content must already be saved to be deleted.');
181
        return $this->httpDelete(self::getRestfulUri('content', $contentId));
182
    }
183
184
    /**
185
     * @param AbstractContent $content
186
     * @param string|null $contentType
187
     * @return ContentSearchResult
188
     * @throws HttpClientException
189
     * @throws JsonException
190
     */
191
    public function children(AbstractContent $content, ?string $contentType = null): ContentSearchResult
192
    {
193
        return $this->hydrateResponse(
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->hydrateRes...entSearchResult::class) returns the type CloudPlayDev\ConfluenceClient\Entity\Hydratable which includes types incompatible with the type-hinted return CloudPlayDev\ConfluenceC...ity\ContentSearchResult.
Loading history...
194
            $this->httpGet(self::getRestfulUri('content', $content->getId(), 'child', $contentType), ['expand' => self::DEFAULT_EXPAND]),
195
            ContentSearchResult::class
196
        );
197
    }
198
199
    /**
200
     * @param AbstractContent $content
201
     * @param string|null $contentType
202
     * @return ContentSearchResult
203
     * @throws HttpClientException
204
     * @throws JsonException
205
     */
206
    public function descendants(AbstractContent $content, ?string $contentType = null): ContentSearchResult
207
    {
208
        return $this->hydrateResponse(
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->hydrateRes...entSearchResult::class) returns the type CloudPlayDev\ConfluenceClient\Entity\Hydratable which includes types incompatible with the type-hinted return CloudPlayDev\ConfluenceC...ity\ContentSearchResult.
Loading history...
209
            $this->httpGet(self::getRestfulUri('content', $content->getId(), 'descendant', $contentType)),
210
            ContentSearchResult::class
211
        );
212
    }
213
214
    /**
215
     * @see https://docs.atlassian.com/atlassian-confluence/REST/6.6.0/#contentbody/convert/{to}-convert
216
     * @param ContentBody $convertBody
217
     * @param string $to
218
     * @param AbstractContent|null $abstractContent
219
     * @return ContentBody
220
     */
221
    public function convert(ContentBody $convertBody, string $to = 'view', ?AbstractContent $abstractContent = null): ContentBody
222
    {
223
        $queryParameter = [];
224
225
        if ($abstractContent && $abstractContent->getId() !== null) {
226
            $queryParameter['pageIdContext'] = $abstractContent->getId();
227
        }
228
229
        if ($abstractContent && $abstractContent->getSpace() !== null) {
230
            $queryParameter['spaceKeyContext'] = $abstractContent->getSpace();
231
        }
232
233
        Assert::true(ContentBody::isSupported($to), 'This conversion target is not supported.');
234
235
        $data = [
236
            'representation' => $convertBody->getRepresentation(),
237
            'value' => $convertBody->getValue()
238
        ];
239
240
        return $this->hydrateResponse(
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->hydrateRes...ity\ContentBody::class) returns the type CloudPlayDev\ConfluenceClient\Entity\Hydratable which includes types incompatible with the type-hinted return CloudPlayDev\ConfluenceClient\Entity\ContentBody.
Loading history...
241
            $this->httpPost(
242
                self::getRestfulUri('contentbody', 'convert', $to),
243
                $queryParameter,
244
                $data
245
            ),
246
            ContentBody::class
247
        );
248
249
    }
250
251
    /**
252
     * Returns the history of a particular piece of content, sorted by version number in descending order.
253
     *
254
     * @param int $contentId
255
     * @return Hydratable
256
     * @throws HttpClientException
257
     */
258
    public function history(int $contentId): Hydratable
259
    {
260
        return $this->hydrateResponse(
261
            $this->httpGet(self::getRestfulUri('content', $contentId, 'history')),
262
            ContentHistory::class
263
        );
264
    }
265
}
266