Skip to content

Commit

Permalink
Add get lease by ID
Browse files Browse the repository at this point in the history
Signed-off-by: Kevin DeJong <kddejong@amazon.com>
  • Loading branch information
kddejong committed Oct 9, 2019
1 parent acf3aea commit 095e070
Show file tree
Hide file tree
Showing 11 changed files with 363 additions and 9 deletions.
39 changes: 39 additions & 0 deletions cmd/lambda/leases/get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package main

import (
"context"
"fmt"
"log"
"net/http"
"path"

"github.com/Optum/Redbox/pkg/db"

"github.com/Optum/Redbox/pkg/api/response"
"github.com/aws/aws-lambda-go/events"
)

type getController struct {
Dao db.DBer
}

// Call - function to return a specific AWS Lease record to the request
func (controller getController) Call(ctx context.Context, req *events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
// Fetch the account.
leaseID := path.Base(req.Path)
lease, err := controller.Dao.GetLeaseByID(leaseID)
if err != nil {
log.Printf("Error Getting Lease for Id: %s", err)
return response.CreateAPIErrorResponse(http.StatusInternalServerError,
response.CreateErrorResponse("ServerError",
fmt.Sprintf("Failed Get on Lease %s",
leaseID))), nil
}
if lease == nil {
log.Printf("Error Getting Lease for Id: %s", err)
return response.NotFoundError(), nil
}

leaseResponse := response.LeaseResponse(*lease)
return response.CreateJSONResponse(http.StatusOK, leaseResponse), nil
}
70 changes: 70 additions & 0 deletions cmd/lambda/leases/get_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package main

import (
"context"
"encoding/json"
"errors"
"net/http"
"testing"

"github.com/Optum/Redbox/pkg/api/response"
"github.com/Optum/Redbox/pkg/db"
"github.com/Optum/Redbox/pkg/db/mocks"
"github.com/aws/aws-lambda-go/events"
"github.com/stretchr/testify/require"
)

func TestGetLeaseByID(t *testing.T) {

t.Run("When the invoking Call and there are no errors", func(t *testing.T) {
expectdLease := &db.RedboxLease{
ID: "unique-id",
AccountID: "123456789",
PrincipalID: "test",
LeaseStatus: db.Active,
LastModifiedOn: 1561149393,
}
expectedLeaseResponse := &response.LeaseResponse{
ID: "unique-id",
AccountID: "123456789",
PrincipalID: "test",
LeaseStatus: db.Active,
LastModifiedOn: 1561149393,
}
mockDb := mocks.DBer{}
mockDb.On("GetLeaseByID", "unique-id").Return(expectdLease, nil)
mockRequest := events.APIGatewayProxyRequest{HTTPMethod: http.MethodGet, Path: "/leases/unique-id"}

controller := getController{
Dao: &mockDb,
}

actualResponse, err := controller.Call(context.TODO(), &mockRequest)
require.Nil(t, err)

parsedResponse := &response.LeaseResponse{}
err = json.Unmarshal([]byte(actualResponse.Body), parsedResponse)
require.Nil(t, err)

require.Equal(t, expectedLeaseResponse, parsedResponse, "Returns a single lease.")
require.Equal(t, actualResponse.StatusCode, 200, "Returns a 200.")
})

t.Run("When the query fails", func(t *testing.T) {
expectedError := errors.New("Error")
mockDb := mocks.DBer{}
mockDb.On("GetLeaseByID", "unique-id").Return(nil, expectedError)
mockRequest := events.APIGatewayProxyRequest{HTTPMethod: http.MethodGet, Path: "/accounts/unique-id"}

controller := getController{
Dao: &mockDb,
}

actualResponse, err := controller.Call(context.TODO(), &mockRequest)
require.Nil(t, err)

require.Equal(t, actualResponse.StatusCode, 500, "Returns a 500.")
require.Equal(t, actualResponse.Body, "{\"error\":{\"code\":\"ServerError\",\"message\":\"Failed Get on Lease unique-id\"}}", "Returns an error response.")
})

}
6 changes: 6 additions & 0 deletions cmd/lambda/leases/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"strings"
"time"

