#pyconjp PyCon JP 2019に参加しました & 発表しました

9/16(月・祝)・17(火)に大田区産業プラザ PiOで開催された「PyCon JP 2019」に参加 & 発表しました。
トップページ - PyCon JP 2019

 
去年同様、今年も無事に参加できてよかったです。
#pyconjp PyCon JP 2018に参加しました - メモ的な思考的な

 
動画もすでに公開されています。ありがとうございます。
PyConJP - YouTube

 
ここでは自分の発表、および全体を通してのメモを残します。誤りがありましたらご指摘ください。

 
目次

 

自分の発表「知ろう!使おう!HDF5ファイル!」

タイムテーブル上の詳細はこちら
知ろう!使おう!HDF5ファイル!

 
スライドです。

 
発表で使用したソースコードはこちらです。
https://github.com/thinkAmi/PyCon_JP_2019_talk

 
動画はこちら。7分くらいから始まるようです。
02-603_知ろう!使おう!HDF5ファイル!(thinkAmi) - YouTube

 
発表ですが、時間配分を誤り、持ち時間を発表で使い切ってしまったことから、時間内に質疑応答ができませんでした。本当に申し訳ないです。

本当は「この発表の次のステップ」についてふれたかったのですが、時間の都合上カットしました。資料だとp77〜あたりです。

 
ただ、ありがたいことに、発表終了後いくつか質問をいただきました。

覚えているのは

  • そもそも、何のデータを入れるためにHDFができたのか
  • クロスプラットフォーム間で、Excelファイルの罫線や内容が壊れないか (特に大きなサイズのExcelファイル)
  • zipファイルとの違い

あたりです。

ただ発表後も緊張が解けず、質問と回答の記憶があやふやです。そのため、もしかしたら質問の内容不足や誤りがあるかもしれません。その場合はお知らせください。

また、ちょっと検証したいこともあるため、検証後にBlog化したいです。

 
というところで、以降は今回のPyCon JP 2019のメモです。

 

参加したセッション

基調講演:Why Python is Eating the World

Cory Althoff 氏

 
書籍「独学プログラマー」の著者による基調講演でした。

Think PythonPythonを学びはじめてからの行動を聞き、やはり何か気になることがあれば行動することが大事だなと感じました。

また、後半のStart somethingのところを聞き、今の自分にできることとして、このBlogを引き続き書いていこうと感じました。

 

ExcelPython による社会インフラシステムの設定ファイルの自動生成

武山 文信 氏

 
設定ファイル という単語につられて参加しました。

お話の中で、設定ファイル生成ツールに求められる条件が整理されていましたので、もし同じような状況になったら参考にしたいです。

互換性を保ちながら、できるところだけでも自動化していく姿も印象に残りました。

 

Djangoで実践ドメイン駆動設計による実装

大島 和輝 氏

 
Djangoでのドメイン駆動設計が気になって参加しました。

セッションの中でもふれられていましたが、Djangoでのドメイン駆動設計例が見当たらないため、今回のソースコードを参考にして取り組んでみたいです。

Python製のDIコンテナ python-inject にも言及しており、後で見てみようと思いました。

 

SupportingPython3 in Large Scale Project

Kir Chou 氏

 

Python2からPython3に切り替えるにあたり、考えるべきことがいくつもあげられていました。

また、技術的な資料へのリンクも豊富にあるのが助かりました。

今のところ手元には移行するものはありませんが、今後出会う機会があれば、参考にしたいと感じました。

 

Python ウェブアプリケーションのためのプロファイラの実装

Yusuke Miyazaki 氏

 
昔、WSGIミドルウェアについて調べていた時に、 wsgi-lineprof を試したことがあったため、内部構造などを知りたくて参加しました。

wsgi-lineprofのアーキテクチャの図が分かりやすかったです。

また、プロファイラはどのような技術で構成されているかの紹介もあり、こんな便利な機能がいろいろとあるんだと勉強になりました。

 

Doujin-activity with Ren'Py

Daisuke Saito 氏

 
来週の技術書典7に参加予定なこともあり、 Doujin というタイトルに惹かれて参加しました。

Ren'Pyの概要と、簡単にゲームができあがるということが分かりました。

 

基調講演:Pythonで切り開く新しい農業

小池 誠 氏

 
きゅうりで機械学習 という話を読んだこともあり、楽しみな基調講演でした。

初めて見た時とは判別機の扱いが変わっていて、判別機が人間のサポートをするようにしたことで、熟練者の技術継承がしやすくなっているように見えました。

講演の中で特に印象に残ったのは、終わりの方の すべては学ぶためにとすれば、失敗ではなくなる。やってみなければわからない という言葉です。

それを聞き、次に活かせるような形で失敗するよう心がけようと思いました。

 

Pythonで始めてみよう関数型プログラミング

寺嶋 哲 氏

 
関数型プログラミングについて、その考え方とPythonで実現するためのライブラリ紹介がありました。

自分の今のコードに取り入れることができそうな関数型の考え方がいくつもあり、参考になりました。

あとで資料を見返して、理解を深めたいと感じました。

 

Pythonでライブをしよう - FoxDotを使った新時代のPython活用法 -

田中 慎太郎 氏

 
同僚です。

社内のPyCon JP 予行演習会にてライブコーディングを聴いた時から「当日は、この次のコマで自分が発表するから、この音楽セッションでリラックスしよう」と考えていました。

デモが進むに従って曲になっていったので、「あの変更でこんなふうに音が変化するんだ」と、とても不思議な感覚でした。音楽に詳しくないせいかもしれませんが、新鮮でした。

Youtube上だとFoxDotの音があまり残っていないようで残念ですが、ライブだと思えば仕方ないですね。。。

 

機械学習ライブラリのPython API作成方法

山入端 孝浩 氏

 

外部ライブラリのラッパーを作る時の大切なこと(既存との互換性やメモリ使用量など)についてふれられていました。

普通に使う時とpytestの時でメモリ使用量が異なるの、あまり着目したことがなかったので、参考になりました。

 

Ansibleを通じて「べき等性」を理解してみよう

Kazuya Takei 氏

 
Ansibleではどんなふうに冪等性を担保しているのか気になっていたこともあり、参加しました。

ソースコードの解説から、Ansibleががんばって標準出力を解析してべき等性を担保していると分かり、意外と泥臭いことをやってると分かりました。

あとは、Ansibleで管理できるルーターがほしいなーと思いました。

 

その他

同僚のスピーカーなど

上記にもありますが、私を含めて、会社から3名がスピーカーとして登壇しました。もう一人はこちら。
Pythonと便利ガジェット、サービス、ツールを使ってセンシング〜見る化してみよう

 
また、今年もぷろぷろ(@pro_proletariat)氏の呼びかけで、社内でPyCon JP 予行演習会が何回か開催されました。ありがとうございました。

 
あと、社内のPython使いの絵師さんが描いたマスコットキャラクター(ぎーらぼちゃん)も、無事デビューできてよかったです。

 

