buto > /dev/null

だいたい急に挑戦してゴールにたどり着かずに飽きる日々です

Seleniumをtypescriptで使ってみる

画面開発でコーディングと動作確認を交互にしていると 毎回フル桁を入力して、ボタン押下して、結果確認して…の流れが結構辛い。

ということで、javascriptコードでブラウザ操作を自動実行してくれるseleniumを始めよう!(今更感)

seleniumインストール

何事もまずはnpm installから!!

npm install selenium-webdriver

次にseleniumが使うブラウザドライバーをダウンロードします

www.selenium.dev

ダウンロードしたドライバーはプロジェクトのルートディレクトリに配置しておきます

TypeScriptでブラウザ操作コードを書く

シンプルなログイン画面にID、パスワードを入力してログインボタンを押下してみます

まずはtsファイルを作成してお作法コードを書きます インストールしたseleniumを使うよ!宣言とchromeの設定です

const webdriver = require('selenium-webdriver');
const { Builder, By, until } = webdriver;
const assert = require('assert');

const capabilities = webdriver.Capabilities.chrome();
capabilities.set('chromeOptions', {
    args: [
        '--headless',
        '--no-sandbox',
        '--disable-gpu',
        `--window-size=1980,1200`
    ]
});

次にテキストボックスへの入力、ボタン押下、結果のassertを行います

/**
 * ログイン成功
 */
(async () => {
    const driver = await new Builder().withCapabilities(capabilities).build();
    await driver.get("http://localhost:3000");
    // IDパスワードを入力
    let userId = await driver.findElement(By.id('loginId'));
    userId.sendKeys("xxxxx");
    let password = await driver.findElement(By.id('password'));
    password.sendKeys("xxxxx");
    // ログイン
    let loginButton = await driver.findElement(By.id('loginButton'));
    loginButton.click();

    await driver.wait(until.elementLocated(By.id('paymentList')), 10000);
    // 支払い一覧ページへ遷移していること
    assert.strictEqual(await driver.getCurrentUrl(), "http://localhost:3000/payment");
    driver.quit();
})();

あらかじめ npm run serve で開発サーバにアプリをホスティングしておき

node loginTest.ts で上記コードを実行しましょう

自動でchromeが起動してログインを再現してくれます!!

Flutter インストールからメニュー表示まで

久しぶりにモバイルアプリを作ってみたくなったのでflutter始めました

iOSAndroidを同じコードで開発できるので効率的です

Flutterインストール

まずはhomebrewでFlutterをインストールしてdoctorコマンドでFlutterの利用開始チェックをする

XcodeとAndroidStudioが入っていればチェックOKなはず)

brew install flutter
flutter doctor

Flutterプロジェクト作成

インストール完了したらFlutterプロジェクトを作成します

--org オプションでパッケージ名を指定できます(指定なしだと com.example となる)

flutter create --org package project_name

プロジェクトが作成できたら標準出力に従ってアプリを起動!

cd project_name
flutter run

プロジェクト作成直後の画面 カウントアップボタンのみがあるシンプルアプリ

f:id:butorisa:20220327165430p:plain

画面をカスタマイズ

↑画面コードは project_name/lib/main.dart に書かれているのでこちらを修正していく

まずはこんな感じで画面を修正しました

  • アプリタイトルを変更
  • AppBarの表示文字・カラーを変更
  • サイドメニュー(Drawer)を追加
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'framboise nail', // ブラウザタブ表示名
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const MyHomePage(title: 'my page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
        backgroundColor: Colors.pinkAccent.shade700,
      ),
      drawer: Drawer( // サイドメニュー
        child: ListView(
          children: <Widget>[
            DrawerHeader(
              child: const Text(
                'risa',
                style: TextStyle(
                  fontSize: 24,
                  color: Colors.white
                ),
              ),
              decoration: BoxDecoration(color: Colors.pinkAccent.shade700),
            ),
            const ListTile(
              title: Text('カタチから探す'),
            ),
            const ListTile(
              title: Text('スクエア'),
              trailing: Icon(Icons.square),
            ),
            const ListTile(
              title: Text('ラウンド'),
              trailing: Icon(Icons.circle),
            ),
            const ListTile(
              title: Text('オーバル'),
              trailing: Icon(Icons.egg),
            ),
            const ListTile(
              title: Text('ポイント'),
              trailing: Icon(Icons.water_drop),
            )
          ],
        ),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: const <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
          ],
        ),
      ),
    );
  }
}

このようにプロジェクト作成直後から画面をカスタマイズできました!

サイドメニューは利用頻度が高そうなのでサラッとコード書けるようにしておきたい

f:id:butorisa:20220327215000p:plain

f:id:butorisa:20220327215027p:plain

ちょっとしたカスタマイズですが、何時間も掛かってしまいました…

webpack フロントエンドでも環境変数を扱う

