Skip to content

Commit

Permalink
Add schema breaking change detection hook for AWS::AppSync::GraphQLSc…
Browse files Browse the repository at this point in the history
…hema (#243)

* Add schema breaking change detection hook for AWS::AppSync::GraphQLSchema

* Add examples in README.md
  • Loading branch information
ndejaco2 authored Oct 10, 2023
1 parent 40f9a6f commit 14d3b96
Show file tree
Hide file tree
Showing 40 changed files with 1,925 additions and 3 deletions.
23 changes: 23 additions & 0 deletions hooks/AppSync_BreakingChangeDetection/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# macOS
.DS_Store
._*

# Maven outputs
.classpath

# IntelliJ
*.iml
.idea
out.java
out/
.settings
.project

# auto-generated files
target/

# our logs
rpdk.log*

# contains credentials
sam-tests/
29 changes: 29 additions & 0 deletions hooks/AppSync_BreakingChangeDetection/.rpdk-config
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"artifact_type": "HOOK",
"typeName": "AwsCommunity::AppSync::BreakingChangeDetection",
"language": "java",
"runtime": "java11",
"entrypoint": "com.awscommunity.appsync.breakingchangedetection.HookHandlerWrapper::handleRequest",
"testEntrypoint": "com.awscommunity.appsync.breakingchangedetection.HookHandlerWrapper::testEntrypoint",
"settings": {
"version": false,
"subparser_name": null,
"verbose": 0,
"force": false,
"type_name": null,
"artifact_type": null,
"endpoint_url": null,
"region": null,
"target_schemas": [],
"profile": null,
"namespace": [
"com",
"awscommunity",
"appsync",
"breakingchangedetection"
],
"codegen_template_path": "default",
"protocolVersion": "2.0.0"
},
"executableEntrypoint": "com.awscommunity.appsync.breakingchangedetection.HookHandlerWrapperExecutable"
}
177 changes: 177 additions & 0 deletions hooks/AppSync_BreakingChangeDetection/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# AwsCommunity::AppSync::BreakingChangeDetection

Validates that an AWS AppSync GraphQL schema update does not introduce a change that would break existing clients of the AWS AppSync GraphQL API. There are three categories of changes in a GraphQL schema:

- **Breaking**: These are changes that are not backwards incompatible for existing API clients, such as changing the return type of an existing field or removing a field from the schema.
- **Dangerous**: These are changes that may be dangerous for existing API clients but are not necessarily breaking, such as adding a new value in an Enum or changing the default value on an argument.
- **Non-breaking**: These are changes that are backwards compatible for existing clients, such as adding a new field or type to the schema.

By default, the hook will fail when an AppSync schema update introduces a change that is in the "Breaking" category.

## Configuration

```bash
# Create a basic type configuration json
cat <<EOF > typeConfiguration.json
{
"CloudFormationConfiguration": {
"HookConfiguration": {
"TargetStacks": "ALL",
"FailureMode": "FAIL",
"Properties": {}
}
}
}
EOF

# enable the hook
aws cloudformation set-type-configuration \
--configuration file://typeConfiguration.json \
--type HOOK \
--type-name AwsCommunity::AppSync::BreakingChangeDetection
```

## Examples

- Original Template:

```
Resources:
BasicGraphQLApi:
Type: "AWS::AppSync::GraphQLApi"
Properties:
Name: BasicApi
AuthenticationType: "AWS_IAM"
BasicGraphQLSchema:
Type: "AWS::AppSync::GraphQLSchema"
Properties:
ApiId: !GetAtt BasicGraphQLApi.ApiId
Definition: |
type Test {
version: String!
type: TestType
}
type Query {
getTests: [Test]!
}
type Mutation {
addTest(version: String!): Test
}
enum TestType {
SIMPLE,
COMPLEX
}
```
### Non-Breaking Change Example

- Adding the new field Test.name is not a breaking change.

