Skip to content

Commit

Permalink
Merge pull request #136 from City-of-Helsinki/UHF-9034
Browse files Browse the repository at this point in the history
UHF-9034: Proper internal address support
  • Loading branch information
tuutti authored Oct 5, 2023
2 parents d304750 + 150ee01 commit 8f39f1d
Show file tree
Hide file tree
Showing 15 changed files with 663 additions and 170 deletions.
342 changes: 297 additions & 45 deletions fixtures/environments.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions helfi_api_base.services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ services:
class: Drupal\helfi_api_base\EventSubscriber\CacheTagInvalidatorSubscriber
arguments:
- '@cache_tags.invalidator'
- '@helfi_api_base.environment_resolver'
tags:
- { name: event_subscriber }

Expand Down
2 changes: 1 addition & 1 deletion phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
colors="true"
cacheResultFile=".phpunit.cache/test-results"
executionOrder="depends,defects"
forceCoversAnnotation="true"
forceCoversAnnotation="false"
beStrictAboutTestsThatDoNotTestAnything="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutChangesToGlobalState="true"
Expand Down
5 changes: 4 additions & 1 deletion src/Cache/CacheTagInvalidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,17 @@ public function __construct(
*
* @param array $tags
* An array of cache tags.
* @param array $instances
* The instances to flush caches from.
*
* @return $this
* The self.
*/
public function invalidateTags(array $tags) : self {
public function invalidateTags(array $tags, array $instances = []) : self {
try {
$this->pubSubManager->sendMessage([
'tags' => $tags,
'instances' => $instances,
]);
}
catch (ConnectionException) {
Expand Down
43 changes: 43 additions & 0 deletions src/Environment/Address.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types = 1);

namespace Drupal\helfi_api_base\Environment;

/**
* A value object to store address information.
*/
final class Address {

/**
* Constructs a new instance.
*
* @param string $domain
* The domain.
* @param string $protocol
* The protocol.
* @param int $port
* The port.
*/
public function __construct(
public readonly string $domain,
public readonly string $protocol = 'https',
public readonly int $port = 443,
) {
}

/**
* Gets the address.
*
* @return string
* The address.
*/
public function getAddress() : string {
$port = '';
if (!in_array($this->port, [80, 443])) {
$port = sprintf(':%d', $this->port);
}
return sprintf('%s://%s%s', $this->protocol, $this->domain, $port);
}

}
71 changes: 19 additions & 52 deletions src/Environment/Environment.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ final class Environment {
/**
* Constructs a new instance.
*
* @param string $domain
* The domain.
* @param \Drupal\helfi_api_base\Environment\Address $address
* The address.
* @param \Drupal\helfi_api_base\Environment\Address $internalAddress
* The internal address.
* @param array $paths
* The paths.
* @param string $protocol
* The protocol.
* @param string $id
* Environment resolver identifier for the project.
* @param \Drupal\helfi_api_base\Environment\EnvironmentEnum $environment
Expand All @@ -26,9 +26,9 @@ final class Environment {
* The environment specific metadata.
*/
public function __construct(
private readonly string $domain,
private readonly Address $address,
private readonly Address $internalAddress,
private readonly array $paths,
private readonly string $protocol,
private readonly string $id,
private readonly EnvironmentEnum $environment,
private readonly ?EnvironmentMetadata $metadata,
Expand All @@ -45,42 +45,6 @@ public function getId() : string {
return $this->id;
}

/**
* Gets the domain.
*
* @return string
* The domain.
*/
public function getDomain() : string {
return $this->domain;
}

/**
* Gets the protocol.
*
* @return string
* The protocol.
*/
public function getProtocol() : string {
return $this->protocol;
}

/**
* Gets the original URL for given language.
*
* @param string $language
* The language.
*
* @return string
* The URL.
*/
private function doGetUrl(string $language) : string {
return vsprintf('%s/%s', [
$this->getBaseUrl(),
ltrim($this->getPath($language), '/'),
]);
}

/**
* Gets the full URL for given language.
*
Expand All @@ -91,11 +55,7 @@ private function doGetUrl(string $language) : string {
* The URL.
*/
public function getUrl(string $language) : string {
$url = $this->doGetUrl($language);
// Local uses an internal address by default to allow containers to
// communicate via API requests. Convert URL back to a proper link that
// works with browsers.
return str_replace(['http://', ':8080'], ['https://', ''], $url);
return sprintf('%s/%s', $this->address->getAddress(), ltrim($this->getPath($language), '/'));
}

/**
Expand All @@ -108,7 +68,7 @@ public function getUrl(string $language) : string {
* The canonical URL.
*/
public function getInternalAddress(string $language) : string {
return $this->doGetUrl($language);
return sprintf('%s/%s', $this->internalAddress->getAddress(), ltrim($this->getPath($language), '/'));
}

/**
Expand All @@ -118,10 +78,17 @@ public function getInternalAddress(string $language) : string {
* The base url.
*/
public function getBaseUrl() : string {
return vsprintf('%s://%s', [
$this->getProtocol(),
$this->getDomain(),
]);
return $this->address->getAddress();
}

/**
* Gets the internal base url.
*
* @return string
* The base url.
*/
public function getInternalBaseUrl() : string {
return $this->internalAddress->getAddress();
}

/**
Expand Down
9 changes: 5 additions & 4 deletions src/Environment/EnvironmentResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,14 @@ private function populateEnvironments(string $pathOrJson) : void {
$project = new Project($id, ProjectMetadata::createFromArray($meta));

foreach ($environments as $environment => $settings) {
if (!isset($settings['domain'], $settings['path'])) {
throw new \InvalidArgumentException('Project missing domain or paths setting.');
if (!isset($settings['address'], $settings['internal_address'], $settings['path'])) {
throw new \InvalidArgumentException('Project missing "address", "internal_address" or "paths" setting.');
}

$project->addEnvironment($environment, new Environment(
$settings['domain'],
new Address(...$settings['address']),
new Address(...$settings['internal_address']),
$settings['path'],
$settings['protocol'] ?? 'https',
$id,
EnvironmentEnum::tryFrom($environment),
EnvironmentMetadata::createFromArray($settings['meta'] ?? [])
Expand Down
33 changes: 33 additions & 0 deletions src/EventSubscriber/CacheTagInvalidatorSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
use Drupal\helfi_api_base\Azure\PubSub\PubSubMessage;
use Drupal\helfi_api_base\Environment\EnvironmentResolverInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
Expand All @@ -18,12 +19,39 @@ final class CacheTagInvalidatorSubscriber implements EventSubscriberInterface {
*
* @param \Drupal\Core\Cache\CacheTagsInvalidatorInterface $cacheTagsInvalidator
* The cache tag invalidator subscriber.
* @param \Drupal\helfi_api_base\Environment\EnvironmentResolverInterface $environmentResolver
* The environment resolver.
*/
public function __construct(
private readonly CacheTagsInvalidatorInterface $cacheTagsInvalidator,
private readonly EnvironmentResolverInterface $environmentResolver,
) {
}

/**
* Checks if the given instance is valid.
*
* @param array $instances
* The instances.
*
* @return bool
* TRUE if valid instance.
*/
private function isValidInstance(array $instances = []) : bool {
if (!$instances) {
return TRUE;
}

try {
$project = $this->environmentResolver->getActiveProject();

return in_array($project->getName(), $instances);
}
catch (\InvalidArgumentException) {
}
return TRUE;
}

/**
* Responds to PubSub message events.
*
Expand All @@ -34,6 +62,11 @@ public function onReceive(PubSubMessage $message) : void {
if (!isset($message->data['data']['tags'])) {
return;
}
$instances = $message->data['data']['instances'] ?? [];

if (!$this->isValidInstance($instances)) {
return;
}
$this->cacheTagsInvalidator->invalidateTags($message->data['data']['tags']);
// Cache invalidator service keeps already invalidated cache tags in a
// static cache and prevents the invalidation of the same tag multiple
Expand Down
38 changes: 33 additions & 5 deletions tests/fixtures/environments.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,30 @@
"sv": "/sv/boende",
"en": "/en/housing"
},
"protocol": "http",
"domain": "helfi-asuminen.docker.so:8080"
"address": {
"protocol": "https",
"domain": "helfi-asuminen.docker.so"
},
"internal_address": {
"protocol": "http",
"domain": "helfi-asuminen.docker.so",
"port": 8080
}
},
"prod": {
"path": {
"fi": "/fi/asuminen",
"sv": "/sv/boende",
"en": "/en/housing"
},
"domain": "www.hel.fi",
"address": {
"protocol": "https",
"domain": "www.hel.fi"
},
"internal_address": {
"protocol": "https",
"domain": "nginx-asuminen-prod.apps.platta.hel.fi"
},
"meta": {}
},
"stage": {
Expand All @@ -29,7 +43,14 @@
"sv": "/sv/staging-boende",
"en": "/en/staging-housing"
},
"domain": "www.hel.fi",
"address": {
"protocol": "https",
"domain": "www.hel.fi"
},
"internal_address": {
"protocol": "https",
"domain": "nginx-asuminen-staging.apps.platta.hel.fi"
},
"meta": {
"openshift_console_link": "https://example.com"
}
Expand All @@ -40,7 +61,14 @@
"sv": "/sv/test-boende",
"en": "/en/test-housing"
},
"domain": "helfi-asuminen-test.docker.so",
"address": {
"protocol": "https",
"domain": "www.hel.fi"
},
"internal_address": {
"protocol": "https",
"domain": "nginx-asuminen-test.apps.arodevtest.hel.fi"
},
"meta": {
"openshift_console_link": "https://example.com"
}
Expand Down
Loading

0 comments on commit 8f39f1d

Please sign in to comment.