bas 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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