OmniAICreator/Qiita-1.07M huggingface dataset:
【HTML&CSS】CSSのtext-overflow:ellipsisを使って、文章の途中を・・・で丸めたい

# はじめに
Webページの見た目を作っているときに、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605286/d22f3858-1116-5f76-327f-6754abcdf03f.png)

こうではなく

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605286/dd644f00-5203-dc70-9408-6b32b71b5e83.png)
こういう見た目にしてほしいと言われ、15分くらい頭を抱えてしまいました。

「css 3点リーダ 丸める」とかで検索すると、上のような画像の作り方は山ほどヒットするのに、下の画像のような肝心のやりたいことの方法が全然見つかりません。

自分の検索力の向上は一旦傍に置いて、とりあえず試行錯誤した結果できたのが、今回の実装方法です。

# 実装方法
完成品はこちらに載せておきます。
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="mdLewjE" data-user="min9000" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/min9000/pen/mdLewjE">
  Untitled</a> by mimori kyoma (<a href="https://codepen.io/min9000">@min9000</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>


## 簡単に説明
```html
<div>
　　<p>
  <span>入門編入門編入門編入門編入門編入門編入門編入門編入門編入門編入門編入門編入門編入門編入門編入門編入門編入門編入門編入門編入門編入門編入門編入門編入門編入門編入門編入門編入門編入門編</span>
  を削除しました
　　</p>
</div>
```
このHTML、何もマークアップしなければ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605286/97cda3b6-b93f-3226-40d1-76456929b3a9.png)
こうなります。長い。
そこで、cssをこんなふうに書きます。

```css
span {
  display: inline-block;
  max-width: calc(100vw - 140px);

  /* 1行で丸めるマークアップ */
  text-overflow: ellipsis;
  overflow: hidden;
  white-space: nowrap;
}
```

丸まりました。

上記のコードは、```span```タグを```inline-block```にして幅を調節できるように、でも改行しないようにした後、最大幅を指定してやり、仕上げに3点リーダを指定しています。
```-140px```は、「を削除しました」の文字部分の長さです。


ただ、上下の余白が気になるので、もう少しCSSを追加します。
```css
span {
  display: inline-block;
  max-width: calc(100vw - 140px);
  /* 1行で丸めるマークアップ */
  text-overflow: ellipsis;
  overflow: hidden;
  white-space: nowrap;

  /* 新しく追加 */
  vertical-align: middle;
  padding-bottom: 3px
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605286/490488c0-08a7-ce95-3755-c9d8308e88e0.png)
綺麗になりました。
ちなみに```padding-bottom```は、高さ調整と、```y```や```g```などの下にはみ出るアルファベットが切れないような調整を兼ねています。
この数字だけは、アナログで様子を見ながら設定しています。。。



## 注意点
```padding-bottom```のアナログな設定がたたって、ブラウザによっては（主にsafariとモバイルのブラウザ）、違う挙動をする可能性があるので、各種ブラウザ確認は必須です。

# 終わりに
どうも小手先でこちょこちょしているだけ感がありしっくりこないので、他にも方法があるんだろうなと思ったりしています。
ただ、いったんは注文通りの見た目が作れたので良かったです。
検索力の向上は追々考えたいと思います。
コールバック関数

コールバック関数とは....他の関数に引数としてワタさえる関数

```javascript

関数①
function hello(){     
    console.log('hello')
}

関数②
function fn(cb) {
    cb();
}

fn(hello);  //hello

1. fn(hello); が実行されることで...
2. 関数②のfn（cb）にhelloが渡され
3.　　関数②の内部のcb();が実行され
4.　「　hello　」　となる




```
TypeScriptでより安全に文字列(string)を数値(number)にパースする

# はじめに
以前に書いた[TypeScript(JavaScript)におけるnullとundefinedそしてfalsy値](https://qiita.com/yoshitama/items/643a86f2cae744f43658)の例文で使用されている例文にて、
文字列型から数値型に変換をする際に一部のパターンで厳密にパースできないので、
今回はより安全にパースをする方法を考えてみる。

# 文字列から数値型へのパース

### parseInt() 
文字列を整数の数値型に変換する。
parseIntは文字列型でないものも前から変換できるところまでパースしてしまう。

```ts
console.log(parseInt('12.3hoge')); // 12
```

### parseFloat()
文字列を少数として数値型に変換する。
parseIntとおなじく先頭から変換できるところまで変換する。

```ts
console.log(parseFloat('12.3hoge')); // 12.3
```

### Number()
文字列を数値型に変換する。
変換できない文字が入っていると`NaN`になる。
`null`を渡すと`0`になる。
```ts
console.log(Number('12.3hoge')); // NaN
console.log(Number(12.3));       // 12.3
```

# パースをするならNumber()がいいのか？
`parseInt()`や`parseFloat()`は数字じゃない文字列も変換してしまうので、
数字じゃない文字がきたらNaNを出してもらいたい。
そのような時は`Number()`がオススメです。
```ts
console.log(parseInt('12.3hoge')); // 12
console.log(parseFloat('12.3hoge')); // 12.3
console.log(Number('12.3hoge')); // NaN
```
しかし`Number()`に`null`を渡すと、`0`が返ってきてしまいます。
```ts
console.log(Number(null)); // 0
```

# より安全に文字列を数値にパースしたい
なので新しいパース関数を用意しました。
最小構成ですが、やりたいことに合わせて加工して使用していただければと思います。

:::note
0文字も`NaN`にしたい場合は、コメントを外してください。
:::
```ts
const parseNumber = (str: string): number => {
  if (typeof str !== 'string') return NaN;
  // if (str.length === 0) return NaN; // 0文字の場合もNaNにしたい場合
  return Number(str);
};
```

# ESlintで従来のパースを禁止する
ESlintで`parseInt()`と`parseFloat()`は無効化できるのですが、
`Number()`の無効化は難しかったです。誰か良い方法があればおしえてください！
```eslintrc.yml
rules: {
  no-restricted-globals: [
    "error",
    {
      "name": "parseInt",
      "message": "Use parseNumber"
    },
    {
      "name": "parseFloat",
      "message": "Use parseNumber"
    },
  ]
}
```

# さいごに
`Number()`が`null`を入れると`0`になってしまうのはbooleanコンテキストが影響しています。
間違いや良い方法などありましたら、コメントなどで教えていただけると幸いです。
SlackをGoogleスプレッドシートにバックアップ

# はじめに

改悪後のアップロードになっちゃってごめんなさい(´；ω；`)ｳｩｩ

## きっかけ

読み飛ばしてどうぞ

Slackが9/1から改悪されたときに，こんな記事を見つけた！

https://qiita.com/Tyamamoto1007/items/bddff523e86016696ad2

お，最高！と思って読んでみたが，必要となるコードの情報はどこにもなく，隅まで読んでみると，個人blogから問い合わせをしろと書いてあった．

面倒だと思いながら問い合わせをすると，ツール利用が5,000円と来た


```txt
Yuto様

Autoworkerの山本と申します。
ご連絡いただきありがとうございました。

大変申し訳ございません。
記事で紹介しているSlackメッセージ保存・退避ツールは現在、
有償(5000円)でのご提供をさせていただいております。

月額等は発生せず、1回お支払いいただくと以後利用可能です。
※第三者へのツール(ソースコード)の再配布は禁止しておりますが、
個人並びに所属する組織内での複製利用いただくことは問題ございません。

お手数をおかけしますが、ご検討のほどお願いいいたします。

ご利用希望の場合、ご連絡いただけましたら販売ページをご案内いたします。
決済いただくと、ご提示いただいたGoogleアカウントにツールとなる
スプレッドシートの権限を付与させていただきます。

よろしくお願いいたします。
```

それなら…と思いネットに頼ると[こんな記事][base]を見つけた．

https://best-cloud.jp/slack-message-log-auto-save-gas/

これだよこれこれ！と思いながら使ってみるが，エラー
確かこんなやつ

```bash
thread_not_found
```

そんな訳で原因調査をして，何とか使えるようにした…という事の顛末．

# 実装

## マイナーチェンジによるアクセス権限の追加

Slack App のUser Token Scopes に以下の3つを追加してください．

- groups:history
- groups:read
- team:read

## GASのコード

コピペのコードを[これ][mine]に代えればOK！
もちろん[元の記事][base]通り，API-TokenとかFOLDER_IDとかは書き換えてください．

https://github.com/Yuto-34/SlackLog/blob/master/dist/slackLog.js

# もうちょい詳しく

折角なので何をしたかもメモをしておく

## エラーの原因

結論から言うと，Googleスプレッドシートに収まる文字列の上限をtsが超えた為．
これだけだと簡単そうに聞こえるけど，見つけるのには実に7hかかった．

tsとはThread Time Seconds の事．恐らくメッセージの送信時間を秒に直したもの．

昨年の段階ではスプレッドシートに入れる事の出来る有効数字を超えていなかったようだが，今現在は桁が1つ変わり，そのせいでエラーが出るようになった模様．

## 対処

tsを今までは1セルに記入して，その値を呼び出して使っていたが，tsの整数部分を入れる列と小数部分を入れる列を作り，分けて記入．使う時はその2つを足して使うという方法．


```js:改変前
const COL_TIME = 6; // 差分取得用に使用するタイムスタンプ
// 中略
ts = sheet.getRange(i, COL_TIME).getValue();
```

