Skip to content

Commit

Permalink
Add support for showing all package versions. Refs #175.
Browse files Browse the repository at this point in the history
  • Loading branch information
nickygerritsen committed Jul 7, 2020
1 parent b456d03 commit 29e4703
Show file tree
Hide file tree
Showing 23 changed files with 651 additions and 12 deletions.
1 change: 1 addition & 0 deletions config/packages/security.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ security:
- { path: ^/$, roles: ROLE_USER }
- { path: ^/organization/.+/overview$, roles: ROLE_ORGANIZATION_ANONYMOUS_USER }
- { path: ^/organization/.+/package$, roles: ROLE_ORGANIZATION_ANONYMOUS_USER }
- { path: ^/organization/.+/package/.+/details$, roles: ROLE_ORGANIZATION_ANONYMOUS_USER }
- { path: ^/organization/.+(/.+)*, roles: ROLE_ORGANIZATION_MEMBER }
- { path: ^/downloads, host: '([a-z0-9_-]+)\.repo\.(.+)', roles: IS_AUTHENTICATED_ANONYMOUSLY}
- { path: ^/, host: '([a-z0-9_-]+)\.repo\.(.+)', roles: ROLE_ORGANIZATION }
2 changes: 1 addition & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ parameters:
path: src/Service/Twig/DateExtension.php
-
message: "#^Expression on left side of \\?\\? is not nullable\\.$#"
count: 2
count: 4
path: src/Service/PackageSynchronizer/ComposerPackageSynchronizer.php
-
message: "#^Variable \\$http_response_header in isset\\(\\) always exists and is not nullable\\.$#"
Expand Down
13 changes: 13 additions & 0 deletions src/Controller/OrganizationController.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,19 @@ public function removePackage(Organization $organization, Package $package): Res
return $this->redirectToRoute('organization_packages', ['organization' => $organization->alias()]);
}

/**
* @Route("/organization/{organization}/package/{package}/details", name="organization_package_details", methods={"GET"}, requirements={"organization"="%organization_pattern%","package"="%uuid_pattern%"})
*/
public function packageDetails(Organization $organization, Package $package, Request $request): Response
{
return $this->render('organization/package/details.html.twig', [
'organization' => $organization,
'package' => $package,
'count' => $this->packageQuery->versionCount($package->id()),
'versions' => $this->packageQuery->getVersions($package->id(), 20, (int) $request->get('offset', 0)),
]);
}

/**
* @Route("/organization/{organization}/package/{package}/stats", name="organization_package_stats", methods={"GET"}, requirements={"organization"="%organization_pattern%","package"="%uuid_pattern%"})
*/
Expand Down
49 changes: 48 additions & 1 deletion src/Entity/Organization/Package.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
namespace Buddy\Repman\Entity\Organization;

use Buddy\Repman\Entity\Organization;
use Buddy\Repman\Entity\Organization\Package\Version;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Ramsey\Uuid\UuidInterface;

Expand Down Expand Up @@ -100,6 +103,12 @@ class Package
*/
private ?\DateTimeImmutable $lastScanDate = null;

/**
* @var Collection<int,Version>|Version[]
* @ORM\OneToMany(targetEntity="Buddy\Repman\Entity\Organization\Package\Version", mappedBy="package", cascade={"persist"}, orphanRemoval=true)
*/
private Collection $versions;

/**
* @param mixed[] $metadata
*/
Expand All @@ -109,6 +118,7 @@ public function __construct(UuidInterface $id, string $type, string $url, array
$this->type = $type;
$this->repositoryUrl = $url;
$this->metadata = $metadata;
$this->versions = new ArrayCollection();
}

public function id(): UuidInterface
Expand All @@ -134,12 +144,20 @@ public function repositoryUrl(): string
return $this->repositoryUrl;
}

