obarc 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +19 -0
  3. data/.gitignore +5 -0
  4. data/.rubocop.yml +1156 -0
  5. data/.travis.yml +12 -0
  6. data/Gemfile +3 -0
  7. data/Gemfile.lock +78 -0
  8. data/README.md +133 -0
  9. data/Rakefile +39 -0
  10. data/doc/OBarc.html +210 -0
  11. data/doc/OBarc/Api.html +883 -0
  12. data/doc/OBarc/Session.html +5399 -0
  13. data/doc/OBarc/Utils.html +117 -0
  14. data/doc/OBarc/Utils/Exceptions.html +115 -0
  15. data/doc/OBarc/Utils/Exceptions/InvalidArgumentError.html +230 -0
  16. data/doc/OBarc/Utils/Exceptions/InvalidElementError.html +218 -0
  17. data/doc/OBarc/Utils/Exceptions/InvalidWordError.html +218 -0
  18. data/doc/OBarc/Utils/Exceptions/MissingArgumentError.html +230 -0
  19. data/doc/OBarc/Utils/Exceptions/OBarcArgumentError.html +151 -0
  20. data/doc/OBarc/Utils/Exceptions/OBarcError.html +291 -0
  21. data/doc/OBarc/Utils/Exceptions/OBarcResponseError.html +520 -0
  22. data/doc/OBarc/Utils/Exceptions/OverLimitError.html +218 -0
  23. data/doc/OBarc/Utils/Exceptions/TimeOutError.html +218 -0
  24. data/doc/OBarc/Utils/Helper.html +115 -0
  25. data/doc/OBarc/Utils/Helper/ObjectExtensions.html +105 -0
  26. data/doc/_index.html +239 -0
  27. data/doc/class_list.html +58 -0
  28. data/doc/css/common.css +1 -0
  29. data/doc/css/full_list.css +57 -0
  30. data/doc/css/style.css +339 -0
  31. data/doc/file.README.html +229 -0
  32. data/doc/file_list.html +60 -0
  33. data/doc/frames.html +26 -0
  34. data/doc/index.html +229 -0
  35. data/doc/js/app.js +219 -0
  36. data/doc/js/full_list.js +181 -0
  37. data/doc/js/jquery.js +4 -0
  38. data/doc/method_list.html +465 -0
  39. data/doc/top-level-namespace.html +112 -0
  40. data/graphics/obarc-logo.png +0 -0
  41. data/graphics/obarc-logo.xcf +0 -0
  42. data/lib/obarc.rb +14 -0
  43. data/lib/obarc/api.rb +220 -0
  44. data/lib/obarc/session.rb +514 -0
  45. data/lib/obarc/utils/exceptions.rb +35 -0
  46. data/lib/obarc/utils/helper.rb +18 -0
  47. data/lib/obarc/version.rb +3 -0
  48. data/obarc.gemspec +32 -0
  49. 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
+ &mdash; 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> &raquo;
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
@@ -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
@@ -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