This commit is contained in:
Pitchaya Boonsarngsuk
2019-07-05 16:56:10 +01:00
parent c3d3dcbff7
commit 6b6c18c79e
9 changed files with 367 additions and 53 deletions

View File

@@ -14,7 +14,7 @@
# https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration
MERCURE_SUBSCRIBE_URL=http://localhost:1337/hub
VARNISH_URL=http://cache-proxy
#VARNISH_URL=http://cache-proxy
###> symfony/framework-bundle ###
APP_ENV=dev
@@ -31,7 +31,7 @@ DATABASE_URL=postgres://api-platform:!ChangeMe!@db/api
###< doctrine/doctrine-bundle ###
###> nelmio/cors-bundle ###
CORS_ALLOW_ORIGIN=^https?://localhost(:[0-9]+)?$
CORS_ALLOW_ORIGIN=^https?://.*$
###< nelmio/cors-bundle ###
###> symfony/mercure-bundle ###

7
api/Readme.md Normal file
View File

@@ -0,0 +1,7 @@
Enable prod mode: `docker-compose exec php composer dump-env prod`
Disable prod mode: delete `.env.local.php`
Clear Prod cache `docker-compose exec php bin/console cache:clear --env=prod`
Swagger: https://api-platform.com/docs/core/swagger/#using-the-openapi-and-swagger-contexts

View File

@@ -9,16 +9,12 @@ api_platform:
mapping:
paths: ['%kernel.project_dir%/src/Entity']
title: Hello API Platform
description: สวัสดีเพื่อนๆ
version: 1.0.0
# Varnish integration, remove if unwanted
http_cache:
invalidation:
enabled: true
varnish_urls: ['%env(VARNISH_URL)%']
max_age: 0
shared_max_age: 3600
vary: ['Content-Type', 'Authorization']
public: true
# Mercure integration, remove if unwanted
mercure:
hub_url: '%env(MERCURE_SUBSCRIBE_URL)%'
show_webby: false
formats:
json: ['application/json']
xml: ['application/xml', 'text/xml']
yaml: ['application/x-yaml']
csv: ['text/csv']
html: ['text/html']

View File

@@ -2,7 +2,7 @@ nelmio_cors:
defaults:
origin_regex: true
allow_origin: ['%env(CORS_ALLOW_ORIGIN)%']
allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE']
allow_methods: ['GET', 'OPTIONS']
allow_headers: ['Content-Type', 'Authorization']
expose_headers: ['Link']
max_age: 3600

View File

@@ -0,0 +1,71 @@
<?php
namespace App\DataProvider;
use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
use ApiPlatform\Core\DataProvider\SubresourceDataProviderInterface;
use ApiPlatform\Core\Exception\InvalidValueException;
use ApiPlatform\Core\Exception\ResourceClassNotSupportedException;
use App\Entity\Creation;
use Symfony\Component\HttpFoundation\RequestStack;
use App\Entity\Circle;
final class CircleDataProvider implements CollectionDataProviderInterface, ItemDataProviderInterface, SubresourceDataProviderInterface, RestrictedDataProviderInterface
{
private $requestStack;
/**
* CircleDataProvider constructor.
* @param $requestStack
*/
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
{
return Circle::class === $resourceClass;
}
public function getCollection(string $resourceClass, string $operationName = null): \Generator
{
$query = $this->requestStack->getCurrentRequest()->query;
$ids = $query->get('ids');
if ($ids !== null) {
if (!preg_match('/^\d+(,\d+)*$/', $ids)) {
throw new InvalidValueException('ids must be comma-seperated integers', 400);
//BadQueryStringException('ids must be comma-seperated integer', 400);
}
$ids = explode(',', $ids);
foreach ($ids as $id) {
yield new Circle((int)$id, $operationName, 'bla');
}
}
else {
for($i = 0; $i< 10; $i++) {
yield new Circle($i, $operationName, json_encode($ids));
}
}
// Returning array works too
// Return empty array if no data
}
public function getItem(string $resourceClass, $id, string $operationName = null, array $context = [])
{
//return null; for 404
$tmp = new Circle($id, is_string($id)? 'true' : 'false', 'a01');
$tmp->creations = [new Creation(55,'55'), new Creation(66,'66')];
return $tmp;
}
public function getSubresource(string $resourceClass, array $identifiers, array $context, string $operationName = null)
{
return $context['property'];
// TODO: Implement getSubresource() method.
}
}

131
api/src/Entity/Circle.php Normal file
View File

