IoT Trust of Toshiba デバイス・ストレージ

メモ

東芝デバイス&ストレージ株式会社のモノのトラストについてまとめたものを以下に示す。 ただし、genAIにて生成したものなので注意されたし。

1. トラストサービスの目的

トラストサービスは、IoT機器のセキュリティを強化するために以下の3つの目的を達成します。

1.1 製造工程からのトレーサビリティ

  • 鍵の書き込み マイコン製造時に安全に鍵を書き込み、認証局から電子証明書を発行。
  • トレーサビリティの確保 鍵と証明書を基に、機器の製造から運用までの追跡可能性を提供。

1.2 機器の長期メンテナンス保証

  • 証明書の長期更新 機器に対する長期サポートを実現。
  • 正規保守部品の提供 パッチ提供や正規部品の配布による安全なメンテナンスを保証。

1.3 IoT化の支援

  • 鍵の配布 IoT機器向けに鍵を配布し、セキュリティ基盤を提供。
  • エコパートナーとの連携 IoT機器のセキュリティを確保するための協力体制を構築。

2. Root of Trust対応マイコン

Root of Trust対応マイコンは、IoT機器のセキュリティをハードウェアレベルで担保する重要なコンポーネントです。

2.1 主な特徴

  • 証明書埋め込み 証明書を埋め込んだマイコンがトラストサービスと安全に接続・認証。
  • 2つのタイプの実装方法
    • コンパニオンタイプ
      • 既存のセキュリティ非対応プロセッサやマイコンに追加することでセキュリティを担保。
      • 最小構成でセキュリティを提供。
      • ホストプロセッサと連携。
    • スタンドアローンタイプ
      • セキュリティ非対応のマイコンを置き換える形で使用。
      • 機能・性能が充実し、堅牢なDPA(Differential Power Analysis)耐性を持つ。
      • スタンドアローンで動作可能。

2.2 主なセキュリティ機能

  • 真贋判定
  • セキュアOTA(Over-The-Air更新)
  • セキュアブート
  • データ保護

3. ソリューション全体の構成

トラストサービスは、東芝とサイバートラストが提供するハードウェア・ソフトウェアの統合ソリューションです。

3.1 東芝提供

  • ハードウェア
    • Root of Trust対応マイコン
  • ソフトウェア
    • デバイスドライバ
    • 暗号エンジン用API
    • サーバ接続用クライアントSDK
      • ISS/OTA Sub Client
      • Key Management

3.2 サイバートラスト提供

  • ソフトウェア
    • セキュアIoTプラットフォーム(証明書認証サービス)

4. 導入のメリット

  • セキュリティの導入ハードルを低減 エッジからクラウドまで、すぐに使えるハードウェア・ソフトウェア・サービスを提供。
  • IoT機器の本物性保証 トラストサービスを通じて、機器の真正性を保証。

5. 参考情報


以上が文書の内容をまとめたものです。必要に応じて追加情報や詳細な説明をお知らせください!

参考

共有

Trends of Mobility Data Spaces in Japan

メモ

データスペースのユースケースのうち、モビリティは各国の事情を踏まえた課題感が存在するはずです。 たとえば、電車のダイヤの乱れが多い地域では、代替手段の探索需要や代替手段を含むスマートな移動に対する需要が高いはずです。 日本に関連するものをまとめてみます。 なお、本内容は生成AIを活用して出力したので内容の正誤、偏りには十分に注意されたし。

はじめに

  • この文書は、日本のモビリティデータスペースに関する取り組みを分析し、国際的な比較を通じてその特徴や課題を明らかにすることを目的としています。
  • 主な結論として、日本は国内でのデータ連携に注力している一方で、国際的な標準化や連携が課題となっていることが示されました。

セクション1:日本のモビリティデータスペースの現状

1.1 Japan Mobility Data Space (JMDS)

  • 概要
    JMDSは、日本国内でのモビリティデータの共有と連携を目指す基盤です。交通状況のリアルタイム分析や効率的なルート計画、公共交通機関の運行管理、自動運転技術の支援などに活用されています。
    • 主要プレイヤー:NTTデータ、デジタル庁、内閣府。
    • 特徴:オープンデータとプライベートデータの統合を推進し、都市間でのデータ共有の促進を目指しています。
    • 課題:地域間の連携不足、データ標準化の遅れ、データプライバシーの懸念。
    • 引用NTTデータの公式発表

1.2 スマートシティとモビリティデータ

  • Kashiwa-no-haスマートシティ
    千葉県柏市の「Kashiwa-no-haスマートシティ」では、モビリティデータを活用して地域住民の移動効率を向上させる取り組みが進められています。
    • 具体例:電動アシスト自転車の共有サービスや、AIを活用した公共交通の運行最適化。
    • 成果:住民の移動時間削減、交通渋滞の緩和。
    • 引用Kashiwa-no-haスマートシティ公式情報
  • 横浜市のモビリティプロジェクト
    横浜市では、モビリティデータを活用した「MaaS(Mobility as a Service)」の導入が進められています。
    • 具体例:スマートフォンアプリを通じて、電車、バス、タクシー、自転車の利用を一元化するサービス。
    • 成果:観光客や住民の利便性向上、公共交通利用の促進。
    • 引用横浜市公式情報

1.3 地方自治体の取り組み

  • 福岡市の取り組み
    福岡市では、交通データを活用した都市計画が進められています。
    • 具体例:交通量データを基にした道路整備計画や、AIを活用した渋滞予測システムの導入。
    • 成果:交通事故の減少、通勤時間の短縮。
    • 引用福岡市公式情報
  • 富山市のコンパクトシティ構想
    富山市では、公共交通を中心とした「コンパクトシティ」構想の一環として、モビリティデータを活用しています。
    • 具体例:路面電車の運行データを活用した効率化や、住民の移動パターン分析による新路線の提案。
    • 成果:公共交通利用率の向上、CO2排出量の削減。
    • 引用富山市公式情報

セクション2:国際比較

  • 日本、欧州、アメリカ、中国のモビリティデータスペースにおける特徴を比較します。それぞれの取り組みは、技術開発の方向性や社会的背景に応じて異なります。

2.1 欧州(EU)の取り組み

  • 概要
    欧州では、GAIA-Xという大規模なデータインフラを中心に、国境を越えたデータ共有が進められています。この取り組みは、EU加盟国間でのデータ連携を強化し、物流や自動運転分野での効率化を目指しています。
    • 特徴:国際的な標準化を重視し、データの相互運用性を確保。
    • 課題:各国の規制調整が複雑で、データプライバシー問題が依然として大きなテーマ。
    • 引用欧州委員会資料

2.2 アメリカの取り組み

  • 概要
    アメリカでは、民間企業が主導する形でモビリティデータの活用が進んでいます。VerizonのThingSpaceなどのIoT基盤が、スマートシティや自動運転車両のデータ連携に活用されています。
    • 特徴:スピード感のある技術開発と、民間主導による柔軟な運用。
    • 課題:標準化が不十分で、データの断片化が課題。
    • 引用Verizon公式情報

2.3 中国の取り組み

  • 概要
    中国では、国家主導でモビリティデータの統合と活用が進められています。AIを活用した渋滞予測や都市間交通の効率化が行われ、都市部での交通問題解決に寄与しています。
    • 特徴:中央集権的な管理により、迅速な意思決定と実行が可能。
    • 課題:データ管理の透明性が低く、プライバシー保護の議論が不足。
    • 引用中国政府資料

2.4 比較表

以下は、日本、欧州、アメリカ、中国のモビリティデータスペースの特徴を比較した表です。

項目 日本 欧州(EU) アメリカ 中国
主導者 政府と民間の協力 政府主導(GAIA-X) 民間主導(Verizonなど) 国家主導
データの種類 オープンデータとプライベートデータの統合 国境を超えたデータ連携 IoTデータの活用 AIを活用した統合データ
主な活用分野 スマートシティ、公共交通、自動運転 自動運転、物流最適化 スマートシティ、IoT 渋滞予測、都市間交通効率化
課題 地域間連携不足、国際標準化の遅れ 規制調整の複雑さ、プライバシー問題 標準化の不十分 データ管理の透明性欠如
参考URL 日本事例 欧州委員会資料 Verizon公式情報 中国政府資料

セクション3:課題と将来の展望

  • 日本の課題
    • 地域間連携の不足:地方自治体間でのデータ共有がまだ十分に進んでいない。
    • 国際的な標準化:欧州や中国のような国際的なデータ共有の枠組みに遅れ。
  • 将来の展望
    • 国内のデータ標準化を進め、地域間連携を強化する。
    • 国際連携を視野に入れたデータ共有基盤の構築。

結論

  • 日本のモビリティデータスペースは、国内でのデータ連携やスマートシティ構築において重要な役割を果たしています。
  • 一方で、国際的な標準化や連携が課題として残っており、欧州や中国の事例を参考にすることで、さらなる発展が期待されます。p

参考

共有

International Data Spaces Trustworthy and sovereign data sharing enable the data economy

メモ

International Data Spaces Trustworthy and sovereign data sharing enable the data economy のホワイトペーパーの概要。 以下は、GenAIを用いたまとめなので内容正誤には注意が必要。

1. 国際データスペースの重要性

国際データスペース(IDS)は、信頼できるデータ共有を可能にし、データ経済の基盤を形成します。データスペースは、複数の組織間でのデータ共有を促進し、参加者間の信頼を構築するための技術的インフラを提供します。IDSAは、データスペースの基準を確立し、データの価値と信頼を維持するための枠組みを提供します【2】。

2. データ主権の擁護

IDSAは、すべての個人と組織が自分のデータを制御できる世界を目指しています。データ主権は、データの管理と責任を誰が持つかを定義し、データの使用ポリシーや契約を明確にすることが重要です。IDSAは、データコネクタを通じて、これらのルールを理解し、遵守することを支援します【3】。

3. IDSAルールブック

IDSAルールブックは、データスペース内の組織間の相互運用性を確保するためのガイドです。データスペースを構築するための基盤を提供し、信頼性を確保するための必須要素を明確にします。相互運用性は、法的、組織的、意味的、技術的なレベルで重要です【4】。

4. データコネクタの役割

データコネクタは、データスペース内の参加者を接続し、データを安全に共有するためのソフトウェアです。これにより、データの流れが円滑になり、信頼性が確保されます。コネクタは、データ交換サービスを提供し、使用ポリシーを強制します【6】。

5. IDS認証

IDS認証は、データ経済における信頼と相互運用性を実現するための重要な役割を果たします。すべてのデータエンドポイントは共通の信頼フレームワークに従い、厳格なセキュリティ基準に基づいて認証されます【7】。

6. データスペースプロトコル

データスペースプロトコル(DSP)は、データスペースの技術実装の中核を成し、参加者間の相互運用性を保証します。標準化されたプロトコルを確立し、システム間のスムーズな通信を促進します【8】。

7. オープンソースの重要性

IDSAは、オープンソースを活用して市場の採用を加速させることを目指しています。透明性と協力を促進し、持続可能なエコシステムを構築することで、データ共有ソリューションの開発を推進します【9】。

8. 標準化の重要性

標準化は、相互運用性を可能にし、技術的障壁を減少させ、貿易を促進します。IDSAは、国際的な標準化活動を通じて、データスペースの実装を支援しています【11】【12】。

9. 成功事例

IDS標準に基づく成功事例が多くの業界で見られます。モビリティデータスペースやCatena-Xなどのプロジェクトは、データの安全な共有を促進し、新しいビジネス機会を生み出しています【16】【17】。

1. モビリティデータスペース(MDS)

  • 目的: 持続可能な交通を促進するために、モビリティサービスと気候変動への貢献を目指します。
  • 参加者: ドイツ政府を含む200以上のステークホルダーが参加。
  • 機能: リアルタイムのモビリティデータを利用し、交通の最適化や公共交通サービスの向上を図ります。
  • 特徴: IDS参照アーキテクチャに基づき、データの出所と品質を保証するための条件を設定できる仕組みが整っています。

2. Catena-X

  • 目的: 自動車産業のデータ主権とコラボレーションを促進します。
  • 参加者: Daimler、BMW、Volkswagenなど、業界の主要企業が参加。
  • 特長: 競争の激しい業界においても、透明で持続可能なサプライチェーンを実現することを目指します。

3. スマートコネクテッドサプライヤーネットワーク(SCSN)

  • 目的: 高度な製造業におけるサプライチェーンの遅延を解消するために開発されました。
  • 機能: パートナー間のコミュニケーションを促進し、重要な情報を追跡する能力を提供します。
  • 効果: 生産性の向上とコスト削減を実現します。

4. Eona-X

  • 目的: 移動、輸送、観光分野におけるデータ主権を促進します。
  • 参加者: Amadeus Group、SNCF、Air Franceなどの主要企業が参加。
  • 特徴: 複数の交通手段や観光地のデータを統合し、利用者の体験を向上させることを目指します。

10. IDSAの国際的な取り組み

IDSAは、データスペースのエコシステムを構築するために、他のデータ関連団体と協力しています。これにより、データ共有の原則を広め、国際的な協力を促進しています【13】【14】。

この要約は、IDSAの目的、活動、及びデータスペースの重要性を強調しています。

オープンソースの重要性

IDSAは、オープンソースを活用することで市場の採用を加速させることを目指しています。オープンソースは、イノベーションと協力を促進し、持続可能で多様なエコシステムを形成するための重要な要素です。IDSAのオープンソース戦略は、IDSコンポーネントの基盤を構築し、透明性、協力、コミュニティの参加を促進します。このアプローチにより、開発が迅速化し、コストが削減され、IDSソリューションが関連性を持ち続けることができます【9】。

また、IDSAはEclipse Foundationと協力し、Eclipse Dataspace Working Group(EDWG)を設立しています。この協力により、データスペースのための普遍的な標準とソフトウェアコンポーネントの開発が加速されます。IDSAは、オープンソースの開発者ネットワークを活用し、信頼できるデータ共有フレームワークの構築を進めています【9】。

https://dataspace.eclipse.org/become-a-member/

Eclipse Foundationの具体的なメンバーシップ費用についての情報は、以下の通りです。

Eclipse Foundationのメンバーシップ費用

メンバーシップレベル 年会費 主な特典
戦略的メンバー (Strategic Members) $50,000 理事会への席、アーキテクチャ評議会への席、戦略的メンバー専用プログラムへのアクセス
貢献メンバー (Contributing Members) $25,000 理事会での代表権、一般集会への参加と投票、Eclipse Working Groupsへの参加
アソシエイトメンバー (Associate Members) $5,000 公開メールリストへの参加、一般集会への出席
コミッターメンバー (Committer Members) 無料または低額 プロジェクトへの貢献

具体的な金額や詳細は、Eclipse Foundationの公式サイトにて確認できます。特に、最新の情報は公式サイトでの確認が推奨されます。 詳細については、以下のリンクを参照してください: - Eclipse Foundation Membership Levels & Fees [1] - Eclipse Foundation FAQ [2]

データスペースレーダーによる影響の推進

データスペースレーダーは、データスペースの取り組みを迅速に収集し、カタログ化するためのツールです。このツールは、世界中のデータスペースイニシアティブを可視化し、100を超えるエントリーが記録されています。

主な機能と目的

  • パノラマビューの提供: データスペースレーダーは、さまざまなセクターや地域、開発段階におけるデータスペースの取り組みを示します。これにより、参加者はどのようなイニシアティブが存在するかを把握できます。

  • 成功事例の紹介: ユーザーフレンドリーなオンラインフォームを通じて収集された成功事例を紹介し、他のプロジェクトチームが最適な技術を選択する際のガイドとして機能します。

  • 評価基盤の提供: データスペースレーダーは、ビジネス、組織、技術の側面から各イニシアティブの開発段階を評価し、包括的な評価の基盤を形成します。

参考

共有

Eclipse Dataspace Protocol Unofficial draft 13 Dec. 2024

メモ

このメモは、2024/12/13時点の GitHub上で公開されているEclipase Dataspace Protocolのドラフト の内容をまとめたものである。 生成AIにて概要を出力したので内容の正誤については注意されたし。

このドキュメントでは、Dataspace Protocolについて説明します。これは、使用制御によって管理され、Webテクノロジーに基づくエンティティ間で相互運用可能なデータ共有を促進するように設計された一連の仕様です。これらの仕様では、Dataspaceと呼ばれる技術システムの連合の一部として、エンティティがデータを公開し、契約を交渉し、データにアクセスするために必要なスキーマとプロトコルを定義しています。

この仕様の目的

この仕様の目的は、データ共有を容易にするために、データの提供方法を定義することです。

  1. データセットがDCATカタログとしてどのように展開され、使用制御がODRLポリシーとしてどのように表現されるか。 [3]
  2. データの使用を規定する契約がどのように構文的に表現され、電子的に交渉されるか。 [3]
  3. 転送プロセスプロトコルを使用してデータセットにどのようにアクセスするか。 [3]

データスペースプロトコルの主な概念

  • データスペース: エンティティ間での相互運用可能なデータセット共有を促進する一連の技術サービス。 [6]
  • 参加者: データセットを提供および/または消費するデータスペースメンバー。 [6]
  • 参加者エージェント: データセットを提供する参加者に代わって操作を実行するテクノロジーシステム。 [6]
  • カタログ: プロバイダー参加者によってアドバタイズされる、データセットとそのオファーを表すエントリの集合。 [6]
  • 契約交渉: データセットの利用規約を定義する契約について、プロバイダーとコンシューマーの間で行われる一連のインタラクション。 [6][8]
  • 転送プロセス: 契約の条件に基づいてデータセットへのアクセスを提供する、プロバイダーとコンシューマーの間の一連のインタラクション。 [7][8]

データスペースプロトコルは、次のドキュメントで構成されています。

  • データスペースモデルとデータスペース用語集のドキュメント。
  • HTTPSでの共通機能とそのバインディング。
  • カタログプロトコルとカタログHTTPSバインディングのドキュメント。
  • 契約交渉プロトコルと契約交渉HTTPSバインディングのドキュメント。
  • 転送プロセスプロトコルと転送プロセスHTTPSバインディングのドキュメント。 [3]

データスペースプロトコルは、データ転送プロセス自体をカバーしていません。 [4]

相互運用性を確保するために、データスペースプロトコルでは、参加者が互いのサポートされているバージョンを確実に発見できるようにする必要があります。 [9]

カタログプロトコル

カタログプロトコルは、コンシューマーが抽象的なメッセージ交換形式を使用してカタログサービスからカタログを要求する方法を定義します。 [10]

契約交渉プロトコル

契約交渉(CN)には、使用契約に基づいて1つ以上のデータセットを提供するプロバイダーと、データセットを要求するコンシューマーの2つの当事者が関与します。 [18]

転送プロセスプロトコル

転送プロセス(TP)には、使用ポリシーに基づいて1つ以上のデータセットを提供するプロバイダーと、データセットを要求するコンシューマーの2つの当事者が関与します。 [29]

参考

共有

Business model paper by IDSA

メモ

はじめに

本報告書は、International Data Spaces Association(IDSA)が2024年11月に発表したポジションペーパーの分析に基づいています。

IDSA releases new position paper Data Spaces Business Models

以下、生成AIによりポイントを書き出しました。内容の正誤についてはご注意くださいませ。

背景と目的

  • データスペースの採用が進む中、ビジネスモデルの確立が課題
  • 技術的な概念から実践的なビジネスモデルへの移行が必要
  • 関係者間の共通理解と効果的なコミュニケーションの基盤構築

主要な発見

  1. データスペースのビジネスモデルには単一の解決策は存在しない
  2. 成功には3つの重要な特性が必要:
    • 協調型(Collaborative)
    • マルチサイド型(Multi-sided)
    • 進化型(Evolving)
  3. 初期段階での公的資金の重要性と、自己持続可能なモデルへの移行の必要性

セクション1:データスペースのビジネスモデルの特徴

1.1 ビジネスモデルの基本概念

  • 定義: 「組織が価値を創造し、提供し、捕捉する方法の説明」(Alex Osterwalder)
  • 重要な構成要素:
    1. WHAT:市場に提供する価値提案
    2. WHO:価値提案を市場に提供する主体
    3. HOW:価値提案の市場への提供方法
    4. TO WHOM:価値提案の対象顧客
    5. BALANCE:コストと収益のバランス

1.2 データスペース固有の特性

  • データの非競合性
    • 同時に複数の利用が可能
    • 使用しても価値が減少しない
  • 分散型システムの特徴
    • データ主権の確保
    • 相互運用性の実現
    • 信頼性の担保

セクション2:実例分析

2.1 モビリティデータスペース(MDS)

背景

  • ドイツ政府主導の取り組み
  • 2019年に開始
  • 約200の参加組織を獲得

ビジネスモデルの特徴

  • 価値提案
    • 安全な分散型データマーケットプレイス
    • 欧州基準に準拠したデータ共有プラットフォーム
  • 収益モデル
    • 初期:公的資金と株主出資
    • 将来:データ取引からの収益
  • 具体的なユースケース
    1
    2
    3
    4
    5
    6
    1. Pay as you drive保険
    - 保険会社とOEMの協力
    - 実際の車両使用データに基づく保険料設定
    2. 都市モビリティ最適化
    - リアルタイム交通データの活用
    - デジタルツインへの統合

2.2 Catena-X

背景

  • 自動車産業のサプライチェーン最適化
  • 主要自動車メーカーの参加(BMW、VW、Ford、Renaultなど)

ビジネスモデルの特徴

  • 運営構造
    • Cofinity-X(運営会社)の設立
    • 参加企業のオンボーディング管理
  • ユースケース一覧
    1
    2
    3
    4
    5
    1. 製品カーボンフットプリント追跡
    2. バッテリー・製品パスポート
    3. サプライチェーンレジリエンス
    4. 部品トレーサビリティ
    5. マスターデータ管理

2.3 Smart Connected Supplier Network(SCSN)

背景

  • 2015年開始の応用研究プロジェクト
  • オランダの製造業サプライチェーン向け
  • 400以上の製造企業と11のITサービスプロバイダーが参加

ビジネスモデルの特徴

  • 価値提案
    • 管理負担の軽減
    • 顧客要求への迅速な対応
    • データ処理の自動化
  • 収益モデル
    • 接続エンドユーザーごとの固定料金
    • ITサービスプロバイダー経由の収益

セクション3:成功要因の分析

3.1 共通する成功要因

  1. 段階的アプローチ

    • 初期:公的資金活用
    • 成長期:参加者拡大
    • 成熟期:自己持続的モデル
  2. エコシステムの構築

    1
    2
    3
    4
    - データ提供者
    - データ消費者
    - サービスプロバイダー
    - 技術プロバイダー

  3. 価値創造の明確化

    • 経済的価値
    • 社会的価値
    • 規制遵守価値

結論

  1. データスペースの成功には、技術的側面とビジネスモデルの両立が不可欠
  2. 初期段階での公的支援と、長期的な自己持続性のバランスが重要
  3. 参加者それぞれの価値創造を明確にし、適切なインセンティブ設計が必要

今後の展望

  • より多様なユースケースの開発
  • 産業間連携の促進
  • グローバルな標準化の進展

「データスペースのビジネスモデルは単一ではなく、状況に応じて進化する必要がある」 ― IDSA Position Paper, 2024

参考

共有

MVD_of_EDC

メモ

MVDを用いた動作確認用のメモ

Eclipse Dataspace Componentsのセットアップ

まず、Eclipse Dataspace Components(EDC)をセットアップします。EDCは、データスペースにおけるデータ連携を実現するためのフレームワークです。以下の手順でEDCをセットアップします。

準備

必要環境 に必要となる環境が書いてある。

  • Docker
  • KinD (other cluster engines may work as well - not tested!)
  • Terraform
  • JDK 17+
  • Git
  • a POSIX compliant shell
  • Postman (to comfortably execute REST requests)
  • openssl, optional, but required to regenerate keys
  • newman (to run Postman collections from the command line)
  • not needed, but recommended: Kubernetes monitoring tools like K9s

Docker

Docker Desktop

kind

kindのインストール方法

JDK

JDK17をインストール

Postman

Postmanのダウンロード

openssl

1
sudo apt install openssl

newman

npmのインストールから必要

1
2
3
sudo apt install nodejs npm n
sudo n stable
sudo npm install -g newman

ソースコードのクローン

今回はMVDを用います。

1
2
git clone https://github.com/eclipse-edc/MinimumViableDataspace.git
cd MinimumViableDataspace

公式サイトの環境構成図

環境構成

ビルドと起動

k8s上で各種サービスを起動する。

ビルド

1
2
./gradlew build
./gradlew -Ppersistence=true dockerize

kind (k8s) 環境起動

1
2
3
4
# Create the cluster
kind create cluster -n mvd --config deployment/kind.config.yaml
# Load docker images into KinD
kind load docker-image controlplane:latest dataplane:latest identity-hub:latest catalog-server:latest sts:latest -n mvd

1
2
3
4
# 確認
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b7f31408c498 kindest/node:v1.27.3 "/usr/local/bin/entr…" 2 minutes ago Up 2 minutes 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp, 127.0.0.1:32825->6443/tcp mvd-control-plane

ingress NGINXコントローラをk8s上にデプロイ

1
2
3
4
5
6
7
8
# Deploy an NGINX ingress
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml

# Wait for the ingress controller to become available
kubectl wait --namespace ingress-nginx \
--for=condition=ready pod \
--selector=app.kubernetes.io/component=controller \
--timeout=90s

今回のMVD環境をデプロイ

1
2
3
4
5

# Deploy the dataspace, type 'yes' when prompted
cd deployment
terraform init
terraform apply

確認

1
kubectl get pods --namespace mvd

