OmniAICreator/Qiita-1.07M huggingface dataset:
FARM_FINGERPRINTをPythonで再現する

# tl; dr

```sh
pip install pyfarmhash
```

で [PyFarmHash](https://github.com/veelion/python-farmhash) をインストールしたうえで、

```python
import struct
import farmhash

def farm_fingerprint(value: str) -> int:
    hash_unsigned = farmhash.fingerprint64(value)
    hash_binary = struct.pack("Q", hash_unsigned)
    return struct.unpack("q", hash_binary)[0]
```

# はじめに：FARM_FINGERPRINTとは

BigQueryやCloud Spannerでは、引数の値からハッシュ値を作る `FARM_FINGERPRINT` という関数が用意されている。
SHA256やSHA512といったおなじみのハッシュ関数も用意されているものの、 `FARM_FINGERPRINT` は戻り値がINT64なので値をそのままカラムに突っ込むことができ、重宝している。
（他のハッシュ関数は戻り値がBYTE型なので変換の手間によってちょっと敬遠してしまう。）

さて、今回BigQuery上で行っていた計算処理を外出しし、Python上で自前で行うように改修を加えることになった。
具体的にはBigQuery上のデータを一度外部にエクスポートし、外部のサーバーのPython上で計算してから、計算結果をBigQueryにインポートする、というものだ。

特に計算処理の過程では `FARM_FINGERPRINT` 関数を使っていたため　`FARM_FINGERPRINT`　も自前で実装しなければならなくなった。

# 実行環境

* Python 3.9.7
* PyFarmHash 0.2.2

# PythonでFARM_FINGERPRINTを再現する

Pythonの場合、コアとなるロジックは [PyFarmHash](https://github.com/veelion/python-farmhash) パッケージとして提供されているため、これを使う。
ライブラリのインストールは `pip install pyfarmhash` で行えばよい。

ハマりポイントは戻り値の符号だった。
PyFarmHashが用意しているハッシュ値計算は `farmhash.fingerprint64` という関数で行われるのだが、この
 `farmhash.fingerprint64` の戻り値の範囲は、0から(2^64)-1である。
つまり **符号なし64ビット整数** の範囲なのだ。
一方、BigQueryのINT64型は **符号あり64ビット整数** （-(2^63)〜(2^63)-1）だ。
したがって単純に `farmhash.fingerprint64` で算出した値をBigQueryにインポートしようとすると範囲エラーによって取り込めない。
したがって符号なし64ビット整数を符号あり64ビット整数に変換する処理が必要になってくる。

Pythonではこのような変換のためにstructというパッケージを使うことができる。
structを使えば、符号あり/なしnビット整数とバイト配列の相互変換を行うことができる。
今回の場合、`farmhash.fingerprint64` の出力である符号なし64ビット整数をバイト配列化し、そのバイト配列を符号あり64ビット整数に再変換すればよい。

```python
import struct
import farmhash

# ハッシュ値を取得（戻り値は符号なし64bit整数の範囲）
hash_unsigned = farmhash.fingerprint64(value)
# hash_unsignedをバイナリー化する（"Q"はhash_unsignedが符号なし64bit整数であることを指定している）
hash_binary = struct.pack("Q", hash_unsigned)
# hash_binaryを符号あり64bit整数化する（"q"は符号あり64bit整数に変換することを指定している）
hash_signed = struct.unpack("q", hash_binary)[0]
```

`struct.pack` がバイト配列への変換関数、 `struct.unpack` がバイト配列からの変換関数だ。
それぞれの関数の第1引数は、入力/出力となる整数の符号あり/なしとビット数を指定するフォーマット文字だ。
[公式のドキュメント](https://docs.python.org/ja/3/library/struct.html)の `書式指定文字` という項に一覧があるが、C言語の型事情に精通していることを前提とした書き方になっていて分かりづらいので、翻訳した表を載せておく（整数型のみ）。

| フォーマット | 厳密な整数型        |
| ------------ | ------------- |
| `b`          | 符号あり8bit  |
| `B`          | 符号なし8bit  |
| `h`          | 符号あり16bit |
| `H`          | 符号なし16bit |
| `l`          | 符号あり32bit |
| `L`          | 符号なし32bit |
| `q`          | 符号あり64bit |
| `Q`          | 符号なし64bit |

この一連の処理を関数化したものが、冒頭に挙げた `farm_fingerprint` となる。

# おわりに

BigQuery、Cloud Spanner互換のfingerprint64処理が書くことができた。

structの話（というかC言語の型の話）はだいぶ端折ったので不明点があればコメントにお願いします。
【メモ】GoogleColab でXGBoost, LightGBM, CatBoost でGPUを使用する方法

- 製造業出身のデータサイエンティストがお送りする記事
- 今回は、GoogleColab で勾配ブースティング決定木（XGBoost, LightGBM, CatBoost）でGPU を使う方法をメモとして残しておきます。

## GPU を使えるようにする方法

### XGBoost

- パラメータ`tree_method='gpu_hist'`を設定するだけです。

```php
# XGBoost
import xgboost as xgb

model = xgb.XGBRegressor(**params, tree_method='gpu_hist')
```

### LightGBM

- LightGBM のGPU バージョンのライブラリーをインストールする必要があります。

```php
!git clone --recursive https://github.com/Microsoft/LightGBM
%cd /content/LightGBM/
!mkdir build
!cmake -DUSE_GPU=1 
!make -j$(nproc)
!sudo apt-get -y install python-pip

!sudo -H pip install setuptools numpy scipy scikit-learn -U
%cd /content/LightGBM/python-package
!sudo python setup.py install --precompile
```

- あとは、パラメータ`device_type='gpu'`を設定するだけです。

```php
# LightGBM
import lightgbm as lgb

model = lgb.LGBMRegressor(**params,device_type='gpu')
```

### CatBoost

- パラメータ`task_type='GPU'`を設定するだけです。

```php
# CatBoost
import catboost as cb

model = cb.CatBoostRegressor(**params, task_type='GPU')
```
[Flutter] AndroidStudioで、dartのformatter、import最適化を保存時に実行する

##　fomatterを保存時に実行する

検索窓で、「`Format code on save`」で検索して、設定をオンにする。

![スクリーンショット 2021-11-29 午後8.58.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121224/90c6bcac-9a69-6b5e-068d-5d86eeeaad98.png)

##　importの最適化を保存時に実行する
検索窓で、「`Import Organize`」で検索して、設定をオンにする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121224/12256263-2a1e-9bde-8ae8-f019ec6db478.png)

# 参考
https://stackoverflow.com/questions/27092772/auto-code-formatting-in-android-studio

https://www.geeksforgeeks.org/what-is-the-shortcut-to-auto-import-all-in-android-studio/
英語学習ゲーム「英語物語」に関するデータラーニングギルドでの分析企画についての振返

どうも！英語物語の開発者、ごんです。

村上さんが代表をされている「データラーニングギルド」様とのコラボ企画で、
ギルドの塾生様とNDAを結んで、
英語物語の分析を塾生様にして頂きました。

開始当初、あまり弊社の分析データが整っておらず、
成果を出すのは難しそうだけど、
村上さんと仲良くなれればいいかなと、
あまり期待はしていなかったのですが、
報告会では様々な貴重な知見を頂き、感謝感謝です。

個別具体的な内容については、それぞれの塾生様が報告されるかと思うので
本記事の方では、今後の英語物語の開発・分析へ役立てるための振り返りをさせて頂きます。

#良かったこと
分析頂いた結果、売上を向上するために集中すべきポイントが見えてきた。
具体的には、
・ユーザ属性によって、LTVは大きく異ることが予想されるため、広告などでは特定のユーザ属性に絞って費用対効果を検証するのが非常に重要である。
・課金単価が高いユーザ様はそうでないユーザ様に比べて、低単価であっても特定の課金アイテムをしている場合があり、課金単価層別の購入アイテム分析は、マネタイズ戦略を考える上で重要になりそう。
・月１回限定１２０円ガチャが、非常に多くの課金UUを締めており、改善のポテンシャルが高いことから、週１回限定490円ガチャに変更したところ、売上がかなり増加した。
・高単価商品に関するポテンシャルが高いことから、月に1回だけかえる1万円の商品を企画できた。
・課金ユーザは、初日に6割が課金するという重要な示唆に基づき、サブスク型のビジネスモデルに社運をかけて転換する意思決定ができた。
・特定のガチャが非常に大きな成果を出していた事を踏まえ、ガチャは乱発するのではなく、一つ一つ盛り上げて、最適化していくのが大切だという知見を得た。

#悪かった事
###データの不備について
冒頭でも記載したとおり、分析データの整備があまりできていなかった。
課金データに関してもテスト課金や不正課金に関するものが玉石混交で、塾生さんの分析時に混乱をきたす要因となった。

###データの共有について
また、FirebaseAnalyticsについて、共有可能だったにも関わらず、
共有が終盤になってしまい、洞察を得るチャンスを逃してしまったかもしれない。
また、直近でのデータについて、共有せずに終わってしまったため、半年ほど前のデータに基づく分析のみの結果となり、直近で色々変えた仕様変更に関する分析までは、対象に含められなかった。

###分析の着眼点について
キックオフミーティングにおいて、弊社としての課題が不明瞭だった事もあり、
分析の着眼点を決める部分でかなりの時間がかかってしまった印象がある。
企画の途中で、ゲーム業界の分析に関して、非常に知見の深い方のフィードバックを得る機会があり、
重要な指標を他社比較することで、分析の着眼点を探すのが非常に効率的だと感じた。

#改善すること
###分析を施策に反映
頂いた知見を元に、重要な事からアプリの開発・運営に反映させて行きます。
特に広告の最適化・ガチャ・ショップ・サブスクモデルへの転換を進めていく予定です。
###分析基盤
今まで自社で分析をきちんと取り組んで来ていなかったが、
企画をうけて、分析して、改善する上で、重要なポイントを探すことは非常にコスパの良い投資であると
思ったため、分析体制の整備を行いたいと思った。

具体的には、
・現在取得しているデータのドキュメント化
・分析にしか使わないデータは、アプリケーションサーバに保存せず、Bigqueryへの直接保存する。
・月次での分析項目の設計と、半自動化
・Firebase・自社アプリサーバ・自社分析サーバへ保存するデータの追加・整理
・テストデータや不正なデータの保存を回避もしくは削除する体制

###取組の進め方
もし次回取組があれば、最初の分析課題を決める段階で、
重要な指標の他社比較や、現在の自社の課題の明確化を行い、
早期に課題を明確かできるようにし、
またその課題解決のために必要な分析データの取得体制の構築、最新データので検証まで行いたい。

村上様、データラーニングギルドの皆様、Akiさま、Otteyさま
この度は貴重な機会、洞察誠にありがとうございました。
yarn start エラー

備忘録として、記事にします。
①「npx create-react-app」 後に「yarn start」できなかった
　エラー内容は残っていないのですが、「node_modules」内にある「tokenize」がexportできないようでした。
②結論
　色々と試しましたが、状況もいまいち把握できていなかったため、「node_,modules」に問題があるとしたら、再インストールすることが有用であると仮定して実行しました。
「rm -rf node_modules」を実行し、「npm install」を実行し、再インストールしました。そして再び「yarn start」すると成功しました
③まとめ
　何に問題があったかは不明瞭であるため、教えていただけると幸いです。yarnやNode.jsに関して知識が無いに等しいので学習していきたいと思います。
【Git】git pullを取り消したいときはgit resetじゃなくgit revertを使おう

##git resetは基本使わない

間違えて```git pull```しちゃったけどやっぱり取り消したいとき、
調べると```git reset --hard```をすればいいよ！という記事をよく見かけた。

基本的に```git reset --hard```は（複数人での開発のときは特に）
ログが変わってしまうため使わないほうが良い。

##git revertを使おう

```git revert```は取り消したいコミットを打ち消すコミットを作成するので
コミットを消したログもちゃんと残せる。

##参考

https://qiita.com/chihiro/items/2fa827d0eac98109e7ee
Windowsバッチスクリプトから呼び出せるロギング・サブルーチンを公開しました

Windowsバッチスクリプトから呼び出せるロギング・サブルーチンを作成しました。GitHubにて公開していますので、宜しければご利用ください。

https://github.com/frogbrigade-net/windows-command-logger

The FROG Brigade dot NETは、私が技術者として活動する時のオーガナイゼーション名です。以下は、GitHubのREADMEの和訳です。

## Requirements

次のWindowsバージョンで、動作を確認しています。

* Windows Server 2008 and later
* Windows 8.1 and later

## Usage

logging.bat を第一引数に "LOG"、第二引数にログレベル、第三引数にログメッセージを指定して呼ぶだけです。

```bat
> SET LOG=CALL modules\logging.bat LOG
> %LOG% D "This is DEBUG level log message."
2021-11-29 08:52:35.65 [D] This is DEBUG level log message.
> %LOG% I "This is INFO level log message."
2021-11-29 08:53:02.41 [I] This is INFO level log message.
> %LOG% W "This is WARNING level log message."
2021-11-29 08:53:12.89 [W] This is WARNING level log message.
> %LOG% E "This is ERROR level log message."
2021-11-29 08:53:24.36 [E] This is ERROR level log message.
> %LOG% ANY_LOG_LEVEL "This is ANY LOG LEVEL log message."
2021-11-29 08:54:42.34 [A] This is ANY LOG LEVEL log message.
```

ロギング・サブルーチンは、整形したログメッセージを標準出力へエコーするだけです。ログレコードをファイルへ書き出したい場合、整形されたログメッセージをファイルへリダイレクトしてください。

```bat
SET LOG=CALL modules\logging.bat LOG
SET LOG_FILE=log-file-%DATE:/=%.log
%LOG% I "This log message is redirected to the log file." >>%LOG_FILE%
```

## Author

* [Nobuyuki Sekimori](https://github.com/juno-rmks)

## License

windows-command-logger は、Apache License で提供しています。
Snake in Yabu

# しくじり

まだ新人エンジニアでやる気に満ち溢れていた頃の話。

100人以上のSEが参加しているような大規模プロジェクトで、20人ほどのチームの一員としてアサインされていました。
その中で唯一の新人だった私はまだ大して役に立てることはなく、周りについていくのと、認めてもらおうという想いで必死でした。

その中で、ある１画面を任せてもらうことになり、「この仕事で結果を残してやる！」と息巻いて臨みました。

求められている以上の要件を盛り盛りに盛り込んで設計をし、顧客との設計レビューで「この方がもっと便利ですよね！」と鼻息荒く説明してたところ

**「いいですね！ でも、これって他の画面も仕様合わせた方が良いですよね？」**

「あ・・・そう・・・で・・・すね・・・」と背中冷たいものが走りながらめっちゃ小さい声で返しました。

そう、画面内には他の画面との共通的な動きをする部分があり（システム的には共通ではなかった）、普通に考えて**ある１画面だけ違う動きをすると違和感が出るような部分**でした。

結局、顧客は私の提案を喜んで受け入れ、他の画面にも共通で私の提案したお節介機能が追加されることになり、なんならもう開発完了している画面まで改修することになってしまい、チームメンバーからは大分白い目で見られました。

# どうすれば良かったか

システム全体を見通して考えて〜みたいな反省点もありますが、それ以前に、**過度に求められていること以上のことをするのはやめましょう**。
時間をかければかけるほど良いものができるのは当然で、**限られた予算と時間の中で一番良い結果を残すにはどうすべきか**を考えるバランス感が一番大事だとのちに思いました。
IICS（Informatica Intelligent Cloud Services）CDI(Cloud Data Integration）Masking Transformationの依存マスキング使い方

# IICS（Informatica Intelligent Cloud Services）を使って依存マスキングを実装してみる

昨今、個人情報の取り扱いなどでマスキングを実施しなければならないことが多いですが、マスキングといっても様々な方法がございますので、今回の記事では高度なマスキングが実装できる依存マスキングの使い方を紹介します。

以下、ドキュメントも参考にしてください。
https://docs.informatica.com/ja_jp/integration-cloud/cloud-data-integration/current-version/_transformations_cloud-data-integration_current-version_ditamap/GUID-30C5C27F-A698-4DED-8D72-27AE129B9991/GUID-FDA46EFA-D5AE-4AE4-98B2-D28F9EC92491.html

### 依存マスキングとは
>依存マスキングは、データのカラムを、別のカラムのデータをマスキングするために使用するカスタムディクショナリの値に置き換えます。

マニュアルの記載は上記のようになっておりますが、カスタムディクショナリのレコード単位で依存性をもたせたマスキングを実施することができます。
（こちら、結果を見てみた方がわかりやすいと思います。）

###実際に作ってみる

以下のステップで設定を作っていきます。
 1.リレーショナルディクショナリ接続の設定
今回はOracleを設定します。
※ストレージ接続は一意のマスキングをするための情報保持に使います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2294451/54f701dc-f2ff-8848-befe-c14a955eee52.png)


 2.カスタム置換の設定（ソースデータのM_SEIカラムのマスキング方法でカスタム置換を設定します）
CDI_MASK_DICTIONARYのSEIカラムを辞書カラムに指定し、ディクショナリのカラムで置換できるようにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2294451/1341c400-efd1-c98d-e7ba-33b2c4d47024.png)


 3.依存マスキングの設定
カスタム置換を設定したM_SEIのカラムを依存カラムに設定し、依存出力カラムでマスクするカラム（ディクショナリのMEIカラム）を設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2294451/348791e6-e562-0eaa-ab7a-59432bdc2291.png)


##マスキング結果
今回、テストデータには各都道府県知事の名前を借用させていただいております。
ディクショナリに設定している姓名のレコード単位でマスキングができていることが確認できました。
依存マスキングが設定されていないとカスタムディクショナリの姓名のランダムな組み合わせマスキングが実施されてしまうことがわかります。

■依存マスキングあり
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2294451/57be9724-a606-5e0d-6230-68d5333b7125.png)

