Skip to content

Commit

Permalink
Add risk endpoint client
Browse files Browse the repository at this point in the history
  • Loading branch information
dawlib committed May 25, 2021
1 parent dcfbc63 commit 235e7bb
Show file tree
Hide file tree
Showing 16 changed files with 725 additions and 0 deletions.
6 changes: 6 additions & 0 deletions lib/castle.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,27 @@
castle/commands/approve_device
castle/commands/authenticate
castle/commands/end_impersonation
castle/commands/filter
castle/commands/get_device
castle/commands/get_devices_for_user
castle/commands/identify
castle/commands/log
castle/commands/report_device
castle/commands/review
castle/commands/risk
castle/commands/start_impersonation
castle/commands/track
castle/api/approve_device
castle/api/authenticate
castle/api/end_impersonation
castle/api/filter
castle/api/get_device
castle/api/get_devices_for_user
castle/api/identify
castle/api/log
castle/api/report_device
castle/api/review
castle/api/risk
castle/api/start_impersonation
castle/api/track
castle/payload/prepare
Expand Down
35 changes: 35 additions & 0 deletions lib/castle/api/filter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

module Castle
module API
# Module for filter endpoint
module Filter
class << self
# @param options [Hash]
# return [Hash]
def call(options = {})
unless options[:no_symbolize]
options = Castle::Utils::DeepSymbolizeKeys.call(options || {})
end
options.delete(:no_symbolize)
http = options.delete(:http)
config = options.delete(:config) || Castle.config

response = Castle::API.call(
Castle::Commands::Filter.build(options),
{},
http,
config
)
response.merge(failover: false, failover_reason: nil)
rescue Castle::RequestError, Castle::InternalServerError => e
unless config.failover_strategy == :throw
return Castle::Failover::PrepareResponse.new(options[:user][:id], reason: e.to_s).call
end

raise e
end
end
end
end
end
35 changes: 35 additions & 0 deletions lib/castle/api/log.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

module Castle
module API
# Module for log endpoint
module Log
class << self
# @param options [Hash]
# return [Hash]
def call(options = {})
unless options[:no_symbolize]
options = Castle::Utils::DeepSymbolizeKeys.call(options || {})
end
options.delete(:no_symbolize)
http = options.delete(:http)
config = options.delete(:config) || Castle.config

response = Castle::API.call(
Castle::Commands::Log.build(options),
{},
http,
config
)
response.merge(failover: false, failover_reason: nil)
rescue Castle::RequestError, Castle::InternalServerError => e
unless config.failover_strategy == :throw
return Castle::Failover::PrepareResponse.new(options[:user][:id], reason: e.to_s).call
end

raise e
end
end
end
end
end
35 changes: 35 additions & 0 deletions lib/castle/api/risk.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

module Castle
module API
# Module for risk endpoint
module Risk
class << self
# @param options [Hash]
# return [Hash]
def call(options = {})
unless options[:no_symbolize]
options = Castle::Utils::DeepSymbolizeKeys.call(options || {})
end
options.delete(:no_symbolize)
http = options.delete(:http)
config = options.delete(:config) || Castle.config

response = Castle::API.call(
Castle::Commands::Risk.build(options),
{},
http,
config
)
response.merge(failover: false, failover_reason: nil)
rescue Castle::RequestError, Castle::InternalServerError => e
unless config.failover_strategy == :throw
return Castle::Failover::PrepareResponse.new(options[:user][:id], reason: e.to_s).call
end

raise e
end
end
end
end
end
40 changes: 40 additions & 0 deletions lib/castle/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,46 @@ def track(options = {})
Castle::API::Track.call(options.merge(context: new_context, no_symbolize: true))
end

# @param options [Hash]
def filter(options = {})
options = Castle::Utils::DeepSymbolizeKeys.call(options || {})

return generate_do_not_track_response(options[:user][:id]) unless tracked?

add_timestamp_if_necessary(options)

new_context = Castle::Context::Merge.call(@context, options[:context])

Castle::API::Filter.call(options.merge(context: new_context, no_symbolize: true))
end

# @param options [Hash]
def risk(options = {})
options = Castle::Utils::DeepSymbolizeKeys.call(options || {})

return generate_do_not_track_response(options[:user][:id]) unless tracked?

add_timestamp_if_necessary(options)

new_context = Castle::Context::Merge.call(@context, options[:context])

Castle::API::Risk.call(options.merge(context: new_context, no_symbolize: true))
end

# @param options [Hash]
def log(options = {})
options = Castle::Utils::DeepSymbolizeKeys.call(options || {})

return generate_do_not_track_response(options[:user][:id]) unless tracked?

add_timestamp_if_necessary(options)

new_context = Castle::Context::Merge.call(@context, options[:context])

Castle::API::Log.call(options.merge(context: new_context, no_symbolize: true))
end


# @param options [Hash]
def start_impersonation(options = {})
options = Castle::Utils::DeepSymbolizeKeys.call(options || {})
Expand Down
23 changes: 23 additions & 0 deletions lib/castle/commands/filter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

