diff --git a/docker-compose.yml b/docker-compose.yml index 0fedb43..f5ef7ab 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,6 +2,15 @@ volumes: redisdata: {} services: + composer-runtime: + ports: + - "9501:9501" + volumes: + - .:/app + image: siteworxpro/composer + entrypoint: "/bin/sh -c 'while true; do sleep 30; done;'" + environment: + PHP_IDE_CONFIG: serverName=localhost dev-runtime: ports: - "9501:9501" diff --git a/src/Controllers/RoutesController.php b/src/Controllers/RoutesController.php index e24bcfc..467bcfe 100644 --- a/src/Controllers/RoutesController.php +++ b/src/Controllers/RoutesController.php @@ -71,21 +71,4 @@ class RoutesController extends Controller return JsonResponseFactory::createJsonResponse(['message' => 'Router deleted successfully']); } - - /** - * @throws \JsonException - */ - public function patch(ServerRequest $request): ResponseInterface - { - $name = $request->getAttribute('id'); - $data = $request->getParsedBody(); - - try { - RedisClient::patchEntity($name, $data, EntityEnum::ROUTER, $this->protocolEnumFromRequest($request)); - } catch (\InvalidArgumentException) { - return JsonResponseFactory::createJsonResponse(['error' => 'Router not found'], 404); - } - - return JsonResponseFactory::createJsonResponse(['message' => 'Router updated successfully']); - } } diff --git a/src/Server.php b/src/Server.php index a4a5de4..b591a38 100644 --- a/src/Server.php +++ b/src/Server.php @@ -127,7 +127,6 @@ class Server $router->get('/', RoutesController::class . '::get'); $router->get('/{id}', RoutesController::class . '::get'); $router->post('/{id}', RoutesController::class . '::post'); - $router->patch('/{id}', RoutesController::class . '::patch'); $router->delete('/{id}', RoutesController::class . '::delete'); }); diff --git a/src/Traefik/RedisClient.php b/src/Traefik/RedisClient.php index e24bd16..f22e3f0 100644 --- a/src/Traefik/RedisClient.php +++ b/src/Traefik/RedisClient.php @@ -41,66 +41,6 @@ class RedisClient } } - /** - * @param string $name - * @param array $data - * @param EntityEnum $entity - * @param ProtocolEnum $type - * @return void - */ - public static function patchEntity( - string $name, - array $data, - EntityEnum $entity, - ProtocolEnum $type = ProtocolEnum::HTTP - ): void { - $collection = self::flattenToDotArray($data); - - $checkKey = 'traefik/' . $type->getValue() . '/' . $entity->getValue() . "/$name"; - - $keys = Redis::keys($checkKey . '/*'); - if (empty($keys)) { - throw new \InvalidArgumentException("The key $checkKey does not exist."); - } - - $cleanedUpKeys = []; - - foreach ($collection as $key => $value) { - // regex if key matches [key].[digit] - if (preg_match('/\.[0-9]+$/', $key)) { - $arrayKey = preg_replace('/\.[0-9]+$/', '', $key); - - if (in_array($arrayKey, $cleanedUpKeys)) { - $redisKey = 'traefik/' . - $type->getValue() . - '/' . - $entity->getValue() . - "/$name/" . - str_replace('.', '/', $key); - - Redis::set($redisKey, $value); - - continue; - } - - // key is an array, delete keys under array - $arrayKeys = Redis::keys($checkKey . '/' . str_replace('.', '/', $arrayKey) . '/*'); - foreach ($arrayKeys as $k) { - Redis::del($k); - } - - $cleanedUpKeys[] = $arrayKey; - } - - $redisKey = 'traefik/' . - $type->getValue() . '/' . $entity->getValue() . - "/$name/" . - str_replace('.', '/', $key); - - Redis::set($redisKey, $value); - } - } - /** * @param string $name * @param EntityEnum $entity diff --git a/tests/Http/JsonResponseFactoryTest.php b/tests/Http/JsonResponseFactoryTest.php index c203350..ed5e6e4 100644 --- a/tests/Http/JsonResponseFactoryTest.php +++ b/tests/Http/JsonResponseFactoryTest.php @@ -40,4 +40,16 @@ class JsonResponseFactoryTest extends TestCase $data = ["invalid" => "\xB1\x31"]; JsonResponseFactory::createJsonResponse($data); } + + public function testCreateResponseReturnsValidResponse(): void + { + $code = 200; + $reasonPhrase = 'OK'; + + $factory = new JsonResponseFactory(); + $response = $factory->createResponse($code, $reasonPhrase); + + $this->assertSame($code, $response->getStatusCode()); + $this->assertSame('application/json', $response->getHeaderLine('Content-Type')); + } } diff --git a/tests/Traefik/MiddlewareServiceTest.php b/tests/Traefik/MiddlewareServiceTest.php new file mode 100644 index 0000000..510ebb0 --- /dev/null +++ b/tests/Traefik/MiddlewareServiceTest.php @@ -0,0 +1,102 @@ +getAllMiddleware(ProtocolEnum::HTTP); + $this->assertTrue(true); + } + + public function testGetMiddlewareTcpAll(): void + { + $this->getAllMiddleware(ProtocolEnum::TCP); + $this->assertTrue(true); + } + + private function getAllMiddleware(ProtocolEnum $protocolEnum): void + { + $commandFactory = Mockery::mock(FactoryInterface::class) + ->expects('supports') + ->andReturn(true) + ->getMock(); + + Redis::expects('getCommandFactory') + ->andReturn($commandFactory); + + Redis::expects('scan') + ->with(0, ['MATCH' => 'traefik/' . $protocolEnum->getValue() . '/middlewares/*']) + ->andReturn([0, [ + 'traefik/' . $protocolEnum->getValue() . '/middlewares/foo/headers/customrequestheaders/Host', + 'traefik/' . $protocolEnum->getValue() . '/middlewares/foo/headers/customrequestheaders/X-Forwarded-Host', + 'traefik/' . $protocolEnum->getValue() . '/middlewares/foo/headers/customrequestheaders/X-Forwarded-Port', + 'traefik/' . $protocolEnum->getValue() . '/middlewares/foo/headers/customrequestheaders/X-Forwarded-Proto', + ]]); + + $services = RedisClient::getAllMiddlewares($protocolEnum); + $this->assertCount(1, $services); + $this->assertSame('foo', $services[0]); + } + + + public function testGetMiddlewareHttp() + { + $this->getIndMiddleware(ProtocolEnum::HTTP); + } + + private function getIndMiddleware(ProtocolEnum $protocolEnum): void + { + $commandFactory = Mockery::mock(FactoryInterface::class) + ->expects('supports') + ->andReturn(true) + ->getMock(); + + Redis::expects('getCommandFactory') + ->andReturn($commandFactory); + + Redis::expects('scan') + ->with(0, ['MATCH' => 'traefik/' . $protocolEnum->getValue() . '/middlewares/foo/*']) + ->andReturn([0, [ + 'traefik/' . $protocolEnum->getValue() . '/middlewares/foo/headers/customrequestheaders/Host', + 'traefik/' . $protocolEnum->getValue() . '/middlewares/foo/headers/customrequestheaders/X-Forwarded-Host', + 'traefik/' . $protocolEnum->getValue() . '/middlewares/foo/headers/customrequestheaders/X-Forwarded-Port', + 'traefik/' . $protocolEnum->getValue() . '/middlewares/foo/headers/customrequestheaders/X-Forwarded-Proto', + ]]); + + Redis::expects('get') + ->with('traefik/' . $protocolEnum->getValue() . '/middlewares/foo/headers/customrequestheaders/Host') + ->andReturn('foo.localhost'); + + Redis::expects('get') + ->with('traefik/' . $protocolEnum->getValue() . '/middlewares/foo/headers/customrequestheaders/X-Forwarded-Host') + ->andReturn('foo.localhost'); + + Redis::expects('get') + ->with('traefik/' . $protocolEnum->getValue() . '/middlewares/foo/headers/customrequestheaders/X-Forwarded-Port') + ->andReturn('80'); + + Redis::expects('get') + ->with('traefik/' . $protocolEnum->getValue() . '/middlewares/foo/headers/customrequestheaders/X-Forwarded-Proto') + ->andReturn('http'); + + $middleware = RedisClient::getMiddleware('foo', $protocolEnum); + + $this->assertCount(4, $middleware['headers']['customrequestheaders']); + $this->assertSame('foo.localhost', $middleware['headers']['customrequestheaders']['Host']); + $this->assertSame('foo.localhost', $middleware['headers']['customrequestheaders']['X-Forwarded-Host']); + $this->assertSame('80', $middleware['headers']['customrequestheaders']['X-Forwarded-Port']); + $this->assertSame('http', $middleware['headers']['customrequestheaders']['X-Forwarded-Proto']); + } +} \ No newline at end of file diff --git a/tests/Traefik/ProtocolEnumTest.php b/tests/Traefik/ProtocolEnumTest.php new file mode 100644 index 0000000..5ed2d64 --- /dev/null +++ b/tests/Traefik/ProtocolEnumTest.php @@ -0,0 +1,19 @@ +assertEquals(ProtocolEnum::HTTP, ProtocolEnum::fromString('http')); + $this->assertEquals(ProtocolEnum::TCP, ProtocolEnum::fromString('tcp')); + $this->assertEquals(ProtocolEnum::UDP, ProtocolEnum::fromString('udp')); + } +} \ No newline at end of file diff --git a/tests/Traefik/RedisClientRoutersTest.php b/tests/Traefik/RedisClientRoutersTest.php index 8d7c2ba..057c920 100644 --- a/tests/Traefik/RedisClientRoutersTest.php +++ b/tests/Traefik/RedisClientRoutersTest.php @@ -68,6 +68,58 @@ class RedisClientRoutersTest extends Unit $this->assertTrue(true); } + public function testGetOneHttp(): void + { + $this->getOneTest(ProtocolEnum::HTTP); + } + + public function testGetOneTcp(): void + { + $this->getOneTest(ProtocolEnum::TCP); + } + + public function testGetOneUdp(): void + { + $this->getOneTest(ProtocolEnum::UDP); + } + + private function getOneTest(ProtocolEnum $protocolEnum): void + { + $commandFactory = Mockery::mock(FactoryInterface::class) + ->expects('supports') + ->andReturn(true) + ->getMock(); + + Redis::expects('getCommandFactory') + ->andReturn($commandFactory); + + Redis::expects('scan') + ->with(0, ['MATCH' => 'traefik/' . $protocolEnum->getValue() . '/routers/foo/*']) + ->andReturn([0, [ + 'traefik/' . $protocolEnum->getValue() . '/routers/foo/rule', + 'traefik/' . $protocolEnum->getValue() . '/routers/foo/service', + ]]); + + Redis::expects('get') + ->with('traefik/' . $protocolEnum->getValue() . '/routers/foo/rule') + ->andReturn('Host(`foo.localhost`)'); + + Redis::expects('get') + ->with('traefik/' . $protocolEnum->getValue() . '/routers/foo/service') + ->andReturn('foo'); + + $routers = RedisClient::getRouter('foo', $protocolEnum); + + $this->assertIsArray($routers); + + $this->assertCount(2, $routers); + + $this->assertEquals([ + 'rule' => 'Host(`foo.localhost`)', + 'service' => 'foo', + ], $routers); + } + private function allTest(ProtocolEnum $protocol): void { $commandFactory = Mockery::mock(FactoryInterface::class) diff --git a/tests/Traefik/RedisClientServiceTest.php b/tests/Traefik/RedisClientServiceTest.php new file mode 100644 index 0000000..1a6fc88 --- /dev/null +++ b/tests/Traefik/RedisClientServiceTest.php @@ -0,0 +1,143 @@ +createTest(ProtocolEnum::HTTP); + $this->assertTrue(true); + } + + public function testCreateServiceTcp(): void + { + $this->createTest(ProtocolEnum::TCP); + $this->assertTrue(true); + } + + public function testCreateServiceUdp(): void + { + $this->createTest(ProtocolEnum::UDP); + $this->assertTrue(true); + } + + + public function createTest(ProtocolEnum $protocol): void + { + + $commandFactory = Mockery::mock(FactoryInterface::class) + ->expects('supports') + ->andReturn(true) + ->getMock(); + + Redis::expects('getCommandFactory') + ->andReturn($commandFactory); + + Redis::expects('scan') + ->with(0, ['MATCH' => 'traefik/' . $protocol->getValue() . '/services/foo/*']) + ->andReturn([0, []]); + + Redis::expects('set') + ->with('traefik/' . $protocol->getValue() . '/services/foo/loadbalancer/servers/server1/url', 'http://foo.localhost:80') + ->once() + ->andReturn(true); + + RedisClient::createOrReplace('foo', [ + 'loadbalancer' => [ + 'servers' => [ + 'server1' => [ + 'url' => 'http://foo.localhost:80', + ], + ], + ], + ], EntityEnum::SERVICE, $protocol); + } + + public function testGetOneHttp(): void + { + $this->runGet(ProtocolEnum::HTTP); + $this->assertTrue(true); + } + + public function testGetOneTcp(): void + { + $this->runGet(ProtocolEnum::TCP); + $this->assertTrue(true); + } + + public function testGetOneUdp(): void + { + $this->runGet(ProtocolEnum::UDP); + $this->assertTrue(true); + } + + private function runGet(ProtocolEnum $protocol): void + { + $commandFactory = Mockery::mock(FactoryInterface::class) + ->expects('supports') + ->andReturn(true) + ->getMock(); + + Redis::expects('getCommandFactory') + ->andReturn($commandFactory); + + Redis::expects('scan') + ->with(0, ['MATCH' => 'traefik/' . $protocol->getValue() . '/services/foo/*']) + ->andReturn([0, [ + 'traefik/' . $protocol->getValue() . '/services/foo/loadbalancer/servers/server1/url', + ]]); + + Redis::expects('get') + ->with('traefik/' . $protocol->getValue() . '/services/foo/loadbalancer/servers/server1/url') + ->andReturn('http://foo.localhost:80'); + + $services = RedisClient::getService('foo', $protocol); + + $this->assertIsArray($services); + $this->assertArrayHasKey('loadbalancer', $services); + $this->assertArrayHasKey('servers', $services['loadbalancer']); + $this->assertArrayHasKey('server1', $services['loadbalancer']['servers']); + $this->assertEquals('http://foo.localhost:80', $services['loadbalancer']['servers']['server1']['url']); + } + + public function testGetAllHttp() + { + $this->runGetAll(ProtocolEnum::HTTP); + $this->assertTrue(true); + } + + private function runGetAll(ProtocolEnum $protocol): void + { + $commandFactory = Mockery::mock(FactoryInterface::class) + ->expects('supports') + ->andReturn(true) + ->getMock(); + + Redis::expects('getCommandFactory') + ->andReturn($commandFactory); + + Redis::expects('scan') + ->with(0, ['MATCH' => 'traefik/' . $protocol->getValue() . '/services/*']) + ->andReturn([0, [ + 'traefik/' . $protocol->getValue() . '/services/foo/loadbalancer/servers/server1/url', + 'traefik/' . $protocol->getValue() . '/services/bar/loadbalancer/servers/server2/url', + ]]); + + $services = RedisClient::getAllServices($protocol); + + $this->assertIsArray($services); + $this->assertCount(2, $services); + } +} \ No newline at end of file