Skip to content

Commit 66da233

Browse files
committed
init and test shared/linked bucket for load to RA WS
1 parent aee7a85 commit 66da233

File tree

1 file changed

+235
-0
lines changed

1 file changed

+235
-0
lines changed
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
<?php
2+
3+
namespace Backend\ReaderWorkspaces;
4+
5+
use Doctrine\DBAL\Connection;
6+
use Keboola\Csv\CsvFile;
7+
use Keboola\StorageApi\Client;
8+
use Keboola\StorageApi\Components;
9+
use Keboola\StorageApi\Options\Components\Configuration;
10+
use Keboola\StorageApi\Options\Components\ListComponentsOptions;
11+
use Keboola\StorageApi\Tokens;
12+
use Keboola\StorageApi\WorkspaceLoginType;
13+
use Keboola\StorageApi\Workspaces;
14+
use Keboola\TableBackendUtils\Escaping\Snowflake\SnowflakeQuote;
15+
use Keboola\Test\Backend\WorkspaceConnectionTrait;
16+
use Keboola\Test\Backend\WorkspaceCredentialsAssertTrait;
17+
use Keboola\Test\Backend\Workspaces\Backend\WorkspaceBackendFactory;
18+
use Keboola\Test\Backend\Workspaces\WorkspacesTestCase;
19+
use Keboola\Test\Utils\PemKeyCertificateGenerator;
20+
use Keboola\Test\Utils\SnowflakeConnectionUtils;
21+
22+
class WorkspacesReaderSharingTest extends WorkspacesTestCase
23+
{
24+
use WorkspaceConnectionTrait;
25+
use WorkspaceCredentialsAssertTrait;
26+
use SnowflakeConnectionUtils;
27+
28+
private static ?Client $client = null;
29+
30+
private static ?Connection $backendConnection = null;
31+
32+
public function setUp(): void
33+
{
34+
$this->_client = $this->getDefaultClient();
35+
$this->tokens = new Tokens($this->_client);
36+
37+
$linkClient = $this->getClient([
38+
'token' => STORAGE_API_LINKING_TOKEN,
39+
'url' => STORAGE_API_URL,
40+
]);
41+
42+
$components = new Components($linkClient);
43+
foreach ($components->listComponents() as $component) {
44+
foreach ($component['configurations'] as $configuration) {
45+
$components->deleteConfiguration($component['id'], $configuration['id']);
46+
}
47+
}
48+
49+
// erase all deleted configurations
50+
foreach ($components->listComponents((new ListComponentsOptions())->setIsDeleted(true)) as $component) {
51+
foreach ($component['configurations'] as $configuration) {
52+
$components->deleteConfiguration($component['id'], $configuration['id']);
53+
}
54+
}
55+
56+
if (self::$client === null) {
57+
self::$client = $this->_client;
58+
}
59+
60+
if (self::$backendConnection === null) {
61+
self::$backendConnection = $this->ensureSnowflakeConnection();
62+
}
63+
64+
// drop all reader workspaces before test
65+
$workspaces = new Workspaces(self::$client);
66+
$filteredReaderWorkspaces = array_filter($workspaces->listWorkspaces(), static fn($workspace
67+
) => $workspace['platformUsageType'] === 'reader');
68+
foreach ($filteredReaderWorkspaces as $workspace) {
69+
$workspaces->deleteWorkspace($workspace['id']);
70+
}
71+
72+
// drop all readers accounts
73+
self::dropReaderAccounts();
74+
75+
// ensure the workspace and the reader account is cleaned up.
76+
self::ensureReaderAccountIsRemoved();
77+
78+
// unlink buckets
79+
$linkedBuckets = $linkClient->listBuckets();
80+
foreach ($linkedBuckets as $bucket) {
81+
$linkClient->dropBucket($bucket['id']);
82+
}
83+
84+
$shareClient = $this->getClient([
85+
'token' => STORAGE_API_SHARE_TOKEN,
86+
'url' => STORAGE_API_URL,
87+
]);
88+
89+
foreach ($shareClient->listBuckets() as $bucket) {
90+
if ($shareClient->isSharedBucket($bucket['id'])) {
91+
$shareClient->unshareBucket($bucket['id']);
92+
}
93+
}
94+
95+
$this->_initEmptyTestBuckets();
96+
}
97+
98+
public function testShareLinkAndLoadTableToReaderWorkspace(): void
99+
{
100+
$linkClient = $this->getClient([
101+
'token' => STORAGE_API_LINKING_TOKEN,
102+
'url' => STORAGE_API_URL,
103+
]);
104+
105+
// Step 1: Create a source bucket and table
106+
$this->_client->createTableAsync(
107+
$this->getTestBucketId(),
108+
'languages',
109+
new CsvFile(__DIR__ . '/../../_data/languages.csv'),
110+
);
111+
112+
$shareClient = $this->getClient([
113+
'token' => STORAGE_API_SHARE_TOKEN,
114+
'url' => STORAGE_API_URL,
115+
]);
116+
117+
$tokenInfo = $linkClient->verifyToken();
118+
$projectId = $tokenInfo['owner']['id'];
119+
// Step 2: Share the bucket with the organization
120+
$shareClient->shareBucketToProjects($this->getTestBucketId(), [$projectId]);
121+
122+
// Step 3: Link the shared bucket to the current project
123+
$response = $linkClient->listSharedBuckets();
124+
$this->assertCount(1, $response);
125+
$sharedBucket = reset($response);
126+
$hashedUniqueTableName = sha1('linked-' . $this->generateDescriptionForTestObject());
127+
$linkedBucketId = $linkClient->linkBucket(
128+
$hashedUniqueTableName,
129+
'in',
130+
$sharedBucket['project']['id'],
131+
$sharedBucket['id'],
132+
null,
133+
false,
134+
);
135+
136+
// Step 4: Create a reader workspace
137+
$workspaces = new Workspaces($linkClient);
138+
$workspace = $this->prepareWorkspace(null, $linkClient);
139+
140+
// Step 5: Load the linked table into the reader workspace
141+
$linkedTableId = $linkedBucketId . '.languages';
142+
$workspaces->loadWorkspaceData($workspace['id'], [
143+
'input' => [
144+
[
145+
'source' => $linkedTableId,
146+
'destination' => 'languages',
147+
],
148+
],
149+
]);
150+
151+
// Step 6: Assert that the data is loaded correctly
152+
$db = WorkspaceBackendFactory::createWorkspaceForSnowflakeDbal($workspace);
153+
$data = $db->fetchAll('languages');
154+
$this->assertCount(5, $data); // languages.csv has 5 rows
155+
}
156+
157+
private function prepareWorkspace(?array $options = null, $client): array
158+
{
159+
$componentId = 'wr-db';
160+
$configurationId = 'main-1';
161+
162+
// create configuration
163+
$components = new Components($client);
164+
$components->addConfiguration((new Configuration())
165+
->setComponentId('wr-db')
166+
->setConfigurationId('main-1')
167+
->setName('readerWS')
168+
->setDescription('some desc'));
169+
170+
$components = new Components($client);
171+
172+
$key = (new PemKeyCertificateGenerator())->createPemKeyCertificate(null);
173+
174+
$workspace = $components->createConfigurationWorkspace(
175+
$componentId,
176+
$configurationId,
177+
$options ?? [
178+
'useCase' => 'reader',
179+
'backend' => 'snowflake',
180+
'loginType' => WorkspaceLoginType::SNOWFLAKE_SERVICE_KEYPAIR,
181+
'publicKey' => $key->getPublicKey(),
182+
],
183+
);
184+
185+
$workspace['connection']['privateKey'] = $key->getPrivateKey();
186+
187+
return $workspace;
188+
}
189+
190+
private function createWorkspaces(): Workspaces
191+
{
192+
$defaultBranchId = $this->getDefaultBranchId($this);
193+
$branchClient = $this->getBranchAwareDefaultClient($defaultBranchId);
194+
195+
return new Workspaces($branchClient);
196+
}
197+
198+
private static function dropReaderAccounts(): void
199+
{
200+
$accounts = self::fetchReadersAccountsForCurrentOrganization();
201+
202+
foreach ($accounts as $account) {
203+
self::$backendConnection?->executeQuery(
204+
sprintf('DROP MANAGED ACCOUNT %s;', $account['account_name']),
205+
);
206+
}
207+
}
208+
209+
private static function ensureReaderAccountIsRemoved(): void
210+
{
211+
$accounts = self::fetchReadersAccountsForCurrentOrganization();
212+
213+
self::assertCount(0, $accounts);
214+
}
215+
216+
/**
217+
* @return array<array{account_name: string}>
218+
* @throws \Doctrine\DBAL\Exception
219+
*/
220+
private static function fetchReadersAccountsForCurrentOrganization(): array
221+
{
222+
$verifiedToken = self::$client?->verifyToken();
223+
$organizationId = $verifiedToken['organization']['id'] ?? '';
224+
225+
/** @var array<array{account_name: string}> $result */
226+
$result = self::$backendConnection?->fetchAllAssociative(
227+
sprintf(
228+
'SHOW MANAGED ACCOUNTS LIKE %s;',
229+
SnowflakeQuote::quote(sprintf('%%_READER_ACCOUNT_%s', $organizationId)),
230+
),
231+
) ?? [];
232+
233+
return $result;
234+
}
235+
}

0 commit comments

Comments
 (0)