Skip to content

Commit

Permalink
ref: Helpers (#204)
Browse files Browse the repository at this point in the history
* fix docstrings

* remove pkg/helpers/helpers.go

* Add test helpers

* refactors based on coderabbit suggestions
  • Loading branch information
mgaeta authored Aug 20, 2024
1 parent c993ccb commit a743ff5
Show file tree
Hide file tree
Showing 17 changed files with 521 additions and 237 deletions.
2 changes: 1 addition & 1 deletion pkg/annotations/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (

type Annotations []*anypb.Any

// Convenience function to create annotations.
// New - Convenience function to create annotations.
func New(msgs ...proto.Message) Annotations {
annos := Annotations{}
for _, msg := range msgs {
Expand Down
2 changes: 1 addition & 1 deletion pkg/field/fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (s SchemaField) String() (string, error) {
return value, nil
}

// StringSlice retuns the default value as a string array.
// StringSlice returns the default value as a string array.
func (s SchemaField) StringSlice() ([]string, error) {
value, ok := s.DefaultValue.([]string)
if !ok {
Expand Down
2 changes: 1 addition & 1 deletion pkg/field/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func (e *ErrConfigurationMissingFields) Push(err error) {
// - repeated fields (by name) are defined
// - if sets of fields are mutually exclusive and required
// together at the same time
// - if fields depedent on themselves
// - if fields depend on themselves
func Validate(c Configuration, v *viper.Viper) error {
present := make(map[string]int)
missingFieldsError := &ErrConfigurationMissingFields{}
Expand Down
164 changes: 11 additions & 153 deletions pkg/helpers/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,171 +2,29 @@ package helpers

import (
"net/http"
"strconv"
"strings"
"time"

v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/conductorone/baton-sdk/pkg/ratelimit"
"github.com/conductorone/baton-sdk/pkg/types/resource"
"github.com/conductorone/baton-sdk/pkg/uhttp"
)

// Deprecated: see resource.SplitFullName.
func SplitFullName(name string) (string, string) {
names := strings.SplitN(name, " ", 2)
var firstName, lastName string

switch len(names) {
case 1:
firstName = names[0]
case 2:
firstName = names[0]
lastName = names[1]
}

return firstName, lastName
}

var limitHeaders = []string{
"X-Ratelimit-Limit",
"Ratelimit-Limit",
"X-RateLimit-Requests-Limit", // Linear uses a non-standard header
}

var remainingHeaders = []string{
"X-Ratelimit-Remaining",
"Ratelimit-Remaining",
"X-RateLimit-Requests-Remaining", // Linear uses a non-standard header
}

var resetAtHeaders = []string{
"X-Ratelimit-Reset",
"Ratelimit-Reset",
"X-RateLimit-Requests-Reset", // Linear uses a non-standard header
"Retry-After", // Often returned with 429
}

const thirtyYears = 60 * 60 * 24 * 365 * (2000 - 1970)

// Many APIs don't follow standards and return incorrect datetimes. This function tries to handle those cases.
func parseTime(timeStr string) (time.Time, error) {
var t time.Time
res, err := strconv.ParseInt(timeStr, 10, 64)
if err != nil {
t, err = time.Parse(time.RFC850, timeStr)
if err != nil {
// Datetimes should be RFC850 but some APIs return RFC3339
t, err = time.Parse(time.RFC3339, timeStr)
}
return t, err
}

// Times are supposed to be in seconds, but some APIs return milliseconds
if res > thirtyYears*1000 {
res /= 1000
}

// Times are supposed to be offsets, but some return absolute seconds since 1970.
if res > thirtyYears {
// If more than 30 years, it's probably an absolute timestamp
t = time.Unix(res, 0)
} else {
// Otherwise, it's a relative timestamp
t = time.Now().Add(time.Second * time.Duration(res))
}

return t, nil
return resource.SplitFullName(name)
}

// Deprecated: see ratelimit.ExtractRateLimitData.
func ExtractRateLimitData(statusCode int, header *http.Header) (*v2.RateLimitDescription, error) {
if header == nil {
return nil, nil
}

var rlstatus v2.RateLimitDescription_Status

var limit int64
var err error
for _, limitHeader := range limitHeaders {
limitStr := header.Get(limitHeader)
if limitStr != "" {
limit, err = strconv.ParseInt(limitStr, 10, 64)
if err != nil {
return nil, err
}
break
}
}

var remaining int64
for _, remainingHeader := range remainingHeaders {
remainingStr := header.Get(remainingHeader)
if remainingStr != "" {
remaining, err = strconv.ParseInt(remainingStr, 10, 64)
if err != nil {
return nil, err
}
break
}
}
if remaining > 0 {
rlstatus = v2.RateLimitDescription_STATUS_OK
}

var resetAt time.Time
for _, resetAtHeader := range resetAtHeaders {
resetAtStr := header.Get(resetAtHeader)
if resetAtStr != "" {
resetAt, err = parseTime(resetAtStr)
if err != nil {
return nil, err
}
break
}
}

if statusCode == http.StatusTooManyRequests {
rlstatus = v2.RateLimitDescription_STATUS_OVERLIMIT
remaining = 0
}

// If we didn't get any rate limit headers and status code is 429, return some sane defaults
if remaining == 0 && resetAt.IsZero() && rlstatus == v2.RateLimitDescription_STATUS_OVERLIMIT {
limit = 1
resetAt = time.Now().Add(time.Second * 60)
}

return &v2.RateLimitDescription{
Status: rlstatus,
Limit: limit,
Remaining: remaining,
ResetAt: timestamppb.New(resetAt),
}, nil
return ratelimit.ExtractRateLimitData(statusCode, header)
}

// Deprecated: see contenttype.IsJSONContentType.
func IsJSONContentType(contentType string) bool {
if !strings.HasPrefix(contentType, "application") {
return false
}

if !strings.Contains(contentType, "json") {
return false
}

return true
}

var xmlContentTypes []string = []string{
"text/xml",
"application/xml",
return uhttp.IsJSONContentType(contentType)
}

// Deprecated: see contenttype.IsXMLContentType.
func IsXMLContentType(contentType string) bool {
// there are some janky APIs out there
normalizedContentType := strings.TrimSpace(strings.ToLower(contentType))

for _, xmlContentType := range xmlContentTypes {
if strings.HasPrefix(normalizedContentType, xmlContentType) {
return true
}
}
return false
return uhttp.IsXMLContentType(contentType)
}
73 changes: 0 additions & 73 deletions pkg/helpers/helpers_test.go

This file was deleted.

2 changes: 1 addition & 1 deletion pkg/ratelimit/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func getRatelimitDescriptors(ctx context.Context, method string, in interface{},
return ret
}

// UnaryServerInterceptor returns a new unary server interceptors that adds zap.Logger to the context.
// UnaryInterceptor returns a new unary server interceptors that adds zap.Logger to the context.
func UnaryInterceptor(now func() time.Time, descriptors ...*ratelimitV1.RateLimitDescriptors_Entry) grpc.UnaryClientInterceptor {
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
// If this is a call to the rate limit service, skip it
Expand Down
Loading

0 comments on commit a743ff5

Please sign in to comment.