Skip to content

Commit 8f7dc96

Browse files
committed
server: Introduce ErrorResponse and use it everywhere
When an error is created when processing a client request, return an ErrorResponse JSON object that includes - title: short, human-readable description of the error - detail: optional, human-readable explanation of the error This standardizes the error response to common data structure. The field names and purpose are inspired by RFC 9457. For 409 errors (trace or experiment) return extended ErrorResponse containing the conflicting trace or experiment respectively. This commit also updates the swagger documentation in respect to the ErrorResponses accordingly. This commit also fixes some incorrect or missing the swagger definitions. Add description to Trace and Experiment model so that they are not overridden by the experiment/trace error description. Contributes to fix issue: eclipse-cdt-cloud/trace-server-protocol#122 Signed-off-by: Bernd Hufmann <[email protected]>
1 parent f229eb4 commit 8f7dc96

File tree

23 files changed

+878
-220
lines changed

23 files changed

+878
-220
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/services/DataProviderConfigurationServiceTest.java

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.DataProviderService;
3131
import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants;
32+
import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.ErrorResponseImpl;
3233
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.DataProviderDescriptorStub;
3334
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.ExperimentModelStub;
3435
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TestDataProviderFactory;
@@ -93,29 +94,29 @@ public void testDataProviderConfigTypesErrors() {
9394
try (Response response = configTypesEndpoint.request(MediaType.APPLICATION_JSON).get()) {
9495
assertNotNull(response);
9596
assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
96-
assertEquals(EndpointConstants.NO_SUCH_TRACE, response.readEntity(String.class));
97+
assertEquals(EndpointConstants.NO_SUCH_TRACE, response.readEntity(ErrorResponseImpl.class).getTitle());
9798
}
9899

99100
WebTarget singleTypeEndpoint = configTypesEndpoint.path(TestSchemaConfigurationSource.TYPE.getId());
100101
try (Response response = singleTypeEndpoint.request(MediaType.APPLICATION_JSON).get()) {
101102
assertNotNull(response);
102103
assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
103-
assertEquals(EndpointConstants.NO_SUCH_TRACE, response.readEntity(String.class));
104+
assertEquals(EndpointConstants.NO_SUCH_TRACE, response.readEntity(ErrorResponseImpl.class).getTitle());
104105
}
105106

106107
// Unknown data provider
107108
configTypesEndpoint = getConfigEndpoint(exp.getUUID().toString(), UNKNOWN_DP_ID);
108109
try (Response response = configTypesEndpoint.request(MediaType.APPLICATION_JSON).get()) {
109110
assertNotNull(response);
110111
assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
111-
assertEquals(EndpointConstants.NO_SUCH_PROVIDER, response.readEntity(String.class));
112+
assertEquals(EndpointConstants.NO_SUCH_PROVIDER, response.readEntity(ErrorResponseImpl.class).getTitle());
112113
}
113114

114115
singleTypeEndpoint = configTypesEndpoint.path(TestSchemaConfigurationSource.TYPE.getId());
115116
try (Response response = singleTypeEndpoint.request(MediaType.APPLICATION_JSON).get()) {
116117
assertNotNull(response);
117118
assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
118-
assertEquals(EndpointConstants.NO_SUCH_PROVIDER, response.readEntity(String.class));
119+
assertEquals(EndpointConstants.NO_SUCH_PROVIDER, response.readEntity(ErrorResponseImpl.class).getTitle());
119120
}
120121

121122
// Test config type is not applicable for another data provider
@@ -124,15 +125,15 @@ public void testDataProviderConfigTypesErrors() {
124125
try (Response response = singleTypeEndpoint.request(MediaType.APPLICATION_JSON).get()) {
125126
assertNotNull(response);
126127
assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
127-
assertEquals(EndpointConstants.NO_SUCH_PROVIDER, response.readEntity(String.class));
128+
assertEquals(EndpointConstants.NO_SUCH_PROVIDER, response.readEntity(ErrorResponseImpl.class).getTitle());
128129
}
129130

130131
configTypesEndpoint = getConfigEndpoint(exp.getUUID().toString(), TestDataProviderFactory.ID);
131132
singleTypeEndpoint = configTypesEndpoint.path(UNKNOWN_TYPE_ID);
132133
try (Response response = singleTypeEndpoint.request(MediaType.APPLICATION_JSON).get()) {
133134
assertNotNull(response);
134135
assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
135-
assertEquals(EndpointConstants.NO_SUCH_CONFIGURATION_TYPE, response.readEntity(String.class));
136+
assertEquals(EndpointConstants.NO_SUCH_CONFIGURATION_TYPE, response.readEntity(ErrorResponseImpl.class).getTitle());
136137
}
137138
}
138139

@@ -194,21 +195,21 @@ public void testCreationOfDerivedDataProvidersErrors() throws IOException, URISy
194195
// Unknown experiment
195196
try (Response response = assertDpPostWithErrors(dpCreationEndpoint, configuration)) {
196197
assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
197-
assertEquals(EndpointConstants.NO_SUCH_TRACE, response.readEntity(String.class));
198+
assertEquals(EndpointConstants.NO_SUCH_TRACE, response.readEntity(ErrorResponseImpl.class).getTitle());
198199
}
199200

200201
// Unknown data provider
201202
dpCreationEndpoint = getDpCreationEndpoint(exp.getUUID().toString(), UNKNOWN_DP_ID);
202203
try (Response response = assertDpPostWithErrors(dpCreationEndpoint, configuration)) {
203204
assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
204-
assertEquals(EndpointConstants.NO_SUCH_PROVIDER + ": " + UNKNOWN_DP_ID, response.readEntity(String.class));
205+
assertEquals(EndpointConstants.NO_SUCH_PROVIDER + ": " + UNKNOWN_DP_ID, response.readEntity(ErrorResponseImpl.class).getTitle());
205206
}
206207

207208
// Test config type is not applicable for another data provider
208209
dpCreationEndpoint = getDpCreationEndpoint(exp.getUUID().toString(), CALL_STACK_DATAPROVIDER_ID);
209210
try (Response response = assertDpPostWithErrors(dpCreationEndpoint, configuration)) {
210211
assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
211-
assertEquals(EndpointConstants.NO_SUCH_PROVIDER, response.readEntity(String.class));
212+
assertEquals(EndpointConstants.NO_SUCH_PROVIDER, response.readEntity(ErrorResponseImpl.class).getTitle());
212213
}
213214

214215
// Invalid config type ID
@@ -222,7 +223,7 @@ public void testCreationOfDerivedDataProvidersErrors() throws IOException, URISy
222223
configuration = builder.build();
223224
try (Response response = assertDpPostWithErrors(dpCreationEndpoint, configuration)) {
224225
assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
225-
assertEquals(EndpointConstants.NO_SUCH_CONFIGURATION_TYPE, response.readEntity(String.class));
226+
assertEquals(EndpointConstants.NO_SUCH_CONFIGURATION_TYPE, response.readEntity(ErrorResponseImpl.class).getTitle());
226227
}
227228
}
228229