ブース

2日目の登壇ということもあり、精神的余裕があまりなく、ブース巡りはあまりできなかったのが残念でした。

1日目のはじめに、Python EDのガラガラでマグカップをいただきました。ありがとうございました。

 
また、書籍「独学プログラマー」のサイン会がありました。めったにない機会なので書籍を購入しサインを頂きました。ありがとうございました。

 

パーティ

ご挨拶したかった方々といろいろとお話できてよかったです。

Welcomeパーティの時もそうでしたが、年々パーティの雰囲気に慣れてきているのか、今年は自分から話しかけられたので成長した感がありました。

 
最後になりましたが、PyCon JP 2019を運営してくださったみなさま、参加者のみなさま、ありがとうございました。

来年も同じ場所で開催されるようですので、楽しみにして一年を過ごしたいと思います!

#agileshinano #glnagano Agile Jam in ながのに参加しました

8/24にギークラボ長野で開かれた「Agile Jam in ながの」に参加しました。
Agile Jam in ながの - connpass

 
平日夜に開催されているワークショップの拡大版で、複数のスクラムマスターの方々からお話を直接聞けるなど、とても有意義なイベントでした。

以下、簡単なメモです。

 
目次

 

オープニング・セッション

@shigeshibu44さん、@chinoppy0727さん

隣の方々と自己紹介をしました。

その時の手法が Moving Motivators で、自分のモチベーションがどこからやってくるのかを伝えるものでした。
Moving Motivators (CHAMPFROGS): Intrinsic Motivation Game - Management 3.0

 
自分は 好奇心熟達受容 を選びましたが、他の方々の内容を聞いて「そういう考え方もいいなー」と感じました。

そのため、Moving Motivatorsは

  • 自分の志向について、振り返りしやすい
  • チームビルディング時に、メンバー間でお互いの志向が知りやすい

など、とても良いツールだなと感じました。

 

セッション1

@itoyama_yuukiさん

全社ウォーターフォールの中、1つのプロジェクトをスクラムで始め、2年後にはほぼ全社スクラムになった流れを伝えてくださいました。

アジャイルはじめの一歩」について考えたりすることもあるので、どれも印象に残ったセッションでした。

  • スクラム導入のために
    • スクラム説明会をやる
      • 各自の役割を伝える
      • スクラムガイドを使って説明する
      • 何度も伝える
      • 自分から社内勉強会を開く
      • スクラムランチやSlackのチャンネルなど、誰もが相談しやすい環境を作る
    • 新規プロダクト or 区切りの良いところから導入する
      • 途中から導入するのは難しい
      • 既存の開発手法を無視すると宣言して始めた
        • 膨大なドキュメント作成への注力をやめる
        • 会社に受け入れる土壌があり、組織的なバックアップもあった
      • 途中から開発モデルが定義され、スムーズに
  • スクラムのプラクティスのうち、取り入れられるところがあれば取り入れる
    • 振り返りだけでもやってみる
    • 定例を開き、みんなで集まって話す
  • スクラムまわり
    • スクラムのイベントには時間を使う
    • 開発チームに対しては、リモートでスクラムをやっても問題ない
      • スクラムのイベントには、Zoomなどを使う
      • PO/SMがリモートだと難しいことかもしれない

 

セッション2

@massa142さん、@moon_in_naganoさん

資料:スクラムの現場 - slideship.com

 
セッションで一番印象に残ったのは

  • スクラム = 現状把握のためのフレームワーク
  • スクラムで大事なことは、透明性を担保すること
    • 開発に必要なものは、社員・業務委託に関わらず自由にアクセスさせる
    • 議事録を作り公開する

です。

その他は以下です。

  • スクラムのコミットメント
    • プランニングで計画を完了させるために最大限努力する
      • これがコミットメントだと、みんなの認識を合わせる
  • 見積もり
    • 工数ではなく、やることの相対的な大きさ
      • 基準チケットの何倍かかるかを見積もる
    • チケットの精度を高めるより、ベロシティが安定することが大事
      • 精度よりも、技術的な話をするのに時間を使う
  • ホワイトボードに問題点を書くことで、チーム vs ホワイトボードという構図にする
    • 個人の問題ではなくチームの問題にする
  • とりあえずやってみる
    • 分からないものについては議論しない
    • やってみた経験・学びを次に活かす
  • ポジティブ思考でやっていくの大事
    • KPTのProblemを歓迎する
  • アジャイルは、ウォーターフォールと対立するわけではない

 

ワークショップ

参加者で話し合いたい内容を挙げて、その内容について2つのテーブルに分かれて話し合うというワークショップでした。

スピーカーの方々へのQAやお互いの意見交換を行い、最後は模造紙にまとめ、ギークラボ長野の壁に張り出しました。

クロージングでは、スクラムのイベントについての紹介もありました。
Regional Scrum Gathering® Tokyo 2019

 

懇親会

場所を懇親会会場を移して、スクラムマスターの方々を交えて

などを行い、楽しい時間を過ごせました。

雰囲気です。

 
最後になりましたが、企画・運営・参加をされたみなさま、ありがとうございました。

*1:お酒が入っていたので、記憶があやしい...

AWS Amplifyで、カスタムカテゴリを作って、カスタムリソースを追加してみた

今まで、Amplifyが用意する以外のリソースを使いたい場合は、 <project_root>/amplify/backend/api/<API name>/stacks ディレクトリの中に、CloudFormation(CFn)ファイルを作成して対応してきました。
AWS AppSync + Amplify JavaScript + CustomResourcesで、既存のDynamoDBなどをDatasourceとしたリゾルバーを作成する - メモ的な思考的な

 
それ以外の方法がないかを探したところ、以下のissueコメントに、カスタムカテゴリを作っても追加できる旨が記載されていました。
Support for custom Resources/CloudFormation templates · Issue #80 · aws-amplify/amplify-cli

そこで、issueに書かれた方法で試してみたため、メモを残します。

 
ちなみに、「カスタムカテゴリ」と表現するのが正しいのかは微妙ですが、

$ amplify --help


| Category      |
| ------------- |
| analytics     |
...

と、元々あるAnalyticsやAPIはカテゴリ(Category)と呼ぶようです。

そのため、この記事ではそれにならって、今回自作するカテゴリのことをカスタムカテゴリと呼ぶことにします。

 
目次

 

環境

 
今回は amplify init & amplify api add で、APIモジュールを追加した後の環境で、

  • CognitoのIDプールを作成し、amplify init で作成した承認済/未認証ロールを紐付ける
  • AppSync GraphQLへのアクセスを許可するIAMポリシーを作成し、未承認ロールに割り当てる

というカスタムリソースを書いてみます。

amplify init & amplify api add は以下のような感じです。

