Skip to content

Commit a72dda0

Browse files
committed
implment Operation, MediaType, Encoding, Parameter
1 parent 96e6a94 commit a72dda0

File tree

11 files changed

+304
-16
lines changed

11 files changed

+304
-16
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,10 @@ This library is currently work in progress, the following list tracks completene
6565
- [x] Path Item Object
6666
- [ ] Operation Object
6767
- [x] External Documentation Object
68-
- [ ] Parameter Object
68+
- [x] Parameter Object
6969
- [x] Request Body Object
70-
- [ ] Media Type Object
71-
- [ ] Encoding Object
70+
- [x] Media Type Object
71+
- [x] Encoding Object
7272
- [ ] Responses Object
7373
- [x] Response Object
7474
- [ ] Callback Object

src/SpecBaseObject.php

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
namespace cebe\openapi;
99

1010
use cebe\openapi\exceptions\ReadonlyPropertyException;
11+
use cebe\openapi\exceptions\TypeErrorException;
1112
use cebe\openapi\exceptions\UnknownPropertyException;
1213
use cebe\openapi\spec\Type;
1314

@@ -72,14 +73,14 @@ public function __construct(array $data)
7273
$this->_properties[$property][] = $item;
7374
} else {
7475
// TODO implement reference objects
75-
$this->_properties[$property][] = new $type[0]($item);
76+
$this->_properties[$property][] = $this->instantiate($type[0], $item);
7677
}
7778
}
7879
break;
7980
case 2:
8081
// map
8182
if ($type[0] !== Type::STRING) {
82-
throw new \Exception('Invalid map key type: ' . $type[0]);
83+
throw new TypeErrorException('Invalid map key type: ' . $type[0]);
8384
}
8485
$this->_properties[$property] = [];
8586
foreach ($data[$property] as $key => $item) {
@@ -92,13 +93,13 @@ public function __construct(array $data)
9293
$this->_properties[$property][$key] = $item;
9394
} else {
9495
// TODO implement reference objects
95-
$this->_properties[$property][$key] = new $type[1]($item);
96+
$this->_properties[$property][$key] = $this->instantiate($type[1], $item);
9697
}
9798
}
9899
break;
99100
}
100101
} else {
101-
$this->_properties[$property] = new $type($data[$property]);
102+
$this->_properties[$property] = $this->instantiate($type, $data[$property]);
102103
}
103104
unset($data[$property]);
104105
}
@@ -107,6 +108,19 @@ public function __construct(array $data)
107108
}
108109
}
109110

111+
private function instantiate($type, $data)
112+
{
113+
try {
114+
return new $type($data);
115+
} catch (\TypeError $e) {
116+
throw new TypeErrorException(
117+
"Unable to instantiate {$type} Object with data '" . print_r($data, true) . "'",
118+
$e->getCode(),
119+
$e
120+
);
121+
}
122+
}
123+
110124
/**
111125
* Validate object data according to OpenAPI spec.
112126
* @return bool whether the loaded data is valid according to OpenAPI spec
@@ -192,7 +206,12 @@ public function __get($name)
192206
return $this->_properties[$name];
193207
}
194208
if (isset(static::attributes()[$name])) {
195-
return is_array(static::attributes()[$name]) ? [] : null;
209+
if (is_array(static::attributes()[$name])) {
210+
return [];
211+
} elseif (static::attributes()[$name] === Type::BOOLEAN) {
212+
return false;
213+
}
214+
return null;
196215
}
197216
throw new UnknownPropertyException('Getting unknown property: ' . \get_class($this) . '::' . $name);
198217
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
/**
4+
* @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors
5+
* @license https://github.com/cebe/php-openapi/blob/master/LICENSE
6+
*/
7+
8+
namespace cebe\openapi\exceptions;
9+
10+
/**
11+
* This exception is thrown if the input data from OpenAPI spec
12+
* provides data in another type that is expected.
13+
*
14+
*/
15+
class TypeErrorException extends \Exception
16+
{
17+
}

src/spec/Encoding.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
/**
4+
* @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors
5+
* @license https://github.com/cebe/php-openapi/blob/master/LICENSE
6+
*/
7+
8+
namespace cebe\openapi\spec;
9+
10+
use cebe\openapi\SpecBaseObject;
11+
12+
/**
13+
* A single encoding definition applied to a single schema property.
14+
*
15+
* @property-read string $contentType
16+
* @property-read Header[]|Reference[] $headers
17+
* @property-read string $style
18+
* @property-read boolean $explode
19+
* @property-read boolean $allowReserved
20+
*/
21+
class Encoding extends SpecBaseObject
22+
{
23+
24+
/**
25+
* @return array array of attributes available in this object.
26+
*/
27+
protected function attributes(): array
28+
{
29+
return [
30+
'contentType' => Type::STRING,
31+
'headers' => [Type::STRING, Header::class],
32+
'style' => Type::STRING,
33+
'explode' => Type::BOOLEAN,
34+
'allowReserved' => Type::BOOLEAN,
35+
];
36+
}
37+
38+
/**
39+
* Perform validation on this object, check data against OpenAPI Specification rules.
40+
*/
41+
protected function performValidation()
42+
{
43+
}
44+
}

src/spec/MediaType.php

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,35 @@
77

88
namespace cebe\openapi\spec;
99

10+
use cebe\openapi\SpecBaseObject;
11+
1012
/**
1113
* Each Media Type Object provides schema and examples for the media type identified by its key.
1214
*
15+
* @property-read Schema|Reference|null $schema
16+
* @property-read mixed $example
17+
* @property-read Example[]|Reference[] $examples
18+
* @property-read Encoding[] $encoding
1319
*/
14-
class MediaType
20+
class MediaType extends SpecBaseObject
1521
{
22+
/**
23+
* @return array array of attributes available in this object.
24+
*/
25+
protected function attributes(): array
26+
{
27+
return [
28+
'schema' => Schema::class, // TODO support Reference
29+
'example' => Type::ANY,
30+
'examples' => [Type::STRING, Example::class], // TODO support Reference
31+
'encoding' => [Type::STRING, Encoding::class],
32+
];
33+
}
34+
35+
/**
36+
* Perform validation on this object, check data against OpenAPI Specification rules.
37+
*/
38+
protected function performValidation()
39+
{
40+
}
1641
}

src/spec/Operation.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class Operation extends SpecBaseObject
3636
protected function attributes(): array
3737
{
3838
return [
39-
'tags' => [Tag::class],
39+
'tags' => [Type::STRING],
4040
'summary' => Type::STRING,
4141
'description' => Type::STRING,
4242
'externalDocs' => ExternalDocumentation::class,

src/spec/Parameter.php

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,14 @@
2121
* @property-read bool $deprecated
2222
* @property-read bool $allowEmptyValue
2323
*
24-
* TODO implement more
24+
* @property-read string $style
25+
* @property-read boolean $explode
26+
* @property-read boolean $allowReserved
27+
* @property-read Schema|Reference|null $schema
28+
* @property-read mixed $example
29+
* @property-read Example[] $examples
30+
*
31+
* @property-read MediaType[] $content
2532
*/
2633
class Parameter extends SpecBaseObject
2734
{
@@ -37,6 +44,15 @@ protected function attributes(): array
3744
'required' => Type::BOOLEAN,
3845
'deprecated' => Type::BOOLEAN,
3946
'allowEmptyValue' => Type::BOOLEAN,
47+
48+
'style' => Type::STRING,
49+
'explode' => Type::BOOLEAN,
50+
'allowReserved' => Type::BOOLEAN,
51+
'schema' => Schema::class,
52+
'example' => Type::ANY,
53+
'examples' => [Type::STRING, Example::class],
54+
55+
'content' => [Type::STRING, MediaType::class],
4056
];
4157
}
4258

@@ -47,6 +63,15 @@ protected function attributes(): array
4763
*/
4864
protected function performValidation()
4965
{
50-
// TODO: Implement performValidation() method.
66+
$this->requireProperties(['name', 'in']);
67+
if ($this->in === 'path') {
68+
$this->requireProperties(['required']);
69+
if (!$this->required) {
70+
$this->addError("Parameter 'required' must be true for 'in': 'path'.");
71+
}
72+
}
73+
if (!empty($this->content) && !empty($this->schema)) {
74+
$this->addError("A parameter MUST contain either a schema property, or a content property, but not both. ");
75+
}
5176
}
5277
}

