teams_connector 0.1.5 → 0.1.6
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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +10 -13
- data/.rubocop.yml +19 -0
- data/CHANGES.md +13 -12
- data/Gemfile +10 -2
- data/README.md +11 -11
- data/Rakefile +9 -3
- data/bin/console +9 -8
- data/lib/core_ext/object.rb +28 -0
- data/lib/teams_connector/builder.rb +32 -16
- data/lib/teams_connector/configuration.rb +12 -9
- data/lib/teams_connector/matchers/expected_number.rb +51 -0
- data/lib/teams_connector/matchers/have_sent_notification_to.rb +60 -94
- data/lib/teams_connector/matchers/with.rb +18 -0
- data/lib/teams_connector/matchers.rb +5 -2
- data/lib/teams_connector/notification/adaptive_card.rb +15 -10
- data/lib/teams_connector/notification/message.rb +10 -6
- data/lib/teams_connector/notification.rb +23 -16
- data/lib/teams_connector/post_job.rb +16 -0
- data/lib/teams_connector/rspec.rb +3 -1
- data/lib/teams_connector/testing.rb +3 -1
- data/lib/teams_connector/version.rb +3 -1
- data/lib/teams_connector.rb +12 -10
- data/teams_connector.gemspec +18 -30
- metadata +16 -92
- data/lib/teams_connector/post_worker.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec351030eb8a9eb02df72d9807fec0ebb3040bb08fa2046182de96ccc01f0523
|
4
|
+
data.tar.gz: 54c5be1eeccab5fc6f0b34f0a226c896d1f96c9902d662c1c99cf88844216f4a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b2e9831c57a4961021faae608a4e2ee0b1752ad4b2ca05adc974a8ac44421ae30175217bfc3705b0810918aef694b4ac1e1323a0ad7945b79bdb313c061b77c3
|
7
|
+
data.tar.gz: c6935973c88bbdf258dbbcda1f5341209eca6ce53738579f95d3da64cbe8cbc28e7c4a499f7e21f14109a6c53d3eadc8c44f4ce5ef9359809f92c0aa6d1d5e0b
|
data/.github/workflows/ruby.yml
CHANGED
@@ -15,21 +15,18 @@ on:
|
|
15
15
|
|
16
16
|
jobs:
|
17
17
|
test:
|
18
|
-
|
19
18
|
runs-on: ubuntu-latest
|
19
|
+
name: Ruby ${{ matrix.ruby-version }}
|
20
20
|
strategy:
|
21
21
|
matrix:
|
22
|
-
ruby-version: ['2.6', '2.7', '3.0']
|
22
|
+
ruby-version: ['2.6', '2.7', '3.0', '3.2']
|
23
23
|
|
24
24
|
steps:
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
34
|
-
- name: Run tests
|
35
|
-
run: bundle exec rake
|
25
|
+
- uses: actions/checkout@v3
|
26
|
+
- name: Set up Ruby
|
27
|
+
uses: ruby/setup-ruby@v1
|
28
|
+
with:
|
29
|
+
ruby-version: ${{ matrix.ruby-version }}
|
30
|
+
bundler-cache: true
|
31
|
+
- name: Run the default task
|
32
|
+
run: bundle exec rake
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
AllCops:
|
2
|
+
NewCops: enable
|
3
|
+
TargetRubyVersion: 2.6
|
4
|
+
|
5
|
+
Layout/LineLength:
|
6
|
+
Exclude:
|
7
|
+
- spec/**/*
|
8
|
+
|
9
|
+
Metrics/BlockLength:
|
10
|
+
Exclude:
|
11
|
+
- spec/**/*
|
12
|
+
- '*.gemspec'
|
13
|
+
|
14
|
+
Style/CaseEquality:
|
15
|
+
Exclude:
|
16
|
+
- lib/teams_connector/matchers/have_sent_notification_to.rb
|
17
|
+
|
18
|
+
Style/Documentation:
|
19
|
+
Enabled: false
|
data/CHANGES.md
CHANGED
@@ -1,26 +1,28 @@
|
|
1
1
|
# Teams Connector Changelog
|
2
2
|
|
3
|
-
0.1.
|
4
|
-
|
3
|
+
## 0.1.6
|
4
|
+
- Enable Github actions for ruby 3
|
5
|
+
- Enable Rubocop linting
|
6
|
+
- Bump compatibility to sidekiq 7
|
7
|
+
- More stringent RuboCop tests
|
8
|
+
- Enable RubyGems MFA required
|
9
|
+
|
10
|
+
## 0.1.5
|
5
11
|
- RSpec Matchers for testing, thanks to [rspec-rails](https://github.com/rspec/rspec-rails) for their ActionCable `have_broadcasted_to` matcher as reference
|
6
12
|
- README update for testing
|
7
13
|
- Sometimes use testing mode internally
|
8
14
|
- Fixed code smells
|
9
15
|
|
10
|
-
0.1.4
|
11
|
-
---
|
16
|
+
## 0.1.4
|
12
17
|
- Add rudimentary testing method
|
13
18
|
|
14
|
-
0.1.3
|
15
|
-
---
|
19
|
+
## 0.1.3
|
16
20
|
- Allow sending a notification to multiple channels at the same time
|
17
21
|
|
18
|
-
0.1.2
|
19
|
-
---
|
22
|
+
## 0.1.2
|
20
23
|
- Use `TeamsConnector::Configuration#load_from_rails_credentials` to load encrypted channel URLs in your Rails environment
|
21
24
|
|
22
|
-
0.1.1
|
23
|
-
---
|
25
|
+
## 0.1.1
|
24
26
|
- Adaptive Card Notification
|
25
27
|
- Send a more complex Adaptive Card instead of the basic Message Card
|
26
28
|
- Builder
|
@@ -35,7 +37,6 @@
|
|
35
37
|
- Change constructor from numbered to named parameters
|
36
38
|
- Pretty print JSON to STDOUT with #pretty_print
|
37
39
|
|
38
|
-
0.1.0
|
39
|
-
---
|
40
|
+
## 0.1.0
|
40
41
|
- Initial commit of the Teams Connector gem
|
41
42
|
- Send messages as cards defined by JSON templates to configured Microsoft Teams channels
|
data/Gemfile
CHANGED
@@ -1,6 +1,14 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
source 'https://rubygems.org'
|
4
4
|
|
5
5
|
# Specify your gem's dependencies in teams_connector.gemspec
|
6
6
|
gemspec
|
7
|
+
|
8
|
+
gem 'bundler', '>= 1.17'
|
9
|
+
gem 'rake', '>= 10.0'
|
10
|
+
gem 'rspec', '>= 3.0'
|
11
|
+
gem 'rubocop'
|
12
|
+
gem 'sidekiq', '< 8'
|
13
|
+
gem 'simplecov'
|
14
|
+
gem 'webmock'
|
data/README.md
CHANGED
@@ -81,11 +81,11 @@ The default templates will be still available so you can mix and match.
|
|
81
81
|
|
82
82
|
#### Default templates
|
83
83
|
|
84
|
-
Template name
|
85
|
-
|
86
|
-
:adaptive_card | A card with the body of an adaptive card for more complex scenarios
|
87
|
-
:facts_card
|
88
|
-
:test_card
|
84
|
+
| Template name | Description |
|
85
|
+
|----------------|---------------------------------------------------------------------|
|
86
|
+
| :adaptive_card | A card with the body of an adaptive card for more complex scenarios |
|
87
|
+
| :facts_card | A card with title, subtitle and a list of facts |
|
88
|
+
| :test_card | A simple text message without any configurable content for testing |
|
89
89
|
|
90
90
|
#### Custom Templates
|
91
91
|
|
@@ -125,25 +125,25 @@ The request elements have the following structure:
|
|
125
125
|
```
|
126
126
|
|
127
127
|
### RSpec Matcher
|
128
|
-
TeamsConnector provides the `
|
128
|
+
TeamsConnector provides the `sent_notification_to?(channel = nil, template = nil)` matcher for RSpec.
|
129
129
|
It is available by adding `require "teams_connector/rspec"` to your `spec_helper.rb`.
|
130
130
|
The matcher supports filtering notifications by channel and template. If one is not given, it does not filter the notifications by it.
|
131
|
-
There exists the alias `send_notification_to` for `
|
131
|
+
There exists the alias `send_notification_to` and `have_sent_notification_to` for `sent_notification_to?`.
|
132
132
|
|
133
133
|
```ruby
|
134
134
|
it "has sent exactly one notification to the channel" do
|
135
|
-
expect { notification.deliver_later }.to
|
135
|
+
expect { notification.deliver_later }.to sent_notification_to?(:channel)
|
136
136
|
end
|
137
137
|
```
|
138
138
|
|
139
139
|
#### Expecting number of notifications
|
140
|
-
By default `
|
140
|
+
By default `sent_notification_to?` expects exactly one matching notification.
|
141
141
|
You can change the expected amount by chaining `exactly`, `at_least` or `at_most`.
|
142
142
|
|
143
143
|
Example:
|
144
144
|
```ruby
|
145
145
|
it "has sent less than 10 notifications to the channel" do
|
146
|
-
expect { notification.deliver_later }.to
|
146
|
+
expect { notification.deliver_later }.to sent_notification_to?(:channel).at_most(10)
|
147
147
|
end
|
148
148
|
```
|
149
149
|
|
@@ -163,7 +163,7 @@ Example:
|
|
163
163
|
```ruby
|
164
164
|
expect {
|
165
165
|
notification(:default, :test_card).deliver_later
|
166
|
-
}.to
|
166
|
+
}.to sent_notification_to?(:default).with { |content, notification|
|
167
167
|
expect(notification[:channel]).to eq :default
|
168
168
|
expect(notification[:template]).to eq :test_card
|
169
169
|
expect(content["sections"]).to include(hash_including("activityTitle", "activitySubtitle", "facts", "markdown" => true))
|
data/Rakefile
CHANGED
@@ -1,6 +1,12 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rspec/core/rake_task'
|
3
5
|
|
4
6
|
RSpec::Core::RakeTask.new(:spec)
|
5
7
|
|
6
|
-
|
8
|
+
require 'rubocop/rake_task'
|
9
|
+
|
10
|
+
RuboCop::RakeTask.new
|
11
|
+
|
12
|
+
task default: %i[spec rubocop]
|
data/bin/console
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
|
-
require
|
4
|
-
require
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'teams_connector'
|
5
6
|
|
6
7
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
8
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -11,26 +12,26 @@ require "teams_connector"
|
|
11
12
|
# Pry.start
|
12
13
|
|
13
14
|
TeamsConnector.configure do |config|
|
14
|
-
if File.exist?(
|
15
|
+
if File.exist?('channels.yml')
|
15
16
|
# NOTE(Keune): The channels available in the console can be set in the file channels.yml. It contains a simple
|
16
17
|
# mapping of channel identifiers to webhook URLs. The channel identifier is loaded as a symbol.
|
17
18
|
#
|
18
19
|
# Example: default: "<INSERT YOUR WEBHOOK URL HERE>"
|
19
|
-
puts
|
20
|
+
puts 'Load channels specified by file'
|
20
21
|
|
21
|
-
require
|
22
|
-
channels = YAML.load_file
|
22
|
+
require 'yaml'
|
23
|
+
channels = YAML.load_file 'channels.yml'
|
23
24
|
channels.each do |k, v|
|
24
25
|
config.channel k.to_sym, v
|
25
26
|
end
|
26
27
|
else
|
27
28
|
# NOTE(Keune): Specify the channels you want to have available in the console
|
28
|
-
config.channel :default,
|
29
|
+
config.channel :default, '<INSERT YOUR WEBHOOK URL HERE>'
|
29
30
|
end
|
30
31
|
config.default = :default
|
31
32
|
config.always_use_default = true
|
32
33
|
config.method = :direct
|
33
34
|
end
|
34
35
|
|
35
|
-
require
|
36
|
+
require 'irb'
|
36
37
|
IRB.start(__FILE__)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# NOTE(Keune): This core extension is taken directly from rails to support a single usage of #present?
|
4
|
+
# See rails/activesupport/lib/active_support/core_ext/object/blank.rb
|
5
|
+
class Object
|
6
|
+
# An object is blank if it's false, empty, or a whitespace string.
|
7
|
+
# For example, +nil+, '', ' ', [], {}, and +false+ are all blank.
|
8
|
+
#
|
9
|
+
# This simplifies
|
10
|
+
#
|
11
|
+
# !address || address.empty?
|
12
|
+
#
|
13
|
+
# to
|
14
|
+
#
|
15
|
+
# address.blank?
|
16
|
+
#
|
17
|
+
# @return [true, false]
|
18
|
+
def blank?
|
19
|
+
respond_to?(:empty?) ? !!empty? : false
|
20
|
+
end
|
21
|
+
|
22
|
+
# An object is present if it's not blank.
|
23
|
+
#
|
24
|
+
# @return [true, false]
|
25
|
+
def present?
|
26
|
+
!blank?
|
27
|
+
end
|
28
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module TeamsConnector
|
2
4
|
class Builder
|
3
5
|
attr_accessor :type, :content
|
@@ -15,8 +17,8 @@ module TeamsConnector
|
|
15
17
|
@content = text
|
16
18
|
end
|
17
19
|
|
18
|
-
def self.container
|
19
|
-
TeamsConnector::Builder.new { |entry| entry.container
|
20
|
+
def self.container(&block)
|
21
|
+
TeamsConnector::Builder.new { |entry| entry.container(&block) }
|
20
22
|
end
|
21
23
|
|
22
24
|
def container
|
@@ -25,8 +27,8 @@ module TeamsConnector
|
|
25
27
|
yield @content
|
26
28
|
end
|
27
29
|
|
28
|
-
def self.facts
|
29
|
-
TeamsConnector::Builder.new { |entry| entry.facts
|
30
|
+
def self.facts(&block)
|
31
|
+
TeamsConnector::Builder.new { |entry| entry.facts(&block) }
|
30
32
|
end
|
31
33
|
|
32
34
|
def facts
|
@@ -38,23 +40,37 @@ module TeamsConnector
|
|
38
40
|
def result
|
39
41
|
case @type
|
40
42
|
when :container
|
41
|
-
|
42
|
-
type: "Container",
|
43
|
-
items: @content.map { |element| element.result }
|
44
|
-
}
|
43
|
+
result_container
|
45
44
|
when :facts
|
46
|
-
|
47
|
-
type: "FactSet",
|
48
|
-
facts: @content.map { |fact| { title: fact[0], value: fact[1] } }
|
49
|
-
}
|
45
|
+
result_facts
|
50
46
|
when :text
|
51
|
-
|
52
|
-
type: "TextBlock",
|
53
|
-
text: @content
|
54
|
-
}
|
47
|
+
result_text
|
55
48
|
else
|
56
49
|
raise TypeError, "The type #{@type} is not supported by the TeamsConnector::Builder"
|
57
50
|
end
|
58
51
|
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def result_container
|
56
|
+
{
|
57
|
+
type: 'Container',
|
58
|
+
items: @content.map(&:result)
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
def result_facts
|
63
|
+
{
|
64
|
+
type: 'FactSet',
|
65
|
+
facts: @content.map { |fact| { title: fact[0], value: fact[1] } }
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
def result_text
|
70
|
+
{
|
71
|
+
type: 'TextBlock',
|
72
|
+
text: @content
|
73
|
+
}
|
74
|
+
end
|
59
75
|
end
|
60
76
|
end
|
@@ -1,8 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module TeamsConnector
|
2
4
|
class Configuration
|
3
|
-
DEFAULT_TEMPLATE_DIR = %w[templates teams_connector]
|
5
|
+
DEFAULT_TEMPLATE_DIR = %w[templates teams_connector].freeze
|
4
6
|
|
5
|
-
|
7
|
+
attr_reader :default, :method
|
8
|
+
attr_accessor :channels, :always_use_default, :template_dir, :color
|
6
9
|
|
7
10
|
def initialize
|
8
11
|
@default = nil
|
@@ -10,28 +13,28 @@ module TeamsConnector
|
|
10
13
|
@always_use_default = false
|
11
14
|
@method = :direct
|
12
15
|
@template_dir = DEFAULT_TEMPLATE_DIR
|
13
|
-
@color =
|
16
|
+
@color = '3f95b5'
|
14
17
|
end
|
15
18
|
|
16
19
|
def default=(channel)
|
17
20
|
raise ArgumentError, "Desired default channel '#{channel}' is not configured" unless @channels.key?(channel)
|
21
|
+
|
18
22
|
@default = channel
|
19
23
|
end
|
20
24
|
|
21
25
|
def method=(method)
|
22
|
-
raise ArgumentError, "Method '#{method
|
23
|
-
raise ArgumentError,
|
26
|
+
raise ArgumentError, "Method '#{method}' is not supported" unless %i[direct sidekiq testing].include? method
|
27
|
+
raise ArgumentError, 'Sidekiq is not available' if method == :sidekiq && !defined? Sidekiq
|
28
|
+
|
24
29
|
@method = method
|
25
30
|
end
|
26
31
|
|
27
32
|
def channel(name, url)
|
28
|
-
@channels[name] = url
|
33
|
+
@channels[name] = url
|
29
34
|
end
|
30
35
|
|
31
36
|
def load_from_rails_credentials
|
32
|
-
unless defined? Rails
|
33
|
-
raise RuntimeError, "This method is only available in Ruby on Rails."
|
34
|
-
end
|
37
|
+
raise 'This method is only available in Ruby on Rails.' unless defined? Rails
|
35
38
|
|
36
39
|
webhook_urls = Rails.application.credentials.teams_connector!
|
37
40
|
webhook_urls.each do |entry|
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TeamsConnector
|
4
|
+
module Matchers
|
5
|
+
module ExpectedNumber
|
6
|
+
def exactly(count)
|
7
|
+
set_expected_number(:exactly, count)
|
8
|
+
self
|
9
|
+
end
|
10
|
+
|
11
|
+
def at_least(count)
|
12
|
+
set_expected_number(:at_least, count)
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def at_most(count)
|
17
|
+
set_expected_number(:at_most, count)
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def times
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def once
|
26
|
+
exactly(:once)
|
27
|
+
end
|
28
|
+
|
29
|
+
def twice
|
30
|
+
exactly(:twice)
|
31
|
+
end
|
32
|
+
|
33
|
+
def thrice
|
34
|
+
exactly(:thrice)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def set_expected_number(relativity, count)
|
40
|
+
@expectation_type = relativity
|
41
|
+
@expected_number =
|
42
|
+
case count
|
43
|
+
when :once then 1
|
44
|
+
when :twice then 2
|
45
|
+
when :thrice then 3
|
46
|
+
else Integer(count)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -1,7 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'expected_number'
|
4
|
+
require_relative 'with'
|
5
|
+
|
1
6
|
module TeamsConnector
|
2
7
|
module Matchers
|
3
8
|
class HaveSentNotificationTo
|
4
9
|
include RSpec::Matchers::Composable
|
10
|
+
include TeamsConnector::Matchers::ExpectedNumber
|
11
|
+
include TeamsConnector::Matchers::With
|
12
|
+
|
13
|
+
class RelativityNotSupported < StandardError; end
|
5
14
|
|
6
15
|
def initialize(channel, template)
|
7
16
|
@filter = {
|
@@ -14,60 +23,17 @@ module TeamsConnector
|
|
14
23
|
set_expected_number(:exactly, 1)
|
15
24
|
end
|
16
25
|
|
17
|
-
def with(data = nil, &block)
|
18
|
-
@data = data
|
19
|
-
@block = block if block
|
20
|
-
self
|
21
|
-
end
|
22
|
-
|
23
|
-
def with_template(template = nil)
|
24
|
-
@template_data = template
|
25
|
-
self
|
26
|
-
end
|
27
|
-
|
28
|
-
def exactly(count)
|
29
|
-
set_expected_number(:exactly, count)
|
30
|
-
self
|
31
|
-
end
|
32
|
-
|
33
|
-
def at_least(count)
|
34
|
-
set_expected_number(:at_least, count)
|
35
|
-
self
|
36
|
-
end
|
37
|
-
|
38
|
-
def at_most(count)
|
39
|
-
set_expected_number(:at_most, count)
|
40
|
-
self
|
41
|
-
end
|
42
|
-
|
43
|
-
def times
|
44
|
-
self
|
45
|
-
end
|
46
|
-
|
47
|
-
def once
|
48
|
-
exactly(:once)
|
49
|
-
end
|
50
|
-
|
51
|
-
def twice
|
52
|
-
exactly(:twice)
|
53
|
-
end
|
54
|
-
|
55
|
-
def thrice
|
56
|
-
exactly(:thrice)
|
57
|
-
end
|
58
|
-
|
59
26
|
def failure_message
|
60
|
-
"expected to send #{base_message}"
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
msg << "\n #{data}"
|
68
|
-
end
|
69
|
-
end
|
27
|
+
msg = "expected to send #{base_message}"
|
28
|
+
if @unmatching_ntfcts.any?
|
29
|
+
msg += "\nSent notifications"
|
30
|
+
msg += " to #{@filter[:channel]}" if @filter[:channel]
|
31
|
+
msg += " of #{@filter[:template]}" if @filter[:template]
|
32
|
+
msg += ':'
|
33
|
+
@unmatching_ntfcts.each { |data| msg += "\n #{data}" }
|
70
34
|
end
|
35
|
+
|
36
|
+
msg
|
71
37
|
end
|
72
38
|
|
73
39
|
def failure_message_when_negated
|
@@ -75,19 +41,11 @@ module TeamsConnector
|
|
75
41
|
end
|
76
42
|
|
77
43
|
def matches?(expectation)
|
78
|
-
|
79
|
-
|
80
|
-
expectation.call
|
81
|
-
in_block_notifications = TeamsConnector.testing.requests.drop(original_count)
|
82
|
-
else
|
83
|
-
in_block_notifications = expectation
|
44
|
+
matching_notifications = in_block_notifications(expectation).select do |msg|
|
45
|
+
@filter.map { |k, v| msg[k] == v unless v.nil? }.compact.all?
|
84
46
|
end
|
85
47
|
|
86
|
-
|
87
|
-
@filter.map { |k, v| msg[k] === v unless v.nil? }.compact.all?
|
88
|
-
}
|
89
|
-
|
90
|
-
check(in_block_notifications)
|
48
|
+
check(matching_notifications)
|
91
49
|
end
|
92
50
|
|
93
51
|
def supports_block_expectations?
|
@@ -98,56 +56,64 @@ module TeamsConnector
|
|
98
56
|
|
99
57
|
def check(notifications)
|
100
58
|
@matching_ntfcts, @unmatching_ntfcts = notifications.partition do |ntfct|
|
101
|
-
|
59
|
+
check_partition(ntfct)
|
60
|
+
end
|
102
61
|
|
103
|
-
|
62
|
+
@matching_count = @matching_ntfcts.size
|
104
63
|
|
105
|
-
|
106
|
-
|
107
|
-
@block.call(decoded, ntfct)
|
108
|
-
result &= true
|
109
|
-
else
|
110
|
-
result = false
|
111
|
-
end
|
64
|
+
check_result
|
65
|
+
end
|
112
66
|
|
113
|
-
|
114
|
-
|
67
|
+
def check_partition(ntfct)
|
68
|
+
result = true
|
115
69
|
|
116
|
-
@
|
70
|
+
result &= ntfct[:template] == @template_data unless @template_data.nil?
|
117
71
|
|
72
|
+
decoded = JSON.parse(ntfct[:content])
|
73
|
+
if @data.nil? || @data === decoded
|
74
|
+
@block.call(decoded, ntfct)
|
75
|
+
result & true
|
76
|
+
else
|
77
|
+
false
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def check_result
|
118
82
|
case @expectation_type
|
119
83
|
when :exactly then @expected_number == @matching_count
|
120
84
|
when :at_most then @expected_number >= @matching_count
|
121
85
|
when :at_least then @expected_number <= @matching_count
|
86
|
+
else
|
87
|
+
raise RelativityNotSupported
|
122
88
|
end
|
123
89
|
end
|
124
90
|
|
125
|
-
def
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
end
|
91
|
+
def in_block_notifications(expectation)
|
92
|
+
if expectation.is_a? Proc
|
93
|
+
original_count = TeamsConnector.testing.requests.size
|
94
|
+
expectation.call
|
95
|
+
TeamsConnector.testing.requests.drop(original_count)
|
96
|
+
else
|
97
|
+
expectation
|
98
|
+
end
|
134
99
|
end
|
135
100
|
|
136
101
|
def base_message
|
137
|
-
"#{message_expectation_modifier} #{@expected_number} notifications"
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
end
|
102
|
+
msg = String("#{message_expectation_modifier} #{@expected_number} notifications")
|
103
|
+
msg += " to #{@filter[:channel]}" if @filter[:channel]
|
104
|
+
msg += " of #{@filter[:template]}" if @filter[:template]
|
105
|
+
msg += " with template #{@template_data}" if @template_data
|
106
|
+
msg += " with content #{data_description(@data)}" if @data
|
107
|
+
msg + ", but sent #{@matching_count}"
|
144
108
|
end
|
145
109
|
|
146
110
|
def message_expectation_modifier
|
147
111
|
case @expectation_type
|
148
|
-
when :exactly then
|
149
|
-
when :at_most then
|
150
|
-
when :at_least then
|
112
|
+
when :exactly then 'exactly'
|
113
|
+
when :at_most then 'at most'
|
114
|
+
when :at_least then 'at least'
|
115
|
+
else
|
116
|
+
raise RelativityNotSupported
|
151
117
|
end
|
152
118
|
end
|
153
119
|
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TeamsConnector
|
4
|
+
module Matchers
|
5
|
+
module With
|
6
|
+
def with(data = nil, &block)
|
7
|
+
@data = data
|
8
|
+
@block = block if block
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
def with_template(template = nil)
|
13
|
+
@template_data = template
|
14
|
+
self
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -1,11 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'teams_connector/matchers/have_sent_notification_to'
|
2
4
|
|
3
5
|
module TeamsConnector
|
4
6
|
module Matchers
|
5
|
-
def
|
7
|
+
def sent_notification_to?(channel = nil, template = nil)
|
6
8
|
HaveSentNotificationTo.new(channel, template)
|
7
9
|
end
|
8
10
|
|
9
|
-
|
11
|
+
alias have_sent_notification_to sent_notification_to?
|
12
|
+
alias send_notification_to have_sent_notification_to
|
10
13
|
end
|
11
14
|
end
|
@@ -1,15 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module TeamsConnector
|
2
|
-
class Notification
|
3
|
-
|
4
|
+
class Notification
|
5
|
+
class AdaptiveCard < Notification
|
6
|
+
attr_accessor :content
|
4
7
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
def initialize(template: :adaptive_card, content: {}, channel: TeamsConnector.configuration.default)
|
9
|
+
super(template: template, channels: channel)
|
10
|
+
@content =
|
11
|
+
if content.instance_of? TeamsConnector::Builder
|
12
|
+
{
|
13
|
+
card: [content.result]
|
14
|
+
}
|
15
|
+
else
|
16
|
+
content
|
17
|
+
end
|
13
18
|
end
|
14
19
|
end
|
15
20
|
end
|
@@ -1,11 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module TeamsConnector
|
2
|
-
class Notification
|
3
|
-
|
4
|
+
class Notification
|
5
|
+
class Message < Notification
|
6
|
+
attr_accessor :summary, :content
|
4
7
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
8
|
+
def initialize(template, summary, content = {}, channel = TeamsConnector.configuration.default)
|
9
|
+
super(template: template, channels: channel)
|
10
|
+
@summary = summary
|
11
|
+
@content = content
|
12
|
+
end
|
9
13
|
end
|
10
14
|
end
|
11
15
|
end
|
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'erb'
|
2
4
|
require 'json'
|
3
5
|
require 'net/http'
|
4
|
-
require 'teams_connector/
|
6
|
+
require 'teams_connector/post_job' if defined? Sidekiq
|
5
7
|
|
6
8
|
module TeamsConnector
|
7
9
|
class Notification
|
@@ -22,17 +24,7 @@ module TeamsConnector
|
|
22
24
|
|
23
25
|
channels = TeamsConnector.configuration.always_use_default ? [TeamsConnector.configuration.default] : @channels
|
24
26
|
channels.each do |channel|
|
25
|
-
|
26
|
-
raise ArgumentError, "The Teams channel '#{channel}' is not available in the configuration." if url.nil?
|
27
|
-
|
28
|
-
if TeamsConnector.configuration.method == :sidekiq
|
29
|
-
TeamsConnector::PostWorker.perform_async(url, content)
|
30
|
-
elsif TeamsConnector.configuration.method == :testing
|
31
|
-
TeamsConnector.testing.perform_request channel, @template, content
|
32
|
-
else
|
33
|
-
response = Net::HTTP.post(URI(url), content, { "Content-Type" => "application/json" })
|
34
|
-
response.value
|
35
|
-
end
|
27
|
+
deliver_channel(channel, content)
|
36
28
|
end
|
37
29
|
end
|
38
30
|
|
@@ -48,11 +40,26 @@ module TeamsConnector
|
|
48
40
|
|
49
41
|
private
|
50
42
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
|
43
|
+
def deliver_channel(channel, content)
|
44
|
+
url = TeamsConnector.configuration.channels[channel]
|
45
|
+
raise ArgumentError, "The Teams channel '#{channel}' is not available in the configuration." if url.nil?
|
46
|
+
|
47
|
+
case TeamsConnector.configuration.method
|
48
|
+
when :sidekiq then TeamsConnector::PostJob.perform_async(url, content)
|
49
|
+
when :testing then TeamsConnector.testing.perform_request channel, @template, content
|
50
|
+
else
|
51
|
+
response = Net::HTTP.post(URI(url), content, { 'Content-Type' => 'application/json' })
|
52
|
+
response.value
|
55
53
|
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def find_template
|
57
|
+
path = File.join(TeamsConnector.project_root, *TeamsConnector.configuration.template_dir, "#{@template}.json.erb")
|
58
|
+
return path if File.exist? path
|
59
|
+
|
60
|
+
path = File.join(TeamsConnector.gem_root,
|
61
|
+
*TeamsConnector::Configuration::DEFAULT_TEMPLATE_DIR,
|
62
|
+
"#{@template}.json.erb")
|
56
63
|
raise ArgumentError, "The template '#{@template}' is not available." unless File.exist? path
|
57
64
|
|
58
65
|
path
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'sidekiq/job'
|
5
|
+
|
6
|
+
module TeamsConnector
|
7
|
+
class PostJob
|
8
|
+
# === Includes ===
|
9
|
+
include Sidekiq::Job
|
10
|
+
|
11
|
+
def perform(url, content)
|
12
|
+
response = Net::HTTP.post(URI(url), content, { 'Content-Type' => 'application/json' })
|
13
|
+
response.value
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module TeamsConnector
|
2
4
|
class Testing
|
3
5
|
attr_reader :requests
|
@@ -7,7 +9,7 @@ module TeamsConnector
|
|
7
9
|
end
|
8
10
|
|
9
11
|
def perform_request(channel, template, content)
|
10
|
-
@requests.push({channel: channel, content: content, template: template, time: Time.now})
|
12
|
+
@requests.push({ channel: channel, content: content, template: template, time: Time.now })
|
11
13
|
end
|
12
14
|
end
|
13
15
|
end
|
data/lib/teams_connector.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'core_ext/object'
|
1
4
|
require 'teams_connector/configuration'
|
2
5
|
require 'teams_connector/version'
|
3
6
|
require 'teams_connector/notification'
|
@@ -7,7 +10,7 @@ require 'teams_connector/builder'
|
|
7
10
|
|
8
11
|
module TeamsConnector
|
9
12
|
class << self
|
10
|
-
|
13
|
+
attr_writer :configuration, :testing
|
11
14
|
end
|
12
15
|
|
13
16
|
def self.configuration
|
@@ -33,19 +36,18 @@ module TeamsConnector
|
|
33
36
|
end
|
34
37
|
|
35
38
|
def self.project_root
|
36
|
-
if defined?(Rails)
|
37
|
-
|
38
|
-
end
|
39
|
-
|
40
|
-
if defined?(Bundler)
|
41
|
-
return Bundler.root
|
42
|
-
end
|
39
|
+
return Rails.root if defined?(Rails)
|
40
|
+
return Bundler.root if defined?(Bundler)
|
43
41
|
|
44
42
|
Dir.pwd
|
45
43
|
end
|
46
44
|
|
47
45
|
def self.gem_root
|
48
|
-
spec = Gem::Specification.find_by_name(
|
49
|
-
|
46
|
+
spec = Gem::Specification.find_by_name('teams_connector')
|
47
|
+
begin
|
48
|
+
spec.gem_dir
|
49
|
+
rescue NoMethodError
|
50
|
+
project_root
|
51
|
+
end
|
50
52
|
end
|
51
53
|
end
|
data/teams_connector.gemspec
CHANGED
@@ -1,42 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
|
-
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require "teams_connector/version"
|
3
|
+
require_relative 'lib/teams_connector/version'
|
5
4
|
|
6
5
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
6
|
+
spec.name = 'teams_connector'
|
8
7
|
spec.version = TeamsConnector::VERSION
|
9
|
-
spec.
|
10
|
-
spec.
|
8
|
+
spec.required_ruby_version = '>= 2.6'
|
9
|
+
spec.authors = ['Lucas Keune']
|
10
|
+
spec.email = ['lucas.keune@qurasoft.de']
|
11
11
|
|
12
|
-
spec.summary =
|
13
|
-
spec.description =
|
14
|
-
spec.homepage =
|
15
|
-
spec.license =
|
12
|
+
spec.summary = 'Simple connector to send messages and cards to Microsoft Teams channels'
|
13
|
+
spec.description = 'Send templated messages or adaptive cards to Microsoft Teams channels'
|
14
|
+
spec.homepage = 'https://github.com/Qurasoft/teams_connector'
|
15
|
+
spec.license = 'MIT'
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
spec.metadata["source_code_uri"] = "https://github.com/Qurasoft/teams_connector"
|
22
|
-
spec.metadata["changelog_uri"] = "https://github.com/Qurasoft/teams_connector/blob/main/CHANGES.md"
|
23
|
-
else
|
24
|
-
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
25
|
-
end
|
17
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
18
|
+
spec.metadata['source_code_uri'] = 'https://github.com/Qurasoft/teams_connector'
|
19
|
+
spec.metadata['changelog_uri'] = 'https://github.com/Qurasoft/teams_connector/blob/main/CHANGES.md'
|
20
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
26
21
|
|
27
22
|
# Specify which files should be added to the gem when it is released.
|
28
23
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
29
|
-
spec.files
|
24
|
+
spec.files = Dir.chdir(__dir__) do
|
30
25
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
31
26
|
end
|
32
|
-
spec.bindir =
|
33
|
-
spec.executables = spec.files.grep(%r{^
|
34
|
-
spec.require_paths = [
|
35
|
-
|
36
|
-
spec.add_development_dependency "bundler", ">= 1.17"
|
37
|
-
spec.add_development_dependency "rake", ">= 10.0"
|
38
|
-
spec.add_development_dependency "rspec", ">= 3.0"
|
39
|
-
spec.add_development_dependency "webmock"
|
40
|
-
spec.add_development_dependency "sidekiq"
|
41
|
-
spec.add_development_dependency "simplecov"
|
27
|
+
spec.bindir = 'bin'
|
28
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
29
|
+
spec.require_paths = ['lib']
|
42
30
|
end
|
metadata
CHANGED
@@ -1,103 +1,22 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: teams_connector
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lucas Keune
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: bundler
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '1.17'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '1.17'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: rake
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '10.0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '10.0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: rspec
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '3.0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '3.0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: webmock
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: sidekiq
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: simplecov
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
11
|
+
date: 2023-12-01 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
97
13
|
description: Send templated messages or adaptive cards to Microsoft Teams channels
|
98
14
|
email:
|
99
15
|
- lucas.keune@qurasoft.de
|
100
|
-
executables:
|
16
|
+
executables:
|
17
|
+
- ".gitignore"
|
18
|
+
- console
|
19
|
+
- setup
|
101
20
|
extensions: []
|
102
21
|
extra_rdoc_files: []
|
103
22
|
files:
|
@@ -105,6 +24,7 @@ files:
|
|
105
24
|
- ".github/workflows/ruby.yml"
|
106
25
|
- ".gitignore"
|
107
26
|
- ".rspec"
|
27
|
+
- ".rubocop.yml"
|
108
28
|
- ".travis.yml"
|
109
29
|
- CHANGES.md
|
110
30
|
- Gemfile
|
@@ -114,15 +34,18 @@ files:
|
|
114
34
|
- bin/.gitignore
|
115
35
|
- bin/console
|
116
36
|
- bin/setup
|
37
|
+
- lib/core_ext/object.rb
|
117
38
|
- lib/teams_connector.rb
|
118
39
|
- lib/teams_connector/builder.rb
|
119
40
|
- lib/teams_connector/configuration.rb
|
120
41
|
- lib/teams_connector/matchers.rb
|
42
|
+
- lib/teams_connector/matchers/expected_number.rb
|
121
43
|
- lib/teams_connector/matchers/have_sent_notification_to.rb
|
44
|
+
- lib/teams_connector/matchers/with.rb
|
122
45
|
- lib/teams_connector/notification.rb
|
123
46
|
- lib/teams_connector/notification/adaptive_card.rb
|
124
47
|
- lib/teams_connector/notification/message.rb
|
125
|
-
- lib/teams_connector/
|
48
|
+
- lib/teams_connector/post_job.rb
|
126
49
|
- lib/teams_connector/rspec.rb
|
127
50
|
- lib/teams_connector/testing.rb
|
128
51
|
- lib/teams_connector/version.rb
|
@@ -137,6 +60,7 @@ metadata:
|
|
137
60
|
homepage_uri: https://github.com/Qurasoft/teams_connector
|
138
61
|
source_code_uri: https://github.com/Qurasoft/teams_connector
|
139
62
|
changelog_uri: https://github.com/Qurasoft/teams_connector/blob/main/CHANGES.md
|
63
|
+
rubygems_mfa_required: 'true'
|
140
64
|
post_install_message:
|
141
65
|
rdoc_options: []
|
142
66
|
require_paths:
|
@@ -145,14 +69,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
145
69
|
requirements:
|
146
70
|
- - ">="
|
147
71
|
- !ruby/object:Gem::Version
|
148
|
-
version: '
|
72
|
+
version: '2.6'
|
149
73
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
150
74
|
requirements:
|
151
75
|
- - ">="
|
152
76
|
- !ruby/object:Gem::Version
|
153
77
|
version: '0'
|
154
78
|
requirements: []
|
155
|
-
rubygems_version: 3.
|
79
|
+
rubygems_version: 3.4.21
|
156
80
|
signing_key:
|
157
81
|
specification_version: 4
|
158
82
|
summary: Simple connector to send messages and cards to Microsoft Teams channels
|
@@ -1,14 +0,0 @@
|
|
1
|
-
require 'net/http'
|
2
|
-
require 'sidekiq/worker'
|
3
|
-
|
4
|
-
module TeamsConnector
|
5
|
-
class PostWorker
|
6
|
-
# === Includes ===
|
7
|
-
include Sidekiq::Worker
|
8
|
-
|
9
|
-
def perform(url, content)
|
10
|
-
response = Net::HTTP.post(URI(url), content, { "Content-Type": "application/json" })
|
11
|
-
response.value
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|