Skip to content

Commit

Permalink
Merge pull request #82 from swagindustries/ok-no-content-response
Browse files Browse the repository at this point in the history
Add OkNoContent response
  • Loading branch information
Nek- authored Oct 28, 2024
2 parents 9489fd7 + 2fcccc8 commit 50011ee
Show file tree
Hide file tree
Showing 19 changed files with 150 additions and 44 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"roave/security-advisories": "dev-latest",
"psr/container": "^v1.1.1 || ^2.0",
"phpunit/phpunit": "^9.6 || ^10.0.0 || ^11.1.3",
"friendsofphp/php-cs-fixer": "3.58.0",
"friendsofphp/php-cs-fixer": "3.64.0",
"phpspec/prophecy": "^1.19",
"twig/twig": "^2.5 || ^3.10",
"doctrine/orm": "^2.6.6 || ^2.17.0",
Expand Down
18 changes: 12 additions & 6 deletions features/crud.feature
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,20 @@ Feature:
{
"id": 1,
"content": "foo",
"publishDate":"2050-01-02T00:00:00+00:00"
"publishDate":"2050-01-02T00:00:00+00:00",
"archived": false
},
{
"id": 2,
"content": "bar",
"publishDate":"2050-01-02T00:00:00+00:00"
"publishDate":"2050-01-02T00:00:00+00:00",
"archived": false
},
{
"id": 3,
"content": "baz",
"publishDate":"2050-01-02T00:00:00+00:00"
"publishDate":"2050-01-02T00:00:00+00:00",
"archived": false
}
]
}
Expand All @@ -61,7 +64,8 @@ Feature:
{
"id": 1,
"content": "hello",
"publishDate":"2050-01-02T00:00:00+00:00"
"publishDate":"2050-01-02T00:00:00+00:00",
"archived": false
}
"""

Expand All @@ -87,12 +91,14 @@ Feature:
{
"id": 2,
"content": "bar",
"publishDate":"2050-01-02T00:00:00+00:00"
"publishDate":"2050-01-02T00:00:00+00:00",
"archived": false
},
{
"id": 3,
"content": "baz",
"publishDate":"2050-01-02T00:00:00+00:00"
"publishDate":"2050-01-02T00:00:00+00:00",
"archived": false
}
]
}
Expand Down
9 changes: 8 additions & 1 deletion features/custom_responses.feature
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ Feature:
{
"id": 1,
"content": "foo",
"publishDate":"2050-01-02T00:00:00+00:00"
"publishDate":"2050-01-02T00:00:00+00:00",
"archived": false
}
]
}
Expand All @@ -33,3 +34,9 @@ Feature:
Scenario: In debug mode, when an exception occurred I should get a response with stacktrace
When I make a GET request on "/error"
Then I should retrieve a stacktrace formatted in JSON

Scenario: return ok content response
Given there are some todos
And I make a PATCH request on "/todos/1/archive"
Then the last response is empty
And the http code is 204
2 changes: 1 addition & 1 deletion src/Crud/Controller/GetAll.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public function __construct(
DataStoreInterface $dataStore,
FilterCollectionFactoryInterface $collectionFactory,
PaginationRequestFactoryInterface $pagesRequestFactory,
?AuthorizationCheckerInterface $checker = null
?AuthorizationCheckerInterface $checker = null,
) {
$this->dataStore = $dataStore;
$this->checker = $checker;
Expand Down
2 changes: 1 addition & 1 deletion src/EventListener/ExceptionListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public function __construct(
MelodiiaConfigurationInterface $config,
OnError $controller,
bool $debug,
?ErrorListener $errorListener = null
?ErrorListener $errorListener = null,
) {
$this->errorListener = $errorListener ?? new ErrorListener($controller, null, $debug);
$this->config = $config;
Expand Down
18 changes: 18 additions & 0 deletions src/Response/OkNoContent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace SwagIndustries\Melodiia\Response;

class OkNoContent implements ApiResponse
{
public function httpStatus(): int
{
return 204;
}

public function headers(): array
{
return [];
}
}
7 changes: 3 additions & 4 deletions tests/Behat/Context/AbstractContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ protected function getContainer(): ContainerInterface
return $this->kernel->getContainer();
}

final protected function request(string $uri, string $verb, $rawContent): Response
final protected function request(string $uri, string $verb, ?string $rawContent): Response
{
$client = $this->getContainer()->get('test.client');

Expand All @@ -42,10 +42,9 @@ final protected function request(string $uri, string $verb, $rawContent): Respon
return self::$response = $client->getResponse();
}

if (null === $rawContent) {
throw new \Exception(sprintf('Cannot process request "%s" with no content.', $verb));
if (null !== $rawContent) {
json_decode($rawContent, true, 512, \JSON_THROW_ON_ERROR);
}
json_decode($rawContent, true, 512, \JSON_THROW_ON_ERROR);

$client->request($verb, $uri, [], [], [], $rawContent);

Expand Down
50 changes: 35 additions & 15 deletions tests/Behat/Context/BasicsContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,29 @@
class BasicsContext extends AbstractContext
{
/**
* @When I make a GET request on :uri
* @BeforeScenario
*/
public function resetDb()
{
@unlink(dirname(__DIR__) . '/../TestApplication/var/data.db');

$entityManager = $this->getContainer()->get('doctrine')->getManager();
$metadatas = $entityManager->getMetadataFactory()->getAllMetadata();
$schemaTool = new SchemaTool($entityManager);
$schemaTool->createSchema($metadatas);
}

/**
* @When I make a :verb request on :uri
*
* @Given I make a :verb request on :uri with the content:
*/
public function iMakeARequestOn($uri, $verb = 'GET', ?PyStringNode $content = null)
{
$this->request($uri, $verb, (string) $content);
if ($content instanceof PyStringNode) {
$content = (string) $content;
}
$this->request($uri, $verb, $content);
}

/**
Expand Down Expand Up @@ -48,19 +64,6 @@ public function iShouldRetrieve(PyStringNode $string)
}
}

/**
* @BeforeScenario
*/
public function resetDb()
{
@unlink(dirname(__DIR__) . '/../TestApplication/var/data.db');

$entityManager = $this->getContainer()->get('doctrine')->getManager();
$metadatas = $entityManager->getMetadataFactory()->getAllMetadata();
$schemaTool = new SchemaTool($entityManager);
$schemaTool->createSchema($metadatas);
}

/**
* @Then I should retrieve a stacktrace formatted in JSON
*/
Expand All @@ -73,4 +76,21 @@ public function iShouldRetrieveAStacktraceFormattedInJson()
Assert::keyExists($content, 'detail');
Assert::keyExists($content, 'trace');
}

/**
* @Then the last response is empty
*/
public function theLastResponseIsEmpty()
{
$responseContent = $this->getLastResponse()->getContent();
Assert::isEmpty($responseContent);
}

/**
* @Then the http code is :httpCode
*/
public function theHttpCodeIs($httpCode)
{
Assert::same($this->getLastResponse()->getStatusCode(), (int) $httpCode);
}
}
2 changes: 1 addition & 1 deletion tests/Melodiia/Crud/FilterCollectionFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function setUp(): void

public function testItCreatesCollection()
{
$subject = new FilterCollectionFactory($this->formFactory->reveal(), [new class() implements FilterInterface {
$subject = new FilterCollectionFactory($this->formFactory->reveal(), [new class implements FilterInterface {
public function filter($queryBuilder, FormInterface $form): void
{ /* do nothing */
}
Expand Down
2 changes: 1 addition & 1 deletion tests/Melodiia/EventListener/ExceptionListenerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public function testItHandleExceptionIfItsUnderAMelodiiaRoute()

private function fakeErrorListener()
{
return new class() extends ErrorListener {
return new class extends ErrorListener {
private $isCalled = false;

public function __construct()
Expand Down
2 changes: 1 addition & 1 deletion tests/Melodiia/Form/ApiTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public function testItReturnsDataForEmptyDataInCaseOfDataAvailable()

public function testItSupportsCustomDataMapper()
{
$customDataMapper = new class() extends DomainObjectsDataMapper {
$customDataMapper = new class extends DomainObjectsDataMapper {
private $hasBeenCalled = false;

public function createObject(iterable $form, ?string $dataClass = null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ protected function getForm($name = 'name', $data = null)
public function testItReorderDataInputWithFormData()
{
$formData = [
new class() extends \ArrayObject implements MelodiiaModel {
new class extends \ArrayObject implements MelodiiaModel {
public function __construct()
{
parent::__construct(['hello' => 'yo', 'world' => 'ye']);
Expand All @@ -79,7 +79,7 @@ public function getId()
return 'foo';
}
},
new class() extends \ArrayObject implements MelodiiaModel {
new class extends \ArrayObject implements MelodiiaModel {
public function __construct()
{
parent::__construct(['hello' => 'yoh', 'world' => 'yeh']);
Expand Down Expand Up @@ -113,7 +113,7 @@ public function getId()
public function testItRemovesDataThatDoesNotExistsAnymore()
{
$formData = [
new class() extends \ArrayObject implements MelodiiaModel {
new class extends \ArrayObject implements MelodiiaModel {
public function __construct()
{
parent::__construct(['hello' => 'yo', 'world' => 'ye']);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public function setUp(): void
$this->contextChain = $this->prophesize(ContextBuilderChainInterface::class);
$this->contextChain->buildContext(Argument::cetera())->willReturn([]);
$this->listener = new SerializeOnKernelView($this->serializer->reveal(), $this->contextChain->reveal());
$this->dummyResponse = new class() implements ApiResponse {
$this->dummyResponse = new class implements ApiResponse {
public function httpStatus(): int
{
return 200;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class ContextBuilderFactoryTest extends TestCase

public function testItBuildContextUsingGivenBuilders()
{
$builder1 = new class() implements ContextBuilderInterface {
$builder1 = new class implements ContextBuilderInterface {
public function buildContext(array $context, ApiResponse $response): array
{
$context['foo'] = true;
Expand All @@ -29,7 +29,7 @@ public function supports(ApiResponse $response): bool
return true;
}
};
$builder2 = new class() implements ContextBuilderInterface {
$builder2 = new class implements ContextBuilderInterface {
public function buildContext(array $context, ApiResponse $response): array
{
$context['baz'] = true;
Expand All @@ -42,7 +42,7 @@ public function supports(ApiResponse $response): bool
return false;
}
};
$builder3 = new class() implements ContextBuilderInterface {
$builder3 = new class implements ContextBuilderInterface {
public function buildContext(array $context, ApiResponse $response): array
{
$context['bar'] = true;
Expand Down
2 changes: 2 additions & 0 deletions tests/TestApplication/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ services:
SwagIndustries\Melodiia\Tests\Behat\:
resource: '../../Behat/*'

TestApplication\Controller\ArchiveTodoAction:
tags: ['controller.service_arguments']
TestApplication\Controller\TodoContainsAction:
tags: ['controller.service_arguments']
TestApplication\Controller\SimulateExceptionAction:
Expand Down
18 changes: 17 additions & 1 deletion tests/TestApplication/config/documentation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,23 @@ paths:
400:
description: Invalid input
content: {}

/todos/{todoId}/archive:
patch:
tags:
- todo
summary: Archive a todo
operationId: archiveTodo
parameters:
- name: todoId
in: path
description: ID of the todo to archive
required: true
schema:
type: integer
format: int64
responses:
204:
description: Todo will be archived
components:
schemas:
Todo:
Expand Down
5 changes: 5 additions & 0 deletions tests/TestApplication/config/routing.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ get_todo_contains:
methods: GET
controller: 'TestApplication\Controller\TodoContainsAction'

archive_todo:
path: /todos/{id}/archive
methods: PATCH
controller: 'TestApplication\Controller\ArchiveTodoAction'

get_todo:
path: /todos/{id}
controller: 'melodiia.crud.controller.get'
Expand Down
20 changes: 20 additions & 0 deletions tests/TestApplication/src/Controller/ArchiveTodoAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace TestApplication\Controller;

use Doctrine\ORM\EntityManagerInterface;
use SwagIndustries\Melodiia\Response\OkNoContent;
use TestApplication\Entity\Todo;

class ArchiveTodoAction
{
public function __invoke(Todo $todo, EntityManagerInterface $entityManager)
{
$entityManager->remove($todo);
$entityManager->flush();

return new OkNoContent();
}
}
Loading

0 comments on commit 50011ee

Please sign in to comment.