Skip to content

Commit 750cf44

Browse files
Data residency support
1 parent 448e245 commit 750cf44

File tree

5 files changed

+188
-2
lines changed

5 files changed

+188
-2
lines changed

apigee_edge.services.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ services:
2424

2525
apigee_edge.key_entity_form_enhancer:
2626
class: Drupal\apigee_edge\KeyEntityFormEnhancer
27-
arguments: ['@apigee_edge.sdk_connector', '@apigee_edge.authentication.oauth_token_storage', '@entity_type.manager', '@config.factory', '@email.validator']
27+
arguments: ['@apigee_edge.sdk_connector', '@apigee_edge.authentication.oauth_token_storage', '@entity_type.manager', '@config.factory', '@email.validator', '@apigee_edge.data_residency_endpoint']
2828
calls:
2929
- [setMessenger, ['@messenger']]
3030
- [setStringTranslation, ['@string_translation']]
@@ -217,3 +217,7 @@ services:
217217
apigee_edge.post_user_delete_action_performer:
218218
class: Drupal\apigee_edge\User\RemoveRelatedDeveloperAccountSynchronousPostUserDeleteActionPerformer
219219
arguments: ['@entity_type.manager', '@logger.channel.apigee_edge']
220+
221+
apigee_edge.data_residency_endpoint:
222+
class: Drupal\apigee_edge\Service\DataResidencyEndpoint
223+
arguments: ['@apigee_edge.sdk_connector', '@state', '@messenger', '@string_translation']

