crowdskout 0.0.4

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.
@@ -0,0 +1,22 @@
1
+ # Copyright (c) 2016 Kyle Schutt. All rights reserved.require
2
+
3
+ module Crowdskout
4
+ module Components
5
+ class Name < Component
6
+ attr_accessor :id, :FullName, :NameTitle, :FirstName, :MiddleName, :LastName, :NameSuffix
7
+
8
+ # Factory method to create an Address object from a json string
9
+ # @param [Hash] props - properties to create object from
10
+ # @return [Address]
11
+ def self.create(props)
12
+ obj = Name.new
13
+ if props
14
+ props.each do |key, value|
15
+ obj.send("#{key}=", value) if obj.respond_to? key
16
+ end
17
+ end
18
+ obj
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,44 @@
1
+ module Crowdskout
2
+ module Components
3
+ class Profile < Component
4
+ attr_accessor :id, :names, :genders
5
+
6
+ def self.create(props)
7
+ obj = Profile.new
8
+ if props
9
+ props.each do |key, value|
10
+ if key.downcase == 'names'
11
+ if value
12
+ obj.names = []
13
+ value.each do |name|
14
+ obj.names << Components::Name.create(name)
15
+ end
16
+ end
17
+ elsif key.downcase == 'genders'
18
+ if value
19
+ obj.genders = []
20
+ value.each do |gender|
21
+ obj.genders << Components::Gender.create(gender)
22
+ end
23
+ end
24
+ else
25
+ obj.send("#{key}=", value) if obj.respond_to? key
26
+ end
27
+ end
28
+ end
29
+ obj
30
+ end
31
+
32
+ def add_names(name)
33
+ @names = [] if @names.nil?
34
+ @names << name
35
+ end
36
+
37
+ def add_genders(gender)
38
+ @genders = [] if @genders.nil?
39
+ @genders << gender
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,15 @@
1
+ #
2
+ # result_set.rb
3
+ #
4
+ # Copyright (c) 2016 Kyle Schutt. All rights reserved.
5
+
6
+ module Crowdskout
7
+ module Components
8
+ class ResultSet
9
+ attr_accessor :results, :next
10
+ def initialize(results, meta)
11
+ @results = results
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ module Crowdskout
2
+ module Exceptions
3
+ class OAuth2Exception < Exception; end
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Crowdskout
2
+ module Exceptions
3
+ class ServiceException < Exception; end
4
+ end
5
+ end
@@ -0,0 +1,66 @@
1
+ module Crowdskout
2
+ module Services
3
+ class AttributeService < BaseService
4
+ class << self
5
+
6
+ # More info - http://docs.crowdskout.com/api/#get-all-attributes
7
+ # @param params - query parameters
8
+ # - limit - the number of attributes to limit
9
+ # - offset - the number of attributes to skip
10
+ def get_attributes(params = {})
11
+ url = Util::Config.get('endpoints.base_url') + Util::Config.get('endpoints.attributes')
12
+ url = build_url(url, params)
13
+
14
+ response = RestClient.get(url, get_headers())
15
+ body = JSON.parse(response.body)
16
+
17
+ attributes = []
18
+ body['data']['list'].each do |attribute|
19
+ attributes << Components::Attribute.create(attribute)
20
+ end if body['data']["total"] > 0
21
+
22
+ Components::ResultSet.new(attributes, body['messages'])
23
+ end
24
+
25
+ # more info - http://docs.crowdskout.com/api/#get-an-attribute-by-id
26
+ # @param [Integration] attribute_id - the id of the attribute to fetch
27
+ def get_attribute(attribute_id)
28
+ raise Exceptions::ServiceException, "Attribute ID is required." if attribute_id.nil?
29
+ url = Util::Config.get('endpoints.base_url') +
30
+ sprintf(Util::Config.get('endpoints.crud_attribute'), attribute_id)
31
+ url = build_url(url)
32
+ response = RestClient.get(url, get_headers())
33
+ Components::Attribute.create(JSON.parse(response.body)["data"])
34
+ end
35
+
36
+ # more info - http://docs.crowdskout.com/api/#create-an-attribute
37
+ def create_attribute(new_attribute)
38
+ raise Exceptions::ServiceException, "Attribute must not be nil" if new_attribute.nil?
39
+ url = Util::Config.get('endpoints.base_url') + Util::Config.get('endpoints.attribute')
40
+ url = build_url(url)
41
+ payload = new_attribute.to_json
42
+ response = RestClient.post(url, payload, get_headers())
43
+ Components::Attribute.create(JSON.parse(response.body)["data"])
44
+ end
45
+
46
+ # more info - http://docs.crowdskout.com/api/#update-an-attribute
47
+ def update_attribute(attribute_id, params = {})
48
+ raise Exceptions::ServiceException, "Attribute ID is required." if attribute_id.nil?
49
+ url = Util::Config.get('endpoints.base_url') + sprintf(Util::Config.get('endpoints.crud_attribute'), attribute_id)
50
+ url = build_url(url)
51
+ response = RestClient.put(url, params, get_headers())
52
+ Components::Attribute.create(JSON.parse(response.body)["data"])
53
+ end
54
+
55
+ # more info - http://docs.crowdskout.com/api/#delete-an-attribute
56
+ def delete_attribute(attribute_id)
57
+ raise Exceptions::ServiceException, "Attribute ID is required." if attribute_id.nil?
58
+ url = Util::Config.get('endpoints.base_url') + sprintf(Util::Config.get('endpoints.crud_attribute'), attribute_id)
59
+ url = build_url(url)
60
+ response = RestClient.delete(url, get_headers())
61
+ response.code == 204
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,50 @@
1
+ module Crowdskout
2
+ module Services
3
+ class BaseService
4
+ class << self
5
+ attr_accessor :api_key, :access_token
6
+
7
+ protected
8
+
9
+ # Return required headers for making an http request with Crowdskout
10
+ # @param [String] content_type - The MIME type of the body of the request, default is 'application/json'
11
+ # @return [Hash] - authorization headers
12
+ def get_headers(content_type = 'application/json')
13
+ {
14
+ :content_type => content_type,
15
+ :accept => 'application/json',
16
+ :authorization => "Bearer #{BaseService.access_token}",
17
+ :user_agent => "AppConnect Ruby SDK v#{Crowdskout::VERSION} (#{RUBY_DESCRIPTION})",
18
+ :x_ctct_request_source => "sdk.ruby.#{Crowdskout::VERSION}"
19
+ }
20
+ end
21
+
22
+ # Build a url from the base url and query parameters hash. Query parameters
23
+ # should not be URL encoded because this method will handle that
24
+ # @param [String] url - The base url
25
+ # @param [Hash] params - A hash with query parameters
26
+ # @return [String] - the url with query parameters hash
27
+ def build_url(url, params = nil)
28
+ if params.respond_to? :each
29
+ params.each do |key, value|
30
+ # Convert dates to CC date format
31
+ if value.respond_to? :iso8601
32
+ params[key] = value.iso8601
33
+ end
34
+
35
+ if key.to_s == 'next' && value.match(/^.*?next=(.*)$/)
36
+ params[key] = $1
37
+ end
38
+ end
39
+ else
40
+ params ||= {}
41
+ end
42
+
43
+ params['api_key'] = BaseService.api_key
44
+ url += '?' + Util::Helpers.http_build_query(params)
45
+ end
46
+
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,17 @@
1
+ module Crowdskout
2
+ module Services
3
+ class FieldService < BaseService
4
+ class << self
5
+ # more info - http://docs.crowdskout.com/api/#get-the-options-for-a-field
6
+ def get_options_for_a_field(field_name, params = {})
7
+ raise Exceptions::ServiceException, "Field name is required." if field_name.nil?
8
+ url = Util::Config.get('endpoints.base_url') +
9
+ sprintf(Util::Config.get('endpoints.fields_options'), field_name)
10
+ url = build_url(url, params)
11
+ response = RestClient.get(url, get_headers())
12
+ Components::FieldOptions.create(JSON.parse(response.body)["data"])
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,76 @@
1
+ module Crowdskout
2
+ module Services
3
+ class ProfileService < BaseService
4
+ class << self
5
+ # more info - http://docs.crowdskout.com/api/#get-a-profile-by-id
6
+ def get_profile(profile_id, collections)
7
+ raise Exceptions::ServiceException, "Profile ID is required." if profile_id.nil?
8
+ raise Exceptions::ServiceException, "A comma-deliminted list of collections is required." if collections.nil?
9
+ params = {
10
+ collections: collections
11
+ }
12
+ url = Util::Config.get('endpoints.base_url') +
13
+ sprintf(Util::Config.get('endpoints.crud_profile'), profile_id)
14
+ url = build_url(url, params)
15
+
16
+ response = RestClient.get(url, get_headers())
17
+ Components::Profile.create(JSON.parse(response.body)["data"])
18
+ end
19
+
20
+ # more info - http://docs.crowdskout.com/api/#create-a-profile
21
+ def create_profile(profile, params = {})
22
+ raise Exceptions::ServiceException, "Profile object must not be nil." if profile.nil?
23
+ url = Util::Config.get('endpoints.base_url') + Util::Config.get('endpoints.profile')
24
+ url = build_url(url, params)
25
+ payload = profile.to_json
26
+ response = RestClient.post(url, payload, get_headers())
27
+ Components::Profile.create(JSON.parse(response.body)["data"])
28
+ end
29
+
30
+ # more info - http://docs.crowdskout.com/api/#create-many-profiles
31
+ def create_profiles_bulk(profiles)
32
+ raise Exceptions::ServiceException, "Must be an array of profiles." if profiles.nil? || !profiles.is_a?(Array)
33
+ url = Util::Config.get('endpoints.base_url') + Util::Config.get('endpoints.profile_bulk')
34
+ url = build_url(url)
35
+ payload = profiles.to_json
36
+ response = RestClient.post(url, payload, get_headers())
37
+ body = JSON.parse(response.body)
38
+
39
+ profiles = []
40
+ body['data'].each do |profile|
41
+ profiles << Components::Profile.create(profile)
42
+ end if body['data'].count > 0
43
+
44
+ Components::ResultSet.new(profiles, body['messages'])
45
+ end
46
+
47
+ # more info - http://docs.crowdskout.com/api/#update-a-profile-by-id
48
+ def update_profile(profile)
49
+ raise Exceptions::ServiceException, "Profile object must not be nil." if profile.nil?
50
+ url = Util::Config.get('endpoints.base_url') + sprintf(Util::Config.get('endpoints.crud_profile'), profile.id)
51
+ url = build_url(url)
52
+ payload = profile.to_json
53
+ response = RestClient.put(url, payload, get_headers())
54
+ Components::Profile.create(JSON.parse(response.body)["data"])
55
+ end
56
+
57
+ # more info - http://docs.crowdskout.com/api/#update-many-profiles
58
+ def update_profiles_bulk(profiles)
59
+ raise Exceptions::ServiceException, "Must be an array of profiles." if profiles.nil? || !profiles.is_a?(Array)
60
+ url = Util::Config.get('endpoints.base_url') + Util::Config.get('endpoints.profile_bulk')
61
+ url = build_url(url)
62
+ payload = profiles.to_json
63
+ response = RestClient.put(url, payload, get_headers())
64
+ body = JSON.parse(response.body)
65
+
66
+ profiles = []
67
+ body['data'].each do |profile|
68
+ profiles << Components::Profile.create(profile)
69
+ end if body['data'].count > 0
70
+
71
+ Components::ResultSet.new(profiles, body['messages'])
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,75 @@
1
+ module Crowdskout
2
+ module Util
3
+ class Config
4
+
5
+ # Return a hash of configuration strings
6
+ # @return [Hash] - hash of configuration properties
7
+ @props = {
8
+ # REST endpoints
9
+ :endpoints => {
10
+ :base_url => 'https://api.crowdskout.com/v1/',
11
+
12
+ :profile => 'profile',
13
+ :crud_profile => 'profile/%s',
14
+ :profile_bulk => 'profile/bulk',
15
+
16
+ :fields_options => 'fields/%s/options',
17
+
18
+ :attributes => 'attributes',
19
+ :attribute => 'attribute',
20
+ :crud_attribute => 'attribute/%s'
21
+ },
22
+
23
+ # OAuth2 Authorization related configuration options
24
+ :auth => {
25
+ :base_url => 'https://api.crowdskout.com/oauth/',
26
+ :response_type_code => 'code',
27
+ :authorization_code_grant_type => 'authorization_code',
28
+ :authorization_endpoint => 'authorize',
29
+ :token_endpoint => 'access_token',
30
+ :api_key => '',
31
+ :api_secret => '',
32
+ :redirect_uri => ''
33
+ },
34
+
35
+ # Errors to be returned for various exceptions
36
+ :errors => {
37
+ :api_key_missing => 'api_key required either explicitly or in configuration.',
38
+ :access_token_missing => 'access_token required explicitly.',
39
+ :api_secret_missing => 'The api_secret is missing in explicit call or configuration.'
40
+ }
41
+ }
42
+
43
+ class << self
44
+ attr_accessor :props
45
+
46
+ def configure
47
+ yield props if block_given?
48
+ end
49
+
50
+ # Get a configuration property given a specified location, example usage: Config::get('auth.token_endpoint')
51
+ # @param [String] index - location of the property to obtain
52
+ # @return [String]
53
+ def get(index)
54
+ properties = index.split('.')
55
+ get_value(properties, props)
56
+ end
57
+
58
+ private
59
+
60
+ # Navigate through a config array looking for a particular index
61
+ # @param [Array] index The index sequence we are navigating down
62
+ # @param [Hash, String] value The portion of the config array to process
63
+ # @return [String]
64
+ def get_value(index, value)
65
+ index = index.is_a?(Array) ? index : [index]
66
+ key = index.shift.to_sym
67
+ value.is_a?(Hash) and value[key] and value[key].is_a?(Hash) ?
68
+ get_value(index, value[key]) :
69
+ value[key]
70
+ end
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,21 @@
1
+ module Crowdskout
2
+ module Util
3
+ class Helpers
4
+ class << self
5
+
6
+ # Build the HTTP query from the given parameters
7
+ # @param [Hash] params
8
+ # @return [String] query string
9
+ def http_build_query(params)
10
+ params.collect{ |k,v| "#{k.to_s}=#{encode(v.to_s)}" }.reverse.join('&')
11
+ end
12
+
13
+ # Escape special characters
14
+ # @param [String] str
15
+ def encode(str)
16
+ CGI.escape(str).gsub('.', '%2E').gsub('-', '%2D')
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ # Copyright, 2016, by Kyle Schutt.
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ module Crowdskout
22
+ VERSION = '0.0.4'
23
+ end
@@ -0,0 +1,114 @@
1
+ #
2
+ # oauth2_spec.rb
3
+ # Crowdskout
4
+ #
5
+ # Copyright (c) 2016 Kyle Schutt. All rights reserved.
6
+
7
+ require 'spec_helper'
8
+
9
+ describe Crowdskout::Auth::OAuth2 do
10
+
11
+ before(:all) {
12
+ Crowdskout::Util::Config.configure do |config|
13
+ config[:auth].delete :api_key
14
+ config[:auth].delete :api_secret
15
+ config[:auth].delete :redirect_uri
16
+ end
17
+ }
18
+
19
+ describe "#new" do
20
+ it "fails without arguments and no configuration" do
21
+ lambda {
22
+ Crowdskout::Auth::OAuth2.new
23
+ }.should raise_error(ArgumentError)
24
+ end
25
+
26
+ it "takes one argument" do
27
+ opts = {:api_key => 'api key', :api_secret => 'secret', :redirect_url => 'redirect url'}
28
+ lambda {
29
+ Crowdskout::Auth::OAuth2.new(opts)
30
+ }.should_not raise_error
31
+ end
32
+ end
33
+
34
+ context "with middle-ware configuration" do
35
+ before(:all) do
36
+ Crowdskout::Util::Config.configure do |config|
37
+ config[:auth][:api_key] = "config_api_key"
38
+ config[:auth][:api_secret] = "config_api_secret"
39
+ config[:auth][:redirect_uri] = "config_redirect_uri"
40
+ end
41
+ end
42
+ let(:proc) { lambda { Crowdskout::Auth::OAuth2.new } }
43
+ it "without error" do
44
+ proc.should_not raise_error
45
+ end
46
+ let(:oauth) { proc.call }
47
+ it "has correct api_key" do
48
+ oauth.client_id.should == "config_api_key"
49
+ end
50
+ it "has correct api_secret" do
51
+ oauth.client_secret.should == "config_api_secret"
52
+ end
53
+ it "has correct redirect_uri" do
54
+ oauth.redirect_uri.should == "config_redirect_uri"
55
+ end
56
+ end
57
+
58
+ context "with middle-ware configuration and explicit opts" do
59
+ before(:all) do
60
+ Crowdskout::Util::Config.configure do |config|
61
+ config[:auth][:api_key] = "config_api_key"
62
+ config[:auth][:api_secret] = "config_api_secret"
63
+ config[:auth][:redirect_uri] = "config_redirect_uri"
64
+ end
65
+ end
66
+ let(:proc) { lambda { Crowdskout::Auth::OAuth2.new :api_key => 'explicit_api_key', :api_secret => 'explicit_api_secret', :redirect_url => 'explicit_redirect_uri' } }
67
+ it "without error" do
68
+ proc.should_not raise_error
69
+ end
70
+ let(:oauth) { proc.call }
71
+ it "has correct api_key" do
72
+ oauth.client_id.should == "explicit_api_key"
73
+ end
74
+ it "has correct api_secret" do
75
+ oauth.client_secret.should == "explicit_api_secret"
76
+ end
77
+ it "has correct redirect_uri" do
78
+ oauth.redirect_uri.should == "explicit_redirect_uri"
79
+ end
80
+ end
81
+
82
+ it "with explicit arguments and no configuration" do
83
+ lambda {
84
+ Crowdskout::Auth::OAuth2.new :api_key => "api_key", :api_secret => "api_secret", :redirect_url => "redirect_uri"
85
+ }.should_not raise_error
86
+ end
87
+
88
+ let(:proc) { lambda { Crowdskout::Auth::OAuth2.new(:api_key => "api_key", :api_secret => "api_secret", :redirect_url => "redirect_uri") } }
89
+ let(:oauth) { proc.call }
90
+
91
+ describe "#get_authorization_url" do
92
+ it "returns a string" do
93
+ oauth.get_authorization_url.should be_kind_of(String)
94
+ oauth.get_authorization_url('my_param').should match('state=my_param')
95
+ end
96
+ end
97
+
98
+ describe "#get_access_token" do
99
+ it "returns a Hash" do
100
+ json = load_file('access_token.json')
101
+ RestClient.stub(:post).and_return(json)
102
+ oauth.get_access_token('token').should be_kind_of(Hash)
103
+ end
104
+
105
+ it "throws an OAuth2Exception in case of error" do
106
+ json = load_file('access_token_error.json')
107
+ RestClient.stub(:post).and_return(json)
108
+ lambda {
109
+ oauth.get_access_token('token')
110
+ }.should raise_exception(Crowdskout::Exceptions::OAuth2Exception,
111
+ 'The resource owner or authorization server denied the request.')
112
+ end
113
+ end
114
+ end