Skip to content

Additional Response Details Proposal #399

Description

@gerryster

Additional Response Details Proposal

This is not a bug but an RFC.

Problem

There are strict rate limits for several actions. For example, meeting write operations are limited to 100 requests per day per user. The Zoom API contains response headers which let the caller know how many requests remain. This information is helpful and should be available via the Zoom gem.

Example Session

Observe what happens when a meeting update rate limit is exceeded:

> for i in {1..101}; do curl -i --request PATCH --header "authorization: Bearer $JWT" --header 'content-type: application/json' --url "https://api.zoom.us/v2/meetings/$meeting_id" -d "{ \"topic\": \"my update $i\" }"; done

HTTP/2 204
date: Tue, 26 Oct 2021 02:27:48 GMT
x-zm-trackingid: v=2.0;clid=us02;rid=WEB_596ca498838a64cf86185261bcfc5a63
x-content-type-options: nosniff
cache-control: no-cache, no-store, must-revalidate, no-transform
pragma: no-cache
expires: Thu, 01 Jan 1970 00:00:00 GMT
set-cookie: zm_aid=""; Domain=.zoom.us; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/; Secure; HttpOnly
set-cookie: zm_haid=""; Domain=.zoom.us; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/; Secure; HttpOnly
set-cookie: zm_tmaid=""; Domain=.zoom.us; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/; Secure; HttpOnly
set-cookie: zm_htmaid=""; Domain=.zoom.us; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/; Secure; HttpOnly
set-cookie: cred=7FEE3662BDDDBE392A2ADB57E4E3E68E; Path=/; Secure; HttpOnly
vary: Origin
vary: Access-Control-Request-Method
vary: Access-Control-Request-Headers
x-ratelimit-category: Light
x-ratelimit-limit: 100
x-ratelimit-remaining: 99
set-cookie: _zm_ctaid=""; Domain=.zoom.us; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/; Secure; HttpOnly
set-cookie: _zm_chtaid=""; Domain=.zoom.us; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/; Secure; HttpOnly
x-zm-zoneid: VA
strict-transport-security: max-age=31536000; includeSubDomains
x-xss-protection: 1; mode=block
referrer-policy: strict-origin-when-cross-origin

And so on until the final two iterations. Observe the x-ratelimit-remaining response header count down:

HTTP/2 204
date: Tue, 26 Oct 2021 02:27:48 GMT
x-zm-trackingid: v=2.0;clid=us02;rid=WEB_0102974fa9c8da2687b2375d1a77d1d1
x-content-type-options: nosniff
cache-control: no-cache, no-store, must-revalidate, no-transform
pragma: no-cache
expires: Thu, 01 Jan 1970 00:00:00 GMT
set-cookie: zm_aid=""; Domain=.zoom.us; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/; Secure; HttpOnly
set-cookie: zm_haid=""; Domain=.zoom.us; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/; Secure; HttpOnly
set-cookie: zm_tmaid=""; Domain=.zoom.us; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/; Secure; HttpOnly
set-cookie: zm_htmaid=""; Domain=.zoom.us; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/; Secure; HttpOnly
set-cookie: cred=878CE20C3062E7F4E0A24D4370A08C30; Path=/; Secure; HttpOnly
vary: Origin
vary: Access-Control-Request-Method
vary: Access-Control-Request-Headers
x-ratelimit-category: Light
x-ratelimit-limit: 100
x-ratelimit-remaining: 0
set-cookie: _zm_ctaid=""; Domain=.zoom.us; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/; Secure; HttpOnly
set-cookie: _zm_chtaid=""; Domain=.zoom.us; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/; Secure; HttpOnly
x-zm-zoneid: VA
retry-after: 2021-10-27T00:00:00Z
strict-transport-security: max-age=31536000; includeSubDomains
x-xss-protection: 1; mode=block
referrer-policy: strict-origin-when-cross-origin

HTTP/2 429
date: Tue, 26 Oct 2021 02:27:48 GMT
content-type: application/json;charset=UTF-8
content-length: 190
x-zm-trackingid: v=2.0;clid=us02;rid=WEB_0b8fe6a9dd55d7955bd1dab3d1db8251
x-content-type-options: nosniff
cache-control: no-cache, no-store, must-revalidate, no-transform
pragma: no-cache
expires: Thu, 01 Jan 1970 00:00:00 GMT
set-cookie: zm_aid=""; Domain=.zoom.us; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/; Secure; HttpOnly
set-cookie: zm_haid=""; Domain=.zoom.us; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/; Secure; HttpOnly
set-cookie: zm_tmaid=""; Domain=.zoom.us; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/; Secure; HttpOnly
set-cookie: zm_htmaid=""; Domain=.zoom.us; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/; Secure; HttpOnly
set-cookie: cred=F53584C4704E4CBE7A3D77AABAB94027; Path=/; Secure; HttpOnly
vary: Origin
vary: Access-Control-Request-Method
vary: Access-Control-Request-Headers
x-ratelimit-category: Light
x-ratelimit-limit: 100
x-ratelimit-remaining: 0
set-cookie: _zm_ctaid=""; Domain=.zoom.us; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/; Secure; HttpOnly
set-cookie: _zm_chtaid=""; Domain=.zoom.us; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/; Secure; HttpOnly
x-zm-zoneid: VA
x-ratelimit-type: Daily-limit
retry-after: 2021-10-27T00:00:00Z
set-cookie: _zm_ctaid=""; Domain=.zoom.us; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/; Secure; HttpOnly
set-cookie: _zm_chtaid=""; Domain=.zoom.us; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/; Secure; HttpOnly

{"code":429,"message":"You have exceeded the daily rate limit (100) of Meeting Create/Update API requests permitted for this particular user. You may resume these requests at GMT 00:00:00."}

Proposal

Currently, responses from this gem are instances of Hash containing the parsed JSON of the HTTP response body from Zoom or integers of the HTTP response code if the body is empty. If configured to do so, a new Zoom::Response object could be returned instead. This will contain three rate limit informational methods: ratelimit_category, ratelimit_limit, and ratelimit_remaining. These will return values corresponding to the response headers. Here is an example usage:

Zoom.configure do |c|
  c.api_key = 'my_key'
  c.api_secret = 'my_secret'
  c.object_responses = true # new option
end

meeting_id = 88888888888

client = Zoom.new
response = client.meeting_update(meeting_id: meeting_id, topic: 'my update')
response.code # 204
response.body # nil or the empty string (open to suggestion here)
response.ratelimit_category # 'Light'
response.ratelimit_limit # 100
response.ratelimit_remaining # 99

response2 = client.meeting_get(meeting_id: meeting_id)
response2.code # 200
response2.body # {"uuid"=>"dfasdfasdfasfdsdf=", "id"=>88888888888, "host_id"=>""232easdfh203, ... }
response2.ratelimit_category # 'Light'
response2.ratelimit_limit # 999999999
response2.ratelimit_remaining # 999999998

This does require an optional new response class while maintaining backward compatibility for more simplistic clients who just want the body or response code. The new response class also opens up new possibilities for features such as enhanced error information like including the HTTP response code (see #388).

I am very interested in feedback on this proposal. Additionally, I am interested submitting a pull request to add such functionality.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesthelp wantedExtra attention is needed

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions