remocon 0.1.0 → 0.2.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.
- checksums.yaml +5 -5
- data/.gitignore +2 -0
- data/.rubocop.yml +133 -25
- data/.ruby-version +1 -1
- data/.travis.yml +9 -4
- data/Gemfile +1 -1
- data/Gemfile.lock +24 -22
- data/README.md +29 -7
- data/Rakefile +15 -2
- data/bin/console +3 -3
- data/bin/get_access_token +2 -0
- data/bin/get_access_token.py +2 -2
- data/cmd/remocon +1 -1
- data/lib/remocon.rb +46 -45
- data/lib/remocon/cli.rb +27 -15
- data/lib/remocon/command/create_command.rb +16 -16
- data/lib/remocon/command/lib/config.rb +82 -0
- data/lib/remocon/command/lib/interpreter_helper.rb +13 -3
- data/lib/remocon/command/pull_command.rb +34 -35
- data/lib/remocon/command/push_command.rb +27 -28
- data/lib/remocon/command/validate_command.rb +19 -9
- data/lib/remocon/interpreter/condition_file_interpreter.rb +10 -8
- data/lib/remocon/interpreter/parameter_file_interpreter.rb +12 -10
- data/lib/remocon/normalizer/json_normalizer.rb +2 -1
- data/lib/remocon/normalizer/normalizer.rb +1 -1
- data/lib/remocon/sorter/condition_sorter.rb +2 -2
- data/lib/remocon/sorter/parameter_sorter.rb +3 -3
- data/lib/remocon/util/array.rb +2 -2
- data/lib/remocon/util/hash.rb +2 -2
- data/lib/remocon/util/string.rb +1 -1
- data/lib/remocon/version.rb +1 -1
- data/prj/config.json +34 -0
- data/remocon.gemspec +20 -21
- metadata +20 -18
@@ -3,19 +3,10 @@
|
|
3
3
|
module Remocon
|
4
4
|
module Command
|
5
5
|
class Push
|
6
|
-
attr_reader :
|
6
|
+
attr_reader :config, :cmd_opts
|
7
7
|
|
8
8
|
def initialize(opts)
|
9
|
-
@
|
10
|
-
|
11
|
-
@project_id = ENV.fetch('FIREBASE_PROJECT_ID')
|
12
|
-
@token = ENV.fetch('REMOTE_CONFIG_ACCESS_TOKEN')
|
13
|
-
@uri = URI.parse("https://firebaseremoteconfig.googleapis.com/v1/projects/#{@project_id}/remoteConfig")
|
14
|
-
@source_filepath = @opts[:source]
|
15
|
-
@etag = File.exist?(@opts[:etag]) ? File.open(@opts[:etag]).read : @opts[:etag] if @opts[:etag]
|
16
|
-
@ignore_etag = @opts[:force]
|
17
|
-
@dest_dir = File.join(@opts[:dest], @project_id) if @opts[:dest]
|
18
|
-
|
9
|
+
@config = Remocon::Config.new(opts)
|
19
10
|
@cmd_opts = { validate_only: false }
|
20
11
|
end
|
21
12
|
|
@@ -36,23 +27,27 @@ module Remocon
|
|
36
27
|
def request
|
37
28
|
return @request if @request
|
38
29
|
|
39
|
-
raise
|
30
|
+
raise "etag should be specified. If you want to ignore this error, then add --force option" unless config.etag
|
40
31
|
|
41
32
|
headers = {
|
42
|
-
|
43
|
-
|
33
|
+
"Authorization" => "Bearer #{config.token}",
|
34
|
+
"Content-Type" => "application/json; UTF8",
|
35
|
+
"If-Match" => config.etag,
|
44
36
|
}
|
45
|
-
headers['If-Match'] = @etag || '*'
|
46
37
|
|
47
38
|
request = Net::HTTP::Put.new(uri.request_uri, headers)
|
48
39
|
request.body = ""
|
49
|
-
request.body << File.read(
|
40
|
+
request.body << File.read(config.config_json_file_path).delete("\r\n")
|
50
41
|
|
51
42
|
@request = request
|
52
43
|
end
|
53
44
|
|
54
45
|
private
|
55
46
|
|
47
|
+
def uri
|
48
|
+
@uri ||= URI.parse(config.endpoint)
|
49
|
+
end
|
50
|
+
|
56
51
|
def do_request
|
57
52
|
response = client.request(request)
|
58
53
|
|
@@ -65,28 +60,32 @@ module Remocon
|
|
65
60
|
when Net::HTTPOK
|
66
61
|
parse_success_body(response, response_body)
|
67
62
|
# intentional behavior
|
68
|
-
STDERR.puts
|
63
|
+
STDERR.puts "Updated successfully."
|
69
64
|
when Net::HTTPBadRequest
|
70
65
|
# sent json contains errors
|
71
66
|
parse_error_body(response, response_body) if response_body
|
72
|
-
STDERR.puts
|
67
|
+
STDERR.puts "400 but no error body" unless response_body
|
73
68
|
when Net::HTTPUnauthorized
|
74
69
|
# token was expired
|
75
|
-
STDERR.puts
|
70
|
+
STDERR.puts "401 Unauthorized. A token might be expired or invalid."
|
76
71
|
when Net::HTTPForbidden
|
77
72
|
# remote config api might be disabled or not yet activated
|
78
|
-
STDERR.puts
|
73
|
+
STDERR.puts "403 Forbidden. RemoteConfig API might not be activated or be disabled."
|
79
74
|
when Net::HTTPConflict
|
80
75
|
# local content is out-to-date
|
81
|
-
STDERR.puts
|
76
|
+
STDERR.puts "409 Conflict. Remote was updated. Please update your local files"
|
82
77
|
end
|
78
|
+
|
79
|
+
response.kind_of?(Net::HTTPOK)
|
83
80
|
end
|
84
81
|
|
85
82
|
def parse_success_body(response, _success_body)
|
86
|
-
|
83
|
+
etag = response.header["etag"]
|
84
|
+
|
85
|
+
return unless etag
|
87
86
|
|
88
|
-
if
|
89
|
-
File.open(
|
87
|
+
if config.project_dir_path
|
88
|
+
File.open(config.etag_file_path, "w+") do |f|
|
90
89
|
f.write(etag)
|
91
90
|
f.flush
|
92
91
|
end
|
@@ -101,13 +100,13 @@ module Remocon
|
|
101
100
|
|
102
101
|
error_body.dig(:error, :details)&.each do |k|
|
103
102
|
# for now, see only errors below
|
104
|
-
next unless k[
|
103
|
+
next unless k["@type"] == "type.googleapis.com/google.rpc.BadRequest"
|
105
104
|
|
106
105
|
k[:fieldViolations].each do |e|
|
107
|
-
if e[:field].start_with?(
|
108
|
-
STDERR.puts
|
106
|
+
if e[:field].start_with?("remote_config.conditions")
|
107
|
+
STDERR.puts "CONDITION DEFINITION ERROR"
|
109
108
|
else
|
110
|
-
STDERR.puts
|
109
|
+
STDERR.puts "PARAMETER DEFINITION ERROR"
|
111
110
|
end
|
112
111
|
|
113
112
|
STDERR.puts e[:description]
|
@@ -5,33 +5,43 @@ module Remocon
|
|
5
5
|
class Validate
|
6
6
|
include Remocon::InterpreterHelper
|
7
7
|
|
8
|
+
attr_reader :config, :cmd_opts
|
9
|
+
|
8
10
|
def initialize(opts)
|
9
|
-
@
|
11
|
+
@config = Remocon::Config.new(opts)
|
12
|
+
@cmd_opts = { validate_only: true }
|
13
|
+
end
|
10
14
|
|
11
|
-
|
12
|
-
|
15
|
+
def require_parameters_file_path
|
16
|
+
config.parameters_file_path
|
17
|
+
end
|
13
18
|
|
14
|
-
|
19
|
+
def require_conditions_file_path
|
20
|
+
config.conditions_file_path
|
15
21
|
end
|
16
22
|
|
17
23
|
def run
|
18
24
|
validate_options
|
19
25
|
|
20
|
-
|
21
|
-
|
26
|
+
errors = parameter_errors + condition_errors
|
27
|
+
|
28
|
+
if errors.empty?
|
29
|
+
STDOUT.puts "No error was found."
|
22
30
|
else
|
23
|
-
|
31
|
+
errors.each do |e|
|
24
32
|
STDERR.puts "#{e.class} #{e.message}"
|
25
33
|
STDERR.puts e.backtrace.join("\n")
|
26
34
|
end
|
27
35
|
end
|
36
|
+
|
37
|
+
errors.empty?
|
28
38
|
end
|
29
39
|
|
30
40
|
private
|
31
41
|
|
32
42
|
def validate_options
|
33
|
-
raise ValidationError,
|
34
|
-
raise ValidationError,
|
43
|
+
raise ValidationError, "A condition file must exist" unless File.exist?(config.conditions_file_path)
|
44
|
+
raise ValidationError, "A parameter file must exist" unless File.exist?(config.parameters_file_path)
|
35
45
|
end
|
36
46
|
end
|
37
47
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Remocon
|
4
4
|
class ConditionFileInterpreter
|
5
|
-
SUPPORTED_ATTRIBUTED = %i
|
5
|
+
SUPPORTED_ATTRIBUTED = %i(name expression tagColor).freeze
|
6
6
|
|
7
7
|
def initialize(filepath)
|
8
8
|
# conditions should be Array
|
@@ -16,14 +16,16 @@ module Remocon
|
|
16
16
|
keys = []
|
17
17
|
|
18
18
|
@yaml.each do |hash|
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
begin
|
20
|
+
raise Remocon::EmptyNameError, "name must not be empty" unless hash[:name]
|
21
|
+
raise Remocon::EmptyExpressionError, "expression must not be empty" unless hash[:expression]
|
22
|
+
raise Remocon::DuplicateKeyError, "#{hash[:name]} is duplicated" if keys.include?(hash[:name])
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
keys.push(hash[:name])
|
25
|
+
rescue Remocon::ValidationError => e
|
26
|
+
raise e unless opts[:validate_only]
|
27
|
+
errors.push(e)
|
28
|
+
end
|
27
29
|
end
|
28
30
|
|
29
31
|
[json_array, errors]
|
@@ -9,19 +9,21 @@ module Remocon
|
|
9
9
|
def read(condition_names, opts = {})
|
10
10
|
errors = []
|
11
11
|
json_hash = @yaml.each_with_object({}) do |(key, body), hash|
|
12
|
-
|
12
|
+
begin
|
13
|
+
raise Remocon::DuplicateKeyError, "#{key} is duplicated" if hash[key]
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
hash[key] = {
|
16
|
+
defaultValue: {
|
17
|
+
value: parse_value_body(key, body)
|
18
|
+
}
|
17
19
|
}
|
18
|
-
}
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
hash[key][:conditionalValues] = parse_condition_body(condition_names, key, body[:conditions]) if body[:conditions]
|
22
|
+
hash[key][:description] = body[:description] if body[:description]
|
23
|
+
rescue Remocon::ValidationError => e
|
24
|
+
raise e unless opts[:validate_only]
|
25
|
+
errors.push(e)
|
26
|
+
end
|
25
27
|
end
|
26
28
|
|
27
29
|
[json_hash.with_indifferent_access, errors]
|
@@ -7,7 +7,8 @@ module Remocon
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def validate
|
10
|
-
str_content = @content.
|
10
|
+
str_content = @content.kind_of?(Hash) ? @content.to_json : @content.to_s
|
11
|
+
puts str_content
|
11
12
|
@json = JSON.parse(str_content).to_json
|
12
13
|
rescue JSON::ParserError => e
|
13
14
|
raise ValidationError, e.message
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Remocon
|
4
4
|
module ConditionSorter
|
5
|
-
CONDITION_KEYS = %i
|
5
|
+
CONDITION_KEYS = %i(name expression tagColor).freeze
|
6
6
|
|
7
7
|
def sort_conditions(conditions)
|
8
8
|
conditions
|
@@ -12,7 +12,7 @@ module Remocon
|
|
12
12
|
if !CONDITION_KEYS.include?(a) && !CONDITION_KEYS.include?(b)
|
13
13
|
a <=> b
|
14
14
|
else
|
15
|
-
(CONDITION_KEYS.index(a) ||
|
15
|
+
(CONDITION_KEYS.index(a) || 10_000) <=> (CONDITION_KEYS.index(b) || 10_000)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
@@ -2,14 +2,14 @@
|
|
2
2
|
|
3
3
|
module Remocon
|
4
4
|
module ParameterSorter
|
5
|
-
PARAMETER_KEYS = %i
|
5
|
+
PARAMETER_KEYS = %i(description value file normalizer conditions options).freeze
|
6
6
|
|
7
7
|
def sort_parameters(parameters)
|
8
8
|
arr = parameters.sort.map do |k, v|
|
9
9
|
hash_arr = v.sort { |(a, _), (b, _)| PARAMETER_KEYS.index(a) <=> PARAMETER_KEYS.index(b) }
|
10
|
-
|
10
|
+
.map do |k1, v1|
|
11
11
|
{
|
12
|
-
|
12
|
+
k1 => k1.to_sym == :conditions ? sort_parameters(v1) : v1
|
13
13
|
}
|
14
14
|
end
|
15
15
|
|
data/lib/remocon/util/array.rb
CHANGED
data/lib/remocon/util/hash.rb
CHANGED
data/lib/remocon/util/string.rb
CHANGED
data/lib/remocon/version.rb
CHANGED
data/prj/config.json
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
{
|
2
|
+
"conditions": [
|
3
|
+
{
|
4
|
+
"expression": "device.os == 'ios'",
|
5
|
+
"name": "condition1",
|
6
|
+
"tagColor": "INDIGO"
|
7
|
+
},
|
8
|
+
{
|
9
|
+
"expression": "device.os == 'ios'",
|
10
|
+
"name": "zxczx",
|
11
|
+
"tagColor": "CYAN"
|
12
|
+
}
|
13
|
+
],
|
14
|
+
"parameters": {
|
15
|
+
"key1": {
|
16
|
+
"defaultValue": {
|
17
|
+
"value": "100"
|
18
|
+
},
|
19
|
+
"conditionalValues": {
|
20
|
+
"condition1": {
|
21
|
+
"value": "200"
|
22
|
+
},
|
23
|
+
"zxczx": {
|
24
|
+
"value": "100"
|
25
|
+
}
|
26
|
+
}
|
27
|
+
},
|
28
|
+
"key2": {
|
29
|
+
"defaultValue": {
|
30
|
+
"value": "123"
|
31
|
+
}
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
data/remocon.gemspec
CHANGED
@@ -1,42 +1,41 @@
|
|
1
|
-
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
|
-
lib = File.expand_path(
|
3
|
+
lib = File.expand_path("lib", __dir__)
|
5
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
6
|
-
require
|
5
|
+
require "remocon/version"
|
7
6
|
|
8
7
|
Gem::Specification.new do |spec|
|
9
|
-
spec.name =
|
8
|
+
spec.name = "remocon"
|
10
9
|
spec.version = Remocon::VERSION
|
11
|
-
spec.authors = [
|
12
|
-
spec.email = [
|
10
|
+
spec.authors = ["Jumpei Matsuda"]
|
11
|
+
spec.email = ["jmatsu.drm@gmail.com"]
|
13
12
|
|
14
|
-
spec.summary =
|
13
|
+
spec.summary = "YAML-based firebase remote config manager"
|
15
14
|
spec.description = "This gem manages RemoteConfig's key-values based on YAML configuration."
|
16
|
-
spec.homepage =
|
17
|
-
spec.license =
|
15
|
+
spec.homepage = "https://github.com/jmatsu/remocon"
|
16
|
+
spec.license = "MIT"
|
18
17
|
|
19
18
|
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
20
19
|
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
21
20
|
if spec.respond_to?(:metadata)
|
22
|
-
spec.metadata[
|
21
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
23
22
|
else
|
24
|
-
raise
|
25
|
-
|
23
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
24
|
+
"public gem pushes."
|
26
25
|
end
|
27
26
|
|
28
27
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
29
28
|
f.match(%r{^(test|spec|features)/})
|
30
29
|
end
|
31
|
-
spec.bindir =
|
30
|
+
spec.bindir = "cmd"
|
32
31
|
spec.executables = spec.files.grep(%r{^cmd/}) { |f| File.basename(f) }
|
33
|
-
spec.require_paths = [
|
32
|
+
spec.require_paths = ["lib"]
|
34
33
|
|
35
|
-
spec.add_dependency
|
36
|
-
spec.add_dependency
|
37
|
-
spec.add_development_dependency
|
38
|
-
spec.add_development_dependency
|
39
|
-
spec.add_development_dependency
|
40
|
-
spec.add_development_dependency
|
41
|
-
spec.add_development_dependency
|
34
|
+
spec.add_dependency "activesupport"
|
35
|
+
spec.add_dependency "thor"
|
36
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
37
|
+
spec.add_development_dependency "pry"
|
38
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
39
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
40
|
+
spec.add_development_dependency "rubocop"
|
42
41
|
end
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: remocon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jumpei Matsuda
|
8
8
|
autorequire:
|
9
9
|
bindir: cmd
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-08-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: activesupport
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
@@ -25,7 +25,7 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: thor
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
@@ -53,47 +53,47 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '1.16'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: pry
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: rake
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
75
|
+
version: '10.0'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
82
|
+
version: '10.0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: rspec
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - "
|
87
|
+
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
89
|
+
version: '3.0'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - "
|
94
|
+
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
96
|
+
version: '3.0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: rubocop
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -136,6 +136,7 @@ files:
|
|
136
136
|
- lib/remocon.rb
|
137
137
|
- lib/remocon/cli.rb
|
138
138
|
- lib/remocon/command/create_command.rb
|
139
|
+
- lib/remocon/command/lib/config.rb
|
139
140
|
- lib/remocon/command/lib/interpreter_helper.rb
|
140
141
|
- lib/remocon/command/pull_command.rb
|
141
142
|
- lib/remocon/command/push_command.rb
|
@@ -160,6 +161,7 @@ files:
|
|
160
161
|
- lib/remocon/util/hash.rb
|
161
162
|
- lib/remocon/util/string.rb
|
162
163
|
- lib/remocon/version.rb
|
164
|
+
- prj/config.json
|
163
165
|
- remocon.gemspec
|
164
166
|
- sample/basketball-b8548/conditions.yml
|
165
167
|
- sample/basketball-b8548/config.json
|
@@ -186,7 +188,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
186
188
|
version: '0'
|
187
189
|
requirements: []
|
188
190
|
rubyforge_project:
|
189
|
-
rubygems_version: 2.
|
191
|
+
rubygems_version: 2.5.2.3
|
190
192
|
signing_key:
|
191
193
|
specification_version: 4
|
192
194
|
summary: YAML-based firebase remote config manager
|