crowdskout 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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