coinkite 0.9 → 0.9.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f9e4b935ab46f376d664cd1d96c0b29ccf0cbe1a
4
- data.tar.gz: a0b409931a40c8522e174eab4b81032768f287ff
3
+ metadata.gz: 09719fb406dae1eb4e0e589a92db14b05f02fb2b
4
+ data.tar.gz: e0c4b67e64237221e2cc1c3ad311e90880ca5686
5
5
  SHA512:
6
- metadata.gz: 29d35f924d8d07e68585512e75ede9ff96594d940597fa68171f5154d9eeabb563e3d255330b35ef6fac75493e1e11bb064066c2763d5afefbbf689bd6374377
7
- data.tar.gz: e98d7d1636cdefdf94142bc3e3c464290c759f3b1d24102383022071a49d79f4138f63a00818117c93727452bdc558d68b57acc6e80618c0818cd7589aefe3dd
6
+ metadata.gz: 968a0e2a3c29b7c560dc44c191628bbe1896145e2027a8deb195d96c577360131d72f137463b1c935ade405edf3c1e3f651fbd251f79b353e95ce2ea9c813e37
7
+ data.tar.gz: aed894aa94f9e9c005067a7f04afbfb9684434ec73d299ce8ef9c8918e6fcc7dc2fcbf1b6fd52bbfde7b6e5dd7d243c127a632fa8a0084e490eaeaa26ef85d8e
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.2
data/README.md CHANGED
@@ -7,7 +7,7 @@ account today!
7
7
  ## Introduction
8
8
 
9
9
  Every request made to the Coinkite API requires three special header
10
- lines. This code can generate the timestamp and signature value
10
+ lines. The code in `sign.rb` can generate the timestamp and signature value
11
11
  required for 2 of those 3 headers. The remaining header is just
12
12
  the API key itself.
13
13
 
@@ -17,6 +17,9 @@ Header lines you need:
17
17
  X-CK-Timestamp: 2014-06-23T03:10:04.905376
18
18
  X-CK-Sign: 0aa7755aaa45189a98a5a8a887a564aa55aa5aa4aa7a98aa2858aaa60a5a56aa
19
19
 
20
+ Use the Gem for a more complete solution that also checks SSL certificates,
21
+ handles some errors and wraps some API calls.
22
+
20
23
  ## How to Install
21
24
 
22
25
  Replace the two values shown here.
@@ -26,7 +29,7 @@ Replace the two values shown here.
26
29
  API_SECRET = 'this-is-my-secret'
27
30
  ````
28
31
 
29
- The keys you need can be created on
32
+ The keys you need can be created at
30
33
  [Coinkite.com under Merchant / API]([https://coinkite.com/merchant/api).
31
34
 
32
35
 
@@ -39,7 +42,7 @@ safely holding all your cryptocurrencies, including Bitcoin and Litecoin.
39
42
 
40
43
  ## Liscense
41
44
 
42
- Copyright (C) 2013 Coinkite Inc. (https://coinkite.com)
45
+ Copyright (C) 2014 Coinkite Inc. (https://coinkite.com)
43
46
 
44
47
  Permission is hereby granted, free of charge, to any person obtaining a copy
45
48
  of this software and associated documentation files (the "Software"), to deal
data/coinkite.gemspec CHANGED
@@ -9,7 +9,7 @@ spec = Gem::Specification.new do |s|
9
9
  s.description = 'Coinkite is the most secure way to transact in bitcoin online. See https://coinkite.com for details.'
10
10
  s.authors = ['Ilia Lobsanov']
11
11
  s.email = ['ilia@lobsanov.com']
12
- s.homepage = 'https://docs.coinkite.com/api/index.html'
12
+ s.homepage = 'https://docs.coinkite.com/'
13
13
  s.license = 'MIT'
14
14
 
15
15
  s.add_dependency('rest-client', '~> 1.4')
@@ -17,6 +17,7 @@ spec = Gem::Specification.new do |s|
17
17
  s.add_dependency('json', '~> 1.8')
18
18
 
19
19
  s.add_development_dependency('rspec', '~>3.0')
20
+ s.add_development_dependency('webmock')
20
21
 
21
22
  s.files = `git ls-files`.split("\n")
22
23
  s.test_files = `git ls-files -- spec/*`.split("\n")
@@ -1,3 +1,3 @@
1
1
  module Coinkite
2
- VERSION = '0.9'
2
+ VERSION = '0.9.1'
3
3
  end
data/lib/coinkite.rb CHANGED
@@ -44,12 +44,12 @@ module Coinkite
44
44
 
45
45
  request_opts = {}
46
46
  request_opts.update(:verify_ssl => OpenSSL::SSL::VERIFY_PEER,
47
- :ssl_ca_file => @ssl_bundle_path,
48
- :headers => request_headers(endpoint).update(headers),
49
- :method => method, :open_timeout => 30,
50
- :payload => payload, :url => url, :timeout => 80)
47
+ :ssl_ca_file => @ssl_bundle_path,
48
+ :method => method, :open_timeout => 30,
49
+ :payload => payload, :url => url, :timeout => 80)
51
50
 
52
51
  begin
52
+ request_opts.update(:headers => request_headers(endpoint).update(headers))
53
53
  response = RestClient::Request.execute(request_opts)
54
54
  rescue SocketError => e
55
55
  handle_restclient_error(e)
@@ -63,8 +63,9 @@ module Coinkite
63
63
  end
64
64
  rescue RestClient::ExceptionWithResponse => e
65
65
  if rcode = e.http_code and rbody = e.http_body
66
+ rbody = JSON.parse(rbody)
66
67
  if rcode == 429 and rbody.has_key?("wait_time")
67
- sleep(rbody.wait_time)
68
+ sleep(rbody["wait_time"])
68
69
  retry
69
70
  else
70
71
  handle_api_error(rcode, rbody)
@@ -76,6 +77,7 @@ module Coinkite
76
77
  handle_restclient_error(e)
77
78
  end
78
79
 
80
+ #puts response.body
79
81
  JSON.parse(response.body)
80
82
  end
81
83
 
@@ -129,7 +131,7 @@ module Coinkite
129
131
  def make_signature(endpoint, force_ts=nil)
130
132
  ts = force_ts || Time.now.utc.iso8601
131
133
  data = endpoint + '|' + ts
132
- hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new('SHA256'), @api_secret, data)
134
+ hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('SHA256'), @api_secret, data)
133
135
 
134
136
  return hmac, ts
135
137
  end
@@ -145,8 +147,8 @@ module Coinkite
145
147
  }
146
148
  end
147
149
 
148
- def get(endpoint)
149
- request('GET', endpoint)
150
+ def get(endpoint, *args)
151
+ request('GET', endpoint, *args)
150
152
  end
151
153
 
152
154
  def get_accounts
@@ -160,6 +162,41 @@ module Coinkite
160
162
  def get_balance(account)
161
163
  get("/v1/account/#{account}")["account"]
162
164
  end
165
+
166
+ def get_iter(endpoint, offset: 0, limit: nil, batch_size: 25, safety_limit: 500, **options)
167
+ Enumerator.new do |yielder|
168
+ loop do
169
+ if limit and limit < batch_size
170
+ batch_size = limit
171
+ end
172
+
173
+ response = get(endpoint, { offset: offset, limit: batch_size })
174
+
175
+ here = response["paging"]["count_here"]
176
+ total = response["paging"]["total_count"]
177
+
178
+ if total > safety_limit
179
+ raise StandardError.new("Too many results (#{total}); consider another approach")
180
+ end
181
+
182
+ raise StopIteration if not here
183
+
184
+ response["results"].each { |entry| yielder.yield entry }
185
+
186
+ offset += here
187
+ if limit != nil
188
+ limit -= here
189
+ raise StopIteration if limit <= 0
190
+ end
191
+ end
192
+ end
193
+ end
194
+
195
+ def get_list(what)
196
+ # this returns a generator function
197
+ endpoint = "/v1/list/#{what}"
198
+ get_iter(endpoint)
199
+ end
163
200
  end
164
201
  end
165
202
 
@@ -1,11 +1,43 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Coinkite do
4
- let(:coinkite) { Coinkite::Client.new(ENV['CK_API_KEY'], ENV['CK_API_SECRET']) }
4
+ before(:all) do
5
+ @coinkite = Coinkite::Client.new(ENV['CK_API_KEY'], ENV['CK_API_SECRET'])
6
+ end
5
7
 
6
8
  describe "#get_accounts" do
7
9
  it "should be retrievable" do
