【Django】モデルの紐づけ(ManyToManyField 編)


こんにちは、生涯腰痛持ちのゴマ太郎です。
前回の記事を書いた後、初めてのギックリ腰を経験しました。ギックリなんて痛いだけやん、我慢すれば普通に生活できるやんと舐めていた過去の自分はどこかへ消え去りました。
案件が忙しくなってしまい前回の Django モデルの ForeignKey から半年以上も空いてしまいましたが、今回はそのお仲間の ManyToManyField について解説していきます。

1. ManyToManyField とは

1-1. モデルを紐づけるリレーションフィールド

前回の ForeignKey 編では Django モデルを紐づけるフィールドの例を下表で紹介しました。

フィールド名関係性使用例
ForeignKey一対多学科 - 学科の在籍学生
ManyToManyField多対多講義 - 講義の履修学生
OneToOneField一対一学生 - 学生のロッカー番号

1-2. ManyToManyField は「多対多」の関係を持たせる

ManyToManyField の「多対多」が何を意味するのか説明します。
前節では「講義 - 講義の履修学生」という例を挙げました。



上図が ManyToManyField の例です。「多対多」というと互いが多くのオブジェクトに紐づく「 2 以上 対 2 以上」の関係と思いがちですが、実際のところは「 0 以上 対 0 以上」のような、互いに紐づくオブジェクト個数に制限がない関係を指します。

ここからは実際に「講義 - 講義の履修学生」の例で ManyToManyField の使用方法を紹介していきます。

2. 前提条件

2-1. 環境

  • Ubuntu 20.04 LTS on WSL2
  • VSCode 1.76.0
  • Python 3.10.8
  • Django 4.1.5

3. ManyToMany の使い方

3-1. models.py での書き方

ManyToManyField を使うには以下のように書きます。

from django.db import models


class Lecture(models.Model):
    name = models.CharField(max_length=30)


class Student(models.Model):
    name = models.CharField(max_length=30)
    lecture = models.ManyToManyField(Lecture, blank=True)


ここで ManyToManyField に関係している記述は以下の行になります。
Student モデルの lecture フィールドに、 Lecture モデルをセットしています。

lecture = models.ManyToManyField(Lecture, blank=True)
フィールド名 = models.ManyToManyField(紐づけたいモデル, blank=True)

ForeignKey では「一対多」のうち【多】のモデルに指定するという規則がありましたが、 ManyToManyField ではどちらのモデルにでも指定が可能です。
ただし ManyToManyField を指定した方とそうでない方で、データの操作方法が変わります。操作方法の違い、指定時の考え方についてはこの後の 4-1 節にて参考記事を紹介しています。


3-2. 管理サイトで確認

確認に必要なコードの追記

ここからは ManyToManyField が追加できていることを管理サイトで確認していきます。
その準備としていくつか追記していきます。

class Lecture(models.Model): # 前節で記述済
    name = models.CharField(max_length=30) # 前節で記述済

    def __str__(self) -> str:
        return self.name

from django.contrib import admin
from .models import Lecture, Student


class LectureAdmin(admin.ModelAdmin):

    list_display = ("name", "_num_of_student")

    # _num_of_student: 講義を履修している学生人数
    def _num_of_student(self, obj):
        return len(Student.objects.filter(lecture=obj))


class StudentAdmin(admin.ModelAdmin):
    list_display = ("name", "_lectures")

    # 履修中の講義名を ", " 区切りで連結
    def _lectures(self, obj):
        return ', '.join(lec.name for lec in obj.lecture.all())


admin.site.register(Lecture, LectureAdmin)
admin.site.register(Student, StudentAdmin)

from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
]

モデルを変更しているためマイグレートも必要です。

python manage.py makemigrations
python manage.py migrate

管理サイト

管理サイト~/admin/にアクセスすると、 LecturesStudents が追加されています。


Lectures に入り、講義を追加します。


NUM OF STUDENT という列がありますが、まだ学生の追加をしていないため全て 0 となっています。


今度は Students に入り、学生を追加します。
学生追加画面には学生の名前に加え、先ほど作成した講義のリストも用意されています。
このリストから 1 つ以上の講義を選択して学生を作成すると、学生と講義が紐づけられることになります。



Lectures に戻ってみると、先ほどは全て 0 だった NUM OF STUDENT が、紐づいている学生の人数に増えているはずです。


以上が ManyToManyField の使用方法になります。

4. おまけ

4-1. どちらに指定すべきか

ManyToManyField のデータを扱うとき、 ManyToManyField を指定したモデル(Student)とそれに紐づけられたモデル(Lecture)でデータの扱い方が異なります。

こちらの記事で分かりやすく解説されていましたので、どちらのモデルに ManyToManyField を指定するべきか迷った際には参考にしてみてください。

realizeznsg.hatenablog.com

4-2. 中間テーブルを自作する

前章のやり方で ManyToManyField によってモデル同士を紐づけると、裏側の DB では中間テーブルを自動作成してくれています。この中間テーブルが持つのは紐づけに最低限必要なフィールド(PK / 学生ID / 講義ID)だけです。もし独自のフィールドも追加したいといった場合には through オプションにより中間テーブルを自作することができます。

through オプションで中間テーブルを自作する方法はこちらの記事で解説されています。

djangobrothers.com

5. 最後に

今回は Django での ManyToManyField の実装方法を解説しました。説明していない内容もまだまだありますので、気になった方は Django 公式ドキュメント をご確認ください。

ここまで読んでいただきありがとうございました。
次回 OneToOneField 編も 書くことがありましたら(?) よろしくお願いいたします。お疲れ様でした!

6. 参考

電子処方箋から考える 高齢者向けUIデザインのポイント

2023年1月から、日本でも電子処方箋が運用開始されました。
電子処方箋は、これまで紙で発行されていた処方箋をデジタルデータに移行するものです。

1.電子化のメリット

これにより、医療機関側は正確な処方・調剤データを参照できるようになります。
さらに、他の医療機関とデータが共有できることで重複投薬や併用禁忌(飲み合わせの問題)が発生する可能性も低下し、患者にも恩恵があります。

2.電子化の懸念材料

一方で電子化による懸念材料もあり、その一つは高齢の患者です。
薬の処方は高齢者のほうが多くなる傾向にありますが、同時にデジタル機器を使いこなしている人の割合が少なくなります。
また、医療関連のシステムは身体的にハンデを負う人も使用することになるため、誰にでも使いやすくする工夫が求められます。

3.UI設計時に参考にすべき点

そういった背景を持つ製品から、参考にすべき点を見ていきたいと思います。

(1) 顔認証付きカードリーダーのUI

では、病院や薬局で見かける顔認証付きカードリーダーのUIを見て、どのような構成になっているかを紐解いてみます。
※画像の使用許可を得る方法が不明だったため、文章のみであることをご容赦ください。

  • 文字は新聞紙の文字サイズより大きい
    文字サイズの適正値は個人の視力に依存するので判断が難しい部分ではありますが、最低でも新聞記事の文字サイズが必要だと考えます。
    これは、新聞紙は高齢者にもなじみ深いという考えに基づきます。

  • 白背景と濃い目の文字色の2色構成(一部反転あり)
    配色は基本的に白地に濃い文字色です。
    色分けによる強調表現は使われていません。
    一部を赤色の文字などで強調したくなりがちですが、色覚障がい者にとって見づらくなることもあるので、不用意に使うのは避けるべきです。
    色覚に限らず障がい者の視点は漏れやすく、注意が必要です。

  • 文章は一般的な日本語のみで表現されている
    IT用語は一切なく、スマホでは当たり前に見る「タッチ」も出てきません。

  • 画面内の文字数:多いもので50文字程度
    行数も長くて5行程度です。

  • 操作は進む選択肢、一部テンキー入力のみ
    画面でのキャンセルや戻る操作はないようです。
    戻る操作は慣れている人には便利であるものの、そうでない人には何をしているかわからなくなることがあります。

  • カードの取り出しでキャンセル
    キャンセルとしての選択肢は、カードの取り出しで可能です。
    間違えたりしたときはとにかくカードを取り出してしまえば解決します。

  • カード取り忘れ通知あり
    完了後のカード取り忘れ時には、画面とアラームで知らせるサポートがあります。
    「うっかりは起こるもの」という前提は重要です。

(2) 優れたUIとは

前述の内容から、優れたUIについてまとめます。

①テキストやアイコンの大きさ、コントラストが十分であること

高齢になると視力の低下が起こります。そのため、文字の大きさの設計はとても重要です。

また、文字色と背景色の色相差や明度差などの視覚的な見やすさを考慮することも必要です。

②明瞭な表現

短く簡潔にすることはもちろんですが、IT用語を避けることでデジタル機器に不慣れな人にも伝わりやすくなることにつながります。

一つ一つの表現に注意が必要です。

③操作の容易さとサポート機能の組み込み

操作できる範囲が不明瞭であったり、何をすればいいのかわからない状況にならないことが重要です。

操作をサポートする機能についても、利用者の立場に立った設計が必要です。

④ハンデを持つ人への考慮

高齢者のみならず、障がい者などのさまざまな人に対して使い勝手のよい構成を考える必要があります。

おわりに

今回はシンプルな製品のUIを見たため、あまり複雑な画面こそ出てきませんでした。
ただ、そもそも複雑な画面の時点で誰にでも使いやすいものになるはずがないので、情報量を必要最小限に絞り込むことがまず第一です。
そのうえで、「優れたUIとは」の内容を押さえた設計であることが重要と考えます。
システムエンジニアの端くれとして、常にこれを念頭に取り組んでいきたく思います。

PySimpleGUIで画面作成

みなさん、こんにちは。入社1年目のsiです。

私はイノベーションLABという部署に配属され、Pythonの研修を受けました。その後、画面を備えた簡易的なツールの開発が必要となったため、学んだPythonを使って作成しようとPySimpleGUIに挑戦しました。本記事では、その際に調べた基本的な画面の作成方法について書いていきます。


1. PySimpleGUIとは

PythonのGUIの1つです。以下のコマンドでインストールして使用しました。

$ python -m pip install PySimpleGUI


readmeよりPySimpleGUIについて以下のように書かれています。

PySimpleGUIはあらゆるレベルのPythonプログラマがGUIを作成できるようにするPythonパッケージです。ウィジェットを含む「レイアウト」を使用して、GUIウィンドウを指定します(PySimpleGUIでは「エレメント」と呼びます)。

使用するプログラムやフレームワークにもよりますがPySimpleGUIでのプログラムはフレームワークのいずれかを直接使用して同じウィンドウを作成するよりも、コードの量は1/2から1/10程度になる場合があります。

日本語に翻訳されたreadmeファイルもあり、他のGUIであるtkinterやQtよりも初期のハードルが低いと考えられています。


2. 前提条件

  • VSCode - 1.83.1
  • Ubuntu - 22.04.3 LTS
  • Python - 3.11.3
  • pip - 23.2.1
  • PySimpleGUI - 4.60.5

3. 画面作成

3-1. 基本の流れ

レイアウトを作成する
ウィンドウを作成する
イベントループを作成する
ウィンドウを閉じる

import PySimpleGUI as sg

# レイアウトの作成
layout = [[ ]]

# ウィンドウの作成
window = sg.Window('Title', layout, size=(500, 200))

# イベントループの作成
while True:
    event, values = window.read()
    if event == sg.WIN_CLOSED:
        break

# ウィンドウを閉じる
window.close()
上記のコードで作成される画面


以下で順番に説明していきます。

3-2. レイアウトの作成

layout = [
            [sg.Text("1. "), sg.Input(key=1)],
            [sg.Text("2. "), sg.Input(key=2)],
            [sg.Text("3. "), sg.Input(key=3)],
            [sg.Text("4. "), sg.Input(key=4)],
            [sg.Text("5. "), sg.Input(key=5)],
            [sg.Button('ok')]
         ]



2次元配列で配置を指定していきます。
内側の配列1つで1行分のレイアウトを表し、カンマ(,)で区切ることで要素を横に配置していきます。要素を縦に並べる場合は、もう1つ配列をつくります。


要素をいくつか紹介します。HTMLで使用するような、一般的な要素は揃っていると思います。

  • sg.Text:テキストの表示
  • sg.Input:一行のテキスト入力欄
  • sg.Multiline:複数行のテキスト入出力
  • sg.Listbox:指定された値が一覧表示され、その中から選択
  • sg.Radio:同じgroup_idで指定された複数の選択肢から1つを選択
  • sg.Checkbox:はい/いいえの二者択一
  • sg.Spin:矢印を使用して選択するリストボックス
  • sg.Button:何らかのイベントを実行

3-3. ウインドウの作成

window = sg.Window('Title', layout, size=(100, 500))

タイトルバーに表示させる文字列とレイアウトを引数に指定します。
また、“size=(横, 縦)”で開くウィンドウサイズを指定することができます。

3-4. イベントループの作成

while True:
    event, values = window.read()
    if event == sg.WIN_CLOSED:
        break

“while True”のループの中で条件式を提示します。
イベントはボタン要素や“enable_event=True”と指定した要素を押したときに実行されます。


ボタン要素の場合、①ボタンのテキスト、②sg.OK( )やsg.Cancel( )といったショートカット、③keyで指定した文字列のいずれかでイベントが実行します。

layout = [[sg.Button('ok'), sg.OK(), sg.Button('-ok-', key='-ok-')]]
while True:
    event, values = window.read()
    if event == sg.WIN_CLOSED:
        break
    if event in ('ok', 'OK', '-ok-'):
        print(event)



“enable_event=True”の場合は、keyで指定した文字列によってイベントを実行させます。


if文の処理を“break”とすると、イベントループの外に出て、ウィンドウが閉じられます。
“sg.WIN_CLOSED”はタイトルバーにある“×”が押された時を表します。代わりに“None”を記述しても同じ動作になります。

3-5. ウィンドウを閉じる

window.close()

終了処理です。ファイル処理と同様、開いて読みこんだウィンドウを"close( )"して終わります。

4. 最後に

レイアウトに2次元配列を使用することで、ソースコード内でも視覚的に配置できるため、感覚的に画面を作成できると思います。FrameやColumnといった要素を使って、複数の要素をグループ化して組み込むことも可能です。
PySimpleGUIのページには、YouTubeの再生リストもあるので、試す際には参照してみてください。
最後までご覧いただきありがとうございます。


5. 参考

www.pysimplegui.org
github.com
PySimpleGUI 2020-2022 Edited (to save you time) Versions - YouTube





WSL2でディストリビューションの名前やファイルの場所を変更する方法


みなさん、こんにちは。イノベーションLABのハヤシです。
オフィスに出社する際にお土産を持ってくると「野球観に行ってきたんですか?」と聞かれます。
だいたい合ってます。聞かれたいのでお土産を買っているところがあります。
夏の甲子園が終わっても、秋の大学野球に、神宮大会。まだまだ楽しみは尽きません。


さて、今回は開発環境構築についてのお話です。


以前、 GitBash での環境構築についての記事を書きました。
blog.css-net.co.jp

最近は、WSL(Windows Subsystem for Linux) 2 が安定してきたのもあってすっかり WSL2 がメイン開発環境になっています。
そこで、今回はすでに WSL2 を活用している方向けに、ディストリビューションの名前や場所を変更する方法についてご紹介します。


このように困っている方の助けになれば幸いです。

  • 最初にディストリビューションをインストールした後、同じもので別環境を作りたいけど名前を変えてインストールできない...!
  • プロジェクトごとのディストリビューションに名前を付けて管理したい
  • ディストリビューションの設定を他のメンバーに共有したい、別のマシンに引っ越したい
  • WSL2 がストレージ容量を圧迫しているので別ドライブに移動させたい


WSL2 自体の環境構築についてもおいおいご紹介できればと思っていますので、あくまで今回の対象は「すでに WSL2 の環境構築が終わっている方」となります。
WSL2 について知りたい方については、こちらをご参照ください。
learn.microsoft.com

1. 前提条件

  • OS - Windows 10
  • WSL(Windows Subsystem for Linux) 2
    • 変更するために何らかのディストリビューションがインストールされていること

※手順の中に出てくる「<>」は、「<>」も含めて指定のものに置換してください。
例)

wsl -t <ディストリビューション名>
# ディストリビューション名が「Ubuntu」の場合は以下のコマンドに置き換える
# wsl -t Ubuntu

2. 手順

2-1. 事前確認

「Windows + x」でメニューを開き、「WindowsPowerShell(管理者)」をクリックして PowerShell を開きましょう。
PowerShell で作業します。

以下のコマンドで、これらを確認しておきましょう。

  • 対象のディストリビューション名
    • ここでは「Ubuntu」
  • 停止しているかどうか
    • 「Stopped」になっていれば停止しています。
wsl -l -v

もしステータスが「Stopped」ではなく「Running」などになっている場合は、以下のコマンドで停止しておきましょう。
※VSCode などで対象のディストリビューションに接続していると起動してしまうので、閉じておきましょう

# 対象のディストリビューションを停止
wsl -t <ディストリビューション名>

2-2. 実施の流れと情報整理

以下の手順で名前と場所変更を行っていきます。

  • ディストリビューションを tar ファイルにエクスポート
  • ディストリビューションを登録解除(削除)
  • エクスポートしたファイルを新しい名前と場所でインポート


実施する前に、以下を決めておきましょう。

  • エクスポートする tar ファイルのパス
  • 新しく設定するディストリビューションの名前
  • 新しくファイルシステムを配置したいディレクトリ
    • WSL2 での作業ファイルが保存されていくので、十分な容量が確保できる場所にしましょう


今回はこれで設定していきます。

  • エクスポートする tar ファイルのパス
    • ⇒ C:\wsl\Ubuntu.tar
    • ※tar ファイルの名前は元のディストリビューションの名前と一致している必要はありません
    • C:\wsl ディレクトリはあらかじめ作成しておきます
  • 新しく設定するディストリビューションの名前
    • ⇒ SampleUbuntu
  • 新しくファイルシステムを配置したいディレクトリ
    • ⇒ C:\wsl\SampleUbuntu
    • ※ディストリビューションの名前と一致している必要はありませんが、一致しているとわかりやすくなります

2-3. エクスポート

それではすすめていきます。
以下のコマンドで、元のディストリビューションをエクスポートします。

# ファイルにエクスポート
wsl --export <元のディストリビューション名> <エクスポートする tar ファイルのパス>

# 今回の例なら以下のようになります
# wsl --export Ubuntu C:\wsl\Ubuntu.tar

少し時間がかかりますので、完了まで待ちましょう。

エクスポートが完了していることを確認します。

2-4. 旧ディストリビューションの登録解除

以下のコマンドで元の環境を登録解除します。

wsl --unregister <元のディストリビューション名>

# 今回の例なら以下のようになります
# wsl --unregister Ubuntu

以下のコマンドで、一覧から消えていることを確認します。

wsl -l -v


2-5. インポートと確認

以下のコマンドでエクスポートしたファイルを新しい名前・場所でインポートします。

wsl --import <新しいディストリビューション名> <ファイルシステムを配置したいディレクトリ> <tar ファイルパス>

# 今回の例なら以下のようになります
# wsl --import SampleUbuntu C:\wsl\SampleUbuntu C:\wsl\Ubuntu.tar

以下のコマンドで作成されていることを確認します。

wsl -l -v

指定したディレクトリにファイルが作成されていることを確認します。

VSCode などから接続できることを確認しましょう。

※PowerShell から、以下のコマンドでも確認できます

wsl -d <新しいディストリビューション名>

# 今回の例なら以下のようになります
# wsl -d SampleUbuntu


インポートできたことが確認出来たら、エクスポートした tar ファイルは不要ですので削除して問題ありません。

2-6. ファイルシステムの配置場所を後から確認する方法

もしファイルシステムを配置しているディレクトリがわからなくなってしまったら、以下のコマンドでレジストリから確認することができます。

Get-ItemProperty -Path Registry::HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss\*

「DistributionName」が対象のディストリビューション名のセクションの、「BasePath」で確認できます。

2-7. 補足

同じ設定で違う名前のディストリビューションを立ち上げたい場合は、 tar ファイルからまた別の名前でインポートすることができます。
基本的な設定が終わった状態の tar ファイルを保存しておいて活用するのもよいですね。


また、この手順を使用すると、 Microsoft Store にないディストリビューションでも tar ファイルを入手すれば自身の WSL2 上に環境を作成することができます。

3. 最後に

手順をまとめます。

# 対象のディストリビューションが停止していることを確認しておく

# ファイルにエクスポート
wsl --export <元のディストリビューション名> <エクスポートする tar ファイルのパス>

# 元のディストリビューションの登録解除
wsl --unregister <元のディストリビューション名>

# 新しい名前でインポート
wsl --import <新しいディストリビューション名> <ファイルシステムを配置したいディレクトリ> <tar ファイルパス>

以上です。

新しくディストリビューションをインストールしたら、わかりやすい名前と場所に変更しておく癖をつけておくとよいでしょう。

それでは。よいエンジニアライフを。

4. 参考


Ruby on RailsでのStripe実装 後編 - 決済処理


こんにちは、イノベーションLABのM.Mです。
今回はQubeで導入したクレジット決済機能のStripeについて記事にしていきたいと思います。
後編では、Stripeを使用したクレジットカードと銀行振込での決済処理について解説します。


前編 - アカウント作成 はこちらです⇩
blog.css-net.co.jp


StripeConnectついて

StripeConnectとは

StripeConnectとは、Connectアカウントと呼ばれる子アカウントを作成し、子アカウントの支払いの管理や、手数料の回収、資金の分配などを行うことができるサービスです。

上図では子アカウントにのみ顧客を紐づけていますが、親アカウントに直接顧客を紐づけることも可能です。

Connectアカウントについて

Connectアカウントには下記の3種類があります。

  • Standardアカウント

ユーザ自身がStripeのフォーム上でアカウントを作成したり、ダッシュボードで支払いを管理することができます。
組み込みが簡単ですが、カスタマイズ性は低いです。

  • Expressアカウント

子アカウントの設定等、全てではありませんがカスタマイズできます。
組み込みやすさはStandardとCustomの中間程度になります。

  • Customアカウント

子アカウントの登録や設定などがアカウント所有者(売り手)にはほぼ見えない状態で、全てをカスタマイズできます。
そのためカスタマイズ性は高いですが、他2つと比較すると組み込みが大変になります。

Qubeでは「Standard」アカウントを利用しているので、ここからは「Standard」アカウントでの組み込みについてご紹介します。

Stripeを利用するための設定

①Gemfileに下記を追加して「bundle install」を実行します。

  # Stripeアカウント作成
  gem "stripe"


②開発者画面のAPIキーのタブを選択し、公開キーとシークレットキーを確認します。
※検証用の場合は画面上の「テスト環境」というスイッチをオンにしてください。



本番用のキーは「pk_live」、テスト用のキーは「pk_test」から始まります。

③config/initializers配下にstripe.rbというファイルを作成し、そこにシークレットキーを設定します。
※シークレットキーを「secret_key」としています

  # Stripeシークレットキー設定
  Stripe.api_key = secret_key


④application.html.slimのhead部分にStripe.jsを読み込むための設定を追加します。

  / Stripeサーバ通信用
  script src="https://js.stripe.com/v3/"

子アカウント(売り手)の登録方法

Stripeアカウント登録画面へのURLを作成し、リダイレクトさせるアクションを作成します。

  def create_stripe_account
    # Stripeアカウント作成
    account = Stripe::Account.create(
       type: "standard"
    )
    # アカウントリンク作成
    url = Stripe::AccountLink.create(
       account: account.id,
       refresh_url: public_root_url,
       return_url: complete_url,
       type: "account_onboarding"
    )
    redirect_to url.url
  end
  • refresh_url:アカウントリンクの有効期限が切れているなどの理由で無効な場合に、ユーザーがリダイレクトされるURL
  • return_url:リンクされたフローを終了・完了するときに、ユーザーがリダイレクトされるURL


登録したいタイミングでこのアクションを呼び出すと、こちらの画面に遷移します。

ここから前編の「Stripeアカウント登録」でご紹介した項目を登録していきます。
※子アカウントも支払いを受ける場合は、企業情報や個人情報、身分証明書等の情報が必要になります。

顧客(買い手)のカード情報登録

StripeConnectを利用した場合、子アカウントに対しての支払いは、そのアカウントに紐づく顧客でなければ行えません。
したがって、下図の場合、顧客Aは「子アカウント2」に支払うことができません。

顧客Aが「子アカウント2」に支払いたい場合は、「子アカウント2」の方にも顧客Aを紐づける必要があります。


Qubeでは親アカウントにカード情報を登録し、それを子アカウントに複製することで、一つのカード情報で全ての子アカウントで支払いを行えるような仕組みにしています。
今回はその場合の登録処理をご紹介します。

親アカウントへの登録

カード情報の親アカウントへの登録については、こちらを参考にしました。
zenn.dev

子アカウントへの複製

  # カードトークンを作成する
  card_token = Stripe::Token.create(
     { customer: "#{顧客のStripeアカウントID}" },
     stripe_account: "#{子アカウントのStripeアカウントID}"
  )
  # 子アカウントの顧客としてアカウントを登録する
  Stripe::Customer.create(
     { source: card_token.id,
       name: "#{顧客の名前}",
       email: "#{顧客のメールアドレス}",
       preferred_locales: ["ja"], },
       stripe_account: "#{子アカウントのStripeアカウントID}"
  )
  • 顧客のStripeアカウントIDは「cus_」から始まるIDになります。
  • 子アカウントのStripeアカウントIDは「acct_」から始まるIDになります。

決済処理

Stripeには、顧客が子アカウントに対して決済を行い、そこから指定の金額を親アカウントへ送金することができる「DirectCharge」という決済方法があります。
今回はその仕組みを利用し、顧客が子アカウントに対して利用料1,100円を支払い、そのうち100円を手数料として親アカウントに支払うことを想定した処理をご紹介します。

クレジットカード

  Stripe::PaymentIntent.create({
     amount: 1100,
     currency: "jpy",
     customer: "#{顧客のStripeアカウントID}",
     application_fee_amount: 100,
     description: "会議室利用料",
     payment_method_types: ["card"],
     confirm: true,
  },
  stripe_account: "#{子アカウントのStripeアカウントID}")
  • amount:支払額(50円以上)
  • currency:通貨
  • application_fee_amount:親アカウントに送金される額
  • description:説明(オブジェクトに付加される任意の文字列)
  • payment_mathod_types:支払い方法
  • confirm:PaymentIntentの作成と確認を同時に行う

銀行振込

  Stripe::PaymentIntent.create({
     amount: 1100,
     currency: "jpy",
     customer: "#{顧客のStripeアカウントID}",
     application_fee_amount: 100,
     description: "会議室利用料",
     payment_method_types: ["customer_balance"],
     payment_method_data: {
        type: "customer_balance",
     },
     payment_method_options: {
        customer_balance: {
           funding_type: "bank_transfer",
           bank_transfer: {
              type: "jp_bank_transfer",
           },
        },
     },
     confirm: true,
  },
  stripe_account: "#{子アカウントのStripeアカウントID}")
  • customer_balance:顧客残高の支払い方法オプションに関する詳細
  • funding_type:顧客残高に十分な資金がない場合に使用される資金調達方法のタイプ
  • bank_transfer:銀行振込タイプ

決済キャンセル

  Stripe::PaymentIntent.retrieve("#{Stripeの支払ID}", stripe_account: "#{子アカウントのStripeアカウントID}").cancel
  • Stripeの支払IDは「pi_」から始まるIDになります。

テスト手順

①「Ruby on RailsでのStripe実装 前編 - Stripeアカウントの作成 - シー・エス・エス イノベーションラボ(ブログ)」で紹介したテスト用の公開キーとシークレットキーを設定します。

②実際に作成した画面から決済を行います。
 クレジットカード決済のテストでは、カード情報として下記を入力してテストしてください。
 ・ カード番号:下記URLの「ブランド別のカード」のカード番号
        Test cards | Stripe のドキュメント
 ・ 有効期限:将来の有効な日付
 ・ その他の項目:任意の値

③下記手順でStripeダッシュボード上から結果を確認します。
 ・「テスト環境」の状態に切り替え、Connectから指定の子アカウントを選択します。

 ・「支払い」を選択します。

最後に

以上がRuby on RailsでStripeのStripeConnectを利用した場合の、子アカウント登録~テストまでの大まかな実装方法になります。
開発中は、Stripeのドキュメントや開発方法をまとめたサイト以外に、Stripeのヘルプサポートチャットにも大変お世話になりました。
9時から18時が日本語対応で、18時以降は英語対応になるようです。チャット以外にも電話やメールでのサポートもあるようなので、これらを活用するのも良いと思います。
今回ご紹介した機能はstripeのごく一部の機能ですが、少しでもお役に立てば幸いです。
お読みいただきありがとうございました。

参考

【Django】モデルの紐づけ(ForeignKey 編)

こんにちは、最近腰痛が気になるゴマ太郎です。
本日は Django モデルの ForeignKey について解説します。
ManyToManyFieldについての記事もあります

1. ForeignKey とは

1-1. モデルを紐づけるリレーションフィールド

ForeignKey は Django のモデル間を紐づけるリレーションフィールドの一つです。
Django のモデルには以下の3種類のリレーションフィールドが用意されており、それらの違いはモデルデータ間の関係性にあります。
各フィールドが使用される場面の例も【大学】をテーマに挙げてみます。

フィールド名関係性使用例
ForeignKey一対多学科 - 学科の在籍学生
ManyToManyField多対多講義 - 講義の履修学生
OneToOneField一対一学生 - 学生のロッカー番号

ちなみに今回扱うのは ForeignKey だけですが、ManyToManyFieldOneToOneField についても後日解説いたします。

1-2. ForeignKey は「一対多」の関係を持たせる

ForeignKey の「一対多」とは何を意味するのか説明します。
そんなの知っとるわという方は読み飛ばしてください。
前節では「学科 - 学科の在籍学生」という例を挙げました。この場合は学科が【一】、学生が【多】となります。

一対多のイメージ

『学生Aさんの学科は?と聞かれたら回答は必ず一つだけであるのに対して、情報学科の学生は?のように聞かれると回答の該当学生が複数いる。』

言ってしまうとこのようなモデルの関係が「一対多」です。
ちなみに、このとき【多】のほうは「複数になってもよいもの」であって、実際は1つだけもしくは NULL でも問題ありません。
ここからは実際に「学科 - 学科の在籍学生」の例で ForeignKey の使用方法を紹介していきます。

2. 前提条件

2-1. 環境

  • Ubuntu 20.04 LTS on WSL2
  • VSCode 1.76.0
  • Docker Engine 23.0.1
  • PostgreSQL 14.4
  • Python 3.10.8
  • Django 4.1.5

3. ForeignKey の使い方

3-1. models.py での書き方

ForeignKey を使うには以下のように書きます。

from django.db import models


class Department(models.Model):
    name = models.CharField(max_length=30)


class Student(models.Model):
    name = models.CharField(max_length=30)
    department = models.ForeignKey(Department, on_delete=models.CASCADE)

ForeignKey は「一対多」のうち【多】のモデルに作成します。

ここで ForeignKey に関係している記述は以下の行になります。
Student モデルの department フィールドに、 Department モデルをセットしています。
department = models.ForeignKey(Department, on_delete=models.CASCADE)
フィールド名 = models.ForeignKey(紐づけたいモデル, on_delete=このあと紹介します)
これだけで ForeignKey の実装は完了です。

