hephaestus 0.6.4 → 0.7.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.
@@ -0,0 +1,89 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ require "openapi_first"
5
+
6
+ module Hephaestus
7
+ module Middleware
8
+ class OpenapiValidation
9
+ API_PATH_PREFIX = "/api/"
10
+ API_SPEC_PATH = Rails.root.join("lib/schemas/api/2023-03-06/openapi.json")
11
+
12
+ def initialize(app, ignore_path: "/settings", match_path: API_PATH_PREFIX, limit_methods_to: nil, spec: API_SPEC_PATH)
13
+ @app = app
14
+ @ignore_path = ignore_path
15
+ @match_path = match_path
16
+ @limit_methods_to = limit_methods_to
17
+ @spec = OpenapiFirst.load(spec)
18
+ end
19
+
20
+ def call(env)
21
+ request = Rack::Request.new(env)
22
+
23
+ return @app.call(env) if request.path.include?(@ignore_path)
24
+ return @app.call(env) unless request.path.starts_with?(@match_path)
25
+
26
+ return @app.call(env) if @limit_methods_to.present? && @limit_methods_to.exclude?(request.request_method)
27
+
28
+ begin
29
+ # force content-type to JSON
30
+ env["CONTENT_TYPE"] = "application/json" if env["CONTENT_TYPE"] != "application/json"
31
+
32
+ validated_request = @spec.validate_request(request)
33
+
34
+ return @app.call(env) if validated_request.valid?
35
+
36
+ case validated_request.error
37
+ when OpenapiFirst::Schema::ValidationError
38
+ error_arr = format_arr(validated_request.error.errors.map(&:message))
39
+ Rails.logger.error(error_arr) if print_user_api_errors?
40
+ [Hephaestus::HTTP::BAD_REQUEST_I, { "Content-Type" => "application/json" }, [error_arr]]
41
+ else
42
+ case validated_request.error.type
43
+ when :not_found
44
+ [Hephaestus::HTTP::NOT_FOUND_I, { "Content-Type" => "application/json" }, [format_str("Not Found")]]
45
+ else
46
+ error_message = if validated_request.error.errors.present?
47
+ format_arr(validated_request.error.errors.map(&:message))
48
+ else
49
+ format_str(validated_request.error.message)
50
+ end
51
+
52
+ Rails.logger.error(error_message) if print_user_api_errors?
53
+ [Hephaestus::HTTP::BAD_REQUEST_I, { "Content-Type" => "application/json" }, [error_message]]
54
+ end
55
+ end
56
+ rescue StandardError => e
57
+ raise e unless Rails.env.production?
58
+
59
+ Rails.logger.error(
60
+ "openapi.request_validation.error",
61
+ path: request.path,
62
+ method: request.env["REQUEST_METHOD"],
63
+ error_message: e.message,
64
+ )
65
+ end
66
+ end
67
+
68
+ private def format_str(error)
69
+ {
70
+ errors: [
71
+ {
72
+ message: error,
73
+ },
74
+ ],
75
+ }.to_json
76
+ end
77
+
78
+ private def format_arr(errors)
79
+ {
80
+ errors: errors.each_with_object([]) do |error, arr|
81
+ arr << {
82
+ message: error,
83
+ }
84
+ end,
85
+ }.to_json
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,50 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ require "opentelemetry"
5
+ require "active_support/parameter_filter"
6
+
7
+ module Hephaestus
8
+ module Middleware
9
+ class TracingAttributes
10
+ attr_reader :app
11
+
12
+ HTTP_REQUEST_CONTENT_LENGTH = "http.request_content_length"
13
+ HTTP_REQUEST_BODY = "http.request.body"
14
+
15
+ def initialize(app)
16
+ @app = app
17
+ @filterer = ActiveSupport::ParameterFilter.new(Rails.application.config.filter_parameters)
18
+ end
19
+
20
+ def call(env)
21
+ request = ActionDispatch::Request.new(env.dup)
22
+
23
+ OpenTelemetry::Trace.current_span.add_attributes({
24
+ "version" => Hephaestus::Engine::GIT_SHA,
25
+ HTTP_REQUEST_CONTENT_LENGTH => env["CONTENT_LENGTH"].to_i,
26
+ HTTP_REQUEST_BODY => process_tracing_body(request),
27
+ })
28
+
29
+ app.call(env)
30
+ end
31
+
32
+ def process_tracing_body(request)
33
+ if Rails.configuration.respond_to?(:tracing_body_filters)
34
+ Rails.configuration.tracing_body_filters.each do |path, filter|
35
+ next unless request.path.starts_with?(path)
36
+
37
+ result = filter.call(request)
38
+
39
+ return result if result.is_a?(String)
40
+
41
+ return result.to_json
42
+ end
43
+ end
44
+
45
+ params = request.params
46
+ @filterer.filter(params).to_json
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,7 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module Hephaestus
5
+ module Middleware
6
+ end
7
+ end
@@ -0,0 +1,105 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module Hephaestus
5
+ module API
6
+ module TestHelpers
7
+ include Rack::Test::Methods
8
+
9
+ def plug(method, path, headers: {}, version: Rails.configuration.yetto_api_version, body: {}, parse: true)
10
+ prepended_path = prepend_plug_path(version, path)
11
+
12
+ http_call(method, prepended_path, headers: headers, version: version, body: body, parse: true)
13
+ end
14
+
15
+ def api(method, path, headers: {}, version: Rails.configuration.yetto_api_version, body: {}, parse: true)
16
+ prepended_path = prepend_api_path(version, path)
17
+
18
+ http_call(method, prepended_path, headers: headers, version: version, body: body, parse: true)
19
+ end
20
+
21
+ def http_call(method, path, headers: {}, version: nil, body: {}, parse: true)
22
+ # explicitly assert headers cannot be nil
23
+ if headers.nil?
24
+ send(method, path, body.to_json)
25
+ else
26
+ headers["HTTP_ACCEPT"] ||= headers.fetch("HTTP_ACCEPT", "application/json")
27
+ headers["CONTENT_TYPE"] ||= headers.fetch("CONTENT_TYPE", "application/json")
28
+ body = body.to_json if headers["CONTENT_TYPE"] == "application/json"
29
+ send(method, path, body, headers)
30
+ end
31
+
32
+ JSON.parse(last_response.body) if last_response.body.present? && parse
33
+ end
34
+
35
+ def assert_response(expected_status, expected_body = nil)
36
+ expected_status = case expected_status
37
+ when :ok, :okay
38
+ Hephaestus::HTTP::OK_I
39
+ when :created
40
+ Hephaestus::HTTP::CREATED_I
41
+ when :no_content
42
+ Hephaestus::HTTP::NO_CONTENT_I
43
+ when :redirect
44
+ Hephaestus::HTTP::FOUND_I
45
+ when :bad_request
46
+ Hephaestus::HTTP::BAD_REQUEST_I
47
+ when :unauthorized
48
+ Hephaestus::HTTP::UNAUTHORIZED_I
49
+ when :forbidden
50
+ Hephaestus::HTTP::FORBIDDEN_I
51
+ when :not_found
52
+ Hephaestus::HTTP::NOT_FOUND_I
53
+ when :not_acceptable
54
+ Hephaestus::HTTP::NOT_ACCEPTABLE_I
55
+ when :service_unavailable
56
+ Hephaestus::HTTP::SERVICE_UNAVAILABLE_I
57
+ else
58
+ raise ArgumentError, "Unknown status: #{expected_status}"
59
+ end
60
+
61
+ assert_equal(expected_status, last_response.status)
62
+ end
63
+
64
+ def prepend_plug_path(version, path)
65
+ "/#{plug_shortname}/#{version}#{path}"
66
+ end
67
+
68
+ def prepend_api_path(version, path)
69
+ "/api/#{version}#{path}"
70
+ end
71
+
72
+ def headers(event, body)
73
+ case event
74
+ when :after_create
75
+ {
76
+ Hephaestus::Headers::HEADER_EVENT => "after_create",
77
+ Hephaestus::Headers::HEADER_RECORD_TYPE => "plug_installation",
78
+ }
79
+ when :after_update_plug_installation
80
+ {
81
+ Hephaestus::Headers::HEADER_EVENT => "after_update",
82
+ Hephaestus::Headers::HEADER_RECORD_TYPE => "plug_installation",
83
+ }
84
+ when :after_update_inbox
85
+ {
86
+ Hephaestus::Headers::HEADER_EVENT => "after_update",
87
+ Hephaestus::Headers::HEADER_RECORD_TYPE => "inbox",
88
+ }
89
+ when :after_update_organization
90
+ {
91
+ Hephaestus::Headers::HEADER_EVENT => "after_update",
92
+ Hephaestus::Headers::HEADER_RECORD_TYPE => "organization",
93
+ }
94
+ when :after_destroy_plug_installation
95
+ {
96
+ Hephaestus::Headers::HEADER_EVENT => "after_destroy",
97
+ Hephaestus::Headers::HEADER_RECORD_TYPE => "plug_installation",
98
+ }
99
+ else
100
+ {}
101
+ end.merge(Hephaestus::Headers::HEADER_SIGNATURE => yetto_auth_header(body))
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,21 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module Hephaestus
5
+ module Webmocks
6
+ module BrowserWebmock
7
+ def browser_mock(status: 200)
8
+ browser_mock = Object.new
9
+ Ferrum::Browser.stubs(:new).returns(browser_mock)
10
+ browser_mock.stubs(:go_to)
11
+ browser_mock.stubs(:quit)
12
+ response_mock = Ferrum::Network::Response.new("", "")
13
+ response_mock.stubs(:wait_for_idle).returns(true)
14
+ network_response_mock = Object.new
15
+ browser_mock.stubs(:network).returns(network_response_mock)
16
+ network_response_mock.stubs(:response).returns(response_mock)
17
+ response_mock.stubs(:status).returns(status)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module Hephaestus
5
+ module Webmocks
6
+ module SlackWebmock
7
+ def assert_requested_send_to_slack_log
8
+ assert_requested(:post, "https://slack.com/the_log_room")
9
+ end
10
+
11
+ def stub_send_to_slack_log
12
+ stub_request(:post, "https://slack.com/the_log_room")
13
+ .to_return(
14
+ status: 200,
15
+ headers: { content_type: "application/json; charset=utf-8" },
16
+ body: {}.to_json,
17
+ )
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,195 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module Hephaestus
5
+ module Webmocks
6
+ module YettoWebmock
7
+ def yetto_auth_header(payload, signing_secret = SIGNING_SECRET)
8
+ "sha256=#{OpenSSL::HMAC.hexdigest(Hephaestus::ValidatesFromYetto::SHA256_DIGEST, signing_secret, payload.to_json)}"
9
+ end
10
+
11
+ def assert_requested_post_access_token(plug_installation_id, times: 1)
12
+ assert_requested(:post, "#{Hephaestus::YettoService::YETTO_API_VERSION_TLD}/plug/installations/#{plug_installation_id}/access_tokens", times: times)
13
+ end
14
+
15
+ def stub_post_access_token(plug_installation_id)
16
+ response = {
17
+ token: Faker::Alphanumeric.alphanumeric(number: 26).upcase,
18
+ }
19
+
20
+ stub_request(:post, "#{Hephaestus::YettoService::YETTO_API_VERSION_TLD}/plug/installations/#{plug_installation_id}/access_tokens")
21
+ .to_return(
22
+ body: response.to_json,
23
+ )
24
+ end
25
+
26
+ def assert_requested_get_plug_installation(plug_installation_id, times: 1)
27
+ assert_requested(:get, "#{Hephaestus::YettoService::YETTO_API_VERSION_TLD}/installations/#{plug_installation_id}", times:)
28
+ end
29
+
30
+ def stub_get_plug_installation(plug_installation_id, response: {}, status: 200)
31
+ stub_request(:get, "#{Hephaestus::YettoService::YETTO_API_VERSION_TLD}/installations/#{plug_installation_id}")
32
+ .to_return(
33
+ status: status,
34
+ headers: { content_type: "application/json; charset=utf-8" },
35
+ body: response.to_json,
36
+ )
37
+ end
38
+
39
+ def assert_requested_update_plug_installation(plug_installation_id)
40
+ assert_requested(:patch, "#{Hephaestus::YettoService::YETTO_API_VERSION_TLD}/installations/#{plug_installation_id}")
41
+ end
42
+
43
+ def assert_not_requested_update_plug_installation(plug_installation_id)
44
+ assert_requested(:patch, "#{Hephaestus::YettoService::YETTO_API_VERSION_TLD}/installations/#{plug_installation_id}", times: 0)
45
+ end
46
+
47
+ def stub_update_plug_installation(plug_installation_id, params, response: {}, status: 200)
48
+ stub_post_access_token(plug_installation_id)
49
+
50
+ response[:plug_installation] = { id: plug_installation_id }
51
+
52
+ stub_request(:patch, "#{Hephaestus::YettoService::YETTO_API_VERSION_TLD}/installations/#{plug_installation_id}")
53
+ .with(
54
+ body: params,
55
+ )
56
+ .to_return(
57
+ status: status,
58
+ headers: { content_type: "application/json; charset=utf-8" },
59
+ body: response.to_json,
60
+ )
61
+ end
62
+
63
+ def assert_requested_create_conversation(plug_installation_id, inbox_id)
64
+ assert_requested(:post, "#{Hephaestus::YettoService::YETTO_API_VERSION_TLD}/inboxes/#{inbox_id}/conversations")
65
+ end
66
+
67
+ def stub_create_conversation(plug_installation_id, inbox_id, payload, response: {})
68
+ stub_post_access_token(plug_installation_id)
69
+
70
+ response[:plug_installation] = { id: plug_installation_id }
71
+
72
+ stub_request(:post, "#{Hephaestus::YettoService::YETTO_API_VERSION_TLD}/inboxes/#{inbox_id}/conversations")
73
+ .with(
74
+ body: payload,
75
+ )
76
+ .to_return(
77
+ status: 200,
78
+ headers: { content_type: "application/json; charset=utf-8" },
79
+ body: response.to_json,
80
+ )
81
+ end
82
+
83
+ def assert_requested_get_plug_installations(filter: "")
84
+ assert_requested(:get, "#{Hephaestus::YettoService::YETTO_API_VERSION_TLD}/plug/installations?#{filter}")
85
+ end
86
+
87
+ def stub_get_plug_installations(response, status: 200, filter: "")
88
+ stub_request(:get, "#{Hephaestus::YettoService::YETTO_API_VERSION_TLD}/plug/installations?#{filter}")
89
+ .to_return(
90
+ status: status,
91
+ headers: { content_type: "application/json; charset=utf-8" },
92
+ body: response.to_json,
93
+ )
94
+ end
95
+
96
+ def assert_requested_get_messages_in_inbox(plug_installation_id, inbox_id, filter: "")
97
+ assert_requested(:get, "#{Hephaestus::YettoService::YETTO_API_VERSION_TLD}/inboxes/#{inbox_id}/messages?#{filter}")
98
+ end
99
+
100
+ def stub_get_messages_in_inbox(plug_installation_id, inbox_id, response, filter: "")
101
+ stub_post_access_token(plug_installation_id)
102
+ stub_request(:get, "#{Hephaestus::YettoService::YETTO_API_VERSION_TLD}/inboxes/#{inbox_id}/messages?#{filter}")
103
+ .to_return(
104
+ status: 200,
105
+ body: response.to_json,
106
+ )
107
+ end
108
+
109
+ def assert_requested_get_messages_in_conversation(plug_installation_id, conversation_id, filter: "")
110
+ assert_requested(:get, "#{::Hephaestus::YettoService::YETTO_API_VERSION_TLD}/conversations/#{conversation_id}/messages?#{filter}")
111
+ end
112
+
113
+ def stub_get_messages_in_conversation(plug_installation_id, conversation_id, response, status: 200, filter: "")
114
+ stub_post_access_token(plug_installation_id)
115
+
116
+ stub_request(:get, "#{::Hephaestus::YettoService::YETTO_API_VERSION_TLD}/conversations/#{conversation_id}/messages?#{filter}")
117
+ .to_return(
118
+ status: status,
119
+ headers: { content_type: "application/json; charset=utf-8" },
120
+ body: response.to_json,
121
+ )
122
+ end
123
+
124
+ def stub_add_message_to_conversation(plug_installation_id, conversation_id, payload, response: {}, status: 200)
125
+ stub_post_access_token(plug_installation_id)
126
+
127
+ response[:plug_installation] = { id: plug_installation_id }
128
+
129
+ stub_request(:post, "#{Hephaestus::YettoService::YETTO_API_VERSION_TLD}/conversations/#{conversation_id}/messages")
130
+ .with(
131
+ body: payload,
132
+ )
133
+ .to_return(
134
+ status: status,
135
+ headers: { content_type: "application/json; charset=utf-8" },
136
+ body: response.to_json,
137
+ )
138
+ end
139
+
140
+ def assert_requested_add_message_to_conversation(plug_installation_id, conversation_id)
141
+ assert_requested(:post, "#{Hephaestus::YettoService::YETTO_API_VERSION_TLD}/conversations/#{conversation_id}/messages")
142
+ end
143
+
144
+ def assert_requested_create_message(plug_installation_id, message_id)
145
+ assert_requested(:post, "#{::Hephaestus::YettoService::YETTO_API_VERSION_TLD}/messages/#{message_id}/replies")
146
+ end
147
+
148
+ def stub_create_message(plug_installation_id, message_id, payload)
149
+ stub_post_access_token(plug_installation_id)
150
+
151
+ stub_request(:post, "#{::Hephaestus::YettoService::YETTO_API_VERSION_TLD}/messages/#{message_id}/replies")
152
+ .with(
153
+ body: payload,
154
+ )
155
+ .to_return(
156
+ status: 200,
157
+ headers: { content_type: "application/json; charset=utf-8" },
158
+ body: {}.to_json,
159
+ )
160
+ end
161
+
162
+ def assert_requested_update_message(plug_installation_id, message_id)
163
+ assert_requested(:patch, "#{::Hephaestus::YettoService::YETTO_API_VERSION_TLD}/messages/#{message_id}")
164
+ end
165
+
166
+ def stub_update_message(plug_installation_id, message_id, payload)
167
+ stub_post_access_token(plug_installation_id)
168
+ stub_request(:patch, "#{::Hephaestus::YettoService::YETTO_API_VERSION_TLD}/messages/#{message_id}")
169
+ .with(
170
+ body: payload,
171
+ )
172
+ .to_return(
173
+ status: 200,
174
+ headers: { content_type: "application/json; charset=utf-8" },
175
+ body: {}.to_json,
176
+ )
177
+ end
178
+
179
+ def assert_requested_find_message_in_conversation_by_metadata(plug_installation_id, conversation_id)
180
+ assert_requested(:get, "#{Hephaestus::YettoService::YETTO_API_VERSION_TLD}/conversations/#{conversation_id}/messages?#{filter}")
181
+ end
182
+
183
+ def stub_find_message_in_conversation(plug_installation_id, conversation_id, response, status: 200, filter: "")
184
+ stub_post_access_token(plug_installation_id)
185
+
186
+ stub_request(:get, "#{Hephaestus::YettoService::YETTO_API_VERSION_TLD}/conversations/#{conversation_id}/messages?#{filter}")
187
+ .to_return(
188
+ status: status,
189
+ headers: { content_type: "application/json; charset=utf-8" },
190
+ body: response.to_json,
191
+ )
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,35 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module ActiveSupport
5
+ class TestCase
6
+ # Run tests in parallel with specified workers
7
+ parallelize(workers: :number_of_processors)
8
+
9
+ def setup
10
+ @organization_id ||= "org_#{Faker::Alphanumeric.alphanumeric(number: 26).upcase}"
11
+ @plug_installation_id ||= "pli_#{Faker::Alphanumeric.alphanumeric(number: 26).upcase}"
12
+ @inbox_id ||= "ibx_#{Faker::Alphanumeric.alphanumeric(number: 26).upcase}"
13
+ @plug_id ||= "plg_#{Faker::Alphanumeric.alphanumeric(number: 26).upcase}"
14
+ @message_id ||= "msg_#{Faker::Alphanumeric.alphanumeric(number: 26).upcase}"
15
+ @conversation_id ||= "cnv_#{Faker::Alphanumeric.alphanumeric(number: 26).upcase}"
16
+ end
17
+
18
+ def file_fixture_path(dir, name)
19
+ Rails.root.join("test", "fixtures", "files", dir, name)
20
+ end
21
+
22
+ def assert_expected_args(expected_args)
23
+ lambda do |job_args_arr|
24
+ job_args_arr.first.each do |actual_args|
25
+ actual_key = actual_args.first
26
+ actual_value = actual_args.second
27
+
28
+ expected_value = expected_args[actual_key]
29
+
30
+ assert_equal(expected_value, actual_value, "Expected `#{actual_key}` to be `#{expected_value.nil? ? "nil" : expected_value}`, but was `#{actual_value.nil? ? "nil" : actual_value}`")
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,45 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ ENV["RAILS_ENV"] ||= "test"
5
+
6
+ require_relative File.join($LOAD_PATH.last, "..", "config", "environment.rb")
7
+
8
+ module Hephaestus
9
+ module TestHelper
10
+ require "minitest/autorun"
11
+ require "minitest/pride"
12
+
13
+ require "debug" if ENV.fetch("DEBUG", false)
14
+
15
+ if ENV["COVERAGE"] == "1"
16
+ require "simplecov"
17
+ require "simplecov-console"
18
+
19
+ SimpleCov.start("rails")
20
+
21
+ # do not crash on failure; we want a distinct job to report the coverage error
22
+ module SimpleCov
23
+ class << self
24
+ def result_exit_status(_)
25
+ SimpleCov::ExitCodes::SUCCESS
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ require "webmock"
32
+ require "httpx/adapters/webmock"
33
+ require "webmock/minitest"
34
+ WebMock.enable!
35
+ WebMock.disable_net_connect!(allow_localhost: true)
36
+
37
+ require_relative "support/rails"
38
+
39
+ # Load everything else from engine's test/support
40
+ Dir[File.expand_path("support/hephaestus/**/*.rb", __dir__)].each { |rb| require(rb) }
41
+
42
+ # https://github.com/freerange/mocha#rails
43
+ require "mocha/minitest"
44
+ end
45
+ end
@@ -2,8 +2,8 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Hephaestus
5
- VERSION = "0.6.4"
6
- RAILS_VERSION = "~> 7.0"
5
+ VERSION = "0.7.0"
6
+ RAILS_VERSION = ">= 8.0"
7
7
  RUBY_VERSION = File
