OmniAICreator/Qiita-1.07M huggingface dataset:
【Laravel】web.phpとapi.phpの違い

## Laravelのルーティングファイルについて
Laravelのルーティングファイルには、web.phpとapi.phpの2種類があります。


### ルーティングファイルの違いについて
### web.php
通常、ブラウザからHTTPリクエストをうけて、画面に表示するようなルーティングを設定する場合に使用する。
CSRF保護などの機能が有効になっているため外部からのPOSTができない。

### api.php
外部からのHTTPリクエストをうけて、値を返却したりするようなルーティング(エンドポイント)を設定する場合に使用する。
CSRF保護が有効になっていないため外部からPOSTができる。


### 動作の違いについて
web.phpとapi.phpの動作の違いについては、```Laravel6```の場合、```app/Http/Kernel.php```で設定されています。
### api.phpには```VerifyCsrfToken```が入っていない

```
   /**
     * The application's route middleware groups.
     *
     * @var array
     */
    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            'throttle:60,1',
            'bindings',
        ],
    ];
```


## まとめ

 - Laravelではルーティングを設定するためのファイルが2種類ある
 - web.phpは画面表示する際のルーティングファイル
 - api.phpのルーティングは外部からのアクセス用のルーティングファイル
Gitのcherry-pickを取り消す

cherry-pickで反映したコミットは「git reset」コマンドで取り消すことができる。

```
$ git reset --hard HEAD~
```

cherry-pickしたブランチをリモートに上げてしまっている場合、取り消ししたブランチをpushしようとすると以下のようなエラーになってしまう。

```
$ git push origin master
To https://gitlab.com/hoge/hogehoge.git
 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'https://gitlab.com/hoge/hogehoge.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
```

これを回避するため、pushする際は「-f」オプションをつける必要がある。

```
$ git push -f origin master
```

※「git reset」コマンドを使用する場合ログに残らないので、残したい場合は「git revert」コマンドを使用する
Amazon SageMakerとAzure MLにおける機械学習モデルのサービング技術比較(後編)

初版: 2020年11月24日  
著者: 橋本恭佑、柿田将幸, 株式会社 日立製作所


## はじめに
本連載では、ストリーム型の機械学習システムをサービングする技術について、
Amazon SageMakerとAzure Machine Learningを比較した結果を紹介しています。
後編では、実際にストリーム型の機械学習システムを両クラウドでサービングして、本投稿で紹介した違いが現れること、
また、SEがAmazon SageMakerとAzure Machine Learningのいずれかの利用を、どのような基準で選択するべきかについて議論します。
なお、本連載に記載のAmazon SageMakerまたはAzure MLの情報は2020年9月末日現在のものであり、今後のアップデート等によって内容が変わることがあります。

### 投稿一覧
1. [Amazon SageMakerとAzure MLにおける機械学習モデルのサービング技術比較（前編）](https://qiita.com/kyosuke_hashimoto/items/48a4e050d6d2504d1e5c)
2. Amazon SageMakerとAzure MLにおける機械学習モデルのサービング技術比較（後編）・・・本投稿


## パブリッククラウドにおけるサービング実機検証と検証項目
Amazon SageMakerとAzure MLでランタイムを作成し、[前編](https://qiita.com/kyosuke_hashimoto/items/48a4e050d6d2504d1e5c)で示した相違点を実機で比較します。
ランタイムは次の手順で作成されます。
1. パブリッククラウドが提供するOSSの入ったコンテナイメージを指定する
2. モデル、前処理、後処理コードを呼び出す
3. 1と2をコンテナとしてコンテナ基盤上にデプロイする

![11_パブリッククラウドにおけるランタイム作成手順.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592367/117a5c18-b20e-f5d3-2784-ee797e17d01c.png)

検証項目は以下の通りです。
1. 前処理コードや後処理コードの実装方法の違い
2. ランタイム作成時のOSS追加可否
3. ランタイム作成時のコンテナ基盤指定有無

## 検証1: 前処理コードと後処理コードの実装方法の比較

Amazon SageMakerでは前処理や後処理を別の関数で書くため、可読性が高くランタイム作成後のコード変更が容易と考えられます。
一方でAzure MLは前処理と後処理を1つの関数に書くため、モデル訓練時の実装に近く、訓練時のコードを流用しやすいと考えられます。

![12_前処理コードと後処理コードの実装方法の比較.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592367/82824787-efba-0193-e363-a78eeb4bb661.png)

## 検証2: ランタイム作成時のOSS追加可否 
Azure MLではランタイム作成時にpipやcondaを利用してOSSを追加できます。
モデル訓練には不要だが推論システム作成には必要なOSSがある場合に、それらをすぐに追加できます。
したがって、最初の推論システムを作成するのに要する時間が短いと考えられます。

![13_ランタイム作成時のOSS追加可否.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592367/faf14a61-b4f2-0247-bbbc-585c1b117812.png)


## 検証3: ランタイム作成時のコンテナ基盤指定有無
Azure MLではランタイム作成先のコンテナ基盤を別途用意して指定する必要があり、コンテナ基盤(AKS)の知識が必要です。

![14_ランタイム作成時のコンテナ基盤指定有無.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592367/c7d19df6-3540-b357-f911-002316abc420.png)

## 実機検証を通したAmazon SageMakerとAzure MLの特徴考察
Amazon SageMakerには以下の特徴があり、推論システム作成時に問題が起こりにくいと考えられます。

* 前処理コードや後処理コードの可読性が高く、コードの誤りに気づきやすい
* 用意したコンテナイメージにないOSSはランタイムにインストールできない
* コンテナ基盤を意識せずにランタイムを作成できる

一方でAzure MLにも以下の特徴があります。

* モデル訓練には不要だが推論システム作成には必要なOSSがある場合に、すぐにそれらを追加できるため、
モデル訓練後からランタイム作成までのリードタイムが短く、顧客がすぐに推論システムを利用しやすいと考えられます。
* 訓練時の前処理や後処理コードを推論システムに流用しやすい
* ランタイム作成時に独自にOSSを追加できる

こうしたAmazon SageMakerとAzure MLの特徴をとらえて、いずれのベンダを利用するか検討すべきといえます。

## おわりに
本投稿ではAmazon SageMakerとAzure MLにおける機械学習システムのサービング技術を比較しました。
実機検証を通して、Azure MLはモデル訓練後から推論システム構築までのリードタイムを短くしたい場合に有効であること、
それ以外の場合はAmazon SageMakerが有効である可能性が高いことを確認しました。
The security token included in the request is expiredとなった時の対応方法

# 事象 : Secrets Managerからシークレット値を取得しようとしたら怒られた
- 環境
    - AWS Cloud9
    - Python 3.6.12

はじめてSecrets Managerにシークレット値を設定してCloud9からシークレット値を取得しようとした。
取得するコードは超親切にシークレット値を設定した時に表示されるのでほぼコピペで使った・・・が`ExpiredTokenException`・・・

```py
def get_backlog_api_key():

    secret_name = 'シークレットの名前'

    session = boto3.session.Session()
    client = session.client(
        service_name='secretsmanager',
        region_name='リージョン'
    )

    try:
        get_secret_value_response = client.get_secret_value(
            SecretId=secret_name
        )
    except ClientError as e:
        print(e.response['Error'])
        raise e
    else:
        if 'SecretString' in get_secret_value_response:
            secret = get_secret_value_response['SecretString']
        else:
            decoded_binary_secret = base64.b64decode(get_secret_value_response['SecretBinary'])
```

```bash
{'Message': 'The security token included in the request is expired', 'Code': 'ExpiredTokenException'}
```

## 原因 : Lambdaに権限がないから
権限の存在を忘却していた。


> Minimum permissions
> To run this command, you must have the following permissions:
> secretsmanager:GetSecretValue
> [GetSecretValue - AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html)

### 対応 : 権限を付与する
1. AWSのコンソール > Lambda > 対象の関数 > [アクセス権限]タブ > [実行ロール]からIAMの画面を開く
1. [アクセス権限]タブ > [ポリシーをアタッチします]ボタン
1. `secretsmanager`を検索 > SecretsManagerReadWriteにチェック > [ポリシーのアタッチ]ボタンで権限追加
    - ちょっと雑、取得するだけならもっと権限の範囲は小さいほうがいいけど今はとりあえず
    - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159761/1036fd28-ee47-8e94-2db5-19358a8065f8.png)
1. Cloud9のLambdaマークから更新ボタンで更新
    - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159761/77463337-e67c-a347-7717-20d97693ba3e.png)
    - 更新しても :arrow_double_down: こうなっている間はうまくいかない。なんでこれが出るのかよくわからない、しばらくしてから更新すると消える。よくわからない。
        - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159761/b90c5f0f-25cf-46a5-a60d-65dfa26ff068.png)
Python3: Google Cloud Translation API の使い方 (Basic)

