Skip to content

Commit

Permalink
TE-10302: TE-9946: TE-10308: TE-10300: Created rule for delete facade…
Browse files Browse the repository at this point in the history
… method (#113)

* TE-10302: Created rule for delete facade method

* TE-10302: Created rule for delete facade method

* TE-10302: Created rule for delete facade method

* TE-10302: Changed violation for facade rule

* TE-10302: Added create rule

* TE-10302: Added create rule

* TE-10302: Added create rule

* TE-10302: created rule update, save

* TE-10302: Create rule get*Collection for facade signature

* TE-10302: updated messages

* TE-10302: fixed error message

Co-authored-by: dima_tsemma <[email protected]>
  • Loading branch information
vol4onok and dimitriyTsemma authored Mar 21, 2022
1 parent 6edac6a commit 4a29a06
Show file tree
Hide file tree
Showing 6 changed files with 502 additions and 0 deletions.
192 changes: 192 additions & 0 deletions src/Common/Bridge/BridgeFacadeMethodsRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
<?php

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

namespace ArchitectureSniffer\Common\Bridge;

use ArchitectureSniffer\SprykerAbstractRule;
use PHPMD\AbstractNode;
use PHPMD\Node\ClassNode;
use PHPMD\Node\MethodNode;
use PHPMD\Rule\ClassAware;
use ReflectionMethod;
use ReflectionType;

class BridgeFacadeMethodsRule extends SprykerAbstractRule implements ClassAware
{
/**
* @var string
*/
protected const RULE = 'Based on naming convention the bridge facade method must follow CRUD signature.';

/**
* @return string
*/
public function getDescription()
{
return static::RULE;
}

/**
* @param \PHPMD\AbstractNode $node
*
* @return void
*/
public function apply(AbstractNode $node)
{
if (
preg_match('([A-Za-z0-9]+Bridge$)', $node->getName()) === 0 ||
preg_match('#.*\\\\Dependency\\\\Facade.*#', $node->getNamespaceName()) === 0 ||
!$node instanceof ClassNode
) {
return;
}
$bridgeName = $node->getFullQualifiedName();

foreach ($node->getMethods() as $method) {
$interfaceMethodName = sprintf('%s::%s', $bridgeName, $method->getName());
$interfaceMethodReflection = new ReflectionMethod($interfaceMethodName);
$methodReturnType = $interfaceMethodReflection->getReturnType();

if (preg_match('/^(get|read|find)/', $method->getName())) {
$this->verifyGetMethod($method, $methodReturnType);

continue;
}

if (preg_match('/^(delete|remove)/', $method->getName())) {
$this->verifyDeleteMethod($method, $methodReturnType);

continue;
}

if (preg_match('/^(create|add)/', $method->getName())) {
$this->verifyCreateMethod($method, $methodReturnType);

continue;
}

if (preg_match('/^(update|change)/', $method->getName())) {
$this->verifyUpdateMethod($method, $methodReturnType);

continue;
}

if (strpos($method->getName(), 'save') === 0) {
$this->addViolation($method, [sprintf(
'Method `%s()` must have `public function [update|create]<DomainEntity>Collection(<DomainEntity>CollectionRequestTransfer): <DomainEntity>CollectionResponseTransfer` signature.',
$method->getName(),
)]);

continue;
}
}
}

/**
* @param \PHPMD\Node\MethodNode $method
* @param \ReflectionType|null $methodReturnType
*
* @return void
*/
protected function verifyGetMethod(MethodNode $method, ?ReflectionType $methodReturnType): void
{
$parameters = $method->getNode()->getParameters();

if (
$method->getParameterCount() !== 1 ||
!$parameters[0]->getClass() ||
!$methodReturnType ||
preg_match('/^get(?<domainEntity>\w+)Collection$/', $method->getName(), $methodNameMatches) === 0 ||
preg_match('/^(?<domainEntity>\w+)CriteriaTransfer$/', $parameters[0]->getClass()->getName(), $methodParameterMatches) === 0 ||
preg_match('/^Generated\\\\Shared\\\\Transfer\\\\(?<domainEntity>\w+)CollectionTransfer$/', $methodReturnType->getName(), $methodReturnTypeMatches) === 0 ||
count(array_unique([$methodNameMatches['domainEntity'], $methodParameterMatches['domainEntity'], $methodReturnTypeMatches['domainEntity']])) !== 1
) {
$this->addViolation($method, [sprintf(
'Method `%s()` must have `public function get<DomainEntity>Collection(<DomainEntity>CriteriaTransfer): <DomainEntity>CollectionTransfer` signature.',
$method->getName(),
)]);
}
}

/**
* @param \PHPMD\Node\MethodNode $method
* @param \ReflectionType|null $methodReturnType
*
* @return void
*/
protected function verifyUpdateMethod(MethodNode $method, ?ReflectionType $methodReturnType): void
{
$parameters = $method->getNode()->getParameters();

if (
$method->getParameterCount() !== 1 ||
!$parameters[0]->getClass() ||
!$methodReturnType ||
preg_match('/^update(?<domainEntity>\w+)Collection$/', $method->getName(), $methodNameMatches) === 0 ||
preg_match('/^(?<domainEntity>\w+)CollectionRequestTransfer$/', $parameters[0]->getClass()->getName(), $methodParameterMatches) === 0 ||
preg_match('/^Generated\\\\Shared\\\\Transfer\\\\(?<domainEntity>\w+)CollectionResponseTransfer$/', $methodReturnType->getName(), $methodReturnTypeMatches) === 0 ||
count(array_unique([$methodNameMatches['domainEntity'], $methodParameterMatches['domainEntity'], $methodReturnTypeMatches['domainEntity']])) !== 1
) {
$this->addViolation($method, [sprintf(
'Method `%s()` must have `public function update<DomainEntity>Collection(<DomainEntity>CollectionRequestTransfer): <DomainEntity>CollectionResponseTransfer` signature.',
$method->getName(),
)]);
}
}

/**
* @param \PHPMD\Node\MethodNode $method
* @param \ReflectionType|null $methodReturnType
*
* @return void
*/
protected function verifyDeleteMethod(MethodNode $method, ?ReflectionType $methodReturnType): void
{
$parameters = $method->getNode()->getParameters();

if (
$method->getParameterCount() !== 1 ||
!$parameters[0]->getClass() ||
!$methodReturnType ||
preg_match('/^delete(?<domainEntity>\w+)Collection$/', $method->getName(), $methodNameMatches) === 0 ||
preg_match('/^(?<domainEntity>\w+)CollectionDeleteCriteriaTransfer$/', $parameters[0]->getClass()->getName(), $methodParameterMatches) === 0 ||
preg_match('/^Generated\\\\Shared\\\\Transfer\\\\(?<domainEntity>\w+)CollectionResponseTransfer$/', $methodReturnType->getName(), $methodReturnTypeMatches) === 0 ||
count(array_unique([$methodNameMatches['domainEntity'], $methodParameterMatches['domainEntity'], $methodReturnTypeMatches['domainEntity']])) !== 1
) {
$this->addViolation($method, [sprintf(
'Method `%s()` must have `public function delete<DomainEntity>Collection(<DomainEntity>CollectionDeleteCriteriaTransfer): <DomainEntity>CollectionResponseTransfer` signature.',
$method->getName(),
)]);
}
}

/**
* @param \PHPMD\Node\MethodNode $method
* @param \ReflectionType|null $methodReturnType
*
* @return void
*/
protected function verifyCreateMethod(MethodNode $method, ?ReflectionType $methodReturnType): void
{
$parameters = $method->getNode()->getParameters();

if (
$method->getParameterCount() !== 1 ||
!$parameters[0]->getClass() ||
!$methodReturnType ||
preg_match('/^create(?<domainEntity>\w+)Collection$/', $method->getName(), $methodNameMatches) === 0 ||
preg_match('/^(?<domainEntity>\w+)CollectionRequestTransfer$/', $parameters[0]->getClass()->getName(), $methodParameterMatches) === 0 ||
preg_match('/^Generated\\\\Shared\\\\Transfer\\\\(?<domainEntity>\w+)CollectionResponseTransfer$/', $methodReturnType->getName(), $methodReturnTypeMatches) === 0 ||
count(array_unique([$methodNameMatches['domainEntity'], $methodParameterMatches['domainEntity'], $methodReturnTypeMatches['domainEntity']])) !== 1
) {
$this->addViolation($method, [sprintf(
'Method `%s()` must have `public function create<DomainEntity>Collection(<DomainEntity>CollectionRequestTransfer): <DomainEntity>CollectionResponseTransfer` signature.',
$method->getName(),
)]);
}
}
}
7 changes: 7 additions & 0 deletions src/Common/ruleset.xml
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,13 @@

<priority>2</priority>
</rule>
<rule
name="BridgeFacadeMethodsRule"
message="Bridges: {0}"
class="ArchitectureSniffer\Common\Bridge\BridgeFacadeMethodsRule">

<priority>2</priority>
</rule>
<rule
name="BridgeMethodsInterfaceRule"
message="Bridges: {0}"
Expand Down
34 changes: 34 additions & 0 deletions tests/Common/Bridge/BridgeFacadeMethodsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

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

namespace ArchitectureSnifferTest\Common\Bridge;

use ArchitectureSniffer\Common\Bridge\BridgeFacadeMethodsRule;
use ArchitectureSnifferTest\AbstractArchitectureSnifferRuleTest;

class BridgeFacadeMethodsTest extends AbstractArchitectureSnifferRuleTest
{
/**
* @return void
*/
public function testRuleDoesNotApplyWhenBridgeFacadeMethodsAreNotCorrect(): void
{
$bridgeMethodsRule = new BridgeFacadeMethodsRule();
$bridgeMethodsRule->setReport($this->getReportMock(17));
$bridgeMethodsRule->apply($this->getClassNode());
}

/**
* @return void
*/
public function testRuleAppliesWhenFacadeBridgeMethodsAreCorrect(): void
{
$bridgeMethodsRule = new BridgeFacadeMethodsRule();
$bridgeMethodsRule->setReport($this->getReportMock(0));
$bridgeMethodsRule->apply($this->getClassNode());
}
}
2 changes: 2 additions & 0 deletions tests/_bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@
require_once '_data/Common/Method/ExternalMethodExtensionReturnTypeRuleTest/testRuleDoesNotApplyWhenClassDoesNotExtendOrImplementExternal.php';
require_once '_data/Common/Method/ExternalMethodExtensionReturnTypeRuleTest/testRuleDoesNotApplyWhenClassImplementsExternalAndHasReturnType.php';
require_once '_data/Common/Method/ExternalMethodExtensionReturnTypeRuleTest/testRuleDoesNotApplyWhenClassExtendsExternalAndHasReturnType.php';
require_once '_data/Common/Bridge/BridgeFacadeMethodsTest/testRuleAppliesWhenFacadeBridgeMethodsAreCorrect.php';
require_once '_data/Common/Bridge/BridgeFacadeMethodsTest/testRuleDoesNotApplyWhenBridgeFacadeMethodsAreNotCorrect.php';
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php

/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/

namespace SprykerTest\Zed\Session\Dependency\Facade;

use Generated\Shared\Transfer\TestCollectionDeleteCriteriaTransfer;
use Generated\Shared\Transfer\TestCollectionRequestTransfer;
use Generated\Shared\Transfer\TestCollectionResponseTransfer;
use Generated\Shared\Transfer\TestCollectionTransfer;
use Generated\Shared\Transfer\TestCriteriaTransfer;
use Spryker\Zed\Test\Facade\SessionToTestFacadeInterface;

class SessionToTestFacadeBridge implements SessionToTestFacadeInterface
{
/**
* @param \Spryker\Zed\Test\Facade\TestFacadeInterface $testFacade
*/
public function __construct($testFacade)
{
}

public function deleteTestCollection(TestCollectionDeleteCriteriaTransfer $testCollectionDeleteCriteriaTransfer): TestCollectionResponseTransfer
{
}

public function createTestCollection(TestCollectionRequestTransfer $testCollectionRequestTransfer): TestCollectionResponseTransfer
{
}

public function updateTestCollection(TestCollectionRequestTransfer $testCollectionRequestTransfer): TestCollectionResponseTransfer
{
}

public function getTestCollection(TestCriteriaTransfer $testCriteriaTransfer): TestCollectionTransfer
{
}
}

// Database module

namespace Spryker\Zed\Test\Facade;

use Generated\Shared\Transfer\TestCollectionDeleteCriteriaTransfer;
use Generated\Shared\Transfer\TestCollectionRequestTransfer;
use Generated\Shared\Transfer\TestCollectionResponseTransfer;
use Generated\Shared\Transfer\TestCollectionTransfer;
use Generated\Shared\Transfer\TestCriteriaTransfer;

interface SessionToTestFacadeInterface
{
public function deleteTestCollection(TestCollectionDeleteCriteriaTransfer $testCollectionDeleteCriteriaTransfer): TestCollectionResponseTransfer;
public function createTestCollection(TestCollectionRequestTransfer $testCollectionRequestTransfer): TestCollectionResponseTransfer;
public function updateTestCollection(TestCollectionRequestTransfer $testCollectionRequestTransfer): TestCollectionResponseTransfer;
public function getTestCollection(TestCriteriaTransfer $testCriteriaTransfer): TestCollectionTransfer;
}

namespace Generated\Shared\Transfer;

class TestCollectionTransfer
{

}

class TestCriteriaTransfer
{

}

class TestCollectionResponseTransfer
{

}

class TestCollectionRequestTransfer
{

}

class TestCollectionDeleteCriteriaTransfer
{

}

namespace Spryker\Zed\Test\Facade;

interface TestFacadeInterface
{
}
Loading

0 comments on commit 4a29a06

Please sign in to comment.