iij-sakagura 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ _yardoc
7
+ Gemfile.lock
8
+ coverage
9
+ doc/
10
+ pkg
11
+ vendor
12
+ spec/reports
13
+ test/tmp
14
+ test/version_tmp
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source 'https://rubygems.org'
2
+
3
+ group :development do
4
+ gem 'rake'
5
+ gem 'yard'
6
+ end
7
+
8
+ group :test do
9
+ gem 'rspec'
10
+ gem 'simplecov', :require => false
11
+ gem 'simplecov-rcov', :require => false
12
+ gem 'webmock'
13
+ gem 'vcr', '~> 2.8.0'
14
+ end
15
+
16
+ gemspec
data/LICENSE.txt ADDED
File without changes
data/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # IIJ API SDK for Ruby
2
+
3
+ ## Installation
4
+
5
+ Add this line to your application's Gemfile:
6
+
7
+ gem 'iij/sakagura'
8
+
9
+ And then execute:
10
+
11
+ $ bundle
12
+
13
+ Or install it yourself as:
14
+
15
+ $ gem install iij-sakagura
16
+
17
+ ## Usage
18
+
19
+ ### Configuration
20
+
21
+ ### Example
22
+
23
+
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :test => :spec
7
+ task :default => :spec
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'iij/sakagura/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "iij-sakagura"
8
+ spec.version = IIJ::Sakagura::VERSION
9
+ spec.authors = ["Takahiro HIMURA"]
10
+ spec.email = ["taka@himura.jp"]
11
+ spec.description = %q{IIJ API platform (Sakagura) SDK}
12
+ spec.summary = %q{IIJ API platform (Sakagura) SDK}
13
+ spec.homepage = "https://github.com/iij/ruby-iij-sakagura"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "json"
22
+ spec.add_dependency "faraday"
23
+ spec.add_dependency "faraday_middleware"
24
+ end
@@ -0,0 +1,41 @@
1
+ module IIJ
2
+ module Sakagura
3
+ module Core
4
+ module Error
5
+ class APIError < StandardError
6
+ def initialize(message = nil, *args)
7
+ if message and message.kind_of? Hash
8
+ @details = message
9
+ if res = @details.fetch(:body, {})["ErrorResponse"]
10
+ @error_type = res["ErrorType"]
11
+ message = res["ErrorMessage"] if res["ErrorMessage"]
12
+ end
13
+ end
14
+
15
+ super
16
+ end
17
+
18
+ attr_reader :details
19
+ attr_reader :error_type
20
+
21
+ [:status, :headers, :body].each do |key|
22
+ define_method(key) do
23
+ details[key]
24
+ end
25
+ end
26
+ end
27
+
28
+ class BadRequest < APIError; end
29
+ class Unauthorized < APIError; end
30
+ class Forbidden < APIError; end
31
+ class NotFound < APIError; end
32
+ class ClientError < APIError; end
33
+ class SystemError < APIError; end
34
+ class BadGateway < APIError; end
35
+ class ServiceUnavailable < APIError; end
36
+ class ServerError < APIError; end
37
+ class BackendError < APIError; end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,40 @@
1
+ module IIJ
2
+ module Sakagura
3
+ module Core
4
+ def self.hash_to_query(hash)
5
+ HashToQueryConverter.convert_hash(hash)
6
+ end
7
+ module HashToQueryConverter
8
+ def self.convert_hash(hash, prefix = nil)
9
+ hash.inject({}) do |stow, pair|
10
+ k, v = pair
11
+ full_key = (prefix ? "#{prefix}." : "") + k
12
+
13
+ if v.kind_of? Array
14
+ stow.merge(self.convert_array(v, full_key))
15
+ elsif v.kind_of? Hash
16
+ stow.merge(self.convert_hash(v, full_key))
17
+ else
18
+ stow.merge({ full_key => v })
19
+ end
20
+ end
21
+ end
22
+
23
+ def self.convert_array(arr, prefix)
24
+ ret = {}
25
+ arr.each.with_index(1) do |v, i|
26
+ full_key = "#{prefix}.#{i}"
27
+ if v.kind_of? Array
28
+ ret.merge!(self.convert_array(v, full_key))
29
+ elsif v.kind_of? Hash
30
+ ret.merge!(self.convert_hash(v, full_key))
31
+ else
32
+ ret[full_key] = v
33
+ end
34
+ end
35
+ ret
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,56 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'faraday'
3
+ require 'iij/sakagura/core/error'
4
+
5
+ module IIJ
6
+ module Sakagura
7
+ module Core
8
+ module Middleware
9
+ class RaiseError < Faraday::Response::Middleware
10
+ def initialize(app)
11
+ super(app)
12
+ end
13
+
14
+ def on_complete(env)
15
+ case env[:status]
16
+ when 200...300
17
+ nil
18
+ when 400
19
+ raise IIJ::Sakagura::Core::Error::BadRequest, response_values(env)
20
+ when 401
21
+ raise IIJ::Sakagura::Core::Error::Unauthorized, response_values(env)
22
+ when 403
23
+ raise IIJ::Sakagura::Core::Error::Forbidden, response_values(env)
24
+ when 404
25
+ raise IIJ::Sakagura::Core::Error::NotFound, response_values(env)
26
+ when 400...500
27
+ raise IIJ::Sakagura::Core::Error::ClientError, response_values(env)
28
+ when 500
29
+ raise IIJ::Sakagura::Core::Error::SystemError, response_values(env)
30
+ when 502
31
+ raise IIJ::Sakagura::Core::Error::BadGateway, response_values(env)
32
+ when 503
33
+ raise IIJ::Sakagura::Core::Error::ServiceUnavailable, response_values(env)
34
+ when 500...600
35
+ raise IIJ::Sakagura::Core::Error::ServerError, response_values(env)
36
+ end
37
+
38
+ parse_body(env)
39
+ end
40
+
41
+ def response_values(env)
42
+ {:status => env[:status], :headers => env[:response_headers], :body => env[:body]}
43
+ end
44
+
45
+ def parse_body(env)
46
+ if env[:body].has_key? "ErrorResponse"
47
+ raise IIJ::Sakagura::Core::Error::BackendError, response_values(env)
48
+ end
49
+ end
50
+ end
51
+
52
+ Faraday.register_middleware :response, :iij_sakagura_raise_error => RaiseError
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,71 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'faraday'
3
+ require 'time'
4
+ require 'base64'
5
+
6
+ module IIJ
7
+ module Sakagura
8
+ module Core
9
+ module Middleware
10
+ class SignatureV2 < Faraday::Middleware
11
+ SIGNATURE_KEY = "Signature".freeze
12
+ def initialize(app, options = {})
13
+ @access_key = options[:access_key]
14
+ @secret_key = options[:secret_key]
15
+ @expire_after = options[:expire_after] || 3600
16
+ super(app)
17
+ end
18
+
19
+ def call(env)
20
+ env[:body] = required_params.merge(env[:body])
21
+ str = make_canonicalized_string(env)
22
+ env[:body][SIGNATURE_KEY] = generate_signature(str)
23
+ @app.call(env)
24
+ end
25
+
26
+ def required_params
27
+ expire = Time.now + @expire_after
28
+
29
+ {
30
+ "AccessKeyId" => @access_key,
31
+ "SignatureVersion" => "2",
32
+ "SignatureMethod" => "HmacSHA256",
33
+ "Expire" => expire.utc.xmlschema
34
+ }
35
+ end
36
+
37
+ def make_canonicalized_string(env)
38
+ url = env[:url]
39
+ hh =
40
+ if [80, 443].include? url.port
41
+ url.host
42
+ else
43
+ "#{url.host}:#{url.port}"
44
+ end
45
+ [
46
+ env[:method].upcase,
47
+ hh,
48
+ url.path,
49
+ make_query_str(env[:body])
50
+ ].join("\n")
51
+ end
52
+
53
+ def escape(str)
54
+ CGI.escape(str).gsub('+', '%20').gsub('%7E', '~')
55
+ end
56
+
57
+ def generate_signature(str)
58
+ digest_method = OpenSSL::Digest::SHA256.new
59
+ digest = OpenSSL::HMAC.digest(digest_method, @secret_key, str)
60
+ Base64.encode64(digest).chomp
61
+ end
62
+
63
+ def make_query_str(params)
64
+ params.sort_by{|k,v| k}.map{|k,v| escape(k.to_s) + "=" + escape(v.to_s) }.join('&')
65
+ end
66
+ end
67
+ Faraday.register_middleware :request, :signature_v2 => SignatureV2
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,47 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+ require 'iij/sakagura/core/hash_to_query'
4
+ require 'iij/sakagura/core/middleware/signature_v2'
5
+ require 'iij/sakagura/core/middleware/iij_sakagura_raise_error'
6
+
7
+ module IIJ
8
+ module Sakagura
9
+ module Core
10
+ class QueryClient
11
+ def initialize(opts = {})
12
+ @endpoint = opts[:endpoint]
13
+ @access_key = opts[:access_key]
14
+ @secret_key = opts[:secret_key]
15
+ @api_version = opts[:api_version]
16
+ @expire_after = opts[:expire_after]
17
+ end
18
+
19
+ attr_reader :endpoint
20
+
21
+ def agent
22
+ @agent ||= Faraday.new(:url => @endpoint) do |builder|
23
+ builder.request :signature_v2, :access_key => @access_key, :secret_key => @secret_key, :expire_after => @expire_after
24
+ builder.request :url_encoded
25
+ builder.response :iij_sakagura_raise_error
26
+ builder.response :json, :content_type => /\bjson$/
27
+ builder.adapter Faraday.default_adapter
28
+ end
29
+ end
30
+
31
+ def make_request(action, params = {})
32
+ {
33
+ "Action" => action,
34
+ "APIVersion" => @api_version
35
+ }.merge(IIJ::Sakagura::Core.hash_to_query(params))
36
+ end
37
+
38
+ def post(action, params = {})
39
+ res = agent.post('/json') do |req|
40
+ req.body = make_request(action, params)
41
+ end
42
+ res.body[action + 'Response']
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,32 @@
1
+ require 'forwardable'
2
+ require 'iij/sakagura/core/query_client'
3
+
4
+ module IIJ
5
+ module Sakagura
6
+ module GP
7
+ class Client
8
+ extend Forwardable
9
+
10
+ DEFAULT_OPTIONS = {
11
+ :endpoint => 'https://gp.api.iij.jp/',
12
+ :api_version => '20130901'
13
+ }
14
+
15
+ def initialize(opts = {})
16
+ opts = DEFAULT_OPTIONS.merge(opts)
17
+ @client = ::IIJ::Sakagura::Core::QueryClient.new(opts)
18
+ end
19
+
20
+ def_delegators :@client, :get, :post
21
+
22
+ def gp(gp_service_code)
23
+ GP.new(self, gp_service_code)
24
+ end
25
+
26
+ def gp_service_code_list
27
+ post("GetGpServiceCodeList")["GpServiceCodeList"]
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,138 @@
1
+ require 'forwardable'
2
+
3
+ module IIJ
4
+ module Sakagura
5
+ module GP
6
+ class GC
7
+ def initialize(client, gp_service_code, gc_service_code)
8
+ @client = client
9
+ @gp_service_code = gp_service_code
10
+ @gc_service_code = gc_service_code
11
+ end
12
+ attr_reader :gp_service_code
13
+ attr_reader :gc_service_code
14
+
15
+ def inspect
16
+ %[#<#{self.class.name} @gp_service_code="#{gp_service_code}", @gc_service_code="#{gc_service_code}">]
17
+ end
18
+
19
+ def default_args
20
+ {
21
+ "GpServiceCode" => gp_service_code,
22
+ "GcServiceCode" => gc_service_code
23
+ }
24
+ end
25
+
26
+ def describe_virtual_machine
27
+ call('DescribeVirtualMachine')
28
+ end
29
+
30
+ def info(opts = {})
31
+ @info = nil if opts[:force]
32
+ @info ||= describe_virtual_machine
33
+ @info
34
+ end
35
+
36
+ def info!
37
+ info(:force => true)
38
+ end
39
+
40
+ def [](attr)
41
+ info[attr]
42
+ end
43
+
44
+ def get_contract_status
45
+ call('GetContractStatus')['Status']
46
+ end
47
+
48
+ def contract_status(opts = {})
49
+ @contract_status = nil if opts[:force]
50
+ @contract_status ||= get_contract_status
51
+ @contract_status
52
+ end
53
+
54
+ def contract_status!
55
+ contract_status(:force => true)
56
+ end
57
+
58
+ def get_virtual_machine_status
59
+ call('GetVirtualMachineStatus')['Status']
60
+ end
61
+
62
+ def status(opts = {})
63
+ @status = nil if opts[:force]
64
+ @status ||= get_virtual_machine_status
65
+ @status
66
+ end
67
+
68
+ def status!
69
+ status(:force => true)
70
+ end
71
+
72
+ def start
73
+ call('StartVirtualMachine')
74
+ end
75
+
76
+ def stop(force = false)
77
+ call('StopVirtualMachine', { "Force" => force ? "Yes" : "No" })
78
+ end
79
+
80
+ def reboot
81
+ call('RebootVirtualMachine')
82
+ end
83
+
84
+ def initialize_vm(var_device_name = nil)
85
+ opts = {}
86
+ opts["VarDeviceName"] = var_device_name if var_device_name
87
+ call('InitializeVirtualMachine', opts)
88
+ end
89
+
90
+ def import_ssh_public_key(public_key)
91
+ call('ImportRootSshPublicKey', "PublicKey" => public_key)
92
+ end
93
+
94
+ def set_label(label)
95
+ @client.post("SetLabel",
96
+ "GpServiceCode" => gp_service_code,
97
+ "ServiceCode" => gc_service_code,
98
+ "Label" => label)
99
+ end
100
+ alias :label= :set_label
101
+
102
+ def change_vm_type(vm_type)
103
+ call('ChangeVirtualMachineType', "VirtualMachineType" => vm_type)
104
+ end
105
+
106
+ def attach_fwlb(gl_service_code)
107
+ call('AttachFwLb', "GlServiceCode" => gl_service_code)
108
+ end
109
+
110
+ def detach_fwlb(gl_service_code)
111
+ call('DetachFwLb', "GlServiceCode" => gl_service_code)
112
+ end
113
+
114
+ def wait_for_start(opts = {}, &block)
115
+ wait_while(proc { status! == "Starting" }, opts, &block)
116
+ end
117
+
118
+ def wait_for_stop(opts = {}, &block)
119
+ wait_while(proc { status! == "Stopping" }, opts, &block)
120
+ end
121
+
122
+ def wait_while(checker, opts = {}, &block)
123
+ opts[:wait_sec] ||= 10
124
+ catch(:abort) do
125
+ while checker.call
126
+ block.call if block
127
+ sleep opts[:wait_sec]
128
+ end
129
+ end
130
+ end
131
+
132
+ def call(api_name, params = {})
133
+ @client.post(api_name, default_args.merge(params))
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,162 @@
1
+ require 'forwardable'
2
+
3
+ module IIJ
4
+ module Sakagura
5
+ module GP
6
+ class GL
7
+ def initialize(client, gp_service_code, gl_service_code)
8
+ @client = client
9
+ @gp_service_code = gp_service_code
10
+ @gl_service_code = gl_service_code
11
+ end
12
+ attr_reader :gp_service_code
13
+ attr_reader :gl_service_code
14
+
15
+ def inspect
16
+ %[#<#{self.class.name} @gp_service_code="#{gp_service_code}", @gl_service_code="#{gl_service_code}">]
17
+ end
18
+
19
+ def default_args
20
+ {
21
+ "GpServiceCode" => gp_service_code,
22
+ "GlServiceCode" => gl_service_code
23
+ }
24
+ end
25
+
26
+ def get_contract_status
27
+ call('GetContractStatus')['Status']
28
+ end
29
+
30
+ def describe_lb
31
+ call('DescribeLb')
32
+ end
33
+
34
+ def describe_fw
35
+ call('DescribeFw')
36
+ end
37
+
38
+ def lb_info(force = false)
39
+ @lb_info = nil if force
40
+ @lb_info ||= describe_lb
41
+ @lb_info
42
+ end
43
+
44
+ def lb_info!
45
+ lb_info(force = true)
46
+ end
47
+
48
+ def set_label(label)
49
+ @client.post("SetLabel",
50
+ "GpServiceCode" => gp_service_code,
51
+ "ServiceCode" => gl_service_code,
52
+ "Label" => label)
53
+ end
54
+ alias :label= :set_label
55
+
56
+ def add_or_update_virtual_server(virtual_server_name, port, protocol, pool, traffic_ip_name_list = nil)
57
+ lb_vserver = lb_info['VirtualServerList'].find{|lb_vserver| lb_vserver['Name'] == virtual_server_name }
58
+ if lb_vserver
59
+ # already exist
60
+ set_lb_virtual_server(virtual_server_name, port, protocol, pool, traffic_ip_name_list)
61
+ else
62
+ # create a new vserver
63
+ add_lb_virtual_server(virtual_server_name, port, protocol, pool, traffic_ip_name_list)
64
+ end
65
+ end
66
+
67
+ def add_lb_virtual_server(virtual_server_name, port, protocol, pool, traffic_ip_name_list = nil)
68
+ opts = {
69
+ "VirtualServerName" => virtual_server_name,
70
+ "Port" => port,
71
+ "Protocol" => protocol,
72
+ "Pool" => pool
73
+ }
74
+ opts["TrafficIpName"] = traffic_ip_name_list if traffic_ip_name_list
75
+ call('AddLbVirtualServer', opts)
76
+ end
77
+
78
+ def set_lb_virtual_server(virtual_server_name, port, protocol, pool, traffic_ip_name_list = nil)
79
+ opts = {
80
+ "VirtualServerName" => virtual_server_name,
81
+ "Port" => port,
82
+ "Protocol" => protocol,
83
+ "Pool" => pool
84
+ }
85
+ opts["TrafficIpName"] = traffic_ip_name_list if traffic_ip_name_list
86
+ call('SetLbVirtualServer', opts)
87
+ end
88
+
89
+ def delete_lb_virtual_server(virtual_server_name)
90
+ call('DeleteLbVirtualServer', "VirtualServerName" => virtual_server_name)
91
+ end
92
+
93
+ def add_or_update_pool(pool, nodes)
94
+ lb_pool = lb_info['PoolList'].find{|lb_pool| lb_pool['Name'] == pool }
95
+ if lb_pool
96
+ # already exist
97
+ set_lb_pool(pool, nodes)
98
+ else
99
+ # create a new pool
100
+ add_lb_pool(pool, nodes)
101
+ end
102
+ end
103
+
104
+ def add_lb_pool(pool, nodes)
105
+ opts = {
106
+ "Pool" => pool
107
+ }
108
+ nodes.each.with_index(1) do |node, i|
109
+ opts["NodeIpAddress.#{i}"] = node[0]
110
+ opts["NodePort.#{i}"] = node[1]
111
+ end
112
+
113
+ call("AddLbPool", opts)
114
+ end
115
+
116
+ def set_lb_pool(pool, nodes)
117
+ opts = {
118
+ "Pool" => pool
119
+ }
120
+ nodes.each.with_index(1) do |node, i|
121
+ opts["NodeIpAddress.#{i}"] = node[0]
122
+ opts["NodePort.#{i}"] = node[1]
123
+ end
124
+
125
+ call("SetLbPool", opts)
126
+ end
127
+
128
+ def add_lb_node(pool, node)
129
+ opts = {
130
+ "Pool" => pool,
131
+ "NodeIpAddress" => node[0],
132
+ "NodePort" => node[1]
133
+ }
134
+
135
+ call("AddLbNode", opts)
136
+ end
137
+
138
+ def delete_lb_pool(pool)
139
+ call("DeleteLbPool", "Pool" => pool)
140
+ end
141
+
142
+ def delete_lb_node(pool, node)
143
+ opts = {
144
+ "Pool" => pool,
145
+ "NodeIpAddress" => node[0],
146
+ "NodePort" => node[1]
147
+ }
148
+
149
+ call("DeleteLbNode", opts)
150
+ end
151
+
152
+ def change_fw_lb_option_type(type)
153
+ call("ChangeFwLbOptionType", "Type" => type)
154
+ end
155
+
156
+ def call(api_name, params = {})
157
+ @client.post(api_name, default_args.merge(params))
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,59 @@
1
+ require 'forwardable'
2
+
3
+ module IIJ
4
+ module Sakagura
5
+ module GP
6
+ class GP
7
+ def initialize(client, gp_service_code)
8
+ @client = client
9
+ @gp_service_code = gp_service_code
10
+ end
11
+ attr_reader :gp_service_code
12
+
13
+ def inspect
14
+ %[#<#{self.class.name} @gp_service_code="#{gp_service_code}">]
15
+ end
16
+
17
+ def gc(gc_service_code)
18
+ ::IIJ::Sakagura::GP::GC.new(@client, gp_service_code, gc_service_code)
19
+ end
20
+
21
+ def gl(gl_service_code)
22
+ ::IIJ::Sakagura::GP::GL.new(@client, gp_service_code, gl_service_code)
23
+ end
24
+
25
+ def default_args
26
+ { "GpServiceCode" => gp_service_code }
27
+ end
28
+
29
+ def service_code_list
30
+ call("GetServiceCodeList")
31
+ end
32
+
33
+ def contract_information
34
+ call("GetContractInformation")
35
+ end
36
+
37
+ def add_clone_virtual_machines(params = {})
38
+ call("AddCloneVirtualMachines", params)
39
+ end
40
+
41
+ def add_virtual_machines(params = {})
42
+ call("AddVirtualMachines", params)
43
+ end
44
+
45
+ def add_fw_lb_option(params = {})
46
+ call("AddFwLbOption", params)
47
+ end
48
+
49
+ def get_virtual_machine_status_list(gc_list)
50
+ call("GetVirtualMachineStatusList", "GcServiceCode" => gc_list)
51
+ end
52
+
53
+ def call(api_name, params = {})
54
+ @client.post(api_name, default_args.merge(params))
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,4 @@
1
+ require 'iij/sakagura/gp/client'
2
+ require 'iij/sakagura/gp/gp'
3
+ require 'iij/sakagura/gp/gc'
4
+ require 'iij/sakagura/gp/gl'
@@ -0,0 +1,5 @@
1
+ module IIJ
2
+ module Sakagura
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ require "iij/sakagura/version"
2
+ require 'iij/sakagura/gp'
@@ -0,0 +1,40 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: <GP_ENDPOINT>/json
6
+ body:
7
+ encoding: UTF-8
8
+ string: AccessKeyId=<ACCESS_KEY>&SignatureVersion=2&SignatureMethod=HmacSHA256&Expire=2013-12-13T03%3A25%3A12Z&Action=GetGpServiceCodeList&APIVersion=20130901&Signature=cJocNhpOTq%2FpWYzcw%2B%2BZlEGnd90sW6kfZiCaTR0ReXc%3D
9
+ headers:
10
+ User-Agent:
11
+ - Faraday v0.8.8
12
+ Content-Type:
13
+ - application/x-www-form-urlencoded
14
+ Accept:
15
+ - ! '*/*'
16
+ response:
17
+ status:
18
+ code: 200
19
+ message: OK
20
+ headers:
21
+ Date:
22
+ - Fri, 13 Dec 2013 02:25:12 GMT
23
+ Server:
24
+ - thin
25
+ Content-Type:
26
+ - application/json; charset=utf-8
27
+ Strict-Transport-Security:
28
+ - max-age=2592000
29
+ X-Content-Type-Options:
30
+ - nosniff
31
+ Connection:
32
+ - close
33
+ Transfer-Encoding:
34
+ - chunked
35
+ body:
36
+ encoding: US-ASCII
37
+ string: ! '{"GetGpServiceCodeListResponse":{"RequestId":"54c22bee-0c47b962-be6162fa-0000e89d-d3d7668c","GpServiceCodeList":["<GP_SERVICE_CODE>"]}}'
38
+ http_version:
39
+ recorded_at: Fri, 13 Dec 2013 02:25:13 GMT
40
+ recorded_with: VCR 2.8.0
@@ -0,0 +1,40 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: <GP_ENDPOINT>/json
6
+ body:
7
+ encoding: UTF-8
8
+ string: AccessKeyId=<ACCESS_KEY>&SignatureVersion=2&SignatureMethod=HmacSHA256&Expire=2013-12-13T03%3A25%3A13Z&Action=GetServiceCodeList&APIVersion=20130901&GpServiceCode=<GP_SERVICE_CODE>&Signature=99FY8wMxFgeP7UU3uLswMQEjT8qDV1Zap8fMdb4OXXk%3D
9
+ headers:
10
+ User-Agent:
11
+ - Faraday v0.8.8
12
+ Content-Type:
13
+ - application/x-www-form-urlencoded
14
+ Accept:
15
+ - ! '*/*'
16
+ response:
17
+ status:
18
+ code: 200
19
+ message: OK
20
+ headers:
21
+ Date:
22
+ - Fri, 13 Dec 2013 02:25:13 GMT
23
+ Server:
24
+ - thin
25
+ Content-Type:
26
+ - application/json; charset=utf-8
27
+ Strict-Transport-Security:
28
+ - max-age=2592000
29
+ X-Content-Type-Options:
30
+ - nosniff
31
+ Connection:
32
+ - close
33
+ Transfer-Encoding:
34
+ - chunked
35
+ body:
36
+ encoding: US-ASCII
37
+ string: ! '{"GetServiceCodeListResponse":{"RequestId":"55c22bee-0d47b962-772762fa-00005c9c-ccd7668c","CustomerCode":"SG9999999","GpServiceCode":"<GP_SERVICE_CODE>","GcServiceCodeList":["<GC_SERVICE_CODE>"],"GnbServiceCodeList":[],"GxServiceCodeList":[],"GlServiceCodeList":[],"GvmServiceCodeList":[],"GvsServiceCodeList":[],"GomServiceCodeList":[]}}'
38
+ http_version:
39
+ recorded_at: Fri, 13 Dec 2013 02:25:13 GMT
40
+ recorded_with: VCR 2.8.0
data/spec/helper.rb ADDED
@@ -0,0 +1,61 @@
1
+ require 'simplecov'
2
+ require 'simplecov-rcov'
3
+
4
+ SimpleCov.formatter = SimpleCov::Formatter::RcovFormatter
5
+ SimpleCov.start do
6
+ add_filter 'vendor/bundle'
7
+ add_filter 'spec'
8
+ end
9
+
10
+ require 'iij/sakagura'
11
+ require 'rspec'
12
+ require 'webmock/rspec'
13
+
14
+ WebMock.disable_net_connect!
15
+
16
+ RSpec.configure do |config|
17
+ config.treat_symbols_as_metadata_keys_with_true_values = true
18
+ end
19
+
20
+ require 'vcr'
21
+ VCR.configure do |c|
22
+ c.cassette_library_dir = 'spec/cassettes'
23
+ c.configure_rspec_metadata!
24
+ c.hook_into :webmock
25
+ c.allow_http_connections_when_no_cassette = true
26
+
27
+ c.filter_sensitive_data("<ACCESS_KEY>") do
28
+ test_access_key
29
+ end
30
+ c.filter_sensitive_data("<SECRET_KEY>") do
31
+ test_secret_key
32
+ end
33
+ c.filter_sensitive_data("<GP_SERVICE_CODE>") do
34
+ test_gp_service_code
35
+ end
36
+ c.filter_sensitive_data("<GP_ENDPOINT>") do
37
+ test_gp_endpoint
38
+ end
39
+ end
40
+
41
+ def test_access_key
42
+ ENV.fetch('IIJ_SAKAGURA_TEST_ACCESS_KEY')
43
+ end
44
+
45
+ def test_secret_key
46
+ ENV.fetch('IIJ_SAKAGURA_TEST_SECRET_KEY')
47
+ end
48
+
49
+ def test_gp_service_code
50
+ ENV.fetch('IIJ_SAKAGURA_TEST_GP_SERVICE_CODE')
51
+ end
52
+
53
+ def test_gp_endpoint
54
+ ENV.fetch('IIJ_SAKAGURA_TEST_GP_ENDPOINT')
55
+ end
56
+
57
+ def signed_gp_client
58
+ IIJ::Sakagura::GP::Client.new(:endpoint => test_gp_endpoint,
59
+ :access_key => test_access_key,
60
+ :secret_key => test_secret_key)
61
+ end
@@ -0,0 +1,96 @@
1
+ require 'helper'
2
+
3
+ shared_examples_for "valid signature" do
4
+ let(:valid_signature) do
5
+ digest = OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, secret_key, string_to_sign)
6
+ Base64.encode64(digest).chomp
7
+ end
8
+
9
+ it do
10
+ response = lambda do |request|
11
+ if CGI.parse(request.body)['Signature'][0] == valid_signature
12
+ { :body => "{}", :status => 200, :headers => { 'Content-Type' => 'application/json' } }
13
+ else
14
+ { :body => "{}", :status => 400, :headers => { 'Content-Type' => 'application/json' } }
15
+ end
16
+ end
17
+
18
+ VCR.turned_off do
19
+ stub_post = stub_request(:post, "https://rspec.api.iij.jp/json").to_return(response)
20
+ client.post(action, params)
21
+ assert_requested(stub_post)
22
+ end
23
+ end
24
+ end
25
+
26
+ describe IIJ::Sakagura::Core::QueryClient do
27
+ let(:endpoint) { "https://rspec.api.iij.jp/" }
28
+ let(:access_key) { "0123456789abcdefghij" }
29
+ let(:secret_key) { "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN" }
30
+ let(:api_version) { "2013-09-01" }
31
+ let(:signed_at) { Time.gm(2013, 10, 1) }
32
+ let(:expire_after) { 3600 }
33
+ before do
34
+ Time.stub(:now).and_return(signed_at)
35
+ end
36
+
37
+ let(:client) do
38
+ IIJ::Sakagura::Core::QueryClient.new(:endpoint => endpoint,
39
+ :access_key => access_key,
40
+ :secret_key => secret_key,
41
+ :api_version => api_version,
42
+ :expire_after => expire_after)
43
+ end
44
+
45
+ describe '#post' do
46
+ describe "signed with valid signature" do
47
+ context "when simple request" do
48
+ let(:action) { "Test" }
49
+ let(:params) { {} }
50
+ let(:string_to_sign) do
51
+ [
52
+ "POST",
53
+ "rspec.api.iij.jp",
54
+ "/json",
55
+ "APIVersion=#{api_version}&AccessKeyId=#{access_key}&Action=#{action}&Expire=2013-10-01T01%3A00%3A00Z&SignatureMethod=HmacSHA256&SignatureVersion=2"
56
+ ].join("\n")
57
+ end
58
+ it_should_behave_like "valid signature"
59
+ end
60
+
61
+ context "when long list params" do
62
+ let(:action) { "Test" }
63
+ let(:params) { (1..10).inject({}) {|h, i| h["Param.#{i}"] = i; h } }
64
+ let(:string_to_sign) do
65
+ param_str = "Param.1=1&Param.10=10&Param.2=2&Param.3=3&Param.4=4&Param.5=5&Param.6=6&Param.7=7&Param.8=8&Param.9=9"
66
+
67
+ [
68
+ "POST",
69
+ "rspec.api.iij.jp",
70
+ "/json",
71
+ "APIVersion=#{api_version}&AccessKeyId=#{access_key}&Action=#{action}&Expire=2013-10-01T01%3A00%3A00Z&#{param_str}&SignatureMethod=HmacSHA256&SignatureVersion=2"
72
+ ].join("\n")
73
+ end
74
+ it_should_behave_like "valid signature"
75
+ end
76
+ end
77
+
78
+ describe "error handler" do
79
+ context "when 401" do
80
+ before do
81
+ stub_request(:post, "https://rspec.api.iij.jp/json").to_return( :status => 401, :body => '{}', :headers => { 'Content-Type' => 'application/json' } )
82
+ end
83
+
84
+ it { expect { client.post("Test", {}) }.to raise_error(IIJ::Sakagura::Core::Error::Unauthorized) }
85
+ end
86
+
87
+ context "when status is ok but response has ErrorResponse key" do
88
+ before do
89
+ stub_request(:post, "https://rspec.api.iij.jp/json").to_return( :status => 200, :body => '{ "ErrorResponse": {} }', :headers => { 'Content-Type' => 'application/json' } )
90
+ end
91
+
92
+ it { expect { client.post("Test", {}) }.to raise_error(IIJ::Sakagura::Core::Error::BackendError) }
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,15 @@
1
+ require 'helper'
2
+
3
+ describe IIJ::Sakagura::GP::Client do
4
+ describe "#gp" do
5
+ it "returns IIJ::Sakagura::GP::GP with specified gp service code" do
6
+ expect(signed_gp_client.gp('gp00000000').gp_service_code).to eq('gp00000000')
7
+ end
8
+ end
9
+
10
+ describe "#gp_service_code_list", :vcr do
11
+ it "returns service code list" do
12
+ expect(signed_gp_client.gp_service_code_list).to be_an(Array)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,19 @@
1
+ require 'helper'
2
+
3
+ describe IIJ::Sakagura::GP::GP do
4
+ subject do
5
+ client = IIJ::Sakagura::GP::Client.new(:endpoint => 'https://gp-stg.api.iij.jp',
6
+ :access_key => test_access_key,
7
+ :secret_key => test_secret_key)
8
+ client.gp(test_gp_service_code)
9
+ end
10
+
11
+ describe "#service_code_list", :vcr do
12
+ let(:response) do
13
+ subject.service_code_list
14
+ end
15
+
16
+ it { expect(response).to be_a(Hash) }
17
+ it { expect(response['GcServiceCodeList']).to be_an(Array) }
18
+ end
19
+ end
@@ -0,0 +1,4 @@
1
+ require 'helper'
2
+
3
+ describe IIJ::Sakagura do
4
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: iij-sakagura
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Takahiro HIMURA
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-12-13 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: json
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: faraday
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: faraday_middleware
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: IIJ API platform (Sakagura) SDK
63
+ email:
64
+ - taka@himura.jp
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .gitignore
70
+ - Gemfile
71
+ - LICENSE.txt
72
+ - README.md
73
+ - Rakefile
74
+ - iij-sakagura.gemspec
75
+ - lib/iij/sakagura.rb
76
+ - lib/iij/sakagura/core/error.rb
77
+ - lib/iij/sakagura/core/hash_to_query.rb
78
+ - lib/iij/sakagura/core/middleware/iij_sakagura_raise_error.rb
79
+ - lib/iij/sakagura/core/middleware/signature_v2.rb
80
+ - lib/iij/sakagura/core/query_client.rb
81
+ - lib/iij/sakagura/gp.rb
82
+ - lib/iij/sakagura/gp/client.rb
83
+ - lib/iij/sakagura/gp/gc.rb
84
+ - lib/iij/sakagura/gp/gl.rb
85
+ - lib/iij/sakagura/gp/gp.rb
86
+ - lib/iij/sakagura/version.rb
87
+ - spec/cassettes/IIJ_Sakagura_GP_Client/_gp_service_code_list/returns_service_code_list.yml
88
+ - spec/cassettes/IIJ_Sakagura_GP_GP/_service_code_list/.yml
89
+ - spec/helper.rb
90
+ - spec/iij/sakagura/core/query_client_spec.rb
91
+ - spec/iij/sakagura/gp/client_spec.rb
92
+ - spec/iij/sakagura/gp/gp_spec.rb
93
+ - spec/iij/sakagura_spec.rb
94
+ homepage: https://github.com/iij/ruby-iij-sakagura
95
+ licenses:
96
+ - MIT
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ! '>='
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ! '>='
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubyforge_project:
115
+ rubygems_version: 1.8.23
116
+ signing_key:
117
+ specification_version: 3
118
+ summary: IIJ API platform (Sakagura) SDK
119
+ test_files:
120
+ - spec/cassettes/IIJ_Sakagura_GP_Client/_gp_service_code_list/returns_service_code_list.yml
121
+ - spec/cassettes/IIJ_Sakagura_GP_GP/_service_code_list/.yml
122
+ - spec/helper.rb
123
+ - spec/iij/sakagura/core/query_client_spec.rb
124
+ - spec/iij/sakagura/gp/client_spec.rb
125
+ - spec/iij/sakagura/gp/gp_spec.rb
126
+ - spec/iij/sakagura_spec.rb