harborapp 0.0.1 → 1.0.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/.gitignore +2 -0
- data/.rspec +2 -0
- data/bin/harbor +54 -0
- data/harborapp.gemspec +3 -0
- data/lib/harborapp/account.rb +33 -0
- data/lib/harborapp/api.rb +77 -0
- data/lib/harborapp/api_error.rb +15 -0
- data/lib/harborapp/api_model.rb +38 -0
- data/lib/harborapp/api_response.rb +92 -0
- data/lib/harborapp/entity.rb +52 -0
- data/lib/harborapp/success_response.rb +16 -0
- data/lib/harborapp/upload.rb +22 -0
- data/lib/harborapp/version.rb +1 -1
- data/lib/harborapp.rb +22 -2
- data/spec/doubles/account.rb +15 -0
- data/spec/doubles/jack.rb +5 -0
- data/spec/doubles/upload.rb +10 -0
- data/spec/harborapp_spec.rb +83 -0
- data/spec/mock_response.rb +1 -0
- metadata +52 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a725ab10740116ff407c7b0eae64d77b368daed3
|
4
|
+
data.tar.gz: 4fbf9f21a80fbc45220fa7ce00297932767f8ffc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 30cd8f0036bb632e125301f44b17a35225fa3f675215cc5bdae5891cf68e6978b37bc20edffdafef23bc076138b106fb7dab86f70e755c27ee5f9663466898ad
|
7
|
+
data.tar.gz: afd2b071658831400ff033615c2bb4808b9432e83f9c35fbd03dcc6acc28500a78e848a38c9a04c020238e3696fbb0e193eeef5dd5c654c4e48d40fa949c98c6
|
data/.gitignore
CHANGED
data/.rspec
ADDED
data/bin/harbor
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
require 'harborapp'
|
4
|
+
require 'commander/import'
|
5
|
+
require 'io/console'
|
6
|
+
|
7
|
+
program :version, '0.0.1'
|
8
|
+
program :description, 'Command-line interface for https://harbor.madebymarket.com file transfer service.'
|
9
|
+
|
10
|
+
command :login do |c|
|
11
|
+
c.syntax = 'harbor login, [options]'
|
12
|
+
c.summary = ''
|
13
|
+
c.description = ''
|
14
|
+
c.example 'description', 'command example'
|
15
|
+
c.action do |args, options|
|
16
|
+
email = ask "Enter your email address: "
|
17
|
+
pw = password "Enter your harbor password (will not be shown):"
|
18
|
+
account = Harborapp::Account.login email, pw
|
19
|
+
if account.success?
|
20
|
+
account.write_creds
|
21
|
+
puts "Logged in, saving your credentials to #{File.expand_path("~/.harbor_auth")}"
|
22
|
+
else
|
23
|
+
puts account.errors
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
command :upload do |c|
|
29
|
+
c.syntax = 'harbor upload, [options]'
|
30
|
+
c.summary = ''
|
31
|
+
c.description = ''
|
32
|
+
c.example 'description', 'command example'
|
33
|
+
c.option '--file STRING', String, 'File to upload'
|
34
|
+
c.action do |args, options|
|
35
|
+
account = Harborapp::Account.from_creds
|
36
|
+
Harborapp.api_key = account.api_key
|
37
|
+
upload = Harborapp::Upload.get_curl_params(options.file)
|
38
|
+
upload.deliver
|
39
|
+
puts "File uploaded! http://s3.amazonaws.com/harborapp/#{upload.key}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
command :logout do |c|
|
44
|
+
c.syntax = 'harbor logout [options]'
|
45
|
+
c.summary = ''
|
46
|
+
c.description = ''
|
47
|
+
c.example 'description', 'command example'
|
48
|
+
c.option '--some-switch', 'Some switch that does something'
|
49
|
+
c.action do |args, options|
|
50
|
+
Harborapp::Account.logout
|
51
|
+
puts "You've been logged out successfully."
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
data/harborapp.gemspec
CHANGED
@@ -18,6 +18,9 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
+
spec.add_dependency "commander"
|
22
|
+
spec.add_dependency "rest-client"
|
23
|
+
|
21
24
|
spec.add_development_dependency "bundler", "~> 1.3"
|
22
25
|
spec.add_development_dependency "rake"
|
23
26
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require_relative "api_model"
|
2
|
+
|
3
|
+
module Harborapp
|
4
|
+
class Account
|
5
|
+
include ApiModel
|
6
|
+
attr_accessor :api_key, :email
|
7
|
+
|
8
|
+
def self.login(email, password)
|
9
|
+
params = {:basic_username => email, :basic_password => password}
|
10
|
+
Harborapp::Api.request :post, "/login", params
|
11
|
+
end
|
12
|
+
|
13
|
+
def write_creds
|
14
|
+
File.open(File.expand_path("~/.harbor_auth"), "w") do |f|
|
15
|
+
f.write({api_key: api_key, email: email}.to_json)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.from_creds
|
20
|
+
fn = File.expand_path("~/.harbor_auth")
|
21
|
+
raise Exception.new("Please log in first") unless File.exist? fn
|
22
|
+
File.open(fn, "r") do |f|
|
23
|
+
hash = JSON.parse f.read
|
24
|
+
self.new hash
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.logout
|
29
|
+
fn = File.expand_path("~/.harbor_auth")
|
30
|
+
File.unlink(fn) if File.exist? fn
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require "rest_client"
|
2
|
+
|
3
|
+
module Harborapp
|
4
|
+
class Api
|
5
|
+
@@api_jack = nil
|
6
|
+
|
7
|
+
def self.jack=(double)
|
8
|
+
@@api_jack = double
|
9
|
+
end
|
10
|
+
def self.jack
|
11
|
+
@@api_jack
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.request method, url, params = {}, api_key = :account_api_key
|
15
|
+
begin
|
16
|
+
basic_username = Harborapp.api_key
|
17
|
+
|
18
|
+
if params[:basic_username]
|
19
|
+
basic_username = params[:basic_username]
|
20
|
+
params.delete(:basic_username)
|
21
|
+
end
|
22
|
+
if params[:basic_password]
|
23
|
+
basic_password = params[:basic_password]
|
24
|
+
params.delete(:basic_password)
|
25
|
+
end
|
26
|
+
|
27
|
+
req_params = { :user => basic_username, :password => basic_password,
|
28
|
+
:method => method, :url => "#{Harborapp.api_url}#{url}", :payload => params}
|
29
|
+
|
30
|
+
self.execute_request(req_params)
|
31
|
+
|
32
|
+
rescue Errno::ECONNREFUSED => e
|
33
|
+
puts e
|
34
|
+
puts e.backtrace
|
35
|
+
raise ApiError.new(500, {}, {"errors" => [{"code" => 993, "message" => "Unable to connect to API server"}]})
|
36
|
+
rescue ExpiredApiKey => e
|
37
|
+
raise e
|
38
|
+
rescue InvalidApiKey => e
|
39
|
+
raise e
|
40
|
+
rescue Exception => e
|
41
|
+
# what kind of generic exceptions might we be loking for?
|
42
|
+
puts e
|
43
|
+
puts e.backtrace
|
44
|
+
raise ApiError.new(500, {}, {"errors" => [{"code" => 996, "message" => "Error getting response from API server "+e.inspect}]})
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.execute_request(params)
|
49
|
+
if Harborapp::Api.jack
|
50
|
+
ApiResponse.new Harborapp::Api.jack.execute(params)
|
51
|
+
else
|
52
|
+
RestClient::Request.new(params).execute do |response, request, result, &block|
|
53
|
+
ApiResponse.new(response)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.xml_request method, url, params = {}
|
59
|
+
basic_username = Harborapp.api_key
|
60
|
+
req_params = { :method => method, :url => url, :payload => params}
|
61
|
+
|
62
|
+
self.execute_xml_request(req_params)
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.execute_xml_request(params)
|
67
|
+
if Harborapp::Api.jack
|
68
|
+
ApiResponse.new Harborapp::Api.jack.execute(params)
|
69
|
+
else
|
70
|
+
RestClient::Request.new(params).execute do |response, request, result, &block|
|
71
|
+
puts response
|
72
|
+
response
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Harborapp
|
2
|
+
class ApiError < StandardError
|
3
|
+
attr_accessor :status, :headers, :errors
|
4
|
+
def initialize(status, headers, body)
|
5
|
+
self.status = status
|
6
|
+
self.headers = headers
|
7
|
+
self.errors = body["errors"]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class ExpiredApiKey < ApiError; end
|
12
|
+
class InvalidApiKey < ApiError; end
|
13
|
+
class UnknownResponse < ApiError; end
|
14
|
+
class NilApiResponse < ApiError; end
|
15
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Harborapp
|
2
|
+
module ApiModel
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(params = {})
|
9
|
+
populate(params)
|
10
|
+
end
|
11
|
+
|
12
|
+
def populate(params = {})
|
13
|
+
params.each do |k,v|
|
14
|
+
instance_variable_set "@#{k}", v
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
attr_accessor :new_record, :errors
|
20
|
+
end
|
21
|
+
|
22
|
+
def errors=(arr); @errors = arr; end
|
23
|
+
def errors; @errors; end
|
24
|
+
|
25
|
+
def new_record?
|
26
|
+
@new_record.nil? ? true : @new_record
|
27
|
+
end
|
28
|
+
|
29
|
+
def success?
|
30
|
+
@errors.nil?
|
31
|
+
end
|
32
|
+
|
33
|
+
def attrs
|
34
|
+
{}.tap {|h| instance_variables.each { |var| h[var[1..-1]] = instance_variable_get(var) } }
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require_relative "entity"
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
module Harborapp
|
5
|
+
class ApiResponse
|
6
|
+
attr_accessor :_body, :_status, :_headers, :_entity, :errors
|
7
|
+
|
8
|
+
def initialize(response)
|
9
|
+
if response.nil? or response.body.nil? or response.code.nil? or response.headers.nil?
|
10
|
+
# this response is broken, raise an error.
|
11
|
+
raise NilApiResponse.new(500, {}, {"errors" => [{"code" => 990, "message" => "API Server sent back an empty response."}]})
|
12
|
+
end
|
13
|
+
|
14
|
+
self._body = ((response.body.is_a? String) ? JSON.parse(response.body) : response.body)
|
15
|
+
self._status = response.code
|
16
|
+
self._headers = response.headers
|
17
|
+
|
18
|
+
case _status
|
19
|
+
when 200, 201
|
20
|
+
self._entity = Harborapp::Entity.new(_body)
|
21
|
+
when 400,401,403,404,500
|
22
|
+
if _body["errors"]
|
23
|
+
self.errors = _body["errors"]
|
24
|
+
# quickly look to see if we have authentication errors. We want to raise
|
25
|
+
# exceptions on api key errors.
|
26
|
+
case errors[0]["code"]
|
27
|
+
when "002"
|
28
|
+
raise InvalidApiKey.new(_status, _hearders, _body)
|
29
|
+
when "007"
|
30
|
+
raise ExpiredApiKey.new(_status, _hearders, _body)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
else
|
34
|
+
# TODO: test unknown resonse and make sure the client can deal with it.
|
35
|
+
raise UnknownResponse.new(_status, _headers, _body)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# enumeratable methods and such
|
40
|
+
def length
|
41
|
+
_entity.list.length if _entity.list
|
42
|
+
end
|
43
|
+
def first
|
44
|
+
if _entity.list
|
45
|
+
_entity.list.first
|
46
|
+
else
|
47
|
+
# TODO: some sort of exception
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def success?
|
52
|
+
errors.nil? or errors.empty?
|
53
|
+
end
|
54
|
+
|
55
|
+
def total_results
|
56
|
+
return nil unless _entity.meta
|
57
|
+
_entity.meta["total_results"]
|
58
|
+
end
|
59
|
+
def offset
|
60
|
+
return nil unless _entity.meta
|
61
|
+
_entity.meta["offset"]
|
62
|
+
end
|
63
|
+
def limit
|
64
|
+
return nil unless _entity.meta
|
65
|
+
_entity.meta["limit"]
|
66
|
+
end
|
67
|
+
|
68
|
+
# if we're trying to access a method directly on the ApiResponse,
|
69
|
+
# the user is probably trying to get an attribute directly from
|
70
|
+
# the single entity that was returned. In this case, we'll simply
|
71
|
+
# look to see if the
|
72
|
+
def method_missing(meth, *args, &block)
|
73
|
+
return nil unless _entity
|
74
|
+
if _entity.object
|
75
|
+
if _entity.object.respond_to? meth
|
76
|
+
_entity.object.send(meth, *args, &block)
|
77
|
+
else
|
78
|
+
raise NoMethodError.new("Unknown attribute #{meth} on ApiResponse or #{_entity.object.class} entity.")
|
79
|
+
end
|
80
|
+
elsif _entity.list
|
81
|
+
if _entity.list.respond_to? meth
|
82
|
+
_entity.list.send(meth, *args, &block)
|
83
|
+
else
|
84
|
+
raise NoMethodError.new("Unknown attribute #{meth} on ApiResponse or #{_entity.list.class} list.")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
class JsonParseError < StandardError; end
|
92
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require_relative "account"
|
2
|
+
require_relative "upload"
|
3
|
+
require_relative "success_response"
|
4
|
+
|
5
|
+
module Harborapp
|
6
|
+
class Entity
|
7
|
+
attr_accessor :list, :object, :meta
|
8
|
+
|
9
|
+
def initialize(json)
|
10
|
+
self.meta = json["meta"] if json["meta"]
|
11
|
+
if json["list"]
|
12
|
+
self.list = Entity.build_from_list(json["list"])
|
13
|
+
else
|
14
|
+
self.object = Entity.build_from_hash(json)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.build_from_list(list)
|
19
|
+
list.map do |e|
|
20
|
+
if e.is_a? Hash
|
21
|
+
self.build_from_hash(e)
|
22
|
+
elsif e.is_a? Array
|
23
|
+
self.build_from_list(e)
|
24
|
+
else
|
25
|
+
# TODO: throw some weird entity error
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.build_from_hash(hash)
|
31
|
+
# our hash should always include a class attribute that we can use
|
32
|
+
# to map back to a proper Entity type
|
33
|
+
#
|
34
|
+
if !hash["success"].nil? and hash.keys.length == 1
|
35
|
+
return Harborapp::SuccessResponse.new hash["success"]
|
36
|
+
end
|
37
|
+
|
38
|
+
hash["new_record"] = false
|
39
|
+
case hash["class"]
|
40
|
+
when "account"
|
41
|
+
Harborapp::Account.new hash
|
42
|
+
when "upload"
|
43
|
+
Harborapp::Upload.new hash
|
44
|
+
else
|
45
|
+
raise NoSuchEntity.new("Unknown return type in API response data: #{hash["class"]}")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class NoSuchEntity < StandardError;
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative "api_model"
|
2
|
+
require "active_support/secure_random"
|
3
|
+
|
4
|
+
module Harborapp
|
5
|
+
class Upload
|
6
|
+
include ApiModel
|
7
|
+
attr_accessor :file_path, :curl_params, :s3_folder, :key
|
8
|
+
|
9
|
+
def self.get_curl_params file
|
10
|
+
params = (file ? {:file => file} : {} )
|
11
|
+
upload = Harborapp::Api.request :post, "/uploads/curl", params
|
12
|
+
upload.file_path = file
|
13
|
+
upload.key = upload.s3_folder + "/" + SecureRandom.hex(6) + "-" + File.basename(file)
|
14
|
+
upload
|
15
|
+
end
|
16
|
+
|
17
|
+
def deliver
|
18
|
+
`curl https://s3.amazonaws.com/harborapp -F AWSAccessKeyId="#{curl_params["AWSAccessKeyId"]}" -F acl="public-read" -F key="#{key}" -F "content-type=#{curl_params["content-type"]}" -F "x-amz-security-token=#{curl_params["x-amz-security-token"]}" -F success_action_status="200" -F policy="#{curl_params["policy"]}" -F signature="#{curl_params["signature"]}" -F file=@"#{file_path}"`
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
data/lib/harborapp/version.rb
CHANGED
data/lib/harborapp.rb
CHANGED
@@ -1,5 +1,25 @@
|
|
1
|
-
|
1
|
+
require_relative "harborapp/version"
|
2
|
+
require_relative "harborapp/api"
|
3
|
+
require_relative "harborapp/api_response"
|
4
|
+
require_relative "harborapp/entity"
|
5
|
+
require_relative "harborapp/api_error"
|
6
|
+
require "rest_client"
|
7
|
+
require "json"
|
2
8
|
|
3
9
|
module Harborapp
|
4
|
-
|
10
|
+
@api_key = nil
|
11
|
+
@api_url = "https://harbor.madebymarket.com/api/v1"
|
12
|
+
|
13
|
+
def self.api_key=(api_key)
|
14
|
+
@api_key = api_key
|
15
|
+
end
|
16
|
+
def self.api_key
|
17
|
+
@api_key
|
18
|
+
end
|
19
|
+
def self.api_url=(api_url)
|
20
|
+
@api_url = api_url
|
21
|
+
end
|
22
|
+
def self.api_url
|
23
|
+
@api_url
|
24
|
+
end
|
5
25
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative "../mock_response"
|
2
|
+
|
3
|
+
def login_success
|
4
|
+
MockResponse.new(200, {"Content-Type" => "application/json"}, {
|
5
|
+
class: "account",
|
6
|
+
email: "user@harbor.madebymarket.com",
|
7
|
+
api_key: "d000c50a4a996ef80a632345caed7dba"
|
8
|
+
}.to_json)
|
9
|
+
end
|
10
|
+
|
11
|
+
def success_response
|
12
|
+
MockResponse.new(200, {"Content-Type" => "application/json"}, {
|
13
|
+
"success" => true,
|
14
|
+
})
|
15
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require_relative "../mock_response"
|
2
|
+
|
3
|
+
def curl_opts_without_filename
|
4
|
+
MockResponse.new(200, {"Content-Type" => "application/json"}, {
|
5
|
+
"class" => "upload",
|
6
|
+
"curl_params"=>{"AWSAccessKeyId"=>"ASIAIWH23YCDV4MBJLBQ", "acl"=>"public-read", "key"=>"__FILENAME__", "content-type"=>"__CONTENT-TYPE__", "file"=>"__FILE__", "x-amz-security-token"=>"AQoDYXdzEMD//////////wEa4AKEVbh1rh2Yn97MpErnUeQaCR8V01JsHZckFPbRggOHu2xBWa0h/WGWhhHkBoh/fsEHdyV/FFbPXTnHAFXdvkk663cSQjPdBtYY+/43wraPXRAlT6UkeWGdObnEyvDTTfoJxsGAx2FigvSmLMz+1LUjTRUpu8LpehTJZpBou84sIFJi+DP049Ku4bzwp3//6U/v3jb5Q8iykixaU7lakry/HVa1UOC2MXD8eshRUKwIoJWwxWuPS9imYb7XBYJ72X9Qj/mVMdzII3fuDBmUzQ1zTwQG+cYOZKoxDewjkM8jIRVvjINjax68bpD35uy0X3Z4zpf9vrCFLOlQoBEDLlsAeIkJnZSi9lgwrfblGYfUZLbcTgDfhubRvp7pv6FMG53vrIfV86t5AUBPS3D5KaHiB3K7zmSIGBUNB44fKPXYDzJka/ooU7xy5O409tSsDXAKwfJHcT4IJXyyO5H7CCkQIJjRhY8F", "success_action_status"=>"200", "policy"=>"eydleHBpcmF0aW9uJzogJzIwMTMtMDctMjNUMTQ6NDc6NTcuMDAwWicsCgkJCQkJJ2NvbmRpdGlvbnMnOiBbCgkJCQkJCXsnYnVja2V0JzogJ2hhcmJvcmFwcCd9LAoJCQkJCQl7J3gtYW16LXNlY3VyaXR5LXRva2VuJzogJ0FRb0RZWGR6RU1ELy8vLy8vLy8vL3dFYTRBS0VWYmgxcmgyWW45N01wRXJuVWVRYUNSOFYwMUpzSFpja0ZQYlJnZ09IdTJ4QldhMGgvV0dXaGhIa0JvaC9mc0VIZHlWL0ZGYlBYVG5IQUZYZHZrazY2M2NTUWpQZEJ0WVkrLzQzd3JhUFhSQWxUNlVrZVdHZE9ibkV5dkRUVGZvSnhzR0F4MkZpZ3ZTbUxNeisxTFVqVFJVcHU4THBlaFRKWnBCb3U4NHNJRkppK0RQMDQ5S3U0Ynp3cDMvLzZVL3YzamI1UThpeWtpeGFVN2xha3J5L0hWYTFVT0MyTVhEOGVzaFJVS3dJb0pXd3hXdVBTOWltWWI3WEJZSjcyWDlRai9tVk1keklJM2Z1REJtVXpRMXpUd1FHK2NZT1pLb3hEZXdqa004aklSVnZqSU5qYXg2OGJwRDM1dXkwWDNaNHpwZjl2ckNGTE9sUW9CRURMbHNBZUlrSm5aU2k5bGd3cmZibEdZZlVaTGJjVGdEZmh1YlJ2cDdwdjZGTUc1M3ZySWZWODZ0NUFVQlBTM0Q1S2FIaUIzSzd6bVNJR0JVTkI0NGZLUFhZRHpKa2Evb29VN3h5NU80MDl0U3NEWEFLd2ZKSGNUNElKWHl5TzVIN0NDa1FJSmpSaFk4Rid9LAoJCQkJCQl7J2FjbCc6ICdwdWJsaWMtcmVhZCd9LAoJCQkJCQl7J3N1Y2Nlc3NfYWN0aW9uX3N0YXR1cyc6ICcyMDAnfSwKCQkJCQkJWydjb250ZW50LWxlbmd0aC1yYW5nZScsIDAsIDEwNzM3NDE4MjRdLAoJCQkJCQlbJ3N0YXJ0cy13aXRoJywgJyRrZXknLCAnOTZmODc2ODcwNjRmLyddLAoJCQkJCQlbJ3N0YXJ0cy13aXRoJywgJyRDb250ZW50LVR5cGUnLCAnJ10sCgkJCQkJXQoJCQkJfQ==", "signature"=>"/Zd4YrigeoqrVXj/6apN9YGb6Bk="}
|
7
|
+
|
8
|
+
}.to_json)
|
9
|
+
end
|
10
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require_relative "../lib/harborapp"
|
2
|
+
require_relative "doubles/jack"
|
3
|
+
require_relative "doubles/account"
|
4
|
+
require_relative "doubles/upload"
|
5
|
+
|
6
|
+
describe Harborapp do
|
7
|
+
before :all do
|
8
|
+
@jack = Harborapp::ApiDouble.new
|
9
|
+
#Harborapp.api_key = "good-user-api-key"
|
10
|
+
Harborapp::Api.jack = @jack
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should let us set an API key on Harborapp module" do
|
14
|
+
Harborapp.api_key = "test-api-key"
|
15
|
+
Harborapp.api_key.should == "test-api-key"
|
16
|
+
end
|
17
|
+
|
18
|
+
context "Logging in and setting credentials" do
|
19
|
+
it "should let a user log in with proper email address and password" do
|
20
|
+
@jack.should_receive(:execute).and_return login_success
|
21
|
+
account = Harborapp::Account.login "user@harborapp.com", "good-password"
|
22
|
+
account._entity.should_not be_nil
|
23
|
+
account._entity.object.should be_a Harborapp::Account
|
24
|
+
account.api_key.should_not be_empty
|
25
|
+
account.email.should_not be_empty
|
26
|
+
end
|
27
|
+
it "should save their api key once logged in" do
|
28
|
+
file = mock('file')
|
29
|
+
File.should_receive(:open).with(File.expand_path("~/.harbor_auth"), "w").and_yield(file)
|
30
|
+
file.should_receive(:write).with({api_key: "test-api-key", email: "test@harbor.madebymarket.com"}.to_json)
|
31
|
+
account = Harborapp::Account.new(
|
32
|
+
email: "test@harbor.madebymarket.com",
|
33
|
+
api_key: "test-api-key"
|
34
|
+
)
|
35
|
+
account.write_creds
|
36
|
+
end
|
37
|
+
it "should read api key from creds file" do
|
38
|
+
file = mock('file')
|
39
|
+
File.should_receive(:open).with(File.expand_path("~/.harbor_auth"), "r").and_yield(file)
|
40
|
+
file.should_receive(:read).and_return({api_key: "test-api-key", email: "test@harbor.madebymarket.com"}.to_json)
|
41
|
+
account = Harborapp::Account.from_creds
|
42
|
+
account.api_key.should eql("test-api-key")
|
43
|
+
account.email.should eql("test@harbor.madebymarket.com")
|
44
|
+
end
|
45
|
+
it "should return nice message with forgot-password link when incorrect"
|
46
|
+
it "should let a user log out and delete their api key" do
|
47
|
+
file = mock('file')
|
48
|
+
File.should_receive(:rm).with(File.expand_path("~/.harbor_auth")).and_return true
|
49
|
+
Harborapp::Account.logout
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "Registering for an account" do
|
54
|
+
it "should let a user register for an account with an email and password"
|
55
|
+
end
|
56
|
+
|
57
|
+
context "sending files" do
|
58
|
+
it "should get curl opts without a filename" do
|
59
|
+
@jack.should_receive(:execute).and_return curl_opts_without_filename
|
60
|
+
upload = Harborapp::Upload.get_curl_params
|
61
|
+
upload.curl_params.should_not be_nil
|
62
|
+
upload.curl_params["AWSAccessKeyId"].should_not be_nil
|
63
|
+
upload.curl_params["acl"].should == "public-read"
|
64
|
+
upload.curl_params["key"].should == "__FILENAME__"
|
65
|
+
upload.curl_params["content-type"].should == "__CONTENT-TYPE__"
|
66
|
+
upload.curl_params["file"].should == "__FILE__"
|
67
|
+
upload.curl_params["x-amz-security-token"].should_not be_empty
|
68
|
+
upload.curl_params["success_action_status"].should == "200"
|
69
|
+
upload.curl_params["policy"].should_not be_empty
|
70
|
+
upload.curl_params["signature"].should_not be_empty
|
71
|
+
end
|
72
|
+
it "should send a file using rest_client and get back its s3 url" do
|
73
|
+
@jack.should_receive(:execute).and_return curl_opts_without_filename, success_response
|
74
|
+
upload = Harborapp::Upload.get_curl_params
|
75
|
+
puts upload.deliver
|
76
|
+
end
|
77
|
+
it "should get curl opts with a filename"
|
78
|
+
it "should get curl opts for a free-level transfer without an api key"
|
79
|
+
it "should let a user send a file using their api key"
|
80
|
+
it "should let a user specify a gpg key to use to encrypt a file before upload"
|
81
|
+
it "should send a file through the free upload area without a key"
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
MockResponse = Struct.new(:code, :headers, :body)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: harborapp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bryan Thompson
|
@@ -10,6 +10,34 @@ bindir: bin
|
|
10
10
|
cert_chain: []
|
11
11
|
date: 2013-07-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: commander
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rest-client
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: bundler
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -41,18 +69,34 @@ dependencies:
|
|
41
69
|
description: CLI for the https://harbor.madebymarket.com file transfer application
|
42
70
|
email:
|
43
71
|
- bryan@madebymarket.com
|
44
|
-
executables:
|
72
|
+
executables:
|
73
|
+
- harbor
|
45
74
|
extensions: []
|
46
75
|
extra_rdoc_files: []
|
47
76
|
files:
|
48
77
|
- .gitignore
|
78
|
+
- .rspec
|
49
79
|
- Gemfile
|
50
80
|
- LICENSE.txt
|
51
81
|
- README.md
|
52
82
|
- Rakefile
|
83
|
+
- bin/harbor
|
53
84
|
- harborapp.gemspec
|
54
85
|
- lib/harborapp.rb
|
86
|
+
- lib/harborapp/account.rb
|
87
|
+
- lib/harborapp/api.rb
|
88
|
+
- lib/harborapp/api_error.rb
|
89
|
+
- lib/harborapp/api_model.rb
|
90
|
+
- lib/harborapp/api_response.rb
|
91
|
+
- lib/harborapp/entity.rb
|
92
|
+
- lib/harborapp/success_response.rb
|
93
|
+
- lib/harborapp/upload.rb
|
55
94
|
- lib/harborapp/version.rb
|
95
|
+
- spec/doubles/account.rb
|
96
|
+
- spec/doubles/jack.rb
|
97
|
+
- spec/doubles/upload.rb
|
98
|
+
- spec/harborapp_spec.rb
|
99
|
+
- spec/mock_response.rb
|
56
100
|
homepage: https://harbor.madebymarket.com
|
57
101
|
licenses:
|
58
102
|
- MIT
|
@@ -77,4 +121,9 @@ rubygems_version: 2.0.3
|
|
77
121
|
signing_key:
|
78
122
|
specification_version: 4
|
79
123
|
summary: CLI for the https://harbor.madebymarket.com file transfer application
|
80
|
-
test_files:
|
124
|
+
test_files:
|
125
|
+
- spec/doubles/account.rb
|
126
|
+
- spec/doubles/jack.rb
|
127
|
+
- spec/doubles/upload.rb
|
128
|
+
- spec/harborapp_spec.rb
|
129
|
+
- spec/mock_response.rb
|