bas 0.4.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/README.md +68 -147
  4. data/lib/bas/bot/base.rb +74 -0
  5. data/lib/bas/bot/compare_wip_limit_count.rb +92 -0
  6. data/lib/bas/bot/fetch_birthdays_from_notion.rb +128 -0
  7. data/lib/bas/bot/fetch_domains_wip_counts_from_notion.rb +121 -0
  8. data/lib/bas/bot/fetch_domains_wip_limit_from_notion.rb +134 -0
  9. data/lib/bas/bot/fetch_emails_from_imap.rb +99 -0
  10. data/lib/bas/bot/fetch_next_week_birthdays_from_notion.rb +142 -0
  11. data/lib/bas/bot/fetch_next_week_ptos_from_notion.rb +162 -0
  12. data/lib/bas/bot/fetch_ptos_from_notion.rb +138 -0
  13. data/lib/bas/bot/format_birthdays.rb +97 -0
  14. data/lib/bas/bot/format_emails.rb +124 -0
  15. data/lib/bas/bot/format_wip_limit_exceeded.rb +97 -0
  16. data/lib/bas/bot/garbage_collector.rb +85 -0
  17. data/lib/bas/bot/humanize_pto.rb +119 -0
  18. data/lib/bas/bot/notify_discord.rb +96 -0
  19. data/lib/bas/read/base.rb +10 -23
  20. data/lib/bas/read/default.rb +16 -0
  21. data/lib/bas/read/postgres.rb +44 -0
  22. data/lib/bas/read/types/response.rb +18 -0
  23. data/lib/bas/utils/discord/integration.rb +43 -0
  24. data/lib/bas/utils/exceptions/function_not_implemented.rb +16 -0
  25. data/lib/bas/utils/exceptions/invalid_process_response.rb +16 -0
  26. data/lib/bas/utils/imap/request.rb +76 -0
  27. data/lib/bas/utils/notion/request.rb +45 -0
  28. data/lib/bas/utils/openai/run_assistant.rb +99 -0
  29. data/lib/bas/utils/postgres/request.rb +50 -0
  30. data/lib/bas/version.rb +1 -1
  31. data/lib/bas/write/base.rb +12 -17
  32. data/lib/bas/write/postgres.rb +45 -0
  33. data/lib/bas/write/postgres_update.rb +49 -0
  34. data/lib/bas.rb +1 -3
  35. metadata +30 -67
  36. data/lib/bas/domain/birthday.rb +0 -25
  37. data/lib/bas/domain/email.rb +0 -34
  38. data/lib/bas/domain/exceptions/function_not_implemented.rb +0 -18
  39. data/lib/bas/domain/issue.rb +0 -22
  40. data/lib/bas/domain/notification.rb +0 -23
  41. data/lib/bas/domain/pto.rb +0 -69
  42. data/lib/bas/domain/work_items_limit.rb +0 -25
  43. data/lib/bas/formatter/base.rb +0 -53
  44. data/lib/bas/formatter/birthday.rb +0 -38
  45. data/lib/bas/formatter/exceptions/invalid_data.rb +0 -15
  46. data/lib/bas/formatter/notification.rb +0 -34
  47. data/lib/bas/formatter/pto.rb +0 -89
  48. data/lib/bas/formatter/support_emails.rb +0 -73
  49. data/lib/bas/formatter/types/response.rb +0 -16
  50. data/lib/bas/formatter/work_items_limit.rb +0 -68
  51. data/lib/bas/process/base.rb +0 -39
  52. data/lib/bas/process/discord/exceptions/invalid_webhook_token.rb +0 -16
  53. data/lib/bas/process/discord/implementation.rb +0 -71
  54. data/lib/bas/process/discord/types/response.rb +0 -22
  55. data/lib/bas/process/openai/base.rb +0 -72
  56. data/lib/bas/process/openai/helper.rb +0 -19
  57. data/lib/bas/process/openai/types/response.rb +0 -27
  58. data/lib/bas/process/openai/use_case/humanize_pto.rb +0 -53
  59. data/lib/bas/process/slack/exceptions/invalid_webhook_token.rb +0 -16
  60. data/lib/bas/process/slack/implementation.rb +0 -70
  61. data/lib/bas/process/slack/types/response.rb +0 -21
  62. data/lib/bas/process/types/response.rb +0 -16
  63. data/lib/bas/read/github/base.rb +0 -57
  64. data/lib/bas/read/github/types/response.rb +0 -27
  65. data/lib/bas/read/github/use_case/repo_issues.rb +0 -17
  66. data/lib/bas/read/imap/base.rb +0 -70
  67. data/lib/bas/read/imap/types/response.rb +0 -27
  68. data/lib/bas/read/imap/use_case/support_emails.rb +0 -26
  69. data/lib/bas/read/notion/base.rb +0 -52
  70. data/lib/bas/read/notion/exceptions/invalid_api_key.rb +0 -15
  71. data/lib/bas/read/notion/exceptions/invalid_database_id.rb +0 -15
  72. data/lib/bas/read/notion/helper.rb +0 -21
  73. data/lib/bas/read/notion/types/response.rb +0 -26
  74. data/lib/bas/read/notion/use_case/birthday_next_week.rb +0 -41
  75. data/lib/bas/read/notion/use_case/birthday_today.rb +0 -29
  76. data/lib/bas/read/notion/use_case/notification.rb +0 -28
  77. data/lib/bas/read/notion/use_case/pto_next_week.rb +0 -71
  78. data/lib/bas/read/notion/use_case/pto_today.rb +0 -30
  79. data/lib/bas/read/notion/use_case/work_items_limit.rb +0 -37
  80. data/lib/bas/read/postgres/base.rb +0 -46
  81. data/lib/bas/read/postgres/helper.rb +0 -16
  82. data/lib/bas/read/postgres/types/response.rb +0 -42
  83. data/lib/bas/read/postgres/use_case/pto_today.rb +0 -32
  84. data/lib/bas/serialize/base.rb +0 -30
  85. data/lib/bas/serialize/github/issues.rb +0 -57
  86. data/lib/bas/serialize/imap/support_emails.rb +0 -56
  87. data/lib/bas/serialize/notion/birthday_today.rb +0 -68
  88. data/lib/bas/serialize/notion/notification.rb +0 -56
  89. data/lib/bas/serialize/notion/pto_today.rb +0 -75
  90. data/lib/bas/serialize/notion/work_items_limit.rb +0 -65
  91. data/lib/bas/serialize/postgres/pto_today.rb +0 -47
  92. data/lib/bas/use_cases/types/config.rb +0 -20
  93. data/lib/bas/use_cases/use_case.rb +0 -42
  94. data/lib/bas/use_cases/use_cases.rb +0 -465
  95. data/lib/bas/write/logs/base.rb +0 -33
  96. data/lib/bas/write/logs/use_case/console_log.rb +0 -22
  97. data/lib/bas/write/notion/base.rb +0 -36
  98. data/lib/bas/write/notion/use_case/empty_notification.rb +0 -38
  99. data/lib/bas/write/notion/use_case/notification.rb +0 -38