# 初期化
$ amplify init
Note: It is recommended to run this command from the root of your app directory
? Enter a name for the project CustomCategory
? Enter a name for the environment dev
? Choose your default editor: Visual Studio Code
? Choose the type of app that you're building javascript
Please tell us about your project
? What javascript framework are you using none
? Source Directory Path:  src
? Distribution Directory Path: dist
? Build Command:  npm run-script build
? Start Command: npm run-script start
Using default provider  awscloudformation

For more information on AWS Profiles, see:
https://docs.aws.amazon.com/cli/latest/userguide/cli-multiple-profiles.html

? Do you want to use an AWS profile? Yes
? Please choose the profile you want to use default

# APIの追加
$ amplify api add
? Please select from one of the below mentioned services GraphQL
? Provide API name: CustomCategoryAPI
? Choose an authorization type for the API API key
? Do you have an annotated GraphQL schema? No
? Do you want a guided schema creation? No
? Provide a custom type name MyType

 

カスタムカテゴリを追加 

ディレクトリの作成

<project_root>/amplify/backend/ ディレクトリの下に、 identity ディレクトリを作成します。

 

CFnテンプレートの作成

今回はIDプールとRoleのマッピングを行うため、

  • AWS::Cognito::IdentityPool
  • AWS::Cognito::IdentityPoolRoleAttachment

の2つのResourceが必要です。

そのため、このResourceが含まれるファイルを template.json として、上記の identity ディレクトリに作成します。

 

IDプールの作成

まずは AWS::Cognito::IdentityPool のリソースを作成します。

amplifyのenvとIDプールの名称をパラメータとして受け取り、CoginitoのIDプールを作成します。

AllowUnauthenticatedIdentities: true としてありますが、IDプールの 認証されていない ID に対してアクセスを有効にする にチェックを入れてみたかっただけで、特に深い意味はありません。

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Parameters": {
    "env": {
      "Type": "String",
    },
    "IdentityPoolName": {
      "Type": "String"
    },
...
  },
  "Resources": {
    "IdentityPool": {
      "Type": "AWS::Cognito::IdentityPool",
      "Properties": {
        "IdentityPoolName": {
          "Fn::Join": [
            "__",
            [
              {
                "Ref": "IdentityPoolName"
              },
              {
                "Ref": "env"
              }
            ]
          ]
        },
        "AllowUnauthenticatedIdentities": "true"
      }
    },

 

amplify initで作成されたものをnested-cloudformation-stack.ymlで確認し、ロールを割り当て

AWS::Cognito::IdentityPoolRoleAttachment を設定するには、

  • 認証されていないロール
  • 認証されたロール

が必要です。

今回は、Amplifyの動きと同じく、amplify init で作成される認証されていないロールと認証されたロールを、自作のIDプールに割り当ててみます。

CFn内でamplify initで作成されるロールを参照する方法を探したところ、 <project_root>/amplify/backend/awscloudformation/nested-cloudformation-stack.yml に記載されているリソースを使えばよさそうでした。

今回は、

  • AuthRole
  • UnauthRole

のArnを Fn::GetAtt で参照します。

 
ただ、CFnのテンプレート内では参照できなかったため、

  • CFnテンプレート (template.json) は Parameter で外部から値を受け取る
  • CFnテンプレートに値を渡すためのファイル parameters.json を作成し、その中で Fn::GetAtt を使ってロールのArnを取得・設定する

としました。

関係する部分を抜粋したのは以下です。

template.json

{
  "AWSTemplateFormatVersion": "2010-09-09",
  ...
  "Parameters": {
    ...
    "AuthRoleArn": {
      "Type": "String"
    },
    "UnauthRoleArn": {
      "Type": "String"
    }
  },
  "Resources": {
    ...
    "IdentityPoolRoleMap": {
      "Type": "AWS::Cognito::IdentityPoolRoleAttachment",
      "Properties": {
        "IdentityPoolId": {
          "Ref": "IdentityPool"
        },
        "Roles": {
          "unauthenticated": {
            "Ref": "UnauthRoleArn"
          },
          "authenticated": {
            "Ref": "AuthRoleArn"
          }
        }
      },
      "DependsOn": "IdentityPool"
    }
  },
...
}

 
parameters.json

{
  "IdentityPoolName": "CustomIdentityPool",
  "AuthRoleArn": {
    "Fn::GetAtt": [
      "AuthRole",
      "Arn"
    ]
  },
  "UnauthRoleArn": {
    "Fn::GetAtt": [
      "UnauthRole",
      "Arn"
    ]
  }
}

 

amplify env checkout

このままの状態で amplify push しようとしても、まだAmplify CLIにはAPIしか認識されていません。

$ amplify status

Current Environment: develop

| Category | Resource name     | Operation | Provider plugin   |
| -------- | ----------------- | --------- | ----------------- |
| Api      | CustomCategoryAPI | Create    | awscloudformation |

 
そのため、amplify env checkout を行い、Amplify CLIに認識してもらいます。

# 環境の確認
$ amplify env list

| Environments |
| ------------ |
| *develop     |


# チェックアウト
$ amplify env checkout develop


# 認識された
$ amplify status

Current Environment: develop

| Category | Resource name      | Operation | Provider plugin   |
| -------- | ------------------ | --------- | ----------------- |
| Api      | CustomCategoryAPI  | Create    | awscloudformation |
| Identity | CustomIdentityPool | Create    | awscloudformation |

 
また、amplify push などの情報を持っている <project_root>/amplify/backend/amplify-meta.json も更新されました。

{
    "providers": {
        "awscloudformation": {
            ...
        }
    },
    "api": {
        "CustomCategoryAPI": {
            ...
            }
        }
    },
    // 追加された
    "identity": {
        "CustomIdentityPool": {
            "providerPlugin": "awscloudformation"
        }
    }
}

 

amplify pushして状況確認

カスタムカテゴリが認識されたため、amplify push すると、成功しました。

$ amplify push

Current Environment: develop

| Category | Resource name      | Operation | Provider plugin   |
| -------- | ------------------ | --------- | ----------------- |
| Api      | CustomCategoryAPI  | Create    | awscloudformation |
| Identity | CustomIdentityPool | Create    | awscloudformation |
? Are you sure you want to continue? Yes

GraphQL schema compiled successfully.

? Do you want to generate code for your newly created GraphQL API Yes
? Choose the code generation language target javascript
? Enter the file name pattern of graphql queries, mutations and subscriptions src/graphql/**/*.js
? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes
? Enter maximum statement depth [increase from default if your schema is deeply nested] 2
...
✔ All resources are updated in the cloud

GraphQL endpoint: https://host.appsync-api.region.amazonaws.com/graphql
GraphQL API KEY: xxx

 
CognitoのIDプールを確認したところ、たしかに作成されていました。

f:id:thinkAmi:20190730221051p:plain:w450

 

IAMポリシーの作成と未承認ロールへの割り当て

