prefab-cloud-ruby 1.8.3 → 1.8.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ddab427174fc8ee9580364b7f55becb58806d98b94968ee3d903ce67e9b01268
4
- data.tar.gz: 181cdf62f18a7c9b09846746597273230f6b13100b302ef4531693f0a3d07085
3
+ metadata.gz: 221fd3505c561f550efb45c86fcdf1ea79a7afd442ef6d2c351d9f0ce433c391
4
+ data.tar.gz: e85a75665a06ed8350e9fe6d44bc5c7462eb1d5d698f2b58a8ae2890472740e2
5
5
  SHA512:
6
- metadata.gz: 784b5d8e0229ec1e953d16a2442561a1cc67e18fc4f398f4878ac4672256892a0d6c314b06b9cd835a8309d184d47a2280d348b8afd396f977c6c57248d7c2b9
7
- data.tar.gz: '0865454d8eab0548aff23e3caa338d037babc8d9a49165424e6b8822d6a5f5b5ba67e8395752bebedb9666ff5cd3f1eac46f4d4df66abc1c8f2274770e4d634f'
6
+ metadata.gz: 34a83eabd765201e831bc00d6b4472f1b67f6eb9271f5cd77f33de7a55571acbd323f2c6ee2c8df8a3df0602f577a3a30daebe983403d0e61870a14f6bcd7b74
7
+ data.tar.gz: fc6f2e0616a62dee54415d1290d163a055391afee5ba62f158aad5d19192ebd129507305dd6cda9735bf8b184e9c7e94657f1005384907150a0049c14cc8c6fe
@@ -25,7 +25,7 @@ jobs:
25
25
  ruby-version: ['2.7', '3.0', '3.3']
26
26
 
27
27
  steps:
28
- - uses: actions/checkout@v3
28
+ - uses: actions/checkout@v4
29
29
  with:
30
30
  submodules: recursive
