-
Notifications
You must be signed in to change notification settings - Fork 20
feat: implement telemetry singleton pattern to reuse telemetry from sdk client root #213
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -78,10 +78,10 @@ public OpenFgaApi(Configuration configuration) throws FgaInvalidParameterExcepti | |
public OpenFgaApi(Configuration configuration, ApiClient apiClient) throws FgaInvalidParameterException { | ||
this.apiClient = apiClient; | ||
this.configuration = configuration; | ||
this.telemetry = new Telemetry(this.configuration); | ||
this.telemetry = configuration.getTelemetry(); | ||
|
||
if (configuration.getCredentials().getCredentialsMethod() == CredentialsMethod.CLIENT_CREDENTIALS) { | ||
this.oAuth2Client = new OAuth2Client(configuration, apiClient); | ||
this.oAuth2Client = new OAuth2Client(configuration, apiClient, this.telemetry); | ||
} else { | ||
Comment on lines
78
to
85
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Telemetry initialization: good reuse, but beware configurationOverride semantics drift Using a single Telemetry instance from the root Configuration and injecting it into OAuth2Client is consistent with the PR goal of reuse. However, downstream calls that accept a ConfigurationOverride will still emit telemetry through the root instance, ignoring any telemetryConfiguration set on the override. This can lead to telemetry being recorded with the wrong configuration (e.g., disabled/enabled, different exporters, different attributes) when an override is used. If overrides are intended to have their own telemetry semantics (Configuration.override() explicitly resets telemetry to lazily reinitialize), pass telemetry from the effective configuration per call rather than from the root. Two viable options:
See suggested call-site fixes in the next comment. 🤖 Prompt for AI Agents
|
||
this.oAuth2Client = null; | ||
} | ||
|
@@ -145,7 +145,7 @@ private CompletableFuture<ApiResponse<BatchCheckResponse>> batchCheck( | |
|
||
try { | ||
HttpRequest request = buildHttpRequest("POST", path, body, configuration); | ||
return new HttpRequestAttempt<>(request, "batchCheck", BatchCheckResponse.class, apiClient, configuration) | ||
return new HttpRequestAttempt<>(request, "batchCheck", BatchCheckResponse.class, apiClient, configuration, this.telemetry) | ||
.addTelemetryAttributes(telemetryAttributes) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Ensure per-call ConfigurationOverride’s telemetry is honored All these call sites pass the root telemetry (this.telemetry), which prevents telemetryConfiguration provided via ConfigurationOverride from taking effect. Use the effective configuration’s telemetry instead. Apply this pattern change across the listed lines: - return new HttpRequestAttempt<>(request, "batchCheck", BatchCheckResponse.class, apiClient, configuration, this.telemetry)
+ return new HttpRequestAttempt<>(request, "batchCheck", BatchCheckResponse.class, apiClient, configuration, configuration.getTelemetry()) - return new HttpRequestAttempt<>(request, "check", CheckResponse.class, apiClient, configuration, this.telemetry)
+ return new HttpRequestAttempt<>(request, "check", CheckResponse.class, apiClient, configuration, configuration.getTelemetry()) - return new HttpRequestAttempt<>(request, "createStore", CreateStoreResponse.class, apiClient, configuration, this.telemetry)
+ return new HttpRequestAttempt<>(request, "createStore", CreateStoreResponse.class, apiClient, configuration, configuration.getTelemetry()) - return new HttpRequestAttempt<>(request, "deleteStore", Void.class, apiClient, configuration, this.telemetry)
+ return new HttpRequestAttempt<>(request, "deleteStore", Void.class, apiClient, configuration, configuration.getTelemetry()) - return new HttpRequestAttempt<>(request, "expand", ExpandResponse.class, apiClient, configuration, this.telemetry)
+ return new HttpRequestAttempt<>(request, "expand", ExpandResponse.class, apiClient, configuration, configuration.getTelemetry()) - return new HttpRequestAttempt<>(request, "getStore", GetStoreResponse.class, apiClient, configuration, this.telemetry)
+ return new HttpRequestAttempt<>(request, "getStore", GetStoreResponse.class, apiClient, configuration, configuration.getTelemetry()) - return new HttpRequestAttempt<>(request, "listObjects", ListObjectsResponse.class, apiClient, configuration, this.telemetry)
+ return new HttpRequestAttempt<>(request, "listObjects", ListObjectsResponse.class, apiClient, configuration, configuration.getTelemetry()) - return new HttpRequestAttempt<>(request, "listStores", ListStoresResponse.class, apiClient, configuration, this.telemetry)
+ return new HttpRequestAttempt<>(request, "listStores", ListStoresResponse.class, apiClient, configuration, configuration.getTelemetry()) - return new HttpRequestAttempt<>(request, "listUsers", ListUsersResponse.class, apiClient, configuration, this.telemetry)
+ return new HttpRequestAttempt<>(request, "listUsers", ListUsersResponse.class, apiClient, configuration, configuration.getTelemetry()) - return new HttpRequestAttempt<>(request, "read", ReadResponse.class, apiClient, configuration, this.telemetry)
+ return new HttpRequestAttempt<>(request, "read", ReadResponse.class, apiClient, configuration, configuration.getTelemetry()) - return new HttpRequestAttempt<>(
- request, "readAssertions", ReadAssertionsResponse.class, apiClient, configuration, this.telemetry)
+ return new HttpRequestAttempt<>(
+ request, "readAssertions", ReadAssertionsResponse.class, apiClient, configuration, configuration.getTelemetry()) - return new HttpRequestAttempt<>(
- request,
- "readAuthorizationModel",
- ReadAuthorizationModelResponse.class,
- apiClient,
- configuration,
- this.telemetry)
+ return new HttpRequestAttempt<>(
+ request,
+ "readAuthorizationModel",
+ ReadAuthorizationModelResponse.class,
+ apiClient,
+ configuration,
+ configuration.getTelemetry()) - return new HttpRequestAttempt<>(
- request,
- "readAuthorizationModels",
- ReadAuthorizationModelsResponse.class,
- apiClient,
- configuration,
- this.telemetry)
+ return new HttpRequestAttempt<>(
+ request,
+ "readAuthorizationModels",
+ ReadAuthorizationModelsResponse.class,
+ apiClient,
+ configuration,
+ configuration.getTelemetry()) - return new HttpRequestAttempt<>(request, "readChanges", ReadChangesResponse.class, apiClient, configuration, this.telemetry)
+ return new HttpRequestAttempt<>(request, "readChanges", ReadChangesResponse.class, apiClient, configuration, configuration.getTelemetry()) - return new HttpRequestAttempt<>(request, "write", Object.class, apiClient, configuration, this.telemetry)
+ return new HttpRequestAttempt<>(request, "write", Object.class, apiClient, configuration, configuration.getTelemetry()) - return new HttpRequestAttempt<>(request, "writeAssertions", Void.class, apiClient, configuration, this.telemetry)
+ return new HttpRequestAttempt<>(request, "writeAssertions", Void.class, apiClient, configuration, configuration.getTelemetry()) - return new HttpRequestAttempt<>(
- request,
- "writeAuthorizationModel",
- WriteAuthorizationModelResponse.class,
- apiClient,
- configuration,
- this.telemetry)
+ return new HttpRequestAttempt<>(
+ request,
+ "writeAuthorizationModel",
+ WriteAuthorizationModelResponse.class,
+ apiClient,
+ configuration,
+ configuration.getTelemetry()) Also applies to: 205-205, 255-255, 304-304, 360-360, 410-410, 466-466, 517-517, 573-573, 629-629, 687-687, 749-751, 815-816, 901-902, 956-956, 1023-1024, 1085-1087 |
||
.attemptHttpRequest(); | ||
} catch (ApiException e) { | ||
|
@@ -201,7 +201,7 @@ private CompletableFuture<ApiResponse<CheckResponse>> check( | |
|
||
try { | ||
HttpRequest request = buildHttpRequest("POST", path, body, configuration); | ||
return new HttpRequestAttempt<>(request, "check", CheckResponse.class, apiClient, configuration) | ||
return new HttpRequestAttempt<>(request, "check", CheckResponse.class, apiClient, configuration, this.telemetry) | ||
.addTelemetryAttributes(telemetryAttributes) | ||
.attemptHttpRequest(); | ||
} catch (ApiException e) { | ||
|
@@ -251,7 +251,7 @@ private CompletableFuture<ApiResponse<CreateStoreResponse>> createStore( | |
|
||
try { | ||
HttpRequest request = buildHttpRequest("POST", path, body, configuration); | ||
return new HttpRequestAttempt<>(request, "createStore", CreateStoreResponse.class, apiClient, configuration) | ||
return new HttpRequestAttempt<>(request, "createStore", CreateStoreResponse.class, apiClient, configuration, this.telemetry) | ||
.addTelemetryAttributes(telemetryAttributes) | ||
.attemptHttpRequest(); | ||
} catch (ApiException e) { | ||
|
@@ -300,7 +300,7 @@ private CompletableFuture<ApiResponse<Void>> deleteStore(String storeId, Configu | |
|
||
try { | ||
HttpRequest request = buildHttpRequest("DELETE", path, configuration); | ||
return new HttpRequestAttempt<>(request, "deleteStore", Void.class, apiClient, configuration) | ||
return new HttpRequestAttempt<>(request, "deleteStore", Void.class, apiClient, configuration, this.telemetry) | ||
.addTelemetryAttributes(telemetryAttributes) | ||
.attemptHttpRequest(); | ||
} catch (ApiException e) { | ||
|
@@ -356,7 +356,7 @@ private CompletableFuture<ApiResponse<ExpandResponse>> expand( | |
|
||
try { | ||
HttpRequest request = buildHttpRequest("POST", path, body, configuration); | ||
return new HttpRequestAttempt<>(request, "expand", ExpandResponse.class, apiClient, configuration) | ||
return new HttpRequestAttempt<>(request, "expand", ExpandResponse.class, apiClient, configuration, this.telemetry) | ||
.addTelemetryAttributes(telemetryAttributes) | ||
.attemptHttpRequest(); | ||
} catch (ApiException e) { | ||
|
@@ -406,7 +406,7 @@ private CompletableFuture<ApiResponse<GetStoreResponse>> getStore(String storeId | |
|
||
try { | ||
HttpRequest request = buildHttpRequest("GET", path, configuration); | ||
return new HttpRequestAttempt<>(request, "getStore", GetStoreResponse.class, apiClient, configuration) | ||
return new HttpRequestAttempt<>(request, "getStore", GetStoreResponse.class, apiClient, configuration, this.telemetry) | ||
.addTelemetryAttributes(telemetryAttributes) | ||
.attemptHttpRequest(); | ||
} catch (ApiException e) { | ||
|
@@ -462,7 +462,7 @@ private CompletableFuture<ApiResponse<ListObjectsResponse>> listObjects( | |
|
||
try { | ||
HttpRequest request = buildHttpRequest("POST", path, body, configuration); | ||
return new HttpRequestAttempt<>(request, "listObjects", ListObjectsResponse.class, apiClient, configuration) | ||
return new HttpRequestAttempt<>(request, "listObjects", ListObjectsResponse.class, apiClient, configuration, this.telemetry) | ||
.addTelemetryAttributes(telemetryAttributes) | ||
.attemptHttpRequest(); | ||
} catch (ApiException e) { | ||
|
@@ -513,7 +513,7 @@ private CompletableFuture<ApiResponse<ListStoresResponse>> listStores( | |
|
||
try { | ||
HttpRequest request = buildHttpRequest("GET", path, configuration); | ||
return new HttpRequestAttempt<>(request, "listStores", ListStoresResponse.class, apiClient, configuration) | ||
return new HttpRequestAttempt<>(request, "listStores", ListStoresResponse.class, apiClient, configuration, this.telemetry) | ||
.addTelemetryAttributes(telemetryAttributes) | ||
.attemptHttpRequest(); | ||
} catch (ApiException e) { | ||
|
@@ -569,7 +569,7 @@ private CompletableFuture<ApiResponse<ListUsersResponse>> listUsers( | |
|
||
try { | ||
HttpRequest request = buildHttpRequest("POST", path, body, configuration); | ||
return new HttpRequestAttempt<>(request, "listUsers", ListUsersResponse.class, apiClient, configuration) | ||
return new HttpRequestAttempt<>(request, "listUsers", ListUsersResponse.class, apiClient, configuration, this.telemetry) | ||
.addTelemetryAttributes(telemetryAttributes) | ||
.attemptHttpRequest(); | ||
} catch (ApiException e) { | ||
|
@@ -625,7 +625,7 @@ private CompletableFuture<ApiResponse<ReadResponse>> read( | |
|
||
try { | ||
HttpRequest request = buildHttpRequest("POST", path, body, configuration); | ||
return new HttpRequestAttempt<>(request, "read", ReadResponse.class, apiClient, configuration) | ||
return new HttpRequestAttempt<>(request, "read", ReadResponse.class, apiClient, configuration, this.telemetry) | ||
.addTelemetryAttributes(telemetryAttributes) | ||
.attemptHttpRequest(); | ||
} catch (ApiException e) { | ||
|
@@ -684,7 +684,7 @@ private CompletableFuture<ApiResponse<ReadAssertionsResponse>> readAssertions( | |
try { | ||
HttpRequest request = buildHttpRequest("GET", path, configuration); | ||
return new HttpRequestAttempt<>( | ||
request, "readAssertions", ReadAssertionsResponse.class, apiClient, configuration) | ||
request, "readAssertions", ReadAssertionsResponse.class, apiClient, configuration, this.telemetry) | ||
.addTelemetryAttributes(telemetryAttributes) | ||
.attemptHttpRequest(); | ||
} catch (ApiException e) { | ||
|
@@ -746,7 +746,8 @@ private CompletableFuture<ApiResponse<ReadAuthorizationModelResponse>> readAutho | |
"readAuthorizationModel", | ||
ReadAuthorizationModelResponse.class, | ||
apiClient, | ||
configuration) | ||
configuration, | ||
this.telemetry) | ||
.addTelemetryAttributes(telemetryAttributes) | ||
.attemptHttpRequest(); | ||
} catch (ApiException e) { | ||
|
@@ -810,7 +811,8 @@ private CompletableFuture<ApiResponse<ReadAuthorizationModelsResponse>> readAuth | |
"readAuthorizationModels", | ||
ReadAuthorizationModelsResponse.class, | ||
apiClient, | ||
configuration) | ||
configuration, | ||
this.telemetry) | ||
.addTelemetryAttributes(telemetryAttributes) | ||
.attemptHttpRequest(); | ||
} catch (ApiException e) { | ||
|
@@ -896,7 +898,7 @@ private CompletableFuture<ApiResponse<ReadChangesResponse>> readChanges( | |
|
||
try { | ||
HttpRequest request = buildHttpRequest("GET", path, configuration); | ||
return new HttpRequestAttempt<>(request, "readChanges", ReadChangesResponse.class, apiClient, configuration) | ||
return new HttpRequestAttempt<>(request, "readChanges", ReadChangesResponse.class, apiClient, configuration, this.telemetry) | ||
.addTelemetryAttributes(telemetryAttributes) | ||
.attemptHttpRequest(); | ||
} catch (ApiException e) { | ||
|
@@ -951,7 +953,7 @@ private CompletableFuture<ApiResponse<Object>> write(String storeId, WriteReques | |
|
||
try { | ||
HttpRequest request = buildHttpRequest("POST", path, body, configuration); | ||
return new HttpRequestAttempt<>(request, "write", Object.class, apiClient, configuration) | ||
return new HttpRequestAttempt<>(request, "write", Object.class, apiClient, configuration, this.telemetry) | ||
.addTelemetryAttributes(telemetryAttributes) | ||
.attemptHttpRequest(); | ||
} catch (ApiException e) { | ||
|
@@ -1018,7 +1020,7 @@ private CompletableFuture<ApiResponse<Void>> writeAssertions( | |
|
||
try { | ||
HttpRequest request = buildHttpRequest("PUT", path, body, configuration); | ||
return new HttpRequestAttempt<>(request, "writeAssertions", Void.class, apiClient, configuration) | ||
return new HttpRequestAttempt<>(request, "writeAssertions", Void.class, apiClient, configuration, this.telemetry) | ||
.addTelemetryAttributes(telemetryAttributes) | ||
.attemptHttpRequest(); | ||
} catch (ApiException e) { | ||
|
@@ -1080,7 +1082,8 @@ private CompletableFuture<ApiResponse<WriteAuthorizationModelResponse>> writeAut | |
"writeAuthorizationModel", | ||
WriteAuthorizationModelResponse.class, | ||
apiClient, | ||
configuration) | ||
configuration, | ||
this.telemetry) | ||
.addTelemetryAttributes(telemetryAttributes) | ||
.attemptHttpRequest(); | ||
} catch (ApiException e) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
/* | ||
* OpenFGA | ||
* A high performance and flexible authorization/permission engine built for developers and inspired by Google Zanzibar. | ||
* | ||
* The version of the OpenAPI document: 1.x | ||
* Contact: [email protected] | ||
* | ||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). | ||
* https://openapi-generator.tech | ||
* Do not edit the class manually. | ||
*/ | ||
|
||
package dev.openfga.sdk.api.configuration; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import dev.openfga.sdk.telemetry.Telemetry; | ||
import org.junit.jupiter.api.Test; | ||
|
||
class ConfigurationTelemetryTest { | ||
@Test | ||
void shouldReturnSameTelemetryInstanceOnMultipleCalls() { | ||
// given | ||
Configuration configuration = new Configuration(); | ||
|
||
// when | ||
Telemetry firstCall = configuration.getTelemetry(); | ||
Telemetry secondCall = configuration.getTelemetry(); | ||
|
||
// then | ||
assertThat(firstCall).isNotNull().isSameAs(secondCall); | ||
} | ||
|
||
@Test | ||
void shouldCreateNewTelemetryInstanceForNewConfiguration() { | ||
// given | ||
Configuration config1 = new Configuration(); | ||
Configuration config2 = new Configuration(); | ||
|
||
// when | ||
Telemetry telemetry1 = config1.getTelemetry(); | ||
Telemetry telemetry2 = config2.getTelemetry(); | ||
|
||
// then | ||
assertThat(telemetry1).isNotNull(); | ||
assertThat(telemetry2).isNotNull(); | ||
assertThat(telemetry1).isNotSameAs(telemetry2); | ||
} | ||
|
||
@Test | ||
void shouldNotCopyTelemetryInstanceWhenUsingOverride() { | ||
// given | ||
Configuration originalConfig = new Configuration(); | ||
Telemetry originalTelemetry = originalConfig.getTelemetry(); // Initialize telemetry | ||
|
||
ConfigurationOverride override = new ConfigurationOverride(); | ||
|
||
// when | ||
Configuration overriddenConfig = originalConfig.override(override); | ||
Telemetry overriddenTelemetry = overriddenConfig.getTelemetry(); | ||
|
||
// then | ||
assertThat(originalTelemetry).isNotNull(); | ||
assertThat(overriddenTelemetry).isNotNull(); | ||
assertThat(originalTelemetry).isNotSameAs(overriddenTelemetry); | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Avoid NPE when a ConfigurationOverride switches to CLIENT_CREDENTIALS
getAccessToken(Configuration) uses the instance field oAuth2Client (built only from the root Configuration). If a per-call ConfigurationOverride specifies CredentialsMethod.CLIENT_CREDENTIALS while the root did not, oAuth2Client is null and a NullPointerException will occur.
Create/choose an OAuth2Client based on the effective configuration (root vs override). Example:
This preserves reuse for the root client while supporting overrides with different credentials.
Would you like me to push a follow-up PR adjusting getAccessToken and updating the call sites accordingly?
🏁 Script executed:
Length of output: 3892
Ensure OAuth2Client is initialized for overrides using CLIENT_CREDENTIALS
getAccessToken(Configuration)
currently always uses the fieldoAuth2Client
, which is only non-null when the root configuration usesCLIENT_CREDENTIALS
. If a per-callConfiguration
override switches toCLIENT_CREDENTIALS
,oAuth2Client
remains null, causing an NPE.Please update the method to reuse the root client when appropriate and instantiate a new one for overrides:
• File:
src/main/java/dev/openfga/sdk/api/OpenFgaApi.java
Location:
private String getAccessToken(Configuration configuration)
(around lines 1261–1268)Suggested replacement:
This preserves reuse of the root client while safely supporting per-call overrides with different credential methods.
🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makes sense
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@SoulPancake, great! The fix I suggested will prevent the NPE and ensure OAuth2Client works correctly with both root configurations and per-call overrides. Let me know if you'd like me to help with anything else related to this telemetry implementation.