From 3fb0b63e675afaed0937a129f1ee0dffec43ad42 Mon Sep 17 00:00:00 2001 From: Michael Burman Date: Wed, 20 Sep 2023 11:31:06 +0300 Subject: [PATCH 1/2] Correctly serialize and deserialize the statusChanges field --- .../java/com/datastax/mgmtapi/NodeOpsProvider.java | 14 ++++++++++++-- .../com/datastax/mgmtapi/resources/models/Job.java | 14 +++++++++++--- .../datastax/mgmtapi/K8OperatorResourcesTest.java | 8 +++++++- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/management-api-agent-common/src/main/java/com/datastax/mgmtapi/NodeOpsProvider.java b/management-api-agent-common/src/main/java/com/datastax/mgmtapi/NodeOpsProvider.java index eb74e3e9..c2d1ab31 100644 --- a/management-api-agent-common/src/main/java/com/datastax/mgmtapi/NodeOpsProvider.java +++ b/management-api-agent-common/src/main/java/com/datastax/mgmtapi/NodeOpsProvider.java @@ -23,6 +23,7 @@ import com.google.common.base.Preconditions; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; +import com.google.common.collect.Lists; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; @@ -79,8 +80,8 @@ public synchronized void unregister() { } @Rpc(name = "jobStatus") - public Map getJobStatus(@RpcParam(name = "job_id") String jobId) { - Map resultMap = new HashMap<>(); + public Map getJobStatus(@RpcParam(name = "job_id") String jobId) { + Map resultMap = new HashMap<>(); Job jobWithId = service.getJobWithId(jobId); if (jobWithId == null) { return resultMap; @@ -93,6 +94,15 @@ public Map getJobStatus(@RpcParam(name = "job_id") String jobId) if (jobWithId.getStatus() == Job.JobStatus.ERROR) { resultMap.put("error", jobWithId.getError().getLocalizedMessage()); } + List> statusChanges = new ArrayList<>(); + for (Job.StatusChange statusChange : jobWithId.getStatusChanges()) { + statusChanges.add( + Lists.newArrayList( + statusChange.getStatus().name(), + Long.valueOf(statusChange.getChangeTime()).toString(), + statusChange.getMessage())); + } + resultMap.put("status_changes", statusChanges); return resultMap; } diff --git a/management-api-server/src/main/java/com/datastax/mgmtapi/resources/models/Job.java b/management-api-server/src/main/java/com/datastax/mgmtapi/resources/models/Job.java index d2ead968..9c82ac34 100644 --- a/management-api-server/src/main/java/com/datastax/mgmtapi/resources/models/Job.java +++ b/management-api-server/src/main/java/com/datastax/mgmtapi/resources/models/Job.java @@ -6,7 +6,9 @@ package com.datastax.mgmtapi.resources.models; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; import java.io.Serializable; import java.util.List; @@ -35,7 +37,9 @@ public enum JobStatus { @JsonProperty(value = "error") private String error; - public class StatusChange { + @JsonFormat(shape = JsonFormat.Shape.ARRAY) + @JsonPropertyOrder({"status", "change_time", "message"}) + public static class StatusChange { @JsonProperty(value = "status") String status; @@ -45,8 +49,12 @@ public class StatusChange { @JsonProperty(value = "message") String message; - public StatusChange(String type, String message) { - changeTime = System.currentTimeMillis(); + @JsonCreator + public StatusChange( + @JsonProperty(value = "status") String type, + @JsonProperty(value = "change_time") String time, + @JsonProperty(value = "message") String message) { + changeTime = Long.parseLong(time); status = type; this.message = message; } diff --git a/management-api-server/src/test/java/com/datastax/mgmtapi/K8OperatorResourcesTest.java b/management-api-server/src/test/java/com/datastax/mgmtapi/K8OperatorResourcesTest.java index ba3dddfc..f0e1443f 100644 --- a/management-api-server/src/test/java/com/datastax/mgmtapi/K8OperatorResourcesTest.java +++ b/management-api-server/src/test/java/com/datastax/mgmtapi/K8OperatorResourcesTest.java @@ -585,13 +585,17 @@ public void testJobStatus() throws Exception { when(mockResultSet.one()).thenReturn(mockRow); - Map jobDetailsRow = new HashMap<>(); + Map jobDetailsRow = new HashMap<>(); jobDetailsRow.put("id", "0fe65b47-98c2-47d8-9c3c-5810c9988e10"); jobDetailsRow.put("type", "CLEANUP"); jobDetailsRow.put("status", "COMPLETED"); jobDetailsRow.put("submit_time", String.valueOf(System.currentTimeMillis())); jobDetailsRow.put("end_time", String.valueOf(System.currentTimeMillis())); + List> statusChanges = new ArrayList<>(); + statusChanges.add(Lists.newArrayList("SUCCESS", "1695183696663", "No message")); + jobDetailsRow.put("status_changes", statusChanges); + when(mockRow.getObject(0)).thenReturn(jobDetailsRow); MockHttpResponse response = @@ -609,6 +613,8 @@ public void testJobStatus() throws Exception { assertEquals("0fe65b47-98c2-47d8-9c3c-5810c9988e10", jobDetails.getJobId()); assertEquals("COMPLETED", jobDetails.getStatus().toString()); assertEquals("CLEANUP", jobDetails.getJobType()); + assertEquals(1, jobDetails.getStatusChanges().size()); + assertEquals("SUCCESS", jobDetails.getStatusChanges().get(0).getStatus()); } @Test From 216d3df5c9c76853f825bf47a8ff16e3d7ec1f19 Mon Sep 17 00:00:00 2001 From: Michael Burman Date: Wed, 20 Sep 2023 19:51:36 +0300 Subject: [PATCH 2/2] Use escaped JSON instead when returning from getJobStatus --- .../com/datastax/mgmtapi/NodeOpsProvider.java | 30 ++++++++++++------ .../mgmtapi/resources/models/Job.java | 31 +++++++++++++++---- .../mgmtapi/K8OperatorResourcesTest.java | 19 ++++++++++-- 3 files changed, 61 insertions(+), 19 deletions(-) diff --git a/management-api-agent-common/src/main/java/com/datastax/mgmtapi/NodeOpsProvider.java b/management-api-agent-common/src/main/java/com/datastax/mgmtapi/NodeOpsProvider.java index c2d1ab31..efe99c4c 100644 --- a/management-api-agent-common/src/main/java/com/datastax/mgmtapi/NodeOpsProvider.java +++ b/management-api-agent-common/src/main/java/com/datastax/mgmtapi/NodeOpsProvider.java @@ -20,10 +20,12 @@ import com.datastax.oss.driver.api.querybuilder.schema.CreateTableWithOptions; import com.datastax.oss.driver.api.querybuilder.schema.OngoingPartitionKey; import com.datastax.oss.driver.internal.core.metadata.schema.parsing.DataTypeCqlNameParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Preconditions; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; -import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; @@ -80,8 +82,8 @@ public synchronized void unregister() { } @Rpc(name = "jobStatus") - public Map getJobStatus(@RpcParam(name = "job_id") String jobId) { - Map resultMap = new HashMap<>(); + public Map getJobStatus(@RpcParam(name = "job_id") String jobId) { + Map resultMap = new HashMap<>(); Job jobWithId = service.getJobWithId(jobId); if (jobWithId == null) { return resultMap; @@ -94,15 +96,23 @@ public Map getJobStatus(@RpcParam(name = "job_id") String jobId) if (jobWithId.getStatus() == Job.JobStatus.ERROR) { resultMap.put("error", jobWithId.getError().getLocalizedMessage()); } - List> statusChanges = new ArrayList<>(); + + List> statusChanges = new ArrayList<>(); for (Job.StatusChange statusChange : jobWithId.getStatusChanges()) { - statusChanges.add( - Lists.newArrayList( - statusChange.getStatus().name(), - Long.valueOf(statusChange.getChangeTime()).toString(), - statusChange.getMessage())); + Map change = Maps.newHashMap(); + change.put("status", statusChange.getStatus().name()); + change.put("change_time", Long.valueOf(statusChange.getChangeTime()).toString()); + change.put("message", statusChange.getMessage()); + statusChanges.add(change); + } + + ObjectMapper objectMapper = new ObjectMapper(); + try { + String s = objectMapper.writeValueAsString(statusChanges); + resultMap.put("status_changes", s); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); } - resultMap.put("status_changes", statusChanges); return resultMap; } diff --git a/management-api-server/src/main/java/com/datastax/mgmtapi/resources/models/Job.java b/management-api-server/src/main/java/com/datastax/mgmtapi/resources/models/Job.java index 9c82ac34..1a5c99f1 100644 --- a/management-api-server/src/main/java/com/datastax/mgmtapi/resources/models/Job.java +++ b/management-api-server/src/main/java/com/datastax/mgmtapi/resources/models/Job.java @@ -6,10 +6,13 @@ package com.datastax.mgmtapi.resources.models; import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; import java.io.Serializable; +import java.util.ArrayList; import java.util.List; public class Job implements Serializable { @@ -37,8 +40,6 @@ public enum JobStatus { @JsonProperty(value = "error") private String error; - @JsonFormat(shape = JsonFormat.Shape.ARRAY) - @JsonPropertyOrder({"status", "change_time", "message"}) public static class StatusChange { @JsonProperty(value = "status") String status; @@ -83,14 +84,32 @@ public Job( @JsonProperty(value = "submit_time") long submitTime, @JsonProperty(value = "end_time") long finishedTime, @JsonProperty(value = "error") String error, - @JsonProperty(value = "status_changes") List changes) { + @JsonProperty(value = "status_changes") String changes) { this.jobId = jobId; this.jobType = jobType; this.status = JobStatus.valueOf(status); this.submitTime = submitTime; this.finishedTime = finishedTime; this.error = error; - this.statusChanges = changes; + this.statusChanges = changes(changes); + } + + public List changes(String s) { + ObjectMapper objectMapper = new ObjectMapper(); + if (s.length() < 2) { + return new ArrayList<>(); + } + try { + JsonNode parent = objectMapper.readTree(s); + + List changes = + objectMapper.readValue(parent.traverse(), new TypeReference>() {}); + + return changes; + + } catch (IOException e) { + throw new RuntimeException(e); + } } public String getJobId() { diff --git a/management-api-server/src/test/java/com/datastax/mgmtapi/K8OperatorResourcesTest.java b/management-api-server/src/test/java/com/datastax/mgmtapi/K8OperatorResourcesTest.java index f0e1443f..0e78d752 100644 --- a/management-api-server/src/test/java/com/datastax/mgmtapi/K8OperatorResourcesTest.java +++ b/management-api-server/src/test/java/com/datastax/mgmtapi/K8OperatorResourcesTest.java @@ -40,12 +40,14 @@ import com.datastax.oss.driver.api.core.cql.ResultSet; import com.datastax.oss.driver.api.core.cql.Row; import com.datastax.oss.driver.api.core.metadata.schema.ClusteringOrder; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.json.JsonMapper; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; @@ -592,9 +594,20 @@ public void testJobStatus() throws Exception { jobDetailsRow.put("submit_time", String.valueOf(System.currentTimeMillis())); jobDetailsRow.put("end_time", String.valueOf(System.currentTimeMillis())); - List> statusChanges = new ArrayList<>(); - statusChanges.add(Lists.newArrayList("SUCCESS", "1695183696663", "No message")); - jobDetailsRow.put("status_changes", statusChanges); + List> statusChanges = new ArrayList<>(); + Map change = Maps.newHashMap(); + change.put("status", "SUCCESS"); + change.put("change_time", "1695183696663"); + change.put("message", "No message"); + statusChanges.add(change); + + ObjectMapper objectMapper = new ObjectMapper(); + try { + String s = objectMapper.writeValueAsString(statusChanges); + jobDetailsRow.put("status_changes", s); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } when(mockRow.getObject(0)).thenReturn(jobDetailsRow);