@@ -244,7 +245,7 @@ public void testDeletionOfDerivedDataProvidersErrors() throws IOException, URISy
244245
try (Response response = dpDeletionEndpoint.request().delete()) {
245246
assertNotNull(response);
246247
assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
247-
assertEquals(EndpointConstants.NO_SUCH_TRACE, response.readEntity(String.class));
248+
assertEquals(EndpointConstants.NO_SUCH_TRACE, response.readEntity(ErrorResponseImpl.class).getTitle());
248249
}
249250

250251
// Unknown input data provider
@@ -253,7 +254,7 @@ public void testDeletionOfDerivedDataProvidersErrors() throws IOException, URISy
253254
try (Response response = dpDeletionEndpoint.request().delete()) {
254255
assertNotNull(response);
255256
assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
256-
assertEquals(EndpointConstants.NO_SUCH_PROVIDER + ": " + UNKNOWN_DP_ID, response.readEntity(String.class));
257+
assertEquals(EndpointConstants.NO_SUCH_PROVIDER + ": " + UNKNOWN_DP_ID, response.readEntity(ErrorResponseImpl.class).getTitle());
257258
}
258259

259260
// Unknown derived data provider
@@ -262,7 +263,7 @@ public void testDeletionOfDerivedDataProvidersErrors() throws IOException, URISy
262263
try (Response response = dpDeletionEndpoint.request().delete()) {
263264
assertNotNull(response);
264265
assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
265-
assertEquals(EndpointConstants.NO_SUCH_DERIVED_PROVIDER + ": " + UNKNOWN_DP_ID, response.readEntity(String.class));
266+
assertEquals(EndpointConstants.NO_SUCH_DERIVED_PROVIDER + ": " + UNKNOWN_DP_ID, response.readEntity(ErrorResponseImpl.class).getTitle());
266267
}
267268

