ZoomAPI（Server-to-server OAuth）でミーティング設定更新の自動化（Update a meeting）してみた！

[バヅクリ](https://buzzkuri.com)CTOの合原です。
[前回](https://qiita.com/kieaiaarh/items/c604541b08ef19135025)投稿では、zoomAPIを使って、Zoomミーティングを作成するアプリケーションを作ってみました。

いいタイミングでこちらのキャンペーンも開催しているので、今回ももちろん、相乗りさせていただきました :sweat_smile: 
https://qiita.com/official-campaigns/engineer-festa/2022

上記作成したところ、すぐさま、
「新規作成＆既にできているZoomミーティングのレコーディング設定を自動化したい」、
という要望が挙がり、それならば！と、
前回作成したアプリケーションに、今回は、[Zoom Meeting update API](https://marketplace.zoom.us/docs/api-reference/zoom-api/methods/#operation/meetingUpdate) を利用して、機能拡張してみました。


## 前提
* [前回](https://qiita.com/kieaiaarh/items/c604541b08ef19135025)の対応ですでにServer-to-server OAuthを使用したzoomアプリの作成は完了済み（access tokenの取得はできる状態）


## 調査
何はともあれ、[リファレンス](https://marketplace.zoom.us/docs/api-reference/zoom-api/methods/#operation/meetingUpdate)をたよりに、まずはAPIについて調査です。

![スクリーンショット 2022-07-13 15.19.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64537/b5c5fae7-7544-5cab-2267-320e6aa3d241.png)

> https://api.zoom.us/v2/meetings/{meetingId}
`meetingIdをパスパラメーターにして、patchリクエスト`なんだとか。。。

正直もう、これが全てでした。
あとは、更新したい項目をjson request bodyに設定するのみ。


## 設計・実装
さて、Railsでどうやるか。まずは設計です。
といっても、こちらも[前回](https://qiita.com/kieaiaarh/items/c604541b08ef19135025)拡張しやすいように設計してあったので、基本、必要な部品を追加するのみです。

### Zooms::Meetingクラスへメソッド追加
* Zoomミーティング作成時はPOSTリクエストでしたが今回は更新＝PATCHリクエストであること
* リクエストURLも変わる

上記の点から、下記のように`patch_api_request`を追加。

```rb:app/zooms/meeting.rb
class Zooms::Meeting < Zooms::Base
  
  private

    def headers
      {
        Authorization: "Bearer #{access_token}",
        "Content-Type": "application/json"
      }
    end

    def api_request_url
      raise NotImplementedError, "You must implement #{self.class}##{__method__}"
    end
      :

    def patch_api_request # 新規追加
      uri = URI.parse(patch_request_url)
      http = Net::HTTP.new(uri.host, uri.port)
      http.use_ssl = uri.scheme == "https"

      http.patch(uri.path, request_body.to_json, headers)
    end
       :

end

```

### Zooms::Meetings::EventUpdatorクラス作成
今回のZoom Meeting Update APIを実行するクラスを作成。
文字通り、このクラスでは「Updateすること」を責務とし、
* 対象のevent
* 更新対象となるパラメータ
を引数として受け取れるようにしました。

```rb:app/models/zooms/meetings/event_updator.rb
class Zooms::Meetings::EventUpdator < ::Zooms::Meeting
  class UpdateApiError < StandardError; end
  include ActiveModel::Model
  include ZoomRequestBodyComposer #　update対象項目のjsonペイロードを作成するmodule

  ATTRS = %i[
    event
  ].freeze

  UPDATE_PARAMS = %i[
    auto_recording
  ].freeze

  attr_accessor :event
  validates :event, presence: true
  validates :auto_recording, inclusion: { in: %w[none cloud], message: "：｢%<value>s」 は無効です" } # 公式のドキュメントに従いRails標準のvalidationを用意

  (ATTRS + UPDATE_PARAMS).each do |a|
    attr_accessor a
  end

  validate :ensure_update_params

  def update!
    return raise ArgumentError, errors.full_messages.join(", ") if invalid?
    return raise ArgumentError, "#{self.class.name}: Not found zoom settings" unless event.use_zoom?

    response = patch_api_request
    raise UpdateApiError, "ERROR RESPONSE: #{response['code']}" unless response.code == "204"

    Rails.logger.info "#{self.class.name}: SUCCESS to update event:#{event.id} zoom meeting!"
  end

  private

    def patch_request_url
      "https://api.zoom.us/v2/meetings/#{zoom_meeting_id}"
    end

    def zoom_meeting_id
      event.zoom_meeting_id.to_s.gsub(/[[:space:]]/, '')
    end

    def request_body
      # https://marketplace.zoom.us/docs/api-reference/zoom-api/methods/#operation/meetingCreate
      composed_body
    end

    def ensure_update_params
      params = UPDATE_PARAMS.map { |a| send(a).present? }
      errors.add :base, "Not found UPDATE PARAMS!" unless params.include? true
    end
end

```

### update対象項目のjsonペイロードを作成するmodule
上記のクラスで、受け取った`UPDATE_PARAMS`を、APIリクエスト用に成形するmoduleを用意します。

```rb:app/models/concerns/zoom_request_body_composer.rb
module ZoomRequestBodyComposer
  extend ActiveSupport::Concern

  def composed_body
    compose
    @body.dup
  end

  private

    def compose
      @body = default_body
      append_auto_recording if auto_recording.present?
      self
    end

    def append_auto_recording
      @body[:settings][:auto_recording] = auto_recording
    end

    def default_body
      {
        settings: {
          auto_recording: nil
        }
      }
    end
end
```

`composed_body `のとこ、もうちょっとシュッとしたかったですが、まぁ、今回はあきらめです :sweat: 

いずれにしてもこうしておくことで、今後他にも自動で設定したい項目が要望といて上がった際も追加するのが容易になります。

大事なのは然るべき責務に応じて設計すること :sweat_smile: 

## API実行をバックグラウンド実行する
ため、Active job作成します。

```rb:app/jobs/zooms/meeting_update_job.rb
class Zooms::MeetingUpdateJob < ApplicationJob
  queue_as :tasks

  def perform(event)
    ::Zooms::Meetings::EventUpdator.new(event: event, auto_recording: event.membership_request.is_recording_yes? ? "cloud" : "none").update!
  end
end
```


## API実行

```rb
$ :
$ ::Zooms::MeetingUpdateJob.perform_later(event)
```

とすることで、サクッと実行が可能になりました。

実際はAPIの使用制限もあるので、　production利用の際は、気をつけてください :sweat: 


# やったことまとめ
前回構築した、[Zooms](https://qiita.com/kieaiaarh/items/c604541b08ef19135025#%E8%A8%AD%E8%A8%88)を拡張する形でZoomMeetingのUpdate処理を作成しました

```sh:app:zooms
$ tree -L 3 app/models/zooms
app/models/zooms
├── app.rb
├── base.rb
├── meeting.rb
└── meetings
    ├── event.rb
    ├── event_updator.rb #こちら新設

```


## 所感

ZoomAPIはとても、標準的なREST API設計されているため、使用のハードルが非常に低いなと思います。
操作したいリソースを念頭に、一度APIを利用できる設計、実装するだけ、Railsでの機能拡張も容易でした。

今回利用したAPIでは、Zoomミーティング作成時にレコーディングON/OFFのみ行いましたが、他にも、さまざまなオプションが設定できるので、[当社](https://buzzkuri.com)のサービスでのZoom利用ケースで柔軟な設定の自動化が期待できそうです。

今後は、ミーティング作成に限らず、他のAPIやSDKも使いつつ、自動化したり、面白いZoomの使い方をビジネスサイドに提案していきたいと思います :sunglasses: 

# 今回も恒例のやつですが :sweat: 
当社では、バックエンドサーバーとして主にRails、フロントエンドでは、Nextjsを利用しています。
新しい物（コト）好きの当社では、絶賛、エンジニアを募集中です！

気になる方はお気軽にご連絡いただけましたらと思います！

![スクリーンショット 2022-08-02 13.25.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64537/eb151153-a52a-64c8-99a5-06149b64e66a.png)


https://www.wantedly.com/projects/1050133

https://buzzkuri.co.jp/recruit/engineer


https://buzzkuri.co.jp