Skip to content

Commit 290389b

Browse files
committed
references to other files
1 parent 80cdd3c commit 290389b

File tree

6 files changed

+186
-2
lines changed

6 files changed

+186
-2
lines changed

README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ READ [OpenAPI](https://www.openapis.org/) 3.0.x YAML and JSON files and make the
1717

1818
## Usage
1919

20+
### Reading Specification information
21+
2022
Read OpenAPI spec from JSON:
2123

2224
```php
@@ -46,6 +48,41 @@ foreach($openapi->paths as $path => $definition) {
4648
Object properties are exactly like in the [OpenAPI specification](https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#openapi-specification).
4749
You may also access additional properties added by specification extensions.
4850

51+
### Reading Specification Files and Resolving References
52+
53+
In the above we have passed the raw JSON or YAML data to the Reader. In order to be able to resolve
54+
references to external files that may exist in the specification files, we must provide the full context.
55+
56+
```php
57+
use cebe\openapi\Reader;
58+
// an absolute URL or file path is needed to allow resolving internal references
59+
$openapi = Reader::readFromJsonFile('https://www.example.com/api/openapi.json');
60+
$openapi = Reader::readFromYamlFile('https://www.example.com/api/openapi.yaml');
61+
```
62+
63+
If data has been loaded in a different way you can manually resolve references like this by giving a context:
64+
65+
```php
66+
$openapi->resolveReferences(
67+
new \cebe\openapi\ReferenceContext($openapi, 'https://www.example.com/api/openapi.yaml')
68+
);
69+
```
70+
71+
### Validation
72+
73+
The library provides simple validation operations, that check basic OpenAPI spec requirements.
74+
75+
```
76+
// return `true` in case no errors have been found, `false` in case of errors.
77+
$specValid = $openapi->validate();
78+
// after validation getErrors() can be used to retrieve the list of errors found.
79+
$errors = $openapi->getErrors();
80+
```
81+
82+
> **Note:** Validation is done on a very basic level and is not complete. So a failing validation will show some errors,
83+
> but the list of errors given may not be complete. Also a passing validation does not necessarily indicate a completely
84+
> valid spec.
85+
4986

5087
## Completeness
5188

src/Reader.php

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,101 @@
77

88
namespace cebe\openapi;
99

10+
use cebe\openapi\exceptions\TypeErrorException;
11+
use cebe\openapi\exceptions\UnresolvableReferenceException;
1012
use cebe\openapi\spec\OpenApi;
1113
use Symfony\Component\Yaml\Yaml;
1214

1315
/**
14-
*
16+
* Utility class to simplify reading JSON or YAML OpenAPI specs.
1517
*
1618
*/
1719
class Reader
1820
{
21+
/**
22+
* Populate OpenAPI spec object from JSON data.
23+
* @param string $json the JSON string to decode.
24+
* @param string $baseType the base Type to instantiate. This must be an instance of [[SpecObjectInterface]].
25+
* The default is [[OpenApi]] which is the base type of a OpenAPI specification file.
26+
* You may choose a different type if you instantiate objects from sub sections of a specification.
27+
* @return SpecObjectInterface|OpenApi the OpenApi object instance.
28+
* @throws TypeErrorException in case invalid spec data is supplied.
29+
*/
1930
public static function readFromJson(string $json, string $baseType = OpenApi::class): SpecObjectInterface
2031
{
2132
return new $baseType(json_decode($json, true));
2233
}
2334

35+
/**
36+
* Populate OpenAPI spec object from YAML data.
37+
* @param string $yaml the YAML string to decode.
38+
* @param string $baseType the base Type to instantiate. This must be an instance of [[SpecObjectInterface]].
39+
* The default is [[OpenApi]] which is the base type of a OpenAPI specification file.
40+
* You may choose a different type if you instantiate objects from sub sections of a specification.
41+
* @return SpecObjectInterface|OpenApi the OpenApi object instance.
42+
* @throws TypeErrorException in case invalid spec data is supplied.
43+
*/
2444
public static function readFromYaml(string $yaml, string $baseType = OpenApi::class): SpecObjectInterface
2545
{
2646
return new $baseType(Yaml::parse($yaml));
2747
}
48+
49+
/**
50+
* Populate OpenAPI spec object from a JSON file.
51+
* @param string $fileName the file name of the file to be read.
52+
* If `$resolveReferences` is true (the default), this should be an absolute URL, a `file://` URI or
53+
* an absolute path to allow resolving relative path references.
54+
* @param string $baseType the base Type to instantiate. This must be an instance of [[SpecObjectInterface]].
55+
* The default is [[OpenApi]] which is the base type of a OpenAPI specification file.
56+
* You may choose a different type if you instantiate objects from sub sections of a specification.
57+
* @param bool $resolveReferences whether to automatically resolve references in the specification.
58+
* If `true`, all [[Reference]] objects will be replaced with their referenced spec objects by calling
59+
* [[SpecObjectInterface::resolveReferences()]].
60+
* @return SpecObjectInterface|OpenApi the OpenApi object instance.
61+
* @throws TypeErrorException in case invalid spec data is supplied.
62+
* @throws UnresolvableReferenceException in case references could not be resolved.
63+
*/
64+
public static function readFromJsonFile(string $fileName, string $baseType = OpenApi::class, $resolveReferences = true): SpecObjectInterface
65+
{
66+
$spec = static::readFromJson(file_get_contents($fileName), $baseType);
67+
if ($resolveReferences) {
68+
$spec->resolveReferences(new ReferenceContext($spec, static::fileName2Uri($fileName)));
69+
}
70+
return $spec;
71+
}
72+
73+
/**
74+
* Populate OpenAPI spec object from YAML file.
75+
* @param string $fileName the file name of the file to be read.
76+
* If `$resolveReferences` is true (the default), this should be an absolute URL, a `file://` URI or
77+
* an absolute path to allow resolving relative path references.
78+
* @param string $baseType the base Type to instantiate. This must be an instance of [[SpecObjectInterface]].
79+
* The default is [[OpenApi]] which is the base type of a OpenAPI specification file.
80+
* You may choose a different type if you instantiate objects from sub sections of a specification.
81+
* @param bool $resolveReferences whether to automatically resolve references in the specification.
82+
* If `true`, all [[Reference]] objects will be replaced with their referenced spec objects by calling
83+
* [[SpecObjectInterface::resolveReferences()]].
84+
* @return SpecObjectInterface|OpenApi the OpenApi object instance.
85+
* @throws TypeErrorException in case invalid spec data is supplied.
86+
* @throws UnresolvableReferenceException in case references could not be resolved.
87+
*/
88+
public static function readFromYamlFile(string $fileName, string $baseType = OpenApi::class, $resolveReferences = true): SpecObjectInterface
89+
{
90+
$spec = static::readFromYaml(file_get_contents($fileName), $baseType);
91+
if ($resolveReferences) {
92+
$spec->resolveReferences(new ReferenceContext($spec, static::fileName2Uri($fileName)));
93+
}
94+
return $spec;
95+
}
96+
97+
private static function fileName2Uri($fileName)
98+
{
99+
if (strpos($fileName, '://') !== false) {
100+
return $fileName;
101+
}
102+
if (strncmp($fileName, '/', 1) === 0) {
103+
return "file://$fileName";
104+
}
105+
throw new UnresolvableReferenceException('Can not resolve references for a specification given as a relative path.');
106+
}
28107
}

src/spec/Reference.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
use cebe\openapi\exceptions\UnresolvableReferenceException;
1212
use cebe\openapi\Reader;
1313
use cebe\openapi\ReferenceContext;
14-
use cebe\openapi\SpecBaseObject;
1514
use cebe\openapi\SpecObjectInterface;
1615

1716
/**

tests/spec/ReferenceTest.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,4 +122,46 @@ public function testResolveCyclicReferenceInDocument()
122122
$this->assertSame($openapi->components->examples['frog-example'], $refExample);
123123
}
124124

125+
public function testResolveFile()
126+
{
127+
$file = __DIR__ . '/data/reference/base.yaml';
128+
/** @var $openapi OpenApi */
129+
$openapi = Reader::readFromYaml(str_replace('##ABSOLUTEPATH##', 'file://' . dirname($file), file_get_contents($file)));
130+
131+
$result = $openapi->validate();
132+
$this->assertEquals([], $openapi->getErrors());
133+
$this->assertTrue($result);
134+
135+
$this->assertInstanceOf(Reference::class, $petItems = $openapi->components->schemas['Pet']);
136+
$this->assertInstanceOf(Reference::class, $petItems = $openapi->components->schemas['Dog']);
137+
138+
$openapi->resolveReferences(new \cebe\openapi\ReferenceContext($openapi, 'file://' . $file));
139+
140+
$this->assertInstanceOf(Schema::class, $petItems = $openapi->components->schemas['Pet']);
141+
$this->assertInstanceOf(Schema::class, $petItems = $openapi->components->schemas['Dog']);
142+
$this->assertArrayHasKey('id', $openapi->components->schemas['Pet']->properties);
143+
$this->assertArrayHasKey('name', $openapi->components->schemas['Dog']->properties);
144+
}
145+
146+
public function testResolveFileHttp()
147+
{
148+
$file = 'https://raw.githubusercontent.com/cebe/php-openapi/master/tests/spec/data/reference/base.yaml';
149+
/** @var $openapi OpenApi */
150+
$openapi = Reader::readFromYaml(str_replace('##ABSOLUTEPATH##', 'https://' . dirname($file), file_get_contents($file)));
151+
152+
$result = $openapi->validate();
153+
$this->assertEquals([], $openapi->getErrors());
154+
$this->assertTrue($result);
155+
156+
$this->assertInstanceOf(Reference::class, $petItems = $openapi->components->schemas['Pet']);
157+
$this->assertInstanceOf(Reference::class, $petItems = $openapi->components->schemas['Dog']);
158+
159+
$openapi->resolveReferences(new \cebe\openapi\ReferenceContext($openapi, $file));
160+
161+
$this->assertInstanceOf(Schema::class, $petItems = $openapi->components->schemas['Pet']);
162+
$this->assertInstanceOf(Schema::class, $petItems = $openapi->components->schemas['Dog']);
163+
$this->assertArrayHasKey('id', $openapi->components->schemas['Pet']->properties);
164+
$this->assertArrayHasKey('name', $openapi->components->schemas['Dog']->properties);
165+
}
166+
125167
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
openapi: 3.0.0
2+
info:
3+
title: Link Example
4+
version: 1.0.0
5+
components:
6+
schemas:
7+
Pet:
8+
$ref: definitions.yaml#/Pet
9+
Dog:
10+
$ref: ##ABSOLUTEPATH##/definitions.yaml#/Dog
11+
paths:
12+
'/pet':
13+
get:
14+
responses:
15+
200:
16+
description: return a pet
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Pet:
2+
type: object
3+
properties:
4+
id:
5+
type: integer
6+
format: int64
7+
Dog:
8+
type: object
9+
properties:
10+
name:
11+
type: string

0 commit comments

Comments
 (0)