268269
Map<String, Object> params = readParametersFromJson(VALID_JSON_FILENAME);
@@ -283,7 +284,7 @@ public void testDeletionOfDerivedDataProvidersErrors() throws IOException, URISy
283284
try (Response response = dpDeletionEndpoint.request().delete()) {
284285
assertNotNull(response);
285286
assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
286-
assertEquals(EndpointConstants.NO_SUCH_PROVIDER, response.readEntity(String.class));
287+
assertEquals(EndpointConstants.NO_SUCH_PROVIDER, response.readEntity(ErrorResponseImpl.class).getTitle());
287288
}
288289

289290
// Successful deletion

trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/services/ExperimentManagerServiceTest.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.eclipse.jdt.annotation.NonNull;
4141
import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.model.views.QueryParameters;
4242
import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.ExperimentManagerService;
43+
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.ExperimentErrorResponseStub;
4344
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.ExperimentModelStub;
4445
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TraceModelStub;
4546
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.utils.RestServerTest;
@@ -64,6 +65,9 @@ public class ExperimentManagerServiceTest extends RestServerTest {
6465
private static final @NonNull ImmutableSet<TraceModelStub> CONTEXT_SWITCH_NOT_INITIALIZED_SET = ImmutableSet.of(sfContextSwitchesKernelNotInitializedStub, sfContextSwitchesUstNotInitializedStub);
6566
private static final @NonNull ExperimentModelStub EXPECTED = new ExperimentModelStub(TEST, CONTEXT_SWITCH_SET);
6667

68+
private static final String EXPERIMENT_NAME_EXISTS = "The experiment (name) already exists and both differ."; //$NON-NLS-1$
69+
private static final String EXPERIMENT_NAME_EXISTS_DETAIL = "The experiment with same name already exists with conflicting parameters. Use a different name to avoid the conflict."; //$NON-NLS-1$
70+
6771
/**
6872
* Basic test for the {@link ExperimentManagerService}
6973
*/
@@ -195,11 +199,16 @@ public void testPostConflicts() {
195199
parameters2.put(TRACES, traceUUIDs2);
196200
try (Response response2 = expTarget.request().post(Entity.json(new QueryParameters(parameters2, Collections.emptyList())))) {
197201
assertEquals("Expected a conflict for posting different experiment", Status.CONFLICT.getStatusCode(), response2.getStatus());
198-
assertEquals("Conflict should return original experiment name", EXPECTED.getName(), response2.readEntity(ExperimentModelStub.class).getName());
202+
ExperimentErrorResponseStub errorResponse = response2.readEntity(ExperimentErrorResponseStub.class);
203+
assertEquals("Conflict detail should be returned", EXPERIMENT_NAME_EXISTS, errorResponse.getTitle());
204+
assertEquals("Conflict detail should be returned", EXPERIMENT_NAME_EXISTS_DETAIL, errorResponse.getDetail());
205+
ExperimentModelStub traceObj = errorResponse.getExperiment();
206+
assertEquals("Conflict should return original experiment", EXPECTED, traceObj);
199207
assertEquals("There should still be only one experiment", ImmutableSet.of(EXPECTED), getExperiments(expTarget));
200208
assertEquals("Failing to add an experiment should not change the trace set", traceSet, getTraces(traces));
201209
assertEquals("Failed to get the experiment by its UUID", EXPECTED, expTarget.path(expStub.getUUID().toString()).request().get(ExperimentModelStub.class));
202210
}
211+
203212
// Post same experiment name, but with traces with the same names, but not the same traces
204213
List<String> traceUUIDs3 = new ArrayList<>();
205214
traceUUIDs3.add(arm64Stub.getUUID().toString());
@@ -209,7 +218,11 @@ public void testPostConflicts() {
209218
parameters3.put(TRACES, traceUUIDs3);
210219
try (Response response3 = expTarget.request().post(Entity.json(new QueryParameters(parameters3, Collections.emptyList())))) {
211220
assertEquals("Expected a conflict for posting different experiment", Status.CONFLICT.getStatusCode(), response3.getStatus());
212-
assertEquals("Conflict should return original experiment name", EXPECTED.getName(), response3.readEntity(ExperimentModelStub.class).getName());
221+
ExperimentErrorResponseStub errorResponse = response3.readEntity(ExperimentErrorResponseStub.class);
222+
assertEquals("Conflict detail should be returned", EXPERIMENT_NAME_EXISTS, errorResponse.getTitle());
223+
assertEquals("Conflict detail should be returned", EXPERIMENT_NAME_EXISTS_DETAIL, errorResponse.getDetail());
224+
ExperimentModelStub traceObj = errorResponse.getExperiment();
225+
assertEquals("Conflict should return original experiment", EXPECTED, traceObj);
213226
assertEquals("There should still be only one experiment", ImmutableSet.of(EXPECTED), getExperiments(expTarget));
214227
assertEquals("Failing to add an experiment should not change the trace set", traceSet, getTraces(traces));
215228
assertEquals("Failed to get the new experiment by its UUID", EXPECTED, expTarget.path(expStub.getUUID().toString()).request().get(ExperimentModelStub.class));

trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/services/TimeGraphDataProviderServiceTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.model.views.QueryParameters;
4343
import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.DataProviderService;
4444
import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants;
45+
import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.ErrorResponseImpl;
4546
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.AnnotationCategoriesOutputResponseStub;
4647
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.AnnotationModelStub;
4748
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.AnnotationResponseStub;
@@ -283,15 +284,15 @@ public void testAnnotationCategoriesErrors() {
283284
try (Response response = endpoint.request(MediaType.APPLICATION_JSON).get()) {
284285
assertNotNull(response);
285286
assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
286-
assertEquals(EndpointConstants.NO_SUCH_TRACE, response.readEntity(String.class));
287+
assertEquals(EndpointConstants.NO_SUCH_TRACE, response.readEntity(ErrorResponseImpl.class).getTitle());
287288
}
288289

289290
// Unknown data provider
290291
endpoint = getAnnotationCategoriesEndpoint(exp.getUUID().toString(), UNKNOWN_DP_ID);
291292
try (Response response = endpoint.request(MediaType.APPLICATION_JSON).get()) {
292293
assertNotNull(response);
293294
assertEquals(Status.METHOD_NOT_ALLOWED.getStatusCode(), response.getStatus());
294-
assertEquals(EndpointConstants.NO_PROVIDER, response.readEntity(String.class));
295+
assertEquals(EndpointConstants.NO_PROVIDER, response.readEntity(ErrorResponseImpl.class).getTitle());
295296
}
296297
}
297298

0 commit comments

Comments
 (0)