public function syncSuccess(string $name, string $description, string $latestReleasedVersion, \DateTimeImmutable $latestReleaseDate): void
/**
* @param string[] $encounteredVersions
*/
public function syncSuccess(string $name, string $description, string $latestReleasedVersion, array $encounteredVersions, \DateTimeImmutable $latestReleaseDate): void
{
$this->setName($name);
$this->description = $description;
$this->latestReleasedVersion = $latestReleasedVersion;
$this->latestReleaseDate = $latestReleaseDate;
foreach ($this->versions as $version) {
if (!in_array($version->version(), $encounteredVersions, true)) {
$this->versions->removeElement($version);
}
}
$this->lastSyncAt = new \DateTimeImmutable();
$this->lastSyncError = null;
}
Expand Down Expand Up @@ -246,4 +264,33 @@ private function setName(string $name): void

$this->name = $name;
}

/**
* @return Collection<int,Version>|Version[]
*/
public function versions(): Collection
{
return $this->versions;
}

public function addOrUpdateVersion(Version $version): void
{
if ($this->getVersion($version->version()) !== false) {
$this->getVersion($version->version())->setReference($version->reference());
$this->getVersion($version->version())->setSize($version->size());

return;
}

$version->setPackage($this);
$this->versions->add($version);
}

/**
* @return Version|false
*/
public function getVersion(string $versionString)
{
return $this->versions->filter(fn (Version $version) => $version->version() === $versionString)->first();
}
}
107 changes: 107 additions & 0 deletions src/Entity/Organization/Package/Version.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

declare(strict_types=1);

namespace Buddy\Repman\Entity\Organization\Package;

use Buddy\Repman\Entity\Organization\Package;
use Doctrine\ORM\Mapping as ORM;
use Ramsey\Uuid\UuidInterface;

/**
* @ORM\Entity
* @ORM\Table(
* name="organization_package_version",
* uniqueConstraints={@ORM\UniqueConstraint(name="package_version", columns={"package_id", "version"})},
* indexes={
* @ORM\Index(name="version_package_id_idx", columns={"package_id"}),
* @ORM\Index(name="version_date_idx", columns={"date"})
* }
* )
*/
class Version
{
/**
* @ORM\Id()
* @ORM\Column(type="uuid")
*/
private UuidInterface $id;

/**
* @ORM\ManyToOne(targetEntity="Buddy\Repman\Entity\Organization\Package", inversedBy="versions")
* @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
*/
private Package $package;

/**
* @ORM\Column(type="string")
*/
private string $version;

/**
* @ORM\Column(type="string")
*/
private string $reference;

/**
* @ORM\Column(type="integer")
*/
private int $size;

/**
* @ORM\Column(type="datetime_immutable")
*/
private \DateTimeImmutable $date;

public function __construct(
UuidInterface $id,
string $version,
string $reference,
int $size,
\DateTimeImmutable $date
) {
$this->id = $id;
$this->version = $version;
$this->reference = $reference;
$this->size = $size;
$this->date = $date;
}

public function id(): UuidInterface
{
return $this->id;
}

public function version(): string
{
return $this->version;
}

public function reference(): string
{
return $this->reference;
}

public function size(): int
{
return $this->size;
}

public function setReference(string $reference): void
{
$this->reference = $reference;
}

public function setSize(int $size): void
{
$this->size = $size;
}

public function setPackage(Package $package): void
{
if (isset($this->package)) {
throw new \RuntimeException('You can not change version package');
}
$this->package = $package;
}
}
42 changes: 42 additions & 0 deletions src/Migrations/Version20200706181929.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace Buddy\Repman\Migrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20200706181929 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->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');

