Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: generate OpenAPI JSON from APIB #602

Merged
merged 1 commit into from
Sep 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,89 @@ jobs:
name: coverage-${{ matrix.php-versions }}
path: coverage.xml

openapi:
name: File generation
needs: test
runs-on: ubuntu-latest
env:
PHPDRAFT_THIRD_PARTY: 1
extensions: curl,json,mbstring,uopz
key: cache-v1 # can be any string, change to clear the extension cache.
strategy:
matrix:
php-versions: [ '8.3' ]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis

- name: Setup cache environment
id: extcache
uses: shivammathur/cache-extensions@v1
with:
php-version: ${{ matrix.php-versions }}
extensions: ${{ env.extensions }}
key: ${{ env.key }}

- name: Cache extensions
uses: actions/cache@v4
with:
path: ${{ steps.extcache.outputs.dir }}
key: ${{ steps.extcache.outputs.key }}
restore-keys: ${{ steps.extcache.outputs.key }}

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: ${{ env.extensions }}
coverage: pcov
tools: pecl,phpunit

- name: Get Composer Cache Directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT"

- name: Cache dependencies
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-

- name: Validate composer.json and composer.lock
run: composer validate

- name: Install dependencies
run: composer install --prefer-dist --no-progress --ignore-platform-reqs

- name: Generate OpenAPI definition and HTML
run: php ./phpdraft --online --file tests/statics/full_test.apib --openapi openapi.json > out.html 2> error.txt || true

- name: Install check-jsonschema
run: pipx install check-jsonschema

- name: Validate OpenAPI spec
run: |
if [ -s "error.txt" ]; then
echo "The file 'error.txt' is not empty."
cat error.txt
exit 1
fi

if [ ! -s "index.html" ]; then
echo "The file 'index.html' is empty."
exit 1
fi

if [ ! -s "openapi.json" ]; then
echo "The file 'openapi.json' is empty."
exit 1
fi

check-jsonschema --schemafile https://spec.openapis.org/oas/3.1/schema/latest openapi.json

analytics:
name: Analytics
needs: test
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
/build/out
/build/*.phar
/tests/statics/index.*
src/Michelf/*
src/.gitignore
vendor/**

Expand All @@ -21,6 +20,8 @@ atlassian-ide-plugin.xml
*.pem

/index.html
/openapi.json

/coverage.xml
/event.json
!/src/PHPDraft/Out/
6 changes: 6 additions & 0 deletions phpdraft
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ try
->opt('help:h', 'This help text', false)
->opt('version:v', 'Print the version for PHPDraft.', false)
->opt('file:f', 'Specifies the file to parse.', false)
->opt('openapi:a', 'Output location for an OpenAPI file.', false)
->opt('yes:y', 'Always accept using the online mode.', false, 'bool')
->opt('online:o', 'Always use the online mode.', false, 'bool')
->opt('template:t', 'Specifies the template to use. (defaults to \'default\').', false)
Expand Down Expand Up @@ -90,6 +91,11 @@ try
$data = json_decode($json_string);
}

if (isset($args['openapi'])) {
$openapi = ParserFactory::getOpenAPI()->init($data);
$openapi->write($args['openapi']);
}

$html = ParserFactory::getJson()->init($data);
$name = 'PHPD_SORT_' . strtoupper($args->getOpt('sort', ''));
$html->sorting = Sorting::${$name} ?? Sorting::PHPD_SORT_NONE->value;
Expand Down
8 changes: 5 additions & 3 deletions src/PHPDraft/Model/HTTPRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

namespace PHPDraft\Model;

use PHPDraft\Model\Elements\ObjectStructureElement;
use PHPDraft\Model\Elements\RequestBodyElement;
use PHPDraft\Model\Elements\StructureElement;

Expand Down Expand Up @@ -43,7 +44,7 @@ class HTTPRequest implements Comparable
*
* @var string
*/
public string $description;
public string $description = '';

/**
* Parent class.
Expand All @@ -68,9 +69,10 @@ class HTTPRequest implements Comparable
/**
* Structure of the request.
*
* @var RequestBodyElement[]|RequestBodyElement
* @var RequestBodyElement[]|RequestBodyElement|ObjectStructureElement
*/
public mixed $struct = [];
public RequestBodyElement|ObjectStructureElement|array|null $struct = [];

/**
* Identifier for the request.
*
Expand Down
4 changes: 2 additions & 2 deletions src/PHPDraft/Model/Transition.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ class Transition extends HierarchyElement
/**
* HTTP method used.
*
* @var string
* @var string|null
*/
public string $method;
public ?string $method = NULL;

/**
* URI.
Expand Down
64 changes: 42 additions & 22 deletions src/PHPDraft/Out/BaseTemplateRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,36 +24,21 @@
* @var int
*/
public int $sorting;

/**
* CSS Files to load.
*
* @var string[]
*/
public array $css = [];
/**
* JS Files to load.
*
* @var string[]
*/
public array $js = [];
/**
* The image to use as a logo.
*
* @var string|null
*/
protected ?string $image = null;
/**
* The template file to load.
* JSON representation of an API Blueprint.
*
* @var string
* @var object
*/
protected string $template;
protected object $object;

/**
* The base data of the API.
*
* @var array<string, mixed>
*/
protected array $base_data;
protected array $base_data = [];

/**
* JSON object of the API blueprint.
*
Expand All @@ -66,4 +51,39 @@
* @var BasicStructureElement[]
*/
protected array $base_structures = [];

/**
* Parse base data
*
* @param object $object
*/
protected function parse_base_data(object $object): void
{
//Prepare base data
if (!is_array($object->content[0]->content)) {
return;
}

$this->base_data['TITLE'] = $object->content[0]->meta->title->content ?? '';

foreach ($object->content[0]->attributes->metadata->content as $meta) {
$this->base_data[$meta->content->key->content] = $meta->content->value->content;
}

foreach ($object->content[0]->content as $value) {
if ($value->element === 'copy') {
$this->base_data['DESC'] = $value->content;
continue;
}

$cat = new Category();
$cat = $cat->parse($value);

Check warning on line 80 in src/PHPDraft/Out/BaseTemplateRenderer.php

View check run for this annotation

Codecov / codecov/patch

src/PHPDraft/Out/BaseTemplateRenderer.php#L79-L80

Added lines #L79 - L80 were not covered by tests

if (($value->meta->classes->content[0]->content ?? null) === 'dataStructures') {
$this->base_structures = array_merge($this->base_structures, $cat->structures);

Check warning on line 83 in src/PHPDraft/Out/BaseTemplateRenderer.php

View check run for this annotation

Codecov / codecov/patch

src/PHPDraft/Out/BaseTemplateRenderer.php#L82-L83

Added lines #L82 - L83 were not covered by tests
} else {
$this->categories[] = $cat;

Check warning on line 85 in src/PHPDraft/Out/BaseTemplateRenderer.php

View check run for this annotation

Codecov / codecov/patch

src/PHPDraft/Out/BaseTemplateRenderer.php#L85

Added line #L85 was not covered by tests
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,36 @@
use Twig\TwigFilter;
use Twig\TwigTest;

class TemplateRenderer extends BaseTemplateRenderer
class HtmlTemplateRenderer extends BaseTemplateRenderer
{


/**
* CSS Files to load.
*
* @var string[]
*/
public array $css = [];

/**
* JS Files to load.
*
* @var string[]
*/
public array $js = [];
/**
* The image to use as a logo.
*
* @var string|null
*/
protected ?string $image = null;
/**
* The template file to load.
*
* @var string
*/
protected string $template;

/**
* TemplateGenerator constructor.
*
Expand Down Expand Up @@ -109,41 +137,6 @@ public function get(object $object): string
]);
}

/**
* Parse base data
*
* @param object $object
*/
private function parse_base_data(object $object): void
{
//Prepare base data
if (!is_array($object->content[0]->content)) {
return;
}

$this->base_data['TITLE'] = $object->content[0]->meta->title->content ?? '';

foreach ($object->content[0]->attributes->metadata->content as $meta) {
$this->base_data[$meta->content->key->content] = $meta->content->value->content;
}

foreach ($object->content[0]->content as $value) {
if ($value->element === 'copy') {
$this->base_data['DESC'] = $value->content;
continue;
}

$cat = new Category();
$cat = $cat->parse($value);

if (($value->meta->classes->content[0]->content ?? null) === 'dataStructures') {
$this->base_structures = array_merge($this->base_structures, $cat->structures);
} else {
$this->categories[] = $cat;
}
}
}

/**
* Get the path to a file to include.
*
Expand Down
Loading
Loading