8
- expect(coinkite.get_accounts).to be_kind_of(Array)
10
+ stub_request(:get, "https://api.coinkite.com/v1/my/accounts").to_return(body: fixture("accounts.json"))
11
+
12
+ expect(@coinkite.get_accounts).to be_kind_of(Array)
13
+ end
14
+ end
15
+
16
+ describe "#get_list('activity')" do
17
+
18
+ before(:all) do
19
+ @activities = @coinkite.get_list("activity")
20
+ end
21
+
22
+ let(:next_item) do
23
+ stub_request(:get, "https://api.coinkite.com/v1/list/activity?limit=25&offset=0").to_return(body: fixture("list_activity.json"))
24
+ @activities.next
25
+ end
26
+
27
+ it "should return an Enumerator" do
28
+ expect(@activities).to be_kind_of(Enumerator)
29
+ end
30
+
31
+ describe "#next" do
32
+ it "should yield the first item" do
33
+ expect(next_item["CK_refnum"]).to eq("8DF23CD7E5-3934A1")
34
+ end
35
+ end
36
+
37
+ describe "#next again" do
38
+ it "should yield the second item" do
39
+ expect(next_item["CK_refnum"]).to eq("635CBAE25F-34E37A")
40
+ end
9
41
  end
10
42
  end
11
43
  end
