OmniAICreator/Qiita-1.07M huggingface dataset:
Wordpress meetup用 Wordpress自動バックアップ

## Wordpressを適切にバックアップして更新しよう

ということです。

あるトラブルが起きてから、このような方法でアップデートを行っています。

### 前提
- Wordpressのすべての自動更新機能を停止する
- wp コマンドが入ってる
- crontab が使用できる

### ソースコード
```wp_update.sh
#!/bin/bash
TAR="/bin/tar"
XZ="/bin/xz"
PHP="/usr/bin/php7.4"
WP="$PHP /usr/bin/wp"
MYSQLDUMP="/usr/bin/mysqldump"
DIR="/home/example.com/backup/wordpress-file"
export HTTP_HOST="あなたのドメイン名"
export HTTPS="on"

DBNAME=データベース名
USERNAME=データベースのA名
PASSWORD=データベースのパスワード
HOST=データベースのサーバー名

mkdir -p $DIR
FNAME="$DIR/Wordpress-`/bin/date +%Y%m%d`.tar"
SQLNAME="$DIR/Wordpress-`/bin/date +%Y%m%d`.sql"

cd /home/example.com/public_html

$TAR cvf $FNAME *.php .htac* wp-admin wp-includes wp-content/languages wp-content/plugins wp-content/themes >/dev/null 2>/dev/null
/bin/rm -rf $FNAME.xz
$XZ -z $FNAME

$MYSQLDUMP -u $USERNAME -p$PASSWORD -h $HOST --no-tablespaces $DBNAME > $SQLNAME
/bin/rm -rf $SQLNAME.xz
$XZ -z $SQLNAME


$WP core update
$WP plugin update --all
$WP core language update
```

## 基本（引っ越し元）
```
mysqldump -u username -ppassword -h host --no-tablespaces databasename > sqlfilename
```
```
tar cvf filename.tar *.php .htac* wp-admin wp-includes wp-content/languages wp-content/plugins wp-content/themes >/dev/null 2>/dev/null
```

## 基本（引っ越し先）
ファイルを転送してから
```
mysql -u username -ppassword -h host databasename < sqlfilename
```
```
cd public_html
(xzしてたら) xz -d filename.tar.xz
tar xvf filename.tar
```

その後、wp-config.php を必要に応じて編集する


## ドメインが変わる場合
```
wp search-replace 'http://before.example.com' 'https://after.example.com'
```
総合試験でよく活用されるテスト設計技法まとめ

### 概要

総合試験に触れる機会があるが経験の浅いというエンジニア向けに、「テスト技法」についてあらかじめ知っておくことで現在実施しているテストがどういった考えによってどういった目的で行われているかというのをより理解することにつながると思い、簡単にまとめようと思いました。
さまざまな技法についてざっとイメージを把握してくれたり、この技法についてもっと詳しく知りたいと思ってもらえたらありがたいです。

## ホワイトボックステスト・ブラックボックステストの違い
**・ホワイトボックステスト**
プログラムの内部に重点をおいたテスト
作り手の目線で内部の処理（命令文や条件分岐など）を把握した上で、手法や網羅率を検討する。
単体試験と呼ばれる、部品・機能ごとの確認に使われることが多いです。

**・ブラックボックステスト**
プログラムの外部に重点をおいたテスト
ユーザー側の視点でシステムに触れ、入力した内容に対して出力される結果を確認する。
UI（ユーザーインターフェイス）の確認なども含まれており、**結合試験のように全体の流れを確認する段階で使われることが多いです。
本稿で紹介する技法は全て「ブラックボックステスト」に分類される技法です。**

また、技法の目的ごとで3つほどに分類でき、それぞれ下記のように呼ばれています。
**・削減型**
　工数を考慮しつつテスト対象をなるべく少ないケースで実施できるように設計する。

**・標的型**
　経験をベースにして、なるべくたくさんのバグを発見できるようにピンポイントに向けて設計する。

**・網羅型**
　テスト対象の条件分岐・パターンを網羅するように設計する。

## 同値分割法
削除型
出力結果が同じになる入力値のグループを同値クラスといいます。
同値分割法は処理結果ごとに分けた同値クラスから、代表的な値をテストする技法です。

例：10〜19歳のみ適用できる割引があった場合
・無効な同値クラス：10歳未満
・有効な同値クラス：10歳以上、20歳未満
・無効な同値クラス：20歳以上

それぞれグループごとにランダムな値だけをテストすればよいので、その分下記のように効率よくケースを絞ることができます。
ケース1：6歳（割引が無効）
ケース2：18歳（割引が有効）
ケース3：87歳（割引が無効）

## 境界値分析
標的型
仕様条件の境界となる値とその隣の値に対してテストする技法です。
バクが混在してるであろう箇所をピンポイントでテストすることが目的となります。

有効な同値クラスの最小値と最大値が境界値となる為、上記の同値分割法で例に挙げた割引では[10歳]と[19歳]が境界値となります。
よって、「境界となる値とその隣の値」は[9歳]と[10歳]、[19歳]と[20歳]となります。

## デシジョンテーブルテスト
網羅型
デシジョンテーブル（決定表）をもとに各組み合わせのケースを導出する技法です。
※デシジョンテーブルとは条件（入力）に対しその結果の動作（出力）を洗い出し、表形式にまとめたものです。

組み合わせ数が多いときは、その分リソースが十分に必要となります。圧縮は可能ですが対象の中身・処理を知らないとリスクがあり、スキル・経験が必要です。

## 状態遷移テスト
網羅型
同じ条件（入力）を実行しても常に同じ結果になるとは限らず、状態によって結果に違いが生じることがあります。
システムの状態が条件やイベントによって変化する状態遷移に着目したテストです。
有効な状態遷移が行われること、無効な状態遷移が行われないことを主に観点とします。

## ユースケーステスト
網羅型
アクターとシステムのやり取りをまとめる事で、想定してないバグ、漏れが発見できます。
※アクターとは要件定義で設定されているユーザーや、システムを操作する管理者、対象システムを利用する外部システムなども含まれます。
いわゆるシナリオテストはユースケーステストより自由度の高いシナリオに基づき実行されます。

## ペア構成テスト
削除型
パラメータが取りうる全ての組み合わせを網羅しようとすると、ケース数が膨大になってしまうことがあります。
一部のテストパターンを合理的な制限でしぼる手法で、2因子間の組み合わせを網羅しつつケースを絞ることができます。
ペアワイズ法のケースを作成する支援ツールなどを使用することが効果的です。

## エラー推測
標的型
過去実際にバグが発生した事例から、ありがち、ありそうなバグを推測して実施することを言います。
しかし経験ベースのテストだけでは人的ミスになりかねません。
体系的な仕様・構築ベースのテストを設計した後、経験ベースの設計を組み込むのが望ましいです。

## 探索的テスト
標的型
事前にテストケースを作成せずに、経験ベースで実施する手法です。
ランダムに（適当に）検証を行う「モンキーテスト」と異なり、実施者の経験や知識への依存度が高いです。
網羅性が担保されにくいですが、準備などの時間が省かれスピードが速く、工数を削減できるなどのメリットがあります。

## リスクベースドテスト
標的型
洗い出したリスクに重点をおいて、テストの優先順位を定める手法です。
顧客への影響度が高い、金銭の取り扱いに大きく関わるなど、基準は決まったものはなく各プロジェクトによってさまざまです。

## 主に参考にしたサイト
ホワイトボックステスト・ブラックボックステストについて
https://service.shiftinc.jp/column/4801/
デシジョンテーブルテストなど
https://www.veriserve.co.jp/gihoz/decision-table.html
経験ベースのテストケースなど
https://webrage.jp/techblog/testing_by_experience/
【ワークアラウンド対応】ChatGPT のチャットルームが表示されないバグについて

## はじめに

**環境**

- Windows 11 home

**対象者**

- 新規のチャットルームではなく、既存のチャットルームを使いたい方


### このページの説明
ChatGPT のチャットルームが表示されないバグについての画面説明と、ワークアラウンド対応について記載してます。
あくまで私個人でのやり方なので、バグそのものの改善策ではありませんので、ご注意下さい。
バグ自体は、Open AI が改善してくれるまで待機が望ましいかな、と思っております。

## 手順

1.1　チャットルームが表示されない状態が下記の状態です。キャッシュクリアやログアウト→ログインしても解決しません。又、New Chat の下部に「History is temporarily unavailable. We're working to restore this feature as soon as possible.」と表示され、履歴機能が現在利用できないことを示しています。このメッセージは、機能が復旧するまで利用できないことを示唆しており、復旧すると再び利用可能になる可能性があります。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3282990/b126954d-99f2-da05-6430-189caf7aca4b.png)

1.2　ですが、ChatGPT 自体は問題なく使えます。テストで「こんにちは」と入力し、送信してみます。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3282990/fd2e5781-2179-5ee3-f2a0-cdafdc60e987.png)

1.3　問題なくやり取り出来ているのが確認出来ましたが、チャットルームは相変わらず表示されない状態です。
ただ、このバグが解消するとバグの間に作成したチャットルームもまとめて表示されるようになります。
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3282990/574230d9-90ba-0399-85af-e0826250c0ab.png)

