prefab-cloud-ruby 0.13.1 → 0.13.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +39 -0
  3. data/.tool-versions +1 -0
  4. data/CODEOWNERS +1 -0
  5. data/Gemfile +11 -4
  6. data/Gemfile.lock +52 -32
  7. data/README.md +19 -1
  8. data/Rakefile +1 -0
  9. data/VERSION +1 -1
  10. data/compile_protos.sh +3 -0
  11. data/lib/prefab/auth_interceptor.rb +1 -0
  12. data/lib/prefab/cancellable_interceptor.rb +1 -0
  13. data/lib/prefab/client.rb +51 -38
  14. data/lib/prefab/config_client.rb +145 -73
  15. data/lib/prefab/config_helper.rb +29 -0
  16. data/lib/prefab/config_loader.rb +98 -13
  17. data/lib/prefab/config_resolver.rb +56 -49
  18. data/lib/prefab/error.rb +6 -0
  19. data/lib/prefab/errors/initialization_timeout_error.rb +13 -0
  20. data/lib/prefab/errors/invalid_api_key_error.rb +19 -0
  21. data/lib/prefab/errors/missing_default_error.rb +13 -0
  22. data/lib/prefab/feature_flag_client.rb +129 -11
  23. data/lib/prefab/internal_logger.rb +29 -0
  24. data/lib/prefab/logger_client.rb +10 -8
  25. data/lib/prefab/murmer3.rb +1 -0
  26. data/lib/prefab/noop_cache.rb +1 -0
  27. data/lib/prefab/noop_stats.rb +1 -0
  28. data/lib/prefab/options.rb +82 -0
  29. data/lib/prefab/ratelimit_client.rb +1 -0
  30. data/lib/prefab-cloud-ruby.rb +10 -0
  31. data/lib/prefab_pb.rb +214 -132
  32. data/lib/prefab_services_pb.rb +35 -6
  33. data/prefab-cloud-ruby.gemspec +29 -10
  34. data/run_test_harness_server.sh +8 -0
  35. data/test/.prefab.test.config.yaml +27 -1
  36. data/test/harness_server.rb +64 -0
  37. data/test/test_client.rb +98 -0
  38. data/test/test_config_client.rb +56 -0
  39. data/test/test_config_loader.rb +39 -25
  40. data/test/test_config_resolver.rb +134 -38
  41. data/test/test_feature_flag_client.rb +277 -35
  42. data/test/test_helper.rb +70 -4
  43. data/test/test_logger.rb +23 -29
  44. metadata +69 -14
  45. data/.ruby-version +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 65817a93520de402bf25dd2ac468d3c28fe0a1a2244080f7d6e3a1b77a3aa9b7
4
- data.tar.gz: 7853f5a5576aaf02c6d55589f8008987645c82951cc10e645e83c5e121fa988f
3
+ metadata.gz: a802a651d70a54a8286549d2b03a7f56470f55c67a3857ab333f8ec0f81f9d86
4
+ data.tar.gz: c4d59c7b59012f0454d40f75c3427f53ac2eac7f42e1213da69c858f17728357
5
5
  SHA512:
6
- metadata.gz: 3ecd82c82287d357dcebc2e264873ac2046323b55aa0125dbcf0175b52c3bdd66ac39cdf71f5cfabd8ba64252bacd9ff7c07473a2e74d92bfbbff43711eef950
7
- data.tar.gz: 61d6de1e9b218f5211dc9e18b1640583a9fcc83785665dad58e201f75f50ff63d6ea8ea0d765d23af87173bde9aca715beb2f534a957241445b3514aedb9d8d6
6
+ metadata.gz: 21d9e587acf0ba6b61bc157b879eb7d5b5a69dcc3af86eb0f1fcd58d80438faa192e104201a89d8b120404938585d230ac5a7506b27af2204b5815029e8b6818
7
+ data.tar.gz: 41f6f7c00816b2e4c05b845983a9bb35a6763954b00d203e320b4bbfeeb1267906374112d50be6920acb398d9f2f6a361d4ca38e3e6dd59e6835f11d5e822f32
@@ -0,0 +1,39 @@
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+ # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
6
+ # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
+
8
+ name: Ruby
9
+
10
+ on:
11
+ push:
12
+ branches: [ "main" ]
13
+ pull_request:
14
+ branches: [ "main" ]
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ test:
21
+
22
+ runs-on: ubuntu-latest
23
+ strategy:
24
+ matrix:
25
+ ruby-version: ['2.6', '2.7', '3.0']
26
+
27
+ steps:
28
+ - uses: actions/checkout@v3
29
+ - name: Set up Ruby
30
+ # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
31
+ # change this to (see https://github.com/ruby/setup-ruby#versioning):
32
+ # uses: ruby/setup-ruby@v1
33
+ # uses: ruby/setup-ruby@2b019609e2b0f1ea1a2bc8ca11cb82ab46ada124
34
+ uses: ruby/setup-ruby@v1
35
+ with:
36
+ ruby-version: ${{ matrix.ruby-version }}
37
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
38
+ - name: Run tests
39
+ run: bundle exec rake
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 3.0.3
data/CODEOWNERS ADDED
@@ -0,0 +1 @@
1
+ * @prefab-cloud/prefabdevs @prefab-cloud/prefabmaintainers @prefab-cloud/prefabadmins
data/Gemfile CHANGED
@@ -2,13 +2,20 @@ source "https://rubygems.org"
2
2
 
