Skip to content

Commit baa01d8

Browse files
Add support for procedural constraint/goal effective arguments
1 parent 184b752 commit baa01d8

File tree

19 files changed

+422
-6
lines changed

19 files changed

+422
-6
lines changed

deployment/hasura/metadata/actions.graphql

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,18 @@ type Query {
101101
): [EffectiveArgumentsResponse!]!
102102
}
103103

104+
type Query {
105+
getConstraintProcedureEffectiveArgumentsBulk(
106+
arguments: [ProcedureEffectiveArgumentsQuery!]
107+
): [ProcedureEffectiveArgumentsResponse!]!
108+
}
109+
110+
type Query {
111+
getSchedulingProcedureEffectiveArgumentsBulk(
112+
arguments: [ProcedureEffectiveArgumentsQuery!]
113+
): [ProcedureEffectiveArgumentsResponse!]!
114+
}
115+
104116
type Query {
105117
getActivityTypeScript(missionModelId: Int!, activityTypeName: String!): DslTypescriptResponse
106118
}
@@ -213,6 +225,20 @@ type EffectiveArgumentsResponse {
213225
typeName: String
214226
}
215227

228+
input ProcedureEffectiveArgumentsQuery {
229+
id: Int!
230+
revision: Int!
231+
arguments: ProcedureArguments
232+
}
233+
234+
type ProcedureEffectiveArgumentsResponse {
235+
success: Boolean!
236+
arguments: ProcedureArguments
237+
errors: [String!]
238+
id: Int!
239+
revision: Int!
240+
}
241+
216242
type AddExternalDatasetResponse {
217243
datasetId: Int!
218244
}
@@ -341,6 +367,8 @@ scalar ModelArguments
341367

342368
scalar ActivityArguments
343369

370+
scalar ProcedureArguments
371+
344372
scalar ProfileSet
345373

346374
scalar SchedulingFailureReason

deployment/hasura/metadata/actions.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ actions:
9898
- role: aerie_admin
9999
- role: user
100100
- role: viewer
101+
- name: getConstraintProcedureEffectiveArgumentsBulk
102+
definition:
103+
kind: ""
104+
handler: "{{AERIE_MERLIN_URL}}/getConstraintProcedureEffectiveArgumentsBulk"
105+
timeout: 300
106+
permissions:
107+
- role: aerie_admin
108+
- role: user
109+
- role: viewer
101110
- name: getActivityTypeScript
102111
definition:
103112
kind: ""
@@ -157,6 +166,15 @@ actions:
157166
permissions:
158167
- role: aerie_admin
159168
- role: user
169+
- name: getSchedulingProcedureEffectiveArgumentsBulk
170+
definition:
171+
kind: ""
172+
handler: "{{AERIE_MERLIN_URL}}/getSchedulingProcedureEffectiveArgumentsBulk"
173+
timeout: 300
174+
permissions:
175+
- role: aerie_admin
176+
- role: user
177+
- role: viewer
160178
- name: constraintsDslTypescript
161179
definition:
162180
kind: ""

e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/EffectiveArgumentsTests.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,17 @@ void afterEach() throws IOException {
6969
hasura.deleteMissionModel(modelId);
7070
}
7171

72+
@Nested
73+
class ProceduralConstraintsArguments {
74+
75+
}
76+
77+
@Nested
78+
class ProceduralSchedulingArguments {
79+
80+
}
81+
82+
7283
@Nested
7384
class ModelEffectiveArguments {
7485
@Test

merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/http/MerlinBindings.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
import java.util.Map;
3131
import java.util.stream.Collectors;
3232

33+
import static gov.nasa.jpl.aerie.json.BasicParsers.listP;
34+
import static gov.nasa.jpl.aerie.merlin.server.http.MerlinParsers.constraintArgumentsP;
3335
import static gov.nasa.jpl.aerie.merlin.server.http.MerlinParsers.parseJson;
3436

3537
import static gov.nasa.jpl.aerie.merlin.server.http.HasuraParsers.hasuraActivityActionP;
@@ -108,6 +110,7 @@ public void apply(final Javalin javalin) {
108110
path("extendExternalDataset", () -> post(this::extendExternalDataset));
109111
path("constraintsDslTypescript", () -> post(this::getConstraintsDslTypescript));
110112
path("refreshConstraintProcedureParameterTypes", () -> post(this::refreshConstrainProcedureParameterTypes));
113+
path("getConstraintProcedureEffectiveArgumentsBulk", () -> post(this::getConstraintProcedureEffectiveArgumentsBulk));
111114
path("health", () -> get(ctx -> ctx.status(200)));
112115
});
113116