1.4　新規のチャットルームではなく、既存のルームを使いたい場合があるかと思います。
下記はあくまで一例ですが、私は Notion というサービスで以下のように database を作成し、名前と用途と URL を記載し管理しています。一時的なワークアラウンドですが、こうする事で対象の URL をクリックすればそのルームが使えるようになります。
![5 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3282990/dac39cd3-749b-e771-7d7b-d42c34eb13de.png)



他にも良い方法があれば、是非教えて下さい。
以上！終わり！
GitHub Actions での DB参照エラー could not translate host name "postgres" to address: Temporary failure in name resolution

:::note
Rails を例にしていますが、GitHub Actions の PostgreSQL サービスコンテナを参照するときに共通の事項です。
:::

# 起きたこと

GitHub Actions で Rails の RSpec を実行したところ、db:create （DB を参照するところ）でエラーが発生しました。
どうやら、services に定義したサービスコンテナにアクセスできていないようだ。

```bash: エラーログ
Run bundle exec rails db:create RAILS_ENV=test
could not translate host name "postgres" to address: Temporary failure in name resolution
Couldn't create '<DB名>' database. Please check your configuration.
```

# 原因

GitHub Actions 実行時、DB ホスト名が `localhost` になっていませんでした。
公式ドキュメントにもサービスの DB への接続方法が記載されていました。

https://docs.github.com/en/actions/using-containerized-services/creating-postgresql-service-containers#running-jobs-directly-on-the-runner-machine

> ランナーマシンで直接ジョブを実行する場合、サービスコンテナのポートをDockerホストのポートにマッピングする必要があります。**Dockerホストからサービスコンテナにアクセスするには、localhostとDockerホストのポート番号を使用します。**


# 解決方法

直接変更できるなら DB ホスト名を `localhost` にしてください。
私の場合、ローカル環境と GitHub Actions とで、テスト実行時の DB ホスト情報が異なるため、CI 用の定義ファイルを追加したうえで、DB 参照前に定義ファイルを更新して対応しました。

```diff_yaml:GitHub Actions 具体例
name: Test RSpec
on:
  push:
jobs:
  rspec:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres
        env:
          POSTGRES_USER: your_user
          POSTGRES_PASSWORD: your_password
          POSTGRES_DB: your_db
        ports:
          - 5432:5432
    steps:
      ...
      - name: Database create and migrate
        run: |
+         cp config/database.ci.yml config/database.yml
          bundle exec rails db:create RAILS_ENV=test
          bundle exec rails db:migrate RAILS_ENV=test
      ...
```

# 参考

https://qiita.com/ktoyod/items/de97f25af2d4089eece9

https://docs.github.com/en/actions/using-containerized-services/creating-postgresql-service-containers
arch linuxで、簡単にssh

arch linuxで、sshをインストールし、パスワードログインに設定します。

```
# インストール・起動時に有効化
sudo pacman -S openssh
sudo systemctl enable sshd

# パスワードログインのための設定
sudo vim /etc/ssh/sshd_config
PasswordAuthentication yes #コメントを外す

# sshdをスタートさせる
sudo systemctl start sshd

# テスト
ssh user@127.0.0.1
```
REST APIについて簡単にまとめた

