OmniAICreator/Qiita-1.07M huggingface dataset:
[Cloudfunctions] Your proposed upload is larger than the maximum object size specified in your Policy Document.

# エラー
CloudFunctionsのデプロイでコケる。

```
$ gcloud functions deploy test_func \
    --trigger-http \
    --allow-unauthenticated \
    --region=asia-northeast1 \
    --runtime=python39
ERROR: (gcloud.functions.deploy) Failed to upload the function source code to signed url: https://storage.googleapis.com/gcf-upload-asia-northeast1-xxxx.zip?GoogleAccessId=service-xxxx@xxxxx.iam.gserviceaccount.com&Expires=xxxxx&Si.
Status: [400:b"<?xml version='1.0' encoding='UTF-8'?><Error><Code>EntityTooLarge</Code>
<Message>Your proposed upload is larger than the maximum object size specified in your Policy Document.</Message><Details>Content-length exceeds upper bound on range</Details></Error>"]
```

# 原因
アップロードするファイルサイズが大きすぎるらしい。
下記画像が、CloudFunctionsのデプロイ最大サイズ。

> Your proposed upload is larger than the maximum object size specified in your Policy Document.

Googleの公式ドキュメントに書いてありました。
![スクリーンショット 2021-12-06 17.24.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121224/a3f3204b-4dd1-045b-b0a1-5d779d4da070.png)


https://cloud.google.com/functions/quotas

# 解決策

`.gcloudignore`にデプロイに不要なファイル・ディレクトリを追記する。
これによって、デプロイ時のアップロードサイズを減らせる。

#### 1. サイズの大きなファイル・ディレクトリを特定
隠しファイルも見たいので、`-a`オプションつけてます。

```bash
$ tree --du -h -a --sort=size
.
├── [ 92M]  venv

...

├── [109M]  bin
├── [ 144]  .env
├── [2.8K]  __pycache__
├── [8.0K]  crawler
├── [ 744]  model
│   ├── [ 113]  item.py
│   └── [ 503]  __pycache__
│       └── [ 407]  item.cpython-39.pyc
├── [  67]  .gcloudignore
└── [  22]  .gitignore

  14K used in 6 directories, 9 files

 7.1K used in 5 directories, 5 files
```

#### 2. デプロイに必要の無いファイル・ディレクトリを`.gcloudignore`に追加
```:.gcloudignore
.DS_Store
.git
.gitignore
README.md
__pycache__/
venv
```

#### 3. 再度デプロイ
無事、デプロイが通りました。

```bash
$ gcloud functions deploy test_func \
    --trigger-http \
    --allow-unauthenticated \
    --region=asia-northeast1 \
    --runtime=python39
Deploying function (may take a while - up to 2 minutes)...⠛
For Cloud Build Logs, visit: https://console.cloud.google.com/cloud-build/builds;region=asia-northeast1/xxxx
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
buildId: xxxx
buildName: projects/xxxx
entryPoint: test_func
httpsTrigger:
  securityLevel: SECURE_OPTIONAL
  url: https://asia-northeast1-xxxx
ingressSettings: ALLOW_ALL
labels:
  deployment-tool: cli-gcloud
name: projects/temille-dev/locations/asia-northeast1/functions/test_func
runtime: python39
serviceAccountEmail: xxxx@appspot.gserviceaccount.com
sourceUploadUrl: https://storage.googleapis.com/gcf-upload-asia-northeast1xxxx.zip
status: ACTIVE
timeout: 60s
updateTime: '2021-12-06T09:02:57.043Z'
versionId: '12'
```
Cloud9 + CodeCommitでサクッとterraform実行環境を作成する

# 目次

1. Cloud9 environmentを作成する

2. Cloud9にhomebrewをinstallする

3. Cloud9にtfenvをinstallする

4. .aws/credentialの設定をする 

5. CodeCommitリポジトリを作成する

#  1.Cloud9 environmentを作成する

こちらのAWS公式ドキュメントを参考に、Cloud9 environmentを作成します。
https://docs.aws.amazon.com/ja_jp/cloud9/latest/user-guide/tutorial-create-environment.html


# 2.Cloud9にhomebrewをinstallする
1.で作成したCloud9 environmentを起動します。
tfenvをinstallするための前提条件として、terminalからhomebrewをinstallします。
※tfenvとは、terraformのバージョン管理ツールのことです。

terminalからec2-userのパスワードを再設定します。

```
sudo passwd ec2-user
```

homebrewをinstallします。

```
sh -c "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh)"
```

homebrewにpathを通します。

```
test -d ~/.linuxbrew && PATH="$HOME/.linuxbrew/bin:$HOME/.linuxbrew/sbin:$PATH"
test -d /home/linuxbrew/.linuxbrew && PATH="/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin:$PATH"
test -r ~/.bash_profile && echo "export PATH='$(brew --prefix)/bin:$(brew --prefix)/sbin'":'"$PATH"' >>~/.bash_profile
echo "export PATH='$(brew --prefix)/bin:$(brew --prefix)/sbin'":'"$PATH"' >>~/.profile
```

brewコマンドを叩いて動作確認します。

```
brew
```


# 3.Cloud9にtfenvをinstallする

tfenvをinstallします。

```
brew intall tfenv
```

tfenvを使用して指定バージョンのterraformをinstallします。

```
tfenv list-remote         #install可能なバージョン一覧を表示
tfenv install x.xx.x      #指定バージョンのterraformをinstall
```

使用するterraformバージョンを指定します。

```
tfenv use x.xx.x
```

terraformコマンドの動作と指定したバージョンになっているかの確認をします。

```
terraform --version
```
 
# 4. .aws/credentialの設定をする 
Cloud9ではAWS managed temporary credentialと呼ばれるAWS側で自動でcredentialを作成する機能がonになっているので、
その機能をoffにし、terraform実行IAM Userのcredentialの入力が必要となります。
画面右上の歯車マークをクリックし、続いて画面左側AWS SettingsをクリックするとAWS managed temporary credentialのon/off画面が出てくるのでoffにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/593135/d28ccb5f-cadc-783a-c0b8-bd7f5928bc69.png)

以下コマンドで、terraform実行IAM Userのcredentialを入力します。

```
aws configure
```

# 5. CodeCommitリポジトリを作成する

こちらのAWS公式ドキュメントを参考にterraformソースコード用CodeCommitリポジトリを作成します。
https://docs.aws.amazon.com/ja_jp/codecommit/latest/userguide/how-to-create-repository.html

こちらのAWS公式ドキュメントを参考に作成したリポジトリへ接続します。
https://docs.aws.amazon.com/ja_jp/codecommit/latest/userguide/how-to-connect.html#how-to-connect-prerequisites
AMIを削除する際はEBSスナップショットの消し忘れに注意しよう

#はじめに
AMIを削除した後、EBSスナップショットが残り続けて余計な料金が発生し続けたことが過去にあったので、備忘録の意味も含めて載せておきたいと思います。
#AMIとは
AMI（Amazon Machine Image）とは、EC2インスタンスを起動する際に必要な、OSやボリューム、アプリケーションなどを含むテンプレートです。AMIには以下の情報が含まれています。

①1 つまたは複数の Amazon Elastic Block Store (Amazon EBS) スナップショット、または、instance-store-backed AMI、インスタンスのルートボリュームのテンプレート (オペレーティングシステム、アプリケーションサーバー、アプリケーションなど)
②AWS アカウントが AMI を使用してインスタンスを起動可能にするための起動許可
③インスタンスの起動時にインスタンスにアタッチするボリュームを指定するブロックデバイスマッピング
参考：[Amazon マシンイメージ (AMI)](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/AMIs.html)