3
3
  gem 'concurrent-ruby', '~> 1.0', '>= 1.0.5'
4
4
  gem 'faraday'
5
- gem 'grpc'
5
+ gem 'ld-eventsource'
6
+ gem 'grpc', :platforms => :ruby
7
+ gem 'google-protobuf', :platforms => :ruby
8
+ gem 'googleapis-common-protos-types', :platforms => :ruby
6
9
 
7
10
  group :development do
8
- gem 'grpc-tools', '~> 1.17.1'
9
- gem "shoulda", ">= 0"
11
+ gem 'grpc-tools', :platforms => :ruby
10
12
  gem "rdoc", "~> 3.12"
11
- gem "bundler", "~> 1.0"
13
+ gem "bundler"
12
14
  gem "juwelier", "~> 2.4.9"
13
15
  gem "simplecov", ">= 0"
16
+ gem 'thin'
17
+ end
18
+
19
+ group :test do
20
+ gem "minitest"
14
21
  end
data/Gemfile.lock CHANGED
@@ -1,24 +1,27 @@
1
1
  GEM
2
2
  remote: https://rubygems.org/
3
3
  specs:
4
- activesupport (5.2.4.5)
5
- concurrent-ruby (~> 1.0, >= 1.0.2)
6
- i18n (>= 0.7, < 2)
7
- minitest (~> 5.1)
8
- tzinfo (~> 1.1)
9
- addressable (2.7.0)
4
+ addressable (2.8.0)
10
5
  public_suffix (>= 2.0.2, < 5.0)
11
6
  builder (3.2.4)
12
- concurrent-ruby (1.1.8)
7
+ concurrent-ruby (1.1.10)
8
+ daemons (1.4.1)
13
9
  descendants_tracker (0.0.4)
14
10
  thread_safe (~> 0.3, >= 0.3.1)
15
11
  docile (1.3.5)
12
+ domain_name (0.5.20190701)
13
+ unf (>= 0.0.5, < 1.0.0)
14
+ eventmachine (1.2.7)
16
15
  faraday (1.3.0)
17
16
  faraday-net_http (~> 1.0)
18
17
  multipart-post (>= 1.2, < 3)
19
18
  ruby2_keywords
20
19
  faraday-net_http (1.0.1)
21
- git (1.8.1)
20
+ ffi (1.15.5)
21
+ ffi-compiler (1.0.1)
22
+ ffi (>= 1.0.0)
23
+ rake
24
+ git (1.11.0)
22
25
  rchardet (~> 1.8)
23
26
  github_api (0.19.0)
24
27
  addressable (~> 2.4)
@@ -26,17 +29,23 @@ GEM
26
29
  faraday (>= 0.8, < 2)
27
30
  hashie (~> 3.5, >= 3.5.2)
28
31
  oauth2 (~> 1.0)
29
- google-protobuf (3.15.6)
30
- googleapis-common-protos-types (1.0.6)
31
- google-protobuf (~> 3.14)
32
- grpc (1.36.0)
32
+ google-protobuf (3.21.4)
33
+ googleapis-common-protos-types (1.3.0)
33
34
  google-protobuf (~> 3.14)
