Skip to content

Commit

Permalink
feat(go-sdk): support OpenTelemetry metrics (#428)
Browse files Browse the repository at this point in the history
  • Loading branch information
ewanharris authored Oct 10, 2024
2 parents d3b75c2 + 08996ce commit 8b4e17b
Show file tree
Hide file tree
Showing 41 changed files with 2,105 additions and 19 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ clients/**/*
# JetBrains IDEs
.idea/
*.iml

config/clients/dotnet/template/example/Example1/obj
config/clients/dotnet/template/example/Example1/bin/
21 changes: 21 additions & 0 deletions config/clients/go/CHANGELOG.md.mustache
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# Changelog

## v0.6.1

### [0.6.1](https://github.com/openfga/go-sdk/compare/v0.6.0...v0.6.1) (2024-09-23)

- refactor(OpenTelemetry): move configuration API into public package namespace (#122)
- docs(OpenTelemetry): initial documentation and example (#123)

## v0.6.0

### [0.6.0](https://github.com/openfga/go-sdk/compare/v0.5.0...v0.6.0) (2024-08-29)

- feat: support OpenTelemetry metrics reporting (#115)
- feat!: support for sending the consistency parameter to the read, check, list users, list objects, and expand endpoints (#117)
- chore(docs): update stale README (#113) - thanks @Code2Life

BREAKING CHANGE:

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).

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.

## v0.5.0

### [0.5.0](https://{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/compare/v0.4.0...v0.5.0) (2024-06-14)
Expand Down
100 changes: 99 additions & 1 deletion config/clients/go/config.overrides.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
"sdkId": "go",
"gitRepoId": "go-sdk",
"packageName": "openfga",
"packageVersion": "0.5.0",
"packageVersion": "0.6.1",
"packageDescription": "Go SDK for OpenFGA",
"packageDetailedDescription": "This is an autogenerated Go SDK for OpenFGA. It provides a wrapper around the [OpenFGA API definition](https://openfga.dev/api).",
"fossaComplianceNoticeId": "41c01c64-f74a-414a-9e39-7aeca87bc47b",
"generateInterfaces": true,
"enumClassPrefix": true,
"openTelemetryDocumentation": "opentelemetry.md",
"files": {
".github/workflows/main.yaml.mustache": {
"destinationFilename": ".github/workflows/main.yaml",
Expand Down Expand Up @@ -86,6 +87,103 @@
"example/example1/go.mod.mustache": {
"destinationFilename": "example/example1/go.mod",
"templateType": "SupportingFiles"
},
"example/opentelemetry/main.go": {},
"example/opentelemetry/go.mod.mustache": {
"destinationFilename": "example/opentelemetry/go.mod",
"templateType": "SupportingFiles"
},
"docs/opentelemetry.md.mustache": {
"destinationFilename": "docs/opentelemetry.md",
"templateType": "SupportingFiles"
},
"telemetry/attribute.mustache": {
"destinationFilename": "telemetry/attribute.go",
"templateType": "SupportingFiles"
},
"telemetry/attribute_test.mustache": {
"destinationFilename": "telemetry/attribute_test.go",
"templateType": "SupportingFiles"
},
"telemetry/attributes.mustache": {
"destinationFilename": "telemetry/attributes.go",
"templateType": "SupportingFiles"
},
"telemetry/attributes_test.mustache": {
"destinationFilename": "telemetry/attributes_test.go",
"templateType": "SupportingFiles"
},
"telemetry/configuration.mustache": {
"destinationFilename": "telemetry/configuration.go",
"templateType": "SupportingFiles"
},
"telemetry/configuration_test.mustache": {
"destinationFilename": "telemetry/configuration_test.go",
"templateType": "SupportingFiles"
},
"telemetry/counter.mustache": {
"destinationFilename": "telemetry/counter.go",
"templateType": "SupportingFiles"
},
"telemetry/counter_test.mustache": {
"destinationFilename": "telemetry/counter_test.go",
"templateType": "SupportingFiles"
},
"telemetry/counters.mustache": {
"destinationFilename": "telemetry/counters.go",
"templateType": "SupportingFiles"
},
"telemetry/counters_test.mustache": {
"destinationFilename": "telemetry/counters_test.go",
"templateType": "SupportingFiles"
},
"telemetry/histogram.mustache": {
"destinationFilename": "telemetry/histogram.go",
"templateType": "SupportingFiles"
},
"telemetry/histogram_test.mustache": {
"destinationFilename": "telemetry/histogram_test.go",
"templateType": "SupportingFiles"
},
"telemetry/histograms.mustache": {
"destinationFilename": "telemetry/histograms.go",
"templateType": "SupportingFiles"
},
"telemetry/histograms_test.mustache": {
"destinationFilename": "telemetry/histograms_test.go",
"templateType": "SupportingFiles"
},
"telemetry/interfaces.mustache": {
"destinationFilename": "telemetry/interfaces.go",
"templateType": "SupportingFiles"
},
"telemetry/interfaces_test.mustache": {
"destinationFilename": "telemetry/interfaces_test.go",
"templateType": "SupportingFiles"
},
"telemetry/metric.mustache": {
"destinationFilename": "telemetry/metric.go",
"templateType": "SupportingFiles"
},
"telemetry/metric_test.mustache": {
"destinationFilename": "telemetry/metric_test.go",
"templateType": "SupportingFiles"
},
"telemetry/metrics.mustache": {
"destinationFilename": "telemetry/metrics.go",
"templateType": "SupportingFiles"
},
"telemetry/metrics_test.mustache": {
"destinationFilename": "telemetry/metrics_test.go",
"templateType": "SupportingFiles"
},
"telemetry/telemetry.mustache": {
"destinationFilename": "telemetry/telemetry.go",
"templateType": "SupportingFiles"
},
"telemetry/telemetry_test.mustache": {
"destinationFilename": "telemetry/telemetry_test.go",
"templateType": "SupportingFiles"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
with:
fetch-depth: 0

Expand All @@ -34,7 +34,7 @@ jobs:
run: govulncheck ./...

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

steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
with:
fetch-depth: 0

Expand Down
4 changes: 2 additions & 2 deletions config/clients/go/template/README_retries.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ Apply your custom retry values by passing this struct to the `ClientConfiguratio
import (
"os"

openfga "github.com/openfga/go-sdk"
. "github.com/openfga/go-sdk/client"
openfga "{{gitHost}}/{{gitUserId}}/{{gitRepoId}}"
. "{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/client"
)

func main() {
Expand Down
26 changes: 25 additions & 1 deletion config/clients/go/template/api.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import (
{{#imports}} "{{import}}"
{{/imports}}

"{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/internal/utils"
internalutils "{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/internal/utils"
telemetry "{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/telemetry"
)

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

{{/returnType}}
metrics := telemetry.GetMetrics(telemetry.TelemetryFactoryParameters{Configuration: a.client.cfg.Telemetry})

var attrs, queryDuration, requestDuration, _ = metrics.BuildTelemetryAttributes(
"{{nickname}}",
map[string]interface{}{ {{#pathParams.0}}
"storeId": r.storeId,{{/pathParams.0}}
"body": localVarPostBody,
},
req,
localVarHTTPResponse,
requestStarted,
i,
)

if requestDuration > 0 {
metrics.RequestDuration(requestDuration, attrs)
}

if queryDuration > 0 {
metrics.QueryDuration(queryDuration, attrs)
}

return {{#returnType}}localVarReturnValue, {{/returnType}}localVarHTTPResponse, nil
}
// should never have reached this
Expand Down
4 changes: 4 additions & 0 deletions config/clients/go/template/api_client.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import (
"strings"
"time"
"unicode/utf8"

"{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/telemetry"
)

var (
Expand Down Expand Up @@ -67,6 +69,8 @@ func NewAPIClient(cfg *Configuration) *APIClient {
if cfg.Credentials == nil {
cfg.HTTPClient = http.DefaultClient
} else {
cfg.Credentials.Context = context.Background()
telemetry.Bind(cfg.Credentials.Context, telemetry.Get(telemetry.TelemetryFactoryParameters{Configuration: cfg.Telemetry}))
var httpClient, headers = cfg.Credentials.GetHttpClientAndHeaderOverrides()
if len(headers) > 0 {
for idx := range headers {
Expand Down
18 changes: 14 additions & 4 deletions config/clients/go/template/client/client.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
fgaSdk "{{gitHost}}/{{gitUserId}}/{{gitRepoId}}"
"{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/credentials"
internalutils "{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/internal/utils"
"{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/telemetry"
"golang.org/x/sync/errgroup"
)

Expand Down Expand Up @@ -39,6 +40,7 @@ type ClientConfiguration struct {
Debug bool `json:"debug"`
HTTPClient *_nethttp.Client
RetryParams *fgaSdk.RetryParams
Telemetry *telemetry.Configuration `json:"telemetry,omitempty"`
}

func newClientConfiguration(cfg *fgaSdk.Configuration) ClientConfiguration {
Expand All @@ -52,6 +54,7 @@ func newClientConfiguration(cfg *fgaSdk.Configuration) ClientConfiguration {
Debug: cfg.Debug,
HTTPClient: cfg.HTTPClient,
RetryParams: cfg.RetryParams,
Telemetry: cfg.Telemetry,
}
}

Expand All @@ -72,6 +75,7 @@ func NewSdkClient(cfg *ClientConfiguration) (*{{appShortName}}Client, error) {
Debug: cfg.Debug,
HTTPClient: cfg.HTTPClient,
RetryParams: cfg.RetryParams,
Telemetry: cfg.Telemetry,
})

if err != nil {
Expand Down Expand Up @@ -2008,17 +2012,23 @@ func (client *{{appShortName}}Client) BatchCheckExecute(request SdkClientBatchCh
return nil, err
}

checkOptions := &ClientCheckOptions{
AuthorizationModelId: authorizationModelId,
StoreId: storeId,
}

if request.GetOptions() != nil && request.GetOptions().Consistency != nil {
checkOptions.Consistency = request.GetOptions().Consistency
}

for index, checkBody := range *request.GetBody() {
index, checkBody := index, checkBody
group.Go(func() error {
singleResponse, err := client.CheckExecute(&SdkClientCheckRequest{
ctx: ctx,
Client: client,
body: &checkBody,
options: &ClientCheckOptions{
AuthorizationModelId: authorizationModelId,
StoreId: storeId,
},
options: checkOptions,
})

if _, ok := err.(fgaSdk.FgaApiAuthenticationError); ok {
Expand Down
12 changes: 9 additions & 3 deletions config/clients/go/template/client/client_test.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -2191,7 +2191,7 @@ func Test{{appShortName}}Client(t *testing.T) {
options := ClientBatchCheckOptions{
AuthorizationModelId: {{packageName}}.PtrString(authModelId),
MaxParallelRequests: {{packageName}}.PtrInt32(5),
Consistency: {{packageName}}.CONSISTENCYPREFERENCE_UNSPECIFIED.Ptr(),
Consistency: {{packageName}}.CONSISTENCYPREFERENCE_HIGHER_CONSISTENCY.Ptr(),
}

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

_, err := fgaClient.BatchCheck(context.Background()).Body(requestBody).Options(options).Execute()
checks, err := fgaClient.BatchCheck(context.Background()).Body(requestBody).Options(options).Execute()
if err != nil {
t.Fatalf("%v", err)
}

for _, check := range *checks {
if check.Error != nil {
t.Fatalf("a check failed %v", check.Error)
}
}
})

t.Run("Expand", func(t *testing.T) {
Expand Down
7 changes: 7 additions & 0 deletions config/clients/go/template/configuration.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net/http"

"{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/credentials"
"{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/telemetry"
)

const (
Expand Down Expand Up @@ -34,6 +35,7 @@ type Configuration struct {
Debug bool `json:"debug,omitempty"`
HTTPClient *http.Client
RetryParams *RetryParams
Telemetry *telemetry.Configuration `json:"telemetry,omitempty"`
}

// DefaultRetryParams returns the default retry parameters
Expand Down Expand Up @@ -70,6 +72,7 @@ func NewConfiguration(config Configuration) (*Configuration, error) {
Debug: config.Debug,
HTTPClient: config.HTTPClient,
RetryParams: config.RetryParams,
Telemetry: config.Telemetry,
}

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

if cfg.Telemetry == nil {
cfg.Telemetry = telemetry.DefaultTelemetryConfiguration()
}

err := cfg.ValidateConfig()

if err != nil {
Expand Down
Loading

0 comments on commit 8b4e17b

Please sign in to comment.