Repository navigation
Deck and note services, OpenAPI with Swagger UI, and a working Alpine Docker image - #3
Merged
Merged
Conversation
The worker now only runs queued jobs one at a time and syncs the changes they made; deck and note logic lives in IDeckService, INoteService and INotetypeService, which report NotFound, Rejected or InfraFail. New endpoints: list, rename and delete decks; get, search, update and delete notes; list note types with their fields. Creating a deck or note answers 201 with its id, a missing deck or note in the URL answers 404. Notes for a missing or filtered deck are rejected instead of landing in the default deck, as rslib would do silently. Not-found errors name the missing object instead of rslib's desktop text. GET /notes without a query answers 422 instead of failing in Oxpecker's query binder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
10.0.x resolves to 10.0.403 (runtime 10.0.14), which setup-dotnet finds but can't download: dotnetorigin answers 409, and Microsoft's 10.0 releases.json still lists 10.0.402 as the latest release. Go back to 10.0.x once 10.0.403 is published. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The image couldn't run: the runtime stage used dotnet/runtime, which lacks ASP.NET Core, and the build never restored Paket, generated the rslib client or built libankit_rslib.so, so publishing failed. The image now builds on sdk:10.0-alpine through the same build script targets CI runs and checks that the native library was published; it runs on aspnet:10.0-alpine as the app user with a writable /app/data. gcompat runs Grpc.Tools' glibc protoc, and musl targets build without crt-static, which a cdylib needs. Host build output stays out of the Docker context. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every endpoint is described through Oxpecker.OpenApi (ApiDocs.fs): an operation id and summary, request bodies, path and query parameters, and each success and error response. The spec is served at /openapi/v1.json and browsable with Swagger UI at /swagger. Schema transformers and HTTP JSON options make the spec match what the API accepts: integers aren't strings, fieldless unions are string enums, non-option record fields aren't nullable, and 204 has no body. Union values are now read case-insensitively. docs/server.md documents "direction": "upload", but only "UPLOAD" bound before. Co-Authored-By: Claude Opus 5.5 <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
Moves the deck and note logic out of
AnkiSyncWorkerinto services and adds the missing commands. On top of that, it describes the API with OpenAPI and Swagger UI, and fixes the Docker image, which couldn't run.Services and endpoints
Services.fs):IDeckService,INoteService,INotetypeService. They returnCommandResult<'a>withNotFound/Rejected/InfraFail, which the handlers map to 404 / 422 / 500.AnkiSyncMsg.submit), and successful changes schedule the debounced sync. A full sync cancels a pending one, a failed job triggers none, and exceptions reach the HTTP caller. Reads don't go through the worker and never trigger a sync.GET /decks,PUT/DELETE /decks/{id},GET /notes?query=…,GET/PUT/DELETE /notes/{id},GET /note-types. Documented indocs/server.md.OpenAPI and Swagger UI
Oxpecker.OpenApi(ApiDocs.fs): operation id, summary, request body, path and query parameters, and every success and error response./openapi/v1.json. Swagger UI is at/swagger, fromSwashbuckle.AspNetCore.SwaggerUI, which only serves the UI for that spec.Docker
dotnet/runtime, which has no ASP.NET Core. The build also never restored Paket, generated the rslib client or builtlibankit_rslib.so, so the image couldn't be built or run.sdk:10.0-alpinethrough the same build-script targets CI runs, and checks that the native library was published. It runs onaspnet:10.0-alpineas theappuser with a writable/app/data. The image is 223 MB.gcompatruns Grpc.Tools' glibcprotoc, and musl targets build withoutcrt-static, which acdylibneeds (.cargo/config.toml, musl only). Host build output stays out of the Docker context../build Run.Build and CI
10.0.402.10.0.xresolved to10.0.403, which wasn't downloadable (HTTP 409). Unpin once it is.Before-Pushnow formats before it verifies, so formatting issues get fixed instead of failing the run.CLAUDE.mdadds guidance for AI agents: build, CI, conventions, architecture and known quirks.Breaking API changes
POST /decksandPOST /notesanswer201 {"id": …}instead of204.404. rslib rejections stay422, now with rslib's reason as the message.Fixes found along the way
cardgen.rs).docs/server.mddocuments"direction": "upload", but only"UPLOAD"bound before.SearchNotesDtouses a lowercasequeryfield.GET /notesreturned 400.bindOrDeclineQuerynow validates an empty model, which gives the usual 422.Known issue, not fixed here
POST /sync/fullreturns 500, because protobuf rejects thenullusername. It should fail with a clear error instead.Tests
DeckServiceTests,NoteServiceTestsandNotetypeServiceTestsrun on a temp collection, without Docker.AnkiSyncWorkerTestsuses a counting fakeISyncService. It depends on timing (200 ms debounce) and passed 8 repeated runs locally../build Before-Pushpasses locally: 9 native and 33 server tests, including the Docker sync tests../build Run: the native library loads against musl, the API works end to end, and non-ASCII text round-trips.🤖 Generated with Claude Code