Skip to content

Commit

Permalink
Merge pull request #38 from outstack/move-email-contents-out-of-list
Browse files Browse the repository at this point in the history
Move email contents out of list
  • Loading branch information
Steve Todd authored Mar 14, 2018
2 parents 9776210 + 0fa7dc7 commit 2ae1975
Show file tree
Hide file tree
Showing 10 changed files with 263 additions and 120 deletions.
22 changes: 22 additions & 0 deletions schemata/outbox_sent_message.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"$schema": "http://json-schema.org/draft-06/schema#",
"type": "object",
"properties": {
"@id": {
"type": "string",
"format": "uri"
},
"template": {
"type": "string"
},
"parameters": {
"type": "object"
},
"resolved": {
"type": "object",
"$ref": "./resolved_message.json"
}

},
"required": ["@id", "template", "parameters", "resolved"]
}
13 changes: 13 additions & 0 deletions schemata/outbox_sent_messages_list.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "http://json-schema.org/draft-06/schema#",
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "./outbox_sent_message.json"
}
}
},
"required": ["items"]
}
17 changes: 17 additions & 0 deletions schemata/participant.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "http://json-schema.org/draft-06/schema#",
"type": "object",
"properties": {
"name": {
"oneOf": [
{"type": "null" },
{"type": "string", "minLength": 1 }
]
},
"email": {
"type": "string",
"format": "email"
}
},
"required": ["email"]
}
40 changes: 40 additions & 0 deletions schemata/resolved_message.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"$schema": "http://json-schema.org/draft-06/schema#",
"type": "object",
"properties": {
"subject": {
"type": "string"
},
"content": {
"type": "object"
},
"sender": {
"$ref": "./participant.json"
},
"recipients": {
"type": "object",
"properties": {
"to": {
"type": "array",
"items": {
"$ref": "./participant.json"
}
},
"cc": {
"type": "array",
"items": {
"$ref": "./participant.json"
}
},
"bcc": {
"type": "array",
"items": {
"$ref": "./participant.json"
}
}
}
}

},
"required": ["subject", "content", "sender", "recipients"]
}
159 changes: 105 additions & 54 deletions src/AppBundle/Controller/OutboxController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
use League\JsonGuard\ValidationError;
use Outstack\Components\ApiProvider\ApiProblemDetails\ApiProblemFactory;
use Outstack\Enveloper\Folders\SentMessagesFolder;
use Outstack\Enveloper\Mail\Message;
use Outstack\Enveloper\Mail\Participants\Participant;
use Outstack\Enveloper\Mail\SentMessage;
use Outstack\Enveloper\Outbox;
use Outstack\Enveloper\PipeprintBridge\Exceptions\PipelineFailed;
use Outstack\Enveloper\Resolution\ParametersFailedSchemaValidation;
Expand All @@ -15,6 +17,7 @@
use Symfony\Component\HttpFoundation\Response;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

