OmniAICreator/Qiita-1.07M huggingface dataset:
データベース移行 と "リアルタイムデータレプリケーション"

こんにちは。インサイトテクノロジーの松尾です！

クラウド移行、ハードウェア更改、システムの作り直しなど、現在動いているデータベースのデータを、他の "新しい" データベースに切り替える "データベース移行" が必要となるシーンはとても多く存在します。そんなデータベース移行(データベースの切り替え)で、しばしば、データレプリケーションツールが使用されますが、そのあたりの仕組みが一見わかりづらいので、改めて記載しておきたいと思います。

## はじめに

データベース移行はどのよう行えばよいでしょうか？

基本的には、現在動いているデータベースから何らかの手段でデータを取り出し、そのデータを新しいデータベースに入れてあげることで実現できそう、ということはわかると思います。最もわかりやすいのは、たとえば、CSVファイルなどに出力し、そのCSVファイルを新しい方に入力してあげればよいのです。または、データベース製品に実装されている、バックアップツールでバックアップを取りリストアしてもよいかもしれません。

* 参考：[データベースサーバリプレイスの考え方](https://atmarkit.itmedia.co.jp/ait/articles/0911/02/news091.html)

CSVファイルにしてもバックアップファイルにしても、一度全てのデータを現在のデータベースから取り出し、それを新しいデータベースに入れる、という処理となります。そのため、データ量が非常に多い場合には、現在のデータベースからの全データ取り出し、新しいデータベースへの挿入には、非常に多くの時間(一般的にはサイズが大きければ数時間以上)がかかりそうであることは、容易に想像できると思います。そのため、移行作業に多くの時間がかかることになり、結果的に、システムの停止時間、すなわちビジネスの停止時間が長くなってしまいます。

そこで停止時間を少しでも短くするために活用されているのが (ニア)"リアルタイムデータレプリケーション" ツールです。

なお、データベースの移行にっともないデータベースの種類も変わるようなケースでは、データベースの切り替えに伴うアプリケーション側の改修も必要となりますが、この投稿ではそれは話題とはしていません。

## システム停止時間をできるだけ少なくしたい

ところで、停止せずにデータベースを切り替えたい、停止時間をできるだけ少なくしたい、というのはどういった業界でしょうか？

病院で稼働しているシステムやECサイト、その他、24時間365日、業務の根幹を支えるシステムが稼働し続けています。多少のメンテナンス時間は確保できたとしても、それをできるだけ短くし、業務への影響を最低限にしたいというニーズが非常に多く存在しています。

## システム停止時間を最小化したデータベース移行

システム停止時間を最小化したデータベース移行と (ニア)"リアルタイムデータレプリケーション"はどういった関係でしょうか？
一見関係ないと思われるかもしれません。が、仕組みは非常にシンプルです。

1. (ニア)"リアルタイムデータレプリケーション" ツールを使い、現在のデータベースと新しいデータベースのデータを事前に同期させておく (同じ状態にしておく)。
   * 最初に全データを移行します
   * 全データ移行後は差分のみを同期します
1. システム停止後にデータが完全に同じ状態であることの確認をする。→ 確認できたらデータ移行完了。
![db-migration-by-data-replication-tool.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/68631468-c061-daa9-acb2-a01deef0b803.png)

データの同期については、一度に全部のデータを持っていくと時間がかかるので、変更があるたびにその分を新しいデータベースに反映させ、両データベースを同じ状態にする、という考え方です。余談ですが、変更があるたびにその分を新しいデータベースに反映させることを CDC (Change Data Capture) と言います。

それぞれのデータベース移行の手法のメリットデメリットを簡単に整理すると以下となります。

|     | DBが備えているツール | CSVファイル経由 | (ニア)"リアルタイムデータレプリケーション" ツール |
| --- | --- | --- | --- |
| 概要 | DBが備えているツール(Data pumpやpgdumpなど)で作業する | CSVファイルに出力して取り込む | Qlik Replicateなどの(ニア)"リアルタイムデータレプリケーション" ツールを使用 |
| メリット | ・DB標準の機能で実施できる | ・DB標準の機能で実施できる<br/>・他のDBへ持っていきやすい | ・システム停止時間の最小化<br/>・他のDBへ持っていきやすい<br/>・GUIなどから操作 |
| デメリット | ・データ量に応じて時間がかかる<br/>・他のDBへは持っていくのが難しい<br/>・CLIから操作  | ・データ量に応じて時間がかかる<br/>・他のDBへ持っていくときはデータ型の違いなどを意識する必要がある<br/>・CLIから操作 | ・ツールでサポートしていないものがある場合の対応<br/>・ツールの費用がかかる |

* 参考：[Qlik Replicate詳細 | インサイトテクノロジー](https://www.insight-tec.com/products/qlik-replicate/details-qlik-replicate/)
* ※厳密には「リアルタイム」ではないので、ニアリアルタイムと記述
* ※ここでは論理レプリケーションツールを想定

システム停止時間の最小化が必要なら、Qlik Replicate のような (ニア)"リアルタイムデータレプリケーション" ツール の利用は必須と言えるでしょう。いっぽうで、そのような要件がない、すなわち移行作業時のシステム停止時間の制約が緩い場合は、DBが備えているツールの利用で十分とも言えますね。

## おわりに

本記事では、システム停止時間を最小化しつつデータベースのデータ移行を実現するにあたり、Qlik Replicate のような (ニア)"リアルタイムデータレプリケーション" ツール の利用が必須となることを紹介しました。

クリティカルなシステムのデータベース移行の方式を検討中の方、ぜひ、レプリケーションツールの活用を検討ください。
new Transform() したい

Unityで、```new Transform()```したくなる時ってありませんか？
目的としては、たとえば
```
for (int y = 0; y < ty; ++y)
{
    for(int x = 0; x < tx; ++x)
    {
        GameObject go = GameObject.CreatePrimitive(PrimitiveType.cube);
        Transform tr = GetRotAndPos(x, y);
        go.transform.position = tr;
    }
}
```
のように、新しいtransformのインスタンスを作って渡したい、などです。
残念ながら```new Transform()```はできませんが、いくつか方法があります。

:::note warn
```Transform tr = new GameObject().transform;```
でgameObjectを作って、値を渡して不要になったDestroy()する、というパワープレイはとりあえずやめておいたほうがよさそうです。
:::

多くの場合、Transformのインスタンスが欲しい=```{Vector3,Quaternion}```のセットを作って渡したいだけ、ではないかと思います。
```{Vector3,Quaternion}```の構造体を作って使用することもできますが、
無名構造体のような感じでタプルを使うこともできます。
```
    (Vector3 pos, Quaternion rot) tr = GetGeoPanelPos(x, y);
    go.transform.position = tr.pos;
    go.transform.rotation = tr.rot;

```
```
(Vector3 pos, Quaternion rot) GetGeoPanelPos(int _x, int _y)
{
    float rx = (float)_x * 4f;
    float ry = (float)_y * 4f;
    Quaternion rot = Quaternion.Euler(ry, rx, 0f);
    Vector3 pos = rot * -Vector3.forward * 10f;
    return (pos,rot);
}
```


または、上記の例のような場合は、GameObjectをInstantiateした後にGetRotAndPos()しても良いわけなので、
```
    GameObject go = GameObject.CreatePrimitive(PrimitiveType.cube);
    SetRotAndPos(ref go, x, y);
```
のように参照渡しを介してgo.transformに値を入れてしまうこともできます。
```
void SetGeoPanelPos(ref GameObject _go, int _x, int _y)
{
    float rx = (float)_x * 4f;
    float ry = (float)_y * 4f;
    _go.transform.rotation = Quaternion.Euler(ry, rx, 0f);
    _go.transform.position = _go.transform.rotation * -Vector3.forward * 10f;
}
```

他にもあれば教えてください。
クエリメソッド、遅延ロードについて

:::note info
最近データベースへの問い合わせを行うタイミングについて、考える時間があったので少しまとめてみました。
:::

### クエリメソッドとは
条件式やソート、グループ化、範囲抽出、結合などを行うための総称。
all/find/first/lastなどのメソッドでできることが限られているので、多くのクエリメソッドを使用する。

▼**主なクエリメソッド**
| メソッド | 概要 |
|:-----------|------------:|
| where       | 条件式で絞り込み        |
| order       | 並び替え        |
| select       | 列の指定        |
| limit       | 抽出するレコード数の指定        |
| offset       | 抽出を開始する数を指定        |
| group       | 特定のキーで結果をグループ化        |
| having       | GROUP BYでさらに制約を付与        |
| joins       | 他のテーブルと結合        |

##### クエリメソッドの特徴
:::note info
その場でデータベースにアクセスしない。
条件句を追加した結果をActiveRecord::Relationオブジェクトとして返すだけ。結果が必要になったところで、初めて、データベースへの問い合わせを行う。
これを**遅延ロード**という。
:::

またメソッド呼び出しを連鎖して、条件を積み上げることができることから、クエリメソッドの性質や記法のことを**メソッドチェーン**という。
メソッドチェーンを利用することで、複合的な条件も自然なコードで指定できるようになる。
Docker Dockerfileはbuild時のみ実行される

# 概要

- Dockerfileを編集しても全然反映されなくてちょっと詰まったので戒め的に記事にする。

# Dockerfileが実行されるタイミング

- Dockerfileはイメージがビルドされる時だけ実行される。
- なのでDockerfileを修正した時は下記のステップを踏んで試す必要がある。
    1. `$ docker-compose build`を実行してイメージからコンテナを作り直す。
    1. `$ docker-compose up -d`を実行してコンテナを起動する。

# ひとりごと

- 昔Dockerを学んだときに知ってたはずが全然覚えてなかった。Dockerfile修正して`$ docker-compose up -d`だけ実行して「なんでDockerfileが古い状態で実行されてるんだろう・・？」って思ってた。
【2023年9月版】RHEL9.2 で作る簡単Linuxルーター

# はじめに

Linuxでルーター構築する勉強したいな。。。

そうだ、ChatGPT先生に教えてもらいながら構築しよう

# 環境

- RHEL9.2
- インターネット側インターフェイス `eth0`、IPは `192.168.0.100/24`、インターネットゲートウェイは `192.168.0.254`
- 内側インターフェイス `eth1`、IPは `172.16.0.254/24`
- 外部から内部への接続はできないようにするだけで、セキュリティは考慮せず

# 構成

```plantuml
@startuml
cloud "インターネット" as internet
rectangle "ローカルネットワーク\n172.16.0.0/24" as local_network {
  collections "クライアント" as devices
  node "NATゲートウェイ(RHEL)" as natgw
}

natgw "192.168.0.100" -- "192.168.0.254" internet
devices -- ".254" natgw
@enduml
```

# 構築手順

## firewalld がなければインストール

```terminalnal
$ sudo yum install firewalld -y
$ sudo systemctl start firewalld
$ sudo systemctl enable firewalld
```

## パッケットのフォワードができるように設定

```terminal
$ echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
$ sudo sysctl -p
net.ipv4.ip_forward = 1
```

## ルーターとして動作するように `firewalld` を設定

```terminal 
$ sudo firewall-cmd --permanent --zone=external --add-masquerade
$ sudo firewall-cmd --permanent --change-interface=eth0 --zone=external
$ sudo firewall-cmd --permanent --change-interface=eth1 --zone=trusted
$ sudo firewall-cmd --reload
```

# さいごに

かんたんでしたね

# 参考資料

[Red Hat Enterprise Linux9 > ファイアウォールおよびパケットフィルターの設定 > 1.8. firewalld を使用した NAT の設定](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/9/html/configuring_firewalls_and_packet_filters/assembly_configuring-nat-using-firewalld_using-and-configuring-firewalld)
CSSを含めてVueコンポーネントをビルドする

## 経緯

ViteでビルドしたVueコンポーネントを、Vueアプリにnpmライブラリとしてimportし、使用した際にVueコンポーネントに設定したCSSが反映されていなかった。


####  SmpleComponentプロジェクト

SmpleComponent.vue
```vue
 <!-- SmpleComponentにCSSを設定 -->
<template>
    <div class='sample' /> 
</template>

<style>
.sample {
    background : black;
}
</style>

```

index.ts
```ts
export default {
    install(app: App, options = {}) {
        app.use(vuetify);
        app.component('SmpleComponent', SmpleComponent);
    }
}
```

####  import先のVueアプリ

```ts
// 自作したSampleComponentをnpmライプラリとしてimportする
import { SampleComponent } from '@lib-sample-parts'
import { createApp, h } from 'vue';

const app = createApp();

app.use(SampleComponent);
app.mount('#app');
```

```vue
<template>
    <!-- SmpleComponentに設定していたCSSが反映されない -->
  <SampleComponent />
</template>
```

## 解決策

**vite-plugin-css-injected-by-js**を使うと
Viteビルドの成果物のjsにcssを挿入するコードを含めることができる

https://github.com/marco-prontera/vite-plugin-css-injected-by-js

## 導入方法

install
```sh
$ # npm
$ npm i vite-plugin-css-injected-by-js --save-dev
$ # pnpm 
$ pnpm add vite-plugin-css-injected-by-js -D
$ # npm
$ yarn add vite-plugin-css-injected-by-js -D
```

vite.config.ts
```typescript
import vue from '@vitejs/plugin-vue';
import { defineConfig } from 'vite';
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js';

export default defineConfig({
    plugins: [
        vue(),
        cssInjectedByJsPlugin(),
    ]
});
```
ようつべ体験記3

[前回](https://qiita.com/matsukatsu/items/12090dc9aeb7807c50d0)

昨日、突然4000ほどインプレッションが伸びている事に気付きました。
詳細モードでコンテンツごとのインプレッション数を表示してみると、この一覧で4番目の動画のインプレッション数が4000程度増えている事がわかりました。
![詳細.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/75eac223-a44b-6d18-4fc8-86f7e2a03e2f.png)

どの経路のインプレッションが上がっているのか確認すると、93% が関連動画であることがわかりました。
どのような動画に関連づけられたのか確認してみましょう。
![動画の分析.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/c89ebab6-4488-8933-58e2-354485a89bca.png)

ん？YOASOBI、Snow Man、なにわ男子...？
![詳細.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/f7b8a65b-306f-e4e3-4cd5-74d9eadc92a3.png)
マスクした箇所はまあ、わからんでもないかなという動画なのですが、全く関係なさそうな動画に関連づけられていました。
このチャンネルはざっくりいうと旅行の動画を垂れ流している感じのチャンネルでして、アイドルを紹介する動画を作っている訳ではないのです。
アイドルの動画を見ている人にこのチャンネルの動画をインプレッションしたところで、そりゃ見んわなという感じです。

試しに `YOASOBI「アイドル」 Official Music Video` の動画のタグを [YouTubeTagChecker](https://chasksite.com/youtubetagchecker) で確認してみます。

> Video Title: YOASOBI「アイドル」 Official Music Video
Channel: Ayase / YOASOBI
Tags: YOASOBI, ヨアソビ, 推しの子, OSHINOKO, 横槍メンゴ, 赤坂アカ

なに一つ一致していません。

この動画は、この日が来るまではブラウジング機能での流入がもっとも多く、59% でした。
![動画の分析.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/eb10acd8-9029-5114-24b3-fa45787bc6e9.png)
ブラウジング機能とは
> トップページやホーム画面、登録チャンネルのフィード、その他のブラウジング機能からのトラフィック。

です。

トップページなどにこの動画のサムネイルが表示されていて、もしくはタイトルを見て、再生してくれた方が多かったようです。
コメントを頂いた方も書かれていたのですが、多分サムネイルが見ていただける動機になっていたのだと思います。
ただ、アイドル動画を見ている最中の人にこの動画のサムネイルを表示して、クリックしてもらえるほどの魅力は無かったのだろうと思います。

この日が来るまでは、関連動画の対象も妥当なものがほとんどでしたが、なぜ突然アイドル好きにゴリ押ししてしまったのか...
ググってもこれといった情報に行き当たりませんでした。

この動画の視聴者をみると、18〜24歳、男性、おそらく日本在住、という感じのようです。
![動画の分析.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/5920e5bb-a0c8-d019-410e-abe70ad976a7.png)

> 18〜24歳、男性、おそらく日本在住

このあたりの人にインプレッションしたら、再生数あがるんじゃね？的なことを YouTube の AI が考え、実施したらこうなったのかもしれません。

何かわかるかなと思って、YOASOBI の動画を再生してみました。
とくに、何もわかりませんでした。

[次回](https://qiita.com/matsukatsu/items/30500a0422089a02dbb2)
SwiftDataの@Modelを付けたクラスにStructやEnumを含めたい

待ちに待ったSwiftDataをアプリで使えないかと調べていたところ、躓きそうな箇所があったのでメモ。

# 問題
SwiftDataの`@Model`をつけたクラスにStructやEnumを置くとエラーが発生する。
このエラー`No exact matches in call to instance method`としか言われないので原因がかなりわかりづらい。
``` swift
import SwiftData

struct Location {
    let latitude: Double
    let longitude: Double
}

@Model
final class Home {
    let location: Location // エラー！！！
    
    init(location: Location) {
        self.location = location
    }
}
```

# 解決方法
プロパティに含めたいStructやEnumをCodableに準拠させる。
### Structの場合
単にCodableに準拠させればOK。
``` swift
struct Location: Codable {
    let latitude: Double
    let longitude: Double
}

@Model
final class Home {
    let location: Location
    
    init(location: Location) {
        self.location = location
    }
}
```

### Enumの場合
IntでもStringでも値型なEnumをCodableに準拠させればOK。
``` swift
enum ChocolateType: Int, Codable {
    case white
    case milk
    case sweet
    case bitter
}

@Model
final class Chocolate {
    var name: String
    var type: ChocolateType
    
    init(name: String, type: ChocolateType) {
        self.name = name
        self.type = type
    }
}
```

# 解説
WWDC2023の[Meet SwiftData](https://developer.apple.com/videos/play/wwdc2023/10187/?time=120)の２分あたりでサラッと複雑な値型はCodableに準拠することで使えると話している。

実際に`@Model`を付けると準拠される[PersistentModelのリファレンス](https://developer.apple.com/documentation/swiftdata/persistentmodel)から、エラーが発生している`getValue(forKey:)`について調べると、以下のいずれかに準拠した値が取得できるとわかる。
- PersistentModel
- RelationshipCollection
- Decodable

また同様にエラーが発生する`setValue(forKey:)`にもEncodableの制約がついたものがある。
よってPersistentModelは`@Model`を付けたクラスであり、RelationshipCollectionは名前からしてリレーション用だと思われるので、その他の`@Model`内で定義される変数はCodableに準拠することで値を格納・取得できるようにしていると考えられる。

[getValue(forKey:)](https://developer.apple.com/documentation/swiftdata/persistentmodel/getvalue(forkey:)-3o59k)
``` swift
func getValue<Value>(forKey: KeyPath<Self, Value>) -> Value where Value : Decodable
```
[setValue(forKey:)](https://developer.apple.com/documentation/swiftdata/persistentmodel/setvalue(forkey:to:)-xt24)
``` swift
func setValue<Value>(
    forKey: KeyPath<Self, Value>,
    to newValue: Value
) where Value : Encodable
```

以下憶測だが、これを踏まえるとSwiftDataの[`@Attribute(originalName:)`](https://developer.apple.com/documentation/swiftdata/attribute(_:originalname:hashmodifier:))を使うこととプロパティ名とスキーマでの名前を分けられるが、内部ではCodableのCodingKeysを使って実現しているのではないかと考えられる。

# 参考文献
- https://developer.apple.com/documentation/swiftdata/
- https://developer.apple.com/videos/play/wwdc2023/10187/
Japan AWS Jr. Champion活動記録 Vol.3

## はじめに
7月は、Jr.Champions有志による自主勉強会とJr.Champions Presentation Seminarが開催されました。業務都合で自主勉強会に参加できなかったので、今回はPresentation Seminarについて書いていきます。これからも活動に関して、定期的にアウトプットしていきます！

↓ 前回の記事はこちら ↓

https://qiita.com/am_i-12/items/c32a926c45dc8298e141

## Japan AWS Jr. Champion Partner Programとは
> [「Japan AWS Jr. Champion Partner Program」](https://aws.amazon.com/jp/blogs/psa/2023-aws-jr-champions/)とは、AWS Partner Network (APN) 参加企業に所属し、現在社会人歴 1 ～ 3 年目で AWS を積極的に学び、アクションを起こし、周囲に影響を与えている APN 若手エンジニアを選出しコミュニティを形成する、日本独自の表彰プログラムです。本プログラムでは、AWS に関して実践してきた技術的な挑戦や、コミュニティのリードなど自ら起点となって周囲に影響を与えているか、アウトプットを通じて周囲へ貢献しているか、という観点が評価の対象になります。2023 Japan AWS Jr. Champions に選出された方は、一定の技術力があり、社内の AWS コミュニティをリードでき、今後は Japan AWS Top Engineers そして Japan AWS Ambassadors になることが期待される皆様です。

2023年は[82名が選出](https://aws.amazon.com/jp/blogs/psa/2023-japan-aws-jr-champions/)されており、これから1年間Japan AWS Jr. Championsとして活動していきます。

AWS Summit Tokyoの表彰時に撮影した集合写真
![img_7571_720.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2851817/b6c165ed-6033-741d-c32c-2338b9be42fe.jpeg)


## プレゼン講座
Jr.Championsのアウトプット力を伸ばすべく、Jr.Champion Lead、Yukkiさんによるプレゼン講座が開催されました。


###  勉強会のテーマと流れ

プレゼンテーションの基礎の基礎　～伝える見せ方してますか～
1. 目的・テーマ
 2. プレゼンテーションのために意識すべきこととは？
 
3. コンテンツの質に関わる3つのポイント
    - コンテンツ
      - オーディエンス
      - キーメッセージ
      - ストーリー
    - テクニック
      - 視線誘導
      - トランジション（つなぎ言葉）
      - 話し方
    - パッション
4. オンラインのプレゼンTips
    - メリットとデメリット
    - 失敗あるある

### 印象に残ったポイント

-  プレゼン≠説明、プレゼン≠資料
   -  プレゼンは聞き手が **行動を起こしたくなるようにするため** に行うもの！
- 分かりやすさには一定程度の **誘導** が必要
- 視線誘導が難しいアーキテクチャ図は **グレーアウト** を活用！
- ぎちぎちのカンペだとパッションが乗りづらい
    - 箇条書きおすすめ
- オンラインでのプレゼンは **飽きが早い** ので変化を意識

### 感想
Yukkiさんの熱のこもったセミナーで、プレゼンについてみっちり学ぶことができました。プレゼンについて網羅的に学ぶのは初めてだったので、勉強になりました。

Yukkiさんは、Jr.Championの活動以外でも多くのプレゼンをされており、今までの経験を踏まえた良いプレゼンのエッセンスを学ぶことができました。話し方や資料作成だけでなく、マイクや照明などプレゼンに関わる全てに気を配り、それを自分の言葉で語れるくらい徹底しているのがすごいなと思いました。

自分は業務でプレゼンをする機会が少なく、毎回成果発表会前にあたふたしているので、プレゼンセミナーで教えていただいたポイントを意識して、次回の発表に臨みたいと思います。

以上です。ありがとうございました。
Liberty で JPA をためす

# 目的
前に構築した Libertyと、Db2, MySQL, Derbyで環境を使って、JPAをためします。

以前の記事はこちら
https://qiita.com/keniooi/items/17867bc73dbfb991c08f

# Gitレポジトリ
用意したGitレポジトリをクローンします。

```
git clone https://github.com/pdprof/db-connections
cd db-connections/derby-docker/
```

# DBイメージのビルド、Libertyイメージのビルド

Libertyと、Db2, MySQL, Derbyのイメージをビルドします。localhost 以外の docker を動かしているホストのIPアドレスをACCESS_HOSTに指定します。

```
export ACCESS_HOST=172.17.0.1
# export ACCESS_HOST=`hostname` # hostnameコマンドで返す値でアクセスできる場合
# export ACCESS_HOST=host.containers.internal # liberty から dbサーバーへの接続を podmanのホスト経由でする場合
./setup-docker.sh
```

# Db2の起動

今回はDb2を起動します。（setup-docker.sh 直後なら起動済みです）

```
docker ps # 起動状況の確認
./db2-start.sh
# ./mysql-start.sh # mysqlを起動する場合
```

# Db2にアクセスするLibertyの起動

```
./ds-db2-start.sh 
# ./ds-mysql-start.sh # MySQLにアクセスする場合
# ./ds-derby-start.sh # Derbyにアクセスする場合
```


# アプリケーションにアクセスして動作確認

./ds-db2-start.sh 

http://localhost:9080/db.connections/
にアクセスすると以下画面が確認できます。localhost部分は環境にあわせて変更します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262366/f18a7d3c-2ce9-be3f-a86a-0ffefaa15618.png)

Simple db access test をクリックすると TABLEの作成とデータ挿入が実行されます。JPA Action Test 以下のボタンでJPAのテストができます。実行される内容は代わり映えしませんが、出力はJSONにしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262366/d76ec298-4edd-7e6d-d917-35202970d658.png)


# トラブルシューティング

毎度のことですが、よくつかう dockerコマンドを書いておきます

接続がうまくいかない場合は、/config/server.xml で指定したホスト名（IPアドレス）が問題ないか、DBが動作しているか確認します。DBは 

```
docker ps # 動作中のContainer確認
docker logs -f mydb2 # ログ確認
docker restart mydb2 # 再起動
docker inspect mydb2 # container 確認
docker exec -it mydb2 bash # Container内状況確認
```

など試してみましょう。mydb2の部分を docker ps で確認した container の別IDにすることでLiberty側も確認できます。

# まとめ

JPA, DBの環境を構築をしてテストをすることができました。
Qiita 始めてみました

## 自己紹介・プロフィール
工学系の学部2年生です！
趣味で軽くプログラミングを勉強しています．
学びがあったり共有したいことがあれば記事を書いていこうと考えています．
成るべく見やすく分かりやすい記事を書いていきたいです．
記事の内容は初心者〜中級者向けを目指しています．

### 勉強してるもの，学びたい事
プログラミング:  Python, C, MATLAB, JavaScript
その他: CAD, CFD, 電子工作

## Qiitaを始めた動機
理由は主に4つあります．
1. 文章力，情報整理力を上げたい．
今後，大学生活の中でも社会に出てからも，資料を作成したりEssayを書いたりする機会が沢山ある．そのような時のために情報を整理したり，まとめる力を伸ばしたい．
2. アウトプット，記録したい．
アウトプットすると記憶が残りやすい，情報が整理しやすい，自分が分かってない箇所が自ずと分かるなど，メリットがある．
また，他人に読まれなくとも自分で見返してこんな事があったなと振り返る事が出来る．
3. 情報発信したい．
何か困ったり解決したことがあったら情報を発信していきたい．
プログラミングを勉強していく過程で，Qiita, Zenn, Reddit, StackOverFlowなど様々なプラットフォームの記事に救われているので，恩を返したい．
4. 取り敢えず記事を書きたい．
記事を書く手段は沢山ある．その中で，今すぐ始められて，環境が整っているQiitaを選びました．

## 最後に
最後まで目を通していただき，ありがとうございました！
HTMLとJSだけで簡単な星評価フォームを作る（CSSなし）

この記事にアクセスしてくださった方々の心境はおそらくこうでしょう。

**「星評価フォームをつくりたいけど、cssを書くのは嫌ダッ！！！」**

CSS嫌いなエンジニアが多いと聞きますが、この記事ではその期待に応えるべく、CSSを使うことなく、簡単な星評価のフォームを実装していきます。

<h1>はじめに</h1>
説明の前に、以下に当てはまる人は、この記事はあまりおすすめしません。
<ul>
    <li>小数点以下の評価を実装したい人（星が半分欠けているみたいなやつ）</li>
    <li>JSが嫌いで嫌いで仕方ない人</li>
</ul>
<br>

実装イメージはこんな感じです。
![スクリーンショット 2023-08-31 16.42.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3092163/7d74f3c9-4b75-ba8e-fbe2-1d08379e39ce.png)
星を押すと、押した星以下すべての星に色がつきます。これだけ。
![スクリーンショット 2023-08-31 16.43.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3092163/02117352-ae5d-1fb7-0098-18450b74602e.png)

<h1>事前準備</h1>
ディレクトリ構成は以下とします。

![スクリーンショット 2023-08-31 22.30.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3092163/daa9d08e-fa0a-e731-0709-2630f0560ffe.png)


ここで、押す前の星(`star_off.png`）と、押した後の星(`star_on.png`）のアイコン画像を`/static`ディレクトリに入れます。今回はこちらからお借りしましたアイコンを使用したいと思います。

https://www.freepik.com/icons/star

<h1>実装</h1>
<h3>HTML</h3>

```html:index.html
<form method="POST">
    <img src="static/star_off.png" id="1" class="star" width="30px" onclick="star(this)">
    <img src="static/star_off.png" id="2" class="star" width="30px" onclick="star(this)">
    <img src="static/star_off.png" id="3" class="star" width="30px" onclick="star(this)">
    <img src="static/star_off.png" id="4" class="star" width="30px" onclick="star(this)">
    <img src="static/star_off.png" id="5" class="star" width="30px" onclick="star(this)">

    <button type="submit" name="star-value">送信</button>
</form>
    
<script type="text/javascript" src="/static/main.js"></script>

```
見やすくするためDOCTYPEやらheadやらは省かせていただきます

<h3>JS</h3>

```javascript:main.js
function star(e){
    const stars = document.getElementsByClassName("star");
    const submit = document.getElementsByName('star-value')[0];

    for(s of stars){
        if (parseInt(s.id)<=parseInt(e.id)){
            s.src = "static/star_on.png";
        }else if (parseInt(s.id)>parseInt(e.id)){
            s.src = "static/star_off.png";
        }
    }
    submit.value = e.id;
}
```

DOMを使って、すべての星の画像と送信ボタンのタグを指定します。
星がクリックされると`onClick`イベントが発生し、すべての星を`for of`回して選択した星のid以下のidをもつ星に`star_on.png`、それ以外には`star_off.png`を適用するという至極単純なやり方です。
選択した星と同じ数字を、送信ボタンの`value`に付与することで、POSTしたときに受け取ることができます。この送信ボタンを`<input type="submit">`にしないのは、`submit`の`value`属性はボタン内のテキストを指すため、ボタン内の文字が変わってしまうのを防ぐためです。
工夫次第でもっといいアプローチができそうですが、とりあえずこんなところです。
コンテナセキュリティ本の要約

# はじめに
コンテナのセキュリティの勉強のために読みました。読んだ本の要約を行うことで内容の理解を深めることを目的としています。

# 今回読んだ本
[基礎から学ぶコンテナセキュリティ――Dockerを通して理解するコンテナの攻撃例と対策](https://www.amazon.co.jp/%E5%9F%BA%E7%A4%8E%E3%81%8B%E3%82%89%E5%AD%A6%E3%81%B6%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E2%80%95%E2%80%95Docker%E3%82%92%E9%80%9A%E3%81%97%E3%81%A6%E7%90%86%E8%A7%A3%E3%81%99%E3%82%8B%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%AE%E6%94%BB%E6%92%83%E4%BE%8B%E3%81%A8%E5%AF%BE%E7%AD%96-Software-Design-plus%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-%E6%A3%AE%E7%94%B0/dp/429713635X/ref=sr_1_1?adgrpid=150201939096&gclid=Cj0KCQjw9MCnBhCYARIsAB1WQVWW9Aut6h2OniJl_JERKyROFDTWKYo3WGXC6bIErKYpt8qs33Ja_KgaAvFjEALw_wcB&hvadid=665617000933&hvdev=c&hvlocphy=1009318&hvnetw=g&hvqmt=e&hvrand=4122265960573956693&hvtargid=kwd-2092987570458&hydadcr=27491_14701191&jp-ad-ap=0&keywords=%E5%9F%BA%E7%A4%8E%E3%81%8B%E3%82%89%E5%AD%A6%E3%81%B6%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3&qid=1693473050&sr=8-1)

著者：森田 浩平
発売日：2023/7/27
出版社：技術評論社

# 要約
### １章「コンテナ型仮想化とは」
- サーバー仮想化(VM)はOSを仮想化して実行する。起動が遅いが，分離レベルが強い。
- コンテナ型仮想化はホストOSの上で，コンテナ単位で独立したアプリケーションを実行する。起動が早いが，分離レベルが弱い。
- Dockerはクライアント/サーバーモデルを採用しており，次のような流れで処理を行う。dockerクライアント -> UNIXドメインソケットorTCP通信 -> Dockerデーモン(dockerd -> containerd -> runc)
- 高レイヤランタイムはイメージの取得やネットワークの管理，低レイヤランタイムへの通信などを行う
- 低レイヤランタイムはコンテナの作成を行う。

### ２章「コンテナの仕組みと要素技術」
- コンテナイメージは，ファイルシステムに対する変更の差分をレイヤとしてまとめたもの
- コンテナはLinuxカーネルが持つ複数の隔離技術で作られる。
- ケーパビリティは

### ３章「コンテナへの主要な攻撃ルート」
- アプリケーションへの攻撃，コンテナランタイムへの攻撃，レジストリへの攻撃など様々なアタックサーフェスがある。
- 外部からTCPで操作できるDocker APIに適切な認証が設定されていない場合，そこを起点にされる。
- runcなどのコンテナランタイム自体の脆弱性を悪用して，コンテナエスケープを起こす攻撃。
- ケーパビリティの設定不備による攻撃。
- cgroupなどでのリソースコントロールが十分ではないときの，コンテナ側からホストを巻き込んだ攻撃。
- コンテナに含まれるソフトウェアが古いとき，その脆弱性をついた攻撃。

### ４章「堅牢なコンテナイメージを作る」
- コンテナイメージのための脆弱性スキャンツールとして，Trivy, Clair, Grypeなどがある。
- Trivyはアプリケーションの依存ライブラリの脆弱性も検出できる。多機能で誤検知も少ない。
- ClairはREST APIを通してスキャンを実行，結果を取得する。
- docker build --secretを使って，レイヤに残さず安全にビルドすることができる。
- Docker Content Trustによって，イメージのデジタル著名をし，利用者が検証できる。
- Dockerfileでrootユーザーを使用しない
- コンテナレジストリでは，オフィシャルイメージなどの信頼できるイメージを利用する。

### ５章「コンテナランタイムをセキュアに運用する」
- 余分なケーパビリティの削除。
- docker-slimでSeccompプロファイルの自動生成ができる。
- cpu，メモリ，ストレージの制限。
- User Namespaceを利用することで，ホストとは別で独立したUID/GIDをNamespace内で持てるようになる。
- ランタイムを非rootで動かせるRootlessモードを使う。
- gVisor，Sysbox，Kata Containers等の分離レベルが強力なコンテナランタイムを利用する。
- CIS Benchmark，OWASP Docker Security Cheatsheet等でまとめられているコンテナのセキュリティベストプラクティスを確認する。

### ６章「セキュアなコンテナ環境の構築」
- 攻撃検知や被害範囲特定のために，ログやモニター監視が必要。
- Dockerでは，/var/lib/docker/contaiiners配下に標準出力，標準エラー出力を出力する。
- ホスト側でデーモンとしてログ収集ソフトを動作させることでコンテナのログを収集する。
- docker events，Dockerプラグイン，Sysdig/Falcoによるコンテナの実行ログの取得。
- Sysdig/Falcoでは，プロセス起動の検知やシステムコールの監視，ファイルへの書き込み検知，外部ネットワーク接続の検知等もできる。
- コンテナエスケープに備えて，ホスト側の監視も大切。

# おわりに
この記事はあくまで，本書に書かれている内容を私なりにかみ砕いて要約したものになります。実際の書籍には，もっとたくさんのことが書かれていますし，ソースコードによる詳しい説明や，Columnでの豆知識など非常に読み応えのあるものとなっています
DBMS(DataBase Management System)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457284/3634f064-8dfe-c8a1-c117-76c58e78f8a0.png)
１：デッドロック
　複数のトランザクションが、互いに相手のロック解放待ちをしている状態です。いわゆる、膠着状態に陥ってしまい、処理が進まなくなってしまいます。

　デッドロックが発生した場合、悪っ循環を断ち切るべく、いずれかのトランザクションを強制終了し再実行します。また、デッドロックを発生させないようにするには、資源をロックする順番をトランザクション間で一致させると良いです。

　例：デッドロックが発生しない　　　　　　　　　　　　デッドロックが発生する
　　Tr.1：　　　　　　　　Tr.2：　　　　　　　　　　Tr.1：　　　　　　　　Tr.2：
①資源Aロック　　　　①資源Aロック　　　　　　　①資源Aロック　　　　①資源Bロック
②資源Bロック　　　　②資源Bロック　　　　　　　②資源Bロック　　　　②資源Aロック
③処理　　　　　　　　③処理　　　　　　　　　　③処理　　　　　　　　③処理　
④資源A解放　　　　　④資源A解放　　　　　　　　④資源A解放　　　　　④資源B解放
⑤資源B解放　　　　　⑤資源B解放　　　　　　　　⑤資源B解放　　　　　⑤資源A解放　

２：障害回復
　システムに障害が発生した場合には、原子性や持続性を実現するために、障害回復処理を行う必要があります。障害回復処理には、２つの処理があります。それぞれログ（ジャーナル）ファイルに記載されている内容を基に処理を行います。

障害回復処理：
ロールバック：説明：障害発生時に実行中であったトランザクションの途中結果を全てキャンセルする。使うログ：更新前ログ
　ロールバック処理は、更新前ログを利用して、未コミットのトランザクションが行った更新を全て取り消す操作です。

ロールフォワード：説明：コミット済みのトランザクションの実行を復旧する。使うログ：更新後ログ
　　ロールフォワード処理は、更新後ログを利用して、コミット済みのトランザクションが行った更新をディスク装置に反映させる操作です。

３：チェックポイント
　チェックポイントは、メモリ中のデータをディスク装置に反映させる地点です。
　データベース中のデータを更新した場合、理想的には、すぐにディスク装置に更新内容を書き出すと良いです。しかし、ディスク装置へのアクセスには時間がかかるため、一旦メモリ上のデータを更新するだけにとどめるという対応をとります。更新データをまとめてディスク装置へ書き出すタイミングがチェックポイントです。
Google Search Labs を使ってみた。

## TL;DR
- Google Search Labs はGoogle検索時に概要を作成してくれる生成AI
- 従来のGoogle検索と合わせて実行できて便利
- テスト段階なので色々課題もありそう

## Google Search Labs とは？

「Google Search Labsについて詳細に教えて」とGoogle Search labsに聞いた結果は以下。
要するに、Google検索時に利用できる生成AI・AIアシスタント（テスト中）ということです。

```
Google Search Labs は、検索に関する大胆なアイデアや機能をテストするための登録制の新しい仕組みです。参加するユーザーは、初期段階の実験的なアイデアをテストし、フィードバックを提供することができます。
Search Labs は、2023 年 8 月 30 日に日本でも試験的に利用できるようになりました。Google アカウントを登録することで、パソコン向けの Chrome ブラウザや、スマートフォン向けの Google アプリで利用できます。
Search Labs に登録すると、検索結果の上に SGE (Search Generative Experience) の回答が表示されます。たとえば、「ロボット掃除機の選び方」と入力すると、SGE の回答が表示されます。
```


## 利用方法
Googleの右上に、気が付いたら見慣れないアイコンが。
これをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2459468/c7f7d385-b563-a46a-5727-e166a4c7054d.png)


チェックをオンにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2459468/11f348b8-1b2d-4f28-cd78-07b7ce4f0577.png)


以降Google検索時に **AIによる概要を生成しますか？** と表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2459468/0ce222cf-9f5a-0230-1159-5315fb4b9d4f.png)


**はい**を選択すると概要が作成されます。
**追加で聞く**を選択すると画面が遷移します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2459468/af38c8e0-e3ae-1de9-6ec9-fa3d747d9f95.png)


対話型のUIに。
~~既視感を覚えますね~~
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2459468/3558460b-7e83-082a-7197-618e791e9e52.png)


追加の質問をすると、先ほどの回答の下に追加の質問と回答が出力されます。
さらにその下には検索結果も続いています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2459468/dd1708fd-23f4-e929-21a5-cdbf572d2039.png)


右上のアイコンをクリックするとテキストと引用元対応がわかる画面に切り替わります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2459468/dbe105b4-d247-8791-caea-7944f292eccc.png)




## 所感
### いいと思ったこと

#### 記事本文を見なくてよい
~~SEO対策だけされた情報量は少ないが文章量だけが多い微妙な~~記事本文を見なくても、サクッと概要を知ることが出来ます。
単語の意味だけ知りたいとか、深い知識も求めていない時にスピードが上がりそうです。

#### 検索エンジンと生成AIが１ページにまとまる
ChatGPTはもちろん、Bing Chatも検索と生成AIは別のページになっていました。
なので、画面を往復することが多かったのですが、それがなくなるのは快適と思います。


#### 連続した検索結果が１ページにまとまる
キーワードを部分的に変えて連続してGoogle検索する場合もあると思います。
その場合、都度画面が切り替わりブラウザを戻ったり、複数タブにしたり、履歴を遡ったりといったことが必要でしたが、追加の質問とその回答含めて検索結果も１ページに残ります。
個人的にはこれが一番便利だなと思いました。



### 改善されてほしいと思ったこと

#### 検索時に表示されないことがある。
Google検索時に「AIによる概要を生成しますか？」が表示されないことがありました。

**会話**から強引に遷移することも可能ですが…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2459468/8b3d09ca-8dee-c4e0-e3b5-5761321edaea.png)


特に何も生成されず、検索結果を羅列するのみでした…
※検索キーワードが悪いだけかも？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2459468/8a533bf1-1396-b8fe-cc9a-35e1521f1876.png)




#### 品質にむら（？）がある

試験運用中なのでむらがある旨、記載されています。
むら（？）というか、検索結果を強引にまとめただけのコレジャナイ回答も結構あるので、改善されるといいなと思いました。
※以下の例だと、最終行の無理矢理感がすごい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2459468/f85296d6-fe0f-541e-e972-a4364ae6c9da.png)



## 参考



https://japan.googleblog.com/2023/08/search-sge.html

https://labs.google.com/search/experiments?is=ag
Apex:カスタム表示ラベルを動的にする方法

カスタム表示ラベル設定

名前：LBL_01
値：全部で{0}件のレコードあります。
```.apex
List<Object> objectList = [SELECT Id,×××__c From Object];//①Object型リスト
List<String> massageList = new List<String>();//②String型のリストを作成
massageList.add( String.valueOf( objectList.size() ) );

String message = String.format(System.Label.LBL_01, massageList);//List型に変換し、messageという変数に詰める
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.CONFIRM, message));//カスタム表示ラベルをメッセージとして出す。

```
②に入れたい値を追加する、順番で取り出すことができる、
例：追加するときは
massageList.add(a);
messageList.add(b);
messageList.add(c);

カスタム表示ラベルで指定する際
{0}の値はa
{1}の値はb
{2}の値はc
LaravelのDB接続で躓いた

# はじめに
Laravelでポートフォリオの作成中、アプリケーションで下記エラーが発生
```
SQLSTATE[HY000] [2002] php_network_getaddresses
```

# 原因
色々ネットで調べたところ.envの設定がdocker-compose.yamlとチグハグになっている事が原因とのこと。

```:.env
DB_CONNECTION=mysql
//修正前
DB_HOST=127.0.0.1
//修正後
DB_HOST=db
DB_PORT=3306
DB_DATABASE=database
DB_USERNAME=db-user
DB_PASSWORD=db-pass

```
```:docker-compose.yaml
  db:
    image: mysql:5.7 
    //↓.envのDB_HOSTをこれと合わせる。
    container_name: db
    environment: 
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: database
      MYSQL_USER: db-user
      MYSQL_PASSWORD: db-pass

```
以上の対応したところ、アプリの方は治ったーー！！と思ったが、、
今度はmigrateの際に同様のエラー発生。

散々悩んだ結果、migrateはコンテナの中で行わないといけないという事が判明。

コンテナ内でmigrateをすることで、無事解決。

docker完全に理解したと思ってたけどまだ知らない事だらけでした。
（StreamAPI）コンパイルエラー：「シンボルを見つけられません   シンボル:   メソッド stream()   場所: タイプ」「シンボルを見つけられません   シンボル:   メソッド isEmpty()   場所: タイプ」の原因がjavaのバージョンにありました

# はじめに
javaに触れ始め、複雑なPGをする中で処理速度改善のためにstreamAPIを知り、無邪気にfor文から置き換えて使っていた際に本記事の表題にあるエラーが表示されました。
コンパイルエラーとなり、buildができない動かない。。。
エラーメッセージで検索してもクリティカルな記事を見つけられず、StreamAPIのリファレンスを再度確認して、Javaのバージョンが原因であることが判明。
Javaのバージョンを無暗に変更することもできない状況で、なんとか動くように修正した内容を備忘（また類似の内容でつまずいた方へ）のために記事を作成いたします。

# エラー内容
①「シンボルを見つけられません
  シンボル:   メソッド isEmpty()
  場所: タイプjava.util.Optional<java.lang.Object>の変数 」

②「シンボルを見つけられません
  シンボル:   メソッド stream()
  場所: タイプjava.util.Optional<java.util.Map<java.lang.String,java.lang.Object>>の変数」

# 原因
javaのバージョン合わないことが原因

自分の環境：java11
デプロイ先の環境：java8

javaのドキュメントを確認すると、エラーで表示されたOptional.isEmpty()はjava11以降、Optional.stream()はjava9以降で動作することが記載されています。
https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/util/Optional.html

開発も終盤に差し掛かり、java8で動いているものをjava11に修正するリスクよりは、PGを修正する選択を取らざるを得ない状況だったため、修正いたしました。

### エラー内容①、②の修正前のソースコード
```java
// エラー内容①の元の処理
// listAのnameがtaroのデータが複数件ある場合、ageが最大のものをリストに追加する
Optional<Object> test1 = testA.stream()
                 .filter(i -> i.get("name").equals("taro"))
                 .max((s1, s2) -> Integer.parseInt(s1.get("age")) - Integer.parseInt(s2.get("age")))
                 .map(x -> x.get("age"));

// testAが空の場合、ageは20として設定する
// ここがエラーの原因
if (testA.isEmpty()) {
     age = "20";
}


// エラー内容②の元の処理
// listBのname毎にデータをグルーピング、ageが最大のものをリストに追加
Map<String, Optional<Map<String, Object>>> test2 = testB.stream()
     .collect(Collectors.groupingBy(data -> data.get("userName")
     Collectors.maxBy(Comparator.comparing(data -> (long) data.get("age")))));

// flatMapでtest2を平坦にする
// ここがエラーの原因
List<Map<String, Object>> testC = test2.values().stream()
     .flatMap(opt -> opt.stream())
     .collect(Collectors.toList());
```

### エラー内容①、②の修正後のソースコード
```java
// エラー内容①の修正後の処理
Object test1 = testA.stream()
                 .filter(i -> i.get("name").equals("taro"))
                 .max((s1, s2) -> Integer.parseInt(s1.get("age")) - Integer.parseInt(s2.get("age")))
                 .map(x -> x.get("age"))
                 .orElse(null); 

if (testA == null) {
     age = "20";
}


// エラー内容②の修正後の処理
Map<String, Optional<Map<String, Object>>> test2 = testB.stream()
     .collect(Collectors.groupingBy(data -> data.get("userName")
     Collectors.maxBy(Comparator.comparing(data -> (long) data.get("age")))));

List<Map<String, Object>> testC = test2.values().stream()
     .flatMap(opt -> opt.isPresent() ? Stream.of(opt.get()) : Stream.empty())
     .collect(Collectors.toList());
```

## 解説
■エラー内容①について
終端処理にorElse(null)を付記することで、test1の定義をOptional<Object>ではなく、Objectにする。
filter条件に一致しない場合はnullが返却させることで、testA.isEmpty()ではなくtestA == nullとすることで対応。

■エラー内容②について
Optional<Map<String, Object>>が存在することを確認して、いたらStream化して返す。
それをflatMapで配列に変換することで対応。

# 終わりに
javaを使った開発を行うにあたって、初歩的な「バージョンによって使えるライブラリが異なる」という原因のエラーでつまずきました。
javaのバージョンを変えずに、試行錯誤して叶えたい実装を行う必要がある際、本記事が一助となれば幸いです。

# 参考
・StreamAPI
https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/util/Optional.html
価値あるサービスと事業との関係について

# 悩み
最近、利用者に適した価値あるサービスってなんだろうかと…
事業側と同じ方向に向いているつもりだが、ぶつかるところが出てくるので最近よく考える。

# 自分の考えの軸
私は、開発会社側の人間なので、ITエンジニア、作り手中心でどうしても考えてしまうが、ITエンジニアは、新しい技術を用いて開発を通じてより品質が担保された価値の高いものを提供したいと考えている。

# 自分の考える価値あるサービス
自分が考える価値提供のあるサービスとは、ざっと下記の４つかなと

① 利用者が求めている本質的なニーズ（使う人の目的を叶える）
② 快適でストレスなく使える品質がある（ちゃんと使える）
③ 他にないワクワクさ、楽しさがある（これはニーズではなく、工夫だと考える）
④ 改善、変化が感じられる（いつも新しさを感じる）

①は、客観的な情報を集めて、第三者的な仮説とそれに至る根拠が必要だと考えます。
よく自分が好きなものとか、自分の考えがこうだ！ってのがあるけど、これが一番危険と考えます。
なので、自分の偏見や根拠や数字で語れない定性意見はできる限り除いて考えることが必要。

②、③は、Pdmやエンジニア、デザイナー、マーケッターなどチームメンバーの腕の見せどころと思います。

マーケティング中心のプロダクト開発とPdmが地に足のついた施策を話し合い取り組む。
そして、デザイナーが利用者をイメージしたUI/UXを考え、エンジニアが新しい技術を用いて、品質と保守性、運用性の高いシステムを提供する。

④は、グロースです。作って終わりが多い中、やはりシステムを育てるという視点が必要で、だからこそ、利用者に新しさを感じさせ、長く使ってもらえると思います。

# 価値あるサービスを実現するには

①から④を実現するには、「ヒト」、「モノ」、「カネ」、「時間」が重要で、開発チームを信用し、事業側がこれらを準備をしていく必要があります。
ただ、これにはコスト過多にならないように、機能を盛り込みすぎないように、計画が間延びしないようにPdmを中心にチームでコントロールが必要と考えます。

先程から話しをしている事業側から開発チームへの信用を得るのがほんとに一番難しいと考えます。
同じ方向性でも置かれた立場、状況が違うでしょう。なので、必ず意見をぶつかることが生じます。
お互い歩み寄りが必要だが、エンジニアも事業側も厳しいと話すにはそれぞれ理由があり、それを理解しないといけないということです。

システムは、昔と違い、今の時代はただ作るのでなく、セキュリティ面や運用・保守、グロースなど様々な視点が必要だし、事業側も事業成長しないと、システムもクソもないことはわかります。

自分の意見や無理な要求だけを述べるのではなく、お互いの立場になって、協力し、問題を解決できる意見を話せるよになることがこれからの企業のシステム開発には重要と考えます。

事業、開発含めてチームであること理解しないといけないということです。
【Git】プッシュしてないコミット確認

# はじめに
pushしてないコミットを確認するためのコマンドを知ったので記録しておきます

# やりかた
`develop`ブランチにpushしていないpushを調べる場合
```:ターミナル
git log origin/develop..develop
```

# おわり
これは覚えておきたいですね
自動運転AIチャレンジでローカルプランニングを自作する 3

## 今回の概要

前回の続きです．

https://qiita.com/yhisaki/items/c4973445a76aa148e789

今回は，前回実装したFrenet Frameを[aichallenge2023-sim](https://github.com/AutomotiveAIChallenge/aichallenge2023-sim)に組み込んでみた結果を見せたいと思います！

## 結果

調子が良いときは，障害物が避けられます．

<iframe width="500" height="400" src="https://www.youtube.com/embed/uqWGKCZiwjQ" title="awsim-obstacle-avoid" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

実際にコードを提出してみると，結果はこんな感じでした．
![Screenshot from 2023-08-31 21-01-37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320782/b9c3e22d-92f7-2140-0616-991f3dded1f0.png)

試行によってかなりばらつきが大きく，手元で実行しても，この点数に行くことは少ないため，ちょっと運が良かったのかなと思います😅


## ちょっとした工夫点

車が障害物に近づきすぎると，障害物が見えなくなってしまうという問題がありました．Lidarの取り付け位置の関係から，近すぎると見えないのかもしれません．そこで，工夫として，前回観測したオブジェクトが，現在の自己位置に近すぎる場合，現在の障害物に，前回の障害物を加える処理をしました．

```python
def update_object(prev_objs, cur_objs, cur_pos):
    prev_obj_xs, prev_obj_ys = prev_objs
    cur_obj_xs, cur_obj_ys = cur_objs
    cur_pos_x, cur_pos_y = cur_pos

    for prev_obj_x, prev_obj_y in zip(prev_obj_xs, prev_obj_ys):
        if np.hypot(cur_pos_x - prev_obj_x, cur_pos_y - prev_obj_y) < 5.0:
            print("update object")
            cur_obj_xs.append(prev_obj_x)
            cur_obj_ys.append(prev_obj_y)
    

    return cur_obj_xs, cur_obj_ys
```

また，障害物リストの中に，同じ障害物が入っている場合は，それを取り除く処理を実装しました．

```python
def remove_duplicate_objects(obj_xs: List[float], obj_ys: List[float]):
    # Initialize lists to store unique object coordinates
    unique_xs = []
    unique_ys = []
    
    for x, y in zip(obj_xs, obj_ys):
        is_unique = True
        # Calculate distances using numpy hypot function
        distances = np.hypot(np.array(unique_xs) - x, np.array(unique_ys) - y)
        
        # Check if the object is close to an existing unique object
        if np.any(distances < 1.0):
            is_unique = False

        if is_unique:
            unique_xs.append(x)
            unique_ys.append(y)
    
    return unique_xs, unique_ys

```

あとは，地道なパラメータチューニングです！
Apex List型→Map型へ変換し、IdをList型にする

Listをfor文で回せば、IdをListに追加していけますが、CpuTimeなどの制限を考えたときに、For文使わずMap型に変換してから、KeySetを使ってIdをListに詰めれます。

まずはfor文のやり方
```.apex

List<Object> objectList = [SELECT Id,×××__c From Object]
List<Id> ids = new List<Id>();
for ( Object ob : objectList ) {
            ids.add( ob.Id );
}
 
```

Map型を使う場合
```.apex

List<Object> objectList = [SELECT Id,×××__c From Object]
Map<Id, Object> obMap = new Map<Id, Object>( objectList );//List型→Map型へ変換し
List <Id> ids = new List<Id>(obMap.keySet());//keySetをつかってMapのIdを取り出す
```
【iRIC v4】マウスのホイールによる拡大縮小の仕様が変更されました。



# **最新版のiRIC v4ではマウスホイールでの画面の拡大縮小操作の際に`ctrl`を押すことが必須です**

2023年8月31日にリリースされたiRIC v4の`4.0.0.6909`においてプリプロセッサーウィンドウや可視化ウィンドウでの拡大・縮小の操作の仕様が変更されました。

変更前まではマウスホイールだけでも拡大縮小が行えていましたが、変更後は`Ctrl`キーを押しながらマウスホイールを回転させることで拡大・縮小操作を行います。


Ctrlキーを押すことに慣れていない人は、しばらく操作が不便に感じるかもしれませんが、iRICで表示される`マウスヒント`では元々Ctrlキーを押しながらマウスホイールで拡大・縮小と書いてあるので、今までマウスホイールだけで拡大・縮小できていたのがバグで今回正常な動作に治ったともいえます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2626673/ae6c4265-7e4e-bafd-9dee-f02185b2173c.png)

仕様変更後は、Ctrlを押している時だけサブウィンドウ内の拡大・縮小がされ、Ctrlを押していない時はiRIC GUIの画面全体がスクロールするようになっています。

![anime.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2626673/acc10a25-8bfa-230c-ac2b-df85290e862c.gif)



# 余談(修正の経緯)

修正前のマウスホイールのみで拡大・縮小が行えていた状況では、複数のサブウィンドウがあり、いくつかが描画範囲外に配置されている場合、拡大・縮小と同時にiRIC GUIの画面全体がスクロールされてしまう不具合があり今回のように修正されました。

（症状例を載せようと思いましたがiRICをバージョンアップしてしまったので画像が載せられません）
PowerAutomateForDesktopの技術検証⓷

　今回の技術検証は、1000ノード以上の大規模ツールを想定してPowerAutomateDesktopでどこまでできるのか見ていきたいと思う。
ちなみに、参考文献は以下の二つである。

＜参考文献＞
１，UiPathとPower Automate Desktopを比較してみる
https://developers.gmo.jp/7797/

２，PADを使ってみよう ー （上級編）複数のデスクトップフローを共通モジュール化して構成しよう
https://qiita.com/YutoKa/items/28c48a9c6d8b4afebceb

# 検証

　実際に、20くらいアクティビティを並べると以下の通りになる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3533364/34fc65cb-833d-cc2f-701c-262b9e68e99f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3533364/e615582e-debd-7f24-34bb-ea2a41ad81bc.png)

 PowerAutomateDesktopでは、フロー作成の際にUiPathで言うところの「シーケンス」しか使えず、「フローチャート」が使えない。そして、御覧のとおり一つのフローに１００ノードも載せると運用の時に大変な印象がある。

　また、サブフローをいくつか作成してノード数を稼ごうとすると以下のようになる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3533364/d7b0cc6c-8e4e-1558-d8cc-5fe52c4f037a.png)

