diff --git a/deployment/hasura/metadata/actions.graphql b/deployment/hasura/metadata/actions.graphql index 22496c288a..8349d072b6 100644 --- a/deployment/hasura/metadata/actions.graphql +++ b/deployment/hasura/metadata/actions.graphql @@ -280,6 +280,9 @@ type ResourceSamplesResponse { type ConstraintResponse { success: String! + constraintId: Int!, + constraintName: String!, + type: String!, errors: [UserCodeError!]! results: [ConstraintResult!]! } @@ -287,9 +290,6 @@ type ConstraintResponse { type ConstraintResult { violations: [ConstraintViolation!]!, gaps: [Interval!]! - constraintId: Int!, - constraintName: String!, - type: String!, resourceIds: [String!]! } diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/ConstraintsTests.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/ConstraintsTests.java index f1f86ef10f..fd91e68acc 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/ConstraintsTests.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/ConstraintsTests.java @@ -104,14 +104,15 @@ void constraintsSucceedOneViolation() throws IOException { final var constraintsResponses = hasura.checkConstraints(planId); assertEquals(1, constraintsResponses.size()); - // Check the Result + // Check the Response final var constraintResponse = constraintsResponses.get(0); assertTrue(constraintResponse.success()); + assertEquals(constraintId, constraintResponse.constraintId()); + assertEquals(constraintName, constraintResponse.constraintName()); + assertEquals("plan", constraintResponse.type()); + // Check the Result assertTrue(constraintResponse.result().isPresent()); final var constraintResult = constraintResponse.result().get(); - assertEquals(constraintId, constraintResult.constraintId()); - assertEquals(constraintName, constraintResult.constraintName()); - // Resources final var resources = constraintResult.resourceIds(); assertEquals(2, resources.size()); @@ -139,7 +140,10 @@ void constraintsSucceedNoViolations() throws IOException { final var constraintResponses = hasura.checkConstraints(planId); assertEquals(1, constraintResponses.size()); - assertTrue( constraintResponses.get(0).success()); + assertTrue(constraintResponses.get(0).success()); + assertEquals(constraintId, constraintResponses.get(0).constraintId()); + assertEquals(constraintName, constraintResponses.get(0).constraintName()); + assertEquals("plan", constraintResponses.get(0).type()); assertTrue( constraintResponses.get(0).result().isPresent()); assertEquals(0, constraintResponses.get(0).result().get().violations().size()); } @@ -153,6 +157,7 @@ void constraintCachedViolation() throws IOException { // There's the correct number of results assertEquals(1, cachedRuns.size()); assertEquals(1, constraintResponses.size()); + assertEquals(cachedRuns.get(0).constraintId(),constraintId); // Check properties final var cachedRun = cachedRuns.get(0); @@ -190,9 +195,6 @@ void constraintCachedNoViolations() throws IOException { // Check results assertTrue(cachedRun.results().isPresent()); final var results = cachedRun.results().get(); - assertEquals(constraintId, results.constraintId()); - assertEquals("fruit_equal_peel",results.constraintName()); - assertEquals("plan",results.type()); assertEquals(2,results.resourceIds().size()); assertEquals("/peel",results.resourceIds().get(0)); assertEquals("/fruit",results.resourceIds().get(1)); @@ -240,13 +242,16 @@ void constraintsWorkMonthLongActivity() throws IOException { final var constraintsResponses = hasura.checkConstraints(planId); assertEquals(1, constraintsResponses.size()); - // Check the Result + // Check the Response final var constraintResponse = constraintsResponses.get(0); assertTrue(constraintResponse.success()); + assertEquals(constraintId,constraintResponse.constraintId()); + assertEquals(constraintName, constraintResponse.constraintName()); + assertEquals("plan", constraintResponse.type()); + + //Check Result assertTrue(constraintResponse.result().isPresent()); final var constraintResult = constraintResponse.result().get(); - assertEquals(constraintId, constraintResult.constraintId()); - assertEquals(constraintName, constraintResult.constraintName()); // Resources final var resources = constraintResult.resourceIds(); @@ -274,6 +279,9 @@ void runConstraintsOnOldSimulation() throws IOException { // Expect no violations on the new simulation final var newConstraintResponses = hasura.checkConstraints(planId, newSimDatasetId); assertEquals(1, newConstraintResponses.size()); + assertEquals(constraintId, newConstraintResponses.get(0).constraintId()); + assertEquals(constraintName, newConstraintResponses.get(0).constraintName()); + assertEquals("plan", newConstraintResponses.get(0).type()); assertTrue(newConstraintResponses.get(0).result().isPresent()); final var newConstraintResult = newConstraintResponses.get(0).result().get(); assertTrue(newConstraintResult.violations().isEmpty()); @@ -286,12 +294,12 @@ void runConstraintsOnOldSimulation() throws IOException { // Check the Result final var oldConstraintResponse = oldConstraintsResponses.get(0); assertTrue(oldConstraintResponse.success()); + assertEquals(constraintId, oldConstraintResponse.constraintId()); + assertEquals(constraintName, oldConstraintResponse.constraintName()); + assertEquals("plan", oldConstraintResponse.type()); assertTrue(oldConstraintResponse.result().isPresent()); final var constraintResult = oldConstraintResponse.result().get(); - assertEquals(constraintId, constraintResult.constraintId()); - assertEquals(constraintName, constraintResult.constraintName()); - // Resources final var resources = constraintResult.resourceIds(); assertEquals(2, resources.size()); @@ -372,22 +380,25 @@ void oneViolationCurrentSimulation() throws IOException { final var noDatasetResponses = hasura.checkConstraints(planId); assertEquals(1, noDatasetResponses.size()); assertTrue(noDatasetResponses.get(0).success()); + assertEquals(constraintId, noDatasetResponses.get(0).constraintId()); + assertEquals(constraintName, noDatasetResponses.get(0).constraintName()); + assertEquals("plan", noDatasetResponses.get(0).type()); assertTrue(noDatasetResponses.get(0).result().isPresent()); final var nRecordResults = noDatasetResponses.get(0).result().get(); // Constraint Results w/ SimDatasetId final var withDatasetResponses = hasura.checkConstraints(planId, simDatasetId); assertEquals(1, withDatasetResponses.size()); - assertTrue( withDatasetResponses.get(0).success()); + assertTrue(withDatasetResponses.get(0).success()); + assertEquals(constraintId, withDatasetResponses.get(0).constraintId()); + assertEquals(constraintName, withDatasetResponses.get(0).constraintName()); + assertEquals("plan", withDatasetResponses.get(0).type()); assertTrue(withDatasetResponses.get(0).result().isPresent()); final var wRecordResults = withDatasetResponses.get(0).result().get(); // The results should be the same assertEquals(nRecordResults, wRecordResults); - // Check the Result - assertEquals(constraintName, nRecordResults.constraintName()); - assertEquals(constraintId, nRecordResults.constraintId()); // Resources assertEquals(1, nRecordResults.resourceIds().size()); @@ -432,13 +443,12 @@ void oneViolationOutdatedSimIdPassed() throws IOException { final var constraintResponse = constraintResponses.get(0); assertTrue(constraintResponse.success()); + assertEquals(constraintId, constraintResponse.constraintId()); + assertEquals(constraintName, constraintResponse.constraintName()); + assertEquals("plan", constraintResponse.type()); assertTrue(constraintResponse.result().isPresent()); final var record = constraintResponse.result().get(); - // Check the Result - assertEquals(constraintName, record.constraintName()); - assertEquals(constraintId, record.constraintId()); - // Resources assertEquals(1, record.resourceIds().size()); assertTrue(record.resourceIds().contains("/my_boolean")); @@ -481,6 +491,9 @@ void compilationFailsOutdatedSimulationSimDatasetId() throws IOException { final var constraintResponses = hasura.checkConstraints(planId, newSimDatasetId); assertEquals(1,constraintResponses.size()); final var constraintResponse = constraintResponses.get(0); + assertEquals(constraintId, constraintResponse.constraintId()); + assertEquals(constraintName, constraintResponse.constraintName()); + assertEquals("plan", constraintResponse.type()); assertFalse(constraintResponse.success()); assertTrue(constraintResponse.result().isEmpty()); assertEquals(1,constraintResponse.errors().size()); @@ -502,6 +515,9 @@ void compilationFailsOutdatedSimulationNoSimDataset() throws IOException { assertEquals(1,constraintResponses.size()); final var constraintResponse = constraintResponses.get(0); assertFalse(constraintResponse.success()); + assertEquals(constraintId, constraintResponse.constraintId()); + assertEquals(constraintName, constraintResponse.constraintName()); + assertEquals("plan", constraintResponse.type()); assertTrue(constraintResponse.result().isEmpty()); assertEquals(1,constraintResponse.errors().size()); final ConstraintError error = constraintResponse.errors().get(0); @@ -526,7 +542,9 @@ void constraintInvalidModification() throws IOException { final var constraintsResponses = hasura.checkConstraints(planId); assertEquals(1, constraintsResponses.size()); assertTrue(constraintsResponses.get(0).success()); - + assertEquals(constraintId, constraintsResponses.get(0).constraintId()); + assertEquals(constraintName, constraintsResponses.get(0).constraintName()); + assertEquals("plan", constraintsResponses.get(0).type()); // Attempt to update a constraint with an invalid syntax and capture the error response hasura.updateConstraint( constraintId, @@ -536,6 +554,9 @@ void constraintInvalidModification() throws IOException { final var constraintsErrorResponses = hasura.checkConstraints(planId); assertEquals(1, constraintsErrorResponses.size()); assertFalse(constraintsErrorResponses.get(0).success()); + assertEquals(constraintId, constraintsErrorResponses.get(0).constraintId()); + assertEquals(constraintName, constraintsErrorResponses.get(0).constraintName()); + assertEquals("plan", constraintsErrorResponses.get(0).type()); assertTrue(constraintsErrorResponses.get(0).result().isEmpty()); assertEquals(1, constraintsErrorResponses.get(0).errors().size()); diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/types/ConstraintRecord.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/types/ConstraintRecord.java index 9ad419b326..48cfebcfbb 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/types/ConstraintRecord.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/types/ConstraintRecord.java @@ -6,6 +6,9 @@ public record ConstraintRecord( boolean success, + int constraintId, + String constraintName, + String type, Optional result, List errors @@ -13,6 +16,9 @@ public record ConstraintRecord( public static ConstraintRecord fromJSON(JsonObject json){ return new ConstraintRecord( json.getBoolean("success"), + json.getInt("constraintId"), + json.getString("constraintName"), + json.getString("type"), json.getJsonObject("results").isEmpty() ? Optional.empty() : Optional.of(ConstraintResult.fromJSON(json.getJsonObject("results"))), json.getJsonArray("errors").getValuesAs(ConstraintError::fromJSON)); } diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/types/ConstraintResult.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/types/ConstraintResult.java index f801e4f988..4f7cd4cdee 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/types/ConstraintResult.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/types/ConstraintResult.java @@ -5,12 +5,11 @@ import javax.json.JsonString; import java.util.List; -public record ConstraintResult(int constraintId, - String constraintName, - String type, - List resourceIds, - List violations, - List gaps){ +public record ConstraintResult( + List resourceIds, + List violations, + List gaps +) { public record ConstraintViolation(List activityInstanceIds, List windows) { public static ConstraintViolation fromJSON(JsonObject json) { @@ -34,9 +33,6 @@ public static ConstraintResult fromJSON(JsonObject json) { final var violations = json.getJsonArray("violations").getValuesAs(ConstraintViolation::fromJSON); return new ConstraintResult( - json.getInt("constraintId"), - json.getString("constraintName"), - json.getString("type"), resourceIds, violations, gaps diff --git a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/GQL.java b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/GQL.java index 304e1ba32a..4fb054b747 100644 --- a/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/GQL.java +++ b/e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/GQL.java @@ -38,11 +38,11 @@ mutation cancelSimulation($id: Int!) { query checkConstraints($planId: Int!, $simulationDatasetId: Int) { constraintViolations(planId: $planId, simulationDatasetId: $simulationDatasetId) { success + constraintId + constraintName + type results { - constraintId - constraintName resourceIds - type gaps { end start diff --git a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/http/ResponseSerializers.java b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/http/ResponseSerializers.java index 326c84a499..560727f92d 100644 --- a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/http/ResponseSerializers.java +++ b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/http/ResponseSerializers.java @@ -18,6 +18,7 @@ import gov.nasa.jpl.aerie.merlin.server.exceptions.NoSuchPlanDatasetException; import gov.nasa.jpl.aerie.merlin.server.exceptions.NoSuchPlanException; import gov.nasa.jpl.aerie.merlin.server.exceptions.SimulationDatasetMismatchException; +import gov.nasa.jpl.aerie.merlin.server.models.Constraint; import gov.nasa.jpl.aerie.merlin.server.remotes.MissionModelAccessException; import gov.nasa.jpl.aerie.merlin.server.services.ConstraintsDSLCompilationService; import gov.nasa.jpl.aerie.merlin.server.services.GetSimulationResultsAction; @@ -216,11 +217,8 @@ public static JsonValue serializeConstraintViolation(final Violation violation) public static JsonValue serializeConstraintResult(final ConstraintResult list) { return Json .createObjectBuilder() - .add("constraintId", list.constraintId) - .add("constraintName", list.constraintName) .add("violations", serializeIterable(ResponseSerializers::serializeConstraintViolation, list.violations)) .add("gaps", serializeIterable(ResponseSerializers::serializeInterval, list.gaps)) - .add("type", list.constraintType.name()) .add("resourceIds", serializeIterable(Json::createValue, list.resourceIds)) .build(); } @@ -315,16 +313,20 @@ public static JsonValue serializeResourceSamples(final Map> list) { - var results = list.stream().map(failable -> { + public static JsonValue serializeConstraintResults(final Map> resultMap) { + var results = resultMap.entrySet().stream().map(entry -> { - var failableObject = failable.getOptional(); + final var constraint = entry.getKey(); + final var failable = entry.getValue(); // There should always be a failable but this is here // just in case - if (failableObject.isEmpty()) { + if (failable.getOptional().isEmpty()) { return Json.createObjectBuilder() .add("success", JsonValue.FALSE) + .add("constraintId", constraint.id()) + .add("constraintName", constraint.name()) + .add("type",constraint.type().name()) .add("errors", Json.createArrayBuilder().add( Json.createObjectBuilder() .add("message", "Internal error processing a constraint") @@ -336,17 +338,20 @@ public static JsonValue serializeConstraintResults(final List> list) // failure was a compilation error if (failable.isFailure() - && failableObject.get() instanceof ConstraintsDSLCompilationService.ConstraintsDSLCompilationResult.Error) { - return serializeConstraintCompileErrors((Failable) failable); + && failable.getOptional().get() instanceof ConstraintsDSLCompilationService.ConstraintsDSLCompilationResult.Error) { + return serializeConstraintCompileErrors(constraint, (Failable) failable); } // failure that are errors exceptions that were captured if (failable.isFailure()) { return Json.createObjectBuilder() .add("success", JsonValue.FALSE) + .add("constraintId", constraint.id()) + .add("constraintName", constraint.name()) + .add("type",constraint.type().name()) .add("errors", Json.createArrayBuilder().add( Json.createObjectBuilder() - .add("message", ((Error) failableObject.get()).getMessage()) + .add("message", failable.getMessage()) .add("stack", "") .add("location", JsonValue.EMPTY_JSON_OBJECT).build()).build()) .add("results", JsonValue.EMPTY_JSON_OBJECT) @@ -354,9 +359,12 @@ public static JsonValue serializeConstraintResults(final List> list) } // successful runs - var constraintResult = (ConstraintResult) failableObject.get(); + var constraintResult = (ConstraintResult) failable.getOptional().get(); return Json.createObjectBuilder() .add("success", JsonValue.TRUE) + .add("constraintId", constraint.id()) + .add("constraintName", constraint.name()) + .add("type",constraint.type().name()) .add("errors", JsonValue.EMPTY_JSON_ARRAY) .add("results", serializeConstraintResult(constraintResult)) .build(); @@ -483,7 +491,7 @@ public static JsonValue serializeInvalidJsonException(final InvalidJsonException .build(); } - public static JsonValue serializeConstraintCompileErrors(final Failable ex) { + public static JsonValue serializeConstraintCompileErrors(final Constraint constraint, final Failable ex) { final var userCodeError = ex .getOptional() @@ -504,6 +512,9 @@ public static JsonValue serializeConstraintCompileErrors(final Failable> getViolations(final PlanId planId, final Optional simulationDatasetId) + public Map> getViolations(final PlanId planId, final Optional simulationDatasetId) throws NoSuchPlanException, MissionModelService.NoSuchMissionModelException, SimulationDatasetMismatchException { final var plan = this.planService.getPlanForValidation(planId); @@ -61,7 +61,7 @@ public List> getViolations(final PlanId planId, final Optional(); - final var constraintResultMap = new HashMap>(); + final var constraintResultMap = new HashMap>(); try { constraintCode.putAll(this.missionModelService.getConstraints(plan.missionModelId)); @@ -78,11 +78,7 @@ public List> getViolations(final PlanId planId, final Optional> getViolations(final PlanId planId, final Optional> getViolations(final PlanId planId, final Optional() {{ @@ -226,7 +222,7 @@ public List> getViolations(final PlanId planId, final Optional> getViolations(final PlanId planId, final Optional> getViolations(final PlanId planId, final Optional entry.getKey().id(), set -> (ConstraintResult) set .getValue() .getOptional() @@ -282,6 +278,6 @@ public List> getViolations(final PlanId planId, final Optional