```
Resources:
BasicGraphQLApi:
Type: "AWS::AppSync::GraphQLApi"
Properties:
Name: BasicApi
AuthenticationType: "AWS_IAM"
BasicGraphQLSchema:
Type: "AWS::AppSync::GraphQLSchema"
Properties:
ApiId: !GetAtt BasicGraphQLApi.ApiId
Definition: |
type Test {
version: String!
type: TestType
name: String
}
type Query {
getTests: [Test]!
getTest(version: String): Test
}
type Mutation {
addTest(version: String!): Test
}
enum TestType {
SIMPLE,
COMPLEX
}
```

### Breaking Change Example

- Removing the Query.getTest field is a breaking change because clients will no longer be able to query it.
```
Resources:
BasicGraphQLApi:
Type: "AWS::AppSync::GraphQLApi"
Properties:
Name: BasicApi
AuthenticationType: "AWS_IAM"
BasicGraphQLSchema:
Type: "AWS::AppSync::GraphQLSchema"
Properties:
ApiId: !GetAtt BasicGraphQLApi.ApiId
Definition: |
type Test {
version: String!
type: TestType
}
type Query {
getTests: [Test]!
}
type Mutation {
addTest(version: String!): Test
}
enum TestType {
SIMPLE,
COMPLEX
}
```

### Dangerous Change Example