React+SpringBootアプリでローカル、ステージング、本番環境用に環境変数ファイルがあって、それぞれに接続するAPIアドレスなどが定義されているのはよくあるケース

SpringBootで環境変数を読み込むのは何度も行ってきましたが、Reactで環境変数にアクセスしたことなかった!

今回フロントエンドはwebpack+Reactだったのでwebpackを使って環境変数を読み込む

そもそもwebpackの話

web資材(js、css、画像などのファイル)をpacking(まとめる)するツール

この記事のようにindex.jsなどのエントリポイントから参照される各モジュール(画面コンポーネントなど)をひとまとめにできる!

まとめる設定をwebpack.config.jsファイルに記述する

goworkship.com

DefinePluginで環境変数を読み込む

あらかじめlocal.envなどの環境変数ファイルをサーバ内に環境変数として読み込まれるようにしておく

今回はdockerだったのでdocker-compose.ymlにビルド実行するコンテナのenv-filesにlocal.envを指定した

webpack.config.jsにDefinePluginを記述して環境変数をフロントエンドのグローバル変数に置き換える

local.envに定義したAPI_GATEWAYはprocess.env.でアクセスしグローバル変数API_HOSTに格納

const webpack = require('webpack');

module.exports = {
  mode: 'development',
  plugins: [
    new webpack.DefinePlugin({
      'API_HOST': JSON.stringify(process.env.API_GATEWAY || "")
    }),
  ]
};

実際にAPI接続するモジュールで declear const API_HOST: string; と記述すると環境変数の値が利用できる

webpack.js.org

環境変数を扱う方法にはdotenv、dotenv-webpackもあるがライブラリを追加したくない場合はこの方法が使える

AWSメッセージキュー SQS

昨日はSNSがモバイル通知、分析用Firehorseなど複数の宛先へデータを流すことができると学んだので

今日はSQSの特徴を学んでいく!!(SESはEメール通知専用っていうのは知ってるからスキップ)

Amazon SQS

f:id:butorisa:20220215202733p:plain

SNSのアイコンと比べると宛先は1つになる?中央の四角がキューを表している??

ya6mablog.com

貯まったメッセージは受信側(Consumer)がキューに問い合わせを行うと、キューからメッセージが配信されて、受信側はメッセージを取得することが出来ます。

SNSはメッセージ発行元(publisher)から一方通行でデータが流れたけど

SQSはpublisherがキューにメッセージを入れて、consumerがポーリングでキューからメッセージを取り出すようだ

SQSを試してみる

f:id:butorisa:20220215204800p:plain

マネジメントコンソールでSQS作成をしてみると、スタンダードとFIFOが選べる

配信順序を気にしなければスタンダードで良さそう(FIFOの方が割高)

f:id:butorisa:20220215205045p:plain

スタンダードキューを作成してみたのでメッセージの送受信をクリックしてみた

メッセージ本文を入力してメッセージ送信をクリック!

f:id:butorisa:20220215205146p:plain

f:id:butorisa:20220215205253p:plain

メッセージがキューに送られたので利用可能なメッセージが1となった

f:id:butorisa:20220215205451p:plain

自動でポーリングされないようだったのでメッセージをポーリングをクリックするとポーリングが開始された

f:id:butorisa:20220215205537p:plain

メッセージが届きました!

SNSと連携

SQSのメニューにSNSサブスクライブとあったので昨日作成したSNSトピックを紐付けてみた

SNSで「わああああああ〜!」とメッセージを発行してみると

f:id:butorisa:20220215210707p:plain

昨日作成したSMS通知がちゃんと届いたのと同時にSQSにもメッセージが渡ってきた!

f:id:butorisa:20220215210931p:plain

f:id:butorisa:20220215211256p:plain

SNSからSQSにメッセージを渡せる機能なのか!

EC2上のアプリからSNSで通知+SQSにデータを流してSQSから他のEC2アプリにデータを連携する時使えそう!

SQSはアプリ間連携に使えるメッセージキューということが分かった

AWSメッセージキュー SNS

AWS資格試験の模擬テストにメッセージキューが頻出するけど違いが分からない、覚えられないのでまとめておく。

AWS複数種類のメッセージキュー(MQ)が提供されているから1つずつ覚えていきます!

メッセージキューってなに??

メールやSMSメッセージ通知をする時にメッセージ作成アプリ→配信アプリに橋渡しをするキュー

メッセージキューって名前だけどメッセージ配信以外のアプリ間データ連携に利用されている

直接アプリ同士で連携してもいいけど、大量データだとアプリサーバに負荷が掛かっちゃうのでMQはよく使われるらしい

キューなので渡されたデータから処理されていく(先入先出)

Amazon SNSとは

f:id:butorisa:20220214232645p:plain

サービスアイコンは左側から入ったデータがフィルタされて複数に流れるような

Amazon Simple Notification Serviceって名前の通り通知(SMS・プッシュ通知)によく使われる

