自分のカテゴリー向けに application/* インテント種別を追加してもらう方法と、最初の挑戦でインテントの理解を取り違えていた点について。
中心となるオブジェクトが商品でも注文でも顧客でもないShopifyアプリを開発しているなら、おそらくこの壁に突き当たったことがあるはずです。Sidekickのアプリ拡張は固定されたインテントの語彙を使い、そこに自分たちのオブジェクトが存在しないのです。
QuotWayをSidekickにつなぎ始めたとき、サポートされていた一覧は application/{ad, campaign, email, faq, loyalty-program, return, review, shipment, ticket} と shopify/{customer, order, product} でした。私たちが開発しているのはBtoBの見積・交渉アプリです。quote はありませんでした。
これは回避できる緩やかな制限ではありません。shopify app deploy はアトミックで、サポートされていないインテント種別をそのまま拒否します。つまり不正なインテントが一つあると、それ単体で失敗するのではなく、アプリのバージョン全体を巻き込みます。私たちは読み取り専用のデータツールをリリースし、残りは保留しました。(その第一段階はShopifyのSidekickアプリ拡張を作るで別途書いています。)
そして依頼しました。Shopifyは application/quote をエコシステムに追加しました。私たちのためだけでなく、すべての見積アプリのためにです。
以下が、そこから学んだことです。完全にモデル化を誤っていた部分も含めて。
正面玄関はあり、それは公開リポジトリです
インテント種別は Shopify/app-intent-types で公開の場で管理されています。ディスカッションを開き、根拠を示すと、プラットフォームのエンジニアが答えてくれます。
私たちの依頼が通った理由を、おおよそ重要な順に挙げます。
自社アプリではなく、カテゴリーのために主張する。 私たちが quote を求めたのは、QuotWayに必要だからではなく、見積・交渉アプリが共通のオブジェクトを持つ、認識可能なShopifyアプリの一群だからです。一つのベンダーのために追加された種別は保守の負債ですが、カテゴリーに役立つ種別はプラットフォームの機能です。Shopifyが社内でそれを正当化しなければならない形で、依頼を書きましょう。
実際のオブジェクトを持ち込む。 私たちは見積とは実際に何なのかを説明しました。バイヤーの依頼が価格付けされ、複数のラウンドにわたって交渉され、注文に変換されるものであること、そしてカテゴリー内のすべてのアプリで意味を持つのはどのフィールドかを示しました。その問い(「スキーマは何を保持する必要があるか」)は、何かがリリースされる前に必ず答えが出ていなければならないものなので、先に答えておくことで先送りにする主な理由がなくなります。
具体的なアクションを提案する。 私たちは open、send_proposal、counter、convert を提案しました。そのすべてが却下されました。それでもRFCの中で最も有益な部分でした。却下された理由がモデルを教えてくれたからです。次の節をご覧ください。間違っていても具体的な提案は、曖昧な提案に勝ります。プラットフォームのチームに、訂正すべき具体的な対象を与えられるからです。
作業を引き受けると申し出る。 types/ のエントリーを追加するフォローアップのPRを開くと申し出ました。彼らは断って自分たちで行いましたが、この申し出は依頼したものを自分で保守する意思を示します。
所要期間は、7月初旬にRFCを提出し、8月27日に承認されました。自分で管理できる機能よりは遅いですが、この欠落を永久に迂回し続けるよりはるかに速いです。
取り違えていたこと:インテントは命令ではなく引き継ぎである
この種別を承認したプラットフォームのエンジニアからの、認識を組み替えてくれた返信がこれです。
「
open、send_proposal、counter、convertは追加しません。すべてのapplication/*種別がサポートするのはcreateとeditだけです。インテントは命令ではなく引き継ぎです。 Sidekickの仕事は、マーチャントが適切なコンテキストが読み込まれた状態であなたのアプリにいる時点で終わり、その先はあなたのUIが担います。」
私たちはインテントをリモコンのAPIとしてモデル化していました。製品がサポートする動詞ごとに一つのインテント、という形です。それは誤りで、その理由が見えると、この面全体が単純になります。
理解しておく価値のある帰結が二つあります。
open はすでに edit が意味しているものです。 「表示するだけ」の別アクションは存在しません。edit は変更操作ではなくナビゲーションの契約だからです。edit:application/quote を登録すると、Sidekickがマーチャントをあなたのページへ導きます。そのページがエディターなのか読み取り専用のビューなのかは、あなたの裁量です。Shopify自身のリファレンス拡張はまさに open-email と open-campaign という名前で、どちらも action = "edit" を登録しています。
自分たちの動詞はインテントではなく、ページ内のツールです。 send_proposal、counter、convert は削られたのではなく、置き場所が変わりました。それらは見積が開かれた後に実行され、自分たちの確認UIへ内容を用意するツールです。そしてそこは、そもそもマーチャントがボタンを押すはずだった場所です。私たちは製品の面全体をインテントの語彙へ持ち上げようとしていましたが、プラットフォームはそうするなと言っているのです。
六つの動詞を求めるRFCを提出しようとしているなら、往復を一つ省けます。種別を求め、動詞はツールとして設計してください。
見落としやすいもう半分
インテントを登録するだけでは何も起きません。次の両方が成り立っている必要があります。
application/quoteのインテントを登録している。- 検索結果が
mimeType: "application/quote"を持っている。
「リソースリンクの
mimeTypeがインテント種別と一致していることが、検索結果を呼び出し可能にします。」
これがつながりです。データ拡張はリソースリンクを返し、登録済みのインテントと mimeType が一致するリンクは、読むだけのカードではなくマーチャントが開けるものになります。
私たちはベンダー独自のmime、application/vnd.quotway.quote を出力していました。標準の種別が存在しない間はそれが正しく、標準が登場した瞬間に私たちを塞ぐものになりました。修正は二つの文字列です。
-const QUOTE_MIME = "application/vnd.quotway.quote";
+const QUOTE_MIME = "application/quote";
- uri: `gid://application/quotway.quote/${q.id}`,
+ uri: `gid://application/quote/${q.id}`,
インテント種別がないものには、ベンダー独自のmimeを残してください。 私たちのパイプライン概要とターゲティングのルールは今も application/vnd.quotway.* を使っています。パイプラインの概要は開けないからです。標準の種別を付けると、存在しないアクションをマーチャントに約束することになります。
具体的な配線
インテント拡張は ui_extension ではなく admin_link です。
[[extensions]]
name = "Open quote"
description = "Open a specific QuotWay quote, quote request (RFQ), proposal or counteroffer…"
handle = "quotway-open-quote"
type = "admin_link"
[[extensions.targeting]]
target = "admin.app.intent.link"
url = "/app/quotes/{id}"
tools = "./tools.json"
instructions = "./instructions.md"
[[extensions.targeting.intents]]
type = "application/quote"
action = "edit"
schema = "./intent-schema.json"
そして、実際にURLが埋められるのはこのスキーマです。
{
"$schema": "https://extensions.shopifycdn.com/shopifycloud/schemas/v1/intent.json",
"value": {
"type": "string",
"description": "The GID of the quote to open.",
"mapTo": "param",
"fieldName": "id"
},
"inputSchema": {
"$ref": "https://extensions.shopifycdn.com/shopifycloud/schemas/v1/application/quote.json",
"type": "object",
"properties": {
"buyer_email": { "type": "string", "description": "…" },
"company": { "type": "string", "description": "…" },
"total": { "type": "number", "description": "…", "minimum": 0 },
"message": { "type": "string", "description": "…" }
}
}
}
時間の節約になる点が四つあります。
mapTo: "param" と fieldName が、url の {id} を埋めます。 名前は一致していなくて構いません。fieldName が対応先を指定します。
GIDは自動的に切り詰められます。 param に対応づけられた値がGIDの場合、Sidekickは最後の / より後のセグメントだけを差し込みます。つまり gid://application/quote/clx123abc は clx123abc として届きます。私たちの既存のルートはすでにそのidを期待していたのでルートの変更は不要でしたが、これはGIDの末尾がルーターの求めるものと一致していなければならないことも意味します。
公開されているスキーマは意図的に最小限です。 application/quote は id を additionalProperties: true とともに公開しています。ドメイン固有のもの、つまり buyer_email、company、total、message はすべて inputSchema.properties の下に自分で宣言します。標準の種別はデータモデルではなく、握手の取り決めです。
ツールは任意ではなく必須です。 Sidekickはツールを持つインテントしか呼び出しません。tools.json のないインテントリンクのターゲットは、登録は通っても一度も発火しません。
二つの落とし穴
admin_link 拡張では api_version は許可されていません。 私たちは ui_extension のTOMLからそれをコピーしてしまい、設定が不正になりました。これで検出できます。
shopify app config validate --json
デプロイのたびに、その前に実行してください。デプロイはアトミックです。不正な拡張はそれ単体で失敗するのではなく、バージョン全体を失敗させます。
予算は見た目より厳しく、インテントは5つ、ツールはリリースするすべての拡張で共有の20個です。 私たちのデータ拡張はすでに15のツールを登録していました。ページ内で内容を用意するツールを3つと、事前入力用を1つ加えて、20のうち19に達しました。充実したデータ拡張とアクションの両方を計画しているなら、まずツールの予算を立ててください。インテントを使い切るよりずっと早くツールが尽きます。
同じ状況にいるなら
アプリの中心オブジェクトが一覧にないなら、おそらくその状況です。おおよそ次のとおりです。
Shopify/app-intent-typesを確認する - 誰かがすでに依頼しているかもしれません。- 自分のアプリではなく、自分のカテゴリーのためにディスカッションを開く。 オブジェクト、一般化できるフィールド、そしてマーチャントが実際に口にする要望を説明します。
- 種別を求め、動詞はツールとして設計する。 得られるのは
createとeditです。それで十分です。 - その間にデータ拡張をリリースする。 読み取り専用のツールはインテントの語彙に触れないため、決して塞がれません。自分の種別が実現したら、mimeの変更は2行の差分です。
- デプロイの前に検証する。
語彙は壁のように見えます。実際には待ち行列です。
QuotWayはShopify向けのBtoB見積・交渉アプリです。application/quote のインテント種別はこのカテゴリーのすべてのアプリが利用できます。もしあなたが同じ種類のアプリを作っているなら、それはあなたのものでもあります。開発者ではなくマーチャントの方は、管理画面で実際に何ができるのかをマーチャント向けの記事で扱っています。
関連記事
QuotWayがこれをあなたのストアでどう扱うかをご覧ください。