```js:改変後
const COL_TIME_INT = 6; // 差分取得用に使用するタイムスタンプ
const COL_TIME_DOUBLE = 7; // 差分取得用に使用するタイムスタンプ
// 中略
ts_int = sheet.getRange(i, COL_TIME_INT).getValue();
ts_double = sheet.getRange(i, COL_TIME_DOUBLE).getValue();
ts = ts_int + ts_double;
```

# おわりに

気が向いたら追記します．
コメント色々下さい．

# 参考サイト

https://best-cloud.jp/slack-message-log-auto-save-gas/

[base]: https://best-cloud.jp/slack-message-log-auto-save-gas/
[gomi]: https://qiita.com/Tyamamoto1007/items/bddff523e86016696ad2
[mine]: https://github.com/Yuto-34/SlackLog/blob/master/dist/slackLog.js
Azure Active Directory B2Cを使いこなそう 第5回「パスワードレス認証」

# 連載予定

1. [Azure Active Directory B2Cの特徴と使いどころ](https://qiita.com/yyukawa/items/78a52275b9dc9c37cd9e)
1. [基本的な使い方（標準ユーザーフロー）を知る](https://qiita.com/yyukawa/items/5996fd6558a9f5fa8631) 
1. [基本的な概念（テナント、アプリケーション、ユーザー）を理解する](https://qiita.com/yyukawa/items/da96b4666ace678db167)
1. [ユーザーフローの日本語化](https://qiita.com/yyukawa/items/f5a6960f5e457b5cc262)
1. [パスワードレス認証](https://qiita.com/yyukawa/items/7c96f9599630420f37c3) ★
1. 条件付きアクセスを使って環境に応じた認証を要求する
1. 柔軟な認証フローを実現する：カスタムポリシー

# IDプロバイダー

IDプロバイダーはユーザーのID情報を保存および検証するサービスです。Azure AD B2Cはデフォルトで多数のIDプロバイダーに対応しています。

## ローカルアカウント

これまで利用してきたIDプロバイダーがローカルアカウントです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16609/9f6f8aaf-119c-9c72-00d8-f47bcb19ebd5.png)

EmailやUsernameを選択した場合は通常のパスワード認証ですが、Phoneはパスワードレス認証になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16609/a64fc331-fdb7-2971-38f3-c7cbde4710c6.png)

### Phoneを試す

Phoneを利用する場合はユーザーIDが電話番号として登録されている必要があります。サインイン方法でユーザ名を選択し、「+81電話番号」をユーザ名の値として登録します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16609/89f30017-627a-74dd-aaab-9e91739acf72.png)

ユーザーフローで電話番号を使うように設定します。以前作成したB2C_1_signinを選びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16609/5967e9f7-a8e8-0041-2a89-489182136080.png)

IDプロバイダーを選びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16609/710ea6c0-0734-1e7f-7b0f-cedd09642bc8.png)

Phone signinを選びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16609/7f327cfd-e936-1027-aec8-dad44984a340.png)

ユーザーフローを実行します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16609/2f428eed-c69c-1516-4845-bcf0119fba0a.png)

先ほど登録した国コード付きの電話番号を入力します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16609/50970ca9-2315-0917-046f-2bb886b16df3.png)

国コードと電話番号が分離された画面になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16609/853d2407-d01b-c402-55e4-44e714b88a7a.png)

SMSが届くので認証コードを入力しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16609/298b85ba-423e-91b4-b4e1-d39c01372295.png)

JWTが払い出されました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16609/0b770603-88a0-b9ea-2275-f53c6aff9e3a.png)
Amazon RDSとは

# Amazon RDSとは
**Amazon Relational Database Service**(Amazon RDS)は、リレーショナルデータベースの６種類の製品をクラウド上で静的な動作条件で利用できるサービス。
Amazon Auroraに加えて、PostgreSQL、MySQL、MariaDB、Oracle Database、SQL Serverに対応し、メモリ、パフォーマンス、I/Dなどが最適化されたデーターベースインスタンスとして提供されている。
RDSは**マネージドサービス**で、アップデートなどが自動で行える。そのため、バックアップなどの面倒なタスクも自動化されていて、管理者が行う必要がない。
AWS Database Migration Service（DMS）を使用すれば、既存のデータベースからの移行や複製ができる。
# インスタンスクラス
RDSはEC2と同様にインスタンス形式で使用する。
インスタンスクラスは、スタンダード・メモリ最適化・バースト可能の３種類があり、クラスによってサイズが用意されている。
※インスタンスクラスによっては、サポートしていないDBMSがある。
データベースインスタンスはVPC上に置く必要がある。
**主なインスタンスクラス**
|用途|インスタンスクラス|内容|
|-|-|-|
|スタンダード|db.m5など|汎用的なインスタンスクラス|
|メモリ最適化|db.x1e|メモリ消費の高いアプリケーション用に最適化されたインスタンスクラス。一部リージョンでのみ使用可能。|
|メモリ最適化|db.x1|メモリ消費に高いアプリケーション用に最適化されたインスタンスクラス。最大値がdb.x1eの半分。|
|メモリ最適化|db.r5など|ネットワークとEBSのパフォーマンスを強化。AWS nitro System。|
|バースト可能|db.t3など|CPUの最大使用量までのバースト可能|

# RDSの料金
RDSの料金は、基本的にEC2と似ているが、RDSの特徴としてDBMSによってインスタンスの料金の違いがある。
**RDSの料金 = ストレージの料金 + データベースインスタンスの料金 + バックアップストレージの料金 + 通信量**
* ストレージ料金
確保しているストレージに対する料金。使用している寮でなく確保している量
* データベースインスタンスの料金（DBMSライセンス料を含む）
EC2インスタンスと同様に、時間当たりの稼働料金。手軽なものから高性能なものまであり、高性能であれば料金が高くなる。また、冗長性や利用するデータベースエンジン（DBMS）によって料金が異なる。
* バックアップストレージの料金
データベースインスタンスではバックアップをとることができる。このバックアップのことを**スナップショット**という。スナップショットはバックアップストレージに作成され、その容量に応じて料金がかかる。
* 通信料
データベースインスタンスがインターネットと通信する場合は、その転送量に応じて料金がかかる。同じVPC内のEC2など、同一のAZ内でしか通信しない場合は、料金は発生しない。
## マルチAZ構成
シングルAZ構成では、１つのAZに配置するのに対してマルチAZ構成では、複数のAZに配置して冗長性をとる。
# RDSのメリットとデメリット
RDSのメリットはマネージドサービスであること。アップデートが自動的に行われるので頻繁にメンテナンスする必要がない。手軽にデータベースを作成でき、オンプレミスからの移行もでき、特別なソフトウェアの調整をすることなく移行できる。
デメリットは、利用者側の自由にできないこと。提供されているDBMSには限りがあり、自動アップデートがデメリットに働くことがある。
## RDSの自動アップデート
RDSの自動アップデート（停止を伴うメンテナンス）は、いきなり行われると不便なので、利用者は以下の３つの選択肢がある。
* そのまま自動アップデートをさせる方法。
* マネジメントコンソールにログインして手動でアップデートしたり、更新時間を指定する方法。
* 更新を無視する方法（先送りにする）
マルコフ連鎖で文章を自動生成する【Python+MeCab】

# 目的
自動で何か喋ってくれるTwitterのbotを作りたい。
そのために、まずは自分のツイートをもとに新しい文章を生成する機能を作ってみる。

# 方法
文章の自動生成については、マルコフ連鎖というものを使ってみる。
元となるデータは自分の過去１か月のツイートとする。

# 開発環境
・Windows 10
・Python 3.10.7

# やったこと
１．Pythonのインストール
２．MeCabのインストール
３．Twitterからデータをダウンロード
４．ダウンロードしたデータを形態素解析して整形する
５．整形したデータを元にしてマルコフ連鎖を用いて文章を生成

## １．Pythonのインストール
以下の公式サイトから最新安定板をダウンロード
https://www.python.org/

このとき、通常のインストールだと何故か端末の個人フォルダの下にインストールされた。
そのため、一度アンインストールして再度、Python用のフォルダを指定してインストールし直した。

また、後の手順でpipを使用するのでここでpipにパスを通しておく。
pipはPythonインストールフォルダのScriptsフォルダ下にある。

## ２．MeCabのインストール
64bit版が以下からダウンロードできるようです。
https://github.com/ikegami-yukino/mecab/releases/tag/v0.996.2

文字コードはUTF-8を指定してインストール。

### ２－１．MeCabを試してみる
インストールできたら、実際にMeCabを使って形態素解析をやってみる。

```
C:\dev\python\markov>py
>>> import MeCab
>>> tagger = MeCab.Tagger('-Owakati')
>>> print(tagger.parse("本日はお日柄もよく"))
本日 は お 日柄 も よく
```

``Tagger``関数の引数にフォーマットを指定しています。
``-Owakati``は分かち書きを表します。

「本日はお日柄もよく」というテキストを単語ごとに分かち書きにして
「本日 は お 日柄 も よく」とスペース区切りで出力されています。

いい感じですね。

## ３．Twitterからデータをダウンロード
Twitterには過去のツイートの内容をCSVで出力する機能があります。
ツイッターアナリティクスから自分の直近１か月のツイートをダウンロードします。

しかし、このCSVにはツイートの本文以外にもURLやいいね数などの情報も一緒になっています。
そこから本文だけを抜き出して、ざっと内容を確認してみました。
``@``を使ったリプライなども混じっているようです。
文章を自動生成する場合に、勝手に他人にリプライを送るわけにはいかないので、``@``から始まる文字列を正規表現を使って空白にリプレース。
同様にリンクも変なURLにならないように消しました。