結果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
NAME                                                   READY   STATUS    RESTARTS   AGE
consumer-controlplane-68c4696c57-2qmh2 1/1 Running 0 55s
consumer-dataplane-75d79bfd56-t5mt2 1/1 Running 0 39s
consumer-identityhub-545fdb579c-df6bq 1/1 Running 0 55s
consumer-postgres-687484545b-sp8xr 1/1 Running 0 96s
consumer-sts-778bb7bb44-kfqxp 1/1 Running 0 70s
consumer-vault-0 1/1 Running 0 95s
dataspace-issuer-server-7ff68bd8b4-qh7c7 1/1 Running 0 96s
provider-catalog-server-58b67bdb89-nxtvq 1/1 Running 0 54s
provider-identityhub-bb68bfcf4-bv42v 1/1 Running 0 55s
provider-manufacturing-controlplane-86bdd7c967-b6r7h 1/1 Running 0 54s
provider-manufacturing-dataplane-7d66445bf8-dp24n 1/1 Running 0 39s
provider-postgres-7fd78d95b8-x2kzd 1/1 Running 0 96s
provider-qna-controlplane-dc894c5ff-qxcd7 1/1 Running 0 55s
provider-qna-dataplane-8574c764fd-t2x8z 1/1 Running 0 39s
provider-sts-64cd87f4f6-n5cvq 1/1 Running 0 70s
provider-vault-0 1/1 Running 0 95s

構成

  • Consumer
    • コントロールプレーン
    • データプレーン
    • アイデンティティ・ハブ
    • PostgreSQLサーバ
    • Vault
  • Provider
    • カタログ
    • QNAのコントロールプレーンとデータプレーン
    • Manufacturingのコントロールプレーンとデータプレーン
    • アイデンティティ・ハブ
    • PostgreSQLサーバ
    • Vault

データの流し込み

プロジェクト直下の、seed-k8s.shを使うとデータを流したりできる。

1
bash seed-k8s.sh

以下、内容を確認。

seed-k8s.shでは、Postman(Newman)を使って、コネクタに各種データを登録するなどしている。 例えば以下の通り。

seed-k8s.sh:23

1
2
3
4
5
6
7
8
echo "Seed data to 'provider-qna' and 'provider-manufacturing'"
for url in 'http://127.0.0.1/provider-manufacturing/cp' 'http://127.0.0.1/provider-qna/cp'
do
newman run \
--folder "Seed" \
--env-var "HOST=$url" \
./deployment/postman/MVD.postman_collection.json
done

上記の newman コマンドの引数に与えられているファイル deployment/postman/MVD.postman_collection.json にPostman(Newman)用のコンフィグファイルがある。 newmanコマンドのfolderオプションにある通り、今回は「Seed」フォルダ以下の内容を実行する。 なお、この登録処理は以下の両方のホストに実施される。 * provider-qna * provider-manufacturing

行われる処理は以下。

  • アセット登録
    • asset-1
    • asset-2
  • ポリシー登録
    • require-membership
    • require-dataprocessor
    • require-sensitive
  • コントラクト登録
    • member-and-dataprocessor-def
      • アクセスポリシーは require-membership
      • コントラクトポリシーは require-dataprocessor
      • 対象アセットは asset-1
    • sensitive-only-def
      • アクセスポリシーは require-membership
      • コントラクトポリシーは require-sensitive
      • 対象アセットは asset-2

続いて、スクリプトにある通り、deployment/postman/MVD.postman_collection.jsonSeed Catalog Server フォルダ以下が実行される。 対象ホストは、 provider-catalog-server

seed-k8s.sh:35

1
2
3
4
5
6
7
echo "Create linked assets on the Catalog Server"
newman run \
--folder "Seed Catalog Server" \
--env-var "HOST=http://127.0.0.1/provider-catalog-server/cp" \
--env-var "PROVIDER_QNA_DSP_URL=http://provider-qna-controlplane:8082" \
--env-var "PROVIDER_MF_DSP_URL=http://provider-manufacturing-controlplane:8082" \
./deployment/postman/MVD.postman_collection.json
  • カタログアセット登録
    • linked-asset-provider-qna
    • linked-asset-provider-manufacturing
  • アセット登録
    • normal-asset-1
  • ポリシー登録
    • require-membership
  • コントラクト定義登録
    • membership-required-def

スクリプトを実行すると以下のような結果が得られる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Seed data to 'provider-qna' and 'provider-manufacturing'          
newman

MVD+IATP

❏ Seed
↳ Create Asset 1
POST http://127.0.0.1/provider-manufacturing/cp/api/management/v3/assets [200 OK, 331B, 66ms]
✓ Status is OK or conflict

↳ Create Asset 2
POST http://127.0.0.1/provider-manufacturing/cp/api/management/v3/assets [200 OK, 331B, 23ms]
✓ Status is OK or conflict

↳ Create Membership Policy
POST http://127.0.0.1/provider-manufacturing/cp/api/management/v3/policydefinitions [200 OK, 342B, 71ms]
✓ Status is OK or conflict

(snip)

┌─────────────────────────┬───────────────────┬──────────────────┐
│ │ executed │ failed │
├─────────────────────────┼───────────────────┼──────────────────┤
│ iterations │ 1 │ 0 │
├─────────────────────────┼───────────────────┼──────────────────┤
│ requests │ 7 │ 0 │
├─────────────────────────┼───────────────────┼──────────────────┤
│ test-scripts │ 14 │ 0 │
├─────────────────────────┼───────────────────┼──────────────────┤
│ prerequest-scripts │ 21 │ 0 │
├─────────────────────────┼───────────────────┼──────────────────┤
│ assertions │ 7 │ 0 │
├─────────────────────────┴───────────────────┴──────────────────┤
│ total run duration: 466ms │
├────────────────────────────────────────────────────────────────┤
│ total data received: 1.45kB (approx) │
├────────────────────────────────────────────────────────────────┤
│ average response time: 34ms [min: 19ms, max: 71ms, s.d.: 21ms] │
└────────────────────────────────────────────────────────────────┘

コンシューマとプロバイダの登録

続いて、コンシューマ登録。

seed-k8s.sh:49

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
echo "Create consumer participant"
CONSUMER_CONTROLPLANE_SERVICE_URL="http://consumer-controlplane:8082"
CONSUMER_IDENTITYHUB_URL="http://consumer-identityhub:7082"
DATA_CONSUMER=$(jq -n --arg url "$CONSUMER_CONTROLPLANE_SERVICE_URL" --arg ihurl "$CONSUMER_IDENTITYHUB_URL" '{
"roles":[],
"serviceEndpoints":[
{
"type": "CredentialService",
"serviceEndpoint": "\($ihurl)/api/presentation/v1/participants/ZGlkOndlYjpjb25zdW1lci1pZGVudGl0eWh1YiUzQTcwODM6Y29uc3VtZXI=",
"id": "consumer-credentialservice-1"
},
{
"type": "ProtocolEndpoint",
"serviceEndpoint": "\($url)/api/dsp",
"id": "consumer-dsp"
}
],
"active": true,
"participantId": "did:web:consumer-identityhub%3A7083:consumer",
"did": "did:web:consumer-identityhub%3A7083:consumer",
"key":{
"keyId": "did:web:consumer-identityhub%3A7083:consumer#key-1",
"privateKeyAlias": "did:web:consumer-identityhub%3A7083:consumer#key-1",
"keyGeneratorParams":{
"algorithm": "EC"
}
}
}')

curl --location "http://127.0.0.1/consumer/cs/api/identity/v1alpha/participants/" \
--header 'Content-Type: application/json' \
--header "x-api-key: $API_KEY" \
--data "$DATA_CONSUMER"

プロバイダ登録

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
echo "Create provider participant"

PROVIDER_CONTROLPLANE_SERVICE_URL="http://provider-catalog-server-controlplane:8082"
PROVIDER_IDENTITYHUB_URL="http://provider-identityhub:7082"

DATA_PROVIDER=$(jq -n --arg url "$PROVIDER_CONTROLPLANE_SERVICE_URL" --arg ihurl "$PROVIDER_IDENTITYHUB_URL" '{
"roles":[],
"serviceEndpoints":[
{
"type": "CredentialService",
"serviceEndpoint": "\($ihurl)/api/presentation/v1/participants/ZGlkOndlYjpwcm92aWRlci1pZGVudGl0eWh1YiUzQTcwODM6cHJvdmlkZXI=",
"id": "provider-credentialservice-1"
},
{
"type": "ProtocolEndpoint",
"serviceEndpoint": "\($url)/api/dsp",
"id": "provider-dsp"
}
],
"active": true,
"participantId": "did:web:provider-identityhub%3A7083:provider",
"did": "did:web:provider-identityhub%3A7083:provider",
"key":{
"keyId": "did:web:provider-identityhub%3A7083:provider#key-1",
"privateKeyAlias": "did:web:provider-identityhub%3A7083:provider#key-1",
"keyGeneratorParams":{
"algorithm": "EC"
}
}
}')

curl --location "http://127.0.0.1/provider/cs/api/identity/v1alpha/participants/" \
--header 'Content-Type: application/json' \
--header "x-api-key: $API_KEY" \
--data "$DATA_PROVIDER"

上記の通り、DIDを登録している。

成功すると以下のようなメッセージが見られる。

1
2
3
4
5
6
7
8
9
(snip)