■依存マスキングなし
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2294451/ed1a6a02-b37d-c3c1-f442-9484c037f441.png)
sqlite3のクロスコンパイル

ZRouter.orgのportsもどきにsqlite3を入れてあるのですが、脆弱性があったようで、CVEの警告がでたのでアップデートしてみました。

アップデートしたバージョンは3.35.5で、以前は使っていなかったtclshを使っているようです。

portsもどきのupdateは本家のファイルを元にします。バージョンがあまり変わってないときは、バージョンの差し替えだけで済みますが、大きく変わってる場合はMakefileを差し替えてportsもどきの設定を埋め込みます。

tcl86のpkgをインストールしてみたのですが、ビルドに失敗します。

どうもホスト用のツールのバイナリをクロスのコンパイラーでコンパイルしてしまっているようです。

いろいろ調べてみたところホスト用バイナリはBCCで定義されたコマンドでコンパイルされ、これは環境変数のBUILD_CCから設定されていました。このためconfigureにBUILD_CC=/usr/bin/ccオプションをつけるとうまくいきました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/57d96a9b-3b64-d439-c224-8b9a26828488.png)

TS0-TS3を選ばないとビルドできません。ZRouterでは/tmpはmdなので、TS0とTS3はほぼ同じだと思います。
mysqlclientインストール時のエラー

