remocon 0.3.1 → 0.4.0.pre.1

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