Create consumer participant
{"apiKey":"ZGlkOndlYjpjb25zdW1lci1pZGVudGl0eWh1YiUzQTcwODM6Y29uc3VtZXI=.TfDP+lAgK+GUb7f+dNSvU9WKpZPNV1zZ/YJ8gNoNlrI3zZkcrg8b7dWAYMYD+k3qOoVdp+ZAE3C5fHWgmsQtww==","clientId":"did:web:consumer-identityhub%3A708
3:consumer","clientSecret":"IMyZ5gqsnXihiftx"}

Create provider participant
{"apiKey":"ZGlkOndlYjpwcm92aWRlci1pZGVudGl0eWh1YiUzQTcwODM6cHJvdmlkZXI=.h7Ta9GRuNSOrD7I/BS681E7OsvsX/YEpDrz/Mj+1nJxG3xAFtwTGCFP0qF/QxWdZ/aJvi7ywehpAu/Cg/Yi37w==","clientId":"did:web:provider-identityhub%3A708
3:provider","clientSecret":"P751oqFsm4npVkai"}

動作確認

READMEの「7. Executing REST requests using Postman」を参考に実施。

変数にはローカル環境用とKubernetes環境用があるので、今回はkubernetes環境用を使用。

deployment/postman/MVD K8S.postman_environment.json

Postmanでインポートすると以下のように環境が見える。

環境

続いて、collectionをインポートする。

deployment/postman/MVD K8S.postman_collection.json

コレクションをインポートすると、以下のように各種リクエストをPostmanから送れるようになる。

環境

カタログ

以下のように、環境をMVD K8Sに変更(右上)し、Get Cached Catalogsを実行すると、カタログ情報が見え、先程登録したasset-1asset-2の情報が見えるはず。

asset-1の場合は以下のような感じ。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
{
"@id": "fcc9cefa-c31e-4155-ad59-35294044cc6d",
"@type": "dcat:Catalog",
"dcat:dataset": [
{
"@id": "asset-1",
"@type": "dcat:Dataset",
"odrl:hasPolicy": {
"@id": "bWVtYmVyLWFuZC1kYXRhcHJvY2Vzc29yLWRlZg==:YXNzZXQtMQ==:OThhZGM3OGQtZDM1Ny00MDQwLTlhYmUtYzI3YTIzYTMyNmUz",
"@type": "odrl:Offer",
"odrl:permission": [],
"odrl:prohibition": [],
"odrl:obligation": {
"odrl:action": {
"@id": "odrl:use"
},
"odrl:constraint": {
"odrl:leftOperand": {
"@id": "DataAccess.level"
},
"odrl:operator": {
"@id": "odrl:eq"
},
"odrl:rightOperand": "processing"
}
}
},
"dcat:distribution": [
{
"@type": "dcat:Distribution",
"dct:format": {
"@id": "HttpData-PULL"
},
"dcat:accessService": {
"@id": "6cccbc00-9fa5-4f99-a2d9-524d2881f72f",
"@type": "dcat:DataService"
}
},
{
"@type": "dcat:Distribution",
"dct:format": {
"@id": "HttpData-PUSH"
},
"dcat:accessService": {
"@id": "6cccbc00-9fa5-4f99-a2d9-524d2881f72f",
"@type": "dcat:DataService"
}
}
],
"description": "This asset requires Membership to view and negotiate.",
"id": "asset-1"
},

README通り、今回はプロバイダのQ&A departmentに着目する。 エンドポイント情報が、上記のアセット情報の直後にあるのを見つける。

1
2
3
4
5
6
7
"dcat:service": {
"@id": "6cccbc00-9fa5-4f99-a2d9-524d2881f72f",
"@type": "dcat:DataService",
"dcat:endpointDescription": "dspace:connector",
"dcat:endpointUrl": "http://provider-qna-controlplane:8082/api/dsp",
"dcat:endpointURL": "http://provider-qna-controlplane:8082/api/dsp"
},

それとアセットのIDを確認しておく、odrl:hasPolicy.@idにある。 今回の例だと、

asset-1

1
2
3
4
"@id": "asset-1",
"@type": "dcat:Dataset",
"odrl:hasPolicy": {
"@id": "bWVtYmVyLWFuZC1kYXRhcHJvY2Vzc29yLWRlZg==:YXNzZXQtMQ==:OThhZGM3OGQtZDM1Ny00MDQwLTlhYmUtYzI3YTIzYTMyNmUz",

asset-2 は一旦省略

コントラクトネゴシエーション

先の手順で確認した、asset-1のIDを変数に設定する。(実際には、各自の値を使用すること)

環境

これは、リクエストボディの以下に用いられる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"@context": [
"https://w3id.org/edc/connector/management/v0.0.1"
],
"@type": "ContractRequest",
"counterPartyAddress": "{{PROVIDER_DSP_URL}}/api/dsp",
"counterPartyId": "{{PROVIDER_ID}}",
"protocol": "dataspace-protocol-http",
"policy": {
"@type": "Offer",
"@id": "{{POLICY_ID_ASSET_1}}",
"assigner": "{{PROVIDER_ID}}",
"permission": [],
"prohibition": [],
"obligation": {
"action": "use",
"constraint": {
"leftOperand": "DataAccess.level",
"operator": "eq",
"rightOperand": "processing"
}
},
"target": "asset-1"
},
"callbackAddresses": []
}

続いて、initiate negotiationを実行する。

環境

レスポンスが見られるはず。 ただし、この状態ではまだコントラクトネゴシエーションを開始しただけ。状況確認が必要。

状況の確認

続いて、Get Contract Negotiationsを実行する。

環境

ステータスがFINALIZEDになっていることがわかる。 今回は、1回実行しただけなので、以下のとおりだが、複数回実行するとそのぶんだけ並ぶ。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[
{
"@type": "ContractNegotiation",
"@id": "378b9249-b869-426f-b224-7879d358d2a1",
"type": "CONSUMER",
"protocol": "dataspace-protocol-http",
"state": "FINALIZED",
"counterPartyId": "did:web:provider-identityhub%3A7083:provider",
"counterPartyAddress": "http://provider-qna-controlplane:8082/api/dsp",
"callbackAddresses": [],
"createdAt": 1737304019738,
"contractAgreementId": "59b8c0b1-a9b1-4b83-b85d-715758941595",
"@context": {
"@vocab": "https://w3id.org/edc/v0.0.1/ns/",
"edc": "https://w3id.org/edc/v0.0.1/ns/",
"odrl": "http://www.w3.org/ns/odrl/2/"
}
}
]

上記からcontractAgreementId がわかる。59b8c0b1-a9b1-4b83-b85d-715758941595である。 このあとのデータ転送において利用する。

トランスファーの初期化

Initiate Transferを実行するが、その際のリクエストボディは以下の通り。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"@context": [
"https://w3id.org/edc/connector/management/v0.0.1"
],
"assetId": "asset-1",
"counterPartyAddress": "{{PROVIDER_DSP_URL}}/api/dsp",
"connectorId": "{{PROVIDER_ID}}",
"contractId": "{{CONTRACT_AGREEMENT_ID}}",
"dataDestination": {
"type": "HttpProxy"
},
"protocol": "dataspace-protocol-http",
"transferType": "HttpData-PULL"
}

上記の、CONTRACT_AGREEMENT_IDに渡す値を先ほど控えた値に書き換えて、Initiate Transferを実行する。 先程控えておいた値にする。各自の値を使用すること。

環境

戻りは以下のような内容。

1
2
3
4
5
6
7
8
9
10
{
"@type": "IdResponse",
"@id": "0278e81e-5dea-4ab7-876f-60cd8a202e09",
"createdAt": 1737304134741,
"@context": {
"@vocab": "https://w3id.org/edc/v0.0.1/ns/",
"edc": "https://w3id.org/edc/v0.0.1/ns/",
"odrl": "http://www.w3.org/ns/odrl/2/"
}
}

これでトランスファーの処理が開始された。ただし、実際のデータ転送が始まったわけではない。 今回は、HttpData-PULLプロトコルを用いることになっており、そのためには実際のデータを取得するためのエンドポイントとアクセスキーを取得しないとならない。

EDR(EndpointDataReference)取得

Get cached EDRsを実行する。特に変数設定などは不要。

環境
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[
{
"@id": "0278e81e-5dea-4ab7-876f-60cd8a202e09",
"@type": "EndpointDataReferenceEntry",
"providerId": "did:web:provider-identityhub%3A7083:provider",
"assetId": "asset-1",
"agreementId": "59b8c0b1-a9b1-4b83-b85d-715758941595",
"transferProcessId": "0278e81e-5dea-4ab7-876f-60cd8a202e09",
"createdAt": 1737304136114,
"contractNegotiationId": "378b9249-b869-426f-b224-7879d358d2a1",
"@context": {
"@vocab": "https://w3id.org/edc/v0.0.1/ns/",
"edc": "https://w3id.org/edc/v0.0.1/ns/",
"odrl": "http://www.w3.org/ns/odrl/2/"
}
}
]

今回は有効なEDRが1個なので上記の通り。 EDRのIDがわかる。0278e81e-5dea-4ab7-876f-60cd8a202e09

アクセストークンの取得

先程確認したEDRのIDを用いて、EDRを取得する。

環境
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"@type": "DataAddress",
"type": "https://w3id.org/idsa/v4.1/HTTP",
"endpoint": "http://provider-qna-dataplane:11002/api/public",
"authType": "bearer",
"endpointType": "https://w3id.org/idsa/v4.1/HTTP",
"authorization": "eyJraWQiOiJkaWQ6d2ViOnByb3ZpZGVyLWlkZW50aXR5aHViJTNBNzA4Mzpwcm92aWRlciNrZXktMSIsImFsZyI6IkVTMjU2In0.eyJpc3MiOiJkaWQ6d2ViOnByb3ZpZGVyLWlkZW50aXR5aHViJTNBNzA4Mzpwcm92aWRlciIsImF1ZCI6ImRpZDp3ZWI6Y29uc3VtZXItaWRlbnRpdHlodWIlM0E3MDgzOmNvbnN1bWVyIiwic3ViIjoiZGlkOndlYjpwcm92aWRlci1pZGVudGl0eWh1YiUzQTcwODM6cHJvdmlkZXIiLCJpYXQiOjE3MzczMDQxMzU3OTQsImp0aSI6IjI3Y2Y0ODFlLTQ5YWYtNDI0YS1iNjc5LTk2YzQ4MmY4ODAyNyJ9.whzgJzBy9ydxontusqowd_wG33KjSfzjxqXxb600csDUdrGHFl45CEQtbRdOfSvffjQTaincEKZpAovS9b2gqg",
"@context": {
"@vocab": "https://w3id.org/edc/v0.0.1/ns/",
"edc": "https://w3id.org/edc/v0.0.1/ns/",
"odrl": "http://www.w3.org/ns/odrl/2/"
}
}

上記の通り、エンドポイント情報とアクセストークが得られた。

1
2
3
4
5
(snip)
"endpoint": "http://provider-qna-dataplane:11002/api/public",
(snip)
"authorization": "eyJraWQiOiJkaWQ6d2ViOnByb3ZpZGVyLWlkZW50aXR5aHViJTNBNzA4Mzpwcm92aWRlciNrZXktMSIsImFsZyI6IkVTMjU2In0.eyJpc3MiOiJkaWQ6d2ViOnByb3ZpZGVyLWlkZW50aXR5aHViJTNBNzA4Mzpwcm92aWRlciIsImF1ZCI6ImRpZDp3ZWI6Y29uc3VtZXItaWRlbnRpdHlodWIlM0E3MDgzOmNvbnN1bWVyIiwic3ViIjoiZGlkOndlYjpwcm92aWRlci1pZGVudGl0eWh1YiUzQTcwODM6cHJvdmlkZXIiLCJpYXQiOjE3MzczMDQxMzU3OTQsImp0aSI6IjI3Y2Y0ODFlLTQ5YWYtNDI0YS1iNjc5LTk2YzQ4MmY4ODAyNyJ9.whzgJzBy9ydxontusqowd_wG33KjSfzjxqXxb600csDUdrGHFl45CEQtbRdOfSvffjQTaincEKZpAovS9b2gqg",
(snip)

データの取得

先程確認したトークンを用いてデータを取得する。

環境
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
},
{
"userId": 1,
"id": 2,
"title": "quis ut nam facilis et officia qui",
"completed": false
},
{
"userId": 1,
"id": 3,
"title": "fugiat veniam minus",
"completed": false
},
{
(snip)

(wip)

参考

共有

Data Governance part of Global Digital Compact

メモ

Global Digital Compact (United Nation) に掲載されている国連のGlobal Digital Compact資料のうち、データガバナンスに関する箇所を軽く解説。

参考

共有

Trouble using Docker Desktop and docker

メモ

Ubuunu22で、Docker Desktopとディストリ向けパッケージのDockerを混在させたときにトラブったので備忘録。 ひとまず、Contextの指定を忘れないように。(自分向けメモ)

1
2
3
4
# コンテキストの確認
docker context ls
# コンテキスト切り替え
docker context use <任意のコンテキスト>

なお、DOCKER_HOSTの環境変数にも注意。 うっかり、なにかの名残で指定していることがあるので。

参考

共有

Trouble about Qemu when I use minikube

メモ

Problems detected in kubelet #17638にも記載されているが、Ubuntu22上でminikubeを実行していた際、minikube serviceを利用するときに以下のエラーが生じた。

1
MK_UNIMPLEMENTED が原因で終了します: minikube サービスは現在、QEMU 上のビルトインネットワークでは実装されていません
1
Exiting due to MK_UNIMPLEMENTED: minikube service is not currently implemented with the builtin network on QEMU

ひとまず、Dockerをドライバとして使用することにした。

1
2
3
4
# 既存のクラスタを破棄
minikube delete
# dockerを利用するよう指定して起動
minikube start --driver=docker

参考

共有

memo of Ouranos Ecosystem IDI and data-transaction-system

メモ

ouranos-ecosystem-idi / data-transaction-systemOuranos Ecosystem の オープンソースソフトウェアが公開されている。

このOSSの主旨は、READMEにある通り、

本リポジトリ(Minimum Ouranos data platform)は企業・業界・国境を跨いだデータ連携・利活用を目指すイニシアティブの 「ウラノス・エコシステム(Ouranos Ecosystem)」におけるデータ流通システムの最小実装を体験するため、 実装の一部をオープンソースとして公開する。

である。

基本的にはREADMEに書かれている通りなので、特別に補足することはないが、なんとなくメモしておく。

README 実行環境 に実行環境の情報が記載されている。記載の通り、基本的にはGo言語を用いて実装されている。

なお、以降以下のディレクトリ内に、各種レポジトリをクローンして用いる。

1
2
3
mkdir -p ~/Sources/ouranos-ecosystem-idi
cd ~/Sources/ouranos-ecosystem-idi
export OE_WORKDIR=~/Sources/ouranos-ecosystem-idi

なお、暫定的にワーキングディレクトリを示す便宜的な環境変数を作成しておいた。

認証データの構成

ユーザ認証システム にレポジトリがあるのでクローンして、認証システムを立ち上げる。

1
2
3
git clone git@github.com:ouranos-ecosystem-idi/user-authentication-system.git
cd ${OE_WORKDIR}/user-authentication-system/
docker compose up -d

docker-compose.ymlの通り、実態はDBMS(postgres)と簡易認証用のfirebaseである。 firebaseではエミュレータを用いる。

エミュレータコンフィグ配下の通り。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"emulators": {
"auth": {
"host": "0.0.0.0",
"port": 9099
},
"ui": {
"enabled": true,
"host": "0.0.0.0",
"port": 4000
},
"singleProjectMode": true
}
}

データベースのスキーマ定義。

1
2
3
cd ${OE_WORKDIR}/user-authentication-system/
export POSTGRESQL_URL='postgres://dhuser:passw0rd@localhost:5432/dhlocal?sslmode=disable'
migrate -path setup/migrations -database ${POSTGRESQL_URL} up

ダミーデータをDBに入力。

1
2
docker cp setup postgres:/setup
docker exec -it postgres bash /setup/setup_seeds.sh

シェルスクリプトは、setup/seeders に含まれているSQLを実行するというもの。

1
2
3
4
5
setup/seeders
setup/seeders/000001_seed_api_keys.sql
setup/seeders/000002_seed_operators.sql
setup/seeders/000003_seed_apikey_operators.sql
setup/seeders/000004_cidrs.sql

上記の通り、APIキー、オペレータのIDなどを入力している。2件ずつ。

確認。

1
docker exec -i postgres bash -c "PGPASSWORD=passw0rd psql -h 127.0.0.1 -p 5432 -U dhuser dhlocal -c 'select * from operators'"
1
2
3
4
5
             operator_id              | operator_name | deleted_at |     created_at      | created_user_id |     updated_at      | updated_user_id | operator_address | open_operator_id |  global_operator_id  
--------------------------------------+---------------+------------+---------------------+-----------------+---------------------+-----------------+------------------+------------------+----------------------
b39e6248-c888-56ca-d9d0-89de1b1adc8e | A社 | | 2024-03-26 12:00:00 | seed | 2024-03-26 12:00:00 | seed | 東京都渋谷区xx | 1234567890123 | 1234ABCD5678EFGH0123
15572d1c-ec13-0d78-7f92-dd4278871373 | B社 | | 2024-03-26 12:00:00 | seed | 2024-03-26 12:00:00 | seed | 東京都渋谷区xx | 1234567890124 | 1234ABCD5678EFGH0124
(2 rows)

エミュレータに事業者情報を追加。

1
make idp-add-local
1
2
3
go run cmd/add_local_user/main.go
Successfully created user with email: oem_a@example.com and set custom claims. Password: oemA&user_01
Successfully created user with email: supplier_b@example.com and set custom claims. Password: supplierB&user_01

Makefileの中身は以下の通り。

Makefile:78

1
2
idp-add-local:
go run cmd/add_local_user/main.go

このプログラムは、シード用CSVから事業者情報を登録する。

cmd/add_local_user/main.go:46

1
2
3
4
5
6
7
8
9
10
11
12
func addOperatorFromCSV(ctx context.Context, app *firebase.App, csvPath string) {

(snip)

// create user by each record
for _, record := range records {
email := record[0]
password := record[1]
operatorID := record[2]
operator := Operator{operatorID, email, password}
addOperator(ctx, authClient, operator)
}

cmd/add_local_user/main.go:76

1
2
3
4
5
func addOperator(ctx context.Context, authClient *auth.Client, operator Operator) {

(snip)

err = authClient.SetCustomUserClaims(ctx, userRecord.UID, customClaims)

上記の通り、firebaseのauth.Clientを用いて登録している。

シードは、 cmd/add_local_user/data/seed.csv である。

1
2
oem_a@example.com,oemA&user_01,b39e6248-c888-56ca-d9d0-89de1b1adc8e
supplier_b@example.com,supplierB&user_01,15572d1c-ec13-0d78-7f92-dd4278871373

OperatorのIDは、先程DBとに登録したものと同じ。

データ流通システムの起動

ビルド

1
2
3
cd ${OE_WORKDIR}/data-transaction-system
go build main.go
docker build -t data-spaces-backend .

起動

1
docker run -v $(pwd)/config/:/app/config/ -td -i --network docker.internal --env-file config/local.env -p 8080:8080 --name data-spaces-backend data-spaces-backend

(このあたりはcomposeになっていない…と)

ユーザ認証システムの起動

ビルド

1
2
3
cd ${OE_WORKDIR}/user-authentication-system/
go build main.go
docker build -t authenticator-backend .

起動

1
docker run -v $(pwd)/config/:/app/config/ -td -i --network docker.internal --env-file config/local.env -p 8081:8081 --name authenticator-backend authenticator-backend

これで一通り起動したはず。

1
docker ps
1
2
3
4
5
CONTAINER ID   IMAGE                                  COMMAND                   CREATED          STATUS                  PORTS                                                                                  NAMES
aa12c592f114 authenticator-backend "/app/server" 33 seconds ago Up 33 seconds 0.0.0.0:8081->8081/tcp, :::8081->8081/tcp authenticator-backend
46c1d0f82cf7 data-spaces-backend "/app/server" 3 minutes ago Up 3 minutes 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp data-spaces-backend
7ccba96acee9 postgres:14 "docker-entrypoint.s…" 2 hours ago Up 2 hours 0.0.0.0:5432->5432/tcp, :::5432->5432/tcp postgres
9e5bc7cf81ed user-authentication-system-firebase "docker-entrypoint.s…" 2 hours ago Up 2 hours 0.0.0.0:4000->4000/tcp, :::4000->4000/tcp, 0.0.0.0:9099->9099/tcp, :::9099->9099/tcp user-authentication-system-firebase-1

A社の事業者認証

事業者認証の通り、

A社がユーザ認証システムで事業者認証をした後に、自身の事業者情報を取得する例を示します。

認証情報取得

前提:

  • PF認定を受けた事業者に発行されるAPIキーを指定しAPIを利用する
  • 事業者はAcountIdとPasswordを事前に払い出されている

認証情報を取得

1
2
3
4
curl -s --location --request POST 'http://localhost:8081/auth/login' --header 'Content-Type: application/json' --header 'apiKey: Sample-APIKey1' --data-raw '{
"operatorAccountId": "oem_a@example.com",
"accountPassword": "oemA&user_01"
}' | jq
1
2
3
4
{
"accessToken": "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJvcGVyYXRvcl9pZCI6ImIzOWU2MjQ4LWM4ODgtNTZjYS1kOWQwLTg5ZGUxYjFhZGM4ZSIsImVtYWlsIjoib2VtX2FAZXhhbXBsZS5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImF1dGhfdGltZSI6MTczMDAzMzQ1OSwidXNlcl9pZCI6IjhmMDVmZWY1LWE3ZGEtNGVjMS1iY2FjLTMyNTIwMzM0ODVkNyIsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsib2VtX2FAZXhhbXBsZS5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9LCJpYXQiOjE3MzAwMzM0NTksImV4cCI6MTczMDAzNzA1OSwiYXVkIjoibG9jYWwiLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vbG9jYWwiLCJzdWIiOiI4ZjA1ZmVmNS1hN2RhLTRlYzEtYmNhYy0zMjUyMDMzNDg1ZDcifQ.",
"refreshToken": "eyJfQXV0aEVtdWxhdG9yUmVmcmVzaFRva2VuIjoiRE8gTk9UIE1PRElGWSIsImxvY2FsSWQiOiI4ZjA1ZmVmNS1hN2RhLTRlYzEtYmNhYy0zMjUyMDMzNDg1ZDciLCJwcm92aWRlciI6InBhc3N3b3JkIiwiZXh0cmFDbGFpbXMiOnt9LCJwcm9qZWN0SWQiOiJsb2NhbCJ9"
}

事業者情報取得

先程取得したアクセストークンを使い、事業者情報を取得してみる。 先の例と同様、APIキーも用いる。

1
2
3
4
curl -s --location --request GET 'http://localhost:8081/api/v1/authInfo?dataTarget=operator' \
--header 'apiKey: Sample-APIKey1' \
--header 'Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJvcGVyYXRvcl9pZCI6ImIzOWU2MjQ4LWM4ODgtNTZjYS1kOWQwLTg5ZGUxYjFhZGM4ZSIsImVtYWlsIjoib2VtX2FAZXhhbXBsZS5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImF1dGhfdGltZSI6MTczMDAzMzQ1OSwidXNlcl9pZCI6IjhmMDVmZWY1LWE3ZGEtNGVjMS1iY2FjLTMyNTIwMzM0ODVkNyIsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsib2VtX2FAZXhhbXBsZS5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9LCJpYXQiOjE3MzAwMzM0NTksImV4cCI6MTczMDAzNzA1OSwiYXVkIjoibG9jYWwiLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vbG9jYWwiLCJzdWIiOiI4ZjA1ZmVmNS1hN2RhLTRlYzEtYmNhYy0zMjUyMDMzNDg1ZDcifQ.' \
| jq
1
2
3
4
5
6
7
8
9
{
"operatorId": "b39e6248-c888-56ca-d9d0-89de1b1adc8e",
"operatorName": "A社",
"operatorAddress": "東京都渋谷区xx",
"openOperatorId": "1234567890123",
"operatorAttribute": {
"globalOperatorId": "1234ABCD5678EFGH0123"
}
}

A社の部品登録およびB社への回答依頼

部品登録 にある通り。 事業所登録、親部品情報の作成、部品構成情報の登録、CFP結果提出の依頼(A社=下流・OEMからB社=上流・サプライヤへの依頼)の順番で進める。

事業所登録

この時点では、事「業」所が登録されていないので登録する。 operatorId は先程取得した事業者情報の値を用いる。openPlantIdplantAddressplantNaeは任意の値。 plantIdはNullで渡す。

1
2
3
4
5
6
7
8
9
10
11
12
13
curl -s --location --request PUT 'http://localhost:8081/api/v1/authInfo?dataTarget=plant' \
--header 'apiKey: Sample-APIKey1' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJvcGVyYXRvcl9pZCI6ImIzOWU2MjQ4LWM4ODgtNTZjYS1kOWQwLTg5ZGUxYjFhZGM4ZSIsImVtYWlsIjoib2VtX2FAZXhhbXBsZS5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImF1dGhfdGltZSI6MTczMDAzMzQ1OSwidXNlcl9pZCI6IjhmMDVmZWY1LWE3ZGEtNGVjMS1iY2FjLTMyNTIwMzM0ODVkNyIsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsib2VtX2FAZXhhbXBsZS5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9LCJpYXQiOjE3MzAwMzM0NTksImV4cCI6MTczMDAzNzA1OSwiYXVkIjoibG9jYWwiLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vbG9jYWwiLCJzdWIiOiI4ZjA1ZmVmNS1hN2RhLTRlYzEtYmNhYy0zMjUyMDMzNDg1ZDcifQ.' \
--data '{
"openPlantId": "1234567890123012345",
"operatorId": "b39e6248-c888-56ca-d9d0-89de1b1adc8e",
"plantAddress": "xx県xx市xxxx町1-1-1234",
"plantId": null,
"plantName": "A工場",
"plantAttribute": {}
}' \
| jq
1
2
3
4
5
6
7
8
9
10
{
"plantId": "4c110e31-46a3-48d9-89d3-afdefcfcb7dc",
"operatorId": "b39e6248-c888-56ca-d9d0-89de1b1adc8e",
"plantName": "A工場",
"plantAddress": "xx県xx市xxxx町1-1-1234",
"openPlantId": "1234567890123012345",
"plantAttribute": {
"globalPlantId": null
}
}

結果中の、plantIdが埋まっていることがわかる。

また、APIが/v1/authInfoであることも注意。

親部品情報の作成

事業所を登録できたので、その情報を使って親部品情報を登録する。 plantIdは先程の戻り値に含まれていたものを利用。 traceIdはNullで渡す。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
curl -s --location --request PUT 'http://localhost:8080/api/v1/datatransport?dataTarget=parts' \
--header 'apiKey: Sample-APIKey1' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJvcGVyYXRvcl9pZCI6ImIzOWU2MjQ4LWM4ODgtNTZjYS1kOWQwLTg5ZGUxYjFhZGM4ZSIsImVtYWlsIjoib2VtX2FAZXhhbXBsZS5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImF1dGhfdGltZSI6MTczMDAzMzQ1OSwidXNlcl9pZCI6IjhmMDVmZWY1LWE3ZGEtNGVjMS1iY2FjLTMyNTIwMzM0ODVkNyIsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsib2VtX2FAZXhhbXBsZS5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9LCJpYXQiOjE3MzAwMzM0NTksImV4cCI6MTczMDAzNzA1OSwiYXVkIjoibG9jYWwiLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vbG9jYWwiLCJzdWIiOiI4ZjA1ZmVmNS1hN2RhLTRlYzEtYmNhYy0zMjUyMDMzNDg1ZDcifQ.' \
--data '{
"amountRequired": null,
"amountRequiredUnit": "kilogram",
"operatorId": "b39e6248-c888-56ca-d9d0-89de1b1adc8e",
"partsName": "部品A",
"plantId": "4c110e31-46a3-48d9-89d3-afdefcfcb7dc",
"supportPartsName": "modelA",
"terminatedFlag": false,
"traceId": null
}' \
| jq
1
2
3
4
5
6
7
8
9
10
{
"traceId": "675b19a3-380e-45d6-921f-38aad289e0d5",
"operatorId": "b39e6248-c888-56ca-d9d0-89de1b1adc8e",
"plantId": "4c110e31-46a3-48d9-89d3-afdefcfcb7dc",
"partsName": "部品A",
"supportPartsName": "modelA",
"terminatedFlag": false,
"amountRequired": null,
"amountRequiredUnit": "kilogram"
}

traceIdが含まれた値が返ってきた。

部品構成情報の登録

親部品の登録が終わり、親部品のtraceIdも払い出されたので、その情報を使って、子部品を登録する。 この時点で子部品のtraceIdはNullだが、戻り値に含まれていることがわかる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
curl -s --location --request PUT 'http://localhost:8080/api/v1/datatransport?dataTarget=partsStructure' \
--header 'apiKey: Sample-APIKey1' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJvcGVyYXRvcl9pZCI6ImIzOWU2MjQ4LWM4ODgtNTZjYS1kOWQwLTg5ZGUxYjFhZGM4ZSIsImVtYWlsIjoib2VtX2FAZXhhbXBsZS5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImF1dGhfdGltZSI6MTczMDAzMzQ1OSwidXNlcl9pZCI6IjhmMDVmZWY1LWE3ZGEtNGVjMS1iY2FjLTMyNTIwMzM0ODVkNyIsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsib2VtX2FAZXhhbXBsZS5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9LCJpYXQiOjE3MzAwMzM0NTksImV4cCI6MTczMDAzNzA1OSwiYXVkIjoibG9jYWwiLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vbG9jYWwiLCJzdWIiOiI4ZjA1ZmVmNS1hN2RhLTRlYzEtYmNhYy0zMjUyMDMzNDg1ZDcifQ.' \
--data '{
"parentPartsModel": {
"amountRequired": null,
"amountRequiredUnit": "kilogram",
"operatorId": "b39e6248-c888-56ca-d9d0-89de1b1adc8e",
"partsName": "部品A",
"plantId": "4c110e31-46a3-48d9-89d3-afdefcfcb7dc",
"supportPartsName": "modelA",
"terminatedFlag": false,
"traceId": "8fc6aa29-5f4f-476e-85e3-2d1b54715891"
},
"childrenPartsModel": [
{
"amountRequired": 5,
"amountRequiredUnit": "kilogram",
"operatorId": "b39e6248-c888-56ca-d9d0-89de1b1adc8e",
"partsName": "部品A1",
"plantId": "4c110e31-46a3-48d9-89d3-afdefcfcb7dc",
"supportPartsName": "modelA-1",
"terminatedFlag": false,
"traceId": null
}
]
}' \
| jq
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"parentPartsModel": {
"traceId": "8fc6aa29-5f4f-476e-85e3-2d1b54715891",
"operatorId": "b39e6248-c888-56ca-d9d0-89de1b1adc8e",
"plantId": "4c110e31-46a3-48d9-89d3-afdefcfcb7dc",
"partsName": "部品A",
"supportPartsName": "modelA",
"terminatedFlag": false,
"amountRequired": null,
"amountRequiredUnit": "kilogram"
},
"childrenPartsModel": [
{
"traceId": "c6e15178-90ed-4605-ad69-2abebae7d18a",
"operatorId": "b39e6248-c888-56ca-d9d0-89de1b1adc8e",
"plantId": "4c110e31-46a3-48d9-89d3-afdefcfcb7dc",
"partsName": "部品A1",
"supportPartsName": "modelA-1",
"terminatedFlag": false,
"amountRequired": 5,
"amountRequiredUnit": "kilogram"
}
]
}

CFP結果提出の依頼(AからBに)

/v1/authInfo APIを用いて、まずはB社の事業者識別子(内部)を取得する。 openOperatorId (事業者識別子(公開))は事前に知っているものとする。

1
2
3
4
curl -s --location --request GET 'http://localhost:8081/api/v1/authInfo?dataTarget=operator&openOperatorId=1234567890124' \
--header 'apiKey: Sample-APIKey1' \
--header 'Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJvcGVyYXRvcl9pZCI6ImIzOWU2MjQ4LWM4ODgtNTZjYS1kOWQwLTg5ZGUxYjFhZGM4ZSIsImVtYWlsIjoib2VtX2FAZXhhbXBsZS5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImF1dGhfdGltZSI6MTczMDAzMzQ1OSwidXNlcl9pZCI6IjhmMDVmZWY1LWE3ZGEtNGVjMS1iY2FjLTMyNTIwMzM0ODVkNyIsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsib2VtX2FAZXhhbXBsZS5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9LCJpYXQiOjE3MzAwMzM0NTksImV4cCI6MTczMDAzNzA1OSwiYXVkIjoibG9jYWwiLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vbG9jYWwiLCJzdWIiOiI4ZjA1ZmVmNS1hN2RhLTRlYzEtYmNhYy0zMjUyMDMzNDg1ZDcifQ.' \
| jq
1
2
3
4
5
6
7
8
9
{
"operatorId": "15572d1c-ec13-0d78-7f92-dd4278871373",
"operatorName": "B社",
"operatorAddress": "東京都渋谷区xx",
"openOperatorId": "1234567890124",
"operatorAttribute": {
"globalOperatorId": "1234ABCD5678EFGH0124"
}
}

続いて、A社からB社に対しての取引関係を作成し、紐付け回答依頼を行う。 downstreamTraceIdには先に取得しておいた、下流(つまり、A社 = OEM側)のもつtraceIdを指定する。先ほど作った親子関係のうち、子部品(つまり、上流由来の部品)のIDを指定する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
curl -s --location --request PUT 'http://localhost:8080/api/v1/datatransport?dataTarget=tradeRequest' \
--header 'apiKey: Sample-APIKey1' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJvcGVyYXRvcl9pZCI6ImIzOWU2MjQ4LWM4ODgtNTZjYS1kOWQwLTg5ZGUxYjFhZGM4ZSIsImVtYWlsIjoib2VtX2FAZXhhbXBsZS5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImF1dGhfdGltZSI6MTczMDAzMzQ1OSwidXNlcl9pZCI6IjhmMDVmZWY1LWE3ZGEtNGVjMS1iY2FjLTMyNTIwMzM0ODVkNyIsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsib2VtX2FAZXhhbXBsZS5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9LCJpYXQiOjE3MzAwMzM0NTksImV4cCI6MTczMDAzNzA1OSwiYXVkIjoibG9jYWwiLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vbG9jYWwiLCJzdWIiOiI4ZjA1ZmVmNS1hN2RhLTRlYzEtYmNhYy0zMjUyMDMzNDg1ZDcifQ.' \
--data '{
"statusModel": {
"message": "来月中にご回答をお願いします。",
"replyMessage": null,
"requestStatus": {},
"requestType": "CFP",
"statusId": null,
"tradeId": null
},
"tradeModel": {
"downstreamOperatorId": "b39e6248-c888-56ca-d9d0-89de1b1adc8e",
"downstreamTraceId": "c6e15178-90ed-4605-ad69-2abebae7d18a",
"tradeId": null,
"upstreamOperatorId": "15572d1c-ec13-0d78-7f92-dd4278871373",
"upstreamTraceId": null
}
}' \
| jq
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"tradeModel": {
"tradeId": "aeb60293-2477-4f58-94ac-8b24cb32103f",
"downstreamOperatorId": "b39e6248-c888-56ca-d9d0-89de1b1adc8e",
"upstreamOperatorId": "15572d1c-ec13-0d78-7f92-dd4278871373",
"downstreamTraceId": "c6e15178-90ed-4605-ad69-2abebae7d18a",
"upstreamTraceId": null
},
"statusModel": {
"statusId": "a6e4a7f2-bb5c-4414-8026-75a46ea4436d",
"tradeId": "aeb60293-2477-4f58-94ac-8b24cb32103f",
"requestStatus": {
"cfpResponseStatus": "NOT_COMPLETED",
"tradeTreeStatus": "UNTERMINATED"
},
"message": "来月中にご回答をお願いします。",
"replyMessage": null,
"requestType": "CFP"
}
}

リクエスト時はNullだった、statusIdtradeIdが返ってきていることがわかる。(traceIdtradeIdが紛らわしいので注意)

B社の部品登録紐付け

部品登録紐付けの通り。 おおむね、B社の事業者認証(アクセストークン取得)、B社の事業所情報の登録、B社親部品登録、紐付け以来の確認、部品登録紐付けの登録、の流れ。

事業社認証

A社のときと同じく、B社においても認証情報を取得する。

1
2
3
4
5
6
7
8
curl -s --location --request POST 'http://localhost:8081/auth/login' \
--header 'Content-Type: application/json' \
--header 'apiKey: Sample-APIKey1' \
--data-raw '{
"operatorAccountId": "supplier_b@example.com",
"accountPassword": "supplierB&user_01"
}' \
| jq
1
2
3
4
{
"accessToken": "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJvcGVyYXRvcl9pZCI6IjE1NTcyZDFjLWVjMTMtMGQ3OC03ZjkyLWRkNDI3ODg3MTM3MyIsImVtYWlsIjoic3VwcGxpZXJfYkBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiYXV0aF90aW1lIjoxNzMwMDM1ODMwLCJ1c2VyX2lkIjoiYzdhZThmM2UtY2Q4Yy00ZDk1LThhNjQtNmY0Y2IyZWRkMWE3IiwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJlbWFpbCI6WyJzdXBwbGllcl9iQGV4YW1wbGUuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifSwiaWF0IjoxNzMwMDM1ODMwLCJleHAiOjE3MzAwMzk0MzAsImF1ZCI6ImxvY2FsIiwiaXNzIjoiaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGUuY29tL2xvY2FsIiwic3ViIjoiYzdhZThmM2UtY2Q4Yy00ZDk1LThhNjQtNmY0Y2IyZWRkMWE3In0.",
"refreshToken": "eyJfQXV0aEVtdWxhdG9yUmVmcmVzaFRva2VuIjoiRE8gTk9UIE1PRElGWSIsImxvY2FsSWQiOiJjN2FlOGYzZS1jZDhjLTRkOTUtOGE2NC02ZjRjYjJlZGQxYTciLCJwcm92aWRlciI6InBhc3N3b3JkIiwiZXh0cmFDbGFpbXMiOnt9LCJwcm9qZWN0SWQiOiJsb2NhbCJ9"
}

