litmus-infrastructure 0.2.6
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 +7 -0
- data/.gitignore +10 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +76 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/rake +16 -0
- data/bin/setup +7 -0
- data/lib/litmus/infrastructure/base.rb +31 -0
- data/lib/litmus/infrastructure/configuration.rb +30 -0
- data/lib/litmus/infrastructure/data_formatter.rb +23 -0
- data/lib/litmus/infrastructure/exceptions.rb +75 -0
- data/lib/litmus/infrastructure/hash_util.rb +39 -0
- data/lib/litmus/infrastructure/model.rb +110 -0
- data/lib/litmus/infrastructure/response.rb +53 -0
- data/lib/litmus/infrastructure/version.rb +5 -0
- data/lib/litmus/infrastructure.rb +43 -0
- data/litmus-infrastructure.gemspec +23 -0
- data/spec/lib/litmus/infrastructure/base_spec.rb +24 -0
- data/spec/lib/litmus/infrastructure/model_spec.rb +74 -0
- data/spec/lib/litmus/infrastructure/response_spec.rb +50 -0
- data/spec/spec_helper.rb +8 -0
- metadata +135 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 30fb2b926c21f50121d2cdd666cb1a456d3c164b7597f8033057eed7a259c248
|
4
|
+
data.tar.gz: '080135fdc2d1401975b9feeefac484bb0ff8c6571364a5f42966b72215b03ebf'
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9a329aaadac90d160ce5140fb23e8fd43c483aeca0bc839c28614b02c805d14279b16d6c4157982f488764e001f7f29ec7c1ada3ea8866210194fe42add884d2
|
7
|
+
data.tar.gz: a57934588f013a3d9495ee337b79feefafefc5610153190481508c514512d765bc8d4ca8a784943d36712424c010141884219a281003e12463feb6466d55aab4
|
data/.gitignore
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.7.2
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Kevin Thompson
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# Litmus::Infrastructure
|
2
|
+
|
3
|
+
A gem that provides an interface between a Ruby application and the Litmus
|
4
|
+
infrastructure API.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
If your production environment will have access to this repo (a user on your production server must have an ssh key that corresponds to a user with access to this repo), you can declare the gem in your gemfile using the `github` parameter:
|
9
|
+
|
10
|
+
``` ruby
|
11
|
+
gem 'litmus-infrastructure', github: 'litmus/litmus-infrastructure-client'
|
12
|
+
```
|
13
|
+
|
14
|
+
If you do not have access to the SSH keys in your production environment, when using Heroku for example, you'll need to use a repo URL that includes the API key for a user with access to this repo:
|
15
|
+
|
16
|
+
``` ruby
|
17
|
+
gem 'litmus-infrastructure', git: 'https://GITHUB_API_KEY_XXXXXX@github.com/litmus/litmus-infrastructure-client.git'
|
18
|
+
```
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
### Rails
|
23
|
+
|
24
|
+
Include `Litmus::Infrastructure` in a module that you'd like to have interact
|
25
|
+
with the infrastructure API:
|
26
|
+
|
27
|
+
``` ruby
|
28
|
+
module Checklist
|
29
|
+
include Litmus::Infrastructure
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
Your integration should be configured by creating an initializer:
|
34
|
+
|
35
|
+
``` ruby
|
36
|
+
Checklist.configure do |config|
|
37
|
+
config.timeout_connect = 5.seconds
|
38
|
+
config.timeout_receive = 1.minute
|
39
|
+
config.username = ENV['INFRASTRUCTURE_USERNAME']
|
40
|
+
config.password = ENV['INFRASTRUCTURE_PASSWORD']
|
41
|
+
config.url = ENV['INFRASTRUCTURE_API_BASE_URL']
|
42
|
+
config.logger = Rails.logger
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
Any objects that should act as models for data from the infrastructure API
|
47
|
+
should extend `Litmus::Infrastructure::Model`:
|
48
|
+
|
49
|
+
``` ruby
|
50
|
+
module Checklist
|
51
|
+
class Result < Litmus::Infrastructure::Model
|
52
|
+
# ...
|
53
|
+
end
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
Your models can define attributes to read from the API using the `attributes`
|
58
|
+
class method:
|
59
|
+
|
60
|
+
``` ruby
|
61
|
+
module Checklist
|
62
|
+
class Result < Litmus::Infrastructure::Model
|
63
|
+
attributes :content, :encoding, :from, :grade, :images
|
64
|
+
# ...
|
65
|
+
end
|
66
|
+
end
|
67
|
+
```
|
68
|
+
|
69
|
+
Once your models are defined, you can interact with the infrastructure API using
|
70
|
+
the class methods `put`, `post`, and `get`, passing the appropriate
|
71
|
+
endpoint/action to these methods as the first parameter, and the JSON payload as
|
72
|
+
the second:
|
73
|
+
|
74
|
+
``` ruby
|
75
|
+
Checklist::Result.post('Checklist', { subject: 'Example' }.to_json)
|
76
|
+
```
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "litmus/infrastructure"
|
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/rake
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'rake' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('rake', 'rake')
|
data/bin/setup
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module Litmus
|
2
|
+
module Infrastructure
|
3
|
+
module Base
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def namespace
|
11
|
+
@namespace ||= parents.find do |mod|
|
12
|
+
mod.included_modules.include?(Litmus::Infrastructure)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def client
|
17
|
+
namespace.http_client
|
18
|
+
end
|
19
|
+
|
20
|
+
def config
|
21
|
+
namespace.configuration
|
22
|
+
end
|
23
|
+
|
24
|
+
def logger
|
25
|
+
config.logger
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Litmus
|
4
|
+
module Infrastructure #:nodoc:
|
5
|
+
|
6
|
+
# Configuration options for Litmus Infrastructure APIs.
|
7
|
+
#
|
8
|
+
# == Attributes
|
9
|
+
#
|
10
|
+
# * <tt>username</tt> - Your username for the Litmus Infrastructure API
|
11
|
+
# * <tt>password</tt> - Your password for the Litmus Infrastructure API
|
12
|
+
# * <tt>logger</tt> - A Logger instance. Defaults to <tt>Logger.new(STDOUT)</tt>.
|
13
|
+
#
|
14
|
+
# == Example
|
15
|
+
# Litmus::Infrastructure.configure do |config|
|
16
|
+
# config.username = 'yourusername'
|
17
|
+
# config.password = 'yourpassword'
|
18
|
+
# config.logger = Rails.logger
|
19
|
+
# end
|
20
|
+
class Configuration
|
21
|
+
attr_accessor :timeout_connect, :timeout_receive, :url, :username, :password
|
22
|
+
attr_writer :url, :logger
|
23
|
+
|
24
|
+
def logger
|
25
|
+
@logger ||= ::Logger.new(STDOUT)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Litmus
|
2
|
+
module Infrastructure
|
3
|
+
module DataFormatter
|
4
|
+
|
5
|
+
def self.format(data)
|
6
|
+
underscore parse(data)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.underscore(data)
|
10
|
+
if data.is_a? Array
|
11
|
+
data.map { |array_value| HashUtil.underscore_keys array_value }
|
12
|
+
else
|
13
|
+
HashUtil.underscore_keys data
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.parse(data)
|
18
|
+
JSON.parse data.content
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Litmus
|
2
|
+
module Infrastructure
|
3
|
+
class GenericError < StandardError
|
4
|
+
include Base
|
5
|
+
end
|
6
|
+
|
7
|
+
# Raised when Litmus::Infrastructure isn't properly configured. See Configuration for
|
8
|
+
# details on configuring Litmus::Infrastructure.
|
9
|
+
class NotConfigured < GenericError; end
|
10
|
+
|
11
|
+
# Raised when a GUID cannot be found and a 404 response code is received.
|
12
|
+
class NotFound < GenericError
|
13
|
+
def initialize(response) #:nodoc:
|
14
|
+
headers = response.http_header
|
15
|
+
req_method = headers.request_method
|
16
|
+
req_uri = headers.request_uri
|
17
|
+
super "#{req_method} #{req_uri} could not be found"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Raised when the API returns an unauthorized or forbidden response.
|
22
|
+
#
|
23
|
+
# === Attributes
|
24
|
+
# * <tt>response</tt> - The Response from the Litmus Infrastructure API
|
25
|
+
class UnauthorizedResponse < GenericError
|
26
|
+
attr_reader :response
|
27
|
+
|
28
|
+
def initialize(response) #:nodoc:
|
29
|
+
@response = response
|
30
|
+
message = 'Your username and/or password were invalid. Please check your credentials and try again.'
|
31
|
+
# logger.error "[Litmus::Infrastructure] [Response] #{response.inspect}"
|
32
|
+
super message
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Raised when a 500 response code has been received.
|
37
|
+
#
|
38
|
+
# === Attributes
|
39
|
+
# * <tt>response</tt> - The Response from the Litmus Infrastructure API
|
40
|
+
class ServerException < GenericError
|
41
|
+
attr_reader :response
|
42
|
+
|
43
|
+
def initialize(response, params = nil) #:nodoc:
|
44
|
+
@response = response
|
45
|
+
status = response.status
|
46
|
+
headers = response.http_header
|
47
|
+
req_method = headers.request_method
|
48
|
+
req_uri = headers.request_uri
|
49
|
+
error = headers.reason_phrase
|
50
|
+
|
51
|
+
message = "#{req_method} #{req_uri} failed with #{status} (#{error})"
|
52
|
+
message += "\n\nResponse: #{response.body}"
|
53
|
+
message += "\n\nRequest body: #{params.to_json}" if params.is_a?(Hash)
|
54
|
+
super message
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Raised when the response code does not match that of the expected code.
|
59
|
+
#
|
60
|
+
# === Attributes
|
61
|
+
# * <tt>response</tt> - The Response from the Litmus Infrastructure API
|
62
|
+
class InvalidResponse < GenericError
|
63
|
+
attr_reader :error_messages, :response
|
64
|
+
|
65
|
+
def initialize(response, expected_status = 200) #:nodoc:
|
66
|
+
@error_messages = response.json? ? Array(response.json[:message]) : []
|
67
|
+
@response = response
|
68
|
+
# logger.error "[Litmus::Infrastructure] [Response] #{response.inspect}"
|
69
|
+
message = "Got status code #{response.status}, but expected #{expected_status}." <<
|
70
|
+
' Message: ' << @error_messages.join(', ') if @error_messages.any?
|
71
|
+
super message
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'active_support/core_ext/string/inflections'
|
2
|
+
|
3
|
+
module Litmus
|
4
|
+
module Infrastructure #:nodoc:
|
5
|
+
|
6
|
+
class HashUtil #:nodoc:
|
7
|
+
# Returns a new hash with all keys converted to :underscore_symbols from its
|
8
|
+
# CamelCase counterpart
|
9
|
+
def self.underscore_keys(hash)
|
10
|
+
deep_transform_keys(hash) { |key| key.underscore.to_sym }
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns a new hash with all keys converted to CameCase
|
14
|
+
def self.camel_keys(hash)
|
15
|
+
deep_transform_keys(hash) { |key| key.to_s.camelcase.to_s }
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# Return a new hash with all keys converted by the block operation. This includes the keys from the root hash and from all nested hashes.
|
21
|
+
def self.deep_transform_keys(object, &block)
|
22
|
+
return object unless object.is_a? Hash
|
23
|
+
result = {}
|
24
|
+
object.each do |key, value|
|
25
|
+
result[yield(key)] =
|
26
|
+
if value.is_a?(Hash)
|
27
|
+
deep_transform_keys(value, &block)
|
28
|
+
elsif value.is_a?(Array)
|
29
|
+
value.map { |array_value| deep_transform_keys(array_value, &block) }
|
30
|
+
else
|
31
|
+
value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
result
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'litmus/infrastructure/base'
|
3
|
+
|
4
|
+
module Litmus
|
5
|
+
module Infrastructure
|
6
|
+
|
7
|
+
class Model
|
8
|
+
include Base
|
9
|
+
|
10
|
+
# Create a model with attributes provided in <tt>attrs</tt>. Only
|
11
|
+
# attributes defined in <tt>attributes</tt> will be allowed.
|
12
|
+
def initialize(attrs = {})
|
13
|
+
set_attributes(attrs)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Serialize the object to a Hash to send back to the API
|
17
|
+
def serialize
|
18
|
+
hash = {}
|
19
|
+
self.class.safe_attributes.each { |key| hash[key] = self.send(key) }
|
20
|
+
HashUtil.camel_keys hash
|
21
|
+
end
|
22
|
+
|
23
|
+
def set_attributes(attrs = {})
|
24
|
+
safe_attrs = attrs.select { |key, _| self.class.safe_attributes.include?(key) }
|
25
|
+
safe_attrs.each { |key, value| self.send "#{key}=".to_sym, value }
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_hash
|
29
|
+
self.class.safe_attributes.inject({}) do |hash, key|
|
30
|
+
hash[key] = instance_variable_get("@#{key}")
|
31
|
+
hash
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class << self
|
36
|
+
|
37
|
+
# Create an array of safe attributes and call <tt>attr_accessor</tt>
|
38
|
+
def attributes(*attrs)
|
39
|
+
@safe_attributes ||= []
|
40
|
+
@safe_attributes.concat attrs
|
41
|
+
attr_accessor(*attrs)
|
42
|
+
end
|
43
|
+
|
44
|
+
def safe_attributes
|
45
|
+
@safe_attributes || []
|
46
|
+
end
|
47
|
+
|
48
|
+
# Perform an HTTP request
|
49
|
+
def request(method = :get, action = nil, params = {}, headers = {}, follow_redirect = false, &block)
|
50
|
+
url = "#{config.url}#{action}"
|
51
|
+
auth = Base64.strict_encode64("#{config.username}:#{config.password}")
|
52
|
+
|
53
|
+
# set timeouts
|
54
|
+
if config.timeout_connect
|
55
|
+
client.connect_timeout = config.timeout_connect.to_i
|
56
|
+
end
|
57
|
+
if config.timeout_receive
|
58
|
+
client.receive_timeout = config.timeout_receive.to_i
|
59
|
+
end
|
60
|
+
|
61
|
+
logger.debug <<-DEBUG.strip
|
62
|
+
[Litmus::Infrastructure] #{ method.to_s.upcase } #{ url }
|
63
|
+
#{ params.inspect }
|
64
|
+
DEBUG
|
65
|
+
|
66
|
+
request_headers = {
|
67
|
+
'Authorization' => "Basic #{auth}",
|
68
|
+
'Accept' => 'application/json',
|
69
|
+
'Content-Type' => 'application/json'
|
70
|
+
}.merge headers
|
71
|
+
|
72
|
+
response = client.send method, url, params, request_headers, follow_redirect, &block
|
73
|
+
|
74
|
+
logger.debug "[Litmus::Infrastructure] Response Code: #{response.status}"
|
75
|
+
|
76
|
+
if response.status == 404
|
77
|
+
raise NotFound.new(response)
|
78
|
+
elsif response.status >= 401 && response.status <= 403
|
79
|
+
raise UnauthorizedResponse.new(response)
|
80
|
+
elsif response.status == 400 || (response.status >= 405 && response.status < 600)
|
81
|
+
raise ServerException.new(response, params)
|
82
|
+
else
|
83
|
+
Response.new response
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Perform a POST request
|
88
|
+
def post(action = nil, params = {})
|
89
|
+
request(:post, action, params)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Perform a PUT request
|
93
|
+
def put(action = nil, params = {})
|
94
|
+
request(:put, action, params)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Perform a GET request
|
98
|
+
def get(action = nil, params = {})
|
99
|
+
request(:get, action, params)
|
100
|
+
end
|
101
|
+
|
102
|
+
def map_response(response, klass)
|
103
|
+
response.json.map { |obj| klass.new obj }
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'litmus/infrastructure/hash_util'
|
3
|
+
|
4
|
+
module Litmus
|
5
|
+
module Infrastructure
|
6
|
+
|
7
|
+
# A response returned from perform an HTTP action. Response gives you access
|
8
|
+
# to the content of the response, status code, and the HTTPClient response.
|
9
|
+
#
|
10
|
+
# === Attributes
|
11
|
+
# * <tt>raw</tt> - The HTTP::Message returned from the request
|
12
|
+
# * <tt>content</tt> - String representation of the response body
|
13
|
+
# * <tt>status</tt> - HTTP status code
|
14
|
+
class Response
|
15
|
+
SUCCESS_CODES = (200..299).freeze
|
16
|
+
attr_accessor :raw, :content, :status
|
17
|
+
|
18
|
+
def initialize(response) #:nodoc:
|
19
|
+
self.raw = response
|
20
|
+
self.content = response.content
|
21
|
+
self.status = response.status
|
22
|
+
end
|
23
|
+
|
24
|
+
# Parse <tt>content</tt> as JSON and return the result. All keys will be
|
25
|
+
# converted to their underscore and symbol equivalent.
|
26
|
+
def json
|
27
|
+
return nil if content.empty?
|
28
|
+
@json ||= JSON.parse(content)
|
29
|
+
if @json.is_a?(Array)
|
30
|
+
@json.map { |obj| HashUtil.underscore_keys obj }
|
31
|
+
else
|
32
|
+
HashUtil.underscore_keys @json
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Determine if the response was a JSON result
|
37
|
+
#
|
38
|
+
# === Returns
|
39
|
+
# Boolean - true if the response's Content-Type is "json"
|
40
|
+
def json?
|
41
|
+
raw.headers['Content-Type'] == 'json'
|
42
|
+
end
|
43
|
+
|
44
|
+
# Determine if the response was successful.
|
45
|
+
#
|
46
|
+
# === Returns
|
47
|
+
# Boolean - true if the response's status is in the 2xx series
|
48
|
+
def successful?
|
49
|
+
SUCCESS_CODES.include? status
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'httpclient'
|
2
|
+
require 'httpclient/include_client'
|
3
|
+
|
4
|
+
require 'litmus/infrastructure/version'
|
5
|
+
require 'litmus/infrastructure/hash_util'
|
6
|
+
require 'litmus/infrastructure/data_formatter'
|
7
|
+
require 'litmus/infrastructure/base'
|
8
|
+
require 'litmus/infrastructure/response'
|
9
|
+
require 'litmus/infrastructure/configuration'
|
10
|
+
require 'litmus/infrastructure/exceptions'
|
11
|
+
require 'litmus/infrastructure/model'
|
12
|
+
|
13
|
+
require 'active_support/core_ext/module'
|
14
|
+
|
15
|
+
module Litmus
|
16
|
+
module Infrastructure
|
17
|
+
def self.included(base)
|
18
|
+
base.extend(ClassMethods)
|
19
|
+
base.extend(HTTPClient::IncludeClient)
|
20
|
+
base.send(:include_http_client)
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
def configure
|
25
|
+
@configuration ||= Configuration.new
|
26
|
+
yield @configuration
|
27
|
+
end
|
28
|
+
|
29
|
+
def configuration
|
30
|
+
if @configuration.nil?
|
31
|
+
raise NotConfigured, "You've not yet configured this service. See documentation for details."
|
32
|
+
end
|
33
|
+
|
34
|
+
@configuration
|
35
|
+
end
|
36
|
+
|
37
|
+
def logger
|
38
|
+
configuration.logger
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'litmus/infrastructure/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'litmus-infrastructure'
|
8
|
+
spec.version = Litmus::Infrastructure::VERSION
|
9
|
+
spec.authors = ["Kevin Thompson", "Eddie Cianci", "Drew Tempelmeyer"]
|
10
|
+
spec.email = ["kevinthompson@litmus.com", "eddie@litmus.com", "drew@litmus.com"]
|
11
|
+
spec.summary = %q{A module for interacting with the Litmus infrastructure APIs}
|
12
|
+
spec.homepage = ""
|
13
|
+
|
14
|
+
spec.files = `git ls-files -z`.split("\x0")
|
15
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
16
|
+
spec.require_paths = ['lib']
|
17
|
+
|
18
|
+
spec.add_dependency 'activesupport', '>= 4.0.0', '< 6.0'
|
19
|
+
spec.add_dependency 'httpclient', '~> 2.5'
|
20
|
+
|
21
|
+
spec.add_development_dependency 'rspec'
|
22
|
+
spec.add_development_dependency 'rake'
|
23
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module Fake1
|
4
|
+
include Litmus::Infrastructure
|
5
|
+
module Foo1
|
6
|
+
class Bar1 < Litmus::Infrastructure::Model
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module Fake2
|
12
|
+
module Foo2
|
13
|
+
include Litmus::Infrastructure
|
14
|
+
class Bar2 < Litmus::Infrastructure::Model
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe Litmus::Infrastructure::Base do
|
20
|
+
it "sets the correct namespace" do
|
21
|
+
expect(Fake1::Foo1::Bar1.namespace).to eq Fake1
|
22
|
+
expect(Fake2::Foo2::Bar2.namespace).to eq Fake2::Foo2
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Litmus::Infrastructure::Model do
|
4
|
+
let(:client){ double('HTTPClient') }
|
5
|
+
let(:logger){ double('Logger') }
|
6
|
+
let(:response){ double('Litmus::Infrastructure::Response', status: 200, content: '') }
|
7
|
+
let(:config){
|
8
|
+
double(
|
9
|
+
'Litmus::Configuration', {
|
10
|
+
url: '',
|
11
|
+
username: '',
|
12
|
+
password: '',
|
13
|
+
timeout_connect: 10,
|
14
|
+
timeout_receive: 10,
|
15
|
+
logger: logger
|
16
|
+
}
|
17
|
+
)
|
18
|
+
}
|
19
|
+
|
20
|
+
before do
|
21
|
+
allow(subject.class).to receive(:client).and_return(client)
|
22
|
+
allow(subject.class).to receive(:config).and_return(config)
|
23
|
+
allow(client).to receive(:connect_timeout=)
|
24
|
+
allow(client).to receive(:receive_timeout=)
|
25
|
+
allow(logger).to receive(:debug)
|
26
|
+
end
|
27
|
+
|
28
|
+
context '::attributes' do
|
29
|
+
class TestModel < Litmus::Infrastructure::Model
|
30
|
+
attributes :test_id
|
31
|
+
attr_accessor :name
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'has no @safe_attributes' do
|
35
|
+
expect(Litmus::Infrastructure::Model.safe_attributes).to eq([])
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'has :test_id in @safe_attributes' do
|
39
|
+
expect(TestModel.safe_attributes).to eq [:test_id]
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'creates writer' do
|
43
|
+
expect(TestModel.new).to respond_to 'test_id='.to_sym
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'creates reader' do
|
47
|
+
expect(TestModel.new).to respond_to :test_id
|
48
|
+
end
|
49
|
+
|
50
|
+
context '::initialize' do
|
51
|
+
it 'only sets what is in @safe_attributes' do
|
52
|
+
model = TestModel.new(test_id: 123, name: 'Test')
|
53
|
+
expect(model.test_id).to eq 123
|
54
|
+
expect(model.name).to be_nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '.request' do
|
60
|
+
context 'when the request content type is text/csv' do
|
61
|
+
let(:auth){ Base64.strict_encode64("#{config.username}:#{config.password}") }
|
62
|
+
let(:request_headers){ Hash['Accept' => 'text/csv', 'Content-Type' => 'text/csv'] }
|
63
|
+
let(:request_arguments){ [:get, 'Example', {}, request_headers] }
|
64
|
+
let(:headers){ Hash['Authorization' => "Basic #{auth}"].merge(request_headers) }
|
65
|
+
let(:arguments){ [:get, 'Example', {}, headers, false] }
|
66
|
+
|
67
|
+
it 'sends the appropriate header when making a request' do
|
68
|
+
expect(client).to receive(:send).with(*arguments).and_return(response)
|
69
|
+
subject.class.request(*request_arguments)
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'litmus/infrastructure/response'
|
3
|
+
|
4
|
+
describe Litmus::Infrastructure::Response do
|
5
|
+
let(:json_string) { { 'Message' => 'Success.' }.to_json }
|
6
|
+
let(:status) { 200 }
|
7
|
+
let(:response) do
|
8
|
+
Litmus::Infrastructure::Response.new(
|
9
|
+
double('HTTP::Message', status: status, content: json_string)
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#json" do
|
14
|
+
it "returns correct data from response" do
|
15
|
+
expect(response.json).to eq(message: "Success.")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#json when response is empty" do
|
20
|
+
let(:json_string) { "" }
|
21
|
+
|
22
|
+
it "returns nil if no data was passed in response" do
|
23
|
+
expect(response.json).to be_nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it '#raw' do
|
28
|
+
expect(response.raw.status).to eq 200
|
29
|
+
end
|
30
|
+
|
31
|
+
it '#content' do
|
32
|
+
expect(response.content).to eq json_string
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#successful?" do
|
36
|
+
context "when the status code is in the 2xx series" do
|
37
|
+
it "returns true" do
|
38
|
+
expect(response).to be_successful
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "when the status code is outside of the 2xx series" do
|
43
|
+
let(:status) { 500 }
|
44
|
+
|
45
|
+
it "returns false" do
|
46
|
+
expect(response).to_not be_successful
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: litmus-infrastructure
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.6
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kevin Thompson
|
8
|
+
- Eddie Cianci
|
9
|
+
- Drew Tempelmeyer
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2021-11-29 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activesupport
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - ">="
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 4.0.0
|
22
|
+
- - "<"
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '6.0'
|
25
|
+
type: :runtime
|
26
|
+
prerelease: false
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: 4.0.0
|
32
|
+
- - "<"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '6.0'
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: httpclient
|
37
|
+
requirement: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '2.5'
|
42
|
+
type: :runtime
|
43
|
+
prerelease: false
|
44
|
+
version_requirements: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '2.5'
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: rspec
|
51
|
+
requirement: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
type: :development
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: rake
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
description:
|
78
|
+
email:
|
79
|
+
- kevinthompson@litmus.com
|
80
|
+
- eddie@litmus.com
|
81
|
+
- drew@litmus.com
|
82
|
+
executables: []
|
83
|
+
extensions: []
|
84
|
+
extra_rdoc_files: []
|
85
|
+
files:
|
86
|
+
- ".gitignore"
|
87
|
+
- ".ruby-version"
|
88
|
+
- Gemfile
|
89
|
+
- LICENSE.txt
|
90
|
+
- README.md
|
91
|
+
- Rakefile
|
92
|
+
- bin/console
|
93
|
+
- bin/rake
|
94
|
+
- bin/setup
|
95
|
+
- lib/litmus/infrastructure.rb
|
96
|
+
- lib/litmus/infrastructure/base.rb
|
97
|
+
- lib/litmus/infrastructure/configuration.rb
|
98
|
+
- lib/litmus/infrastructure/data_formatter.rb
|
99
|
+
- lib/litmus/infrastructure/exceptions.rb
|
100
|
+
- lib/litmus/infrastructure/hash_util.rb
|
101
|
+
- lib/litmus/infrastructure/model.rb
|
102
|
+
- lib/litmus/infrastructure/response.rb
|
103
|
+
- lib/litmus/infrastructure/version.rb
|
104
|
+
- litmus-infrastructure.gemspec
|
105
|
+
- spec/lib/litmus/infrastructure/base_spec.rb
|
106
|
+
- spec/lib/litmus/infrastructure/model_spec.rb
|
107
|
+
- spec/lib/litmus/infrastructure/response_spec.rb
|
108
|
+
- spec/spec_helper.rb
|
109
|
+
homepage: ''
|
110
|
+
licenses: []
|
111
|
+
metadata: {}
|
112
|
+
post_install_message:
|
113
|
+
rdoc_options: []
|
114
|
+
require_paths:
|
115
|
+
- lib
|
116
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
requirements: []
|
127
|
+
rubygems_version: 3.1.4
|
128
|
+
signing_key:
|
129
|
+
specification_version: 4
|
130
|
+
summary: A module for interacting with the Litmus infrastructure APIs
|
131
|
+
test_files:
|
132
|
+
- spec/lib/litmus/infrastructure/base_spec.rb
|
133
|
+
- spec/lib/litmus/infrastructure/model_spec.rb
|
134
|
+
- spec/lib/litmus/infrastructure/response_spec.rb
|
135
|
+
- spec/spec_helper.rb
|