Repository navigation
Conversation
…ponents Implement the Quickwit REST API as Kestra tasks under io.kestra.plugin.quickwit, replacing the plugin template task: - search.Search and search.Trigger, the latter polling with a KV store watermark - ingest.Ingest, serializing documents to NDJSON - index.Create/Get/List/Delete/Clear - source.Create/Toggle/Delete/ResetCheckpoint - deletetask.Create/List All HTTP calls go through Kestra's io.kestra.core.http.client. Quickwit ships no authentication of its own, so basicAuth and custom headers are optional and only needed behind a reverse proxy or gateway.
Add 80 unit tests over 9 classes: an unconditional happy path for each of the 15 tasks and the trigger, plus non-2xx, empty-body, credential, rendering and path-encoding coverage. Also add the plugin documentation resources (how-to, per-subpackage metadata and icons, Quickwit mark reused from the upstream logo) and rewrite AGENTS.md and README around the implemented classes. Two fixes surfaced by the tests: - pathSegment used URLEncoder, which emits '+' for a space; inside a URI path that is a literal plus sign, so it now percent-encodes - metadata for the 'index' subpackage must keep its dotted filename, otherwise it collides with the root metadata file; META-004 is disabled accordingly and MetadataConsistencyTest enforces the invariant it would have checked
Send search parameters as CSV, use the delete-task API’s `search_fields` array, and correct timestamp filters and startup instructions. Add regression coverage for invalid examples.
jymaire
left a comment
There was a problem hiding this comment.
Thanks for the work here, the structure is clean (shared base task, QuickwitService, WireMock coverage, per-subpackage metadata). I reviewed the head 0bf5138 against the Kestra contribution guidelines and the Quickwit REST docs; I did not run the build. Inline comments carry suggested fixes.
Main concern: search.Trigger watermark. It uses wall-clock now, while Quickwit's start_timestamp filters on the document timestamp field. Late documents are lost, overflow beyond maxHits is skipped (not delivered later as documented), and a boundary-second document can be delivered twice. The tests don't exercise any of these (the "no refire" test just re-stubs an empty response).
Other guideline gaps (not inline):
- Rendered properties should be prefixed with
r(renderedIndex->rIndex, etc.). - No YAML flow sanity checks in
src/test/resources/flows. - Tests are WireMock-only; Testcontainers is preferred (the PR already ships a Quickwit service in
docker-compose.yml), with.github/setup-unit.sh/docker-compose-ci.ymlif so. - No local QA screenshots in the PR description.
- Two commits are not Conventional Commits (
Fix Quickwit request formats and examples,duplicate logic removed); the PR title also ends with a trailing space. - Some outputs echo inputs (
Search.Output.fetchType,Ingest.Output.commit,Trigger.Output.index/query); guidelines say not to duplicate properties in outputs. search.Triggerreads the watermark with a warn-and-fallback to "whole result set" on any KV error, which would re-fire on every poll during a KV outage; failing the evaluation seems safer.
Nothing here is a blocker for the overall design; the watermark and the secret/key items are the ones I would fix before merging.
Co-authored-by: jymaire <jymaire@users.noreply.github.com>
|
I am now working on other review that you have told above that are not inline. |
|
You can now check i have fixed the left issues! |
jymaire
left a comment
There was a problem hiding this comment.
Follow-up review on ec65eca. Thanks for the watermark rework. Using document timestamps, failing the poll on KV errors, the flow sanity checks and outputs that no longer repeat inputs are all good changes.
This time I checked every suggestion before posting. I applied all of them locally on top of ec65eca, and ./gradlew test passes (90 tests). The new TriggerTest cases fail on the current head.
Apologies: three of my earlier suggestions were wrong, and you were right to revert them.
@Minon aProperty<Integer>breaks validation withHV000030: No validator could be found for constraint 'jakarta.validation.constraints.Min' validating type 'Property<Integer>', so every trigger evaluation and task validation fails. The bounds are now checked at render time instead.- The
:in my length-prefixed key is not a valid KV key character ([a-zA-Z0-9][a-zA-Z0-9._-]*). The corrected suggestion uses-.
I reopened those three threads. The bounds and the key collision are still open, so each one now has a working suggestion.
Blocking, all in search.Trigger:
- Timestamps are parsed as numbers, but Quickwit returns RFC 3339 strings by default. As a result, every poll against an index with the default
output_formatfails, including the index from the plugin's own how-to. sort_by: +timestampsorts descending in Quickwit (+→Desc,-→Asc), so documents beyondmaxHitsare still skipped.- A truncated page drops the rest of its last second.
- The watermark is still written before the execution is generated.
The suggestions depend on each other (imports, the timestamp(...) helper, QuickwitService.requireAtLeast), so please apply them together in one batch.
Still open from the first review: Testcontainers coverage, local QA screenshots in the description, and the trailing space in the PR title.
Co-authored-by: jymaire <jymaire@users.noreply.github.com>
|
@jymaire Is there other things i am missing in this pr? |
jymaire
left a comment
There was a problem hiding this comment.
Follow-up review on 16f4113. Thanks for applying the batch, everything from my last review is in: RFC 3339 parsing, ascending sort with -, the truncated-page hold-back, writing the watermark after the execution is generated, the length-prefixed key and the render-time bounds. I ran ./gradlew test on this head and all 90 tests pass.
I found two new issues, both in the how-to page (io.kestra.plugin.quickwit.md). Each inline comment has a suggestion.
Still open from earlier reviews (unchanged, not repeated inline):
- Testcontainers coverage. Tests are still WireMock-only.
- Local QA screenshots in the PR description.
- The
Search.sortBydescription should explain Quickwit's prefix semantics (-ascending,+descending), and thetimestampFielddescription still says "in seconds". It should mention the supportedoutput_formatvalues (rfc3339orunix_timestamp_secs).
The PR description also still says late documents are picked up later. Please update it in the same pass as the first inline comment.
Co-authored-by: jymaire <jymaire@users.noreply.github.com>
WireMock covers every task including the error paths, but stubs cannot catch API drift. QuickwitContainerTest creates an index, ingests two documents with FORCE commit and reads them back against a real Quickwit node via Testcontainers. It aborts where Docker is unavailable, and QUICKWIT_IT_URL points it at an existing node instead.
|
@jymaire I have added the Testcontainer coverage, |
What changes are being made and why?
Driving Quickwit from Kestra today means
io.kestra.plugin.core.http.Requestplus hand-built JSON, so every flow repeats the same four things: the base URL, theapi/v1prefix, query-string encoding and error handling. This plugin wraps those once and exposes typed tasks, so a flow only declares what it wants to search, ingest or manage.This PR turns the
plugin-quickwitscaffold into a working plugin for the Quickwit REST API.Components shipped (11 tasks + 1 polling trigger):
searchSearch,TriggeringestIngest(withCommitenum)indexCreate,Get,List,Delete,ClearsourceCreate,Toggle,Delete,ResetCheckpointdeletetaskCreate,ListShared plumbing
AbstractQuickwitTaskholds the connection properties once —url(required) plus optionalbasicAuth,headers,connectTimeoutandreadTimeout— so every component takes them flat, with no extra nesting.QuickwitServicebuilds the endpoints, creates the requests and turns failures intoIllegalStateExceptionmessages that carry Quickwit's ownmessagefield. A doc-mapping mismatch therefore reads asdoc mapping does not contain field 'timestamp'instead of an opaqueHTTP 400.models/holds the API response POJOs, so downstream tasks consume typed outputs rather than Pebble expressions over a raw JSON body.Notable design decisions
basicAuthandheadersexist for clusters behind a reverse proxy or an API gateway, and both are documented as such.configVersion, notversion. Kestra reservesversionto pin a plugin version, so Quickwit's index-config format version is namedconfigVersion.search.Triggerkeeps its watermark in the namespace KV Store, advancing it only after the execution is created, so a document is delivered once. The trigger polls ascending ontimestampField; documents that arrive late, with an event timestamp already below the watermark, are not picked up.stateKeyallows two triggers to share a watermark deliberately.fetchTypeonSearch(FETCH/FETCH_ONE/STORE/NONE) so a large result set can go to internal storage as a URI instead of into the task output.Ingesthandles the two things that bite users: Quickwit only accepts NDJSON, so the task serializes a document, a list, akestra://URI, afile://path or a JSON string; and with the defaultcommit: AUTOdocuments are queued but not yet searchable, whichWAIT_FORandFORCEfix.detailedResponse: truereturns theparseFailuresentry per rejected document.Also in this PR
docker-compose.ymlstarts a single-node Quickwit with every service enabled on port 7280 alongside Kestra, so the examples can be run against a real cluster.org.wiremock:wiremock-jetty12) with@KestraTestcovers every task, the trigger, the connection options and the error handling — no live cluster needed.META-004is disabled inbuild.gradlewith an explanatory comment: a subpackage is literally namedindex, so its metadata file collides with the rootindex.yamlthat the same rule requires. The invariant is still enforced byMetadataConsistencyTest.src/main/resources/doc/io.kestra.plugin.quickwit.md), the README rewritten around the actual components, icons and metadata per subpackage.closes #13
QA Screenshots
Unit tests — `./gradlew build` passing
Local Quickwit up — `curl http://localhost:7280/api/v1/cluster`
Flows runs
Trigger firing on new doc
Setup Instructions
docker compose up -d— starts Kestra + single-node Quickwit onhttp://localhost:7280src/main/resources/doc/[io.kestra.plugin.quickwit.md](file:///C:/Users/tanav/c/plugin-quickwit/src/main/resources/doc/io.kestra.plugin.quickwit.md)(orindex.Create)basicAuthfor reverse-proxy basic authheaders:withAuthorization: "Bearer {{ secret('QUICKWIT_GATEWAY_TOKEN') }}"for gateway tokenscurl http://localhost:7280/api/v1/clusterContributor Checklist ✅