OmniAICreator/Qiita-1.07M huggingface dataset:
SEO対策の一貫で画像の動的リサイズシステムを実装した

こんにちは。サーバーサイド/webフロントエンジニアをやっている @cappyns です。
今回は https://www.sakeai.com/ のSEO対策をするにあたって画像の動的リサイズシステムを実装したので、そちらについて紹介しようと思います。

# モチベーション
Sakeaiはユーザーが飲んだ日本酒の口コミを投稿するプラットフォームです。アプリでは口コミを投稿/閲覧することができ、Webでは投稿された口コミを閲覧することができるようになっています。

<img width=800 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321028/7b49bef3-e2a9-e6fb-20fc-378d34e70ac3.png">

当然、口コミを表示する際にはユーザーがアップロードした画像も一緒に表示するわけですが、近年のスマホの進化によりユーザーがアップロードする画像の解像度はとても高いです。Webでこれをこのまま表示してしまうと画像のレンダリングに時間がかかり、SEO観点で非常によろしくありません。

解決策としてユーザーが画像をアップロードした時に自動で必要なサイズの画像を生成し、別途保存しておくという手段も考えられました。しかし、その手段では表示箇所によって異なる必要なサイズを全て保存しておかなくてはいけない上、デザイン変更などで必要なサイズが変化した場合には再度全ての画像の新しいサイズを生成し直さなくてはなりません。

それはめんどくさいので今回は保存する画像は1種類にしつつ、アクセス時に動的に画像のサイズを変化させる仕組みを実装しました。

# 仕様
実装したシステムでは以下のように画像の保存場所やサイズ、リサイズ方法をURLのpathとして表現できるようになっています。
```
https://img.sakeai.com/[保存場所(サーバー)]/[保存場所(フォルダ)]/[リサイズ方法]/[幅]/[高さ]/[ファイル名]
```

実際に使っている画像で見てみると、同じ画像でもpathによってサイズやリサイズ方法が変わっていることがわかります。

1. https://img.sakeai.com/s3/images/cover/300/300/IXNuyl7y.jpg
1. https://img.sakeai.com/s3/images/cover/100/200/IXNuyl7y.jpg
1. https://img.sakeai.com/s3/images/contain/300/300/IXNuyl7y.jpg

# アーキテクチャ
基本的にはかなり簡単な構成となっていて、s3の画像をlambdaで処理してCloudFront、API Gatewayを経由して配信する形になっています。
少し特殊な点としてSakeaiでは歴史的経緯によって画像が一部XServerに配置されたままになっています。そこでlambdaではアクセス時のパスを見てs3から画像を取得するのか、XServerから画像を取得するのか制御するように実装しています。

<img width=800 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321028/bb6c0862-5c87-e21e-c204-a469c4b6eea9.png">

# 費用感
この記事を書いている時点でこのシステムには1日あたり1000リクエスト程度来ていますが、$0.3/monthくらいのコストに収まっています。これからもっとリクエストが増えたとしても全然許容できそうです。

# 最後に
このシステムによって気軽にいろんなサイズの画像を試すことができました。search console上の指標も上昇してきており、一安心です。
ぜひ画像のリサイズに困ったら参考にしてみてください。
結合テスト設計書の作成（前回の続き）

# 本日は結合テストの設計書を作成したのだ
前回は単体テストの設計をした！という話を書きましたが、
今日は、結合テストについてですわ

では、そもそも結合テストとは如何なるものか、
というところから確認していきましょう

# 「結合テスト」とは？
結合テストとは、単体テストを終えた各モジュールを組み合わせて、意図した通りの動作や挙動になっているかを確認するテストです。 一般的なウォーターフォールモデルの開発においては、プロジェクトの中盤から終盤にかけてさまざまな組み合わせの結合テストが複数回に渡って実施されます。

参考記事
https://products.sint.co.jp/obpm/blog/join-test#:~:text=%E7%B5%90%E5%90%88%E3%83%86%E3%82%B9%E3%83%88%E3%81%A8%E3%81%AF%E3%80%81%E5%8D%98%E4%BD%93%E3%83%86%E3%82%B9%E3%83%88,%E6%B8%A1%E3%81%A3%E3%81%A6%E5%AE%9F%E6%96%BD%E3%81%95%E3%82%8C%E3%81%BE%E3%81%99%E3%80%82

なるほど、わからん。

ということでいつもの如くわたしが理解している内容を確認してみます。

結合テストとは、
単体テストで行ったテスト内容を項目ごとにまとめて、
一連の動作をまとめた時に問題なく動作するか確認するテスト
ということであっていますでしょう

# 単体テストとの比較
データの作成を行うのテストの場合
・単体テスト
入力フォームに文字が入力できること
入力フォームに数字を入力して、追加ボタンを押した時、エラー文が表示されること
入力フォームに文字を入力していない状態で追加ボタン押下時、エラー文が表示されること

こんな感じ

・結合テスト
入力フォームに文字を入力した状態で追加ボタンを押下時、管理画面に遷移、追加したデータが表示されていること

こんな感じ

あっていますでしょうかね？

# 結合テストは単体テストより、楽しい！
というのが感想ですな

単体テストはゲームで言うところのデバック作業に近い
壁無限に当たりまくるやつ、やったことないけど

結合テストもデバックだが、
一連の流れ、ゲームで言うと、ひとつの村をクリアできるかテストする。

多分こんな感じやろ

ということで、今日はこんなところでしょう！

早寝早起きするんじゃよ

おわり
複数テーブルの結合

# はじめに
複数テーブルの結合についてのメモ

# 参考
https://www.amazon.co.jp/%E3%82%B9%E3%83%83%E3%82%AD%E3%83%AA%E3%82%8F%E3%81%8B%E3%82%8BSQL%E5%85%A5%E9%96%80-%E7%AC%AC3%E7%89%88-%E3%83%89%E3%83%AA%E3%83%AB256%E5%95%8F%E4%BB%98%E3%81%8D-%E3%82%B9%E3%83%83%E3%82%AD%E3%83%AA%E3%82%8F%E3%81%8B%E3%82%8B%E5%85%A5%E9%96%80%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-%E4%B8%AD%E5%B1%B1/dp/4295013390/ref=sr_1_1_sspa?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=DXLWG95FMIRS&keywords=SQL&qid=1694080827&sprefix=sql%2Caps%2C201&sr=8-1-spons&sp_csd=d2lkZ2V0TmFtZT1zcF9hdGY&psc=1

# テーブルの結合
結合条件が満たされた行を一つ一つ繋ぐこと

# 主な結合
## JOIN
* 使いどころ
  - 2つのテーブルの間で完全に一致する行だけを取得したい場合
* `INNER JOIN`ともかけるが、省略された`JOIN`を用いることが多い
* 繋ぐべき右表の行が複数あるとき、左表の行を複製して結合するため、元の左表の行数より増える

```sql
SELECT 
    orders.order_id, customers.customer_name 
FROM 
    orders 
JOIN customers 
    ON orders.customer_id = customers.customer_id;
```

## LEFT JOIN
* 使いどころ
    * 必ず左テーブルの情報は取得したい場合
    * 左テーブルに存在すれば、右テーブルにマッチする情報が存在しない場合でもNULLで取得する
* `LEFT OUTER JOIN`ともかけるが、省略された`LEFT JOIN`を用いることが多い

```sql
SELECT
    employees.emp_id, employees.emp_name, departments.dept_name
FROM
    employees
LEFT JOIN departments 
    ON employees.emp_id = departments.emp_id;
```

## RIGHT JOIN
* 使いどころ
    * 必ず右テーブルの情報は取得したい場合
    * 右テーブルに存在すれば、左テーブルにマッチする情報が存在しない場合でもNULLで取得する
* `RIGHT OUTER JOIN`ともかけるが、省略された`RIGHT JOIN`を用いることが多い
* LEFT JOINの逆


```SQL
SELECT
    users.name, orders.order_date, orders.order_amount
FROM
    users
RIGHT JOIN orders 
    ON users.user_id = orders.user_id;
```
【Git】差分を確認する

# はじめに
最近、pushする前に差分を確認しておきたいときに活用してます

# やりかた
developとmainの差分を確認したいとします

developに移動します
```:ターミナル
git checkout develop
```

以下のコマンドで差分を確認します
```:ターミナル
git diff main
```

# おわり
目指せgit操作マスター
IP（ネットワーク層）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457284/10a5fce4-6cbe-d31c-652c-f685a2aa0702.png)
　IP(internet Protocol)は、データの配送方法について規定したプロトコルです。インターネットではIPを利用してデータを宛先まで配送します。宛先の指定には、IPアドレスを用います。現在広く利用されているIPアドレスはIPv4（IPバージョン４）のIPアドレスで、３２ビットのアドレスです。しかし、IPv4のIPアドレスは全て割り当てずになってしまったので、次世代のIPv6（IPバージョン６）のIPアドレスを利用するように徐々に移ってきています。IPv6のIPアドレスは１２８ビットのアドレスですから、すべて割り当て切ってしまう心配はありません。
PR上に意図してないコミットが表示される時


## 背景

今回やりたい事としては、下記コミットをPRの表示から消すこと。

![スクリーンショット 2023-09-06 10.10.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2597943/bb3f37b1-dbe4-0f94-34fa-8fdb94998937.png)