31
31
  - name: Set up Ruby
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.8.5 - 2024-09-27
4
+
5
+ - Fix JS bootstrapping and improve performance (#206)
6
+ - Promote `datafile` from `x_datafile` (#205)
7
+
8
+ ## 1.8.4 - 2024-09-19
9
+
10
+ - Use `stream` subdomain for SSE (#203)
11
+
3
12
  ## 1.8.3 - 2024-09-16
4
13
 
5
14
  - Add JavaScript stub & bootstrapping (#200)
data/Gemfile.lock CHANGED
@@ -62,7 +62,7 @@ GEM
62
62
  faraday (>= 0.8, < 2)
63
63
  hashie (~> 3.5, >= 3.5.2)
64
64
  oauth2 (~> 1.0)
65
- google-protobuf (3.25.3)
65
+ google-protobuf (3.25.5)
66
66
  googleapis-common-protos-types (1.14.0)
67
67
  google-protobuf (~> 3.18)
68
68
  hashie (3.6.0)
@@ -152,7 +152,7 @@ GEM
152
152
  concurrent-ruby (~> 1.0)
153
153
  uuid (2.3.9)
154
154
  macaddr (~> 1.0)
155
- webrick (1.8.1)
155
+ webrick (1.8.2)
156
156
 
157
157
  PLATFORMS
158
158
  ruby
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.8.3
1
+ 1.8.5
@@ -30,7 +30,7 @@ module Prefab
30
30
  end
31
31
 
32
32
  # this will return the actual value of confidential, use reportable_value unless you need it
33
- def unwrap
33
+ def unwrap(raw_json: false)
34
34
  raw = case @config_value.type
35
35
  when :int, :string, :double, :bool, :log_level
36
36
  @config_value.public_send(@config_value.type)
@@ -39,7 +39,11 @@ module Prefab
39
39
  when :duration
40
40
  Prefab::Duration.new(@config_value.duration.definition)
41
41
  when :json
42
- JSON.parse(@config_value.json.json)
42
+ if raw_json
43
+ @config_value.json.json
44
+ else
45
+ JSON.parse(@config_value.json.json)
46
+ end
43
47
  else
44
48
  LOG.error "Unknown type: #{@config_value.type}"
45
49
  raise "Unknown type: #{@config_value.type}"
@@ -3,50 +3,37 @@
3
3
  module Prefab
4
4
  class JavaScriptStub
5
5
  LOG = Prefab::InternalLogger.new(self)
6
+ CAMELS = {}
6
7
 
7
8
  def initialize(client = nil)
8
9
  @client = client || Prefab.instance
9
10
  end
10
11
 
11
- # Generate the JavaScript snippet to bootstrap the client SDK. This will
12
- # include the configuration values that are permitted to be sent to the
13
- # client SDK.
14
- #
15
- # If the context provided to the client SDK is not the same as the context
16
- # used to generate the configuration values, the client SDK will still
17
- # generate a fetch to get the correct values for the context.
18
- #
19
- # Any keys that could not be resolved will be logged as a warning to the
20
- # console.
21
12
  def bootstrap(context)
22
- configs, warnings = data(context)
13
+ configs, warnings = data(context, :bootstrap)
23
14
  <<~JS
24
15
  window._prefabBootstrap = {
25
- configs: #{JSON.dump(configs)},
16
+ evaluations: #{JSON.dump(configs)},
26
17
  context: #{JSON.dump(context)}
27
18
  }
28
19
  #{log_warnings(warnings)}
29
20
  JS
30
21
  end
31
22
 
32
- # Generate the JavaScript snippet to *replace* the client SDK. Use this to
33
- # get `prefab.get` and `prefab.isEnabled` functions on the window object.
34
- #
35
- # Only use this if you are not using the client SDK and do not need
36
- # client-side context.
37
- #
38
- # Any keys that could not be resolved will be logged as a warning to the
39
- # console.
40
- def generate_stub(context)
41
- configs, warnings = data(context)
23
+ def generate_stub(context, callback = nil)
24
+ configs, warnings = data(context, :stub)
42
25
  <<~JS
43
26
  window.prefab = window.prefab || {};
44
27
  window.prefab.config = #{JSON.dump(configs)};
45
28
  window.prefab.get = function(key) {
46
- return window.prefab.config[key];
29
+ var value = window.prefab.config[key];
30
+ #{callback && " #{callback}(key, value);"}
31
+ return value;
47
32
  };
48
33
  window.prefab.isEnabled = function(key) {
49
- return window.prefab.config[key] === true;
34
+ var value = window.prefab.config[key] === true;
35
+ #{callback && " #{callback}(key, value);"}
36
+ return value;
50
37
  };
51
38
  #{log_warnings(warnings)}
52
39
  JS
@@ -55,7 +42,7 @@ module Prefab
55
42
  private
56
43
 
57
44
  def underlying_value(value)
58
- v = Prefab::ConfigValueUnwrapper.new(value, @client.resolver).unwrap
45
+ v = Prefab::ConfigValueUnwrapper.new(value, @client.resolver).unwrap(raw_json: true)
59
46
  case v
60
47
  when Google::Protobuf::RepeatedField
61
48
  v.to_a
@@ -70,11 +57,11 @@ module Prefab
70
57
  return '' if warnings.empty?
71
58
 
72
59
  <<~JS
73
- console.warn('The following keys could not be resolved:', #{JSON.dump(@warnings)});
60
+ console.warn('The following keys could not be resolved:', #{JSON.dump(warnings)});
74
61
  JS
75
62
  end
76
63
 
77
- def data(context)
64
+ def data(context, mode)
78
65
  permitted = {}
79
66
  warnings = []
80
67
  resolver_keys = @client.resolver.keys
@@ -83,8 +70,13 @@ module Prefab
83
70
  begin
84
71
  config = @client.resolver.raw(key)
85
72
 
86
- if config.config_type == :FEATURE_FLAG || config.send_to_client_sdk
87
- permitted[key] = underlying_value(@client.resolver.get(key, context).value)
73
+ if config.config_type == :FEATURE_FLAG || config.send_to_client_sdk || config.config_type == :LOG_LEVEL
74
+ value = @client.resolver.get(key, context).value
75
+ if mode == :bootstrap
76
+ permitted[key] = { value: { to_camel_case(value.type) => underlying_value(value) } }
77
+ else
78
+ permitted[key] = underlying_value(value)
79
+ end
88
80
  end
89
81
  rescue StandardError => e
90
82
  LOG.warn("Could not resolve key #{key}: #{e}")
@@ -95,5 +87,13 @@ module Prefab
95
87
 
96
88
  [permitted, warnings]
97
89
  end
90
+
91
+ def to_camel_case(str)
92
+ CAMELS[str] ||= begin
93
+ str.to_s.split('_').map.with_index { |word, index|
94
+ index == 0 ? word : word.capitalize
95
+ }.join
96
+ end
97
+ end
98
98
  end
99
99
  end
@@ -66,6 +66,7 @@ module Prefab
66
66
  collect_evaluation_summaries: true,
67
67
  collect_max_evaluation_summaries: DEFAULT_MAX_EVAL_SUMMARIES,
68
68
  allow_telemetry_in_local_mode: false,
69
+ datafile: ENV['PREFAB_DATAFILE'],
69
70
  x_datafile: ENV['PREFAB_DATAFILE'],
70
71
  x_use_local_cache: false,
71
72
  global_context: {}
@@ -76,7 +77,13 @@ module Prefab
76
77
  @initialization_timeout_sec = initialization_timeout_sec
77
78
  @on_init_failure = on_init_failure
78
79
  @prefab_datasources = prefab_datasources
79
- @datafile = x_datafile
80
+
81
+ @datafile = datafile || x_datafile
82
+
83
+ if !x_datafile.nil?
84
+ warn '[DEPRECATION] x_datafile is deprecated. Please provide `datafile` instead'
85
+ end
86
+
80
87
  @prefab_config_classpath_dir = prefab_config_classpath_dir
81
88
  @prefab_config_override_dir = prefab_config_override_dir
82
89
  @prefab_envs = Array(prefab_envs)
data/lib/prefab/prefab.rb CHANGED
@@ -78,14 +78,40 @@ module Prefab
78
78
  @singleton.is_ff?(key)
79
79
  end
80
80
 
81
+ # Generate the JavaScript snippet to bootstrap the client SDK. This will
82
+ # include the configuration values that are permitted to be sent to the
83
+ # client SDK.
84
+ #
85
+ # If the context provided to the client SDK is not the same as the context
86
+ # used to generate the configuration values, the client SDK will still
87
+ # generate a fetch to get the correct values for the context.
88
+ #
89
+ # Any keys that could not be resolved will be logged as a warning to the
90
+ # console.
81
91
  def self.bootstrap_javascript(context)
82
92
  ensure_initialized
83
93
  Prefab::JavaScriptStub.new(@singleton).bootstrap(context)
84
94
  end
85
95
 
86
- def self.generate_javascript_stub(context)
96
+ # Generate the JavaScript snippet to *replace* the client SDK. Use this to
97
+ # get `prefab.get` and `prefab.isEnabled` functions on the window object.
98
+ #
99
+ # Only use this if you are not using the client SDK and do not need
100
+ # client-side context.
101
+ #
102
+ # Any keys that could not be resolved will be logged as a warning to the
103
+ # console.
104
+ #
105
+ # You can pass an optional callback function to be called with the key and
106
+ # value of each configuration value. This can be useful for logging,
107
+ # tracking experiment exposure, etc.
108
+ #
109
+ # e.g.
110
+ # - `Prefab.generate_javascript_stub(context, "reportExperimentExposure")`
111
+ # - `Prefab.generate_javascript_stub(context, "(key,value)=>{console.log({eval: 'eval', key,value})}")`
112
+ def self.generate_javascript_stub(context, callback = nil)
87
113
  ensure_initialized
88
- Prefab::JavaScriptStub.new(@singleton).generate_stub(context)
114
+ Prefab::JavaScriptStub.new(@singleton).generate_stub(context, callback)
89
115
  end
90
116
 
91
117
  private
@@ -106,7 +106,7 @@ module Prefab
106
106
  @source_index = 0
107
107
  end
108
108
 
109
- return @prefab_options.sse_sources[@source_index]
109
+ return @prefab_options.sse_sources[@source_index].sub(/(belt|suspenders)\./, 'stream.')
110
110
  end
111
111
  end
112
112
  end
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: prefab-cloud-ruby 1.8.3 ruby lib
5
+ # stub: prefab-cloud-ruby 1.8.5 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "prefab-cloud-ruby".freeze
9
- s.version = "1.8.3"
9
+ s.version = "1.8.5"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["Jeff Dwyer".freeze]
14
- s.date = "2024-09-16"
14
+ s.date = "2024-09-27"
15
15
  s.description = "Feature Flags, Live Config, and Dynamic Log Levels as a service".freeze
16
16
  s.email = "jdwyer@prefab.cloud".freeze
17
17
  s.extra_rdoc_files = [
@@ -84,6 +84,7 @@ Gem::Specification.new do |s|
84
84
  "prefab-cloud-ruby.gemspec",
85
85
  "test/.prefab.default.config.yaml",
86
86
  "test/.prefab.unit_tests.config.yaml",
87
+ "test/fixtures/datafile.json",
87
88
  "test/integration_test.rb",
88
89
  "test/integration_test_helpers.rb",
89
90
  "test/support/common_helpers.rb",
@@ -0,0 +1,87 @@
1
+ {
2
+ "configs": [
3
+ {
4
+ "id": "16825372746571694",
5
+ "projectId": "202",
6
+ "key": "log-level",
7
+ "changedBy": {
8
+ "email": "jeffrey.chupp@prefab.cloud"
9
+ },
10
+ "configType": "DELETED"
11
+ },
12
+ {
13
+ "id": "17271108409487302",
14
+ "projectId": "202",
15
+ "key": "flag.list.environments",
16
+ "changedBy": {
17
+ "email": "jeffrey.chupp@prefab.cloud"
18
+ },
19
+ "rows": [
20
+ {
21
+ "projectEnvId": "308",
22
+ "values": [
23
+ {
24
+ "criteria": [
25
+ {
26
+ "propertyName": "user.key",
27
+ "operator": "PROP_IS_ONE_OF",
28
+ "valueToMatch": {
29
+ "stringList": {
30
+ "values": [
31
+ "5905ecd1-9bbf-4711-a663-4f713628a78c"
32
+ ]
33
+ }
34
+ }
35
+ }
36
+ ],
37
+ "value": {
38
+ "bool": true
39
+ }
40
+ },
41
+ {
42
+ "value": {
43
+ "bool": true
44
+ }
45
+ }
46
+ ]
47
+ }
48
+ ],
49
+ "allowableValues": [
50
+ {
51
+ "bool": false
52
+ },
53
+ {
54
+ "bool": true
55
+ }
56
+ ],
57
+ "configType": "FEATURE_FLAG",
58
+ "valueType": "BOOL"
59
+ },
60
+ {
61
+ "id": "17271831941669987",
62
+ "projectId": "202",
63
+ "key": "my.test.string",
64
+ "changedBy": {
65
+ "userId": "3",
66
+ "apiKeyId": "481"
67
+ },
68
+ "rows": [
69
+ {
70
+ "values": [
71
+ {
72
+ "value": {
73
+ "string": "hello world"
74
+ }
75
+ }
76
+ ]
77
+ }
78
+ ],
79
+ "configType": "CONFIG",
80
+ "valueType": "STRING"
81
+ }
82
+ ],
83
+ "configServicePointer": {
84
+ "projectId": "202",
85
+ "projectEnvId": "308"
86
+ }
87
+ }
data/test/test_client.rb CHANGED
@@ -427,6 +427,14 @@ class TestClient < Minitest::Test
427
427
  refute client.is_ff?('does_not_exist')
428
428
  end
429
429
 
430
+ def test_with_datafile
431
+ datafile = "#{Dir.pwd}/test/fixtures/datafile.json"
432
+ client = new_client(datafile: datafile, prefab_datasources: :all)
433
+
434
+ assert client.get('flag.list.environments')
435
+ assert_equal "hello world", client.get('my.test.string')
436
+ end
437
+
430
438
  private
431
439
 
432
440
  def basic_value_config
@@ -48,6 +48,34 @@ class JavascriptStubTest < Minitest::Test
48
48
  rows: [DEFAULT_ROW]
49
49
  )
50
50
 
51
+ json_config = PrefabProto::Config.new(
52
+ id: 234,
53
+ key: 'json-config',
54
+ config_type: PrefabProto::ConfigType::CONFIG,
55
+ send_to_client_sdk: true,
56
+ rows: [
57
+ PrefabProto::ConfigRow.new(
58
+ values: [
59
+ PrefabProto::ConditionalValue.new(value: PrefabProto::ConfigValue.new(json: PrefabProto::Json.new(json: '{"key":"value"}')))
60
+ ]
61
+ )
62
+ ]
63
+ )
64
+
65
+ duration_config = PrefabProto::Config.new(
66
+ id: 236,
67
+ key: 'duration-config',
68
+ config_type: PrefabProto::ConfigType::CONFIG,
69
+ send_to_client_sdk: true,
70
+ rows: [
71
+ PrefabProto::ConfigRow.new(
72
+ values: [
73
+ PrefabProto::ConditionalValue.new(value: PrefabProto::ConfigValue.new(duration: PrefabProto::IsoDuration.new(definition: "P4DT12H30M5S")))
74
+ ]
75
+ )
76
+ ]
77
+ )
78
+
51
79
  ff = PrefabProto::Config.new(
52
80
  id: 456,
53
81
  key: 'feature-flag',
@@ -72,7 +100,7 @@ class JavascriptStubTest < Minitest::Test
72
100
  )
73
101
 
74
102
  @client = new_client(
75
- config: [log_level, config_for_sdk, config_not_for_sdk, ff],
103
+ config: [log_level, config_for_sdk, config_not_for_sdk, ff, json_config, duration_config],
76
104
  project_env_id: PROJECT_ENV_ID,
77
105
  collect_evaluation_summaries: true,
78
106
  prefab_config_override_dir: '/tmp',
@@ -85,20 +113,29 @@ class JavascriptStubTest < Minitest::Test
85
113
  def test_bootstrap
86
114
  result = Prefab::JavaScriptStub.new(@client).bootstrap({})
87
115
 
116
+
117
+ File.open('/tmp/prefab_config.json', 'w') do |f|
118
+ f.write(result)
119
+ end
88
120
  assert_equal %(
89
121
  window._prefabBootstrap = {
90
- configs: {"basic-config":"default_value","feature-flag":false},
122
+ evaluations: {"log-level":{"value":{"logLevel":"INFO"}},"basic-config":{"value":{"string":"default_value"}},"feature-flag":{"value":{"bool":false}},"json-config":{"value":{"json":"{\\"key\\":\\"value\\"}"}},"duration-config":{"value":{"duration":{"ms":390605000.0,"seconds":390605.0}}}},
91
123
  context: {}
92
124
  }
93
125
  ).strip, result.strip
94
126
 
95
127
  result = Prefab::JavaScriptStub.new(@client).bootstrap({ user: { email: 'gmail.com' } })
96
128
 
129
+ File.open('/tmp/prefab_config.json', 'w') do |f|
130
+ f.write(result)
131
+ end
132
+
97
133
  assert_equal %(
98
134
  window._prefabBootstrap = {
99
- configs: {"basic-config":"default_value","feature-flag":true},
135
+ evaluations: {"log-level":{"value":{"logLevel":"INFO"}},"basic-config":{"value":{"string":"default_value"}},"feature-flag":{"value":{"bool":true}},"json-config":{"value":{"json":"{\\"key\\":\\"value\\"}"}},"duration-config":{"value":{"duration":{"ms":390605000.0,"seconds":390605.0}}}},
100
136
  context: {"user":{"email":"gmail.com"}}
101
137
  }
138
+
102
139
  ).strip, result.strip
103
140
  end
104
141
 
@@ -107,25 +144,33 @@ window._prefabBootstrap = {
107
144
 
108
145
  assert_equal %(
109
146
  window.prefab = window.prefab || {};
110
- window.prefab.config = {"basic-config":"default_value","feature-flag":false};
147
+ window.prefab.config = {"log-level":"INFO","basic-config":"default_value","feature-flag":false,"json-config":"{\\"key\\":\\"value\\"}","duration-config":{"ms":390605000.0,"seconds":390605.0}};
111
148
  window.prefab.get = function(key) {
112
- return window.prefab.config[key];
149
+ var value = window.prefab.config[key];
150
+
151
+ return value;
113
152
  };
114
153
  window.prefab.isEnabled = function(key) {
115
- return window.prefab.config[key] === true;
154
+ var value = window.prefab.config[key] === true;
155
+
156
+ return value;
116
157
  };
117
158
  ).strip, result.strip
118
159
 
119
- result = Prefab::JavaScriptStub.new(@client).generate_stub({ user: { email: 'gmail.com' } })
160
+ result = Prefab::JavaScriptStub.new(@client).generate_stub({ user: { email: 'gmail.com' } }, 'myEvalCallback')
120
161
 
121
162
  assert_equal %(
122
163
  window.prefab = window.prefab || {};
123
- window.prefab.config = {"basic-config":"default_value","feature-flag":true};
164
+ window.prefab.config = {"log-level":"INFO","basic-config":"default_value","feature-flag":true,"json-config":"{\\"key\\":\\"value\\"}","duration-config":{"ms":390605000.0,"seconds":390605.0}};
124
165
  window.prefab.get = function(key) {
125
- return window.prefab.config[key];
166
+ var value = window.prefab.config[key];
167
+ myEvalCallback(key, value);
168
+ return value;
126
169
  };
127
170
  window.prefab.isEnabled = function(key) {
128
- return window.prefab.config[key] === true;
171
+ var value = window.prefab.config[key] === true;
172
+ myEvalCallback(key, value);
173
+ return value;
129
174
  };
130
175
 
131
176
  ).strip, result.strip
data/test/test_options.rb CHANGED
@@ -85,4 +85,9 @@ class TestOptions < Minitest::Test
85
85
  options = Prefab::Options.new(context_upload_mode: :none)
86
86
  assert_equal 0, options.collect_max_shapes
87
87
  end
88
+
89
+ def test_loading_a_datafile
90
+ options = Prefab::Options.new(datafile: "#{Dir.pwd}/test/fixtures/datafile.json")
91
+ assert_equal "#{Dir.pwd}/test/fixtures/datafile.json", options.datafile
92
+ end
88
93
  end
@@ -6,7 +6,7 @@ require 'webrick'
6
6
  class TestSSEConfigClient < Minitest::Test
7
7
  def test_client
8
8
  sources = [
9
- 'https://api.staging-prefab.cloud/'
9
+ 'https://belt.staging-prefab.cloud/'
10
10
  ]
11
11
 
12
12
  options = Prefab::Options.new(sources: sources, api_key: ENV.fetch('PREFAB_INTEGRATION_TEST_API_KEY', nil))
@@ -16,6 +16,7 @@ class TestSSEConfigClient < Minitest::Test
16
16
  client = Prefab::SSEConfigClient.new(options, config_loader)
17
17
 
18
18
  assert_equal 4, client.headers['x-prefab-start-at-id']
19
+ assert_equal "https://stream.staging-prefab.cloud", client.source
19
20
 
20
21
  result = nil
21
22
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prefab-cloud-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.3
4
+ version: 1.8.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Dwyer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-16 00:00:00.000000000 Z
11
+ date: 2024-09-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -285,6 +285,7 @@ files:
285
285
  - prefab-cloud-ruby.gemspec
286
286
  - test/.prefab.default.config.yaml
287
287
  - test/.prefab.unit_tests.config.yaml
288
+ - test/fixtures/datafile.json
288
289
  - test/integration_test.rb
289
290
  - test/integration_test_helpers.rb
290
291
  - test/support/common_helpers.rb