OmniAICreator/Qiita-1.07M huggingface dataset:
データリンク層の主なプロトコルの種類

# ARP
ARPはいつ使うのか？

https://qiita.com/masatom86650860/items/2450d4688107cd480adc

ipパケットを送るとき送り先のipでmacを調べるため必要。
データリンク層における送り先を指定するための手段。
これがないと別のLANからの情報を送ることができない。

#### GARP
>「自身に設定するIPアドレスの重複確認」、「ARPテーブルの更新」を主な目的としたもので目的IPアドレスに自身が使用するIPアドレスを指定し、MACアドレスを問い合わせる

自分自身のMACアドレスを調べるのか。重複防止、テーブルの更新を行う

# RARP
>`MACアドレスからIPアドレスを取得`するためのプロトコルです。電源オフ時にIPアドレスを保持することができない（IPアドレスを保持するハードディスクを持たない）機器が、`電源オン時に自分のMACアドレスから自身に割り当てられているIPアドレスを知るため`に使用します。

これはARPの逆。
# PPP
>`二点間をポイント２ポイントで接続する`ためのデータリンクプロトコルです。`WANを介して２つのノードをダイアルアップ接続する時に使用`されています。PPPは、`ネットワーク層とのネゴシエーションを行うNCP`(ネットワーク制御プログラム)と`リンクネゴシエーションを行うLCP`(リンク制御プロトコル)から構成されていて、リンク制御やエラー処理機能を持ちます。

https://qiita.com/masatom86650860/items/f980f3c567648b245d39

IPパケットを流すために異なるLAN同士を繋げる道を作る役割

* NCPとLCP
* リンク制御やエラー処理機能を持つ

# PPPoE
>PPPと同等な機能をイーサネット(LAN)上で実現するプロトコルです。`PPPフレームをイーサネットフレームでカプセル化することで実現`します。

https://qiita.com/masatom86650860/items/4b5e6ca01790e4e47f20


* PPPフレームとは？

https://docs.oracle.com/cd/E19096-01/sol.ppp301/805-6382/6j6114iu5/index.html

多分イーサネットフレームと同じものだと思う。

# 感想
いくつか調べた。それぞれ少し詳しく知りたいので今後分離するかもしれない。
tfmigrate mv したときに resource は mv 後の場所に置く

# tfmigrate mv したい

- resource を新，旧，両方，置けばいいのか

が疑問に思ってしまったので，メモを残しておく．

新に置けばよい．

おそらく，

1. `state mv`
1. `terraform plan`
1. diff 計算

しているだけなので，旧リソースがあっても意味が無い(= 旧create & 新destroyする → error) のであろう．

## あと

mv したときのsrc 側の resource 名を間違えていたら，特にエラーにならなかった
mv 失敗するとか無く，success してしまったので，注意が必要そう

と思ったけど，エラーになったケースもあった，何か条件があるのだろう．

## あと2

module 全体を移動するのに `xmv` が便利そう

## Refs

- https://github.com/minamijoyo/tfmigrate?tab=readme-ov-file#state-mv
- https://github.com/minamijoyo/tfmigrate?tab=readme-ov-file#state-xmv
aws cloudwatch logsinsights　ログ確認クエリ(kosekiメモNO6)


# はじめに

aws cloudWatchのログインサイト、よくクエリを実行してシステムログを確認するので、
クエリのテンプレートをメモしておきます。

# 動作環境
aws cloudwatchアクセスできるユーザー。


# 使い方
fields、filter、parseなどのコマンドは参考文献から確認できます。

```logsinsights.log
fields @message
| filter @logStream like /testtesttest001/
| parse @message '"message":"*"' as logMessage
| fileter logMessage like /test01/ and logMessage not like /test02/
| display logMessage
| sort @timestamp desc
#| limit 2000
```

# 参考文献
[CloudWatch Logs Insights クエリ構文](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/CWL_QuerySyntax.html)

***
以上
SPAでCSRF対策を検討する

## はじめに
運用しているSPA(シングルページアプリケーション)を脆弱性診断ツールにかけたところ、CSRFに対する指摘を受けました。
SPAに対するCSRF対策の知識が薄かったので、改めてSPAでのCSRFリスク評価や対策の考え方を調査・検討した時の備忘録を残しておきます。

## Cookie(Session)認証の場合 (MPAで広く採用)
まず前提として、広く知られているマルチページアプリケーションでのCSRF攻撃の流れをおさらいします。

