Skip to content

Commit

Permalink
SDK-4967: Discouraged packages checker (#51)
Browse files Browse the repository at this point in the history
  • Loading branch information
sergeyspryker authored Nov 13, 2023
1 parent e875c3f commit 4051946
Show file tree
Hide file tree
Showing 12 changed files with 654 additions and 12 deletions.
3 changes: 3 additions & 0 deletions config/checkers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,6 @@ parameters:

# Spryker dev packages checker
spryker_dev_packages_checker_doc_url: https://docs.spryker.com/docs/scos/dev/guidelines/keeping-a-project-upgradable/upgradability-guidelines/spryker-dev-packages-checker.html

# Discouraged packages checker
discouraged_packages_checker_doc_url: https://docs.spryker.com/docs/scos/dev/guidelines/keeping-a-project-upgradable/upgradability-guidelines/discouraged_packages_checker.html
6 changes: 6 additions & 0 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ imports:
parameters:
env(EVALUATOR_PROJECT_DIR): ''
env(GITHUB_AUTH): ~
env(RELEASE_APP_URL): 'https://api.release.spryker.com'
report_file: 'reports/evaluator.*'
current_php_version: "%env(default:current_server_php_version:string:PROJECT_PHP_VERSION)%"
current_server_php_version: !php/const PHP_VERSION
Expand All @@ -18,6 +19,7 @@ services:
$projectDirEnv: '%env(EVALUATOR_PROJECT_DIR)%'
$githubAuth: '%env(GITHUB_AUTH)%'
$toolingFile: '%tooling_file%'
$releaseAppUrl: '%env(RELEASE_APP_URL)%'

_instanceof:
SprykerSdk\Evaluator\Checker\CheckerInterface:
Expand Down Expand Up @@ -142,6 +144,10 @@ services:
arguments:
$checkerDocUrl: '%spryker_dev_packages_checker_doc_url%'

SprykerSdk\Evaluator\Checker\DiscouragedPackagesChecker\DiscouragedPackagesChecker:
arguments:
$checkerDocUrl: '%discouraged_packages_checker_doc_url%'

# Third-party
PhpParser\ParserFactory: ~
Symfony\Component\Finder\Finder: ~
Expand Down
49 changes: 49 additions & 0 deletions src/Checker/DiscouragedPackagesChecker/DiscouragedPackageDto.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

/**
* Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
* Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
*/

declare(strict_types=1);

namespace SprykerSdk\Evaluator\Checker\DiscouragedPackagesChecker;

class DiscouragedPackageDto
{
/**
* @var string
*/
protected string $packageName;

/**
* @var string
*/
protected string $reason;

/**
* @param string $packageName
* @param string $reason
*/
public function __construct(string $packageName, string $reason)
{
$this->packageName = $packageName;
$this->reason = $reason;
}

/**
* @return string
*/
public function getPackageName(): string
{
return $this->packageName;
}

/**
* @return string
*/
public function getReason(): string
{
return $this->reason;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php

/**
* Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
* Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
*/

declare(strict_types=1);

namespace SprykerSdk\Evaluator\Checker\DiscouragedPackagesChecker;

use Psr\Http\Client\ClientExceptionInterface;
use SprykerSdk\Evaluator\Checker\AbstractChecker;
use SprykerSdk\Evaluator\Dto\CheckerInputDataDto;
use SprykerSdk\Evaluator\Dto\CheckerResponseDto;
use SprykerSdk\Evaluator\Dto\ViolationDto;
use SprykerSdk\Evaluator\Reader\ComposerReaderInterface;

class DiscouragedPackagesChecker extends AbstractChecker
{
/**
* @var string
*/
public const NAME = 'DISCOURAGED_PACKAGES_CHECKER';

/**
* @var \SprykerSdk\Evaluator\Checker\DiscouragedPackagesChecker\DiscouragedPackagesFetcherInterface
*/
protected DiscouragedPackagesFetcherInterface $discouragedPackagesFetcher;

/**
* @var \SprykerSdk\Evaluator\Reader\ComposerReaderInterface
*/
protected ComposerReaderInterface $composerReader;

/**
* @var string
*/
protected string $checkerDocUrl;

/**
* @param \SprykerSdk\Evaluator\Checker\DiscouragedPackagesChecker\DiscouragedPackagesFetcherInterface $discouragedPackagesFetcher
* @param \SprykerSdk\Evaluator\Reader\ComposerReaderInterface $composerReader
* @param string $checkerDocUrl
*/
public function __construct(
DiscouragedPackagesFetcherInterface $discouragedPackagesFetcher,
ComposerReaderInterface $composerReader,
string $checkerDocUrl = ''
) {
$this->discouragedPackagesFetcher = $discouragedPackagesFetcher;
$this->composerReader = $composerReader;
$this->checkerDocUrl = $checkerDocUrl;
}

/**
* @param \SprykerSdk\Evaluator\Dto\CheckerInputDataDto $inputData
*
* @return \SprykerSdk\Evaluator\Dto\CheckerResponseDto
*/
public function check(CheckerInputDataDto $inputData): CheckerResponseDto
{
$projectInstalledPackages = array_keys($this->composerReader->getInstalledPackages());

try {
$discouragedPackages = $this->discouragedPackagesFetcher->fetchDiscouragedPackagesByPackageNames(
$projectInstalledPackages,
);
} catch (ClientExceptionInterface | \InvalidArgumentException $clientException) {
return new CheckerResponseDto(
[new ViolationDto(sprintf('Release app api request issue: %s', $clientException->getMessage()))],
$this->checkerDocUrl,
);
}

$violations = [];

foreach ($discouragedPackages as $discouragedPackage) {
$violations[] = new ViolationDto($discouragedPackage->getReason(), $discouragedPackage->getPackageName());
}

return new CheckerResponseDto($violations, $this->checkerDocUrl);
}

/**
* @return string
*/
public function getName(): string
{
return static::NAME;
}
}
123 changes: 123 additions & 0 deletions src/Checker/DiscouragedPackagesChecker/DiscouragedPackagesFetcher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?php

/**
* Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
* Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
*/

declare(strict_types=1);

namespace SprykerSdk\Evaluator\Checker\DiscouragedPackagesChecker;

use GuzzleHttp\Psr7\Request;
use InvalidArgumentException;
use SprykerSdk\Evaluator\External\Http\HttpClientFactoryInterface;

class DiscouragedPackagesFetcher implements DiscouragedPackagesFetcherInterface
{
/**
* @var string
*/
protected const RESULT_KEY = 'result';

/**
* @var string
*/
protected const NAME_KEY = 'name';

/**
* @var string
*/
protected const REASON_KEY = 'reason';

/**
* @var string
*/
protected const API_ENDPOINT = '/discouraged-packages-analyze.json';

/**
* string
*
* @var string
*/
protected const API_METHOD = 'POST';

/**
* @var \SprykerSdk\Evaluator\External\Http\HttpClientFactoryInterface
*/
protected HttpClientFactoryInterface $httpClientFactory;

/**
* @var string
*/
protected string $releaseAppUrl;

/**
* @param \SprykerSdk\Evaluator\External\Http\HttpClientFactoryInterface $httpClientFactory
* @param string $releaseAppUrl
*/
public function __construct(HttpClientFactoryInterface $httpClientFactory, string $releaseAppUrl)
{
$this->httpClientFactory = $httpClientFactory;
$this->releaseAppUrl = $releaseAppUrl;
}

/**
* @param array<string> $packageNames
*
* @return array<\SprykerSdk\Evaluator\Checker\DiscouragedPackagesChecker\DiscouragedPackageDto>
*/
public function fetchDiscouragedPackagesByPackageNames(array $packageNames): array
{
$request = new Request(
static::API_METHOD,
$this->getReleaseAppUrl() . static::API_ENDPOINT,
['Content-Type' => 'application/json'],
json_encode($packageNames, JSON_THROW_ON_ERROR),
);

$response = $this->httpClientFactory->createClient()->send($request);

return $this->buildDiscouragedPackageDtos(
json_decode((string)$response->getBody(), true, 512, \JSON_THROW_ON_ERROR),
);
}

/**
* @param array<mixed> $responseData
*
* @throws \InvalidArgumentException
*
* @return array<\SprykerSdk\Evaluator\Checker\DiscouragedPackagesChecker\DiscouragedPackageDto>
*/
protected function buildDiscouragedPackageDtos(array $responseData): array
{
if (!isset($responseData[static::RESULT_KEY])) {
throw new InvalidArgumentException(sprintf('Unable to find "%s" key in release app response', static::RESULT_KEY));
}

if (!is_array($responseData[static::RESULT_KEY])) {
throw new InvalidArgumentException('Result data should be an array');
}

$result = [];

foreach ($responseData[static::RESULT_KEY] as $packageData) {
if (!isset($packageData[static::NAME_KEY], $packageData[static::REASON_KEY])) {
throw new InvalidArgumentException('Invalid package data');
}

$result[] = new DiscouragedPackageDto($packageData[static::NAME_KEY], $packageData[static::REASON_KEY]);
}

return $result;
}

/**
* @return string
*/
protected function getReleaseAppUrl(): string
{
return rtrim($this->releaseAppUrl);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

/**
* Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
* Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
*/

declare(strict_types=1);

namespace SprykerSdk\Evaluator\Checker\DiscouragedPackagesChecker;

interface DiscouragedPackagesFetcherInterface
{
/**
* @param array<string> $packageNames
*
* @throws \Psr\Http\Client\ClientExceptionInterface
*
* @return array<\SprykerSdk\Evaluator\Checker\DiscouragedPackagesChecker\DiscouragedPackageDto>
*/
public function fetchDiscouragedPackagesByPackageNames(array $packageNames): array;
}
39 changes: 30 additions & 9 deletions src/Reader/ComposerReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

namespace SprykerSdk\Evaluator\Reader;

use InvalidArgumentException;
use SprykerSdk\Evaluator\Filesystem\Filesystem;
use SprykerSdk\Evaluator\Resolver\PathResolverInterface;

class ComposerReader implements ComposerReaderInterface
Expand Down Expand Up @@ -56,12 +56,19 @@ class ComposerReader implements ComposerReaderInterface
*/
protected PathResolverInterface $pathResolver;

/**
* @var \SprykerSdk\Evaluator\Filesystem\Filesystem
*/
protected Filesystem $filesystem;

/**
* @param \SprykerSdk\Evaluator\Resolver\PathResolverInterface $pathResolver
* @param \SprykerSdk\Evaluator\Filesystem\Filesystem $filesystem
*/
public function __construct(PathResolverInterface $pathResolver)
public function __construct(PathResolverInterface $pathResolver, Filesystem $filesystem)
{
$this->pathResolver = $pathResolver;
$this->filesystem = $filesystem;
}

/**
Expand Down Expand Up @@ -91,17 +98,11 @@ public function getComposerRequirePackages(): array
/**
* @param string $filePath
*
* @throws \InvalidArgumentException
*
* @return array<mixed>
*/
protected function readFile(string $filePath): array
{
$content = file_get_contents($filePath);

if ($content === false) {
throw new InvalidArgumentException(sprintf('Unable to read file %s. Error: %s', $filePath, error_get_last()['message'] ?? '-'));
}
$content = $this->filesystem->readFile($filePath);

return json_decode($content, true, 512, \JSON_THROW_ON_ERROR);
}
Expand Down Expand Up @@ -139,4 +140,24 @@ public function getProjectName(): string

return $composerJsonContent[static::NAME_KEY];
}

/**
* @return array<mixed, array<string, mixed>>
*/
public function getInstalledPackages(): array
{
$installedPackages = [];

$composerLock = $this->getComposerLockData();

foreach ($composerLock[static::PACKAGES_KEY] as $package) {
$installedPackages[$package[static::NAME_KEY]] = $package;
}

foreach ($composerLock[static::PACKAGES_DEV_KEY] as $package) {
$installedPackages[$package[static::NAME_KEY]] = $package;
}

return $installedPackages;
}
}
Loading

0 comments on commit 4051946

Please sign in to comment.