remocon 0.3.1 → 0.4.0.pre.1

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
  SHA1:
3
- metadata.gz: 0c79702f9f1b2f149cf70b67ad75502787d3602d
4
- data.tar.gz: 072c77a91b9fd05e41434b3f98ad4171aba3e640
3
+ metadata.gz: fb536a401c5e10685550492b77a5cda9ac02a001
4
+ data.tar.gz: 51f618bb2ea5efc99a418a057ac90b1bc5965cca
5
5
  SHA512:
6
- metadata.gz: a67a7225afe9bad7771de4fad15f814ce0ace4377969e506ef296744edb03db9176dae6ebe28b94eaf1cc70f1789638ef0023cc89a455222a16dfc9950136fcd
7
- data.tar.gz: f295af07b3bd976cd35abbf331f32709a72a9a3dc41364e7ec872e70941137840e1f01b0fe65aaf6808f7925981af7909bf0af0f25d9c2cf63d015df9db554f7
6
+ metadata.gz: eb31b664990aa124faece75e0d8cd4d0f789ce3c176e0b62b4348c7a69bb8b88a0854f1bebd6afc51744f00103995d6e2b7a4d249a7d382461cf2df79117be80
7
+ data.tar.gz: ad58cfeee9e91c53f8a2a21927298d384a662f520de1f607b9d94b4b2561c0954904b6cbbfb3a2a7ec034b0db91e773e93361da42468a61e8055cfe601de29fb
data/.rubocop.yml CHANGED
@@ -136,7 +136,7 @@ Metrics/BlockLength:
136
136
  Style/MixinGrouping:
137
137
  Enabled: false
138
138
 
139
- Style/FileName:
139
+ Naming:
140
140
  Enabled: false
141
141
 
142
142
  Layout/IndentHeredoc:
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- remocon (0.3.1)
4
+ remocon (0.4.0.pre.1)
5
5
  activesupport
6
6
  googleauth
7
7
  thor
data/lib/remocon.rb CHANGED
@@ -40,6 +40,7 @@ require "remocon/normalizer/type_normalizer_factory"
40
40
 
41
41
  require "remocon/command/lib/config"
42
42
  require "remocon/command/lib/interpreter_helper"
43
+ require "remocon/command/lib/request"
43
44
 
44
45
  require "remocon/command/create_command"
45
46
  require "remocon/command/get_token_command"
data/lib/remocon/cli.rb CHANGED
@@ -33,6 +33,7 @@ module Remocon
33
33
  end
34
34
 
35
35
  desc "pull", "Pull remote configs"
36
+ option :merge, type: :boolean, default: true, desc: "use the hash merge algorithm if true. default is true."
36
37
  option :prefix, type: :string, desc: "the directory name which will contain project-related files"
37
38
  option :token, type: :string, desc: "access token to your project"
38
39
  option :id, type: :string, desc: "your project id"
@@ -82,5 +82,9 @@ module Remocon
82
82
  opts[:force] && "*" || opts[:"raw-etag"] || File.exist?(etag_file_path) && File.open(etag_file_path).read
83
83
  end
84
84
  end
85
+
86
+ def merge?
87
+ @merge ||= opts[:merge]
88
+ end
85
89
  end
86
90
  end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Remocon