####AMIの利用料金について
前述の①の説明にある通りAMIはEBSスナップショットも含んでいるため、既存のEC2からAMIを作成した際には必ずEBSスナップショットが作成されます。
そしてEBSスナップショットにはサイズと保持期間に対して利用料金がかかります。
参考：[EBSスナップショットの利用料金](https://aws.amazon.com/jp/premiumsupport/knowledge-center/ebs-snapshot-billing/)
つまりAMI自体に料金は発生しないけど、作成した際にセットで作られるEBSスナップショットに利用料金が発生してしまうのです。

#EBSスナップショットが作られることの確認
AMIを作成すると同時にEBSスナップショットが作られることを実際に確認してみます。
ここではテスト用のEC2インスタンスを用意しました。
このEC2インスタンスには、［/dev/xvda］と［/dev/sdb］のデバイス名で2つのEBSボリュームがアタッチされています。
![WS000000.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689573/d3b8299d-ca04-9381-fb9e-8bbe99c0cab5.jpeg)

この状態で、アクションボタンからAMIを作成してみます。（途中の過程は省略します）
![WS000001.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689573/bcf4109c-d924-3e0b-1a7a-6fdca0560280.jpeg)

ステータスがavailableとなり、AMIの作成が完了しました。
![WS000002.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689573/e1a46e5f-0989-4dd4-fffa-12441f5423ae.jpeg)

これと同時に、EBSスナップショットが作成されていることも見て取れます。
![WS000003.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689573/bab48f4f-8687-85fd-5d8a-64a154661523.jpeg)

#AMIの削除
今度はAMIの削除を行ってみます。
アクションボタンから、登録解除を選択すると、AMIを削除することが出来ます。
![WS000005.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689573/f250fadf-ef9f-9424-cde4-d6111d2ae12c.jpeg)

AMIが削除されてことが確認できました。、
![WS000006.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689573/aae61f72-f9d4-e79c-dd9c-fb91c140be55.jpeg)

しかし、EBSスナップショットの一覧を確認してみると、AMIに紐づいてたスナップショットは依然として残ったままです。
つまりAMIを削除しただけでは、EBSスナップショットは消えてくれず、料金が加算され続けてしまうのです。
![WS000007.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689573/8e4db6ff-e8bb-c4d7-bdaa-246923f8836a.jpeg)

####EBSスナップショットの削除
このままでは料金が加算され続けてしまうので、スナップショットの削除も行います。
アクションボタンから、削除を選択すると、EBSスナップショットを削除することが出来ます。
![WS000008.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689573/0c3d5ccc-62c8-5aeb-2de9-f89c9275cf85.jpeg)

これで料金が加算されることはなくなりました！
![WS000009.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689573/2306a0f9-5d58-60ca-3b92-5f4195046f95.jpeg)

#追記
AMIを削除する前にEBSスナップショットを削除しようとすると、下記のエラーが発生します。
![WS000004.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689573/f608741d-cf91-ade6-3a60-47d80bdea7be.jpeg)
これはAMIがEBSスナップショットと紐づいているからであり、仮にEBSスナップショットを削除してしまうとそのAMIはもう使えなくなってしまうためです。
従って削除を行う際は、必ずAMI→EBSスナップショットの順番で削除するようにしましょう。
nodenvのnode versionsリストをアプデする

久しぶりに触って忘れたので、メモ用に残します。

公式[1]にあったのでそのまま転載します。

```shell
$ cd ~/.nodenv/plugins/node-build
$ git pull
```


[1] https://github.com/nodenv/nodenv#updating-the-list-of-available-node-versions
審査ってなあに？

#**自己紹介**

こんにちは！App Review（以降「審査」）チームのさんしです、好きな食べ物はプロテインです。
最近はクレアチンにも手を出していて、成長期真っ盛りの39歳です。

昨年までアドベントカレンダーはQA部門のみで実施していましたが、今回から部内全体でやろうぜということになり、審査チームからも投稿させていただきます。

でも、「審査」と聞いてもピンと来ない方がほとんどなのではないでしょうか。
ということで、初回ということもありグリーのアプリ審査ってどんなことをやっているのかを紹介させていただければと思います！



#**審査ってなあに？**

ひとことで言うと「あんしん・あんぜんを守る番人」です。

ゲームや各種サービスを運営するに当たって、守るべき法令や業界ガイドライン等が存在します。
運営するサービスで法令違反をしてしまうのは大問題というのは言わずもがなですが、最近ではガチャの誤表記などによる、いわゆる「炎上」事案が業界でも相次いで発生しています。
こういった問題を発生させてしまうと、お客さまにご迷惑をおかけしてしまうどころか、多額の返金に発展したり、企業への信頼低下によるユーザー離れなどによって、事業を揺るがしかねない大きなリスクへと発展してしまいます。

実際にニュース記事などでご覧になり、影響の大きさに驚かれた経験がある方も多いのではないでしょうか。

何も対処せずに運営していると上述のとおり事業を揺るがしかねないリスクに発展してしまう為、それら遵守すべき法令等がきちんと守られているか、リリースしても問題ない仕様や表示になっているのか、世に出る前に審査を行いリスクを未然に防ぐことが僕たち審査チームの仕事です。


#**ミッション**

上でも触れましたが、事業を揺るがしかねないリスクを未然に防がなければならない為、僕らのミッションは第一に「事業を守る」こと！
なんですが、これだけだとなんだか夢がないというか。
事業を守るのは当たり前にしないといけないよね、ということで、加えて「付加価値向上で事業に貢献」することをミッションに掲げています。

品質向上だったり工数削減だったり、単に守るだけじゃなく攻めの部分もしっかりやっていきますよ！という意志が込められています。


#**具体的な業務内容**

グリーグループではゲーム事業、メタバース事業、広告・メディア事業の3本の事業を柱として注力しており、審査チームではそれら事業を対象に業務に当たっています。
現状ではゲーム事業を中心に対応している為、ゲーム事業前提でご説明します。

**新規開発フェーズ**
ローンチする予定のゲーム企画に対し各種法令等が守られているのか審査を行います。
企画初期段階から仕様の確認を行っていき、開発後半でプレイアブルになったら仕様に加えUIの審査も行います。
ローンチ直前には、ガチャやショップなどの商材で不当表示にあたる表示がないか、検証作業も行います。（これを表示検証と呼んでいます）

ポイントとしては、特定の企画の審査を行うのではなく、ゲームの根幹仕様から各種表示まで、上流工程から網羅的にゲーム全体を審査することで、リスクを未然に防いでいるという点です。

**運用フェーズ**
大きく2つ、新規追加・変更する企画の審査と、ガチャなどの施策リリース前に行う表示検証に分かれます。
追加する企画についてはゲーム内の新たな機能などはもちろん、ゲーム外で実施するキャンペーンやイベントなども対象にしています。

審査フローのイメージは以下のような感じです。
![審査フロー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/930124/d7373f50-b651-e024-ad56-b3ea9042db71.png)


ちなみに、「法令を確認するって、法務の仕事じゃないの？」と思うかもしれませんが、法務がゲームの細かい仕様や表示すべてを確認するのはリソース等の観点から現実的ではありません。
そのため審査チームが独立した組織としてチェック要件をナレッジ化・オペレーション化し、判断に迷う要件が発生したら適宜法務と連携するような体制を取っています。

また、審査チームがプロダクトと法務の間に立ち、適切な落としどころに着地させる緩衝材のような役割も果たしています。
利害関係の異なる双方の間に入ることで、プロダクトのやりたいことを適法の範囲内で実施できるよう、日々行動しています。

別の視点で、「表示検証ってQAと何が違うの？」とご質問をいただくことが多くあります。
検証を行うという点ではQAと大きな違いは無いのですが、以下のような違いがあります。

| QA      |正しさの根拠は仕様書。仕様書通りの実装・挙動になっていることを担保|
|:------------|:------------|
| <B> 表示検証  |<B> 正しさの根拠は法令。不当表示にあたる表示になっていないことを担保|

例えば、QAだと仕様書どおりに実装されていればOKとなるところ、表示検証の場合「仕様書どおりだとしても、誤認させる表示になっていないか」という視点で検証を行います。
そのために、不当表示に関する訓練を受けたスタッフにて検証を行う体制になっています。



#**審査の特徴**

なんといっても、審査が「リリース承認権限」を持っているということかなと思います。
他社さんと意見交換していても、基本的には事業側で権限を持っていることがほとんどで、ここまで強い権限を持つというのは珍しいのではないでしょうか。

といっても「承認できないのでリリース不可！」となるケースはほぼなく、そうならないよう調整するのも審査としての大事な仕事です。

気を付けていることとしては、強い権限を持つ部門だからこそ、事務的にならないように気を付けています。
プロダクトとの距離が生まれてしまうと連携もしづらくなりますし、それによって品質リスクが高まってしまうなんてことにもなりかねない。
なので、極力柔軟に対応したり、「貢献できることはなんだろうか」と常々考えて行動するようにしています。

#**今後の展望**

審査という仕事は業界内でずっとニッチな分野でした。
何にどこまでコストを割くべきか、どの観点までカバーするべきなのかなど最適解が無く、ベストプラクティスがありませんでした。

ですがここ数年は他社さんでも審査に似た組織が組成されてきており、意見交換をさせていただく機会も増えてきました。

異なる会社、同じ立場の方々と意見を交えることで、「こうしなきゃいけないよね、こうあるべきだよね」と、業界としてあるべき姿が見えつつあります。

今後は「自分の会社の品質が守れていればいいや」ではなく、業界全体の品質底上げに繋がる取り組みができたらいいなと思います。
いや、します！
【現役インフラエンジニアが語る】未経験者が気になるインフラエンジニアの実際の仕事内容

## はじめに
こんにちは、日本リック エンジニア採用チームです。

今回は日本リックに新卒で入社して4年目になるインフラエンジニアYさんの記事です。

日本リックのインフラエンジニアの仕事内容や、求められるスキル、イメージと現実とのギャップ、インフラエンジニアの魅力について教えてもらいました。

「インフラエンジニアの実際の仕事内容を知りたい」という方の参考になれば嬉しいです。



## 私のプロフィール（20才後半/男性）
新卒で入社してからの最初の現場ではある団体の情シス配下のヘルプデスクでヘルプデスク業務を行なっていました。各種申請の処理や問い合わせ対応、外部へ依頼が必要な作業が入ってくるとその作業内容の整理と依頼をしていました。

ヘルプデスクの次は企業に敷設された無線ネットワーク老朽化に伴う更改案件に参加していました。工事業者の方やＳＩパートナー(無線構築担当)、設置先オフィス部署担当者との間で各種調査・作業の日程調整、切り替え時の作業担当、ドキュメント作成など行なっていました。

現在は通信系大手企業およびそのグループ会社に対するセキュリティ全般の業務を行なっています。企画にはまだ携われていませんが、設計構築から運用立て付けまでくらい工程に関わらせて頂いています。以前までの現場とは違い繰り返しの作業がなくなったこと、昨今の状況と緊急事態宣言が明けたことを受けて、週一出社でほぼテレワークな環境です。チームの連携向上と負荷のいいとこ取りを狙っています。


## 未経験者が気になるインフラエンジニアの実際の仕事内容
インフラエンジニアと一言にいっても実際の業務内容は無数にあります。

ザックリですが、私が従事した二つ目の業務では以下のようなことを行なっていました。
（インフラエンジニアの中の一業務としてこれを取り上げています。）

１：スイッチ、AP（アクセスポイント）のキッティング
２：設置箇所にいる部署の担当との調査日・作業日の日程調整
３：スイッチ、APを更改・新設する現場の調査（電波設計）
４：新旧設備の切り替え作業、手順書作成
５：作業後の各種納品ドキュメント作成
６：各種作業の進捗管理、課題解決

ここで注目していただきたいのは、いわゆる**インフラエンジニア界隈で必要とされる技術を用いないとできないことばかりではなく**（１、４くらい）、excelやメール・電話、打ち合わせをしていることが多かったという点です。

また、比較的ユーザに近い領域でしたので、作業はユーザへの業務影響を考慮して、作業自体が定時後開始となり、解散が深夜となることが多かったです。


## インフラエンジニアの現場で求められるスキル・知識
さまざまな仕事内容があるとは思いますが、未経験から転職して初めて従事する運用や保守といった仕事で、技術が占める割合は上の例とそう変わらないと思います。

では技術は軽視していいのかと思う方もいるかもしれませんがそうではありません。
現場で従事するための共通言語に相当するスキルだと思います。

私が思うに、インフラエンジニアに求められるのは、**キャッチアップ力**だと考えています。


### 理想と現実の違い
現実の環境が教本の世界と異なるのは、必ずしも最新技術の組み合わせが正解ではないことです。理想が必ずしも正解ではないとも言えます。

実際には、設計構築した際の情勢、企業規模や財務体力を鑑みて今回は

・ここを重視したい
・ここはそこまでケアしなくてもよい
・ここは将来の課題として、今回は考慮しない

というような整理と決定がされ、その経過が現在まで続く環境を形作っています。

現場を理解すると言うことは、現場に残された課題を理解することだと考えています。
この考え方は、将来企画や設計を志す方にも必要な課題抽出・課題解決のスキルにも繋がります。
これができないと業務の本質をわからずに、表面的にこなすだけになります。状況を理解できないので、日々の業務の意味がわかりません。
ただこなすだけで、一作業者としての動き方しかできなくなります。

また当然ですが現場が異なればそれだけそれぞれの歴史は異なっていて、本やネットなどで調べることができない（＝事前にインプットすることができない）生きた情報ですから、現場に参画していち早くその場の一員として動けるようになるためには、こうした力が必要とされていると感じます。
これができれば、定型的な仕事も常に改善の意識を向けながら進められますし、そうでない仕事も、現場の知識があることで効率よく、スムーズに進むと思います。


## 私が感じるインフラエンジニアの魅力
エンジニアの中でもインフラエンジニアは、魅力を感じにくいかもしれません。

言語開発者が「業務効率のためのアプリを内製で作った」となれば、それに対してみんなの仕事にわかりやすいプラスを届けたことになりますから、さまざまな声が届くことと思います。。
「助かる」「こここういうのもほしい」...モチベーションにつながりそうです。

しかしインフラエンジニアは、インフラを安定稼働させることが求められます。
安定的な稼働が続くと、ユーザーは自分達がそういった土壌の上で日々業務にあたっているということすら意識しなくなってきます。

また障害などでサービス利用不可の時間が発生したりするとクレームが来ることもあります。
そう言う意味ではエンドユーザから直接感謝の言葉を受けたい！という気持ちが強い人よりは、花形ではないものの当たり前を維持する役割にやりがいを感じる人向きな仕事かな、と思います。
絶対に必要なポジションですし。

日々、一見問題なく利用できているインフラを、黒子として支える、そういった縁の下の力持ち的振舞にやりがいと魅力を感じています。
【Python】dhash実装【OpenCV】

## はじめに
OpenCV Pythonを使ってdhashを利用した画像の類似度計算を行いたかったのですが、メソッドが存在しなかったため手実装しました。
OpenCVとnumpyを利用しています。

## ライブラリに頼る場合
https://pypi.org/project/dhash/
というライブラリがあるようです。
実装もこれを参考にしました。

## 実装方法
上記ライブラリの実装を見てみます。

- Convert the image to grayscale
- Downsize it to a 9x9 thumbnail (size=8 means an 8+1 by 8+1 image)
- Produce a 64-bit “row hash”: a 1 bit means the pixel intensity is increasing in the x direction, 0 means it’s decreasing
- Do the same to produce a 64-bit “column hash” in the y direction
- Combine the two values to produce the final 128-bit hash value

GrayScaleに変換した後リサイズ、横方向と縦方向で差分を取って結合、という手順のようです。

```dhash.py
import numpy as np
import cv2

image = cv2.imread('画像のパス')
# 8 * 8 の64bitハッシュを作成する
hash_size = 8
# グレースケール変換
image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# リサイズ方法はINTER_AREAが縮小向け
resized_image_gray = cv2.resize(image_gray, (hash_size + 1, hash_size + 1), cv2.INTER_AREA)
# 差分計算 横方向と縦方向それぞれ計算する
dhash_h = resized_image_gray[:-1, 1:] > resized_image_gray[:-1, :-1]
dhash_v = resized_image_gray.T[:-1, 1:] > resized_image_gray.T[:-1, :-1]

# 横方向と縦方向を結合
dhash = np.concatenate((dhash_h.reshape(-1), dhash_v.reshape(-1)))
```

dhashで類似度を比較する場合、ハミング距離を用いるようです。

```distance.py
hamming_distance = np.count_nonzero(dhash != dhash)
```
同じハッシュを渡せば0が返ります。

Lenaさんの名前入り写真と通常写真の距離は2でした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491627/8d07ac8c-b893-3c7b-8b93-a377ec60ba25.png)


