basecradle 0.10.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "api_object"
4
+ require_relative "headers"
4
5
  require_relative "items"
5
6
  require_relative "serialization"
6
7
  require_relative "user"
@@ -73,169 +74,9 @@ module BaseCradle
73
74
  end
74
75
  end
75
76
 
76
- # One delivery's request headers: the wire's own spelling, looked up case-insensitively.
77
- #
78
- # A +Hash+ of exactly what the wire carried — one pair per header, +Content-Type+ and
79
- # +Content-Length+ included — so iterating, +keys+, +to_h+, +to_json+ and +==+ all read
80
- # the platform's own spelling and nothing is renamed. Only *lookup* folds case, because
81
- # header names are case-insensitive by RFC and the platform does not preserve the
82
- # sender's casing: it stores names canonicalized to Title-Case per segment
83
- # (+X-Github-Delivery+, not +X-GitHub-Delivery+). So a vendor's published spelling finds
84
- # the header it names, and so does any other casing of it:
85
- #
86
- # event.content.headers["X-GitHub-Delivery"] # GitHub's own published spelling
87
- # event.content.headers["x-github-delivery"] # the lowercase form
88
- # event.content.headers["X-Github-Delivery"] # what the wire actually carried
89
- #
90
- # Every read that takes a header name folds: +fetch+, +dig+, +values_at+, +fetch_values+,
91
- # +to_proc+ and +key?+ (with +has_key?+, +include?+ and +member?+). A header that was
92
- # genuinely not delivered is *absent*, never +nil+ — +[]+ and a fallback-less +fetch+ or
93
- # +fetch_values+ raise +KeyError+ naming the headers that did arrive, while +fetch+ with
94
- # a fallback, +dig+ and +values_at+ answer for it the way +Hash+ does.
95
- #
96
- # The methods that *reshape* the hash rather than read one name — +slice+, +except+,
97
- # +select+, +transform_values+ — and the mutators are left exactly as +Hash+ defines
98
- # them: case-sensitive, on the platform's own spelling. This is a read of one delivery
99
- # that already happened. Converting away from this type gives up the folding too
100
- # (+to_h+, <tt>{**headers}</tt>, and anything else that hands back a plain +Hash+);
101
- # +merge+ and +dup+ keep it.
102
- #
103
- # What it does *not* print is the values: +inspect+, +to_s+ and +pp+ render the header
104
- # names alone, because these are the sender's headers and one of them may be the
105
- # sender's credential. Every read is unaffected — and so is +to_json+, which still
106
- # emits the delivery verbatim. Converting away from this type gives the redaction up
107
- # exactly as it gives up the folding, and for the same reason: +slice+, +except+,
108
- # +select+, +transform_values+ and +to_h+ hand back a plain +Hash+, whose +inspect+
109
- # prints pairs.
110
- class WebhookEventHeaders < Hash
111
- # Hash's own exact-match membership, kept under a private name before the case-folding
112
- # +key?+ below takes the name: a caller who wrote the wire's own spelling is answered
113
- # without a scan, and it is the tiebreak if a derived copy holds two casings of one
114
- # name.
115
- alias_method :exact_key?, :key?
116
- private :exact_key?
117
-
118
- NO_FALLBACK = Object.new.freeze
119
- private_constant :NO_FALLBACK
120
-
121
- # +client:+ is unused — headers carry no verbs — but every wrapped model is built with
122
- # it (see ApiObject.attribute's +wrap:+), so it is accepted.
123
- def initialize(data, client: nil)
124
- super()
125
- update(data)
126
- end
127
-
128
- # The header's value, matched case-insensitively. Raises +KeyError+ when no casing of
129
- # the name was delivered.
130
- def [](name)
131
- wire = wire_name(name)
132
- wire.nil? ? raise_missing(name) : super(wire)
133
- end
134
-
135
- # Hash-like: the value, or the fallback — a default argument, or a block, which is
136
- # handed the name as written — when the header was not delivered. With no fallback it
137
- # behaves like +[]+.
138
- def fetch(name, fallback = NO_FALLBACK, &block)
139
- wire = wire_name(name)
140
- return super(wire) if wire
141
- return yield(name) if block
142
- return fallback unless fallback.equal?(NO_FALLBACK)
143
-
144
- raise_missing(name)
145
- end
146
-
147
- # Did this delivery carry the header, under any casing?
148
- def key?(name)
149
- !wire_name(name).nil?
150
- end
151
- alias_method :has_key?, :key?
152
- alias_method :include?, :key?
153
- alias_method :member?, :key?
154
-
155
- # Ruby's nil-returning read, case folded: the value, or +nil+ for a header that was
156
- # not delivered.
157
- def dig(name, *rest)
158
- wire = wire_name(name)
159
- wire.nil? ? nil : super(wire, *rest)
160
- end
161
-
162
- # Several headers at once, folded the same way — +nil+ for one that was not delivered,
163
- # as +Hash+ answers for a key it has no value for.
164
- def values_at(*names)
165
- names.map { |name| dig(name) }
166
- end
167
-
168
- # As +values_at+, but a header that was not delivered raises, exactly as +fetch+ does.
169
- def fetch_values(*names, &block)
170
- names.map { |name| block ? fetch(name, &block) : fetch(name) }
171
- end
172
-
173
- # +&headers+ — a header name to its value, folded, +nil+ for one not delivered.
174
- def to_proc
175
- ->(name) { dig(name) }
176
- end
177
-
178
- # The header *names* this delivery carried, and none of their values.
179
- #
180
- # +Hash+'s own +inspect+ prints every pair, and these headers are the *sender's*,
181
- # stored verbatim by the platform: a sender that authenticates its POST to an ingest
182
- # URL puts its credential in one of them, so +logger.debug(event.content.headers)+
183
- # would write another party's secret into our logs. Every other renderable object in
184
- # this SDK is already safe by one rule — +ApiObject#inspect+ prints field names, never
185
- # values — and this is the one class that sat outside it, because +Hash+ brought its
186
- # own render.
187
- #
188
- # Only the human-facing render changes. Every *read* is untouched and still
189
- # wire-exact: lookups, +fetch+, +each+, +keys+, +to_h+, +to_json+, +==+.
190
- #
191
- # Names are sorted, as +ApiObject#inspect+ sorts its fields and as the +KeyError+ a
192
- # missing header raises already lists the ones that did arrive.
193
- def inspect
194
- return "#<#{self.class}>" if empty?
195
-
196
- "#<#{self.class} #{keys.sort_by(&:to_s).join(', ')}>"
197
- end
198
-
199
- # +Hash+ aliases +to_s+ to its *own* +inspect+ — one shared method entry — so
200
- # overriding +inspect+ alone would leave <tt>"#{headers}"</tt> and +puts headers+
201
- # printing every value. Late-bound rather than +alias to_s inspect+, so a subclass
202
- # that redacts more is not bypassed by interpolation (as +Client#to_s+ is).
203
- def to_s
204
- inspect
205
- end
206
-
207
- # +pp+ reaches for +pretty_print+, and +Hash+ brings one that prints the pairs — the
208
- # same leak through the one door +inspect+ does not cover. Delegating puts +pp+ back
209
- # on the SDK-wide rule: +pp+ uses an object's own +inspect+ when it defines one, which
210
- # is why every +ApiObject+ is already safe here and only a +Hash+ descendant was not.
211
- def pretty_print(printer)
212
- printer.text(inspect)
213
- end
214
-
215
- private
216
-
217
- # The wire's own spelling of +name+, or nil if no casing of it was delivered. Anything
218
- # but a String is not a header name, so it is never matched rather than being coerced
219
- # into one: +headers[:host]+ finds nothing, and so misses like any other absent name.
220
- # Keys are type-checked too — +merge+ hands back one of these, so a caller's derived
221
- # copy can hold a key that never came off the wire.
222
- def wire_name(name)
223
- return nil unless name.is_a?(String)
224
- return name if exact_key?(name)
225
-
226
- each_key { |wire| return wire if wire.is_a?(String) && wire.casecmp?(name) }
227
- nil
228
- end
229
-
230
- def raise_missing(name)
231
- raise KeyError.new(
232
- "No #{name.inspect} header on this delivery. Header names are matched " \
233
- "case-insensitively, so no casing of it was delivered either. " \
234
- "Headers present: #{keys.sort_by(&:to_s).inspect}",
235
- receiver: self, key: name
236
- )
237
- end
238
- end
77
+ # One delivery's request headers, exactly as the sender sent them — read through
78
+ # RequestHeaders' rules: case-insensitive lookup, a render of names and never values.
79
+ class WebhookEventHeaders < RequestHeaders; end
239
80
 
