bns 0.1.1 → 0.2.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 +9 -0
- data/Gemfile +2 -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/work_items_limit.rb +39 -0
- data/lib/bns/fetcher/base.rb +13 -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 +76 -0
- data/lib/bns/formatter/work_items_limit.rb +43 -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 +227 -49
- data/lib/bns/version.rb +1 -1
- metadata +25 -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,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,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../domain/pto"
|
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::Pto structure for a dispatcher.
|
11
|
+
class Pto < Base
|
12
|
+
# Initializes the Slack formatter with essential configuration parameters.
|
13
|
+
#
|
14
|
+
# <b>timezone</b> : expect an string with the time difference relative to the UTC. Example: "-05:00"
|
15
|
+
def initialize(config = {})
|
16
|
+
super(config)
|
17
|
+
|
18
|
+
@timezone = config[:timezone]
|
19
|
+
end
|
20
|
+
|
21
|
+
# Implements the logic for building a formatted payload with the given template for PTO's.
|
22
|
+
#
|
23
|
+
# <br>
|
24
|
+
# <b>Params:</b>
|
25
|
+
# * <tt>List<Domain::Pto></tt> pto_list: List of mapped PTO's.
|
26
|
+
#
|
27
|
+
# <br>
|
28
|
+
# <b>raises</b> <tt>Formatter::Exceptions::InvalidData</tt> when invalid data is provided.
|
29
|
+
#
|
30
|
+
# <br>
|
31
|
+
# <b>returns</b> <tt>String</tt> payload, formatted payload suitable for a Dispatcher.
|
32
|
+
#
|
33
|
+
|
34
|
+
def format(ptos_list)
|
35
|
+
raise Formatter::Exceptions::InvalidData unless ptos_list.all? { |pto| pto.is_a?(Domain::Pto) }
|
36
|
+
|
37
|
+
ptos_list.reduce("") do |payload, pto|
|
38
|
+
built_template = build_template(Domain::Pto::ATTRIBUTES, pto)
|
39
|
+
payload + format_message_by_case(built_template.gsub("\n", ""), pto)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def format_message_by_case(built_template, pto)
|
46
|
+
date_start = format_timezone(pto.start_date).strftime("%F")
|
47
|
+
date_end = format_timezone(pto.end_date).strftime("%F")
|
48
|
+
|
49
|
+
if date_start == date_end
|
50
|
+
interval = same_day_interval(pto)
|
51
|
+
day_message = today?(date_start) ? "today" : "the day #{date_start}"
|
52
|
+
|
53
|
+
"#{built_template} #{day_message} #{interval}\n"
|
54
|
+
else
|
55
|
+
"#{built_template} from #{date_start} to #{date_end}\n"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def same_day_interval(pto)
|
60
|
+
time_start = format_timezone(pto.start_date).strftime("%I:%M %P")
|
61
|
+
time_end = format_timezone(pto.end_date).strftime("%I:%M %P")
|
62
|
+
|
63
|
+
time_start == time_end ? "all day" : "from #{time_start} to #{time_end}"
|
64
|
+
end
|
65
|
+
|
66
|
+
def format_timezone(date)
|
67
|
+
@timezone.nil? ? Time.new(date) : Time.new(date, in: @timezone)
|
68
|
+
end
|
69
|
+
|
70
|
+
def today?(date)
|
71
|
+
time_now = Time.now.strftime("%F")
|
72
|
+
|
73
|
+
date == format_timezone(time_now).strftime("%F")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,43 @@
|
|
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
|
+
attr_reader :limit
|
13
|
+
|
14
|
+
# Implements the logic for building a formatted payload with the given template for wip limits.
|
15
|
+
#
|
16
|
+
# <br>
|
17
|
+
# <b>Params:</b>
|
18
|
+
# * <tt>List<Domain::WorkItemsLimit></tt> work_items_list: List of mapped work items limits.
|
19
|
+
#
|
20
|
+
# <br>
|
21
|
+
# <b>raises</b> <tt>Formatter::Exceptions::InvalidData</tt> when invalid data is provided.
|
22
|
+
#
|
23
|
+
# <br>
|
24
|
+
# <b>returns</b> <tt>String</tt> payload, formatted payload suitable for a Dispatcher.
|
25
|
+
#
|
26
|
+
|
27
|
+
def format(work_items_list)
|
28
|
+
raise Formatter::Exceptions::InvalidData unless work_items_list.all? do |work_item|
|
29
|
+
work_item.is_a?(Domain::WorkItemsLimit)
|
30
|
+
end
|
31
|
+
|
32
|
+
exceeded_domains(work_items_list).reduce("") do |payload, work_items_limit|
|
33
|
+
payload + build_template(Domain::WorkItemsLimit::ATTRIBUTES, work_items_limit)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def exceeded_domains(work_items_list)
|
40
|
+
work_items_list.filter { |work_item| work_item.total > work_item.wip_limit }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -8,9 +8,11 @@ module Mapper
|
|
8
8
|
##
|
9
9
|
# This class implementats the methods of the Mapper::Base module, specifically designed for preparing or
|
10
10
|
# shaping birthdays data coming from a Fetcher::Base implementation.
|
11
|
-
class
|
11
|
+
class BirthdayToday
|
12
12
|
include Base
|
13
13
|
|
14
|
+
BIRTHDAY_PARAMS = ["Complete Name", "BD_this_year"].freeze
|
15
|
+
|
14
16
|
# Implements the logic for shaping the results from a fetcher response.
|
15
17
|
#
|
16
18
|
# <br>
|
@@ -27,7 +29,7 @@ module Mapper
|
|
27
29
|
normalized_notion_data = normalize_response(notion_response.results)
|
28
30
|
|
29
31
|
normalized_notion_data.map do |birthday|
|
30
|
-
Domain::Birthday.new(birthday["
|
32
|
+
Domain::Birthday.new(birthday["Complete Name"], birthday["BD_this_year"])
|
31
33
|
end
|
32
34
|
end
|
33
35
|
|
@@ -36,32 +38,22 @@ module Mapper
|
|
36
38
|
def normalize_response(results)
|
37
39
|
return [] if results.nil?
|
38
40
|
|
39
|
-
normalized_results = []
|
40
|
-
|
41
41
|
results.map do |value|
|
42
|
-
|
43
|
-
properties.delete("Name")
|
42
|
+
birthday_fields = value["properties"].slice(*BIRTHDAY_PARAMS)
|
44
43
|
|
45
|
-
|
44
|
+
birthday_fields.each do |field, birthday_value|
|
45
|
+
birthday_fields[field] = extract_birthday_value(field, birthday_value)
|
46
|
+
end
|
46
47
|
|
47
|
-
|
48
|
+
birthday_fields
|
48
49
|
end
|
49
|
-
|
50
|
-
normalized_results
|
51
50
|
end
|
52
51
|
|
53
|
-
def
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
if k == "Complete Name"
|
58
|
-
normalized_value["name"] = extract_rich_text_field_value(v)
|
59
|
-
elsif k == "BD_this_year"
|
60
|
-
normalized_value["birth_date"] = extract_date_field_value(v)
|
61
|
-
end
|
52
|
+
def extract_birthday_value(field, value)
|
53
|
+
case field
|
54
|
+
when "Complete Name" then extract_rich_text_field_value(value)
|
55
|
+
when "BD_this_year" then extract_date_field_value(value)
|
62
56
|
end
|
63
|
-
|
64
|
-
normalized_value
|
65
57
|
end
|
66
58
|
|
67
59
|
def extract_rich_text_field_value(data)
|
@@ -9,9 +9,11 @@ module Mapper
|
|
9
9
|
# This class implementats the methods of the Mapper::Base module, specifically designed for preparing or
|
10
10
|
# shaping PTO's data coming from a Fetcher::Base implementation.
|
11
11
|
#
|
12
|
-
class
|
12
|
+
class PtoToday
|
13
13
|
include Base
|
14
14
|
|
15
|
+
PTO_PARAMS = ["Person", "Desde?", "Hasta?"].freeze
|
16
|
+
|
15
17
|
# Implements the logic for shaping the results from a fetcher response.
|
16
18
|
#
|
17
19
|
# <br>
|
@@ -26,8 +28,9 @@ module Mapper
|
|
26
28
|
return [] if notion_response.results.empty?
|
27
29
|
|
28
30
|
normalized_notion_data = normalize_response(notion_response.results)
|
31
|
+
|
29
32
|
normalized_notion_data.map do |pto|
|
30
|
-
Domain::Pto.new(pto["
|
33
|
+
Domain::Pto.new(pto["Person"], pto["Desde?"], pto["Hasta?"])
|
31
34
|
end
|
32
35
|
end
|
33
36
|
|
@@ -36,39 +39,22 @@ module Mapper
|
|
36
39
|
def normalize_response(response)
|
37
40
|
return [] if response.nil?
|
38
41
|
|
39
|
-
normalized_response = []
|
40
|
-
|
41
42
|
response.map do |value|
|
42
|
-
|
43
|
-
properties.delete("Name")
|
43
|
+
pto_fields = value["properties"].slice(*PTO_PARAMS)
|
44
44
|
|
45
|
-
|
45
|
+
pto_fields.each do |field, pto_value|
|
46
|
+
pto_fields[field] = extract_pto_value(field, pto_value)
|
47
|
+
end
|
46
48
|
|
47
|
-
|
49
|
+
pto_fields
|
48
50
|
end
|
49
|
-
|
50
|
-
normalized_response
|
51
51
|
end
|
52
52
|
|
53
|
-
def
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
end
|
59
|
-
|
60
|
-
normalized_value
|
61
|
-
end
|
62
|
-
|
63
|
-
def extract_pto_fields(key, value, normalized_value)
|
64
|
-
case key
|
65
|
-
when "Person"
|
66
|
-
user_name = extract_person_field_value(value)
|
67
|
-
normalized_value["name"] = user_name
|
68
|
-
when "Desde?"
|
69
|
-
normalized_value["start"] = extract_date_field_value(value)
|
70
|
-
when "Hasta?"
|
71
|
-
normalized_value["end"] = extract_date_field_value(value)
|
53
|
+
def extract_pto_value(field, value)
|
54
|
+
case field
|
55
|
+
when "Person" then extract_person_field_value(value)
|
56
|
+
when "Desde?" then extract_date_field_value(value)
|
57
|
+
when "Hasta?" then extract_date_field_value(value)
|
72
58
|
end
|
73
59
|
end
|
74
60
|
|
@@ -79,18 +65,6 @@ module Mapper
|
|
79
65
|
def extract_date_field_value(data)
|
80
66
|
data["date"]["start"]
|
81
67
|
end
|
82
|
-
|
83
|
-
def format_date(str_date)
|
84
|
-
return "" if str_date.nil?
|
85
|
-
|
86
|
-
if str_date.include?("T")
|
87
|
-
format = "%Y-%m-%d|%I:%M %p"
|
88
|
-
datetime = Time.new(str_date)
|
89
|
-
datetime.strftime(format)
|
90
|
-
else
|
91
|
-
str_date
|
92
|
-
end
|
93
|
-
end
|
94
68
|
end
|
95
69
|
end
|
96
70
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../../domain/work_items_limit"
|
4
|
+
require_relative "../base"
|
5
|
+
|
6
|
+
module Mapper
|
7
|
+
module Notion
|
8
|
+
##
|
9
|
+
# This class implementats the methods of the Mapper::Base module, specifically designed
|
10
|
+
# for preparing or shaping work items data coming from a Fetcher::Base implementation.
|
11
|
+
class WorkItemsLimit
|
12
|
+
include Base
|
13
|
+
|
14
|
+
WORK_ITEM_PARAMS = ["Responsible domain"].freeze
|
15
|
+
|
16
|
+
# Implements the logic for shaping the results from a fetcher response.
|
17
|
+
#
|
18
|
+
# <br>
|
19
|
+
# <b>Params:</b>
|
20
|
+
# * <tt>Fetcher::Notion::Types::Response</tt> notion_response: Notion response object.
|
21
|
+
#
|
22
|
+
# <br>
|
23
|
+
# <b>return</b> <tt>List<Domain::WorkItem></tt> work_items_list, mapped work items to be used by a
|
24
|
+
# Formatter::Base implementation.
|
25
|
+
#
|
26
|
+
def map(notion_response)
|
27
|
+
return [] if notion_response.results.empty?
|
28
|
+
|
29
|
+
normalized_notion_data = normalize_response(notion_response.results)
|
30
|
+
|
31
|
+
domain_items_count = count_domain_items(normalized_notion_data)
|
32
|
+
|
33
|
+
domain_items_count.map do |domain, items_count|
|
34
|
+
Domain::WorkItemsLimit.new(domain, items_count)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def normalize_response(results)
|
41
|
+
return [] if results.nil?
|
42
|
+
|
43
|
+
results.map do |value|
|
44
|
+
work_item_fields = value["properties"].slice(*WORK_ITEM_PARAMS)
|
45
|
+
|
46
|
+
work_item_fields.each do |field, work_item_value|
|
47
|
+
work_item_fields[field] = extract_domain_field_value(work_item_value)
|
48
|
+
end
|
49
|
+
|
50
|
+
work_item_fields
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def extract_domain_field_value(data)
|
55
|
+
data["select"]["name"]
|
56
|
+
end
|
57
|
+
|
58
|
+
def count_domain_items(work_items_list)
|
59
|
+
domain_work_items = work_items_list.group_by { |work_item| work_item["Responsible domain"] }
|
60
|
+
|
61
|
+
domain_work_items.transform_values(&:count)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../../domain/pto"
|
4
|
+
require_relative "../base"
|
5
|
+
|
6
|
+
module Mapper
|
7
|
+
module Postgres
|
8
|
+
##
|
9
|
+
# This class implementats the methods of the Mapper::Base module, specifically designed for preparing or
|
10
|
+
# shaping PTO's data coming from the Fetcher::Postgres::Pto class.
|
11
|
+
#
|
12
|
+
class PtoToday
|
13
|
+
# Implements the logic for shaping the results from a fetcher response.
|
14
|
+
#
|
15
|
+
# <br>
|
16
|
+
# <b>Params:</b>
|
17
|
+
# * <tt>Fetcher::Postgres::Types::Response</tt> pg_response: Postgres response object.
|
18
|
+
#
|
19
|
+
# <br>
|
20
|
+
# <b>returns</b> <tt>List<Domain::Pto></tt> ptos_list, mapped PTO's to be used by a Formatter::Base
|
21
|
+
# implementation.
|
22
|
+
#
|
23
|
+
def map(pg_response)
|
24
|
+
return [] if pg_response.records.empty?
|
25
|
+
|
26
|
+
ptos = build_map(pg_response)
|
27
|
+
|
28
|
+
ptos.map do |pto|
|
29
|
+
name = pto["name"]
|
30
|
+
start_date = pto["start_date"]
|
31
|
+
end_date = pto["end_date"]
|
32
|
+
|
33
|
+
Domain::Pto.new(name, start_date, end_date)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def build_map(pg_response)
|
40
|
+
fields = pg_response.fields
|
41
|
+
values = pg_response.records
|
42
|
+
|
43
|
+
values.map { |value| Hash[fields.zip(value)] }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|