bas 0.4.0 → 1.0.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 (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