Skip to Content

JMAPアクション

JMAPアクションは、JMAP (RFC 8620 、RFC 8621 )のメソッドを呼びます。セッションを取得し、各呼び出しのアカウントを補い、呼び出しを1回のリクエストで送ります。レスポンスは呼び出しのidごとに返し、メソッドのエラーは1つのリストにまとめます。

外部アクションとしてmozership/probe-jmap で公開しています。ワークフローが初めて使うときにProbeがダウンロードし、固定したコミットのaction.ymlが示すSHA-256と一致する実行ファイルだけを実行します。Probe v1.21.0以降が必要です。このバージョンから、action.ymlが申告するガードとパラメータを読みます。

基本的な構文

ステップでは40文字のコミットSHAでアクションを固定します。各リリース のノートの先頭に、コピーして使うusesの行があります。

steps: - name: Read the latest message uses: github.com/mozership/probe-jmap@52000030f1bd839677830d1ab07a024183f581b7 # v0.2.0 with: url: https://jmap.example.com basic_auth: username: "{{vars.user}}" password: "{{vars.pass}}" calls: - method: Email/query id: latest args: sort: [{property: receivedAt, isAscending: false}] limit: 1 - method: Email/get id: email args: "#ids": {resultOf: latest, path: /ids} properties: [subject, from, receivedAt] test: status == 0 && len(res.results.email.list) == 1 echo: "Latest: {{res.results.email.list[0].subject}}"

パラメータ

パラメータ型必須デフォルト説明
urlStringはい-サーバー。httpまたはhttps。パスのないURLはサーバーとして扱い、セッションを/.well-known/jmapから取得します。パスのあるURLはセッションのURLそのものとして扱います
callsArrayはい-メソッドの呼び出し。この順に1回のリクエストで送ります
usingArrayいいえ推定リクエストが使うcapability。既定ではcoreと、callsのメソッドを定義するcapabilityです
account_idStringいいえプライマリアカウントargsにaccountIdがない呼び出しのアカウント
basic_authObjectいいえ-HTTP Basic認証のusernameとpassword
headersObjectいいえ-Authorization: Bearer <token>などのリクエストヘッダー
timeoutDurationいいえ30sセッションとAPIのリクエストを合わせた制限時間。10sのような形式か秒数で指定します。0を指定すると制限しません

callsの各呼び出しには次を書きます。

フィールド必須説明
methodはいEmail/getのようなメソッド名
argsいいえメソッドの引数
idいいえ呼び出しのid。back-referenceとres.resultsで使います。既定はcと呼び出しの位置(c0から)です

argsにaccountIdも#accountIdもない呼び出しは、account_idのアカウントで行います。account_idもなければ、セッションがメソッドのcapabilityに示すプライマリアカウントで行います。Core/echoはアカウントなしで行います。

名前が#で始まる引数(back-reference)では、nameを省略できます。省略すると、resultOfが指す呼び出しのメソッドを補います。back-referenceは引数全体を置き換えるため、filterの中のように引数の内側には書けません。

capability

usingはメソッド名の型から推定します。

型capability
Coreurn:ietf:params:jmap:core
Mailbox、Thread、Email、SearchSnippeturn:ietf:params:jmap:mail
Identity、EmailSubmissionurn:ietf:params:jmap:submission
VacationResponseurn:ietf:params:jmap:vacationresponse
MDNurn:ietf:params:jmap:mdn
Bloburn:ietf:params:jmap:blob
Quotaurn:ietf:params:jmap:quota
SieveScripturn:ietf:params:jmap:sieve
AddressBook、ContactCardurn:ietf:params:jmap:contacts

これ以外の型のメソッドにはusingが必要です。usingを指定したときは、書いたとおりに送ります。

上記のパラメータ以外のキーをwithに書くと、何も送る前にステップが失敗します。action.ymlはパラメータをparamsとして申告しているため、probe checkがそのキーを行番号とともに報告します。

レスポンスオブジェクト