8
8
  .read("#{File.dirname(__FILE__)}/../../.ruby-version")
9
9
  .strip
data/lib/hephaestus.rb CHANGED
@@ -10,6 +10,7 @@ end
10
10
  require "debug" if debugging?
11
11
 
12
12
  require "hephaestus/version"
13
+ require "hephaestus/engine"
13
14
  require "hephaestus/exit_on_failure"
14
15
 
15
16
  require "hephaestus/generators/app_generator"
@@ -0,0 +1,7 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ # desc "Explaining what the task does"
5
+ # task :hephaestus do
6
+ # # Task goes here
7
+ # end
@@ -0,0 +1,9 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ require "rubygems/package_task"
5
+
6
+ GEMSPEC = Bundler.load_gemspec(File.join(__dir__, "..", "..", "hephaestus.gemspec")) unless defined?(GEMSPEC)
7
+ gem_path = Gem::PackageTask.new(GEMSPEC).define
8
+ desc "Package the ruby gem"
9
+ task "package" => [gem_path]
@@ -0,0 +1,6 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ require "rubocop/rake_task"
5
+
6
+ RuboCop::RakeTask.new
data/lib/version.rb ADDED
@@ -0,0 +1,6 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module Hephaestus
5
+ VERSION = "0.1.0"
6
+ end
@@ -48,6 +48,7 @@ class AppController < ApplicationController
48
48
  text_content: text_body,
49
49
  is_public: true,
50
50
  author: {
51
+ version: "2023-03-06",
51
52
  name: from_email,
52
53
  },
53
54
  attachments: bparam_attachments,