まずは、AppSync GraphQLへのアクセスを許可するIAMポリシーを作成します。

IAMポリシーでGraphQLへのアクセスを許可するには "arn:aws:appsync:${AWS::Region}:${AWS::AccountId}:apis/${apiID}/types/Mutation/* のようなResourceに対して設定する必要があります。

ただ、この中の apiID については、カスタムカテゴリの中では取得できませんでした。一方、APIカテゴリの中では取得できました。

 
そのため、 <project_root>/amplify/backend/api/<API name>/stacks/ ディレクトリの中に IAMRole.json を作成します。

この中で

  • パラメータ AppSyncApiId を参照し、AppSyncのAPI IDを取得
  • パラメータ UnauthRoleName を参照し、未承認ロールに対しポリシーを割り当て

を行います。

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "AppSync GraphQL Policy",
  "Metadata": {},
  "Parameters": {
    "AppSyncApiId": {
      "Type": "String",
      "Description": "The id of the AppSync API associated with this project."
    },
    "env": {
      "Type": "String",
      "Description": "The environment name. e.g. Dev, Test, or Production",
      "Default": "NONE"
    },
    "UnauthRoleName": {
      "Type": "String"
    }
  },
  "Resources": {
    "AppSyncGraphQLPolicy": {
      "Type": "AWS::IAM::ManagedPolicy",
      "Properties": {
        "PolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Action": [
                "appsync:GraphQL"
              ],
              "Resource": [
                {
                  "Fn::Sub": [
                    "arn:aws:appsync:${AWS::Region}:${AWS::AccountId}:apis/${apiID}/types/Query/*",
                    {
                      "apiID": {
                        "Ref": "AppSyncApiId"
                      }
                    }
                  ]
                },
                {
                  "Fn::Sub": [
                    "arn:aws:appsync:${AWS::Region}:${AWS::AccountId}:apis/${apiID}/types/Mutation/*",
                    {
                      "apiID": {
                        "Ref": "AppSyncApiId"
                      }
                    }
                  ]
                }
              ]
            }
          ]
        },
        "ManagedPolicyName": {
          "Fn::Join": [
            "-",
            [
              "AppSyncGraphQLPolicy",
              {
                "Ref": "env"
              }
            ]
          ]
        },
        "Roles": [
          {
            "Ref": "UnauthRoleName"
          }
        ]
      }
    }
  },
  "Outputs": {
    "EmptyOutput": {
      "Description": "An empty output. You may delete this if you have at least one resource above.",
      "Value": ""
    }
  }
}

 

結果確認

未認証のロールに、AppSyncのGraphQLアクセスを許可するIAMポリシー AppSyncGraphQLPolicy-develop が割り当てられていました。

Cognito IDプールでの未認証ロール

f:id:thinkAmi:20190730222341p:plain:w450

ロールに割り当てられたポリシー

f:id:thinkAmi:20190730222416p:plain:w450

 

カスタムカテゴリを使わず、APIのカスタムリソースとして作成

ちなみに、今回作成したCognito IDプールのCFnファイルですが、カスタムカテゴリを使わなくても、APIカテゴリのカスタムリソースとすることも可能です。

