From 781b2f0e5f210a20c400583f12bf94df3204e9cc Mon Sep 17 00:00:00 2001 From: Tristan Cartledge Date: Wed, 4 Jun 2025 14:55:08 +0000 Subject: [PATCH] feat(docs): document sdkHooksConfigAccess feature flag - Add documentation for sdkHooksConfigAccess flag in SDK hooks - Explain behavior differences between true/false settings - Document version compatibility for SDKs before/after May 2025 - Provide migration instructions for existing SDKs - Clarify SDK init hook signature differences based on flag value --- docs/customize/code/sdk-hooks.mdx | 153 ++++++++++++++++++++++-------- 1 file changed, 115 insertions(+), 38 deletions(-) diff --git a/docs/customize/code/sdk-hooks.mdx b/docs/customize/code/sdk-hooks.mdx index f696b161..7be21d10 100644 --- a/docs/customize/code/sdk-hooks.mdx +++ b/docs/customize/code/sdk-hooks.mdx @@ -15,10 +15,42 @@ SDK Hooks enable custom logic to be added to SDK functions and request lifecycle Hooks can be applied to the following lifecycle events: -- **On SDK initialization:** Modify the base server URL, wrap or override the HTTP client, add tracing, inject global headers, and manage authentication. -- **Before request:** Cancel an outgoing request, transform the request contents, or add tracing. -- **After success:** When a successful response is received, add tracing and logging, validate the response, return an error, or transform the raw response before deserialization. -- **After error:** On connection errors or unsuccessful responses, add tracing and logging or transform the returned error. +- **On SDK initialization:** Modify the SDK configuration, base server URL, wrap or override the HTTP client, add tracing, inject global headers, and manage authentication. +- **Before request:** Cancel an outgoing request, transform the request contents, or add tracing. Access to SDK configuration and operation context. +- **After success:** When a successful response is received, add tracing and logging, validate the response, return an error, or transform the raw response before deserialization. Access to SDK configuration and operation context. +- **After error:** On connection errors or unsuccessful responses, add tracing and logging or transform the returned error. Access to SDK configuration and operation context. + +## Hook Context + +All hooks (except SDK initialization) receive a `HookContext` object that provides access to: + +- **SDK Configuration:** The complete SDK configuration object, allowing hooks to access custom settings, authentication details, and other configuration parameters. +- **Base URL:** The base URL being used for the request. +- **Operation ID:** The unique identifier for the API operation being called. +- **OAuth2 Scopes:** The OAuth2 scopes required for the operation (if applicable). +- **Security Source:** The security configuration or source for the operation. +- **Retry Configuration:** The retry settings for the operation. + +## SDK Configuration Access + + + SDK configuration access in hooks is controlled by the `sdkHooksConfigAccess` feature flag in the `generation` section of your `gen.yaml` configuration file. + + +The `sdkHooksConfigAccess` feature flag determines whether hooks have access to the complete SDK configuration object: + +- **`sdkHooksConfigAccess: true`** (default for newly generated SDKs): Hooks receive full access to the SDK configuration through the `HookContext` object, and the SDK initialization hook receives the complete configuration object as a parameter. + +- **`sdkHooksConfigAccess: false`** (default for SDKs generated before May 2025): Hooks have limited access to SDK configuration, and the SDK initialization hook has a different signature that doesn't include the configuration parameter. + +### Version Compatibility + +- **New SDKs (May 2025 and later)**: The `sdkHooksConfigAccess` flag defaults to `true`, providing full configuration access. +- **Existing SDKs (before May 2025)**: The flag defaults to `false` to maintain backward compatibility. You can manually set it to `true` in your `gen.yaml` file to enable full configuration access. + +When `sdkHooksConfigAccess` is set to `false`, the SDK initialization hook will have a different signature that doesn't receive the configuration object as a parameter, limiting the customization options available during SDK initialization. + +To enable full SDK configuration access in existing SDKs, add `sdkHooksConfigAccess: true` under the `generation` section in your `gen.yaml` file. ## Add a Hook @@ -180,22 +212,29 @@ var ( _ afterErrorHook = (*ExampleHook)(nil) ) -func (i *ExampleHook) SDKInit(baseURL string, client HTTPClient) (string, HTTPClient) { - // modify the baseURL or wrap the client used by the SDK here and return the updated values - return baseURL, client +func (i *ExampleHook) SDKInit(config SDKConfig) SDKConfig { + // modify the SDK configuration, baseURL, or wrap the client used by the SDK here and return the updated config + // Access config.BaseURL, config.Client, and other configuration options + return config } func (i *ExampleHook) BeforeRequest(hookCtx BeforeRequestContext, req *http.Request) (*http.Request, error) { + // Access SDK configuration: hookCtx.Config + // Access operation details: hookCtx.OperationID, hookCtx.BaseURL // modify the request object before it is sent, such as adding headers or query parameters, or return an error to stop the request from being sent return req, nil } func (i *ExampleHook) AfterSuccess(hookCtx AfterSuccessContext, res *http.Response) (*http.Response, error) { + // Access SDK configuration: hookCtx.Config + // Access operation details: hookCtx.OperationID, hookCtx.BaseURL // modify the response object before deserialization or return an error to stop the response from being deserialized return res, nil } func (i *ExampleHook) AfterError(hookCtx AfterErrorContext, res *http.Response, err error) (*http.Response, error) { + // Access SDK configuration: hookCtx.Config + // Access operation details: hookCtx.OperationID, hookCtx.BaseURL // modify the response before it is deserialized as a custom error or the error object before it is returned or return an error wrapped in the FailEarly error in this package to exit from the hook chain early return res, err } @@ -213,22 +252,25 @@ from ..types import ( AfterSuccessHook, BeforeRequestContext, BeforeRequestHook, - HttpClient, + SDKConfiguration, SDKInitHook, ) class ExampleHook(SDKInitHook, BeforeRequestHook, AfterSuccessHook, AfterErrorHook): - def sdk_init(self, base_url: str, client: HttpClient) -> Tuple[str, HttpClient]: - # modify the base_url or wrap the client used by the SDK here and return the - # updated values + def sdk_init(self, config: SDKConfiguration) -> SDKConfiguration: + # modify the SDK configuration, base_url, or wrap the client used by the SDK here and return the + # updated configuration + # Access config.base_url, config.client, and other configuration options - return base_url, client + return config def before_request( self, hook_ctx: BeforeRequestContext, request: httpx.Request ) -> Union[httpx.Request, Exception]: + # Access SDK configuration: hook_ctx.config + # Access operation details: hook_ctx.operation_id, hook_ctx.base_url # modify the request object before it is sent, such as adding headers or query # parameters, or raise an exception to stop the request @@ -237,6 +279,8 @@ class ExampleHook(SDKInitHook, BeforeRequestHook, AfterSuccessHook, AfterErrorHo def after_success( self, hook_ctx: AfterSuccessContext, response: httpx.Response ) -> Union[httpx.Response, Exception]: + # Access SDK configuration: hook_ctx.config + # Access operation details: hook_ctx.operation_id, hook_ctx.base_url # modify the response object before deserialization or raise an exception to stop # the response from being returned @@ -248,6 +292,8 @@ class ExampleHook(SDKInitHook, BeforeRequestHook, AfterSuccessHook, AfterErrorHo response: Optional[httpx.Response], error: Optional[Exception], ) -> Union[Tuple[Optional[httpx.Response], Optional[Exception]], Exception]: + # Access SDK configuration: hook_ctx.config + # Access operation details: hook_ctx.operation_id, hook_ctx.base_url # modify the response before it is deserialized as a custom error or the error # object before it is returned or raise an exception to stop processing of other # error hooks and return early @@ -256,7 +302,7 @@ class ExampleHook(SDKInitHook, BeforeRequestHook, AfterSuccessHook, AfterErrorHo ``` ```typescript !!tabs TypeScript -import { HTTPClient } from "../lib/http"; +import { SDKOptions } from "../lib/config"; import { AfterErrorContext, AfterErrorHook, @@ -265,25 +311,27 @@ import { BeforeRequestContext, BeforeRequestHook, SDKInitHook, - SDKInitOptions, } from "./types"; export class ExampleHook implements SDKInitHook, BeforeRequestHook, AfterSuccessHook, AfterErrorHook { - sdkInit(opts: SDKInitOptions): SDKInitOptions { - const { baseURL, client } = opts; - - // modify the baseURL or wrap the client used by the SDK here and return the updated values - return { baseURL: baseURL, client: client }; + sdkInit(opts: SDKOptions): SDKOptions { + // modify the SDK configuration, baseURL, or wrap the client used by the SDK here and return the updated options + // Access opts.baseURL, opts.client, and other configuration options + return opts; } beforeRequest(hookCtx: BeforeRequestContext, request: Request): Request { + // Access SDK configuration: hookCtx.options + // Access operation details: hookCtx.operationID, hookCtx.baseURL // modify the request object before it is sent, such as adding headers or query parameters, or throw an error to stop the request from being sent return request; } afterSuccess(hookCtx: AfterSuccessContext, response: Response): Response { + // Access SDK configuration: hookCtx.options + // Access operation details: hookCtx.operationID, hookCtx.baseURL // modify the response object before deserialization or throw an error to stop the response from being deserialized return response; } @@ -293,6 +341,8 @@ export class ExampleHook response: Response | null, error: unknown, ): { response: Response | null; error: unknown } { + // Access SDK configuration: hookCtx.options + // Access operation details: hookCtx.operationID, hookCtx.baseURL // modify the response before it is deserialized as a custom error or the error object before it is returned or throw an error to stop processing of other error hooks and return early return { response, error }; } @@ -310,7 +360,7 @@ import dev.speakeasyapi.speakeasy.utils.Hook.AfterSuccessContext; import dev.speakeasyapi.speakeasy.utils.Hook.BeforeRequest; import dev.speakeasyapi.speakeasy.utils.Hook.BeforeRequestContext; import dev.speakeasyapi.speakeasy.utils.Hook.SdkInit; -import dev.speakeasyapi.speakeasy.utils.Hook.SdkInitData; +import dev.speakeasyapi.speakeasy.SDKConfiguration; import java.io.InputStream; import java.net.http.HttpRequest; @@ -320,13 +370,16 @@ import java.util.Optional; final class ExampleHook implements BeforeRequest, AfterError, AfterSuccess, SdkInit { @Override - public SdkInitData sdkInit(SdkInitData data) { - // modify the baseURL or wrap the client used by the SDK here and return the updated values - return new SdkInitData(data.baseUrl(), data.client()); + public SDKConfiguration sdkInit(SDKConfiguration config) { + // modify the SDK configuration, baseURL, or wrap the client used by the SDK here and return the updated config + // Access config properties and modify as needed + return config; } @Override public HttpRequest beforeRequest(BeforeRequestContext context, HttpRequest request) throws Exception { + // Access SDK configuration: context.sdkConfiguration() + // Access operation details: context.operationId(), context.baseUrl() // modify the request object before it is sent, such as adding headers or query parameters // or throw an error to stop the request from being sent @@ -341,6 +394,8 @@ final class ExampleHook implements BeforeRequest, AfterError, AfterSuccess, SdkI @Override public HttpResponse afterSuccess(AfterSuccessContext context, HttpResponse response) throws Exception { + // Access SDK configuration: context.sdkConfiguration() + // Access operation details: context.operationId(), context.baseUrl() // modify the response object before deserialization or throw an exception to stop the // response from being deserialized return response; @@ -349,10 +404,12 @@ final class ExampleHook implements BeforeRequest, AfterError, AfterSuccess, SdkI @Override public HttpResponse afterError(AfterErrorContext context, Optional> response, Optional error) throws Exception { + // Access SDK configuration: context.sdkConfiguration() + // Access operation details: context.operationId(), context.baseUrl() // modify the response before it is deserialized as a custom error or the exception // object before it is thrown or throw a FailEarlyException to stop processing of // other error hooks and return early - return response; + return response.orElse(null); } } ``` @@ -365,26 +422,33 @@ namespace Speakeasy.Hooks public class ExampleHook : ISDKInitHook, IBeforeRequestHook, IAfterSuccessHook, IAfterErrorHook { - public (string, ISpeakeasyHttpClient) SDKInit(string baseURL, ISpeakeasyHttpClient client) + public SDKConfig SDKInit(SDKConfig config) { - // modify the baseURL or wrap the client used by the SDK here and return the updated values - return (baseURL, client); + // modify the SDK configuration, baseURL, or wrap the client used by the SDK here and return the updated config + // Access config.BaseURL, config.Client, and other configuration options + return config; } public async Task BeforeRequestAsync(BeforeRequestContext hookCtx, HttpRequestMessage request) { + // Access SDK configuration: hookCtx.SDKConfiguration + // Access operation details: hookCtx.OperationID, hookCtx.BaseURL // modify the request object before it is sent, such as adding headers or query parameters, or throw an exception to stop the request from being sent return request; } public async Task AfterSuccessAsync(AfterSuccessContext hookCtx, HttpResponseMessage response) { + // Access SDK configuration: hookCtx.SDKConfiguration + // Access operation details: hookCtx.OperationID, hookCtx.BaseURL // modify the response object before deserialization or throw an exception to stop the response from being returned return response; } public async Task<(HttpResponseMessage?, Exception?)> AfterErrorAsync(AfterErrorContext hookCtx, HttpResponseMessage? response, Exception error) { + // Access SDK configuration: hookCtx.SDKConfiguration + // Access operation details: hookCtx.OperationID, hookCtx.BaseURL // modify the response before it is deserialized as a custom error // return (response, null); @@ -407,26 +471,33 @@ namespace Speakeasy\Hooks; class ExampleHook implements AfterErrorHook, AfterSuccessHook, BeforeRequestHook, SDKInitHook { - public function sdkInit(string $baseUrl, \GuzzleHttp\ClientInterface $client): SDKRequestContext + public function sdkInit(SDKConfiguration $config): SDKConfiguration { - // modify the baseURL or wrap the client used by the SDK here and return the updated values - return new SDKRequestContext($baseUrl, new TestClient($client)); + // modify the SDK configuration, baseURL, or wrap the client used by the SDK here and return the updated config + // Access config properties and modify as needed + return $config; } public function beforeRequest(BeforeRequestContext $context, RequestInterface $request): RequestInterface { + // Access SDK configuration: $context->config + // Access operation details: $context->operationID, $context->baseURL // modify the request object before it is sent, such as adding headers or query parameters, or throw an exception to stop the request from being sent return $request; } public function afterSuccess(AfterSuccessContext $context, ResponseInterface $response): ResponseInterface { + // Access SDK configuration: $context->config + // Access operation details: $context->operationID, $context->baseURL // modify the response object before deserialization or throw an exception to stop the response from being returned return $response; } public function afterError(AfterErrorContext $context, ?ResponseInterface $response, ?\Throwable $exception): ErrorResponseContext { + // Access SDK configuration: $context->config + // Access operation details: $context->operationID, $context->baseURL // modify the response before it is deserialized as a custom error // return new ErrorResponseContext($response, null); @@ -438,7 +509,7 @@ class ExampleHook implements AfterErrorHook, AfterSuccessHook, BeforeRequestHook // response and error cannot both be null return new ErrorResponseContext($response, $exception); - } + } } ``` @@ -461,15 +532,15 @@ module ExampleSDK sig do override.params( - base_url: String, - client: Faraday::Connection - ).returns([String, Faraday::Connection]) + config: SDKConfiguration + ).returns(SDKConfiguration) end - def sdk_init(base_url:, client:) - # modify the base_url or wrap the client used by the SDK here and return - # the updated values + def sdk_init(config:) + # modify the SDK configuration, base_url, or wrap the client used by the SDK here and return + # the updated configuration + # Access config properties and modify as needed - return base_url, client + config end sig do @@ -479,6 +550,8 @@ module ExampleSDK ).returns(Faraday::Request) end def before_request(hook_ctx:, request:) + # Access SDK configuration: hook_ctx.config + # Access operation details: hook_ctx.operation_id, hook_ctx.base_url # modify the request object before it is sent, such as adding headers or # query parameters, or raise an exception to stop the request @@ -492,6 +565,8 @@ module ExampleSDK ).returns(Faraday::Response) end def after_success(hook_ctx:, response:) + # Access SDK configuration: hook_ctx.config + # Access operation details: hook_ctx.operation_id, hook_ctx.base_url # modify the response object before deserialization or raise an # exception to stop the response from being returned @@ -506,6 +581,8 @@ module ExampleSDK ).returns(T.nilable(Faraday::Response)) end def after_error(error:, hook_ctx:, response:) + # Access SDK configuration: hook_ctx.config + # Access operation details: hook_ctx.operation_id, hook_ctx.base_url # modify the response before it is deserialized or raise an exception to # stop processing of other error hooks and return early