diff --git a/e2e-tests/src/tests/bindings.test.ts b/e2e-tests/src/tests/bindings.test.ts index 5fcea8bf18..8bf55c4641 100644 --- a/e2e-tests/src/tests/bindings.test.ts +++ b/e2e-tests/src/tests/bindings.test.ts @@ -11,6 +11,7 @@ import * as urls from '../utilities/urls.js'; test.describe('Merlin Bindings', () => { let mission_model_id: number; let plan_id: number; + let second_plan_id: number; let admin: User = { username: "Admin_User", default_role: "aerie_admin", @@ -50,11 +51,16 @@ test.describe('Merlin Bindings', () => { duration : time.getIntervalFromDoyRange(plan_start_timestamp, plan_end_timestamp) }; plan_id = await req.createPlan(request, plan_input, admin.session); + + // Insert the Second Plan + plan_input.name = plan_input.name +' (2)'; + second_plan_id = await req.createPlan(request, plan_input, admin.session); }); test.afterAll(async ({ request }) => { // Remove Model and Plan await req.deleteMissionModel(request, mission_model_id); await req.deletePlan(request, plan_id); + await req.deletePlan(request, second_plan_id); // Remove Users await req.deleteUser(request, admin.username); @@ -201,6 +207,21 @@ test.describe('Merlin Bindings', () => { cause: "no simulation datasets found for plan id " + plan_id }); + // Returns a 404 if given an invalid simulation dataset id + // message is "Simulation Dataset with id `1` does not belong to Plan with id `1` + const invalidSimDatasetId = (await awaitSimulation(request, second_plan_id)).simulationDatasetId; + response = await request.post(`${urls.MERLIN_URL}/constraintViolations`, { + data: { + action: {name: "check_constraints"}, + input: {planId: plan_id, simulationDatasetId: invalidSimDatasetId}, + request_query: "", + session_variables: admin.session}}); + expect(response.status()).toEqual(404); + expect((await response.json())).toEqual({ + message: "input mismatch exception", + cause: "Simulation Dataset with id `" + invalidSimDatasetId + "` does not belong to Plan with id `"+plan_id+"`" + }); + // Simulation already tested; run one // "status" is not "failed" response = await request.post(`${urls.MERLIN_URL}/getSimulationResults`, { diff --git a/e2e-tests/src/tests/constraints-load-old-sim-results.test.ts b/e2e-tests/src/tests/constraints-load-old-sim-results.test.ts new file mode 100644 index 0000000000..521eda3e5d --- /dev/null +++ b/e2e-tests/src/tests/constraints-load-old-sim-results.test.ts @@ -0,0 +1,108 @@ +import { expect, test } from '@playwright/test'; +import req, { awaitSimulation } from '../utilities/requests.js'; + +test.describe.serial('Check Constraints Against Specific Sim Datasets', () => { + const constraintName = 'fruit_equal_peel'; + const activity_offset_hours = 1; + + const plan_start_timestamp = "2023-01-01T00:00:00+00:00"; + + let mission_model_id: number; + let plan_id: number; + let constraint_id: number; + let violation: Violation; + let activity_id: number; + + test.beforeAll(async ({request}) => { + let rd = Math.random() * 100000; + let jar_id = await req.uploadJarFile(request); + // Add Mission Model + const model: MissionModelInsertInput = { + jar_id, + mission: 'aerie_e2e_tests', + name: 'Banananation (e2e tests)', + version: rd + "", + }; + mission_model_id = await req.createMissionModel(request, model); + + // Add Plan + const plan_input: CreatePlanInput = { + model_id: mission_model_id, + name: 'test_plan' + rd, + start_time: plan_start_timestamp, + duration: "24:00:00" + }; + plan_id = await req.createPlan(request, plan_input); + + // Add Activity + const activityToInsert: ActivityInsertInput = { + arguments: { + biteSize: 2, + }, + plan_id: plan_id, + type: 'BiteBanana', + start_offset: activity_offset_hours + 'h', + }; + activity_id = await req.insertActivity(request, activityToInsert); + + // Add Constraint + const constraint: ConstraintInsertInput = { + name: constraintName, + definition: 'export default (): Constraint => Real.Resource("/fruit").equal(Real.Resource("/peel"))', + description: '', + model_id: null, + plan_id, + }; + constraint_id = await req.insertConstraint(request, constraint); + }); + + test.afterAll(async ({request}) => { + // Delete Constraint + await req.deleteConstraint(request, constraint_id); + + // Deleting Plan and Model cascades the rest of the cleanup + await req.deleteMissionModel(request, mission_model_id); + await req.deletePlan(request, plan_id); + }); + + test('Constraints Loads Specified Outdated Sim Dataset', async ({request}) => { + // Simulate + const oldSimDatasetId = (await awaitSimulation(request, plan_id)).simulationDatasetId; + + // Invalidate Results and Resim + await req.deleteActivity(request, plan_id, activity_id); + const newSimulationDatasetId = (await awaitSimulation(request, plan_id)).simulationDatasetId; + + // Assert No Violations on Newest Results + const newConstraintResults = await req.checkConstraints(request, plan_id, newSimulationDatasetId); + expect(newConstraintResults).toHaveLength(0); + + // Assert One Violation on Old Results + const oldConstraintResults = await req.checkConstraints(request, plan_id, oldSimDatasetId); + expect(oldConstraintResults).toHaveLength(1); + + // Assert the Results to be as expected + const result = oldConstraintResults.pop(); + expect(result.constraintName).toEqual(constraintName); + expect(result.constraintId).toEqual(constraint_id); + + // Resources + expect(result.resourceIds).toHaveLength(2); + expect(result.resourceIds).toContain('/fruit'); + expect(result.resourceIds).toContain('/peel'); + + // Violation + expect(result.violations).toHaveLength(1); + + // Violation Window + const plan_duration_micro = 24 * 60 * 60 * 1000 * 1000; + const activity_offset_micro = activity_offset_hours * 60 * 60 * 1000 * 1000; + + violation = result.violations[0]; + expect(violation.windows[0].start).toEqual(activity_offset_micro); + expect(violation.windows[0].end).toEqual(plan_duration_micro); + + // Gaps + expect(result.gaps).toHaveLength(0); + }); +}); diff --git a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/exceptions/SimulationDatasetMismatchException.java b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/exceptions/SimulationDatasetMismatchException.java new file mode 100644 index 0000000000..3cbffcb7cf --- /dev/null +++ b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/exceptions/SimulationDatasetMismatchException.java @@ -0,0 +1,15 @@ +package gov.nasa.jpl.aerie.merlin.server.exceptions; + +import gov.nasa.jpl.aerie.merlin.server.models.PlanId; +import gov.nasa.jpl.aerie.merlin.server.models.SimulationDatasetId; + +public class SimulationDatasetMismatchException extends Exception { + public final SimulationDatasetId simDatasetId; + public final PlanId planId; + + public SimulationDatasetMismatchException(final PlanId pid, final SimulationDatasetId sid) { + super("Simulation Dataset with id `" + sid.id() + "` does not belong to Plan with id `"+pid.id()+"`"); + this.planId = pid; + this.simDatasetId = sid; + } +} diff --git a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/http/MerlinBindings.java b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/http/MerlinBindings.java index ac2ee5c9e3..9b4575e8ce 100644 --- a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/http/MerlinBindings.java +++ b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/http/MerlinBindings.java @@ -6,6 +6,7 @@ import gov.nasa.jpl.aerie.merlin.protocol.types.InstantiationException; 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.services.ConstraintAction; import gov.nasa.jpl.aerie.merlin.server.models.HasuraAction; import gov.nasa.jpl.aerie.merlin.server.models.PlanId; @@ -250,6 +251,8 @@ private void getConstraintViolations(final Context ctx) { ctx.status(404).result(ExceptionSerializers.serializeNoSuchPlanException(ex).toString()); } catch (final InputMismatchException ex) { ctx.status(404).result(ResponseSerializers.serializeInputMismatchException(ex).toString()); + } catch (SimulationDatasetMismatchException ex) { + ctx.status(404).result(ResponseSerializers.serializeSimulationDatasetMismatchException(ex).toString()); } catch (final PermissionsServiceException ex) { ctx.status(503).result(ExceptionSerializers.serializePermissionsServiceException(ex).toString()); } catch (final Unauthorized ex) { 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 8464790776..5a66e90e69 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 @@ -16,6 +16,7 @@ import gov.nasa.jpl.aerie.merlin.protocol.types.ValueSchema; 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.remotes.MissionModelAccessException; import gov.nasa.jpl.aerie.merlin.server.services.GetSimulationResultsAction; import gov.nasa.jpl.aerie.merlin.server.services.LocalMissionModelService; @@ -468,6 +469,13 @@ public static JsonValue serializeInputMismatchException(final InputMismatchExcep .build(); } + public static JsonValue serializeSimulationDatasetMismatchException(final SimulationDatasetMismatchException ex){ + return Json.createObjectBuilder() + .add("message", "input mismatch exception") + .add("cause", ex.getMessage()) + .build(); + } + private static final class ValueSchemaSerializer implements ValueSchema.Visitor { @Override public JsonValue onReal() { diff --git a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/InMemoryResultsCellRepository.java b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/InMemoryResultsCellRepository.java index 5203009ae9..1bcb7d9199 100644 --- a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/InMemoryResultsCellRepository.java +++ b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/InMemoryResultsCellRepository.java @@ -61,6 +61,11 @@ public Optional lookup(final PlanId planId) { } } + @Override + public Optional lookup(final PlanId planId, final SimulationDatasetId simulationDatasetId) { + return lookup(planId); + } + public boolean isEqualTo(final InMemoryResultsCellRepository other) { return this.cells.equals(other.cells); } diff --git a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/ResultsCellRepository.java b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/ResultsCellRepository.java index da70f00389..df80e37b0b 100644 --- a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/ResultsCellRepository.java +++ b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/ResultsCellRepository.java @@ -1,7 +1,9 @@ package gov.nasa.jpl.aerie.merlin.server.remotes; import gov.nasa.jpl.aerie.merlin.server.ResultsProtocol; +import gov.nasa.jpl.aerie.merlin.server.exceptions.SimulationDatasetMismatchException; import gov.nasa.jpl.aerie.merlin.server.models.PlanId; +import gov.nasa.jpl.aerie.merlin.server.models.SimulationDatasetId; import java.util.Optional; @@ -11,4 +13,6 @@ public interface ResultsCellRepository { Optional claim(PlanId planId, Long datasetId); Optional lookup(PlanId planId); + + Optional lookup(PlanId planId, SimulationDatasetId simulationDatasetId) throws SimulationDatasetMismatchException; } diff --git a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/postgres/GetSimulationDatasetByIdAction.java b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/postgres/GetSimulationDatasetByIdAction.java new file mode 100644 index 0000000000..364f810f3a --- /dev/null +++ b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/postgres/GetSimulationDatasetByIdAction.java @@ -0,0 +1,71 @@ +package gov.nasa.jpl.aerie.merlin.server.remotes.postgres; + +import gov.nasa.jpl.aerie.merlin.server.models.Timestamp; +import org.intellij.lang.annotations.Language; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Optional; + +public class GetSimulationDatasetByIdAction implements AutoCloseable { + private static final @Language("Sql") String sql = """ + select + d.simulation_id as simulation_id, + d.status as status, + d.reason as reason, + d.canceled as canceled, + to_char(d.simulation_start_time, 'YYYY-DDD"T"HH24:MI:SS.FF6') as simulation_start_time, + to_char(d.simulation_end_time, 'YYYY-DDD"T"HH24:MI:SS.FF6') as simulation_end_time, + d.dataset_id as dataset_id + from simulation_dataset as d + where + d.id = ? + """; + + private final PreparedStatement statement; + + public GetSimulationDatasetByIdAction(final Connection connection) throws SQLException { + this.statement = connection.prepareStatement(sql); + } + + public Optional get( + final long simulationDatasetId + ) throws SQLException { + this.statement.setLong(1, simulationDatasetId); + + try (final var results = this.statement.executeQuery()) { + if (!results.next()) return Optional.empty(); + + final SimulationStateRecord.Status status; + try { + status = SimulationStateRecord.Status.fromString(results.getString("status")); + } catch (final SimulationStateRecord.Status.InvalidSimulationStatusException ex) { + throw new Error("Simulation Dataset initialized with invalid state."); + } + + final var simulationId = results.getLong("simulation_id"); + final var reason = PreparedStatements.getFailureReason(results, 3); + final var canceled = results.getBoolean("canceled"); + final var state = new SimulationStateRecord(status, reason); + final var simStartTime = Timestamp.fromString(results.getString("simulation_start_time")); + final var simEndTime = Timestamp.fromString(results.getString("simulation_end_time")); + final var datasetId = results.getLong("dataset_id"); + + return Optional.of( + new SimulationDatasetRecord( + simulationId, + datasetId, + state, + canceled, + simStartTime, + simEndTime, + simulationDatasetId)); + } + } + + @Override + public void close() throws SQLException { + this.statement.close(); + } +} diff --git a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/postgres/PostgresConstraintRepository.java b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/postgres/PostgresConstraintRepository.java index ef5d823dc2..13a29000d5 100644 --- a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/postgres/PostgresConstraintRepository.java +++ b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/postgres/PostgresConstraintRepository.java @@ -35,8 +35,9 @@ public void insertConstraintRuns( @Override public Map getValidConstraintRuns(List constraintIds, SimulationDatasetId simulationDatasetId) { - try (final var connection = this.dataSource.getConnection()) { - final var constraintRuns = new GetValidConstraintRunsAction(connection, constraintIds, simulationDatasetId).get(); + try (final var connection = this.dataSource.getConnection(); + final var validConstraintRunAction = new GetValidConstraintRunsAction(connection, constraintIds, simulationDatasetId)) { + final var constraintRuns = validConstraintRunAction.get(); final var validConstraintRuns = new HashMap(); for (final var constraintRun : constraintRuns) { diff --git a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/postgres/PostgresResultsCellRepository.java b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/postgres/PostgresResultsCellRepository.java index 7c020a91c5..5f4b0d1896 100644 --- a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/postgres/PostgresResultsCellRepository.java +++ b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/remotes/postgres/PostgresResultsCellRepository.java @@ -12,6 +12,7 @@ import gov.nasa.jpl.aerie.merlin.protocol.types.ValueSchema; import gov.nasa.jpl.aerie.merlin.server.ResultsProtocol; import gov.nasa.jpl.aerie.merlin.server.ResultsProtocol.State; +import gov.nasa.jpl.aerie.merlin.server.exceptions.SimulationDatasetMismatchException; import gov.nasa.jpl.aerie.merlin.server.models.PlanId; import gov.nasa.jpl.aerie.merlin.server.models.ProfileSet; import gov.nasa.jpl.aerie.merlin.server.models.SimulationDatasetId; @@ -120,14 +121,32 @@ public Optional claim(final PlanId planId, final Long public Optional lookup(final PlanId planId) { try (final var connection = this.dataSource.getConnection()) { final var simulation = getSimulation(connection, planId); - final var datasetRecord = lookupSimulationDatasetRecord( - connection, - simulation.id()); - final var datasetId$ = datasetRecord.map(SimulationDatasetRecord::datasetId); + final var datasetRecord = lookupSimulationDatasetRecord(connection, simulation.id()); + + if (datasetRecord.isEmpty()) return Optional.empty(); - if (datasetId$.isEmpty()) return Optional.empty(); + final var datasetId = datasetRecord.get().datasetId(); + return Optional.of(new PostgresResultsCell(this.dataSource, simulation, datasetId)); + } catch (final SQLException ex) { + throw new DatabaseException("Failed to get simulation", ex); + } + } + + @Override + public Optional lookup(final PlanId planId, final SimulationDatasetId simulationDatasetId) throws SimulationDatasetMismatchException { + try (final var connection = this.dataSource.getConnection()) { + final var simulation = getSimulation(connection, planId); + final var datasetRecord = getSimulationDatasetRecordById(connection, simulationDatasetId.id()); + + if (datasetRecord.isEmpty()) return Optional.empty(); + // If this check fails, then the specified sim dataset is not a simulation for the specified plan + if (datasetRecord.get().simulationId() != simulation.id()) { + throw new SimulationDatasetMismatchException( + planId, + new SimulationDatasetId(datasetRecord.get().simulationDatasetId())); + } - final var datasetId = datasetId$.get(); + final var datasetId = datasetRecord.get().datasetId(); return Optional.of(new PostgresResultsCell(this.dataSource, simulation, datasetId)); } catch (final SQLException ex) { throw new DatabaseException("Failed to get simulation", ex); @@ -165,6 +184,16 @@ private static Optional getSimulationDatasetRecord( } } + private static Optional getSimulationDatasetRecordById( + final Connection connection, + final long simulationDatasetId + ) throws SQLException + { + try (final var lookupSimulationDatasetAction = new GetSimulationDatasetByIdAction(connection)) { + return lookupSimulationDatasetAction.get(simulationDatasetId); + } + } + private static SimulationDatasetRecord createSimulationDataset( final Connection connection, final SimulationRecord simulation, diff --git a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/services/CachedSimulationService.java b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/services/CachedSimulationService.java index 28958aed0b..98f0849a28 100644 --- a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/services/CachedSimulationService.java +++ b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/services/CachedSimulationService.java @@ -1,7 +1,9 @@ package gov.nasa.jpl.aerie.merlin.server.services; import gov.nasa.jpl.aerie.merlin.server.ResultsProtocol; +import gov.nasa.jpl.aerie.merlin.server.exceptions.SimulationDatasetMismatchException; import gov.nasa.jpl.aerie.merlin.server.models.PlanId; +import gov.nasa.jpl.aerie.merlin.server.models.SimulationDatasetId; import gov.nasa.jpl.aerie.merlin.server.models.SimulationResultsHandle; import gov.nasa.jpl.aerie.merlin.server.remotes.ResultsCellRepository; @@ -33,4 +35,13 @@ public Optional get(final PlanId planId, final Revision s.results() : null); } + + @Override + public Optional get(final PlanId planId, final SimulationDatasetId simulationDatasetId) throws SimulationDatasetMismatchException { + return this.store.lookup(planId, simulationDatasetId) // Only return results that have already been cached + .map(ResultsProtocol.ReaderRole::get) + .map(state -> state instanceof final ResultsProtocol.State.Success s ? + s.results() : + null); + } } diff --git a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/services/ConstraintAction.java b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/services/ConstraintAction.java index fbe49780d3..cfffe041e4 100644 --- a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/services/ConstraintAction.java +++ b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/services/ConstraintAction.java @@ -9,6 +9,7 @@ import gov.nasa.jpl.aerie.constraints.time.Interval; import gov.nasa.jpl.aerie.constraints.tree.Expression; import gov.nasa.jpl.aerie.merlin.protocol.types.Duration; +import gov.nasa.jpl.aerie.merlin.server.exceptions.SimulationDatasetMismatchException; import gov.nasa.jpl.aerie.merlin.server.models.SimulationDatasetId; import gov.nasa.jpl.aerie.merlin.server.models.SimulationResultsHandle; import gov.nasa.jpl.aerie.merlin.server.exceptions.NoSuchPlanException; @@ -23,7 +24,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Optional; public class ConstraintAction { @@ -48,10 +48,17 @@ public ConstraintAction( } public List getViolations(final PlanId planId, final Optional simulationDatasetId) - throws NoSuchPlanException, MissionModelService.NoSuchMissionModelException - { + throws NoSuchPlanException, MissionModelService.NoSuchMissionModelException, SimulationDatasetMismatchException { final var plan = this.planService.getPlanForValidation(planId); - final var revisionData = this.planService.getPlanRevisionData(planId); + final Optional resultsHandle$; + if (simulationDatasetId.isPresent()) { + resultsHandle$ = this.simulationService.get(planId, simulationDatasetId.get()); + } else { + final var revisionData = this.planService.getPlanRevisionData(planId); + resultsHandle$ = this.simulationService.get(planId, revisionData); + } + + final var constraintCode = new HashMap(); try { @@ -61,10 +68,9 @@ public List getViolations(final PlanId planId, final Optional< throw new RuntimeException("Assumption falsified -- mission model for existing plan does not exist"); } - final var resultsHandle$ = this.simulationService.get(planId, revisionData); - final var simDatasetId = simulationDatasetId.orElseGet(() -> resultsHandle$ + final var simDatasetId = resultsHandle$ .map(SimulationResultsHandle::getSimulationDatasetId) - .orElseThrow(() -> new InputMismatchException("no simulation datasets found for plan id " + planId.id()))); + .orElseThrow(() -> new InputMismatchException("no simulation datasets found for plan id " + planId.id())); final var violations = new HashMap(); final var validConstraintRuns = this.constraintService.getValidConstraintRuns(constraintCode.values().stream().toList(), simDatasetId); diff --git a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/services/SimulationService.java b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/services/SimulationService.java index 4dcfd660fa..d8b8de2a20 100644 --- a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/services/SimulationService.java +++ b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/services/SimulationService.java @@ -1,12 +1,17 @@ package gov.nasa.jpl.aerie.merlin.server.services; import gov.nasa.jpl.aerie.merlin.server.ResultsProtocol; +import gov.nasa.jpl.aerie.merlin.server.exceptions.SimulationDatasetMismatchException; import gov.nasa.jpl.aerie.merlin.server.models.PlanId; +import gov.nasa.jpl.aerie.merlin.server.models.SimulationDatasetId; import gov.nasa.jpl.aerie.merlin.server.models.SimulationResultsHandle; import java.util.Optional; public interface SimulationService { ResultsProtocol.State getSimulationResults(PlanId planId, RevisionData revisionData, final String requestedBy); + Optional get(PlanId planId, RevisionData revisionData); + + Optional get(PlanId planId, SimulationDatasetId simulationDatasetId) throws SimulationDatasetMismatchException; } diff --git a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/services/UncachedSimulationService.java b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/services/UncachedSimulationService.java index 62ec146f1d..f183ae57fc 100644 --- a/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/services/UncachedSimulationService.java +++ b/merlin-server/src/main/java/gov/nasa/jpl/aerie/merlin/server/services/UncachedSimulationService.java @@ -3,6 +3,7 @@ import gov.nasa.jpl.aerie.merlin.server.ResultsProtocol; import gov.nasa.jpl.aerie.merlin.server.mocks.InMemoryRevisionData; import gov.nasa.jpl.aerie.merlin.server.models.PlanId; +import gov.nasa.jpl.aerie.merlin.server.models.SimulationDatasetId; import gov.nasa.jpl.aerie.merlin.server.models.SimulationResultsHandle; import gov.nasa.jpl.aerie.merlin.server.remotes.InMemoryResultsCellRepository.InMemoryCell; @@ -43,4 +44,9 @@ public Optional get(final PlanId planId, final Revision s.results() : null); } + + @Override + public Optional get(PlanId planId, SimulationDatasetId simulationDatasetId) { + throw new UnsupportedOperationException(); // Cannot get a cached result from an uncached service + } }