refs #1975. Integration ogGit. Crete image and deploy
testing/ogcore-api/pipeline/head There was a failure building this commit Details

pull/37/head
Manuel Aranda Rosales 2025-06-26 17:01:16 +02:00
parent 73f65e65a2
commit af5ad81199
37 changed files with 1317 additions and 652 deletions

View File

@ -1,50 +0,0 @@
resources:
App\Entity\GitImageRepository:
processor: App\State\Processor\GitImageRepositoryProcessor
input: App\Dto\Input\GitImageRepositoryInput
output: App\Dto\Output\GitImageRepositoryOutput
normalizationContext:
groups: ['default', 'git-image-repository:read']
denormalizationContext:
groups: ['git-image-repository:write']
operations:
ApiPlatform\Metadata\GetCollection:
provider: App\State\Provider\GitImageRepositoryProvider
filters:
- 'api_platform.filter.image_image_repository.order'
- 'api_platform.filter.image_image_repository.search'
ApiPlatform\Metadata\Get:
provider: App\State\Provider\GitImageRepositoryProvider
ApiPlatform\Metadata\Put:
provider: App\State\Provider\GitImageRepositoryProvider
ApiPlatform\Metadata\Patch:
provider: App\State\Provider\GitImageRepositoryProvider
ApiPlatform\Metadata\Post: ~
ApiPlatform\Metadata\Delete: ~
get_image_ogrepository:
shortName: OgRepository Server
description: Get image in OgRepository
class: ApiPlatform\Metadata\Get
method: GET
input: false
uriTemplate: /image-image-repositories/server/{uuid}/get
controller: App\Controller\OgRepository\Image\GetAction
get_image_tags:
shortName: OgRepository Server
description: Get image tags in OgRepository
class: ApiPlatform\Metadata\Get
method: GET
input: false
uriTemplate: /git-image-repositories/server/{uuid}/get-tags
controller: App\Controller\OgRepository\Git\GetTagsAction
properties:
App\Entity\GitImageRepository:
id:
identifier: false
uuid:
identifier: true

View File

@ -0,0 +1,109 @@
resources:
App\Entity\GitRepository:
processor: App\State\Processor\GitRepositoryProcessor
input: App\Dto\Input\GitRepositoryInput
output: App\Dto\Output\GitRepositoryOutput
normalizationContext:
groups: ['default', 'git-repository:read']
denormalizationContext:
groups: ['git-repository:write']
operations:
ApiPlatform\Metadata\GetCollection:
provider: App\State\Provider\GitRepositoryProvider
filters:
- 'api_platform.filter.git_repository.order'
- 'api_platform.filter.git_repository.search'
openapiContext:
summary: 'Obtener lista de repositorios Git'
description: 'Obtiene todos los repositorios Git de un servidor específico'
parameters:
- name: 'repository'
in: 'query'
required: true
schema:
type: 'integer'
description: 'ID del ImageRepository que contiene los repositorios'
- name: 'page'
in: 'query'
schema:
type: 'integer'
default: 1
description: 'Número de página'
- name: 'limit'
in: 'query'
schema:
type: 'integer'
default: 10
description: 'Elementos por página'
ApiPlatform\Metadata\Get:
provider: App\State\Provider\GitRepositoryProvider
openapiContext:
summary: 'Obtener un repositorio Git específico'
description: 'Obtiene un repositorio Git específico por su nombre'
parameters:
- name: 'repository'
in: 'query'
required: true
schema:
type: 'integer'
description: 'ID del ImageRepository'
ApiPlatform\Metadata\Put:
provider: App\State\Provider\GitRepositoryProvider
openapiContext:
summary: 'Actualizar un repositorio Git'
description: 'Actualiza un repositorio Git existente en el servidor externo'
parameters:
- name: 'repository'
in: 'query'
required: true
schema:
type: 'integer'
description: 'ID del ImageRepository'
ApiPlatform\Metadata\Patch:
provider: App\State\Provider\GitRepositoryProvider
openapiContext:
summary: 'Actualizar parcialmente un repositorio Git'
description: 'Actualiza parcialmente un repositorio Git existente'
parameters:
- name: 'repository'
in: 'query'
required: true
schema:
type: 'integer'
description: 'ID del ImageRepository'
ApiPlatform\Metadata\Post:
openapiContext:
summary: 'Crear un nuevo repositorio Git'
description: 'Crea un nuevo repositorio Git en el servidor externo. El ID del ImageRepository debe incluirse en el body del request.'
ApiPlatform\Metadata\Delete:
openapiContext:
summary: 'Eliminar un repositorio Git'
description: 'Elimina un repositorio Git del servidor externo'
parameters:
- name: 'repository'
in: 'query'
required: true
schema:
type: 'integer'
description: 'ID del ImageRepository'
git_deploy_image:
shortName: Git Repository
description: Deploy Git image
class: ApiPlatform\Metadata\Post
method: POST
input: App\Dto\Input\DeployGitImageInput
uriTemplate: /git-repositories/deploy-image
controller: App\Controller\DeployGitImageAction
properties:
App\Entity\GitRepository:
id:
identifier: false
name:
identifier: true

View File

@ -78,19 +78,29 @@ resources:
get_collection_images_oggit:
shortName: OgRepository Server
description: Get collection of image in OgRepository
class: ApiPlatform\Metadata\Post
method: POST
class: ApiPlatform\Metadata\Get
method: GET
input: false
uriTemplate: /image-repositories/server/git/{uuid}/get-collection
controller: App\Controller\OgRepository\Git\GetCollectionAction
git_image_ogrepository_sync:
git_repository_commits:
shortName: OgRepository Server
description: Get commits from a specific branch of a Git repository
class: ApiPlatform\Metadata\Post
method: POST
input: false
uriTemplate: /image-repositories/server/git/{uuid}/sync
controller: App\Controller\OgRepository\Git\SyncAction
input: App\Dto\Input\GetCommitsInput
uriTemplate: /image-repositories/server/git/{uuid}/commits
controller: App\Controller\OgRepository\Git\GetCommitsAction
git_repository_branches:
shortName: OgRepository Server
description: Get branches from a Git repository
class: ApiPlatform\Metadata\Post
method: POST
input: App\Dto\Input\GetBranchesInput
uriTemplate: /image-repositories/server/git/{uuid}/branches
controller: App\Controller\OgRepository\Git\GetBranchesAction
properties:
App\Entity\ImageRepository:

View File

@ -166,8 +166,3 @@ services:
bind:
$collectionProvider: '@api_platform.doctrine.orm.state.collection_provider'
$itemProvider: '@api_platform.doctrine.orm.state.item_provider'
App\State\Provider\GitImageRepositoryProvider:
bind:
$collectionProvider: '@api_platform.doctrine.orm.state.collection_provider'
$itemProvider: '@api_platform.doctrine.orm.state.item_provider'

View File

