Module: ShelfWatch::Http

Defined in:
lib/shelfwatch/http.rb

Overview

HTTP helpers and response handling.

Class Method Summary collapse

Class Method Details

.decode_body(raw) ⇒ Object



91
92
93
94
95
96
97
# File 'lib/shelfwatch/http.rb', line 91

def decode_body(raw)
  return nil if raw.nil? || raw == ""

  JSON.parse(raw)
rescue JSON::ParserError
  raw
end

.detail_message(body, fallback) ⇒ Object



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/shelfwatch/http.rb', line 99

def detail_message(body, fallback)
  if body.is_a?(Hash)
    detail = body["detail"]
    if detail.is_a?(String) && !detail.empty?
      return detail
    end
    if detail.is_a?(Array) && !detail.empty?
      parts = detail.map do |item|
        if item.is_a?(Hash)
          msg = item["msg"] || item["message"]
          msg ? msg.to_s : nil
        else
          item.to_s
        end
      end.compact
      return parts.join("; ") unless parts.empty?
    end
    message = body["message"]
    return message if message.is_a?(String) && !message.empty?
  end
  return body if body.is_a?(String) && !body.empty?

  fallback
end

.drop_none(params) ⇒ Object

Omit nil query params so the API receives only set values. Booleans are serialized as "true" / "false" strings.



35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/shelfwatch/http.rb', line 35

def drop_none(params)
  out = {}
  params.each do |key, value|
    next if value.nil?

    out[key] = if value == true || value == false
                 value ? "true" : "false"
               else
                 value
               end
  end
  out
end

.format_date(value) ⇒ Object

Format a date-like value for visit/schedule endpoints. Strings pass through. Time/Date/DateTime → ISO-8601.



60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/shelfwatch/http.rb', line 60

def format_date(value)
  case value
  when String
    value
  when Time
    value.iso8601
  when DateTime
    value.iso8601
  when Date
    value.iso8601
  else
    value.to_s
  end
end

.format_day(value) ⇒ Object

Format a calendar day as YYYY-MM-DD for report endpoints.



76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/shelfwatch/http.rb', line 76

def format_day(value)
  case value
  when String
    value
  when Time
    value.to_date.iso8601
  when DateTime
    value.to_date.iso8601
  when Date
    value.iso8601
  else
    value.to_s
  end
end

.join_csv(value) ⇒ Object

Accept a string or sequence and serialize as comma-separated.



50
51
52
53
54
55
56
# File 'lib/shelfwatch/http.rb', line 50

def join_csv(value)
  return nil if value.nil?
  return value if value.is_a?(String)
  return value.map(&:to_s).join(",") if value.is_a?(Array)

  value.to_s
end

.raise_for_status(status, body, reason_phrase = nil) ⇒ Object

Map HTTP errors to typed SDK exceptions.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/shelfwatch/http.rb', line 12

def raise_for_status(status, body, reason_phrase = nil)
  return if status >= 200 && status < 300

  parsed = decode_body(body)
  fallback = (reason_phrase && !reason_phrase.empty?) ? reason_phrase : "Request failed"
  message = detail_message(parsed, fallback)

  case status
  when 400 then raise ValidationError.new(message, status_code: status, body: parsed)
  when 401 then raise AuthenticationError.new(message, status_code: status, body: parsed)
  when 403 then raise ForbiddenError.new(message, status_code: status, body: parsed)
  when 404 then raise NotFoundError.new(message, status_code: status, body: parsed)
  when 429 then raise RateLimitError.new(message, status_code: status, body: parsed)
  else
    if status >= 500
      raise ServerError.new(message, status_code: status, body: parsed)
    end
    raise Error.new(message, status_code: status, body: parsed)
  end
end