Class: X::PostUsage

Inherits:
Object
  • Object
show all
Includes:
Serialization, ValueEquality, ValueMarshalling
Defined in:
x-objects/lib/x/objects/post_usage.rb

Overview

How many posts the app's project has read, as the usage endpoint reports it

The API caps the posts a project reads each month, and bills each post read, so the usage shows both what a project has spent and how much of its cap remains.

Constant Summary collapse

FIELDS =

Every field of the usage

%w[cap_reset_day daily_client_app_usage daily_project_usage project_cap project_id project_usage].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attrs) ⇒ PostUsage

Initialize the usage from the attributes the API reported

Examples:

Build a usage

X::PostUsage.new({"project_usage" => "1234"})

Parameters:

  • attrs (Hash{String, Symbol => Object}) —

    the attributes

Raises:

  • (ArgumentError) —

    if the attributes are not a Hash



83
84
85
86
# File 'x-objects/lib/x/objects/post_usage.rb', line 83

def initialize(attrs)
  @attrs = Utils.deep_freeze(Utils.attributes!(attrs))
  freeze
end

Instance Attribute Details

#attrs ⇒ Hash{String => Object} (readonly)

The raw attributes of the usage

Examples:

Get the raw attributes

usage.attrs # => {"project_usage" => "1234", "project_cap" => "3000000", ...}

Returns:

  • (Hash{String => Object}) —

    the attributes



34
35
36
# File 'x-objects/lib/x/objects/post_usage.rb', line 34

def attrs
  @attrs
end

Class Method Details

.current(client:, **params) {|problem| ... } ⇒ PostUsage?

Look up the current post usage of the project the client's app belongs to

A project has one usage, so it is looked up by no identifier, as X::User.current looks up the one user a client signs in as. The usage endpoint takes app-only authentication, so a client that signs its requests with OAuth 1.0a looks the usage up with a copy that authenticates as the app. A client signed in with OAuth 2.0 as a user that holds no credentials of the app requests as the user, which the endpoint refuses with X::Forbidden.

A response that holds no usage returns nil, as X::User.current does for a users/me that holds no user, and passes the problems it reported to the block, if there is one.

Examples:

Look up the usage of the last 30 days

X::PostUsage.current(client: client, days: 30)&.project_usage

Log why the API returned no usage

X::PostUsage.current(client: client) { |problem| warn problem.detail }

Parameters:

  • client (Object) —

    the client used to make the request

  • params (Hash) —

    query parameters, such as days, the number of days to report, which is 7 by default

Yield Parameters:

  • problem (Problem) —

    each problem the API reported

Returns:

  • (PostUsage, nil) —

    the usage, or nil if the response holds none



55
56
57
58
59
# File 'x-objects/lib/x/objects/post_usage.rb', line 55

def self.current(client:, **params)
  body = Utils.app_client(client).get(Utils.path(ENDPOINT, {"usage.fields" => FIELDS}.merge(params)), **Utils::JSON_CLASSES)
  Problem.all_from(body).each { |problem| yield problem } if block_given?
  Hash.try_convert(body.to_h["data"])&.then { |data| new(data) }
end

.current!(client:, **params) ⇒ PostUsage

Look up the current post usage of the project, which must be returned

Examples:

Look up the usage of the last 30 days

X::PostUsage.current!(client: client, days: 30).project_usage

Parameters:

  • client (Object) —

    the client used to make the request

  • params (Hash) —

    query parameters, such as days, the number of days to report, which is 7 by default

Returns:

Raises:



70
71
72
73
# File 'x-objects/lib/x/objects/post_usage.rb', line 70

def self.current!(client:, **params)
  problems = [] #: Array[Problem]
  current(client:, **params) { |problem| problems << problem } || raise(MissingResource.new("#{ENDPOINT} returned no usage", problems:))
end

Instance Method Details

#cap_reset_day ⇒ Integer?

The day of the month the billing cycle, and so the usage, starts over

