diff --git a/CHANGELOG.md b/CHANGELOG.md index 535c8be..bb1a2d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## v0.21.0 (Unreleased) + +Support [`handlers.handlerSchema`](https://github.com/aws-cloudformation/cloudformation-resource-schema#handlers). + ## v0.20.0 (December 19, 2022) Support [`arrayType`](https://github.com/aws-cloudformation/cloudformation-resource-schema#arraytype). diff --git a/handler.go b/handler.go index aa8b796..bba792b 100644 --- a/handler.go +++ b/handler.go @@ -12,6 +12,7 @@ const ( ) type Handler struct { - Permissions []string `json:"permissions,omitempty"` - TimeoutInMinutes int `json:"timeoutInMinutes,omitempty"` + HandlerSchema *HandlerSchema `json:"handlerSchema,omitempty"` + Permissions []string `json:"permissions,omitempty"` + TimeoutInMinutes int `json:"timeoutInMinutes,omitempty"` } diff --git a/handler_schema.go b/handler_schema.go new file mode 100644 index 0000000..3b14431 --- /dev/null +++ b/handler_schema.go @@ -0,0 +1,12 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package cfschema + +type HandlerSchema struct { + AllOf []*PropertySubschema `json:"allOf,omitempty"` + AnyOf []*PropertySubschema `json:"anyOf,omitempty"` + OneOf []*PropertySubschema `json:"oneOf,omitempty"` + Properties map[string]*Property `json:"properties,omitempty"` + Required []string `json:"required,omitempty"` +} diff --git a/handler_schema_test.go b/handler_schema_test.go new file mode 100644 index 0000000..5f3252b --- /dev/null +++ b/handler_schema_test.go @@ -0,0 +1,75 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package cfschema_test + +import ( + "path/filepath" + "testing" + + cfschema "github.com/hashicorp/aws-cloudformation-resource-schema-sdk-go" +) + +func TestHandlerSchema(t *testing.T) { + testCases := []struct { + TestDescription string + MetaSchemaPath string + ResourceSchemaPath string + ExpectError bool + ExpectedHandlerSchema int + }{ + { + TestDescription: "no handlerSchema", + MetaSchemaPath: "provider.definition.schema.v1.json", + ResourceSchemaPath: "AWS_CloudWatch_MetricStream.json", + }, + { + TestDescription: "list handlerSchema", + MetaSchemaPath: "provider.definition.schema.v1.json", + ResourceSchemaPath: "AWS_NetworkManager_TransitGatewayRegistration.json", + ExpectedHandlerSchema: 1, + }, + } + + for _, testCase := range testCases { + testCase := testCase + + t.Run(testCase.TestDescription, func(t *testing.T) { + metaSchema, err := cfschema.NewMetaJsonSchemaPath(filepath.Join("testdata", testCase.MetaSchemaPath)) + + if err != nil { + t.Fatalf("unexpected NewMetaJsonSchemaPath() error: %s", err) + } + + resourceSchema, err := cfschema.NewResourceJsonSchemaPath(filepath.Join("testdata", testCase.ResourceSchemaPath)) + + if err != nil { + t.Fatalf("unexpected NewResourceJsonSchemaPath() error: %s", err) + } + + err = metaSchema.ValidateResourceJsonSchema(resourceSchema) + + if err != nil { + t.Fatalf("unexpected ValidateResourceJsonSchema() error: %s", err) + } + + resource, err := resourceSchema.Resource() + + if err != nil { + t.Fatalf("unexpected Resource() error: %s", err) + } + + got := 0 + + for _, handler := range resource.Handlers { + if handler.HandlerSchema != nil { + got++ + } + } + + if actual, expected := got, testCase.ExpectedHandlerSchema; actual != expected { + t.Errorf("expected %d handlerSchema elements, got: %d", expected, actual) + } + }) + } +} diff --git a/resource.go b/resource.go index 9859967..1b8bb70 100644 --- a/resource.go +++ b/resource.go @@ -18,6 +18,8 @@ type Resource struct { DeprecatedProperties PropertyJsonPointers `json:"deprecatedProperties,omitempty"` Description *string `json:"description,omitempty"` Handlers map[string]*Handler `json:"handlers,omitempty"` + NonPublicDefinitions PropertyJsonPointers `json:"nonPublicDefinitions,omitempty"` + NonPublicProperties PropertyJsonPointers `json:"nonPublicProperties,omitempty"` OneOf []*PropertySubschema `json:"oneOf,omitempty"` PrimaryIdentifier PropertyJsonPointers `json:"primaryIdentifier,omitempty"` Properties map[string]*Property `json:"properties,omitempty"` diff --git a/tagging.go b/tagging.go index ef981c4..3a7bc95 100644 --- a/tagging.go +++ b/tagging.go @@ -9,4 +9,5 @@ type Tagging struct { TagUpdatable *bool `json:"tagUpdatable,omitempty"` CloudFormationSystemTags *bool `json:"cloudFormationSystemTags,omitempty"` TagProperty *PropertyJsonPointer `json:"tagProperty,omitempty"` + Permissions []string `json:"permissions,omitempty"` } diff --git a/testdata/AWS_NetworkManager_TransitGatewayRegistration.json b/testdata/AWS_NetworkManager_TransitGatewayRegistration.json new file mode 100644 index 0000000..1e4bab7 --- /dev/null +++ b/testdata/AWS_NetworkManager_TransitGatewayRegistration.json @@ -0,0 +1,62 @@ +{ + "typeName": "AWS::NetworkManager::TransitGatewayRegistration", + "description": "The AWS::NetworkManager::TransitGatewayRegistration type registers a transit gateway in your global network. The transit gateway can be in any AWS Region, but it must be owned by the same AWS account that owns the global network. You cannot register a transit gateway in more than one global network.", + "sourceUrl": "https://github.com/aws-cloudformation/aws-cloudformation-resource-providers-networkmanager.git", + "properties": { + "GlobalNetworkId": { + "description": "The ID of the global network.", + "type": "string" + }, + "TransitGatewayArn": { + "description": "The Amazon Resource Name (ARN) of the transit gateway.", + "type": "string" + } + }, + "taggable": false, + "additionalProperties": false, + "required": [ + "GlobalNetworkId", + "TransitGatewayArn" + ], + "createOnlyProperties": [ + "/properties/GlobalNetworkId", + "/properties/TransitGatewayArn" + ], + "primaryIdentifier": [ + "/properties/GlobalNetworkId", + "/properties/TransitGatewayArn" + ], + "handlers": { + "create": { + "permissions": [ + "networkmanager:RegisterTransitGateway" + ], + "timeoutInMinutes": 30 + }, + "read": { + "permissions": [ + "networkmanager:GetTransitGatewayRegistrations" + ] + }, + "list": { + "handlerSchema": { + "properties": { + "GlobalNetworkId": { + "$ref": "resource-schema.json#/properties/GlobalNetworkId" + } + }, + "required": ["GlobalNetworkId"] + }, + "permissions": [ + "networkmanager:GetTransitGatewayRegistrations" + ] + }, + "delete": { + "permissions": [ + "networkmanager:DeregisterTransitGateway" + ], + "timeoutInMinutes": 30 + } + } +} + \ No newline at end of file diff --git a/testdata/provider.definition.schema.v1.json b/testdata/provider.definition.schema.v1.json index 4b3fa4d..1d32814 100644 --- a/testdata/provider.definition.schema.v1.json +++ b/testdata/provider.definition.schema.v1.json @@ -4,6 +4,57 @@ "title": "CloudFormation Resource Provider Definition MetaSchema", "description": "This schema validates a CloudFormation resource provider definition.", "definitions": { + "handlerSchema": { + "type": "object", + "properties": { + "properties": { + "$ref": "file://./base.definition.schema.v1.json#/properties/properties" + }, + "required": { + "$ref": "file://./base.definition.schema.v1.json#/properties/required" + }, + "allOf": { + "$ref": "file://./base.definition.schema.v1.json#/definitions/schemaArray" + }, + "anyOf": { + "$ref": "file://./base.definition.schema.v1.json#/definitions/schemaArray" + }, + "oneOf": { + "$ref": "file://./base.definition.schema.v1.json#/definitions/schemaArray" + } + }, + "required": [ + "properties" + ], + "additionalProperties": false + }, + "handlerDefinitionWithSchemaOverride": { + "description": "Defines any execution operations which can be performed on this resource provider", + "type": "object", + "properties": { + "handlerSchema": { + "$ref": "#/definitions/handlerSchema" + }, + "permissions": { + "type": "array", + "items": { + "type": "string" + }, + "additionalItems": false + }, + "timeoutInMinutes": { + "description": "Defines the timeout for the entire operation to be interpreted by the invoker of the handler. The default is 120 (2 hours).", + "type": "integer", + "minimum": 2, + "maximum": 2160, + "default": 120 + } + }, + "additionalProperties": false, + "required": [ + "permissions" + ] + }, "handlerDefinition": { "description": "Defines any execution operations which can be performed on this resource provider", "type": "object", @@ -145,6 +196,13 @@ "description": "A reference to the Tags property in the schema.", "$ref": "http://json-schema.org/draft-07/schema#/properties/$ref", "default": "/properties/Tags" + }, + "permissions": { + "type": "array", + "items": { + "type": "string" + }, + "additionalItems": false } }, "required": [ @@ -183,7 +241,7 @@ "$ref": "#/definitions/handlerDefinition" }, "list": { - "$ref": "#/definitions/handlerDefinition" + "$ref": "#/definitions/handlerDefinitionWithSchemaOverride" } }, "additionalProperties": false @@ -204,6 +262,14 @@ "description": "A list of JSON pointers for properties that can only be updated under certain conditions. For example, you can upgrade the engine version of an RDS DBInstance but you cannot downgrade it. When updating this property for a resource in a CloudFormation stack, the resource will be replaced if it cannot be updated.", "$ref": "file://./base.definition.schema.v1.json#/definitions/jsonPointerArray" }, + "nonPublicProperties": { + "description": "A list of JSON pointers for properties that are hidden. These properties will still be used but will not be visible", + "$ref": "file://./base.definition.schema.v1.json#/definitions/jsonPointerArray" + }, + "nonPublicDefinitions": { + "description": "A list of JSON pointers for definitions that are hidden. These definitions will still be used but will not be visible", + "$ref": "file://./base.definition.schema.v1.json#/definitions/jsonPointerArray" + }, "createOnlyProperties": { "description": "A list of JSON pointers to properties that are only able to be specified by the customer when creating a resource. Conversely, any property *not* in this list can be applied to an Update request.", "$ref": "file://./base.definition.schema.v1.json#/definitions/jsonPointerArray"