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,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../domain/exceptions/function_not_implemented"
4
+
5
+ module Mapper
6
+ ##
7
+ # The Mapper::Base module serves as the foundation for implementing specific data shaping logic within the
8
+ # Mapper module. Defines essential methods, that provide a blueprint for organizing or shaping data in a manner
9
+ # suitable for downstream formatting processes.
10
+ #
11
+ module Base
12
+ # An method meant to prepare or organize the data coming from an implementation of the Fetcher::Base interface.
13
+ # Must be overridden by subclasses, with specific logic based on the use case.
14
+ #
15
+ # <br>
16
+ # <b>Params:</b>
17
+ # * <tt>Fetcher::Notion::Types::Response</tt> response: Response produced by a fetcher.
18
+ #
19
+ # <br>
20
+ #
21
+ # <b>raises</b> <tt>Domain::Exceptions::FunctionNotImplemented</tt> when missing implementation.
22
+ # <br>
23
+ #
24
+ # <b>returns</b> <tt>List<Domain::></tt> Mapped list of data, ready to be formatted.
25
+ #
26
+ def map(_response)
27
+ raise Domain::Exceptions::FunctionNotImplemented
28
+ end
29
+ end
30
+ 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
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../domain/birthday"
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 for preparing or
10
+ # shaping birthdays data coming from a Fetcher::Base implementation.
11
+ class BirthdayToday
12
+ include Base
13
+
14
+ BIRTHDAY_PARAMS = ["Complete Name", "BD_this_year"].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::Birthday></tt> birthdays_list, mapped birthdays 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
+ normalized_notion_data.map do |birthday|
32
+ Domain::Birthday.new(birthday["Complete Name"], birthday["BD_this_year"])
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def normalize_response(results)
39
+ return [] if results.nil?
40
+
41
+ results.map do |value|
42
+ birthday_fields = value["properties"].slice(*BIRTHDAY_PARAMS)
43
+
44
+ birthday_fields.each do |field, birthday_value|
45
+ birthday_fields[field] = extract_birthday_value(field, birthday_value)
46
+ end
47
+
48
+ birthday_fields
49
+ end
50
+ end
51
+
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)
56
+ end
57
+ end
58
+
59
+ def extract_rich_text_field_value(data)
60
+ data["rich_text"][0]["plain_text"]
61
+ end
62
+
63
+ def extract_date_field_value(data)
64
+ data["formula"]["date"]["start"]
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../domain/pto"
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 for preparing or
10
+ # shaping PTO's data coming from a Fetcher::Base implementation.
11
+ #
12
+ class PtoToday
13
+ include Base
14
+
15
+ PTO_PARAMS = ["Person", "Desde?", "Hasta?"].freeze
16
+
17
+ # Implements the logic for shaping the results from a fetcher response.
18
+ #
19
+ # <br>
20
+ # <b>Params:</b>
21
+ # * <tt>Fetcher::Notion::Types::Response</tt> notion_response: Notion response object.
22
+ #
23
+ # <br>
24
+ # <b>returns</b> <tt>List<Domain::Pto></tt> ptos_list, mapped PTO's to be used by a Formatter::Base
25
+ # implementation.
26
+ #
27
+ def map(notion_response)
28
+ return [] if notion_response.results.empty?
29
+
30
+ normalized_notion_data = normalize_response(notion_response.results)
31
+
32
+ normalized_notion_data.map do |pto|
33
+ Domain::Pto.new(pto["Person"], pto["Desde?"], pto["Hasta?"])
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def normalize_response(response)
40
+ return [] if response.nil?
41
+
42
+ response.map do |value|
43
+ pto_fields = value["properties"].slice(*PTO_PARAMS)
44
+
45
+ pto_fields.each do |field, pto_value|
46
+ pto_fields[field] = extract_pto_value(field, pto_value)
47
+ end
48
+
49
+ pto_fields
50
+ end
51
+ end
52
+
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)
58
+ end
59
+ end
60
+
61
+ def extract_person_field_value(data)
62
+ data["people"][0]["name"]
63
+ end
64
+
65
+ def extract_date_field_value(data)
66
+ data["date"]["start"]
67
+ end
68
+ end
69
+ end
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
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UseCases
4
+ module Types
5
+ ##
6
+ # Represents a the configuration composing the initial components required by a UseCases::UseCase implementation.
7
+ #
8
+ class Config
9
+ attr_reader :fetcher, :mapper, :formatter, :dispatcher
10
+
11
+ def initialize(fetcher, mapper, formatter, dispatcher)
12
+ @fetcher = fetcher
13
+ @mapper = mapper
14
+ @formatter = formatter
15
+ @dispatcher = dispatcher
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UseCases
4
+ ##
5
+ # The UseCases::UseCase class represents a generic structure for use cases within the system. It encapsulates the
6
+ # logic flow by coordinating the execution of its components to fulfill a specific use case.
7
+ #
8
+ class UseCase
9
+ attr_reader :fetcher, :mapper, :formatter, :dispatcher
10
+
11
+ # Initializes the use case with the necessary components.
12
+ #
13
+ # <br>
14
+ # <b>Params:</b>
15
+ # * <tt>Usecases::Types::Config</tt> config, The components required to instantiate a use case.
16
+ #
17
+ def initialize(config)
18
+ @fetcher = config.fetcher
19
+ @mapper = config.mapper
20
+ @formatter = config.formatter
21
+ @dispatcher = config.dispatcher
22
+ end
23
+
24
+ # Executes the use case by orchestrating the sequential execution of the fetcher, mapper, formatter, and dispatcher.
25
+ #
26
+ # <br>
27
+ # <b>returns</b> <tt>Dispatcher::Discord::Types::Response</tt>
28
+ #
29
+ def perform
30
+ response = fetcher.fetch
31
+
32
+ mappings = mapper.map(response)
33
+
34
+ formatted_payload = formatter.format(mappings)
35
+
36
+ dispatcher.dispatch(formatted_payload)
37
+ end
38
+ end
39
+ end