src/spec/Responses.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public function __construct(array $data)
2828
{
2929
foreach ($data as $statusCode => $response) {
3030
if ((is_numeric($statusCode) && $statusCode >= 100 && $statusCode <= 600) || $statusCode === 'default') {
31-
$this->_responses[$statusCode] = $response;
31+
$this->_responses[$statusCode] = new Response($response);
3232
} else {
3333
$this->_errors[] = "$statusCode is not a valid HTTP status code.";
3434
}

tests/spec/OperationTest.php

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
use cebe\openapi\Reader;
4+
use cebe\openapi\spec\Operation;
5+
6+
/**
7+
* @covers \cebe\openapi\spec\Operation
8+
*/
9+
class OperationTest extends \PHPUnit\Framework\TestCase
10+
{
11+
public function testRead()
12+
{
13+
/** @var $operation Operation */
14+
$operation = Reader::readFromYaml(<<<'YAML'
15+
tags:
16+
- pet
17+
summary: Updates a pet in the store with form data
18+
operationId: updatePetWithForm
19+
parameters:
20+
- name: petId
21+
in: path
22+
description: ID of pet that needs to be updated
23+
required: true
24+
schema:
25+
type: string
26+
requestBody:
27+
content:
28+
'application/x-www-form-urlencoded':
29+
schema:
30+
properties:
31+
name:
32+
description: Updated name of the pet
33+
type: string
34+
status:
35+
description: Updated status of the pet
36+
type: string
37+
required:
38+
- status
39+
responses:
40+
'200':
41+
description: Pet updated.
42+
content:
43+
'application/json': {}
44+
'application/xml': {}
45+
'405':
46+
description: Method Not Allowed
47+
content:
48+
'application/json': {}
49+
'application/xml': {}
50+
security:
51+
- petstore_auth:
52+
- write:pets
53+
- read:pets
54+
YAML
55+
, Operation::class);
56+
57+
$result = $operation->validate();
58+
$this->assertEquals([], $operation->getErrors());
59+
$this->assertTrue($result);
60+
61+
$this->assertCount(1, $operation->tags);
62+
$this->assertEquals(['pet'], $operation->tags);
63+
64+
$this->assertEquals('Updates a pet in the store with form data', $operation->summary);
65+
$this->assertEquals('updatePetWithForm', $operation->operationId);
66+
67+
$this->assertCount(1, $operation->parameters);
68+
$this->assertInstanceOf(\cebe\openapi\spec\Parameter::class, $operation->parameters[0]);
69+
$this->assertEquals('petId', $operation->parameters[0]->name);
70+
71+
$this->assertInstanceOf(\cebe\openapi\spec\RequestBody::class, $operation->requestBody);
72+
$this->assertCount(1, $operation->requestBody->content);
73+
$this->assertArrayHasKey('application/x-www-form-urlencoded', $operation->requestBody->content);
74+
75+
$this->assertInstanceOf(\cebe\openapi\spec\Responses::class, $operation->responses);
76+
77+
$this->assertCount(1, $operation->security);
78+
$this->assertInstanceOf(\cebe\openapi\spec\SecurityRequirement::class, $operation->security[0]);
79+
$this->assertCount(2, $operation->security[0]->petstore_auth);
80+
$this->assertEquals(['write:pets', 'read:pets'], $operation->security[0]->petstore_auth);
81+
}
82+
}

0 commit comments

Comments
 (0)