LoginSignup
4
0

More than 3 years have passed since last update.

krewSheetで「保存失敗」を無理やりハンドリングする

Last updated at Posted at 2019-04-24

はじめに

kintone連携サービスのkrewSheet便利ですよね!
何よりJavaScript APIがあるのがとても良い!
krewSheetの一覧画面で編集した結果をうまく使ってJSの独自処理を追加できるので、重宝してます。

kintone JavaScript APIの仕様にうまいこと似せて、
こんな感じのイベントハンドリングができます。楽しいね!
https://docs.krew.grapecity.com/krewsheet/#event_recordlist.html

こんなプログラム作ってました

Webpackでビルドする前提です。

import swal from 'sweetalert'

const CANCEL_MESSAGE = 'キャンセルしました。'

kintone.events.on('app.record.index.show', event => {
  krewsheet.events.on('app.record.index.edit.submit', async event => {
    const confirmed = await swal({
      text: '他のアプリにAPI発行とか色々します。よろしいですか?',
      buttons: {
        // OKするとアラートを消さずにspinner状態になって待機
        confirm: { closeModal: false },
        cancel: true,
      },
    })
    if (!confirmed) {
      event.error = CANCEL_MESSAGE
    }
    return event
  })

  krewsheet.events.on('app.record.index.edit.submit.success', async event => {
    try {
      // 更新したレコードを使って他のアプリにAPI発行とか色々

      // 成功しても失敗しても、spinner状態になってたアラートはここで消える
      await swal('成功しました')
    } catch (e) {
      await swal('失敗しました')
    }
  })

  return event
})

4行でまとめるとこんな感じ。

  1. krewSheetの一覧画面で何かしらレコード更新
  2. SweetAlertのこんな感じのダイアログを出して、OK押したらSpinner状態になって待機
  3. krewSheetがレコード更新完了したら、更新したレコードを使って他のアプリにAPI発行とか色々
  4. 成功しても失敗しても新しいアラートを出して、2のSpinnerはそこで消える

submit.failureハンドラが欲しい!

ここで一つ問題があるんです。
kintoneの「レコードアクセス権」を設定していて「編集権限がない」場合、krewSheetがレコード保存に失敗しやがるんですよ!
その場合、submit.successハンドラが発火しないので、submitハンドラで表示したアラートのSpinnerがずーっと消えずに残っちゃうんです。

画面左下にこっそりこんなエラーが出たり、console.errorにもエラー表示が出るけど、詳しくない人だったら、気づかずにずーっと待ってしまうはず。
スクリーンショット 2019-04-25 2.19.27.png
スクリーンショット 2019-04-25 2.50.03.png

なので、app.record.index.edit.submit.failureみたいなイベントハンドラが本当は欲しいんですよね。そうすれば「krewSheetがレコード保存に失敗した時はこうリカバリ」ってロジックが自前で書ける。

無いから自作しました!

unhandledなErrorがthrowでもされていれば、こんな方法で無理やりハンドリングしてやることもできます。
ユーザのブラウザで起きた JavaScript のエラーを収集する

でも今回はただ「エラー出力がされているだけ」なので、その方法は無理。
ぢゃあどうするか?コンソールだけじゃなくて画面上にエラーが出てるのなら、DOM監視でエラーハンドリングしてやることにしました。

この記事がとても参考になりました。感謝!
JavaScriptのMutationObserverでDOMの変化を監視する方法

そして、どうせならkrewsheet.events.on()みたいなイベントハンドリングをしてやりたいってことで、EventEmitter2ライブラリを使いました。

完成版がこちらになります!

main.js
import swal from 'sweetalert'
import EventEmitter2 from 'eventemitter2'
import emitHandler from './krewErrorHandler'

const CANCEL_MESSAGE = 'キャンセルしました。'

