OmniAICreator/Qiita-1.07M huggingface dataset:
Hexabase TypeScript SDKでのワークスペース・プロジェクト・データストアを名前で取得できます

[Hexabase](https://www.hexabase.com/)は企業においても安心して利用できるBaaS（Backend as a Service）を提供しています。多くのBaaSがそうであるように、主にフロントエンド開発者に利用してもらいたいと考えています。そこで現在、TypeScript SDKの開発が進められています。

[@hexabase/hexabase-js - npm](https://www.npmjs.com/package/@hexabase/hexabase-js)

この記事ではHexabase TypeScript SDKにおいて、ワークスペース・プロジェクト・データストアの取得方法が簡単になった件を紹介します。

## インストール

インストールはnpmやyarnを使って行います。

```
# npmの場合
npm install @hexabase/hexabase-js

# yarnの場合
yarn add @hexabase/hexabase-js
```

## インポート

インポートすると、 `HexabaseClient` というオブジェクトが取得できます。

```ts
import { HexabaseClient } from "@hexabase/hexabase-js";
```

## 初期化

HexabaseClientを初期化します。

```js
const client = new HexabaseClient();
```

## ワークスペースについて

ワークスペースはHexabaseの中で、最上位のオブジェクトです。従来、ワークスペースを取得する際にはワークスペースIDを使っていました。ワークスペースIDは `6513e1c97f90e97937eb9eef` のようなユニークな文字列です。

```js
await client.setWorkspace("6513e1c97f90e97937eb9eef");
```

このワークスペースIDはHexabase管理画面のURLから取得できます。

```
https://app.hexabase.com/h/6513e1c97f90e97937eb9eef // ← このID
```

これでは分かりづらいので、ワークスペース名を使って取得できるようになりました。

```js
await client.setWorkspace("my_workspace");
```

このワークスペース名は、管理画面で設定するものです。

## プロジェクトについて

ワークスペースは複数のプロジェクトを保有します。従来、このプロジェクトもプロジェクトIDを使って取得していました。

```js
const project = await client.currentWorkspace.project("5f9e1c97f90e97937eb9eef");
```

こちらも同じく、管理画面で設定するプロジェクト名が利用できます。

```js
const project = await client.currentWorkspace.project("my_project");
```

## データストアについて

プロジェクトは複数のデータストア（RDDMSのテーブルに相当）を保有します。データストアもデータストアIDを使って取得していました。

```js
const datastore = await project.datastore("5f9e1c97f90e97937eb9eef");
```

データストア名を使って取得できるようになりました。

```js
const mainTable = await project.datastore("MainTable");
```

このように自分で設定した名前がそのまま使えるので、開発がスムーズに進められるでしょう。実際の流れは以下のようになります。

```js
import { HexabaseClient } from "@hexabase/hexabase-js";

const client = new HexabaseClient();

await client.setWorkspace("my_workspace");
const project = await client.currentWorkspace.project("my_project");
const mainTable = await project.datastore("MainTable");

const item = await mainTable.item();
```

## まとめ

Hexabase TypeScript SDKを使えば、VueやReactなどと連携したWebアプリを素早く開発できるようになります。2024年3月現在絶賛開発を進めていますので、ぜひ試していただいてフィードバックいただければ嬉しいです！

[hexabase/hexabase-js](https://github.com/hexabase/hexabase-js)
Saasfly - 現代的なSaaSアプリケーション作成を簡素化！

# 🎉 Saasfly - 現代的なSaaSアプリケーション作成を簡素化！

## 📢 プロジェクト概要

Saasfly は、スタートアップチームや個人の開発者のために丁寧に作られた、完全に無料のオープンソースのエンタープライズ級Next.jsテンプレートです。国際化サポート、SEO最適化、モノレポ管理など、すぐに使える豊富な機能を統合し、最新のSaaSアプリケーションを迅速にセットアップするのに役立ちます。最先端の技術スタックを採用することで、Saasfly はコードの品質とパフォーマンスの最適化を維持しながら、開発効率を確保します。

## 🌟 主な特徴

- 🆓 **完全無料**：Saasfly はMITライセンス契約に準拠したオープンソースプロジェクトで、無料で使用・修正できます。
- 🚀 **迅速な開発**：Saasfly は様々なスキャフォールディングとUIコンポーネントライブラリを提供し、アプリケーションプロトタイプの迅速な構築を可能にします。
- 🌍 **国際化サポート**：統合された国際化スキームにより、Saasfly はアプリケーションが簡単に複数の言語をサポートできるようにします。
- 🔍 **SEOフレンドリー**：Saasfly は検索エンジン用に最適化されており、検索結果でのアプリケーションのランクを向上させます。
- 📦 **モノレポ管理**：モノレポアーキテクチャを採用することで、Saasfly は複雑なアプリケーションコードの管理を簡素化します。
- 🛠️ **エンタープライズレベルのサポート**：エンタープライズユーザーに専門的なサポートとコンサルティングサービスを提供し、Saasfly の可能性を最大限に引き出します。

## 🙌 コミュニティ参加

Saasfly の成長は、コミュニティのサポートと密接に関係しています。スター⭐️をつけたり、問題を報告🐛したり、コードを提供🎁したりと、Saasfly の開発に参加していただくことを心から歓迎します。一緒により良いSaasflyを作り上げていきましょう！

## 👀 プロジェクトプレビュー

Saasfly の魅力を間近で体験したいですか？[デモサイト](https://show.saasfly.io)にアクセスして、直接ご覧ください！

## 🔗 関連リンク

- プロジェクトURL: [https://github.com/saasfly/saasfly](https://github.com/saasfly/saasfly)
- デモサイト: [https://show.saasfly.io](https://show.saasfly.io)
- ドキュメント: [https://document.saasfly.io](https://document.saasfly.io)

## 🚀 始めましょう

Saasfly でSaaS起業の旅を始めましょう。SaaS開発がこれまで以上に簡単になります！Saasfly に⭐️を付けて、サポートを示し、さらなる改善を促進してください！
入力規則の上限

## 1.エディションによって上限数は異なる。

公式

https://help.salesforce.com/s/articleView?id=000383591&type=1



## ２．エディションの確認


１．画面右上にある自分のユーザー名（またはアイコン）をクリック
２．[設定] を選択
３．左メニューの[管理] より [組織プロファイル] > [組織情報]をクリック
４．組織の詳細画面にて「組織のエディション」の項目を確認します。
Material Design In XAML Toolkit Ver 5.0.0

# WPFでMaterial Design In XAML Toolkitを使用する方法

:::note info
MaterialDesignThemes
バージョン：5.0.0
:::
## インストール
MaterialDesignThemesをNuGetパッケージの管理からインストールしてください。
単にアプリケーションをMaterialDesignにしたい場合にはMaterialDesignColorsは不要です。

## App.xamlに設定を追加
古い記事では`MaterialDesign3.Defaults.xaml`の部分が`MaterialDesign.Defaults.xaml`となっています。

```xml:App.xaml
<Application
    x:Class="MaterialDesign.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MaterialDesign"
    xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
    StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <materialDesign:BundledTheme BaseTheme="Dark" PrimaryColor="DeepPurple" SecondaryColor="Lime" />
                <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign3.Defaults.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

```

## MainWindow.xamlの編集
使用するWindowにスタイルを追加
```xml
<Window [...]
        Style="{StaticResource MaterialDesignWindow}"
        [...] >
```
【ASP.NET Core】EF Core Power Tools を使って Database First でモデルを作成

## 要約

[EF Core Power Tools - Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=ErikEJ.EFCorePowerTools&ssr=false#overview)
をインストールすると、Visual Studio 2022 で既存のデータベースからモデルを作成できる。

## きっかけ

ASP.NET Core Razor Pages を、Visual Studio 2022 を使いながら勉強中。
先にモデルを作成してからデータベースへマイグレーションするコードファーストの例はよく見るのですが、既存のデータベースからモデルを作成するデータベースファーストのやり方が分からずネットを検索していたときに見つけたのが、 EF Core Power Tools でした。

## インストールなど

自分の操作した環境

- **[EF Core Power Tools](https://marketplace.visualstudio.com/items?itemName=ErikEJ.EFCorePowerTools&ssr=false#overview)** Version	2.6.199　を、
- **Visual Studio Community 2022** Version 17.9.4　に対してインストール。*1
- ASP.NET Core プロジェクト
  - テンプレート： ASP.NET Core Web アプリ
  - ターゲットフレームワーク： .Net 8.0
  - NuGet パッケージで以下をインストール *2
    1. Microsoft.EntityFrameworkCore.SqlServer (8.0.3)
    2. Microsoft.EntityFrameworkCore.Design (8.0.3)
    3. Microsoft.EntityFrameworkCore.Tools (8.0.3)
    4. Microsoft.VisualStudio.Web.CodeGeneration.Design (8.0.2)
:::note info
*1:　EF Core Power Tools のインストール時に、対象とする Visual Studio を選択（確認）する画面が表示される。 
:::
:::note warn
*2:　これらの NuGet パッケージが全部必須かは分からないが、少なくとも　1.　2.　あたりは後からでもプロジェクトにパッケージを入れておかないと、ビルドエラーになる。
:::

[^1]: あああ

## 結果

Visual Studio のソリューションエクスプローラーで、プロジェクトを右クリックして「追加」→「新しい項目」を選択すると、下図のように、左側のリストに **Data** が表示されるようになり、これを選ぶと「**EF Core Database Wizard**」が出てくるようになる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/548326/ee4a8916-23fe-290e-9072-1ed45903f8b8.png)

あとは、このウィザードに従って進めていけば、多少の手直しは要りましたが、既存のデータベース（テーブル）からモデルを作成することが出来ました。

## 参考

[EF Core Power Tools](https://marketplace.visualstudio.com/items?itemName=ErikEJ.EFCorePowerTools&ssr=false#overview) の存在を知ったのは、このページから。

:::note
[Entity Framework Core 8 の計画 - Microsoft Learn](https://learn.microsoft.com/ja-jp/ef/core/what-is-new/ef-core-8.0/plan#ef-core-database-first-in-visual-studio)
:::

> **Visual Studio での EF Core Database First**
> 
> 価値提案: Visual Studio のすぐに使用できる Database First ツール。
> 
> EF Core Power Tools では、EF Core ツールの優れたビジュアル エクスペリエンスを提供します。 ただし、Power Tools は Visual Studio にすぐにインストールされないため、探すのが困難な場合があります。 .NET 8 の時間枠で、Visual Studio で EF Core ツールを簡単に見つけられるようにする予定です。 この作業の主な焦点は、既存のデータベース ("Database First" と呼ばれる) から EF モデルをスキャフォールディング (リバース エンジニアリング) することですが、他の領域も含まれる場合があります。

良く出来た素晴らしいツールなので、簡単に見つけられるように、早くなってもらいたいものです。
Truthを使ってみた

以前、Androidでの単体テストを[投稿](https://qiita.com/shimizu-you/items/ad8ad91b9b27c227d2a5)しました。
その際、アサーションはJunitのものを使っていました。

今回は、Googleが提供している[Truth](https://github.com/google/truth)を使ってみたいと思います。

### 導入

```gradle
testImplementation("com.google.truth:truth:1.4.2")
// Android用
testImplementation("androidx.test.ext:truth:1.5.0")
```

### 実装

```kotlin
@HiltAndroidTest
@Config(application = HiltTestApplication::class)
@RunWith(AndroidJUnit4::class)
class SampleTest {

    @get:Rule
    var hiltRule = HiltAndroidRule(this)

    @Inject
    lateinit var sampleRepository: SampleRepository

    @Before
    fun setUp() {
        hiltRule.inject()
    }

    @Test
    fun `整数テスト001`() {
        val result = sampleRepository.checkInteger(100)
        assertThat(result).isTrue()
    }
    
    @Test
    fun `整数テスト002`() {
        val result = sampleRepository.checkInteger(-1)
        assertThat(result).isFalse()
    }
}
```

### 比較

* JUnitの場合、検証した結果を事前に想定してアサーションを用意しないといけません

```
assertTrue(result)
```

* Truthの場合、assertThatにオブジェクトを渡すとそのオブジェクトに対しての検証内容を決めることが出来ます

```
assertThat(result).isTrue()
```

例えば、文字列を検証したい場合は、assertThatに文字列を渡すと何を検証出来るか分かるようになっています。

![スクリーンショット 2024-03-27 15.41.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148871/33b907a3-8661-c346-dac7-4d3d53432512.png)

```
assertThat("string").isEqualTo("string")

// 下記は、エラーとなります。
assertThat("string").isTrue()
```

* 数値の場合、このような検証も出来ます。JUnitでは、一致検証のみなので、このような検証は難しいです

```
// numberは、10より大きい
val number = 100
assertThat(number).isGreaterThan(10)
```


### まとめ

JUnitのアサーションでも問題ありませんが、直感的にアサーションが出来るTruthは、便利だと思います。
Android以外にJavaでもアサーション出来ますので、楽しい単体テストを作ってみてください。
【Github actions・Nextjs】firebaseにデプロイできない

# はじめに
Nextjsのアプリケーションをfirebaseにデプロイしているのですが、github actionsで自動デプロイしようとしたところうまくいかなかったので共有します。

# 問題
github actions上で以下のエラーが出ました。

```
   Thank you for trying our early preview of Next.js support on Firebase Hosting.
   During the preview, support is best-effort and breaking changes can be expected. Proceed with caution.
   The integration is known to work with Next.js version 12 - 14.0. You may encounter errors.
   Documentation: https://firebase.google.com/docs/hosting/frameworks/nextjs
   File a bug: https://github.com/firebase/firebase-tools/issues/new?template=bug_report.md
   Submit a feature request: https://github.com/firebase/firebase-tools/issues/new?template=feature_request.md
   We'd love to learn from you. Express your interest in helping us shape the future of Firebase Hosting: https://goo.gle/41enW5X

// ここに注目
Error: HTTP Error: 403, Permission 'firebasehosting.sites.get' denied on resource 'projects/tech-blog-774b8/sites/tech-blog-774b8' (or it may not exist)
Error: Process completed with exit code 1.
```

# 解決策
firebaseへデプロイするのに使用したサービスアカウントのロールを見直しました。

**「Firebase Develop 管理者」** というロールが必要でした。
(エラーログにもpermissionがないと出ています)

# 終わりに
すごく基礎的なところで躓いていました。chatgptに聞いてログの内容が分かったので、次からはしっかり読めると思います。permission errorが出ていたらロールが問題かも！って笑
OpsRamp Agent のアップデート方法

本記事では、OpsRamp Agentのアップデート方法について説明します。

# 概要
OpsRampでは、OpsRamp Agentをポータルからアップデートすることができます。また、自動的にアップデートするよう設定することもできます。

https://docs.opsramp.com/platform-features/agents/updating-agents/#update-agents

# 設定方法

Setup > Monitoring > Vistara Agent のアップデートからアップデート・設定することが可能です。

- 対象のデバイスにチェックを入れ、上部メニューの「許可する」をクリック後「今すぐアップデートする」、またはアクション欄の「今すぐアップデートする」からアップデートすることができます。
- 「許可する」をクリックすると自動アップデートが有効化されます。有効化するとOpsRamp Agentは、6時間ごとにアップデートがあるか確認し、自動的にアップデートを実施します。
- 対象のデバイスにチェックを入れ、上部メニューの「許可しない」をクリックするとアップデートを無効化することができます。
![agent-update.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2344939/679d1605-a7d6-efaa-4f19-06b6e17f8063.png)

Update欄が「許可する」、またはアクション欄が「今すぐアップデートする」(すでに「許可する」していた場合)となっている場合、インストールされているAgentより新しいバージョンのアップデートがあります。
Update欄が「Up-to-date」となっている場合、インストールされているAgentは最新のバージョンとなります。

---------------------------------------
[<img width = 250 height = 200 src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2344939/111de30a-4d33-aa33-5c7d-83d9015093e6.png">](https://www.vistanet.jp/knowledge-base/)
C#のTimerでハマったメモ


## C#のTimerは4種類ある

https://takap-tech.com/entry/2017/09/09/225342

この記事を読んで
「ほーんとりあえず`System.Timers.Timer`使っとけばOKみたいやな」
って思ってたらハマったのでメモ。

注：自分もまだ原理を理解しきれていないのであやふやな書き方をしている

## 結論

WindowsFormなら、`System.Windows.Forms.Timer`を使おう

## 元のソース

```csharp:View
public partial class View : Form, IView
{
    Presenter presenter;
    
    public View()
    {
        InitializeComponent();
        presenter = new Presenter(this);
    }

    // デザイナーで追加したタイマー
    Timer IView.timer { get => winFormTimer; }

    // タイマー作動時
    private void winFormTimer_Tick(object sender, EventArgs e)
    {
        // プレゼンターのメソッドを呼ぶだけ
        presenter.timer_Tick();
    }

    public void UpdateChart(Point[] points)
    {
        // 引数からグラフを更新する（割愛）
    }
}
```

```csharp:Presenter
class Presenter
{
    private IView view;

    public Presenter(IView _view)
    {
        view = _view;
    }

    public void timer_Tick()
    {
        // 外部と通信

        // 通信データを使ってグラフの描画点の配列を作成
        var points = new Point[30];

        // Viewのグラフ更新メソッドに渡す
        view.UpdateChart(points);
    }

    // 他にも必要に応じてtimerを止めたり動かしたりしてる
}
```

うーん…このtimer、プレゼンターでしか使っとらんし、`System.Windows.Forms.Timer`は精度良くないらしいし、ここはPresenterに`System.Timers.Timer`を置く形に変更してみよ！

## 変更後

```csharp:View
public partial class View : Form, IView
{
    Presenter presenter;
    
    public View()
    {
        InitializeComponent();
        presenter = new Presenter(this);
    }

    public void UpdateChart(Point[] points)
    {
        // 引数からグラフを更新する（割愛）
    }
}
```

```csharp:Presenter
class Presenter
{
    private IView view;

    private System.Timers.Timer timer;

    public Presenter(IView _view)
    {
        view = _view;

        timer.Elapsed += timer_Tick;
    }

    public void timer_Tick(object sender, EventArgs e)
    {
        // 外部と通信処理

        // 通信データを使ってグラフの描画点の配列を作成
        var points = new Point[30];

        // Viewのグラフ更新メソッドに渡す
        view.UpdateChart(points);
    }

    // 他にも必要に応じてtimerを止めたり動かしたりしてる
}
```

結果：**「外部と通信処理」の箇所で
「メッセージ フィルターはアプリケーションがビジーであることを示しています。」
って言われまくる**

## なんで？

__ぶっちゃけよくわかってない。__
なんとなくの理解で書くと…

- 通信処理はViewと同じスレッドで動く
- 通信処理には個数制限（セマフォ的な？）がある
- 通信処理とtimerが同じスレッドで動く場合、timerは通信処理と同期して動く
- そのため、個数制限があふれることはない
- 通信処理とtimerが別スレッドで動く場合、個数制限に関係なくタイマーが動く
- そのため、個数制限に関係なくタイマーは通信を要求し続け、結果**アプリケーションがビジー**になる

多分こう。

## 対策

おとなしく`System.Windows.Forms.Timer`を使うと元に戻った。
あるいは、`System.Timers.Timer`の`SynchronizingObject`プロパティにViewを設定してもいいらしい。
aws s3 laravel9 で アップロードと表示

awsのアカウントはすでにあること。

# 初期設定

検索ボックスに s3 と入力すりゃ設定画面が出てくる。
赤枠んとこにバケット名を入れる
で、設定も変更

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31338/5819a425-e285-1e24-6ed1-be7189c5d075.png)

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31338/fea54b4a-e9c4-98fa-b8b3-29a4c42620f2.png)


参考
https://qiita.com/dayjournal/items/c827a17917127bff3906

で、管理画面上から適当に画像をアップロードしてみる。

これだけでは画像が公開されない。


バケットポリシーを設定 → 「変更の保存」をクリック。今回は、Amazon S3からオブジェクトを取得するためのアクセス許可を付与。

![名称未設定-6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31338/512e4adf-43c1-d603-19de-5c42c9d64589.png)

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Statement1",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::バケット名/*"
        }
    ]
}
```

![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31338/85d8c1e6-74d4-056f-41a6-dc6f2386c34c.png)


にアクセスすれば、画像が見れる。

# IAMでアクセスキーとアクセスシークレットキーを取得

![名称未設定-7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31338/88dd0df1-86b1-662c-e706-22f1f58d02ee.png)

許可ポリシーに AmazonS3FullAccessしとく

で、このユーザーのアクセスキーとアクセスシークレットキーを取得する。

# laravel9の設定

まず、注意したいのが、すでにawsの情報は.envに入っているので、
新規項目を追加せずあるものに追記すること！！！3時間ハマった。
じゃないとenvの情報が消えlaravel側で空白になってしまう。


参考
https://taishou.ne.jp/laravel-s3-connect/


インスコ

```
composer require league/flysystem-aws-s3-v3
```

あれ？エラー？
```
composer update guzzlehttp/psr7 --with-all-dependencies
```

をやる。


awsのキーを書き込む
すでに.envにかかれているので項目は追加しなくていい！

```.env
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=ap-northeast-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
```

以上

# アップロードしよう

```hoge.php
    public static function test(request $request)
    {

        $localFilePath = public_path("/img/ibu.jpg");
        $s3FileName = time().'ibu.jpg'; // S3に保存するファイル名

        $fileContent = file_get_contents($localFilePath);

        $result = Storage::disk('s3')->put($s3FileName, $fileContent);

        if ($result) {
            echo 'アップロード成功';
        } else {
            echo 'アップロード失敗';
        }

        die();

        //略
```

これでアップロードできましたね
VBA　文字列をまとめてから消す

アクティブセルから並んでるデータをまとめて、その後行削除する
実行前
![前.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3681625/01e025a9-e283-9711-04c4-bd8fb44a3b34.png)
実行後
![後.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3681625/5b9dbfe0-b205-9b9b-1627-566126f890bf.png)


```.vba
Option Explicit
Sub Main()

#文字列の取得
	Dim w1, w2, w3, w4, w5, w6, w7, w8, w9, w0 As Variant
	w1 = ActiveCell.Value
	w2 = ActiveCell.Offset(1, 0).Value
	w3 = ActiveCell.Offset(2, 0).Value
	w4 = ActiveCell.Offset(3, 0).Value
	w5 = ActiveCell.Offset(4, 0).Value
	w6 = ActiveCell.Offset(5, 0).Value
	w7 = ActiveCell.Offset(6, 0).Value
	w8 = ActiveCell.Offset(7, 0).Value
	w9 = ActiveCell.Offset(8, 0).Value
	w0 = ActiveCell.Offset(9, 0).Value

#結合
	Dim connectWord As String
	connectWord = w1 & w2 & w3 & w4 & w5 & w6 & w7 & w8 & w9 & w0
	ActiveCell.Value =  connectWord

#消去
	Dim rowsNo As Integer
	rowsNo = ActiveCell.Row +1
	Dim i As Integer
	For i = 1 To 9
		Rows(rowsNo).Delete
	Next i
End Sub

```
【MS-Copilot（Designer）】使用上での注意点。


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

　今回は、MS-Copilot（Designer）を使用する上での注意点を書きます。これは私が知っている範囲です。

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

　ニュースは
　　ありませんので、
　　　本題にいきます。

　今回は、
　　同じDALL-E3を
　　　使っているBing改めCopilotでの
　　　　注意点を、私が知っている
　　　　　範囲で書きます。
***
　まずは、
　　普段ChatGPT＋DALL-E3を
　　　使っている私が、
　　　　なぜCopilotを説明するのか。

　目的は三つ。
　・オフィスで使うことを想定して
　・無料で利用できる
　・ChatGPTの代替

　Copilotは
　　Windowsで
　　　MSOfficeを
　　　　使っていれば、
　　　　　当然関わるものです。

　ある程度の
　　使い方、使い心地は
　　　知っておきたいものです。

　それに
　　無料で利用できるのも
　　　ありがたいサービスです。

　まぁ、
　　結局は、ChatGPT＋DALL-E3の
　　　代替でしかありませんけどね。
***
　代替とはいえ、
　　Copilotも
　　　内部的には
　　　　ChatGPT＋DALL-E3です。

　ところが、
　　そこには
　　　違いが存在していました。
***
　Copilotは
　　GPT-4を使っています。
　　（一部噂にはGPT-4 Turbo使用とか）

　ChatGPTと
　　同じエンジンです。

　画像生成AIも
　　同じくDALL-E3です。

　どこに違いがあるのか？

　構成は同じでも
　　制約や動作が
　　　異なっています。

　これは
　　Microsoftの
　　　考え方から来るのでしょうね。
***
　前置きが長くなりました。

　まず、
　　Google Play Storeで
　　　“Copilot”で検索すると
　　　　BingとMicrosoft Copilotが
　　　　　現れますので、
　　　　　　今回は後者を使用します。

　画像生成をする場合は
　　どちらでも同じかと思われます。

　あっ、描画の際に
　　Bing ImageCreatorは
　　　入力枠がありますが、
　　　　MS-Copilot Designerの場合、
　　　　　そうした入力枠はなく、
　　　　　　ChatGPTと同じく、
　　　　　　　やり取りで
　　　　　　　　行なわれます。

　**【ここで注意！】**
　このBing ImageCreatorの
　　入力枠の文字数制限480文字は
　　　MS-Copilot Designerにも
　　　　受け継がれています。

　つまり、
　　ChatGPTでDALL-E3に渡した
　　　プロンプトの多くが長くて、
　　　　プロンプトの後ろの方が
　　　　　カットされるのです。

　だから
　　Copilotに追加の指示を
　　　しても、変更がかからず、
　　　　“なぜなぜどうして？”となり、
　　　　　イライラすることに
　　　　　　なります。

　対処方法としては、
　　このBing ImageCreatorから
　　　受け継いだ性質を
　　　　わかった上で、
　　　　　画像生成を行なうしか
　　　　　　ありません。

　文字数制限の
　　480文字にオーバーしないように
　　　注意すること、です。
***
　次に、
　　Copilotとのやり取りに
　　　関してです。

　画像生成を目的に
　　やり取りする際は、
　　　文字数制限にも
　　　　注意が必要ですが
　　　　　このやり取りにも
　　　　　　注意が必要です。

　ChatGPTと同じように
　　やっていくと、
　　　おなしくなることに
　　　　気付きます。

　ChatGPTでは
　　指示をしたあとは
　　　プロンプトを補正していきます。

　しかし、
　　Copilotの場合は、
　　　プロンプトに
　　　　指示を後方に
　　　　　積み上げていきます。

　このため、
　　生成画像が
　　　おかしくなります。

　例えば、
　　女性を描いていた、としましょう。

　描かれた年齢が上で
　　修正するために
　　　“もう少し幼く”と指示。

　ちょうどいい感じになったので
　　ほかの点の修正をします。

　描画された画像は
　　さらに幼くなってしまいました。

　プロンプトの補正がされずに
　　“幼く”の指示が
　　　残ってしまったのです。

　これの対処方法は
　　描画ごとに、プロンプトを
　　　出力させていくしか
　　　　ありません。

　プロンプトを確認して、
　　おかしい箇所のチェックを
　　　するのです。
***
　プロンプトの出力で
　　思い出しましたが、
　　　画像のプロンプトと
　　　　Copilotのプロンプトは
　　　　　同一ではありません。

　文字数制限のために
　　DALL-E3に渡される前に
　　　カットされますが、
　　　　Copilot側では
　　　　　保持されています。

　この点が
　　ややこしいことなので、
　　　イラつきますね😅
***
　とりあえず
　　この二点に
　　　気を付ければ、
　　　　“あれっ？”と
　　　　　思ったときに
　　　　　　“もしかして？”と
　　　　　　　慌てずに対処できる
　　　　　　　　ことでしょう。

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

　今回は、ここまで。
eclipse で Tomcat始動時に「ポートが無効」と表示されたら

# もし以下のような事象に遭遇したら…
~~~
１つ以上のポートが無効なため、サーバーを始動できません。
サーバー・エディタを開き、無効なポートを訂正してください。
~~~
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3658170/560f851e-001f-25a4-4151-05a64e616cfd.png)

# Tomcat管理ポートを設定しよう
こんな感じ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3658170/6de7a9d7-583e-ce44-a54d-f152ba604888.png)

以上
簡単にBrainf**kを書けるプログラムを制作中

# 簡単にBrainf**kを書けるプログラムを制作中
Brainf**kってHello, World!だけで難しいらしいです。
実際のコード
```
>+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]
>++++++++[<++++>-] <.>+++++++++++[<++++++++>-]<-.--------.+++
.------.--------.[-]>++++++++[<++++>- ]<+.[-]++++++++++.
```
出力
```
Hello world!
```
解読性皆無でちょっとやばいですね。
そこで、私、開発しました。
Brainhackを。
## 使い方
まずこのようなコードを保存します。
```
printtext Hello, World!
```
言うまでもなくハローワールドですね。
それを自作「「BrainHack」」の実行ファイルにドラッグ＆ドロップ
そうすると...
out.bf.txtが出力されました。
それを見てみると...
```
[-]++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.[-]++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.[-]++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.[-]++++++++++++++++++++++++++++++++++++++++++++.[-]++++++++++++++++++++++++++++++++.[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.[-]++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.[-]++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.[-]++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.[-]+++++++++++++++++++++++++++++++++.
```
何かが出力されました。これをBrainf**kに入れてみましょう。
出力
```
Hello, World!
```
はい、出力できました。
これはですね、私オリジナルの言語をBrainf**kに変換できます。
これはスペースで区切る言語です。テキストは自動的に結合されるのでご心配なく
書いている当時である関数はこういうものです。
機能は詳しく言いません。すみません。
```
// 最初の関数名は大文字小文字問いません
printtext Hello, World!

printvar 1
set 10 5
cp 10 20
ptr 10 + 5
ptr 10 * 5
printvars 10 2
savetext 10 hello
```
詳しくはコメントで聞いてほしいです。
Github上で説明書をいつか作っておきます。

https://github.com/TNTSuperMan/BrainHack

C#で書いています。
できればアドバイスなど色々お願いします。
.githubをpushしているのにgithub actionsが使えない

# 初めに
前回Reactで作成したアプリをGit Hubにpushしたタイミングで、Firebaseに自動デプロイするという仕組みを構築中です。
まずは手動でWorkflowを実行したいと思い、Git Hubにpusjしてみましたが、リポジトリのActionタブを確認しても、作成したファイルが選択できない状態でした。

# 問題点
以下の記事を参考に進めていました。
https://qiita.com/Teach/items/d2c4d7bec98228df1807
フォルダ構成を以下のように構築しました。
study-record/github-actions-sample/.github/workflows/helloworld.yml
（study-recordはプロジェクト名）

# 解決策
本来「.github」フォルダはディレクトリ直下に置かないと正常に機能しません。
ですが今回勘違いしてディレクトリ直下に「github-actions-sample」フォルダを作成して、その中に「.github」フォルダを配置していました。
正しい構造は以下のとおりです。
study-record/.github/workflows/helloworld.yml

# 終わりに
かなりレベルの低いミスをしてしまいました...
おそらくちゃんと調べられたら簡単に解決するミスなのですが、それを解決できなかった自分の調査力はまだまだだなと痛感...
これから開発を行なっていくにあたって、自分で解決する力はとても重要なので調べる力もスキルアップしていきたいと感じました！
多分難しい言葉や内容を読んでも難しい記事に対して、かなり抵抗がある方だと思うので理解することを諦めず根気よく読み込まないといけないですね！
キーボード入力をやりやすくするライブラリを作った件について

LibKeyというライブラリです。
言語はJavascriptです。
Githubに公開しています。

https://github.com/TNTSuperMan/libkey

libkey.jsが開発用ファイルで、libkey.min.jsが圧縮(?)したファイルです。
サンプルプログラム(index.html)にVue2を使っているのでNoticeに記述しています。
ライセンスはApache License 2.0です。
# 仕組み
形式はクラスです。場所はwindow.libkeyです。
クラスなのでこのように使います。
```js
let KeyInput = new libkey();
```
クラスのコンストラクタではまず、addEventListenerでキーボードイベントを追加してキーボードの入力を監視(?)します。
```js:実際のコードの一部(一部インデントを修正)
constructor(){
    document.addEventListener("keydown",e=>{
        if(e.key && !this.isPress(e.key)
        && e.key.length === 1) this.pressKeyArray.push(e.key)
            this.#setmeta(e,true)
        })
        document.addEventListener("keyup",e=>{
            if(e.key) this.pressKeyArray.splice(
                this.pressKeyArray.indexOf(e.key) )
            this.#setmeta(e,false)
        })
    }
```
このコードは、isPressメゾッドで既に入力の変数に設定されているかを確認してから、入力の変数に追加しています。
そのあとにエンターキーなどの少し特殊なキーの設定を#setmeta関数に丸投げします。
# 使い方
## 変数
クラスのインスタンスを作ると、中身の変数はこのようなものがあります
- pressKeyArray
- isPressEtcKey
- ondown
- onup
### キー入力状態変数
pressKeyArrayは、今入力されている英数字・記号(正確にはKeyboardEvent.keyの値が一文字になる入力)の配列です。
KeyboardEvent.keyに関してはこちらをご参照ください。

https://developer.mozilla.org/ja/docs/Web/API/KeyboardEvent/key

```js:pressKeyArrayの中身のサンプル
["a", "@", "1"] //これはA、@、1が同時押しされている状態です。
```
isPressEtcKeyは、その他Ctrlキー、Altキーなどの少し特殊なキーが押されているかをBooleanで保存しています。
中身はこのようになっています。
```js:isPressEtcKeyの中身(コメントなど訂正あり)
isPressEtcKey = {
    upArrow:false,   //上矢印キー
    downArrow:false, //下矢印キー
    leftArrow:false, //左矢印キー
    rightArrow:false,//右矢印キー
    enter:false,     //エンターキー
    space:false,     //スペースキー
    alt:false,       //Altキー
    ctrl:false,      //Ctrlキー
    shift:false      //Shiftキー
}
```
もちろんtrueが押されている状態で、falseが押されていない状態です。
### イベント変数
ondown変数は、キーボードが押されたときに実行するイベント関数です。
初期値はe=>eです(エラー防止のため)
代入はこのようにします。
```js:代入例
let KeyInput = new libkey();
KeyInput.ondown = e => {
    console.log(e);
}
```
渡される引数のeは、KeyboardEvent.keyの値です。

onup変数は、キーボードから指が離された(?)ときに発生します。
引数はondown変数と同じです。代入方法もondownと同じです。
# サンプルプログラム(手抜き)
```html:index.html
<p>Ctrlキーを押すと直前に押したキーをアラートするよ！</p>
<script src="libkey.js"></script> <!--libkeyライブラリ本体-->
<script src="script.js"></script>
```
```js:script.js
let keyinput = new libkey(); //ライブラリの初期化
let lastInput = "";
keyinput.ondown = e => {
    if(keyinput.isPressEtcKey.ctrl){
        alert(lastInput);
    }else{
        lastInput = e;
    }
}
```
修正案がありましたらGithubやコメントで教えてほしいです。
最後まで見てくれてありがとうございました。
Groovy -> Kotlin DSLの移行Tips

# 概要

Android Studio Giraffeより、ビルドスクリプトの推奨がKotlin DSLになりました。新規作成するプロジェクトにおいて古いプロジェクトのビルド設定などを参考にする際、Groovyで記述されているbuild.gradleに遭遇することはあるはずです。
そのようなbuild.gradleをどう移行すれば良いかのTipsを備忘録としてまとめます。

## 定数の宣言

バージョン設定などで使用していたfinalはvalに書き直します。

```build.gradle
final room_version = "2.5.0"
implementation("androidx.room:room-runtime:$room_version")
```

```build.gradle.kts
val room_version = "2.6.1"
implementation("androidx.room:room-runtime:$room_version")
```

## buildFlavorの設定

```build.gradle
flavorDimensions = ["env"]
productFlavors {
    prod {
        dimension "env"
        applicationIdSuffix ""
        versionNameSuffix ""
    }
    stg {
        dimension "env"
        applicationIdSuffix ".stg"
        versionNameSuffix "-stg"
    }
    ...
```

```build.gradle.kts
flavorDimensions.add("env")
productFlavors {
    create("prod") {
        dimension = "env"
        applicationIdSuffix = ""
        versionNameSuffix = ""
    }
    create("stg") {
        dimension = "env"
        applicationIdSuffix = ".stg"
        versionNameSuffix = "-stg"
    }
    ...
```

## local.propertiesなどのファイルから値をロードする

```build.gradle
def properties = new Properties()
    properties.load(project.rootProject.file('local.properties').newDataInputStream())
def secret = properties.getProperty("SECRET")
```

```build.gradle.kts
val keystoreFile = project.rootProject.file("local.properties")
val properties = Properties()
properties.load(keystoreFile.inputStream())
val secret = properties.getProperty("SECRET") ?: ""
```

## gradle.propertiesからのロード

gradle.propertiesに`foo=`などの記述があればそのまま指定できましたが、project.propertiesから取得するようにします。

```build.gradle
buildConfigField("String", "FOO", '"' + "${foo}" + '"')
```

```build.gradle.kts
buildConfigField("String", "FOO", '"' + "${project.properties["foo"] ?: ""}" + '"')
```

## マニュフェストのプレースホルダー

配列にするのではなく、キーで直接指定します。

```build.gradle
manifestPlaceholders = [
    appLabel: "アプリ名",
    foo: "bar"
]
```

```build.gradle.kts
manifestPlaceholders["appLabel"] = "アプリ名"
manifestPlaceholders["foo"] = "bar"
```

## プラグインの指定

`()`で囲います。

```build.gradle
id "com.starter.easylauncher" version "6.2.0"
```

```build.gradle.kts
id("com.starter.easylauncher") version "6.2.0"
```

## ktlintの設定

https://pinterest.github.io/ktlint/latest/install/integrations/#custom-gradle-integration-with-kotlin-dsl

のリンクを参照ください。

## JVMの指定

```build.gradle
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KaptGenerateStubs).configureEach {
    kotlinOptions.jvmTarget = JavaVersion.VERSION_17.toString()
}
```

```build.gradle.kts
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
    kotlinOptions {
        jvmTarget = "17"
    }
}
```

## まとめ

アプリケーションコードでもあまり頻繁に触れることのない部分です。書き方は当然似ますが、書き換えとなるとソースが少ないの現状があるなのでTipsなどを活用ください。
どんな記述がモダンなのかは[Now In Android](https://github.com/android/nowinandroid)が良いサンプルとなるのでご覧いただくことをお勧めします。
無料でSSDのクローニング2024年3月

# 無料でSSDのクローニング2024年3月

## 背景
SSDを新しいの買ったんでOS含め起動ドライブを変更しようとしたがなかなか無料でクローニングする手段が見つからず困った。色々ソフト見るんだけど皆無料とか言っといてインストールして「さてクローニングするか」って実行するとpro版に加入してくださいって言ってくる。f**k

## 解決法
解決法は簡単でMacrium software社製のhomeでクローニングするだけ

ここもクローニングソフトは有料だけど無料トライアル30日とか言って無料でクローニング出来ます。メールアドレスの認証はあったけどクレジットカードとかの認証はないので実質無料でクローニングできる。ありがたや～。

クローニングした後はPC落として今までのドライブ引っこ抜いて起動してください。それで問題なく起動すればOK。起動しないでBIOS画面言ったらブートメニューで新しいSSDを選択してそれでブートしてください。（念のため引っこ抜きましたが引っこ抜かなくてもBIOS開いて起動優先順位を新しいSSDに変更するなり、ブートメニューでそのSSDを選択するなりしてくれればOK）

で、多分今までのSSDと同じデータサイズのパーティションが新しいSSDにできていて余剰分が未割当になっちゃってるんでwindowsマーク右クリ→ディスクの管理から「ボリュームの拡張」でサイズを増やしてください。未割当領域との間に「回復パーティション」とかあったらcmdからdiskpartでパーティション消してください。間にパーティションあると結合できないっぽい。

## 注意書き
割と適当なのでそれぞれの手順に関してはぐぐってください。PC起動できなくなっても責任持ちません。あくまでMacriumが無料なのは2024年3月時点の話ですので今後は他のソフトのように無料じゃなくなるかもしれません。
なんとなくで触れてきた「pom,maven,jar」について

## やること
Javaのアプリを作るにあたってなんとなくで触れてきたファイルについて整理する。

## 選ばれし、ものたち
- pomファイル
- maven
- jarファイル

### chatgptに聞いてみた
MavenはPOMを使用してJavaプロジェクトを管理し、プロジェクトのビルドと依存関係の解決を自動化します。そして、その結果としてJARファイルが生成され、他のプロジェクトで利用できるようになります。

### それぞれ
・pomファイル
プロジェクトのメタデータ（名前、Ver、依存関係、プラグイン）を記述
・maven
Javaプロジェクトのビルドを実行する。このとき、pomファイルから情報を得る
・jarファイル
Javaのプログラムやリソースをまとめたもの。pomをmavenでビルドした時に生成される。デプロイの時に使用される＊

＊Webアプリの時にはWARファイルも必要となる

### まとめ
pomファイルに依存関係やプラグインを記載し、 mavenでpomをインプットにして、ビルドし、jarファイルを生成する。jarファイルにはプログラムやリソースをまとめたすべてがまとめられる。
Snowflakeでよく利用している地理空間関数を紹介！

Snowflakeでは地理空間関数を利用して位置情報データの分析ができます。
普段よく利用している地理空間関数を紹介したいと思います！

### 緯度経度から地理空間データ型を生成
緯度経度（テキスト）から[地理空間データ](https://docs.snowflake.com/ja/sql-reference/data-types-geospatial#data-types)を作成します。地理空間データ型を作成しておくと、後々の処理が楽です。

`user_id, log_time, longitude, latitude`の4つのカラムを持っているテーブルを例に、Geometry型のカラムを作ります。

 はじめに、geomという名称のGeometry型のカラムを追加します。
 ```sql
ALTER TABLE sample ADD COLUMN geom geometry;;
```


続いて、緯度経度（longitude, latitude）を用いてGeometryを作ります。
以下のクエリでは以下の4ステップを行っています。
1. `st_makepoint()`で緯度経度からGeograpyオブジェクトを構築する。
2. `st_asgeojson()`でGeograpyオブジェクトをGeoJSON化する。
3. `st_geometry()`でGeoJSONをGeometry化する。
4. `st_setsrid()`でGeometryにSRIDをセットする。 
```sql
UPDATE sample
SET geom = ST_SetSRID(TO_GEOMETRY(ST_ASGEOJSON(ST_MakePoint(longitude, latitude))), 4326);
```
緯度経度情報から直接Geometryを作成する関数はないため、一度Geograpyオブジェクトを作った後にそれをGeometryに変換しています。
GeograpyとGeometryを変換する関数はないため、[公式ドキュメント](https://docs.snowflake.com/ja/sql-reference/data-types-geospatial#converting-between-geography-and-geometry)にあるように一度GeoJSONを挟む形で変換します。


### 地理空間データ型をWKTで出力する
地理空間データ型でデータベースに登録されているメッシュやポリゴンデータをQGISで表示したい場合にはWKTに変換するのがおすすめです。
CSVで出力すれば、QGISはWKTカラムを参照してポリゴンなどを描画してくれます。
変換は`ST_ASTEXT()`関数でサクッとできます。

例えば、1kmメッシュごとの集計結果をQGISで描画したいというときには、以下のような感じで集計結果をCSVで出力しています。
```sql
with temp as (
    -- メッシュごとに何かしらの集計
)
select
    mesh_1km, -- meshID等
    count, -- 何かしらの集計結果
    st_aswkt(m.geom) as wkt -- geometryをwktに変換
from
    temp t
    join mesh.mesh_1km m -- メッシュテーブルを別途用意してある想定
    on t.mesh_1km = m.mesh_id
;
```

### 範囲内の地物を抽出する
特定のエリア内にあるログを抽出するときには`ST_Within()`をよく利用します。
例えば、areaというポリゴンテーブルの中にあるログをsampleテーブルから抽出したいときには、以下のようにします。
```sql
select
    s.*
from
    sample s
    join area a
    on st_within(s.geom, a.geom)
;
```

<br>

また、ポイントやラインから一定のエリア内にあるものを抽出するときには`ST_DWithin()`を利用します。
例えば、複数の道路ラインから25m以内にあるログをsampleテーブル抽出して、道路ごとのログ数をカウントするみたいなときには、以下のようにします。（ついでにWKTを付与して、QGISで可視化もできるようにしています。）
```sql
select
    road_name,
    count(*) as log_count,
    st_aswkt(r.geom) as wkt -- QGIS可視化用に道路ラインのwktを付与
from
    sample s
    join road_line r
    on st_dwithin( -- st_dwithinはgeography型のみ可
        to_geography(ST_ASGEOJSON(s.geom)),
        to_geography(ST_ASGEOJSON(r.geom)),
        25) -- 第3引数はメートルで指定
group by
    road_name,
    wkt
;
```

### ポイント間の距離を計算する
ポイント間の距離を計算するには`ST_Distance()`を利用します。
例えば、時系列になっているGPSログデータに対して、ログ間の距離（移動距離）を計算するには以下のようにします。
```sql
select
    id,
    log_time,
    ST_Distance(
        to_geography(ST_ASGEOJSON(geom)),
        to_geography(lag(ST_ASGEOJSON(geom)) OVER (PARTITION BY id ORDER BY log_time))
    ) as distance
from sample
;
```

### ポイント間のラインを作成する
ポイント間のラインを作成するには`ST_MakeLine()`を利用します。
例えば、From-To間のラインを作るには以下のようにします。（fromとtoのポイントのgeometryが付与されている想定）
```sql
select
    from_area,
    to_area,
    ST_MakeLine(from_geom, to_geom)
from
    sample
;
```

# おわりに
Snowflakeの地理空間データへの対応はどんどんバージョンアップしているようです。
最近ではH3メッシュに関連する関数も利用できるようになっています。

https://www.snowflake.com/blog/getting-started-with-h3-hexagonal-grid/?lang=ja

H3メッシュを利用した解析は[以前からチャレンジしたい](https://qiita.com/se_yataro/items/df047d86cac095724b6d)と思っているもののあまりトライできていないので、これを期に触ってみたいと思います！
LINE WORKS の WOFF の利用例 救急隊搬送受入要請

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/LINEWORKS?src=hash&amp;ref_src=twsrc%5Etfw">#LINEWORKS</a> の WOFF を使った開発イメージを動画にしました。<br><br>救急隊と受入病院間の受入要請を想定しています。<br><br>WOFF のフォームで、様々な入力要素を使って容易に入力ができます。<br>入力を受けて Bot で救急隊と病院間で受入可否の情報伝達を行います。 <a href="https://t.co/iGiBuc9jk9">pic.twitter.com/iGiBuc9jk9</a></p>&mdash; iwaohig (@iwaohig) <a href="https://twitter.com/iwaohig/status/1772837953112519136?ref_src=twsrc%5Etfw">March 27, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
LINE WORKS の WOFF の利用例 ハウスメーカー訪問報告

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/LINEWORKS?src=hash&amp;ref_src=twsrc%5Etfw">#LINEWORKS</a> のWOFF を使った開発イメージを動画にしました。WOFF のフォームで、様々な入力要素を使って容易に入力ができます。<br><br>さらに入力されたデータを基に LINE WORKS の掲示板に記事投稿や、チームと個人のカレンダーに次回予定登録を行っています。 <a href="https://t.co/4mECKKlydc">pic.twitter.com/4mECKKlydc</a></p>&mdash; iwaohig (@iwaohig) <a href="https://twitter.com/iwaohig/status/1772837436110074064?ref_src=twsrc%5Etfw">March 27, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
S3+CloudFrontで静的Webページをホスティングする際にハマったこと

## 前提
#### 現状
SPAのフロントページをホスティングしたS3「**通称バケットA**」にCloudFront経由でのみアクセスできるようにしている。

#### やりたいこと
特定パス `/signage/{userName}` にアクセスされた時のみ、別のS3「**通称バケットB**」にホスティングした静的Webページを表示したい。

#### その他
- {userName}はユーザーによって可変
- バケットA、バケットB共にパブリックブロックは全てOFF
- S3の静的ウェブサイトホスティング機能はCloudFrontを通してアクセスするので無効状態

## 手順
◆ バケットB直下にindex.html等の成果物を格納
[![Image from Gyazo](https://i.gyazo.com/52f5dfd7900accacb77ed73a44a68163.png)](https://gyazo.com/52f5dfd7900accacb77ed73a44a68163)
◆ CloudFrontのオリジンにバケットBを追加
◆ CloudFrontのポリシーをバケットBのバケットポリシーに設定
[![Image from Gyazo](https://i.gyazo.com/92e207685a6eaa5d4a02781b5140da4b.png)](https://gyazo.com/92e207685a6eaa5d4a02781b5140da4b)
◆ CloudFrontのビヘイビアにて`/signage/*`へのアクセスはバケットBを向くように設定
[![Image from Gyazo](https://i.gyazo.com/543812facedc9bc10686b2a773b90e77.png)](https://gyazo.com/543812facedc9bc10686b2a773b90e77)

### 発生した問題1
ここまで行った手順で、`/signage/mori-dev`へアクセスした時はバケットBの静的ページを表示してくれると思ったが、バケットAのページを表示してしまう。

### 原因
CloudFrontのビヘイビア設定`/signage/*`が効いていないのか大分疑ったが、原因は別にあった。
`/signage/mori-dev`へアクセスするとバケットB自体にはアクセスしていたが、`バケットB/signage/mori-dev/`を探しに行ってしまう。
index.htmlはバケットB直下に置いており、`バケットB/signage/mori-dev/`は存在しないオブジェクトなので403エラーが発生していた。
CloudFrontのエラーページ設定で403エラーが発生した場合は200と見做してバケットAのindex.htmlを表示する、というカスタマイズをしていたので、バケットBにはアクセスできていないように見えてハマった。
[![Image from Gyazo](https://i.gyazo.com/532de7beee0cd7adfc7e13216fa0553f.png)](https://gyazo.com/532de7beee0cd7adfc7e13216fa0553f)

### 対応
試しにバケットBの階層化をキチンとして`バケットB/signage/mori-dev/index.html`を生成したところ、`/signage/mori-dev/index.html`で表示することが可能になった。
[![Image from Gyazo](https://i.gyazo.com/ff81fce2c963ba6fb6d0386772bb7e39.png)](https://gyazo.com/ff81fce2c963ba6fb6d0386772bb7e39)

しかし`mori-dev`の部分は可変であり、ユーザー分階層を用意するわけにはいかない。
S3のプレフィックスにはワイルドカードは使えない。
ということでCloudFront Functionを使用して対応することに。

CloudFront -> 関数 -> 新規作成
```javascript
function handler(event) {
    var request = event.request;
    var uri = request.uri;
    if (uri.startsWith('/signage/')) {
        // signageパスへのアクセスは全てバケット直下のindex.htmlに
        request.uri = '/index.html';
    }
    return request;
}
```
作成した関数をディストリビューションに紐づける
[![Image from Gyazo](https://i.gyazo.com/ee0499d1d048406b38b87c5cd17c6676.png)](https://gyazo.com/ee0499d1d048406b38b87c5cd17c6676)

これによって`/signage/mori-dev`のようなアクセスは全てバケットBのindex.htmlを見るようになりました。

### 起きた問題その2
`/signage/mori-dev`へのアクセスでバケットBのindex.htmlを表示するようになったものの、index.htmlからバケットBの同階層にあるbundle.jsが読み込めずに403エラー。

### 原因
bundle.jsはビヘイビア設定の対象ではないので、バケットA/bundle.jsへアクセスしようとして403エラーが発生していた。
ビルドはWebpackで行っていたので、`webpack.config.js`を設定して、ビルド成果物の接頭辞に`signage-resoure-`をつけるようにした。
その上で、CloudFrontのビヘイビア設定を追加し、`/signage-resoure-*`へのアクセスはバケットBを向くように追加
[![Image from Gyazo](https://i.gyazo.com/ef9d2fd9f32a4c3beee00f18171b7271.png)](https://gyazo.com/ef9d2fd9f32a4c3beee00f18171b7271)

これにて、無事`/signage/{userName}`へのアクセスでバケットBの静的Webページを正しく表示できるようになりました。
[![Image from Gyazo](https://i.gyazo.com/756533a6725c78ac8aba018528dcae6b.png)](https://gyazo.com/756533a6725c78ac8aba018528dcae6b)

## まとめ
S3へのWebホスティングはバケット内での構造化をちゃんとしていないと、存在しないオブジェクトへのアクセスになり403エラーが発生する。
上記が発生した場合に、CloudFrontで403エラーのカスタムエラーレスポンスを行なっていると、原因発見が遠くなってしまう。
CloudFront周りの調査は一時的にキャッシュを無効化して調査することが解決への近道。
Replace public key in solana project



> ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169726/4dc67ee4-79e9-25af-9a00-8ab6a5585a57.jpeg)

## View the public key of an existing project
```shell
anchor keys list
```

## Generate a new private key

```shell
solana-keygen new -s -f --no-bip39-passphrase -o new-keypair.json
cp -f ./new-keypair.json ./target/deploy/abc-keypair.json
```

## Replace the public key 

replace the public key in the `declare_id` and `Anchor. toml` files，run the following command:

```bash
anchor keys sync
```

## python version

```python
import os
import re
import toml
import subprocess

keyfile = "./new-keypair.json"

# mkdir -p ./target/deploy
dirshell = ['mkdir', '-p', './target/deploy']
ok = subprocess.run(dirshell, capture_output=True, text=True)

# solana-keygen new -s --force --no-bip39-passphrase -o new-keypair.json
shell = ['solana-keygen', 'new', '-s','--force', '--no-bip39-passphrase', '-o', keyfile]
result1 = subprocess.run(shell, capture_output=True, text=True)
print(result1.stdout)

# cp -f ./new-keypair.json ./target/deploy/abc-keypair.json
cpshell = ['cp', '-f', keyfile, './target/deploy/abc-keypair.json']
subprocess.run(cpshell, capture_output=True, text=True)
print("create new keypair file: ", "./target/deploy/abc-keypair.json")

# solana address -k ./new-keypair.json
solanashell = ['solana', 'address', '-k', keyfile]
result2 = subprocess.run(solanashell, capture_output=True, text=True)

addr = result2.stdout.replace("\n", "")
print("ProjectId: ", addr)

declare_id_regex = r"^(([\w]+::)*)declare_id!\(\"(\w*)\"\)"

rsfile = "./programs/abc/src/lib.rs"
with open(rsfile, 'r') as file:
    libdata = file.read()

    change_str = f'declare_id!("{addr}")'
    libresult = re.sub(declare_id_regex, change_str, libdata, 0, re.MULTILINE)

with open(rsfile, 'w') as file:
    file.write(libresult)

tomlfile = "./Anchor.toml"
with open(tomlfile, 'r') as file:
   tomldata = toml.load(file)

tomldata['programs']['localnet']['abc'] = addr

with open(tomlfile, 'w') as file:
    toml.dump(tomldata, file)
```
webviewからネイティブアプリに値を渡す方法

-----------------
[技術スタック]

ネイティブアプリ
- Android: Kotlin
- iOS: Swift

ウェブアプリ
- Typescript（Next.js）

-----------------

webview → ネイティブアプリ にデータを受け渡すパターンの一つとして、JavaScriptの引数に値を入れて受け渡す方法があります。

1. ネイティブアプリ側で関数を定義
（ネイティブアプリのコードは省略します。）

2. webview側で関数を呼び出す

以下webview側のJavaScriptのコードです。

```
[Android]
window.Android.functionName.postMessage(value)
```
```
[Swift]
window.webkit.messageHandlers.functionName.postMessage(value)
```

上記をonClickなどのイベントで呼び出すことで、ネイティブアプリ側で関数を実行するとともに引数に入れた値をネイティブアプリに渡すことができます。
下罫線か上罫線か？罫線の位置を調べるマクロを作ってみた

## はじめに

> おい、そこの罫線
> A1の下罫線か、A2の上罫線か、どっちなんだい！
> https://twitter.com/Tsutsui0524/status/1772401667331289207

エクセルで「下罫線と上罫線のどっち？」というツイートを見かけました。
確かに疑問だと思ったので、罫線の位置を調べるマクロを作ってみました。

## 本記事の対象者

- エクセルVBAが分かる方。

## 罫線の位置を調べるマクロ

```vb
Sub 罫線の位置を調べる()
    Dim s_result As String
    Dim bs As Borders
    Set bs = ActiveCell.Borders
    If bs(3).LineStyle <> xlNone Then
        s_result = s_result + "上"
    End If
    If bs(2).LineStyle <> xlNone Then
        s_result = s_result + "右"
    End If
    If bs(4).LineStyle <> xlNone Then
        s_result = s_result + "下"
    End If
    If bs(1).LineStyle <> xlNone Then
        s_result = s_result + "左"
    End If
    If s_result = "" Then
        MsgBox ("上右下左に罫線はありません。")
    Else
        MsgBox ("罫線は「" + s_result + "」です。")
    End If
End Sub
```

## マクロの解説

Visual Basic Editorを立ち上げてActiveCellのBordersのItemを調べてみました。

するとBorderが1から6まであって、順に左、右、上、下、左上から右下への斜め、右上から左下の斜めの罫線に対応していることが分かりました。

作成したマクロは、選択した1つのセルの上右下左のどこに罫線があるかをダイアログボックスで報告する関数になります。

## たかが罫線と侮るなかれ

A1の下罫線は、必ずしもA2の上罫線とは限りません。A1とA2のどちらかに罫線があれば、A1とA2の間に罫線があるように見えます。A1とA2の間に罫線があったとしても、A1の下罫線とA2の上罫線のどちらかは見分けがつかないということです。A1の下罫線とA2の上罫線の両者とも存在することもあります。

罫線は奥深いです:thinking:
【Python】ローマ字名前を名前形式に変換する

◆問題
例のように、ローマ字の名前を名前形式に変換する方法が分からなかった。
例）YAMADA TarouをYamada Tarouにする

◆対処法
.title()を使用すればできた。

```python:conversion.py
name = "YAMADA Tarou"
formatted_name = name.title()
print(formatted_name)

"""
# 実行結果
Yamada Tarou
"""
```
【PHP】Postリクエストを受取り、Bodyの内容を取得する

## はじめに
最近PHPをはじめました。
HTTPのPostリクエストを受取り、Bodyの内容を取得する方法が分からず調べてみました。

## 結論
下記コードで実現できました！

**Version**
PHP：5.5.3
Cake:2.5.8

```PHP:
    // POSTリクエスト以外はエラー
    if (!$this->request->is('post')) {
        echo json_encode(["is_success" => false, "error" => "POSTリクエスト不使用"]);
        return;
    }

    // リクエストボディの内容を取得
    $json = file_get_contents('php://input');
    $data = json_decode($json, true);

    // リクエストボディからuserIDを取得
    $user_id = $data['user_id'];
```

## 詳細
ChatGPTに聞きながら詳細をまとめました。

**1. POSTリクエストのチェック**
最初のブロックは、受け取ったリクエストがPOSTメソッドであるかを確認しています。Bodyの内容を取得するのに必須ではないですがPostリクエストであることを条件としています。

**2. リクエストボディの取得**
```file_get_contents('php://input')```を使ってリクエストボディの内容を取得します。```php://input```は、リクエストのボディ部分にアクセスするためのPHPのラッパーで、生のPOSTデータを読み込むために使われているそうです。
取得したJSON形式のデータはjson_decode()関数によってPHPの配列に変換されます。json_decodeの第二引数にtrueを指定することで、結果が連想配列として返されます。
```PHP
// 連想配列の例（キーを指定してセットする）
$array = array('apple'=>'りんご', 'peach'=>'もも', 'pear'=>'なし');
```

**3. userIDの取得** 
最後に、連想配列として解析されたデータからuser_idキーに対応する値を取得しています。この値は変数$user_idに代入します。

## まとめ
PHP初心者ですがHTTP通信の理解が深まったり、動的型付けがどういうものなのかが見えてきて面白いです！

## 最後に
私の働いている会社で経験の有無を問わず採用を行っています。
興味のある方は是非カジュアル面談から応募してみてください！

https://jambo-support.com/recruit_engineer/
果たして自分の書いているコードは読みやすいか

# はじめに
こんにちは。エンジニア2年目のasukittです。
今年は読書習慣を付けるため1か月に1冊本を読む目標を立てています。
またアウトプットをして定着させるために本記事の作成にいたりました。

# 今回の書籍
現在の業務で、汚いコードで悩んでいる同僚がいて軽く話を聞いて自分も該当しているのではと思って調べている内にこの本と巡り合いました。
また「技術書　おすすめ　エンジニア」と調べたら、ほぼ確実に入っている良書なので買いました。
それが、
[リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック](https://www.amazon.co.jp/%E3%83%AA%E3%83%BC%E3%83%80%E3%83%96%E3%83%AB%E3%82%B3%E3%83%BC%E3%83%89-%E2%80%95%E3%82%88%E3%82%8A%E8%89%AF%E3%81%84%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E6%9B%B8%E3%81%8F%E3%81%9F%E3%82%81%E3%81%AE%E3%82%B7%E3%83%B3%E3%83%97%E3%83%AB%E3%81%A7%E5%AE%9F%E8%B7%B5%E7%9A%84%E3%81%AA%E3%83%86%E3%82%AF%E3%83%8B%E3%83%83%E3%82%AF-Theory-practice-Boswell/dp/4873115655 )

![81+3DpjuMdL._SL1500_.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2957298/0775f498-1906-a90d-847b-d967f551814a.jpeg)

# できていたこと
・ネストを浅くすること

・if文で否定形を使わないこと

・正確な内容のコメントを書くこと

・do/whileは極力使わない

・ドモルガンなどの発想の転換でコード量を減らす

・1つの関数には1つの機能

・日本語で説明してからコードに落とし込むこと

# 学んだこと
### 変数の命名方法
読みやすいコードにするためには、
・変数に使う英語を正確に選定する（Get→Fetch,Download）

・汎用的な変数を避ける(tmpなど)

・具体的に名前を付ける（ServerCanStart→CanListenOnPort）

・変数に大事な情報を追加する（秒だと_ms）

変数の命名には気を付けていましたが、それは禁止事項だったり最低限のマナーを守っているだけだったことがわかりました。

### 変数の表す範囲
・限界値を含める時は○○_limitではなく○○_min,○○_max

・○○_start,○○_stop→○○_first,○○_last

・複数の意味で取れる単語は避ける（read,get）

### 見やすい書き方
・改行位置を揃える

・縦の線を揃える

・宣言をブロックごとに分ける

### 不要なコメント
・コードを見たらすぐにわかること

・わかりにくい変数をコメントで説明するなら変数名を変える

### 必要なコメント
・定数の理由をコメントで示す

・コードが他のやり方でない理由

・ファイルやクラスの全体像の概要

# まだ難しかった内容
・一時変数、中間結果を削除する→リファクタリング段階ではできることもある程度だった

・無関係の下位問題の抽出→無関係かどうかを判断するのが難しかった

・テストコードも簡潔化→テストコードをあまり使ったことがないので理解が難しかった

・CやC++独特の書き方の部分→習熟度が低く難しく感じてしまった

・ベルトコンベヤー設計と時間バケツ設定→C++で書かれていたこともありあまり理解ができなかった

# まとめ
これまで自分が読みやすいコードを書くためにしたことといえば、Pythonの習得の際にPEP8を軽く読んだことだった。またわかるように必要以上に丁寧にコメントを書いていたことも気づいた。
また現在の現場ではコードレビューをしてもらう機会がないのでこのように自分で気を付けていくことが今後の人のためだなと思った。
この本はコードを書いていてふと気づいたときに読み直しながら実践し徐々に力をつけていくものなので普段から念頭に置いてコーディングをしようと思った。

次に読みたい本は下記
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
・[良いコード/悪いコードで学ぶ設計入門 ―保守しやすい 成長し続けるコードの書き方](https://www.amazon.co.jp/dp/4297127830/?coliid=IAIJDCZ09CKTR&colid=25GN8PGBR0MY6&psc=1&ref_=list_c_wl_lv_ov_lig_dp_it)

・[オブジェクト指向でなぜつくるのか 第３版　知っておきたいOOP、設計、アジャイル開発の基礎知識](https://www.amazon.co.jp/dp/B092HDFJKK/?coliid=I2PQCL2NZ3HSA0&colid=25GN8PGBR0MY6&psc=0&ref_=list_c_wl_lv_ov_lig_dp_it)

・[コンピュータはなぜ動くのか 第２版 知っておきたいハードウエア＆ソフトウエアの基礎知識](https://www.amazon.co.jp/dp/B0BHMS1BYP/?coliid=I288R5OIDSGW32&colid=25GN8PGBR0MY6&psc=0&ref_=list_c_wl_lv_ov_lig_dp_it)

・[プログラムはなぜ動くのか 第3版 知っておきたいプログラミングの基礎知識](https://www.amazon.co.jp/dp/4296000195/?coliid=IC58CH4CBEPO4&colid=25GN8PGBR0MY6&psc=1&ref_=list_c_wl_lv_ov_lig_dp_it)

・[世界一流エンジニアの思考法](https://www.amazon.co.jp/dp/B0CKSZV5DQ/?coliid=IP9LGT0EKSIRT&colid=25GN8PGBR0MY6&psc=0&ref_=list_c_wl_lv_ov_lig_dp_it)

・[達人に学ぶDB設計＋SQL](https://www.amazon.co.jp/dp/B0CJVL5PC7/?coliid=I2MYQ1PKCI0Y4L&colid=25GN8PGBR0MY6&psc=1&ref_=list_c_wl_lv_ov_lig_dp_it)
Hinemosジョブ管理機能をAWSマネージドサービス化できるか検討してみた

## 1.はじめに

AWS Step Functionsは、AWSでマイクロサービスに対する一連の操作（ステップ）を管理するための非常に効果的なツールです。
一方で、HinemosはITリソースの運用管理に使用されるオープンソースの統合運用管理ソフトウェアです。
この記事では、AWS Step Functionsを使ってHinemosのジョブ管理をどのように実現するか考察します。

なお、Hinemosに関する詳細については、以下に記載の通りです。
サブスクリプションモデルも提供されており、ミッションクリティカルなシステムにおいても有効となる運用管理ツールになります。

・Hinemosとは
https://www.hinemos.info/hinemos

・Hinemosのサブスクリプションサービス
https://www.intellilink.co.jp/topics/news_release/2023/032200.aspx

## 2.検討対象とするジョブパターンとその内容
Hinemosのジョブ管理機能の内、今回AWSサービスで実現方法を検討するジョブパターンは以下の通りとなります。
関わっている案件で主に利用しているものを中心にピックアップしています。


| No　| ジョブパターン | 概要 |
|:-----------:|:------------:|:------------:|
| 1 |   スケジュール起動(cron型)      |年次/月次/日次などの定められたスケジュールによって起動するもの          |
| 2 |    スケジュール起動(rate型)    | 30分毎/1時間毎などの定められた間隔によって起動するもの|
| 3 | スケジュール起動(カレンダー型)| 特定条件(第一週に月曜日がある月の第二日曜日など)で起動するもの|
| 4 | スケジュール起動(cron型)+他ジョブ/ジョブネットステータス連動 | cron型起動かつ先行ジョブ/ジョブネットのステータスを条件として起動するもの |
| 5 | 手動起動      | 手動で起動するもの |


## 3.各ジョブパターンに対するAWSサービス構成
検討対象となるHinemosのジョブをどのようにAWSサービスで実現するかを検討しました。各ジョブパターンの処理方式は以下になります。

No.1 スケジュール起動(cron型)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576019/23aecbcf-756d-0902-8fb2-6861cf00ae47.png)

No.2 スケジュール起動(rate型)													
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576019/b903ece0-7bf9-1669-1702-1e85507f071f.png)

							
No.3 スケジュール起動(カレンダー型)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576019/e0aad0af-b7c2-1f50-6ccc-79da12e4dff0.png)

No.4 スケジュール起動(cron型)+他ジョブ/ジョブネットステータス連動
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576019/685dc380-1347-a990-a288-758a32320f9c.png)

No.5 手動起動      
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576019/cef0e401-ab5f-c0a5-c1b4-504d85f5be21.png)


## 4.検証結果と課題
いくつかのHinemosジョブパターンについて、今回検討したAWSサービス構成で実現できるか実機で検証し、想定通りにジョブが実行できることを確認できました。
ただし、Step Functionsの仕様から下記のような課題があることも分かりました。

・デフォルトではコマンドの実行結果を待たない
・処理の分岐は択一であり、1-n構成でのジョブフローは実現できない
・ジョブのリラン/スキップは厳密には対応できない

## 5.まとめ
今回、Hinemosジョブ管理機能で実現可能なジョブパターンについてAWS Step Functionsを使った構成を検討しました。今後、ジョブのAWSクラウド移行を検討する際には参考になると思います。
ただし、前項の通り課題がいくつかあることも分かりました。解決策としては何らかのDBサービスを利用してジョブ実行ステータスを保管する必要があると考えてます。
次回はDBサービスを利用してジョブ実行ステータスを保存した場合の構成について検討したいと思います。

※ 本ブログに記載した内容は個人の見解であり、所属する会社、組織とは関係ありません。