@ -298,7 +298,7 @@ services:
api_platform.filter.trace.search:
parent: 'api_platform.doctrine.orm.search_filter'
arguments: [ { 'id': 'exact', 'command': 'exact', 'client.id': 'exact', status: 'exact' } ]
arguments: [ { 'id': 'exact', 'command': 'exact', 'client.id': 'exact', 'status': 'exact' } ]
tags: [ 'api_platform.filter' ]
api_platform.filter.trace.order:
@ -347,4 +347,28 @@ services:
arguments: [ { 'enabled': ~ } ]
tags: [ 'api_platform.filter' ]
api_platform.filter.git_repository.order:
parent: 'api_platform.doctrine.orm.order_filter'
arguments:
$properties: { 'id': ~, 'name': ~ }
$orderParameterName: 'order'
tags: [ 'api_platform.filter' ]
api_platform.filter.git_repository.search:
parent: 'api_platform.doctrine.orm.search_filter'
arguments: [ { 'id': 'exact', 'name': 'partial', 'description': 'partial' } ]
tags: [ 'api_platform.filter' ]
api_platform.filter.git_image_repository.order:
parent: 'api_platform.doctrine.orm.order_filter'
arguments:
$properties: { 'id': ~, 'name': ~, 'status': ~, 'tag': ~, 'branch': ~ }
$orderParameterName: 'order'
tags: [ 'api_platform.filter' ]
api_platform.filter.git_image_repository.search:
parent: 'api_platform.doctrine.orm.search_filter'
arguments: [ { 'id': 'exact', 'name': 'partial', 'status': 'exact', 'tag': 'partial', 'branch': 'partial', 'image.id': 'exact', 'repository.id': 'exact', 'gitRepository.id': 'exact' } ]
tags: [ 'api_platform.filter' ]

View File

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20250618114502 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE git_repository (id INT AUTO_INCREMENT NOT NULL, uuid CHAR(36) NOT NULL COMMENT \'(DC2Type:uuid)\', migration_id VARCHAR(255) DEFAULT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, created_by VARCHAR(255) DEFAULT NULL, updated_by VARCHAR(255) DEFAULT NULL, description VARCHAR(255) DEFAULT NULL, name VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_C2B3204AD17F50A6 (uuid), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('ALTER TABLE git_image_repository ADD git_repository_id INT DEFAULT NULL');
$this->addSql('ALTER TABLE git_image_repository ADD CONSTRAINT FK_E6944D5EDCA00B70 FOREIGN KEY (git_repository_id) REFERENCES git_repository (id)');
$this->addSql('CREATE INDEX IDX_E6944D5EDCA00B70 ON git_image_repository (git_repository_id)');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE git_image_repository DROP FOREIGN KEY FK_E6944D5EDCA00B70');
$this->addSql('DROP TABLE git_repository');
$this->addSql('DROP INDEX IDX_E6944D5EDCA00B70 ON git_image_repository');
$this->addSql('ALTER TABLE git_image_repository DROP git_repository_id');
}
}

View File

@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20250618115400 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE git_repository ADD repository_id INT DEFAULT NULL');
$this->addSql('ALTER TABLE git_repository ADD CONSTRAINT FK_C2B3204A50C9D4F7 FOREIGN KEY (repository_id) REFERENCES image_repository (id)');
$this->addSql('CREATE INDEX IDX_C2B3204A50C9D4F7 ON git_repository (repository_id)');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE git_repository DROP FOREIGN KEY FK_C2B3204A50C9D4F7');
$this->addSql('DROP INDEX IDX_C2B3204A50C9D4F7 ON git_repository');
$this->addSql('ALTER TABLE git_repository DROP repository_id');
}
}

View File

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20250626145458 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE git_image_repository DROP FOREIGN KEY FK_E6944D5EDCA00B70');
$this->addSql('ALTER TABLE git_image_repository DROP FOREIGN KEY FK_E6944D5E3DA5256D');
$this->addSql('ALTER TABLE git_image_repository DROP FOREIGN KEY FK_E6944D5E50C9D4F7');
$this->addSql('DROP TABLE git_image_repository');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE git_image_repository (id INT AUTO_INCREMENT NOT NULL, image_id INT NOT NULL, repository_id INT DEFAULT NULL, git_repository_id INT DEFAULT NULL, uuid CHAR(36) CHARACTER SET utf8mb4 NOT NULL COLLATE `utf8mb4_unicode_ci` COMMENT \'(DC2Type:uuid)\', migration_id VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_unicode_ci`, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, created_by VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_unicode_ci`, updated_by VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_unicode_ci`, status VARCHAR(255) CHARACTER SET utf8mb4 NOT NULL COLLATE `utf8mb4_unicode_ci`, branch VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_unicode_ci`, created TINYINT(1) NOT NULL, name VARCHAR(255) CHARACTER SET utf8mb4 NOT NULL COLLATE `utf8mb4_unicode_ci`, tag VARCHAR(255) CHARACTER SET utf8mb4 NOT NULL COLLATE `utf8mb4_unicode_ci`, version INT DEFAULT NULL, description VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_unicode_ci`, UNIQUE INDEX UNIQ_E6944D5ED17F50A6 (uuid), INDEX IDX_E6944D5E3DA5256D (image_id), INDEX IDX_E6944D5E50C9D4F7 (repository_id), INDEX IDX_E6944D5EDCA00B70 (git_repository_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB COMMENT = \'\' ');
$this->addSql('ALTER TABLE git_image_repository ADD CONSTRAINT FK_E6944D5EDCA00B70 FOREIGN KEY (git_repository_id) REFERENCES git_repository (id)');
$this->addSql('ALTER TABLE git_image_repository ADD CONSTRAINT FK_E6944D5E3DA5256D FOREIGN KEY (image_id) REFERENCES image (id)');
$this->addSql('ALTER TABLE git_image_repository ADD CONSTRAINT FK_E6944D5E50C9D4F7 FOREIGN KEY (repository_id) REFERENCES image_repository (id)');
}
}

View File

@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use ApiPlatform\Validator\ValidatorInterface;
use App\Dto\Input\DeployGitImageInput;
use App\Entity\Command;
use App\Entity\Image;
use App\Model\ClientStatus;
use App\Entity\ImageImageRepository;
use App\Entity\OrganizationalUnit;
use App\Entity\Partition;
use App\Model\CommandTypes;
use App\Model\DeployMethodTypes;
use App\Model\TraceStatus;
use App\Service\Trace\CreateService;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class DeployGitImageAction extends AbstractController
{
public function __construct(
protected readonly EntityManagerInterface $entityManager,
protected readonly CreateService $createService,
protected readonly ValidatorInterface $validator,
public readonly \App\Controller\OgAgent\DeployGitImageAction $deployGitImageOgAgentAction,
) {
}
/**
* @throws RedirectionExceptionInterface
* @throws ClientExceptionInterface
* @throws ServerExceptionInterface
*/
public function __invoke(DeployGitImageInput $input): JsonResponse
{
$this->validator->validate($input);
$this->handleGitDeployment($input);
return new JsonResponse(data: [], status: Response::HTTP_OK);
}
private function handleGitDeployment(DeployGitImageInput $input): void
{
foreach ($input->clients as $client) {
$inputData = $this->createInputData($input, $client->getEntity());
$this->processDeployment($client->getEntity(), $input, $inputData, DeployMethodTypes::GIT);
}
}
private function processDeployment($client, DeployGitImageInput $input, array $inputData, string $deployType): void
{
$agentJobId = $this->deployGitImageOgAgentAction->__invoke($input, $client);
if (!$agentJobId) {
if ($input->queue) {
$this->createService->__invoke($client, CommandTypes::DEPLOY_IMAGE, TraceStatus::PENDING, null, $inputData);
}
return;
}
$this->createService->__invoke($client, CommandTypes::DEPLOY_IMAGE, TraceStatus::IN_PROGRESS, $agentJobId, $inputData);
}
private function createInputData(DeployGitImageInput $input, $client): array
{
return [
'method' => $input->method,
'client' => $client->getUuid(),
'hexsha' => $input->hexsha,
'repositoryName' => $input->repositoryName,
'branch' => $input->branch,
'numDisk' => (string) $input->diskNumber,
'numPartition' => (string) $input->partitionNumber,
'type' => $input->type,
];
}
}

View File

