campact_user_service 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 214a865845390e44559c068f95a74ad8a85a46ff83eb5192edcc8e0b4f39440d
4
+ data.tar.gz: 7b2664936fec33426d6807f114a7820cb34455d5d9e2278fd017f14b13c720f9
5
+ SHA512:
6
+ metadata.gz: 362b5d1d2735112327bb6b1f4c4d3137a719c565af5e37ef81bbf58958ea57400f9753c69b6132513a56bcd6639a1c0a565b84d28eda48833602cd2265bd6490
7
+ data.tar.gz: 8ea75a760589072fb567c3548f0b147f5420477ebbfae0009cf31614af365d3982c485b77857ead0af601df814c182d75a105056a65f8e0fa38d160f8e8a6493
@@ -0,0 +1 @@
1
+ campact-user-service
@@ -0,0 +1 @@
1
+ 2.5.3
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem 'base32', '~> 0.3.2'
4
+ gem 'rotp', '~> 3.3', '>= 3.3.1'
5
+ gem 'faraday', '>= 0.12.2', '< 1.0'
6
+ gem 'json', '~> 2.1'
7
+
8
+ group :development do
9
+ gem 'byebug', '>= 0'
10
+ gem 'rspec', '>= 0'
11
+ gem 'shoulda', '>= 0'
12
+ gem 'webmock', '>= 0'
13
+ gem 'rdoc', '~> 6.0', '>= 6.0.4'
14
+ gem 'bundler', '~> 1.16', '>= 1.16.2'
15
+ gem 'juwelier', '~> 2.4', '>= 2.4.9'
16
+ gem 'simplecov', '= 0.16.1'
17
+ gem 'pry-byebug', '~> 3.6'
18
+ gem 'faraday-detailed_logger', '~> 2.1', '>= 2.1.2'
19
+ end
data/LICENSE ADDED
@@ -0,0 +1,10 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 ControlShift
4
+
5
+ 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:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ 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.
10
+
@@ -0,0 +1,6 @@
1
+ # campact-user-service
2
+ a ruby gem that wraps the Campact user service API
3
+
4
+ ## License
5
+
6
+ MIT. See [LICENSE](LICENSE) for details.
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'juwelier'
15
+ Juwelier::Tasks.new do |gem|
16
+ gem.name = "campact_user_service"
17
+ gem.homepage = "http://github.com/controlshift/campact_user_service"
18
+ gem.license = "MIT"
19
+ gem.summary = %Q{Ruby wrapper for Campact User Service}
20
+ gem.description = %Q{Ruby wrapper for Campact User Service}
21
+ gem.email = "hello@controlshiftlabs.com"
22
+ gem.authors = ["ControlShift"]
23
+
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Juwelier::RubygemsDotOrgTasks.new
27
+
28
+ require 'rspec/core'
29
+ require 'rspec/core/rake_task'
30
+ RSpec::Core::RakeTask.new(:spec) do |spec|
31
+ spec.pattern = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ end
38
+
39
+ task :default => :spec
40
+
41
+ require 'rdoc/task'
42
+ Rake::RDocTask.new do |rdoc|
43
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
44
+
45
+ rdoc.rdoc_dir = 'rdoc'
46
+ rdoc.title = "krautbuster_api #{version}"
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 2.0.1
@@ -0,0 +1,98 @@
1
+ # Generated by juwelier
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+ # stub: campact_user_service 2.0.1 ruby lib
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "campact_user_service".freeze
9
+ s.version = "2.0.1"
10
+
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib".freeze]
13
+ s.authors = ["ControlShift".freeze]
14
+ s.date = "2019-08-12"
15
+ s.description = "Ruby wrapper for Campact User Service".freeze
16
+ s.email = "hello@controlshiftlabs.com".freeze
17
+ s.extra_rdoc_files = [
18
+ "LICENSE",
19
+ "README.md"
20
+ ]
21
+ s.files = [
22
+ ".ruby-gemset",
23
+ ".ruby-version",
24
+ "Gemfile",
25
+ "LICENSE",
26
+ "README.md",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "campact_user_service.gemspec",
30
+ "example.rb",
31
+ "lib/campact_user_service.rb",
32
+ "lib/campact_user_service/account.rb",
33
+ "lib/campact_user_service/client.rb",
34
+ "lib/campact_user_service/response_error.rb",
35
+ "lib/campact_user_service/session.rb",
36
+ "spec/account_spec.rb",
37
+ "spec/campact_user_service_spec.rb",
38
+ "spec/client_spec.rb",
39
+ "spec/session_spec.rb",
40
+ "spec/spec_helper.rb"
41
+ ]
42
+ s.homepage = "http://github.com/controlshift/campact_user_service".freeze
43
+ s.licenses = ["MIT".freeze]
44
+ s.rubygems_version = "3.0.3".freeze
45
+ s.summary = "Ruby wrapper for Campact User Service".freeze
46
+
47
+ if s.respond_to? :specification_version then
48
+ s.specification_version = 4
49
+
50
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
51
+ s.add_runtime_dependency(%q<base32>.freeze, ["~> 0.3.2"])
52
+ s.add_runtime_dependency(%q<rotp>.freeze, ["~> 3.3", ">= 3.3.1"])
53
+ s.add_runtime_dependency(%q<faraday>.freeze, [">= 0.12.2", "< 1.0"])
54
+ s.add_runtime_dependency(%q<json>.freeze, ["~> 2.1"])
55
+ s.add_development_dependency(%q<byebug>.freeze, [">= 0"])
56
+ s.add_development_dependency(%q<rspec>.freeze, [">= 0"])
57
+ s.add_development_dependency(%q<shoulda>.freeze, [">= 0"])
58
+ s.add_development_dependency(%q<webmock>.freeze, [">= 0"])
59
+ s.add_development_dependency(%q<rdoc>.freeze, ["~> 6.0", ">= 6.0.4"])
60
+ s.add_development_dependency(%q<bundler>.freeze, ["~> 1.16", ">= 1.16.2"])
61
+ s.add_development_dependency(%q<juwelier>.freeze, ["~> 2.4", ">= 2.4.9"])
62
+ s.add_development_dependency(%q<simplecov>.freeze, ["= 0.16.1"])
63
+ s.add_development_dependency(%q<pry-byebug>.freeze, ["~> 3.6"])
64
+ s.add_development_dependency(%q<faraday-detailed_logger>.freeze, ["~> 2.1", ">= 2.1.2"])
65
+ else
66
+ s.add_dependency(%q<base32>.freeze, ["~> 0.3.2"])
67
+ s.add_dependency(%q<rotp>.freeze, ["~> 3.3", ">= 3.3.1"])
68
+ s.add_dependency(%q<faraday>.freeze, [">= 0.12.2", "< 1.0"])
69
+ s.add_dependency(%q<json>.freeze, ["~> 2.1"])
70
+ s.add_dependency(%q<byebug>.freeze, [">= 0"])
71
+ s.add_dependency(%q<rspec>.freeze, [">= 0"])
72
+ s.add_dependency(%q<shoulda>.freeze, [">= 0"])
73
+ s.add_dependency(%q<webmock>.freeze, [">= 0"])
74
+ s.add_dependency(%q<rdoc>.freeze, ["~> 6.0", ">= 6.0.4"])
75
+ s.add_dependency(%q<bundler>.freeze, ["~> 1.16", ">= 1.16.2"])
76
+ s.add_dependency(%q<juwelier>.freeze, ["~> 2.4", ">= 2.4.9"])
77
+ s.add_dependency(%q<simplecov>.freeze, ["= 0.16.1"])
78
+ s.add_dependency(%q<pry-byebug>.freeze, ["~> 3.6"])
79
+ s.add_dependency(%q<faraday-detailed_logger>.freeze, ["~> 2.1", ">= 2.1.2"])
80
+ end
81
+ else
82
+ s.add_dependency(%q<base32>.freeze, ["~> 0.3.2"])
83
+ s.add_dependency(%q<rotp>.freeze, ["~> 3.3", ">= 3.3.1"])
84
+ s.add_dependency(%q<faraday>.freeze, [">= 0.12.2", "< 1.0"])
85
+ s.add_dependency(%q<json>.freeze, ["~> 2.1"])
86
+ s.add_dependency(%q<byebug>.freeze, [">= 0"])
87
+ s.add_dependency(%q<rspec>.freeze, [">= 0"])
88
+ s.add_dependency(%q<shoulda>.freeze, [">= 0"])
89
+ s.add_dependency(%q<webmock>.freeze, [">= 0"])
90
+ s.add_dependency(%q<rdoc>.freeze, ["~> 6.0", ">= 6.0.4"])
91
+ s.add_dependency(%q<bundler>.freeze, ["~> 1.16", ">= 1.16.2"])
92
+ s.add_dependency(%q<juwelier>.freeze, ["~> 2.4", ">= 2.4.9"])
93
+ s.add_dependency(%q<simplecov>.freeze, ["= 0.16.1"])
94
+ s.add_dependency(%q<pry-byebug>.freeze, ["~> 3.6"])
95
+ s.add_dependency(%q<faraday-detailed_logger>.freeze, ["~> 2.1", ">= 2.1.2"])
96
+ end
97
+ end
98
+
@@ -0,0 +1,80 @@
1
+ $LOAD_PATH << File.join(File.dirname(__FILE__), 'lib')
2
+
3
+ require 'campact_user_service'
4
+ require 'faraday/detailed_logger'
5
+
6
+ def instrument_connection_with_extended_logging(client, username, password)
7
+ default_options = {
8
+ ssl: { verify: true },
9
+ headers: {
10
+ 'Accept' => "application/json;q=0.1",
11
+ 'Accept-Charset' => "utf-8",
12
+ 'User-Agent' => 'campact_user_service'
13
+ }
14
+ }
15
+
16
+ faraday_builder = ->(faraday) do
17
+ faraday.response :detailed_logger
18
+ faraday.basic_auth username, password
19
+ faraday.adapter Faraday.default_adapter
20
+ end
21
+
22
+ instrumented_connection = Faraday.new(
23
+ "#{client.scheme}://#{client.host}:#{client.port}",
24
+ default_options,
25
+ &faraday_builder
26
+ )
27
+ client.instance_variable_set(:@connection, instrumented_connection)
28
+ end
29
+
30
+ # Pick which API to connect to
31
+ # 1 for session
32
+ # 2 for user
33
+ puts "Which user service are you going to use?\n\t1) session\n\t2) user"
34
+ option = gets.chomp
35
+
36
+ # Get username
37
+ puts "I'll need your API credentials"
38
+ puts "Enter your API username"
39
+ username = gets.chomp
40
+
41
+ # Get password
42
+ puts "I'll need your password now"
43
+ password = gets.chomp
44
+
45
+ # Now connect to the right API
46
+ user_service = case option
47
+ when '1'
48
+ puts "Now I'll need a session token"
49
+ token = gets.chomp
50
+ session = CampactUserService.session(
51
+ token,
52
+ 'campact-demo-session',
53
+ {
54
+ scheme: 'http',
55
+ host: 'demo.campact.de',
56
+ port: '10004'
57
+ }
58
+ )
59
+ when '2'
60
+ puts "I'll need a user ID (email address). In practice I won't need this here because it can be derived through the session token"
61
+ user_id = gets.chomp
62
+ account = CampactUserService.account(
63
+ user_id,
64
+ {
65
+ scheme: 'http',
66
+ host: 'demo.campact.de',
67
+ port: '10003'
68
+ }
69
+ )
70
+ else
71
+ raise 'Invalid option'
72
+ end
73
+
74
+ instrument_connection_with_extended_logging(user_service.client, username, password)
75
+
76
+ puts "Waiting for your command..."
77
+ require 'pry-byebug'
78
+ binding.pry
79
+
80
+ puts 'Goodbye!'
@@ -0,0 +1,17 @@
1
+ require 'campact_user_service/client'
2
+ require 'campact_user_service/session'
3
+ require 'campact_user_service/account'
4
+
5
+ module CampactUserService
6
+ class << self
7
+ def session(session_id, session_cookie_name, options)
8
+ client = CampactUserService::Client.new(options)
9
+ CampactUserService::Session.new(client, session_id, session_cookie_name)
10
+ end
11
+
12
+ def account(user_id, options)
13
+ client = CampactUserService::Client.new(options)
14
+ CampactUserService::Account.new(client, user_id)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,51 @@
1
+ module CampactUserService
2
+ class Account
3
+ attr_reader :client, :user_id
4
+
5
+ def initialize(client, user_id)
6
+ @client = client
7
+ @user_id = user_id
8
+ end
9
+
10
+ def exists?
11
+ account && account["id"]
12
+ end
13
+
14
+ def subscribed_to_newsletter?
15
+ subscriptions = account.dig('emailaddress', 'subscriptions') || []
16
+ subscriptions.any? {|s| s['type'] == 'newsletter' }
17
+ end
18
+
19
+ def allow_prefill?
20
+ prefill = account.dig('preferences', 'prefill_forms')
21
+ prefill.to_s == 'allowed'
22
+ end
23
+
24
+ def name
25
+ account['name']
26
+ end
27
+
28
+ def email
29
+ account.dig('emailaddress', 'emailaddress')
30
+ end
31
+
32
+ def address
33
+ account['postaladdress']
34
+ end
35
+
36
+ def preferences
37
+ account['preferences']
38
+ end
39
+
40
+ def donor_info
41
+ account['donorclass']
42
+ end
43
+
44
+ private
45
+
46
+ def account
47
+ escaped_user_id = CGI.escape(user_id)
48
+ @account_info ||= (client.get_request("api/v1/accounts/#{escaped_user_id}") || {})
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,90 @@
1
+ require 'faraday'
2
+ require 'json'
3
+ require 'base32'
4
+ require 'rotp'
5
+ require 'campact_user_service/response_error'
6
+
7
+ module CampactUserService
8
+ class Client
9
+ TIMEOUT = 5.freeze
10
+
11
+ attr_reader :connection, :scheme, :host, :port
12
+
13
+ def initialize(options)
14
+ @scheme = options.fetch(:scheme, 'https')
15
+ @host = options.fetch(:host)
16
+ @port = options[:port]
17
+ faraday_options = default_faraday_options.merge(options.delete(:faraday) || {})
18
+ adapter = faraday_options.delete(:adapter) || Faraday.default_adapter
19
+
20
+ @connection = Faraday.new(endpoint, faraday_options) do |faraday|
21
+ if options.has_key?(:enable_auth)
22
+ faraday.basic_auth options[:username], options[:password]
23
+ end
24
+ faraday.adapter adapter
25
+ end
26
+ end
27
+
28
+ %i(get delete).each do |verb|
29
+ define_method("#{verb}_request") do |path, options={}|
30
+ request(verb, path, options)
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def request(verb, path, options)
37
+ response = connection.send(verb.to_sym) do |req|
38
+ req.url path
39
+ req.options.timeout = TIMEOUT
40
+ req.options.open_timeout = TIMEOUT
41
+ if options.has_key?(:cookies)
42
+ req.headers['Cookie'] = format_cookies(options[:cookies])
43
+ end
44
+ end
45
+
46
+ case response.status
47
+ when 200
48
+ body = (response.body.nil? || response.body == '') ? '{ }' : response.body
49
+ JSON.parse(body)
50
+ when 201..299
51
+ true
52
+ when 404
53
+ nil
54
+ when 300..599
55
+ raise ResponseError.new(response.status, response.body)
56
+ else
57
+ nil
58
+ end
59
+ end
60
+
61
+ def default_faraday_options
62
+ {
63
+ ssl: { verify: true },
64
+ headers: {
65
+ 'Accept' => "application/json;q=0.1",
66
+ 'Accept-Charset' => "utf-8",
67
+ 'User-Agent' => 'campact_user_service'
68
+ }
69
+ }
70
+ end
71
+
72
+ def endpoint
73
+ endpoint = "#{scheme}://#{host}"
74
+ if !port.nil?
75
+ endpoint << ":#{port}"
76
+ end
77
+
78
+ endpoint
79
+ end
80
+
81
+ def format_cookies(cookies)
82
+ case cookies
83
+ when String
84
+ cookies
85
+ when Hash
86
+ cookies.map {|k,v| "#{k}=#{v};" }.join
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,11 @@
1
+ module CampactUserService
2
+ class ResponseError < StandardError
3
+ attr_reader :status_code, :body
4
+
5
+ def initialize(status_code, body)
6
+ super("Response error. Status code: #{status_code} - Body: #{body}")
7
+ @status_code = status_code
8
+ @body = body
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,37 @@
1
+ module CampactUserService
2
+ class Session
3
+ attr_reader :client, :session_cookie_name, :session_id
4
+
5
+ def initialize(client, session_id, session_cookie_name)
6
+ @client = client
7
+ @session_id = session_id
8
+ @session_cookie_name = session_cookie_name
9
+ end
10
+
11
+ def exists?
12
+ session && session['id']
13
+ end
14
+
15
+ def user_id
16
+ session["user_id"]
17
+ end
18
+
19
+ def has_soft_login_session?
20
+ session["permission_level"] == 'limited'
21
+ end
22
+
23
+ def has_hard_login_session?
24
+ session["permission_level"] == 'full'
25
+ end
26
+
27
+ def destroy
28
+ client.delete_request('/v1/sessions', cookies: {session_cookie_name => session_id})
29
+ end
30
+
31
+ private
32
+
33
+ def session
34
+ @session_info ||= (client.get_request('/v1/sessions', cookies: {session_cookie_name => session_id}) || {})
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,198 @@
1
+ require 'spec_helper'
2
+
3
+ describe CampactUserService::Account do
4
+ let(:client) { CampactUserService::Client.new(host: 'test.com') }
5
+ let(:session_id) { '123456abcdef' }
6
+ let(:session_cookie_name) { 'cus-session' }
7
+ let(:user_id) { 'test@example.org' }
8
+
9
+ subject {
10
+ CampactUserService::Account.new(client, user_id)
11
+ }
12
+
13
+ before(:each) do
14
+ WebMock.disable_net_connect!
15
+ end
16
+
17
+ describe '#exists?' do
18
+ it 'should return true where a valid user object is returned' do
19
+ stub_request(:get, "https://test.com/api/v1/accounts/#{user_id}")
20
+ .to_return(body: {
21
+ "id": "id-123"
22
+ }.to_json)
23
+
24
+ expect(subject.exists?).to be_truthy
25
+ end
26
+
27
+ it 'should not return true where an invalid user object is returned' do
28
+ stub_request(:get, "https://test.com/api/v1/accounts/#{user_id}")
29
+ .to_return(body: {
30
+ "error": true
31
+ }.to_json)
32
+
33
+ expect(subject.exists?).to be_falsey
34
+ end
35
+ end
36
+
37
+ describe 'URI escaping' do
38
+ it 'should escape the user ID in any requests' do
39
+ malicious_user_id = "\11\15"
40
+ malicious_subject = CampactUserService::Account.new(
41
+ client,
42
+ malicious_user_id
43
+ )
44
+ stub_request(:get, "https://test.com/api/v1/accounts/%09%0D")
45
+ .to_return(body: '', status: 404)
46
+
47
+ expect(malicious_subject.exists?).to be_falsey
48
+ end
49
+
50
+ it 'should escape "/" characters' do
51
+ malicious_user_id = "foo@example.com/secrets"
52
+ malicious_subject = CampactUserService::Account.new(
53
+ client,
54
+ malicious_user_id
55
+ )
56
+ stub_request(:get, "https://test.com/api/v1/accounts/foo@example.com%2Fsecrets")
57
+ .to_return(body: '', status: 404)
58
+
59
+ expect(malicious_subject.exists?).to be_falsey
60
+ end
61
+ end
62
+
63
+ describe '#name' do
64
+ it 'should retrieve all of the names and gender of the user' do
65
+ stub_request(:get, "https://test.com/api/v1/accounts/#{user_id}")
66
+ .to_return(body: {
67
+ "name": {
68
+ "firstname": "Alice",
69
+ "lastname": "Wu",
70
+ "gender": "female",
71
+ "title": "PhD",
72
+ "fullname": "PhD Alice Wu"
73
+ },
74
+ }.to_json)
75
+
76
+ name = subject.name
77
+ expect(name['firstname']).to eq 'Alice'
78
+ expect(name['lastname']).to eq 'Wu'
79
+ expect(name['gender']).to eq 'female'
80
+ expect(name['title']).to eq 'PhD'
81
+ expect(name['fullname']).to eq 'PhD Alice Wu'
82
+ end
83
+ end
84
+
85
+ describe '#email' do
86
+ it 'should retrieve the email address of the user' do
87
+ stub_request(:get, "https://test.com/api/v1/accounts/#{user_id}")
88
+ .to_return(body: {
89
+ "emailaddress": {
90
+ "emailaddress": "foobar@example.com"
91
+ }
92
+ }.to_json)
93
+
94
+ email = subject.email
95
+ expect(email).to eq 'foobar@example.com'
96
+ end
97
+
98
+ it 'should return nil where no email address is set' do
99
+ stub_request(:get, "https://test.com/api/v1/accounts/#{user_id}")
100
+ .to_return(body: {
101
+ "emailaddress": {}
102
+ }.to_json)
103
+
104
+ email = subject.email
105
+ expect(email).to be_nil
106
+ end
107
+ end
108
+
109
+ describe '#address' do
110
+ it 'should retrieve the address of the user' do
111
+ stub_request(:get, "https://test.com/api/v1/accounts/#{user_id}")
112
+ .to_return(body: {
113
+ "postaladdress": {
114
+ "street": "123 Fake Street",
115
+ "postalcode": "E1234",
116
+ "locality": "London",
117
+ "countrycode": "GB"
118
+ }
119
+ }.to_json)
120
+
121
+ address = subject.address
122
+ expect(address['street']).to eq '123 Fake Street'
123
+ end
124
+ end
125
+
126
+ describe '#subscribed_to_newsletter?' do
127
+ it 'should be true where the user is subscribed' do
128
+ stub_request(:get, "https://test.com/api/v1/accounts/#{user_id}")
129
+ .to_return(body: {
130
+ "emailaddress": {
131
+ "emailaddress": "foobar@example.com",
132
+ "subscriptions": [{"type": "newsletter"}]
133
+ }
134
+ }.to_json)
135
+
136
+ expect(subject.subscribed_to_newsletter?).to be_truthy
137
+ end
138
+
139
+ it 'should be false where the user is not subscribed' do
140
+ stub_request(:get, "https://test.com/api/v1/accounts/#{user_id}")
141
+ .to_return(body: {
142
+ "emailaddress": {
143
+ "emailaddress": "foobar@example.com",
144
+ "subscriptions": []
145
+ }
146
+ }.to_json)
147
+
148
+ expect(subject.subscribed_to_newsletter?).to be_falsey
149
+ end
150
+
151
+ it 'should be false where user subscription is different than newsletter' do
152
+ stub_request(:get, "https://test.com/api/v1/accounts/#{user_id}")
153
+ .to_return(body: {
154
+ "emailaddress": {
155
+ "emailaddress": "foobar@example.com",
156
+ "subscriptions": [{"type": "foo"}]
157
+ }
158
+ }.to_json)
159
+
160
+ expect(subject.subscribed_to_newsletter?).to be_falsey
161
+ end
162
+ end
163
+
164
+ describe '#allow_prefill?' do
165
+ it 'should allow prefilling where the user has opted-in' do
166
+ stub_request(:get, "https://test.com/api/v1/accounts/#{user_id}")
167
+ .to_return(body: {
168
+ "preferences": {
169
+ "prefill_forms": "allowed"
170
+ }
171
+ }.to_json)
172
+
173
+ expect(subject.allow_prefill?).to be_truthy
174
+ end
175
+
176
+ it 'should not allow prefilling where the user has not decided' do
177
+ stub_request(:get, "https://test.com/api/v1/accounts/#{user_id}")
178
+ .to_return(body: {
179
+ "preferences": {
180
+ "prefill_forms": "undecided"
181
+ }
182
+ }.to_json)
183
+
184
+ expect(subject.allow_prefill?).to be_falsey
185
+ end
186
+
187
+ it 'should not allow prefilling where the user has opted out' do
188
+ stub_request(:get, "https://test.com/api/v1/accounts/#{user_id}")
189
+ .to_return(body: {
190
+ "preferences": {
191
+ "prefill_forms": "disallowed"
192
+ }
193
+ }.to_json)
194
+
195
+ expect(subject.allow_prefill?).to be_falsey
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe CampactUserService do
4
+ subject { CampactUserService }
5
+
6
+ describe '.session' do
7
+ it 'should create Session instance with correct arguments' do
8
+ options = { foo: 'bar', foo2: 'bar2' }
9
+ client = double
10
+ expect(CampactUserService::Client).to receive(:new).with(options).and_return(client)
11
+ session_id = '123abcd'
12
+ session_cookie_name = 'campact-session'
13
+ session_api = double
14
+ expect(CampactUserService::Session).to receive(:new).with(client, session_id, session_cookie_name).and_return(session_api)
15
+
16
+ session = subject.session(session_id, session_cookie_name, options)
17
+
18
+ expect(session).to be(session_api)
19
+ end
20
+ end
21
+
22
+ describe '.account' do
23
+ it 'should create Account instance with correct arguments' do
24
+ options = { foo: 'bar', foo2: 'bar2' }
25
+ client = double
26
+ expect(CampactUserService::Client).to receive(:new).with(options).and_return(client)
27
+ user_id = 'test@example.com'
28
+ account_api = double
29
+ expect(CampactUserService::Account).to receive(:new).with(client, user_id).and_return(account_api)
30
+
31
+ account = subject.account(user_id, options)
32
+
33
+ expect(account).to be(account_api)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,161 @@
1
+ require 'spec_helper'
2
+
3
+ describe CampactUserService::Client do
4
+ describe 'initialization' do
5
+ let(:options) {
6
+ {
7
+ scheme: 'https',
8
+ host: 'demo.campact.de',
9
+ port: '10003',
10
+ faraday: { a_custom_option: 'foo', adapter: :an_adapter }
11
+ }
12
+ }
13
+ let(:faraday_builder) { double(adapter: true) }
14
+
15
+ it 'should initialize connection from options' do
16
+ expect(faraday_builder).to receive(:adapter).with(:an_adapter)
17
+
18
+ expect(Faraday).to receive(:new) do |endpoint, initialization_options|
19
+ expect(endpoint).to eq 'https://demo.campact.de:10003'
20
+ expect(initialization_options[:a_custom_option]).to eq 'foo'
21
+ end.and_yield(faraday_builder)
22
+
23
+ CampactUserService::Client.new(options)
24
+ end
25
+
26
+ it 'should use default faraday options for connection' do
27
+ expect(Faraday).to receive(:new) do |endpoint, initialization_options|
28
+ expect(initialization_options[:ssl][:verify]).to be_truthy
29
+ expect(initialization_options[:headers]['Accept']).to eq "application/json;q=0.1"
30
+ expect(initialization_options[:headers]['Accept-Charset']).to eq "utf-8"
31
+ expect(initialization_options[:headers]['User-Agent']).to eq 'campact_user_service'
32
+ end.and_yield(faraday_builder)
33
+
34
+ CampactUserService::Client.new(options)
35
+ end
36
+
37
+ it 'should allow overriding default options' do
38
+ options[:faraday][:ssl] = {verify: false}
39
+ options[:faraday][:headers] = {'Accept'=>'custom-accept-header', 'Accept-Charset'=>'custom-accept-charset-header', 'User-Agent'=>'custom-user-agent-header'}
40
+
41
+ expect(Faraday).to receive(:new) do |endpoint, initialization_options|
42
+ expect(initialization_options[:ssl][:verify]).to be_falsey
43
+ expect(initialization_options[:headers]['Accept']).to eq 'custom-accept-header'
44
+ expect(initialization_options[:headers]['Accept-Charset']).to eq 'custom-accept-charset-header'
45
+ expect(initialization_options[:headers]['User-Agent']).to eq 'custom-user-agent-header'
46
+ end.and_yield(faraday_builder)
47
+
48
+ CampactUserService::Client.new(options)
49
+ end
50
+ end
51
+
52
+ shared_examples 'error handling' do
53
+ [310, 410, 510].each do |unexpected_status_code|
54
+ it 'should raise error if status code is 3xx, 4xx or 5xx' do
55
+ expect(request_builder).to receive(:url).with('/foo/bar')
56
+ expect(request_builder_options).to receive(:timeout=).with(CampactUserService::Client::TIMEOUT)
57
+ expect(request_builder_options).to receive(:open_timeout=).with(CampactUserService::Client::TIMEOUT)
58
+ allow(response).to receive(:status).and_return(unexpected_status_code)
59
+ allow(response).to receive(:body).and_return('An error occurred')
60
+
61
+ expect { subject.send(method_under_test, '/foo/bar') }.to raise_error do |error|
62
+ expect(error).to be_an_instance_of CampactUserService::ResponseError
63
+ expect(error.status_code).to eq(unexpected_status_code)
64
+ expect(error.body).to eq('An error occurred')
65
+ end
66
+ end
67
+ end
68
+
69
+ it 'should return nil if status code is 404' do
70
+ expect(request_builder).to receive(:url).with('/foo/bar')
71
+ expect(request_builder_options).to receive(:timeout=).with(CampactUserService::Client::TIMEOUT)
72
+ expect(request_builder_options).to receive(:open_timeout=).with(CampactUserService::Client::TIMEOUT)
73
+ allow(response).to receive(:status).and_return(404)
74
+
75
+ response = subject.send(method_under_test, '/foo/bar')
76
+
77
+ expect(response).to be_nil
78
+ end
79
+ end
80
+
81
+ describe '#get_request' do
82
+ let(:connection) { double }
83
+ let(:request_builder) { double }
84
+ let(:request_builder_options) { double }
85
+ let(:response) { double(body: '') }
86
+ let(:method_under_test) { :get_request }
87
+
88
+ subject { CampactUserService::Client.new(host: 'demo.campact.de') }
89
+
90
+ before :each do
91
+ expect(Faraday).to receive(:new).and_yield(double(adapter: true)).and_return(connection)
92
+ expect(request_builder).to receive(:options).at_least(:once).and_return(request_builder_options)
93
+ expect(connection).to receive(:get).and_yield(request_builder).and_return(response)
94
+ end
95
+
96
+ include_examples 'error handling'
97
+
98
+ it 'should perform get request on provided path and return true if response is 204' do
99
+ expect(request_builder).to receive(:url).with('/foo/bar')
100
+ expect(request_builder_options).to receive(:timeout=).with(CampactUserService::Client::TIMEOUT)
101
+ expect(request_builder_options).to receive(:open_timeout=).with(CampactUserService::Client::TIMEOUT)
102
+ allow(response).to receive(:status).and_return(204)
103
+
104
+ expect(subject.get_request('/foo/bar')).to be_truthy
105
+ end
106
+
107
+ context 'stubbed request builder' do
108
+ let(:headers_builder) { double }
109
+
110
+ before :each do
111
+ allow(request_builder).to receive(:url).with('/foo/bar')
112
+ allow(request_builder_options).to receive(:timeout=).with(CampactUserService::Client::TIMEOUT)
113
+ allow(request_builder_options).to receive(:open_timeout=).with(CampactUserService::Client::TIMEOUT)
114
+ allow(request_builder).to receive(:headers).and_return(headers_builder)
115
+ end
116
+
117
+ it 'should set cookies sent as string' do
118
+ expect(headers_builder).to receive(:[]=).with('Cookie', 'foo=bar;xyz=abc')
119
+ allow(response).to receive(:status).and_return(200)
120
+
121
+ subject.get_request('/foo/bar', cookies: 'foo=bar;xyz=abc')
122
+ end
123
+
124
+ it 'should set cookies sent as hash' do
125
+ expect(headers_builder).to receive(:[]=).with('Cookie', 'foo=bar;xyz=abc;')
126
+ allow(response).to receive(:status).and_return(200)
127
+
128
+ subject.get_request('/foo/bar', cookies: {'foo' => 'bar', 'xyz' => 'abc'})
129
+ end
130
+
131
+ it 'should parse JSON response on successful response' do
132
+ allow(response).to receive(:status).and_return(200)
133
+ allow(response).to receive(:body).and_return({a_field: 'foo', another_field: 'bar'}.to_json)
134
+
135
+ response = subject.get_request('/foo/bar')
136
+
137
+ expect(response).not_to be_nil
138
+ expect(response['a_field']).to eq 'foo'
139
+ expect(response['another_field']).to eq 'bar'
140
+ end
141
+ end
142
+ end
143
+
144
+ describe '#delete_request' do
145
+ let(:connection) { double }
146
+ let(:request_builder) { double }
147
+ let(:request_builder_options) { double }
148
+ let(:response) { double(body: '') }
149
+ let(:method_under_test) { :delete_request }
150
+
151
+ subject { CampactUserService::Client.new(host: 'demo.campact.de') }
152
+
153
+ before :each do
154
+ expect(Faraday).to receive(:new).and_yield(double(adapter: true)).and_return(connection)
155
+ expect(request_builder).to receive(:options).at_least(:once).and_return(request_builder_options)
156
+ expect(connection).to receive(:delete).and_yield(request_builder).and_return(response)
157
+ end
158
+
159
+ include_examples 'error handling'
160
+ end
161
+ end
@@ -0,0 +1,83 @@
1
+ require 'spec_helper'
2
+
3
+ describe CampactUserService::Session do
4
+ let(:client) { CampactUserService::Client.new(host: 'test.com') }
5
+ let(:session_id) { '123456abcdef' }
6
+ let(:session_cookie_name) { 'cus-session' }
7
+
8
+ subject { CampactUserService::Session.new(client, session_id, session_cookie_name) }
9
+
10
+ describe '#exists?' do
11
+ it 'should be truthy when the session exists' do
12
+ stub_request(:get, 'https://test.com/v1/sessions')
13
+ .with(headers: {'Cookie' => "cus-session=#{session_id};"})
14
+ .to_return(body: {id: 'session-id'}.to_json)
15
+
16
+ expect(subject.exists?).to be_truthy
17
+ end
18
+
19
+ it "should be falsey when the session doesn't exist" do
20
+ stub_request(:get, 'https://test.com/v1/sessions')
21
+ .with(headers: {'Cookie' => "cus-session=#{session_id};"})
22
+ .to_return(body: '', status: 404)
23
+
24
+ expect(subject.exists?).to be_falsey
25
+ end
26
+ end
27
+
28
+ describe '#user_id' do
29
+ it 'should be present' do
30
+ stub_request(:get, 'https://test.com/v1/sessions')
31
+ .with(headers: {'Cookie' => "cus-session=#{session_id};"})
32
+ .to_return(body: {user_id: 'user@example.org'}.to_json)
33
+
34
+ expect(subject.user_id).to eq('user@example.org')
35
+ end
36
+ end
37
+
38
+ describe '#has_soft_login_session?' do
39
+ it 'should be true where permission level is limited' do
40
+ stub_request(:get, 'https://test.com/v1/sessions')
41
+ .with(headers: {'Cookie' => "cus-session=#{session_id};"})
42
+ .to_return(body: {permission_level: 'limited'}.to_json)
43
+
44
+ expect(subject.has_soft_login_session?).to be_truthy
45
+ end
46
+
47
+ it 'should be false where permission level is full' do
48
+ stub_request(:get, 'https://test.com/v1/sessions')
49
+ .with(headers: {'Cookie' => "cus-session=#{session_id};"})
50
+ .to_return(body: {permission_level: 'full'}.to_json)
51
+
52
+ expect(subject.has_soft_login_session?).to be_falsey
53
+ end
54
+ end
55
+
56
+ describe '#has_hard_login_session?' do
57
+ it 'should be true where permission level is full' do
58
+ stub_request(:get, 'https://test.com/v1/sessions')
59
+ .with(headers: {'Cookie' => "cus-session=#{session_id};"})
60
+ .to_return(body: {permission_level: 'full'}.to_json)
61
+
62
+ expect(subject.has_hard_login_session?).to be_truthy
63
+ end
64
+
65
+ it 'should be false where permission level is limited' do
66
+ stub_request(:get, 'https://test.com/v1/sessions')
67
+ .with(headers: {'Cookie' => "cus-session=#{session_id};"})
68
+ .to_return(body: {permission_level: 'limited'}.to_json)
69
+
70
+ expect(subject.has_hard_login_session?).to be_falsey
71
+ end
72
+ end
73
+
74
+ describe '#destroy' do
75
+ it 'should perform DELETE request' do
76
+ stub_request(:delete, 'https://test.com/v1/sessions')
77
+ .with(headers: {'Cookie' => "cus-session=#{session_id};"})
78
+ .to_return(body: '', status: 204)
79
+
80
+ expect(subject.destroy).to be_truthy
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,18 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'webmock/rspec'
5
+ require 'campact_user_service'
6
+
7
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
8
+
9
+ RSpec.configure do |config|
10
+ config.include WebMock::API
11
+
12
+ config.before(:each) do
13
+ WebMock.reset!
14
+ end
15
+ config.after(:each) do
16
+ WebMock.reset!
17
+ end
18
+ end
metadata ADDED
@@ -0,0 +1,295 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: campact_user_service
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.1
5
+ platform: ruby
6
+ authors:
7
+ - ControlShift
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-08-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: base32
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.3.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.3.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: rotp
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.3'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 3.3.1
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '3.3'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 3.3.1
47
+ - !ruby/object:Gem::Dependency
48
+ name: faraday
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 0.12.2
54
+ - - "<"
55
+ - !ruby/object:Gem::Version
56
+ version: '1.0'
57
+ type: :runtime
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 0.12.2
64
+ - - "<"
65
+ - !ruby/object:Gem::Version
66
+ version: '1.0'
67
+ - !ruby/object:Gem::Dependency
68
+ name: json
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '2.1'
74
+ type: :runtime
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '2.1'
81
+ - !ruby/object:Gem::Dependency
82
+ name: byebug
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ - !ruby/object:Gem::Dependency
96
+ name: rspec
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ - !ruby/object:Gem::Dependency
110
+ name: shoulda
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ type: :development
117
+ prerelease: false
118
+ version_requirements: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ - !ruby/object:Gem::Dependency
124
+ name: webmock
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ - !ruby/object:Gem::Dependency
138
+ name: rdoc
139
+ requirement: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - "~>"
142
+ - !ruby/object:Gem::Version
143
+ version: '6.0'
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: 6.0.4
147
+ type: :development
148
+ prerelease: false
149
+ version_requirements: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - "~>"
152
+ - !ruby/object:Gem::Version
153
+ version: '6.0'
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ version: 6.0.4
157
+ - !ruby/object:Gem::Dependency
158
+ name: bundler
159
+ requirement: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - "~>"
162
+ - !ruby/object:Gem::Version
163
+ version: '1.16'
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: 1.16.2
167
+ type: :development
168
+ prerelease: false
169
+ version_requirements: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '1.16'
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ version: 1.16.2
177
+ - !ruby/object:Gem::Dependency
178
+ name: juwelier
179
+ requirement: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - "~>"
182
+ - !ruby/object:Gem::Version
183
+ version: '2.4'
184
+ - - ">="
185
+ - !ruby/object:Gem::Version
186
+ version: 2.4.9
187
+ type: :development
188
+ prerelease: false
189
+ version_requirements: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - "~>"
192
+ - !ruby/object:Gem::Version
193
+ version: '2.4'
194
+ - - ">="
195
+ - !ruby/object:Gem::Version
196
+ version: 2.4.9
197
+ - !ruby/object:Gem::Dependency
198
+ name: simplecov
199
+ requirement: !ruby/object:Gem::Requirement
200
+ requirements:
201
+ - - '='
202
+ - !ruby/object:Gem::Version
203
+ version: 0.16.1
204
+ type: :development
205
+ prerelease: false
206
+ version_requirements: !ruby/object:Gem::Requirement
207
+ requirements:
208
+ - - '='
209
+ - !ruby/object:Gem::Version
210
+ version: 0.16.1
211
+ - !ruby/object:Gem::Dependency
212
+ name: pry-byebug
213
+ requirement: !ruby/object:Gem::Requirement
214
+ requirements:
215
+ - - "~>"
216
+ - !ruby/object:Gem::Version
217
+ version: '3.6'
218
+ type: :development
219
+ prerelease: false
220
+ version_requirements: !ruby/object:Gem::Requirement
221
+ requirements:
222
+ - - "~>"
223
+ - !ruby/object:Gem::Version
224
+ version: '3.6'
225
+ - !ruby/object:Gem::Dependency
226
+ name: faraday-detailed_logger
227
+ requirement: !ruby/object:Gem::Requirement
228
+ requirements:
229
+ - - "~>"
230
+ - !ruby/object:Gem::Version
231
+ version: '2.1'
232
+ - - ">="
233
+ - !ruby/object:Gem::Version
234
+ version: 2.1.2
235
+ type: :development
236
+ prerelease: false
237
+ version_requirements: !ruby/object:Gem::Requirement
238
+ requirements:
239
+ - - "~>"
240
+ - !ruby/object:Gem::Version
241
+ version: '2.1'
242
+ - - ">="
243
+ - !ruby/object:Gem::Version
244
+ version: 2.1.2
245
+ description: Ruby wrapper for Campact User Service
246
+ email: hello@controlshiftlabs.com
247
+ executables: []
248
+ extensions: []
249
+ extra_rdoc_files:
250
+ - LICENSE
251
+ - README.md
252
+ files:
253
+ - ".ruby-gemset"
254
+ - ".ruby-version"
255
+ - Gemfile
256
+ - LICENSE
257
+ - README.md
258
+ - Rakefile
259
+ - VERSION
260
+ - campact_user_service.gemspec
261
+ - example.rb
262
+ - lib/campact_user_service.rb
263
+ - lib/campact_user_service/account.rb
264
+ - lib/campact_user_service/client.rb
265
+ - lib/campact_user_service/response_error.rb
266
+ - lib/campact_user_service/session.rb
267
+ - spec/account_spec.rb
268
+ - spec/campact_user_service_spec.rb
269
+ - spec/client_spec.rb
270
+ - spec/session_spec.rb
271
+ - spec/spec_helper.rb
272
+ homepage: http://github.com/controlshift/campact_user_service
273
+ licenses:
274
+ - MIT
275
+ metadata: {}
276
+ post_install_message:
277
+ rdoc_options: []
278
+ require_paths:
279
+ - lib
280
+ required_ruby_version: !ruby/object:Gem::Requirement
281
+ requirements:
282
+ - - ">="
283
+ - !ruby/object:Gem::Version
284
+ version: '0'
285
+ required_rubygems_version: !ruby/object:Gem::Requirement
286
+ requirements:
287
+ - - ">="
288
+ - !ruby/object:Gem::Version
289
+ version: '0'
290
+ requirements: []
291
+ rubygems_version: 3.0.3
292
+ signing_key:
293
+ specification_version: 4
294
+ summary: Ruby wrapper for Campact User Service
295
+ test_files: []