Skip to content
4 changes: 2 additions & 2 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ jobs:

- name: Require newer phpunit/phpunit version
run: "composer require phpunit/phpunit '^11.4' --dev --no-interaction --ansi --no-install"
if: matrix.php == '8.3'
if: matrix.php >= '8.3'

- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v2"
Expand All @@ -109,7 +109,7 @@ jobs:

- name: PHPUnit tests
run: make test-debug
if: matrix.php == '8.3'
if: matrix.php >= '8.3'

- name: Code coverage
run: make coverage
29 changes: 25 additions & 4 deletions src/json/JsonReference.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ final class JsonReference implements JsonSerializable
* @var JsonPointer
*/
private $_pointer;
/**
* @var string|null
*/
private $_summary;
/**
* @var string|null
*/
private $_description;

/**
* Create a JSON Reference instance from a JSON document.
Expand All @@ -42,7 +50,11 @@ public static function createFromJson(string $json): JsonReference
if (!isset($refObject['$ref'])) {
throw new MalformedJsonReferenceObjectException('JSON Reference Object must contain the "$ref" member.');
}
return static::createFromReference($refObject['$ref']);
return static::createFromReference(
$refObject['$ref'],
isset($refObject['summary']) ? $refObject['summary'] : null,
isset($refObject['description']) ? $refObject['description'] : null
);
}

/**
Expand All @@ -66,9 +78,14 @@ public static function createFromUri(string $uri, ?JsonPointer $jsonPointer = nu
* @return JsonReference
* @throws InvalidJsonPointerSyntaxException if an invalid JSON pointer string is passed as part of the fragment section.
*/
public static function createFromReference(string $referenceURI): JsonReference
{
public static function createFromReference(
string $referenceURI,
?string $summary = null,
?string $description = null
): JsonReference {
$jsonReference = new JsonReference();
$jsonReference->_summary = $summary;
$jsonReference->_description = $description;
if (strpos($referenceURI, '#') !== false) {
list($uri, $fragment) = explode('#', $referenceURI, 2);
$jsonReference->_uri = $uri;
Expand Down Expand Up @@ -129,6 +146,10 @@ public function getReference(): string
#[\ReturnTypeWillChange]
public function jsonSerialize() //: mixed
{
return (object)['$ref' => $this->getReference()];
return (object) array_filter([
'$ref' => $this->getReference(),
'summary' => $this->_summary,
'description' => $this->_description,
]);
}
}
56 changes: 50 additions & 6 deletions src/spec/Reference.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
namespace cebe\openapi\spec;

use cebe\openapi\DocumentContextInterface;
use cebe\openapi\exceptions\IOException;
use cebe\openapi\exceptions\TypeErrorException;
use cebe\openapi\exceptions\UnresolvableReferenceException;
use cebe\openapi\json\InvalidJsonPointerSyntaxException;
Expand All @@ -17,7 +16,6 @@
use cebe\openapi\json\NonexistentJsonPointerReferenceException;
use cebe\openapi\ReferenceContext;
use cebe\openapi\SpecObjectInterface;
use Symfony\Component\Yaml\Yaml;

/**
* Reference Object
Expand All @@ -37,6 +35,14 @@ class Reference implements SpecObjectInterface, DocumentContextInterface
* @var string
*/
private $_ref;
/**
* @var string|null
*/
private $_summary;
/**
* @var string|null
*/
private $_description;
/**
* @var JsonReference|null
*/
Expand Down Expand Up @@ -81,15 +87,33 @@ public function __construct(array $data, ?string $to = null)
'Unable to instantiate Reference Object, value of $ref must be a string.'
);
}
if (isset($data['summary']) && !is_string($data['summary'])) {
throw new TypeErrorException(
'Unable to instantiate Reference Object, value of summary must be a string.'
);
}
if (isset($data['description']) && !is_string($data['description'])) {
throw new TypeErrorException(
'Unable to instantiate Reference Object, value of description must be a string.'
);
}

$this->_to = $to;
$this->_ref = $data['$ref'];
$this->_summary = $data['summary'] ?? null;
$this->_description = $data['description'] ?? null;
try {
$this->_jsonReference = JsonReference::createFromReference($this->_ref);
$this->_jsonReference = JsonReference::createFromReference(
$this->_ref,
$this->_summary,
$this->_description
);
} catch (InvalidJsonPointerSyntaxException $e) {
$this->_errors[] = 'Reference: value of $ref is not a valid JSON pointer: ' . $e->getMessage();
}
if (count($data) !== 1) {
$this->_errors[] = 'Reference: additional properties are given. Only $ref should be set in a Reference Object.';

if (!empty(array_diff(array_keys($data), ['$ref', 'summary', 'description']))) {
$this->_errors[] = 'Reference: additional properties are given. Only $ref, summary and description should be set in a Reference Object.';
}
}

Expand All @@ -99,7 +123,11 @@ public function __construct(array $data, ?string $to = null)
*/
public function getSerializableData()
{
return (object) ['$ref' => $this->_ref];
return (object) array_filter([
'$ref' => $this->_ref,
'summary' => $this->_summary,
'description' => $this->_description,
]);
}

/**
Expand Down Expand Up @@ -135,6 +163,22 @@ public function getReference()
return $this->_ref;
}

/**
* @return string|null
*/
public function getSummary()
{
return $this->_summary;
}

/**
* @return string|null
*/
public function getDescription()
{
return $this->_description;
}

/**
* @return JsonReference the JSON Reference.
*/
Expand Down
45 changes: 45 additions & 0 deletions tests/spec/ReferenceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -657,4 +657,49 @@ public function testResolveRelativePathAll()
}
}

public function testReferenceExtraFields()
{
/** @var $openapi OpenApi */
$openapi = Reader::readFromYaml(<<<'YAML'
openapi: 3.1.0
info:
title: test api
version: 1.0.0
components:
schemas:
Pet:
type: object
properties:
id:
type: integer
paths:
'/pet':
get:
responses:
200:
description: return a pet
content:
'application/json':
schema:
$ref: "#/components/schemas/Pet"
summary: 'Pet Schema'
description: 'This is a pet schema'
YAML
, OpenApi::class);

$result = $openapi->validate();
$this->assertEquals([], $openapi->getErrors());
$this->assertTrue($result);

/** @var $petResponse Response */
$petResponse = $openapi->paths->getPath('/pet')->get->responses['200'];
$this->assertInstanceOf(Reference::class, $ref = $petResponse->content['application/json']->schema);
$this->assertEquals('Pet Schema', $ref->getSummary());
$this->assertEquals('This is a pet schema', $ref->getDescription());

$openapi->resolveReferences(new \cebe\openapi\ReferenceContext($openapi, 'file:///tmp/openapi.yaml'));

$this->assertInstanceOf(Schema::class, $refSchema = $petResponse->content['application/json']->schema);
$this->assertSame($openapi->components->schemas['Pet'], $refSchema);
}
}
Loading