3-2. 管理サイトで確認

確認に必要なコードの追記

ここからは ForeignKey が追加できていることを管理サイトで確認していきます。
その準備としていくつか追記します。

from django.contrib import admin
from .models import Department, Student


class StudentAdmin(admin.ModelAdmin):
    list_display = ('name', 'department')


admin.site.register(Department)
admin.site.register(Student, StudentAdmin)

from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
]

モデルを変更しているためマイグレートも必要です。
python manage.py makemigrations
python manage.py migrate

管理サイト

管理サイト~/admin/にアクセスすると、 Department と Student が追加されています。
管理サイト

学科データをここで登録します。
Departmentを追加

学科の登録が完了すると一覧画面でデータが追加されていることが確認できます。
Department追加後の一覧画面

次に Student に移動し、学生データを登録します。
Studentを追加

学生データ登録画面には、 Name と Department のフィールドがあり、 Department の選択肢に先ほど登録した学科が入っていれば ForeignKey の実装は成功です。
ちなみにプルダウン右側のでも学科を追加することができます。
Studentの登録画面

複数名の学生を同じ学科に紐づけられていることが確認できます。
このようにForeignKey を実装すると、モデル間の「一対多」の紐づけが可能になります。
Student追加後の一覧画面

3-3. on_delete の種類

on_delete 属性は、 ForeignKey などのリレーションフィールドが参照している親(学科)データを削除したい際の、紐づいている子(学生)データへのアクションを指定します。

設定データ削除時の動作
CASCADE削除する学科データに紐づく学生データも削除する。
PROTECT他のデータに紐づいていれば削除できない。
RESTRICT削除できずエラーが表示される。
学科データを削除した際、紐づく学生データは削除されずに残る。
SET_NULL削除される学科データの代わりに NULL がセットされる。
SET_DEFAULT削除される学科データの代わりにデフォルト値がセットされる。
SET()削除される学科データの代わりに指定したデータがセットされる

4. 最後に

今回は Django での ForeignKey の実装方法を解説しました。説明していない内容もまだまだありますので、気になった方は Django 公式ドキュメント をご確認ください。

ここまで読んでいただきありがとうございました。
ManyToManyField 編もありますので、よろしくお願いいたします。お疲れ様でした!

blog.css-net.co.jp

5. 参考


docs.djangoproject.com

Ruby on RailsでのStripe実装 前編 - Stripeアカウントの作成

こんにちは、イノベーションLABのM.Mです。

今回はQubeで導入したクレジット決済機能のStripeについて記事にしていきたいと思います。

今回は前編として、Stripeのアカウント作成手順について解説します。

Stripeとは?

 特徴・仕組み

Stripeとは初期費用や月額利用料が不要で、決済ごとに手数料が加算される仕組みのオンライン決済システムです。                                                        
Stripeを通じて、クレジットカード、銀行振込、ウォレット(googlepay等)、店舗支払いなど複数の決済手段に対応できます。                                                          
アカウント作成後すぐに利用でき、24時間対応のカスタマーサポートがあります。 

 

手数料

決済手数料はクレジットカード決済の場合3.6%、銀行振込の場合1.5%です。

これらの決済手数料は一律であり、支払方法やカード会社による変動はありません。

※端数は四捨五入されます

※決済手数料には消費税は含まれません

 

対応しているクレジットカード

Stripeでは下記のクレジットカードに対応しています。

Visa、Mastercard、American Express、Diners、Discover、Intract、JCB、

China Union Pay

JCB、Diners、Discoverについては別途審査が必要になります。

審査プロセスには約3営業日かかり、この期間はJCBカードの保有者からの決済は受け付けられません。

JCBによる決済の受け付けが承認されると、Diners、Discover保有者の決済も受け付けられるようになります。

 

Stripeアカウント登録

①下記のサイトからメール、氏名、国、パスワードを入力してアカウントを作成します。

https://dashboard.stripe.com/register

②アカウントを作成するとStripeからメールが届くので「メールアドレスを確認」をクリックします。

※検証用の実装(テスト環境のみの利用)の場合はここで終了し、実際に決済を発生させる場合はここら先のアカウントの有効化を行ってください

③「支払いの受け取りを設定する」をクリックしてアカウントの有効化に進みます。


④この画面が表示されるので、ビジネスの確認から順に登録していきます。

登録項目

  • ビジネスの確認

  ◦ ビジネスのタイプ

   ・事業所在地

   ・事業形態

  ◦ 事業詳細

   ・法人番号(検索サイト:https://www.houjin-bangou.nta.go.jp

   ・法人名

   ・事業所在地

   ・ビジネス電話番号

   ・業種

   ・事業のウェブサイト

    ※Stripeを通して販売される商品・サービスの詳細と価格が確認できるURLで

     ある必要があります。

   ・商品、サービス内容の詳細(通常顧客に請求するタイミング を記入する)

  ◦ 申請者の確認

   ・氏名

   ・メールアドレス

   ・役職

   ・生年月日

   ・自宅住所

   ・電話番号

  ◦ 会社の取締役

  ◦ 改正割販法に関連する質問(5問)

  ◦ 公開情報

   ・店舗名、サービス名

   ・短い表記(短い明細書表記は支払いに関する特定の詳細とペアにすることがで

    きる)

   ・お客様問合わせ電話番号

  • 銀行を追加する(Stripeから入金される銀行口座)

  ◦ 銀行口座情報

   ・口座名義

   ・金融機関

   ・支店

   ・口座番号

  • アカウントを安全に保つ(2段階認証の設定)

  ◦ 2段階認証

  • オプションを追加する

  ◦ 税額計算

  ◦ オプションを追加する

 

⑤全ての項目を入力して送信し、画面を再読み込みすると「ご対応が必要です」と表示されるので、そちらをクリックします。

※この時点ではまだ有効化が完了していません

 

⑥下記の手順で身分証明書(運転免許証、パスポートなど)を登録します。

 



※ファイルをアップロードする場合は拡張子が.pjp、.jpg、.pjpeg、.jpeg、.jfif、.pngのいずれかでないとアップロードすることができません。

※複合機でスキャンした画像は審査に通らないので、直接撮影した画像が良いと思います。

 

⑦全ての登録が完了するとStripeから「ビジネスの準備が整いました」というメールが送信され、本番環境で使用できるようになります。

最後に

Qubeへの導入時、アカウント有効化の審査で一度落ちてしまいました。

その際に原因は詳しく書いておらず、問い合わせもしてみたのですが、詳しい内容は公表されないようです。

調査したところ、下記のような注意点があるようなので、このいずれかに引っかかってしまったことが原因かと考えられます。

  • 禁止業種にあたらない(https://stripe.com/jp/legal/restricted-businesses
  • 事業のWebサイトに登録するサイトに商品の詳細と価格、顧客の商品購入方法、特定商取引法に基づく表示を明示する
  • 身分証明書の情報とStripeアカウントの情報を合致させる

これらをふまえ再登録したところ、無事審査に通りました。

後編ではこちらのStripeアカウントを利用した、Ruby on RailsでのStripe実装手順をご紹介したいと思います。

 

後編はこちら

blog.css-net.co.jp

GiteaをDockerで立ててみよう!

はじめに

こんにちは。dazyuです。

ラクダとか貨物を載せて歩いてくれる家畜動物: 駄獣が元ネタです。

最近LAMB/ラムという映画を見ました。
アイスランドが舞台とのことで、内容もさることながら雄大な自然を背景にしたシーン一つ一つが美しく、見て損はなかった作品でした。

今回の投稿が初めてのブログになります。お手柔らかにお願いします。

概要

本稿ではdockerの練習がてら、Giteaというgitサービスをローカルに立てます。

GiteaのイメージとしてはGitHubやGitLabみたいなコード管理サービスです。

要所はgiteaの初期設定とポートフォワーディングです。

ステップtoステップな説明形式です。#6にまとめを載せているのでTL;DRだけ教えろ!みたいな方はそこを参照ください。

  • Giteaのページ

https://gitea.io/en-us/

  • dockerのGiteaサンプルページ

https://docs.docker.com/samples/gitea/

環境

  • Ubuntu 20.04.4 LTS on WSL2
  • VSCode 1.76.0
  • Docker Engine 20.10.18
  • PostgreSQL 14.7
  • Gitea 1.18.5
  • git 2.25.1

前提としてdocker, gitが使える状態です。インストール等は他記事をご参照ください。

WSLを使用していますが、Ubuntu環境なら再現性があると思っています。

What is Gitea

公式ドキュメントから引用すると

Gitea は、コミュニティ主導で開発が進められている Go 言語製の軽量のコードホスティングソリューションです。コードは MIT ライセンスで公開されています。

とのことです…Go製なんですね。個人的ないいねポイントは

  • コミュニティ主導
  • 軽量
  • バイナリ/パッケージ対応
  • MITライセンス
  • docker/vagrant対応

です。

広い範囲のサポートがあり結構取り入れやすいと思うで、公式のDocsの対応一覧だけでも覗いてみてください。

構築するシステムの概説

概要の再掲になりますが、今回はdockerを利用して環境構築をします。

gitea構築に必要なものは「Gitea本体+保存先DB」となります。
(もちろんプロキシサーバーも構成に組み込めますがローカルで動かすだけなので本稿では上記構成です。)

保存先のDBはPostgreSQL, MySQL, SQLite, MSSQLが使用できます。

今回はPostgreSQLで構築します。
(dockerで立てるだけなのでパスワードの設定等凝っていません。)

その次にGiteaを導入します。立ち上げた後になりますが、ポチポチボタンを押しておしまいです。
(こちらも同様にパスワードの設定凝っていません。)

構築作業

ここから実際に構築作業をします。#環境で挙げたようにVSCode上のターミナルで作業しますがエディタ等はお好きなものを使ってください。

1. docker-compose.ymlの作成

プロジェクト用のディレクトリに移動してください。ディレクトリを新たに作るのが良いと思います。

作業するディレクトリまで来たら以下でファイルを作成します。

(ファイル名指定でエディタ立ち上げ)

code docker-compose.yml

※VSCodeを使ってない方は好きなエディタで読み替えてください。

立ち上がりましたら、以下をコピペして保存します。

version: "3"

networks:
  gitea:
    external: false

services:
  server:
    image: gitea/gitea:latest
    container_name: gitea
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - GITEA__database__DB_TYPE=postgres
      - GITEA__database__HOST=db:5432
      - GITEA__database__NAME=gitea
      - GITEA__database__USER=gitea
      - GITEA__database__PASSWD=gitea
    restart: always
    networks:
      - gitea
    volumes:
      - ./gitea:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3000:3000"
      - "222:22"
    depends_on:
      - db

  db:
    image: postgres:14
    restart: always
    environment:
      - POSTGRES_USER=gitea
      - POSTGRES_PASSWORD=gitea
      - POSTGRES_DB=gitea
    networks:
      - gitea
    volumes:
      - ./postgres:/var/lib/postgresql/data
001compose.yml作成

ファイル名はdocker-compose.ymlで保存してください。

2. dockerで環境立ち上げ

環境を立ち上げます。dockerイメージファイルを拾ってくるのでオフラインだとエラー吐くと思います。

docker compose up

-dオプションでバックグラウンドで立ち上げてもいいですがログを見たいので、シェルを一個使って表示させておきます。

002compose upの挙動

スクショのようにaccept connectionしていたら成功です。

3. Giteaの設定

フォームを確認するだけです。

コピペしたdocker-compose.ymlのport項目にあるようにport3000で待ち受けているのでhttp://127.0.0.1:3000(localhost:3000)にアクセスします。

003 Giteaの初期設定

特に何もいじらないで「giteaをインストール」ボタンをクリックしましょう。

004 ログイン画面

サインイン画面に遷移します。初めてなので右上の「登録」ボタンで画面遷移します。

くれぐれもパスワードは忘れないでくださいね。

005 サインアップ画面

登録情報を入力したら「アカウント登録」から次に進みましょう。

006 登録完了

無事登録&ログインできました。

細かい検証はしていませんがここで作ったアカウントがadminユーザーになるはずです。

4. 動作確認

4.1. git clone

実際にgitがhttps方式で使えるか検証します。

リポジトリ作成から。

前節の図006の画面で+ボタンからリポジトリ作成画面に遷移します。

007 画面上でリポジトリ作成

各種項目を埋めます。必須項目だけでいいです。

任意選択でテンプレートのissueラベルが用意されているのはいいですね。

008 リポジトリページ

リポジトリが無事作成されました。サーバーからローカルに通信ができるか確認します。

別のコンソールを開いて先ほどまでの作業ディレクトリ直下にcloneします。

git clone <URL>
009 リポジトリのclone
4.2. git push

適当なファイルを作成します。例としてREADMEとpythonファイル(引数に文字列長*1000yenを返すアプリケーション)を用意しました。

gitが本題なので飛ばしても良いです。README.mdだけ用意しましょう。

010 README.mdの作成
011 Pythonのソースファイル作成
import sys


def main(argv):
    if len(argv) == 2:
        print(f'{len(argv[1]) * 1000} yen')
    else:
        print('one word')


if __name__ == "__main__":
    main(sys.argv)
012 アプリケーションの挙動(任意)

ファイル作成できたらgit add, commit, pushをしましょう。

013 Giteaにpush

VSCodeだと上に入力欄が出てくれるのでGiteaに登録したユーザー名とパスワードをちょちょいと入力して終わりです。

014 push完了確認

コンソール上でpush成功確認できました。

次にブラウザに戻ってリロードしてみましょう。

015 画面からpush確認

ファイルがpushされているのを画面上からも確認できました。(調整したので僕の画像はhash値が変わってしまっていますが気にしないでください)

ここまでで一通りの動作確認ができました。おめでとうございます!

5. ポートフォワード設定

補足の章です。

設定の必要性のところから説明します。

Railsアプリケーションなど開発環境によってデフォルトでport3000を使用する環境があります。
その際、ポートが被ってしまうと立ち上げができなくなってしまうので、(常に立ち上げることになるであろう)gitea側を変更しておこうというものです。

反対にポートが被らない限り変更は不要です。

5.1. docker-compose.ymlのポート変更

ポートを確認します。

docker port gitea
017 使用中のポートの確認

今は内も外も3000ポートが紐づいています。

確認と同時に変更しましょう。#1で作成したdocker-composeファイルのportsの部分を変更します。

services:
  server:
    image: gitea/gitea:latest
    # (省略)
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3030:3000"
      - "222:22"
    depends_on:
      - db

portsの左側が3000から3030に変更されました。

変更を保存したら再起動します。dockerが動作しているコンソールに移動してCtrl+Cで停止後、再度起動します。

docker compose up

元のコンソールに戻ってポートを確認します。

docker port gitea

右側が変更されています。ちゃんとポートが変更されたみたいです。

ブラウザからアクセスします。

019 ポート3000でのアクセスができないことを確認
020 ポート3030でのアクセス

変更後のポートでアクセスができました!おめでとうございます!

…と喜びたいですが、赤枠で「設定ファイルの記述と違うよ」とエラーが出ているので次節で修正しましょう。

5.2. giteaの設定ファイル変更

ROOT_URLの設定をします。

右上の三角アイコン>サイト管理>設定から現在の設定が見れます。

021 ブラウザから設定を確認する

設定ファイルのパスは「/data/gitea/conf/app.ini」みたいです。
ちなみに設定だったり環境変数の一覧はここから確認できます。(https://docs.gitea.io/en-us/config-cheat-sheet/)

このパスにあるapp.iniファイルを編集すれば良いってことですね。

dockerで立ち上げているコンテナ内部に入ります。

giteaのサービス名はserverなのでserver指定でシェルを立ち上げます。

docker compose exec server bash

viでさっくり編集して保存します。(viの使い方は割愛させてください…)

以下のように[server]ROOT_URLのlocalhost後のポート番号のみ3030に変更、保存すればOKです。(ピンクのカーソルの当たっている行です)

022 viで設定編集

最後にbashから抜けるためにexitして作業終了です。

023 docker内からexit

再度ブラウザから確認しましょう。

024 エラーが出ていないことを確認

今度こそポートフォワーディング作業終了です。

6. まとめ

ここまでの手順を振り返り、全部一気に終わらせてしまう方法にまとめたものです。

  1. docker-compose.ymlをコピペして保存
  2. port設定を確認する。
  3. environmentに必要な設定値を追加。(5.2.のROOT_URLをgitea変数同様に指定)
  4. docker compose up
  5. 初期設定(3.の部分)
  6. 完成!gitとしていいように使おう!

7. お片付け

ここまで読んで、作業してくれた方ならばローカルGiteaとともに日々を送っていただけることかと思いますが、やむにやまれぬ事情でPC端末上から消去しなければならない場面がでてくることもありましょう。そんなときのために削除するためのコマンドをご紹介しておきます。

ちゃんとした説明をしたいのですが、削除にあたりdockerのimageだったりcontainerの説明をする必要があり…それだけでいくつもの記事が作れてしまうのでここではケースごとのコマンドだけに留めておきます。



パターン1. 今回作った中身だけ削除したい。またこのPCでdocker使うしDBとか大元のimageは残しておきたい。

  • upの反対、downします。自分がいるディレクトリのcomposeファイルの設定範囲内を削除します。
docker compose down

パターン2. 今回使った中身もダウンロードしてきたimageも消したい。

docker compose down --rmi

パターン3. 設定で会社用のメールアドレスが入れちゃったからDBだけ消し飛ばしたい。

docker compose rm db

おわりに

以上初めてのブログでした。

giteaはwikiも同梱しているので、個人用のリッチなタスク管理、メモツールとしても使えるのではないかなと思います。

量が多くなってしまうので細かい設定までカバーできなかったのが少し心残りでした。

何度見返しても不安ですが、分かりやすい説明になっていれば幸いです。

参考一覧

  • giteaのページ

https://gitea.io/en-us/

  • giteaの環境変数チートシート

https://docs.gitea.io/en-us/config-cheat-sheet/

  • dockerのgiteaサンプルページ

https://docs.docker.com/samples/gitea/

Djangoでメール送信を実装してみる


みなさん、こんにちは。S.Yです。
実際に現場でDjangoでのメール送信を実装する機会があったので実装方法についてまとめました。

前提条件

次の環境で動作させていきます。

  • Visual Studio Code version: 1.76.0
  • python version: 3.9.14
  • django version: 4.1.7

メール送信実装手順

メールサーバーの設定

settings.pyでメールの送信設定を追加します。
settings.py内の EMAIL_HOST と EMAIL_PORT で指定された SMTP ホストとポートを使用して、メールが送信されます。EMAIL_HOST_USER と EMAIL_HOST_PASSWORD が指定されている場合は、SMTP サーバーの認証に使われます。
settings.py

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtpサーバー'
EMAIL_HOST_USER = 'デフォルトのEmailアドレス'
EMAIL_HOST_PASSWORD = 'メールサーバーのパスワード'
EMAIL_PORT = '1025' #メールサーバーで指定されているポート
EMAIL_USE_TLS = False # 送信中の文章を暗号化
DEFAULT_FROM_EMAIL = '任意のメールアドレス'

開発環境でメールをコンソールに表示するには以下のように変更します。

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

メールの送信

send_mail()でメールを送信します。 引数は主に("題名", "本文", "送信元メールアドレス", "宛先メールアドレス")です。
from_emailがNoneの場合、DEFAULT_FROM_EMAIL の値を使用します。
fail_silentlyは入力必須ではありません。Falseの場合、送信エラーが発生した場合に smtplib.SMTPException を発生させます。
views.py

    """題名"""
    subject = "題名"
    """本文"""
    message = "本文"
    """送信元メールアドレス"""
    from_email = "system@example.com"
    """宛先メールアドレス"""
    recipient_list = [
        "to@example.com"
    ]
    
    send_mail(subject, message, from_email, recipient_list, fail_silently=False))

サンプルプログラム

以上のことを踏まえてコンソールにメールの送信内容を表示するプログラムを作成してみます。

Djangoプロジェクト作成

まずはプロジェクトを作成します。

django-admin startproject mysite
cd mysite
python manage.py startapp send_mail

プロジェクト構成

mysite
- mysite
  - __pycache__
  - __init__.py
  - settings.py
  - urls.py
  - wsgi.py
- send_mail 
  - __pycache__
  - migrations
  - templates  # 追加
     - index.html  # 追加
  - admin.py
  - apps.py
  - models.py
  - tests.py
  - urls.py  # 追加
  - views.py
- db.sqlite3
- manage.py

アプリケーションの編集

"INSTALLED_APPS"にsend_mail.apps.AppConfigを追加します。
メールの送信設定はsettings.pyの一番下に追加します。
mysite/mysite/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'send_mail.apps.SendMailConfig', #追加
]

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
EMAIL_HOST = 'localhost'
EMAIL_PORT = 1025
EMAIL_HOST_USER = ''
EMAIL_HOST_PASSWORD = ''
EMAIL_USE_TLS = False
DEFAULT_FROM_EMAIL = 'system@example.com'

templateを作成します。
mysite/send_mail/templates/index.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>success</title>
</head>
<body>
	<h1>send mail complete!</h1>
</body>
</html>

view.pyを修正します。
メールを送信する関数を実装します。
mysite/send_mail/views.py

from django.shortcuts import render
from django.core.mail import send_mail

def index(request):
    """題名"""
    subject = "題名"
    """本文"""
    message = "本文"
    """送信元メールアドレス"""
    from_email = "system@example.com"
    """宛先メールアドレス"""
    recipient_list = [
        "to@example.com"
    ]
    send_mail(subject, message, from_email, recipient_list, fail_silently=False)
    return render(request, 'index.html')

先ほどview.pyで作成した関数のルーティングを設定します。
mysite/send_mail/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.index),
]

