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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +172 -0
- data/README.md +146 -8
- data/lib/basecradle/api_object.rb +24 -4
- data/lib/basecradle/client.rb +41 -23
- data/lib/basecradle/contact_messages.rb +164 -0
- data/lib/basecradle/dashboard.rb +13 -1
- data/lib/basecradle/errors.rb +18 -5
- data/lib/basecradle/headers.rb +164 -0
- data/lib/basecradle/rendering.rb +71 -0
- data/lib/basecradle/serialization.rb +52 -8
- data/lib/basecradle/version.rb +1 -1
- data/lib/basecradle/webhooks.rb +4 -163
- data/lib/basecradle.rb +6 -3
- metadata +4 -1
data/lib/basecradle/webhooks.rb
CHANGED
|
@@ -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
|
|
77
|
-
#
|
|
78
|
-
|
|
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:)+.
|
|
22
|
-
#
|
|
23
|
-
#
|
|
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.
|
|
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
|