　つまり、WinActor同様に横に広がっていく形式になりそうではある。

　このように確かに、大規模開発というより小規模開発に適しているというのは一定程度理解できる。ただし、PowerAutomateDesktopはWinActorと違い、UiPathと同様に他のデスクトップフローを呼び出せる。

　この方法を用いれば、ノード数の増加は可能かもしれない。
SparkからOracleへのjdbc接続時に沼った話

Databricks上のPySparkからjdbc経由でOracleに接続するパフォーマンスチューニングをしていたら解消までに沼ってだいぶ時間を要したので、備忘として書き残しておきます。

# やっていたこと

https://spark.apache.org/docs/latest/sql-data-sources-jdbc.html

Databricks上で上記参考にpartitionColumn、lowerBound、upperBoundにタイムスタンプ列を使ってreadしようとしたところ、以下のようなエラーが発生

~~~Python:PySpark
jdbcDF = spark.read \
    .format("jdbc") \
    .option("url", <URL>) \
    .option("dbtable", <table>) \
    .option("user", <user>) \
    .option("password", <password>) \
    .option("numPartitions", spark.default.parallelism) \
    .option("partitionColumn", <TIMESTAMP>) \
    .option("lowerBound", "2018-01-01 00:00:00") \
    .option("upperBound", "2022-12-31 23:59:59") \
    .load()