@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
namespace App\Controller\OgAgent;
use App\Dto\Input\DeployGitImageInput;
use App\Entity\Client;
use App\Entity\Image;
use App\Model\ClientStatus;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
class DeployGitImageAction extends AbstractOgAgentController
{
/**
* @throws RedirectionExceptionInterface
* @throws ClientExceptionInterface
* @throws TransportExceptionInterface
* @throws ServerExceptionInterface
*/
public function __invoke(DeployGitImageInput $input, Client $client)
{
if (!$client->getIp()) {
throw new BadRequestHttpException('IP is required');
}
if (!$input->hexsha && !$input->tag) {
throw new BadRequestHttpException('Either hexsha or tag is required for Git image deployment');
}
if (!$input->repositoryName) {
throw new BadRequestHttpException('Repository name is required for Git image deployment');
}
if (!$input->branch) {
throw new BadRequestHttpException('Branch is required for Git image deployment');
}
$repository = $client->getRepository();
$data = [
'dsk' => (string) $input->diskNumber,
'par' => (string) $input->partitionNumber,
'ifs' => "1",
'idi' => 1,
'nci' => $input->repositoryName,
'ipr' => $repository->getIp(),
'nfn' => 'RestaurarImagenGit',
'ptc' => 'git',
'ids' => '0',
'ref' => $input->hexsha ?? $input->tag
];
$url = 'https://'.$client->getIp().':8000/opengnsys/RestaurarImagenGit';
$response = $this->createRequest(
method: 'POST',
url: $url,
params: [
'json' => $data,
],
token: $client->getToken(),
);
$this->logger->info('Deploying Git image', [
'repository' => $input->repositoryName,
'branch' => $input->branch,
'ref' => $input->hexsha ?? $input->tag,
'client' => $client->getIp()
]);
if (isset($response['error']) && $response['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) {
throw new BadRequestHttpException('Error deploying Git image');
}
$jobId = $response['job_id'];
$client->setStatus(ClientStatus::BUSY);
$this->entityManager->persist($client);
$this->entityManager->flush();
return $jobId;
}
}

View File

@ -5,6 +5,7 @@ namespace App\Controller\OgRepository\Git;
use App\Controller\OgRepository\AbstractOgRepositoryController;
use App\Entity\Image;
use App\Entity\ImageRepository;
use App\Entity\GitRepository;
use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
@ -24,15 +25,16 @@ class CreateRepositoryAction extends AbstractOgRepositoryController
* @throws RedirectionExceptionInterface
* @throws ClientExceptionInterface
*/
public function __invoke(Image $image, ImageRepository $repository): JsonResponse
public function __invoke(GitRepository $gitRepository): JsonResponse
{
$params = [
'json' => [
'name' => $image->getName(),
'name' => $gitRepository->getName(),
'description' => $gitRepository->getDescription(),
]
];
$content = $this->createRequest('POST', 'http://'.$repository->getIp().':8006/ogrepository/v1/git/repositories', $params);
$content = $this->createRequest('POST', 'http://'.$gitRepository->getRepository()->getIp().':8006/ogrepository/v1/git/repositories', $params);
if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR ) {
throw new BadRequestHttpException('Error creating repository');

View File

@ -0,0 +1,36 @@
<?php
namespace App\Controller\OgRepository\Git;
use App\Controller\OgRepository\AbstractOgRepositoryController;
use App\Dto\Input\GetBranchesInput;
use App\Entity\ImageRepository;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\Validator\Exception\ValidatorException;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
#[AsController]
class GetBranchesAction extends AbstractOgRepositoryController
{
/**
* @throws TransportExceptionInterface
* @throws ServerExceptionInterface
* @throws RedirectionExceptionInterface
* @throws ClientExceptionInterface
*/
public function __invoke(ImageRepository $repository, GetBranchesInput $input): JsonResponse
{
$content = $this->createRequest('GET', 'http://'.$repository->getIp().':8006/ogrepository/v1/git/repositories/'.$input->repositoryName.'/branches');
if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) {
throw new \Exception('Error obteniendo branches del repositorio');
}
return new JsonResponse(data: $content, status: Response::HTTP_OK);
}
}

View File

@ -26,6 +26,10 @@ class GetCollectionAction extends AbstractOgRepositoryController
{
$content = $this->createRequest('GET', 'http://'.$data->getIp().':8006/ogrepository/v1/git/repositories');
if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) {
throw new \Exception('Error obteniendo commits del repositorio');
}
return new JsonResponse(data: $content, status: Response::HTTP_OK);
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace App\Controller\OgRepository\Git;
use App\Controller\OgRepository\AbstractOgRepositoryController;
use App\Dto\Input\GetCommitsInput;
use App\Entity\ImageRepository;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\Validator\Exception\ValidatorException;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
#[AsController]
class GetCommitsAction extends AbstractOgRepositoryController
{
/**
* @throws TransportExceptionInterface
* @throws ServerExceptionInterface
* @throws RedirectionExceptionInterface
* @throws ClientExceptionInterface
*/
public function __invoke(ImageRepository $repository, GetCommitsInput $input): JsonResponse
{
$content = $this->createRequest('GET', 'http://'.$repository->getIp().':8006/ogrepository/v1/git/repositories/'.$input->repositoryName.'/branches/'.$input->branch.'/commits');
if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) {
throw new \Exception('Error obteniendo commits del repositorio');
}
return new JsonResponse(data: $content, status: Response::HTTP_OK);
}
}

View File

@ -3,7 +3,8 @@
namespace App\Controller\OgRepository\Git;
use App\Controller\OgRepository\AbstractOgRepositoryController;
use App\Entity\GitImageRepository;
use App\Entity\Image;
use App\Entity\ImageRepository;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\AsController;
@ -15,7 +16,7 @@ use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
#[AsController]
class GetTagsAction extends AbstractOgRepositoryController
class GetRepositoriesAction extends AbstractOgRepositoryController
{
/**
* @throws TransportExceptionInterface
@ -23,15 +24,12 @@ class GetTagsAction extends AbstractOgRepositoryController
* @throws RedirectionExceptionInterface
* @throws ClientExceptionInterface
*/
public function __invoke(GitImageRepository $gitImageRepository): JsonResponse
public function __invoke(ImageRepository $repository): JsonResponse
{
$repository = $gitImageRepository->getRepository();
$image = $gitImageRepository->getImage();
$content = $this->createRequest('GET', 'http://'.$repository->getIp().':8006/ogrepository/v1/git/repositories/'.$image->getName().'/tags');
$content = $this->createRequest('GET', 'http://'.$repository->getIp().':8006/ogrepository/v1/git/repositories');
if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR ) {
throw new BadRequestHttpException('Error getting tags');
throw new BadRequestHttpException('Error getting repositories');
}
return new JsonResponse(data: $content, status: Response::HTTP_OK);

View File

@ -3,13 +3,11 @@
namespace App\Controller\OgRepository\Git;
use App\Controller\OgRepository\AbstractOgRepositoryController;
use App\Entity\GitImageRepository;
use App\Entity\Image;
use App\Entity\Client;
use App\Entity\ImageRepository;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Validator\Exception\ValidatorException;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
@ -17,7 +15,7 @@ use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
#[AsController]
class CreateTagAction extends AbstractOgRepositoryController
class SshKeyAction extends AbstractOgRepositoryController
{
/**
* @throws TransportExceptionInterface
@ -25,23 +23,15 @@ class CreateTagAction extends AbstractOgRepositoryController
* @throws RedirectionExceptionInterface
* @throws ClientExceptionInterface
*/
public function __invoke(Image $image, ImageRepository $repository, GitImageRepository $gitImageRepository): JsonResponse
public function __invoke(ImageRepository $repository, Client $client): JsonResponse
{
$params = [
$content = $this->createRequest('POST', 'http://'.$repository->getIp().':8006/ogrepository/v1/git/ssh_key', [
'json' => [
'commit' => "HEAD",
'message' => $gitImageRepository->getVersion(),
'name' => $image->getName(),
]
];
'description' => $client->getName()->getOglive()->getName(),
'oglive' => $client->getOglive()->getDirectory().'.iso',
],
]);
$content = $this->createRequest('POST', 'http://'.$repository->getIp().':8006/ogrepository/v1/git/repositories/'.
$image->getName().'/tags', $params);
if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR ) {
throw new BadRequestHttpException('Error creating repository');
}
return new JsonResponse(data: [], status: Response::HTTP_OK);
return new JsonResponse(data: $content, status: Response::HTTP_OK);
}
}

