-
Notifications
You must be signed in to change notification settings - Fork 56
IDE-4857: Add PathRewriteConnector for environment based API path rewriting to make commands work with MEO.
#1977
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
Open
vishalkhode1
wants to merge
3
commits into
main
Choose a base branch
from
IDE-4857
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+453
−8
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Acquia\Cli\CloudApi; | ||
|
|
||
| use AcquiaCloudApi\Connector\ConnectorInterface; | ||
| use Psr\Http\Message\RequestInterface; | ||
| use Psr\Http\Message\ResponseInterface; | ||
|
|
||
| /** | ||
| * Decorates a ConnectorInterface to rewrite specific API paths before sending | ||
| * requests. Useful for redirecting legacy or alternative API endpoints to new | ||
| * ones transparently. | ||
| */ | ||
| final class PathRewriteConnector implements ConnectorInterface | ||
| { | ||
| /** | ||
| * The underlying connector to which requests are delegated after path rewriting. | ||
| */ | ||
| private ConnectorInterface $inner; | ||
|
|
||
| /** | ||
| * PathRewriteConnector constructor. | ||
| * | ||
| * @param ConnectorInterface $inner The connector to decorate. | ||
| */ | ||
| public function __construct( | ||
| ConnectorInterface $inner, | ||
| ) { | ||
| $this->inner = $inner; | ||
| } | ||
|
|
||
| /** | ||
| * Creates a PSR-7 request, rewriting the path if it matches a rewrite rule. | ||
| * | ||
| * @param string $verb HTTP method (e.g., 'GET', 'POST'). | ||
| * @param string $path The original API path. | ||
| * @return RequestInterface The PSR-7 request with possibly rewritten path. | ||
| */ | ||
| public function createRequest(string $verb, string $path): RequestInterface | ||
| { | ||
| return $this->inner->createRequest($verb, $this->rewritePath($path)); | ||
| } | ||
|
|
||
| /** | ||
| * Sends an HTTP request, rewriting the path if it matches a rewrite rule. | ||
| * | ||
| * @param string $verb HTTP method (e.g., 'GET', 'POST'). | ||
| * @param string $path The original API path. | ||
| * @param array<string, mixed> $options Additional request options. | ||
| * @return ResponseInterface The HTTP response. | ||
| */ | ||
| public function sendRequest(string $verb, string $path, array $options): ResponseInterface | ||
| { | ||
| return $this->inner->sendRequest($verb, $this->rewritePath($path), $options); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the base URI for the API. | ||
| * | ||
| * @return string The base URI. | ||
| */ | ||
| public function getBaseUri(): string | ||
| { | ||
| return $this->inner->getBaseUri(); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the access token for URL authentication. | ||
| * | ||
| * @return string The access token. | ||
| */ | ||
| public function getUrlAccessToken(): string | ||
| { | ||
| return $this->inner->getUrlAccessToken(); | ||
| } | ||
|
|
||
| /** | ||
| * Rewrites the API path using preg_replace if it matches any rewrite rule. | ||
| * | ||
| * @param string $path The original API path. | ||
| * @return string The rewritten path, or the original if no rule matches. | ||
| */ | ||
| private function rewritePath(string $path): string | ||
| { | ||
| foreach ($this->getPathsToRewrite() as $pattern => $replacement) { | ||
| if (preg_match($pattern, $path) === 1) { | ||
| return (string) preg_replace($pattern, $replacement, $path); | ||
| } | ||
| } | ||
|
|
||
| // Return the original path if no rewrite rule matches. | ||
| return $path; | ||
| } | ||
|
|
||
| /** | ||
| * Returns an array of regex patterns and their corresponding replacement paths for rewriting API request paths. | ||
| * | ||
| * Two rules cover all cases: | ||
| * - Paths with a trailing segment: /applications/{uuid}/foo/bar → /translation/codebases/{codebaseUuid}/foo/bar | ||
| * - Bare application UUID paths: /applications/{uuid} → /translation/codebases/{codebaseUuid} | ||
| * | ||
| * The first rule uses a capture group ($1) so any trailing path is preserved automatically, | ||
| * avoiding the need to enumerate every possible sub-path. | ||
| * | ||
| * @return array<string, string> Regex pattern => preg_replace replacement string. | ||
| */ | ||
| private function getPathsToRewrite(): array | ||
| { | ||
| $codebaseUuid = $this->getCodeBaseUuid(); | ||
| return [ | ||
| // Matches bare /applications/{uuid} with no trailing segment. | ||
| '#^/applications/[0-9a-f\-]+$#i' => '/translation/codebases/' . $codebaseUuid, | ||
| // Matches /applications/{uuid}/{anything} and preserves the trailing segment via $1. | ||
| '#^/applications/[0-9a-f\-]+/(.+)$#i' => '/translation/codebases/' . $codebaseUuid . '/$1', | ||
| ]; | ||
vishalkhode1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| /** | ||
| * Retrieves the codebase UUID. | ||
| */ | ||
| private function getCodeBaseUuid(): string | ||
| { | ||
| $codebaseUuid = getenv('AH_CODEBASE_UUID'); | ||
| if (!$codebaseUuid) { | ||
| throw new \RuntimeException('Environment variable AH_CODEBASE_UUID is not set.'); | ||
| } | ||
| return $codebaseUuid; | ||
vishalkhode1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Acquia\Cli\Tests\CloudApi; | ||
|
|
||
| use Acquia\Cli\CloudApi\ConnectorFactory; | ||
| use Acquia\Cli\CloudApi\PathRewriteConnector; | ||
| use AcquiaCloudApi\Connector\Connector; | ||
| use PHPUnit\Framework\TestCase; | ||
|
|
||
| /** | ||
| * @covers \Acquia\Cli\CloudApi\ConnectorFactory | ||
| * | ||
| * Unit tests for the ConnectorFactory. Ensures that the factory returns the correct | ||
| * connector type depending on the presence of the AH_CODEBASE_UUID environment variable. | ||
| */ | ||
| class ConnectorFactoryTest extends TestCase | ||
| { | ||
| /** | ||
| * Stores the original value of AH_CODEBASE_UUID to restore after each test. | ||
| */ | ||
| private string|false $originalEnv; | ||
|
|
||
| /** | ||
| * Saves the original environment variable before each test. | ||
| */ | ||
| protected function setUp(): void | ||
| { | ||
| parent::setUp(); | ||
| $this->originalEnv = getenv('AH_CODEBASE_UUID'); | ||
| } | ||
|
|
||
|
|
||
| /** | ||
| * @dataProvider connectorFactoryProvider | ||
| */ | ||
| public function testCreateConnectorFactoryBehavior(?string $envValue, string $expectedClass): void | ||
| { | ||
| if ($envValue !== null) { | ||
| putenv("AH_CODEBASE_UUID=$envValue"); | ||
| } else { | ||
| putenv('AH_CODEBASE_UUID'); | ||
| } | ||
| $factory = new ConnectorFactory(['key' => 'k', 'secret' => 's'], 'https://api.example.com'); | ||
| $connector = $factory->createConnector(); | ||
| $this->assertInstanceOf($expectedClass, $connector); | ||
| } | ||
|
|
||
| /** | ||
| * Data provider for testCreateConnectorFactoryBehavior() test. | ||
| * | ||
| * @return array<int, array{0: string|null, 1: class-string}> | ||
| */ | ||
| public static function connectorFactoryProvider(): array | ||
| { | ||
| return [ | ||
| // Env set: should return PathRewriteConnector. | ||
| ['1234-5678-uuid', PathRewriteConnector::class], | ||
| // Env not set: should return Connector. | ||
| [null, Connector::class], | ||
| ]; | ||
| } | ||
|
|
||
| /** | ||
| * Restores the original environment variable after each test. | ||
| */ | ||
| protected function tearDown(): void | ||
| { | ||
| if ($this->originalEnv === false) { | ||
| putenv('AH_CODEBASE_UUID'); | ||
| } else { | ||
| putenv('AH_CODEBASE_UUID=' . $this->originalEnv); | ||
| } | ||
| parent::tearDown(); | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.