whiplash-app 0.2.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c39a9db7a5846ef00446af1eddd5be55c8240dc4
4
+ data.tar.gz: 15cb72ab9cc973219e8edba2fed1c15893fdeb7c
5
+ SHA512:
6
+ metadata.gz: a23d95521223c0ae13609e99a782bc63064be34e74b08ee87be3da7472550d84c27634308da50cfd5dbd647ed94c5b397a07b6253a58041f00efc70e5d8aacf1
7
+ data.tar.gz: 86276e51e588c597897f9578bc2e1d59e829ca323cfae103f425ca46330a5db642c452174f546f1e1cdf9bf9781ae06e1181f0855b1e2d424ab53197370341b2
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.3
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in whiplash-app.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # whiplash-app
2
+
3
+ The whiplash-app gem allows your Whiplash application to access the Whiplash
4
+ API and perform authentication, signatures and signature verification, and basic
5
+ CRUD functions against the api.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'whiplash-app'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install whiplash-app
22
+
23
+ ## Usage
24
+
25
+ ###Authentication
26
+ In order to authenticate, make sure the following `ENV` vars are set:
27
+
28
+ ```ruby
29
+ ENV["WHIPLASH_CLIENT_ID"]
30
+ ENV["WHIPLASH_CLIENT_SECRET"]
31
+ ENV["WHIPLASH_CLIENT_SCOPE"]
32
+ ```
33
+ Once those are set, authentication is handled in app. While authentication is
34
+ mostly baked into the calls, there is a method that allows for getting the app's
35
+ token from the main app: `Whiplash::App.token`.
36
+
37
+ ### API URL
38
+ In order to set your api url, you can use the following environment URL:
39
+ ```
40
+ ENV["WHIPLASH_API_URL"]
41
+ ```
42
+ If it isn't set, then the API URL defaults to either `https://testing.whiplashmerch.com` (test or dev environment) or `https://www.whiplashmerch.com` (prod environment).
43
+
44
+ ###Rails AR type calls
45
+
46
+ In order to make the use of the gem seem more "AR-ish", we've added AR oriented methods that can be used for basic object creation/deletion/updating/viewing. The basic gist of these AR style CRUD methods is that they will all follow the same pattern. If you are performing a collection action, such as `create` or `find`, the pattern is this:
47
+
48
+ ```ruby
49
+ Whiplash::App.create(resource, params, headers)
50
+ ```
51
+
52
+ For member actions, such as `show`, or `destroy` methods, the pattern is this:
53
+
54
+ ```ruby
55
+ Whiplash::App.find(resource, id, headers)
56
+ Whiplash::App.destroy(resource, id, headers)
57
+ ```
58
+
59
+ Finally, for `update` calls, it's a mixture of those:
60
+
61
+ ```ruby
62
+ Whiplash::App.update(resource, id, params_to_update, headers)
63
+ ```
64
+
65
+ So, basic AR style calls can be performed like so:
66
+
67
+ ```ruby
68
+ Whiplash::App.find_all('orders', {}, { customer_id: 187 })
69
+ Whiplash::App.find('orders', 1)
70
+ Whiplash::App.create('orders', { key: "value", key2: "value" }, { customer_id: 187 } )
71
+ Whiplash::App.update('orders', 1, { key: "value"}, { customer_id: 187 } )
72
+ Whiplash::App.destroy('orders', 1, { customer_id: 187 } )
73
+ Whiplash::App.count('customers') #unlike other calls, which return Faraday responses, this call returns an integer.
74
+ ```
75
+
76
+ ###CRUD Wrapper methods
77
+ In reality, all of these methods are simply wrapper methods around simple `GET/POST/PUT/DELETE` wrappers on Faraday, so if you want to get more granular,you can also make calls that simply reference the lower level REST verb:
78
+
79
+ ```ruby
80
+ Whiplash::App.get('orders', {}, {})
81
+ ```
82
+ Which will return all orders and roughly correspond to an index call. If you need to use `Whiplash::App` for nonRESTful calls, simply drop the full endpoint in as your first argument:
83
+
84
+ ```ruby
85
+ Whiplash::App.get('orders/non_restful_action', {}, {})
86
+ ```
87
+ `POST`, `PUT`, and `DELETE` calls can be performed in much the same way:
88
+ ```ruby
89
+ Whiplash::App.post(endpoint, params, headers) #POST request to the specified endpoint passing the payload in params
90
+ ```
91
+ ```ruby
92
+ Whiplash::App.put(endpoint, params, headers) #PUT request to the specified endpoint passing the payload in params
93
+ ```
94
+ ```ruby
95
+ Whiplash::App.delete(endpoint, params, headers) #DELETE request to the specified endpoint. Params would probably just be an id.
96
+ ```
97
+
98
+ *IMPORTANT!!!! PLEASE READ!*
99
+ It's best if possible to use the AR style methods. They hide a lot of issues with the way Faraday handles params. For example, this should, in theory, work:
100
+ ```ruby
101
+ Whiplash::App.get('orders', {id: 1}, {customer_id: 187})
102
+ ```
103
+ BUT, due to the way Faraday handles params, this would not, as expected, route to `orders#show` in the Whiplash App, but would instead route to `orders#index`, so it wouldn't return the expected singular order with an ID of 1, but all orders for that customer.
104
+
105
+ The gem works best when the base CRUD methods are used ONLY for situations where a singular endpoint can't be reached, such as a cancel action, or uncancel, which would have to be done like so:
106
+ ```ruby
107
+ Whiplash::App.post('orders/1/cancel', {}, {customer_id: 187})
108
+ ```
109
+
110
+
111
+ ###Signing and Verifying.
112
+ `whiplash-app` supports signing and verifying signatures like so:
113
+ ```ruby
114
+ Whiplash::App.signature(request_body)
115
+ ```
116
+ and verifications are done like so:
117
+ ```ruby
118
+ Whiplash::App.verified?(request)
119
+ ```
120
+
121
+ ###Caching
122
+ `whiplash-app` is Cache agnostic, relying on the `moneta` gem to provide that
123
+ interface. However, if you intend to specify `REDIS` as your key-value store of
124
+ choice, it's dead simple. Simply declare the following variables:
125
+ ```
126
+ ENV["REDIS_HOST"]
127
+ ENV["REDIS_PORT"]
128
+ ENV["REDIS_PASSWORD"]
129
+ ENV["REDIS_NAMESPACE"]
130
+ ```
131
+ If those are provided, `moneta` will use your redis connection and will namespace your cache storage under the redis namespace. By default, if you do not declare a `REDIS_NAMESPACE` value, the app will default to the `WHIPLASH_CLIENT_ID`.
132
+
133
+ ## Development
134
+
135
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
136
+
137
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
138
+
139
+ ## Contributing
140
+
141
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/whiplash-app.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "whiplash/app"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,23 @@
1
+ module Whiplash
2
+ module App
3
+ module ApiConfig
4
+
5
+ def api_url
6
+ if defined?(Rails)
7
+ %w(development test).include?(Rails.env.to_s) ? testing_url : production_url
8
+ else
9
+ ENV["WHIPLASH_API_URL"]
10
+ end
11
+ end
12
+
13
+ def production_url
14
+ ENV["WHIPLASH_API_URL"] || "https://www.getwhiplash.com"
15
+ end
16
+
17
+ def testing_url
18
+ ENV["WHIPLASH_API_URL"] || "https://testing.getwhiplash.com"
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ require "moneta"
2
+ require "whiplash/app/moneta/namespace"
3
+ module Whiplash
4
+ module App
5
+ module Caching
6
+
7
+ def cache_store
8
+ if ENV["REDIS_HOST"]
9
+ store = Moneta.new(:Redis, host: ENV["REDIS_HOST"], port: ENV["REDIS_PORT"], password: ENV["REDIS_PASSWORD"], expires: 7200)
10
+ Moneta::Namespace.new store, namespace_value
11
+ else
12
+ Moneta.new(:File, dir: "tmp", expires: 7200)
13
+ end
14
+ end
15
+
16
+ def namespace_value
17
+ ENV["REDIS_NAMESPACE"] || ENV["WHIPLASH_CLIENT_ID"]
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,57 @@
1
+ module Whiplash
2
+ module App
3
+ module Connections
4
+
5
+ def app_request(options = {})
6
+ if options[:params][:id]
7
+ endpoint = [options[:endpoint], options[:params].delete(:id)].join('/')
8
+ else
9
+ endpoint = options[:endpoint]
10
+ end
11
+ connection.send(options[:method],
12
+ endpoint,
13
+ options[:params],
14
+ sanitize_headers(options[:headers]))
15
+ end
16
+
17
+ def delete(endpoint, params = {}, headers = nil)
18
+ app_request(method: :delete,
19
+ endpoint: endpoint,
20
+ params: params,
21
+ headers: headers)
22
+ end
23
+
24
+ def get(endpoint, params = {}, headers = nil)
25
+ app_request(method: :get,
26
+ endpoint: endpoint,
27
+ params: params,
28
+ headers: headers)
29
+ end
30
+
31
+ def post(endpoint, params = {}, headers = nil)
32
+ app_request(method: :post,
33
+ endpoint: endpoint,
34
+ params: params,
35
+ headers: headers)
36
+ end
37
+
38
+ def put(endpoint, params = {}, headers = nil)
39
+ app_request(method: :put,
40
+ endpoint: endpoint,
41
+ params: params,
42
+ headers: headers)
43
+ end
44
+
45
+ def sanitize_headers(headers)
46
+ if headers
47
+ {}.tap do |hash|
48
+ headers.each do |k,v|
49
+ hash["X-#{k.to_s.upcase.gsub('_','-')}"] = v.to_s
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,30 @@
1
+ module Whiplash
2
+ module App
3
+ module FinderMethods
4
+
5
+ def count(resource, params = {}, headers = nil)
6
+ get("#{resource}/count", params, headers).body["count"]
7
+ end
8
+
9
+ def create(resource, params, headers = nil)
10
+ post("#{resource}", params, headers)
11
+ end
12
+
13
+ def destroy(resource, id, headers = nil)
14
+ delete("#{resource}", { id: id }, headers)
15
+ end
16
+
17
+ def find(resource, id, headers = nil)
18
+ get("#{resource}", { id: id }, headers)
19
+ end
20
+
21
+ def find_all(resource, params = {}, headers = nil)
22
+ get("#{resource}", params, headers)
23
+ end
24
+
25
+ def update(resource, id, params = {}, headers = nil)
26
+ put("#{resource}", params.merge(id: id), headers)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,45 @@
1
+ module Moneta
2
+ class Namespace
3
+ attr_reader :moneta_store
4
+
5
+ def initialize store, ns
6
+ @moneta_store, @ns = store, ns
7
+ end
8
+
9
+ def [] key
10
+ @moneta_store["#{@ns}:#{key}"]
11
+ end
12
+
13
+ def []= key, value
14
+ @moneta_store["#{@ns}:#{key}"] = value
15
+ end
16
+
17
+ def delete key
18
+ @moneta_store.delete "#{@ns}:#{key}"
19
+ end
20
+
21
+ def key? key
22
+ @moneta_store.key? "#{@ns}:#{key}"
23
+ end
24
+
25
+ def has_key? key
26
+ @moneta_store.has_key? "#{@ns}:#{key}"
27
+ end
28
+
29
+ def store key, value, options
30
+ @moneta_store.store "#{@ns}:#{key}", value, options
31
+ end
32
+
33
+ def update_key key, options
34
+ @moneta_store.update_key "#{@ns}:#{key}", options
35
+ end
36
+
37
+ def clear
38
+ @moneta_store.clear
39
+ end
40
+
41
+ def method_missing method, args
42
+ @moneta_store.call method, *args
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,22 @@
1
+ module Whiplash
2
+ module App
3
+ module Signing
4
+
5
+ def request_body(body)
6
+ body.blank? ? ENV["WHIPLASH_CLIENT_ID"] : body
7
+ end
8
+
9
+ def signature(body)
10
+ sha256 = OpenSSL::Digest::SHA256.new
11
+ OpenSSL::HMAC.hexdigest(sha256,
12
+ ENV["WHIPLASH_CLIENT_SECRET"], request_body(body))
13
+ end
14
+
15
+ def verified?(request)
16
+ body = request.try(:body).try(:read)
17
+ request.headers["X-WHIPLASH-SIGNATURE"] == signature(body)
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,5 @@
1
+ module Whiplash
2
+ module App
3
+ VERSION = "0.2.0"
4
+ end
5
+ end
@@ -0,0 +1,49 @@
1
+ require "whiplash/app/api_config"
2
+ require "whiplash/app/caching"
3
+ require "whiplash/app/connections"
4
+ require "whiplash/app/finder_methods"
5
+ require "whiplash/app/signing"
6
+ require "whiplash/app/version"
7
+ require "oauth2"
8
+ require "faraday_middleware"
9
+
10
+ module Whiplash
11
+
12
+ module App
13
+ class << self
14
+ include Whiplash::App::ApiConfig
15
+ include Whiplash::App::Caching
16
+ include Whiplash::App::Connections
17
+ include Whiplash::App::FinderMethods
18
+ include Whiplash::App::Signing
19
+
20
+ attr_accessor :customer_id, :shop_id
21
+
22
+ def client
23
+ OAuth2::Client.new(ENV["WHIPLASH_CLIENT_ID"], ENV["WHIPLASH_CLIENT_SECRET"], site: api_url)
24
+ end
25
+
26
+ def connection
27
+ out = Faraday.new [api_url, "api/v2"].join("/") do |conn|
28
+ conn.request :oauth2, token, token_type: "bearer"
29
+ conn.request :json
30
+ conn.response :json, :content_type => /\bjson$/
31
+ conn.use :instrumentation
32
+ conn.adapter Faraday.default_adapter
33
+ end
34
+ return out
35
+ end
36
+
37
+ def refresh_token!
38
+ oauth_token = client.client_credentials.get_token(scope: ENV["WHIPLASH_CLIENT_SCOPE"])
39
+ new_token = oauth_token.token
40
+ cache_store["whiplash_api_token"] = new_token
41
+ end
42
+
43
+ def token
44
+ refresh_token! unless cache_store["whiplash_api_token"]
45
+ return cache_store["whiplash_api_token"]
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'whiplash/app/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "whiplash-app"
8
+ spec.version = Whiplash::App::VERSION
9
+ spec.authors = ["Don Sullivan, Mark Dickson"]
10
+ spec.email = ["developers@getwhiplash.com"]
11
+
12
+ spec.summary = "this gem provides connectivity to the Whiplash API for authentication and REST requests."
13
+ spec.description = "this gem provides connectivity to the Whiplash API for authentication and REST requests."
14
+ spec.homepage = "https://github.com/whiplashmerch/whiplash-app"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "oauth2", "~> 1.2.0"
22
+ spec.add_dependency "faraday_middleware", "~> 0.10.0"
23
+ spec.add_dependency "moneta", "~> 0.8.0"
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.11"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_development_dependency "rspec", "~> 3.0"
28
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: whiplash-app
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Don Sullivan, Mark Dickson
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-05-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: oauth2
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday_middleware
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.10.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.10.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: moneta
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.8.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.8.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.11'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.11'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.0'
97
+ description: this gem provides connectivity to the Whiplash API for authentication
98
+ and REST requests.
99
+ email:
100
+ - developers@getwhiplash.com
101
+ executables: []
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - ".rspec"
107
+ - ".travis.yml"
108
+ - Gemfile
109
+ - README.md
110
+ - Rakefile
111
+ - bin/console
112
+ - bin/setup
113
+ - lib/whiplash/app.rb
114
+ - lib/whiplash/app/api_config.rb
115
+ - lib/whiplash/app/caching.rb
116
+ - lib/whiplash/app/connections.rb
117
+ - lib/whiplash/app/finder_methods.rb
118
+ - lib/whiplash/app/moneta/namespace.rb
119
+ - lib/whiplash/app/signing.rb
120
+ - lib/whiplash/app/version.rb
121
+ - whiplash-app.gemspec
122
+ homepage: https://github.com/whiplashmerch/whiplash-app
123
+ licenses: []
124
+ metadata: {}
125
+ post_install_message:
126
+ rdoc_options: []
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ requirements: []
140
+ rubyforge_project:
141
+ rubygems_version: 2.4.5.1
142
+ signing_key:
143
+ specification_version: 4
144
+ summary: this gem provides connectivity to the Whiplash API for authentication and
145
+ REST requests.
146
+ test_files: []
147
+ has_rdoc: