OmniAICreator/Qiita-1.07M huggingface dataset:
OCI Logging Analyitcsによるログ分析の手順③ -リンク分析① - 

LAのビジュアライゼーションの中には、リンク分析という特殊な分析手法があります。このリンク分析は、種類の異なるログ等を結び付けてグループ化することによって、例外的なパターンを監視して異常値を検出するなどの直感的なビジュアライゼーションを提供します。
具体的なユースケースは、[ドキュメント](https://docs.oracle.com/ja-jp/iaas/logging-analytics/doc/perform-advanced-analytics-link.html)にも記載があるのですが、実際に使用するには理解しずらいところも多いので今回はもう少し具体的に説明したいと思います。

まず、以下のリンク分析のスライドを見てください。例えば、Apache等のWebアクセスのログにはURLやデータ転送量などフィールドがありますが、URLでリンクするとそれぞれ3つのグループにサマリーされ件数がカウントされます。そして、Size(転送量)フィールドに平均を指定することでリンク時に平均を算出した新しいフィールドが生成されます。これらの値を縦・横軸、値に指定してチャートにプロットするというのが基本的なリンク分析のできることです


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562423/04f8b0f7-5d9b-42d3-aee2-68918c89021b.png)

LAのダッシュボードの中には、リンク分析を使っているものが結構ありますので、サンプルログを使って、実際にリンク分析の画面を見てみたいと思います。

## ダッシュボードにあるVCNフローログのリンク分析
[前回の手順](https://qiita.com/western24/items/49b044c733b69a33bae8#%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E6%9C%89%E5%8A%B9%E3%81%AB%E3%81%99%E3%82%8B)にてサンプルログを有効化し、時間を過去24時間に指定して、管理 -> ダッシュボードからVCNフローログをクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562423/324ea244-7a21-4ed3-8e0b-f5b7316832d3.png)

ダッシュボードの下部にあるのがリンク分析。これはVCNフローログの送信元と宛先のIPアドレスでリンク(サマリー)したもので、バブルの大きさが通信量を表している。一番大きいバブル(=最も通信量が多い)は、いつ頃、どの送信元から送信先のIPアドレスだったか、というのが一目でわかるようになっている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562423/f5c82c4e-3826-4c86-90fe-52eb00556528.png)

右上のアイコンをクリックすると、この分析画面を構成している以下のログ・エクスプローラーの画面に遷移する。左側のグループ化基準には、リンクしているフィールドを表しており、Time, Source IP, Destination IPでリンクしている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562423/b69c3dcd-e444-440f-b9ee-604a5b629b01.png)

上部にある問い合わせのコマンドの内容を少し解説すると、

'Entity Type' = 'OCI VCN Virtual Network Interface Card' and 'Log Source' = 'OCI VCN Flow Unified Schema Logs' | eval VNIC = Entity | <font color=Blue> eval Bandwidth = 'Content Size Out' / (1024 * 1024) </font color> | <font color=Red> link Time, 'Source IP', 'Destination IP' </font color>| <font color=Green> stats avg(Bandwidth) as Bandwidth, unique('Source IP') as 'Source IP', unique('Destination IP') as 'Destination IP', unique(VNIC) as VNIC, avg('Destination Port') as 'Destination Port'  </font color> | <font color=purple>classify topcount = 300 correlate = -*, 'Destination IP', 'Destination Port' 'Start Time', 'Source IP', Bandwidth, VNIC as 'Network Analysis'</font color>
- <font color=Blue>eval</font color>は、新しいフィールドの生成を意味し、Content Size Outフィールドを1024*1024で割って算出した値をBandwidthに入れている
- <font color=Red>Link</font color>は、Time、Source IP、 Destination IPフィールドを指定
- <font color=Green>stats</font color>は、サマリー統計に関する内容を意味し、Bandwidthを平均したものをasでリネーム、Souce IPを単一にしたものをasでリネームなどしている
- <font color=purple>Classify</font color>は、以下のバブルチャートにて設定した内容が反映されている

歯車アイコンをクリックすると、バブルチャートを構成している要素を確認できる。XY軸に設定しているフィールド、上記で指定したBandwidthが値に指定されているのが分かる。相関付けをしておくと、バブルにマウスオーバーした際に指定したフィールドの値が表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562423/cbba1627-c833-405c-99fa-bdf5f649c5ca.png)

まずは、リンク分析の特徴を漠然と理解した上で、[次回](https://qiita.com/Western24/items/429b3bb6e24d8f87cf62)は実際にリンク分析のユースケースを使って、一から分析画面を作成していく手順を紹介したいと思います。
OCI API Gateway で発生するSet-Cookieヘッダーのドメイン属性上書き問題とその解決

## はじめに

OCIのAPI Gatewayを利用して、バックエンドにOCI Functionsを設定し、クライアントにSet-Cookieヘッダーを返す際に、意図しないドメイン属性への変更が発生する問題に直面しました。
本記事では、問題の切り分け、調査過程、最終的な結論をまとめます。

## 問題の概要

API Gatewayにはsub.example.comというドメインを設定しています。
そのAPI Gatewayのバックエンドに設定したOCI Functionsで、Set-Cookie ヘッダーを利用してクライアントへクッキーを送信した際、ドメイン属性（Domain）を「example.com」に設定しても、レスポンスヘッダーでは「sub.example.com」に上書きされる問題が発生しました。

以下のようなコードで Set-Cookie を設定しています。

```Python
import io
import json
import secrets

from fdk.response import Response
from fdk.context import InvokeContext

def handler(ctx: InvokeContext, data: io.BytesIO = None) -> Response:

    value = secrets.token_hex(32)

    return Response(
        ctx, response_data=json.dumps(
                {
                    "code": 200,
                    "testCookie": value
                }
        ),
        headers={
            "Content-Type": "application/json",
            "Set-Cookie": f"testCookie={value}; Secure; HttpOnly; Domain=example.com; Path=/; Max-Age=2592000; SameSite=None"
        },
        status_code=200
    )
```

しかし、実際のレスポンスヘッダーでは Domain=sub.example.com に書き換えられてしまいます。

## 問題の切り分け
### 1.ドメイン属性の指定
- Domain=example.com を指定 → sub.example.com に上書き
- Domain=.example.com を指定 → sub.example.com に上書き
- Domain を指定しない → Set-Cookie に Domain 属性が含まれない。セットされるCookieのドメイン属性はsub.example.comとなる。

### 2.CORS の影響
OCI サポートから「CORS ポリシーが影響している可能性がある」との指摘を受けたため、一時的に CORS ポリシーを無効化して動作を確認しました。

しかし、CORS ポリシーを無効にしても、Set-Cookie の Domain が sub.example.com に上書きされる挙動は変わりませんでした。

### 3.API Gateway の仕様かバグか
再度 OCI サポートに問い合わせたところ、「現在の挙動はバグであり、修正対応中」との回答を得ました。

## まとめ
OCI API Gateway で Set-Cookie ヘッダーのドメイン属性が上書きされる問題について、調査を進めた結果、OCI 側のバグであることが判明しました。

最終的には OCIサポートの対応により迅速に修正がされ、意図した Domain 属性でクッキーをセットできるようになりました。

この問題を通じて、OCI API Gateway の挙動に関する理解が深まり、また、公式サポートへの問い合わせの重要性を再認識しました。

同様の問題に直面した際には、まず API Gateway の仕様を確認し、必要に応じてサポートに問い合わせることが有効です。

本記事が、同じ問題に悩む方の参考になれば幸いです。
OCI API Gateway ルート数上限の話

## はじめに

OCI API Gateway には、デプロイメントごとのルート数が 50個までという制限があります。

[参考: APIゲートウェイの内部制限](https://docs.oracle.com/ja-jp/iaas/Content/APIGateway/Reference/apigatewaylimits.htm)

API設計時にはこの制限を考慮しておらず、機能追加の段階でルート数制限の存在を知りました。
新しいエンドポイントを追加しようとした際、この制約に直面、回避策を検討する必要がありました。

## ルート数制限への対応策

この制限に対処するために、様々な選択肢を検討しましたが、最終的にバックエンドサービスに登録しているファンクションのエントリーポイントでルーティングを行う方式を採用しました。

```
[Client] → [OCI API Gateway] → [OCI Function (エントリーポイント)] → [OCI Function (各モジュール)]
```



### デプロイメントのルート設定

まず、デプロイメントのルート設定を以下のように設定しました。

- パス：　`/{wildcard_path*}`
- メソッド：　GET, POST, PUT, DELETE
- バックエンドタイプ：　Oracleファンクション

### エントリーポイントであるファンクションのサンプル

```:Python
import io
from urllib.parse import urlparse

import oci
from fdk.response import Response  
from fdk.context import InvokeContext

import apps.getUsers as getUsers
import apps.postUser as postUser

def handler(ctx: InvokeContext, data: io.BytesIO = None) -> Response:
    requestUrl = ctx.RequestURL()
    parsedUrl = urlparse(requestUrl)

    if ctx.Method() == "GET":
        if parsedUrl.path == "/v1/users":
            result = getUsers.handler(ctx, data)
            return result

    if ctx.Method() == "POST":
        if parsedUrl.path == "/v1/users":
            result = postUser.handler(ctx, data)
            return result
```

## まとめ
OCI API Gateway のルート数制限に対応するために、エントリーポイントファンクションを活用したルーティング方式を採用しました。

この方式は、新しいエンドポイントを簡単に追加できるという利点があります。
しかし、コードの複雑化や単一障害点になる可能性といった課題もあるため、適切な設計と運用が求められます。

### 反省点
今回の対応では、エントリーポイントのファンクションを自作しルーティングを行いましたが、事前に適切な API 設計を行っていればこの問題を回避できた可能性があります。

また、ライブラリやフレームワークを活用すれば、よりシンプルでメンテナンスしやすい実装ができていたかもしれません。

今後の開発では、より柔軟な設計を事前に検討していきたいと考えています。
Oracle NoSQL Database のダンプとリストア手法の検討

## はじめに

Oracle NoSQL Database Cloud Serviceを使用しています。DBデータのダンプとリストアについて、現状は効率的な方法が見つかっていません。

特にダンプについて、現状では個々のレコード単位でのエクスポート（コンソールからレコード横の三点リーダーから「JSONのダウンロード」を選択）しか方法が見つけられていません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3982228/8cc731e0-28c7-4465-9f60-ce0c105a48a1.png)

多くのレコードを格納するDBのダンプを行う際には、上記の方法では現実的ではありませんでしたので、より効率的なDBダンプとリストアの方法を調査しました。


## 調査
ドキュメントには使えそうなユーティリティがないので、サポートに問い合わせ中です。

https://docs.oracle.com/ja/cloud/paas/nosql-cloud/fmnne/?tab=Python


アプリケーション的に解決すると、以下のような方法が考えられます：

1. テーブル全件取得
2. JSONファイルに出力
3. ObjectStorage（バケット）に保存

このような処理を行うファンクションを作成することで解決できるかもしれません。

### 検証結果

テスト用のファンクションを作成したところ、バケットにJSONファイルを出力することができました。

また、そのJSONファイルをそのまま「データをアップロード」で流し込むことで、データのリストアができました。

![画像1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3982228/aaa70e52-05d6-414d-8a43-6336dcdbd05b.png)



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3982228/c7c1394b-240f-4610-9f69-cd70038d7b24.png)


## まとめ

DDLを事前に取得しておくことが前提となりますが、この方法を使えば、データのダンプおよびリストアの効率が向上する可能性があります。
Oracle NoSQL Database Cloud Serviceのテーブル数上限ではまった話

## 背景
1つのテナント内で本番環境と開発環境を分けていました。  
開発環境で十分な検証を行った後、本番環境のNoSQL Databaseに新しいテーブルを作成しようとしたところ、予期せぬエラーが発生しました。  
エラーメッセージは以下のとおりです。

```
Cannot create table hoge, already at tenant total table count limit of 30が原因で、表"hoge"に対する操作CREATE_TABLEが失敗しました
```

### 調査
調査したところ、Oracle NoSQL Database Cloud Serviceにはテナントごとのテーブル数上限が30個までという制限があり、公式ドキュメントにも記載されていました。


[公式ドキュメント]

https://docs.public.oneportal.content.oci.oraclecloud.com/ja-jp/iaas/nosql-database/doc/cloud-limits.html


## 対応
幸い、この制限はOCIのサポートリクエストを通じて、テーブル数上限の引き上げ申請が可能でした。  
今回のケースでは、申請後およそ1営業日で制限が引き上げられました。  

ただし、OCIの制限引き上げリクエストには、以下の選択肢しかありませんでした。  
- 読み取りユニット制限  
- 書き込みユニット制限  
- テーブルサイズ制限  

そのため、NoSQLのテーブル数上限を引き上げる場合は、まずテクニカルサポートへ直接問い合わせる必要があります。
Raspberry Pi OSのタスクバーにアプリケーションを登録する

![20250218_15h58m40s_grim_crop.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99470/5997045f-4bd9-4b26-a35b-c6834bfbc35e.jpeg)

↑こんな感じでVSCodeとか置けます。

## やりかた

`~/.config/wf-panel-pi.ini`を編集すればOK (2025年2月時点ではこれでいけました)

VSCodeを足すだけならこんな感じにします。(000003までは最初から書いてあります)

```~/.config/wf-panel-pi.ini
[panel]
launcher_000001=lxde-x-www-browser.desktop
launcher_000002=pcmanfm.desktop
launcher_000003=lxterminal.desktop
launcher_000004=code.desktop
```

`なんちゃら.desktop`は`/usr/share/applications/`に入っているものを書くみたいです。

編集が終わったらファイルを保存して、再起動しましょう。登録されているはずです。
Oracle NoSQL Database 主キーがUUID型の時にプレースホルダが使用できない不具合ではまった話

## 背景

OCIファンクションを作成し、Oracle NoSQL Database Python SDK（`borneo`）を使用してデータを取得する際、`PrepareRequest` を利用してSQLにプレースホルダをバインドする実装を行いました。
しかし、主キーがUUID型のカラムに対してプレースホルダを使用すると、`PREPARE: Illegal Argument: Invalid UUID string` というエラーが発生しました。

## 環境とテーブル定義

以下のようなテーブルを作成します。

```:SQL
CREATE TABLE test (
    userId STRING AS UUID,
    userName STRING,
    uuidStr STRING AS UUID,
    PRIMARY KEY(userId)
)
```
このテーブルに以下のデータを登録します。

```:JSON
{
  "userId": "10000000-0000-4000-8000-000000000000",
  "userName": "taro",
  "uuidStr": "20000000-0000-4000-8000-000000000000"
}
```

## 実装と発生した問題
エラーが発生したコードは以下の通りです。

```:Python
import io
import os
import logging
from borneo import NoSQLHandle, NoSQLHandleConfig, Regions, QueryRequest, PrepareRequest
from borneo.iam import SignatureProvider

def handler(ctx, data: io.BytesIO = None):
    try:
        compartmentId = os.getenv('COMPARTMENT_OCID')
        provider = SignatureProvider.create_with_resource_principal()
        config = NoSQLHandleConfig(Regions.AP_TOKYO_1, provider).set_default_compartment(compartmentId)
        handle = NoSQLHandle(config)

        userId = "10000000-0000-4000-8000-000000000000"
        
        sqlText = 'DECLARE $userId STRING; SELECT userId, userName FROM test WHERE userId = $userId'
        prepareRequest = PrepareRequest().set_statement(sqlText)
        prepareResult = handle.prepare(prepareRequest)
        prepareStatement = prepareResult.get_prepared_statement()
        prepareStatement.set_variable('$userId', userId)
        queryRequest = QueryRequest().set_prepared_statement(prepareStatement)
        queryResult = handle.query_iterable(queryRequest)
    
    except Exception as ex:
        logging.getLogger().info(str(ex))
```
        
このコードを実行すると、以下のエラーが発生しました。


```
PREPARE: Illegal Argument: Invalid UUID string:
Traceback (most recent call last):
  File "/function/testsql.py", line 27, in handler
    prepareResult = handle.prepare(prepareRequest)
  ...
borneo.exception.IllegalArgumentException: PREPARE: Illegal Argument: Invalid UUID string:
```

一方で、主キーではないUUID型カラム uuidStr に対してプレースホルダを使用すると、エラーは発生しません。

