bas 0.1.0

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.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/._.rspec_status +0 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +14 -0
  5. data/CHANGELOG.md +4 -0
  6. data/CODE_OF_CONDUCT.md +132 -0
  7. data/CONTRIBUTING.md +66 -0
  8. data/Gemfile +24 -0
  9. data/LICENSE +21 -0
  10. data/README.md +362 -0
  11. data/Rakefile +12 -0
  12. data/SECURITY.md +13 -0
  13. data/lib/bas/dispatcher/base.rb +31 -0
  14. data/lib/bas/dispatcher/discord/exceptions/invalid_webhook_token.rb +16 -0
  15. data/lib/bas/dispatcher/discord/implementation.rb +51 -0
  16. data/lib/bas/dispatcher/discord/types/response.rb +22 -0
  17. data/lib/bas/dispatcher/slack/exceptions/invalid_webhook_token.rb +16 -0
  18. data/lib/bas/dispatcher/slack/implementation.rb +51 -0
  19. data/lib/bas/dispatcher/slack/types/response.rb +21 -0
  20. data/lib/bas/domain/birthday.rb +25 -0
  21. data/lib/bas/domain/email.rb +34 -0
  22. data/lib/bas/domain/exceptions/function_not_implemented.rb +18 -0
  23. data/lib/bas/domain/pto.rb +28 -0
  24. data/lib/bas/domain/work_items_limit.rb +25 -0
  25. data/lib/bas/fetcher/base.rb +43 -0
  26. data/lib/bas/fetcher/imap/base.rb +70 -0
  27. data/lib/bas/fetcher/imap/types/response.rb +27 -0
  28. data/lib/bas/fetcher/imap/use_case/support_emails.rb +26 -0
  29. data/lib/bas/fetcher/notion/base.rb +52 -0
  30. data/lib/bas/fetcher/notion/exceptions/invalid_api_key.rb +15 -0
  31. data/lib/bas/fetcher/notion/exceptions/invalid_database_id.rb +15 -0
  32. data/lib/bas/fetcher/notion/helper.rb +21 -0
  33. data/lib/bas/fetcher/notion/types/response.rb +26 -0
  34. data/lib/bas/fetcher/notion/use_case/birthday_next_week.rb +41 -0
  35. data/lib/bas/fetcher/notion/use_case/birthday_today.rb +29 -0
  36. data/lib/bas/fetcher/notion/use_case/pto_next_week.rb +71 -0
  37. data/lib/bas/fetcher/notion/use_case/pto_today.rb +30 -0
  38. data/lib/bas/fetcher/notion/use_case/work_items_limit.rb +37 -0
  39. data/lib/bas/fetcher/postgres/base.rb +46 -0
  40. data/lib/bas/fetcher/postgres/helper.rb +16 -0
  41. data/lib/bas/fetcher/postgres/types/response.rb +42 -0
  42. data/lib/bas/fetcher/postgres/use_case/pto_today.rb +32 -0
  43. data/lib/bas/formatter/base.rb +53 -0
  44. data/lib/bas/formatter/birthday.rb +34 -0
  45. data/lib/bas/formatter/exceptions/invalid_data.rb +15 -0
  46. data/lib/bas/formatter/pto.rb +88 -0
  47. data/lib/bas/formatter/support_emails.rb +69 -0
  48. data/lib/bas/formatter/work_items_limit.rb +64 -0
  49. data/lib/bas/mapper/base.rb +30 -0
  50. data/lib/bas/mapper/imap/support_emails.rb +56 -0
  51. data/lib/bas/mapper/notion/birthday_today.rb +68 -0
  52. data/lib/bas/mapper/notion/pto_today.rb +70 -0
  53. data/lib/bas/mapper/notion/work_items_limit.rb +65 -0
  54. data/lib/bas/mapper/postgres/pto_today.rb +47 -0
  55. data/lib/bas/use_cases/types/config.rb +19 -0
  56. data/lib/bas/use_cases/use_case.rb +39 -0
  57. data/lib/bas/use_cases/use_cases.rb +377 -0
  58. data/lib/bas/version.rb +6 -0
  59. data/lib/bas.rb +9 -0
  60. data/renovate.json +6 -0
  61. data/sig/business_automation_system.rbs +4 -0
  62. metadata +107 -0
@@ -0,0 +1,41 @@
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 birthdays data from Notion.
10
+ #
11
+ class BirthdayNextWeek < Notion::Base
12
+ DAYS_BEFORE_NOTIFY = 8
13
+
14
+ # Implements the data fetching filter for next week Birthdays data from Notion.
15
+ #
16
+ def fetch
17
+ filter = {
18
+ filter: {
19
+ or: [
20
+ { property: "BD_this_year", date: { equals: eight_days_from_now } }
21
+ ]
22
+ }
23
+ }
24
+
25
+ execute(filter)
26
+ end
27
+
28
+ private
29
+
30
+ def eight_days_from_now
31
+ date = Time.now.utc + days_in_second(DAYS_BEFORE_NOTIFY)
32
+
33
+ date.utc.strftime("%F").to_s
34
+ end
35
+
36
+ def days_in_second(days)
37
+ days * 24 * 60 * 60
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,29 @@
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 birthday data from Notion.
10
+ #
11
+ class BirthdayToday < Notion::Base
12
+ # Implements the data fetching filter for todays Birthdays data from Notion.
13
+ #
14
+ def fetch
15
+ today = Time.now.utc.strftime("%F").to_s
16
+
17
+ filter = {
18
+ filter: {
19
+ or: [
20
+ { property: "BD_this_year", date: { equals: today } }
21
+ ]
22
+ }
23
+ }
24
+
25
+ execute(filter)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -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
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../domain/exceptions/function_not_implemented"
4
+ require "erb"
5
+
6
+ module Formatter
7
+ ##
8
+ # The Formatter::Base module serves as the foundation for implementing specific data presentation logic
9
+ # within the Formatter module. Defines essential methods, that provide a blueprint for creating custom
10
+ # formatters tailored to different use cases.
11
+ #
12
+ class Base
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.
24
+ # Must be overridden by subclasses, with specific logic based on the use case.
25
+ #
26
+ # <br>
27
+ # <b>Params:</b>
28
+ # * <tt>List<Domain::></tt> domain_data: List of specific domain objects depending on the use case.
29
+ #
30
+ # <br>
31
+ # <b>raises</b> <tt>Domain::Exceptions::FunctionNotImplemented</tt> when missing implementation.
32
+ #
33
+ # <b>returns</b> <tt>String</tt> Formatted payload suitable for a Dispatcher::Base implementation.
34
+ #
35
+ def format(_domain_data)
36
+ raise Domain::Exceptions::FunctionNotImplemented
37
+ end
38
+
39
+ protected
40
+
41
+ def build_template(attributes, instance)
42
+ formated_template = format_template(attributes, instance)
43
+
44
+ "#{ERB.new(formated_template).result(binding)}\n"
45
+ end
46
+
47
+ def format_template(attributes, _instance)
48
+ attributes.reduce(template) do |formated_template, attribute|
49
+ formated_template.gsub(attribute, "<%= instance.#{attribute} %>")
50
+ end
51
+ end
52
+ end
53
+ 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