OmniAICreator/Qiita-1.07M huggingface dataset:
【Linux】管理者(root)権限を持ちたい

単純にrootになる場合は su コマンドを使う。

```
$ su -
Password:rootのパスワードを入力
```

sudo コマンドでもrootになれる。

```
$ sudo -s
```

コマンドを実行する時に先頭に sudo をつけても良いが、パスワード聞かれる。

```
$ sudo cat /var/log/messages
```

一般ユーザがsudoを使ってrootになれるように設定をする必要がある。
/etc/sudoers が設定ファイルだが、viコマンドなどで直接編集することは推奨されていない。
visudo コマンドを使って編集する方法が強く推奨されている。

rootのパスワードが流れないので、sudo -s でrootになった方が安全な気もする。
が、sudoが使えるユーザーのパスワードが漏れたらアウト。

## ユーザがsudoを使えるようにする方法

```
対象のユーザを wheel グループに追加する。
# usermod -G wheel user1

ユーザが wheel グループに所属することを確認する。
# id user1
uid=501(user1) gid=501(user1) groups=501(user1),10(wheel)
```

visudoコマンドを使って設定する。

```
sudo -s を実行した時に、ユーザーのパスワード入力を求める場合は以下をコメントアウトから外す。
---------------------------------------------------
## Allows people in group wheel to run all commands
 %wheel ALL=(ALL)       ALL　　★ここ
---------------------------------------------------
```
```
sudo -s を実行した時に、ユーザーのパスワード入力を求めない場合は以下をコメントアウトから外す。
---------------------------------------------------
## Same thing without a password
 %wheel ALL=(ALL)       NOPASSWD: ALL　　★ここ
---------------------------------------------------
```
djangoのエラー'OperationalError'の解決方法

#環境
mac 10.15.4
django 3.1.1

#何が起こったか
`runserver`をしても表題エラーが表示されるので、その解決方法を記載します。

#OperationalErrorとはどのようなエラーなのか
単直に言って『モデルを作成、変更したらデータベースに反映！』をしなかったが故のエラーです。
djangoでは自分でデータベースをいじらなくていい代わりに、マイグレーションを行う必要があります。今回はこのマイグレーションをスッポ抜かしてました。

#どうやるのか
そもそもマイグレーションファイルを作成する必要があります。その作成をするのが
`python manage.py makemigrations`です。
作成したマイグレーションファイルを適用しなければ使えません。`python manage.py migrate`でデータベースに適用することができます。
Raspberry Pi環境作成メモ

## 初めに
家で眠っていいたRaspberry piを有効に使いたいと思ったので、環境を構築した。
リモートワークがメインとなり、自宅でmacとwindows、linuxといろいろ動かしている点と使用してない外付けHDDがあったためファイルサーバーを作成することにした。

## やったこと
### Raspberr Pi のアップデート(Wheezy -> buster)
https://www.raspberrypi.org/documentation/raspbian/updating.md

- 公式ページから最新OSイメージをダウンロード
- 公式ページからOS作成用にRaspberry Pi Imagerをダウンロードし、Windows PCにインストール
- SDカードにRaspberry pi Imagerを使用して書き込み

### Raspberr Piの初期設定

- ssh接続の許可
    - GUIでシステム設定から許可に変更
- IPを固定化
    - /etc/dhcpcd.conf内を書き換え
 
### Disk設定

ファイルシステムはexFATでフォーマット

```
sudo apt install exfat-fuse
sudo apt install exfat-utils
sudo mkfs -t exfat /dev/sdb2
sudo mkfs -t exfat /dev/sda2
mkdir /mnt/v1
mount /dev/sda2 /mnt/v1
```

### Samba設定

```
sudo apt install samba
　# WINS設定はデフォルトの<いいえ>でインストール
vi /etc/samba/smb.conf
  # 以下を追記
  [share]
   path = /mnt/v1
   writable = yes
   guest ok = yes
   guest only = yes
   create mode = 0777
   directory mode = 0777
testparm
sudo systemctl restart smbd nmbd
sudo systemctl enable smbd nmbd
```

### アクセス

- windows: computeアイコンを右クリック。ネットワークドライブの割り当てを選択し、フォルダーに\\IP\shareを入力する
- mac: finderでsmb://IP/shareを入力する
EclipseのXdebugが勝手に起動してしまう

PHPアプリケーションをxamppを利用して開発をしている時に、Eclipseを使用して開発中のWebサイトへブラウザからアクセスするたびに、勝手にEclipseのXdebugが起動してしまい、Webサイトへアクセスできないことがあります。
Eclipseを閉じれば再度アクセス可能になりますが、これでは開発効率が悪いと思います。
この事象は以下の方法で、回避することが可能です。

- 【xampp】のパス\PHP\php.iniを開く
- xdebug.remote_enableをxdebug.remote_enable=0とする
- xdebug.remote_autostartをxdebug.remote_autostart=0とする
デバイスのライトを点ける

フラッシュや懐中電灯に使われているライトを使う方法です。
点灯消灯を制御できます。

![Oct-06-2020 10-21-18.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/1a6d6f70-bf4a-d3aa-e763-da2e68f159e1.gif)


AVFoundationのAVCaptureDeviceから制御できます。

#1,点灯

```swift
let avCaptureDevice = AVCaptureDevice.default(for: AVMediaType.video)

if avCaptureDevice!.hasTorch, avCaptureDevice!.isTorchAvailable { // キャプチャデバイスにライトがあるか、　ライトが使用可能な状態か
    do {
        try avCaptureDevice!.lockForConfiguration() // デバイスにアクセスするときはこれする。
        try avCaptureDevice!.setTorchModeOn(level: 1.0) // 点灯。明るさレベルは 0.0 ~ 1.0
    } catch let error {
        print(error)
    }
    avCaptureDevice!.unlockForConfiguration()
}
```

torchMode = .on　でも最大レベルで点灯できます。
torchMode = .auto　は「キャプチャデバイスは継続的に光のレベルを監視し、必要に応じてトーチを使用します。」とのことです。

#2,消灯

```swift
do {
    try avCaptureDevice!.lockForConfiguration()
} catch let error {
    print(error)
}
avCaptureDevice!.torchMode = .off
avCaptureDevice!.unlockForConfiguration()
```

🐣

****
お仕事のご相談こちらまで
rockyshikoku@gmail.com

Core MLを使ったアプリを作っています。
機械学習関連の情報を発信しています。

[Twitter](https://twitter.com/JackdeS11)
[Medium](https://medium.com/@rockyshikoku)
ZendeskのサポートチケットデータをRedshiftへロードする

今回のブログでは、XplentyのRest APIコンポーネントを使用し、ZendeskのSupport APIにアクセスし、チケットデータをIncrementalでロードする手順について解説します。

#Zendesk側での設定
管理メニューから「API」を選択する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/43e897ae-32cf-4f4c-ab18-f6b5e84627cd.png)



パスワードアクセスを有効に設定する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/309cab76-bcc9-b657-1c50-e4492908d77f.png)


#Xplenty側の設定
##Step1: Rest API Componentの設定
Rest API コンポーネントでBasic認証を選択し、ユーザー名とパスワードを入力する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/c0deea48-8ce4-bbb1-83f1-d0587472a182.png)

今回は、Support APIのIncremental Exportを使用するので、[ドキュメント](https://developer.zendesk.com/rest_api/docs/support/incremental_export)のルールに従い、URLを入力。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/b0e61022-a817-5804-6feb-4c143f3eaa2e.png)


**URL**: https://**サブドメイン名**.zendesk.com/api/v2/**incremental**/tickets.json?**start_time=1601768082**&include=metric_sets

**start_time**: UnixTimeの数値を指定

ResponseでJSON Pathを入力
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/6e8d5f88-b655-84e7-405e-2b519a15b4fd.png)

Data Previewで結果が正しく取得できるのを確認したら、必要な列を選択し、Saveボタンをクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/f6fa0706-ad33-3eee-368a-0fdc5890aeda.png)

##Step2: Selectコンポーネントで入れ子になっているJSON部分の値を取得する式をセット
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/5976609c-005a-e433-eee9-2d6493774dbc.png)

##Step3: ロード先のDBもしくはDWHを設定（今回はRedshiftへロード）
Redshiftへの接続を設定し、テーブル名の入力および差分更新の設定（Merge with existing data using delete and insert）を選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/209f1a24-fbf6-772c-d288-f14d6f5e81bd.png)


今回作成したデータフローは、こんな感じになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/e6beb6fc-984e-8c36-0963-b1d83b452041.png)

##Step4: 自動で差分更新を行うための変数設定
最初の設定では、URLのパラメータ「start_time」を固定値で実装しましたが、今後自動的に前回取り込み分からの差分を取得できるよう変数を利用したリクエストURLが生成できるよう設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/0f5c8de2-4049-3c3f-6213-e4858e666e89.png)

###1. 差分更新のため、前回実行時間を取得する変数をセットします。 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/68e4fbc9-6974-f8af-1876-0d1b73eb6ac9.png)

