prefab-cloud-ruby 0.13.0 → 0.13.1

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/.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