//go:build example
// +build example

package main

import (
	"fmt"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/client"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/defaults"
	"github.com/aws/aws-sdk-go/aws/endpoints"
	"github.com/aws/aws-sdk-go/aws/request"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
)

func main() {
	sess := session.Must(
		session.NewSession(&aws.Config{
			// Use a custom retryer to provide custom retry rules.
			Retryer: CustomRetryer{
				DefaultRetryer: client.DefaultRetryer{
					NumMaxRetries: client.DefaultRetryerMaxNumRetries,
				}},

			// Use the SDK's SharedCredentialsProvider directly instead of the
			// SDK's default credential chain. This ensures that the
			// application can call Config.Credentials.Expire. This  is counter
			// to the SDK's default credentials chain, which  will never reread
			// the shared credentials file.
			Credentials: credentials.NewCredentials(&credentials.SharedCredentialsProvider{
				Filename: defaults.SharedCredentialsFilename(),
				Profile:  "default",
			}),
			Region: aws.String(endpoints.UsWest2RegionID),
		}),
	)
	// Add a request handler to the AfterRetry handler stack that is used by the
	// SDK to be executed after the SDK has determined if it will retry.
	// This handler forces the SDK's Credentials to be expired, and next call to
	// Credentials.Get will attempt to refresh the credentials.
	sess.Handlers.AfterRetry.PushBack(func(req *request.Request) {
		if aerr, ok := req.Error.(awserr.RequestFailure); ok && aerr != nil {
			if aerr.Code() == "InvalidClaimException" {
				// Force the credentials to expire based on error code.  Next
				// call to Credentials.Get will attempt to refresh credentials.
				req.Config.Credentials.Expire()
			}
		}
	})

	svc := cloudwatchlogs.New(sess)

	resp, err := svc.DescribeLogGroups(&cloudwatchlogs.DescribeLogGroupsInput{})

	fmt.Println(resp, err)
}

// CustomRetryer wraps the SDK's built in DefaultRetryer adding additional
// custom features. Such as, no retry for 5xx status codes, and refresh
// credentials.
type CustomRetryer struct {
	client.DefaultRetryer
}

// ShouldRetry overrides the SDK's built in DefaultRetryer adding customization
// to not retry 5xx status codes.
func (r CustomRetryer) ShouldRetry(req *request.Request) bool {
	if req.HTTPResponse.StatusCode >= 500 {
		// Don't retry any 5xx status codes.
		return false
	}

	// Fallback to SDK's built in retry rules
	return r.DefaultRetryer.ShouldRetry(req)
}
