Skip to content

Commit 8b4e17b

Browse files
authored
feat(go-sdk): support OpenTelemetry metrics (#428)
2 parents d3b75c2 + 08996ce commit 8b4e17b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2105
-19
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,6 @@ clients/**/*
1212
# JetBrains IDEs
1313
.idea/
1414
*.iml
15+
16+
config/clients/dotnet/template/example/Example1/obj
17+
config/clients/dotnet/template/example/Example1/bin/

config/clients/go/CHANGELOG.md.mustache

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
# Changelog
22

3+
## v0.6.1
4+
5+
### [0.6.1](https://github.com/openfga/go-sdk/compare/v0.6.0...v0.6.1) (2024-09-23)
6+
7+
- refactor(OpenTelemetry): move configuration API into public package namespace (#122)
8+
- docs(OpenTelemetry): initial documentation and example (#123)
9+
10+
## v0.6.0
11+
12+
### [0.6.0](https://github.com/openfga/go-sdk/compare/v0.5.0...v0.6.0) (2024-08-29)
13+
14+
- feat: support OpenTelemetry metrics reporting (#115)
15+
- feat!: support for sending the consistency parameter to the read, check, list users, list objects, and expand endpoints (#117)
16+
- chore(docs): update stale README (#113) - thanks @Code2Life
17+
18+
BREAKING CHANGE:
19+
20+
When the generator converts enums in the open API definition, by default it removes the type prefix. For example, `TYPE_NAME_UNSPECIFIED` is converted to a const named `UNSPECIFIED`. This leads to potential collisions with other enums, and as the consistency type is a new enum, we finally got a collision (was just a matter of time).
21+
22+
The fix for this is to specify `"enumClassPrefix": true` in the generation config. This will then include the class name on the const name, which resoles collision issues. This means any enum value, such as `INT` now becomes `TYPENAME_INT`. The main impact of this is the `TypeName` consts and error codes. The fix is to add the class name prefix as discussed above.
23+
324
## v0.5.0
425

526
### [0.5.0](https://{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/compare/v0.4.0...v0.5.0) (2024-06-14)

config/clients/go/config.overrides.json

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
"sdkId": "go",
33
"gitRepoId": "go-sdk",
44
"packageName": "openfga",
5-
"packageVersion": "0.5.0",
5+
"packageVersion": "0.6.1",
66
"packageDescription": "Go SDK for OpenFGA",
77
"packageDetailedDescription": "This is an autogenerated Go SDK for OpenFGA. It provides a wrapper around the [OpenFGA API definition](https://openfga.dev/api).",
88
"fossaComplianceNoticeId": "41c01c64-f74a-414a-9e39-7aeca87bc47b",
99
"generateInterfaces": true,
1010
"enumClassPrefix": true,
11+
"openTelemetryDocumentation": "opentelemetry.md",
1112
"files": {
1213
".github/workflows/main.yaml.mustache": {
1314
"destinationFilename": ".github/workflows/main.yaml",
@@ -86,6 +87,103 @@
8687
"example/example1/go.mod.mustache": {
8788
"destinationFilename": "example/example1/go.mod",
8889
"templateType": "SupportingFiles"
90+
},
91+
"example/opentelemetry/main.go": {},
92+
"example/opentelemetry/go.mod.mustache": {
93+
"destinationFilename": "example/opentelemetry/go.mod",
94+
"templateType": "SupportingFiles"
95+
},
96+
"docs/opentelemetry.md.mustache": {
97+
"destinationFilename": "docs/opentelemetry.md",
98+
"templateType": "SupportingFiles"
99+
},
100+
"telemetry/attribute.mustache": {
101+
"destinationFilename": "telemetry/attribute.go",
102+
"templateType": "SupportingFiles"
103+
},
104+
"telemetry/attribute_test.mustache": {
105+
"destinationFilename": "telemetry/attribute_test.go",
106+
"templateType": "SupportingFiles"
107+
},
108+
"telemetry/attributes.mustache": {
109+
"destinationFilename": "telemetry/attributes.go",
110+
"templateType": "SupportingFiles"
111+
},
112+
"telemetry/attributes_test.mustache": {
113+
"destinationFilename": "telemetry/attributes_test.go",
114+
"templateType": "SupportingFiles"
115+
},
116+
"telemetry/configuration.mustache": {
117+
"destinationFilename": "telemetry/configuration.go",
118+
"templateType": "SupportingFiles"
119+
},
120+
"telemetry/configuration_test.mustache": {
121+
"destinationFilename": "telemetry/configuration_test.go",
122+
"templateType": "SupportingFiles"
123+
},
124+
"telemetry/counter.mustache": {
125+
"destinationFilename": "telemetry/counter.go",
126+
"templateType": "SupportingFiles"
127+
},
128+
"telemetry/counter_test.mustache": {
129+
"destinationFilename": "telemetry/counter_test.go",
130+
"templateType": "SupportingFiles"
131+
},
132+
"telemetry/counters.mustache": {
133+
"destinationFilename": "telemetry/counters.go",
134+
"templateType": "SupportingFiles"
135+
},
136+
"telemetry/counters_test.mustache": {
137+
"destinationFilename": "telemetry/counters_test.go",
138+
"templateType": "SupportingFiles"
139+
},
140+
"telemetry/histogram.mustache": {
141+
"destinationFilename": "telemetry/histogram.go",
142+
"templateType": "SupportingFiles"
143+
},
144+
"telemetry/histogram_test.mustache": {
145+
"destinationFilename": "telemetry/histogram_test.go",
146+
"templateType": "SupportingFiles"
147+
},
148+
"telemetry/histograms.mustache": {
149+
"destinationFilename": "telemetry/histograms.go",
150+
"templateType": "SupportingFiles"
151+
},
152+
"telemetry/histograms_test.mustache": {
153+
"destinationFilename": "telemetry/histograms_test.go",
154+
"templateType": "SupportingFiles"
155+
},
156+
"telemetry/interfaces.mustache": {
157+
"destinationFilename": "telemetry/interfaces.go",
158+
"templateType": "SupportingFiles"
159+
},
160+
"telemetry/interfaces_test.mustache": {
161+
"destinationFilename": "telemetry/interfaces_test.go",
162+
"templateType": "SupportingFiles"
163+
},
164+
"telemetry/metric.mustache": {
165+
"destinationFilename": "telemetry/metric.go",
166+
"templateType": "SupportingFiles"
167+
},
168+
"telemetry/metric_test.mustache": {
169+
"destinationFilename": "telemetry/metric_test.go",
170+
"templateType": "SupportingFiles"
171+
},
172+
"telemetry/metrics.mustache": {
173+
"destinationFilename": "telemetry/metrics.go",
174+
"templateType": "SupportingFiles"
175+
},
176+
"telemetry/metrics_test.mustache": {
177+
"destinationFilename": "telemetry/metrics_test.go",
178+
"templateType": "SupportingFiles"
179+
},
180+
"telemetry/telemetry.mustache": {
181+
"destinationFilename": "telemetry/telemetry.go",
182+
"templateType": "SupportingFiles"
183+
},
184+
"telemetry/telemetry_test.mustache": {
185+
"destinationFilename": "telemetry/telemetry_test.go",
186+
"templateType": "SupportingFiles"
89187
}
90188
}
91189
}

config/clients/go/template/.github/workflows/main.yaml.mustache

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
test:
1212
runs-on: ubuntu-latest
1313
steps:
14-
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
14+
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
1515
with:
1616
fetch-depth: 0
1717

@@ -34,7 +34,7 @@ jobs:
3434
run: govulncheck ./...
3535

3636
- name: Upload coverage to Codecov
37-
uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0
37+
uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0
3838
continue-on-error: true
3939
with:
4040
token: ${{ secrets.CODECOV_TOKEN }}
@@ -46,7 +46,7 @@ jobs:
4646
needs: [test]
4747

4848
steps:
49-
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
49+
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
5050
with:
5151
fetch-depth: 0
5252

config/clients/go/template/README_retries.mustache

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ Apply your custom retry values by passing this struct to the `ClientConfiguratio
88
import (
99
"os"
1010

11-
openfga "github.com/openfga/go-sdk"
12-
. "github.com/openfga/go-sdk/client"
11+
openfga "{{gitHost}}/{{gitUserId}}/{{gitRepoId}}"
12+
. "{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/client"
1313
)
1414

1515
func main() {

config/clients/go/template/api.mustache

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import (
1212
{{#imports}} "{{import}}"
1313
{{/imports}}
1414

15-
"{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/internal/utils"
15+
internalutils "{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/internal/utils"
16+
telemetry "{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/telemetry"
1617
)
1718

1819
// Linger please
@@ -93,6 +94,7 @@ func (a *{{{classname}}}Service) {{{nickname}}}(ctx _context.Context{{#pathParam
9394
func (a *{{{classname}}}Service) {{nickname}}Execute(r {{#structPrefix}}{{&classname}}{{/structPrefix}}Api{{operationId}}Request) ({{#returnType}}{{{.}}}, {{/returnType}}*_nethttp.Response, error) {
9495
var maxRetry int
9596
var minWaitInMs int
97+
var requestStarted time.Time = time.Now()
9698
9799
if (a.RetryParams != nil) {
98100
maxRetry = a.RetryParams.MinWaitInMs
@@ -498,6 +500,28 @@ func (a *{{{classname}}}Service) {{nickname}}Execute(r {{#structPrefix}}{{&class
498500
}
499501

500502
{{/returnType}}
503+
metrics := telemetry.GetMetrics(telemetry.TelemetryFactoryParameters{Configuration: a.client.cfg.Telemetry})
504+
505+
var attrs, queryDuration, requestDuration, _ = metrics.BuildTelemetryAttributes(
506+
"{{nickname}}",
507+
map[string]interface{}{ {{#pathParams.0}}
508+
"storeId": r.storeId,{{/pathParams.0}}
509+
"body": localVarPostBody,
510+
},
511+
req,
512+
localVarHTTPResponse,
513+
requestStarted,
514+
i,
515+
)
516+
517+
if requestDuration > 0 {
518+
metrics.RequestDuration(requestDuration, attrs)
519+
}
520+
521+
if queryDuration > 0 {
522+
metrics.QueryDuration(queryDuration, attrs)
523+
}
524+
501525
return {{#returnType}}localVarReturnValue, {{/returnType}}localVarHTTPResponse, nil
502526
}
503527
// should never have reached this

config/clients/go/template/api_client.mustache

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import (
1919
"strings"
2020
"time"
2121
"unicode/utf8"
22+
23+
"{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/telemetry"
2224
)
2325

2426
var (
@@ -67,6 +69,8 @@ func NewAPIClient(cfg *Configuration) *APIClient {
6769
if cfg.Credentials == nil {
6870
cfg.HTTPClient = http.DefaultClient
6971
} else {
72+
cfg.Credentials.Context = context.Background()
73+
telemetry.Bind(cfg.Credentials.Context, telemetry.Get(telemetry.TelemetryFactoryParameters{Configuration: cfg.Telemetry}))
7074
var httpClient, headers = cfg.Credentials.GetHttpClientAndHeaderOverrides()
7175
if len(headers) > 0 {
7276
for idx := range headers {

config/clients/go/template/client/client.mustache

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
fgaSdk "{{gitHost}}/{{gitUserId}}/{{gitRepoId}}"
1212
"{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/credentials"
1313
internalutils "{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/internal/utils"
14+
"{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/telemetry"
1415
"golang.org/x/sync/errgroup"
1516
)
1617

@@ -39,6 +40,7 @@ type ClientConfiguration struct {
3940
Debug bool `json:"debug"`
4041
HTTPClient *_nethttp.Client
4142
RetryParams *fgaSdk.RetryParams
43+
Telemetry *telemetry.Configuration `json:"telemetry,omitempty"`
4244
}
4345

4446
func newClientConfiguration(cfg *fgaSdk.Configuration) ClientConfiguration {
@@ -52,6 +54,7 @@ func newClientConfiguration(cfg *fgaSdk.Configuration) ClientConfiguration {
5254
Debug: cfg.Debug,
5355
HTTPClient: cfg.HTTPClient,
5456
RetryParams: cfg.RetryParams,
57+
Telemetry: cfg.Telemetry,
5558
}
5659
}
5760

@@ -72,6 +75,7 @@ func NewSdkClient(cfg *ClientConfiguration) (*{{appShortName}}Client, error) {
7275
Debug: cfg.Debug,
7376
HTTPClient: cfg.HTTPClient,
7477
RetryParams: cfg.RetryParams,
78+
Telemetry: cfg.Telemetry,
7579
})
7680

7781
if err != nil {
@@ -2008,17 +2012,23 @@ func (client *{{appShortName}}Client) BatchCheckExecute(request SdkClientBatchCh
20082012
return nil, err
20092013
}
20102014

2015+
checkOptions := &ClientCheckOptions{
2016+
AuthorizationModelId: authorizationModelId,
2017+
StoreId: storeId,
2018+
}
2019+
2020+
if request.GetOptions() != nil && request.GetOptions().Consistency != nil {
2021+
checkOptions.Consistency = request.GetOptions().Consistency
2022+
}
2023+
20112024
for index, checkBody := range *request.GetBody() {
20122025
index, checkBody := index, checkBody
20132026
group.Go(func() error {
20142027
singleResponse, err := client.CheckExecute(&SdkClientCheckRequest{
20152028
ctx: ctx,
20162029
Client: client,
20172030
body: &checkBody,
2018-
options: &ClientCheckOptions{
2019-
AuthorizationModelId: authorizationModelId,
2020-
StoreId: storeId,
2021-
},
2031+
options: checkOptions,
20222032
})
20232033

20242034
if _, ok := err.(fgaSdk.FgaApiAuthenticationError); ok {

config/clients/go/template/client/client_test.mustache

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2191,7 +2191,7 @@ func Test{{appShortName}}Client(t *testing.T) {
21912191
options := ClientBatchCheckOptions{
21922192
AuthorizationModelId: {{packageName}}.PtrString(authModelId),
21932193
MaxParallelRequests: {{packageName}}.PtrInt32(5),
2194-
Consistency: {{packageName}}.CONSISTENCYPREFERENCE_UNSPECIFIED.Ptr(),
2194+
Consistency: {{packageName}}.CONSISTENCYPREFERENCE_HIGHER_CONSISTENCY.Ptr(),
21952195
}
21962196

21972197
var expectedResponse {{packageName}}.CheckResponse
@@ -2202,7 +2202,7 @@ func Test{{appShortName}}Client(t *testing.T) {
22022202
httpmock.Activate()
22032203
defer httpmock.DeactivateAndReset()
22042204
httpmock.RegisterMatcherResponder(test.Method, fmt.Sprintf("%s/stores/%s/%s", fgaClient.GetConfig().ApiUrl, getStoreId(t, fgaClient), test.RequestPath),
2205-
httpmock.BodyContainsString(`"consistency":"UNSPECIFIED"`),
2205+
httpmock.BodyContainsString(`"consistency":"HIGHER_CONSISTENCY"`),
22062206
func(req *http.Request) (*http.Response, error) {
22072207
resp, err := httpmock.NewJsonResponse(test.ResponseStatus, expectedResponse)
22082208
if err != nil {
@@ -2212,10 +2212,16 @@ func Test{{appShortName}}Client(t *testing.T) {
22122212
},
22132213
)
22142214

2215-
_, err := fgaClient.BatchCheck(context.Background()).Body(requestBody).Options(options).Execute()
2215+
checks, err := fgaClient.BatchCheck(context.Background()).Body(requestBody).Options(options).Execute()
22162216
if err != nil {
22172217
t.Fatalf("%v", err)
22182218
}
2219+
2220+
for _, check := range *checks {
2221+
if check.Error != nil {
2222+
t.Fatalf("a check failed %v", check.Error)
2223+
}
2224+
}
22192225
})
22202226

22212227
t.Run("Expand", func(t *testing.T) {

config/clients/go/template/configuration.mustache

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"net/http"
66

77
"{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/credentials"
8+
"{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/telemetry"
89
)
910

1011
const (
@@ -34,6 +35,7 @@ type Configuration struct {
3435
Debug bool `json:"debug,omitempty"`
3536
HTTPClient *http.Client
3637
RetryParams *RetryParams
38+
Telemetry *telemetry.Configuration `json:"telemetry,omitempty"`
3739
}
3840

3941
// DefaultRetryParams returns the default retry parameters
@@ -70,6 +72,7 @@ func NewConfiguration(config Configuration) (*Configuration, error) {
7072
Debug: config.Debug,
7173
HTTPClient: config.HTTPClient,
7274
RetryParams: config.RetryParams,
75+
Telemetry: config.Telemetry,
7376
}
7477

7578
if cfg.UserAgent == "" {
@@ -80,6 +83,10 @@ func NewConfiguration(config Configuration) (*Configuration, error) {
8083
cfg.DefaultHeaders = make(map[string]string)
8184
}
8285

86+
if cfg.Telemetry == nil {
87+
cfg.Telemetry = telemetry.DefaultTelemetryConfiguration()
88+
}
89+
8390
err := cfg.ValidateConfig()
8491

8592
if err != nil {

0 commit comments

Comments
 (0)