できるだけ最小構成のAWS AppSync with Lambda認証

## 作りたいもの
CDKでつくるMutation, Query, Subscriptionをするだけのできるだけ小さいサンプル２
認証をLambdaでする

完成品 https://github.com/tky/app-sync-with-lambda-auth-example

## 注意点
- subscription使うためにはschemaはcodeで定義
- aws-cdk/aws-appsync-alphaは将来変更が入るかも

## 実装

importから

```
import { Duration, Stack, StackProps } from "aws-cdk-lib";
import { Construct } from "constructs";
import { Table, AttributeType } from "aws-cdk-lib/aws-dynamodb";
import {
  AuthorizationType,
  Directive,
  GraphqlApi,
  Field,
  GraphqlType,
  InputType,
  MappingTemplate,
  ObjectType,
  PrimaryKey,
  Values,
  ResolvableField,
  Schema,
} from "@aws-cdk/aws-appsync-alpha";
import { RemovalPolicy } from "aws-cdk-lib";
import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs";
```

簡単なschema

```
    const typeUser = new ObjectType("User", {
      definition: {
        id: GraphqlType.string({ isRequired: true }),
        name: GraphqlType.string({ isRequired: true }),
        age: GraphqlType.int({ isRequired: false }),
      },
    });

    const typeUserInput = new InputType("UserInput", {
      definition: {
        name: GraphqlType.string({ isRequired: true }),
        age: GraphqlType.int({ isRequired: false }),
      },
    });

    const schema = new Schema();
    schema.addType(typeUser);
    schema.addType(typeUserInput);
```

認証用のlambda

NodejsFunctionはこの書き方だと、{stackのファイル名}.{NodejsFunctionのid}.tsというファイルを探しに行きます。
cdk-stack.tsでhandlerというidにしたのでこのcdk-stack.tsと同じディレクトリにあるcdk-stack.handler.tsが必要です。
id変えたりstackのファイル名を変更する場合は注意が必要

```
    const handler = new NodejsFunction(this, "handler", {
      functionName: "app-sync-with-lambda-auth-hander",
    });
```

API。認証をLambaにする

```
    const api = new GraphqlApi(this, "api", {
      name: "app-sync-with-lambda-auth-example-api",
      schema,
      authorizationConfig: {
        defaultAuthorization: {
          authorizationType: AuthorizationType.LAMBDA,
          lambdaAuthorizerConfig: {
            handler,
            resultsCacheTtl: Duration.seconds(1),
          },
        },
      },
      xrayEnabled: false,
    });
```

dynamodb

```
    const table = new Table(this, "table", {
      tableName: "app-sync-with-lambda-auth-example-table",
      partitionKey: {
        name: "id",
        type: AttributeType.STRING,
      },
      removalPolicy: RemovalPolicy.DESTROY,
    });

    const dataSource = api.addDynamoDbDataSource("data_source", table);
```

Query

```
    schema.addQuery(
      "getUser",
      new ResolvableField({
        returnType: typeUser.attribute(),
        args: {
          id: GraphqlType.id({ isRequired: true }),
        },
        dataSource,
        requestMappingTemplate: MappingTemplate.dynamoDbGetItem("id", "id"),
        responseMappingTemplate: MappingTemplate.dynamoDbResultItem(),
      })
    );
```

Mutation

```
    schema.addMutation(
      "addUser",
      new ResolvableField({
        returnType: typeUser.attribute(),
        args: {
          id: GraphqlType.string({ isRequired: true }),
          input: typeUserInput.attribute({ isRequired: true }),
        },
        dataSource,
        requestMappingTemplate: MappingTemplate.dynamoDbPutItem(
          PrimaryKey.partition("id").is("id"),
          Values.projecting("input")
        ),
        responseMappingTemplate: MappingTemplate.dynamoDbResultItem(),
      })
    );
```

Subscription

```
    schema.addSubscription(
      "updatedUser",
      new Field({
        returnType: typeUser.attribute(),
        args: { id: GraphqlType.id({ isRequired: true }) },
        directives: [Directive.subscribe("addUser")],
      })
    );
```

## 実行

API認証の場合は```-H "x-api-key:${API_KEY}"```が必要でしたが今回はlambda認証なので代わりに```-H "Authorization:${KEY}"```が必要です

とりあえずABCにしてあります

```
export QUERY='
  mutation AddUser {
    addUser(id: "user1", input: {age: 22, name: "first user"}) {
      age
      id
      name
    }
  }
'
```

```
gq ${API_ENDPOINT} -H "Authorization:ABC" -q "${QUERY}"
Executing query... done
{
  "data": {
    "addUser": {
      "age": 22,
      "id": "user1",
      "name": "first user"
    }
  }
}
```

もちろん他の値だと認証失敗

```
$ gq ${API_ENDPOINT} -H "Authorization:XYZ" -q "${QUERY}"
Executing query... error
Error:  {
  errors: [
    {
      errorType: 'UnauthorizedException',
      message: 'You are not authorized to make this call.'
    }
  ]
}
```

lambda

``` lib/cdk-stack.hander.ts
exports.handler = async (event: any) => {
  console.log(JSON.stringify(event));

  const isAuthorized = event.authorizationToken == "ABC";

  const response = {
    isAuthorized,
  };

  console.log(JSON.stringify(response));
  return response;
};
```

hotswapつけてwatchするとlambda変更してすぐデプロイしてくれて便利です。
ついでにconsoleにログも出してくれます。

```
$cdk watch --hotswap
```