## 参考
https://pypi.org/project/dhash/
https://i.botch.me/2019/08/26/dhashを生成するライブラリ作った/
https://tech.unifa-e.com/entry/2017/11/27/111546
【CentOS8】Sudoersファイルにユーザーを追加する方法

##＜はじめに＞
管理者権限を持つrootユーザーとしてコマンドを実行してみたところ


うん？sudoコマンドで実行できないぞ？？
ということがあってsudoersファイルにユーザーを追加して対処した時の記録をメモしたものです。

##＜現象＞
ルートディレクトリ配下にstorageディレクトリを作成するため、sudoコマンドを使って実行するが、以下のメッセージが理由で作成できない。
![キャプチャ2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1172565/3726e8e0-62b5-001c-127a-10999ea63d0b.png)
##＜原因＞
sodoersファイルにユーザーの設定がないため
※sudoersファイルとは、sudoコマンドで変更できるユーザーと実行できるコマンドを設定するためのファイルである
##＜対処＞
sudoersファイルにユーザーの設定を追加する必要がある。
以下の手順で行った。

①su - コマンドでrootユーザーに切り替える。
![キャプチャ5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1172565/ebba050a-3d19-6cc4-f6a4-7383048d0a5b.png)
②sudoersファイルを編集するためvisudoコマンドを実行する。
![キャプチャ6.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1172565/4532b544-4100-d5d2-8ab7-d745cdc7f6b2.png)
③sudoersファイル内の一般ユーザーの設定に"(ユーザ名) ALL=(ALL) ALL"を追加する。
※「ユーザ ホスト=(権限) コマンド」の記述で追加すること。上記はユーザーにすべてのコマンドの実行権限を与える設定になる。
![キャプチャ7.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1172565/e7a0c960-1119-25dd-05b1-ef8df67733e5.png)

結果、sudoコマンドで実行できるようになった！
lsコマンドでstorageディレクトリが作成できていることを確認
![キャプチャ7.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1172565/c7dba8aa-ba11-37af-5937-cc9a9eed485d.png)