[![Image from Gyazo](https://i.gyazo.com/4a5109e45d7767a2e45a70ffbf2d55be.jpg)](https://gyazo.com/4a5109e45d7767a2e45a70ffbf2d55be)

- Cookie(Session)認証を採用している正規ウェブサイトにログイン
- ブラウザのCookieに正規サイトのセッションIDが保存される
- ユーザーが広告の誤クリックなどで罠サイト(trap.com)にリダイレクトしてしまう
- 罠サイトを開いた時に、正規サイトに向けたCSRF攻撃用スクリプトが発火する
- 正規サイトではリクエストを実行していないが罠サイト(trap.com)から実行される
- 罠サイトからのリクエスト時に、Cookieに保存されていたセッションIDが自動付与された状態で実行される
- **CSRF攻撃成立**
ユーザー自身が正規サイトでリクエストを実行したわけではないにもかかわらず、罠サイトから発行されたリクエストが、ユーザーのセッションを利用して実行されてしまう

このような流れでCSRF攻撃が成立してしまうため、ページにCSRFトークンを埋め込んで検証するなどの対策が必要になります。(今回の記事ではスコープ外なので割愛)

## トークン認証の場合 (SPAで広く採用)
次にシングルページアプリケーションでのCSRF攻撃の流れを確認します。

[![Image from Gyazo](https://i.gyazo.com/c3b9cd7f987e4c28abdd7c6b15173432.jpg)](https://gyazo.com/c3b9cd7f987e4c28abdd7c6b15173432)

- トークン認証を採用している正規ウェブサイトにログイン
- ブラウザのlocalStorageまたはsessionStorageに正規サイトの認証トークンが保存される
Cookieには認証情報は保存されていない
- ユーザーが広告の誤クリックなどで罠サイト(trap.com)
にリダイレクトしてしまう
- 罠サイトを開いた時に、正規サイトに向けたCSRF攻撃用スクリプトが発火する
- 正規サイトではリクエストを実行していないが罠サイトから実行される
- 認証トークンはlocalStorageに保存されているため、罠サイトからのリクエストでは自動で含めることができない
- **CSRF攻撃不成立**
罠サイトからリクエストは発行されるが、トークンを自動付与することはできないので認証エラーで実行は成立しない
:::note
※ localStorageに保存されたデータは同じオリジンのみアクセス可能なので、罠サイトの異なるオリジンから、正規サイトのlocalStorageに保存されたデータはアクセスすることができない
:::

このような流れで罠サイトからは認証情報を得ることができないので、CSRF攻撃が成立しません。

## なぜトークン認証を行なっているSPAでCSRF攻撃の指摘を受けたのか？
トークン認証を行う場合は基本的にCSRFの攻撃対象にならないが、なぜ今回は指摘されたのか？
指摘を受けたのは下記のリクエストです。

**A. メールアドレスを元にアカウント有無を確認するリクエスト**
**B. 未ログイン時のレコード作成リクエスト**

これらはどちらも未認証のAPIです。
確かに未認証APIは、誰でもアクセス可能なため、CSRF攻撃によって不正なリクエストが発生する可能性があります。
ここで指摘を受けていました。
では果たして未認証のAPIはCSRF対策を行うべきなのでしょうか？

下記はIPAからの引用です

[![Image from Gyazo](https://i.gyazo.com/8607a8daa5f7dcd535fdabccf5f63850.png)](https://gyazo.com/8607a8daa5f7dcd535fdabccf5f63850)

https://www.ipa.go.jp/security/vuln/websecurity/csrf.html

## 今回の指摘対象にてCSRF攻撃は問題になるか？

**A. メールアドレスを元にアカウント有無を確認するリクエスト**
  - 実行されたところで特に問題は起きない

**B. 未ログイン時のレコード作成リクエスト**
  - 意図せずにレコードが作成される可能性あり

## 指摘を潰すために対策を導入すべきか

**Aについて**
- このAPIは、指定されたメールアドレスのユーザーが存在するかを確認するだけであり、システムへの影響は極めて限定的
- 仮にCSRF攻撃が行われたとしても、攻撃者が得られる情報は「ユーザーが存在するか?」に留まる

**Bについて**
- レコードが実際に作成される可能性はあるが、未認証のオープンAPIであることから、そもそも誰でもリクエストを送信できる仕様
- このAPIはパラメータが複雑で、正常リクエストを突き止めるのは困難である
- パラメータが適切でなければレコードが作成されない設計になっているため、不正リクエストが成功する可能性は低い
- CSRF対策ではなく、リクエスト形式を解析しようとするようなDoS攻撃に備えた対策をするべきではないか

## 結論
認証後のAPIが指摘対象となっている場合、もしくは認証なしのAPIでも掲示板などの匿名で誰でも見ることができる場所にレコード生成できるような公開APIが指摘対象となっている場合は、CSRF対策を講じるべきかと思います。
しかし今回指摘されたこれら未認証のAPIに対しては、CSRF攻撃の本質的な「ログインユーザーになりすまして重要なリクエストを実施する」というものに当てはまらないので、対応は不要と判断しました。

## SPAにCSRF対策を講じる場合
**認証が必要なAPI**
- トークン認証なのでCSRF攻撃の対象外

**認証なしのAPI**
- CORSポリシーの最適化
- `application/json` の強制（単純リクエストでなくすることで、プリフライトリクエストを発生させてCORSポリシーで弾く）
- 独自リクエストヘッダの強制（単純リクエストでなくすることで、プリフライトリクエストを発生させてCORSポリシーで弾く）
- CSRF対策以外に、リクエスト数を制限するレートリミットなどのDoS対策

### 参考

https://kimuson.dev/blog/%E3%83%95%E3%83%AD%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%83%89/csrf_spa/
M5NanoC6、GROVER- MicroSwitch(マイクロスイッチ(リミットスイッチ))で遊ぶ。(スイッチクラス利用)



今回のお題

![o_coq558.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/4f54b495-3142-bd3b-d1db-67a993894c65.jpeg)

![o_coq559.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/52a67c78-a2fd-0806-bc29-cf1a4f74efa4.jpeg)



目的
Groveポートを通して接点の状態を入力して遊ぶ
何でも良かったんだけど、安く、早く手に入ったのが
マイクロスイッチ



結果

![o_coq557.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/eba55023-e49b-d31f-9124-317608ddd40b.jpeg)

![o_coq560.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/7cc3873c-d1db-4580-aba5-98e1fd6f26e3.jpeg)


プログラム

```c++



//button_Grover_button_M5NanoC6_1

#include <M5NanoC6.h>

#include "Arduino.h"
#include "utility/Button_Class.hpp"

using namespace m5;

Button_Class BtnS;


void setup() {

    NanoC6.begin();

    pinMode(1, INPUT); //Grover GPIO1
  
    pinMode(M5NANO_C6_BLUE_LED_PIN, OUTPUT);
}


void loop() {
    
    uint32_t ms = millis();
    BtnS.setRawState(ms, digitalRead(1)); //Grover GPIO1

    
    if (BtnS.wasPressed()) {
        Serial.println("Button A was pressed");
        digitalWrite(M5NANO_C6_BLUE_LED_PIN, HIGH);
    }
    if (BtnS.wasHold()) {
        Serial.println("Button A was hold");
    }
    if (BtnS.wasReleased()) {
        Serial.println("Button A was released");
        digitalWrite(M5NANO_C6_BLUE_LED_PIN, LOW);
    }
}


```
Google Colaboratory入門

今回は、Pythonプログラムの作成や動作確認に便利なGoogle Colaboratoryについて紹介します。
# Google Colaboratoryとは?
Google Colaboratory(通称 Google Colab)は、Googleが提供する無料のクラウドベースのPythonプログラミング環境で、特にデータサイエンス・AIの分野で広く利用されています。
インストールや設定が不要でPythonコードを実行できるため、初心者からプロフェッショナルまで幅広い層に支持されています。

![image-14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3676048/57b7fb0e-4f8e-b326-41ca-4c64ccd2f519.png)


# Google Colabの特徴
## 無料で利用可能
Google Colabは基本的に無料で利用できます。無料プランでも基本的にGPUを利用できますが、時間帯によっては有料プランのユーザが優先され、利用できない場合があります。
## クラウドベースでどこでもアクセス可能
Google Colabはクラウドベースの環境であるため、インターネットに接続されていれば、どこからでも利用できます。
これにより、様々な場所や端末から作業を継続でき、他の方と共有するのも簡単に行えます。
## 必要なライブラリが簡単に利用可能
データサイエンスや機械学習でよく利用されるPythonのライブラリ(NumPy、Pandas、TensorFlow、PyTorchなど)が最初からインストールされています。
また、pipコマンドを使って追加のライブラリを簡単に追加することもできます。
# Google Colabの使い方
Google Colabを始めるにはGoogleアカウントでログインし、
https://colab.research.google.com/notebooks/welcome.ipynb?hl=ja
にアクセスします。
その画面から「ファイル」→「ドライブの新しいノートブック」で新規に開始することができます。

![image-12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3676048/4bd702f9-8909-4113-b879-9d61d21b5159.png)

Google Colabは、「セル」と呼ばれるブロックにコードを入力し、このセル単位でプログラムを実行する対話型の実行環境です。
セルで実行された結果は、そのままセルの下に表示されるため、インタラクティブにコードを試行錯誤できます。
さらに、「すべてのセルを実行」ですべてのセルを最初から逐次実行することもできます。

![image-4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3676048/3fd75145-3133-db78-c1cb-b7461c17aff6.png)


また、テキスト用のセルも用意されており、Markdown形式で記述してコードの説明を追加できます。

![image-7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3676048/d1546c18-f716-17e0-e6fd-4b4a6f288bc2.png)

「!」をつけてLinuxコマンドをセル上で実行することもできます。transformersなどの標準装備されていないライブラリを`!pip install transformers`とセルで実行することでインストールすることができます。

![image-10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3676048/a9467533-c5a1-fa40-ec3d-a7eac30942e8.png)

また、`!mkdir`でColab環境内に一時的にディレクトリを作成したり、`!wget`でファイルをダウンロードし、保管することもできます。

![image-11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3676048/4b6d1f86-0fe1-f5cf-a58f-d8d54cf76ffb.png)

Google Driveと連携してドライブにファイルを保存あるいはドライブにあるファイルを読み込んだりすることも可能です。
ファイルメニューから「ドライブをマウント」をクリックすることでGoogle Driveをマウントできます。

![image-13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3676048/f56162f4-ab9a-c2ba-ffc7-c679805bed59.png)


# まとめ
Google Colabは、Python初心者からデータサイエンスのプロフェッショナルまで幅広いユーザーにとって非常に便利なツールです。
無料で利用でき、強力なクラウドベースの計算リソースを提供しているので、Pythonを学び始める、あるいは機械学習を効率的に試したい方にGoogle Colabは非常におすすめです。
短時間内、大量シーケンス採番のSQL パフォーマンス改善対策(kosekiメモNO5)

# はじめに

データ移行PJTに、シーケンス採番はパフォーマンスの課題になったことがわかりました。
シーケンスのcashsizeを調整してみましたが、効果はあんまりなく、さらにselectの属性を調整して解決できましたので、メモしておきます。

# 動作環境
環境的には関係なし。


# 使い方
キャッシュを保存しないように設定する。

>flushCache:
true を指定した場合、ステートメント実行時にローカルキャッシュおよび２次キャッシュがフラッシュ（＝クリア）されます。select ステートメントの場合、デフォルト値は false です。

>useCache:
true を指定した場合、ステートメントの結果が２次キャッシュに保存されます。select ステートメントの場合、デフォルト値は true です。


```testMapper.xml
<select id="selectIdBySeq" resultType="String" flushCache="true" useCache="false">
    select nextval('schema.testSequence')
</select>
```

# 参考文献
[Mapper XML ファイル | flushCache userCache](https://mybatis.org/mybatis-3/ja/sqlmap-xml.html)

***
以上
ruby初心者の備忘録１〜素数判定ロジック〜

お疲れ様です。セキュリティエンジニアを目指している吉川です。
日々の備忘録を自分が後で見直せるように書いていきます。
ruby初心者なのでもし同じように初心者の方が見た時に知識を共有できるよう頑張って更新します。よろしくお願いいたします。


コードを書いてrubyを覚えていこうという試みです。

# 素数判定ロジック

素数判定メソッド
def prime?(number)
  return false if number <= 1
  (2..Math.sqrt(number)).each do |i|
    return false if number % i == 0
  end
  true
end

テスト用の配列
numbers = [2, 3, 4, 7, 10, 13, 17, 18, 19]

各数字が素数かどうかを判定して表示
numbers.each do |num|
  if prime?(num)
    puts "#{num} is a prime number."
  else
    puts "#{num} is not a prime number."
  end
end



素数ってなに？ >>1とその数自身でしか割り切れない自然数


# return false if number <= 1
素数ではない特別なケースをチェック。1以下の数字は素数ではないので、
falseを返す。0や負の数、1も素数ではないことを記述。

# (2..Math.sqrt(number)).each do |i|
2からMath.sqrt(number)（√number）までの整数を使ってループ。

√（平方根）を使う理由：例えば、n = 100 の場合
100が素数かどうかを調べたいとき、2から100まで全部で割り算を試すのは非効率。
100 の平方根である √100 = 10 までの数で割り算を試せば十分。
100の約数は必ず10以下の数のペア（例: 2×50, 4×25）と、10以上の数のペアが対応する形になっています。

結論、nが素数でない場合、その約数の一つは √n以下に存在するはず。それを利用して、余計な計算を省いているのが((2..Math.sqrt(number)).each do |i|)の部分。
データリンク層の制御

データを送り出すときのタイミングを知れるそうだ

# CSMA/CD(搬送波感知多重アクセス)
>イーサネットで採用されているメディアアクセス制御方式で、`衝突検知方式`を採用する。

> * 各ノードは伝送媒体が`使用中かどうかを調べて、使用中出なければデータの送信を開始`する。
> * `複数のノードが同時に通信を開始するとデータの衝突`が起こる。
> * 衝突を検知し、`一定時間（ランダム）待った後で、再送`する。
> * `一定の距離以上のケーブルでは衝突が検知できない`

だからブリッジやスイッチングパスで衝突範囲を細かくするのか

> トラフィックが増加するにつれて衝突が多くなり、再送が増え、さらにトラフィックが増加するといった悪循環に陥る可能性があります。このため、`伝送路の使用率が３０%を超えると実用的でなく`なります。

# トークンパッシング方式
>`トークンによる送信制御`を行う方式です。バス型のLANで使用するトークンバス方式と、リング型LANで使用するトークンリング方式があります。

> * ネットワーク上を`フリートークンと呼ばれる送信権`のためのパケットが巡回する。
> * `フリートークンを獲得したノードのみ`が送信を行うので衝突を回避できる。

>`伝送媒体上での衝突は発生しません`が、トラフィックが増加するにつれ`トークンを獲得しにくくなり`、徐々に`遅延時時間が増加`します。しかし、衝突による再送制御の必要がないため、伝送路の使用率に対する`遅延時間の増加の程度はCSMA/CD方式より緩やか`です。

> 伝送媒体が十分空いていれば、オーバヘッドの小さいCSMA/CDの方が速い
### トークンリング方式の形
https://e-words.jp/w/%E3%83%AA%E3%83%B3%E3%82%B0%E5%9E%8B%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF.html#google_vignette

トークンパッシング方式の形はわからなかった。

# TDMA方式
>CSMA/DC、トークンパッシング法式と並ぶ主要なデータリンク技術です。TDMAでは、`ネットワーク（伝送路）を利用できる時間を細かく区切り、割り当てられた時間は各ノードが独占`する方式です。なお、TDMAは`コレクション型の通信であり、相手との通信路を確立してから通信`します。

### TDM（時分割多重）
>ネットワーク上にデータを送信する時間を割り当て（これをタイムスロットという）、`タイムスロットごとに異なるデータを伝送する`ことで多重化を図る。TDMを用いたアクセス制御がTDMA

https://gihyo.jp/book/2023/978-4-297-13865-3

# 感想
現在使用されている方式はどれだろうか？または用途によって変わるのか？
トークンというデータを流す権利を獲得することで衝突を回避して伝送速度を落とさないようにしている

気になることが書いてあった。

https://fujikura-solutions.co.jp/technology/106/

https://www.komazawa-u.ac.jp/~w3c/lecture/network/ccna3.html

https://www.keyence.co.jp/ss/products/controls/network/basics/topology.jsp
M5NanoC6、GROVER- Button(ボタンスイッチ)で遊ぶ。(スイッチクラス利用)




今回のお題


![o_coq555.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/ff6b1626-4513-697b-4dbb-d2c49ec97ca7.jpeg)

![o_coq556.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/90630e41-0169-5db1-4b32-98e25aca76d7.jpeg)

結果

![o_coq554.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/d564af17-0041-1660-3e68-0f4d602b5f89.jpeg)


プログラム

```c++


//button_Grover_button_M5NanoC6_1

#include <M5NanoC6.h>

#include "Arduino.h"
#include "utility/Button_Class.hpp"

using namespace m5;

Button_Class BtnS;


void setup() {

    NanoC6.begin();

    pinMode(1, INPUT); //Grover GPIO1
  
    pinMode(M5NANO_C6_BLUE_LED_PIN, OUTPUT);
}


void loop() {
    
    uint32_t ms = millis();
    BtnS.setRawState(ms, digitalRead(1)); //Grover GPIO1

    
    if (BtnS.wasPressed()) {
        Serial.println("Button A was pressed");
        digitalWrite(M5NANO_C6_BLUE_LED_PIN, HIGH);
    }
    if (BtnS.wasHold()) {
        Serial.println("Button A was hold");
    }
    if (BtnS.wasReleased()) {
        Serial.println("Button A was released");
        digitalWrite(M5NANO_C6_BLUE_LED_PIN, LOW);
    }
}


```
大量データ取得SQLのパフォーマンスの対策ーーCursor(カーソル)(kosekiメモNO4)


データ移行PJTの中に、大量データ取得sqlのパフォーマンスがすごく悪くて、対策などをメモしておきます。

## 背景
移行元データ量は数千万が存在して、全量データを一発で取得するのはかなり時間がかかります。
対策としては、limit offsetを利用して、単位件数で取得していきました。
select * from table limit 10000 offset 0;

が、offsetは大きくなると、実施時間が長くなり、落ちることも発生しました。

## 対策
1\. 取得結果のreturn listはListからCursorに変更する。
import org.apache.ibatis.cursor.Cursor;

2\. select sqlにfetchSizeを追加する。


サンプルソース：

```java:testMapper.java
package com.xxx.xxx.xxx.xxx.test;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.cursor.Cursor;

@Mapper
public interface TestMapper{

Cursor<testDto> selectAll();

}
```


```xml:testMapper.xml
<select id="selectAll" fetchSize="10000" restultType="com.xxx.xxx.xxx.xxx.test.testDto">
    select * from testTable
    where a = ''
</select>

```

:::note info
参照リンク集：

[[MyBatis]大量データをマッピングするときはCursorを使おう](https://qiita.com/riekure/items/761abcf54c9216db596f "MyBatis]大量データをマッピングするときはCursorを使おう")

[MyBatis　6.2.2.3.1. fetchSizeの設定](https://terasolunaorg.github.io/guideline/5.2.1.RELEASE/ja/ArchitectureInDetail/DataAccessDetail/DataAccessMyBatis3.html#fetchsize "6.2.2.3.1. fetchSizeの設定")
:::

***
以上
PythonでExcelにデータバーを追加する

Excelのデータバーは、データの可視化ツールです。セルの値の大きさを棒グラフの形で視覚化することができ、特に同じ列や行の値を比較するのに適しています。データ・バーを追加すると、大量の数値情報を一目で見ることができます。この記事では、PythonでExcelにデータバーを追加する方法を紹介します。


## Python Excel API - インストール
[**Spire.XLS for Python**](https://www.e-iceblue.com/Introduce/xls-for-python.html)ライブラリは、様々なExcel条件付き書式の設定やその他のExcel処理機能をサポートしています。下記のpipコマンドで直接インストールできます： 
>pip install Spire.XLS

また、以下のリンクから製品パッケージをダウンロードし、ローカルパスからインストールすることもできます。

https://www.e-iceblue.com/Download/Spire-XLS-Python.html


## PythonでExcelにデータバーを追加する
Excelの指定されたセル領域にデータバーを追加するには、Spire.XLS for Pythonライブラリが提供する条件付き書式のための様々なインターフェースを通じて実現する必要があります。主な手順は以下の通りです：
1. `LoadFromFile()` メソッドを使用して Excel ファイルをロードします。
2. 指定されたワークシートを取得し、それに条件付き書式を追加して **`XlsConditionalFormats`** オブジェクトを返します。
3. **`AddRange()`** メソッドを使用して、条件付き書式設定を適用するセル範囲を指定します。
4. **`AddCondition()`** メソッドを使用して条件を追加し、**`IConditionalFormat.FormatType`** プロパティによってそのタイプを `DataBarDataBar` に設定します。
5. データ・バーの塗りつぶし効果と色を設定します。
6. `SaveToFile()` メソッドを使用して、ドキュメントを保存します。

**Pythonのコード：**
```python
from spire.xls import *
from spire.xls.common import *

# Excelファイルの読み込み
workbook = Workbook()
workbook.LoadFromFile("sample.xlsx")

# 最初のワークシートを取得する
sheet = workbook.Worksheets[0]

# 指定したセル範囲に条件付き書式を適用する
xcfs = sheet.ConditionalFormats.Add()
xcfs.AddRange(sheet.Range["D2:D12"])

# データ・バーの追加
format = xcfs.AddCondition()
format.FormatType = ConditionalFormatType.DataBar

# 塗りつぶし効果と色の設定
format.DataBar.BarFillType = DataBarFillType.DataBarFillGradient
format.DataBar.BarColor = Color.get_Red()

# ファイルを保存する
workbook.SaveToFile("ExcelDataBar.xlsx", ExcelVersion.Version2016)
workbook.Dispose()
```

***出力ファイル***
![ExcelDataBar.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2811193/12db2d7e-c90f-0a6d-cc9d-954bf7e75817.png)
* * *

Spire.XLS for Pythonライブラリは、行の色の交互表示、平均値より上または下の値の強調表示、上位および下位の値の強調表示など、その他の条件付き書式の設定もサポートしています。

https://www.e-iceblue.com/Tutorials/Python/Spire.XLS-for-Python/Program-Guide/Conditional-Formatting/Python-Apply-Conditional-Formatting-in-Excel.html
PHPにおけるNULL安全

# NULL安全とは
　NULLによるエラーをコンパイルまたは静的解析で検出し、実行時にNULLによるエラーを起こさないことです。NULL安全が高いか低いかは基本的にはプログラム言語に依存します。型には基本的にNULL許容型とNULL非許容型があり、それらが別物として扱われることがNULL安全を満たす条件です。理由は後述します。

　PHPにおけると銘打っておきながら、まずはkotlinという言語を例に上げてNULL安全の説明をします。kotlinは静的型付き言語なので基本的に型を定義していくのですが、通常定義される型はNULL非許容型になります。NULL非許容型とはその名の通りNULLを代入できない型のことです。
```kotlin
//NULL非許容型
var intObject: Int = 10
var stringObject: String = "test"
var sampleObject: SampleObject = SampleObject()

//以下をコンパイルするとエラー（最近はIDEで怒ってくれる）
var intObject: Int = null
var stringObject: String = null
var sampleObject: SampleObject = null
```
　NULL許容型（NULLも代入できる型）を利用するには明示する必要があります。
```kotlin
//NULL許容型
var intObject: Int? = 10
var stringObject: String? = "test"
var sampleObject: SampleObject? = SampleObject()

//以下はコンパイルしてもエラー出さない
var intObject: Int? = null
var stringObject: String? = null
var sampleObject: SampleObject? = null
```
　NULL許容型を参照する場合は以下の制約が設けられます。変数がNULLであっても何らかの意図的な処理を行わせるというニュアンスです。
```kotlin
//セーフコール演算子（変数がNULLならその先は実行せずNULLを返す）
var stringObject: String? = "test"
var stringObjectLength: Int? = stringObject?.length

//非NULLアサーション演算子（変数がNULLならNullPointerExceptionを投げる）
var intObject: Int? = 10
var addObject: Int = 10 + intObject!!

//if文でNULLじゃないことを保証する
var intObject: Int? = 10
if (intObject != null) {
  var addObject: Int = 10 + intObject
}
```
　NULL許容型とNULL非許容型を別物として扱うことで、コンパイル時にNULL許容型が参照ルールを守っているかをチェックできるので、コンパイル時点でNULLによるエラーをすべて洗い出せるよねというのがNULL安全の思想です。

　NULL安全のメリットとしてはコーディング中に変数が扱いやすくなることだと思います。ある変数を利用するとき、その変数の宣言時の型がNULL非許容型であればその型が（lateinitとかはありますが）確実に入っているので、その変数がどういった処理を辿ってきたかやNULLが入る可能性を考慮してif文で防御するといった作業が不要になります。NULL許容型の場合だけ、その変数の処理を確認して利用時点での正しい参照方法を選択すればよいです。強い制約下の元コーディングが行えるので認知不可が軽減でき、その制約も明示すれば回避できるので縛られすぎることもないです。

# PHPにおけるNULL安全
　話が長くなりましたが本題のPHPにおけるNULL安全に移りたいと思います。前提としてPHPはNULL安全が低いです。NULL許容型とNULL非許容型を区別してコンパイラまたは静的解析時にNULL許容型に対してチェックを行うことでNULL安全は高まるので、実行時型が決定される動的型付き言語だとそもそも難しいです。
　ただ書き方次第でNULL安全は高めることが出来ます。

①引数、戻り値の型を指定する。
　PHPの引数、戻り値に設定される型はNULL非許容型です。なので型を指定しておけば実行しなくてもintelephenseやStanでエラーを出してくれます。
　※型の制約はあくまで引数として受け取った時だけで、ローカル変数として使う場合は型の制約がないので注意。
```php
function foo(string $name) {
    return $name;
}
foo(null); //TypeError

function too(): string {
    return null;
}
too() //TypeError
```
　NULL許容型の場合は以下のように書きます。
```php
function doo(?string $name): ?string {
    return $name;
}
doo(null) //エラーなし
 ```

②クラス変数宣言時に型を指定する。
　クラス変数に設定される型もNULL非許容型です。これも型を指定しておけば実行しなくてもintelephenseやStanでエラーを出してくれます。PHPStanはLevel8からNULL非許容型プロパティのアクセスをチェックします。
```php
//NULLの参照。PHPStan解析レベル８から怒ってくれる。「$this->testValue ?? ""」にしたら怒られない。
class Test {
	public ?string $testValue;

	function doo(): void {
		echo(mb_strlen($this->testValue));
	}

	function too(): void {
		$this->testValue = "ddd";
	}
}
```
```php
//NULLの代入。PHPStan解析レベル８で怒ってくれる
class Test {
	public string $testValue;

	function doo(): void {
		$this->testValue = null;
	}
}

//以下のようなケースはPHPStan解析レベル８でも怒ってくれない。tooの戻り値が「null」とか「null|string」とかなら怒ってくれるので引数、戻り値の型指定が重要になる
class Test {
	public string $testValue;

	function doo(): void {
		$this->testValue = $this->too();
	}


	function too() {
		return null;
	}
}
```
　まとめると、引数、戻り値の型を指定して、クラス変数の型を指定して、PHPStanの解析レベルを８まで上げたら比較的NULL安全になります。道のりは長いですが頑張りましょう。
GithubにSSH接続できているのに、名前解決エラーによりGit操作ができないとき

**`ssh -T git@github.com`を実行した際に，`You've successfully authenticated`と表示されているのが前提です**
# 原因
~/.gitconfigに[user]項目以外の項目（[url "github:"]）が含まれていた
# 解決策
~/.gitconfigから[url "github:"]や[url "git@github.com:"]の項目を削除する（全体を削除しなくても.insteadofの項目を除くだけでも良いと思います）
【AtCoder】AtCoderで意外と役立つテンプレ集1

# はじめに

競プロerのwhile-true-ifです。久しぶりにAtCoderのまじめな記事を書きます。

# テンプレートの良さ

皆さんはテンプレートを使っていますか？テンプレートを使うことで、コードを書く時間を短くすることができます。また、自分なりにカスタマイズすることで、C++のようなわかりにくい言語が書きやすくなります。

# 自分のPythonで使うテンプレート

```python
def si():
    return input()
def i():
    return int(input())
def m():
    return map(int,input().split())
def l():
    return list(map(int,input().split()))
def o(s):
    print(s)
    return 0
def bin(n):
    return format(n, 'b')
def re(a):
    return list(reversed(a))

se=list("abcdefghijklmnopqrstuvwxyz")
le=list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")


```

よく使うのは、`m()`です。AtCoderの半角スペース区切りはよく出てくるのでかなり助かります。AtCoderのAB問題のような簡単なものでは、`print`を`o`にするのも案外役に立ちます。

# 問題によって使うことがあるテンプレート

## 二次元累積和

```python
def Cumulative_Sum_2D(X):#2次元累積和 Xにグリッド情報
    row=[]
    H=len(X)
    W=len(X[0])
    import itertools
    for i in range(H):
        row.append([0]+list(itertools.accumulate(X[i])))

    col=[list(x) for x in zip(*row)]

    grid1=[]
    grid=[]
    for i in range(W+1):
        grid1.append([0]+list(itertools.accumulate(col[i])))

    grid=[list(x) for x in zip(*grid1)]
    return grid
```

2次元累積和はたまに使うことがあります。面倒なのでいざという時のために作っておくと役に立つかもしれません。

## 座標圧縮

```python
# 座標圧縮(0スタート)
def compression0(A:list):
    D = list(set(A))
    D.sort()
    X = dict()
    for i in range(len(D)):
        X[D[i]] = i
    ret = []
    for a in A:
        ret.append(X[a])
    return ret

# 座標圧縮(1スタート)
def compression1(A:list):
    D = list(set(A))
    D.sort()
    X = dict()
    for i in range(len(D)):
        X[D[i]] = i + 1
    ret = []
    for a in A:
        ret.append(X[a])
    return ret
```

上の座標圧縮は一番小さい値が0スタートで、下の座標圧縮は一番小さい値が1スタートになります。AtCoderでは結構役立ちます。

## 文字圧縮

```python
def string_compression(s: list):
    a = []
    now = s[0]
    c = 0
    for i in range(len(s)):
        if s[i] == now:
            c += 1
        else:
            a.append([now, c])
            now = s[i]
            c = 1
    a.append([now, c])
    return a
```

文字列のlistが与えられ、[文字,連続した数]で圧縮するだけ。役立つ場面は少ない。

# 終わりに

今回の記事で紹介したテンプレートは一部です。次の記事では、特定の問題に特化したテンプレを紹介するつもりです。
Raspberry Pi 覚書

## Raspberry Pi Imager 
[Raspberry Pi OS](https://www.raspberrypi.com/software/)

## MAC hostsの場所
/private/etc/hosts

### 初期ログイン
- ubuntu の場合

```
user ubuntu
passward ubuntu
You are required to change your password immediately (administrator enforced)
Current password:
Nre password:
Retype new password;
```
### バージョン確認

- Kernel バージョン確認

```
>uname -r
6.6.47+rpt-rpi-2712
```

```
>uname -a
Linux raspi3 6.6.47+rpt-rpi-2712 #1 SMP PREEMPT Debian 1:6.6.47-1+rpt1
(2024-09-02) aarch64 GNU/Linux
```

- OSのバージョン確認

```
>lsb_release -a
No LSB modules are available.
Distributor ID:	Debian
Description:	Debian GNU/Linux 12 (bookworm)
Release:	12
Codename:	bookworm
```

### インストールパッケージ

```
sudo apt install g++ gcc cmake make
sudo apt install libi2c-dev git-core
sudo apt install alsa-utils libasound-dev
```

### SSH 立ち上げ

```
sudo apt install openssh-server
sudo apt install net-tools
sudo systemctl start ssh
```
## netplan の設定
- 99-custom.yamlを作成する

```
sudo vi /etc/netplan/99-custom.yaml
```

```
sudo netplan apply
```

### Network の確認

```
ifconfig
wifi-status
```

## Nodejs インストール
[Nodejs Installation](https://github.com/nodejs/help/wiki/Installation)

- 圧縮ファイルを解答する

```
sudo mkdir -p /usr/local/lib/nodejs
sudo tar -xJvf node-$VERSION-$DISTRO.tar.xz -C /usr/local/lib/nodejs 
```

- ~/.profile にnodejsのパスを追加
```
# Nodejs
VERSION=v16.16.0
DISTRO=linux-arm64
export PATH=/usr/local/lib/nodejs/node-$VERSION-$DISTRO/bin:$PATH
```

- ~/.profile　を更新
```
. ~/.profile
```

## tar の解凍

```
tar -xvf abcdefg.tar.xx
tar -xvf abcdefg.tar.xx -C directory
```

## GUI の導入

[Ubuntu Server22.04に後からGUI（デスクトップ）環境をインストールする方法](https://4thsight.xyz/40719)

```
sudo apt install ubuntu-desktop
```

[Ubuntu 20.04の起動時CUI、GUI設定](https://tek2tech.com/ubuntu-2004-desktop-environment/)

```
systemctl get-default
```

graphical.targetだとGUI起動、multi-user.targetだとCUIでの起動です。

```
sudo systemctl set-default multi-user
sudo systemctl start gdm3
```

## Webサーバを立ち上げる

[ラズベリーパイでWebサーバー「Apache」を設定する](https://www.fabshop.jp/webserver-apache/)

```
sudo apt install apache2
```

- /var/www/html ルートのフォルダ

raspberry.pi からは

```
http://localhost/index.html
```

同じネットワーク上のブラウザから

```
http://raspi.local/index.html
```

でアクセスできる。

## Local 設定

### 日本語キーボード設定

```
sudo dpkg-reconfigure keyboard-configuration
```

### 時計の設定

```
sudo timedatectl set-timezone Asia/Tokyo
```

### ローカル設定

```
locale -a
sudo locale-gen ja_JP.UTF-8
```

### Chromeum のインストール

```
sudo apt install chromium-browser
```

### I2Cバスの検索

```
i2cdetect -y 0
I2C bus name doesn't match any bus present!
```
Raspberry Piでは、デバイスでI2Cバスの番号が1に設定されている事が多い。
その場合には、以下のエラーが出る。解決するためには以下のコマンド、この場合には18H, 70Hにデバイスがつながっている。
```
i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- 18 -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: 70 -- -- -- -- -- -- -- 
```
[Unity][RoboCup] 3DLogPlayerを作ってみて学んだこと

[2024/10/27]
WebGL版のビルドに対応し、GitHub Pagesに公開しました。
https://suzukinene.github.io/CrossViewWeb/

# はじめに
会社員をやめ、フリーランスとして働き始めるにあたり、自分のポートフォリオとして初めてOSSに挑戦しました。

# 作ったもの
RoboCup シミュレーション 2Dリーグのログファイルを読み込んで試合を再現する3Dアプリを、Unityを使って開発しました。
RoboCupに参加したことはありませんが、以前から作ってみたいと思っていたものを、今回開発してみました。
「2Dリーグなのに、なんで3Dアプリ？」と興味を持たれた方、ぜひご覧いただければと思います。

![CrossView_Playing_001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/843818/3538006d-3322-f0c8-ab1b-d7e95a2316ba.png)

![CrossView_ThirdPerson_001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/843818/b7614fec-73c9-d4a7-335e-8cdc6c83780d.png)

# 保管場所
以下のURLをご参照ください。
（releaseページに、Windows版のインストーラもあります。）

https://github.com/SuzukiNene/CrossView3D

READMEに詳細を書きましたが、
Unity Asset Storeで購入したFREEの3Dモデルなどの外部データを私がGitHubで公開することはできないので、リポジトリには含めておらず、そのままではビルドできません。ご了承ください。

# 学んだこと
開発においては次の項目を学習し、実装に盛り込みました。

- 3DモデルにAnimationを適用する
- スクリプトからAnimation Stateを切り替える（Idle,Dash,Kickなど）
- 3Dモデルの首振り（顔の向き）をOnAnimatorIK()で制御
- ボールやプレーヤーの移動、向きの変更を滑らかに変化させる
- 自分でMeshを作って扇形のオブジェクトを足元に描画
- プレーヤーの視界（角度と距離）をスポットライトで表現
- 3DモデルのAnimator(FieldPlayerとGoalKeeper)を実行時に切替える
- UnityStandaloneFileBrowser(OSS)でファイル選択
- UnityWebRequestで、ローカルファイルまたはURLから読み込み
- DownloadHandlerScriptの活用で、受信データを逐次処理
- ログファイルのS式(S-Expression)を、主に正規表現でパース処理
- MonoBehaviour.InvokeRepeating()でタイマー処理
- プレーヤーの背番号は、UIレイヤー上にオーバーレイ表示
- 画面分割（CameraのViewPort Rect）
- 番外：InnoSetupでインストーラを作成

※もし、参考になりそうな内容がありましたら、ぜひ。
※また、ライセンス等でポカをしでかしている箇所がありましたら、ご指摘いただけますと大変助かります。

以上です。
csv.DictReaderオブジェクトを使いまわしてはいけない

# まとめ
csv.DictReaderオブジェクトを複数回参照してはいけない。
使いまわしたい場合、都度オブジェクトを再生成して参照するべき。

# 経緯
下記コードの実行時、1度目と2度目で出力結果が変わった。
原因はcsv.DictReaderがイテレータであることにより、1度目のループでイテレータが消費された為であった。
```py
def is_in_targets(data, targets: csv.DictReader):
    if "No." not in targets.fieldnames:
        return False

    # csv.DictReaderをforループにかける
    for row in targets:
        if data["番号"] not in row["No."]:
            continue

        if data["年齢"] in row["Age"]:
            return True
    return False

data = {"番号": "1", "年齢": "12"}
targets = """No.,Age
1,12"""
targets_csv = csv.DictReader(StringIO(targets))
print(is_in_targets(data, targets_csv))    # 出力結果: True
print(is_in_targets(data, targets_csv))    # 出力結果: False
```

# 対策
### 1. リスト変換
```py
-    for row in targets:
+    for row in list(targets):
```
#### 結果：失敗
list()を実行した場合もイテレータが消費されてしまう

### 2. オブジェクト再生成
DictReaderオブジェクトを直に渡すのではなく、元のデータを渡した後に関数内でDictReaderオブジェクトを生成する
```py
- def is_in_targets(data, targets: csv.DictReader):
+ def is_in_targets(data, targets_str: str):
+     targets = csv.DictReader(StringIO(targets_str))
```
#### 結果：成功
```py
data = {"番号": "1", "年齢": "12"}
targets = """No.,Age
1,12"""
print(is_in_targets(data, targets))    # 出力結果：True
print(is_in_targets(data, targets))    # 出力結果：True
```
phpの参照渡し（リファレンス渡し）を使ってみる

:::note alert
参照渡しは非推奨のようです。コメントに詳細付けてもらったのでそちらをご確認ください。
:::

<h1>概要</h1>
公式マニュアルには以下のように記載がされています。
<blockquote>
<b>リファレンス渡し</b><br>
リファレンスにより関数に変数を渡すことが可能です。この場合、関数内で その引数を修正可能になります。構文は次のようになります。
</blockquote>
マニュアル内では関数での利用法について記載がありますが、私が使用したforeach内での活用方法について記載します。

<h1>活用方法</h1>
共有する関数内で生成されたオブジェクトにおいて、一部の要素が不要といった状況が発生しました。
<br>


<h3>オブジェクトの特性</h3>

```tex
・親キーがランダムである
・削除対象の子キーが同一である
```


<h3>上記に倣ったオブジェクトの例</h3>

```php
$data = [
    '123456' => [
        'aaa' => [...],
        'bbb' => [...],
        'ccc' => [...]
    ],
    '446913' => [
        'aaa' => [...],
        'bbb' => [...],
        'ccc' => [...]
    ],
    '84351654' => [
        'aaa' => [...],
        'bbb' => [...],
        'ccc' => [...]
    ],
];
```

`ccc`のキーを持つオブジェクトを`$data`から削除をしたいとき、以下の様にforeachで回します。

```php
foreach($data as &$trim_data){
    //親キー＝'ccc'のオブジェクトを削除
    unset($trim_data['ccc']);
}
unset($trim_data);
var_dump($data);
```


<h3>出力結果</h3>

```objc
array(3) {
  ["123456"]=>
  array(3) {
    ["aaa"]=>
    ...
    ["bbb"]=>
    ...
  }
  ["446913"]=>
  array(3) {
    ["aaa"]=>
    ...
    ["bbb"]=>
    ...
  }
  ["84351654"]=>
  array(3) {
    ["aaa"]=>
    ...
    ["bbb"]=>
    ...
  }
}
```

<h1>まとめ</h1>
関数内で変数を参照渡しを利用して操作する場合、その変更はすべての呼び出し元に作用します。<br>参照渡しを使用することで、関数内での変更が直接呼び出し元の変数に影響を与えることになります。<br>これにより、変更を意図的に呼び出し元に反映させたい場合に便利です。
<br><br>
一方で、参照渡しを使わない場合は、関数内での操作が呼び出し元に影響を与えません。これにより、呼び出し元の変数を安全に操作することができると思い記事にしてみました。
<br><br>
こっちのがいいよみたいのがあればぜひコメントで教えてください！


<h1>参考</h1>
<a href='https://www.php.net/manual/ja/language.references.pass.php'>
php公式マニュアル
</a>
【ほぼ自分用】Linux系で必要なコマンドやライブラリがインストールされているか確認するシェルを作ったので共有

# 目次
[1.はじめに](#1-はじめに)
[2.前提条件](#2-前提条件)
[3.実装内容について](#3-実装内容について)
[4.シェルの中身](#4-シェルの中身)
[5.まとめ](#5-まとめ)

# 1. はじめに
このソフトウェアインストールしてくれやとか言われてドキュメントを見たら必要なライブラリとかコマンドがびっしりある....そんなときありますよね？（ない）
必要パッケージを一個一個確認するの手間だなと思って作ってみました。（β版）
結構適当に作ったので改善点とかここおかしくね？みたいなのあればコメントいただけるとしっぽブンブンです。
よろしくお願いします。

# 2. 前提条件
試したOS種はAL2023とRHEL8.9のみなので、動作保証はこいつらだけです。
あと実行するにあたっての権限付与とかのコマンドや作成までの過程はわかるものとして省いてますので、初心者の方については権限付与含め勉強がてら調べてみてください。

# 3. 実装内容について
ただ記載したコマンドとパッケージ名を確認していくだけです。
対話形式にしようとしたけどそれもまぁめんどくさいかなと。。。
確認したいライブラリ名やコマンドをリスト部分に""で囲って記載して編集してください
例では1,2のみになってますが行を増やせばその分チェックできる。。。。はず。。。。


# 4. シェルの中身
```shell:checkpackage-command

#!/bin/bash

# ライブラリとコマンドのリスト
libraries=(
    "確認したいライブラリ名１"
    "確認したいライブラリ名２"
)

commands=(
    "確認したいコマンド名１"
    "確認したいコマンド名２"
)

# インストールされていないライブラリとコマンドを保持するリスト
missing_libraries=()
missing_commands=()

# ライブラリの確認
for lib in "${libraries[@]}"; do
    if ! ldconfig -p | grep -q "$lib"; then
        missing_libraries+=("$lib")
    fi
done

# コマンドの確認
for cmd in "${commands[@]}"; do
    if ! command -v "$cmd" &> /dev/null; then
        missing_commands+=("$cmd")
    fi
done

# 結果の表示
if [ ${#missing_libraries[@]} -eq 0 ] && [ ${#missing_commands[@]} -eq 0 ]; then
    echo "全てのライブラリとコマンドがインストールされています。"
else
    if [ ${#missing_libraries[@]} -ne 0 ]; then
        echo "インストールされていないライブラリ:"
        for lib in "${missing_libraries[@]}"; do
            echo "$lib"
        done
    fi

    if [ ${#missing_commands[@]} -ne 0 ]; then
        echo "インストールされていないコマンド:"
        for cmd in "${missing_commands[@]}"; do
            echo "$cmd"
        done
    fi
fi
```

# 5. まとめ
必要ライブラリ＆コマンドがインストールされていないことがわかったらインストールは手動でやっていってください。
余談）インストールも自動化しようと思ったのですが、なんか怖いので確認のみのシェルにしてます。

検証環境とかでお試しで使ってみてくださいね
Webpacker::Manifest::MissingEntryError at / のエラーに悩まされた(備忘録)

Rails、Dockerで作業をしていたら以下のようなエラーが出てしまい、解決に時間がかかりました。

## エラーについて
エラーは以下の通りです。
```docker:エラー内容
Webpacker::Manifest::MissingEntryError at /

Webpacker can't find application.js in /myapp/public/packs/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.
```
調べてみると、webpackerがコンパイルされていないことが原因のようです。
したがって、以下のコマンドをターミナルで実行してみました。

```
$ docker compose exec web bash
$ bundle exec rails webpacker:install
$ bundle exec rails webpacker:compile
```

しかし、再度`Webpacker::Manifest::MissingEntryError at /`のエラーが出てしまいました。
```docker:エラー内容
Webpacker::Manifest::MissingEntryError at /

Webpacker can't find application.js in /myapp/public/packs/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.
```
そこで、以下のコマンドを実行してみました。
```
$ docker-compose run web bin/rails assets:precompile
```
これは、docker-compose で web コンテナを一時的に起動し、その中で bin/rails assets:precompile コマンドを実行して、Railsアプリケーションのアセットを事前にコンパイルする、という意味です。

簡単にまとめると、**Railsアプリのアセット(CSSやJavaScript)を本番環境用にコンパイル(より効率的な形式｟圧縮、結合｠に変換して、ブラウザが読み込みやすく)するということ**です。

その後、以下のコマンドを実行します。
```
$ docker compose up
```
すると、エラーがなくなりました！

## 応用編
```
$ bundle exec rails webpacker:compile
```
このコマンドは、Webpackerを使ってJavaScriptや関連するファイル（CSSや画像など）をコンパイルするためのコマンドです。
上記 `$ docker-compose run web bin/rails assets:precompile` との違いについて調べました。

結論でいうと、モダンなJavaScript(ES6やReact、Vueなど)を使っているなら、`webpacker:compile`を実行、伝統的なRailsアセット(Sprockets)を使っているなら、`assets:precompile`を実行という違いでした。

どちらも、「アセットを、本番環境用に最適化・コンパイルするために使われるコマンド」という点は共通していました。
状況によって使い分けることが大切なようです！

### 追記 2025/1/20
上記のエラーがまた出てしまい、以下のコマンドを試した
```
$ docker-compose run web bin/rails assets:precompile
```
しかし、エラー時は解決せず。
そこで、webpackerのキャッシュをクリアしてみることに。
```
$ rails webpacker:clobber
$ rails webpacker:compile
```
続いて、依存関係の再インストール
```
$ docker-compose run web yarn install
$ docker-compose run web bin/rails assets:precompile
```
すると、エラーがなくなりました！

Webpackerのキャッシュをクリアしたことや、依存関係の再インストールによって、環境が正しく同期されたことで、エラーが消えたと推測できます。
**キャッシュが古いとどのような問題が起こるか**、**依存関係が不整合である場合にどのような問題が起こるか**については今後まとめていきたいと思います。
Oracle 19c clientをしっかりアンインストールする

## INS-32103: 指定されたOracleホーム・ユーザーが、既存のホームのユーザーと一致しません。
※記載内容は自己責任にてお願いいたします。
※なんらかの理由で中途半端な削除となってしまったケース

## 状況
Oracle 19c clinetのbit数違いで誤って32bitインストールが実施された。
アンインストールと再インストールを行ったが、再インストール時に  
```
INS-32103: 指定されたOracleホーム・ユーザーが、既存のホームのユーザーと一致しません。
```
が表示され、指定のインストールが実施できないとの連絡を受けた。

思いのほか綺麗に再インストールできたが、WEB上に情報源がほとんどなかったため投稿。

## 対応
システム環境変数からOracle homeを探す。
インストーラーをダウンロードし、分かりやすい場所に置く。
以下のコマンドを発行
```
cd 分かりやすい場所
setup.exe -deinstall -home ここにOracel homeの場所
```
サービスにてOracleから始まるサービスがあれば停止
再起動
システム環境変数からOracle homeが消えていることを確認。あれば削除。
Program Files直下のOracle関連のフォルダ削除
インストール先のフォルダ削除
スタートボタン内のフォルダ削除
HKEY_CURRENT_USER　Software　内のOracleフォルダ削除
HKEY_LOCAL_MACHINE　Software　内のOracleフォルダ削除
念のため再起動して再インストール
サイト、メンテナンスページを表示

### 変更前
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3894299/5b308cec-d388-3053-327a-30b8df68d45c.png)

### 変更後
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3894299/5ee7a581-8e89-3aa1-6fe8-6f597b2aea13.png)

エラー発生
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3894299/6c941962-5f69-c457-2826-ff1d2be4cc16.png)

以下の設定ではエラーが解消しない
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3894299/53e92f89-82fd-093e-08c9-b6c11bc64d88.png)

### エラー解消方法
対象のサイトの [ ワークスペース ] > [ 管理 ] > [ ページ ] > [ Force.com に移動 ]
[ 公開アクセス設定 ] ボタンをクリック
＝＞ゲストユーザプロファイルページが表示される
下方にスクロールし、「有効な Visualforce ページアクセス」に、メンテナンスページ用のVisualforce ページを追加する。

ゲストユーザープロファイルを「ビルド」の以下の画面からも遷移できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3894299/b675983a-e564-b2a5-54e7-3a92cafc4fa3.png)


## 問題点

https://xxxx.my.site.com/byoauraOne/s/login/
では以前と前のログイン画面へアクセス出来てしまう。

ログアウト
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3894299/886ee841-3650-b38c-abd9-48aaaa0d90d5.png)

または、ログインボタンを押すと
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3894299/dc0486a6-0c7e-f8ab-d0a7-442664019bc9.png)

カスタムしたメンテナンスページが表示される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3894299/d302c22b-a982-3b54-ec70-62600cf716be.png)

### サンプルコード
```html
<apex:page id="CommunityInMaintenance" standardStylesheets="false" sideBar="false" showHeader="false" applyHtmlTag="true" applyBodyTag="false">

   <style>
       .centered{
           text-align: center;
       }
   </style>
   <div class="centered">
               <h1 class="text-center">メンテナンス中です。</h1>
               <p class="text-center">メンテナンス予定時間：2024/10/24の0:00～22:00</p>
   </div>

</apex:page>
```
参考したページ
[Experience Cloud（コミュニティ）のメンテナンスモードについて](https://regardie.dev/post-2799)
【Error】link_toブロックという概念


今回は`link_to`を使用していてエラーが起こったのでどのようなエラーで解決までの内容について記事にまとめておきたいと思います。

## 環境
- Windows, WSL
- Docker
- Ruby 3.2.3
- Rails 7.1.3



## エラーが発生したコード
```
<%= link_to "削除", post_path(@post), id: "button-delete-#{@post.id}", data: { turbo_method: :delete, turbo_confirm: "削除しますか？" }, class: "bg-red-500 hover:bg-red-700 text-white font-bold py-3 px-6 rounded text-lg" do %>
<% end %>
```
👆がエラーが発生したコードになります。
何がいけないのかぱっと見、気づくことができなかった私:sweat:

エラー画面には次のようなことが記載されていました
```
NoMethodError in Posts#show
Showing /myapp/app/views/posts/show.html.erb where line #40 raised:
undefined method stringify_keys' for "/posts/1":String
```


## 原因
改めて、私が入力していたコードを見直すと次のようなことが原因であるとわかりました。
- `link_to `ヘルパーの使い方が問題
- `link_to` ヘルパーを `do` ブロック付きで使用する場合、リンクの中身をブロック内に書く必要があるが**ブロック構文とテキストを同時に使おう**としている（Railsではサポートされていない）
- `link_to `に最初の引数 "削除" を渡しているため、`do `ブロックは使えない。
- `do` ブロックを使う場合、テキストやアイコンはブロック内に書く必要がある


## 解決策
**1.ブロックなしで書く場合（シンプルな方法）**
```
<%= link_to "削除", post_path(@post), id: "button-delete-#{@post.id}", data: { turbo_method: :delete, turbo_confirm: "削除しますか？" }, class: "bg-red-500 hover:bg-red-700 text-white font-bold py-3 px-6 rounded text-lg" %>
```
`"削除"` というテキストがボタンに表示され、削除処理が実行される


**2:ブロックを使う場合（テキストやアイコンを追加したい場合）**

```
<%= link_to post_path(@post), id: "button-delete-#{@post.id}", data: { turbo_method: :delete, turbo_confirm: "削除しますか？" }, class: "bg-red-500 hover:bg-red-700 text-white font-bold py-3 px-6 rounded text-lg" do %>
  削除
<% end %>
```
`do ... end` の中に `"削除"` というテキストが入るため、エラーは発生しない。


## さいごにまとめ
今回は削除ボタンに後からアイコンを追加したいので2番目でコードの修正を行いました。
アイコンを後から追加したい時は2番。テキストだけの時はブロックなしでの入力。
基本的なことはエラーが起こって改めて気づき。
今回の記事が何か参考になれば幸いです。
GitHubでソースをpushする方法

# 概要
GitHubでソースをpushする方法をメモとして残す。

# リポジトリの初期化
pushするソース配下のディレクトリに移動して
下記のコマンドを実行してリポジトリを作成する

```
git init
```

# ワーキングツリーからインデックスに追加

ワーキングツリーからインデックスにソースを追加する

```
git add .
```

# コミット
下記のコマンドでコミットを実行する

```
git commit -m <コメント>
```

# リモートリポジトリの紐づけ
下記のコマンドで紐づけを行う。
リモートリポジトリ名は「origin」
リモートリポジトリURLは「GitHub」から取得する

```
git remote add <リモートリポジトリ名><リモートリポジトリURL>
```

# リモートリポジトリのアップロード
```
git push -u origin main
```
Chrome拡張機能からページのグローバル変数にアクセスする

## 背景
Chromeの拡張機能から、対象ページに定義されているグローバル変数にアクセスしたいと思いましたが、その方法を見つけるのに苦労したので、ここに記録しておきます。

## 前提条件

以下のように、対象ページにwindow.$DATAというグローバル変数が定義されているとします。
このグローバル変数の値をコンソールに出力します。

```html:index.html
<script>
window.$DATA = {"PARAM":test}
</script>
```

## 失敗：スクリプトをインジェクトする

```text
./
├──manifest.json
└──content-script.js
```

```json:manifest.json
{
    "manifest_version": 3,
    "content_scripts": [
        {
            "js": [
                "content-script.js"
            ],
            "matches": [
                "https://*/*"
            ]
        }
    ]
}
```

```js:content-script.js
// ページにスクリプトをインジェクトする
const script = document.createElement('script');
script.textContent = `
  (function() {
    console.log(window.$DATA.PARAM);
  })();
`;
document.documentElement.appendChild(script);
script.remove();  // スクリプトをインジェクト後、削除する
```

こちらの方法では、以下のようなエラーが発生します。

:::note alert
Refused to execute inline script because it violates the following Content Security Policy directive
:::

このエラーは、Webページで設定された **Content Security Policy (CSP)** によって、インラインスクリプトの実行が禁止されているために発生します。

## 成功：`web_accessible_resources`を利用する

```text
./
├──manifest.json
├──content-script.js
└──web_accessible_resources.js
```

```json:manifest.json
{
    "manifest_version": 3,
    "content_scripts": [
        {
            "js": [
                "content-script.js"
            ],
            "matches": [
                "https://*/*"
            ]
        }
    ],
    "web_accessible_resources": [
        {
            "resources": [
                "web_accessible_resources.js"
            ],
            "matches": [
                "https://*/*"
            ]
        }
    ]
}
```

```js:content-script.js
const injectScript = (filePath, tag) => {
    var node = document.getElementsByTagName(tag)[0];
    var script = document.createElement('script');
    script.setAttribute('type', 'text/javascript');
    script.setAttribute('src', filePath);
    node.appendChild(script);
}
injectScript(chrome.runtime.getURL('web_accessible_resources.js'), 'body');
```

```js:web_accessible_resources.js
console.log(window.$DATA.PARAM);
```

:::note warn
多くのWebページはCSPを設定しており、インラインスクリプトや外部スクリプトの読み込みを厳密に制御しています。通常、Chrome拡張機能が注入するスクリプトは、このCSPの影響を受けるため、例えば「インラインスクリプト禁止」や「外部リソース制限」によってエラーが発生します。
:::

:::note
しかし、`web_accessible_resources`によってChrome拡張機能のリソースが正しく公開されると、そのリソースはCSPの制約に反しない形でWebページに読み込まれることができるようになります。これでエラーが発生しなくなります。
:::

## 参考資料
https://silverbirder180.hatenablog.com/entry/2022/01/16/000000#Web-Accessible-Resources
Azure Databricks上のデータをクラスタ経由で参照する際の最小権限

Azure Databricks上のデータを参照するにあたり、エンドユーザー(パワーユーザー)についても
直接Azure Databricks上の特定のカタログ、スキーマあるいはテーブル単位でPower BI Desktopから閲覧をさせたいシチュエーションの際に調査したところで最小の権限を書き留めておく。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269528/7ec13f5a-57d9-1dc8-e6ce-e75585b9964c.png)

上記を実現したい場合以下の2つの権限が必要
・利用するクラスターの「アタッチ可」権限
  ←利用するクラスターの起動、停止及び利用に使用
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269528/b7921925-f0b9-d890-0109-3691b3fcf604.png)


・対象のスキーマ(テーブル、カタログ単位でもOK)の権限プリセットのデータリーダー権限　
　←対象のオブジェクトの閲覧権限
　下図はカタログ単位の例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269528/414bd72b-79a8-0b18-8304-04dd3d6ba18e.png)
TomcatでのDBCPのデフォルト実装を確認してみた

# 概要
JDBCを利用してデータベース関連の処理をする際に、これまではDriverManager を利用してコネクションを取得していたが、一般的には、DataSource と JNDI という仕組みを使ってコネクションを取得する方が良いということを知った。
DataSource を用いれば、リソース情報を外部ファイルに記述することができるほか、コネクションプーリングの機能も使える。
このDataSourceの実装クラスは複数あるが、Tomcatに含まれるデフォルトの実装クラスが何か知りたかったので、プログラムを書いて実験してみたところ、ところ、Tomcatバージョン8、9、10においては`org.apache.tomcat.dbcp.dbcp2.BasicDataSource`がデフォルトで用いられることが分かった。

# DataSourceのデフォルトの実装クラスは？
Tomcat には、Tomcat DBCP Pool という Tomcat に特化した実装がある。Apache common DBCP よりもパフォーマンスが優れているとされている。
実装の切り替えには、DataSouce の設定時に、factory 属性を記述してファクトリクラスを切り替えるだけで良い。
factory 属性を記述しない場合は、デフォルトの実装クラスが用いられるとのこと。
Tomcatのバージョンによってデフォルトの実装クラスが違うようで、ChatGPTに聞いてみたが、結果が判然としなかった。
そこで、実際にプログラムを書いて実験してみた。

# 実験
### 環境
- eclipse(pleiades all in one) 2022
- 上記に含まれるTomcat。すなわちTomcat8、9、10
### プログラム
試験のためのプログラムは次のとおり。
```jsp
<%@page import="javax.sql.DataSource"%>
<%@page import="javax.naming.InitialContext"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<%
	final String RESOURCENAME = "jdbc/mydb";

    try {
        InitialContext ic = new InitialContext();
        DataSource ds = (DataSource)ic.lookup("java:comp/env/" + RESOURCENAME);
        out.print(ds);
    } catch (Exception e) {
        // TODO: handle exception
        out.print(e);
    }
    %>

</body>
</html>
```
これを、Tomcatのバージョンごとのサーバ上でそれぞれ動かした。

### 結果
Tomcat8、9、10 すべて
`org.apache.tomcat.dbcp.dbcp2.BasicDataSource`
であった。
ChatGPTで教育用の教科書やガイドを自動生成する方法：初心者向けガイド

![hq720.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3916556/2eb55bae-4d4c-c3e8-b559-36164a8ef20b.jpeg)

コンテンツ制作におけるAIの進展により、教育技術は新たな高みに達しつつあります。特に学習教材の自動生成はAIの最もエキサイティングな応用の1つであり、教科書の作成もその一環です。教育者や開発者にとって、学習コンテンツ作成にかかる時間を短縮することが大きな魅力となっています。ChatGPTは、シンプルなプロンプトに基づいて人間らしいテキストを生成できるため、この目的に非常に人気のあるツールとなっています。

この記事では、ChatGPTを使って教科書やガイド、レッスンプランなどの教育コンテンツを自動的に生成する方法を見ていきます。

なぜChatGPTを教科書作成に使うのか？
教科書やその他の教育リソースを作成するのは時間がかかります。教育者やコンテンツ作成者にとって、コンテンツを執筆し、トピックをリサーチし、読みやすい形式に整えることは容易ではありません。ChatGPTは以下の方法で支援することができます：

コンテンツ生成プロセスのスピードアップ: チャプター全体をわずか数分でChatGPTが生成します。
編集のための基盤を提供: AIが生成したテキストは複雑なトピックに対して調整が必要ですが、良い出発点を提供します。
パーソナライズされた学習体験: 教科書は、学生の知識レベルや特定の分野に焦点を当てたものを作成できます。
始めに：ChatGPTの設定
まず、ChatGPTでテキストを書けるようにするためには、OpenAIのAPIにアクセスする必要があります。以下はステップバイステップのガイドです。

ステップ1: OpenAI APIへのアクセス
OpenAI APIにサインアップするにはこちらから。アクセスできたら、APIキーを取得し、それを使ってプロンプトを送信し、モデルからの応答を受け取ります。

ステップ2: OpenAI Pythonパッケージのインストール
Pythonを使用してChatGPTとやり取りする場合、OpenAIパッケージをインストールする必要があります：

bash
Copy code
pip install openai
次に、スクリプト内でAPIキーを初期化します。

python
Copy code
import openai

openai.api_key = 'your-api-key-here'
ステップ3: プロンプトを送信して教育用テキストを生成
設定が完了したら、プロンプトをChatGPTに送信し、教育コンテンツを生成します。たとえば、「Pythonの概要」についてのチャプターを作成したい場合は、次のプロンプトを送信できます。

python
Copy code
response = openai.Completion.create(
  model="text-davinci-003",
  prompt="Write a chapter on Introduction to Python. Cover the basics, including data types, variables, and control flow statements.",
  temperature=0.7,
  max_tokens=1500
)

chapter_text = response.choices[0].text.strip()
print(chapter_text)
ステップ4: 教科書の構造化
複数のセクションのテキスト生成が完了したら、教科書を構造化する必要があります。ほとんどの教育用教材には、次のような明確な構造があります：

目次: これはコンテンツ作成の最後に生成され、チャプターに基づきます。
チャプターの導入: 各章の簡単な概要を紹介します。
メインコンテンツ: 詳細な説明とともに例、ダイアグラム、またはコードスニペットを含みます。
演習やクイズセクション: 学習を強化するための内容です。
これにより、プロンプトに基づいて教科書のさまざまなセクションを作成できます。例えば、「コントロールフローに関する初級者向けのPython演習を書いてください」や「変数に関するチャプターを要約してください」といったプロンプトが考えられます。

ステップ5: 仕上げと編集
ChatGPTは教科書の大部分を自動生成できますが、最終的な仕上げは手動で行う必要があります。これには以下が含まれます：

技術的な正確さを確保。
必要に応じて図やダイアグラムを追加。
読みやすさの向上。
上級テクニック：異なる受講者向けに教科書を微調整する
ChatGPTの強力な機能の1つに、受講者に応じてコンテンツをカスタマイズできる点があります。小学生から大学生まで、さまざまな教育レベルに合わせて内容を調整できます。たとえば、高校生向けのコンテンツを生成したい場合は、次のようにプロンプトを指定します：

python
Copy code
response = openai.Completion.create(
  model="text-davinci-003",
  prompt="Write a chapter on Python loops for high school students. Keep the explanations simple and include relatable examples.",
  temperature=0.7,
  max_tokens=1500
)
このようにプロンプトを調整することで、教科書の内容をターゲットとする受講者に最適化できます。

まとめ
ChatGPTを使って教科書や教育用ガイドを自動生成することで、教育者や開発者はコンテンツ作成の新しい革命を手に入れました。AIは、少ない入力で素晴らしい教育素材を生み出し、時間とリソースを節約できます。教育者は、AIが生成したコンテンツを微調整し、学生のニーズに合わせた高品質な学習教材を作成することができます。
ecsからrdsにrds proxyを挟まずに直接接続する際のssl/tls証明書について



通常はrds proxyを介してRDSに接続を行っていたが、今回はrds proxyを介さずに直接RDSに接続を行った。

その際に、
```
failed to initialize database, got error tls: failed to verify certificate: x509: certificate signed by unknown authority
```
が発生した。

これを解決するために以下公式ドキュメントを参照して証明書バンドルをインストールした。

https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL.html

自身のリージョンによる証明書のバンドルをダウンロードし、作業ディレクトリに配置する。

CA証明書を読み込み、カスタムTLS設定に登録を行う。

```Go
import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"log"

	"sample/domain"
	"sample/handler"

	mysqlDriver "github.com/go-sql-driver/mysql"
	"github.com/labstack/echo/v4"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

func main() {
	// DSN（Data Source Name）の作成
	dsn := "sample_user:sampleHogeHoge@tcp(XXXXX-instance-1.hogehogehoge.ap-northeast-1.rds.amazonaws.com:3306)/mydb?tls=custom&allowCleartextPasswords=true&parseTime=true&loc=Asia%2FTokyo"

	// CA証明書を読み込む
	rootCertPool := x509.NewCertPool()
	pem, err := ioutil.ReadFile("../ap-northeast-1-bundle.pem") // ダウンロードした証明書のパスに変更
	if err != nil {
		log.Fatal(err)
	}
	if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
		log.Fatal("Failed to append PEM.")
	}

	// カスタムTLS設定を登録
	err = mysqlDriver.RegisterTLSConfig("custom", &tls.Config{
		RootCAs: rootCertPool,
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(dsn)

	// データベースに接続
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic("データベースへの接続に失敗しました")
	}

	// Userテーブルの自動マイグレーション
	db.AutoMigrate(&domain.User{}, &domain.Article{})

	// Echo インスタンスの作成
	e := NewEchoRouter(db)

	// サーバーの起動
	e.Logger.Fatal(e.Start(":8080"))
}
```

その後にいつも通りにDB接続する処理を行うことで接続することができた。

DSNについては

tls=true

としていた設定を

tls=custom

と変更することで先ほど行った設定に則って接続を行ってくれる。

（tls=trueの設定の場合、デフォルトの認証局を使用する設定となる）
AWS IAMユーザMFAコード認証用のChrome拡張機能（kosekiメモNO3）

## AWS IAMユーザのMFA認証

モバイルでの認証より、ブラウザからMFAコードを取得する方がやりやすいので、
以下のchrome拡張機能をお勧めしています。

:::note info
*Authenticator*
:::
***
### chromeストアからDL
![スクリーンショット 2024-10-24 14.09.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526627/a771ce4e-15c0-a419-39f0-d696aa4bf1a0.png)
***
### authenticatorからAWS MFAのQRコードをスキャン
![スクリーンショット 2024-10-24 14.09.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526627/4ed901bd-42ea-0a10-69ff-df578df90fae.png)
***
以上