```:Python
uuidStr = "10000000-0000-4000-8000-000000000000"
sqlText = 'DECLARE $uuidStr STRING; SELECT userId, userName FROM test WHERE uuidStr = $uuidStr'
prepareRequest = PrepareRequest().set_statement(sqlText)
prepareResult = handle.prepare(prepareRequest)
prepareStatement = prepareResult.get_prepared_statement()
prepareStatement.set_variable('$uuidStr', uuidStr)
queryRequest = QueryRequest().set_prepared_statement(prepareStatement)
queryResult = handle.query_iterable(queryRequest)
```


## 問い合わせと対応状況
この件について、Oracle サポートに問い合わせたところ、開発チームもこの問題を認識しており、修正対応中であるとの回答を得ました。
およそ10営業日後、この問題が解消されたと連絡があり、同現象が再現しないことを確認しました。

## まとめ
この問題は、UUID型の主キーに対してプレースホルダを使用すると発生するバグでした。
開発チームによる修正が行われたため、現在は正常に動作します。
OCIのサポートも的確に問題を把握し、迅速に対応してくれた点は非常に評価できます。
Conquer Your WGU Ethics in Technology Exam: Practice Makes Perfect

<p data-sourcepos="3:1-3:589">Preparing for the WGU Ethics in Technology exam can feel daunting, but with the right approach and resources, you can confidently achieve success. One of the most effective strategies is utilizing practice dumps. These resources offer a simulated exam experience, allowing you to familiarize yourself with the format, question types, and time constraints. By working through practice questions, you can identify areas where you excel and pinpoint concepts that require further study. This targeted approach maximizes your study time and focuses your efforts on the most crucial material.</p>
<p data-sourcepos="5:1-5:571">Practice dumps provide invaluable insights into the specific ethical dilemmas and scenarios covered in the actual exam. They often present realistic case studies that require you to apply ethical frameworks and principles to make informed decisions. This active engagement with the material solidifies your understanding and strengthens your ability to analyze complex ethical situations. Furthermore, working through these practice scenarios helps you develop a systematic approach to problem-solving, which is essential for tackling the exam's challenging questions.</p>
<p data-sourcepos="7:1-7:538"><strong><a href="https://www.certstopics.com/product-detail/ethics-in-technology.html">https://www.certstopics.com/product-detail/ethics-in-technology.html</a></strong></p>
<p data-sourcepos="7:1-7:538">Beyond simply familiarizing yourself with the content, practice dumps offer a crucial opportunity to refine your test-taking strategies. You can experiment with different time management techniques to ensure you allocate sufficient time to each question. Additionally, you can practice identifying keywords and understanding the nuances of the questions to avoid common pitfalls and improve your accuracy. This preparation not only boosts your confidence but also reduces test anxiety, allowing you to perform at your best on exam day.</p>
<p data-sourcepos="9:1-9:572">While practice dumps are a powerful tool, it's important to use them as part of a comprehensive study plan. Supplement your practice with thorough review of the course materials, including readings, videos, and any other resources provided by WGU. A balanced approach that combines theoretical knowledge with practical application through practice questions will give you the strongest foundation for success. Remember, the goal is not just to memorize answers but to truly understand the underlying ethical principles and their implications in the field of technology.</p>
<p data-sourcepos="11:1-11:456">Ultimately, your success on the WGU Ethics in Technology exam hinges on your dedication and preparation. By leveraging practice dumps strategically, coupled with a well-rounded study approach, you can confidently approach the exam and demonstrate your understanding of ethical considerations in the ever-evolving world of technology. Embrace the challenge, utilize the available resources, and you'll be well on your way to achieving your academic goals.</p>
Hono🔥 フレームワーク基礎

Honoはクラウド環境で動作する軽量なWebフレームワークです。本記事では、Honoの特徴と簡単なアプリケーションの作り方について解説します。

**Honoは、TypeScript/JavaScriptで書かれたシンプルかつ強力なWebフレームワークです。小さなコード量で軽快に動作し、クラウド上のサーバーレス環境にデプロイできるよう最適化されています。ミドルウェアやルーティングを活用して、スケーラブルなAPIを構築できます**。


インストール

Node.jsがインストールされている環境でプロジェクトを作成します。

以下のコマンドでHonoをインストールします。
```
npm install hono
```
はじめてのHonoアプリ

以下に、最も基本的なHonoアプリの例を示します。
```Typescript
import { Hono } from 'hono'

const app = new Hono()

app.get('/', (c) => {
  return c.text('Hello Hono!')
})

export default app
```

実行方法

上記のコードをindex.tsやapp.tsなどのファイルに保存します。

ローカルで実行する場合は、適切なNode.js実行環境とトランスパイラ（ts-nodeなど）を用意した上で、次のように実行します。
```
ts-node index.ts
```
※ 純粋なJavaScriptで書く場合、拡張子を.jsに変えて実行してください。

ルーティング

Honoでは、エンドポイントごとにHTTPメソッドとパスを定義できます。以下の例では/usersというパスを管理するルーターを追加しています。
```Typescript
app.get('/users', (c) => {
  // ユーザー一覧を返す例
  return c.json([
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' },
  ])
})

app.post('/users', (c) => {
  // ユーザーを作成する処理
  // ...
  return c.text('ユーザーが作成されました')
})
```

ミドルウェア

Honoにはミドルウェアを追加でき、ログ出力や認証などの処理を共通化できます。
```Typescript
app.use('*', async (c, next) => {
  console.log(`アクセス: ${c.req.method} ${c.req.url}`)
  await next()
})
```
ルートごとにミドルウェアを設定することも可能です。

デプロイ

Honoはクラウドワーカー環境向けに設計されています。Cloudflare Workersへデプロイする例を示します。

1. wranglerのインストール
```
npm install -g wrangler
```
2. wrangler.tomlの設定

Cloudflare Workers用の設定ファイルwrangler.tomlを作成し、以下のように設定します。
```Javascript
name = "my-hono-app"
main = "index.js"   # ビルド後のメインファイルを指定
compatibility_date = "2025-02-16"
```
[build]
```
command = "npm run build"
```
[vars]
```
ENV = "production"
```

3. ビルドスクリプトの用意

package.jsonにビルドスクリプトを設定し、TypeScriptからJavaScriptに変換します。
```Typescript
"scripts": {
  "build": "tsc index.ts --outDir dist"
}
```
4. デプロイ
```
wrangler publish
```
正常にデプロイされれば、Cloudflare WorkersのURLでHonoアプリが動作します。

まとめ

Honoは非常に軽量でシンプルな構成ですが、拡張性が高くサーバーレス環境への対応もスムーズです。上記のように基本的なWebアプリからAPIサーバーまで、幅広い用途で活用できます。ぜひ一度、手軽に始めてみてください。
Understanding the Importance of COBIT Design and Implementation Certification

<p data-sourcepos="5:1-5:516">In today's complex digital landscape, effective IT governance is crucial for organizational success. The ISACA COBIT Design and Implementation certification equips professionals with the knowledge and skills to design, implement, and manage robust IT governance frameworks based on the globally recognized COBIT framework. This certification is highly valued by employers across industries, as it demonstrates a deep understanding of IT governance best practices and the ability to align IT with business objectives.</p>
<p data-sourcepos="7:1-7:75"><a href="https://www.dumpswrap.com/product-detail/COBIT-Design-and-Implementation.html"><strong>https://www.dumpswrap.com/product-detail/COBIT-Design-and-Implementation.html</strong></a></p>
<p data-sourcepos="7:1-7:75"><strong>Key Benefits of Achieving COBIT Design and Implementation Certification</strong></p>
<p data-sourcepos="9:1-9:476">Earning the COBIT Design and Implementation certification offers numerous benefits for IT professionals. It enhances career prospects by demonstrating expertise in a critical area of IT management. It also increases earning potential and opens doors to leadership roles. Moreover, the certification provides a comprehensive understanding of COBIT principles, enabling professionals to contribute significantly to their organization's IT governance and risk management efforts.</p>
<p data-sourcepos="11:1-11:83"><strong>Responsible Preparation Strategies for the COBIT Design and Implementation Exam</strong></p>
<p data-sourcepos="13:1-13:438">Preparing for the COBIT Design and Implementation exam requires a structured and dedicated approach. It is essential to thoroughly study the official COBIT publications and understand the key concepts and frameworks. Participating in ISACA-approved training courses and workshops can also be beneficial. Additionally, utilizing practice exams and study guides can help reinforce learning and identify areas that require further attention.</p>
<p data-sourcepos="15:1-15:49"><strong>Focusing on Ethical and Legal Study Practices</strong></p>
<p data-sourcepos="17:1-17:350">While preparing for the exam, it is crucial to focus on ethical and legal study practices. Avoid using exam dumps or unauthorized materials, as this can lead to disqualification and damage your professional reputation. Instead, concentrate on building a strong foundation of knowledge and understanding through legitimate resources and study methods.</p>
<p data-sourcepos="19:1-19:55"><strong>Embracing the Journey Towards Certification Success</strong></p>
<p data-sourcepos="21:1-21:392">The COBIT Design and Implementation certification is a valuable asset for any IT professional. By focusing on responsible preparation strategies and ethical study practices, you can increase your chances of success and reap the many benefits this certification offers. Embrace the journey, dedicate yourself to learning, and you will be well on your way to achieving your certification goals.</p>
PowerBIを理解したい人にむけたTips（随時追記）

## はじめに
PowerBIを使いこなしたくてMicrosoft LearnやUdemyで独学を続けています。そのなかで感じるのは、PowerBIは用語も操作も独特！ということ。メモに残して何度も見返している内容を記事にします

## どんなひとに向けた記事？
Excelはひととおり使えるけど、テーブル操作やPivotをバリバリ使ってきたわけではない人

## おすすめ学習コンテンツ
１．＜Udemy＞ Microsoft Power BI - Power BI Desktop入門講座（マスカワ シゲル）
２．

## 用語解説
DAX：エクセルの関数のようなもの。「メジャーの追加」で作成

## データを読み込むときのお作法
・いきなり「読み込み」ではなく「編集」
・シートの名前とデータタイプを確認する

## ビジュアルをつくったときにやること３つ
字をおおきくする　視覚化 ＞ 書式 ＞ グリッド
タイトルをつける　視覚化 ＞ 書式 ＞ タイトルON ＞ タイトルテキスト
データラベルをつける

## 見た目の調整
カスタムビジュアルをつけると見栄えする（会社ロゴとか）
数字にカンマをつける　モデリング ＞

## クエリの操作（Power Query Editor）
クエリのコピペ
・クエリ表示 ＞ 詳細エディタ ＞ 右クリックでコピー
・新たなファイル ＞ データを取得 ＞ 空のクエリ ＞ 詳細エディタ

クエリの名前の編集　・・・プロパティで変更
加えた操作をキャンセルしたいとき　・・・適用したステップで✕を選択
参照ファイルの名前を変更したり保存先を変更した場合
→エラーが出ているクエリ（警告マーク）を選択し　データソースの設定 ＞ リンク変更
クエリはマージできる

## Tips：操作や機能について
データ ＞ データツール ＞ 重複の削除
PowerPivotはそのままPowerBIに取り込める
PowerPivotはエクセル上でPowerQuery/リレーション/DAXが使える
PowerBIで作ったものはPowerPivotにインポートすることはできない
PowerBIカスタムビジュアルを使用するにはサインインが必要

## PowerBIとPowerAppsの連携方法２選
１．PowerAppsのキャンバスアプリにPowerBIタイルを埋め込む
２．PowerBIダッシュボードにキャンバスアプリを埋め込む

## Tips：各用語の性質を感覚的に理解するための文章
PowerQueryで変換してPowerPivotに読み込む
テーブルの名前がクエリの名前になる
リレーションをつくる（データ ＞ データツール ＞ リレーションシップ）
Pivotテーブルをつくる
DAXによって新しいメジャーをつくる
SalesforceワークフローをCSVに一覧化する

Salesforceを昔から使っているお客様環境の遺物に手を付けるときがきました。
フローへ移行されずに残っていたワークフロールールを確認するために、既存のワークフロールールを一覧化するためにちょっとしたコードを用意しました。

### ワークフロールールの一覧化
``` CSVへの変換.sh
# ワークフローファイルを解析し、ワークフロー一覧、項目更新一覧、メールアラート一覧をCSVに出力するスクリプト
$workflowDir = "<ワークフローファイルフォルダへのパス>force-app\main\default\workflows"
$outputWorkflowCsv = "<ワークフロー一覧ファイル出力先へのパス>\workflow_list.csv"
$outputFieldUpdateCsv = "<項目自動更新一覧ファイル出力先へのパス>\field_update_list.csv"
$outputEmailAlertCsv = "<メールアラート一覧ファイル出力先へのパス>\email_alert_list.csv"

[System.Collections.ArrayList]$workflowList = @()
[System.Collections.ArrayList]$fieldUpdateList = @()
[System.Collections.ArrayList]$emailAlertList = @()

# ワークフローファイルを読み取る
Get-ChildItem -Path $workflowDir -Filter "*.workflow-meta.xml" | ForEach-Object {
    $xml = [xml](Get-Content $_.FullName -Encoding UTF8)
    $objectName = $_.BaseName -replace ".workflow-meta"

    # ワークフローのルールを取得
    foreach ($rule in $xml.Workflow.rules) {
        $actionName = ""
        # ワークフローのルールのアクションリストを取得
        foreach ($action in $rule.actions) {
            $actionName += $action.name + "(" + $action.type + "); "
        }
        $workflowList.Add([PSCustomObject]@{
            Object = $objectName
            RuleName = $rule.fullName
            Active = $rule.active
            TriggerType = $rule.triggerType
            Description = $rule.description
            actionName = $actionName
        }) | Out-Null
    }

    # 項目更新ルールを取得
    foreach ($update in $xml.Workflow.fieldUpdates) {
        $fieldUpdateList.Add([PSCustomObject]@{
            Object = $objectName
            FieldUpdateName = $update.fullName
            Field = $update.field
            Operation = $update.operation
            Formula = $update.formula
            Description = $update.description
        }) | Out-Null
    }

    # メールアラートを取得
    foreach ($alert in $xml.Workflow.alerts) {
        $emailAlertList.Add([PSCustomObject]@{
            Object = $objectName
            AlertName = $alert.fullName
            Description = $alert.description
            Recipients = ($alert.recipients | ForEach-Object { $_.field }) -join "; "
            SenderAddress = $alert.senderAddress
            Template = $alert.template
        }) | Out-Null
    }
}

# CSVに出力
$workflowList | Export-Csv -Path $outputWorkflowCsv -Encoding UTF8 -NoTypeInformation
$fieldUpdateList | Export-Csv -Path $outputFieldUpdateCsv -Encoding UTF8 -NoTypeInformation
$emailAlertList | Export-Csv -Path $outputEmailAlertCsv -Encoding UTF8 -NoTypeInformation

Write-Host "ワークフロー一覧を $outputWorkflowCsv に出力しました。"
Write-Host "項目更新一覧を $outputFieldUpdateCsv に出力しました。"
Write-Host "メールアラート一覧を $outputEmailAlertCsv に出力しました。"
```

## 利用方法
上記シェルを開いて、ワークフローのメタファイルが保存されているパスを指定してください。
指定フォルダに入っている「Account.workflow-meta.xml」のようなワークフローファイルをワークフロールール一覧、項目更新一覧、メールアラート一覧に分割して保存します。
Next.js × Rails API × Pusher でリアルタイム通知を１時間で実装する

# Next.js × Rails API × Pusher でリアルタイム通知を実装する

今回は Next.js（フロントエンド）と Rails API（バックエンド）を使って、リアルタイム通知を Pusher で実装する方法について書いていこうと思います。

## 🎯 なぜ Pusher なのか？

リアルタイム通知を実装する手段はいくつかありますが、Pusher は以下の理由で採用しました。

- **簡単に導入できる**: WebSocket の実装を自前でやると結構面倒ですが、Pusher を使えばシンプルに実装可能。
- **無料枠がある**: 個人開発で試す分には十分。
- **Next.js との相性が良い**: Pusher の JavaScript SDK を使えば、クライアント側も簡単にリアルタイム更新ができる。

それでは、実装を見ていきましょう！

---

## 🚀 Pusher のセットアップ

まずは Pusher のアカウントを作成し、アプリを作成します。

1. [Pusher](https://pusher.com/) にアクセスし、アカウントを作成。
2. **Channels** のアプリを新規作成。
3. ダッシュボードに表示される `app_id`, `key`, `secret`, `cluster` をメモ。

次に、Rails API で Pusher を使えるようにセットアップします。

### 📌 Rails 側の設定

```ruby
# Gemfile

gem 'pusher'
```

```bash
$ bundle install
```

環境変数を `.env` に設定（dotenv を使う場合）

```
PUSHER_APP_ID=your_app_id
PUSHER_KEY=your_key
PUSHER_SECRET=your_secret
PUSHER_CLUSTER=your_cluster
```

Pusher を初期化する設定を `config/initializers/pusher.rb` に追加。

```ruby
require 'pusher'

Pusher.app_id = ENV['PUSHER_APP_ID']
Pusher.key = ENV['PUSHER_KEY']
Pusher.secret = ENV['PUSHER_SECRET']
Pusher.cluster = ENV['PUSHER_CLUSTER']
Pusher.logger = Rails.logger
Pusher.encrypted = true
```

### 🔔 通知を送るメソッドを実装

```ruby
class NotificationService
  def self.notify(channel_name)

    Pusher.trigger(channel_name, 'new_notification', {
      message: "新しいメッセージがあります"
    })
  rescue StandardError => e
    Rails.logger.error "Pusher trigger failed: #{e.message}"
  end
end
```

**ポイント:**

- ユーザーごとに異なる `channel_name` を設定。
- `notification` のデータを JSON に変換して送信。
- 例外処理を追加してエラー時にログ出力。

これでバックエンド側の準備は OK です！

---

## 🎨 Next.js 側の実装

Pusher の JavaScript SDK をインストールします。(yarnを使った例）

```bash
$ yarn add pusher-js
```

Pusher を初期化して、通知を受け取るロジックを実装します。

```tsx
import { useEffect, useState } from 'react';
import Pusher from 'pusher-js';

const pusher = new Pusher(process.env.NEXT_PUBLIC_PUSHER_KEY!, {
  cluster: process.env.NEXT_PUBLIC_PUSHER_CLUSTER!,
});

const NotificationComponent = () => {
  const [notifications, setNotifications] = useState([]);
  const [unreadCount, setUnreadCount] = useState(0);

  useEffect(() => {
    const channel = pusher.subscribe('hoge');

    channel.bind('new_notification', (data) => {
      setNotifications((prev) => [data.notification, ...prev]);
      setUnreadCount((prev) => prev + 1);
    });

    return () => {
      channel.unbind_all();
      channel.unsubscribe();
    };
  }, []);

  return (
    <div>
      <h3>通知 ({unreadCount})</h3>
      <ul>
        {notifications.map((notif, index) => (
          <li key={index}>{notif.message}</li>
        ))}
      </ul>
    </div>
  );
};

export default NotificationComponent;
```

**ポイント:**

- `useEffect` を使って Pusher のチャンネルを購読。
- `bind('new_notification', callback)` でリアルタイム通知を受け取る。
- 通知を state に追加し、未読カウントを更新。
- `useEffect` のクリーンアップでチャンネルを解除。


## 🎬 まとめ

これで、Next.js × Rails API × Pusher を使ってリアルタイム通知を実装できました！

✅ Rails 側で Pusher をセットアップし、通知を送信。
✅ Next.js 側で Pusher を購読し、リアルタイム更新。
✅ クリーンアップ処理も忘れずに実装。

個人開発でも活躍する Pusher、ぜひ使ってみてください！🚀
５、2進数の足し算とひき算


**足し算とひき算の基本は10進数と変わりない。**
**桁が上がったり下がったりする時の数が異なる。**

**・足し算**

  　1111   
＋  101
＿＿＿＿
 10100

1+1は10なので桁上がり
1+0+1は10なのでまた桁上がり
1+1+1は11なのでまたまた桁上がり
1+1は10またまたまた桁上がり
繰り上がった1がそのまま入る


**・ひき算**
ひき算を考える前に、負の数は補数を使って表す。

**補数は2つある**
①その桁数で最大値を得る為に補う数
②次の桁に繰り上がる為に補う数

・10進数では9の補数と10の補数
10進数123という数があるとする。
3桁の最大値は999
999 -123＝876←9の補数

次の桁に上がる数は1000
1000 -123＝877←10の補数


・2進数では1の補数と2の補数を使う
☆4桁の2進数0011という数があるとする。
4桁の最大値は1111
1111 -0011＝1100（いくつ足せばその数になるか考える）
　　　　　　　↑これが1の補数
1100という数値を補ってやれば最大値が得られる。

次の桁に繰り上がる数は10000
10000 -0011＝1101（いくつ足せばその数になるか考える）
　　　　　　　　↑これが2の補数
1101という数値を補ってやれば桁上がりする数が得られる。

**負の数を表すのに2の補数を使う。**
＜2の補数の簡単な求め方＞
10進数の3は、8ビットの2進数では00000011　
①全てのビットを反転させる（0と1を入れ替える）→11111100　
②それに1を加算する
10進数の-3は、2進数では、11111101　　←2の補数

 〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜  
 5-3を4けた2進数で表すと、
 0101-0011
 -0011は、+（-0011）とも表せる。
 -0011は、2の補数表現を使って、1101と表せる。

つまりコンピューターは、
このような足し算を使って2という答えを出す。
 0101＋1101＝（10010←桁あふれしたビットは切り捨て。）0010
 （5）　（-3）　　（2）
 

〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜


 ☆ちなみに2の補数を使って負の数を表す場合も、
 1ビット目は符号として扱うことができる。
 先頭のビットが0の場合は負の数、
 0は正の数を表す。
 
 ☆正の数と負の数は、互いに2の補数表現となる関係にある。

 
|−8|-7|-6|-5|~|-2|-1|0|
|-|-|-|-|-|-|-|-|
|1000|1001|1010|1011|~|1110|1111|0000|
|_|7|6|5|~|2|1|
|_|0111|0110|0101|~|0010|0001|

☆負の数の方が表せる数が１つ多くなることに注意する。
AWS重要知識(AWS-SAA,SAP対策) 〜EC2編〜

## 目的
AWSの主要サービスであるEC2を網羅的に学ぶ

## EC2のSLAおよびサービスクレジット
重要度⭐️⭐️⭐️⭐️⭐️

クラウドを扱うにあたって、SLAおよびサービスクレジットは切り離せない。

下記サイトがEC2のSLAに関する公式ドキュメント
https://d1.awsstatic.com/legal/AmazonComputeServiceLevelAgreement/Amazon_Compute_Service_Level_Agreement_Japanese_2022-05-25.pdf


## EC2のリージョン、サブネットは後から変更できない。
重要度⭐️⭐️⭐️⭐️⭐️

まぁ、そりゃそうか
スナップショット取って作り直すしかない

## EC2の復元
重要度⭐️⭐️⭐️⭐️

EC2の復元の際に、マシン内部のデータはAMIで保持されるがEIP,セキュリティグループ,IAMロールの挙動

### EIP
→復元時に新しいEIPがアタッチされる
　IPアドレスが変わって困るケースの方が多いので復元する際はEIPをデタッチして再アタッチするようにする

### セキュリティグループ
 →同じVPCに復元する際は同じセキュリティグループがアタッチ
 違うVPCの場合はVPCのデフォルトのセキュリティグループがアタッチされる

### IAMロール
 →何もアタッチされない
 手動でアタッチする(まぁ自動化できるかもしれんが)


## AMIについて
:::note
AMIとは
マシンイメージ
EC2はこのAMI内の情報を元にOSやソフト、ミドルを構築する
:::

### AMIの種類
重要度⭐️⭐️⭐️⭐️⭐️

AMIの種類

①:クイックスタートAMI
→AWS側で用意されたOSだけのAMI
AmazonLinuxなどは無料枠なのでこれを使ったことがある人は多いはず

②:Marketplace AMI
→企業が作成したAMI
OracleならOracleが既にセットアップされた各OSのAMI
みたいな感じで提供
ただし、ミドルのライセンス料＋αのため非常に使用料が高い。
あとドキュメントが基本英語。

③:自分のAMI
→既に存在するEC2から取得したAMI
 バックアップなどで使用するケースが多い

④:コミュニティAMI
→公式ではなく、有志が作成したAMI(公式もあるが)
OS、ソフト、ミドルがセットになって提供されているケースが多い。
しかし、厳正な審査はないのでセキュリティ的に安全とは言い切れないのであまり使うケースはない。

## AMIとEBSスナップショット
重要度⭐️⭐️⭐️

AMIは『EBSスナップショット』+『インスタンス構成情報』
AMIを作成するとそれと依存関係を持つEBSスナップショットも作成される。
EBSスナップショットは『EBSのスナップショット』のみ

AMI自体には料金はかからないので基本的にEC2のバックアップはAMIになると思われる。

## EBSスナップショット
EBSスナップショットはS3で保存される。
2回目以降は差分バックアップ方式

EBSスナップショットは有料(約0.05 USD/GB/月)

## EC2スナップショットの自動取得&世代管理

### Data Lifecycle Manager
重要度⭐️⭐️⭐️

EC2のAMIバックアップの世代管理/自動取得はDLM(Data Lifecycle Manager)で可能
日付/日時でバックアップ頻度を指定できる
また、AMIはリージョン間コピー可能

:::note
バックアップのターゲットはタグ指定のみ
クロスアカウントコピーは非対応
:::

### AWS Backup
重要度⭐️⭐️⭐️⭐️
Data Lifecycle ManagerはEC2/EBS特化のバックアップサービスに対してこっちはほぼすべてのサービスに対応しています。
自動取得/世代管理/リージョン間コピーにももちろん対応


バックアップの信頼度/柔軟性はData Lifecycle Managerより高いですが、コストは割高です。

:::note
AWS Backupはクロスアカウントコピーにも対応
また、RDSなども一括でバックアップできるため全体での復元が容易
:::


でもAWS Backup使ってるケースの方が多い気がしている。。。

AWS backupのEC2インスタンス復元の資料
https://docs.aws.amazon.com/ja_jp/aws-backup/latest/devguide/restoring-ec2.html?utm_source=chatgpt.com
## EC2の時刻同期
重要度⭐️⭐️

LinuxのEC2の時刻同期はAmazonのNTPが自動設定されている。
また時刻同期に使用するサービスはChorny

名称 :: Amazon Time Sync Service
IPアドレス:: 169.254.169.123


この時刻同期の通信はセキュリティグループで制御できず、外部からこのNTPにはアクセスできない。

時刻同期に関するドキュメント
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/set-time.html



## EC2はAMIから作り直すとec2-userが復活する
重要度⭐️⭐️⭐️⭐️

EC2を作成するとデフォルトでroot権限を持った「ec2-user」が作られる
運用上は他のユーザーでsudo切り替えなどを行うため、必要性がなく削除することが多い

```
sudo userdel -r ec2-user
```

しかし、AMIから作り直すと復活してしまう
これを防ぐためには削除に加えて「ec2-user」でのログイン自体を禁止する必要あり


ec2-userはデフォルトで存在するユーザーかつrootである関係上、一番攻撃対象になりやすい。必ずログイン自体を禁止しておくこと。


## EC2のインスタンスサイズ変更制約
重要度⭐️⭐️
EC2の起動時にはインスタンスサイズを選択する。
これは後から変更できるが互換性のあるインスタンスサイズにしか変更できない。

t2.micro → t4g.micro
↑
世代とプロセッサが違うので不可能

その他の制約については下記公式ドキュメント参照
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/resize-limitations.html
(インスタンスタイプによってはEBSのアタッチ数上限があるのは知らなかった)
Raspberry Pi OS BookwormでGUIアプリを自動起動させる3つの方法

## はじめに

最近、業務においてラズパイで動くGUIアプリケーションを開発していたりなんかしています。

そんな流れで、OS起動時にアプリを自動起動する方法を探していました。
いわゆるWindowsのスタートアップにあたるものです。GUIのプログラムを起動時に実行するための設定方法を探していました。

結局のところ、ラズパイOSの最新であるBookwormでは、使用するディスプレイサーバーを設定で変更することができ、それぞれに設定方法が異なるのだとわかりました。
自分の備忘も兼ねて、まとめました。

## 動作環境

Raspberry Pi 5

![スクリーンショット 2025-02-18 145451.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84215/56c84c84-9280-407f-979b-85133e8e3476.png)


## ディスプレイサーバーを切り替える

ラズパイOSの最新である `Bookworm` になり、ディスプレイサーバーも旧来のX11環境(X Window System)から `Wayland`が標準となりました。
Bookwormがリリースされた当初は `Wayfire`（Waylandコンポジタ実装の一つ）を使っていたようですが、
最新では `Labwc` が採用されて新規にOSイメージを作成するとこちらが標準になっているようです。

どのディスプレイサーバーを使用するかは、`raspi-config`で変更することが可能です。

1. コンソールを開き、`sudo raspi-config`コマンドを実行する
2. `Advanced Options` を選択
3. `Wayland` を選択
4. `X11` `Wayfire` `Labwc` から使用したいものを選択

![スクリーンショット 2025-02-18 101937.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84215/d66db8be-ac07-4578-8589-786d90ed10f5.png)

![スクリーンショット 2025-02-18 102030.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84215/01225214-2269-44e1-a540-594a77245db3.png)

![スクリーンショット 2025-02-18 102106.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84215/011132ae-0705-45ac-ae06-93581fd59864.png)

## X11 (Openbox with X11) でautostart設定
autostartファイルの雛形を、ホームディレクトリのコンフィグにコピーし、`~/.config/lxsession/LXDE-pi/autostart` ファイルに実行したいコマンド名を追記する。

```sh
$ mkdir -p ~/.config/lxsession/LXDE-pi
$ cp /etc/xdg/lxsession/LXDE-pi/autostart ~/.config/lxsession/LXDE-pi/
$ echo 'bash {スクリプトのパス}/xxx.sh' >> ~/.config/lxsession/LXDE-pi/autostart
```

## Wayfire (Wayfire with Wayland) でautostart設定
最下部または既存のautostartセクションに追記する。

```sh:~/.config/wayfire.ini
[autostart]
panel = wfrespawn wf-panel-pi
background = wfrespawn pcmanfm --desktop --profile LXDE-pi
xdg-autostart = lxsession-xdg-autostart
{処理名} = bash {スクリプトのパス}/xxx.sh
```

## Labwc (Labwc with Wayland) でautostart設定
`autostart`ファイルを作成し、実行したいコマンドを追記する。

```sh:~/.config/labwc/autostart
bash {スクリプトのパス}/xxx.sh &
```

## おわりに
以上、ユーザー固有の自動起動設定についてのまとめでした。
これを書きながら、Google検索していると、普段から当たり前のようにRaspberryPiやLinux環境をさわっていたり、ディスプレイサーバーを切り替えて遊んでいる方たちにはごくごく知られた事なのかなと思ったり、わざわざまとめるまでもないのかと思ったりもしました。が、自分自身が忘れた頃にまた同じ検索キーワードでGoogle検索をして、ほしい情報にたどり着くまでに時間を消費するのも勿体無いので、まとめておくことにしました。

システム共通の自動起動設定がまた別にあるのねという事に気付いたりしましたが、それはまた別の機会に。


## 参考
- [Raspberry Piでプログラムを自動起動する5種類の方法を比較・解説](https://qiita.com/karaage0703/items/ed18f318a1775b28eab4) - Qiita
- [Raspberry Pi OS bookwormでアプリを自動起動させる方法](https://raspida.com/wayfire-autostart-setting/) - ラズパイダ
- [Autostart with labwc](https://forums.raspberrypi.com/viewtopic.php?t=379321) - Raspberry Pi Forum
- [Waylandのコンポジター　Wayfire→Labwc](https://qiita.com/shinya_nakashima/items/067083f33a6e7d3f0c50) - Qiita
【初心者向け】PowerApps + Power Automate で Azure Blob Storage の画像を削除する方法

# はじめに
Azure Blob Storageに格納されている画像をPower AppsのアプリとPower Automateのフローを組み合わせて、削除を実行する方法を解説します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3719434/6b34d996-d971-48b5-830d-17162e1080d0.png)

**↓↓画像の追加方法については以下の記事に投稿してますのでこちらをお読みください↓↓**

https://qiita.com/Sanwa_Paint/items/e0d98f9c91f325d474c4




:::note warn
Azure Blob Storageのコネクタを利用するにはプレミアムライセンスが必要です。
標準のライセンスでは利用できないため、必ず先にライセンスを確認しておいてください。
:::

## Azure Blob Storage とは？
Microsoft社が提供するクラウド用オブジェクトストレージソリューションです。
> テキストデータやバイナリデータなどの大量の非構造化データを格納するために最適化されています。

https://learn.microsoft.com/ja-jp/azure/storage/blobs/storage-blobs-introduction


## 方法
**1\. Power Automate側でフローの作成**
+ Power Automate にサインインして、フローの作成を開始します。
+ Power Automateのホーム画面で「+ 作成」をクリックします。
+ 「インスタントクラウドフロー」を選択します。
+ 「Power Appsがフローを呼び出したとき(V2)」のトリガーを選択して「作成」を押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3719434/21e25911-038f-4158-8690-45abb7183b4a.png)

+ 「+入力の追加」を押してPower Apps側から受け取るパラメータの設定を行ってください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3719434/d0081c34-c6fa-40c4-a635-5e761fb35a03.png)

+ 「新しいステップ」を追加しAzure Blob Storageを検索し「BLOBを削除する(V2)」を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3719434/cc1cbe53-eed8-4437-835e-ea699870583a.png)

+ 「ストレージアカウント名またはBLOBエンドポイント」と「BLOB」を設定します。
「BLOB」名は直接記入していますが、変数に格納したり、Power Apps側から受け取るパラメータに設定したりも可能ですので、使いやすいやり方で記入してください。
今回はファイル名のみパラメータを使用しています。

+ 設定が完了したらフローを保存します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3719434/88e0bcf4-0a09-45e1-8586-daf589f75dd4.png)


:::note info
設定の前にPower AutomateにAzure Blob Storageが接続されているか確認をしておいてください。
接続されていない場合は接続を行ってください。
接続方法については公式ドキュメントをご確認ください。
:::

**【Power Apps から Azure Blob Storage に接続する】**

https://learn.microsoft.com/ja-jp/power-apps/maker/canvas-apps/connections/connection-azure-blob-storage


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3719434/d7be60f5-3823-4a90-99db-91635e9ae528.png)


**2\. Power Apps側でフロー実行をするボタンの作成**
+ さきほど作成したフローをPower Apps内に追加する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3719434/d963f530-7df3-4f02-8b3f-49234368c62a.png)

+ ボタンを置き、`OnSelect`を以下のように記入。

```ruby:書き方
BLOB削除フロー.Run("sample1")
```

+ 引数の `"sample1"` はPower Automateへ引き渡すパラメータです。今回は直打ちしていますが、`UploadedImage.Image` のように動的に変更することも可能です。

以上で設定は完了です。

## 実行してみる
では、作ったフローを実際に動かしてみましょう。
今回はBLOBストレージ内に置いてある `sample1.jpg` というファイルを削除できれば成功です。

**【実行前】**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3719434/5cf5cde7-1ab0-470a-9127-5a952eec3ed1.png)


**【実行後】**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3719434/353a0f0d-1989-48de-ba9e-76746629edd6.png)


**【フローの確認】**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3719434/4783882b-f8f2-4014-8b99-7231ada3e6e7.png)


## まとめ
以上がPower AppsとPower Automateを利用したAzure Blob Storageの画像の削除方法になります！
他にも方法はありますが、こちらのやり方が初心者の方には一番分かりやすい方法だと思います！
また追記事項などありましたら追加していく予定です！
ぜひ試してみてください！！！
PHPで可変関数を初めてちゃんと使った

可変関数を初めてちゃんと使ったので、記録。

# 概要
PHPには「可変関数」という概念がある。
https://www.php.net/manual/ja/functions.variable-functions.php

> PHP は可変関数(variable functions)の概念をサポートします。 これにより、変数名の後に括弧が付いている場合、その値が何であろうと PHPは、同名の関数を探し実行を試みます。 この機能は、コールバック、関数テーブル等を実装するために使用可能です。

とのこと。

共通部品に関数名の文字列を渡し、共通部品側では外部から受け取った変数名で関数を実行！なんてことができる。

# 同じクラス内で使う場合
関数名の文字列を変数として受け取り、その変数を用いて実行することができる。

```php
class MyClass {
  public function execute($functionName) {
    $this->$functionName();
  }

  private function method1() {
    $this->execute('method2');
  }

  private function method2() {
    // 処理
  }
}
```

# 異なるクラス同士で使う場合
外部クラスに存在する関数を実行する場合は、関数名単体の文字列指定では使えない。
完全修飾名（例：`MyClass::myMethod`）で指定するか、クロージャーを使う必要がある。

## 完全修飾名で関数名を受け渡しする
public static メソッド名の文字列を受け取り、call_user_func(メソッド名の文字列, 引数) で実行する。

呼び出される側（関数を定義しているクラス）
```php
public function handle($data) {
    $xxxClass = new xxxClass();
    $xxxClass->execute(
        $data,
        'App\Http\Controllers\XXXController::myMethod' // public staticメソッド
    );
}
```

呼び出し側
```php
class xxxClass
{
    public function execute($data, $method) {
        $result = call_user_func($method, $data);
        // 処理
    }
}
```


## クロージャーを使う
関数をクロージャーとして渡すこともできる。

呼び出される側（関数を定義しているクラス）
```php
public function handle($data) {
    $xxxClass = new xxxClass();
    $xxxClass->execute(
        $data,
        function ($params) { // クロージャーで formatData を渡す
            return $this->formatData($params);
        }
    );
}

private function formatData($params)
{
    // コントローラー独自の処理
}
```

呼び出し側
```php
class xxxClass
{
    public function execute($data, $method) {
        $result = $method($data);
        // 処理
    }
}
```

なお、無名関数で渡すこともできる。
```php
$xxxClass->execute(
    $data,
    function ($params) {
        // 処理
    }
);
```


# 使ってみた
事例：取得したUserデータを1,000件ずつchunkしてCSVに書き込みたい。
書き込み処理は他の機能でも使えるようにUseCaseに切り出したい。
行フォーマットは該当のコントローラでしか利用しないので、privateメソッドにしておきたい。

Controller
```php
public function handle() {
    // ファイルパスなどを準備
    $useCase = new xxxUseCase();
    $useCase->execute(
        User::query(),
        $filePath,
        function ($model) {
            return $this->rowFormatter($model);
        }
    );
    
    // 作成したCSVをダウンロード
}

private function rowFormatter(User $user): array
{
    return [
        $user->id,
        $user->name,
        $user->email,
        // 他の加工処理など
    ];
}
```

UseCase
```php
function execute($query, $filePath, $rowFormatter) {
    $fp = fopen($filePath, 'a');
    $query->chunk(1000, function ($models) use ($fp, $rowFormatter) {
        foreach ($models as $model) {
            $row = $rowFormatter($model); // 関数を呼び出す
            $this->writeRow($fp, $row);
        }
    });
    fclose($fp);
}
```
Pythonで等差数列・等比数列の総和を求める関数

まずは、数列をlistに格納してからsumで総和を計算する方法。
- Space Complexity: O(n)（たぶん）
- Time Complexity: O(n)

```Python
# 等差数列
def sigma_arith_loop(a, d, l, m):
    """
    a: int or float - 初項
    d: int or float - 公差
    l: int          - 計算の始点
    m: int          - 計算の終点
    """
    ls = [a + d * (k - 1) for k in range(l, m + 1)] 
    
    return sum(ls)

# 等比数列
def sigma_geo_loop(a, r, l, m):
    """
    a: int or float - 初項
    r: int or float - 公比
    l: int          - 計算の始点
    m: int          - 計算の終点
    """
    ls = [a * r ** (k - 1) for k in range(l, m + 1)]
    
    return sum(ls)
```
次に、公式を使用する方法。こちらの方が計算効率はいい。
- Sapce Complexity: O(1)
- Time Complexity: O(1)
```python
# 等差数列
def sigma_arith(a, d, l, m):
    """
    a: int or float - 初項
    d: int or float - 公差
    l: int          - 計算の始点
    m: int          - 計算の終点
    """
    # この数列の一般式は a_n = a + (n-1)d
    # これをa_n = dn + Bの形に変換すると  a_n = dn + (a-b)
    # 等差数列の総和の公式に当てはめる
    return (m - l + 1) * (2 * a + (m + l - 2) * d) / 2

# 等比数列
def sigma_geo(a, r, l, m):
    """
    a: int or float - 初項
    r: int or float - 公比
    l: int          - 計算の始点
    m: int          - 計算の終点
    """
    # この数式の一般式はa_n = a * r ** (n-1)
    return a * (r ** m - r ** (l - 1)) / (r - 1)
```
[動的計画法の最適化] Knuth optimization in DP

LeetCodeをやっていて学んだ。
個人wikiに書いていたが、せっかくなので誰かのためになると信じて共有する。

## 背景

Leetcode でこのゲーム理論の問題を解いている時に学んだ。詳細はこの記事では解説しないが、`O(N^3)` 時間かかるアルゴリズムを `O(N^2)` 時間に最適化するために必要だった。

**[1563. Stone Game V](https://leetcode.com/problems/stone-game-v)**

実際にknuth optimizationを使用した自分の解答は[これ](https://leetcode.com/problems/stone-game-v/solutions/6437035/bottom-up-dp-with-knuth-optimization-by-hflwb/)。

## アルゴリズム

早速説明する。

Knuth optimization is what I can use in ***range DP***.

```python
@cache
def min_cost(start, end):
    for mid in range(start, end):
        # With knuth optimization, I can find the "best cut", which minimizes:
        # cost(mid) + min_cost(start, mid) + min_cost(mid + 1, end)
```

Note that `cost()` function must satisfy the following conditions.

```
For a ≤ b ≤ c ≤ d,

1. (MLI) It is a Monotone on the lattice of intervals: [cost(b, c) ≤ cost(a, d)]
2. (QI)  It satisfies the quadrangle inequality:       [cost(a, c) + cost(b, d) ≤ cost(b, c) + cost(a, d)]
```

That being said, Knuth optimization tells this. Use this to reduce the number of mids to be checked in L5.

```python
best_cut[i][j-1] - 1 <= best_cut[i][j] <= best_cut[i+1][j] + 1
```

Now, use bottom up DP to make use of this optimization. Fill in cells in this order, so that I can reduce the ***candidates*** of the best cut in each `(start, end)`.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534442/3a86b594-a0db-45a0-83f8-16cd69cdbe5e.png)
「はじめてのNetSuite～学習方法のススメ～」

<br>
こんにちは！株式会社IT働楽研究所の戸谷です！

年々、花粉の到来が早まっていますね。2月中旬の現在、すでにくしゃみや鼻詰まりに苦しんでいます。電車内で鼻をすする音やくしゃみが聞こえるたびに、「花粉に負けるな、未来は俺らの手の中」と心の中からエールを送っています。

さて今回は、初めてNetSuiteを触るシステム担当者様向けに、学習方法をご紹介します！

 

## はじめに
<!--
何についての記事なのか、誰に向けた記事なのかを簡単に説明しましょう。
-->

### この記事でわかる・できること
<!--
読んだら何がわかるようになるのか、何ができるようになるのかを書きましょう。
-->

- NetSuiteを動画学習する際の適切な順序が理解できる。
- 効率的に学習を進めるための前提を整理できる・


### この記事の対象者
<!--
誰に向けた記事なのかを明確にすることで読者に選んでもらいましょう。
-->

- NetSuiteを契約済みで、これからシステム担当者として運用を担う方
- NetSuite導入済みで、前任のシステム担当者から引き継ぎを受ける方



## 学習の前提

まず、NetSuiteとはどのようなシステムでしょうか？
NetSuiteは企業の業務を一元管理するためのクラウドERP（統合基幹業務システム）です。
企業内では、営業部門が独自のツールを使用し、製造部門が生産管理システムを活用し、総務・経理部門が会計システムを利用するといったように、異なるシステムが併存するケースが一般的です。
NetSuiteは、これらの業務を統合し、一元管理するためのシステムです。
企業の業務を丸ごと対応することを想定しているため、相当数の機能を備えており、大規模なシステムとなっています。

### NetSuiteの特徴

- 企業の業務を統括管理できるシステム
- 広範な機能を持ち、非常に大規模なシステム





### LCS（Learning Cloud Support）を活用する

Oracle公式からアナウンスされているNetSuite説明動画とデモ環境のリクエストから実際にNetSuite環境を操作しながら理解が出来るコースとなっております。
1. まずは「NetSuiteの基礎」コースを受講
2. 次に「NetSuite - 管理者の基礎」を受講
3. その後、自社のビジネス要件に沿って必要なコースを選択
LCSを活用することで、体系的にNetSuiteの基礎から応用まで学習できます。

コース一覧は下記のような形です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3987195/7d020023-42bf-444f-99e9-5fb939a621c5.png)

## 学習のその先へ

LCSを利用して学んだことを実務で活かすことが重要です。

NetSuiteのシステム担当者になると、日々ユーザーからの問い合わせ対応や、ビジネスシーンの変化にあわせて運用ルールの変更検討を行う必要があります。
そのため、常に「この業務はNetSuiteで行うとどうなるのだろう？」と考え続け、不明点に答えが出ないようであればがOracleサポートへの問い合わせを行う等、頭も身体もフル回転することが求められます。

## NetSuite担当者の役割
- システムの適切な運用・改善を主導する
- 入力ユーザーの疑問や問題を解決する
- ベンダー依存を減らし、コスト削減を図る

 システム担当者が自ら学習し,対応できる範囲を広げることで、外部ベンダーへの依頼コストやコミュニケーションコストの削減につながります。


## まとめ

NetSuiteの学習には、LCSを活用することで包括的な理解を得ることができます。基礎からしっかり学び、運用に活かすことで、システムをより効果的に活用できるようになります。

適切な学習を怠ると、後々高額な外部支援コストが発生するケースもあります。システム担当者の知識が、企業全体の業務効率に大きな影響を与えることを忘れずに、しっかり学んでいきましょう！



## 参考資料
<!--

https://qiita.com

[リンク1](https://qiita.com)
-->

https://qiita.com

[リンク1](https://qiita.com)
Apache Icebergで地理空間情報の型(GEOMETRY, GEOGRAPHY)に対応（但しV3から）

# 概要
Apache `Iceberg`のV3から公式のTable Specとして、`GEOMETRY`型, `GEOGRAPHY`型の列が追加されました。
もはや『GeoParquet 2.0』ではなく普通のParquetの世界で地理空間情報が扱える
ようになり、[オープンテーブルフォーマットの型でちゃんとサポートされた](https://iceberg.apache.org/spec/#appendix-e-format-version-changes)ということですね。

https://www.linkedin.com/posts/jatorre_geoparquet-20-going-native-activity-7295876940106784768-hvb_?utm_source=share&utm_medium=member_desktop&rcm=ACoAABwZiigBU12Vo1ks2WK5hZrIYSrZnwqIF7I

『GeoParquet 2.0』から1年9カ月...ついに来ましたね:wink:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30994/7660923e-c628-4dbf-95b6-d85732486648.png)

https://github.com/opengeospatial/geoparquet/discussions/169


## AWSでの利用（S3 Tables）
Apache Icebergといえば、Open Table Format (OTF) の代表格なわけですが、AWSでは、マネージドIcebergテーブルとして`Amazon S3 Tables`（AWS re:Invent 2024で発表されたS3の新機能）が提供されています。

https://aws.amazon.com/jp/about-aws/whats-new/2025/01/amazon-s3-tables-additional-aws-regions/

- S3バケットの『メタデータ設定を作成』で、『テーブルバケット』を作成することで、Icebergテーブルを作成する。するとバケットのメタデータを管理し、検索を可能にしてくれます（これまでのように都度Glueテーブルを作る必要がなくなる。自動的なメンテもしてくれる）
- この`Apache Iceberg`では、Data LayerにてParquetをサポートしており、V3から公式に`GEOMETRY`型, `GEOGRAPHY`型がサポートされるわけですね
- `Athena`では まだ`Iceberg`のバージョン`1.4.2`をサポート、とドキュメントにはありますが、v2のテーブルを作成できるのでv3もそのうち対応されるでしょう
    - [Apache Iceberg テーブルをクエリする
](https://docs.aws.amazon.com/ja_jp/athena/latest/ug/querying-iceberg.html)
    - [Athena の Iceberg テーブルでサポートされているデータ型
](https://docs.aws.amazon.com/ja_jp/athena/latest/ug/querying-iceberg-supported-data-types.html)

## 何がうれしいのか
`Geoparquet`推し（地理空間情報の技術者とか？）に、OTFの代表格であるApache Icebergの公式サポートが加わったことで、地理空間情報の分野でもクラウドネイティブ化が大いに盛り上がることでしょう（個人の感想です）:angel:


## 参考情報

- Iceberg（V3）でのTable Spec
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30994/3e93a6fc-51de-47ef-b7fc-dd4e169d6441.png)

https://github.com/apache/iceberg/blob/main/format/spec.md#version-3-extended-types-and-capabilities

https://iceberg.apache.org/spec/#parquet

https://github.com/apache/parquet-format/pull/240