プロパティ型説明
res.codeIntegerHTTPステータスコード
res.statusString"200 OK"のようなHTTPステータス行
res.headersObject正規化した名前をキーとするレスポンスヘッダー
res.sessionObjectサーバーが返したセッションオブジェクト。ないときはnull
res.resultsObject各メソッドのレスポンスの引数。呼び出しのidをキーにします。メソッドのエラーもエラーオブジェクトとしてここに入ります
res.responsesArrayすべてのメソッドのレスポンス。順にname、args、idを持ちます
res.errorsArray失敗したもの。内容は下記のとおりです。何も失敗しなければ空です
res.bodyAnyレスポンスボディ全体。JSONなら解析した値、それ以外は文字列
res.rawbodyString解析前のボディ。ボディがJSONのときに入ります
reqObjectsession_url、APIのurl、using、送ったcalls、headers
rtDurationセッションとAPIのリクエストを合わせた時間
statusIntegerAPIが2xxでメソッドのレスポンスを返し、res.errorsが空なら0。それ以外は1

JMAPのサーバーは、実行できなかったメソッドにもHTTP 200で応答します。そのため、res.codeだけでは呼び出しが成功したかわかりません。res.errorsはすべての失敗を集めます。各要素は、サーバーが返したエラーオブジェクトに次のフィールドを加えたものです。

kind失敗したもの加えるフィールド
methoderrorのレスポンスが返ったメソッドの呼び出しid、method
notCreated、notUpdated、notDestroyed/setの呼び出しのレコードid、method、key(作成のidかレコードのid)
requestproblem detailsのオブジェクトが返ったリクエスト全体-
sessionサーバーが返さなかったセッションdescription

サーバーがセッションを返さなかったときは、メソッドの呼び出しを送りません。このときresはセッションのリクエストへのレスポンスです。

サーバーが返したレスポンスはすべて結果として扱います。そのため、メソッドのエラーや401もテストで確かめられます。接続の拒否やタイムアウトのように、レスポンスを得られなかったリクエストだけが、ステップをエラーとして失敗させます。

使用例

メッセージを送り、届くのを待つ

Email/setでメッセージを作り、EmailSubmission/setで送ります。送るメッセージは作成のid #msgで指します。続くステップではretryで届くのを待ちます。

steps: - name: Send a message to bob uses: github.com/mozership/probe-jmap@52000030f1bd839677830d1ab07a024183f581b7 # v0.2.0 with: url: "{{vars.url}}" basic_auth: {username: "{{vars.alice}}", password: "{{vars.alice_pass}}"} calls: - method: Email/set id: create args: create: msg: mailboxIds: "{{outputs.alice.sent}}": true from: [{email: "{{vars.alice}}"}] to: [{email: "{{vars.bob}}"}] subject: "{{vars.subject}}" bodyValues: {body: {value: Sent by probe.}} textBody: [{partId: body, type: text/plain}] - method: EmailSubmission/set id: submit args: create: sub: identityId: "{{outputs.alice.identity}}" emailId: "#msg" test: status == 0 && res.results.submit.created.sub.id != nil - name: Wait for it in bob's mailbox uses: github.com/mozership/probe-jmap@52000030f1bd839677830d1ab07a024183f581b7 # v0.2.0 with: url: "{{vars.url}}" basic_auth: {username: "{{vars.bob}}", password: "{{vars.bob_pass}}"} calls: - method: Email/query id: query args: filter: {subject: "{{vars.subject}}"} - method: Email/get id: get args: "#ids": {resultOf: query, path: /ids} properties: [subject, receivedAt] retry: max_attempts: 30 interval: 1s test: status == 0 && len(res.results.get.list) == 1

失敗を確かめる

steps: - name: A record that cannot be created uses: github.com/mozership/probe-jmap@52000030f1bd839677830d1ab07a024183f581b7 # v0.2.0 with: url: "{{vars.url}}" basic_auth: {username: "{{vars.user}}", password: "{{vars.pass}}"} calls: - method: Email/set args: create: bad: mailboxIds: {no-such-mailbox: true} test: | status == 1 && res.errors[0].kind == "notCreated" && res.errors[0].key == "bad"

ガード

--read-onlyを指定した実行では、/get、/query、/changes、/queryChanges、/lookup、/echo以外のメソッドを含むステップを、何も送る前に拒否します。--allow-hostが許可しないホストは、セッション、APIのURL、リダイレクト先のいずれでも拒否します。action.ymlはguard: [read-only, allow-host]を申告しているため、どちらのガードの下でも--allow-actionなしで実行します。ガードを参照してください。

関連項目

  • 外部アクション - Probeが外部アクションを解決し、照合する仕組み
  • IMAP - IMAPでのメールボックスの操作
  • SMTP - サーバーへのメールの配送
  • GraphQL - Probeとあわせて公開しているもう1つの外部アクション
更新日時