View File

@ -1,73 +0,0 @@
<?php
namespace App\Controller\OgRepository\Git;
use App\Controller\OgRepository\AbstractOgRepositoryController;
use App\Entity\GitImageRepository;
use App\Entity\Image;
use App\Entity\ImageImageRepository;
use App\Entity\ImageRepository;
use App\Model\ImageStatus;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
class SyncAction extends AbstractOgRepositoryController
{
/**
* @throws TransportExceptionInterface
* @throws ServerExceptionInterface
* @throws RedirectionExceptionInterface
* @throws ClientExceptionInterface
*/
public function __invoke(ImageRepository $input): JsonResponse
{
$content = $this->createRequest('GET', 'http://' . $input->getIp() . ':8006/ogrepository/v1/git/repositories');
if (!isset($content['repositories'])) {
return new JsonResponse(data: 'No repositories found', status: Response::HTTP_NOT_FOUND);
}
$repository = $this->entityManager->getRepository(GitImageRepository::class);
$existingRepositories = $repository->findBy(['imageRepository' => $input]);
foreach ($content['repositories'] as $repositoryData) {
$gitImageRepositoryEntity = $repository->findOneBy([
'imageRepository' => $input,
'name' => $repositoryData
]);
$imageEntity = $this->entityManager->getRepository(Image::class)->findOneBy(['name' => $repositoryData]);
if (!$imageEntity) {
$imageEntity = new Image();
$imageEntity->setName($repositoryData);
$imageEntity->setRemotePc(false);
$imageEntity->setIsGlobal(false);
$this->entityManager->persist($imageEntity);
}
if (!$gitImageRepositoryEntity) {
$gitImageRepositoryEntity = new GitImageRepository();
$gitImageRepositoryEntity->setName($repositoryData);
$gitImageRepositoryEntity->setStatus(ImageStatus::SUCCESS);
$gitImageRepositoryEntity->setImageRepository($input);
$gitImageRepositoryEntity->setImage($imageEntity);
$gitImageRepositoryEntity->setBranch('main');
$gitImageRepositoryEntity->setCreated(true);
$gitImageRepositoryEntity->setCreatedAt(new \DateTime());
$this->entityManager->persist($gitImageRepositoryEntity);
}
}
$this->entityManager->flush();
return new JsonResponse(data: $content, status: Response::HTTP_OK);
}
}

