obarc 0.5.2
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 +7 -0
- data/.codeclimate.yml +19 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +1156 -0
- data/.travis.yml +12 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +78 -0
- data/README.md +133 -0
- data/Rakefile +39 -0
- data/doc/OBarc.html +210 -0
- data/doc/OBarc/Api.html +883 -0
- data/doc/OBarc/Session.html +5399 -0
- data/doc/OBarc/Utils.html +117 -0
- data/doc/OBarc/Utils/Exceptions.html +115 -0
- data/doc/OBarc/Utils/Exceptions/InvalidArgumentError.html +230 -0
- data/doc/OBarc/Utils/Exceptions/InvalidElementError.html +218 -0
- data/doc/OBarc/Utils/Exceptions/InvalidWordError.html +218 -0
- data/doc/OBarc/Utils/Exceptions/MissingArgumentError.html +230 -0
- data/doc/OBarc/Utils/Exceptions/OBarcArgumentError.html +151 -0
- data/doc/OBarc/Utils/Exceptions/OBarcError.html +291 -0
- data/doc/OBarc/Utils/Exceptions/OBarcResponseError.html +520 -0
- data/doc/OBarc/Utils/Exceptions/OverLimitError.html +218 -0
- data/doc/OBarc/Utils/Exceptions/TimeOutError.html +218 -0
- data/doc/OBarc/Utils/Helper.html +115 -0
- data/doc/OBarc/Utils/Helper/ObjectExtensions.html +105 -0
- data/doc/_index.html +239 -0
- data/doc/class_list.html +58 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +57 -0
- data/doc/css/style.css +339 -0
- data/doc/file.README.html +229 -0
- data/doc/file_list.html +60 -0
- data/doc/frames.html +26 -0
- data/doc/index.html +229 -0
- data/doc/js/app.js +219 -0
- data/doc/js/full_list.js +181 -0
- data/doc/js/jquery.js +4 -0
- data/doc/method_list.html +465 -0
- data/doc/top-level-namespace.html +112 -0
- data/graphics/obarc-logo.png +0 -0
- data/graphics/obarc-logo.xcf +0 -0
- data/lib/obarc.rb +14 -0
- data/lib/obarc/api.rb +220 -0
- data/lib/obarc/session.rb +514 -0
- data/lib/obarc/utils/exceptions.rb +35 -0
- data/lib/obarc/utils/helper.rb +18 -0
- data/lib/obarc/version.rb +3 -0
- data/obarc.gemspec +32 -0
- metadata +276 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
3
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
4
|
+
<head>
|
5
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
6
|
+
<title>
|
7
|
+
Top Level Namespace
|
8
|
+
|
9
|
+
— Documentation by YARD 0.8.7.6
|
10
|
+
|
11
|
+
</title>
|
12
|
+
|
13
|
+
<link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8" />
|
14
|
+
|
15
|
+
<link rel="stylesheet" href="css/common.css" type="text/css" charset="utf-8" />
|
16
|
+
|
17
|
+
<script type="text/javascript" charset="utf-8">
|
18
|
+
hasFrames = window.top.frames.main ? true : false;
|
19
|
+
relpath = '';
|
20
|
+
framesUrl = "frames.html#!top-level-namespace.html";
|
21
|
+
</script>
|
22
|
+
|
23
|
+
|
24
|
+
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
|
25
|
+
|
26
|
+
<script type="text/javascript" charset="utf-8" src="js/app.js"></script>
|
27
|
+
|
28
|
+
|
29
|
+
</head>
|
30
|
+
<body>
|
31
|
+
<div id="header">
|
32
|
+
<div id="menu">
|
33
|
+
|
34
|
+
<a href="_index.html">Index</a> »
|
35
|
+
|
36
|
+
|
37
|
+
<span class="title">Top Level Namespace</span>
|
38
|
+
|
39
|
+
|
40
|
+
<div class="noframes"><span class="title">(</span><a href="." target="_top">no frames</a><span class="title">)</span></div>
|
41
|
+
</div>
|
42
|
+
|
43
|
+
<div id="search">
|
44
|
+
|
45
|
+
<a class="full_list_link" id="class_list_link"
|
46
|
+
href="class_list.html">
|
47
|
+
Class List
|
48
|
+
</a>
|
49
|
+
|
50
|
+
<a class="full_list_link" id="method_list_link"
|
51
|
+
href="method_list.html">
|
52
|
+
Method List
|
53
|
+
</a>
|
54
|
+
|
55
|
+
<a class="full_list_link" id="file_list_link"
|
56
|
+
href="file_list.html">
|
57
|
+
File List
|
58
|
+
</a>
|
59
|
+
|
60
|
+
</div>
|
61
|
+
<div class="clear"></div>
|
62
|
+
</div>
|
63
|
+
|
64
|
+
<iframe id="search_frame"></iframe>
|
65
|
+
|
66
|
+
<div id="content"><h1>Top Level Namespace
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
</h1>
|
71
|
+
|
72
|
+
<dl class="box">
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
|
81
|
+
</dl>
|
82
|
+
<div class="clear"></div>
|
83
|
+
|
84
|
+
<h2>Defined Under Namespace</h2>
|
85
|
+
<p class="children">
|
86
|
+
|
87
|
+
|
88
|
+
<strong class="modules">Modules:</strong> <span class='object_link'><a href="OBarc.html" title="OBarc (module)">OBarc</a></span>
|
89
|
+
|
90
|
+
|
91
|
+
|
92
|
+
|
93
|
+
</p>
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
</div>
|
104
|
+
|
105
|
+
<div id="footer">
|
106
|
+
Generated on Tue Apr 4 17:29:29 2017 by
|
107
|
+
<a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
108
|
+
0.8.7.6 (ruby-2.2.5).
|
109
|
+
</div>
|
110
|
+
|
111
|
+
</body>
|
112
|
+
</html>
|
Binary file
|
Binary file
|
data/lib/obarc.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'obarc/version'
|
2
|
+
require 'json'
|
3
|
+
require 'rest-client'
|
4
|
+
|
5
|
+
module OBarc
|
6
|
+
require 'obarc/utils/helper'
|
7
|
+
require 'obarc/session'
|
8
|
+
extend self
|
9
|
+
|
10
|
+
def login!(options)
|
11
|
+
options[:base_url] ||= "#{options[:protocol]}://#{options[:server_host]}:#{options[:server_port]}/api/#{options[:api_version]}"
|
12
|
+
Session.new(username: options[:username], password: options[:password], cookies: Api.post_login(options).cookies)
|
13
|
+
end
|
14
|
+
end
|
data/lib/obarc/api.rb
ADDED
@@ -0,0 +1,220 @@
|
|
1
|
+
require 'obarc/utils/helper'
|
2
|
+
require 'obarc/utils/exceptions'
|
3
|
+
require 'uri'
|
4
|
+
require 'base64'
|
5
|
+
|
6
|
+
module OBarc
|
7
|
+
module Api
|
8
|
+
extend self
|
9
|
+
using Utils::Helper::ObjectExtensions
|
10
|
+
|
11
|
+
DEFAULT_TIMEOUT = 60 * 60 * 1
|
12
|
+
|
13
|
+
VALID_ACTIONS = {
|
14
|
+
get: %i(image profile listings followers following contracts shutdown
|
15
|
+
settings connected_peers routing_table notifications chat_messages
|
16
|
+
chat_conversations sales purchases order cases order_messages ratings
|
17
|
+
btc_price),
|
18
|
+
post: %i(login follow unfollow profile social_accounts
|
19
|
+
contract make_moderator unmake_moderator purchase_contract
|
20
|
+
confirm_order upload_image complete_order settings
|
21
|
+
mark_notification_as_read broadcast mark_chat_message_as_read
|
22
|
+
check_for_payment dispute_contract close_dispute release_funds refund
|
23
|
+
mark_discussion_as_read),
|
24
|
+
delete: %i(social_accounts contract chat_conversation)
|
25
|
+
}.freeze
|
26
|
+
|
27
|
+
# POST api/v1/login
|
28
|
+
def post_login(options = {})
|
29
|
+
url = "#{build_base_url(options)}/login"
|
30
|
+
auth = build_authentication(options)
|
31
|
+
|
32
|
+
raise Utils::Exceptions::MissingArgumentError, [:username, :password] if auth.empty?
|
33
|
+
|
34
|
+
execute method: :post, url: url, headers: {params: auth},
|
35
|
+
verify_ssl: build_verify_ssl(options)
|
36
|
+
end
|
37
|
+
|
38
|
+
def ping(options = {})
|
39
|
+
execute(method: :get,
|
40
|
+
url: "#{build_base_url(options)}/connected_peers?_=#{Time.now.to_i}",
|
41
|
+
headers: build_headers(options), verify_ssl: build_verify_ssl(options))
|
42
|
+
end
|
43
|
+
|
44
|
+
def respond_to_missing?(m, include_private = false)
|
45
|
+
verb = m.to_s.split('_')
|
46
|
+
rest_method = verb[0].to_sym
|
47
|
+
return false unless VALID_ACTIONS.keys.include?(rest_method)
|
48
|
+
|
49
|
+
endpoint = verb[1..-1].join('_')
|
50
|
+
return false if endpoint.nil?
|
51
|
+
|
52
|
+
VALID_ACTIONS[rest_method].include?(endpoint.to_sym)
|
53
|
+
end
|
54
|
+
|
55
|
+
def method_missing(m, *args, &block)
|
56
|
+
super unless respond_to_missing?(m)
|
57
|
+
|
58
|
+
# Many of the calls to restapi.py are uniform enough for DRY code, but the
|
59
|
+
# ones that aren't are mapped here.
|
60
|
+
|
61
|
+
rest_method, endpoint, params, options = case m
|
62
|
+
when :get_image then [:get, 'get_image', args[0], args[1]]
|
63
|
+
when :get_listings then [:get, 'get_listings', args[0], args[1]]
|
64
|
+
when :get_followers then [:get, 'get_followers', args[0], args[1]]
|
65
|
+
when :get_following then [:get, 'get_following', args[0], args[1]]
|
66
|
+
when :post_contract then [:post, 'contracts', args[0], args[1]]
|
67
|
+
when :delete_contract then [:delete, 'contracts', args[0], args[1]]
|
68
|
+
when :get_notifications then [:get, 'get_notifications', nil, args[0]]
|
69
|
+
when :get_chat_conversations then [:get, 'get_chat_conversations', nil, args[0]]
|
70
|
+
when :get_sales then [:get, 'get_sales', nil, args[0]]
|
71
|
+
when :get_purchases then [:get, 'get_purchases', nil, args[0]]
|
72
|
+
when :get_cases then [:get, 'get_cases', nil, args[0]]
|
73
|
+
when :get_ratings then [:get, 'get_ratings', args[0], args[1]]
|
74
|
+
else
|
75
|
+
verb = m.to_s.split('_')
|
76
|
+
a = [verb[0].to_sym, verb[1..-1].join('_')]
|
77
|
+
a << if args.size == 1
|
78
|
+
nil
|
79
|
+
else
|
80
|
+
args[0]
|
81
|
+
end
|
82
|
+
a << args[args.size - 1]
|
83
|
+
end
|
84
|
+
|
85
|
+
url = "#{build_base_url(options)}/#{endpoint}" if !!endpoint && !!options
|
86
|
+
if !!rest_method && !!url && !!options
|
87
|
+
headers = build_headers(options)
|
88
|
+
headers = headers.merge(params: params.compact) if !!params
|
89
|
+
return execute method: rest_method, url: url, headers: headers,
|
90
|
+
verify_ssl: build_verify_ssl(options)
|
91
|
+
end
|
92
|
+
|
93
|
+
raise Utils::Exceptions::OBarcError, "Did not handle #{m} as expected, arguments: #{args}"
|
94
|
+
end
|
95
|
+
|
96
|
+
# POST api/v1/upload_image
|
97
|
+
def post_upload_image(options = {})
|
98
|
+
elements = [:image, :avatar, :header]
|
99
|
+
params = options.slice(*elements)
|
100
|
+
options = options.delete_if { |k, v| elements.include? k }
|
101
|
+
|
102
|
+
url = "#{build_base_url(options)}/upload_images"
|
103
|
+
execute method: :post, url: url,
|
104
|
+
headers: build_headers(options).merge(params: params.compact),
|
105
|
+
verify_ssl: build_verify_ssl(options)
|
106
|
+
end
|
107
|
+
|
108
|
+
# GET api/v1/contracts
|
109
|
+
def get_contracts(contracts, options = {})
|
110
|
+
id = contracts[:id]
|
111
|
+
guid = contracts[:guid]
|
112
|
+
|
113
|
+
if !!id && id.size != 40
|
114
|
+
raise Utils::Exceptions::InvalidArgumentError, id: "must be 40 characters, if present (was: #{id.size})"
|
115
|
+
elsif !!guid && guid.size != 40
|
116
|
+
raise Utils::Exceptions::InvalidArgumentError, guid: "must be 40 characters, if present (was: #{guid.size})"
|
117
|
+
end
|
118
|
+
|
119
|
+
url = "#{build_base_url(options)}/contracts"
|
120
|
+
execute method: :get, url: url,
|
121
|
+
headers: build_headers(options).merge(params: contracts.compact),
|
122
|
+
verify_ssl: build_verify_ssl(options)
|
123
|
+
end
|
124
|
+
|
125
|
+
# GET api/v1/get_chat_messages
|
126
|
+
def get_chat_messages(chat_messages, options = {})
|
127
|
+
guid = chat_messages[:guid]
|
128
|
+
|
129
|
+
if guid.nil? || guid.size != 40
|
130
|
+
raise Utils::Exceptions::InvalidArgumentError, guid: "must be present, 40 characters (was: #{guid.inspect})"
|
131
|
+
end
|
132
|
+
|
133
|
+
url = "#{build_base_url(options)}/get_chat_messages"
|
134
|
+
execute method: :get, url: url,
|
135
|
+
headers: build_headers(options).merge(params: chat_messages.compact),
|
136
|
+
verify_ssl: build_verify_ssl(options)
|
137
|
+
end
|
138
|
+
|
139
|
+
# GET api/v1/get_order
|
140
|
+
def get_order(order, options = {})
|
141
|
+
order_id = order[:order_id]
|
142
|
+
|
143
|
+
if order_id.nil?
|
144
|
+
raise Utils::Exceptions::InvalidArgumentError, order_id: 'must be present'
|
145
|
+
end
|
146
|
+
|
147
|
+
url = "#{build_base_url(options)}/get_order"
|
148
|
+
execute method: :get, url: url,
|
149
|
+
headers: build_headers(options).merge(params: order.compact),
|
150
|
+
verify_ssl: build_verify_ssl(options)
|
151
|
+
end
|
152
|
+
private
|
153
|
+
def execute(options = {})
|
154
|
+
if options[:method] == :post
|
155
|
+
options[:headers][:content_type] = 'application/x-www-form-urlencoded'
|
156
|
+
end
|
157
|
+
|
158
|
+
if !!options[:headers][:params]
|
159
|
+
params = options[:headers].delete(:params)
|
160
|
+
|
161
|
+
if params.values.map(&:class).include? Array
|
162
|
+
# Dropping to a lower level for parameters since they're more
|
163
|
+
# complicated due to the presense of an Array. Note that these
|
164
|
+
# parameters go # outside the header.
|
165
|
+
options[:url] += "?#{URI::encode_www_form params}"
|
166
|
+
elsif params.values.map(&:class).any? { |c| [Tempfile, File, StringIO].include?(c) }
|
167
|
+
# Handling (possibly) large files.
|
168
|
+
options[:payload] = {multipart: true}
|
169
|
+
params.each do |k, v|
|
170
|
+
if v.respond_to? :read
|
171
|
+
options[:payload][k] = Base64.strict_encode64(v.read)
|
172
|
+
else
|
173
|
+
options[:payload][k] = v
|
174
|
+
# FIXME Might want to warn that we are possibly mixing multipart
|
175
|
+
# with simple payload.
|
176
|
+
end
|
177
|
+
end
|
178
|
+
else
|
179
|
+
options[:headers][:params] = params
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
RestClient::Request.execute(options.merge(timeout: DEFAULT_TIMEOUT))
|
184
|
+
end
|
185
|
+
|
186
|
+
def build_authentication(options = {})
|
187
|
+
if options.kind_of? Session
|
188
|
+
{username: options.username, password: options.password}
|
189
|
+
else
|
190
|
+
options.slice(:username, :password)
|
191
|
+
end.compact
|
192
|
+
end
|
193
|
+
|
194
|
+
def build_base_url(options = {})
|
195
|
+
if options.kind_of? Session
|
196
|
+
options.base_url
|
197
|
+
elsif options.kind_of? Hash
|
198
|
+
options[:base_url]
|
199
|
+
else
|
200
|
+
raise Utils::Exceptions::OBarcError, "Unable to build base URL using: #{options.inspect}, expected a OBarc::Session or Hash."
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def build_headers(options = {})
|
205
|
+
if options.kind_of? Session
|
206
|
+
{cookies: options.cookies}
|
207
|
+
else
|
208
|
+
options.slice(:cookies)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def build_verify_ssl(options = {})
|
213
|
+
if options.kind_of? Session
|
214
|
+
!!options.verify_ssl
|
215
|
+
else
|
216
|
+
!!options[:verify_ssl]
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
@@ -0,0 +1,514 @@
|
|
1
|
+
require 'obarc/api'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'logging'
|
4
|
+
|
5
|
+
module OBarc
|
6
|
+
class Session
|
7
|
+
OPTIONS_KEYS = %i(protocol server_host server_port api_version username
|
8
|
+
password base_url logger cookies verify_ssl)
|
9
|
+
|
10
|
+
attr_accessor :cookies, :username, :password, :verify_ssl
|
11
|
+
attr_writer :base_url, :logger
|
12
|
+
|
13
|
+
DEFAULT_OPTIONS = {
|
14
|
+
protocol: 'http',
|
15
|
+
server_host: 'localhost',
|
16
|
+
server_port: '18469',
|
17
|
+
api_version: 'v1',
|
18
|
+
verify_ssl: true
|
19
|
+
}
|
20
|
+
|
21
|
+
def initialize(options = {})
|
22
|
+
OPTIONS_KEYS.each do |k|
|
23
|
+
value = if options.key?(k)
|
24
|
+
options[k]
|
25
|
+
else
|
26
|
+
DEFAULT_OPTIONS[k]
|
27
|
+
end
|
28
|
+
|
29
|
+
instance_variable_set "@#{k}".to_sym, value
|
30
|
+
end
|
31
|
+
|
32
|
+
@base_url ||= base_url
|
33
|
+
@logger ||= logger
|
34
|
+
@cookies ||= Api::post_login(self).cookies
|
35
|
+
end
|
36
|
+
|
37
|
+
def base_url
|
38
|
+
@base_url ||= "#{@protocol}://#{@server_host}:#{@server_port}/api/#{@api_version}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def logger
|
42
|
+
@logger ||= Logging.logger(STDOUT)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Check if there's a valid session.
|
46
|
+
#
|
47
|
+
# @return [Boolean] True if the session is valid.
|
48
|
+
def ping
|
49
|
+
return false if !(json = Api::ping(self))
|
50
|
+
!!JSON[json]['num_peers']
|
51
|
+
rescue RestClient::Unauthorized => e
|
52
|
+
logger.warn(e)
|
53
|
+
false
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns the image for the hash specified.
|
57
|
+
# @param image [Hash] containing the hash: of the target image, required
|
58
|
+
# @return [Object] The image will be returned in .jpg format.
|
59
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-image
|
60
|
+
def image(image); Api::get_image(image, self); end
|
61
|
+
|
62
|
+
# Returns the profile data of the user’s node, or that of a target node.
|
63
|
+
#
|
64
|
+
# @param profile [Hash] containing the guid: of the target node, optional
|
65
|
+
# * The global unique identifier (guid; 40 character hex string) of the node to get the profile data from
|
66
|
+
# * If the guid is omitted, your own node’s profile will be returned
|
67
|
+
# @return [Hash]
|
68
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-profile
|
69
|
+
def profile(profile = nil); JSON[Api::get_profile(profile, self)]; end
|
70
|
+
|
71
|
+
# Returns just the profile's social accounts of the user’s node, or that of a target node.
|
72
|
+
#
|
73
|
+
# @param profile [Hash] containing the guid: of the target node, optional
|
74
|
+
# * The global unique identifier (guid; 40 character hex string) of the node to get the profile data from
|
75
|
+
# * If the guid is omitted, your own node’s social accounts will be returned
|
76
|
+
# @return [Hash]
|
77
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-profile
|
78
|
+
def social_accounts(profile = nil)
|
79
|
+
result = JSON[Api::get_profile(profile, self)]
|
80
|
+
|
81
|
+
if !!result && !!result['profile'] && !!result['profile']['social_accounts']
|
82
|
+
result['profile']['social_accounts']
|
83
|
+
else
|
84
|
+
[]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns the listings of the user’s node, or that of a target node.
|
89
|
+
#
|
90
|
+
# @param listings [Hash] containing the guid: of the target node, optional
|
91
|
+
# * If the guid is omitted, the server will look for listings in your own node’s database.
|
92
|
+
# @return [Hash]
|
93
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-get_listings
|
94
|
+
def listings(listings = nil); JSON[Api::get_listings(listings, self)]; end
|
95
|
+
|
96
|
+
# Finds the listings of the user’s node, or that of a target node.
|
97
|
+
#
|
98
|
+
# @param options [Hash] containing:
|
99
|
+
# * guid: of the target node, optional
|
100
|
+
# *If the guid is omitted, the server will look for listings in your own node’s database.
|
101
|
+
# * pattern: [Regex] search phrase
|
102
|
+
# @return [Hash]
|
103
|
+
def query_listings(options = {})
|
104
|
+
pattern = options.delete(:pattern)
|
105
|
+
all_listings = JSON[Api::get_listings(options, self)]
|
106
|
+
listings = all_listings['listings']
|
107
|
+
|
108
|
+
if !!pattern
|
109
|
+
listings = listings.select do |l|
|
110
|
+
[l['contract_hash'], l['category'], l['title'],
|
111
|
+
l['price'].to_s, l['origin'], l['currency_code'],
|
112
|
+
l['ships_to'].join].join(' ') =~ pattern
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
return {'listings' => listings} if listings === all_listings
|
117
|
+
|
118
|
+
start = Time.now.to_i
|
119
|
+
|
120
|
+
@cache_timestamp = if (start - (@cache_timestamp ||= 0)) > 300
|
121
|
+
@contracts_cache = {}
|
122
|
+
start
|
123
|
+
else
|
124
|
+
@cache_timestamp
|
125
|
+
end
|
126
|
+
|
127
|
+
@contracts_cache ||= {}
|
128
|
+
(all_listings['listings'] - listings).each do |listing|
|
129
|
+
contract_hash = listing['contract_hash']
|
130
|
+
contract = @contracts_cache[contract_hash] ||= contracts(options.merge(id: listing['contract_hash']))
|
131
|
+
next unless !!contract
|
132
|
+
|
133
|
+
l = contract['vendor_offer']['listing']
|
134
|
+
|
135
|
+
if [l['metadata']['expiry'], l['item']['category'], l['item']['sku'],
|
136
|
+
l['item']['description'], l['item']['process_time'],
|
137
|
+
l['item']['keywords'].join].join(' ') =~ pattern
|
138
|
+
listings << listing && next
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
{'listings' => listings}
|
143
|
+
end
|
144
|
+
|
145
|
+
# Returns the followers of the user’s node, or that of a target node.
|
146
|
+
#
|
147
|
+
# @param followers [Hash] containing the guid: of the target node, optional
|
148
|
+
# * If the guid is omitted, the server will look for followers in your own node’s database.
|
149
|
+
# @return [Hash]
|
150
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-get_followers
|
151
|
+
def followers(followers = nil); JSON[Api::get_followers(followers, self)]; end
|
152
|
+
|
153
|
+
# Returns the following of the user’s node, or that of a target node.
|
154
|
+
#
|
155
|
+
# @param following [Hash] containing the guid: of the target node, optional
|
156
|
+
# * If the guid is omitted, the server will look for following in your own node’s database.
|
157
|
+
# @return [Hash]
|
158
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-get_following
|
159
|
+
def following(following = nil); JSON[Api::get_following(following, self)]; end
|
160
|
+
|
161
|
+
# Follows a target node and will cause you to receive notifications from
|
162
|
+
# that node after certain event (e.g. new listing, broadcast messages) and
|
163
|
+
# share some metadata (in future).
|
164
|
+
#
|
165
|
+
# @param follow [Hash] containing the guid: of the target node, required
|
166
|
+
# @return [Hash] containing: "success" => true or false
|
167
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-follow
|
168
|
+
def follow(follow); JSON[Api::post_follow(follow, self)]; end
|
169
|
+
|
170
|
+
# Stop following a target node, will cease to receive notifications and
|
171
|
+
# sharing metadata.
|
172
|
+
#
|
173
|
+
# @param unfollow [Hash] containing the guid: of the target node, required
|
174
|
+
# @return [Hash] containing: "success" => true or false
|
175
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-unfollow
|
176
|
+
def unfollow(unfollow); JSON[Api::post_unfollow(unfollow, self)]; end
|
177
|
+
|
178
|
+
# Add data related to the node's profile into the database, which will be
|
179
|
+
# visible to other nodes.
|
180
|
+
#
|
181
|
+
# @param profile [Hash]
|
182
|
+
# @return [Hash] containing: "success" => true or false
|
183
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-profile
|
184
|
+
def update_profile(profile = {}); JSON[Api::post_profile(profile, self)]; end
|
185
|
+
|
186
|
+
# Adds a social account to the user profile data of the user.
|
187
|
+
#
|
188
|
+
# @param social_account [Hash] e.g.: account_type: 'TWITTER', username: '@inertia186'
|
189
|
+
# @return [Hash] containing: "success" => true or false
|
190
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-social_accounts
|
191
|
+
def add_social_account(social_account = {}); JSON[Api::post_social_accounts(social_account, self)]; end
|
192
|
+
|
193
|
+
# Undocumented.
|
194
|
+
#
|
195
|
+
# @return [Hash]
|
196
|
+
def delete_social_account(social_account = {}); JSON[Api::delete_social_accounts(social_account, self)]; end
|
197
|
+
|
198
|
+
# Retrieves the listings created by either your node or a target node.
|
199
|
+
#
|
200
|
+
# @param contracts [Hash] containing the:
|
201
|
+
# * id: Unique identifier of the contract SHA256 of the JSON formatted contract (40 character hex string), required
|
202
|
+
# * guid: GUID of the node if the call is made to a target node. If omitted, the API will search for a contract ID created by your own node
|
203
|
+
# @return [Hash]
|
204
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-contracts
|
205
|
+
def contracts(contracts = {}); JSON[Api::get_contracts(contracts, self)]; end
|
206
|
+
|
207
|
+
# Creates a listing contract, which is saved to the database and local file
|
208
|
+
# system, as well as publish the keywords in the distributed hash table.
|
209
|
+
#
|
210
|
+
# @param contract [Hash] containing:
|
211
|
+
# * expiration_date: [UTC] Formatted string. The date the contract should expire in string formatted UTC datetime. Example:
|
212
|
+
# * "2015-11-01T00:00 UTC"
|
213
|
+
# * "" if the contract never expires
|
214
|
+
# * metadata_category: [category] Formatted string. Select from:
|
215
|
+
# * physical good
|
216
|
+
# * digital good
|
217
|
+
# * service
|
218
|
+
# * title: [title text] String. Title of the product for sale
|
219
|
+
# * description: [description text] String. Description of the item, content or service
|
220
|
+
# * currency_code: [code] Formatted string. The currency the product is priced in. may either be “btc” or a currency from this list
|
221
|
+
# * price: [value] String. The price per unit in the same currency as currency_code.
|
222
|
+
# * process_time: [time] String. The time it will take to prepare the item for shipping
|
223
|
+
# * nsfw: [true/false] Boolean. Is the item not suitable for work (i.e. 18+)
|
224
|
+
# * shipping_origin: [country/region] Formatted string. Required and only applicable if the metadata_category is a physical good
|
225
|
+
# * Where the item ships from
|
226
|
+
# * Must be a formatted string from this list
|
227
|
+
# * shipping_regions: [locations] Formatted string. Required and only applicable if the metadata_category is a physical good.
|
228
|
+
# * A list of countries/regions where the product will ship to
|
229
|
+
# * Each item in the list must be formatted from this list
|
230
|
+
# * est_delivery_domestic: [time] Estimated delivery time for domestic shipments
|
231
|
+
# * est_delivery_international: [time] String. Estimated delivery time for international shipments.
|
232
|
+
# * terms_conditions: [terms and conditions text] String. Any terms or conditions the user wishes to include.
|
233
|
+
# * returns: [returns policy text] String. Return policy.
|
234
|
+
# * shipping_currency_code: [currency code] Formatted string. The currency code used to price shipping. may either be “btc” or a currency from this list.
|
235
|
+
# * shipping_domestic: [price] String. The price of domestic shipping in the selected currency code.
|
236
|
+
# * shipping_international: [price] String. The price of nternational shipping in the selected currency code.
|
237
|
+
# * keywords: [keyword text] String. A list of string search terms for the listing. Must be fewer than 10.
|
238
|
+
# * category: [category text] Sting. A user-generated category for this product. Will show in store’s category list.
|
239
|
+
# * condition: [condition text] The condition of the product
|
240
|
+
# * sku: [sku text] String. Stock keeping unit (sku) for the listing.
|
241
|
+
# * images: [String, Array<String>] 40 character hex string. A list of SHA256 image hashes. The images should be uploaded using the upload_image api call.
|
242
|
+
# * images: '04192728d0fd8dfe6663f429a5c03a7faf907930'
|
243
|
+
# * images: ['04192728d0fd8dfe6663f429a5c03a7faf907930', '0dee4786fd02d6bc673b50309a3c831acf78ec70']
|
244
|
+
# * image_urls: [Array<String>] An array of image URLs for OBarc to first download then automatically store to this record.
|
245
|
+
# * image_urls: 'http://i.imgur.com/YHBh57j.gif'
|
246
|
+
# * image_urls: ['http://i.imgur.com/uC2KUQ6.png', 'http://i.imgur.com/RliU8Gn.jpg']
|
247
|
+
# * image_data: [Array<String>] An array of Base64 images for OBarc to automatically store to this record.
|
248
|
+
# * image_data: <String>
|
249
|
+
# * image_data: [<String>, <String>]
|
250
|
+
# * free_shipping: [boolean] "true" or "false"
|
251
|
+
# * moderators: [guids] GUID: 40 character hex string. A list of moderator GUIDs that the vendor wishes to use
|
252
|
+
# * Note: the moderator must have been previously returned by the get_moderators websocket call.
|
253
|
+
# * Given the UI workflow, this call should always be made before the contract is set.
|
254
|
+
# * options: [options text] String. A list of options for the product. Example: “size”, “color”
|
255
|
+
# * option: [option text] String.
|
256
|
+
# * For each option in the options list, another argument should be added using that option name and a list of value
|
257
|
+
# * For example, given “color” in the options list, choose from "red", "green", "purple" etc
|
258
|
+
# @return [Hash] containing: "success" => true or false, "id" => Integer
|
259
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-contracts
|
260
|
+
def create_contract(contract = {})
|
261
|
+
# Note, passing contract_id appears to create a clone that re-uses the
|
262
|
+
# original contract_id.
|
263
|
+
|
264
|
+
%i(image_urls image_data).each do |symbol|
|
265
|
+
upload_contract_images_with(symbol, contract) if !!contract[symbol]
|
266
|
+
end
|
267
|
+
|
268
|
+
JSON[Api::post_contract(contract, self)]
|
269
|
+
end
|
270
|
+
|
271
|
+
def upload_contract_images_with(symbol, contract = {})
|
272
|
+
contract[:images] = [contract.delete(symbol)].flatten.map do |image|
|
273
|
+
response = if image.size < 2000 && image =~ URI::ABS_URI
|
274
|
+
upload_image(image: open(image, 'rb'))
|
275
|
+
else
|
276
|
+
upload_image(image: image)
|
277
|
+
end
|
278
|
+
|
279
|
+
response['image_hashes'] if response['success']
|
280
|
+
end.flatten
|
281
|
+
end
|
282
|
+
|
283
|
+
# Undocumented.
|
284
|
+
#
|
285
|
+
# @return [Hash]
|
286
|
+
def delete_contract(contract = {}); JSON[Api::delete_contract(contract, self)]; end
|
287
|
+
|
288
|
+
# Sets your node as a Moderator, which is discoverable on the network.
|
289
|
+
#
|
290
|
+
# @return [Hash] containing: "success" => true or false
|
291
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-make_moderator
|
292
|
+
def make_moderator; JSON[Api::post_make_moderator(self)]; end
|
293
|
+
|
294
|
+
# Removes the node as a Moderator and is no longer discoverable on the
|
295
|
+
# network as a Moderator.
|
296
|
+
#
|
297
|
+
# @return [Hash] containing: "success" => true or false
|
298
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post--unmake_moderator
|
299
|
+
def unmake_moderator; JSON[Api::post_unmake_moderator(self)]; end
|
300
|
+
|
301
|
+
# Purchases a contract by sending the purchase into the Vendor. The Buyer
|
302
|
+
# waits for a response to indicate whether the purchase is successful or
|
303
|
+
# not. If successful, the Buyer needs to fund the direct or multisig
|
304
|
+
# address.
|
305
|
+
#
|
306
|
+
# @param purchase_contract [Hash]
|
307
|
+
# @return [Hash] containing:
|
308
|
+
# * "success" => true or false
|
309
|
+
# * "address" => "bitcoin address to fund"
|
310
|
+
# * "amount" => "amount to fund"
|
311
|
+
# * "order_id" => "purchase order id"
|
312
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-purchase_contract
|
313
|
+
def purchase_contract(purchase_contract = {}); JSON[Api::post_purchase_contract(purchase_contract, self)]; end
|
314
|
+
|
315
|
+
# Sends the order confirmation and shipping information to the Buyer. If
|
316
|
+
# he’s offline, it will embed this data into the dht. The API call also
|
317
|
+
# updates the status of the order in the database.
|
318
|
+
#
|
319
|
+
# @param confirm_order [Hash]
|
320
|
+
# @return [Hash] containing: "success" => true or false
|
321
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-confirm_order
|
322
|
+
def confirm_order(confirm_order = {}); JSON[Api::post_confirm_order(confirm_order, self)]; end
|
323
|
+
|
324
|
+
# Saves the image in the file system and a pointer to it in the db.
|
325
|
+
#
|
326
|
+
# @param image [Hash] containing:
|
327
|
+
# * image: a list of product images to upload (LIST of images in base64. data only, no base64 prefix)
|
328
|
+
# * avatar: use this if uploading an avatar image (base64 image)
|
329
|
+
# * header: use this if uploading a header image (base64 image)
|
330
|
+
# @return [Hash] containing:
|
331
|
+
# * "success" => true or false
|
332
|
+
# * "image_hashes" => [list_of_image_hashes]
|
333
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-upload_image
|
334
|
+
def upload_image(image = {}); JSON[Api::post_upload_image(image.merge(cookies: cookies, base_url: base_url, verify_ssl: verify_ssl))]; end
|
335
|
+
|
336
|
+
# Sends a message with a partially signed transaction releasing funds from
|
337
|
+
# escrow to the Vendor as well as review data.
|
338
|
+
#
|
339
|
+
# @param complete_order [Hash]
|
340
|
+
# @return [Hash] containing: "success" => true or false
|
341
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-complete_order
|
342
|
+
def complete_order(complete_order = {}); JSON[Api::post_complete_order(complete_order, self)]; end
|
343
|
+
|
344
|
+
# Changes the settings of the node and pushes them to the database.
|
345
|
+
#
|
346
|
+
# @param settings [Hash]
|
347
|
+
# @return [Hash] containing: "success" => true or false
|
348
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-settings
|
349
|
+
def update_settings(settings = {}); JSON[Api::post_settings(settings, self)]; end
|
350
|
+
|
351
|
+
# Returns the settings of your node.
|
352
|
+
#
|
353
|
+
# @return [Hash]
|
354
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-settings
|
355
|
+
def settings; JSON[Api::get_settings(self)]; end
|
356
|
+
|
357
|
+
# Undocumented
|
358
|
+
#
|
359
|
+
# @return [Hash]
|
360
|
+
def connected_peers; JSON[Api::get_connected_peers(self)]; end
|
361
|
+
|
362
|
+
# Retreive a history of all notifications your node has received. Notifications can be sent due to:
|
363
|
+
# * A node following you
|
364
|
+
# * Events related to a purchase or sale
|
365
|
+
#
|
366
|
+
# @param FIXME not yet supported, future: limit-[number of most recent notifications]
|
367
|
+
# * Default is unlimited
|
368
|
+
# * Counts from most recent
|
369
|
+
# @return [Hash]
|
370
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-notifications
|
371
|
+
def notifications; JSON[Api::get_notifications(self)]; end
|
372
|
+
|
373
|
+
# Retrieves all chat message received from other nodes.
|
374
|
+
#
|
375
|
+
# @param chat_messages [Hash] containing:
|
376
|
+
# * guid [String] target node, required
|
377
|
+
# * limit [Integer] max number of chat messages to return (ignored)
|
378
|
+
# * start [FIXME] the starting point in the message list
|
379
|
+
# @return [Hash]
|
380
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-get_chat_messages
|
381
|
+
def chat_messages(chat_messages = {}); JSON[Api::get_chat_messages(chat_messages, self)]; end
|
382
|
+
|
383
|
+
# Retreives a list of outstandng conversations.
|
384
|
+
#
|
385
|
+
# @return [Hash]
|
386
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-get_chat_conversations
|
387
|
+
def chat_conversations; JSON[Api::get_chat_conversations(self)]; end
|
388
|
+
|
389
|
+
# Undocumented
|
390
|
+
#
|
391
|
+
# @return [Hash]
|
392
|
+
def delete_chat_conversation(delete_chat_conversation); JSON[Api::delete_chat_conversation(delete_chat_conversation, self)]; end
|
393
|
+
|
394
|
+
# Retrieves any sales made by the node.
|
395
|
+
#
|
396
|
+
# @return [Hash]
|
397
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-get_sales
|
398
|
+
def sales; JSON[Api::get_sales(self)]; end
|
399
|
+
|
400
|
+
# Retrieves any purchases made by the node.
|
401
|
+
#
|
402
|
+
# @return [Hash]
|
403
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-get_purchases
|
404
|
+
def purchases; JSON[Api::get_purchases(self)]; end
|
405
|
+
|
406
|
+
# Undocumented
|
407
|
+
#
|
408
|
+
# @return [Hash]
|
409
|
+
def order(order); JSON[Api::get_order(order, self)]; end
|
410
|
+
|
411
|
+
# Undocumented
|
412
|
+
#
|
413
|
+
# @return [Hash]
|
414
|
+
def cases; JSON[Api::get_cases(self)]; end
|
415
|
+
|
416
|
+
# Undocumented
|
417
|
+
#
|
418
|
+
# @return [Hash]
|
419
|
+
def order_messages(order_messages); JSON[Api::get_order_messages(order_messages, self)]; end
|
420
|
+
|
421
|
+
# Undocumented
|
422
|
+
#
|
423
|
+
# @return [Hash]
|
424
|
+
def ratings(ratings); JSON[Api::get_ratings(ratings, self)]; end
|
425
|
+
|
426
|
+
# Marks a notification as read in the database.
|
427
|
+
#
|
428
|
+
# @param notification [Hash] containing id:
|
429
|
+
# * 40 character hex string
|
430
|
+
# * Every notification has an ID that must be referenced in order to mark as read
|
431
|
+
# @return [Hash] containing: "success" => true or false
|
432
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-mark_notification_as_read
|
433
|
+
def mark_notification_as_read(notification = nil); JSON[Api::post_mark_notification_as_read(notification, self)]; end
|
434
|
+
|
435
|
+
# Sends some kind of "Twitter-like" message to all nodes that are following
|
436
|
+
# you. This call can take a while to complete.
|
437
|
+
#
|
438
|
+
# @return [Hash] containing:
|
439
|
+
# * "success" => true or false
|
440
|
+
# * "peers_reached" => [number reached]
|
441
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-broadcast
|
442
|
+
def broadcast(message = {}); JSON[Api::post_broadcast(message, self)]; end
|
443
|
+
|
444
|
+
# Undocumented
|
445
|
+
#
|
446
|
+
# @return [Hash]
|
447
|
+
def btc_price; JSON[Api::get_btc_price(self)]; end
|
448
|
+
|
449
|
+
# Undocumented
|
450
|
+
#
|
451
|
+
# @return [Hash]
|
452
|
+
def routing_table; JSON[Api::get_routing_table(self)]; end
|
453
|
+
|
454
|
+
# Marks all chat messages with a specific node as read in the database.
|
455
|
+
#
|
456
|
+
# @param chat_message_as_read [Hash] containing:
|
457
|
+
# * guid [String] GUID of the party you are chatting with
|
458
|
+
# @return [Hash] containing:
|
459
|
+
# * "success" => true or false
|
460
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-mark_chat_message_as_read
|
461
|
+
def mark_chat_message_as_read(mark_chat_message_as_read = nil); JSON[Api::post_mark_chat_message_as_read(mark_chat_message_as_read, self)]; end
|
462
|
+
|
463
|
+
# Sends a Twitter-like message to all nodes that are following you.
|
464
|
+
#
|
465
|
+
# @param check_for_payment [Hash] containing:
|
466
|
+
# * order_id [Integer]
|
467
|
+
# @return [Hash] containing:
|
468
|
+
# * "success" => true or false
|
469
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-check_for_payment
|
470
|
+
def check_for_payment(check_for_payment); JSON[Api::post_check_for_payment(check_for_payment, self)]; end
|
471
|
+
|
472
|
+
# Undocumented
|
473
|
+
#
|
474
|
+
# @param dispute_contract [Hash] containing:
|
475
|
+
# * order_id [Integer]
|
476
|
+
# @return [Hash]
|
477
|
+
def dispute_contract(dispute_contract = nil); JSON[Api::post_dispute_contract(dispute_contract, self)]; end
|
478
|
+
|
479
|
+
# Undocumented
|
480
|
+
#
|
481
|
+
# @param dispute_contract [Hash] containing:
|
482
|
+
# * order_id [Integer]
|
483
|
+
# * resolution [String]
|
484
|
+
# * buyer_percentage [Float]
|
485
|
+
# * vendor_percentage [Float]
|
486
|
+
# * moderator_percentage [Float]
|
487
|
+
# * moderator_address [String]
|
488
|
+
# @return [Hash]
|
489
|
+
def close_dispute(close_dispute = nil); JSON[Api::post_close_dispute(close_dispute, self)]; end
|
490
|
+
|
491
|
+
# Undocumented
|
492
|
+
#
|
493
|
+
# @return [Hash]
|
494
|
+
def release_funds(release_funds = nil); JSON[Api::post_release_funds(release_funds, self)]; end
|
495
|
+
|
496
|
+
# Undocumented
|
497
|
+
#
|
498
|
+
# @return [Hash]
|
499
|
+
def refund(refund = nil); JSON[Api::post_refund(refund, self)]; end
|
500
|
+
|
501
|
+
# Undocumented
|
502
|
+
#
|
503
|
+
# @return [Hash]
|
504
|
+
def mark_discussion_as_read(mark_discussion_as_read = nil); JSON[Api::post_mark_discussion_as_read(mark_discussion_as_read, self)]; end
|
505
|
+
|
506
|
+
# API call to cleanly disconnect from connected nodes and shutsdown the OpenBazaar server component.
|
507
|
+
#
|
508
|
+
# @return nil
|
509
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-shutdown
|
510
|
+
def shutdown!
|
511
|
+
Api::get_shutdown(self)
|
512
|
+
end
|
513
|
+
end
|
514
|
+
end
|