4
+ module Request
5
+ def self.push(config)
6
+ raise "etag should be specified. If you want to ignore this error, then please add --force option" unless config.etag
7
+
8
+ client, uri = Request.build_client(config)
9
+
10
+ headers = {
11
+ "Authorization" => "Bearer #{config.token}",
12
+ "Content-Type" => "application/json; UTF8",
13
+ "If-Match" => config.etag,
14
+ }
15
+
16
+ request = Net::HTTP::Put.new(uri.request_uri, headers)
17
+ request.body = +""
18
+ request.body << File.read(config.config_json_file_path).delete("\r\n")
19
+
20
+ response = client.request(request)
21
+
22
+ response_body = begin
23
+ json_str = response.try(:read_body)
24
+ (json_str ? JSON.parse(json_str) : {}).with_indifferent_access
25
+ end
26
+
27
+ return response, response_body
28
+ end
29
+
30
+ def self.pull(config)
31
+ raw_json, etag = open(config.endpoint, "Authorization" => "Bearer #{config.token}") do |io|
32
+ [io.read, io.meta["etag"]]
33
+ end
34
+
35
+ [raw_json, etag]
36
+ end
37
+
38
+ def self.fetch_etag(config)
39
+ # remote config api doesn't support head request so we need to use GET instead.
40
+
41
+ client, uri = Request.build_client(config)
42
+
43
+ headers = {
44
+ "Authorization" => "Bearer #{config.token}",
45
+ "Content-Type" => "application/json; UTF8",
46
+ "Content-Encoding" => "gzip",
47
+ }
48
+
49
+ request = Net::HTTP::Get.new(uri.request_uri, headers)
50
+ response = client.request(request)
51
+
52
+ response.kind_of?(Net::HTTPOK) && response.header["etag"]
53
+ end
54
+
55
+ def self.build_client(config)
56
+ uri = URI.parse(config.endpoint)
57
+ client = Net::HTTP.new(uri.host, uri.port)
58
+ client.use_ssl = true
59
+ return client, uri
60
+ end
61
+ end
62
+ end
@@ -3,13 +3,40 @@
3
3
  module Remocon
4
4
  module Command
5
5
  class Pull
6
+ class RemoteConfig
7
+ include Remocon::InterpreterHelper
8
+
9
+ attr_reader :config
10
+
11
+ def initialize(opts)
12
+ @config = Remocon::Config.new(opts)
13
+ end
14
+
15
+ def require_parameters_file_path
16
+ config.parameters_file_path
17
+ end
18
+
19
+ def require_conditions_file_path
20
+ config.conditions_file_path
21
+ end
22
+
23
+ def raw_conditions
24
+ YAML.safe_load(File.open(require_conditions_file_path).read).map(&:with_indifferent_access)
25
+ end
26
+
27
+ def raw_parameters
28
+ YAML.safe_load(File.open(require_parameters_file_path).read).with_indifferent_access
29
+ end
30
+ end
31
+
6
32
  include Remocon::InterpreterHelper
7
33
 
8
- attr_reader :config, :cmd_opts
34
+ attr_reader :config, :cmd_opts, :left
9
35
 
10
36
  def initialize(opts)
11
37
  @config = Remocon::Config.new(opts)
12
38
  @cmd_opts = { validate_only: false }
39
+ @left = RemoteConfig.new(opts)
13
40
  end
14
41
 
15
42
  def require_parameters_file_path
@@ -21,7 +48,7 @@ module Remocon
21
48
  end
22
49
 
23
50
  def run
24
- raw_json, etag = do_request
51
+ raw_json, etag = Remocon::Request.pull(config)
25
52
 
26
53
  raw_hash = JSON.parse(raw_json).with_indifferent_access
27
54
 
@@ -30,18 +57,100 @@ module Remocon
30
57
  conditions = raw_hash[:conditions] || []
31
58
  parameters = raw_hash[:parameters] || {}
32
59
 