@@ -0,0 +1,26 @@
1
+ {
2
+ "paging": {
3
+ "count_here": 2,
4
+ "limit": 25,
5
+ "offset": 0,
6
+ "total_count": 2
7
+ },
8
+ "results": [
9
+ {
10
+ "CK_refnum": "F033F8C7CA-B2B217",
11
+ "CK_type": "CKAccount",
12
+ "coin_type": "BTC",
13
+ "is_closed": false,
14
+ "name": "My Bitcoins",
15
+ "subaccount": 0
16
+ },
17
+ {
18
+ "CK_refnum": "4469EFE54E-DE212F",
19
+ "CK_type": "CKAccount",
20
+ "coin_type": "LTC",
21
+ "is_closed": false,
22
+ "name": "My Litecoins",
23
+ "subaccount": 1
24
+ }
25
+ ]
26
+ }
@@ -0,0 +1,221 @@
1
+ {
2
+ "filters": {},
3
+ "paging": {
4
+ "count_here": 25,
5
+ "limit": 25,
6
+ "offset": 0,
7
+ "total_count": 50
8
+ },
9
+ "results": [
10
+ {
11
+ "CK_refnum": "8DF23CD7E5-3934A1",
12
+ "CK_type": "CKActivityLog",
13
+ "created_at": "2014-07-16T00:54:42.771753Z",
14
+ "detail_url": "https://coinkite.com/detail-view/204AFD04E2-582536",
15
+ "ip_address": "70.28.22.73",
16
+ "is_failed_login": true,
17
+ "summary": "API Security: Timestamp (2014-07-16T00:54:23Z) is same as last request (2014-07-16T00:54:23)"
18
+ },
19
+ {
20
+ "CK_refnum": "635CBAE25F-34E37A",
21
+ "CK_type": "CKActivityLog",
22
+ "created_at": "2014-07-15T07:12:52.963799Z",
23
+ "summary": "Session logout due to: idle"
24
+ },
25
+ {
26
+ "CK_refnum": "6EEE9E70EB-2182C2",
27
+ "CK_type": "CKActivityLog",
28
+ "created_at": "2014-07-15T06:42:24.693065Z",
29
+ "ip_address": "173.230.162.137",
30
+ "is_good_login": true,
31
+ "summary": "Successful login"
32
+ },
33
+ {
34
+ "CK_refnum": "379DC805EB-D6500C",
35
+ "CK_type": "CKActivityLog",
36
+ "created_at": "2014-07-15T06:41:38.094765Z",
37
+ "ip_address": "173.230.162.137",
38
+ "is_failed_login": true,
39
+ "summary": "Wrong password provided"
40
+ },
41
+ {
42
+ "CK_refnum": "C087D345E9-169E52",
43
+ "CK_type": "CKActivityLog",
44
+ "created_at": "2014-07-15T06:41:28.450025Z",
45
+ "ip_address": "173.230.162.137",
46
+ "is_failed_login": true,
47
+ "summary": "Wrong password provided"
48
+ },
49
+ {
50
+ "CK_refnum": "FC7B5563A5-46A711",
51
+ "CK_type": "CKActivityLog",
52
+ "created_at": "2014-07-15T02:47:00.456048Z",
53
+ "summary": "Session logout due to: idle"
54
+ },
55
+ {
56
+ "CK_refnum": "38409A3FD4-A77931",
57
+ "CK_type": "CKActivityLog",
58
+ "created_at": "2014-07-15T02:15:34.210910Z",
59
+ "email_msg": {
60
+ "CK_refnum": "3B4176F6BA-E366DC",
61
+ "CK_type": "CKEmailMessage",
62
+ "template": "event_notify",
63
+ "to": "foo@bar.com"
64
+ },
65
+ "summary": "Sent email: \"Coinkite notification: API Key added/changed\" to foo@bar.com"
66
+ },
67
+ {
68
+ "CK_refnum": "43C3F7C40B-B34430",
69
+ "CK_type": "CKActivityLog",
70
+ "created_at": "2014-07-15T02:15:34.011270Z",
71
+ "detail_url": "https://coinkite.com/detail-view/204AFD04E2-582536",
72
+ "ip_address": "173.230.162.137",
73
+ "summary": "Edited API Key (xxx-xxx-xxxx): perms [read] \u2192 [term, read, send2, xfer, recv, events, send]"
74
+ },
75
+ {
76
+ "CK_refnum": "13CCF205B4-457B5A",
77
+ "CK_type": "CKActivityLog",
78
+ "created_at": "2014-07-15T02:15:05.307082Z",
79
+ "email_msg": {
80
+ "CK_refnum": "4981672827-8B8376",
81
+ "CK_type": "CKEmailMessage",
82
+ "template": "event_notify",
83
+ "to": "foo@bar.com"
84
+ },
85
+ "summary": "Sent email: \"Coinkite notification: API Key added/changed\" to foo@bar.com"
86
+ },
87
+ {
88
+ "CK_refnum": "509D38A9F4-927A96",
89
+ "CK_type": "CKActivityLog",
90
+ "created_at": "2014-07-15T02:15:05.152984Z",
91
+ "detail_url": "https://coinkite.com/detail-view/204AFD04E2-582536",
92
+ "ip_address": "173.230.162.137",
93
+ "summary": "New API key created: xxx-xxx-xxxx"
94
+ },
95
+ {
96
+ "CK_refnum": "39EDD669B7-D707DB",
97
+ "CK_type": "CKActivityLog",
98
+ "created_at": "2014-07-15T02:07:19.250733Z",
99
+ "ip_address": "173.230.162.137",
100
+ "is_good_login": true,
101
+ "summary": "Successful login"
102
+ },
103
+ {
104
+ "CK_refnum": "8BE9182341-5B345F",
105
+ "CK_type": "CKActivityLog",
106
+ "created_at": "2014-07-15T02:06:50.360758Z",
107
+ "ip_address": "173.230.162.137",
108
+ "is_failed_login": true,
109
+ "summary": "Password right, but no token given"
110
+ },
111
+ {
112
+ "CK_refnum": "E060DF5539-6B10E1",
113
+ "CK_type": "CKActivityLog",
114
+ "created_at": "2014-07-15T02:06:39.615590Z",
115
+ "ip_address": "173.230.162.137",
116
+ "is_failed_login": true,
117
+ "summary": "Password right, but no token given"
118
+ },
119
+ {
120
+ "CK_refnum": "F79ABE27E1-E9E836",
121
+ "CK_type": "CKActivityLog",
122
+ "created_at": "2014-07-15T01:56:46.168697Z",
123
+ "ip_address": "173.230.162.137",
124
+ "is_failed_login": true,
125
+ "summary": "Password right, but wrong token given"
126
+ },
127
+ {
128
+ "CK_refnum": "4A815CA99A-656010",
129
+ "CK_type": "CKActivityLog",
130
+ "created_at": "2014-07-03T19:44:06.590482Z",
131
+ "summary": "Session logout due to: idle"
132
+ },
133
+ {
134
+ "CK_refnum": "BF3FFEBF97-02309D",
135
+ "CK_type": "CKActivityLog",
136
+ "created_at": "2014-07-03T19:09:20.358185Z",
137
+ "ip_address": "174.114.27.33",
138
+ "is_good_login": true,
139
+ "summary": "Successful login"
140
+ },
141
+ {
142
+ "CK_refnum": "E151FBF523-DB6774",
143
+ "CK_type": "CKActivityLog",
144
+ "created_at": "2014-07-03T18:27:11.538728Z",
145
+ "summary": "Session logout due to: idle"
146
+ },
147
+ {
148
+ "CK_refnum": "3882BAAA86-1F09C9",
149
+ "CK_type": "CKActivityLog",
150
+ "created_at": "2014-07-03T17:51:54.778717Z",
151
+ "ip_address": "174.114.27.33",
152
+ "is_good_login": true,
153
+ "summary": "Successful login"
154
+ },
155
+ {
156
+ "CK_refnum": "D011210218-184323",
157
+ "CK_type": "CKActivityLog",
158
+ "created_at": "2014-07-03T17:51:29.361788Z",
159
+ "ip_address": "174.114.27.33",
160
+ "is_failed_login": true,
161
+ "summary": "Password right, but no token given"
162
+ },
163
+ {
164
+ "CK_refnum": "890F381357-FEC713",
165
+ "CK_type": "CKActivityLog",
166
+ "created_at": "2014-06-23T13:50:43.190186Z",
167
+ "email_msg": {
168
+ "CK_refnum": "501D894646-4F0D49",
169
+ "CK_type": "CKEmailMessage",
170
+ "template": "outstanding",
171
+ "to": "foo@bar.com"
172
+ },
173
+ "summary": "Sent email: \"Reminder: Empty Account\" to foo@bar.com"
174
+ },
175
+ {
176
+ "CK_refnum": "6A2A3AB45B-8E4B39",
177
+ "CK_type": "CKActivityLog",
178
+ "created_at": "2014-05-14T13:50:29.757841Z",
179
+ "email_msg": {
180
+ "CK_refnum": "656C56B946-81069E",
181
+ "CK_type": "CKEmailMessage",
182
+ "template": "outstanding",
183
+ "to": "foo@bar.com"
184
+ },
185
+ "summary": "Sent email: \"Reminder: Empty Account\" to foo@bar.com"
186
+ },
187
+ {
188
+ "CK_refnum": "EA1FED5560-337C96",
189
+ "CK_type": "CKActivityLog",
190
+ "created_at": "2014-04-10T21:59:36.462092Z",
191
+ "summary": "Session logout due to: idle"
192
+ },
193
+ {
194
+ "CK_refnum": "644EAB22CC-8CF221",
195
+ "CK_type": "CKActivityLog",
196
+ "created_at": "2014-04-10T21:27:24.028223Z",
197
+ "ip_address": "199.33.114.66",
198
+ "summary": "Updated your country to CA: Canada"
199
+ },
200
+ {
201
+ "CK_refnum": "3FD13A7BAB-F9E413",
202
+ "CK_type": "CKActivityLog",
203
+ "created_at": "2014-04-10T21:27:24.005037Z",
204
+ "ip_address": "199.33.114.66",
205
+ "is_good_login": true,
206
+ "summary": "Successful login"
207
+ },
208
+ {
209
+ "CK_refnum": "E4D82430DE-3F4A49",
210
+ "CK_type": "CKActivityLog",
211
+ "created_at": "2014-04-09T13:51:02.205891Z",
212
+ "email_msg": {
213
+ "CK_refnum": "DBF7AE83BA-AE2865",
214
+ "CK_type": "CKEmailMessage",
215
+ "template": "outstanding",
216
+ "to": "foo@bar.com"
217
+ },
218
+ "summary": "Sent email: \"Empty Account\" to foo@bar.com"
219
+ }
220
+ ]
221
+ }
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'coinkite'
2
2
  require 'rspec'