## REST APIとは
- `REST`のルールで作った`API`のこと。
    - `REST`
        - URLと[HTTPメソッド](https://qiita.com/RammaruIshii/items/57f0469fe18f41a33ab8)でリソースへアクセスする考え方（シンプルなWEBシステムの設計思想）。
        - `RE`presentational `S`tate `T`ransfer = 具体的に状態を定義した情報のやり取り
        - 「RESTの４原則」というものがあり、これを満たすものが「RESTfulなシステム」と呼んだりする。「RESTの４原則」は別途後述する。
        
    - `API`
        - システムとシステムを繋げる機能（プログラムの情報をやり取りする窓口のようなもの）
        
-  REST APIはRESTful APIを省略した言い方。
    -  RESTful APIだったりWEB APIともしばしば呼ばれる。
![スクリーンショット 2023-03-23 18.17.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2915639/97174182-002e-e05e-a56c-084bf0de10aa.png)


## RESTの４原則とREST　APIのまとめ
「RESTの４原則」は字の如く４つの原則からなっている。
- `統一インターフェース`
    - あらかじめ定義・共有された方法でやり取りされること。
        - 例えば、「HTTPメソッドでやり取りする〜」・「やり取りする形はJSON形式、HTML形式にしようね〜」みたいな感じ。
    - インターフェースとは？（おまけ）
        - ざっくり言うと、`何かと何かがくっつく部分`のことを指す。[こちらの記事](https://wa3.i-3-i.info/word16512.html)が鬼分かりやすい。
![スクリーンショット 2023-03-23 19.32.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2915639/a6c31994-023a-4186-4f95-5a3ea5d28079.png)
        
- `アドレス可能性`
    - 全ての情報が一意なURLを持っていて、提供する情報をURLで表現できること。
![スクリーンショット 2023-03-23 19.32.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2915639/b570e323-694c-cad5-a926-12882ba234ba.png)
- `接続性`
    - やり取りされる情報はハイパーリンクを含めることができる。
        - 1つのリンクから別の情報にリンク（接続）することができる。また、RESTfulなシステム同士なら円滑に情報連携を行うことができる。
![スクリーンショット 2023-03-23 19.33.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2915639/9a1fb3df-fd7c-a0f8-d16d-9690f75f62d5.png)

- `ステートレス性`
    - 状態（ステート）がないということ。
        - RESTでは`やりとりが一回ごとに完結する`
        - 前のやり取りの結果に影響を受けないシンプルな設計にできるというメリットがある。
        ![スクリーンショット 2023-03-23 19.49.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2915639/465c01e9-0e89-2d7b-d6c5-6823332fe73c.png) 

- `REST　APIのまとめ　`
    - RESTの４原則に沿ったAPIのこと。
    ![スクリーンショット 2023-03-23 18.17.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2915639/97174182-002e-e05e-a56c-084bf0de10aa.png)
中央のポータルサイトがGoogleのようなサイトでユーザーは住んでいる地域を登録している→この地域情報を敵予報のAPIサーバーに渡して、その地域の天気の情報を検索してポータルサイトに返す。
（スマホアプリからも同様、使用しているブラウザなどから登録情報を活用してAPIサーバーを叩く）


## おわりに
間違い等ございましたらコメント欄にてご指摘ください。

## 参考記事
- [REST APIとは？ざっくりと理解してみる【初心者向け】](https://tech.012grp.co.jp/entry/rest_api_basics)

- [【簡単に】REST APIとは？初心者もわかりやすく解説](https://tech-parrot.com/programming/describe-about-restful-api/#REST_APIAPI)

- [「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典:インターフェース](https://wa3.i-3-i.info/word16512.html)
PostgreSQL: シーケンスの使い方

次のページを参考にしました。
[【PostgreSQL】シーケンス（serial）を作成する、値を設定する](https://postgresweb.com/post-5455)

次のデータベースを使います。
>ユーザー　scott
>パスワード　tiger123
>データベース　db_test

作成方法

```text
postgres=# CREATE ROLE scott login password 'tiger123';
CREATE ROLE
postgres=# create database db_test;
CREATE DATABASE
postgres=# alter database db_test owner to scott;
ALTER DATABASE
```

開始時の状態

```text
$ psql -U scott db_test
psql (15.2)
"help"でヘルプを表示します。

db_test=> \dt
リレーションが見つかりませんでした。
db_test=> \d
リレーションが見つかりませんでした。
```

テーブルの作成

```text
db_test=> CREATE TABLE literature
(
        seq serial NOT NULL,  -- serial列
        title character(10),
        CONSTRAINT pk_llx PRIMARY KEY (seq)
);
CREATE TABLE
```

この時の状態

```text
db_test=> \dt
             リレーション一覧
 スキーマ |    名前    |  タイプ  | 所有者 
----------+------------+----------+--------
 public   | literature | テーブル | scott
(1 行)

db_test=> \d
                  リレーション一覧
 スキーマ |        名前        |   タイプ   | 所有者 
----------+--------------------+------------+--------
 public   | literature         | テーブル   | scott
 public   | literature_seq_seq | シーケンス | scott
(2 行)
```

データの挿入

```text
db_test=> insert into literature(title) values('坊っちゃん');
insert into literature(title) values('草枕');
insert into literature(title) values('それから');
insert into literature(title) values('三四郎');
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
```

この時の状態

```text
db_test=> select * from literature;
 seq |      title      
-----+-----------------
   1 | 坊っちゃん     
   2 | 草枕        
   3 | それから      
   4 | 三四郎       
(4 行)

db_test=> select * from literature_seq_seq;
 last_value | log_cnt | is_called 
------------+---------+-----------
          4 |      29 | t
(1 行)
```

シーケンスの値の取得

```text
db_test=> select sequencename,last_value from pg_sequences;
    sequencename    | last_value 
--------------------+------------
 literature_seq_seq |          4
(1 行)
```

シーケンスを限定してシーケンスの値の取得

```text
db_test=> select sequencename,last_value from pg_sequences where sequencename = 'literature_seq_seq';
    sequencename    | last_value 
--------------------+------------
 literature_seq_seq |          4
(1 行)
```

シーケンスの値の変更

```text
db_test=> SELECT setval('literature_seq_seq',10);
 setval 
--------
     10
(1 行)
```

シーケンスの値

```text
db_test=> select sequencename,last_value from pg_sequences;
    sequencename    | last_value 
--------------------+------------
 literature_seq_seq |         10
(1 行)
```

データの挿入

```text
db_test=> insert into literature(title) values('こころ');
insert into literature(title) values('明暗');
INSERT 0 1
INSERT 0 1
```

この時の状態

```text
db_test=> select * from literature;
 seq |      title      
-----+-----------------
   1 | 坊っちゃん     
   2 | 草枕        
   3 | それから      
   4 | 三四郎       
  11 | こころ       
  12 | 明暗        
(6 行)
```

シーケンスの値

```text
db_test=> select sequencename,last_value from pg_sequences;
    sequencename    | last_value 
--------------------+------------
 literature_seq_seq |         12
(1 行)
```

## 注意

PostgreSQL 9.2.24 では、pg_sequences は使えません。
代わりに

```text
db_test=> select last_value from literature_seq_seq;
 last_value 
------------
          4
(1 行)
```
SQLFuffでdbtプロジェクトのSQLを整形する方法[.sqlfluff]

# はじめに

sqlfluffでdbtプロジェクトのSQLファイルを整形する方法を記載します。
dbt公式のコーディング規約である`dbt Style Guide`に合わせて設定をカスタマイズする方法についても触れます。
（予約語を小文字にするなど）

CUIだけで実行可能ですが、`Visual Studio Code`を使っているのでそちらでの使い方も載せます。

# SQLFluffとは

> SQLFLuff とは Fishtown Analytics 社が作成した SQL Lint ツールで、多人数での SQL メンテナンスを実行してく際にとても役立つツールの一つです。
[「ソフトウェア開発者のための OSS、まとめてみました！」より引用](https://kewpie13.hatenablog.com/entry/2022/10/11/082837#:~:text=SQLFLuff%20%E3%81%A8%E3%81%AF%20Fishtown%20Analytics,%E3%83%84%E3%83%BC%E3%83%AB%E3%81%AE%E4%B8%80%E3%81%A4%E3%81%A7%E3%81%99%E3%80%82)

# インストール

pipenvを使用している前提で記載します。

## SQLFuffのパッケージをインストールする
```
pipenv install sqlfluff
pipenv install sqlfluff-templater-dbt
```

`Visual Studio Code`を使用している場合の場合、SQLFuffの便利な拡張機能があるのでお好みでご活用ください
https://marketplace.visualstudio.com/items?itemName=dorzey.vscode-sqlfluff

# .sqlfluffの作成


SQLFluffの設定を行うには`.sqlfluff`ファイルを作成する必要があります。
`.sqlfluff`ファイルはプロジェクトルートに配置します。

# .sqlfluffの例

公式ドキュメントは[こちら](https://docs.sqlfluff.com/en/stable/configuration.html)

現状、私の環境では以下のようにしています。ご参考までにどうぞ。

```conf:.sqlfluff
[sqlfluff]
dialect = bigquery
templater = dbt

[sqlfluff:templater:dbt]
project_dir = ./　 # 相対パスでプロジェクトのルートを記載します
profiles_dir = ~/.dbt/ # dbtのprofiles.ymlが格納されているパスを記載します。（私の場合dbt power userという拡張を使っており、profiles.ymlが`~/.dbt/`にあるのでこのように記載しています）
profile = default # profiles.ymlの<profile-name>の部分（下記の`profiles.yml`のドキュメントを参考）
target = dev # profiles.ymlの<target-name>の部分（下記の`profiles.yml`のドキュメントを参考）

[sqlfluff:templater:jinja]
apply_dbt_builtins = False

[sqlfluff:rules:capitalisation.keywords]
capitalisation_policy = lower

[sqlfluff:rules:capitalisation.functions]
extended_capitalisation_policy = lower

[sqlfluff:rules:convention.select_trailing_comma]
select_clause_trailing_comma = require
```

[profiles.ymlのドキュメント](https://docs.getdbt.com/reference/profiles.yml)

# .sqlfluffの解説

dbtに対応するための設定と、一部`dbt Style Guide`に合わせるための設定を記述しています。
[dbt Style Guide](https://github.com/dbt-labs/corp/blob/main/dbt_style_guide.md)

## apply_dbt_builtins
```
[sqlfluff:templater:jinja]
apply_dbt_builtins = False
```
この部分はdbt jinjaの組み込み関数（ref)などの機能に対応するかどうかというフラグになっています。
(まだSQLFuffを導入したばかりなので、一旦Falseにして様子を見てます)

## 予約語と関数の小文字固定

`dbt Style Guide`に合わせて予約語は全て小文字にします。
デフォルトだとsqlファイル内の最初の予約語のケースに合わせる設定（consistent: 一貫性のある）になっているので変更しました。
```
[sqlfluff:rules:capitalisation.keywords]
capitalisation_policy = lower

[sqlfluff:rules:capitalisation.functions]
extended_capitalisation_policy = lower
```

## trailing commna (後ろのカンマ）の有効化


`dbt Style Guide`に合わせてtrailing commaを有効にします。

`trailing commma`とは、select文などで値を列挙した後の最後のカンマです

```sql:trailing commnaの例
select
    a,
    b,
    c,←これ
from test
```

```
[sqlfluff:rules:convention.select_trailing_comma]
select_clause_trailing_comma = require
```

## インデントをスペース４つ(four spaces)に

これはsqlfluffのデフォルトなのであえて記載追加はしませんでした。


# SQLFuffでSQLファイルを整形する

vscodeの場合、以下の操作で実行できます。
右クリック→`Format Document`
![スクリーンショット 2023-03-23 19.42.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/268801/23cecc40-9bfe-a1be-fcf2-af67df5aad5b.png)

コマンドでやる場合は以下です。

```
sqlfluff fix /path/to/sql.sql
```

プロジェクト内のすべてのsqlを整形したい場合は
```
sqlfluff fix
```
でいけます。

# おわりに
以上、参考になりましたら幸いです。
MSAL(Microsoft Authentication Library)について


# MSAL(Microsoft Authentication Library)とは
2019年に公開され(?)、Microsoft Graph API認証用のライブラリです。
ASDL(Azure Active Directory 認証ライブラリ)のサポートは 2023 年 6 月に終了するため、
今からGraphAPIの開発はMSALの使用をお勧めします。

# 認証種類
・トークン
・Authorization code (承認コード)
・クライアントの資格情報（クライアントシークレットOR 証明書）
・デバイス コード
・Implicit grant (暗黙的な付与)
・On-behalf-of (OBO)
・ユーザー名/パスワード (ROPC)→推奨ではない
・統合 Windows 認証 (IWA)

ネットで調べた結果、クライアントシークレットの認証がよく使われている。
ユーザー名/パスワードの認証は、推奨されていない。

# 使い方
①Azure Active Directoryアプリ登録
https://learn.microsoft.com/ja-jp/azure/active-directory/develop/quickstart-register-app
②GraphAPIのアクセス許可を追加、テナント管理者を同意する
https://learn.microsoft.com/ja-jp/graph/security-authorization
③認証方式を確認する（クライアントシークレット　ORユーザー名/パスワード）
④MSAL.NET を使用してクライアント アプリケーションを初期化する
https://learn.microsoft.com/ja-jp/azure/active-directory/develop/msal-net-initializing-client-applications
https://qiita.com/songoku/items/7d35f55e7d574d8ca112
⑤クライアントシークレットで認証して、GraphAPIを呼び出す
```
// user.read,file.read なども可能
var scopes = new string[] { "https://graph.microsoft.com/.default" };
var tenantId = テナントID;

// Configure the MSAL client as a confidential client
var confidentialClient = ConfidentialClientApplicationBuilder
                .Create(クライアントID)
                .WithAuthority($"https://login.microsoftonline.com/{テナントID}/v2.0")
                .WithClientSecret(クライアントシークレット)
                .Build();

// Retrieve an access token for Microsoft Graph 
var authResult = await confidentialClient
         .AcquireTokenForClient(scopes)
         .ExecuteAsync();
 //認証
var authProvider = new DelegateAuthenticationProvider(request =>
{
    request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
    return Task.CompletedTask;
});

var graphClient = new GraphServiceClient(authProvider);
```
# 委任されたアクセス許可とアプリケーションのアクセス許可
Azure Active DirectoryでGraph APIを選択するとき、下記の選択肢が出てきます。

・「委任されたアクセス許可」: 指定したユーザーの権限でAPIを使用する(ユーザで操作する)

・「アプリケーションのアクセス許可」 :AzureADアプリに設定した権限でAPIを使用する(ユーザなし、アプリケーションで操作する)

# 注意点
① ユーザ/パスワードで認証する場合、ユーザーのMFA（二重認証)を解除する必要がある











# 参考サイト
https://learn.microsoft.com/ja-jp/azure/active-directory/develop/msal-overview

https://learn.microsoft.com/ja-jp/azure/active-directory/azuread-dev/active-directory-authentication-libraries

https://learn.microsoft.com/ja-jp/azure/active-directory/develop/msal-authentication-flows

https://learn.microsoft.com/ja-jp/azure/active-directory/develop/microsoft-identity-web

https://stackoverflow.com/questions/75257771/how-to-create-microsoft-graphserviceclient-in-asp-net-core-web-api-6
# Web3 Event 2023

Let’s Start
--
# 【WEB2.0→WEB3.0-What’s the newest tech trend for U?-】
**イベント概要は開催主のHPをお借りします**
→ [HPリンク](https://connpass.com/event/275592/)

Astar Networkの創設者である渡辺 創太さんとまたお会いできました。
新たな会社Startale labsの創設事由やこれから目指す方向についてお話を聞きました。
去年から日本はWeb3に注力していくよというニュースがありましたが、Web3関連の法整備はまだできていないので、Web3界隈に進軍したいが、法整備を待っている大手企業は結構多いと思います。
なんか整い次第、一気に爆発するような雰囲気がしますね。

また、食品メーカーに入社し、ToB営業、商品のPMに従事していた小澤さん（AstarGames 代表取締役）のお話を聞いてびっくりしました。
ITと全く関係ない業界からWeb3界隈の最先端までチャレンジしている方もいるなと考えて、自分もより一層努力しないといけないなと思いました。

あと、Web3界隈の最先端で活躍しているエンジニアの話を聞いて、Web2からWeb3になるまでの考え方、システム設計、プロトコール設計がかなり難しかったみたいです。
Web3の思考と衝突している伝統金融システムなど、これからどうやってWeb2とWeb3のハイブリッド型に変換するのが課題だと感じましたね。

-- 
イベント後のネットワーキング（懇親会）で色んな方とお話ができてよかったです。

Startale labsコアチームメンバーの３名とお話しして、コード＊ドキュメント類がめんどくさいという共感を得て、重要ではないドキュメントはいつ省略できるだろう。。。。
今は昔と違い、構造体やDB設計は全部コードベースになって、コード自体が定義書みたいなものです。
コード読めない方の立場から考えると、読んでもわからないですよね。

コードを書いて、ChatGPTで定義書を生成するとかできないですかね。
↑ 時間がある、ちょっと検証してみたいと思います。

--
NFCカードに配布用URLを埋め込んて、イベント会場などでNFTを配布するアイデアを用いて起業している方もいらっしゃて、デモンストレーションをみながら議論しました。
会場内の配布用QRコードがSNSに無断転載することを防げるし、配布用URLが読み込む度、変化されるため、NFCカードを読み込んで、配布用URL取得してからの無断転載も阻止できます。
課題があって、解決案もあって、かなり実用的ですね。
NFC＊NFTをコラボして、色んなアイデアが出てきそうです。

--
来月シンガポールに行って、会社を作る人も......Web3エンジニアの派遣会社社長さんも......色々お話しできました。

以上、現場からでした。
日本初（発）のブロックチェーン（Astar Network）に貢献したいな....と強く思いました。


# 【αU market × SoudanNFT】リアル飲食店とKDDIとのコラボ
<div align="center">
<img src="https://prcdn.freetls.fastly.net/release_image/106563/22/106563-22-5259210674026a9f76b8961b947e373a-1400x350.png" width=50%>
</div>

**イベント概要は開催主のHPをお借りします**
→ [HPリンク](https://market.alpha-u.io/news/1)
→ [SoudanNFT Twitter](https://twitter.com/SoudanNFT)

--
仮想筋肉（CryptoMuscle/NFT界の執事）さんとお会いできました。
ちょっとしかお話ができず残念です。
NFTコミュニティを推進して、かなり大変そうです。が、楽しんでいる様子です。
また、次回ゆっくり会話したいなと思います。
→ [仮想筋肉さんのTwitter](https://twitter.com/crypto_muscle_)


--
通花ちゃんというアイドル系NFTの発行、コミュニティの拡大をめざしているフランス人の方とお話しして、フランス当地のNFT文化と日本国内のNFT文化が全然違うことにびっくりしました。

フランス系NFTは油絵や美術品みないものが多いらしいです。
確かに日系NFTはアニメキャラクターがメインという印象ですね。

また、海外のNFTコレクターはまだ投機目的で、ほんとにそのものが好きで購入される方はまだ少ないことについて議論しました。
NFT愛というより推し愛が本質かなと感じました！

通花ちゃんは結構可愛いので、Twitterもぜひ見てみてください。
→ [通花ちゃん Twitter](https://twitter.com/vivien_cryptofr)

追記：通花＝通貨...ネーミングセンスがいいですね。

--
また、NFTや仮想通貨などが全然わからなかった方もいらっしゃて、NFTコミュニティを体験するために参加したらしいです。
完全初心者目線から色んな課題をわかってきました。
まず、Not your keys not your coins（鍵を持たぬ者は、コインを持たず)）という概念が全然知らなかったですね。

今回KDDIさんがご提供しているサービスは暗号鍵やキーワードの設定などを略して簡単にNFTの購入や所有を体験することができます。
かなり便利だと思います。

ただし、Not your keys not your coinsという概念から、提供しているサービスは完全中央集権型で、NFTの無効化などは全部運営側がコントロール可能になるということです。
仮想通貨交換業者、いわゆる取引所に自分が所有している通貨・コインを預け入れたまま放置する行為と同様だと思います。

Web2的な中央集権型思考からWeb3的な非中央集権型思考（P2P型思考）に転換するまであと何年ぐらい必要でしょうか。
確かにシン・Web3になると、詐欺などなにがあった場合、法律が力になれず、全部自己責任になりますが、その代わりに、自分のものに対して完全なコントロール権を握れます。

これについて正解が誰もわからないので、自分なりの正を持ちながら前に進めことが大事だと思います。

--
あと、コラボしているカフェ（Roasted COFFEE LABORATORY 渋谷神南店）のハンバーガーはめっちゃおいしかったです！
また食べに行きたいと思います。

# 最後に
コロナが少しずつ落ち着いたので、これから色んなオフラインイベントが復活して開催されるかもしれません。
積極的に色んなイベントを参加してみて、また有用な情報をまとめて発信していきたいなと思います。
気楽に～
TerraformでAzure Virtual Machineのブートストラップする方法

# はじめに
TerraformでAzure VMのブートストラップする方法を備忘録として残します。
自分が確認した限りでは3通り方法があったのでそのやり方について記載します。

# VMのUser Dataで設定する

`azurerm_linux_virtual_machine`の`user_data`に設定する。

```tf:VirtualMachine.tf
resource "azurerm_linux_virtual_machine" "test" {
  name                = "test-machine"
  resource_group_name = azurerm_resource_group.test.name
  location            = azurerm_resource_group.test.location
  size                = "Standard_B1ls"

  # SSHキー設定省略

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  ## Ubuntu 22.04
  source_image_reference {
    publisher = "Canonical"
    offer     = "0001-com-ubuntu-server-jammy"
    sku       = "22_04-lts-gen2"
    version   = "latest"
  }
  # ここで設定する
  # file関数で読み込み、base64でエンコードする必要がある
  user_data = base64encode(file("init.sh"))
}
```

```bash:init.sh
#! /bin/bash
sudo apt-get update
sudo apt-get install -y nginx
```
:::note info
Terraform内で変数宣言して格納することも可能です。
:::

https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/linux_virtual_machine

# VMのremote-exec Provisionerを設定する

```tf:VirtualMachine.tf
resource "azurerm_linux_virtual_machine" "test" {
  name                = "test-machine"
  resource_group_name = azurerm_resource_group.test.name
  location            = azurerm_resource_group.test.location
  size                = "Standard_B1ls"

  # SSHキー設定省略

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  ## Ubuntu 22.04
  source_image_reference {
    publisher = "Canonical"
    offer     = "0001-com-ubuntu-server-jammy"
    sku       = "22_04-lts-gen2"
    version   = "latest"
  }

  # remote-execの接続設定
  connection {
    type     = "ssh"
    user     = "root"
    password = var.root_password
    host     = self.public_ip
  }

  # ここで設定する
  provisioner "remote-exec" {
    inline = [
      "apt update",
      "apt install -y nginx",
    ]
  }
}
```


# カスタムスクリプトを設定する

`azurerm_virtual_machine_extension`で設定する。

```tf:
resource "azurerm_virtual_machine_extension" "test" {
  name                 = "test-vm-nginx"
  virtual_machine_id   = azurerm_linux_virtual_machine.test.id
  publisher            = "Microsoft.Azure.Extensions"
  type                 = "CustomScript"
  type_handler_version = "2.0"

  settings = <<INIT
  {
    "commandToExecute": "apt-get update && apt-get install -y nginx"
  }
INIT
}
```

https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/virtual_machine_extension

以上です。

# 最後に
誰かの参考になれば嬉しいです！
webブラウザからサーバのファイルを読み込む方法をchatGPTに学ぶ

chatGPTが作ったプログラムは信用できないので、以下の手順で必要な関数を得る。
- まず動くプログラムを作ってもらいテストする。
- OKだったら動くプログラムから必要な部分だけを関数にしてもらう。
- その関数を使って、さらにテストプログラムを作ってもらいチェックする。

# まず具体的なプログラムをchatGPT4に作ってもらう
私「bodyにボタン、テキスト表示エリア、テキスト入力エリアを配置する。クリックすると、入力エリアのテキストをファイル名として使用して、テキストエリアにファイルを読み込むようにしたい。サーバーのファイルを読み込むプログラムを書いて。」

chatGPT4はフレームワークなどを使用したプログラムの例を教えてくれたが、シンプルな構成がよかったので付け加えた。

私「apacheとjavascriptのみを使って」

これで期待していた、シンプルな動く例を得られた。
テストしたら、ちゃんと動いた。

ApacheサーバーとJavaScriptを使用して、クライアントからサーバーのファイルを読み込む例を作成する。

私「例題としてすごく参考になった。Javascriptの関数を書いてほしい。必要とする部分はファイル名を指定して、テキストファイルを返す関数が欲しい。」

望み通りの関数が得られた。

chatGPT4が作ってくれた、指定されたファイル名に基づいてテキストファイルの内容を取得し返すJavaScript関数
```javascript
async function fetchFileContent(filename) {
    try {
        const response = await fetch(filename);
        if (response.ok) {
            const fileContent = await response.text();
            return fileContent;
        } else {
            throw new Error('ファイルの読み込みに失敗しました。');
        }
    } catch (error) {
        console.error(error);
        throw error;
    }
}

```

## 使用例も書いてもらった
ファイル名を入力してボタンをクリックすると、指定されたファイル名のテキストファイルの内容を表示する

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>File Reader</title>
</head>
<body>
    <input type="text" id="filenameInput" placeholder="ファイル名を入力">
    <button id="loadBtn">ファイルを読み込む</button>
    <div id="fileContent" style="border: 1px solid black; padding: 10px;"></div>
    <script>
        async function fetchFileContent(filename) {
            try {
                const response = await fetch(filename);
                if (response.ok) {
                    const fileContent = await response.text();
                    return fileContent;
                } else {
                    throw new Error('ファイルの読み込みに失敗しました。');
                }
            } catch (error) {
                console.error(error);
                throw error;
            }
        }

        document.getElementById('loadBtn').addEventListener('click', async function() {
            const filename = document.getElementById('filenameInput').value;
            if (!filename) {
                alert('ファイル名を入力してください。');
                return;
            }

            try {
                const fileContent = await fetchFileContent(filename);
                document.getElementById('fileContent').innerText = fileContent;
            } catch (error) {
                console.error(error);
                alert('ファイルの読み込みに失敗しました。');
            }
        });
    </script>
</body>
</html>
```

## chatGPT4が教えてくれた注意事項
この方法ではセキュリティ上のリスクがある。
認証や権限の設定を行わない場合、誰でもサーバー上の任意のファイルにアクセスできる可能性がある。
実際のアプリケーションでこの方法を使用する場合は、適切な認証と権限の設定を行う必要がある。

## 初心者用の注意事項
Apacheが必要。
ローカルでは動かない。
Dockerとは？【初心者OK！Docker入門＋応用シリーズ】

# はじめに
本記事は **[「初心者OK！Docker入門＋応用：ゼロからでも実務で使えるスキルが身に付ける」](https://qiita.com/kenny_J_7/items/5fa59d4238058c1060e8)** のシリーズ記事の１つです。
「初心者OK！Docker入門＋応用：ゼロからでも実務で使えるスキルが身に付ける」のシリーズについては、以下の元記事をご覧ください。

https://qiita.com/kenny_J_7/items/5fa59d4238058c1060e8

# Docker とは？
Dockerwを小難しい言葉で説明すると、

**「コンテナ仮想化技術を使って、アプリケーションを開発・配置・実行するための隔離された環境を提供するツール」**

ということになります。

では、 **「コンテナ」** とはなんでしょうか？

# コンテナとは？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181439/c46ef920-ec14-c821-50df-d406e6ddaee8.png)

コンテナは、貨物船で荷物を運ぶときに使われる、硬い箱のこと。
この箱は非常に頑丈な密閉された箱になっています。

この船で使われるコンテナのように、

ソフトウェア開発での「コンテナ」とは、

**パソコンやサーバなどのマシン上で、独立して互いに隔離され、干渉されない環境**

といった性質を持っています。

# コンテナに何を入れるのか？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181439/6572cf6f-0952-ada5-e3eb-d33b8f961a55.png)
じゃあ、ソフトウェア開発でのコンテナには何を入れるのか？というと、なんでも入れられます。

OSには、Ubuntu Linuxを
データベースには、MySQLを
データ分析のために、Pythonを
etc...

好きなように自分の必要なソフトウェアをコンテナに入れることが可能です。
言い換えると、コンテナの中に小さなパソコンが1台入っているような感覚です。

# コンテナにはホストOSが必要
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181439/53016383-cd4c-815e-4850-8f08182b187b.png)
ただ、コンテナはそれだけで動くことはできません。

どこかのOSが動いているホストマシンが必要です。

このホストマシンで動くOSのことを、ホストOSと呼んだりします。

通常は、Dockerを動かすパソコンのOSがホストOSに該当します。
（Ex. Mac PCを使っているなら、ホストOS ＝ macOS）


# Docker とコンテナの関係
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181439/f96c7939-4589-5777-2476-fda0031dc0fd.png)
上図のように、Dockerはコンテナを作ったり、ホストOSと仲介したりします。
たとえば、コンテナ内で行ったなんらかの処理の結果を、Dockerが動いているPC上のファイルに保存したい場合には、Dockerを通じて、ホストOSにファイル書き込みの要求が行われます。
他にもコンテナの管理は全て、Docker によって行われます。


# コンテナのメリット
では、コンテナがあると何が嬉しいのでしょうか？

## ホストマシンの環境に依存しない
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181439/7d29bd96-088e-c42f-b5a3-99a9cbad118f.png)
たとえば、MySQLとPythonをインストールする場合の例で考えてみましょう。

コンテナがない環境である、上図の左側では、ホストマシン（ホストOSが動くPC）に直接これらのソフトウェアをインストールすることになります。
この場合、すでに存在する他のソフトウェアと干渉する可能性がありえます。
これによって、
**「AさんのPCだと上手く環境構築ができるのに、自分のPCだとインストールで失敗する」**
みたいなことが起こり得るということです。

一方、コンテナのある環境では、コンテナの内部はホストマシンから完全に隔離されているため、ホストマシンの状態には依存せず、干渉しません。
よって、　**バージョンの競合やホストマシンの環境による不具合といった問題が起きません。**
これは大きなメリットです。

## ホストOSに依存しない
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181439/ccaa4085-2694-9d98-7062-bb3ef4a26839.png)
さらに、コンテナの中では隔離されていて、それが動くホストOSには依存しません。
なので、上図のようにコンテナを起動するホストOSが macOS でも Windows でもコンテナの中での挙動はほぼ同じことが保証されます。
（厳密には多少の違いはあるのですが、ほとんどのケースでは無視できるレベルかと思います）

## ホストマシンに複数のコンテナも配置可能
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181439/2f98475a-abd8-8720-40b4-d23616e80566.png)
コンテナは１つのホストマシンの上に複数のコンテナを配置可能です。
たとえば上図のように、同じWindowsマシン上に、Ubuntu Linux環境のコンテナと、CentOS Linux環境のコンテナという全く異なる環境のコンテナを同時に動かすことも可能です。
これによって、気軽に隔離されたさまざまな環境をお使いのPC上に複数立ち上げることができるというわけです。

そして、当然これらのコンテナは隔離されていますので、互いに干渉することはありません。

# Next
https://qiita.com/kenny_J_7/items/ef4a5eb412ff943410e3

# Docker講義動画を Udemy で配信中（クーポンあり）
今回の内容を含むUdemyの動画コースを配信しています。

動画の方が理解しやすい！という方も多いと思いますので、ぜひ興味のある方はご覧ください。
動画版の方が丁寧に説明しているのと、**動画版にしかないコンテンツも多くあります。**
たとえば、動画版ではDockerの基礎部分だけでなく実際の業務に即したアプリ開発を体験するセクションも用意してあります。


以下リンクからだと割引で買えます。

https://dockercourse.page.link/coupon-qiita
Vue.jsのv-bindとv-model

# v-bind
**Vue.jsのディレクティブの一つ**
HTMLの属性にVue.jsのデータをバインドすることができ、JavaScriptの式を用いて動的にHTMLの属性を変更することが出来ます。
v-bindの省略形としてコロン(:)を使うことも可能です。

#### サンプルコード
```html
<template>
  <div>
    <h2 v-bind:title="titleText">Hello, World!</h2>
  </div>
</template>

<script>
export default {
  data() {
    return {
      titleText: 'タイトルです！'
    }
  }
}

</script>

```
`v-bind:title`を使用して、`<h2>要素`の`title属性`をVue.jsのデータ `titleText` にバインド
`titleText`の値が変更されると、`<h2>要素`の`title属性`も自動的に変更される

**参考記事**

https://ja.vuejs.org/api/built-in-directives.html#v-bind

# v-model

フォーム要素とVue.jsのデータを双方向でバインドするために使用されます。

v-modelでフォーム要素に入力された値を変更すると、Vue.jsのデータも同時に更新されるため、フォームの値を簡単に管理することが出来る

### サンプルコード
```html
<template>
  <div>
    <input type="text" v-model="textMessage">
    <p>入力されたメッセージ: {{ textMessage }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      textMessage: ''
    }
  }
}
</script>
```

<input>要素にv-modelを設定して、データ`textMessage`とバインドしており、<input>要素に入力された値が `textMessage` に自動的に反映されます。

<p>要素内でtextMessageの値を表示しているため、入力されたメッセージをリアルタイムで表示することができます。

**参考記事**

https://ja.vuejs.org/api/built-in-directives.html#v-model
Unity では iOS, Android ビルド時に dynamic は使えない

Unity では、動的な型 `dynamic` が使えますが、

アプリビルド時には、使えません。理由は、
iOS + IL2CPP
だとビルドエラーになるから。

https://light11.hatenadiary.com/entry/2019/05/19/172107

早く対応してくれると嬉しい🐡
C# .NET6 DLL　動的ロードについて

#C# WPF .NET6 DLL 動的ロードについての調査事項

#AssemblyロードにてDLLを読み込む

#問題点　
動的DLLをアプリケーション起動中に再ロードしてもオブジェクトが解放されな
い

#解決案
　##AppDomainを使用しての解放
    マイクロソフトのリファレンスから「.NET で使用できない.NET Framework テクノロジー」を参照　.NET は使用できない事が判明

　##AssemblyLoadContextを使用しての解放
    参考 URL https://www.ayumax.net/entry/2019/12/10/000000/
             http://crocell01.blog18.fc2.com/blog-entry-173.html
             https://learn.microsoft.com/ja-jp/dotnet/core/tutorials/creating-app-with-plugin-support

#コード
```
    public class manage
    {
        static List<sampleInterface> CommadList;

        public void LoadUnLoad()
        {
            string currentpath = System.IO.Directory.GetCurrentDirectory();

            //CommadList = new List<sampleInterface>();

            // プラグインパスチェック
            if (currentpath != null && System.IO.Directory.Exists(currentpath))
            {

                // ディレクトリ内のDLLファイルパスを取得
                foreach (string dll in Directory.GetFiles(currentpath, "ClassLibrary*.dll"))
                {
                    ExecuteAndUnload(dll, out WeakReference alcWeakRef);

                    // アンロードされるまで待つ
                    int counter = 0;
                    for (counter = 0; alcWeakRef.IsAlive && (counter < 10); counter++)
                    {
                        GC.Collect();
                        GC.WaitForPendingFinalizers();
                    }

                    if (counter < 10)
                    {
                        System.Diagnostics.Debug.WriteLine("アンロード成功");
                    }
                    else
                    {
                        System.Diagnostics.Debug.WriteLine("アンロード失敗");
                    }
                }
            }


        }


        [MethodImpl(MethodImplOptions.NoInlining)]
        void ExecuteAndUnload(string assemblyPath, out WeakReference alcWeakRef)
        {
            // アセンブリをロードするAssemblyLoadContextを作成
            var alc = new TestAssemblyLoadContext();

            // アセンブリをロード
            Assembly a = alc.LoadFromAssemblyPath(assemblyPath);

            // 外からアンロードを検知するために弱参照を設定
            alcWeakRef = new WeakReference(alc, trackResurrection: true);

            // リフレクションで関数コール


            foreach (Type type in a.GetTypes())
            {
                // リフレクションで関数コール
                Type t = type.GetInterfaces().FirstOrDefault((_t) => _t == typeof(sampleInterface));

                if (t == default(sampleInterface)) continue;

                // 取得した型のインスタンスを作成
                var obj = Activator.CreateInstance(type);

                //CommadList.Add(((sampleInterface)obj));
                
                // 処理を実行
                ((sampleInterface)obj).sample2();

                // アンロード実施
                alc.Unload();
                break;
            }
        }
    }

```
#結果
アンロードはできたが、CommadList処理をメソッド内に含めるアンロードが失敗する
実際の使用方法は、ロードしたオブジェクトを使いまわしたい為、外部で保持する必要があるのだが、保持してしまうとアンロードが出来なくなることが分かった。

また、CommadListにnullを入れてもダメ、CommadListをロード、アンロードする内部で記述するだけでもアンロードができなかった。

本当に閉じた状態でしか使用ができないようだ。
Ubuntu + LibreOffice のみ日本語入力ができない



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/641a4441-b3e2-1dc8-009b-0fc6f7e80d28.png)

ここの「あ」がLibreOffice に切り替えると消えてしまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/d4b5e8ce-1789-0f1a-02ec-3d06b17ca2e2.png)

# 環境

- Ubuntu 22.04 LTS
- LibreOffice 7.3.7.2
- ibus-mozc
- 
LibreOffice は Ubuntu ディストリビューション付属のパッケージです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/3c4ba9bc-9ae0-900f-0559-c5e9c60b9499.png)

英語環境でインストールした状態から、日本語環境を追加インストールしたのでパッケージが足りてないようです。

# 対応・・・ダメでした

libreoffice-gtk をインストールします。

```
$ sudo apt install libreoffice-gtk
[sudo] nanbuwks のパスワード: 
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています... 完了        
状態情報を読み取っています... 完了        
以下のパッケージが自動でインストールされましたが、もう必要とされていません:
  libflashrom1 libftdi1-2 libpython2-stdlib libpython2.7-minimal
  libpython2.7-stdlib python-pkg-resources python-setuptools python2
  python2-minimal python2.7 python2.7-minimal
これを削除するには 'sudo apt autoremove' を利用してください。
以下の追加パッケージがインストールされます:
  libreoffice-gtk2
以下のパッケージが新たにインストールされます:
  libreoffice-gtk libreoffice-gtk2
アップグレード: 0 個、新規インストール: 2 個、削除: 0 個、保留: 208 個。
13.9 kB のアーカイブを取得する必要があります。
この操作後に追加で 496 kB のディスク容量が消費されます。
続行しますか? [Y/n] y
取得:1 http://jp.archive.ubuntu.com/ubuntu jammy-updates/universe amd64 libreoffice-gtk2 all 1:7.3.7-0ubuntu0.22.04.2 [7,738 B]
取得:2 http://jp.archive.ubuntu.com/ubuntu jammy-updates/universe amd64 libreoffice-gtk all 1:7.3.7-0ubuntu0.22.04.2 [6,124 B]
13.9 kB を 2秒 で取得しました (8,736 B/s)   
以前に未選択のパッケージ libreoffice-gtk2 を選択しています。
(データベースを読み込んでいます ... 現在 213317 個のファイルとディレクトリがイン
ストールされています。)
.../libreoffice-gtk2_1%3a7.3.7-0ubuntu0.22.04.2_all.deb を展開する準備をしていま
す ...
libreoffice-gtk2 (1:7.3.7-0ubuntu0.22.04.2) を展開しています...
以前に未選択のパッケージ libreoffice-gtk を選択しています。
.../libreoffice-gtk_1%3a7.3.7-0ubuntu0.22.04.2_all.deb を展開する準備をしていま
す ...
libreoffice-gtk (1:7.3.7-0ubuntu0.22.04.2) を展開しています...
libreoffice-gtk2 (1:7.3.7-0ubuntu0.22.04.2) を設定しています ...
libreoffice-gtk (1:7.3.7-0ubuntu0.22.04.2) を設定しています ...
```

これで直ったと思ったら・・・一時的にうまく行ったと思ったら、すぐにもとに戻ってしましました。

# 再インストール

一旦 LibreOffice をアンインストールして、必要なパッケージを再インストールします (2023/5/23追記：これは必要なかったかも。この後の「ibus-mozc を再インストール」をご覧ください。)


```
$ sudo apt remove libreoffice
$ sudo apt update
$ sudo apt -y install libreoffice libreoffice-l10n-ja libreoffice-dmaths libreoffice-ogltrans libreoffice-writer2xhtml libreoffice-pdfimport libreoffice-help-ja
```


(ここから2023/5/23追記)

LibreOffice 再インストールでうまく言ったように思えましたが、しばらくするとまた日本語入力がダメになってしまいました。Google Chrome だと調子がいいのですが、それ以外のアプリも LibreOffice と同様の挙動になったりして不安定です。結局以下のようにして解決しました。


# ibus-mozc を再インストール

```
$ sudo apt purge ibus-mozc

```

再起動してから、

```
$ sudo apt install ibus-mozc

```
この後再起動して、アプリを起動して IME を ON にすると・・・最初はIMEが起動できませんでしたがアプリを切り替えたりしているうちにうまく動くようになりました。

なお、
「トグルキー操作なしで日本語入力を」
https://qiita.com/nanbuwks/items/d53911b0b8bfd8c6c548
の設定を ` sudo apt purge ` する前にしていましたが、その設定は消されずに、`sudo apt install ` 後も有効になっていました。
dockerfileに関して

# Dockerfileの変数に関して
毎回忘れちゃうので備忘録

## ARG

Dockerfile内で使用できる変数として利用できる
docker-compose v2より、docker-compose.yml内でargsを利用でき、docker-compose.ymlで具体的な値を設定、Dockerfileで利用する的なことができる

- Dockerfile

```
ARG NODE_VER
ARG RUBY_VER
ARG YARN_VER
```

- docker-compose.yml

```
  web:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        NODE_VER: "12"
        RUBY_VER: "2.6.6"
        YARN_VER: "v1.22.15"
        BUNDLE_VER: "2.3.5"

```

## FROM

作成するコンテナのベースとなるイメージを指定する

- Dockerfile

```
FROM ruby:${RUBY_VER} //${ARGで設定している変数が利用できる}
```

## EXPOSE
公開するコンテナのポートを指定する

- Dockerfile

```
EXPOSE 80
```

## RUN

イメージを作成する際、実行したいコマンドなどを記載できる
複数指定する場合は「&&」が推奨されている


- Dockerfile

```
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash - && apt-get install -y nodejs
RUN apt-get install -y libjpeg62 libpng-dev libxrender-dev libfontconfig1-dev libxext-dev fonts-ipafont-gothic
RUN npm install --global yarn
```

## CMD
コンテナ起動時に実行するコマンドとオプションを変更できる
原則は末尾に記載する
コンテナを実行したときに”デフォルトで”実行されるコマンド
ENTRYPOINTも指定されている場合は、ENTRYPOINTの引数として渡される

- Dockerfile

```
# CMD ["実行コマンド", "オプション1", "オプション2"...]
# 記載例
CMD ["ls", "-a"]
```

## ENTRYPOINT
コンテナ起動時に実行するコマンドとオプションを変更できる
原則は末尾に記載する
コンテナを実行したときに”必ず”実行されるコマンド

- Dockerfile

```
# ENTRYPOINT  ["実行コマンド", "オプション1", "オプション2"...]
# 記載例
ENTRYPOINT ["ping","127.0.0.1","-c","100"]
```

## WORKDIR
操作するディレクトリの絶対パス（推奨）を指定する
以降はそのディレクトリを基準にDockerfileに書いた操作が実行される

- Dockerfile

```
# WORKDIR 絶対パス
# 記載例
WORKDIR /opt
今まで書いてきたDockerfile
```

## ENV

環境変数の設定できる

- Dockerfile

```
# ENV 環境変数名=値
# 記載例
ENV DB_USER="user" \
    DB_PASSWORD="password" \
    DB_DATABASE="sample_db"
```

## COPY
明示的なコピー元とコピー先のファイルまたはディレクトリを指定して、ローカルファイルを再帰的にコピーする
 COPYでは、場所を宣言する必要がある
COPYはソース側のマシンにあるものをImageへコピーするだけというシンプルな機能

- Dockerfile

```
COPY ./file.conf /etc/conf
```

## ADD
ローカルファイルを再帰的にコピーし、存在しない場合は暗黙的にコピー先ディレクトリを作成し、アーカイブをコピー元としてローカルURLまたはリモートURLとして受け入れる
アーカイブはそれぞれコピー先ディレクトリに展開またはダウンロードされる
ADDは対象が圧縮ファイルであればそれを展開してImageへ移し、リモート上のリソースも引っ張ってくる機能がついている


- Dockerfile

```
ADD ./file.tar /etc/conf
```

#### ADDのリスク
- リモート上のリソースを自動で取りに行くのでダウンロード中、中間者攻撃の対象となりえる
- 圧縮ファイルを自動で展開するので、ZIP爆弾やZIP SLIP脆弱性攻撃の対象となりえる

#### ※使い分けとしては容量が大きいファイル等をtarで圧縮した場合はADDでコピー、それ以外はCOPYといった感じ




## 参考記事
https://docs.docker.jp/engine/reference/builder.html
https://alterbo.jp/blog/ryu2-2106/
https://qiita.com/momotaro98/items/bf34eef176cc2bdb6892
https://qiita.com/soushiy/items/0945bcbc7ecce4822985
https://penpen-dev.com/blog/docker-cmd-entrypoin/
AWSにおいてRDP接続をするための方法を調べた

# はじめに
なんとなくわかったつもりで、全くわかっていなかったRDP接続について、今回改めて調べたことをまとめてみました。

# RDP接続とは
リモートデスクトッププロトコル (RDP) は、デスクトップコンピューターをリモートで使用するためのプロトコルまたは技術的な規格です。

リモートデスクトップは、別のコンピューターから遠く離れたデスクトップコンピューターに接続して使用する機能です。リモートデスクトップを使用するユーザーは、実際にデスクトップコンピューターの前に座っているかのように、デスクトップにアクセスし、ファイルを開いたり編集したり、アプリケーションを使用したりできます。従業員が、出張時や在宅勤務時に、リモートデスクトップソフトウェアを使用して職場のコンピューターにアクセスすることがよくあります。

参照：https://www.cloudflare.com/ja-jp/learning/access-management/what-is-the-remote-desktop-protocol/


RDPのイメージがつかめない人は、ラジコンを操作するところを想像してみるといいとのことで、以下の例がとても分かりやすかったので、載せておきます。
>リモコンから出る電波とラジコンで受ける電波は同じじゃないと困りますよね。
ラジコンがリモコン以外の電波までガンガンに受け付けてしまったら誤作動しまくりです。
そのため、ラジコン用の電波が「どんなものか」は事前に決まっています。
リモコンは、どんなものか決まっているラジコン用の電波を出すことで、ラジコンとやり取りします。
言い換えると「ラジコンと、やり取りする際のルール」が決まっているわけです。
その「やり取りする際のルール」のリモートデスクトップ版がRDPです。
リモートデスクトップ接続するときは、RDPというルールに従って、コンピュータ同士がやり取りします

参照：https://wa3.i-3-i.info/word1679.html

## RDP接続をAWSで実現するには
AWSでRDP接続をするための方法をいくか紹介します。

### ① RDPクライアント
おすすめ度：★☆☆☆☆


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115411/50c8d75a-236b-2658-7830-6f8ca2a6c56d.png)

Public Subnetに配置したEC2インスタンスへのアクセスは、セキュリティグループによって、特定のIPアドレスのみに制限します。
しかし、パブリックサブネットにEC2を配置しているため、特定のIPアドレスのみに制限していたとしても、DDoSとIPスプーフィングの攻撃を受ける（特定のIPアドレスになりすまし、悪意のある攻撃を仕掛けられる）ことがあります。AWSの管理するグローバルIPアドレスは世界中で把握されており、この攻撃を受けやすいため、おすすめできません。

### ② ポートフォワーディングでのRDP接続
おすすめ度：★★☆☆☆
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115411/c72b5ca6-adf2-ddb6-45f0-562aed089f9a.png)

セキュリティグループの設定や、踏み台サーバの管理が必要となり、運用の手間が多くなります。
そのため、ポートフォワーディングでRDP接続をする場合は、③のSession Managerを利用したRDP接続をおすすめします。

### ③ Session ManagerでポートフォワーディングしてRDP接続
おすすめ度：★★★☆☆
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115411/b0ef0cbd-2fc8-b046-6b8b-e7c6ce46d0e1.png)
②と比べ、踏み台サーバが不要になるため、ポート（22、3389）を開く必要がなくなります。
また、AWSコンソールかCLIベースでアクセスでき、CloudTrailやS3、CloudWatch Logsにログを残すことができます。
しかし、ポートフォワーディングには前準備が必要で、少し面倒です。


### ④ Fleet ManagerでRDP接続
おすすめ度：★★★★☆
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115411/a2b0018f-22c1-f2e5-6b53-d66df9c0f44e.png)
Fleet Managerを利用することで、Windows上の様々な操作をWeb GUIから行うことができます。
①、②、③と比べ、EC2インスタンスのポートを開放する必要がないため、よりセキュアになります。
Fleet Managerは日本語キーボードに対応しておらず、例えばローカルのメモアプリで日本語を打ち、リモートサーバにコピペするというひと工夫が必要であったり、RDP接続での画面操作になるためロギングツールを入れないとログを取得できないなどのデメリットもあります。

# おわりに
自分の勉強したことをまとめる備忘録として残しておきます。
1ヶ月目の自分に言ってやりたいこと

## はじめに

1ヶ月目の方はもしかしたら耳が痛いかもしれません。
心の準備はいいですか？

それでは、どうぞ。


## エラー文を読め。

初めの頃は、来る日も来る日も真っ赤なエラー画面の嵐。
嫌気が差していた私は、ろくにエラー文も読まずに自分の書いたコードと睨めっこ。
「ここが間違っているに違いない！」という謎の勘に従って修正し、当然のごとく同じエラー。
あんなに親切極まりない文章を、なぜ読まなかったのか。
ひよっこのお前の勘が当たるわけない。エラー文が全てだ。
英語だからって逃げるな。向き合え。

## 内部挙動を疑うな。己を疑え。

毎度おなじみのSyntaxError。
コードを確認。異常はない。
「バグった...?」
そんなわけあるか。
隅々まで目を凝らして確認しなさい。
（<% end %>と書くべきところを<%= end %>と書いていた時の話）

## 諦めるな。立ち向かえ。

知らない言語が目の前に現れた時。
いかにも複雑そうな見た目のコードに遭遇した時。
そっ閉じして現実から逃れようとするな。むやみに休憩をとるな。
そんなことをしたって状況は変わらない。
問題を後回しにするな。今、その瞬間から理解しようと努めなさい。
見た目に反して意外と単純な場合も多い。怯むな。

## 時には諦めろ。

分からないからっていつまでも立ち止まるな。
明らかに強敵な相手は無視。確実に倒せる相手から着実に仕留めていこう。
分からないことをそのままにしておく気持ち悪さもわかる。
だがいずれ分かる時が来る。11ヶ月目の私が保証する。
APIもFormオブジェクトも今では自由自在に操れるようになった。
だから大丈夫。時が来るのを待つんだ。
Power BI DesktopとPower BI サービスの比較

PL-900などの試験で、Power BI DesktopとPower BI サービスの違いについてよく出てきますが、MSLearnでも触れられない話題を１つ載せておきたいと思います。

# MS Learnでの説明

https://learn.microsoft.com/ja-jp/power-bi/fundamentals/service-service-vs-desktop

上記ドキュメントの説明に下記の絵が記載されてます。
Power BI Desktopはあとからテーブルに列追加して計算列を追加できたり、Power Queryで変換できたり・・など自分専用にお手軽に編集できるのがよいところ。
一方で、Power BI サービスは出来上がったレポートを他のメンバーに共有したり、ダッシュボードを作ったり、お金はかかるけど、見える化できるのがよいところ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2885406/feb8e016-5f17-77e4-4bb5-bb2850fc2255.png)

# MS Learnでの説明以外での違いについて
<strong><font color=red>Power BI サービスを利用して、EXCELファイルのデータをPower BI上に取り込む際に、テーブルのリレーションが作成されない</font></strong>

### Power BI Desktopの場合
リレーションシップの管理ができ、Excelデータを取り込んだ際に自動的にリレーションが張られる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2885406/06f6acb6-8edd-004a-e3ca-679303c5fcdd.png)


### Power BI サービスの場合
Excelを取り込むために、データセットを作成したが、リレーションシップを管理できそうな項目がどこにもない
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2885406/685f3454-1f25-bb66-3611-45048c0f5fee.png)

この違いについてマイクロソフトに問い合わせてみたところ、「Power BIサービスでリレーションシップを設定する機能は実装されていません」との回答だった。

うーん、やっぱりPower BIサービスのみを単独で使うのはあまりメリットがないのかもしれない。

ただ、Power BIに関しては、コミュニティサイト([Microsoft Idea](https://ideas.powerbi.com/ideas/))にて、追加してほしい機能のリクエストが投票されていて、投票数が多いほど改修される可能性が上がるらしいので、私ももちろん投票しましたよ！

以上、プチコラムでした。
【Swift】DataをStringに変換する

# はじめに
URLSessionなどで通信した場合、結果はData型で返ってきます。
しかし、Data型では中身がどうなっているかわからないので文字列に変換して、中身を確認する必要があります。
その際に頻繁に使うので記録しておきます。

# やりかた
```swift
let dataString = String(data: data, encoding: .utf8)
```

# おわり
私はSwiftを勉強する前は、Pythonをやっていました。
Pythonではjsonを扱うのがめっちゃ簡単でデコード用の構造体など用意する必要がなかったので、初心者の時はSwiftめんど！って思ってました笑
Astro製WebサイトにFirebase Analyticsを導入する

# はじめに

本稿では、[Astro](https://astro.build/)を使って生成されたWebサイトに[Firebase Analytics](https://firebase.google.com/docs/analytics?hl=ja)を導入する方法を紹介します。

Astroとは、コンテンツにフォーカスした高速なWebサイトを構築するためのフレームワークで、Static Site Generator (SSG: 静的サイトジェネレータ)の一つです。Astroの詳細はここでは割愛します。

本稿を読むにあたり前提として、Astroプロジェクトの作成、Firebaseプロジェクトの作成およびAnalyticsの有効化は既に完了しているものとします。

:::note info
Astro v2系、Firebase v9 SDKを使用します
:::

# Firebase JavaScriptライブラリの追加

下記コマンドでAstroプロジェクトにFirebaseライブラリを追加します。

```shell
yarn add firebase
# npmの場合は npm install firebase
```

# Firebaseコンポーネントの作成

Firebase.astroファイルをAstroプロジェクトのsrc/componentsディレクトリ配下に追加します。

このファイルには、 `<script>` タグを追加して以下のようなJavaScriptを追記します。このJavaScriptは、Analyticsを有効化したときに表示されたスクリプトをそのままコピペするで構いません。(このスクリプトはFirebaseのプロジェクトの設定で確認することができます)

```html:src/components/Firebase.astro
<script>
    // Import the functions you need from the SDKs you need
    import { initializeApp } from "firebase/app";
    import { getAnalytics } from "firebase/analytics";
    // TODO: Add SDKs for Firebase products that you want to use
    // https://firebase.google.com/docs/web/setup#available-libraries

    // Your web app's Firebase configuration
    // For Firebase JS SDK v7.20.0 and later, measurementId is optional
    const firebaseConfig = {
        apiKey: "Your Api Key",
        authDomain: "Your Auth Domain",
        projectId: "Your Project Id",
        storageBucket: "Your Storage Bucket",
        messagingSenderId: "Your Messaging Sender Id",
        appId: "Your App Id",
        measurementId: "Your Measurement Id"
    };

    // Initialize Firebase
    const app = initializeApp(firebaseConfig);
    const analytics = getAnalytics(app);
</script>
```

`.astro` ファイルに `<script>` タグを追加することで、**クライアントサイドJavaScript**を追加することができます。
`import { initializeApp } from "firebase/app"` のようにNodeモジュールをインポートすることができ、すべてバンドルされた上で `<head>` に.jsファイルが挿入されます。

【参考】 https://docs.astro.build/en/guides/client-side-scripts/

# Firebaseコンポーネントの利用

上記で作成したFirebase.astroをアナリティクスを使いたいページに挿入します。
本稿の例では、全ページにアナリティクスを効かせたいので、Layout.astro (全ページ共通のガワコンポーネント)に `<Firebase/>` コンポーネントを挿入しています。

```html:src/layouts/Layout.astro
---
import Firebase from '../components/Firebase.astro';

---

<!DOCTYPE html>
<html lang="en">
<head>
    <!-- Something -->
</head>

<body>
  <!-- Firebase Analyticsの利用 -->
  <Firebase/>
  <!-- 各ページのコンテンツを挿入 -->
  <slot/>
</body>
</html>
```


以上でAstro製WebサイトにFirebase Analyticsを導入することができました。
y''+3y'+2y=24e^(2x)+sinx「今週の微分方程式 #4」をwolframalphaとsympyでやってみたい。  

1min.– わんみん様の計算との違いがわかりませんでした。申し訳ありません。
・wolframで,
？？？サンプルの解の族
？？？Sample solution family
常微分方程式の分類　二階線形常微分方程式
ODE classification　　second-order linear ordinary differential equation
・sympyのplotでグラフがでます。

オリジナル

https://youtu.be/PRXi0zkfuYg

# wolframで

https://www.wolframalpha.com/input?i=y%27%27%2B3y%27%2B2y%3D24e%5E%282x%29%2Bsinx&lang=ja

English

https://www.wolframalpha.com/input?i=y%27%27%2B3y%27%2B2y%3D24e%5E%282x%29%2Bsinx


### 点検?
勉強中

# sympyで
plotでグラフがでます。

```python
from sympy import *
x = Symbol  ('x',real=True)
y = Function('y')(x)
var('C1 C2')
ans=dsolve(Eq(diff(y,x,2)+3*diff(y,x)+2*y,exp(2*x)+sin(x)))
print("#",ans)
print("#",ans.rhs)
plot (ans.rhs.subs({C1:1,C2:1})) 
# Eq(y(x), C1*exp(-2*x) + C2*exp(-x) + exp(2*x)/12 + sin(x)/10 - 3*cos(x)/10)
# C1*exp(-2*x) + C2*exp(-x) + exp(2*x)/12 + sin(x)/10 - 3*cos(x)/10
```

解の族？にしていません。1本だけです。

![0png.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109698/7178391c-365a-c73a-a352-7415265c0e17.png)

# 参考


# つづく

https://qiita.com/mrrclb48z/items/9eac3fcf3b040034885d