Skip to content

Commit 0a80255

Browse files
committed
Add atomic operations schema
1 parent 2f22140 commit 0a80255

File tree

3 files changed

+361
-2
lines changed

3 files changed

+361
-2
lines changed

src/Extension/Atomic/Atomic.php

Lines changed: 223 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@
1212
use Tobyz\JsonApiServer\Extension\Atomic\Exception\InvalidAtomicOperationException;
1313
use Tobyz\JsonApiServer\Extension\Atomic\Exception\InvalidAtomicOperationsException;
1414
use Tobyz\JsonApiServer\Extension\Extension;
15+
use Tobyz\JsonApiServer\JsonApi;
16+
use Tobyz\JsonApiServer\OpenApi\ProvidesRootSchema;
17+
use Tobyz\JsonApiServer\SchemaContext;
1518

16-
class Atomic extends Extension
19+
class Atomic extends Extension implements ProvidesRootSchema
1720
{
1821
public const URI = 'https://jsonapi.org/ext/atomic';
1922

@@ -156,4 +159,223 @@ private function replaceLids(array &$array, array $lids): array
156159

157160
return $array;
158161
}
162+
163+
public function rootSchema(SchemaContext $context): array
164+
{
165+
$mediaType = JsonApi::MEDIA_TYPE . '; ext=' . static::URI;
166+
167+
return [
168+
'paths' => [
169+
"/$this->path" => [
170+
'post' => [
171+
'summary' => 'Perform a set of atomic operations.',
172+
'requestBody' => [
173+
'required' => true,
174+
'content' => [
175+
$mediaType => [
176+
'schema' => [
177+
'$ref' => '#/components/schemas/jsonApiAtomicOperationsDocument',
178+
],
179+
],
180+
],
181+
],
182+
'responses' => [
183+
'200' => [
184+
'description' => 'Atomic operations executed successfully.',
185+
'content' => [
186+
$mediaType => [
187+
'schema' => [
188+
'$ref' => '#/components/schemas/jsonApiAtomicResultsDocument',
189+
],
190+
],
191+
],
192+
],
193+
],
194+
],
195+
],
196+
],
197+
'components' => [
198+
'schemas' => [
199+
'jsonApiAtomicOperationsDocument' => [
200+
'type' => 'object',
201+
'required' => ['atomic:operations'],
202+
'properties' => [
203+
'atomic:operations' => [
204+
'type' => 'array',
205+
'minItems' => 1,
206+
'items' => [
207+
'$ref' => '#/components/schemas/jsonApiAtomicOperation',
208+
],
209+
],
210+
'jsonapi' => ['type' => 'object'],
211+
'meta' => ['type' => 'object'],
212+
'links' => ['type' => 'object'],
213+
],
214+
'additionalProperties' => true,
215+
],
216+
'jsonApiAtomicOperation' => [
217+
'type' => 'object',
218+
'required' => ['op'],
219+
'properties' => [
220+
'op' => [
221+
'type' => 'string',
222+
'enum' => ['add', 'update', 'remove'],
223+
],
224+
'href' => ['type' => 'string', 'format' => 'uri-reference'],
225+
'ref' => [
226+
'$ref' => '#/components/schemas/jsonApiAtomicRef',
227+
],
228+
'params' => [
229+
'type' => 'object',
230+
'additionalProperties' => [
231+
'oneOf' => [
232+
['type' => 'string'],
233+
['type' => 'number'],
234+
['type' => 'integer'],
235+
['type' => 'boolean'],
236+
[
237+
'type' => 'array',
238+
'items' => ['type' => 'string'],
239+
],
240+
],
241+
],
242+
],
243+
'data' => [
244+
'$ref' => '#/components/schemas/jsonApiAtomicOperationData',
245+
],
246+
'meta' => ['type' => 'object'],
247+
],
248+
'additionalProperties' => true,
249+
],
250+
'jsonApiAtomicRef' => [
251+
'type' => 'object',
252+
'properties' => [
253+
'type' => ['type' => 'string'],
254+
'id' => ['type' => 'string'],
255+
'lid' => ['type' => 'string'],
256+
'relationship' => [
257+
'oneOf' => [
258+
['type' => 'string'],
259+
[
260+
'type' => 'object',
261+
'properties' => [
262+
'type' => ['type' => 'string'],
263+
'id' => ['type' => 'string'],
264+
'lid' => ['type' => 'string'],
265+
'name' => ['type' => 'string'],
266+
],
267+
'additionalProperties' => true,
268+
],
269+
],
270+
],
271+
'meta' => ['type' => 'object'],
272+
],
273+
'additionalProperties' => true,
274+
],
275+
'jsonApiAtomicOperationData' => [
276+
'anyOf' => [
277+
['$ref' => '#/components/schemas/jsonApiAtomicResourceObject'],
278+
['$ref' => '#/components/schemas/jsonApiAtomicRelationshipData'],
279+
],
280+
],
281+
'jsonApiAtomicRelationshipData' => [
282+
'oneOf' => [
283+
['$ref' => '#/components/schemas/jsonApiAtomicResourceIdentifier'],
284+
[
285+
'type' => 'array',
286+
'items' => [
287+
'$ref' => '#/components/schemas/jsonApiAtomicResourceIdentifier',
288+
],
289+
],
290+
['type' => 'null'],
291+
],
292+
],
293+
'jsonApiAtomicResourceObject' => [
294+
'type' => 'object',
295+
'required' => ['type'],
296+
'properties' => [
297+
'type' => ['type' => 'string'],
298+
'id' => ['type' => 'string'],
299+
'lid' => ['type' => 'string'],
300+
'attributes' => ['type' => 'object'],
301+
'relationships' => [
302+
'type' => 'object',
303+
'additionalProperties' => [
304+
'type' => 'object',
305+
'properties' => [
306+
'data' => [
307+
'$ref' => '#/components/schemas/jsonApiAtomicRelationshipData',
308+
],
309+
'meta' => ['type' => 'object'],
310+
'links' => ['type' => 'object'],
311+
],
312+
'additionalProperties' => true,
313+
],
314+
],
315+
'meta' => ['type' => 'object'],
316+
],
317+
'additionalProperties' => true,
318+
],
319+
'jsonApiAtomicResourceIdentifier' => [
320+
'type' => 'object',
321+
'required' => ['type'],
322+
'properties' => [
323+
'type' => ['type' => 'string'],
324+
'id' => ['type' => 'string'],
325+
'lid' => ['type' => 'string'],
326+
'meta' => ['type' => 'object'],
327+
],
328+
'additionalProperties' => true,
329+
],
330+
'jsonApiAtomicResultsDocument' => [
331+
'type' => 'object',
332+
'required' => ['atomic:results'],
333+
'properties' => [
334+
'atomic:results' => [
335+
'type' => 'array',
336+
'items' => [
337+
'oneOf' => [
338+
['type' => 'null'],
339+
[
340+
'$ref' => '#/components/schemas/jsonApiAtomicResultDocument',
341+
],
342+
],
343+
],
344+
],
345+
'jsonapi' => ['type' => 'object'],
346+
'meta' => ['type' => 'object'],
347+
'links' => ['type' => 'object'],
348+
],
349+
'additionalProperties' => true,
350+
],
351+
'jsonApiAtomicResultDocument' => [
352+
'type' => 'object',
353+
'properties' => [
354+
'data' => [
355+
'anyOf' => [
356+
['type' => 'object'],
357+
[
358+
'type' => 'array',
359+
'items' => ['type' => 'object'],
360+
],
361+
['type' => 'null'],
362+
],
363+
],
364+
'errors' => [
365+
'type' => 'array',
366+
'items' => ['type' => 'object'],
367+
],
368+
'meta' => ['type' => 'object'],
369+
'links' => ['type' => 'object'],
370+
'included' => [
371+
'type' => 'array',
372+
'items' => ['type' => 'object'],
373+
],
374+
],
375+
'additionalProperties' => true,
376+
],
377+
],
378+
],
379+
];
380+
}
159381
}

