featurehub-sdk 1.0.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.
@@ -0,0 +1,227 @@
1
+ # frozen_string_literal: true
2
+
3
+ # these are bulk copied in from OpenAPI and left in original case
4
+ module FeatureHub
5
+ module Sdk
6
+ # rubocop:disable Naming/ConstantName
7
+ # rubocop:disable Metrics/ModuleLength
8
+ module StrategyAttributeCountryName
9
+ Afghanistan = "afghanistan"
10
+ Albania = "albania"
11
+ Algeria = "algeria"
12
+ Andorra = "andorra"
13
+ Angola = "angola"
14
+ AntiguaAndBarbuda = "antigua_and_barbuda"
15
+ Argentina = "argentina"
16
+ Armenia = "armenia"
17
+ Australia = "australia"
18
+ Austria = "austria"
19
+ Azerbaijan = "azerbaijan"
20
+ TheBahamas = "the_bahamas"
21
+ Bahrain = "bahrain"
22
+ Bangladesh = "bangladesh"
23
+ Barbados = "barbados"
24
+ Belarus = "belarus"
25
+ Belgium = "belgium"
26
+ Belize = "belize"
27
+ Benin = "benin"
28
+ Bhutan = "bhutan"
29
+ Bolivia = "bolivia"
30
+ BosniaAndHerzegovina = "bosnia_and_herzegovina"
31
+ Botswana = "botswana"
32
+ Brazil = "brazil"
33
+ Brunei = "brunei"
34
+ Bulgaria = "bulgaria"
35
+ BurkinaFaso = "burkina_faso"
36
+ Burundi = "burundi"
37
+ CaboVerde = "cabo_verde"
38
+ Cambodia = "cambodia"
39
+ Cameroon = "cameroon"
40
+ Canada = "canada"
41
+ CentralAfricanRepublic = "central_african_republic"
42
+ Chad = "chad"
43
+ Chile = "chile"
44
+ China = "china"
45
+ Colombia = "colombia"
46
+ Comoros = "comoros"
47
+ CongoDemocraticRepublicOfThe = "congo_democratic_republic_of_the"
48
+ CongoRepublicOfThe = "congo_republic_of_the"
49
+ CostaRica = "costa_rica"
50
+ CoteDivoire = "cote_divoire"
51
+ Croatia = "croatia"
52
+ Cuba = "cuba"
53
+ Cyprus = "cyprus"
54
+ CzechRepublic = "czech_republic"
55
+ Denmark = "denmark"
56
+ Djibouti = "djibouti"
57
+ Dominica = "dominica"
58
+ DominicanRepublic = "dominican_republic"
59
+ EastTimor = "east_timor"
60
+ Ecuador = "ecuador"
61
+ Egypt = "egypt"
62
+ ElSalvador = "el_salvador"
63
+ EquatorialGuinea = "equatorial_guinea"
64
+ Eritrea = "eritrea"
65
+ Estonia = "estonia"
66
+ Eswatini = "eswatini"
67
+ Ethiopia = "ethiopia"
68
+ Fiji = "fiji"
69
+ Finland = "finland"
70
+ France = "france"
71
+ Gabon = "gabon"
72
+ TheGambia = "the_gambia"
73
+ Georgia = "georgia"
74
+ Germany = "germany"
75
+ Ghana = "ghana"
76
+ Greece = "greece"
77
+ Grenada = "grenada"
78
+ Guatemala = "guatemala"
79
+ Guinea = "guinea"
80
+ GuineaBissau = "guinea_bissau"
81
+ Guyana = "guyana"
82
+ Haiti = "haiti"
83
+ Honduras = "honduras"
84
+ Hungary = "hungary"
85
+ Iceland = "iceland"
86
+ India = "india"
87
+ Indonesia = "indonesia"
88
+ Iran = "iran"
89
+ Iraq = "iraq"
90
+ Ireland = "ireland"
91
+ Israel = "israel"
92
+ Italy = "italy"
93
+ Jamaica = "jamaica"
94
+ Japan = "japan"
95
+ Jordan = "jordan"
96
+ Kazakhstan = "kazakhstan"
97
+ Kenya = "kenya"
98
+ Kiribati = "kiribati"
99
+ KoreaNorth = "korea_north"
100
+ KoreaSouth = "korea_south"
101
+ Kosovo = "kosovo"
102
+ Kuwait = "kuwait"
103
+ Kyrgyzstan = "kyrgyzstan"
104
+ Laos = "laos"
105
+ Latvia = "latvia"
106
+ Lebanon = "lebanon"
107
+ Lesotho = "lesotho"
108
+ Liberia = "liberia"
109
+ Libya = "libya"
110
+ Liechtenstein = "liechtenstein"
111
+ Lithuania = "lithuania"
112
+ Luxembourg = "luxembourg"
113
+ Madagascar = "madagascar"
114
+ Malawi = "malawi"
115
+ Malaysia = "malaysia"
116
+ Maldives = "maldives"
117
+ Mali = "mali"
118
+ Malta = "malta"
119
+ MarshallIslands = "marshall_islands"
120
+ Mauritania = "mauritania"
121
+ Mauritius = "mauritius"
122
+ Mexico = "mexico"
123
+ MicronesiaFederatedStatesOf = "micronesia_federated_states_of"
124
+ Moldova = "moldova"
125
+ Monaco = "monaco"
126
+ Mongolia = "mongolia"
127
+ Montenegro = "montenegro"
128
+ Morocco = "morocco"
129
+ Mozambique = "mozambique"
130
+ Myanmar = "myanmar"
131
+ Namibia = "namibia"
132
+ Nauru = "nauru"
133
+ Nepal = "nepal"
134
+ Netherlands = "netherlands"
135
+ NewZealand = "new_zealand"
136
+ Nicaragua = "nicaragua"
137
+ Niger = "niger"
138
+ Nigeria = "nigeria"
139
+ NorthMacedonia = "north_macedonia"
140
+ Norway = "norway"
141
+ Oman = "oman"
142
+ Pakistan = "pakistan"
143
+ Palau = "palau"
144
+ Panama = "panama"
145
+ PapuaNewGuinea = "papua_new_guinea"
146
+ Paraguay = "paraguay"
147
+ Peru = "peru"
148
+ Philippines = "philippines"
149
+ Poland = "poland"
150
+ Portugal = "portugal"
151
+ Qatar = "qatar"
152
+ Romania = "romania"
153
+ Russia = "russia"
154
+ Rwanda = "rwanda"
155
+ SaintKittsAndNevis = "saint_kitts_and_nevis"
156
+ SaintLucia = "saint_lucia"
157
+ SaintVincentAndTheGrenadines = "saint_vincent_and_the_grenadines"
158
+ Samoa = "samoa"
159
+ SanMarino = "san_marino"
160
+ SaoTomeAndPrincipe = "sao_tome_and_principe"
161
+ SaudiArabia = "saudi_arabia"
162
+ Senegal = "senegal"
163
+ Serbia = "serbia"
164
+ Seychelles = "seychelles"
165
+ SierraLeone = "sierra_leone"
166
+ Singapore = "singapore"
167
+ Slovakia = "slovakia"
168
+ Slovenia = "slovenia"
169
+ SolomonIslands = "solomon_islands"
170
+ Somalia = "somalia"
171
+ SouthAfrica = "south_africa"
172
+ Spain = "spain"
173
+ SriLanka = "sri_lanka"
174
+ Sudan = "sudan"
175
+ SudanSouth = "sudan_south"
176
+ Suriname = "suriname"
177
+ Sweden = "sweden"
178
+ Switzerland = "switzerland"
179
+ Syria = "syria"
180
+ Taiwan = "taiwan"
181
+ Tajikistan = "tajikistan"
182
+ Tanzania = "tanzania"
183
+ Thailand = "thailand"
184
+ Togo = "togo"
185
+ Tonga = "tonga"
186
+ TrinidadAndTobago = "trinidad_and_tobago"
187
+ Tunisia = "tunisia"
188
+ Turkey = "turkey"
189
+ Turkmenistan = "turkmenistan"
190
+ Tuvalu = "tuvalu"
191
+ Uganda = "uganda"
192
+ Ukraine = "ukraine"
193
+ UnitedArabEmirates = "united_arab_emirates"
194
+ UnitedKingdom = "united_kingdom"
195
+ UnitedStates = "united_states"
196
+ Uruguay = "uruguay"
197
+ Uzbekistan = "uzbekistan"
198
+ Vanuatu = "vanuatu"
199
+ VaticanCity = "vatican_city"
200
+ Venezuela = "venezuela"
201
+ Vietnam = "vietnam"
202
+ Yemen = "yemen"
203
+ Zambia = "zambia"
204
+ Zimbabwe = "zimbabwe"
205
+ end
206
+
207
+ module StrategyAttributeDeviceName
208
+ Browser = "browser"
209
+ Mobile = "mobile"
210
+ Desktop = "desktop"
211
+ Server = "server"
212
+ Watch = "watch"
213
+ Embedded = "embedded"
214
+ end
215
+
216
+ module StrategyAttributePlatformName
217
+ Linux = "linux"
218
+ Windows = "windows"
219
+ Macos = "macos"
220
+ Android = "android"
221
+ Ios = "ios"
222
+ end
223
+
224
+ # rubocop:enable Metrics/ModuleLength
225
+ # rubocop:enable Naming/ConstantName
226
+ end
227
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ld-eventsource"
4
+ require "json"
5
+
6
+ module FeatureHub
7
+ module Sdk
8
+ # provides a streaming service
9
+ class StreamingEdgeService < FeatureHub::Sdk::EdgeService
10
+ attr_reader :repository, :sse_client, :url, :closed
11
+
12
+ def initialize(repository, api_keys, edge_url, logger = nil)
13
+ super(repository, api_keys, edge_url)
14
+
15
+ @url = "#{edge_url}features/#{api_keys[0]}"
16
+ @repository = repository
17
+ @sse_client = nil
18
+ @closed = true
19
+ @logger = logger || FeatureHub::Sdk.default_logger
20
+ end
21
+
22
+ def poll
23
+ start_streaming unless @sse_client
24
+ end
25
+
26
+ def active
27
+ !@closed && !@sse_client.nil?
28
+ end
29
+
30
+ def close
31
+ @closed = true
32
+ return if @sse_client.nil?
33
+
34
+ @sse_client.close
35
+ @sse_client = nil
36
+ end
37
+
38
+ private
39
+
40
+ def start_streaming
41
+ @closed = false
42
+ @logger.info("streaming from #{@url}")
43
+ @sse_client = SSE::Client.new(@url) do |client|
44
+ client.on_event do |event|
45
+ @repository.notify(event.type, JSON.parse(event.data))
46
+ end
47
+ client.on_error do |error|
48
+ if error.is_a?(SSE::Errors::HTTPStatusError) && (error.status == 404)
49
+ @repository.notify("failure", nil)
50
+ close
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FeatureHub
4
+ # already documented elsewhere
5
+ module Sdk
6
+ VERSION = "1.0.0"
7
+
8
+ def default_logger
9
+ log = ::Logger.new($stdout)
10
+ log.level = ::Logger::WARN
11
+ log.progname = "featurehub-sdk"
12
+ log
13
+ end
14
+
15
+ module_function :default_logger
16
+ end
17
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "feature_hub/sdk/impl/rollout_holders"
4
+ require_relative "feature_hub/sdk/impl/apply_features"
5
+ require_relative "feature_hub/sdk/percentage_calc"
6
+ require_relative "feature_hub/sdk/impl/murmur3_percentage"
7
+ require_relative "feature_hub/sdk/version"
8
+ require_relative "feature_hub/sdk/context"
9
+ require_relative "feature_hub/sdk/feature_hub_config"
10
+ require_relative "feature_hub/sdk/internal_feature_repository"
11
+ require_relative "feature_hub/sdk/feature_repository"
12
+ require_relative "feature_hub/sdk/feature_state"
13
+ require_relative "feature_hub/sdk/interceptors"
14
+ require_relative "feature_hub/sdk/strategy_attributes"
15
+ require_relative "feature_hub/sdk/impl/strategy_wrappers"
16
+ require_relative "feature_hub/sdk/poll_edge_service"
17
+ require_relative "feature_hub/sdk/streaming_edge_service"
18
+
19
+ # the FeatureHub SDK namespace
20
+ module FeatureHub
21
+ # the main SDK
22
+
23
+ module Sdk
24
+ # classes inside here are intended to be internal details, do not rely on them
25
+ module Impl
26
+ end
27
+
28
+ class Error < StandardError; end
29
+ end
30
+ end
@@ -0,0 +1,256 @@
1
+ module FeatureHub
2
+ module Sdk
3
+ VERSION: String
4
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
5
+ #
6
+
7
+ class FeatureState
8
+ attr_reader key: Symbol
9
+
10
+ def initialize: (key: String, repo: InternalFeatureRepository,
11
+ feature_state: Hash[untyped, untyped]?, parent_state: FeatureState?, ctx: ClientContext?) -> void
12
+
13
+ def locked?: -> bool
14
+ def exists?: (top_feature: FeatureState?) -> bool
15
+ def id: -> String?
16
+ def feature_type: -> String?
17
+
18
+ def with_context: (ctx: ClientContext) -> FeatureState
19
+
20
+ def update_feature_state: (feature_state: Hash[untyped, untyped]) -> void
21
+ # this is the feature state of the top level, it always walks up
22
+ def feature_state: () -> Hash[untyped, untyped]
23
+
24
+ # this is strictly the internal feature state, further down from the top level it will be nil
25
+ def internal_feature_state: () -> Hash[untyped, untyped]
26
+
27
+ def value: () -> [bool? | String? | Float?]
28
+
29
+ def version: () -> int
30
+
31
+ def string: () -> String?
32
+
33
+ def number: () -> Float
34
+
35
+ def raw_json: () -> String?
36
+
37
+ def boolean: () -> bool?
38
+
39
+ def flag: () -> bool?
40
+
41
+ def enabled?: () -> bool
42
+
43
+ def set?: () -> bool?
44
+
45
+ private
46
+
47
+ def top_feature_state: -> FeatureState
48
+
49
+ def _feature_state: -> Hash[untyped, untyped]
50
+
51
+ def _set_feature_state: (feature_state: Hash[untyped, untyped]) -> void
52
+
53
+ def get_value: (feature_type: String?) -> [bool? | String? | Float?]
54
+ end
55
+
56
+ class Applied
57
+ attr_reader matched: bool
58
+ attr_reader value: [bool? | String? | Float?]
59
+
60
+ def initialize:(matched: bool, value: [bool? | String? | Float?]) -> void
61
+ end
62
+
63
+ class InterceptorValue
64
+ def initialize: (val: [bool? | String? | Float? ]) -> void
65
+ def cast:(feature_type: String?) -> [bool? | String? | Float? ]
66
+ end
67
+
68
+ class ValueInterceptor
69
+ def intercepted_value: (feature_key: Symbol) -> InterceptorValue?
70
+ end
71
+
72
+ class EnvironmentInterceptor < ValueInterceptor
73
+ end
74
+
75
+ class PercentageCalculator
76
+ def determine_client_percentage: (percentage_text: String, feature_id: String) -> int
77
+ end
78
+
79
+ class ApplyFeatures
80
+ def initialize: (percent_calculator: PercentageCalculator?, matcher_repository: MatcherRepository?) -> void
81
+ def apply: (strategies: Array[RolloutStrategy], key: String, feature_value_id: String, context: ClientContext) -> Applied
82
+ def match_attribute: (context: ClientContext, rs: RolloutStrategyAttribute) -> bool
83
+ def self.determine_percentage_key: (context: ClientContext, rsi: RolloutStrategy) -> String?
84
+ end
85
+
86
+ class InternalFeatureRepository
87
+ def feature: (key: String) -> FeatureState?
88
+ def find_interceptor: (feature_value: String) -> InterceptorValue?
89
+ def ready?: -> bool
90
+ def not_ready!: -> void
91
+ def apply: (strategies: Array[RolloutStrategy], key: String, feature_id: String, context: ClientContext) -> Applied
92
+ def notify: (status: String, data: Hash[untyped, untyped]) -> void
93
+ end
94
+
95
+ class FeatureHubRepository < InternalFeatureRepository
96
+ @features: Hash[String, FeatureState]
97
+ @ready: bool
98
+
99
+ def initialize: (apply_features: nil | ApplyFeatures ) -> void
100
+ def apply:(strategies: Array[RolloutStrategy], key: String, feature_id: String, context: ClientContext) -> Applied
101
+
102
+ def notify: (status: String, data: Hash[untyped, untyped]) -> void
103
+ def feature: (key: String) -> FeatureState
104
+ def register_interceptor: (interceptor: ValueInterceptor) -> void
105
+
106
+ def find_interceptor: (feature_value: String) -> InterceptorValue?
107
+ def ready?: -> bool
108
+ def not_ready!: -> void
109
+
110
+ def extract_feature_state: -> Array[Hash[untyped, untyped]]
111
+ end
112
+
113
+ class ClientContext
114
+ attr_reader repo: InternalFeatureRepository
115
+
116
+ def initialize: (repository: InternalFeatureRepository) -> void
117
+ def user_key: (value: String) -> ClientContext
118
+ def session_key: (value: String) -> ClientContext
119
+ def version: (value: String) -> ClientContext
120
+ def country: (value: Symbol ) -> ClientContext
121
+ def platform: (value: Symbol ) -> ClientContext
122
+ def device: (value: Symbol ) -> ClientContext
123
+ def attribute_value: (key: String, values: Array[String] ) -> ClientContext
124
+ def clear: -> ClientContext
125
+ def get_attr: (key: String, default_val: String?) -> String?
126
+ def default_percentage_key: -> String?
127
+ def enabled: (key: String) -> bool?
128
+
129
+ def feature: (key: String) -> FeatureState
130
+ def set?: (key: String) -> bool?
131
+ def number: (key: String) -> Float?
132
+ def string: (key: String) -> String?
133
+ def json: (key: String) -> Hash[untyped, untyped]?
134
+ def raw_json: (key: String) -> String?
135
+ def flag: (key: String) -> bool?
136
+ def boolean: (key: String) -> bool?
137
+ def exists?: (key: String) -> bool
138
+
139
+ def build: -> ClientContext
140
+ def build_sync: -> ClientContext
141
+ def close: -> ClientContext
142
+ end
143
+
144
+ class EdgeService
145
+ def initialize: (repository: InternalFeatureRepository, api_keys: Array[String], edge_url: String) -> void
146
+ def poll: -> void
147
+ def context_change: (new_header: String?) -> void
148
+ def close: -> void
149
+ end
150
+
151
+ class ServerEvalFeatureContext < ClientContext
152
+ @edge: EdgeService
153
+
154
+ def initialize: (repository: InternalFeatureRepository, edge: EdgeService) -> void
155
+ end
156
+
157
+ class ClientEvalFeatureContext < ClientContext
158
+ @edge: EdgeService
159
+
160
+ def initialize: (repository: InternalFeatureRepository, edge: EdgeService) -> void
161
+ end
162
+
163
+ class FeatureHubConfig
164
+ attr_reader edge_url: String
165
+ attr_reader api_keys: Array[String]
166
+ attr_reader client_evaluated: bool
167
+
168
+ def initialize: (edge_url: String, api_keys: Array[String], repository: InternalFeatureRepository) -> void
169
+
170
+ def repository: (repository: InternalFeatureRepository?) -> InternalFeatureRepository
171
+
172
+ def init: -> void
173
+
174
+ def get_or_create_edge_service: -> EdgeService
175
+
176
+ # can't figure this out
177
+ # def edge_service_provider:
178
+ #
179
+ def use_polling_edge_service: (interval: int?) -> void
180
+
181
+ def new_context: -> ClientContext
182
+
183
+ def close: -> void
184
+ end
185
+
186
+ class PollingEdgeService
187
+
188
+ end
189
+
190
+ class RolloutStrategyCondition
191
+ def equals?: -> bool
192
+ def ends_with?: -> bool
193
+ def starts_with?: -> bool
194
+ def greater?: -> bool
195
+ def greater_equals?: -> bool
196
+ def less?: -> bool
197
+ def less_equals?: -> bool
198
+ def not_equals?: -> bool
199
+ def includes?: -> bool
200
+ def excludes?: -> bool
201
+ def regex?: -> bool
202
+ end
203
+
204
+ class RolloutStrategyAttribute
205
+ attr_reader id: String
206
+ attr_reader conditional: RolloutStrategyCondition
207
+ attr_reader field_name: String
208
+ attr_reader values: Array[[bool? | String? | Float?]]
209
+ attr_reader field_type: String
210
+
211
+ def float_values: -> Array[Float]
212
+ def str_values: -> Array[String]
213
+ end
214
+
215
+ class RolloutStrategy
216
+ attr_reader attributes: Array[RolloutStrategyAttribute]
217
+ attr_reader id: String
218
+ attr_reader name: String?
219
+ attr_reader percentage: int
220
+ attr_reader percentage_attributes: Array[String]
221
+ attr_reader value: [bool? | String? | Float?]
222
+
223
+ def percentage_attributes?: -> bool
224
+ def attributes?: -> bool
225
+ end
226
+
227
+ class StrategyMatcher
228
+ def match: (supplied_value: String, attr: RolloutStrategyAttribute ) -> bool
229
+ end
230
+
231
+ class BooleanMatcher < StrategyMatcher
232
+ def match: (supplied_value: String, attr: RolloutStrategyAttribute ) -> bool
233
+ end
234
+
235
+ class StringMatcher < StrategyMatcher
236
+ end
237
+
238
+ class NumberMatcher < StrategyMatcher
239
+ end
240
+
241
+ class SemanticVersionMatcher < StrategyMatcher
242
+ end
243
+
244
+ class IPNetworkMatcher < StrategyMatcher
245
+ end
246
+
247
+ class MatcherRepository
248
+ def find_matcher: (attr: RolloutStrategyAttribute) -> StrategyMatcher
249
+ end
250
+
251
+ class MatcherRegistry < MatcherRepository
252
+ end
253
+ end
254
+
255
+ end
256
+ end