Resources:
  EventBus:
    Type: AWS::Events::EventBus
    Properties:
      Name: !Sub "${AWS::StackName}-EventBus"

  EventBridgeRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Action:
          - sts:AssumeRole
          Principal:
            Service:
            - appsync.amazonaws.com
            - lambda.amazonaws.com

  AppSyncApi:
    Type: AWS::AppSync::GraphQLApi
    Properties:
      Name: AppSyncApi
      AuthenticationType: API_KEY

  ApiSchema:
    Type: AWS::AppSync::GraphQLSchema
    Properties:
      ApiId: !GetAtt AppSyncApi.ApiId
      Definition: |
        type EntryDetails {
          ErrorCode: String
          ErrorMessage: String
          EventId: String!
        }

        type PutEventsResult {
          Entries: [EntryDetails!]!
          FailedEntry: Int
        }

        type Query {
          sayHello: PutEventsResult!
        }

        schema {
          query: Query
        }

  AppSyncEventBusDataSource:
    Type: AWS::AppSync::DataSource
    Properties:
      ApiId: !GetAtt AppSyncApi.ApiId
      Name: AppSyncEventBusDataSource
      Type: AMAZON_EVENTBRIDGE
      ServiceRoleArn: !GetAtt EventBridgeRole.Arn
      EventBridgeConfig:
        EventBusArn: !GetAtt 'EventBus.Arn'

  AppSyncSayHelloResolver:
    DependsOn: ApiSchema
    Type: AWS::AppSync::Resolver
    Properties:
      ApiId: !GetAtt AppSyncApi.ApiId
      TypeName: Query
      FieldName: sayHello
      DataSourceName: !GetAtt AppSyncEventBusDataSource.Name
      Runtime:
        Name: APPSYNC_JS
        RuntimeVersion: 1.0.0
      Code: |
        import { util } from '@aws-appsync/utils';
        export function request(ctx) {
          return {
            "operation" : "PutEvents",
            "events" : [{
              "source": "com.mycompany.myapp",
              "detail": {
                "key1" : "value1",
                "key2" : "value2"
              },
              "resources": ["Resource1", "Resource2"],
              "detailType": "myDetailType"
            }]
          }
        }

        export function response(ctx) {
          if(ctx.error)
            util.error(ctx.error.message, ctx.error.type, ctx.result)
          else
            return ctx.result
        }

  Connector:
    Type: AWS::Serverless::Connector
    Properties:
      Source:
        Id: AppSyncEventBusDataSource
      Destination:
        Id: EventBus
      Permissions:
      - Write

  ApiKey:
    Type: AWS::AppSync::ApiKey
    Properties:
      ApiId: !GetAtt AppSyncApi.ApiId

  TriggerFunction:
    Type: AWS::Serverless::Function
    Properties:
      Role: !GetAtt EventBridgeRole.Arn
      Environment:
        Variables:
          API_KEY: !GetAtt ApiKey.ApiKey
          GRAPHQL_URL: !GetAtt AppSyncApi.GraphQLUrl
          EventBusName: !Ref EventBus
      Runtime: nodejs16.x
      Handler: index.handler
      InlineCode: |
        const https = require("https");

        exports.handler = async () => {
          const queries = {
            sayHello: /* GraphQL */ `
              query {
                sayHello {
                  Entries {
                    ErrorCode
                    EventId
                    ErrorMessage
                  }
                  FailedEntry
                }
              }
            `,
          };

          const fetch = async (url, options) =>
            new Promise((resolve, reject) => {
              const req = https.request(url, options, (res) => {
                const body = [];
                res.on("data", (chunk) => body.push(chunk));
                res.on("end", () => {
                  const resString = Buffer.concat(body).toString();
                  resolve(resString);
                });
              });
              req.on("error", (err) => {
                reject(err);
              });
              req.on("timeout", () => {
                req.destroy();
                reject(new Error("Request time out"));
              });
              req.write(options.body);
              req.end();
            });

          const makeRequest = async (queryName) => {
            const options = {
              method: "POST",
              headers: {
                "x-api-key": process.env.API_KEY,
              },
              body: JSON.stringify({ query: queries[queryName] }),
              timeout: 600000, // ms
            };

            const response = await fetch(process.env.GRAPHQL_URL, options);
            let body = JSON.parse(response);
            const data = body.data?.[queryName];
            
            if (body.errors !== undefined) {
              throw JSON.stringify(body.errors);
            }
            
            if (data.FailedEntry != null || data.ErrorCode != null ) {
              throw new Error(
                `${queryName} error: failed to send event to eventbus ${process.env.EventBusName}`);
            }
            
            return body.data;
          };

          await makeRequest("sayHello");
        };

Metadata:
  SamTransformTest: true