@@ -0,0 +1,131 @@
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Annotation\ApiSubresource;
use Symfony\Component\Validator\Constraints as Assert;
/**
* A circle
* @ApiResource(
* attributes={"pagination_enabled"=false},
* collectionOperations={
* "get"={
* "method"="GET",
* "swagger_context"={
* "summary"="Get the list of circles",
* "description"="Get the list of circles",
* "parameters" = {
* {
* "name"="ids",
* "in"="query",
* "description"="Comma-seperated list of circle ids. If not specified, will return all circles",
* "example"="123,456",
* "pattern"="^\d+(,\d+)*$",
* "type":"string"
* }
* }
* },
* "requirements"={"ids"="\d+(,\d+)*"}
* }
* },
* itemOperations={
* "get"={
* "method"="GET",
* "requirements"={"id"="\d+"}
* }
* }
* )
*/
class Circle
{
/**
* @var int Circle Id
* @ApiProperty(identifier=true)
* @Assert\NotBlank
* @Assert\Type(type="int")
*/
private $id;
/**
* @var string Circle name
* @Assert\NotBlank
*/
public $name = '';
/**
* @var string Booth location
* @Assert\NotBlank
*/
public $booth = '';
/**
* @var string Description
*/
public $description;
/**
* @var string Cutout image url
*/
public $cutout;
/**
* @var string Menu image url
*/
public $menu;
/**
* @var string Cover image url
*/
public $cover;
/**
* @var int Spaces
* @Assert\Type(type="int")
*/
public $sp;
/**
* @var string Type of circle
*/
public $type;
// Members
/**
* @ApiSubresource()
* @var integer[] Members
*/
public $members;
// Creations
/**
* @ApiSubresource()
* @var Creation[] Creations
*/
public $creations;
/**
* Circle constructor.
* @param int $id
* @param string $name
* @param string $booth
*/
public function __construct(int $id, string $name, string $booth)
{
$this->id = $id;
$this->name = $name;
$this->booth = $booth;
}
/**
* @return integer
*/
public function getId(): int
{
return $this->id;
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Validator\Constraints as Assert;
/**
* A Creation
* @ApiResource(
* attributes={"pagination_enabled"=false},
* collectionOperations={"get"},
* itemOperations={
* "get"={
* "method"="GET",
* "requirements"={"id"="\d+"}
* }
* }
* )
*/
class Creation
{
/**
* @var int Circle Id
* @ApiProperty(identifier=true)
* @Assert\NotBlank
* @Assert\Type(type="int")
*/
private $id;
/**
* @var string Creation name
* @Assert\NotBlank
*/
public $name = '';
/**
* @var integer Circle ID
* @Assert\NotBlank
*/
public $circleId;
/**
* @param int $id
* @param string $name
*/
public function __construct(int $id, string $name)
{
$this->id = $id;
$this->name = $name;
}
/**
* @return integer
*/
public function getId(): int
{
return $this->id;
}
}

View File

@@ -1,38 +0,0 @@
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* This is a dummy entity. Remove it!
*
* @ApiResource
* @ORM\Entity
*/
class Greeting
{
/**
* @var int The entity Id
*
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @var string A nice person
*
* @ORM\Column
* @Assert\NotBlank
*/
public $name = '';
public function getId(): int
{
return $this->id;
}
}

View File

@@ -0,0 +1,85 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% if title %}{{ title }} - {% endif %}API Platform</title>
{% block stylesheet %}
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700">
<link rel="stylesheet" href="{{ asset('bundles/apiplatform/swagger-ui/swagger-ui.css') }}">
<link rel="stylesheet" href="{{ asset('bundles/apiplatform/style.css') }}">
{% endblock %}
{# json_encode(65) is for JSON_UNESCAPED_SLASHES|JSON_HEX_TAG to avoid JS XSS #}
<script id="swagger-data" type="application/json">{{ swagger_data|json_encode(65)|raw }}</script>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" style="position:absolute;width:0;height:0">
<defs>
<symbol viewBox="0 0 20 20" id="unlocked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path>
</symbol>
<symbol viewBox="0 0 20 20" id="locked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z"></path>
</symbol>
<symbol viewBox="0 0 20 20" id="close">
<path d="M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z"></path>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow">
<path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z"></path>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow-down">
<path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z"></path>
</symbol>
<symbol viewBox="0 0 24 24" id="jump-to">
<path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z"></path>
</symbol>
<symbol viewBox="0 0 24 24" id="expand">
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"></path>
</symbol>
</defs>
</svg>
<header>
<a id="logo" href="https://api-platform.com"><img src="{{ asset('bundles/apiplatform/logo-header.svg') }}" alt="API Platform"></a>
</header>
{% if showWebby %}
<div class="web"><img src="{{ asset('bundles/apiplatform/web.png') }}"></div>
<div class="webby"><img src="{{ asset('bundles/apiplatform/webby.png') }}"></div>
{% endif %}
<div id="swagger-ui" class="api-platform"></div>
<div class="swagger-ui" id="formats">
<div class="information-container wrapper">
<div class="info">
Other API docs:
{% set active_ui = app.request.get('ui', 'swagger_ui') %}
{% if swaggerUiEnabled and active_ui != 'swagger_ui' %}<a href="{{ path('api_entrypoint') }}">Swagger UI</a>{% endif %}
{% if reDocEnabled and active_ui != 're_doc' %}<a href="{{ path('api_entrypoint', {'ui': 're_doc'}) }}">ReDoc</a>{% endif %}
</div>
</div>
</div>
{% block javascript %}
{% if (reDocEnabled and not swaggerUiEnabled) or (reDocEnabled and 're_doc' == active_ui) %}
<script src="{{ asset('bundles/apiplatform/redoc/redoc.standalone.js') }}"></script>
<script src="{{ asset('bundles/apiplatform/init-redoc-ui.js') }}"></script>
{% elseif (swaggerUiEnabled) %}
<script src="{{ asset('bundles/apiplatform/swagger-ui/swagger-ui-bundle.js') }}"></script>
<script src="{{ asset('bundles/apiplatform/swagger-ui/swagger-ui-standalone-preset.js') }}"></script>
<script src="{{ asset('bundles/apiplatform/init-swagger-ui.js') }}"></script>
{% endif %}
{% endblock %}
</body>
</html>