src/OpenApi/OpenApiGenerator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public function generate(JsonApi $api): array
7272

7373
$context = new SchemaContext($api);
7474

75-
foreach ([...$api->collections, ...$api->resources] as $provider) {
75+
foreach ([...$api->extensions, ...$api->collections, ...$api->resources] as $provider) {
7676
if ($provider instanceof ProvidesRootSchema) {
7777
$document = array_replace_recursive($document, $provider->rootSchema($context));
7878
}

tests/specification/AtomicOperationsTest.php

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Tobyz\JsonApiServer\Exception\BadRequestException;
99
use Tobyz\JsonApiServer\Extension\Atomic\Atomic;
1010
use Tobyz\JsonApiServer\JsonApi;
11+
use Tobyz\JsonApiServer\OpenApi\OpenApiGenerator;
1112
use Tobyz\JsonApiServer\Schema\Field\Attribute;
1213
use Tobyz\Tests\JsonApiServer\AbstractTestCase;
1314
use Tobyz\Tests\JsonApiServer\MockResource;
@@ -126,4 +127,140 @@ public function test_atomic_operations_error_prefix()
126127
);
127128
}
128129
}
130+
131+
public function test_atomic_operations_schema()
132+
{
133+
$definition = (new OpenApiGenerator())->generate($this->api);
134+
135+
$this->assertArraySubset(
136+
[
137+
'paths' => [
138+
'/operations' => [
139+
'post' => [
140+
'requestBody' => [
141+
'required' => true,
142+
'content' => [
143+
static::MEDIA_TYPE => [
144+
'schema' => [
145+
'$ref' =>
146+
'#/components/schemas/jsonApiAtomicOperationsDocument',
147+
],
148+
],
149+
],
150+
],
151+
'responses' => [
152+
'200' => [
153+
'content' => [
154+
static::MEDIA_TYPE => [
155+
'schema' => [
156+
'$ref' =>
157+
'#/components/schemas/jsonApiAtomicResultsDocument',
158+
],
159+
],
160+
],
161+
],
162+
],
163+
],
164+
],
165+
],
166+
'components' => [
167+
'schemas' => [
168+
'jsonApiAtomicOperationsDocument' => [
169+
'required' => ['atomic:operations'],
170+
'properties' => [
171+
'atomic:operations' => [
172+
'items' => [
173+
'$ref' =>
174+
'#/components/schemas/jsonApiAtomicOperation',
175+
],
176+
],
177+
],
178+
],
179+
'jsonApiAtomicOperation' => [
180+
'required' => ['op'],
181+
'properties' => [
182+
'op' => [
183+
'enum' => ['add', 'update', 'remove'],
184+
],
185+
'data' => [
186+
'$ref' =>
187+
'#/components/schemas/jsonApiAtomicOperationData',
188+
],
189+
'ref' => [
190+
'$ref' => '#/components/schemas/jsonApiAtomicRef',
191+
],
192+
],
193+
],
194+
'jsonApiAtomicOperationData' => [
195+
'anyOf' => [
196+
['$ref' => '#/components/schemas/jsonApiAtomicResourceObject'],
197+
[
198+
'$ref' =>
199+
'#/components/schemas/jsonApiAtomicRelationshipData',
200+
],
201+
],
202+
],
203+
'jsonApiAtomicRelationshipData' => [
204+
'oneOf' => [
205+
['$ref' => '#/components/schemas/jsonApiAtomicResourceIdentifier'],
206+
['type' => 'array'],
207+
['type' => 'null'],
208+
],
209+
],
210+
'jsonApiAtomicResourceIdentifier' => [
211+
'required' => ['type'],
212+
'properties' => [
213+
'lid' => ['type' => 'string'],
214+
],
215+
],
216+
'jsonApiAtomicResourceObject' => [
217+
'required' => ['type'],
218+
'properties' => [
219+
'lid' => ['type' => 'string'],
220+
'relationships' => ['type' => 'object'],
221+
],
222+
],
223+
'jsonApiAtomicRef' => [
224+
'properties' => [
225+
'relationship' => [
226+
'oneOf' => [
227+
['type' => 'string'],
228+
['type' => 'object'],
229+
],
230+
],
231+
],
232+
],
233+
'jsonApiAtomicResultsDocument' => [
234+
'required' => ['atomic:results'],
235+
'properties' => [
236+
'atomic:results' => [
237+
'items' => [
238+
'oneOf' => [
239+
['type' => 'null'],
240+
[
241+
'$ref' =>
242+
'#/components/schemas/jsonApiAtomicResultDocument',
243+
],
244+
],
245+
],
246+
],
247+
],
248+
],
249+
'jsonApiAtomicResultDocument' => [
250+
'properties' => [
251+
'data' => [
252+
'anyOf' => [
253+
['type' => 'object'],
254+
['type' => 'array'],
255+
['type' => 'null'],
256+
],
257+
],
258+
],
259+
],
260+
],
261+
],
262+
],
263+
$definition,
264+
);
265+
}
129266
}

0 commit comments

Comments
 (0)