#EC2とMySQLを接続させるときに出会ったエラー
```pip install mysqlclient```
上記をしているのに,runserverすると
```django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module.
Did you install mysqlclient?```
と怒られる。
##解決策
```
sudo yum install python3-devel mysql-devel
sudo yum install gcc
```
してから
```pip install mysqlclient```
するとうまくいった。
Rubyで長文を変数に入れるあれ

いろんな言語扱っていると細かい書き方忘れがちですよね。

`if`ひとつとっても、「`if (...) {...}`だっけ？」、「`if ... then ... end`だっけ？」、「あれ？`then`いらないんだっけ？」、「最後は`end if`だっけ？」となって、1年ぶりに書く言語でNo資料、NoGoogleで書ける自信がありません。

なので久々に書く言語では、過去に書いたソースを流し見したりして、なんとなくの勘を取り戻したりします。

その時に**「こういうことができた気がするけど、書き方完全に忘れた」**ってのがよく出てきて、rubyでよく忘れるのが**ヒアドキュメント**の書き方です。

```konnayatsu.rb
json = <<'EOS'
{
  "language": "Ruby"
}
EOS
```

**ヒアドキュメント**って単語を覚えていればそれでググれば済む話なんですが、この単語が忘れがち。

**<<**を使うことだけは覚えているので**「ruby <<」**とかでググると

```korejanai.rb
ary = [1]
ary << 2
p ary      # [1, 2]
```

配列の代入でがち。
**「ruby << 文字列」**でググっても

```korejanai_reloaded.rb
str = "string"
str.concat "XXX"
p str    # => "stringXXX"

str << "YYY"
p str    # => "stringXXXYYY"

str << 65  # 文字AのASCIIコード
p str    # => "stringXXXYYYA"
```

文字列の連結でがち。

そういうときは、単語の連結は諦めて、いっそ想いをそのままGoogleに伝えてハートとハートをぶつけ合った方が、良い結果が得られます。

https://www.google.com/search?q=ruby+%E8%A4%87%E6%95%B0%E8%A1%8C%E3%81%AE%E6%96%87%E5%AD%97%E5%88%97%E3%81%AE%E7%BE%8E%E3%81%97%E3%81%84%E6%9B%B8%E3%81%8D%E6%96%B9%E3%82%92%E6%95%99%E3%81%88%E3%82%84%E3%81%8C%E3%82%8C
【Swift】個人的によく使うアプリ起動時の分岐処理

私がアプリ起動時の分岐処理によく使う方法です。
以下の３パターンに分けています。

* 初回起動時
* 更新時
* 通常起動

#コード
サンプルコードはSwiftUIですがUIKitでも問題ありません。
SwiftUIをimportすればAppStorageが使えますし、
AppStorageを使わずにUserDefaultsを直接扱っても良いです。

```swift
import SwiftUI

enum LaunchStatus {
    case FirstLaunch
    case NewVersionLaunch
    case Launched
}

class LaunchUtil {
    static let launchedVersionKey = "launchedVersion"
    @AppStorage(launchedVersionKey) static var launchedVersion = ""
    
    static var launchStatus: LaunchStatus {
        get{
            let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
            let launchedVersion = self.launchedVersion
            
            self.launchedVersion = version
            
            if launchedVersion == "" {
                return .FirstLaunch
            }
            
            return version == launchedVersion ? .Launched : .NewVersionLaunch
        }
    }
}


struct ContentView: View {
    
    init(){
        switch LaunchUtil.launchStatus {
            case .FirstLaunch : 
                self.addSampleData()
                self.showTutorial()
            case .NewVersionLaunch : 
                self.showUpdateText()
            default: break
        }
    }

    var body: some View {
        //略
    }

    //略
}
```

#解説
AppStorage（UserDefaults）に初期値を設定し、
起動時に現在のバージョンを取得します。

以下が現在のバージョンの取得です。アプリバージョンを取得できます。

```swift
Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
```

２つの値を比較して以下の通りに条件分岐します。

* 初期値なら初回起動
* 現在の値と異なれば更新時
* 同じ値なら通常起動

サンプルコードでなんとなくわかると思いますが、
私は初回起動時にCoreDataへのサンプルデータの追加とチュートリアル表示、
更新時には更新内容の表示をよく行います。

#ブログ記事の紹介
本記事は最低限の内容にしています。
コードと簡単な説明があれば十分で、余談や細かい説明はあるだけ邪魔という人も多いと思うので
（私もよくあります）

* UserDefault版のコード
* AppStorageとUserDefaultの関係
* もう少し具体的な実用例

などを見たい場合はブログ記事も見てみて下さい。
AdMobの初期化処理もこの辺で行うので一緒に紹介しています。


https://thwork.net/2021/11/29/swift_app_launch/

こちらはAppStorageについてです。

https://thwork.net/2021/08/23/swiftui_userdefaults_appstorage/
HTTPリクエストとレスポンス解説

##背景
HTTPのリクエストとレスポンスの構成が少し複雑なので整理する

##HTTPリクエスト
ちなみにHTTPリクエストというのはクライアント（webブラウザ）から送られる通信のものです。

```
例：　
POST / HTTP/1.1　[リクエストライン]
Host: www.google.co.jp [ヘッダー]
Connection: keep-alive　　[ヘッダー]
User-Agent: Mozilla/5.0(Windows NT 6.1;WOW64)　[ヘッダー] 
Accept-Encoding: gzip,deflate [ヘッダー]

Keyword=AWS [ボディ]
```

HTTPリクエストはは↑のように構成されていて、リクエストラインはHTTPメソッドの種類、そしてHTTPプロトコルの種類を表示しています。

Hostというのはリクエスト先のサーバー名のことです。

Connectionというのは通信状況を示しています。

User-Agentといのはブラウザの種類を示しています。

Accept-Encodingというのは情報を圧縮するときの方式をサーバに伝えるために表示されています。
(WebサイトはHTMLやCSSなどのいろんな言語で構成されていますが、普通に表示させようとするとデータ量がかさんでしまうので、それを減らすために圧縮してから情報をサーバに送ります。このときに複数ある圧縮の方式からクライアントサイドはこの圧縮方式を使っていますよ！というのをサーバに伝えてくれています。)

##HTTPレスポンス
HTTPレスポンスというのはクライアントからのリクエストを受け取ったサーバがそれに返信する形でクライアントに送る情報のことです。

```
例：
HTTP/1.1 200 OK [ステータスライン]
Date: Wed 09 Apr 2021 06:38:47 GMT [ヘッダー]
Cache-Control: private, max-age=0; [ヘッダー]
Content-Type: text/html; charset=UTF-8 [ヘッダー]
Server: gws [ヘッダー]

<html><body> .. [ボディ]
```

HTTPレスポンスは↑のように構成されています。
ステータスラインとはリクエストに対しての結果を伝えています。200 OKとなっている場合は接続が正常だという意味で、これが4XXみたいに百の位が4とかなっているとクライアントエラー(クライアント側のリクエストに何らかのエラーがある)ということを示しています。
そして、その左のHTTP/1.1というのはHTTPプロトコルのversionを示していて、現在では通常HTTP/1.1が使われています。ちなみにHTTP/1.0はtティム・バーナーズリーというインターネット会の始祖的な人が作りました。

Dateは日付を表しています。

Cache-Controlとは読み込んだページをキャッシュとして保存して良いかの確認を表している文です。（キャッシュというのは同じページを開いた時にいちいち最初から読み込んでいるとデータがかさむので、読み込んだページをキャッシュとして保存し、データを節約することができるものです。）

Content-Typeとはページに表示しているデータそのものを表しています。text/htmlであればHTMLとしてユーザーに表示、画像ならimage/pngみたいな感じになります。

serverとはWebサーバの名前です。ここではgoggle serverを意味しています。
TypeOrmでカラム単位またはテーブル単位で大文字小文字の区別をさせる方法


# 前提

MySQLはデフォルトだとカラムで大文字と小文字を区別しません。なので、passwordなどのカラムを作った場合

```
password
PASSOWRD
PassWord
```

のようなものが

```sql
select * from passwords where password = "PaSsWoRd"
```
などのクエリですべてマッチしてしまいます。これを回避するには、Collationに `utf8mb4_bin'や'ascii_bin'の設定が必要です。これをTypeOrmのEntityで設定する方法を紹介します。


# カラム単位で設定

ColumnOptionsにcharsetとcollationがあるので、そちらを設定します。

```typescript
import { Column, Entity } from "typeorm"

@Entity()
export class User {

  @Column({
    charset: "utf8mb4",
    collation: "utf8mb4_bin"
  })
  username!: string