60
+ if config.merge? && File.exist?(config.parameters_file_path) && File.exist?(config.parameters_file_path)
61
+ unchanged_conditions, added_conditions, changed_conditions, = conditions_diff(left.raw_conditions, conditions)
62
+ unchanged_parameters, added_parameters, changed_parameters, = parameters_diff(left.raw_parameters, parameters)
63
+
64
+ conditions_yaml = JSON.parse(sort_conditions(unchanged_conditions + added_conditions + changed_conditions).to_json).to_yaml
65
+ parameters_yaml = JSON.parse(sort_parameters(unchanged_parameters.merge(added_parameters).merge(changed_parameters)).to_json).to_yaml
66
+ else
67
+ conditions_yaml = JSON.parse(Remocon::ConditionFileDumper.new(sort_conditions(conditions)).dump.to_json).to_yaml
68
+ parameters_yaml = JSON.parse(Remocon::ParameterFileDumper.new(sort_parameters(parameters)).dump.to_json).to_yaml
69
+ end
70
+
71
+ write_to_files(conditions_yaml, parameters_yaml, etag)
72
+ end
73
+
74
+ def conditions_diff(left, right)
75
+ left_names = left.map { |c| c[:name] }
76
+ right_names = right.map { |c| c[:name] }
77
+
78
+ added_names = right_names - left_names
79
+ removed_names = left_names - right_names
80
+
81
+ added = added_names.each_with_object([]) do |k, acc|
82
+ acc.push(right.find { |c| c[:name] == k })
83
+ end
84
+
85
+ changed = []
86
+ unchanged = []
87
+
88
+ (right_names & left_names).each do |k|
89
+ old = left.find { |c| c[:name] == k }
90
+ new = Remocon::ConditionFileDumper.new(right.find { |c| c[:name] == k }).dump
91
+
92
+ if old == new
93
+ unchanged.push(old)
94
+ else
95
+ changed.push(new)
96
+ end
97
+ end
98
+
99
+ removed = []
100
+
101
+ removed_names.each do |k|
102
+ removed.push(left.find { |c| c[:name] == k })
103
+ end
104
+
105
+ [unchanged, added, changed, removed]
106
+ end
107
+
108
+ def parameters_diff(left, right)
109
+ added_keys = right.keys - left.keys
110
+ removed_keys = left.keys - right.keys
111
+
112
+ added = added_keys.each_with_object({}) do |k, acc|
113
+ acc.merge!(Remocon::ParameterFileDumper.new({ k => right[k] }).dump)
114
+ end
115
+
116
+ changed = {}
117
+ unchanged = {}
118
+
119
+ (right.keys & left.keys).each do |k|
120
+ old = { k => left[k] }
121
+ new = Remocon::ParameterFileDumper.new({ k => right[k] }).dump
122
+
123
+ if old == new
124
+ unchanged.merge!(old)
125
+ else
126
+ changed.merge!(new)
127
+ end
128
+ end
129
+
130
+ removed = {}
131
+
132
+ removed_keys.each do |k|
133
+ removed[k] = left[k]
134
+ end
135
+
136
+ [unchanged, added, changed, removed]
137
+ end
138
+
139
+ private
140
+
141
+ def write_to_files(conditions_yaml, parameters_yaml, etag)
33
142
  File.open(config.conditions_file_path, "w+") do |f|
34
- f.write(JSON.parse(Remocon::ConditionFileDumper.new(sort_conditions(conditions)).dump.to_json).to_yaml)
143
+ f.write(conditions_yaml)
35
144
  f.flush
36
145
  end
37
146
 
38
147
  File.open(config.parameters_file_path, "w+") do |f|
39
- f.write(JSON.parse(Remocon::ParameterFileDumper.new(sort_parameters(parameters)).dump.to_json).to_yaml)
148
+ f.write(parameters_yaml)
40
149
  f.flush
41
150
  end
42
151
 
43
152
  File.open(config.config_json_file_path, "w+") do |f|
44
- f.write(JSON.pretty_generate({ conditions: sort_conditions(conditions), parameters: sort_parameters(parameters) }))
153
+ f.write(JSON.pretty_generate({ conditions: condition_array, parameters: parameter_hash }))
45
154
  f.flush
46
155
  end
47
156
 
@@ -50,16 +159,6 @@ module Remocon
50
159
  f.flush
51
160
  end
52
161
  end
53
-
54
- private
55
-
56
- def do_request
57
- raw_json, etag = open(config.endpoint, "Authorization" => "Bearer #{config.token}") do |io|
58
- [io.read, io.meta["etag"]]
59
- end
60
-
61
- [raw_json, etag]
62
- end
63
162
  end
64
163
  end
65
164
  end
@@ -1,4 +1,4 @@
1
- # frozen_string_literal: false
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Remocon
4
4
  module Command
@@ -11,72 +11,23 @@ module Remocon
11
11
  end
12
12
 
13
13
  def run
14
- # to prevent a real request in spec
15
14
  do_request
16
15
  end
17
16
 
18
- def client
19
- return @client if @client
20
-
21
- client = Net::HTTP.new(uri.host, uri.port)
22
- client.use_ssl = true
23
-
24
- @client = client
25
- end
26
-
27
- def request
28
- return @request if @request
29
-
30
- raise "etag should be specified. If you want to ignore this error, then add --force option" unless config.etag
31
-
32
- headers = {
33
- "Authorization" => "Bearer #{config.token}",
34
- "Content-Type" => "application/json; UTF8",
35
- "If-Match" => config.etag,
36
- }
37
-
38
- request = Net::HTTP::Put.new(uri.request_uri, headers)
39
- request.body = ""
40
- request.body << File.read(config.config_json_file_path).delete("\r\n")
41
-
42
- @request = request
43
- end
44
-
45
17
  private
46
18
 
47
- def uri
48
- @uri ||= URI.parse(config.endpoint)
49
- end
50
-
51
19
  def do_request
52
- response = client.request(request)
20
+ response, response_body = Remocon::Request.push(config)
53
21
 
54
- response_body = begin
55
- json_str = response&.read_body
56
- JSON.parse(json_str).with_indifferent_access if json_str
57
- end
58
-
59
- case response
60
- when Net::HTTPOK
61
- parse_success_body(response, response_body)
62
- # intentional behavior
63
- STDERR.puts "Updated successfully."
64
- when Net::HTTPBadRequest
65
- # sent json contains errors
66
- parse_error_body(response, response_body) if response_body
67
- STDERR.puts "400 but no error body" unless response_body
68
- when Net::HTTPUnauthorized
69
- # token was expired
70
- STDERR.puts "401 Unauthorized. A token might be expired or invalid."
71
- when Net::HTTPForbidden
72
- # remote config api might be disabled or not yet activated
73
- STDERR.puts "403 Forbidden. RemoteConfig API might not be activated or be disabled."
74
- when Net::HTTPConflict
75
- # local content is out-to-date
76
- STDERR.puts "409 Conflict. Remote was updated. Please update your local files"
22
+ (response.kind_of?(Net::HTTPOK) && parse_success_body(response, response_body)).tap do |result|
23
+ unless result
24
+ if response_body.blank?
25
+ STDERR.puts "No error body"
26
+ else
27
+ parse_error_body(response, response_body)
28
+ end
29
+ end
77
30
  end
78
-
79
- response.kind_of?(Net::HTTPOK)
80
31
  end
81
32
 
82
33
  def parse_success_body(response, _success_body)
@@ -84,19 +35,18 @@ module Remocon
84
35
 
85
36
  return unless etag
86
37
 
87
- if config.project_dir_path
88
- File.open(config.etag_file_path, "w+") do |f|
89
- f.write(etag)
90
- f.flush
91
- end
92
- else
93
- STDOUT.puts etag
38
+ File.open(config.etag_file_path, "w+") do |f|
39
+ f.write(etag)
40
+ f.flush
94
41
  end
42
+
43
+ STDOUT.puts(etag)
44
+ true
95
45
  end
96
46
 
97
47
  def parse_error_body(_response, error_body)
98
- STDERR.puts "Error name : #{error_body[:error][:status]}"
99
- STDERR.puts "Please check your json file"
48
+ STDERR.puts error_body[:error][:status]
49
+ STDERR.puts error_body[:error][:message]
100
50
 
101
51
  error_body.dig(:error, :details)&.each do |k|
102
52
  # for now, see only errors below
@@ -8,7 +8,7 @@ module Remocon
8
8
  attr_reader :config, :cmd_opts
9
9
 
10
10
  def initialize(opts)
11
- @config = Remocon::Config.new(opts)
11
+ @config = Remocon::Config.new(opts.merge(force: false))
12
12
  @cmd_opts = { validate_only: true }
13
13
  end
14
14
 
@@ -23,18 +23,22 @@ module Remocon
23
23
  def run
24
24
  validate_options
25
25
 
26
- errors = parameter_errors + condition_errors
26
+ errors = parameter_errors + condition_errors + etag_errors
27
27
 
28
+ print_errors(errors)
29
+
30
+ errors.empty?
31
+ end
32
+
33
+ def print_errors(errors)
28
34
  if errors.empty?
29
35
  STDOUT.puts "No error was found."
30
36
  else
31
37
  errors.each do |e|
32
38
  STDERR.puts "#{e.class} #{e.message}"
33
- STDERR.puts e.backtrace.join("\n")
39
+ STDERR.puts e.backtrace&.join("\n")
34
40
  end
35
41
  end
36
-
37
- errors.empty?
38
42
  end
39
43
 
40
44
  private
@@ -42,6 +46,19 @@ module Remocon
42
46
  def validate_options
43
47
  raise ValidationError, "A condition file must exist" unless File.exist?(config.conditions_file_path)
44
48
  raise ValidationError, "A parameter file must exist" unless File.exist?(config.parameters_file_path)
49
+ raise ValidationError, "An etag file must exist" unless File.exist?(config.etag_file_path)
50
+ end
51
+
52
+ def remote_etag
53
+ @remote_etag ||= Remocon::Request.fetch_etag(config)
54
+ end
55
+
56
+ def etag_errors
57
+ if config.etag != remote_etag
58
+ [ValidationError.new("#{config.etag} is found but the latest etag is #{remote_etag || 'none'}")]
59
+ else
60
+ []
61
+ end
45
62
  end
46
63
  end
47
64
  end
@@ -6,6 +6,7 @@ module Remocon
6
6
 
7
7
  def sort_conditions(conditions)
8
8
  conditions
9
+ .map(&:symbolize_keys)
9
10
  .sort_by { |e| e[:name] }
10
11
  .map do |e|
11
12
  arr = e.sort do |(a, _), (b, _)|
@@ -6,7 +6,7 @@ module Remocon
6
6
 
7
7
  def sort_parameters(parameters)
8
8
  arr = parameters.sort.map do |k, v|
9
- hash_arr = v.sort { |(a, _), (b, _)| PARAMETER_KEYS.index(a) <=> PARAMETER_KEYS.index(b) }
9
+ hash_arr = v.symbolize_keys.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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Remocon
4
- VERSION = "0.3.1"
4
+ VERSION = "0.4.0-1"
5
5
  end
@@ -14,19 +14,11 @@
14
14
  "parameters": {
15
15
  "key1": {
16
16
  "defaultValue": {
17
- "value": "100",
18
- "conditions": {
19
- "condition1": {
20
- "value": "200"
21
- },
22
- "zxczx": {
23
- "value": "100"
24
- }
25
- }
17
+ "value": "100"
26
18
  },
27
19
  "conditionalValues": {
28
20
  "condition1": {
29
- "value": "200"
21
+ "value": "400"
30
22
  },
31
23
  "zxczx": {
32
24
  "value": "100"
@@ -55,7 +47,7 @@
55
47
  },
56
48
  "key6": {
57
49
  "defaultValue": {
58
- "value": "{\"value\":\"xxxx\"}"
50
+ "value": "{\"value\":\"234234234234234\"}"
59
51
  }
60
52
  },
61
53
  "key7": {
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: remocon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0.pre.1
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-08-10 00:00:00.000000000 Z
11
+ date: 2018-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -153,6 +153,7 @@ files:
153
153
  - lib/remocon/command/get_token_command.rb
154
154
  - lib/remocon/command/lib/config.rb
155
155
  - lib/remocon/command/lib/interpreter_helper.rb
156
+ - lib/remocon/command/lib/request.rb
156
157
  - lib/remocon/command/pull_command.rb
157
158
  - lib/remocon/command/push_command.rb
158
159
  - lib/remocon/command/validate_command.rb
@@ -197,9 +198,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
197
198
  version: '0'
198
199
  required_rubygems_version: !ruby/object:Gem::Requirement
199
200
  requirements:
200
- - - ">="
201
+ - - ">"
201
202
  - !ruby/object:Gem::Version
202
- version: '0'
203
+ version: 1.3.1
203
204
  requirements: []
204
205
  rubyforge_project:
205
206
  rubygems_version: 2.5.2.3