- **変数名**：start_date
- **式**：ToUnixTime(CASE WHEN **$_PACKAGE_LAST_SUCCESSFUL_JOB_SUBMISSION_TIMESTAMP** == '' THEN ToDate('2000-01-01T00:00:00.000Z') ELSE ToDate(**$_PACKAGE_LAST_SUCCESSFUL_JOB_SUBMISSION_TIMESTAMP**) END)

式で使用しているシステム変数「**$_PACKAGE_LAST_SUCCESSFUL_JOB_SUBMISSION_TIMESTAMP**」には、自動的に前回ジョブ実行日時がセットされる。

###2. URLのstart_timeの値に変数をセットします。（太字部分は変数）
**URL**: https://サブドメイン名.zendesk.com/api/v2/incremental/tickets.json?start_time=**$start_date**&include=metric_sets
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65461/1fa36062-e087-9f3b-d0d4-9e1c2013cb6b.png)

これで簡単に差分だけを更新するDataflowパッケージの完成です。
もし、自社でお使いのSaaSサービスをクラウドに接続して取得したい場合は、[Xplenty](https://www.xplenty.com/jp/)のHPからオンラインデモをリクエストし、相談してみてはいかがでしょうか？
MySQL 8.0 にしたら datetime カラムが truncated されて人生に悩んだ

# 背景

MySQL 8.0にしてしばらく、おかしいデータが有ることに気付いた。
INSERT文を見てみたら成功しているがwarningが出ている。失敗ではないのでエラーを上げておらず気づかなかった。

> 'Data truncated for column x at row 1' 

よくあるのは、varchar(255) のカラムに 256文字以上のデータを突っ込んだときだ。この場合最初に255文字だけがinsertされて、後ろは切り捨てられる。
そう思って見ると、そのカラムは datetime 型だった。
datetimeでtruncatedってなんやろな。

Insertしようとしてるデータは `2020-10-02T12:34:56.000+0900` だった。ああ・・・。
MySQL 5.7の頃はそれでもtruncatedされて　`2020-10-02 12:34:56` がinsertされていたが、目の前のMySQL 8.0では truncate された結果 `0000-00-00 00:00:00` が入っていた。なるほど・・・


# @@sql_mode

`0000-00-00 00:00:00` はそのdatetime columnのデフォルト値だ。warningのためにそのfieldに当てたデータはすべて切り捨てられデフォルト値が入った。この動きはあれだ。sql_modeだ。移行前後の環境を比較した。

## mysql 5.7

```sql
select @@sql_mode;
ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
```

## mysql 8.0

```sql
select @@sql_mode;
'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'
```

STRICT_TRANS_TABLES が原因でいいと思うのだが、Manualを読んでも自分が経験した事象と合わなくてよくわからなかった。が、真面目に読み進めたらあっさり原因が書いていた。

> If strict mode is enabled, '0000-00-00' is not permitted and inserts produce an error, unless IGNORE is given as well. For `INSERT IGNORE and UPDATE IGNORE`, '0000-00-00' is permitted and inserts produce a warning.
> https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html#sql-mode-strict

strict mode というのは `either STRICT_ALL_TABLES or STRICT_TRANS_TABLES is enabled` のときだ。今回は `STRICT_TRANS_TABLES` が有効だったので **strict mode is enabled** だ。

その状態では本来 '0000-00-00' はエラーになるが、 `INSERT IGNORE` するとwarningは出るが成功する（値が入る）。これが原因だった。

# 解決法

`STRICT_TRANS_TABLES` を外すというのは最高最短の解決法だ。しかし一歩上を目指すならやっちゃいけない。いいエンジニアになるには遠回りする勇気が必要らしい。

### 1. INSERT文から IGNORE を外す。

とりあえず、で作ったコードにはIGNOREが入りまくっている。しかしいつの間にかそのサービスの運用は義務になり止まると怒られが発生される。いいエンジニアは最初からIGNOREを使わないでスパパっと完璧なコードが書けるものだ。そうなりたいものだ。ひとつずつでいいから IGNORE は消していこう。`STRICT_TRANS_TABLES` な時代には似合わないstatementだ。

### 2. truncateしない文字数のデータをinsertする

今回は Jira API から返ってきた date っぽい値をそのまんま INSERT していた。事前に必要な文字数だけに短くして INSERT した。

 `2020-10-02T12:34:56.000+0900` →  `2020-10-02T12:34:56`

最初はやっつけでよかったコードだ。APIをそのまま渡したら入った。そのまま運用した。この判断が悪かったのだろうか。これまで一年動き続けた価値はあると思っていたが。最近こういう結果がよかったのかどうか、よくわからない。その時の僕の開発スタイルはこうだ。API結果はできるだけ残したいので、必要そうな値をまとめてINSERT IGNOREで投げてみる。warningが出ようが、重要なカラムさえ入っていればok. あとは問題になってから直せばいい。そんなこんなで全部NULLとか0000なカラムが僕のテーブルにはよくある。それが僕をハメることもしばしばだ。最初からwarningを見ていれば今回の問題は防げたのだろう。しかしsmall startにそのコストが見合うのだろうか？　答えは、一発で完璧なことができるスーパーエンジニアになるしかないような気がする。


# まとめ

型を意識するのがいいエンジニアの条件らしい。php/jsから入った自分にはその意識がまったくもって希薄だ。

warningもエラーと見なす運用体制が必要だ。まずは IGNORE を消していこう。がんばろう。

で、そもそもなんでこのIGNOREがあったかというと `INSERT ON DUPLICATE KEY UPDATE` 的な話があってそこはそこで改めて勉強し直さないとな、というところ

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">我々は何故2015年にもなってSELECTして無かったらINSERTする場合のベストパターンを確立できていないのか。</p>&mdash; songmu (@songmu) <a href="https://twitter.com/songmu/status/621672337784451073?ref_src=twsrc%5Etfw">July 16, 2015</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

> https://songmu.jp/riji/entry/2015-07-20-insert-ignore.html

おしまい
ServiceNow - システムプロパティを利用する

#ServiceNowのシステムプロパティの使い方

ServiceNowインスタンス共有のプロパティを定義することができる。
定義したプロパティをスクリプトから参照することが可能。

例：
```
var sys_prop = gs.getProperty('system.properties.name');
```

##プロパティを定義する
1.アプリケーションナビゲータから「sys_properties.list」を検索。システムプロパティ一覧が表示される。
![system_properties_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698027/c994eb06-2b86-bcff-0726-8cde03a81903.png)
2.「新規」ボタンを押下
![system_properties_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698027/034ffaab-218d-777f-6926-d7d8bed12138.png)
3.「名前」にプロパティ名、値にプロパティの値を入力した後に「送信」ボタンを押下。これでプロパティはServiceNowインスタンスで定義される。
![system_properties_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698027/06c7b8b2-ed5c-27a7-8a5f-03abef853216.png)
##プロパティの利用
プロパティはサーバ側スクリプトで利用可能
例：
「スクリプト-バックグラウンド」を開き、次のスクリプトを実行

```
var ip_address = gs.getProperty('ozawa.internal.service.ip');
gs.info("ip:" + ip_address);
```
![system_properties_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698027/8437cfa4-fc02-98b1-e419-aa8d4b4e5522.png)
実行結果：
![system_properties_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698027/efd399a9-729c-18c0-0671-749a62eaf69f.png)
脱トラックパッド

## 脱トラックパッド計画
普段の業務ではvimを使うこともありトラックパッドをあまり触りたくなくなってきたのでキーボードのショートカットキーを覚えていこうと決意。
現在の設定のメモ。これから追加していきます。

### 普段
| キー | 動作 |
|--- | ---| 
| ⌘+h| 一文字削除|

### chrome(vimumあり)
| キー | 動作 |
| --- | --- |
| ⌘+l| 検索窓に移動|
| d | 下に50%スクロール|
| u | 上に50%スクロール|
| H | 前のタブに移動 |
| L | 次のタブに移動 |
| h | 戻る |
| j | 進む |
Mendix 銀行選択ウィジェット

# はじめに

Mendixで、簡単に銀行名を選択したい時に使えるウィジェットを使ってみます。このウィジェットは、ピエールソフト様の [bank-auto](https://github.com/zipaddr/zipaddr.github.io) ライブラリー（無償版）を使用して、オンラインで銀行名、コードを選択することができます。アプリ内に銀行データベースなどを用意する必要がありません。
このサンプルウィジェットでは、ピエールソフト様のbank-autoライブラリー（無償版）を利用させて頂いています。

# 準備

- あらかじめ、Mendixでアプリを作成しておきます。
- [こちら](https://github.com/naofum/Mendix-BankcodeWidget/releases) からMendix銀行選択ウィジェット (BankcodeWidget.mpk
) をダウンロードします。
- ダウンロードしたファイルを、アプリのプロジェクトフォルダー下にあるwidgetsフォルダーにコピーします。通常プロジェクトフォルダーは、C:¥Users¥<ユーザーID>¥Documents¥Mendix¥アプリ名-main のような場所にあります。

# 画面設定

アプリに画面を作成します。
この画面は少なくとも、銀行名、銀行コード、支店名、支店コードを入力する４つのテキストボックスを含みます。

![bankcode1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/751319/7d517945-eeba-3cae-5a19-0ab1bcca025b.png)

この画面に、「add widget」で銀行選択ウィジェットを追加します。

![bankcode3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/751319/5f1f9431-83cb-4a21-3155-f1d05b13f400.png)

ウィジェットのプロパティには、上記で作成した４つのテキストボックスの名前を指定します。

![bankcode2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/751319/095c08e3-b40f-64cf-c60c-928fb2ff072f.png)

銀行コードを入力し始めると、前方一致で選択肢が表示されます。

![bankcode4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/751319/4753a456-2a19-74bf-4c7c-ca405beb4900.png)

一覧を選択すると、銀行名、銀行コードがセットされます。支店も同様です。

![bankcode5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/751319/ea0eb3e5-4e79-0c00-eb15-f6ac6c965fe5.png)

検索は名前からも、コードからも行うことができます。

![bankcode6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/751319/f497f91f-ebfe-6785-abf6-7c527ebb6389.png)

# 謝辞

bank-auto ライブラリーでは、ピエールソフト様により金融機関情報が更新されており、最新の情報を利用することができます。ライブラリー（無償版）を利用させて頂き、ピエールソフト様大変ありがとうございます。
数学公式(3D処理用・ベクトル関連)

# 数学公式（3D処理用・ベクトル関連）

- 以下は3D処理用・ベクトルに関する数学公式
- これを利用すれば1通りの作業ができるはずと思いメモを残す
- ベクトルや座標をなるべく(x, y, z)に分解して式を書くようにした

---

## ベクトルの大きさ（長さ・ノルム）

$$
\mid \vec{a} \mid = \sqrt{a_x^2 +a_y^2 +a_z^2}
$$

- ベクトルの長さ（ノルム）を求める式
- ルート計算があるので多様すると処理に時間がかかる
  - 距離の比較だけで正確な数値が必要なければそのままルート計算せずに比較したほうが良い

---

## 内積の定義と代数的定義

- $\thetaはラジアン$
$$
\vec{a} \cdot  \vec{b} = \mid \vec{a} \mid \mid \vec{b} \mid \cos \theta
$$
$$
\vec{a} \cdot \vec{b} = a_x b_x + a_y b_y + a_z b_z \cdots
$$

- 内積を求める公式

---

## 外積

$$
\vec{a} \times \vec{b} = (a_yb_z-b_ya_z , (-1)(a_xb_z - b_xa_z) , a_xb_y-b_xa_y)
$$

- 外積を求める式
- $\vec{a}$を$\vec{b}$として傾ける場合の回転軸を求めるときに利用した

---

## ベクトルのなす角

- $\vec{a}$ $\vec{b}$は交差するベクトル
$$
\cos \theta  = \frac{\vec{a} \cdot  \vec{b}}{\mid \vec{a} \mid \mid \vec{b} \mid} = \frac{a_xb_x + a_yb_y + a_zb_z}{\sqrt{a_x^2 +a_y^2 +a_z^2}\sqrt{b_x^2 +b_y^2 +b_z^2}}
$$
$$
\theta  = \cos^{-1}\left(\frac{a_xb_x + a_yb_y + a_zb_z}{\sqrt{a_x^2 +a_y^2 +a_z^2} \sqrt{b_x^2 +b_y^2 +b_z^2}}\right)
$$

- $\vec{a}$を$\vec{b}$として傾ける場合の回転角度を求めるときに利用した
- このなす角と上記外積を使って、ロドリゲスの回転公式の表現行列にあてはめれば、$\vec{a}$から$\vec{b}$への回転マトリクスとして利用可能
- 主に3Dモデルの構成点をぐるっと回したいときに利用した
- この$\theta$は0～180度（0～$\pi$)の範囲で返すため、回転方向に留意する必要がある

---

## 面積（ベクトルで二辺を指定した三角形の面積）

- $\vec{a}$ $\vec{b}$は交差するベクトル
- $\frac{1}{2}$を取り払えば台形の面積となる
$$
S = \frac{1}{2}\sqrt{\mid\vec{a}\mid^2\mid\vec{b}\mid^2-(\vec{a} \cdot \vec{b})} = \mid\vec{a} \times \vec{b}\mid
$$
$$
S = \frac{1}{2}\sqrt{(a_yb_z - a_zb_y)^2 +(a_zb_x - a_xb_z)^2 +(a_xb_y - a_yb_x)^2}
$$

- 3Dモデルを三角形パッチで分割した後、表面積を求めるために利用したがルートが計算に入っているので多様すると面積計算に異常に時間がかかってしまう
- 最後の最後にルートすれば・・・どうだろうか

---

## ロドリゲスの回転公式の表現行列

- $\vec{n}$は回転軸となるベクトル
- $\theta$は回転角度（ラジアン）
$$
Rn(\theta)=\begin{pmatrix}
n_x^2(1-\cos\theta)+\cos\theta & n_xn_y(1-\cos\theta)-n_z\sin\theta & n_xn_z(1-\cos\theta)+n_y\sin\theta \\\\
n_xn_y(1-\cos\theta)+n_z\sin\theta & n_y^2(1-\cos\theta)-\cos\theta & n_yn_z(1-\cos\theta)+n_x\sin\theta \\\\
n_xn_z(1-\cos\theta)+n_y\sin\theta & n_yn_z(1-\cos\theta)-n_x\sin\theta & n_z^2(1-\cos\theta)+\cos\theta \\\\
\end{pmatrix}
$$

- 外積となす角を元に3Dモデルを回転したい場合に利用した
- ただし、$\theta$は$\pm$を持たない場合に回転させた方向があっていない場合があるため、考慮が必要となる（場合がある）

---

## ラジアンから度

$$\deg=rad\times\frac{180.0}{\pi}$$

- 備忘録
 
---

## 度からラジアン

$$rad=\deg\times\frac{\pi}{180.0}$$

- 備忘録
[Android開発]Javaで画像をサーバーから取得してImageViewに設定しよう！！

#説明
画像ファイルをURLで参照して、サーバーから取得する方法です。（非同期処理）

#Java

```java:GetImagesUrl
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.widget.ImageView;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;

public class ImageUrl extends AsyncTask<Integer, Integer, Bitmap> {

    ImageView bmImage;

    public ImageUrl(ImageView bmImage) {
        this.bmImage = bmImage;
    }

    @Override
    public Bitmap doInBackground(Integer... integers) {
        Bitmap image;
        URL imageUrl = null;
        try {
            //引数の数字で取得するURLを変える
            //Staric変数として画像のURLを定義しよう
            if (integers[0] == 0) {
                imageUrl = new URL(Constants.Image1);
            } else if (integers[0] == 1) {
                imageUrl = new URL(Constants.Image2);
            } else if (integers[0] == 2) {
                imageUrl = new URL(Constants.Image3);
            }
            InputStream imageIs;
            imageIs = imageUrl.openStream();
            image = BitmapFactory.decodeStream(imageIs);
        } catch (MalformedURLException e) {
            return null;
        } catch (IOException e) {
            return null;
        }
        return image;
    }

    protected void onPostExecute(Bitmap result) {
        bmImage.setImageBitmap(result);
    }
}
```

##実行

```java:main
 int ImageNum;
 //ImageNumに入っている数値で対象の画像を取得して、ImageViewに設定する
 new ImageUrl((ImageView) view.findViewById(R.id.any_image)).execute(ImageNum);
```
MySQL dumpファイルを読み込む

## 目的

- dumpファイルをMySQLに読み込む方法をまとめる

## 実施環境

- ハードウェア環境

| 項目 | 情報 |
| --- | --- |
| OS | macOS Catalina(10.15.5) |
| ハードウェア | MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports) |
| プロセッサ | 2 GHz クアッドコアIntel Core i5 |
| メモリ | 32 GB 3733 MHz LPDDR4 |
| グラフィックス | Intel Iris Plus Graphics 1536 MB |

