Skip to content

Commit

Permalink
Merge pull request goldspecdigital#18 from goldspecdigital/develop
Browse files Browse the repository at this point in the history
v2.0.0
  • Loading branch information
matthew-inamdar authored May 19, 2019
2 parents 18f4275 + a5d04a5 commit f5ef7d7
Show file tree
Hide file tree
Showing 68 changed files with 4,827 additions and 607 deletions.
149 changes: 131 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,14 @@

## Introduction

An object oriented approach to generating OpenAPI docs, implemented in PHP.
An object oriented approach to generating OpenAPI specs, implemented in PHP.

You can build up your API spec using PHP classes, and then export the spec to
JSON (or YAML with the help of another package).
You can build up your API spec using immutable PHP classes, and then export the
spec to JSON (or YAML with the help of another package).

This package is **dependency free** and makes heavy use of **PHP 7 features**,
mainly being **type hints** and enabling **strict types**. This should make your
life a lot easier when working with a good IDE that can use this information.

## Installing

Expand All @@ -59,7 +63,7 @@ See the code sample below for the most basic usage:

```php
use GoldSpecDigital\ObjectOrientedOAS\Objects\{
Info, MediaType, Operation, PathItem, Paths, Response, Schema, Tag
Info, MediaType, Operation, PathItem, Response, Schema, Tag
};
use GoldSpecDigital\ObjectOrientedOAS\OpenApi;

Expand All @@ -77,17 +81,19 @@ $info = Info::create()
// Create the user schema.
$userSchema = Schema::object()
->properties(
Schema::string('id')->format(Schema::UUID),
Schema::string('id')->format(Schema::FORMAT_UUID),
Schema::string('name'),
Schema::integer('age')->example(23),
Schema::string('created_at')->format(Schema::DATE_TIME)
Schema::string('created_at')->format(Schema::FORMAT_DATE_TIME)
);

// Create the user response.
$userResponse = Response::create()
->statusCode(200)
->description('OK')
->content(MediaType::json($userSchema));
->content(
MediaType::json()->schema($userSchema)
);

// Create the operation for the route (i.e. GET, POST, etc.).
$showUser = Operation::get()
Expand All @@ -97,19 +103,15 @@ $showUser = Operation::get()
->operationId('users.show');

// Define the /users path along with the supported operations.
$userPaths = PathItem::create()
$usersPath = PathItem::create()
->route('/users')
->operations($showUser);

// Define all of the paths supported by the API.
$paths = Paths::create()
->pathItems($userPaths);

// Create the main OpenAPI object composed off everything created above.
$openApi = OpenApi::create()
->version(OpenApi::VERSION_3_0_1)
->openapi(OpenApi::OPENAPI_3_0_2)
->info($info)
->paths($paths)
->paths($usersPath)
->tags($usersTag);

header('Content-Type: application/json');
Expand All @@ -126,7 +128,7 @@ separate files easily will be a massive help.

```yaml
---
openapi: 3.0.1
openapi: 3.0.2
info:
title: API Specification
description: For using the Example App API
Expand Down Expand Up @@ -182,13 +184,124 @@ $yaml = Yaml::dump($array);

## Guidance

