diff --git a/config/packages/api_platform.yaml b/config/packages/api_platform.yaml index 87f7a1d..655b636 100644 --- a/config/packages/api_platform.yaml +++ b/config/packages/api_platform.yaml @@ -1,9 +1,24 @@ api_platform: - title: Goteo API - version: 1.0.0 + title: The v4 API + version: 4.0.0 + show_webby: false + collection: + order: 'DESC' + openapi: + contact: + name: Fundación Platoniq + url: http://platoniq.net/contact/ + email: contacto@platoniq.net + termsOfService: + license: + name: MIT + url: + swagger_ui_extra_configuration: + filter: true formats: json: ['application/json'] - # jsonld: ['application/ld+json'] + jsonld: ['application/ld+json'] + csv: ['text/csv'] docs_formats: html: ['text/html'] json: ['application/json'] @@ -18,5 +33,3 @@ api_platform: rfc_7807_compliant_errors: true event_listeners_backward_compatibility_layer: false keep_legacy_inflector: false - enable_re_doc: false - show_webby: false diff --git a/config/services.yaml b/config/services.yaml index f7e2ba4..a5fe3ab 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -24,6 +24,12 @@ services: - '../src/Entity/' - '../src/Kernel.php' + App\OpenApi\OpenApiFactory: + autoconfigure: false + decorates: 'api_platform.openapi.factory' + arguments: + - '@App\OpenApi\OpenApiFactory.inner' + App\Library\Economy\Currency\ExchangeLocator: arguments: - !tagged 'app.lib.economy.currency.exchange' diff --git a/public/style.css b/public/style.css index d2a4ea7..453004a 100644 --- a/public/style.css +++ b/public/style.css @@ -1,3 +1,44 @@ body { margin-top: 0; -} \ No newline at end of file +} + +#formats { + margin: 0; + padding: 20px 40px; + max-width: 100%; + background-color: #088787; +} + +#formats .information-container { + margin: 0; + padding: 0; + max-width: none; + text-align: left; +} + +#formats .information-container .info { + margin: 0; + padding: 0; +} + +#formats .information-container .info p { + color: #2c2c2c; + margin: 10px; +} + +#formats .information-container .info a { + color: #fff; + text-decoration: none; +} + +ul[role="menu"]+div { + position: sticky !important; +} + +.hl-yellow { + background-color: #EFE1A8; +} + +a { + color: #2BBBB7; +} diff --git a/src/OpenApi/OpenApiDescription.md b/src/OpenApi/OpenApiDescription.md new file mode 100644 index 0000000..fa1a67f --- /dev/null +++ b/src/OpenApi/OpenApiDescription.md @@ -0,0 +1,10 @@ +# Introduction +The v4 API is a multi-capable web API based on the [API Platform](https://api-platform.com/) framework, built to support the future generation of the [Goteo](https://goteo.org) crowdfunding platform, aiming to be it's new underlying engine. + +v4 exposes a REST API with predictable and resource-oriented URLs, accepts request bodies (payloads) encoded in multiple open standard formats such as JSON, including [JSON-LD](https://json-ld.org/) and [Hydra](https://www.hydra-cg.com/), returns responses encoded in the same formats, and uses standard HTTP verbs and status codes. + +This API is documented in the OpenApi initiative [Specification v3.1.0](https://spec.openapis.org/oas/v3.1.0), which allows v4 to be easily understood by API development suites, documentation generators and SDK generators for the convenience of developers and API users. Most of the spec is generated automatically as changes are introduced, but some additional content such as this introduction and other sections might not be updated as regularly. + + +This API is still in early development and is not set to have backward compatibility with the Goteo API v1 (http://api.goteo.org/v1/). Major changes are to be expected. + \ No newline at end of file diff --git a/src/OpenApi/OpenApiFactory.php b/src/OpenApi/OpenApiFactory.php new file mode 100644 index 0000000..21b74f1 --- /dev/null +++ b/src/OpenApi/OpenApiFactory.php @@ -0,0 +1,133 @@ +getOperationId()); + $idResource = $operation->getTags()[0]; + $idOperation = array_slice($idParts, -1)[0]; + + switch ($idOperation) { + case 'collection': + $operationId = sprintf('List all %ss', $idResource); + break; + case 'post': + $operationId = sprintf('Create one %s', $idResource); + break; + case 'get': + $operationId = sprintf('Retrieve one %s', $idResource); + break; + case 'put': + $operationId = sprintf('Update one %s', $idResource); + break; + case 'delete': + $operationId = sprintf('Delete one %s', $idResource); + break; + case 'patch': + $operationId = sprintf('Patch one %s', $idResource); + break; + default: + $operationId = $operation->getOperationId(); + } + + return $operation + ->withOperationId($operationId) + ->withSummary($operationId); + } + + private function updatePathItemOperations(PathItem $pathItem): PathItem + { + if ($pathItem->getGet()) { + $pathItem = $pathItem->withGet( + $this->updateOperation($pathItem->getGet()) + ); + } + + if ($pathItem->getPost()) { + $pathItem = $pathItem->withPost( + $this->updateOperation($pathItem->getPost()) + ); + } + + if ($pathItem->getPut()) { + $pathItem = $pathItem->withPut( + $this->updateOperation($pathItem->getPut()) + ); + } + + if ($pathItem->getDelete()) { + $pathItem = $pathItem->withDelete( + $this->updateOperation($pathItem->getDelete()) + ); + } + + if ($pathItem->getPatch()) { + $pathItem = $pathItem->withPatch( + $this->updateOperation($pathItem->getPatch()) + ); + } + + return $pathItem; + } + + public function __invoke(array $context = []): OpenApi + { + $openApi = $this->decorated->__invoke($context); + + $openApi = $openApi->withInfo( + $openApi + ->getInfo() + ->withDescription( + \file_get_contents(sprintf( + '%s%s%s', + __DIR__, + DIRECTORY_SEPARATOR, + 'OpenApiDescription.md' + )) + ) + ); + + $tags = []; + foreach ($openApi->getComponents()->getSchemas() as $name => $schema) { + if (\preg_match('/.*\.json/', $name)) continue; + if (empty($schema['description'])) continue; + + $tags[] = [ + 'name' => $name, + 'description' => $schema['description'] + ]; + } + + $openApi = $openApi->withTags($tags); + + $paths = new Paths(); + foreach ($openApi->getPaths()->getPaths() as $path => $pathItem) { + $pathItem = $this->updatePathItemOperations($pathItem); + + $paths->addPath($path, $pathItem); + } + + $openApi = $openApi->withPaths($paths); + + $openApi = $openApi->withServers([ + new Model\Server(sprintf('%s://%s', $_SERVER['REQUEST_SCHEME'], $_SERVER['HTTP_HOST'])) + ]); + + return $openApi; + } +} diff --git a/templates/bundles/ApiPlatformBundle/SwaggerUi/index.html.twig b/templates/bundles/ApiPlatformBundle/SwaggerUi/index.html.twig index 1091f5c..ba874fa 100644 --- a/templates/bundles/ApiPlatformBundle/SwaggerUi/index.html.twig +++ b/templates/bundles/ApiPlatformBundle/SwaggerUi/index.html.twig @@ -1,87 +1,122 @@ + {% block head_metas %} - + {% endblock %} {% block title %} - {% if title %}{{ title }}{% endif %} + {{ title }} {% endblock %} {% block stylesheet %} - - - - - + + + + + {% endblock %} - {% set oauth_data = {'oauth': swagger_data.oauth|merge({'redirectUrl' : absolute_url(asset('bundles/apiplatform/swagger-ui/oauth2-redirect.html', assetPackage)) })} %} + {% set oauth_data = {'oauth': swagger_data.oauth|merge({'redirectUrl' : + absolute_url(asset('bundles/apiplatform/swagger-ui/oauth2-redirect.html', assetPackage)) })} %} {% block head_javascript %} - {# json_encode(65) is for JSON_UNESCAPED_SLASHES|JSON_HEX_TAG to avoid JS XSS #} - + {# json_encode(65) is for JSON_UNESCAPED_SLASHES|JSON_HEX_TAG to avoid JS XSS #} + {% endblock %} - - - - - + + + + + + - - - + + + + - - - + + + + - - - + + + + - - - + + + + - - - + + + - - - + + + - - + + -
+
-
-
-
- Available formats: - {% for format in formats|keys %} - {{ format }} - {% endfor %} -
- Built with: - API Platform +
+
+
+

Available formats:

+

+ {% for format in formats|keys %} + {{ format }} + {% endfor %} +

+
+
+

Other API docs:

+

+ {% set active_ui = app.request.get('ui', 're_doc') %} + {% if swaggerUiEnabled and active_ui != 'swagger_ui' %} + Swagger UI + {% endif %} + {% if reDocEnabled and active_ui != 're_doc' %} + ReDoc + {% endif %} +

+
+
+

Built with:

+

+ API Platform +

+
-
-{% block javascript %} + {% block javascript %} + {% if (swaggerUiEnabled and not reDocEnabled) or (swaggerUiEnabled and 'swagger_ui' == active_ui) %} + {% else %} + + + {% endif %} -{% endblock %} + {% endblock %} - + + \ No newline at end of file