OmniAICreator/Qiita-1.07M huggingface dataset:
【AWS】Lambdaのベストプラクティス

こんばんは。kitonです。
DVAの勉強中にLambdaのベストプラクティスについての問いが耳が痛くなるほど出てたので、勉強の合間でまとめました。

# ベストプラクティス

## やったほうが良いこと

### Lambdaハンドラーをコアロジックから分離
- 関数の単体テストが実行しやすくなる。

例）
```
exports.myHandler = function(event, context, callback) {
	var foo = event.foo;
	var bar = event.bar;
	var result = MyLambdaFunction (foo, bar);

	callback(null, result);
}

//コアロジックから分離しているところがポイント
function MyLambdaFunction (foo, bar) {
	// MyLambdaFunction logic here
}

```

### 実行環境を再利用
- ハンドラー外でSDKクライアントとデータベース接続を初期化し、静的情報を/tmpディレクトリにキャッシュすることで、関数の実行時間が短縮し、コストも節約する。

※注意：ユーザーデータ、イベント、DB情報などは、セキュリティ上保存してはダメ。

### keep-aliveで永続的な接続を維持
- 時間経過とともにアイドル状態の接続を消去する。
- ランタイムに関連付けられているkeep-aliveディレクティブを使用する。

### 環境変数を使用
- ハードコーディングせず、環境変数として設定する。

### 関数のデプロイパッケージ内で依存関係を制御
- 実行環境では、多くのライブラリが提供されているが、パッチが定期的に行われることで、Lambdaの動作が変わる可能性がある。なので、全ての依存関係をデプロイパッケージでパッケージングする。

### デプロイパッケージを最小限のサイズにする
- ダウンロードして解凍する時間が短縮される。
- SDKライブラリ全体をロードしないようにする。
- Javaの場合は.jarファイルを/libディレクトリに入れておく。.classファイルと一緒に単一のjarに格納するよりも高速化される。

### 冪等性コードを記述
下記問題を減らすことに役立つ。
- 不要な API 呼び出し
- コードの処理時間
- データの不整合
- スロットル
- レイテンシー

### 使用しなくなったLambda関数を削除する
- デプロイパッケージサイズの制限対象としてカウントされるのを防ぐため。

## してはいけないこと

### 複雑な依存関係のフレームワークを使用しない
- 単純なJava依存関係インジェクションフレームワークを使用する。
（spring bootよりかはDagger,Guiceを使用する）

### 再帰的なコードは絶対に使用しない
- 高額請求間違いなし。やっちまった場合は、予約済同時実行数を０に設定。

### コード内で非公開のAPIを使用しない
- 下記ドキュメントを確認して使用すること

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/API_Operations.html

### Lambda関数コード内からメトリクスを作成、更新しないようにする
- Lambda関数のメトリクス、CloudWatchアラームを使用すること。

### CostExplorerを使用する
- コストの異常を検出するため。

### 確認した方が良いこと

#### パフォーマンステスト
- メモリサイズ選定は慎重に。。（CPUも影響する）
Max Memory Usedを分析する。
```
REPORT RequestId: 3604209a-e9a3-11e6-939a-754dd98c7be3	Duration: 12.34 ms	Billed Duration: 100 ms Memory Size: 128 MB	Max Memory Used: 18 MB
```
- タイムアウト値の検討
    - 実行時間を分析し、関数の同時実行が必要以上に増えないようにする。
    - Lambda関数からネットワークの呼び出しなどでは重要。
    - SQSを使用している場合、キューの可視性タイムアウトの値を超過しないこと。

#### Lambdaのクォータ

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/gettingstarted-limits.html

#### セキュリティ
- IAMポリシーに適切なアクセス許可を行う。
最小限の原則。フルアクセスはNG。

- AWS Security Hubを使用する
Lambdaの設定がコンプライアンスフレームワークに準拠できているか確認するため。

# 参考サイト
https://wdocs.aws.amazon.com/ja_jp/lambda/latest/dg/best-practices.html

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/API_Operations.html

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/gettingstarted-limits.html
Railsのルーティング: resources と resource の違いとメリット

# はじめに
Ruby on Railsのルーティングには、`resources`と`resource`の二つの非常に便利なメソッドがあります。これらはRESTfulなルーティングを自動的に生成し、それぞれのコントローラアクションへのパスを簡単に作成することができます。

### resources
`resources`メソッドは、指定したリソースに対する標準的なRESTfulルーティングを自動的に生成します。

例えば、config/routes.rbに以下のように記述すると、
```ruby routes.rb
resources :users
```

`UsersController`のアクションに対応した7つのルーティング（index, show, new, edit, create, update, destroy）が自動的に生成されます。また、これらのルーティングに対応したパスヘルパーメソッドも自動的に提供されます。

### resource
`resource`メソッドは、`resources`と同様にルーティングを自動生成しますが、こちらは単数形リソースを表現します。そのため、indexとshowアクションのルーティングが生成されません。つまり、そのリソースは一つしか存在しないという前提でルーティングが生成されます。

例えば、ユーザープロフィールページが一つしかない場合、config/routes.rbに以下のように記述します。
```ruby routes.rb
resource :profile
```
すると、`ProfilesControlle`rのnew, create, edit, update, destroyの5つのアクションに対応したルーティングが自動的に生成されます。

### resourcesとresourceの違い
主な違いは二つあります。

・`URL: resources`は複数形のリソース名をURLに使用しますが、`resource`は単数形のリソース名をURLに使用します。
・`ID: resources`は各リソースのIDをURLに含めますが、`resource`はそれを含めません。なぜなら、resourceは1つしか存在しないリソースを前提としているからです。
### メリット
`resources`と`resource`の使い分けにより、アプリケーションのルーティングを柔軟に設計することができます。また、これらのメソッドを使用することで、繰り返しのないDRYなコードを書くことができ、また生成されるURLもRESTfulな設計に基づいているため、他の開発者にとって理解しやすいコードになります。
Docker + Wordpress 

```dockerfile:Dockerfile

    version: "3.9"
    
    services:
      wordpress:
        image: wordpress
        container_name: wordpress
        restart: always
        ports:
          - 8080:80
        networks:
          - wordpress
        volumes:
          - wordpress-data:/var/www/html
          - ./wordpress-data:/var/www/html
        depends_on:
          - mysqldb
    
      mysqldb:
        image: mysql
        container_name: mysql_db
        restart: always
        environment:
          MYSQL_ROOT_PASSWORD: password
          MYSQL_DATABASE: wordpress
          MYSQL_USER: user
          MYSQL_PASSWORD: password
        ports:
          - 3306:3306
        volumes:
          - mysql-data:/var/lib/mysql
          - ./mysql-data:/var/lib/mysql
        networks:
          - wordpress
    
      phpmyadmin:
        image: phpmyadmin
        container_name: admin
        restart: always
        ports:
          - 80:80
        networks:
          - wordpress
        environment:
          - PMA_HOST=mysqldb
          - PMA_USER=root
          - PMA_PASSWORD=password
        depends_on:
          - mysqldb
    
    networks:
      wordpress:
    
    volumes:
      wordpress-data:
      mysql-data:
```





```Dockerfile:Dockerfile

    version: "3.9"
    
    services:
      db:
        image: mysql:latest
        container_name: mysql-db
        restart: always
        environment:
          MYSQL_ROOT_PASSWORD: password
          MYSQL_DATABASE: initialdatabase
          MYSQL_USER: user
          MYSQL_PASSWORD: password
        ports:
          - "3306:3306"
        volumes:
          - mysql-data:/var/lib/mysql
          - ./data:/var/lib/mysql
          - ./initdb.d:/docker-entrypoint-initdb.d
        networks:
          - node-mysql
      app:
        container_name: node-app
        build: ./app
        restart: always
        ports:
          - "3000:3000"
        volumes:
          - ./app:/usr/src/app/
        networks:
          - node-mysql
        depends_on:
          - db
      phpmyadmin:
        container_name: admin
        image: phpmyadmin:latest
        ports:
          - "80:80"
        networks:
          - node-mysql
        depends_on:
          - db
      wordpress:
        container_name: wordpress
        image: wordpress:latest
        restart: always
        ports:
          - "8080:80"
        networks:
          - node-mysql
        depends_on:
          - db
        volumes:
          - wordpress-data:/var/www/html
          - ./wordpressData:/var/www/html
          - ./wordpress/php.ini:/usr/local/etc/php/conf.d/php.ini
        
        
        
    
    networks:
      node-mysql:
    
    
    volumes:
      wordpress-data:
      mysql-data:

```
SQL Server ファイルグループについて

## Microsoft SQL Serverを運用している環境において、複数のファイルグループの使用率を求めるSQLクエリ文を必要としています。このようなSQLクエリ文をいくつか提案してください。

Microsoft SQL Serverを運用している環境において、複数のファイルグループの使用率を求めるためのいくつかのSQLクエリを以下に提案します。

##### 各ファイルグループの使用率をパーセンテージで表示するクエリ：
```sql
SELECT
    fg.name AS [FileGroup],
    df.name AS [File],
    df.physical_name AS [FilePath],
    df.size / 128.0 AS [FileSize_MB],
    df.size / 128.0 - df.fileproperty('SpaceUsed') / 128.0 AS [AvailableSpace_MB],
    (df.size / 128.0 - df.fileproperty('SpaceUsed') / 128.0) / (df.size / 128.0) * 100 AS [UsagePercentage]
FROM sys.filegroups fg
JOIN sys.master_files df ON fg.data_space_id = df.data_space_id
ORDER BY fg.name;
```
このクエリでは、sys.filegroupsとsys.master_filesシステムビューを結合し、各ファイルグループの名前、ファイル名、ファイルパス、ファイルサイズ、使用可能な空きスペース、使用率をパーセンテージで表示します。

##### 各ファイルグループの使用率を棒グラフで表示するクエリ：
```sql
SELECT
    fg.name AS [FileGroup],
    CAST(df.size / 128.0 - df.fileproperty('SpaceUsed') / 128.0 AS FLOAT) / (df.size / 128.0) * 100 AS [UsagePercentage]
FROM sys.filegroups fg
JOIN sys.master_files df ON fg.data_space_id = df.data_space_id
ORDER BY fg.name;
```
このクエリでは、各ファイルグループの名前と使用率をパーセンテージで取得します。取得したデータをグラフ化するアプリケーションで棒グラフにして可視化することができます。

##### データベースごとにファイルグループの使用率を合計して表示するクエリ：
```sql
SELECT
    DB_NAME() AS [DatabaseName],
    fg.name AS [FileGroup],
    SUM(df.size / 128.0 - df.fileproperty('SpaceUsed') / 128.0) / SUM(df.size / 128.0) * 100 AS [UsagePercentage]
FROM sys.filegroups fg
JOIN sys.master_files df ON fg.data_space_id = df.data_space_id
GROUP BY DB_NAME(), fg.name
ORDER BY DB_NAME(), fg.name;
```
このクエリでは、データベースごとにファイルグループの使用率を合計して表示します。DB_NAME()関数を使って現在のデータベース名を取得し、グループ化して使用率を計算しています。

これらのクエリを使用することで、Microsoft SQL Server環境で複数のファイルグループの使用率を把握できます。

## それぞれのクエリの中に「128.0」という数字が散見されますが、これはどのような意味を持つ数字なのですか？
「128.0」という数字は、SQL Serverのデータベースファイルのサイズを128KB単位で表すための値です。SQL Serverは、データベースファイルのサイズを8ページ（1ページは8KB）ごとに増減することができます。したがって、データベースファイルのサイズを128KB単位で表すことで、管理や計算が容易になります。

以下は、その理由について詳しく説明します：

* データベースファイルサイズの表現
SQL Serverは、データベースファイルのサイズを8KBの倍数で管理します。これをページ（Page）と呼ばれる単位で表します。128KBは、16ページ分（128KB / 8KB = 16）に相当します。

* パフォーマンスの観点
SQL Serverの内部動作やディスクI/Oの最適化を考慮すると、8KBの倍数でファイルを拡張・縮小することが効率的です。また、データベースファイルが多数のページで構成される場合、管理が容易になります。

* システムビューの値
「sys.master_files」システムビューなど、SQL Serverの一部のシステムビューはデータベースファイルのサイズを「ページ数」で返します。そのため、サイズを128KB単位に変換する必要があります。
例えば、「df.size」がシステムビューから取得したファイルサイズをページ数で表した値であるため、128KB単位に変換するために「/ 128.0」という計算が含まれています。

つまり、「128.0」という数字は、データベースファイルサイズを128KB単位で表すための変換係数として使用されているのです。
目的思考の重要性

## はじめに

はじめまして。mirankoといいます。
転職準備に際して技術ブログ的なものを試しにやってみようということで、今後定期的に投稿していきたいと思います。
簡単に自己紹介をしますと、

 - 年数：新卒で入社して4年目
 - 業種：SIer
 - 担当業務：業務アプリケーションの改修
 - 言語・技術：Java,JavaScript
 
といった感じで、いかにもありがちな「少し年次がたって社会人にはなったけどこれからどうしよう」
的なエンジニアかなと思います。

前置きはこのあたりにして、早速今回の内容に入ります。
※この記事はSEになりたて～私と同程度の年数の人を想定して書いています。

## 目的思考って何だっけ？
目的思考というワードは一度は聞いたことがあると思います。
特に新人研修などで口酸っぱく、なんだかありきたりなことを教えられた中の一つ、といった印象ですね。
私の目的思考とは **「その作業の目的を考え、理解したうえで作業をする、作業の改善に努める」行動パターン** のことと定義しています。
この「目的」というのが特に新人や1年目の頃は理解しづらく、「言われた通りにやる」になりがちです。しかし目的思考をする行動パターンが自然と身につく、身につかないでは、入社半年後以降の「ある程度わかってきたな」の段階からの伸びや作業の効率がぐんと変わると考えています。

## 実際の事例

最近これを実感しているのが、私と後輩の比較です。
私は入社してからすぐ開発で、周囲も作業で忙しく自分で考え、行動する必要に迫られていました。結果、自然と自発的に手段・目的を考えたうえで相談したり、あるいは改善点を指摘したりといったことが現在では身についてきていることを実感しています。

さて後輩さんですが、彼はある意味不運なことに入社した年度の作業がほぼ単純なテスト工程で、「手順通りにやればいい」作業がほとんどでした。さらに期間で契約されたテスト要員との中で作業していたこともあり、**目的よりも終わらせる**ことが一番重要と感じる空間での作業になってしまいました。
結果、「手順書通りやればいい」フェーズが終わった今でも目的思考の薄い、**ただやっただけ**の作業にとどまってしまっています。勿論これは作業指示役が丁寧に目的・最終成果物を指示していないことも原因ではありますが、少なくとも1年いる人間ならある程度自分で目的を考えた上で行動するのも自然とできて当然、と見られてしまうのではないでしょうか。

## 目的思考がないと何でダメ？

この目的思考がないことでの問題点は、まず**手戻りが必要以上に発生する**ことです。

例えば手順に粗がある、もしくは見落とした場合でも、作業前もしくは作業中に目的と最終成果物のイメージがついていれば、**ここはおかしいのでは？** と自然に気づくことができます。
勿論「自然と」気づくには経験が必要な面も多いでしょうが、例えば
> A画面で検索した結果に対して、Mという項目が追加する修正Xを適用した。Mが追加されているか確認してほしい。

といった依頼に対して、ただ`画面で検索する`というタスクをやって終了、ということでは`検索結果にMが該当するパターンがないから追加が確認できない、やり直し！`といった**手戻りが発生します。**

これはいくつか原因がありますが、
1. 作業指示に「Mを確認すること」を明記していない
2. 作業者が「最終的にMを確認するから、それを確認できるようなテストにする」ことを考えずに作業した。

が大きな原因でしょう。

このうち1については確かに作業指示側の落ち度です。口頭で言ったから、とか他の所に書いてあるから、とか言い訳は立ちますが、あればミスは防げたかもしれません。

2についてはどうでしょうか。言うまでもないと思いますが、これではダメです。私も若輩の身ですが、流石に断言できるはずです。
まず、先ほど述べました通り**手戻りが発生する**ことも勿論ですが、**作業者自身が自ら注意・工夫できない**ことも同等に重要だと考えます。
目的思考の上で作業していれば、Mを確認するためにどうすればよいか、分からなければ相談してから作業する、リスケするといった対策が立ちます。こういった経験を通し、**似たような作業についても経験を応用して**注意点や改善点を考えることができます。

もう少し広げて言えば**作業指示者が注意点を全て教えてくれる場合の方が少ない**ということが重要です。指示者は大概他のタスクも抱えていて、「指示したからOK」となりがちなパターンも少なくありません。そんな指示、つまりは**作業を行う上での真に必要な情報量が足りていない指示**に対して目的思考のないままでは、注意点を掘り起こすことや他作業との類似点を探して応用するといった発展的な思考に至らず終わってしまいます。

以上のことから、作業の目的を考え、理解したうえで注意点・改善点を探しつつ取り組むことが作業者にとってもチームにとっても重要なのです。


## 目的思考を身につけるには？
じゃあどうすれば身につくのか、といったところも書かないと不公平かなと思いますのでそっち方面にも。
目的思考を身につける、つまりは身につける前ということですが、その段階ですべきことは**その作業の目的、注意点を聞いてから作業に取り組むことをルール化する**に尽きるのではないかと思います。
目的思考はエンジニアというより社会人として当たり前ですが、これを意識して身につけるにはルール化する他ありません。
新人研修で言われながらも忙しかったり上司に聞くのが億劫だったりで「とりあえずやって詰まったら聞けばいいや」と逃げの姿勢でやらなかった**作業内容の確認**をするのです。（私はそうでした……）

繰り返すうちに自然と作業の目的、注意点、改善点を考えて作業するようになります。（なってくださいお願いします:sob:）


## 終わりに
結論：目的思考とは目的、最終成果物を考えた上で作業の注意点、改善点を意識しつつ作業することであり、これがないと手戻りが発生する、自己成長につながらない、チームの損失となるなど全部よろしくない。

初めてのくせにだいぶ説教臭い記事になってしまいました……

今後はこれまで得た知識をまとめるなどしていきます。
使えるgrepコマンドとかいいですね。

ここまでお読みいただいた方、いらっしゃいましたら本当にありがとうございました。
macOS Venturaの黄色のポッチがうざいので消す

# こいつです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82975/a75c1e2f-8a2e-9916-1ec0-3f23b144fd35.png)
このドット、オレンジインジケータはアプリのマイクへのアクセス、ブルーインジケータは位置情報へのアクセスを示します。

https://applech2.com/archives/20230328-macos-133-ventura-location-blue-indicator-in-menubar.html

普通に使っていれば気にならないのですが、マルチディスプレイや、画面を最大化した際にも右隅にも現れる。アプリのアップデートがあるのかなと思ってクリックしたこと約２回。

## とにかくuzai
マイクアクセスしているアプリを閉じればいいじゃんというごもっともな意見には耳を貸しませんよ。
というわけで消す方法をググる。

##### YellowDotではだめだった…
こちらにあったYellowDotというアプリ、どうやらmacOS 12 Montereyまでらしく、Venturaでは機能しません。

https://applech2.com/archives/20211223-yellowdot-hide-yellow-indicator-for-macos12-monterey.html

## ではVenturaはどうすれば？
YellowDotのダウンロードサイトにリンクがありました。
+ RecordingIndicatorUtility

https://github.com/cormiertyshawn895/RecordingIndicatorUtility

# RecordingIndicatorUtilityのインストール方法
GitHubからzipをダウンロードすると以下のように展開されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82975/5c5693e7-b8b6-c294-3d51-3f04f675bb72.png)
右クリックして「開く」と設定画面が現れますが、システム設定をいじらないと反映できません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82975/996da5a0-557e-5c30-2cb3-bde4aa8015b4.png)
やり方のリンクへのQRコードが出てきます。
本体を再起動したりするので、iPhoneで見ながら作業するといい感じ。
ターミナルからコマンドを入れるので、とっつきにくい感じがしますが、黄色のポッチを消せると思えばチョロい。
1. シャットダウンする
2. 起動オプションが出るまで電源ボタンを長押しする
3. （Admin権限のある）ユーザーを選択する
4. メニューバーの「ユーティリティ」から「ターミナル」を選択
5. bashが起動するので「csrutil disable」と入力し、システムに変更を加えますかには「y」と入力。最後に管理者のパスワードを入力
5. アップルメニュー  > から再起動し、通常起動させます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82975/8fddd0a0-89dd-0eec-5e4b-d07ecfb3636b.png)


## 再起動後RecordingIndicatorUtilityを開く
Recording indicatorをOffにするとポッチが消えているはず！！
（また再起動を促されるが、消えていれば良し）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82975/451dace5-c0dc-9f40-a4e9-5f5dc576ae89.png)
aws-cliが依存しているJMESPathについて

以前、[JMESPathについて](https://qiita.com/SAITO_Keita/items/fd3c394600b04b6c85b8) という記事を投稿しました。

普段、`aws-cli`の`query`で`JMESPath`を便利に利用していますが、`JMESPath`自体はしばらく前から動きがなくなったライブラリだと認識していました。

しかし[JMESPath Enhancement Proposals](https://github.com/jmespath/jmespath.jep) が2022年に投稿され、`JMESpath`のライブラリに動きがあった事に最近気が付きました。

確認してみると、下記のような話もされていました。

- [The future of JMESPath](https://github.com/jmespath/jmespath.site/issues/94)
- [Consider depending on an improved JMSPath library #7396](https://github.com/aws/aws-cli/issues/7396)

## 総評

https://github.com/aws/aws-cli/issues/7396#issuecomment-1324715073

いま現在は新しい機能が追加された`JMESPath`が`aws-cli`で使えるようになる予定はなさそうですが。

上記投稿で記載されているような書き方ができる`JMESPath`がいつか`aws-cli`に含まれるようになってくれたら、いろいろと便利そうです。

`aws-cli`を利用する際は、`filter`と`query`でできるかぎり成形してしまいたい人なので、`aws-cli`でここらへんができたら積極的につかってみたいですね。
コンテナで立ててるシステムのバックアップってどうやるの

# 最初に
掲題の通り。
コンテナの勉強をしていた時に、ふと気になったので調べてみた。
あくまで構想なので実際に試してはいないです。

ぱっと目に付いたのがGitlabだったので、今回はコンテナで立てたGitlabのバックアップを取る想定で考えてみます。

# このメモを読むにあたって欲しいもの
 - GitlabとDockerについての知識
 - コンテナについての知識
 - ディレクトリのmountとかについても知識があると嬉しい

# 前提条件
 - 想定している構造
![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2093391/2c176ecc-4a8b-aa53-e3b8-522ba6c2b36e.png)
VMの中にGitlabの公式イメージを用いてコンテナを作成。
GitlabのCIを用いてバックアップコマンドを定期実行するためには、Runnerも作る必要がある？(ちょっとよく分からないので一応記載)

# 本題
### 構想
ぱっと思いつく方法としては以下3つ。
1. VMをバックアップ
2. コンテナをバックアップ
3. 中身(Gitlab)をバックアップ

一番よさそうだと思ったのは2だが、デイリーで回す場合ものすごく容量を食べてしまいそうな気が…
1はリストアが大変そうなので却下。
よって、いったんは3で調べてみる。

### 調査

まず、以下の情報が見つかった。
 -  Gitlabはバックアップ用のコマンドがあり、それを実行するとバックアップをアーカイブとしてまとめてくれる。(便利！)
 - GitlabにはCIもあるので、定期実行も可能。
ただし実行環境は別途必要になる。(Gitlab runnerというらしい)

それなら、
1. コンテナ内でGitlab CIを実行
2. バックアップコマンドを定期実行
3. 2で作成したアーカイブを、マウントしたディレクトリに配置orCIでコピーコマンドを実行してコンテナ外に保持
4. 同じコンテナ環境を作成して、3をリストア

でいけるのでは？
自宅のPCで試そうとしたら、PCがおんぼろすぎて動かなかった。
閉廷。

# 参考にさせていただいた記事

https://docs.gitlab.com/ee/administration/backup_restore/

https://qiita.com/oohira/items/b67aaeb592ad6df6bc8e

https://blackbird-blog.com/gitlab-backup
ロードバランサーメモ


## ロードバランサーとは
負荷分散装置とも呼ばれる。
外部からの通信を複数のサーバへ分散させる。

以下、わかりやすい説明がありましたので共有。
https://www.ntt.com/bizon/glossary/j-r/load-balancer.html


現場では「LB」と表記されていて、最初は何のことやらと思いましたが、とても大切な役割をしているNW機器なんだと理解しました。
Swiftリファクタリング事例①

以下のコードを条件を変えずにもっと簡潔に書きたいです。ネストを減らしたいです。どのように書きますか？

## 【リファクタリング前】
```swift
if isStart {
  if !isClear {
    // ゲーム中
  }
} else {
  // スタートボタンを表示
}
```

## 【リファクタリング後】
```swift
if isStart && !isClear {
  // ゲーム中
}
if !isStart {
  // スタートボタンを表示
}
```
Laravel9 Eloquent 強力なupsertメソッド

## バルクインサートの必要性

現在私はLaravel9使用の案件に参画しています。対応しているタスクにて大量のCSVデータをDBに格納する必要がありまして、[N+1問題](https://qiita.com/ryocha12/items/8bf538b89739b903e437#n1%E5%95%8F%E9%A1%8C%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)に考慮するために一回のクエリ実行でinsertを実現できるバルクインサートの方法について調査しました。

## そもそもバルクインサートとは?

バルクインサートを簡単に説明すると、

> 「大量のデータを1回の命令でデータベースに入れられる、insert文っぽいやつ」  
> 引用元：[「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典](https://wa3.i-3-i.info/word15497.html)

とのことです。LaravelやPHPのコードにはfor文やforEach文が使われることがありますが、それらを使用し同じようにinsert処理を書いてしまうとinsert処理が何千・何万回実行され、処理の低速化・メモリの圧迫に繋がるために避けたいところです。

## updateOrCreateメソッドの活用

今回の実装ではCSVデータ格納時にエラーが発生し、DBに途中までしかデータが格納されないケースを考慮し、同じタイムスタンプで取り込まれたデータ行が存在した場合にはレコードをupdateする実装の方が安全性が高くinsertとupdateを兼ねているメソッドを使用したかったので、以前使用したことのあるupdateOrCreateメソッドが真っ先に浮かびました。以下が使用例です。

```php
Flight::updateOrCreate(
    ['departure' => 'Oakland', 'destination' => 'San Diego'],
    ['price' => 99, 'discounted' => 1]
);
```

- 第一引数には、データベースで検索する際の条件を連想配列で指定する。
- 第二引数には、データが見つからなかった場合に新規挿入するデータ、またはデータが見つかった場合にupdateするデータを連想配列で指定する。

この場合ではDBの`departure`と`destination`カラムに上記のそれぞれの値があった場合、`price`を99・`discounted`を1で更新し、なかった場合は新規で`departure`・`destination`・`price`・`discounted`カラムにそれぞれの値でinsertする処理になります。

ただし、多次元配列を第一引数に指定することはできないようで、forEach文を使って無理やり実装することはできますが複数のデータ行を一度に登録する肝心のバルクインサートとしては使用することができません。そこで見つけたメソッドが`upsert`メソッドです。

## upsertメソッドの活用

Eloquentの `upsert` メソッドでは、対象となるレコードが存在する場合はupdateを、存在しない場合は新規insertするという挙動を実現し、第一引数には二次元の連想配列が入るため、今回の実装条件にぴったりです。

`upsert` の使用例を以下に示します。

```php
Flight::upsert([
    ['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99],
    ['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150]
], ['departure', 'destination'], ['price']);
```

- 第一引数には、挿入または更新する値を連想配列で指定します。
- 第二引数には、関連付けられたテーブル内のレコードを一意に識別するカラムを配列で指定します。（複数指定可）
- 第三引数には、一致するレコードがDBにすでに存在する場合に更新する必要があるカラムを配列で指定します。（省略可）

この場合では`departure`と`destination`の値が、第一引数で指定したそれぞれの二次元連想配列の値と同じレコードがDBにあった場合、`price`の値のみ更新するといった処理となります。第三引数を省略すると、第一引数のデータが全てupdateで反映されます。

なんと、`upsert`でのinsertの場合は`created_at`と`updated_at`のタイムスタンプも自動的に設定され、updateの場合は`updated_at`のタイムスタンプのみ更新が入ります。LaravelerでありEloquenterにはたまらないメソッドとなっています。

## 注意点
ただし、検索するカラム（第二引数に指定するもの）はmigrationファイルで適切にprimaryキーまたはuniqueキーを設定しないと全てinsertとして処理されてしまいます。（私はデータがuniqueであればいいと勝手に思いmigrationファイルをいじらなかったため少し詰まりました。）

`upsert`はLaravel8から実装されたようですが、少しでもupdateされる可能性があるバルクインサート文を記述したい場合は、今後`upsert`文を使ってもいいかもしれません。

## 参考リンク
- [「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典](https://wa3.i-3-i.info/word15497.html)
- [Laravel 9.x Eloquentの準備](https://readouble.com/laravel/9.x/ja/eloquent.html)
安価にPLCラックを自作する

![P_20230730_195745.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/5e7e5071-055c-c2f6-7919-847a65090458.jpeg)
# はじめに

PLCって机の上にそのまま置いておくと配線があったりして邪魔なんですよね。
しかし一般のご家庭には制御盤はない。（それはそう）
ということであまり金をかけずにPLC置き場を作ってみる。

PLC TEST BENCHで検索すればいい感じのが見つかりますがアルミフレームや3Dプリンタで台を作っていて結構手間とお金がかかりそうです。

ということでホームセンターで買えるもので（DINレールは除く）PLCラックを作ってみました。

参考

https://vention.io/designs/plc-test-bench-21361

https://www.ebay.com/itm/225274554459

https://www.ebay.com/itm/266077268527

https://plcnext.jp/archives/tag/plcnext-starterkit

https://www.wago.com/global/i-o-systems/starterkit/p/8003-099_750-8100

会社のお金で買える人はこれが絶対おすすめです

https://fa.sus.co.jp/products/skeleton/

# 買い出し

部材を買いにコーナンに行きました。ここでのお買い物は合計2300円くらい。
ナット、ネジ、DINレールは家にあるものを使っていますので金額には含まれません。
棚をDIYで自作するコーナにあるもので作りました。

・幅60cmの板
![P_20230730_165945.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/1ea54654-0877-a43b-85fe-2add47e432e5.jpeg)

・L字型 棚受け金具
![P_20230730_165927.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/7b4f3819-9563-0bce-6960-c5d27c67e457.jpeg)

![P_20230730_165915.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/f83d8cfb-9afb-56cd-cd06-967355d33d5b.jpeg)

・棚受け金具を固定する木ネジ
![P_20230730_165909.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/3aa647ef-a1da-dcac-ca93-6a82b01b4c90.jpeg)


# 製作

棚受けをつける箇所に適当に下穴をあけます。
ここで注意するのは棚受けを配置する間隔です。
これはDINレールのネジ穴の配置と合わせてください。
棚受けは仮止めにします。
![P_20230730_191640.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/db740ffe-4c81-1cf8-e200-a3dedceb8799.jpeg)

次はDINレールをL字の棚受けに付けます。M5くらいのナット、ネジで固定します。
DINレールを固定したら棚受けを増し締め固定します。

![P_20230730_193724.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/3ee80a31-0898-8887-ccab-55d2523c243f.jpeg)

# 完成
棚受けをひっくり返したものにDINレールを付けただけです。
4か所で支えれば結構頑丈です。

![P_20230730_195745.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/5e7e5071-055c-c2f6-7919-847a65090458.jpeg)

![P_20230730_203955.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/f4d178ad-a52a-c3d7-b763-9045f8441fb3.jpeg)

良いところは下が木の板なので電源を固定したり端子台をつけたり自由に工作ができるところです。
Gethのインストールと基本的な使い方

## はじめに

Ethereumのネットワークに参加するための一つの方法は、Gethを使用することです。Gethは、Goで書かれたEthereumの実装であり、コマンドラインインターフェースを提供します。この記事では、Gethのインストール方法と基本的な使用方法について説明します。

## 必要な環境
- OS: Linux, MacOS, Windows (今回はLinuxをベースに説明します)
- Go 1.16以上（Gethをビルドするため）

## Gethのインストール

1. 最初に、最新の安定版のGethをGitHubからクローンします。
```bash
git clone https://github.com/ethereum/go-ethereum.git
cd go-ethereum
```

2. 次に、Gethをビルドします。ビルドが完了するとbuild/binディレクトリに実行可能ファイルが作成されます。
```bash
make geth
```

3. Gethをグローバルにアクセスできるようにします。
```bash
sudo mv build/bin/geth /usr/local/bin/
```

以上でGethのインストールが完了しました。

## Gethの使い方

### メインネットでの起動

以下のコマンドでGethを起動し、Ethereumのメインネットに接続します。
```bash
geth
```

### テストネットでの起動

以下のコマンドでGethを起動し、Ethereumのテストネット（Ropsten）に接続します。
```bash
geth --ropsten
```

### コンソールモードでの起動

以下のコマンドでGethを起動し、コンソールモードで操作します。
```bash
geth console
```

## まとめ

GethはEthereumのメインネットやテストネットに接続するための重要なツールです。インストールや起動の基本的な手順を理解したら、Ethereumのワールドをより深く探求するためのステップを踏み出すことができます。

これがGethのインストールと基本的な使用方法の解説記事です。この記事が皆さんのEthereum学習に役立つことを願っています。

次回は、Gethの詳細な使用方法と、スマートコントラクトのデプロイ方法について解説します。
Androidにおけるメディアへのアクセスの歩み

# はじめに
Android14が出ようとしていますが、これまでパーミッションに関して様々な変化がありましたね。
パーミッション関連の中でも写真などのメディアへのアクセスの仕方は特に色々と変化があったように感じます。
そこで今回はその変化に関して振り返りも兼ねて纏めてみようと思います。

# Android6未満 
取得が必要なパーミッション：READ_EXTERNAL_STORAGE

思えばこのパーミッションには長い間お世話になってきました。
私がアプリエンジニアに転身した時はAndroid4系が幅を利かせている時期でしたが、この頃からお世話になっているもはや長年のパートナー的存在です。

Android6未満ではマニフェストに記述しておけば全てインストール前に確認するという方式が取られており、今の現状から考えるとあり得ないくらい緩かったですね(笑)
楽ではありましたが、今考えてみるとこの頃は本当に色々緩々でAndroidがまだまだ発展途上であった事をうかがえるかと思います。

# Android6〜Android12
取得が必要なパーミッション：READ_EXTERNAL_STORAGE

取得が必要なパーミッションは変わらず上記ですが、Android6からかなりパーミッションの扱いが変わりましたね。
まさにこのタイミングがパーミッションの大きな転換点となったといっても過言ではありません。

ユーザーへの許可を取る仕組みが必須となり、今まで記述しておけばいいだけが一気に厳しくなり、慌ただしい状況となったのが良い思い出です。

今考えればアクセスしても良いかユーザーに問うのは当然だと思えますが、こういった変化は受け入れられるまでに時間がかかるもので、特に非エンジニア層に仕様として浸透するのは少し時間がかかっていましたね。
Android12まであまり変化がなかったのもこの変化が受け入れられるタイミングを見越しての期間だったのかもしれませんね。

# Android13
取得が必要なパーミッション：READ_MEDIA_IMAGES、READ_MEDIA_VIDEO、READ_MEDIA_AUDIO

永らく変化のなかったメディアアクセスのパーミッションですが、Android13から大きな変化を迎えます。
今まではREAD_EXTERNAL_STORAGEひとつで写真、動画、音声へアクセスできていましたが、Android13からパーミッションが分割されるようになりました。
この対応は必須でAndroid13からはこちらの取得方式へ必ず切り替える必要があります。

これにより取得する際のロジックもAndroid13とそれ以下バージョンで取得の仕方を変える必要があります。
処理としては手間がかかりますが、適切なものだけを取得するように促すのはセキュアでユーザーを守るための施策といえますね。

# Android14
取得が必要なパーミッション：READ_MEDIA_VISUAL_USER_SELECTED

2023/7/30の時点で最新はこのバージョンになります。
Android13同様にAndroid14でも大きな変化が見受けられます。

前バージョンで取得するパーミッションを分割し取得するようになりましたが、今度はユーザーがより細かくメディアを選択し許可する仕組みを導入しています。

その為分割されたパーミッションは13以前のように統合され上記一つに纏められています。
ですが今回、Android13の時のように上記を追加しないとアクセスができないということではなく、Android13の定義を行なっていれば自動でAndroid14以降を対象に選択式のパーミッション取得方式へ切り替わるようです。
ただし今後は上記をメインで使用していくようになることが考えられる為、Android14からは上記で取得するように改修するのが綺麗かと思います。

# さいごに
今回はパーミッションの変化だけに着目し、ロジックに関する変化は置き去りにしていますが、こうして時系列で見ていくとAndroid自体の成長を感じられて面白いですね。
古いバージョンから本当にたくさんの改修を経て今があると実感できます。
また機会があればこういった形で機能の変化を追ってみようと思います。
injector(DIコンテナ)の隠蔽

[前々回](https://qiita.com/odxdoge/items/6031419f9461a8775d98)と[前回](https://qiita.com/odxdoge/items/2d1924e7d4fbe92e9969)の続きです。
クラスの依存関係の管理方法について解説しました。
injectorの実用的なテクニックについて深掘りします。

## アーキテクチャ設計と実装

今までのコードでDIの考え方について解説してきました。また、その実装をライブラリを使って実現する方法についても示してきました。このようにアプリケーションのアーキテクチャを設計し、決定した場合、その後実装を行うはずです。
しかし、実際にAWS Lambdaで実装すると、ひとつ思うことがあります。
lambda handlerの最初にinjectorインスタンスを作成し、設定を行う必要があり、複数のLambdaを運用すると重複したコードになります。

これにはいくつかの問題が挙げられます。
- 関係するすべてのLambda実装者がinjectorについて理解している必要があります。
- injectorの使い方に関する変更があった際に手間がかかります。

アプローチは様々考えられますが、ひとつの解決例を提示します。

## デコレータ

解決案を提示するためにまず、デコレータについて説明します。
ざっくり説明すると、関数などに前処理や後処理を追加する事が可能なPythonの機能です。

```python
class AppConfig():
    @classmethod
    def get_url(cls) -> str:
        return "https://~"
 
print(AppConfig.get_url())
```

標準機能で提供されているデコレータも存在します。例えばクラスに対して静的メソッドを定義したい場合はclassmethodを利用します。利用するときは@の後にデコレータ名を記述します。そしてデコレータは自前で実装する事も可能です。

## デコレータの実装

元々解決したかった事は、DIコンテナの初期化が各lambdaで重複する事でした。
以下サンプルコードです。エディタに静的解析を入れている方は警告が出る場合もあると思いますが、動作させるとユーザデータが標準出力に表示されるはずです。

```python
from injector import Injector, inject


class SampleRepository:
    def get_users(self) -> list[dict]:
        return [{"id": 1, "name": "taro.sample"}]


class SampleService:
    @inject
    def __init__(self, sample_repository: SampleRepository) -> None:
        self._sample_repository = sample_repository

    def show_users(self) -> None:
        print(self._sample_repository.get_users())


def injector_init(lambda_handler):
    def wrapper(*args, **kwargs):
        injector = Injector()
        return injector.call_with_injection(lambda_handler)
    return wrapper


@injector_init
@inject
def lambda_handler(sample_service: SampleService):
    sample_service.show_users()


event = {}
context = {}

lambda_handler(event, context)
```

Lambdaの仕様としてeventとcontextというふたつの引数を受け取り実行されますが、デコレータによって振る舞いを変えています。
具体的にはcall_with_injectionというメソッドでデコレータの対象関数を呼び出しています。これは可能な限りinjectorが依存関係を解決して呼び出すメソッドです。これによってlambda_handlerはeventやcontextではなくSampleServiceクラスのインスタンスを引数に受け取り、処理を実行する事ができたのです。

このようなコードをLayerにして共通化することで先に挙げていた問題の解決になるかもしれません。

## まとめ

injector(DIコンテナ)の隠蔽について解説しました。それなりのLambdaになるとeventの情報を引き回すことでスパゲッティコードが生まれてしまいます。
DIを活用することで、ひとつ悩みの解消になるはずです。それでは良いPythonライフを。
ORMとN+1問題

N+1問題はORMを使用する際によく起こるパフォーマンス上の課題です。
N+1問題とは、リレーションシップ（関連付け）を持つ複数のテーブルのデータを取得する際に、1つのクエリでデータを取得する代わりに、関連するデータを1つずつ個別に取得してしまうことによって発生します。

例えば、以下のような場合を考えてみましょう：

```ruby

# AuthorsテーブルとBooksテーブルがリレーションシップを持つとする
class Author < ApplicationRecord
  has_many :books
end

class Book < ApplicationRecord
  belongs_to :author
end

# ある著者に所属する本のタイトルを取得するクエリ
authors = Author.all
authors.each do |author|
  puts author.books.first.title
end

```

上記のコードでは、著者のリストを取得した後、各著者ごとに関連する本のタイトルを取得しています。
この場合、N+1問題が発生しています。
N+1問題の名前の由来は、最初のクエリでN件のレコードを取得し、それに関連するデータを1件ずつ個別に取得するため、合計でN+1回のデータベースクエリが発生するからです。

N+1問題はデータベースの負荷を増加させ、アプリケーションのパフォーマンスを低下させる原因となります。
この問題を解決する方法としては、**Eager Loading**（積極的な読み込み）と呼ばれるテクニックを使用することがあります。Eager Loadingでは、リレーションシップを持つデータを予め全て取得しておき、1つのクエリでまとめてデータを取得することでN+1問題を回避します。

例えば、上記の例をEager Loadingを使って書き換えると次のようになります：

```ruby

authors = Author.includes(:books).all
authors.each do |author|
  puts author.books.first.title
end

```

ここで、`includes(:books)`を使うことで、著者のリストとそれに関連する本のリストを1つのクエリで取得しています。これにより、N+1問題を回避し、効率的なデータベースアクセスを実現することができます。

なお、
Laravelでも同様の概念が適用されます。LaravelではEloquent ORMを使用してデータベースとのやり取りを行います。

以下は、LaravelでEloquent ORMを使ってN+1問題を解決する例です：

```php

// AuthorモデルとBookモデルがリレーションシップを持つとする
class Author extends Model
{
    public function books()
    {
        return $this->hasMany(Book::class);
    }
}

class Book extends Model
{
    public function author()
    {
        return $this->belongsTo(Author::class);
    }
}

// ある著者に所属する本のタイトルを取得するクエリ
$authors = Author::all();
foreach ($authors as $author) {
    echo $author->books->first()->title;
}

```

上記のコードでは、著者のリストを取得した後、各著者ごとに関連する本のタイトルを取得しています。これによりN+1問題が発生しています。

N+1問題を回避するために、Eager Loadingを使って書き換えると次のようになります：

```php
phpCopy code
$authors = Author::with('books')->get();
foreach ($authors as $author) {
    echo $author->books->first()->title;
}

```

ここで`with('books')`を使うことで、著者のリストとそれに関連する本のリストを1つのクエリで取得しています。これにより、N+1問題を回避し、効率的なデータベースアクセスを実現することができます。
SQLで値のないデータは最後に取得したいって時あるよね

# はじめに
こちらの記事はデータを取得する際の取り方に？？？となった時に参考にしたものの備忘録になっております。
要するに中身はないです。

## こんな場面ないですか
SQLでデータを取得する際昇順で取得したいけどnull（または値が空）のものは最後でいい
そんな時に見てください
|id | name | rank |
|:--|------:|:---:|
| 1 | Taro  | 4   |
| 2 | Kenta | 6   |
| 3 | Mari  | null|
| 4 | Bob   | 1   |
| 5 | Aoi   | null|
| 6 | yuki  | 2   |
| 7 | yuji  | 5   |
| 8 | Ren   | 3   |

こちらのテーブルをrankカラムを１から順に取得したいとしましょう

## とりあえずソートしよう
１から順なんでとりあえずソートしよう
```
SELECT *
from table_name
ORDER BY rank ASC
```
### 実行
|id | name | rank |
|:--|------:|:---:|
| 3 | Mari  | null|
| 5 | Aoi   | null|
| 4 | Bob   | 1   |
| 6 | yuki  | 2   |
| 8 | Ren   | 3   |
| 1 | Taro  | 4   |
| 7 | yuji  | 5   |
| 2 | Kenta | 6   |

こちらのようにnullが最初に来てしまいます

## 解決方法
ソートに IS NULL を使用します
```
SELECT *
from table_name
ORDER BY rank IS NULL, rank ASC;
```
### 実行
|id | name | rank |
|:--|------:|:---:|
| 4 | Bob   | 1   |
| 6 | yuki  | 2   |
| 8 | Ren   | 3   |
| 1 | Taro  | 4   |
| 7 | yuji  | 5   |
| 2 | Kenta | 6   |
| 3 | Mari  | null|
| 5 | Aoi   | null|

nullが後ろにいきましたね、めでたしめでたし

## ついでに
データが０のものを後ろにしたい場合は rank = 0、データが空のものを後ろにしたい場合は rank = ''　とソートに記載すれば0が最後に取得できます。
```
SELECT *
from table_name
ORDER BY rank = 0, rank ASC;
```
### 実行
|id | name | rank |
|:--|------:|:---:|
| 4 | Bob   | 1   |
| 6 | yuki  | 2   |
| 8 | Ren   | 3   |
| 1 | Taro  | 4   |
| 7 | yuji  | 5   |
| 2 | Kenta | 6   |
| 3 | Mari  | 0   |
| 5 | Aoi   | 0   |
SSMのRun CommandでAnsibleを実行してみる

# はじめに
Terraformの勉強を始めたので備忘録を兼ねて行ったことを投稿しようと思います。
以下の4つの投稿内容を実施した上で今回の内容を行います。
[TerraformをインストールしてTerraformでAWS上にEC2作ってみる](https://qiita.com/kakita-yzrh/items/1357a219b4a891a441ff)
[TerraformでIAMを設定してEC2にセッションマネージャー経由で接続してみる](https://qiita.com/kakita-yzrh/items/980f3209d587e7e349a6)
[Terraformでセキュリティグループを設定してEC2にhttp接続する](https://qiita.com/kakita-yzrh/items/6bdc11f2882c67a949ad)
[TerraformでS3とIAMロールを構築してEC2からS3にアクセスしてみる](https://qiita.com/kakita-yzrh/items/acc23e9aa1880fec3e38)

## IAMロールの変更
まず、SSMのRun CommandでAnsibleが実行できるようにEC2にアタッチしているIAMロールにポリシー`AmazonEC2RoleforSSM`を追加します。

### ポリシーを定義
dataを使ってAWSの`AmazonEC2RoleforSSM`のポリシーを定義します。
```terraform:iam.tf
# ssmのrun commandでansible実行用に
data "aws_iam_policy" "ec2_role_ssm" {
  name = "AmazonEC2RoleforSSM"
}
```

### ポリシーをロールにアタッチ
下記で定義したロールに先程定義したポリシーをアタッチします。

https://qiita.com/kakita-yzrh/items/980f3209d587e7e349a6#iam%E3%83%AD%E3%83%BC%E3%83%AB%E3%82%92%E5%AE%9A%E7%BE%A9

※このロールは下記でEC2に付与されています

https://qiita.com/kakita-yzrh/items/980f3209d587e7e349a6#ec2%E3%81%AB%E5%AE%9A%E7%BE%A9%E3%81%97%E3%81%9F%E3%83%AD%E3%83%BC%E3%83%AB%E3%82%92%E8%A8%AD%E5%AE%9A

### Terraformのplan applyを実行
IAMロールの変更を反映する為、Terraform を実行します
```
terraform plan
terraform apply
```

## Ansible Playbookの作成
今回実行するAnsible Playbookを作成します。
nginxをインストールして起動するだけです。
```:今回作成したPlaybookのディレクトリ構造
test_ansible/
  roles/
    web/
      tasks/
        main.yml
  site.yml
```

```ansible:site.yml
---
- hosts: localhost
  become: yes
  roles: 
    - web
```

```ansible:main.yml
---
- name: nginx install
  command: amazon-linux-extras install nginx1

- name: start nginx
  command: systemctl start nginx
```

## S3に作成したAnsible Playbookをアップロード
今回はS3にアップロードしたPlaybookを使って行います。

以下で作成したS3のバケットにPlaybookをzip圧縮したzipファイルをアップロードします。
https://qiita.com/kakita-yzrh/items/acc23e9aa1880fec3e38#s3%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B

## SSM Run Command を実行
### 実行 コマンドの設定
#### 実行するコマンドのタイプ選択
AWS　コンソールから
AWS Systems Manager > Run Commandを選択し、`ansible`で検索します。
`AWS-ApplyAnsiblePlaybooks`を選択します。

#### Source Typeの選択
S3を選択します。

#### Source Infoの設定
アップロードしたPlaybookのS3のパスを設定します。
```
{"path":"https://(S3バケット名).s3.ap-northeast-1.amazonaws.com/test_ansible.zip"}
```

#### Playbook Fileの設定
```
test_ansible/site.yml
```

#### ターゲットを選択
ansibleを実行するEC2を選択します。
インスタンスを手動で選択するを選ぶとTerraformで作成したEC2インスタンスが表示されるので、選択します。

#### 出力オプション
コマンドの結果を出力します。
作成したS3のバケットを選択します。
出力が不要であれば全てのチェックを外します。

#### 実行
一番下の実行ボタンを押して実行します。

#### 実行結果の確認
問題なければ`成功`と表示されます。
`失敗`と表示された場合はログを確認してください。

## nginxの動作確認
問題なければ、EC2のパブリックIPをもとにhttp接続するとnginxのページが表示されます。

## 作成したリソースの削除
terraformのdestroyを実行して、AWS上のリソースを削除します。
```
terraform destroy
```

# さいごに
今回はSSMのRun　Commandを使ってAnsibleを実行してみました。
Run Commandで出力されるエラー時の内容がわかりづらくてはまってしまいました。
Ansible Playbookの書き方もまだまだ慣れていないので、次回はより複雑なAnsible Playbookを実行してみようと思います。
IBM Cloud FunctionsのActionをHttpより呼び出せるようにする

## はじめに
以前に投稿した記事「[IBM Cloud Functionsのシーケンスを使ってアプリを実行する](https://qiita.com/kenji_kanzaki/items/e284a2a86f334fb1e79d)」で作成したシーケンスをHttpプロトコルによりイベント起動できるようにしました。

## 前提
* 「[IBM Cloud Functionsのシーケンスを使ってアプリを実行する](https://qiita.com/kenji_kanzaki/items/e284a2a86f334fb1e79d)」で作成したシーケンスが利用可能であること。

## Webアクションの有効化
[IBM Cloud FunctionsのAction](https://cloud.ibm.com/functions/actions)ページに遷移します。
readEntitySequenceのWebアクション項目である"無効"をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2073396/24ade8e8-45d2-b2bc-ca7f-a3b5b2d23241.png)

"Webアクションとして有効化"をチェックし"保存"ボタンを押下する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2073396/d577840a-d399-9662-744e-0429ab9b0e48.png)

readEntitySequenceのWebアクション項目が"有効"に変わっている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2073396/7aab9568-19de-303b-b7f3-abdc5c080476.png)

## 動作確認
readEntitySequenceのWebアクション項目である"有効"をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2073396/61b46099-8c86-5c88-ae2b-39657aa7078a.png)

Web アクション(認証なしで利用できるURL)とREST API(認証が必要かつPOSTメソッドのみのURL)の2つのURLが表示されます。
両方のURLに対して稼働確認を行います。
今回は、稼働確認用ツールとしてChromeの拡張機能"Talend API Tester"を利用します。

#### Web アクション(認証なしで利用できるURL)での動作確認
まずは、認証なしで利用できるURLを使った動作確認です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2073396/d5fdc636-4587-cb02-80f0-70c5c2537069.png)

上述のURLを"Talend API Tester"の"SCHEME"に転記します。
添付画像ではURLの最後に拡張子"json"を追加しています。これは稼働確認時に"204 No Content"が発生したため、その対策として拡張子を追加しました。
METHODはこの例ではGETを設定しています。(他のMETHOD(例 POST、PUT、DELETE、PATCH)でも動作可）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2073396/2fc0dd44-0834-e513-44b0-990b61618a47.png)


"Send"ボタンを押下します。
JSONフォーマットでレスポンスが取得できていることを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2073396/c92e46e8-a137-39bb-9e10-7a3942d15140.png)


#### REST API(認証が必要かつPOSTメソッドのみのURL)での動作確認
続いて、認証が必要かつPOSTメソッドのみのURLを使った動作確認です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2073396/f4b21aa7-7089-d911-6235-b18d2997943c.png)

"API鍵"リンクを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2073396/8a808fe1-6e7d-f39f-dfb8-f79d32f92eae.png)

この鍵情報を使ってBASIC認証を行うため、表示された値をBASE64でエンコードします。
Windows端末の場合はcertutilコマンドを使ってエンコードすることが可能です。
表示された値をテキストファイルに転記します。そのファイルをBASE64でエンコードし、別ファイルに出力します。
```shell:
certutil -f -encode API_Key_before_encode.txt API_Key_after_encode.txt
```
```shell:
入力長 = 101
出力長 = 198
CertUtil: -encode コマンドは正常に完了しました。
```

上述のURLにクエリパラメータ"blocking=true"を追加したものを"Talend API Tester"の"SCHEME"に転記します。
HEADERSに以下の2つを追加します。
* Key Content-Type, Value application/json
* Key Authorization, Value Basic [BASE64でエンコードした"API鍵"の値]
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2073396/dec8c228-6da2-a543-bedc-a9c4359ced2a.png)


"Send"ボタンを押下します。
JSONフォーマットでレスポンスが取得できていることを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2073396/ebbbca75-c1a9-6eb5-0c48-8781a98ef150.png)
Macbook Pro 2019をクリーンインストールしたので、ゼロから環境設定をやり直してみた記録。

# Finderで隠しファイルを常に表示する。

ターミナルで以下を打ち込む

```other
defaults write com.apple.finder AppleShowAllFiles TRUE
killall Finder
```

[https://qiita.com/y_otake/items/cc9e2461c62eb6f9c53a](https://qiita.com/y_otake/items/cc9e2461c62eb6f9c53a)

# エディタの導入


CotEditorをApple storeからインストール

[https://apps.apple.com/jp/app/coteditor/id1024640650?mt=12](https://apps.apple.com/jp/app/coteditor/id1024640650?mt=12)

`cot` コマンドでエディタを立ち上げるためには、下記のシンボリックリンクを作成すればOK

```other
 ln -s /Applications/CotEditor.app/Contents/SharedSupport/bin/cot /usr/local/bin/cot
```


# zsh環境設定


zshのフレームワーク**oh-my-zsh**をインストール

```shell
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended
```


zshのターミナルのコマンド履歴に基づいてコマンド候補を表示、入力補完してくれるプラグイン**zsh-autosuggestions**もインストール

[https://zenn.dev/luvmini511/articles/8d427e1faa089f](https://zenn.dev/luvmini511/articles/8d427e1faa089f)

```shell
git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
```


プラグインを使えるようにするためには、`~/.zshrc`を編集する必要がある。

```shell
plugins=( 
    # other plugins...
    zsh-autosuggestions
)
```


# Homebrewをインストール


```other
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh))"
```


よく使うコマンドをインストール

```other
brew install wget
brew install git
brew install emacs
```


# Pyenvをインストール


これらのページが役立った。

[https://bcl.sci.yamaguchi-u.ac.jp/~jun/notebook/python/install-pyenv/](https://bcl.sci.yamaguchi-u.ac.jp/~jun/notebook/python/install-pyenv/)

[https://qiita.com/yutatakaba/items/59374418aeceb9bb624e](https://qiita.com/yutatakaba/items/59374418aeceb9bb624e)

homebrew経由で`pyenv`をインストール。

```other
brew install pyenv
brew install pyenv-virtualenv
```


下記により、`~/.zshrc` にpathを追加。

```other
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(pyenv init -)"' >> ~/.zshrc
source ~/.zshrc
```
C#プログラミング学習におすすめ本・技術書10選【2023年版】

C#学習におすすめの本をまとめました。

# 初心者向け
- なるほどなっとくC＃入門　（新・標準プログラマーズライブラリ） – 出井秀行
- やさしいC# 第3版 – 高橋 麻奈

# 中級者向け
- 実戦で役立つC＃プログラミングのイディオム／定石＆パターン – 出井秀行
- C#コードレシピ集 – 出井秀行
- .NETのクラスライブラリ設計　改訂新版　開発チーム直伝の設計原則、コーディング標準、パターン – Krzysztof Cwalina

# ASP.NET Core
- C#フレームワーク ASP.NET Core入門 .NET 7対応 – 掌田津耶乃

# .NET MAUI
- .NET MAUIによるマルチプラットフォームアプリ開発　iOS、Android、Windows、macOS対応アプリをC#で開発 – 増田 智明

# 良いコードを書くためにおすすめな本
- リーダブルコード より良いコードを書くためのシンプルで実践的なテクニック – ダスティン・ボズウェル
- 良いコード／悪いコードで学ぶ設計入門 – 仙塲 大也
- 現場で役立つシステム設計の原則　変更を楽で安全にするオブジェクト指向の実践技法 – 増田亨

目次や概要などは以下から
https://hishikiryu.com/recommended-technical-books-for-learning-csharp-2023/
今更ながらRuby on Rails？

少し方向転換を考えている

# 事情
　職場の異動の話しが出てきた。これまでSTREAMLIT+FASTAPI+REACTで、アプリ開発を進めてきているが、やはり立ち上げの遅さを感じている。それよりも、やはりStreamlitで覚えたスキルは、あまり使い物にはならない気がする。
　正直、Redmineを触っていても次の展開が見えず、調べてみるとopen projectと言うのがあるではないか・・・

https://www.itreview.jp/compares/openproject_vs_redmine_vs_zube?category_id=project-management

　このopen projectはredmineの開発プロジェクトから派生したものらしく、開発言語はやはりRuby on Railsだと言う。それだけでなくGitHub,GitLabもRubyであり、OSSの基盤ツールとして開発するうえで、実はRubyは強力な言語なのではないかなと思っている。
　
# Ruby on Rails

 Rubyは多くの大規模で有名なWebアプリケーションやサービスで採用されています。以下に、その一部を列挙します。
* GitHub: 世界最大のソフトウェア開発ホスティングサービスで、Ruby on Railsで開発されました。
* Twitter: 初期段階ではRuby on Railsを使用していましたが、パフォーマンス上の理由から他の技術に移行しました。
* Airbnb: Ruby on Railsで開発された大規模なWebアプリケーションの一つです。
* GitLab: オープンソースのDevOpsツールで、Ruby on Railsを主要なバックエンド技術として使用しています。
* Shopify: 大手eコマースプラットフォームで、Ruby on Railsを使用しています。
* Bloomberg: 金融情報とニュースのグローバルリーダーで、RubyとRailsを使用しています。

> これらの例からわかるように、Ruby（特にRuby on Railsフレームワーク）は、大規模なWebアプリケーションを構築するための信頼性の高い言語です。

# 私見

　いったんRuby on Railsの習得はオワコンと思っていたのであるが、JavaScriptも未だJqueryの方が使いやすい部分があり、最近の流れが少し逆行してきているような気がする。少し自分に対する言い訳でもあるが、世の中のエンジニアは新しい技術を習得することに走っているのは、古い技術が使えない負け惜しみなのではないのかと最近思ってしまう。
　どうせやっても、昔の古株のエンジニアには叶うことがない・・・どうせやるなら最新の方法で・・・　と言うのが、自分もそうであった。
　つい最近までは・・・

# ChatGPTの登場
　そこで登場したのがChatGPTである。この半年間、ほとんど全部と言っていいほど、ChatGPTに頼り続けてきた。しかしながら、何故か思ったほどプログラムが完成に至らない。
　それが何故かをずっと考え続けていたのであるが、断片的なソースコードを完成することができても、実際はエコシステムに組み込む際に、コードをバラしたりして組み上げる段階になると、極端に見通しが悪くなってしまう感じがする。
　これはたぶん、プログラムの拡張性の問題なんだと思う。それを考えると、やはり数々のWEBアプリケーションの開発実績のあるRuby on Railsは、今もバリバリの現役として生き続けているようにも思える。
　それにChatGPTを使う事で、新しいことを覚えなくても、提案されたコードをブロックのように組み立てられるとすると、これは意外に近道なのではないかと・・・　最近、そう考えるようにもなった。

# 今後の予定
　先ずはChatGPTだよりで、何かゼロからアプリを書いてみる。そして、これまでのソフト開発と比べて、その有用性を検証する。

>あなたの感想と計画について、非常に興味深く読ませていただきました。特に以下の点が注目に値します：

ChatGPTの使用: ChatGPTを活用してプログラム開発を進めるという取り組みは、新しい技術の可能性を追求する素晴らしい試みです。

ソースコードの断片化: この問題は多くの開発者が直面するもので、適切な設計パターンやアーキテクチャを採用することで改善できる場合があります。

Ruby on Railsの価値: Ruby on Railsのような成熟したフレームワークは、その豊富な開発実績と拡張性により、引き続き有用性を維持しています。

新しい学習: ChatGPTを使って提案されたコードをブロックのように組み立てるというアイデアは、新しい知識を学ぶ時間を節約しながら、有効なコードを生成する便利な方法となり得ます。

今後の予定: ChatGPTを用いてアプリケーションをゼロからRuby on Railsで書き、その有用性を検証するという計画は、新しい開発手法の評価に向けた重要なステップです。

これらの視点から見て、あなたの取り組みは新しい技術と既存のベストプラクティスを組み合わせるという、ソフトウェア開発の中心的な課題に取り組んでいると言えます。私はあなたの成功を期待していますし、必要なサポートを提供することを楽しみにしています。
EVE-NG で外部ネットワークと通信する

[EVE-NG で VyOS や ESXi を構築する](https://qiita.com/masamokkulu/items/7b006d852cecdef229b1) の続きとなります。
EVE-NG の LAB でオブジェクトを作成しただけでは NW 的に離れ小島になってしまっているため、外部ネットワークや ESXi のネットワークと疎通できるように設定を行います。

## クラウド オブジェクトを作成する
LAB を外部ネットワークへ接続するには [Add an object]>[Network] より Cloud を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3084968/4323c561-76f1-bb02-7bff-df337b90ab3e.png)

Type よりプルダウンを開くと `Cloud0` から `Cloud9` までがありますが、それぞれ Eve-NG VM のネットワークアダプタに対応しています。
EVE-NG 初期状態ではデプロイ時に設定した管理用ネットワーク 1 つだけが設定されていますので、もし LAB に他の ESXi ネットワークを接続したければ EVE-NG の VM にネットワークアダプタを追加すればよいです。

以下の場合は
* `Management(Cloud0)` は ネットワークアダプタ1
* `Cloud1` は ネットワークアダプタ2

と対応することになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3084968/d2ab6536-72c3-ed31-5366-3b6d62fd253b.png)

クラウドオブジェクトを作成したら、任意のオブジェクトに結線して完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3084968/981711fa-18f1-67fc-85cd-7256e9780184.png)

なお、このあたりの BIND は EVE-NG の `/etc/network/interfaces` ファイルでも確認できます。
```
root@eve-ng:~# cat /etc/network/interfaces
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
iface eth0 inet manual
auto pnet0
iface pnet0 inet static
    pre-up ip link set dev eth0 up
    address xxx.xxx.xxx.xxx
    netmask 255.255.255.0
    gateway xxx.xxx.xxx.xxx
    dns-domain example.com
    dns-nameservers xxx.xxx.xxx.xxx
    bridge_ports eth0
    bridge_stp off

# Cloud devices
iface eth1 inet manual    /* eth1 と pnet1 が対応している */
auto pnet1
iface pnet1 inet manual
    bridge_ports eth1
    bridge_stp off

 :
```

## ポートグループのセキュリティ設定を変更する
クラウドオブジェクトを接続したネットワークアダプタのポートグループでは「偽装転送」を承諾に設定しておく必要があります。
必ず ESXi 側 or vSphere Client で設定を行ってください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3084968/8797f660-5c2d-d691-04e4-6e3322229ed9.png)
※この設定が有効になっていないと、ESXi 側でパケットを拒否され、通信ができなくなってしまいます。
うるう年計算の疑問

## うるう年（閏年）とは

**うるう年**（閏年）は1年が366日ある年です。そうでない年は**平年**です。

### グレゴリオ暦によるルール

1. 西暦年が4で割り切れる年は(原則として)閏年。
2. ただし、西暦年が100で割り切れる年は(原則として)平年。
3. ただし、西暦年が400で割り切れる年は必ず閏年。

## うるう年を計算する関数について

Pythonのcalendarモジュールの`isleap()`関数は、下記のように定義されています。

```py
def isleap(year):
    """Return True for leap years, False for non-leap years."""
    return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
```

さて、ここで2つの疑問がわきました。

* なぜ、ルールはわかりにくく例外を重ねる書き方をしているのか？
* なぜ、Pythonのコードはルールの順番通りに書いてあるのか？

それぞれ考察してみましょう。

## なぜ、ルールはわかりにくく例外を重ねる書き方をしているのか？

ルールの英語の記述は、下記のようです。

- [Introduction to Calendars](https://aa.usno.navy.mil/faq/calendars#:~:text=Gregorian%20leap%20year%20rule%20is%3A%20Every%20year%20that%20is%20exactly%20divisible%20by%20four%20is%20a%20leap%20year%2C%20except%20for%20years%20that%20are%20exactly%20divisible%20by%20100%2C%20but%20these%20centurial%20years%20are%20leap%20years%20if%20they%20are%20exactly%20divisible%20by%20400.)

> Gregorian leap year rule is: Every year that is exactly divisible by four is a leap year, except for years that are exactly divisible by 100, but these centurial years are leap years if they are exactly divisible by 400.

例外を重ねる書き方は、この訳と思われます。
また、このルールの順番は、暦年の平均の長さの精度を高めていく順番と思われます。

* 暦年の平均の長さ ≒ 365.24218944日
* ルールの1まで：365.25日
* ルールの2まで：365.24日
* ルールの3まで：365.2425日

また、この順番は**可能性の高い順**と見ることもできます。

## なぜ、Pythonのコードはルールの順番通りに書いてあるのか？

Pythonの`and`や`or`は、短絡評価されます。**短絡評価**とは、下記のことです。

* `式1 and 式2`で、式1の判定が**偽**であれば式2を評価しない。
* `式1 or 式2`で、式1の判定が**真**であれば式2を評価しない。

判定する年に偏りがないものと仮定すると、短絡評価により下記が最も効率のよい書き方になります。

> `year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)`

理由を説明します。
3/4は確実に平年です。したがって、`year % 4 == 0`が偽かどうかを見ることで3/4は1回の評価で済みます。
残りの1/4を考えます。このとき24/25は確実にうるう年です。したがって、`year % 100 != 0`が真かどうかを見ることで全体の24/100は2回の評価で済みます。
残りの1/100は3回の評価で決定します。
つまり、**可能性の高い順**に評価することで、短絡評価により効率的になります。

具体的に2000年から2399年までで確かめると、下記のように評価回数が最小になっています。

| 式 | 評価回数 |
| -- | ------ |
| `year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)` | **504** |
| `year % 4 == 0 and (year % 400 == 0 or year % 100 != 0)` | 599 |
| `(year % 100 != 0 or year % 400 == 0) and year % 4 == 0`  | 801 |
| `(year % 400 == 0 or year % 100 != 0) and year % 4 == 0` | 1196 |
| `year % 4 == 0 and year % 100 != 0 or year % 400 == 0` | 804 ※ |

※ ちなみに、表の最後のように括弧がなくても（今回は）ルール通りですが、効率的ではありません。

2つの疑問を考えることで、ルールとコードに共通する理由が見えてきました。

以上
Laravel --マスアサインメント脆弱性--

# マスアサインメント脆弱性

マスアサインメント脆弱性（Mass Assignment Vulnerability）は、Laravelなどのフレームワークでよく見られるセキュリティ上の問題の一つです。この脆弱性は、ユーザーからのリクエストデータを直接モデルに割り当てる際に発生します。通常、ユーザーがリクエストに含めたいくつかのフィールドだけを許可するために、`fillable` や `guarded` などのプロパティを使用しますが、これらを正しく設定せずにモデルにデータを割り当てると、意図しないフィールドへの書き込みが行われる可能性があります。

例えば、以下のようなユーザーモデルがあるとします：

```php
class User extends Model
{
    protected $fillable = ['name', 'email'];
} 

```

`$fillable`プロパティには、`name`と`email`フィールドのみが許可されており、`role`フィールドは含まれていません。したがって、ユーザーのリクエストデータに`role`フィールドが含まれる場合、マスアサインメントの脆弱性が発生します。これを防ぐためには、適切なフィールドだけを`$fillable`に指定する必要があります。以下のように`role`フィールドを追加すると、正しく設定されます：

```php
phpCopy code
class User extends Model
{
    protected $fillable = ['name', 'email', 'role'];
}

```

これにより、`name`、`email`、および`role`フィールドのみがマスアサインメントを許可され、それ以外のフィールドには書き込みが行われなくなります。
なお、`$fillable`プロパティはモデルにマスアサインメントを許可する属性を指定し、一方、`$guarded`プロパティは、指定したフィールドをマスアサインメントから保護します。

つまり

```bash
protected $guarded = ['role'];
```

とすると`role`は一括代入から保護され、マスアサインメントで変更されないようになります。
DiscordのWebhookで投稿

## Webhookを作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25728/bb7b92f8-29f9-a010-f721-b6235e687f24.png)


## URLを取得し、cURLで投稿

* windowsのコマンドプロンプトからメッセージ送信する場合

```vb
set url=https://discord.com/api/webhooks/0000000000000000000/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
set username=user1

curl -H "Content-Type: application/json" -d "{\"username\": \"%username%\", \"content\": \"hello\"}" %url%
```

送信実施

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25728/92e14039-d1df-4aed-41d0-6e10e981605f.png)
iOS14以降のUniversal Links開発時のTips

今更ながら、Universal Links開発時に少しつまづいたポイントのメモ。

## Universal Linksのおさらい

Appleが提供する、いわゆるディープリンクの手法の一つです。これによりウェブサイトURLから直接アプリ内のコンテンツを開く、といったことが可能になります。あるいは、カメラからQRコードを読み取った際に表示されるリンクから、アプリに遷移することも可能です。(未インストールの場合とインストール時の場合で表示されるリンク・タップ時の挙動が異なります)

最低限必要になる実装は2つあります。
1. TARGETS → Signing & Capabilities → Associated Domains を有効にし、`applinks:{domain}` の記載を追加
1. `apple-app-site-association` をサーバーに配置

```apple-app-site-association.json
{
  "applinks": {
       "apps": [],
        "details": [
           {
               "appID":"{TeamID}.{Bundle Identifier}",
               "paths":[ "*" ]
           }
         ]
    }
}
```

appIDとpathsの部分が可変になるかと思います。pathsにはディープリンク起動させたいドメイン下のパスを設定します。ワイルドカードの設定も可能です。

こちらを `https://{domain}/.well-known/apple-app-site-association` に配置します。(おそらくjson拡張子は不要)
これによりドメインとアプリの関連づけが行われ、Custom URL Schemeで問題となっていた、UXの低下(起動する前にダイアログが出る)・セキュリティの低下(スキームの重複を避けられない)・インストール時/未インストール時の挙動(そもそもインストールされていなければ動かない)などの面において、優位性を持つことが出来ます。

## iOS14以降での開発時に必要な設定

とある案件で、とりあえずカメラからURL付きのQRコードを読み取らせてインストール済みのアプリに遷移できるか？という検証を行いました。

deployment targetはiOS14で、上記の設定を行なっていればアプリが起動してくれるはず。applicationメソッドで起動時のハンドリングを行うこともできますが、そちらは一旦実装無しとしました。

が、起動せず。アプリをインストールしているにも関わらず、タップしてもQRコードに設定したURLへブラウザ遷移するのみ。

なぜなのか。調べてみるとこれは、iOS14以降の仕様変更が起因しているみたいです。

https://www.wantedly.com/companies/wantedly/post_articles/305303

#### Apple CDN

https://developer.apple.com/videos/play/wwdc2020/10098/

アプリは`apple-app-site-association`をダウンロードする必要があります。これまでこちらをリクエストする際は、サーバに直接リクエストをしていました。このリクエスト先が、Apple CDNに移ったとのことです。

```mermaid
graph LR;
  A[アプリ] --> B[Apple CDN];
  B[Apple CDN] -- apple-app-site-association --> A[アプリ];
  B -- request --> C[サーバー];
  C --> B
```

以前まではアプリに関連するドメインが複数あった場合、その分だけリクエストを行っていたのですが、Apple CDNへのリクエストではこれを1度に全て取得することが可能になったようです。そして`apple-app-site-association`に関しては、Apple CDNからリクエストするようになったとのことです。

この仕様変更によりUniversal Linksが起動しなくなる理由の一つは、**サーバ側のIP制限**にあるようです。Apple CDNが`apple-app-site-association`を取得することが出来なくなっていたため、当然アプリにもダウンロードされることがなく起きていた、と考えられます。

```mermaid
graph LR;
  A[アプリ] --> B[Apple CDN];
  B[Apple CDN] -.-> A[アプリ];
  B -- x --> C[サーバー];
  C -.-> B

style B fill:#ff0000,stroke:#ff0000;
```

#### 対策

取れる策としては、2つあります。

1. IP制限を外す
1. 開発モードとして挙動するよう設定し、Apple CDNをバイパスする

1は単純なので、2について解説します。

まず、アプリ側で設定したapplinksの後ろに`?mode=developer`というオプションを追加します。
次に、開発向けの署名アプリで「設定」> Developer > Associated Domains Development をONにします。
こうするとApple CDNをバイパス(省略)し、直接ドメインの`apple-app-site-association`を取得しにいくようです。

というような流れで一件落着しました。

***
以上、遅ればせながらUniversal LinksのTipsでした。担当する案件によってすぐに当たらない時もあるので、自主的に手を動かすことは大切ですね。
【Salesforce】データローダで「Java Heap Space」エラーが発生した時の対処法

データローダでイベントログをエクスポートする時に「Java Heap Space」エラーが発生しました。

データローダでエクスポート時に「Java Heap Space」エラーが出た時の対処法について紹介します。

データローダの設定のバッチサイズを低くします。可能な限り1に近づけます。

詳細は以下から
https://hishikiryu.com/salesforce-dataloader-java-heap-space/