View File

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace App\Dto\Input;
use ApiPlatform\Metadata\ApiProperty;
use App\Dto\Output\ClientOutput;
use App\Validator\Constraints\ClientsHaveSamePartitionCount;
use Symfony\Component\Serializer\Annotation\Groups;
#[ClientsHaveSamePartitionCount]
class DeployGitImageInput
{
#[Groups(['git-repository:write'])]
#[ApiProperty(description: 'The type of the image deployment', example: "")]
public ?string $type = 'git';
/**
* @var ClientOutput[]
*/
#[Groups(['git-repository:write'])]
#[ApiProperty(description: 'The clients to deploy the Git image to')]
public ?array $clients = [];
#[Groups(['git-repository:write'])]
#[ApiProperty(description: 'The disk number to deploy to', example: 1)]
public ?int $diskNumber = null;
#[Groups(['git-repository:write'])]
#[ApiProperty(description: 'The partition number to deploy to', example: 1)]
public ?int $partitionNumber = null;
#[Groups(['git-repository:write'])]
#[ApiProperty(description: 'The name of the Git repository', example: "my-git-repo")]
public ?string $repositoryName = null;
#[Groups(['git-repository:write'])]
#[ApiProperty(description: 'The Git branch to deploy', example: "main")]
public ?string $branch = null;
#[Groups(['git-repository:write'])]
#[ApiProperty(description: 'The Git commit hash (hexsha) to deploy', example: "a1b2c3d4e5f6")]
public ?string $hexsha = null;
#[Groups(['git-repository:write'])]
#[ApiProperty(description: 'Whether to queue the deployment', example: false)]
public bool $queue = false;
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Dto\Input;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use OpenApi\Annotations as OA;
/**
* @OA\Schema(
* description="Datos de entrada para obtener las branches de un repositorio Git",
* required={"repositoryName"}
* )
*/
final class GetBranchesInput
{
/**
* @OA\Property(
* description="Nombre del repositorio Git",
* example="demo-oggit"
* )
*/
#[Groups(['repository:write'])]
#[Assert\NotBlank(message: 'El nombre del repositorio es requerido')]
public ?string $repositoryName = null;
}

View File

@ -0,0 +1,36 @@
<?php
namespace App\Dto\Input;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use OpenApi\Annotations as OA;
/**
* @OA\Schema(
* description="Datos de entrada para obtener los commits de un repositorio Git",
* required={"repositoryName", "branch"}
* )
*/
final class GetCommitsInput
{
/**
* @OA\Property(
* description="Nombre del repositorio Git",
* example="demo-oggit"
* )
*/
#[Groups(['repository:write'])]
#[Assert\NotBlank(message: 'El nombre del repositorio es requerido')]
public ?string $repositoryName = null;
/**
* @OA\Property(
* description="Nombre de la rama del repositorio",
* example="main"
* )
*/
#[Groups(['repository:write'])]
#[Assert\NotBlank(message: 'El nombre de la rama es requerido')]
public ?string $branch = null;
}

View File

@ -1,33 +0,0 @@
<?php
namespace App\Dto\Input;
use App\Entity\GitImageRepository;
use App\Entity\ImageImageRepository;
use Symfony\Component\Serializer\Annotation\Groups;
final class GitImageRepositoryInput
{
#[Groups(['git-image-repository:write'])]
public ?string $description = '';
public function __construct(?GitImageRepository $gitImageRepository = null)
{
if (!$gitImageRepository) {
return;
}
$this->description = $gitImageRepository->getDescription();
}
public function createOrUpdateEntity(?GitImageRepository $gitImageRepository = null): GitImageRepository
{
if (!$gitImageRepository) {
$gitImageRepository = new GitImageRepository();
}
$gitImageRepository->setDescription($this->description);
return $gitImageRepository;
}
}

View File

@ -0,0 +1,71 @@
<?php
namespace App\Dto\Input;
use App\Entity\GitRepository;
use App\Repository\ImageRepositoryRepository;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use OpenApi\Annotations as OA;
/**
* @OA\Schema(
* description="Datos de entrada para crear o actualizar un repositorio Git",
* required={"name", "repository"}
* )
*/
final class GitRepositoryInput
{
/**
* @OA\Property(
* description="Nombre del repositorio Git",
* example="mi-repositorio"
* )
*/
#[Groups(['git-repository:write'])]
#[Assert\NotBlank(message: 'El nombre del repositorio es requerido')]
public ?string $name = null;
/**
* @OA\Property(
* description="Descripción del repositorio",
* example="Repositorio para el proyecto principal"
* )
*/
#[Groups(['git-repository:write'])]
public ?string $description = null;
/**
* @OA\Property(
* description="ID del ImageRepository donde se creará el repositorio",
* example=1
* )
*/
#[Groups(['git-repository:write'])]
#[Assert\NotNull(message: 'El ID del ImageRepository es requerido')]
#[Assert\Type(type: 'integer', message: 'El ID del ImageRepository debe ser un número entero')]
public ?int $repository = null; // Ahora es el ID del ImageRepository
public function __construct(?GitRepository $gitRepository = null)
{
if (!$gitRepository) {
return;
}
$this->name = $gitRepository->getName();
$this->description = $gitRepository->getDescription();
$this->repository = $gitRepository->getRepository()?->getId();
}
public function createOrUpdateEntity(?GitRepository $gitRepository = null): GitRepository
{
if (!$gitRepository) {
$gitRepository = new GitRepository();
}
$gitRepository->setName($this->name);
$gitRepository->setDescription($this->description);
return $gitRepository;
}
}

View File

@ -1,62 +0,0 @@
<?php
namespace App\Dto\Output;
use ApiPlatform\Metadata\Get;
use App\Entity\GitImageRepository;
use App\Entity\ImageImageRepository;
use Symfony\Component\Serializer\Annotation\Groups;
#[Get(shortName: 'GitImageRepository')]
final class GitImageRepositoryOutput extends AbstractOutput
{
#[Groups(['git-image-repository:read', 'image:read'])]
public ?ImageOutput $image = null;
#[Groups(['git-image-repository:read', 'image:read'])]
public ?ImageRepositoryOutput $imageRepository = null;
#[Groups(['git-image-repository:read', 'image:read'])]
public string $status = '';
#[Groups(['git-image-repository:read', 'image:read'])]
public ?string $description = '';
#[Groups(['git-image-repository:read', 'image:read'])]
public string $name = '';
#[Groups(['git-image-repository:read', 'image:read'])]
public ?string $branch = '';
#[Groups(['git-image-repository:read', 'image:read'])]
public ?string $tag = '';
#[Groups(['git-image-repository:read', 'image:read'])]
public \DateTime $createdAt;
#[Groups(['git-image-repository:read', 'image:read'])]
public ?string $createdBy = null;
public function __construct(GitImageRepository $gitImageRepository, array $context = [])
{
parent::__construct($gitImageRepository);
if (isset($context['groups']) && in_array('git-image-repository:read', $context['groups'])) {
if ($gitImageRepository->getImage()) {
$this->image = new ImageOutput($gitImageRepository->getImage());
}
}
if ($gitImageRepository->getRepository()) {
$this->imageRepository = new ImageRepositoryOutput($gitImageRepository->getRepository());
}
$this->name = $gitImageRepository->getName();
$this->description = $gitImageRepository->getDescription();
$this->status = $gitImageRepository->getStatus();
$this->branch = $gitImageRepository->getBranch();
$this->tag = $gitImageRepository->getTag();
$this->createdAt = $gitImageRepository->getCreatedAt();
$this->createdBy = $gitImageRepository->getCreatedBy();
}
}

View File

@ -0,0 +1,101 @@
<?php
namespace App\Dto\Output;
use ApiPlatform\Metadata\Get;
use App\Entity\GitRepository;
use Symfony\Component\Serializer\Annotation\Groups;
use OpenApi\Annotations as OA;
/**
* @OA\Schema(
* description="Datos de salida de un repositorio Git"
* )
*/
#[Get(shortName: 'GitRepository')]
final class GitRepositoryOutput
{
/**
* @OA\Property(
* description="UUID del repositorio Git",
* example="123e4567-e89b-12d3-a456-426614174000"
* )
*/
#[Groups(['git-repository:read', 'git-image-repository:read'])]
public string $uuid = '';
/**
* @OA\Property(
* description="ID del repositorio Git",
* example=1
* )
*/
#[Groups(['git-repository:read', 'git-image-repository:read'])]
public int $id = 0;
/**
* @OA\Property(
* description="Nombre del repositorio Git",
* example="mi-repositorio"
* )
*/
#[Groups(['git-repository:read', 'git-image-repository:read'])]
public string $name = '';
/**
* @OA\Property(
* description="Descripción del repositorio",
* example="Repositorio para el proyecto principal"
* )
*/
#[Groups(['git-repository:read', 'git-image-repository:read'])]
public ?string $description = '';
/**
* @OA\Property(
* description="Servidor donde se encuentra el repositorio",
* example={"id": 1, "name": "Servidor Principal", "ip": "192.168.1.100"}
* )
*/
#[Groups(['git-repository:read', 'git-image-repository:read'])]
public ?ImageRepositoryOutput $repository = null;
/**
* @OA\Property(
* description="Fecha de creación del repositorio",
* example="2024-01-01T00:00:00+00:00"
* )
*/
#[Groups(['git-repository:read', 'git-image-repository:read'])]
public ?\DateTime $createdAt = null;
/**
* @OA\Property(
* description="Usuario que creó el repositorio",
* example="admin"
* )
*/
#[Groups(['git-repository:read', 'git-image-repository:read'])]
public ?string $createdBy = null;
public function __construct(GitRepository $gitRepository, array $context = [])
{
if ($gitRepository->getId()) {
$this->uuid = $gitRepository->getUuid()->toString();
$this->id = $gitRepository->getId();
} else {
$this->uuid = $gitRepository->getUuid()->toString();
$this->id = 0;
}
$this->name = $gitRepository->getName();
$this->description = $gitRepository->getDescription();
if ($gitRepository->getRepository()) {
$this->repository = new ImageRepositoryOutput($gitRepository->getRepository());
}
$this->createdAt = $gitRepository->getCreatedAt() ?: new \DateTime();
$this->createdBy = $gitRepository->getCreatedBy();
}
}

View File

@ -1,134 +0,0 @@
<?php
namespace App\Entity;
use App\Repository\GitImageRepositoryRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: GitImageRepositoryRepository::class)]
class GitImageRepository extends AbstractEntity
{
use NameableTrait;
#[ORM\ManyToOne(inversedBy: 'gitImageRepositories')]
#[ORM\JoinColumn(nullable: false)]
private ?Image $image = null;
#[ORM\ManyToOne(inversedBy: 'gitImageRepositories')]
private ?ImageRepository $repository = null;
#[ORM\Column(length: 255)]
private ?string $status = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $description = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $branch = null;
#[ORM\Column(length: 255)]
private ?string $tag = null;
#[ORM\Column(nullable: true)]
private ?int $version = null;
#[ORM\Column]
private ?bool $created = null;
public function getImage(): ?Image
{
return $this->image;
}
public function setImage(?Image $image): static
{
$this->image = $image;
return $this;
}
public function getStatus(): ?string
{
return $this->status;
}
public function setStatus(string $status): static
{
$this->status = $status;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(?string $description): static
{
$this->description = $description;
return $this;
}
public function getBranch(): ?string
{
return $this->branch;
}
public function setBranch(?string $branch): static
{
$this->branch = $branch;
return $this;
}
public function getTag(): ?string
{
return $this->tag;
}
public function setTag(?string $tag): static
{
$this->tag = $tag;
return $this;
}
public function getVersion(): ?int
{
return $this->version;
}
public function setVersion(?int $version): static
{
$this->version = $version;
return $this;
}
public function isCreated(): ?bool
{
return $this->created;
}
public function setCreated(bool $created): static
{
$this->created = $created;
return $this;
}
public function getRepository(): ?ImageRepository
{
return $this->repository;
}
public function setRepository(?ImageRepository $imageRepository): static
{
$this->repository = $imageRepository;
return $this;
}
}

View File

@ -0,0 +1,51 @@
<?php
namespace App\Entity;
use App\Repository\GitRepositoryRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use App\Entity\AbstractEntity;
#[ORM\Entity(repositoryClass: GitRepositoryRepository::class)]
class GitRepository extends AbstractEntity
{
use NameableTrait;
#[ORM\Column(length: 255, nullable: true)]
private ?string $description = null;
#[ORM\ManyToOne(inversedBy: 'gitRepositories')]
private ?ImageRepository $repository = null;
public function __construct()
{
parent::__construct();
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(?string $description): static
{
$this->description = $description;
return $this;
}
public function getRepository(): ?ImageRepository
{
return $this->repository;
}
public function setRepository(?ImageRepository $repository): static
{
$this->repository = $repository;
return $this;
}
}

View File

@ -43,12 +43,6 @@ class Image extends AbstractEntity
#[ORM\Column(nullable: true)]
private ?int $version = null;
/**
* @var Collection<int, GitImageRepository>
*/
#[ORM\OneToMany(mappedBy: 'image', targetEntity: GitImageRepository::class,cascade: ['persist'], orphanRemoval: true)]
private Collection $gitImageRepositories;
#[ORM\Column(length: 255)]
private ?string $type = null;
@ -56,7 +50,6 @@ class Image extends AbstractEntity
{
parent::__construct();
$this->imageImageRepositories = new ArrayCollection();
$this->gitImageRepositories = new ArrayCollection();
}
public function getSoftwareProfile(): ?SoftwareProfile
@ -187,36 +180,6 @@ class Image extends AbstractEntity
return $this;
}
/**
* @return Collection<int, GitImageRepository>
*/
public function getGitImageRepositories(): Collection
{
return $this->gitImageRepositories;
}
public function addGitImageRepository(GitImageRepository $gitImageRepository): static
{
if (!$this->gitImageRepositories->contains($gitImageRepository)) {
$this->gitImageRepositories->add($gitImageRepository);
$gitImageRepository->setImage($this);
}
return $this;
}
public function removeGitImageRepository(GitImageRepository $gitImageRepository): static
{
if ($this->gitImageRepositories->removeElement($gitImageRepository)) {
// set the owning side to null (unless already changed)
if ($gitImageRepository->getImage() === $this) {
$gitImageRepository->setImage(null);
}
}
return $this;
}
public function getType(): ?string
{
return $this->type;

View File

@ -32,17 +32,10 @@ class ImageRepository extends AbstractEntity
#[ORM\Column(length: 255, nullable: true)]
private ?string $sshPort = null;
/**
* @var Collection<int, GitImageRepository>
*/
#[ORM\OneToMany(mappedBy: 'repository', targetEntity: GitImageRepository::class)]
private Collection $gitImageRepositories;
public function __construct()
{
parent::__construct();
$this->imageImageRepositories = new ArrayCollection();
$this->gitImageRepositories = new ArrayCollection();
}
public function getIp(): ?string
@ -122,34 +115,4 @@ class ImageRepository extends AbstractEntity
return $this;
}
/**
* @return Collection<int, GitImageRepository>
*/
public function getGitImageRepositories(): Collection
{
return $this->gitImageRepositories;
}
public function addGitImageRepository(GitImageRepository $gitImageRepository): static
{
if (!$this->gitImageRepositories->contains($gitImageRepository)) {
$this->gitImageRepositories->add($gitImageRepository);
$gitImageRepository->setImageRepository($this);
}
return $this;
}
public function removeGitImageRepository(GitImageRepository $gitImageRepository): static
{
if ($this->gitImageRepositories->removeElement($gitImageRepository)) {
// set the owning side to null (unless already changed)
if ($gitImageRepository->getImageRepository() === $this) {
$gitImageRepository->setImageRepository(null);
}
}
return $this;
}
}

View File

@ -1,33 +0,0 @@
<?php
namespace App\Repository;
use App\Entity\GitImageRepository;
use App\Entity\Image;
use App\Entity\ImageRepository as Repository;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<GitImageRepository>
*/
class GitImageRepositoryRepository extends AbstractRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, GitImageRepository::class);
}
public function findLatestVersionByImageAndRepository(Image $image, Repository $repository): ?GitImageRepository
{
return $this->createQueryBuilder('i')
->andWhere('i.image = :imageId')
->setParameter('imageId', $image->getId())
->andWhere('i.repository = :repository')
->setParameter('repository', $repository->getId())
->orderBy('i.version', 'DESC')
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Repository;
use App\Entity\GitRepository;
use App\Repository\AbstractRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<GitRepository>
*/
class GitRepositoryRepository extends AbstractRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, GitRepository::class);
}
}

View File

@ -0,0 +1,139 @@
<?php
namespace App\Service;
use App\Entity\GitRepository;
use App\Entity\ImageRepository;
use App\Controller\OgRepository\Git\GetCollectionAction;
use App\Controller\OgRepository\Git\CreateRepositoryAction;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class ExternalGitRepositoryService
{
public function __construct(
private HttpClientInterface $httpClient,
private GetCollectionAction $getCollectionAction,
private CreateRepositoryAction $createRepositoryAction
) {}
/**
* Obtiene todos los repositorios de un servidor externo
*/
public function getRepositories(ImageRepository $imageRepository): array
{
try {
$response = $this->getCollectionAction->__invoke($imageRepository);
$content = json_decode($response->getContent(), true);
if ($response->getStatusCode() !== Response::HTTP_OK) {
throw new BadRequestHttpException('Error obteniendo repositorios del servidor externo');
}
// El GetCollectionAction devuelve directamente el contenido de la API externa
if (!isset($content['repositories'])) {
return [];
}
return $content['repositories'];
} catch (\Exception $e) {
throw new BadRequestHttpException('Error obteniendo repositorios: ' . $e->getMessage());
}
}
/**
* Obtiene un repositorio específico por nombre
*/
public function getRepository(string $name, ImageRepository $imageRepository): ?array
{
$repositories = $this->getRepositories($imageRepository);
foreach ($repositories as $repo) {
// La API externa devuelve los nombres como strings simples
if ($repo === $name) {
return ['name' => $repo];
}
}
return null;
}
/**
* Crea un repositorio en el servidor externo
*/
public function createRepository(GitRepository $gitRepository): array
{
try {
$response = $this->createRepositoryAction->__invoke($gitRepository);
if ($response->getStatusCode() !== Response::HTTP_OK) {
throw new BadRequestHttpException('Error creando repositorio en el servidor externo');
}
// El CreateRepositoryAction devuelve un array vacío en caso de éxito
return json_decode($response->getContent(), true) ?: [];
} catch (\Exception $e) {
throw new BadRequestHttpException('Error creando repositorio: ' . $e->getMessage());
}
}
/**
* Actualiza un repositorio en el servidor externo
*/
public function updateRepository(GitRepository $gitRepository): array
{
$response = $this->httpClient->request('PUT',
"http://{$gitRepository->getRepository()->getIp()}:8006/ogrepository/v1/git/repositories/{$gitRepository->getName()}",
[
'json' => [
'name' => $gitRepository->getName(),
'description' => $gitRepository->getDescription(),
]
]
);
if ($response->getStatusCode() !== Response::HTTP_OK) {
throw new BadRequestHttpException('Error actualizando repositorio en el servidor externo');
}
return $response->toArray();
}
/**
* Elimina un repositorio del servidor externo
*/
public function deleteRepository(string $name, ImageRepository $imageRepository): void
{
$response = $this->httpClient->request('DELETE',
"http://{$imageRepository->getIp()}:8006/ogrepository/v1/git/repositories/{$name}"
);
if ($response->getStatusCode() !== Response::HTTP_OK) {
throw new BadRequestHttpException('Error eliminando repositorio del servidor externo');
}
}
/**
* Convierte datos de la API externa a una entidad GitRepository
*/
public function createEntityFromExternalData($externalData, ImageRepository $imageRepository): GitRepository
{
$gitRepository = new GitRepository();
// La API externa devuelve los nombres como strings simples
if (is_string($externalData)) {
$gitRepository->setName($externalData);
} elseif (is_array($externalData) && isset($externalData['name'])) {
$gitRepository->setName($externalData['name']);
$gitRepository->setDescription($externalData['description'] ?? null);
} else {
throw new \InvalidArgumentException('Los datos externos deben ser string o array con nombre');
}
$gitRepository->setRepository($imageRepository);
return $gitRepository;
}
}

View File

@ -1,68 +0,0 @@
<?php
namespace App\State\Processor;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
use ApiPlatform\State\ProcessorInterface;
use ApiPlatform\Validator\ValidatorInterface;
use App\Dto\Input\GitImageRepositoryInput;
use App\Dto\Output\GitImageRepositoryOutput;
use App\Repository\GitImageRepositoryRepository;
readonly class GitImageRepositoryProcessor implements ProcessorInterface
{
public function __construct(
private GitImageRepositoryRepository $imageRepository,
private ValidatorInterface $validator,
)
{
}
/**
* @throws \Exception
*/
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): GitImageRepositoryOutput|null
{
switch ($operation){
case $operation instanceof Post:
case $operation instanceof Put:
case $operation instanceof Patch:
return $this->processCreateOrUpdate($data, $operation, $uriVariables, $context);
case $operation instanceof Delete:
return $this->processDelete($data, $operation, $uriVariables, $context);
}
}
/**
* @throws \Exception
*/
private function processCreateOrUpdate($data, Operation $operation, array $uriVariables = [], array $context = []): GitImageRepositoryOutput
{
if (!($data instanceof GitImageRepositoryInput)) {
throw new \Exception(sprintf('data is not instance of %s', GitImageRepositoryInput::class));
}
$entity = null;
if (isset($uriVariables['uuid'])) {
$entity = $this->imageRepository->findOneByUuid($uriVariables['uuid']);
}
$image = $data->createOrUpdateEntity($entity);
$this->validator->validate($image);
$this->imageRepository->save($image);
return new GitImageRepositoryOutput($image);
}
private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null
{
$user = $this->imageRepository->findOneByUuid($uriVariables['uuid']);
$this->imageRepository->delete($user);
return null;
}
}

View File

@ -0,0 +1,113 @@
<?php
namespace App\State\Processor;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
use ApiPlatform\State\ProcessorInterface;
use ApiPlatform\Validator\ValidatorInterface;
use App\Dto\Input\GitRepositoryInput;
use App\Dto\Output\GitRepositoryOutput;
use App\Service\ExternalGitRepositoryService;
use App\Repository\ImageRepositoryRepository;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\KernelInterface;
readonly class GitRepositoryProcessor implements ProcessorInterface
{
public function __construct(
private ExternalGitRepositoryService $externalService,
private ImageRepositoryRepository $imageRepositoryRepository,
private ValidatorInterface $validator,
private RequestStack $requestStack,
private KernelInterface $kernel
)
{
}
/**
* @throws \Exception
*/
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): GitRepositoryOutput|null
{
switch ($operation){
case $operation instanceof Post:
case $operation instanceof Put:
case $operation instanceof Patch:
return $this->processCreateOrUpdate($data, $operation, $uriVariables, $context);
case $operation instanceof Delete:
return $this->processDelete($data, $operation, $uriVariables, $context);
}
}
/**
* @throws \Exception
*/
private function processCreateOrUpdate($data, Operation $operation, array $uriVariables = [], array $context = []): GitRepositoryOutput
{
if (!($data instanceof GitRepositoryInput)) {
throw new \Exception(sprintf('data is not instance of %s', GitRepositoryInput::class));
}
// Obtener el repositoryId del input data
$repositoryId = $data->repository;
if (!$repositoryId) {
throw new \InvalidArgumentException('El campo "repository" es requerido');
}
$imageRepository = $this->imageRepositoryRepository->find($repositoryId);
if (!$imageRepository) {
throw new \Exception('ImageRepository no encontrado');
}
$gitRepository = $data->createOrUpdateEntity();
$gitRepository->setRepository($imageRepository);
$this->validator->validate($gitRepository);
if ($this->kernel->getEnvironment() !== 'test') {
try {
if ($operation instanceof Post) {
$this->externalService->createRepository($gitRepository);
} else {
$this->externalService->updateRepository($gitRepository);
}
} catch (\Exception $e) {
throw new \Exception('Error en operación con API externa: ' . $e->getMessage());
}
}
return new GitRepositoryOutput($gitRepository);
}
private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null
{
// Obtener el ImageRepository desde los parámetros
$request = $this->requestStack->getCurrentRequest();
$repositoryId = $request->query->get('repository');
$name = $uriVariables['name'];
if (!$repositoryId || !$name) {
throw new \InvalidArgumentException('Los parámetros "repository" y "name" son requeridos');
}
$imageRepository = $this->imageRepositoryRepository->find($repositoryId);
if (!$imageRepository) {
throw new \Exception('ImageRepository no encontrado');
}
if ($this->kernel->getEnvironment() !== 'test') {
try {
$this->externalService->deleteRepository($name, $imageRepository);
} catch (\Exception $e) {
throw new \Exception('Error eliminando repositorio en API externa: ' . $e->getMessage());
}
}
return null;
}
}

View File

