Skip to content

Commit c10714c

Browse files
siwei-zhang-workSiwei Zhangbhufmann
authored
swagger: Fix specification for sampling in xy output (#238)
server: Fix SeriesModel xValues swagger definition/serializers/tests Use xValues for array of longs for x values, e.g. timestamps Use xCategoires for array of category strings for x values use XRanges for arrays of range (start/stop) for x values This simplifies the swagger definition and implemntation for xValues of SeriesModel for both server and client side. Update TSP version to 0.6.0 Signed-off-by: Bernd Hufmann <[email protected]> Signed-off-by: Siwei Zhang <[email protected]> Co-authored-by: Siwei Zhang <[email protected]> Co-authored-by: Bernd Hufmann <[email protected]>
1 parent 3e335c3 commit c10714c

File tree

7 files changed

+148
-128
lines changed

7 files changed

+148
-128
lines changed

trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/webapp/SamplingSerializerTest.java

Lines changed: 54 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111

1212
package org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.webapp;
1313

14-
import static org.junit.Assert.*;
14+
import static org.junit.Assert.assertEquals;
1515

16+
import java.io.IOException;
17+
import java.io.Serializable;
1618
import java.util.List;
1719

1820
import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.webapp.SamplingSerializer;
@@ -24,18 +26,22 @@
2426
import org.junit.Before;
2527
import org.junit.Test;
2628

29+
import com.fasterxml.jackson.core.JsonGenerator;
2730
import com.fasterxml.jackson.core.JsonProcessingException;
2831
import com.fasterxml.jackson.databind.ObjectMapper;
32+
import com.fasterxml.jackson.databind.SerializerProvider;
2933
import com.fasterxml.jackson.databind.module.SimpleModule;
34+
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
3035

3136
/**
32-
* Test suite for {@link SamplingSerializer} and {@link SamplingDeserializer}.
37+
* Test suite for {@link SamplingSerializer}.
3338
* <p>
3439
* Validates JSON round-trip behavior for different {@link ISampling}
3540
* subtypes: {@link Timestamps}, {@link Categories}, and {@link Ranges}.
3641
*
3742
* @author Siwei Zhang
3843
*/
44+
@SuppressWarnings("null")
3945
public class SamplingSerializerTest {
4046

4147
private ObjectMapper fMapper;
@@ -49,53 +55,85 @@ public void setup() {
4955
fMapper = new ObjectMapper();
5056
SimpleModule module = new SimpleModule();
5157
module.addSerializer(ISampling.class, new SamplingSerializer());
58+
module.addSerializer(TestSampling.class, new TestSamplingSerializer());
5259
fMapper.registerModule(module);
5360
}
5461

5562
/**
56-
* Test round-trip serialization and deserialization for
57-
* {@link ISampling.Timestamps}. The format is a flat array of @NonNull Longs.
63+
* Test round-trip serialization {@link ISampling.Timestamps}.
64+
* The format is a flat array of @NonNull Longs.
5865
*
5966
* @throws JsonProcessingException
6067
* if JSON processing fails
6168
*/
6269
@Test
6370
public void testTimestampsRoundTrip() throws JsonProcessingException {
64-
ISampling original = new Timestamps(new long[] { 1, 2, 3 });
71+
TestSampling original = new TestSampling(new Timestamps(new long[] { 1, 2, 3 }));
6572
String json = fMapper.writeValueAsString(original);
66-
assertEquals("[1,2,3]", json);
73+
assertEquals("{\"xValues\":[1,2,3]}", json);
6774
}
6875

6976
/**
70-
* Test round-trip serialization and deserialization for
71-
* {@link ISampling.Categories}. The format is an array of strings.
77+
* Test round-trip serialization {@link ISampling.Categories}.
78+
* The format is an array of strings.
7279
*
7380
* @throws JsonProcessingException
7481
* if JSON processing fails
7582
*/
7683
@Test
7784
public void testCategoriesRoundTrip() throws JsonProcessingException {
78-
ISampling original = new Categories(List.of("Read", "Write", "Idle"));
85+
TestSampling original = new TestSampling(new Categories(List.of("Read", "Write", "Idle")));
7986
String json = fMapper.writeValueAsString(original);
80-
assertEquals("[\"Read\",\"Write\",\"Idle\"]", json);
87+
assertEquals("{\"xCategories\":[\"Read\",\"Write\",\"Idle\"]}", json);
8188
}
8289

8390
/**
84-
* Test round-trip serialization and deserialization for
85-
* {@link ISampling.Ranges}. The format is a 2D array of timestamp
86-
* ranges, i.e., {@code [[start, end], ...]}.
91+
* Test round-trip serialization {@link ISampling.Ranges}.
92+
* The format is an array of range json objects.
8793
*
8894
* @throws JsonProcessingException
8995
* if JSON processing fails
9096
*/
9197
@Test
9298
public void testTimeRangesRoundTrip() throws JsonProcessingException {
93-
ISampling original = new Ranges(List.of(
99+
TestSampling original = new TestSampling(new Ranges(List.of(
94100
new Range<>(1L, 2L),
95101
new Range<>(2L, 3L),
96-
new Range<>(3L, 4L)
102+
new Range<>(3L, 4L))
97103
));
98104
String json = fMapper.writeValueAsString(original);
99-
assertEquals("[[1,2],[2,3],[3,4]]", json);
105+
assertEquals("{\"xRanges\":[{\"start\":1,\"end\":2},{\"start\":2,\"end\":3},{\"start\":3,\"end\":4}]}", json);
106+
}
107+
108+
private class TestSampling implements Serializable {
109+
private static final long serialVersionUID = 4522987850097455165L;
110+
ISampling fSampling;
111+
112+
public TestSampling(ISampling sampling) {
113+
fSampling = sampling;
114+
}
115+
116+
public ISampling getSampling() {
117+
return fSampling;
118+
}
119+
}
120+
121+
private class TestSamplingSerializer extends StdSerializer<TestSampling> {
122+
123+
private static final long serialVersionUID = 4522987850097455165L;
124+
125+
/**
126+
* Constructor
127+
*/
128+
public TestSamplingSerializer() {
129+
super(TestSampling.class);
130+
}
131+
132+
@Override
133+
public void serialize(TestSampling value, JsonGenerator gen, SerializerProvider provider) throws IOException {
134+
gen.writeStartObject();
135+
gen.writeObject(value.getSampling());
136+
gen.writeEndObject();
137+
}
100138
}
101139
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.model;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
5+
6+
/**
7+
* Represents a closed interval [start, end] for a sampling range.
8+
*/
9+
@Schema(
10+
name = "Range",
11+
description = "An object representing a closed interval with a start and end."
12+
)
13+
class Range {
14+
@Schema(description = "Start of the range (inclusive).", requiredMode = RequiredMode.REQUIRED)
15+
private final long start;
16+
17+
@Schema(description = "End of the range (inclusive).", requiredMode = RequiredMode.REQUIRED)
18+
private final long end;
19+
20+
public Range(long start, long end) {
21+
this.start = start;
22+
this.end = end;
23+
}
24+
25+
public long getStart() {
26+
return start;
27+
}
28+
29+
public long getEnd() {
30+
return end;
31+
}
32+
}

trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/model/Sampling.java

Lines changed: 0 additions & 88 deletions
This file was deleted.

trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/model/SeriesModel.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
package org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.model;
1313

14+
import java.util.List;
15+
1416
import org.eclipse.jdt.annotation.NonNull;
1517

1618
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -39,15 +41,25 @@ public interface SeriesModel {
3941
String getSeriesName();
4042

4143
/**
42-
* @return The X values.
44+
* @return The X values as list of longs
4345
*/
4446
@JsonProperty("xValues")
45-
@Schema(description = "Sampling values", requiredMode = RequiredMode.REQUIRED, oneOf = {
46-
Sampling.TimestampSampling.class,
47-
Sampling.CategorySampling.class,
48-
Sampling.RangeSampling.class
49-
})
50-
Sampling getXValues();
47+
@Schema(description = "X values as list of int64 values (e.g. timestamps). Example: [100, 200, 350]. Mutually exclusive with xCategories/xRanges.")
48+
public List<Long> getXValues();
49+
50+
/**
51+
* @return The X values as list of strings
52+
*/
53+
@JsonProperty("xCategories")
54+
@Schema(description = "X values as list of category strings. Example: [\"READ\", \"WRITE\"]. Mutually exclusive with xValues/xRanges.")
55+
public List<String> getXCategories();
56+
57+
/**
58+
* @return The X values as list of ranges
59+
*/
60+
@JsonProperty("xRanges")
61+
@Schema(description = "X values as list of start/end range objects. Example: [{\"start\": 10, \"end\": 20}, {\"start\": 50, \"end\": 75}]. Mutually exclusive with xValues/xCategories.")
62+
public List<Range> getXRanges();
5163

5264
/**
5365
* @return The Y values.

trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/EndpointConstants.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public final class EndpointConstants {
8484
static final String LICENSE = "Apache 2"; //$NON-NLS-1$
8585
static final String LICENSE_URL = "http://www.apache.org/licenses/"; //$NON-NLS-1$
8686
/** The TSP version */
87-
public static final String VERSION = "0.5.0"; //$NON-NLS-1$
87+
public static final String VERSION = "0.6.0"; //$NON-NLS-1$
8888
static final String SERVER = "https://localhost:8080/tsp/api"; //$NON-NLS-1$
8989

9090
/**

trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/webapp/SamplingSerializer.java

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,13 @@
2626

2727
/**
2828
* Custom serializer for all Sampling subtypes.
29-
* - Timestamps → flat array: [1, 2, 3]
30-
* - Categories → array of strings: ["Read", "Write"]
31-
* - TimeRanges → array of arrays: [[1, 2], [2, 3]]
29+
*
30+
* Note: It serializes only the object content without JSON object start and end marker. Hence
31+
* ISampling has to be embedded inside an outer JSON object
32+
*
33+
* - Timestamps → flat array → "xValues": [1, 2, 3]
34+
* - Categories → array of strings → "xCategories": ["Read", "Write"]
35+
* - Ranges → array of Range → "xRanges": [{ "start": 1, "end": 2 }, { "start": 3, "end": 4 }]
3236
*/
3337
public class SamplingSerializer extends StdSerializer<ISampling> {
3438

@@ -43,23 +47,20 @@ public SamplingSerializer() {
4347

4448
@Override
4549
public void serialize(ISampling value, JsonGenerator gen, SerializerProvider provider) throws IOException {
50+
// Note that ISampling is not serialized as a json object, but only as json field.
51+
// Hence it has to be embedded in an outer JSON object
4652
if (value instanceof Timestamps timestamps) {
47-
gen.writeArray(timestamps.timestamps(), 0, timestamps.timestamps().length);
48-
53+
gen.writeObjectField("xValues", timestamps.timestamps()); //$NON-NLS-1$
4954
} else if (value instanceof Categories categories) {
50-
gen.writeStartArray();
51-
for (String category : categories.categories()) {
52-
gen.writeString(category);
53-
}
54-
gen.writeEndArray();
55-
55+
gen.writeObjectField("xCategories", categories.categories()); //$NON-NLS-1$
5656
} else if (value instanceof Ranges timeRanges) {
57+
gen.writeFieldName("xRanges"); //$NON-NLS-1$
5758
gen.writeStartArray();
5859
for (Range<@NonNull Long> range : timeRanges.ranges()) {
59-
gen.writeStartArray();
60-
gen.writeNumber(range.start());
61-
gen.writeNumber(range.end());
62-
gen.writeEndArray();
60+
gen.writeStartObject();
61+
gen.writeNumberField("start", range.start()); //$NON-NLS-1$
62+
gen.writeNumberField("end", range.end()); //$NON-NLS-1$
63+
gen.writeEndObject();
6364
}
6465
gen.writeEndArray();
6566
} else {

trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/webapp/SeriesModelSerializer.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616
import org.eclipse.jdt.annotation.NonNull;
1717
import org.eclipse.tracecompass.tmf.core.model.OutputElementStyle;
1818
import org.eclipse.tracecompass.tmf.core.model.StyleProperties;
19+
import org.eclipse.tracecompass.tmf.core.model.ISampling;
20+
import org.eclipse.tracecompass.tmf.core.model.ISampling.Categories;
21+
import org.eclipse.tracecompass.tmf.core.model.ISampling.Range;
22+
import org.eclipse.tracecompass.tmf.core.model.ISampling.Ranges;
23+
import org.eclipse.tracecompass.tmf.core.model.ISampling.Timestamps;
1924
import org.eclipse.tracecompass.tmf.core.model.xy.ISeriesModel;
2025

2126
import com.fasterxml.jackson.core.JsonGenerator;
@@ -47,7 +52,27 @@ public void serialize(ISeriesModel value, JsonGenerator gen, SerializerProvider
4752
gen.writeStartObject();
4853
gen.writeNumberField("seriesId", value.getId()); //$NON-NLS-1$
4954
gen.writeStringField("seriesName", value.getName()); //$NON-NLS-1$
50-
gen.writeObjectField("xValues", value.getSampling()); //$NON-NLS-1$
55+
56+
// Serialize the sampling
57+
ISampling sampling = value.getSampling();
58+
if (sampling instanceof Timestamps timestamps) {
59+
gen.writeObjectField("xValues", timestamps.timestamps()); //$NON-NLS-1$
60+
} else if (sampling instanceof Categories categories) {
61+
gen.writeObjectField("xCategories", categories.categories()); //$NON-NLS-1$
62+
} else if (sampling instanceof Ranges timeRanges) {
63+
gen.writeFieldName("xRanges"); //$NON-NLS-1$
64+
gen.writeStartArray();
65+
for (Range<@NonNull Long> range : timeRanges.ranges()) {
66+
gen.writeStartObject();
67+
gen.writeNumberField("start", range.start()); //$NON-NLS-1$
68+
gen.writeNumberField("end", range.end()); //$NON-NLS-1$
69+
gen.writeEndObject();
70+
}
71+
gen.writeEndArray();
72+
} else {
73+
throw new IllegalArgumentException("Unknown Sampling type: " + value.getClass().getName()); //$NON-NLS-1$
74+
}
75+
5176
gen.writeObjectField("yValues", value.getData()); //$NON-NLS-1$
5277

5378
// no-op trim below, null-related (unlikely case) warning otherwise-

0 commit comments

Comments
 (0)