どういう状態かというと…

間違えて、developの中にmasterをマージするという失態。

```jsx
               E---F feature/deleteTodo
              /
A---B---C---D master
A---B---C develop
```

↑ で D の部分が、PR に表示されているコミットで、develop にマージする必要がないもの。

 master ブランチから feature/deleteTodo ブランチを切り出して、develop にマージしようとしているため、master にしかないコミットが差分として PR 上に表示されていたらしい。

やりたい事としては、以下である。

```jsx
          E---F feature/deleteTodo
         /
A---B---C develop
A---B---C---D master
```

feature/deletetodoをmasterから切っていたので、これをorigin/developのブランチの最新のコミットをベースにリベース。

## 手段

今回のキーワードは二つ

1. `git rebase`
2. `force push`

今修正したいブランチは、feature/deleteTodoなので、ローカルのfeature/deleteTodo上で、

```jsx
git rebase origin/develop
```

とすることで、`feature/deleteTodo`ブランチのコミット履歴が`develop`ブランチの最新の状態に追いつく。

次に、

```jsx
git push -f origin feature/deleteTodo
```

上記コマンドを打つことで、変更された`feature/deleteTodo`ブランチをリモートリポジトリの`origin`に強制プッシュ。

## 結果

消えた！！！！

masterにしかないコミット履歴とかあるとそれが差分としてPRの時に表示されてしまうということを学んだ。

## 参考資料

https://qiita.com/hitochan777/items/ea08df354b42be57e5fc
DOMを利用してXMLを読み込むサンプル

JavaにはXMLを扱うライブラリがいくつか用意されています。そのうちDOMと呼ばれるライブラリを使ってXMLを読みこむサンプルをここでは紹介します。

:::note info
自分用のメモの側面があります。XMLを読み込むスクリプトを書くことがたまにあり、そのたびにググらないようにするための記事です。
:::

ECサイトの注文情報を記載したorder.xmlがあるとします。

```xml:order.xml
<?xml version="1.0"?>
<Orders>
	<Order Customer="高津一郎" Address="神奈川県川崎市高津区" Phone="0000-1111-2222">
		<Product>ボールペン</Product>
		<Price>100</Price>
		<Quantity>1</Quantity>
	</Order>
	<Order Customer="中原二郎" Address="神奈川県中原市中原区" Phone="1111-2222-3333">
		<Product>消しゴム</Product>
		<Price>150</Price>
		<Quantity>2</Quantity>
	</Order>
	<Order Customer="多摩三郎" Address="神奈川県川崎市多摩区" Phone="2222-3333-4444">
		<Product>はさみ</Product>
		<Price>200</Price>
		<Quantity>4</Quantity>
	</Order>
</Orders>
```

このorder.xmlに対して、以下の要件があるとします。

1. Order要素の属性すべてをPretty Printする
2. Order要素の子要素すべてをPretty Printする

これを実現するコードは以下の通りです。

```java
Path xmlPath = Paths.get("order.xml");
try (InputStream inputStream = Files.newInputStream(xmlPath)) {
	Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream);

	// ルート要素を取得する。
	Element orders = document.getDocumentElement();

	// ルート要素の直下にあるノードの一覧を取得する。
	NodeList orderList = orders.getChildNodes();
	for (int i = 0; i < orderList.getLength(); i++) {
		// Order要素を取得する。NodeListには要素以外も格納されているので、要素だけを取得するようにする。
		if (orderList.item(i).getNodeType() != Node.ELEMENT_NODE) {
			continue;
		}
		Element order = (Element) orderList.item(i);

		// Order要素の属性をすべて取得し、Pretty Printする
		NamedNodeMap attributes = order.getAttributes();
		for (int j = 0; j < attributes.getLength(); j++) {
			Attr attribute = (Attr) attributes.item(j);
			String name = attribute.getName();
			String value = attribute.getValue();
			System.out.printf("%s=%s%n", name, value);
		}

		// Order要素の直下にあるノードの一覧を取得する。
		NodeList orderDetailList = order.getChildNodes();
		for (int j = 0; j < orderDetailList.getLength(); j++) {
			// Order要素の直下にある要素を取得する。NodeListには要素以外も格納されているので、要素だけを取得するようにする。
			if (orderDetailList.item(j).getNodeType() != Node.ELEMENT_NODE) {
				continue;
			}
			Element orderDetail = (Element) orderDetailList.item(j);

			// Order要素の直下にある要素をPretty Printする。
			String tagName = orderDetail.getTagName();
			String textContent = orderDetail.getTextContent();
			System.out.printf("%s=%s%n", tagName, textContent);
		}

		System.out.println("------------------------------");
	}
} catch (IOException | SAXException | ParserConfigurationException e) {
	e.printStackTrace();
}
```

このプログラムを実行すると、以下のような結果が得られるはずです。

```
Address=神奈川県川崎市高津区
Customer=高津一郎
Phone=0000-1111-2222
Product=ボールペン
Price=100
Quantity=1
------------------------------
Address=神奈川県中原市中原区
Customer=中原二郎
Phone=1111-2222-3333
Product=消しゴム
Price=150
Quantity=2
------------------------------
Address=神奈川県川崎市多摩区
Customer=多摩三郎
Phone=2222-3333-4444
Product=はさみ
Price=200
Quantity=4
------------------------------
```

**環境情報**

```
D:\>javac -version
javac 17.0.3

D:\>java -version
openjdk version "17.0.3" 2022-04-19
OpenJDK Runtime Environment Temurin-17.0.3+7 (build 17.0.3+7)
OpenJDK 64-Bit Server VM Temurin-17.0.3+7 (build 17.0.3+7, mixed mode, sharing)
```
既存のコンピューティング環境に構築されたCloud9に自動停止機能をつける方法

# 前提
https://qiita.com/a_chaya/items/f9c3c58713539b3348a8

こちらの記事を元に、EC2上にCloud9環境を構築していること