"log"
Expand Down Expand Up @@ -351,6 +352,11 @@ func router(ctx context.Context, req *events.APIGatewayProxyRequest) (
switch req.HTTPMethod {
case "GET":
// Placeholder until a proper GET gets implemented
if strings.Contains(req.Path, "/leases/") {
getController := getController{Dao: dbSvc}
return getController.Call(ctx, req)
}

return createAPIResponse(http.StatusOK, "{\"message\":\"pong\"}"), nil
case "POST":
prov := &provision.AccountProvision{
Expand Down
8 changes: 4 additions & 4 deletions modules/gateway.tf
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ data "template_file" "aws_redbox_api_swagger" {
template = file("${path.module}/swaggerRedbox.yaml")

vars = {
router_lambda_arn = aws_lambda_function.leases.invoke_arn
accounts_lambda = aws_lambda_function.accounts_lambda.invoke_arn
usages_lambda = aws_lambda_function.usage.invoke_arn
namespace = "AWS_Redbox-${var.namespace}"
leases_lambda = aws_lambda_function.leases.invoke_arn
accounts_lambda = aws_lambda_function.accounts_lambda.invoke_arn
usages_lambda = aws_lambda_function.usage.invoke_arn
namespace = "AWS_Redbox-${var.namespace}"
}
}

Expand Down
36 changes: 31 additions & 5 deletions modules/swaggerRedbox.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ paths:
403:
description: "Failed to authenticate request"
x-amazon-apigateway-integration:
uri: ${router_lambda_arn}
uri: ${leases_lambda}
httpMethod: "POST"
type: "aws_proxy"
passthroughBehavior: "when_no_match"
Expand Down Expand Up @@ -175,7 +175,7 @@ paths:
403:
description: "Failed to authenticate request"
x-amazon-apigateway-integration:
uri: ${router_lambda_arn}
uri: ${leases_lambda}
httpMethod: "POST"
type: "aws_proxy"
passthroughBehavior: "when_no_match"
Expand All @@ -202,7 +202,30 @@ paths:
403:
description: "Failed to authenticate request"
x-amazon-apigateway-integration:
uri: ${router_lambda_arn}
uri: ${leases_lambda}
httpMethod: "POST"
type: "aws_proxy"
passthroughBehavior: "when_no_match"
security:
- sigv4: []
"/leases/{id}":
get:
summary: Get a lease by Id
produces:
- application/json
parameters:
- in: path
name: id
type: string
required: true
description: Id for lease
responses:
200:
$ref: "#/definitions/lease"
403:
description: "Failed to retrieve lease"
x-amazon-apigateway-integration:
uri: ${leases_lambda}
httpMethod: "POST"
type: "aws_proxy"
passthroughBehavior: "when_no_match"
Expand Down Expand Up @@ -244,9 +267,12 @@ securityDefinitions:
x-amazon-apigateway-authtype: "awsSigv4"
definitions:
lease:
description: "Lease Id"
description: "Lease Details"
type: object
properties:
id:
type: string
description: Lease ID
principalId:
type: string
description: principalId of the lease to get
Expand Down Expand Up @@ -354,4 +380,4 @@ definitions:
description: usage cost currency
timeToLive:
type: number
description: ttl attribute as Epoch Timestamp
description: ttl attribute as Epoch Timestamp
12 changes: 12 additions & 0 deletions modules/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,15 @@ variable "redbox_principal_policy" {
description = "Location of file with the policy used for the RedBox Principal Account"
default = ""
}

variable "update_lease_status_schedule_expression" {
type = string
description = "Update lease status schedule"
default = "rate(5 minutes)"
}

variable "update_lease_status_enabled" {
type = bool
description = "Update lease status enabled"
default = true
}
5 changes: 5 additions & 0 deletions pkg/api/response/lease.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,15 @@ type LeaseResponse struct {
PrincipalID string `json:"principalId"`
ID string `json:"id"`
LeaseStatus db.LeaseStatus `json:"leaseStatus"`
LeaseStatusReason string `json:"leaseStatusReturn"`
CreatedOn int64 `json:"createdOn"`
LastModifiedOn int64 `json:"lastModifiedOn"`
BudgetAmount float64 `json:"budgetAmount"`
BudgetCurrency string `json:"budgetCurrency"`
BudgetNotificationEmails []string `json:"budgetNotificationEmails"`
LeaseStatusModifiedOn int64 `json:"leaseStatusModifiedOn"`
RequestedLeaseStart int64 `json:"requestedLeaseStart"`
ActualLeaseStart int64 `json:"actualLeaseStart"`
RequestedLeaseEnd int64 `json:"requestedLeaseEnd"`
ActualLeaseEnd int64 `json:"actualLeaseEnd"`
}
30 changes: 30 additions & 0 deletions pkg/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type DBer interface {
GetReadyAccount() (*RedboxAccount, error)
GetAccountsForReset() ([]*RedboxAccount, error)
GetAccounts() ([]*RedboxAccount, error)
GetLeaseByID(leaseID string) (*RedboxLease, error)
FindAccountsByStatus(status AccountStatus) ([]*RedboxAccount, error)
FindAccountsByPrincipalID(principalID string) ([]*RedboxAccount, error)
PutAccount(account RedboxAccount) error
Expand Down Expand Up @@ -218,6 +219,35 @@ func (db *DB) FindAccountsByPrincipalID(principalID string) ([]*RedboxAccount, e
return accounts, nil
}

// GetLeaseByID gets a lease by ID
func (db *DB) GetLeaseByID(leaseID string) (*RedboxLease, error) {

input := &dynamodb.QueryInput{
ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
":a1": {
S: aws.String(leaseID),
},
},
KeyConditionExpression: aws.String("Id = :a1"),
TableName: aws.String(db.LeaseTableName),
IndexName: aws.String("LeaseId"),
}

resp, err := db.Client.Query(input)
if err != nil {
return nil, err
}

if len(resp.Items) < 1 {
return nil, fmt.Errorf("No Lease found with id: %s", leaseID)
}
if len(resp.Items) > 1 {
return nil, fmt.Errorf("Found more than one Lease with id: %s", leaseID)
}

return unmarshalLease(resp.Items[0])
}

// GetLease retrieves a Lease for the
// given accountID and principalID
func (db *DB) GetLease(accountID string, principalID string) (*RedboxLease, error) {
Expand Down
23 changes: 23 additions & 0 deletions pkg/db/mocks/DBer.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 095e070

Please sign in to comment.