@ -12,12 +12,14 @@ use ApiPlatform\Validator\ValidatorInterface;
use App\Dto\Input\ImageImageRepositoryInput;
use App\Dto\Output\ImageImageRepositoryOutput;
use App\Repository\ImageImageRepositoryRepository;
use Doctrine\ORM\EntityManagerInterface;
readonly class ImageImageRepositoryProcessor implements ProcessorInterface
{
public function __construct(
private ImageImageRepositoryRepository $imageRepository,
private ValidatorInterface $validator,
private EntityManagerInterface $entityManager
)
{
}
@ -60,8 +62,18 @@ readonly class ImageImageRepositoryProcessor implements ProcessorInterface
private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null
{
$user = $this->imageRepository->findOneByUuid($uriVariables['uuid']);
$this->imageRepository->delete($user);
$imageImageRepository = $this->imageRepository->findOneByUuid($uriVariables['uuid']);
$this->imageRepository->delete($imageImageRepository);
$repository = $imageImageRepository->getRepository();
$image = $imageImageRepository->getImage();
$imageImageCollection = $image->getImageImageRepositories();
if ($imageImageCollection->isEmpty()) {
$this->entityManager->remove($image);
$this->entityManager->flush();
}
return null;
}

View File

@ -20,6 +20,8 @@ use App\Repository\ImageRepository;
use App\Repository\ImageRepositoryRepository as ImageRepositoryRepository;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Psr\Log\LoggerInterface;
readonly class ImageProcessor implements ProcessorInterface
{
@ -28,6 +30,7 @@ readonly class ImageProcessor implements ProcessorInterface
private ValidatorInterface $validator,
private CreateImageAction $createImageActionController,
private KernelInterface $kernel,
private LoggerInterface $logger,
)
{
}
@ -63,21 +66,36 @@ readonly class ImageProcessor implements ProcessorInterface
$entity = $this->imageRepository->findOneByUuid($uriVariables['uuid']);
}
try {
$response = null;
$image = null;
if ($data->selectedImage) {
$response = $this->createImageActionController->__invoke($data->queue, $data->selectedImage->getEntity(), $data->partition->getEntity(), $data->client->getEntity(), $data->gitRepository);
} else {
$image = $data->createOrUpdateEntity($entity);
if ($data->selectedImage){
$response = $this->createImageActionController->__invoke($data->selectedImage->getEntity(), $data->partition->getEntity(), $data->client->getEntity());
} else {
$image = $data->createOrUpdateEntity($entity);
if ($this->kernel->getEnvironment() !== 'test') {
$response = $this->createImageActionController->__invoke($data->queue, $image, null, null, $data->gitRepository);
}
if ($this->kernel->getEnvironment() !== 'test') {
$response = $this->createImageActionController->__invoke($image);
$this->validator->validate($image);
$this->imageRepository->save($image);
}
$this->validator->validate($image);
$this->imageRepository->save($image);
}
if ($response instanceof JsonResponse && $response->getStatusCode() >= 400) {
$content = json_decode($response->getContent(), true);
throw new \Exception($content['error'] ?? 'Error creating image');
}
return new ImageOutput($data->selectedImage?->getEntity() ?? $image);
return new ImageOutput($data->selectedImage?->getEntity() ?? $image);
} catch (\Exception $e) {
$this->logger->error('Error processing image creation/update', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
throw new \Exception('Error processing image: ' . $e->getMessage());
}
}
private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null

View File

@ -1,71 +0,0 @@
<?php
namespace App\State\Provider;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Put;
use ApiPlatform\State\Pagination\TraversablePaginator;
use ApiPlatform\State\ProviderInterface;
use App\Dto\Input\GitImageRepositoryInput;
use App\Dto\Output\GitImageRepositoryOutput;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
readonly class GitImageRepositoryProvider implements ProviderInterface
{
public function __construct(
private ProviderInterface $collectionProvider,
private ProviderInterface $itemProvider
)
{
}
public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
{
switch ($operation){
case $operation instanceof GetCollection:
return $this->provideCollection($operation, $uriVariables, $context);
case $operation instanceof Patch:
case $operation instanceof Put:
return $this->provideInput($operation, $uriVariables, $context);
case $operation instanceof Get:
return $this->provideItem($operation, $uriVariables, $context);
}
}
private function provideCollection(Operation $operation, array $uriVariables = [], array $context = []): object
{
$paginator = $this->collectionProvider->provide($operation, $uriVariables, $context);
$items = new \ArrayObject();
foreach ($paginator->getIterator() as $item){
$items[] = new GitImageRepositoryOutput($item, $context);
}
return new TraversablePaginator($items, $paginator->getCurrentPage(), $paginator->getItemsPerPage(), $paginator->getTotalItems());
}
public function provideItem(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
{
$item = $this->itemProvider->provide($operation, $uriVariables, $context);
if (!$item) {
throw new NotFoundHttpException('ImageImageRepository not found');
}
return new GitImageRepositoryOutput($item);
}
public function provideInput(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
{
if (isset($uriVariables['uuid'])) {
$item = $this->itemProvider->provide($operation, $uriVariables, $context);
return $item !== null ? new GitImageRepositoryInput($item) : null;
}
return new GitImageRepositoryInput();
}
}

View File

@ -0,0 +1,136 @@
<?php
namespace App\State\Provider;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Put;
use ApiPlatform\State\Pagination\TraversablePaginator;
use ApiPlatform\State\ProviderInterface;
use App\Dto\Input\GitRepositoryInput;
use App\Dto\Output\GitRepositoryOutput;
use App\Service\ExternalGitRepositoryService;
use App\Repository\ImageRepositoryRepository;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpFoundation\RequestStack;
readonly class GitRepositoryProvider implements ProviderInterface
{
public function __construct(
private ExternalGitRepositoryService $externalService,
private ImageRepositoryRepository $imageRepositoryRepository,
private RequestStack $requestStack
)
{
}
public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
{
switch ($operation){
case $operation instanceof GetCollection:
return $this->provideCollection($operation, $uriVariables, $context);
case $operation instanceof Patch:
case $operation instanceof Put:
return $this->provideInput($operation, $uriVariables, $context);
case $operation instanceof Get:
return $this->provideItem($operation, $uriVariables, $context);
}
}
private function provideCollection(Operation $operation, array $uriVariables = [], array $context = []): object
{
// Obtener el ImageRepository desde los parámetros de la request
$request = $this->requestStack->getCurrentRequest();
$repositoryId = $request->query->get('repository');
if (!$repositoryId) {
throw new \InvalidArgumentException('El parámetro "repository" es requerido');
}
$imageRepository = $this->imageRepositoryRepository->find($repositoryId);
if (!$imageRepository) {
throw new NotFoundHttpException('ImageRepository no encontrado');
}
// Obtener repositorios de la API externa
$externalRepositories = $this->externalService->getRepositories($imageRepository);
$items = new \ArrayObject();
foreach ($externalRepositories as $externalData) {
$gitRepository = $this->externalService->createEntityFromExternalData($externalData, $imageRepository);
$items[] = new GitRepositoryOutput($gitRepository, $context);
}
// Simular paginación básica
$page = $request->query->getInt('page', 1);
$limit = $request->query->getInt('limit', 10);
$offset = ($page - 1) * $limit;
$paginatedItems = array_slice($items->getArrayCopy(), $offset, $limit);
return new TraversablePaginator(
new \ArrayObject($paginatedItems),
$page,
$limit,
count($items)
);
}
public function provideItem(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
{
// Obtener el nombre del repositorio desde los parámetros
$request = $this->requestStack->getCurrentRequest();
$repositoryId = $request->query->get('repository');
$name = $uriVariables['name'] ?? $request->query->get('name');
if (!$repositoryId || !$name) {
throw new \InvalidArgumentException('Los parámetros "repository" y "name" son requeridos');
}
$imageRepository = $this->imageRepositoryRepository->find($repositoryId);
if (!$imageRepository) {
throw new NotFoundHttpException('ImageRepository no encontrado');
}
// Obtener repositorio específico de la API externa
$externalData = $this->externalService->getRepository($name, $imageRepository);
if (!$externalData) {
throw new NotFoundHttpException('GitRepository no encontrado');
}
$gitRepository = $this->externalService->createEntityFromExternalData($externalData, $imageRepository);
return new GitRepositoryOutput($gitRepository);
}
public function provideInput(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
{
if (isset($uriVariables['name'])) {
// Para operaciones de actualización, necesitamos obtener el repositorio existente
$request = $this->requestStack->getCurrentRequest();
$repositoryId = $request->query->get('repository');
$name = $uriVariables['name'];
if (!$repositoryId) {
throw new \InvalidArgumentException('El parámetro "repository" es requerido');
}
$imageRepository = $this->imageRepositoryRepository->find($repositoryId);
if (!$imageRepository) {
throw new NotFoundHttpException('ImageRepository no encontrado');
}
$externalData = $this->externalService->getRepository($name, $imageRepository);
if (!$externalData) {
throw new NotFoundHttpException('GitRepository no encontrado');
}
$gitRepository = $this->externalService->createEntityFromExternalData($externalData, $imageRepository);
return new GitRepositoryInput($gitRepository);
}
return new GitRepositoryInput();
}
}