diff --git a/composer.json b/composer.json
index 374fed26..0211ed05 100644
--- a/composer.json
+++ b/composer.json
@@ -1,7 +1,8 @@
{
- "name": "acelaya/url-shortener",
+ "name": "shlinkio/shlink",
"type": "project",
- "homepage": "https://github.com/acelaya/url-shortener",
+ "homepage": "http://shlink.io",
+ "description": "A PHP-based URL shortener application with analytics and management",
"license": "MIT",
"authors": [
{
@@ -19,6 +20,8 @@
"zendframework/zend-stdlib": "^2.7",
"zendframework/zend-servicemanager": "^3.0",
"zendframework/zend-paginator": "^2.6",
+ "zendframework/zend-config": "^2.6",
+ "mtymek/expressive-config-manager": "^0.4",
"doctrine/orm": "^2.5",
"guzzlehttp/guzzle": "^6.2",
"acelaya/zsm-annotated-services": "^0.2.0",
@@ -34,12 +37,18 @@
},
"autoload": {
"psr-4": {
- "Acelaya\\UrlShortener\\": "src"
+ "Shlinkio\\Shlink\\CLI\\": "module/CLI/src",
+ "Shlinkio\\Shlink\\Rest\\": "module/Rest/src",
+ "Shlinkio\\Shlink\\Core\\": "module/Core/src",
+ "Shlinkio\\Shlink\\Common\\": "module/Common/src"
}
},
"autoload-dev": {
"psr-4": {
- "AcelayaTest\\UrlShortener\\": "tests"
+ "ShlinkioTest\\Shlink\\CLI\\": "module/CLI/test",
+ "ShlinkioTest\\Shlink\\Rest\\": "module/Rest/test",
+ "ShlinkioTest\\Shlink\\Core\\": "module/Core/test",
+ "ShlinkioTest\\Shlink\\Common\\": "module/Common/test"
}
},
"scripts": {
diff --git a/config/autoload/middleware-pipeline.global.php b/config/autoload/middleware-pipeline.global.php
index fc6f85f0..ca116a95 100644
--- a/config/autoload/middleware-pipeline.global.php
+++ b/config/autoload/middleware-pipeline.global.php
@@ -1,5 +1,4 @@
10,
],
- 'rest' => [
- 'path' => '/rest',
- 'middleware' => [
- Middleware\CheckAuthenticationMiddleware::class,
- Middleware\CrossDomainMiddleware::class,
- ],
- 'priority' => 5,
- ],
-
'post-routing' => [
'middleware' => [
Helper\UrlHelperMiddleware::class,
diff --git a/config/autoload/services.global.php b/config/autoload/services.global.php
index 4d6cc40e..dc99033c 100644
--- a/config/autoload/services.global.php
+++ b/config/autoload/services.global.php
@@ -1,13 +1,4 @@
[
'factories' => [
Expressive\Application::class => Container\ApplicationFactory::class,
- Console\Application::class => CLI\Factory\ApplicationFactory::class,
// Url helpers
Helper\UrlHelper::class => Helper\UrlHelperFactory::class,
@@ -33,38 +23,10 @@ return [
// View
'Zend\Expressive\FinalHandler' => Container\TemplatedErrorHandlerFactory::class,
Template\TemplateRendererInterface::class => Twig\TwigRendererFactory::class,
-
- // Services
- EntityManager::class => EntityManagerFactory::class,
- GuzzleHttp\Client::class => InvokableFactory::class,
- Service\UrlShortener::class => AnnotatedFactory::class,
- Service\VisitsTracker::class => AnnotatedFactory::class,
- Service\ShortUrlService::class => AnnotatedFactory::class,
- Service\RestTokenService::class => AnnotatedFactory::class,
- Cache::class => CacheFactory::class,
-
- // Cli commands
- CLI\Command\GenerateShortcodeCommand::class => AnnotatedFactory::class,
- CLI\Command\ResolveUrlCommand::class => AnnotatedFactory::class,
- CLI\Command\ListShortcodesCommand::class => AnnotatedFactory::class,
- CLI\Command\GetVisitsCommand::class => AnnotatedFactory::class,
-
- // Middleware
- Middleware\Routable\RedirectMiddleware::class => AnnotatedFactory::class,
- Middleware\Rest\AuthenticateMiddleware::class => AnnotatedFactory::class,
- Middleware\Rest\CreateShortcodeMiddleware::class => AnnotatedFactory::class,
- Middleware\Rest\ResolveUrlMiddleware::class => AnnotatedFactory::class,
- Middleware\Rest\GetVisitsMiddleware::class => AnnotatedFactory::class,
- Middleware\Rest\ListShortcodesMiddleware::class => AnnotatedFactory::class,
- Middleware\CrossDomainMiddleware::class => InvokableFactory::class,
- Middleware\CheckAuthenticationMiddleware::class => AnnotatedFactory::class,
],
'aliases' => [
- 'em' => EntityManager::class,
- 'httpClient' => GuzzleHttp\Client::class,
Router\RouterInterface::class => Router\FastRouteRouter::class,
- AnnotatedFactory::CACHE_SERVICE => Cache::class,
- ]
+ ],
],
];
diff --git a/config/autoload/templates.global.php b/config/autoload/templates.global.php
index 2beb9918..f102baa0 100644
--- a/config/autoload/templates.global.php
+++ b/config/autoload/templates.global.php
@@ -2,12 +2,6 @@
return [
- 'templates' => [
- 'paths' => [
- 'templates'
- ],
- ],
-
'twig' => [
'cache_dir' => 'data/cache/twig',
'extensions' => [
diff --git a/config/autoload/zend-expressive.global.php b/config/autoload/zend-expressive.global.php
index db5e3f38..aa2e9d3b 100644
--- a/config/autoload/zend-expressive.global.php
+++ b/config/autoload/zend-expressive.global.php
@@ -1,14 +1,8 @@
false,
+ 'config_cache_enabled' => true,
- 'config_cache_enabled' => false,
-
- 'zend-expressive' => [
- 'error_handler' => [
- 'template_404' => 'error/404.html.twig',
- 'template_error' => 'error/error.html.twig',
- ],
- ],
];
diff --git a/cli-config.php b/config/cli-config.php
similarity index 83%
rename from cli-config.php
rename to config/cli-config.php
index be9b8b82..30e31a93 100644
--- a/cli-config.php
+++ b/config/cli-config.php
@@ -4,7 +4,7 @@ use Doctrine\ORM\Tools\Console\ConsoleRunner;
use Interop\Container\ContainerInterface;
/** @var ContainerInterface $container */
-$container = include __DIR__ . '/config/container.php';
+$container = include __DIR__ . '/container.php';
/** @var EntityManager $em */
$em = $container->get(EntityManager::class);
diff --git a/config/config.php b/config/config.php
index a3d0e7ac..736b8fd4 100644
--- a/config/config.php
+++ b/config/config.php
@@ -1,6 +1,10 @@
getMergedConfig();
+});
diff --git a/config/autoload/cli.global.php b/module/CLI/config/cli.config.php
similarity index 87%
rename from config/autoload/cli.global.php
rename to module/CLI/config/cli.config.php
index 1276cc9e..6c34f19c 100644
--- a/config/autoload/cli.global.php
+++ b/module/CLI/config/cli.config.php
@@ -1,5 +1,5 @@
[
+ 'factories' => [
+ Console\Application::class => CLI\Factory\ApplicationFactory::class,
+
+ CLI\Command\GenerateShortcodeCommand::class => AnnotatedFactory::class,
+ CLI\Command\ResolveUrlCommand::class => AnnotatedFactory::class,
+ CLI\Command\ListShortcodesCommand::class => AnnotatedFactory::class,
+ CLI\Command\GetVisitsCommand::class => AnnotatedFactory::class,
+ ],
+ ],
+
+];
diff --git a/src/CLI/Command/GenerateShortcodeCommand.php b/module/CLI/src/Command/GenerateShortcodeCommand.php
similarity index 91%
rename from src/CLI/Command/GenerateShortcodeCommand.php
rename to module/CLI/src/Command/GenerateShortcodeCommand.php
index ffc355db..91cbd228 100644
--- a/src/CLI/Command/GenerateShortcodeCommand.php
+++ b/module/CLI/src/Command/GenerateShortcodeCommand.php
@@ -1,16 +1,15 @@
factory = new ApplicationFactory();
+ }
+
+ /**
+ * @test
+ */
+ public function serviceIsCreated()
+ {
+ $instance = $this->factory->__invoke($this->createServiceManager(), '');
+ $this->assertInstanceOf(Application::class, $instance);
+ }
+
+ /**
+ * @test
+ */
+ public function allCommandsWhichAreServicesAreAdded()
+ {
+ $sm = $this->createServiceManager([
+ 'commands' => [
+ 'foo',
+ 'bar',
+ 'baz',
+ ],
+ ]);
+ $sm->setService('foo', $this->prophesize(Command::class)->reveal());
+ $sm->setService('baz', $this->prophesize(Command::class)->reveal());
+
+ /** @var Application $instance */
+ $instance = $this->factory->__invoke($sm, '');
+ $this->assertInstanceOf(Application::class, $instance);
+ $this->assertCount(2, $instance->all());
+ }
+
+ protected function createServiceManager($config = [])
+ {
+ return new ServiceManager(['services' => [
+ 'config' => [
+ 'cli' => $config,
+ ],
+ ]]);
+ }
+}
diff --git a/module/Common/config/services.config.php b/module/Common/config/services.config.php
new file mode 100644
index 00000000..b3c4e14d
--- /dev/null
+++ b/module/Common/config/services.config.php
@@ -0,0 +1,24 @@
+ [
+ 'factories' => [
+ EntityManager::class => EntityManagerFactory::class,
+ GuzzleHttp\Client::class => InvokableFactory::class,
+ Cache::class => CacheFactory::class,
+ ],
+ 'aliases' => [
+ 'em' => EntityManager::class,
+ 'httpClient' => GuzzleHttp\Client::class,
+ AnnotatedFactory::CACHE_SERVICE => Cache::class,
+ ],
+ ],
+
+];
diff --git a/module/Common/src/ConfigProvider.php b/module/Common/src/ConfigProvider.php
new file mode 100644
index 00000000..9af040c2
--- /dev/null
+++ b/module/Common/src/ConfigProvider.php
@@ -0,0 +1,13 @@
+ [
+ [
+ 'name' => 'long-url-redirect',
+ 'path' => '/{shortCode}',
+ 'middleware' => RedirectMiddleware::class,
+ 'allowed_methods' => ['GET'],
+ ],
+ ],
+
+];
diff --git a/module/Core/config/services.config.php b/module/Core/config/services.config.php
new file mode 100644
index 00000000..cab7ca41
--- /dev/null
+++ b/module/Core/config/services.config.php
@@ -0,0 +1,20 @@
+ [
+ 'factories' => [
+ // Services
+ Service\UrlShortener::class => AnnotatedFactory::class,
+ Service\VisitsTracker::class => AnnotatedFactory::class,
+ Service\ShortUrlService::class => AnnotatedFactory::class,
+
+ // Middleware
+ RedirectMiddleware::class => AnnotatedFactory::class,
+ ],
+ ],
+
+];
diff --git a/module/Core/config/templates.config.php b/module/Core/config/templates.config.php
new file mode 100644
index 00000000..da3623c1
--- /dev/null
+++ b/module/Core/config/templates.config.php
@@ -0,0 +1,11 @@
+ [
+ 'paths' => [
+ 'module/Core/templates',
+ ],
+ ],
+
+];
diff --git a/module/Core/config/zend-expressive.config.php b/module/Core/config/zend-expressive.config.php
new file mode 100644
index 00000000..c5fefe8f
--- /dev/null
+++ b/module/Core/config/zend-expressive.config.php
@@ -0,0 +1,12 @@
+ [
+ 'error_handler' => [
+ 'template_404' => 'core/error/404.html.twig',
+ 'template_error' => 'core/error/error.html.twig',
+ ],
+ ],
+
+];
diff --git a/src/Middleware/Routable/RedirectMiddleware.php b/module/Core/src/Action/RedirectMiddleware.php
similarity index 91%
rename from src/Middleware/Routable/RedirectMiddleware.php
rename to module/Core/src/Action/RedirectMiddleware.php
index 85b42eb6..5b1907ca 100644
--- a/src/Middleware/Routable/RedirectMiddleware.php
+++ b/module/Core/src/Action/RedirectMiddleware.php
@@ -1,13 +1,13 @@
httpClient = $httpClient;
$this->em = $em;
- $this->chars = $chars;
+ $this->chars = empty($chars) ? self::DEFAULT_CHARS : $chars;
}
/**
diff --git a/src/Service/UrlShortenerInterface.php b/module/Core/src/Service/UrlShortenerInterface.php
similarity index 74%
rename from src/Service/UrlShortenerInterface.php
rename to module/Core/src/Service/UrlShortenerInterface.php
index 6623f392..6cbb6723 100644
--- a/src/Service/UrlShortenerInterface.php
+++ b/module/Core/src/Service/UrlShortenerInterface.php
@@ -1,10 +1,10 @@
[
+ 'rest' => [
+ 'path' => '/rest',
+ 'middleware' => [
+ Middleware\CheckAuthenticationMiddleware::class,
+ Middleware\CrossDomainMiddleware::class,
+ ],
+ 'priority' => 5,
+ ],
+ ],
+];
diff --git a/config/autoload/rest.global.php b/module/Rest/config/rest.config.php
similarity index 100%
rename from config/autoload/rest.global.php
rename to module/Rest/config/rest.config.php
diff --git a/config/autoload/routes.global.php b/module/Rest/config/routes.config.php
similarity index 54%
rename from config/autoload/routes.global.php
rename to module/Rest/config/routes.config.php
index 87133f09..e8abb6fe 100644
--- a/config/autoload/routes.global.php
+++ b/module/Rest/config/routes.config.php
@@ -1,46 +1,37 @@
[
- [
- 'name' => 'long-url-redirect',
- 'path' => '/{shortCode}',
- 'middleware' => Routable\RedirectMiddleware::class,
- 'allowed_methods' => ['GET'],
- ],
-
- // Rest
[
'name' => 'rest-authenticate',
'path' => '/rest/authenticate',
- 'middleware' => Rest\AuthenticateMiddleware::class,
+ 'middleware' => Action\AuthenticateMiddleware::class,
'allowed_methods' => ['POST', 'OPTIONS'],
],
[
'name' => 'rest-create-shortcode',
'path' => '/rest/short-codes',
- 'middleware' => Rest\CreateShortcodeMiddleware::class,
+ 'middleware' => Action\CreateShortcodeMiddleware::class,
'allowed_methods' => ['POST', 'OPTIONS'],
],
[
'name' => 'rest-resolve-url',
'path' => '/rest/short-codes/{shortCode}',
- 'middleware' => Rest\ResolveUrlMiddleware::class,
+ 'middleware' => Action\ResolveUrlMiddleware::class,
'allowed_methods' => ['GET', 'OPTIONS'],
],
[
- 'name' => 'rest-list-shortened-url',
+ 'name' => 'rest-lActionist-shortened-url',
'path' => '/rest/short-codes',
- 'middleware' => Rest\ListShortcodesMiddleware::class,
+ 'middleware' => Action\ListShortcodesMiddleware::class,
'allowed_methods' => ['GET'],
],
[
'name' => 'rest-get-visits',
'path' => '/rest/visits/{shortCode}',
- 'middleware' => Rest\GetVisitsMiddleware::class,
+ 'middleware' => Action\GetVisitsMiddleware::class,
'allowed_methods' => ['GET', 'OPTIONS'],
],
],
diff --git a/module/Rest/config/services.config.php b/module/Rest/config/services.config.php
new file mode 100644
index 00000000..aff4cc96
--- /dev/null
+++ b/module/Rest/config/services.config.php
@@ -0,0 +1,25 @@
+ [
+ 'factories' => [
+ Service\RestTokenService::class => AnnotatedFactory::class,
+
+ Action\AuthenticateMiddleware::class => AnnotatedFactory::class,
+ Action\CreateShortcodeMiddleware::class => AnnotatedFactory::class,
+ Action\ResolveUrlMiddleware::class => AnnotatedFactory::class,
+ Action\GetVisitsMiddleware::class => AnnotatedFactory::class,
+ Action\ListShortcodesMiddleware::class => AnnotatedFactory::class,
+
+ Middleware\CrossDomainMiddleware::class => InvokableFactory::class,
+ Middleware\CheckAuthenticationMiddleware::class => AnnotatedFactory::class,
+ ],
+ ],
+
+];
diff --git a/src/Middleware/Rest/AbstractRestMiddleware.php b/module/Rest/src/Action/AbstractRestMiddleware.php
similarity index 97%
rename from src/Middleware/Rest/AbstractRestMiddleware.php
rename to module/Rest/src/Action/AbstractRestMiddleware.php
index 1168ff60..9ef870f4 100644
--- a/src/Middleware/Rest/AbstractRestMiddleware.php
+++ b/module/Rest/src/Action/AbstractRestMiddleware.php
@@ -1,5 +1,5 @@
middleware = new CrossDomainMiddleware();
+ }
+
+ /**
+ * @test
+ */
+ public function anyRequestIncludesTheAllowAccessHeader()
+ {
+ $response = $this->middleware->__invoke(
+ ServerRequestFactory::fromGlobals(),
+ new Response(),
+ function ($req, $resp) {
+ return $resp;
+ }
+ );
+
+ $headers = $response->getHeaders();
+ $this->assertArrayHasKey('Access-Control-Allow-Origin', $headers);
+ $this->assertArrayNotHasKey('Access-Control-Allow-Headers', $headers);
+ }
+
+ /**
+ * @test
+ */
+ public function optionsRequestIncludesMoreHeaders()
+ {
+ $request = ServerRequestFactory::fromGlobals(['REQUEST_METHOD' => 'OPTIONS']);
+
+ $response = $this->middleware->__invoke($request, new Response(), function ($req, $resp) {
+ return $resp;
+ });
+
+ $headers = $response->getHeaders();
+ $this->assertArrayHasKey('Access-Control-Allow-Origin', $headers);
+ $this->assertArrayHasKey('Access-Control-Allow-Headers', $headers);
+ }
+}
diff --git a/phpcs.xml b/phpcs.xml
index 51649659..ae134872 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -16,8 +16,7 @@
- src
- tests
+ module
config
public/index.php
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 0721b191..65d36865 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,7 +1,16 @@
-
- ./tests
+
+ ./module/Common/test
+
+
+ ./module/Core/test
+
+
+ ./module/Rest/test
+
+
+ ./module/CLI/test
diff --git a/src/Exception/ExceptionInterface.php b/src/Exception/ExceptionInterface.php
deleted file mode 100644
index d6bdc788..00000000
--- a/src/Exception/ExceptionInterface.php
+++ /dev/null
@@ -1,6 +0,0 @@
-