Skip to content

Commit

Permalink
Refactored FeedSourceProcessor with updated validations and schema pr…
Browse files Browse the repository at this point in the history
…eparations.
  • Loading branch information
jeppekroghitk committed Nov 12, 2024
1 parent cc27bde commit cb1af88
Showing 1 changed file with 66 additions and 28 deletions.
94 changes: 66 additions & 28 deletions src/State/FeedSourceProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,25 @@
use ApiPlatform\State\ProcessorInterface;
use App\Dto\FeedSourceInput;
use App\Entity\Tenant\FeedSource;
use App\Exceptions\UnknownFeedTypeException;
use App\Repository\FeedSourceRepository;
use App\Service\FeedService;
use Doctrine\ORM\EntityManagerInterface;
use JsonSchema\Constraints\Factory;
use JsonSchema\SchemaStorage;
use JsonSchema\Validator;
use PHPStan\BetterReflection\Reflection\Exception\ClassDoesNotExist;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpKernel\Exception\ConflictHttpException;

class FeedSourceProcessor extends AbstractProcessor
{
private const string PATTERN_WITHOUT_PROTOCOL = '^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{2,6}$';
private const string PATTERN_WITH_PROTOCOL = 'https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)';

public function __construct(
private readonly EntityManagerInterface $entityManager,
ProcessorInterface $persistProcessor,
private readonly ProcessorInterface $removeProcessor,
private readonly FeedSourceRepository $feedSourceRepository,
private readonly FeedService $feedService,
private readonly Security $security,
) {
parent::__construct($entityManager, $persistProcessor, $removeProcessor);
}
Expand All @@ -48,52 +49,89 @@ public function process($data, Operation $operation, array $uriVariables = [], a

protected function fromInput(mixed $object, Operation $operation, array $uriVariables, array $context): FeedSource

Check failure on line 50 in src/State/FeedSourceProcessor.php

View workflow job for this annotation

GitHub Actions / Psalm (PHP 8.3)

MoreSpecificReturnType

src/State/FeedSourceProcessor.php:50:109: MoreSpecificReturnType: The declared return type 'App\Entity\Tenant\FeedSource' for App\State\FeedSourceProcessor::fromInput is more specific than the inferred return type 'null|object' (see https://psalm.dev/070)
{
// FIXME Do we really have to do (something like) this to load an existing object into the entity manager?
// Set feed source properties
$feedSource = $this->loadPrevious(new FeedSource(), $context);
$this->updateFeedSourceProperties($feedSource, $object);

Check failure on line 54 in src/State/FeedSourceProcessor.php

View workflow job for this annotation

GitHub Actions / Psalm (PHP 8.3)

ArgumentTypeCoercion

src/State/FeedSourceProcessor.php:54:43: ArgumentTypeCoercion: Argument 1 of App\State\FeedSourceProcessor::updateFeedSourceProperties expects App\Entity\Tenant\FeedSource, but parent type null|object provided (see https://psalm.dev/193)

Check failure on line 54 in src/State/FeedSourceProcessor.php

View workflow job for this annotation

GitHub Actions / Psalm (PHP 8.3)

PossiblyNullArgument

src/State/FeedSourceProcessor.php:54:43: PossiblyNullArgument: Argument 1 of App\State\FeedSourceProcessor::updateFeedSourceProperties cannot be null, possibly null value provided (see https://psalm.dev/078)

/* @var FeedSourceInput $object */
empty($object->title) ?: $feedSource->setTitle($object->title);
empty($object->description) ?: $feedSource->setDescription($object->description);
empty($object->createdBy) ?: $feedSource->setCreatedBy($object->createdBy);
empty($object->modifiedBy) ?: $feedSource->setModifiedBy($object->modifiedBy);
empty($object->secrets) ?: $feedSource->setSecrets($object->secrets);
empty($object->feedType) ?: $feedSource->setFeedType($object->feedType);
// Set tenant
$user = $this->security->getUser();
$feedSource->setTenant($user->getActiveTenant());

Check failure on line 58 in src/State/FeedSourceProcessor.php

View workflow job for this annotation

GitHub Actions / Psalm (PHP 8.3)

PossiblyNullReference

src/State/FeedSourceProcessor.php:58:22: PossiblyNullReference: Cannot call method setTenant on possibly null value (see https://psalm.dev/083)

Check failure on line 58 in src/State/FeedSourceProcessor.php

View workflow job for this annotation

GitHub Actions / Psalm (PHP 8.3)

PossiblyNullReference

src/State/FeedSourceProcessor.php:58:39: PossiblyNullReference: Cannot call method getActiveTenant on possibly null value (see https://psalm.dev/083)

Check failure on line 58 in src/State/FeedSourceProcessor.php

View workflow job for this annotation

GitHub Actions / Psalm (PHP 8.3)

UndefinedInterfaceMethod

src/State/FeedSourceProcessor.php:58:39: UndefinedInterfaceMethod: Method Symfony\Component\Security\Core\User\UserInterface::getActiveTenant does not exist (see https://psalm.dev/181)

// Validate feed source
$this->validateFeedSource($object, $operation);

return $feedSource;

Check failure on line 63 in src/State/FeedSourceProcessor.php

View workflow job for this annotation

GitHub Actions / Psalm (PHP 8.3)

LessSpecificReturnStatement

src/State/FeedSourceProcessor.php:63:16: LessSpecificReturnStatement: The type 'null|object' is more general than the declared return type 'App\Entity\Tenant\FeedSource' for App\State\FeedSourceProcessor::fromInput (see https://psalm.dev/129)

Check failure on line 63 in src/State/FeedSourceProcessor.php

View workflow job for this annotation

GitHub Actions / Psalm (PHP 8.3)

NullableReturnStatement

src/State/FeedSourceProcessor.php:63:16: NullableReturnStatement: The declared return type 'App\Entity\Tenant\FeedSource' for App\State\FeedSourceProcessor::fromInput is not nullable, but the function returns 'null|object' (see https://psalm.dev/139)
}

protected function updateFeedSourceProperties(FeedSource $feedSource, FeedSourceInput $object): void
{
if (!empty($object->title)) {
$feedSource->setTitle($object->title);
}
if (!empty($object->description)) {
$feedSource->setDescription($object->description);
}
if (!empty($object->createdBy)) {
$feedSource->setCreatedBy($object->createdBy);
}
if (!empty($object->modifiedBy)) {
$feedSource->setModifiedBy($object->modifiedBy);
}
if (!empty($object->secrets)) {
$feedSource->setSecrets($object->secrets);
}
if (!empty($object->feedType)) {
$feedSource->setFeedType($object->feedType);
}
$feedSource->setSupportedFeedOutputType($feedSource->getSupportedFeedOutputType());

Check failure on line 86 in src/State/FeedSourceProcessor.php

View workflow job for this annotation

GitHub Actions / Psalm (PHP 8.3)

PossiblyNullArgument

src/State/FeedSourceProcessor.php:86:49: PossiblyNullArgument: Argument 1 of App\Entity\Tenant\FeedSource::setSupportedFeedOutputType cannot be null, possibly null value provided (see https://psalm.dev/078)
}

/**
* @throws \JsonException
* @throws UnknownFeedTypeException
*/
private function validateFeedSource(object $object, Operation $operation): void
{
$schemaStorage = new SchemaStorage();
$feedSourceValidationSchema = (new FeedSource())->getSchema();
$schemaStorage->addSchema('file://contentSchema', $feedSourceValidationSchema);
$validator = new Validator(new Factory($schemaStorage));
$validator->validate($object, $feedSourceValidationSchema);
$validator = $this->prepareValidator();

if (!$validator->isValid()) {
throw new InvalidArgumentException($validator->getErrors()[0]['property'].' '.$validator->getErrors()[0]['message']);
}
// Validate base feed source
$this->executeValidation($object, $validator);

// Validate dynamic feed type class
$feedTypeClassName = $object->feedType;
$feedType = $this->feedService->getFeedType($feedTypeClassName);

if (!class_exists($feedTypeClassName)) {
throw new ClassDoesNotExist('Provided feed type class does not exist');
}
$feedTypeValidationSchema = $feedTypeClassName::getSchema();
$feedTypeValidationSchema = $feedType->getSchema();

if($operation instanceof Put && empty($object->secrets)) {
// If updating and secrets are not set, don't validate.
if ($operation instanceof Put && empty($object->secrets)) {
return;
}

// Validate secrets based on specific feed type.
$secrets = (object) $object->secrets;
$validator->validate($secrets, $feedTypeValidationSchema);
$this->executeValidation($secrets, $validator, $feedTypeValidationSchema);
}

private function prepareValidator(): validator
{
$schemaStorage = new SchemaStorage();
$feedSourceValidationSchema = (new FeedSource())->getSchema();
$schemaStorage->addSchema('file://contentSchema', $feedSourceValidationSchema);

return new Validator(new Factory($schemaStorage));
}

if (!$validator->isValid()) {
throw new InvalidArgumentException($validator->getErrors()[0]['property'].' '.$validator->getErrors()[0]['message']);
private function executeValidation($object, $validator, $schema = null): void
{
$validator->validate($object, $schema ?? (new FeedSource())->getSchema());
if (! $validator->isValid()) {
throw new InvalidArgumentException($this->getErrorMessage($validator));
}
}

private function getErrorMessage($validator): string
{
return $validator->getErrors()[0]['property'].' '.$validator->getErrors()[0]['message'];
}
}

0 comments on commit cb1af88

Please sign in to comment.