参考情報：
[/etc/sudoersとは「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典](https://wa3.i-3-i.info/word13805.html)

[【CeontOS】「sudoers ファイル内にありません。この事象は記録・報告されます」というメッセージが出た時の対処](https://notepad-blog.com/content/112/)

[CentOSでsudoを使えるよう設定する](https://it-study.info/centos-sudo-enable/)

[覚えておきたいviコマンド](https://qiita.com/LostEnryu/items/faa0905ffecdeb794af5)
pipでAnacondaをぶっ壊しちゃった☆

[「しくじり先生 Advent Calendar 2021」](https://qiita.com/advent-calendar/2021/shikujiri)9日目の記事です。

自分のローカル環境の話なので、大きな被害はなかったですが自戒を込めて。
また調べていたら、このやらかし例が案外出てこなかったので誰かの参考になればと思います。

##2021年のある日
新しいパッケージをインストールすることになりました。
Anaconda Promptを開いて、いつものようにこう打ちました。

```python
pip install モジュール名
```
(どんなモジュールをインストールしようとしたかは忘れた)

一旦別の作業があったのでAnaconda Promptは閉じます。その後Jupyter notebookで作業しようと思い、再度Anaconda Promptを開いてコマンドを打ち・・・

**ん？** **あれ？**

Jupyter notebookが立ち上がらない。さあどうしよう？

コマンドをもう1回打ち直す。それでも立ち上がらない。
裏で走っていたアプリを終了させてみた。それでも立ち上がらない。
困ったときには奥の手再起動だ！それでも立ち上がらない。

あ、これはちょっとやばいやつかもしれない:innocent:
その日中にやらないといけない内容があり、顔には出さないもののかなり焦りました。

##原因
元の環境に戻すにも原因が分からないことにはどうしようもないので、「pip インストール　エラー」みたいな感じで検索していきました。
そうしたら下記の記事を発見。

https://onoz000.hatenablog.com/entry/2018/02/11/142347

内容をざくっとまとめてみた。

* Anacondaは基本condaでパッケージ管理されている
* pipでインストールすると、conda環境と混ざっておかしくなるケースがあるよ
* やらかしたら再インストール必要かもね

まさにこれです。condaで管理されているパッケージを、pipでインストールしてしまったのが原因でした。
当時はcondaとpipの使い分けを知らず、「とりあえずpipでインストールすればいいだろ」ぐらいにしか考えていませんでした。
 
記事だと2020年では共存した場合の対策も取られつつあるよとありましたが、2021年でも発生してしまいました。ここはツールやPCのスペックにも依存するかもしれません。

##その後
とりあえず記事通り、Anacondaをアンインストールしました。
Anacondaを再インストールしようとしたら、特定の条件下では有償化なっていることも判明しました(ある意味これもやらかし)
ということで、minicondaを利用して再構築することになりました(面倒だった)。

基本condaでパッケージをインストールして、対応していないものだけpipを使うようにしました。
今のところ再クラッシュはないので安心しています。
CloudFrontを利用してAPIの応答時間を短縮

[KINTO Technologies Advent Calendar 2021 - Qiita](https://qiita.com/advent-calendar/2021/kinto-technologies) の6日目の記事です。

# はじめに
単一地域だけではなく、世界中のユーザーに向けたAPIを提供する際、ユーザーの地域によってはAPIの応答時間が遅くなってしまう事があります。

地域毎にエンドポイントを分割したり等色々な解決方法はあると思いますが、CloudFrontを利用したシンプルなアーキテクチャで応答時間を短縮する方法を記載します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327741/0d477177-4229-a40f-2ad5-be7deb9b8bbb.png)

# キャッシュさせないCloudFrontを利用する
下記の図の用にCloudFrontを経由してオリジンサーバにアクセスさせます。
CloudFrontはキャッシュを利用した静的コンテンツの高速配信としてよく利用されると思いますが、
この時CloudFrontはキャッシュをさせず、あくまで経由だけの為に利用します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327741/7857eeae-ee25-f788-6e2a-0905faa8f724.png)

キャッシュさせないCloudFrontを利用する事により当然CDN最大の特徴である、キャッシュを利用しての応答時間高速化の恩恵は受ける事ができません。
ですが、ユーザーに近いエッジサーバを経由したアクセスを行う事ができ、複数の地域からでも応答速度の改善を行う事ができます。

■キャッシュさせないCloudFrontを利用する事により受けられる恩恵
・世界中に配置された300以上のエッジロケーションにより最短距離で応答を返す事がきる
・エッジサーバとオリジンサーバの通信はAWS ネットワークバックボーンを介しているため、高速で通信される

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327741/1fc364c6-785a-7c1b-40a0-8e2729d6faa2.png)


# キャッシュさせないCloudFrontを利用すると良いこと
・APIを提供する地域が増えてもインフラ構成を変更・追加する必要が無い
　・ インフラ構成が肥大化しないため、保守・運用コストも増加しない
・CloudFrontで提供されているセキュリティ機能を使う事ができる

当社では、トヨタ車のサブスク「KINTO」等の企画/開発を行っており、エンジニアを募集中です。
[KINTO Technologies コーポレートサイト](https://www.kinto-technologies.com/)

# 参考
[グローバルエッジネットワーク](https://aws.amazon.com/jp/cloudfront/features/?whats-new-cloudfront.sort-by=item.additionalFields.postDateTime&whats-new-cloudfront.sort-order=desc)
[Amazon CloudFront を活用したウェブサイトの可用性向上](https://aws.amazon.com/jp/blogs/news/improve-your-website-availability-with-amazon-cloudfront/)
OpsRamp 新規デバイス検知時にProcess Workflowでアラートを作成してみた。

#概要
以前の記事[「OpsRamp 自動検知設定/監視設定について」](https://qiita.com/yusawada/private/0822e6e462acb00056b3 "Qiita Home")で自動検知設定についてご紹介しましたが、例えばIaaSインスタンスがクラウド上で増えた場合。OpsRamp側でも新規にインスタンスが自動的に登録されます。そこで新規追加時にOpsRamp側でそれを知らせるAlertを生成するWorkflowを作成してみました。
今回はAWS環境のリソースを対象にします。

まずWorkflowの説明をします。
1, OpsRamp上でデバイス=インスタンスが新規追加される
2, それをトリガーにWorkflowが動く
3, 新規アラートが生成される

Workflow自体は非常にシンプルです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342082/d85adedf-971a-00da-5a1b-23bc901ed4f2.png" width=100%>

#各タスクの説明
画像の各タスクをWorkflowの流れと一緒に順に説明します。
※原則記載の無い設定項目はデフォルト値です。

###startevent(新規検知)
AWSリソースが新規検知された場合、このWorkflowを開始する。
自動検知したAWSのインスタンスのMakeのフィールドが「AWS」となっていたため、
こちらで条件を付けてみます。

| 項目 | 値 |
|:-|:-|
|  Name | Created|
| ID  | 自動入力  |
|  Configuration | Resource|
| Configuration2  | Created  |
| Filter Criteria  | $make = "AWS"  |

###Service Task(アラート生成)
検知されたリソースの情報をもとにinfoアラートを生成します。

| 項目 | 値 |
|:-|:-|
| Name  | Alert|
| ID  | 自動入力|
| Service| Platform Service|
| Task| Create Alert|
| Subject| $created.resource.name discovered|
| Current State| info|
| Metric Name| new_resource_discovered|
| Resource Name| $created.resource.name|
| Description| NAME:<code>$created.resource.name</code>, IP:<code>$created.resource.ipAddress</code>|

#アラート確認
実際に確認してみます。

アラートを生成するために、デバイスを手動で作成します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342082/3c296b2c-dbfd-c050-d772-22f5a3f81c76.png" width=70%>

このようにアラートが作成されました。
重要なアラートについては、Alert Escalationからメール送信してもいいかもしれません。
![0030.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342082/9a6ac884-d0ae-294c-e95a-14dbca4f15a6.png)

以上です。
PowerAutomate で OneDrive フォルダ上の最新ファイルを取得する

## やりたいこと

OneDriveのファイルの中から最新のファイルを見つける。

> ・・・他に方法ありそうな気はするのですがこれくらい力技でやっておけば他にも応用がききそう。
> OneDriveをSharePointにしたり、最新ファイルじゃなくて特定条件にしたり。

## さっそく手順

### **OneDriveからファイルを取得「フォルダー内のファイルのリスト」**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/b60194df-263f-97dc-abaf-d14381333436.png)

トリガーは何でもよいです。
もし該当フォルダのファイル数がそこそこ多いのであれば…「・・・」の「設定」の「改ページ」の設定を「オン」にして「しきい値」を多めにしておきましょう。デフォルトだとファイル取得の上限がけっこう少なめに設定されているようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/6617fbfb-384d-485d-4c40-6495fbcb5fe9.png)

### **Apply to eachで全ファイルを調べて一番新しいものをとってくる**

全体ではこんな感じです。
やっていることはファイル一覧の中から最大値をみつけるだけなのですが、詳しく説明していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/dc7e1413-eeac-151a-9f62-c4114fa933b5.png)

１つ１つの処理を見ていきます。

まず「変数」を初期化して用意します。
初期値は「`ticks('1970-01-01')`」です。適当にありえないくらい古い日付をいれておきます。
`ticks()` という関数で日付を整数値にしているのがポイントです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/8543d139-80c1-9036-5002-3aecf876d6c3.png)

Apply to each で全ファイルを１つ１つ見ながら次の条件で見ていきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/b6a5543a-4969-4df9-7419-50543d634816.png)

左辺は「`ticks(item()['LastModified'])`」です。つまりファイルの最終更新日を`ticks()`で数値にしたもの。
右辺は「`最新日付`」変数です。初期値だと 1970-01-01 という日付の `ticks()` の数値がはいっています。

数値同士なので大小で比較可能なので、「ファイルの最終更新日」が「最新日付（1970-01-01）」より大きい＝新しい日付かどうかをチェックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/826b08fc-ee9a-0d97-04ee-7717ef7953c9.png)

「**はい**」の場合には、変数「`最新日付`」に「`ticks(item()['LastModified'])`」つまり今チェックしている「ファイルの更新日」をいれます。

「**いいえ**」の場合にはなにもしません。

これを全ファイルにを繰り返すことで、新しいファイルを見つけ次第「`最新日付`」の更新をしていくことで、もっとも新しいファイルを見つけることができます。

### **最新のファイルを見つけてどうする？**

後はお好きにお使いください！
例えば「ファイル名が知りたい」だったら上の処理の「はい」の中でファイル名を変数にいれておけばよいです。

> もともとやりたかったことは、、、、
> ログを格納しているフォルダのファイル一覧の最終更新日がXX時間以上古い＝ログが更新されていない場合にバッチが止まっている！？
> みたいなチェックをするために作った処理です。
> なので「最新日付」が「`ticks(addToTime(utcNow(), -10, 'Hour'))`」≒現在時刻より10時間前と比べてどう？みたいな処理をいれることで監視処理に使いました。
失敗しないREST API

Web開発でREST-APIが採用されるケースは未だ多いと思います
ですがREST-APIは思想のようなもので、大した仕様が定義されていません

「REST-APIでやろう」というのは**HTTPSでいい感じにやろうや**と言っているのと同じくらいルールがない状態です
なんの取り決めもなく進めるとカオスになります

REST-API採用時、最低限のガイドラインとなる仕様やツールを紹介します

## [仕様] [OpenAPI (Swagger)](https://swagger.io/specification/) を使う
OpenAPIはREST-APIを記述するための仕様の1つです。これに従って記述することで秩序が生まれます
例えば、以下のようなIDをkeyにしたDictionary形式のレスポンスはOpenAPIで表現できないのでNGです

```json
{
  "users" : [
    "1": {"name": "hoge"},
    "2": {"name": "fuga"}
  ]
}
```

REST-APIの簡素な思想と違って、OpenAPIは割と仕様がマッチョです
全部読むのは大変なので、困ったときに見る形でOKだと思います

### Version 3以上を使う
Version 3以上を使いましょう。ファイルの先頭に書きます `openapi: "3.0.x"`
新しいバージョンを使うの理由は、単純にドキュメントの探しやすさです
古いSwaggerのサンプルでは version: 2を使っているケースがあるので注意しましょう


## [Viewer] Swagger UI で見る
これはチームで統一されていれば何でもいいと思います
Swaggerが導入しやすいと思うので紹介します

### [Swagger Editor (Online)](https://editor.swagger.io/)
デモの入力欄にコピペすればpreview欄（右側)に反映されます
コピペする手間はありますが、何の準備もいらないので楽です

### VSCode プラグイン
VSCodeで開発をしているならこちら
いくつか種類があるようなのでお好きなものを

### [Docker](https://hub.docker.com/r/swaggerapi/swagger-ui)
Swagger UIの Docker Imageが公開されているので、オンラインデモと同じ環境を自分で構築することができます

自分が試したときはこちらの記事を参考にさせてもらいました
[docker-composeでSwagger【Swagger】【Docker】](https://tech-up.hatenablog.com/entry/2019/04/10/104218)

## [Editor] [Stoplight Studio](https://stoplight.io/studio/) で書く
OpenAPI記述をめちゃ楽にしてくれる最高のアプリです

YAML手書きのペイン集 :head_bandage: 
YAMLを手書きするにはOpenAPIの知識がそこそこ必要
チームで記述しているとわずかに個人差が出るので統一感がなくなっていく
OpenAPI的に不正な構文でもSwaggerUIがよしなに表示してくれたりするので、間違いに気づけなかったりする

Stoplight StudioならUIに従って書いていくだけなのでほとんど迷いがないです。個人差も出ません
コンポーネント駆動開発について調べた

## 今までの開発手順
コンポーネント単位で開発してきた。アトミックデザインを念頭に開発し、各コンポーネントを組み立て、一枚のページを構築させまた、ユニットテストた結合テストといったバグを早期に発見できるように開発してきたが、タイトルのことを調べるうちに違和感を感じた。

自分のやり方を改善していくべきなのかと思いました。
そのアウトプットの内容をここに記入していこうと思う。

## Component Driven Development - CDD -
端的に言いますと「ボトムアップ」の開発プロセスで進めていく手法になります。
コンポーネントとは、ある責務を持った部品です。
その部品たちを小さな単位から開発していき、小さなコンポーネントを中くらいのコンポーネントに組み立てていき、そしてページを作り上げプロジェクトに統合していきます。
小さな単位はユーザーにとって特に重要ではないがプロジェクト統合までいくとユーザーにとって必要な情報があります。

少し抽象度が高いですが、自分の理解の仕方なのでご了承ください。ただポイントは押させられていると思います。

このコンポーネントを構築していくのに役立つのが、Atomic Designとゆう概念で、コンポーネントが持っている責務がメンタルモデルのフローのどこかに属します。

なので、あくまでもページベースの開発ではなく、コンポーネントベースの開発とゆうことです。

### メリット
#### 品質
コンポーネントを小さな単位で構築し、関連している状態を引き合うことで、違ったストーリーでコンポーネントが動作することを検証確認します。

#### 耐久性
UIの各種テストを各シナリオで行うことができるので早期にバグを発見することができます。

#### 速度
コンポーネントの組み立ての際に再利用を繰り返しますのでスピーディに行うことが可能です。

#### 効率
複数人でのUI開発に際し、個別でコンポーネントの構築していきますので、並列的に進行していきます。 

## まとめ
長くなるので他でまた書こうと思いますが、コンポーネントを開発していくにあたってStoryBookを使うことが今では一般化しているかと思います。こちらを使うことで、少ない作業でコンポーネントを数多く構築することができ、テストも同時に行うことができますし、様々なシナリオで機能することも考慮し構築できますので、柔軟性の高いコンポーネント構築も容易に開発することが可能です。
そして、アジャイル開発時に有効に働きます。小さな単位で構築していきますので、変更や追加などの突発的な要望にも応えることが可能です。

ただ、デメリットとしては、一線を引かないといつまでも続いてしまうとゆうところが挙げられるのと、ページ単位で構築していく開発には不向きな面もあります。

なので、メリットデメリットをしっかり考えた上で、こういった開発手法を検討した方がいいでしょう。

今回調べて改めて、考える機会に出会うことができました。
フロントエンドエンジニアとして従事しているのですが、これからもUI開発に関わる技術などの進歩がありますのでしっかりキャッチアップしていこうと思います。
OpsRamp AWSイベント連携

本記事では、OpsRampのAWSイベント連携機能について説明します。

# 概要
OpsRampには、Amazon CloudWatch Alarmを取り込む機能およびOpsRampで検知したアラートをAmazon Eventbridgeに送る機能があります。
Amazon CloudWatch Alarm連携機能では、Amazon CloudWatchで検知されたAlarmをOpsRampのアラートとして取り込むことにとり、メール通知・Process Automation・インシデント管理といったOpsRampの機能を使用することができます。
Amazon Eventbridge連携機能では、OpsRampで検知したアラートをAmazon Eventbridgeに送ることにより、AWSの機能と連携することができます。

# Amazon CloudWatch Alarm連携
## Amazon CloudWatch Alarmの設定
Amazon CloudWatch Alarm連携では、AlarmをAmazon SNS / SQSを使用します。下記を参照し、設定ください。
[Amazon CloudWatch Alarm](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/AlarmThatSendsEmail.html)
[Amazon SNS](https://docs.aws.amazon.com/ja_jp/sns/latest/dg/sns-create-topic.html)
[Amazon SQS](https://docs.aws.amazon.com/ja_jp/sns/latest/dg/subscribe-sqs-queue-to-sns-topic.html)

## OpsRampの設定
Amazon CloudWatch Alarmの取り込み設定は、AWSインテグレーションのオプション機能から設定します。
Amazon SQSの設定で取得したエンドポイントURLを入力します。
![aws_integration-CloudWatch-Alarm_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2344939/41f7e269-0f16-f448-5754-de1ac4186af9.png)

※ AWSインテグレーションの設定は、下記記事を参照ください。
[OpsRamp 自動検知設定/監視設定について](https://qiita.com/yusawada/items/0822e6e462acb00056b3)

## 結果
以下のようにアラートとして取り込めます。
![opsramp_alert-cloudwatchalarm.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2344939/f4bd195f-2fcc-8276-5093-336e93b3eb62.png)

# Amazon Eventbridge連携
## Amazon Eventbridgeの設定
### イベントバスの作成
必要に応じてイベントバスを作成します。
[イベントバスの作成](https://docs.aws.amazon.com/ja_jp/eventbridge/latest/userguide/eb-create-event-bus.html)
### ルールの作成
イベントルールを作成します。
このルールにより、イベント内の値をもとに、イベントを処理する対象にすることや処理する内容を変えることが可能です。

カスタムでイベントパターンを定義します。このイベントパターンの定義は、OpsRamp側の設定と合わせる必要があります。
![aws_eventbridge-event_pattern.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2344939/6c17e2de-cb03-8a6e-2729-f6946cf13ccd.png)

その他については、下記を参照し、任意に設定ください。
[ルールの作成](https://docs.aws.amazon.com/ja_jp/eventbridge/latest/userguide/eb-create-rule.html)

## OpsRampの設定
AWS EventBridgeインテグレーションをインストールします。
![opsramp-AWS_EventBridge_integration.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2344939/ee846070-02c0-15b3-85c1-a66a57e00a74.png)

AWS上で設定したAmazon EventBridgeに合わせ、インストールしたAWS EventBridgeインテグレーションを設定します。
![opsramp-AWS_EventBridge_integration2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2344939/63138334-8827-a86e-da3e-90a2e5581de8.png)

[Exports] > [Streaming Export]からStreaming Exportを設定します。
[Export Category Type]に[Alert]、[Stream Export to]に設定したAWS EventBridgeインテグレーションを選択します。
その他については、任意に設定ください。
![opsramp-Streaming_Export.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2344939/3dd8a06e-ac7e-105e-76ec-1958baacd0b4.png)


## 結果
以下のようにAWSにイベントを連携できます。
![aws-CloudWatch_event.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2344939/6d9169d6-161a-42a1-feb0-50fcf4c9e020.png)


---------------------------------------
[<img width = 250 height = 200 src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2344939/111de30a-4d33-aa33-5c7d-83d9015093e6.png">](https://www.vistanet.jp/knowledge-base/)
[Android]OkHttpのタグ機能を活用する

[OkHttp](https://github.com/square/okhttp) では `Request` オブジェクトを作成するときに「タグ」を設定することができます。

このタグについて実用例と共に紹介したいと思います。

また、タグは [Retrofit](https://github.com/square/retrofit) でも設定することができるので、この部分についても紹介します。

# タグを使った認証情報の設定

例えば何らかの Web API で、認証が必要なものとそうでないものがあったとします。

認証情報はヘッダで指定する場合、OkHttp でリクエストするなら以下のように記述できます。

```kotlin
val request = Request.Builder()
    .url("...")
    .addHeader("Authorization", "...") // 認証情報を設定.
    .build()

val response = okHttpClient.newCall(request).execute()
```

このように認証情報が必要なリクエストに対して都度設定しても問題ないですが、タグと Interceptor を組み合わせることによって、認証情報のヘッダの設定を一括で処理させることも可能です。

まずは `Request` オブジェクトを生成するときに、このリクエストでは「認証情報を設定する必要がある」というタグを設定します。

```kotlin
// タグオブジェクト.
// このタグが設定されているリクエストには、Authorization ヘッダが必要。
object RequireAuthorizationTag

val request = Request.Builder()
    .url("xxx")
    .tag(RequireAuthorizationTag::class.java, RequireAuthorizationTag) // タグを設定する.
    .build()

val response = okHttpClient.newCall(request).execute()
```

そして `Interceptor` を継承した `AddAuthorizationHeaderInterceptor` を作成し、ここでタグが設定されているリクエストに対して `Authorization` ヘッダを指定します。

```kotlin
class AddAuthorizationHeaderInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
　　　　　// リクエストに設定されている RequireAuthorizationTag タグを取得する.
        // もしタグが設定されていなければ tag は null となる.
        val tag = request.tag(RequireAuthorizationTag::class.java)
        return if (tag != null) {
            // タグが設定されていた場合、認証情報を設定する.
            chain.proceed(request)
                .newBuilder()
                .header("Authorization", "...")
                .build()
        } else {
            chain.proceed(request)
        }
    }
}
```

最後に作成した `AddAuthorizationHeaderInterceptor` を `OkHttpClient` に設定します。

```kotlin
val okHttpClient = OkHttpClient.Builder()
    .addInterceptor(AddAuthorizationHeaderInterceptor()) // Interceptor を設定.
    .build()
```

# Retrofit でもタグが設定できる

この機能は Retrofit でも使用することができます。

Retrofit でもリクエストごとにタグを設定することができ、その設定は `@Tag` アノテーションによって行います。

```kotlin
interface ApiService {
  
    @GET("...")
    fun getMyMessage(@Tag RequireAuthorizationTag tag): Message
}
```

これで呼び出す時に `RequireAuthorizationTag` を指定すればリクエストにタグが設定される状態となるので、先ほどと同じように `Interceptor` でタグを参照することができます。

```kotlin
apiService.getMyMessage(RequireAuthorizationTag)
```

# 注意点

## 同一の型のタグは2つ以上設定できない

リクエストごとにタグを設定することができますが、OkHttp の `Request.Builder` 内のコードを見るとタグの設定を保持しておくためのオブジェクトは以下のような定義になっています。

```kotlin
internal var tags: MutableMap<Class<*>, Any> = mutableMapOf()
```

キーの部分が `Class<*>` になっているため、**同一の型のタグを2つ以上設定することができません**。

また Retrofit で同一の型のタグが2つ以上設定されている場合、**実行時に例外を投げるので注意してください**。

## 設定するタグの型について

先ほど紹介した例の中では、以下のようにタグ用のオブジェクトを作り、それをタグとして設定しています。

```kotlin
object RequireAuthorizationTag
```

タグには `Boolean` などのプリミティブ型の指定も可能です。

しかし、上記のようにオブジェクトを作った方が意味のある名前にできてコードが見やすくなると私は思っています。

# 最後に

OkHttp のタグ機能を紹介しました。

この記事がどなたかのお役に立てれば幸いです。
ウホッ🦍 いいkintone

[ウホホ ウッホホホ ウホホー 2021 【6ウホホ】](https://qiita.com/advent-calendar/2021/kintone)
[（日本語？訳：kintone Advent Calendar 2021 【6日目】）](https://qiita.com/advent-calendar/2021/kintone)

**ウホウホ、ウホ、ウホホウホウホウッホリウホホホウホ・・・・・・！**

（日本語訳：今回は、ある日Twitterに流れた衝撃的なウッホリカスタマイズ・・・・！）

https://twitter.com/wakabamark6/status/1466606282996326400?s=20

**ホ、ウホホホウホ、ウッホリウホホホウホ！ウホホホ。**

（日本語訳：を、リスペクトした↓↓こういったウッホリカスタマイズをしてみたのでやってみてね！という話です。）

https://twitter.com/juri_don/status/1466702771902902275?s=20


```js
kintone.events.on(
  [
    "portal.show", // ポータルに仕込みたいとき
    "space.portal.show",  // スペースのトップに仕込みたいとき
    "app.record.index.show",
    "app.record.detail.show",
  ],
  () => {
    replaceUho(document.getElementsByTagName("div"));
  }
);

const replaceUho = (elements) => {
  const reg = /です。|でし|ます。|まし|ませ/g;
  for (const el of elements) {
    if (el.childNodes.length === 0 && el.textContent.match(reg)) {
      el.textContent = el.textContent.replace(reg, "ウホッ🦍");
    } else {
      replaceUho(el.childNodes);
    }
  }
};
```

# ウッホ（結果）

**🦍ウホー**
（ビフォー）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/65612bd3-6a7e-d492-6c6d-6299f1a0609f.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/f75747de-e923-3e1a-3ff1-bcf19c878cfd.png)


**🦍ウホホー**
（アフター）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/082146a9-6591-1a0f-2b4b-151217559ff7.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/28999155-43f5-dffb-cee9-260510f7d59c.png)




# ウホホ（まとめ）

**ウッホリウホホホウホ、ウッホリウホホホウホ。**

（日本語訳：ほっこりネタを目指していましたが、ウッホリネタになってしまいました。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/d0bd8f27-8619-f671-0610-0c15836d3a06.png)
Azure Functions が参照している Storage アカウントを確認する

[アプリケーション設定 `AzureWebJobsStorage`](https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-app-settings#azurewebjobsstorage) から辿れるので、ゴニョゴニョすると一覧表示できる。


```bash:shell.sh
#!/bin/bash -l
set -Ceu

ids=`az functionapp list --query '[].id' -o tsv`
for id in $ids; do
  read rg name <<< $(echo $id | awk -F '/' '{print $5, $9}')
  storage=$(az functionapp config appsettings list -g $rg -n $name --query "[?name == 'AzureWebJobsStorage'].value" -o tsv | awk -F ';' '{for(i = 1; i <= NF; i++){printf $i "\n"}}' | grep AccountName | awk -F '=' '{print $2}')
  echo $rg, $name, $storage
done
```

## Usage
```
./shell.sh | column -t
```
PowerAutomate で Apply to each が遅い！

##  **ことのほったん** 

PowerAutomate を使って 1000個 のデータをグルグルまわした処理をするのにかかった時間  **5分**  ！

これはおそすぎる！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/097fb83c-8674-c2bc-30dd-3e379587e87b.png)


##  **解決策を考える** 

###  **その１ コンカレンシー制御をつかう（並列処理）** 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/7f939b42-49d3-8032-8df2-899147397198.png)


これを使うとループ処理を並列で行えます。
50並列にすることで時間は  **35秒**  になりました。

通常はこれで高速化できますが、並列処理できないことってのも意外とあります。
順序性が大事なものなど。

### **その２ 事前にフィルタをかけてデータ数自体を減らす**

可能であれば事前にデータ数を減らしましょう。
なんだかんだこれが一番効果があります。

とはいってもデータ数を減らすために Apply to each を使っちゃうと結局時間かかってしまうので……「アレイのフィルター処理」 というのを使います。

この例だと、データ（レコード）中に "a" を含むレコードだけをとりだして、それに対して Apply to each を行ってます。
データ数が少なくなるのでもちろん早くなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/7b648593-2aad-2051-7f4e-63c47a275179.png)

ポイントとしては**「アレイのフィルター処理」はちゃんと早い**です。なのでもし Apply to each を使う理由が、特定のデータの抽出や重複チェックみたいなものなのであれば、すぐに「アレイのフィルター処理」に書き換えちゃいましょう！


##  **けつろん** 

- Apply to each は遅い。
- コンカレンシー制御を使えば早くなる。
- それよりも配列のデータ数を減らせないか？も考える。データ数を減らすときには「アレイのフィルター処理」を使う
JSTQB社内勉強会についてのあれそれ

CYBIRDエンジニア Advent Calendar 9日目担当の @kazurasaka です。
8日目は @yuki_utsumi さんによる「[数十TBのデータをGoogleドライブにアップしよう](https://qiita.com/yuki_utsumi/private/ab9179b03b388a123893)」でした。
これ手動でやると地獄を見るやつですね。自動化の手法などを詳しく解説していただいたので、面倒なことはシステムに任せて優雅に記事を書きましょう。

## はじめに
技術的な記事が続いていますが、本日は社内勉強会を開催する参考になるようなノウハウ的なやつです。
また、私はエンジニアとして技術部門に所属しつつ、品質管理部も兼務しています。
今回は品質管理部での経験をもとに記事を書きました。

### 前段：JSTQBとは
[JSTQB 認定テスト技術者資格](http://jstqb.jp/)
ISTQB（International Software Testing Qualifications Board）と互換性を持つ
ざっくりいうとJSTQBはテスト技術者の資格かつ国際資格です。
共通言語で海外の有資格者と話が通じるよ！やったね！

全てのベースとなるFLとその上位であるALが、現在日本（JSTQB）で取れるものになります。

### 現在の社内資格保有者
- 品質管理部内で4割
  - そのうち1割が上位資格のALを取得

## 本題：持続可能な勉強会の作り方
開始したのが2021年2月からなので、現在10ヶ月とちょっと、もうすぐ一年に手が届きます。
最初は一対一で始めた勉強会も、現在は品質管理部内の実働部隊ほぼ全員が参加するまでになりました。

### 下準備：参加メンバーの傾向を理解する
勉強会に慣れているのかいないのか、資格取得済みかそうでないか
社内勉強会なので実務の忙しさも考慮に入れます。

JSTQB勉強会はこんな感じ。

- 勉強会不慣れ
- 資格取得は混在（焦点は取得していない人に合わせる）

### 下準備：目的と目標を定める
目標は定量的なもの、目的はそれ以外のものというざっくりした区分です。

JSTQB勉強会はこんな感じ。

- 目標：資格試験の受験者数を増やす（合格までいけたらいいな）
- 目的：シラバスの文面と実務を結びつける

※余談※
シラバスは全てのジャンルへ当てはめるために抽象度が高めなので、言いたいことはわかったけど実際どう使えば？？？？となる率が高い気がしますね。

### 勉強会：定期開催の間隔を決める
週1なのか、隔週なのか。
曜日も決めておきますが、絶対にまもるというよりは目安な感じで。
実務の忙しさに合わせてずらしたりします。

間隔は基本守るようにすると、途中で勉強会が立ち消えることが少ないです。

### 勉強会：定期的に勉強することに慣れる
必要な課題を事前に消化する系は、慣れてないと辛いので無しで。
JSTQB勉強会では事前になにかをやっておく、というものを設けていません。問題は先出ししているので、やりたい人は目を通しておいてね形式。
継続的に知識を入れる習慣をつけることを主題においています。

ここらへんは最初に集まった参加メンバーの傾向によって大きく変わる部分ですね。
JSTQB勉強会以外の別の勉強会なんかも定期的に参加していますが、そちらだと担当部分の事前読み込みとまとめなんかをきっちりやります。

### 勉強会：主催が負担にならない仕組み
詳細な準備とか資料の用意とか、毎週やってるとかなり負担が大きいので、できる限り簡略化しました。
JSTQBは幸いにも問題アプリなどがありますので、そちらを流用しています。
準備はスクショをとって共有フォルダにUPするだけ。だいたい10分あれば終わります。
入れたあとはslackのワークフローをポチーっとしてお知らせ。

### 勉強会：ファシリテーションの心構え
講師、という立ち位置よりはファシリテーションの技法を念頭に置いています。
一方的に説明するより、参加してもらうこと。
複数人だとなるべく均等に話を振ること。
横道にそれても話を膨らませつつ本筋に戻すこと。

理解度に合わせてアドリブで問題追加とか、問いかけとかもやります。
理解することが実感として感じられると、モチベーションも上がりますしね。

### 勉強会：ちょっとした語彙の変換
「わからないことはある？」より「ここまでで自分で説明できない語句はある？」
勉強してるとわかったつもりになるのはめちゃくちゃ多いです。
それの簡単な打開策は「自分が他人に説明できるか」だと思います。
勉強会は講師側も勉強になるとはよく言いますが、まあ、説明するためには理解してないと無理なので。
それを参加者にも適応する形ですね。

### 勉強会：参加の心理的障壁を下げる
否定しない、比べない。これが大前提ですね。
この環境の中では自由に発言していいという空気を作る。
ポジティブは全面的に褒める。ネガティブはサラリと流す。それくらいを心がけています。

## まとめ：持続可能な勉強会の作り方
- 準備が負担にならず
- 参加が負担にならず
- 理解する楽しみが増えていく

そんな状態に持っていければ、ながーく続くものになると思います。

明日のCYBIRDエンジニア Advent Calendar 2021 10日目は、 @shiso_c 「イケメンに絆されて入社したらScrollViewに囚われた話。」
お楽しみに〜！
WAFCharm導入後にRate-based-ruleを追加しようとした

# これはなに？

WAFCharm導入後、AWS Shield Advancedを追加導入するためレートベースルールを同じWebACLに追加を試みた結果です。
元々の構成もTerraformで構築されており、多仕向へのデプロイを想定していたのでTerraformもしくはcliで実行したかったですが上手くいかなかったので備忘録です。

## まず考えたこと
構築済の部分はTerrafomで構築されていたので、Terraformに定義されたWebACLリソースのRuleに追加する案。
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_web_acl#rules
### 問題1
構築後、WAFCharmから配信されるルールはTerraform管理外ではあるが差分として出てくるのでruleを<span style="color: #d61b09">**ignore**</span>していた。
なのでルールをさらに追加しようとするとWAFCharmから配信されるルールを<span style="color: #e04b9e">import</span> or<span style="color: #e04b9e">追記</span>してapplyする必要がある。

### 問題2
今後WAFCharmからルールが配信される度に差分が出るし、<span style="color: #069417">tfファイル</span>がごちゃつくのでナシで。

## 次に考えたこと
Terraformの<span style="color: #2357b5">**local-exec**</span>の機能を用いてaws cliを叩いてルールを追加する案。
https://www.terraform.io/docs/language/resources/provisioners/local-exec.html
### 問題1
まずはaws cliで叩いてどんなもんか試してみようとした。

アップデートに必要な--lock-tokenを取得するコマンド

```
aws wafv2 get-web-acl --name test-webacl --scope CLOUDFRONT --id xxxxxxxxxx-4332-b1e6-3fdae9dfc0d3 --region us-east-1 | jq -r '.LockToken'
```
https://docs.aws.amazon.com/cli/latest/reference/wafv2/get-web-acl.html

・アップデートのコマンド

```
aws wafv2 update-web-acl --name test-webacl --scope CLOUDFRONT --id XXXXXXXXX-b1e6-3fdae9dfc0d3 --default-action=Block={} --rules file://ratebase-rule.json --visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=test-webacl --lock-token 1b53ce78-04a3-4a3b-xxxxxxxxxxx --region us-east-1
```
https://docs.aws.amazon.com/cli/latest/reference/wafv2/update-web-acl.html

・ratebase-rule.json

```ratebase-rule.json
[
  {
    "Name": "ratebase-rule",
    "Priority": 0,
    "Statement": {
      "RateBasedStatement": {
        "Limit": 10000,
        "AggregateKeyType": "FORWARDED_IP",
        "ForwardedIPConfig": {
          "HeaderName": "X-Forwarded-For",
          "FallbackBehavior": "MATCH"
        }
      }
    },
    "Action": {
      "Block": {}
    },
    "VisibilityConfig": {
      "SampledRequestsEnabled": true,
      "CloudWatchMetricsEnabled": true,
      "MetricName": "ratebase-rule"
    }
  }
]
```
しかし、update-web-aclなので--rulesで渡すルールのjsonはWAFCharmで配信されたルールも含めて実行してあげないと、jsonに定義されたルール以外が消えてしまうことに気が付く。。。

## まとめ
・結果的にShieldAdvancedの導入は部分的にコードでデプロイし、ルールの追加はマネジメントコンソールから行いました。

・複数環境へWAFCharmのルールを入れる際はタイミングは少し考えた方が良い。

・WebACLのcliの意外と大変だったので誰かの役に立てば幸いです。
S3のデフォルト暗号化を後から無効にしたかった

# 経緯
・S3バケットをデフォルト暗号化を**有効**にして作成した。
　→ けどやっぱり**無効**にしたい。
 
# 困った事
・**バケット**でデフォルト暗号化を**無効**にしたけど、元々入っていたオブジェクトは暗号化されたままになっていた。
　確かにオブジェクト毎に暗号化設定を無効に切り替えていけば可能。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435702/387ee278-bde0-91b3-6058-bea36f6c686a.png)



# 暗号化を無効にしたかった理由
・オブジェクトだけが暗号化されていても、[ダウンロード時に復号](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/bucket-encryption.html)されるので何ら問題ない。じゃあ何か？
　単に、バケット全体で暗号化が無効になっているのに中に入っているオブジェクトが暗号化されているのは何となく
　**気持ち悪い**と思ったからです。(完全に見栄えの問題ですね　他にも何か問題ありそうであれば教えてください
 
# 解決手順
・暗号化されたオブジェクトがあるディレクトリをこんな感じと想定する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435702/c531b9ae-c3b8-d4b2-46e7-1266821d0ba8.png)



1. バケットのデフォルト暗号化を**無効**にする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435702/4efa0786-99ed-4a2c-adbf-770f91d5e9d5.png)




2.  オブジェクトが入っているディレクトリに対して暗号化を無効にする
  オブジェクト1つ1つに対して、暗号化を設定するのは数にもよるが面倒なので**ディレクトリ**へ無効を設定。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435702/6d252395-0722-d111-7c8d-26e4ca517f07.png)


3.対象のオブジェクトの暗号化が無効化されているか確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435702/fde89815-1543-3171-aa2e-5bccb969daf6.png)


# ポイントと注意点
・手順の**順番**は大事です。「1.バケットのデフォルト暗号化を無効にする」の作業を先にやらないとS3バケット内でPutされたオブジェクトが暗号化される設定となっている為、暗号化を無効にしたオブジェクトが再Putされた時に<span style="color: #d61b09">**再び暗号化**</span>されて何も変化が無いように見えます。

・バケットでデフォルト有効化が無効になっていても、手順「2.」にある画像の様に**無効にする**が選択された状態で開かれるが、<span style="color: #d61b09">**このまま変更の保存**</span>をすることでディレクトリ配下のオブジェクトなどに対し、設定が上書きされる。
Flutter × Firebase ユーザー登録

本投稿は <a href='https://qiita.com/advent-calendar/2021/tecotec' target='_blank'> TECOTEC Advent Calendar 2021 </a>の7日目の記事です。 

こんばんは、次世代デジタル基盤開発事業部（旧ブロックチェーン事業部）の広瀬です。

今回はよく見かけるメールアドレスとパスワードによるユーザー作成機能を実装しました。
ユーザ作成だけだと味気なかったため、コレクション「users」を用意し
こちらにもデータを残す想定で進めます。
※復習もかねて

比較的簡単な実装なので、サクッと進めます。

# 前提条件
Flutter開発環境が構築されていること

# 使用したパッケージ
firebase_auth
cloud_firestore

<!-- more -->

# 本題
それでは早速作成から

```
static final _firebaseAuth = FirebaseAuth.instance;
static final _firestoreInstance = FirebaseFirestore.instance;
static final CollectionReference users = _firestoreInstance.collection("users");

//作成
var result = await _firebaseAuth.createUserWithEmailAndPassword(email: メールアドレス, password: パスワード);
await users.doc(result.user!.uid).set({
        'name' : ユーザ名,
        'self_introduction':自己紹介,
        'create_time':Timestamp.now(),
        'update_time':Timestamp.now()});
```

これでユーザー情報がFirebaseに登録されていれば、完了です。
いきなりソースで驚くかもしれませんが、ユーザ−作成は以上です。
あとのパラメーターはお好みで設定していただければ大丈夫です。

FirebaseFirestore.instanceのcollectionからuidを指定すると取得できます。
ユーザーの更新や削除は下記の通りです。
※更新している情報は例です。

```
static final _firestoreInstance = FirebaseFirestore.instance;
static final CollectionReference users = _firestoreInstance.collection("users");

//更新
await users.doc(uid).update({
        'name' : ユーザ名,
        'self_introduction':自己紹介,
        'update_time':Timestamp.now(),
      });

//削除
await users.doc(uid).delete();
```
ユーザー情報が更新されていれば、完了です。

今回簡単なユーザー機能を紹介しましたが、
ほかにもgoogle_sign_in.dartパッケージを利用したGoogle認証機能や
登録したメールアドレスへメールを送信し、アドレスの確認機能など実装できます。
また機会があれば続きから紹介したいと思います。

以上、Firebaseを利用したユーザーの作成、更新、削除機能の実装でした。

# 感想
いかがだったでしょうか。
今回人生初のプログラムに関するブログを投稿させていただきました。
このブログ用に簡単なＳＮＳアプリを作成し、そのアカウント機能を一部抜粋した内容になっておりますが、Modelなど真面目に分けて作成したため、逆にブログにまとめるため時間がかかってしまいました。

こういう場合は、画面と処理の2ファイルでまとめても良いかもしれませんね。
書き方、量については引き続き考えたいと思います。


 [https://www.tecotec.co.jp/recruit/:embed:cite]
SwiftUIでかんたんにパーティクルを表示する

# 概要

SwiftUIで、以下のような、ボタンを押すとスパークする画面効果を使ってみたいと思います。

![Simulator Screen Recording - iPod touch (7th generation) - 2021-12-06 at 15.18.02.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/687960/4a127334-e4ee-8ba3-d735-bb81b20a0103.gif)

# 手順

今回のパーティクルは`SpriteKit`を使って実現します。

## パーティクルファイルの用意

New Fileより、 `SpriteKit Particle File`を選択し、新しいパーティクルファイルを作成します。
パーティクルファイルには、`Fire`や `Snow` など様々なテンプレートが用意されているので、これだけでもいい感じのアニメーションが実現できます。

![スクリーンショット 2021-12-06 15.20.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/687960/cfd29e5e-2e18-76f7-b4fa-869b91a78fa6.png)


## SwiftUI側の実装

SwiftUIで実装するのは以下です。

- import SpriteKit
- GeometryReaderを通してのSKSceneの作成 
- SpriteViewの配置

### SKSceneの用意
パーティクルを表示させるためのSKSceneを作成します。
まずは先ほど作ったパーティクルファイルを`SKEmitterNode`として読み込み、SKSceneを作成し、addChildで追加を行います。
そのままではParticleのバックグラウンドの色も同時に表示するため、透明にしたい場合はbackgroundColorの設定も必要です。

SKSceneはsizeのプロパティのinitで作成を行う必要があるため、CGSizeが必要になります。

```swift
let emitterNode = SKEmitterNode(fileNamed: "SparkParticle")! //作成したパーティクルのファイル名
let scene = SKScene(size: size)
scene.backgroundColor = .clear
scene.anchorPoint = CGPoint(x: 0.5, y: 0.5) //中央にパーティクルを配置
scene.addChild(emitterNode)
```

### SpriteView
SwiftUIには`SpriteView`というクラスが用意されています。
これはSpriteKitで表現されるUIをSwiftUI上で表示することができるViewになります。
これに、先ほど用意したSKSceneを割り当てます。

前出の通り、SKSceneにはCGSizeが必要になるので、`GeometryReader`を通してサイズを取得し、SKSceneに与えます。

SpriteViewを透明にしたい場合、SpriteViewのoptionに`allowsTransparency`を与える必要があります。

### SwiftUI 完成のコード

```swift:SKSceneView
struct SKSceneView: View {
    let size: CGSize
    
    private var scene: SKScene {
        let emitterNode = SKEmitterNode(fileNamed: "SparkParticle")! //作成したパーティクルのファイル名
        let scene = SKScene(size: size)
        scene.backgroundColor = .clear
        scene.anchorPoint = CGPoint(x: 0.5, y: 0.5) //中央にパーティクルを配置
        scene.addChild(emitterNode)
        return scene
    }
    
    var body: some View {
        SpriteView(scene: scene, options: [.allowsTransparency])
    }
}
```

```swift:ContentView
struct ContentView: View {
    @State
    private var isFire: Bool = false
    
    var body: some View {
        ZStack {
            Button {
                isFire = true
            } label: {
                Text("Fire!")
            }
            
            if isFire {
                GeometryReader { geometry in
                    SKSceneView(size: geometry.size)
                }
            }
        }
    }
}
```

PreviewのRunでも、パーティクルの動作を確認することができます。
![パーティクルのPreview Run](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/687960/a5d4893f-6406-4029-6ef6-60d6b8f5bede.gif)
【microCMS】Nuxt.js用モジュールを使用するときの動的ページの作成

#はじめに
こんにちは。
こちらの記事では、microCMSのNuxt.js用モジュールを使用した際に、動的ページでデータを反映させる方法を記しています。
誤っている点がございましたらコメントいただけると幸いです。

##前提
前回の記事で紹介した、「Nuxt.js用モジュールを使用したAPI通信」を行う前は、`axios通信`でデータの取得を行なっていました。
ですが、モジュールを使用するにあたり、記述方法が変わって動的ページの作成につまづいたので、今回記事を執筆いたします。

https://qiita.com/YuStarrr/items/5769d914c64e21242130


##やりたいこと
axios通信では下記のような記述だったので、モジュールを使用する記述に書き変えたい。

```javascript:pages/_slug.vue
<template>
  <div>
    <pre>{{ sampleData }}</pre>
  </div>
</template>

<script>
export default {
async asyncData({ params, $axios }) {
  const sample = await $axios.get(
    `https://サービス名.microcms.io/api/v1/APIのエンドポイント/${params.slug}`,
    {
      headers: { "X-API-KEY": "APIキーを入力" },
    }
  );
  const sampleData = sample.data;
  return { sampleData };
},
}
```


##実装手順
microCMS公式を参考に進めていきます。
（モジュールのインストール、`nuxt.config.js`の設定、`.env`ファイルの作成は、先ほど紹介した前回の記事に記載したので割愛します。）

>公式：[microCMSのNuxt.js用モジュールを公開しました](https://blog.microcms.io/nuxt-microcms-module/)

###1. pagesの設定
下記のようにエンドポイントまで記述します。

```javascript:pages/_slug.vue
<template>
  <div>
    <pre>{{ sample }}</pre>
  </div>
</template>

<script>
export default {
async asyncData({ $microcms }) {
  const sample = await $microcms.get({
    endpoint: "APIのエンドポイント",
  });
  return { sample };
},
}
```

###2. 動的ページの対応
動的ページなのでasyncDataの引数に`params`をとり、エンドポイントの後ろに`${params.slug}`を付与することで、動的ページのデータも反映することができます。

>参考：[ページディレクトリ](https://nuxtjs.org/docs/directory-structure/pages/)

```javascript:pages/_slug.vue
<template>
  <div>
    <pre>{{ sample }}</pre>
  </div>
</template>

<script>
export default {
async asyncData({ params, $microcms }) {
  const sample = await $microcms.get({
    endpoint: `APIのエンドポイント/${params.slug}`,
  });
  return { sample };
},
}
```


<br>


#おわりに
ここまでmicroCMSのNuxt.js用モジュールを使用した際に、動的ページでデータを反映させる方法についてまとめました。

以上、最後まで読んでいただきありがとうございました！
よければLGTMを押してくれると嬉しいです！