magellan-cli 0.5.9 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +1 -1
- data/Gemfile +1 -0
- data/Gemfile.lock +9 -4
- data/lib/magellan/cli.rb +1 -2
- data/lib/magellan/cli/base.rb +103 -1
- data/lib/magellan/cli/command.rb +21 -5
- data/lib/magellan/cli/direct.rb +1 -2
- data/lib/magellan/cli/http.rb +222 -114
- data/lib/magellan/cli/locales/en.yml +5 -0
- data/lib/magellan/cli/locales/ja.yml +5 -0
- data/lib/magellan/cli/messaging/base.rb +3 -2
- data/lib/magellan/cli/resources/base.rb +7 -4
- data/lib/magellan/cli/resources/client_version.rb +20 -0
- data/lib/magellan/cli/version.rb +1 -1
- data/magellan-cli.gemspec +1 -1
- data/spec/magellan/cli/command_spec.rb +16 -1
- data/spec/magellan/cli/http_spec.rb +24 -0
- data/spec/magellan/cli/login_spec.rb +10 -3
- data/spec/magellan/cli/resources/client_version_spec.rb +31 -2
- data/spec/magellan/cli/resources/deselect_spec.rb +13 -0
- data/spec/magellan/cli/resources/organization_spec.rb +2 -2
- data/spec/magellan/cli/resources/project_spec.rb +5 -7
- data/spec/magellan/cli/resources/team_spec.rb +2 -2
- metadata +6 -5
- data/lib/magellan/cli/login.rb +0 -119
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7db0837835cc09a010e80b50ed05ef816eac9c2d
|
4
|
+
data.tar.gz: 5eaf1c7d35daeedab002dd47d6dc1f6298846550
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 75ca604ec8f59c335e9eb4fe75e1ee9fff085dcb5212f7c86d47ea16a2efa7a51a2346b22d5738df7b15739a411218772658dffbb8ae6f18a755d0ed1094875e
|
7
|
+
data.tar.gz: d60150518e847386d483cd410d06bc8f02c340ffdbe43a34e51574f8023374e9b2d09c2881607be1242018cbddb6a80c5ebe36d14b0b9d32c56317a06db8367f
|
data/.rspec
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
--format
|
1
|
+
--format Fuubar
|
2
2
|
--color
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
magellan-cli (0.
|
4
|
+
magellan-cli (0.6.0)
|
5
5
|
activesupport (~> 4.1.4)
|
6
6
|
groovenauts-thor
|
7
7
|
httpclient (~> 2.5)
|
8
8
|
i18n
|
9
|
-
libmagellan (~> 0.2.
|
9
|
+
libmagellan (~> 0.2.4)
|
10
10
|
nokogiri
|
11
11
|
psych (>= 2.0.0, <= 2.0.8)
|
12
12
|
text-table (~> 1.2.3)
|
@@ -20,7 +20,7 @@ GEM
|
|
20
20
|
minitest (~> 5.1)
|
21
21
|
thread_safe (~> 0.1)
|
22
22
|
tzinfo (~> 1.1)
|
23
|
-
addressable (2.3.
|
23
|
+
addressable (2.3.7)
|
24
24
|
binding_of_caller (0.7.2)
|
25
25
|
debug_inspector (>= 0.0.1)
|
26
26
|
byebug (2.7.0)
|
@@ -34,12 +34,15 @@ GEM
|
|
34
34
|
docile (1.1.5)
|
35
35
|
faraday (0.9.1)
|
36
36
|
multipart-post (>= 1.2, < 3)
|
37
|
+
fuubar (2.0.0)
|
38
|
+
rspec (~> 3.0)
|
39
|
+
ruby-progressbar (~> 1.4)
|
37
40
|
groovenauts-thor (0.19.1.1)
|
38
41
|
httpclient (2.6.0.1)
|
39
42
|
i18n (0.7.0)
|
40
43
|
json (1.8.2)
|
41
44
|
jwt (1.2.1)
|
42
|
-
libmagellan (0.2.
|
45
|
+
libmagellan (0.2.4)
|
43
46
|
activesupport
|
44
47
|
mqtt (~> 0.3.1)
|
45
48
|
signet (~> 0.5.0)
|
@@ -75,6 +78,7 @@ GEM
|
|
75
78
|
rspec-mocks (3.0.4)
|
76
79
|
rspec-support (~> 3.0.0)
|
77
80
|
rspec-support (3.0.4)
|
81
|
+
ruby-progressbar (1.7.1)
|
78
82
|
signet (0.5.1)
|
79
83
|
addressable (>= 2.2.3)
|
80
84
|
faraday (>= 0.9.0.rc5)
|
@@ -96,6 +100,7 @@ PLATFORMS
|
|
96
100
|
|
97
101
|
DEPENDENCIES
|
98
102
|
bundler (~> 1.6)
|
103
|
+
fuubar
|
99
104
|
magellan-cli!
|
100
105
|
pry
|
101
106
|
pry-byebug
|
data/lib/magellan/cli.rb
CHANGED
@@ -6,11 +6,10 @@ module Magellan
|
|
6
6
|
autoload :Command , "magellan/cli/command"
|
7
7
|
|
8
8
|
autoload :Base , "magellan/cli/base"
|
9
|
-
autoload :Http , "magellan/cli/http"
|
10
9
|
autoload :Direct , "magellan/cli/direct"
|
11
10
|
autoload :Resources, "magellan/cli/resources"
|
12
11
|
|
13
|
-
autoload :
|
12
|
+
autoload :Http , "magellan/cli/http"
|
14
13
|
autoload :Ssl , "magellan/cli/ssl"
|
15
14
|
|
16
15
|
autoload :Messaging, "magellan/cli/messaging"
|
data/lib/magellan/cli/base.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
require "magellan/cli"
|
2
3
|
|
3
4
|
require 'thor'
|
@@ -36,6 +37,9 @@ module Magellan
|
|
36
37
|
def log_info(msg)
|
37
38
|
self.class.log_info(msg)
|
38
39
|
end
|
40
|
+
def log_warning(msg)
|
41
|
+
self.class.log_warning(msg)
|
42
|
+
end
|
39
43
|
def log_success(msg)
|
40
44
|
self.class.log_success(msg)
|
41
45
|
end
|
@@ -53,8 +57,104 @@ module Magellan
|
|
53
57
|
log_verbose(" " << e.backtrace.join("\n "))
|
54
58
|
exit(1)
|
55
59
|
end
|
60
|
+
|
61
|
+
def http_conn
|
62
|
+
@http_conn ||= Cli::Http.new(self)
|
63
|
+
end
|
64
|
+
|
65
|
+
def login!(email, password)
|
66
|
+
logined = http_conn.api_login!(email, password)
|
67
|
+
if logined
|
68
|
+
log_success I18n.t(:success, scope: [:http, :login])
|
69
|
+
else
|
70
|
+
log_error I18n.t(:error, scope: [:http, :login])
|
71
|
+
exit(1)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def login_by_token!(email, token)
|
76
|
+
logined = http_conn.api_login_by_token!(email, token)
|
77
|
+
if logined
|
78
|
+
log_success I18n.t(:success, scope: [:http, :token])
|
79
|
+
else
|
80
|
+
log_error I18n.t(:error, scope: [:http, :token])
|
81
|
+
exit(1)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def http_access
|
86
|
+
if block_given?
|
87
|
+
http_conn.check_login_auth!
|
88
|
+
return yield(http_conn)
|
89
|
+
else
|
90
|
+
return log_success(I18n.t(:ok, scope: [:http, :access_api]))
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
# ログインしてGETします
|
96
|
+
# @param [String] rel_path http.base_url からの相対パス
|
97
|
+
# @param [Hash] params クエリ文字列
|
98
|
+
# @return [Object] レスポンスのBODYをJSONとしてパースした結果オブジェクト
|
99
|
+
def get_json(rel_path, params = {})
|
100
|
+
http_access do |api|
|
101
|
+
api.get_json(rel_path, params = {})
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# ログインしてPOSTします
|
106
|
+
# @param [String] rel_path http.base_url からの相対パス
|
107
|
+
# @param [Hash] params POSTで渡されるパラメータ
|
108
|
+
# @return nil
|
109
|
+
def post(rel_path, params)
|
110
|
+
http_access do |api|
|
111
|
+
api.post(rel_path, params)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# ログインしてJSON形式のbodyをPOSTします
|
116
|
+
# @param [String] rel_path http.base_url からの相対パス
|
117
|
+
# @param [Hash] params POSTで渡されるパラメータ
|
118
|
+
# @return nil
|
119
|
+
def post_json(rel_path, params)
|
120
|
+
http_access do |api|
|
121
|
+
api.post_json(rel_path, params)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# ログインしてPUTします
|
126
|
+
# @param [String] rel_path http.base_url からの相対パス
|
127
|
+
# @param [Hash] params PUTで渡されるパラメータ
|
128
|
+
# @return nil
|
129
|
+
def put(rel_path, params)
|
130
|
+
http_access do |api|
|
131
|
+
api.put(rel_path, params)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# ログインしてJSON形式のbodyをPUTします
|
136
|
+
# @param [String] rel_path http.base_url からの相対パス
|
137
|
+
# @param [Hash] params PUTで渡されるパラメータ
|
138
|
+
# @return nil
|
139
|
+
def put_json(rel_path, params)
|
140
|
+
http_access do |api|
|
141
|
+
api.put_json(rel_path, params)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# ログインしてDELETEします
|
146
|
+
# @param [String] rel_path http.base_url からの相対パス
|
147
|
+
# @return nil
|
148
|
+
def delete(rel_path)
|
149
|
+
http_access do |api|
|
150
|
+
api.delete(rel_path)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
56
154
|
end
|
57
155
|
|
156
|
+
|
157
|
+
|
58
158
|
class << self
|
59
159
|
def puts_with_color(color_no, msg)
|
60
160
|
$stderr.puts("\e[#{color_no}m#{msg}\e[0m")
|
@@ -66,6 +166,9 @@ module Magellan
|
|
66
166
|
def log_info(msg)
|
67
167
|
puts_with_color(0, msg)
|
68
168
|
end
|
169
|
+
def log_warning(msg)
|
170
|
+
puts_with_color(33, msg)
|
171
|
+
end
|
69
172
|
def log_success(msg)
|
70
173
|
puts_with_color(32, msg)
|
71
174
|
end
|
@@ -160,4 +263,3 @@ module Magellan
|
|
160
263
|
end
|
161
264
|
end
|
162
265
|
end
|
163
|
-
|
data/lib/magellan/cli/command.rb
CHANGED
@@ -69,29 +69,45 @@ module Magellan
|
|
69
69
|
desc "login", I18n.t(:login, scope: [:command, :cmd])
|
70
70
|
method_option :email, aliases: "-e", desc: I18n.t(:email, scope: [:command, :cmd_login])
|
71
71
|
method_option :password, aliases: "-p", desc: I18n.t(:password, scope: [:command, :cmd_login])
|
72
|
+
method_option :authentication_token, aliases: "-t", desc: I18n.t(:authentication_token, scope: [:command, :cmd_login])
|
72
73
|
def login
|
73
74
|
unless email = options[:email]
|
74
75
|
print "email: "
|
75
76
|
email = STDIN.gets.strip
|
76
77
|
end
|
77
78
|
|
78
|
-
|
79
|
+
password = options[:password]
|
80
|
+
token = options[:authentication_token]
|
81
|
+
|
82
|
+
if password.blank? && token.blank?
|
83
|
+
log_warning I18n.t(:warning, scope: :login)
|
79
84
|
print "password: "
|
80
85
|
password = STDIN.noecho(&:gets).chomp
|
81
86
|
puts ""
|
82
87
|
end
|
83
88
|
|
84
|
-
|
89
|
+
if password.blank? && token.blank?
|
90
|
+
print "authentication_token: "
|
91
|
+
token = STDIN.noecho(&:gets).chomp
|
92
|
+
puts ""
|
93
|
+
end
|
94
|
+
|
95
|
+
result =
|
96
|
+
if password.present?
|
97
|
+
login!(email, password)
|
98
|
+
else
|
99
|
+
login_by_token!(email, token)
|
100
|
+
end
|
101
|
+
|
85
102
|
select_single_resources
|
86
103
|
result
|
87
104
|
end
|
88
105
|
|
89
106
|
desc "info", I18n.t(:info, scope: [:command, :cmd])
|
90
107
|
def info
|
91
|
-
|
92
|
-
cli.check_login_auth!
|
108
|
+
http_conn.check_login_auth!
|
93
109
|
selections = load_selections || {}
|
94
|
-
d = {"user" =>
|
110
|
+
d = {"user" => http_conn.login_auth["email"] }
|
95
111
|
Resources::MAPPING.each do |classname, name|
|
96
112
|
klass = ::Magellan::Cli::Resources.const_get(classname)
|
97
113
|
attr = klass.caption_attr
|
data/lib/magellan/cli/direct.rb
CHANGED
@@ -3,7 +3,7 @@ require "magellan/cli"
|
|
3
3
|
|
4
4
|
module Magellan
|
5
5
|
module Cli
|
6
|
-
class Direct < ::Magellan::Cli::
|
6
|
+
class Direct < ::Magellan::Cli::Base
|
7
7
|
|
8
8
|
desc "get PATH", "Send GET request with PATH"
|
9
9
|
def get(path)
|
@@ -29,4 +29,3 @@ module Magellan
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
32
|
-
|
data/lib/magellan/cli/http.rb
CHANGED
@@ -1,149 +1,257 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
require "magellan/cli"
|
3
3
|
|
4
|
+
require 'httpclient'
|
5
|
+
require 'json'
|
6
|
+
require 'uri'
|
7
|
+
require 'nokogiri'
|
8
|
+
|
9
|
+
require 'active_support/core_ext/hash/keys'
|
10
|
+
|
4
11
|
module Magellan
|
5
12
|
module Cli
|
6
|
-
class Http
|
13
|
+
class Http
|
14
|
+
include Magellan::Cli::FileAccess
|
7
15
|
|
8
|
-
|
16
|
+
DEFAULT_HTTP_PORT = (ENV['DEFAULT_HTTP_PORT' ] || 80).to_i
|
17
|
+
DEFAULT_HTTPS_PORT = (ENV['DEFAULT_HTTPS_PORT'] || 443).to_i
|
9
18
|
|
10
|
-
|
11
|
-
|
12
|
-
|
19
|
+
attr_reader :cmd
|
20
|
+
attr_reader :httpclient
|
21
|
+
attr_reader :base_url
|
13
22
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
exit(1)
|
21
|
-
end
|
22
|
-
end
|
23
|
+
attr_reader :auth_token
|
24
|
+
attr_reader :login_auth
|
25
|
+
|
26
|
+
def self.base_url
|
27
|
+
@base_url ||= (ENV["MAGELLAN_SITE"] || "https://api-asia.magellanic-clouds.com")
|
28
|
+
end
|
23
29
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
30
|
+
# Magellan::Cli::Httpのコンストラクタです。
|
31
|
+
#
|
32
|
+
# @param [String] base_url_or_host 接続先の基準となるURLあるいはホスト名
|
33
|
+
# @param [Hash] options オプション
|
34
|
+
# @option options [String] :api_version APIのバージョン。デフォルトは "1.0.0"
|
35
|
+
def initialize(cmd)
|
36
|
+
@cmd = cmd
|
37
|
+
base_url_or_host = self.class.base_url
|
38
|
+
if base_url_or_host =~ URI.regexp
|
39
|
+
@base_url = base_url_or_host.sub(/\/\Z/, '')
|
40
|
+
uri = URI.parse(@base_url)
|
41
|
+
else
|
42
|
+
if config_path = search_file(".magellan-cli.yml")
|
43
|
+
config = YAML.load_file_with_erb(config_path)
|
44
|
+
options = config[base_url_or_host.to_s].deep_symbolize_keys
|
28
45
|
else
|
29
|
-
|
46
|
+
options = {}
|
30
47
|
end
|
48
|
+
uri = URI::Generic.build({scheme: "http", host: base_url_or_host, port: DEFAULT_HTTP_PORT}.update(options))
|
49
|
+
@base_url = uri.to_s
|
31
50
|
end
|
32
51
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
end
|
46
|
-
log_verbose(JSON.pretty_generate(r))
|
47
|
-
r
|
48
|
-
else
|
49
|
-
obj = JSON.parse(res.body) rescue nil
|
50
|
-
if obj and obj.is_a?(Hash) and obj["message"]
|
51
|
-
fatal(obj["message"])
|
52
|
-
else
|
53
|
-
msg = "HTTP Error: status=#{res.status}\n#{res.body}"
|
54
|
-
log_verbose(msg)
|
55
|
-
end
|
56
|
-
end
|
52
|
+
@httpclient = HTTPClient.new
|
53
|
+
@httpclient.debug_dev = ColorDebugDev.new($stderr) if cmd.verbose?
|
54
|
+
@httpclient.ssl_config.verify_mode = nil # 自己署名の証明書をOKにする
|
55
|
+
@login_auth = load_selections["login"]
|
56
|
+
end
|
57
|
+
|
58
|
+
class ColorDebugDev
|
59
|
+
def initialize(dev)
|
60
|
+
@dev = dev
|
61
|
+
end
|
62
|
+
def <<(msg)
|
63
|
+
@dev << "\e[34m#{msg}\e[0m"
|
57
64
|
end
|
65
|
+
end
|
58
66
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
end
|
71
|
-
log_verbose("GET #{url}")
|
72
|
-
# "Unknown key: max-age = 0" というメッセージを表示させないために$stderrを一時的に上書き
|
73
|
-
$stderr, bak = StringIO.new, $stderr
|
74
|
-
res = nil
|
75
|
-
begin
|
76
|
-
res = api.httpclient.get(url)
|
77
|
-
ensure
|
78
|
-
$stderr = bak
|
79
|
-
end
|
80
|
-
check_response(res)
|
81
|
-
end
|
67
|
+
def login_form_url
|
68
|
+
@login_form_url ||= base_url + "/users/sign_in.html"
|
69
|
+
end
|
70
|
+
def login_url
|
71
|
+
@login_url ||= base_url + "/api/sign_in.json"
|
72
|
+
end
|
73
|
+
|
74
|
+
def check_login_auth!
|
75
|
+
auth = login_auth
|
76
|
+
if auth.nil? || auth.empty?
|
77
|
+
raise Magellan::Cli::Error, I18n.t(:not_logged_in, scope: [:login, :check_login_auth], command: File.basename($0))
|
82
78
|
end
|
79
|
+
end
|
83
80
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
81
|
+
# magellan-apiサーバに接続してログインの検証とアクセストークンの保存を行います。
|
82
|
+
#
|
83
|
+
# @return [boolean] login成功/失敗
|
84
|
+
def api_login!(email, password)
|
85
|
+
@auth_token ||= get_auth_token
|
86
|
+
params = {
|
87
|
+
"user" => {
|
88
|
+
"email" => email,
|
89
|
+
"password" => password
|
90
|
+
},
|
91
|
+
"authenticity_token" => @auth_token
|
92
|
+
}.to_json
|
93
|
+
res2 = Ssl.retry_on_ssl_error("login"){ @httpclient.post(login_url, params, JSON_HEADER) }
|
94
|
+
|
95
|
+
case res2.status
|
96
|
+
when 200...300 then logined = true
|
97
|
+
else logined = false
|
93
98
|
end
|
94
99
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
access_api do |api|
|
101
|
-
params = api.login_auth.update(params || {})
|
102
|
-
process_res(api, :post, rel_path, params.to_json, JSON_HEADER)
|
103
|
-
end
|
100
|
+
if logined
|
101
|
+
body = JSON.parse res2.body
|
102
|
+
write_login_info!(email, body["token"])
|
103
|
+
else
|
104
|
+
reset_login_info!
|
104
105
|
end
|
106
|
+
logined
|
107
|
+
end
|
105
108
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
109
|
+
def api_login_by_token!(email, token)
|
110
|
+
url = "#{base_url}/admin/magellan~auth~organization.json"
|
111
|
+
params = {"email" => email, "token" => token}
|
112
|
+
res = httpclient.get(url, params)
|
113
|
+
|
114
|
+
logined =
|
115
|
+
case res.status
|
116
|
+
when 200...400 then true
|
117
|
+
else false
|
114
118
|
end
|
119
|
+
|
120
|
+
if logined
|
121
|
+
write_login_info!(email, token)
|
122
|
+
else
|
123
|
+
reset_login_info!
|
115
124
|
end
|
125
|
+
logined
|
126
|
+
end
|
116
127
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
128
|
+
|
129
|
+
def write_login_info!(email, token)
|
130
|
+
update_selections("login" => {"email" => email, "token" => token })
|
131
|
+
end
|
132
|
+
|
133
|
+
def reset_login_info!
|
134
|
+
update_selections("login" => nil)
|
135
|
+
end
|
136
|
+
|
137
|
+
def get_auth_token
|
138
|
+
res = Ssl.retry_on_ssl_error("login_form"){ @httpclient.get(login_form_url) }
|
139
|
+
doc = Nokogiri::HTML.parse(res.body, login_form_url, res.body_encoding.to_s)
|
140
|
+
node = doc.xpath('//input[@name="authenticity_token"]').first
|
141
|
+
unless node
|
142
|
+
raise Cli::Error.new("fail to login Magellan")
|
126
143
|
end
|
144
|
+
node.attribute('value').value
|
145
|
+
end
|
127
146
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
147
|
+
# @httpclient.inspectの戻り値の文字列が巨大なので、inspectで出力しないようにします。
|
148
|
+
def inspect
|
149
|
+
r = "#<#{self.class.name}:#{self.object_id} "
|
150
|
+
fields = (instance_variables - [:@httpclient]).map{|f| "#{f}=#{instance_variable_get(f).inspect}"}
|
151
|
+
r << fields.join(", ") << ">"
|
152
|
+
end
|
153
|
+
|
154
|
+
def search_file(basename)
|
155
|
+
dirs = [".", "./config", ENV['HOME']].join(",")
|
156
|
+
Dir["{#{dirs}}/#{basename}"].select{|path| File.readable?(path)}.first
|
157
|
+
end
|
158
|
+
private :search_file
|
159
|
+
|
160
|
+
|
161
|
+
def check_response(res)
|
162
|
+
case res.status
|
163
|
+
when 200...400 then
|
164
|
+
begin
|
165
|
+
r = JSON.parse(res.body)
|
166
|
+
rescue
|
167
|
+
# DELETE で 302 Found を返す時に HTML がレンダリングされることがあるので
|
168
|
+
# status code 300 台では JSON パースエラーを無視する
|
169
|
+
if res.status >= 300
|
170
|
+
return nil
|
171
|
+
end
|
172
|
+
raise
|
173
|
+
end
|
174
|
+
r
|
175
|
+
else
|
176
|
+
obj = JSON.parse(res.body) rescue nil
|
177
|
+
if obj and obj.is_a?(Hash) and obj["message"]
|
178
|
+
fatal(obj["message"])
|
135
179
|
end
|
136
180
|
end
|
181
|
+
end
|
137
182
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
183
|
+
# ログインしてGETします
|
184
|
+
# @param [String] rel_path cli.base_url からの相対パス
|
185
|
+
# @param [Hash] params クエリ文字列
|
186
|
+
# @return [Object] レスポンスのBODYをJSONとしてパースした結果オブジェクト
|
187
|
+
def get_json(rel_path, params = {})
|
188
|
+
url = "#{base_url}#{rel_path}"
|
189
|
+
params.update(yield) if block_given?
|
190
|
+
params = login_auth.merge(params)
|
191
|
+
if params && !params.empty?
|
192
|
+
url << '?' << params.map{|k,v| "%s=%s" % [CGI.escape(k.to_s), CGI.escape(v.to_s)] }.join("&")
|
193
|
+
end
|
194
|
+
# "Unknown key: max-age = 0" というメッセージを表示させないために$stderrを一時的に上書き
|
195
|
+
$stderr, bak = StringIO.new, $stderr
|
196
|
+
res = nil
|
197
|
+
begin
|
198
|
+
res = httpclient.get(url)
|
199
|
+
ensure
|
200
|
+
$stderr = bak
|
143
201
|
end
|
202
|
+
check_response(res)
|
203
|
+
end
|
204
|
+
|
205
|
+
# ログインしてPOSTします
|
206
|
+
# @param [String] rel_path cli.base_url からの相対パス
|
207
|
+
# @param [Hash] params POSTで渡されるパラメータ
|
208
|
+
# @return nil
|
209
|
+
def post(rel_path, params)
|
210
|
+
params = login_auth.update(params || {})
|
211
|
+
process_res(:post, rel_path, params)
|
212
|
+
end
|
213
|
+
|
214
|
+
# ログインしてJSON形式のbodyをPOSTします
|
215
|
+
# @param [String] rel_path cli.base_url からの相対パス
|
216
|
+
# @param [Hash] params POSTで渡されるパラメータ
|
217
|
+
# @return nil
|
218
|
+
def post_json(rel_path, params)
|
219
|
+
params = login_auth.update(params || {})
|
220
|
+
process_res(:post, rel_path, params.to_json, JSON_HEADER)
|
221
|
+
end
|
144
222
|
|
223
|
+
# ログインしてPUTします
|
224
|
+
# @param [String] rel_path cli.base_url からの相対パス
|
225
|
+
# @param [Hash] params PUTで渡されるパラメータ
|
226
|
+
# @return nil
|
227
|
+
def put(rel_path, params)
|
228
|
+
params = login_auth.update(params || {})
|
229
|
+
process_res(:put, rel_path, params)
|
145
230
|
end
|
231
|
+
|
232
|
+
# ログインしてJSON形式のbodyをPUTします
|
233
|
+
# @param [String] rel_path cli.base_url からの相対パス
|
234
|
+
# @param [Hash] params PUTで渡されるパラメータ
|
235
|
+
# @return nil
|
236
|
+
def put_json(rel_path, params)
|
237
|
+
params = login_auth.update(params || {})
|
238
|
+
process_res(:put, rel_path, params.to_json, JSON_HEADER)
|
239
|
+
end
|
240
|
+
|
241
|
+
# ログインしてDELETEします
|
242
|
+
# @param [String] rel_path cli.base_url からの相対パス
|
243
|
+
# @return nil
|
244
|
+
def delete(rel_path)
|
245
|
+
params = login_auth
|
246
|
+
process_res(:delete, rel_path, params.to_json, JSON_HEADER)
|
247
|
+
end
|
248
|
+
|
249
|
+
def process_res(http_method, rel_path, *args)
|
250
|
+
url = "#{base_url}#{rel_path}"
|
251
|
+
res = httpclient.send(http_method, url, *args)
|
252
|
+
check_response(res)
|
253
|
+
end
|
254
|
+
|
146
255
|
end
|
147
256
|
end
|
148
257
|
end
|
149
|
-
|
@@ -32,6 +32,7 @@ en:
|
|
32
32
|
cmd_login:
|
33
33
|
email: "email address for login"
|
34
34
|
password: "password for login"
|
35
|
+
authentication_token: "authentication token for login"
|
35
36
|
cmd_help:
|
36
37
|
for_more_detail: "type `%{command} help RESOURCE` for more detail"
|
37
38
|
file_access:
|
@@ -43,9 +44,13 @@ en:
|
|
43
44
|
error: "Login failure"
|
44
45
|
access_api:
|
45
46
|
ok: "OK"
|
47
|
+
token:
|
48
|
+
success: "Authorized"
|
49
|
+
error: "Unauthorized"
|
46
50
|
login:
|
47
51
|
check_login_auth:
|
48
52
|
not_logged_in: "Not logined yet. type `%{command} login`."
|
53
|
+
warning: "Please refer to the password empty if you want to login with authentication token."
|
49
54
|
messaging:
|
50
55
|
http:
|
51
56
|
ping: "Send a HTTP request to check connection"
|
@@ -30,6 +30,7 @@ ja:
|
|
30
30
|
cmd_login:
|
31
31
|
email: "ログインするアカウントのメールアドレス"
|
32
32
|
password: "ログインするアカウントのパスワード"
|
33
|
+
authentication_token: "ログインするアカウントの認証トークン"
|
33
34
|
cmd_help:
|
34
35
|
for_more_detail: "詳しくは `%{command} help RESOURCE` を実行してください"
|
35
36
|
file_access:
|
@@ -41,9 +42,13 @@ ja:
|
|
41
42
|
error: "ログインに失敗しました"
|
42
43
|
access_api:
|
43
44
|
ok: "OK"
|
45
|
+
token:
|
46
|
+
success: "認証しました"
|
47
|
+
error: "認証に失敗しました"
|
44
48
|
login:
|
45
49
|
check_login_auth:
|
46
50
|
not_logged_in: "ログインしていません。`%{command} login` を実行してログインしてください"
|
51
|
+
warning: "認証トークンでログインする場合はパスワードを空にしてください"
|
47
52
|
messaging:
|
48
53
|
http:
|
49
54
|
ping: "接続確認のリクエストを送信します"
|
@@ -7,7 +7,7 @@ require 'yaml'
|
|
7
7
|
module Magellan
|
8
8
|
module Cli
|
9
9
|
module Messaging
|
10
|
-
class Base < Magellan::Cli::
|
10
|
+
class Base < Magellan::Cli::Base
|
11
11
|
include Magellan::Cli::FileAccess
|
12
12
|
|
13
13
|
no_commands do
|
@@ -18,7 +18,7 @@ module Magellan
|
|
18
18
|
private
|
19
19
|
|
20
20
|
def load_selection!(name, &block)
|
21
|
-
return
|
21
|
+
return http_access{ load_selection(name, &block) }
|
22
22
|
end
|
23
23
|
|
24
24
|
def find(klass, id = nil)
|
@@ -39,6 +39,7 @@ module Magellan
|
|
39
39
|
client_version: client_version,
|
40
40
|
mqtt_host: ENV["MAGELLAN_MQTT_SERVER_HOST"] || DEFAULT_MAGELLAN_MQTT_SERVER_HOST,
|
41
41
|
mqtt_port: ENV["MAGELLAN_MQTT_SERVER_PORT"] || DEFAULT_MAGELLAN_MQTT_SERVER_PORT,
|
42
|
+
verbose: options[:verbose],
|
42
43
|
}
|
43
44
|
uri = ENV["MAGELLAN_HTTP_SERVER_URL"] || DEFAULT_MAGELLAN_HTTP_SERVER_URL
|
44
45
|
log_verbose("HTTP URL : #{uri.inspect}")
|
@@ -17,16 +17,16 @@ module Magellan
|
|
17
17
|
class NotFound < Magellan::Cli::Error
|
18
18
|
end
|
19
19
|
|
20
|
-
class Base < ::Magellan::Cli::
|
20
|
+
class Base < ::Magellan::Cli::Base
|
21
21
|
include Magellan::Cli::FileAccess
|
22
22
|
|
23
23
|
no_commands do
|
24
24
|
|
25
25
|
def load_selection!(name, &block)
|
26
|
-
return
|
26
|
+
return http_access{ load_selection(name, &block) }
|
27
27
|
end
|
28
28
|
def update_selections!(hash = nil, &block)
|
29
|
-
return
|
29
|
+
return http_access{ update_selections(hash, &block) }
|
30
30
|
end
|
31
31
|
|
32
32
|
def build_query(hash)
|
@@ -41,7 +41,7 @@ module Magellan
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def default_query
|
44
|
-
|
44
|
+
http_access do |cli|
|
45
45
|
sel = load_selections
|
46
46
|
q = {}
|
47
47
|
(self.class.resource_dependency || {}).each do |f, res|
|
@@ -169,6 +169,9 @@ module Magellan
|
|
169
169
|
def select(name)
|
170
170
|
q = build_query(self.class.caption_attr => name).update(default_query)
|
171
171
|
update_first_result(self.class.parameter_name, name, "/admin/#{self.class.resource_key}.json", q)
|
172
|
+
update_selections! do |s|
|
173
|
+
self.class.deselect_dependants(s)
|
174
|
+
end
|
172
175
|
end
|
173
176
|
|
174
177
|
def deselect
|
@@ -16,6 +16,7 @@ module Magellan
|
|
16
16
|
self.caption_attr = "version"
|
17
17
|
|
18
18
|
desc "create VERSION", I18n.t(:create, scope: [:resources, :client_version, :cmd], resource_name: resource_name)
|
19
|
+
method_option :domain, aliases: "-d", desc: I18n.t(:domain, scope: [:resources, :client_version, :cmd_create])
|
19
20
|
def create(version)
|
20
21
|
project = load_selection!(Project)
|
21
22
|
stage = load_selection!(Stage)
|
@@ -24,12 +25,31 @@ module Magellan
|
|
24
25
|
"stage_title_id" => stage["id"],
|
25
26
|
"version" => version,
|
26
27
|
}
|
28
|
+
if d = options[:domain]
|
29
|
+
attrs["domain"] = d
|
30
|
+
end
|
27
31
|
params = { parameter_name => attrs }
|
28
32
|
post_json("/admin/#{resource_key}/new.json", params)
|
29
33
|
# TODO implement select method
|
30
34
|
# select(version)
|
31
35
|
end
|
32
36
|
|
37
|
+
desc "update ATTRIBUTES", I18n.t(:update, scope: [:resources, :client_version, :cmd], resource_name: resource_name)
|
38
|
+
def update(attrs)
|
39
|
+
if File.readable?(attrs)
|
40
|
+
attrs = YAML.load_file(attrs)
|
41
|
+
else
|
42
|
+
attrs = JSON.parse(attrs)
|
43
|
+
end
|
44
|
+
cv = load_selection!(self.class)
|
45
|
+
self.class.hidden_fields.each do |f| attrs.delete(f) end
|
46
|
+
self.class.field_associations.keys.each do |f| attrs.delete(f) end
|
47
|
+
params = {
|
48
|
+
parameter_name => attrs
|
49
|
+
}
|
50
|
+
put_json("/admin/#{resource_key}/#{cv["id"]}/edit.json", params)
|
51
|
+
end
|
52
|
+
|
33
53
|
desc "delete VERSION", I18n.t(:delete, scope: [:resources, :client_version, :cmd], resource_name: resource_name)
|
34
54
|
def delete(version)
|
35
55
|
q = build_query("version" => version).update(default_query)
|
data/lib/magellan/cli/version.rb
CHANGED
data/magellan-cli.gemspec
CHANGED
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.add_runtime_dependency "text-table", "~> 1.2.3"
|
26
26
|
spec.add_runtime_dependency "i18n"
|
27
27
|
spec.add_runtime_dependency "psych", ">= 2.0.0", "<= 2.0.8"
|
28
|
-
spec.add_runtime_dependency "libmagellan", "~> 0.2.
|
28
|
+
spec.add_runtime_dependency "libmagellan", "~> 0.2.4"
|
29
29
|
|
30
30
|
spec.add_development_dependency "bundler", "~> 1.6"
|
31
31
|
spec.add_development_dependency "rake", "~> 10.0"
|
@@ -10,7 +10,8 @@ describe Magellan::Cli::Command do
|
|
10
10
|
before do
|
11
11
|
$stdout = StringIO.new
|
12
12
|
allow(command).to receive(:select_single_resources)
|
13
|
-
allow(
|
13
|
+
allow(command).to receive(:login!).and_return("OK")
|
14
|
+
allow(command).to receive(:login_by_token!).and_return("OK")
|
14
15
|
end
|
15
16
|
it "nothing options" do
|
16
17
|
allow($stdin).to receive(:gets).and_return(string).once
|
@@ -19,6 +20,13 @@ describe Magellan::Cli::Command do
|
|
19
20
|
expect($stdout.string).to eq "email: password: \n"
|
20
21
|
end
|
21
22
|
|
23
|
+
it "nothing options" do
|
24
|
+
allow($stdin).to receive(:gets ).and_return(string).once # email
|
25
|
+
allow($stdin).to receive(:noecho).and_return("\n").twice # password
|
26
|
+
expect(command.login).to eq "OK"
|
27
|
+
expect($stdout.string).to eq "email: password: \nauthentication_token: \n"
|
28
|
+
end
|
29
|
+
|
22
30
|
it "only email" do
|
23
31
|
allow($stdin).to receive(:noecho).and_return(string).twice
|
24
32
|
command.options = Thor::CoreExt::HashWithIndifferentAccess.new email: string
|
@@ -32,6 +40,13 @@ describe Magellan::Cli::Command do
|
|
32
40
|
expect(command.login).to eq "OK"
|
33
41
|
expect($stdout.string).to eq "email: "
|
34
42
|
end
|
43
|
+
|
44
|
+
it "only authentication_token" do
|
45
|
+
allow($stdin).to receive(:gets).and_return(string).once
|
46
|
+
command.options = Thor::CoreExt::HashWithIndifferentAccess.new authentication_token: string
|
47
|
+
expect(command.login).to eq "OK"
|
48
|
+
expect($stdout.string).to eq "email: "
|
49
|
+
end
|
35
50
|
end
|
36
51
|
end
|
37
52
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Magellan::Cli::Messaging::Http do
|
5
|
+
|
6
|
+
let(:cmd){ Magellan::Cli::Messaging::Http.new }
|
7
|
+
|
8
|
+
describe :login_by_token do
|
9
|
+
let(:email) { "user1@example.com" }
|
10
|
+
let(:password) { "password" }
|
11
|
+
let(:success_res) { double(:success_res, status: 200) }
|
12
|
+
let(:error_res) { double(:error_res, status: 401) }
|
13
|
+
|
14
|
+
it "success" do
|
15
|
+
allow(cmd.http_conn.httpclient).to receive(:get).and_return(success_res)
|
16
|
+
cmd.login_by_token!(email, password)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "error" do
|
20
|
+
allow(cmd.http_conn.httpclient).to receive(:get).and_return(error_res)
|
21
|
+
expect{cmd.login_by_token!(email, password)}.to raise_error(SystemExit)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,12 +1,17 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
|
-
describe Magellan::Cli::
|
4
|
+
describe Magellan::Cli::Http do
|
5
5
|
|
6
6
|
describe :login! do
|
7
7
|
let(:email){ "magellan@groovenauts.jp" }
|
8
8
|
let(:password){ "password" }
|
9
9
|
let(:auth_token){ "047bcCC1dnyVE+7DWE6YKIJF97L/qHk1mPrf2oaqWtE=" }
|
10
|
+
let(:cmd) do
|
11
|
+
cmd = double(:cmd)
|
12
|
+
allow(cmd).to receive(:verbose?).and_return(false)
|
13
|
+
cmd
|
14
|
+
end
|
10
15
|
|
11
16
|
before do
|
12
17
|
allow(File).to receive(:readable?).and_return(true)
|
@@ -14,7 +19,8 @@ describe Magellan::Cli::Login do
|
|
14
19
|
end
|
15
20
|
|
16
21
|
context :production do
|
17
|
-
|
22
|
+
before{ allow(Magellan::Cli::Http).to receive(:base_url).and_return("https://example.com") }
|
23
|
+
let(:cli){ Magellan::Cli::Http.new(cmd) }
|
18
24
|
before do
|
19
25
|
res = double(:res)
|
20
26
|
allow(cli.httpclient).to receive(:get).with("https://example.com/users/sign_in.html").and_return(res)
|
@@ -50,7 +56,8 @@ describe Magellan::Cli::Login do
|
|
50
56
|
end
|
51
57
|
|
52
58
|
context :development do
|
53
|
-
|
59
|
+
before{ allow(Magellan::Cli::Http).to receive(:base_url).and_return("http://localhost:3001") }
|
60
|
+
let(:cli){ Magellan::Cli::Http.new(cmd) }
|
54
61
|
before do
|
55
62
|
res = double(:res)
|
56
63
|
allow(cli.httpclient).to receive(:get).with("http://localhost:3001/users/sign_in.html").and_return(res)
|
@@ -5,8 +5,8 @@ describe Magellan::Cli::Resources::ClientVersion do
|
|
5
5
|
|
6
6
|
let(:cmd){ Magellan::Cli::Resources::ClientVersion.new }
|
7
7
|
|
8
|
-
let(:
|
9
|
-
before{ allow(cmd).to receive(:
|
8
|
+
let(:http_conn){ double(:http_conn, :check_login_auth! => nil) }
|
9
|
+
before{ allow(cmd).to receive(:http_conn).and_return(http_conn) }
|
10
10
|
|
11
11
|
describe :list do
|
12
12
|
before do
|
@@ -33,6 +33,17 @@ describe Magellan::Cli::Resources::ClientVersion do
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
+
describe :success_with_domain do
|
37
|
+
before do
|
38
|
+
allow(cmd).to receive(:load_selections).and_return({Magellan::Cli::Resources::Project.parameter_name => {"id" => 1, "name" => "ProjectA"}, Magellan::Cli::Resources::Stage.parameter_name => {"id" => 1, "name" => "StageA"} })
|
39
|
+
expect(cmd).to receive(:post_json).with("/admin/client_version/new.json", { "client_version" => { "project_id" => 1, "stage_title_id" => 1, "version" => "1.1.0", "domain" => "foo.example.com" } })
|
40
|
+
end
|
41
|
+
it do
|
42
|
+
cmd.options = {domain: "foo.example.com"}
|
43
|
+
cmd.create("1.1.0")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
36
47
|
describe :error do
|
37
48
|
context "stage not selected" do
|
38
49
|
before do
|
@@ -47,4 +58,22 @@ describe Magellan::Cli::Resources::ClientVersion do
|
|
47
58
|
end
|
48
59
|
end
|
49
60
|
|
61
|
+
describe :update do
|
62
|
+
let(:selections) do
|
63
|
+
{
|
64
|
+
Magellan::Cli::Resources::Project.parameter_name => {"id" => 1, "name" => "ProjectA"},
|
65
|
+
Magellan::Cli::Resources::ClientVersion.parameter_name => {"id" => 1, "version" => "Sandbox1"}
|
66
|
+
}
|
67
|
+
end
|
68
|
+
describe :success_to_update_domain do
|
69
|
+
before do
|
70
|
+
allow(cmd).to receive(:load_selections).and_return(selections)
|
71
|
+
expect(cmd).to receive(:put_json).with("/admin/client_version/1/edit.json", { "client_version" => { "domain" => "bar.example.com" } })
|
72
|
+
end
|
73
|
+
it do
|
74
|
+
cmd.update('{"domain": "bar.example.com"}')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
50
79
|
end
|
@@ -65,4 +65,17 @@ describe "deselect" do
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
+
describe Magellan::Cli::Resources::Organization do
|
69
|
+
context "select another one" do
|
70
|
+
it do
|
71
|
+
allow(subject).to receive(:get_json).
|
72
|
+
with("/admin/magellan~auth~organization.json", an_instance_of(Hash)).
|
73
|
+
and_return([{"id" => 5, "name" => "test3"}])
|
74
|
+
expect(YAML.load_file(work_yaml)).to include(Magellan::Cli::Resources::Project.parameter_name)
|
75
|
+
subject.select "test3"
|
76
|
+
expect(YAML.load_file(work_yaml)).to_not include(Magellan::Cli::Resources::Project.parameter_name)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
68
81
|
end
|
@@ -5,8 +5,8 @@ describe Magellan::Cli::Resources::Organization do
|
|
5
5
|
|
6
6
|
let(:cmd){ Magellan::Cli::Resources::Organization.new }
|
7
7
|
|
8
|
-
let(:
|
9
|
-
before{ allow(cmd).to receive(:
|
8
|
+
let(:http_conn){ double(:http_conn, :check_login_auth! => nil) }
|
9
|
+
before{ allow(cmd).to receive(:http_conn).and_return(http_conn) }
|
10
10
|
|
11
11
|
describe :list do
|
12
12
|
before do
|
@@ -4,23 +4,21 @@ require 'spec_helper'
|
|
4
4
|
describe Magellan::Cli::Resources::Project do
|
5
5
|
|
6
6
|
let(:base_url){ "https://localhost:3000" }
|
7
|
+
before{ allow(Magellan::Cli::Http).to receive(:base_url).and_return(base_url) }
|
7
8
|
let(:httpclient){ double(:httpclient) }
|
8
|
-
let(:cli) do
|
9
|
-
cli = double(:access_api, base_url: base_url, auth_token: {}, login_auth: {})
|
10
|
-
allow(cli).to receive(:httpclient).and_return(httpclient)
|
11
|
-
cli
|
12
|
-
end
|
13
9
|
let(:res){ double(:res, status: 200, body: {}.to_json) }
|
14
10
|
|
15
11
|
let(:cmd){ Magellan::Cli::Resources::Project.new }
|
16
12
|
|
17
13
|
before do
|
18
|
-
allow(cmd).to receive(:
|
14
|
+
allow(cmd.http_conn).to receive(:httpclient).and_return(httpclient)
|
15
|
+
allow(cmd.http_conn).to receive(:check_login_auth!)
|
16
|
+
allow(cmd.http_conn).to receive(:login_auth).and_return({})
|
19
17
|
end
|
20
18
|
|
21
19
|
describe :list do
|
22
20
|
it do
|
23
|
-
expect(httpclient).to receive(:get).with(%
|
21
|
+
expect(httpclient).to receive(:get).with(%{#{base_url}/admin/project.json}).and_return(res)
|
24
22
|
expect($stdout).to receive(:puts)
|
25
23
|
cmd.list
|
26
24
|
end
|
@@ -5,8 +5,8 @@ describe Magellan::Cli::Resources::Team do
|
|
5
5
|
|
6
6
|
let(:cmd){ Magellan::Cli::Resources::Team.new }
|
7
7
|
|
8
|
-
let(:
|
9
|
-
before{ allow(cmd).to receive(:
|
8
|
+
let(:http_conn){ double(:http_conn, :check_login_auth! => nil) }
|
9
|
+
before{ allow(cmd).to receive(:http_conn).and_return(http_conn) }
|
10
10
|
|
11
11
|
describe :list do
|
12
12
|
before do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: magellan-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- akm2000
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-02-
|
11
|
+
date: 2015-02-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httpclient
|
@@ -120,14 +120,14 @@ dependencies:
|
|
120
120
|
requirements:
|
121
121
|
- - "~>"
|
122
122
|
- !ruby/object:Gem::Version
|
123
|
-
version: 0.2.
|
123
|
+
version: 0.2.4
|
124
124
|
type: :runtime
|
125
125
|
prerelease: false
|
126
126
|
version_requirements: !ruby/object:Gem::Requirement
|
127
127
|
requirements:
|
128
128
|
- - "~>"
|
129
129
|
- !ruby/object:Gem::Version
|
130
|
-
version: 0.2.
|
130
|
+
version: 0.2.4
|
131
131
|
- !ruby/object:Gem::Dependency
|
132
132
|
name: bundler
|
133
133
|
requirement: !ruby/object:Gem::Requirement
|
@@ -199,7 +199,6 @@ files:
|
|
199
199
|
- lib/magellan/cli/i18n.rb
|
200
200
|
- lib/magellan/cli/locales/en.yml
|
201
201
|
- lib/magellan/cli/locales/ja.yml
|
202
|
-
- lib/magellan/cli/login.rb
|
203
202
|
- lib/magellan/cli/messaging.rb
|
204
203
|
- lib/magellan/cli/messaging/base.rb
|
205
204
|
- lib/magellan/cli/messaging/http.rb
|
@@ -246,6 +245,7 @@ files:
|
|
246
245
|
- spec/magellan/cli/Magellan.yml
|
247
246
|
- spec/magellan/cli/command_spec.rb
|
248
247
|
- spec/magellan/cli/file_access_spec.rb
|
248
|
+
- spec/magellan/cli/http_spec.rb
|
249
249
|
- spec/magellan/cli/login_page.html
|
250
250
|
- spec/magellan/cli/login_spec.rb
|
251
251
|
- spec/magellan/cli/messaging/http_body.json
|
@@ -292,6 +292,7 @@ test_files:
|
|
292
292
|
- spec/magellan/cli/Magellan.yml
|
293
293
|
- spec/magellan/cli/command_spec.rb
|
294
294
|
- spec/magellan/cli/file_access_spec.rb
|
295
|
+
- spec/magellan/cli/http_spec.rb
|
295
296
|
- spec/magellan/cli/login_page.html
|
296
297
|
- spec/magellan/cli/login_spec.rb
|
297
298
|
- spec/magellan/cli/messaging/http_body.json
|
data/lib/magellan/cli/login.rb
DELETED
@@ -1,119 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
require "magellan/cli"
|
3
|
-
|
4
|
-
require 'httpclient'
|
5
|
-
require 'json'
|
6
|
-
require 'uri'
|
7
|
-
require 'nokogiri'
|
8
|
-
|
9
|
-
require 'active_support/core_ext/hash/keys'
|
10
|
-
|
11
|
-
module Magellan
|
12
|
-
module Cli
|
13
|
-
class Login
|
14
|
-
include Magellan::Cli::FileAccess
|
15
|
-
|
16
|
-
PRODUCTION_HTTP_PORT = 80
|
17
|
-
PRODUCTION_HTTPS_PORT = 443
|
18
|
-
|
19
|
-
DEFAULT_HTTP_PORT = (ENV['DEFAULT_HTTP_PORT' ] || 80).to_i
|
20
|
-
DEFAULT_HTTPS_PORT = (ENV['DEFAULT_HTTPS_PORT'] || 443).to_i
|
21
|
-
|
22
|
-
attr_reader :httpclient
|
23
|
-
attr_reader :base_url
|
24
|
-
|
25
|
-
attr_reader :auth_token
|
26
|
-
attr_reader :login_auth
|
27
|
-
|
28
|
-
# Magellan::Cli::Loginのコンストラクタです。
|
29
|
-
#
|
30
|
-
# @param [String] base_url_or_host 接続先の基準となるURLあるいはホスト名
|
31
|
-
# @param [Hash] options オプション
|
32
|
-
# @option options [String] :api_version APIのバージョン。デフォルトは "1.0.0"
|
33
|
-
def initialize(base_url_or_host = nil, options = {})
|
34
|
-
base_url_or_host ||= (ENV["MAGELLAN_SITE"] || "https://api-asia.magellanic-clouds.com")
|
35
|
-
if base_url_or_host =~ URI.regexp
|
36
|
-
@base_url = base_url_or_host.sub(/\/\Z/, '')
|
37
|
-
uri = URI.parse(@base_url)
|
38
|
-
else
|
39
|
-
if config_path = search_file(".magellan-cli.yml")
|
40
|
-
config = YAML.load_file_with_erb(config_path)
|
41
|
-
options = config[base_url_or_host.to_s].deep_symbolize_keys.update(options)
|
42
|
-
end
|
43
|
-
uri = URI::Generic.build({scheme: "http", host: base_url_or_host, port: DEFAULT_HTTP_PORT}.update(options))
|
44
|
-
@base_url = uri.to_s
|
45
|
-
end
|
46
|
-
|
47
|
-
@httpclient = HTTPClient.new
|
48
|
-
@httpclient.ssl_config.verify_mode = nil # 自己署名の証明書をOKにする
|
49
|
-
@login_auth = load_selections["login"]
|
50
|
-
end
|
51
|
-
|
52
|
-
def login_form_url
|
53
|
-
@login_form_url ||= base_url + "/users/sign_in.html"
|
54
|
-
end
|
55
|
-
def login_url
|
56
|
-
@login_url ||= base_url + "/api/sign_in.json"
|
57
|
-
end
|
58
|
-
|
59
|
-
def check_login_auth!
|
60
|
-
auth = login_auth
|
61
|
-
if auth.nil? || auth.empty?
|
62
|
-
raise Magellan::Cli::Error, I18n.t(:not_logged_in, scope: [:login, :check_login_auth], command: File.basename($0))
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
# magellan-apiサーバに接続してログインの検証とアクセストークンの保存を行います。
|
67
|
-
#
|
68
|
-
# @return [boolean] login成功/失敗
|
69
|
-
def api_login!(email, password)
|
70
|
-
@auth_token ||= get_auth_token
|
71
|
-
params = {
|
72
|
-
"user" => {
|
73
|
-
"email" => email,
|
74
|
-
"password" => password
|
75
|
-
},
|
76
|
-
"authenticity_token" => @auth_token
|
77
|
-
}.to_json
|
78
|
-
res2 = Ssl.retry_on_ssl_error("login"){ @httpclient.post(login_url, params, JSON_HEADER) }
|
79
|
-
|
80
|
-
case res2.status
|
81
|
-
when 200...300 then logined = true
|
82
|
-
else logined = false
|
83
|
-
end
|
84
|
-
|
85
|
-
if logined
|
86
|
-
body = JSON.parse res2.body
|
87
|
-
update_selections("login" => {"email" => email, "token" => body["token"] })
|
88
|
-
else
|
89
|
-
update_selections("login" => nil)
|
90
|
-
end
|
91
|
-
logined
|
92
|
-
end
|
93
|
-
|
94
|
-
def get_auth_token
|
95
|
-
res = Ssl.retry_on_ssl_error("login_form"){ @httpclient.get(login_form_url) }
|
96
|
-
doc = Nokogiri::HTML.parse(res.body, login_form_url, res.body_encoding.to_s)
|
97
|
-
node = doc.xpath('//input[@name="authenticity_token"]').first
|
98
|
-
unless node
|
99
|
-
raise Cli::Error.new("fail to login Magellan")
|
100
|
-
end
|
101
|
-
node.attribute('value').value
|
102
|
-
end
|
103
|
-
|
104
|
-
# @httpclient.inspectの戻り値の文字列が巨大なので、inspectで出力しないようにします。
|
105
|
-
def inspect
|
106
|
-
r = "#<#{self.class.name}:#{self.object_id} "
|
107
|
-
fields = (instance_variables - [:@httpclient]).map{|f| "#{f}=#{instance_variable_get(f).inspect}"}
|
108
|
-
r << fields.join(", ") << ">"
|
109
|
-
end
|
110
|
-
|
111
|
-
def search_file(basename)
|
112
|
-
dirs = [".", "./config", ENV['HOME']].join(",")
|
113
|
-
Dir["{#{dirs}}/#{basename}"].select{|path| File.readable?(path)}.first
|
114
|
-
end
|
115
|
-
private :search_file
|
116
|
-
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|