Skip to content

Commit

Permalink
Dependency/dependant tracking (#426)
Browse files Browse the repository at this point in the history
* Basic dependency/dependant tracking

* Add other link types to test a design

* Allow searching for dependants

* Slightly nicer looking package list

* Move dependant link to overview & update/fix tests

* Fix doctrine schema validation

* Add missing foreign key

* Rename index to what doctrine suggests

* Test each of the link types

* Improve test coverage
  • Loading branch information
giggsey authored Apr 8, 2021
1 parent 507a4bb commit f4307ba
Show file tree
Hide file tree
Showing 19 changed files with 514 additions and 41 deletions.
5 changes: 5 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ parameters:
count: 1
path: src/Service/PackageSynchronizer/ComposerPackageSynchronizer.php

-
message: "#^Variable method call on Composer\\\\Package\\\\PackageInterface\\.$#"
count: 1
path: src/Service/PackageSynchronizer/ComposerPackageSynchronizer.php

-
message: "#^Variable property access on DateInterval\\.$#"
count: 1
Expand Down
19 changes: 19 additions & 0 deletions src/Controller/OrganizationController.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,32 @@ public function packageDetails(Organization $organization, PackageDetails $packa
{
$filter = Filter::fromRequest($request);

$packageLinks = $this->packageQuery->getLinks($package->id(), $organization->id());

/** @var string $packageName */
$packageName = $package->name();

$dependantCount = $this->packageQuery->getDependantCount($packageName, $organization->id());

$groupedPackageLinks = [];

foreach ($packageLinks as $packageLink) {
if (!isset($groupedPackageLinks[$packageLink->type()])) {
$groupedPackageLinks[$packageLink->type()] = [];
}

$groupedPackageLinks[$packageLink->type()][] = $packageLink;
}

return $this->render('organization/package/details.html.twig', [
'organization' => $organization,
'package' => $package,
'filter' => $filter,
'count' => $this->packageQuery->versionCount($package->id()),
'versions' => $this->packageQuery->getVersions($package->id(), $filter),
'installs' => $this->packageQuery->getInstalls($package->id(), 0),
'packageLinks' => $groupedPackageLinks,
'dependantCount' => $dependantCount,
]);
}

Expand Down
31 changes: 30 additions & 1 deletion src/Entity/Organization/Package.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Buddy\Repman\Entity\Organization;

use Buddy\Repman\Entity\Organization;
use Buddy\Repman\Entity\Organization\Package\Link;
use Buddy\Repman\Entity\Organization\Package\Version;
use Buddy\Repman\Entity\User\OAuthToken;
use Doctrine\Common\Collections\ArrayCollection;
Expand Down Expand Up @@ -120,6 +121,12 @@ class Package
*/
private Collection $versions;

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

/**
* @ORM\Column(type="integer")
*/
Expand All @@ -136,6 +143,7 @@ public function __construct(UuidInterface $id, string $type, string $url, array
$this->metadata = $metadata;
$this->keepLastReleases = $keepLastReleases;
$this->versions = new ArrayCollection();
$this->links = new ArrayCollection();
}

public function id(): UuidInterface
Expand Down Expand Up @@ -163,8 +171,9 @@ public function repositoryUrl(): string

/**
* @param string[] $encounteredVersions
* @param string[] $encounteredLinks
*/
public function syncSuccess(string $name, string $description, string $latestReleasedVersion, array $encounteredVersions, \DateTimeImmutable $latestReleaseDate): void
public function syncSuccess(string $name, string $description, string $latestReleasedVersion, array $encounteredVersions, array $encounteredLinks, \DateTimeImmutable $latestReleaseDate): void
{
$this->setName($name);
$this->description = $description;
Expand All @@ -175,6 +184,11 @@ public function syncSuccess(string $name, string $description, string $latestRel
$this->versions->removeElement($version);
}
}
foreach ($this->links as $link) {
if (!in_array($link->type().'-'.$link->target(), $encounteredLinks, true)) {
$this->links->removeElement($link);
}
}
$this->lastSyncAt = new \DateTimeImmutable();
$this->lastSyncError = null;
}
Expand Down Expand Up @@ -320,6 +334,21 @@ public function addOrUpdateVersion(Version $version): void
$this->versions->add($version);
}

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

public function addLink(Link $link): void
{
$link->setPackage($this);
$link->setOrganization($this->organization);
$this->links->add($link);
}

public function removeVersion(Version $version): void
{
$this->versions->removeElement($version);
Expand Down
111 changes: 111 additions & 0 deletions src/Entity/Organization/Package/Link.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?php

declare(strict_types=1);

namespace Buddy\Repman\Entity\Organization\Package;

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

/**
* @ORM\Entity
* @ORM\Table(
* name="organization_package_link",
* indexes={
* @ORM\Index(name="link_package_id_idx", columns={"package_id"}),
* @ORM\Index(name="link_target_idx", columns={"target"}),
* }
* )
*/
class Link
{
/**
* @ORM\Id()
* @ORM\Column(type="uuid")
*/
private UuidInterface $id;

/**
* @ORM\ManyToOne(targetEntity="Buddy\Repman\Entity\Organization")
* @ORM\JoinColumn(nullable=false)
*/
private Organization $organization;

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

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

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

/**
* @ORM\Column(name="`constraint`",type="string")
*/
private string $constraint;

private ?string $packageId;
private ?string $targetPackageId;

public function __construct(
UuidInterface $id,
string $type,
string $target,
string $constraint,
?string $packageId = null,
?string $targetPackageId = null
) {
$this->id = $id;
$this->type = $type;
$this->target = $target;
$this->constraint = $constraint;
$this->packageId = $packageId;
$this->targetPackageId = $targetPackageId;
}

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

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

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

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

public function setOrganization(Organization $organization): void
{
if (isset($this->organization)) {
throw new \RuntimeException('You can not change link organization');
}
$this->organization = $organization;
}

public function setPackage(Package $package): void
{
if (isset($this->package)) {
throw new \RuntimeException('You can not change link package');
}
$this->package = $package;
}
}
41 changes: 41 additions & 0 deletions src/Migrations/Version20210309201702.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?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 Version20210309201702 extends AbstractMigration
{
public function getDescription(): string
{
return 'Add package links';
}

public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs

$this->addSql('CREATE TABLE organization_package_link (id UUID NOT NULL, organization_id UUID NOT NULL, package_id UUID NOT NULL, target VARCHAR(255) NOT NULL, "constraint" VARCHAR(255) NOT NULL, type VARCHAR (255) NOT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE INDEX link_package_id_idx ON organization_package_link (package_id)');
$this->addSql('CREATE INDEX IDX_4A06082932C8A3DE ON organization_package_link (organization_id)');
$this->addSql('CREATE INDEX link_target_idx ON organization_package_link (target)');
$this->addSql('COMMENT ON COLUMN organization_package_link.id IS \'(DC2Type:uuid)\'');
$this->addSql('COMMENT ON COLUMN organization_package_link.package_id IS \'(DC2Type:uuid)\'');
$this->addSql('COMMENT ON COLUMN organization_package_link.organization_id IS \'(DC2Type:uuid)\'');
$this->addSql('ALTER TABLE organization_package_link ADD CONSTRAINT FK_CAKE4LIFE FOREIGN KEY (package_id) REFERENCES organization_package (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE organization_package_link ADD CONSTRAINT FK_4A06082932C8A3DE FOREIGN KEY (organization_id) REFERENCES organization (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
}

public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs

$this->addSql('DROP TABLE organization_package_link');
}
}
8 changes: 8 additions & 0 deletions src/Query/User/PackageQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Buddy\Repman\Query\User;

use Buddy\Repman\Entity\Organization\Package\Link;
use Buddy\Repman\Query\Filter;
use Buddy\Repman\Query\User\Model\Installs;
use Buddy\Repman\Query\User\Model\Package;
Expand Down Expand Up @@ -46,6 +47,13 @@ public function versionCount(string $packageId): int;
*/
public function getVersions(string $packageId, Filter $filter): array;

/**
* @return Link[]
*/
public function getLinks(string $packageId, string $organizationId): array;

public function getDependantCount(string $packageName, string $organizationId): int;

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

/**
Expand Down
Loading

0 comments on commit f4307ba

Please sign in to comment.