prefab-cloud-ruby 0.13.0 → 0.13.1

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -0
  3. data/Gemfile +4 -11
  4. data/Gemfile.lock +32 -52
  5. data/README.md +1 -19
  6. data/Rakefile +0 -1
  7. data/VERSION +1 -1
  8. data/compile_protos.sh +0 -3
  9. data/lib/prefab/auth_interceptor.rb +0 -1
  10. data/lib/prefab/cancellable_interceptor.rb +0 -1
  11. data/lib/prefab/client.rb +36 -49
  12. data/lib/prefab/config_client.rb +73 -145
  13. data/lib/prefab/config_loader.rb +13 -98
  14. data/lib/prefab/config_resolver.rb +49 -56
  15. data/lib/prefab/feature_flag_client.rb +11 -129
  16. data/lib/prefab/logger_client.rb +8 -10
  17. data/lib/prefab/murmer3.rb +0 -1
  18. data/lib/prefab/noop_cache.rb +0 -1
  19. data/lib/prefab/noop_stats.rb +0 -1
  20. data/lib/prefab/ratelimit_client.rb +0 -1
  21. data/lib/prefab-cloud-ruby.rb +0 -10
  22. data/lib/prefab_pb.rb +132 -214
  23. data/lib/prefab_services_pb.rb +6 -35
  24. data/prefab-cloud-ruby.gemspec +11 -30
  25. data/test/.prefab.test.config.yaml +1 -27
  26. data/test/test_config_loader.rb +25 -39
  27. data/test/test_config_resolver.rb +38 -134
  28. data/test/test_feature_flag_client.rb +35 -277
  29. data/test/test_helper.rb +4 -70
  30. data/test/test_logger.rb +29 -23
  31. metadata +15 -70
  32. data/.github/workflows/ruby.yml +0 -39
  33. data/.tool-versions +0 -1
  34. data/CODEOWNERS +0 -1
  35. data/lib/prefab/config_helper.rb +0 -29
  36. data/lib/prefab/error.rb +0 -6
  37. data/lib/prefab/errors/initialization_timeout_error.rb +0 -13
  38. data/lib/prefab/errors/invalid_api_key_error.rb +0 -19
  39. data/lib/prefab/errors/missing_default_error.rb +0 -13
  40. data/lib/prefab/internal_logger.rb +0 -29
  41. data/lib/prefab/options.rb +0 -82
  42. data/run_test_harness_server.sh +0 -8
  43. data/test/harness_server.rb +0 -64
  44. data/test/test_client.rb +0 -91
  45. data/test/test_config_client.rb +0 -56