SNSスマホにメッセージ送信

f:id:butorisa:20220214235359p:plain

トピック(後述)のタイプをスタンダードにするとSMS・プッシュ通知などのメッセージ通知ができ

FIFOにするとSQSのみ選択できた(こちらはアプリ間データ連携に使う時に選択するっぽい)

トピックを作成しなくても左メニューSMSを選択して電話番号を登録

ワンタイムパスワードで認証後、テストSMSが送信できる

f:id:butorisa:20220214235047p:plain

今度はスタンダードタイプのトピックから送ってみる

トピックを選択してSMS送信をサブスクリプションに設定すると、こんなメッセージが届いた(一番下)

f:id:butorisa:20220215000402p:plain

トピックから送信するとメッセージの先頭に「トピック名>」って付くみたい

アプリからSMS通知するには

携帯電話に発行する - Amazon Simple Notification Service

公式ドキュメントにコードから電話番号を動的に渡してAmazon SNSからSMS通知できるみたい

あらかじめトピックを作っておかなくてもSMSが送れるように見える

Amazon SNS トピックを作成する - Amazon Simple Notification Service

トピックは配信先のグループなので「メッセージ配信+配信内容を分析するRedshiftにデータ登録」を実現したい時に

トピックのサブスクリプションにSMS通知、kinesisFirehorseを登録してあげればトピックでメッセージ発行をすると

SMS通知が届き、一方ではFirehorse経由でRedshiftにデータが登録されました〜となる

Grid systemを思い出す

今ドキのレスポンシブな画面ではGridsystemが定番!だと思いますが、すぐに忘れちゃうので復習

css-tricks.com

html+bootstrapで簡単な検索フォームを作ってみます

f:id:butorisa:20220106225926p:plain

<!DOCTYPE html>
<html lang="en" dir="ltr">

<head>
  <meta charset="utf-8">
  <title>データ検索</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
</head>

<body>
  <div class="container m-4">
    <div class="row">
      <div class="col">
        <h4>データ検索</h4>
      </div>
    </div>
  </div>
  <div class="container m-4">
    <div class="row justify-content-start">
      <div class="col-md-2">
        <h6>カテゴリ</h6>
      </div>
      <div class="col-md-2">
        <h6>在庫</h6>
      </div>
    </div>

    <div class="row justify-content-start">
      <div class="col-md-2">
        <select class="form-select" aria-label="">
          <option selected>飲み物</option>
          <option value="1">お菓子</option>
          <option value="2">雑貨</option>
        </select>
      </div>
      <div class="col-md-2 align-self-end">
        <div class="form-check">
          <input class="form-check-input" type="checkbox" value="" id="flexCheckDefault">
          <label class="form-check-label" for="flexCheckDefault">
            在庫ありのみ
          </label>
        </div>
      </div>
    </div>

    <div class="row justify-content-start mt-2">
      <div class="col-md-4">
        <h6>商品名</h6>
        <input type="text" name="" value="" class="form-control">
      </div>
    </div>

    <div class="row justify-content-start mt-2">
      <div class="col-md-3"></div>
      <div class="col-md-1 text-end">
        <button type="button" class="btn btn-outline-primary">検索</button>
      </div>
    </div>

  </div>
</body>

</html>

分かりづらいですが、紫がrow・黄色がcolです

f:id:butorisa:20220106231529p:plain

最上部の項目、カテゴリと在庫をlabelとinputでrowを分けたのはチェックボックスを下寄せにするためです!

(rowを分けないと在庫のラベル表示が左のカテゴリのラベル表示とズレちゃいます)

検索ボタンの右寄せは空の col-md-3 分のdivを定義して無理やりぽいですが実現しています

(もうちょっとスマートな方法ないかな…?)

Webhookってなんだっけ??

先日RDS同士のデータ連携について調べたので打ち合わせで「調べました!」と報告すると「あとWebhookもありだよね」と言われました

あれ?Webhookってなんだったっけ…??

更新情報を他アプリにリアルタイム提供

qiita.com

GitHubにプッシュしたらCodePipelineが動き出す仕組み!JIRA更新した時のslack通知!

そういえば前にRedmine(EC2)でチケット更新したらslack通知する設定した記憶が…

あれがWebhookだったのか

RDS同期への応用方法

dev.classmethod.jp

理想はこうなんだけど

  1. RDSが更新されたらEventBridgeで更新を検知

  2. 更新されたテーブル名、CRUD区分、更新データをlambda引数に渡しもう一方のRDSを更新

EventBridgeでRDS更新は検知できなさそう??(RDSイベントになさそう)

EventBridgeからlambdaに更新したテーブル名とかデータとか渡せるんだろうか??

引数設定を入力トランスフォーマーにしたらいけるかもって思ったけど、入力トランスフォーマーに渡す値はイベントテキスト(定数json)だから無理かも…

時間ある時に苦戦してみるか笑