diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0cb6eeb --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +/.bundle/ +/.yardoc +/Gemfile.lock +/_yardoc/ +/coverage/ +/doc/ +/pkg/ +/spec/reports/ +/tmp/ diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..8c18f1a --- /dev/null +++ b/.rspec @@ -0,0 +1,2 @@ +--format documentation +--color diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..c871179 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at dan@tomlinson.io. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..3774db7 --- /dev/null +++ b/Gemfile @@ -0,0 +1,4 @@ +source 'https://rubygems.org' + +# Specify your gem's dependencies in twentysix.gemspec +gemspec diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..c474714 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Danielle Tomlinson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4f25350 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# Twentysix + +Completely unofficial Ruby gem for the N26 API. + +## Installation + +Add this line to your application's Gemfile: + +```ruby +gem 'twentysix' +``` + +And then execute: + + $ bundle + +Or install it yourself as: + + $ gem install twentysix + +## Contributing + +Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/twentysix. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. + + +## License + +The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). + diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..4c774a2 --- /dev/null +++ b/Rakefile @@ -0,0 +1,6 @@ +require 'bundler/gem_tasks' +require 'rspec/core/rake_task' + +RSpec::Core::RakeTask.new(:spec) + +task default: :spec diff --git a/bin/console b/bin/console new file mode 100755 index 0000000..019064c --- /dev/null +++ b/bin/console @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby + +require 'bundler/setup' +require 'twentysix' + +# You can add fixtures and/or initialization code here to make experimenting +# with your gem easier. You can also use a different console, if you like. + +# (If you use this, don't forget to add pry to your Gemfile!) +# require "pry" +# Pry.start + +require 'irb' +IRB.start diff --git a/bin/setup b/bin/setup new file mode 100755 index 0000000..dce67d8 --- /dev/null +++ b/bin/setup @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' +set -vx + +bundle install + +# Do any other automated setup that you need to do here diff --git a/lib/twentysix.rb b/lib/twentysix.rb new file mode 100644 index 0000000..197aa18 --- /dev/null +++ b/lib/twentysix.rb @@ -0,0 +1,6 @@ +require 'twentysix/version' +require 'twentysix/core' + +module TwentySix + # Your code goes here... +end diff --git a/lib/twentysix/core.rb b/lib/twentysix/core.rb new file mode 100644 index 0000000..a2d12b9 --- /dev/null +++ b/lib/twentysix/core.rb @@ -0,0 +1,162 @@ +module TwentySix + class Core + require 'httparty' + require 'deep_merge' + include HTTParty + base_uri 'https://api.tech26.de' + + def self.authenticate(username, password) + response = post('/oauth/token', + body: { + 'username' => username, + 'password' => password, + 'grant_type' => 'password' + }, + headers: { + 'Authorization' => 'Basic bXktdHJ1c3RlZC13ZHBDbGllbnQ6c2VjcmV0', + 'User-Agent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.109 Safari/537.36' + }) + if response['access_token'] + new(response['access_token']) + else + response + end + end + + def initialize(access_token) + @access_token = access_token + end + + def whoami + get '/api/me' + end + + def account_summary + get '/api/accounts' + end + + def addresses + get '/api/addresses' + end + + def contacts + get '/api/smrt/contacts' + end + + def categories + get '/api/smrt/categories' + end + + def card_with_id(id) + get "/api/cards/#{id}" + end + + def cards + get '/api/cards' + end + + def card_limits(card_id) + get "/api/settings/limits/#{card_id}" + end + + def update_card_limits(card_id, limits) + post "/api/settings/limits/#{card_id}", options: { body: limits } + end + + def statements + get '/api/statements' + end + + def statement(id, pdf: false) + prefix = if pdf + '' + else + 'json/' + end + get "/api/statements/#{prefix}#{id}", options: { headers: { 'Content-Type' => 'application/json' } } + end + + def transactions(count: 50, + include_pending: false, + text_filter: nil, + from_time: nil, + to_time: nil) + query = { + limit: count, + pending: include_pending + } + + if from_time && to_time + query['from'] = from_time.to_i + query['to'] = to_time.to_i + end + + query['textFilter'] = text_filter if text_filter + + get '/api/smrt/transactions', options: { query: query } + end + + def transaction(id) + get "/api/transactions/#{id}" + end + + def transaction_metadata(smartlink_id) + get "/api/transactions/#{smartlink_id}/metadata" + end + + # @TODO: Figure out what `SecurityContext` is to make this actually work. + # + def create_transfer(pin, name, iban, bic, amount, reference) + post '/api/transactions', options: { + body: { + pin: pin, + transaction: { + partnerName: name, + partnerBic: bic, + partnerIban: iban, + amount: amount, + referenceText: reference, + type: 'DT' + } + }.to_json, + headers: { 'Content-Type' => 'application/json' } + } + end + + def barzahlen_summary + get '/api/barzahlen/check' + end + + def block_card(card_id) + post "/api/cards/#{card_id}/block" + end + + def unblock_card(card_id) + post "/api/cards/#{card_id}/unblock" + end + + private + + def get(uri, options: {}) + opts = default_options + opts.deep_merge!(options) + self.class.get(uri, opts) + end + + def post(uri, options: {}) + opts = default_options + opts.deep_merge!(options) + self.class.post(uri, opts) + end + + def default_options + { headers: default_headers } + end + + def default_headers + { + 'Authorization' => "Bearer #{@access_token}" + } + end + end +end diff --git a/lib/twentysix/version.rb b/lib/twentysix/version.rb new file mode 100644 index 0000000..02c18dc --- /dev/null +++ b/lib/twentysix/version.rb @@ -0,0 +1,3 @@ +module TwentySix + VERSION = '0.1.0'.freeze +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..0ea56a3 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,2 @@ +$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) +require 'twentysix' diff --git a/spec/twentysix_spec.rb b/spec/twentysix_spec.rb new file mode 100644 index 0000000..9e943ca --- /dev/null +++ b/spec/twentysix_spec.rb @@ -0,0 +1,11 @@ +require 'spec_helper' + +describe TwentySix do + it 'has a version number' do + expect(TwentySix::VERSION).not_to be nil + end + + it 'does something useful' do + expect(false).to eq(true) + end +end diff --git a/twentysix.gemspec b/twentysix.gemspec new file mode 100644 index 0000000..9c1b30a --- /dev/null +++ b/twentysix.gemspec @@ -0,0 +1,28 @@ +# coding: utf-8 +lib = File.expand_path('../lib', __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require 'twentysix/version' + +Gem::Specification.new do |spec| + spec.name = 'twentysix' + spec.version = TwentySix::VERSION + spec.authors = ['Danielle Tomlinson'] + spec.email = ['dan@tomlinson.io'] + + spec.summary = 'A small wrapper around the n26 Banking API.' + spec.homepage = 'https://github.com/dantoml/twentysix' + spec.license = 'MIT' + + spec.files = `git ls-files -z`.split("\x0").reject do |f| + f.match(%r{^(test|spec|features)/}) + end + spec.bindir = 'exe' + spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } + spec.require_paths = ['lib'] + + spec.add_runtime_dependency 'deep_merge' + + spec.add_development_dependency 'bundler', '~> 1.13.a' + spec.add_development_dependency 'rake', '~> 10.0' + spec.add_development_dependency 'rspec', '~> 3.0' +end