## ４．ダウンロードしたデータを形態素解析して整形する
しかしこれだけでも、自動生成に使うにはまだ足りません。
先ほどのMeCabを使って単語を分かち書きにします。

```splitted.py
import MeCab

input = open('./data.txt', 'r', encoding='utf-8') # 元となるツイートのデータ
output = open('./splitted.txt', 'w', encoding='utf-8') # ツイートを分かち書きしたもの

mecab = MeCab.Tagger("-Owakati")

for line in input.read().split('\n'):
	splittedLine = ' '.join(mecab.parse(line).split())
	output.write(splittedLine)
	output.write('\n')

input.close()
output.close()
```

注意した点としては、``open()``関数の引数``encoding``の存在です。
Pythonはインストールした環境の文字エンコーディングがデフォルトとなるそうで、僕はWindows環境にインストールしたので``cp932``になっているようです。
そのまま``open``しようとすると、``UnicodeDecodeError: 'cp932' codec can't decode・・・``とエラーが出ます。
そのため、``UTF-8``指定しているということです。

この``splitted.py``ファイルを実行することで、ツイートの本文データを自動生成に使えるフォーマットに整形することができました。

## ５．整形したデータを元にしてマルコフ連鎖を用いて文章を生成
文章の自動生成はマルコフ連鎖を用います。
Pythonにはこのマルコフ連鎖ライブラリ``markovify``というものがありますので、まずこれをインストールします。
```
pip install markovify
```

インストールが成功したら、実際に``markovify``を使ったコードを書きます。


```generate.py
import markovify

input = open('./splitted.txt', 'r', encoding='utf-8')
model = markovify.NewlineText(input.read())
sentence = model.make_sentence()

# 文章は分かち書きで出力されるのでスペースをトリミング
print(sentence.replace(" ",""))

input.close()
```

では、いよいよ``generate.py``を実行して文章を自動生成してみましょう。

```
C:\dev\python\markov>py generate.py
神奈川県で聖地巡りしたいもっともっとフィルタリングしたんだよ…
C:\dev\python\markov>py generate.py
誰か詳しい人の積みプラというものを愛せるようになるバッグ
C:\dev\python\markov>py generate.py
旅行の写真は粗が見えないかな
```

うん。いい感じですね。
文章はちょっとハチャメチャ感がありますが、お遊びで作るbotには逆に可愛げがあって良いでしょう。

これをベースにしておもしろいbotを作って遊びましょう。

## 参考資料
https://qiita.com/shge/items/fbfce6b54d2e0cc1b382

https://qiita.com/Haruka-Ogawa/items/c2116f0eb5c859955d63
OpensearchのGraviton2移行を簡単にはできなかったお話

移行スッとできるかなと思ってコケたので共有。

## Gravtion2に移行したい！
Qiitaでは記事検索機能などでOpensearchを使っています。
以前[エイヤっとアップデートした](https://qiita.com/WakameSun/items/4d46f99a03f2abdc9eba)後も、マイナーなアップデートやパッチはちまちまと上げていたのですが、Gravtion2への移行とgp3への移行がまだできていませんでした。
Gravtion2とgp3への移行は特に費用面で魅力(同じくらいのスペックでも1割~2割安くなる)であり、RIも合わせれば相当なコストダウンを期待することができます。

## じゃあ早速変更だ
善は急げということで、まずstaging環境で使っているOpensearchのインスタンスタイプをm4.large.searchからm6g.large.searchに変更しようとしました。
セレクトボックスから選んで変えるだけ・・・と思いきや変更ボタンを押しても画面遷移を行いません（また、エラーメッセージも出てません）。
ノードあたりのEBSストレージサイズの容量を変えるだけの変更可能だったので、インスタンスタイプの変更で何か問題がありそうということで調べることにしました。

![スクリーンショット 2022-09-07 12.45.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359721/580ff955-923f-295d-ee21-171947647047.png)

↑おしても変更が適用されないボタン君(上の方の設定は載せると怒られるやつなのでごめんなさい)

## それっぽい記述を見つける
公式のドキュメントを読み漁っていたところ、[こちらのドキュメント](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/resize-limitations.html)で気になる記述がありました。

>AMI はプロセッサのアーキテクチャに固有であるため、プロセッサアーキテクチャが現在のインスタンスタイプと同じインスタンスタイプを選択する必要があります。次に例を示します。
>現在のインスタンスタイプが、Arm アーキテクチャに基づくプロセッサである場合、対象となるインスタンスタイプは、Arm アーキテクチャベースのプロセッサ (C6g や M6g など) をサポートするものに制限されます

詰まるところ、m6gはArmアーキテクチャで現在使っているm4は非Armなので、互換性はないということだったんですね。
なので、今回の場合はGraviton2を体験するには新しくインスタンスを立ててデータを投入する必要があるということでした。

## 制約は他にも
今回Qiitaでは問題なかったのですが、Gravtion2に移行するためにはElasticsearchのバージョンが7.9以上である必要があるそうです([こちら参照](https://aws.amazon.com/jp/about-aws/whats-new/2021/11/amazon-opensearch-service-offers-m6g-instances-new-regions/))。
新しくインスタンスを立てる時はOpensearch1.3、Elasticsearch7.10、6.8からの選択になるので、もし今立ってるインスタンスのバージョンが違う場合は注意が必要です。
109.イテレーターとfor...of

# イテレーターとは
反復操作を行うための『オブジェクト』の総称のこと
ここでいう反復は繰り返しの処理のこと
## 例　反復操作を行う際に使用するオブジェクト
String
Array
Map
Set
arguments
などなど
## イテレーターの検索方法
#### 1.コンソールに配列名を入れます
Consoleタブ→>arryを入力
![スクリーンショット 2022-09-07 10.15.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/714631/07e1ca7f-e0d2-d43d-6fa5-a52f76684a6a.png)
#### 2.Prototypeをクリック
![スクリーンショット 2022-09-07 10.20.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/714631/9b0cd652-a451-467f-f0db-9726fcfe3a6a.png)
#### 3.プロトタイプのオブジェクトが表示される
![スクリーンショット 2022-09-07 10.25.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/714631/8c071c7c-e1a8-da85-800d-956a584d57da.png)
#### 4.Symbol配下のイレテータを参照
![スクリーンショット 2022-09-07 10.44.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/714631/9fdedab7-b0b5-9c01-e134-78ac651ab3ba.png)
＊そして、今回のfor ofは、ここにあるイテレーターを参照としている
店名から店コードの確認など、ちょっと確認したい時に活用できるGoogleスプレッドシートを使ってコード検索できるLINE Botの作成 

## はじめに
私は小売りに勤めていますが、初めてLINEBotを作ってみた記事を投稿します。

スタッフ的な仕事をしている関係で、データベースを使用する事が多いのですが、店舗名と店舗コードを覚えておく事が苦手な為、エクセルで店舗一覧としてデータベースを作成し、その都度、エクセルを開いて確認していました。
今回、LINEBotに出会い、毎回エクセルを開く手間を削減できると思い、Googleのスプレッドシートを利用して、店名を入れることで店コードを呼び出したり、店コードを入れることで店名を呼び出すLINEBotを作成しました。

## 使用ツール
・Make
・Googleスプレッドシート
・LINE

## Botの構造
構造はシンプルです。
エクセルのデータをGoogleのスプレッドシートに変換した後、Google Sheetsのモジュールにセットして、Toolsのモジュールを挟んで繋ぎます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2792549/a70ef7e1-1251-ecc5-b5eb-7903388562b3.png)

Googleのスプレッドシートをモジュールに読み込ませて、リクエストを設定します。
店コードなら店名、または店名なら店コードをフィルタリングしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2792549/341784c5-b9ee-a4d8-8f78-521a22aeacf0.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2792549/7c685bca-3c71-148c-d4df-4c98e8265eaf.png)


次に、Toolsでリクエストに対する応答を設定して完成です。
その際、存在しないリクエストに対する応答も設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2792549/56e249e2-c030-3d21-8bc8-b6c63d13f8b5.png)


LINEではこのように応答が返ります。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Googleスプレッドシートを使ってコード検索できるLINE Botの動画 <a href="https://t.co/qNnOasPIVA">pic.twitter.com/qNnOasPIVA</a></p>&mdash; 白川圭司 (@KTNZrtzqWVRmEjI) <a href="https://twitter.com/KTNZrtzqWVRmEjI/status/1567382317219606530?ref_src=twsrc%5Etfw">September 7, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## 今回の作成に至るまで
当初は、無料APIとJSONを使用して商品検索できるものを作成しようと試みましたが、何回やっても、最初のデータが返ってきてしまい、リクエスト通りの回答が作成されず、ネットを検索してもピンポイントで事象を特定できず、勉強にはなりましたが、今回は断念しました。
もっと力を付けてから再チャレンジするつもりです。

## 終わりに
初めてLINEBotを作成してみて、アプリを構築する技術が身近にあり、簡単に作れるようになっている事を知り、感動すると共に、年甲斐もなくワクワクしました。
まだまだ技術が伴っていない為、今回はシンプルなものでしたが、もっと色々なことを学んで、今回のBotもバージョンアップさせていきたいと考えております。

この拙い初心者の記事が誰かの参考になれば幸いです。
【Homebrew】Analyticsを無効にする方法

## はじめに
Homebrew は、Google Analytics を使用して匿名のユーザー行動集計分析を収集しています。
詳細については以下のページをご参照ください。

https://docs.brew.sh/Analytics

