diff --git a/.gitignore b/.gitignore index a63de16..a5d4f19 100644 --- a/.gitignore +++ b/.gitignore @@ -1,26 +1,27 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll *.so +*.dylib -# Folders -_obj -_test -vendor/bundle -.local - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* +# Test binary, built with `go test -c` +*.test -_testmain.go +# Output of the go coverage tool, specifically when used with LiteIDE +*.out -*.exe -*.test -*.prof +# misc +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +Icon? +ehthumbs.db +Thumbs.db +*~ +*.swp +*.swo +.vscode +.idea diff --git a/README.md b/README.md index 5012eb5..1d480b0 100644 --- a/README.md +++ b/README.md @@ -33,23 +33,44 @@ aws-sdk-go-wrapper Simple wrapper for aws-sdk-go At this time, it suports services below, -- [`DynamoDB`](https://github.com/evalphobia/aws-sdk-go-wrapper/tree/master/dynamodb) - - ListTables - - DescribeTable +- [`CloudWatch`](/cloudwatch) + - GetMetricStatistics +- [`CostExplorer`](/costexplorer) + - GetCostAndUsage +- [`DynamoDB`](/dynamodb) + - BatchWriteItem - CreateTable - - UpdateTable + - DeleteItem - DeleteTable - - Scan - - Query + - DescribeTable - GetItem + - ListTables - PutItem - - DeleteItem -- [`Kinesis`](https://github.com/evalphobia/aws-sdk-go-wrapper/tree/master/kinesis) - - DescribeStream + - Query + - UpdateTable + - Scan +- [`IAM`](/iam) + - GetGroup + - GetGroupPolicy + - GetPolicyVersion + - GetRolePolicy + - GetUserPolicy + - ListEntitiesForPolicy + - ListGroups + - ListGroupPolicies + - ListPolicies + - ListUsers + - ListUserPolicies + - ListRoles + - ListRolePolicies +- [`Kinesis`](/kinesis) - CreateStream + - DeleteStream + - DescribeStream + - GetRecords - GetShardIterator - PutRecord -- [`KMS`](https://github.com/evalphobia/aws-sdk-go-wrapper/tree/master/kms) +- [`KMS`](/kms) - CreateAlias - CreateKey - Decrypt @@ -57,36 +78,51 @@ At this time, it suports services below, - Encrypt - ReEncrypt - ScheduleKeyDeletion -- [`Rekognition`](https://github.com/evalphobia/aws-sdk-go-wrapper/tree/master/rekognition) +- [`Pinpoint`](/pinpoint) + - SendEmail +- [`Rekognition`](/rekognition) - CompareFaces + - CreateCollection + - DeleteCollection + - DeleteFaces - DetectFaces - DetectLabels - DetectModerationLabels - GetCelebrityInfo + - IndexFaces + - ListCollections + - ListFaces - RecognizeCelebrities -- [`S3`](https://github.com/evalphobia/aws-sdk-go-wrapper/tree/master/s3) + - SearchFaces + - SearchFacesByImage +- [`S3`](/s3) + - CreateBucket + - DeleteBucket + - DeleteObject - GetObject + - HeadObject - PutObject - - DeleteObject -- [`SNS`](https://github.com/evalphobia/aws-sdk-go-wrapper/tree/master/sns) +- [`SNS`](/sns) - CreatePlatformEndpoint - CreateTopic - DeleteTopic - - Subscribe - - Publish - GetEndpointAttributes + - Publish - SetEndpointAttributes -- [`SQS`](https://github.com/evalphobia/aws-sdk-go-wrapper/tree/master/sqs) - - GetQueueUrl + - Subscribe +- [`SQS`](/sqs) + - ChangeMessageVisibility - CreateQueue - - DeleteQueue - - PurgeQueue - - SendMessageBatch - - ReceiveMessage - DeleteMessage - DeleteMessageBatch + - DeleteQueue - GetQueueAttributes -- [`X-Ray`](https://github.com/evalphobia/aws-sdk-go-wrapper/tree/master/xray) + - GetQueueUrl + - ListQueues + - PurgeQueue + - ReceiveMessage + - SendMessageBatch +- [`X-Ray`](/xray) - PutTraceSegments # Quick Usage diff --git a/costexplorer/client.go b/costexplorer/client.go new file mode 100644 index 0000000..524de05 --- /dev/null +++ b/costexplorer/client.go @@ -0,0 +1,64 @@ +package costexplorer + +import ( + SDK "github.com/aws/aws-sdk-go/service/costexplorer" + + "github.com/evalphobia/aws-sdk-go-wrapper/config" + "github.com/evalphobia/aws-sdk-go-wrapper/log" +) + +const ( + serviceName = "CostExplorer" +) + +// CostExplorer has *SDK.CostExplorer client. +type CostExplorer struct { + client *SDK.CostExplorer + + logger log.Logger +} + +// New returns initialized *CostExplorer. +func New(conf config.Config) (*CostExplorer, error) { + sess, err := conf.Session() + if err != nil { + return nil, err + } + + svc := &CostExplorer{ + client: SDK.New(sess), + logger: log.DefaultLogger, + } + return svc, nil +} + +// SetLogger sets logger. +func (svc *CostExplorer) SetLogger(logger log.Logger) { + svc.logger = logger +} + +// GetCostAndUsage executes GetCostAndUsage operation with customized input. +func (svc *CostExplorer) GetCostAndUsage(input GetCostAndUsageInput) (UsageResult, error) { + return svc.DoGetCostAndUsage(input.ToInput()) +} + +// DoGetCostAndUsage executes GetCostAndUsage operation. +func (svc *CostExplorer) DoGetCostAndUsage(input *SDK.GetCostAndUsageInput) (UsageResult, error) { + output, err := svc.client.GetCostAndUsage(input) + if err != nil { + svc.Errorf("error on `GetCostAndUsage` operation; error=%w;", err) + return UsageResult{}, err + } + + return NewUsageResult(output), nil +} + +// Infof logging information. +func (svc *CostExplorer) Infof(format string, v ...interface{}) { + svc.logger.Infof(serviceName, format, v...) +} + +// Errorf logging error information. +func (svc *CostExplorer) Errorf(format string, v ...interface{}) { + svc.logger.Errorf(serviceName, format, v...) +} diff --git a/costexplorer/request_type.go b/costexplorer/request_type.go new file mode 100644 index 0000000..3a0e8f6 --- /dev/null +++ b/costexplorer/request_type.go @@ -0,0 +1,169 @@ +package costexplorer + +import ( + "time" + + SDK "github.com/aws/aws-sdk-go/service/costexplorer" + "github.com/evalphobia/aws-sdk-go-wrapper/private/pointers" +) + +const ( + GranularityMonthly = SDK.GranularityMonthly + GranularityDaily = SDK.GranularityDaily + GranularityHourly = SDK.GranularityHourly + + GroupByDimension = SDK.GroupDefinitionTypeDimension + GroupByTag = SDK.GroupDefinitionTypeTag + + MetricBlendedCost = SDK.MetricBlendedCost + MetricUnblendedCost = SDK.MetricUnblendedCost + MetricAmortizedCost = SDK.MetricAmortizedCost + MetricNetUnblendedCost = SDK.MetricNetUnblendedCost + MetricNetAmortizedCost = SDK.MetricNetAmortizedCost + MetricUsageQuantity = SDK.MetricUsageQuantity + MetricNormalizedUsageAmount = SDK.MetricNormalizedUsageAmount +) + +// GetCostAndUsageInput is optional parameters for `GetCostAndUsage`. +type GetCostAndUsageInput struct { + NextPageToken string + TimePeriodStart time.Time + TimePeriodEnd time.Time + + GranularityMonthly bool + GranularityDaily bool + GranularityHourly bool + + GroupByDimensionAZ bool + GroupByDimensionInstanceType bool + GroupByDimensionLegalEntityName bool + GroupByDimensionLinkedAccount bool + GroupByDimensionOperation bool + GroupByDimensionPlatform bool + GroupByDimensionPurchaseType bool + GroupByDimensionService bool + GroupByDimensionTenancy bool + GroupByDimensionRecordType bool + GroupByDimensionUsageType bool + GroupByTagKeys []string + + MetricAmortizedCost bool + MetricBlendedCost bool + MetricNetAmortizedCost bool + MetricNetUnblendedCost bool + MetricNormalizedUsageAmount bool + MetricUnblendedCost bool + MetricUsageQuantity bool + + Filter *SDK.Expression +} + +// ToInput converts to *SDK.GetCostAndUsageInput. +func (u GetCostAndUsageInput) ToInput() *SDK.GetCostAndUsageInput { + in := &SDK.GetCostAndUsageInput{} + + // set NextPageToken + if u.NextPageToken != "" { + in.NextPageToken = pointers.String(u.NextPageToken) + } + + // set TimePeriod + if u.TimePeriodEnd.IsZero() { + u.TimePeriodEnd = time.Now().AddDate(0, 0, -1) + } + if u.TimePeriodStart.IsZero() { + u.TimePeriodStart = u.TimePeriodEnd.AddDate(0, 0, -1) + } + + in.TimePeriod = &SDK.DateInterval{ + Start: pointers.String(u.TimePeriodStart.Format("2006-01-02")), + End: pointers.String(u.TimePeriodEnd.Format("2006-01-02")), + } + + // set Granularity + switch { + case u.GranularityDaily: + in.Granularity = pointers.String(GranularityDaily) + case u.GranularityMonthly: + in.Granularity = pointers.String(GranularityMonthly) + case u.GranularityHourly: + in.Granularity = pointers.String(GranularityHourly) + default: + in.Granularity = pointers.String(GranularityDaily) + } + + // set GroupBy + if u.GroupByDimensionAZ { + in.GroupBy = append(in.GroupBy, newGroupDefinitionDimension(SDK.DimensionAz)) + } + if u.GroupByDimensionInstanceType { + in.GroupBy = append(in.GroupBy, newGroupDefinitionDimension(SDK.DimensionInstanceType)) + } + if u.GroupByDimensionLinkedAccount { + in.GroupBy = append(in.GroupBy, newGroupDefinitionDimension(SDK.DimensionLinkedAccount)) + } + if u.GroupByDimensionOperation { + in.GroupBy = append(in.GroupBy, newGroupDefinitionDimension(SDK.DimensionOperation)) + } + if u.GroupByDimensionPurchaseType { + in.GroupBy = append(in.GroupBy, newGroupDefinitionDimension(SDK.DimensionPurchaseType)) + } + if u.GroupByDimensionService { + in.GroupBy = append(in.GroupBy, newGroupDefinitionDimension(SDK.DimensionService)) + } + if u.GroupByDimensionTenancy { + in.GroupBy = append(in.GroupBy, newGroupDefinitionDimension(SDK.DimensionTenancy)) + } + if u.GroupByDimensionRecordType { + in.GroupBy = append(in.GroupBy, newGroupDefinitionDimension(SDK.DimensionRecordType)) + } + if u.GroupByDimensionUsageType { + in.GroupBy = append(in.GroupBy, newGroupDefinitionDimension(SDK.DimensionUsageType)) + } + for _, v := range u.GroupByTagKeys { + in.GroupBy = append(in.GroupBy, newGroupDefinitionTag(v)) + } + + // set Metrics + if u.MetricAmortizedCost { + in.Metrics = append(in.Metrics, pointers.String(SDK.MetricAmortizedCost)) + } + if u.MetricBlendedCost { + in.Metrics = append(in.Metrics, pointers.String(SDK.MetricBlendedCost)) + } + if u.MetricNetAmortizedCost { + in.Metrics = append(in.Metrics, pointers.String(SDK.MetricNetAmortizedCost)) + } + if u.MetricNetUnblendedCost { + in.Metrics = append(in.Metrics, pointers.String(SDK.MetricNetUnblendedCost)) + } + if u.MetricNormalizedUsageAmount { + in.Metrics = append(in.Metrics, pointers.String(SDK.MetricNormalizedUsageAmount)) + } + if u.MetricUnblendedCost { + in.Metrics = append(in.Metrics, pointers.String(SDK.MetricUnblendedCost)) + } + if u.MetricUsageQuantity { + in.Metrics = append(in.Metrics, pointers.String(SDK.MetricUsageQuantity)) + } + if len(in.Metrics) == 0 { + in.Metrics = append(in.Metrics, pointers.String(SDK.MetricUnblendedCost)) + } + + in.Filter = u.Filter + return in +} + +func newGroupDefinitionDimension(key string) *SDK.GroupDefinition { + return &SDK.GroupDefinition{ + Type: pointers.String(GroupByDimension), + Key: pointers.String(key), + } +} + +func newGroupDefinitionTag(key string) *SDK.GroupDefinition { + return &SDK.GroupDefinition{ + Type: pointers.String(GroupByTag), + Key: pointers.String(key), + } +} diff --git a/costexplorer/response_type.go b/costexplorer/response_type.go new file mode 100644 index 0000000..379e567 --- /dev/null +++ b/costexplorer/response_type.go @@ -0,0 +1,220 @@ +package costexplorer + +import ( + SDK "github.com/aws/aws-sdk-go/service/costexplorer" +) + +// UsageResult represents results from `GetCostAndUsage` operation. +type UsageResult struct { + GroupDefinitions []GroupDefinition + ResultsByTime []ResultByTime + NextPageToken string +} + +func NewUsageResult(output *SDK.GetCostAndUsageOutput) UsageResult { + r := UsageResult{} + if output == nil { + return r + } + + if output.NextPageToken != nil { + r.NextPageToken = *output.NextPageToken + } + + r.GroupDefinitions = newGroupDefinitions(output.GroupDefinitions) + r.ResultsByTime = newResultsByTime(output.ResultsByTime) + return r +} + +type GroupDefinition struct { + Key string + Type string +} + +func newGroupDefinitions(list []*SDK.GroupDefinition) []GroupDefinition { + if len(list) == 0 { + return nil + } + + results := make([]GroupDefinition, len(list)) + for i, v := range list { + results[i] = newGroupDefinition(v) + } + return results +} + +func newGroupDefinition(d *SDK.GroupDefinition) GroupDefinition { + result := GroupDefinition{} + if d == nil { + return result + } + + if d.Key != nil { + result.Key = *d.Key + } + if d.Type != nil { + result.Type = *d.Type + } + return result +} + +type ResultByTime struct { + Estimated bool + TimePeriodStart string + TimePeriodEnd string + + Groups []Group + Total MetricValues +} + +func newResultsByTime(list []*SDK.ResultByTime) []ResultByTime { + if len(list) == 0 { + return nil + } + + results := make([]ResultByTime, len(list)) + for i, v := range list { + results[i] = newResultByTime(v) + } + return results +} + +func newResultByTime(d *SDK.ResultByTime) ResultByTime { + result := ResultByTime{} + if d == nil { + return result + } + + if d.Estimated != nil { + result.Estimated = *d.Estimated + } + + if d.TimePeriod != nil { + dt := d.TimePeriod + if dt.Start != nil { + result.TimePeriodStart = *dt.Start + } + if dt.End != nil { + result.TimePeriodEnd = *dt.End + } + } + + result.Groups = newGroups(d.Groups) + result.Total = newMetricValues(d.Total) + return result +} + +type Group struct { + Keys []string + MetricValues +} + +func newGroups(list []*SDK.Group) []Group { + if len(list) == 0 { + return nil + } + + results := make([]Group, len(list)) + for i, v := range list { + results[i] = newGroup(v) + } + return results +} + +func newGroup(g *SDK.Group) Group { + result := Group{} + if g == nil { + return result + } + + if len(g.Keys) != 0 { + keys := make([]string, len(g.Keys)) + for i, v := range g.Keys { + keys[i] = *v + } + result.Keys = keys + } + + result.MetricValues = newMetricValues(g.Metrics) + return result +} + +type MetricValues struct { + AmortizedCost MetricValue + NetAmortizedCost MetricValue + BlendedCost MetricValue + UnblendedCost MetricValue + NetUnblendedCost MetricValue + NormalizedUsageAmount MetricValue + UsageQuantity MetricValue +} + +func newMetricValues(m map[string]*SDK.MetricValue) MetricValues { + result := MetricValues{} + if m == nil { + return result + } + + if v, ok := m["AmortizedCost"]; ok { + result.AmortizedCost = newMetricValue(v) + } + if v, ok := m["NetAmortizedCost"]; ok { + result.NetAmortizedCost = newMetricValue(v) + } + if v, ok := m["BlendedCost"]; ok { + result.BlendedCost = newMetricValue(v) + } + if v, ok := m["UnblendedCost"]; ok { + result.UnblendedCost = newMetricValue(v) + } + if v, ok := m["NetUnblendedCost"]; ok { + result.NetUnblendedCost = newMetricValue(v) + } + if v, ok := m["NormalizedUsageAmount"]; ok { + result.NormalizedUsageAmount = newMetricValue(v) + } + if v, ok := m["UsageQuantity"]; ok { + result.UsageQuantity = newMetricValue(v) + } + return result +} + +func (v MetricValues) GetOne() (amount, unit string) { + switch { + case v.UnblendedCost.Unit != "": + return v.UnblendedCost.Amount, v.UnblendedCost.Unit + case v.BlendedCost.Unit != "": + return v.BlendedCost.Amount, v.BlendedCost.Unit + case v.UsageQuantity.Unit != "": + return v.UsageQuantity.Amount, v.UsageQuantity.Unit + case v.NetUnblendedCost.Unit != "": + return v.NetUnblendedCost.Amount, v.NetUnblendedCost.Unit + case v.AmortizedCost.Unit != "": + return v.AmortizedCost.Amount, v.AmortizedCost.Unit + case v.NetAmortizedCost.Unit != "": + return v.NetAmortizedCost.Amount, v.NetAmortizedCost.Unit + case v.NormalizedUsageAmount.Unit != "": + return v.NormalizedUsageAmount.Amount, v.NormalizedUsageAmount.Unit + } + return "", "" +} + +type MetricValue struct { + Amount string + Unit string +} + +func newMetricValue(v *SDK.MetricValue) MetricValue { + result := MetricValue{} + if v == nil { + return result + } + + if v.Amount != nil { + result.Amount = *v.Amount + } + if v.Unit != nil { + result.Unit = *v.Unit + } + return result +}