$this->addSql('CREATE TABLE organization_package_version (id UUID NOT NULL, package_id UUID NOT NULL, version VARCHAR(255) NOT NULL, reference VARCHAR(255) NOT NULL, size INT NOT NULL, date TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE INDEX version_package_id_idx ON organization_package_version (package_id)');
$this->addSql('CREATE INDEX version_date_idx ON organization_package_version (date)');
$this->addSql('CREATE UNIQUE INDEX package_version ON organization_package_version (package_id, version)');
$this->addSql('COMMENT ON COLUMN organization_package_version.id IS \'(DC2Type:uuid)\'');
$this->addSql('COMMENT ON COLUMN organization_package_version.package_id IS \'(DC2Type:uuid)\'');
$this->addSql('COMMENT ON COLUMN organization_package_version.date IS \'(DC2Type:datetime_immutable)\'');
$this->addSql('ALTER TABLE organization_package_version ADD CONSTRAINT FK_62DF469AF44CABFF FOREIGN KEY (package_id) REFERENCES organization_package (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
}

public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');

$this->addSql('DROP TABLE organization_package_version');
}
}
41 changes: 41 additions & 0 deletions src/Query/User/Model/Version.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

namespace Buddy\Repman\Query\User\Model;

final class Version
{
private string $version;
private string $reference;
private int $size;
private \DateTimeImmutable $date;

public function __construct(string $version, string $reference, int $size, \DateTimeImmutable $date)
{
$this->version = $version;
$this->reference = $reference;
$this->size = $size;
$this->date = $date;
}

public function version(): string
{
return $this->version;
}

public function reference(): string
{
return $this->reference;
}

public function size(): int
{
return $this->size;
}

public function date(): \DateTimeImmutable
{
return $this->date;
}
}
8 changes: 8 additions & 0 deletions src/Query/User/PackageQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Buddy\Repman\Query\User\Model\Package;
use Buddy\Repman\Query\User\Model\PackageName;
use Buddy\Repman\Query\User\Model\ScanResult;
use Buddy\Repman\Query\User\Model\Version;
use Buddy\Repman\Query\User\Model\WebhookRequest;
use Munus\Control\Option;

Expand All @@ -30,6 +31,13 @@ public function count(string $organizationId): int;
*/
public function getById(string $id): Option;

public function versionCount(string $packageId): int;

/**
* @return Version[]
*/
public function getVersions(string $packageId, int $limit = 20, int $offset = 0): array;

public function getInstalls(string $packageId, int $lastDays = 30, ?string $version = null): Installs;

/**
Expand Down
42 changes: 42 additions & 0 deletions src/Query/User/PackageQuery/DbalPackageQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Buddy\Repman\Query\User\Model\Package;
use Buddy\Repman\Query\User\Model\PackageName;
use Buddy\Repman\Query\User\Model\ScanResult;
use Buddy\Repman\Query\User\Model\Version;
use Buddy\Repman\Query\User\Model\WebhookRequest;
use Buddy\Repman\Query\User\PackageQuery;
use Doctrine\DBAL\Connection;
Expand Down Expand Up @@ -103,6 +104,47 @@ public function getById(string $id): Option
return Option::some($this->hydratePackage($data));
}

public function versionCount(string $packageId): int
{
return (int) $this
->connection
->fetchColumn(
'SELECT COUNT(id) FROM "organization_package_version"
WHERE package_id = :package_id',
[
':package_id' => $packageId,
]
);
}

/**
* @return Version[]
*/
public function getVersions(string $packageId, int $limit = 20, int $offset = 0): array
{
return array_map(function (array $data): Version {
return new Version(
$data['version'],
$data['reference'],
$data['size'],
new \DateTimeImmutable($data['date'])
);
}, $this->connection->fetchAll(
'SELECT
version,
reference,
size,
date
FROM organization_package_version
WHERE package_id = :package_id
ORDER BY date DESC
LIMIT :limit OFFSET :offset', [
':package_id' => $packageId,
':limit' => $limit,
':offset' => $offset,
]));
}

public function getInstalls(string $packageId, int $lastDays = 30, ?string $version = null): Installs
{
$params = [
Expand Down
Loading

0 comments on commit 29e4703

Please sign in to comment.