Skip to content

Releases: fireflyframework/fireflyframework-rust

v26.6.28

16 Jun 18:03
ec2ef67

Choose a tag to compare

A Spring Boot parity increment: the declarative HTTP-interface client — the
highest single value lever from the parity-gap analysis (it lifts the REST/HTTP
clients area off the floor).

Added

  • #[http_client] — a declarative HTTP-interface client, the analog of
    Spring 6's @HttpExchange (the modern OpenFeign replacement). Annotate a
    trait of methods with the same verb attributes a #[rest_controller]
    uses and the macro generates a <Trait>Impl that issues the requests over a
    WebClient — the mirror image of a controller.
    • Verbs: #[get("/path")] / #[post] / #[put] / #[delete] /
      #[patch] + generic #[request(method = "…")]. Path variables use the
      framework's :id syntax (same as the server macro); {id} is a compile
      error pointing at :id.
    • Argument binding needs no attributes in the common case: a name-matched
      :var arg is the path variable, the lone non-scalar arg on a body verb is
      the JSON body, the rest are query params (Option omits when None,
      Vec/&[_] repeat). Override with #[path] / #[query("k")] /
      #[header("X")] / #[body]. Every :var must bind exactly once or it is a
      compile error; an Option/Vec/slice path variable is rejected.
    • Return shapes: async fn -> Result<T, ClientError> (the ergonomic
      default), Result<T, E: From<ClientError>>, non-async Mono<T> / Flux<T>
      (returned directly; a Flux defaults Accept: application/x-ndjson), and
      WebClientResponse (the .exchange() escape hatch).
    • Construction: <Trait>Impl::new(base_url) or ::with_client(WebClient);
      the type is Clone. With #[http_client(... bean)] it is registered as a
      @Service and bound to dyn Trait, so #[autowired] Arc<dyn Trait>
      resolves (pulling a shared WebClient bean, named via client = "…").
    • Error fidelity (documented): an awaited Result<T, ClientError>
      surfaces every failure as ClientError::Problem (carrying a FireflyError
      with the original status/code, so the classifiers still work); the
      structured Transport/Decode/Encode/InvalidUrl variants survive only
      on the Mono/Flux return forms.
  • firefly_client::encode_path_segment — RFC 3986 path-segment
    percent-encoding (used by generated clients; also public).

The macro reuses the server #[rest_controller]'s verb-attribute grammar
(MappingAttr/VERBS/join_path), so client and server can't drift. Designed
via a scored 3-proposal panel and adversarially reviewed (the review caught a
runtime footgun — an Option path variable producing …/Some(x) URLs — now a
compile error). The firefly::prelude now also re-exports WebClient /
ClientError / new_web_client.

v26.6.27

16 Jun 18:03
b746625

Choose a tag to compare

A Spring Boot parity increment: declarative rollback rules on
#[transactional]. Chosen from a 16-area parity-gap analysis as the best
value-to-effort gap (the transaction runtime already supported it).

Added

  • #[transactional(no_rollback_for = "<pat>", rollback_only_for = "<pat>")]
    — declarative transaction rollback rules. Spring names exception types;
    because Rust's Result already separates failure from success, the Firefly
    analog names an error pattern. By default every Err rolls back; then:

    • no_rollback_for = "P"Spring's @Transactional(noRollbackFor = …):
      an Err matching pattern P commits instead of rolling back;
    • rollback_only_for = "P": roll back only when the Err matches P,
      committing the rest;
    • with both, no_rollback_for wins on overlap.

    The pattern is any Rust match pattern valid for the fn's error type (no if
    guard), alternatives included ("Error::A | Error::B"). The macro lowers to
    the already-present transactional_with / transactional_with_on runtime
    entry points (which take a should_rollback(&E) -> bool predicate), composes
    with manager = "…", and the generated predicate is matches!-based, so a
    pattern that does not fit the error type is a compile error.

    rollback_only_for is not named rollback_for: Spring's rollbackFor is
    additive (it widens the set of exceptions that roll back), but Rust has no
    checked/unchecked split — every Err already rolls back — so the faithful
    rule here is restrictive. Writing rollback_for is a friendly compile error
    pointing at the two rules above, so a Spring port can't be silently inverted.
    No runtime or API changes elsewhere.

v26.6.26

16 Jun 18:03
c835604

Choose a tag to compare

A correctness release. Every one of the 74 per-crate README.md files was
audited against that crate's actual shipped public API (43 confirmed fixes
across 28 crates), and the audit surfaced a real framework bug: the per-crate
VERSION constant was a hardcoded literal frozen at "26.6.24" instead of
tracking the crate version.

