Module: ZeroClick::Sellers::Stateful::Contracts

Defined in:
lib/zeroclick/sellers/stateful/contracts.rb

Class Method Summary collapse

Class Method Details

.as_version(value) ⇒ Object

A non-negative integer. JSON 1.0 counts; true and "1" do not.



51
52
53
54
55
56
57
58
59
60
61
# File 'lib/zeroclick/sellers/stateful/contracts.rb', line 51

def as_version(value)
  return nil if value.is_a?(TrueClass) || value.is_a?(FalseClass)
  return value.negative? ? nil : value if value.is_a?(Integer)

  if value.is_a?(Float) && value.finite? && (value % 1).zero?
    version = value.to_i
    return version.negative? ? nil : version
  end

  nil
end

.iso_datetime?(value) ⇒ Boolean

Returns:

  • (Boolean)


42
43
44
# File 'lib/zeroclick/sellers/stateful/contracts.rb', line 42

def iso_datetime?(value)
  value.is_a?(String) && ISO_DATETIME_RE.match?(value)
end

.non_empty_string?(value) ⇒ Boolean

Returns:

  • (Boolean)


46
47
48
# File 'lib/zeroclick/sellers/stateful/contracts.rb', line 46

def non_empty_string?(value)
  value.is_a?(String) && !value.empty?
end

.optional_id(container, key) ⇒ Object

Absent and null both mean "not present"; an empty string is invalid. Returns [value, ok].



65
66
67
68
69
70
71
# File 'lib/zeroclick/sellers/stateful/contracts.rb', line 65

def optional_id(container, key)
  value = container[key]
  return [nil, true] if value.nil?
  return [value, true] if non_empty_string?(value)

  [nil, false]
end

.parse_mint_key_request(body) ⇒ Object



73
74
75
76
77
78
79
80
# File 'lib/zeroclick/sellers/stateful/contracts.rb', line 73

def parse_mint_key_request(body)
  return nil unless body.is_a?(Hash) && non_empty_string?(body["agentId"])

  buyer_id, ok = optional_id(body, "buyerId")
  return nil unless ok

  MintKeyRequest.new(agent_id: body["agentId"], buyer_id: buyer_id)
end

.parse_money_field(container, key, required:, issues:) ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/zeroclick/sellers/stateful/contracts.rb', line 121

def parse_money_field(container, key, required:, issues:)
  unless container.key?(key)
    issues << "state.#{key}: required (may be null)" if required
    return nil
  end

  value = container[key]
  return nil if value.nil?
  return value if Money.money_usd?(value)

  issues << "state.#{key}: expected a six-decimal USD string or null"
  nil
end

.parse_period(value, issues) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/zeroclick/sellers/stateful/contracts.rb', line 101

def parse_period(value, issues)
  unless value.is_a?(Hash)
    issues << "state.period: expected an object"
    return nil
  end

  before = issues.length
  start = value["start"]
  issues << "state.period.start: expected an ISO datetime" unless iso_datetime?(start)
  # Required but nullable: absence is a different mistake from null,
  # and only absence is an error.
  issues << "state.period.end: required (may be null)" unless value.key?("end")
  finish = value["end"]
  issues << "state.period.end: expected an ISO datetime or null" if !finish.nil? && !iso_datetime?(finish)

  return nil if issues.length != before

  AccessPeriod.new(start: start, end: finish)
end

.parse_plan(value, issues) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/zeroclick/sellers/stateful/contracts.rb', line 82

def parse_plan(value, issues)
  unless value.is_a?(Hash)
    issues << "plan: expected an object"
    return nil
  end

  before = issues.length
  %w[slug name billingMode interval].each do |key|
    issues << "plan.#{key}: expected a string" unless value[key].is_a?(String)
  end
  issues << "plan.basePriceUsd: expected a six-decimal USD string" unless Money.money_usd?(value["basePriceUsd"])
  return nil if issues.length != before

  EntitlementPlan.new(
    slug: value["slug"], name: value["name"], billing_mode: value["billingMode"],
    interval: value["interval"], base_price_usd: value["basePriceUsd"]
  )
end