Class: Square::Reporting::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/square/reporting_helper.rb,
lib/square/reporting/client.rb

Overview

Custom, +.fernignore+-protected polling helper for the Reporting API.

The /reporting/v1/load endpoint is asynchronous: a query that is still being computed comes back as an HTTP 200 whose body is { "error": "Continue wait" } rather than the results. Callers are expected to re-send the identical request, with backoff, until real results arrive. This file reopens the generated Client to add load_and_wait, which owns that retry loop.

Examples:

require "square"
require "square/reporting_helper"

client = Square::Client.new(token: ENV.fetch("SQUARE_TOKEN"))
response = client.reporting.load_and_wait(query: { ... })

Constant Summary collapse

CONTINUE_WAIT_SENTINEL =

The value of the error field the Reporting API returns on an HTTP 200 while a /reporting/v1/load query is still processing. It is NOT a failure — the identical request should be re-sent until real results arrive. See https://developer.squareup.com/docs/reporting-api/overview.

"Continue wait"
DEFAULT_MAX_ATTEMPTS =

Default polling parameters for #load_and_wait.

20
DEFAULT_INITIAL_DELAY =
2.0
DEFAULT_MAX_DELAY =
20.0
DEFAULT_BACKOFF_FACTOR =
2.0
POLL_TICK =

Granularity (seconds) at which a backoff wait checks should_cancel, so cancellation stays responsive even during a long delay.

0.1

Instance Method Summary collapse

Constructor Details

#initialize(client:) ⇒ void

Parameters:



9
10
11
# File 'lib/square/reporting/client.rb', line 9

def initialize(client:)
  @client = client
end

Instance Method Details

#get_metadata(request_options: {}, **params) ⇒ Square::Types::MetadataResponse

Describes the data available to query: the cubes, views, measures, dimensions, and segments you can reference in a reporting query. Call this first to discover the schema, then pass the members you need to load.

Parameters:

  • request_options (Hash) (defaults to: {})
  • params (Hash)

Options Hash (request_options:):

  • :base_url (String)
  • :additional_headers (Hash{String => Object})
  • :additional_query_parameters (Hash{String => Object})
  • :additional_body_parameters (Hash{String => Object})
  • :timeout_in_seconds (Integer)

Returns:



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/square/reporting/client.rb', line 25

def (request_options: {}, **params)
  Square::Internal::Types::Utils.normalize_keys(params)
  request = Square::Internal::JSON::Request.new(
    base_url: request_options[:base_url],
    method: "GET",
    path: "reporting/v1/meta",
    request_options: request_options
  )
  begin
    response = @client.send(request)
  rescue Net::HTTPRequestTimeout
    raise Square::Errors::TimeoutError
  end
  code = response.code.to_i
  if code.between?(200, 299)
    Square::Types::MetadataResponse.load(response.body)
  else
    error_class = Square::Errors::ResponseError.subclass_for_code(code)
    raise error_class.new(response.body, code: code)
  end
end

#load(request_options: {}, **params) ⇒ Square::Types::LoadResponse

Runs a reporting query against the discovered schema and returns the aggregated results. Long-running queries may return a "Continue wait" response while processing — retry the same request until results are ready.

Parameters:

Options Hash (request_options:):

  • :base_url (String)
  • :additional_headers (Hash{String => Object})
  • :additional_query_parameters (Hash{String => Object})
  • :additional_body_parameters (Hash{String => Object})
  • :timeout_in_seconds (Integer)

Returns:



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/square/reporting/client.rb', line 59

def load(request_options: {}, **params)
  params = Square::Internal::Types::Utils.normalize_keys(params)
  request = Square::Internal::JSON::Request.new(
    base_url: request_options[:base_url],
    method: "POST",
    path: "reporting/v1/load",
    body: Square::Reporting::Types::LoadRequest.new(params).to_h,
    request_options: request_options
  )
  begin
    response = @client.send(request)
  rescue Net::HTTPRequestTimeout
    raise Square::Errors::TimeoutError
  end
  code = response.code.to_i
  if code.between?(200, 299)
    Square::Types::LoadResponse.load(response.body)
  else
    error_class = Square::Errors::ResponseError.subclass_for_code(code)
    raise error_class.new(response.body, code: code)
  end
end

#load_and_wait(request_options: {}, max_attempts: DEFAULT_MAX_ATTEMPTS, initial_delay: DEFAULT_INITIAL_DELAY, max_delay: DEFAULT_MAX_DELAY, backoff_factor: DEFAULT_BACKOFF_FACTOR, should_cancel: nil, **params) ⇒ Square::Types::LoadResponse

Runs a reporting query and transparently polls until it resolves, returning the final Types::LoadResponse.

A query that is still being computed comes back as an HTTP 200 whose body is { "error": "Continue wait" }. That body deserializes into a LoadResponse whose error key lands in the model's extra fields (it is not a declared field) while results stays nil; that is the signal to retry. load_and_wait re-sends the identical request with exponential backoff until real results arrive.

Parameters:

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

    forwarded verbatim to each underlying #load call.

  • max_attempts (Integer) (defaults to: DEFAULT_MAX_ATTEMPTS)

    maximum number of load calls before giving up. Defaults to DEFAULT_MAX_ATTEMPTS.

  • initial_delay (Numeric) (defaults to: DEFAULT_INITIAL_DELAY)

    delay (seconds) before the first retry. Defaults to DEFAULT_INITIAL_DELAY.

  • max_delay (Numeric) (defaults to: DEFAULT_MAX_DELAY)

    upper bound (seconds) on the backoff delay. Defaults to DEFAULT_MAX_DELAY.

  • backoff_factor (Numeric) (defaults to: DEFAULT_BACKOFF_FACTOR)

    multiplier applied to the delay after each attempt. Defaults to DEFAULT_BACKOFF_FACTOR.

  • should_cancel (#call, nil) (defaults to: nil)

    optional predicate checked before each attempt and during each backoff wait; when it returns a truthy value the loop aborts with PollingCancelledError. This is the Ruby idiom for TypeScript's AbortSignal / Go's context.Context.

  • params (Hash)

    the reporting query, forwarded verbatim to #load (same keyword arguments as a direct load call).

Returns:

Raises:



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/square/reporting_helper.rb', line 79

def load_and_wait(
  request_options: {},
  max_attempts: DEFAULT_MAX_ATTEMPTS,
  initial_delay: DEFAULT_INITIAL_DELAY,
  max_delay: DEFAULT_MAX_DELAY,
  backoff_factor: DEFAULT_BACKOFF_FACTOR,
  should_cancel: nil,
  **params
)
  delay = initial_delay
  attempt = 1

  loop do
    raise PollingCancelledError if should_cancel&.call

    response = load(request_options: request_options, **params)
    return response unless continue_wait?(response)

    break if attempt >= max_attempts

    cancellable_sleep(delay, should_cancel)

    attempt += 1
    delay = [delay * backoff_factor, max_delay].min
  end

  raise ContinueWaitTimeoutError,
        "Reporting query did not complete after #{max_attempts} attempts " \
        "(#{CONTINUE_WAIT_SENTINEL.inspect})"
end