This is an automated email from the ASF dual-hosted git repository. yasith pushed a commit to branch AIRAVATA-3981/integration-health-check in repository https://gitbox.apache.org/repos/asf/airavata.git
commit a25464a1c31d93f04a33c8f78e57d7a9cc625f09 Author: yasithdev <[email protected]> AuthorDate: Thu Mar 26 15:41:00 2026 -0500 chore: final cleanup — gitignore docs/superpowers, fix health endpoint format - Wrap /health/services response in {"services": {...}} per spec - Update ServiceHealthEndpointTest and BackgroundServiceHealthTest to unwrap the "services" key before asserting on service entries - Add docs/superpowers/ to .gitignore and remove already-committed files from git tracking (local copies preserved) --- .gitignore | 2 + .../patform/monitoring/MonitoringServer.java | 2 +- ...2026-03-26-airavata-thrift-server-extraction.md | 1090 -------------------- ...-26-airavata-thrift-server-extraction-design.md | 190 ---- .../integration/BackgroundServiceHealthTest.java | 4 +- .../integration/ServiceHealthEndpointTest.java | 8 +- 6 files changed, 12 insertions(+), 1284 deletions(-) diff --git a/.gitignore b/.gitignore index 6a9a2b896a..a9c37a7d2d 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,5 @@ keystores/airavata.p12 keystores/aes.p12 modules/*/distribution/ modules/*/*/distribution/ + +docs/superpowers/ diff --git a/airavata-api/src/main/java/org/apache/airavata/patform/monitoring/MonitoringServer.java b/airavata-api/src/main/java/org/apache/airavata/patform/monitoring/MonitoringServer.java index 1f7bac390d..43fa6f7afe 100644 --- a/airavata-api/src/main/java/org/apache/airavata/patform/monitoring/MonitoringServer.java +++ b/airavata-api/src/main/java/org/apache/airavata/patform/monitoring/MonitoringServer.java @@ -137,7 +137,7 @@ public class MonitoringServer implements IServer { try { Map<String, ServiceStatus> statuses = serviceRegistry != null ? serviceRegistry.getStatuses() : Map.of(); - sendJson(exchange, 200, statuses); + sendJson(exchange, 200, Map.of("services", statuses)); } catch (Exception e) { logger.error("Error serving /health/services", e); sendResponse(exchange, 500, "Internal Server Error"); diff --git a/docs/superpowers/plans/2026-03-26-airavata-thrift-server-extraction.md b/docs/superpowers/plans/2026-03-26-airavata-thrift-server-extraction.md deleted file mode 100644 index 837d304414..0000000000 --- a/docs/superpowers/plans/2026-03-26-airavata-thrift-server-extraction.md +++ /dev/null @@ -1,1090 +0,0 @@ -# Airavata Thrift Server Extraction Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Extract all thrift server concerns from `airavata-api` into a new `airavata-thrift-server` Maven module, introduce proto DTOs in `airavata-api`, and set up MapStruct mapping at the thrift handler boundary. - -**Architecture:** `airavata-thrift-server` depends on `airavata-api`. Thrift IDL lives in `airavata-thrift-server/src/main/thrift/`. Server bootstrap (AiravataServer), the main API handler (AiravataServerHandler), profile handlers, OrchestratorServerHandler, ThriftAdapter, and Constants move to `airavata-thrift-server`. Data-access handlers (RegistryServerHandler, SharingRegistryServerHandler, CredentialStoreServerHandler) stay in `airavata-api` because services depend on them directly — [...] - -**Tech Stack:** Maven, Apache Thrift 0.22.0, Protocol Buffers 4.30.1, MapStruct 1.6.3, protobuf-maven-plugin 3.10.2 - -**Spec:** `docs/superpowers/specs/2026-03-26-airavata-thrift-server-extraction-design.md` - ---- - -## File Structure - -### New files (airavata-thrift-server) - -| File | Responsibility | -|------|---------------| -| `airavata-thrift-server/pom.xml` | Module POM — depends on airavata-api, libthrift, MapStruct | -| `airavata-thrift-server/src/main/thrift/**` | Moved from `thrift-interface-descriptions/` — all IDL files | -| `airavata-thrift-server/src/main/java/org/apache/airavata/api/server/AiravataServer.java` | Moved — thrift server bootstrap | -| `airavata-thrift-server/src/main/java/org/apache/airavata/api/server/util/Constants.java` | Moved — server config constants | -| `airavata-thrift-server/src/main/java/org/apache/airavata/api/server/handler/AiravataServerHandler.java` | Moved — main API thrift handler | -| `airavata-thrift-server/src/main/java/org/apache/airavata/api/server/handler/ThriftAdapter.java` | Moved — thrift error translation | -| `airavata-thrift-server/src/main/java/org/apache/airavata/service/profile/handlers/UserProfileServiceHandler.java` | Moved | -| `airavata-thrift-server/src/main/java/org/apache/airavata/service/profile/handlers/TenantProfileServiceHandler.java` | Moved | -| `airavata-thrift-server/src/main/java/org/apache/airavata/service/profile/handlers/IamAdminServicesHandler.java` | Moved | -| `airavata-thrift-server/src/main/java/org/apache/airavata/service/profile/handlers/GroupManagerServiceHandler.java` | Moved | -| `airavata-thrift-server/src/main/java/org/apache/airavata/orchestrator/server/OrchestratorServerHandler.java` | Moved | - -### New files (airavata-api — proto) - -| File | Responsibility | -|------|---------------| -| `airavata-api/src/main/proto/org/apache/airavata/model/workspace/workspace.proto` | Proto mirror of workspace_model.thrift | -| `airavata-api/src/main/proto/org/apache/airavata/model/experiment/experiment.proto` | Proto mirror of experiment_model.thrift | -| `airavata-api/src/main/proto/org/apache/airavata/model/status/status.proto` | Proto mirror of status_models.thrift | -| ... (one proto per thrift data-model file — 28 total) | Mechanical 1:1 translation | -| `airavata-api/src/main/proto/org/apache/airavata/model/security/security.proto` | Proto mirror of security_model.thrift | -| `airavata-api/src/main/proto/org/apache/airavata/model/error/errors.proto` | Proto mirror of airavata_errors.thrift | -| `airavata-api/src/main/proto/org/apache/airavata/model/commons/commons.proto` | Proto mirror of airavata_commons.thrift | - -### Modified files - -| File | Change | -|------|--------| -| `pom.xml` (root) | Add `airavata-thrift-server` module before `airavata-api` in module order | -| `airavata-api/pom.xml` | Update thrift compiler path, add protobuf-java + protobuf-maven-plugin, add MapStruct deps | - -### Deleted files (from airavata-api) - -| File | Reason | -|------|--------| -| `airavata-api/src/main/java/org/apache/airavata/api/server/AiravataServer.java` | Moved | -| `airavata-api/src/main/java/org/apache/airavata/api/server/util/Constants.java` | Moved | -| `airavata-api/src/main/java/org/apache/airavata/api/server/handler/AiravataServerHandler.java` | Moved | -| `airavata-api/src/main/java/org/apache/airavata/api/server/handler/ThriftAdapter.java` | Moved | -| `airavata-api/src/main/java/org/apache/airavata/service/profile/handlers/UserProfileServiceHandler.java` | Moved | -| `airavata-api/src/main/java/org/apache/airavata/service/profile/handlers/TenantProfileServiceHandler.java` | Moved | -| `airavata-api/src/main/java/org/apache/airavata/service/profile/handlers/IamAdminServicesHandler.java` | Moved | -| `airavata-api/src/main/java/org/apache/airavata/service/profile/handlers/GroupManagerServiceHandler.java` | Moved | -| `airavata-api/src/main/java/org/apache/airavata/orchestrator/server/OrchestratorServerHandler.java` | Moved | -| `thrift-interface-descriptions/` | Moved to airavata-thrift-server/src/main/thrift/ | - ---- - -### Task 1: Create git worktree - -**Files:** -- None (git operation) - -- [ ] **Step 1: Create worktree branch** - -```bash -cd /Users/yasith/code/artisan/airavata -git worktree add ../airavata-thrift-extraction -b feat/thrift-server-extraction -``` - -- [ ] **Step 2: Verify worktree** - -```bash -cd /Users/yasith/code/artisan/airavata-thrift-extraction -git branch --show-current -``` - -Expected: `feat/thrift-server-extraction` - -All subsequent tasks run inside `/Users/yasith/code/artisan/airavata-thrift-extraction/`. - ---- - -### Task 2: Create airavata-thrift-server Maven module - -**Files:** -- Create: `airavata-thrift-server/pom.xml` -- Modify: `pom.xml` (root) - -- [ ] **Step 1: Create module directory** - -```bash -mkdir -p airavata-thrift-server/src/main/java -``` - -- [ ] **Step 2: Write airavata-thrift-server/pom.xml** - -```xml -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - - <modelVersion>4.0.0</modelVersion> - - <parent> - <groupId>org.apache.airavata</groupId> - <artifactId>airavata</artifactId> - <version>0.21-SNAPSHOT</version> - <relativePath>../pom.xml</relativePath> - </parent> - - <artifactId>airavata-thrift-server</artifactId> - <packaging>jar</packaging> - <name>Airavata Thrift Server</name> - - <dependencies> - <dependency> - <groupId>org.apache.airavata</groupId> - <artifactId>airavata-api</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>org.apache.thrift</groupId> - <artifactId>libthrift</artifactId> - </dependency> - <dependency> - <groupId>org.slf4j</groupId> - <artifactId>slf4j-api</artifactId> - </dependency> - </dependencies> -</project> -``` - -- [ ] **Step 3: Add module to root pom.xml** - -In `pom.xml`, add `airavata-thrift-server` AFTER `airavata-api` in the `<modules>` block (it depends on airavata-api): - -```xml - <modules> - <module>airavata-api</module> - <module>airavata-thrift-server</module> - <module>modules/file-server</module> - <module>modules/agent-framework/agent-service</module> - <module>modules/research-framework/research-service</module> - <module>modules/restproxy</module> - <module>modules/ide-integration</module> - </modules> -``` - -- [ ] **Step 4: Verify module resolves** - -```bash -mvn validate -pl airavata-thrift-server -am --quiet -``` - -Expected: BUILD SUCCESS (no compilation yet, just POM validation) - -- [ ] **Step 5: Commit** - -```bash -git add airavata-thrift-server/pom.xml pom.xml -git commit -m "feat: add airavata-thrift-server Maven module scaffold" -``` - ---- - -### Task 3: Move thrift IDL files - -**Files:** -- Move: `thrift-interface-descriptions/` → `airavata-thrift-server/src/main/thrift/` -- Modify: `airavata-api/pom.xml` (update thrift compiler path) - -- [ ] **Step 1: Move the directory** - -```bash -git mv thrift-interface-descriptions airavata-thrift-server/src/main/thrift -``` - -- [ ] **Step 2: Update thrift compiler path in airavata-api/pom.xml** - -In `airavata-api/pom.xml`, find the `maven-antrun-plugin` execution `generate-thrift-sources` and change the thrift input path from: - -```xml -<arg value="${project.basedir}/../thrift-interface-descriptions/stubs_java.thrift" /> -``` - -to: - -```xml -<arg value="${project.basedir}/../airavata-thrift-server/src/main/thrift/stubs_java.thrift" /> -``` - -- [ ] **Step 3: Verify thrift compilation still works** - -```bash -mvn generate-sources -pl airavata-api --quiet -ls airavata-api/target/generated-sources/thrift/org/apache/airavata/api/Airavata.java -``` - -Expected: file exists (thrift compiler found the moved IDL and generated code) - -- [ ] **Step 4: Commit** - -```bash -git add -A -git commit -m "refactor: move thrift IDL to airavata-thrift-server/src/main/thrift" -``` - ---- - -### Task 4: Move server bootstrap to airavata-thrift-server - -**Files:** -- Move: `airavata-api/src/main/java/org/apache/airavata/api/server/AiravataServer.java` → `airavata-thrift-server/src/main/java/org/apache/airavata/api/server/AiravataServer.java` -- Move: `airavata-api/src/main/java/org/apache/airavata/api/server/util/Constants.java` → `airavata-thrift-server/src/main/java/org/apache/airavata/api/server/util/Constants.java` - -- [ ] **Step 1: Create target directories** - -```bash -mkdir -p airavata-thrift-server/src/main/java/org/apache/airavata/api/server/util -``` - -- [ ] **Step 2: Move AiravataServer.java** - -```bash -git mv airavata-api/src/main/java/org/apache/airavata/api/server/AiravataServer.java \ - airavata-thrift-server/src/main/java/org/apache/airavata/api/server/AiravataServer.java -``` - -- [ ] **Step 3: Move Constants.java** - -```bash -git mv airavata-api/src/main/java/org/apache/airavata/api/server/util/Constants.java \ - airavata-thrift-server/src/main/java/org/apache/airavata/api/server/util/Constants.java -``` - -Note: `Constants.java` contains `API_SERVER_PORT`, `API_SERVER_HOST`, `API_SERVER_MIN_THREADS` — used only by `AiravataServer.java`. The `ThriftAdapter.java` imports `org.apache.airavata.common.utils.Constants` (a different class in commons), NOT this one. - -- [ ] **Step 4: Commit** - -```bash -git add -A -git commit -m "refactor: move AiravataServer and Constants to airavata-thrift-server" -``` - ---- - -### Task 5: Move AiravataServerHandler and ThriftAdapter - -**Files:** -- Move: `airavata-api/src/main/java/org/apache/airavata/api/server/handler/AiravataServerHandler.java` → `airavata-thrift-server/...` -- Move: `airavata-api/src/main/java/org/apache/airavata/api/server/handler/ThriftAdapter.java` → `airavata-thrift-server/...` - -- [ ] **Step 1: Create target directory** - -```bash -mkdir -p airavata-thrift-server/src/main/java/org/apache/airavata/api/server/handler -``` - -- [ ] **Step 2: Move both files** - -```bash -git mv airavata-api/src/main/java/org/apache/airavata/api/server/handler/AiravataServerHandler.java \ - airavata-thrift-server/src/main/java/org/apache/airavata/api/server/handler/AiravataServerHandler.java - -git mv airavata-api/src/main/java/org/apache/airavata/api/server/handler/ThriftAdapter.java \ - airavata-thrift-server/src/main/java/org/apache/airavata/api/server/handler/ThriftAdapter.java -``` - -No import changes needed — packages stay the same, and `airavata-thrift-server` depends on `airavata-api` which provides all service classes and handler classes they reference. - -- [ ] **Step 3: Commit** - -```bash -git add -A -git commit -m "refactor: move AiravataServerHandler and ThriftAdapter to airavata-thrift-server" -``` - ---- - -### Task 6: Move profile handlers and OrchestratorServerHandler - -**Files:** -- Move: `airavata-api/src/main/java/org/apache/airavata/service/profile/handlers/UserProfileServiceHandler.java` -- Move: `airavata-api/src/main/java/org/apache/airavata/service/profile/handlers/TenantProfileServiceHandler.java` -- Move: `airavata-api/src/main/java/org/apache/airavata/service/profile/handlers/IamAdminServicesHandler.java` -- Move: `airavata-api/src/main/java/org/apache/airavata/service/profile/handlers/GroupManagerServiceHandler.java` -- Move: `airavata-api/src/main/java/org/apache/airavata/orchestrator/server/OrchestratorServerHandler.java` - -- [ ] **Step 1: Create target directories** - -```bash -mkdir -p airavata-thrift-server/src/main/java/org/apache/airavata/service/profile/handlers -mkdir -p airavata-thrift-server/src/main/java/org/apache/airavata/orchestrator/server -``` - -- [ ] **Step 2: Move profile handlers** - -```bash -git mv airavata-api/src/main/java/org/apache/airavata/service/profile/handlers/UserProfileServiceHandler.java \ - airavata-thrift-server/src/main/java/org/apache/airavata/service/profile/handlers/UserProfileServiceHandler.java - -git mv airavata-api/src/main/java/org/apache/airavata/service/profile/handlers/TenantProfileServiceHandler.java \ - airavata-thrift-server/src/main/java/org/apache/airavata/service/profile/handlers/TenantProfileServiceHandler.java - -git mv airavata-api/src/main/java/org/apache/airavata/service/profile/handlers/IamAdminServicesHandler.java \ - airavata-thrift-server/src/main/java/org/apache/airavata/service/profile/handlers/IamAdminServicesHandler.java - -git mv airavata-api/src/main/java/org/apache/airavata/service/profile/handlers/GroupManagerServiceHandler.java \ - airavata-thrift-server/src/main/java/org/apache/airavata/service/profile/handlers/GroupManagerServiceHandler.java -``` - -- [ ] **Step 3: Move OrchestratorServerHandler** - -```bash -git mv airavata-api/src/main/java/org/apache/airavata/orchestrator/server/OrchestratorServerHandler.java \ - airavata-thrift-server/src/main/java/org/apache/airavata/orchestrator/server/OrchestratorServerHandler.java -``` - -- [ ] **Step 4: Commit** - -```bash -git add -A -git commit -m "refactor: move profile handlers and OrchestratorServerHandler to airavata-thrift-server" -``` - ---- - -### Task 7: Verify full build compiles - -**Files:** -- Possibly modify: `airavata-thrift-server/pom.xml` (if missing deps surface) - -- [ ] **Step 1: Build airavata-api (should still compile — only lost handler classes it no longer owns)** - -```bash -mvn compile -pl airavata-api --quiet 2>&1 | tail -5 -``` - -Expected: BUILD SUCCESS. If any code in `airavata-api` still imports the moved handler classes, identify the files and update imports or remove dead references. - -- [ ] **Step 2: Build airavata-thrift-server** - -```bash -mvn compile -pl airavata-thrift-server -am 2>&1 | tail -20 -``` - -Expected: BUILD SUCCESS. If compilation fails due to missing dependencies, add them to `airavata-thrift-server/pom.xml`. Likely candidates: -- Profile service CPI constants (thrift generated — already available via airavata-api dependency) -- Helix/ZooKeeper classes used by OrchestratorServerHandler -- Messaging classes used by AiravataServerHandler -- Monitoring/background service classes used by AiravataServer - -For each missing dependency, add it to `airavata-thrift-server/pom.xml`: - -```xml -<!-- Add as needed based on compilation errors --> -<dependency> - <groupId>org.apache.helix</groupId> - <artifactId>helix-core</artifactId> -</dependency> -<dependency> - <groupId>io.prometheus</groupId> - <artifactId>simpleclient</artifactId> -</dependency> -<!-- etc. --> -``` - -- [ ] **Step 3: Fix any compilation errors** - -Iterate: read error → add dependency or fix import → rebuild. Continue until BUILD SUCCESS for both modules. - -- [ ] **Step 4: Run tests** - -```bash -mvn test -pl airavata-api --quiet 2>&1 | tail -5 -``` - -Expected: Tests pass (no behavioral changes, only file moves). - -- [ ] **Step 5: Commit** - -```bash -git add -A -git commit -m "fix: resolve airavata-thrift-server compilation dependencies" -``` - ---- - -### Task 8: Create proto files — workspace model (representative example) - -**Files:** -- Create: `airavata-api/src/main/proto/org/apache/airavata/model/workspace/workspace.proto` - -This task demonstrates the proto translation pattern. All subsequent proto files follow the same rules. - -**Translation rules (thrift → proto):** -- `namespace java X` → `option java_package = "X"; option java_outer_classname = "XProtos";` -- `struct` → `message` -- `enum` → `enum` (add `UNKNOWN = 0` sentinel as proto3 requires 0-value) -- `required/optional` qualifiers are dropped (proto3 has no required/optional) -- Field numbers match thrift field IDs -- `string` → `string`, `i32` → `int32`, `i64` → `int64`, `bool` → `bool`, `double` → `double`, `binary` → `bytes` -- `list<T>` → `repeated T` -- `map<K,V>` → `map<K,V>` -- `set<T>` → `repeated T` (proto has no set; enforce uniqueness in application code) -- Cross-file references: use `import` and fully qualified message names - -- [ ] **Step 1: Create directory** - -```bash -mkdir -p airavata-api/src/main/proto/org/apache/airavata/model/workspace -``` - -- [ ] **Step 2: Write workspace.proto** - -```protobuf -syntax = "proto3"; - -package org.apache.airavata.model.workspace; - -option java_package = "org.apache.airavata.model.workspace"; -option java_outer_classname = "WorkspaceProtos"; -option java_multiple_files = true; - -import "org/apache/airavata/model/commons/commons.proto"; - -message Group { - string group_name = 1; - string description = 2; -} - -message Project { - string project_id = 1; - string owner = 2; - string gateway_id = 3; - string name = 4; - string description = 5; - int64 creation_time = 6; - repeated string shared_users = 7; - repeated string shared_groups = 8; -} - -message User { - string airavata_internal_user_id = 1; - string user_name = 2; - string gateway_id = 3; - string first_name = 4; - string last_name = 5; - string email = 6; -} - -enum GatewayApprovalStatus { - GATEWAY_APPROVAL_STATUS_UNKNOWN = 0; - REQUESTED = 1; - APPROVED = 2; - ACTIVE = 3; - DEACTIVATED = 4; - CANCELLED = 5; - DENIED = 6; - CREATED = 7; - DEPLOYED = 8; -} - -message Gateway { - string airavata_internal_gateway_id = 1; - string gateway_id = 2; - GatewayApprovalStatus gateway_approval_status = 3; - string gateway_name = 4; - string domain = 5; - string email_address = 6; - string gateway_acronym = 7; - string gateway_url = 8; - string gateway_public_abstract = 9; - string review_proposal_description = 10; - string gateway_admin_first_name = 11; - string gateway_admin_last_name = 12; - string gateway_admin_email = 13; - string identity_server_user_name = 14; - string identity_server_password_token = 15; - string declined_reason = 16; - string oauth_client_id = 17; - string oauth_client_secret = 18; - int64 request_creation_time = 19; - string requester_username = 20; -} - -message GatewayUsageReportingCommand { - string gateway_id = 1; - string compute_resource_id = 2; - string command = 3; -} - -enum NotificationPriority { - NOTIFICATION_PRIORITY_UNKNOWN = 0; - LOW = 1; - NORMAL = 2; - HIGH = 3; -} - -message Notification { - string notification_id = 1; - string gateway_id = 2; - string title = 3; - string notification_message = 4; - int64 creation_time = 5; - int64 published_time = 6; - int64 expiration_time = 7; - NotificationPriority priority = 8; -} -``` - -- [ ] **Step 3: Commit** - -```bash -git add airavata-api/src/main/proto/ -git commit -m "feat: add workspace proto definitions" -``` - ---- - -### Task 9: Create proto files — commons and security - -**Files:** -- Create: `airavata-api/src/main/proto/org/apache/airavata/model/commons/commons.proto` -- Create: `airavata-api/src/main/proto/org/apache/airavata/model/security/security.proto` - -These are foundational protos referenced by many others. - -- [ ] **Step 1: Create directories** - -```bash -mkdir -p airavata-api/src/main/proto/org/apache/airavata/model/commons -mkdir -p airavata-api/src/main/proto/org/apache/airavata/model/security -``` - -- [ ] **Step 2: Write commons.proto** - -Read `airavata-thrift-server/src/main/thrift/airavata-apis/airavata_commons.thrift` to get the exact constants and types. Translate to: - -```protobuf -syntax = "proto3"; - -package org.apache.airavata.model.commons; - -option java_package = "org.apache.airavata.model.commons"; -option java_outer_classname = "CommonsProtos"; -option java_multiple_files = true; - -// DEFAULT_ID is a compile-time constant in thrift; in proto it becomes a convention. -// Consumers should use "DO_NOT_SET_AT_CLIENTS" as the default for ID fields. - -message ValidationResults { - bool validation_state = 1; - repeated ValidationResult validation_result_list = 2; -} - -message ValidationResult { - bool result = 1; - string error_details = 2; -} -``` - -- [ ] **Step 3: Write security.proto** - -Read `airavata-thrift-server/src/main/thrift/airavata-apis/security_model.thrift` and translate: - -```protobuf -syntax = "proto3"; - -package org.apache.airavata.model.security; - -option java_package = "org.apache.airavata.model.security"; -option java_outer_classname = "SecurityProtos"; -option java_multiple_files = true; - -message AuthzToken { - string access_token = 1; - map<string, string> claims_map = 2; -} -``` - -- [ ] **Step 4: Commit** - -```bash -git add airavata-api/src/main/proto/ -git commit -m "feat: add commons and security proto definitions" -``` - ---- - -### Task 10: Create proto files — status and experiment models - -**Files:** -- Create: `airavata-api/src/main/proto/org/apache/airavata/model/status/status.proto` -- Create: `airavata-api/src/main/proto/org/apache/airavata/model/experiment/experiment.proto` - -- [ ] **Step 1: Create directories** - -```bash -mkdir -p airavata-api/src/main/proto/org/apache/airavata/model/status -mkdir -p airavata-api/src/main/proto/org/apache/airavata/model/experiment -``` - -- [ ] **Step 2: Write status.proto** - -Read `airavata-thrift-server/src/main/thrift/data-models/status_models.thrift` and translate all enums/structs. Example structure: - -```protobuf -syntax = "proto3"; - -package org.apache.airavata.model.status; - -option java_package = "org.apache.airavata.model.status"; -option java_outer_classname = "StatusProtos"; -option java_multiple_files = true; - -enum ExperimentState { - EXPERIMENT_STATE_UNKNOWN = 0; - CREATED = 1; - VALIDATED = 2; - SCHEDULED = 3; - LAUNCHED = 4; - EXECUTING = 5; - CANCELING = 6; - CANCELED = 7; - COMPLETED = 8; - FAILED = 9; -} - -message ExperimentStatus { - ExperimentState state = 1; - int64 time_of_state_change = 2; - string reason = 3; - int64 status_id = 4; -} - -enum JobState { - JOB_STATE_UNKNOWN = 0; - SUBMITTED = 1; - QUEUED = 2; - ACTIVE = 3; - COMPLETE = 4; - CANCELED = 5; - FAILED = 6; - SUSPENDED = 7; - NON_CRITICAL_FAIL = 8; -} - -message JobStatus { - JobState job_state = 1; - int64 time_of_state_change = 2; - string reason = 3; - int64 status_id = 4; -} - -enum ProcessState { - PROCESS_STATE_UNKNOWN = 0; - CREATED = 1; - VALIDATED = 2; - STARTED = 3; - PRE_PROCESSING = 4; - CONFIGURING_WORKSPACE = 5; - INPUT_DATA_STAGING = 6; - EXECUTING = 7; - MONITORING = 8; - OUTPUT_DATA_STAGING = 9; - POST_PROCESSING = 10; - COMPLETED = 11; - FAILED = 12; - CANCELLING = 13; - CANCELED = 14; -} - -message ProcessStatus { - ProcessState state = 1; - int64 time_of_state_change = 2; - string reason = 3; - int64 status_id = 4; -} - -enum TaskState { - TASK_STATE_UNKNOWN = 0; - CREATED = 1; - EXECUTING = 2; - COMPLETED = 3; - FAILED = 4; - CANCELED = 5; -} - -message TaskStatus { - TaskState state = 1; - int64 time_of_state_change = 2; - string reason = 3; - int64 status_id = 4; -} - -message QueueStatusModel { - string host_name = 1; - string queue_name = 2; - bool queue_up = 3; - int32 running_jobs = 4; - int32 queued_jobs = 5; - int64 time = 6; -} -``` - -- [ ] **Step 3: Write experiment.proto** - -Read `airavata-thrift-server/src/main/thrift/data-models/experiment_model.thrift` and translate. Use imports for cross-references: - -```protobuf -syntax = "proto3"; - -package org.apache.airavata.model.experiment; - -option java_package = "org.apache.airavata.model.experiment"; -option java_outer_classname = "ExperimentProtos"; -option java_multiple_files = true; - -import "org/apache/airavata/model/commons/commons.proto"; -import "org/apache/airavata/model/status/status.proto"; -// import other model protos as needed for cross-references - -enum ExperimentType { - EXPERIMENT_TYPE_UNKNOWN = 0; - SINGLE_APPLICATION = 1; - WORKFLOW = 2; -} - -enum ExperimentSearchFields { - EXPERIMENT_SEARCH_FIELDS_UNKNOWN = 0; - EXPERIMENT_NAME = 1; - EXPERIMENT_DESC = 2; - APPLICATION_ID = 3; - FROM_DATE = 4; - TO_DATE = 5; - STATUS = 6; - PROJECT_ID = 7; - USER_NAME = 8; - JOB_ID = 9; -} - -enum ProjectSearchFields { - PROJECT_SEARCH_FIELDS_UNKNOWN = 0; - PROJECT_NAME = 1; - PROJECT_DESCRIPTION = 2; -} - -// Continue translating all structs from experiment_model.thrift: -// UserConfigurationDataModel, ExperimentModel, ExperimentSummaryModel, ExperimentStatistics -// Follow the same field-number and naming conventions as workspace.proto -``` - -- [ ] **Step 4: Commit** - -```bash -git add airavata-api/src/main/proto/ -git commit -m "feat: add status and experiment proto definitions" -``` - ---- - -### Task 11: Create remaining proto files - -**Files:** -- Create: one `.proto` file per thrift data-model file (approximately 25 more) - -This is mechanical translation work. For each thrift file in `airavata-thrift-server/src/main/thrift/data-models/`, create a corresponding proto file following the exact pattern from Tasks 8-10. - -- [ ] **Step 1: Create all remaining proto files** - -Complete checklist of thrift files to translate (check off each): - -| Thrift File | Proto Output Path | -|-------------|------------------| -| `account_provisioning_model.thrift` | `org/apache/airavata/model/appcatalog/accountprovisioning/account_provisioning.proto` | -| `application_deployment_model.thrift` | `org/apache/airavata/model/appcatalog/appdeployment/app_deployment.proto` | -| `application_interface_model.thrift` | `org/apache/airavata/model/appcatalog/appinterface/app_interface.proto` | -| `application_io_models.thrift` | `org/apache/airavata/model/application/io/application_io.proto` | -| `compute_resource_model.thrift` | `org/apache/airavata/model/appcatalog/computeresource/compute_resource.proto` | -| `credential_store_models.thrift` | `org/apache/airavata/model/credential/store/credential_store.proto` | -| `data_movement_models.thrift` | `org/apache/airavata/model/data/movement/data_movement.proto` | -| `gateway_groups_model.thrift` | `org/apache/airavata/model/appcatalog/gatewaygroups/gateway_groups.proto` | -| `gateway_resource_profile_model.thrift` | `org/apache/airavata/model/appcatalog/gatewayprofile/gateway_profile.proto` | -| `group_manager_model.thrift` | `org/apache/airavata/model/group/group_manager.proto` | -| `group_resource_profile_model.thrift` | `org/apache/airavata/model/appcatalog/groupresourceprofile/group_resource_profile.proto` | -| `job_model.thrift` | `org/apache/airavata/model/job/job.proto` | -| `parallelism_model.thrift` | `org/apache/airavata/model/parallelism/parallelism.proto` | -| `parser_model.thrift` | `org/apache/airavata/model/appcatalog/parser/parser.proto` | -| `process_model.thrift` | `org/apache/airavata/model/process/process.proto` | -| `replica_catalog_models.thrift` | `org/apache/airavata/model/data/replica/replica_catalog.proto` | -| `scheduling_model.thrift` | `org/apache/airavata/model/scheduling/scheduling.proto` | -| `sharing_models.thrift` | `org/apache/airavata/model/sharing/sharing.proto` | -| `storage_resource_model.thrift` | `org/apache/airavata/model/appcatalog/storageresource/storage_resource.proto` | -| `task_model.thrift` | `org/apache/airavata/model/task/task.proto` | -| `tenant_profile_model.thrift` | `org/apache/airavata/model/tenant/tenant_profile.proto` | -| `user_profile_model.thrift` | `org/apache/airavata/model/user/user_profile.proto` | -| `user_resource_profile_model.thrift` | `org/apache/airavata/model/appcatalog/userresourceprofile/user_resource_profile.proto` | -| `workflow_data_model.thrift` | `org/apache/airavata/model/workflow/data/workflow_data.proto` | -| `workflow_model.thrift` | `org/apache/airavata/model/workflow/workflow.proto` | - -Also translate API-level thrift files: - -| Thrift File | Proto Output Path | -|-------------|------------------| -| `airavata_errors.thrift` | `org/apache/airavata/model/error/errors.proto` | -| `db_event_model.thrift` | `org/apache/airavata/model/dbevent/db_event.proto` | -| `messaging_events.thrift` | `org/apache/airavata/model/messaging/messaging_events.proto` | - -For each file: -1. Read the thrift file -2. Create the proto file following translation rules from Task 8 -3. Match field numbers to thrift field IDs -4. Add `UNKNOWN = 0` sentinel to every enum -5. Use `import` for cross-file references -6. Set `java_package` matching the thrift `namespace java` - -- [ ] **Step 2: Commit** - -```bash -git add airavata-api/src/main/proto/ -git commit -m "feat: add all remaining proto definitions mirroring thrift IDL" -``` - ---- - -### Task 12: Set up protoc compilation in airavata-api - -**Files:** -- Modify: `airavata-api/pom.xml` - -- [ ] **Step 1: Add protobuf-java dependency** - -In `airavata-api/pom.xml` `<dependencies>` section, add: - -```xml - <dependency> - <groupId>com.google.protobuf</groupId> - <artifactId>protobuf-java</artifactId> - </dependency> -``` - -- [ ] **Step 2: Add protobuf-maven-plugin** - -In `airavata-api/pom.xml` `<build><plugins>` section, add the protobuf-maven-plugin (already in root POM's pluginManagement): - -```xml - <plugin> - <groupId>io.github.ascopes</groupId> - <artifactId>protobuf-maven-plugin</artifactId> - <executions> - <execution> - <id>generate-proto-sources</id> - <goals> - <goal>generate</goal> - </goals> - <configuration> - <sourceDirectories> - <sourceDirectory>${project.basedir}/src/main/proto</sourceDirectory> - </sourceDirectories> - </configuration> - </execution> - </executions> - </plugin> -``` - -- [ ] **Step 3: Verify proto compilation** - -```bash -mvn generate-sources -pl airavata-api --quiet 2>&1 | tail -10 -ls airavata-api/target/generated-sources/protobuf/java/org/apache/airavata/model/workspace/ -``` - -Expected: generated Java files for the proto messages (Project.java, Gateway.java, etc.) - -- [ ] **Step 4: Full compile** - -```bash -mvn compile -pl airavata-api --quiet 2>&1 | tail -5 -``` - -Expected: BUILD SUCCESS — both thrift generated code and proto generated code compile. - -- [ ] **Step 5: Commit** - -```bash -git add airavata-api/pom.xml -git commit -m "build: add protobuf-java dependency and protoc compilation to airavata-api" -``` - ---- - -### Task 13: Add MapStruct to airavata-thrift-server - -**Files:** -- Modify: `airavata-thrift-server/pom.xml` -- Modify: `pom.xml` (root — add MapStruct to dependency management) - -- [ ] **Step 1: Add MapStruct to root pom.xml dependency management** - -In `pom.xml` root, add to `<dependencyManagement><dependencies>`: - -```xml - <dependency> - <groupId>org.mapstruct</groupId> - <artifactId>mapstruct</artifactId> - <version>1.6.3</version> - </dependency> - <dependency> - <groupId>org.mapstruct</groupId> - <artifactId>mapstruct-processor</artifactId> - <version>1.6.3</version> - </dependency> -``` - -- [ ] **Step 2: Add MapStruct dependency to airavata-thrift-server/pom.xml** - -Add to `<dependencies>`: - -```xml - <dependency> - <groupId>org.mapstruct</groupId> - <artifactId>mapstruct</artifactId> - </dependency> -``` - -Add to `<build><plugins>`: - -```xml - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <configuration> - <annotationProcessorPaths> - <path> - <groupId>org.mapstruct</groupId> - <artifactId>mapstruct-processor</artifactId> - <version>1.6.3</version> - </path> - </annotationProcessorPaths> - </configuration> - </plugin> - </plugins> - </build> -``` - -- [ ] **Step 3: Verify build** - -```bash -mvn compile -pl airavata-thrift-server -am --quiet 2>&1 | tail -5 -``` - -Expected: BUILD SUCCESS - -- [ ] **Step 4: Commit** - -```bash -git add pom.xml airavata-thrift-server/pom.xml -git commit -m "build: add MapStruct dependency to airavata-thrift-server" -``` - ---- - -### Task 14: Create representative MapStruct mapper - -**Files:** -- Create: `airavata-thrift-server/src/main/java/org/apache/airavata/api/server/mapper/WorkspaceMapper.java` - -This establishes the mapper pattern. Full mapper coverage is built incrementally as services are converted to proto types (follow-up work). - -- [ ] **Step 1: Create mapper directory** - -```bash -mkdir -p airavata-thrift-server/src/main/java/org/apache/airavata/api/server/mapper -``` - -- [ ] **Step 2: Write WorkspaceMapper.java** - -```java -package org.apache.airavata.api.server.mapper; - -import org.apache.airavata.model.workspace.Gateway; -import org.apache.airavata.model.workspace.Notification; -import org.apache.airavata.model.workspace.Project; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.factory.Mappers; - -/** - * Maps between thrift-generated workspace types and proto-generated workspace types. - * - * <p>Thrift types use camelCase field accessors (getGatewayId()). - * Proto types use snake_case with standard proto getters (getGatewayId()). - * MapStruct handles the mapping automatically when accessor names match. - */ -@Mapper -public interface WorkspaceMapper { - WorkspaceMapper INSTANCE = Mappers.getMapper(WorkspaceMapper.class); - - // --- Project --- - org.apache.airavata.model.workspace.ProjectProtos.Project toProto(Project thrift); - Project toThrift(org.apache.airavata.model.workspace.ProjectProtos.Project proto); - - // --- Gateway --- - org.apache.airavata.model.workspace.WorkspaceProtos.Gateway toProtoGateway(Gateway thrift); - Gateway toThriftGateway(org.apache.airavata.model.workspace.WorkspaceProtos.Gateway proto); -} -``` - -Note: The exact proto class names depend on how protoc generates them (based on `java_multiple_files`, `java_outer_classname`). Adjust class references after verifying proto compilation output from Task 12. - -- [ ] **Step 3: Verify compilation** - -```bash -mvn compile -pl airavata-thrift-server -am --quiet 2>&1 | tail -10 -``` - -If proto class names don't match, check `airavata-api/target/generated-sources/` for exact class names and update mapper imports. - -- [ ] **Step 4: Commit** - -```bash -git add airavata-thrift-server/src/main/java/org/apache/airavata/api/server/mapper/ -git commit -m "feat: add representative MapStruct mapper for workspace types" -``` - ---- - -### Task 15: Final build verification and cleanup - -**Files:** -- None (verification only) - -- [ ] **Step 1: Full project build** - -```bash -mvn clean compile -T4 2>&1 | tail -20 -``` - -Expected: BUILD SUCCESS for all modules - -- [ ] **Step 2: Run tests** - -```bash -mvn test -pl airavata-api,airavata-thrift-server 2>&1 | tail -20 -``` - -Expected: All tests pass - -- [ ] **Step 3: Verify no remaining handler references in airavata-api** - -```bash -grep -r "AiravataServer\b" airavata-api/src/main/java/ --include="*.java" | grep -v "import" -grep -r "AiravataServerHandler\b" airavata-api/src/main/java/ --include="*.java" | grep -v "import" -``` - -Expected: No matches (these classes have moved). If there are stale references, remove them. - -- [ ] **Step 4: Commit** - -```bash -git add -A -git commit -m "chore: final cleanup after thrift server extraction" -``` - ---- - -## Deferred Work (Future Plans) - -These items are explicitly out of scope for this plan but are required for full thrift removal from `airavata-api`: - -1. **Extract data access from Registry/Sharing/Credential handlers** — Create non-thrift interfaces in `airavata-api`, refactor services to use them, move handler implementations to `airavata-thrift-server` -2. **Convert service signatures to proto types** — Change all 14 service classes to accept/return proto types instead of thrift types -3. **Move thrift code generation to airavata-thrift-server** — Once `airavata-api` no longer imports any thrift types -4. **Remove libthrift dependency from airavata-api** — Final step after all thrift references are eliminated -5. **Full MapStruct mapper coverage** — One mapper per domain (experiment, credential, sharing, etc.) diff --git a/docs/superpowers/specs/2026-03-26-airavata-thrift-server-extraction-design.md b/docs/superpowers/specs/2026-03-26-airavata-thrift-server-extraction-design.md deleted file mode 100644 index c792b0596e..0000000000 --- a/docs/superpowers/specs/2026-03-26-airavata-thrift-server-extraction-design.md +++ /dev/null @@ -1,190 +0,0 @@ -# Airavata Thrift Server Extraction Design - -**Date:** 2026-03-26 -**Status:** Draft - -## Goal - -Extract all thrift server concerns from `airavata-api` into a new `airavata-thrift-server` Maven module. Introduce protobuf DTOs as the canonical data types in the service layer, making `airavata-api` completely thrift-free. MapStruct mappers in `airavata-thrift-server` bridge proto and thrift types at the handler boundary. - -## Repository Layout (Post-Extraction) - -``` -airavata/ -├── airavata-thrift-server/ # NEW — all thrift concerns -│ ├── pom.xml -│ └── src/main/ -│ ├── java/ -│ │ └── org/apache/airavata/ -│ │ ├── server/ # AiravataServer.java (TMultiplexedProcessor bootstrap) -│ │ ├── handler/ # All 9 thrift handlers -│ │ └── mapper/ # MapStruct mappers (proto <-> thrift) -│ └── thrift/ # Moved from thrift-interface-descriptions/ -│ ├── airavata-apis/ -│ ├── base-api/ -│ ├── data-models/ -│ ├── service-cpis/ -│ └── stubs_java.thrift (etc.) -│ -├── airavata-api/ # SLIMMED — services + background processes -│ ├── pom.xml -│ └── src/main/ -│ ├── java/ -│ │ └── org/apache/airavata/ -│ │ ├── service/ # Business logic services -│ │ ├── repository/ # JPA data access -│ │ └── background/ # Helix, monitors, event managers, etc. -│ └── proto/ # NEW — .proto files mirroring thrift IDL -│ └── org/apache/airavata/ -│ ├── model/ -│ ├── api/ -│ ├── registry/ -│ ├── credential/ -│ ├── sharing/ -│ └── ... -│ -├── modules/ # Unchanged -└── pom.xml # Root — adds airavata-thrift-server module -``` - -## Dependency Graph - -``` -airavata-thrift-server -├── depends on: airavata-api (service layer + proto generated classes) -├── depends on: libthrift (Apache Thrift runtime) -├── owns: thrift IDL compilation -> generated thrift Java classes -├── owns: MapStruct mappers (proto <-> thrift) -└── owns: TMultiplexedProcessor, TThreadPoolServer bootstrap - -airavata-api -├── depends on: protobuf-java (for generated proto classes) -├── owns: service layer (o.a.a.service.*) -├── owns: repository / data access layer -├── owns: background services (Helix, monitors, event managers) -├── owns: proto definitions + protoc generation -└── NO thrift dependency -``` - -## Thrift Services Moved (All 9) - -| Service Name | Handler Class | -|---------------------|------------------------------------| -| Airavata | AiravataServerHandler | -| RegistryService | RegistryServerHandler | -| SharingRegistry | SharingRegistryServerHandler | -| CredentialStore | CredentialStoreServerHandler | -| UserProfile | UserProfileServiceHandler | -| TenantProfile | TenantProfileServiceHandler | -| IamAdminServices | IamAdminServicesHandler | -| GroupManager | GroupManagerServiceHandler | -| Orchestrator | OrchestratorServerHandler | - -## What Stays in `airavata-api` - -### Services (refactored to proto types) -All 14 business logic services: ExperimentService, ApplicationCatalogService, CredentialService, GatewayService, GatewayResourceProfileService, GroupResourceProfileService, NotificationService, ParserService, ProjectService, ResourceService, UserResourceProfileService, DataProductService, ResourceSharingService, SSHAccountService. - -### Data Access -JPA entities, repositories, Hibernate config, database initialization (ExpCatalogDB, AppCatalogDB, ReplicaCatalogDB, WorkflowCatalogDB, CredentialStoreDB, UserProfileCatalogDB). - -### Background Services (retains its own main entry point) -- DB Event Manager -- Monitoring Server (Prometheus on port 9097) -- Cluster Status Monitor -- Data Interpreter -- Process Rescheduler -- Helix Controller/Participant -- Workflow Managers (Pre/Post/Parser) -- Job Monitors (Email + Real-time) - -### Removed from `airavata-api` -- `AiravataServer.java` -- All 9 thrift handler classes -- Thrift IDL files -- `libthrift` dependency -- Thrift code generation Maven plugin config - -## Proto File Strategy - -Proto files mirror thrift IDL 1:1 — same fields, same nesting, same enums. Mechanical translation, not a redesign. - -### Conventions -- Field numbers match thrift field IDs where possible -- Proto uses `snake_case` per protobuf convention; MapStruct handles naming differences -- No proto service definitions — only message types (no gRPC yet) -- Package structure mirrors thrift: `org.apache.airavata.model.*`, `org.apache.airavata.api.*`, etc. -- Generated via protoc Maven plugin in `airavata-api` - -### Example - -Thrift: -```thrift -struct ExperimentModel { - 1: string experimentId, - 2: string projectId, - 3: string gatewayId, - 4: ExperimentType experimentType, - 5: string userName, - 6: string experimentName, - 7: i64 creationTime -} -``` - -Proto: -```protobuf -message ExperimentModel { - string experiment_id = 1; - string project_id = 2; - string gateway_id = 3; - ExperimentType experiment_type = 4; - string user_name = 5; - string experiment_name = 6; - int64 creation_time = 7; -} -``` - -## Handler Conversion Pattern - -``` -Thrift Client - -> TMultiplexedProcessor (airavata-thrift-server) - -> ThriftHandler.method(ThriftRequest) - -> MapStruct: ThriftRequest -> ProtoDTO - -> Service.method(ProtoDTO) (airavata-api) - -> returns ProtoResult - -> MapStruct: ProtoResult -> ThriftResponse - -> returns ThriftResponse -``` - -### Example — createExperiment - -```java -// In airavata-thrift-server: AiravataServerHandler -public String createExperiment(AuthzToken authzToken, String gatewayId, ExperimentModel experiment) { - var protoExperiment = ExperimentMapper.INSTANCE.toProto(experiment); - var protoAuthz = AuthzTokenMapper.INSTANCE.toProto(authzToken); - var result = experimentService.createExperiment(protoAuthz, gatewayId, protoExperiment); - return result; -} -``` - -### MapStruct Mapper - -```java -@Mapper -public interface ExperimentMapper { - ExperimentMapper INSTANCE = Mappers.getMapper(ExperimentMapper.class); - ExperimentProto toProto(ExperimentModel thriftModel); - ExperimentModel toThrift(ExperimentProto proto); -} -``` - -## Build Order - -1. `airavata-api` builds first — compiles proto files, produces service classes + proto generated code -2. `airavata-thrift-server` builds second — compiles thrift IDL, compiles handlers + mappers against airavata-api - -## Development - -Work is done in a git worktree off the main `airavata` repo for isolation. Thrift IDL content is unchanged (no SDK breakage). Thrift spec changes are a separate future effort. diff --git a/integration-tests/src/test/java/org/apache/airavata/integration/BackgroundServiceHealthTest.java b/integration-tests/src/test/java/org/apache/airavata/integration/BackgroundServiceHealthTest.java index 14d0aff42d..677f53e8eb 100644 --- a/integration-tests/src/test/java/org/apache/airavata/integration/BackgroundServiceHealthTest.java +++ b/integration-tests/src/test/java/org/apache/airavata/integration/BackgroundServiceHealthTest.java @@ -73,7 +73,9 @@ class BackgroundServiceHealthTest { HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode(), "Could not reach /health/services — got HTTP " + response.statusCode()); - cachedRoot = objectMapper.readTree(response.body()); + JsonNode body = objectMapper.readTree(response.body()); + cachedRoot = body.get("services"); + assertNotNull(cachedRoot, "Expected 'services' key in /health/services response"); } static Stream<String> expectedServices() { diff --git a/integration-tests/src/test/java/org/apache/airavata/integration/ServiceHealthEndpointTest.java b/integration-tests/src/test/java/org/apache/airavata/integration/ServiceHealthEndpointTest.java index 219ff00d35..b9b12048e9 100644 --- a/integration-tests/src/test/java/org/apache/airavata/integration/ServiceHealthEndpointTest.java +++ b/integration-tests/src/test/java/org/apache/airavata/integration/ServiceHealthEndpointTest.java @@ -82,7 +82,9 @@ class ServiceHealthEndpointTest { JsonNode root = objectMapper.readTree(response.body()); assertNotNull(root, "Response body could not be parsed as JSON"); assertTrue(root.isObject(), "Expected JSON object at root, got: " + root.getNodeType()); - assertFalse(root.isEmpty(), "Expected at least one service entry in the response"); + JsonNode services = root.get("services"); + assertNotNull(services, "Expected 'services' key in response, got: " + root); + assertFalse(services.isEmpty(), "Expected at least one service entry in the response"); } @Test @@ -91,7 +93,9 @@ class ServiceHealthEndpointTest { assertEquals(200, response.statusCode()); JsonNode root = objectMapper.readTree(response.body()); - Iterator<Map.Entry<String, JsonNode>> fields = root.fields(); + JsonNode services = root.get("services"); + assertNotNull(services, "Expected 'services' key in response"); + Iterator<Map.Entry<String, JsonNode>> fields = services.fields(); while (fields.hasNext()) { Map.Entry<String, JsonNode> entry = fields.next(); String serviceName = entry.getKey();