- ソフトウェア環境

| 項目 | 情報 | 備考 |
| --- | --- | --- |
| MySQLバージョン | 8.0.19 for osx10.13 on x86_64 | Homwbrewを用いてこちらの方法で導入→[Mac HomebrewでMySQLをインストールする](https://qiita.com/miriwo/items/ef6a064bc545fbea29d9) |

## 前提条件

- 実施環境に準ずる環境が構築されていること。
- MySQLが導入されMySQLのrootアカウントのパスワードがわかっていること。
    - MySQLのrootアカウントのパスワードがわからなくなってしまった方はこちら→[Mac ローカル環境の MySQL 8.x のrootパスワードを忘れた時のリセット方法](https://qiita.com/miriwo/items/1880e9d2ebcfd3c0e60d)
- dumpファイルがすでに用意されていること。

## 前提情報

- `~/dump`ディレクトリ直下にdumpファイル「dump_yyyymmdd.sql」が存在するとする。
- MySQLのデータベース「test」にdumpファイル「dump_yyyymmdd.sql」を読み込ませる方法をまとめる。

## 詳細

1. 下記コマンドを実行して`~/dump`ディレクトリに移動する。

    ```terminal
    cd ~/dump
    ```
1. 下記コマンドを実行してdumpファイルを読み込む。（RDSのエンドポイントを指定する場合`-h`オプションをつけてエンドポイントを指定する。）

    ```terminal
    mysql -u root -pルートユーザのパスワード dumpを読み込むデータベース名 < dumpファイル名
    ```
1. MySQLのデータベース「test」にdumpファイル「dump_yyyymmdd.sql」を読み込ませるときは下記のようになる。

    ```terminal
    mysql -u root -pルートユーザのパスワード test < dump_yyyymmdd.sql
    ```
Accessのクエリがオーバーフローしたので対処した

Microsoft Access で作った業務アプリケーションが、
**実行時エラー '6': オーバーフロー**
と表示され処理が止まるというので調べてみた。
![overflow](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/50781187-9ff1-faf3-f0c1-4a622614623c.png)
オーバーフローがVBAコード中の代入文で停止している場合は、対処は難しくない。
数値はデータ型ごとに扱える値の範囲が決まっていて、その範囲を超えて代入しようとすると、このエラーになる。
業務アプリでありがちなのは、`Integer`で宣言した変数に32767を超える値を代入しようとした … など。

厄介なのはクエリの実行中にオーバーフローした場合。
大抵はサブクエリを含むと思うが、どのクエリの？どの式で？オーバーフローが発生したかまではVBE（VBAの統合開発環境）で教えてくれないからだ。

こんなとき、貴方がSQLを読めるなら、クエリを**SQLビュー**に切り替えて覗くことを勧める。
![sqlview.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/43d25019-4bbc-4e10-160a-5442b6a9b742.png)
とはいえ、Accessは**SQLフォーマッタ**を用意しないので、そのままでは見づらい。
SQLを整形してくれるオンラインサイトを活用しよう。

- [SQLフォーマッターFor WEB](https://atl2.net/webtool/sql%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%82%BF%E3%83%BCfor-web/)
- [SQL整形フォーマッター](https://araishi.com/sql-formatter/)
- [SQL文整形](https://rakko.tools/tools/37/)
- [Instant SQL Formatter](http://www.dpriver.com/pp/sqlformat.htm)

例えば [SQLフォーマッターFor WEB](https://atl2.net/webtool/sql%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%82%BF%E3%83%BCfor-web/) にかけると、こんな感じで整形してくれる。

```sql:SQL
SELECT
    T03_住民税.市区町村コード,
    T03_住民税.指定番号,
    Q16_当月人事.社員番号,
    Q11_当月異動届参照.氏名,
    Q11_当月異動届参照.入社年月日,
    Q11_当月異動届参照.退職発令日,
    Q11_当月異動届参照.異動後会社コード AS 当月会社コード,
    Q11_当月異動届参照.市区町村コード AS 当月市区町村,
    Q11_当月異動届参照.会社コード AS 前月会社コード,
    Q12_前月異動届参照.市区町村コード AS 前月市区町村,
    T03_住民税.[9月] AS 前月住民税,
    T03_住民税.[10月] AS 当月住民税,
    IIf(
        [当月住民税] <> [当月徴収税額],
        '税額相違',
        ''
    ) AS 税額相違,
    IIf(
        [当月住民税] <> [前月住民税],
        '税額変更',
        ''
    ) AS 税額変更,
    IIf(
        [当月住民税] = 0,
        '当月抹消',
        ''
    ) AS 当月抹消,
    IIf(
        [当月会社コード] = '',
        '',
        IIf(
            [当月会社コード] <> [前月会社コード],
            '会社変更',
            ''
        )
    ) AS 会社変更,
    T03_住民税.年税額,
    T03_住民税.[6月],
    T03_住民税.[7月],
    T03_住民税.[8月],
    T03_住民税.[9月],
    T03_住民税.[10月],
    T03_住民税.[11月],
    T03_住民税.[12月],
    T03_住民税.[1月],
    T03_住民税.[2月],
    T03_住民税.[3月],
    T03_住民税.[4月],
    T03_住民税.[5月],
    Q18_当月徴収税額.住民税 AS 当月徴収税額
FROM
    (
        (
            (
                Q16_当月人事
                LEFT JOIN
                    Q11_当月異動届参照
                ON  Q16_当月人事.社員番号 = Q11_当月異動届参照.社員番号
            )
            LEFT JOIN
                Q12_前月異動届参照
            ON  Q16_当月人事.社員番号 = Q12_前月異動届参照.社員番号
        )
        LEFT JOIN
            T03_住民税
        ON  Q16_当月人事.社員番号 = T03_住民税.社員番号
    )
    LEFT JOIN
        Q18_当月徴収税額
    ON  Q16_当月人事.社員番号 = Q18_当月徴収税額.社員コード
WHERE
    (
        T03_住民税.[9月] <> 0
    )
OR  (
        T03_住民税.[10月] <> 0
    )
ORDER BY
    T03_住民税.市区町村コード,
    T03_住民税.指定番号,
    Q16_当月人事.社員番号
```

これで怪しそうな箇所が、ひと目で分かる。
今回は入力データに不備があり、ゼロで除算したことによるオーバーフローであった。
クエリで発生するオーバーフローは、このパターンが多い。
ServiceNow - 親テーブル／子テーブルを探す

#概要
ServiceNowではテーブルの属性が継承される。CIの構成を把握するためにも親テーブルを把握したい場合がある。

##親テーブルを表示する
次のスクリプトはベーステーブルまで親テーブルを表示する。

```
var tableName = 'cmdb_ci_linux_server';

var table = new TableUtils(tableName);
gs.info("parent tables:" + table.getTables());
gs.info("base table:" + table.getAbsoluteBase());
```
実行結果：

```
*** Script: parent tables:[cmdb_ci_linux_server, cmdb_ci_server, cmdb_ci_computer, cmdb_ci_hardware, cmdb_ci, cmdb]
*** Script: base table:cmdb_ci
```

##継承さあれているか確認する
スクリプトでテーブルが継承しているか確認する

```
var tableName = 'cmdb_hardware_product_model';
var parentTable = 'cmdb_ci';

var ci = new GlideRecord(tableName);
if (ci.instanceOf(parentTable )) {
  gs.print('yes.its a part of ' + parentTable);
} else {
  gs.print('NO.Its not a part of ' + parentTable);
}
```
実行結果：

```
*** Script: NO.Its not a part of cmdb_ci
```

##継承しているテーブル一覧を表示(子テーブル一覧を表示)

```
var tableName = 'cmdb_ci_unix_server';

var table = new TableUtils(tableName);
gs.print(table.getTableExtensions());
```
メモ: DNAT、SNAT の設定

![images1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25728/a28924ef-d635-ba77-cd98-60c247ee079f.png)


## DNAT

### Yamaha

* [[RTX1100] Destination NAT (静的 IP マスカレード) の設定を行う。 | 僕とガジェット](https://www.gadgets-today.net/?p=980)

```go
nat descriptor masquerade static 2 1 172.16.1.2 udp 500=500
nat descriptor masquerade static 2 2 172.16.1.2 udp 4500=4500
```

### VyOS

```go
set nat destination rule 10 description 'Port Forward: HTTP to 192.168.0.100'

set nat destination rule 10 destination port '80'
set nat destination rule 10 inbound-interface 'eth0'
set nat destination rule 10 protocol 'tcp'
set nat destination rule 10 translation address '192.168.0.100'
```

## Linux (iptables)

* [LinuxのiptablesでDNATするメモ | 日常系エンジニアのTech Blog](https://sky-joker.tech/2018/11/25/linux%E3%81%AEiptables%E3%81%A6%E3%82%99dnat%E3%81%99%E3%82%8B%E3%83%A1%E3%83%A2/)

### OS側の準備

```go:/etc/sysctl.d/99-ipv4_ip_forward.conf
net.ipv4.ip_forward=1
```

```sh
sysctl -p
```

### DNAT ( 送信先アドレスを変換 )

* [参考](https://yoru9zine.hatenablog.com/entry/2015/12/19/072304)
* 入ってくるパケットのIPを変換
    * インターネットからLAN内にアクセスする時に、送信先のパブリックIPアドレス → ローカルIPに変換
* `192.168.100.1:80` に来た通信 を `192.168.0.117:80` に転送

```go
iptables -t nat \
 -A PREROUTING \
 -p tcp \
 -d 192.168.100.1 \
 --dport 80 -j DNAT \
 --to-destination 192.168.0.117:80
```

### SNAT ( 送信元アドレスを変換 )

* 送信元IPアドレスの変換
    * 送信元のプライベートIPアドレス → パブリックIPアドレスに変換
* [Linuxで作るファイアウォール［NAT設定編］ (1/2)：ゼロから始めるLinuxセキュリティ（4） - ＠IT](https://www.atmarkit.co.jp/ait/articles/0111/23/news003.html)
* `192.168.0.117`にアクセスするとき、送信元アドレスを`192.168.0.118`に変換する例

```go
iptables -t nat \
 -A POSTROUTING \
 -p tcp \
 -d 192.168.0.117 \
 --dport 80 \
 -j SNAT \
 --to-source 192.168.0.118
```
【Go】DBへのRead/Writeでエンドポイントを分けたい

# はじめに

レプリケーションに対応するため、DBのエンドポイントをRead/Writeで分けることにしました。
Go/Gorm2でどのように対応するか検討しました。

## 利用環境
- Go: 1.15.2
- Gorm2: v1.20.1
- Echo: v4.1.17

# 結論から

Endpoint別に複数のConnectionを持ったTypeに、Gormの各Methodをラップした。
こうすることで、connectionを利用する側はendpointを意識せずにDBへの操作を可能にしています。

```db/connect.go
package db

import (
	"os"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

type SqlHandler struct {
	ReadConn  *gorm.DB
	WriteConn *gorm.DB
}

func Connect() *SqlHandler {
	readConn, err := gorm.Open(GetDBConfig("read"))
	if err != nil {
		panic(err.Error())
	}

	writeConn, err := gorm.Open(GetDBConfig("write"))
	if err != nil {
		panic(err.Error())
	}

	sqlHandler := SqlHandler{
		ReadConn:  readConn,
		WriteConn: writeConn,
	}

	return &sqlHandler
}

func GetDBConfig(endpoint string) (gorm.Dialector, *gorm.Config) {
	USER := "myUser"
	PASS := "hoge1234"
	PROTOCOL := "tcp(" + endpoint + ":5050)"
	DBNAME := "myDB"
	OPTION := "charset=utf8&parseTime=True&loc=Local"

	mysqlConfig := mysql.Config{
		DSN: USER + ":" + PASS + "@" + PROTOCOL + "/" + DBNAME + "?" + OPTION,
	}

	config := &gorm.Config{}

	return mysql.New(mysqlConfig), config
}

func (handler *SqlHandler) Find(out interface{}, where ...interface{}) *gorm.DB {
	return handler.ReadConn.Find(out, where...)
}

func (handler *SqlHandler) Exec(sql string, values ...interface{}) *gorm.DB {
	return handler.WriteConn.Exec(sql, values...)
}

func (handler *SqlHandler) First(out interface{}, where ...interface{}) *gorm.DB {
	return handler.ReadConn.Find(out, where...)
}

func (handler *SqlHandler) Raw(sql string, values ...interface{}) *gorm.DB {
	return handler.WriteConn.Raw(sql, values...)
}

func (handler *SqlHandler) Create(value interface{}) *gorm.DB {
	return handler.WriteConn.Create(value)
}

func (handler *SqlHandler) Save(value interface{}) *gorm.DB {
	return handler.WriteConn.Save(value)
}

func (handler *SqlHandler) Delete(value interface{}) *gorm.DB {
	return handler.WriteConn.Delete(value)
}

func (handler *SqlHandler) Where(query interface{}, args ...interface{}) *gorm.DB {
	return handler.WriteConn.Where(query, args...)
}

```

# レプリケーション

## is 何

[レプリケーション](https://ja.wikipedia.org/wiki/%E3%83%AC%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3)

## 気になる所

`func Exec` や `func Raw`など、queryそのものが発行できる部分については、read/write双方の可能性があり、どちらに振り分けるか悩むところです。
今回はwriteに振り分けました。なるべく利用者はendpointを意識せずに使えるようにしたい...

# おわりに

CQRSを利用することを決めており、そもそもSQLベタ書きが増えそうな気がしています。
Gormを使う意味が少し薄れますが、やはりORMとしてのTypeへのマッピングを考えると欲しいところ。
マッピング部分だけ自作したりすると良いのかなと思います！！

# 参考 

- [Golang - EchoとGORMでClean Architecture APIを構築する](https://qiita.com/so-heee/items/0cca93008eae635c642a)
React(CRA)とRailsAPIを同一リポジトリで動かす方法

##前提条件
- Heroku blogの記事から必要最小限の部分だけ引用したものになります
- Ruby、Rails、Node、npmまたはyarnなどの基本的なインストールが済んでいること
- Railsアプリを作成したことがある
- Reactアプリをcreate-react-appで作成したことがある
- Herokuでホスティングをしたことがある(gitでデプロイ)

[参考記事：A Rock Solid, Modern Web Stack—Rails 5 API + ActiveAdmin + Create React App on Heroku](https://blog.heroku.com/a-rock-solid-modern-web-stack)

## Railsアプリの作成

rails new　コマンドでrails アプリを作成します。

--apiコマンドでapiモードにするのをお忘れなく

```terminal
rails new rails_app --api
```

作成したアプリが起動するか確かめましょう。

```terminal:
cd rails_app
rails s
```

## Reactアプリの作成

Railsアプリのルートディレクトリ上でclientという名前でReactアプリを構築します。

```terminal:
npm create-react-app client
```

Railsと同じように、Reactが動くか確かめましょう。

```terminal:
cd client
npm start 
```

このままだと開発中にReactとRailsで別ポートとなるので
package.jsonに設定を追加します。

```json:package.json
{
  "name": "client",
  "version": "0.1.0",
  "private": true,
  "proxy": "http://localhost:3001", #ここを追加
  ...
}
```

この設定によりRails側には、Reactアプリがポート3001で起動しているように認識させます。


## React - Rails ローカル編

ローカル環境立ち上げ用のタスクを作成します。

ProcfileにReactとRailsを立ち上げるコマンドを書き、Railsのタスクで呼び出す方法にします。

Procfileはheroku上で利用されるファイルで、プロセスのタイプ：コマンドで記述できます。

```
<process type>: <command>
```

今回は、ルートディレクトリにProcfile.devというファイル名で、RailsアプリとReactアプリの起動コマンドを記述します。

```:Procfile.dev
web: cd client && PORT=3000 npm start
api: PORT=3001 && bundle exec rails s
```

呼び出しコマンドを簡潔にしたいため、taskにします。

```ruby:start.rake
namespace :start do
    desc 'Start development server'
    task :dev do
      exec 'heroku local -f Procfile.dev'
    end
end
```

taskでprocfileの起動コマンドをキックします。

```terminal
rake start:dev
```

ReactとRailsがどちらも立ち上がったと思います。

ターミナル上のログでわかりづらい場合はRails側で何かしらJsonを返すようにしておき、React側からfetchできるようにしておくとわかりやすいと思います。

## React - Rails Heroku編

メインテーマであるHeroku上へのデプロイです。

まずはルートディレクトリにpackage.jsonファイルを作成します。

RailsのpublicディレクトリにReactの静的ファイルを作成するコマンドを記述します。

```json:package.json
{
    "name": "list-of-ingredients",
    "license": "MIT",
    "engines": {
      "node": "10.15.3",
      "yarn": "1.15.2"
    },
    "scripts": {
      "build": "yarn --cwd client install && yarn --cwd client build",
      "deploy": "cp -a client/build/. public/",
      "heroku-postbuild": "yarn build && yarn deploy"
    }
}

```


production用のProcfileを作成し、railsの起動コマンドを記述します。

必要に応じてrelease後のmigrateコマンドも記述します。

```:Procfile
web: bundle exec rails s
release: bin/rake db:migrate
```

<br>
CLIを利用してheroku上のdynoの作成とビルドパックの設定を行います。

```terminal:アプリの作成
heroku apps:create
```

```terminal:ビルドパックの設定
heroku buildpacks:add heroku/nodejs --index 1
heroku buildpacks:add heroku/ruby --index 2
```


heroku上の準備ができたのでgitでpushします。

```
git add .
git commit -m "React - Rails"
git push heroku master
```

アプリを開きます。

```
heroku open
```
## あとがき

みなさん無事に動きましたでしょうか？

もっと良い方法があれば是非教えていただけるとありがたいです。

もし動かない場合は下記の参考元の記事を読んでみてください！

[参考記事：A Rock Solid, Modern Web Stack—Rails 5 API + ActiveAdmin + Create React App on Heroku](https://blog.heroku.com/a-rock-solid-modern-web-stack)
【React Native】 SafeAreaView

## SafeAreaVieww

SafeAreaViewは、デバイスのセーフエリア境界内にコンテンツをレンダリングします。
現在、iOSバージョン11以降を搭載したiOSデバイスにのみ適用されます。
SafeAreaViewコンポーネント使用することでステータスバーにコンテンツが被ることなくレンダリングされます。

| SafeAreaView使用 |SafeAreaView未使用 | 
|:-----------|------------:|
| ![SafeAreaView.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595437/3fa61d17-1654-0f2e-6103-6b33db7c8051.png) | ![withoutSafeAreaView.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595437/24e599bc-0a13-3a57-d24c-df40a2e8f995.png)|


### paddingスタイルは効かない

SafeAreaViewに適用されたスタイルのpaddingは無視されます。
解決方法としては、SafeAreaView内のコンテンツに対してpadding適用させます。

```jsx
export function SafeAreaViewAndStatusBar({}: Props) {
  return (
    <SafeAreaView style={styles.root}>
      <View style={styles.paddingTop}>
        <Text style={styles.text}>hoge</Text>
      </View>
    </SafeAreaView>
  );
}

// SafeAreaView要素に対してpaddingを適用してもスタイルは反映されません
// 代わりにSafeAreaView内のコンテンツにスタイルを適用させる事で問題を解決する。
const styles = StyleSheet.create({
  root: {
    flex: 1
  },
  view: {
    paddingTop: 30
  },
  text: {
    fontSize: 30,
  },
});
```

### ステータスバーのみ余白をとる
下記の画像の通り、SafeAreaViewでコンテンツをラップすると画面下にも余白が生まれてしまいます。
これを解決するには`<SafeAreaView />`とする必要があります。

```jsx
export default function Sample2({}) {
  return (
    <View style={{flex: 1}}>
      <SafeAreaView />
      <ScrollView
        scrollEventThrottle={16}
        onScroll={Animated.event([
          {nativeEvent: {contentOffset: {y: scrollY}}},
        ])}>
        <View>
          ....
        </View>   
      </ScrollView>
    </View>
  );
}
```

### Android対応
ステータスバーの余白はiOSのみに適用されます。
そのため、Androidに余白を設定させる必要があります。

まずreact-nativeから**StatusBar**をimportしてそのプロパティであるcurrentHeightからステータスバーの高さを取ります。
Platfotmがandroidであれば**StatusBar.currentHeight**で取得した高さを余白として**paddingTop**に設定します。

```jsx
import React from "react";
import {
  SafeAreaView,
  StyleSheet,
  Platform,
  StatusBar,
  Text,
  View,
} from "react-native";

export default function Sample() {
  return (
    <SafeAreaView style={styles.root}>
      <StatusBar />
      <View>
        <Text>status bar</Text>
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  root: {
    backgroundColor: "white",
    paddingTop: Platform.OS === "android" ? StatusBar.currentHeight : 0,
  },
});
```
【React Native】ScrollView

### 概要
- ページ内にいくつかのコンポーネントを表示したいが画面からはみ出してしまった場合などに`ScrollView`を用いる。

→ 理由は`Viewコンポーネント`を使用した場合では、はみ出した要素をスクロールして確認する事が出来ないためである。

- `ScrollView`はラップしている全ての子要素を一度にレンダリングさせるため、`パフォーマンスはあまり良くない`。(FlatListはlazyロードするので一度に全てを表示する事はない)
- ScrollView内でmap処理などをする事があればFlatListコンポーネントを使うべきである。
- ScrollViewを用いれば縦スクロールだけでなく横スクロールの制御も可能になる。
- contentContainerStyleでList内のスタイルを設定する事が出来る。

### 公式の見解

ScrollViewは、限られたサイズのものを表示するのに最適である。
ScrollView内ではスクリーン上に表示されていない要素含め全ての要素がレンダリングされてしまう。
画面内に収まりきらない多くのアイテムリストを表示させる場合、ScrollViewの代わりに`FlatList`を使用すべきである。

## ScrollView使用時の親要素のスタイルはflexGrow: 1

scrollEnabledがtrueの場合のみ要素のスクロールが可能になる

→ defaultではスクロール可能になっているので要素が画面より大きい場合にスクロールONにする時などに有効

```tsx
const [contentHeight, setContentHeight] = useState(0);
const handleContentSizeChange = (width, height) => {
  setContentHeight(height);
};
const {width, height} = Dimensions.get('window'); // hooksのuseWindowDimensions()を用いてもwidht, heightを取得する事が出来る
const scrollEnabled = contentHeight > height;

// rootはflexGrow: 1にしないと要素が表示されなくなる
<SafeAreaView style={styles.root}>
  <ScrollView
    contentContainerStyle={styles.scrollView}
    onContentSizeChange={handleContentSizeChange}
		scrollEnabled={scrollEnabled}
  >
    <Text style={styles.text}>
      Lorem ipsum dolor sit amet, consectetur adipisicing elit. Illo,
      inventore, maiores? Accusantium iure nam odio officia. Aut autem,
      consequuntur cum dignissimos dolorum excepturi fugiat hic minima
      molestias nesciunt provident ut?
    </Text>
  </ScrollView>
</SafeAreaView>
```

## スクロールバーを表示させない

`showsVerticalScrollIndicator`にfalseを渡す事でスクロールバーを非表示にする。

```tsx
<ScrollView showsVerticalScrollIndicator={false}>
	<Text style={styles.text}>React Native</Text>
</ScrollView>
```

## 横スクロールの実装

ScrollViewコンポーネントに`horizonal`を渡す事で横スクロールに対応する事が可能。

```tsx
<ScrollView horizonal>
	<Text style={styles.text}>React Native</Text>
</ScrollView>
```
Adobe Character Animator の OpenGL エラーに対処した

# Adobe Character Animator とは
パペット（Photoshop や Illustrator で作ったキャラクターのこと）を読み込んで VTuber などのアニメーションを作るソフトである。
パペットは、Webカメラに映る自分の動きに合わせて一緒に動いてくれる。
ここは文章で説明するより、次の動画を観てもらうのが一目瞭然だろう。
https://helpx.adobe.com/jp/adobe-character-animator/how-to/create-puppet-animation.html

Character Animator でシーンを作成しようとすると、
**AIF/OpenGL error -- graphics card driver may need updating.**
というエラーが発生するとのこと。
![OpenGL_error](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/0e01409c-114e-2a2f-bbb2-ecbf7895a8bb.png)
対処したので手順を共有しておく。

# OpenGL のバージョンを確認しよう
[Character Animator の必要システム構成](https://helpx.adobe.com/jp/adobe-character-animator/user-guide.html/jp/adobe-character-animator/system-requirements.ug.html)によると、**OpenGL**のバージョン3.2以降をサポートするグラフィックスドライバが必須らしい。
OpenGLとはグラフィックスを操作するAPIのひとつ。Windows、macOS、Linuxなどで使用できるクロスプラットフォームなAPIである。実際の処理は、ビデオカード（GPU）のハードウェアベンダー、例えばNVIDIAとかIntelとかが実装する。DirectXというWindows専用のAPIもある。

[OpenGL Extensions Viewer](http://www.realtech-vr.com/home/glview) で OpenGL のバージョンを確認。これは実際の画面と少し異なるがご容赦いただきたい。
![OpenGL](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/74c22975-7f61-9db3-a867-cbe455c40bce.png)
バージョン番号は上図赤枠内に表示するのだが、3.2より前のバージョンだった。
Intelのオンボードグラフィックスドライバが未対応とは考えにくく、管理者アカウントに切り替えてドライバを確認する。

# グラフィックスドライバを確認しよう
Windowsキーを押しながら`X`を押し、クイックアクセスメニューから [デバイスマネージャ] を選択。
![QuickAccessMenu](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/772e2470-bcf1-e3a5-f228-0f7e64e94612.png)

ディスプレイアダプターの項目を確認する。
![DeviceManager](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/c36b7961-3af4-d1b5-33db-90564a209e17.png)
**Microsoft 基本ディスプレイアダプター**が設定されていた。
これはハードウェアベンダーのドライバがインストールされていない場合に使われるWindows組み込みの汎用ドライバである。自作に近いBTOパソコンでは起こり得る話だ。
これが原因なので、Intelの専用ドライバに変更する。

アダプター名を右クリック ▶ [ドライバーの更新] を選択し、「最新版を自動検索」を実行。
![adaptor](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/a12064e6-fe58-be8c-f784-60e40886d819.png)
Intelのドライバが設定され、OpenGLのバージョンが要件を満たせばOKだ。

# Intel Driver and Support Assistant を導入しよう
メーカーの既製パソコンでは無いため、アップデータは自前で用意しなければならない。
[インテルドライバー&サポートアシスタント](https://www.intel.co.jp/content/www/jp/ja/support/detect.html)をインストールし、すべてのドライバを最新の状態に保つようにする。
![inteldriver](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/10a55ca5-36fb-6192-46c0-27c30e62220c.png)
スクラムプロジェクトでの「意図を伝える方法」を考えてみる

## はじめに
先日までスクラムプロジェクトに携わっていましたが、参画当初は<b>作りたいものは一緒なはずなのにいざ出来上がるとPOやステークホルダーから「ここはこうしてほしかった」「ここはこうなるとは思わなかった」みたいに言われる</b>ことが多かったように思います。

今になって考えてみると、お互いに上手く意図を伝えることができてなかったように思います。  
今は意図を伝えられる手段を身に付けたので当初よりは格段にお互いの認識がずれないまま開発を行えています。
スクラムやアジャイル的な開発に慣れているチームは当たり前にやっていることだとは思いますが、画面デザインや仕様を固めながら開発を進めていくことにお慣れていなかった私みたいな人もいると思うので、スクラムじゃなくてもすぐに実践できることを整理して記事にしてみます。

## なぜ認識がずれるのか
スクラムチームにおいて、POと開発チームは「同じものを作り、同じものを実現する」ことを目標としていると思います。
では、なぜ認識がずれるのかというと<b>ロールが違う</b>からです。
POは何を実現したいかを考えているし、開発チームはどうやって実現するかを無意識的に考えているはずです。
<b>同じものについて話し合っているのに考えていることが違う</b>ので、会話の中ですり合わせられない部分が発生し、認識がずれたままになってしまいます。

そこで、異なるロールを持つメンバーが認識を合わせつための方法を何点か紹介します。

## 文章で伝える
### 5W1Hを持ったユーザーストーリー

スクラムプロジェクトであれば、まず思い浮かぶのがユーザーストーリーだと思います。

[ユーザーストーリー | 例とテンプレート | Atlassian](https://www.atlassian.com/ja/agile/project-management/user-stories)
↑の説明では、  
>ユーザーストーリーは、「ペルソナ + ニーズ + 目的」としてよく表される開発タスクです。

とありますが、一般的には

- 誰が
- どんな目的のために
- 何をしたい

といったことが記述されます。

このフォーマットに則って
「<b>営業マン</b>が<b>商談で使用する</b>ために<b>物件情報を閲覧</b>したい」
といったユーザーストーリーを作成しても良いですが、POと開発者がさらに共通認識を持つためには<b>5W1H</b>を意識すると良いです。
「<b>営業マン</b>が<b>日中</b>、<b>営業所内で</b>の<b>商談で使用する</b>ために<b>iPadを使用</b>して<b>クライアントと一緒に物件情報を閲覧</b>したい」
といった内容のユーザーストーリーにすることで、

- 営業所内でクライアントと一緒に閲覧するのであれば営業マンが説明したい項目の文字は大きくした方がいい
- iPadを使用して閲覧するので物件画像はフリックでのスライダー表示ができるといい
- PC、スマートフォンでの表示調整は優先度を下げることができる

など、認識合わせの材料にもなりますし、優先順位の判断基準を作ることもできます。

プロジェクトの形式上ユーザーストーリーを作成しない場合でも、5W1Hを意識すると意図を伝える助けになります。

## イメージで伝える
### ペーパープロトタイピング
イメージで伝えると言っても、方法が何種類かあります。

新規サービスを立ち上げる時は、「ペーパープロトタイピング」がよく使われると思います。

[必要な物は紙とペンだけ！ペーパープロトタイピングのススメ](https://www.webcreatorbox.com/webinfo/paper-prototyping)

読んで字の如くですが、紙とペンを使って表示したい画面や挙動を表現することで意図を伝えます。

先述したユーザーストーリーはこういう場面でも活きてきます。
5W1Hを図で表現してすり合わせることができると、文字だけで意図を伝え合っていた時の数倍はお互いの認識が合います。

最終的に表示される画面を想像してすり合わせるので当然といえば当然ですが・・

### キャプチャ
昨今はリモートワークが主流となっておりチームで集まってペーパープロトタイピングをする環境も機会も少なくなっていると思います。
そんな時は画面イメージをサクッと作るか簡単な挙動を実装してしまってキャプチャで認識合わせをしてしまいます。

画面イメージは主にdraw.ioを使って作成しています。

[draw.ioがモックアップとか作成するのに便利だった件 - Qiita](https://qiita.com/kotti/items/bbfda9fbd71227853e33)
文字の大きさや色などはこの時点で大体すり合わせられます。

ちなみに私が一番強力だと思っているのは<b>実際の挙動をGifで連携</b>することです。
具体的な方法はこちらで紹介しています。

[WindowsでGifキャプチャを作成できる「ScreenToGif」がめちゃくちゃ便利](https://www.ooooouchi.info/entry/2020/09/16/120000)

最終的な画面イメージをそのまま見せられるのでフィードバックももらいやすいです。

![20200915121315.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246580/65604935-3f30-72ce-0b1d-635f91d47b79.gif)

## 最後に
意図を伝える方法を整理してみましたが、いざ方法を整理してみると<b>意図を伝えることも手段で、目的は意図を反映したシステムを作ること</b>であることがわかりました。  
POは<b>開発者へ意図を伝えることによってシステム上で何を表現して欲しいのか</b>、開発者は<b>POは何を表現して欲しくて意図を伝えているのか</b>を常に考えることが重要なんだなと思いました。
ポリラインを一定距離ごとにぶった切る turf.lineChunk

緯度経度群（LineString）を、一定距離ごとに分割したいというニッチな要求にも応えてくれる [turfjs](https://turfjs.org/) すごい。

例えば、東京－名古屋－大阪 を結ぶ LineString があるとします。

```typescript
const line = turf.lineString([[139.76, 35.68], [136.90, 35.17], [135.51, 34.68]]);
```

これを、[turf.lineChunk](https://turfjs.org/docs/#lineChunk) を使って「100km 毎に分割」します。
```typescript
const chunk = turf.lineChunk(line, 100, { units: 'kilometers' }) as FeatureCollection;
```

100km ごとに分割された図形群は ``FeatureCollection`` として返却されます。
``Feature`` 型は少々冗長なので、図形の座標群だけ抜き出すと次のように。

```json
[
  [ 
    [ 139.76, 35.68 ],
    [ 138.67760192668752, 35.49567029653387 ] 
  ],
  [
    [ 138.67760192668752, 35.49567029653387], 
    [ 137.60028816204638, 35.30171921680228] 
  ],
  [ 
    [ 137.60028816204638, 35.30171921680228 ], 
    [ 136.9, 35.17 ], 
    [ 136.54843014074316, 35.04811131423963 ]
  ],
  [ 
    [ 136.54843014074316, 35.04811131423963 ], 
    [ 135.54211990949918, 34.69156812570359 ] 
  ],
  [ 
    [ 135.54211990949918, 34.69156812570359 ], 
    [ 135.51, 34.68 ] 
  ]
]
```

東京－名古屋－大阪 のポリラインを 100km ごとに分割したら、5つの LineString になった、ということです。
そして、3つ目の要素は経由地である名古屋の緯度経度を含んでいるため、ここだけ要素数が3になっています。純粋に 100km 間隔のポリラインを得たいならば、先頭の点だけ(終点は末端だけ)を抽出すればよいことになります。

次の例は、東京－名古屋－大阪 のポリラインを 300 等分して、それぞれの標高を取得し、断面図を作る処理です（``elevationService`` は、[地理院地図の標高タイルを使って標高APIを作る](https://zenn.dev/amay077/articles/1a08a8ed71bfd280ef76) を見てね）。

```typescript
const line = turf.lineString([[139.76, 35.68], [136.90, 35.17], [135.51, 34.68]]);
const len = turf.length(line, { units: 'kilometers' });

const chunk = turf.lineChunk(line, len / 300, { units: 'kilometers' }) as FeatureCollection;

const points = chunk.features.map((f, index) => {
  const ln = f.geometry as LineString;
  if (index < chunk.features.length - 1) {
    return ln.coordinates[0];
  } else {
    return ln.coordinates.slice(-1)[0];
  }
});

const elevations = await this.elevationService.getElevations(
  points.map(p => ({ lat: p[1], lng: p[0] }))
);

console.log(`elevations`, JSON.stringify(elevations));
```

``elevations`` を EXCEL などを使ってプロットすると、次のようになります。

![](https://storage.googleapis.com/zenn-user-upload/htql4zi9ejqx1dxn4inghqyyi7ig)

これは、地理院地図の [断面図作成](https://maps.gsi.go.jp/help/intro/kinolist/4-danmen.html) を模したものです。
地理院地図を使って、東京－名古屋－大阪の断面図を作ると、次のようになります。
![](https://storage.googleapis.com/zenn-user-upload/iahnpo9t45d4fwxpn3zf4ojyc2ae)

ほぼ同じ見た目を再現できたんじゃあないかと思います。

ちなみに、「ポリラインの開始点から距離 x 進んだ座標(緯度経度)」を得るには、[turf.along](https://turfjs.org/docs/#along) が便利です。
【Windowsサーバ関連】リモートデスクトップが繋がらない。　原因は　CredSSP 暗号化オラクルの修復である可能性があります

　新規構築したWindows2016サーバはリモートデスクトップが繋がらないという非常事態が発生しました、調べるところは下図のようになったんです。
![errormsg.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/685108/abdf8bd0-db06-6ee0-01ed-2ba963eaacf7.png)

　次の手は、サーバ側のポリシー変更です。ただ、この方法はポリシーを緩めることになるので、実施される方はその事を念頭に置いてください。
![S1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/685108/31861c95-2260-968b-55b0-5090342a7a50.jpeg)

２．[コンピューターの構成] -> [管理用テンプレート] -> [Windows コンポーネント] -> 
    [リモート デスクトップ サービス] -> [リモート デスクトップ サービス セッション ホスト] -> [セキュリティ]
![S2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/685108/976dab25-6194-ae95-b87d-c22a11883acc.jpeg)
![S6.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/685108/673f772e-460d-59db-4226-9ad929cd76c5.jpeg)

３．ポリシー名 : リモート接続にネットワーク レベル認証を使用したユーザー認証を必要とする設定を「 無効」にして「OK」をクリック
![S4.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/685108/0482ee11-0e64-ccc2-c173-983715b60b7a.jpeg)
設定後
![S5.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/685108/f50753a3-886e-e98b-a506-a95263368535.jpeg)

これで、無事にリモート接続しました。
AWSSAAに落ちた時の悔しさを書く

## はじめに
##### 自分のスキル感
+ 実務4ヶ月程度(メインはバックエンド)
+ AWSたまに触るぐらい(超主要なサービスは理解していた)

よーするに**AWSあんまり知らない駆け出しエンジニア**ということになります。




#####資格を取りたいと思った理由は以下の2つ
+ 昨今AWSがインフラに使われるのは主流になってきていて自分も体系的な知識をつけたいと思っていたから。
+ 資格を持っていると**AWSに関してこれぐらいの知識ありますよ〜**という明確な基準ができるから。


一口に「AWS触ったことがある」と言ってもどのサービスなのか、どれぐらいの知識があるのかなど、線引きがしづらいと思っていた。
そこで資格の勉強をしてみようと思いました。





## 勉強したこと
勉強期間は**1ヶ月ちょっと**です。
基本的に勉強したのは下記の2つのみです

[この1冊で合格！AWS認定ソリューションアーキテクトアソシエイト テキスト&問題集](https://www.amazon.co.jp/%E3%81%93%E3%81%AE1%E5%86%8A%E3%81%A7%E5%90%88%E6%A0%BC-AWS%E8%AA%8D%E5%AE%9A%E3%82%BD%E3%83%AA%E3%83%A5%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%88-%E3%82%A2%E3%82%BD%E3%82%B7%E3%82%A8%E3%82%A4%E3%83%88-%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88-%E5%95%8F%E9%A1%8C%E9%9B%86/dp/4046042036)

上記の本を軽く読み通してSAAに試験に出そうなサービスの概要を知りました。
各サービスの内容もある程度書かれているため、ざっくり学ぶにはちょーどよかったです。
少し出たのが古いのですが、サービスを体系的に学ぶ分にはそう悪くはないかなという印象です。
最後に一応軽い模擬試験もあります。
あとで見返したりして何回も使いました。

[【SAA-C02版】AWS 認定ソリューションアーキテクト アソシエイト模擬試験問題集（6回分390問)](https://www.udemy.com/course/aws-knan/)
ネットで合格体験記を読んでいるとよく出てきていたので、こちらもやりました。
セール時は安くなっているので、その時に購入するといいです。
模擬試験が6回分ありますが、3周ぐらいはやったかなという印象です

模擬試験1 1回目:49% 2回目:86% ３回目:83%
模擬試験2 1回目:38% 2回目:56% ３回目:73%
模擬試験3 1回目:47% 2回目:56% ３回目:69%
模擬試験4 1回目:43% 2回目:86% ３回目:74%
模擬試験5 1回目:41% 2回目:49% ３回目:69%
模擬試験6 1回目:43% 2回目:63% ３回目:78%


わからないサービスの内容についてはqiitaの記事を見たり、本を見たり、blackbeltもたまに参考にしていました。
自分の中では、これぐらいで**ギリギリ受かるか受からないか**どちらかぐらいだろうと思っていました。




## 試験当日
試験の内容は言えないのですが、問題を1周といてフラグ立てていた問題だけ見直して終了しました。
他の方も言っていますが、2択までは絞り込めるのですが、そこからは直感で選んでいました。
なかなか難しかったです。

問題を解き終わってアンケートを答えると、目の前に
**不合格**
の文字があり、自分は崩れ落ちました。
目の前が真っ暗になりました。


## 試験結果　
自分の中で大体何点かも予想できませんでした。
ギリギリ落ちたとかそのレベルであって欲しかったのですが、結果は642点と話にならないレベルでした。。。
![スクリーンショット 2020-10-06 8.41.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689030/ccac255e-d1ad-ad93-3b75-e98af626ab09.png)


高パフォーマンス、セキュリティ、コスト最適化　この辺は再学習の必要があるとのことです。ふむふむ
![スクリーンショット 2020-10-06 8.40.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689030/8cdcccc6-fff7-98f8-fe65-5cf40d09444f.png)



## 最後に
**めちゃめちゃ悔しい**
次こそはAWSSAAを必ず取りたいと思いました。

しかしながら、勉強するのがなかなか難しい。。。
どのサービスの知識が抜けているのか、実際の問題はいろんなサービスが関連して出てきて、いろんな場面を想定(コスト最適化するのか、パフォーマンスを重視するのかなど)しなければならないため、**何を勉強すれば良いのか**というのがあまり明確ではないと思いました。


自分の努力不足を認め、また1から勉強していきたいと思います。

何かAWSSAAに関してアドバイスをいただけると幸いです。
最後まで見ていただきありがとうございました。






**次こそは必ずAWSSAAに1000倍返しダァ!!!!!!!!!!!!!!!!!!!!!!!**
Homebrew概要

## Homebrewとは
Macのパッケージ管理システム。（他にMacPorts、Finkなどがある）
GitとRubyで動作する。

### 特徴
- 必要なソフトウェアは自動的にインストールしてくれる。
- OS機能の補完なので無駄に機能を追加しない。
- インストール、アップグレードが簡単。

### インストール
[公式サイト](https://brew.sh/index_ja)へアクセスし、指定のスクリプトをターミナルに貼り付ける。

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

### 基本操作

- 操作はコマンド入力。
- 入力開始は **brew** とすることが基本。

### 主要コマンド

1. インストール：　**brew install パッケージ名**
2. アンインストール：　**brew uninstall パッケージ名**
3. インストール一覧表示：　**brew list**
4. 起動状況確認：　**brew services list**
5. 問題検出：　**brew doctor**
6. ローカル情報確認：　**brew --config**
7. 本体アップデート：　**brew update**
8. パッケージアップデート：　**brew upgrade**
9. ゴミデータ消去：　**brew cleanup**

## 参考
- [Homebrew公式サイト](https://brew.sh/index_ja)
- [今さらだけどHomebrewのコマンドをちゃんと理解して使おう
](https://qiita.com/fuqda/items/db8aff0ba4068aea2cc6)
SlackのIncoming Webhookのtextを彩る

# Slackでリッチなメッセージを表示したい

リモートワーク主体になって、slackに全部通知したい今日この頃。
Slackを鮮やかに彩りたい全ての人に送ります。

# 最初にIncoming Webhookの探し方。
いつも`Incoming Webhook`の例への行方がわからなくなるので、書いておきます。


1. [app's management dashboard](https://api.slack.com/apps)で対象のアプリをタップします。
2. Incoming Webhooksを押します

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199256/db41a644-381d-24fb-8027-f276fb1ed9ab.png" width=400>
3. サンプルのcurlを叩くとメッセージがきます。




`Incoming Webhook`を試したことがない方はこちらで新規に作成してください。
[Slack での Incoming Webhook の利用 | Slack](https://slack.com/intl/ja-jp/help/articles/115005265063-Slack-%E3%81%A7%E3%81%AE-Incoming-Webhook-%E3%81%AE%E5%88%A9%E7%94%A8)

# 基本の型
上で紹介した手順を試すと、以下のcurlが表示されます。

```bash
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"Hello, World!"}' \
https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
```

これが基本形となり、`--data`のjsonを変更していくことで、リッチなメッセージを作ります。


# リッチメッセージの茨の道

リッチなメッセージについて調べると、以下のページがでます。
これらを参照しながらメッセージのjsonを手で変更するのは、茨の道なので注意！

* [An overview of message composition | Slack](https://api.slack.com/messaging/composing)
* [Creating rich message layouts | Slack](https://api.slack.com/messaging/composing/layouts#attachments)


# 現れた救いの神
実は茨の道で頑張っていた僕に救いの神が。
[Block Kit ビルダー](https://api.slack.com/block-kit-builder)様の降臨です。



![スクリーンショット 2020-10-05 20.20.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199256/a9efc532-a140-f7ae-7c43-022a03ca3972.png)

GUIでリッチなメッセージを作れるデザインツールです。



# Block Kit ビルダーの使い方
画像一枚で説明してみました。

![スクリーンショット_2020-10-05_21.33.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199256/9af82f4a-d03e-b02d-879f-9e58d23fb6fa.png)

# まとめ

楽しいメッセージで、楽しいリモートワークを！
WebアプリへのLINEログイン実装で少しハマったところ

WebサイトへLambda+Node.js+APIGateWayを含めて、LINEログイン機能を実装しようとした。
参考にしたサイトが
**「WebアプリにLINEログイン機能を組み込む」**
[URL] http://xp-cloud.jp/blog/2019/11/27/6116/

非常に分かり易くて参考になったが、一部どうしても動かない（LINE認証画面が出てこない）為、エラーメッセージを眺めてみると、「stateが無い」といったことを言われているので、あらためて、LINE公式を確認すると

```
"queryStringParameters": {
	"error": "invalid_request",
	"error_description": "'state' is not specified."
	},
```


stateというパラメータは「必須」ということだった。

![20201006-1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/509691/bef57cfc-93b9-500f-0e7f-0f4ed11ecf0e.jpeg)
LINE公式サイト：
https://developers.line.biz/ja/docs/line-login/integrate-line-login/#making-an-authorization-request
　
　
パラメタに「state」を足してパラメタ付きのリンクをたたくと、無事に表示されたLINEログイン画面。
![20201006-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/509691/e1ba2b11-2ce8-1dab-edfc-df6661288407.png)

その他の動作（Lamda～APIGateway）に関しては問題なく稼働。ありがとうございます作者様。