次のページを参考にしました。
[テキストの翻訳（Basic)](https://cloud.google.com/translate/docs/basic/quickstart?hl=ja)

```py:ex01.py
#! /usr/bin/python
#
#
from google.cloud import translate_v2 as translate

text="Es war einmal ein kleines Mädchen."
target="ja"

translate_client = translate.Client()
#
result = translate_client.translate(text, target_language=target)

print("Text: {}".format(result["input"]))
print("Translation: {}".format(result["translatedText"]))
print("Detected source language: {}".format(result["detectedSourceLanguage"]))
```

実行方法

```bash
export GOOGLE_APPLICATION_CREDENTIALS=./***.json
#
./ex01.py
```

実行結果

```text
Text: Es war einmal ein kleines Mädchen.
Translation: 昔々、小さな女の子がいました。
Detected source language: de
```

関連ページ
[https://qiita.com/ekzemplaro/items/7316dfb0534405520831](https://qiita.com/ekzemplaro/items/7316dfb0534405520831)
Android studio：Kotlin：on start(){}を入れるとアプリがクラッシュする


```kotlin:main.kt
    override fun onStart() {
        super.onStart()
    }
```

なぜかアプリがクラッシュする。
onstart にもsetContentView(R.layout.activity_main)を入れると解決した。

```kotlin:main.kt
    override fun onStart() {
        super.onStart()
　　　　setContentView(R.layout.activity_main)
    }
```

画面の表示指示がないとだめっぽい。
LibreOffice CalcでVBAを使ってみた

#はじめに
- [LibreOffice Calc でVBAマクロを使う（始め方） - Qiita]
(https://qiita.com/hisabo/items/abdd558e4dd0785eaf92)

を参考にさせて頂きました。圧倒的感謝……！

#環境
- Windows 10 Home (64bit)
- LibreOffice Calc 6.4 (x64)

#手順

##マクロの作成
LibreOffice Calcのメニューから[ツール]→[マクロ]→[マクロの管理]→[Basic]を選択すると[Basicマクロ]ダイアログが表示される。（**Alt+F11キー**）

マクロの記録先から〜.odsファイルを選択し[新規作成]ボタンをクリックする。
名前：Module1
[LibreOffice Basic]ウィンドウが表示される。

```Module1
Option VBASupport 1

Sub Main
	MsgBox "hello, world"
End Sub
```

マクロエディタの実行でマクロの単体テストができる。

##コントロールにマクロを割り当てる
LibreOffice Calcのメニューから[表示]→[ツールバー]→[フォームコントロール]を選択する。
デザインモードにしてボタンを配置する。

プッシュボタンを右クリックして[コントロールのプロパティ]を選択する。

[イベント]タブの実行時にマクロを割り当てる。
ライブラリ：〜.ods/Standard/Modul1
マクロ名：Main

デザインモードを解除してプッシュボタンをクリックする。

#おまけ

```Module1
Sub Speak
	Dim voice
	Set voice = CreateObject("SAPI.SpVoice")
	voice.Speak("hello, world")
End Sub
```
便利な Drawing 系 Web サービス


## システム構成図

### glify

[glify](https://www.ricksoft.jp/atlassian/gliffy/)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18351/e9028e01-eb44-2cad-680f-682272ec95c0.png)

### Cacoo

[Cacoo](https://cacoo.com/ja/)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18351/266c0f9b-7b55-b18f-118d-f5cd7cee7f1b.png)

### draw.io

[draw.io](https://app.diagrams.net/)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18351/efa9d5d4-b192-48bc-e18b-0838c0d4327b.png)


## ER 図作成

### dbdiagram.io

[dbdiagram.io - Database Relationship Diagrams Design Tool](https://dbdiagram.io/home)
![image](https://dbdiagram.io/static/img/dbdiagram-demo.0791273.gif)
▼ サンプル
https://dbdiagram.io/d/5d919bdfff5115114db4be2e


### wwwsqldesigner

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18351/71f5cbd1-c9fa-c4ee-a6b1-d7157e901c43.png)
[ondras/wwwsqldesigner: WWW SQL Designer, your online SQL diagramming tool](https://github.com/ondras/wwwsqldesigner)

▼ サンプル
https://ondras.zarovi.cz/sql/demo/?keyword=default
An error occurred (InvalidSignatureException) when calling the GetSecretValue operation: Signature expired:となった時の対応方法

# Secrets Managerからキーを取得しようとしたら怒られた

- 環境
    - Windows10 Pro 64bit バージョン1909
    - aws-cli/2.1.3 Python/3.7.7 Windows/10 exe/AMD64

```bash
$ aws secretsmanager get-secret-value --secret-id hoge/ponsuke

An error occurred (InvalidSignatureException) when calling the GetSecretValue operation: Signature expired: 20201124T070055Z is now earlier than 20201124T070155Z (20201124T070655Z - 5 min.)

```

## 原因 : パソコンの時間がずれているから

数か月前から5分くらいずれているなぁって思っていた。
ドメインにも入っているのになんでかなぁと、放置していた。
Windows10になってからいまいちメニューがよくわからないから・・・なんつって。

### 対応 : パソコンの時間を修正する
1. スマホの時間を確認する(スマホが正しい前提)
1. Win + R で[ファイル名を指定して実行]ダイアログを表示
1. `timedate.cpl`を入力してEnter
1. [日付と時刻の変更]ボタンでダイアログを表示して時間をスマホに合わせる
    - 前に見たとき変更できなかったような・・・気のせいかなぁ
    - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159761/e5d8d43b-1189-6171-b41b-0ed0228707c2.png)

```bash
# めでたくとれた
$ aws secretsmanager get-secret-value --secret-id hoge/ponsuke
{
    "ARN": "arn:aws:secretsmanager:ap-northeast-1:8xxxxxxxx:secret:hoge/ponsuke-6XXXXX",
    "Name": "hoge/ponsuke",
    "VersionId": "3-xxxx-xxx...",
    "SecretString": "{\"ponsuke\":\"tarou\"}",
    "VersionStages": [
        "AWSCURRENT"
    ],
    "CreatedDate": "2020-11-24T14:48:38.906000+09:00"
}
```
GitHub ActionsでGoアプリをGoogle App Engineにデプロイする

# TL;DR
GitHub ActionsでGoアプリをGoogle App Engineにデプロイするには色々な方法が存在していたようだが，現在はGoogleの提供するgoogle-github-actions/setup-gcloudと言うActionを使うのが良いらしい．
https://github.com/google-github-actions/setup-gcloud

# Secretsの設定
GCP_PROJECT_IDにGCPのプロジェクトIDを設定する．
GCP_SA_KEYにGCPのサービスアカウントの鍵を設定する．サービスアカウントはデフォルトのもので良い．別サービスから移行する場合は忘れずに過去の鍵を削除しておく．

# workflowファイル
```.github/workflows/deploy.yml
name: "Deploy"

on: 
  push:
    branches:
      - master

jobs:
  deploy:

    runs-on: ubuntu-latest

    steps:
    - name: Checkout repository
      uses: actions/checkout@v2

    - name: Set up Cloud SDK
      uses: google-github-actions/setup-gcloud@master
      with:
        project_id: ${{ secrets.GCP_PROJECT_ID }}
        service_account_key: ${{ secrets.GCP_SA_KEY }}
        export_default_credentials: true

    - name: Use gcloud CLI
      run: gcloud app deploy --quiet app/web/app.yaml
```

最初--quietをつけ忘れてエラーになってしまった．

# まとめ
思ったより簡単にGitHub Actionsに移行できた．みんな使おう！
【SAP】SAP TechEd 2020　~概要編~

#はじめに#
去年初めてTechEd（ラスベガス）に参加し、今年はオンラインで開催されるということで迷い人も多そうなので概要を書いてみました。例年SAP社のBIGイベントは、以下２つが開催されています。

　　・**_SAPPHIRE NOW_**：クライアントやパートナーに対するセールス向けのイベント
　　・**_TechEd_**：エンジニア向けのイベント

**_SAPPHIRE NOW_**でSAP社の戦略やビジョンが示され、**_TechEd_**でその年にSAP社が力を入れているテクノロジーについて紹介し、トレーニングが受講可能なイベントとなります。**_TechEd_**はArch/Development/Analysisのコンサルタントやエンジニアが、最新で詳細な技術をキャッチアップすることができるイベントです。


イメージ的にはこんな感じです。
　　・**SAPPHIRE NOW**： 戦略・ビジョン発表イベント(世界中から集まる）
　　・**SAP TechEd**： 一番大きな技術イベント(世界中から集まる）
　　・**Inside Track Tokyo**：日本国内でみんなが集まる有志勉強会
　　・**TechJAM**：  SAPジャパン主催の一番大きな技術イベント

#TechEd 2020とは？#

2019年は、ラスベガス/バルセロナ/バンガロールで実施され、今年もオースティン(米国)で開催予定でしたが、Covid19の影響により**_オンライン開催_**となり、12/8-10まで48時間通しで500以上のセッションが開催されます。また、**_参加は無償で誰でも可能_**（昨年までは有償）となりました。日本時間ですと、深夜に開催されるセッションもあるため、この３日間は日中に本業もこなしながらですと、寝不足になりそうですね。

今年のメイントラックは以下8つ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/792264/dc08010e-d47d-f089-fea3-a6daf567865b.png)
昨年とトラック名称は違えど同様のテーマもあります。

***

各トラックはさらにサブトラックに分かれており、複数のセッションが開催されます。
セッションは、主に以下の形式に分類されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/792264/29ac7e29-1b16-9e01-62e8-f2150abd786c.png)
この中でもハンズオンは、人気が高く、人数制限もあるため、すぐに予約をしないと埋まってしまいます。
（2020/11/23時点でほぼ全てのハンズオンが埋まってしまいました。）
ですが、後々動画配信されることが多いです。

■事前準備として
ハンズオン受講者向けですが、セッション開始1週間前くらいにハンズオン用資料（英語）が公開されますので、事前学習をお勧めいたします。セッション自体が英語なので、資料をGoogle 翻訳しておくだけでハンズオンの理解度が変わります。英語に強みを持つ方でも事前にお目通し頂く事が望ましいです。

#参加するためには？#
以下の公式サイトで申し込み可能です。
https://events.sapteched.com/pages/sap/sapteched2020/index
※S-IDの作成が必要です

SAP社のSAPTechEd紹介動画もオンライン、無料であることがコンセプトになっていますね。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/Vimeo%E3%81%A7?src=hash&amp;ref_src=twsrc%5Etfw">#Vimeoで</a>「SAP TechEd - Sign Up Now - EDIT 02」を見る。 <a href="https://t.co/c7dmgApro9">https://t.co/c7dmgApro9</a></p>&mdash; Ksolくん@SAPメイン (@KK15611945) <a href="https://twitter.com/KK15611945/status/1331129863185395713?ref_src=twsrc%5Etfw">November 24, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

※リンク切れしてる場合は以下
https://events.sapteched.com/pages/sap/sapteched2020/index


申し込み後は、参加したいセッションを探して、**_ADD TO SCHEDULE_**を押下しましょう。ハンズオンも話を聞く講義も同じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/792264/45a8ed9b-84c3-29ef-2347-d1bff236b870.png)

**_My Dashboard_**から自分のカレンダーにアクセスして、申し込んだセッションをカレンダー形式で参照することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/792264/46ddfa53-cf79-e240-a0dc-20a920ba74ba.png)

来年以降、またオンライン開催となるかは現時点では分かりません。
（ソフトバンクの孫正義さんは、第二四半期決算発表の中で、国内では来年秋頃まで続くとみているようです）
また現地開催となると、社内で参加することの承認を取得し、参加費を支払い、その時点で持っているタスクの調整をして、参加となります。今年はせっかく誰でも参加できる良い機会ですので、奮って参加しましょう。

当社でも複数名で参加しますので、今後セッションを受けた感想等記載したいと思います。
offset()とmatch()の自分なり解説

#はじめに
Excelを使用して調査をすることが多くなり、関数を多用しています。
その中で、自分自身が苦労したので解説をみていただくことで、誰かの役に立てればと思います。
#対象読者
普段Excelをそこまで触らない人、
関数はSUMくらいしか触らない人向け
#自分なり解説
最低限の説明なので、[]は今回説明省略。
MATCH(対象セル,比較列,[照合の種類])
// 対象セルの値を比較列から探す
// 比較列の中で対象セルと比較列が一致した位置を返す
OFFSET(対象セル,縦の位置、横の位置,[高さ],[幅])
// 対象セルから縦にいくつ動くか
// 対象セルから横にいくつ動くか

では上記を二つ合わせるとどうなるか？

OFFSET(対象セル,MATCH(対象セル,比較列))
①MATCH()の処理が先に行われ、一致した行の位置を取得
②OFFSET()で、MATCH()した位置まで移動

#おわりに
調査の時、多用して、最初理解するまで時間がかかったので、整理のために載せました。
こんなこと知ってるよって方はどうぞこの記事をスルーしてください。
WPFのListView,ListBoxで選択要素変更とコマンドを結び付ける方法

# はじめに
本記事は、WPFのListView,ListBoxで選択要素変更とコマンドを結び付ける方法を備忘録として残すことを目的としています。

# 方法
筆者は[こちら](https://stackoverflow.com/questions/4897775/wpf-binding-ui-events-to-commands-in-viewmodel)の記事を参考にしました。
以下のSampleWindowのDataContextにはSampleWindowViewModelが設定されているとします。

- SampleWindowViewModel.cs

```csharp

using Sample.ViewModels
{
    // SampleWindowViewModelのViewModel
    public class SampleWindowViewModel
    {
        // 選択変更時のコマンド
        public ICommand SelectionChangedCommand { get; }
    }
}
```

- SampleWindow.xaml

```xml

<Window x:Class="Sample.Views.SampleWindow"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">

    <Grid>
        <ListView ・・・>
            <!-- ListViewのSelectionChangedイベントが発生するとSelectionChangeCommandコマンドが実行されるように設定 -->
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="SelectionChanged">
                    <i:InvokeCommandAction Command="{Biding Path = SelectionChangedCommand}" />
                </i:EventTrigger>
            </i:Interaction.Triggers>
    </Grid>
</Window>
```

SampleWindow.xamlの「xmlns:i=・・・」の部分でSystem.Window.Interactivityの機能を使えるように名前空間をusingしています。その後のListViewの「i:Interaction.Triggers」でトリガを設定しています。具体的には「i:EventTrigger EventName="SelectionChanged"」なので、選択変更後イベントをトリガにしてアクションを実行します。アクションは「i:InvokeCommandAction Command="{Biding Path = SlectionChangedCommand}"」なので、DataContextのSelectionChangeCommandという名前のコマンドが実行されます。具体的にはSampleWindowViewModel.SelectionChangeCommandが実行されることになります。

# 参考文献
- Stack Overflowの記事
    - https://stackoverflow.com/questions/4897775/wpf-binding-ui-events-to-commands-in-viewmodel
RFCを考慮したemail形式バリデーション

```
/^[-a-z0-9~#&'*/?`\|!$%^&*_=+}{\'?]+(\.[-a-z0-9~#&'*/?`\|!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+)|(docomo\ezweb\softbank)*\.(aero|arpa|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro|travel|mobi|[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i; 

```
PPAPや暗号化zipやめるべき? 代替方法について

# PPAPや暗号化zipやめるべき? 代替方法について

2019年11月 [JPAAWG](https://www.jpaawg.org)の第2回General MeetingのOpen Round Tableで「暗号化zipやめるべき? 代替方法について」というタイトルで議論が行われました。
この時、ファシリテーターをやらせていただきました。
PPAPの代替が話題になっていますので、もったいないので記録として公開しておきます。

内容については、個人の見解ではなく、あくまでこの時集まった方々の意見の集大成となります。

Round Tableへの参加者は20～30人程で、活発な意見交換が行われました。
![roundtable2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245486/9bf66887-6b9a-a698-4415-af24ca0d70b4.jpeg)

Open Round Tableの各テーマ毎のまとめ発表の様子です。
![roundtable1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245486/60e89cf8-dbe5-64c0-3449-2860a2b7116d.jpeg)

## はじめに

当ラウンドテーブルではテーマを暗号化 zip に絞り、パスワードの受け渡し方法についてはスコープ外としました。

その上で、まず、暗号化 zip が存在しない世界において、暗号化 zip があると何がうれしいのかについて議論をおこないました。

## 暗号化 zip で実現できること

- 特定の人だけが見ることができる
- 受信者に安心感
- 送信者に安心感
- 会社の規定に準拠できる
  - ISMS でやれと言われている
  - お客様からの指示
- 圧縮できる
- 転送時にも安心
- 間違って Virus Scan で止められない
- CRC があるので改ざんされない
- アーカイブされるので便利

次に、すべての添付ファイルが暗号化 zip になっている世界において、暗号化 zip がなくなれば何がうれしいのかについて議論をおこないました。

## 暗号化 zip しなければ実現できること

- 添付ファイルのインデックスを作成できる
- ゲートウェイでウィルスチェックができる ← 安心感
- 業務の効率化になる
- メールの通数が減る
  - PPAP のパスワードが来ないので
- 外国へ送るときのめんどくさい説明がなくなる
- パスワードをなくして開けなくなるようにことがおきない

次に、上記の、暗号化 zip でできること、暗号化 zip しないことによってできること、のそれぞれの項目でユーザが実現したい本質的な要求を探りました。

## 上記のようなことをやりたい動機

- 機密な情報の流出を防ぎたい
- 個人情報を見せたくない
- 他の人に見られることで損害を被らないようにしたい
- 誤送信防止のため
- メールが手元から離れた後にどうなるか分からない不安だから
  - 出してしまったメールは取り返せないので
- 大きなサイズのファイルを小さくしたい
- フォルダ構造を表現したい
- メールロストを防ぎたい
- 添付ファイル数が多いとメーラーから取り出すのがめんどくさい
- 業務を効率化したい
  - 検索など
- 安心・安全のため
- スパマーの抜け道を防ぎたい
- 受信者が悪いファイルを開いたときに自分の責任になるのを避けたい
- 会社の信頼が落ちないようにしたい
- パスワードでわざわざ開く時間がもったいない

これらは、暗号化 zip で実現したいことと、ZIP 暗号化しないことで実現したいことの両方が混在しているので、つまり、上記が本当は実現したいことと考えられます。

キーワードは、**安心感**。

    人は、安心感のために、zip 暗号化をし、
    また、安心感のために、zip 暗号化をしない。

ここまで議論した時点で、1 人を除いてほぼ全員が暗号化 zip に対して反対派でした。

そこで、現状の暗号化 zip に対して、何が実現できれば賛成派になれるかについて議論しました。この結論が代替手段になるはずです。

## zip 暗号化の代替手段に必要なもの

- 暗号化は必要
- パスワードを使わなくてよい
- zip より強度の高い暗号化
- シームレスな複合化
- ウィルススキャンが動作する
- 本当に渡したい人にだけ渡ったという確証が欲しい

最終的に、当ラウンドテーブル参加者の暗号化zi 反対派を含む全員が、この代替手段には賛成するということが確認できました。
【PHP】XSS攻撃への対策

投稿フォームを作成している際、特に対策をしていない場合XSS攻撃を受けてしまう事を学んだ。

PHPではXSS攻撃の対策としてhtmlspecialchars関数が有効であるのでこの関数を使用してフォームを作成。

この関数を使用することによってフォームに入力された物がただの文字列として認識され、javascripなどを記入されても反応しない様になります。


```PHP
<html lang="ja">
<body>
<h1>投稿フォーム</h1>
<form action="example.php" method="post">
<input type="text" name="comment"><br/>
<input type="submit" value="送信">
</form>
<?php
$comment = htmlspecialchars($_POST['comment'], ENT_QUOTES, 'UTF-8');
echo $comment;
?>
</body>
</html>
```

htmlspecialcharsは名前が長いので関数に入れてしまう事も考えたが、このコード自体は短いものであったので今回はそのまま記述。
pythonのみで２次元配列の転置、型変換を行う方法[備忘録]

# やりたいこと
pythonの二次元配列を転置してさらにint　をstrに変換する
pandas やnd.array は使わないで行いたい

```example.txt
[
[5, 0, 1, 1], 
[1, 0, 1, 5], 
[0, 1, 6, 0], 
[0, 4, 3, 0], 
[5, 2, 0, 0], 
[5, 0, 1, 1], 
[0, 6, 0, 1], 
[0, 1, 0, 6]
]

=>

[
['5', '1', '0', '0', '5', '5', '0', '0'], 
['0', '0', '1', '4', '2', '0', '6', '1'],
 ['1', '1', '6', '3', '0', '1', '0', '0'], 
['1', '5', '0', '0', '0', '1', '1', '6']
]
```

# code
```hoge.py
cnt_ACGT_t = [list(map(str,i)) for i in zip(*cnt_ACGT)]
```

# 転置
複数のイテラブルを受け取り、同じ場所の要素でまとめたイテレータを返す組み込み関数であるzipを用いる

```
zip(*cnt_ACGT)
```
でcnt_ACGTのリストを転置したものがイテレータオブジェクトに変換された



# 全要素のintからstrの変換
zip(*cnt_ACGT)はイテレータなので、そのままリスト内包表記を使って各要素をstrに変換する
１行ごとのリストにたいしてmap でstrに変換する

# 参考
https://note.nkmk.me/python-list-transpose/ おなじみnkmkさんのブログを参考にしました
Error: A Network Change was detected. Gmail Salesforce Extension for Chrome

気になった、質問と回答

https://trailblazers.salesforce.com/answers?id=9064S000000DLsM

 Gmail Salesforce Extension for ChromeでA Network Change was detectedという画面になる。

![Qiita](https://trailblazers.salesforce.com/servlet/rtaImage?eid=9064S000000DLsM&feoid=Body&refid=0EM4S000000UN3n "Qiita")


回答
拡張機能を読み込もうとしたときにインターネット接続が変更されたことが原因である可能性があります。接続を復元しても問題が解決しない場合は、拡張機能を再インストールするか、SF資格情報に変更があったかどうかを確認してください。
【脱初心者！！】Railsでmodelにスコープを定義する！

# はじめに
最近までwhereやorderはついついcontrollerに書いてしまうことが多かったのですが、modelで定義した方がメリットが多いので今回はmodelにスコープを定義するやり方を解説していきます！

# modelに定義するメリット
- controllerをみやすくシンプルにできる(Controllerを太らせないことが脱初心者への第一歩です！)
- modelに書くことによりテストが書きやすくなる！(RailsでいうとRspecのmodel_specでテストがかけますね！)

# modelのスコープって何？
modelのスコープとは複数のクエリをまとめたメソッドのことです！！
where, order, join, limit, distinct ...etc
などでまとめることですね！

# さっそくやってみる
今回は予約テーブルのreservationsコントローラーを設定しています

reservaiontsテーブル
title(string)とstart_time(datetime)のカラムがあります。

```ruby:reservations.controller.rb
class ReservationsController < ApplicationController

  def index
    #ページネーションをつかっています(Reservation.allだと思ってください)
    @reservations = Reservation.paginate(page: params[:page])

    #全ての予約からstart_timeが今日より後の予約を 降順で表示する
    @reservations_index = @reservations.where("start_time > ?", Date.today).order(start_time: :desc)
  end
```
ご覧の通り少しコントローラーが長くなり読みづらいです。

これをmodelに定義してみます。

```ruby:reservation.rb
class Reservation < ApplicationRecord
  scope :future_reservations, -> { where("start_time > ?", Date.today).order(start_time: :desc) }
```
解説するとscopeでまず定義しひとまずfuture_reservationsでメソッドを定義します。
その後はcontrollerの記述をリファクタリングするだけです！！

ちなみに

```ruby:reservation.rb
  scope :future_reservations, -> do
    where("start_time > ?", Date.today).order(start_time: :desc)
  end
```
コードが長くなる場合はこちらの方が綺麗なので覚えておきましょう！！

```ruby:reservations.controller.rb
  def index
    @reservations = Reservation.paginate(page: params[:page])
    @reservations_index = @reservations.future_reservations
  end
```

最後にcontrollerにメソッドを書いてすっきりさせましょう！
お疲れ様でした！！！
[C++] もうとにかく今すぐデバッグのためのログを出力したいときの関数(C++)

# もくじ

https://tera1707.com/entry/2022/02/06/144447

# やりたいこと

C++でアプリをつくっているときに、もう何でもいいから今すぐ簡単にログを出力させて、それを見たい。
例えば、VisualStudioでデバッグ実行がしたくてもできないけど、どの経路を通ったかを知りたくて、でもMessageBoxは出したくない、かつ、Releaseビルドでやりたい(≒OutputDebugStringとかがVS上で見れない)というとき。

# やり方

下記のような簡単なログ用関数を貼り付けて使用する。
終わったら、関数ごと消す。

```cpp
#include <iostream>
#include <stdio.h>
#include <tchar.h>
#include <string>
#include <shlobj.h>
#include <time.h>
#include <thread>   // std::this_thread::get_id()を使うのに必要
#include <fstream>  // std::wofstreamを使うのに必要

void OutputLogToCChokka(std::wstring txt)
{
    //FILE* fp = NULL;
    auto t = time(nullptr);
    auto tmv = tm();
    auto error = localtime_s(&tmv, &t); // ローカル時間(タイムゾーンに合わせた時間)を取得

    WCHAR buf[256] = { 0 };
    wcsftime(buf, 256, L"%Y/%m/%d %H:%M:%S ", &tmv);

    // 現在のスレッドIDを出力
    auto thId = std::this_thread::get_id();

    // ログ出力
    std::wstring logtxt = buf + txt;

    // 「thread::id」型のthIdをfputやfprintf等でファイルに書くやり方がわからなかったので別の書き込み方法にした
    //auto ret = _wfopen_s(&fp, L"C:\\mylog.log", L"a+");
    //if (fp != NULL)
    //{
    //    fputws(logtxt.c_str(), fp);
    //    fclose(fp);
    //    fp = NULL;
    //}

    // ファイルを開く(なければ作成)
    // C直下のファイルに書くにはexe実行時に管理者権限にする必要アリ
    std::wofstream ofs(L"C:\\mylog.log", std::ios::app);
    if (!ofs)
    {
        return;
    }
    // 現在時刻とスレッドIDを付けたログをファイルに書き込み
    ofs << thId << L"  " << logtxt.c_str() << std::endl;
    std::wcout << thId << L"  " << logtxt.c_str() << std::endl;
    // ファイル閉じる
    ofs.close();
}

int main()
{
    OutputLogToCChokka(L"aiueo");
    system("pause");
}

```

※このときはいろんな都合で、その処理が走っているスレッドのIDをみたかったのでスレッドIDをログに出すようにしている。

で、C言語的書き方が好きだったので`fprintf`でログに書き込みしようとしたが、「thread::id」型のthIdをfputやfprintf等でファイルに書くやり方がわからなかったので別の書き込み方法(std::wofstreamを使う方法)にした。

# メモ
書いていて思いましたが、
`std::wstring logtxt = buf + txt;`
なんていう書き方(bufは`WCHAR buf[256]`、txtは`std::wstring`)ができるんですね。
変な感じがします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/52d94fd3-1901-0b69-0f30-136a3db6c427.png)
これのおかげなんですねきっと。
OffensiveNim之偏僻语言shellcode加载器

@byt3bl33d3r 师傅在[twitter](https://twitter.com/byt3bl33d3r/status/1330572114970042369)上发布了他最新的一个GitHub仓库，名为OffensiveNim。该仓库使用介绍了一种新的编译型语言来写一些工具，比如minidump、shellcode loader。

# 为什么选择nim？
1. 可以直接编译为C、C++、Objective-C和Javascript。
2. 语法简单，不依赖运行时虚拟机。
3. 具有极其成熟的外部接口API。
4. 跨平台交叉编译。
5. 可以将代码直接编译为Javascript，甚至初步支持WebAssembly

# 使用OffensiveNim
OffensiveNim提供了几个代码demo

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/593424/66ff231a-9664-570d-25e6-5905eff9f7cf.png)

我们使用kali来编译OffensiveNim提供的demo，先git clone下来

```
git clone https://github.com/byt3bl33d3r/OffensiveNim.git
```

安装nim编译器和nimble包管理器。

```
sudo su
apt update
apt install nim
```

如果你要编译出Windows下可以运行的exe和dll文件，那么必须安装mingw

```
apt install mingw-w64
```

然后使用包管理器nimble安装winim包

```
nimble install winim
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/593424/024ab4d0-f93b-d0c6-5d92-86be36f48a23.png)

最后cd到OffensiveNim的根目录，运行make命令即可。

几分钟编译完之后会在bin目录下生成exe可执行文件，拖回本地执行即可。

# 加载shellcode
拖回本地的文件落地就被Windows defender杀烂，根据特征应该是msf的shellcode特征。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/593424/317762cd-c236-8db7-c74b-14bd1a43534e.png)

查看其代码在shellcode_bin.nim中写死了shellcode。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/593424/d88ccb61-0a3c-3388-402c-b800886ea3d4.png)

那就用nim写一个shellcode混淆的就可以了，实现的代码就不放了，就一个xor加密shellcode。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/593424/bc8b0219-a918-26ac-5f2a-15eab95b8d26.png)

正常上线
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/593424/f6996282-3cea-addf-47d7-5d9044be5dd8.png)

# 参考
1. https://github.com/byt3bl33d3r/OffensiveNim/
2. https://twitter.com/byt3bl33d3r/status/1330572114970042369
3. https://nim-lang.org/
でかすぎる改行なしのsitemap.xml内のURLをLinuxコマンドで調べる

でかすぎる改行なしのsitemap.xml内にurlがいくつ書いてあるか調べたくて、Linuxコマンドでサクッとやってみた。

`</url>` の後に改行コード入れて、行数を調べることで判定してみる。

sedの出力で改行コードを出すのに`\n`とか入れてみても`n`と文字が出てしまうので、結局コマンドラインから改行そのものを入れた。ダブルクオートではなくシングルクオートで囲まないとうまくいかない。改行前の`\`も忘れずに。

```
$ cat sitemap.xml | sed -e 's/<\/url>/<\/url>\
/g' | wc -l
```

出てきた数値−1がurlの数、のはず。
SQL Developerでデバッグができない

##はじめに
SQL Developer でPL/SQLをデバックしようとしたときエラーが発生。

```
PL/SQLの実行中: CALL DBMS_DEBUG_JDWP.CONNECT_TCP( '127.99.99.99', '60000' )
ORA-24247: アクセス制御リスト(ACL)によりネットワーク・アクセスが拒否されました
```

アクセス制御リストにいないIPはデバッグができないようになっているのが原因とのこと。
このエラーが発生したときの解決方法をメモ。

##解決方法
host（今回の場合だと'127.99.99.99'）とprincipal_nameを記載して下記を実行する。

```
BEGIN

 DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE
 (
    host => 'ホスト名　または　ドメイン名',  
    lower_port => null,
    upper_port => null,
    ace => xs$ace_type(privilege_list => xs$name_list('jdwp'),
    principal_name => 'スキーマ名',
    principal_type => xs_acl.ptype_db)
 );
END;
```
【PHP】九九表

九九表を作成する問題を説いた際、短時間で綺麗にコードを書く事ができなかったので備忘録として完成させたコードを載せます。

```PHP
<?php
    //縦方向に出力するためのループ
	for( $i = 1 ; $i <= 9 ; $i++ ){

	    //横方向に出力するためのループ
		for( $j = 1 ; $j <= 9 ; $j++ ){

		    //一桁の場合空白を入れる様にする
			if( $i * $j < 10) echo " ";
			echo $i * $j, " ";
		}
		echo "\n";
	}
?>
```

このコードによって表示される結果は下記。

```
 1  2  3  4  5  6  7  8  9 
 2  4  6  8 10 12 14 16 18 
 3  6  9 12 15 18 21 24 27 
 4  8 12 16 20 24 28 32 36 
 5 10 15 20 25 30 35 40 45 
 6 12 18 24 30 36 42 48 54 
 7 14 21 28 35 42 49 56 63 
 8 16 24 32 40 48 56 64 72 
 9 18 27 36 45 54 63 72 81 
```
[iOS]Xcodeでシミュレーターを使ってアプリを検証する

## 1. シミュレーターの起動とアプリ起動
### 1.1. シミュレーターの起動
1. XCodeウィンドウ上部の「端末設定ボタン」を押下
2. 開かれるリスト内のiOS Simulatorsから、起動したい端末を選択
 
### 1.2. シミュレーターでアプリを起動
1. XCodeウィンドウ上部の「TARGETボタン」を押下
2. ビルドしたいターゲットを選択

## 2. シミュレーター端末のOSバージョン確認
1. Xcodeの上部メニューにあるWindow > Devices and Simulators を選択
2. Simulatorsタブを選択
3. 端末一覧が表示されるので、OSバージョンを確認したい端末を押下すると確認できる。

## 3. シミュレーター端末のインストール
デフォルトだと、そのXCodeインストール時の最新のiOSでしか各シミュレーター端末が使えない。過去バージョンiOSでシミュレーターを起動したい場合は、自分でダウンロードしてインストールする必要がある。

### 3.1. ダウンロード&インストール
1. XCodeウィンドウ上部の「端末設定ボタン」を押下
2. Download Simulatorsを押下
3. DLしたいiOSバージョンを選択して、Check and Install Nowを押下
4. ダウンロード完了後にXCode再起動

### 3.2. 新しいシミュレーター端末の登録
1. Devices and Simulatorsを開く
2. Simulatorsタブを選択
3. 左下にある+ボタンを押下
4. 「Create a new simulator」ダイアログで端末、OSバージョン、そして名付けを行ってCreateを押下
DockerでLaravelを構築してみた


# Laradockとは
> Docker環境でLaravel環境を簡単に構築できるDockerイメージです。
Laradockのプロジェクトは大きくなり、SymfonyやWordpress環境までも構築出来るようになりました。

> 機能としては、Nginx, Apacheなどのwebサーバー、PHPの各バージョン、Mysql, PostgreSQL等の各データベースを自由に選択できます。

> 今回はNginx、PHP-FPMが動く環境を作り、Laravelを立ち上げるまでを記載します。

# 前提
・dockerインストールしていること
・docker-composeコマンドが実行できるようになっていることが必要。

# 環境
> Windows10 Pro

# 手順
## [1] CMD開く。作業ディレクトリを作成してそこまで移動する

```vim
cd C:\Users\user
```

## [2] フォルダ内でLaradockをgithubからcloneします。

```vim
git clone https://github.com/laradock/laradock.git
```

## [3] Laravel環境を構築するまでの下準備

> nginx（webサーバー。Apacheでも良いが、今回はLaradockの公式マニュアルに沿いました）
php-fpm（PHPを実行する環境。いわゆるfastCGI）

### .envの作成

```vim
copy env-example .env
```
### dockerコンテナの立ち上げ

```vim
docker-compose up -d nginx
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57249/5348484d-cb11-24bf-7ca5-813bac9940b9.png)


### コンテナが立ち上がっているかを確認する
```vim
docker-compose ps
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57249/bab76198-9d67-3130-58f2-f147b555ad32.png)


### workspaceにログインし、Laravelをセットアップする

```vim
docker-compose exec workspace bash
```
> workspaceにログインすると、最初の状態では/var/wwwにいる。この場所は、Windows側のprojectsフォルダ直下と同様の場所を表している。（laradockディレクトリ直下ではない)

> /var/wwwの位置でLaravelをセットアップする

```vim
composer create-project laravel/laravel app01
```

> 現時点でディレクトリ構成は以下のようになっているかと思います。
親：projects
子1：laradock … Dockerコンテナ
子2：app1 … Laravel本体

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57249/092dd1bf-c2e1-ae8f-6f9c-6a9a82163221.png)

###  Dockerダッシュボード状態
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57249/053e1313-2038-e328-3a86-6a779ce27add.png)

### ブラウザで動作確認する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57249/33be9d88-d94b-de04-64d1-ec35206a1511.png)

> ginx（webサーバー）の設定をします。設定ファイルを編集するため、一度、dockerコンテナを停止します。Windows側のlaradockディレクトリ直下で以下のコマンドを叩く

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57249/a8e8f097-0580-a6ba-7728-13a7030abb5d.png)

> dockerコンテナが全てストップします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57249/a5db0c5e-7f57-fb2b-9f55-57ba0fc6faa9.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57249/14750202-9190-e502-321b-3d4bc7ea2c62.png)

### コンテナを再度立ち上げる
```vim
docker-compose up -d nginx
```

### コンテナが立ち上がったら、ブラウザでhttp://localhost へアクセス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57249/52d6c486-6c33-d5aa-776e-2f7ecb7b5068.png)


# 参考
1. [http://laradock.io](http://laradock.io)
2. [http://laradock.io/introduction/](http://laradock.io/introduction/)
3. 環境作成時に次のエラーが出た場合
 
 > UnexpectedValueException
  The stream or file "/var/www/storage/logs/laravel.log" could not be opened: 
  failed to open stream: Permission denied

>> laravel.logに書き込もうとして権限がなかった時に発生します。

```vim
sudo chmod 777 -R storage/
```

> 参考になったURL
[https://teratail.com/questions/236475](https://teratail.com/questions/236475)
uPyCraftを用いたESP32のpingコマンドの方法

#はじめに
　こんにちは．こちらの記事はESP32からpingコマンドを実行したいけど方法がわからない人向けの記事となっています．この記事を書くきっかけはインターネットで「ESP32 ping 方法」と検索してもそのほとんどがC言語で書かれているか，環境がArduino IDEであったりと自分が望む記事が出てこなかったのが理由です．苦労した末にたどり着いた記事がとても役に立ったので自分と同じような人がいれば役に立つと思いますのでご査収ください．

#この記事に向いている人
+ ESP32を用いてpingコマンドを使いたい
+ 統合開発環境(IDE)にuPyCraftを使っている

#今回使ったもの (使用環境)
+ windows10
+ uPyCraft1.1
+ ESP32

#実装前
　MicroPythonを使用します．IDEはuPyCraftを使用します．どうやって用意するかはこちらの記事で詳しく書かれています([ESP32にmicropythonを入れる方法](https://qiita.com/sirius1000/items/0d8aad6abae996051ff6))．また，wifiにつなぐ必要もありますのでこちらの記事に方法が載っています([ESP32をWifiに接続する方法](https://qiita.com/sirius1000/items/5313aa099c58ab748921))．この二つができて初めてpingに取り掛かる準備ができます．
　さて，Pythonでpingコマンドを使うのであればライブラリから「import pings」や「import subprocess」を呼び出してpingコマンドを使うと思うのですがMicroPythonではこれがライブラリとして入ってないです．MicroPythonで使えるライブラリ一覧は下記となります．
![モジュール一覧.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557262/4456eb39-8783-c256-068e-df931f3d36d7.png)
　御覧の通りPythonよりライブラリの数が少ないですね．そして，pingコマンドを使いたいのにそのライブラリが無いという問題に直面します．無い場合どうするべきか，一番良いのは自分で作ることですが私にはその実力はないので実力のある人の力を借ります．こちらの記事([uPing - Ping library for MicroPython](https://forum.micropython.org/viewtopic.php?f=15&t=5287&sid=75e05f9e15e2f63dad3a2a06c7e7150d))でMicroPythonで自作のpingモジュールを作りpingコマンドを再現しています．今回はその記事を参考にpingがきちんと通っているのかどうかを確認するプログラムを作りたいと思います．

#pingライブラリを作る
自作pingを作ったShawwwnさんの[git](https://gist.github.com/shawwwn/91cc8979e33e82af6d99ec34c38195fb)を拝借してpingライブラリを使えるようにします．ちなみに45行目の「h.id = urandom.randomint(0, 65355)」を「h.id = urandom.getrandbits(16)」に変えると正しく動くそうです．

このプログラムをuPyCraftで実行すると下記の結果が得られると思いますこの結果がでれば成功です．MicroPythonでpingのライブラリが疑似的にですが使えるようになりました．
![uping.py実行結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557262/f91266df-bc0a-1901-3314-4b86fe3723d2.png)
試しにGoogleが無償で提供している「Google Public DNS」のIPアドレス8.8.8.8に向かってやってみます．
![ping実行結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557262/8abe8a8d-90f3-2812-f089-a6646881e731.png)
成功です．

#簡単な接続判定プログラム
使うことができるようになったpingライブラリでping接続判定をしたいと思います．

```
import uping
target = "8.8.8.8"
aaa = uping.ping(target, count=1)

if aaa == (1, 1):
  print('成功')
else:
  print('失敗')
```
![qiita3.py実行結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557262/92b00f2f-e548-7b0b-efa6-a0d6649a126d.png)
うまく判定することができました．

#参考文献
- [ESP32にmicropythonを入れる方法（uPyCraftを使います）](https://qiita.com/sirius1000/items/0d8aad6abae996051ff6)
- [ESP32をWifiに接続する方法](https://qiita.com/sirius1000/items/5313aa099c58ab748921)
- [uPing - Ping library for MicroPython](https://forum.micropython.org/viewtopic.php?f=15&t=5287&sid=75e05f9e15e2f63dad3a2a06c7e7150d)
- [github](https://gist.github.com/shawwwn/91cc8979e33e82af6d99ec34c38195fb)
GoのnilスライスはFirestoreだとnullになるから気をつける

## 問題

GoでFirestoreのドキュメントにデータを書き込んでいて、配列を期待するフィールドに何故かnullが入っていて不思議になった。
データの型としても

```go
type Fuga struct {
	Hoge        []string         `firestore:"hoge"`
}
```

だし、スライスに何も入ってなかったらFirestore側には空配列入って終わりでしょって思ってた。

## スライスのゼロ値はnil

https://godoc.org/cloud.google.com/go/firestore#DocumentRef.Create の以下の文章を見るに、Firestoreはあらゆる型の `nil` を `null` に扱うらしい。

> nils of any type convert to Null.

スライスのゼロ値はnilなので、ただ構造体を初期化しただけだとスライスはnilのまま -> そのままfirestore側に保存するとnullになるということのようだった。
なので、構造体を初期化する時にスライスも `make` 使うなりしてnilではない空のスライスを作ってあげることを意識した方が良いなと思った。
CORSのエラーが出たけど解消できなかったときのメモ

#エラー内容

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737363/f8cf9f47-7603-2aeb-adaa-a14fa963793b.png)


#構成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737363/90fec644-fd7a-f251-3d48-a40d9608bd2e.png)

・codepenからaxiosでIFTTTのwebhooksのURLにリクエスト
・LINEnotifyで通知

#なんのエラーか

異なるオリジン（ドメイン＋ポート番号の情報）で通信することを許可されていない場合に起こるエラー

#解決方法
基本的にサーバーサイドでの実装

```
Access-Control-Allow-Origin: https://trusted-one.co.jp  // 特定のサイトを許可する
Access-Control-Allow-Origin: *   // 全てのサイトを許可する
Access-Control-Allow-Headers "X-Requested-With, Origin, X-Csrftoken, Content-Type, Accept"  // この辺は使うフレームワークにより異なるが許可するヘッダーを定義しておく。
```

こちらからcodepenもIFTTTのサーバーをさわれないので解決できない。。


Chromeの拡張機能を使う
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737363/dc0e824f-6a9d-2a14-5fe6-d2525028d280.png)

入れてみたが変化なし。。




#参考
[なんとなく CORS がわかる...はもう終わりにする。]
(https://qiita.com/att55/items/2154a8aad8bf1409db2b)
[CORSについてざっくり理解する]
(https://qiita.com/eshow/items/e47e7214dc12f130f456)
[CORS関連、これだけ知っとけばまぁ大丈夫]
(https://qiita.com/rooooomania/items/4d0f6275372f413765de#cors%E3%81%A8%E3%83%A6%E3%83%BC%E3%82%B6%E8%AA%8D%E8%A8%BC%E6%83%85%E5%A0%B1)
[【CORS】クロスドメインによるエラーを解決する]
(https://developer.yukimonkey.com/article/20200227/)
DockerでNode.js環境構築 

# 概要
`Dockerfile`と`Docker-compose`を使って簡単にnode.jsの環境を作る。

# ディレクトリ構造
```
node_app
├─ Dockerfile
└─ docker-compose.yml
```

### Dockerfile の記載
```Dockerfile:Dockerfile
#イメージを指定
FROM node:12.4.0-alpine

# 環境変数を定義
ENV NODE_ENV=development

# 雛形パッケージのインストール
RUN npm install -g express-generator

# 初期カレントディレクトリ
WORKDIR /app

# 開放ポート
EXPOSE 3000
```
### docker-compose.yml の記載
```javascript:docker-compose.yml
version: '3.7'
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: node
    volumes:
      - ./app:/app
    ports:
      - "8080:3000"
```
`context` ディレクトリの指定 「 . 」 で同じディレクトリ

`container_name`　コンテナネームの指定

`volumes`　データーのマウント共有の指定 左がホスト 右がコンテナ

`ports`　コンテナとホストのポートをつなげる指定

#### Dockerfile からイメージのビルド
```
$ docker-compose build
```
#### コンテナを生成、起動

-d （デタッチ オプション）でバックグラウンドで起動

```
$ docker-compose up -d
```

#### コンテナに接続

-i（--interactive）標準入力を設定させ、コマンドを対話的にする
-t（--tty）疑似ターミナルの割り当て

```
$ docker exec -it [CONTAINER_ID] bash
```
#### express-generator で雛形の作成

今回はテンプレートエンジンはejsを使用

```
# express --view=ejs /app
```
#### パッケージのインストール

```
# npm install
```

#### サーバーを起動

```
# npm start
```
local hostに接続して Welcome to Express! と表示されれば完成です。

# 参考本
[Docker ドキュメント](https://docs.docker.jp/)
Docker/Kubernetes 実践コンテナ開発入門
pythonで文字列をdatetime型に変換する際のtimezone指定

# datetimeとtimezone
文字列からdatetime型にし、それをelasticsearchに登録するときに、
timezoneを指定しないとおかしなことになりました。
その際に行ったことの```備忘録```です。もっと楽なやり方あったら教えていただきたいです。

## python3.7より前の場合
- 文字列のフォーマット: ```YYYY-mm-dd HH:MM:SS+0900```
    - ```+0900```を末尾に追加　(UTC+9時間なので　)
- strptime()のフォーマット: ```%Y-%m-%d %H:%M:%S%z```
  - ```%z```がtimezoneに対応している

```python:例
from datetime import datetime as dt

# 文字列
time_str = "2020-11-22 18:00:00+0900"
# timezoneを含んだdatetime型
time_dt = dt.strptime(time_str, '%Y-%m-%d %H:%M:%S%z')

print(time_dt.tzinfo)
# UTC+09:00
```


## ptyhon3.7の場合

```fromisoformat()``` を利用できます。
ISO 8601形式では末尾に```+HH:MM```または```-HH:MM```の形式でタイムゾーン情報を含んでいるので、
それに準ずる形の文字列を引数とします。

```python:
# 文字列
time_str = '2020-09-12 12:22:30+09:00'
# timezoneを含んだdatetime型
time_dt = dt.fromisoformat(time_str)

print(time_dt.tzinfo)
# UTC+09:00
```

# 参考サイト

[Python, datetime, pytzでタイムゾーンを設定・取得・変換・削除 \| note.nkmk.me](https://note.nkmk.me/python-datetime-pytz-timezone/)
TypeScript 配列でundefinedとnullを消す方法

TS使っていて配列をfilterかけて取り出すとundefinedを考慮してくれた。  
しかし、undefinedの消し方がわからず苦戦しました。

## 結論

```ts
  function notEmpty<TValue>(value: TValue | null | undefined): value is TValue {
    return value !== null && value !== undefined
  }
  const result = Data.filter(notEmpty)
```

## NGな方法

```ts
const result = Data.filter(item => item !== undefined)
```
javascript 基礎まとめ②


①の基礎まとめの続き

１．関数
Rubyでいう「メソッド」はJavaScriptでいう「関数」
Ruby だと

```ruby
def　メソッド名
　処理内容
end
```

これがJavaScriptだと

```javascript
function 関数名 （引数） ｛
　処理内容
}
```

例

```javascript

function hello (){
  console.log("こんにちは！")
}

hello()

```
になる。

ちなみに、↑の書き方は「関数宣言」、↓が「関数式」


```javascript
hello =function (){
  console.log("こんにちは！")
}

hello()
/// こんにちは！と表示される///

```
この２つは読み込む順番が違う。関数式は

```javascript
hello()
hello =function (){
  console.log("こんにちは！")
}
///エラーになる///
```

としても先にhello = function()、、、の方を読み込まれるので
この書き方はエラーになる。

ちなみにこの

```javascript
function (){
```
は無名関数と言われている。

さらに

```javascript

function (){}
```
 は

```javascript
() =>{}
```

と書換が可能



書いていくうちに頭は整理されるけど、見返すと

説明わかりづらすぎる。。。

※間違い等あればいただきたいです！
RubyでLINEのアサーション署名キーからチャネルアクセストークンv2.1(JWT)を発行する

## 概要
RubyでLINEのMessaging APIで使用するチャネルアクセストークンv2.1(JWT)を発行します。
公式リファレンスは以下。
https://developers.line.biz/ja/docs/messaging-api/generate-json-web-token/
### 前提
* アサーション署名キーを発行済みであること（未発行の場合は、上記公式リファレンスを参考に発行すること）
* JWTそのものの概要説明は省きます

## 背景
アサーション署名キーからJWTの発行に手間取ったので、残しておきます。
・公式リファレンスの実装例がJavaScriptしかない
・ググっても有用な情報が出てこなかった

（Ruby初学者のため、間違いがありましたらコメントいただけると助かります。）

## 実装
### gemのインストール 
 
```rb
# Gemfile
gem 'json-jwt'  
```

`bundle install`

### JWT発行
```rb
require 'time'
require 'json/jwt'

# アサーション署名キーのJSONをパース
jwk = JSON.parse(Constants::LINE_ASSERTION_SIGN_KEY)　

# ペイロードとヘッダーを定義(LINE公式リファレンスより)
payload = { iss: Constants::LINE_CHANNEL_ID,
            sub: Constants::LINE_CHANNEL_ID,
            aud: "https://api.line.me/",
            exp: Time.now.to_i + 60 * 30,
            token_exp: 60 * 60 * 24 * 30 }
header = { alg: "RS256",typ: "JWT",kid: jwk["kid"] }

# 未署名のJWTを作成
jwt_nonsig = JSON::JWT.new(payload)
jwt_nonsig.header = header

# アサーション署名キーから秘密鍵を生成
private_key = JSON::JWK.new(jwk).to_key

# 署名付きのJWTを生成
jwt = jwt_nonsig.sign(private_key).to_s

######## あとは公式リファレンスに沿って、APIにPOST
```

### POST例

```
curl -v -X POST https://api.line.me/oauth2/v2.1/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer' \
--data-urlencode '{発行したjwt}'
```

## はまったところ
* そもそもアサーション署名キーの形式が謎  
pem形式のような文字列がザーっと書いてある秘密鍵は見たことがあったが、アサーション署名キーのようなjson形式は初見で、JWTのgemでそのままの形式では使用できなかった。  
⇒JWK Set 形式と呼ぶらしい。
https://qiita.com/trysmr/items/e8d4225ff6a603e9e21a  


* `gem jwk`を使用するとエラーになる
`gem jwk`もJWTのライブラリとして有名どころらしく、最初はこちらを使用したが、以下のエラーが発生した。
https://github.com/jwt/ruby-jwt

```
# railsコンソール
$ JWT::JWK.import(jwk)
JWT::JWKError: Key type (kty) not provided
```

"kty"には"RSA"が入っている。ソースを見たらktyに何かしら値が入っていればいいみたい(推測)で、読み込めていないのかな（推測）と思い、jwkのハッシュをシンボル化して、再度実行。

```
# railsコンソール
$ JWT::JWK.import(jwk.symbolize)
NoMethodError: undefined method `tr' for nil:NilClass
Did you mean?  try
from /usr/local/lib/ruby/2.6.0/base64.rb:101:in `urlsafe_decode64'
```
学習不足です。あきらめました・・・
(JWT::JWKでbase64インスタントに対してtrメソッドが呼ばれているところまでは見れましたが、これ以上は時間がかかりそうなのでギブアップ)
【備忘録】AWSでCakePHP4の環境構築



# 1. EC2のインスタンス作成 ~ PHP7.4インストール
「EC2立ち上げ→Apacheインストール→PHP7.4インストール」の流れはこちらの記事を参考にしました。
[AWS（EC2）を立ち上げてPHPを動かす話](https://qiita.com/daichi_sugiyama/items/623218b6e9173d5e7ed7)

# 2. PHP拡張モジュールのインストール
```shell
$ sudo yum -y --enablerepo=remi-php74 install php74-php-intl php74-php-mbstring php74-php-pdo
```
apache再起動

```shell
$ sudo systemctl restart httpd
```

# 3. composerのインストール


```shell
$ curl -sS https://getcomposer.org/installer | sudo php
$ sudo mv composer.phar /usr/local/bin/composer
$ sudo ln -s /usr/local/bin/composer /usr/bin/composer
```
# 4. cakephp4のインストール
phpのバージョンが7.2以上ならcakephpのバージョンを指定しなくても自動的にversion4がインストールされると思います。

```shell
$ composer create-project --prefer-dist cakephp/app my-app
```

# 5. Apacheの設定
vimでapacheの設定ファイルを開きます。

```conf
$ sudo vim /etc/httpd/conf/httpd.conf 
```

DocumentRootを"/var/www/html"から先ほどインストールしたcakephpディレクトリのwebrootに変更します。
.htaccessでの設定の上書きを許可するよう、AllowOverride を All に設定します。

```conf
DocumentRoot "/var/www/html/my-app/webroot"

<Directory "/var/www/html">
    Options FollowSymLinks
    AllowOverride All
</Directory>
```
その後、apacheを再起動

# 6. 完了
ブラウザで次のように表示されればインストール完了です。
![スクリーンショット 2020-11-24 13.54.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617482/4b783ff6-077f-6384-5444-b0cea95cea26.png)
Slackに業務改善案を投稿して、Integromatを使ってGoogle Spread Sheetに反映

#目安箱を、、作りたかった、、

今、テレワークでなかなか顔を合わさないチームメンバーと業務改善をしたいが、
MTGの時間だけではなかなか解決しにくくなってきた。
そこで、__業務改善を提案できる箱（機会）__を用意して、
思いついたときに先に入れておけば、都度皆が見て__考えておいて__、
MTGの場では__棚卸__と__ディスカッション__を始められる。

というスキームに変えようと思ったのがきっかけです。

#Slack→IntegromatのWebhook→SpreadSheetの構成
実際は、SlackのOut Going Webhook　→　IntegromatのWebhook　→　SpreadSheet

<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737206/3b47b720-54a7-ba81-b12f-d70403f540c6.png">

#やった方法
１．IntegromatのWebhookURLを出す　https://hook・・の部分
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737206/de64ac88-65ca-8bf0-a87d-3a7a8ebee354.png)

２．Slackの 「Out Going Webhook」に取得対象のチャネル・トリガー・URLを設定
　今回の場合、提案を頭につけて投稿してもらうようにしました。
　「提案」をトリガーにして収集
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737206/f49d18d8-917c-0918-136f-7fc7c325fac6.png">

３．IntegromatのSpreadSheetコネクタで、認証して書き込み先のファイル・シート・書き込み内容を指定する

結構簡単にできました。
投稿内容の加工が必要であれば、一度GASを挟むとかするのかな。
簡単な加工であればIntegromat側で出来そう。

###IntegromatにSlackのコネクタはありますが、ClientIdとか取得する必要があって、面倒だったのでやめました。


#参考にさせていただいたサイト
https://qiita.com/allein-s/items/3653fb94ab335ab6fe52
ゲストログイン機能でたまに、パスワードエラーが出ていたが、解決した

#はじめに
　先日、ゲストログイン機能を実装したが、数回に一度エラーが出てしまうことがわかり、原因を追求した。

##ゲストログイン機能実装について

　私が以前投稿した、「ゲストログイン機能の実装方法」は[こちら](https://qiita.com/TerToEer_sho/items/07f6e9c0dd10964783a3)

##エラーの原因
パスワードについてのエラーで、バリデーションによって弾かれていたので、この部分と予測。

```ruby
user.password = SecureRandom.urlsafe_base64
```
`SecureRandom.urlsafe_base64`について調べて見ると、
ランダムに、大文字英字、小文字英字、数字、<font color=red>記号の「-(ハイフン)」「_(アンダースコア)」</font>を生成していることに気づいた。
そう、この記号はパスワードのバリデーションに含めていない！だから、時々、記号が含まれるパスワードがゲストログインの際に生成され、エラーとなっていた。

##解決方法

シンプルに、

```ruby
user.password = '1234abcd'
```
と、こちらでパスワードを指定すれば、簡単に解決はできるが…
セキュリティがガバガバになってしまう。

ランダムにパスワードを生成してくれることは諦めたくない。
さらにググると、
同じようにランダム生成の`SecureRandom`を使った上で、別の生成内容を見つけた。

```ruby
user.password = SecureRandom.alphanumeric
```
これだ！！

`SecureRandom.alphanumeric`は、英数字62種類の中から、ランダムに生成してくれる。
つまり、大文字英字（26種）、小文字英字(26種)、数字(10種)。
さらに、引数で文字列のサイズを指定できる。

```ruby
user.password = SecureRandom.alphanumeric(6)
#=>'Ajge4l'
```
引数に指定すれば、バリデーションで字数に制限をかけていた場合でも、エラーにならない！！
CDKでクロスアカウントアクセス可能なS3バケットを作成しようとしてハマった

# この記事の内容はあまり参考になりません…(2021-01-31 追記)
- `new Bucket(...)` でS3バケットが毎度作成されると勘違いしていたせいで、変な記事を書いてしまいました…
- 普通に「`new Bucket(...)` で作成したS3バケットに `addToResourcePolicy` でバケットポリシーを付与したり、その他変更を行っていく」ようにすれば、cdkで毎度差分をチェックしてくれるので、S3バケットが毎度作成されることもなく、普通に権限の更新ができました…
- よって、下記はベストプラクティスにはなり得ないと思います。が、戒めのために残しておきます…
 

# ハマった流れ

1. CDKでのインフラ構成に初挑戦
2. S3バケットを作成、別のAWSアカウントからの書き込みを許可したい
3. できたと思ったら、別アカウントからアクセスできない事態が発生
    * ブロックパブリックアクセスをオフにした→アクセスできたが、セキュリティ面に不安
    * [公式doc](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/access-control-block-public-access.html)見る限り、ブロックパブリックアクセスをオンにしたままクロスアカウントアクセス許可できるはず…
4. バケット作成した→できた。
5. そのS3バケットを `Bucket.fromBucketName(...)` で利用し、別サービスをデプロイしたら、クロスアカウントアクセスがまた不可に。
    * 見ると、バケットポリシーが消えている
6. `addToResourcePolicy`で再付与 → できない
7. 混乱

# ブロックパブリックアクセスをオンにしたままクロスアカウントアクセス許可する方法
* ブロックパブリックアクセスについては、全てオンでOK（CDKだと `BlockPublicAccess.BLOCK_ALL` は設定してOK）
* バケットポリシーでクロスアカウントアクセスを許可するのだが、公式ドキュメントの[「パブリック」の意味](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/access-control-block-public-access.html)にしたがって、「非パブリックのポリシー」とみなされる書き方をしないと、ブロックパブリックアクセスが優先されてアクセス不可になるので注意。

というわけで今回はここの問題ではなかった。

# CDKによるS3バケットの作成と利用について
* `new Bucket(...)` で作成したS3バケットに `addToResourcePolicy` でバケットポリシーを付与可能
* `Bucket.fromBucketName(...)` で呼び出したS3バケットには **`addToResourcePolicy`等の操作ができないが、2020/11/24現在エラーは出ない**。（[対応するGitHubのissue](https://github.com/aws/aws-cdk/issues/6548)）
    * 別スタックやCloudFormationで管理されていないリソースの改変を防ぐための仕様、とのこと

そのため、**既存のS3バケットを利用する際、同じStackで作成済みのS3バケットを利用するとクロスアカウントアクセス可能なバケットポリシー設定がうまくいかない**。

* `Bucket.fromBucketName(...)` で呼び出したS3バケットに `addToResourcePolicy` で権限設定
    *  不可。`.fromBucketName(...)`で呼び出すと`addToResourcePolicy`が使えない。
* `new Bucket(...)` でバケット作成した時点で `addToResourcePolicy` で権限設定済みのバケットを `Bucket.fromBucketName(...)` で呼び出す。
    *  できそうだが不可。**同じスタック内でバケットポリシーを付与していて、その記述がコードから消えるので、スタックで管理されているバケットポリシーが消される。**

# じゃあどうするか？
* S3バケットのスタックだけ分ける
    * アリだが、そもそも分けたスタック内でのアップデートがうまく行かないならCDKでやるメリットってないような。
* S3は手動で管理する
    * 一旦、これに落ち着きました。
    * CDKによる柔軟なアップデートとS3のデータストレージとしての活用方法（消さないし、作り変えたりも頻繁ではない）が、そこまでマッチしないかな、と。
    * もし良い活用方法を知っている方が居たら教えてほしいです。