~~~

~~~Error:Error
ORA-01843: not a valid month
~~~

# 結論

~~~Python:PySpark
jdbcDF = spark.read \
    .format("jdbc") \
    .option("url", <URL>) \
    .option("dbtable", <table>) \
    .option("user", <user>) \
    .option("password", <password>) \
    .option("numPartitions", spark.default.parallelism) \
    .option("partitionColumn", <TIMESTAMP>) \
    .option("lowerBound", "2018-01-01 00:00:00") \
    .option("upperBound", "2022-12-31 23:59:59") \
    .option("sessionInitStatement", "ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD'  NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'") \
    .load()
~~~

sessionInitStatementに複数指定する方法が分からなかったり、色々な所に沼ポイントがありました。  
（はじめは以下のように書いて通らず苦しんでいた。sessionInitStatementを複数回やると一番最後のもので上書きされる。）

~~~Python:誤った設定値の例
.option("sessionInitStatement", "ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'") \
.option("sessionInitStatement", "ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD')
~~~


突破口になったのは以下の構文でセッションパラメータを抜いてくることでした。
これで「最後に指定した奴しか効いてない」に気付けました。

~~~Python:PySpark
jdbcDF = spark.read \
    .format("jdbc") \
    .option("url", <URL>) \
    .option("query", "SELECT * FROM nls_session_parameters") \
    .option("user", <user>) \
    .option("password", <password>) \
    .load()
~~~

ちなみに以下のようにHH24:MI:SSまでで止めてしまうと、lowerBoundからupperBoundまでの間の計算でほぼ確実に秒に端数が出るので以下のようなエラーになります。

~~~Python:誤った設定値の例
.option("sessionInitStatement", "ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS'") \
~~~

~~~:Error
ORA-01830: date format picture ends before converting entire input string
~~~
同じところでハマる人の目に留まりますよう。
【作業メモ】Google CloudのNatural Language APIでさっくりエンティティ分析

## はじめに

APIのお勉強として、コマンドプロンプトからNatural Language API叩いて文章内のエンティティを抽出してもらう。
gcloudコマンドは既に使える状態なので、そこら辺の話は割愛。

ドキュメントは以下

https://cloud.google.com/natural-language/docs/basics?hl=ja

## 1. APIを有効化する

Google Cloudコンソールの[APIとサービス] → [ライブラリ]から「Cloud Natural Language API」を有効化する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/34bb6430-ea86-2904-8217-a98f10376438.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/4dca6a9c-b9f6-e86d-b76d-d75b9b305324.png)