mysite/mysite/urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('send_mail.urls')), #"追加"
]

動作確認

サーバーを起動する

python manage.py runserver

html

無事に送信されたメールがコンソールに出力されました。
コンソール

デフォルトでは題名が文字化けしてしまいます。日本語で表示するには、EmailBackend を拡張することで対処できます。具体的な方法は以下の資料が詳しいのでご参照ください。

おわりに

send_mail()を使うことでメール送信を実装することができました。
お役に立てば幸いです。
最後までお読みいただきありがとうございます。

参考

https://docs.djangoproject.com/en/4.1/topics/email/

【GAS】Google Apps Script で Webhook を使って Google Chat に投稿する(スレッド指定方法も)


みなさん、こんにちは。イノベーションLABのハヤシです。
仙台と京都の街中をなんの目的もなく歩き回るのが好きな埼玉県民です。
もちろん埼玉も大好きです。


今回は、GAS で簡単なスクリプトを作成して Google Chat に投稿する方法をご紹介します。

※投稿処理に使用するのは Webhook なので、対象の Webhook URL がわかれば Slack などへの投稿にも変更できます。
参考)
Sending messages using Incoming Webhooks | Slack


ただ、今回の記事の肝は「Google Chat のスレッドに投稿したい!」というところなので、スレッド投稿で困っている方の参考になれば幸いです。



GAS についてのファーストステップはこちらをご覧ください。
blog.css-net.co.jp

1. 前提条件

1-1. 必要なもの

  • Google アカウント
    • 最初のスクリプト作成までは無料版でできますが、 Chat への投稿は有料版のみです
  • Google Chat Space
    • スレッド形式のスペースにしてください(有料版のみ)
  • ブラウザ

2. GAS のスクリプトを作成

2-1. 最初のスクリプト作成

まずは単純なスクリプトを作成します。

Google Driveで、 新規 -> その他 -> Google Apps Script を選択します

※Google Apps Script がない場合は「アプリを追加」で追加しておきましょう

ファイルが開いたら、プロジェクト名をクリックして変更しておきます。


また、デフォルトで表示されているコードを、以下のようにしてみましょう

function myFunction() {
  console.log('Hello World!');
}

これは、「Hello World!」という文字をログに出力するだけのプログラムです。

保存マークをクリックして保存し、実行マークをクリックで実行します。
ログが出力されました。


2-2. GAS からWebhook 実行

2-2-1. Webhook URL の設定と確認

ここからは、有料版の Google Chat のみで可能になります。

作成してある Chat Space のスペース名横の「▼」をクリックし、「Webhookを管理」をクリックします。

Webhook の名前と、指定したい場合はアバター(アイコン)の URL を入力し、「保存」をクリックします。

※すでに他の Webhook が追加されている場合は、「別のWebhookを追加」をクリックします。


2-2-2. Webhook でメッセージを送信

先ほど作成したプロジェクト(Script01)のコードを、以下のように書き換えます。

function myFunction() {
  const webhookurl = '<Webhook URL>';

  const data = {
    // 投稿したいメッセージ
    'text': 'Hello World!!'
  };
  const options = {
    'method': 'post',
    'headers' : {
      'Content-Type': 'application/json; charset=UTF-8'
    },
    'payload': JSON.stringify(data)
  };
  // Webhook URL に POST
  var response = UrlFetchApp.fetch(webhookurl, options);
  // レスポンスをログ出力
  Logger.log(response.getContentText());
}

<Webhook URL> には、先ほど作成した Webhook の横のコピーアイコンをクリックして、 URL をコピーしたものを設定してください。

保存して実行しようとすると、承認をせまるダイアログが表示されます。

プロジェクトを実行するのに権限が必要なので、「権限を確認」をクリックします。

承認するアカウントを選択します。

Webhook URL に対して POST する = 外部サービスへ接続する、ということなので、「許可」をクリックします。

※今回は Google 内のサービスに POST ですが、外部サービスの可能性もあるためです

許可されると、実行されてログが出力されます。

Chat Space にも投稿されました。


3-1. GAS から Webhook でチャット送信(同一スレッド)

3-1-1. スクリプト変更

このソースコードでは、実行するたびに新しいスレッドが作成されます。

同一スレッドにする方法をご紹介します。

先ほどのコードの webhookurl を以下のように変更します。

  const THREAD_KEY = 'ABCDE';
  const webhookurl = '<Webhook URL>' + `&threadKey=${THREAD_KEY}`;

この状態で保存し、何度か実行すると、同一スレッドに投稿されます。


3-2. GAS から Webhook でチャット送信(既存スレッド指定)

3-2-1. 準備

先ほどの方法はアプリからの投稿を連続させる方法でした。
既存のスレッドに投稿する方法をご紹介します。

対象のスレッドを作成しておきます。


3-2-2. スレッド ID 確認

まずはスレッド ID を確認します。

※Google Chrome での実施方法を説明しますが、他ブラウザでも可能です。ブラウザによって方法は異なります。

F12 で、開発者ツールを開きます。
左端のアイコンをクリックし、要素選択可能な状態にしてから対象のスレッドをクリックします。

ソースの中の「data-id」を探します。これがスレッドID です。(ない場合は、前後の要素を確認してみましょう)


3-2-3. スクリプト変更

スレッド ID を指定して実行してみます。

先ほどのコードの webhookurl はもとに戻して、「oprions」に「thread」を追加します。

  const webhookurl = '<Webhook URL>';

  const data = {
    // 投稿したいメッセージ
    'text': 'Hello World!!  スレッド',
    // スレッド情報
    'thread': {
        'name': 'spaces/<スペースID>/threads/<スレッドID>'
    },
  };

※「スペースID」は、 webhookurl の「spaces/<スペースID>/」の部分を参照してください。

保存して実行すると、スレッドに連続して投稿されています。


3-2-4. 参考

REST Resource: spaces.messages  |  Chat API  |  Google Developers

4. 最後に

Google Chat でメッセージを送ることができても、スレッドを指定する方法がちょっとクセがありますよね。

この方法は、 GAS からの投稿に限らず AWS Lambda からの投稿など外部から Webhook を使用して投稿する際には同様に考えることができます。
参考になさってみてください。


それでは、よい自動化ライフを。


5. 参考

workspace.google.co.jp

developers.google.com

developers.google.com



【GAS】Google Apps Script(GAS) の概要と最初の一歩


みなさん、こんにちは。イノベーションLABのハヤシです。

今回は、いよいよ Google Apps Script(GAS)についての記事です。
GAS の概要と、サンプルプログラムの作成と実行について解説します。

1. 前提条件

1-1. 必要なもの

  • Google アカウント
    • 無料の Gmail アカウントでも OK
  • ブラウザ

2. GAS とは

2-1. 概要

Google Apps Script 、略して GAS(ガス)は、主に Google 関連サービスをローコードで素早く統合、自動化、拡張することができるプラットフォームです。
Gmail アカウントを持っていれば利用でき、本格的な開発の経験がなくても独自のソリューションを構築できます。

コードは JavaScript で記述しますが、各 Google サービスは専用ライブラリで簡単に操作できます。
Visual Basic for Applications(VBA) を利用したことがある方は似たようなイメージでとらえていただいてよいと思います。


Excel 同様にマクロを作成することもできます。 Excel では Excel 上で VBA で記述されますが、 Google スプレッドシートでは GAS 上で JavaScript で記述されます。

また、 HTML やスタイルシートファイルを作成し、簡易 Web アプリとして公開することもできます。

2-2. 特徴

開発環境の構築が不要

ブラウザ上で作成・実行することができ、ファイルは Google ドライブに保存されるため、 PC 上での開発環境構築の手間なく始められます。

無料で利用できる

Google Workspace の有料プランに加入していない、個人の Gmail アカウントでも利用することができます。
※ただし、一部機能は有料プランでないと実行できないものや、有料プランよりも制限が厳しいものもあります。
例)Google Chat の制限で bot へのメッセージ送信は無料アカウントではできない、メール本文として設定できるサイズが無料プランだと少ない、など
参考)
Google Chat で利用できる機能を確認する(使用するアカウント別) - Google Chat ヘルプ

Google 各サービスと連携しやすい

Google の各サービスを操作するための機能が整備されているので、簡単に各サービスの操作をすることができます。

トリガー機能で自動実行することができる

GAS にはトリガーという機能があり、特定の条件で作成したプログラムを自動実行することができます。
トリガーには、時間ベースのトリガー、イベントドリブントリガーなどがあります。


時間ベースのトリガーでは、特定の時間になったらプログラムを実行させることができます。
1時間ごとに実行させる、毎日何時頃に実行させる、毎週何曜日の何時頃に実行させる... など選択することができます。
※注意点は、「何時頃」の場合 1時間の範囲内 でしか指定ができません。

参考)
Simple Triggers  |  Apps Script  |  Google Developers
Installable Triggers  |  Apps Script  |  Google Developers

2-3. 注意点

ローカルのファイル操作はできない

ブラウザからプログラムを実行するため、 PC 上のファイルの操作はできません。
Excel のマクロを GAS で置き換えようとする場合は、 ファイルを Google ドライブ上で管理する必要があるので注意してください。

実行制限がある

GAS で実行するプログラムには制限があります。

代表的なものだと実行時間などがあり、これは無料プランでも有料プランでもほぼ変わりません。

  • スクリプトのランタイム(実行時間)
    • 6分/実行(無料・有料ともに)
  • 同時実行
    • 30/ユーザー(無料・有料ともに)
  • メール本文のサイズ
    • 200KB/メッセージ(無料)
    • 400KB/メッセージ(有料)

※2022年8月時点ですので、最新は以下から確認してください。
developers.google.com