  @Column({
    charset: "ascii",
    collation: "ascii_bin"
  })
  password!: string
}
```

collationに、`{charset}_bin`の設定をしてやると良いです。

# テーブル単位(Entity単位)で設定

EntityOptionsにはcollationの設定が無いのでEngineで代用します。

```typescript
import { Column, Entity } from "typeorm"

@Entity({
  engine: "InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin"
})
export class User {

  @Column()
  username!: string

  // DefaultのCharset以外にする場合は、collationの設定必要
  @Column({
    charset: "ascii",
    collation: "ascii_bin"
  })
  password!: string
}
```
GCP Workflows で base64 変換をする

ドキュメントを探してもわかりにくかったので

GCP Workflows 内で base64 変換をする方法。

文字列をバイト列に変換してから base64 変換します

```
    steps:
    - getBytes:
       call: text.encode
       args:
          text: "aaaaa"
       result: bytes
    - getEncoded:
       call: base64.encode
       args: 
          data: ${bytes}
       result: encoded
```
【javascript】fetch

# fetch
サーバー上からデータを取得することができる。

## case
取得するデータ形式

* json
    * オブジェクトのプロパティは""で囲む
    * 要素の最後にカンマをつけない。

```json

[
  {
    "name": "Bob",
    "age": 23
  },
  {
    "name": "Tim",
    "age": 30
  },
  {
    "name": "Sun",
    "age": 25
  }
]

``` 


```js

fetch('users.json') //取得先のURLセット
console.log(fetch('users.json'))

```
* Promise（すなわちthenが使用できる）とResponseが返却されているのがわかる。

![スクリーンショット 2021-11-29 19.04.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378066/f3b771a9-6ae9-323d-2320-115522e1ffc0.png)

* responseを覗いてみる。
![スクリーンショット 2021-11-29 19.10.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378066/2b8da65f-96e0-aea6-523f-46c3777ae8e6.png)

* prototypeにjsonがある。


* jsonファイルにアクセスする。
    * thenで階層を深く潜り、jsonファイルにアクセスできた。

```js

fetch('users.json').then(function(response){
    console.log(response)
    return response.json();
}).then(function(json){
    console.log(json)
});

```

![スクリーンショット 2021-11-29 19.13.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378066/b5a8ab85-b340-5204-c852-de191ce7ef6b.png)

* jsonファイルを加工する。

```js

fetch('users.json').then(function(response){
    console.log(response)
    return response.json();
}).then(function(json){
    console.log(json)
    for(const user of json){
        console.log(`私は${user.name}です。年齢は${user.age}になりました。`)
    }
});

// 私はBobです。年齢は23になりました。
// 私はTimです。年齢は30になりました。
// 私はSunです。年齢は25になりました。


```

* await asyncで書いてみる

```js

async function fetchTest(){
    const response = await fetch('users.json'); //awaitでpromiseを受け取る。
    const json = await response.json();
    for(const user of json){
        console.log(`私は${user.name}です。年齢は${user.age}になりました。`);
    }
}

fetchTest();


// 私はBobです。年齢は23になりました。
// 私はTimです。年齢は30になりました。
// 私はSunです。年齢は25になりました。
Xcode13から出るConditional downcast from 'String?' to 'String' does nothingについて

![Xcode-13.0](https://img.shields.io/badge/Xcode-13.0-brightgreen) ![Swift-5.5](https://img.shields.io/badge/Swift-5.5-brightgreen) ![iOS-15.0](https://img.shields.io/badge/iOS-15.0-brightgreen)

## はじめに
Xcode13 で以下のような不思議な警告が出るようになってたので共有しときます（原因わかる方いたらぜひ教えて下さい:bow:）。

`Conditional downcast from 'String?' to 'String' does nothing`

<img width="700" alt="hoge" src="https://user-images.githubusercontent.com/34936885/143847259-995061ed-45c9-4192-964d-481d8340d44c.png">

## 現象
やっていることは `hoge?.first` という `String??` から `String?` にしているだけです。

```swift
let hoge: [String?]? = ["hoge"]
let fuga = hoge?.first as? String
print(fuga!)
```

こちらのコードは Xcode12.5.1 では警告が出ないですが Xcode13.0 だと警告が出るようになっていました。

## 対応
とりあえず下記のように書くと警告は出ませんでした。

* パターン１

    ```swift
    let hoge: [String?]? = ["hoge"]
    let foo = hoge?.first
    let piyo = foo as? String
    print(piyo!)
    ```
* パターン２
  
    ```swift
    let hoge: [String?]? = ["hoge"]
    let fuga = hoge?.first ?? nil
    print(fuga!)
    ```
* パターン３

    ```swift
    let hoge: [String?]? = ["hoge"]
    let fuga = hoge?.first?.flatMap { $0 }
    print(fuga!)
    ```

不思議なのは警告がパターン１の `foo as? String` では出ないけど `hoge?.first as? String` で出ること（なんでだろう:thinking:）。

<img width="700" alt="piyo" src="https://user-images.githubusercontent.com/34936885/143847346-b03f4301-501c-44d5-822c-e748499c0624.png">

## おわりに
けっきょく原因はわかりませんでしたがなんか不思議だなあということで記事にしてみました。

なんかわかる方いればぜひ教えて下さい:bow:

Xcode のバージョンアップで今まで問題なかった書き方に警告が出るのはわりとよくある:upside_down:
Javaのシステムでcsvからインポートする機能を実装する方法

まずはJSPから。

formは以下のように書く。

```
<form id="" action="" method="POST" enctype="multipart/form-data">
```

inputのタイプはfileに

```
<div class="tr">
	<label for="csv" class="th">csv:</label>
	<div class="td">
		<input type="file" id="csv" name="csv" required>
	</div>
</div>
```

JSPは以上です。
サーブレットはアノテーションのすぐ下に以下を追記

```
@MultipartConfig(
	maxFileSize=10000000,
	maxRequestSize=10000000,
	fileSizeThreshold=10000000
)
```

doPostを以下のように記述

```
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	// TODO Auto-generated method stub

	// 文字化け対策
	request.setCharacterEncoding("UTF-8");
	response.setContentType("text/html;charset=UTF-8");

	// 接続情報
	db = DB接続情報を代入;
	Connection conn = null;

	// SQL
	PreparedStatement pstmt1 = null;

	// 送信情報の取得
	Part csv = request.getPart("csv");
	BufferedReader br = null;

	try {
		// データベース接続情報取得
		conn = db.getConnection();

		// csv読み込み
		InputStream is = csv.getInputStream();
		InputStreamReader isr = new InputStreamReader(is);
		br = new BufferedReader(isr);
		String line;
					
		while ((line = br.readLine()) != null) {
			String[] data = line.split(",");

			// SQL実行
			String sql1 = "INSERT INTO table(aaa,bbb,ccc) VALUES(?,?,?)";
			pstmt1 = conn.prepareStatement(sql1);
			pstmt1.setString(1, data[0].trim());
			pstmt1.setString(2, data[1].trim());
			pstmt1.setString(3, data[2].trim());
			pstmt1.executeUpdate();
		}

	} catch (Exception e) {
		System.out.println(e.getMessage());
	} finally {
		try {
			pstmt1.close();
		} catch (SQLException e) { }

		try {
			br.close();
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
	}

	response.sendRedirect("/");
}
```

csvファイル情報取得部分

```
Part csv = request.getPart("csv");
BufferedReader br = null;
```

csv読み込み部分

```
InputStream is = csv.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
br = new BufferedReader(isr);
String line;
```

while文でデータベースに保存

```
while ((line = br.readLine()) != null) {
	String[] data = line.split(",");

	// SQL実行
	String sql1 = "INSERT INTO table(aaa,bbb,ccc) VALUES(?,?,?)";
	pstmt1 = conn.prepareStatement(sql1);
	pstmt1.setString(1, data[0].trim());
	pstmt1.setString(2, data[1].trim());
	pstmt1.setString(3, data[2].trim());
	pstmt1.executeUpdate();
}
```

catchで例外処理

```
catch (Exception e) {
	System.out.println(e.getMessage());
}
```

最後にbrを解放して終わり

```
try {
	br.close();
} catch (Exception e) {
	System.out.println(e.getMessage());
}
```
BigQueryのデータセットにアクセス出来るアカウント一覧の取得方法

[BigQuery Advent Calendar 2021](https://qiita.com/advent-calendar/2021/bigquery) 3日目の記事になります！

BigQueryのデータセットにアクセス出来るアカウント一覧を取りたいという場面があり色々苦労しつつも取り方がわかりました。

ここでいうアクセス出来るとはデータセット内のテーブルのデータを参照出来ることを指しています。


このようなデータを取りたい場合は基本的に[CloudAssetInventory](https://cloud.google.com/asset-inventory/docs/overview)を使えば出来ます。

以下のコマンドでまずは取ってみました
org配下の権限をすべて確認するためこちらのコマンドを叩くにはorgレベルの権限が必要となります。

```bash
gcloud asset analyze-iam-policy --organization="XXXXXXX" --full-resource-name='//bigquery.googleapis.com/projects/XXXXXX/datasets/XXXXX' --permissions='bigquery.tables.getData'
```


これでなんとなく取れたのですが、googleGroupで取れてしまうのでGroupに誰がいるかわかりません。

そこで `expand-groups`をつかいます

```bash
gcloud asset analyze-iam-policy --organization="XXXXXXX" --full-resource-name='//bigquery.googleapis.com/projects/XXXXXX/datasets/XXXXX' --permissions='bigquery.tables.getData' --expand-groups
```


こちらにするとグループは展開されました！！
ですが、まだ問題がありプロジェクトレベルでの編集権限ある人など基本ロールをもつアカウントの詳細が表示されませんでした。

一旦他の方法もさぐってみて以下のコマンドだと取れそうだったのですが

```bash
gcloud projects get-iam-policy PROJECT_ID --flatten="bindings[].members" --format="table(bindings)" --filter="bindings.role=(roles/owner,roles/editor,roles/viewer)"
```

こちらで行う場合にはorg配下の全プロジェクトに対してこちらのコマンドを叩かなければいけずあまり現実的ではなさそう



そして、サポートなどに問い合わせた結果、なぜか`permissons`オプションを取ると基本ロールも展開されることがわかりました！！

```bash
gcloud asset analyze-iam-policy --organization="XXXXXXX" --full-resource-name='//bigquery.googleapis.com/projects/PROJECT_ID/datasets/DATASET_ID' --expand-groups
```

これで、dataset配下のテーブルに対して読み取り権限のある人一覧を取得することが出来ました！
上記のコマンドですと余分な情報が色々出てしまうので、とりあえずデータセットに対してなんかしらの権限ある人をだすでよいのでした、雑ですが以下のように絞り込めばとれそうです。

ほんとはjsonで出力してjqコマンドなどで必要な権限の人だけを取ったりするのがいいのかな


```bash
gcloud asset analyze-iam-policy --organization="XXXXXXX" --full-resource-name='//bigquery.googleapis.com/projects/PROJECT_ID/datasets/DATASET_ID' --expand-groups | grep @ |awk -F ":" '{print $NF}' | sort -u
```
Webシステム開発におけるUI設計とUX設計について感じたこと

#記事の内容
自社での新サービス開発を行っているのですが、UI/UX部分の設計を担当するデザイナーさんと一緒に仕事をしています。
UI/UXの設計について感じたことを簡単にメモします。あくまでも私が感じたことです。

#UIとUXの違いってこうかも？と思ったこと
１つのサービスの中に複数の機能があり、その機能の中にチャット機能があったとする
![service.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/476876/e2df779a-bf7f-ba46-d1b0-284eb88d5cc0.png)

このチャット機能単体で考えて、ユーザーがあると便利だと思う機能、見た目を検討していくのがUI設計
![service2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/476876/04b1d128-2fa6-28f7-cce8-58c710c23937.png)

他の機能との連携を強化し、サービス全体の利便性の向上を検討するのがUX設計
![service3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/476876/6f0ee0ca-0497-01c3-c28f-b7b9243ba3f4.png)

#特に強く思ったこと
この例では「チャット」機能を例として挙げていますが、チャット機能のUIは設計すればするほど、ChatworkやSlack、Teamsを使えば良いという結論に辿り着きます。
しかし、UX視点で他機能とチャットとの連携を考えていくと、サービス全体での利便性向上に繋がる使い道が出てきます。
UI設計を軽んじる訳ではありませんが、シンプルな機能だからこそ、このサービスの中にチャットを組み込む意義は何か？を考えるとUX設計が大事だなと感じました
WorkatoのPythonコネクターでスクレイピングをしてみる

# はじめに

先に以下の記事を書かせていただきましたが、その中でlxmlはいいぞ的な感じの内容を書いていました。

https://qiita.com/tabimoba/items/cd32b1ce55e2f2168d0f

そこで、WorkatoのPythonコネクターでlxmlを利用してスクレイピングを試してみました。


# 取得内容

リックソフトのWebサイトの「ニュース」（以下赤枠）の内容を取得してみます。

https://www.ricksoft.jp/ 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64876/29abcdbf-96df-5b42-7dfd-6821cf0069eb.png)


このページのソースを表示すると、次の通りになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64876/15dcbd22-45ea-d943-c359-88ddb90ed7f2.png)

# レシピ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64876/49c4e9b6-45dc-7340-acf5-c3c8edaac424.png)


# コード

Pythonコネクターを追加し、CodeへPythonのコードを入力します。（コード例はスクリーンショットの後にあります）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64876/26722bbd-13c9-24e6-6464-184ceb0933d9.png)


```python
from urllib import request
import lxml.html

def main(input):
	url = 'https://www.ricksoft.jp/'
	res = request.urlopen(url)
	html = res.read().decode()
	res.close()
	root = lxml.html.fromstring(html)
	result = []
	for article in root.xpath('//div[@id="news"]/div/dl'):
		date = article.xpath('./dt')[0].text
		link = url + article.xpath('./dd/a/@href')[0]
		text = article.xpath('./dd/a')[0].text
		result.append({'date':date, 'link':link, 'text':text})
        
	return { 'result': result }
```

# 出力結果

XPathで非常に簡単にHTML要素内のテキストや属性の値が取得できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64876/dbe2f429-ac2b-7c0a-2874-cb2c9062fb04.png)
【javascript】async, await

# async, await
* Promiseをより直感的に使用する目的で作成された
* ES8以上

## async
Promiseを返却する関数の宣言を行う。

* 関数の先頭にasyncと書くとPromiseを返却することが担保されている。
* グローバルコンテキストでは使用できない。関数コンテキストで使用する。


## await
Promiseを返却する関数の非同期処理が完了するまで待つ。


## case

* Promiseチェーンもより簡単に書けるようになる。

* init()関数でsleep関数を実行する。
    * asyncは実行する関数が非同期で実行されることを明示している。使用しないとエラーになる。
    * awaitはresolveで受け取る引数valを受け取ることになる。
        * **すなわちawaitで受け取るのはPromiseのインスタンス**
    * 引数valをもつsleep関数でPromiseを実行
    * setTimeoutで1秒後にresoleve,valをインクリメント
 

```js

function sleep(val) {
  return new Promise(function(resolve) {
    setTimeout(function() {
      console.log(val++);
      resolve(val);
    }, 1000);
  });
}

async function init(){
  let val = await sleep(0)
  val = await sleep(val)
  val = await sleep(val)
  val = await sleep(val)
}

init();

>>> 0
>>> 1
>>> 2
>>> 3

```
AWS CLIで Web サイトを構築、管理、運用する(4日目)

