Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions deployment/hasura/metadata/actions.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,18 @@ type Query {
): [EffectiveArgumentsResponse!]!
}

type Query {
getConstraintProcedureEffectiveArgumentsBulk(
arguments: [ProcedureEffectiveArgumentsInput!]!
): [ProcedureEffectiveArgumentsResponse!]!
}

type Query {
getSchedulingProcedureEffectiveArgumentsBulk(
arguments: [ProcedureEffectiveArgumentsInput!]!
): [ProcedureEffectiveArgumentsResponse!]!
}

type Query {
getActivityTypeScript(missionModelId: Int!, activityTypeName: String!): DslTypescriptResponse
}
Expand Down Expand Up @@ -213,6 +225,20 @@ type EffectiveArgumentsResponse {
typeName: String
}

input ProcedureEffectiveArgumentsInput {
id: Int!
revision: Int!
arguments: ProcedureArguments!
}

type ProcedureEffectiveArgumentsResponse {
success: Boolean!
arguments: ProcedureArguments!
errors: [String!]
id: Int!
revision: Int!
}

type AddExternalDatasetResponse {
datasetId: Int!
}
Expand Down Expand Up @@ -341,6 +367,8 @@ scalar ModelArguments

scalar ActivityArguments

scalar ProcedureArguments

scalar ProfileSet

