bitget.rb 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5b63d59a8aebe65c300d42fe6dec98145f4efe93d21f7054f0ec9e076031dca9
4
- data.tar.gz: 795de1366e0ddf3194c5a43b1ccac0910b7acc8ad2798982957cd350b10d8483
3
+ metadata.gz: b73e07027de34083f0b3733d967c45f9556436af137a9b22accb4c7ebccc4403
4
+ data.tar.gz: 59f22d1ae6e505a996d08e89923df0262d1dc65c9f898463d26d154370db1dce
5
5
  SHA512:
6
- metadata.gz: 4d6585e095c3a376910d5c0f282ccf13e10868fd7dcffb944bfa9ce01ac21ecd741827e0fce768ffa7d6467a5d002e7dddf14a03f5d351d33e8dc87dfdfd1992
7
- data.tar.gz: ba02b1df209e591007cc18901448c711dfe630085b01796dd67d617db3fd4331c5f233ea1761c59789683502acf0e5df2367242f0f33b29e7675239a1d93abf0
6
+ metadata.gz: 59530b4225ba03fa3c1d0070a4cb7595bc2ec3d679767f4287290beee42a42622a0859599d361ddae04630bbb0066f7dd642b83aec3a871af45fde1d7ea626c8
7
+ data.tar.gz: a6897b20c54c8d996f853d206c252d9c553fe31879d248a1dcfc6d553763b989684763f14bfbc7d87f19ae07bc63af839a57c5236b79284bc7715ee125a83191
data/README.md CHANGED
@@ -30,6 +30,65 @@ bitget_client = Bitget::Client.new(
30
30
  )
31
31
  ```
32
32
 
33
+ ### Configuration
34
+
35
+ Settings may be declared once, and every client built afterwards reads them as its defaults.
36
+
37
+ ```ruby
38
+ Bitget.configure do |config|
39
+ config.api_key = 'api_key0'
40
+ config.api_secret = 'api_secret0'
41
+ config.api_passphrase = 'api_passphrase0'
42
+ end
43
+
44
+ bitget_client = Bitget::Client.new
45
+ ```
46
+
47
+ The settings are `api_key`, `api_secret`, `api_passphrase`, `debug` and `logger`.
48
+
49
+ Any of them may still be given per client, which wins over the configured value. The
50
+ credentials are named arguments; the rest go under `options`.
51
+
52
+ ```ruby
53
+ bitget_client = Bitget::Client.new(
54
+ api_key: 'api_key1',
55
+ options: {logger: Logger.new('bitget.log', 'daily')}
56
+ )
57
+ ```
58
+
59
+ ### Logging
60
+
61
+ A client logs when it has somewhere to log to, and not at all when it does not. Hand it any
62
+ object answering to `#info` and `#error`; the standard library's `Logger` will do.
63
+
64
+ ```ruby
65
+ require 'logger'
66
+
67
+ Bitget.configure do |config|
68
+ config.logger = Logger.new($stdout)
69
+ end
70
+ ```
71
+
72
+ `Logger` rotates by itself, so a daily log file wants no more than its second argument. It
73
+ will not create the directory, so make that first.
74
+
75
+ ```ruby
76
+ require 'fileutils'
77
+ require 'logger'
78
+
79
+ log_file_path = File.expand_path(File.join(%w{~ log bitget log.txt}))
80
+ FileUtils.mkdir_p(File.dirname(log_file_path))
81
+
82
+ Bitget.configure do |config|
83
+ config.logger = Logger.new(log_file_path, 'daily')
84
+ end
85
+ ```
86
+
87
+ Before 0.6.0 the client chose the path, created the directory and rotated the file itself. It
88
+ no longer does any of that: what is logged is the client's business and where it goes is
89
+ yours, which is what lets a StringIO logger in a test, or a levelled one, or something which
90
+ is not a Logger at all, work as well as the above.
91
+
33
92
  ### Retrieve Info on All the Coins Traded