kintone.events.on('app.record.index.show', event => {
  krewsheet.events.on('app.record.index.edit.submit', async event => {
    const confirmed = await swal({
      text: '他のアプリにAPI発行とか色々します。よろしいですか?',
      buttons: {
        cancel: true,
        // OKするとアラートを消さずにspinner状態になって待機
        confirm: { closeModal: false },
      },
    })
    if (!confirmed) {
      event.error = CANCEL_MESSAGE
    }
    return event
  })

  krewsheet.events.on('app.record.index.edit.submit.success', async event => {
    try {
      // 更新したレコードを使って他のアプリにAPI発行とか色々

      // 成功しても失敗しても、spinner状態になってたアラートはここで消える
      await swal('成功しました')
    } catch (e) {
      await swal('失敗しました')
    }
  })

  // EventEmitter2を新しいグローバルオブジェクトに割り当てる
  window.krewsheetNeo = { events: new EventEmitter2() }

  // 保存失敗時のハンドリング処理(Neoがつきます)
  krewsheetNeo.events.on('app.record.index.edit.submit.failure', async event => {
    await swal('エラー:' + event.error)
  })

  // 通常のkrewsheet一覧表示イベント
  krewsheet.events.on('app.record.index.show', event => {
    // failureイベントハンドラの発火タイミングをこの中で設定
    emitHandler(CANCEL_MESSAGE)
  })

  return event
})
krewErrorHandler.js
export default (...excludeErrors) => {
  // 監視ターゲットの取得(krewSheetのステータスバー)
  const target = document.querySelector('.GCSK_statusbar')

  // オブザーバーの作成
  const observer = new MutationObserver(records => {
    // エラーメッセージ表示時だけが対象
    if (records.every(r => r.target.textContent !== 'エラー')) {
      return
    }

    // ステータスバー内のエラーメッセージを取得
    const error = document.querySelector('#GCSK-statusMessage').textContent
    if (excludeErrors.includes(error)) {
      // 特定のエラーメッセージはハンドリングから除外
      return
    }

    // イベント発火
    krewsheetNeo.events.emit('app.record.index.edit.submit.failure', { error })
  })

  // 監視オプションの作成
  const options = {
    childList: true, // 子要素リストの変化を監視
    subtree: true, // 子孫ノードを監視対象に含める
  }

  // 監視の開始
  observer.observe(target, options)
}

どうですか!めっちゃそれっぽいのが出来ましたよ!
これでレコードアクセス権の問題で保存に失敗した時も、綺麗にアラートを消してあげることができます。

ポイントは、confirmダイアログをキャンセルした時も便宜上エラー扱いになるので、その場合だけはメッセージを判定してハンドリングの対象外にしてあげることですね。
スクリーンショット 2019-04-25 2.19.35.png

終わりに

ってわけで、頑張って作りはしましたが、グレープシティさん、どうか公式でapp.record.index.edit.submit.failureハンドラをお願いします:confounded:

あとkintoneのように、submitハンドラ内でreturn falseするとキャンセルできるようにもして欲しいなぁ〜。

追記)kintone REST APIの evaluate.json 使った方が良さそう…

去年の夏に、こんな/k/v1/records/acl/evaluate.jsonと言うAPIコマンドが追加されたんでした!
https://developer.cybozu.io/hc/ja/articles/360000869566

今回failureハンドラを作った唯一の目的が「編集権限がないレコードを更新試みて失敗した場合」だったので、それを回避するだけならsubmitハンドラ内でevaluate.jsonを使って事前チェックしてやれば十分じゃないか・・・

import swal from 'sweetalert'

const CANCEL_MESSAGE = 'キャンセルしました。'

kintone.events.on('app.record.index.show', event => {
  krewsheet.events.on('app.record.index.edit.submit', async event => {
    // 追記ここから
    // REST APIで更新対象レコードのアクセス権を調べる
    const ids = event.records.map(record => record.$id.value)
    const { rights } = await kintone.api('/k/v1/records/acl/evaluate', 'GET', {
      app: kintone.app.getId(),
      ids,
    })
    // アクセス権がないレコードが1つでもあったら以降の処理はキャンセル
    if (rights.some(acl => !acl.record.editable)) {
      return event
    }
    // 追記ここまで

    const confirmed = await swal({
      text: '他のアプリにAPI発行とか色々します。よろしいですか?',
      buttons: {
        cancel: true,
        // OKするとアラートを消さずにspinner状態になって待機
        confirm: { closeModal: false },
      },
    })
    if (!confirmed) {
      event.error = CANCEL_MESSAGE
    }
    return event
  })

  krewsheet.events.on('app.record.index.edit.submit.success', async event => {
    try {
      // 更新したレコードを使って他のアプリにAPI発行とか色々

      // 成功しても失敗しても、spinner状態になってたアラートはここで消える
      await swal('成功しました')
    } catch (e) {
      await swal('失敗しました')
    }
  })

  return event
})

krewSheet側に改善してもらいたいのは、ぜひ一覧表示時にevaluate.jsonを考慮して「アクセス権のないレコードを編集状態にできない」って仕様にしてもらいたいですね!そうすればsubmitハンドラが発火すらしないので、より楽にカスタマイズできる。
(結局failureハンドラは要らなかったのか・・・・orz)

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0