Fixed

  • VERSION no longer drifts from the crate version. Every crate's
    pub const VERSION was a hardcoded "26.6.24" string that nothing kept in
    sync with the workspace version — so firefly_kernel::VERSION, the actuator
    /actuator/version payload, and the startup banner all reported a stale
    release number, and the version_matches_crate_version guard tests only
    passed while the workspace happened to sit at 26.6.24. All 52 hardcoded
    constants now derive from env!("CARGO_PKG_VERSION") (the re-exporting
    crates already chained to firefly_kernel::VERSION), so VERSION is now
    always exactly the crate version and can never drift again. The cli
    FRAMEWORK_VERSION constant got the same treatment, and the handful of
    unit/integration tests that asserted VERSION == "26.6.24" against a frozen
    literal now assert against env!("CARGO_PKG_VERSION"), so the guard holds for
    every release instead of only when the workspace happened to sit at that
    number. (The CLI's render_for / SBOM-parser fixtures keep their literal
    sample versions — that string is arbitrary test data, not the build version.)

  • Phantom / incomplete public-surface docs. Documented APIs now match the
    source: admin's AdminDeps gained its environment field; openapi's
    RouteDef (4 missing fields: request_schema / response_schema /
    query_schema / pageable), Parameter::{query,header}, Builder::{add_schema, add_schema_descriptors, from_inventory, docs_router}, and the DocsConfig
    struct are now listed; orchestration's CompensationPolicy (now all six
    variants incl. GroupedParallel) and SagaError (all four variants);
    starter-web's WebStack::{set_security, set_exception_advice}; testkit's
    BuiltSlice::web_client; idp's Error enum + change_password signature;
    security, webhooks, kernel, transactional, plugins surface fixes.

  • Wrong signatures / variant names. The notifications-* READMEs used the
    wire spellings EmailStatus::SENT / FAILED; the Rust variants are Sent /
    Failed (#[serde(rename = "SENT")] only affects the JSON). plugins showed
    a Vec<Arc<dyn Any>> annotation that does not type-check against the real
    Extension = Arc<dyn Any + Send + Sync>. notifications-twilio,
    session-redis parameter names corrected.

  • Wrong facts. admin: the bean graph does ship dependency edges
    (one per autowired dependency), not "nodes-only". backoffice: the middleware
    order includes TraceContext (Problem → TraceContext → Correlation → Idempotency → BackOffice). resilience, starter-core, eda-kafka,
    session-postgres, session-mongodb, container (a warmform typo) fixes.

  • Stale version pins. Crate-README dependency examples that still pinned the
    long-stale 26.6.7 now use the self-maintaining minor pin version = "26.6"
    (the convention firefly / testkit / webhooks already used); example
    VERSION outputs updated to the release version.

  • firefly-cache doc comment. Removed the stale "once the Redis adapter
    ships in the next minor" note — firefly-cache-redis (RedisAdapter) has
    shipped and is a published workspace member.

v26.6.25

16 Jun 18:03
c75c9c5

Choose a tag to compare

A correctness-and-hygiene release: the workspace is rustfmt-clean again and
every book example was re-audited against the shipped API. No behavioural or
API changes.

Fixed

  • Workspace formatting. cargo fmt --all --check (and therefore
    make ci) was silently failing on main: prior changes to
    firefly-openapi, an observability test, and the lumen-ledger sample
    controller/main.rs were never formatted. Reformatted; make ci now passes
    fmt-checkclippy -D warningsbuildtest end to end.
  • Book example accuracy (full-chapter audit, every finding verified against
    source).
    Corrected: a FireflyApplication::new(..).run() missing .await;
    a WalletView read model missing its Schema derive; the CQRS middleware
    order (ValidationMiddleware is installed first by Core, so with the bus's
    reverse-iteration wrapping it is outermost — prose and the SVG diagram
    now read Validation → Correlation → QueryCache, not Correlation-first);
    three WebClient snippets that built a Mono/Flux without
    .block().await; the experience-tier signal endpoint path
    (POST /journeys/:id/data, not /confirm); a CLI --url :8081 lacking a
    scheme; a streaming handler/test missing a Response import and the
    open_with_deposit(&app) argument; a stale 79 members / “four samples”
    module-index count (now 86 / five reference samples); and two samples/lumen
    GitHub links that pointed at the org root instead of the file.

v26.6.24

16 Jun 18:03
74a030f

Choose a tag to compare

Spec-based filtering is now first-class on derived repositories, with a
filtering endpoint in the sample.

Added

  • #[derive(SqlxRepository)] also implements ReactiveSpecificationRepository
    (find_by_spec / find_by_spec_paged) by delegation — so any derived
    repository runs composable, dialect-aware Specification queries out of the
    box (the Spring Data JpaSpecificationExecutor analog), not just CRUD +
    derived queries.
  • lumen-ledger GET /api/v1/wallets/search — a WalletFilter query DTO
    (owner / currency / status / minBalance / maxBalance, each an OpenAPI query
    parameter) that the @Service turns into a firefly::data::Specification the
    repository compiles to a WHERE. At least one criterion is required — a
    no-filter search is a 422, not an unscoped list-everything (and, like the
    rest of this auth-free demo, a real service would authorization-scope the
    results). New WalletFilter DTO; WalletService::search.

v26.6.23

16 Jun 18:03
fbc0c02

Choose a tag to compare

OpenAPI operation parameters & bodies now render in full (Swagger UI / ReDoc
showed bare operations with no inputs before).

Added

  • Query parameters are generated from a Query<T> / ValidQuery<T>
    extractor: the generator expands T's #[derive(Schema)] fields into one
    in: query parameter each (required iff non-optional). A PageRequest
    argument adds the standard page / size / sort parameters.
  • Header parameters are declared on a verb attribute:
    #[post("/x", header("Idempotency-Key", required, description = "…"))] (and
    query("…") for an extra query parameter) → an in: header parameter the
    handler reads like any axum header.
  • RouteDescriptor gains query_schema / pageable / parameters
    (ParamDescriptor); the openapi Builder expands them; Parameter::{query, header} constructors.