34
93
  ```ruby
35
94
  bitget_client.spot_public_coins
@@ -0,0 +1,32 @@
1
+ # Bitget/Configuration.rb
2
+ # Bitget::Configuration
3
+
4
+ module Bitget
5
+ class Configuration
6
+ attr_accessor :api_key, :api_secret, :api_passphrase, :debug, :logger
7
+
8
+ def initialize
9
+ @api_key = nil
10
+ @api_secret = nil
11
+ @api_passphrase = nil
12
+ @debug = false
13
+ @logger = nil
14
+ end
15
+ end
16
+
17
+ class << self
18
+ attr_writer :configuration
19
+
20
+ def configuration
21
+ @configuration ||= Configuration.new
22
+ end
23
+
24
+ def configure
25
+ yield(configuration)
26
+ end
27
+
28
+ def reset_configuration!
29
+ @configuration = Configuration.new
30
+ end
31
+ end
32
+ end
data/lib/Bitget/Error.rb CHANGED
@@ -1,9 +1,31 @@
1
1
  # Bitget/Error.rb
2
2
  # Bitget::Error
3
3
 
4
+ require 'json'
5
+
4
6
  module Bitget
5
7
  class Error < RuntimeError
6
- attr_reader :code, :message, :body
8
+ attr_reader\
9
+ :code,
10
+ :message,
11
+ :body
12
+
13
+ # Bitget answers most errors with a JSON body carrying its own code and
14
+ # message, which are more use than the HTTP ones. Not all of them: a 418
15
+ # arrives with an empty body and there is nothing to parse. Where the body
16
+ # says nothing the HTTP code and message answer instead, so that an error
17
+ # renders as something rather than as a pair of blanks.
18
+ def error_code
19
+ @parsed_body['code'] || @code
20
+ end
21
+
22
+ def error_message
23
+ @parsed_body['msg'] || @parsed_body['message'] || @message
24
+ end
25
+
26
+ def to_s
27
+ "Bitget::Error: #{error_code} - #{error_message}"
28
+ end
7
29
 
8
30
  private
9
31
 
@@ -11,6 +33,16 @@ module Bitget
11
33
  @code = code
12
34
  @message = message
13
35
  @body = body
36
+ @parsed_body = parsed(body)
37
+ end
38
+
39
+ # An error is raised in answer to something already having gone wrong, so it
40
+ # must construct from whatever arrived. Raising a JSON::ParserError from
41
+ # here loses the error being reported and replaces it with one about parsing.
42
+ def parsed(body)
43
+ JSON.parse(body)
44
+ rescue JSON::ParserError
45
+ {}
14
46
  end
15
47
  end
16
48
  end
@@ -8,8 +8,8 @@ require 'json'
8
8
  require 'logger'
9
9
  require 'openssl'
10
10
 
11
+ require_relative '../Configuration'
11
12
  require_relative '../Error'
12
- require_relative '../../Hash/x_www_form_urlencode'
13
13
 
14
14
  module Bitget
15
15
  module V2
@@ -18,28 +18,11 @@ module Bitget
18
18
  API_HOST = 'api.bitget.com'
19
19
 
20
20
  class << self
21
- attr_writer :log_file_path
22
21
 
23
22
  def path_prefix
24
23
  '/api/v2'
25
24
  end
26
25
 
27
- def default_log_file_path
28
- File.join(%w{~ log bitget log.txt})
29
- end
30
-
31
- def log_file_path
32
- File.expand_path(@log_file_path || default_log_file_path)
33
- end
34
-
35
- def log_file
36
- FileUtils.mkdir_p(File.dirname(log_file_path))
37
- File.open(log_file_path, File::WRONLY | File::APPEND | File::CREAT)
38
- end
39
-
40
- def logger
41
- @logger ||= Logger.new(log_file, 'daily')
42
- end
43
26
  end # class << self
44
27
 
45
28
  # Market
@@ -1824,15 +1807,22 @@ module Bitget
1824
1807
  :api_key,
1825
1808
  :api_secret,
1826
1809
  :api_passphrase,
1827
- :use_logging
1810
+ :debug,
1811
+ :logger
1828
1812
 
1829
1813
  private
1830
1814
 
1831
- def initialize(api_key:, api_secret:, api_passphrase:, use_logging: false)
1832
- @api_key = api_key
1833
- @api_secret = api_secret
1834
- @api_passphrase = api_passphrase
1835
- @use_logging = use_logging
1815
+ # Anything not given here is taken from Bitget.configuration, so that a
1816
+ # setting declared once need not be repeated at every call site. The
1817
+ # credentials fall back upon nil, having no meaningful nil value of their
1818
+ # own; the options ask whether the key was given, nil being a value a
1819
+ # caller may mean for a logger.
1820
+ def initialize(api_key: nil, api_secret: nil, api_passphrase: nil, options: {})
1821
+ @api_key = api_key || Bitget.configuration.api_key
1822
+ @api_secret = api_secret || Bitget.configuration.api_secret
1823
+ @api_passphrase = api_passphrase || Bitget.configuration.api_passphrase
1824
+ @debug = options.key?(:debug) ? options[:debug] : Bitget.configuration.debug
1825
+ @logger = options.key?(:logger) ? options[:logger] : Bitget.configuration.logger
1836
1826
  end
1837
1827
 
1838
1828
  def full_path(path)
@@ -1845,7 +1835,8 @@ module Bitget
1845
1835
  end
1846
1836
 
1847
1837
  def timestamp
1848
- @timestamp ||= (Time.now.to_f * 1000).to_i.to_s
1838
+ # @timestamp ||= (Time.now.to_f * 1000).to_i.to_s
1839
+ @timestamp ||= (Time.now.to_i * 1000).to_s
1849
1840
  end
1850
1841
 
1851
1842
  def message(verb:, path:, args:)
@@ -1884,6 +1875,10 @@ module Bitget
1884
1875
  }
1885
1876
  end
1886
1877
 
1878
+ def use_logging?
1879
+ !@logger.nil?
1880
+ end
1881
+
1887
1882
  def log_args?(args)
1888
1883
  !args.values.all?(&:nil?)
1889
1884
  end
@@ -1894,21 +1889,21 @@ module Bitget
1894
1889
  log_string << " Args: #{args}\n"
1895
1890
  end
1896
1891
  log_string << " Headers: #{headers}\n"
1897
- self.class.logger.info(log_string)
1892
+ logger.info(log_string)
1898
1893
  end
1899
1894
 
1900
1895
  def log_response(code:, message:, body:)
1901
1896
  log_string = "Code: #{code}\n"
1902
1897
  log_string << "Message: #{message}\n"
1903
1898
  log_string << "Body: #{body}\n"
1904
- self.class.logger.info(log_string)
1899
+ logger.info(log_string)
1905
1900
  end
1906
1901
 
1907
1902
  def log_error(code:, message:, body:)
1908
1903
  log_string = "Code: #{code}\n"
1909
1904
  log_string << "Message: #{message}\n"
1910
1905
  log_string << "Body: #{body}\n"
1911
- self.class.logger.error(log_string)
1906
+ logger.error(log_string)
1912
1907
  end
1913
1908
 
1914
1909
  def do_request(verb:, path:, args: {})
@@ -1916,7 +1911,7 @@ module Bitget
1916
1911
  message = message(verb: verb, path: path, args: sorted_args)
1917
1912
  signature = signature(message)
1918
1913
  headers = headers(signature)
1919
- log_request(verb: verb, request_string: request_string(path), args: sorted_args, headers: headers) if @use_logging
1914
+ log_request(verb: verb, request_string: request_string(path), args: sorted_args, headers: headers) if use_logging?
1920
1915
  @timestamp = nil
1921
1916
  HTTP.send(verb.to_s.downcase, request_string(path), sorted_args, headers)
1922
1917
  end
@@ -1932,23 +1927,11 @@ module Bitget
1932
1927
  def handle_response(response)
1933
1928
  if response.success?
1934
1929
  parsed_body = JSON.parse(response.body)
1935
- log_response(
1936
- code: response.code,
1937
- message: response.message,
1938
- body: response.body
1939
- ) if @use_logging
1930
+ log_response(code: response.code, message: response.message, body: response.body) if use_logging?
1940
1931
  parsed_body
1941
1932
  else
1942
- log_error(
1943
- code: response.code,
1944
- message: response.message,
1945
- body: response.body
1946
- ) if @use_logging
1947
- raise Bitget::Error.new(
1948
- code: response.code,
1949
- message: response.message,
1950
- body: response.body
1951
- )
1933
+ log_error(code: response.code, message: response.message, body: response.body) if use_logging?
1934
+ raise Bitget::Error.new(code: response.code, message: response.message, body: response.body)
1952
1935
  end
1953
1936
  end
1954
1937
  end
@@ -2,5 +2,5 @@
2
2
  # Bitget::VERSION
3
3
 
4
4
  module Bitget
5
- VERSION = '0.5.0'
5
+ VERSION = '0.6.0'
6
6
  end
@@ -4,3 +4,5 @@ lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
4
4
  $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
5
5
 
6
6
  require_relative './Bitget/Client'
7
+
8
+ module Bitget; end
@@ -0,0 +1,87 @@
1
+ require_relative './helper'
2
+
3
+ describe Bitget::Configuration do
4
+ describe "#initialize" do
5
+ it "sets default values" do
6
+ config = Bitget::Configuration.new
7
+ _(config.debug).must_equal(false)
8
+ _(config.api_key).must_be_nil
9
+ _(config.api_secret).must_be_nil
10
+ _(config.api_passphrase).must_be_nil
11
+ _(config.logger).must_be_nil
12
+ end
13
+
14
+ it "declares a setting for everything a client reads" do
15
+ _(Bitget::Configuration.new.public_methods(false).grep(/[^=]\z/).sort) \
16
+ .must_equal(%i{api_key api_passphrase api_secret debug logger})
17
+ end
18
+ end
19
+
20
+ # The credentials are named arguments and fall back upon nil, having no
21
+ # meaningful nil of their own. The options are given under options: and fall
22
+ # back upon the key being absent, nil being a value a caller may mean.
23
+ describe "the credentials, which are named arguments" do
24
+ before do
25
+ Bitget.configure do |config|
26
+ config.api_key = 'configured_key'
27
+ config.api_secret = 'configured_secret'
28
+ config.api_passphrase = 'configured_passphrase'
29
+ end
30
+ end
31
+
32
+ it "supplies every one, so that a client needs no arguments at all" do
33
+ client = Bitget::Client.new
34
+ _(client.api_key).must_equal('configured_key')
35
+ _(client.api_secret).must_equal('configured_secret')
36
+ _(client.api_passphrase).must_equal('configured_passphrase')
37
+ end
38
+
39
+ it "yields to each one given, and only to that one" do
40
+ _(Bitget::Client.new(api_key: 'given').api_key).must_equal('given')
41
+ _(Bitget::Client.new(api_key: 'given').api_secret).must_equal('configured_secret')
42
+ _(Bitget::Client.new(api_secret: 'given').api_secret).must_equal('given')
43
+ _(Bitget::Client.new(api_secret: 'given').api_key).must_equal('configured_key')
44
+ _(Bitget::Client.new(api_passphrase: 'given').api_passphrase).must_equal('given')
45
+ _(Bitget::Client.new(api_passphrase: 'given').api_key).must_equal('configured_key')
46
+ end
47
+
48
+ it "is unreachable where nothing is configured and nothing is given" do
49
+ Bitget.reset_configuration!
50
+ client = Bitget::Client.new
51
+ _(client.api_key).must_be_nil
52
+ _(client.api_secret).must_be_nil
53
+ _(client.api_passphrase).must_be_nil
54
+ end
55
+ end
56
+
57
+ describe "the options, which are given under options:" do
58
+ let(:logger){Logger.new(StringIO.new)}
59
+
60
+ before do
61
+ Bitget.configure do |config|
62
+ config.debug = true
63
+ config.logger = logger
64
+ end
65
+ end
66
+
67
+ it "supplies every one" do
68
+ client = Bitget::Client.new
69
+ _(client.debug).must_equal(true)
70
+ _(client.logger).must_equal(logger)
71
+ end
72
+
73
+ it "yields to each one given, and only to that one" do
74
+ other = Logger.new(StringIO.new)
75
+ _(Bitget::Client.new(options: {debug: false}).debug).must_equal(false)
76
+ _(Bitget::Client.new(options: {debug: false}).logger).must_equal(logger)
77
+ _(Bitget::Client.new(options: {logger: other}).logger).must_equal(other)
78
+ _(Bitget::Client.new(options: {logger: other}).debug).must_equal(true)
79
+ end
80
+
81
+ it "takes nil as a value rather than as an absence" do
82
+ client = Bitget::Client.new(options: {logger: nil})
83
+ _(client.logger).must_be_nil
84
+ _(client.send(:use_logging?)).must_equal(false)
85
+ end
86
+ end
87
+ end
@@ -1,5 +1,4 @@
1
1
  require_relative '../helper'
2
- require_relative '../../lib/Bitget/V2/Client'
3
2
 
4
3
  describe Bitget::V2::Client do
5
4
  let(:api_key){ENV.fetch('BITGET_API_KEY', '<API_KEY>')}
@@ -14,6 +13,19 @@ describe Bitget::V2::Client do
14
13
  )
15
14
  end
16
15
 
16
+ describe "where a request is addressed" do
17
+ # Written out rather than built from API_HOST and .path_prefix, which would
18
+ # pass however wrong either of them was.
19
+ it "is the scheme, the host, the version prefix, and the path" do
20
+ _(client.send(:request_string, '/spot/public/coins')) \
21
+ .must_equal('https://api.bitget.com/api/v2/spot/public/coins')
22
+ end
23
+
24
+ it "takes its version prefix from the client, which is what makes V2 a class of its own" do
25
+ _(Bitget::V2::Client.path_prefix).must_equal('/api/v2')
26
+ end
27
+ end
28
+
17
29
  # Market
18
30
 
19
31
  describe "#spot_public_coins" do
@@ -998,12 +1010,12 @@ describe Bitget::V2::Client do
998
1010
  let(:mock_error){Minitest::Mock.new}
999
1011
 
1000
1012
  before do
1001
- client.use_logging = true
1013
+ client.logger = Logger.new(StringIO.new)
1002
1014
  mock_error.expect(:call, nil, [], code: '418', message: "I'm a teapot", body: '')
1003
1015
  end
1004
1016
 
1005
1017
  after do
1006
- client.use_logging = false
1018
+ client.logger = nil
1007
1019
  mock_error.verify
1008
1020
  end
1009
1021
 
@@ -1070,79 +1082,49 @@ describe Bitget::V2::Client do
1070
1082
  end
1071
1083
 
1072
1084
  describe "logging" do
1085
+ let(:log){StringIO.new}
1086
+
1073
1087
  before do
1074
- client.use_logging = true
1075
- FileUtils.rm_f(client.class.log_file_path)
1076
- client.class.instance_variable_set(:@log_file_path, nil)
1077
- client.class.instance_variable_set(:@logger, nil)
1088
+ client.logger = Logger.new(log)
1078
1089
  end
1079
1090
 
1080
1091
  after do
1081
- client.use_logging = false
1082
- FileUtils.rm_f(client.class.log_file_path)
1083
- end
1084
-
1085
- describe "logging configuration" do
1086
- it "uses the configured log file path" do
1087
- client.class.log_file_path = '/tmp/path'
1088
- _(client.class.log_file_path).must_equal(File.expand_path('/tmp/path'))
1089
- end
1090
-
1091
- it "creates log directory if it doesn't exist" do
1092
- nested_path = File.join(Dir.tmpdir, 'bitget_test', 'nested', 'test.log')
1093
- client.class.log_file_path = nested_path
1094
- client.class.logger
1095
- _(File.directory?(File.dirname(nested_path))).must_equal(true)
1096
- end
1097
-
1098
- it "creates a daily rotating logger" do
1099
- _(client.class.logger).must_be_kind_of(Logger)
1100
- _(File.exist?(client.class.log_file_path)).must_equal(true)
1101
- end
1092
+ client.logger = nil
1102
1093
  end
1103
1094
 
1104
1095
  describe "request logging" do
1105
1096
  it "logs requests" do
1106
- client.class.logger
1107
- VCR.use_cassette('v2/spot/public/coins-when_coin_is_supplied') do
1108
- client.spot_public_coins(coin: 'BTC')
1097
+ VCR.use_cassette("v2/spot/public/coins-when_coin_is_supplied") do
1098
+ client.spot_public_coins(coin: "BTC")
1109
1099
  end
1110
- client.class.logger.close
1111
- log_content = File.read(client.class.log_file_path)
1112
- _(log_content).must_match(/GET https:\/\/api.bitget.com\/api\/v2\/spot\/public\/coins/)
1113
- _(log_content).must_match(/Args: \{coin: \"BTC\"}/)
1114
- _(log_content).must_match(/Headers: .+\"Content-Type\" => \"application\/json\"/)
1100
+ _(log.string).must_match(/GET https:\/\/api.bitget.com\/api\/v2\/spot\/public\/coins/)
1101
+ _(log.string).must_match(/Args: \{coin: "BTC"}/)
1102
+ _(log.string).must_match(/Headers: .+"Content-Type" => "application\/json"/)
1115
1103
  end
1116
1104
  end
1117
1105
 
1118
1106
  describe "response logging" do
1119
1107
  it "logs responses" do
1120
- client.class.logger
1121
- VCR.use_cassette('v2/spot/public/coins-when_coin_is_supplied') do
1122
- client.spot_public_coins(coin: 'BTC')
1108
+ VCR.use_cassette("v2/spot/public/coins-when_coin_is_supplied") do
1109
+ client.spot_public_coins(coin: "BTC")
1123
1110
  end
1124
- client.class.logger.close
1125
- log_content = File.read(client.class.log_file_path)
1126
- _(log_content).must_match(/Code:/)
1127
- _(log_content).must_match(/Message:/)
1128
- _(log_content).must_match(/Body:/)
1111
+ _(log.string).must_match(/Code:/)
1112
+ _(log.string).must_match(/Message:/)
1113
+ _(log.string).must_match(/Body:/)
1129
1114
  end
1130
1115
  end
1131
1116
 
1132
1117
  describe "error logging" do
1133
1118
  it "logs error responses" do
1134
- client.class.logger
1135
- VCR.use_cassette('v2/spot/public/coins-when_error_occurs') do
1119
+ VCR.use_cassette("v2/spot/public/coins-when_error_occurs") do
1136
1120
  begin
1137
- client.spot_public_coins(coin: 'INVALID')
1121
+ client.spot_public_coins(coin: "INVALID")
1138
1122
  rescue Bitget::Error
1139
1123
  end
1140
1124
  end
1141
- client.class.logger.close
1142
- log_content = File.read(client.class.log_file_path)
1143
- _(log_content).must_match(/Code:/)
1144
- _(log_content).must_match(/Message:/)
1145
- _(log_content).must_match(/Body:/)
1125
+ _(log.string).must_match(/Code:/)
1126
+ _(log.string).must_match(/Message:/)
1127
+ _(log.string).must_match(/Body:/)
1146
1128
  end
1147
1129
  end
1148
1130
  end
data/test/helper.rb CHANGED
@@ -1,13 +1,27 @@
1
1
  require 'minitest/autorun'
2
- require 'minitest-spec-context'
2
+ require 'minitest/mock'
3
3
  require 'minitest/spec'
4
+ require 'minitest-spec-context'
4
5
  require 'vcr'
6
+ require 'webmock' # Necessary? Was working without.
7
+
8
+ $LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
9
+ require 'bitget'
5
10
 
6
11
  VCR.configure do |config|
7
- config.cassette_library_dir = File.expand_path('../fixtures/vcr_cassettes', __FILE__)
12
+ # config.cassette_library_dir = File.expand_path('../fixtures/vcr_cassettes', __FILE__)
13
+ config.cassette_library_dir = 'test/fixtures/vcr_cassettes'
14
+
8
15
  config.hook_into :webmock
9
16
 
10
17
  config.filter_sensitive_data('<API_KEY>'){ENV['BITGET_API_KEY']}
11
18
  config.filter_sensitive_data('<API_SECRET>'){ENV['BITGET_API_SECRET'] }
12
19
  config.filter_sensitive_data('<API_PASSPHRASE>'){ENV['BITGET_API_PASSPHRASE']}
13
20
  end
21
+
22
+ class Minitest::Test
23
+ def before_setup
24
+ super
25
+ Bitget.reset_configuration!
26
+ end
27
+ end
data/test/test_all.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # test/test_all.rb
2
2
 
3
- require_relative './client_test'
4
- require_relative './V2/client_test'
3
+ require_relative './V2/Client_test'
4
+ require_relative './Configuration_test'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bitget.rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - thoran
@@ -32,18 +32,16 @@ files:
32
32
  - Gemfile
33
33
  - README.md
34
34
  - bitget.rb.gemspec
35
+ - lib/Bitget.rb
35
36
  - lib/Bitget/Client.rb
37
+ - lib/Bitget/Configuration.rb
36
38
  - lib/Bitget/Error.rb
37
39
  - lib/Bitget/V2/Client.rb
38
40
  - lib/Bitget/VERSiON.rb
39
- - lib/Hash/x_www_form_urlencode.rb
40
41
  - lib/String/url_encode.rb
41
- - lib/Thoran/Hash/XWwwFormUrlencode/x_www_form_urlencode.rb
42
42
  - lib/Thoran/String/UrlEncode/url_encode.rb
43
- - lib/bitget.rb
44
- - test/V2/client_logging_test.rb
43
+ - test/Configuration_test.rb
45
44
  - test/V2/client_test.rb
46
- - test/client_test.rb
47
45
  - test/helper.rb
48
46
  - test/test_all.rb
49
47
  homepage: http://github.com/thoran/bitget.rb
@@ -64,7 +62,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
64
62
  - !ruby/object:Gem::Version
65
63
  version: '0'
66
64
  requirements: []
67
- rubygems_version: 3.6.9
65
+ rubygems_version: 4.0.18
68
66
  specification_version: 4
69
67
  summary: Access the Bitget API with Ruby.
70
68
  test_files: []
@@ -1,7 +0,0 @@
1
- # Hash/x_www_form_urlencode.rb
2
- # Hash#x_www_form_urlencode
3
-
4
- # 20241009
5
- # 0.1.0
6
-
7
- require 'Thoran/Hash/XWwwFormUrlencode/x_www_form_urlencode'
@@ -1,26 +0,0 @@
1
- # Thoran/Hash/XWwwFormUrlEncode/x_www_form_urlencode.rb
2
- # Thoran::Hash::XWwwFormUrlEncode#x_www_form_urlencode
3
-
4
- # 20241009
5
- # 0.2.0
6
-
7
- # Changes since 0.1:
8
- # -/0: (The class name and the snake case name are consistent now.)
9
- # 1. /XWWWFormUrlEncode/XWwwFormUrlEncode/
10
-
11
- require 'Thoran/String/UrlEncode/url_encode'
12
-
13
- module Thoran
14
- module Hash
15
- module XWwwFormUrlEncode
16
-
17
- def x_www_form_url_encode(joiner = '&')
18
- inject([]){|a,e| a << "#{e.first.to_s.url_encode}=#{e.last.to_s.url_encode}" unless e.last.nil?; a}.join(joiner)
19
- end
20
- alias_method :x_www_form_urlencode, :x_www_form_url_encode
21
-
22
- end
23
- end
24
- end
25
-
26
- Hash.send(:include, Thoran::Hash::XWwwFormUrlEncode)
@@ -1,92 +0,0 @@
1
- require_relative '../helper'
2
- require_relative '../../lib/Bitget/V2/Client'
3
-
4
- describe Bitget::V2::Client do
5
- let(:api_key){ENV.fetch('BITGET_API_KEY', '<API_KEY>')}
6
- let(:api_secret){ENV.fetch('BITGET_API_SECRET', '<API_SECRET>')}
7
- let(:api_passphrase){ENV.fetch('BITGET_API_PASSPHRASE', '<API_PASSPHRASE>')}
8
-
9
- let(:client) do
10
- Bitget::V2::Client.new(
11
- api_key: api_key,
12
- api_secret: api_secret,
13
- api_passphrase: api_passphrase
14
- )
15
- end
16
-
17
- describe "logging" do
18
- before do
19
- FileUtils.rm_f(client.class.log_file_path)
20
- client.class.instance_variable_set(:@log_file_path, nil)
21
- client.class.instance_variable_set(:@logger, nil)
22
- end
23
-
24
- after do
25
- FileUtils.rm_f(client.class.log_file_path)
26
- end
27
-
28
- describe "logging configuration" do
29
- it "uses the configured log file path" do
30
- client.class.log_file_path = '/some/path'
31
- _(client.class.log_file_path).must_equal(File.expand_path('/some/path'))
32
- end
33
-
34
- it "creates log directory if it doesn't exist" do
35
- nested_path = File.join(Dir.tmpdir, 'bitget_test', 'nested', 'test.log')
36
- client.class.log_file_path = nested_path
37
- client.class.logger
38
- _(File.directory?(File.dirname(nested_path))).must_equal(true)
39
- end
40
-
41
- it "creates a daily rotating logger" do
42
- _(client.class.logger).must_be_kind_of(Logger)
43
- _(File.exist?(client.class.log_file_path)).must_equal(true)
44
- end
45
- end
46
-
47
- describe "request logging" do
48
- it "logs requests" do
49
- client.class.logger
50
- VCR.use_cassette('v2/spot/public/coins-when_coin_is_supplied') do
51
- client.spot_public_coins(coin: 'BTC')
52
- end
53
- client.class.logger.close
54
- log_content = File.read(client.class.log_file_path)
55
- _(log_content).must_match(/GET https:\/\/api.bitget.com\/api\/v2\/spot\/public\/coins/)
56
- _(log_content).must_match(/Args: \{coin: \"BTC\"}/)
57
- _(log_content).must_match(/Headers: .+\"Content-Type\" => \"application\/json\"/)
58
- end
59
- end
60
-
61
- describe "response logging" do
62
- it "logs responses" do
63
- client.class.logger
64
- VCR.use_cassette('v2/spot/public/coins-when_coin_is_supplied') do
65
- client.spot_public_coins(coin: 'BTC')
66
- end
67
- client.class.logger.close
68
- log_content = File.read(client.class.log_file_path)
69
- _(log_content).must_match(/Code:/)
70
- _(log_content).must_match(/Message:/)
71
- _(log_content).must_match(/Body:/)
72
- end
73
- end
74
-
75
- describe "error logging" do
76
- it "logs error responses" do
77
- client.class.logger
78
- VCR.use_cassette('v2/spot/public/coins-when_error_occurs') do
79
- begin
80
- client.spot_public_coins(coin: 'INVALID')
81
- rescue Bitget::Error
82
- end
83
- end
84
- client.class.logger.close
85
- log_content = File.read(client.class.log_file_path)
86
- _(log_content).must_match(/Code:/)
87
- _(log_content).must_match(/Message:/)
88
- _(log_content).must_match(/Body:/)
89
- end
90
- end
91
- end
92
- end
data/test/client_test.rb DELETED
@@ -1,198 +0,0 @@
1
- require_relative './helper'
2
- require_relative '../lib/Bitget/Client'
3
-
4
- describe Bitget::Client do
5
- let(:client) do
6
- Bitget::Client.new(
7
- api_key: ENV.fetch('BITGET_API_KEY', '<API_KEY>'),
8
- api_secret: ENV.fetch('BITGET_API_SECRET', '<API_SECRET>'),
9
- api_passphrase: ENV.fetch('BITGET_API_PASSPHRASE', '<API_PASSPHRASE>')
10
- )
11
- end
12
-
13
- # Market
14
-
15
- %w[
16
- spot_public_coins
17
- spot_public_symbols
18
- spot_market_vip_free_rate
19
- spot_market_tickers
20
- spot_market_merge_depth
21
- spot_market_orderbook
22
- spot_market_candles
23
- spot_market_history_candles
24
- spot_market_fills
25
- spot_market_fills_history
26
- ].each do |method|
27
- describe "##{method}" do
28
- it "delegates to V2::Client" do
29
- mock_v2_client = Minitest::Mock.new
30
- client.instance_variable_set(:@v2_client, mock_v2_client)
31
-
32
- args = case method
33
- when 'spot_market_merge_depth', 'spot_market_orderbook'
34
- {symbol: 'BTCUSDT'}
35
- when 'spot_market_candles', 'spot_market_history_candles'
36
- {symbol: 'BTCUSDT', granularity: '1min'}
37
- when 'spot_market_fills', 'spot_market_fills_history'
38
- {symbol: 'BTCUSDT'}
39
- else
40
- {}
41
- end
42
-
43
- mock_v2_client.expect(method.to_sym, nil, [args])
44
- client.send(method, **args)
45
- mock_v2_client.verify
46
- end
47
- end
48
- end
49
-
50
- # Trade
51
-
52
- %w[
53
- spot_trade_place_order
54
- spot_trade_cancel_replace_order
55
- spot_trade_batch_cancel_replace_order
56
- spot_trade_cancel_order
57
- spot_trade_batch_orders
58
- spot_trade_batch_cancel_order
59
- spot_trade_cancel_symbol_order
60
- spot_trade_order_info
61
- spot_trade_unfilled_orders
62
- spot_trade_history_orders
63
- spot_trade_fills
64
- ].each do |method|
65
- describe "##{method}" do
66
- it "delegates to V2::Client" do
67
- mock_v2_client = Minitest::Mock.new
68
- client.instance_variable_set(:@v2_client, mock_v2_client)
69
-
70
- args = case method
71
- when 'spot_trade_place_order'
72
- {
73
- symbol: 'BTCUSDT',
74
- side: 'buy',
75
- order_type: 'limit',
76
- force: 'normal',
77
- size: '0.001',
78
- price: '30000'
79
- }
80
- when /cancel|modify|info/
81
- {symbol: 'BTCUSDT', order_id: '123456'}
82
- when /batch/
83
- {symbol: 'BTCUSDT', order_ids: ['123', '456']}
84
- else
85
- {symbol: 'BTCUSDT'}
86
- end
87
-
88
- mock_v2_client.expect(method.to_sym, nil, [args])
89
- client.send(method, **args)
90
- mock_v2_client.verify
91
- end
92
- end
93
- end
94
-
95
- # Trigger
96
-
97
- %w[
98
- spot_trade_place_plan_order
99
- spot_trade_modify_plan_order
100
- spot_trade_cancel_plan_order
101
- spot_trade_current_plan_order
102
- spot_trade_plan_sub_order
103
- spot_trade_history_plan_order
104
- spot_trade_batch_cancel_plan_order
105
- ].each do |method|
106
- describe "##{method}" do
107
- it "delegates to V2::Client" do
108
- mock_v2_client = Minitest::Mock.new
109
- client.instance_variable_set(:@v2_client, mock_v2_client)
110
-
111
- args = case method
112
- when 'spot_trade_place_plan_order'
113
- {
114
- symbol: 'BTCUSDT',
115
- side: 'buy',
116
- order_type: 'limit',
117
- size: '0.001',
118
- trigger_price: '31000',
119
- execute_price: '30000'
120
- }
121
- when 'spot_trade_modify_plan_order'
122
- {
123
- order_id: '123456',
124
- trigger_price: '31000',
125
- execute_price: '30000',
126
- size: '0.001'
127
- }
128
- when 'spot_trade_cancel_plan_order'
129
- {order_id: '123456'}
130
- when 'spot_trade_plan_sub_order'
131
- {order_id: '123456'}
132
- when 'spot_trade_batch_cancel_plan_order'
133
- {symbol: 'BTCUSDT', order_ids: ['123', '456']}
134
- else
135
- {symbol: 'BTCUSDT'}
136
- end
137
-
138
- mock_v2_client.expect(method.to_sym, nil, [args])
139
- client.send(method, **args)
140
- mock_v2_client.verify
141
- end
142
- end
143
- end
144
-
145
- # Account
146
-
147
- %w[
148
- spot_account_info
149
- spot_account_assets
150
- spot_account_subaccount_assets
151
- spot_account_bills
152
- spot_account_sub_main_trans_record
153
- spot_account_transfer_records
154
- spot_account_switch_deduct
155
- spot_account_deduct_info
156
- spot_wallet_modify_deposit_account
157
- spot_wallet_transfer
158
- spot_wallet_transfer_coin_info
159
- spot_wallet_subaccount_transfer
160
- spot_wallet_withdrawal
161
- spot_wallet_cancel_withdrawal
162
- spot_wallet_deposit_address
163
- spot_wallet_subaccount_deposit_address
164
- spot_wallet_subaccount_deposit_records
165
- spot_wallet_withdrawal_records
166
- spot_wallet_deposit_records
167
- ].each do |method|
168
- describe "##{method}" do
169
- it "delegates to V2::Client" do
170
- mock_v2_client = Minitest::Mock.new
171
- client.instance_variable_set(:@v2_client, mock_v2_client)
172
-
173
- args = case method
174
- when 'spot_wallet_withdrawal'
175
- {
176
- coin: 'USDT',
177
- chain: 'TRC20',
178
- address: '0x1234567890abcdef',
179
- amount: '100'
180
- }
181
- when /transfer/
182
- {
183
- coin: 'USDT',
184
- amount: '100'
185
- }
186
- when /subaccount/
187
- {subaccount_id: '123456'}
188
- else
189
- {}
190
- end
191
-
192
- mock_v2_client.expect(method.to_sym, nil, [args])
193
- client.send(method, **args)
194
- mock_v2_client.verify
195
- end
196
- end
197
- end
198
- end