OmniAICreator/Qiita-1.07M huggingface dataset:
デフォルトを信用すると、将来苦しい思いをする話

こちらは [本番環境でやらかしちゃった人 Advent Calendar 2021](https://qiita.com/advent-calendar/2021/yarakashi-production)の 23日目。今日は、わたしがゆかちゃんに懺悔を行う日です。

懺悔の前に、Perfume のかしゆかことゆかちゃん誕生日おめでとうﾜｰｲヽ(ﾟ∀ﾟ)ﾒ(ﾟ∀ﾟ)ﾒ(ﾟ∀ﾟ)ﾉﾜｰｲ

めでたい日に懺悔するとか、自分のクレイジーさが大好きです。

---

と思って、とてもたくさんのネタを準備してきたのですが、いずれも前職に関わるもので笑えなかったり、人によっては怒られそうな内容だったりするので、ちょっと別路線で行きます。ごめんなさい。

とは言え、どっかで懺悔したいので、誰か話を聞きに来て下さい。

## デフォルトだからって設計しないで済むわけではない

初めてだったんだからと許して欲しいタイプなんですが。初めてSANを構成した時の話です。その SANスイッチはデフォルトの定義が [FC-AL](https://www.wdic.org/w/WDIC/FC-AL) だったんです。p2pも指定できたんですが、なんか設定が面倒だったので。デフォルトだし、こっちが推奨だろうという若気の至りがありました。

冗長化で 2台構成だったんですけど、覚えてないんだけど Loop-id 設定するだけで簡単に実装できた記憶があるんですよね。まぁとにかく ID 指定すればオッケーで楽々ですませたんです。どうせサーバ2台とストレージ1台という構成で、増える要素も予定もなかったから。

### 発生する障害

もうそんなサーバを作ったことも忘れて、運用も他のメンバーになったころ。その運用メンバーから連絡が来ました。

「SANスイッチ壊れたから新しいものと入れ替えたいんだけど、設定がわからない」

OKOK。Loop-IDの情報くらいはあるよな。それを設定しておいてくれ。

「そんなものを設定するところはない」

...?

「そもそも構成なに?設計書とか設定残ってないの意味不明すぎる」

いやいや、Loop-ID の情報残ってるやんけ。他はデフォルトだぞ? そこしか変更してない。

どうやっても動かないし、交換すると、なんと「サーバが停止する」。

そう、なんとなくお分かりかと思いますが、なんとデフォルトの構成が p2pになったんですね。firmwareが上がったから。だから設定項目もないし、そもそも FC-AL なんて設定する人がいないから、その考えがみんなになかったんですよね。「Arbitrated Loop って構成になってるはずなんだけど」って言っても「なんですか、それ」みたいになる。そう、時も過ぎて時代はp2pだったのだ!(FC-AL一強なんて時代はなかったと思うけど)

ということで、なんやかんやで、運用メンバーたちに謝り倒してなんとかしてもらいました。

### 歯止め

そう、設計は正しく。設定も正しく。デフォルトだから良いよね、ではなく。なぜこの値でいいのか。デフォルト値であろうがその設定値は正しく残しておきましょうと言うことになりました。

というか、ホント、デフォルト値は変わって欲しくない。デフォルトの値が変わったり解釈が変わって、なんかどっかの証券取引止まったよね。怖い、怖すぎる。

とりあえず、正しい設計と、引き継げるような要素を明確にして、以後はそのようなことはなくなったと思いたい。

ツライ。
Flutter on the WebでURL形式を変更

この記事は、[「Flutter Advent Calendar 2021」](https://qiita.com/advent-calendar/2021/flutter)に投稿した「Flutter on the WebをFirebase Hostingで公開した」の一部となります。

# URL形式の変更
Flutter on the Webの初期状態のURLのパスは、以下のように「#(ハッシュ)」ベースになります。
![スクリーンショット 2021-08-14 20.02.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4668/d8108414-0480-cb1c-a1d1-67335d43935a.png)
このままでもいいのですが、せっかくなのでハッシュ無しのパスに変更したかったので`flutter_web_plugins`パッケージを適用しました。

```yaml:pubspec.yaml
dependencies:
  flutter_web_plugins:
    sdk: flutter
```

`main()` に `setUrlStrategy(PathUrlStrategy());` を追加します。

```dart:main.dart
import 'package:flutter_web_plugins/flutter_web_plugins.dart';

void main() {
  setUrlStrategy(PathUrlStrategy());
  runApp(MyApp());
}
```

URLの形式が変わっています。

![スクリーンショット 2021-08-14 20.02.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4668/26ec9be0-c67b-e296-0964-2bd04db3a887.png)

# WEB以外でもビルドしたい場合

`setUrlStrategy(PathUrlStrategy())`を追加すると、WEB以外でのビルドが通らないようになります。
`flutter test`すらエラーになります。
`flutter test --platform chrome`なら大丈夫ですけど。

WEB以外へも対応したい場合は、以下のようにするとWEBとそれ以外で読み込むdartファイルを場合分けすることが出来ます。

```dart:main.dart
// ignore: avoid_web_libraries_in_flutter
import 'dart:html';
import 'configure_nonweb.dart' if (dart.library.html) 'configure_web.dart';
```

同じ関数を用意して、WEBの時だけ`setUrlStrategy(PathUrlStrategy())`を呼ぶようにします。

```dart:configure_web.dart
void configureApp() {
  setUrlStrategy(PathUrlStrategy());
}
```

```dart:configure_nonweb.dart
void configureApp() {
}
```

以上です。
【Python】【Twitter】自動ツイ消しシステム

#1. はじめに
　皆さん，普段どのようなツイートしていますでしょうか？
　「ああ，ツイート全部にいいねが付いてたらな…」
　なんて思ったことありませんでしょうか？

　そんなあなたの願いを叶えるシステムを思いつきました．
　それが今回紹介する"自動ツイ消し"システムです．

　具体的には一定時間ごとに各ツイートに付けられた"いいね"の数を数え，
　閾値以下のツイートを削除します．
　これによって，あなたの投稿には
　いいねがたくさんつけられたツイートのみが残ります．

#2. 準備
###Twitter API
　まず初めに以下のリンクから，あなたのアカウントを
　Twitterの開発者アカウントに登録する必要があります．
　https://developer.twitter.com/en

　次に，そのアカウントのTwitter APIを取得する必要があります．

　詳しい方法に関しては，様々な方が記事を作成しているので，
　そちらを参考にするとよいと思います．

###ライブラリのインストール
　今回必要なライブラリは以下の通りです．

- tweepy

　本記事のメインとなるライブラリです．
　ツイートのいいねの数をカウント，削除を実行します．

- schedule

　プログラムの定期実行を行います．

- time

　プログラムを一時停止します．
<br>
　インストールは以下のコマンドで可能です．

```Python:module_install
pip install tweepy
pip install schedule
pip install time
```
#3. サンプルコード
```Python:Fake_Like_TimeLine_for_Twitter.py
import tweepy
import schedule
from time import sleep

#API
consumer_key="***"
consumer_secret="***"

#ACCESS TOKEN
access_token="***"
access_token_secret="***"

#TWITTERオブジェクト
auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)
api = tweepy.API(auth)

#USER_ID取得
Account="***"

#削除タスク
def task_delete():

    COUNT=50
    
    while True:

        tweets = api.user_timeline(Account, count=COUNT)

        for tweet in tweets:
            Like_count = tweet.favorite_count
            tweetID = tweet.id

            if Like_count <= 5:
                api.destroy_status(tweetID)
        
hour=3
schedule.every(hour).hours.do(task_delete)

while True:
    schedule.run_pending()
    sleep(1)

```

consumer_key=
consumer_secret=
access_token=
access_token_secret=
には開発者アカウントの画面で
取得したAPIとアクセストークンを入力してください．

Account=
には適用するTwitterアカウントのIDを入力してください．

[追記:2022/05/22]
twitterIDの@以下を入力します。


COUNT=50
は一定時間に50個のツイートのいいねをチェックすることを表します．

if Like_count <= 5:
はいいねの数が5個以下のツイートを削除することを表します．．

hour=3
は3時間に一回定期実行することを表します．

#4. 最後に
今回，自動ツイ消しシステムの紹介をしました．
躓くポイントとしてはTwitterアカウントの開発者申請とAPI取得だと思います．
私も実際にここが一番苦労しました．
参考文献は以下に載せておくので参考にしてください．

#5. 参考文献
https://blog.palettecms.jp/article/20103
Redashで最新の情報をSQLで取得してSlackに通知したい

# はじめに

この記事は [株式会社Diverse Advent Calendar2021](https://qiita.com/advent-calendar/2021/diverse) 23日目の記事です。

redash(OSS版)からSlackへKPIを通知していたところ、get_query_result()だとキャッシュしか送れないことに気づき、都度SQL実行する形に置き換えました。
※get_query_result()が用意されているものの、実行時にクエリが発行されるわけではなく、最後に実行したキャッシュを参照するだけなので今回は使わない。
Prameterを使っている場合も実行結果がキャッシュされないためこの記事のやり方が有効。

データソース(MySQL)とデータソース(Python)の2つで分けてたものをデータソース(Python)の１つに集約できます。

# 何ができるようになるか

- RedashのPythonでSQL実行(リアルタイム)＆Slack投稿

# さっそく作ってみる

## SQL書く、実行する

```python
# SQL書く
# 最新のユーザー10件を取得する
query='\
SELECT id,regist_date \
FROM user　\
ORDER BY id DESC \
LIMIT 10'

# SQL実行
values = execute_query('mysql', query)['rows']
print values # 雑にredash上で確認する際に使用

# redash画面上の出力内容の設定
# Slackに通知するだけならこの部分は書かなくてOK
result = {}
add_result_column(result, 'id', 'id', 'integer')
add_result_column(result, 'regist_date', 'regist_date', 'date')
add_result_row(result, values[0])
```

- DBから取得したデータ(UTF-8)の取り扱い時に以下のようなエラーが出る場合はエンコードを指定する。

```text
Error running query: <type 'exceptions.UnicodeEncodeError'> 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128)
```

```python
import sys
from imp import reload
reload(sys)
sys.setdefaultencoding('utf-8')
```

## Slackに通知する

- Pythonで通知するならslack-sdk使いたいところ。だけどredashへのrequestsで頑張る。
    - ※ここはググればサンプルコードいっぱいあります。

```Python
# Slackに投稿する関数の定義
def slack(content):
    import requests
    import json

    # Incoming Webhooksで取得したURLを設定
    SLACK_URL = "https://hooks.slack.com/services/hogehogehoge"
    payload = { 
        "text": content,
    }
    data = json.dumps(payload)
    response = requests.post(SLACK_URL, data)

# 実際に投稿する
slack('test')
```

## SQLで取得したデータをSlackに投稿する

```python
import sys
from imp import reload
reload(sys)
sys.setdefaultencoding('utf-8')

##############################################
def slack(content):
    import requests
    import json

    SLACK_URL = "https://hooks.slack.com/services/hogehoge" 
    payload = { 
        "text": content
    }
    data = json.dumps(payload)
    response = requests.post(SLACK_URL, data)
##############################################

query='\
SELECT id,regist_date \
FROM user　\
ORDER BY id DESC \
LIMIT 10'

values = execute_query('dbの名前を書く', query)['rows']

# ここでSlack投稿内容を整形
content = ''
for row in values:
    content = content + str(row['id']) + ':' + str(row['regist_date']) + '\n'
slack(content)
```

# 参考

add_result_column, add_result_rowの使い方について以下の方の記事を参考とさせていただきました。
https://qiita.com/terra_yucco/items/fc4d3b3270a168dbd18d
既存プロダクトでローコードテスト自動化ツールT-DASHを試したらびっくりするほど簡単に導入出来た

:sparkles: メリークリスマス :santa::tada::christmas_tree: 
この記事は[T-DASHを使ってみた！【PR】T-DASH Advent Calendar 2021](https://qiita.com/advent-calendar/2021/tdash)の24日目の記事です。

## T-DASHとは

T-DASHはWebアプリケーションの動作確認・検証をコードを書かずカンタンに作成・実行できるテスト自動化ツールです！

https://www.t-dash.io/

## T-DASHはこんな人におすすめ！

T-DASHは以下のような方にお勧めかなと思います。

- テスターとして働いている方
- E2Eを簡単に実行したい方
- E2Eに興味はあるけどプログラミングを書いてまでしたくない方

T-DASHはGUIを利用してノーコード/ローコードでテスト実行できるアプリになるので、
「自分でプログラムを書くことでテストを実行したい方」や「GitHub Actionsなどと連携して、コマンドベースで自動化したい方」等は対象ではないのかなと感じました。

## さっそく使ってみる

テストを実行するのはとても簡単で、`画面定義` と `テストシナリオ/テストケース` を画面からポチポチと作成するだけで完成します。
インストール方法などは他の方の記事や公式マニュアルを参考にしてください。
※実際のプロジェクトで実行してみたので、一部隠してる項目があります

### 画面定義

画面定義を簡単にいうと、画面上に存在する `ボタンやテキストボックスに名前を付ける` ページになります。

![2021-12-23_14h07_33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324647/ab808a6c-45fb-8cad-5e1c-d0fa555ed6b4.png)

#### 画面キャプチャ

画面定義を紐づける為には手入力でも出来るのですが、 `画面キャプチャ機能` を利用する方が圧倒的に簡単です。
以下3ステップを必要な要素(ボタンやテキスト)分繰り返して、要素に名前を付けていきます

1. 「要素名を入力」と書いてるテキストボックスに名前を入力します(ログインボタン等なんでもいいです)
1. 入力すると要素の一覧に追加されるので、フォーカスがあたった状態にします
1. 「トラッキングボタン」をクリックして対象の要素(ボタンなど)をクリックします (Chrome等に搭載されている開発者ツールとほぼ同じUIです)

![2021-12-23_14h13_46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324647/2a17cb4c-4df9-c3bc-c294-62757e38d7f5.png)


### テストシナリオ / テストケース作成

実際に操作したいテストケースを作成する画面になります。

以下3ステップを繰り返すことでテストケースを作成していきます。
基本的にはマウスでクリックしていくだけで、テストケースが作られていきます！

1. 実行したい内容を左の一覧から選択する (テキストを入力する、要素をクリックする等)
1. 左の一覧の下の方に要素が並んでるので選択する (例：ログインボタン等)
1. 上記2ステップで基本的なテストケースは完成しているので、テキスト入力等が必要な右の一覧に入力する

![2021-12-23_14h51_55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324647/ac2f19bd-dadd-b362-4daa-77538524a5ee.png)


## 感想

ここに書いてある操作だけで、「サイトにログインして、必要な項目を登録して、画面遷移する」などのことが出来てしまいます！
マウスクリックだけでテストケースが作られるのは楽でいいですね！
エンジニアじゃない人からすると、E2Eのテストコードを書くというのはどうしても敷居が高いものだったのですが
これであればハードルなくE2Eのテスト実行を導入できそうです！

## 要望 / 欲しい機能

### テストケースを行ごとにオン・オフしたい

テストがこけて試行錯誤するときに、全部走ると都合が悪いことがあります。
簡単に復旧可能なので消せばいいのですが、行ごとにテスト実行のオン・オフ出来ると嬉しいです！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324647/3f5cc0c8-d291-c65e-f000-e4424f3c454f.png)


### テスト実行の強制停止機能が欲しい

テストを実行したあと、「修正が足りなかった！」となった場合に最後まで走りきるのを待つ必要があるので途中で止めれるようになると嬉しいです！

### テストがこけたときに自動で画面キャプチャを取りたい

テストがこけたときにどんな画面構成になっていたのかを把握したいので、テストがこけた瞬間の画面キャプチャを自動で取れるようになると嬉しいです！

### プロジェクト全体をエクスポートしたい

同一プロジェクトに複数人かかわることがあるので、テストケースだけではなくプロジェクト全体をエクスポート・インポート出来るようになると嬉しいです！
※プロジェクトフォルダーをコピーすれば共有できそう？【未検証】
beamerでルーティングを定義

この記事は、[「Flutter Advent Calendar 2021」](https://qiita.com/advent-calendar/2021/flutter)に投稿した「Flutter on the WebをFirebase Hostingで公開した」の一部となります。

# ルーティング
Flutterのルーティングは当初、Navigator 1.0で記述していたのですが、[Navigator 2.0](https://docs.google.com/document/d/1Q0jx0l4-xymph9O6zLaOY4d_f7YFpNWX_eGbzYxr9wY/edit) に対応した方がページ遷移やURLの反映がすっきりするとわかったので書き換えました。
ただ、Navigator 2.0はまじめに書くとシンプルなルーティングでもコーディングが多過ぎるので`beamer`パッケージを利用しています。

https://pub.dev/packages/beamer

パッケージを追加して、

```sh
$ flutter pub add beamer
```

ルーティングを定義、

```dart:main.dart
import 'package:beamer/beamer.dart';

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
        routeInformationParser: BeamerParser(),
        routerDelegate: BeamerDelegate(
            initialPath: '/',
            locationBuilder: RoutesLocationBuilder(routes: {
              '/': (context, state, data) => MyHomePage(), // ホーム
              '/horse/:horseId': (context, state, data) => HorsePage(
                  horseId: int.parse(state.pathParameters['horseId'] ?? '1')), // 馬ページ
              '/race/:raceId': (context, state, data) => RacePage(
                  raceId: int.parse(state.pathParameters['raceId'] ?? '1')), // レースページ
              '/series/:seriesId': (context, state, data) => SeriesPage(
                  seriesId: int.parse(state.pathParameters['seriesId'] ?? '1')) // レース履歴ページ
            }),
      ));
  }
}
```

ボタンからは`beamToNamed`と記述すれば遷移します。

```dart:main.dart
ElevatedButton(
    child: Text('Go to race 1'),
    onPressed:() => Beamer.of(context).beamToNamed('/race/1'))
```

欠点は、けっこう仕様、というか定義が変わるところでしょうか。
ここ7ヶ月で2回ルーティングの定義を書き直しました。
さほど手間ではないのですが、`flutter pub upgrade`した後にvscode上でコードがエラーになって赤く変わるのは心臓によくありませんでした。

以上です。
IBM CloudでGitとJenkinsを連携させてみた（1.Git環境構築）

#目的
IBM Cloud環境にて，gitおよびjenkinsを構築し，Jenkins と Git を連携させ、ビルド・リリースのためのCI/CD環境基盤を構築する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374963/62d9a2cc-8125-4e54-167b-a74cc55c5a36.png)


#手順
構築時に利用する設定項目は，以下の値で作成いたします。

|  設定項目           |  設定値              |
| --- | ---|
|  リソースグループ    |  default  |
|  Gitリポジトリー名    |  Git_repo|
|  ツールチェーン名    |  Git_Jenkins_Chain  |
|  仮想プライベート・クラウド名    |  vpc  |

#手順詳細

##1.Git環境の構築

<details><summary>1.ツールチェーンの作成</summary><div>

##1.ツールチェーンの作成
IBM Cloud環境へログイン
ログインURL
https://cloud.ibm.com/login
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374963/bfb7fbe9-444b-d198-a9f9-20abf46e0fc3.png)

###ダッシュボードのメニューから，メニューよりDevOpsを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374963/86ffe740-a6da-cb6d-dea1-80d3b2410299.png)

###IBM Cloud上でtoolChainを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374963/75784340-bbe5-3094-7c39-2108cafb81ec.png)

###Build Your Own ツールチェーンを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374963/b98de8f8-6526-e028-41c0-f6b69ad61320.png)

######ツールチェーンの作成
ツールチェーン名の入力，および，リソースグループを選択し，作成ボタンを押下
ツールチェーン名
Git_Jenkins_Chain
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374963/419806fc-e641-d222-f504-9c48e0073fe0.png)

######ツールチェーンが作成されたことを確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374963/91bed234-b8d2-fc85-4fda-8790cbffd6ea.png)

</div></details>
<details><summary>2.gitリポジトリーの作成</summary><div>

##2.gitリポジトリーの作成
######ツールの追加を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374963/e0eda67d-1d98-ebd6-6360-0bce737001a5.png)

###GitリポジトリーおよびIssueトラッキングを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374963/0904ae99-180e-5a52-cc83-eebc25224fca.png)

###Gitリポジトリーの作成
リポジトリー名を入力し，統合の作成を選択
リポジトリー名は，Git_repo
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374963/4fa784e1-ab76-5413-7952-ebdd2284f398.png)

###Gitリポジトリーの作成確認
入力したリポジトリー名で作成されたことを確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374963/a53e995e-f7c2-b627-e703-295279ec3c1f.png)


</div></details>
<details><summary>3.gitの設定</summary><div>

##3.gitの設定
###gitリポジトリーを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374963/4dca51be-7e8f-2a70-0b74-f73f99310e7a.png)

###テスト用のファイルを追加
検証用に以下のスクリプトを記載

@echo off
echo Hello world

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374963/0432de10-77d4-3ae1-023c-a8de8974e941.png)

###テスト用ファイルが追加されたことを確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374963/06af93ae-6799-d0ba-aa3b-b68ce6012aa6.png)

</div></details>
無料？！Oracle Cloud の信じられない程スペックいいVPSを作ってみました。

# 背景

無料で使えるクラウドサーバリソースという記事に乗っていないものを共有させて頂きます。

https://qiita.com/goyae/items/196221169391dd44125d

実は、Oracle Cloud で 4CPU/24GBメモリ(?!)までの無料VPSが作れます。

# 特徴・落とし穴

特徴か落とし穴か人それぞれだと思いますが、
上記に書いてた無料リソースは__ARMインスタンスのみが対象です__。

ま、Raspberry PiやApple SiliconのおかげでほとんどのパッケージはARMを対応しているので、
個人的はそんなに気にしていませんでした。

# 検証
### インスタンス作成

アカウントを登録し、早速作ってみました。
登録する時にHomeリージョンを選べることが必要です。
ARM版VMは東京リージョンに少ないという警告が出たので、大阪リージョンにしました。

今回は、2CPU/12GBメモリのインスタンスを作りました。
デフォルトでは1CPUごとに6GBメモリが振り分けられますが、
メモリ量が個別で調整できるそうです。

OSはOracle Linuxが基準のようですが、
個人的馴染んでいるUbuntuもあるのでそちらにしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285534/6414ef62-2482-5601-a8a2-a1e4ebbc9f24.png)

最初に「リソースが不足ため作成できない」というエラーが出ましたが、
数時間後に再度作ってみたら行けました。（やはり人気ですかね？）

### SSH

実際に入ってみて色んなコマンドを叩きました。

まず、カーネルをチェック。

```
$ uname -a
Linux hoge-blog 5.11.0-1022-oracle #23~20.04.1-Ubuntu SMP Fri Nov 12 15:45:47 UTC 2021 aarch64 aarch64 aarch64 GNU/Linux
```

間違いなくARMですね。

次はスペックを確認。
![image.psd.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285534/9bceb872-51b7-f5e4-b00e-d1729f13936c.jpeg)

CPUとメモリが合ってますね。騙されていないですね笑

### ホスティング

個人ブログのために立ち上がったサーバなので、
`docker`＋`docker-compose`を入れて、
サービスを起動してみました。

ちゃんとサイトが見れるし、サクサク動いています。
パフォーマンス面も特に問題ありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285534/de07a782-8212-0363-63d4-1c8d98cfdd86.png)

流石にブログサイトにしてはオーバスペックかもしれません。。。

# 最後に

やはり無料インスタンスが人気なのですぐ作れないかもしれないですが、
自分みたいにケチですがVPSを作りたい人は是非作ってみてください！

https://www.oracle.com/jp/cloud/
【k8s】俺用YAMLテンプレート(Deployment/Service/Ingress/IngressClass)

#### クラスタ作成用コマンド
```
eksctl create cluster
 --name test-cluster
 --region ap-northeast-1
 --nodegroup-name linux-nodes
 --node-type t2.micro --nodes 3
```
---
#### Deployment

```yaml
apiVersion: apps/v1 
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx #template下で定義した【PodのLabel】
  replicas: 1 #Podの数
  template: #template下にPodの定義
    metadata:
      labels:
        app: nginx # 【PodのLabel】
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
```

---
#### Service

```yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  labels:
    app: nginx
spec:
  type: ClusterIP　#デフォルトのServiceType
# type: NodePort 
  selector:
    app: nginx
  ports:
      - protocol: TCP
        port: 80
        targetPort: 80
#        nodePort: 30080 ←ServiceをNodePortで外部公開する場合に指定
```

---
#### Ingress

・AWS Load Balancer Controllerを利用するアノテーション有

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-ingress
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP":80,"HTTPS": 443}]'
    alb.ingress.kubernetes.io/actions.response-404: >
      {"type":"fixed-response","fixedResponseConfig":{"contentType":"text/plain","statusCode":"404","messageBody":"not found"}}

  labels:
    app: nginx-ingress
spec:
  rules:
    - host: <★ドメイン名★>
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: nginx-service
                port:
                  number: 8085
          - path: /actuator
            pathType: Prefix
            backend:
              service:
                name: response-404
                port:
                  name: use-annotation

```

---
#### IngressClass/IngressParams

・Ingressを管理するIngressControllerをIngressClassで決定する

```yaml
apiVersion: elbv2.k8s.aws/v1beta1
kind: IngressClassParams
metadata:
  name: alb-ingress-class-cfg
spec:
  group:
    name: default-group
---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: alb-ingress-class
  annotations:
    ingressclass.kubernetes.io/is-default-class: "true" #Ingressに紐づくデフォルトのIngressClass
spec:
  controller: ingress.k8s.aws/alb
  parameters:
    apiGroup: elbv2.k8s.aws
    kind: IngressClassParams
    name: alb-ingress-class-cfg
```
【Mac】Wi-fiには接続されているのにネットワークが繋がらない時の対処法【OBGノウハウ】

先日(2021年10月26日)に正式発表されたMacBookProを購入し、それまで使用していたMacBookProから引っ越しを行なった際に起きたトラブルです。
検索してもなかなか見つからなかった為、畑違いですが備忘録作成しました。

#Wi-Fiには繋がっているのに…
移行アシスタントを使ってMacの中身をそっくりそのまま移行。
トラックパッドの設定まで引き継がれていて、あまりの快適さにびっくりしたのも束の間。
ネットに繋がらず、アプリも開けない状態になってしまいました。

なんなら移行前はネット繋がっていたような……？
……という場合はなおさら、有効かもしれません。

#アプリを開こうとした時の挙動
ちなみに、アプリを開こうとすると下記のようなメッセージが出てきました。
インストールしようにもネットに繋がらないので「インストール」ボタンをクリックしても何も起きない状態でした。
>Rosettaにより、Intelプロセッサを前提とした機能をAppleシリコン搭載のMacで実行できます。Rosettaの使用を開始するには、インストール後にアプリケーションを開き直す必要があります。

>このソフトウェアを使用すると、ダウンロードしているソフトウェアに適用されるソフトウェア使用許諾契約に同意したものとみなされます。Apple製品のソフトウェア使用許諾契約の一覧は次をご覧ください: https://www.apple.com/jp/legal/sla/

#解決方法
私のPCの場合は、<b>ウィルスバスターがネットワーク接続を邪魔していた</b>ことが原因でした。
###①システム環境設定から「ネットワーク」を開く
Dockまたはメニューバー右上のAppleアイコンから、「システム環境設定」をクリックします。
「システム環境設定」が開いたら、「ネットワーク」を開きます。
![スクリーンショット 2021-11-04 13.34.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2100978/000c6b99-20cd-d3eb-e7f0-7cfcb71204df.png)

###②ネットワーク内に「Trendo Micro Extension」があるかを確認しましょう。
あ、あるーーーーーッ！　これを削除すればネットにつながるかも…！
![スクリーンショット 2021-11-04 13.35.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2100978/31fa9abd-f43e-7aac-7d6e-513741297747.png)

###③「Trendo Micro Extension」を削除する
鍵の解除を忘れずに。
![スクリーンショット 2021-11-04 13.36.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2100978/127f95f1-d4a9-7639-24db-6d84efbee112.png)

「Trendo Micro Extension」を選択し、「-」をクリックしてネットワークを削除します。
![スクリーンショット 2021-11-04 13.36.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2100978/41047a72-8c74-9cbb-0da9-95f3c57bc3ff.png)

###④「適用」して完了。
この処理をしないと、せっかく削除したはずの手間が水の泡です。
「適用」して、修正を保存しましょう。
逆に間違えて違う項目を削除してしまった場合は、「元に戻す」で作業前に戻りますので、落ち着いてもう一度作業しましょう。
![スクリーンショット 2021-11-04 13.38.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2100978/c95b800c-8f64-67fb-b30a-cde05b02c48b.png)



#【おまけ】その他の一般的な解決方法
私自身が今回解決前に参考にしたサイトは下記です。
今回は原因が違った為、下記の記載では解決できませんでしたが上記の方法で解決しなかった場合は逆に下記のサイトを参考にすると効果的かもしれません。

https://support.apple.com/ja-jp/HT202663

https://www.kashi-mo.com/media/83754/?cid=1014301518.1636001335

https://kerubito.net/apple/9267

https://townwifi.jp/media/1167/
Flutter on the Webでindex.htmlやOGPを変更

この記事は、[「Flutter Advent Calendar 2021」](https://qiita.com/advent-calendar/2021/flutter)に投稿した「Flutter on the WebをFirebase Hostingで公開した」の一部となります。

# webフォルダ

このwebフォルダ自体がビルド時のベースとして利用され、`build/web`フォルダへ出力されます。

# index.html のカスタマイズ
`web/index.html`をカスタマイズします。
アイコン類は、`web/icons/*、web/favicon.png`を差し替えればこちらもビルド時に`build/web`フォルダへ複製されます。

```html:index.html
  <meta name="description" content="ウマ娘の銘と魂を受け継ぐ源泉となった異世界（現実世界）の競走馬たちは実際のレースで如何に競い合ったのかをご覧下さい。">

  <!-- iOS meta tags & icons -->
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
  <meta name="apple-mobile-web-app-title" content="ウマ娘ダイアグラム">
  <link rel="apple-touch-icon" href="icons/Icon-192.png">
```

# manifest.json (ウェブアプリマニフェスト)
`web/manifest.json`をカスタマイズします。

```json:manifest.json
{
    "name": "ウマ娘ダイアグラム",
    "short_name": "ウマ娘ダイアグ",
    "start_url": ".",
    "display": "standalone",
    "background_color": "#99cc33",
    "theme_color": "#99cc33",
    "description": "Uma Diagram database.",
    "orientation": "portrait-primary",
    "prefer_related_applications": false,
    "icons": [
        {
            "src": "icons/Icon-192.png",
            "sizes": "192x192",
            "type": "image/png"
        },
        {
            "src": "icons/Icon-512.png",
            "sizes": "512x512",
            "type": "image/png"
        }
    ]
}
```

ウェブアプリマニフェスト
https://developer.mozilla.org/ja/docs/Web/Manifest

ウェブアプリマニフェストを設定すると、Android等の対応ブラウザで「ホーム画面にアプリを追加」とサジェッションされるようになります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4668/1c93622a-68ee-db4c-5380-587e590e094e.jpeg" width="512px">

# OGP (Open Graph Protcol) と Twitter Card を設定
```html:index.html
  <!-- Twitter Card AND OGP -->
  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:site" content="@umadiagram" />
  <meta name="twitter:creator" content="@rukari" />
  <meta property="og:title" content="ウマ娘ダイアグラム | 異世界（現実世界）での競走馬たちの系譜" />
  <meta property="og:description" content="銘と魂を受け継ぐ源泉となった異世界（現実世界）の競走馬たちは実際のレースで如何に競い合ったのか…" />
  <meta property="og:image" content="https://images.umadiagram.com/summary.png" />
```

ただしこのTwitter Cardの設定は、Flutter webアプリがPWAなのでURLによるルーティングを設定しても全ページで固定化されてしまいます。
これは別途「Firebase FunctionsでHTML出力をカスタマイ」の記事で説明します。
【企画段階】イベントごとのお得な情報をLINEで管理したい

#クリスマスケーキ、おせち、恵方巻、、、
季節ごとにイベントがあり、ごちそうの予約販売を見かける機会が増えました。各企業、自社のＨＰや店頭で宣伝しております。しかし、これらの情報、ユーザー目線で見ると散乱している。
選ぶ楽しさに改善の余地があるのではと考えています！

##"生活圏にあるお店"の予約販売の情報を知りたい
生活圏にあるお店の予約販売情報が分かれば比較、検討が簡単に出来るのではないかと考えています。LINEチラシなど、お店情報をまとめてくれるアプリはありますが、店舗単位でしかもお店側が訴求したい情報がメインです。
ネット購入であれば商品単位で閲覧が可能ですが、実店舗単位だとなかなか実現が難しいのが現状です。

##問題と課題を整理する
【問題】
イベントごとの予約商品情報を探すのがめんどくさい
【課題】
キーワードなどで容易に関連情報をまとめて取得したい

##解決方法を考える、LINEBotをどう使う？
こんな感じのイメージを考えています
![スクリーンショット 2021-12-23 17.31.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1811885/06b3fa78-a016-dd69-dd8d-4d65b411f798.png)

##技術的な課題、Web情報をどう取得するか？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1811885/652db713-c766-84ab-28ee-184e7f398a11.png)

###技術課題を解決する為に鋭意研究中です！！
更新していきます。

よろしくお願いします。
C#でカノジョをつくる（23日目）

お疲れ様です。たなしょです。
今日はテンプレートの返答をさせるためのテンプレート返答用のテキストと辞書クラスにテンプレート用の処理を追加していきます。

## テンプレートテキストを作成する
`%noun%`を置換することでテンプレートの言葉として登録します。
この%noun%の数が後ほど学習部分で役に立ちます。
下記がテキストの中身ね。左が%noun%の出現回数です。

```
1\t%noun%なのね
1\t%noun%がいいんだ
1\t%noun%はやだ！
1\t%noun%が問題だね
1\t%noun%が！
```

## テンプレート辞書を作成する
テンプレート辞書を作成します。
1.テンプレート辞書をオープンします

```c#
string[] t_lines = File.ReadAllLines(
            @"dics\template.txt",
            System.Text.Encoding.UTF8
            );
```
2.一行ずつを加工します（改行をとりのぞく）

```c#
List<string> new_lines = new();
     
foreach (string line in t_lines)
{
    string str = line.Replace("\n", "");

    if (line != "")
    {
        new_lines.Add(str);
    }
}
```
3.\tでキーと文字列を分けて%noun%の出現回数のキーが存在する確認します。

```c#
foreach (string line in new_lines)
{
    // add code
    // line replace \\t -> \t
    string rep_line = line.Replace("\\t", "\t");
    string[] carveLine = rep_line.Split(new Char[] { '\t' });

    if (!_templateDictionary.ContainsKey(carveLine[0]))
    {
        _templateDictionary.Add(carveLine[0], new List<string> { });
    }

    _templateDictionary[carveLine[0]].Add(carveLine[1]);
}
```
4.学習メソッドにテンプレート学習メソッド追加します。

```c#
StudyTemplate(parts);
```
5.ユーザーの発言をテンプレート学習して、テンプレート辞書に登録します。
5-1.形態素と品詞情報に分けて名詞(一般|固有名詞|サ変接続|形容動詞語幹)にマッチングするか調べます。

```c#
foreach (string[] morpheme in parts)
{
    if (Analyzer.KeyWordCheck(morpheme[1]).Success)
```
5-2.形態素を%noun%に書き換えてcount+1します。

```c#
morpheme[0] = "%noun%";
count++;
```
5-3.%noun%が存在して、%noun%の出現回数がテンプレート辞書はキー値である出現回数を追加します。

```c#
if (count > 0)
{
    string num = Convert.ToString(count);

    if (!_templateDictionary.ContainsKey(num))
    {
        _templateDictionary.Add(num, new List<string>());
    }
```
5-4.テンプレート辞書の中にテンプレート文字列が存在しない場合、リストに追加します。

```c#
if (!_templateDictionary[num].Contains(tempStr))
{ 
    _templateDictionary[num].Add(tempStr);
}
```
6.テンプレートの連想配列を1行ずつキー値と文字列を取り出して\tでつなげてリストに格納してソートで%noun%の出現回数順にソートをかけてファイルに書き込みます。

```c#
List<string> templateLine = new();

foreach (var dic in _templateDictionary)
{
    foreach (var temp in dic.Value)
    {
        templateLine.Add(dic.Key + "\\t" + temp);
    }

    templateLine.Sort();

    File.WriteAllLines(
        @"dics\template.txt",
        templateLine,
        System.Text.Encoding.UTF8
        );
}
```

## 最後に
今回はテンプレートの会話をするための土台となるテンプレート辞書とその仕組みを作成しました。
明日はレスポンスを返す処理と一連の処理をフォームに反映させたいと思います。
【p5.js】p5.soundで音を生成してみる

この記事は Qiita p5js アドベントカレンダー24日目の記事です。

## これはなに
前日に書いた[【p5.js】p5.soundで音を利用した表現を描画する](https://qiita.com/shibuya01055/items/f4c1604f79f442ac3399)の続編です。
上記の記事ではpreload()で用意した音を使用しましたが、今回はp5.soundで音を生成してみる内容です。

## 試してみる
### オシレーターを用意する
まずは音の出力に必須となるオシレーターを用意します。
#### オシレーターとは
シンセサイザーが音を出すためにはその元となる波形が必要になります。
オシレーターはその波形をつくってあげるもののことで、p5.soundでは`p5.OSCillator`というクラスが用意されています。

#### インスタンスを用意する
p5.Oscillatorをnewしてインスタンスをsetup()内に用意します。
引数の`sine`は波形の種類のことで、
sine（正弦波）、triangle（三角形）、sawtooth（のこぎり波）、square（矩形波）の４種類があります。
この辺はシンセサイザーを触ったことがある人はおなじみなのではないでしょうか。

```javascript
function setup() {
  createCanvas(600, 600);	
  const osc = new p5.Oscillator('sine')
}
```

### 音を出す
`osc.start()`をsetupに追加して実行します。

```javascript
function setup() {
  createCanvas(600, 600);	
  const osc = new p5.Oscillator('sine')
  // 追加
  osc.start()
}
```

実行すると「ポーーーーーーーーーーー」という音がなりました！
音の出力だけなら`createCanvas()`も不要なので、setup()内のたった2行で音が出せるということがわかりました。
恐るべしp5.sound。

### freqメソッドで周波数を変える
周波数をfreqメソッドで変更することで音色を変えることができます。
下記のようなコードを書いてみました。

```javascript
let isPlaying = false
let osc = null

function setup() {
  // createCanvas(600, 600);	
  osc = new p5.Oscillator('sine')
  osc.start()
  isPlaying = true
  // 周波数1000からスタート
  osc.freq(1000)
}

function draw() {
  // 開始から1秒後にrandom(400)な周波数が渡される
  if(isPlaying) osc.freq(random(400), 1)
}
```
実行から1秒間は1000から数値が下がっていくように発音されます。
その後、フレームレートごと（1秒間に60回）の頻度でrandom()が返す値がfreqに渡るため、
「ボコボコボコボコッ！」と激しく音色が変わることが確認できると思います。

### 音の情報を描画する
音の周波数や音量をリアルタイムで描画してみましょう。
p5には`FFT`というクラスがあり、これが持つメソッドを用いて周波数などを描画することができます。

リファレンスを参考に下記のようなコードを書いてみました。
keyPressedとkeyReleasedで、キーボードを入力しているときだけ音が鳴るようにしています。

またfft.analyze（周波数域）とfft.waveform(振幅)がもつ情報を用いて、
発生しているがどの帯域でどれくらいの音量が出ているかを表しています。

```javascript
let osc = null
let env = null

function setup() {
  createCanvas(600,600);
  osc = new p5.Oscillator('sine')
  osc.freq(500)
  fft = new p5.FFT();
}
function draw() {
  background(220);
  
  let spectrum = fft.analyze();
  
  for (let i = 0; i< spectrum.length; i++){
    let x = map(i, 0, spectrum.length, 0, width);
    let h = -height + map(spectrum[i], 0, 255, height, 0);
    rect(x, height, width / spectrum.length, h )
  }
  
  let waveform = fft.waveform();
  noFill();
  beginShape();
  stroke(20);
  for (let i = 0; i < waveform.length; i++){
    let x = map(i, 0, waveform.length, 0, width);
    let y = map( waveform[i], -1, 1, 0, height);
    vertex(x,y);
  }
  endShape();
}

function keyPressed() {
  osc.start()
}

function keyReleased() {
  osc.stop()
}
```

![画面収録-2021-12-23-16.57.04.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358229/709c1191-dc46-fd1f-df06-bf0a55731e68.gif)

## あとがき
まだまだp5.soundには音を操るための関数やクラスがたくさん用意されています。
組み合わせていけばオリジナルのシンセサイザーなんかも作れたり、
もしかしたらライブコーディングで演奏なんかもできるのかもしれません。

## 参考
[p5.js reference - p5.sound](https://p5js.org/reference/#/libraries/p5.sound)
レガシーな大規模システムの修正でレビュー時に大切にしていること

#はじめに

日頃、巨大な業務アプリケーションのコードを扱っています。

10～20年選手のコードが生き残っており、とある１つの計算処理単位中の分岐数が2万超、ドキュメントもテストも十分ではないレガシーだが現役のコードたちです。
感覚的なものですが、同僚による"ジェンガで出来たサグラダファミリア"は言い得て妙だなと思っております。

今回はそんな繊細なバランスで成り立っているコード群に、不具合修正を行うときにレビュワーとして気を付けていることを５点に絞って紹介します。
なお今回はJavaを中心に話をしています。


# 第一条　何が何でもノンデグ担保

デグレードしていないことがスーパーウルトラハイパー大事。
修正が不十分であることよりも優先。

必要十分な修正**以外**が入っていないか。

if文追加した範囲が大き過ぎるとか、returnが追加されている場合など、要注意。
修正したいケース以外の所まで仕様が変わっていないだろうか。

ついでに修正しちゃいたい気持ちになったとしても、分からないなら触るべからず。触らぬ神に祟りなし。
触りたいなら、テストせよ。絶対にだ。
たぶん大丈夫なんてものは存在しない。確認した事実こそが全て。


【理由】
　余計なトラブルを回避しよう。今のまま良い奇跡のバランスが取れている可能性がある。
　どうしても変えたいなら、変更の影響がないか、がんばって調べる＆影響が出る先との調整が必要なのだ。
　それだけの覚悟はお持ちか。
　そのネゴシエーションコストを払うのは、あなたではないかもしれない。
　胸に手を当てて考えてみよう。今はその時だろうか。


# 第二条　スコープ！スコープ！！スコープ！！！

privateでいいならprivateにしておこう。
publicである必要性がないならpublicにしないこと。

JUnitで呼び出したいからpublicにしてるって？ それならpackage privateでいい。

【理由】
　自ら影響範囲を広げるリスクをおかしてはいけない。
　後々に、どこから参照されているか調べるコストを増やす。

同様の理由で、下手にリフレクションして呼び出すとかしない方がいい。
場所と使いどころにもよるが、後々の保守にあたるエンジニアたちの影響範囲調査が大変になり過ぎるような実装は負債になる。

また過度な共通化もしない方がよい。
コンテキストの差異が後々判明してきて、共通化の中に様々な例外ケースを入れないといけないことになったりする。
後からちょっと手を入れるために、調査・テストしなくてはいけない範囲が膨大になる。
社内製の共通ライブラリみたいなものは、一度できてしまうと、顧客からは機能強化のニーズが出てきにくい部分なので保守は手薄になってしまうことがある。
後々、誰が担当するか問題やら、誰も分かる人がいない問題が起きたりする。

※参考　[プログラマが知るべき97のこと 共有は慎重に](https://xn--97-273ae6a4irb6e2hsoiozc2g4b8082p.com/%E3%82%A8%E3%83%83%E3%82%BB%E3%82%A4/%E5%85%B1%E6%9C%89%E3%81%AF%E6%85%8E%E9%87%8D%E3%81%AB/)


# 第三条　周囲との調和

ハーモニー。その世界の文化に馴染むコードを書く。

メソッド内⇒クラス⇒パッケージ/jar単位⇒機能のサブシステム単位⇒製品単位
の順で、郷に入らば郷に従おう。

どうしても嫌なら、全部、書き直そう。全部直してテストできないなら、周りに合わせておくべし。

コメントと実装が乖離している？
実装とコメントが素敵な二重奏を奏で続けるために、コメントも修正しよう。


【理由】
　後から読んだときに、理解に余計なコストがかからない。
　（例：同クラス内でsya_idが急にemployee_idになっていたら、戸惑う。
　　　　一緒の概念なのか逐一確認が必要になる。例え、中途半端な日本語で微妙な命名と思っていようがsya_idを使おう）
　後々に修正入れる際に、どちらに合わせるべきか、迷わせなくて済む。
　規則性があると類似調査がしやすい。


# 第四条　論理的整合性

不具合事象、原因、原因への対処方法、その対処を選択した理由、影響範囲の認識。

これをレビュイーが語れるか。論理に破綻がないか。さらに、修正しているコードの間でも、整合性が取れているか。
ソースコードレビュー時には、一行ずつ、なぜこれが必要なのか、これでいい理由は？を考える。

分からないことは聞いてよい。
レビュワーがすべてを分かっているべきだから、質問するのは恥だ・・・なんてことはない。
無論、同じ質問をし続けないように学ぶ必要はある。
しかし疑問はそのままにしない方が、総合的に考えたら合理的である。

レビュイーと一緒に調べたっていい。あるいは、分かるひとに協力を依頼するのも良いかもしれない。

とにかく、ん？なんか変だな？っていう感覚をそのままにしないこと。

【理由】
１．レビュイーのバイアス
　自分自身がソースを書いていると、動いた！できた！完璧！！という気持ちになる。
　特に取り組んでいるのが、複雑なジェンガだと、難問を解消したときの喜びは大きい。
　それが故に、ここで陥る正常性バイアスは非常に強力で、自力で抜け出すのが非常に困難である。
　完全に忘れるまで寝かせておくなどすれば気づくかもしれないのだが、そんな暇はない。　
　他人に説明したり、質問されたりする中で、初めて、これひょっとして破綻してるんじゃ？と気付くことは多々ある。

２．可読性
　よくわからないこと、は他の人にも分からない可能性が高い。つまり後から理解するのが難しい。
　そういうコードは長く保守していく上で、リスクになる。


# 第五条　1度に1つずつ

One by one, Step by step

複数目的の大量の修正を1つのcommitに詰め込み過ぎない。
小さめに意味のある単位でcommitを分ける。

またMRの単位でも不具合毎に分けるべし。

【理由】
　論理的整合性を確認する上でも、とても有用。大きすぎると覚えていられない。
　また、何らかの上手くいかないことが起きた時、切り戻しが楽。
　納期の都合上、優先度の低いものを先送りするときなどにも、分かれて管理できていると作業が楽。


# さいごに

私自身、社内の複数の背景が異なるプロダクトでレビューする経験を経て、コードを大体読めれば、業務ドメインの深い知識や、アプリの特性への知見が浅くとも、そこでレビューする意味があると実感しています。
レビューの目的は共通知を得ることだとか教育だとか世には色んな意見がありますが、私は仕事上でのレビュワーとしての仕事は、お客様・自分たちの未来が少しでも問題の少ない世界にするためのガーディアン活動かなと思っています。

これまで流した汗と涙から厳選したポイントたちです。これらだけでも、痛い目に遭う確率は結構減らせるんじゃないかと思います。

巨大な既存コードに手を入れる方、レビュワーとして修正にGOサインを出す立場になったばかり方、二の轍を踏む人が少しでも減らんことを祈っています。

メリークリスマス！
[Unity]アトリビュートの活用 - メソッドを非ランタイム時に実行

アトリビュートとは変数宣言の前に書いたりする[ ]で囲まれた、[Serializefield]とかのあれです。
アトリビュートをつけると、特殊な扱いを受けるものになったりします。
Serializefield以外にもいろいろな種類があるのですが、使ってて役立ったものを紹介します

##[ContextMenu]

[ContextMenu]をつけると、メソッドを非ランタイム時に実行することができるようになります。
どういうことかというと、とりあえず見てみましょう

ボタンを押すと、パネルのテキストが変更されるというメソッドを作ります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689266/2d20ef65-a18b-1be1-61a6-4d68186d9770.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689266/9cef620e-010b-dd4a-2a49-140340a50792.png)
このメソッドをインスペクターから設定する場合、ボタンコンポーネントにChangeTextメソッドを設定すると思います
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689266/cc871776-f70e-d32e-9fd8-59ab961c55c3.png)

これで、ボタンを押すとテキストが変わるということができました
![398410000099.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689266/6f2dbb86-c2f7-22b9-b63d-ea85f79ac006.gif)
___

今作ったメソッドを、インスペクターから実行できるようにします。
ChangeTextメソッドの前に、`[ContextMenu("ChangeText")]`と足します。引数にはメソッド名を文字列で入れます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689266/6265a8ee-c6d8-b0ae-e5ae-0d53fa3a00b3.png)
これで準備が整いました

メソッドを書いたスクリプトがアタッチされているオブジェクトのコンポーネントから、右上のメニューを開くと、ChangeTextのメソッド名が追加されています
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689266/f9183bc1-2c41-6fea-ee76-bce0c110e98c.png)

これを押すと、再生時でなくても、そのメソッドを実行することができます
![3984100000999.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689266/c28d2c7b-5549-a62d-5b7e-0bf774f4b761.gif)
___
さて、unityは
・再生中に変更した値は再生が終わると元に戻る
という仕様ですが、そもそも今回は非再生時に実行したので、テキストを変更してもずっと変わったままになっています

これで何ができるかというと、例えば、
大量のオブジェクトを生成して配置するというのを手動でやりたくないとき、スクリプトでやることができるわけです

ただし、これで実行したものはアンドゥが利かないので注意
HTML Living Standardが標準規格になるまでの流れ

##はじめに
:::note
この記事はHTMLについて学び始めたばかりの初学者がまとめました。
:::
私は今インターンを行っている大学生です。インターンを行うにあたりHTML(以下htmlとする),cssについて学び始めました。
その際にhtmlの規格に、今年変更があったことを知ったので変更されるまでの経緯や今までの規格との違いについてまとめてみました。

##HTMLとは
最初に今までの規格との違いについて述べる前にhtmlとは何かを簡単に説明します。**HTML(HyperText Markup Language)**はWebページを記述するために開発された言語です。インターネットで公開されているWebサイトのほとんどがhtmlで記述されています。

##W3CとWHATWG
![W3C.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2416038/885cab56-d6c6-4e3d-5d7f-a9ea75ff4ec8.png)
**W3C(World Wide Web Consortium)**とはHTML,CSS,DOMなどのWebに関する仕様を決めている機関です。
![WHATWG.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2416038/a50816ae-7d87-6332-4847-e7835cf63433.png)
**WHATWG(Web Hypertext Application Technology Working Group)**とはHTMLや関連技術を開発するためのコミュニティです。W3Cがウェブ開発者の要望を軽視していることなどを不満に思ったApple、Mozilla、Operaの開発者たちによって2004年に結成されました。2017年末にはMicrosoftも参加しています。

##HTML5が廃止されHTML Living Standardが標準規格に
Microsoft は W3C に準拠していましたが、Google, Mozilla, Apple, Operaなどの主要ブラウザではW3Cに準拠することを辞め、HTML Living Standardを標準仕様として採用し始めました。W3C派だったMicrosoftも、2018年12月6日、EdgeをChromeと同じChromiumベースに移行することを発表。こういった流れからW3CはHTMLの標準化をWHATWGに委ねることとなりました。
そして2021年1月28日にW3CのHTML関連の標準仕様は全て廃止され、WHATWGのHTML Living Standardに統一されることとなりました。

##HTML Living StandardとHTML5の違い
HTML5からHTML Living Standardに変更がありましたがHTMLを記述する上での大きな違いはありません。
ですが、追加、廃止されたタグや属性があります。[こちらのページ][link-1]や[こちらのページ][link-2]などから確認することができます。

##最後に
今回の記事ではhtml5からhtml living standardへ規格が変更された経緯などについてまとめてきました。
html living standardにバージョンなどはなく頻繁に更新されていくので正確な情報を得るには[公式ページ][kousiki]や[翻訳されたページ][honnyaku]を確認することが重要だと思います。

##参考文献

https://html.spec.whatwg.org/multipage/

https://momdo.github.io/html/

https://sd-webworks.com/column/html-living-standard/

https://www.tohoho-web.com/html/memo/htmlls.htm

https://qiita.com/ikea_shark_jk/items/deba88cbb8e5756fc929

[link-1]:https://sd-webworks.com/column/html-living-standard/
[link-2]:https://www.tohoho-web.com/html/memo/htmlls.htm
[link-3]:https://qiita.com/ikea_shark_jk/items/deba88cbb8e5756fc929
[honnyaku]:https://momdo.github.io/html/
[kousiki]:https://html.spec.whatwg.org/multipage/
【Mosaic-HAT】Raspberry Pi+RTKLIB＋RTK2GOのログを解析してみよう

しばらくご無沙汰しておりました。
前回、[Raspberry Pi+RTKLIBでRTK移動局を作ってみよう２](https://qiita.com/KIT-tokunaga/items/3a9b46843c847fb4157b)で、インターネット上の無償NTripサーバRTK2GOを経由してMosaic-HAT基準局と移動局を繋げましたが、どうもFIXしないようです。
[Raspberry Pi+RTKLIBでRTK移動局を作ってみよう](https://qiita.com/KIT-tokunaga/items/f9a7249bdba8b1aceb3b)の、RTKLIBの補正アルゴリズムを使えばFIXしますが、それではMosaic-HATの機能を使いこなしたことになりません。

ということで、問題分析をすることになりました。
＃本件、セプテントリオ様には、大変お世話になりました。

##問題の切り出し
RTKLIBの補正ではうまく言っていることを考えると、
〇基準局：Mosaic-HAT→RasPi（COM1でRTCM3メッセージを伝達）
〇基準局：RasPi→RTK2GO（RTKLIBのstr2strでRTCM3メッセージを伝達）
？移動局：RTK2GO→RasPi（RTKLIBのstr2strでRTCM3メッセージを伝達）
？移動局：RasPi→Mosaic-HAT（COM1でRTCM3メッセージを受信－とくに設定なし）
と、基準局はシロ。
さらに、移動局でRTKLIBのrtk2rsvを使った補正は成功しているので、
RTK2GO側は、リクエストが来れば正しいRTCM3を伝達してくれていることに。
よって、str2strによるMosaic-HATへのPushあたりに問題がありそうです。
といっても、確認するのは、ボーレート設定くらいしかない・・・

そこで重い腰を上げて、Mosaic-HATの内部状態を調べることにします。
Mosaic-HATは、SBFというバイナリメッセージで、リアルタイムの状態情報を知らせてくれます。
これを取得し、アナライザで調べます。

RasPiとMosaic-HATの接続はそのままに、Mosaic-HAT上のマイクロUSBをPCに接続します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1255232/f16a1841-5819-99b1-6008-a6e1ba7bda26.png)

##ウェブアプリの設定
こちらはSBF出力設定のために使います。
PC上からブラウザを開き、192.168.3.1にアクセスしてウェブアプリを開きます。
ここでメニューからNMEA/SBF Outを選択し、SBFを指定ポートに出力させます。
NMEA/SBF Output StreamsのNew SBF streamを選択し、下図のように設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1255232/68e8e459-ae12-c5e1-a1eb-ef692e88d26d.png)

終わったら、OK→OKします。

##RxLoggerの設定
SeptentrioからRxToolアプリをダウンロードし、DataLinkを実行します。
RxToolについては、[こちら](https://qiita.com/KIT-tokunaga/items/cc2b02320fd9e6e9134e)

Connection1から6までの設定がありますので、いずれか（ここではConnection1）の"TCP/IP Client"と記載されているBOXをクリックし、ポート設定を行います。ここで、SerialタブのPortに、USB COM Port1(COM4)を選択し、OKとします。また、Log Fileにチェックをし、ログファイルを指定してください。拡張子は何でもよいです。下図のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1255232/19e67647-23bf-3e36-cffb-abecd5d913dc.png)

基準局、移動局が運用状態になっているかを確認したのち、左上のConnectを押して、ログ開始です。
Connection1の右下の枠が赤から緑に変わります。
なお、緑枠になっても、"I/O 0.0/0.0 kBps"である場合は、ウェブアプリの設定を再度実行しSBF出力を開始させます。下図のように、USB1(Out:SBF XXkb/s)の通信パスができていれば成功です。
数分間、記録した後、Disconnectで停止します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1255232/7fdf7af3-3554-9552-993d-adf8b4c185a5.png)

これでロギングは終了です。

##SBF Analyzerでの確認
先ほど記録したSBFメッセージはバイナリデータのため、そのままでは読めません。
RxToolのSBF Analyzerで確認します。
アプリを起動したのち、SBF Fileに先ほどのログを設定します。Analyzerのチェックは、デフォルトのままだと大量のプロット図を吐き出すので、SelectのチェックをPVTのDifferential Corrections Ageのみにします。さらにPlot Optionsを選択し、下図のようにTo Plot1以外のチェックを外し、1枚だけの図にします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1255232/bfbbd0e2-0d01-fdaf-c76f-2b1499c75950.png)

これで出力された図は下図のようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1255232/7a5ff393-e654-c41d-1aa7-0316af8da255.png)

基準局からは、1004, 1006, 1033の3タイプのメッセージを送信しているはずなのに、Message Type 1004しか表示されていません＝受信していません。ちなみに別のマウントポイントに設定しても、やはり1004しか受信していませんでした。RasPiのstr2strで転送し損ねているのか、Mosaic-HATが受けきれていないのか？
問題が絞り込めました。

##まとめ
SBFメッセージの収集方法を知れば、トラブル解析はかなり楽になります。
まだ解決はしていませんので、次回は解決したという報告をお送りしたいです。
Laravel snappyでちょっとハマったメモ

Laravel snappy を使用してPDF出力をしていたところ
 `The process has been signaled with signal "11".`というエラーに悩まされたので備忘録です

barryvdh/laravel-dompdf を使用した場合でも
表示に凄く時間がかかる原因の一つであることも確認しました
 

# 環境
WSL2 Ubuntu 20.04.3
docker 4.2.0 で centos 8 を起動

php：8.0.10
Laravel：8.57.0

# エラーが起きたコード

まずはエラーが起きたコードを簡単に再現します

``` php:pdf_image.blade.php
<html>
    <head>
        <link rel="stylesheet" href="{{ asset('/common/css/pdf_image.css') }}">
    </head>

    <body>
        <div class="text">
            <p>画像を表示します</p>
        </div>
        <div class="image">
            <img src="{{ asset('/common/images/photo.jpg') }}">
        </div>
    </body>
</html>
```

``` php:pdfController.php
use Barryvdh\Snappy\Facades\SnappyPdf as PDF;

class pdfController extends Controller
{
    public function download()
    {
        $html = view('pdf_image')->render();
        $pdf = PDF::loadHTML($html)->setOption('encoding', 'utf-8');

        return $pdf->download('image_view.pdf');
    }
}
```
上記コード実行したところ `The process has been signaled with signal "11".`というエラーが発生しました
このエラーがググってもまぁ解決策が何もわからない

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588936/9c0d87f1-c781-187d-c74e-efa1e9875494.png)

# 原因

CSSや画像の表示に `asset()` を使用していたことが原因でした
`asset()` から `public_path()` に書き換えることでエラー解消しPDFダウンロードができるようになりました

PDFダウンロードの前にブラウザ上でプレビュー画面を用意してましたが
プレビューでは`public_path()` で表示ができず `asset()`で表示できていたので
そのまま行けるだろうと思ってた為にハマってました


``` php:pdf_image.blade.php
<html>
    <head>
        // asset から public_path に変更
        <link rel="stylesheet" href="{{ public_path('/common/css/pdf_image.css') }}">
    </head>

    <body>
        <div class="text">
            <p>画像を表示します</p>
        </div>
        <div class="image">
            // asset から public_path に変更
            <img src="{{ public_path('/common/images/photo.jpg') }}">
        </div>
    </body>
</html>
```
draw.io (diagrams.net)  忘れがちな操作方法の覚書

# この文書は

多機能・高性能・無料etc の作図ツール [draw.io](https://www.diagrams.net/) の特定の操作方法についての記事です。
とても便利なのですが、これまで使ってきたVisioやOffice系ツールと操作方法が異なるところが多く、しばらく使わないと忘れてしまいがちなので、調べて知ったことをこの記事にまとめていこうと思います。

## List系コンポーネントへの要素追加

- List Item を ドラッグ＆ドロップで追加する
![qiita.drawio - diagrams.net 2021-12-23 16-00-35_Trim_2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4608/e98d4bc3-152d-59f1-3a62-5b051a909fdd.gif)

- テーブル中のList Itemをコピペした上で、テーブルの中に入れなおす
![qiita.drawio - diagrams.net 2021-12-23 16-01-54_Trim.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4608/1841b92b-235a-90ce-cfea-3b1ff470e810.gif)

- コンポーネントを選択して、フォーマットパネルの `配置` タブ `テーブル` のメニューより追加
![qiita.drawio - diagrams.net 2021-12-23 16-03-43_Trim.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4608/acf6ec07-800d-2c27-df77-473b4d8513da.gif)


## コンポーネント内のラベルの値変更
コンポーネントをダブルクリックすると、ラベル部分にフォーカスが入る。
ラベル部分をダブルクリックだとフォーカスが入らない場合があるので、選択した時のエリア(青枠)内をダブルクリックする。
![qiita.drawio - diagrams.net 2021-12-23 16-04-57_Trim.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4608/f93a7a3d-f151-caec-51b1-c22ec2125415.gif)


## コンポーネントの変更
- 新しいコンポーネントをコンポーネント選択エリアで選択し、ドラッグ＆ドロップで、変更したいコンポーネントに重ねる
![qiita.drawio - diagrams.net 2021-12-23 16-06-54_Trim_3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4608/6200b159-59a8-b777-f1b4-14a2e6849ba8.gif)

- キーボードショートカットを使って複数コンポーネントを一括置換する方法もある　→ [参考](#参考) のリンク参照

## よく使うコンポーネントをストック
左端のコンポーネント選択エリア `スクラッチパッド` のエリアにコンポーネントをドラッグすると保存される。
使う場合は、スクラッチパッドからドラッグして配置すれば :ok: 
スタイル変更を保って保存しておけるのが便利。
![qiita.drawio - diagrams.net 2021-12-23 16-07-50_Trim.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4608/453dfbe3-adf4-e4eb-ae99-4dac9e9e3ece.gif)


## 参考
https://drawio.freshdesk.com/support/solutions/articles/16000061977-add-rows-to-erd-tables-lists-and-uml-classes

https://zukai.kakinota.net/how-to-zukai/appli/draw.io/basic/replace-existing-shapes/
[HTTP]GETとPOSTの違い

#HTTPとは
**HTTP**（Hypertext Transfer Protocol）とは、webサイトを表示する際に**webサーバとクライアント間の情報のやり取りを行うためのプロトコル**。情報のやり取りは、クライアントがリクエストを送り、サーバがレスポンスを返す仕組みになっている。この仕組みにおいて、クライアントがリクエストを送るメソッドに```GET```と```POST```がある。この2つのメソッドの違いについて簡単にまとめる。

#GETメソッド
**GETメソッド**は、データの取得に使用される。
■ **パラメータの渡し方**
パラメータのキーと値をURLに付加してリクエストを送る。
例：http://<span>sample<span>?keyword1=value1&keyword2=value2
上記のように、URLの後ろに```?```、そのあとに```<キー>=<値>```の形で付加される。パラメータが複数の場合は、```&```で繋ぐ。

■ **送信するデータ量に制限がある**
GETはURLの後ろにパラメータを付加するため、URLの文字数で送れるデータ量が制限されてしまう。

■ **テキストデータのみ送信可能**

#POSTメソッド
**POSTメソッド**は、データの登録に使用される。
■ **パラメータの渡し方**
パラメータは、HTTPのBodyに格納される。そのため、パラメータはURLから見えない。

```
keyword1=value1&keyword2=value2
```
GETと同じく、```<キー>=<値>```の形で書き、パラメータが複数の場合は```&```で繋ぐ。

■　**送信するデータ量の制限がない**
HTTPのBodyにパラメータが格納されるため、基本的には制限がない。
そのため、GETより多くのデータ量を送ることができる。

■ **テキストデータ、バイナリが送信可能**
個人的によく使うgitのサブコマンドとオプション

この記事は[PONOS Advent Calendar 2021](https://qiita.com/advent-calendar/2021/ponos)の25日目の記事です。

昨日は私の[UnityのLightAnchorについてもう少し詳しく](https://qiita.com/block/private/b61e970d7c0e0fbfe8fc)でした。

---

## よく使っているgitのサブコマンドとオプションの紹介

### `git status`
手元の変更されているファイルやディレクトリを確認できます。

### `git diff`
変更内容を確認できます。
`git diff ファイル名`で特定のファイルやディレクトリの変更だけを確認できます。
他にも様々なオプションがありますがほぼこれだけ使っています。

### `git add .`
変更のあるファイルや新規に追加したファイルをすべてステージングします。
変更ファイルが複数ある時に1個づつaddしていくのは辛いので`git add .`で一括追加します。

### `git reset .`
すべてのステージングを取り消します。

### `git restore .`
コミットされていないすべての変更をなかったことにします。
色々とコードを書き換えて見たけどやっぱり戻そう、という時に使います。
`git restore ファイル名`で特定のファイルだけ変更を取り消すこともできます。
古い`git`では使えない事があるのでその時は`git`を更新しましょう。

### `git fetch -ap`
通常の`fetch`の動作に加えてリモートでは削除されたブランチだが手元に情報が残っているかつチェックアウトしていないブランチの情報を削除できます。
`git branch -a`とかでいつまでも削除されたリモートのブランチが残るのが嫌なので`fetch`は基本的に`git fetch -ap`で行っています。

### `git commit -m "コメント"`
コミットです。

### `git push -f`
強制`push`です。
リモートに上げたコミットのコメントが間違っているときなどに次に紹介する`git commit --amend`してから`git push -f`したりします。

### `git commit --amend`
直前のコミットのコメントを書き換えるのに使います。
コミットコメントを書き間違えた時にお世話になります。

### `git log -n 確認したいログの数`
間近のコミット履歴を確認するのに使います。
ほとんどの場合1~3個前くらいの履歴で事足りるので`git log -n 1`とかで使います。

### `git merge origin/ブランチ名`
手元に最新のmasterやdevelopをマージする時に使います。
`git fetch -ap`で最新の情報を取ってきて`git merge origin/develop`や`git merge origin/master`で最新の変更をマージします。
これがないとdevelopに切り替えて`pull`して元のブランチに切り替えて`git merge develop`とかしなきゃいけないので手間です。

### `git switch ブランチ名`
ブランチを切り替えます。
新しくブランチを作って切り替える場合は`git switch -c ブランチ名`です。
こちらも古い`git`では使えない事があるのでその時は`git`を更新しましょう。

### `git branch -m ブランチ名`
ブランチ名を変更します。
ブランチ名もよく間違えるので使います。

### `git author`
自作のサブコマンドです。Rust製です。
実際に使ってますが若干宣伝です。([2年前のアドカレでも宣伝してます](https://qiita.com/block/items/d828c79ad1a4166c52d2))
`user.name`と`user.email`を確認/設定するのに`git config user.name`, `git config user.email`を叩くのが嫌で作りました。
`git author`で`sample_user <sample@email.com>`のように確認できます。
`gut author sample_user sample@email.com`でセットもできます。
間違えて違うユーザーでコミットした時にAuthor/Commiterを書き換える`replace`コマンドもあるのですが使いづらいので次のバージョンで改修予定です。

## まとめ
よく使うサブコマンドとオプションの紹介でした。
`git commit --amend`とか`git branch -m ブランチ名`とかを覚えておくと間違えた時に修正がすぐにできるので便利です。

---

良いお年を
PulumiでawslogsdeliveryアカウントのIDを取得する関数が使えるようになった

## はじめに

数ヶ月前に、awslogsdeliveryのアカウントIDを取得する関数の`getLogDeliveryCanonicalUserId`が使えるようになりました。これで、CloudFrontのログをS3に出力する権限の設定が読みやすくなります!

https://www.pulumi.com/registry/packages/aws/api-docs/cloudfront/getlogdeliverycanonicaluserid/

## 使い方

使い方はかんたんです。
上記のサイトからまるまる引用してきます。

```diff_typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

+const exampleLogDeliveryCanonicalUserId = aws.cloudfront.getLogDeliveryCanonicalUserId({});
const exampleBucket = new aws.s3.Bucket("exampleBucket", {grants: [{
+    id: exampleLogDeliveryCanonicalUserId.then(exampleLogDeliveryCanonicalUserId => exampleLogDeliveryCanonicalUserId.id),
    type: "CanonicalUser",
    permissions: ["FULL_CONTROL"],
}]});
```

今までは、以下のようにIDをベタで書いていた方が多いと思います。

```ts
const awslogsdeliveryAccountId =
  "c4c1ede66af53448b93c283ce9448c4ba468c9432aa01d700d3878632f77d2d0";
const exampleBucket = new aws.s3.Bucket("exampleBucket", {
  grants: [
    {
      id: awslogsdeliveryAccountId,
      type: "CanonicalUser",
      permissions: ["FULL_CONTROL"],
    },
  ],
});

```

このコードを後から見た時に、この文字列が何なのかが分からなくなるなという方が多かったのではないかと思います。

## awslogsdelivery とは?

CloudFrontからS3にログ出力をするときに使われるアカウントです。

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html

ログの出力先のS3に対して、`awslogsdelivery`アカウントにFULL_CONTROL権限を付与することで、バケットへの書き込みができるようになります。先程出てきた`c4c1ede66af53448b93c283ce9448c4ba468c9432aa01d700d3878632f77d2d0`という文字列は、このアカウントのcanonical IDになります。

## Terraformでももちろん使える

Terraformでは`aws_cloudfront_log_delivery_canonical_user_id`のData Sourceにありましたので、Terraformをお使いの方も利用できます。

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/cloudfront_log_delivery_canonical_user_id

## 最後に

getLogDeliveryCanonicalUserId は地味ながら個人的には嬉しい機能だと思います!