bns 0.1.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/._.rspec_status +0 -0
- data/CHANGELOG.md +14 -0
- data/Gemfile +6 -0
- data/README.md +159 -3
- data/lib/bns/dispatcher/discord/exceptions/invalid_webhook_token.rb +2 -2
- data/lib/bns/dispatcher/discord/implementation.rb +1 -1
- data/lib/bns/dispatcher/slack/exceptions/invalid_webhook_token.rb +16 -0
- data/lib/bns/dispatcher/slack/implementation.rb +51 -0
- data/lib/bns/dispatcher/slack/types/response.rb +21 -0
- data/lib/bns/domain/email.rb +34 -0
- data/lib/bns/domain/work_items_limit.rb +25 -0
- data/lib/bns/fetcher/base.rb +13 -0
- data/lib/bns/fetcher/imap/base.rb +70 -0
- data/lib/bns/fetcher/imap/types/response.rb +27 -0
- data/lib/bns/fetcher/imap/use_case/support_emails.rb +26 -0
- data/lib/bns/fetcher/notion/{pto.rb → base.rb} +11 -7
- data/lib/bns/fetcher/notion/types/response.rb +1 -1
- data/lib/bns/fetcher/notion/use_case/birthday_next_week.rb +41 -0
- data/lib/bns/fetcher/notion/use_case/birthday_today.rb +29 -0
- data/lib/bns/fetcher/notion/use_case/pto_next_week.rb +71 -0
- data/lib/bns/fetcher/notion/use_case/pto_today.rb +30 -0
- data/lib/bns/fetcher/notion/use_case/work_items_limit.rb +37 -0
- data/lib/bns/fetcher/postgres/base.rb +46 -0
- data/lib/bns/fetcher/postgres/helper.rb +16 -0
- data/lib/bns/fetcher/postgres/types/response.rb +42 -0
- data/lib/bns/fetcher/postgres/use_case/pto_today.rb +32 -0
- data/lib/bns/formatter/base.rb +11 -8
- data/lib/bns/formatter/birthday.rb +34 -0
- data/lib/bns/formatter/exceptions/invalid_data.rb +15 -0
- data/lib/bns/formatter/pto.rb +88 -0
- data/lib/bns/formatter/support_emails.rb +69 -0
- data/lib/bns/formatter/work_items_limit.rb +64 -0
- data/lib/bns/mapper/imap/support_emails.rb +56 -0
- data/lib/bns/mapper/notion/{birthday.rb → birthday_today.rb} +13 -21
- data/lib/bns/mapper/notion/{pto.rb → pto_today.rb} +15 -41
- data/lib/bns/mapper/notion/work_items_limit.rb +65 -0
- data/lib/bns/mapper/postgres/pto_today.rb +47 -0
- data/lib/bns/use_cases/use_cases.rb +276 -49
- data/lib/bns/version.rb +1 -1
- metadata +31 -9
- data/lib/bns/fetcher/notion/birthday.rb +0 -53
- data/lib/bns/formatter/discord/birthday.rb +0 -36
- data/lib/bns/formatter/discord/exceptions/invalid_data.rb +0 -17
- data/lib/bns/formatter/discord/pto.rb +0 -49
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../base"
|
4
|
+
|
5
|
+
module Fetcher
|
6
|
+
module Notion
|
7
|
+
##
|
8
|
+
# This class is an implementation of the Fetcher::Notion::Base interface, specifically designed
|
9
|
+
# for fetching next week Paid Time Off (PTO) data from Notion.
|
10
|
+
#
|
11
|
+
class PtoNextWeek < Notion::Base
|
12
|
+
# Implements the data fetching filter for next week PTO's data from Notion.
|
13
|
+
#
|
14
|
+
def fetch
|
15
|
+
filter = build_filter
|
16
|
+
|
17
|
+
execute(filter)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def next_week_dates
|
23
|
+
monday = next_week_monday
|
24
|
+
sunday = monday + 6
|
25
|
+
|
26
|
+
[monday, sunday]
|
27
|
+
end
|
28
|
+
|
29
|
+
def next_week_monday
|
30
|
+
today = Date.today
|
31
|
+
week_day = today.wday
|
32
|
+
|
33
|
+
days = week_day.zero? ? 1 : 8 - week_day
|
34
|
+
|
35
|
+
today + days
|
36
|
+
end
|
37
|
+
|
38
|
+
def build_filter
|
39
|
+
monday, sunday = next_week_dates
|
40
|
+
|
41
|
+
{
|
42
|
+
filter: {
|
43
|
+
or: [
|
44
|
+
belong_next_week("Desde?", monday, sunday),
|
45
|
+
belong_next_week("Hasta?", monday, sunday),
|
46
|
+
cover_next_week(monday, sunday)
|
47
|
+
]
|
48
|
+
}
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
def belong_next_week(property, after_day, before_day)
|
53
|
+
{
|
54
|
+
and: [
|
55
|
+
{ property: property, date: { on_or_after: after_day } },
|
56
|
+
{ property: property, date: { on_or_before: before_day } }
|
57
|
+
]
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def cover_next_week(monday, sunday)
|
62
|
+
{
|
63
|
+
and: [
|
64
|
+
{ property: "Hasta?", date: { on_or_after: sunday } },
|
65
|
+
{ property: "Desde?", date: { on_or_before: monday } }
|
66
|
+
]
|
67
|
+
}
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../base"
|
4
|
+
|
5
|
+
module Fetcher
|
6
|
+
module Notion
|
7
|
+
##
|
8
|
+
# This class is an implementation of the Fetcher::Notion::Base interface, specifically designed
|
9
|
+
# for fetching Paid Time Off (PTO) data from Notion.
|
10
|
+
#
|
11
|
+
class PtoToday < Notion::Base
|
12
|
+
# Implements the data fetching filter for todays PTO's data from Notion.
|
13
|
+
#
|
14
|
+
def fetch
|
15
|
+
today = Time.now.utc.strftime("%F").to_s
|
16
|
+
|
17
|
+
filter = {
|
18
|
+
filter: {
|
19
|
+
"and": [
|
20
|
+
{ property: "Desde?", date: { on_or_before: today } },
|
21
|
+
{ property: "Hasta?", date: { on_or_after: today } }
|
22
|
+
]
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
execute(filter)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../base"
|
4
|
+
|
5
|
+
module Fetcher
|
6
|
+
module Notion
|
7
|
+
##
|
8
|
+
# This class is an implementation of the Fetcher::Notion::Base interface, specifically designed
|
9
|
+
# for counting "in progress" work items from work item database in Notion.
|
10
|
+
#
|
11
|
+
class WorkItemsLimit < Notion::Base
|
12
|
+
# Implements the data fetching count of "in progress" work items from Notion.
|
13
|
+
#
|
14
|
+
def fetch
|
15
|
+
filter = {
|
16
|
+
filter: {
|
17
|
+
"and": [
|
18
|
+
{ property: "OK", formula: { string: { contains: "✅" } } },
|
19
|
+
{ "or": status_conditions }
|
20
|
+
]
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
execute(filter)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def status_conditions
|
30
|
+
[
|
31
|
+
{ property: "Status", status: { equals: "In Progress" } },
|
32
|
+
{ property: "Status", status: { equals: "On Hold" } }
|
33
|
+
]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pg"
|
4
|
+
|
5
|
+
require_relative "../base"
|
6
|
+
require_relative "./types/response"
|
7
|
+
require_relative "./helper"
|
8
|
+
|
9
|
+
module Fetcher
|
10
|
+
module Postgres
|
11
|
+
##
|
12
|
+
# This class is an implementation of the Fetcher::Base interface, specifically designed
|
13
|
+
# for fetching data from Postgres.
|
14
|
+
#
|
15
|
+
class Base < Fetcher::Base
|
16
|
+
protected
|
17
|
+
|
18
|
+
# Implements the data fetching logic from a Postgres database. It use the PG gem
|
19
|
+
# to request data from a local or external database and returns a validated response.
|
20
|
+
#
|
21
|
+
# Gem: pg (https://rubygems.org/gems/pg)
|
22
|
+
#
|
23
|
+
def execute(query)
|
24
|
+
pg_connection = PG::Connection.new(config[:connection])
|
25
|
+
|
26
|
+
pg_result = execute_query(pg_connection, query)
|
27
|
+
|
28
|
+
postgres_response = Fetcher::Postgres::Types::Response.new(pg_result)
|
29
|
+
|
30
|
+
Fetcher::Postgres::Helper.validate_response(postgres_response)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def execute_query(pg_connection, query)
|
36
|
+
if query.is_a? String
|
37
|
+
pg_connection.exec(query)
|
38
|
+
else
|
39
|
+
sentence, params = query
|
40
|
+
|
41
|
+
pg_connection.exec_params(sentence, params)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fetcher
|
4
|
+
module Postgres
|
5
|
+
##
|
6
|
+
# Provides common fuctionalities along the Postgres domain.
|
7
|
+
#
|
8
|
+
module Helper
|
9
|
+
def self.validate_response(response)
|
10
|
+
response.response.check_result
|
11
|
+
|
12
|
+
response
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fetcher
|
4
|
+
module Postgres
|
5
|
+
module Types
|
6
|
+
##
|
7
|
+
# Represents a response received from the Postgres API. It encapsulates essential information about the response,
|
8
|
+
# providing a structured way to handle and analyze it's responses.
|
9
|
+
class Response
|
10
|
+
attr_reader :status, :message, :response, :fields, :records
|
11
|
+
|
12
|
+
SUCCESS_STATUS = "PGRES_TUPLES_OK"
|
13
|
+
|
14
|
+
def initialize(response)
|
15
|
+
if response.res_status == SUCCESS_STATUS
|
16
|
+
success_response(response)
|
17
|
+
else
|
18
|
+
failure_response(response)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def success_response(response)
|
25
|
+
@status = response.res_status
|
26
|
+
@message = "success"
|
27
|
+
@response = response
|
28
|
+
@fields = response.fields
|
29
|
+
@records = response.values
|
30
|
+
end
|
31
|
+
|
32
|
+
def failure_response(response)
|
33
|
+
@status = response.res_status
|
34
|
+
@message = response.result_error_message
|
35
|
+
@response = response
|
36
|
+
@fields = nil
|
37
|
+
@records = nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../base"
|
4
|
+
|
5
|
+
module Fetcher
|
6
|
+
module Postgres
|
7
|
+
##
|
8
|
+
# This class is an implementation of the Fetcher::Postgres::Base interface, specifically designed
|
9
|
+
# for fetching Paid Time Off (PTO) data from a Postgres Database.
|
10
|
+
#
|
11
|
+
class PtoToday < Base
|
12
|
+
# Implements the data fetching query for todays PTO data from a Postgres database.
|
13
|
+
#
|
14
|
+
def fetch
|
15
|
+
execute(build_query)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def build_query
|
21
|
+
today = Time.now.utc.strftime("%F").to_s
|
22
|
+
|
23
|
+
start_time = "#{today}T00:00:00"
|
24
|
+
end_time = "#{today}T23:59:59"
|
25
|
+
|
26
|
+
where = "(start_date <= $1 AND end_date >= $1) OR (start_date>= $2 AND end_date <= $3)"
|
27
|
+
|
28
|
+
["SELECT * FROM pto WHERE #{where}", [today, start_time, end_time]]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/bns/formatter/base.rb
CHANGED
@@ -10,8 +10,17 @@ module Formatter
|
|
10
10
|
# formatters tailored to different use cases.
|
11
11
|
#
|
12
12
|
class Base
|
13
|
-
|
14
|
-
|
13
|
+
attr_reader :template
|
14
|
+
|
15
|
+
# Initializes the fetcher with essential configuration parameters.
|
16
|
+
#
|
17
|
+
def initialize(config = {})
|
18
|
+
@config = config
|
19
|
+
@template = config[:template]
|
20
|
+
end
|
21
|
+
|
22
|
+
# This method is designed to provide a specified format for data from any implementation of
|
23
|
+
# the Mapper::Base interface.
|
15
24
|
# Must be overridden by subclasses, with specific logic based on the use case.
|
16
25
|
#
|
17
26
|
# <br>
|
@@ -23,12 +32,6 @@ module Formatter
|
|
23
32
|
#
|
24
33
|
# <b>returns</b> <tt>String</tt> Formatted payload suitable for a Dispatcher::Base implementation.
|
25
34
|
#
|
26
|
-
attr_reader :template
|
27
|
-
|
28
|
-
def initialize(config = {})
|
29
|
-
@template = config[:template]
|
30
|
-
end
|
31
|
-
|
32
35
|
def format(_domain_data)
|
33
36
|
raise Domain::Exceptions::FunctionNotImplemented
|
34
37
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../domain/birthday"
|
4
|
+
require_relative "./exceptions/invalid_data"
|
5
|
+
require_relative "./base"
|
6
|
+
|
7
|
+
module Formatter
|
8
|
+
##
|
9
|
+
# This class implements methods from the Formatter::Base module, tailored to format the
|
10
|
+
# Domain::Birthday structure for a dispatcher.
|
11
|
+
class Birthday < Base
|
12
|
+
# Implements the logic for building a formatted payload with the given template for birthdays.
|
13
|
+
#
|
14
|
+
# <br>
|
15
|
+
# <b>Params:</b>
|
16
|
+
# * <tt>List<Domain::Birthday></tt> birthdays_list: list of mapped birthdays.
|
17
|
+
#
|
18
|
+
# <br>
|
19
|
+
# <b>raises</b> <tt>Formatter::Exceptions::InvalidData</tt> when invalid data is provided.
|
20
|
+
#
|
21
|
+
# <br>
|
22
|
+
# <b>returns</b> <tt>String</tt> payload: formatted payload suitable for a Dispatcher.
|
23
|
+
#
|
24
|
+
def format(birthdays_list)
|
25
|
+
raise Formatter::Exceptions::InvalidData unless birthdays_list.all? do |brithday|
|
26
|
+
brithday.is_a?(Domain::Birthday)
|
27
|
+
end
|
28
|
+
|
29
|
+
birthdays_list.reduce("") do |payload, birthday|
|
30
|
+
payload + build_template(Domain::Birthday::ATTRIBUTES, birthday)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Formatter
|
4
|
+
module Exceptions
|
5
|
+
##
|
6
|
+
# Provides a domain-specific representation for errors that occurs when trying to process invalid
|
7
|
+
# data on a Fetcher::Base implementation
|
8
|
+
#
|
9
|
+
class InvalidData < StandardError
|
10
|
+
def initialize(message = "")
|
11
|
+
super(message)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "date"
|
4
|
+
|
5
|
+
require_relative "../domain/pto"
|
6
|
+
require_relative "./exceptions/invalid_data"
|
7
|
+
require_relative "./base"
|
8
|
+
|
9
|
+
module Formatter
|
10
|
+
##
|
11
|
+
# This class implements methods from the Formatter::Base module, tailored to format the
|
12
|
+
# Domain::Pto structure for a dispatcher.
|
13
|
+
class Pto < Base
|
14
|
+
DEFAULT_TIME_ZONE = "+00:00"
|
15
|
+
|
16
|
+
# Initializes the Slack formatter with essential configuration parameters.
|
17
|
+
#
|
18
|
+
# <b>timezone</b> : expect an string with the time difference relative to the UTC. Example: "-05:00"
|
19
|
+
def initialize(config = {})
|
20
|
+
super(config)
|
21
|
+
|
22
|
+
@timezone = config[:timezone] || DEFAULT_TIME_ZONE
|
23
|
+
end
|
24
|
+
|
25
|
+
# Implements the logic for building a formatted payload with the given template for PTO's.
|
26
|
+
#
|
27
|
+
# <br>
|
28
|
+
# <b>Params:</b>
|
29
|
+
# * <tt>List<Domain::Pto></tt> pto_list: List of mapped PTO's.
|
30
|
+
#
|
31
|
+
# <br>
|
32
|
+
# <b>raises</b> <tt>Formatter::Exceptions::InvalidData</tt> when invalid data is provided.
|
33
|
+
#
|
34
|
+
# <br>
|
35
|
+
# <b>returns</b> <tt>String</tt> payload, formatted payload suitable for a Dispatcher.
|
36
|
+
#
|
37
|
+
|
38
|
+
def format(ptos_list)
|
39
|
+
raise Formatter::Exceptions::InvalidData unless ptos_list.all? { |pto| pto.is_a?(Domain::Pto) }
|
40
|
+
|
41
|
+
ptos_list.reduce("") do |payload, pto|
|
42
|
+
built_template = build_template(Domain::Pto::ATTRIBUTES, pto)
|
43
|
+
payload + format_message_by_case(built_template.gsub("\n", ""), pto)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def format_message_by_case(built_template, pto)
|
50
|
+
date_start = format_timezone(pto.start_date).strftime("%F")
|
51
|
+
date_end = format_timezone(pto.end_date).strftime("%F")
|
52
|
+
|
53
|
+
if date_start == date_end
|
54
|
+
interval = same_day_interval(pto)
|
55
|
+
day_message = today?(date_start) ? "today" : "the day #{date_start}"
|
56
|
+
|
57
|
+
"#{built_template} #{day_message} #{interval}\n"
|
58
|
+
else
|
59
|
+
"#{built_template} from #{date_start} to #{date_end}\n"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def same_day_interval(pto)
|
64
|
+
time_start = format_timezone(pto.start_date).strftime("%I:%M %P")
|
65
|
+
time_end = format_timezone(pto.end_date).strftime("%I:%M %P")
|
66
|
+
|
67
|
+
time_start == time_end ? "all day" : "from #{time_start} to #{time_end}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def format_timezone(date)
|
71
|
+
date_time = build_date(date)
|
72
|
+
|
73
|
+
Time.at(date_time, in: @timezone)
|
74
|
+
end
|
75
|
+
|
76
|
+
def today?(date)
|
77
|
+
time_now = Time.now.strftime("%F")
|
78
|
+
|
79
|
+
date == format_timezone(time_now).strftime("%F")
|
80
|
+
end
|
81
|
+
|
82
|
+
def build_date(date)
|
83
|
+
date_time = date.include?("T") ? date : "#{date}T00:00:00.000#{@timezone}"
|
84
|
+
|
85
|
+
DateTime.parse(date_time).to_time
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../domain/email"
|
4
|
+
require_relative "./exceptions/invalid_data"
|
5
|
+
require_relative "./base"
|
6
|
+
|
7
|
+
module Formatter
|
8
|
+
##
|
9
|
+
# This class implements methods from the Formatter::Base module, tailored to format the
|
10
|
+
# Domain::Email structure for a dispatcher.
|
11
|
+
class SupportEmails < Base
|
12
|
+
DEFAULT_TIME_ZONE = "+00:00"
|
13
|
+
|
14
|
+
# Initializes the formatter with essential configuration parameters.
|
15
|
+
#
|
16
|
+
# <b>timezone</b> : expect an string with the time difference relative to the UTC. Example: "-05:00"
|
17
|
+
def initialize(config = {})
|
18
|
+
super(config)
|
19
|
+
|
20
|
+
@timezone = config[:timezone] || DEFAULT_TIME_ZONE
|
21
|
+
@frecuency = config[:frecuency]
|
22
|
+
end
|
23
|
+
|
24
|
+
# Implements the logic for building a formatted payload with the given template for support emails.
|
25
|
+
#
|
26
|
+
# <br>
|
27
|
+
# <b>Params:</b>
|
28
|
+
# * <tt>List<Domain::Email></tt> support_emails_list: list of support emails.
|
29
|
+
#
|
30
|
+
# <br>
|
31
|
+
# <b>raises</b> <tt>Formatter::Exceptions::InvalidData</tt> when invalid data is provided.
|
32
|
+
#
|
33
|
+
# <br>
|
34
|
+
# <b>returns</b> <tt>String</tt> payload: formatted payload suitable for a Dispatcher.
|
35
|
+
#
|
36
|
+
def format(support_emails_list)
|
37
|
+
raise Formatter::Exceptions::InvalidData unless support_emails_list.all? do |support_email|
|
38
|
+
support_email.is_a?(Domain::Email)
|
39
|
+
end
|
40
|
+
|
41
|
+
process_emails(support_emails_list).reduce("") do |payload, support_email|
|
42
|
+
payload + build_template(Domain::Email::ATTRIBUTES, support_email)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def process_emails(emails)
|
49
|
+
emails.each { |email| email.date = at_timezone(email.date) }
|
50
|
+
emails.filter! { |email| email.date > time_window } unless @frecuency.nil?
|
51
|
+
|
52
|
+
format_timestamp(emails)
|
53
|
+
end
|
54
|
+
|
55
|
+
def format_timestamp(emails)
|
56
|
+
emails.each { |email| email.date = email.date.strftime("%F %r") }
|
57
|
+
end
|
58
|
+
|
59
|
+
def time_window
|
60
|
+
date_time = Time.now - (60 * 60 * @frecuency)
|
61
|
+
|
62
|
+
at_timezone(date_time)
|
63
|
+
end
|
64
|
+
|
65
|
+
def at_timezone(date)
|
66
|
+
Time.at(date, in: @timezone)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../domain/work_items_limit"
|
4
|
+
require_relative "./exceptions/invalid_data"
|
5
|
+
require_relative "./base"
|
6
|
+
|
7
|
+
module Formatter
|
8
|
+
##
|
9
|
+
# This class implements methods from the Formatter::Base module, tailored to format the
|
10
|
+
# Domain::WorkItemsLimit structure for a dispatcher.
|
11
|
+
class WorkItemsLimit < Base
|
12
|
+
DEFAULT_DOMAIN_LIMIT = 6
|
13
|
+
|
14
|
+
# Initializes the formatter with essential configuration parameters.
|
15
|
+
#
|
16
|
+
# <b>limits</b> : expect a map with the wip limits by domain. Example: { "ops": 5 }
|
17
|
+
def initialize(config = {})
|
18
|
+
super(config)
|
19
|
+
|
20
|
+
@limits = config[:limits]
|
21
|
+
end
|
22
|
+
|
23
|
+
# Implements the logic for building a formatted payload with the given template for wip limits.
|
24
|
+
#
|
25
|
+
# <br>
|
26
|
+
# <b>Params:</b>
|
27
|
+
# * <tt>List<Domain::WorkItemsLimit></tt> work_items_list: List of mapped work items limits.
|
28
|
+
#
|
29
|
+
# <br>
|
30
|
+
# <b>raises</b> <tt>Formatter::Exceptions::InvalidData</tt> when invalid data is provided.
|
31
|
+
#
|
32
|
+
# <br>
|
33
|
+
# <b>returns</b> <tt>String</tt> payload, formatted payload suitable for a Dispatcher.
|
34
|
+
#
|
35
|
+
|
36
|
+
def format(work_items_list)
|
37
|
+
raise Formatter::Exceptions::InvalidData unless work_items_list.all? do |work_item|
|
38
|
+
work_item.is_a?(Domain::WorkItemsLimit)
|
39
|
+
end
|
40
|
+
|
41
|
+
exceeded_domains(work_items_list).reduce("") do |payload, work_items_limit|
|
42
|
+
built_template = build_template(Domain::WorkItemsLimit::ATTRIBUTES, work_items_limit)
|
43
|
+
payload + format_message_by_case(built_template.gsub("\n", ""), work_items_limit)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def format_message_by_case(template, work_items_limit)
|
50
|
+
total_items = work_items_limit.total
|
51
|
+
limit = domain_limit(work_items_limit.domain)
|
52
|
+
|
53
|
+
template + ", #{total_items} of #{limit}\n"
|
54
|
+
end
|
55
|
+
|
56
|
+
def exceeded_domains(work_items_list)
|
57
|
+
work_items_list.filter { |work_item| work_item.total > domain_limit(work_item.domain) }
|
58
|
+
end
|
59
|
+
|
60
|
+
def domain_limit(domain)
|
61
|
+
@limits[domain.to_sym] || DEFAULT_DOMAIN_LIMIT
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../../domain/email"
|
4
|
+
require_relative "../base"
|
5
|
+
|
6
|
+
module Mapper
|
7
|
+
module Imap
|
8
|
+
##
|
9
|
+
# This class implementats the methods of the Mapper::Base module, specifically designed for
|
10
|
+
# preparing or shaping support emails data coming from a Fetcher::Base implementation.
|
11
|
+
class SupportEmails
|
12
|
+
include Base
|
13
|
+
|
14
|
+
# Implements the logic for shaping the results from a fetcher response.
|
15
|
+
#
|
16
|
+
# <br>
|
17
|
+
# <b>Params:</b>
|
18
|
+
# * <tt>Fetcher::Imap::Types::Response</tt> imap_response: Array of imap emails data.
|
19
|
+
#
|
20
|
+
# <br>
|
21
|
+
# <b>return</b> <tt>List<Domain::Email></tt> support_emails_list, mapped support emails to be used by a
|
22
|
+
# Formatter::Base implementation.
|
23
|
+
#
|
24
|
+
def map(imap_response)
|
25
|
+
return [] if imap_response.results.empty?
|
26
|
+
|
27
|
+
normalized_email_data = normalize_response(imap_response.results)
|
28
|
+
|
29
|
+
normalized_email_data.map do |email|
|
30
|
+
Domain::Email.new(email["subject"], email["sender"], email["date"])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def normalize_response(results)
|
37
|
+
return [] if results.nil?
|
38
|
+
|
39
|
+
results.map do |value|
|
40
|
+
{
|
41
|
+
"sender" => extract_sender(value),
|
42
|
+
"date" => value.date,
|
43
|
+
"subject" => value.subject
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def extract_sender(value)
|
49
|
+
mailbox = value.sender[0]["mailbox"]
|
50
|
+
host = value.sender[0]["host"]
|
51
|
+
|
52
|
+
"#{mailbox}@#{host}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|