Skip to content
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

ref: Helpers #204

Merged
merged 4 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading