Skip to content

Commit 293d9d0

Browse files
authored
feat(okta): Add rate limiter config (#10880)
Closes #10570
1 parent bb4fb01 commit 293d9d0

File tree

4 files changed

+115
-45
lines changed

4 files changed

+115
-45
lines changed

plugins/source/okta/client/client.go

Lines changed: 12 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,14 @@ package client
22

33
import (
44
"context"
5-
"errors"
65
"fmt"
7-
"os"
6+
"time"
87

98
"github.com/cloudquery/plugin-pb-go/specs"
109
"github.com/cloudquery/plugin-sdk/v2/plugins/source"
1110
"github.com/cloudquery/plugin-sdk/v2/schema"
1211
"github.com/okta/okta-sdk-golang/v3/okta"
1312
"github.com/rs/zerolog"
14-
"github.com/thoas/go-funk"
1513
)
1614

1715
type Client struct {
@@ -23,8 +21,6 @@ type Client struct {
2321
*okta.APIClient
2422
}
2523

26-
const exampleDomain = "https://<CHANGE_THIS_TO_YOUR_OKTA_DOMAIN>.okta.com"
27-
2824
func (c *Client) Logger() *zerolog.Logger {
2925
return &c.logger
3026
}
@@ -42,48 +38,25 @@ func New(logger zerolog.Logger, s specs.Source, okt *okta.APIClient) *Client {
4238
}
4339
}
4440

45-
func Configure(_ context.Context, logger zerolog.Logger, s specs.Source, _ source.Options) (schema.ClientMeta, error) {
46-
oktaSpec := &Spec{}
47-
if err := s.UnmarshalSpec(oktaSpec); err != nil {
41+
func Configure(_ context.Context, logger zerolog.Logger, srcSpec specs.Source, _ source.Options) (schema.ClientMeta, error) {
42+
spec := &Spec{}
43+
if err := srcSpec.UnmarshalSpec(spec); err != nil {
4844
return nil, fmt.Errorf("failed to unmarshal okta spec: %w", err)
4945
}
5046

51-
oktaToken, ok := os.LookupEnv("OKTA_API_TOKEN")
52-
if !ok {
53-
if oktaSpec.Token == "" {
54-
return nil, errors.New("missing OKTA_API_TOKEN, either set it as an environment variable or pass it in the configuration")
55-
}
56-
57-
oktaToken = oktaSpec.Token
58-
}
59-
60-
if oktaSpec.Domain == "" || oktaSpec.Domain == exampleDomain {
61-
return nil, errors.New(`failed to configure provider, please set your okta "domain" in okta.yml`)
47+
spec.setDefaults()
48+
if err := spec.validate(); err != nil {
49+
return nil, err
6250
}
6351

6452
cf := okta.NewConfiguration(
65-
okta.WithOrgUrl(oktaSpec.Domain),
66-
okta.WithToken(oktaToken),
53+
okta.WithOrgUrl(spec.Domain),
54+
okta.WithToken(spec.Token),
6755
okta.WithCache(true),
56+
okta.WithRateLimitMaxBackOff(int64(spec.RateLimit.MaxBackoff/time.Second)), // this param takes int64 of seconds
57+
okta.WithRateLimitMaxRetries(spec.RateLimit.MaxRetries),
6858
)
6959
c := okta.NewAPIClient(cf)
7060

71-
return New(logger, s, c), nil
72-
}
73-
74-
func ResolveNullableTime(path string) schema.ColumnResolver {
75-
return func(ctx context.Context, meta schema.ClientMeta, resource *schema.Resource, c schema.Column) error {
76-
data := funk.Get(resource.Item, path)
77-
if data == nil {
78-
return nil
79-
}
80-
ts, ok := data.(okta.NullableTime)
81-
if !ok {
82-
return fmt.Errorf("unexpected type, want \"okta.NullableTime\", have \"%T\"", data)
83-
}
84-
if !ts.IsSet() {
85-
return resource.Set(c.Name, nil)
86-
}
87-
return resource.Set(c.Name, ts.Get())
88-
}
61+
return New(logger, srcSpec, c), nil
8962
}

plugins/source/okta/client/spec.go

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,60 @@
11
package client
22

3-
type Spec struct {
4-
Token string `json:"token,omitempty"`
5-
Domain string `json:"domain"`
3+
import (
4+
"fmt"
5+
"os"
6+
"time"
7+
)
8+
9+
type (
10+
Spec struct {
11+
Token string `json:"token,omitempty"`
12+
Domain string `json:"domain,omitempty"`
13+
RateLimit *RateLimit `json:"rate_limit,omitempty"`
14+
}
15+
RateLimit struct {
16+
MaxBackoff time.Duration `json:"max_backoff,omitempty"`
17+
MaxRetries int32 `json:"max_retries,omitempty"`
18+
}
19+
)
20+
21+
const (
22+
OktaAPIToken = "OKTA_API_TOKEN"
23+
)
24+
25+
func (s *Spec) setDefaults() {
26+
const (
27+
minRetries = int32(3)
28+
minBackOff = 5 * time.Second
29+
)
30+
31+
if s.RateLimit == nil {
32+
s.RateLimit = new(RateLimit)
33+
}
34+
35+
if s.RateLimit.MaxRetries < minRetries {
36+
s.RateLimit.MaxRetries = minRetries
37+
}
38+
39+
if s.RateLimit.MaxBackoff < minBackOff {
40+
s.RateLimit.MaxBackoff = minBackOff
41+
}
42+
43+
if len(s.Token) == 0 {
44+
s.Token = os.Getenv(OktaAPIToken)
45+
}
46+
}
47+
48+
func (s *Spec) validate() error {
49+
if len(s.Token) == 0 {
50+
return fmt.Errorf("missing API token (should be set in the configuration or as %q environment variable)", OktaAPIToken)
51+
}
52+
53+
const exampleDomain = "https://<CHANGE_THIS_TO_YOUR_OKTA_DOMAIN>.okta.com"
54+
switch s.Domain {
55+
case "", exampleDomain:
56+
return fmt.Errorf("missing \"domain\" in plugin configuration")
57+
}
58+
59+
return nil
660
}

plugins/source/okta/client/transformers.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package client
22

33
import (
4+
"context"
5+
"fmt"
46
"reflect"
57

68
"github.com/cloudquery/plugin-sdk/v2/schema"
79
"github.com/cloudquery/plugin-sdk/v2/transformers"
810
"github.com/okta/okta-sdk-golang/v3/okta"
11+
"github.com/thoas/go-funk"
912
)
1013

1114
var options = []transformers.StructTransformerOption{
@@ -27,8 +30,25 @@ func typeTransformer(field reflect.StructField) (schema.ValueType, error) {
2730

2831
func resolverTransformer(field reflect.StructField, path string) schema.ColumnResolver {
2932
if field.Type == reflect.TypeOf(okta.NullableTime{}) {
30-
return ResolveNullableTime(path)
33+
return resolveNullableTime(path)
3134
}
3235

3336
return transformers.DefaultResolverTransformer(field, path)
3437
}
38+
39+
func resolveNullableTime(path string) schema.ColumnResolver {
40+
return func(ctx context.Context, meta schema.ClientMeta, resource *schema.Resource, c schema.Column) error {
41+
data := funk.Get(resource.Item, path)
42+
if data == nil {
43+
return nil
44+
}
45+
ts, ok := data.(okta.NullableTime)
46+
if !ok {
47+
return fmt.Errorf("unexpected type, want \"okta.NullableTime\", have \"%T\"", data)
48+
}
49+
if !ts.IsSet() {
50+
return resource.Set(c.Name, nil)
51+
}
52+
return resource.Set(c.Name, ts.Get())
53+
}
54+
}

website/pages/docs/plugins/sources/okta/overview.mdx

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,31 @@ The following example sets up the Okta plugin, and connects it to a postgresql d
2222

2323
<Configuration/>
2424

25-
- `domain` (Required) - Specify the Okta domain you are fetching from. [Visit this link](https://developer.okta.com/docs/guides/find-your-domain/findorg/) to find your Okta domain
26-
- `token` (Optional) - Okta Token to access the API. You can set this with an `OKTA_API_TOKEN` environment variable
25+
- `domain` (`string`, required)
26+
27+
Specify the Okta domain you are fetching from.
28+
[Visit this link](https://developer.okta.com/docs/guides/find-your-domain/findorg/) to find your Okta domain.
29+
30+
- `token` (`string`, optional)
31+
32+
Token for Okta API access.
33+
You can set this with an `OKTA_API_TOKEN` environment variable.
34+
35+
- `rate_limit` ([Rate limit](#rate-limit-spec) spec, optional. Default: see [rate limit](#rate-limit-spec) spec defaults)
36+
37+
Rate limit configuration.
38+
39+
### Rate limit spec
40+
41+
- `max_backoff` (`duration`, optional. Default: `5s`)
42+
43+
Max backoff interval to be used.
44+
If the value specified is less than the default one, the default one is used.
45+
46+
- `max_retries` (`int32`, optional. Default: `3`)
47+
48+
Max retries to be performed.
49+
If the value specified is less than the default one, the default one is used.
2750

2851
## Example Queries
2952

0 commit comments

Comments
 (0)