## 2. API叩く
--contentは自由に書き換えてどうぞ
```
gcloud ml language analyze-entities --content="I like to write a shell script."
```

こんな感じで返ってくるよ
```
{
  "entities": [
    {
      "mentions": [
        {
          "text": {
            "beginOffset": 18,
            "content": "shell script"
          },
          "type": "COMMON"
        }
      ],
      "metadata": {},
      "name": "shell script",
      "salience": 1.0,
      "type": "WORK_OF_ART"
    }
  ],
  "language": "en"
}
```

## おわりに
とりあえずAPI何かしら叩いてみたかったので試した
レスポンスの項目の意味調べるのが次回の宿題かな
テスト102

```vb

Sub reg_test()
'特定のファイルを取得する
Dim reg As Object
Dim str1 As String
Dim str2 As String
Dim str3 As String

str1 = "DOGテスト12345678.xls"  'True
str2 = "DOGテスト12345678.xlsx"  'False
str3 = "AADOFテスト12345A78.xls"  'False...〆(･ω･ )ﾒﾓﾒﾓ

Set reg = CreateObject("VBScript.RegExp")

With reg
    .Pattern = "^DOGテスト\d{8}\.xls$"   'パターンを指定
    .IgnoreCase = False       '大文字と小文字を区別する(False)、しない(True)
    .Global = True           '文字列全体を検索する(True)、しない(False)
End With


If reg.test(str1) = True Then

    MsgBox "一致"

Else

    MsgBox "不一致"

End If

Set reg = Nothing

'-------------------------------------
Set reg = CreateObject("VBScript.RegExp")

str1 = "DOGテスト（千葉）ABC_99988877_456789.xlsx"  'True
str2 = "ああDOGテスト（東京）ABC_99988777_456789.xlsx"  'False
str3 = "DOGテスト（千葉）ABC_991888771_456789.xlsx"  'False

With reg
    .Pattern = "^DOGテスト（千葉）ABC_\d{8}_\d{6}\.xlsx$"   'パターンを指定
    .IgnoreCase = False       '大文字と小文字を区別する(False)、しない(True)
    .Global = True           '文字列全体を検索する(True)、しない(False)
End With

If reg.test(str1) = True Then

    MsgBox "一致"
    
Else

    MsgBox "不一致"

End If

Set reg = Nothing

End Sub
認証と認可の話

# はじめに
仕事柄 サービス間のSSO などの話をしている中で、認証と認可を区別していない方を見受けるのでまとめてみました。
認証と認可はセットに扱う事が多いので、混同しやすいのだと思います。

## 認証
誰(Identity) を確認することです。
一般的には、IDとパスワードを連想すると思います。

## 認可
利用権限を与えることです。
一般的には、電車の定期券などを考えてもらえば良いと思います。

定期券には利用権限があって、許可された範囲なら電車に乗ることが出来ます。
間違えやすいのは、許可単体では認証の仕組みが無い事が多いです。
拾った定期券でも電車に乗れてしまうという事です。

# 認証認可の仕組み
世の中には色々な認証や認可の仕組みがあります。
認証認可の中で一般的な仕組みをいくつか紹介します。

## 認証でよく使う仕組み

### IDとパスワード
誰？を判断するための ID と、不正に利用させないため本人しか知り得ないはずの文字情報で判断します。

### 3要素認証
指紋などの生体情報を使用したり、スマホにショートメールを送信したり
IDとパスワード以外に、本人だと証明できる3つ目の物理的な情報を利用します。

### SAML認証
認証サーバが発行するXML形式のマークアップ情報を使用した認証の仕組みです。
認証情報を複数サービスで利用することが可能で、SSO( シングルサインオン )を実現するために利用することが多いです。

## 認可でよく使う仕組み
### OAuth
認可サーバが発行するアクセストークンと呼ばれる情報を持って認可状況を確認します。
サービスを跨いだ認可が可能で SSO と混同されやすい。
こちらは、「◯◯サービスに対して利用情報を公開して良いですか？」と確認させるための仕組み

# まとめ
認証と認可は似て非なる物です。
混同してしまうと、機能外部分の作り込みが増えてしまうのでお気をつけ下さい。
RailsアプリでAmazon SQS のメッセージを受信しよう

aws-sdk-rails というgemを使うと、SQSをキューにしてActiveJobsを動かすことができます。
今回はsidekiqユーザーに向けてその使い方を紹介したいと思います。

とはいえsidekiqを使わないというわけではないです。

# 受信設定
AWSの認証設定をしているとして、受信の設定をします。

```yaml:config/aws_sqs_active_job.yml
queues:
  my_queue: 'https://sqs.ap-northeast-1.amazonaws.com/1234567890/sqs_queue'
```
みたいな感じでQueueを設定します。このURLはAWS上で作成したSQSのエンドポイントを指定します。

でもって、Jobは普段通り、
```ruby:app/jobs/my_job.rb
class MyJob < ApplicationJob
  queue_as :sqs_queue
  def perform(x, y)
    ...
  end
end
```
という感じで作ります。
SQSでポストするデータは、こんな感じで作ればよいです。
```json
{"queue_name":"sqs_queue",
 "job_class":"MyJob",
 "job_id":"<id>",
 "arguments":["<custom_id>", ...]}
```

あとは、Rails.root で以下を実行すれば処理できます。
```sh
bundle exec aws_sqs_active_job --queue sqs_queue
```
ここでのミソは、ActiveJobの設定はしていないってところです。

なので、これまで通りsidekiqを使い続けられます。

# 応用 SES受信
やっぱり一番の応用は、メール受信ですね。
* SESでメール受信
* S3にメールを保存
* EventBridgeに保存イベントを通知
* EventBridgeで通知されたイベントを上記のメッセージフォーマットに書き換えてSQSへポスト
* SQSをリスンしてS3に保存されたメールを解析し、処理を行う。

こんな感じでメール受信が実装できます。
個人的には Action Mailboxよりコントロールしやすい感じがします。

そのほかにも、動画のエンコードなどlambdaの処理の完了通知とか、そういった応用が考えられます。

AWSでできることはやってもらって終わったら通知してもらうっていうのが楽に安定するシステムを作るコツかなぁなんて思います。
4ヶ月ほどVisual StudioでGitHub Copilot使ってみた感想

## はじめに

Visual StudioでGitHub Copilotを4月くらいに導入し、結構面白い機能だったのでまとめてみます。
Copilot ChatやCopilot Xあたりも気になるので、機会があれば試してみたいです。

## インストール

インストール手順はMicrosoftのドキュメントの通りです。

https://learn.microsoft.com/ja-jp/visualstudio/ide/visual-studio-github-copilot-extension?view=vs-2022

プランは基本有料なのですが、個人プランは30日間の試用期間があり、その間に有用性を感じたので有料でもその後も使い続けています。

## どんな感じか

簡単に言えば以下のような感じに次に記述するコードを提案してくれる機能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1062357/83168ab8-ef64-ce05-83af-723d2d950f25.png)

これは私が要素を上に移動するMoveUpコマンドを実装しており、次にMoveDownも実装したいなぁ～と思っていたところドンピシャなコードを提案してくれて、ついスクリーンショットを撮ってしまったところです。

この例は実装コードでしたが、コメントも前後の内容を踏まえて良さげな文章を提案してくれたり、実装コードに対してコメントを入れようとすると実装をいい感じに言語化してくれたコメントを提案してくれます。


これによりTabキーを押していくだけでどんどんコードが出来上がり、特に同じような記述やコメントが複数回出てくるような単体テストの実装ではTabキーを押していたら実装が完了してしまったこともありました。

## 思ったところ

そもそもの実装コードが充実（充実とは単純な実装量だったりコメントでの説明が豊富という意味です）している場合により効果が発揮されると感じました。
基本的にすでに記述されている内容のフォーマットだったり書きっぷりを真似して提案してくれるので、提案するための情報が多ければ多いほど、より利用しやすい提案をしてくれる感じがします。

逆に注意すべきだなと感じたのは、以下の点です。

- 提案が正しいわけではないため検証は当然必要。
    - すでに実装してあることのバリエーションを補完してくれるような場合は大概良いですが、コメントでやりたいことを記載して自動生成されたコードが本当に行いたいことを実現してくれているのかは要チェックです。コメントでは書ききれなかった要件を満たしていないことやパフォーマンスが低いやり方をしている場合もあります。
- 著作権やライセンス問題
    - 他のプロジェクトから取得された可能性があるコードかもしれないという使用面の問題もありますが、ビジネスのコードに使う場合収集されてしまう可能性もある点です。
    - それらは以下の設定ができるので会社のチームで導入などを考えている場合は設定を推奨します。
        -  Suggestions matching public code で公開されているリポジトリに含まれるコードとほぼ一致する場合は提案しないようにする。
参考：https://dev.classmethod.jp/articles/intro-github-copilot-for-business/#toc-5
      - [Githubのアカウントの設定]()からCopilotの改善に使われないようにする（以下はビジネスプランだとそもそも設定できなくなるみたいですが）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1062357/209488dc-4d23-1f88-8cec-27bfd7bfd77e.png)

## さいごに

ChatGPTあたりと同じで強力なツールですが、何も考えずに使うとかえって問題につながることもあるかもしれません。  うまく活用して簡単なことはCopilotにまかせて重要なことを考える時間を増やしていきたいですね。
Nuxt3+VitestでnavigateTo()をモック化する

# 結論
`vi.stubGlobal()`を使います。
`vi.spyOn()`や`vi.mock()`も試したがうまくいきませんでした。
ベストプラクティスではないかもしれないが参考になれば幸いです。
# 前提
`unplugin-auto-import`は導入済み
（詳しくは参考ページをご覧ください。）
# コード例
ホームボタンをクリックしたらトップページに遷移するような挙動のテストを行う場合
```test.spec.ts
import { vi } from 'vitest'

const mock = vi.fn();
vi.stubGlobal('navigateTo', mock);

describe('Unit Test', () => {

    // wrapperの設定は省略

    it('Home Button Clicked', () => {
        wrapper.find('#home-btn').trigger('clicked');
        expect(mock).toHaveBeenCalledWith('/')
    });
});
```
# navigateTo()とは
Nuxt3でルーティングに使われる関数です。
`router.push()`の代わりに使うことが推奨されています。
↓公式ドキュメント
https://nuxt.com/docs/api/composables/use-router

Nuxt3では自動インポートされるためimport文を書くことなく使用できます。
一方、Vitestでは自動インポートされないためモック化しなければなりません。
（モック化せずにテストを走らせると`ReferenceError`となります。）
# stubGlobal()とは
公式ドキュメントによると、JSDOMやNodeと関係のないグローバル変数をモック化することでできるらしいです。
↓公式ドキュメント
https://vitest.dev/guide/mocking.html#globals
# 参考ページ
- Nuxt 3 × Vitest で単体テストの実行環境を作る
https://zenn.dev/ytr0903/articles/8f4e3c0e529c6f
【プラクティス紹介】1分でさらっと分かる「星取表」

![skill_map.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3526800/185f8fa4-93c5-d381-c98c-c65963c8db1c.jpeg)

## プラクティス名（別名）
星取表　（スキルマップ、スキルマトリックス）

## プラクティスの目的・狙い
* メンバーの保有スキルの可視化
* 補強すべきスキルの明確化（リスク可視化）

## どんな時に使うか
* チーム立ち上げ時（必要なスキルが揃っているか確認したい時）
* メンバー編成検討時

## 実施手順
1. マトリックス表を作成し、縦軸にメンバー、横軸にプロジェクトで必要なスキル/知識を書く
1. 各自、現在のスキルレベルを**自己評価**し、記入する（評価基準はチームで定める）
1. 全体のバランスを見て、スキルアップしたい（させたい）箇所にマーキングする（1人につき1～2カ所程度）
1. 定期的に更新し、スキルアップ状況をフォローする

## アレンジ例
* 星取表にマークを記入しながら、自身のこれまでの経歴を紹介する
* 「☆」の人が「↑」の人のメンターとなる

## アンチパターン
* 星取表を人事評価に流用する（＝心理的安全性が失われる）
* 本人以外がスキルレベルを評価して記入する

## 参考情報

https://www.shoeisha.co.jp/book/detail/9784798153346


## こぼれ話（私的コメント）
星取表はチームの強みと弱みが可視化される非常に有用なツールですが、１つだけ落とし穴があります。それはあくまでも「可視化」のツールであって、スキルアップ自体は自己責任ということです。「↑」を書き入れただけで満足してしまうと、1か月後に見直した時に特に何も変わっていない、といった状況になりがちです。具体的にどうやってスキルアップを図るのかは別途考えないといけません。言われれば当たり前の話なんですが、なぜか星取表を眺めていると勝手にスキルアップしていくかのように錯覚してしまうから不思議です。


　[アジャイルプラクティス一覧へ戻る](https://qiita.com/kakashi_h5/items/20584998c5ca4594b9ea)
AWSへのアクセスだけ特別なルーティングをしたい

下記URLからjson形式でIPアドレスのリストが取れるそうです。
https://ip-ranges.amazonaws.com/ip-ranges.json

なので、これをルーティングテーブルとして設定するプログラムを書きました。

```ruby:routing.rb
require 'open-uri'
require 'json'
pp = URI.open('https://ip-ranges.amazonaws.com/ip-ranges.json') {|f| JSON.parse(f.read) }
pp['prefixes'].select{ |p| p['region'] == 'ap-northeast-1' }
              .map { |p| p['ip_prefix'] }
              .each { |ip| system "ip route add #{ip} via 192.168.1.254 dev eth0" }
```

この手のプログラムを簡単に直感的に書けるところがrubyの良いところですよね。