126 lines
3.7 KiB
Go
126 lines
3.7 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package billing
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/hashicorp/boundary/internal/billing"
|
|
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
|
"github.com/hashicorp/boundary/internal/daemon/controller/common"
|
|
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
|
|
"github.com/hashicorp/boundary/internal/errors"
|
|
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
|
"github.com/hashicorp/boundary/internal/types/action"
|
|
"github.com/hashicorp/boundary/internal/types/resource"
|
|
"github.com/hashicorp/boundary/internal/types/scope"
|
|
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/billing"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
|
)
|
|
|
|
var (
|
|
// IdActions contains the set of actions that can be performed on
|
|
// individual resources
|
|
IdActions = action.NewActionSet()
|
|
|
|
// CollectionActions contains the set of actions that can be performed on
|
|
// this collection
|
|
CollectionActions = action.NewActionSet(
|
|
action.MonthlyActiveUsers,
|
|
)
|
|
)
|
|
|
|
func init() {
|
|
// TODO: refactor to remove IdActions and CollectionActions package variables
|
|
action.RegisterResource(resource.Billing, IdActions, CollectionActions)
|
|
}
|
|
|
|
type Service struct {
|
|
pbs.UnsafeBillingServiceServer
|
|
|
|
repoFn common.BillingRepoFactory
|
|
}
|
|
|
|
var _ pbs.BillingServiceServer = (*Service)(nil)
|
|
|
|
// NewService returns a billing service which handles billing related requests to boundary.
|
|
func NewService(
|
|
ctx context.Context,
|
|
repoFn common.BillingRepoFactory,
|
|
) (Service, error) {
|
|
const op = "billing.NewService"
|
|
if repoFn == nil {
|
|
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing billing repository")
|
|
}
|
|
return Service{
|
|
repoFn: repoFn,
|
|
}, nil
|
|
}
|
|
|
|
func (s Service) MonthlyActiveUsers(ctx context.Context, req *pbs.MonthlyActiveUsersRequest) (*pbs.MonthlyActiveUsersResponse, error) {
|
|
const op = "billing.(Service).MonthlyActiveUsers"
|
|
|
|
authResults := s.authResult(ctx, action.MonthlyActiveUsers)
|
|
if authResults.Error != nil {
|
|
return nil, errors.Wrap(ctx, authResults.Error, op)
|
|
}
|
|
|
|
// Get the billing information
|
|
repo, err := s.repoFn()
|
|
if err != nil {
|
|
return nil, errors.Wrap(ctx, err, op)
|
|
}
|
|
|
|
var startTime, endTime *time.Time
|
|
if req.GetStartTime() != "" {
|
|
st, err := time.Parse("2006-01", req.GetStartTime())
|
|
if err != nil {
|
|
return nil, handlers.ApiErrorWithCodeAndMessage(codes.InvalidArgument, "start time is in an invalid format")
|
|
}
|
|
startTime = &st
|
|
}
|
|
if req.GetEndTime() != "" {
|
|
et, err := time.Parse("2006-01", req.GetEndTime())
|
|
if err != nil {
|
|
return nil, handlers.ApiErrorWithCodeAndMessage(codes.InvalidArgument, "end time is in an invalid format")
|
|
}
|
|
endTime = &et
|
|
}
|
|
if startTime != nil && endTime != nil && !endTime.After(*startTime) {
|
|
return nil, handlers.ApiErrorWithCodeAndMessage(codes.InvalidArgument, "start time is not before end time")
|
|
}
|
|
|
|
months, err := repo.MonthlyActiveUsers(
|
|
ctx,
|
|
billing.WithStartTime(startTime),
|
|
billing.WithEndTime(endTime),
|
|
)
|
|
if err != nil {
|
|
return nil, handlers.ApiErrorWithCodeAndMessage(codes.InvalidArgument, err.Error())
|
|
}
|
|
|
|
var activeUsers []*pb.ActiveUsers
|
|
for _, month := range months {
|
|
maud := &pb.ActiveUsers{
|
|
StartTime: timestamppb.New(month.StartTime),
|
|
EndTime: timestamppb.New(month.EndTime),
|
|
Count: month.ActiveUsersCount,
|
|
}
|
|
activeUsers = append(activeUsers, maud)
|
|
}
|
|
|
|
return &pbs.MonthlyActiveUsersResponse{Items: activeUsers}, nil
|
|
}
|
|
|
|
func (s Service) authResult(ctx context.Context, a action.Type) auth.VerifyResults {
|
|
opts := []auth.Option{
|
|
auth.WithType(resource.Billing),
|
|
auth.WithAction(a),
|
|
auth.WithScopeId(scope.Global.String()),
|
|
}
|
|
return auth.Verify(ctx, opts...)
|
|
}
|