Fixed

  • Request bodies are inferred from Valid<T>, not only Json<T> — so the
    validating extractor's body (the common case) is documented as a
    requestBody. POST/PATCH operations previously showed no body in Swagger UI.

lumen-ledger

  • Query DTOs (OwnerQuery, StatusQuery) and StatusBody are #[derive(Schema)]
    so their fields render; POST /wallets declares an Idempotency-Key header.

v26.6.22

16 Jun 18:03
dafae3f

Choose a tag to compare

Security: remove an unauthenticated all-wallets listing from the sample
(flagged by automated security review of 26.6.21).

Fixed

  • lumen-ledger: GET /api/v1/wallets is owner-scoped again
    ?owner= is required. 26.6.21 had made it list every wallet when owner
    was omitted (WalletService::list_all), an unauthenticated enumeration of all
    account holders + balances (IDOR / broken access control). The unfiltered
    listing and list_all are removed; a missing owner now returns a clear
    RFC 9457 400 (the \owner` query parameter is required …) instead of the confusing raw missing field owner` deserialization error — the actual DX issue
    behind the original report. The controller documents why an unfiltered listing
    must be authorization-scoped (admin authority + caller-scoping) in a real
    service, which this auth-free feature sample intentionally does not wire.

v26.6.21

16 Jun 18:03
a4b8cb9

Choose a tag to compare

API docs move to the management port, and the surfaces are hardened.

Changed

  • OpenAPI docs (Swagger UI / ReDoc / /v3/api-docs) are served on the
    management port
    , beside actuator + admin — not the public API port. They
    expose the whole API surface and every schema, a control-plane concern, so the
    public data-plane port no longer serves them.
  • The OpenAPI document now declares the API base URL as its server
    (Builder::add_server), so Swagger UI's Try it out / ReDoc target the API
    port rather than the management origin the docs load from. Derived from the API
    bind address (wildcard host → localhost); overridable with
    FIREFLY_OPENAPI_SERVER_URL.

Fixed

  • The management listener now answers an RFC 9457 application/problem+json
    404 for unknown paths (it previously returned axum's bare empty body),
    matching the public API.
  • lumen-ledger GET /api/v1/wallets lists every wallet when ?owner= is
    omitted (an optional filter) — a bare collection request is a 200, not a
    "missing query parameter" 400. Adds WalletService::list_all.

v26.6.20

16 Jun 18:03
224f294

Choose a tag to compare

The lumen-ledger sample gains the transactional transfer use case, on a new
#[transactional] option that binds the boundary to an explicit manager.

Added

  • #[transactional(manager = "<expr>")] (firefly-macros) — Spring's
    @Transactional("txManager"). Drives an explicit TransactionManager (the
    expression m yields a value with &m: &Arc<dyn TransactionManager>, e.g.
    self.tx_manager()) via transactional_on, instead of the process-global
    registry. For a multi-datasource service, or to keep per-instance / per-test
    isolation.
  • lumen-ledger transferPOST /api/v1/wallets/:id/transfer +
    WalletService::transfer move funds between wallets atomically under
    #[transactional(manager = "self.tx_manager()")]: the debit and credit commit
    together or not at all, and a rejected transfer (insufficient funds, inactive
    party, self-transfer, bad destination) moves no money and renders RFC 9457
    422. New TransferRequest DTO; the service autowires the Db to build its
    own manager.

Docs

  • Book: the layered-microservices web-surface table gains an atomic transfer
    row; the declarative-macros #[transactional] section documents the manager
    option; the persistence-config note explains the per-instance-manager choice.

v26.6.19

16 Jun 18:03
cb9ce1b

Choose a tag to compare

Spring Boot parity push, PR 9/N — Tier B: Actuator DI/route introspection.

Added

  • /actuator/beans, /actuator/mappings, /actuator/conditions
    (firefly-actuator) — Spring Boot Actuator's introspection endpoints, rendered
    from the framework's compile-time inventory (firefly_container::{discovered, routes}), so they need no live container:
    • beans — every DI bean (type, module, scope, stereotype, primary, lazy),
      grouped under contexts.application.beans.
    • mappings — every #[rest_controller] route (method, path, controller,
      handler, summary), the RequestMappingHandlerMapping analog.
    • conditions — the @Profile / @ConditionalOn… guards each
      conditionally-registered bean declares.
    • mount() auto-registers all three (override-respecting); each is served only
      when the ExposureConfig includes it, exactly as Spring gates them behind
      exposure.include. Also exposed via register_introspection /
      BeansEndpoint / MappingsEndpoint / ConditionsEndpoint.

This completes the prioritized Tier A + Tier B Spring-Boot-parity gap list.