## Breaking changes

- `on_subgraph_request` hook has been renamed to `on_graphql_subgraph_request`.
- Most extension methods now also receive a `ctx` argument which exposes context from previous extensions.
- `authorize_query` doesn't receive a `token` anymore, it can be retrieved from the context instead.
- `IntoQueryAuthorization` has been renamed to `IntoAuthorizeQueryOutput`, and the `From<(AuthorizationDecisions, Vec<u8>)>` case is not supported anymore due to the
  ambiguity whether the bytes correspond to the context or the state (for the `on_response` hook).
- headers in the `authorize_query` are not mutable anymore. They required a lock, instead you can provide additional headers with either `header` or `headers` method in the `AUthorizeQueryOutput`:

```rust
fn authorize_query(
    &mut self,
    ctx: &AuthenticatedRequestContext,
    headers: &SubgraphHeaders,
    elements: QueryElements<'_>,
) -> Result<impl IntoAuthorizeQueryOutput, ErrorResponse> {
    let decisions = todo!(); // Whether to grant or not certain elements in the query.
    Ok(AuthorizeQueryOutput::new(decisions)
        .header("extra-header-to-send-to-subgraph", "value"))
}
```

## Features

- Add a new hook `on_virtual_subgraph_request`, the equivalent of `on_graphql_subgraph_request` but for virtual subgraphs, so for resolver extensions.
- `subgraph_name` is now available in both `on_graphql_subgraph_request`.

To facilitate context propagation, we now have three contexts: `RequestContext`, `AuthenticatedRequestContext` and `AuthorizedOperationContext`.

### RequestContext

Received by `authenticate` and `on_response`:

- hooks context provided by `on_request` hook.

### AuthenticatedRequestContext

Received by `authorize_query`:

- hooks context provided by `on_request` hook.
- authentication token provided by `authenticate`.

### AuthorizedOperationContext

Received by `on_graphql_subgraph_request`, `on_graphql_subgraph_request`, `authorize_response` and all resolver methods, exposing:

- hooks context provided by `on_request` hook.
- authentication token provided by `authenticate`.
- authorization context provided by `authorize_query`.

### Examples

To propagate data from the `on_request` hook to other hooks:

```rust
use grafbase_sdk::{
    HooksExtension,
    host_io::{
        event_queue::EventQueue,
        http::{Method, StatusCode},
    },
    types::{Configuration, Error, ErrorResponse, GatewayHeaders, OnRequestOutput, RequestContext},
};

#[derive(HooksExtension)]
struct Hooks;

impl HooksExtension for Hooks {
    fn new(config: Configuration) -> Result<Self, Error> {
        Ok(Self)
    }

    #[allow(refining_impl_trait)]
    fn on_request(
        &mut self,
        url: &str,
        method: Method,
        headers: &mut GatewayHeaders,
    ) -> Result<OnRequestOutput, ErrorResponse> {
        // We use postcard here being the fastest option as a non-descriptive serde-compatible format. But
        // you may also use among others: serde_json, minicbor_serde or rkyv
        Ok(OnRequestOutput::new().context(postcard::to_allocvec(&HookData { url: url.to_string() }).unwrap()))
    }

    fn on_response(
        &mut self,
        ctx: &RequestContext,
        status: StatusCode,
        headers: &mut GatewayHeaders,
        event_queue: EventQueue,
    ) -> Result<(), Error> {
        let HookData { url } = postcard::from_bytes(&ctx.hooks_context()).unwrap();
        Ok(())
    }
}

#[derive(serde::Serialize, serde::Deserialize)]
struct HookData {
    url: String,
}
```

To propagate data from an authorization extension to hooks:

```rust
// === Authorization extension ===
use std::collections::HashMap;

use grafbase_sdk::{
    AuthorizationExtension, IntoAuthorizeQueryOutput,
    types::{
        AuthenticatedRequestContext, AuthorizationDecisions, AuthorizeQueryOutput, Configuration, Error, ErrorResponse,
        QueryElements, SubgraphHeaders,
    },
};

#[derive(AuthorizationExtension)]
struct Authz;

impl AuthorizationExtension for Authz {
    fn new(config: Configuration) -> Result<Self, Error> {
        Ok(Self)
    }

    fn authorize_query(
        &mut self,
        ctx: &AuthenticatedRequestContext,
        headers: &SubgraphHeaders,
        elements: QueryElements<'_>,
    ) -> Result<impl IntoAuthorizeQueryOutput, ErrorResponse> {
        let tokens = SubgraphTokens {
            tokens: vec![("products".to_string(), "token".to_string())]
                .into_iter()
                .collect(),
        };
        // Here we used serde_json, but there are fast altnerative like minicbor_serde, postcard or even rkyv.
        let context = serde_json::to_vec(&tokens).unwrap();
        Ok(AuthorizeQueryOutput::new(AuthorizationDecisions::grant_all()).context(context))
    }
}

#[derive(serde::Serialize, serde::Deserialize)]
struct SubgraphTokens {
    // Token by subgraph name
    tokens: HashMap<String, String>,
}
```

```rust
// === Hooks extensions ===
use std::collections::HashMap;

use grafbase_sdk::{
    HooksExtension,
    types::{AuthorizedOperationContext, Configuration, Error, HttpRequestParts},
};

#[derive(HooksExtension)]
struct Hooks;

impl HooksExtension for Hooks {
    fn new(config: Configuration) -> Result<Self, Error> {
        Ok(Self)
    }

    fn on_graphql_subgraph_request(
        &mut self,
        ctx: &AuthorizedOperationContext,
        subgraph_name: &str,
        parts: &mut HttpRequestParts,
    ) -> Result<(), Error> {
        let context: SubgraphTokens = serde_json::from_slice(&ctx.authorization_context()?).unwrap();
        if let Some(token) = context.tokens.get(subgraph_name) {
            parts.headers.append("Authorization", token);
        }
        Ok(())
    }
}

#[derive(serde::Serialize, serde::Deserialize)]
struct SubgraphTokens {
    // Token by subgraph name
    tokens: HashMap<String, String>,
}
```

If all of your extensions are built within the same cargo workspace, we recommend having a common crate with those context structs and use `postcard` for serialization.