事業所の登録

A社のときと同じく、B社においても事業「所」情報を登録する。

1
2
3
4
5
6
7
8
9
10
11
12
13
curl -s --location --request PUT 'http://localhost:8081/api/v1/authInfo?dataTarget=plant' \
--header 'apiKey: Sample-APIKey1' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJvcGVyYXRvcl9pZCI6IjE1NTcyZDFjLWVjMTMtMGQ3OC03ZjkyLWRkNDI3ODg3MTM3MyIsImVtYWlsIjoic3VwcGxpZXJfYkBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiYXV0aF90aW1lIjoxNzMwMDM1ODMwLCJ1c2VyX2lkIjoiYzdhZThmM2UtY2Q4Yy00ZDk1LThhNjQtNmY0Y2IyZWRkMWE3IiwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJlbWFpbCI6WyJzdXBwbGllcl9iQGV4YW1wbGUuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifSwiaWF0IjoxNzMwMDM1ODMwLCJleHAiOjE3MzAwMzk0MzAsImF1ZCI6ImxvY2FsIiwiaXNzIjoiaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGUuY29tL2xvY2FsIiwic3ViIjoiYzdhZThmM2UtY2Q4Yy00ZDk1LThhNjQtNmY0Y2IyZWRkMWE3In0.' \
--data '{
"openPlantId": "1234567890124012345",
"operatorId": "15572d1c-ec13-0d78-7f92-dd4278871373",
"plantAddress": "xx県xx市xxxx町2-1-1234",
"plantId": null,
"plantName": "B工場",
"plantAttribute": {}
}' \
| jq
1
2
3
4
5
6
7
8
9
10
{
"plantId": "2dbe5ede-0140-4a47-8bf7-3ab3947866c7",
"operatorId": "15572d1c-ec13-0d78-7f92-dd4278871373",
"plantName": "B工場",
"plantAddress": "xx県xx市xxxx町2-1-1234",
"openPlantId": "1234567890124012345",
"plantAttribute": {
"globalPlantId": null
}
}

リクエストのときはNullだった、plantIdが返っていることがわかる。

親部品情報の作成

A社のときと同じく、親部品情報を作成する。 認証トークンとplantIdは先程取得したものを用いること。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
curl -s --location --request PUT 'http://localhost:8080/api/v1/datatransport?dataTarget=parts' \
--header 'apiKey: Sample-APIKey1' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJvcGVyYXRvcl9pZCI6IjE1NTcyZDFjLWVjMTMtMGQ3OC03ZjkyLWRkNDI3ODg3MTM3MyIsImVtYWlsIjoic3VwcGxpZXJfYkBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiYXV0aF90aW1lIjoxNzMwMDM1ODMwLCJ1c2VyX2lkIjoiYzdhZThmM2UtY2Q4Yy00ZDk1LThhNjQtNmY0Y2IyZWRkMWE3IiwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJlbWFpbCI6WyJzdXBwbGllcl9iQGV4YW1wbGUuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifSwiaWF0IjoxNzMwMDM1ODMwLCJleHAiOjE3MzAwMzk0MzAsImF1ZCI6ImxvY2FsIiwiaXNzIjoiaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGUuY29tL2xvY2FsIiwic3ViIjoiYzdhZThmM2UtY2Q4Yy00ZDk1LThhNjQtNmY0Y2IyZWRkMWE3In0.' \
--data '{
"amountRequired": null,
"amountRequiredUnit": "kilogram",
"operatorId": "15572d1c-ec13-0d78-7f92-dd4278871373",
"partsName": "部品B",
"plantId": "2dbe5ede-0140-4a47-8bf7-3ab3947866c7",
"supportPartsName": "modelB",
"terminatedFlag": true,
"traceId": null
}' \
| jq
1
2
3
4
5
6
7
8
9
10
{
"traceId": "28410c0f-1796-4d1b-b30b-db4aa4b8d28f",
"operatorId": "15572d1c-ec13-0d78-7f92-dd4278871373",
"plantId": "2dbe5ede-0140-4a47-8bf7-3ab3947866c7",
"partsName": "部品B",
"supportPartsName": "modelB",
"terminatedFlag": true,
"amountRequired": null,
"amountRequiredUnit": "kilogram"
}

リクエスト時はNullだったtraceIdが返っていることがわかる。

紐付け依頼確認

今回は、予めA社(OEM、下流)から回答依頼を送っているのだが、改めてB社(サプライヤ、上流)側で依頼を確認する。

1
2
3
4
curl -s --location --request GET 'http://localhost:8080/api/v1/datatransport?dataTarget=tradeResponse' \
--header 'apiKey: Sample-APIKey1' \
--header 'Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJvcGVyYXRvcl9pZCI6IjE1NTcyZDFjLWVjMTMtMGQ3OC03ZjkyLWRkNDI3ODg3MTM3MyIsImVtYWlsIjoic3VwcGxpZXJfYkBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiYXV0aF90aW1lIjoxNzMwMDM1ODMwLCJ1c2VyX2lkIjoiYzdhZThmM2UtY2Q4Yy00ZDk1LThhNjQtNmY0Y2IyZWRkMWE3IiwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJlbWFpbCI6WyJzdXBwbGllcl9iQGV4YW1wbGUuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifSwiaWF0IjoxNzMwMDM1ODMwLCJleHAiOjE3MzAwMzk0MzAsImF1ZCI6ImxvY2FsIiwiaXNzIjoiaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGUuY29tL2xvY2FsIiwic3ViIjoiYzdhZThmM2UtY2Q4Yy00ZDk1LThhNjQtNmY0Y2IyZWRkMWE3In0.' \
| jq
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
[
{
"statusModel": {
"statusId": "a6e4a7f2-bb5c-4414-8026-75a46ea4436d",
"tradeId": "aeb60293-2477-4f58-94ac-8b24cb32103f",
"requestStatus": {
"cfpResponseStatus": "NOT_COMPLETED",
"tradeTreeStatus": "UNTERMINATED"
},
"message": "来月中にご回答をお願いします。",
"replyMessage": null,
"requestType": "CFP"
},
"tradeModel": {
"tradeId": "aeb60293-2477-4f58-94ac-8b24cb32103f",
"downstreamOperatorId": "b39e6248-c888-56ca-d9d0-89de1b1adc8e",
"upstreamOperatorId": "15572d1c-ec13-0d78-7f92-dd4278871373",
"downstreamTraceId": "c6e15178-90ed-4605-ad69-2abebae7d18a",
"upstreamTraceId": null
},
"partsModel": {
"traceId": "c6e15178-90ed-4605-ad69-2abebae7d18a",
"operatorId": "b39e6248-c888-56ca-d9d0-89de1b1adc8e",
"plantId": "4c110e31-46a3-48d9-89d3-afdefcfcb7dc",
"partsName": "部品A",
"supportPartsName": "modelA",
"terminatedFlag": false,
"amountRequired": null,
"amountRequiredUnit": "kilogram"
}
}
]

statusIdtradeIdが先程A社側で見たものと同じであることがわかる。

部品登録紐付けの登録

依頼に回答する。APIのパラメータtradeIdtraceIdには、先程取得した依頼回答にあった値を用いる。つまり、traddeIdは依頼回答一覧似合ったものを用いる。traceIdには、先程B社側で作成した親部品情報を用いる。(A社側の値ではないことに注意)

1
2
3
4
curl -s --location --request PUT 'http://localhost:8080/api/v1/datatransport?dataTarget=tradeResponse&tradeId=aeb60293-2477-4f58-94ac-8b24cb32103f&traceId=28410c0f-1796-4d1b-b30b-db4aa4b8d28f' \
--header 'apiKey: Sample-APIKey1' \
--header 'Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJvcGVyYXRvcl9pZCI6IjE1NTcyZDFjLWVjMTMtMGQ3OC03ZjkyLWRkNDI3ODg3MTM3MyIsImVtYWlsIjoic3VwcGxpZXJfYkBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiYXV0aF90aW1lIjoxNzMwMDM1ODMwLCJ1c2VyX2lkIjoiYzdhZThmM2UtY2Q4Yy00ZDk1LThhNjQtNmY0Y2IyZWRkMWE3IiwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJlbWFpbCI6WyJzdXBwbGllcl9iQGV4YW1wbGUuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifSwiaWF0IjoxNzMwMDM1ODMwLCJleHAiOjE3MzAwMzk0MzAsImF1ZCI6ImxvY2FsIiwiaXNzIjoiaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGUuY29tL2xvY2FsIiwic3ViIjoiYzdhZThmM2UtY2Q4Yy00ZDk1LThhNjQtNmY0Y2IyZWRkMWE3In0.' \
| jq
1
2
3
4
5
6
7
{
"tradeId": "aeb60293-2477-4f58-94ac-8b24cb32103f",
"downstreamOperatorId": "b39e6248-c888-56ca-d9d0-89de1b1adc8e",
"upstreamOperatorId": "15572d1c-ec13-0d78-7f92-dd4278871373",
"downstreamTraceId": "c6e15178-90ed-4605-ad69-2abebae7d18a",
"upstreamTraceId": "28410c0f-1796-4d1b-b30b-db4aa4b8d28f"
}

以上で、A社とB社の間で異なる部品情報を持ったまま、それらをつなぐ関係情報が作成された。

B社のCFP情報の伝達

CFP情報伝達の通り。B社が登録する。 手順書に記載があるが、ここで登録するCFP値は、アプリケーション側で自社の利用する算出されたものを登録する、という前提になっている。

traceIdには、先程作成したB社側の親部品情報の値を用いる。

なお、本来の実装では、この作業を実施後に、A社に対してCFP情報、DQR情報を開示する手順があるのだが、本記事で紹介している手順では自動で許可されるようになっている。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
curl -s --location --request PUT 'http://localhost:8080/api/v1/datatransport?dataTarget=cfp' \
--header 'apiKey: Sample-APIKey1' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJvcGVyYXRvcl9pZCI6IjE1NTcyZDFjLWVjMTMtMGQ3OC03ZjkyLWRkNDI3ODg3MTM3MyIsImVtYWlsIjoic3VwcGxpZXJfYkBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiYXV0aF90aW1lIjoxNzMwMDM1ODMwLCJ1c2VyX2lkIjoiYzdhZThmM2UtY2Q4Yy00ZDk1LThhNjQtNmY0Y2IyZWRkMWE3IiwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJlbWFpbCI6WyJzdXBwbGllcl9iQGV4YW1wbGUuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifSwiaWF0IjoxNzMwMDM1ODMwLCJleHAiOjE3MzAwMzk0MzAsImF1ZCI6ImxvY2FsIiwiaXNzIjoiaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGUuY29tL2xvY2FsIiwic3ViIjoiYzdhZThmM2UtY2Q4Yy00ZDk1LThhNjQtNmY0Y2IyZWRkMWE3In0.' \
--data '[
{
"cfpId": null,
"traceId": "28410c0f-1796-4d1b-b30b-db4aa4b8d28f",
"ghgEmission": 1.5,
"ghgDeclaredUnit": "kgCO2e/kilogram",
"cfpType": "preProduction",
"dqrType": "preProcessing",
"dqrValue": {
"TeR": 1,
"GeR": 2,
"TiR": 3
}
},
{
"cfpId": null,
"traceId": "28410c0f-1796-4d1b-b30b-db4aa4b8d28f",
"ghgEmission": 10.0,
"ghgDeclaredUnit": "kgCO2e/kilogram",
"cfpType": "mainProduction",
"dqrType": "mainProcessing",
"dqrValue": {
"TeR": 2,
"GeR": 3,
"TiR": 4
}
},
{
"cfpId": null,
"traceId": "28410c0f-1796-4d1b-b30b-db4aa4b8d28f",
"ghgEmission": 0,
"ghgDeclaredUnit": "kgCO2e/kilogram",
"cfpType": "preComponent",
"dqrType": "preProcessing",
"dqrValue": {
"TeR": 1,
"GeR": 2,
"TiR": 3
}
},
{
"cfpId": null,
"traceId": "28410c0f-1796-4d1b-b30b-db4aa4b8d28f",
"ghgEmission": 0,
"ghgDeclaredUnit": "kgCO2e/kilogram",
"cfpType": "mainComponent",
"dqrType": "mainProcessing",
"dqrValue": {
"TeR": 2,
"GeR": 3,
"TiR": 4
}
}
]' \
| jq
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
[
{
"cfpId": "2b289894-846f-4563-916f-0c238aa5216f",
"traceId": "28410c0f-1796-4d1b-b30b-db4aa4b8d28f",
"ghgEmission": 1.5,
"ghgDeclaredUnit": "kgCO2e/kilogram",
"cfpType": "preProduction",
"dqrType": "preProcessing",
"dqrValue": {
"TeR": 1,
"GeR": 2,
"TiR": 3
}
},
{
"cfpId": "2b289894-846f-4563-916f-0c238aa5216f",
"traceId": "28410c0f-1796-4d1b-b30b-db4aa4b8d28f",
"ghgEmission": 10,
"ghgDeclaredUnit": "kgCO2e/kilogram",
"cfpType": "mainProduction",
"dqrType": "mainProcessing",
"dqrValue": {
"TeR": 2,
"GeR": 3,
"TiR": 4
}
},
{
"cfpId": "2b289894-846f-4563-916f-0c238aa5216f",
"traceId": "28410c0f-1796-4d1b-b30b-db4aa4b8d28f",
"ghgEmission": 0,
"ghgDeclaredUnit": "kgCO2e/kilogram",
"cfpType": "preComponent",
"dqrType": "preProcessing",
"dqrValue": {
"TeR": 1,
"GeR": 2,
"TiR": 3
}
},
{
"cfpId": "2b289894-846f-4563-916f-0c238aa5216f",
"traceId": "28410c0f-1796-4d1b-b30b-db4aa4b8d28f",
"ghgEmission": 0,
"ghgDeclaredUnit": "kgCO2e/kilogram",
"cfpType": "mainComponent",
"dqrType": "mainProcessing",
"dqrValue": {
"TeR": 2,
"GeR": 3,
"TiR": 4
}
}
]

A社における回答取得

回答取得 の通り。 A社側で、B社の回答を取得する。

回答依頼情報の取得

タイムアウトしてトークンが無効になっているかもしれないので、認証情報を取得

1
2
3
4
curl -s --location --request POST 'http://localhost:8081/auth/login' --header 'Content-Type: application/json' --header 'apiKey: Sample-APIKey1' --data-raw '{
"operatorAccountId": "oem_a@example.com",
"accountPassword": "oemA&user_01"
}' | jq
1
2
3
4
{
"accessToken": "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJvcGVyYXRvcl9pZCI6ImIzOWU2MjQ4LWM4ODgtNTZjYS1kOWQwLTg5ZGUxYjFhZGM4ZSIsImVtYWlsIjoib2VtX2FAZXhhbXBsZS5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImF1dGhfdGltZSI6MTczMDAzODQ1NSwidXNlcl9pZCI6IjhmMDVmZWY1LWE3ZGEtNGVjMS1iY2FjLTMyNTIwMzM0ODVkNyIsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsib2VtX2FAZXhhbXBsZS5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9LCJpYXQiOjE3MzAwMzg0NTUsImV4cCI6MTczMDA0MjA1NSwiYXVkIjoibG9jYWwiLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vbG9jYWwiLCJzdWIiOiI4ZjA1ZmVmNS1hN2RhLTRlYzEtYmNhYy0zMjUyMDMzNDg1ZDcifQ.",
"refreshToken": "eyJfQXV0aEVtdWxhdG9yUmVmcmVzaFRva2VuIjoiRE8gTk9UIE1PRElGWSIsImxvY2FsSWQiOiI4ZjA1ZmVmNS1hN2RhLTRlYzEtYmNhYy0zMjUyMDMzNDg1ZDciLCJwcm92aWRlciI6InBhc3N3b3JkIiwiZXh0cmFDbGFpbXMiOnt9LCJwcm9qZWN0SWQiOiJsb2NhbCJ9"
}

取得したアクセストークンを使いつつ、回答情報を取得。

1
2
3
4
curl -s --location --request GET 'http://localhost:8080/api/v1/datatransport?dataTarget=tradeRequest' \
--header 'apiKey: Sample-APIKey1' \
--header 'Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJvcGVyYXRvcl9pZCI6ImIzOWU2MjQ4LWM4ODgtNTZjYS1kOWQwLTg5ZGUxYjFhZGM4ZSIsImVtYWlsIjoib2VtX2FAZXhhbXBsZS5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImF1dGhfdGltZSI6MTczMDAzODQ1NSwidXNlcl9pZCI6IjhmMDVmZWY1LWE3ZGEtNGVjMS1iY2FjLTMyNTIwMzM0ODVkNyIsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsib2VtX2FAZXhhbXBsZS5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9LCJpYXQiOjE3MzAwMzg0NTUsImV4cCI6MTczMDA0MjA1NSwiYXVkIjoibG9jYWwiLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vbG9jYWwiLCJzdWIiOiI4ZjA1ZmVmNS1hN2RhLTRlYzEtYmNhYy0zMjUyMDMzNDg1ZDcifQ.' \
| jq
1
2
3
4
5
6
7
8
9
[
{
"tradeId": "aeb60293-2477-4f58-94ac-8b24cb32103f",
"downstreamOperatorId": "b39e6248-c888-56ca-d9d0-89de1b1adc8e",
"upstreamOperatorId": "15572d1c-ec13-0d78-7f92-dd4278871373",
"downstreamTraceId": "c6e15178-90ed-4605-ad69-2abebae7d18a",
"upstreamTraceId": "28410c0f-1796-4d1b-b30b-db4aa4b8d28f"
}
]

依頼情報のステータス確認

  • statusTarget=REQUEST ... A社が依頼元のステータスのみ取得
  • traceId ... A社がB社に依頼している自社のtraceId(子部品のID)を指定する。
1
2
3
4
curl -s --location --request GET 'http://localhost:8080/api/v1/datatransport?dataTarget=status&statusTarget=REQUEST&traceId=c6e15178-90ed-4605-ad69-2abebae7d18a' \
--header 'apiKey: Sample-APIKey1' \
--header 'Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJvcGVyYXRvcl9pZCI6ImIzOWU2MjQ4LWM4ODgtNTZjYS1kOWQwLTg5ZGUxYjFhZGM4ZSIsImVtYWlsIjoib2VtX2FAZXhhbXBsZS5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImF1dGhfdGltZSI6MTczMDAzODQ1NSwidXNlcl9pZCI6IjhmMDVmZWY1LWE3ZGEtNGVjMS1iY2FjLTMyNTIwMzM0ODVkNyIsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsib2VtX2FAZXhhbXBsZS5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9LCJpYXQiOjE3MzAwMzg0NTUsImV4cCI6MTczMDA0MjA1NSwiYXVkIjoibG9jYWwiLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vbG9jYWwiLCJzdWIiOiI4ZjA1ZmVmNS1hN2RhLTRlYzEtYmNhYy0zMjUyMDMzNDg1ZDcifQ.' \
| jq
1
2
3
4
5
6
7
8
9
10
11
12
13
[
{
"statusId": "a6e4a7f2-bb5c-4414-8026-75a46ea4436d",
"tradeId": "aeb60293-2477-4f58-94ac-8b24cb32103f",
"requestStatus": {
"cfpResponseStatus": "COMPLETED",
"tradeTreeStatus": "TERMINATED"
},
"message": "来月中にご回答をお願いします。",
"replyMessage": null,
"requestType": "CFP"
}
]

A社において完成品のCFP値を算出する

完成品CFP値の通り。 本来は、アプリケーションにてCFPを計算する。 traceIdには、A社側の子部品のtraceIdを指定する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
curl -s --location --request PUT 'http://localhost:8080/api/v1/datatransport?dataTarget=cfp' \
--header 'apiKey: Sample-APIKey1' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJvcGVyYXRvcl9pZCI6ImIzOWU2MjQ4LWM4ODgtNTZjYS1kOWQwLTg5ZGUxYjFhZGM4ZSIsImVtYWlsIjoib2VtX2FAZXhhbXBsZS5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImF1dGhfdGltZSI6MTczMDAzODQ1NSwidXNlcl9pZCI6IjhmMDVmZWY1LWE3ZGEtNGVjMS1iY2FjLTMyNTIwMzM0ODVkNyIsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsib2VtX2FAZXhhbXBsZS5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9LCJpYXQiOjE3MzAwMzg0NTUsImV4cCI6MTczMDA0MjA1NSwiYXVkIjoibG9jYWwiLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vbG9jYWwiLCJzdWIiOiI4ZjA1ZmVmNS1hN2RhLTRlYzEtYmNhYy0zMjUyMDMzNDg1ZDcifQ.' \
--data '[
{
"cfpId": null,
"traceId": "c6e15178-90ed-4605-ad69-2abebae7d18a",
"ghgEmission": 3.0,
"ghgDeclaredUnit": "kgCO2e/kilogram",
"cfpType": "preProduction",
"dqrType": "preProcessing",
"dqrValue": {
"TeR": 1,
"GeR": 2,
"TiR": 3
}
},
{
"cfpId": null,
"traceId": "c6e15178-90ed-4605-ad69-2abebae7d18a",
"ghgEmission": 20.0,
"ghgDeclaredUnit": "kgCO2e/kilogram",
"cfpType": "mainProduction",
"dqrType": "mainProcessing",
"dqrValue": {
"TeR": 2,
"GeR": 3,
"TiR": 4
}
},
{
"cfpId": null,
"traceId": "c6e15178-90ed-4605-ad69-2abebae7d18a",
"ghgEmission": 0,
"ghgDeclaredUnit": "kgCO2e/kilogram",
"cfpType": "preComponent",
"dqrType": "preProcessing",
"dqrValue": {
"TeR": 1,
"GeR": 2,
"TiR": 3
}
},
{
"cfpId": null,
"traceId": "c6e15178-90ed-4605-ad69-2abebae7d18a",
"ghgEmission": 0,
"ghgDeclaredUnit": "kgCO2e/kilogram",
"cfpType": "mainComponent",
"dqrType": "mainProcessing",
"dqrValue": {
"TeR": 2,
"GeR": 3,
"TiR": 4
}
}
]' \
| jq
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
[
{
"cfpId": "b626d92e-e6e3-4769-98d1-1ff8b6714230",
"traceId": "c6e15178-90ed-4605-ad69-2abebae7d18a",
"ghgEmission": 3,
"ghgDeclaredUnit": "kgCO2e/kilogram",
"cfpType": "preProduction",
"dqrType": "preProcessing",
"dqrValue": {
"TeR": 1,
"GeR": 2,
"TiR": 3
}
},
{
"cfpId": "b626d92e-e6e3-4769-98d1-1ff8b6714230",
"traceId": "c6e15178-90ed-4605-ad69-2abebae7d18a",
"ghgEmission": 20,
"ghgDeclaredUnit": "kgCO2e/kilogram",
"cfpType": "mainProduction",
"dqrType": "mainProcessing",
"dqrValue": {
"TeR": 2,
"GeR": 3,
"TiR": 4
}
},
{
"cfpId": "b626d92e-e6e3-4769-98d1-1ff8b6714230",
"traceId": "c6e15178-90ed-4605-ad69-2abebae7d18a",
"ghgEmission": 0,
"ghgDeclaredUnit": "kgCO2e/kilogram",
"cfpType": "preComponent",
"dqrType": "preProcessing",
"dqrValue": {
"TeR": 1,
"GeR": 2,
"TiR": 3
}
},
{
"cfpId": "b626d92e-e6e3-4769-98d1-1ff8b6714230",
"traceId": "c6e15178-90ed-4605-ad69-2abebae7d18a",
"ghgEmission": 0,
"ghgDeclaredUnit": "kgCO2e/kilogram",
"cfpType": "mainComponent",
"dqrType": "mainProcessing",
"dqrValue": {
"TeR": 2,
"GeR": 3,
"TiR": 4
}
}
]

(wip)

データ流通システムの様子

データ流通システムの main.go を見ると概要がわかる。

このアプリケーションは、Echoを使って作られている。

Routerの様子

APIは単純で、/api/v1/datatransportのGETとPUTのみ。

presentation/http/echo/router/router.go:39

1
2
e.GET("/api/v1/datatransport", func(c echo.Context) error { return h.GetOuranos(c) })
e.PUT("/api/v1/datatransport", func(c echo.Context) error { return h.PutOuranos(c) })

部品情報登録のハンドラー

hhandler.AppHandlerなのだが、以下の通り。

1
2
3
4
5
6
// appHandler
// Summary: This is structure which defines appHandler.
type appHandler struct {
handler.AuthHandler
handler.OuranosHandler
}

AuthHandlerは名前の通り、認証関連。APIキーやトークンの処理。

presentation/http/echo/handler/auth_handler.go:10

1
2
3
4
AuthHandler interface {
VerifyAPIKey(c echo.Context) error
VerifyToken(c echo.Context) (*string, error)
}

OuranosHandlerがデータのやり取りのためのハンドラーであり、GET/PUTを扱う。 ハンドラーの定義は以下の通り。

presentation/http/echo/handler/ouranos_handler.go:5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type (
OuranosHandler interface {
GetOuranos(c echo.Context) error
PutOuranos(c echo.Context) error
}

ouranosHandler struct {
cfpHandler ICfpHandler
cfpCertificationHandler ICfpCertificationHandler
partsHandler IPartsHandler
partsStructureHandler IPartsStructureHandler
tradeHandler ITradeHandler
statusHandler IStatusHandler
}
)

PutOuranosを見てみる。

presentation/http/echo/handler/ouranos_put.go:15

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func (h *ouranosHandler) PutOuranos(c echo.Context) error {
method := c.Request().Method
operatorID := c.Get("operatorID").(string)

dataTarget := c.QueryParam("dataTarget")

switch dataTarget {
case "partsStructure":
return h.partsStructureHandler.PutPartsStructureModel(c)
case "parts":
return h.partsHandler.PutPartsModel(c)
case "tradeRequest":
return h.tradeHandler.PutTradeRequest(c)
case "tradeResponse":
return h.tradeHandler.PutTradeResponse(c)
case "cfp":
return h.cfpHandler.PutCfp(c)
case "status":
return h.statusHandler.PutStatus(c)
default:
errDetails := common.UnexpectedQueryParameter("dataTarget")
return echo.NewHTTPError(common.HTTPErrorGenerate(http.StatusBadRequest, common.HTTPErrorSourceDataspace, common.Err400InvalidRequest, operatorID, dataTarget, method, errDetails))
}
}

dataTargetの中身によって処理を変える。 例えば、親部品作成時には、partsが呼ばれていた。 インターフェース定義は以下。

presentation/http/echo/handler/ouranos_traceability_parts.go:18

1
2
3
4
5
6
7
8
type IPartsHandler interface {
// GetPartsModel
// Summary: This is function which defines #8 GetPartsList.
GetPartsModel(c echo.Context) error
// PutPartsModel
// Summary: This is function which defines #5 PutPartsItem.
PutPartsModel(c echo.Context) error
}

PutPartsModelの実装が以下。

presentation/http/echo/handler/ouranos_traceability_parts.go:144

1
2
3
4
5
6
7
8
func (h *partsHandler) PutPartsModel(c echo.Context) error {

dataTarget := c.QueryParam("dataTarget")
method := c.Request().Method

operatorID := c.Get("operatorID").(string)

(snip)

最初にoperatorIDを取得している。

続いて入力を取得。

presentation/http/echo/handler/ouranos_traceability_parts.go:151

1
2
3
4
5
6
7
if err := c.Bind(&putPartsInput); err != nil {
logger.Set(c).Warnf(err.Error())
errDetails := common.FormatBindErrMsg(err)

return echo.NewHTTPError(common.HTTPErrorGenerate(http.StatusBadRequest, common.HTTPErrorSourceDataspace, common.Err400Validation, operatorID, dataTarget, method, errDetails))
}
fmt.Printf("PutPartsInput: %+v\n", putPartsInput)

domain/model/traceability/ouranos_parts_model.go:138

1
2
3
4
5
6
7
8
9
10
type PutPartsInput struct {
OperatorID string `json:"operatorId"`
TraceID *string `json:"traceId"`
PlantID string `json:"plantId"`
PartsName string `json:"partsName"`
SupportPartsName *string `json:"supportPartsName"`
TerminatedFlag *bool `json:"terminatedFlag"`
AmountRequired *float64 `json:"amountRequired"`
AmountRequiredUnit *string `json:"amountRequiredUnit"`
}

バリデーション。

presentation/http/echo/handler/ouranos_traceability_parts.go:159

1
2
3
4
5
6
7
8
9
10
11
if err := putPartsInput.Validate(); err != nil {
logger.Set(c).Warnf(err.Error())
errDetails := err.Error()

return echo.NewHTTPError(common.HTTPErrorGenerate(http.StatusBadRequest, common.HTTPErrorSourceDataspace, common.Err400Validation, operatorID, dataTarget, method, errDetails))
}
if operatorID != putPartsInput.OperatorID {
logger.Set(c).Warnf(common.Err403AccessDenied)

return echo.NewHTTPError(common.HTTPErrorGenerate(http.StatusForbidden, common.HTTPErrorSourceDataspace, common.Err403AccessDenied, operatorID, dataTarget, method))
}

パーツのモデルを作成。

1
2
3
4
5
6
7
8
9
10
11
partsModel, err := putPartsInput.ToModel()
if err != nil {
logger.Set(c).Warnf(err.Error())
errDetails := err.Error()

return echo.NewHTTPError(common.HTTPErrorGenerate(http.StatusBadRequest, common.HTTPErrorSourceDataspace, common.Err400Validation, operatorID, dataTarget, method, errDetails))
}
var partsStructureModel = traceability.PartsStructureModel{
ParentPartsModel: &partsModel,
}
fmt.Printf("PutParts: PartsStructureModel: %+v\n", partsStructureModel)

パーツモデルをPut。

presentation/http/echo/handler/ouranos_traceability_parts.go:183

1
res, err := h.partsStructureUsecase.PutPartsStructure(c, partsStructureModel)

PUtPartsStructureの実装のひとつは以下。

usecase/parts_structure_usecase_datastore_impl.go:55

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func (u *partsStructureUsecase) PutPartsStructure(c echo.Context, partsStructureModel traceability.PartsStructureModel) (traceability.PartsStructureModel, error) {

partsStructure, err := u.OuranosRepository.PutPartsStructure(partsStructureModel)
if err != nil {
logger.Set(c).Errorf(err.Error())
return traceability.PartsStructureModel{}, err
}
partsStructureModels, err := partsStructure.ToModel()
if err != nil {
logger.Set(c).Errorf(err.Error())

return traceability.PartsStructureModel{}, err
}
return partsStructureModels, nil
}

もうひとつは、

usecase/parts_structure_usecase_traceability_impl.go:61

1
2
3
func (u *partsStructureTraceabilityUsecase) PutPartsStructure(c echo.Context, partsStructureModel traceability.PartsStructureModel) (traceability.PartsStructureModel, error) {

(snip)

参考

共有