Support output properties on aws.apigateway APIs

This commit is contained in:
Luke Hoban 2017-06-03 16:36:06 -07:00
parent 92a9925201
commit 26a2f95c48
14 changed files with 155 additions and 25 deletions

View file

@ -32,6 +32,11 @@ type Deployment struct {
// stageName is a name for the stage that API Gateway creates with this deployment. Use only alphanumeric
// characters.
StageName *string `lumi:"stageName,optional"`
// The identifier for the deployment resource.
ID string `lumi:"id,out"`
// The date and time that the deployment resource was created.
CreatedDate string `lumi:"createdDate,out"`
}
type StageDescription struct {

View file

@ -41,6 +41,22 @@ type RestAPI struct {
APIName *string `lumi:"apiName,optional"`
// Custom header parameters for the request.
Parameters *[]string `lumi:"parameters,optional"`
// The API's identifier. This identifier is unique across all of your APIs in Amazon API Gateway.
ID string `lumi:"id,out"`
// The timestamp when the API was created.
CreatedDate string `lumi:"createdDate,out"`
// A version identifier for the API.
Version string `lumi:"version,out"`
// TODO[pulumi/lumi#198] Exposing array-valued output properties
// currently triggers failures serializing resource state, so
// supressing these properties.
// The warning messages reported when failonwarnings is turned on during API import.
//Warnings []string `lumi:"warnings,out"`
// The list of binary media types supported by the RestApi. By default, the RestApi supports only UTF-8-encoded
// text payloads.
//BinaryMediaTypes []string `lumi:"binaryMediaTypes,out"`
}
// S3Location is a property of the RestAPI resource that specifies the Amazon Simple Storage Service (Amazon S3)

View file

@ -43,4 +43,9 @@ type Stage struct {
// variable value is the value. Variable names are limited to alphanumeric characters. Values must match the
// following regular expression: `[A-Za-z0-9-._~:/?#&=,]+`.
Variables *map[string]string `lumi:"variables,optional"`
// The timestamp when the stage was created.
CreatedDate string `lumi:"createdDate,out"`
// The timestamp when the stage last updated.
LastUpdatedDate string `lumi:"lastUpdatedDate,out"`
}

View file

@ -13,6 +13,8 @@ export class Deployment extends lumi.Resource implements DeploymentArgs {
public description?: string;
public stageDescription?: StageDescription;
public stageName?: string;
@lumi.out public id: string;
@lumi.out public createdDate: string;
constructor(name: string, args: DeploymentArgs) {
super();

View file

@ -14,6 +14,9 @@ export class RestAPI extends lumi.Resource implements RestAPIArgs {
public failOnWarnings?: boolean;
public apiName?: string;
public parameters?: string[];
@lumi.out public id: string;
@lumi.out public createdDate: string;
@lumi.out public version: string;
constructor(name: string, args?: RestAPIArgs) {
super();

View file

@ -19,6 +19,8 @@ export class Stage extends lumi.Resource implements StageArgs {
public description?: string;
public methodSettings?: MethodSetting[];
public variables?: {[key: string]: string};
@lumi.out public createdDate: string;
@lumi.out public lastUpdatedDate: string;
constructor(name: string, args: StageArgs) {
super();

View file

@ -4,7 +4,6 @@ package apigateway
import (
"crypto/sha1"
"errors"
"fmt"
"strings"
@ -47,11 +46,12 @@ func (p *deploymentProvider) Check(ctx context.Context, obj *apigateway.Deployme
// Create allocates a new instance of the provided resource and returns its unique ID afterwards. (The input ID
// must be blank.) If this call fails, the resource must not have been created (i.e., it is "transacational").
func (p *deploymentProvider) Create(ctx context.Context, obj *apigateway.Deployment) (resource.ID, error) {
// If an explicit name is given, use it. Otherwise, auto-generate a name in part based on the resource name.
var stageName string
if obj.StageName != nil {
stageName = *obj.StageName
} else {
stageName = resource.NewUniqueHex(obj.Name+"_", maxDeploymentName, sha1.Size)
stageName = resource.NewUniqueHex(*obj.Name+"_", maxDeploymentName, sha1.Size)
}
fmt.Printf("Creating APIGateway Deployment '%v'\n", obj.Name)
create := &awsapigateway.CreateDeploymentInput{
@ -63,13 +63,34 @@ func (p *deploymentProvider) Create(ctx context.Context, obj *apigateway.Deploym
if err != nil {
return "", err
}
id := resource.ID(string(obj.RestAPI) + ":" + *deployment.Id + ":" + stageName)
id := resource.ID(string(obj.RestAPI) + ":" + *deployment.Id)
return id, nil
}
// Get reads the instance state identified by ID, returning a populated resource object, or an error if not found.
func (p *deploymentProvider) Get(ctx context.Context, id resource.ID) (*apigateway.Deployment, error) {
return nil, errors.New("Not yet implemented - Get")
parts := strings.Split(id.String(), ":")
contract.Assertf(len(parts) == 2, "expected deployment ID to be of the form <restAPIID>:<deploymentId>")
restAPIID := parts[0]
deploymentID := parts[1]
resp, err := p.ctx.APIGateway().GetDeployment(&awsapigateway.GetDeploymentInput{
RestApiId: aws.String(restAPIID),
DeploymentId: aws.String(deploymentID),
})
if err != nil {
return nil, err
}
if resp == nil || resp.Id == nil {
return nil, nil
}
return &apigateway.Deployment{
RestAPI: resource.ID(restAPIID),
ID: aws.StringValue(resp.Id),
Description: resp.Description,
CreatedDate: resp.CreatedDate.String(),
}, nil
}
// InspectChange checks what impacts a hypothetical update will have on the resource's properties.
@ -88,7 +109,7 @@ func (p *deploymentProvider) Update(ctx context.Context, id resource.ID,
}
if len(ops) > 0 {
parts := strings.Split(id.String(), ":")
contract.Assertf(len(parts) == 3, "expected stage ID to be of the form <restAPIID>:<deploymentId>")
contract.Assertf(len(parts) == 2, "expected deployment ID to be of the form <restAPIID>:<deploymentId>")
deploymentID := parts[1]
update := &awsapigateway.UpdateDeploymentInput{
RestApiId: aws.String(string(new.RestAPI)),
@ -107,14 +128,27 @@ func (p *deploymentProvider) Update(ctx context.Context, id resource.ID,
func (p *deploymentProvider) Delete(ctx context.Context, id resource.ID) error {
fmt.Printf("Deleting APIGateway Deployment '%v'\n", id)
parts := strings.Split(id.String(), ":")
contract.Assertf(len(parts) == 3, "expected stage ID to be of the form <restAPIID>:<deploymentId>")
contract.Assertf(len(parts) == 2, "expected deployment ID to be of the form <restAPIID>:<deploymentId>")
restAPIID := parts[0]
deploymentID := parts[1]
stageName := parts[2]
_, err := p.ctx.APIGateway().DeleteStage(&awsapigateway.DeleteStageInput{
RestApiId: aws.String(restAPIID),
StageName: aws.String(stageName),
resp, err := p.ctx.APIGateway().GetStages(&awsapigateway.GetStagesInput{
RestApiId: aws.String(restAPIID),
DeploymentId: aws.String(deploymentID),
})
if err != nil || resp == nil {
return err
}
if len(resp.Item) == 1 {
// Assume that the single stage associated with this deployment
// is the stage that was automatically created along with the deployment.
_, err := p.ctx.APIGateway().DeleteStage(&awsapigateway.DeleteStageInput{
RestApiId: aws.String(restAPIID),
StageName: resp.Item[0].StageName,
})
if err != nil {
return err
}
}
_, err = p.ctx.APIGateway().DeleteDeployment(&awsapigateway.DeleteDeploymentInput{
RestApiId: aws.String(restAPIID),
DeploymentId: aws.String(deploymentID),

View file

@ -11,7 +11,7 @@ import (
type TestStruct struct {
Number float64 `json:"number"`
OptionalString *string `json:"string,omitempty"`
OptionalString *string `json:"optionalString,omitempty"`
OptionalNumber *float64 `json:"optionalNumber,omitempty"`
OptionalBool *bool `json:"optionalBool,omitempty"`
OptionalArray *[]TestStruct `json:"optionalArray,omitempty"`
@ -45,31 +45,31 @@ func Test(t *testing.T) {
expectedPatchOps := []*apigateway.PatchOperation{
&apigateway.PatchOperation{
Op: aws.String("add"),
Path: aws.String("/OptionalArray/1"),
Value: aws.String("{\"Number\": 1}"),
Path: aws.String("/optionalArray/1"),
Value: aws.String("{\"number\": 1}"),
},
&apigateway.PatchOperation{
Op: aws.String("replace"),
Path: aws.String("/OptionalArray/0/Number"),
Path: aws.String("/optionalArray/0/number"),
Value: aws.String("3"),
},
&apigateway.PatchOperation{
Op: aws.String("add"),
Path: aws.String("/OptionalArray/0/OptionalBool"),
Path: aws.String("/optionalArray/0/optionalBool"),
Value: aws.String("true"),
},
&apigateway.PatchOperation{
Op: aws.String("remove"),
Path: aws.String("/OptionalBool"),
Path: aws.String("/optionalBool"),
},
&apigateway.PatchOperation{
Op: aws.String("add"),
Path: aws.String("/OptionalNumber"),
Path: aws.String("/optionalNumber"),
Value: aws.String("3"),
},
&apigateway.PatchOperation{
Op: aws.String("replace"),
Path: aws.String("/OptionalString"),
Path: aws.String("/optionalString"),
Value: aws.String("goodbye"),
},
}

View file

@ -46,11 +46,12 @@ func (p *restAPIProvider) Check(ctx context.Context, obj *apigateway.RestAPI) ([
// Create allocates a new instance of the provided resource and returns its unique ID afterwards. (The input ID
// must be blank.) If this call fails, the resource must not have been created (i.e., it is "transacational").
func (p *restAPIProvider) Create(ctx context.Context, obj *apigateway.RestAPI) (resource.ID, error) {
// If an explicit name is given, use it. Otherwise, auto-generate a name in part based on the resource name.
var apiName string
if obj.APIName != nil {
apiName = *obj.APIName
} else {
apiName = resource.NewUniqueHex(obj.Name+"-", maxRestAPIName, sha1.Size)
apiName = resource.NewUniqueHex(*obj.Name+"-", maxRestAPIName, sha1.Size)
}
// First create the API Gateway
@ -87,7 +88,29 @@ func (p *restAPIProvider) Create(ctx context.Context, obj *apigateway.RestAPI) (
// Get reads the instance state identified by ID, returning a populated resource object, or an error if not found.
func (p *restAPIProvider) Get(ctx context.Context, id resource.ID) (*apigateway.RestAPI, error) {
return nil, errors.New("Not yet implemented - Get")
resp, err := p.ctx.APIGateway().GetRestApi(&awsapigateway.GetRestApiInput{
RestApiId: aws.String(string(id)),
})
if err != nil {
return nil, err
}
if resp == nil || resp.Id == nil {
return nil, nil
}
return &apigateway.RestAPI{
ID: aws.StringValue(resp.Id),
APIName: resp.Name,
Description: resp.Description,
CreatedDate: resp.CreatedDate.String(),
Version: aws.StringValue(resp.Version),
// TODO[pulumi/lumi#198] Exposing array-valued output properties
// currently triggers failures serializing resource state, so
// supressing these properties.
// Warnings: aws.StringValueSlice(resp.Warnings),
// BinaryMediaTypes: aws.StringValueSlice(resp.BinaryMediaTypes),
}, nil
}
// InspectChange checks what impacts a hypothetical update will have on the resource's properties.

View file

@ -3,7 +3,6 @@
package apigateway
import (
"errors"
"fmt"
"github.com/aws/aws-sdk-go/aws"
@ -52,7 +51,7 @@ func (p *stageProvider) Create(ctx context.Context, obj *apigateway.Stage) (reso
}
fmt.Printf("Creating APIGateway Stage '%v' with stage name '%v'\n", obj.Name, obj.StageName)
parts := strings.Split(string(obj.Deployment), ":")
contract.Assertf(len(parts) == 3, "expected deployment ID to be of the form <restAPIID>:<deploymentid>:<stagename>")
contract.Assertf(len(parts) == 2, "expected deployment ID to be of the form <restAPIID>:<deploymentid>")
deploymentID := parts[1]
create := &awsapigateway.CreateStageInput{
StageName: aws.String(obj.StageName),
@ -75,7 +74,35 @@ func (p *stageProvider) Create(ctx context.Context, obj *apigateway.Stage) (reso
// Get reads the instance state identified by ID, returning a populated resource object, or an error if not found.
func (p *stageProvider) Get(ctx context.Context, id resource.ID) (*apigateway.Stage, error) {
return nil, errors.New("Not yet implemented - Get")
parts := strings.Split(id.String(), ":")
contract.Assertf(len(parts) == 2, "expected stage ID to be of the form <restAPIID>:<stagename>")
restAPIID := parts[0]
stageName := parts[1]
resp, err := p.ctx.APIGateway().GetStage(&awsapigateway.GetStageInput{
RestApiId: aws.String(restAPIID),
StageName: aws.String(stageName),
})
if err != nil {
return nil, err
}
if resp == nil || resp.DeploymentId == nil {
return nil, nil
}
deploymentID := resource.ID(restAPIID + ":" + aws.StringValue(resp.DeploymentId))
variables := aws.StringValueMap(resp.Variables)
return &apigateway.Stage{
RestAPI: resource.ID(restAPIID),
Deployment: deploymentID,
CacheClusterEnabled: resp.CacheClusterEnabled,
CacheClusterSize: resp.CacheClusterSize,
StageName: aws.StringValue(resp.StageName),
Variables: &variables,
Description: resp.Description,
CreatedDate: resp.CreatedDate.String(),
LastUpdatedDate: resp.LastUpdatedDate.String(),
}, nil
}
// InspectChange checks what impacts a hypothetical update will have on the resource's properties.
@ -95,7 +122,7 @@ func (p *stageProvider) Update(ctx context.Context, id resource.ID,
if diff.Updated(apigateway.Stage_Deployment) {
parts := strings.Split(string(new.Deployment), ":")
contract.Assertf(len(parts) == 3, "expected deployment ID to be of the form <restAPIID>:<deploymentid>:<stagename>")
contract.Assertf(len(parts) == 2, "expected deployment ID to be of the form <restAPIID>:<deploymentid>")
deploymentID := parts[1]
ops = append(ops, &awsapigateway.PatchOperation{
Op: aws.String("replace"),

View file

@ -179,6 +179,8 @@ type Deployment struct {
Description *string `json:"description,omitempty"`
StageDescription *StageDescription `json:"stageDescription,omitempty"`
StageName *string `json:"stageName,omitempty"`
ID string `json:"id,omitempty"`
CreatedDate string `json:"createdDate,omitempty"`
}
// Deployment's properties have constants to make dealing with diffs and property bags easier.
@ -188,6 +190,8 @@ const (
Deployment_Description = "description"
Deployment_StageDescription = "stageDescription"
Deployment_StageName = "stageName"
Deployment_ID = "id"
Deployment_CreatedDate = "createdDate"
)
/* Marshalable StageDescription structure(s) */

View file

@ -182,6 +182,9 @@ type RestAPI struct {
FailOnWarnings *bool `json:"failOnWarnings,omitempty"`
APIName *string `json:"apiName,omitempty"`
Parameters *[]string `json:"parameters,omitempty"`
ID string `json:"id,omitempty"`
CreatedDate string `json:"createdDate,omitempty"`
Version string `json:"version,omitempty"`
}
// RestAPI's properties have constants to make dealing with diffs and property bags easier.
@ -194,6 +197,9 @@ const (
RestAPI_FailOnWarnings = "failOnWarnings"
RestAPI_APIName = "apiName"
RestAPI_Parameters = "parameters"
RestAPI_ID = "id"
RestAPI_CreatedDate = "createdDate"
RestAPI_Version = "version"
)
/* Marshalable S3Location structure(s) */

View file

@ -190,6 +190,8 @@ type Stage struct {
Description *string `json:"description,omitempty"`
MethodSettings *[]MethodSetting `json:"methodSettings,omitempty"`
Variables *map[string]string `json:"variables,omitempty"`
CreatedDate string `json:"createdDate,omitempty"`
LastUpdatedDate string `json:"lastUpdatedDate,omitempty"`
}
// Stage's properties have constants to make dealing with diffs and property bags easier.
@ -204,6 +206,8 @@ const (
Stage_Description = "description"
Stage_MethodSettings = "methodSettings"
Stage_Variables = "variables"
Stage_CreatedDate = "createdDate"
Stage_LastUpdatedDate = "lastUpdatedDate"
)

View file

@ -21,7 +21,6 @@ import (
"fmt"
"sort"
"strconv"
"strings"
"github.com/pulumi/lumi/pkg/compiler/ast"