Skip to content

Commit dbc9830

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 the detailed error message and an optional details map that can be used to provide contextual details regarding the error case. This standardizes the error response to common data structure. Use the optional details map to return the conflicting trace or experiment object for 409 (conflict) error case. This commit also updates the swagger documentation accordingly. This commit fixes the swagger documentation for trace and experiment regarding error code 409 (conflict) and some missing definitions. Contributes to fix issue: eclipse-cdt-cloud/trace-server-protocol#122 Signed-off-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
1 parent f229eb4 commit dbc9830

File tree

17 files changed

+660
-218
lines changed

17 files changed

+660
-218
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).getMessage());
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).getMessage());
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).getMessage());
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).getMessage());
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).getMessage());
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).getMessage());
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).getMessage());
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).getMessage());
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).getMessage());
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).getMessage());
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).getMessage());
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).getMessage());
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).getMessage());
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).getMessage());
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: 19 additions & 0 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,8 @@ 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 with same name already exists with conflicting parameters. Use a different name to avoid the conflict. See error details for conflicting experiment."; //$NON-NLS-1$
69+
6770
/**
6871
* Basic test for the {@link ExperimentManagerService}
6972
*/
@@ -145,6 +148,22 @@ public void testRePost() {
145148

146149
}
147150

151+
// Create a new experiment with the same name but different traces which should return in a conflict
152+
traceUUIDs = new ArrayList<>();
153+
traceUUIDs.add(ustStub.getUUID().toString());
154+
parameters.put(TRACES, traceUUIDs);
155+
156+
try (Response response = expTarget.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())))) {
157+
assertEquals(409, response.getStatus());
158+
ExperimentErrorResponseStub errorResponse = response.readEntity(ExperimentErrorResponseStub.class);
159+
assertTrue(errorResponse.getMessage().equals(EXPERIMENT_NAME_EXISTS));
160+
Map<String, ExperimentModelStub> details = errorResponse.getDetails();
161+
assertNotNull(details);
162+
assertTrue(details.containsKey("experiment"));
163+
ExperimentModelStub traceObj = details.get("experiment");
164+
assertEquals(EXPECTED, traceObj);
165+
}
166+
148167
try (Response deleteResponse = expTarget.path(expStub.getUUID().toString()).request().delete()) {
149168
assertEquals("Failed to DELETE the experiment", EXPECTED, deleteResponse.readEntity(ExperimentModelStub.class));
150169
}

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).getMessage());
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).getMessage());
295296
}
296297
}
297298

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

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717

1818
import java.io.IOException;
1919
import java.util.Collections;
20+
import java.util.HashMap;
21+
import java.util.Map;
2022