<project_root>/amplify/backend/api/<API name>/build/cloud-formation-template.json を見ても、APIのカスタムリソースのパラメータが各リソースを参照できています。

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "An auto-generated nested stack.",
    "Metadata": {},
    "Parameters": {
        ...
        "AuthRoleArn": {
            "Type": "String"
        },
        "UnauthRoleArn": {
            "Type": "String"
        }
    },
    "Resources": {
        "GraphQLAPI": {
            "Type": "AWS::AppSync::GraphQLApi",
            ...
        },
        "GraphQLAPIKey": {
            "Type": "AWS::AppSync::ApiKey",
            ...
        },
        "GraphQLSchema": {
            "Type": "AWS::AppSync::GraphQLSchema",
            ...
        },
        "MyType": {
            "Type": "AWS::CloudFormation::Stack",
            ...
        },
        "CustomResourcesjson": {
            "Type": "AWS::CloudFormation::Stack",
            ...
        },
        "IAMRolejson": {
            "Type": "AWS::CloudFormation::Stack",
            ...
        },
        "templatejson": {
            "Type": "AWS::CloudFormation::Stack",
            "Properties": {
                "Parameters": {
                    "env": {
                        "Ref": "env"
                    },
                    "StackIdentityPoolName": {
                        "Ref": "StackIdentityPoolName"
                    },
                    "AuthRoleArn": {
                        "Ref": "AuthRoleArn"
                    },
                    "UnauthRoleArn": {
                        "Ref": "UnauthRoleArn"
                    }
                },

 

まとめ

以上より、AWS Amplifyでカスタムリソースを作る場合は

  • <project_root>/amplify/backend/awscloudformation/nested-cloudformation-stack.yml
  • <project_root>/amplify/backend/api/<API name>/build/cloud-formation-template.json

などを見て使えるリソースを探しつつ、

  • APIの中のstacksの中にCFnファイルを作成
  • カスタムカテゴリを作成し、その中にCFnファイルを作成

のどちらを行い、必要なパラメータは parameters.json に記載すれば良いことが分かりました。

 

ソースコード

Githubに上げました。ディレクトcustom_category 以下が今回のファイルです。
https://github.com/thinkAmi-sandbox/AWS_AppSync_Amplify-sample

AWS Amplifyで、amplify env を使って、開発環境の共有と分離をしてみた

開発をしていると、AWS Amplifyを使ったソースコードGithubなどでチームで共有することがあります。

AppSync APIやDynamoDBなどの環境を同一にして、チームで開発する方法を探したところ、公式ドキュメントに amplify env を使う方法が記載されていました。

 
そこで、実際にどんな感じになるのか試してみたため、メモを残します。

 
目次

 

環境

 

共有するソースコード

今回は、以下のリポジトリをチーム間で共有し、開発を進めるものとします。なお、 team-provider-info.json ファイルには機密情報が含まれていたため、わざと空ファイルにしてあります。
https://github.com/thinkAmi-sandbox/appsync_app_for_multi_developer

 
DataSourceとしてDynamoDBを使います。schema.graphqlは以下の通りです。

type Todo {
    title: String!
    content: String
}

input CreateTodoInput {
    title: String!
    content: String
}


type Query {
    getTodo(title: String!): Todo
}

type Mutation {
    createTodo(input: CreateTodoInput!): Todo
}

 
また、動作確認としてAppSyncを使ったWebアプリを用意します。

各ボタンをクリックすると、対応するQueryやMutationを実行します。

import Amplify, {API} from 'aws-amplify';
import awsconfig from './aws-exports';
import * as queries from "./graphql/queries";
import * as mutations from "./graphql/mutations";


const queryButton = document.getElementById('query');
queryButton.addEventListener('click', () => {
  console.log('Run Query!');
  Amplify.configure(awsconfig);
  API.graphql(
    {
      query: queries.getTodo,
      authMode: 'API_KEY',
      variables: {
        title: "hello"
      }
    }
  ).then((data) => {
    console.log(data);
  })
    .catch((e) => {
      console.log('error!');
      console.log(e)
    });
});


const mutationButton = document.getElementById('mutation');
mutationButton.addEventListener('click', () => {
  console.log('Run Mutation!');

  const content = document.getElementById('content').value;
  Amplify.configure(awsconfig);
  API.graphql(
    {
      query: mutations.createTodo,
      authMode: 'API_KEY',
      variables: {
        input: {
          title: "hello",
          content: content
        }
      }
    }
  ).then((data) => {
    console.log(data);
  })
    .catch((e) => {
      console.log('error!');
      console.log(e)
    });
});

 
また、DynamoDBには以下が登録されているとします。

f:id:thinkAmi:20190727203118p:plain:w300

 

git clone & amplify init で既存のAppSync環境にアクセスする

上記のリポジトリを手元に持ってきます。

$ git clone git@github.com:thinkAmi-sandbox/appsync_app_for_multi_developer.git .

 
git cloneしただけではAmplifyの環境ができていないため、 amplify init を実行します。

ゼロから開発するときと異なり、 Do you want to use an existing environment? が表示されます。

ここでは既存のAppSync環境にアクセスするため、 Yes を選択します。

$ amplify init
Note: It is recommended to run this command from the root of your app directory

# 既存のAppSync環境へアクセスするため、 "Yes" を選択
? Do you want to use an existing environment? Yes

? Choose the environment you would like to use: dev
? Choose your default editor: Visual Studio Code
Using default provider  awscloudformation

For more information on AWS Profiles, see:
https://docs.aws.amazon.com/cli/latest/userguide/cli-multiple-profiles.html

? Do you want to use an AWS profile? Yes
? Please choose the profile you want to use default
✔ Initialized provider successfully.

 
amplify status で状況を確認します。既存の環境が認識されているようです。

$ amplify status

Current Environment: dev

| Category | Resource name     | Operation | Provider plugin   |
| -------- | ----------------- | --------- | ----------------- |
| Api      | MultiDeveloperAPI | No Change | awscloudformation |

 
動作を確認するため、必要なモジュールをインストールします。

$ npm install

 
別の開発環境で実行していると分かるよう、 webpack.config.js 中のポート番号を 9501 へと変更します。

devServer: {
...
  port: 9501

 
起動して動作を確認します。ポートは 9501 を使っているようです。

$ npm start

...
ℹ 「wds」: Project is running at http://localhost:9501/
...

 
Queryボタンを押すと、既存のDynamoDBの値が取得できました。

f:id:thinkAmi:20190727203154p:plain

 

新しい環境 staging を作成し、動作確認

続いて、 amplify env add で新しい環境 staging を作成します。

$ amplify env add
Note: It is recommended to run this command from the root of your app directory

# 既存の環境を使うか確認されるため、 "No" を選択
? Do you want to use an existing environment? No

# あとは、ゼロから開発したときと同様
? Enter a name for the environment staging
Using default provider  awscloudformation

For more information on AWS Profiles, see:
https://docs.aws.amazon.com/cli/latest/userguide/cli-multiple-profiles.html

? Do you want to use an AWS profile? Yes
? Please choose the profile you want to use default
⠋ Initializing project in the cloud...

# 新しくstackが作成される
CREATE_IN_PROGRESS multideveloper-staging-xxx AWS::CloudFormation::Stack             
CREATE_IN_PROGRESS DeploymentBucket                      AWS::S3::Bucket
...

 
実行後、 amplify env list すると、環境が増えていました。また、新しく作成した環境 staging にも切り替わっています。

f:id:thinkAmi:20190727204103p:plain:w150

 
また、 amplify status で状況を確認すると、 Create に変わっていました。

$ amplify status

Current Environment: staging

| Category | Resource name     | Operation | Provider plugin   |
| -------- | ----------------- | --------- | ----------------- |
| Api      | MultiDeveloperAPI | Create    | awscloudformation |

 
他に、team-provider-info.json ファイルにも、新規環境 staging が追加されています。
https://aws-amplify.github.io/docs/cli-toolchain/quickstart#sharing-projects-outside-the-team-

{
    "dev": {
        "awscloudformation": {
            "AuthRoleName": "xxx",
            "UnauthRoleArn": "xxx",
            "AuthRoleArn": "xxx",
            "Region": "xxx",
            "DeploymentBucketName": "xxx",
            "UnauthRoleName": "xxx",
            "StackName": "xxx",
            "StackId": "xxx"
        }
    },
    "staging": {
        "awscloudformation": {
            "AuthRoleName": "yyy",
            "UnauthRoleArn": "yyy",
            "AuthRoleArn": "yyy",
            "Region": "yyy",
            "DeploymentBucketName": "yyy",
            "UnauthRoleName": "yyy",
            "StackName": "yyy",
            "StackId": "yyy"
        }
    }
}

 
ためしに、この状態でQueryを実行してみたところ、エラーとなりました。

f:id:thinkAmi:20190727204004p:plain:w450

 
AppSync API 環境がまだ作成されていないようなので、 amplify push したところ、新しいAppSync APIが作成されました。

$ amplify push

Current Environment: staging

| Category | Resource name     | Operation | Provider plugin   |
| -------- | ----------------- | --------- | ----------------- |
| Api      | MultiDeveloperAPI | Create    | awscloudformation |
? Are you sure you want to continue? Yes

GraphQL schema compiled successfully.

? Do you want to generate code for your newly created GraphQL API Yes
? Choose the code generation language target javascript
? Enter the file name pattern of graphql queries, mutations and subscriptions src/graphql/**/*.js
? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes
? Enter maximum statement depth [increase from default if your schema is deeply nested] 2
⠹ Updating resources in the cloud. This may take a few minutes...

...
CREATE_IN_PROGRESS apiMultiDeveloperAPI                  AWS::CloudFormation::Stack
...

 
各種AWSリソースも追加されています。

AppSync API

f:id:thinkAmi:20190727204252p:plain:w450

DynamoDB

f:id:thinkAmi:20190727204311p:plain:w450

S3

f:id:thinkAmi:20190727204338p:plain:w450

 
この状態でQueryやMutationを実行したところ、正常に動作しました。

f:id:thinkAmi:20190727204417p:plain:w450

 
DynamoDBにも登録されています。

f:id:thinkAmi:20190727204445p:plain:w300

 
以上より、 amplify env add で新しい環境を作成することで、既存の環境はそのままに、別の環境で作業できることが分かりました。

 

amplify env checkout で環境を切り替えて確認

再度、 dev 環境に切り替えた時に、元のリソースにアクセスできるかを確認します。

 
まずは、staging環境のDynamoDBの列contentsを bar へと更新します。

f:id:thinkAmi:20190727204728p:plain:w300

 
続いて、環境を切り替えます。

# 状況を確認
$ amplify env list

| Environments |
| ------------ |
| dev          |
| *staging     |

# checkout で切り替え
$ amplify env checkout dev
✔ Initialized provider successfully.
Initialized your environment successfully.

# 再度状況を確認
$ amplify env list

| Environments |
| ------------ |
| *dev         |
| staging      |

 
この状態でWebアプリのQueryを実行すると、dev環境のDynamoDBの値が取得できました。dev環境へアクセスできているようです。

f:id:thinkAmi:20190727204916p:plain:w450

 

注意:amplify delete は全環境を削除

amplify delete は実行時に

Are you sure you want to continue?(This would delete all the environments of the project from the cloud and wipe out all the local amplify resource files)

と表示されるように、全環境のリソースを削除します。うっかり amplify delete すると大変なことになります。

 
実際に試してみると

# env を確認
$ amplify env list

| Environments |
| ------------ |
| *dev         |
| staging      |


# stagingへと移動
$ amplify env checkout staging
✔ Initialized provider successfully.
Initialized your environment successfully.


# 削除
$ amplify delete
? Are you sure you want to continue?(This would delete all the environments of the project from the cloud and wipe out all the local amplify resource files) Yes

Deleting env:dev  # devを削除

Deleting env:staging  # stagingを削除
✔ Project deleted in the cloud
Project deleted locally.


# 確認すると、amplifyプロジェクト自体がない
$ amplify env list
/path/to/lib/node_modules/@aws-amplify/cli/node_modules/gluegun/build/index.js:13
    throw up;
    ^

Error: You are not working inside a valid amplify project.

となりました。

 
また、実際のリソースもありません。

DynamoDB

f:id:thinkAmi:20190728080221p:plain:w300

 
AppSync API

f:id:thinkAmi:20190728080245p:plain:w300

AWS AppSyncにて、同じ内容でMutationした場合に、Subscriptionがどうなるかを試してみた

AWS AppSyncでは、Mutationした時の通知をSubscriptionで受け取れます。
リアルタイムデータ - AWS AppSync

ただ、DynamoDBのレコードと同じ内容でMutationした場合でも、Subscriptionがどのように動作するのか気になったため、試してみた時のメモを残します。

 
目次

 

環境

  • AWS AppSync
  • aws-amplify 1.1.32

 

Schema

今回用意したSchemaです。Mutationに

  • createTodo
  • updateTodo

の2つを用意しています。

今回は、onCreateTodoのSubscriptionがupdateTodoのMutationに反応しないかを確認するため、create/updateと名称を分けています。

ただし、GraphQL的にはSQLのInsert/Updateのような区別はありません。

type Todo {
    title: String!
    content: String
}

input TodoInput {
    title: String!
    content: String
}

type Mutation {
    createTodo(input: TodoInput!): Todo
    updateTodo(input: TodoInput!): Todo
}

type Subscription {
    onCreateTodo(title: String, content: String): Todo
        @aws_subscribe(mutations: ["createTodo"])
    onUpdateTodo(title: String, content: String): Todo
        @aws_subscribe(mutations: ["updateTodo"])
}

 

ゾルバー

createTodo/updateTodoとも、リゾルバーのリクエストテンプレート/レスポンステンプレートは同じです。

リクエストテンプレート
{
  "version": "2017-02-28",
  "operation": "PutItem",
  "key": {
    "title": { "S" : "${context.args.input.title}" }
  },
  "attributeValues": $util.dynamodb.toMapValuesJson($context.args.input)
}

 

レスポンステンプレート
$util.toJson($context.result)

 

Amplifyを使ったアプリ
index.html

Mutation/Subscriptionするためのボタンを用意しています。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Amplify Framework</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div class="app">
  <div>Subscribe: Create (title="foo", content="bar")
    <button id="subscribe-create">Subscribe (Create)</button>
  </div>
  <div>Subscribe: Update (title="foo", content="bar")
    <button id="subscribe-update">Subscribe (Update)</button>
  </div>

  <hr>

  <div>データ作成
    <label for="title">タイトル</label>
    <input type="text" id="title" value="foo" />
    <label for="content">内容</label>
    <input type="text" id="content" value="bar" />
  </div>

  <div>Create button
    <button id="create">Create</button>
  </div>
  <div>Update button
    <button id="update">Update</button>
  </div>


</div>
<script src="main.bundle.js"></script>
</body>
</html>

 

app.js

ボタンのclickイベントで

  • HTMLの内容でMutation
  • Mutationに対するSubscription

が実行されるようにしています。

 
なお、Subscriptionについては、 unsubscribe() を使っていないため、

  • title = "foo"
  • content = "bar"

な内容でMutaitonされると、毎回反応するようにしています。

import Amplify, { API } from 'aws-amplify';
import awsconfig from './aws-exports';
import * as mutations from "./graphql/mutations";
import * as subscriptions from "./graphql/subscriptions";


// データ作成
const createButton = document.getElementById('create');
createButton.addEventListener('click', () => {
  callMutation(mutations.createTodo, 'create');
});

// データ更新
const updateButton = document.getElementById('update');
updateButton.addEventListener('click', () => {
  callMutation(mutations.updateTodo, 'update');
});


// Mutation関数以外はすべて共通
const callMutation = (func, funcType) => {
  Amplify.configure(awsconfig);
  API.graphql(
    {
      query: func,
      authMode: 'API_KEY',
      variables: {
        input: {
          title: document.getElementById('title').value,
          content: document.getElementById('content').value,
        }
      }
    }
  ).then((data) => {
    console.log(`${funcType}: target data.`);
    console.log(data);
  })
    .catch((e) => {
      console.log(`mutation(${funcType}) error`);
      console.log(e)
    });
};


// --------------------------
// Subscriptions
// --------------------------

// CreateをSubscribe
const subscribeCreateButton = document.getElementById('subscribe-create');
subscribeCreateButton.addEventListener('click', () => {
  callSubscription(subscriptions.onCreateTodo, 'subscribe(Create)');
});


// UpdateをSubscribe
const subscribeUpdateButton = document.getElementById('subscribe-update');
subscribeUpdateButton.addEventListener('click', () => {
  callSubscription(subscriptions.onUpdateTodo, 'subscribe(Update)');
});


// Subscription関数以外は共通
const callSubscription = (func, funcType) => {
  Amplify.configure(awsconfig);

  console.log(`subscribe(${funcType}): start...`);

  const client = API.graphql(
    {
      query: func,
      authMode: 'API_KEY',
      variables: {
        title: "foo",
        content: "bar"
      }
    }
  ).subscribe({
    next: (data) => {
      console.log(`subscribed: ${funcType}`);
      console.log(data);
    },
    error: (err) => {
      console.log(err);
      console.log(`sub error (${funcType})`);
    },
    close: () => console.log('sub done')
  })
};

 

動作確認

準備:Subscription開始
  • createTodo
  • updateTodo

のMutationに対してSubscriptionを実行しておきます。

f:id:thinkAmi:20190726215322p:plain:w450

 

新規登録時

Mutation createTodo() の実行ボタンを押したしたところ、想定通り、Subscription onCreateTodo() だけが反応しました。

f:id:thinkAmi:20190726215411p:plain:w450

 
上記を展開した内容はこちら。

f:id:thinkAmi:20190726215515p:plain:w450

 

同じ内容で更新時

同じ内容のまま、再度 Mutation createTodo() の実行ボタンを押したところ、Subscription onCreateTodo() が再度反応しました。

f:id:thinkAmi:20190726215733p:plain:w450

 
これより、特に設定をしなければ、同じ内容で更新した場合もSubscriptionで通知を受け取ることができるようです。

 

参考:createTodoは、同じキーで呼び出すとエラーにしたい

上記で見た通り、Mutation createTodo はDynamoDBに存在するキーで実行してもエラーになりません。

ただ、新規登録用のMutationなど、DynamoDBに存在するキーで実行した時はエラーにしたい場合、リゾルバーのリクエストテンプレートに condition を追加して制御します。

今回は attribute_not_exists を使って制御します。
https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Functions

{
  "version": "2017-02-28",
  "operation": "PutItem",
  "key": {
    "title": { "S" : "${context.args.input.title}" }
  },
  "attributeValues": $util.dynamodb.toMapValuesJson($context.args.input),
## 以降を追加
  "condition": {
      "expression": "attribute_not_exists(#title)",
      "expressionNames": {
          "#title": "title"
    }
  }
}

 
また、DynamoDBの状態は以下とします。

f:id:thinkAmi:20190726220957p:plain:w300

 
この時に、

  • title = "foo"
  • content = "ham"

にて、Mutation createTodo するとエラー DynamoDB:ConditionalCheckFailedException になります。

f:id:thinkAmi:20190726221157p:plain:w450

 
ただし、

  • title = "foo"
  • content = "bar"

の場合には、エラーとはなりません。

f:id:thinkAmi:20190726221316p:plain:w450

 
もう少し考える必要がありそうなものの、今回はこれくらいにします。

 

ソースコード

Githubに上げました。ディレクトsubscription_for_same_update の中が今回のファイルです。
https://github.com/thinkAmi-sandbox/AWS_AppSync_Amplify-sample

AWS Amplify CLIで、.graphqlconfig.ymlを使って、schema.jsonやqueries.jsonなどの保存場所を変更する

Amplify CLIamplify push することで、必要に応じてAppSyncのスキーマファイル ( <project_root>/amplify/backend/api/"api_name"/schema.graphql ) を元に、Amplify Framework向けのソースコードを生成してくれます。

$ amplify push

Current Environment: dev

| Category | Resource name    | Operation | Provider plugin   |
| -------- | ---------------- | --------- | ----------------- |
| Api      | graphqlconfigApi | Create    | awscloudformation |
? Are you sure you want to continue? Yes

GraphQL schema compiled successfully.

? Do you want to generate code for your newly created GraphQL API Yes
? Choose the code generation language target javascript
? Enter the file name pattern of graphql queries, mutations and subscriptions src/graphql/**/*.js

# 自動生成するか
? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes

? Enter maximum statement depth [increase from default if your schema is deeply nested] 2

...
✔ Downloaded the schema
✔ Generated GraphQL operations successfully and saved at src/graphql
✔ All resources are updated in the cloud

 
デフォルトでは、 src/graphql ディレクトリに作成されます。

ただ、amplify push後に、自動生成ファイルの保存先を変更しようと思ったため、その時のことをメモします。

 

環境

  • Amplify CLI 1.8.5

 

.graphqlconfig.ymlを使った修正

.graphqlconfig.yml ファイルについて、公式ドキュメントでは

When a project is configured to generate code with codegen, it stores all the configuration .graphqlconfig.yml file in the root folder of your project.

https://aws-amplify.github.io/docs/cli-toolchain/graphql#codegen

と記載されています。

ただ、どの項目を修正することで保管場所が変更になるか分からなかったため、いろいろと試してみました。

 

queries.json/mutations.json/subscriptions.jsonの移動

GraphQLに対応したファイル

を移動するには、項目 docsFilePath を修正します。

試しに、docsFilePathsrc_2nd/graphql へと変更し、 schema.graphql を修正した後、 amplify push してみました。

$ amplify push

Current Environment: dev

| Category | Resource name    | Operation | Provider plugin   |
| -------- | ---------------- | --------- | ----------------- |
| Api      | graphqlconfigApi | Update    | awscloudformation |
? Are you sure you want to continue? Yes

GraphQL schema compiled successfully.

? Do you want to update code for your updated GraphQL API Yes
? Do you want to generate GraphQL statements (queries, mutations and subscription) based on your schema types? This will overwrite your current graphql queries, mutations and subs
criptions Yes

...
✔ Downloaded the schema
✔ Generated GraphQL operations successfully and saved at src_2nd/graphql
✔ All resources are updated in the cloud

 
すると、

の3ファイルが src_2nd/graphql の中に作成されました。
https://github.com/thinkAmi-sandbox/AWS_AppSync_Amplify-sample/commit/d4658252211362d35296110fdef8b19b507d4e08#diff-65fd0b65d8da646fd955c3e7deaee9b8

 

shema.jsonの移動

shema.json の場合は、項目 schemaPath を修正します。あとは amplify push することで、新しい場所に作成されます。
https://github.com/thinkAmi-sandbox/AWS_AppSync_Amplify-sample/commit/6efb091d1a9d2891cccefe56565c6e05a99019c4#diff-65fd0b65d8da646fd955c3e7deaee9b8

 
ちなみに、 Amplify CLIのバージョンが古い場合はschema.jsonの移動はできません。

 
もし移動したい場合は、最新のAmplify CLIにアップデートする必要があります(手元だと、1.8.5で移動可能でした)。

 

ソースコード

GitHubに上げました。 graphqlconfig_yml ディレクトリの中が今回のファイルです。
https://github.com/thinkAmi-sandbox/AWS_AppSync_Amplify-sample

AWS AppSync + Amplifyで、Subscriptionに引数を追加し、一部のみ通知を受け取ってみた

AWS AppSyncでは、Subscriptionを使うことでMutationに対する通知を受け取ることができます。
リアルタイムデータ - AWS AppSync

また、AWS AppSyncにAWS Amplifyを組み合わせた場合でも、Amplify FrameworkのSubscriptionが使えます。
Subscription | AWS Amplify | API

 
ドキュメントを読んでいたところ、Subscriptionに引数を追加することで、一部のMutationに対してのみ通知を受け取ることもできそうでした。

そこで、AWS AppSync + Amplifyで試してみたため、その時のメモを残します。

 
目次

 

環境

 

Schemaと確認内容

今回用意したAmplifyの schema.graphql はこちらです。

type Blog
        @model(queries: {get: "getBlog"}, mutations: {create: "createBlog"},
            subscriptions: { onCreate: [
                "none",
                "titleOnly(title: String!)",
                "all(title: String!, content: String)",
            ]})
        @key(fields: ["title"]) {
    title: String!
    content: String
}

 
これをデプロイすると、AppSync Console上のSchemaは以下のような感じになります。

type Blog {
    title: String!
    content: String
}

input CreateBlogInput {
    title: String!
    content: String
}

type Mutation {
    createBlog(input: CreateBlogInput!): Blog
}

type Query {
    getBlog(title: String!): Blog
}

type Subscription {
    none: Blog
        @aws_subscribe(mutations: ["createBlog"])
    titleOnly(title: String!): Blog
        @aws_subscribe(mutations: ["createBlog"])
    all(title: String!, content: String): Blog
        @aws_subscribe(mutations: ["createBlog"])
}

 
今回は、このSchemaに対し、3つのSubscription

  • none (引数なし)
  • titleOnly (キーtitleのみの引数)
  • all (キーtitle、およびそれ以外の項目(content)も引数に含む)

を実行した後にMutation createBlog を実行することで、各Subscriptionがどのように動作するかを確認します。

 
動作確認用の環境として、以下の index.html と src/app.js を用意しました。

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Amplify Framework</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div class="app">
  <div>フィルターなし
    <button id="none">Non Filter</button>
  </div>
  <div>タイトルだけ (title=foo)
    <button id="title_only">Title Only</button>
  </div>
  <div>すべて (title=foo, content=bar)
    <button id="all">All Filter</button>
  </div>


  <div>データ作成
    <label for="title">タイトル</label>
    <input type="text" id="title" placeholder="my title" />
    <label for="content">内容</label>
    <input type="text" id="content" placeholder="my content" />
  </div>

  <div>
    <button id="create">Create</button>
  </div>


</div>
<script src="main.bundle.js"></script>
</body>
</html>

 

app.js

subscriptionを実行する時の引数は、それぞれ以下を指定しました。

subscriptionsのメソッド 引数
none なし
titleOnly title = 'foo'
all title = 'foo' , content = 'bar'

また、一度通知を受け取ったら再度受け取らないよう、 unsubscribe() メソッドを使って通知を中止しています。
https://aws-amplify.github.io/docs/js/api#subscriptions

 

import Amplify, { API } from 'aws-amplify';
import awsconfig from './aws-exports';
import * as mutations from "./graphql/mutations";
import * as subscriptions from "./graphql/subscriptions";


// データ作成
const creteButton = document.getElementById('create');
creteButton.addEventListener('click', () => {
  Amplify.configure(awsconfig);
  API.graphql(
    {
      query: mutations.createBlog,
      authMode: 'API_KEY',
      variables: {
        input: {
          title: document.getElementById('title').value,
          content: document.getElementById('content').value,
        }
      }
    }
  ).then((data) => {
    console.log('created: target data.');
    console.log(data);
  })
    .catch((e) => {
      console.log('mutation error');
      console.log(e)
    });
});


// --------------------------
// Subscriptions
// --------------------------

// non filter
const nonFilterButton = document.getElementById('none');
nonFilterButton.addEventListener('click', () => {
  Amplify.configure(awsconfig);

  console.log('subscribe(none): start...');

  const client = API.graphql(
    {
      query: subscriptions.none,
      authMode: 'API_KEY',
    }
  ).subscribe({
    next: (data) => {
      console.log('subscribed: none');
      console.log(data);
      // 一回通知を受け取ったら、それ以降は受け取らないようにする
      client.unsubscribe();
      console.log('finish: none');
    },
    error: (err) => {
      console.log(err);
      client.unsubscribe();
      console.log('sub error (none)');
    },
    close: () => console.log('sub done')
  })
});


// title filter
const titleFilterButton = document.getElementById('title_only');
titleFilterButton.addEventListener('click', () => {
  Amplify.configure(awsconfig);

  console.log('subscribe(title): start...');

  const client = API.graphql(
    {
      query: subscriptions.titleOnly,
      authMode: 'API_KEY',
      variables: {
        title: "foo"
      }
    }
  ).subscribe({
    next: (data) => {
      console.log('subscribed: title');
      console.log(data);
      client.unsubscribe();
      console.log('finish: title');
    },
    error: (err) => {
      console.log(err);
      client.unsubscribe();
      console.log('sub error (title)');
    },
    close: () => console.log('sub done')
  })
});


const allButton = document.getElementById('all');
allButton.addEventListener('click', () => {
  Amplify.configure(awsconfig);

  console.log('subscribe(all): start...');

  const client = API.graphql(
    {
      query: subscriptions.all,
      authMode: 'API_KEY',
      variables: {
        title: "foo",
        content: "bar"
      }
    }
  ).subscribe({
    next: (data) => {
      console.log('subscribed: all');
      console.log(data);
      client.unsubscribe();
      console.log('finish: all');
    },
    error: (err) => {
      console.log(err);
      client.unsubscribe();
      console.log('sub error (all)');
    },
    close: () => console.log('sub done')
  })
});

 

動作確認

どのSubscriptionの引数にも一致しないtitleのMutationを実行

title = 'ham' , content = 'spam' のMutationを実行した時は

  • 引数なしの none()

のSubscriptionで通知を受け取りました。

f:id:thinkAmi:20190721204206p:plain:w450

 

一部の引数ありのSubscriptionに一致する、titleが "foo" のMutationを実行

title = 'foo' , content = 'spam' のMutationを実行した時は、

  • 引数なしの none()
  • 引数 title = 'foo' を設定した titleOnly()

の2つのSubscriptionで通知を受け取りました。

f:id:thinkAmi:20190721204836p:plain:w450

 

引数ありのSubscriptionすべてに一致する、titleが "foo" & contentが "bar" のMutationを実行

title = 'foo' , content = 'bar' のMutationを実行した時は、すべてのSubscriptionで通知を受け取りました。

f:id:thinkAmi:20190721205009p:plain:w450

 

まとめ

以上より、Mutationでの更新内容とSubscriptionの引数値がすべて一致した場合に、通知を受け取れるようでした。

例えば

const client = API.graphql(
  {
    query: subscriptions.all,
    authMode: 'API_KEY',
    variables: {
      title: "foo",
      content: "bar"
    }
  }
)

は、title == "foo" && content == "bar" のMutationのみ通知を受け取るようです。

 

ソースコード

Githubに上げました。 filter_subscription ディレクトリ以下が今回のファイルです。
https://github.com/thinkAmi-sandbox/AWS_AppSync_Amplify-sample