gitlab-labkit 4.3.0 → 4.4.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/lib/labkit/rate_limit/README.md +7 -0
- data/lib/labkit/rate_limit/matcher.rb +28 -7
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f25b6fb5022dbe4d1740df490b991da48f8d2d9432926a0c23ec41dcc566b1c0
|
|
4
|
+
data.tar.gz: 8e1fb5578809e9a87726e95d181fec9426c67c64aa5862e1ab6afd08d4ac5d20
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dad133ba83a3e8e9b60825936befc9af2586bc80685b227c0517553c32681bd89ac42c0c9c2d7d68b3dd5865e2f54672ca4882711f7b71d92f67442af98abbb7
|
|
7
|
+
data.tar.gz: d0c781167401e351f2c0989cbdbb2d761886cc5f27b723d96d99228f33bb1a4f04b52af3669c8c655b512378f37842c09d920628abb9284212bf17a1e57d9b9e
|
|
@@ -174,10 +174,17 @@ A `match` hash gates whether a rule applies. Each value is normalised through
|
|
|
174
174
|
| `Regexp` | `re` | `match: { endpoint: %r{\A/api/} }` |
|
|
175
175
|
| `{ eq: <value> }` | `eq` | `match: { method: { eq: "POST" } }` (YAML-friendly) |
|
|
176
176
|
| `{ re: <source> }`| `re` | `match: { endpoint: { re: '\A/api/' } }` (YAML-friendly) |
|
|
177
|
+
| `{ oneOf: <Array> }` | `oneOf` | `match: { user: { oneOf: ["7", "42"] } }` (YAML-friendly) |
|
|
177
178
|
|
|
178
179
|
`re` coerces the identifier value via `#to_s` before matching, so it can be
|
|
179
180
|
used against non-String values (e.g. matching a 503 status against `{ re: '^5' }`).
|
|
180
181
|
|
|
182
|
+
`oneOf` is set membership via `Set#include?` (`eql?`/`hash` equality, no
|
|
183
|
+
coercion): it agrees with `eq` for Strings, Symbols, booleans, nil, and
|
|
184
|
+
same-class numerics, but cross-type numerics differ (`1 == 1.0`, yet
|
|
185
|
+
`{ oneOf: [1] }` does not match `1.0`). A bare Array is rejected; membership must
|
|
186
|
+
be spelled `{ oneOf: [...] }` explicitly.
|
|
187
|
+
|
|
181
188
|
Glob and prefix matchers are intentionally out of scope.
|
|
182
189
|
|
|
183
190
|
### Evaluation flow
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "set"
|
|
4
|
+
|
|
3
5
|
module Labkit
|
|
4
6
|
module RateLimit
|
|
5
7
|
# Matcher is the internal representation of a single key/value predicate in
|
|
@@ -11,6 +13,14 @@ module Labkit
|
|
|
11
13
|
# - a Regexp instance -> :re matcher (Ruby convenience)
|
|
12
14
|
# - { eq: <value> } -> :eq matcher (canonical, YAML-compatible)
|
|
13
15
|
# - { re: <String|Regexp> } -> :re matcher (canonical, YAML-compatible)
|
|
16
|
+
# - { oneOf: <Array> } -> :oneOf matcher (set membership, YAML-compatible)
|
|
17
|
+
#
|
|
18
|
+
# A bare Array is rejected rather than treated as :oneOf, so a value that
|
|
19
|
+
# was meant as a single matcher hash but arrived as an Array fails loudly
|
|
20
|
+
# instead of silently becoming a membership test.
|
|
21
|
+
#
|
|
22
|
+
# :oneOf membership is Set#include? (eql?/hash equality), not :eq's ==, so
|
|
23
|
+
# cross-type numerics differ (1 == 1.0, but {oneOf: [1]} does not match 1.0).
|
|
14
24
|
#
|
|
15
25
|
# Hash-key naming follows the metrics-catalog selector pattern. Glob,
|
|
16
26
|
# prefix, and other matcher kinds are intentionally out of scope here; see
|
|
@@ -20,7 +30,7 @@ module Labkit
|
|
|
20
30
|
# the regex, so callers can match non-String identifier values such as
|
|
21
31
|
# Integer status codes (e.g. {status: { re: "^5" }} against status: 503).
|
|
22
32
|
class Matcher < Data.define(:type, :value)
|
|
23
|
-
KNOWN_HASH_KEYS = %i[eq re].freeze
|
|
33
|
+
KNOWN_HASH_KEYS = %i[eq re oneOf].freeze
|
|
24
34
|
MAX_REGEX_SOURCE_LENGTH = 200
|
|
25
35
|
ERROR_INSPECT_LIMIT = 80
|
|
26
36
|
|
|
@@ -42,18 +52,14 @@ module Labkit
|
|
|
42
52
|
when Hash
|
|
43
53
|
from_hash(input)
|
|
44
54
|
when Array
|
|
45
|
-
raise ArgumentError,
|
|
46
|
-
"rate-limit match value must be a single-key Hash like {re: \"...\"} or {eq: ...}, got #{truncate_for_error(input)}"
|
|
55
|
+
raise ArgumentError, invalid_shape_error(input)
|
|
47
56
|
else
|
|
48
57
|
new(type: :eq, value: input)
|
|
49
58
|
end
|
|
50
59
|
end
|
|
51
60
|
|
|
52
61
|
def self.from_hash(input)
|
|
53
|
-
if input.size != 1
|
|
54
|
-
raise ArgumentError,
|
|
55
|
-
"rate-limit match value must be a single-key Hash like {re: \"...\"} or {eq: ...}, got #{truncate_for_error(input)}"
|
|
56
|
-
end
|
|
62
|
+
raise ArgumentError, invalid_shape_error(input) if input.size != 1
|
|
57
63
|
|
|
58
64
|
type, source = input.first
|
|
59
65
|
type_sym = type.to_sym
|
|
@@ -71,6 +77,13 @@ module Labkit
|
|
|
71
77
|
case type_sym
|
|
72
78
|
when :eq
|
|
73
79
|
new(type: :eq, value: source)
|
|
80
|
+
when :oneOf
|
|
81
|
+
unless source.is_a?(Array)
|
|
82
|
+
raise ArgumentError,
|
|
83
|
+
"rate-limit match value {oneOf: ...} must be an Array, got #{truncate_for_error(source)}"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
new(type: :oneOf, value: Set.new(source).freeze)
|
|
74
87
|
when :re
|
|
75
88
|
if source.to_s.length > MAX_REGEX_SOURCE_LENGTH
|
|
76
89
|
raise ArgumentError,
|
|
@@ -113,6 +126,12 @@ module Labkit
|
|
|
113
126
|
|
|
114
127
|
private_class_method :with_match_timeout
|
|
115
128
|
|
|
129
|
+
def self.invalid_shape_error(input)
|
|
130
|
+
"rate-limit match value must be a single-key Hash like {re: \"...\"}, {eq: ...} or {oneOf: [...]}, " \
|
|
131
|
+
"got #{truncate_for_error(input)}"
|
|
132
|
+
end
|
|
133
|
+
private_class_method :invalid_shape_error
|
|
134
|
+
|
|
116
135
|
def self.truncate_for_error(value)
|
|
117
136
|
s = value.inspect
|
|
118
137
|
s.length > ERROR_INSPECT_LIMIT ? "#{s[0, ERROR_INSPECT_LIMIT]}...(truncated)" : s
|
|
@@ -123,6 +142,8 @@ module Labkit
|
|
|
123
142
|
case type
|
|
124
143
|
when :eq
|
|
125
144
|
value == identifier_value
|
|
145
|
+
when :oneOf
|
|
146
|
+
value.include?(identifier_value)
|
|
126
147
|
when :re
|
|
127
148
|
value.match?(identifier_value.to_s)
|
|
128
149
|
else
|