OmniAICreator/Qiita-1.07M huggingface dataset:
DST Root CA X3 has expired 対策

#※注意書き※　暫定記事です。
#DST Root CA X3 の期限切れ
Let'sencrypt ユーザの皆さんなら、よくご存知でしょうが、　CA証明書、2021/09/30 に期限が切れてしまったことで様々な影響があると伝えられてきました。明確に「対応がないもの」というのが古いOSであったりするのですが、<a href="https://letsencrypt.org/docs/certificate-compatibility/">証明書のコンパチビリティ</a> で示されているような古いOSに限らず　例えば、Windows10あたりでも、自動で入れ替えられたりはしないまま古い証明書が「無効なまま」参照され、VPNなどでは弾かれて(13801エラー)しまいます。

筆者はStrongSwanのKEv2/VPNサービスで「認証エラー」となるため、数時間潰されてしまいました。（証明書が無効ですとエラーが出ればいいのに）

Webブラウザ中でなら昔から手慣れたインターフェイスで管理できるのですが、OSはそうでもないことが多いのです。

#何をすれば良いのか
接続先サーバの、FQDNに紐付けられている電子署名が、Let'sencrypt で発行されているため、VPNがつながらなくなって困っている人向けの処方です。

##Windowsの場合は削除する必要がある（未検証？）

- certlm.msc ：このツールで <a href="https://docs.microsoft.com/ja-jp/windows-hardware/drivers/install/certificate-stores"> 証明書ストア</a> にアクセスして確認します。以下の2つがインストールされていて、<b>2020/09/30</b> に期限を迎えているのであれば、確実につながらないでしょう。
- ![DST-ROOT-CA-X3-1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91470/07da4c3e-4b80-9a49-6d90-967aaf615e08.jpeg)
![DST-ROOT-CA-X3-2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91470/81c7963a-595e-34ac-a937-b40a6c7b4816.jpeg)


### 以下の証明書を入れることでチェーン切れを解決します。（Windows/iOS)
---
<b>Root Certficate : ISRG Root X1 (RSA 4096, O = Internet Security Research Group, CN = ISRG Root X1) </b>

- <a href="https://letsencrypt.org/certs/isrg-root-x1-cross-signed.der">Self-signed: der</a>

