Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/docker 8.2 #1629

Merged
merged 3 commits into from
Dec 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
* [#1599](https://github.com/shlinkio/shlink/issues/1599) Added support for credentials on redis DSNs, either only password, or both username and password.
* [#1616](https://github.com/shlinkio/shlink/issues/1616) Added support to import orphan visits when importing short URLs from another Shlink instance.
* [#1519](https://github.com/shlinkio/shlink/issues/1519) Allowing to search short URLs by default domain.
* [#1555](https://github.com/shlinkio/shlink/issues/1555) Added full support for PHP 8.2, pdating the dockr image to this version.
* [#1555](https://github.com/shlinkio/shlink/issues/1555) and [#1625](https://github.com/shlinkio/shlink/issues/1625) Added full support for PHP 8.2, updating the docker image to this version.

### Changed
* [#1563](https://github.com/shlinkio/shlink/issues/1563) Moved logic to reuse command options to option classes instead of base abstract command classes.
* [#1569](https://github.com/shlinkio/shlink/issues/1569) Migrated test doubles from phpspec/prophecy to PHPUnit mocks.
* [#1329](https://github.com/shlinkio/shlink/issues/1329) Split some logic from `VisitRepository` and `ShortUrlRepository` into separated repository classes.

### Deprecated
* *Nothing*
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM php:8.1.13-alpine3.17 as base
FROM php:8.2-alpine3.17 as base

ARG SHLINK_VERSION=latest
ENV SHLINK_VERSION ${SHLINK_VERSION}
Expand All @@ -15,7 +15,7 @@ WORKDIR /etc/shlink
# Install required PHP extensions
RUN \
# Temp install dev dependencies needed to compile the extensions
apk add --no-cache --virtual .dev-deps sqlite-dev postgresql-dev icu-dev libzip-dev zlib-dev libpng-dev && \
apk add --no-cache --virtual .dev-deps sqlite-dev postgresql-dev icu-dev libzip-dev zlib-dev libpng-dev linux-headers && \
docker-php-ext-install -j"$(nproc)" pdo_mysql pdo_pgsql intl calendar sockets bcmath zip gd && \
apk add --no-cache sqlite-libs && \
docker-php-ext-install -j"$(nproc)" pdo_sqlite && \
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"php-middleware/request-id": "^4.1",
"pugx/shortid-php": "^1.1",
"ramsey/uuid": "^4.5",
"shlinkio/shlink-common": "dev-main#e2a5bb7 as 5.2",
"shlinkio/shlink-common": "dev-main#107b753 as 5.2",
"shlinkio/shlink-config": "dev-main#96c81fb as 2.3",
"shlinkio/shlink-event-dispatcher": "^2.6",
"shlinkio/shlink-importer": "dev-main#c97662b as 5.0",
Expand Down Expand Up @@ -135,7 +135,7 @@
"infect:ci:unit": "@infect:ci:base --coverage=build/coverage-unit --min-msi=80",
"infect:ci:db": "@infect:ci:base --coverage=build/coverage-db --min-msi=95 --configuration=infection-db.json5",
"infect:ci:api": "@infect:ci:base --coverage=build/coverage-api --min-msi=80 --configuration=infection-api.json5",
"infect:ci:cli": "@infect:ci:base --coverage=build/coverage-cli --min-msi=80 --configuration=infection-cli.json5",
"infect:ci:cli": "@infect:ci:base --coverage=build/coverage-cli --min-msi=90 --configuration=infection-cli.json5",
"infect:ci": "@parallel infect:ci:unit infect:ci:db infect:ci:api infect:ci:cli",
"infect:test": [
"@parallel test:unit:ci test:db:sqlite:ci test:api:ci",
Expand Down
6 changes: 4 additions & 2 deletions data/infra/php.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM php:8.1.13-fpm-alpine3.17
FROM php:8.2-fpm-alpine3.17
MAINTAINER Alejandro Celaya <[email protected]>

ENV APCU_VERSION 5.1.21
Expand Down Expand Up @@ -31,7 +31,9 @@ RUN docker-php-ext-install gd
RUN apk add --no-cache postgresql-dev
RUN docker-php-ext-install pdo_pgsql

RUN docker-php-ext-install sockets
RUN apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS linux-headers && \
docker-php-ext-install sockets && \
apk del .phpize-deps
RUN docker-php-ext-install bcmath

# Install APCu extension
Expand Down
6 changes: 4 additions & 2 deletions data/infra/roadrunner.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM php:8.1.13-alpine3.17
FROM php:8.2-alpine3.17
MAINTAINER Alejandro Celaya <[email protected]>

ENV APCU_VERSION 5.1.21
Expand Down Expand Up @@ -31,7 +31,9 @@ RUN docker-php-ext-install gd
RUN apk add --no-cache postgresql-dev
RUN docker-php-ext-install pdo_pgsql

RUN docker-php-ext-install sockets
RUN apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS linux-headers && \
docker-php-ext-install sockets && \
apk del .phpize-deps
RUN docker-php-ext-install bcmath

# Install APCu extension
Expand Down
6 changes: 4 additions & 2 deletions data/infra/swoole.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM php:8.1.13-alpine3.17
FROM php:8.2-alpine3.17
MAINTAINER Alejandro Celaya <[email protected]>

ENV APCU_VERSION 5.1.21
Expand Down Expand Up @@ -33,7 +33,9 @@ RUN docker-php-ext-install gd
RUN apk add --no-cache postgresql-dev
RUN docker-php-ext-install pdo_pgsql

RUN docker-php-ext-install sockets
RUN apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS linux-headers && \
docker-php-ext-install sockets && \
apk del .phpize-deps
RUN docker-php-ext-install bcmath

# Install APCu extension
Expand Down
10 changes: 9 additions & 1 deletion module/Core/config/dependencies.config.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@
EntityRepositoryFactory::class,
ShortUrl\Entity\ShortUrl::class,
],
ShortUrl\Repository\CrawlableShortCodesQuery::class => [
EntityRepositoryFactory::class,
ShortUrl\Entity\ShortUrl::class,
],

Tag\TagService::class => ConfigAbstractFactory::class,

Expand All @@ -61,6 +65,10 @@
Visit\Geolocation\VisitToLocationHelper::class => ConfigAbstractFactory::class,
Visit\VisitsStatsHelper::class => ConfigAbstractFactory::class,
Visit\Transformer\OrphanVisitDataTransformer::class => InvokableFactory::class,
Visit\Repository\VisitLocationRepository::class => [
EntityRepositoryFactory::class,
Visit\Entity\Visit::class,
],

Util\UrlValidator::class => ConfigAbstractFactory::class,
Util\DoctrineBatchHelper::class => ConfigAbstractFactory::class,
Expand Down Expand Up @@ -119,7 +127,7 @@
ShortUrl\Repository\ShortUrlListRepository::class,
Options\UrlShortenerOptions::class,
],
Visit\Geolocation\VisitLocator::class => ['em'],
Visit\Geolocation\VisitLocator::class => ['em', Visit\Repository\VisitLocationRepository::class],
Visit\Geolocation\VisitToLocationHelper::class => [IpLocationResolverInterface::class],
Visit\VisitsStatsHelper::class => ['em'],
Tag\TagService::class => ['em'],
Expand Down
10 changes: 3 additions & 7 deletions module/Core/src/Crawling/CrawlingHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,16 @@

namespace Shlinkio\Shlink\Core\Crawling;

use Doctrine\ORM\EntityManagerInterface;
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
use Shlinkio\Shlink\Core\ShortUrl\Repository\ShortUrlRepositoryInterface;
use Shlinkio\Shlink\Core\ShortUrl\Repository\CrawlableShortCodesQueryInterface;

class CrawlingHelper implements CrawlingHelperInterface
{
public function __construct(private EntityManagerInterface $em)
public function __construct(private readonly CrawlableShortCodesQueryInterface $query)
{
}

public function listCrawlableShortCodes(): iterable
{
/** @var ShortUrlRepositoryInterface $repo */
$repo = $this->em->getRepository(ShortUrl::class);
yield from $repo->findCrawlableShortCodes();
yield from ($this->query)();
}
}
38 changes: 38 additions & 0 deletions module/Core/src/ShortUrl/Repository/CrawlableShortCodesQuery.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace Shlinkio\Shlink\Core\ShortUrl\Repository;

use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository;
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;

class CrawlableShortCodesQuery extends EntitySpecificationRepository implements CrawlableShortCodesQueryInterface
{
/**
* @return iterable<string>
*/
public function __invoke(): iterable
{
$blockSize = 1000;
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('DISTINCT s.shortCode')
->from(ShortUrl::class, 's')
->where($qb->expr()->eq('s.crawlable', ':crawlable'))
->setParameter('crawlable', true)
->setMaxResults($blockSize);

$page = 0;
do {
$qbClone = (clone $qb)->setFirstResult($blockSize * $page);
$iterator = $qbClone->getQuery()->toIterable();
$resultsFound = false;
$page++;

foreach ($iterator as ['shortCode' => $shortCode]) {
$resultsFound = true;
yield $shortCode;
}
} while ($resultsFound);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Shlinkio\Shlink\Core\ShortUrl\Repository;

interface CrawlableShortCodesQueryInterface
{
/**
* @return iterable<string>
*/
public function __invoke(): iterable;
}
24 changes: 0 additions & 24 deletions module/Core/src/ShortUrl/Repository/ShortUrlRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -190,28 +190,4 @@ private function whereDomainIs(QueryBuilder $qb, ?string $domain): void
$qb->andWhere($qb->expr()->isNull('s.domain'));
}
}

public function findCrawlableShortCodes(): iterable
{
$blockSize = 1000;
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('DISTINCT s.shortCode')
->from(ShortUrl::class, 's')
->where($qb->expr()->eq('s.crawlable', ':crawlable'))
->setParameter('crawlable', true)
->setMaxResults($blockSize);

$page = 0;
do {
$qbClone = (clone $qb)->setFirstResult($blockSize * $page);
$iterator = $qbClone->getQuery()->toIterable();
$resultsFound = false;
$page++;

foreach ($iterator as ['shortCode' => $shortCode]) {
$resultsFound = true;
yield $shortCode;
}
} while ($resultsFound);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,4 @@ public function shortCodeIsInUseWithLock(ShortUrlIdentifier $identifier, ?Specif
public function findOneMatching(ShortUrlCreation $meta): ?ShortUrl;

public function findOneByImportedUrl(ImportedShlinkUrl $url): ?ShortUrl;

public function findCrawlableShortCodes(): iterable;
}
13 changes: 5 additions & 8 deletions module/Core/src/Visit/Geolocation/VisitLocator.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,15 @@
use Shlinkio\Shlink\Core\Exception\IpCannotBeLocatedException;
use Shlinkio\Shlink\Core\Visit\Entity\Visit;
use Shlinkio\Shlink\Core\Visit\Entity\VisitLocation;
use Shlinkio\Shlink\Core\Visit\Repository\VisitRepositoryInterface;
use Shlinkio\Shlink\Core\Visit\Repository\VisitLocationRepositoryInterface;
use Shlinkio\Shlink\IpGeolocation\Model\Location;

class VisitLocator implements VisitLocatorInterface
{
private VisitRepositoryInterface $repo;

public function __construct(private EntityManagerInterface $em)
{
/** @var VisitRepositoryInterface $repo */
$repo = $em->getRepository(Visit::class);
$this->repo = $repo;
public function __construct(
private readonly EntityManagerInterface $em,
private readonly VisitLocationRepositoryInterface $repo,
) {
}

public function locateUnlocatedVisits(VisitGeolocationHelperInterface $helper): void
Expand Down
74 changes: 74 additions & 0 deletions module/Core/src/Visit/Repository/VisitLocationRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

declare(strict_types=1);

namespace Shlinkio\Shlink\Core\Visit\Repository;

use Doctrine\ORM\QueryBuilder;
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository;
use Shlinkio\Shlink\Core\Visit\Entity\Visit;

class VisitLocationRepository extends EntitySpecificationRepository implements VisitLocationRepositoryInterface
{
/**
* @return iterable<Visit>
*/
public function findUnlocatedVisits(int $blockSize = self::DEFAULT_BLOCK_SIZE): iterable
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('v')
->from(Visit::class, 'v')
->where($qb->expr()->isNull('v.visitLocation'));

return $this->visitsIterableForQuery($qb, $blockSize);
}

/**
* @return iterable<Visit>
*/
public function findVisitsWithEmptyLocation(int $blockSize = self::DEFAULT_BLOCK_SIZE): iterable
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('v')
->from(Visit::class, 'v')
->join('v.visitLocation', 'vl')
->where($qb->expr()->isNotNull('v.visitLocation'))
->andWhere($qb->expr()->eq('vl.isEmpty', ':isEmpty'))
->setParameter('isEmpty', true);

return $this->visitsIterableForQuery($qb, $blockSize);
}

/**
* @return iterable<Visit>
*/
public function findAllVisits(int $blockSize = self::DEFAULT_BLOCK_SIZE): iterable
{
$qb = $this->createQueryBuilder('v');
return $this->visitsIterableForQuery($qb, $blockSize);
}

private function visitsIterableForQuery(QueryBuilder $qb, int $blockSize): iterable
{
$originalQueryBuilder = $qb->setMaxResults($blockSize)
->orderBy('v.id', 'ASC');
$lastId = '0';

do {
$qb = (clone $originalQueryBuilder)->andWhere($qb->expr()->gt('v.id', $lastId));
$iterator = $qb->getQuery()->toIterable();
$resultsFound = false;
/** @var Visit|null $lastProcessedVisit */
$lastProcessedVisit = null;

foreach ($iterator as $key => $visit) {
$resultsFound = true;
$lastProcessedVisit = $visit;
yield $key => $visit;
}

// As the query is ordered by ID, we can take the last one every time in order to exclude the whole list
$lastId = $lastProcessedVisit?->getId() ?? $lastId;
} while ($resultsFound);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace Shlinkio\Shlink\Core\Visit\Repository;

use Shlinkio\Shlink\Core\Visit\Entity\Visit;

interface VisitLocationRepositoryInterface
{
public const DEFAULT_BLOCK_SIZE = 10000;

/**
* @return iterable<Visit>
*/
public function findUnlocatedVisits(int $blockSize = self::DEFAULT_BLOCK_SIZE): iterable;

/**
* @return iterable<Visit>
*/
public function findVisitsWithEmptyLocation(int $blockSize = self::DEFAULT_BLOCK_SIZE): iterable;

/**
* @return iterable<Visit>
*/
public function findAllVisits(int $blockSize = self::DEFAULT_BLOCK_SIZE): iterable;
}
Loading