この、 6 分の時間制限はなかなか厳しいのではないでしょうか。
6分以上かかる処理を実行しようとすると、6分で処理が止まってしまいますのでお気を付けください。

3. GAS 活用例

3-1. Google サービス自動化

様々な Google サービスを操作することで、日々の業務を自動化することができます。
※各サービスの機能によっては有料の Google Workspace でしか実現できないものもあります

  • Google ドキュメントを作成して、URLをメール送信する
  • Youtube の視聴回数を毎日スプレッドシートに記入する
  • Google スプレッドシートから PDF を生成してURLをメール送信する
  • Google フォームからファイルをアップロードできるようにする

これらは公式のサンプルとしても提供されているので、気になる方は試してみてください。
developers.google.com

3-2. 他のサービスとの連携

GAS の特徴は、 Google の各サービスとの連携が簡単であることを述べました。
その他のサービスについても Google のサービスほど簡単ではないにしろ、通常のプログラムからの連携と同様 Webhook や API を利用して連携することが可能です。


例えば、 GAS で「Google Chat に投稿する」プログラムを、「Slack に投稿する」に変更することも可能です。
MA ツールから定期的にデータを取得してメールしたり、会計ツールのデータをスプレッドシートに転記したり、活用方法は様々です。


ただし、他のサービスの API の制限や、 GAS 自体の制限にはくれぐれも注意するようにしましょう。

3-3. (簡易)Web アプリケーション開発

GAS では主に JavaScript を使用してプログラムを書きますが、 HTML やスタイルシートのファイルを作成し、 Web アプリケーションとして公開することもできます。


通常、 Web アプリケーションを公開する場合はホスティングするサーバを準備する必要がありますが、 GAS の場合は Google 上で実行されるので、新たに準備する必要がありません。


※ただし、独自ドメインは使用できず、基本的に作成したサイトの上部にはこのようなバナーが表示されます。
Google Workspace の同一ドメイン内のみで表示する場合や、 Google Site から呼び出した場合など表示されない条件もあります。

そのため、外部に公開する Web アプリというよりは、社内での簡易な業務アプリに画面が欲しい時、などに利用するイメージです。


また、画面をともなわない API としてデプロイも可能なので、サーバーレスで API を開発することも可能です。

4. スクリプトの種類

GAS には、 2 つのタイプがあります。
それぞれ作成方法や特徴が異なりますので、見ていきましょう。

4-1. スタンドアロンスクリプト

概要

Google スプレッドシートやドキュメントなど、 Google のサービスに紐づけを行わず、単独で動作するスクリプトです。

作成したプロジェクトは Google ドライブに通常のファイルと同じように表示されます。


様々なサービスと連携したい場合や、特定のファイルに紐づいた処理ではない場合に選択することが多いでしょう。

4-2. コンテナバインドスクリプト

概要

特定の Google スプレッドシートなどから作成したスクリプトです。

作成したプロジェクトは紐づけたファイルからしか表示できず、 Google ドライブには表示されません。また、接続の解除もできません。
特定にファイルに対するアドオンやマクロを作成するイメージです。

紐づけたファイルをスタンドアロンスクリプトより簡単に操作することができます。


特定のファイルに特化した操作を行う場合に選択することが多いでしょう。

参考

Container-bound Scripts  |  Apps Script  |  Google Developers

5. サンプルプログラム作成

5-1. スタンドアロンスクリプトの作成

5-1-1. プロジェクトの作成

以下のいずれの方法でも作成できます。

※どちらの方法で作成しても、ファイルは Google ドライブのマイドライブの直下 に保存されます(特定フォルダを開いていたとしても)。必要に応じて移動させてください。

◆パターン1
ブラウザで Google ドライブを開き、左上の「新規」 -> 「その他」 -> 「Google Apps Script」をクリックします。

※一覧にない場合は「アプリを追加」で追加してください

◆パターン2
https://script.google.com/ にアクセスし、左上の「新しいプロジェクト」をクリックします。


いずれの方法でも、以下のような画面に遷移します。

この時点でマイドライブ直下に「無題のプロジェクト」というファイルが出来上がります。

5-1-2. プロジェクトの編集・保存・実行

作成したプロジェクトでは、「コード.gs」というファイルが自動で作成し、開かれています。

まずはプロジェクトの名前を選択して、変更しておきましょう。

Google ドライブ上の表示も変更されます

表示されているエディタの内容を以下のように書き換えます。
※ <名前> 部分は任意の値に書き換えてください

function myFunction() {
  const name = '<名前>';
  console.log('Hello, %s!!', name);
}

「保存」アイコンをクリックすると保存され、「実行」をクリックすると対象の関数が実行され、ログが出力されます。
※複数関数が定義されている場合、「デバッグ」の右隣りにあるドロップダウンで指定されている関数が実行されます

5-1-3. Google ドキュメントを作成してメールを送る処理を実装

ここからは、 Google が提供しているクイックスタートを参考にしていきます。
developers.google.com

GitHub のコードをさきほどのエディタに貼り付けて、保存します。

このソースコードでは、以下のような処理を行っています。

  • Google ドキュメント作成
    • 「DocumentApp」という Google ドキュメントを操作するライブラリの「create」で「Hello, world!」というファイル名のドキュメントを作成する
    • 作成したファイルの内容を「appendParagraph」で書き込む
    • 「getUrl」でファイルの URL を取得する
  • メール送信
    • 「Session.getActiveUser().getEmail()」でログインしているユーザのメールアドレスを取得する
    • 先ほど作成したファイルのファイル名を「getName」で取得して件名とする
    • 「GmailApp」という Gmail を操作するライブラリの「sendEmail」で対象のメールアドレスに対して件名と本文を指定してメール送信する
  • エラー処理
    • エラーが発生した場合はエラーログを出力する


先ほど同様「実行」をクリックすると、権限の確認ダイアログが表示されます。
さきほどはログを出力するだけでしたが、今回はファイルの作成とメール送信を行っているので、それらを実行する権限が必要になります。

「権限を確認」をクリックします。

実行するアカウントを選択します

※無料の Google アカウントの場合は以下の画面に遷移します。表示されているのが信頼できるアドレスであることを確認し、「詳細」をクリックしてください

「StandAlone01(安全ではないページ)に移動」をクリックします。

許可されるものが Gmail と Google ドキュメントだけであることを確認し、「許可」をクリックします。

実行が完了すると、「Hello, world!」というファイルが作成されていることが確認できます。

ファイルを開くと、スクリプトで指定した内容になっているはずです。

また、自分自身のメールアドレス宛にドキュメントの URL がメール送信されています。

これでスタンドアロンスクリプトの作成は完了です。

※アカウントに権限を許可すると、セキュリティメールが届きます。
身に覚えのない許可通知が来た場合は、「アクティビティを確認」で確認・対処しておきましょう。

5-2. コンテナバインドスクリプトの作成

ここでは、 Codelab で公開されているサンプルにそって、 Google スプレッドシートにバインドして、マップや Gmail とも連携するアプリを作成していきます。
codelabs.developers.google.com

5-2-1. 元となるファイルを作成

今回の例では、スプレッドシートを作成します。
Google ドライブの左上から、「新規」 -> 「Google スプレッドシート」 -> 「空白のスプレッドシート」を選択します、

一番左上の A-1 に、住所を入力します。日本語でも大丈夫なので、こんなふうにしてみました。

熊本県熊本市中央区本丸1−1


5-2-2. スクリプトを作成

作成したスプレッドシートに、スクリプトを追加します。

メニューバーで「拡張機能」 -> 「Apps Script」を選択すると、コードエディターが開きます。

スクリプトを以下の内容で上書きして保存します
※Codelab の内容から少し変えています

/** @OnlyCurrentDoc */
function sendMap() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const address = sheet.getRange('A1').getValue();
  const map = Maps.newStaticMap().setLanguage('ja').addMarker(address);
  const email = Session.getActiveUser().getEmail();

  GmailApp.sendEmail(email, 'Map', 'See below.', {attachments:[map]});
}

このソースコードでは、以下のような処理を行っています。

  • Google スプレッドシートからデータ取得
    • 「SpreadsheetApp」という Google スプレッドシートを操作するライブラリの「cgetActiveSheet」でアクティブなシートを取得する(これはスタンドアロンスクリプトでは特定できないため使用不可)
    • 取得したシートの A1 セルに記載された住所情報を取得
  • Google マップ
    • 「Maps」というGoogle マップを操作するライブラリを使用して、取得した住所情報にマーカー(赤いピン)をセットした日本語の地図情報を作成する
  • メール送信
    • 「Session.getActiveUser().getEmail()」でログインしているユーザのメールアドレスを取得する
    • 「GmailApp」という Gmail を操作するライブラリの「sendEmail」で対象のメールアドレスに対して件名と本文、先ほどの地図情報を添付ファイルとして指定してメール送信する

5-2-3. スクリプトを実行
「実行」をクリックすると、権限の確認ダイアログが表示されます。
今回はファイルの読み取りとメール送信を行っているので、それらを実行する権限が必要になります。

権限を承認する方法は、前述の 5-1-3 に記載していますのでそちらをご参照ください。

実行が完了すると、自分自身のメールアドレス宛にマップの情報が添付されたメールが届いています。

これでコンテナバインドスクリプトの作成は完了です。

6. さいごに

GAS とはなんなのか、どういう風に作成するのか、ということをお伝えしました。
みなさん、やりたいことは浮かんできたでしょうか?
ぜひ、様々な処理を自動化してみてください。

こちらでは公式から様々なサンプルが提供されていますので、参考になさってください。
developers.google.com


ちなみに、ここでは触れられませんでしたが、 clasp というものを使うと自分の PC 上で好きなエディタで開発もできるようになります。
github.com

バージョン管理がしたい、お気に入りのエディタを使いたい、などありましたら、試してみてください。


それでは、よい自動化ライフを!

7. 参考

Google Apps Script: Google Workspace を自動化、統合、拡張。
Apps Script  |  Google Developers
Reference overview  |  Apps Script  |  Google Developers

【SAM + Cognito + Amplify ライブラリでログイン機能を実装した React アプリを作成】Part3 - API に認証追加とログインページ実装


みなさん、こんにちは。イノベーションLABのハヤシです。

以下の構成の Web アプリをさくっと作る手順をご紹介します。
 バックエンド:Node.js の REST API(Amazon API Gateway / AWS Lambda)
 フロントエンド:TypeScript の React

今回は全 3 回のうちの最終回、認証と、お片付け編です。
必要な部分だけでも、ぜひ参考にしてみてください。

1回目はこちら↓
blog.css-net.co.jp
2回目はこちら↓
blog.css-net.co.jp

1. 前提条件

1-1. 想定読者

「さくっとログイン機能を実装した Web アプリが作りたい」
「せっかくなら SPA + REST API でやりたい」
「AWS SAM や Amplify ライブラリを使ってみたい」
といった方々を想定しています。


必要最低限の方法のみ解説しているので、ほぼサンプルコードのままです。
その後のカスタマイズには、 Node.js や TypeScript / React の知識が必要になります。


とはいえ、この記事は知識があることを前提としていませんので、手順通りに実施していく分にはそれらの知識は不要です。
雰囲気をつかむために、まずはチャレンジしてみるのもおすすめです!

1-2. 前提条件

OS - Windows 10 Pro (Home でも問題ありません)
VSCode - 1.69.1
Git - 2.37.1.windows.1
AWS CLI - 2.7.14
SAM CLI - 1.53.0
Node.js - 16.16.0


aws configure で認証設定がすんでいること
以下コマンドで、「Account」が自分が操作可能な Account ID になっていることを確認する

aws sts get-caller-identity

# Profile を指定している場合は今後のコマンドも全て Profile を指定してください
# aws sts get-caller-identity --profile <Profile名>


この後の手順は特に指示がない限り、エディタは VSCode でターミナルは Git Bash での操作となります

環境構築手順はこちらで紹介しています
blog.css-net.co.jp


2. 認証

2-1. API 実行を認証必須にする(バックエンド)

Cognito を追加して、 API を認証しないと利用できないように設定します。
VSCode のターミナルで作業します。
バックエンド用ディレクトリ(C:\work\tutorial-app\tutorial-app-back)にいる前提で進めていきます。


前回の手順で追加した API Gateway のあとに、 Cognito の設定を追記して、 API Gateway にも Auth 設定を追加しておきます。
以下は、ユーザ名/Eメール でユーザを作成する例です。

template.yaml

  # --------------------
  # API Gateway
  # --------------------
  SampleApi:
    Type: AWS::Serverless::Api
    Properties:
      Name: "tutorial-app-api"
      StageName: dev
      OpenApiVersion: 3.0.2
      EndpointConfiguration: REGIONAL
      Cors:
        AllowMethods: "'GET,POST,DELETE,PATCH,OPTIONS'"
        AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
        AllowOrigin: "'*'"
# ----- 追加↓
      Auth:
        DefaultAuthorizer: SampleCognitoAuth
        AddDefaultAuthorizerToCorsPreflight: false
        Authorizers:
          SampleCognitoAuth:
            UserPoolArn: !GetAtt SampleUserPool.Arn

  # --------------------
  # Cognito
  # --------------------
  # UserPool
  SampleUserPool:
    Type: AWS::Cognito::UserPool
    Properties:
      UserPoolName: "tutorial-app-userpool"
      Schema:
      -
        Name: email
        AttributeDataType: String
        Mutable: true
        Required: true
      AutoVerifiedAttributes:
        - email
      UsernameConfiguration:
          CaseSensitive: false
      AccountRecoverySetting:
        RecoveryMechanisms:
        -
          Name: verified_email
          Priority: 1
      DeviceConfiguration:
        DeviceOnlyRememberedOnUserPrompt: true
  # UserPoolClient
  SampleUserPoolClient:
    Type: AWS::Cognito::UserPoolClient
    Properties:
      UserPoolId: !Ref SampleUserPool
      ClientName: "tutorial-app-userpoolclient"
      ExplicitAuthFlows:
        - ALLOW_ADMIN_USER_PASSWORD_AUTH
        - ALLOW_CUSTOM_AUTH
        - ALLOW_USER_SRP_AUTH
        - ALLOW_REFRESH_TOKEN_AUTH
      PreventUserExistenceErrors: ENABLED
      ReadAttributes:
        - email
        - email_verified
      WriteAttributes:
        - email
# ----- 追加↑

Outputs:
  WebEndpoint:
    Description: "API Gateway endpoint URL for dev stage"
    Value: !Sub "https://${SampleApi}.execute-api.${AWS::Region}.amazonaws.com/dev"


ビルド / デプロイをします。

sam build
sam deploy --config-env dev


Cognito のユーザープールが作成されていることを確認します。
マネジメントコンソールで確認します。
◆Cognito -> ユーザープール -> tutorial-app-userpool


この状態で React アプリを起動しているブラウザを更新すると、 401 エラー(認証エラー)でデータが取得できないはずです。

これについてはログインページ実装後に実施します。

2-2. ログインページ実装(フロントエンド)

React の UI ライブラリを使用して、認証を追加していきます。
VSCode のターミナルで作業します。
フロントエンド用ディレクトリ(C:\work\tutorial-app\tutorial-app-front)にいる前提で進めていきます。

参考
Authenticator | Amplify UI for React


上記を参考に、 src/App.tsx を以下のように変更します。

import React from 'react';
import { Authenticator } from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';

import './App.css';
import Home from './pages/Home';

function App() {
  return (
    <Authenticator signUpAttributes={['email']}>
      {({ signOut, user }) => (
        <Home />
      )}
    </Authenticator>
  );
}

export default App;


サインインページが表示されます。
※まだサインアップ/サインインはできません


作成した Cognito を認証情報として使用するように設定します。
src/aws-exports.js に、「Auth」セクションを追記します。
src/aws-exports.js