# 手順
1. 以下のファイルを作成する
    ``` bash
    #!/bin/bash
    
    SSH_CONNECTIONS=$(ps -A x | grep sshd | grep \\[priv\\] | wc -l)
    INSTANCE_ID=$(ec2-metadata -i | grep -oP 'instance-id: \K\S+')
    TIMESTAMP=$(date --utc +%FT%T.%3NZ)
    
    aws cloudwatch put-metric-data --metric-name "SSHConnections" --dimensions InstanceId=$INSTANCE_ID --namespace "EC2" --value $SSH_CONNECTIONS --timestamp $TIMESTAMP
    ````

2. cronをインストールして起動時に有効化させる
    ```bash
    $ sudo dnf -y install cronie-noanacron
    $ sudo systemctl enable crond
    $ sudo systemctl restart crond
    $ crontab -e
    */1 * * * * (手順１で作成したファイルのフルパス)
    # ssh-count.sh の権限が775でなかったら、権限を変更してください。
    ```
3. 以下の条件でCloudwatch アラームを作成する
    *  グラフのメトリクス：先ほどのcronが実行された結果作成されたメトリクス
    *  メトリクスー統計：最小
    *  メトリクスー期間：1分
    *  条件ー閾値の種類：静的
    *  条件ーアラーム条件：以下（＜＝）
    *  条件ー閾値：0
    *  その他ーアラームを実行するデータポイント：15/15
    *  通知：（不要なので削除する）
    *  EC2アクションーアラーム状態のトリガー：アラーム状態
    *  EC2アクションーアクション：このインスタンスを停止
    *  アラーム名：（任意の名前）


これで15分SSH接続がないと自動的にEC2がシャットダウンされます！！
シャットダウンまでの時間を延ばしたい場合、アラームを実行するデータポイントの設定を変えてください。
Cloud9環境をAmazonLinux2023で構築する方法

# 背景
* Cloud9環境でReact開発をしている際にNode.jsのバージョンを18系に上げたい
* Cloud9が提供しているAmazonLinux2ではNode.js v18.xは対応していない（glibcのバージョン問題のため）
上記の2点を解消するために、AmazonLinux2023上にCloud9環境を構築することにしました。

# 手順
1. EC2インスタンスを作成する
    * AWSコンソールのEC2サービス画面の[インスタンスを起動]からインスタンスを作成する
        * ソフトウェアイメージ（AMI）：Amazon Linux 2023 AMI（ami-04beabd6a4fb6ab6f）
        * インスタンスタイプ：任意のもので可
        * キーペア：pemでもppkでもOK。作成してローカルにDLすること。
          ※ Windowsユーザーは自身のユーザーディレクトリの配下の任意の場所に置く必要あり。
          ※　macユーザーはキーペアファイルの権限を600にしておくこと。
        * セキュリティグループ：（SSHで接続できるようにしておく）
        * ストレージ
            * サイズ：40GB
            * ボリュームタイプ：gp3
            * IOPS：3000
            * 暗号化済み：暗号化済み
            * KMSキー：デフォルト

2. 作成したEC2にSSH接続してセットアップを行う
     
    ```bash
    # Node.jsのインストール
    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
    . ~/.nvm/nvm.sh
    nvm install v18.17.0 
    nvm alias default v18.17.0
    # pythonの設定
    echo "alias python=python3" >> ~/.bashrc
    source .bashrc
    curl -O https://bootstrap.pypa.io/get-pip.py
    sudo python3 get-pip.py 
    python -m pip --version
    rm get-pip.py
    pip install --upgrade pip==22.1.2
    pip install invoke==1.7.1
    pip install boto3==1.24.30
    # Cloud9のインストール
    sudo yum -y groupinstall "Development Tools"
    curl -L https://d3kgj69l4ph6w4.cloudfront.net/static/c9-install-2.0.0.sh | bash
    mkdir environment
    sudo chmod u=rwx,g=rx,o=rx ~/environment
    ```
3. Cloud9環境を作成する
    * AWSコンソールでCloud9のサービス画面の[環境を作成]をクリックする
    * 以下を設定して作成ボタンをクリックする
        * 名前：（任意設定）
        * 環境タイプ：既存のコンピューティング
            * ここで得られたSSHキーはあらかじめ作成したEC2の ~/.ssh/authorized_keys に追記しておく。
        * ユーザー：ec2-user
        * ホスト：（作成したEC2のパブリックDNSもしくはパブリックIP）
        * その他詳細（環境パス）：~/environment
DockerのAlpineでglibc問題を一発解決する

今更感はありますが、最近Docker始めました🐳

これまで何となく難しそう的な先入観でのらりくらり避けてきたわけですが、いざ触れてみたら思いのほか好奇心を掻き立てられてしまいました。

そんでもって、のらりくらりしてきた間の歴史や最近のトレンドを追ううちに、多分みなさんも一度は躓いたであろう問題に漏れなく僕もぶち当たり、無事洗礼を受けたというワケです。

今回は歴史の復習と解決した課題についてつらつらとメモっていきます。

# Alpineとlibc
とりあえずベースに軽いAlpineを使おうと思ったらアプリケーションが動かなくてゲッソリしたこと、あると思います。

その原因はC言語の共通ライブラリにあります。

一般的なLinuxディストリビューションの多くは [glibc](https://www.gnu.org/software/libc) (GNU C Library) を採用しています。
glibcは歴史が古く採用実績も多いですが、故に中身も肥大化してしまったため、モダンでスリムなglibcの移行先として [musl](https://www.musl-libc.org) が開発されました。

muslは後発なだけあって軽量高速ですがglibcとの互換性がありません。
流石にそれはあんまりとのことで互換レイヤも用意されましたが、あくまでオプション扱いで部分互換に留まります。

そして、軽量がウリのAlpineが採用したのはmuslでした。
ここで運命が分かたれてしまったのです。

muslの歴史は浅く、巷のアプリケーションはほとんどがglibcに依存した状態のため、幾多ものDockerユーザーに頭痛を引き起こしてきたという話に繋がるわけです。

C言語のアプリケーションではない、例えばNode.jsやRustでもこの問題にぶち当たるのは、それらの実行エンジンやコンパイラが内部でglibcを使用しているためです。

# Distroless
そんなこんなでAlpineって互換性イマイチじゃね？という風潮が出た辺りでGoogleが [Distroless](https://github.com/GoogleContainerTools/distroless) というAlpineに代わる便利な超軽量イメージをリリースしました。

Distrolessは名前からも想像できる通り、パッケージマネージャはおろかシェルすら削ぎ落としたストイックなイメージで、最低限の依存関係だけ入れておくから後はよろしくやってね、という感じです。

そしてDistrolessには幾つかバリエーションがあり、それは以下の通りです。

- 単品
- glibcを同梱
- glibcとlibsslを同梱
- glibcとlibsslとlibgccを同梱

そう、最初からglibcが同梱されているのです。
これを使えば、ノープロブレムでglibcに依存したアプリケーションを実行できます、やったね！

...
......
.........

# でもやっぱりAlpineを使いたい
しかし人生、シェルが入ってる親切なディストリビューションに色々と積み上げたい時だってあります。

最初はglibcをソースからビルドしたり、サードパーティのコンパイル済バイナリも試しましたが、どれもせっかくクリーンなベースイメージを複雑にしてしまい、腑に落ちませんでした。

そして頭を悩ませているうちに、天才的発想が舞い降りてきました😏

それがこちら。

```dockerfile:alpine.dockerfile
FROM gcr.io/distroless/cc-debian12:latest AS cc

FROM alpine:latest AS sym