src/KeyEntityFormEnhancer.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
use Drupal\apigee_edge\Plugin\EdgeKeyTypeInterface;
4242
use Drupal\apigee_edge\Plugin\KeyProviderRequirementsInterface;
4343
use Drupal\apigee_edge\Plugin\KeyType\ApigeeAuthKeyType;
44+
use Drupal\apigee_edge\Service\DataResidencyEndpointInterface;
4445
use Drupal\key\Form\KeyFormBase;
4546
use Drupal\key\KeyInterface;
4647
use Drupal\key\Plugin\KeyProviderSettableValueInterface;
@@ -99,6 +100,13 @@ final class KeyEntityFormEnhancer {
99100
*/
100101
private $emailValidator;
101102

103+
/**
104+
* The data residency endpoint service.
105+
*
106+
* @var \Drupal\apigee_edge\Service\DataResidencyEndpointInterface
107+
*/
108+
private $dataResidencyEndpoint;
109+
102110
/**
103111
* KeyEntityFormEnhancer constructor.
104112
*
@@ -112,13 +120,16 @@ final class KeyEntityFormEnhancer {
112120
* The config factory.
113121
* @param \Drupal\Component\Utility\EmailValidatorInterface $email_validator
114122
* The email validator.
123+
* @param \Drupal\apigee_edge\Service\DataResidencyEndpointInterface $data_residency_endpoint
124+
* The data residency endpoint service.
115125
*/
116-
public function __construct(SDKConnectorInterface $connector, OauthTokenStorageInterface $oauth_token_storage, EntityTypeManagerInterface $entity_type_manager, ConfigFactoryInterface $config_factory, EmailValidatorInterface $email_validator) {
126+
public function __construct(SDKConnectorInterface $connector, OauthTokenStorageInterface $oauth_token_storage, EntityTypeManagerInterface $entity_type_manager, ConfigFactoryInterface $config_factory, EmailValidatorInterface $email_validator, DataResidencyEndpointInterface $data_residency_endpoint) {
117127
$this->connector = $connector;
118128
$this->entityTypeManager = $entity_type_manager;
119129
$this->oauthTokenStorage = $oauth_token_storage;
120130
$this->configFactory = $config_factory;
121131
$this->emailValidator = $email_validator;
132+
$this->dataResidencyEndpoint = $data_residency_endpoint;
122133
}
123134

124135
/**
@@ -342,6 +353,14 @@ public function validateForm(array &$form, FormStateInterface $form_state): void
342353
// Clear existing OAuth token data.
343354
$this->cleanUpOauthTokenData();
344355
}
356+
357+
// Data Residency check.
358+
\Drupal::state()->delete(DataResidencyEndpointInterface::DRZ_ENDPOINT);
359+
$key_value_array = json_decode($key_value, TRUE);
360+
if ($key_value_array['instance_type'] == EdgeKeyTypeInterface::INSTANCE_TYPE_HYBRID) {
361+
$this->dataResidencyEndpoint->getEndpoint($test_key);
362+
}
363+
345364
// Test the connection.
346365
$this->connector->testConnection($test_key);
347366
$this->messenger()->addStatus($this->t('Connection successful.'));

src/Plugin/EdgeKeyTypeBase.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use Drupal\apigee_edge\Exception\AuthenticationKeyValueMalformedException;
2727
use Drupal\key\KeyInterface;
2828
use Drupal\key\Plugin\KeyTypeBase;
29+
use Drupal\apigee_edge\Service\DataResidencyEndpointInterface;
2930

3031
/**
3132
* Defines a base class for Apigee Edge Key Type plugins.
@@ -70,6 +71,9 @@ public function getAuthenticationType(KeyInterface $key): string {
7071
*/
7172
public function getEndpoint(KeyInterface $key): string {
7273
if ($this->getInstanceType($key) === EdgeKeyTypeInterface::INSTANCE_TYPE_HYBRID) {
74+
if ($endpoint = \Drupal::state()->get(DataResidencyEndpointInterface::DRZ_ENDPOINT)) {
75+
return $endpoint;
76+
}
7377
return ClientInterface::APIGEE_ON_GCP_ENDPOINT;
7478
}
7579
elseif ($this->getInstanceType($key) === EdgeKeyTypeInterface::INSTANCE_TYPE_PUBLIC) {
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<?php
2+
3+
/**
4+
* Copyright 2025 Google Inc.
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU General Public License
8+
* version 2 as published by the Free Software Foundation.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18+
* MA 02110-1301, USA.
19+
*/
20+
21+
namespace Drupal\apigee_edge\Service;
22+
23+
use Apigee\Edge\Api\Management\Controller\OrganizationController;
24+
use Apigee\Edge\ClientInterface;
25+
use Drupal\apigee_edge\SDKConnectorInterface;
26+
use Drupal\Core\State\StateInterface;
27+
use Drupal\key\KeyInterface;
28+
use Drupal\Core\Messenger\MessengerInterface;
29+
use Drupal\Core\StringTranslation\StringTranslationTrait;
30+
use Drupal\Core\StringTranslation\TranslationInterface;
31+
32+
/**
33+
* Discovers the data residency endpoint for a given key.
34+
*/
35+
class DataResidencyEndpoint implements DataResidencyEndpointInterface {
36+
use StringTranslationTrait;
37+
38+
/**
39+
* The SDK connector service.
40+
*
41+
* @var \Drupal\apigee_edge\SDKConnectorInterface
42+
*/
43+
protected $sdkConnector;
44+
45+
/**
46+
* The state service.
47+
*
48+
* @var \Drupal\Core\State\StateInterface
49+
*/
50+
protected $state;
51+
52+
/**
53+
* The messenger service.
54+
*
55+
* @var \Drupal\Core\Messenger\MessengerInterface
56+
*/
57+
protected $messenger;
58+
59+
/**
60+
* The string translation service.
61+
*
62+
* @var \Drupal\Core\StringTranslation\TranslationInterface
63+
*/
64+
protected $stringTranslation;
65+
66+
/**
67+
* Constructs a new DataResidencyEndpoint object.
68+
*
69+
* @param \Drupal\apigee_edge\SDKConnectorInterface $sdk_connector
70+
* The SDK connector service.
71+
* @param \Drupal\Core\State\StateInterface $state
72+
* The state service.
73+
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
74+
* The messenger service.
75+
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
76+
* String translation.
77+
*/
78+
public function __construct(SDKConnectorInterface $sdk_connector, StateInterface $state, MessengerInterface $messenger, TranslationInterface $string_translation) {
79+
$this->sdkConnector = $sdk_connector;
80+
$this->state = $state;
81+
$this->messenger = $messenger;
82+
$this->stringTranslation = $string_translation;
83+
}
84+
85+
/**
86+
* {@inheritdoc}
87+
*/
88+
public function getEndpoint(KeyInterface $key): void {
89+
/** @var \Drupal\apigee_edge\Plugin\KeyType\ApigeeAuthKeyType $key_type */
90+
$key_type = $key->getKeyType();
91+
92+
try {
93+
$base_endpoint = ClientInterface::APIGEE_ON_GCP_ENDPOINT;
94+
95+
$client = $this->sdkConnector->buildClient($key_type->getAuthenticationMethod($key), $base_endpoint);
96+
$orgController = new OrganizationController($client);
97+
$dataResidencyData = $orgController->getProjectMapping($key_type->getOrganization($key));
98+
99+
if (isset($dataResidencyData['location']) && $dataResidencyData['location']) {
100+
$dataResidencyEndpoint = str_replace("https://", "https://{$dataResidencyData['location']}-", $base_endpoint);
101+
102+
$this->state->set(self::DRZ_ENDPOINT, $dataResidencyEndpoint);
103+
$this->messenger->addStatus($this->stringTranslation->translate('Data residency is enabled for this organization. Service endpoint being used is @serviceEndpoint', ['@serviceEndpoint' => $dataResidencyEndpoint]));
104+
}
105+
else {
106+
$this->state->delete(self::DRZ_ENDPOINT);
107+
}
108+
}
109+
catch (\Exception $e) {
110+
// We can ignore this exception, as it means that the data residency
111+
// endpoint could not be determined.
112+
$this->state->delete(self::DRZ_ENDPOINT);
113+
}
114+
}
115+
116+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
/**
4+
* Copyright 2025 Google Inc.
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU General Public License
8+
* version 2 as published by the Free Software Foundation.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18+
* MA 02110-1301, USA.
19+
*/
20+
21+
namespace Drupal\apigee_edge\Service;
22+
23+
use Drupal\key\KeyInterface;
24+
25+
/**
26+
* Defines an interface for discovering the data residency endpoint.
27+
*/
28+
interface DataResidencyEndpointInterface {
29+
30+
/**
31+
* The key used to store the endpoint in the state.
32+
*/
33+
public const DRZ_ENDPOINT = 'drzendpoint';
34+
35+
/**
36+
* Discovers and stores the data residency endpoint for a given key.
37+
*
38+
* @param \Drupal\key\KeyInterface $key
39+
* The key to discover the endpoint for.
40+
*/
41+
public function getEndpoint(KeyInterface $key): void;
42+
43+
}

0 commit comments

Comments
 (0)