OmniAICreator/Qiita-1.07M huggingface dataset:
Paidy Checkoutで送信する顧客の住所を、StripeのAddress Elementで収集する


決済手段としてPaidyを追加する場合、商品の発送先住所をPaidy Checkoutを開始するAPIリクエストに送信する必要があります。この記事では、Paidyで顧客の住所情報を収集する際に、Stripeの住所フォーム機能を使う方法を紹介します。

## Paidyでは発送先住所が必須

Paidy Checkoutでは、`shipping_address`で郵便番号から始まる住所を送信する必要があります。

```js
const payload = {
    ...
    "shipping_address": {
        "line1": "AXISビル 10F",
        "line2": "六本木4-22-1",
        "city": "港区",
        "state": "東京都",
        "zip": "106-2004"
    }
};
 paidyHandler.launch(payload);
```


https://paidy.com/docs/jp/paidycheckout.html#shippingaddress_pay

この値を取得するための住所フォームを、Stripeの埋め込みフォームで簡単に実装してみましょう。

## Stripeを使って住所フォームを実装する

Stripe.js SDKを利用して、　Address Elementを表示させましょう。SDKを利用して、対象のHTMLにフォームをマウントさせる形で実装します。

```html
<div id="address-element"></div>
<script>

const appearance = { /* appearance */ };
const options = { mode: 'shipping' };
const elements = stripe.elements({ clientSecret, appearance });
const addressElement = elements.create('address', options);
addressElement.mount('#address-element');
</script>
```

![スクリーンショット 2022-11-21 13.19.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/fb35dd1e-9cd5-0a20-6d0a-04f05bb32eef.png)

GoogleのAPIを利用した住所のオートコンプリートにも対応していますので、住所フォームをより簡単に作ることができます。


![スクリーンショット 2022-11-21 13.20.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/be132584-4893-5330-9016-de74c07b995d.png)

## 住所の入力完了判定と、データの取得方法

住所の入力が完了したかどうかは、`change`イベントで判断できます。`event.complete`が`true`であれば、住所入力が完了していると判断できます。また、`event.value.addess`から住所情報をオブジェクト形式で受け取ることができますので、この内容をPaidy Checkoutを実行する処理に利用しましょう。

```diff_javascript
addressElement.mount('#address-element');

let customerAddress
addressElement.on('change', (event) => {
  if (event.complete){
    // Extract potentially complete address
    address = event.value.address;
  }
})
```

StripeのAddress Elementを利用する場合、オブジェクトのキーが一部Paidyと異なる点に注意しましょう。`country`の削除と`postal_code`キーを`zip`キーに変更することで、Paidyへのリクエストに使えるようになります。

```diff_javascript
+if (!address) return;
+const {
+  postal_code: zip,
+  state,
+  city,
+  line1,
+  line2,
+} = address;
const payload = {
    ...
    "shipping_address": {
+        zip,
+       state,
+       city,
+       line1,
+       line2,
    }
};
 paidyHandler.launch(payload);
```

## まとめ

Paidyのような住所情報が必須の決済サービスを使う際でも、Stripe.jsのSDKを使うことで開発工数を削減することができます。決済手段を選択するUIのカスタマイズにもStripeを活用できますので、こちらの記事もあわせてご覧ください。
Teradata AutoML

