oursprivacy-ingest 0.1.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.
Files changed (89) hide show
  1. checksums.yaml +7 -0
  2. data/.ignore +2 -0
  3. data/CHANGELOG.md +16 -0
  4. data/README.md +206 -0
  5. data/SECURITY.md +27 -0
  6. data/lib/oursprivacy_ingest/client.rb +64 -0
  7. data/lib/oursprivacy_ingest/errors.rb +228 -0
  8. data/lib/oursprivacy_ingest/file_part.rb +58 -0
  9. data/lib/oursprivacy_ingest/internal/transport/base_client.rb +573 -0
  10. data/lib/oursprivacy_ingest/internal/transport/pooled_net_requester.rb +204 -0
  11. data/lib/oursprivacy_ingest/internal/type/array_of.rb +168 -0
  12. data/lib/oursprivacy_ingest/internal/type/base_model.rb +534 -0
  13. data/lib/oursprivacy_ingest/internal/type/base_page.rb +55 -0
  14. data/lib/oursprivacy_ingest/internal/type/boolean.rb +77 -0
  15. data/lib/oursprivacy_ingest/internal/type/converter.rb +327 -0
  16. data/lib/oursprivacy_ingest/internal/type/enum.rb +133 -0
  17. data/lib/oursprivacy_ingest/internal/type/file_input.rb +111 -0
  18. data/lib/oursprivacy_ingest/internal/type/hash_of.rb +188 -0
  19. data/lib/oursprivacy_ingest/internal/type/request_parameters.rb +42 -0
  20. data/lib/oursprivacy_ingest/internal/type/union.rb +245 -0
  21. data/lib/oursprivacy_ingest/internal/type/unknown.rb +81 -0
  22. data/lib/oursprivacy_ingest/internal/util.rb +915 -0
  23. data/lib/oursprivacy_ingest/internal.rb +20 -0
  24. data/lib/oursprivacy_ingest/models/track_event_params.rb +1009 -0
  25. data/lib/oursprivacy_ingest/models/track_event_response.rb +26 -0
  26. data/lib/oursprivacy_ingest/models/visitor_upsert_params.rb +972 -0
  27. data/lib/oursprivacy_ingest/models/visitor_upsert_response.rb +26 -0
  28. data/lib/oursprivacy_ingest/models.rb +48 -0
  29. data/lib/oursprivacy_ingest/request_options.rb +79 -0
  30. data/lib/oursprivacy_ingest/resources/track.rb +61 -0
  31. data/lib/oursprivacy_ingest/resources/visitor.rb +52 -0
  32. data/lib/oursprivacy_ingest/version.rb +5 -0
  33. data/lib/oursprivacy_ingest.rb +59 -0
  34. data/manifest.yaml +15 -0
  35. data/rbi/oursprivacy_ingest/client.rbi +46 -0
  36. data/rbi/oursprivacy_ingest/errors.rbi +205 -0
  37. data/rbi/oursprivacy_ingest/file_part.rbi +37 -0
  38. data/rbi/oursprivacy_ingest/internal/transport/base_client.rbi +305 -0
  39. data/rbi/oursprivacy_ingest/internal/transport/pooled_net_requester.rbi +80 -0
  40. data/rbi/oursprivacy_ingest/internal/type/array_of.rbi +108 -0
  41. data/rbi/oursprivacy_ingest/internal/type/base_model.rbi +316 -0
  42. data/rbi/oursprivacy_ingest/internal/type/base_page.rbi +43 -0
  43. data/rbi/oursprivacy_ingest/internal/type/boolean.rbi +58 -0
  44. data/rbi/oursprivacy_ingest/internal/type/converter.rbi +225 -0
  45. data/rbi/oursprivacy_ingest/internal/type/enum.rbi +82 -0
  46. data/rbi/oursprivacy_ingest/internal/type/file_input.rbi +59 -0
  47. data/rbi/oursprivacy_ingest/internal/type/hash_of.rbi +108 -0
  48. data/rbi/oursprivacy_ingest/internal/type/request_parameters.rbi +33 -0
  49. data/rbi/oursprivacy_ingest/internal/type/union.rbi +134 -0
  50. data/rbi/oursprivacy_ingest/internal/type/unknown.rbi +58 -0
  51. data/rbi/oursprivacy_ingest/internal/util.rbi +487 -0
  52. data/rbi/oursprivacy_ingest/internal.rbi +18 -0
  53. data/rbi/oursprivacy_ingest/models/track_event_params.rbi +1072 -0
  54. data/rbi/oursprivacy_ingest/models/track_event_response.rbi +71 -0
  55. data/rbi/oursprivacy_ingest/models/visitor_upsert_params.rbi +1035 -0
  56. data/rbi/oursprivacy_ingest/models/visitor_upsert_response.rbi +71 -0
  57. data/rbi/oursprivacy_ingest/models.rbi +7 -0
  58. data/rbi/oursprivacy_ingest/request_options.rbi +64 -0
  59. data/rbi/oursprivacy_ingest/resources/track.rbi +74 -0
  60. data/rbi/oursprivacy_ingest/resources/visitor.rbi +58 -0
  61. data/rbi/oursprivacy_ingest/version.rbi +5 -0
  62. data/sig/oursprivacy_ingest/client.rbs +25 -0
  63. data/sig/oursprivacy_ingest/errors.rbs +117 -0
  64. data/sig/oursprivacy_ingest/file_part.rbs +21 -0
  65. data/sig/oursprivacy_ingest/internal/transport/base_client.rbs +131 -0
  66. data/sig/oursprivacy_ingest/internal/transport/pooled_net_requester.rbs +45 -0
  67. data/sig/oursprivacy_ingest/internal/type/array_of.rbs +48 -0
  68. data/sig/oursprivacy_ingest/internal/type/base_model.rbs +106 -0
  69. data/sig/oursprivacy_ingest/internal/type/base_page.rbs +24 -0
  70. data/sig/oursprivacy_ingest/internal/type/boolean.rbs +26 -0
  71. data/sig/oursprivacy_ingest/internal/type/converter.rbs +79 -0
  72. data/sig/oursprivacy_ingest/internal/type/enum.rbs +32 -0
  73. data/sig/oursprivacy_ingest/internal/type/file_input.rbs +25 -0
  74. data/sig/oursprivacy_ingest/internal/type/hash_of.rbs +48 -0
  75. data/sig/oursprivacy_ingest/internal/type/request_parameters.rbs +20 -0
  76. data/sig/oursprivacy_ingest/internal/type/union.rbs +52 -0
  77. data/sig/oursprivacy_ingest/internal/type/unknown.rbs +26 -0
  78. data/sig/oursprivacy_ingest/internal/util.rbs +185 -0
  79. data/sig/oursprivacy_ingest/internal.rbs +10 -0
  80. data/sig/oursprivacy_ingest/models/track_event_params.rbs +673 -0
  81. data/sig/oursprivacy_ingest/models/track_event_response.rbs +28 -0
  82. data/sig/oursprivacy_ingest/models/visitor_upsert_params.rbs +653 -0
  83. data/sig/oursprivacy_ingest/models/visitor_upsert_response.rbs +28 -0
  84. data/sig/oursprivacy_ingest/models.rbs +5 -0
  85. data/sig/oursprivacy_ingest/request_options.rbs +36 -0
  86. data/sig/oursprivacy_ingest/resources/track.rbs +21 -0
  87. data/sig/oursprivacy_ingest/resources/visitor.rbs +17 -0
  88. data/sig/oursprivacy_ingest/version.rbs +3 -0
  89. metadata +146 -0
