From 7aa0567abb9a69ab4ecbc21259caf35eb75d466c Mon Sep 17 00:00:00 2001 From: Ondrej Popelka Date: Fri, 24 Apr 2026 16:37:37 +0200 Subject: [PATCH 1/3] feat(sandboxes-service-api-client): add type filter and type field to AppsApiClient https://linear.app/keboola/issue/AJDA-1942 --- .../src/Apps/App.php | 14 +++++ .../src/Apps/AppsApiClient.php | 10 ++-- .../tests/Apps/AppsApiClientTest.php | 52 +++++++++++++++++++ 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/libs/sandboxes-service-api-client/src/Apps/App.php b/libs/sandboxes-service-api-client/src/Apps/App.php index e9bacbedc..3b3b7675c 100644 --- a/libs/sandboxes-service-api-client/src/Apps/App.php +++ b/libs/sandboxes-service-api-client/src/Apps/App.php @@ -61,6 +61,7 @@ class App private string $id; private string $projectId; private string $componentId; + private ?string $type = null; private ?string $branchId = null; private string $configId; private string $configVersion; @@ -83,6 +84,7 @@ public static function fromArray(array $in): self $app->setId((string) $in['id']); $app->setProjectId((string) $in['projectId']); $app->setComponentId((string) $in['componentId']); + $app->setType(isset($in['type']) ? (string) $in['type'] : null); $app->setBranchId(isset($in['branchId']) ? $in['branchId'] : null); $app->setConfigId((string) $in['configId']); $app->setConfigVersion((string) $in['configVersion']); @@ -102,6 +104,7 @@ public function toArray(): array 'id' => $this->id, 'projectId' => $this->projectId, 'componentId' => $this->componentId, + 'type' => $this->type, 'branchId' => $this->branchId, 'configId' => $this->configId, 'configVersion' => $this->configVersion, @@ -147,6 +150,17 @@ public function setComponentId(string $componentId): self return $this; } + public function getType(): ?string + { + return $this->type; + } + + public function setType(?string $type): self + { + $this->type = $type; + return $this; + } + public function getBranchId(): ?string { return $this->branchId; diff --git a/libs/sandboxes-service-api-client/src/Apps/AppsApiClient.php b/libs/sandboxes-service-api-client/src/Apps/AppsApiClient.php index 1c4efce9f..d872008e3 100644 --- a/libs/sandboxes-service-api-client/src/Apps/AppsApiClient.php +++ b/libs/sandboxes-service-api-client/src/Apps/AppsApiClient.php @@ -19,11 +19,12 @@ public function __construct(ApiClientConfiguration $configuration) } /** - * @param int|null $offset - * @param int|null $limit + * @param int|null $offset + * @param int|null $limit + * @param list $types Filter by app type (e.g. 'python', 'r', 'streamlit') * @return array */ - public function listApps(?int $offset = null, ?int $limit = null): array + public function listApps(?int $offset = null, ?int $limit = null, array $types = []): array { $queryParams = []; if ($offset !== null) { @@ -32,6 +33,9 @@ public function listApps(?int $offset = null, ?int $limit = null): array if ($limit !== null) { $queryParams['limit'] = (string) $limit; } + foreach ($types as $type) { + $queryParams['type'][] = $type; + } $uri = '/apps'; if (!empty($queryParams)) { diff --git a/libs/sandboxes-service-api-client/tests/Apps/AppsApiClientTest.php b/libs/sandboxes-service-api-client/tests/Apps/AppsApiClientTest.php index fc60d98f4..507c59e9e 100644 --- a/libs/sandboxes-service-api-client/tests/Apps/AppsApiClientTest.php +++ b/libs/sandboxes-service-api-client/tests/Apps/AppsApiClientTest.php @@ -326,6 +326,58 @@ public function testListAppsWithOnlyLimit(): void ); } + public function testListAppsWithTypes(): void + { + $responseBody = [ + [ + 'id' => 'app-id-1', + 'projectId' => 'project-id', + 'componentId' => 'keboola.jupyter-sandbox', + 'type' => 'python', + 'branchId' => null, + 'configId' => 'config-id-1', + 'configVersion' => '1', + 'state' => 'running', + 'desiredState' => 'running', + 'lastRequestTimestamp' => '2024-02-01T08:00:00+01:00', + 'url' => null, + 'autoSuspendAfterSeconds' => 3600, + 'provisioningStrategy' => 'operator', + ], + ]; + + $requestHandler = self::createRequestHandler($requestsHistory, [ + new Response( + 200, + ['Content-Type' => 'application/json'], + Json::encodeArray($responseBody), + ), + ]); + + $client = new AppsApiClient( + new ApiClientConfiguration( + baseUrl: 'https://data-apps.keboola.com', + storageToken: 'my-token', + userAgent: 'Keboola Sandboxes Service API PHP Client', + requestHandler: $requestHandler(...), + ), + ); + $result = $client->listApps(types: ['python', 'r']); + + self::assertEquals([App::fromArray($responseBody[0])], $result); + + self::assertCount(1, $requestsHistory); + self::assertRequestEquals( + 'GET', + 'https://data-apps.keboola.com/apps?type%5B0%5D=python&type%5B1%5D=r', + [ + 'X-StorageApi-Token' => 'my-token', + ], + '', + $requestsHistory[0]['request'], + ); + } + public function testCreateApp(): void { $responseBody = [ From 08197a449628623fe9fc1d40f6e8ed5c49bb3fd2 Mon Sep 17 00:00:00 2001 From: Ondrej Popelka Date: Fri, 24 Apr 2026 23:07:46 +0200 Subject: [PATCH 2/3] feat(sandboxes-service-api-client): add type filter and type field to AppsApiClient https://linear.app/keboola/issue/AJDA-1942 --- libs/sandboxes-service-api-client/tests/Apps/AppTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/sandboxes-service-api-client/tests/Apps/AppTest.php b/libs/sandboxes-service-api-client/tests/Apps/AppTest.php index 9bdd51977..c4249f494 100644 --- a/libs/sandboxes-service-api-client/tests/Apps/AppTest.php +++ b/libs/sandboxes-service-api-client/tests/Apps/AppTest.php @@ -71,6 +71,7 @@ public function testToArray(): void 'id' => 'app-id', 'projectId' => 'project-id', 'componentId' => 'keboola.data-apps', + 'type' => null, 'branchId' => 'branch-id', 'configId' => 'config-id', 'configVersion' => '5', From 1939902d3248491f61db8def3946f8bdd1fddb08 Mon Sep 17 00:00:00 2001 From: Ondrej Popelka Date: Fri, 24 Apr 2026 23:26:08 +0200 Subject: [PATCH 3/3] feat(sandboxes-service-api-client): add type filter and type field to AppsApiClient https://linear.app/keboola/issue/AJDA-1942 --- .../src/Apps/AppsApiClient.php | 14 ++++++-- .../tests/Apps/AppsApiClientTest.php | 36 +++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/libs/sandboxes-service-api-client/src/Apps/AppsApiClient.php b/libs/sandboxes-service-api-client/src/Apps/AppsApiClient.php index d872008e3..8b514ca4f 100644 --- a/libs/sandboxes-service-api-client/src/Apps/AppsApiClient.php +++ b/libs/sandboxes-service-api-client/src/Apps/AppsApiClient.php @@ -21,11 +21,16 @@ public function __construct(ApiClientConfiguration $configuration) /** * @param int|null $offset * @param int|null $limit - * @param list $types Filter by app type (e.g. 'python', 'r', 'streamlit') + * @param list $types Filter by app type (e.g. 'python', 'r', 'streamlit') + * @param bool $listAll When true, lists all apps in the project regardless of token ownership * @return array */ - public function listApps(?int $offset = null, ?int $limit = null, array $types = []): array - { + public function listApps( + ?int $offset = null, + ?int $limit = null, + array $types = [], + bool $listAll = false, + ): array { $queryParams = []; if ($offset !== null) { $queryParams['offset'] = (string) $offset; @@ -36,6 +41,9 @@ public function listApps(?int $offset = null, ?int $limit = null, array $types = foreach ($types as $type) { $queryParams['type'][] = $type; } + if ($listAll) { + $queryParams['listAll'] = '1'; + } $uri = '/apps'; if (!empty($queryParams)) { diff --git a/libs/sandboxes-service-api-client/tests/Apps/AppsApiClientTest.php b/libs/sandboxes-service-api-client/tests/Apps/AppsApiClientTest.php index 507c59e9e..5a479304a 100644 --- a/libs/sandboxes-service-api-client/tests/Apps/AppsApiClientTest.php +++ b/libs/sandboxes-service-api-client/tests/Apps/AppsApiClientTest.php @@ -326,6 +326,42 @@ public function testListAppsWithOnlyLimit(): void ); } + public function testListAppsWithListAll(): void + { + $responseBody = []; + + $requestHandler = self::createRequestHandler($requestsHistory, [ + new Response( + 200, + ['Content-Type' => 'application/json'], + Json::encodeArray($responseBody), + ), + ]); + + $client = new AppsApiClient( + new ApiClientConfiguration( + baseUrl: 'https://data-apps.keboola.com', + storageToken: 'my-token', + userAgent: 'Keboola Sandboxes Service API PHP Client', + requestHandler: $requestHandler(...), + ), + ); + $result = $client->listApps(listAll: true); + + self::assertEquals([], $result); + + self::assertCount(1, $requestsHistory); + self::assertRequestEquals( + 'GET', + 'https://data-apps.keboola.com/apps?listAll=1', + [ + 'X-StorageApi-Token' => 'my-token', + ], + '', + $requestsHistory[0]['request'], + ); + } + public function testListAppsWithTypes(): void { $responseBody = [