If you want to learn more about the OpenAPI schema, or if you would like a quick
reference, then check out the [OpenAPI Map](https://openapi-map.apihandyman.io/?version=3.0)
project created by [Arnaud Lauret](http://apihandyman.io/).
If you want to learn more about the OpenAPI schema, then have a look at the
official [OpenAPI Specification](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md).

Alternatively, if you would like a quick reference, then check out the
[OpenAPI Map](https://openapi-map.apihandyman.io/?version=3.0) project created
by [Arnaud Lauret](http://apihandyman.io/).

You can use this interactive tool to figure out what objects go where and how
they relate to one another.

## Usage

### Setting and unsetting properties

Each object has setter methods for it's supported properties. Most of these
methods allow `null` values which will need to be explicitly passed (see the
next example for how to unset using variadic setter methods). This will have the
effect of unsetting the property:

```php
$info = Info::create()
->title('Example API');

$openApi = OpenAPI::create()
->info($info);
// $openApi->toJson() -> '{"info": {"title": "Example API"}}'

$openApi = $openApi->info(null);
// $openApi->toJson() -> '{}'
```

For variadic setter methods, if you call the method and don't supply any
parameters, then this will have the effect of unsetting the property:

```php
$path = PathItem::create()
->route('/users');

$openApi = OpenAPI::create()
->paths($path);
// $openApi->toJson() -> '{"paths": {"/users": []}}'

$openApi = $openApi->paths();
// $openApi->toJson() -> '{}'
```

### Retrieving properties

You can easily retrieve a property using a magic getter. These have been
implemented for all properties for every object. DocBlocks have been provided
to give better auto-completion in IDEs:

```php
$info = Info::create()->title('Example API');

// $info->title => 'Example API'
```

### Object ID

Every object has an optional `$objectId` property which is a `string` and can
either be set in the class constructor or the preferred `create()` method. This
property is used when a parent object needs to use a name for the children.

An example of this in use is when a schema object is composed of other schema
properties:

```php
$schema = Schema::create()
->type(Schema::TYPE_OBJECT)
->properties(
Schema::create('username')->type(Schema::TYPE_STRING),
Schema::create('age')->type(Schema::TYPE_INTEGER)
);

$schema->toJson();
/*
{
"type": "object",
"properties": {
"username": {
"type": "string"
},
"age": {
"type": "integer"
}
}
}
*/
```

If an object contains any helper creation methods, then these methods also allow
you to specify the `$objectId` property as a parameter. The code sample below is
functionally identical to the one above:

```php
$schema = Schema::object()
->properties(
Schema::string('username'),
Schema::integer('age')
);

$schema->toJson();
/*
{
"type": "object",
"properties": {
"username": {
"type": "string"
},
"age": {
"type": "integer"
}
}
}
*/
```

## Running the tests

To run the test suite you can use the following command:
Expand Down
3 changes: 2 additions & 1 deletion phpcs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
<ruleset name="PSR1/2">
<description>The PSR1 and PSR2 coding standards</description>

<file>src</file>
<file>./src</file>
<exclude-pattern>./tests</exclude-pattern>

<rule ref="PSR1"/>
<rule ref="PSR2"/>
Expand Down
10 changes: 10 additions & 0 deletions src/Contracts/SchemaContract.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace GoldSpecDigital\ObjectOrientedOAS\Contracts;

interface SchemaContract
{
//
}
16 changes: 16 additions & 0 deletions src/Objects/AllOf.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace GoldSpecDigital\ObjectOrientedOAS\Objects;

class AllOf extends SchemaComposition
{
/**
* @return string
*/
protected function compositionType(): string
{
return 'allOf';
}
}
16 changes: 16 additions & 0 deletions src/Objects/AnyOf.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace GoldSpecDigital\ObjectOrientedOAS\Objects;

class AnyOf extends SchemaComposition
{
/**
* @return string
*/
protected function compositionType(): string
{
return 'anyOf';
}
}
31 changes: 27 additions & 4 deletions src/Objects/BaseObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,37 @@
use GoldSpecDigital\ObjectOrientedOAS\Exceptions\PropertyDoesNotExistException;
use JsonSerializable;

/**
* @property string|null $objectId
*/
abstract class BaseObject implements JsonSerializable
{
/**
* Object constructor.
* @var string|null
*/
protected function __construct()
protected $objectId;

/**
* BaseObject constructor.
*
* @param string|null $objectId
*/
public function __construct(string $objectId = null)
{
// Prevent instantiation.
$this->objectId = $objectId;
}

/**
* @param string|null $objectId
* @return \GoldSpecDigital\ObjectOrientedOAS\Objects\BaseObject
*/
public function objectId(?string $objectId): self
{
$instance = clone $this;

$instance->objectId = $objectId;

return $instance;
}

/**
Expand Down Expand Up @@ -47,7 +70,7 @@ public function jsonSerialize(): array
*/
public function __get(string $name)
{
if (isset($this->$name)) {
if (property_exists($this, $name)) {
return $this->$name;
}

Expand Down
11 changes: 6 additions & 5 deletions src/Objects/Components.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ class Components extends BaseObject
protected $securitySchemes;

/**
* @param string|null $objectId
* @return \GoldSpecDigital\ObjectOrientedOAS\Objects\Components
*/
public static function create(): self
public static function create(string $objectId = null): self
{
return new static();
return new static($objectId);
}

/**
Expand All @@ -43,12 +44,12 @@ public function securitySchemes(SecurityScheme ...$securitySchemes): self
public function toArray(): array
{
$securitySchemes = [];
foreach ($this->securitySchemes as $securityScheme) {
$securitySchemes[$securityScheme->name] = $securityScheme;
foreach ($this->securitySchemes ?? [] as $securityScheme) {
$securitySchemes[$securityScheme->objectId] = $securityScheme;
}

return Arr::filter([
'securitySchemes' => $securitySchemes,
'securitySchemes' => $securitySchemes ?: null,
]);
}
}
25 changes: 7 additions & 18 deletions src/Objects/Contact.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,16 @@ class Contact extends BaseObject
protected $email;

/**
* @param string|null $name
* @param string|null $url
* @param string|null $email
* @param string|null $objectId
* @return \GoldSpecDigital\ObjectOrientedOAS\Objects\Contact
*/
public static function create(
string $name = null,
string $url = null,
string $email = null
): self {
$instance = new static();

$instance->name = $name;
$instance->url = $url;
$instance->email = $email;

return $instance;
public static function create(string $objectId = null): self
{
return new static($objectId);
}

/**
* @param null|string $name
* @param string|null $name
* @return \GoldSpecDigital\ObjectOrientedOAS\Objects\Contact
*/
public function name(?string $name): self
Expand All @@ -62,7 +51,7 @@ public function name(?string $name): self
}

/**
* @param null|string $url
* @param string|null $url
* @return \GoldSpecDigital\ObjectOrientedOAS\Objects\Contact
*/
public function url(?string $url): self
Expand All @@ -75,7 +64,7 @@ public function url(?string $url): self
}

/**
* @param null|string $email
* @param string|null $email
* @return \GoldSpecDigital\ObjectOrientedOAS\Objects\Contact
*/
public function email(?string $email): self
Expand Down
Loading

0 comments on commit f5ef7d7

Please sign in to comment.