--
<b>Intermediate Certificates: Let’s Encrypt R3 (RSA 2048, O = Let's Encrypt, CN = R3)</b> 

- <a href="https://letsencrypt.org/certs/lets-encrypt-r3.der">Signed by ISRG Root X1: der</a>

それぞれの証明書のインストールについては調べれば解ると思いますので、割愛します。
[Docker]ERROR: no matching manifest for linux/arm64/v8 in the manifest list entriesについて

# はじめに
本記事では、`ERROR: no matching manifest for linux/arm64/v8 in the manifest list entries`の解決方法を記述します。

# コード

```docker-compose.yml
version: '3'
services:
  db:
    image: mysql:8.0
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - ./src/db/mysql_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: password

  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - ./src:/app
    ports:
      - "3000:3000"
    depends_on:
      - db
```

```Dockerfile:Dockerfile
FROM ruby:2.7

ENV RAILS_ENV=production

RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
  && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
  && apt-get update -qq \
  && apt-get install -y nodejs yarn
WORKDIR /app
COPY ./src /app
RUN bundle config --local set path 'vendor/bundle' \
  && bundle install

COPY start.sh /start.sh
RUN chmod 744 /start.sh
CMD [ "sh", "/start.sh" ]
```

## ターミナルにて

```
% docker-compose run web rails new . --force --database=mysql
Creating network "rails_docker_default" with the default driver
Pulling db (mysql:8.0)...
8.0: Pulling from library/mysql
ERROR: no matching manifest for linux/arm64/v8 in the manifest list entries
```

`ERROR: no matching manifest for linux/arm64/v8 in the manifest list entries`というエラーが発生しました。

# 結論
`M1チップ問題`でした。

何が良くて、何にエラーが出るのかイマイチわからないです。。

`platform: linux/x86_64`を追記しました。

```docker-compose.yml
version: '3'
services:
  db:
    platform: linux/x86_64 # M1チップ対応のため追記
    image: mysql:8.0
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - ./src/db/mysql_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: password

  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - ./src:/app
    ports:
      - "3000:3000"
    depends_on:
      - db
```

ターミナルにて再度コマンド入力し、成功しました！

以上です。

# 終わりに
`M1チップ問題`なかなかややこしいですね。
随時対応していきましょう。

以下参考サイトです。
[M1 Macでno matching manifest for linux/arm64/v8が発生した](https://ryotarch.com/docker/no-matching-manifest-for-linux-arm64-v8-on-m1-mac/)

明日も頑張ります。
外部のDockerコンテナにIPをつけて直接アクセスする方法（社内サーバ用）

自PCでは無く社内にあるサーバにDockerコンテナを多数起動してクライアントから直接コンテナにアクセスする方法（主に開発、社内サーバ用）。

dockerコンテナのポートを変えてdockerホストにマップしたり、dockerホストでproxyを建ててアクセスする方法はたくさん出てくるのですが、あまり出てこないので残しておきます。この方法のメリットは 用途別にホスト名が別けられることと、 http://servername:8080 のようにいちいちポート番号を考えなくても良いことです。


## 結論
1. ルータでコンテナIPへstaticルートを切る
1. dockerサーバでip4をforwardする

## 構成図
下記のようなネットワークだった場合の例。

- 社内ネットワーク（クライアント、ルータ、サーバ）
192.168.0.0/24

- dockerコンテナ（サーバ内のdockerコンテナ）
172.21.0.0/24

![Untitled2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20132/5f8c5ff4-ab24-b3c3-b130-4c8e6df2779b.png)

## 設定
今回の例では、クライアントからnginxコンテナに直接アクセスします。DockerサーバはLinux。

### dockerコンテナ
下記のようにブリッジのネットワークを作って、コンテナにIPをつける。

docker-compose.yml

```yaml
version: '3.6'

networks:
  share_nw:
    driver: bridge
    driver_opts:
      com.docker.network.bridge.name: docker1
    ipam:
      driver: default
      config:
        - subnet: 172.21.0.0/24

services:
  nginx:
    image: nginx:latest
    networks:
      share_nw:
        ipv4_address: 172.21.0.10

```

### ルータ
説明書を見てルータのLAN側 静的ルーティングを設定する。
今回の例では、ネットワーク：172.21.0.0/24 → ゲートウェイ：サーバのIP（192.168.0.10）にする。

例）
https://www.asus.com/jp/support/FAQ/1011706/

### Dockerサーバ
IPv4 フォワーディングを有効にする。

```
sudo vi /etc/sysctl.conf
net.ipv4.ip_forward=1

reboot
```

### 確認
クライアントPCから `ping 172.21.0.10` とかしてみる。

## ちょっと使いやすく
### 社内（簡易）DNSサーバを建てる
dnsmasqと言う簡易DNSサーバのコンテナを起動し、各コンテナのIPを名前解決できるようにし、クライアントのDNSはdnsmasqのIPにしておく（ルータのDHCPでDNSをdnsmasqのIPを配るように設定する）。

dnsmasqは下記のような設定をしておく。

```
nginx-server.example.local     172.21.0.10
```

dnsmasq (docker hub)
https://hub.docker.com/search?q=dnsmasq&type=image

https://qiita.com/tags/dnsmasq

### web経由でコンテナを起動/停止
Portainerがまぁまぁ使いやすい。web経由でコンテナを起動/停止、imageのpullや操作なんかができます。

https://www.portainer.io/

https://qiita.com/tags/portainer
cloudwatch 概念について

## 勉強前イメージ

メトリクスとモニタリングがちょっとややこしくなってきた

## 調査

### cloudwatchの概念について

- Namespaces

cloudWatchメトリクスのコンテナになります。
名前空間のメトリクスは切り離され、
異なるアプリケーションのメトリクスが同じ統計に集約されないようになっています。
メトリクスの作成時に名前空間名を指定する必要があります。

- Metrics

基本的な概念で監視対象の変数で、その変数の値になります。
作成されたリージョンのみに存在しています。
例えばEC2のCPU利用率は提供される1つのメトリクスになります。

- Dimensions

メトリクスを一意に識別する名前と値のペアになります。
1つのメトリクスには最大で10個のディメンションを定義することができます。
メトリクスの検索やフィルタなどに利用できます。

- Resolution

生成されたメトリクスはデフォルトで標準解像度になりますが、
カスタムメトリクスは `標準解像度` と `高解像度` のいずれかで定義することができ、
高分解能のメトリクスを使用すると1秒・5秒・10秒・30秒、60秒の倍数の期間で読み取り、取得出来ます。
また、高解像度のメトリクスでアラームを設定すると、
10秒・30秒の期間で高解像度のアラームと、60秒の倍数の通常アラームで設定できます。
高解像度アラームは別途費用が発生します。

- Statistics

指定した期間のメトリクスデータの集計になります。
指定した期間内に、名前空間・メトリクス名・ディメンション・データポイントの測定単位を用いて行われます。

- Percentiles

データにおける値の相対的な位置を表しています。
メトリクスの中に統計としてPercentilesがサポートされていて、
平均・最小・最大・合計と同様にPercentilesでモニタリングも出来ます。

- Alarms

アラームを使用するとアクションを自動的に実行させることが出来ます。
他で使用されているアラームと同様のイメージで、
指定したメトリクスを監視し、一定のしきい値から外れた際にアクションを実行します。

## 勉強後イメージ

概念についてメトリクスはなかったけど、
メトリクス → 監視対象
モニタリング → 監視間隔
ってことかな。
言葉難しいね？？？

## 参考

- [Amazon CloudWatch の概念](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html)
- [AWS再入門2018 CloudWatch編](https://dev.classmethod.jp/articles/2018-aws-re-entering-cloudwatch/#toc-statistics)
[SwiftUI] 変数の値をNavigationLinkのdestinationに指定して画面遷移できるようにしたい。


## 概要
使いまわせるviewは、できるだけ細かく作って、車輪の再開発だけは避けたい！！！
と思い、List用のcellのviewを作成。
しかし、cellにあるNavigationLinkのdestinationプロパティの値で怒られ、ハマる。。。

## Cannot find type 'Destination' in scopeで怒られ、引数に遷移したいviewを渡せない。。。
![スクリーンショット 2021-10-08 21.55.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1322570/4a7f8885-c557-2176-73ac-3335e08b3016.png)


```swift
struct NavigationLinkView: View {
    
    var body: some View {
        VStack(spacing: 30) {
            NavigationLinkCell(title: "home", destination: HomeView())
            NavigationLinkCell(title: "favorite", destination: FavoriteView())
            NavigationLinkCell(title: "test", destination: TestView())
        }
    }
}

struct NavigationLinkCell: View{

    let title: String
    let destination: Destination
    
    var body: some View {
        NavigationLink(destination: destination) {
            Text(title)
        }
    }
}

struct HomeView: View {
    var body: some View {
        Text("HomeView")
    }
}

struct FavoriteView: View {
    var body: some View {
        Text("FavoriteView")
    }
}

struct TestView: View {
    var body: some View {
        Text("TestView")
    }
}
```


## 解決策
"Destination : View" 　型をつけてやります。

```swift
struct NavigationLinkCell<Destination : View>: View{

    let title: String
    let destination: Destination
    
    var body: some View {
        NavigationLink(destination: destination) {
            Text(title)
        }
    }
}
```


![スクリーンショット 2021-10-08 22.01.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1322570/73f1e614-996f-4641-6046-e161c2066398.png)
Jenkins pipeline でリストとして渡された複数の job を並列に流す

# 概要
Jenkins で複数の似通った名前の job を並列に一気に流したいということが、Jenkins おじさんをやっているとあると思います。
並列に流すことで効率よく job を実行します。

## どういうことか

しかし以下のようにナイーブに書いてしまうと、各 job が完了するまで次の job が実行されません。
各々が独立しているときには、これだと長い時間がかかって困ってしまいます。
また parallel だと、変数を for で回すことができません。

```
pipeline {
    parameters {
        string(name: "SERVICE_LIST", description: "service list. comma separated")
    }

    options {
        timestamps()
    }

    stages {
        stage("kick job") {
            steps {
                script {
                    service_list = params.SERVICE_LIST.replaceAll("\\s","").split(",")
                    
                    for (int i = 0; i < service_list .size(); ++i) {
                        def service = service_list [i]
                        build(job: "./stop-${env}")    
                    }
                }
            }
        }
    }
}

```

そこで、次のようにすることで job を並列に実行することができます。（あとは slave の executer 依存...）

```
pipeline {
    parameters {
        string(name: "SERVICE_LIST", description: "service list. comma separated")
    }

    options {
        timestamps()
    }

    stages {
        stage("kick job") {
            steps {
                script {
                    service_list = params.SERVICE_LIST.replaceAll("\\s","").split(",")
                    def branches = [:]
                    
                    for (int i = 0; i < service_list.size(); ++i) {
                        def service = service_list[i]
                        branches["stop_${env}"] = {
                            build(job: "./stop-${env}")
                        }
                    }
                    parallel branches
                }
            }
        }
    }
}
```
【Python】HTTP通信でGETを直接書く

# はじめに

HTTP 1.1 の通信でGETするのをsocket 通信として実装したいと思いました。
で「どんなメッセージを送ればよいのか」を調べたのですが、意外と良く分かりませんでした。
なのですが、それが簡単にわかる方法を見つけたので、ここにメモリます。

行ったこと：

- HTTP GETリクエストを送られるデータを確認した
- HTTPサーバにpython でGET を送ってみた

# 実験

## GET request とその response を見る

ローカルにHTTPサーバを立てます。
まず、何か書いたテキストファイルを用意します。

```
.
└── hello.txt
```

このディレクトリでHTTP サーバを立てます。http.server については、[こちら](https://docs.python.org/ja/3/library/http.server.html)に使い方の説明がありますが、bind するアドレス、listen するport、ディレクトリなど指定できます。

```
$ python3 -m http.server
```

これに対して、curl を -v オプションを付けて実行します。

```
$ curl -v localhost:8000/hello.txt
*   Trying 127.0.0.1:8000...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET /hello.txt HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: SimpleHTTP/0.6 Python/3.8.10
< Date: Thu, 07 Oct 2021 15:46:15 GMT
< Content-type: text/plain
< Content-Length: 10
< Last-Modified: Thu, 07 Oct 2021 15:45:10 GMT
<
hello!!


* Closing connection 0
```

これで、送受信されるメッセージが見れました。


## Python でGET request

TCP client でこのメッセージを送ってみます。

python で書くTCP client は以下の通りです。socket でsend/recv するのは binary array です。文字列との間の変換には decode, encode が必要なので注意。

```Python
import socket

hostname, port = "127.0.0.1", 8000
server_request = \
    "GET /hello.txt HTTP/1.1\r\n" \
    + "Host: localhost:8000\r\n" \
    + "User-Agent: curl/7.68.0\r\n" \
    + "Accept: */*\r\n\r\n"
print(f"request:\n{server_request}")
try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(5.0)
    s.connect_ex( (hostname, port) )
    #s.settimeout(None)
    ret = s.sendall(bytes(server_request, 'utf-8'))
except Exception as e:
    print(f"{e}")
    raise e

try:
    data = s.recv(8192*16)
    print( f"response({len(data)}):")
    print(data.decode('unicode-escape'))
except Exception as e:
    raise e
```

さて、動かしてみると、、、

```
$ python3 python_get_client.py
request:
GET /hello.txt HTTP/1.1
Host: localhost:8000
User-Agent: curl/7.68.0
Accept: */*


response(186):
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.8.10
Date: Thu, 07 Oct 2021 23:02:17 GMT
Content-type: text/plain
Content-Length: 10
Last-Modified: Thu, 07 Oct 2021 15:45:10 GMT


```

想定通り、同じレスポンス（当然だ^^;）を得られました。ばんざい。

# まとめ

HTTP GETのリクエストとレスポンスを見ることができた。
次のステップとして、[ここ](https://kazuhira-r.hatenablog.com/entry/2020/04/09/221350)にあるように CONNECT でproxy tunnel を実装してみたいです。
(2021/10/08)

- 続編を書きました。(2021/10/08)

  https://qiita.com/XPT60/items/8d857253e867b201847c
【JavaScript】find()クイズ！

棚に以下が置いてあります。

```ts
const stack = [
   {name:"たまねぎ", date:true}, 
   {name:"葡萄", date:false}, 
   {name:"たまねぎ", date:false}, 
   {name:"林檎", date:false},
];
```
今日はカレーを作りたいので「たまねぎ」が欲しいです。
以下のように欲しい食材を定義しました。

```ts
const target = {name:"たまねぎ"};
```

では、findを用いて「たまねぎ」を棚から取りましょう。注意点としてdate（消費期限）が切れる前の「たまねぎ」だけを棚から取りましょう。
※trueは賞味期限が切れていません。

# 答え

```ts
const TAMANEGI = stack.find((food)=>{
  if(!food.date){
    return false
  }
  return food.name === target.name;
})
```

# さらにレベルアップ
条件に合う「たまねぎ」がない場合は「undifinde」が返りますが、そんなもの食べられないので、食べ物を返しましょう。以下のコードの一部を書き換えconsole.logで「いただきます！」を出力してください。

```ts
  const stack = [
    { name: "たまねぎ", date: true },
    { name: "葡萄", date: false },
    { name: "たまねぎ", date: false },
    { name: "林檎", date: false }
  ];

  const target = { name: "たまねぎマン" };

  const TAMANEGI = stack.find((food) => {
    if (!food.date) {
      return false;
    }
    return food.name === target.name;
  });

  if ("らーめん" === TAMANEGI.name) {
    console.log("いただきます！");
  }
```

# 答え

```ts
  const stack = [
    { name: "たまねぎ", date: true },
    { name: "葡萄", date: false },
    { name: "たまねぎ", date: false },
    { name: "林檎", date: false }
  ];

  const target = { name: "たまねぎマン" };

  const TAMANEGI = stack.find((food) => {
    if (!food.date) {
      return false;
    }
    return food.name === target.name;
  }) ?? { name: "らーめん" };

  if ("らーめん" === TAMANEGI.name) {
    console.log("いただきます！");
  }
```
null合体演算子最強！！
【Arduino】switch()文の注意点

switch文はすっきりしたコードが書きやすく愛用していたのですが、なかなか気付きにくいエラーに遭遇したため備忘録用に。
# エラー1: 内部で変数を宣言
switch文の内部で変数を宣言する際には注意が必要です。
下のプログラムでは、途中で分岐処理が止まってしまいました（写真左）。

```c:switch_bad1.ino
//悪い例（写真左）
//switch文のみ抜粋

int i = random(3);  //0~2のランダム値（整数）

switch (i) {
  case 0:
    Serial.println("零郎");   //i=0の処理
    break;
  case 1:
    String itiro = ("一郎");  //変数を宣言！・・・・(悪い例)
    Serial.println(itiro);    //i=1の処理
    break;
  case 2:
    Serial.println("二郎");   //i=2の処理
    break;
}
```
![変数宣言.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1230173/1e5621eb-2fcd-9432-8777-80e6509ab2b2.png)

:::note warn
このエラーの厄介なところは、<b>コンパイルが通常通り完了してしまう</b>ことです。
また、変数を宣言した部分の処理（上の場合は i=1 の処理）まで実行されてしまうことも、エラーに気付きにくい要因となっています。
:::

### 対処法
- 関数に直接値を書き込む。
- switch文の外で変数を宣言する。
- ``if()``や``while()``などで入れ子にして宣言する。

```c:switch_good1.ino
//良い例（写真右）
int i = random(3);  //0~2のランダム値（整数）

String itiro = "一郎";          //switch文の外で宣言

switch (i) {
  case 0:
    Serial.println("零郎");     //関数に直接入れる
    break;
  case 1:
    Serial.println(itiro);      //switch文の外で宣言した変数を使用
    break;
  case 2:
    if(1) {
      String ziro = "二郎";     //入れ子構造の中で宣言
      Serial.println(ziro);
    }
break;
```
# エラー2: 同じ値を参照
``case``で同じ値を参照するとエラーになります。

```c:switch_bad2.ino
//悪い例

switch(i){
  case 0 || 1:
    Serial.println("0または1だよ");  //0か1の時の処理
    break;
  case 1:
    Serial.println("1だよ");        //1の時の処理（"1"が処理被り → エラー！）
    break;
}
```
:::note
こちらは、<b>Arduino IDE側でエラーを検知してくれる</b>ため、すぐに分かります。
同様の処理をするための方法は色々とありますが、無理やり"switch-case"形式で書くと可読性が悪化する可能性があるため、他の手段も模索しましょう。
:::

```c:switch_good2.ino
//良い例

//1.break;を除く方法（エラーの原因になりやすく非推奨）
switch(i){
  case 0:
    Serial.println("0または1だよ");  //0の時の処理（break; が無いので、そのままcase 1:へ）
  case 1:
    Serial.println("1だよ");        //1の時の処理
    break;
}

//2.case内で分岐
switch(i){
  case 0 || 1:
    Serial.println("0か1だよ");   //0か1の処理
    if(i == 1){
      Serial.println("1だよ");    //1の処理
    }
    break;
}
```
# エラー3: String型を取り扱う
``case``で``String``（文字列型）クラスを参照するとエラーになります。

```c:switch_bad3.ino
//悪い例

String str = "morning";

switch(str){
  case "morning":
    Serial.println("おはよう");     //str=="morning"の処理（エラー！）
    break;
  case "noon":
    Serial.println("こんにちは");   //str=="noon"の処理（エラー！）
    break;
}
```
:::note
こちらも、<b>Arduino IDE側でエラーを検知してくれます</b>。
String型は<b>char型を発展した配列</b>ですので、caseに単体の定数しか入れることのできないArduino IDEでは扱うことができないようです。
よって、同様の処理を行いたい場合は<b>if()文を使用する</b>ことになります。
:::

```c:switch_good3.ino
//良い例

String str = "morning";

if(str == "morning"){
    Serial.println("おはよう");     //str=="morning"の処理（エラー！）
}
else if(str == "noon"){
    Serial.println("こんにちは");   //str=="noon"の処理（エラー！）
}
```
【第七回】コロナ禍だから何かできることをー　自宅療養者連絡ツール　ー

#前回までのあらすじ
[【第六回】コロナ禍だから何かできることを](https://qiita.com/STYLE-D/items/321038c8028eb9e9231f)

####実装イメージ図
LINEを利用して保健所の担当者の負担を軽減するとともに、自宅療養者はいつでもつながっている安心感を持たせるためのツールイメージです。
![メイン.00_00_51_12.Still002.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/744261/cf85391e-aef8-5a59-0a1d-63d8e15412ce.jpeg)


前回までで一通りの流れを説明したので、今回からはシステムについて大きく説明していきたいと思います。
まずは、データベースの構造から。

##データベースの構造
![システム構成-データベース構造.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/744261/368e9c5a-d555-3c1d-7c90-98e9e7b1d565.jpeg)

###地区情報
作業者や利用者が住んでいる地区の情報を登録しておきます。
仮に企業が利用する場合などは、支店や拠点毎に登録することも可能です。

メールアドレス、ホスト名、ポート名、ユーザー名、パスワードについては
メールアドレスのチェックを行う場合に使用します。

ここ
![メール配信.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/744261/284a65f6-c41d-cfd3-5dc7-5468b00b7963.jpeg)


###作業者情報
地区ごとに作業者の情報を登録します。
####　権限
権限は、
・システム管理者
・管理者
・作業管理者
・作業者
の４つに分かれ、権限によって利用できる画面が変わります。
####　利用可否
利用可否は、利用可能なデータかどうかの判断をします。
（一時利用不可とか）



###利用者情報
####　各ID
利用者情報については、IDが２種類存在します。
新規で登録されたときは、利用者IDだけが自動採番されます。
今回のLINE連携を使用しない場合はこのままですが、
利用者からLINE連携の処理が来た時に、LINE IDが格納されます。

これにより、LINEとの連携が可能になります。

####　最終連絡日
最終連絡日においては、利用者からLINEの通知が来た場合、
もしくは作業者が個別で連絡し、画面上からコメントを書き込んだ時に日付が入ります。
すると、最後にやり取りした日がわかるため、状況によってメールで担当者に報告したり、画面上でアクションを発生させたりすることができます。
（今回は、１２時間以上は注意喚起、２４時間以上は警告としています。）

####　状態
状態については、
・未確認
・自宅療養中
・入院
・完了
としてあります。

「未確認」は利用者登録したとき、「自宅療養中」と「入院」は言葉通り、「完了」は完治や死亡など管理対象外となった場合です。

###連絡情報
ここに担当者と利用者のやり取りを蓄積していきます。
####　手段
手段はやり取りした方法を自動で更新します。
パターンとしては、
・LINE通知
・画面上で状態を変更したとき
・連絡後コメントを入力したとき
以上の３パターンになります。

次回は、WEB画面のレイアウトを含めて説明します。

#コロナ禍だから何かできることをー　自宅療養者連絡ツール　ー
[【第一回】実装イメージ図と動画](https://qiita.com/STYLE-D/items/743bbecae1c52be15505)
[【第二回】LINEからデータを取得して返すまでの流れ](https://qiita.com/STYLE-D/items/9e0addd8df44be8590b7)
[【第三回】LINEからデータベースまでの流れ](https://qiita.com/STYLE-D/items/345e9142948c00330e71)
[【第四回】データベースへの更新までの流れ](https://qiita.com/STYLE-D/items/8af17d7f94f5c351ede0)
[【第五回】ユーザー登録の仕組み-LINEbotの設定部分](https://qiita.com/STYLE-D/items/a745bd5faeb68caa1d7c)
[【第六回】ユーザー登録の仕組みLIFFで表示する画面の開発](https://qiita.com/STYLE-D/items/321038c8028eb9e9231f)
->>[【第七回】データベースの構造](https://qiita.com/STYLE-D/items/71085dc9a04042f33f0e)
[【第八回】WEB画面上でできる機能](https://qiita.com/STYLE-D/items/c32e9098a3948e8f32f2)
[【第九回】まとめ](https://qiita.com/STYLE-D/items/77eb6a8e61ae2201565f)
PostgreSQL を Power BI のデータソースとした際の備忘録

# PostgreSQL 
View 生成などの SQL とか

## 全Table削除 SQL
Create 用のSQLが更新して渡されてきたので、SQL実行前に Schema 毎削除する為

```sql:Schema毎削除
DROP SCHEMA public CASCADE;
CREATE SCHEMA public;
```

## 日付(startdatetime)⇒日付(startdate)・時刻分割(starttime)
BI で日付は分割したほうがいいよって記事があったので。

https://qiita.com/PowerBIxyz/items/c12416169f13093f6e8a

```sql:pgAdmin[コード]
table.startdatetime::date AS startdate,
table.startdatetime::time without time zone AS starttime,
```
```sql:CreateQuery
CAST(public.table.startdatetime AS DATE) as StartDate,
CAST(public.table.startdatetime AS DATE) as StartTime,
```

## 時刻差分抽出
差分が必要だったので

```sql:pgAdmin[コード]
 (table.enddatetime - table.startdatetime)::time without time zone AS difftime,
```
```sql:CreateQuery
CAST((public.table.enddatetime - public.table.startdatetime) AS Time) as DiffTime,
```

## 集計後結合

SQL 書いてないとこういうの大変・・。今回も LinqPad で書いて雛型 SQL にしました
条件式もついつい、If 書いちゃうしね・・
あとでパフォーマンス分析はしないと・・

```sql:集計と結合
SELECT
    t1.ID,
    t1.WorkTime,
    t1.RestTime,
    machine.Name
FROM (
    SELECT t.ID,
	COALESCE(SUM(CASE WHEN t0.status <= 4 THEN t0.time END), 0)::INT AS WorkTime, 
	COALESCE(SUM(CASE WHEN t0.status > 4 THEN t0.timeEND), 0)::INT AS RestTime
    FROM table AS t
    INNER JOIN tabletime AS t0 ON t.ID = t0.ID
    GROUP BY t.ID
) AS t1
    INNER JOIN machine ON t1.MachineID = machine.ID
```

COALESCE は、Null対処用。

https://www.postgresql.jp/document/9.4/html/functions-conditional.html

## 連続値を生成して、固定データを追加
範囲型とか、集合データの返却とか、便利なものがあるんだなと勉強になりました
日付なんかも連続生成できるとかも素敵ですね

```sql:Sample
SELECT 
	ids as ID, 
	ids as Percentage
FROM generate_series(1,4) as ids
UNION
SELECT
	serial as ID,
	serial * 2.5 as Percentage
FROM generate_series(5,10, 1) as serial
ORDER BY ID
```

https://www.postgresql.jp/document/13/html/functions-srf.html

# LinqPad 
Sample Data の生成を C# で書きたかったので、以下を使って操作した際の注意点

https://qiita.com/baku2san/items/6a98e24bcfc55e2f5122

## 型相違対処
|PostgreSQL|C#|対処|例|
|-|-|-|-|
|Bigint|Int64||
|int|Integer||
|BigSerial|Int64||
|Serial|Integer||
|bit|BitArray|16進指定⇒BitArray定義|new BitArray(new bool[]{true})|
|Real|Single|Suffix "F" を付ける。|2.5F|
[Ruby] tallyメソッド

##はじめに
Rubyで便利なメソッドを見つけたので記事にしておきます。

##tallyメソッド
リファレンスマニュアルではEnumerable#tallyとして掲載されています。
普段は配列に対して使うことが多そうです。

その辺はまだまだ勉強不足なため、配列を使って説明します。
配列に対して使用し、ハッシュで配列の中のそれぞれの要素が何個ずつあるか教えてくれます。

例えば```[1, 2, 2, 2, 3]```という配列があるとして、tallyを使うと```{1=>1, 2=>3, 3=>1}```というように返してくれます。


```ruby
[1, 2, 2, 2, 3].tally
=> {1=>1, 2=>3, 3=>1}
```


重複している要素を数えたりするときに使えます。
似たメソッドにgroup_byメソッドがあります.

group_byはブロックを必要とし、いろんな使い方ができるみたいです。

tallyのように出力させる方法もあれば、さっきの配列を```{1=>[1], 2=>[2, 2, 2], 3=>[3]}```のように出力させることができるみたいです。使い方には少しコツがいるようです。
【py2exe】NameError: name 'WinDLL' is not defined

MacやUbuntuで

```
pip install py2exe
```

したり、import py2exeすると

```
NameError: name 'WinDLL' is not defined
```

などたくさんエラーがでる。これはpy2exeはWindows上で動かすことのみを前提としているからである。
つまり、MacやUbuntu上ではpy2exeは動かない。
WindowsのPowerShell上なら動いた。

考えてみると、Windows実行アプリがWindowsが必要なのも納得はいく
初めてのCloudFormation 環境準備からVPC作成まで

この記事を書いている私について

AWS CloudTechという動画学習サービスに参加し、AWSエンジニアを目指すための備忘録です:fire:

これまでに学習してきた内容は下記になります。

■インフラ基礎
　・サーバー
　・ネットワーク
　・Linux運用
■AWS
　・VPC
　・EC2
　・IAM
　・RDS
　・ELB
　・Cloudwatch
　・AutoScaling
　・S3
　・Route 53
　・CloudFront

AWS CloudTechはAWSの基本を学ぶことができ、slackのコミュニティも素晴らしく、さらにSAA・SOA・DVA対策コースもある。
環境を用意して下さったくろかわさん(@AwsskillC)に感謝です。

残りの課題がまだありますので、こちらも学習していきます:fire:

■AWS
　・AWS CLI
　・Lambda
　・Docker
　・Docker-compose
　・ECS←最近講座に追加されました！

本記事の目標
-
まずは開発環境を整えることから始めます。
その後、Cloud Formationを使いVPCを構築します。

※CloudFormationではYMLファイルを取り扱います。

###Cloud Formationとは
AWSリソースをコード化してくれるサービスで、
テキストファイル(=テンプレート)にリソースの構造を記述してAWSリソースを自動作成してくれます。
今回からCloud Formationの構築に入っていきます。

今回の構成図
-

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/465906/902ac72d-32f9-bd38-81f1-da6899c26c56.png)

今回は単純にVPCの作成のみです。


構築の流れ
-

以下の流れで構築を進めます。

・環境構築
　-VSCodeのインストール
　　-拡張のインストール
・テンプレートの作成
・スタックの作成
・構築出来たか確認


・環境構築
-
PCはWindows10前提となります。
Macの方は必要な個所を読み替えていただくようお願いします。

###VSCodeのインストール

CloudFormationではコードを扱うため、VSCodeをインストールします。
他のエディタでも問題ありませんが、拡張することで構成図も書けますし、
何より使いやすいのでお勧めします。

VSCodeのインストールはこちらの記事を参考にさせていただきました。

https://qiita.com/psychoroid/items/7d85ae6bade4a67aedb1


###VSCode拡張のインストール

####draw.ioのインストール
こちらの記事を参考にさせていただきました。
ちなみに、本記事に記載している構成図もdraw.ioを使って書いています（VPCのみですが、、、笑

https://qiita.com/tukky/items/c4f9f084dce36e975f03

####YML関連のインストール
YMLで快適に書くために下記のインストールを行います。

・CloudFormation
・YAML
・Japanese Language Pack for Visual Studio Code

↓ここからはお好みでインストールしてください。
・indent-rainbow
・Indenticator

こちらの記事を参考にさせていただきました。

https://qiita.com/akirarara16/items/e9eb6e4ad694c2eb8458

・テンプレートの作成
-

CloudFormationにアップロードするymlは以下となります。

```
AWSTemplateFormatVersion: 2010-09-09
Resources: 
  MyVPC2:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.8.0/21
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: MyVPC2fromcf
```



・スタックの作成
-

マネージメントコンソールから[CloudFormation]を選択し、[スタックの作成]をクリック。
次に、[テンプレートファイルのアップロード]を選んで、ymlをアップロードします。

・構築出来たか確認
-
スタックのステータスが「CREATE_COMPLETE」となったら、
実際にVPCが作成されているか確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/465906/40afce42-d6a2-cf63-d757-edbfb9a049bb.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/465906/0b3b0e3e-68f9-9293-cdbb-26b6c503ba47.png)

本講座は以上となります。
VPCの作成自体は簡単なのですが、
VSCodeのセッティングに時間が掛かってしまいましたので、
これから慣れていきたいと思います。


本課題で発生する費用
-
VPCのみでは費用は発生しません。


最後に
-

CloudFormationを使っていく準備が出来ました。
次回からの講座でサブネット・EC2・RDSの作成を行っていく予定です。
※SAAも勉強中です！


AWS CloudTechの課題としてこれらが残っていますので、
やったことを今後のQiita記事にして発信していきたいと思います。
これから始められる方の参考になれば嬉しいです。

今後の学習予定↓↓↓
・CloudFormationの作成、更新
・Lamda関数
・Docker
・ECS

内容に不備がございましたら、ご指摘いただけますと幸いです。


今後の励みになりますので、良ければ「LGTM」をお願いします。 :bow:
閲覧ありがとうございました。

---
この記事はAWS初学者を導く体系的な動画学習サービス「AWS CloudTech」のハンズオンを基に作成しました。

https://aws-cloud-tech.com/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/465906/00637e63-5339-1b83-c3c1-720e95174ec0.png)
SwiftのClosureにおける `$0` とは結局何者なのか

先日、業務でSwiftのClosureについて説明する機会があったのですが、「SwiftのClosureにおける `$0` とは何なのか」をうまく説明できなかったので悔しくて調べました。

## そもそもClosureってなんだっけ。
[Swift.org](https://docs.swift.org/swift-book/LanguageGuide/Closures.html)によると、下記の定義となっています。

> Closures are self-contained blocks of functionality that can be passed around and used in your code.
> 訳: クロージャーは、コード内で受け渡して使用できる**機能**の自己完結型のブロックです。

この**機能(原文ではfunctionality)** という部分が大事で、Closureを利用するとなにか任意の振る舞いを別のどこかに受け渡し、自身のコードの箇所ではない全然別の場所で振る舞いを起こすことができるというのが面白い点だと思っています。
(ここでの **振る舞い** はなにか変数を操作したり、成功時のハンドラだったりというようなプログラムとしての動き全般を指しています)

## Closureってどう書くんだっけ & $0ってなんだっけ。

SwiftのClosureは下記のように記述することができます

```swift
let result: ((Int) -> Bool) = { price -> Bool in
    if price < 1500 {
        return true
    } else {
        return false
    }
}
```

これだけだとシンプルなので、下記のようにもっと短くすることができます

```swift
let result((Int) -> Bool) = { price -> Bool in
    return price < 1500
}
```

この書き方をする場合、`return price <= 1500`の改行すらも取り除くことができるので、下記のようにきれいにすることができます :hand_splayed: :thumbsup: 

```swift
let result((Int) -> Bool) = { price -> Bool in return price < 1500 }
```

## ClosureのShorthand Argument Names

上記を何度も定義から書くのはしんどいため、ここでSwiftが気を利かせてくれて、クロージャーが1行のときは、クロージャー内部で利用することができる `$0` 系の仮引数を設定してくれます

```swift
let result: ((Int) -> Bool) = { return $0 < 1500 }
```


Swift.org上だと下記のように説明しています
> Swift automatically provides shorthand argument names to inline closures, which can be used to refer to the values of the closure’s arguments by the names \$0, \$1, \$2, and so on.
> 訳: Swiftは、インラインクロージャに短縮引数名を自動的に提供します。これを使用して、クロージャの引数の値を$ 0、$ 1、$ 2などの名前で参照できます。

このため、SwiftのClosureでよく使っている `$0` は実は、Swiftが気を利かせて作ってくれている 仮引数名なのだよ、という話でした。

## 補足: $0は衝突します。

(良いか悪いかはおいておいて)closureの中にclosureを書こうとする場合、 `Shorthand Argument Names` を利用すると名前が衝突してコンパイルできなくなります

```swift
let sources = [[1,2,3,4,5,6,7,8,9,10],[1,2,3,4,5,6,7,8,9,10],[1,2,3,4,5,6,7,8,9,10]]

sources.forEach({ print($0.forEach({ print($0) }) })
// ↑ $0の名前が衝突してコンパイルできなくなる
```

なので、この場合は素直に引数名を定義してあげましょう

```swift
let sources = [[1,2,3,4,5,6,7,8,9,10],[1,2,3,4,5,6,7,8,9,10],[1,2,3,4,5,6,7,8,9,10]]

sources.forEach({ values -> Void in print(values.forEach({ value -> Void in print(value) })) })
```

上記の例ではどちらにも引数を定義していますが、 `$0` が衝突さえしなければ大丈夫なので、どちらか片方だけでも大丈夫だと思います(多分)


これで明日からコンパイルエラーに困っても多分大丈夫です！
量子化とその誤差

<h2>量子化とは？</h2>
関数を離散化してNbitで表し、コンピュータに解析させることができる形にすることです。量子力学の「量子」も、もともとは離散化することからきています。

<h2>今回実装していくもの</h2>
具体的に$ cosx $関数をBbitであらわして、実際にグラフがどうなるのかを見ていきます。
 
前準備としては下のような形です。

<ul>
<li>サンプリング周期を$ \tau $とし関数を$ f(n\tau) = x $でサンプリングします</li>
<li>$ B $bitつかい線形量子化します。($ i = 1, 2, \cdots , 2^B = N $)</li>
<li>$ x_i < x < x_{i + 1} $のときは $ \bar{x_i} $とします（$ \bar{x_i} = \frac{x_{i + 1} + x_i}{2} $）<\li>
</ul>

<h2>実装</h2>
```
import numpy as np
import matplotlib.pyplot as plt
import cmath
import bisect

#cosxを量子化してみる

t = 0.01 #サンプリング周期
max_x = 1
min_x = -1
B = 3 #使用するBit数

#x_barを計算して配列に格納
X = []
for i in range(2 ** B):
  x_pre = min_x + (i / (2 ** B)) * abs(max_x - min_x)
  x_nex = min_x + (i + 1) / (2 ** B) * abs(max_x - min_x)
  x_bar = (x_pre + x_nex) / 2
  X.append(x_bar)
#サンプリングした値を量子化する
#cosxは一周期

T = 2 * np.pi

#量子化した後のデータ
x = []
y = []

for i in np.arange(0, T, t):
  x_sample = np.cos(i)
  #xに一番近い値を二分探索で求める
  index = bisect.bisect(X, x_sample)
  x.append(i)
  if index == 0:
    y.append(X[index])
  elif index == 2 ** B:
    y.append(X[index - 1])
  else:
    #xと距離が近いほうの値を採用する。
    x_front_dis = abs(x_sample - X[index - 1])
    x_back_dis = abs(x_sample - X[index])
    if x_front_dis < x_back_dis:
      y.append(X[index - 1])
    else:
      y.append(X[index])
```

量子化する前とした後のグラフは次のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1127342/b6bae7e7-0b46-f3ae-7c83-14575d69dd5d.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1127342/a0cfc62d-84ee-97a3-77fe-daa4563b10ee.png)

bitの数を増やせばもときれいに近似できます。
【エラー】Mysql2::Error: Duplicate entry ' ' for key ' '

##環境

Ruby 3.0.2
Rails 6.1.4.1
omniauth-google-oauth2 1.0.0
devise 4.8.0

##状況
Rspecを実行した際にエラーを吐かれた

```
 ActiveRecord::RecordNotUnique:
       Mysql2::Error: Duplicate entry '100000000000000000000' for key 'users.index_users_on_uid'
```

```spec/factories/users.rb
FactoryBot.define do
  factory :user do
    name { 'test employee' }
    sequence(:email) { |n| "tester#{n}@example.com" }
    provider { 'google_oauth2' }
    uid { '100000000000000000000' }
  end
end
```

##解決法

uidがかぶっていたのでエラーが出ていた。sequenceを使って一意にする

```spec/factories/users.rb
FactoryBot.define do
  factory :user do
    name { 'test employee' }
    sequence(:email) { |n| "tester#{n}@example.com" }
    provider { 'google_oauth2' }
    sequence(:uid) { |n| "1#{n}00000000000000000000" }
  end
end
iCloud+の「メールを非公開(偽装アドレス)」で実験してみた

# 以下，偽装アドレスと呼称します
コレに興味がある方は基本的な使い方は大丈夫だと思われまスゥゥゥ…

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

#### 簡単な説明
macOS Monterey（Public Beta版でそこそこ安定しているやつがあるから[登録して落としてこよう](https://beta.apple.com/sp/ja/betaprogram/)）
設定 → Apple ID → メールを非公開（オプション）に移動
左下の『➕』で自動生成，ラベルとメモを忘れずに書き加える
アドレスをコピペして実際に使う，以上



## 用語リスト

![説明.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540440/a685e198-8ac5-560f-0e8b-31fab5b488c1.png)


### 偽装アドレス
さっきの手順で手に入れたアドレス
`xxx@icloud.com`とおく


### 自分アドレス
自分の本来のiCloudのアドレス
`XXX@icloud.com`とおく


### 相手アドレス
やり取りをしたい相手のアドレス
`YYY@example.ne.jp`とでもおくか


### 串用アドレス
メールの送信先に実際に置かれるアドレス
4つ偽装アドレスで5つくらい取ってから実験してみた
ユーザーの視点では「プロキシが持つアドレス」に見えるのでそう呼びたい…
`YYY@example.ne.jp`を用いると
`YYY_at_example_ne_jp_[aa][nnnnnnnnnn][bb]_[mm][cccccc]@icloud.com`みたいに導出される

`[aa]`・`[bb]`・`[cccccc]`は同じ偽装アドレスを用いて通信した相手アドレスであれば共通する
`[nnnnnnnnnn]`（10桁）・`[mm]`（2桁）は相手アドレスによって完全にランダムに振られる
なんでこの桁数や構成になったのかは不明…




# 実際に制限ルールを探ってみた



## ルール1.文通するには最初に相手から送られてくる必要がある

相手アドレスから偽装アドレスにメールが飛んでくると，相手アドレスに対応する串用アドレスが発行され，串用アドレスから自分アドレスにメールが飛んでくる（Appleのsmtpサーバから転送されてくる）
偽装アドレスを用いて相手アドレスにメールを返信するにはその真逆の動作をしなければならず，自分アドレスから串用アドレスに飛ばすことで偽装アドレスから相手アドレスにメールが飛んでいく

すなわち送信アドレスに対応した串側アドレスをユーザーが叩くことで偽装アドレスから相手アドレスへのアクセスが行えるが，串側アドレスの発行は外側から受けたメールを処理するAppleのsmtpサーバでしかできない以上，**こちらから先制して送りつけることは不可能**，対応できる串側アドレスがなければユーザーはなにもできない
~~濫用防止のためと言われれば当然はであるが~~


## ルール2.串側アドレスの乱数文字列がユーザ認証の役目を果たす

試しに1文字だけ弄って送信したらエラーを吐かれたし，偽装アドレスによって串側アドレスのフォーマットの約半分が定まっていることから，自分アドレスと偽装アドレスの権限照合・串側アドレスと相手アドレスの宛先照合が行われているとみて良さそう
予想するに，**他のiCloudユーザが他人の串側アドレスを宛先にして送信してもダメ**だったりするのではないだろうか…？（誰かやってくれ）

ちなみに宛先が間違っていると代替のsmtpサーバに送信され，Undelivered Mail Returned to Serverされる



## ルール3.串側アドレスを宛先に含む場合はアドレス1つ分しか対応しない

宛先に2つ以上の串側アドレスを登録して1度に送ろうとすると，

+ Web版iCloudメールでは警告メッセージを吐かれる
  + 「機能的に無理です！」
+ Macデフォルトのメールソフトでは**約5分間隔で宛先1つずつに再送される**
  + 「too many recipients!」
    + 代替のstmpサーバの指定を要求されるが，何を選んでも結局は再送になる．
  + なお1つ目の宛先にはすぐに送れる
  + 個別送信扱いになるので他の宛先は見えなくなる

または串側アドレスと通常のアドレスを登録して1度に送ろうとすると，

+ Web版iCloudメールでは警告メッセージを吐かれる
  + 「Undelivered Mail Returned to Server」
  + **どっちも届かない**
+ Macデフォルトのメールソフトでは約5分間隔で宛先1つずつに再送される
  + 代替のstmpサーバの指定を要求される
    + 選ぶとバラバラに（串側アドレスはAppleのsmtpサーバで，それ以外は代替smtpサーバで）再送される
    + 相手アドレスから見れば宛先の連名は見れないが，**通常のアドレスからは丸見え**
  + 1つ目の串側アドレスや通常のアドレスの宛先にはすぐに送れる

つまりメールソフトにおける動作としては，

1. まずはiCloudサーバにまるごと送る
1. 串側アドレスがある場合は1つしか受け付けられないと返信する
1. 代替smtpサーバがない場合は全て破棄，ある場合は
    1. 串側アドレス1つをiCloudサーバに送り
    1. その他全てを代替smtpサーバに送る
    1. 串側アドレスが残れば返送されるので
        1. 約5分間隔で1つずつ再送する
1. エラーが無くなれば完了

という感じ…っぽい



## ルール4.串側アドレスはApple側で保持される

以上のルールを満たすには，偽装アドレスのみならず串側アドレスもAppleが管理していると見るべきだろう
というかリンク切れが起きたりしたらこっちも大変なので，そこら辺は頑張ってセーブし続けてほしい~~月額160円でやっていいサービスか？これが…~~




# 感想

今後また仕様が何かしら変わるかも知れません
が，とりあえず串側アドレスには偽装アドレスと一意に結び付けられる成分があることは頭に入れておくべきだと思いました
万が一の場合にはそれが流出することでやり取りが特定される可能性も考えられます
あとはどれだけ串側アドレスが保持されるのかやってみなくちゃわかりません，1年間の未使用でリセットとかありそうですし…

**匿名化ヨシ！~~ご安全に！~~**

###### 全てのきっかけ

https://twitter.com/Soluna_Eureka/status/1446095138808221707?s=20
Google Analytics の トラッキングID を探す 2021年版


Google Analytics のUI変更により、トラッキングIDの確認方法が変更されているのでメモです。

Google Analycisを開き、該当するアプリを選択した後に「管理」を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110265/ace2dfe4-f157-9108-19c8-8574048e9257.png)

「管理」から「アカウント設定」「データストリーム」を開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110265/b5e576c6-19ea-0f34-d999-06ad9d888ee4.png)

「データストリーム」から、該当のサイトを開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110265/6fa39a27-93ad-b277-9539-04c5a5936622.png)

すると「データストリーム」「ウェブストリーム」に「測定ID」としてトラッキングIDが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110265/2708aba6-7b90-1073-4dc4-b672f8dc6168.png)

以上。
SonarLintにおける静的コード解析をSonarQubeサーバで実施する 

# はじめに
SonarQubeサーバ上で静的コード解析をしつつ、コーディング中にも同様の解析が行えるようにするため、VS Codeの拡張機能である、「[SonarLint](https://www.sonarlint.org/vscode/)」を使っていました。
しかし、SonarQubeサーバ上でコード解析ルールのチューニングを行っていたため、以下の問題が発生しました。

* SonarQubeサーバとSonarLint上で、検知するBug, Code Smellに差異がある

そこで、SonarLintにおける静的コード解析をSonarQubeサーバで実施することで、問題を解消することにしました。

## 設定導入時のバージョン
* SonarQubeサーバ: 8.3.1.34397
* VS Code: 1.59.1
* SonarLint: 2.3.0
* mac: 11.6

# 設定
実際に各ツール上で設定を行っていきます

## SonarQubeサーバ
### アクセストークン発行
SonarQubeサーバでは、当該サーバと接続するための、アクセストークンを発行します。
今回は、admin権限を持つユーザでアクセストークン発行を実施しました。

1. アクセストークン発行対象ユーザ選択
    1. admin権限でログイン
    1. 画面上部の「Administration」をクリック
    1. Administrationメニュー内の「Security - Users」をクリック
    1. トークンを発行したいユーザの「Tokens」列に記載の「三」を選択する(画像赤枠参照)![1.ping](https://raw.githubusercontent.com/not-0x138/qiita-image/main/SonarLint%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E9%9D%99%E7%9A%84%E3%82%B3%E3%83%BC%E3%83%89%E8%A7%A3%E6%9E%90%E3%82%92SonarQube%E3%82%B5%E3%83%BC%E3%83%90%E3%81%A7%E5%AE%9F%E6%96%BD%E3%81%99%E3%82%8B%20/1.png)
1. アクセストークン発行
    1. 発行するアクセストークンにつける任意の名称を入力し、「Generate」ボタンをクリック
    1. トークン文字列を控えておく

:::note warn
警告
アクセストークンの発行後、画面を閉じるとトークン文字列は再閲覧不可なので注意してください。
:::

## SonarLint(VS Code)
SonarLint(VS Code)では、発行したアクセストークンを利用して、sonarQubeサーバと接続を行うための設定をします。
sonarQubeサーバ上に複数のプロジェクトがあり、それぞれ使用言語やルールが異なる場合を鑑み、VS Code上のワークスペースとsonarQubeサーバのプロジェクトを紐付けます。

* SonarQubeサーバと接続
    1. ユーザー設定ファイルの編集
        1. コマンドパレットを開いて（cmd+shift+p）「>Open Settings」と入力し、setting.jsonを開く
        1. 下記の設定を追加する

```json
  "sonarlint.connectedMode.connections.sonarqube": [
    {
      "serverUrl": "自身のSonarQubeサーバのURL",
      "token": "発行したトークン文字列"
    }
  ]
```

* SonarQubeサーバ上の各プロジェクトとの連結
    1. ワークスペース設定ファイルの編集
        1. <プロジェクトルート>/.vscode/settings.jsonを開く
        1. 下記の設定を追加する

```json
  "sonarlint.connectedMode.project": {
    "projectKey": "SonarQubeサーバ上のプロジェクトキー"
  }
```
**SonarQubeサーバ上のプロジェクトキーは、sonarQubeサーバ上で確認ができます。**

1. admin権限でログイン
1. 画面上部の「Administration」をクリック
1. Administrationメニュー内の「Projects - Management」をクリック
1. Key列に記載の値をコピペ

:::note warn
ワークスペース設定ファイルにSonarQubeサーバの接続情報を記載することも可能です。
ただし、当該ファイルをgit管理している場合、SonarQubeサーバの接続情報がリモートリポジトリに格納されることは留意してください。
:::
GKEの外部LB用Ingressをextensions/v1beta1からnetworking.k8s.io/v1にマイグレートする方法

## 想定読者

- GKEで外部LB用Ingressを利用している
- IngressのAPIバージョン`extensions/v1beta1`または`networking.k8s.io/v1beta1`を利用している
- k8sのバージョンv1.19~ を利用している

## 背景

k8sのバージョンv1.22から`extensions/v1beta1`および`networking.k8s.io/v1beta1`のIngressが利用出来なくなります。

https://kubernetes.io/docs/reference/using-api/deprecation-guide/#ingress-v122

ということで早速公式の指示に従ってIngressを`networking.k8s.io/v1`にマイグレートしましょう！

## 注目すべきマニフェストの変更点

今回のアップデートでIngressのAPIが以下のように変更されています。

- spec.backend -> spec.defaultBackend
- serviceName -> service.name
- servicePort -> service.port.number
- serviceName -> service.port.name
- L7ルーティングをする場合はpathTypeの指定が必須

## kubectl convertによるマニフェストのマイグレート

既存のマニフェストを元に新しいバージョンのマニフェストを生成する場合は[以下のコマンドを利用することが推奨されています。](https://kubernetes.io/docs/reference/using-api/deprecation-guide/#migrate-to-non-deprecated-apis)

```
> kubectl convert -f old-ingress.yaml --output-version networking.k8s.io/v1
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.allow-http: "false"
    kubernetes.io/ingress.global-static-ip-name: sample-ip
  creationTimestamp: null
  labels:
    app: app-sample
  name: ingress-sample
spec:
  defaultBackend:
    service:
      name: service-sample
      port:
        number: 8080
status:
  loadBalancer: {}
```

`kubectl convert`が利用出来ない場合はkubectlのバージョンを最新化した上で[公式ドキュメント](https://kubernetes.io/docs/tasks/tools/install-kubectl-macos/#install-kubectl-convert-plugin)に従ってpluginをインストールしましょう。

## Ingressリソースのマイグレート手順

### 一般的な方法

既にIngressリソースを作成されている場合は、前述した`kubectl convert`により生成したマニフェストを再適用するだけで問題無くマイグレートが可能です。

```
> kubectl convert -f old-ingress.yaml --output-version networking.k8s.io/v1 > new-ingress.yaml
> kubectl apply -f new-ingress.yaml
ingress.networking.k8s.io/ingress-sample configured
```

確認した限りダウンタイムの発生は無いですが、トラフィックの多い本番環境に適用する場合は事前に開発環境でダウンタイムの確認をしておくことを推奨します。

### カナリアリリースによる方法

先ほどのやり方は簡単ですが、やり直しやロールバックが実質的に不可能で、既にサービスインしているIngressに対して実行するのは怖いです。
そんな場合にもう一つの選択肢として「[CloudDNSの重み付きラウンドロビン](https://cloud.google.com/dns/docs/zones/manage-routing-policies)」を利用したカナリアリリースがあります。

重み付きラウンドロビンは重みに従ってDNSが返すAレコードを分散させる機能です。
今回の場合であれば、旧IngressのIPを99%、新しく作成したIngressのIPを1%の確率で返す、といったような設定を入れることでIngressのカナリアリリースが可能になります。

#### 作業手順

1. 外部IPを新規作成する
1. `networking.k8s.io/v1`のIngressリソースを作成する
    1. リソース名は旧Ingressリソースと異なるものを付けるようにしてください
    1. `kubernetes.io/ingress.allow-http:false`を指定する場合は`ingress.gcp.kubernetes.io/pre-shared-cert`に旧Ingressに紐付けられているマネージド証明書の名前を指定してください
1. CloudDNSの重み付きラウンドロビンに2つのIngressのIPを重みとともに設定する
1. 100%リリース出来たら旧Ingressのリソースを削除する

`kubernetes.io/ingress.allow-http:false`の挙動は癖が強いので注意してください。
このアノテーションを作成済みのIngressにあとから追加しようとするとエラーが発生するため、つけ忘れた場合はリソースの作成を最初からやり直す羽目になったりします。。。

## まとめ

- k8sのバージョンv1.22から`extensions/v1beta1`および`networking.k8s.io/v1beta1`のIngressが利用出来なくなる
- 古いマニフェストから新しいバージョンのマニフェストを生成する場合は`kubectl convert`を使う
- Ingressをカナリアリリースする場合はCloudDNSの重み付きラウンドロビンを使う
【Rails×Vue】Error: Can't resolve '@smartweb/vue-flash-message' の解決

Rails×VueでSPA化に取り組んでいる初学者の備忘録です。

#エラー詳細

Vueを用いてフラッシュメッセージを表示するため@smartweb/vue-flash-messageを導入。
https://www.npmjs.com/package/@smartweb/vue-flash-message/v/next

```
npm i @smartweb/vue-flash-message@next
```
開発環境で問題なく動作したためherokuにデプロイしようとしたところ次のエラーが発生した。

```
ModuleNotFoundError: Module not found: Error: Can't resolve '@smartweb/vue-flash-message' in '/tmp/build_7d25413c/app/javascript/packs'
           at 
.
.
.
.
.

Field 'browser' doesn't contain a valid alias configuration
           resolve as module
             looking for modules in /tmp/build_7d25413c/app/javascript
               using description file: /tmp/build_7d25413c/package.json (relative path: ./app/javascript)
                 Field 'browser' doesn't contain a valid alias configuration
                 using description file: /tmp/build_7d25413c/package.json (relative path: ./app/javascript/@smartweb/vue-flash-message)
                   no extension
                     Field 'browser' doesn't contain a valid alias configuration
.
.
.
.

                     /tmp/build_7d25413c/app/javascript/@smartweb/vue-flash-message doesn't exist
```

#エラー原因

本番環境上で@smartweb/vue-flash-messageが読み込まれていなかった。

```package.json
"dependencies": {
    "@rails/actioncable": "^6.0.0",
    "@rails/activestorage": "^6.0.0",
    "@rails/ujs": "^6.0.0",
    "@rails/webpacker": "^5.4.3",
    "@smartweb/vue-flash-message": "^1.0.0-alpha.12",
    "@vue/babel-plugin-jsx": "^1.1.0",
    "@vue/compiler-sfc": "^3.2.19",
    "axios": "^0.22.0",
    "turbolinks": "^5.2.0",
    "vue": "^3.2.19",
    "vue-loader": "^16.8.1",
    "vue-router": "^4.0.11",
    "vuex": "^4.0.2"
  },

```

package.jsonを見るとdependencesに記載があるので問題ないと思っていたがyarnでプラグインをインストールする必要があった。

#解決

一度, プラグインをアンインストールし、

```
yarn add @smartweb/vue-flash-message@next
```

で再インストール。herokuにデプロイしたところ、package.jsonとyarn.lockが混在していると表示が出たので

```
git rm package-lock.json
```

でyarn.lockを残すようにしてデプロイを実行。
エラーが解決した。
TypeScriptを好きになりたい

# 久しぶりにTypeScriptを触って
未経験で入社して2ヶ月と少し経ちました。
最近では、バックエンドを動かしながら課題を進めていますが、入社する前までは、JavaScriptを中心にHTMLやCSSを学んでいましたが、入社して初めて、本格的にバックエンド（今回ではAWS）の使い開発を進めるにあたり、かなり苦戦しています。特にAPIを使って欲しい情報を取得するという流れも、アプリを作る上では当たり前のことですが、その当たり前すら初めての経験で、毎日新しい発見と数多くのエラーに遭遇しています。

TypeScriptも少しだけ基礎的なことを学び、簡単なTODOリストなどに型を付けるという作業はしましたが、AWS Amplifyなどを使って開発に挑戦してみる際に、どうしても型定義でのエラーが目立ってしまい、なかなか本題のバックエンド開発の方に行き着かないということもありました。そのため、その時はTypeScriptを使わずReactでフロント側を作り課題を進めました。
そんな中、今回久しぶりにTypeScriptを触り、かなり型指定でハマってしまいました。

# どうしてハマったのか
TypeScriotを最初学習している際には一から作りながら、それに合わせて型を決めるという感じで進めていたのですが、今回は、社内でよく使うサービスを学びつつTypeScriptも書こうということで、ある程度サンプルが出来上がっているものをReactで少し書き換えてそれにTypeScriptで型を付けるという流れで行いました。
久しぶりに触り、少し忘れているところもありましたが、とりあえずどういった値が入るのかを一つ一つ調べて型を定義していきました。（少し余談ですが、頭が大文字から始まるのと小文字から始まる（Stringとstringなど）ので意味が大分違ってくることを知り、基本小文字で書くということを今回初めて知りました。）

まず、ハマった原因一つ目は、しっかり理解できている型以外は、エラーを消すのが目的になっていたことです。
エラーのメッセージで出てくる中には、入る値は分かっていても、使っているライブラリなどによっては初めてみるものも多く、調べてある程度機能を把握するものの、それが他にどのように影響するかを深くまで理解できていないことがあります。そうなった際に、とりあえずエラーを消すことだけを考えてしまい、調べて出てきたものを試し、それによってエラーがなくなれば良しとしているところがあり、これにより後々直した際に、それが原因でまたハマってしまうという流れができてしまいました。これにより、そもそも型を付ける本来の目的を見失っており、ここでやっていることは型にanyを付けていることと何も変わらないと感じました。

二つ目のハマった原因は、TypeScriptを型の付け方の理解度がまだ低いことにあると思います。
そもそも今まで使ってこなかったので仕方ないのですが、それでも学習した基本の型などはできると思っていました。
しかし、実際は「この型であっているのか？」「関数コンポーネントの場合、FCで[children]を含まない場合はVFCを使うが、こんな書き方だったか？」などと自信がなくなり、結果今回、「利用しているライブラリの型を変えるのでは？」と普通に考えたら、あり得ない考えに至ってしまいました。

三つ目にハマった原因は、タイトルにもある通りTypeScriptのありがたみを感じておらず、少し毛嫌いしていることにあると思います。
まだ、ほとんど使ったことがないのに何気毛嫌いしているんだと思いますが、使ったことがない上にその便利さに気付けていないんだと自分でも思います。学び始めたタイミングも、Reactを段々と理解してきた矢先にTypeScriptを学び始め、自分の中で整理できていない状況で学習を進めていたために、混乱してしまい理解が中途半端な状態であったような気がします。

# さいごに
TypeScriptは会社の先輩やネット記事などでも、開発においてはとても重要だということは知っているのですが、自分が使った方がいいと認識のもと自信を持ってTypeScriptを使う段階にまだいけてないので、とりあえず今は、とにかくTypeScriptと向き合う回数を増やし、少しでも好きになれるように努力していきたいです。
[CakePHP4] bake で複数 twig を運用する際は -t オプションを使う

## 答え

```sh
bin/cake bake template table_name -t theme_name
```

### やっていたこと
bootstrap_cli.php で複数の Theme を利用していた。

```php:config/bootstrap_cli.php
// template 生成用のテーマ (AdminLTE 専用)
Configure::write('Bake.theme', 'AdminLteTheme');

// Model 生成用のテーマ
Configure::write('Bake.theme', 'GenerationGapModelBaker');
```

### オプションなしで bake で template 生成
`vendor/cakephp/bake/templates/bake/Template` 配下の twig が適用され、bootstrap_cli.php で `Configure::write` したテーマが適用されなかった。

```sh
bin/cake bake template table_name
```

### 原因
テーマは `vendor/cakephp/bake/src/Command/TemplateCommand.php` の `TemplateRenderer($args->getOption('theme'));` で最後に追加したテーマを適用する。
結果として別の　twig、または vendor 配下の FW の twig が適用される。

```php:vendor/cakephp/bake/src/Command/TemplateCommand.php
    public function getContent(Arguments $args, ConsoleIo $io, string $action, ?array $vars = null)
    {
        if (!$vars) {
            $vars = $this->_loadController($io);
        }

        if (empty($vars['primaryKey'])) {
            $io->error('Cannot generate views for models with no primary key');
            $this->abort();
        }

        // 最後に追加したテーマを適用
        $renderer = new TemplateRenderer($args->getOption('theme'));
        $renderer->set('action', $action);
        $renderer->set('plugin', $this->plugin);
        $renderer->set($vars);

        $indexColumns = 0;
        if ($action === 'index' && $args->getOption('index-columns') !== null) {
            $indexColumns = $args->getOption('index-columns');
        }
        $renderer->set('indexColumns', $indexColumns);

        return $renderer->generate("Bake.Template/$action");
    }
```

### 対応
テーマ名をオプションで指定する。

```sh
bin/cake bake template table_name -t theme_name
```

### help 見よう
help をちゃんと読んでいなかった…
`default` と、選択可能なテーマ `choices` がわかる。

```sh
$ bin/cake bake template --help
(中略)
--theme, -t          The theme to use when baking code.
                     (default:
                     GenerationGapModelBaker)
                     (choices:
                     AdminLTE|Bake|GenerationGapModelBaker|Migrations)
```

## 複数テーマ利用例
### plugins 配下の構成

bake 自動ファイル生成用に、複数の twig を plugins 配下に作成

- template 用
- model 用

実際に、AdminLTE テーマに対応した template 生成用の twig ファイルと、カスタマイズした model を生成する twig ファイルを共存した例です。
（GenerationGapModelBaker は現在は [パッケージ化](https://packagist.org/packages/lancers/cakephp-generation-gap-pattern-model-baker) してます）

```bash
$ pwd
~/www/app/plugins
$ pwd;find . | sort | sed '1d;s/^\.//;s/\/\([^/]*\)$/|--\1/;s/\/[^/|]*/|  /g'
|--AdminLteTheme
|  |--templates
|  |  |--bake
|  |  |  |--Template
|  |  |  |  |--add.twig
|  |  |  |  |--edit.twig
|  |  |  |  |--index.twig
|  |  |  |  |--view.twig
|--GenerationGapModelBaker
|  |--templates
|  |  |--bake
|  |  |  |--Model
|  |  |  |  |--entity.twig
|  |  |  |  |--extended_entity.twig
|  |  |  |  |--extended_table.twig
|  |  |  |  |--table.twig
```
スマホブラウザで「:hover」疑似クラスを無効にする

Web制作持によく使われる**```:hover```**擬似クラスですが、レスポンシブ対応させたWebページで
タップ操作しかないスマホブラウザでは```:hover```のスタイル変化を無効にしたいという場合の実装法です。

めちゃくちゃシンプルですが、

```CSS
@media screen and (min-width: 600px) {
 a:hover {
 }
}
```

のようにメディアクエリの```(min-width: ***px)```でスタイルを適用する最低画面幅を設定し、
その中で```:hover```のスタイルを定義します。

上の例では「600px以上の画面幅があるときは```<a>```に```:hover```を適用してね」ということなので、厳密には「スマホブラウザのみで無効にする」というわけではありませんが、実際問題的にはスマホ表示時には無効にできると思います。
<br>
以上、個人的備忘録失礼しました。
【Multiple Select】特定のトリガーでプルダウンをリセットする方法

# やりたいこと

あるボタンのクリックイベントで、Multiple Selectの選択状況をリセットしたい。

Multiple Selectとは、チェックボックスで複数選択できるプルダウンなどを作れるjQueryプラグイン。

公式は[こちら](https://multiple-select.wenzhixin.net.cn/en/)

# Multiple Selectの特徴

Multiple Selectは通常のselect要素で作るプルダウンとは少し異なるため、valueの取得の仕方などの勝手が変わってきます。

そのかわり、Multiple Selectの公式で用意されているさまざまなオプションやメソッドを使うことができます。

今回は「プルダウンのリセット」が目的なので、uncheckAllメソッドを使用していきます。uncheckAllは「すべてのチェックを外す」メソッドになります。

# uncheckAllでリセット

```html
<button id='reset'>リセット</button>

<select id='multiple-select'>
	<option value='1'>りんご</option>
	<option value='2'>バナナ</option>
	<option value='3'>ぶどう</option>
</select>
```


```javascript
$(function () {
    $('#reset').click(function () {
        const $select = $('#multiple-select')
        $select.multipleSelect('uncheckAll')
    })
})
```

# まとめ

他にもさまざまなメソッドが用意されており、上記とは逆にすべて選択状態にするメソッドなんかもありますので、Multiple Selectを使っている方はぜひ試してみてください。