class OutboxController extends Controller
{
Expand Down Expand Up @@ -125,39 +128,7 @@ function(ValidationError $e) {
try {
$message = $outbox->preview($payload->template, $payload->parameters);

$acceptableContentTypes = $request->getAcceptableContentTypes();
if (empty($acceptableContentTypes)) {
$acceptableContentTypes[] = 'application/json';
}

foreach ($acceptableContentTypes as $contentType) {
if ($contentType == 'text/plain' && $message->getText()) {

return new Response($message->getText(), 200, ['Content-type' => 'text/plain']);
}
if ($contentType == 'text/html') {
return new Response($message->getHtml(), 200, ['Content-type' => 'text/html']);
}

if ($contentType == 'application/json') {
return new JsonResponse(['html' => $message->getHtml(), 'text' => $message->getText()], 200, ['Content-type' => 'application/json']);
}
}

$availableContentTypes = [
'application/json',
'text/html'
];
if ($message->getText()) {
$availableContentTypes[] = 'text/plain';
}

return $this->problemFactory
->createProblem(406, 'Not Acceptable')
->setDetail('No version of this email matching your Accept header could be found')
->addField('availableContentTypes', $availableContentTypes)
->buildJsonResponse();

return $this->serialiseMessageContentsNegotiatingType($request, $message);

} catch (PipelineFailed $e) {
return $this->problemFactory
Expand All @@ -184,36 +155,39 @@ function(ValidationError $e) {


/**
* @Route("/outbox")
* @Route("/outbox", name="app.outbox.list")
* @Method("GET")
*/
public function listAction()
{
$data = [];
$data = (object) [
'items' => []
];
foreach ($this->sentMessages->listAll() as $sentMessage) {
$resolved = $sentMessage->getResolvedMessage();
$data[] = [
'id' => $sentMessage->getId(),
'template' => $sentMessage->getTemplate(),
'parameters' => $sentMessage->getParameters(),
'resolved' => [
'subject' => $resolved->getSubject(),
'sender' => $this->serialiseParticipant($resolved->getSender()),
'content' => [
'text' => $resolved->getText(),
'html' => $resolved->getHtml()
],
'recipients' => [
'to' => array_map([$this, 'serialiseParticipant'], $resolved->getTo()->getIterator()->getArrayCopy()),
'cc' => array_map([$this, 'serialiseParticipant'], $resolved->getCc()->getIterator()->getArrayCopy()),
'bcc' => array_map([$this, 'serialiseParticipant'], $resolved->getBcc()->getIterator()->getArrayCopy()),
]
]
];
$data->items[] = $this->serialiseSentMessage($sentMessage);
}
return new JsonResponse($data, 200);
}

/**
* @Route("/outbox/{id}", name="app.outbox.view", requirements={"id"="[a-zA-Z0-9]{8}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{12}"})
* @Method("GET")
*/
public function findAction(string $id)
{
$data = $this->serialiseSentMessage($this->sentMessages->find($id));
return new Response(json_encode($data), 200);
}

/**
* @Route("/outbox/{id}/content", name="app.outbox.view.content", requirements={"id"="[a-zA-Z0-9]{8}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{12}"})
* @Method("GET")
*/
public function viewContentAction(Request $request, string $id)
{
return $this->serialiseMessageContentsNegotiatingType($request, $this->sentMessages->find($id)->getResolvedMessage());
}

/**
* @Route("/outbox")
* @Method("DELETE")
Expand All @@ -233,4 +207,81 @@ private function serialiseParticipant(Participant $participant)
'email' => (string) $participant->getEmailAddress()
];
}

private function serialiseSentMessage(SentMessage $sentMessage): array
{
$resolved = $sentMessage->getResolvedMessage();
$messageData = [
'@id' => $this->generateUrl(
'app.outbox.view',
[
'id' => $sentMessage->getId()
],
UrlGeneratorInterface::ABSOLUTE_URL
),
'template' => $sentMessage->getTemplate(),
'parameters' => $sentMessage->getParameters(),
'resolved' => [
'subject' => $resolved->getSubject(),
'sender' => $this->serialiseParticipant($resolved->getSender()),
'content' => [
'@id' => $this->generateUrl('app.outbox.view.content', ['id' => $sentMessage->getId()], UrlGeneratorInterface::ABSOLUTE_URL),
'availableContentTypes' => $this->serialiseAvailableContentTypes($sentMessage->getResolvedMessage())
],
'recipients' => [
'to' => array_map([$this, 'serialiseParticipant'],
$resolved->getTo()->getIterator()->getArrayCopy()),
'cc' => array_map([$this, 'serialiseParticipant'],
$resolved->getCc()->getIterator()->getArrayCopy()),
'bcc' => array_map([$this, 'serialiseParticipant'],
$resolved->getBcc()->getIterator()->getArrayCopy()),
]
]
];
return $messageData;
}

private function serialiseAvailableContentTypes(Message $message)
{
$availableContentTypes = [
'application/json',
'text/html'
];
if ($message->getText()) {
$availableContentTypes[] = 'text/plain';
}

return $availableContentTypes;
}

private function serialiseMessageContentsNegotiatingType(Request $request, Message $message)
{
$acceptableContentTypes = $request->getAcceptableContentTypes();
if (empty($acceptableContentTypes)) {
$acceptableContentTypes[] = 'application/json';
}

foreach ($acceptableContentTypes as $contentType) {
if ($contentType == 'text/plain' && $message->getText()) {

return new Response($message->getText(), 200, ['Content-type' => 'text/plain']);
}
if ($contentType == 'text/html') {
return new Response($message->getHtml(), 200, ['Content-type' => 'text/html']);
}

if ($contentType == 'application/json') {
return new JsonResponse(['html' => $message->getHtml(), 'text' => $message->getText()], 200, ['Content-type' => 'application/json']);
}
}

$availableContentTypes = $this->serialiseAvailableContentTypes($message);

return $this->problemFactory
->createProblem(406, 'Not Acceptable')
->setDetail('No version of this email matching your Accept header could be found')
->addField('availableContentTypes', $availableContentTypes)
->buildJsonResponse();

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,9 @@ public function deleteAll(): void
{
$this->manager->createQuery("DELETE FROM " . SentMessage::class)->execute();
}

public function find(string $id): SentMessage
{
return $this->manager->getRepository(SentMessage::class)->find($id);
}
}
25 changes: 0 additions & 25 deletions src/Outstack/Enveloper/Folders/InMemorySentMessagesFolder.php

This file was deleted.

2 changes: 2 additions & 0 deletions src/Outstack/Enveloper/Folders/SentMessagesFolder.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ public function listAll();

public function deleteAll(): void;

public function find(string $id): SentMessage;

}
27 changes: 27 additions & 0 deletions tests/Functional/AbstractApiTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

namespace Outstack\Enveloper\Tests\Functional;

use Helmich\JsonAssert\JsonAssertions;
use Http\Client\HttpClient;
use League\JsonGuard\ValidationError;
use Outstack\Components\HttpInterop\Psr7\ServerEnvironmentRequestFactory;
use Outstack\Components\SymfonyKernelHttpClient\SymfonyKernelHttpClient;
use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory;
Expand All @@ -21,6 +23,31 @@ abstract class AbstractApiTestCase extends KernelTestCase
*/
protected $client;

protected $baseUri = "http://enveloper.test";

protected function assertJsonDocumentMatchesSchema($document, $schema)
{
$projectDir = __DIR__.'/../../';
$dereferencer = \League\JsonReference\Dereferencer::draft6();
$schema = $dereferencer->dereference("file://{$projectDir}/schemata/$schema");

$validator = new \League\JsonGuard\Validator($document, $schema);

$this->assertFalse(
$validator->fails(),
implode(
"\n",
array_map(
function(ValidationError $error) {
return $error->getDataPath() . ": " . $error->getMessage() . json_encode($error->getData());
},
$validator->errors()
)
)
);
}


public function setUp()
{
parent::setUp();
Expand Down
Loading

0 comments on commit 2ae1975

Please sign in to comment.