240
81
  # One inbound delivery: what was sent, and the two facts about the endpoint as it was at
241
82
  # the moment of receipt. Everything in the event's embedded endpoint is *current*; these
data/lib/basecradle.rb CHANGED
@@ -9,6 +9,8 @@ require_relative "basecradle/pagination"
9
9
  require_relative "basecradle/items"
10
10
  require_relative "basecradle/webhooks"
11
11
  require_relative "basecradle/sessions"
12
+ require_relative "basecradle/headers"
13
+ require_relative "basecradle/contact_messages"
12
14
  require_relative "basecradle/timeline"
13
15
  require_relative "basecradle/timelines"
14
16
  require_relative "basecradle/client"
@@ -18,8 +20,9 @@ require_relative "basecradle/client"
18
20
  # same API (https://basecradle.com).
19
21
  #
20
22
  # Start with a client: +BaseCradle::Client.new+ (token from BASECRADLE_TOKEN) or
21
- # +BaseCradle::Client.login(email_address:, password:)+. The self-discovery +me+
22
- # flow, timelines, messages, sessions, and the trust handshake land in subsequent
23
- # releases, mirroring the BaseCradle API.
23
+ # +BaseCradle::Client.login(email_address:, password:)+. From there, +bc.me+ answers
24
+ # "who am I?", and every other surface of the BaseCradle API hangs off the client:
25
+ # timelines and their messages, assets, tasks and webhooks, your own sessions, and the
26
+ # users directory with its trust handshake.
24
27
  module BaseCradle
25
28
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: basecradle
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.2
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Drawk Kwast
@@ -22,10 +22,13 @@ files:
22
22
  - lib/basecradle.rb
23
23
  - lib/basecradle/api_object.rb
24
24
  - lib/basecradle/client.rb
25
+ - lib/basecradle/contact_messages.rb
25
26
  - lib/basecradle/dashboard.rb
26
27
  - lib/basecradle/errors.rb
28
+ - lib/basecradle/headers.rb
27
29
  - lib/basecradle/items.rb
28
30
  - lib/basecradle/pagination.rb
31
+ - lib/basecradle/rendering.rb
29
32
  - lib/basecradle/serialization.rb
30
33
  - lib/basecradle/sessions.rb
31
34
  - lib/basecradle/timeline.rb