Skip to content

chore: remove storage from REST - #2530

Open
rsynek wants to merge 5 commits into
TimefoldAI:mainfrom
rsynek:chore/disconnect-rest-from-storage
Open

chore: remove storage from REST#2530
rsynek wants to merge 5 commits into
TimefoldAI:mainfrom
rsynek:chore/disconnect-rest-from-storage

Conversation

@rsynek

@rsynek rsynek commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Fixes https://github.com/TimefoldAI/timefold-solver-enterprise/issues/591.

  • REST no longer directly uses storage
  • the logic coordinating storage and events moved to a separate service, SolverWorkerFacade
  • SolverWorkerFacade is now unit-tested
  • one fix along the way: from-patch used to catch IllegalStateException, but the patching utils throw IllegalArgumentException

@rsynek
rsynek requested a review from winklerm July 24, 2026 12:47
@rsynek
rsynek force-pushed the chore/disconnect-rest-from-storage branch from 22c0f7d to 1f33d50 Compare July 24, 2026 12:49
Comment thread service/worker/pom.xml
Comment thread service/worker/pom.xml

@winklerm winklerm left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, looks much better, the API is clearly defined - love that!

@rsynek
rsynek force-pushed the chore/disconnect-rest-from-storage branch from 1f33d50 to 953f87c Compare July 27, 2026 09:11
@rsynek

rsynek commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

@LDWNS the failing downstream ESS is caused by assignability analysis using the storage service, which is being removed from the REST endpoint by this PR.

Options:

  • for now: try injecting the storage service directly
  • long-term: part of assignability analysis should move to solver-service, so the model won't need to access the storage at all

@rsynek
rsynek marked this pull request as ready for review July 27, 2026 11:48
@rsynek
rsynek requested a review from triceo as a code owner July 27, 2026 11:48
Copilot AI review requested due to automatic review settings July 27, 2026 11:48
@rsynek

rsynek commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

The code coverage is still below the standards, but this PR actually increases it for the solver-worker module.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR decouples the REST layer from direct storage/messaging usage by introducing a SolverWorkerFacade abstraction that orchestrates storage operations and solver-worker lifecycle events/commands, and updates Quarkus resource generation accordingly.

Changes:

  • Introduce SolverWorkerFacade (interface) and DefaultSolverWorkerFacade (implementation) to centralize dataset lifecycle orchestration (create/derive/patch/solve/terminate/logs).
  • Refactor REST resources and Quarkus-generated resource constructor signatures to depend on SolverWorkerFacade instead of storage + channel emitters.
  • Add unit tests and test utilities for the new facade, and update dependencies to include the JSON patch module.

Reviewed changes