この機能を無効にする方法について記載します。

## 有効か確認する
Analytics が有効になっているかは以下のコマンドで確認できます。
`Analytics are enabled.`と出力された場合は有効です。

```zsh:ターミナル
$ brew analytics
Analytics are enabled.
UUID: XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
```

## 無効にする方法
方法は以下の2つがあります。

- [環境変数で設定する](https://qiita.com/P-man_Brown/items/ab22021c07277323eef7#%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0%E3%81%A7%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B)
- [コマンドで設定する](https://qiita.com/P-man_Brown/items/ab22021c07277323eef7#%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%A7%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B)

### 環境変数で設定する
環境変数でAnalyticsを無効にするためには`~/.zshrc`に以下を追記します。

```~/.zshrc
export HOMEBREW_NO_ANALYTICS=1
```

無効になったか確認します。
`Analytics are disabled.`と出力されれば無効になっています。

```zsh:ターミナル
$ source ~/.zshrc

$ brew analytics
Analytics are disabled.
```

### コマンドで設定する
ターミナルで以下のコマンドを実行することで無効にできます。

```zsh:ターミナル
brew analytics off
```

無効になったか確認します。
`Analytics are disabled.`と出力されれば無効になっています。

```zsh:ターミナル
$ brew analytics
Analytics are disabled.
```
非機能要求を投影したドメインモデル

最近、会社のVirtualプロジェクトにて宿泊予約管理システムのモデルを考えております。
そこで学んだことを書こうと思います。

このシステムで予約者アクターさんの使うユースケース「新規で宿泊予約をする」に注目します。

この【宿泊予約】てものに対する認識の曖昧さを定義せずにモデリングすると
結構分析の段階でもモデルが劇的に変わったりします。
　
なぜなら、
a. その宿泊予約とは、１回の予約で１部屋しか予約できないようなものを指すのか？
![スクリーンショット (72).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2781175/faa34efc-f894-3779-5319-71617fe6eb8a.png)


b. それとも１回の予約で複数の部屋を予約できるものなのか。
![スクリーンショット (71).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2781175/a7438f43-43e5-cbf6-9d72-29afed196ddc.png)


最低でもこの２通りあります。（モデルの図は見切れてます）
予約詳細や宿泊詳細といったものがあるかないかで結構違います。

「え？　そんなん１回の予約で複数の部屋を予約できるに決まってんじゃん」て？
実はそこが落とし穴であり、このモデリングの楽しいとこだったりします。
　


まずはa.なのかb.なのか曖昧さをきちんと定義せずにモデリングしたうえでの発見を聞いてください。

　
aの一回の予約で一部屋しか予約できない場合、
作るものはbよりもシンプルなため開発者目線では嬉しいけども、
予約者の目線では複数の部屋を予約したくても、その都度予約を何度もしないといけなくて面倒くさい。
すなわち品質特性の項目の使用性の低下につながります。
（非機能要求と ISO9126 | オブジェクトの広場 (ogis-ri.co.jp)を参照）
　
　
対してbのように、一回の予約で複数の部屋を予約して大人数を分けたりできる場合は、
予約者目線では上記のaの場合みたいに
複数の部屋を予約したくても、その都度予約を何度もしないといけない、みたいなストレスなく便利な反面、
予約詳細というaにはなかった新たな概念が必要になり、
モデルが先程よりも複雑になるから、開発者側からしたら嬉しくないてことになります。
すなわち品質特性の項目の保守性の低下につながる。

　

ここで重要なのはそもそものステークホルダーの予約に対する関心事でどれを優先させるのか、です。

ドメインモデリングをするときに経験やなんとなくだけでモデリングをするだけでは
そのモデルは価値のないものになってしまいます。
これではせっかくみんなで丹精込めてつくったモデルアートが台無しでかわいそう、、、。
だからステークホルダーの視点での議論を必ずしなくてはいけません。

　
このシステムを物語るうえで、何人ものステークホルダーが存在します。
ちょっと何人かあげてみましょう。

まずはシステムを使う予約者ユーザーさん。
あとは開発を依頼してきた依頼者さん。
開発を請け負うPMさん、開発者さん。(ここではPMさんも含めて開発側の人たち、ってくくりに入れてしまいましょうか)
その他にもフロントで予約内容のチェックをしたりする宿泊ホテルの従業員さんなど。

　
今回は簡単のために、ユーザーさんと開発者だけの２種類に厳選して話を進めてみます。

便利で見やすいツールだと個人的に思いますので、
今回はastahプラグインの匠methodのステークホルダーモデルを使用しました。
図の中の赤の付箋はは嬉しくないこと、青はこんなだったら嬉しいなと思うことです。

![スクリーンショット (73).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2781175/312724b5-f9ed-996e-8b72-9f321341a74a.png)


いかがでしょうか？？


いまは一回の予約で一部屋しか予約できなくてもいいとしても、
それは開発者にとっては【今この瞬間は】楽できてうれしいかもしれませんが、

「一回の予約で複数の部屋予約したい」とユーザーさんが思ったときに　
低い使用性を感じ　⇒　このシステム使わなくなる　⇒　開発者たちの信用に響きますね。

もしも仮に複数の部屋予約できるように仕様変更しようものなら、
アーキテクチャを大幅に変えないといけなそうですね。
これは保守性の低下につながり開発者泣かせです。

　

てことは、開発者たちは初回ちょっと大変な思いしてでも、
最初から予約者目線を優先した方が良さげだと、ステークホルダーモデルを通して論理的にわかりました。


結果、上記のaよりもbのドメインモデルの方がよいとわかりましたね。8888888


　
つまり何が言いたかったかっていうと、
ドメインモデルを通して
ユーザーにとっての嬉しいことの品質特性の使用性と、開発者にとっての嬉しいことの保守性は
ときに相反関係になり得ることをこのモデルを作っている中で学べたのです。


まさかそのトレードオフ分析を、分析フェーズのドメインモデル作る段階で考慮することで
劇的にモデルが洗練されるとは思ってませんでした。

　
複数のステークホルダーさんたちのどの要望を優先するか、トレードオフしたうえで、
できあがるドメインモデルを作り上げることで、
つくったモデルは価値のあるアートになりえます。
これって結構な感動じゃないですか？？

機能要求を満たすための実現手段だったドメインモデルに、
品質を考慮した論理性やひとの思いがのってるモデルなわけですから。


　
ということで、今後ドメインモデルを作る際には、
充分にステークホルダーモデルで、各ステークホルダーの関心事を整理したうえで
それをモデルに反映させていきたいと強く思いました。
Ghidraのショートカットをデスクトップに作成（Ubuntu）

Ubuntuにて、Ghidraのショートカット（ランチャー）をデスクトップに作成します。

# 状況

Ghidar関連のファイルたちは`/opt`配下にある状態です：

```
shoebill@pwner:~$ ls /opt/
ghidra_10.1.5_PUBLIC

shoebill@pwner:~$ ls /opt/ghidra_10.1.5_PUBLIC/
docs        Ghidra     ghidraRun.bat  LICENSE   server
Extensions  ghidraRun  GPL            licenses  support
```

# アイコン（ico）ファイルの入手

https://github.com/NationalSecurityAgency/ghidra/blob/master/Ghidra/RuntimeScripts/Windows/support/ghidra.ico

デフォルトではGhidraのicoファイルがなかったので上のリンクからicoファイルをダウンロードしました。

このicoファイルを`support`ディレクトリ配下（自分の場合は`/opt/ghidra_10.1.5_PUBLIC/support/`）に置きます。

# .desktopファイルの作成

（この.desktopファイルはデスクトップに作成しました。）

```:Ghidra.desktop
[Desktop Entry]
Categories=Application;Development;
Comment[en_US]=Ghidra Software Reverse Engineering Suite
Comment=Ghidra Software Reverse Engineering Suite
Exec=/opt/ghidra_10.1.5_PUBLIC/ghidraRun
GenericName[en_US]=Ghidra Software Reverse Engineering Suite
GenericName=Ghidra Software Reverse Engineering Suite
Icon=/opt/ghidra_10.1.5_PUBLIC/support/ghidra.ico
MimeType=
Name[en_US]=Ghidra
Name=Ghidra
Path=/opt/ghidra_10.1.5_PUBLIC
StartupNotify=false
Terminal=false
TerminalOptions=
Type=Application
Version=1.0
X-DBUS-ServiceName=
X-DBUS-StartupType=none
X-KDE-SubstituteUID=false
X-KDE-Username=
```

各自の環境に合わせてパスを設定する項目：

- Exec
- Icon
- Path

# 最後に実行権限を与える

アイコンを右クリックし、「Allow Launching」で実行する許可を与えます：

![allowlaunching.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2659122/371ded07-e493-b8fc-ac2e-2f5dfbe42194.png)

以上で、Ghidra起動用のショートカットアイコンが作成できました。

# アプリ一覧で表示されるようにする

`Ghidra.desktop`を`~/.local/share/applications/`または`/usr/share/applications/`配下に置くと、アプリボタンを押してアプリ一覧に表示されます。
アロー関数の書き方


アロー関数の書き方 
アローとは弓矢と言う意味

アロー関数とは...無名関数を記述しやすくした省略記法
() => {};


```javascript
// 1.関数
function a(name){
    return 'hello' + name;
}

// 2.関数式
const b = function(name){
    return 'hello' + name;
}

// 3.アロー関数の場合①−１
const b = (name) => {
    return 'hello' + name;
}


// 4-1.引数が１つの場合は「　（　）　」はいらない。
const b = name => {
    return 'hello' + name;
}

// アロー関数完成！！！！
// 5-1.定義の本文が１行の場合は、「 { } 」「 return 」 いらない
const b = name => 'hello' + name;

// 5-2.定義の本文が2行の場合は、「 { } 」「 return 」必要
const b = name => {
    'hello' + name;
}



// 関数の実行
console.log('Tom');  //hello Tom



// 6.引数が２つの場合
const b = (name, name2) => {
    return 'hello' + name + ' ' + name2;
}

console.log(Tom,Bob)  //hello Tom Bob

// 7.　ダミー変数作成の場合
const b = _ => 'hello';

     orばtr

const b = () => 'hello';

// 8. 
```

| | 無名関数 | アロー関数| 
|---|---|---|
| this      | ◯ | × |
| arguments | ◯ | × |
| new       | ◯ | × |
| prototype | ◯ | × |



「アロー関数」と「this」の違い

```javascript
name = 'John'

const person = {
    name:'Tom';
 // hello: () => {  アロー関数の場合
    hello: function() {
        console.log('Hello' + this.name);
    }
}


person.hello(); //Hello Tom 　　アロー関数にしていない場合
person.hello(); //Hello John アロー関数にした場合
```

thisは「this」、「arguments」を取らないので、スコープチェーンを使ってレキスカルスコープで「John」を見つける

オブジェクトのメソッドとして実行される場合
`this` => 呼び出し元のオブジェクト


関数（アロー関数）のメソッドとして実行される場合
`this` => グローバルオブジェクト
reactでkeyを指定したのにエラーが消えないときの回避策

## はじめに

みんな嫌いな↓のエラー。

```
Each child in a list should have a unique "key" prop.
```

「はいはいkey指定すれば良いんでしょ」と思ったのに、エラーが消えない❗
エラーが出るコードは↓。

```tsx
  {arrays.map((element) => (
    <>
      <div key={`${element}1`}>ほげ</div>
      <div key={`${element}2`}>ふが</div>
    </>
  ))}
```

## 結論

↓のようにすればよい。あ、 `Fragment` はちゃんと `import` してね。

```tsx
  {arrays.map((element) => (
    <Fragment key={element}>
      <div>ほげ</div>
      <div>ふが</div>
    </Fragment>
  ))}
```

## keyはループする要素の最上位の要素に指定しないと意味がない

[React公式doc](https://reactjs.org/docs/lists-and-keys.html)の↓の記述が該当するかな。

> Keys used within arrays should be unique among their siblings.

で、ここで疑問なのが最上位の要素...っぽい `<>` ってそもそもなんなの❓ってこと。

## `<>` ってなんぞや❓

これはShort Syntaxで、正式には `Fragments` というらしい。
[公式docはこちら](https://reactjs.org/docs/fragments.html)。

> A common pattern in React is for a component to return multiple elements.

CSS gridを使ってるときなど、複数の要素を `div` で囲まずにﾀﾞﾗｰっと並べたいときによく使う。

混乱しやすいのは、reactのツリー構造とreactが吐くHTMLのツリー構造が違うこと。
Reactでは

- Fragment1
    - div:ほげ
    - div:ふが
- Fragment2
    - div:ほげ
    - div:ふが

のようになっているが、HTMLは

- div:ほげ
- div:ふが
- div:ほげ
- div:ふが

のようになっている。
ここで、keyを指定すべき最上位要素とはreactのツリー構造で考えなくてはならない。
よって、 `div` 要素じゃなく　`Fragment` 要素に `key` を指定する必要があるってわけ。

なお、 `<>` 記法のときは `key` が指定できないとのこと。なぜ😭

> You can use `<>` `</>` the same way you’d use any other element except that it doesn’t support keys or attributes.
#05 UI Kit | Game Server Services(GS2)

https://www.youtube.com/watch?v=8-x809Kkc8Q

:::note alert
月内の画像のアップロード容量制限に引っかかってしまったため、後日画像をアップロードします
:::

# Session.05 UI Kit

第五回は GS2 の UI コンポーネントシステムである UI Kit を使用した UI 構築について解説します。

# UI Kit とは

UI Kit とは、ゲームエンジン上で UI を構築する工程を大幅に効率化するためのソリューションです。
これまで、クライアント開発者がサーバーからデータを取得して、UIに値を反映するプログラムをしながら開発を進めてきました。

UI Kit はそのアプローチとは異なり、ゲームエンジン上でテキストコンポーネントに対して GS2 上のどのデータを反映したいかを宣言的に定義することで、プログラムを一切記述することなく UI に値を表示することができます。

# UI Components

今回使用する UI Kit のコンポーネントを解説します。

## Fetcher

GS2から値を取得するコンポーネントです。
GameObject に対して、このコンポーネントを付与し、データの種類を定義して使用します。
子ノードに データを参照するようなコンポーネントを付与した場合、親階層に存在する Fetcher が取得したデータの値を使用して動作します。

## Label

親ノードの Fetcher で取得したデータの情報を表示するコンポーネントです。
format パラメーターで指定した形式で値を整形し、onUpdate コールバックが呼び出されます。
onUpdate コールバックにテキストコンポーネントを指定することで、テキストの値を更新することができます。

## Enabler

データの状況に応じてノードを有効化・無効化するコンポーネントです。
データの種類によって、有効化するために設定できる条件が異なります。
たとえば、GS2-Showcase の Display Item であれば 「データ読み込み中」「購入可能」「購入不可能」といった条件に基づき、表示・非表示を制御できます。

Unity の特性上、Enabler をつけたコンポーネントが非表示になると、表示判定処理が動かなくなるため、再度表示条件を満たしたとしても表示することができません。
Enabler は実際に操作するノードより親階層に配置するようにしましょう。

# Support Prefabs

UI Kit には単純なGS2のAPI呼び出しをプログラムを記述せずに実行できるようにするサポートプレハブが用意されています。

## Gs2ExchangeExchangeAction

GS2-Exchange で交換を実行するプレハブです。
交換を実行する Rate Model を指定することで、コンポーネントをアタッチした GameObject を有効化するだけで交換処理を実行できます。

これで UI Kit に関する学習は終了です。
それでは、ここからは実際に実装して UI Kit を利用してみましょう。

# Demo

それでは、実際にUnityで実装してみましょう。

## リソースの定義

まずは、今回使用する GS2 リソースを ScriptableObject で定義します。
これで、UI を組み立てる準備が整いました。

## ログイン

ログイン処理も UI Kit を使って実現してみましょう。
まずは、UI Kit の Gs2ClientHolder プレハブを配置します。
Gs2AccountAutoLogin プレハブを配置します。
続けて、環境設定 ScriptableObject を作成し、クライアントIDやクライアントシークレットを設定します。
配置したプレハブに設定を反映します。

## UI の作成

ここからしばらくはUIを配置します。
UIの準備が整いましたので、UI Kit で動作を加えていきましょう。

## GS2-Inventory の情報を出力

現在のポーションの所持数量を表示するようにします。
まずは Fetcher を追加します。Potion を表す ScriptableObject を設定します。
続けて、Label を追加します。
format に指定できるプレースホルダを確認します。
onUpdate にテキストコンポーネントを設定します。

## GS2-Limit の情報を出力

次は、交換回数カウンターの値を表示するようにします。
同じく Fetcher を追加し、カウンターの種類を設定します。
続けて、Label を追加し、format を設定、onUpdate でテキストコンポーネントを設定します。

## GS2-Exchange の交換レートの情報を出力

交換レートの Fetcher を設定し、交換レートを設定します。
交換によって入手できるアイテムの情報をテキストコンポーネントに反映します。
残り交換可能回数をテキストコンポーネントに反映します。

## 交換実行ボタンを配置

交換ボタンに交換処理のサポートプレハブを関連づけます。
サポートプレハブは初期状態では非アクティブにしておき、交換ボタンを押したらアクティブにするようにします。
交換済みの状態の時に表示する非アクティブなボタンを用意します。

Enabler を使用して、交換可能な時と交換済みの時で表示するボタンを切り替えるようにします。

## 動作確認

実行してみましょう。

勝手に通信が行われ、テキストコンポーネントに値が反映されていきます。
交換ボタンを押します。
その後の状態変化にもUIは勝手に追従します。

## データの読み込み中はラベルを非表示にする

起動時に一瞬テキストコンポーネントの初期値が表示されていたのが気になるかもしれません。
読み込み中はコンポーネントを表示しないようにしてみます。

Enabler を追加し、読み込み完了時にテキストコンポーネントのノードを有効化するように設定します。

実行してみましょう。

先ほどとは異なり、読み込みが終わったら表示されるようになりました。
そもそも読み込みが終わるまで画面自体表示したくないようであれば、一般的に行われているように起動時に GS2-SDK でデータを一度でも取得しておけばキャッシュが作られ、ちらつきを無くすことができます。

これで UI Kit の解説は終了です。
開発効率が劇的に改善されるイメージを持っていただけていることに期待します。
【Rspec】リファラをheaderに設定してリダイレクトするケースを作成する。

メモとして残します。

controllerに`redirect_to :back`と記載されているactionに対してのactionケースを確認する場合、
リファラが設定されていないと、リダイレクト先を確認するケースでエラーとなる。
そのため、以下でリファラの設定方法を記述します。

# ■やり方

リクエストヘッダに`HTTP_REFERER`をキーにリファラとなるURLを設定。

```rb
RSpec.describe 'put /hoge_save', type: :request do
  let(:headers){ { 'HTTP_REFERER' => referer } }
  let!(:referer){ "/hoge_prev" }

  it 'リファラにリダイレクトすること' do
    put '/hoge_save', {'params'=>'hoge'}, headers
    expect(response).to redirect_to referer
  end
end
```

以上
Google Colaboratoryで型ヒントを使う方法

## Google Colaboratoryでは型ヒントが使えない

バージョン確認

```py
!python --version
#　Python 3.7.13
```

バージョンが3.7なので、annotationsをインポートする必要があります。

## 解決方法

ノートブックの最初で以下を記載します。

```py
from __future__ import annotations
```
【Rails】redirect_toを使うときはステータスコードを指定しよう！

## 結論
redirect_toをstatusオプションなしで使用すると思わぬ事故が起きることがある。
なのでstatusオプションを使ってステータスコードを指定するかredirect_to以外のメソッドを使用しよう。

## redirect_toについて
https://railsdoc.com/page/redirect_to

## 起きたこと
https://railsguides.jp/getting_started.html#%E3%82%B3%E3%83%A1%E3%83%B3%E3%83%88%E3%82%92%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8B

このRailsのチュートリアル？をやっていたところステータスコードを指定しないでredirect_toを使用したら思わぬ挙動が発生した。

### blogアプリで記事にコメントをつけて、そのコメントを削除すると記事も削除された！！！
コメント削除のviewファイル
```erb
<p>
    <%= link_to "Destroy comment", [comment.article, comment], data: {
      turbo_method: :delete,
      turbo_confirm: "Are you sure?"
    } %>
</p>
```

comments_controller.rb
```rb
def destroy
    @article = Article.find(params[:article_id])
    @comment = @article.comments.find(params[:id])
    @comment.destroy
    redirect_to article_path(@article)
end
```

やっていることは記事をパラメータの記事ID指定で取得する。  
その記事がもつコメントの中から、パラメータの（コメントの）ID指定でコメントを取得する。  
そのコメントを削除したら、article_path（articleのshow）にリダイレクトする。
ということをやっているはずだった。

けれど、redirect_toにstatusオプションをつけないで使用したために記事まで削除されてしまった。

## 原因
redirect_toにはstatusオプションを指定することでステータスコードを指定することができる機能がある。

```rb
redirect_to post_url(@post), status: 301
```

そして、指定しないときはstatus: 302がデフォルトになっている。←これが原因

> 仕様書ではリダイレクトの際にメソッド (と本文) を変更しないよう要求しています

https://developer.mozilla.org/ja/docs/Web/HTTP/Status/302


つまり、deleteメソッドをarticle_pathにリダイレクトしてしまっていた。
なのでrailsはdeleteメソッドに対応するアクションを実行して記事を削除してしまった。
~~確かにリダイレクトだからそーゆーもんかというような気もする~~ ←事後諸葛亮

## 結論

redirect_toをstatusオプションなしで使用すると思わぬ事故が起きることがある。
なのでstatusオプションを使ってステータスコードを指定するかredirect_to以外のメソッドを使用しよう。
Salesforce認定アドミニストレーター試験の公式練習問題を解説してみた-その016

こんにちは。本橋孝昭です。

Salesforce認定アドミニストレーター試験の公式練習問題があるのはご存じでしょうか。
Trailheadにフラッシュカードとして何度も解ける練習問題がございます。

今回はTrailhead
アドミニストレーター認定の準備: 構成、設定とオブジェクト
オブジェクトマネージャと Lightning アプリケーションビルダーの確認
　にある練習問題4問目をご紹介します。
https://trailhead.salesforce.com/ja/content/learn/modules/administrator-certification-prep-setup-and-objects/practice-user-setup

なお、こちらの内容はYouTubeでも公開しております

https://www.youtube.com/watch?v=6lWDVr-D9EY

<iframe width="560" height="315" src="https://www.youtube.com/embed/6lWDVr-D9EY" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>


オブジェクトマネージャと Lightning アプリケーションビルダーの確認
練習問題4問目はこちらです


![004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/7a40be2e-8101-3077-21bd-a213819fbef6.png)




数式問題を問われる問題ですね

今回の数式はどの関数を使用するか
という問題です

正解はCです

では、問題を解説します

今回正解であるIF関数を使用すると
○ならこれを出す。▽ならこれを出す
という分岐が可能です


例えば従業員数が1000人以上は赤旗。500人以上は黄色、500人未満は緑
という分岐の場合、数式はこのようになります
数式

IF ( NumberOfEmployees >= 1000 ,
IMAGE ( "/img/samples/flag_red.gif", "red", 15, 15 ) ,
IF ( NumberOfEmployees >= 500,
IMAGE ( "/img/samples/flag_yellow.gif", "yellow", 15, 15),
IMAGE ( "/img/samples/flag_green.gif", "green", 15 , 15 )))

Aについて

Aは項目型が選択リストに使用する項目です

今回の「従業員数」項目型は選択リストではないので誤りです
https://trailhead.salesforce.com/ja/content/learn/modules/advanced_formulas/picklist_formulas


Bについて
BのTEXT() は、パーセント、数値、日付、日付/時間、選択リスト、通貨などの項目をテキストに変換する関数です。

今回作成する数式は項目型が問われていないので間違いです
詳細はこちらのTrailheadを確認してください


Dについて
AND()関数は２つ以上の要素がどちらとも
その条件に合っているか否かを判定します

今回の場合、もしも作成する数式が
・従業員数が1000人以上で都道府県が東京である
とか
・従業員数が1000人以上で業種が製造業である

のように複数の要素を見る数式ならAND（）関数が必要でした

しかし、今回の場合「従業員数」の項目値しか見ない数式を作成するのでAND関数は間違いですね


いかがでしたでしょうか

数式は何度も作成してみてやっと身に付きます

私のブログ記事に今回の問題の内容やいくつかの数式を作成解説した記事があるのでご確認ください

https://qiita.com/Takaa/items/fbe5f88ae84612e61031

今日の内容はここまでです。
皆さん楽しく元気にSalesforceを学びましょう！
Ubuntuとdockerをwsl2で再連携

Ubuntu18.04から
Ubuntu20.04に乗り換えた際につまずいた
wslとdockerについての手順です。

# 環境
windows10
Ubuntu18.04-> Ubuntu20.04(両方入っている状態)
docker
wsl

# 事象
Ubuntu-20.04をインストールしたのち
ubuntu-20.04を起動しdockerコマンドを適当にいれたらエラーが発生した。

```ubuntu:ubuntu-20.04
>docker ps

The command 'docker' could not be found in this WSL 1 distro.
We recommend to convert this distro to WSL 2 and activate
the WSL integration in Docker Desktop settings.

For details about using Docker Desktop with WSL 2, visit:

https://docs.docker.com/go/wsl2/
```

要約すると
現在のUbuntu-20.04のwslは1なので
dockerを使用するならwsl2にしてくださいね

ということ。

# 現状の設定確認

コマンドプロンプトを開き
wsl -l -v
と入力すると現在のwslの設定が確認できる

```cmd:コマンドプロンプト
>wsl -l -v

  NAME         STATE   VERSION
* Ubuntu-18.04 RUNNING 2
  Ubuntu-20.04 RUNNING 1
```

Ubuntu-20.04はやはり1。
あと、主で利用するものが
Ubuntu-18.04になっているのでこれを
Ubuntu-20.04にする

```cmd:コマンドプロンプト
// 主で使用するものを設定
>wsl --set-default Ubuntu-20.04
// wslを2に設定
>wsl --set-version Ubuntu-20.04 2
>wsl -l -v

  NAME         STATE   VERSION
* Ubuntu-20.04 RUNNING 2
  Ubuntu-18.04 RUNNING 2
```

すると、dockerの
settings Resources WSL Integrationに
Ubuntu-20.04が表示されるようになる

![docker_setting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2436967/91701479-fef3-e6f7-6c65-3497850fe9fe.png)

このUbuntu-20.04にチェックを入れてやる

# 最後に
Ubuntu-20.04およびdockerを再起動(windows毎再起動なら確実)すれば
Ubuntu-20.04にてdockerにアクセスできるようになる
RDS(postgres)マイナーアップグレード手順について

# はじめに
RDSのマイナーアップグレード(11.10のEOL)について、AWSから通知があり、
マスターのみのRDS(postgres)を11.10から11.15にマイナーアップグレードを行う必要がありました。
備忘として、コンソールでの作業手順をまとめてみました。

# アップグレード対象の確認
AWSコンソールにログインし、RDS一覧画面から該当のRDSの詳細画面に遷移します。
「設定」タブ内の「エンジンバージョン」が11.10(EOLバージョン)であることを確認します。
![rdb1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2740559/3ac16295-3792-79e2-190f-7added80e893.png)

# アップグレード作業
RDSの詳細画面右上の「変更」ボタンを押下します。
![rdb2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2740559/f4ca5a8d-f80d-0222-ce5a-66c8f1cc43ca.png)

設定変更画面にて、設定欄の「DBエンジンバージョン」のリストからアップグレード先のバージョン(11.15)を選択する。
最下部にスクロールし、「続行」ボタンを押下する。
![rds3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2740559/f80493c6-1646-8fbb-7830-b9b350c6638d.png)

変更のサマリーを参照し、エンジンバージョンの現新の値を確認する。
又、変更のスケジューリング欄のタイミングを「すぐに適用」のラジオボタンを選択する。
問題なければ、最下部にスクロールし、「DBインスタンス変更」ボタンを押下する。
![rds5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2740559/d6450440-25b4-6f29-cfef-629bb99b69e9.png)

アップグレード処理が実行されます。
アップグレード前後のスナップショットと、アップグレード処理が実行されるため、
20分程のダウンタイムが発生した後、11.15へのアップグレードが完了しました。
![rds6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2740559/a1f27349-d18b-87be-5aa7-6e523e1b2649.png)


# 考慮事項など
・クラスター構成でない場合、自動でアップグレード前と後のスナップショットが取得されるため、
　事前にスナップショットを取得する必要はありません。
　戻しが発生した場合、自動で取得されたスナップショットから復元することが可能です。

・リードレプリカが存在する場合、リードレプリカ⇒マスターの順番でアップグレードする必要があります。

・ダウンタイムが発生する為、システムが稼働していない時間帯を見計って作業する必要があります。
Inconsistency detected by ld.soのエラー解決

picoCTF2021のBinaryExploitation「Here's a libc」の問題を解こうとしたら以下のようにエラーが発生：

```
shoebill@pwner:~/pico$ ls
libc.so.6  vuln

shoebill@pwner:~/pico$ ./vuln
Inconsistency detected by ld.so: dl-call-libc-early-init.c: 37: _dl_call_libc_early_init: Assertion `sym != NULL' failed!
```

リンカのバージョンと与えられたlibcのバージョンの違いによるエラーと考えられる。

これを行なっているUbuntuの情報：

```
shoebill@pwner:~$ uname -a
Linux pwner 5.15.0-47-generic #51-Ubuntu SMP Thu Aug 11 07:51:15 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
```

# pwninitで解決

https://github.com/io12/pwninit

[ここ](https://github.com/io12/pwninit/releases)から直接バイナリをダウンロードしました。

`pkg-config`が必要になるので事前にインストールしておきます：

```
sudo apt install pkg-config
```

目的の実行ファイルと`libc.so`のある場所で`pwninit`を実行します：

```
shoebill@pwner:~/pico$ ls
libc.so.6  pwninit  vuln

shoebill@pwner:~/pico$ ./pwninit 
bin: ./vuln
libc: ./libc.so.6

fetching linker
https://launchpad.net/ubuntu/+archive/primary/+files//libc6_2.27-3ubuntu1.2_amd64.deb
unstripping libc
https://launchpad.net/ubuntu/+archive/primary/+files//libc6-dbg_2.27-3ubuntu1.2_amd64.deb
warning: failed unstripping libc: failed running eu-unstrip, please install elfutils: No such file or directory (os error 2)
setting ./ld-2.27.so executable
copying ./vuln to ./vuln_patched
running patchelf on ./vuln_patched
writing solve.py stub

shoebill@pwner:~/pico$ ls
ld-2.27.so  libc.so.6  pwninit  solve.py  vuln  vuln_patched

shoebill@pwner:~/pico$ ./vuln_patched 
WeLcOmE To mY EcHo sErVeR!
```

無事実行できるようになりました。

こちらも参考：
https://sh0ebill.hatenablog.com/entry/2022/09/28/215346
さくらのVPSをReaRでクローンする

# やりたいこと
さくらのVPSで作成したサーバーのクローン（複製）を作りたい。

# 概要
さくらのVPSにはサーバーのクローンを作ったり、ディスクのバックアップやリストアを行う機能がありません。同じ構成のサーバーを複数作りたいときや、テスト用に今の環境をクローンして使いたい場合はちょっと不便です。
そこで ReaR (Relax-and-Recover) を使って、クローン対象のサーバーをバックアップして、クローン先にリストアを行います。ReaRは起動可能なISOファイルを作成できるので、OSをインストールする感覚でリストアが可能です。

# 使用環境
Rocky Linux 8.6

# ReaRのインストール
~~~
# dnf install rear
~~~

# ReaRの設定
/etc/rear/local.conf を以下のようにしました。生成されるISOファイルは /tmp に出力されます。/tmpはバックアップの対象外のはずなので、生成中のISOファイルがバックアップされることはありません。
~~~ /etc/rear/local.conf
OUTPUT=ISO
OUTPUT_URL=file:///tmp
BACKUP=NETFS
BACKUP_URL=iso:///backup/
~~~

# バックアップの実行
~~~
# rear -v mkbackup
~~~
しばらくするとバックアップが完了するので、/tmpを見てみます。すると自分のホスト名のディレクトリの下に、.isoファイルが作成されています。
~~~
# ll /tmp/ホスト名/
-rw-------   1 root root        202  9月  7 09:57 README
-rw-------   1 root root        276  9月  7 09:57 VERSION
-rw-------   1 root root 1391476736  9月  7 09:57 rear-ホスト名.iso
-rw-------   1 root root     105196  9月  7 09:57 rear-ホスト名.log
~~~

# クローン先のサーバーを用意する
さくらのVPSのコントロールパネルを開き、はじめにクローン先となる、新しいサーバーを作成します。たしか新規作成時はISOファイルからは作れなかったはずなので、まずは適当なサーバーを作成しておきます。
次にサーバーの「OS再インストール」を選択し、「OSインストール形式」は「ISOインストール」を選択、「ISOイメージの詳細」は「アップロードする」を選択すると、SFTPサーバーの情報が表示されます。ファイルはSFTPでアップロードを行います。

# ISOファイルのアップロード
ISOファイルをPCにダウンロードしてからアップロードするか、もしくはサーバー上から直接アップロードします。以下は直接アップロードする手順です。
~~~
# sftp ユーザー名@ホスト名
cd iso
put /tmp/ホスト名/rear-ホスト名.iso
exit
~~~
ファイルはログインしたディレクトリの iso/ にアップロードします。アップロードすると、さくらのVPSのコントロールパネルにもファイルの情報が表示されます。

# リストアを実行する
ISOファイルのアップロードが完了したら、コントロールパネルの指示通りに進みます。VNCコンソールを開くと、ReaRのメニューが表示されていますので、「Automatic Recover　xxxx」を選択します。
![ReaR Boot Menu](https://akibabara.com/blog/wp-content/uploads/2022/09/rear.png)
しばらくするとリストアが終了するので 3) Reboot を選択します。電源がオフになるので、再度電源をオンにすると、クローンしたシステムが起動します。

# リストア先の設定を修正する
クローン元のネットワーク設定のまま起動してしまうので、このままではネットワークに繋がりません。VNCコンソールからログインして、ネットワークの設定を変更します。設定に必要な情報は、コントロールパネルの上で確認できます。これで外部からsshでログインできるようになるので、その他必要な修正などを行います。

# 余談
さくらのVPSにディスクのクローンや、バックアップ、リストア機能が欲しいです！ConoHaのVPSにはその機能があって、簡単にクローンが作れるんですよね。さくらインターネットではさくらのクラウドにこれらの機能があるので、格安なVPSサービスとの差別化ということで、あえて実装してないのかもしれません。さくらのクラウドはサーバーとディスクを自由に組み替えたりできるので、実験用の環境を簡単に構築できて便利です。

# 追記: ISOイメージから起動できなかった場合
今までこの方法で問題なくリストアできていたのですが、なぜかRocky Linux 8.7ではISOイメージからの起動ができませんでした。ローカル環境で試したところ、512MBのメモリではエラーすら出ず、1GBでは起動中にエラーで止まってしまいます。メモリを2GBにすると正常に起動しました。かといってさくらのVPSではインストール時だけメモリを増やすなんてことはできません。そこで、初期OS上で無理矢理リストアすることにしました。

手順
1. VPSで普通にOSをインストールして、sshでログインできる状態にする。 （以下、rootで実行することを想定）
1. dnf install tar rsync
1. VPSにログインして、 /backup を作成する
1. PC上でReaRで作成したISOを開いて、backup/backup.tar.gz を取り出す
1. backup.tar.gz をSFTPで /backup に転送する
1. ファイルを展開する tar xfzp backup.tar.gz --numeric-owner
1. 以下のスクリプトでクローンする
1. 再起動する（shutdownコマンドか効かなかったので強制再起動）
1. コンソールからnmtui等でネットワークの設定を行う
1. 不要なファイル /bakup を削除する

~~~
CLONEDIR="bin boot dev etc home lib lib64 opt root run sbin srv usr var"
for cdir in $CLONEDIR
do
  echo Clone: $cdir
  #echo rsync -rtlzvogpHAX --delete --dry-run $cdir /
  rsync -rtlzvogpHAX --delete $cdir /
done
~~~

稼働しているシステム上でファイルを書き換えるという、ちょっと乱暴なやり方でしたが、ひとまず復元はできました。コピーすべきではないファイルがある場合はrsyncの --exclude オプションで除外できます。これは正しくない、こうした方がいい、といった情報がございましたら、コメントで教えていただけると助かります。

追記；/etc/fstab は除外したほうがいいかも。たまたまUUIDが同じなだけで、元のインストールイメージが異なると別のUUIDになっている可能性があります。
as const は「〇〇」を、「□□的に」設定する（△△）。

結論から先に
##### 〇〇→readonly
##### □□→再帰
##### △△→アサーション

_as const は「readonly」を、「再帰的に」設定する（アサーション）_

理解をより容易にするには、
この後半部分から先に砕いていこう。

「再帰的に」設定する…
→「再帰（さいき）は、あるものについて記述する際に、記述しているものそれ自身への参照が、その記述中にあらわれることをいう。」

その定義が当てはまる部分について、
理解をより容易にするために、
先に和訳をしておこう。
「as ... 」→「...として」

```
A. Bite this.
噛んで。
B. Bite this as dog.
噛んで（犬として）。
```

（英語理解者にはいうまでもないが）
上記の英訳は、文脈的に後から先述の動作について
「それは __そもそも__ こういう前提であった」と、定義 __しなおして__ いる。
「（変数を）定義する」という動作が過去から未来への一方向であるのなら、
前提は読んで字のごとく前であり、
動作を前提を配置したうえで（それが実際であれ概念上のことであれ）（再度）実行されなければならない。

上記の例でいえば
命令に対する動作は、
A.では一度噛めば完了する。
B.でも一度噛めば完了する。
（ただし、「犬のように」噛まなかった場合は完了せず、再度「犬のように」噛む必要がある。）
このゆえに、文脈上・概念上の作業は再帰的に2回行われている。
一度、A.と同様に「噛み」を完了フラグに持っていったうえで
「犬として」前提を付加して「改めて」噛んでいる。
（猫のようには噛まなかったわけだ。）

理解をより容易にするのは
asの文脈上の機能の完全な理解ではないだろうかとおもうこの頃。
Twitter API returned a 403 (Forbidden), You currently have Essential access which includes access to Twitter API v2 endpoints only.　エラーとAPI v2作成について

# はじめに
この記事では、表題のエラーが出る問題について説明および代替えのコードを記載していく。
# 目次
[はじめに](#はじめに)
[目次](#目次)
[メイン](#メイン)
- [なぜエラーが出るのか](#なぜエラーが出るのか)
- [解決策](#解決策)
- [最後に](#最後に)
# メイン
## なぜエラーが出るのか
原因として、実行したコードがTwitter API v2ではなく、おそらくTwitter API v1のほうを使用したためだと考えられる。
2021年11月に仕様が変わったようで、
初期段階のアクセスレベルが「Essential」になり、使用できるのはTwitter API v2のみとなったため過去のコードを実行するとエラーが起こる。
仕様変更前に登録したユーザーは、v1にもアクセスできる「Elevated」のアクセスレベルが付与されているとのこと。(普通免許改正の準普通車のようだ。)
なので、ユーザーによって実行できるコードの可不可が発生しうるのだろう。

## 解決策
解決策はv2を利用するコードを使用するだけなのだが、サンプルコードが少なくYouTube(https://youtu.be/GqzbvoAsSrg) で紹介されていたものを参考に記載する。
このコード良いところは、Twitter上の特定のアカウントに絞られずに全ツイートから取得するところだ。

まず、tweepyをpipでインストールする。
```
$ pip install tweepy
```
次に、Twitter Portal(https://developer.twitter.com/en/portal/dashboard) にアクセスする。
※事前にTwitter Developer(https://developer.twitter.com/en) でTwitterアカウントでログインとアカウント認証を行っておくこと。Twitter APIを利用するには必須。

Projects&Appsからappを作成しする。
Keys and tokensタブを選択し、bearer_token・consumer_key・consumer_secret・access_token・access_token_secretを確認してどこかに保存しておく。再度確認はできないので、忘れると再作成される。
※このキーは決して漏洩しないように。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2780634/fce16b4d-1047-2b94-c4f4-9379b74cfe6a.png)

保存したキーで、コードを作成していく。
今回はリツイートを除いた、「タピオカ」というツイートを10件ほど取得する。
ツイートの取得には上限があり、自動化で取得する予定なら気を付けなければならないだろう。
以下が全体のコード。

```python:tweepy.py
import tweepy
import json

client = tweepy.Client()
#　クライアント情報を入力(先ほど保存したキーを入力)
def getClient():
    client = tweepy.Client(bearer_token="XXX",
                           consumer_key="XXX",
                           consumer_secret="XXX",
                           access_token="XXX",
                           access_token_secret="XXX")
    return client
#　実際のツイートの取得メソッド
def search(query):
    
    client = getClient()
    #queryが検索条件で、max_resultsで取得件数
    tweets = client.search_recent_tweets(query=query,max_results = 10)
    data = tweets.data

    results = []
    
    if not data is None and len(data) > 0:

       for tweet in data:
        obj = {}
        obj['id'] = tweet.id
        obj['text'] = tweet.text
        results.append(obj)

    else:
        return []
    return results
#　タピオカとリツイートではないツイートを条件にツイートを取得
tweets = search("タピオカ -is:retweet")
#　取得したツイートを表示
if len(tweets) > 0:
    for x in tweets:
        print(x)
else:
     print('no maching')
```

## 最後に
Twitter APIの記事が少なかったことや、特定のアカウントに絞らずにツイートを取得するやり方、今回のエラーコードの記事、
すべての要素を含むの記事がなかったので書いてみた。以上。
S3の画像が更新された際に、CHATWORKに画像をPOSTする


Google Newsshowcaseの外形監視のため、AWSのSynthetics Canariesを使用しています。
（Google NewsshowcaseをSynthetics Canariesを監視する説明は、別投稿で。）

Synthetics Canariesはキャプチャをs3に置きますが、すぐに確認したく、CHATWORKに画像をPOSTすることに。
Synthetics Canariesのスクリプトを修正しても良かったのですが、機能をファットにせずに役割分担をしようと。

* Synthetics Canariesは、外形監視のデータを集めるだけ
* Cloudwatch Logsで、データをチェックするだけ
* 本スクリプトは、キャプチャを送るだけ

axiosをつかって、CHATWORKに画像をPOSTするのに少しハマりました。

https://developer.chatwork.com/reference/post-rooms-room_id-files
公式だとよくわかりませんでした。

https://www.npmjs.com/package/chatwork-api-client/v/0.3.3?activeTab=readme
npmがあったので参考に。それを使えば良いのでは……、と思いつつ、作者がCHATWORKを使用しなくなったためdeprecatedです。


1. form-dataを作成する。fileやmessageをappendする。
1. fileはobjectにして、メタデータが必要。


```
import axios from 'axios'
import FormData from 'form-data'

export const postChatworkfiles = async (cwRoomId, cwTokenKey, file, fileName, message = '') => {
    const form = new FormData();

    form.append('file', file, {
        contentType: 'image/png',
        filename: fileName,
        knownLength: file.length,
    });
    form.append('message', message)

    let config = {
        headers: {
            'X-ChatWorkToken': cwTokenKey,
            'Content-Type': 'multipart/form-data',
            'Accept': 'application/json'
            ,
            ...form.getHeaders()
        }
    };

    try {
        let response = await axios.post('https://api.chatwork.com/v2/rooms/' + cwRoomId + '/files', form, config);
        console.log((JSON.stringify(response.data)));
        return response.data;
    } catch (error) {
        console.log(JSON.stringify(error));
    }
}

```

s3からファイルを呼ぶ側。v3からstream形式での取得になったので、取得したBufferを結合して、上記のCHATWORK post関数に。
```
export const lambdaHandler = async (event, context) => {
    const s3Client = new S3Client({
        region: "ap-northeast-1"
    });
    try {
        const streamToString = (stream) =>
            new Promise((resolve, reject) => {
                const chunks = [];
                stream.on("data", (chunk) => chunks.push(chunk));
                stream.on("error", reject);
                stream.on("end", () => resolve(Buffer.concat(chunks)));
            });

        const data = await s3Client.send(new GetObjectCommand({ Bucket:  event.Records[0].s3.bucket.name , Key: event.Records[0].s3.object.key }));
        const bodyContents = await streamToString(data.Body);
     const r = await postChatworkfiles('{roomid}','{token}',bodyContents, event.Records[0].s3.object.key,'今日のgoogleｰshowcaseです')
    
```






S3のイベント設定。他の画像に反応しないように、prefixと、suffixをつけて、画像更新時にイベント発火する形
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83911/abd8ccf1-6404-877b-5d1f-2919879ef750.png)



成功。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83911/c77fe75e-2187-17af-8eb4-477c931dd08b.png)
[InnoDB] Cannot boot server version 800xx on data directory built by version 800xx. Downgrade is not supported

When try to boot up my Docker container for MySQL, it kept exiting immediately. 

Error log: 

:::note alert
``
> 1 [ERROR] [MY-013171] [InnoDB] Cannot boot server version 80021 on data directory built by > version 80022. Downgrade is not supported
> mysqld: Can't open file: 'mysql.ibd' (errno: 0 - )
> 1 [ERROR] [MY-010334] [Server] Failed to initialize DD Storage Engine
> 0 [ERROR] [MY-010020] [Server] Data Dictionary initialization failed.
> 0 [ERROR] [MY-010119] [Server] Aborting
``
:::

Problem:

:::note warn
Docker container is created for MySQL8.0.22 at before, and I update Dockerfile to running older 8.0.21. But the OLD image is still contained by Docker Desktop.
:::

:::note warn
Remove image from Docker Desktop will not work. It will still Contained by Docker.
:::

The solution:

:::note info
1. Delete all old images using Docker rmi command not *Docker Desktop*.
   [x] Didn't work
2. Clear build cahche
   docker builder prune
   [x] Didn't work
3. Clear all volumes
   docker system df
   docker volume prune
   [O] worked!
4. rebuild the container with the older version of MySQL and then restarting the container.
:::


Reference(Thanks!):
[Mysql | Docker → Can’t start Container because of Boot Server Version Error.](https://medium.com/code-kings/mysql-docker-cant-start-container-because-of-boot-server-version-error-3defe9c6dcc1)
[DockerのBuild Cacheの削除](https://qiita.com/hyt-sasaki/items/e2cbc4cfb5ae698de498)