monzo-cli 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,50 @@
1
+ require 'multi_json'
2
+
3
+ module Mondo
4
+ class Error < StandardError
5
+ end
6
+
7
+ class ApiError < Error
8
+ attr_reader :response, :code, :description
9
+
10
+ def initialize(response)
11
+ @response = response
12
+ @code = response.status
13
+
14
+ puts response.inspect
15
+
16
+ begin
17
+ parsed_response = MultiJson.decode(response.body)
18
+ errors = parsed_response["message"]
19
+ @description = stringify_errors(errors)
20
+ rescue MultiJson::ParseError
21
+ @description = response.body ? response.body.strip : "Unknown error"
22
+ end
23
+ end
24
+
25
+ def to_s
26
+ "#{super}. #{self.description}"
27
+ end
28
+
29
+ private
30
+
31
+ def stringify_errors(errors)
32
+ case errors
33
+ when Array
34
+ errors.join(", ")
35
+ when Hash
36
+ errors.flat_map do |field, messages|
37
+ messages.map { |message| "#{field} #{message}" }
38
+ end.join(", ")
39
+ else
40
+ errors.to_s
41
+ end
42
+ end
43
+ end
44
+
45
+ class ClientError < Error
46
+ end
47
+
48
+ class SignatureError < Error
49
+ end
50
+ end
@@ -0,0 +1,31 @@
1
+ module Mondo
2
+ class FeedItem < Resource
3
+
4
+ attr_accessor :title,
5
+ :image_url,
6
+ :url, # TODO - make me consistent when the Mondo API changes
7
+ :background_color,
8
+ :body,
9
+ :type
10
+
11
+
12
+ # temporary fix until the API accepts JSON data in the request body
13
+ def save
14
+ self.client.api_post("/feed", self.create_params)
15
+ end
16
+
17
+ def create_params
18
+ {
19
+ account_id: self.client.account_id,
20
+ type: "basic",
21
+ url: self.url,
22
+ params: {
23
+ title: self.title,
24
+ image_url: self.image_url,
25
+ background_color: self.background_color,
26
+ body: self.body
27
+ }
28
+ }
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,15 @@
1
+ module Mondo
2
+ class Merchant < Resource
3
+
4
+ attr_accessor :id, :group_id, :logo, :name, :raw_data, :address, :emoji
5
+
6
+ boolean_accessor :online, :is_load, :settled
7
+
8
+ date_accessor :created
9
+
10
+ def address
11
+ ::Mondo::Address.new(raw_data['address'], self.client)
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,52 @@
1
+ module Mondo
2
+ class Resource
3
+
4
+ attr_accessor :client, :raw_data
5
+
6
+ def initialize(hash={}, client)
7
+ self.raw_data = hash
8
+ self.client = client
9
+ hash.each { |key,val| send("#{key}=", val) if respond_to?("#{key}=") }
10
+ self.to_s
11
+ end
12
+
13
+ def to_s
14
+ "#<#{self.class} #{raw_data}>"
15
+ end
16
+
17
+ def inspect
18
+ self.to_s
19
+ end
20
+
21
+ class << self
22
+ def date_writer(*args)
23
+ args.each do |attr|
24
+ define_method("#{attr.to_s}=".to_sym) do |date|
25
+ date = (date.is_a?(String) ? DateTime.parse(date) : date) rescue date
26
+ instance_variable_set("@#{attr}", date)
27
+ end
28
+ end
29
+ end
30
+
31
+ def date_accessor(*args)
32
+ attr_reader(*args)
33
+ date_writer(*args)
34
+ end
35
+
36
+ def boolean_accessor(*attrs)
37
+ attr_accessor(*attrs)
38
+ alias_question(attrs)
39
+ end
40
+
41
+ def boolean_reader(*attrs)
42
+ attr_reader(*attrs)
43
+ alias_question(attrs)
44
+ end
45
+
46
+ private
47
+ def alias_question(attrs)
48
+ attrs.each{ |attr| define_method("#{attr}?"){ send(attr) || false } }
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,29 @@
1
+ module Mondo
2
+ class Response
3
+ attr_reader :response, :parsed
4
+ attr_accessor :error, :options
5
+
6
+ # Initializes a Response instance
7
+ #
8
+ # @param [Faraday::Response] response The Faraday response instance
9
+ def initialize(resp)
10
+ @response = resp
11
+ @parsed = -> { MultiJson.load(body) rescue body }.call
12
+ end
13
+
14
+ # The HTTP response headers
15
+ def headers
16
+ response.headers
17
+ end
18
+
19
+ # The HTTP response status code
20
+ def status
21
+ response.status
22
+ end
23
+
24
+ # The HTTP response body
25
+ def body
26
+ response.body || ''
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,80 @@
1
+ module Mondo
2
+ class Transaction < Resource
3
+
4
+ attr_accessor :id,
5
+ :description,
6
+ :notes,
7
+ :metadata,
8
+ :is_load,
9
+ :category,
10
+ :settled,
11
+ :decline_reason
12
+
13
+ date_accessor :created
14
+ date_accessor :settled
15
+
16
+ def declined?
17
+ raw_data['decline_reason'].present?
18
+ end
19
+
20
+ def amount
21
+ Money.new(raw_data['amount'], currency)
22
+ end
23
+
24
+ def local_amount
25
+ Money.new(raw_data['local_amount'], local_currency)
26
+ end
27
+
28
+ def account_balance
29
+ Money.new(raw_data['account_balance'], currency)
30
+ end
31
+
32
+ def currency
33
+ Money::Currency.new(raw_data['currency'])
34
+ end
35
+
36
+ def local_currency
37
+ Money::Currency.new(raw_data['local_currency'])
38
+ end
39
+
40
+ def save_metadata
41
+ self.client.api_patch("/transactions/#{self.id}", metadata: self.metadata)
42
+ end
43
+
44
+ def register_attachment(args={})
45
+ attachment = Attachment.new(
46
+ {
47
+ external_id: self.id,
48
+ file_url: args.fetch(:file_url),
49
+ file_type: args.fetch(:file_type)
50
+ },
51
+ self.client
52
+ )
53
+
54
+ self.attachments << attachment if attachment.register
55
+ end
56
+
57
+ def attachments
58
+ @transactions ||= begin
59
+ raw_data['attachments'].map { |tx| Attachment.new(tx, self.client) }
60
+ end
61
+ end
62
+
63
+ def merchant(opts={})
64
+ unless raw_data['merchant'].kind_of?(Hash)
65
+ # Go and refetch the transaction with merchant info expanded
66
+ self.raw_data['merchant'] = self.client.transaction(self.id, expand: [:merchant]).raw_data['merchant']
67
+ end
68
+
69
+ ::Mondo::Merchant.new(raw_data['merchant'], client) unless raw_data['merchant'].nil?
70
+ end
71
+
72
+ def tags
73
+ metadata["tags"]
74
+ end
75
+
76
+ def tags=(t)
77
+ metadata["tags"] = t
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,16 @@
1
+ require 'uri'
2
+
3
+ module Mondo
4
+ module Utils
5
+ extend self
6
+
7
+ # String Helpers
8
+ def camelize(str)
9
+ str.split('_').map(&:capitalize).join
10
+ end
11
+
12
+ def underscore(str)
13
+ str.gsub(/(.)([A-Z])/) { "#{$1}_#{$2.downcase}" }.downcase
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module Mondo
2
+ VERSION = '0.5.0'.freeze
3
+ end
@@ -0,0 +1,15 @@
1
+ module Mondo
2
+ class WebHook < Resource
3
+
4
+ attr_accessor :id, :account_id, :url
5
+
6
+ def save
7
+ self.client.api_post("webhooks", account_id: account_id, url: url)
8
+ end
9
+
10
+ def delete
11
+ self.client.api_delete("webhooks/#{self.id}")
12
+ true
13
+ end
14
+ end
15
+ end
data/lib/api/mondo.rb ADDED
@@ -0,0 +1,21 @@
1
+ module Mondo
2
+ require_relative '../api/mondo/client'
3
+ require_relative '../api/mondo/version'
4
+
5
+ require_relative '../api/mondo/response'
6
+ require_relative '../api/mondo/resource'
7
+ require_relative '../api/mondo/errors'
8
+ require_relative '../api/mondo/utils'
9
+
10
+ require_relative '../api/mondo/transaction'
11
+ require_relative '../api/mondo/feed_item'
12
+
13
+ require_relative '../api/mondo/card'
14
+ require_relative '../api/mondo/balance'
15
+ require_relative '../api/mondo/address'
16
+ require_relative '../api/mondo/transaction'
17
+ require_relative '../api/mondo/account'
18
+ require_relative '../api/mondo/merchant'
19
+ require_relative '../api/mondo/attachment'
20
+ require_relative '../api/mondo/web_hook'
21
+ end
@@ -0,0 +1,31 @@
1
+ require 'yaml'
2
+
3
+ class ConfigParser
4
+
5
+ def initialize(path_to_config)
6
+ @path_to_config = path_to_config
7
+ @parsed = nil
8
+
9
+ @parsed = begin
10
+ YAML.load(File.open(@path_to_config))
11
+ rescue ArgumentError => e
12
+ puts "Could not parse YAML: #{e.message}"
13
+ end if File.exist?(@path_to_config)
14
+
15
+ end
16
+
17
+ def parse
18
+ return nil unless valid?
19
+ @parsed
20
+ end
21
+
22
+ def valid?
23
+ return nil if @parsed.nil?
24
+ return nil if @parsed['access_token'].nil?
25
+ return nil if @parsed['account_id'].nil?
26
+ return nil if @parsed['user_id'].nil?
27
+
28
+ true
29
+ end
30
+
31
+ end
@@ -0,0 +1,27 @@
1
+ require_relative '../api/mondo'
2
+
3
+ class MonzoApi
4
+
5
+ def initialize(config)
6
+ @user_id = config.parse['user_id']
7
+ @account_id = config.parse['account_id']
8
+ @access_token = config.parse['access_token']
9
+
10
+ @monzo = Mondo::Client.new(
11
+ token: @access_token,
12
+ account_id: @user_id)
13
+ end
14
+
15
+ def balance
16
+ @monzo.balance @account_id
17
+ end
18
+
19
+ def accounts
20
+ @monzo.accounts
21
+ end
22
+
23
+ def transactions
24
+ @monzo.transactions
25
+ end
26
+
27
+ end
@@ -0,0 +1,3 @@
1
+ module Monzo
2
+ VERSION = '0.0.1'
3
+ end
data/lib/monzo.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'monzo/version.rb'
2
+
3
+ # Add requires for other files you add to your project here, so
4
+ # you just need to require this one file in your bin file
data/monzo.gemspec ADDED
@@ -0,0 +1,36 @@
1
+ # Ensure we require the local version and not one we might have installed already
2
+ require File.join([File.dirname(__FILE__), 'lib', 'monzo', 'version.rb'])
3
+ spec = Gem::Specification.new do |s|
4
+ s.name = 'monzo-cli'
5
+ s.version = Monzo::VERSION
6
+ s.author = 'cesar ferreira'
7
+ s.email = 'cesar.manuel.ferreira@gmail.com'
8
+ s.homepage = 'http://cesarferreira.com'
9
+ s.platform = Gem::Platform::RUBY
10
+ s.summary = 'A description of your project'
11
+ s.files = `git ls-files`.split("
12
+ ")
13
+ s.require_paths << 'lib'
14
+ s.required_ruby_version = '>= 2.0.0'
15
+ s.bindir = 'bin'
16
+ s.executables << 'monzo-cli'
17
+
18
+ s.add_development_dependency 'rake'
19
+ s.add_development_dependency 'aruba'
20
+ s.add_development_dependency 'rspec'
21
+ s.add_development_dependency 'pry'
22
+
23
+ s.add_runtime_dependency 'gli', '2.14.0'
24
+ s.add_runtime_dependency 'terminal-table', '>= 1.7.3'
25
+ # s.add_runtime_dependency 'mondo', '~> 0.5.0'
26
+ s.add_runtime_dependency 'colorize', '~> 0.7'
27
+
28
+
29
+
30
+ s.add_runtime_dependency 'oauth2', '~> 1.0'
31
+ s.add_runtime_dependency 'money'
32
+ s.add_runtime_dependency 'multi_json', '~> 1.10'
33
+ s.add_runtime_dependency 'activesupport', '~> 3.2'
34
+
35
+
36
+ end
@@ -0,0 +1,2 @@
1
+ user_id: user_18231092askdas9212
2
+ account_id: acc_0aksdaklsjSh28181
@@ -0,0 +1,3 @@
1
+ user_id: user_18231092askdas9212
2
+ account_id: acc_0aksdaklsjSh28181
3
+ access_token: Qnjdas8hakxdjasQscGVgnVGIVXpvpZ5uCxkQ5XLnDHnOPoBtXreQ6adBo
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+ require 'monzo/config_parser'
3
+
4
+ describe '# Config Parser' do
5
+
6
+ it '# The file is present' do
7
+
8
+ # Given
9
+ path = 'spec/assets/test_config.yml'
10
+
11
+ # When
12
+ config = ConfigParser.new(path)
13
+
14
+ # Then
15
+ expect(config.parse).to_not be_nil
16
+ expect(config.parse['access_token']).to eq('Qnjdas8hakxdjasQscGVgnVGIVXpvpZ5uCxkQ5XLnDHnOPoBtXreQ6adBo')
17
+ expect(config.parse['account_id']).to eq('acc_0aksdaklsjSh28181')
18
+ expect(config.parse['user_id']).to eq('user_18231092askdas9212')
19
+
20
+ end
21
+
22
+ it '# The file is not present' do
23
+
24
+ # Given
25
+ path = 'spec/assets/NOT_CORRECT/test_config.yml'
26
+
27
+ # When
28
+ config = ConfigParser.new(path)
29
+
30
+ # Then
31
+ expect(config.parse).to be_nil
32
+
33
+ end
34
+
35
+ it '# The file is present but is corrupt' do
36
+
37
+ # Given
38
+ path = 'spec/assets/bad_test_config.yml'
39
+
40
+ # When
41
+ config = ConfigParser.new(path)
42
+
43
+ # Then
44
+ expect(config.parse).to be_nil
45
+
46
+ end
47
+
48
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+ require 'monzo/monzo_api'
3
+
4
+ describe '# Monzo API' do
5
+
6
+ # it '# The file is present' do
7
+ #
8
+ # # Given
9
+ # hash = 'spec/assets/test_config.yml'
10
+ #
11
+ # # When
12
+ # config = MonzoApi.new(hash)
13
+ #
14
+ # # Then
15
+ # expect(config.parse['access_token']).to eq('Qnjdas8hakxdjasQscGVgnVGIVXpvpZ5uCxkQ5XLnDHnOPoBtXreQ6adBo')
16
+ # expect(config.parse['account_id']).to eq('acc_0aksdaklsjSh28181')
17
+ # expect(config.parse['user_id']).to eq('user_18231092askdas9212')
18
+ #
19
+ # end
20
+ # expect { ... }.to raise_error
21
+
22
+ end
@@ -0,0 +1,25 @@
1
+ require 'monzo'
2
+ RSpec.configure do |config|
3
+ # rspec-expectations config goes here. You can use an alternate
4
+ # assertion/expectation library such as wrong or the stdlib/minitest
5
+ # assertions if you prefer.
6
+ config.expect_with :rspec do |expectations|
7
+ # This option will default to `true` in RSpec 4. It makes the `description`
8
+ # and `failure_message` of custom matchers include text for helper methods
9
+ # defined using `chain`, e.g.:
10
+ # be_bigger_than(2).and_smaller_than(4).description
11
+ # # => "be bigger than 2 and smaller than 4"
12
+ # ...rather than:
13
+ # # => "be bigger than 2"
14
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
15
+ end
16
+
17
+ # rspec-mocks config goes here. You can use an alternate test double
18
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
19
+ config.mock_with :rspec do |mocks|
20
+ # Prevents you from mocking or stubbing a method that does not exist on
21
+ # a real object. This is generally recommended, and will default to
22
+ # `true` in RSpec 4.
23
+ mocks.verify_partial_doubles = true
24
+ end
25
+ end