Open
Conversation
…k-end layer Implements issue #386 (MDL-01a, API-01a, BE-01a) covering the full vertical slice for unit management: shared library models, API operation definitions, and the complete back-end controller/service/datastore stack. ## Shared library (edifikana/shared) - Add `UnitModel` — client-facing domain model with all unit fields - Add `GetUnitsQueryParams` — query params (org_id, optional property_id) for the list-units endpoint ## API contract (edifikana/api) - Add `UnitApi` — defines five typed operations: createUnit (POST), getUnit (GET /{id}), getUnits (GET ?org_id=&property_id=), updateUnit (PUT /{id}), deleteUnit (DELETE /{id}) ## Back-end server (edifikana/back-end) ### Domain / persistence layer - Add `Unit` service model (mirrors DB schema, includes `createdAt`) - Add `UnitEntity` + `CreateUnitEntity` Supabase entity with soft-delete support - Add `UnitDatastore` interface (createUnit, getUnit, getUnits, updateUnit, deleteUnit) - Add `SupabaseUnitDatastore` implementation: - Queries filter `deleted_at IS NULL` (soft-delete pattern) - updateUnit uses partial PATCH via `UnitEntity.CreateUnitEntity` - deleteUnit sets `deleted_at = clock.now()` instead of hard-deleting - Add mapper functions in `SupabaseMappers`: `CreateUnitEntity(...)` factory and `UnitEntity.toUnit()` extension ### Service layer - Add `UnitService` — thin delegation to `UnitDatastore` with `.getOrThrow()` / `.getOrNull()` unwrapping ### Controller layer - Add `UnitController` with RBAC gates per endpoint: - createUnit → MANAGER or higher (scoped to org) - getUnit → EMPLOYEE or higher (scoped to unit → org lookup) - getUnits → EMPLOYEE or higher (scoped to org) - updateUnit → MANAGER or higher (scoped to unit → org lookup) - deleteUnit → ADMIN or higher (scoped to unit → org lookup) - Add `Unit.toUnitNetworkResponse()` mapper in `NetworkMappers` ### Authorization - Extend `RBACService` with `UnitId`-scoped `hasRole` / `hasRoleOrHigher` overloads; looks up the unit's `orgId` via `UnitDatastore` and delegates to the existing org-level role check (same pattern as DocumentId / PropertyId) - Add `unitDatastore: UnitDatastore` constructor parameter to `RBACService` ### Dependency injection - Register `SupabaseUnitDatastore` → `UnitDatastore` in `DatastoreModule` - Register `UnitService` in `ServicesModule` - Register `UnitController` as `Controller` in `ControllerModule` ## Tests - Add `UnitControllerTest` — 10 tests covering success and auth-failure paths for all five CRUD operations using MockK + Koin test harness - Add JSON fixture files for all request/response bodies - Update `TestModule`: add `UnitController` to `TestControllerModule` and mock `UnitService` in `TestServiceModule` - Fix `RBACServiceTest`: pass new `unitDatastore` mock to `RBACService` constructor Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add UnitServiceTest covering all five CRUD operations including null-return and failure paths for getUnit - Add not-found test to UnitControllerTest; fix UnitController.getUnit to throw NotFoundException instead of returning null (null body causes 500 in the framework serializer) - Add purgeUnit to UnitDatastore interface and SupabaseUnitDatastore implementation (hard-delete of soft-deleted records, for test cleanup) - Add SupabaseUnitDatastoreIntegrationTest with eight tests covering create, get, list (full and property-filtered), update, delete, and not-found edge cases - Extend SupabaseIntegrationTest base class with unitDatastore, unit resource tracking, createTestUnit helper, and tearDown cleanup - Document null-means-no-change semantics on UpdateUnitNetworkRequest and SupabaseUnitDatastore.updateUnit Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.
Summary
Implements the Unit CRUD API for issue #386, covering all three task groups from the MVP project plan:
UnitModel,UnitId, and network request/response types (CreateUnitNetworkRequest,UpdateUnitNetworkRequest,UnitNetworkResponse,UnitListNetworkResponse,GetUnitsQueryParams)UnitApiwith create, get, getList, update, and delete operationsUnitController,UnitService,UnitDatastoreinterface,SupabaseUnitDatastoreimplementation,UnitEntity, DI wiring in all three modules, RBAC integrationChanges
UnitController— five endpoints with role-gated access (EMPLOYEE to read, MANAGER to write, ADMIN to delete);getUnitthrowsNotFoundExceptionon missing unitUnitService— thin delegation layer toUnitDatastorewithrunSuspendCatching/getOrThrow/getOrNullerror handlingSupabaseUnitDatastore— full CRUD + soft-delete +purgeUnit(hard-delete for test cleanup);updateUnituses null-means-no-change partial update semantics, documented in both the datastore andUpdateUnitNetworkRequestRBACService— extended withhasRole/hasRoleOrHigheroverloads forUnitId, resolving org via unit lookupControllerModule,DatastoreModule,ServicesModuleupdatedTests
UnitControllerTest— 11 tests covering success and auth-failure paths for all five operations, plus a not-found case forgetUnitUnitServiceTest— 8 tests covering all operations including null/failure paths and partial-update null forwardingSupabaseUnitDatastoreIntegrationTest— 8 integration tests: create, get, not-found, list (unfiltered and property-filtered), partial update, soft-delete, and delete of non-existent recordSupabaseIntegrationTestbase class extended with unit resource tracking andcreateTestUnithelperTest plan
./gradlew :edifikana:back-end:release :edifikana:shared:releasepasses locallyintegTestsource set)Closes #386