Class: SmilyCli::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/smily_cli/client.rb

Overview

A thin, generic wrapper over the official BookingSync::API::Client. It reuses that client's connection handling, JSON:API serialization, Link header pagination and typed errors, and adds exactly what a CLI needs: path-based (rather than method-per-resource) access, cross-page collection, write-body wrapping, and translation of gem errors into ApiError.

Everything is driven by a resource path, so any v3 endpoint is reachable — including ones the Registry doesn't list.

Instance Method Summary collapse

Constructor Details

#initialize(token:, base_url: Context::DEFAULT_BASE_URL, account_id: nil, max_pages: Context::DEFAULT_MAX_PAGES, retries: 0, max_retry_wait: 60, sleeper: nil, verbose: false) ⇒ Client

Returns a new instance of Client.

Parameters:

  • token (String)

    OAuth access token

  • base_url (String) (defaults to: Context::DEFAULT_BASE_URL)

    site base URL (the client appends /api/v3)

  • account_id (String, Integer, nil) (defaults to: nil)

    scope requests to one account. Sent as the account_id query parameter on every request, which is how a Client-Credentials-flow token (spanning many accounts) is pinned to a single account. Harmless for single-account (Authorization Code) tokens.

  • max_pages (Integer) (defaults to: Context::DEFAULT_MAX_PAGES)

    hard backstop on auto-pagination: stop after this many pages even if the server keeps advertising a next link.

  • retries (Integer) (defaults to: 0)

    how many times to retry a 429 Too Many Requests, waiting for the rate-limit window before each retry.

  • max_retry_wait (Integer) (defaults to: 60)

    cap (seconds) on any single retry wait.

  • sleeper (#call) (defaults to: nil)

    injectable sleep, for tests. Defaults to Kernel#sleep.

  • verbose (Boolean) (defaults to: false)

    log HTTP traffic to stderr



29
30
31
32
33
34
35
36
37
38
# File 'lib/smily_cli/client.rb', line 29

def initialize(token:, base_url: Context::DEFAULT_BASE_URL, account_id: nil,
               max_pages: Context::DEFAULT_MAX_PAGES, retries: 0,
               max_retry_wait: 60, sleeper: nil, verbose: false)
  @account_id = 
  @max_pages = max_pages
  @retries = retries
  @max_retry_wait = max_retry_wait
  @sleeper = sleeper || ->(seconds) { sleep(seconds) }
  @api = BookingSync::API.new(token, base_url: base_url, logger: build_logger(verbose))
end

Instance Method Details

#create(path, resource_key, attributes, query: {}) ⇒ Result

Create a member. Wraps attributes into the { key: [attrs] } envelope v3 expects, unless the caller already supplied that envelope.

Parameters:

  • path (String)

    collection path

  • resource_key (String)

    JSON:API key (usually the last path segment)

  • attributes (Hash, Array)

    attributes or a ready envelope

  • query (Hash) (defaults to: {})

Returns:



91
92
93
94
# File 'lib/smily_cli/client.rb', line 91

def create(path, resource_key, attributes, query: {})
  response = call(:post, path, query: stringify(query), body: wrap_body(resource_key, attributes))
  Result.from_response(response, single: true)
end

#delete(path, id, body: nil, query: {}) ⇒ Result

Delete/cancel a member. Returns an empty Result on 204.

Returns:



107
108
109
110
# File 'lib/smily_cli/client.rb', line 107

def delete(path, id, body: nil, query: {})
  response = call(:delete, member_path(path, id), query: stringify(query), body: body)
  Result.from_response(response, single: true)
end

#get(path, id, query: {}) ⇒ Result

Fetch a single member.

Parameters:

  • path (String)

    collection path

  • id (String, Integer)
  • query (Hash) (defaults to: {})

Returns:



78
79
80
81
# File 'lib/smily_cli/client.rb', line 78

def get(path, id, query: {})
  response = call(:get, member_path(path, id), query: stringify(query))
  Result.from_response(response, single: true)
end

#list(path, query: {}, limit: nil, page: nil, per_page: nil, all: false) ⇒ Result

List a collection, transparently following pagination as requested.

Parameters:

  • path (String)

    collection path, e.g. "rentals"

  • query (Hash) (defaults to: {})

    query parameters (filters, include, fields, ...)

  • limit (Integer, nil) (defaults to: nil)

    stop after this many records (follows pages)

  • page (Integer, nil) (defaults to: nil)

    fetch one specific page (disables following)

  • per_page (Integer, nil) (defaults to: nil)

    page size hint

  • all (Boolean) (defaults to: false)

    fetch every page

Returns:



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/smily_cli/client.rb', line 49

def list(path, query: {}, limit: nil, page: nil, per_page: nil, all: false)
  q = stringify(query)
  q["per_page"] = per_page if per_page
  # With a limit but no explicit page size, request exactly what we need
  # (capped at the API max of 100) instead of paging at the default 25.
  q["per_page"] = [limit, 100].min if per_page.nil? && limit && page.nil?
  q["page"] = page if page

  response = call(:get, path, query: q)
  first = Result.from_response(response)
  records = first.records.dup
  last = first

  last = follow_pages(response, records, limit: limit) if all || (limit && page.nil?)

  records = records.first(limit) if limit
  Result.new(
    records: records, single: false,
    envelope: last.envelope, key: last.key,
    status: last.status, headers: last.headers
  )
end

#meResult

Fetch /me (current application + account).

Returns:



128
129
130
# File 'lib/smily_cli/client.rb', line 128

def me
  Result.from_response(call(:get, "me", query: {}), single: true)
end

#request(method, path, query: {}, body: nil) ⇒ Result

Escape hatch: issue an arbitrary request against any path. The body is sent verbatim (no envelope wrapping) so callers stay in full control.

Parameters:

  • method (Symbol, String)

    :get/:post/:put/:patch/:delete

  • path (String)

    path or full URL (base + /api/v3 are stripped)

  • query (Hash) (defaults to: {})
  • body (Hash, String, nil) (defaults to: nil)

Returns:



120
121
122
123
# File 'lib/smily_cli/client.rb', line 120

def request(method, path, query: {}, body: nil)
  response = call(method.to_sym, path, query: stringify(query), body: body)
  Result.from_response(response)
end

#update(path, resource_key, id, attributes, query: {}) ⇒ Result

Update a member (HTTP PUT, matching the v3 reference).

Returns:



99
100
101
102
# File 'lib/smily_cli/client.rb', line 99

def update(path, resource_key, id, attributes, query: {})
  response = call(:put, member_path(path, id), query: stringify(query), body: wrap_body(resource_key, attributes))
  Result.from_response(response, single: true)
end