module Castle
module Commands
# Generates the payload for the filter request
class Filter
class << self
# @param options [Hash]
# @return [Castle::Command]
def build(options = {})
Castle::Validators::Present.call(options, %i[event])
context = Castle::Context::Sanitize.call(options[:context])

Castle::Command.new(
'filter',
options.merge(context: context, sent_at: Castle::Utils::GetTimestamp.call),
:post
)
end
end
end
end
end
23 changes: 23 additions & 0 deletions lib/castle/commands/log.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

module Castle
module Commands
# Generates the payload for the log request
class Log
class << self
# @param options [Hash]
# @return [Castle::Command]
def build(options = {})
Castle::Validators::Present.call(options, %i[event])
context = Castle::Context::Sanitize.call(options[:context])

Castle::Command.new(
'log',
options.merge(context: context, sent_at: Castle::Utils::GetTimestamp.call),
:post
)
end
end
end
end
end
23 changes: 23 additions & 0 deletions lib/castle/commands/risk.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

module Castle
module Commands
# Generates the payload for the risk request
class Risk
class << self
# @param options [Hash]
# @return [Castle::Command]
def build(options = {})
Castle::Validators::Present.call(options, %i[event])
context = Castle::Context::Sanitize.call(options[:context])

Castle::Command.new(
'risk',
options.merge(context: context, sent_at: Castle::Utils::GetTimestamp.call),
:post
)
end
end
end
end
end
5 changes: 5 additions & 0 deletions spec/lib/castle/api/filter_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# frozen_string_literal: true

describe Castle::API::Filter do
pending
end
5 changes: 5 additions & 0 deletions spec/lib/castle/api/log_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# frozen_string_literal: true

describe Castle::API::Log do
pending
end
5 changes: 5 additions & 0 deletions spec/lib/castle/api/risk_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# frozen_string_literal: true

describe Castle::API::Risk do
pending
end
12 changes: 12 additions & 0 deletions spec/lib/castle/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -375,4 +375,16 @@
it { expect(client).to be_tracked }
end
end

describe 'filter' do
it_behaves_like 'action request', :filter
end

describe 'risk' do
it_behaves_like 'action request', :risk
end

describe 'log' do
it_behaves_like 'action request', :log
end
end
106 changes: 106 additions & 0 deletions spec/lib/castle/commands/filter_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# frozen_string_literal: true

describe Castle::Commands::Filter do
subject(:instance) { described_class }

let(:context) { { test: { test1: '1' } } }
let(:user) { { id: '1234', email: 'foobar@mail.com' } }
let(:default_payload) do
{
request_token: '7e51335b-f4bc-4bc7-875d-b713fb61eb23-bf021a3022a1a302',
event: '$registration',
user: user,
sent_at: time_auto,
context: context
}
end
let(:time_now) { Time.now }
let(:time_auto) { time_now.utc.iso8601(3) }

before { Timecop.freeze(time_now) }

after { Timecop.return }

describe '.build' do
subject(:command) { instance.build(payload) }

context 'with properties' do
let(:payload) { default_payload.merge(properties: { test: '1' }) }
let(:command_data) do
default_payload.merge(properties: { test: '1' }, context: context)
end

it { expect(command.method).to be_eql(:post) }
it { expect(command.path).to be_eql('filter') }
it { expect(command.data).to be_eql(command_data) }
end

context 'with user_traits' do
let(:payload) { default_payload.merge(user_traits: { test: '1' }) }
let(:command_data) do
default_payload.merge(user_traits: { test: '1' }, context: context)
end

it { expect(command.method).to be_eql(:post) }
it { expect(command.path).to be_eql('filter') }
it { expect(command.data).to be_eql(command_data) }
end

context 'when active true' do
let(:payload) { default_payload.merge(context: context.merge(active: true)) }
let(:command_data) do
default_payload.merge(context: context.merge(active: true))
end

it { expect(command.method).to be_eql(:post) }
it { expect(command.path).to be_eql('filter') }
it { expect(command.data).to be_eql(command_data) }
end

context 'when active false' do
let(:payload) { default_payload.merge(context: context.merge(active: false)) }
let(:command_data) do
default_payload.merge(context: context.merge(active: false))
end

it { expect(command.method).to be_eql(:post) }
it { expect(command.path).to be_eql('filter') }
it { expect(command.data).to be_eql(command_data) }
end

context 'when active string' do
let(:payload) { default_payload.merge(context: context.merge(active: 'string')) }
let(:command_data) { default_payload.merge(context: context) }

it { expect(command.method).to be_eql(:post) }
it { expect(command.path).to be_eql('filter') }
it { expect(command.data).to be_eql(command_data) }
end
end

describe '#validate!' do
subject(:validate!) { instance.build(payload) }

context 'with event not present' do
let(:payload) { {} }

it do
expect do
validate!
end.to raise_error(Castle::InvalidParametersError, 'event is missing or empty')
end
end

context 'with user not present' do
let(:payload) { { event: '$login' } }

it { expect { validate! }.not_to raise_error }
end

context 'with event and user present' do
let(:payload) { { event: '$login', user: user } }

it { expect { validate! }.not_to raise_error }
end
end
end
Loading

0 comments on commit 235e7bb

Please sign in to comment.