@@ -1,73 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../domain/email"
4
- require_relative "./exceptions/invalid_data"
5
- require_relative "./base"
6
- require_relative "./types/response"
7
-
8
- module Formatter
9
- ##
10
- # This class implements methods from the Formatter::Base module, tailored to format the
11
- # Domain::Email structure for a Process.
12
- class SupportEmails < Base
13
- DEFAULT_TIME_ZONE = "+00:00"
14
-
15
- # Initializes the formatter with essential configuration parameters.
16
- #
17
- # <b>timezone</b> : expect an string with the time difference relative to the UTC. Example: "-05:00"
18
- def initialize(config = {})
19
- super(config)
20
-
21
- @timezone = config[:timezone] || DEFAULT_TIME_ZONE
22
- @frecuency = config[:frecuency]
23
- end
24
-
25
- # Implements the logic for building a formatted payload with the given template for support emails.
26
- #
27
- # <br>
28
- # <b>Params:</b>
29
- # * <tt>List<Domain::Email></tt> support_emails_list: list of support emails.
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>Formatter::Types::Response</tt> formatter response: standard output for
36
- # the formatted payload suitable for a Process.
37
- #
38
- def format(support_emails_list)
39
- raise Formatter::Exceptions::InvalidData unless support_emails_list.all? do |support_email|
40
- support_email.is_a?(Domain::Email)
41
- end
42
-
43
- response = process_emails(support_emails_list).reduce("") do |payload, support_email|
44
- payload + build_template(Domain::Email::ATTRIBUTES, support_email)
45
- end
46
-
47
- Formatter::Types::Response.new(response)
48
- end
49
-
50
- private
51
-
52
- def process_emails(emails)
53
- emails.each { |email| email.date = at_timezone(email.date) }
54
- emails.filter! { |email| email.date > time_window } unless @frecuency.nil?
55
-
56
- format_timestamp(emails)
57
- end
58
-
59
- def format_timestamp(emails)
60
- emails.each { |email| email.date = email.date.strftime("%F %r") }
61
- end
62
-
63
- def time_window
64
- date_time = Time.now - (60 * 60 * @frecuency)
65
-
66
- at_timezone(date_time)
67
- end
68
-
69
- def at_timezone(date)
70
- Time.at(date, in: @timezone)
71
- end
72
- end
73
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Formatter
4
- module Types
5
- ##
6
- # Represents a response received from a Formatter. It encapsulates the formatted data to be used by
7
- # a Process or a Write component.
8
- class Response
9
- attr_reader :data
10
-
11
- def initialize(response)
12
- @data = response
13
- end
14
- end
15
- end
16
- end
@@ -1,68 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../domain/work_items_limit"
4
- require_relative "./exceptions/invalid_data"
5
- require_relative "./base"
6
- require_relative "./types/response"
7
-
8
- module Formatter
9
- ##
10
- # This class implements methods from the Formatter::Base module, tailored to format the
11
- # Domain::WorkItemsLimit structure for a Process.
12
- class WorkItemsLimit < Base
13
- DEFAULT_DOMAIN_LIMIT = 6
14
-
15
- # Initializes the formatter with essential configuration parameters.
16
- #
17
- # <b>limits</b> : expect a map with the wip limits by domain. Example: { "ops": 5 }
18
- def initialize(config = {})
19
- super(config)
20
-
21
- @limits = config[:limits]
22
- end
23
-
24
- # Implements the logic for building a formatted payload with the given template for wip limits.
25
- #
26
- # <br>
27
- # <b>Params:</b>
28
- # * <tt>List<Domain::WorkItemsLimit></tt> work_items_list: List of serialized work items limits.
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>Formatter::Types::Response</tt> formatter response: standard output for
35
- # the formatted payload suitable for a Process.
36
- #
37
-
38
- def format(work_items_list)
39
- raise Formatter::Exceptions::InvalidData unless work_items_list.all? do |work_item|
40
- work_item.is_a?(Domain::WorkItemsLimit)
41
- end
42
-
43
- response = exceeded_domains(work_items_list).reduce("") do |payload, work_items_limit|
44
- built_template = build_template(Domain::WorkItemsLimit::ATTRIBUTES, work_items_limit)
45
- payload + format_message_by_case(built_template.gsub("\n", ""), work_items_limit)
46
- end
47
-
48
- Formatter::Types::Response.new(response)
49
- end
50
-
51
- private
52
-
53
- def format_message_by_case(template, work_items_limit)
54
- total_items = work_items_limit.total
55
- limit = domain_limit(work_items_limit.domain)
56
-
57
- template + ", #{total_items} of #{limit}\n"
58
- end
59
-
60
- def exceeded_domains(work_items_list)
61
- work_items_list.filter { |work_item| work_item.total > domain_limit(work_item.domain) }
62
- end
63
-
64
- def domain_limit(domain)
65
- @limits[domain.to_sym] || DEFAULT_DOMAIN_LIMIT
66
- end
67
- end
68
- end
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../domain/exceptions/function_not_implemented"
4
- require_relative "./types/response"
5
-
6
- module Process
7
- ##
8
- # Serves as a foundational structure for implementing specific process. Acting as an interface,
9
- # this class defines essential attributes and methods, providing a blueprint for creating custom
10
- # process tailored to different platforms or services.
11
- #
12
- class Base
13
- attr_reader :config
14
-
15
- # Initializes the process with essential configuration parameters.
16
- #
17
- def initialize(config = {})
18
- @config = config
19
- end
20
-
21
- # A method meant to send messages to an specific destination depending on the implementation.
22
- # Must be overridden by subclasses, with specific logic based on the use case.
23
- #
24
- # <br>
25
- # <b>returns</b> a <tt>Process::Types::Response</tt>: standard output for a process
26
- #
27
- def execute(format_response)
28
- Process::Types::Response.new(format_response.data)
29
- end
30
-
31
- protected
32
-
33
- def valid_format_response(format_response)
34
- return format_response if format_response.is_a?(Formatter::Types::Response)
35
-
36
- raise Formatter::Exceptions::InvalidData
37
- end
38
- end
39
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Process
4
- module Discord
5
- module Exceptions
6
- ##
7
- # Domain specific representation when an invalid Discord webhook token is provided to Discord.
8
- #
9
- class InvalidWebookToken < StandardError
10
- def initialize(message = "The provided Webhook token is invalid.")
11
- super(message)
12
- end
13
- end
14
- end
15
- end
16
- end
@@ -1,71 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../base"
4
- require_relative "./exceptions/invalid_webhook_token"
5
- require_relative "./types/response"
6
-
7
- module Process
8
- module Discord
9
- ##
10
- # This class is an implementation of the Process::Base interface, specifically designed
11
- # for sending messages to Discord.
12
- #
13
- class Implementation < Base
14
- attr_reader :webhook, :name
15
-
16
- # Initializes the process with essential configuration parameters.
17
- #
18
- def initialize(config = {})
19
- super(config)
20
-
21
- @webhook = config[:webhook]
22
- @name = config[:name]
23
- end
24
-
25
- # Implements the sending process logic for the Discord use case. It sends a POST request to
26
- # the Discord webhook with the specified payload.
27
- #
28
- # <br>
29
- # <b>Params:</b>
30
- # * <tt>Formatter::Types::Response</tt> formatter response: standard formatter response
31
- # with the Payload to be send to discord.
32
- # <br>
33
- # <b>raises</b> <tt>Exceptions::Discord::InvalidWebookToken</tt> if the provided webhook
34
- # token is invalid.
35
- #
36
- # <br>
37
- # <b>returns</b> <tt>Process::Types::Response</tt>
38
- #
39
- def execute(format_response)
40
- response = valid_format_response(format_response)
41
-
42
- body = post_body(response.data)
43
-
44
- response = HTTParty.post(webhook, { body:, headers: { "Content-Type" => "application/json" } })
45
-
46
- discord_response = Process::Discord::Types::Response.new(response)
47
-
48
- validate_response(discord_response)
49
- end
50
-
51
- private
52
-
53
- def post_body(payload)
54
- {
55
- username: name,
56
- avatar_url: "",
57
- content: payload
58
- }.to_json
59
- end
60
-
61
- def validate_response(response)
62
- case response.code
63
- when 50_027
64
- raise Discord::Exceptions::InvalidWebookToken, response.message
65
- else
66
- Process::Types::Response.new(response)
67
- end
68
- end
69
- end
70
- end
71
- end
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Process
4
- module Discord
5
- module Types
6
- ##
7
- # Represents a response received from Discord. It encapsulates essential information about the response,
8
- # providing a structured way to handle and analyze Discord server responses.
9
- #
10
- class Response
11
- attr_reader :code, :http_code, :message, :response
12
-
13
- def initialize(response)
14
- @http_code = response.code
15
- @code = response["code"]
16
- @message = response.message
17
- @response = response.response
18
- end
19
- end
20
- end
21
- end
22
- end
@@ -1,72 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "httparty"
4
-
5
- require_relative "../base"
6
- require_relative "./types/response"
7
- require_relative "./helper"
8
-
9
- module Process
10
- module OpenAI
11
- ##
12
- # This class is an implementation of the Process::Base interface, specifically designed
13
- # for requesting to the OpenAI API for chat completion.
14
- #
15
- class Base < Process::Base
16
- OPENAI_BASE_URL = "https://api.openai.com"
17
- DEFAULT_N_CHOICES = 1
18
-
19
- # Initializes the process with essential configuration parameters.
20
- #
21
- def initialize(config = {})
22
- super(config)
23
-
24
- @n_choices = config[:n_choices] || DEFAULT_N_CHOICES
25
- end
26
-
27
- protected
28
-
29
- # Implements the sending process logic for the OpenAI API. It sends a
30
- # POST request to the OpenAI API for chat completion with the specified payload.
31
- #
32
- # <br>
33
- # <b>Params:</b>
34
- # * <tt>Formatter::Types::Response</tt> formatter response: standard formatter response
35
- # with the data to be send to OpenAI.
36
- # <br>
37
- # <b>raises</b> <tt>StandardError</tt> if the API returns an error response
38
- #
39
- # <br>
40
- # <b>returns</b> <tt>Process::Types::Response</tt>
41
- #
42
- def process(messages)
43
- url = "#{OPENAI_BASE_URL}/v1/chat/completions"
44
-
45
- httparty_response = HTTParty.post(url, { body: body(messages).to_json, headers: })
46
-
47
- openai_response = Process::OpenAI::Types::Response.new(httparty_response)
48
-
49
- response = Process::OpenAI::Helper.validate_response(openai_response)
50
-
51
- Process::Types::Response.new(response)
52
- end
53
-
54
- private
55
-
56
- def body(messages)
57
- {
58
- "model": config[:model],
59
- "n": @n_choices,
60
- "messages": messages
61
- }
62
- end
63
-
64
- def headers
65
- {
66
- "Authorization" => "Bearer #{config[:secret]}",
67
- "Content-Type" => "application/json"
68
- }
69
- end
70
- end
71
- end
72
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Process
4
- module OpenAI
5
- ##
6
- # Provides common fuctionalities along the Process::OpenAI domain.
7
- #
8
- module Helper
9
- def self.validate_response(response)
10
- case response.status_code
11
- when 200
12
- response
13
- else
14
- raise StandardError, response.message
15
- end
16
- end
17
- end
18
- end
19
- end
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Process
4
- module OpenAI
5
- module Types
6
- ##
7
- # Represents a response received from the OpenAI chat completion service API. It encapsulates
8
- # essential information about the response, providing a structured way to handle and analyze
9
- # its responses.
10
- class Response
11
- attr_reader :status_code, :message, :choices
12
-
13
- def initialize(response)
14
- if response["error"]
15
- @status_code = response.code
16
- @message = response["error"]
17
- @choices = []
18
- else
19
- @status_code = 200
20
- @message = "success"
21
- @choices = response["choices"]
22
- end
23
- end
24
- end
25
- end
26
- end
27
- end
@@ -1,53 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../base"
4
-
5
- module Process
6
- module OpenAI
7
- ##
8
- # This class is an implementation of the Process::OpenAI::Base interface, specifically designed
9
- # to humanize formatted PTO messages for better understanding.
10
- #
11
- class HumanizePto < OpenAI::Base
12
- # Implements the data process to humanize formatted PTO messages.
13
- #
14
- def execute(format_response)
15
- messages = [
16
- {
17
- "role": "user",
18
- "content": content(format_response.data)
19
- }
20
- ]
21
-
22
- process(messages)
23
- end
24
-
25
- private
26
-
27
- def content(data)
28
- <<~MESSAGE
29
- The following message is too complex for a human to read since it has specific dates formatted as YYYY-MM-DD:
30
-
31
- \"#{data}\"
32
-
33
- Create a text that gives the same message in a more human-readable and context-valuable fashion for a human.
34
- Use the current date (#{current_date}) to provide context.
35
- Try grouping information and using bullet points to make it easier to read the information at a quick glance.
36
- Additionally, keep in mind that we work from Monday to Friday - not weekends.
37
- Please, just give the PTOs message and avoid the intro message such as \"Here is a reader-friendly message\".
38
- Add emojis for a cool message, but keep it seriously.
39
-
40
- For example:
41
- The input "Jane Doe is on PTO from 2024-04-08 to 2024-04-26", means that Jane will be on PTO starting at 2024-04-08
42
- and ending at 2024-04-26, i.e, she will be back the next work-day which is 2024-04-29.
43
- MESSAGE
44
- end
45
-
46
- def current_date
47
- utc_today = Time.now.utc
48
-
49
- Time.at(utc_today, in: config[:timezone]).strftime("%A, %F").to_s
50
- end
51
- end
52
- end
53
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Process
4
- module Slack
5
- module Exceptions
6
- ##
7
- # Domain specific representation when an invalid webhook token is provided to Slack.
8
- #
9
- class InvalidWebookToken < StandardError
10
- def initialize(message = "The provided Webhook token is invalid.")
11
- super(message)
12
- end
13
- end
14
- end
15
- end
16
- end
@@ -1,70 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../base"
4
- require_relative "./exceptions/invalid_webhook_token"
5
- require_relative "./types/response"
6
-
7
- module Process
8
- module Slack
9
- ##
10
- # This class is an implementation of the Process::Base interface, specifically designed
11
- # for sending messages to Slack.
12
- #
13
- class Implementation < Base
14
- attr_reader :webhook, :name
15
-
16
- # Initializes the process with essential configuration parameters.
17
- #
18
- def initialize(config = {})
19
- super(config)
20
-
21
- @webhook = config[:webhook]
22
- @name = config[:name]
23
- end
24
-
25
- # Implements the sending process logic for the Slack use case. It sends a POST request to
26
- # the Slack webhook with the specified payload.
27
- #
28
- # <br>
29
- # <b>Params:</b>
30
- # * <tt>Formatter::Types::Response</tt> formatter response: standard formatter response
31
- # with the Payload to be send to slack.
32
- # <br>
33
- # <b>raises</b> <tt>Exceptions::Slack::InvalidWebookToken</tt> if the provided webhook
34
- # token is invalid.
35
- #
36
- # <br>
37
- # <b>returns</b> <tt>Process::Types::Response</tt>
38
- #
39
- def execute(format_response)
40
- response = valid_format_response(format_response)
41
-
42
- body = post_body(response.data)
43
-
44
- response = HTTParty.post(webhook, { body:, headers: { "Content-Type" => "application/json" } })
45
-
46
- slack_response = Process::Discord::Types::Response.new(response)
47
-
48
- validate_response(slack_response)
49
- end
50
-
51
- private
52
-
53
- def post_body(payload)
54
- {
55
- username: name,
56
- text: payload
57
- }.to_json
58
- end
59
-
60
- def validate_response(response)
61
- case response.http_code
62
- when 403
63
- raise Process::Slack::Exceptions::InvalidWebookToken, response.message
64
- else
65
- Process::Types::Response.new(response)
66
- end
67
- end
68
- end
69
- end
70
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Process
4
- module Slack
5
- module Types
6
- ##
7
- # Represents a response received from Slack. It encapsulates essential information about the response,
8
- # providing a structured way to handle and analyze Slack server responses.
9
- #
10
- class Response
11
- attr_reader :body, :http_code, :message
12
-
13
- def initialize(response)
14
- @http_code = response.code
15
- @message = response.message
16
- @body = response.body
17
- end
18
- end
19
- end
20
- end
21
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Process
4
- module Types
5
- ##
6
- # Represents a response received from a Process. It encapsulates the formatted data to be used by
7
- # a Write component.
8
- class Response
9
- attr_reader :data
10
-
11
- def initialize(response)
12
- @data = response
13
- end
14
- end
15
- end
16
- end
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "octokit"
4
- require "openssl"
5
- require "jwt"
6
-
7
- require_relative "../base"
8
- require_relative "./types/response"
9
-
10
- module Read
11
- module Github
12
- ##
13
- # This class is an implementation of the Read::Base interface, specifically designed
14
- # for reading data from a GitHub repository.
15
- #
16
- class Base < Read::Base
17
- protected
18
-
19
- # Implements the data reading logic to get data from a Github repository.
20
- # It connects to Github using the octokit gem, authenticates with a github app,
21
- # request the data and returns a validated response.
22
- #
23
- def read(method, *filter)
24
- octokit_response = octokit.public_send(method, *filter)
25
-
26
- Read::Github::Types::Response.new(octokit_response)
27
- end
28
-
29
- private
30
-
31
- def octokit
32
- Octokit::Client.new(bearer_token: access_token)
33
- end
34
-
35
- def access_token
36
- app = Octokit::Client.new(client_id: config[:app_id], bearer_token: jwt)
37
-
38
- app.create_app_installation_access_token(config[:installation_id])[:token]
39
- end
40
-
41
- def jwt
42
- private_pem = File.read(config[:secret_path])
43
- private_key = OpenSSL::PKey::RSA.new(private_pem)
44
-
45
- JWT.encode(jwt_payload, private_key, "RS256")
46
- end
47
-
48
- def jwt_payload
49
- {
50
- iat: Time.now.to_i - 60,
51
- exp: Time.now.to_i + (10 * 60),
52
- iss: config[:app_id]
53
- }
54
- end
55
- end
56
- end
57
- end