scalar SchedulingFailureReason
Expand Down
18 changes: 18 additions & 0 deletions deployment/hasura/metadata/actions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,15 @@ actions:
- role: aerie_admin
- role: user
- role: viewer
- name: getConstraintProcedureEffectiveArgumentsBulk
definition:
kind: ""
handler: "{{AERIE_MERLIN_URL}}/getConstraintProcedureEffectiveArgumentsBulk"
timeout: 300
permissions:
- role: aerie_admin
- role: user
- role: viewer
- name: getActivityTypeScript
definition:
kind: ""
Expand Down Expand Up @@ -157,6 +166,15 @@ actions:
permissions:
- role: aerie_admin
- role: user
- name: getSchedulingProcedureEffectiveArgumentsBulk
definition:
kind: ""
handler: "{{AERIE_SCHEDULER_URL}}/getSchedulingProcedureEffectiveArgumentsBulk"
timeout: 300
permissions:
- role: aerie_admin
- role: user
- role: viewer
- name: constraintsDslTypescript
definition:
kind: ""
Expand Down
3 changes: 2 additions & 1 deletion e2e-tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ dependencies {
annotationProcessor project(':procedural:processor')

implementation project(":procedural:scheduling")
implementation project(":procedural:constraints")
implementation project(":procedural:timeline")
implementation project(':merlin-sdk')
implementation project(':type-utils')
Expand Down Expand Up @@ -104,7 +105,7 @@ tasks.create("generateProcedureJarTasks") {
}

files.toList().each { file ->
final nameWithoutExtension = file.name.replace(".java", "")
final nameWithoutExtension = file.name.replace(".java", "").replace("Mapper", "")
final taskName = "buildProcedureJar_${nameWithoutExtension}"

println "Generating ${taskName} task, which will build ${nameWithoutExtension}.jar"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package gov.nasa.jpl.aerie.e2e.procedural.scheduling.procedures;

import gov.nasa.ammos.aerie.procedural.scheduling.annotations.WithDefaults;
import gov.nasa.ammos.aerie.procedural.scheduling.plan.EditablePlan;
import gov.nasa.ammos.aerie.procedural.scheduling.Goal;
import gov.nasa.ammos.aerie.procedural.scheduling.annotations.SchedulingProcedure;
Expand All @@ -17,7 +18,7 @@
* one every 6hrs.
*/
@SchedulingProcedure
public record DumbRecurrenceGoal(int quantity) implements Goal {
public record DumbRecurrenceGoal(int quantity, int biteSize) implements Goal {
@Override
public void run(@NotNull final EditablePlan plan) {
final var firstTime = Duration.hours(24);
Expand All @@ -27,7 +28,7 @@ public void run(@NotNull final EditablePlan plan) {
for (var i = 0; i < quantity; i++) {
plan.create(
new NewDirective(
new AnyDirective(Map.of("biteSize", SerializedValue.of(1))),
new AnyDirective(Map.of("biteSize", SerializedValue.of(biteSize))),
"It's a bite banana activity",
"BiteBanana",
new DirectiveStart.Absolute(currentTime)
Expand All @@ -37,4 +38,11 @@ public void run(@NotNull final EditablePlan plan) {
}
plan.commit();
}

/**
* Default parameters. Quantity is provided but biteSize is not so it is required.
*/
public static @WithDefaults class Defaults {
public int quantity = 360;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package gov.nasa.jpl.aerie.e2e.procedural.scheduling.procedures;

import gov.nasa.ammos.aerie.procedural.scheduling.Goal;
import gov.nasa.ammos.aerie.procedural.scheduling.annotations.SchedulingProcedure;
import gov.nasa.ammos.aerie.procedural.scheduling.annotations.Template;
import gov.nasa.ammos.aerie.procedural.scheduling.plan.EditablePlan;
import gov.nasa.ammos.aerie.procedural.scheduling.plan.NewDirective;
import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.AnyDirective;
import gov.nasa.ammos.aerie.procedural.timeline.payloads.activities.DirectiveStart;
import gov.nasa.jpl.aerie.merlin.protocol.types.Duration;
import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue;
import org.jetbrains.annotations.NotNull;

import java.util.Map;

/**
* Waits 24hrs into the plan, then places `quantity` number of BiteBanana activities,
* one every 6hrs.
*/
@SchedulingProcedure
public record DumbRecurrenceGoalWithTemplateDefaults(int quantity, int biteSize) implements Goal {
@Override
public void run(@NotNull final EditablePlan plan) {
final var firstTime = Duration.hours(24);
final var step = Duration.hours(6);

var currentTime = firstTime;
for (var i = 0; i < quantity; i++) {
plan.create(
new NewDirective(
new AnyDirective(Map.of("biteSize", SerializedValue.of(biteSize))),
"It's a bite banana activity",
"BiteBanana",
new DirectiveStart.Absolute(currentTime)
)
);
currentTime = currentTime.plus(step);
}
plan.commit();
}

public static @Template DumbRecurrenceGoalWithTemplateDefaults create() {
return new DumbRecurrenceGoalWithTemplateDefaults(3, 5);
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package gov.nasa.jpl.aerie.e2e.procedural.scheduling.procedures;

import gov.nasa.ammos.aerie.procedural.constraints.Constraint;
import gov.nasa.ammos.aerie.procedural.constraints.Violations;
import gov.nasa.ammos.aerie.procedural.constraints.annotations.ConstraintProcedure;
import gov.nasa.ammos.aerie.procedural.scheduling.annotations.WithDefaults;
import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Real;
import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan;
import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulationResults;
import org.jetbrains.annotations.NotNull;

@ConstraintProcedure
public record FruitThresholdConstraint(int lowerBound, int upperBound) implements Constraint {
@NotNull
@Override
public Violations run(@NotNull Plan plan, @NotNull SimulationResults simResults) {
final var fruit = simResults.resource("/fruit", Real.deserializer());

return Violations.on(
fruit.lessThan(upperBound).and(fruit.greaterThan(lowerBound)),
false
);
}

@WithDefaults
public static class Template{
public int lowerBound = 5;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package gov.nasa.jpl.aerie.e2e.procedural.constraints;

import gov.nasa.jpl.aerie.e2e.procedural.scheduling.ProceduralSchedulingSetup;
import gov.nasa.jpl.aerie.e2e.types.ConstraintInvocationId;
import gov.nasa.jpl.aerie.e2e.utils.GatewayRequests;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import javax.json.Json;
import java.io.IOException;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class BasicConstraintTests extends ProceduralSchedulingSetup {
private ConstraintInvocationId fruitTresholdConstraintId;

@BeforeEach
void localBeforeEach() throws IOException {
try (final var gateway = new GatewayRequests(playwright)) {
final int fruitTresholdConstraintJarId = gateway.uploadJarFile("build/libs/FruitThresholdConstraint.jar");
// Add Scheduling Procedure
fruitTresholdConstraintId = hasura.createConstraintSpecProcedure(
"Test Constraint Procedure 1",
fruitTresholdConstraintJarId,
planId
);
}
}

@AfterEach
void localAfterEach() throws IOException {
hasura.deleteConstraint(fruitTresholdConstraintId.id());
}

/**
* Run a spec with one procedure in it with required params but no args set
* Should fail because one argument is provided in the template but not the other
*/
@Test
void executeConstraintRunWithoutArguments() throws IOException {
hasura.awaitSimulation(planId);
final var resp = hasura.checkConstraints(planId);
assertEquals(1, resp.constraintsRun().size());
assertEquals(1, resp.constraintsRun().getFirst().errors().size());
resp.constraintsRun().getFirst().errors().getFirst().message().contains("gov.nasa.jpl.aerie.merlin.protocol.types.InstantiationException: Invalid arguments for input type \"FruitThresholdConstraint\": extraneous arguments: [], unconstructable arguments: [], missing arguments: [MissingArgument[parameterName=upperBound, schema=IntSchema[]]], valid arguments: [ValidArgument[parameterName=lowerBound, serializedValue=NumericValue[value=5]]]");
}

/**
* Run a constraint that has one template argument and requires one other argument
*/
@Test
void executeConstraintRunWithArguments() throws IOException {
final var args = Json.createObjectBuilder().add("upperBound", 10).build();
hasura.updateConstraintArguments(fruitTresholdConstraintId.invocationId(), args);
hasura.awaitSimulation(planId);
final var resp = hasura.checkConstraints(planId);
assertTrue(resp.constraintsRun().getFirst().success());
}

/**
* Queries the procedural constraints arguments.
*/
@Test
void effectiveArgumentsQuery() throws IOException {
final var effectiveArgs = hasura.getEffectiveProceduralConstraintsArgumentsBulk(
List.of(Pair.of(fruitTresholdConstraintId.id(), Json.createObjectBuilder().add("upperBound", 10).build())));
assertEquals(1, effectiveArgs.size());
assertTrue(effectiveArgs.get(0).success());
assertTrue(effectiveArgs.get(0).arguments().isPresent());
assertTrue(effectiveArgs.get(0).errors().isEmpty());

// Check returned Arguments
final var args = effectiveArgs.get(0).arguments().get();
assertEquals(2, args.size());
assertEquals(10, args.getInt("upperBound"));
assertEquals(5, args.getInt("lowerBound"));
}
}
Loading
Loading