ubersicht-ruby-sdk 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6c517f3a08f30c20b2f5f3a219547519c797a91b2b8bc167dc838b24607a4d9f
4
+ data.tar.gz: dce099aceebd7d36c2dcac19ed007a2f8cf432db81d0ddbe5c5b14a3744327ad
5
+ SHA512:
6
+ metadata.gz: 0f62f52d9d9ff24342861466cd4d2ebdefd193cb7940e051152b77d0a62f68d8b8f9df5658ed53db5bd6bb1d75b235c96d4764c7ebbd391618594e89866a9853
7
+ data.tar.gz: 833078838952d0a4fb62f656578f0d7c351a77e8869c2dbc920d7a6695de142926596d9bf0e11658bf8c9d0b7d69747c631d09aa504bcca983268295bd488fee
@@ -0,0 +1,63 @@
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+ # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
6
+ # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
+
8
+ name: Tests
9
+
10
+ on:
11
+ push:
12
+ branches: [main]
13
+ pull_request:
14
+ branches: [main]
15
+
16
+ jobs:
17
+ rubocop:
18
+ runs-on: ubuntu-latest
19
+ timeout-minutes: 2
20
+ strategy:
21
+ matrix:
22
+ ruby-version: ["3.0.2"]
23
+
24
+ steps:
25
+ - uses: actions/checkout@v2
26
+ - name: Set up Ruby
27
+ # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
28
+ # change this to (see https://github.com/ruby/setup-ruby#versioning):
29
+ uses: ruby/setup-ruby@v1
30
+ with:
31
+ ruby-version: ${{ matrix.ruby-version }}
32
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
33
+ - name: Run rubocop
34
+ run: bundle exec rubocop
35
+ rspec:
36
+ runs-on: ubuntu-latest
37
+ timeout-minutes: 2
38
+ strategy:
39
+ matrix:
40
+ ruby-version: ["3.0.2"]
41
+
42
+ steps:
43
+ - uses: actions/checkout@v2
44
+ - name: Set up Ruby
45
+ # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
46
+ # change this to (see https://github.com/ruby/setup-ruby#versioning):
47
+ uses: ruby/setup-ruby@v1
48
+ with:
49
+ ruby-version: ${{ matrix.ruby-version }}
50
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
51
+ - name: Run rspec
52
+ run: bundle exec rspec
53
+ - name: Simplecov Report
54
+ uses: aki77/simplecov-report-action@v1
55
+ with:
56
+ failedThreshold: 95
57
+ token: ${{ secrets.GITHUB_TOKEN }}
58
+ - name: Upload coverage results
59
+ uses: actions/upload-artifact@master
60
+ if: always()
61
+ with:
62
+ name: coverage-report
63
+ path: coverage
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format progress
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,58 @@
1
+ require:
2
+ - rubocop-performance
3
+ - rubocop-rake
4
+ - rubocop-rspec
5
+
6
+ inherit_mode:
7
+ merge:
8
+ - Include
9
+
10
+ AllCops:
11
+ Include:
12
+ - "**/*.gemspec"
13
+ - "**/*.rake"
14
+ - "**/Gemfile"
15
+ - "**/Rakefile"
16
+ Exclude:
17
+ - "bin/**/*"
18
+ - "config.ru"
19
+ - "vendor/bundle/**/*" # for CI
20
+ CacheRootDirectory: tmp/cache/rubocop_cache/
21
+ TargetRubyVersion: '2.6'
22
+ DefaultFormatter: fuubar
23
+ NewCops: enable
24
+
25
+ Layout/EndOfLine:
26
+ EnforcedStyle: lf
27
+
28
+ Layout/LineLength:
29
+ Max: 120
30
+
31
+ Layout/MultilineMethodCallIndentation:
32
+ EnforcedStyle: indented
33
+ IndentationWidth: 2
34
+
35
+ Lint/AmbiguousBlockAssociation:
36
+ Exclude:
37
+ - "spec/**/*"
38
+
39
+ Metrics/BlockLength:
40
+ Enabled: false
41
+
42
+ Performance/MethodObjectAsBlock:
43
+ Enabled: false
44
+
45
+ RSpec/AnyInstance:
46
+ Enabled: false
47
+
48
+ RSpec/ExampleLength:
49
+ Enabled: false
50
+
51
+ RSpec/MultipleExpectations:
52
+ Enabled: false
53
+
54
+ Style/Documentation:
55
+ Enabled: false
56
+
57
+ Style/FrozenStringLiteralComment:
58
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.6.3
7
+ before_install: gem install bundler -v 2.0.2
data/CHANGELOG.md ADDED
@@ -0,0 +1,18 @@
1
+ ## Not released
2
+ ## 0.2.0
3
+
4
+ - [ingestion] added `event.payload.event_id` to prevent events duplication on Ubersicht side
5
+ - [ingestion] added `event.payload.event_id` to event signature
6
+ - [ingestion] added `debug` option to ingestion client. If false then request will be ran in a new thread.
7
+
8
+ Braking changes:
9
+
10
+ - [ingestion] replaced `ingest` signature to keywords
11
+ - [ingestion] renamed `event.type` with `event.transaction_type`
12
+ - [ingestion] replaced `clien.ingest_events(events)` with `clien.ingest(event)`
13
+
14
+ ## 0.1.0
15
+
16
+ - [ingestion] added Ubersicht::Ingestion::Client#ingest_events
17
+ - [ingestion] added signature to event payload, `payload.signature`
18
+ - [ingestion] added validaton of event type based on types allowed for provider
data/Dockerfile ADDED
@@ -0,0 +1,24 @@
1
+ # Dockerfile
2
+ # Use ruby image to build our own image
3
+ FROM ruby:3.0.2
4
+
5
+ ARG APP_ROOT=/app
6
+ ARG BUILD_PACKAGES="\
7
+ vim \
8
+ "
9
+
10
+ # token for fetching private gems https://www.surminus.com/blog/installing-private-gems-during-a-docker-build/
11
+ ARG BUNDLE_GITHUB__COM
12
+
13
+ # We specify everything will happen within the /app folder inside the container
14
+ WORKDIR $APP_ROOT
15
+
16
+ ENV PATH="${APP_ROOT}/bin:${PATH}"
17
+
18
+ RUN apt-get update -qq && apt-get install -y --fix-missing --no-install-recommends $BUILD_PACKAGES && \
19
+ apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
20
+
21
+ # We install all the dependencies
22
+ # RUN bundle config set jobs "$(getconf _NPROCESSORS_ONLN)" && \
23
+ # bundle config set path /usr/local/bundle && \
24
+ # bundle install
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ubersicht-ruby-sdk.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,140 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ubersicht-ruby-sdk (0.2.0)
5
+ dry-struct (~> 1.0)
6
+ faraday (>= 0.9.2, < 2)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ addressable (2.8.0)
12
+ public_suffix (>= 2.0.2, < 5.0)
13
+ ast (2.4.2)
14
+ coderay (1.1.3)
15
+ concurrent-ruby (1.1.9)
16
+ crack (0.4.5)
17
+ rexml
18
+ diff-lcs (1.4.4)
19
+ docile (1.4.0)
20
+ dry-configurable (0.12.1)
21
+ concurrent-ruby (~> 1.0)
22
+ dry-core (~> 0.5, >= 0.5.0)
23
+ dry-container (0.8.0)
24
+ concurrent-ruby (~> 1.0)
25
+ dry-configurable (~> 0.1, >= 0.1.3)
26
+ dry-core (0.7.1)
27
+ concurrent-ruby (~> 1.0)
28
+ dry-inflector (0.2.1)
29
+ dry-logic (1.2.0)
30
+ concurrent-ruby (~> 1.0)
31
+ dry-core (~> 0.5, >= 0.5)
32
+ dry-struct (1.4.0)
33
+ dry-core (~> 0.5, >= 0.5)
34
+ dry-types (~> 1.5)
35
+ ice_nine (~> 0.11)
36
+ dry-types (1.5.1)
37
+ concurrent-ruby (~> 1.0)
38
+ dry-container (~> 0.3)
39
+ dry-core (~> 0.5, >= 0.5)
40
+ dry-inflector (~> 0.1, >= 0.1.2)
41
+ dry-logic (~> 1.0, >= 1.0.2)
42
+ faraday (1.7.1)
43
+ faraday-em_http (~> 1.0)
44
+ faraday-em_synchrony (~> 1.0)
45
+ faraday-excon (~> 1.1)
46
+ faraday-httpclient (~> 1.0.1)
47
+ faraday-net_http (~> 1.0)
48
+ faraday-net_http_persistent (~> 1.1)
49
+ faraday-patron (~> 1.0)
50
+ faraday-rack (~> 1.0)
51
+ multipart-post (>= 1.2, < 3)
52
+ ruby2_keywords (>= 0.0.4)
53
+ faraday-em_http (1.0.0)
54
+ faraday-em_synchrony (1.0.0)
55
+ faraday-excon (1.1.0)
56
+ faraday-httpclient (1.0.1)
57
+ faraday-net_http (1.0.1)
58
+ faraday-net_http_persistent (1.2.0)
59
+ faraday-patron (1.0.0)
60
+ faraday-rack (1.0.0)
61
+ hashdiff (1.0.1)
62
+ ice_nine (0.11.2)
63
+ method_source (1.0.0)
64
+ multipart-post (2.1.1)
65
+ parallel (1.20.1)
66
+ parser (3.0.2.0)
67
+ ast (~> 2.4.1)
68
+ pry (0.14.1)
69
+ coderay (~> 1.1)
70
+ method_source (~> 1.0)
71
+ public_suffix (4.0.6)
72
+ rainbow (3.0.0)
73
+ rake (10.5.0)
74
+ regexp_parser (2.1.1)
75
+ rexml (3.2.5)
76
+ rspec (3.10.0)
77
+ rspec-core (~> 3.10.0)
78
+ rspec-expectations (~> 3.10.0)
79
+ rspec-mocks (~> 3.10.0)
80
+ rspec-core (3.10.1)
81
+ rspec-support (~> 3.10.0)
82
+ rspec-expectations (3.10.1)
83
+ diff-lcs (>= 1.2.0, < 2.0)
84
+ rspec-support (~> 3.10.0)
85
+ rspec-mocks (3.10.2)
86
+ diff-lcs (>= 1.2.0, < 2.0)
87
+ rspec-support (~> 3.10.0)
88
+ rspec-support (3.10.2)
89
+ rubocop (1.20.0)
90
+ parallel (~> 1.10)
91
+ parser (>= 3.0.0.0)
92
+ rainbow (>= 2.2.2, < 4.0)
93
+ regexp_parser (>= 1.8, < 3.0)
94
+ rexml
95
+ rubocop-ast (>= 1.9.1, < 2.0)
96
+ ruby-progressbar (~> 1.7)
97
+ unicode-display_width (>= 1.4.0, < 3.0)
98
+ rubocop-ast (1.11.0)
99
+ parser (>= 3.0.1.1)
100
+ rubocop-performance (1.11.5)
101
+ rubocop (>= 1.7.0, < 2.0)
102
+ rubocop-ast (>= 0.4.0)
103
+ rubocop-rake (0.6.0)
104
+ rubocop (~> 1.0)
105
+ rubocop-rspec (2.4.0)
106
+ rubocop (~> 1.0)
107
+ rubocop-ast (>= 1.1.0)
108
+ ruby-progressbar (1.11.0)
109
+ ruby2_keywords (0.0.5)
110
+ simplecov (0.21.2)
111
+ docile (~> 1.1)
112
+ simplecov-html (~> 0.11)
113
+ simplecov_json_formatter (~> 0.1)
114
+ simplecov-html (0.12.3)
115
+ simplecov_json_formatter (0.1.3)
116
+ unicode-display_width (2.0.0)
117
+ webmock (3.14.0)
118
+ addressable (>= 2.8.0)
119
+ crack (>= 0.3.2)
120
+ hashdiff (>= 0.4.0, < 2.0.0)
121
+
122
+ PLATFORMS
123
+ ruby
124
+ x86_64-linux
125
+
126
+ DEPENDENCIES
127
+ bundler (~> 2.0)
128
+ pry
129
+ rake (~> 10.0)
130
+ rspec (~> 3.0)
131
+ rubocop (~> 1.0)
132
+ rubocop-performance (~> 1.0)
133
+ rubocop-rake (~> 0.6)
134
+ rubocop-rspec (~> 2.0)
135
+ simplecov (~> 0.1)
136
+ ubersicht-ruby-sdk!
137
+ webmock
138
+
139
+ BUNDLED WITH
140
+ 2.2.22
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 starfish.codes
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Vadim Lazebny
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # Ubersicht API library for Ruby
2
+
3
+ This is the officially supported Ruby library for using Ubersicht's APIs.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'ubersicht-ruby-sdk', git: 'https://github.com/starfish-codes/ubersicht-ruby-sdk.git',
11
+ branch: 'main', require: 'ubersicht'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install ubersicht-ruby-sdk
21
+
22
+ ## Usage
23
+
24
+ ### Ingestion
25
+
26
+ Build client:
27
+
28
+ ```sh
29
+ client = Ubersicht::Ingestion::Client.new(
30
+ hmac_key: '44782DEF547AAA06C910C43932B1EB0C71FC68D9D0C057550C48EC2ACF6BA056',
31
+ account_id: '1001',
32
+ pass: 'password',
33
+ provider: 'DAuth',
34
+ url: '<api-base-url>',
35
+ user: 'user',
36
+ debug: true
37
+ )
38
+ ```
39
+
40
+ Parameters:
41
+
42
+ * `hmac_key` - (required) used for signing notification on client side and validation on server side (Ubersicht plugin config)
43
+ * `account_id` - (required) account which receives notification events
44
+ * `pass` - (required) basic auth password (Ubersicht plugin config)
45
+ * `user` - (required) basic auth username (Ubersicht plugin config)
46
+ * `provider` - (required) plugin provider in Ubersicht platform, e.g. DAuth
47
+ * `url` - (required) Ubersicht API root url. Different for production and testing environment.
48
+ * `debug` - (optional) default=false. If true then request is performed in the current thread, if false then new thread is created.
49
+
50
+ Send event:
51
+
52
+ ```sh
53
+ event = {
54
+ # required
55
+ transaction_type: 'DeviceBinding'
56
+ event_code: 'REQUESTED',
57
+ # optional
58
+ event_date: 2021-10-10 10:10:10,
59
+ event_group_id: 'eb2bc8bb-f584-4801-b98c-361a0c2d38f8',
60
+ event_id: 'ed62d0c1-f2a5-41b7-ab58-24c033eec508',,
61
+ }
62
+ client.ingest(**event)
63
+ ```
64
+
65
+ Event attributes:
66
+
67
+ * `transaction_type` (required) - a process or resource to which event belongs, e.g. DeviceBinding, Authentication
68
+ * `event_code` (required) - string identifier of a transition
69
+ * `event_date` - time when event was triggered
70
+ * `event_group_id` - correlated transaction id (allows to link some events together)
71
+ * `event_id` - unique event identifier (allows to silence duplicated events)
72
+
73
+ ## Development
74
+
75
+ After checking out the repo, run `bin/setup` to install dependencies.
76
+ Then, run `rake spec` to run the tests.
77
+ You can also run `bin/console` for an interactive prompt that will allow you to experiment.
78
+
79
+ To install this gem onto your local machine, run `bundle exec rake install`.
80
+ To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`,
81
+ which will create a git tag for the version, push git commits and tags, and push the `.gem`
82
+ file to [rubygems.org](https://rubygems.org).
83
+
84
+ ## Contributing
85
+
86
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/ubersicht-ruby-sdk.
87
+
88
+ ## License
89
+
90
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'ubersicht'
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(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,15 @@
1
+ version: "3.8"
2
+ services:
3
+ gem:
4
+ container_name: ubersicht_ruby_sdk
5
+ build:
6
+ context: .
7
+ dockerfile: Dockerfile
8
+ volumes:
9
+ - ./:/app
10
+ - ~/.gitconfig:/etc/gitconfig # allows to create commits inside container
11
+ - bundle_path:/usr/local/bundle
12
+ stdin_open: true
13
+ tty: true
14
+ volumes:
15
+ bundle_path:
@@ -0,0 +1,33 @@
1
+ module Ubersicht
2
+ module Ingestion
3
+ class BuildIngestionEvent
4
+ def self.call(*args, &block)
5
+ new.call(*args, &block)
6
+ end
7
+
8
+ def initialize
9
+ @validate_hmac = ::Ubersicht::Ingestion::HmacValidator.with_ubersicht
10
+ @event_class = ::Ubersicht::Ingestion::Event
11
+ end
12
+
13
+ def call(event, hmac_key, provider)
14
+ validate_transaction_type(event[:transaction_type], provider)
15
+
16
+ attrs = @event_class.new(event).to_h
17
+ attrs[:payload][:hmac_signature] = @validate_hmac.calculate_notification_hmac(attrs, hmac_key)
18
+ attrs[:provider] = provider
19
+ attrs
20
+ rescue ::Dry::Struct::Error => e
21
+ raise ::Ubersicht::ValidationError, e.message
22
+ end
23
+
24
+ private
25
+
26
+ def validate_transaction_type(transaction_type, provider)
27
+ return if (types = ::Ubersicht::Ingestion::PROVIDER_TO_TYPES_MAP[provider]).include?(transaction_type)
28
+
29
+ raise Ubersicht::Error, "Unknown type '#{transaction_type}'. #{provider} supports only #{types.join(', ')}." \
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,86 @@
1
+ module Ubersicht
2
+ module Ingestion
3
+ class Client
4
+ def self.default(*args)
5
+ new(*args) do |faraday|
6
+ faraday.request :url_encoded
7
+ # faraday.response :raise_error
8
+ faraday.response :logger, Logger.new($stdout),
9
+ headers: { request: false, response: false },
10
+ bodies: { request: true, response: true }
11
+ faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
12
+ end
13
+ end
14
+
15
+ def initialize(options = {}, &block) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
16
+ raise ArgumentError, 'Account id cannot be blank' if empty?(account_id = options[:account_id])
17
+ raise ArgumentError, 'Hmac key cannot be blank' if empty?(hmac_key = options[:hmac_key])
18
+ raise ArgumentError, 'Password cannot be blank' if empty?(pass = options[:pass])
19
+ raise ArgumentError, 'Url cannot be blank' if empty?(url = options[:url])
20
+ raise ArgumentError, 'Username cannot be blank' if empty?(user = options[:user])
21
+ unless ::Ubersicht::Ingestion::PROVIDERS.include?(provider = options[:provider])
22
+ raise ArgumentError, "Not supported provider '#{provider}'"
23
+ end
24
+
25
+ @account_id = account_id
26
+ @debug = options[:debug] || false
27
+ @hmac_key = hmac_key
28
+ @provider = provider
29
+ @conn = setup_conn(url, user, pass, &block)
30
+ end
31
+
32
+ def ingest(transaction_type:, event_code:, event_date: Time.now, **payload)
33
+ event = {
34
+ event_code: event_code,
35
+ event_date: event_date.iso8601(3),
36
+ payload: payload,
37
+ transaction_type: transaction_type
38
+ }
39
+ ingest_events([event])
40
+ end
41
+
42
+ private
43
+
44
+ attr_reader :account_id, :debug, :hmac_key, :provider
45
+
46
+ def ingest_events(events)
47
+ body = {
48
+ events: events.map { |event| ::Ubersicht::Ingestion::BuildIngestionEvent.call(event, hmac_key, provider) }
49
+ }
50
+ url = "accounts/#{account_id}/plugins/#{provider.downcase}/notification_webhooks"
51
+ process { handle_response(@conn.post(url, body.to_json)) }
52
+ end
53
+
54
+ def process(&block)
55
+ return yield if debug
56
+
57
+ Thread.new(&block)
58
+ nil
59
+ end
60
+
61
+ def handle_response(response)
62
+ case response.status
63
+ when 200..299
64
+ response.body
65
+ when 300..599
66
+ raise ::Ubersicht::Error, response.body
67
+ end
68
+ end
69
+
70
+ def empty?(value)
71
+ value.to_s.empty?
72
+ end
73
+
74
+ def setup_conn(url, user, pass)
75
+ headers = {
76
+ 'Content-Type' => 'application/json'
77
+ }
78
+ Faraday.new(url: url, headers: headers) do |faraday|
79
+ faraday.request(:basic_auth, user, pass)
80
+
81
+ yield(faraday) if block_given?
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,17 @@
1
+ module Ubersicht
2
+ module Ingestion
3
+ module Types
4
+ include ::Dry.Types()
5
+ end
6
+
7
+ class Event < ::Dry::Struct
8
+ attribute :event_code, Types::String
9
+ attribute :event_date, Types::String
10
+ attribute :transaction_type, Types::String # DeviceBinding
11
+ attribute :payload, Types::Hash.optional.default({}.freeze)
12
+ # payload.event_date
13
+ # payload.event_group_id
14
+ # payload.event_id
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,83 @@
1
+ module Ubersicht
2
+ module Ingestion
3
+ # source: https://github.com/Adyen/adyen-ruby-api-library/blob/92e3fc98de808375ad701ba459aec3425d49c43c/spec/utils/hmac_validator_spec.rb
4
+ class HmacValidator
5
+ DATA_SEPARATOR = ':'.freeze
6
+ HMAC_ALGORITHM = 'sha256'.freeze
7
+
8
+ ADYEN_HMAC_SIGNATURE_PATH = 'additionalData.hmacSignature'.freeze
9
+ ADYEN_VALIDATION_KEYS = %w[pspReference
10
+ originalReference
11
+ merchantAccountCode
12
+ merchantReference
13
+ amount.value
14
+ amount.currency
15
+ eventCode
16
+ success].freeze
17
+
18
+ UBERSICHT_HMAC_SIGNATURE_PATH = 'payload.hmac_signature'.freeze
19
+ UBERSICHT_VALIDATION_KEYS = %w[payload.event_group_id
20
+ payload.event_id
21
+ transaction_type
22
+ event_code
23
+ event_date].freeze
24
+
25
+ def self.with_adyen
26
+ new(ADYEN_HMAC_SIGNATURE_PATH, ADYEN_VALIDATION_KEYS)
27
+ end
28
+
29
+ def self.with_ubersicht
30
+ new(UBERSICHT_HMAC_SIGNATURE_PATH, UBERSICHT_VALIDATION_KEYS)
31
+ end
32
+
33
+ def initialize(hmac_signature_path, validation_keys)
34
+ @hmac_signature_path = hmac_signature_path
35
+ @validation_keys = validation_keys
36
+ end
37
+
38
+ def calculate_notification_hmac(notification_request_item, hmac_key)
39
+ data = data_to_sign(notification_request_item)
40
+
41
+ Base64.strict_encode64(OpenSSL::HMAC.digest(HMAC_ALGORITHM, [hmac_key].pack('H*'), data))
42
+ end
43
+
44
+ def data_to_sign(notification_request_item)
45
+ validation_keys
46
+ .map { |key| fetch(notification_request_item, key).to_s }
47
+ .map { |value| value.gsub('\\', '\\\\').gsub(':', '\\:') }
48
+ .join(DATA_SEPARATOR)
49
+ end
50
+
51
+ def valid_notification_hmac?(notification_request_item, hmac_key)
52
+ expected_sign = calculate_notification_hmac(notification_request_item, hmac_key)
53
+ merchant_sign = fetch(notification_request_item, hmac_signature_path)
54
+
55
+ expected_sign == merchant_sign
56
+ end
57
+
58
+ def valid_notifications?(notification_request_items, hmac_key)
59
+ notification_request_items.all? do |notification_request_item|
60
+ valid_notification_hmac?(notification_request_item, hmac_key)
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ attr_reader \
67
+ :hmac_signature_path,
68
+ :validation_keys
69
+
70
+ def fetch(hash, keys)
71
+ keys.to_s.split('.').reduce(hash) do |acc, key|
72
+ next acc if acc.nil?
73
+
74
+ if key.to_i.to_s == key
75
+ acc[key.to_i]
76
+ else
77
+ acc[key].nil? ? acc[key.to_sym] : acc[key]
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,19 @@
1
+ module Ubersicht
2
+ module Ingestion
3
+ ACCEPTED_RESPONSE = '[accepted]'.freeze
4
+ ACCEPTED_STATUS = 200
5
+ REJECTED_RESPONSE = '[rejected]'.freeze
6
+ REJECTED_STATUS = 400
7
+
8
+ PROVIDERS = [
9
+ DAUTH_PROVIDER = 'DAuth'.freeze
10
+ ].freeze
11
+
12
+ PROVIDER_TO_TYPES_MAP = {
13
+ DAUTH_PROVIDER => %w[
14
+ Authentication
15
+ DeviceBinding
16
+ ]
17
+ }.freeze
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module Ubersicht
2
+ VERSION = '0.2.0'.freeze
3
+ end
data/lib/ubersicht.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'dry-struct'
2
+ require 'faraday'
3
+
4
+ require 'ubersicht/version'
5
+ require 'ubersicht/ingestion'
6
+ require 'ubersicht/ingestion/build_ingestion_event'
7
+ require 'ubersicht/ingestion/client'
8
+ require 'ubersicht/ingestion/event'
9
+ require 'ubersicht/ingestion/hmac_validator'
10
+
11
+ module Ubersicht
12
+ Error = Class.new(StandardError)
13
+ ValidationError = Class.new(Error)
14
+ end
@@ -0,0 +1 @@
1
+ require 'ubersicht'
@@ -0,0 +1,43 @@
1
+ lib = File.expand_path('lib', __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'ubersicht/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'ubersicht-ruby-sdk'
7
+ spec.version = Ubersicht::VERSION
8
+ spec.authors = ['Starfish Team']
9
+ spec.email = ['support@starfish.codes']
10
+
11
+ spec.summary = %(Official Ubersicht SDK)
12
+ spec.description = %(Official Ubersicht SDK. Simplifies integration with Ubersicht API)
13
+ spec.homepage = 'https://github.com/starfish-codes/ubersicht-ruby-sdk'
14
+ spec.license = 'MIT'
15
+
16
+ # spec.metadata["allowed_push_host"] = "rubygems.org"
17
+
18
+ spec.required_ruby_version = '>= 2.6'
19
+ spec.metadata['homepage_uri'] = spec.homepage
20
+ spec.metadata['source_code_uri'] = spec.homepage
21
+ spec.metadata['changelog_uri'] = 'https://github.com/starfish-codes/ubersicht-ruby-sdk/blob/main/CHANGELOG.md'
22
+
23
+ # Specify which files should be added to the gem when it is released.
24
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
27
+ end
28
+ spec.require_paths = ['lib']
29
+
30
+ spec.add_dependency 'dry-struct', '~> 1.0'
31
+ spec.add_dependency 'faraday', ['>= 0.9.2', '< 2']
32
+
33
+ spec.add_development_dependency 'bundler', '~> 2.0'
34
+ spec.add_development_dependency 'pry'
35
+ spec.add_development_dependency 'rake', '~> 10.0'
36
+ spec.add_development_dependency 'rspec', '~> 3.0'
37
+ spec.add_development_dependency 'rubocop', '~> 1.0'
38
+ spec.add_development_dependency 'rubocop-performance', '~> 1.0'
39
+ spec.add_development_dependency 'rubocop-rake', '~> 0.6'
40
+ spec.add_development_dependency 'rubocop-rspec', '~> 2.0'
41
+ spec.add_development_dependency 'simplecov', '~> 0.1'
42
+ spec.add_development_dependency 'webmock'
43
+ end
metadata ADDED
@@ -0,0 +1,245 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ubersicht-ruby-sdk
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Starfish Team
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-09-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dry-struct
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.9.2
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: '2'
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.9.2
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: '2'
47
+ - !ruby/object:Gem::Dependency
48
+ name: bundler
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '2.0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '2.0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: pry
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rake
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '10.0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '10.0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: rspec
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '3.0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '3.0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: rubocop
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '1.0'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '1.0'
117
+ - !ruby/object:Gem::Dependency
118
+ name: rubocop-performance
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '1.0'
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '1.0'
131
+ - !ruby/object:Gem::Dependency
132
+ name: rubocop-rake
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '0.6'
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: '0.6'
145
+ - !ruby/object:Gem::Dependency
146
+ name: rubocop-rspec
147
+ requirement: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: '2.0'
152
+ type: :development
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - "~>"
157
+ - !ruby/object:Gem::Version
158
+ version: '2.0'
159
+ - !ruby/object:Gem::Dependency
160
+ name: simplecov
161
+ requirement: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - "~>"
164
+ - !ruby/object:Gem::Version
165
+ version: '0.1'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - "~>"
171
+ - !ruby/object:Gem::Version
172
+ version: '0.1'
173
+ - !ruby/object:Gem::Dependency
174
+ name: webmock
175
+ requirement: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ type: :development
181
+ prerelease: false
182
+ version_requirements: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - ">="
185
+ - !ruby/object:Gem::Version
186
+ version: '0'
187
+ description: Official Ubersicht SDK. Simplifies integration with Ubersicht API
188
+ email:
189
+ - support@starfish.codes
190
+ executables: []
191
+ extensions: []
192
+ extra_rdoc_files: []
193
+ files:
194
+ - ".github/workflows/test.yml"
195
+ - ".gitignore"
196
+ - ".rspec"
197
+ - ".rubocop.yml"
198
+ - ".travis.yml"
199
+ - CHANGELOG.md
200
+ - Dockerfile
201
+ - Gemfile
202
+ - Gemfile.lock
203
+ - LICENSE
204
+ - LICENSE.txt
205
+ - README.md
206
+ - Rakefile
207
+ - bin/console
208
+ - bin/setup
209
+ - docker-compose.yml
210
+ - lib/ubersicht.rb
211
+ - lib/ubersicht/ingestion.rb
212
+ - lib/ubersicht/ingestion/build_ingestion_event.rb
213
+ - lib/ubersicht/ingestion/client.rb
214
+ - lib/ubersicht/ingestion/event.rb
215
+ - lib/ubersicht/ingestion/hmac_validator.rb
216
+ - lib/ubersicht/version.rb
217
+ - lib/ubersicht_ruby_sdk.rb
218
+ - ubersicht-ruby-sdk.gemspec
219
+ homepage: https://github.com/starfish-codes/ubersicht-ruby-sdk
220
+ licenses:
221
+ - MIT
222
+ metadata:
223
+ homepage_uri: https://github.com/starfish-codes/ubersicht-ruby-sdk
224
+ source_code_uri: https://github.com/starfish-codes/ubersicht-ruby-sdk
225
+ changelog_uri: https://github.com/starfish-codes/ubersicht-ruby-sdk/blob/main/CHANGELOG.md
226
+ post_install_message:
227
+ rdoc_options: []
228
+ require_paths:
229
+ - lib
230
+ required_ruby_version: !ruby/object:Gem::Requirement
231
+ requirements:
232
+ - - ">="
233
+ - !ruby/object:Gem::Version
234
+ version: '2.6'
235
+ required_rubygems_version: !ruby/object:Gem::Requirement
236
+ requirements:
237
+ - - ">="
238
+ - !ruby/object:Gem::Version
239
+ version: '0'
240
+ requirements: []
241
+ rubygems_version: 3.0.3
242
+ signing_key:
243
+ specification_version: 4
244
+ summary: Official Ubersicht SDK
245
+ test_files: []