35
+ grpc (1.43.1)
36
+ google-protobuf (~> 3.18)
34
37
  googleapis-common-protos-types (~> 1.0)
35
- grpc-tools (1.17.1)
38
+ grpc-tools (1.43.1)
36
39
  hashie (3.6.0)
37
40
  highline (2.0.3)
38
- i18n (1.8.9)
39
- concurrent-ruby (~> 1.0)
41
+ http (5.0.1)
42
+ addressable (~> 2.3)
43
+ http-cookie (~> 1.0)
44
+ http-form_data (~> 2.2)
45
+ llhttp-ffi (~> 0.3.0)
46
+ http-cookie (1.0.4)
47
+ domain_name (~> 0.5)
48
+ http-form_data (2.3.0)
40
49
  json (1.8.6)
41
50
  juwelier (2.4.9)
42
51
  builder
@@ -53,13 +62,20 @@ GEM
53
62
  jwt (2.2.2)
54
63
  kamelcase (0.0.2)
55
64
  semver2 (~> 3)
56
- mini_portile2 (2.4.0)
57
- minitest (5.14.4)
65
+ ld-eventsource (2.2.0)
66
+ concurrent-ruby (~> 1.0)
67
+ http (>= 4.4.1, < 6.0.0)
68
+ llhttp-ffi (0.3.1)
69
+ ffi-compiler (~> 1.0)
70
+ rake (~> 13.0)
71
+ mini_portile2 (2.8.0)
72
+ minitest (5.16.2)
58
73
  multi_json (1.15.0)
59
74
  multi_xml (0.6.0)
60
75
  multipart-post (2.1.1)
61
- nokogiri (1.10.10)
62
- mini_portile2 (~> 2.4.0)
76
+ nokogiri (1.13.6)
77
+ mini_portile2 (~> 2.8.0)
78
+ racc (~> 1.4)
63
79
  oauth2 (1.4.7)
64
80
  faraday (>= 0.8, < 2.0)
65
81
  jwt (>= 1.0, < 3.0)
@@ -68,40 +84,44 @@ GEM
68
84
  rack (>= 1.2, < 3)
69
85
  psych (3.3.1)
70
86
  public_suffix (4.0.6)
71
- rack (2.2.3)
87
+ racc (1.6.0)
88
+ rack (2.2.4)
72
89
  rake (13.0.3)
73
90
  rchardet (1.8.0)
74
91
  rdoc (3.12.2)
75
92
  json (~> 1.4)
76
93
  ruby2_keywords (0.0.4)
77
94
  semver2 (3.4.2)
78
- shoulda (4.0.0)
79
- shoulda-context (~> 2.0)
80
- shoulda-matchers (~> 4.0)
81
- shoulda-context (2.0.0)
82
- shoulda-matchers (4.5.1)
83
- activesupport (>= 4.2.0)
84
95
  simplecov (0.18.5)
85
96
  docile (~> 1.1)
86
97
  simplecov-html (~> 0.11)
87
98
  simplecov-html (0.12.3)
99
+ thin (1.8.1)
100
+ daemons (~> 1.0, >= 1.0.9)
101
+ eventmachine (~> 1.0, >= 1.0.4)
102
+ rack (>= 1, < 3)
88
103
  thread_safe (0.3.6)
89
- tzinfo (1.2.9)
90
- thread_safe (~> 0.1)
104
+ unf (0.1.4)
105
+ unf_ext
106
+ unf_ext (0.0.8)
91
107
 
92
108
  PLATFORMS
93
109
  ruby
94
110
 
95
111
  DEPENDENCIES
96
- bundler (~> 1.0)
112
+ bundler
97
113
  concurrent-ruby (~> 1.0, >= 1.0.5)
98
114
  faraday
115
+ google-protobuf
116
+ googleapis-common-protos-types
99
117
  grpc
100
- grpc-tools (~> 1.17.1)
118
+ grpc-tools
101
119
  juwelier (~> 2.4.9)
120
+ ld-eventsource
121
+ minitest
102
122
  rdoc (~> 3.12)
103
- shoulda
104
123
  simplecov
124
+ thin
105
125
 
106
126
  BUNDLED WITH
107
- 1.16.0
127
+ 2.3.5
data/README.md CHANGED
@@ -43,6 +43,15 @@ on_worker_boot do
43
43
  end
