-
-
Notifications
You must be signed in to change notification settings - Fork 66
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #303 from vtsykun/feat/composer-push
Allow to publish artifact package via API or composer push plugin
Showing
21 changed files
with
626 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Packeton\Controller; | ||
|
||
use Packeton\Exception\ValidationException; | ||
use Packeton\Form\Handler\PushPackageHandler; | ||
use Packeton\Form\Type\Push\NexusPushType; | ||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | ||
use Symfony\Component\Form\FormInterface; | ||
use Symfony\Component\HttpFoundation\JsonResponse; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpFoundation\Response; | ||
use Symfony\Component\Routing\Annotation\Route; | ||
use Symfony\Component\Security\Http\Attribute\IsGranted; | ||
|
||
#[Route(defaults: ['_format' => 'json'])] | ||
class PushPackagesController extends AbstractController | ||
{ | ||
#[IsGranted('ROLE_MAINTAINER')] | ||
#[Route('/packages/upload/{name}/{version}', name: 'package_push_nexus', requirements: ['name' => '%package_name_regex%'], methods: ['PUT', 'POST'])] | ||
#[Route('/api/packages/upload/{name}/{version}', name: 'package_push_api', requirements: ['name' => '%package_name_regex%'], methods: ['PUT'])] | ||
public function pushNexusAction(PushPackageHandler $handler, Request $request, string $name, string $version): Response | ||
{ | ||
$form = $this->createApiForm(NexusPushType::class, options: ['method' => $request->getMethod()]); | ||
|
||
try { | ||
$handler($form, $request, $name, $version, $this->getUser()); | ||
} catch (ValidationException $e) { | ||
return new JsonResponse(['title' => $e->getMessage(), 'errors' => $e->getFormErrors()], 400); | ||
} | ||
|
||
return new JsonResponse([], 201); | ||
} | ||
|
||
protected function createApiForm(string $type, mixed $data = null, array $options = []): FormInterface | ||
{ | ||
$options['csrf_protection'] = false; | ||
return $this->container->get('form.factory')->createNamed('', $type, $data, $options); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Packeton\Exception; | ||
|
||
use Symfony\Component\Form\FormInterface; | ||
|
||
class ValidationException extends \RuntimeException implements DebugHttpExceptionInterface | ||
{ | ||
private array $errors = []; | ||
|
||
public static function create(string $message, FormInterface|array $errors = [], ?\Throwable $previous = null): static | ||
{ | ||
$exception = new static($message, 400, $previous); | ||
$exception->errors = $errors instanceof FormInterface ? $exception->getErrors($errors) : $errors; | ||
|
||
return $exception; | ||
} | ||
|
||
private function getErrors(FormInterface $form): array | ||
{ | ||
$errors = $base = []; | ||
|
||
foreach ($form->getErrors() as $error) { | ||
$base[] = $error->getMessage(); | ||
} | ||
foreach ($form as $child) { | ||
foreach ($child->getErrors(true) as $error) { | ||
$errors[$child->getName()] = $error->getMessage(); | ||
} | ||
} | ||
if (count($base) > 0) { | ||
$errors['root'] = implode("\n", $base); | ||
} | ||
|
||
return $errors; | ||
} | ||
|
||
public function getFormErrors(): array | ||
{ | ||
return $this->errors; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Packeton\Exception; | ||
|
||
class ZipballException extends \RuntimeException implements DebugHttpExceptionInterface | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Packeton\Form\Handler; | ||
|
||
use Doctrine\Persistence\ManagerRegistry; | ||
use Packeton\Entity\Package; | ||
use Packeton\Entity\User; | ||
use Packeton\Exception\ValidationException; | ||
use Packeton\Exception\ZipballException; | ||
use Packeton\Form\Model\PushRequestDtoInterface; | ||
use Packeton\Model\PackageManager; | ||
use Packeton\Package\RepTypes; | ||
use Packeton\Repository\PackageRepository; | ||
use Packeton\Service\Artifact\ArtifactPushHandler; | ||
use Symfony\Component\Form\FormInterface; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\Security\Core\User\UserInterface; | ||
|
||
class PushPackageHandler | ||
{ | ||
public function __construct( | ||
private readonly ManagerRegistry $registry, | ||
private readonly PackageManager $packageManager, | ||
private readonly ArtifactPushHandler $handler, | ||
) { | ||
} | ||
|
||
public function __invoke(FormInterface $form, Request $request, string $name, ?string $version = null, ?UserInterface $user = null): void | ||
{ | ||
$request->request->set('version', $version ?? 'dev-master'); | ||
|
||
$form->handleRequest($request); | ||
if (!$form->isSubmitted() || !$form->isValid()) { | ||
throw ValidationException::create("Validation errors", $form); | ||
} | ||
|
||
/** @var PushRequestDtoInterface $dtoRequest */ | ||
$dtoRequest = $form->getData(); | ||
$package = $this->getRepo()->getPackageByName($name); | ||
if (null === $package) { | ||
$package = $this->createArtifactPackage($name, $user); | ||
} | ||
|
||
try { | ||
$this->handler->addVersion($dtoRequest, $package); | ||
} catch (ZipballException $e) { | ||
throw ValidationException::create($e->getMessage(), previous: $e); | ||
} | ||
} | ||
|
||
private function getRepo(): PackageRepository | ||
{ | ||
return $this->registry->getRepository(Package::class); | ||
} | ||
|
||
private function createArtifactPackage(string $name, ?UserInterface $user = null): Package | ||
{ | ||
$em = $this->registry->getManager(); | ||
$package = new Package(); | ||
$package->setName($name); | ||
$package->setRepoType(RepTypes::CUSTOM); | ||
$package->setRepositoryPath(null); | ||
|
||
$user = null !== $user ? | ||
$this->registry->getRepository(User::class)->findOneBy(['username' => $user->getUserIdentifier()]) | ||
: $user; | ||
|
||
if ($user instanceof User) { | ||
$package->addMaintainer($user); | ||
} | ||
|
||
$em->persist($package); | ||
$em->flush(); | ||
|
||
$this->packageManager->insetPackage($package); | ||
|
||
return $package; | ||
} | ||
} |
Oops, something went wrong.