use Filter

This commit is contained in:
Hemachandar
2026-02-16 13:53:33 +05:30
parent 90e69b011f
commit 6c5e73de3a
8 changed files with 316 additions and 171 deletions
+6 -7
View File
@@ -9,8 +9,8 @@ use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
use Appwrite\SDK\MethodType;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Transformation\Adapter\BranchDomain;
use Appwrite\Transformation\Transformation;
use Appwrite\Filter\Filter;
use Appwrite\Filter\Adapter\BranchDomain as FilterBranchDomain;
use Appwrite\Utopia\Database\Validator\Queries\Installations;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
@@ -371,17 +371,16 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
// VCS branch preview
if (!empty($providerBranch)) {
$transformation = new Transformation([new BranchDomain()]);
$transformation
$filter = new Filter([new FilterBranchDomain()]);
$filter
->setInput([
'branch' => $providerBranch,
'resourceId' => $resource->getId(),
'projectId' => $project->getId(),
'sitesDomain' => $sitesDomain,
])
->setTraits([])
->transform();
$domain = $transformation->getOutput();
->filter();
$domain = $filter->getOutput();
$ruleId = md5($domain);
try {
$authorization->skip(
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace Appwrite\Filter;
abstract class Adapter
{
protected mixed $input;
protected mixed $output;
public function __construct()
{
}
public function setInput(mixed $input): self
{
$this->input = $input;
return $this;
}
public function getOutput(): mixed
{
return $this->output;
}
abstract public function isValid(mixed $input): bool;
abstract public function filter(): self;
}
@@ -1,8 +1,8 @@
<?php
namespace Appwrite\Transformation\Adapter;
namespace Appwrite\Filter\Adapter;
use Appwrite\Transformation\Adapter;
use Appwrite\Filter\Adapter;
use Utopia\Validator\Text;
class BranchDomain extends Adapter
@@ -17,24 +17,30 @@ class BranchDomain extends Adapter
*/
public const HASH_SUFFIX_LENGTH = 7;
/**
* @param array<mixed> $traits
*/
public function isValid(array $traits): bool
public function isValid(mixed $input): bool
{
$branch = $input['branch'] ?? '';
$resourceId = $input['resourceId'] ?? '';
$projectId = $input['projectId'] ?? '';
$sitesDomain = $input['sitesDomain'] ?? '';
if (empty($branch) || empty($resourceId) || empty($projectId) || empty($sitesDomain)) {
return false;
}
return true;
}
/**
* Transform branch name into a valid domain name.
*
* Pre-process branch name to a valid domain name.
*
* Input should be an array with:
* - 'branch' (string): The branch name
* - 'resourceId' (string): The resource ID (site or function)
* - 'projectId' (string): The project ID
* - 'sitesDomain' (string): The base sites domain
*/
public function transform(): void
public function filter(): self
{
$branch = $this->input['branch'] ?? '';
$resourceId = $this->input['resourceId'] ?? '';
@@ -44,6 +50,7 @@ class BranchDomain extends Adapter
$branchPrefix = $this->generateBranchPrefix($branch);
$resourceProjectHash = substr(hash('sha256', $resourceId . $projectId), 0, self::HASH_SUFFIX_LENGTH);
$this->output = strtolower("branch-{$branchPrefix}-{$resourceProjectHash}.{$sitesDomain}");
return $this;
}
/**
@@ -92,7 +99,7 @@ class BranchDomain extends Adapter
if (isset($allowedCharsFlip[$char])) {
$sanitized .= $char;
} else {
// Prevents two -- or more in row
// Prevents two -- or more in a row
if (strlen($sanitized) > 0 && $sanitized[strlen($sanitized) - 1] !== '-') {
$sanitized .= '-';
}
@@ -101,4 +108,4 @@ class BranchDomain extends Adapter
return trim($sanitized, '-');
}
}
}
+62
View File
@@ -0,0 +1,62 @@
<?php
namespace Appwrite\Filter;
class Filter
{
/**
* @var array<mixed> $adapters
*/
protected array $adapters;
protected mixed $input;
protected mixed $output;
/**
* @param array<Adapter> $adapters
*/
public function __construct(array $adapters = [])
{
$this->adapters = $adapters;
}
public function setInput(mixed $input): self
{
$this->input = $input;
return $this;
}
public function getOutput(): mixed
{
return $this->output;
}
public function addAdapter(Adapter $adapter): self
{
$this->adapters[] = $adapter;
return $this;
}
public function filter(): bool
{
foreach ($this->adapters as $adapter) {
if (!$adapter->isValid($this->input)) {
return false;
}
}
$output = $this->input;
foreach ($this->adapters as $adapter) {
$output = $adapter
->setInput($output)
->filter()
->getOutput();
}
$this->output = $output;
return true;
}
}
@@ -6,8 +6,8 @@ use Appwrite\Event\Build;
use Appwrite\Extend\Exception;
use Appwrite\Platform\Action;
use Appwrite\Platform\Modules\Compute\Validator\Specification as SpecificationValidator;
use Appwrite\Transformation\Adapter\BranchDomain;
use Appwrite\Transformation\Transformation;
use Appwrite\Filter\Filter;
use Appwrite\Filter\Adapter\BranchDomain as FilterBranchDomain;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Document;
@@ -298,17 +298,16 @@ class Base extends Action
// VCS branch preview
if (!empty($providerBranch)) {
$transformation = new Transformation([new BranchDomain()]);
$transformation
$filter = new Filter([new FilterBranchDomain()]);
$filter
->setInput([
'branch' => $providerBranch,
'resourceId' => $site->getId(),
'projectId' => $project->getId(),
'sitesDomain' => $sitesDomain,
])
->setTraits([])
->transform();
$domain = $transformation->getOutput();
->filter();
$domain = $filter->getOutput();
$ruleId = md5($domain);
try {
$authorization->skip(
@@ -9,8 +9,8 @@ use Appwrite\Event\Realtime;
use Appwrite\Event\Screenshot;
use Appwrite\Event\StatsUsage;
use Appwrite\Event\Webhook;
use Appwrite\Transformation\Adapter\BranchDomain;
use Appwrite\Transformation\Transformation;
use Appwrite\Filter\Filter;
use Appwrite\Filter\Adapter\BranchDomain as FilterBranchDomain;
use Appwrite\Utopia\Response\Model\Deployment;
use Appwrite\Vcs\Comment;
use Exception;
@@ -1039,17 +1039,16 @@ class Builds extends Action
// VCS branch
$branchName = $deployment->getAttribute('providerBranch');
if (!empty($branchName)) {
$transformation = new Transformation([new BranchDomain()]);
$transformation
$filter = new Filter([new FilterBranchDomain()]);
$filter
->setInput([
'branch' => $branchName,
'resourceId' => $resource->getId(),
'projectId' => $project->getId(),
'sitesDomain' => $platform['sitesDomain'],
])
->setTraits([])
->transform();
$domain = $transformation->getOutput();
->filter();
$domain = $filter->getOutput();
$ruleId = md5($domain);
try {
+189
View File
@@ -0,0 +1,189 @@
<?php
namespace Tests\Unit\Filter;
use Appwrite\Filter\Filter;
use Appwrite\Filter\Adapter\BranchDomain;
use PHPUnit\Framework\TestCase;
class FilterTest extends TestCase
{
public function testBranchDomain(): void
{
$filter = new Filter([new BranchDomain()]);
// Branch name with slash
$success = $filter
->setInput([
'branch' => 'feature/test',
'resourceId' => 'site123',
'projectId' => 'proj456',
'sitesDomain' => 'appwrite.network'
])
->filter();
$this->assertTrue($success);
$domain = $filter->getOutput();
$this->assertStringNotContainsString('/', $domain);
$this->assertStringStartsWith('branch-feature-test-', $domain);
$this->assertStringEndsWith('.appwrite.network', $domain);
// Branch domain consistency
$success = $filter
->setInput([
'branch' => 'feature/test',
'resourceId' => 'site123',
'projectId' => 'proj456',
'sitesDomain' => 'appwrite.network'
])
->filter();
$this->assertTrue($success);
$domain2 = $filter->getOutput();
$this->assertEquals($domain, $domain2);
// Different resources should produce different domains
$success = $filter
->setInput([
'branch' => 'feature/test',
'resourceId' => 'site789',
'projectId' => 'proj456',
'sitesDomain' => 'appwrite.network'
])
->filter();
$this->assertTrue($success);
$domain2 = $filter->getOutput();
$this->assertNotEquals($domain, $domain2);
// Different projects should produce different domains
$success = $filter
->setInput([
'branch' => 'feature/test',
'resourceId' => 'site123',
'projectId' => 'proj789',
'sitesDomain' => 'appwrite.network'
])
->filter();
$this->assertTrue($success);
$domain2 = $filter->getOutput();
$this->assertNotEquals($domain, $domain2);
// Some real-world branch names
$success = $filter
->setInput([
'branch' => 'feature/SER-1234',
'resourceId' => 'site123',
'projectId' => 'proj456',
'sitesDomain' => 'appwrite.network'
])
->filter();
$this->assertTrue($success);
$domain = $filter->getOutput();
$this->assertStringStartsWith('branch-feature-ser-1234-', $domain);
$this->assertStringEndsWith('.appwrite.network', $domain);
$success = $filter
->setInput([
'branch' => 'bugfix/fix-login',
'resourceId' => 'site123',
'projectId' => 'proj456',
'sitesDomain' => 'appwrite.network'
])
->filter();
$this->assertTrue($success);
$domain = $filter->getOutput();
$this->assertStringStartsWith('branch-bugfix-fix-login-', $domain);
$this->assertStringEndsWith('.appwrite.network', $domain);
$success = $filter
->setInput([
'branch' => 'hotfix/v1.2.3',
'resourceId' => 'site123',
'projectId' => 'proj456',
'sitesDomain' => 'appwrite.network'
])
->filter();
$this->assertTrue($success);
$domain = $filter->getOutput();
$this->assertStringStartsWith('branch-hotfix-v1-2-3-', $domain);
$this->assertStringEndsWith('.appwrite.network', $domain);
$success = $filter
->setInput([
'branch' => 'release/2024.01',
'resourceId' => 'site123',
'projectId' => 'proj456',
'sitesDomain' => 'appwrite.network'
])
->filter();
$this->assertTrue($success);
$domain = $filter->getOutput();
$this->assertStringStartsWith('branch-release-2024-01-', $domain);
$this->assertStringEndsWith('.appwrite.network', $domain);
$success = $filter
->setInput([
'branch' => 'user/john/experiment',
'resourceId' => 'site123',
'projectId' => 'proj456',
'sitesDomain' => 'appwrite.network'
])
->filter();
$this->assertTrue($success);
$domain = $filter->getOutput();
$this->assertStringStartsWith('branch-user-john-experi-', $domain);
$this->assertStringEndsWith('.appwrite.network', $domain);
$success = $filter
->setInput([
'branch' => 'dependabot/npm_and_yarn/lodash-4.17.21',
'resourceId' => 'site123',
'projectId' => 'proj456',
'sitesDomain' => 'appwrite.network'
])
->filter();
$this->assertTrue($success);
$domain = $filter->getOutput();
$this->assertStringStartsWith('branch-dependabot-npm-a-', $domain);
$this->assertStringEndsWith('.appwrite.network', $domain);
// Invalid inputs
$success = $filter
->setInput([
'branch' => '',
'resourceId' => 'site123',
'projectId' => 'proj456',
'sitesDomain' => 'appwrite.network'
])
->filter();
$this->assertFalse($success);
$success = $filter
->setInput([
'branch' => 'feature/test',
'resourceId' => '',
'projectId' => 'proj456',
'sitesDomain' => 'appwrite.network'
])
->filter();
$this->assertFalse($success);
$success = $filter
->setInput([
'branch' => 'feature/test',
'resourceId' => 'site123',
'projectId' => '',
'sitesDomain' => 'appwrite.network'
])
->filter();
$this->assertFalse($success);
$success = $filter
->setInput([
'branch' => 'feature/test',
'resourceId' => 'site123',
'projectId' => 'proj456',
'sitesDomain' => ''
])
->filter();
$this->assertFalse($success);
}
}
@@ -2,7 +2,6 @@
namespace Tests\Unit\Transformation;
use Appwrite\Transformation\Adapter\BranchDomain;
use Appwrite\Transformation\Adapter\Mock;
use Appwrite\Transformation\Adapter\Preview;
use Appwrite\Transformation\Transformation;
@@ -41,142 +40,4 @@ class TransformationTest extends TestCase
$this->assertStringContainsString("Preview by", $transformer->getOutput());
$this->assertStringContainsString("Mock:", $transformer->getOutput());
}
public function testBranchDomain(): void
{
$transformer = new Transformation([new BranchDomain()]);
// Branch name with slash
$transformer
->setInput([
'branch' => 'feature/test',
'resourceId' => 'site123',
'projectId' => 'proj456',
'sitesDomain' => 'appwrite.network'
])
->setTraits([])
->transform();
$domain = $transformer->getOutput();
$this->assertStringNotContainsString('/', $domain);
$this->assertStringStartsWith('branch-feature-test-', $domain);
$this->assertStringEndsWith('.appwrite.network', $domain);
// Branch domain consistency
$transformer
->setInput([
'branch' => 'feature/test',
'resourceId' => 'site123',
'projectId' => 'proj456',
'sitesDomain' => 'appwrite.network'
])
->setTraits([])
->transform();
$domain2 = $transformer->getOutput();
$this->assertEquals($domain, $domain2);
// Different resources should produce different domains
$transformer
->setInput([
'branch' => 'feature/test',
'resourceId' => 'site789',
'projectId' => 'proj456',
'sitesDomain' => 'appwrite.network'
])
->setTraits([])
->transform();
$domain2 = $transformer->getOutput();
$this->assertNotEquals($domain, $domain2);
// Different projects should produce different domains
$transformer
->setInput([
'branch' => 'feature/test',
'resourceId' => 'site123',
'projectId' => 'proj789',
'sitesDomain' => 'appwrite.network'
])
->setTraits([])
->transform();
$domain2 = $transformer->getOutput();
$this->assertNotEquals($domain, $domain2);
// Some real-world branch names
$transformer
->setInput([
'branch' => 'feature/SER-1234',
'resourceId' => 'site123',
'projectId' => 'proj456',
'sitesDomain' => 'appwrite.network'
])
->setTraits([])
->transform();
$domain = $transformer->getOutput();
$this->assertStringStartsWith('branch-feature-ser-1234-', $domain);
$this->assertStringEndsWith('.appwrite.network', $domain);
$transformer
->setInput([
'branch' => 'bugfix/fix-login',
'resourceId' => 'site123',
'projectId' => 'proj456',
'sitesDomain' => 'appwrite.network'
])
->setTraits([])
->transform();
$domain = $transformer->getOutput();
$this->assertStringStartsWith('branch-bugfix-fix-login-', $domain);
$this->assertStringEndsWith('.appwrite.network', $domain);
$transformer
->setInput([
'branch' => 'hotfix/v1.2.3',
'resourceId' => 'site123',
'projectId' => 'proj456',
'sitesDomain' => 'appwrite.network'
])
->setTraits([])
->transform();
$domain = $transformer->getOutput();
$this->assertStringStartsWith('branch-hotfix-v1-2-3-', $domain);
$this->assertStringEndsWith('.appwrite.network', $domain);
$transformer
->setInput([
'branch' => 'release/2024.01',
'resourceId' => 'site123',
'projectId' => 'proj456',
'sitesDomain' => 'appwrite.network'
])
->setTraits([])
->transform();
$domain = $transformer->getOutput();
$this->assertStringStartsWith('branch-release-2024-01-', $domain);
$this->assertStringEndsWith('.appwrite.network', $domain);
$transformer
->setInput([
'branch' => 'user/john/experiment',
'resourceId' => 'site123',
'projectId' => 'proj456',
'sitesDomain' => 'appwrite.network'
])
->setTraits([])
->transform();
$domain = $transformer->getOutput();
$this->assertStringStartsWith('branch-user-john-experi-', $domain);
$this->assertStringEndsWith('.appwrite.network', $domain);
$transformer
->setInput([
'branch' => 'dependabot/npm_and_yarn/lodash-4.17.21',
'resourceId' => 'site123',
'projectId' => 'proj456',
'sitesDomain' => 'appwrite.network'
])
->setTraits([])
->transform();
$domain = $transformer->getOutput();
$this->assertStringStartsWith('branch-dependabot-npm-a-', $domain);
$this->assertStringEndsWith('.appwrite.network', $domain);
}
}