const config = {
  Auth: {
    region: process.env.REACT_APP_AWS_AUTH_REGION,
    userPoolId: process.env.REACT_APP_AWS_AUTH_USER_POOL_ID,
    userPoolWebClientId: process.env.REACT_APP_AWS_AUTH_CLIENT_ID
  },
  API: {
      endpoints: [
          {
              name: "MainApi",
              endpoint: process.env.REACT_APP_AWS_API_ENDPOINT_MAIN,
          },
      ]
  }
};

export default config;


作成した Cognito の ID を確認します。
マネジメントコンソールで確認します。
◆Cognito -> ユーザープール -> tutorial-app-userpool
ユーザープールID

アプリケーションの統合 -> アプリクライアントと分析 -> クライアントID


.env ファイルと .env_sample ファイルを以下のように変更します。
※REACT_APP_AWS_API_ENDPOINT_MAIN はもともと設定済み

.env

REACT_APP_AWS_AUTH_REGION="ap-northeast-1"
REACT_APP_AWS_AUTH_USER_POOL_ID="<ユーザープールID>"
REACT_APP_AWS_AUTH_CLIENT_ID="<クライアントID>"
REACT_APP_AWS_API_ENDPOINT_MAIN="<APIエンドポイント>"

.env_sample

REACT_APP_AWS_AUTH_REGION="ap-northeast-1"
REACT_APP_AWS_AUTH_USER_POOL_ID="ap-northeast-1_xxxxxxxxx"
REACT_APP_AWS_AUTH_CLIENT_ID="xxxxxxxxxx"
REACT_APP_AWS_API_ENDPOINT_MAIN="https://xxxxxxxxxx"


フロントエンドのアプリを起動している場合は Ctrl + C で停止させてから、 npm start で改めて起動させます。
※再起動させないと、今設定した env(環境変数)が適用されません


「Create Account」タブをクリックし、ユーザ名とメールアドレスでアカウントを作成します。


登録したメールアドレスに認証コードが届きます。


元の画面は認証コード入力画面に遷移しているはずなので、届いたコードを入力して「Confirm」をクリックします。


ログインが成功すると、 Home 画面が表示されます。(API はまだエラーなのでデータは表示されません)

2-3. API 呼び出しに認証情報を付与(フロントエンド)

VSCode のターミナルで作業します。
フロントエンド用ディレクトリ(C:\work\tutorial-app\tutorial-app-front)にいる前提で進めていきます。

src/pages/Home.tsx

import { API, Auth } from 'aws-amplify'

// ~略~

  const getSampleData = async () => {
    try {
      const apiName = 'MainApi';
      const path = '/';
      const myInit = {
        headers: { 
          Authorization: `Bearer ${(await Auth.currentSession()).getIdToken().getJwtToken()}`,
        },
      };
    
      const data = await API.get(apiName, path, myInit);
      setSampleData(data)
    } catch (err) { console.log('error getting data') }
  }


ブラウザを更新すると、データが表示されているはずです。(開発者ツールでもエラーが消えています)


ここまでで back / front ともに push しておきましょう。
tutorial-app-front ディレクトリで

git add .
git commit -m "認証追加"
git push origin main

tutorial-app-back ディレクトリで

git add .
git commit -m "認証追加"
git push origin main

3. おまけ

3-1. サインアウト実装

Home 画面にユーザ名の表示とサインアウトボタンを追加しましょう。
VSCode のターミナルで作業します。
フロントエンド用ディレクトリ(C:\work\tutorial-app\tutorial-app-front)にいる前提で進めていきます。


Home.tsx と App.tsx ファイルを以下のように編集します。

src/pages/Home.tsx

import React, { useEffect, useState } from 'react'
import { API, Auth } from 'aws-amplify'
import { CognitoUserAmplify } from '@aws-amplify/ui';
import '@aws-amplify/ui-react/styles.css';

type SampleDataType = [
  {
    id: string;
    name: string;
  }
];
const initialSampleData = {id: "", name: ""};

type HomeProps = {
  signOut: VoidFunction;
  user: CognitoUserAmplify;
};

const Home: React.FC<HomeProps> = (props) => {
  const [sampleData, setSampleData] = useState<SampleDataType>([initialSampleData])

  useEffect(() => {
    getSampleData()
  }, [])

  async function getSampleData() {
    try {
      const apiName = 'MainApi';
      const path = '/';
      const myInit = {
        headers: { 
          Authorization: `Bearer ${(await Auth.currentSession()).getIdToken().getJwtToken()}`,
        },
      };
    
      const data = await API.get(apiName, path, myInit);
      setSampleData(data)
    } catch (err) { console.log('error getting data') }
  }

  return (
    <div className="App">
      <p>ようこそ、 {props.user.username} さん!</p>
      <p>登録データ一覧</p>
      {
        sampleData.map((data, index) => (
          <div key={data.id ? data.id : index}>
            <p>{data.name}</p>
          </div>
        ))
      }
      <button onClick={props.signOut}>Sign out</button>
    </div>
  );
}

export default Home;


src/App.tsx

import React from 'react';
import { Authenticator } from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';

import './App.css';
import Home from './pages/Home';

function App() {
  return (
    <Authenticator signUpAttributes={['email']}>
      {({ signOut, user }) => (
        <Home signOut={signOut!} user={user!} />
      )}
    </Authenticator>
  );
}

export default App;


ブラウザを更新すると、サインアウトボタンが表示されます。


クリックすると、サインイン画面に戻ります。
サインインすると Home 画面が表示されることを確認しましょう。

push しておきます。
tutorial-app-front ディレクトリで

git add .
git commit -m "サインアウトボタン追加"
git push origin main

3-2. サインイン画面日本語化

VSCode のターミナルで作業します。
フロントエンド用ディレクトリ(C:\work\tutorial-app\tutorial-app-front)にいる前提で進めていきます。


App.tsx ファイルを以下のように編集します。

src/App.tsx

import React from 'react';
import { I18n } from 'aws-amplify'
import { Authenticator, translations } from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';

import './App.css';
import Home from './pages/Home';

I18n.putVocabularies(translations);
I18n.setLanguage('ja');

function App() {
  return (
    <Authenticator signUpAttributes={['email']}>
      {({ signOut, user }) => (
        <Home signOut={signOut!} user={user!} />
      )}
    </Authenticator>
  );
}

export default App;


ブラウザを更新して、日本語化されていることを確認します。



push しておきます。
tutorial-app-front ディレクトリで

git add src/App.tsx
git commit -m "サインイン画面日本語化"
git push origin main

4. リソース削除

4-1. 削除対象のリソース一覧

これで作成編は終わりです。確認が終わったら、作成したものを削除しておきましょう。
今回の手順で AWS 上に作成されているリソースは以下のとおりです。

★…tutorial-app-repo-stack スタックで作成
☆…tutorial-app-back-stack スタックで作成
無印...手動/自動で作成

マネジメントコンソールで確認します。
◆CloudFormation -> スタック

  • ★tutorial-app-repo-stack
  • ☆tutorial-app-back-stack

◆CodeCommit -> リポジトリ

  • ★tutorial-app-front
  • ★tutorial-app-back

◆Lambda -> アプリケーション

  • ☆tutorial-app-back-stack

◆CloudWatch -> ログ -> ロググループ

  • /aws/lambda/tutorial-app-back-stack-getAllItemsFunction-<ランダム文字列>
  • /aws/lambda/tutorial-app-back-stack-putItemFunction-<ランダム文字列>
  • /aws/lambda/tutorial-app-back-stack-getByIdFunction-<ランダム文字列>

※1度も関数を実行していない場合は作成されていない

◆API Gateway -> API

  • ☆tutorial-app-api

◆Cognito -> ユーザープール

  • ☆tutorial-app-userpool

◆DynamoDB -> テーブル

  • ☆tutorial-app-back-stack-SampleTable-<ランダム文字列>

◆IAM -> ロール

  • ☆tutorial-app-back-stack-getAllItemsFunctionRole-<ランダム文字列>
  • ☆tutorial-app-back-stack-getByIdFunctionRole-<ランダム文字列>
  • ☆tutorial-app-back-stack-putItemFunctionRole-<ランダム文字列>

◆S3 -> バケット

  • aws-sam-cli-managed-default-samclisourcebucket-<ランダム文字列>
    • tutorial-app-back-stack/ ディレクトリ

※手動で削除する場合、バケット自体は他で利用している可能性があるので不用意に削除しないこと。

4-2. 削除

VSCode のターミナルで作業します。
フバックエンド用ディレクトリ(C:\work\tutorial-app\tutorial-app-back)にいる前提で進めていきます。


以下のコマンドを実行します。

sam delete --config-env dev


選択肢は以下のとおり。

  • Are you sure you want to delete the stack tutorial-app-back-stack in the region ap-northeast-1 ? [y/N]:
    • y
  • Are you sure you want to delete the folder tutorial-app-back-stack in S3 which contains the artifacts? [y/N]:
    • y


tutorial-app-back-stack スタックと紐づくリソース(上記の☆のリソース)、S3 バケットにアップされたファイルが削除されます。


Stack 削除で削除されないロググループも削除しておきましょう。
以下を選択します(作成されていない場合もあります)。

◆CloudWatch -> ログ -> ロググループ

  • /aws/lambda/tutorial-app-back-stack-getAllItemsFunction-<ランダム文字列>
  • /aws/lambda/tutorial-app-back-stack-putItemFunction-<ランダム文字列>
  • /aws/lambda/tutorial-app-back-stack-getByIdFunction-<ランダム文字列>

対象にチェックを入れ、「アクション」 -> 「ロググループの削除」をクリックします。


確認ダイアログで「削除」をクリックすると削除されます。


CodeCommit のリポジトリも削除する場合は、 tutorial-app-repo-stack スタックを選択し、「削除」をクリックします。
◆CloudFormation -> スタック


確認ダイアログで「スタックの削除」をクリックします。


スタック一覧から消え、ステータスが DELETE_COMPLETE になれば削除完了です。

5. 参考

5-1. 認証

AWS::Cognito::UserPool - AWS CloudFormation
AWS::Cognito::UserPoolClient - AWS CloudFormation


【SAM + Cognito + Amplify ライブラリでログイン機能を実装した React アプリを作成】Part2 - React アプリから API 実行


みなさん、こんにちは。イノベーションLABのハヤシです。

以下の構成の Web アプリをさくっと作る手順をご紹介します。
 バックエンド:Node.js の REST API(Amazon API Gateway / AWS Lambda)
 フロントエンド:TypeScript の React

今回は全 3 回のうちの 2 回目、フロントエンド実装編です。
必要な部分だけでも、ぜひ参考にしてみてください。

1回目はこちら↓
blog.css-net.co.jp


1. 前提条件

1-1. 想定読者

「さくっとログイン機能を実装した Web アプリが作りたい」
「せっかくなら SPA + REST API でやりたい」
「AWS SAM や Amplify ライブラリを使ってみたい」
といった方々を想定しています。


必要最低限の方法のみ解説しているので、ほぼサンプルコードのままです。
その後のカスタマイズには、 Node.js や TypeScript / React の知識が必要になります。


とはいえ、この記事は知識があることを前提としていませんので、手順通りに実施していく分にはそれらの知識は不要です。
雰囲気をつかむために、まずはチャレンジしてみるのもおすすめです!

1-2. 前提条件

OS - Windows 10 Pro (Home でも問題ありません)
VSCode - 1.69.1
Git - 2.37.1.windows.1
AWS CLI - 2.7.14
SAM CLI - 1.53.0
Node.js - 16.16.0


aws configure で認証設定がすんでいること
以下コマンドで、「Account」が自分が操作可能な Account ID になっていることを確認する

aws sts get-caller-identity

# Profile を指定している場合は今後のコマンドも全て Profile を指定してください
# aws sts get-caller-identity --profile <Profile名>


この後の手順は特に指示がない限り、エディタは VSCode でターミナルは Git Bash での操作となります

環境構築手順はこちらで紹介しています
blog.css-net.co.jp

1-3. この手順でインストールするもの

React - 18.2.0
TypeScript - 4.7.4
aws-amplify - 4.3.27
@aws-amplify/ui-react - 3.0.4

2. フロントエンド SPA

2-1. React アプリ作成

前回作成した作業用ディレクトリ(「C:\work\tutorial-app」)をVSCode で開きます。
この状態からスタートです。
VSCode のターミナルで作業します。


以下のコマンドで、 TypeScript で React アプリを作成します。

npx create-react-app tutorial-app-front --template typescript


Amplify に必要なライブラリをインストールします。

cd tutorial-app-front
npm install aws-amplify @aws-amplify/ui-react

※React 18 との依存関係でエラーになる場合は以下のコマンドで強制的にインストールしてください

npm install aws-amplify @aws-amplify/ui-react --legacy-peer-deps

参考
React 18 peer dependency errors from radix dependencies · Issue #2089 · aws-amplify/amplify-ui · GitHub


tutorial-app-front ディレクトリで以下コマンドを実行して、ブラウザ(http://localhost:3000/)でアプリが立ち上がることを確認します。

npm start


2-2. コンポーネント分割

このあとログイン処理を実装するため、ページを分割しておきます。
VSCode のターミナルで作業します。
フロントエンド用ディレクトリ(C:\work\tutorial-app\tutorial-app-front)にいる前提で進めていきます。


src/pages ディレクトリを新規作成し、 Home.tsx ファイルを以下の内容で追加します。
src/pages/Home.tsx

import React from 'react';

const Home = () => {
  return (
    <div>
      ようこそ!
    </div>
  );
}

export default Home;


App.tsx ファイルを、今作成した Home コンポーネントを呼び出すように書き換えておきます。
src/App.tsx

import React from 'react';
import './App.css';
import Home from './pages/Home';

function App() {
  return (
    <div className="App">
      <Home />
    </div>
  );
}

export default App;


ブラウザでの表示がこのようになっていれば OK です。


確認ができたら Ctrl + C で、一度アプリを停止しておきます。

2-3. リポジトリに登録

ここまでの状態で Push しておきましょう。
VSCode のターミナルで作業します。
フロントエンド用ディレクトリ(C:\work\tutorial-app\tutorial-app-front)にいる前提で進めていきます。


以下のコマンドで、バックエンドの時と同様にローカルリポジトリとリモートリポジトリを紐づけます

git remote add origin https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/tutorial-app-front

※「https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/tutorial-app-front」の部分は、前回記事でリポジトリ作成時に確認した front 用のリポジトリの HTTPS パスを指定してください


以下のコマンドで、ブランチ名の変更とリモートリポジトリへの Push をします。

git branch -m main
git add .
git commit -m "first commit"
git push origin main


マネジメントコンソールで作業します。
ファイルがアップされていることを確認します。
◆CodeCommit -> ソース -> リポジトリ -> tutorial-app-front

2-4. Amplify ライブラリの設定

src/aws-exports.js ファイルを追加して、Amplify ライブラリで AWS リソースを使う設定を書き込みます。
VSCode のターミナルで作業します。
フロントエンド用ディレクトリ(C:\work\tutorial-app\tutorial-app-front)にいる前提で進めていきます。

src/aws-exports.js

const config = {
  API: {
      endpoints: [
          {
              name: "MainApi",
              endpoint: process.env.REACT_APP_AWS_API_ENDPOINT_MAIN,
          },
      ]
  }
};

export default config;

このファイルでは、環境変数から API のエンドポイントを取得するようになっています。
環境変数の設定ファイルを追加しましょう。


まずは API のエンドポイントを確認しておきます。

◆---------- APIのエンドポイント確認方法(クリックで開く) ----------◆

マネジメントコンソールで作業します。
1. API Gateway 画面から確認
◆API Gateway -> API -> tutorial-app-api
ステージ -> dev -> 「URL の呼び出し:」に記載されています。


2. CloudFormation 画面から確認
◆CloudFormation -> スタック -> tutorial-app-back-stack
出力 -> 「WebEndpoint」の値です。

◆---------- APIのエンドポイント確認方法 ここまで ----------◆


.env ファイルを追加して、以下の内容で保存します。(確認した API のエンドポイントを記載してください)
.env

REACT_APP_AWS_API_ENDPOINT_MAIN="https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev"


環境変数ファイルは .gitignore へ記載してリポジトリ管理から外しておきます。

echo .env >> .gitignore

かわりにサンプルファイルを追加しておきます。
.env_sample ファイルを作成し、以下の内容で保存します。
.env_sample

REACT_APP_AWS_API_ENDPOINT_MAIN="https://xxxxxxxxxx"


src/index.tsx に以下を追記します。
src/index.tsx

import reportWebVitals from './reportWebVitals';
// ----- 追加↓
import { Amplify } from 'aws-amplify';
import awsExports from './aws-exports';
Amplify.configure(awsExports);
// ----- 追加↑

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);