Copilot reviewed 25 out of 25 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
service/worker/src/test/java/ai/timefold/solver/service/worker/impl/testutil/RecordingMutinyEmitter.java Adds a test emitter that records Mutiny payloads for facade unit tests.
service/worker/src/test/java/ai/timefold/solver/service/worker/impl/testutil/RecordingEmitter.java Adds a test emitter that records reactive-messaging payloads for facade unit tests.
service/worker/src/test/java/ai/timefold/solver/service/worker/impl/testdata/TestdataStorageService.java Provides a real AbstractStorageService implementation for worker facade tests.
service/worker/src/test/java/ai/timefold/solver/service/worker/impl/testdata/TestdataStorage.java Adds an in-memory Storage implementation to avoid mocked storage in tests.
service/worker/src/test/java/ai/timefold/solver/service/worker/impl/testdata/TestdataModelOutputMetrics.java Minimal test model output metrics type for storage/facade tests.
service/worker/src/test/java/ai/timefold/solver/service/worker/impl/testdata/TestdataModelOutput.java Minimal test model output type for storage/facade tests.
service/worker/src/test/java/ai/timefold/solver/service/worker/impl/testdata/TestdataModelInputMetrics.java Minimal test model input metrics type for storage/facade tests.
service/worker/src/test/java/ai/timefold/solver/service/worker/impl/testdata/TestdataModelInput.java Minimal test model input with equality for asserting storage writes in tests.
service/worker/src/test/java/ai/timefold/solver/service/worker/impl/testdata/TestdataModelConstraintJustification.java Minimal constraint justification type for the generic storage service.
service/worker/src/test/java/ai/timefold/solver/service/worker/impl/testdata/TestdataModelConfigOverrides.java Minimal config overrides type used by the test storage service.
service/worker/src/test/java/ai/timefold/solver/service/worker/impl/DefaultSolverWorkerFacadeTest.java Adds unit tests validating facade orchestration and persistence behavior.
service/worker/src/main/java/ai/timefold/solver/service/worker/impl/DefaultSolverWorkerFacade.java New worker-side implementation coordinating storage with lifecycle events/commands.
service/worker/pom.xml Adjusts worker dependencies to include the JSON patch module and Jackson support.
service/test-model/src/test/java/ai/timefold/solver/model/testmodel/EmployeeScheduleResourceTest.java Fixes import location for OperationOnPost after package reorganization.
service/rest/src/main/java/ai/timefold/solver/service/rest/impl/AbstractModelAPIResource.java Refactors REST endpoints to delegate lifecycle operations to SolverWorkerFacade.
service/rest/src/main/java/ai/timefold/solver/service/rest/api/ModelRest.java Removes REST extension hook exposing storage directly.
service/quarkus/deployment/src/main/java/ai/timefold/solver/service/quarkus/deployment/rest/ModelResourceWithMapsTypeInfo.java Updates generated resource constructor signature to inject SolverWorkerFacade.
service/quarkus/deployment/src/main/java/ai/timefold/solver/service/quarkus/deployment/rest/ModelResourceTypeInfo.java Removes channel-annotation plumbing now that emitters are no longer injected.
service/quarkus/deployment/src/main/java/ai/timefold/solver/service/quarkus/deployment/rest/ModelResourceBeanGenerator.java Updates generation logic (and example) to align with new constructor signatures.
service/quarkus/deployment/src/main/java/ai/timefold/solver/service/quarkus/deployment/rest/EnterpriseModelResourceWithMapsTypeInfo.java Updates enterprise generated resource constructor signature to inject SolverWorkerFacade.
service/quarkus/deployment/src/main/java/ai/timefold/solver/service/quarkus/deployment/rest/EnterpriseModelResourceTypeInfo.java Updates enterprise generated resource constructor signature to inject SolverWorkerFacade.
service/quarkus/deployment/src/main/java/ai/timefold/solver/service/quarkus/deployment/rest/DefaultModelResourceTypeInfo.java Updates default generated resource constructor signature to inject SolverWorkerFacade.
service/maps/service-rest/src/main/java/ai/timefold/solver/service/maps/service/rest/impl/AbstractMapsModelAPIResource.java Aligns maps REST resource constructor to the new SolverWorkerFacade injection model.
service/json/src/main/java/ai/timefold/solver/service/json/internal/patch/JsonPatch.java Minor cleanup using isEmpty() and String.isEmpty() in patch logic.
service/definition/src/main/java/ai/timefold/solver/service/definition/impl/solver/SolverWorkerFacade.java Adds the new facade interface used by REST and implemented by the worker.
Comments suppressed due to low confidence (2)

service/definition/src/main/java/ai/timefold/solver/service/definition/impl/solver/SolverWorkerFacade.java:129

  • The terminate(..) Javadoc has @param and @return on the same line, which can cause Javadoc generation to fail with failOnError=true.
    /**
     * Terminates the ongoing solve for the given dataset (if any) and returns the current model response
     * 
     * @param id the dataset identifier * @return the {@link ModelResponse} for the dataset after termination
     */

service/definition/src/main/java/ai/timefold/solver/service/definition/impl/solver/SolverWorkerFacade.java:163

  • The getModelInput(..) Javadoc block contains stray '*' characters and inline tags that can break Javadoc generation when failOnError=true.
    /**
     * * Returns the {@link ModelInput} originally submitted for the given dataset. * * @param id the dataset identifier
     * * @return the model input, or {@code null} if not found
     */

Copilot AI review requested due to automatic review settings July 27, 2026 14:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

service/worker/src/main/java/ai/timefold/solver/service/worker/impl/DefaultSolverWorkerFacade.java:296

  • createPatchedDataset(...) can throw a NullPointerException if the parent metadata is missing even when the parent input exists, so handle parentRun==null consistently with the earlier not-found checks.
            metadata.setParentId(id);
            // set origin id based on the origin id of the parent data set
            Metadata parentRun = getMetadata(id);
            metadata.setOriginId(parentRun.getOriginId());
            metadata.datasetCreated();

Copilot AI review requested due to automatic review settings July 27, 2026 15:04
@rsynek
rsynek force-pushed the chore/disconnect-rest-from-storage branch from 6da485d to 2d8e7ce Compare July 27, 2026 15:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 2 comments.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 27, 2026 15:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

service/definition/src/main/java/ai/timefold/solver/service/definition/impl/solver/SolverWorkerFacade.java:37

  • The Javadoc for createDataset(String, DatasetSelector, ...) has an extra /** line that makes the comment malformed and can fail the build under strict Javadoc settings; remove the stray /** and keep a single Javadoc block.
    /**
     /**
      * Creates a new dataset derived from an existing one (identified by {@code id}) without starting the solving process.

Copilot AI review requested due to automatic review settings July 28, 2026 08:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (4)

service/definition/src/main/java/ai/timefold/solver/service/definition/impl/solver/SolverWorkerFacade.java:38

  • The Javadoc contains an accidental nested /** line which produces broken documentation output and may trigger doclint failures; remove the extra * /** line so the block starts directly with the description.
    /**
     * /**
     * Creates a new dataset derived from an existing one (identified by {@code id}) without starting the solving process.
     * The resulting dataset is validated and computed asynchronously.

service/definition/src/main/java/ai/timefold/solver/service/definition/impl/solver/SolverWorkerFacade.java:178

  • The getSolvedModelInput Javadoc is duplicated and includes a stray /** line which can cause Javadoc generation warnings/errors; keep a single Javadoc block without the duplicate fragment.
    /**
     * Returns the {@link ModelInput} representing the solved state of the given dataset (i.e. planning entities updated
     * with the best solution).
     * 
     * /**
     * Returns the {@link ModelInput} representing the solved state of the given dataset (i.e. planning entities updated
     * with the best solution).
     * 

service/worker/src/main/java/ai/timefold/solver/service/worker/impl/DefaultSolverWorkerFacade.java:261

  • parentRun is dereferenced without a null-check, so a partial/failed storeProblem write (input present but metadata missing) would cause an NPE; throw ItemNotFoundException when getMetadata(id) returns null.
        // set origin id based on the origin id of the parent data set
        Metadata parentRun = getMetadata(id);
        metadata.setOriginId(parentRun.getOriginId());

service/worker/src/main/java/ai/timefold/solver/service/worker/impl/DefaultSolverWorkerFacade.java:296

  • parentRun is dereferenced without a null-check, so inconsistent storage state could crash patching with an NPE; throw ItemNotFoundException when getMetadata(id) returns null before using getOriginId().
            // set origin id based on the origin id of the parent data set
            Metadata parentRun = getMetadata(id);
            metadata.setOriginId(parentRun.getOriginId());
            metadata.datasetCreated();

Copilot AI review requested due to automatic review settings July 28, 2026 08:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

service/rest/src/main/java/ai/timefold/solver/service/rest/impl/AbstractModelAPIResource.java:281

  • fromPatch() allows an empty/null request body via @Validated(nullable = true, ...) but then dereferences patchRequest through solverWorkerFacade.*, which can cause a NullPointerException at runtime; make the body non-nullable by removing nullable = true (or add an explicit null check and return 400).
            @Validated(nullable = true,
                    operationId = OperationId.FROM_PATCH) ModelInputPatchRequest<ModelConfigurationOverrides_> patchRequest) {

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
53.2% Coverage on New Code (required ≥ 70%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants