OmniAICreator/Qiita-1.07M huggingface dataset:
OCIでAnsible実行時、mountモジュールで /etc/fstab パースエラーに四苦八苦した話

OCI（Oracle Cloud Infrastructure）を利用することがあり、その中でAnsibleのmountタスクがエラーになるケースがあったので紹介します。
といっても仕事の中でのことだったのと、あれこれ細かな課題対応が続発するなかでのこと。エラー発生時のキャプチャや具体的なエラーメッセージの内容を示せませんがご容赦ください。

# 発生条件

* OCIのDB System（VM/BM）やComputeを利用している。
* mountモジュールで state=mounted を指定してマウントを行おうとしている。

他にも条件があるはずです。Googleで検索をしても類似事象を見つけられなかたことからそう推測しています。
ひとまず分かったのはここまでです。

Playbookではmountモジュールに state=mounted を指定しています。
このためモジュールは、「指定されたデバイスが指定されたマウントポイントにマウントされていることを確認、されていなければマウント、加えて /etc/fstab ファイルにそのマウントの定義情報を書き込んで再起動後もマウントが約束されるようにする」ところまで行ってくれます。

# 発生事象

mountモジュールの処理がエラーとなりPlaybookの実行が中断されます。
エラーメッセージとしては /etc/fstab のパースに失敗した云々の内容が出力されます。

```
fatal: [(hostname)]: FALED! => {"changed": false, "msg": "Error mounting (mount point): mount: /etc/fstab: parse error: ignore entry at line (line number). \nmount: can't find (mount point) in /etc/fstab\n"}
```

/etc/fstab の内容を確認すると、本来「既存エントリーの次の行に、つまり改行を挟んで、新規エントリーを追加」しなくてはならないところが「既存エントリーの末尾にそのまま、つまり改行なしに、新規エントリーを追加」してしまっている状態。

期待される書式：

```
<device><TAB><mount point><TAB>...<TAB><file system check order><LF>
<device><TAB><mount point><TAB>...<TAB><file system check order><LF>
<device><TAB><mount point><TAB>...<TAB><file system check order><LF>
...
```

実際の書き込まれた書式：

```
<device><TAB><mount point><TAB>...<TAB><file system check order><LF>
<device><TAB><mount point><TAB>...<TAB><file system check order><device><TAB><mount point><TAB>...<TAB><file system check order><LF>
...
```

なるほどこれではパースに失敗するだろうな、仮にこの状態で /etc/fstab に基づきマウントが実行されたら既存エントリーで定義されたマウントも一蓮托生でエラーになるだろうな、という状態です。

ちなみにこの不正な「新規エントリー」部分をvi等で削除したり、「既存エントリー」と「新規エントリー」の間に改行を挿入してやったりしてからPlaybookを再実行すると今度は正常に処理ができます。後者の改行挿入はともかく前者の「一旦消せば次は成功」という動作はちょっと不可思議です。

# 回避策

mountモジュールのまえにlineinfileモジュールで「先頭に改行文字を持つfstabエントリーを挿入」してしまいました。とても雑な対応ですがこれで後続のmountモジュールは動作するようになりました。

# 原因

不明。

なんとなくの推測としては、Ansibleのmountモジュールは /etc/fstab ファイルの末尾、既存の最後のエントリーの行末に、改行文字がもともと入っていることを前提にしているのではないか、ところが私が作業していた環境では（何らかの理由で）そうなっておらず、「既存エントリーの直後に、改行なしに、新規エントリーを挿入」ということになったのではないか、──と想像しています。
tableタグをJavascriptで操作する。HTMLTableElementについて

HTMLtableElementというインターフェースを使うことで、**tableタグをJavaScriptで操作できる**ことが判明。

HTMLTableElementの概要とは何か？と使い方のまとめ。



<br>
##HTMLTableElementとは？

HTML5から実装された機能で、tableタグをオブジェクトに変換し**行列の状態を取得したり、行列を追加したりなどの操作ができる**。

HTMLElementであるタグ（要素）をAPIで操作する形となる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/e731496e-ff3a-68ae-0632-04f6e880fd1e.png)


これを使うと、例えばtableタグ内のtrタグは、`rowsプロパティ`で表される。

列を追加したい場合は`insertRow()`メソッドを使うことで、trタグを追加できる。


###・HTMLTableElement関連のAPI
HTMLTableElementで用意されているのは、tableタグ直下のthタグ、trタグなどの操作のみ。

trタグ内のtdタグの取得や追加にはHTMLTableRowElementを使う。






<br>
##なにが便利なのか？
tableタグをそのまま処理できるということに尽きる。

例えば、以下のような、３行３列のtableは、trタグやtdタグで表される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/748a2316-e618-079b-2d87-cf86d1d2766c.png)


```html:tableタグ
<table id="xx">
  <tr>
    <td rowspan="1" colspan="1"><br></td>
    <td rowspan="1" colspan="1"><br></td>
    <td rowspan="1" colspan="1"><br></td>
  </tr>
  <tr>
    <td rowspan="1" colspan="1"><br></td>
    <td rowspan="1" colspan="1"><br></td>
    <td rowspan="1" colspan="1"><br></td>
  </tr>
  <tr>
    <td rowspan="1" colspan="1"><br></td>
    <td rowspan="1" colspan="1"><br></td>
    <td rowspan="1" colspan="1"><br></td>
  </tr>
</table>
```

これを、tableタグのまま、行や列の数を取得したり、追加することができる。



<br>
##実例

###1. 行と列数の取得
実例として、列数と行番号を取得してみる。

取得したtableタグを変数`table`（任意）とし、その行プロパティの長さを取得する場合は、

・`table.rows.length`

同様の考え方で、取得した行のセル数を取得するには、HTMLTableRowElementのcellsを使う。1行目のセル数を取得する式は以下となる。

・`table.rows[0].cells.length`

```js:行列数の取得
function RowCellLength(table){
    console.log("row-length:", table.rows.length)
    console.log("cell-length:", table.rows[0].cells.length)
}
```

**▼実際の実行例**
以下のようなテーブルのid名をxxとして、行列数を取得する関数RowCellLengthに渡すと、行数と列数を返してくれる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/bafb9406-2d99-8ada-4fda-e8202cbe2dec.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/93c16294-6157-9ef6-6a13-49cd6a04eb9d.png)



<br>
###2. 行の追加
応用編としてテーブルの3行目に３列追加する場合は以下となる。

```js:
function createTable(table){
 
    //3行目に行を追加
    newRow = table.insertRow(2)

    //セルを3つ追加
    for (let i=0; i <3; i++ ){
      newCell = newRow.insertCell(0) //先頭にセルを追加
      newCell.colSpan = "1"  //colspan="1"をセット
      newCell.rowSpan = "1"　//rowspan="1"をセット
      newCell.innerHTML = "new cell"  //セルの中に値を入力
    }
}
```

・`table.insertRow(i)`
i番目に行を追加する。0スタート。

・`row.insertCell(i)`
指定した行のi番目にセル（tdタグ）を追加する。0スタート。

・`cell.colSpan = "1"`
指定したセルの属性にcolspan="1"をセットする。

・`row.colSpan = "1"`
指定したセルの属性にrowspan="1"をセットする。

・`cell.innerHTML = "文字列" `
指定したセル（tdタグ）内に文字列を挿入する。タグも挿入可能。


上記処理で、以下のようなセルが生成される。

```html:
<tr><td colspan="1" rowspan="1">new cell</td>
```

これをfor文で3回繰り返すとセルが３つになる。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/64a0dcd2-2715-eff3-edbb-333aafcbba1e.png)
紙に墨で書かれた絵の、紙の部分を透明にする

# これは何？

紙に黒で書かれた絵があって。
何も書かれていない部分→透明
黒い部分→不透明の黒
という変換をしたくなった。

墨が薄い部分は薄さに合わせた半透明にしたい。

この作業のために簡単な画像処理をしたので、その記録。

# 作戦

1. 紙をスキャンして、グレースケール png にする。
2. 紙のテクスチャが無くなり、墨のムラを減らすようにレベル調整する。
3. png を HSL に変換し、L(輝度) だけの画像を得て、白黒反転する → 輝 と名付ける。
4. 元画像と同じサイズの真っ黒な画像を用意する → 黒 と名付ける。
5. 赤=「黒」, 緑=「黒」, 青=「黒」, 不透明度=「輝」 で一枚の画像にする。

## レベル調整

こんなコマンドで:

```bash
convert scanned.png -fx "(u-0.3)/0.5" level.png
```

暗部3割と明部2割を捨てて残りの5割を活かすので、こんな計算になる。
`0.3` と `0.5` はスキャンする素材やスキャナによって調整する。

## png を HSL に変換し、L(輝度) だけの画像を得て、白黒反転する

こんなコマンドで:

```bash
convert level.png -colorspace HSL -separate -negate lumi.png
```

実行すると `lumi-0.png`, `lumi-1.png`, `lumi-2.png` の3つの画像ができてしまう。必要なのは `lumi-2.png` だけ。

## 元画像と同じサイズの真っ黒な画像を用意する

こんなコマンドで:

```bash
convert scanned.png -evaluate multiply 0 black.png
```

全画素に0を乗じれば真っ黒になる。

## 5. 赤=「黒」, 緑=「黒」, 青=「黒」, 不透明度=「輝」 で一枚の画像にする。


こんなコマンドで:

```bash
convert black.png black.png black.png lumi-2.png -combine alphapaper.png
```

各プレーンを結合できる。 `convert 赤 緑 青 不透明度 -combine 結果` だと思う。

## 処理の例

|段階|画像|
|:--|:--|
|スキャン画像|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6854/1bd3c210-fc79-8ad7-52d8-93177ef56304.png)|
|レベル調整後|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6854/5f6684cf-665b-586c-4dbb-a14d3b56adac.png)|
|反転した輝度プレーン|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6854/189b70f1-3c6c-09e0-9d04-c3e0e5d2ac07.png)|
|出来上がった透過画像|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6854/1e37274a-f3c6-e14e-6ded-545c666506f6.png)|
|透過画像との合成の例|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6854/f91dd83e-3836-61c6-6f88-fec3af6da113.png)|
寄生虫のようなマルウェアについて - KOSENセキュリティコンテスト2020

**注意：この記事は「KOSENセキュリティーコンテスト2020」で出された「寄生虫のようなマルウェア」について調べて、再び解いてみたという記事です。予めご了承ください**

# 初めに
この記事は、そこで出された「寄生虫のようなマルウェア」という問題を解く鍵となった`Helminth`というマルウェアについて、調べて再び解いてみました。なぜ、再びなのかというと、DNSクエリは取り出したのですが、デコード時にチームメイトとエスパーでFLAGを通してしまったからです。
また間違い等ありましたら、教えていただければ幸いです。

# 寄生虫のようなマルウェア

問題の内容は次のようです。

![kiseichu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664958/39bf4fd3-555e-6299-0169-8ea4ff42d1f2.png)

![helminthpcapng.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664958/e4f11d2b-06f1-1212-d9a6-cc846bceb00b.jpeg)

`Helminth`は、マルウェアでよく使われる`TXTレコード`ではなく`Aレコード`を使うようです。(詳しくは参考文献の方を参照していただければと思います。)

`Helminth`は`Aレコード`の制約で、載せられる情報は`8bit`までなので、それに収めるためにASCIIコード`(7bit)`が用いられていて、ドメイン名に載せる情報の表現がASCIIコードになってるみたいです。

### 通信内容を取り出す

まず`tshark`を使って、127.0.0.1の`DNSクエリ`を取り出します。

![127001DNS.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664958/459681c6-95fe-5ed7-0074-4f184acef660.jpeg)

パケットの中身を見ると`s.com`の通信で行われていることがわかるので、次のように実行します。([Google CTFとTrendmicro CTF - 生産性のない話](https://blue-blue.hatenablog.com/entry/2017/07/01/003336)より拝借しました。)

```sh
tshark -r helminth.pcapng -T fields -e dns.resp.name | grep s.com | awk -F . '{print $1}'
```

これを実行すると、余計なものも出てくるのでそれは削除します。

```:取り出した後
00L01000JQ30D0A433A5C55736572735C7368753E747970652022433A5C
00L01001YRE50726F6772616D2046696C65735C526F6F6B69655C435446
00L01002QSF5C464C41472E74787422200D0A464C41477B48656C6D696E
00L010034YQ74685F6E69686F6E676F5F64655F6B697365696368755F72
00L01004K33617369796F21777777777D0D0A
```

### 通信の詳細
次に通信は以下の形式になるようなので`DATA`の部分だけ取り出します。

```
00[botid][cmdid][partid][(random)][DATA].domain

cmdid ← 実行コマンド応答時の cmdid
partid ← 実行結果を分割したときの通し番号(001,00Aとか)
DATA ← 実行結果(ASCIIコード 16進数表現)
```

![first-address.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664958/5f576012-d4e1-5496-e6d6-b6573959bea8.jpeg)

`botid`はASCIIコードの10進数表現をIPアドレスとして埋め込んでいるようなので、最初のIPアドレスが`76.0.0.0`なので`L`です。
また、`random`の部分の文字数は3文字のようなので、それも取り除きます。

```:最終の文字列
0D0A433A5C55736572735C7368753E747970652022433A5C
50726F6772616D2046696C65735C526F6F6B69655C435446
5C464C41472E74787422200D0A464C41477B48656C6D696E
74685F6E69686F6E676F5F64655F6B697365696368755F72
617369796F21777777777D0D0A
```

この文字列を[cyberchief](https://gchq.github.io/CyberChef)に突っ込んでASCIIコードなので16進数でデコードします。
![cyberchief.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664958/e9bf0da8-f5f6-0ab9-7a4c-19855ab596d6.jpeg)

FLAGは`FLAG{Helminth_nihongo_de_kiseichu_rasiyo!wwww}`でした。

### 手順

- DNSクエリを取り出す
- `00[botid][cmdid][partid][(random)][DATA].domain`の形式に従って、`DATA`の部分を取り出す
- `botid`は、一番最初のサーバーからの返答で判断し、`random`の部分は3文字であるので削除する
- 上記の手順を踏んだら、任意の方法でデコードする

# 最後に
私にとって、マルウェアの通信を解析するのが初めてでした。その中で、しっかりどういう形で送信されているのか、特徴を知った上で改めてFLAGを得ることができました。


# 参考文献
[1] [標的型攻撃で使われたマルウェアを解析して C2 サーバを作った。そのマルウェアは DNSトンネリングを行う珍しいものだった。](http://shutingrz.hatenablog.com/entry/2017/05/25/225636)
Pythonでシグナル処理 〜 終了時に指定された処理を実行する 〜

## TL;DR
- Herokuでは再起動時に**SIGTERM**を送信して、10秒間応答がなければ**SIGKILL**でプロセスを殺すよ
- Pythonでは標準に含まれる`signal`というライブラリでシグナル関連の処理をするよ

## 結論
```signal_handling.py
import signal

def signal_handler(signal_number, frame):
    # ここにシグナルを受け取ったときに実行したい処理を書く
    pass

signal.signal(signal.SIGTERM, receive_signal)
```

## Motivation
Herokuでは、**おおよそ24時間毎にdynoが自動で再起動されてしまいます。**
例えばdynoがデータベースサーバーと常時通信する必要がある場合、急に落ちるとデータベースに不整合が生じる可能性があります。
これを防ぐため、**dynoの再起動時に特定の処理を実行するようにする**必要がありました。
今回はdynoの再起動時に発行される**シグナル**を用いてそれを実現することにしました。

## そもそもシグナルとは
> シグナル（英: signal）とは、Unix系（POSIX標準に類似の）OSにおける、限定的なプロセス間通信の形式を使って、プロセスに対し、非同期で、イベントの発生を伝える機構である。シグナルが送信された際、OSは宛先プロセスの正常な処理の流れに割り込む。
> [シグナル - Wikipedia](https://ja.wikipedia.org/wiki/%E3%82%B7%E3%82%B0%E3%83%8A%E3%83%AB_(Unix)) より

要するに、プロセスの外で発生したイベントに対応するために、プロセスに伝わる信号のことです。
例えば**Ctrl + C**でプロセスを終了するときや、**タスクマネージャー（アクティビティーモニター）でプロセスを終了する時**などにも、プロセスの終了を通知するシグナルが送信されます。

## シグナル一覧とその意味
重要なものだけ紹介します。

| シグナル名 | シグナル番号 | 詳細                   |
| ---------- | ------------ | ---------------------- |
| SIGINT     | 2            | Ctrl+Cが入力された時   |
| SIGKILL    | 9            | プロセスの**強制終了** |
| SIGTERM    | 15           | プロセスの**終了**、`kill`コマンド     |

それぞれのシグナルには、規定の処理が設定されています。
今回はそれを変更することでハンドリングを実現します。
しかし、**SIGKILLについては、あくまで「強制終了」なので、既定の処理云々以前に問答無用でプロセスが消えます。**
Ctrl+Cを入力しても一発では落ちないアプリも、これを使っていたわけです。

参考: [Linuxのシグナルまとめ -- ぺけみさお](https://www.xmisao.com/2013/11/10/linux-kill-signals.html)


## ソースコードの解説
```signal_handling.py
import signal

def signal_handler(signal_number, frame):
    # ここにシグナルを受け取ったときに実行したい処理を書く
    pass

signal.signal(signal.SIGTERM, receive_signal)
```

`signal`ライブラリ内の`signal.signal()`という関数を用いてハンドリングを行います。
第一引数にはハンドルしたい`Signals`型の引数が入り、`signal.<シグナル名>`の形式になっています。
今回の場合は`SIGTERM`です。
第二引数には**実際に呼び出したい関数の<font color=red>関数オブジェクト</font>を渡します。**

なお、呼び出される関数は2つの引数を受け取ります。
**第一引数はシグナルの番号受け取り、第二引数にはフレームを受け取ります。**
ここではフレームは無視しても問題ないでしょう。

参考: [signal --- 非同期イベントにハンドラを設定する Python 3.9.1rc1 ドキュメント](https://docs.python.org/ja/3/library/signal.html)
XplentyによるXMLデータ処理

Xplentyでは、CSVファイルの他にJSONやXMLデータフォーマットを簡単に処理することができます。この記事ではXplentyでXMLを処理しやすくする機能を例に紹介します。

#概要とリソース
デモのために、処理するサンプルXMLファイルへのリンクは[こちら]((https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms762271(v=vs.85)))です。 

ファイルは以下の画像のようなXML構造になっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/588842db-6374-6ea8-f8f6-b3b366e780a8.png)

このデータを処理するための鍵となるのが、Xplentyの関数「[XPath](https://www.xplenty.com/docs/xpath/)」と「[XPathToBag](https://www.xplenty.com/docs/xpathtobag/)」です。これらをデータパイプラインで検証してみましょう。

Setting up the Xplenty Data Pipeline
#Xplentyデータパイプラインの設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/5dfb3f07-bf5a-f117-8327-6facb9e9acb8.png)

Xplentyパイプラインの構成要素をそれぞれ順番に説明していきます。

1. **XML_Source**: 上記で共有されたリンクからのXMLファイルをクラウドストレージにコピーし、File Storage Source Componentを使用して読み込みます。

2. **XPathToBag**: このステップでは、XPath `/catalog/book`に一致するようにXPathToBag関数を呼び出します。これは、Bagデータ型の`<catalog> </catalog>`の下にあるすべての書籍を取得します。　例） `XPathToBag(data,'/catalog/book')`

3. **Flatten_Books**: Flatten()関数を使用して、構造体の各レコードを個別のbookレコードとして取得します。 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/28515b45-ee28-f4a1-a22a-97c45fb02dbf.png)

4. **XPath**: このステップでは、XPath関数を使用して、book構造体の個々の要素を取得することができます。ここでは、上記の`<book> </book>`構造体にXPathを設定したコンポーネントを見てみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/4ba45541-64df-1818-f444-822acc7f6dd7.png)XPathとその例についての追加のリファレンスは、[freeformatter.com](https://www.freeformatter.com/xpath-tester.html)のようなXPath評価ツールを参照してください。

5. **Destination**: XML から処理された個々のフィールドは、Destination（この例では BigQuery テーブル）に保存されます。

以下の画像は、出力されたレコードの例を示しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/837f768e-24ec-3966-fa8a-43111d8c4847.png)

ファイルやAPIレスポンスからXMLをパースしてテーブル形式の構造に変換することは、データを検索する上で重要です。また、他のデータセットとのブレンドにより、データ分析をより容易にすることができます。

#まとめ
信頼できるドキュメントベースの情報転送として、XMLベースのファイルやAPIがユースケースとしてよく見られます。XplentyではこうしたXMLデータを簡単に処理するための機能が用意されています。ぜひ、[Xplenty](https://www.xplenty.com/jp/)のフリートライアルでお試しください。
ソフトウェアテストの練習・学習に使えるテスト対象アプリケーション・仕様・例題など

この記事は[ソフトウェアテストの小ネタ Advent Calendar 2020 - Qiita](https://qiita.com/advent-calendar/2020/software-testing-koneta)の１日目です。
いよいよ始まりましたね。

ソフトウェアテストについて学習したり、学習した内容をもとに腕試しをしたりするのには、対象となるモノが必要です。

世の中にはこうした場合に使えるテスト対象アプリやテスト用の開発ドキュメント、問題などが公開されているので、まとめてみます。

## テスト対象アプリケーション

- [FindingBugs](http://milk0824.sakura.ne.jp/finding_bugs/)
[テスト練習用のバグだらけWebAppを公開します【Finding bugs】 - テスターちゃん【4コマ漫画】](http://testerchan.hatenadiary.com/entry/2018/04/01/210308)
    - テスターちゃんで有名なまつさんが公開してくださっているアプリケーションです。Twitter風のアプリケーションで、探索的テストの練習に使えます。
- [バグダス - デバッガー検定 -](https://store.steampowered.com/app/1002360/_/)
    - テスト対象アプリケーションと区分していいのか迷いますが、あえてバグのあるもの、ということでここでご紹介。
    - Androidアプリもあります＞[バグダス - デバッガー検定 - - Google Play のアプリ](https://play.google.com/store/apps/details?id=jp.JiruoSoftware.bugdasu&hl=ja&gl=US)


## 自動テスト対象アプリケーション

- [STARHOTEL](https://github.com/SoftwareTestAutomationResearch/STARHOTEL-Teaching-Materials)
    - SeleniumなどWebのテスト自動化を学んだことがある方には有名な、ホテル予約サイト風Webアプリケーションです。
    - htmlファイルを直接開いて動かせるようになっており、一度ダウンロードしてしまえば外部との通信が発生しません。どれだけ失敗をしても外部のサーバに負荷をかけるようなことがないので、安心して使うことができるというメリットがあります。
- [HOTEL PLANISPHERE - テスト自動化練習サイト](https://hotel.testplanisphere.dev/)
    - こちらもホテル予約サイト風Webアプリケーションです。STARHOTELに比べて作りがリッチになっています。
- 脆弱性のテストの練習につかえるもの
    - [脆弱性のテストツールをテストするためのサンプルアプリケーションまとめ - shiro庵](https://yuki-shiro.hatenablog.com/entry/2019/05/02/233955)を参照

## 仕様

- [話題沸騰ポット 要求仕様書](http://www.sessame.jp/workinggroup/WorkingGroup2/POT_Specification.htm)
    - テスト設計コンテストの題材でも使われていた要求仕様書です。コレをもとにテストを考えてみると、過去のテスコン参加者の成果物などと見比べてアレコレ気付けるというメリットもあります。
- [(お題) レッツゴーデベロッパーモバイルのスマートフォン利用料金計算](https://github.com/5000dai/free-style-battle)
    - テスト専用の練習問題ではありませんが、『ソフトウェアテスト技法練習帳』ではソフトウェアテスト技法を用いる対象として使わせていただきました


## 練習問題

-  [[例題]テニスのスコアボード: ソフトウェアテストの勉強室](http://softest.cocolog-nifty.com/blog/2008/02/post_537f.html)
- [秋山さんのテスト技法練習問題 - Togetter](https://togetter.com/li/1101218)
    - 連続したツイートで問題と解説が２８問（！）
[Python]自然言語処理ツールGiNZAを使って湯婆婆を実装してみた。



# 概要
- Qiitaの謎の湯婆婆ムーブに乗りました笑。
- 普通にPythonで湯婆婆を実装すると、ランダムで名前一文字が選ばれます。[参考](https://qiita.com/everylittle/items/aae58c241194c0e5f515)

- ランダムに名前が選ばれると、変な名前になることがあります。
  - 例：萩野千尋⇒野　とか…
- [GiNZA](https://qiita.com/cove_ht/items/63ffdd8ff237d4845566)を使って湯婆婆を実装します。
- GiNZAを使う理由
  - 名前同士の類似度を比較することで、一番高い類似度の名前が選ばれます。
  - フルネームと類似度が高い一文字を選ぶことで、より近い名前を湯婆婆に与えてもらえるからです。

# コード
```Python
import spacy
import warnings
warnings.simplefilter('ignore')

nlp = spacy.load("ja_ginza")

print("湯婆婆：契約書だよ。そこに名前を書きな。")
full_name = input("契約書：")

print()
print("湯婆婆：フン。" + full_name + "というのかい？")

max_similarity = 0
new_name = "" # 新しい名前
for name in full_name:
    similarity = nlp(full_name).similarity(nlp(name)) # フルネームと名前の一文字の類似度を出す

    if similarity > max_similarity:
        max_similarity = similarity
        new_name = name

if max_similarity == 0:
    print("意味のわからない名前だね。")
else:
    print("湯婆婆：贅沢な名だねぇ。")
    print("湯婆婆：今からおまえの名前は" + new_name + "だ。いいかい、" + new_name + "だよ。分かったら返事をするんだ、" + new_name + "！！")


```

# 実行結果

### 良い例

```
湯婆婆：契約書だよ。そこに名前を書きな。

契約書：萩野千尋

湯婆婆：フン。萩野千尋というのかい？
湯婆婆：贅沢な名だねぇ。
湯婆婆：今からおまえの名前は千だ。いいかい、千だよ。分かったら返事をするんだ、千！！
```

- 類似度の比較

||萩野千尋との類似度比較|
|---|---|
|萩|0.20879780892202868|
|野|0.15629871529551143|
|千|0.7294977215873004|
|尋|0.4434461637748406|

- 「萩野千尋」と「千」の類似度が１番高いため、「千」が選ばれました。


### 悪い例

```
湯婆婆：契約書だよ。そこに名前を書きな。

契約書：湯婆婆

湯婆婆：フン。湯婆婆というのかい？
意味のわからない名前だね。

```

- 類似度の比較

||湯婆婆|
|---|---|
|湯|0.0|
|婆|0.0|
|婆|0.0|

- 名前の中には類似度を出力できないものがあるため、類似度は0となります。

# まとめ
- GiNZAを使うことで、一番良い一文字を湯婆婆に与えてもらえる。（湯婆婆などの名前の例外あり）


# 参考
[【初心者向け】自然言語処理ツール「GiNZA」を用いた言語解析（形態素解析からベクトル化まで）](https://qiita.com/cove_ht/items/63ffdd8ff237d4845566)
[Python 3で湯婆婆を実装してみる](https://qiita.com/everylittle/items/aae58c241194c0e5f515)
ジョジョのセリフを形態素解析してみた[Python]

# 概要
- タイトルの通り、ジョジョのセリフを形態素解析してみました。
- 癖のあるジョジョのセリフは形態素解析できるのか？

# コード
```Python
import spacy 

nlp = spacy.load('ja_ginza')

jojo = ["さすがディオ！俺達に出来ないことを平然とやってのけるッ！そこにシビれる！あこがれるゥ！",
        "ディオォォオオーッ！君がッ！泣くまで！殴るのをやめないッ！",
        "JOJO、そのチェリー食べないのか？ガッつくようだがぼくの好物なんだ…くれないか？レロレロレロ"]

for text in jojo:
    for sent  in nlp(text).sents:
        for token in sent:
            print(token.i, token.orth_, token.lemma_, token.pos_, 
                  token.tag_, token.dep_, token.head.i)
    print("----------------------------")

```

# 実行結果
```
0 さすが さすが ADV 副詞 advmod 1
1 ディオ ディオ PROPN 名詞-固有名詞-一般 ROOT 1
2 ！ ! PUNCT 補助記号-句点 punct 1
3 俺 俺 PRON 代名詞 nmod 4
4 達 達 NOUN 接尾辞-名詞的-一般 obl 6
5 に に ADP 助詞-格助詞 case 4
6 出来 出来る VERB 動詞-非自立可能 acl 8
7 ない ない AUX 助動詞 aux 6
8 こと こと NOUN 名詞-普通名詞-一般 obj 12
9 を を ADP 助詞-格助詞 case 8
10 平然 平然 ADJ 形状詞-タリ advmod 12
11 と と ADP 助詞-格助詞 case 10
12 やっ やる VERB 動詞-非自立可能 advcl 14
13 て て SCONJ 助詞-接続助詞 mark 12
14 のける のける VERB 動詞-一般 ROOT 14
15 ッ ッ NOUN 接尾辞-名詞的-助数詞 punct 14
16 ！ ! PUNCT 補助記号-句点 punct 14
17 そこ そこ PRON 代名詞 obl 19
18 に に ADP 助詞-格助詞 case 17
19 シビれる シビれる VERB 動詞-一般 ROOT 19
20 ！ ! PUNCT 補助記号-句点 punct 19
21 あこがれる あこがれる VERB 動詞-一般 dep 22
22 ゥ ゥ SYM 補助記号-一般 ROOT 22
23 ！ ! PUNCT 補助記号-句点 punct 22
----------------------------
0 ディオォォオオーッ ディオォォオオーッ NOUN 名詞-普通名詞-一般 ROOT 0
1 ！ ! PUNCT 補助記号-句点 punct 0
2 君 君 PRON 代名詞 ROOT 2
3 が が ADP 助詞-格助詞 case 2
4 ッ ッ NOUN 名詞-普通名詞-一般 compound 2
5 ！ ! PUNCT 補助記号-句点 punct 2
6 泣く 泣く VERB 動詞-一般 ROOT 6
7 まで まで PART 助詞-副助詞 mark 6
8 ！ ! PUNCT 補助記号-句点 punct 6
9 殴る 殴る VERB 動詞-一般 obj 12
10 の の SCONJ 助詞-準体助詞 mark 9
11 を を ADP 助詞-格助詞 case 9
12 やめ やめる VERB 動詞-一般 ROOT 12
13 ない ない AUX 助動詞 aux 12
14 ッ ッ NOUN 接尾辞-名詞的-助数詞 punct 12
15 ！ ! PUNCT 補助記号-句点 punct 12
----------------------------
0 JOJO jojo NOUN 名詞-普通名詞-一般 nmod 3
1 、 、 PUNCT 補助記号-読点 punct 0
2 その その DET 連体詞 det 3
3 チェリー チェリー NOUN 名詞-普通名詞-一般 obl 4
4 食べ 食べる VERB 動詞-一般 ROOT 4
5 ない ない AUX 助動詞 aux 4
6 の の SCONJ 助詞-準体助詞 mark 4
7 か か PART 助詞-終助詞 mark 4
8 ？ ？ PUNCT 補助記号-句点 punct 4
9 ガッ ガッ NOUN 名詞-普通名詞-助数詞可能 advmod 11
10 つく つく VERB 動詞-一般 compound 11
11 よう よう AUX 形状詞-助動詞語幹 advcl 16
12 だ だ AUX 助動詞 cop 11
13 が が SCONJ 助詞-接続助詞 mark 11
14 ぼく ぼく PRON 代名詞 nmod 16
15 の の ADP 助詞-格助詞 case 14
16 好物 好物 NOUN 名詞-普通名詞-一般 advcl 21
17 な だ AUX 助動詞 cop 16
18 ん ん SCONJ 助詞-準体助詞 mark 16
19 だ だ AUX 助動詞 cop 16
20 … . PUNCT 補助記号-句点 compound 16
21 くれ くれる AUX 動詞-非自立可能 ROOT 21
22 ない ない AUX 助動詞 aux 21
23 か か PART 助詞-終助詞 mark 21
24 ？ ？ PUNCT 補助記号-句点 punct 21
25 レロレロレロ レロレロレロ NOUN 名詞-普通名詞-一般 ROOT 25
----------------------------
```

# まとめ
- 意外にも形態素解析ができました。

# 参考
[【初心者向け】自然言語処理ツール「GiNZA」を用いた言語解析（形態素解析からベクトル化まで）](https://qiita.com/cove_ht/items/63ffdd8ff237d4845566)
[ジョジョ・名セリフ集](http://kajipon.sakura.ne.jp/art/jojo9.htm)
フレックスボックスの解除「要素を縦並びにする」

やりたいこと→要素を横並びから縦並びに置き換えたい

```
@media(max-width:767px){
 /* 横並び解除 */
 .menu-item{
   display: block;
 }
}
```
通常時のcss
.menu-item{
  display: flex;
}
子要素横並びになる

スマートフォン向けのcss
.menu-item{
  display:block;
}
子要素が縦並びになる
xdebug 3.0.0がリリースされてCircleCIで"Use of undefined constant XDEBUG_CC_UNUSED"が出たからついでにphpdbgに移行して8倍速くなった

お疲れ様です！

2020/11/25、xdebugの3.0.0がリリースされ、CircleCIでインストールされるようになりました。
どの記事を参考にしたか忘れたのですが、config.ymlの以下の記述があったためです。

```.circleci/config.yml
#jobs:
#  build:
#    stemps:内
      - run: pecl install xdebug && docker-php-ext-enable xdebug
```

#対応

そもそもPHP7だし、カバレッジを取得したいだけなのにxdebugを使う必要が無かったので、
ここはコメントアウトし、phpunitの実行部分にphpdbg -qrrを付けてphpdbgを使うようにしました。

```.circleci/config.yml
#jobs:
#  build:
#    stemps:内
#      - run: pecl install xdebug && docker-php-ext-enable xdebug
```

```.circleci/config.yml
      # run tests with phpunit
      - run: phpdbg -qrr ./vendor/bin/phpunit -d memory_limit=512M --coverage-html build/coverage-report
```

#まとめ
PHP7だったのになんで今までphpdbg使わなかったんだろう🤔
カバレッジの時間が80分から10分になりました🥳

#参考
[php7 + phpdbgで高速カバレッジ出力](https://matchingood-engineer.hatenablog.com/entry/2017/03/17/193521)
Progate jQuery 初級編

JavaScriptが終わったのでjQueryへ突入です。
jQueryを用いることで、動作をつけるのはもちろんですが、HTMLの文章、CSSのスタイル、これらを上書き出来ることが驚きでした。
ただ、こちらで修正等をしすぎると、動作の機敏さがなくなってしまうのでしょうか。。
なんでも出来ちゃいそうで、やっぱりプログラミングは面白いですね！
ローカルでMySQLコンテナに大量INSERTしていたら"unexpected EOF"になった時の話

# はじめに
データ削除バッチのベンチマークを取るため、ローカルのMySQLコンテナにデータを800万レコードほどINSERTしていたところ、 `packets.go:36: unexpected EOF` というエラーに遭遇しました。その時に調べたことや対処したことなどをまとめます。

環境：

- Go 1.15.2
- go-sql-driver/mysql v1.4.1
- MySQL 5.7.30

# 対処と原因
## MaxLifetimeを設定する
`packets.go:36: unexpected EOF` でぐぐってみると、結構ヒットしたので比較的よくある現象のようです。それらによると、 `SetConnMaxLifetime` を設定すれば解消するとのことでした。

`SetConnMaxLifetime` に関してはこちらの記事で説明されています。その名の通り、接続の寿命を設定するもののようです。
[Re: Configuring sql.DB for Better Performance](http://dsas.blog.klab.org/archives/2018-02/configure-sql-db.html)

> MySQL では wait_timeout という設定で接続がサーバーから切られる恐れがあります。また、OSやルーターが長時間利用されていないTCP接続を切断することもあります。どのケースでも、 go-sql-driver/mysql はクエリを送信した後、レスポンスを受信しようとして初めてTCPが切断されたことを知ります。切断を検知するのに何十秒もかかるかもしれませんし、送信したクエリが実行されたかどうかを知ることもできないので安全なリトライもできません。
こういった危険をなるべく避けるためには、長時間使われていなかった接続を再利用せずに切断し、新しい接続を使うべきです。 SetConnMaxLifetime() は接続の寿命を設定するAPIですが、寿命を10秒に設定しておけば、10秒使われていなかった接続を再利用することもありません。

引数に与える秒数に関しては、

> SetConnMaxLifetime() は最大接続数 × 1秒 程度に設定する。多くの環境で1秒に1回接続する程度の負荷は問題にならない。

とあったため、とりあえず1秒を設定しました。

つまり、こんな感じの実装です。

```go
db.SetConnMaxLifetime(time.Second)
```

これにより、エラーは発生しなくなりました！！

## なぜこの設定で問題解消されるのか
`MaxLifetime` の値はデフォルトでは無制限です。
一見すると、コネクションはできるだけ使いまわした方が再接続のオーバーヘッドも少なく、良いように思われるのですが、接続時間が長すぎることのデメリットも多くあるようです。いくつかの記事でそのことが説明されていますが、例えば、こちらの記事ではこのように説明されていました。

> コネクション回数が減る代わりにアイドルのコネクションが必要なくなってもキープし続ける負荷があったり、DB起因でコネクションが中断されたり、フェイルオーバしたりした際に、外部要因で既に切れたコネクションを持ち続けて切断の検知に時間がかかったり、新しいコネクションに切り替えて欲しい際に切り替えがなかなか起こらないというリスクを含んでしまいます

[sql.DBのチューニング方法](https://qiita.com/go_sagawa/items/11929cd0883608a6888d#maxlifetime%E3%81%AE%E6%84%8F%E5%91%B3%E8%A8%AD%E5%AE%9A%E3%81%97%E3%81%AA%E3%81%84%E3%81%A8%E3%81%A9%E3%81%86%E3%81%AA%E3%82%8B%E3%81%8B)

つまり、今回は「大量INSERT中になにかしらのDBコンテナ側の起因でコネクションが中断された」と推測されます。
DB側でコネクションが中断されても、GoクライアントはFIN通知を受け取るわけではないのでそのコネクションは生きていると判断します。Goクライアントからクエリパケットを送る際に初めてパケット喪失をして気づき、 `unexpected EOF` のエラーを受け取る格好になります。
このようにならない為に、Goクライアント側で `MaxLifeTime` の設定をし、Goクライアント側で接続を1秒毎に切っておく対処を追加することで解消されるのかと。

## なぜMySQLでエラーが発生したのか
ここまででは、 `unexpected EOF` が発生する理由と解決方法はわかったのですが、そもそもなぜMySQLでエラーが発生してしまったのかという根本原因が分かっていません。

MySQLコンテナ内のログを確認してみると、 `ibuf cursor restoration fails` というエラーログがでていることが分かりました。これ自体の意味はよく分かっていないのですが、 MySQL5.7.31でfixされたようです。
https://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-31.html

## 最終的な解決手段
そこで、当時最新であったMySQL5.7.32にアップデート（ローカルにある5.7のimagreをいったんrmして、再起動）して、 `SetConnMaxLifetime` の実装を削除して、無事動作することを確認しました。
つまり、バージョン上げるだけで解決しました。

# これから
本番環境やステージング環境ではAWSのAuroraを使っており、Auroraでも同様の問題が発生してしまうのかの確認が必要です。
それはこれからの話なので、進展ありましたら、記載します。
Aura component でショートカットキーのイベントを得る

以前にも作った記憶があるのですが、すっかりやり方を忘れてしまった。
また忘れないように記録しておきます。

コントローラの　doInitの中に以下を書くだけ。

```
doInit : function(component, event, helper) {

   window.addEventListener('keydown', function(e) {
      if (e.key === 'PageUp') {
         alert("PageUp");
      }
   });
},
```

***注意点***
コンポーネントが入れ子になっている場合は、子コンポーネントではイベントが取れないので注意です。
親でイベントを拾って、子に渡してあげないとダメです。


https://andyinthecloud.com/2018/12/16/custom-keyboard-shortcuts-with-lightning-background-utilities/
[時系列分析]コロナの陽性者数を予測してみた。[Python]



# 概要
- コロナの陽性者数がどう変化するのか気になったので、時系列分析を使って予測をしていきます。
- 実行結果は正解データ無しとありで見ていきたいと思います。


# 実行結果（正解データ無し）

- actualは実際の値（赤線）、predictionは予測の値（青線）
- １２月３１日まで予測
- 徐々に陽性者数が伸びている。
- 正解データが無いため、この予測があっているか不明。

![Figure_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/763295/6752586c-fd0c-2f87-652c-a217765328af.png)



# コード
```Python
import pandas as pd
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore') # 計算警告を非表示

plt.style.use('ggplot') # グラフのデザイン変更（お好みで利用）
plt.xkcd() # グラフのデザイン変更（お好みで利用、日本語は未対応）
import statsmodels.api as sm # version 0.8.0以上

# CSVファイル読み込み（事前にダウンロードしたCSVファイルを利用）
df = pd.read_csv("data/pcr_positive_daily.csv")

corona = pd.Series(df["PCR 検査陽性者数(単日)"].values,
                        index=pd.to_datetime(df["日付"]))

corona_train2 = corona["2020-01-16":"2020-11-28"]


# SRIMAモデル（テストデータ1年を除いてモデル作成）
sarimax_train = sm.tsa.SARIMAX(corona_train2, 
                        order=(3, 1, 3),
                        seasonal_order=(0, 1, 1, 30),
                        enforce_stationarity = False,
                        enforce_invertibility = False
                        ).fit()

sarimax_train2_pred = sarimax_train.predict("2020-11-28", "2020-12-31") # テストデータ1年分予測

plt.figure(figsize=(8, 4))

plt.plot(corona, label="actual")
plt.plot(sarimax_train2_pred, c="b", label="prediction", alpha=0.7)
plt.ylabel('PCR')
plt.xlabel('MONTH')
plt.legend(loc='best')

```




# 実行結果（正解データあり）

- actualは実際の値（赤線）、predictionは予測の値（青線）
- １０月３０日から１１月２８日のテストデータでは陽性者数が下がっている。
- 予測の精度が低いことがわかった。

![Figure_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/763295/11ef07e6-c0e6-0760-21ff-91a58a941e97.png)



# コード
```Python
import pandas as pd
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore') # 計算警告を非表示

plt.style.use('ggplot') # グラフのデザイン変更（お好みで利用）
plt.xkcd() # グラフのデザイン変更（お好みで利用、日本語は未対応）
import statsmodels.api as sm # version 0.8.0以上

# CSVファイル読み込み（事前にダウンロードしたCSVファイルを利用）
df = pd.read_csv("data/pcr_positive_daily.csv")

corona = pd.Series(df["PCR 検査陽性者数(単日)"].values,
                       index=pd.to_datetime(df["日付"]))

corona_train2 = corona["2020-01-16":"2020-10-30"]
corona_test2 = corona['2020-11-01':'2020-11-28']

# SRIMAモデル（テストデータ1年を除いてモデル作成）
sarimax_train = sm.tsa.SARIMAX(corona_train2, 
                        order=(3, 1, 3),
                        seasonal_order=(0, 1, 1, 30),
                        enforce_stationarity = False,
                        enforce_invertibility = False
                        ).fit()
sarimax_train2_pred = sarimax_train.predict("2020-10-30", "2020-11-28") # テストデータ1年分予測

plt.figure(figsize=(8, 4))

plt.plot(corona, label="actual")
plt.plot(sarimax_train2_pred, c="b", label="prediction", alpha=0.7)
plt.ylabel('PCR')
plt.xlabel('MONTH')
plt.legend(loc='best')
```




# まとめ
- データ数が少ないため、精度の低い予測グラフとなった。
- 陽性者数以外のデータを加えることで、精度を上げることが可能かも…？




# 参考
- [オープンデータ｜厚生労働省](https://www.mhlw.go.jp/stf/covid-19/open-data.html)
- [Pythonで時系列分析の練習（9）SARIMAモデルで未来予測](https://momonoki2017.blogspot.com/2018/03/python10.html)
[BOXモデル]box-sizing: border-box; / content-box; の違い

box-sizing: `content-box;`を使うことがあったのでおさらいメモです。

# box-sizingとは
以下4つで整形された要素
①content + ②border + ③padding + ④margin

box-sizing: border-box;とbox-sizing: content-box;の違いは`width内にどこまでいれるか`である。
完全に正しい表現ではないかもしれないがあくまでイメージです。(詳細は下記)


# box-sizing: content-box;
`width = ①content`となる。

つまり仮に②border + ③paddingがあった場合、`widthの幅から ②border + ③padding の幅が上乗された見た目になる`

### 例
- 定義したwidth = `250px`
- 見目サイズ = `330px` ( 250px (①width)+20px (②border)+60px (③padding) )
- widthと見た目サイズが`ずれる`

<p class="codepen" data-height="550" data-theme-id="dark" data-default-tab="css,result" data-user="nonMan21" data-slug-hash="MWjYZRL" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="MWjYZRL">
  <span>See the Pen <a href="https://codepen.io/nonMan21/pen/MWjYZRL">
  MWjYZRL</a> by non Man (<a href="https://codepen.io/nonMan21">@nonMan21</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

> 特に何もしないとcontent-box;です。
しかし、実際にpaddingやborderをいれるたびに頭の中で思っていたwidthからずれるのはコーディングしていても意図しないズレが起きるし、せっかく調整したあとに修正する場合に非常に効率が悪いことが多いです。

# box-sizing: border-box;
`width = ①content + ②border + ③padding`となる。

つまり仮に②border + ③paddingがあった場合でも、`widthの幅で変わらない`

### 例
- 定義したwidth = `250px`
- 見目サイズ = `250px` ( 170px (①width)+20px (②border)+60px (③padding) )
- widthと見た目サイズが`同じ`

<p class="codepen" data-height="400" data-theme-id="dark" data-default-tab="css,result" data-user="nonMan21" data-slug-hash="yLayGrG" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="yLayGrG">
  <span>See the Pen <a href="https://codepen.io/nonMan21/pen/yLayGrG">
  yLayGrG</a> by non Man (<a href="https://codepen.io/nonMan21">@nonMan21</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

> まず全体のレイアウントを考えてwidthを設定、その後borderやpaddingで中身をスタイルすることが多いと思います。
`border-box;`を設定しとくと要素のwidthは変わらず、中身だけを集中してスタイルできるし、幅の修正をする際も基本的にはwidthだけを考えればOKで効率が良いことの方が多いです。
そのため、通常はbase.cssの`htmlセレクタなどにborder-box;`を設定してしまうことが多いです。


# content-boxをあえて使うとき
最近あった例ですが、小さい画像コンテンツにborderをつけるときに使いました。

border-boxは中身が画像でもいい感じに縮小してくれます。が、そもそも小さい画像の場合は見えにくくなってしまうため必要に応じてcontent-boxを使ってあげるのも良いかもしれません。
※ 下記例は一部切り取ったのであんまり変わらないかもですが、小さい要素程サイト全体で見た時にサイズ感の変化に違和感が出ることもしばしばでした。

### border-boxの場合
「白いborder入れたら画像が小さくなっちゃって少し見えにくい...!?💦」
![スクリーンショット 2020-11-30 10.29.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/825124/0a57a9e8-168c-3cd4-1998-51b4a285344d.png)

### content-boxの場合
「画像サイズは元のままだし、borderの太さ調整が来ても基本OK!」
![スクリーンショット 2020-11-30 10.30.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/825124/ac79cf9f-8264-62f0-548a-6593f682219d.png)
レスポンシブデザインcss『画像を伸縮できるようにする』

１画像を伸縮できるようにするcss

```
img{
  max-width: 100%;
  height: auto;
}
```

解説
全てのimgタグに適用される。画像は親要素のボックス幅に合わせて伸縮するようになる。
しかし、画像の元々のサイズより拡大することはない。
レスポンシブデザインで画像を扱う場合、ほぼパターンと考えて良い。
#1(Java)　整数、小数を代入する際の留意点・強制的な型変換

#最初に
初めまして、現在就職活動中兼javaを学習しています。kosukeと申します。
qiitaでは、学習中にアウトプットしておきたい内容を備忘録として残すために投稿させて頂きます。
マークダウン記法もよく理解できていないため、見づらかったらすみません。。。（これから改善していきます。）
また、誤った点がありましたら、コメントにてご指摘いただけると幸いです。



#目次
・代入する際のルール
・型を無視した場合
・エラーを出さず、強制的に代入させる
・型の大小関係の説明



##代入する際のルール
javaで変数を宣言する際は、

（　　型　　）　（　　変数名　　）　　=  　　(　　値　　）;
の順番で宣言するルールがあるため、

```
int num = 3;
```
のような記述になります。

始めにアウトプットも兼ねて上記のコードを説明すると、
intというのは整数が代入されるという意味の型を指しています。
そのため、変数numには整数の値が代入されなければいけません。




##型を無視した場合

例えば、

```
int num = 5.0;
```

のようなコードはコンパイルエラーになります。
こちらの理由は、int型を指定しているにも関わらず、double型の値を代入しているためです。

整数の型と小数の型には大小関係があり、それに従わないとエラーになるのです。
※型の大小関係は下の内容で説明しています。



##エラーを出さず、強制的に代入させる
上記コードの改善点は、
double型の5.0という値をint型に変換することで解決する事が出来ます。

```
int num = (int)5.0;
```

このように変数名の前に記述されている型を指定する事で、5.0が整数5に変換されnumに代入されます。（強制的な型変換を指示する演算子をキャスト演算子と言います。）

しかし、こちらの変換方法は一見便利なように見えますが、デメリットもあります。
変換して小数5.0が整数5になりましたが、小数点以下が捨てられ情報の欠如が発生します。
これによりデータの欠損にも繋がるため、よほどの理由がない限りキャスト演算子は使用されないようです。




##型の大小関係の説明

<　大小関係（byte型が最小でdouble型が最大となります）　>
1:byte（最小）
2:short
3:int
4:long
5:float
6:double（最大）

byte・shortはそれほど利用頻度が高くないみたいなので、整数の値を扱う時はint型を指定するという解釈をしています。
しかし、long型の場合、扱う数値が大きいかつ値の末尾にLやlが付いている特徴があります。

float・doubleは小数点の値を代入する際用いられる型になります。
こちらも、小数を扱う場合大体doubleを指定するそうですが、float型の場合は、値の末尾にFかfが付いています。

私がこちらの大小関係を理解する際に用いた考え方が、

＜　　型を箱と考える　　＞

byte型が一番小さい箱で型が大きくなるにつれて箱が大きくなっていくイメージです。
先ほどの誤ったコードを例にすると、

```
int num = 5.0;
```

intという箱にdouble型は大きすぎて入りません。。。
なので、こちらのコードはエラーになるのです。



しかしこちらのコードは、
```
double num = 3;
```

doubleという大きい箱にint型は入れる事が可能です。
（int型の方がdouble型よりも大小関係が小さいため）
こちらの考え方で学習するとコードのミスを減らす事が出来ました。


参考テキスト：スッキリわかるjava入門


#終わりに
最後まで読んで頂き有り難うございました。
初めてのqiita投稿という事で、勝手が分からず読みにくいと感じられた方。申し訳ないです。。。
今後も投稿を続けていくので、書き方は都度改善していきます。
【PostgreSQL】『対向（peer）認証に失敗しました』―psqlログインできない際の対応

#### 背景
以下の環境でWEBシステムを構築し、EC2上のPstgreSQLへマイグレーションを試みた際に、『対向（peer）認証に失敗しました』というエラーが発生。
その対処を以下に記します。

先人たちの知恵をお借りするなどして解決できたことを、この場をお借りして感謝するとともに、大変恐縮ですが自分のメモとしても、こちらへまとめておきます。

#### 環境
- AWS EC2 (Amazon Linux 2)
- Python 3.7.9　　　　※2020/11/29時点のAmazon Linux2でのデフォルト
- Django 3.1.3
- PostgreSQL 11.5　　※同上
- Nginx 1.12　　　　　※同上
- Gunicorn
- Putty 0.74

### 1.現象
EC2上のPostgreSQLへ、pythonの仮想環境（venv）より`python manage.py migrate`を試みた際に下記エラーが発生。
この時点までに、PostgreSQLでちゃんとユーザーにログイン権限とパスワードを設定しています。

```:ターミナル
(venv) > python manage.py migrate

・・・割愛・・・

psql: FATAL:  ユーザ "postgres" で対向(peer)認証に失敗しました
```

### 2.原因
peer認証という仕組みにあり。

>Peer認証とは、カーネルからクライアント上のシステムユーザ名を取得し、PostgreSQLデータベースユーザと同一である場合のみ接続が許可される。

つまり、PostgreSQL内のユーザーとUNIXユーザで、ユーザー名が一致してさえいれば認証情報なしでログイン出来てしまう仕組みです。
ログインの際、postgresユーザー名とパスワードを指定しても、ローカルからのアクセスはUNIXユーザー名とパスワードがpostgresのものと同じでないとダメだったのです。

ちなみに、peer認証のデメリットは以下とのこと。
>peer認証のデメリットは、パスワードを入力せずに済むが、ユーザー毎に権限を変えて使い分けたいときや一時的に別のpsqlユーザーとしてログインしたいときは相当にやりづらいですし、psql側でユーザーを作る際に、同名のUNIXユーザーも追加する必要があるために面倒。

### 3.対処
以下の手順ですすめます。

**1) postgresユーザー:"postgres"のパスワードをもう一度設定する**

```:ターミナル
$ sudo su postgres -c 'psql --username=postgres'
```

**2) psqlに入る**

```:ターミナル
postgres=# ALTER USER postgres with encrypted password 'your_password';
```

**3) PostgreSQL側の挙動を変更するために、`pg_hba.conf` を編集**
(上記環境ですと、`/var/lib/pgsql/data/pg_hba.conf`にあります。)

コマンドプロンプトより、`sudo vi /var/lib/pgsql/data/pg_hba.conf`と入力。
ファイルを開くと以下のようになっています。

```:pg_hba.conf
# ローカルで動いているpsqlへアクセスする場合
local   all             all                                     peer
# 他のクライアントからpsqlへアクセス可能にする場合(例)
host    all             all             127.0.0.1/32            peer
```

パスワード認証させたい場合は、`peer`の部分を`md5`に変更して保存します。

```:pg_hba.conf
#ローカルで動いているpsqlへアクセスする場合
local   all             postgres                                md5
#他のクライアントからpsqlへアクセス可能にする場合(例)
host    all             all             127.0.0.1/32            md5
```

編集時に「i」キー押下、編集後「esc」押下し`:wq!`と入力して保存。
（保存せずに抜けたい場合は`:q!`を入力。）
コマンドプロンプトに戻ります。

**4) PostgreSQLの再起動**

```:ターミナル
> sudo service postgresql restart
```

これで再度ログインを行なうと今度はパスワードを求められ、正しい情報を入力することで、自分のUNIXユーザーでもログインできる設定になります。

#### 4.結果

自分のUNIXユーザーからpostgresユーザー："postgres"として、psqlログインできました。

```:ターミナル
> psql -U postgres -h localhost -W

・・・【割愛】・・・

postgres=#
```


---
##### 参考
- [peer認証の関係でpsqlログインできない時の対処法](https://qiita.com/tomlla/items/9fa2feab1b9bd8749584)　　[@tomlla](https://qiita.com/tomlla)様
- [PostgreSQL “対向(peer)認証に失敗しました” エラーが出るときの対処法](https://cpoint-lab.co.jp/article/201807/4217/)
- [PostgreSQL 認証に失敗しないための Ident、MD5、Trust 比較](https://server.etutsplus.com/how-to-configure-postgresql-to-use-ident-md5-trust-authentication/)

---


##### （編集後記）
Qiita初投稿です。
誤記や記載不足等ありましたら、ご指摘くださいませ。
何卒よろしくお願いいたします。
Postfixメモ

deferredキューに滞留しているメールが再送を何日で諦めるかはこの設定値で決まる。
以下は5日の場合

```
$ postconf | grep -i lifetime
bounce_queue_lifetime = 5d
maximal_queue_lifetime = 5d
```

```
滞留メールを見る
# postqueue -p

個別にメールをHOLDする
# postsuper -h キューID

HOLD中のメールをキューに入れ直す（キューIDが変わる）
# postsuper -r キューID

HOLDキューにあるメールを全て再送する
# postsuper -H ALL
```
curlとシェルスクリプトで連番になっている画像ファイルを取得

# 環境

- macOS Big Sur 11.0.1
- GNU bash, version 3.2.57(1)-release
- curl 7.64.1



# curlで画像を取得する

画像の取得だけならcurlコマンドで実行できる。

```terminal:terminal
curl -O %画像のURL%
# 例： curl -O http://hogehoge.com/fuga1.jpg
```

# シェルスクリプトでforループする

例えば以下のような連続したファイルを一気に取得したい場合、curlコマンドを複数回実行するのはちょっとしんどい。

```
curl -O http://hogehoge.com/fuga1.jpg
curl -O http://hogehoge.com/fuga2.jpg
　　　:
　　　:
curl -O http://hogehoge.com/fuga90.jpg
```

シェルスクリプトでforを使うことで、手抜きします。
まずはシェルスクリプトのファイルを作成します。
terminal.appを開き、ダウンロードするディレクトリを作成/移動し、以下のコマンドでshファイルを作成します。

```terminal:terminal
% vi download.sh
```

作成と同時にviが起動します。(操作方法は割愛。Qiitaには良質な記事があるのでそちらを参照)
以下を記載し、保存します。

```sh:download.sh
for((i=1; i<90; i++))
do
curl -O "http://hogehoge.com/fuga${i}.jpg" # ${i}でiを文字列として展開する
done
```

実行する時はbashからshファイルを指定して実行します。

```terminal:terminal
% bash download.sh
```

こんな感じのメッセージがズラっと出てくればOK

```terminal:terminal
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    65    0    65    0     0    160      0 --:--:-- --:--:-- --:--:--   160
```

# 注意

一般で公開されているサーバーへ軽率にぶっ放すとよろしくないので使い方は考えましょう。
オークファン2020AdventCalendar初日

# はじめに
オークファンで開発部長をしている @gakkie と言います。
今年で、AdventCalendarに参加するのは3年目になります。

当社は、社員40人くらい＋業務委託の方々で、子会社を含めていくつかのサービスを運営しております。
サービス毎に歴史が違い、PHPで書かれているサービスもあれば、サーバサイドKotlinで運営しているサービスもあります。

このコロナ禍でも、特にECを行っている商品流通プラットフォーム事業が非常に成長しており、業務拡大に対して開発組織も強化していきたいと考えております。

そんな社内の状況を、少しずつ紹介していければと思いますので、どうぞよろしくお願いいたします。

# オークファングループの紹介

決算資料より、オークファングループは何をする会社なのか、というページを添付します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80151/864f8de4-30b0-dfd9-38d8-e916be54b6a9.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80151/1e29016f-5734-ba99-4130-8a157b19ef97.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80151/713623f2-88ea-8841-e107-fa7f4feb712c.png)



# 利用技術

先に記載したとおり、複数のサービスがあり、利用している環境や技術も多岐にわたります。
主なものを紹介すると、

## 開発環境
* InteliJ、PhpStorm
* Vagrant、Docker
* Mac、Windows

## 言語
* PHP、kotlin、Java、Go、Python、Ruby、Perl

## DB
* MySQL、PostgreSQL、SQL Server
* Redis、memcached
* ElasticSearch、Solr

## インフラ
* AWS、Azure、DC、IDCF、さくら

## その他
* backlog
* AdobeXD
* GoogleDrive
* Kibela
* PlantUML
* SharePoint
* trello
* Redmine

他にもいろいろあります。

# もし気になることがあれば
今回のAdventCalendarで気になることがあれば、いつでもご連絡ください。
また、当社に興味を持っていただける方がいらっしゃれば、お気軽にお話だけでも聞きに来てください！
Windows のシャットダウン時にうっかり VirtualBox VM のシャットダウンを忘れてしまう方へ

Windows で VirtualBox を使っている方にはわかるかもしれないのですが、普段の業務終了時に VM を終了せずに PC をシャットダウンしようとして↓のようなメッセージが表示されたことはないですか？ :smiling_imp:

> VirtualBox Interface
> このアプリがシャットダウンを妨げています。

私もこれが出ると「うわ・・・またやっちまった・・・」となります :sweat:

そして一旦デスクトップに戻りタスクマネージャを開き `VirtualBox Interface` を終了し・・・

せっかく早く帰れると思ったのに :pensive:

今回は、そんなお悩みをシェルスクリプトで解決してみようと思います :blue_heart:

## 対策？

対策は簡単です :bulb:

ズバリ、

**PC をシャットダウンする前に VM をすべて終了する**

です :wink:

というか、これを忘れてしまうから困っているわけなんですが :sweat_smile:

## シャットダウンのコマンドを用意しよう

Windows のシャットダウンはコマンドで実行することができます。

```bash
shutdown -s -t 0
```

`-s` はシャットダウン、 `-t 0` は処理が行われるまでの時間（秒）を表しています。
この例の場合だと、「0 秒経ったらシャットダウン」、つまり即時シャットダウンのことを意味しています。

シャットダウンコマンドについては [こちらの記事](https://www.atmarkit.co.jp/ait/articles/0601/14/news013.html) を参考にさせていただきました。

## 起動中の VM をすべて終了するコマンドを用意しよう

VirtualBox には VM の操作をコマンドラインで行える `VBoxManage` が用意されています。
これを利用して、起動中の VM を一覧で表示し、それをシャットダウンの引数に渡すようにします。

なお、 `bash: VBoxManage: command not found` になるよという方は、 `VBoxManage` を環境変数に登録してください。

`VBoxManage` の場所に関しては [【VirtualBox】VBoxManageを使う - Qiita](https://qiita.com/shinaK/items/ddd7fb51d26f641af041) が参考になるかと思います。

```bash
VBoxManage list runningvms | cut -d' ' -f1 | xargs -I{} VBoxManage controlvm {} acpipowerbutton
```

なお、 VBoxManage のコマンドについては [Virtualboxの仮想マシンをコマンドラインから操作 - Qiita](https://qiita.com/tukiyo3/items/5ecea7f95cb961f07194) を参考にさせていただきました。

## スクリプトを完成させよう

必要なコマンドはそろいましたので、いよいよスクリプトを作っていこうと思います。
方針は簡単で、起動中の VM をすべて終了してから Windows をシャットダウンさせるだけなのですが、落とし穴が一つあります。
**VM を終了するコマンドは、 VM のシャットダウンシグナルを送るのみですぐに処理が終わり、 VM のシャットダウンを待ちません。**
なので、上記 2 つのコマンドをそのまま並べて実行すると、また VM が終了しないまま PC のシャットダウンに入ってしまいます :cold_sweat:

これを回避するために、起動中の VM の一覧を定期的にコマンドで確認し、出力がなくなってから PC のシャットダウンに入るような処理を記述します。

```bash:shutdown_vms.sh
#!/usr/bin/env bash
INTERVAL=3 # 起動中の VM 一覧を確認する周期

VBoxManage list runningvms | cut -d' ' -f1 | xargs -I{} VBoxManage controlvm {} acpipowerbutton

while [ $(VBoxManage list runningvms | wc -l) -ne 0 ]
do
    echo "shutting down VMs..."
    sleep ${INTERVAL}
done
```

そして、シャットダウン時に実行するファイルを作成します。

```bash:shutdown_entrypoint.sh
#!/usr/bin/env bash

./shutdown_vms.sh
wait

echo "bye"
sleep 1

shutdown -s -t 0
```

これで完成です :exclamation: :relaxed:

## 動作確認しよう

1. VM をいくつか立ち上げておきます。
1. 上で作成した `shutdown_vms.sh` と `shutdown_entrypoint.sh` を同じディレクトリに配置します。
1. エクスプローラの画面で `shutdown_entrypoint.sh` をダブルクリック、あるいは Git Bash で `./shutdown_entrypoint.sh` を実行します。

こんな感じで出力されていたらうまくいっています :wink:

```html
shutting down VMs...
shutting down VMs...
shutting down VMs...
bye
```

みなさんも身の回りの面倒なことを自動化して快適に開発し、快適におうちに帰りましょう :house_with_garden: :yum:
M1でLinux仮想を動かす(SimpleVM)

2020/11/30現在まだ仮想系のものがdocker含めてリリースされていないのですが、海外掲示板でHypervisor.frameworkを使っていち早く試している人がいたので、物は試しということでやってみました。普通に動きますので、いち早くM1上の仮想を体験したい人はどうぞ。

追記:11/30 もっと簡単なのはこちら→[M1でLinux仮想を動かす(QEMU)](https://qiita.com/bellx2/items/c06e1c00ad9de77a4fb9)

![ SimpleVM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14768/2937aee4-1955-10a8-743f-4e71a0a5ff87.png)

※掲示板から落としたバイナリーの安全性については各自の責任でお願いします。

# 使うもの

+ M1チップ搭載Mac
+ [SimpleVM](https://github.com/KhaosT/SimpleVM)
+ Ubuntu ARM64 ISOイメージ 
+ [Keka](https://www.keka.io/ja/)

もしくはUbuntu入りバイナリーを[MacRumorsのスレ](https://forums.macrumors.com/threads/ubuntu-linux-virtualized-on-m1-success.2270365/)から探し出す。(New!

# SimpleVMの準備
以下のどちらかでSimpeVMを作ります

1. XCodeでコンパイルー[MacRumorsのスレ](https://forums.macrumors.com/threads/ubuntu-linux-virtualized-on-m1-success.2270365/)をよく読む（SwiftTermなど依存関係有、フォーク有
2. [MacRumorsのスレ](https://forums.macrumors.com/threads/ubuntu-linux-virtualized-on-m1-success.2270365/)からビルド済のSimpleVMのバイナリーを探し出す。

# 環境構築＆実行
+ ISOイメージをダウンロードする
+ Kekaでisoファイルをunarchiveする(diskutilsではマウントできない）
+ vmlinuzのファイル名をvmlinuz.gzに変更してunarchiveする(非圧縮のvmlinuzができる)
+ vmlinuzとinitrdをSimpleVMのUIにドロップ
+ isoイメージファイルをSimpleVMのUIにドロップ
+ Start

# Ubuntu入りバイナリー

[MacRumorsのスレ](https://forums.macrumors.com/threads/ubuntu-linux-virtualized-on-m1-success.2270365/)の中にSimpleVMとUbuntuを組み入れたものがありました(New!

# ベンチマーク結果

sysbench

```
sudo apt install sysbench
sysbench cpu --num-threads=1 --cpu-max-prime=9999 run
```

手元の実測値です（シングル）

| 機種 | CPU speed (event/sec) | 
|:-----------|------------:|
| MacBook Air (M1,2020)| 10937.45 |
| i7-8700 @ 3.20GHz | 1506.57 | 
| Ryzen 5 PRO 3400GE (3.3GHz)| 1909.44| 
| Ryzen 7 PRO 4750GE (3.1GHz)| 2164.02| 

シングルコア性能が間違いかと思う程高いんですが。掲示板では別のベンチマークのでの結果でSimpleVM利用で[ThreadRipper 2990wxより早いという情報](http://hoult.org/arm64_mini.html)もありました。

i7-8700は仕事用のLinux系のビルド環境ですが、いつのまにか先日導入した自宅esxiのRyzenに負けてるのが地味にショックです。

他のベンチマークを以下にまとめておきます。

[Ubuntu環境のベンチマーク比較（Apple Silicon M1他](https://qiita.com/bellx2/items/a373cb786c36ec64f37d)


#参考

[MacRumors Forum: Ubuntu Linux virtualized on M1 (success!)](https://forums.macrumors.com/threads/ubuntu-linux-virtualized-on-m1-success.2270365/)

# ぶつぶつ

他にまだ仮想関連はリリースされていないのにさくっと動いた事、そのスピード、[SimpleVM](https://github.com/KhaosT/SimpleVM)のコードのシンプルさ(Hypervisor.frameworkの素晴らしさ)に驚きました。
Azure Windows Virtual Desktop構築時に忘れがちなポイント

WVD構築時に上手く動かないとき、忘れがちな設定等のポイントを列挙
WVDの構成は以下を想定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/481044/6045282a-c4e2-a16a-3a82-b1c6c450c068.png)


### AADDSにログインできない<br>↓<br>Azure Active Directoryユーザーのパスワードを変更してAADDSへ同期する
WVDログイン時にユーザ名は認識するがログインできない場合は、AADDS(Azure Active Directory Domain Service)にパスワードハッシュが同期されていない場合が多い
AAD⇒AADDSの初期同期には数時間から数日かかることがある
またパスワード変更同期は30分以上かかることがある

### FSLogixが上手く動かない<br>↓<br>ロール"ストレージ ファイル　データ SMB 共有共同作成者"を割り当てる
FSLogix(SMB)を利用するには"dataActions"権限が必要
組み込みロール"共同作成者"などには"dataActions"権限は含まれていないため、その場合は上記ロールを割り当てる必要がある
それぞれのロール定義はページ下部 参考に記載

### セッションホスト作成に失敗する<br>↓<br>マスタイメージ作成用VM起動時にブート診断を無効化する
セッションホスト作成に失敗する場合、マスタイメージ作成用VMの起動時にブート診断を無効化したイメージを使用すると成功することがある
↓portalでの設定箇所
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/481044/7f255a54-4ead-5a6e-0cf0-2a5b508d195c.png)
↓参考url
https://www.robinhobo.com/windows-virtual-desktop-wvd-image-management-how-to-manage-and-deploy-custom-images-including-versioning-with-the-azure-shared-image-gallery-sig/

### リモート接続時に日本語入力ができない<br>↓<br>イメージのWindows10のバージョンをクライアント側と合わせる
例えばクライアント側のWindows10のバージョンが2004の場合、リモート側のイメージもバージョン2004を使用することで日本語入力することができる
バージョンの確認方法は"ファイル名を指定して実行"⇒"winver"
↓参考url
https://qiita.com/takeokams/items/4e0753047822b13bbcf7

---
### 参考

- 組み込みロール"共同作成者"のロール定義

```json
{
  "assignableScopes": [
    "/"
  ],
  "description": "Grants full access to manage all resources, but does not allow you to assign roles in Azure RBAC.",
  "id": "/subscriptions/{subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c",
  "name": "b24988ac-6180-42a0-ab88-20f7382dd24c",
  "permissions": [
    {
      "actions": [
        "*"
      ],
      "notActions": [
        "Microsoft.Authorization/*/Delete",
        "Microsoft.Authorization/*/Write",
        "Microsoft.Authorization/elevateAccess/Action",
        "Microsoft.Blueprint/blueprintAssignments/write",
        "Microsoft.Blueprint/blueprintAssignments/delete"
      ],
      "dataActions": [],
      "notDataActions": []
    }
  ],
  "roleName": "Contributor",
  "roleType": "BuiltInRole",
  "type": "Microsoft.Authorization/roleDefinitions"
}
```

- 組み込みロール"ストレージ ファイル　データ SMB 共有共同作成者"のロール定義

```json
{
  "assignableScopes": [
    "/"
  ],
  "description": "Allows for read, write, and delete access in Azure Storage file shares over SMB",
  "id": "/subscriptions/{subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb",
  "name": "0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb",
  "permissions": [
    {
      "actions": [],
      "notActions": [],
      "dataActions": [
        "Microsoft.Storage/storageAccounts/fileServices/fileshares/files/read",
        "Microsoft.Storage/storageAccounts/fileServices/fileshares/files/write",
        "Microsoft.Storage/storageAccounts/fileServices/fileshares/files/delete"
      ],
      "notDataActions": []
    }
  ],
  "roleName": "Storage File Data SMB Share Contributor",
  "roleType": "BuiltInRole",
  "type": "Microsoft.Authorization/roleDefinitions"
}
```
DartのtryParseの引数にnullを渡すとnullが返らないので注意しよう

# 3行で

- `tryParse`は内部で`parse`を呼んでおり、 型変換に失敗すると`parse`は例外（`FormatException`）を返し、`tryParse`は例外の代わりに`null`を返す 
- `tryParse`の引数に`null`を渡すと、`ArgumentError(Invalid argument)`が発生するが、 `FormatException`ではないので、`tryParse`は`null`を返さない
- `tryParse`で例外処理してるので失敗なら`null`が必ず来るはずだと思い込み実装すると危険なので、null safetyが有効になるまでは注意したい

# 検証

- DartPadで検証した結果は以下のとおり
- null safetyだとコンパイルで気づく可能性があるので安心です

❌ null safety | ✅ null safety
:-: | :-:
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37385/a30dd4e4-0d11-807c-6dac-35098c56f98a.png width=580/> | <img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37385/4aa10476-c9ec-f1c1-8c0f-12a904dc2628.png width=580/>

# コメントに注意書きはあるけど...

- `int.tryParse`のコメントには注意書きがあるけど、`DateTime.tryParse`には注意書きがなく、`DateTime.parse`の方にしかなかったりする
- 一度誤って`null`を渡して痛い目をみないと気づかない可能性が高い

`int.tryParse` コメント有り | `DateTime.tryParse` コメント無し
:-: | :-:
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37385/965d288e-af15-06c6-13f9-5f42e2daebd8.png width=580/> | <img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37385/3826914a-c847-207e-fb75-5826b565936f.png width=580/>

# どうする？

- null safetyは来年の春ごろらしいので今から有効にするのは時期尚早でいろいろと対応が大変です
- とりあえず、下記のような応急処置になりそう

```dart:空文字にする
final result = int.tryParse(v ?? '');
```

```dart:特定の型で制御する
try {
  final result = int.parse(null);
  print(result);
} on ArgumentError catch (e, s) {
  print('✅ $e $s');
} on FormatException catch (e, s) {
  print('✅ $e $s');
}
```

```dart:全部キャッチする
try {
  final result = int.parse(null);
  print(result);
} catch (e, s) {
  print('✅ $e $s');
}
```


# 参考

- https://api.dart.dev/stable/2.10.2/dart-core/int-class.html#static-methods