Home.tsx を API で取得したデータを表示するように変更します。
src/pages/Home.tsx

import React, { useEffect, useState } from 'react'
import { API } from 'aws-amplify'

type SampleDataType = [
  {
    id: string;
    name: string;
  }
];
const initialSampleData = {id: "", name: ""};

const Home: React.FC = () => {
  const [sampleData, setSampleData] = useState<SampleDataType>([initialSampleData])

  useEffect(() => {
    getSampleData()
  }, [])

  const getSampleData = async () => {
    try {
      const apiName = 'MainApi';
      const path = '/';
      const myInit = {};
    
      const data = await API.get(apiName, path, myInit);
      setSampleData(data)
    } catch (err) { console.log('error getting data') }
  }

  return (
    <div className="App">
      <p>ようこそ!</p>
      <p>登録データ一覧</p>
      {
        sampleData.map((data, index) => (
          <div key={data.id ? data.id : index}>
            <p>{data.name}</p>
          </div>
        ))
      }
    </div>
  );
}

export default Home;


tutorial-app-front ディレクトリでアプリを実行します。

npm start


ブラウザで F12 を押下して、開発者ツールを見るとエラーが出ています。


API 呼び出しをするには CORS 設定が必要なので、バックエンド側の修正を行っていきます。
参考
オリジン間リソース共有 (CORS) - HTTP | MDN

2-5. バックエンドの CORS 設定

VSCode のターミナルで作業します。
ここからは、バックエンド用ディレクトリ(C:\work\tutorial-app\tutorial-app-back)にいる前提で進めていきます。


REST API で CORS 設定をするには Lambda のソースコードのレスポンスを修正する必要があります。
参考
REST API リソースの CORS を有効にする - Amazon API Gateway


src/handlers 内の「get-all-items.js」「get-by-id.js」「put-item.js」それぞれの「response」を指定している部分に、「headers」を追加します。

src/handlers/get-all-items.js
src/handlers/get-by-id.js
src/handlers/put-item.js

    const response = {
        statusCode: 200,
// ----- 追加↓
        headers: {
            "Access-Control-Allow-Headers" : "Content-Type",
            "Access-Control-Allow-Origin": "*",
            "Access-Control-Allow-Methods": "OPTIONS,POST,GET"
        },
// ----- 追加↑
        body: JSON.stringify(items)
    };


API Gateway にも CORS 設定をしておきます。
template.yaml の API Gateway 設定に「Cors」を追加します。

template.yaml

  # --------------------
  # API Gateway
  # --------------------
  SampleApi:
    Type: AWS::Serverless::Api
    Properties:
      Name: "tutorial-app-api"
      StageName: dev
      OpenApiVersion: 3.0.2
      EndpointConfiguration: REGIONAL
# ----- 追加↓
      Cors:
        AllowMethods: "'GET,POST,DELETE,PATCH,OPTIONS'"
        AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
        AllowOrigin: "'*'"
# ----- 追加↑


ビルド / デプロイをします。

sam build
sam deploy --config-env dev


デプロイが終わったら、ブラウザで開いている React アプリを更新してみましょう。
※停止している場合は、 tutorial-app-front ディレクトリで npm start して起動してください

データが表示されます。


※開発者ツールを開いている場合に表示されるこのエラーは、ログインページを実装する際に対応されるので今は気にしないで大丈夫です。


バックエンド / フロントエンド ともに push しておきましょう。

tutorial-app-front ディレクトリで

git add .
git commit -m "API 呼び出し追加"
git push origin main

tutorial-app-back ディレクトリで

git add .
git commit -m "CORS 設定追加"
git push origin main


React アプリから、自作した API を実行することに成功しました。
次回は API への認証の追加と、ログインページを実装していきます。

3. 参考

Adding TypeScript | Create React App
Tutorial - Set up fullstack project - React - AWS Amplify Docs


【SAM + Cognito + Amplify ライブラリでログイン機能を実装した React アプリを作成】Part1 - SAM で API 作成


みなさん、こんにちは。イノベーションLABのハヤシです。

今回は、以下の構成の Web アプリをさくっと作る手順をご紹介します。
 バックエンド:Node.js の REST API(Amazon API Gateway / AWS Lambda)
 フロントエンド:TypeScript の React

今回は全 3 回のうちの 1 回です。
必要な部分だけでも、ぜひ参考にしてみてください。

環境構築手順はこちらで紹介しています
blog.css-net.co.jp


1. 前提条件

1-1. 想定読者

「さくっとログイン機能を実装した Web アプリが作りたい」
「せっかくなら SPA + REST API でやりたい」
「AWS SAM や Amplify ライブラリを使ってみたい」
といった方々を想定しています。


必要最低限の方法のみ解説しているので、ほぼサンプルコードのままです。
その後のカスタマイズには、 Node.js や TypeScript / React の知識が必要になります。


とはいえ、この記事は知識があることを前提としていませんので、手順通りに実施していく分にはそれらの知識は不要です。
雰囲気をつかむために、まずはチャレンジしてみるのもおすすめです!

1-2. 作成するもの

Part 3 までで、以下の内容を実装します。

  • DynamoDB を利してデータを登録/取得する API(Part1 / 2 / 3)
  • サインイン画面(Part3)

  • サインイン後にAPIからデータを取得して表示する画面(Part2 / 3)


1-3. 前提条件

OS - Windows 10 Pro (Home でも問題ありません)
VSCode - 1.69.1
Git - 2.37.1.windows.1
AWS CLI - 2.7.14
SAM CLI - 1.53.0
Node.js - 16.16.0


aws configure で認証設定がすんでいること
以下コマンドで、「Account」が自分が操作可能な Account ID になっていることを確認する

aws sts get-caller-identity

# Profile を指定している場合は今後のコマンドも全て Profile を指定してください
# aws sts get-caller-identity --profile <Profile名>


この後の手順は特に指示がない限り、エディタは VSCode でターミナルは Git Bash での操作となります

環境構築手順はこちらで紹介しています
blog.css-net.co.jp


2. 事前準備

2-1. 作業ディレクトリ作成

エクスプローラで作業用のディレクトリを作成しておきましょう。
私は「C:\work\tutorial-app」というディレクトリを作成して、 VSCode で開きました。
この状態からスタートです。

2-2. リポジトリ作成

まずは フロントエンド、バックエンドともにソースコードを管理する Git リポジトリを作成します。
GitHub でも良いのですが、今回は CodeCommit に作成します。


マネジメントコンソールから手動で作っても良いですが、CloudFormation で作ってみましょう。
VSCode のターミナルで作業します。


作業用ディレクトリに「templates」ディレクトリを作成し、以下の内容で「01_repo_template.yaml」というファイルを作成します。
templates/01_repo_template.yaml

AWSTemplateFormatVersion: "2010-09-09"

Description:
  CodeCommit Repository

Parameters:
  BackendRepoName:
    Type: String
    Default: "tutorial-app-back"
  FrontendRepoName:
    Type: String
    Default: "tutorial-app-front"

Resources:
  BackendRepository:
    Type: AWS::CodeCommit::Repository
    Properties: 
      RepositoryName: !Ref BackendRepoName
  FrontendRepository:
    Type: AWS::CodeCommit::Repository
    Properties: 
      RepositoryName: !Ref FrontendRepoName

Outputs:
  BackendHttpsUrl:
    Description: The URL to use for cloning the repository over HTTPS.
    Value: !GetAtt BackendRepository.CloneUrlHttp
    Export:
      Name: !Sub "${AWS::StackName}-Backend-HttpsUrl"
  BackendSshUrl:
    Description: The URL to use for cloning the repository over SSH.
    Value: !GetAtt BackendRepository.CloneUrlSsh
    Export:
      Name: !Sub "${AWS::StackName}-Backend-SSHUrl"
  FrontendHttpsUrl:
    Description: The URL to use for cloning the repository over HTTPS.
    Value: !GetAtt FrontendRepository.CloneUrlHttp
    Export:
      Name: !Sub "${AWS::StackName}-Frontend-HttpsUrl"
  FrontendSshUrl:
    Description: The URL to use for cloning the repository over SSH.
    Value: !GetAtt FrontendRepository.CloneUrlSsh
    Export:
      Name: !Sub "${AWS::StackName}-Frontend-SSHUrl"


バックエンド用に「tutorial-app-back」、フロントエンド用に「tutorial-app-front」という名前でリポジトリを作成するテンプレートです。


templates ディレクトリに移動して、デプロイコマンドを実行します。

cd templates
TEMPLATE_NAME="01_repo_template.yaml"
REPO_STACK_NAME="tutorial-app-repo-stack"

BACKEND_REPO_NAME="tutorial-app-back"
FRONTEND_REPO_NAME="tutorial-app-front"

aws cloudformation deploy --template ./${TEMPLATE_NAME} \
--stack-name ${REPO_STACK_NAME} \
--parameter-overrides \
  BackendRepoName=${BACKEND_REPO_NAME} \
  FrontendRepoName=${FRONTEND_REPO_NAME}


マネジメントコンソールで作業します。
以下のものができあがっていることを確認しましょう。

◆CloudFormation -> スタック
「tutorial-app-repo-stack」という名前の Stack

◆CodeCommit -> ソース -> リポジトリ
tutorial-app-back」「tutorial-app-front」という名前のリポジトリ


今後の設定のために、リポジトリの HTTPS パスを取得します。

◆CodeCommit -> ソース -> リポジトリ -> <対象リポジトリ名> -> URLのクローン -> HTTPSのクローン

※ back / front それぞれコピーして、メモしておきましょう


または、 CloudFormation からも確認できます。
◆CloudFormation -> スタック -> tutorial-app-repo-stack -> 出力
BackendHttpsUrl / FrontendHttpsUrl の値が HTTPS パスです。

2-3. ターミナルで git 設定

ターミナルで git 用の設定をしておきましょう。
以下を参考に進めていきます。
docs.aws.amazon.com

VSCode のターミナルで作業します。

以下のコマンドを実行して、 CodeCommit への認証に AWS の認証情報を使用する設定をします。

git config --global credential.helper '!aws codecommit credential-helper $@'
# aws configure で profile 指定をしている場合はこちら
# git config --global credential.helper '!aws codecommit credential-helper --profile <Profile名> $@'
git config --global credential.UseHttpPath true


※複数の AWS アカウントを利用する可能性がある場合は以下を指定してリポジトリごとの設定にしておきます。

git config --global credential.<HTTPSパス>.helper '!aws codecommit credential-helper $@'
# aws configure で profile 指定をしている場合はこちら
# git config --global credential.<HTTPSパス>.helper '!aws codecommit credential-helper --profile <Profile名> $@'
git config --global credential.<HTTPSパス>.UseHttpPath true


以下のコマンドで、 git を利用する際のユーザ情報をセットしておきます。

USER_NAME="<ユーザ名(Taro Hayashi など)>"
USER_MAIL="<メールアドレス>"

git config --global user.name "${USER_NAME}"
git config --global user.email ${USER_MAIL}


参考
AWS CodeCommit リポジトリを作成する - AWS CodeCommit
AWS::CodeCommit::Repository - AWS CloudFormation
AWS CloudFormation で CodeCommit リソースを作成する - AWS CodeCommit
変換を伴うテンプレートの迅速なデプロイ - AWS CloudFormation

3. バックエンド API

3-1. API 作成

VSCode のターミナルで作業します。
作業用ディレクトリ(C:\work\tutorial-app)にいる前提で進めていきます。
ターミナルで以下コマンド(現在位置を出力)を打って、「/c/work/tutorial-app」であることを確認してください。

pwd

※「/c/work/tutorial-app/templates」にいる場合は「cd ../」で1階層上に戻ってください。


以下のコマンドで、 SAM のテンプレートに従ってプロジェクトを作成していきます

sam init

選択肢は以下のものを選んでみましょう。

  • Which template source would you like to use?
    • 1 - AWS Quick Start Templates
  • Choose an AWS Quick Start application template
    • 3 - Serverless API
  • Which runtime would you like to use?
    • 3 - nodejs16.x
  • Would you like to enable X-Ray tracing on the function(s) in your application? [y/N]:
    • n
    • ※ X-Ray は Lambda の監視をするための設定ですが、今回は設定しないようにします。
  • Project name [sam-app]:
    • tutorial-app-back


以下のような結果になります。

$ sam init

You can preselect a particular runtime or package type when using the `sam init` experience.
Call `sam init --help` to learn more.

Which template source would you like to use?
        1 - AWS Quick Start Templates
        2 - Custom Template Location
Choice: 1

Choose an AWS Quick Start application template
        1 - Hello World Example
        2 - Multi-step workflow
        3 - Serverless API
        4 - Scheduled task
        5 - Standalone function
        6 - Data processing
        7 - Infrastructure event management
        8 - Machine Learning
Template: 3

Which runtime would you like to use?
        1 - dotnet6
        2 - dotnetcore3.1
        3 - nodejs16.x
        4 - nodejs14.x
        5 - nodejs12.x
        6 - python3.9
        7 - python3.8
Runtime: 3

Based on your selections, the only Package type available is Zip.
We will proceed to selecting the Package type as Zip.

Based on your selections, the only dependency manager available is npm.
We will proceed copying the template using npm.

Would you like to enable X-Ray tracing on the function(s) in your application?  [y/N]: n

Project name [sam-app]: tutorial-app-back

Cloning from https://github.com/aws/aws-sam-cli-app-templates (process may take a moment)

    -----------------------
    Generating application:
    -----------------------
    Name: tutorial-app-back
    Runtime: nodejs16.x
    Architectures: x86_64
    Dependency Manager: npm
    Application Template: quick-start-web
    Output Directory: .

    Next steps can be found in the README file at ./tutorial-app-back/README.md


    Commands you can use next
    =========================
    [*] Create pipeline: cd tutorial-app-back && sam pipeline init --bootstrap
    [*] Validate SAM template: sam validate
    [*] Test Function in the Cloud: sam sync --stack-name {stack-name} --watch


最新の SAM では以下のエラーが出る方がいらっしゃるかと思います。
これは Windows のロングパス設定によるもので、 SAM init を実行するにはこれを ON にする必要があります。

Cloning from https://github.com/aws/aws-sam-cli-app-templates (process may take a moment)Error: Unstable state when updating repo. Check that you have permissions to create/delete files in C:\Users\xxxxx\AppData\Roaming\AWS SAM directory or file an issue at https://github.com/aws/aws-sam-cli/issues

上記エラーが発生した場合は↓の手順を実施してみてください

◆---------- SAM init エラー対応(クリックで開く) ----------◆

PowerShell を管理者モードで開きます。 VSCode からであれば、ターミナルの「+」マークの右横をクリックし、「PowerShell」を開きます。


以下のコマンドを打つと、 PowerShell が管理者モードで別ウィンドウで開きます。

Start-Process PowerShell.exe -Verb runas


管理者モードの PowerShell で以下のコマンドを実行すると、 Windows のロングパスの設定を ON にすることができます。

New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" `
>> -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force

※レジストリエディタで「HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem」の「LongPathsEnabled」を 1 にしても OK です


参考
Maximum Path Length Limitation - Win32 apps | Microsoft Learn


この設定が終わったら、あらためて「SAM init」を実行してみてください。

◆---------- SAM init エラー対応 ここまで ----------◆


3-2. API デプロイ

VSCode のターミナルで作業します。

無事にコマンドが終了すると、プロジェクトのディレクトリが作成されます。
以下のコマンドで、ビルドとデプロイをしてみましょう。

# /c/work/tutorial-app/ で実行
cd tutorial-app-back
# ビルド
sam build


ここで、Lambda 用のライブラリのインストールやパッケージ化が行われるので、少し時間がかかります。

ビルドが終わったら、いよいよデプロイしましょう。

# デプロイ
sam deploy --guided


選択肢は以下のものを選んでみましょう。

  • Stack Name [sam-app]:
    • tutorial-app-back-stack
    • ※CloudFormation で作成されるスタック名です
  • AWS Region [ap-northeast-1]:
    • そのまま Enter
    • ※デプロイするリージョンです。そのまま「ap-northeast-1(東京)」で良いので Enter です
  • Confirm changes before deploy [y/N]:
    • n
    • ※deploy 前に変更を確認するアクションを入れるかどうかです。ひとまず No としておきます
  • Allow SAM CLI IAM role creation [Y/n]:
    • y
    • ※IAM Role を作成することを許可するかどうかです。作成したいので Yes です
  • Disable rollback [y/N]:
    • n
    • ※ロールバックを許可しない設定です。失敗したらロールバックさせたいので No にします
  • getAllItemsFunction may not have authorization defined, Is this okay? [y/N]:
    • y
    • ※ここから 3 つは、それぞれの API に認証がついていないが良いか?というものです。後でつけるので、今は Yes にしておきます
  • getByIdFunction may not have authorization defined, Is this okay? [y/N]:
    • y
  • putItemFunction may not have authorization defined, Is this okay? [y/N]:
    • y
  • Save arguments to configuration file [Y/n]:
    • y
    • ※今回の設定を設定ファイルに保存するかどうか。保存するので Yes です
  • SAM configuration file [samconfig.toml]:
    • そのまま Enter
    • ※設定ファイルの名前です。デフォルトの「samconfig.toml」でよいのでそのまま Enter です
  • SAM configuration environment [default]:
    • dev
    • ※今回のデプロイ設定の環境名です。default のままでもよいですが、ここは「dev」としておきます


以下のような結果になるはずです。

$ sam deploy --guided

Configuring SAM deploy
======================

        Looking for config file [samconfig.toml] :  Not found

        Setting default arguments for 'sam deploy'
        =========================================
        Stack Name [sam-app]: tutorial-app-back-stack
        AWS Region [ap-northeast-1]: 
        #Shows you resources changes to be deployed and require a 'Y' to initiate deploy
        Confirm changes before deploy [y/N]: n
        #SAM needs permission to be able to create roles to connect to the resources in your template
        Allow SAM CLI IAM role creation [Y/n]: y
        #Preserves the state of previously provisioned resources when an operation fails
        Disable rollback [y/N]: n
        getAllItemsFunction may not have authorization defined, Is this okay? [y/N]: y
        getByIdFunction may not have authorization defined, Is this okay? [y/N]: y
        putItemFunction may not have authorization defined, Is this okay? [y/N]: y
        Save arguments to configuration file [Y/n]: y
        SAM configuration file [samconfig.toml]: 
        SAM configuration environment [default]: dev

########## 中略 ##########

Successfully created/updated stack - tutorial-app-back-stack in ap-northeast-1


マネジメントコンソールで作業します。
以下のものができあがっていることを確認しましょう。

◆CloudFormation -> スタック
「tutorial-app-back-stack」という名前の Stack

◆DynamoDB -> テーブル
「tutorial-app-back-stack-SampleTable-<ランダム文字列>」という名前のテーブル

◆API Gateway -> API
「tutorial-app-back-stack」という名前の API

◆Lambda -> アプリケーション
「tutorial-app-back-stack」という名前のアプリケーション

◆Lambda -> 関数
「tutorial-app-back-stack-getAllItemsFunction-<ランダム文字列>」
「tutorial-app-back-stack-getByIdFunction-<ランダム文字列>」
「tutorial-app-back-stack-putItemFunction-<ランダム文字列>」
という名前の関数

◆IAM -> Role
「tutorial-app-back-stack-getAllItemsFunctionRole-<ランダム文字列>」
「tutorial-app-back-stack-getByIdFunctionRole-<ランダム文字列>」
「tutorial-app-back-stack-putItemFunctionRole-<ランダム文字列>」
という名前のロール


このアカウント・リージョンで初めてデプロイする場合は、デプロイ用の S3 バケットも作成されているはずです。
このバケットに、デプロイに必要なものがアップロードされます。

◆CloudFormation -> スタック
「aws-sam-cli-managed-default」という名前の Stack

◆S3 -> バケット
「aws-sam-cli-managed-default-samclisourcebucket-<ランダム文字列>」という名前のバケット

3-3. API 動作確認

デプロイした API の動作確認をしてみましょう。
マネジメントコンソールで作業します。

◆API Gateway -> API -> tutorial-app-back-stack

GET -> テスト をクリックします。


「テスト」ボタンをクリックすると、「tutorial-app-back-stack-getAllItemsFunction-<ランダム文字列>」関数が実行されて、結果が取得されます。
※まだ DynamoDB にデータが入っていないので、空([])です


今度は POST -> テスト をクリックします


リクエスト本文に以下を入力し「テスト」ボタンをクリックすると、「tutorial-app-back-stack-putItemFunction-<ランダム文字列>」関数が実行されて、結果が取得されます。

※リクエスト本文

{
    "id": "id1",
    "name": "name1"
}


DynamoDB にもデータが登録されているかどうか確認しましょう。

◆DynamoDB -> テーブル -> tutorial-app-back-stack-SampleTable-<ランダム文字列>
テーブルアイテムの検索 をクリックします。


データが登録されていることが確認できます。


もう一度 GET でテストをして、今度はデータが返ってくることを確認します。


※Postman などを利用してデータの登録 / 確認をすることもできます
www.postman.com



API を利用してデータが登録/取得できることを確認できました。

3-4. リポジトリに登録

ここまでのデータを最初に作成したリポジトリに Push しておきましょう。
VSCode のターミナルで作業します。
バックエンド用ディレクトリ(C:\work\tutorial-app\tutorial-app-back)にいる前提で進めていきます。

以下のコマンドを実行して git リポジトリとして初期化します。

# /c/work/tutorial-app/tutorial-app-back/ で実行
git init


SAM で作成したファイルのうち、 git 管理から外したいファイルを「.gitignore」ファイルに追記します。
以下のコマンドを実行してください。

echo .aws-sam/ >> .gitignore
echo samconfig.toml >> .gitignore


以下のコマンドで、管理対象ファイルを確認します。

git status


.aws-sam/ と samconfig.toml が入っていないことを確認して、以下コマンドで全てを Add / Commit します

git add .
git commit -m "first commit"


以下のコマンドで、ローカルリポジトリとリモートリポジトリを紐づけます

git remote add origin https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/tutorial-app-back

※「https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/tutorial-app-back」の部分は、 リポジトリ作成時に確認した back 用のリポジトリの HTTPS パスを指定してください


長らく git のメインブランチは master でしたが、今後は main という名前を主流とすることになっています。
参考
The default branch for newly-created repositories is now main | GitHub Changelog


デフォルトだとブランチが「master」になっていますので、以下のコマンドで「main」に変えておきましょう。

git branch -m main


以下のコマンドで、リモートリポジトリに Push します。

git push origin main


マネジメントコンソールで作業します。
ファイルがアップされていることを確認します。
◆CodeCommit -> ソース -> リポジトリ -> tutorial-app-back

3-5. API 設定を分離

この後認証設定などをやりやすくするため、 API の作りをデフォルトから少し変えておきます。
VSCode のターミナルで作業します。
バックエンド用ディレクトリ(C:\work\tutorial-app\tutorial-app-back)にいる前提で進めていきます。


template.yaml を修正します。

  • getAllItemsFunction / getByIdFunction / putItemFunction それぞれの Events の最後に 2 行「RestApiId」設定を追加する
  • SampleTable の後、Outputs の前に API Gateway の設定を追加
  • Outputs の WebEndpoint の値を変更

template.yaml

# getAllItemsFunction / getByIdFunction / putItemFunction
# それぞれの Events の最後に 2 行追加する
  getAllItemsFunction:
    Type: AWS::Serverless::Function
    Properties:
# ~ 略 ~
      Events:
        Api:
          Type: Api
          Properties:
            Path: /
            Method: GET
            RestApiId:    # 追加
              Ref: SampleApi    # 追加

# ~ 略 ~

  SampleTable:
    Type: AWS::Serverless::SimpleTable
    Properties:
      PrimaryKey:
        Name: id
        Type: String
      ProvisionedThroughput:
        ReadCapacityUnits: 2
        WriteCapacityUnits: 2

# ↓ ----- 追加 ----- ↓
  # --------------------
  # API Gateway
  # --------------------
  SampleApi:
    Type: AWS::Serverless::Api
    Properties:
      Name: "tutorial-app-api"
      StageName: dev
      OpenApiVersion: 3.0.2
      EndpointConfiguration: REGIONAL
# ↑ ----- 追加 ----- ↑

Outputs:
  WebEndpoint:
    Description: "API Gateway endpoint URL for dev stage"    # 変更
    Value: !Sub "https://${SampleApi}.execute-api.${AWS::Region}.amazonaws.com/dev"    # 変更


template.yaml の変更が終わったら、ビルド/デプロイをします。

sam build
sam deploy --config-env dev

さきほど sam deploy --guided でデプロイした際の「SAM configuration environment」で設定した環境名を引数に入れることで、その時作成した設定ファイルでデプロイされます。
※環境名を default で作成した場合は「sam deploy」のみで OK

3-6. 修正後のバックエンド API 動作確認

デプロイが完了したら、API Gateway の画面で動作確認をしておきましょう。
マネジメントコンソールで作業します。

◆API Gateway -> API
「tutorial-app-api」という名前の API を開きます。(最初の API とは名前が変わっていますので注意)

POST -> テスト をクリックして、リクエスト本文に以下を入力し、「テスト」ボタンをクリックしてデータを登録しましょう。
※リクエスト本文

{
    "id": "id2",
    "name": "name2"
}


次に、 GET -> テスト -> テスト をクリックして、結果が返ってくることを確認しましょう。


DynamoDB にもデータが登録されているかどうか確認しましょう。
◆DynamoDB -> テーブル -> tutorial-app-back-stack-SampleTable-<ランダム文字列>
テーブルアイテムの検索 をクリックして、データが登録されていることを確認します。


修正後の API でもデータが登録/参照できることが確認できました。


ここまでをリポジトリに Push しておきましょう。

git add template.yaml
git commit -m "API を共通化"
git push origin main

バックエンドの API 実装が完了しました。
次回はフロントエンドを実装してきます。

4. 参考

AWS CloudFormation(テンプレートを使ったリソースのモデル化と管理)| AWS
AWS サーバーレスアプリケーションモデル - アマゾン ウェブ サービス


Flutter環境構築(Windows) 後編 - エミュレータ作成と実行


みなさん、こんにちは。イノベーションLABのハヤシです。
イノベーションLABでは今期から、社内の技術開拓に力を入れています。
その中で挑戦した Flutter の環境構築について記事にしていきます。


今回は後編として、Android エミュレータの作成と、プロジェクト作成・実行について解説していきます。


前編では、 Flutter SDK / Android Studio などのインストールを解説しています。
この記事の内容は、前編の内容まで完了していることを前提にしています。


前編はこちら👇
blog.css-net.co.jp

前提条件

Windows 10 Pro 64bit
プロセッサ:Intel Core i7
Flutter: 3.0.0-stable
Android Studio: Chipmunk 2021.2.1 for Windows 64-bit
Visual Studio: Community 2022 (17.2.1)
※Flutter の環境構築(前編の内容)が実施済み

Android エミュレータ作成

Android Studio を起動し、「More Actions」から「Virtual Device Manager」を選択します

「Create device」をクリックします

任意のデバイスを選択して「Next」をクリックします

任意のバージョンの「Download」をクリックしてダウンロードします

ダウンロードが完了したら、「Next」をクリックします

「Finish」をクリックします

三角マークをクリックすると、デバイスが起動します

※Detected ADB という Warning が出るので、解決していきます

ADB のパスが認識できないとのことなので、 adb.exe のパスを設定に追加します
※今までの手順通りでは「C:\Users\<ユーザ名>\AppData\Local\Android\Sdk\platform-tools」にあります

Emulator のメニューから設定を開き、「Settings」 => 「Use detected ADB location」のトグルボタンを OFF にし、ファイル選択ダイアログから adb.exe のパスを設定します

Emulator を停止し、再度起動すると Warning ダイアログが表示されなくなります

プロジェクト作成と実行(Android Studio)

アプリの作成と実行

Android Studio を起動して「New Flutter Project」を選択します

Flutter SDK path に「C:\src\flutter」を入力し、「Next」をクリックします

Project name にプロジェクト名、 Project location にプロジェクトを配置したいパスを指定し、web アプリを作成したい場合は Platforms の Web にもチェックを入れて、「Finish」をクリックします

ディレクトリ作成の確認が出たら「create」をクリックします

デフォルトのコードが作成されます

上部のデバイスマーク(「<no device selected>」と表記されている個所)で「Chrome(Web)」を選択し、三角の再生ボタンをクリックすると、 Chrome が立ち上がり、デフォルトのアプリが起動します

Android Emulator で起動する場合は右上のデバイスマークをクリックし、起動したい Emulator で三角の再生ボタンをクリックすると Emulator が起動します

デバイスが mobile になっていることを確認し、右上の三角マークをクリックすると、 Emulator でアプリが起動します(初回は 10 分程度かかりました...)

プロジェクト作成と実行(VSCode)

Flutter プラグインインストール

VSCode を起動して、サイドバーの拡張機能マークまたは「Ctrl + Shift + x」で拡張機能メニューを開き、
「flutter」で検索して「Flutter」の拡張機能をインストールします

アプリの作成と実行

「Ctrl + Shift + p」でコマンドパレットを開き、「flutter」と検索し、「Flutter: New Project」をクリックします

「Application」を選択します

プロジェクトを作成したいフォルダを開き、「Select a folder to create the project in」をクリックします

作成するプロジェクト名と Enter を入力します

デフォルトのコードが作成されます

右下の「No Device」をクリックし、「Chrome」を選択します

右下の表示が「Chrome」になったことを確認し、F5 を押下(または右上の三角マークをクリック)します

Chrome が立ち上がり、デフォルトのアプリが起動します

※右上の三角マークの右横をクリックし、「Start Debugging」でデバッグ実行、「Rub Without Debugging」でデバッグなし実行ができます
※F5 でデバッグ実行、 Ctrl + F5 ででバックなし実行もできます

Android Emulator で起動する場合は右下のデバイス選択時に作成済みの Emulator を選択します
※「cold boot」は、電源 OFF 状態からの起動となります

web 同様 F5 押下(または右上の三角マークをクリック)すると、 Emulator でアプリが起動します

以上です。
これで Flutter 開発のスタートラインに立てました!
引き続き取り組んでいきたいと思います。

参考

developer.android.com
docs.flutter.dev
flutter.ctrnost.com