oscerd opened a new pull request, #27195:
URL: https://github.com/apache/camel/pull/27195

   Resolves [CAMEL-25184](https://issues.apache.org/jira/browse/CAMEL-25184).
   
   `camel-openfga` shipped with the five decision operations (`check`, 
`batchCheck`, `listObjects`,
   `listRelations`, `listUsers`) and the two write operations. That covers "may 
this subject do this",
   but not "what access exists" — which is what you need to build an 
access-review report, to drive a
   projection or cache off the change log, or to work out *why* a check 
answered the way it did. This
   adds the three read operations that answer those questions.
   
   ## Operations
   
   | Operation | Answers | Body on the way out |
   |---|---|---|
   | `readTuples` | which tuples match a filter | `List<Map<String, Object>>` 
keyed `user` / `relation` / `object` / `timestamp` |
   | `readChanges` | what changed in the store, oldest first | same, plus 
`operation` = `WRITE` or `DELETE` |
   | `expand` | how a relation resolves, as a tree | the SDK's `UsersetTree` |
   
   `readTuples` deliberately emits **exactly the keys `writeTuples` and 
`deleteTuples` already accept**,
   so revoking what you just read needs no transformation in between:
   
   ```java
   from("direct:revokeEverythingBobHas")
       
.to("openfga:readTuples?storeId={{fga.store}}&object=document:&user=user:bob")
       .to("openfga:deleteTuples?storeId={{fga.store}}");
   ```
   
   `whatReadTuplesReturnsCanBeRevokedWithoutReshaping` is the test that holds 
that contract in place —
   it is the property most likely to be broken by a well-meant tidy-up of 
either shape.
   
   `expand` is the one operation that does not leave a `List` on the body. Its 
answer *is* a tree, and
   flattening it would destroy the only information the operation exists to 
provide, so the
   `UsersetTree` goes on the body as the SDK returns it.
   
   ## Paging
   
   `pageSize` bounds a request; the token comes back on 
`CamelOpenFgaContinuationToken` and is fed back
   in through `continuationToken` (Simple-evaluated, so it can come off an 
exchange property):
   
   ```java
   from("timer:sync?period=30000")
       .to("openfga:readChanges?storeId={{fga.store}}&type=document"
           + "&continuationToken=${exchangeProperty.fgaToken}")
       .setProperty("fgaToken", header("CamelOpenFgaContinuationToken"))
       .split(body()).to("direct:applyChange");
   ```
   
   On the last page the header is **removed** rather than left holding the 
previous value, so a route
   looping on it terminates instead of re-reading the final page for ever.
   
   `startTime` (ISO-8601) bounds where a first `readChanges` begins — without 
it, a first read against a
   busy store pages through the entire history before it reaches anything 
current. It is parsed when
   the endpoint starts, not on the first exchange, so a typo fails the route 
rather than the message.
   
   ## What OpenFGA actually accepts as a read filter
   
   This is the part I got wrong first and only found because an integration 
test failed against a real
   server. The Read API filter is **not** "every part is optional". Probed 
against OpenFGA 1.21.0:
   
   * no filter at all — reads the whole store, a page at a time: **accepted**
   * `object=document:` **plus** a `user` — **accepted**
   * `object=document:budget`, with or without user/relation — **accepted**
   * `user` alone, `relation` alone, or `object=document:` alone — **rejected 
by the server**
   
   OpenFGA wants an object *type* as soon as any filter is given, and will not 
take an empty object id
   and an empty user together. `validateReadFilter` enforces that before the 
call and names the option
   to change, instead of letting an opaque HTTP 400 surface as if the component 
had malfunctioned.
   That rule is also why a type-only `document:` is accepted as a read filter 
but still rejected as an
   `object` everywhere else — a filter and an identifier are not the same 
thing, and the existing
   identifier validation is unchanged.
   
   ## Tests
   
   * `OpenFgaReadOperationsTest` — 11 tests: the three operations, the 
round-trip contract above, the
     filter rules the server imposes, the token-removal-on-last-page behaviour, 
and
     `refusesAStartTimeThatIsNotATimestampWhenTheEndpointStarts`.
   * `OpenFgaIT` — 3 new integration tests against a real OpenFGA container, 
which is where the filter
     rule came from.
   * 109 unit tests and 15 integration tests green; full reactor `BUILD 
SUCCESS`.
   
   Generated metadata gained `pageSize`, `continuationToken`, `startTime` and
   `CamelOpenFgaContinuationToken` and lost nothing.
   
   ## Note for whoever merges
   
   This overlaps #27185 (CAMEL-25183, contextual tuples) in `OpenFgaProducer` 
and
   `OpenFgaConfiguration`. Whichever lands second needs a rebase — neither 
change depends on the other,
   so the order does not matter.
   
   _Claude Code on behalf of oscerd_
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to