@@ -1,39 +0,0 @@
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 DELETED
@@ -1 +0,0 @@
1
- ruby 3.0.3
data/CODEOWNERS DELETED
@@ -1 +0,0 @@
1
- * @prefab-cloud/prefabdevs @prefab-cloud/prefabmaintainers @prefab-cloud/prefabadmins
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
- module Prefab
3
- module ConfigHelper
4
- def value_of(config_value)
5
- case config_value.type
6
- when :string
7
- config_value.string
8
- when :int
9
- config_value.int
10
- when :double
11
- config_value.double
12
- when :bool
13
- config_value.bool
14
- when :feature_flag
15
- config_value.feature_flag
16
- when :segment
17
- config_value.segment
18
- end
19
- end
20
-
21
- def value_of_variant(feature_flag_variant)
22
- return feature_flag_variant.string if feature_flag_variant.has_string?
23
- return feature_flag_variant.int if feature_flag_variant.has_int?
24
- return feature_flag_variant.double if feature_flag_variant.has_double?
25
- return feature_flag_variant.bool if feature_flag_variant.has_bool?
26
- return nil
27
- end
28
- end
29
- end
data/lib/prefab/error.rb DELETED
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Prefab
4
- class Error < StandardError
5
- end
6
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Prefab
4
- module Errors
5
- class InitializationTimeoutError < Prefab::Error
6
- def initialize(timeout_sec, key)
7
- message = "Prefab couldn't initialize in #{timeout_sec} second timeout. Trying to fetch key `#{key}`."
8
-
9
- super(message)
10
- end
11
- end
12
- end
13
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Prefab
4
- module Errors
5
- class InvalidApiKeyError < Prefab::Error
6
- def initialize(key)
7
- if key.nil? || key.empty?
8
- message = "No API key. Set PREFAB_API_KEY env var or use PREFAB_DATASOURCES=LOCAL_ONLY"
9
-
10
- super(message)
11
- else
12
- message = "Your API key format is invalid. Expecting something like 123-development-yourapikey-SDK. You provided `#{key}`"
13
-
14
- super(message)
15
- end
16
- end
17
- end
18
- end
19
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Prefab
4
- module Errors
5
- class MissingDefaultError < Prefab::Error
6
- def initialize(key)
7
- message = "No value found for key '#{key}' and no default was provided.\n\nIf you'd prefer returning `nil` rather than raising when this occurs, modify the `on_no_default` value you provide in your Prefab::Options."
8
-
9
- super(message)
10
- end
11
- end
12
- end
13
- end
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
- module Prefab
3
- class InternalLogger < Logger
4
- def initialize(path, logger)
5
- @path = path
6
- @logger = logger
7
- end
8
-
9
- def debug(progname = nil, &block)
10
- @logger.log_internal yield, @path, progname, DEBUG
11
- end
12
-
13
- def info(progname = nil, &block)
14
- @logger.log_internal yield, @path, progname, INFO
15
- end
16
-
17
- def warn(progname = nil, &block)
18
- @logger.log_internal yield, @path, progname, WARN
19
- end
20
-
21
- def error(progname = nil, &block)
22
- @logger.log_internal yield, @path, progname, ERROR
23
- end
24
-
25
- def fatal(progname = nil, &block)
26
- @logger.log_internal yield, @path, progname, FATAL
27
- end
28
- end
29
- end
@@ -1,82 +0,0 @@
1
- # frozen_string_literal: true
2
- module Prefab
3
- class Options
4
- attr_reader :api_key
5
- attr_reader :logdev
6
- attr_reader :log_formatter
7
- attr_reader :stats
8
- attr_reader :shared_cache
9
- attr_reader :namespace
10
- attr_reader :prefab_api_url
11
- attr_reader :prefab_grpc_url
12
- attr_reader :on_no_default
13
- attr_reader :initialization_timeout_sec
14
- attr_reader :on_init_failure
15
- attr_reader :prefab_config_override_dir
16
- attr_reader :prefab_config_classpath_dir
17
- attr_reader :defaults_env
18
-
19
- DEFAULT_LOG_FORMATTER = proc { |severity, datetime, progname, msg|
20
- "#{severity.ljust(5)} #{datetime}: #{progname} #{msg}\n"
21
- }
22
-
23
- module ON_INITIALIZATION_FAILURE
24
- RAISE = 1
25
- RETURN = 2
26
- end
27
- module ON_NO_DEFAULT
28
- RAISE = 1
29
- RETURN_NIL = 2
30
- end
31
- module DATASOURCES
32
- ALL = 1
33
- LOCAL_ONLY = 2
34
- end
35
-
36
- def initialize(
37
- api_key: ENV['PREFAB_API_KEY'],
38
- logdev: $stdout,
39
- stats: NoopStats.new, # receives increment("prefab.limitcheck", {:tags=>["policy_group:page_view", "pass:true"]})
40
- shared_cache: NoopCache.new, # Something that quacks like Rails.cache ideally memcached
41
- namespace: "",
42
- log_formatter: DEFAULT_LOG_FORMATTER,
43
- prefab_api_url: ENV["PREFAB_API_URL"] || 'https://api.prefab.cloud',
44
- prefab_grpc_url: ENV["PREFAB_GRPC_URL"] || 'grpc.prefab.cloud:443',
45
- on_no_default: ON_NO_DEFAULT::RAISE, # options :raise, :warn_and_return_nil,
46
- initialization_timeout_sec: 10, # how long to wait before on_init_failure
47
- on_init_failure: ON_INITIALIZATION_FAILURE::RAISE, #options :unlock_and_continue, :lock_and_keep_trying, :raise
48
- # new_config_callback: nil, #callback method
49
- # live_override_url: nil,
50
- prefab_datasources: ENV['PREFAB_DATASOURCES'] == "LOCAL_ONLY" ? DATASOURCES::LOCAL_ONLY : DATASOURCES::ALL,
51
- prefab_config_override_dir: Dir.home,
52
- prefab_config_classpath_dir: ".",
53
- defaults_env: ""
54
- )
55
- # debugger
56
- @api_key = api_key
57
- @logdev = logdev
58
- @stats = stats
59
- @shared_cache = shared_cache
60
- @namespace = namespace
61
- @log_formatter = log_formatter
62
- @prefab_api_url = prefab_api_url
63
- @prefab_grpc_url = prefab_grpc_url
64
- @on_no_default = on_no_default
65
- @initialization_timeout_sec = initialization_timeout_sec
66
- @on_init_failure = on_init_failure
67
- @prefab_datasources = prefab_datasources
68
- @prefab_config_classpath_dir = prefab_config_classpath_dir
69
- @prefab_config_override_dir = prefab_config_override_dir
70
- @defaults_env = defaults_env
71
- end
72
-
73
- def local_only?
74
- @prefab_datasources == DATASOURCES::LOCAL_ONLY
75
- end
76
-
77
- # https://api.prefab.cloud -> https://api-prefab-cloud.global.ssl.fastly.net
78
- def url_for_api_cdn
79
- ENV['PREFAB_CDN_URL'] || "#{@prefab_api_url.gsub(/\./, "-")}.global.ssl.fastly.net"
80
- end
81
- end
82
- end
@@ -1,8 +0,0 @@
1
- #! /usr/bin/env bash
2
-
3
- PREFAB_CDN_URL="https://api-prefab-cloud.global.ssl.fastly.net" \
4
- PREFAB_LOG_CLIENT_BOOTSTRAP_LOG_LEVEL=debug \
5
- PREFAB_CLOUD_HTTP=true \
6
- PREFAB_API_KEY="1|local_development_api_key" \
7
- PREFAB_GRPC_URL="localhost:50051" \
8
- ruby -Ilib test/harness_server.rb
@@ -1,64 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'prefab-cloud-ruby'
3
- require 'rack'
4
- require 'base64'
5
- require 'json'
6
-
7
- handler = Rack::Handler::Thin
8
-
9
- #
10
- # This is a very lightweight server that allows the compliance harness to excercise the prefab client
11
- #
12
- class RackApp
13
- def call(env)
14
- props = CGI::parse(env["QUERY_STRING"])
15
- props = JSON.parse(Base64.decode64(props["props"][0]))
16
-
17
- key = props["key"]
18
- namespace = props["namespace"]
19
- api_key = props["api_key"]
20
- user_key = props["user_key"]
21
- is_feature_flag = !props["feature_flag"].nil?
22
- attributes = props["attributes"]
23
- puts props
24
-
25
- options = Prefab::Options.new(
26
- api_key: api_key,
27
- namespace: namespace,
28
- initialization_timeout_sec: 1,
29
- # We want to `return` rather than raise so we'll use the initial payload if we can't connect to the SSE server
30
- on_init_failure: Prefab::Options::ON_INITIALIZATION_FAILURE::RETURN,
31
- # Want to return `nil` rather than raise so we can verify empty values
32
- on_no_default: Prefab::Options::ON_NO_DEFAULT::RETURN_NIL
33
- )
34
-
35
- client = Prefab::Client.new(options)
36
-
37
- puts "Key #{key}"
38
- puts "User #{user_key}"
39
- puts "api_key #{api_key}"
40
- puts "Namespace #{namespace}"
41
- puts "Props! #{props}"
42
- puts "is_feature_flag! #{is_feature_flag}"
43
-
44
- puts client.config_client.to_s
45
-
46
- if is_feature_flag
47
- puts "EVALFF #{key} #{user_key}"
48
- rtn = client.feature_flag_client.get(key, user_key, attributes).to_s
49
- else
50
- rtn = client.config_client.get(key).to_s
51
- end
52
-
53
- puts "return #{rtn}"
54
-
55
- [200, { "Content-Type" => "text/plain" }, rtn]
56
-
57
- rescue Exception => e
58
- puts "ERROR #{e.message}"
59
- puts e.backtrace
60
- [500, { "Content-Type" => "text/plain" }, e.message]
61
- end
62
- end
63
-
64
- handler.run RackApp.new
data/test/test_client.rb DELETED
@@ -1,91 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'test_helper'
3
-
4
- class TestClient < Minitest::Test
5
- def setup
6
- @client = new_client
7
- end
8
-
9
- def test_get
10
- assert_equal "test sample value", @client.get("sample")
11
- assert_equal 123, @client.get("sample_int")
12
- end
13
-
14
- def test_get_with_default
15
- # A `false` value is not replaced with the default
16
- assert_equal false, @client.get("false_value", "red")
17
-
18
- # A falsy value is not replaced with the default
19
- assert_equal 0, @client.get("zero_value", "red")
20
-
21
- # A missing value returns the default
22
- assert_equal "buckets", @client.get("missing_value", "buckets")
23
- end
24
-
25
- def test_get_with_missing_default
26
- # it raises by default
27
- err = assert_raises(Prefab::Errors::MissingDefaultError) do
28
- assert_nil @client.get("missing_value")
29
- end
30
-
31
- assert_match(/No value found for key/, err.message)
32
- assert_match(/on_no_default/, err.message)
33
-
34
- # you can opt-in to return `nil` instead
35
- client = new_client(on_no_default: Prefab::Options::ON_NO_DEFAULT::RETURN_NIL)
36
- assert_nil client.get("missing_value")
37
- end
38
-
39
- def test_enabled
40
- assert_equal false, @client.enabled?("does_not_exist")
41
- assert_equal true, @client.enabled?("enabled_flag")
42
- assert_equal false, @client.enabled?("disabled_flag")
43
- assert_equal false, @client.enabled?("flag_with_a_value")
44
- end
45
-
46
- def test_ff_enabled_with_lookup_key
47
- assert_equal false, @client.enabled?("in_lookup_key", "jimmy")
48
- assert_equal true, @client.enabled?("in_lookup_key", "abc123")
49
- assert_equal true, @client.enabled?("in_lookup_key", "xyz987")
50
- end
51
-
52
- def test_ff_get_with_lookup_key
53
- assert_nil @client.get("in_lookup_key", "jimmy")
54
- assert_equal "DEFAULT", @client.get("in_lookup_key", "jimmy", {}, "DEFAULT")
55
-
56
- assert_equal true, @client.get("in_lookup_key", "abc123")
57
- assert_equal true, @client.get("in_lookup_key", "xyz987")
58
- end
59
-
60
- def test_ff_enabled_with_attributes
61
- assert_equal false, @client.enabled?("just_my_domain", "abc123", { domain: "gmail.com" })
62
- assert_equal false, @client.enabled?("just_my_domain", "abc123", { domain: "prefab.cloud" })
63
- assert_equal false, @client.enabled?("just_my_domain", "abc123", { domain: "example.com" })
64
- end
65
-
66
- def test_ff_get_with_attributes
67
- assert_nil @client.get("just_my_domain", "abc123", { domain: "gmail.com" })
68
- assert_equal "DEFAULT", @client.get("just_my_domain", "abc123", { domain: "gmail.com" }, "DEFAULT")
69
-
70
- assert_equal "new-version", @client.get("just_my_domain", "abc123", { domain: "prefab.cloud" })
71
- assert_equal "new-version", @client.get("just_my_domain", "abc123", { domain: "example.com" })
72
- end
73
-
74
- def test_getting_feature_flag_value
75
- assert_equal false, @client.enabled?("flag_with_a_value")
76
- assert_equal "all-features", @client.get("flag_with_a_value")
77
- end
78
-
79
- private
80
-
81
- def new_client(overrides = {})
82
- options = Prefab::Options.new(**{
83
- prefab_config_override_dir: "none",
84
- prefab_config_classpath_dir: "test",
85
- defaults_env: "unit_tests",
86
- prefab_datasources: Prefab::Options::DATASOURCES::LOCAL_ONLY
87
- }.merge(overrides))
88
-
89
- Prefab::Client.new(options)
90
- end
91
- end
@@ -1,56 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'test_helper'
3
-
4
- class TestConfigClient < Minitest::Test
5
- def setup
6
- options = Prefab::Options.new(
7
- prefab_config_override_dir: "none",
8
- prefab_config_classpath_dir: "test",
9
- defaults_env: "unit_tests",
10
- prefab_datasources: Prefab::Options::DATASOURCES::LOCAL_ONLY
11
- )
12
-
13
- @config_client = Prefab::ConfigClient.new(MockBaseClient.new(options), 10)
14
- end
15
-
16
- def test_load
17
- assert_equal "test sample value", @config_client.get("sample")
18
- assert_equal 123, @config_client.get("sample_int")
19
- end
20
-
21
- def test_initialization_timeout_error
22
- options = Prefab::Options.new(
23
- api_key: "123-ENV-KEY-SDK",
24
- initialization_timeout_sec: 0.01,
25
- logdev: StringIO.new
26
- )
27
-
28
- err = assert_raises(Prefab::Errors::InitializationTimeoutError) do
29
- Prefab::Client.new(options).config_client.get("anything")
30
- end
31
-
32
- assert_match(/couldn't initialize in 0.01 second timeout/, err.message)
33
- end
34
-
35
- def test_invalid_api_key_error
36
- options = Prefab::Options.new(
37
- api_key: "",
38
- )
39
-
40
- err = assert_raises(Prefab::Errors::InvalidApiKeyError) do
41
- Prefab::Client.new(options).config_client.get("anything")
42
- end
43
-
44
- assert_match(/No API key/, err.message)
45
-
46
- options = Prefab::Options.new(
47
- api_key: "invalid",
48
- )
49
-
50
- err = assert_raises(Prefab::Errors::InvalidApiKeyError) do
51
- Prefab::Client.new(options).config_client.get("anything")
52
- end
53
-
54
- assert_match(/format is invalid/, err.message)
55
- end
56
- end