COPY --from=cc --chmod=755 --chown=root:root /lib/*-linux-gnu/ld-linux-* /usr/local/lib/

RUN mkdir -p /tmp/lib
RUN ln -s /usr/local/lib/ld-linux-* /tmp/lib/

FROM gcr.io/distroless/static-debian12:latest

ENV LD_LIBRARY_PATH="/usr/local/lib"

COPY --from=cc --chmod=755 --chown=root:root /lib/*-linux-gnu/* /usr/local/lib/
COPY --from=sym --chmod=755 --chown=root:root /tmp/lib /lib
COPY --from=sym --chmod=755 --chown=root:root /tmp/lib /lib64
```

聡明な諸先輩方なら何をしているかは一目瞭然かと思いますが、ざっと解説します。

1. glibc同梱版のDistrolessイメージを用意
2. Alpineイメージを用意
3. Distroless内のglibc関連をAlpineへコピーする
4. マルチステージビルドで出来あがり

これなら、クリーンなオフィシャルイメージのみを使用しつつglibcも簡単に入手できるので「怪レいイメージ」に頼らず済みます。

[Deno](https://deno.land) で試しましたが、無事に動作確認できました。

試したアーキテクチャはx86_64だけですが、意図的にDockerfile上でアーキテクチャが明示されてるパス表記はワイルドカードで読み飛ばしてるので、AlpineとDistrolessが両方とも対応していれば動くと思います。

# おわりに
「Debian-slimを使え」とかそーいうのナシな！
個人的にAlpineが好きなの！
スクロール操作の新次元：GSAPのScrollTriggerでstart, end, pinを使う方法

gsapのScrollTriggerのstart, end, pinについて忘れてしまっていたのですぐ思い出せる様にわかりやすくまとめて行こうと思います。これらは基本的かもしれませんがとても便利なプロパティだと思います。

以下、デモとなります。
※開発中はmarkerをtrueにしておくとstart,endが視覚に確認できます。
<p class="codepen" data-height="400" data-default-tab="result" data-slug-hash="poqbgzK" data-user="jikkensya" style="height: 400px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/jikkensya/pen/poqbgzK">
  scrollTriggerDemo</a> by FrontendKanazawa (<a href="https://codepen.io/jikkensya">@jikkensya</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>


## start, endの値について
値は"triggerのエレメントのy スクリーンのy"といった順番で指定できます。
言葉よりもデモに表示されているマーカーと値を見比べた方が分かりやすいかもしれません。
### 01
一番目のセクション(.s1)には、
```javascript
start: "top+=50 top",
end: "bottom-=50 bottom",
markers: true
```
と設定されてあるため、セクションの一番上から50px分yを追加した位置にstartマーカーが表示されています。また、スクリーンの一番上にscroll-startマーカーが表示されています。
この2つのマーカーが重なったときに指定したアニメーション等が発火します。

### 02
2番目のセクションのセクション(.s2)はpinのデモです。
大きな違いはpinを加えた事意外にendの最後の値をtopにしている事です。
これによりスタートが発火してから、セクション(.s2)の一番下がスクリーンの一番上にヒットするまでセクション(.s2)が固定されます。これも言葉だと少し分かりにくいのでデモのmarkersの設定を変えて視覚的に確かめると良いかもしれません。
```javascript
start: "top top",
end: "bottom top",
pin: true,
markers: false
```

### 03
03は01と同じですがmarkersがfalseになっています。
markersを各セクションごとに分かりやすく表示できたら良かったのですがmarkersのスタイルは基本的に編集できないようでした。そのため01のマーカーだけを表示しています。

## pinの応用
最後に参考までとなりますが、以下のデモのような横スクロールなど様々な応用ができるのではないかと思います。
<p class="codepen" data-height="400" data-default-tab="result" data-slug-hash="BavQJYp" data-user="jikkensya" style="height: 400px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/jikkensya/pen/BavQJYp">
  Horizontal Scroll</a> by FrontendKanazawa (<a href="https://codepen.io/jikkensya">@jikkensya</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
PrismaでupdateManyしたい

Prismaで複数のレコードを bulk update したい。
Prismaのドキュメントを確認するとupdateManyがある。
しかしupdateMany APIは、複数のレコードに同じ値をいれるものであって、期待したAPIではない。

やり方としては、愚直にupdateをループ処理するしかない、
しかしループ処理は使いたくない。

悩んで調べていると、以下のように 親の更新で子を bulk update することができた。

```typescript
await prisma.my_column.update({
  data: {
    my_child: {
      update: myArray.map((item) => ({
        where: { id: item.id },
        data: item,
      }))
    }
  }
});
```

これなら親の数だけループ処理すればいいので、クエリを発行数をだいぶ減らすことができた。
リレーションがある場合限定になるが、覚えておくとパフォーマンスチューニング時に使える。

また、`update` だけではなく、`create` やリレーションの付け替えを行う `connect` を使ってより複雑なupdateをすることができる。

```typescript
// 子の update, create, connect を同時に行うのは update でしかできないので、
// 親がない場合は事前に create しておく
await prisma.my_column.create({
  data: myData,
})

// 親の update と、
// 子の update, create, connect を同時に行う
await prisma.my_column.update({
  data: {
    ...myData,
    my_child: {
      connect: myArray.map(item => ({
        id: item.id
      })),
      update: myArray.map((item) => ({
        where: { id: item.id },
        data: item,
      })),
      create: creatingMyArray.map((item) => ({
        ...item,
      })),
    }
  }
});
```

今回はパフォーマンスが問題になっていた部分を、これによって解消することができた。
flutterの言語選択を作る時に悩んだメモ

### 国際化対応のアプリを作る事になった

調べた感じだとクロスプラットフォーム開発環境で更新されているものは少なくなっていた。
Unityだとバイナリが無駄にデカいので今回はflutterで行う事にしました。
flutterの言語はdartで、C++/C#, kotlin, javaに似ているので比較的に入りやすい言語な感じですね。
国際化対応する場合も比較的使いやすい機能が公式(flutter_localizations)であるのですが、各言語毎にファイル(ARB)を作ると管理が面倒くさいので、個人的には表形式で管理したい。
調べたところ、fast_i18nというライブラリが良さそうですが、開発はslangに移行しているようなのでslangを使用することにしました。
jsonやyamlでも管理できますが、今回はCSVです。

### CSVにする理由

CSVにする理由は幾つかありますが大ざっぱに纏めると以下になります。

- 言語ファイルは、エンジニア以外にも編集してもらいたい
    エンジニア以外にjsonやyamlで入力してくださいとは言いにくい
- 言語毎に入力者は別の人なのでデータのコンフリクトを考えると共同編集できるようにしたい(GoogleSpreadSheet)
- CSVならEXCELやSpreadSheetから持ってこれるので編集も管理も容易になる
    今回はSpreadSheetをpythonからGoogleAPIでCSVに書き出した後「flutter pub run slang」を実行するにした
- 未対応のワードの抽出は、slangの機能にあるが、CSVの様な一覧性があれば抜けは見れば分かるので見落としは少ないはず

### 言語一覧の表示に使うリストをどうするか？

さて、本題の言語一覧の表示に使うリストをどうするかです。
とりあえず他の人はどうしているか知る為に、幾つかのサイトでみると……。
ふむふむ。
こんな感じで使用可能言語一覧をハードコードしてドロップダウンリストに表示するコートが多そうな印象です。
```
{
    "en" : "English",
    "ja" : "日本語",
}
```

うむむ。
自分があっているかどうか分からないものをコードで管理したくない。
出来れば分からないものは分かる人にお願いしたい。
という事でデータを基に表示するようにしたい。

slangのデータをビルドすると、i18n.g.dart(変換後のソースファイル名)にAppLocaleというenum型があります。
都合が良い事に、enumの要素から言語クラスに紐づいています。
後は、紐づいたクラスで特定のキー(今回はlanguage)の値を取得できれば…

言語選択時に表示する表示項目を作っておく(i18n.csv)
|key|en|ja|...|
|-|-|-|-|
|language|English|日本語|...|

コードにするとこんな感じ
```dart
    var languages = {};
    for(var value in AppLocale.values) {
        languages[value.languageCode] = value.build().language;
    }
    print(languages);
```
print(languages)の結果
```jsonc
{
    en: English,
    ja: 日本語
}
```

という事で、無事データから表示するようにできました。
めでたし、めでたし。
[Java] コンストラクタって？


まず初めに、私はJava初心者であり、テキストなどで勉強をしたことをアウトプットするためにこの記事を書きました。

## コンストラクタってなに？
#### クラス名と同一名称で、インスタンス生成直後に自動的に実行されるメソッドのことである。

インスタンスを生成後、以下のように初期値をセットしている。
（newで生み出されたばかりのインスタンスフィールド(nameやhp)には何も入っていないため）
```java:初期値セット
public class Main {
    public static void main(String[] args) {
        Hero h1 = new Hero();
        h1.name = "タロウ";
        h1.hp = 100;
        ...
    }
}
```

実際の開発現場では複数人で開発することがほとんどであるため、1人ですべての開発を行うことはまずありえない。
そのためクラスを作るにあたっては、自分以外の開発者がこのクラスを利用することも考えておかなければいけない。
そして、それぞれの開発者が正しく値に代入してくれるとも限らない。
このような場合に備えて、Javaでは **「インスタンスが生まれた直後に自動実行される処理」** を予め定義できるようになっている。
「インスタンスが生まれた直後に自動実行される処理」→これがコンストラクタである。
```java:コンストラクタ
public class Hero {
    String name;
    int hp;
    ...
    public Hero() {
        thie.hp = 100;
    }
}
```
Hero()というメソッドが追加されている。
よってこのクラスがnewされた直後に自動的に実行されるようになっている。

また、コンストラクタは私たち開発者が直接呼び出すことはできない。
しかし以下の場合は呼び出すことができる

- オーバーロードしたコンストラクタから、別のコンストラクタを呼び出せる（ **this();** ）
- 継承した場合は子のコンストラクタ内で、親のコンストラクタを呼び出せる（ **super();** ）
  - 特に継承した場合、子クラスのコンストラクタで super(); をつけないと、親クラスの初期化が行われずに問題になることがある。必要に応じて super(); を呼ぶこと。


インスタンスの生成時にJVMによって呼び出されるものである。
　JVM：Java Virtual Machine。Javaプログラムを実行するためのソフトウェア

## コンストラクタの定義方法

**コンストラクタと見なされる条件**
1. メソッド名がクラス名と完全に一致
2. メソッド宣言に戻り値が記述されていない（voidもだめ）
```
public class クラス名 {
　クラス名() {
　　自動的に実行する処理
　}
}
```
例にあげたように100などの固定値を代入するだけであればフィールド宣言を **int hp = 100;** にすれば良い。
複雑な条件がある場合、コンストラクタを使うのがよいとされている。

### 複雑な条件ってなに？

##### コンストラクタで引数を追加情報として受け取る
```java
public class Hero {
    String name;
    int hp;
    ...
    public Hero(String name) {
        thie.hp = 100;
        this.name = name;
    }
}
```
newするときに名前の初期値も指定できる。

newで引数を渡すには以下ように書く。
```java
public class Main {
    public static void main(String[] args) {
        Hero h1 = new Hero("ハナコ");
        ...
    }
}
```

##### オーバーロードにより複数定義可能
※オーバーロード：同じ名前だが引数が異なるメソッドを複数定義する
```java
public class Hero {
    String name;
    int hp;
    ...
    public Hero(String name) {
        thie.hp = 100;
        this.name = name;
    }
    // 今回追加したコンストラクタ
    public Hero() {
        thie.hp = 100;
        this.name = "テスト";
    }
}
```
コンストラクタをオーバーロードしたクラスを利用するには以下である。
```java
public class Main {
    public static void main(String[] args) {
        Hero h1 = new Hero("ハナコ"); // h1.nameはハナコ
        Hero h2 = new Hero();        // h2.nameはテスト
        ...
    }
}
```

##### 暗黙のコンストラクタ
本来、すべてのクラスは1つ以上のコンストラクタ定義を持っている必要がある。
クラスにコンストラクタ定義が１つもない場合に限って、コンパイルが「引数なし・処理内容なし」のデフォルトコンストラクタを自動的に定義される。

```java
public class Map {
    ...
    public Map() {
    }
}
```
普通に書くとこうだが、
```java
public class Map {
    ...
}
```
これでよい。
PrismaでfindUniqueじゃなくてfindUniqueManyしたい

`id` ではなく `unique key` でまとめてデータを取得したいこと、あると思います。

`unique key` で取得するには `findUnique` というAPIが用意されています。
しかし、まとめて取得する `findUniqueMany` みたいなAPIはありません。

やりたい場合は、以下のように `findMany` で `where or` に配列を渡すことで対応できます。

```typescript
prisma.my_column.findMany({
  where: {
    OR: myArray.map(item => ({
      unique_key_1: item.unique_key_1,
      unique_key_2: item.unique_key_2,
      unique_key_3: item.unique_key_3,
    }))
  }
})
```

## 追記

他にも以下の書き方でも実現できました

```typescript
prisma.my_column.findMany({
  where: {
    unique_key_1: {
      in: myArray.map(item => item.unique_key_1),
    },
    unique_key_2: {
      in: myArray.map(item => item.unique_key_2),
    },
    unique_key_3: {
      in: myArray.map(item => item.unique_key_3),
    },
  }
})
```



まれによく使うので、覚えておくと役立ちます。
【AWS】CodeBuildで`no basic auth credentials`エラーが発生した場合の原因と解決方法

# 概要
AWS Codebuildで`no basic auth credentials`というエラーに遭遇しました。
本記事ではその原因と解決方法を記載します。

# エラー内容
AWS Codebuildで使う`buildspec.yml`において、以下の内容でビルドしました。

```buildspec.yml
version: 0.2
phases:
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws --version
      - aws ecr get-login-password --region ap-northeast-1
  build:
    commands:
      - echo Build started on `date`
      - echo Logging in to Docker Hub...
      - docker login -u $USER_DOCKER -p $DOCKER_KEY
      - echo Building the Docker image...
      - docker build -t $REPOSITORY_URI:latest .
## 以後省略
```

すると、以下のエラーがCodebuildのログに表示され、失敗しました。


```
no basic auth credentials
```

# 原因と解決方法
同じような現象の記事を見つけました。
[Can't push image to Amazon ECR - fails with "no basic auth credentials"](https://stackoverflow.com/questions/34689445/cant-push-image-to-amazon-ecr-fails-with-no-basic-auth-credentials)

そちらに記載してあるとおり、

```yaml
- $(aws ecr get-login --no-include-email --region $AWS_DEFAULT_REGION)
```

というように表記を変えたところ、このエラーがなくなり、ビルドに成功しました。

なぜこれで解決できるようになったかというと、、、

最初の方法では、
・ECRに対するDocker認証トークンを取得
・その出力を直接標準出力に表示(=出力がDockerに提供されていない)
ということが行われています。

修正後の方法では、
・AWS CLIコマンドにより、サブシェル内でECRの認証情報を取得し、その結果を実行中のシェルに埋め込む
・Docker Hubへのログインに必要なECRの認証情報が提供され、Dockerはそのトークンを使用して正常にDocker Hubにログイン
ということが行われています。

つまり、`$(...)`を使用することで、コマンドの実行結果を変数に格納し、後でその変数を別のコマンドに渡す（パイプする）ことができるということです。認証情報を受け渡すには一般的な方法らしいです。

### `aws ecr get-login`コマンドについて
`aws ecr get-login`コマンドは、Amazon ECRにログインするための認証情報を取得するコマンドです。このコマンドを実行すると、Dockerクライアントに必要な認証情報を含む`docker login`コマンドが出力されます。
この`docker login`コマンドを実行することで、Docker Hubにログインするための認証情報が提供される、という流れなのです。

しかし、DockerクライアントがDocker Hubに対して適切な認証情報を持っていないと、`no basic auth credentials`というエラーが発生する、というわけです。


## `WARNING! Using --password via the CLI is insecure.`について

上記の方法だと、Dockerがセキュリティの警告を表示する可能性があります。この警告は、Docker CLIがパスワードをコマンドライン引数として直接受け取る（=パスワードを平文で渡す）ことはセキュリティ上のリスクであるためです。
以下のようにビルドログに表示されていました。

```
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
```

メッセージにある通り、`docker login`コマンドには`--password-stdin`オプションを使用することが推奨されています。これにより、パスワードは標準入力から受け取られ、セキュリティ的なリスクが軽減されます。

```yaml
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
```

これにより、パスワードはコマンドライン引数ではなく、標準入力から提供されるため、セキュリティが向上すると言えるでしょう。

同様に、ビルド時のログインコマンドも以下のようにするとよりセキュアです。

```yaml
 - echo $DOCKER_KEY | docker login -u $USER_DOCKER --password-stdin
```

### `--password-stdinオプション`の詳細
Docker Hubなどのコンテナレジストリに対して安全に認証情報（通常はパスワード）を提供するためのDockerコマンドのオプションです。

`docker login`コマンドでは、ユーザー名とパスワードをコマンドライン引数として指定します。しかし、パスワードがコマンドラインに平文で表示され、シェルの履歴やプロセスリストに含まれるとセキュリティ的によろしくないです。
ということで、このオプションが導入されたんですね。

基本的な使い方は以下になります。

```
echo $PASSWORD | docker login -u $USERNAME --password-stdin
```

`echoコマンド`を使用してパスワード(`$PASSWORD`)を標準出力に送り、`docker login`コマンドがそれを標準入力から読み取ります。これにより、パスワードがセキュアに扱われ、認証情報がコマンドラインに表示されることがなくなります。
初心者が初心者に教える初心者のためのGit&GitHub

# はじめに
Qiita初投稿です。というかエンジニア未経験です。

知り合いと趣味での開発プロジェクトにおいて、初心者にGitとGitHubの使い方の講師してくれと頼まれました。
ド初心者の挑戦ですね。

# 前準備
* GitHubのアカウント登録をする
* Git for Windowsをダウンロードしてアカウント設定しておく
* SSH接続したいので公開鍵をGitHubに登録しておく
* vscodeにGit関連の拡張機能を入れる。私はGit Graph,Git History,GitLensを入れてます。実際に全部使ってるかは知らないです

# リポジトリ作成～コミットまで
Git for WindowsをインストールしたらGit Bashが起動できるので、そこで作業をしていきます。まずは適当な場所に作業ディレクトリを作成していきます。
とりあえずStudyGitというディレクトリを作成しました。
~~~
$ mkdir StudyGit
$ cd StudyGit
~~~
これで現在のディレクトリがStudyGitになりました。
このディレクトリ直下にGitリポジトリを作成していきます。
~~~
$ git init
~~~
これで作成できました。次にこのリポジトリに適当なファイルを作ります。とりあえずgit.pyとかいう適当なファイルを作りました。これもせっかくなのでコマンドライン上で作成しちゃいましょう。
~~~
$ echo print\(4\) > git.py
$ cat git.py
print(4)
$ ls
git.py
~~~
こんな感じで表示されればとりあえず大丈夫です。
それでは早速ステージングしましょう。ステージングとはコミットするための前準備だそうです（無知すぎる）
~~~
$ git add git.py
~~~
ステータスを見てみます。
~~~
$ git status
On branch main
Your branch is ahead of 'origin/main' by 2 commits.
  (use "git push" to publish your local commits)

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   git2.py
~~~
(記事書く前に少し触ってたので既に二つほどコミットしてありますが、Changes to be committedの部分が上記膿瘍に表示されていれば大丈夫です)

それでは実際にコミットしていきます。
~~~
$ git commit -m "コミットします"
[main 0b4f3a0] コミットします
 1 file changed, 1 insertion(+)
 create mode 100644 git2.py
~~~
logコマンドを叩いて次のように出てくれば成功です。
~~~
commit 0b4f3a0e29f16272664ad442c9425b661ee8676c (HEAD -> main)
Author: kuriken121227 <kuri12ken27@gmail.com>
Date:   Thu Sep 7 19:21:09 2023 +0900

    コミットします
~~~

# 感想
自分が使っている中でも適当にプッシュ、コミット、プルしか使っていないので今回の記事を通してしっかりと知見を広げていきたいと思います。
2023年のAIの機会：アンドリュー・エングのまとめ

## 主要な洞察

AIの潜在的な影響と価値

- AIは、教育とAI開発の両方で示されたように、無数の人々の生活を変え、改善する潜在能力を持っています。
- AIは電気と同様の汎用技術であり、幅広い用途に有用である潜在能力を持っています。
- AIにおける教師あり学習の財務的価値は既に膨大で、Googleのような企業が年間1000億ドル以上の価値を持つ可能性があります。
- AIの機会は特定の産業や用途に限定されていないため、AI技術は汎用的で、スタートアップと大企業の両方に価値を創造し機会を捉える潜在能力を持っています。
- "汎用技術は多くの異なるタスクに役立つ...教師あり学習のエキサイティングなユースケースを見つけることはまだ完全にはできていないものの、"

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/225269/38009091-c5d2-237f-57ff-902ed379d907.png)

- AIの台頭は、長期的な影響を持つ深い価値のあるアプリケーションを作成する機会を提供しています。
- 目標は、AIの価値をテックワールドと消費者ソフトウェア以外のすべての産業と経済の他の部分に拡大し、その利益をもたらすことです。
- 多様なAIの機会を追求するために、エングはAIファンドを立ち上げ、大手テック企業内での運営よりも、ユースケース、マーケット進出戦略、お客様基盤の幅広さから、これがより効率的であると見なしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/225269/6507a543-9874-7874-d06f-6145238df2e4.png)



## さまざまな産業でのAIの応用
- 生成AIは、教師あり学習と共に、新しいコンテンツの作成や入力に基づいた出力の生成を可能にするAIの最も重要なツールの1つです。
- このタイムライン内で構築および展開される一部のAIシステムは、さまざまな産業におけるAI技術の潜在的な影響と価値を示すことができます。
- プロンプトベースのAIの普及が期待されており、より多くの人々によって構築されるカスタムAIアプリケーションの洪水を引き起こす可能性があり、AI駆動のイノベーションの大幅な増加の可能性を示しています。
- AI技術は農業での作物の成長の最適化と食品生産の増加に利用でき、農家と環境の両方に利益をもたらします。
- 海上輸送におけるAIパワード技術は、燃料の大幅な節約と環境への利益をもたらし、船1隻あたり年間約45万ドルの節約の可能性があります。

## 動画

https://www.youtube.com/watch?v=5p248yoa3oE
Pythonでメール作成・自動送信

# 使用モジュール
Python標準モジュールの ```smtplib```, ```email.mime.text ```を使用します。
ライブラリ　：smtplib（公式ドキュメント）
インストール：不要
https://docs.python.org/ja/3/library/smtplib.html

ライブラリ　：email.mime.text （公式ドキュメント）
インストール：不要
https://docs.python.org/ja/3/library/email.mime.html

```smtplib ```は、SMTPプロトコルクライアントの機能を備えたモジュールです。 SMTPを利用することでメールの送受信が可能になります。

```email.mime.text ```はMIMEオブジェクトを作成するために使用します。

# Pythonでメールを作成して自動送信する
```py
# ライブラリ設定
import smtplib
from email.mime.text import MIMEText
 
#  メール情報の設定
from_email ='*****@gmail.com' 
to_email = '宛先メールアドレス' 
cc_mail = ''
mail_title = 'メール件名'
message = '''
メール本文を記入
'''
 
#  MIMEオブジェクトでメールを作成
msg = MIMEText(message, 'plain')
msg['Subject'] = mail_title
msg['To'] = to_email
msg['From'] = from_email
msg['cc'] = cc_mail
 
#  サーバを指定してメールを送信する
smtp_host = 'smtp.gmail.com'
smtp_port = 587
smtp_password = 'Gmailで取得したアプリパスワード'
 
server = smtplib.SMTP(smtp_host, smtp_port)
server.starttls()
server.login(from_email, smtp_password)
server.send_message(msg)
server.quit()
```

# 解説
```
import smtplib
from email.mime.text import MIMEText
```
SMTPでメールを送信するために ```smtplib ```、MIME形式でメールを作成するために ```email.mime.text```モジュールから``` MIMEText ```クラスをインポートします。
```
msg = MIMEText(message, ‘plain’)
```
MIMETextクラスの第一引数にメール本文 ```message```、第二引数にメール形式 ```'plain' ```を指定して、MIMEオブジェクトを生成します。HTML形式のメールを送付したい場合は、メール形式に ```'html' ```を指定します。
```
server = smtplib.SMTP(smtp_host, smtp_port) 
```
```smtplib``` モジュールの ```SMTP``` クラスで引数をサーバアドレス、ポート番号を指定してオブジェクトを生成します。
```
server.starttls()
```
TLS (Transport Layer Security) モードでSMTP接続します。以降のSMTPコマンドは暗号化されます。
```
server.login(from_email, smtp_password)
```
暗号化した通信でGmailのSMTPサーバにログインします。引数のユーザ名は送信元のGmailアドレスと同じため ```from_email``` を指定します。
```
server.send_message(msg)
```
```send_message()```の引数にMIMEオブジェクトを指定してメールを送信します。
```
server.quit()
```
```server.quit()```でSMTP接続を終了してメール送信処理は完了です。
【プラクティス紹介】1分でさらっと分かる「リリーススプリント」

## プラクティス名
リリーススプリント

## プラクティスの目的・狙い
* リリース作業に必要な期間を確保する（リリース前後の不随タスクを含む）

## どんな時に使うか
* そのスプリント中にリリース作業があるかどうかでベロシティが乱高下してしまう
* PBI消化作業とリリース作業が並走することでミスが頻発し、障害を誘発してしまう

## 実施手順
1. 通常のスプリントとは別にリリース作業専用のスプリントを設ける（期間は任意）
1. 本スプリント中はリリース作業に専念し、PBIの消化は原則行わない
1. スクラムイベントの開催も任意とする（が、デイリースクラムぐらいは続けた方がよい）

本来、スクラムの理想としては全ての作業を通常スプリント中に消化すべきである。
しかし現実問題として、リリース手続きや事後フォローを考えると、PBIを消化しつつそれらの作業をこなすことが難しいことがある。リリース作業に集中し、それだけを確実にやり遂げた方がよい場合にこのプラクティスを適用する。

## アレンジ例
* 週次サイクルを崩さないためリリースススプリント期間を1週間とし、余った日は技術調査や環境改善などに充てる

## アンチパターン
* 何でも後回しにしすぎて、リリーススプリント自体が長期化する
* リリーススプリントでやるから、と全く手をつけていなかった非機能要件テストで致命的な問題が発生し、リリース自体が中止になる

## 参考情報

https://www.ryuzee.com/contents/blog/7123

## こぼれ話（私的コメント）
「リリーススプリントが必要なのはUndoneタスクを溜め込んでいるからでは？」
「そもそもリリーススプリントという考え方自体がアンチパターンなのでは？」
そんな批判の声が聞こえてきそうなプラクティスではあります。ですが、例えばウォーターフォールから移行したてのチームや、パイプラインも組めていないような環境下では、どうしたって通常のスプリント内で安定的にリリースすることに無理があるケースもあります。理想と現実の狭間を埋めるために編み出された工夫を、単に「教科書通りではないから」という理由で否定したくありません。確かにやり方としてベストプラクティスではないかもしれませんが、チーム全員で話し合い、自分たちの現場では今はこれが必要だという結論に至ったならば、それがベストな判断のはずです。ベロシティ計測としても通常スプリントとは別扱いにした方が都合がよいと思います。


　[アジャイルプラクティス一覧へ戻る](https://qiita.com/kakashi_h5/items/20584998c5ca4594b9ea)
keras-rlのcannot import name '__version__' from 'tensorflow.keras'

rl/callbacks.py内の

```python
from tensorflow.keras import __version__ as KERAS_VERSION
```
を

```python
from keras import __version__
```
に変える．

[スタックオーバーフローのリンク](https://stackoverflow.com/questions/76650327/how-to-fix-cannot-import-name-version-from-tensorflow-keras)
Javaで「引数が異なるコンストラクタ」の動作を確認してみた

# 概要
Javaで「引数が異なるコンストラクタ」の動作を確認してみました。以下のページを参考にしました。

https://www.javadrive.jp/start/constructor/index3.html

# 実装
以下のファイルを作成しました。
```CTest10.java
class CTest10{
  public static void main(String args[]){
    Television tv1 = new Television("居間");
    tv1.dispChannel();

    Television tv2 = new Television();
    tv2.dispChannel();
  }
}

class Television{
  private int channelNo;
  private String place;

  Television(){
    channelNo = 1;
    place = "未定";
  }

  Television(String newPlace){
    channelNo = 1;
    place = newPlace;
  }

  public void setChannel(int newChannelNo){
    channelNo = newChannelNo;
  }

  public void dispChannel(){
    System.out.println("現在のチャンネルは" + channelNo+ "です");
    System.out.println("設置してある場所は" + place + "です");
  }
}
```
以下のコマンドを実行しました。
```
$ javac CTtest10.java 
$ java CTest10 
現在のチャンネルは1です
設置してある場所は居間です
現在のチャンネルは1です
設置してある場所は未定です
```

# まとめ
何かの役に立てばと。
JavaのStreamAPIとOptional

会社でJava初心者向けに勉強会が開催され、StreamAPIとOptinalについて学習したので記事にしました。

### Stream API

Stream APIはコレクションの要素に対する一連の操作を効率的かつ簡潔に行うためのAPIです。以下のような特徴があります。

1. **ラムダ式と関数型インターフェイス**: Stream APIはラムダ式と関数型インターフェイスを活用しています。
2. **中間操作と終端操作**: Streamの操作は、中間操作（変換やフィルタリングを行う）と終端操作（結果を生成する）の2種類があります。
3. **パイプライン処理**: Streamの操作はパイプラインとして連続して適用することができます。

以下は、Stream APIを使用した簡単なコード例です。

```java
import java.util.Arrays;
import java.util.List;

public class StreamExample {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("Apple", "Banana", "Cherry", "Date", "Elderberry");

        list.stream() // Streamの生成
            .filter(s -> s.length() > 5) // 中間操作: 文字数が5より大きい要素をフィルタリング
            .map(String::toUpperCase) // 中間操作: 文字列を大文字に変換
            .forEach(System.out::println); // 終端操作: 各要素を出力
    }
}

```

### Optional

Optionalクラスは、NULLを返す可能性のあるメソッドが、代わりに`Optional`インスタンスを返すように設計されています。これにより、NULLポインタ例外を防ぐことができます。

以下は、`Optional`クラスの基本的な使用法のいくつかです。

1. **ofNullable**: NULLかもしれない値をラップします。
2. **ifPresent**: 値が存在する場合に実行するアクションを指定します。
3. **orElse**: 値が存在しない場合に返されるデフォルト値を指定します。
4. **map**: 値が存在する場合に適用される関数を指定します。

以下は、`Optional`の基本的な使用法の例です。

```java
import java.util.Optional;

public class OptionalExample {
    public static void main(String[] args) {
        Optional<String> optional = Optional.ofNullable("Hello, World!");

        optional.ifPresent(System.out::println); // 値が存在するため、"Hello, World!"を出力

        String value = optional.orElse("Default value"); // 値が存在するため、"Hello, World!"を返します
        System.out.println(value);

        Optional<String> emptyOptional = Optional.empty();

        emptyOptional.ifPresent(System.out::println); // 値が存在しないため、何も出力されません

        String defaultValue = emptyOptional.orElse("Default value"); // 値が存在しないため、"Default value"を返します
        System.out.println(defaultValue);
    }
}

```
SessionManagerでEC2に入れるようにする

SessionManagerでEC2に入れるようにするためのメモ。

1.「AmazonEC2RoleforSSM」のポリシーを設定したIAMロールを作成
2.EC2にIAMロールを設定

参考：https://aws.amazon.com/jp/getting-started/hands-on/remotely-run-commands-ec2-instance-systems-manager/?nc1=h_ls
【Rails】テスト編：rails test コマンドで行える事

# ［備忘録］覚えとくと使い勝手の良いコマンド
こんにちは！
今回は`rails test`におけるコマンド操作について書き溜めておきます。

# Rails Testコマンドとは
`rails test`コマンドの基本的な使い方
Railsには、テストの実行と管理を支援するためのコマンドラインツールが用意されていいて、その中でも基本的なコマンドが`rails test`です。

```.bash
rails test
```
上記のコマンドを使用することで、テストスイート内のすべてのテストケースを実行できます。
テストが成功すると緑の点が表示され、エラーがある場合は赤い点が表示されます。

#### テスト結果の確認
テストの実行後、コンソールにテスト結果が表示されます。
![スクリーンショット 2023-09-07 17.02.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524805/d6c66efc-9636-e195-7057-755af279c9c1.png)

成功したテストケースと失敗したテストケースの数が表示され、詳細な情報も提供されます。
これを通じて、どのテストが失敗したのかを特定し、問題を修正できます。

## 特定のテストフォルダを実行する
モデルテストだけ、コントローラーテストだけ、などなど特定のフォルダ内のテストファイルだけを実行したい場合は、パスに[*]アスタリスクを付けます。
```.bash
rails test test/system/*
```

## 特定のテストファイルを実行する
記述したtestファイルのパスをコマンドに記述することで、特定のファイルのみ実行する事ができます。

```.bash
rails test test/path/to/test_file.rb
```

## 特定の行のテストを実行する
以下のようなテストがあったとします。
```.test/path/to/test_file.rb
 1 require "test_helper"
 2
 3 class TestFile < ActiveSupport::TestCase
 4  test "the truth" do
 5    assert true
 6  end
 7 
 8  test "calculate" do
 9    calculated_number = 1 + 1
10
11    assert_equal 2, calculated_number
12  end
13 end
```
8行目のテストだけ実行したい場合は、パスの末尾に`：`コロンと行数を記述します。

```.bash
rails test test/path/to/test_file.rb:8
```

## 詳細なテスト結果を表示
テスト結果に詳細な情報が表示され、失敗したテストのスタックトレースなどが確認できます。
```.bash
rails test --verbose
```

緑の点だった部分が文字列となるイメージですね
```.bash
rails test --verbose

Run options: --verbose

# Running:

TestFile#test_the_truth = 0.92 s = .
TestFile#test_calculate = 0.91 s = .
```

今回は以上となります。
ほかにも数多くのオプションや小技がありますので別の機会に第二弾を投稿しますね。
【Flutter】iOS依存のプラグインを追加した時のエラー処理

iOSに依存したプラグインをインストールしたとき、ビルドエラーが発生することがある。

様々な理由が考えられるが、Cocoapodsのリポジトリが更新されていないことが原因で発生することが多いため、

以下のコマンドを実行する

```shell
pod install --repo-update
```
Dataformで管理している実テーブルをDigdag経由で定期更新する

## 実現したいこと

BigQuery上に蓄積したデータを使って何かしらの集計データをテーブルに格納する時に
Dataformでテーブルに入れるデータの集計ロジックをコードで管理しつつ、定期更新できるようにします。

Dataform上にもワークフローを定義してスケジューリングする機能が提供されていますが、BigQueryのデータを更新するスケジューラーが分散すると管理しづらいため、NateeではETLツールとして使っているDigdagで更新する方式を取ることにしました。

DataformをAPI経由で使う時に、画面の操作からは直感的に理解しづらいポイントがあったので備忘録がてらまとめます。
Dataformの基本的な使い方については触れません。

## 実装

### Dataform
クエリの結果をテーブルに格納したい場合は、下記のような記述でDataformに登録します。

```
config {
  type: "table",
  schema: "sample",
  description: "Qiita用のサンプルテーブル",
  tags: ["daily"],
  columns: {
    id: "autoincrement",
    name: "名前",
    created_at: "作成日",
  }
}
SELECT
  id,
  name,
  created_at
FROM
  ${ref(qiita_example)}
WHERE
  weight > 100
```

ポイントはtagsの<b>"daily"</b>です。
日時で更新したいテーブル全てにdailyタグをつけておくことで、日時更新対象をタグベースで判定できます。



## Digdag(呼び出し側)

上記のテーブル更新を実行するためにDigdagからpythonでAPIをコールしてワークフローを実行します。
(Digdagである必要はないですし、Cloud Composerを使うとより容易に実現できます)

Dataform上でワークフローを実行するために必要な手順は下記の4つです。

- APIの有効化とサービスアカウントに権限追加
- 必要なライブラリのインストール
- sqlxファイルのコンパイル
- コンパイル結果を使ってワークフローを実行

### APIの有効化と権限追加

GCPを利用している人ならわかると思いますが、DataformのAPIを有効化し
下記ページを参考に適切な権限を付与しましょう。
https://cloud.google.com/dataform/docs/access-control?hl=ja#predefined_roles

### ライブラリインストール

Pythonライブラリが提供されているのでそれを使います

```bash
pip install google-cloud-dataform
```

[参考](https://cloud.google.com/dataform/docs/reference/libraries?hl=ja)


### コンパイル

画面上では自動的にやってくれるのであまり意識することがないですが、APIだと明示的に実行する必要があります。
自分はdeployというworkspaceを作っていて、mainブランチにマージしたコードをdeployワークスペースから反映するフローにしています。
(まだ自動化はできていないので、ここもGithub Actions経由でできるようにしていきたい)

そのため、deployワークスペースを参照すれば最新コードが存在しているという前提で下記のような処理でコンパイルします。

```python
import google.cloud.dataform as dataform
#...略

client = dataform.DataformClient()

parent = f"projects/{project}/locations/{location}/repositories/{repository}"
compilation_result = dataform.CompilationResult(
    name="deploy workspace compilation result",
    workspace=f"{parent}/workspaces/deploy"
)
request = dataform.CreateCompilationResultRequest(parent=parent, compilation_result=compilation_result)
compilation_result = client.create_compilation_result(request=request)
```

dataform.CompilationResult の引数に特定workspaceを指定することでそのワークスペースに反映されているコードの内容からコンパイル結果を得ることができます。
git_commitishという引数にブランチ名を渡すことも可能ですが、運用のフロー的にワークスペースを利用しています。

CompilationResultRequestを作成してcreate_compilation_resultに渡すことでコンパイル結果を取得できます。

### ワークフローの実行

次にコンパイル結果を使ったワークフローの実行です。
Dataformの画面からBigQueryに適用する際には裏でワークフローが実行されているのでAPI経由でそれを再現します。

dailyタグが含まれるものを全て実行するワークフローの起動は下記のコードで実現できます。

```python
invocation_config = dataform.WorkflowInvocation.InvocationConfig(included_tags=["daily"])
wf_invocation = dataform.WorkflowInvocation(
    name="daily workflow invocation",
    compilation_result=compilation_result.name,
    invocation_config=invocation_config
)
wf_invocation_request = dataform.CreateWorkflowInvocationRequest(parent=parent, workflow_invocation=wf_invocation)
result = client.create_workflow_invocation(request=wf_invocation_request)
```

## 最後に

上記の手順はあまり調べても出てこないので、ライブラリの中のコメントを参考に実装しました。
特に日本語の例が少ないので今後Dataformがより盛り上がっていくと嬉しいですね！
実はES2022以前まで配列末尾検索メソッドが無かった…..

# ES2022以前

ES2022以前まではなぜか配列を直接末尾から検索メソッドがありませんでした。

そのため以下のように`slice()`で配列を再生成後、`reverse()`で破壊的逆順にして検索をしていました。

```jsx
const nums = [0,1,2,3];
const search = nums.slice().reverse().find((num) => num > 0);
console.log(search); // 3
```

# ES2023で直接逆順から検索可能に

なんと`findLast()`が登場したおかげで以下のように検索できるようになりました！！

また非破壊的に検索されるため`slice()`で配列を再生成する必要がありません！！

なおNode.jsでは18系から対応しています。

```jsx
const nums = [0,1,2,3];
const search = nums.findLast((num) => num > 0);
console.log(search); // 3
console.log(nums); // [0, 1, 2, 3]
```

# 最後に

なんで普通に需要ありそうなのに今までなかったの？なぁぜなぁぜ？

<iframe width="560" height="315" src="https://www.youtube.com/embed/kjuDK_tR57A?si=NKYwpx4bDHkq4kqS" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
Rspecエラー　Blocked host: www.example.com\nTo allow requests to www.example.com make sure it is a valid hostname

# 概要
visitで訪れたpageの中に、新規登録という文字列があるかどうかを確認するためシステムスペックのテストを実行したところエラーが発生

# テストコード
```
it '正しい情報を入力すれば、新規登録できる' do
      # トップページに遷移
      visit root_path
      # ヘッダーに新規登録ボタンがある
      expect(page).to have_content('新規登録')
```

# エラー文
```
 Failure/Error: expect(page).to have_content('新規登録')
       expected to find text "新規登録" in "Blocked host: www.example.com\nTo allow requests to www.example.com make sure it is a valid hostname (containing only numbers, letters, dashes and dots), then add the following to your environment configuration:\nconfig.hosts << \"www.example.com\""
```

# 解決手順
`initializers`内に`blocked_hosts.rb`というファイルを作成
以下を記載
```:config/initializers/blocked_hosts.rb
Rails.application.configure do
  config.hosts << "www.example.com"
end
```
するとエラーが変わりました

```
 Failure/Error: <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
     
     ActionView::Template::Error:
       Webpacker can't find application.js in /One_Trip/public/packs-test/manifest.json. Possible causes:
       1. You want to set webpacker.yml value of compile to true for your environment
          unless you are using the `webpack -w` or the webpack-dev-server.
       2. webpack has not yet re-run to reflect updates.
       3. You have misconfigured Webpacker's config/webpacker.yml file.
       4. Your webpack configuration is not creating a manifest.
       Your manifest contains:
       {
       }
```
webpacker.yaml の test の設定を development に合わせてみたら成功するようになりました。
↓デフォルト設定
```:webpacker.yaml
test:
  <<: *default
  compile: true

  # Compile test packs to a separate directory
  public_output_path: packs-test
```
developmentと全く同じ設定に変更

```
test:
  <<: *default
  compile: true

  # Reference: https://webpack.js.org/configuration/dev-server/
  dev_server:
    https: false
    host: localhost
    port: 3035
    public: localhost:3035
    hmr: false
    # Inline should be set to true if using HMR
    inline: true
    overlay: true
    compress: true
    disable_host_check: true
    use_local_ip: false
    quiet: false
    pretty: false
    headers:
      'Access-Control-Allow-Origin': '*'
    watch_options:
      ignored: '**/node_modules/**'
```

以上でエラーが解消されテストが通るようになりました。

# 参考資料
[Railsの開発環境でサブドメインをテストする](https://qiita.com/tatsumin0206/items/8b4d0930c0f58cef7813)
AWS – Terraformで作成した Lambda関数で実行時エラー (  lambda_handler is undefined or not exported ) ( node.js )

# エラー

Lambda実行時にこんなエラーが起こる (node.js)
 

```
index.lambda_handler is undefined or not exported
```

スクリプトは

```js
export const handler = async(event) => {
   // ...
}
```

# 解決

- ファイル名を index.js / index.mjs にする
- 関数名を lambda_handler にする

で解決した

```js
export const lambda_handler = async(event) => {
   // ...
}
```

ファイル名と関数名を揃える必要があるみたいだ


# Terraform 

Terraformではhandlerをこのように指定していた

```
resource "aws_lambda_function" "xxx" {
  handler          = "index.lambda_handler"
}
```


# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ


# Twitter

https://twitter.com/YumaInaura
エンジニアインターンの探し方！個人的におすすめなサイト


エンジニアインターンを探したいと思っても、なかなか見つからなかったり、どうやって探したら良いかわからない人も多いと思います。この記事では、エンジニアインターンの探し方にフォーカスして回答していきます！

## エンジニアインターンの探し方

実際に僕がエンジニアの長期インターンを探す際に使ったサイトをご紹介します！

- [Renew(リニュー)](https://renew-career.com)
- [Wantedly(ウォンテッドリー)](https://www.wantedly.com)

### Renew(リニュー)

https://renew-career.com

Renew(リニュー)は、大学1年生から始められる長期インターン専門サイトです。
エンジニアの職種検索はもちろん、RubyやPHP、Pythonなど、プログラミング言語からも探すことができました。

エンジニアインターンの求人数は少ないですが、フロントエンドエンジニアの募集や、バックエンドエンジニアの募集、機械学習やPythonエンジニアの募集など、本格的なインターンシップが複数掲載されています！

実際に僕が面白そうだと思ったインターンはこちらです！

- [スパイスファクトリー株式会社（バックエンド開発×東京）](https://renew-career.com/companies/81/joboffers/151)
- [株式会社ANIMA GROUP（バックエンド開発×東京）](https://renew-career.com/companies/95/joboffers/206)
- [株式会社雛菊（機械学習×東京）](https://renew-career.com/companies/56/joboffers/140)
- [株式会社rexcornu（フロントエンド開発×フルリモート）](https://renew-career.com/companies/46/joboffers/57)

#### よかった点

- 応募後の返信がとにかく早かった
- 開発環境や仕事内容がわかりやすかった
- 写真が多くて会社の雰囲気がわかりやすい

#### 足りなかった点

- 求人の掲載数


### Wantedly(ウォンテッドリー)

https://www.wantedly.com

Wantedly(ウォンテッドリー)は、就職活動・転職活動でも使われている求人サイトです。
エンジニアの中でも、フロントエンドやバックエンドなどの職種検索ができます。

エンジニアインターンの求人数は圧倒的に多かったのですが、応募後になかなか返信がなかったりするデメリットは感じました、、。写真も多く掲載されているので、会社の雰囲気も掴みやすいです！

実際に僕が面白そうだと思ったインターンはこちらです！

- [株式会社ナイトレイ(バックエンド開発×東京)](https://www.wantedly.com/projects/467606)
- [株式会社FLAT(フロントエンド開発×東京)](https://www.wantedly.com/projects/612200)
- [株式会社Everforth(バックエンド開発×東京)](https://www.wantedly.com/projects/76377)

#### 良かった点

- 求人数が多い
- 仕事内容やビジョンがわかりやすい
- 魅力的な会社が多かった

#### 悪かった点

- 応募後に返信がない企業が多かった..

## まとめ

エンジニアのスキルアップにインターンは本当におすすめです！
首都圏中心にはなってしまいますが、スキルアップのためにも是非インターン始めてみてください！
【xarray】複数の次元を1次元に結合（平坦化）する (stack)

この記事では、以下のライブラリを用いる。

```Python
import xarray as xr
```

# DataArray.stack()
`DataArray`のメソッド`stack()`を使うと、複数の次元を1次元に結合することができる（平坦化）。

次の`DataArray`を例として説明する。

```Python
values = [['a','b','c'],['d','e','f']]
dims = ['x','y']
coords = {'x': [0,1], 'y': [0,1,2]}
array = xr.DataArray(values, dims=dims, coords=coords)

array
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3552468/649ad0da-b184-fa7c-4949-a05d5b537d7c.png)


## 使い方①
`stack()`の引数に、結合後の名前と結合したい次元を指定する。
```Python
stacked_array = array.stack(position=['x','y'])
stacked_array
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3552468/7482cb99-925f-80c2-945c-5492d5d3528b.png)

## 使い方②
`DataArray`のメソッド`unstack()`を使うと、`stack()`で結合した次元をもとに戻すことができる。
```Python
unstacked_array = stacked_array.unstack('position')
unstacked_array
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3552468/7bb71bed-9b36-09ed-6802-6df03e99b309.png)

# 公式ドキュメント

https://docs.xarray.dev/en/latest/generated/xarray.DataArray.stack.html#xarray.DataArray.stack

https://docs.xarray.dev/en/latest/user-guide/reshaping.html#stack-and-unstack
テンセントクラウドでのTKE構築でのエラー

テンセントクラウドにてQCloudResourceFullAccessポリシーを適用しているユーザーでTencent Kubernetes Engine(TKE)を構築しようとしたら、エラー発生。

![MicrosoftTeams-image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1096909/189fa4e3-46a2-a669-cb93-a33d810d5713.png)

解決策、QCloudFinanceFullAccessポリシーも必要でした
RustのAxum(POST)でボディを受け取ろうとするとtraitエラーが発生する

# はじめに

Axumでボディを受け取って処理をするPOSTのAPIを作成したところかなり解決に時間がかかったのでまとめます。
ちなみに根本的原因はわからないので詳しい方はコメントをいただきたいです。

# 問題

`POST`のAPIを一つ作りました
ここではPOSTされたボディを`Json`で受け取るようになっています

```rs:router.rs
async fn post_todo(
    Json(payload): Json<SearchCondition>,
    Extension(container): Extension<Arc<Container>>,
) -> impl IntoResponse {
```

しかし以下のエラーが発生してしまいます

```
the trait bound `fn(Json<SearchCondition>, Extension<Arc<Container>>, HeaderMap) -> impl Future<Output = impl IntoResponse> {post_v2_news}: Handler<_, _, _>` is not satisfied
the following other types implement trait `Handler<T, S, B>`:
  <Or<L, R, Lt, Rt, S, B> as Handler<(M, Lt, Rt), S, B>>
  <IntoHandler<H, T, S, B> as Handler<T, S, B>>
  <Layered<L, H, T, S, B, B2> as Handler<T, S, B2>>
  <MethodRouter<S, B> as Handler<(), S, B>>rustc
the trait bound `fn(axum::Json<SearchCondition>, Extension<Arc<Container>>, HeaderMap) -> impl Future<Output = impl IntoResponse> {post_v2_news}: Handler<_, _, _>` is not satisfied
the following other types implement trait `Handler<T, S, B>`:
  <Or<L, R, Lt, Rt, S, B> as Handler<(M, Lt, Rt), S, B>>
  <IntoHandler<H, T, S, B> as Handler<T, S, B>>
  <Layered<L, H, T, S, B, B2> as Handler<T, S, B2>>
  <MethodRouter<S, B> as Handler<(), S, B>>rustc
```

`JSON`は`Axum`から利用しているので謎でした

# 解決方法

以下で解決ができました

```rs:router.rs
async fn post_todo(
    Extension(container): Extension<Arc<Container>>,
    Json(payload): Json<SearchCondition>,
) -> impl IntoResponse {
```

引数のボディの受け取りの順序を変更したところうまくうごくようになりました
基本的にハンドラーは引数の順序に依存がないようにできているはずなのですが、なぜか依存が生まれていて起きているようでした

# おわりに

インターンできてくれたRust1日目の方がまさかの解決してくれて驚きました
プログラミングにもビギナーズラックがあるんですかね

それにしてもこの挙動は困ります。よくありそうなパターンだと思うので