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,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