[Teradataml automl - テラデータ AutoML](https://docs.teradata.com/r/Lake/Teradata-Package-for-Python-Function-Reference-on-VantageCloud-Lake/teradataml-AutoML) [2024/08/20時点]の翻訳です。

:::note information
各機能の詳細な説明に関しては、段階的にリンクを追加していきますので、リンクから詳細ページを参照してご利用下さい。
:::

## AutoMLによる機械学習のアプローチ方法の変化
Teradata AutoMLは、エンドツーエンドの機械学習フローを自動化する機能を提供します。

<img width="900" alt="automl.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2740723/647af27a-d114-bece-50a0-962e9657de11.png">

## Teradata AutoMLの特徴
Teradata AutoML は、以下の特徴があります。

<img width="900" alt="TeradataAutoML.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2740723/8cab5a15-7cc4-39b8-3c4a-0c364eed1f8f.png">

## Teradata AutoMLのフェーズごとの対応機能
AutoML は、次の 6つのフェーズでさまざまなプロセスを実行します。
<img width="900" alt="automlfunction.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2740723/00aaf277-fd4e-82b7-1fee-661afa096582.png">


## 実行イメージの例
#### 実行方法
```python:実行方法の例
load_example_data("decisionforestpredict", ["housing_train", "housing_test"]) 	
								### データセットのローディング
housing_train = DataFrame.from_table(“housing_train”) 	### データフレームの作成
housing_test = DataFrame.from_table("housing_test")
aml = AutoML(	task_type=“Regression”, 			###  AutoMLインスタンスの作成
		exclude=['knn’], 
		verbose=2, 
		max_runtime_secs=300, 
		stopping_metric='R2’, 
		stopping_tolerance=0.7)
aml.fit(housing_train,housing_train.price)			### トレーニングの実施
prediction = aml.predict(housing_test)
```

#### 実行イメージの例(リーダーズボードの表示)
<img width="900" alt="leadersboard.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2740723/3df9b2c6-186c-94c2-a271-b46107ddff29.png">

#### 実行イメージの例(ハイパーパラメータチューニング)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2740723/bd50d63e-b548-7efc-028a-e17825252b79.png)

#### 動作環境
teradataml 20.00.00.00以上

## おわりに
:::note warn
警告
この本書は[Teradata Vantageドキュメンテーション](https://docs.teradata.com/)よりトピックに必要な情報を抜粋したものです。掲載内容の正確性・完全性・信頼性・最新性を保証するものではございません。正確な内容については、原本をご参照下さい。
また、修正が必要な箇所や、ご要望についてはコメントをよろしくお願いします。
:::

#### Teradata へのお問合せ
[Teradataへのお問合せ](https://www.teradata.jp/About-Us/Contact)
MatplotlibとSeabornの使い方


# データビジュアライゼーションの基本：MatplotlibとSeabornの使い方

データ分析において、視覚的にデータを理解することは非常に重要です。この記事では、Pythonの代表的な視覚化ライブラリであるMatplotlibとSeabornを使って、データを効果的に可視化する方法を紹介します。

## 1. はじめに

データの視覚化は、データのパターンやトレンドを迅速に把握するための強力な手段です。ここでは、基本的なプロットから、もう少し複雑なプロットまで、具体例を通して解説します。この記事の内容は、以下のリソースを参考にしています。

- [Matplotlib・Seaborn｜AI Curriculum Theory](https://zenn.dev/academix/books/0da15caa65b59f/viewer/4_matplotlib_seaborn)
- [参考動画](https://youtu.be/bsYJ3hTvx7c?si=GaV7i8syPmTV-2lN)
- [Google Colabでのコード実装](https://colab.research.google.com/drive/1NbDLaFtRi7e1A8h1Y_wsKl3sUgEqWD8I?usp=sharing)

## 2. Matplotlibの基本

### 2-1. Matplotlibとは？

Matplotlibは、Pythonで最も広く使われているデータ可視化ライブラリです。直線グラフ、散布図、ヒストグラムなど、基本的なプロットを簡単に作成できます。

```python
import matplotlib.pyplot as plt

# シンプルな線グラフ
x = [1, 2, 3, 4, 5]
y = [2, 4, 6, 8, 10]

plt.plot(x, y)
plt.title("シンプルな線グラフ")
plt.xlabel("X軸")
plt.ylabel("Y軸")
plt.show()
```

このコードでは、`plot`関数を使って簡単な線グラフを作成しています。`title`、`xlabel`、`ylabel`を使ってタイトルや軸ラベルを追加することで、グラフの情報をわかりやすく伝えることができます。

### 2-2. グラフのカスタマイズ

Matplotlibは、多様なカスタマイズオプションを提供しています。以下の例では、グラフの色やスタイルを変更します。

```python
plt.plot(x, y, color='red', linestyle='--', marker='o')
plt.title("カスタマイズされた線グラフ")
plt.show()
```

`color`や`linestyle`、`marker`のオプションを指定することで、見た目を調整できます。

## 3. Seabornでさらに美しいビジュアライゼーションを

### 3-1. Seabornとは？

Seabornは、Matplotlibをベースにした高レベルの視覚化ライブラリで、より美しく、洗練されたグラフを簡単に作成することができます。特に、統計データの可視化に優れています。

```python
import seaborn as sns
import matplotlib.pyplot as plt

# サンプルデータのロード
tips = sns.load_dataset("tips")

# Seabornでの散布図
sns.scatterplot(x="total_bill", y="tip", data=tips)
plt.title("Seabornでの散布図")
plt.show()
```

このコードでは、Seabornを使ってチップデータセットの散布図を描画しています。Seabornは、データフレームからのプロットを容易にするシンプルなAPIを提供しています。

### 3-2. Seabornの便利な機能

Seabornには、複雑なグラフを簡単に作成できる多くの機能があります。例えば、カテゴリ変数ごとに色分けを行ったり、回帰線を自動的に追加したりすることができます。

```python
sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips)
plt.title("Seabornでの回帰プロット")
plt.show()
```

`lmplot`を使うことで、回帰線付きの散布図が簡単に描画でき、`hue`引数でデータをカテゴリ別に色分けできます。

## 4. おわりに

MatplotlibとSeabornは、どちらも強力なデータ可視化ツールです。それぞれの利点を理解し、目的に応じて使い分けることで、より効果的なデータ分析が可能になります。今回紹介した例を参考に、ぜひ自身のプロジェクトに応用してみてください。

さらに詳しいコード例や実践は、[こちらのGoogle Colab](https://colab.research.google.com/drive/1NbDLaFtRi7e1A8h1Y_wsKl3sUgEqWD8I?usp=sharing)で確認できますので、ぜひチェックしてみてください。
商談商品を使ったカスタムレポート


+ [カスタムレポートタイプの項目レイアウトのプロパティに商品名の項目が出てこない](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007eqzj2SAA)

下記情報を見つけたため、商品オブジェクトにカスタムの商品名を追加し数式を設定しました。

[標準レポートの [商品 ID] 項目が見つからない (salesforce.com)](https://help.salesforce.com/s/articleView?id=000386022&type=1&_ga=2.162538367.2100202771.1724120992-1263901214.1724040188)

上記のヘルプは少々目的が違うようです。


データモデルを見ると以下のように商談商品から商品を参照できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/c8445d7a-173d-d35a-e4b7-6f32a7303977.png)

https://architect.salesforce.com/view/MCOSE2FGUMY5FGHA64A2R4AEIFCQ

ProductId　といういかにも商品オブジェクトの参照項目があるのですが、関連する商品レコードの ID。バージョン 3.0 ではこの項目は廃止され、後方互換性の目的でのみ提供されています。

https://developer.salesforce.com/docs/atlas.ja-jp.252.0.object_reference.meta/object_reference/sforce_api_objects_opportunitylineitem.htm

商談商品に数式項目を作る時に以下のように設定してみてください。

```
PricebookEntry.Product2.Name
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/8bb07ce3-5fc6-bf6e-4bd6-51c42d5b3369.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/68a779b8-8222-6688-85d2-3ac893935570.png)
Contact Form 7 で、メールテンプレート上でプルダウンのラベル値を取得する方法

# 結論：`[_raw_{field_name}]` と書く

お問い合わせ種別をContact Form 7で記述すると、以下のような記述になると思います。

```
[select* type id:type class:form-select include_blank "イベントに関する問い合わせ|event" "IRに関する問い合わせ|ir" "求人に関する問い合わせ|recruit" "ホームページに関する問い合わせ|web" "その他|etc"]
```

こうすれば、プルダウンの画面上に表示される選択肢では日本語名が表示され、POSTされる値はvalueに入っている英語名を送ることが出来ます。

でも、メールテンプレートでメールタグを使って出力すると、
valueに入っている値のほうが取得されます

```
お問い合わせ種別: [type]
↓
お問い合わせ種別: ir
```

こういう場合、`[_raw_{field_name}]`のように書けば、ラベル値のほうを取得できます

```
お問い合わせ種別: [_raw_type]
↓
お問い合わせ種別: IRに関する問い合わせ
```

以上
Selenide(Kotlin)のProxyを使うとCannot access class 'com.browserup.bup.BrowserUpProxy'. 

# はじめに

SelenideでProxyを使う際にエラーが出たので残します

# 問題

SelenideのProxyを使うために以下のサイトを参考に実装をしました

https://selenide.org/2019/12/18/advent-calendar-network-logs-with-proxy/

```kotlin
val bmp = WebDriverRunner.getSelenideProxy().proxy
```

しかし`proxy`の部分で以下のエラーが出ました

```
Cannot access class 'com.browserup.bup.BrowserUpProxy'. Check your module classpath for missing or conflicting dependencies
```

# 解決方法

以下のパッケージを追加したところドキュメント通りにできました

```xml:pom.xml
    <dependency>
      <groupId>com.codeborne</groupId>
      <artifactId>selenide-proxy</artifactId>
      <version>7.4.2</version>
    </dependency>
```

# おわりに

Proxyという機能を知ったことで選択肢が増えたのでよかったです
FastAPIでホットリロードができない貴兄に

FastAPIでホットリロードがなぜかできなくなっちゃったとお嘆きの貴兄。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13435/1e1c4f97-3a41-c2ef-e822-e00d40b6103b.png)

こんなエラーが出ていませんか？

```
_rust_notify.WatchfilesRustInternalError: error in underlying watcher: IO error for operation on <path>: No such file or directory (os error 2)
```
これです。
ググったら以下がありまして、

https://github.com/encode/uvicorn/discussions/2361

> Downgrading watchfiles to version 0.21.0 solved the issue for me.

とのことでした。とりあえずこれで凌ぎましょう。

```
[tool.poetry.dependencies]
python = "^3.11"
fastapi = "^0.112.1"
uvicorn = {extras = ["standard"], version = "^0.30.6"}
sqlalchemy = "^2.0.32"
pymysql = "^1.1.1"
watchfiles = "0.21.0"
```

このようにpyproject.tomlに

>watchfiles = "0.21.0"

を追加して

```
$ docker-compose run --entrypoint "poetry install --no-root" demo-app

Package operations: 0 installs, 1 update, 0 removals
 - Downgrading watchfiles (0.23.0 -> 0.21.0) 
```

ダウングレードできました。
これでさきほどのエラーは出なくなりました。ホットリロードもされるようになりました。
よかったですね、貴兄。
JuliaでGmailを送付する方法

# サーバーで計算を回しているとき
長い計算が終了した際に、自動でメールを送ってくれるとすごく助かります。

# 方法

[`SMTPClient.jl`](https://juliapackages.com/p/smtpclient)というパッケージとGmailのアプリパスワードを使用して実装できました。

# Gmailの設定
次のサイトを参考に作成しております。
- [アプリパスワードの生成](https://zenn.dev/daddy_yukio/articles/8c6a15fc09548a#%E3%82%A2%E3%83%97%E3%83%AA%E3%83%91%E3%82%B9%E3%83%AF%E3%83%BC%E3%83%89%E3%81%AE%E7%94%9F%E6%88%90)

[Googleアカウントの設定画面](https://myaccount.google.com)から [セキュリティ] > [2段階認証プロセス] > [アプリパスワード]に移動します。

アプリは「メール」、デバイスは「その他（名前を入力）」>「Gmail Julia」としてアプリパスワードを取得して、コピーしておいてください。（後ほどコードに貼り付けます。)

# Juliaの設定
1. [SMTPClient](https://juliapackages.com/p/smtpclient)パッケージを追加してください。
2. 次のようなファイルを作成してください。
    ```julia:smtp.json
    {"passwd":"(コピペした16桁のアプリパスワード)","username":"(Gmailのメールアドレス)","from":"(著名)","url":"smtps://smtp.gmail.com:465"}
     ``` 
1. 続いて、メールを送るためのコードを次のように作成してください。
    ```julia:sending_mail.jl
    using JSON
    using SMTPClient, Dates


    function readsettings()
        str = open(f -> read(f, String), "(smtp.jsonのあるディレクトリのパス)/smtp.json")
        settings = JSON.parse(str)
        return settings
    end


    function sendmail(subject::String, message::String)
        settings = readsettings()
        opt = SendOptions(
            isSSL=true,
            username=settings["username"],
            passwd=settings["passwd"])
        # Provide the message body as RFC5322 within an IO
        dtstr = Dates.format(Dates.now(), "e, dd u YYYY HH:MM:SS +0900")
        body = IOBuffer(
            "Date: " * dtstr * "\r\n" *
            "From: " * settings["from"] * " <" * settings["username"] * ">\r\n" *
            "To: " * settings["username"] * "\r\n" *
            "Subject: " * subject * "\r\n" *
            "\r\n" *
            message * 
            "\r\n")
        url = settings["url"]
        rcpt = ["<" * settings["username"] * ">"]
        from = "<" * settings["username"] * ">"
        resp = send(url, rcpt, from, body, opt)
    end
    ```

以上で設定は終了です。

# 使い方
`sending_mail.jl`をインポートします。
```julia
julia> include("sending_mail.jl")
```
その後、次のように実行することでメールが送付できます。
```julia
julia> sendmail(
          "(メールのタイトル)",
          "(メールの本文)"
      )
```
これで完了です。


# 最後に
セキュリティには十分お気を付けください！
【解説】IDトークン、アクセストークン、リフレッシュトークンの違いと役割


# 【解説】JWT形式で発行されるIDトークン、アクセストークン、リフレッシュトークンの違いと役割

Webアプリケーションやモバイルアプリケーションの開発において、認証と認可は非常に重要な要素です。Amazon CognitoやOAuth 2.0などの認証プロバイダーを使用する際に、しばしば登場するのがIDトークン、アクセストークン、リフレッシュトークンの3種類のトークンです。

この記事では、それぞれのトークンがどのような役割を果たしているのか、どのように使い分けるべきかを詳しく解説します。

## トークンの基本概念

### JWT（JSON Web Token）とは？
3種類のトークンは、通常、JWT（JSON Web Token）形式で発行されます。JWTは、ヘッダー、ペイロード、署名の3つの部分で構成され、各部分はドットで区切られた文字列になっています。

```bash
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
```

JWTは、ユーザー情報や認可情報を安全にクライアントとサーバー間でやり取りするためのトークン形式です。

## 1. IDトークン

### 概要
IDトークンは、ユーザーが認証されたことをクライアントアプリケーションに通知するためのトークンです。これにより、クライアントアプリケーションは「誰がログインしているのか」を把握することができます。

### 主な用途
- **ユーザー情報の表示**: IDトークンには、ユーザーの名前やメールアドレスなどの情報が含まれており、クライアントアプリケーションでこれらの情報を表示する際に使用します
- **ユーザの認証**: シングルページアプリケーション（SPA）などでは、IDトークンが有効なものかを確認することで、ユーザーの認証を行う事ができる

### 含まれる主なクレーム
- `sub`: ユーザーの一意識別子（ID）
- `email`: ユーザーのメールアドレス
- `name`: ユーザーのフルネーム
- `iat`: トークンの発行日時
- `exp`: トークンの有効期限

### 使用シナリオ
IDトークンは、主にクライアントアプリケーション内で使用され、ユーザーのプロファイル情報を表示したり、ユーザーがログインしていることを確認するために使われます。

## 2. アクセストークン

### 概要
アクセストークンは、ユーザーが特定のリソースやAPIにアクセスする権限を持っていることを証明するためのトークンです。APIリクエストにおいて、認証済みのユーザーとしてリクエストを送信するために使われます。

### 主な用途
- **APIアクセスの認可**: クライアントアプリケーションがAPIにアクセスする際に、このトークンを使用してユーザーがその操作を行う権限を持っていることを証明します

### 含まれる主なクレーム
- `sub`: ユーザーの一意識別子（ID）
- `iss`: トークンの発行者
- `aud`: トークンの対象（クライアントID）
- `scope`: ユーザーが持つアクセス権限
- `exp`: トークンの有効期限

### 使用シナリオ
アクセストークンは、APIリクエストの`Authorization` ヘッダーに含めて使用されます。これにより、リソースサーバー（APIサーバー）は、このリクエストが正当なものであり、リクエスト元ユーザーがその操作を行う権限を持っていることを確認できます。

## 3. リフレッシュトークン

### 概要
リフレッシュトークンは、アクセストークンの有効期限が切れた後でも、ユーザーが再度ログインすることなく新しいアクセストークンを取得するためのトークンです。これにより、ユーザーのセッションをシームレスに延長することができます。

### 主な用途
- **アクセストークンの再発行**: クライアントアプリケーションが、ユーザーの再ログインを求めることなく、バックグラウンドで新しいアクセストークンを取得するために使用します。

### 含まれる主なクレーム
リフレッシュトークン自体には、一般的にユーザー情報や認可情報が含まれず、サーバー側でのみ有効性を検証するために使用されます。

### 使用シナリオ
リフレッシュトークンは、クライアントアプリケーションがアクセストークンの有効期限が切れたときに、新しいアクセストークンを取得するためのリクエストをサーバーに送信する際に使用します。このプロセスにより、ユーザーは再度ログインすることなくセッションを継続できます。

## トークンの使い分け

| トークン種別 | 役割 | 使用場所 | 有効期間 | 主なクレーム |
| --- | --- | --- | --- | --- |
| **IDトークン** | ユーザーの認証情報を提供 | クライアントアプリ内 | 短期間（数分〜1時間） | sub, email, name |
| **アクセストークン** | APIアクセスの認可 | APIリクエスト | 短期間（数分〜1時間） | sub, scope, exp |
| **リフレッシュトークン** | アクセストークンの再発行 | サーバー側（バックエンド） | 長期間（数日〜数週間） | なし（クライアントが知る必要がないため） |

## まとめ

Cognitoを使う上で色々な用語がでてきますが、違いが理解できたので、実際の設計での使い分けする事ができました。
IDトークン、アクセストークン、リフレッシュトークンは、それぞれ異なる役割を持ち、認証と認可のプロセスにおいて重要な役割を果たします。


# 参考
https://qiita.com/UpAllNight/items/ec522da70a6fe86a1d16
sfdx(v7) から sf(v2)にアップデート

# やったこと
1. Node.jsのバージョンアップ
1. sfdx, sfのアンインストール
1. sfdx, sfの再インストール

# Node.jsのバージョンアップ

Homebrewのアップデート
```tcl
brew update
```

Nodeの安定版をインストールし、安定版に切り替える
```tcl
nodebrew install stable
nodebrew use stable
```

Nodeのバージョン確認
```tcl
node -v
v22.4.1
```

# sfdx, sfのアンインストール

sfdx(v7)のアンインストール
```tcl
npm uninstall sfdx-cli --global
```

sfdxがアンインストールされたことを確認
command not foundになっていればOK
```tcl
sfdx version
zsh: command not found: sfdx
```

# sfdx, sfの再インストール

sf(v2)のインストール
```tcl
npm install @salesforce/cli --global
```

インストールできていることを確認
```tcl
# sf
sf version
@salesforce/cli/2.49.7 darwin-x64 node-v20.9.0

# sfdx
sfdx version
@salesforce/cli/2.49.7 darwin-x64 node-v20.9.0
```

# エラー

> (node:96459) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)

Nodeの安定版をインストールしたらv22.4.1になったが、その際`sf version`で上記エラーが発生した。
Nodeのバージョンをv20にすることで回避できるそう。

```tcl
# v20.9.0をインストール
nodebrew install v20.9.0
nodebrew use v20.9.0

# Nodeバージョン確認
node -v
v20.9.0
```


# 参考

[Move from sfdx (v7) to sf (v2)](https://developer.salesforce.com/docs/atlas.en-us.sfdx_setup.meta/sfdx_setup/sfdx_setup_move_to_sf_v2.htm)

[ERROR：[DEP0040] DeprecationWarning: The punycode module is deprecated.の解決法](https://tomatosauce.jp/punycode-deprecated/)
ダークモード実装時の備忘録

Webサイトでのダークモードの実装には、主に以下の二つの方法があります。
本記事ではこの二つの実装方法について記載します。

1. [ブラウザやOSの設定を検知してダークモードを適用する](#1-ブラウザやosの設定を検知してダークモードを適用する)
2. [サイト内に設置したボタンでダークモードとライトモードを切り替える](#2-サイト内に設置したボタンでダークモードとライトモードを切り替える)

## 1. ブラウザやOSの設定を検知してダークモードを適用する

ユーザのブラウザやOSの設定に基づいて、自動的にダークモードまたはライトモードのスタイルを適用します。

これを実現するには、CSSのメディアクエリ `prefers-color-scheme` を使用します。
このメディアクエリは、ユーザがダークモードを選択しているかどうかを検知し、その結果に基づいてスタイルを適用します。

### 実装例

以下のコードは、ユーザの設定に応じてダークモードまたはライトモードのスタイルを適用する例です。

```css
:root {
  --dark-bg: 33, 33, 33;
  --dark-color: 255, 255, 255;
  --light-bg: 255, 255, 255;
  --light-color: 33, 33, 33;
}

/* ブラウザやOSのモード設定がダークモードの場合 */
@media (prefers-color-scheme: dark) {
  body {
    background-color: rgb(var(--dark-bg));
    color: rgb(var(--dark-color));
  }
}

/* デフォルトのライトモードのスタイル */
body {
  background-color: rgb(var(--light-bg));
  color: rgb(var(--light-color));
}
```

## 2. サイト内に設置したボタンでダークモードとライトモードを切り替える

JavaScriptでボタンを押したときに`dark`というクラスを`body`に付与し、そのクラスをもとにCSSでダークテーマのスタイルを適用します。

また、ユーザのテーマ選択を`localStorage`に保存し、次回訪問時にその設定を反映するようにします。

### 実装例

以下のコードは、`dark`クラスを付与してダークモードを適用する例です。

```html
<button id="toggle-theme">テーマを切り替える</button>

<script>
  const toggleButton = document.getElementById('toggle-theme');

  // ページ読み込み時に保存されたテーマを適用
  const savedTheme = localStorage.getItem('theme');
  if (savedTheme === 'dark') document.body.classList.add('dark');

  toggleButton.addEventListener('click', () => {
    document.body.classList.toggle('dark');
    
    // テーマの保存
    if (document.body.classList.contains('dark')) {
      localStorage.setItem('theme', 'dark');
      return;
    } 
    localStorage.setItem('theme', 'light');
  });
</script>

<style>
:root {
  --dark-bg: 33, 33, 33;
  --dark-color: 255, 255, 255;
  --light-bg: 255, 255, 255;
  --light-color: 33, 33, 33;
}

/* ダークモードのスタイル */
body.dark {
    background-color: rgb(var(--dark-bg));
    color: rgb(var(--dark-color));
}

/* デフォルトのライトモードのスタイル */
body {
  background-color: rgb(var(--light-bg));
  color: rgb(var(--light-color));
}
</style>
```

### まとめ

基本的には2.方法で実装しているサイトが多いイメージです。
状況に応じて、二つの方法のどちらかを使うまたは、組み合わせて使いましょう。
文字列を圧縮・展開する/バイナリ文字列を 16 進数文字列に変換する

# この記事は？

Ruby で以下を行う方法を備忘録として残します ✍️

- 文字列を [zlib](https://docs.ruby-lang.org/ja/latest/library/zlib.html) で圧縮・展開する。
- バイナリ文字列を 16 進数文字列に、あるいは16 進数文字列をバイナリ文字列に変換する。

# 方法

```rb
require 'zlib'

string = 'ヨーヨー振りたい 🪀'

# 文字列を圧縮 → バイナリ文字列 → 16 進数文字列
binary_string = Zlib::Deflate.deflate(string, Zlib::BEST_COMPRESSION)
#=> "x\xDA{\xDC\xBC\xE2q\xF3\x9E\xC7`\xF2Y\xCF\xFA\xC7M]\x8F\e\xE7?nlQ\xF80\x7FU\x03\x00+q\x135"
hex_string = binary_string.unpack1('H*')
#=> "78da7bdcbce271f39ec760f259cffac74d5d8f1be73f6e6c51f8307f5503002b711335"

# 16 進数文字列 → バイナリ文字列 → 展開
binary_string = [hex_string].pack('H*')
#=> "x\xDA{\xDC\xBC\xE2q\xF3\x9E\xC7`\xF2Y\xCF\xFA\xC7M]\x8F\e\xE7?nlQ\xF80\x7FU\x03\x00+q\x135"
Zlib::Inflate.inflate(binary_string)
#=> "\xE3\x83\xA8\xE3\x83\xBC\xE3\x83\xA8\xE3\x83\xBC\xE6\x8C\xAF\xE3\x82\x8A\xE3\x81\x9F\xE3\x81\x84 \xF0\x9F\xAA\x80"
Zlib::Inflate.inflate(binary_string).force_encoding(Encoding::UTF_8)
#=> "ヨーヨー振りたい 🪀"
```

# バージョン情報

```shell
$ ruby -v
ruby 3.3.4 (2024-07-09 revision be1089c8ec) [arm64-darwin22]
```
WinUI 3 空のテンプレートで、Release ビルドで例外

2024-8-20 時点

いずれ解決されるでしょうが、日本語で解説がないのでメモしておきます。

![20240820 101743 MicrosoftVisualStudio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132059/6d1333f4-64cc-7776-e72c-647b80288754.png)

WinUI 3の、テンプレートで、空のアプリ、パッケージ化（デスクトップのWinUI 3）から、プロジェクトを作成します。

![20240820 102550 Anonymous.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132059/4ff74829-825d-369c-ba1d-4d9f32e7241e.png)

Releaseでビルドします。

すると、
「ハンドルされていない、Win32の例外が発生しました。」
「デバッガーが  にアタッチされていますが、このハンドルされない例外をデバッグするように構成されていません。」
![20240820 102244 Just-In-Timeデバッガーを選択する.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132059/53e100a8-7e2e-5261-74ad-0f22f517d189.png)

これでは、どこで例外が発生しているのか分からないので、

![20240820 102307 App4-MicrosoftVisualStudio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132059/839fff2e-505a-ed06-2da8-576d1486419d.png)

「ネイティブコードのデバッグを有効にする」
にします。

すると、例外は、次で発生していることがわかります。

![20240820 102335 App4(デバッグ中)-MicrosoftVisualStudio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132059/3fa0e56d-69e4-5d5c-c74f-235203e53708.png)

現時点での解決方法は、次を変更します。
![20240820 102437 App4-MicrosoftVisualStudio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132059/9048ab1a-e767-426c-4d74-fd4b3506213a.png)

``` xml
<PublishTrimmed Condition="'$(Configuration)' != 'Debug'">False</PublishTrimmed>
```

`PublishTrimmed`を、Falseにしますが、本来はTrueにしておくプロパティのはずです。
そのため、問題が解決したら、元のTrueに戻さないといけないと思われます。


### 環境
- Microsoft Visual Studio Community 2022 Version 17.11.0

### 参考

- [[UI] [Bug] Unhandled XamlParseException at launch in release](https://github.com/jcoliz/LogoSlideMaker/issues/23)
- [WinUI 3 - Blank App, Packaged (WinUI 3 in Desktop) C# project template crashes in Release configuration](https://github.com/microsoft/microsoft-ui-xaml/issues/9675)
Bizrobo! DAで入力処理が空ぶる～「hasKeyboardFocus」を使ってさらにロボットの安定化！～

こんにちは！
今回はBizrobo! 「hasKeyboardFocus」を使ってさらにロボットの安定化！です。
早速ですが、入力が空ぶってしまう、、、という経験はありませんか？
今回はその入力が空ぶることに対する対策(100%ではないですが、、、)をご紹介します。


<br>
<br>
◆そもそもhasKeyboardFocusとは<br>
hasKeyboardFocus自体のことは私も知りません。(おいおい)<br>
ただ、Bizrobo!においていえばカーソルが当たっている時に生成される属性の1つです。<br>
メモ帳を使って実際に見せますね。<br>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796227/1d3a2581-450f-471b-6bc1-72a3122a7493.png)
<編集 handle="264548" name="テキスト エディター" className="Edit" automationId="15" isEnabled="true" isKeyboardFocusable="true" der_x="8" der_y="51" der_width="861" der_height="508">
 ↑カーソルが当たっていないのでない

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796227/25faacc6-8435-6d6f-9741-97503e0c9a9f.png)

<編集 handle="264548" name="テキスト エディター" className="Edit" automationId="15" <font color=”red”>hasKeyboardFocus="true"</font> isEnabled="true" isKeyboardFocusable="true" der_x="8" der_y="51" der_width="861" der_height="508">
 ↑カーソルが当たっていると「hasKeyboardFocus="true"」属性を認識するようになる


◆hasKeyboardFocusをどうやって使うのか
ここからは実際にどうやって使うのかを箇条書きで説明します。<br><br>

・使うステップ：GuardedChoice(LocationFound)

①とりあえず、対象画面からLocationFoundを作る
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796227/0d1a7242-838a-e269-a111-e9a45e470870.png)

②デフォルトだと、「hasKeyboardFocus="true"」の属性がないので足す
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796227/ef8329f0-24d1-a5db-8c3f-9419691d0f61.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796227/161c62a2-852b-3c47-064c-d75fff4c77ce.png)



③このままだと、無限待機になる可能性があるのでタイムアウト設定をする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796227/1e2f8fd7-96ae-c6fb-9612-7e22b61aa77e.png)

④そのあと、入力処理を入れる<br>
以上で完成です！


※InputTextで入力させる場合
デフォルトだと、InputTextの前に何もないのでここに上記と同じ入力待機を設ける
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796227/8b9640c7-f3d3-e76d-2a31-69a744cca857.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796227/f2a50587-b113-4b48-b345-de08b8c16ee3.png)
※DA端末の動きが遅いときはMoveMouseの後ろとPressKeyの前後に少しだけ待機時間を置いてみてもいいかもしれません。
<br><br>

ここまでお疲れ様でした。
これで皆さんのロボットがより安定すれば幸いです。
実は、LocationFoundのコンポーネントってかなり自由度高いので今後も紹介していければと思います。

では、より良いRPAライフを～
Reactのステータスのロック？（課題のメモ）

# はじめに
まず、この記事は、解決策ではないです。
私が課題に感じていることのメモと、できればだれか、いい方法があったら教えてほしいなぁという内容です。

# 課題
Reactで、親子関係のコンポーネントがあり、１組の親と子だけでなく結構深くて、子たちを包含するよう親を更新しないといけない状況です。
SVGで描いているので、親のサイズは、HTMLのように勝手に広がってくれたりせず、自分で再計算しないといけない。（ブラウザはどうやってるんだろう🤔 深さ優先の逐次処理か）
雑に描くと↓こんな感じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/722610/1090c1fd-ac71-a111-eb55-7e2ba53a085c.png)

入れ子になっていて、子も、詳細を計算してみないとサイズが確定しないので計算してみた後に、子から親へ、「自分のサイズは100pxでしたー」「子の絵は描いておいたので親さんは親さんでよろしくー」と伝えていく。受け取った親は、子たちのサイズを合計したり子たちの隙間を考えながら、親自身のサイズを決定して自身を描く。そしてまたその親へ・・・。

として実装してみたところ、どうやら複数の子からの要求がほぼ同時に上がってきたとき、親も自分で計算していると、子の要求が一部無視されるような不整合が発生しました。

大げさに書くと、こんな状況だと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/722610/da660691-fca4-57b5-9fde-a84ce8cc1b53.png)

結果的に、オレンジの310pxでは描画されず、水色の110pxになる。子１、子２は、子自身によって適切な位置に描画されている。なぜ水色がオレンジに追い抜かれるかは疑問ですが、微妙なタッチの差とか、子１が特殊だったり、いくつかの条件が重なって起きる模様。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/722610/c662bf51-5c0a-875d-1984-031cc5d76148.png)

詳しく説明はしませんが、意図的にこの問題を発生させてみたコードは下記。このサンプルでは、子たちの最大の高さを、親の高さとして包含する、というものです。幅ではなく。（本質的には、子の計算結果を使って親を計算するという意味で同じということで。）

https://github.com/yo16/react_many_draw_trigger

親が灰色、子たちが緑で、数字は高さです。子はランダム(0～99)で自分の高さを決め、親は子たちの最大の高さを使って自分の高さを決めます。
一番右端の`26`が親が認識した子たちの最大高さで、子たちを包含するはずなのに、最小の`26`の子に合わせた高さになってしまっている。正解は当然`97`。
親ではわざと、大きな子ほど速く、小さな子ほど長く待ってから親を計算することで、再現させています。上に描いた手書きの絵の"計算中・・・"のところ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/722610/8645eb97-293b-6c6f-629c-8ee766375ec7.png)


# 対応方法の検討
いくつかの方法があるんじゃないかと思いますが、描画処理の頻度を減らすという意味でも、子たちの要求をまとめて、親はまとめて描画する方法がベターなんじゃないかと思ってます。
JavaScriptでいうと`Promise.all()`のイメージ。トリガーは子だけど、子を描画せよ！というのは親なので、いけるような気もする。それを、Reactでどう書くのかわからないけど。

あと、この手のことは、DB更新の世界では根幹の話で、方法が確立されています。更新する際にレコードをロックして、ほかのセッションの更新処理は待たせるという方法。
上の絵の例でいうと、水色が親での計算（更新）をするときに、親のレコードをロック（select for update）して、他の処理は待たせる。オレンジはスタートで待たされるので、追い抜かれることはない。
この手法は、DB更新の世界では悲観的ロックという名前がついています。この処理は作るのが大変なので、もし本当にやるなら、この問題が起きていると確信してからやりたい。極力やりたくない。たぶんやらない。やれない。（悲観🤣）

もう１つDB更新の世界で楽観的ロックというのがあって、これは更新する前にもう一度最新状態をチェックするという方法。
上の絵の例でいうと、水色の世界線で親を更新する直前に、最新の子１と子２をもう一度チェックすることによって、「自分の手持ちの子２は0pxだけど、DBでは200px・・・違うじゃん！あっぶね」と気づけるという技です。
DBの場合でいうと、違う場合はエラー処理として捨てるかもしれない。でも子２の正解がわかったんだから、もう一度最初から計算してもいいよねと思う。
これは、Reactでできるかなぁ。ちょっとわからない。水色の世界線からオレンジの世界線（JavaScriptではセッションと呼ぶのかわからないけど）の値をとるみたいなものだから。でももしそれができるなら、仕組みとしてはそんなに難しくなさそう。

ちなみに悲観的ロックは多くの短時間の処理が起こるときに使うべしとされ、楽観的ロックは処理があまり多くないときとされている。その意味では、どちらかというと、悲観的ロックが必要な場面な気はしている。

さらに別の方法の案。`useContext`を使って、１か所で更新すればという話も、雑に調べると出てくる。でもそれでは、この問題は解決しなさそう。今、親でわざと「小さな子ほど長く待つ」という処理をやっているけど、それがcontextの更新で発生したら同じだから。


# おわりに（課題のメモとしては終わり）
どうするかはまだ決めていませんが、課題と思いついた解決案は書いておくという、githubにissueを登録したようなエントリーでした。

Reactの中の処理が結構頑張ってくれているはずなんだけど、こういう問題が起きてしまうのは、たぶん私のコーディング方法がよくないんだと思う。そのために、力技で専用の回避ロジックを入れることが正しいのかという疑問にぶち当たる。そもそも正しいコーディングをすることが、一番の回避方法なんだろう。
でもそういうことは、「はじめてのReact」みたいな本にはあまり書いてなくて、プロの現場の間で受け継がれる技なんだろうと思う。
Reactでもなんでも、独学での技術習得の道の険しさは、こういうところだと思った。
【自分用メモ】Perl：warnのメッセージをSTDERRとログファイルに出すFile::Teeが便利

# 概要
Linuxのteeコマンドのように、出力を２つに分けるにはFile::Teeを使うと便利

# 詳細
出力が長いエラーログを出すバッチジョブで、作業の進行状況や突発的な異常時はリアルタイムの画面で確認したいが、作業終了時にはログファイルで確認しておきたいと思った。
これには、PerlのFile::Teeモジュールを以下のように使用すれば良い。

## インストール
モジュールは

```bash
$ sudo cpan File::Tee
```

でインストールしてもいいが、

ぼくはUbuntuのapt-cacheコマンドを使って

```bash
$ apt-cache search File::Tee
libfile-tee-perl - Perl module to duplicate streams like the tee unix command
```

とするとlibfile-tee-perlというのが見つかったので

```bash
$ sudo apt install libfile-tee-perl
```

としてインストールした。

## サンプルコード
以下のコードはSTDERRというファイルハンドルを/home/cf/tee.txtにコピーしている。
入口が１つで出口が２つだからT（ティー）と言うそうだ。

```perl
#!/usr/bin/perl

# tee.pl

use strict;
use warnings;
use File::Tee qw(tee);

# 出力ファイルのパス
my $logfile = "/home/cf/tee.txt";

# STDERRをteeに設定
tee(\*STDERR, '>>', $logfile);

# ここからのwarn、die、systemコマンドのSTDERR出力はファイルとターミナルの両方に出力されるはす

# テスト出力
warn "aaa \n";
warn "bbbbb \n";
die  "ccccccc \n";
```

実行すると以下のようになる。

```bash
$ ./tee.pl # 実行
aaa
bbbbb
cccccc

$ cat ./tee.txt # ログファイルの確認
aaa
bbbbb
cccccc
```

できてるー。

この場合warn、dieの他にsystemコマンドで外部コマンドを実行したときのエラー出力もtee出来る。
STDOUTをteeすることも、普通のファイルハンドルで２つのファイルを出すことも出来るはず（未確認）。

## IO::Teeというのもあるが

なぜかうまくいかなかった。

```perl
#!/usr/bin/perl

# tee_failed.pl

use strict;
use warnings;
use IO::Tee;

# 出力ファイルのパス
my $logfile = '/home/cf/logfile.txt';

# IO::Teeを使用してSTDERRをファイルとターミナルに出力
open my $log_fh, '>>', $logfile or die "Could not open log file: $!";
my $tee = IO::Tee->new(\*STDERR, $log_fh);
select $tee;

# ここからのwarn、die、systemコマンドのSTDERR出力はファイルとターミナルの両方に出力されるはす

# テスト出力
warn "aaa \n";
warn "bbbbb \n";
die  "ccccccc \n";
```

何のエラーもなく0バイトのファイルができ、何も入っていない状態になった。
なぜかは追求してません。
ひどくてゴメンネ

**（この項終わり）**
最小努力と最短時間でDP-900合格記

昨日(PL-300)に引き続きDP-900も無事一回で合格しました。
勉強時間は当日朝起きてから2時間という今までで一番の手抜き…(前日はPL-300で疲れ果てて寝落ちたため…)。

毎度の如くほぼ前日＆当日の詰め込みショートカット勉強法なので、きちんと正攻法で勉強することをお勧めします。


過去の体験記はこちら：

https://qiita.com/ponponmikankan/items/fd363bb0323569d3676f

https://qiita.com/ponponmikankan/items/77202be0de32cda5893d

https://qiita.com/ponponmikankan/items/4b64124be47a34467019

https://qiita.com/ponponmikankan/items/c27d6db74bac7cb44fef

https://qiita.com/ponponmikankan/items/f25fa53b13f10df574d6


## 前提

データ系経験（？）としてはPower BIやExcelを使って、手元のデータを触るくらい。
データベース言語でごりごりデータを操作したりなにかを抽出したことも、ほぼありません。（昔N予備校やっていたときにちょっとだけMongoDBかなにかを触ったくらい）

## 参考にした勉強法

DP-900については合格記がほかの試験に比べると少なかったです。
ひたすらLearnを2時間で叩き込みました。Learnもほかのやつと比べると少なめなので短時間でもなんとかなりました。

## 実際の勉強でやったこと

### Learnを飛ばし読み（1時間半程度）

https://learn.microsoft.com/ja-jp/training/courses/dp-900t00/

7時間くらいありますが最終セクションはPower BIなので実質スキップできるのと
途中もけっこうAZ-900で学んだ内容やデータベースの基礎知識的な内容なので読み飛ばせます。
似たような製品名を覚えるのがAZ-900、SC-900同様とても苦しいですが、特徴をキーワードとして組み合わせて覚えられれば意外といけます。選択式だからなんとかなってるけど、たぶん記述だったら死んでる。

### プラクティス評価の実施（3周くらい）

https://learn.microsoft.com/ja-jp/credentials/certifications/azure-data-fundamentals/practice/assessment?assessment-type=practice&assessmentId=24&practice-assessment-type=certification

最初は正答率が低くても、問題がパターン化されているのでわりとすぐに慣れます（？）

### 試験の感想

試験時間は1時間で、45分くらいかかりました。（見直し1周）。
ほかの900系同様、択一やドラッグドロップ系なので焦ることはないです。

プラクティス問題でも見たことない質問ももちろん出てくるのですが、落ち着いて解けばいけます。
数問単位で出たカテゴリとしてはPower BI系の最適なグラフに関してや、データの構造化の話、リレーショナルデータベースの特徴、トランザクションのACID特性についてが出たかも。
ACID特性については、3問もでてにんまり。原子性好きすぎだろ！

…というわけで、朝の2時間しかなく結構焦ったのですが、今回も超最低限勉強法で無事合格！
次はAI-900を目指そうと思います。もはやここまできたら900系は制覇したくなってきました。あと3つ！
[paiza]売り切れ！在庫の山！（【殿堂入りキャンペーン】残り物の量 (paizaランク C 相当)

# 瓜売りが瓜売りに来て瓜売れ残り売り売り帰る瓜売りの声
うりうりウルセェ！

https://paiza.jp/works/mondai/c_rank_skillcheck_archive/leftover

# チェック処理とかは端折ってる
/** ここから定型文 */
ここで記載したクラスについては、
[こっちの記事](https://qiita.com/AsilHatake/items/40204293bdf59cb9f9d8)に書いてある、標準入力をList化したりしてるMainクラスから呼び出す予定だし・なんか継承したりしてるYO!!

ちな、Mainを実行して標準入力から入力するのクソ面倒くさいので、
Java編については問題の入力例をパラメータにしたテストクラスとかも作って公開するYO!!

<details open><summary>開発・実行環境はこんな感じ</summary>

- VSCode
- Java 17
- jUnit 5.9
- maven

pom.xmlはこんな感じ

```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>jp.co.asil</groupId>
    <artifactId>paiza202408</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.9.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>
```

</details>
/** ここまで定型文 */

## 実装クラス 
```java:Leftover.java
package jp.co.asil.paiza202408;

import java.text.DecimalFormat;
import java.util.List;

public class Leftover extends Question {

  public Leftover(List<String> list) {
    super(list);
  }

  /**
   * あなたはスーパーマーケットを経営しています。
   * 
   * 今、ある生鮮食品を m[kg] 仕入れました。とても新鮮だったため、生のまま販売したところ、 m[kg] のうち p[%]
   * を売ることができました。次にその売れ残りをすべてお惣菜にして販売したところ、売れ残った量のうち q[%] が売れました
   * 
   * さて、m[kg] 仕入れたこの食品は最終的に何kg 売れ残ったでしょうか。ただし、生鮮食品をお総菜にする際は、量を変えることなく調理できるものとします。
   */
  @Override
  public List<String> answer() {
    double[] conds = List.of(list.get(0).split(" ")).stream().mapToDouble(Double::parseDouble).toArray();
    return List.of(conds[1] == 100D || conds[2] == 100D ? "0"
        : new DecimalFormat("#.##########").format(conds[0] * (100D - conds[1]) * (100D - conds[2]) / 10000D));
  }
}
```

## テストクラス 
```java:LeftoverTest.java
package jp.co.asil.paiza202408;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.List;

import org.junit.jupiter.api.Test;

public class LeftoverTest {
  @Test
  void testAnswer1() {
    Leftover testClass = new Leftover(List.of("1 80 40"));
    assertEquals(testClass.answer().get(0), "0.12");
  }

  @Test
  void testAnswer2() {
    Leftover testClass = new Leftover(List.of("10 31 52"));
    assertEquals(testClass.answer().get(0), "3.312");
  }

  @Test
  void testAnswer3() {
    Leftover testClass = new Leftover(List.of("1 99 99"));
    assertEquals(testClass.answer().get(0), "0.0001");
  }
}
```

# ざっくり
少数を扱うからDoubleを使うよ、最後にフォーマット掛けてるよぐらい。
本来ならBigDecimalとか使って計算しろよって感じだけど、即席で作ったのでこんなもん。
開発に詰まったとき、一次情報だけで解決できてる？

# 導入

開発中に「これ、どうしよう？」って問題にぶつかること、ありますよね。そんなとき、「まずは一次情報をチェックしよう！」って言われることが多いです。公式ドキュメントや開発元の資料って信頼できるし、幅広くカバーされているので、頼りたくなる気持ちは分かります。でも、実際にそれだけで問題が解決できてますか？

# 一次情報の概要と限界

## 一次情報の強み

一次情報って、公式ドキュメントとか開発元が提供する情報のこと。信頼性が高くて、どんな状況でも使えるように汎用的に作られているんです。だから、新しいツールやフレームワークを学ぶときには、本当に役立つんですよね。

## 一次情報の限界

でも、正直に言うと、一次情報だけじゃ解決できないこともあります。たとえば、特定のエラーメッセージが出たときに、公式ドキュメントにはその具体的な解決策が載ってないことって多いんですよね。それに、エラーコードのリストとか概要しか載ってないこともあって、「これじゃ、どうすればいいの？」ってなりがちです。そして、どれだけ検索しても求めている情報が見つからないことも多いです。

# 二次情報の利点

## 二次情報の強み

そこで登場するのが、StackOverflowやQiitaみたいな二次情報です。ここには、実際に開発者が「このエラーが出たとき、こうやって解決したよ」って具体的な解決策が共有されています。これが結構助かるんです！もちろん、すべての環境にピッタリ当てはまるわけじゃないし、ちょっと古い情報も混じってるけど、それでもリアルな体験談があるから頼りになりますよね。

## 二次情報の課題

ただし、二次情報にも注意が必要です。特定の環境に依存していたり、情報が古い場合もあります。でも、実際に直面した問題に対する実践的な解決策が見つかるので、それだけでも大きなメリットがあります。

# ケーススタディ

## 例1: 一次情報で解決できたケース

例えば、新しいライブラリを使うとき、公式ドキュメントを見ながら設定を進めれば、たいていのことは問題なくいきますよね。このように、一次情報がバッチリ役立つ場面もあります。

## 例2: 二次情報が役立ったケース

一方で、特定のエラーメッセージが出たとき、公式ドキュメントには解決策が見当たらなかったけど、StackOverflowで同じ問題に直面した人の投稿を見つけて、それを参考にして解決できた、なんてこともよくありますよね。

## 比較と考察

この二つを比べてみると、一次情報は基本的な理解や導入に役立ちますが、二次情報は具体的な問題解決に強いんです。だから、状況に応じてどちらを使うかを考えるのが大事です。

# 使い分けの推奨

開発をスムーズに進めるには、一次情報と二次情報のどちらも上手く使い分けることがポイントです。一次情報は信頼性が高くて、基本的なところを押さえるのに最適。でも、特定の問題に直面したときは、二次情報の方が効率的に解決できることも多いです。時間を節約しつつ、最適な解決策を見つけるために、状況に応じてうまく使い分けましょう。

# 結論

まとめると、一次情報と二次情報の両方が開発には欠かせないリソースです。それぞれの良さを理解して、必要に応じて使い分けることで、よりスムーズに問題解決ができるようになります。特に複雑な問題にぶつかったときは、まずは一次情報で基本を押さえつつ、具体的な解決策が必要なときは二次情報を頼るのがオススメです。
[T-DASH] 変数を含むXPathの画面要素に対してテスト自動化する

# Outline

テスト自動化の操作・検証対象となる画面要素が、ほぼ同じ構成であるが、一部分だけ異なる場合がある。例えば、以下の図のように、複数のプランがあり、その任意のプランを選んでテスト（複数のデータバリエーションテスト）を行いたい場合が該当する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/b26e9559-4e5a-7a1b-0d8a-b08ff11a9de5.png)

この図では、左下のプレミアムプランの”このプランで予約”のボタンの（最適化した）XPathは以下のとおりである

```
//h5[text()="プレミアムプラン"]/../a[text()="このプランで予約"]
```

ただ、ほかのプランで予約するテストをしたい場合どうなるか？
以下のとおりである。

| 画面要素 | XPath | 
|:-----------|:------------|
| お得な特典付プラン       | //h5[text()="お得な特典付プラン"]/../a[text()="このプランで予約"]        |
| ディナー付きプラン       | //h5[text()="ディナー付きプラン"]/../a[text()="このプランで予約"]        |
| お得なプラン       | //h5[text()="お得なプラン"]/../a[text()="このプランで予約"]        |

もし、これをひとつづつ画面要素として登録すると、画面要素の数が多くなり、保守が大変である。
また、テストケース側では、テストデータの都度画面要素を選びなおしになるため、こちらも保守が大変である。

これを解決させるには、以下のように画面要素に変数を含めることである。

| 画面要素 | XPath | 
|:-----------|:------------|
| ${planname}       | //h5[text()="${planname}"]/../a[text()="このプランで予約"]        |

こうすれば、変数${planname}のみをコントロールするだけで済む。

もちいるテスト自動化ツール

https://service.valtes.co.jp/t-dash/


# テスト対象

テスト自動化練習サイトを用いる。
Email : ichiro@example.com / Password : password でログイン後、宿泊予約をクリックした画面で、変数で指定したプランを予約する。

https://hotel.testplanisphere.dev/ja/index.html


# T-DASH

## 画面定義

画面キャプチャツールでは、変数を含んだ画面要素のXPathは取れません。
そのため、chrome developer toolを使い、かつ変数化できそうなところを探し出して、XPathを直接画面定義に記載する。

| 要素名 | 値 | 
|:-----------|:------------|
| 任意_このプランで予約       | //h5[text()="${planname}"]/../a[text()="このプランで予約"]        |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/13ad2ee7-ce5a-6d8c-c84d-60cdb6d911ec.png)

## カスタム動作

T-DASHの標準機能では、変数を新規に定義して値を設定する機能はありません。
そのため、変数を設定するカスタム動作を作成する

以下設定内容を reserve.yamlのファイル名で保存する
```
ACT-CAT-CUSTOM-792c1a17-5c76-489e-8155-05106db44f12:
  action_category_name: 予約
  icon: ''
  color: '#de5b64'
  custom_data:
    file_name: Reservation
    pip_list: []
    library_list: []
  actions:
    ACT-CUSTOM-028f3104-9d1e-4856-98a3-6a8781d804d2:
      action_name: plannameの変数を設定する
      action_type: operation
      action_format: 「設定値1」を変数plannameに設定する
      action_note: ''
      action_args:
      - value1
      action_def:
      - - Set Suite Variable
        - ${planname}
        - ${value1}
```

次に、作成された reserve.yamlをT-DASHの動作定義 -> カスタム動作 -> カスタム動作をインポート にてインポートし、実際にインポートされた設定内容を開く

このカスタム動作は、単純に設定値１の値を、変数${planname} 、画面定義で用いた変数を新規に定義して設定する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/4689d99e-e0db-3501-b898-2a6d978cd927.png)

## テストケース

1行目で、カスタム動作「plannameの変数を設定する」を呼び出し、${planname}にプラン名「ディナー付きプラン」を設定する
5行目で、変数${planname}を含むXPathである、「任意_このプランで予約」をクリックする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/ca8927a2-a1c4-016a-b822-b74759627085.png)


## テスト実行

1行目で指定したプラン、「ディナー付きプラン」が選ばれて実行されたことが確認できる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/fb42a7b6-38a1-5137-e886-fda9a433ab9c.png)


同様にして、変数を「お得なプラン」に変更すれば、「お得なプラン」が選ばれることが確認できる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/124b8402-c85c-f881-9bd8-4d184f764bb8.png)



このようにして、同じ構造のXPathに変数を用いることで、スクリプトの保守性を上げることができる。
Google Play Core Library から Play In-App Review Library への移行

# 背景
2024年7月13日に Google Play から以下のようなメールが届きました。

> 件名：
> [ご対応のお願い] Play Core Maven の依存関係を Android 14 用にアップデートしてください
>
> 本文から抜粋：
> 旧バージョンの Play Core Library は Android 14 をターゲットとするアプリでクラッシュするおそれがあるため、できる限り速やかに新しいバージョンのライブラリにアップデートしていただく必要があります。なお、Google Play では 8 月 31 日以降、すべてのアプリの新規リリースにおいて Android 14 をターゲットとすることが求められます。
>
> お客様のアプリでは、targetSdkVersion 34 に対応していないバージョンの Play Core ライブラリが引き続き使用されていますので、以下に示します。
>
> •		com.google.android.play:core-ktx, com.google.android.play:core
>
> クラッシュを防ぐため、アプリ内の Play Core Maven の依存関係をアップデートしてください。

6月19日にも同じようなメールが来ていましたが、対応せずに放置していたためリマインドされたものと思われます。

# 概要
Google Play Core Library （`com.google.android.play:core-ktx` および `com.google.android.play:core`） の 1.8.1 を使用していたのですが、targetSdkVersion 34 に対応するにはライブラリをアップデートする必要があるようです。
しかし Google Play Core Library 自体は更新が終了し、機能ごとに複数のライブラリに分割されました。
そのため、アプリで使用する機能に合わせて新しいライブラリへ移行する必要があります。

# 対応
Google Play Core Library が提供している機能のうち、私のアプリで使用していた機能は「アプリ内レビューをリクエストする」のみでした。
そのため、Play In-App Review Library に差し替えるだけで対応することが出来ました。

```diff
-    implementation('com.google.android.play:core-ktx:1.8.1')
+    implementation 'com.google.android.play:review:2.0.1'
+    implementation 'com.google.android.play:review-ktx:2.0.1'
```

`ReviewManager` の仕様は旧ライブラリと変わらないため、ソースコードの修正は不要でした。

もし他の Google Play Core Library の機能を使用している場合は、該当する新しいライブラリへの移行が必要です。Google の公式ドキュメントを参照して、正しいライブラリを選んでください。

# 参考
https://developer.android.com/guide/playcore?hl=ja
今こそChatGPTを使わない検索力を身に付けよう!

何かと便利なChatGPTですが、世界中の企業の75%がChatGPTの利用を禁止・制限することを検討[^1]しており、実際の業務では使えないことが多々あります。

こんな今だからこそ自力で必要な情報にたどり着く力が必要になってくるのではないでしょうか？

そこで、Google検索で特に使える技法を実例を交えて紹介していきます。

## 1. フレーズ検索(" ")
ダブルクォーテーションで囲むことで、特定のフレーズを含むコンテンツを検索できます。
大文字、小文字の区別はされません。
```
"textile" 文字色
```
textileに完全一致したフレーズが含まれるページの検索ができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597961/d2d1bc06-f6b7-a115-e626-41af268c6f6e.png)

## 2. NOT検索(-)
マイナス記号を検索キーワードや他の演算子の前に付けることで、
そのキーワードや演算子を含まないコンテンツを検索できます。
```
"textile" 文字色 -markdown
```
markdownについて書かれたページを検索結果から除外することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597961/5c09f101-e73f-9919-deec-81c699fa92df.png)

## 3.AND検索
検索キーワードや他の演算子の間にANDを付けることで、検索キーワードや演算子が少なくとも一つ含まれるコンテンツを検索してくれます。
(ANDが小文字だと演算子ではなくキーワードとして認識されるので注意してください。)
```
"textile" AND "markdown" 文字色
```
ちなみにデフォルトがAND検索なので、
```
"textile" "markdown" 文字色
```
と同じ意味です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597961/5fc33996-0cbf-b600-3df3-257199eb4b56.png)

## 4.OR検索
検索キーワードや他の演算子の間にORを付けることで、検索キーワードや演算子が少なくとも一つ含まれるコンテンツを検索してくれます。
(ORが小文字だと演算子ではなくキーワードとして認識されるので注意してください。)
```
"textile" OR "markdown" 文字色 
```
とすれば、qiitaかMicrosoft Learnのサイトがヒットします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597961/d86ab27e-3cdf-2779-fb09-52476067df29.png)

(markdownが強すぎてtextileが上に出てこないですね(-_-;)

## 5. サイト指定検索
特定のサイトから情報を探したいときには「site:example.com」を使います。
たとえばQiitaから探したいときは
```
site:qiita.com "textile" 文字色 -markdown
```
とすればqiita.comのサイトだけを検索対象としてくれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597961/f2783a67-b6bf-3e81-3c28-1ddc5a8d06bf.png)

## その他
他にも、検索するときに使える演算子はたくさんあります。
Googleのサイトに載っていますので、うまく活用してみてください。

https://support.google.com/cloudsearch/answer/6172299?hl=ja&co=GENIE.Platform%3DDesktop&oco=0

[^1]: 2023年8月8日 Black Berry社の調査による
CDKPipelineで実装したパイプラインにスナップショットテストの工程を追加する

# 完成物
前回までの記事で書いたパイプラインにスナップショットテストの工程を追加したいと思います

https://qiita.com/hosomatu/items/1afb4c4dc01da1707abd


![Qiita Image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480176/540dc042-8940-94f2-365d-111c4fb6c7e6.png)

実現させたい動きとしては
1. ローカルでスナップショットを実行して内容に問題がなかったら'npm test -- -u'で更新する
2. 変更をmainにプッシュ
3. GitHubActionsによってS3にソースコードをアップロード
4. S3の変更をトリガーとしてパイプライン起動
5. **Buildの部分でテストを実行。1の工程を忘れていたらデプロイさせないようにする**
6. テストが通ったらデプロイ実行

## 実際のコード

公式を見ると「テストステージ」として追加する機能としてはなさそうだったので、既存のステージに対してステップを追加する形で追加する必要がありました。

他に参考になりそうな記事もなかったのでその方針で実装することにしました。

https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.pipelines-readme.html

lib/stacks/pipeline.stack.ts
```typescript
// CodePipeline、CodeBuildの作成。S3バケットをソースとしたパイプラインの構築
    const pipeline = new CodePipeline(this, 'PipelineName', {
      pipelineName: 'CdkPipelineName',
      synth: new ShellStep('Synth', {
        input: CodePipelineSource.s3(bucket, 'source-code.zip'),
        commands: [
          'npm ci',
          'npm run build',
          'npx cdk synth'
        ],
      }),
    });

    // テストステップを新しく作成
    const testStep = new ShellStep('RunTests', {
      commands: [
        'npm install',
        'npm test'
      ]
    });

    // デプロイステージにテストステップの工程を追加する
    pipeline.addStage(deployStage, {
      pre: [testStep], //ここを追記。ステージの事前アクションとしてステップを追加した。
    });
  }

```

<br>

ここについて
パイプラインの各フェーズ（ビルド、テスト、デプロイなど）が異なる独自の仮想環境なのでnpm installしないと、「jestがないよ」とエラーになりました。

```typescript
// テストステップ
    const testStep = new ShellStep('RunTests', {
      commands: [
        'npm install',
        'npm test'
      ]
    });
```

## 実際の挙動
きちんとスナップショットテストを更新せずにデプロイしようとしてもデプロイされません。
詳細を表示によって、どこで落ちたのかも確認することができます。

![test pipeline.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480176/3193e13f-125e-8fd1-2c9a-21b1a077442d.png)


テストをクリアするときちんとデプロイされます。
![success.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480176/5d514be4-cab9-f492-f695-dcb6d42cbce8.png)
Windows で Meta Quest に Split APK / XAPK のアプリをサイドロードするには

直接 ADB でするほうが確実．

# 参考

https://android.stackexchange.com/questions/221204/how-to-install-xapk-apks-or-multiple-apks-via-adb

https://androbranch.medium.com/what-is-an-xapk-file-and-how-do-you-install-one-on-android-05579efd1954

# 経緯

ハコスコの [GoodBrain](https://play.google.com/store/apps/details?id=com.hacosco.goodbrain) アプリを Meta Quest にインストールしたかったが，SideQuest では Extracting なんとかという処理がいつまでも終了せず，Meta Quest Developer Hub ではインストール処理がスタートしている素振りすら見せなかった．Lonely Cat Games の [X-plore File Manager](https://play.google.com/store/apps/details?id=com.lonelycatgames.Xplore) で確認すると `Split APK 5x` との記述がみられた．Google検索すると上記の記事が見つかり，Split APK についてはインストールができることを確認した．XAPK については備忘録としてメモのみ残す．

# Split APK の場合

フォーマットの実体は `.zip` ファイル．`.apk` ファイルとして出力された場合は拡張子を `.zip` に書き換えておく．展開すると，例えば GoodBrain [0.5.2] では以下のファイルが確認できる．
- base.apk
- split_config.arm64_v8a.apk
- split_config.en.apk
- split_config.ja.apk
- split_config.xxhdpi.apk

**手順１**
　当該フォルダをカレントディレクトリとしてコマンドラインを起動し，開発者モードが有効化された Android 端末を接続し，ADB 接続も許可しておく．また下記コマンドにより ADB 接続の対象を確認する，

```terminal
# 入力
adb devices

# 出力の例
List of devices attached
XXXXXXXXXXXXXX  device
emulator-XXXX   offline

# XXXXXXXXXXXXXX は実際には英数字で構成された ID になっている．
```

**手順２**
　下記コマンドにより，接続対象として特定のデバイス（Specific device）をオプションで指定したうえで，split されたすべての `.apk` を一括インストールする．（以下は GoodBrain [0.5.2] の例）

```terminal
# 入力
adb -s XXXXXXXXXXXXXX install-multiple "base.apk" "split_config.arm64_v8a.apk" "split_config.en.apk" "split_config.ja.apk" "split_config.xxhdpi.apk"

# 出力
Success
```

# XAPK の場合

実際には試していないためメモのみ残す．

**手順１**
　こちらも実態は `.zip` ファイル．`.xapk` で出力された場合は `.zip` に書き換える．フォルダに展開し，複数の apk があれば下記コマンドのようにインストールする．
```terminal
adb install-multiple "com.application.name.apk" "config.one.apk" "config.xxhdpi.apk"
# あくまでも例；必要に応じて書き換える
```

**手順２**
　Android フォルダに `.obb` がある場合は，下記コマンドにより当該ファイルを実機上にコピーする．

```terminal
adb push Android\obb\com.application.name /storage/emulated/0/Android/obb/
# com.application.name は書き換える
```

icon.png と manifast.json のファイルは無視して良いらしい．

# そのほか

あとインストール先の CPU アーキテクチャも要確認とのこと．
Quest の場合，ARM64 を含み忘れないように．
複数 apk を指定する場合の記述の順番は自由．

# 付録1）adb のインストール

1. 下記サイトから「SDK Platform-Tools for Windows」（`platform-tools-latest-windows.zip`）をダウンロードする．
https://developer.android.com/tools/releases/platform-tools
1. 中身の `platform-tools` フォルダを `C:\` 直下など任意の場所に移動する．
1. 当該ディレクトリをパスに登録する．

# 付録2）apk の入手方法

1. 普通に対象アプリを Google Play Store で Android 端末上にインストール．
1. 当該 Android 端末上の X-plorer アプリの App manager > Installed から対象アプリを選択し，Download 等の適当なフォルダにコピーする．
1. `.apk` もしくは `.zip`（あるいはまた別の形式）で出力されるので，当該ファイルを Windows PC にコピーする．
PowerShellに右クリックでペーストするとクォートが消える

## TL;DR
PSReadlineのバグ(っぽい)。
以下のいずれかで対処可能。

1. **[推奨]Ctrl+Vで貼り付け**
1. PSReadlineを無効化して貼り付け

```powershell:powershell
> Remove-Module PSReadline
```

## 環境
- Windows 10 Enterprise 22H2 (OSビルド：19045.4651)
```powershell:powershell
PS C:\Users\hogehoge> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.19041.4648
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.19041.4648
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
```

## 経緯

以下の「Windows 7 and later, using Microsoft Powershell:」部分を実行しようとしたらハマった。

[Supply basic auth headers](https://developer.atlassian.com/cloud/jira/platform/basic-auth-for-rest-apis/#supply-basic-auth-headers)

1. 上記コードをコピー
1. Powershellを開き、**右クリック**でペースト
1. Enterで実行
1. 以下のエラーが発生

```powershell:powershell
PS C:\Users\hogehoge> $Text = user@example.com:api_token_string
user@example.com:api_token_string : 用語 'user@example.com:api_token_string' は、コマンドレット、関数、スクリプト ファ
イル、または操作可能なプログラムの名前として認識されません。名前が正しく記述されていることを確認し、パスが含まれている
場合はそのパスが正しいことを確認してから、再試行してください。
発生場所 行:1 文字:9
+ $Text = user@example.com:api_token_string
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (user@example.com:api_token_string:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException
```


調べてみたところ、全角文字がおかしくなる現象を発見。
[PowerShellで全角文字を入力すると表示がおかしくなる問題について の補足](https://blog.shibata.tech/entry/2018/11/14/194112)

クォートについての記述はなかったが、試しにRemove-Moduleしてみたらクォートが消えることはなくなった。