44
44
  ```
45
45
 
46
+ ## Logging & Debugging
47
+ In classpath or ~/.prefab.overrides.config.yaml set
48
+ ```log_level.prefab: debug```
49
+
50
+ To debug issues before this config file has been read, set env var
51
+ ```
52
+ PREFAB_LOG_CLIENT_BOOTSTRAP_LOG_LEVEL = debug
53
+ ```
54
+
46
55
  ## Contributing to prefab-cloud-ruby
47
56
 
48
57
  * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
@@ -53,7 +62,16 @@ end
53
62
  * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
54
63
  * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
55
64
 
65
+ ## Release
66
+
67
+ ```shell
68
+ update VERSION
69
+ bundle exec rake gemspec:generate
70
+ git commit & push
71
+ REMOTE_BRANCH=main LOCAL_BRANCH=main bundle exec rake release
72
+ ```
73
+
56
74
  ## Copyright
57
75
 
58
- Copyright (c) 2018 Jeff Dwyer. See LICENSE.txt for
76
+ Copyright (c) 2022 Jeff Dwyer. See LICENSE.txt for
59
77
  further details.
data/Rakefile CHANGED
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'rubygems'
4
5
  require 'bundler'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.13.1
1
+ 0.13.2
data/compile_protos.sh CHANGED
@@ -1,2 +1,5 @@
1
1
  #!/usr/bin/env bash
2
2
  grpc_tools_ruby_protoc -I ../prefab-cloud/ --ruby_out=lib --grpc_out=lib prefab.proto
3
+ # on M1 you need to
4
+ # 1. run in rosetta
5
+ # 2. mv gems/2.6.0/gems/grpc-tools-1.43.1/bin/x86_64-macos x86-macos
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Prefab
2
3
  class AuthInterceptor < GRPC::ClientInterceptor
3
4
  def initialize(api_key)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Prefab
2
3
  class CancellableInterceptor < GRPC::ClientInterceptor
3
4
  WAIT_SEC = 3
data/lib/prefab/client.rb CHANGED
@@ -1,44 +1,39 @@
1
+ # frozen_string_literal: true
1
2
  module Prefab
2
3
  class Client
3
-
4
4
  MAX_SLEEP_SEC = 10
5
5
  BASE_SLEEP_SEC = 0.5
6
- DEFAULT_LOG_FORMATTER = proc {|severity, datetime, progname, msg|
7
- "#{severity.ljust(5)} #{datetime}: #{progname} #{msg}\n"
8
- }
9
-
10
-
11
- attr_reader :account_id, :shared_cache, :stats, :namespace, :interceptor, :api_key
12
-
13
- def initialize(api_key: ENV['PREFAB_API_KEY'],
14
- logdev: nil,
15
- stats: nil, # receives increment("prefab.limitcheck", {:tags=>["policy_group:page_view", "pass:true"]})
16
- shared_cache: nil, # Something that quacks like Rails.cache ideally memcached
17
- local: false,
18
- namespace: "",
19
- log_formatter: DEFAULT_LOG_FORMATTER
20
- )
21
- raise "No API key. Set PREFAB_API_KEY env var" if api_key.nil? || api_key.empty?
22
- @logdev = (logdev || $stdout)
23
- @log_formatter = log_formatter
24
- @local = local
25
- @stats = (stats || NoopStats.new)
26
- @shared_cache = (shared_cache || NoopCache.new)
27
- @api_key = api_key
28
- @account_id = api_key.split("|")[0].to_i
29
- @namespace = namespace
30
- @interceptor = Prefab::AuthInterceptor.new(api_key)
6
+ NO_DEFAULT_PROVIDED = :no_default_provided
7
+
8
+ attr_reader :project_id, :shared_cache, :stats, :namespace, :interceptor, :api_key, :prefab_api_url, :options
9
+
10
+ def initialize(options = Prefab::Options.new)
11
+ @options = options
12
+ @shared_cache = @options.shared_cache
13
+ @stats = @options.stats
14
+ @namespace = @options.namespace
31
15
  @stubs = {}
32
16
 
33
- at_exit do
34
- channel.destroy
17
+ if @options.local_only?
18
+ @project_id = 0
19
+ log_internal Logger::INFO, "Prefab Running in Local Mode"
20
+ else
21
+ @api_key = @options.api_key
22
+ raise Prefab::Errors::InvalidApiKeyError.new(@api_key) if @api_key.nil? || @api_key.empty? || api_key.count("-") != 3
23
+ @project_id = @api_key.split("-")[0].to_i # unvalidated, but that's ok. APIs only listen to the actual passwd
24
+ @interceptor = Prefab::AuthInterceptor.new(@api_key)
25
+ @prefab_api_url = @options.prefab_api_url
26
+ @prefab_grpc_url = @options.prefab_grpc_url
27
+ log_internal Logger::INFO, "Prefab Connecting to: #{@prefab_api_url} and #{@prefab_grpc_url} Secure: #{http_secure?}"
28
+ at_exit do
29
+ channel.destroy
30
+ end
35
31
  end
36
32
  end
37
33
 
38
34
  def channel
39
- credentials = ENV["PREFAB_CLOUD_HTTP"] == "true" ? :this_channel_is_insecure : creds
40
- url = ENV["PREFAB_API_URL"] || 'api.prefab.cloud:443'
41
- @_channel ||= GRPC::Core::Channel.new(url, nil, credentials)
35
+ credentials = http_secure? ? creds : :this_channel_is_insecure
36
+ @_channel ||= GRPC::Core::Channel.new(@prefab_grpc_url, nil, credentials)
42
37
  end
43
38
 
44
39
  def config_client(timeout: 5.0)
@@ -54,11 +49,11 @@ module Prefab
54
49
  end
55
50
 
56
51
  def log
57
- @logger_client ||= Prefab::LoggerClient.new(@logdev, formatter: @log_formatter)
52
+ @logger_client ||= Prefab::LoggerClient.new(@options.logdev, formatter: @options.log_formatter)
58
53
  end
59
54
 
60
- def log_internal(level, msg)
61
- log.log_internal msg, "prefab", nil, level
55
+ def log_internal(level, msg, path = "prefab")
56
+ log.log_internal msg, path, nil, level
62
57
  end
63
58
 
64
59
  def request(service, method, req_options: {}, params: {})
@@ -88,7 +83,7 @@ module Prefab
88
83
  end
89
84
 
90
85
  def cache_key(post_fix)
91
- "prefab:#{account_id}:#{post_fix}"
86
+ "prefab:#{project_id}:#{post_fix}"
92
87
  end
93
88
 
94
89
  def reset!
@@ -96,8 +91,26 @@ module Prefab
96
91
  @_channel = nil
97
92
  end
98
93
 
94
+ def enabled?(feature_name, lookup_key=nil, attributes={})
95
+ feature_flag_client.feature_is_on_for?(feature_name, lookup_key, attributes: attributes)
96
+ end
97
+
98
+ def get(key, default_or_lookup_key=NO_DEFAULT_PROVIDED, attributes={}, ff_default=NO_DEFAULT_PROVIDED)
99
+ result = config_client.get(key, default_or_lookup_key)
100
+
101
+ if result.is_a?(Prefab::FeatureFlag)
102
+ feature_flag_client.get(key, default_or_lookup_key, attributes, default: ff_default)
103
+ else
104
+ result
105
+ end
106
+ end
107
+
99
108
  private
100
109
 
110
+ def http_secure?
111
+ ENV["PREFAB_CLOUD_HTTP"] != "true"
112
+ end
113
+
101
114
  def stub_for(service, timeout)
102
115
  @stubs["#{service}_#{timeout}"] ||= service::Stub.new(nil,
103
116
  nil,
@@ -113,10 +126,10 @@ module Prefab
113
126
  def ssl_certs
114
127
  ssl_certs = ""
115
128
  Dir["#{OpenSSL::X509::DEFAULT_CERT_DIR}/*.pem"].each do |cert|
116
- ssl_certs << File.open(cert).read
129
+ ssl_certs += File.open(cert).read
117
130
  end
118
- if OpenSSL::X509::DEFAULT_CERT_FILE && File.exists?(OpenSSL::X509::DEFAULT_CERT_FILE)
119
- ssl_certs << File.open(OpenSSL::X509::DEFAULT_CERT_FILE).read
131
+ if OpenSSL::X509::DEFAULT_CERT_FILE && File.exist?(OpenSSL::X509::DEFAULT_CERT_FILE)
132
+ ssl_certs += File.open(OpenSSL::X509::DEFAULT_CERT_FILE).read
120
133
  end
121
134
  ssl_certs
122
135
  rescue => e