3
+ require 'webmock/rspec'
3
4
 
4
5
  RSpec.configure do |config|
5
6
  config.expect_with :rspec do |c|
@@ -7,3 +8,10 @@ RSpec.configure do |config|
7
8
  end
8
9
  end
9
10
 
11
+ def fixture_path
12
+ File.expand_path("../fixtures", __FILE__)
13
+ end
14
+
15
+ def fixture(file)
16
+ File.new(fixture_path + '/' + file)
17
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: coinkite
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.9'
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ilia Lobsanov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-15 00:00:00.000000000 Z
11
+ date: 2014-07-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rest-client
@@ -72,6 +72,20 @@ dependencies:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
74
  version: '3.0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: webmock
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
75
89
  description: Coinkite is the most secure way to transact in bitcoin online. See https://coinkite.com
76
90
  for details.
77
91
  email:
@@ -81,6 +95,7 @@ extensions: []
81
95
  extra_rdoc_files: []
82
96
  files:
83
97
  - ".gitignore"
98
+ - ".ruby-version"
84
99
  - Gemfile
85
100
  - README.md
86
101
  - coinkite.gemspec
@@ -91,8 +106,10 @@ files:
91
106
  - lib/data/ca-certificates.crt
92
107
  - sign.rb
93
108
  - spec/coinkite_spec.rb
109
+ - spec/fixtures/accounts.json
110
+ - spec/fixtures/list_activity.json
94
111
  - spec/spec_helper.rb
95
- homepage: https://docs.coinkite.com/api/index.html
112
+ homepage: https://docs.coinkite.com/
96
113
  licenses:
97
114
  - MIT
98
115
  metadata: {}
@@ -118,4 +135,6 @@ specification_version: 4
118
135
  summary: Ruby bindings for the Coinkite API
119
136
  test_files:
120
137
  - spec/coinkite_spec.rb
138
+ - spec/fixtures/accounts.json
139
+ - spec/fixtures/list_activity.json
121
140
  - spec/spec_helper.rb