@@ -0,0 +1,204 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OursprivacyIngest
4
+ module Internal
5
+ module Transport
6
+ # @api private
7
+ class PooledNetRequester
8
+ extend OursprivacyIngest::Internal::Util::SorbetRuntimeSupport
9
+
10
+ # from the golang stdlib
11
+ # https://github.com/golang/go/blob/c8eced8580028328fde7c03cbfcb720ce15b2358/src/net/http/transport.go#L49
12
+ KEEP_ALIVE_TIMEOUT = 30
13
+
14
+ DEFAULT_MAX_CONNECTIONS = [Etc.nprocessors, 99].max
15
+
16
+ class << self
17
+ # @api private
18
+ #
19
+ # @param url [URI::Generic]
20
+ #
21
+ # @return [Net::HTTP]
22
+ def connect(url)
23
+ port =
24
+ case [url.port, url.scheme]
25
+ in [Integer, _]
26
+ url.port
27
+ in [nil, "http" | "ws"]
28
+ Net::HTTP.http_default_port
29
+ in [nil, "https" | "wss"]
30
+ Net::HTTP.https_default_port
31
+ end
32
+
33
+ Net::HTTP.new(url.host, port).tap do
34
+ _1.use_ssl = %w[https wss].include?(url.scheme)
35
+ _1.max_retries = 0
36
+ end
37
+ end
38
+
39
+ # @api private
40
+ #
41
+ # @param conn [Net::HTTP]
42
+ # @param deadline [Float]
43
+ def calibrate_socket_timeout(conn, deadline)
44
+ timeout = deadline - OursprivacyIngest::Internal::Util.monotonic_secs
45
+ conn.open_timeout = conn.read_timeout = conn.write_timeout = conn.continue_timeout = timeout
46
+ end
47
+
48
+ # @api private
49
+ #
50
+ # @param request [Hash{Symbol=>Object}] .
51
+ #
52
+ # @option request [Symbol] :method
53
+ #
54
+ # @option request [URI::Generic] :url
55
+ #
56
+ # @option request [Hash{String=>String}] :headers
57
+ #
58
+ # @param blk [Proc]
59
+ #
60
+ # @yieldparam [String]
61
+ # @return [Array(Net::HTTPGenericRequest, Proc)]
62
+ def build_request(request, &blk)
63
+ method, url, headers, body = request.fetch_values(:method, :url, :headers, :body)
64
+ req = Net::HTTPGenericRequest.new(
65
+ method.to_s.upcase,
66
+ !body.nil?,
67
+ method != :head,
68
+ URI(url.to_s) # ensure we construct a URI class of the right scheme
69
+ )
70
+
71
+ headers.each { req[_1] = _2 }
72
+
73
+ case body
74
+ in nil
75
+ nil
76
+ in String
77
+ req["content-length"] ||= body.bytesize.to_s unless req["transfer-encoding"]
78
+ req.body_stream = OursprivacyIngest::Internal::Util::ReadIOAdapter.new(body, &blk)
79
+ in StringIO
80
+ req["content-length"] ||= body.size.to_s unless req["transfer-encoding"]
81
+ req.body_stream = OursprivacyIngest::Internal::Util::ReadIOAdapter.new(body, &blk)
82
+ in Pathname | IO | Enumerator
83
+ req["transfer-encoding"] ||= "chunked" unless req["content-length"]
84
+ req.body_stream = OursprivacyIngest::Internal::Util::ReadIOAdapter.new(body, &blk)
85
+ end
86
+
87
+ [req, req.body_stream&.method(:close)]
88
+ end
89
+ end
90
+
91
+ # @api private
92
+ #
93
+ # @param url [URI::Generic]
94
+ # @param deadline [Float]
95
+ # @param blk [Proc]
96
+ #
97
+ # @raise [Timeout::Error]
98
+ # @yieldparam [Net::HTTP]
99
+ private def with_pool(url, deadline:, &blk)
100
+ origin = OursprivacyIngest::Internal::Util.uri_origin(url)
101
+ timeout = deadline - OursprivacyIngest::Internal::Util.monotonic_secs
102
+ pool =
103
+ @mutex.synchronize do
104
+ @pools[origin] ||= ConnectionPool.new(size: @size) do
105
+ self.class.connect(url)
106
+ end
107
+ end
108
+
109
+ pool.with(timeout: timeout, &blk)
110
+ end
111
+
112
+ # @api private
113
+ #
114
+ # @param request [Hash{Symbol=>Object}] .
115
+ #
116
+ # @option request [Symbol] :method
117
+ #
118
+ # @option request [URI::Generic] :url
119
+ #
120
+ # @option request [Hash{String=>String}] :headers
121
+ #
122
+ # @option request [Object] :body
123
+ #
124
+ # @option request [Float] :deadline
125
+ #
126
+ # @return [Array(Integer, Net::HTTPResponse, Enumerable<String>)]
127
+ def execute(request)
128
+ url, deadline = request.fetch_values(:url, :deadline)
129
+
130
+ req = nil
131
+ finished = false
132
+
133
+ # rubocop:disable Metrics/BlockLength
134
+ enum = Enumerator.new do |y|
135
+ next if finished
136
+
137
+ with_pool(url, deadline: deadline) do |conn|
138
+ eof = false
139
+ closing = nil
140
+ ::Thread.handle_interrupt(Object => :never) do
141
+ ::Thread.handle_interrupt(Object => :immediate) do
142
+ req, closing = self.class.build_request(request) do
143
+ self.class.calibrate_socket_timeout(conn, deadline)
144
+ end
145
+
146
+ self.class.calibrate_socket_timeout(conn, deadline)
147
+ unless conn.started?
148
+ conn.keep_alive_timeout = self.class::KEEP_ALIVE_TIMEOUT
149
+ conn.start
150
+ end
151
+
152
+ self.class.calibrate_socket_timeout(conn, deadline)
153
+ conn.request(req) do |rsp|
154
+ y << [req, rsp]
155
+ break if finished
156
+
157
+ rsp.read_body do |bytes|
158
+ y << bytes.force_encoding(Encoding::BINARY)
159
+ break if finished
160
+
161
+ self.class.calibrate_socket_timeout(conn, deadline)
162
+ end
163
+ eof = true
164
+ end
165
+ end
166
+ ensure
167
+ begin
168
+ conn.finish if !eof && conn&.started?
169
+ ensure
170
+ closing&.call
171
+ end
172
+ end
173
+ end
174
+ rescue Timeout::Error
175
+ raise OursprivacyIngest::Errors::APITimeoutError.new(url: url, request: req)
176
+ rescue StandardError
177
+ raise OursprivacyIngest::Errors::APIConnectionError.new(url: url, request: req)
178
+ end
179
+ # rubocop:enable Metrics/BlockLength
180
+
181
+ _, response = enum.next
182
+ body = OursprivacyIngest::Internal::Util.fused_enum(enum, external: true) do
183
+ finished = true
184
+ loop { enum.next }
185
+ end
186
+ [Integer(response.code), response, body]
187
+ end
188
+
189
+ # @api private
190
+ #
191
+ # @param size [Integer]
192
+ def initialize(size: self.class::DEFAULT_MAX_CONNECTIONS)
193
+ @mutex = Mutex.new
194
+ @size = size
195
+ @pools = {}
196
+ end
197
+
198
+ define_sorbet_constant!(:Request) do
199
+ T.type_alias { {method: Symbol, url: URI::Generic, headers: T::Hash[String, String], body: T.anything, deadline: Float} }
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,168 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OursprivacyIngest
4
+ module Internal
5
+ module Type
6
+ # @api private
7
+ #
8
+ # @abstract
9
+ #
10
+ # @generic Elem
11
+ #
12
+ # Array of items of a given type.
13
+ class ArrayOf
14
+ include OursprivacyIngest::Internal::Type::Converter
15
+ include OursprivacyIngest::Internal::Util::SorbetRuntimeSupport
16
+
17
+ private_class_method :new
18
+
19
+ # @overload [](type_info, spec = {})
20
+ #
21
+ # @param type_info [Hash{Symbol=>Object}, Proc, OursprivacyIngest::Internal::Type::Converter, Class]
22
+ #
23
+ # @param spec [Hash{Symbol=>Object}] .
24
+ #
25
+ # @option spec [NilClass, TrueClass, FalseClass, Integer, Float, Symbol] :const
26
+ #
27
+ # @option spec [Proc] :enum
28
+ #
29
+ # @option spec [Proc] :union
30
+ #
31
+ # @option spec [Boolean] :"nil?"
32
+ #
33
+ # @return [self]
34
+ def self.[](...) = new(...)
35
+
36
+ # @api public
37
+ #
38
+ # @param other [Object]
39
+ #
40
+ # @return [Boolean]
41
+ def ===(other) = other.is_a?(Array) && other.all?(item_type)
42
+
43
+ # @api public
44
+ #
45
+ # @param other [Object]
46
+ #
47
+ # @return [Boolean]
48
+ def ==(other)
49
+ # rubocop:disable Layout/LineLength
50
+ other.is_a?(OursprivacyIngest::Internal::Type::ArrayOf) && other.nilable? == nilable? && other.item_type == item_type
51
+ # rubocop:enable Layout/LineLength
52
+ end
53
+
54
+ # @api public
55
+ #
56
+ # @return [Integer]
57
+ def hash = [self.class, item_type].hash
58
+
59
+ # @api private
60
+ #
61
+ # @param value [Array<Object>, Object]
62
+ #
63
+ # @param state [Hash{Symbol=>Object}] .
64
+ #
65
+ # @option state [Boolean] :translate_names
66
+ #
67
+ # @option state [Boolean] :strictness
68
+ #
69
+ # @option state [Hash{Symbol=>Object}] :exactness
70
+ #
71
+ # @option state [Class<StandardError>] :error
72
+ #
73
+ # @option state [Integer] :branched
74
+ #
75
+ # @return [Array<Object>, Object]
76
+ def coerce(value, state:)
77
+ exactness = state.fetch(:exactness)
78
+
79
+ unless value.is_a?(Array)
80
+ exactness[:no] += 1
81
+ state[:error] = TypeError.new("#{value.class} can't be coerced into #{Array}")
82
+ return value
83
+ end
84
+
85
+ target = item_type
86
+ exactness[:yes] += 1
87
+ value
88
+ .map do |item|
89
+ case [nilable?, item]
90
+ in [true, nil]
91
+ exactness[:yes] += 1
92
+ nil
93
+ else
94
+ OursprivacyIngest::Internal::Type::Converter.coerce(target, item, state: state)
95
+ end
96
+ end
97
+ end
98
+
99
+ # @api private
100
+ #
101
+ # @param value [Array<Object>, Object]
102
+ #
103
+ # @param state [Hash{Symbol=>Object}] .
104
+ #
105
+ # @option state [Boolean] :can_retry
106
+ #
107
+ # @return [Array<Object>, Object]
108
+ def dump(value, state:)
109
+ target = item_type
110
+ if value.is_a?(Array)
111
+ value.map do
112
+ OursprivacyIngest::Internal::Type::Converter.dump(target, _1, state: state)
113
+ end
114
+ else
115
+ super
116
+ end
117
+ end
118
+
119
+ # @api private
120
+ #
121
+ # @return [Object]
122
+ def to_sorbet_type
123
+ T::Array[OursprivacyIngest::Internal::Util::SorbetRuntimeSupport.to_sorbet_type(item_type)]
124
+ end
125
+
126
+ # @api private
127
+ #
128
+ # @return [generic<Elem>]
129
+ protected def item_type = @item_type_fn.call
130
+
131
+ # @api private
132
+ #
133
+ # @return [Boolean]
134
+ protected def nilable? = @nilable
135
+
136
+ # @api private
137
+ #
138
+ # @param type_info [Hash{Symbol=>Object}, Proc, OursprivacyIngest::Internal::Type::Converter, Class]
139
+ #
140
+ # @param spec [Hash{Symbol=>Object}] .
141
+ #
142
+ # @option spec [NilClass, TrueClass, FalseClass, Integer, Float, Symbol] :const
143
+ #
144
+ # @option spec [Proc] :enum
145
+ #
146
+ # @option spec [Proc] :union
147
+ #
148
+ # @option spec [Boolean] :"nil?"
149
+ def initialize(type_info, spec = {})
150
+ @item_type_fn = OursprivacyIngest::Internal::Type::Converter.type_info(type_info || spec)
151
+ @meta = OursprivacyIngest::Internal::Type::Converter.meta_info(type_info, spec)
152
+ @nilable = spec.fetch(:nil?, false)
153
+ end
154
+
155
+ # @api private
156
+ #
157
+ # @param depth [Integer]
158
+ #
159
+ # @return [String]
160
+ def inspect(depth: 0)
161
+ items = OursprivacyIngest::Internal::Type::Converter.inspect(item_type, depth: depth.succ)
162
+
163
+ "#{self.class}[#{[items, nilable? ? 'nil' : nil].compact.join(' | ')}]"
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end