feat(op-work-packages): write-model + CreateService over a store#66
Merged
Conversation
Add the WorkPackage write path on top of the #64/#65 model, mirroring the op-journals service house pattern. - NewWorkPackage — the create write-model / input DTO (Deserialize, so an API layer can parse a request body). Required NOT NULL columns + optional priority/assignee/parent/description; to_work_package() maps it onto a fresh domain entity. - WorkPackageStore — async persistence port (insert / get / list_for_project). - WorkPackageService — create (validate subject non-blank -> build -> persist, returns the entity with its assigned id), find (NotFound), list_for_project. - MemoryWorkPackageStore — in-memory impl for tests/prototyping. - WorkPackageError + WorkPackageResult (thiserror). 6 service tests (persist+id, blank-subject rejection, optional-field mapping, NotFound, project filter, write-model deserialize/default) + the 6 model tests = 12 pass. fmt clean; op-work-packages clippy-clean (0 warnings in this crate; the transitive -D warnings op-core lints are pre-existing, untouched). Deps: async-trait + thiserror (runtime), tokio (dev, for #[tokio::test]).
AdaWorldAPI
pushed a commit
that referenced
this pull request
Jul 1, 2026
Add the WorkPackage update write path on top of the #66 CreateService. - UpdateWorkPackage — a sparse patch write-model (Deserialize); each Some field is applied, None leaves the value untouched. (Un-setting a nullable FK to NULL is a documented follow-up — needs Option<Option<_>>.) - WorkPackageStore::update — persist changes to an existing row. - WorkPackageService::update(id, expected_lock_version, changes) — optimistic locking: NotFound if absent, Conflict{expected,actual} if the caller's version is stale, Validation if the patch would blank the subject; on success applies the patch, bumps lock_version, refreshes updated_at, persists, returns the entity. - WorkPackageError::Conflict variant. - MemoryWorkPackageStore::update. 4 new update tests (apply+bump, stale-version conflict, NotFound, blank- subject rejection) -> 16 tests pass. fmt clean; op-work-packages clippy-clean (0 warnings in this crate; transitive op-core -D-warnings lints pre-existing). Follow-up: emit a change event on create/update so the composition layer can journal it (op-work-packages <-> op-journals), without coupling the crate to op-journals.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The WorkPackage write path on top of the #64/#65 model, mirroring the
op-journalsservice house pattern (error enum +Resultalias + asyncStoretrait + service + in-memory store).NewWorkPackage— the create write-model / input DTO (Deserialize, so an API layer can parse a request body straight into it). Required NOT NULL columns + optionalpriority_id/assigned_to_id/parent_id/description;to_work_package()maps it onto a fresh domain entity.WorkPackageStore— async persistence port (insert/get/list_for_project).WorkPackageService—create(validatesubjectnon-blank → build → persist, returns the entity with its assigned id),find(→NotFound),list_for_project.MemoryWorkPackageStore— in-memory impl for tests/prototyping.WorkPackageError+WorkPackageResult(thiserror).Tests
6 service tests (persist + id assignment, blank-subject rejection, optional-field mapping,
NotFound, project filtering, write-model deserialize with optionals defaulting) + the 6 existing model tests = 12 pass.Gate
cargo +1.95 fmt --checkclean;op-work-packagesis clippy-clean (0 warnings in this crate's own code). As on #64,-D warningstransitively surfaces the pre-existingop-corelints (should_implement_trait, unused imports/vars, unreachable patterns) already onmain—op-coreuntouched here.Deps
async-trait+thiserror(runtime),tokio(dev, for#[tokio::test]). Cargo.lock updated with the new edges only.Branch note
Follows the merged #65; branch restarted from the new
main(d716204) per the merged-branch rule, single follow-up commit, no overlap with the walledodoo-rs-transcodebranch.Follow-ups: an
updatepath (set-attributes +lock_versionbump + journal hook);op-canonclass_idwiring on the model; the sqlx-backedWorkPackageStoreinop-db.🤖 Generated with Claude Code
Generated by Claude Code