[3日目](https://qiita.com/hirosys/private/29a4ba8bd076cfc95b93)では、更新作業後の公開設定の簡略化を目的としたバケットポリシーについてお伝えしました。
4日目は、[2日目](https://qiita.com/hirosys/private/1d15cbd778635cf9820d)で設定したログファイルのリテンションを設定し、無尽蔵にログファイルが増え続けないようにします。

# 4日目の要約
ログファイルが無尽蔵に増えないようにするよ！

# AWS CLI の準備
このあたりをみて、好きなバージョンとお使いのOSにあった環境設定をしてくださいね。
なんなら、 AWS CloudShell で実行するのも楽でよいと思います。
この記事シリーズは、AWS CloudShell で実行し、実行例を載せています。

バージョン１
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv1.html

バージョン２
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2.html

# 概要
ライフサイクルを設定して一定期間が過ぎたログファイルを削除する。

# さあ、やってみよう！
ログファイルを収集し始めたはよいが、その後どうするかを考える際の用件というのは多々あります。

* 30日過ぎたら、アクセス頻度が低くなるので安い階層に移す？
* 業界基準でン年は保存しておかないとならない？
* 常にセキュリティ監視のためにいじらない？

このあたりを自動的にやってくれるのが、ライフサイクル設定です。

## ライフサイクルを設定する
ライフサイクルのルール(設定)は JSON 形式で指定します。3日目にお伝えした通り、AWS CLI は JSON ファイルを読み込むことができるので、ルールを記述した JSON ファイルを先に作成しておくと作業がしやすいです。

### ライフサイクルルールのファイルを作成する
このルールは、ログファイルが格納されたら30日後に削除されるものです。
必要に応じて期間を長くしたり、階層(Glacerに移行など)設定などを盛り込むとよいでしょう。

```JSON:lifecycle.json
{
    "Rules": [
        {
            "Filter": {
                "Prefix": "/"
            },
            "Status": "Enabled",
            "Expiration": {
                "Days": 30
            },
            "ID": "logfile"
        }
    ]
}
```

## コマンドを実行する
s3api put-bucket-lifecycle-configuration コマンドでライフサイクルを設定します。

```sh
aws s3api put-bucket-lifecycle-configuration --bucket <LogsBUCKETNAME> --lifecycle-configuration file://lifecycle.json
```

このコマンドも正常終了しても特に何も返しません。

念のため、 s3api get-bucket-lifecycle-configuration コマンドで設定が入っていることを確認します。

```sh
aws s3api get-bucket-lifecycle-configuration --bucket <LogsBUCKETNAME>
```

ライフサイクル設定がなされていると、以下の様に JSON 形式で設定内容が出力されます。順序が前後しているかもしれませんが、同等の内容が出力されます。

```JSON
{
    "Rules": [
        {
            "Expiration": {
                "Days": 30
            },
            "ID": "logfile",
            "Filter": {
                "Prefix": "/"
            },
            "Status": "Enabled"
        }
    ]
}
```

また、このライフサイクル設定が入っていると、ログファイル(オブジェクト)に有効期限日時が設定されます。
この有効期限日時を過ぎると、ライフサイクルで設定された動作を行います(この例であれば削除される)。

この有効期限日時は、s3api head-object コマンドで確認できます。

```sh
aws s3api head-object --bucket <LogsBUCKETNAME> --key <OBJECTNAME/LogFile>
```

有効期限日時が設定されていると、**Expiration** に日時が設定されています。

```JSON
{
    "AcceptRanges": "bytes",
    "Expiration": "expiry-date=\"Wed, 30 Dec 2021 00:00:00 GMT\", rule-id=\"logfile\"",
    "LastModified": "2021-11-29T05:12:48+00:00",
    "ContentLength": 4551,
    "ETag": "\"*******************************\"",
    "ContentType": "text/plain",
    "Metadata": {}
}
```

削除動作はこの日時が過ぎ去るまで待つか、試用として、有効期限を1日程度にしてみて確認するのもよいでしょう。

# まとめ
S3 で配信している Web サイトへのアクセスログを無尽蔵に増やすことなく一定期間保持できるようになりました。ログだけではなく、IoT のセンシングデータを一定期間保持して削除するなどといった使い方もできますね。

しかし、まだまだWeb サイトの構築や管理、運用については問題や課題がありますよね。
引き続き見ていきます。

* 今回使ったコマンド
 * s3api put-bucket-lifecycle-configuration
 * s3api get-bucket-lifecycle-configuration
 * s3api head-object
React.js 勉強 #3

React.jsを学んでいく学習記録になります。
今までの記録はこちらから。

[React.js 勉強 #1](https://qiita.com/hase01234/items/82924c7d675587de3366)
[React.js 勉強 #2](https://qiita.com/hase01234/items/75422a9847dddc3f7015)

# propsについて
WEBアプリケーションを作っていってるのですが、Modal機能を実装する際にpropsを使用したので、
メモ程度になりますが、書き留めておきます。

propsはVue.jsでも触ったことがあり、僕の認識では**コンポーネント間のデータ受け渡しができる**という感じです。

まずは簡単に記述していきます。

```src/test.js
import React from 'react'

const ChildrenTest = () => {
  return (
    <div>
      子コンポーネント
    </div>
  )
}

const ParentTest = () => {
  return (
    <div>
      親コンポーネント
      <ChildrenTest />
    </div>
  )
}

export default ParentTest
```

・・・これコンポーネントできてますよね？
独学だと合っているか本当に分からないので不安ですので、間違っていたら教えて頂けますと幸いでございます:pray:

上記を記述すると、ParentTestの内容とChildrenTestの内容が表示されていると思います。
![スクリーンショット 2021-11-29 18.10.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99346/68c1f3e7-adb7-5ccd-060b-361df9aa7faf.png)

じゃあ、ParentTestでの値をChildrenTestに渡したい時はどうすればいいの？って時に活躍するのが、**props**です！！

例えば親コンポーネント(ParentTest)の値を子コンポーネント(ChildrenTest)に渡して表示したい場合、まずは子コンポーネントを呼び出している後ろに渡したい値を追加してあげます。

```src/test.js
import React from 'react'

const ChildrenTest = () => {
  return (
    <div>
      子コンポーネント
    </div>
  )
}

const ParentTest = () => {
  const text = "最近寒いですね"
  return (
    <div>
      親コンポーネント
      <ChildrenTest text={text} />
    </div>
  )
}

export default ParentTest
```

子コンポーネントのtextにtext(最近寒いですね)を渡しています。
では、それをどう受け取るかというと、

```src/test.js
import React from 'react'

const ChildrenTest = (props) => {
  return (
    <div>
      子コンポーネント:{props.text}
    </div>
  )
}

const ParentTest = () => {
  const text = "最近寒いですね"
  return (
    <div>
      親コンポーネント
      <ChildrenTest text={text} />
    </div>
  )
}

export default ParentTest
```

こうすることで、無事コンポーネント間の値の受け渡しに成功しているはずです。
![スクリーンショット 2021-11-29 18.26.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99346/859a8515-06d9-817e-8fe8-6f61223b347d.png)

ちなみに今回は関数コンポーネントで実装していますが、クラスコンポーネントの際では記述の仕方が大きく変わってくるので、注意が必要です。

# 最後に
今回のサンプルでは実用的なイメージは沸かないと思いますが、
僕自身の備忘録として残しておきます。

何か間違いがあれば、ご指摘頂けますと幸いでございます。
WorkatoにPythonコネクターが追加されました


# はじめに
2021年11月、WorkatoにPythonコネクターが追加されました！

https://docs.workato.com/connectors/python.html

これにより、WorkatoではRubyとJavaScipt, Pythonの3つのローコード系ランタイムが提供されることになります。Pythonを得意とするITエンジニアの方にとっても、Pythonに興味のある方にとっても、非常に魅力的なアップデートであろうと思います。

また、ローコード系のコネクターとして、これまでもRubyコネクターとJavaScriptコネクターはありましたが、Pythonコネクターはそれらのコネクターと比べて非常に強力なものとなっています。個人的に感じたPythonコネクターの特長について、以下で簡単に紹介をします。

# Pythonコネクターの特長

## Python 3.9の標準ライブラリが利用できる

Rubyコネクターは、Workatoの機能を補完するには非常に役立つものでしたが、利用可能なメソッドが限定されており、「Rubyが使える」というよりは「Rubyっぽい何かが使える（サブセットの側面が強い）」というものでした。

具体的にどのようなRubyのメソッドが利用可能であるかは以下のページを参照していただければと思いますが、Rubyコネクターで何か機能を実装しようとすると、以下のページに記載されているメソッドの範囲でやりくりをしなければならず、不便に感じることがしばしばありました。

https://docs.workato.com/developing-connectors/sdk/sdk-reference/ruby_methods.html

これに対して、Pythonコネクターの場合はPython 3.9の標準ライブラリより提供されるメソッドがほぼ利用可能となっています（セキュリティ上利用できない、あるいは結果が返されないメソッドもありますが、そのようなものを除けばほぼ全ての標準ライブラリより提供されるメソッドが利用可能です）。標準ライブラリのこのメソッドは使えるだろうか、ということを考える必要がほぼ無いため、ストレスなくスピーディに必要な機能実装を行うことができます。

## 標準出力の結果が取得可能 － 手軽にprintデバッグができる

Rubyコネクターの悩みどころの１つとして、デバッグのしづらさがありました。printデバッグを行いたい場合、デバッグしたい内容をセットするための変数と出力用の要素を用意し、変数の内容を出力用の要素にセットするという作業が必要でした。 

これに対して、Pythonコネクターでは `print` メソッドの出力結果が `log` という要素に出力されるようになっています。デバッグ用の変数を用意したり、出力結果にデバッグ出力用の要素をわざわざ用意して代入する必要はありません（printデバッグのためのハンドリングをレシピ作成者側で行う必要はありません）ので、非常に手軽にprintデバッグが行えます。

## lxmlが使える － XPathでHTMLの処理がはかどる

個人的にWorkatoに望んでいた機能の１つとして、Workato単体で（SaaSやLambda等を利用することなく）Webサイト・システムのスクレイピングや取得したHTMLのパースを簡単に行いたい、ということがあります。

社内外を問わず、APIをもたないWebサイト・システムや、XML/JSON/RSSなどの構造化データの出力に対応しない、レガシーなWebサイト・システムはまだまだ存在します。しかしながらWorkatoにはHTMLパーサーが用意されていないため、これらのWebサイト・システムとの連携にはハードルがありました。

HTMLパーサーを使わず、ノーコード（様々なコネクターやループ、条件分岐を駆使して）やRubyコネクターあるいはJavaScriptコネクターの文字列処理を駆使して必要な値を取得する方法で対応することも可能ですが、実装の難易度が高いという問題がありました。Workatoでの実装がコスト面で現実的ではないため、AWS LambdaやRPAで対応する（Workatoからそれらに処理を任せる）こともしばしばありました。

Pythonコネクターではlxmlライブラリに対応しているため、lxmlライブラリをインポートすることで、XPathを利用して非常に簡単にHTMLの特定の要素のテキストや属性の値を手軽に取得することができます。ノーコードにはなりませんが、これまでと比べて非常に簡単にHTMLを処理できるようになりました。

個人的にはlxmlが使えるのは非常に助かる、これが使えるだけでもPythonコネクターには高い価値があると感じています。

# 最後に

早速Pythonコネクターを試していますが、とても満足しています。これまではRubyコネクターで処理を色々書いていましたが、今後実装するものはPythonコネクターで書いていこうかな、なんて思っています。

なお、Pythonコネクターはコネクター内でインラインでコードを書くことは出来ますが、そのコードをカスタムコネクターに転用することはできません（JavaScriptコネクターも同様）。もし、カスタムコネクターとしての実装を検討する場合は、Rubyコネクターの利用をお勧めします。Rubyコネクターで実装したコードは、カスタムコネクターでも利用することができます。
Miroはいいぞ　～私的おすすめ機能と使い方～

本記事は2021年のMiro Advent Calendar向けに執筆した記事です。
私は仕事の中で日常的にMiroを活用しています。日頃からMiroを使う中で便利だと感じる機能や使い方を紹介します。
完全に個人目線の内容ですのであしからず。

# Miroとの出会い
Miroと出会ったのは、1年半ほど前に新しいチームでアジャイル開発をするようになったのがきっかけでした。
それまでは業務の中でチャットツールやタスク管理ツールは利用してきたものの、全く使用感が異なるのが新鮮だったのを覚えています。

最初の印象としては、**「いろんなことができそうだけど、どんな使い方をすればいいんだろう」**という困惑です。
「付箋を貼る」「線でつなぐ」といった基本的な使い方は直感的にできるものの、与えられたまっさらなボードを前に、どんな風に使うかのイメージがわかなかったのです。
"いろいろできる"はわかるんですが、"どう使うか"は思いつかない、という"手段"はわかるけど"目的"が描けないという感じですね。


# Miroはいいぞ
ここからが本題ですが、そんな私が徐々に使う中で伝えられることとしては「Miroはいいぞ」ということをお伝えできればなと思います。
「そんなの普通にやってるよ」、って方もたくさんいると思いますが、少しでも参考になる部分があると嬉しいです。


# ふりかえり
Miroを使い始めるきっかけになったチームでは日常的にふりかえりを実施しています。ふりかえり、って反省会になりがちなんですよね。
そんなときも、無機質な文字の羅列で淡々とふりかえるのではなく、カラフルなオブジェクトでふりかえりができるだけれでも多少気持ちが和むものです。
[ふりかえりを拡張する「ふりかえりチートシート」](https://qiita.com/viva_tweet_x/items/b06f56ce83038fc2bb8f)←のようなチートシートを参考に、いろいろなふりかえり手法を試してみるとMiroのカラフルさと相まっていい感じです。
こちらの例では、キャッチーなモチーフを使いながら「チームを加速してくれるもの（ロケット）」「チーム活動の阻害要因（隕石）」「チームのありたい状態（右上の惑星）」について感じることを共有し、自分たちの現状や有りたい姿を話し合ったものです。

<img width="1000" alt="チームでのふりかえり" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/754254/f8b8f1ed-2d16-ea5e-a209-0d3572dc7316.png">


# ステークホルダーマップ
私の所属している組織では、社内のプロジェクトに支援として参画することがああり、いろいろな組織の人達と一緒に業務をする機会が多いです。
いくつもの組織の人と仕事をしていると、「△△は誰に相談すればいいんだっけ」「〇〇さん、ってどういうつながりで会話したんだっけ、、」とたまに忘れてしまうことがあります。
そんなときに役立つのがステークホルダーマップです。ステークホルダーマップがあると、忘れたときの思い出しや新しいメンバーが加わったときに、チームを取り巻く環境がみえやすくなります。
Miroのいいところは、共同編集ができることや、フレーム機能、オブジェクトをつなぐ機能が使いやすいところですね。
フレーム機能でささっとグルーピングしたり、オブジェクト同士をつなげるのが簡単です。
また、ファイルとして保存する描画ツールと違いオンラインで見れるため、最新版をダウンロードする必要がありません。
「ファイル更新されてるからダウンロードしないと、、、」など考えずに、ダッシュボードからポチッとすれば、常に最新の状態が見れます。

<img width="1000" alt="ステークホルダーマップ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/754254/a602a6ab-d87a-00fa-f75b-597408a87354.png">


# 成果物のイメージすり合わせ
私のチームでは、開発やら企画やらなんでもやります。
そんなこんなでまだ見ぬ成果物について会話することが多いんですね。
そうするとよくあるのが「やりたいことは分かったけど、成果物はどういうイメージになるんだろう」「あーはいはいそういうの作ればいいのね（実際のイメージと違う）」ということがよくあります。
そんなときに↓みたいな感じでイメージを書いてしまえば一発ですね。仮にバシッと完璧にイメージが合っていなくても、そんなにかけはなれた物が出来上がることはあまりないです。

<img width="1000" alt="イメージすり合わせ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/754254/07df84da-f4ca-ac77-994a-795128baded8.png">



# おわりに
いかがだったでしょうか。
調べてみるとMiroでバーチャルなオープンキャンパスをしているような教育機関もあったりするようです。使い方は無限大ですね！
どういう使い方ができるか、は徐々にノウハウが溜まってきているものの、まだまだ自分で新しい使い方を生み出すということはできてないなと感じています。
いろいろな使い方をどんどん発信していき、生き生きとした社会になっていくといいですね！
【SwiftUI】GeometryReaderを使うと座標がずれる

## 現象
環境はXCode13.1。

座標や縦・横幅の取得に便利な`GeometryReader`を使った時に、階層下の`VStack`内で生成したUI部品の座標がずれたので解決策をメモ。
`HStack`や`ZStack`では未検証。

以下のようなコードを書いていた。（サイズなどは適当）

```.swift
GeometryReader { geometry in
    VStack() {
        Button(action: {}){
            Text("Button")
                .foregroundColor(Color.white)
                .padding(.all, 10.0)
                .background(Color.blue)
        }
    }
}
```

おや...？
<img width="300" alt="scleenshot-1" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163599/6427e708-0626-45d1-0cc8-d10edff9fe65.png">
明らかに中央から表示がずれている...？

## 原因
`VStack`に色をつけてみるとこんな感じになる。
<img width="300" alt="scleenshot-2" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163599/72df6abc-d380-b20c-13ec-ddf5317a6d21.png">
見やすさのために`height`を指定してやるとこんな感じ。
<img width="300" alt="scleenshot-3" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163599/282a86ec-4c05-33b0-f6e9-def79d914658.png">

**なぜかWidthが画面幅より狭くなっている...**

## 解決策
と、いうことで`VStack`を画面いっぱいまで広げてやればいいので、以下のようにコードを変更。

```.swift
GeometryReader { geometry in
    VStack() {
        Button(action: {}){
            Text("Button")
                .foregroundColor(Color.white)
                .padding(.all, 10.0)
                .background(Color.blue)
        }
        .frame(width: geometry.frame(in:.local).width, height: /*適当に*/)
        .background(Color.red)
    }
}
```

<img width="300" alt="scleenshot-3" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163599/937298d9-835d-2f9b-ce4d-515fe5454bd7.png">
無事解決した。

`geometry`自体の横幅は画面サイズいっぱいまで広がっているので、原因をなかなか特定できなかった。
これは仕様なのかバグなのか...
Javaのシステムで画像を取り扱う方法

まずはJSPのformで下記を追記します。

```
enctype="multipart/form-data"
```

こんな風になります。

```
<form action="" method="POST" enctype="multipart/form-data">
```

インプットタグはタイプをfileにします。

```
<div class="tr">
	<label for="logo">画像:</label>
	<div class="td">
		<input type="file" id="logo" name="logo">
	</div>
</div>
```

JSPは以上です。

サーブレットでは以下の記述をアノテーションのすぐ下に追記します。

```
@MultipartConfig(
	maxFileSize=10000000,
	maxRequestSize=10000000,
	fileSizeThreshold=10000000
)
```

画像データの取り込み部分

```
// 画像のファイル名取得
Part part = request.getPart("logo");
String logo = Paths.get(part.getSubmittedFileName()).getFileName().toString();
String logo_name = logo.isEmpty() ? "" : logo;
```

logo_nameは文字列としてDBに保存します。画像ファイルそのものは新しく作ったフォルダに保存します。
まずWEB-INFと同じディレクトリに空のフォルダを作ります。（ここではlogoという名前でフォルダを作ります。）

次に以下のコードで記述します。

```
// 画像アップロード
String path = getServletContext().getRealPath("/logo");
part.write(path + File.separator + logo_name);
```

以上で画像データの保存はできました。
保存した画像データの表示は以下の記述でできます。

```
<img src="/コンテキストルート/logo/DBに保存したlogo_nameの文字列">
```
[AWS] AWS Single Sign-On で Organizations 管理外の AWS アカウントにアクセスする

複数の AWS アカウントを使い分けている場合、[AWS Organizations](https://aws.amazon.com/jp/organizations/) と、[AWS Single Sign-On](https://aws.amazon.com/jp/single-sign-on/) を組み合わせてユーザ管理するってことが多いと思います。
AWS Single Sign-On では、AWS Organizations 配下ではない AWS アカウントに対してもアクセスできるように設定することができるので、必要に応じて使ってみましょう。
なお Organizations 配下の AWS アカウントに対しては提供されているコマンドラインアクセス用のトークン( "Command line or programmatic access" のリンクから取得できるもの )は提供されず、AWS ダッシュボードにアクセスできるだけとなりますので、ご注意ください。

# SSO 側の AWS アカウント

## アプリケーションとして External AWS Account を追加

Applications から New Application　として `External AWS Account` を追加します。
![スクリーンショット 2021-11-29 17.29.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25964/92d15825-2232-24be-26c2-c6f02b77c0af.png)

次に表示される画面で `AWS SSO SAML metadata file` をダウンロードしておきます。
他の箇所は特に必要する必要はないです。
`Display name` と `Description` だけ、お好みで変更してください。
![スクリーンショット 2021-11-29 17.30.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25964/4220dd9d-1cea-1f31-ca6a-f58c7f1b6249.png)

# SSO される側の AWS アカウント

SSO される側の AWS アカウントでは、IAM で Identity Providor を追加し、それに割り当てる IAM ロールを作成します。
AWS SSO　からアクセスした際に、その IAM ロールが利用されます。

## Identity Providor の追加

IAM のサイドメニューから [Identity providers](https://console.aws.amazon.com/iamv2/home?#/identity_providers) を選択して `Add providor` しましょう。
`Providor name` を適当に設定し `Metadata document` として、先ほど SSO 側の AWS アカウントでダウンロードした `AWS SSO SAML metadata file` をアップロードします。
![スクリーンショット 2021-11-29 17.41.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25964/d564ed96-605d-a7a5-4e9f-3baa13f7873f.png)

作成した Identity providor をクリックして詳細を表示し、ARN をコピーしておきます。
`arn:aws:iam::{AWS account ID}:saml-provider/{Providor name}` のような形式になっていると思います。
これは後で使います。

## IAM ロールの割り当て

作成した Identity providor をクリックして詳細を表示し、`Assign role` をクリックします。
新しく IAM Role を作ってもいいし、既存の IAM Role を使ってもいいです。
![スクリーンショット 2021-11-29 17.48.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25964/63816f9a-48e1-0646-149f-b823c57b89b7.png)

作成した IAM Role の ARN もコピーしておいてください。
`arn:aws:iam::{AWS account ID}:role/{Role name}` のような形式になっていると思います。

これも後で使います。

# SSO 側の AWS アカウント

再び SSO側の AWS アカウントに戻ってきます。
ここで、先ほど作った Identity providor と IAM Role の情報を設定します。

## Attribute mapping の追加

作成したアプリケーションをクリックして詳細を表示し、`Attribute mappings` タブで `Add new attribute mapping` をクリックして追加します。
内容は以下の通り

項目|値
---|---
**User attribute in the application** | https://aws.amazon.com/SAML/Attributes/Role
**Maps to this string value or user attribute in AWS SSO**|arn:aws:iam::{AWS account ID}:saml-provider/{Providor name},arn:aws:iam::{AWS account ID}:role/{Role name}
**Format**|unspecified

`Maps to this string value or user attribute in AWS SSO` は Identity Providor の ARN と IAM Role の ARN を `,` 区切りで書いてください。

## アプリケーションへのユーザ( or グループ )の割り当て

あとは、このアプリケーションを使用できるユーザ(or グループ)を割り当ててあげれば良いです。
Organizations 配下の AWS アカウントのようにパーミションセットを設定して複数の権限を割り当てることはできないので、複数の権限を割り当てたい時は、複数のアプリケーションを作成してください。

これで AWS SSO のスタート画面に External AWS Account が追加されているはずです。
Webの勉強はじめてみた その5 ～JavaScript編 演算子～

今日も1コマ。
論理値について勉強しました。
if文って基礎にしてかなり厄介な気がする。
そういやSelect文ってあるんだっけ。JSはSwitchか。

<h2>今日やったこと</h2>
:::note
1. 比較演算子
2. 論理演算子
:::

<h2>気付いたこと</h2>
<h3>1. 比較演算子</h3>
:::note
== と ===
!= と !==
:::
曖昧さをとにかく排除するなら===や!==を使うのがいい。
変数の宣言に型の明示を必要としないから、こういうのも気をつけたほうがいいのかも。

:::note warn
変数の初期値にnull
:::
VB.Net だと宣言した型によって空文字とか0を入れてた。
どっちがいいんだろう。

<h3>2. 論理演算子</h3>
:::note
否定(NOT): !
論理和(OR): ||
論理積(AND): &&
排他的論理和(XOR)
:::
試される国語力。。
ビット演算子と区別するために記号を重ねてるらしい。
以前にphpで使ったことあるけれどビットはややこしかった。

:::note warn
論理値を扱う変数はあたまにisをつける
:::
言語によって違いそうだけど、頭に置いておく。

<h3>まとめ</h3>
変数名に直接的な英単語が多いのかなという印象。
.NETだと予約されてる単語や名前空間が多かったので、少し違和感。
GCPのVMインスタンスにOps Agent入れてログモニタリング

GCPのVMインスタンス上のLaravelのログをモニタリングしたかったので、その設定メモ
今回は新しいVMを使っているので実施していませんが、古いVMを使っている場合、ログをロギングサービスに飛ばすのに[サービスアカウントが必要](https://cloud.google.com/logging/docs/agent/ops-agent/authorization)なようです。


参考: https://cloud.google.com/logging/docs/agent/ops-agent
対象OS: Ubuntu 18.04 LTS

## Ops Agentのインストール

VMインスタンスにSSHログインして下記を実行

```
curl -sSO https://dl.google.com/cloudagents/add-google-cloud-ops-agent-repo.sh
sudo bash add-google-cloud-ops-agent-repo.sh --also-install
```



## 設定ファイルを作成して反映

このファイルはユーザー定義ファイルとなっており、デフォルトの設定ファイルに追加設定したい場合に作成します
シンタックスエラーとかあると再起動時にエラーになります。

```
sudo vim /etc/google-cloud-ops-agent/config.yaml
------------------
logging:
  receivers:
    laravel_log:
      type: files
      include_paths:
        - /var/www/html/laravel/storage/logs/*.log

  service:
    pipelines:
      custom_pipeline:
        receivers: [laravel_log]
-----------------

# 反映
sudo service google-cloud-ops-agent restart
```

この設定ファイルがちょっと難しくて、より複雑な設定をする場合、例えばtcpポートでログを受け取るなどは下記のドキュメントを読み込むと良いと思います。
https://cloud.google.com/logging/docs/agent/ops-agent/configuration
初歩的な仕様把握のやり方

##記事概要
~~（個人的に）~~頭に入りやすいと思う仕様把握のやり方
#初めに
この記事は私個人が頭に入りやすいと思う仕様把握のやり方をまとめたものになります。
以下の項目に該当する方にはもしかすると少しなら今後のテスト業務に活かせるようなヒントとなる内容になっているかもしれません。
**・仕様把握って何！？どこから始めたらいいの！？
・仕様把握しているけど、なかなか仕様が頭に入ってこない…
・仕様把握中メモも取ってるけどごちゃごちゃしてしまって何があったのかよく分からなくなる**
##仕様把握のやり方　その①
ではさっそく本題に入っていきたいと思います。
「仕様把握って何！？どこから始めたらいいの！？」となる方、特にテスト業務が初めての新卒の方に多いのではないでしょうか。

私も新卒で入社して最初に言われたことが**「じゃあとりあえず仕様把握してて」**でした。
語感からどういう作業を指示されているのかは分かります。
**”仕様”**を**”把握”**する。そういうことですよね。

ではどうやって仕様把握を行っていくか。
私も入社当時、ただ闇雲に仕様書を読んでもなかなか頭に内容が入って来なくて、
**（これただ読んでるだけじゃダメだな）**と早々に気づきました。
##仕様把握のやり方　その②
私が仕様把握を行う際に気を付けている点は以下になります。

**1.仕様書名に番号が振られている場合は番号が若い順から見ていく**

**2.ゲーム画面の画像が仕様書に貼られている場合は、
　画面の一番左上から徐々に右下の方へ向かって表示物や機能などの仕様を把握する**

**3.思いついた観点は仕様把握がまだ終わってなくても忘れないうちにメモする**

では少し詳しく解説していってみます。
***
**1.仕様書名に番号が振られている場合は番号が若い順から見ていく**
こちらは読んで字のごとくですね。
大体番号が若いものから順に【タイトル画面】⇒【ログイン】⇒【ホーム画面】⇒…
となっている仕様書が多いため**番号には従いましょう。**

番号が記載されていない仕様書に関しても、
**アプリ起動後のフローを考えて仕様書を読んでいくといいと思います。**
***
**2.ゲーム画面の画像が仕様書に貼られている場合は、
　画面の一番左上から徐々に右下の方へ向かって画面上の表示物や機能などの仕様を把握する**
これを意識することで、仕様説明がごちゃごちゃしている仕様書でも、確認漏れ無く仕様把握ができる…はずです。
***
**3.思いついた観点は仕様把握がまだ終わってなくても忘れないうちにメモする**
仕様把握中に思いついた観点、**「めんどくさいから後からまとめてメモしよう。」**等と思っている方いらっしゃいませんか。
めんどくさくても思いついた時点でメモした方がいいです。

**「仕様がまだ全部分からないし、仕様書にざっと目を通した後でまとめてメモしよう。」**と思っていても、後からメモする段階になるとほぼ確実に忘れています。

また、メモを取る際も**「自分が書いたものだから適当に書いてても分かるだろう」**と思って雑にメモを取ると、後から見返した時にほぼ確実に分かりません。

**雑なメモの例**
・新規登録
└入力ミス
└認証メール送信

**丁寧なメモの例**
・新規登録確認
└入力が不完全な場合登録できないか
　└パスワード間違い
　└メールアドレス間違い
　└既存アカウントと重複
└入力が完全な場合認証メールが送信されるか

また、テストパターンやテスト条件等、仕様把握の段階で思いつくものはすべて丁寧にメモを取っておいた方が後々楽になると思います。
##最後に
本当に初歩的なことしか書いていない記事でしたが、誰かのお役に立っていたなら幸いです。
最後までご覧いただきありがとうございました。
【RSpec】APIテストでformat: :json

##環境
Ruby 3.0.2
Rails 6.1.4.1


```routes.rb```で```format: :json```指定しておくと、
APIテストの方で省略できる。

```config/routes.rb
Rails.application.routes.draw do
  namespace :api, format: 'json' do
    resources :users
  end
end
```

```spec.rb
get api_v1_abc_path(format: :json)

#↓

get '/api/v1/abc'
```

##参考

https://qiita.com/kat0/items/be6114fb4694b9576304