mirror of
https://github.com/shlinkio/shlink.git
synced 2025-03-14 04:00:57 +03:00
commit
536309afb6
31 changed files with 711 additions and 37 deletions
80
data/migrations/Version20160820191203.php
Normal file
80
data/migrations/Version20160820191203.php
Normal file
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Migrations\AbstractMigration;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
class Version20160820191203 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
{
|
||||
// Check if the tables already exist
|
||||
$tables = $schema->getTables();
|
||||
foreach ($tables as $table) {
|
||||
if ($table->getName() === 'tags') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->createTagsTable($schema);
|
||||
$this->createShortUrlsInTagsTable($schema);
|
||||
}
|
||||
|
||||
protected function createTagsTable(Schema $schema)
|
||||
{
|
||||
$table = $schema->createTable('tags');
|
||||
$table->addColumn('id', Type::BIGINT, [
|
||||
'unsigned' => true,
|
||||
'autoincrement' => true,
|
||||
'notnull' => true,
|
||||
]);
|
||||
$table->addColumn('name', Type::STRING, [
|
||||
'length' => 255,
|
||||
'notnull' => true,
|
||||
]);
|
||||
$table->addUniqueIndex(['name']);
|
||||
|
||||
$table->setPrimaryKey(['id']);
|
||||
}
|
||||
|
||||
protected function createShortUrlsInTagsTable(Schema $schema)
|
||||
{
|
||||
$table = $schema->createTable('short_urls_in_tags');
|
||||
$table->addColumn('short_url_id', Type::BIGINT, [
|
||||
'unsigned' => true,
|
||||
'notnull' => true,
|
||||
]);
|
||||
$table->addColumn('tag_id', Type::BIGINT, [
|
||||
'unsigned' => true,
|
||||
'notnull' => true,
|
||||
]);
|
||||
|
||||
$table->addForeignKeyConstraint('tags', ['tag_id'], ['id'], [
|
||||
'onDelete' => 'CASCADE',
|
||||
'onUpdate' => 'RESTRICT',
|
||||
]);
|
||||
$table->addForeignKeyConstraint('short_urls', ['short_url_id'], ['id'], [
|
||||
'onDelete' => 'CASCADE',
|
||||
'onUpdate' => 'RESTRICT',
|
||||
]);
|
||||
|
||||
$table->setPrimaryKey(['short_url_id', 'tag_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function down(Schema $schema)
|
||||
{
|
||||
$schema->dropTable('short_urls_in_tags');
|
||||
$schema->dropTable('tags');
|
||||
}
|
||||
}
|
Binary file not shown.
|
@ -1,8 +1,8 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Shlink 1.0\n"
|
||||
"POT-Creation-Date: 2016-08-18 17:24+0200\n"
|
||||
"PO-Revision-Date: 2016-08-18 17:26+0200\n"
|
||||
"POT-Creation-Date: 2016-08-21 18:16+0200\n"
|
||||
"PO-Revision-Date: 2016-08-21 18:16+0200\n"
|
||||
"Last-Translator: Alejandro Celaya <alejandro@alejandrocelaya.com>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: es_ES\n"
|
||||
|
@ -104,6 +104,9 @@ msgstr ""
|
|||
msgid "The long URL to parse"
|
||||
msgstr "La URL larga a procesar"
|
||||
|
||||
msgid "Tags to apply to the new short URL"
|
||||
msgstr "Etiquetas a aplicar a la nueva URL acortada"
|
||||
|
||||
msgid "A long URL was not provided. Which URL do you want to shorten?:"
|
||||
msgstr "No se ha proporcionado una URL larga. ¿Qué URL deseas acortar?"
|
||||
|
||||
|
@ -159,6 +162,9 @@ msgstr "Listar todas las URLs cortas"
|
|||
msgid "The first page to list (%s items per page)"
|
||||
msgstr "La primera página a listar (%s elementos por página)"
|
||||
|
||||
msgid "Whether to display the tags or not"
|
||||
msgstr "Si se desea mostrar las etiquetas o no"
|
||||
|
||||
msgid "Short code"
|
||||
msgstr "Código corto"
|
||||
|
||||
|
@ -171,6 +177,9 @@ msgstr "Fecha de creación"
|
|||
msgid "Visits count"
|
||||
msgstr "Número de visitas"
|
||||
|
||||
msgid "Tags"
|
||||
msgstr "Etiquetas"
|
||||
|
||||
msgid "You have reached last page"
|
||||
msgstr "Has alcanzado la última página"
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ use Symfony\Component\Console\Command\Command;
|
|||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
use Zend\Diactoros\Uri;
|
||||
|
@ -54,7 +55,13 @@ class GenerateShortcodeCommand extends Command
|
|||
->setDescription(
|
||||
$this->translator->translate('Generates a short code for provided URL and returns the short URL')
|
||||
)
|
||||
->addArgument('longUrl', InputArgument::REQUIRED, $this->translator->translate('The long URL to parse'));
|
||||
->addArgument('longUrl', InputArgument::REQUIRED, $this->translator->translate('The long URL to parse'))
|
||||
->addOption(
|
||||
'tags',
|
||||
't',
|
||||
InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL,
|
||||
$this->translator->translate('Tags to apply to the new short URL')
|
||||
);
|
||||
}
|
||||
|
||||
public function interact(InputInterface $input, OutputInterface $output)
|
||||
|
@ -80,6 +87,13 @@ class GenerateShortcodeCommand extends Command
|
|||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$longUrl = $input->getArgument('longUrl');
|
||||
$tags = $input->getOption('tags');
|
||||
$processedTags = [];
|
||||
foreach ($tags as $key => $tag) {
|
||||
$explodedTags = explode(',', $tag);
|
||||
$processedTags = array_merge($processedTags, $explodedTags);
|
||||
}
|
||||
$tags = $processedTags;
|
||||
|
||||
try {
|
||||
if (! isset($longUrl)) {
|
||||
|
@ -87,10 +101,10 @@ class GenerateShortcodeCommand extends Command
|
|||
return;
|
||||
}
|
||||
|
||||
$shortCode = $this->urlShortener->urlToShortCode(new Uri($longUrl));
|
||||
$shortCode = $this->urlShortener->urlToShortCode(new Uri($longUrl), $tags);
|
||||
$shortUrl = (new Uri())->withPath($shortCode)
|
||||
->withScheme($this->domainConfig['schema'])
|
||||
->withHost($this->domainConfig['hostname']);
|
||||
->withScheme($this->domainConfig['schema'])
|
||||
->withHost($this->domainConfig['hostname']);
|
||||
|
||||
$output->writeln([
|
||||
sprintf('%s <info>%s</info>', $this->translator->translate('Processed URL:'), $longUrl),
|
||||
|
|
|
@ -55,12 +55,20 @@ class ListShortcodesCommand extends Command
|
|||
PaginableRepositoryAdapter::ITEMS_PER_PAGE
|
||||
),
|
||||
1
|
||||
)
|
||||
->addOption(
|
||||
'tags',
|
||||
't',
|
||||
InputOption::VALUE_NONE,
|
||||
$this->translator->translate('Whether to display the tags or not')
|
||||
);
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$page = intval($input->getOption('page'));
|
||||
$showTags = $input->getOption('tags');
|
||||
|
||||
/** @var QuestionHelper $helper */
|
||||
$helper = $this->getHelper('question');
|
||||
|
||||
|
@ -68,15 +76,31 @@ class ListShortcodesCommand extends Command
|
|||
$result = $this->shortUrlService->listShortUrls($page);
|
||||
$page++;
|
||||
$table = new Table($output);
|
||||
$table->setHeaders([
|
||||
|
||||
$headers = [
|
||||
$this->translator->translate('Short code'),
|
||||
$this->translator->translate('Original URL'),
|
||||
$this->translator->translate('Date created'),
|
||||
$this->translator->translate('Visits count'),
|
||||
]);
|
||||
];
|
||||
if ($showTags) {
|
||||
$headers[] = $this->translator->translate('Tags');
|
||||
}
|
||||
$table->setHeaders($headers);
|
||||
|
||||
foreach ($result as $row) {
|
||||
$table->addRow(array_values($row->jsonSerialize()));
|
||||
$shortUrl = $row->jsonSerialize();
|
||||
if ($showTags) {
|
||||
$shortUrl['tags'] = [];
|
||||
foreach ($row->getTags() as $tag) {
|
||||
$shortUrl['tags'][] = $tag->getName();
|
||||
}
|
||||
$shortUrl['tags'] = implode(', ', $shortUrl['tags']);
|
||||
} else {
|
||||
unset($shortUrl['tags']);
|
||||
}
|
||||
|
||||
$table->addRow(array_values($shortUrl));
|
||||
}
|
||||
$table->render();
|
||||
|
||||
|
|
|
@ -39,8 +39,8 @@ class GenerateShortcodeCommandTest extends TestCase
|
|||
*/
|
||||
public function properShortCodeIsCreatedIfLongUrlIsCorrect()
|
||||
{
|
||||
$this->urlShortener->urlToShortCode(Argument::any())->willReturn('abc123')
|
||||
->shouldBeCalledTimes(1);
|
||||
$this->urlShortener->urlToShortCode(Argument::cetera())->willReturn('abc123')
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$this->commandTester->execute([
|
||||
'command' => 'shortcode:generate',
|
||||
|
@ -55,8 +55,8 @@ class GenerateShortcodeCommandTest extends TestCase
|
|||
*/
|
||||
public function exceptionWhileParsingLongUrlOutputsError()
|
||||
{
|
||||
$this->urlShortener->urlToShortCode(Argument::any())->willThrow(new InvalidUrlException())
|
||||
->shouldBeCalledTimes(1);
|
||||
$this->urlShortener->urlToShortCode(Argument::cetera())->willThrow(new InvalidUrlException())
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$this->commandTester->execute([
|
||||
'command' => 'shortcode:generate',
|
||||
|
|
|
@ -108,6 +108,23 @@ class ListShortcodesCommandTest extends TestCase
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function ifTagsFlagIsProvidedTagsColumnIsIncluded()
|
||||
{
|
||||
$this->questionHelper->setInputStream($this->getInputStream('\n'));
|
||||
$this->shortUrlService->listShortUrls(1)->willReturn(new Paginator(new ArrayAdapter()))
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$this->commandTester->execute([
|
||||
'command' => 'shortcode:list',
|
||||
'--tags' => true,
|
||||
]);
|
||||
$output = $this->commandTester->getDisplay();
|
||||
$this->assertTrue(strpos($output, 'Tags') > 0);
|
||||
}
|
||||
|
||||
protected function getInputStream($inputData)
|
||||
{
|
||||
$stream = fopen('php://memory', 'r+', false);
|
||||
|
|
Binary file not shown.
|
@ -1,9 +1,9 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Shlink 1.0\n"
|
||||
"POT-Creation-Date: 2016-07-21 16:50+0200\n"
|
||||
"PO-Revision-Date: 2016-07-21 16:51+0200\n"
|
||||
"Last-Translator: \n"
|
||||
"POT-Creation-Date: 2016-08-21 18:17+0200\n"
|
||||
"PO-Revision-Date: 2016-08-21 18:17+0200\n"
|
||||
"Last-Translator: Alejandro Celaya <alejandro@alejandrocelaya.com>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: es_ES\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
|
|
@ -42,6 +42,16 @@ class ShortUrl extends AbstractEntity implements \JsonSerializable
|
|||
* @ORM\OneToMany(targetEntity=Visit::class, mappedBy="shortUrl", fetch="EXTRA_LAZY")
|
||||
*/
|
||||
protected $visits;
|
||||
/**
|
||||
* @var Collection|Tag[]
|
||||
* @ORM\ManyToMany(targetEntity=Tag::class, cascade={"persist"})
|
||||
* @ORM\JoinTable(name="short_urls_in_tags", joinColumns={
|
||||
* @ORM\JoinColumn(name="short_url_id", referencedColumnName="id")
|
||||
* }, inverseJoinColumns={
|
||||
* @ORM\JoinColumn(name="tag_id", referencedColumnName="id")
|
||||
* })
|
||||
*/
|
||||
protected $tags;
|
||||
|
||||
/**
|
||||
* ShortUrl constructor.
|
||||
|
@ -51,6 +61,7 @@ class ShortUrl extends AbstractEntity implements \JsonSerializable
|
|||
$this->setDateCreated(new \DateTime());
|
||||
$this->setVisits(new ArrayCollection());
|
||||
$this->setShortCode('');
|
||||
$this->tags = new ArrayCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -125,6 +136,34 @@ class ShortUrl extends AbstractEntity implements \JsonSerializable
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection|Tag[]
|
||||
*/
|
||||
public function getTags()
|
||||
{
|
||||
return $this->tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection|Tag[] $tags
|
||||
* @return $this
|
||||
*/
|
||||
public function setTags($tags)
|
||||
{
|
||||
$this->tags = $tags;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Tag $tag
|
||||
* @return $this
|
||||
*/
|
||||
public function addTag(Tag $tag)
|
||||
{
|
||||
$this->tags->add($tag);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify data which should be serialized to JSON
|
||||
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php
|
||||
|
@ -139,6 +178,7 @@ class ShortUrl extends AbstractEntity implements \JsonSerializable
|
|||
'originalUrl' => $this->originalUrl,
|
||||
'dateCreated' => isset($this->dateCreated) ? $this->dateCreated->format(\DateTime::ISO8601) : null,
|
||||
'visitsCount' => count($this->visits),
|
||||
'tags' => $this->tags->toArray(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
52
module/Core/src/Entity/Tag.php
Normal file
52
module/Core/src/Entity/Tag.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
namespace Shlinkio\Shlink\Core\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Shlinkio\Shlink\Common\Entity\AbstractEntity;
|
||||
|
||||
/**
|
||||
* Class Tag
|
||||
* @author
|
||||
* @link
|
||||
*
|
||||
* @ORM\Entity()
|
||||
* @ORM\Table(name="tags")
|
||||
*/
|
||||
class Tag extends AbstractEntity implements \JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @ORM\Column(unique=true)
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return $this
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify data which should be serialized to JSON
|
||||
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php
|
||||
* @return mixed data which can be serialized by <b>json_encode</b>,
|
||||
* which is a value of any type other than a resource.
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ use Shlinkio\Shlink\Common\Exception\RuntimeException;
|
|||
|
||||
class InvalidShortCodeException extends RuntimeException
|
||||
{
|
||||
public static function fromShortCode($shortCode, $charSet, \Exception $previous = null)
|
||||
public static function fromCharset($shortCode, $charSet, \Exception $previous = null)
|
||||
{
|
||||
$code = isset($previous) ? $previous->getCode() : -1;
|
||||
return new static(
|
||||
|
@ -14,4 +14,9 @@ class InvalidShortCodeException extends RuntimeException
|
|||
$previous
|
||||
);
|
||||
}
|
||||
|
||||
public static function fromNotFoundShortCode($shortCode)
|
||||
{
|
||||
return new static(sprintf('Provided short code "%s" does not belong to a short URL', $shortCode));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,11 +5,15 @@ use Acelaya\ZsmAnnotatedServices\Annotation\Inject;
|
|||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Shlinkio\Shlink\Common\Paginator\Adapter\PaginableRepositoryAdapter;
|
||||
use Shlinkio\Shlink\Core\Entity\ShortUrl;
|
||||
use Shlinkio\Shlink\Core\Exception\InvalidShortCodeException;
|
||||
use Shlinkio\Shlink\Core\Repository\ShortUrlRepository;
|
||||
use Shlinkio\Shlink\Core\Util\TagManagerTrait;
|
||||
use Zend\Paginator\Paginator;
|
||||
|
||||
class ShortUrlService implements ShortUrlServiceInterface
|
||||
{
|
||||
use TagManagerTrait;
|
||||
|
||||
/**
|
||||
* @var EntityManagerInterface
|
||||
*/
|
||||
|
@ -40,4 +44,26 @@ class ShortUrlService implements ShortUrlServiceInterface
|
|||
|
||||
return $paginator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $shortCode
|
||||
* @param string[] $tags
|
||||
* @return ShortUrl
|
||||
* @throws InvalidShortCodeException
|
||||
*/
|
||||
public function setTagsByShortCode($shortCode, array $tags = [])
|
||||
{
|
||||
/** @var ShortUrl $shortUrl */
|
||||
$shortUrl = $this->em->getRepository(ShortUrl::class)->findOneBy([
|
||||
'shortCode' => $shortCode,
|
||||
]);
|
||||
if (! isset($shortUrl)) {
|
||||
throw InvalidShortCodeException::fromNotFoundShortCode($shortCode);
|
||||
}
|
||||
|
||||
$shortUrl->setTags($this->tagNamesToEntities($this->em, $tags));
|
||||
$this->em->flush();
|
||||
|
||||
return $shortUrl;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
namespace Shlinkio\Shlink\Core\Service;
|
||||
|
||||
use Shlinkio\Shlink\Core\Entity\ShortUrl;
|
||||
use Shlinkio\Shlink\Core\Exception\InvalidShortCodeException;
|
||||
use Zend\Paginator\Paginator;
|
||||
|
||||
interface ShortUrlServiceInterface
|
||||
|
@ -11,4 +12,12 @@ interface ShortUrlServiceInterface
|
|||
* @return ShortUrl[]|Paginator
|
||||
*/
|
||||
public function listShortUrls($page = 1);
|
||||
|
||||
/**
|
||||
* @param string $shortCode
|
||||
* @param string[] $tags
|
||||
* @return ShortUrl
|
||||
* @throws InvalidShortCodeException
|
||||
*/
|
||||
public function setTagsByShortCode($shortCode, array $tags = []);
|
||||
}
|
||||
|
|
|
@ -12,9 +12,12 @@ use Shlinkio\Shlink\Common\Exception\RuntimeException;
|
|||
use Shlinkio\Shlink\Core\Entity\ShortUrl;
|
||||
use Shlinkio\Shlink\Core\Exception\InvalidShortCodeException;
|
||||
use Shlinkio\Shlink\Core\Exception\InvalidUrlException;
|
||||
use Shlinkio\Shlink\Core\Util\TagManagerTrait;
|
||||
|
||||
class UrlShortener implements UrlShortenerInterface
|
||||
{
|
||||
use TagManagerTrait;
|
||||
|
||||
const DEFAULT_CHARS = '123456789bcdfghjkmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ';
|
||||
|
||||
/**
|
||||
|
@ -59,15 +62,16 @@ class UrlShortener implements UrlShortenerInterface
|
|||
* Creates and persists a unique shortcode generated for provided url
|
||||
*
|
||||
* @param UriInterface $url
|
||||
* @param string[] $tags
|
||||
* @return string
|
||||
* @throws InvalidUrlException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function urlToShortCode(UriInterface $url)
|
||||
public function urlToShortCode(UriInterface $url, array $tags = [])
|
||||
{
|
||||
// If the url already exists in the database, just return its short code
|
||||
$shortUrl = $this->em->getRepository(ShortUrl::class)->findOneBy([
|
||||
'originalUrl' => $url
|
||||
'originalUrl' => $url,
|
||||
]);
|
||||
if (isset($shortUrl)) {
|
||||
return $shortUrl->getShortCode();
|
||||
|
@ -88,7 +92,8 @@ class UrlShortener implements UrlShortenerInterface
|
|||
|
||||
// Generate the short code and persist it
|
||||
$shortCode = $this->convertAutoincrementIdToShortCode($shortUrl->getId());
|
||||
$shortUrl->setShortCode($shortCode);
|
||||
$shortUrl->setShortCode($shortCode)
|
||||
->setTags($this->tagNamesToEntities($this->em, $tags));
|
||||
$this->em->flush();
|
||||
|
||||
$this->em->commit();
|
||||
|
@ -156,7 +161,7 @@ class UrlShortener implements UrlShortenerInterface
|
|||
|
||||
// Validate short code format
|
||||
if (! preg_match('|[' . $this->chars . "]+|", $shortCode)) {
|
||||
throw InvalidShortCodeException::fromShortCode($shortCode, $this->chars);
|
||||
throw InvalidShortCodeException::fromCharset($shortCode, $this->chars);
|
||||
}
|
||||
|
||||
/** @var ShortUrl $shortUrl */
|
||||
|
|
|
@ -12,11 +12,12 @@ interface UrlShortenerInterface
|
|||
* Creates and persists a unique shortcode generated for provided url
|
||||
*
|
||||
* @param UriInterface $url
|
||||
* @param string[] $tags
|
||||
* @return string
|
||||
* @throws InvalidUrlException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function urlToShortCode(UriInterface $url);
|
||||
public function urlToShortCode(UriInterface $url, array $tags = []);
|
||||
|
||||
/**
|
||||
* Tries to find the mapped URL for provided short code. Returns null if not found
|
||||
|
|
38
module/Core/src/Util/TagManagerTrait.php
Normal file
38
module/Core/src/Util/TagManagerTrait.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
namespace Shlinkio\Shlink\Core\Util;
|
||||
|
||||
use Doctrine\Common\Collections;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Shlinkio\Shlink\Core\Entity\Tag;
|
||||
|
||||
trait TagManagerTrait
|
||||
{
|
||||
/**
|
||||
* @param EntityManagerInterface $em
|
||||
* @param string[] $tags
|
||||
* @return Collections\Collection|Tag[]
|
||||
*/
|
||||
protected function tagNamesToEntities(EntityManagerInterface $em, array $tags)
|
||||
{
|
||||
$entities = [];
|
||||
foreach ($tags as $tagName) {
|
||||
$tagName = $this->normalizeTagName($tagName);
|
||||
$tag = $em->getRepository(Tag::class)->findOneBy(['name' => $tagName]) ?: (new Tag())->setName($tagName);
|
||||
$em->persist($tag);
|
||||
$entities[] = $tag;
|
||||
}
|
||||
|
||||
return new Collections\ArrayCollection($entities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag names are trimmed, lowercased and spaces are replaced by dashes
|
||||
*
|
||||
* @param string $tagName
|
||||
* @return string
|
||||
*/
|
||||
protected function normalizeTagName($tagName)
|
||||
{
|
||||
return str_replace(' ', '-', strtolower(trim($tagName)));
|
||||
}
|
||||
}
|
18
module/Core/test/Entity/TagTest.php
Normal file
18
module/Core/test/Entity/TagTest.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
namespace ShlinkioTest\Shlink\Core\Entity;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Shlinkio\Shlink\Core\Entity\Tag;
|
||||
|
||||
class TagTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function jsonSerializationOfTagsReturnsItsName()
|
||||
{
|
||||
$tag = new Tag();
|
||||
$tag->setName('This is my name');
|
||||
$this->assertEquals($tag->getName(), $tag->jsonSerialize());
|
||||
}
|
||||
}
|
|
@ -2,10 +2,12 @@
|
|||
namespace ShlinkioTest\Shlink\Core\Service;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Shlinkio\Shlink\Core\Entity\ShortUrl;
|
||||
use Shlinkio\Shlink\Core\Entity\Tag;
|
||||
use Shlinkio\Shlink\Core\Repository\ShortUrlRepository;
|
||||
use Shlinkio\Shlink\Core\Service\ShortUrlService;
|
||||
|
||||
|
@ -23,6 +25,8 @@ class ShortUrlServiceTest extends TestCase
|
|||
public function setUp()
|
||||
{
|
||||
$this->em = $this->prophesize(EntityManagerInterface::class);
|
||||
$this->em->persist(Argument::any())->willReturn(null);
|
||||
$this->em->flush()->willReturn(null);
|
||||
$this->service = new ShortUrlService($this->em->reveal());
|
||||
}
|
||||
|
||||
|
@ -46,4 +50,40 @@ class ShortUrlServiceTest extends TestCase
|
|||
$list = $this->service->listShortUrls();
|
||||
$this->assertEquals(4, $list->getCurrentItemCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @expectedException \Shlinkio\Shlink\Core\Exception\InvalidShortCodeException
|
||||
*/
|
||||
public function exceptionIsThrownWhenSettingTagsOnInvalidShortcode()
|
||||
{
|
||||
$shortCode = 'abc123';
|
||||
$repo = $this->prophesize(ShortUrlRepository::class);
|
||||
$repo->findOneBy(['shortCode' => $shortCode])->willReturn(null)
|
||||
->shouldBeCalledTimes(1);
|
||||
$this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal());
|
||||
|
||||
$this->service->setTagsByShortCode($shortCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function providedTagsAreGetFromRepoAndSetToTheShortUrl()
|
||||
{
|
||||
$shortUrl = $this->prophesize(ShortUrl::class);
|
||||
$shortUrl->setTags(Argument::any())->shouldBeCalledTimes(1);
|
||||
$shortCode = 'abc123';
|
||||
$repo = $this->prophesize(ShortUrlRepository::class);
|
||||
$repo->findOneBy(['shortCode' => $shortCode])->willReturn($shortUrl->reveal())
|
||||
->shouldBeCalledTimes(1);
|
||||
$this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal());
|
||||
|
||||
$tagRepo = $this->prophesize(EntityRepository::class);
|
||||
$tagRepo->findOneBy(['name' => 'foo'])->willReturn(new Tag())->shouldbeCalledTimes(1);
|
||||
$tagRepo->findOneBy(['name' => 'bar'])->willReturn(null)->shouldbeCalledTimes(1);
|
||||
$this->em->getRepository(Tag::class)->willReturn($tagRepo->reveal());
|
||||
|
||||
$this->service->setTagsByShortCode($shortCode, ['foo', 'bar']);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,9 @@ return [
|
|||
Action\ResolveUrlAction::class => AnnotatedFactory::class,
|
||||
Action\GetVisitsAction::class => AnnotatedFactory::class,
|
||||
Action\ListShortcodesAction::class => AnnotatedFactory::class,
|
||||
Action\EditTagsAction::class => AnnotatedFactory::class,
|
||||
|
||||
Middleware\BodyParserMiddleware::class => AnnotatedFactory::class,
|
||||
Middleware\CrossDomainMiddleware::class => InvokableFactory::class,
|
||||
Middleware\CheckAuthenticationMiddleware::class => AnnotatedFactory::class,
|
||||
],
|
||||
|
|
|
@ -7,6 +7,7 @@ return [
|
|||
'rest' => [
|
||||
'path' => '/rest',
|
||||
'middleware' => [
|
||||
Middleware\BodyParserMiddleware::class,
|
||||
Middleware\CheckAuthenticationMiddleware::class,
|
||||
Middleware\CrossDomainMiddleware::class,
|
||||
],
|
||||
|
|
|
@ -34,6 +34,12 @@ return [
|
|||
'middleware' => Action\GetVisitsAction::class,
|
||||
'allowed_methods' => ['GET', 'OPTIONS'],
|
||||
],
|
||||
[
|
||||
'name' => 'rest-edit-tags',
|
||||
'path' => '/rest/short-codes/{shortCode}/tags',
|
||||
'middleware' => Action\EditTagsAction::class,
|
||||
'allowed_methods' => ['PUT', 'OPTIONS'],
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
|
|
Binary file not shown.
|
@ -1,8 +1,8 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Shlink 1.0\n"
|
||||
"POT-Creation-Date: 2016-08-18 17:27+0200\n"
|
||||
"PO-Revision-Date: 2016-08-18 17:27+0200\n"
|
||||
"POT-Creation-Date: 2016-08-21 18:17+0200\n"
|
||||
"PO-Revision-Date: 2016-08-21 18:17+0200\n"
|
||||
"Last-Translator: Alejandro Celaya <alejandro@alejandrocelaya.com>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: es_ES\n"
|
||||
|
@ -35,14 +35,17 @@ msgstr "La URL proporcionada \"%s\" es inválida. Prueba con una diferente."
|
|||
msgid "Unexpected error occurred"
|
||||
msgstr "Ocurrió un error inesperado"
|
||||
|
||||
#, php-format
|
||||
msgid "Provided short code %s does not exist"
|
||||
msgstr "El código corto \"%s\" proporcionado no existe"
|
||||
msgid "A list of tags was not provided"
|
||||
msgstr "No se ha proporcionado una lista de etiquetas"
|
||||
|
||||
#, php-format
|
||||
msgid "No URL found for short code \"%s\""
|
||||
msgstr "No se ha encontrado ninguna URL para el código corto \"%s\""
|
||||
|
||||
#, php-format
|
||||
msgid "Provided short code %s does not exist"
|
||||
msgstr "El código corto \"%s\" proporcionado no existe"
|
||||
|
||||
#, php-format
|
||||
msgid "Provided short code \"%s\" has an invalid format"
|
||||
msgstr "El código corto proporcionado \"%s\" tiene un formato no inválido"
|
||||
|
|
|
@ -16,7 +16,7 @@ use Zend\I18n\Translator\TranslatorInterface;
|
|||
class CreateShortcodeAction extends AbstractRestAction
|
||||
{
|
||||
/**
|
||||
* @var UrlShortener|UrlShortenerInterface
|
||||
* @var UrlShortenerInterface
|
||||
*/
|
||||
private $urlShortener;
|
||||
/**
|
||||
|
@ -31,7 +31,7 @@ class CreateShortcodeAction extends AbstractRestAction
|
|||
/**
|
||||
* GenerateShortcodeMiddleware constructor.
|
||||
*
|
||||
* @param UrlShortenerInterface|UrlShortener $urlShortener
|
||||
* @param UrlShortenerInterface $urlShortener
|
||||
* @param TranslatorInterface $translator
|
||||
* @param array $domainConfig
|
||||
* @param LoggerInterface|null $logger
|
||||
|
@ -66,9 +66,10 @@ class CreateShortcodeAction extends AbstractRestAction
|
|||
], 400);
|
||||
}
|
||||
$longUrl = $postData['longUrl'];
|
||||
$tags = isset($postData['tags']) && is_array($postData['tags']) ? $postData['tags'] : [];
|
||||
|
||||
try {
|
||||
$shortCode = $this->urlShortener->urlToShortCode(new Uri($longUrl));
|
||||
$shortCode = $this->urlShortener->urlToShortCode(new Uri($longUrl), $tags);
|
||||
$shortUrl = (new Uri())->withPath($shortCode)
|
||||
->withScheme($this->domainConfig['schema'])
|
||||
->withHost($this->domainConfig['hostname']);
|
||||
|
|
73
module/Rest/src/Action/EditTagsAction.php
Normal file
73
module/Rest/src/Action/EditTagsAction.php
Normal file
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
namespace Shlinkio\Shlink\Rest\Action;
|
||||
|
||||
use Acelaya\ZsmAnnotatedServices\Annotation\Inject;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shlinkio\Shlink\Core\Exception\InvalidShortCodeException;
|
||||
use Shlinkio\Shlink\Core\Service\ShortUrlService;
|
||||
use Shlinkio\Shlink\Core\Service\ShortUrlServiceInterface;
|
||||
use Shlinkio\Shlink\Rest\Util\RestUtils;
|
||||
use Zend\Diactoros\Response\JsonResponse;
|
||||
use Zend\I18n\Translator\TranslatorInterface;
|
||||
|
||||
class EditTagsAction extends AbstractRestAction
|
||||
{
|
||||
/**
|
||||
* @var ShortUrlServiceInterface
|
||||
*/
|
||||
private $shortUrlService;
|
||||
/**
|
||||
* @var TranslatorInterface
|
||||
*/
|
||||
private $translator;
|
||||
|
||||
/**
|
||||
* EditTagsAction constructor.
|
||||
* @param ShortUrlServiceInterface $shortUrlService
|
||||
* @param TranslatorInterface $translator
|
||||
* @param LoggerInterface|null $logger
|
||||
*
|
||||
* @Inject({ShortUrlService::class, "translator", "Logger_Shlink"})
|
||||
*/
|
||||
public function __construct(
|
||||
ShortUrlServiceInterface $shortUrlService,
|
||||
TranslatorInterface $translator,
|
||||
LoggerInterface $logger = null
|
||||
) {
|
||||
parent::__construct($logger);
|
||||
$this->shortUrlService = $shortUrlService;
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @param callable|null $out
|
||||
* @return null|Response
|
||||
*/
|
||||
protected function dispatch(Request $request, Response $response, callable $out = null)
|
||||
{
|
||||
$shortCode = $request->getAttribute('shortCode');
|
||||
$bodyParams = $request->getParsedBody();
|
||||
|
||||
if (! isset($bodyParams['tags'])) {
|
||||
return new JsonResponse([
|
||||
'error' => RestUtils::INVALID_ARGUMENT_ERROR,
|
||||
'message' => $this->translator->translate('A list of tags was not provided'),
|
||||
], 400);
|
||||
}
|
||||
$tags = $bodyParams['tags'];
|
||||
|
||||
try {
|
||||
$shortUrl = $this->shortUrlService->setTagsByShortCode($shortCode, $tags);
|
||||
return new JsonResponse(['tags' => $shortUrl->getTags()->toArray()]);
|
||||
} catch (InvalidShortCodeException $e) {
|
||||
return new JsonResponse([
|
||||
'error' => RestUtils::getRestErrorCodeFromException($e),
|
||||
'message' => sprintf($this->translator->translate('No URL found for short code "%s"'), $shortCode),
|
||||
], 404);
|
||||
}
|
||||
}
|
||||
}
|
52
module/Rest/src/Middleware/BodyParserMiddleware.php
Normal file
52
module/Rest/src/Middleware/BodyParserMiddleware.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
namespace Shlinkio\Shlink\Rest\Middleware;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Zend\Stratigility\MiddlewareInterface;
|
||||
|
||||
class BodyParserMiddleware implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* Process an incoming request and/or response.
|
||||
*
|
||||
* Accepts a server-side request and a response instance, and does
|
||||
* something with them.
|
||||
*
|
||||
* If the response is not complete and/or further processing would not
|
||||
* interfere with the work done in the middleware, or if the middleware
|
||||
* wants to delegate to another process, it can use the `$out` callable
|
||||
* if present.
|
||||
*
|
||||
* If the middleware does not return a value, execution of the current
|
||||
* request is considered complete, and the response instance provided will
|
||||
* be considered the response to return.
|
||||
*
|
||||
* Alternately, the middleware may return a response instance.
|
||||
*
|
||||
* Often, middleware will `return $out();`, with the assumption that a
|
||||
* later middleware will return a response.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @param null|callable $out
|
||||
* @return null|Response
|
||||
*/
|
||||
public function __invoke(Request $request, Response $response, callable $out = null)
|
||||
{
|
||||
$method = $request->getMethod();
|
||||
if (! in_array($method, ['PUT', 'PATCH'])) {
|
||||
return $out($request, $response);
|
||||
}
|
||||
|
||||
$contentType = $request->getHeaderLine('Content-type');
|
||||
$rawBody = (string) $request->getBody();
|
||||
if (in_array($contentType, ['application/json', 'text/json', 'application/x-json'])) {
|
||||
return $out($request->withParsedBody(json_decode($rawBody, true)), $response);
|
||||
}
|
||||
|
||||
$parsedBody = [];
|
||||
parse_str($rawBody, $parsedBody);
|
||||
return $out($request->withParsedBody($parsedBody), $response);
|
||||
}
|
||||
}
|
|
@ -48,7 +48,7 @@ class CrossDomainMiddleware implements MiddlewareInterface
|
|||
|
||||
// Add OPTIONS-specific headers
|
||||
foreach ([
|
||||
'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, OPTIONS', // TODO Should be based on path
|
||||
'Access-Control-Allow-Methods' => 'GET,POST,PUT,DELETE,OPTIONS', // TODO Should be based on path
|
||||
'Access-Control-Max-Age' => '1000',
|
||||
'Access-Control-Allow-Headers' => $request->getHeaderLine('Access-Control-Request-Headers'),
|
||||
] as $key => $value) {
|
||||
|
|
|
@ -47,8 +47,9 @@ class CreateShortcodeActionTest extends TestCase
|
|||
*/
|
||||
public function properShortcodeConversionReturnsData()
|
||||
{
|
||||
$this->urlShortener->urlToShortCode(Argument::type(Uri::class))->willReturn('abc123')
|
||||
->shouldBeCalledTimes(1);
|
||||
$this->urlShortener->urlToShortCode(Argument::type(Uri::class), Argument::type('array'))
|
||||
->willReturn('abc123')
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$request = ServerRequestFactory::fromGlobals()->withParsedBody([
|
||||
'longUrl' => 'http://www.domain.com/foo/bar',
|
||||
|
@ -63,8 +64,9 @@ class CreateShortcodeActionTest extends TestCase
|
|||
*/
|
||||
public function anInvalidUrlReturnsError()
|
||||
{
|
||||
$this->urlShortener->urlToShortCode(Argument::type(Uri::class))->willThrow(InvalidUrlException::class)
|
||||
->shouldBeCalledTimes(1);
|
||||
$this->urlShortener->urlToShortCode(Argument::type(Uri::class), Argument::type('array'))
|
||||
->willThrow(InvalidUrlException::class)
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$request = ServerRequestFactory::fromGlobals()->withParsedBody([
|
||||
'longUrl' => 'http://www.domain.com/foo/bar',
|
||||
|
@ -79,8 +81,9 @@ class CreateShortcodeActionTest extends TestCase
|
|||
*/
|
||||
public function aGenericExceptionWillReturnError()
|
||||
{
|
||||
$this->urlShortener->urlToShortCode(Argument::type(Uri::class))->willThrow(\Exception::class)
|
||||
->shouldBeCalledTimes(1);
|
||||
$this->urlShortener->urlToShortCode(Argument::type(Uri::class), Argument::type('array'))
|
||||
->willThrow(\Exception::class)
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$request = ServerRequestFactory::fromGlobals()->withParsedBody([
|
||||
'longUrl' => 'http://www.domain.com/foo/bar',
|
||||
|
|
76
module/Rest/test/Action/EditTagsActionTest.php
Normal file
76
module/Rest/test/Action/EditTagsActionTest.php
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
namespace ShlinkioTest\Shlink\Rest\Action;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Shlinkio\Shlink\Core\Entity\ShortUrl;
|
||||
use Shlinkio\Shlink\Core\Exception\InvalidShortCodeException;
|
||||
use Shlinkio\Shlink\Core\Service\ShortUrlService;
|
||||
use Shlinkio\Shlink\Rest\Action\EditTagsAction;
|
||||
use Zend\Diactoros\Response;
|
||||
use Zend\Diactoros\ServerRequestFactory;
|
||||
use Zend\I18n\Translator\Translator;
|
||||
|
||||
class EditTagsActionTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var EditTagsAction
|
||||
*/
|
||||
protected $action;
|
||||
/**
|
||||
* @var ObjectProphecy
|
||||
*/
|
||||
private $shortUrlService;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->shortUrlService = $this->prophesize(ShortUrlService::class);
|
||||
$this->action = new EditTagsAction($this->shortUrlService->reveal(), Translator::factory([]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function notProvidingTagsReturnsError()
|
||||
{
|
||||
$response = $this->action->__invoke(
|
||||
ServerRequestFactory::fromGlobals()->withAttribute('shortCode', 'abc123'),
|
||||
new Response()
|
||||
);
|
||||
$this->assertEquals(400, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function anInvalidShortCodeReturnsNotFound()
|
||||
{
|
||||
$shortCode = 'abc123';
|
||||
$this->shortUrlService->setTagsByShortCode($shortCode, [])->willThrow(InvalidShortCodeException::class)
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$response = $this->action->__invoke(
|
||||
ServerRequestFactory::fromGlobals()->withAttribute('shortCode', 'abc123')
|
||||
->withParsedBody(['tags' => []]),
|
||||
new Response()
|
||||
);
|
||||
$this->assertEquals(404, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function tagsListIsReturnedIfCorrectShortCodeIsProvided()
|
||||
{
|
||||
$shortCode = 'abc123';
|
||||
$this->shortUrlService->setTagsByShortCode($shortCode, [])->willReturn(new ShortUrl())
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$response = $this->action->__invoke(
|
||||
ServerRequestFactory::fromGlobals()->withAttribute('shortCode', 'abc123')
|
||||
->withParsedBody(['tags' => []]),
|
||||
new Response()
|
||||
);
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
}
|
79
module/Rest/test/Middleware/BodyParserMiddlewareTest.php
Normal file
79
module/Rest/test/Middleware/BodyParserMiddlewareTest.php
Normal file
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
namespace ShlinkioTest\Shlink\Rest\Middleware;
|
||||
|
||||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Shlinkio\Shlink\Rest\Middleware\BodyParserMiddleware;
|
||||
use Zend\Diactoros\Response;
|
||||
use Zend\Diactoros\ServerRequestFactory;
|
||||
use Zend\Diactoros\Stream;
|
||||
|
||||
class BodyParserMiddlewareTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var BodyParserMiddleware
|
||||
*/
|
||||
private $middleware;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->middleware = new BodyParserMiddleware();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function requestsFromOtherMethodsJustFallbackToNextMiddleware()
|
||||
{
|
||||
$request = ServerRequestFactory::fromGlobals()->withMethod('GET');
|
||||
$test = $this;
|
||||
$this->middleware->__invoke($request, new Response(), function ($req, $resp) use ($test, $request) {
|
||||
$test->assertSame($request, $req);
|
||||
});
|
||||
|
||||
$request = $request->withMethod('POST');
|
||||
$test = $this;
|
||||
$this->middleware->__invoke($request, new Response(), function ($req, $resp) use ($test, $request) {
|
||||
$test->assertSame($request, $req);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function jsonRequestsAreJsonDecoded()
|
||||
{
|
||||
$body = new Stream('php://temp', 'wr');
|
||||
$body->write('{"foo": "bar", "bar": ["one", 5]}');
|
||||
$request = ServerRequestFactory::fromGlobals()->withMethod('PUT')
|
||||
->withBody($body)
|
||||
->withHeader('content-type', 'application/json');
|
||||
$test = $this;
|
||||
$this->middleware->__invoke($request, new Response(), function (Request $req, $resp) use ($test, $request) {
|
||||
$test->assertNotSame($request, $req);
|
||||
$test->assertEquals([
|
||||
'foo' => 'bar',
|
||||
'bar' => ['one', 5],
|
||||
], $req->getParsedBody());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function regularRequestsAreUrlDecoded()
|
||||
{
|
||||
$body = new Stream('php://temp', 'wr');
|
||||
$body->write('foo=bar&bar[]=one&bar[]=5');
|
||||
$request = ServerRequestFactory::fromGlobals()->withMethod('PUT')
|
||||
->withBody($body);
|
||||
$test = $this;
|
||||
$this->middleware->__invoke($request, new Response(), function (Request $req, $resp) use ($test, $request) {
|
||||
$test->assertNotSame($request, $req);
|
||||
$test->assertEquals([
|
||||
'foo' => 'bar',
|
||||
'bar' => ['one', 5],
|
||||
], $req->getParsedBody());
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue