fix(api): HTTP semantics, group config resource, simpler fields, strict lint - #42
Merged
Merged
Conversation
- IpAddress uses anyOf; the ipv4/ipv6 branches differ only by format.
- Node and provider create/delete report a configuration change during
admission as 409 state_conflict; 412 is only for a failed If-Match.
- JSON Patch operation objects ignore members they do not define
(RFC 6902 section 4).
- If-Match follows RFC 9110 section 13.1.1: `*`, tag lists and weak tags
are well-formed and are compared, not rejected as malformed.
- GET /config/sources/{source_id} sends the content hash as ETag.
- A known path with an unsupported method returns 405 with Allow.
- errors.md states once that body checks precede the 412 precondition.
- Every 401 carries WWW-Authenticate: Bearer.
- A non-loopback listener needs a deployment secret or password mode.
GET /groups/{group_id} carries runtime selection and health, which change
without a configuration change, so one strong ETag tied to the
configuration revision cannot describe it. The group now sends no ETag.
GET /groups/{group_id}/config returns {policy, config}, the document the
patch already targeted, with the configuration revision as ETag. PATCH
moves to the same path; operation paths (/policy, /config/<option>) and
bodies are unchanged. If-Match is optional in the schema so a retained
replay can omit it; a server that requires it returns 428.
- mode_override is honk's Clash mode, which dae does not have. It leaves the shared outbound step and is documented as x-honk in the honk notes. - A probe request no longer carries purpose: the kind determines it. Results and health observations keep purpose; the capability drops its purposes axis. - Recorder mode has one spelling, "on" | "off" | "auto", in the PATCH body, GET recording.*.mode and resources.flows.recording. - Discovery auth is required; a draft has no older servers to allow for.
Every operation carries one area tag, declared with a description at the root. Lint now extends recommended-strict, so any finding fails CI. Deliberate exceptions are listed per location in .redocly.lint-ignore.yaml (conditional then/not branches, SSE payload schemas bound through x-event-data-schemas, the local server), explained in redocly.yaml. info-license stays off until the repository has a license. The RouteStepData and FlowDetail oneOf branches now require their discriminator, and the unused PreconditionFailed response is removed.
GET /config/sources/{id} tags its body with content_sha256, so the body now carries only identity and content; writable and loaded_at stay in the GET /config list. A masked body has no ETag. EntityTagList follows RFC 9110: empty list elements are ignored and an entity tag has no space or tab.
The group patch example carries its Content-Type and If-Match, so the replay test drops a header that is actually there. The source PUT If-Match example is a content hash. Node and provider create 409 also covers a configuration change during admission. Discovery no longer mentions servers without auth.
…obes A new group-configuration PATCH without If-Match returns 428; a retained replay may omit it. The body-before-precondition order is stated as a deviation from RFC 9110 §13.2.1.
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.
Final-acceptance fixes, part F1: HTTP and JSON Patch semantics, a group configuration resource, fewer engine-specific fields, and a strict Redocly lint. Findings come from the two final audits (A = astra, O = opus).
Items
IpAddressusesanyOf(A12, O3). Both branches are strings that differ only byformat, and OAS 3.1 treatsformatas an annotation by default, sooneOfrejected every address. dae: none, the value is still an IPv4 or IPv6 string.If-Match(A9, O1, O2). Node and provider create and delete now report a configuration change during admission as409 state_conflict; the412responses on the two deletes are gone. dae: return 409 when its write lock sees a newer revision.GET /groups/{id}carries selection and health, so a strongETagtied to the configuration revision did not describe it. NewGET /groups/{id}/configreturns{policy, config}with the revision asETag;PATCHmoves to the same path. The operation paths (/policy,/config/<option>) do not change, since the patch already targeted that document.If-Matchis optional in the schema so a retained replay can omit it; a server that requires it returns 428.GET /groups/{id}keepsconfigand sends noETag. dae: one extra read route over the group's configured options.GroupConfigdocument", butGroupConfighas nopolicy, andpolicyis patchable. The document is therefore{policy, config}, which groups.md already defined as the patch target.additionalProperties: false, and the rule that request objects reject unknown fields now excludes patch operations. dae: check the members each op needs and ignore the rest.If-Matchper RFC 9110 §13.1.1 (A8, O8).*matches an existing resource, a list matches when any strong tag matches, and a weak tag never matches. A header that does not match returns 412; a header that is not entity-tag syntax returns 400. The new sharedEntityTagListschema accepts the full syntax. errors.md has a new "Conditional requests" section. dae: standard entity-tag list parsing.GET /config/sources/{id}sendsETag(O8): the quotedcontent_sha256, the valuePUTcompares. dae: the hash it already computes.Allow(O8, RFC 9110 §15.5.6) for a known path with an unsupported method, as new ErrorCodemethod_not_allowed. Unknown paths stay 404. dae: Go's router reports method mismatches separately.WWW-Authenticateon every 401 (A7). The sharedUnauthorizedresponse requiresWWW-Authenticate: Bearerand shows it in its example. The two inline 401 responses in config.yaml now use the shared response. honk already sends the header.{operation_id}. No contract change; HC row for honk's{id}.mode_overrideleaves the shared outbound step (O4). It is honk's Clash mode, which dae does not have. honk-notes documents it asdata["x-honk"].mode_override. flows.md now says "rules that an engine-wide outbound mode does not override, where the engine has such a mode" instead of "must/block resisting mode override".purposeremoved (A15). The kind determines it:tcp_connectandhttptestdata, anddnstestsdns. Theprobes.purposescapability is gone. Results and health keeppurpose. dae: derive it from the kind."on" | "off" | "auto"in the PATCH body,recording.*.modeandresources.flows.recording(which keepssampled).true,falseandon_demandare gone.authrequired (O24). A draft has no older servers to allow for.recommended-strict, so CI (yarn check:contract) fails on any finding. Exceptions are listed by location in.redocly.lint-ignore.yaml, andredocly.yamlgives the reason for each:then/notbranches that require properties the parent defines, SSE payload schemas bound throughx-event-data-schemas, and the localhost server. TheRouteStepDataandFlowDetailoneOfbranches now require their discriminator, and the unusedPreconditionFailedresponse is removed.info-licenseis off, not satisfied: the repository has no license, and choosing one is up to the maintainers.Review fixes
ETag(high).GET /config/sources/{source_id}returnsConfigSourceContent: id, path, absolute_path, kind, content_sha256, bytes, content and line_count, all fixed for given bytes.writableandloaded_atstay in theGET /configsource list (ConfigSource=ConfigSourceContentplus those two), so one strongETagnever covers two bodies (RFC 9110 §8.8.1). A body with a masked listener-secret value is not whatPUTreplaces, so it has noETag.PUTstill compares the stored content hash. dae: serialize the source without the two fields and set the header only when nothing was masked.EntityTagList. The pattern follows RFC 9110 §5.6.1.2 and §8.8.3: empty list elements ("17", , "18") are accepted, spaces, tabs and inner quotes inside a tag are not, and only an uppercaseW/marks a weak tag. Tests cover each case. dae: split on commas, trim OWS, skip empty elements, and reject any element that is notW/"…"or"…"overetagc.Content-Type: application/json-patch+jsonandIf-Match: "17", so the replay test removes a header that is present. The sharedIfMatchexample (sourcePUT) is a quoted SHA-256;IfMatchOptionalkeeps"17".409also covers a configuration change during admission (state_conflict). Discovery drops the case of a server withoutauth, which is now required.PATCHwithoutIf-Matchreturns428, and a retained idempotent replay may omit it; the error contract, groups page andIfMatchOptionalsay the same. The body-before-precondition order is stated as a deviation from RFC 9110 §13.2.1. Recording policy, probepurpose, the groupETagrationale and the retry step are reworded without changing their meaning. dae: requiresIf-Matchon group patches, as honk already does.info-licensestays off. The maintainers (CODEOWNERS) choose the license; the rule comes back once the repository has one.The drift ledger now also records: the single-source body and
ETagchange for honk (native_api/config.rs:577-578); group patch validation split into operation-shape checks before the412precondition and failedtest(409) or unsupported values (422) after it; and no transition release for probepurpose: honk rejects it as an unknown field and doona stops sending it. doona does not read the single-sourceGET, so it needs no change for item 1.Checks
yarn check:contract: bundle, strict lint with 0 problems (96 ignored by location), checker (575 examples, 222 schemas, 47 paths), 86/86 tests.hexo clean && hexo generate: builds.honk and doona
The drift ledger has an F1 row for each honk change: 412→409 on node/provider writes, the group config route, patch members,
If-Matchparsing, sourceETag, 405, patch validation order, the operations link,x-honk.mode_override, probepurpose, recorder strings. It also has D3 items for doona: the group editor route, the recorder strings, dropping probepurpose, and readingmode_overridefromx-honk.