It is an Integer whether the response holds it as a number or as a String, as it holds the other counts.

Examples:

Get the reset day

usage.cap_reset_day # => 16

Returns:

  • (Integer, nil) —

    the day of the month

Raises:



121
# File 'x-objects/lib/x/objects/post_usage.rb', line 121

def cap_reset_day = integer("cap_reset_day", attrs["cap_reset_day"])

#daily ⇒ Hash{Time => Integer}

The number of posts the project read each day

Examples:

Get the posts read yesterday

usage.daily.values.last(2).first

Returns:

  • (Hash{Time => Integer}) —

    the number of posts, keyed by the start of each day

Raises:

  • (InvalidAttribute) —

    if the response holds a day without a date in ISO 8601, or a number that is not one, or holds the days as something other than a list of objects



131
# File 'x-objects/lib/x/objects/post_usage.rb', line 131

def daily = days("daily", Shape.dig("#{self.class}#daily", attrs, %w[daily_project_usage usage]))

#daily_by_app ⇒ Hash{Integer, nil => Hash{Time => Integer}}

The number of posts each of the project's apps read each day

Examples:

Total the posts each app read

usage.daily_by_app.transform_values { |days| days.values.sum }

Returns:

  • (Hash{Integer, nil => Hash{Time => Integer}}) —

    the daily usage, keyed by the identifier of each app

Raises:

  • (InvalidAttribute) —

    if the response holds an app identifier that is not a number, a day without a date in ISO 8601, or a number that is not one, or holds the apps or their days as something other than a list of objects



141
142
143
144
145
146
147
# File 'x-objects/lib/x/objects/post_usage.rb', line 141

def daily_by_app
  by_app = {} #: Hash[Integer?, Hash[Time, Integer]]
  Shape.objects("#{self.class}#daily_by_app", attrs["daily_client_app_usage"]).each do |app|
    by_app[integer("daily_by_app", app["client_app_id"])] = days("daily_by_app", app["usage"])
  end
  by_app.freeze
end

#deconstruct_keys(keys) ⇒ Hash{Symbol => Object}

Deconstruct the usage into what its readers read, so it matches a hash pattern

Only the readers a pattern names are read, so one that names the counts of the project matches a usage whose days cannot be read.

Examples:

Warn when the project has read nine tenths of its cap

case usage in {project_usage: Integer => used, project_cap: Integer => cap} if used * 10 >= cap * 9 then warn "near the cap"
end

Parameters:

  • keys (Array<Symbol>, nil) —

    the keys the pattern asks for, or nil for every reader

Returns:

  • (Hash{Symbol => Object}) —

    what the readers read

Raises:

  • (InvalidAttribute) —

    if the pattern asks for what the response holds as something that cannot be read



161
# File 'x-objects/lib/x/objects/post_usage.rb', line 161

def deconstruct_keys(keys) = Utils.deconstruct(self, keys, %i[project_id project_usage project_cap cap_reset_day daily daily_by_app])

#project_cap ⇒ Integer?

The number of posts the project may read in a billing cycle

Examples:

Get the cap

usage.project_cap # => 3000000

Returns:

  • (Integer, nil) —

    the cap



110
# File 'x-objects/lib/x/objects/post_usage.rb', line 110

def project_cap = integer("project_cap", attrs["project_cap"])

#project_id ⇒ Integer?

The identifier of the project

Examples:

Get the project identifier

usage.project_id # => 1234567890

Returns:

  • (Integer, nil) —

    the identifier



94
# File 'x-objects/lib/x/objects/post_usage.rb', line 94

def project_id = integer("project_id", attrs["project_id"])

#project_usage ⇒ Integer?

The number of posts the project has read in the current billing cycle

Examples:

Get the posts read this cycle

usage.project_usage # => 1234

Returns:

  • (Integer, nil) —

    the number of posts



102
# File 'x-objects/lib/x/objects/post_usage.rb', line 102

def project_usage = integer("project_usage", attrs["project_usage"])