- Adding an Enum Value to an existing enum type is considered a "Dangerous" change as it may require changes to handling on the client. It will not break existing queries.
```
Resources:
BasicGraphQLApi:
Type: "AWS::AppSync::GraphQLApi"
Properties:
Name: BasicApi
AuthenticationType: "AWS_IAM"
BasicGraphQLSchema:
Type: "AWS::AppSync::GraphQLSchema"
Properties:
ApiId: !GetAtt BasicGraphQLApi.ApiId
Definition: |
type Test {
version: String!
type: TestType
name: String
}
type Query {
getTests: [Test]!
getTest(version: String): Test
}
type Mutation {
addTest(version: String!): Test
}
enum TestType {
SIMPLE,
COMPLEX
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"typeName": "AwsCommunity::AppSync::BreakingChangeDetection",
"description": "PreUpdate hook to perform breaking change detection on an AWS AppSync schema change.",
"sourceUrl": "https://github.com/aws-cloudformation/community-registry-extensions/tree/main/hooks/AppSync_BreakingChangeDetection",
"documentationUrl": "https://github.com/aws-cloudformation/community-registry-extensions/tree/main/hooks/AppSync_BreakingChangeDetection/README.md",
"typeConfiguration": {
"properties": {
"ConsiderDangerousChangesBreaking": {
"description": "Whether to consider changes that are in the DANGEROUS category as BREAKING. Changes in the DANGEROUS category wont break existing requests but could affect the runtime behavior of clients. Adding an enum value is an example of a dangerous change.",
"type": "boolean",
"default": false
}
},
"additionalProperties": false
},
"required": [],
"handlers": {
"preUpdate": {
"targetNames": [
"AWS::AppSync::GraphQLSchema"
],
"permissions": [
"s3:GetObject"
]
}
},
"additionalProperties": false
}
45 changes: 45 additions & 0 deletions hooks/AppSync_BreakingChangeDetection/docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# AwsCommunity::AppSync::BreakingChangeDetection

## Activation

To activate a hook in your account, use the following JSON as the `Configuration` request parameter for [`SetTypeConfiguration`](https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_SetTypeConfiguration.html) API request.

### Configuration

<pre>
{
"CloudFormationConfiguration": {
"<a href="https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/hooks-structure.html#hooks-hook-configuration" title="HookConfiguration">HookConfiguration</a>": {
"<a href="https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/hooks-structure.html#hooks-targetstacks" title="TargetStacks">TargetStacks</a>": <a href="#footnote-1">"ALL" | "NONE"</a>,
"<a href="https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/hooks-structure.html#hooks-failuremode" title="FailureMode">FailureMode</a>": <a href="#footnote-1">"FAIL" | "WARN"</a> ,
"<a href="https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/hooks-structure.html#hooks-properties" title="Properties">Properties</a>" : {
"<a href="#considerdangerouschangesbreaking" title="ConsiderDangerousChangesBreaking">ConsiderDangerousChangesBreaking</a>" : <i>Boolean</i>
}
}
}
}
</pre>

## Properties

#### ConsiderDangerousChangesBreaking

Whether to consider changes that are in the DANGEROUS category as BREAKING. Changes in the DANGEROUS category wont break existing requests but could affect the runtime behavior of clients. Adding an enum value is an example of a dangerous change.

_Required_: No

_Type_: Boolean


---

## Targets

* `AWS::AppSync::GraphQLSchema`

---

<p id="footnote-1"><i> Please note that the enum values for <a href="https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/hooks-structure.html#hooks-targetstacks" title="TargetStacks">
TargetStacks</a> and <a href="https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/hooks-structure.html#hooks-failuremode" title="FailureMode">FailureMode</a>
might go out of date, please refer to their official documentation page for up-to-date values. </i></p>

40 changes: 40 additions & 0 deletions hooks/AppSync_BreakingChangeDetection/hook-role.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
AWSTemplateFormatVersion: "2010-09-09"
Description: >
This CloudFormation template creates a role assumed by CloudFormation
during Hook operations on behalf of the customer.
Resources:
ExecutionRole:
Type: AWS::IAM::Role
Properties:
MaxSessionDuration: 8400
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- hooks.cloudformation.amazonaws.com
- resources.cloudformation.amazonaws.com
Action: sts:AssumeRole
Condition:
StringEquals:
aws:SourceAccount:
Ref: AWS::AccountId
StringLike:
aws:SourceArn:
Fn::Sub: arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:type/hook/AwsCommunity-AppSync-BreakingChangeDetection/*
Path: "/"
Policies:
- PolicyName: HookTypePolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- "s3:GetObject"
Resource: "*"
Outputs:
ExecutionRoleArn:
Value:
Fn::GetAtt: ExecutionRole.Arn
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"AWS::AppSync::GraphQLSchema": {
"resourceProperties": {
"ApiId": "123",
"Definition": "schema {\nquery: Query\nmutation: Mutation\n }\n\n type Query {\n singlePost(id: ID!): Post2\n }\n\n type Mutation {\n putPost(id: ID!, title: String!): Post2\n }\n\n type Post2 {\n id: ID!\n title: String!\n }"
},
"previousResourceProperties": {
"ApiId": "123",
"Definition": "schema {\nquery: Query\nmutation: Mutation\n }\n\n type Query {\n singlePost(id: ID!): Post\n }\n\n type Mutation {\n putPost(id: ID!, title: String!): Post\n }\n\n type Post {\n id: ID!\n title: String!\n }"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"AWS::AppSync::GraphQLSchema": {
"resourceProperties": {
"ApiId": "123",
"Definition": "schema {\nquery: Query\nmutation: Mutation\n }\n\n type Query {\n singlePost(id: ID!): Post\n }\n\n type Mutation {\n putPost(id: ID!, title: String!): Post\n }\n\n type Post {\n id: ID!\n title: String!\ndate: AWSDateTime\n }"
},
"previousResourceProperties": {
"ApiId": "123",
"Definition": "schema {\nquery: Query\nmutation: Mutation\n }\n\n type Query {\n singlePost(id: ID!): Post\n }\n\n type Mutation {\n putPost(id: ID!, title: String!): Post\n }\n\n type Post {\n id: ID!\n title: String!\n }"
}
}
}
1 change: 1 addition & 0 deletions hooks/AppSync_BreakingChangeDetection/lombok.config
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lombok.addLombokGeneratedAnnotation = true
Loading

0 comments on commit 14d3b96

Please sign in to comment.