23+
import javax.ws.rs.client.Entity;
2124
import javax.ws.rs.client.WebTarget;
2225
import javax.ws.rs.core.MediaType;
2326
import javax.ws.rs.core.Response;
@@ -31,7 +34,12 @@
3134
import org.eclipse.core.runtime.FileLocator;
3235
import org.eclipse.core.runtime.IPath;
3336
import org.eclipse.core.runtime.Path;
37+
import org.eclipse.jdt.annotation.NonNull;
38+
import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.model.views.QueryParameters;
39+
import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.ErrorResponseImpl;
3440
import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.TraceManagerService;
41+
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.ExperimentModelStub;
42+
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TraceErrorResponseStub;
3543
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TraceModelStub;
3644
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.utils.RestServerTest;
3745
import org.eclipse.tracecompass.testtraces.ctf.CtfTestTrace;
@@ -50,6 +58,14 @@
5058
@SuppressWarnings("null")
5159
public class TraceManagerServiceTest extends RestServerTest {
5260

61+
private static final String TEST = "test";
62+
private static final @NonNull ImmutableSet<TraceModelStub> CONTEXT_SWITCH_SET = ImmutableSet.of(sfContextSwitchesKernelStub, sfContextSwitchesUstStub);
63+
private static final @NonNull ImmutableSet<TraceModelStub> CONTEXT_SWITCH_NOT_INITIALIZED_SET = ImmutableSet.of(sfContextSwitchesUstNotInitializedStub);
64+
private static final @NonNull ExperimentModelStub EXPECTED = new ExperimentModelStub(TEST, CONTEXT_SWITCH_SET);
65+
66+
private static final String NAME_EXISTS = "The trace with same name already exists with conflicting parameters. Use a different name to avoid the conflict. See error details for conflicting trace."; //$NON-NLS-1$
67+
private static final String DELTETE_TRACE_IN_USE = "The trace is in use by at least one experiment thus cannot be deleted. See error details for conflicting trace."; //$NON-NLS-1$
68+
5369
/**
5470
* Test basic operations on the {@link TraceManagerService}.
5571
*/
@@ -110,6 +126,27 @@ public void testConflictingTraces() {
110126
// matter if the path is different, the trace will be added
111127
assertPost(traces, sfArm64KernelNotIntitialzedStub);
112128
assertEquals(ImmutableSet.of(sfContextSwitchesKernelNotInitializedStub, sfArm64KernelNotIntitialzedStub), getTraces(traces));
129+
130+
// Verify conflicting parameters (same trace but provided and different trace type)
131+
Map<String, Object> parameters = new HashMap<>();
132+
parameters.put(NAME, sfContextSwitchesKernelNotInitializedStub.getName());
133+
parameters.put(URI, sfContextSwitchesKernelNotInitializedStub.getPath());
134+
parameters.put(TYPE_ID, "org.eclipse.linuxtools.tmf.ui.type.ctf");
135+
try (Response response = traces.request().post(Entity.json(new QueryParameters(parameters , Collections.emptyList())))) {
136+
assertEquals(409, response.getStatus());
137+
TraceErrorResponseStub errorResponse = response.readEntity(TraceErrorResponseStub.class);
138+
assertTrue(errorResponse.getMessage().equals(NAME_EXISTS));
139+
Map<String, TraceModelStub> details = errorResponse.getDetails();
140+
assertNotNull(details);
141+
assertTrue(details.containsKey("trace"));
142+
TraceModelStub traceObj = details.get("trace");
143+
assertEquals(sfContextSwitchesKernelNotInitializedStub, traceObj);
144+
}
145+
parameters.put(NAME, sfContextSwitchesKernelNotInitializedStub.getName() + "(1)");
146+
try (Response response = traces.request().post(Entity.json(new QueryParameters(parameters , Collections.emptyList())))) {
147+
assertEquals(200, response.getStatus());
148+
}
149+
113150
}
114151

115152
/**
@@ -161,4 +198,52 @@ public void testWorkspaceStructure() throws CoreException, IOException {
161198
// Verify that the trace type is the kernel trace type
162199
assertEquals("org.eclipse.linuxtools.lttng2.kernel.tracetype", traceType);
163200
}
201+
202+
/**
203+
* Test error case if trace does not exist
204+
*/
205+
@Test
206+
public void testTraceNotExist() {
207+
WebTarget traces = getApplicationEndpoint().path(TRACES);
208+
Map<String, Object> parameters = new HashMap<>();
209+
parameters.put(NAME, "trace-does-not-exist");
210+
parameters.put(URI, "/path/does/not/exist");
211+
try (Response response = traces.request().post(Entity.json(new QueryParameters(parameters , Collections.emptyList())))) {
212+
int code = response.getStatus();
213+
assertEquals("Post trace should fail", 404, code);
214+
ErrorResponseImpl result = response.readEntity(ErrorResponseImpl.class);
215+
assertNotNull(result);
216+
assertNotNull(result.getMessage());
217+
}
218+
}
219+
220+
/**
221+
* Test delete of trace which is still in use by on experiment
222+
*/
223+
@Test
224+
public void testDeleteConflict() {
225+
WebTarget application = getApplicationEndpoint();
226+
WebTarget traces = application.path(TRACES);
227+
WebTarget expTarget = application.path(EXPERIMENTS);
228+
TraceModelStub ustStub = assertPost(traces, sfContextSwitchesUstNotInitializedStub);
229+
ExperimentModelStub expStub = assertPostExperiment(TEST, ustStub);
230+
231+
try (Response response = traces.path(ustStub.getUUID().toString()).request().delete()) {
232+
assertEquals(409, response.getStatus());
233+
TraceErrorResponseStub errorResponse = response.readEntity(TraceErrorResponseStub.class);
234+
assertTrue(errorResponse.getMessage().equals(DELTETE_TRACE_IN_USE));
235+
Map<String, TraceModelStub> details = errorResponse.getDetails();
236+
assertNotNull(details);
237+
assertTrue(details.containsKey("trace"));
238+
TraceModelStub traceObj = details.get("trace");
239+
assertEquals(ustStub, traceObj);
240+
}
241+
242+
try (Response deleteResponse = expTarget.path(expStub.getUUID().toString()).request().delete()) {
243+
assertEquals("Failed to DELETE the experiment", EXPECTED, deleteResponse.readEntity(ExperimentModelStub.class));
244+
assertEquals("experiment set should be empty at this point", Collections.emptySet(), getExperiments(expTarget));
245+
assertEquals("Deleting an experiment should not change the trace set", CONTEXT_SWITCH_NOT_INITIALIZED_SET, getTraces(traces));
246+
}
247+
}
248+
164249
}

0 commit comments

Comments
 (0)