@@ -439,6 +442,20 @@ private void getActivityEffectiveArgumentsBulk(final Context ctx) {
439442
}
440443
}
441444

445+
private void getConstraintProcedureEffectiveArgumentsBulk(final Context ctx) {
446+
try {
447+
final var input = parseJson(ctx.body(), listP(constraintArgumentsP()));
448+
final var responses = this.constraintAction.getSchedulingProcedureEffectiveArguments(input);
449+
ctx.result(ResponseSerializers.serializeIterable(
450+
ResponseSerializers::serializeConstraintBulkEffectiveArgumentResponse, responses).toString());
451+
} catch (final InvalidJsonException ex) {
452+
ctx.status(400).result(ResponseSerializers.serializeInvalidJsonException(ex).toString());
453+
} catch (final InvalidEntityException ex) {
454+
ctx.status(400).result(ResponseSerializers.serializeInvalidEntityException(ex).toString());
455+
}
456+
}
457+
458+
442459
private void addExternalDataset(final Context ctx) {
443460
try {
444461
final var body = parseJson(ctx.body(), hasuraUploadExternalDatasetActionP);

merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/http/MerlinParsers.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import gov.nasa.jpl.aerie.json.SchemaCache;
66
import gov.nasa.jpl.aerie.merlin.driver.SimulationFailure;
77
import gov.nasa.jpl.aerie.merlin.protocol.types.Duration;
8+
import gov.nasa.jpl.aerie.merlin.server.models.ConstraintArguments;
9+
import gov.nasa.jpl.aerie.merlin.server.models.ConstraintId;
810
import gov.nasa.jpl.aerie.merlin.server.models.DatasetId;
911
import gov.nasa.jpl.aerie.merlin.server.models.PlanId;
1012
import gov.nasa.jpl.aerie.merlin.server.models.SimulationDatasetId;
@@ -24,6 +26,7 @@
2426
import static gov.nasa.jpl.aerie.json.BasicParsers.*;
2527
import static gov.nasa.jpl.aerie.json.Uncurry.tuple;
2628
import static gov.nasa.jpl.aerie.json.Uncurry.untuple;
29+
import static gov.nasa.jpl.aerie.merlin.driver.json.SerializedValueJsonParser.serializedValueP;
2730
import static gov.nasa.jpl.aerie.merlin.server.remotes.postgres.PostgresParsers.pgTimestampP;
2831

2932
public abstract class MerlinParsers {
@@ -107,6 +110,26 @@ public JsonValue unparse(final Timestamp value) {
107110
failure -> tuple(failure.type(), failure.message(), failure.data(), Optional.ofNullable(failure.trace()), new Timestamp(failure.timestamp()))
108111
);
109112

113+
public static JsonParser<ConstraintArguments> constraintArgumentsP() {
114+
return productP
115+
.field("constraint_id", constraintIdP())
116+
.field("arguments", mapP(serializedValueP))
117+
.map(
118+
untuple(ConstraintArguments::new),
119+
constraintArguments -> tuple(constraintArguments.constraintId(), constraintArguments.arguments()));
120+
}
121+
122+
123+
public static JsonParser<ConstraintId> constraintIdP() {
124+
return productP
125+
.field("constraint_id", longP)
126+
.field("revision", longP)
127+
.map(
128+
untuple(ConstraintId::new),
129+
constraintArguments -> tuple(constraintArguments.id(), constraintArguments.revision()));
130+
}
131+
132+
110133
public static <T> T parseJson(final String subject, final JsonParser<T> parser)
111134
throws InvalidJsonException, InvalidEntityException
112135
{

merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/http/ResponseSerializers.java

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import gov.nasa.jpl.aerie.constraints.InputMismatchException;
44
import gov.nasa.jpl.aerie.constraints.model.ConstraintResult;
55
import gov.nasa.jpl.aerie.json.JsonParseResult.FailureReason;
6+
import gov.nasa.jpl.aerie.merlin.server.models.ConstraintId;
67
import gov.nasa.jpl.aerie.merlin.server.models.ConstraintRecord;
78
import gov.nasa.jpl.aerie.merlin.driver.json.ValueSchemaJsonParser;
89
import gov.nasa.jpl.aerie.merlin.protocol.model.InputType.Parameter;
@@ -15,7 +16,9 @@
1516
import gov.nasa.jpl.aerie.merlin.server.exceptions.NoSuchPlanException;
1617
import gov.nasa.jpl.aerie.merlin.server.exceptions.SimulationDatasetMismatchException;
1718
import gov.nasa.jpl.aerie.merlin.server.models.ConstraintsCompilationError;
19+
import gov.nasa.jpl.aerie.merlin.server.models.ProcedureLoader;
1820
import gov.nasa.jpl.aerie.merlin.server.remotes.MissionModelAccessException;
21+
import gov.nasa.jpl.aerie.merlin.server.services.BulkConstraintEffectiveArgumentResponse;
1922
import gov.nasa.jpl.aerie.merlin.server.services.GetSimulationResultsAction;
2023
import gov.nasa.jpl.aerie.merlin.server.services.LocalMissionModelService;
2124
import gov.nasa.jpl.aerie.merlin.server.services.MissionModelService;
@@ -117,6 +120,57 @@ public static JsonValue serializeBulkEffectiveArgumentResponseList(final List<Bu
117120
return serializeIterable(ResponseSerializers::serializeBulkEffectiveArgumentResponse, responses);
118121
}
119122

123+
public static JsonValue serializeConstraintBulkEffectiveArgumentResponse(BulkConstraintEffectiveArgumentResponse response) {
124+
if (response instanceof BulkConstraintEffectiveArgumentResponse.Success(
125+
ConstraintId constraintId, Map<String, SerializedValue> effectiveArguments)) {
126+
return Json.createObjectBuilder()
127+
.add("id", constraintId.id())
128+
.add("revision", constraintId.revision())
129+
.add("success", JsonValue.TRUE)
130+
.add(
131+
"arguments",
132+
serializeMap(
133+
ResponseSerializers::serializeArgument,
134+
effectiveArguments))
135+
.build();
136+
} else if (response instanceof BulkConstraintEffectiveArgumentResponse.TypeFailure(ConstraintId constraintId)) {
137+
return Json.createObjectBuilder()
138+
.add("id", constraintId.id())
139+
.add("revision", constraintId.revision())
140+
.add("success", JsonValue.FALSE)
141+
.add("errors", "Goal is not procedural")
142+
.build();
143+
} else if (response instanceof BulkConstraintEffectiveArgumentResponse.InstantiationFailure(
144+
ConstraintId constraintId,
145+
InstantiationException ex)) {
146+
return Json.createObjectBuilder(
147+
serializeInstantiationException(ex).asJsonObject())
148+
.add("success", JsonValue.FALSE)
149+
.add("id", constraintId.id())
150+
.add("revision", constraintId.revision())
151+
.build();
152+
} else if (response instanceof BulkConstraintEffectiveArgumentResponse.NoGoalFailure(ConstraintId constraintId)) {
153+
return Json.createObjectBuilder()
154+
.add("success", JsonValue.FALSE)
155+
.add("id", constraintId.id())
156+
.add("revision", constraintId.revision())
157+
.add("errors", "There is no goal with this id")
158+
.build();
159+
} else if (response instanceof BulkConstraintEffectiveArgumentResponse.ProcedureLoadFailure(
160+
ConstraintId constraintId, ProcedureLoader.ProcedureLoadException ex)) {
161+
return Json.createObjectBuilder()
162+
.add("success", JsonValue.FALSE)
163+
.add("id", constraintId.id())
164+
.add("revision", constraintId.revision())
165+
.add("errors", "Error when loading the procedure jar")
166+
.build();
167+
}
168+
return Json.createObjectBuilder()
169+
.add("success", JsonValue.FALSE)
170+
.add("errors", String.format("Internal error: %s", response))
171+
.build();
172+
}
173+
120174
public static JsonValue serializeBulkEffectiveArgumentResponse(BulkEffectiveArgumentResponse response) {
121175
// TODO use pattern matching in switch statement with JDK 21
122176
if (response instanceof BulkEffectiveArgumentResponse.Success s) {
@@ -243,10 +297,10 @@ public static JsonValue serializeConstraintResults(final int requestId, final Ma
243297
if (fallible.isFailure()) {
244298
return Json.createObjectBuilder()
245299
.add("success", JsonValue.FALSE)
246-
.add("constraintId", constraint.constraintId())
300+
.add("id", constraint.constraintId())
247301
.add("constraintInvocationId", constraint.invocationId())
248302
.add("constraintName", constraint.name())
249-
.add("constraintRevision", constraint.revision())
303+
.add("revision", constraint.revision())
250304
.add("errors", serializeConstraintErrors(fallible.getFailureOptional().orElse(List.of())))
251305
.add("results", JsonValue.EMPTY_JSON_OBJECT)
252306
.build();
@@ -256,10 +310,10 @@ public static JsonValue serializeConstraintResults(final int requestId, final Ma
256310
if (fallible.getOptional().isEmpty()) {
257311
return Json.createObjectBuilder()
258312
.add("success", JsonValue.FALSE)
259-
.add("constraintId", constraint.constraintId())
313+
.add("id", constraint.constraintId())
260314
.add("constraintInvocationId", constraint.invocationId())
261315
.add("constraintName", constraint.name())
262-
.add("constraintRevision", constraint.revision())
316+
.add("revision", constraint.revision())
263317
.add("errors", Json.createArrayBuilder().add(
264318
Json.createObjectBuilder()
265319
.add("message", "Internal error processing a constraint")
@@ -273,10 +327,10 @@ public static JsonValue serializeConstraintResults(final int requestId, final Ma
273327
var constraintResult = (ConstraintResult) fallible.getOptional().get();
274328
return Json.createObjectBuilder()
275329
.add("success", JsonValue.TRUE)
276-
.add("constraintId", constraint.constraintId())
330+
.add("id", constraint.constraintId())
277331
.add("constraintInvocationId", constraint.invocationId())
278332
.add("constraintName", constraint.name())
279-
.add("constraintRevision", constraint.revision())
333+
.add("revision", constraint.revision())
280334
.add("errors", JsonValue.EMPTY_JSON_ARRAY)
281335
.add("results", constraintResult.toJSON())
282336
.build();
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package gov.nasa.jpl.aerie.merlin.server.models;
2+
3+
import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue;
4+
5+
import java.util.Map;
6+
7+
public record ConstraintArguments(
8+
ConstraintId constraintId,
9+
Map<String, SerializedValue> arguments) {}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package gov.nasa.jpl.aerie.merlin.server.models;
2+
3+
public record ConstraintId(long id, long revision) { }
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package gov.nasa.jpl.aerie.merlin.server.services;
2+
3+
import gov.nasa.jpl.aerie.merlin.protocol.types.InstantiationException;
4+
import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue;
5+
import gov.nasa.jpl.aerie.merlin.server.models.ConstraintId;
6+
import gov.nasa.jpl.aerie.merlin.server.models.ProcedureLoader;
7+
8+
import java.util.Map;
9+
10+
public sealed interface BulkConstraintEffectiveArgumentResponse {
11+
record Success(ConstraintId constraintId, Map<String, SerializedValue> effectiveArguments) implements BulkConstraintEffectiveArgumentResponse { }
12+
record NoGoalFailure(ConstraintId constraintId) implements BulkConstraintEffectiveArgumentResponse { }
13+
record InstantiationFailure(ConstraintId constraintId, InstantiationException ex) implements BulkConstraintEffectiveArgumentResponse { }
14+
record TypeFailure(ConstraintId constraintId) implements BulkConstraintEffectiveArgumentResponse { }
15+
record ProcedureLoadFailure(ConstraintId constraintId, ProcedureLoader.ProcedureLoadException ex) implements BulkConstraintEffectiveArgumentResponse { }
16+
}

merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/services/ConstraintAction.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package gov.nasa.jpl.aerie.merlin.server.services;
22

3+
import gov.nasa.ammos.aerie.procedural.constraints.ProcedureMapper;
34
import gov.nasa.jpl.aerie.constraints.InputMismatchException;
45
import gov.nasa.jpl.aerie.constraints.model.DiscreteProfile;
56
import gov.nasa.jpl.aerie.constraints.model.*;
67
import gov.nasa.jpl.aerie.constraints.tree.Expression;
8+
import gov.nasa.jpl.aerie.merlin.protocol.types.InstantiationException;
79
import gov.nasa.jpl.aerie.merlin.server.exceptions.NoSuchPlanException;
810
import gov.nasa.jpl.aerie.merlin.server.exceptions.SimulationDatasetMismatchException;
911
import gov.nasa.jpl.aerie.merlin.server.http.Fallible;
@@ -40,6 +42,37 @@ public void refreshConstraintProcedureParameterTypes(long constraintId, long rev
4042
constraintService.refreshConstraintProcedureParameterTypes(constraintId, revision);
4143
}
4244

45+
public List<BulkConstraintEffectiveArgumentResponse> getSchedulingProcedureEffectiveArguments(
46+
List<ConstraintArguments> procedureArgumentsList) {
47+
final var responses = new ArrayList<BulkConstraintEffectiveArgumentResponse>();
48+
final var constraints = this.planService.getConstraintsById(procedureArgumentsList.stream().map(p -> new ConstraintId(p.constraintId().id(), p.constraintId().revision())).toList());
49+
for (final var procedureArguments : procedureArgumentsList) {
50+
final var constraint = constraints.get(procedureArguments.constraintId());
51+
switch (constraint.type()) {
52+
case ConstraintType.EDSL e -> {
53+
responses.add(new BulkConstraintEffectiveArgumentResponse.TypeFailure(procedureArguments.constraintId()));
54+
}
55+
case ConstraintType.JAR j -> {
56+
final ProcedureMapper<?> procedureMapper;
57+
try {
58+
procedureMapper = ProcedureLoader.loadProcedure(j.path());
59+
60+
responses.add(new BulkConstraintEffectiveArgumentResponse.Success(
61+
procedureArguments.constraintId(),
62+
procedureMapper.getInputType().getEffectiveArguments(procedureArguments.arguments())));
63+
64+
} catch (InstantiationException e) {
65+
responses.add(new BulkConstraintEffectiveArgumentResponse.InstantiationFailure(procedureArguments.constraintId(), e));
66+
} catch (ProcedureLoader.ProcedureLoadException e) {
67+
responses.add(new BulkConstraintEffectiveArgumentResponse.ProcedureLoadFailure(procedureArguments.constraintId(), e));
68+
}
69+
}
70+
}
71+
}
72+
return responses;
73+
}
74+
75+
4376
/**
4477
* Check the constraints on a plan's specification for violations.
4578
*

0 commit comments

Comments
 (0)