magellan-cli 0.5.9 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|