remocon 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|