hephaestus 0.7.1 → 0.7.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -0
- data/app/controllers/concerns/hephaestus/responses.rb +107 -0
- data/app/controllers/concerns/hephaestus/validates_from_yetto.rb +51 -0
- data/app/controllers/hephaestus/application_controller.rb +42 -0
- data/app/controllers/hephaestus/root_controller.rb +10 -0
- data/app/controllers/hephaestus/settings_controller.rb +20 -0
- data/app/controllers/hephaestus/staff_controller.rb +20 -0
- data/app/jobs/hephaestus/application_job.rb +12 -0
- data/app/jobs/hephaestus/update_yetto_job.rb +39 -0
- data/app/models/hephaestus/application_record.rb +8 -0
- data/app/serializers/hephaestus/error_serializer.rb +18 -0
- data/app/serializers/hephaestus/headers.rb +27 -0
- data/app/services/hephaestus/yetto_service.rb +99 -0
- data/app/views/layouts/staff.html.erb +7 -0
- data/app/views/staff/index.html.erb +1 -0
- data/config/database.yml +49 -0
- data/config/environments/development.rb +89 -0
- data/config/environments/production.rb +96 -0
- data/config/environments/staging.rb +94 -0
- data/config/environments/test.rb +73 -0
- data/config/initializers/application.rb +14 -0
- data/config/initializers/cors.rb +19 -0
- data/config/initializers/environment.rb +94 -0
- data/config/initializers/filter_parameter_logging.rb +23 -0
- data/config/initializers/inflections.rb +21 -0
- data/config/initializers/litestream.rb +36 -0
- data/config/initializers/lograge.rb +27 -0
- data/config/initializers/opentelemetry.rb +41 -0
- data/config/initializers/sidekiq.rb +13 -0
- data/config/initializers/slack_webhook_logger.rb +19 -0
- data/config/litestream.yml +12 -0
- data/config/queue.yml +18 -0
- data/config/recurring.yml +10 -0
- data/config/routes.rb +17 -0
- data/db/queue_schema.rb +129 -0
- data/lib/hephaestus/support/hephaestus/webmocks/yetto_webmock.rb +1 -1
- data/lib/hephaestus/version.rb +1 -1
- data/templates/app/services/yetto_service.rb +1 -1
- metadata +36 -19
- data/templates/.dockerignore +0 -39
- data/templates/.env.sample +0 -6
- data/templates/.github/dependabot.yml +0 -27
- data/templates/.github/workflows/automerge.yml +0 -17
- data/templates/.github/workflows/deploy.yml +0 -30
- data/templates/.github/workflows/licenses.yml +0 -23
- data/templates/.github/workflows/lint.yml +0 -32
- data/templates/.github/workflows/security.yml +0 -15
- data/templates/.github/workflows/sorbet.yml +0 -19
- data/templates/.github/workflows/test.yml +0 -21
- data/templates/.licensed.yml +0 -43
- data/templates/.rubocop.yml +0 -5
- data/templates/.ruby-version +0 -1
- data/templates/.vscode/extensions.json +0 -9
- data/templates/.vscode/launch.json +0 -13
- data/templates/.vscode/settings.json +0 -52
- data/templates/test/integration/.keep +0 -0
- data/templates/test/mailers/.keep +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d7c2f9d61793dbe5625865f819df27d6c9d63af6a8b62d24baad0179cb18292a
|
4
|
+
data.tar.gz: 2b3b19c2cf731b161b8d669e82613ef734b3724580ffaed458912e16ea2dee4e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0dab68747b8859e1f6d0b24bb2d5d3243f4bb0278602d132eaa4a822d18b7ffaf69b9f683edf1fe1af807bfced0e009010433095725c083582decf8b0a4116f8
|
7
|
+
data.tar.gz: aaf595de123fbb62eb0582e05d94f3f437c2e0eca9564c563ec6485ba65aede1f690c8d91b266be5235c5755770136b7c46a7646c0b49006131dff1978b78c13
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,22 @@
|
|
1
|
+
# [v0.7.2.1] - 16-11-2024
|
2
|
+
## What's Changed
|
3
|
+
* include all files in the gem by @gjtorikian in https://github.com/yettoapp/hephaestus/pull/31
|
4
|
+
|
5
|
+
|
6
|
+
**Full Changelog**: https://github.com/yettoapp/hephaestus/compare/v0.7.2...v0.7.2.1
|
7
|
+
# [v0.7.2] - 16-11-2024
|
8
|
+
## What's Changed
|
9
|
+
* [skip test] Release v0.7.1 by @github-actions in https://github.com/yettoapp/hephaestus/pull/28
|
10
|
+
* Move secrets into Hephaestus by @gjtorikian in https://github.com/yettoapp/hephaestus/pull/29
|
11
|
+
|
12
|
+
|
13
|
+
**Full Changelog**: https://github.com/yettoapp/hephaestus/compare/v0.7.1...v0.7.2
|
14
|
+
# [v0.7.1] - 16-11-2024
|
15
|
+
## What's Changed
|
16
|
+
* Run as an engine by @gjtorikian in https://github.com/yettoapp/hephaestus/pull/27
|
17
|
+
|
18
|
+
|
19
|
+
**Full Changelog**: https://github.com/yettoapp/hephaestus/compare/v0.7.0...v0.7.1
|
1
20
|
# [v0.7.0] - 15-11-2024
|
2
21
|
## What's Changed
|
3
22
|
* Run as an engine by @gjtorikian in https://github.com/yettoapp/hephaestus/pull/25
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Hephaestus
|
5
|
+
module Responses
|
6
|
+
def no_content
|
7
|
+
head(:no_content)
|
8
|
+
end
|
9
|
+
|
10
|
+
def okay
|
11
|
+
render(
|
12
|
+
json: {
|
13
|
+
message: "OK",
|
14
|
+
}.to_json,
|
15
|
+
status: :ok,
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
def created
|
20
|
+
render(
|
21
|
+
json: {
|
22
|
+
message: "Created",
|
23
|
+
}.to_json,
|
24
|
+
status: :created,
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
def bad_request
|
29
|
+
render(
|
30
|
+
json: {
|
31
|
+
errors: [
|
32
|
+
{
|
33
|
+
message: "Bad Request",
|
34
|
+
},
|
35
|
+
],
|
36
|
+
}.to_json,
|
37
|
+
status: :bad_request,
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
def forbidden
|
42
|
+
render(
|
43
|
+
json: {
|
44
|
+
errors: [
|
45
|
+
{
|
46
|
+
message: "Forbidden",
|
47
|
+
},
|
48
|
+
],
|
49
|
+
}.to_json,
|
50
|
+
status: :forbidden,
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
def not_acceptable
|
55
|
+
render(
|
56
|
+
json: ::Hephaestus::ErrorSerializer.format("Not Acceptable").to_json,
|
57
|
+
status: :not_acceptable,
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
def not_found
|
62
|
+
render(
|
63
|
+
json: ::Hephaestus::ErrorSerializer.format("Not Found").to_json,
|
64
|
+
status: :not_found,
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
def service_unavailable(msg)
|
69
|
+
render(
|
70
|
+
json: {
|
71
|
+
errors: [
|
72
|
+
{
|
73
|
+
message: "Service Unavailable: #{msg}",
|
74
|
+
},
|
75
|
+
],
|
76
|
+
}.to_json,
|
77
|
+
status: :service_unavailable,
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
def bad_gateway
|
82
|
+
render(
|
83
|
+
json: {
|
84
|
+
errors: [
|
85
|
+
{
|
86
|
+
message: "Bad Gateway",
|
87
|
+
},
|
88
|
+
],
|
89
|
+
}.to_json,
|
90
|
+
status: :bad_gateway,
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
def internal_server_error
|
95
|
+
render(
|
96
|
+
json: {
|
97
|
+
errors: [
|
98
|
+
{
|
99
|
+
message: "Internal Server Error",
|
100
|
+
},
|
101
|
+
],
|
102
|
+
}.to_json,
|
103
|
+
status: :internal_server_error,
|
104
|
+
)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Hephaestus
|
5
|
+
module ValidatesFromYetto
|
6
|
+
SHA256_DIGEST = OpenSSL::Digest.new("sha256")
|
7
|
+
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
include Hephaestus::Responses
|
11
|
+
|
12
|
+
included do
|
13
|
+
before_action :from_yetto?
|
14
|
+
end
|
15
|
+
|
16
|
+
def from_yetto?
|
17
|
+
return bad_request if request.headers.blank?
|
18
|
+
|
19
|
+
yetto_signature = request.headers.fetch(Hephaestus::Headers::HEADER_SIGNATURE, "")
|
20
|
+
|
21
|
+
return bad_request unless yetto_signature.start_with?("sha256=")
|
22
|
+
|
23
|
+
hmac_header = yetto_signature.split("sha256=").last
|
24
|
+
body = request.env.fetch("RAW_POST_DATA", "")
|
25
|
+
|
26
|
+
calculated_hmac = OpenSSL::HMAC.hexdigest(SHA256_DIGEST, Hephaestus::YETTO_SIGNING_SECRET, body)
|
27
|
+
|
28
|
+
return true if ActiveSupport::SecurityUtils.secure_compare(calculated_hmac, hmac_header)
|
29
|
+
|
30
|
+
bad_request
|
31
|
+
end
|
32
|
+
|
33
|
+
def from_yetto_inline?
|
34
|
+
return bad_request if request.headers.blank?
|
35
|
+
|
36
|
+
yetto_signature = request.headers.fetch(Hephaestus::Headers::HEADER_SIGNATURE, "")
|
37
|
+
|
38
|
+
return bad_request unless yetto_signature.start_with?("sha256=")
|
39
|
+
|
40
|
+
hmac_header = yetto_signature.split("sha256=").last
|
41
|
+
body = params["encrypted_payload"]
|
42
|
+
|
43
|
+
@payload = T.let(ActiveSupport::MessageEncryptor.new(Hephaestus::YETTO_SIGNING_SECRET, url_safe: true, serializer: :json).decrypt_and_verify(body), T.nilable(String))
|
44
|
+
calculated_hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha256"), Hephaestus::YETTO_SIGNING_SECRET, @payload)
|
45
|
+
|
46
|
+
return true if ActiveSupport::SecurityUtils.secure_compare(calculated_hmac, hmac_header)
|
47
|
+
|
48
|
+
bad_request
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "opentelemetry-semantic_conventions"
|
5
|
+
|
6
|
+
module Hephaestus
|
7
|
+
class ApplicationController < ActionController::Base
|
8
|
+
include ActionController::MimeResponds
|
9
|
+
|
10
|
+
include Hephaestus::Headers
|
11
|
+
include Hephaestus::Responses
|
12
|
+
|
13
|
+
rescue_from ActionController::UnknownFormat, with: :not_acceptable
|
14
|
+
protect_from_forgery with: :null_session
|
15
|
+
|
16
|
+
def ensure_json_request
|
17
|
+
return if request.format.json?
|
18
|
+
|
19
|
+
not_acceptable
|
20
|
+
end
|
21
|
+
|
22
|
+
# Yetto sends a unique state parameter as part of the OAuth installation process.
|
23
|
+
# This decodes that into something the plug can use.
|
24
|
+
def parse_state(state)
|
25
|
+
decoded_state = Base64.urlsafe_decode64(state)
|
26
|
+
state = JSON.parse(decoded_state)
|
27
|
+
|
28
|
+
{
|
29
|
+
version: state["version"],
|
30
|
+
salt: state["salt"],
|
31
|
+
nonce: state["nonce"] || "",
|
32
|
+
plug_installation_id: state["plug_installation_id"],
|
33
|
+
membership_id: state["membership_id"], # optional, used for conversation.viewed events
|
34
|
+
redirect_to: state["redirect_to"],
|
35
|
+
}
|
36
|
+
rescue ArgumentError # invalid base64
|
37
|
+
{}
|
38
|
+
rescue JSON::ParserError # invalid JSON
|
39
|
+
{}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Hephaestus
|
5
|
+
class SettingsController < ApplicationController
|
6
|
+
before_action :ensure_json_request
|
7
|
+
|
8
|
+
def new
|
9
|
+
set_step
|
10
|
+
render("settings/new")
|
11
|
+
end
|
12
|
+
|
13
|
+
def edit
|
14
|
+
end
|
15
|
+
|
16
|
+
def set_step
|
17
|
+
@step = params.fetch(:step, 1).to_i || 1
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Hephaestus
|
5
|
+
class StaffController < ApplicationController
|
6
|
+
layout "staff"
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def staff_request?(request)
|
10
|
+
return true if Rails.env.development?
|
11
|
+
|
12
|
+
true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def index
|
17
|
+
render404 unless StaffController.staff_request?(request)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Hephaestus
|
5
|
+
class ApplicationJob < ActiveJob::Base
|
6
|
+
# Automatically retry jobs that encountered a deadlock
|
7
|
+
# retry_on ActiveRecord::Deadlocked
|
8
|
+
|
9
|
+
# Most jobs are safe to ignore if the underlying records are no longer available
|
10
|
+
# discard_on ActiveJob::DeserializationError
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Send updated data to Yetto to store in the database
|
5
|
+
# This can be used to update installation data or message data
|
6
|
+
module Hephaestus
|
7
|
+
class UpdateYettoJob < ApplicationJob
|
8
|
+
queue_as :high_priority_update_yetto
|
9
|
+
|
10
|
+
def perform(params)
|
11
|
+
type = params.delete(:type)
|
12
|
+
params.deep_symbolize_keys!
|
13
|
+
|
14
|
+
inbox_id = params.fetch(:inbox, {}).fetch(:id, nil)
|
15
|
+
plug_installation_id = params.fetch(:plug_installation, {}).fetch(:id, nil)
|
16
|
+
conversation_id = params.fetch(:conversation, {}).fetch(:id, nil)
|
17
|
+
message_id = params.fetch(:message, {}).fetch(:id, nil)
|
18
|
+
|
19
|
+
response = case type
|
20
|
+
when "update_plug_installation"
|
21
|
+
YettoService.update_plug_installation(plug_installation_id, params[:payload])
|
22
|
+
when "create_conversation"
|
23
|
+
YettoService.create_conversation(plug_installation_id, inbox_id, params[:payload])
|
24
|
+
when "add_message_to_conversation"
|
25
|
+
YettoService.add_message_to_conversation(plug_installation_id, conversation_id, params[:payload])
|
26
|
+
when "create_message_reply"
|
27
|
+
YettoService.create_message_reply(plug_installation_id, message_id, params[:payload])
|
28
|
+
when "update_message"
|
29
|
+
YettoService.update_message(plug_installation_id, message_id, params[:payload])
|
30
|
+
end
|
31
|
+
|
32
|
+
response, type = Rails.configuration.enhance_update_yetto_job.call(params, type, response) if Rails.configuration.respond_to?(:enhance_update_yetto_job)
|
33
|
+
|
34
|
+
if response.present? && response.unavailable?
|
35
|
+
logger.error("Could not #{type} for #{plug_installation_id}: #{response.parsed_json_body}")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Hephaestus
|
5
|
+
module Headers
|
6
|
+
YETTO_DELIVERY_ID = "HTTP_X_YETTO_DELIVERY_ID"
|
7
|
+
|
8
|
+
HEADER_EVENT = "HTTP_X_YETTO_EVENT"
|
9
|
+
EVENT_AFTER_CREATE = "created"
|
10
|
+
EVENT_AFTER_UPDATE = "updated"
|
11
|
+
EVENT_AFTER_RENAME = "renamed"
|
12
|
+
EVENT_AFTER_DESTROY = "destroyed"
|
13
|
+
|
14
|
+
# for conversations
|
15
|
+
EVENT_AFTER_VIEW = "viewed"
|
16
|
+
|
17
|
+
HEADER_RECORD_TYPE = "HTTP_X_YETTO_RECORD_TYPE"
|
18
|
+
RECORD_TYPE_PLUG_INSTALLATION = "plug_installation"
|
19
|
+
RECORD_TYPE_MESSAGE = "message"
|
20
|
+
RECORD_TYPE_INBOX = "inbox"
|
21
|
+
RECORD_TYPE_ORGANIZATION = "organization"
|
22
|
+
|
23
|
+
RECORD_TYPE_CONVERSATION = "conversation"
|
24
|
+
|
25
|
+
HEADER_SIGNATURE = "HTTP_X_YETTO_SIGNATURE"
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "jwt"
|
5
|
+
require "openssl"
|
6
|
+
require "httpsensible"
|
7
|
+
|
8
|
+
class Object
|
9
|
+
# Relies on the fix implemented in https://github.com/rails/rails/pull/39966 to ensure
|
10
|
+
# that blank values don't result in queries with empty values, eg. `?filter[metadata]=`, when
|
11
|
+
# we prefer (and accept!) `?filter[metadata]`.
|
12
|
+
def to_query(key)
|
13
|
+
blank? ? CGI.escape(key.to_param).to_s : "#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module Hephaestus
|
18
|
+
class YettoService
|
19
|
+
# Version is set by the consuming plug
|
20
|
+
YETTO_API_VERSION_TLD = "#{Hephaestus::PROTOCOL}#{Hephaestus::YETTO_API_URL}/#{Rails.configuration.yetto_api_version}"
|
21
|
+
|
22
|
+
class << self
|
23
|
+
def yetto_client
|
24
|
+
@yetto_client ||= Httpsensible::Client.new(user_agent: "#{Rails.application.class.module_parent.name}/#{Hephaestus::Engine::GIT_SHA}")
|
25
|
+
end
|
26
|
+
|
27
|
+
def encoded_jwt
|
28
|
+
Httpsensible::JWT.encode_jwt(Hephaestus::YETTO_PLUG_PEM, Hephaestus::YETTO_PLUG_ID)
|
29
|
+
end
|
30
|
+
|
31
|
+
def perform_token_exchange(plug_installation_id)
|
32
|
+
response = yetto_client.with_headers({ "Authorization" => "Bearer #{encoded_jwt}" }).post("#{YETTO_API_VERSION_TLD}/plug/installations/#{plug_installation_id}/access_tokens")
|
33
|
+
body = response.parsed_json_body
|
34
|
+
body["token"]
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_plug_installation(plug_installation_id)
|
38
|
+
token = perform_token_exchange(plug_installation_id)
|
39
|
+
yetto_client.with_headers({ "Authorization" => "Bearer #{token}" }).get("#{YETTO_API_VERSION_TLD}/installations/#{plug_installation_id}")
|
40
|
+
end
|
41
|
+
|
42
|
+
def update_plug_installation(plug_installation_id, params)
|
43
|
+
plug_installation = {}
|
44
|
+
|
45
|
+
plug_installation[:settings] = params.fetch(:settings, {})
|
46
|
+
plug_installation[:credentials] = params.fetch(:credentials, {})
|
47
|
+
|
48
|
+
token = perform_token_exchange(plug_installation_id)
|
49
|
+
yetto_client.with_headers({ "Authorization" => "Bearer #{token}" }).patch("#{YETTO_API_VERSION_TLD}/installations/#{plug_installation_id}", json: plug_installation)
|
50
|
+
end
|
51
|
+
|
52
|
+
def get_messages_in_inbox(plug_installation_id, inbox_id, filter: {})
|
53
|
+
token = perform_token_exchange(plug_installation_id)
|
54
|
+
|
55
|
+
yetto_client.with_headers({ "Authorization" => "Bearer #{token}" }).get("#{YETTO_API_VERSION_TLD}/inboxes/#{inbox_id}/messages#{to_filter_query(filter)}")
|
56
|
+
end
|
57
|
+
|
58
|
+
def get_messages_in_conversation(plug_installation_id, conversation_id, filter: {})
|
59
|
+
token = perform_token_exchange(plug_installation_id)
|
60
|
+
|
61
|
+
yetto_client.with_headers("Authorization" => "Bearer #{token}").get("#{YETTO_API_VERSION_TLD}/conversations/#{conversation_id}/messages#{to_filter_query(filter)}")
|
62
|
+
end
|
63
|
+
|
64
|
+
def update_message(plug_installation_id, message_id, params)
|
65
|
+
token = perform_token_exchange(plug_installation_id)
|
66
|
+
|
67
|
+
yetto_client.with_headers("Authorization" => "Bearer #{token}").patch("#{YETTO_API_VERSION_TLD}/messages/#{message_id}", json: params)
|
68
|
+
end
|
69
|
+
|
70
|
+
def create_conversation(plug_installation_id, inbox_id, params)
|
71
|
+
token = perform_token_exchange(plug_installation_id)
|
72
|
+
|
73
|
+
yetto_client.with_headers({ "Authorization" => "Bearer #{token}" }).post("#{YETTO_API_VERSION_TLD}/inboxes/#{inbox_id}/conversations", json: params)
|
74
|
+
end
|
75
|
+
|
76
|
+
def create_message_reply(plug_installation_id, message_id, params)
|
77
|
+
token = perform_token_exchange(plug_installation_id)
|
78
|
+
|
79
|
+
yetto_client.with_headers("Authorization" => "Bearer #{token}").post("#{YETTO_API_VERSION_TLD}/messages/#{message_id}/replies", json: params)
|
80
|
+
end
|
81
|
+
|
82
|
+
def add_message_to_conversation(plug_installation_id, conversation_id, params)
|
83
|
+
token = perform_token_exchange(plug_installation_id)
|
84
|
+
|
85
|
+
yetto_client.with_headers({ "Authorization" => "Bearer #{token}" }).post("#{YETTO_API_VERSION_TLD}/conversations/#{conversation_id}/messages", json: params)
|
86
|
+
end
|
87
|
+
|
88
|
+
def get_plug_installations(filter: {})
|
89
|
+
yetto_client.with_headers({ "Authorization" => "Bearer #{encoded_jwt}" }).get("#{YETTO_API_VERSION_TLD}/plug/installations#{to_filter_query(filter)}")
|
90
|
+
end
|
91
|
+
|
92
|
+
private def to_filter_query(hash)
|
93
|
+
return "" if hash.nil?
|
94
|
+
|
95
|
+
"?#{hash.to_query("filter")}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Hello, staff!
|
data/config/database.yml
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# SQLite. Versions 3.8.0 and up are supported.
|
2
|
+
# gem install sqlite3
|
3
|
+
#
|
4
|
+
# Ensure the SQLite 3 gem is defined in your Gemfile
|
5
|
+
# gem "sqlite3"
|
6
|
+
#
|
7
|
+
default: &default
|
8
|
+
adapter: sqlite3
|
9
|
+
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
|
10
|
+
timeout: 5000
|
11
|
+
|
12
|
+
development:
|
13
|
+
primary:
|
14
|
+
<<: *default
|
15
|
+
database: storage/development.sqlite3
|
16
|
+
queue:
|
17
|
+
<<: *default
|
18
|
+
database: storage/development_queue.sqlite3
|
19
|
+
migrations_paths: db/queue_migrate
|
20
|
+
|
21
|
+
|
22
|
+
# Warning: The database defined as "test" will be erased and
|
23
|
+
# re-generated from your development database when you run "rake".
|
24
|
+
# Do not set this db to the same as development or production.
|
25
|
+
test:
|
26
|
+
primary:
|
27
|
+
<<: *default
|
28
|
+
database: storage/test.sqlite3
|
29
|
+
queue:
|
30
|
+
<<: *default
|
31
|
+
database: storage/test_queue.sqlite3
|
32
|
+
migrations_paths: db/queue_migrate
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
# SQLite3 write its data on the local filesystem, as such it requires
|
37
|
+
# persistent disks. If you are deploying to a managed service, you should
|
38
|
+
# make sure it provides disk persistence, as many don't.
|
39
|
+
#
|
40
|
+
# Similarly, if you deploy your application as a Docker container, you must
|
41
|
+
# ensure the database is located in a persisted volume.
|
42
|
+
production:
|
43
|
+
primary:
|
44
|
+
<<: *default
|
45
|
+
database: /mnt/data/production.sqlite3
|
46
|
+
queue:
|
47
|
+
<<: *default
|
48
|
+
database: /mnt/data/production_queue.sqlite3
|
49
|
+
migrations_paths: db/queue_migrate
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "active_support/core_ext/integer/time"
|
5
|
+
|
6
|
+
Rails.application.configure do
|
7
|
+
# Settings specified here will take precedence over those in config/application.rb.
|
8
|
+
|
9
|
+
# In the development environment your application's code is reloaded any time
|
10
|
+
# it changes. This slows down response time but is perfect for development
|
11
|
+
# since you don't have to restart the web server when you make code changes.
|
12
|
+
config.enable_reloading = true
|
13
|
+
|
14
|
+
# Do not eager load code on boot.
|
15
|
+
config.eager_load = false
|
16
|
+
|
17
|
+
# Show full error reports.
|
18
|
+
config.consider_all_requests_local = true
|
19
|
+
|
20
|
+
# Enable server timing.
|
21
|
+
config.server_timing = true
|
22
|
+
|
23
|
+
# Enable/disable caching. By default caching is disabled.
|
24
|
+
# Run rails dev:cache to toggle caching.
|
25
|
+
if Rails.root.join("tmp/caching-dev.txt").exist?
|
26
|
+
config.action_controller.perform_caching = true
|
27
|
+
config.action_controller.enable_fragment_cache_logging = true
|
28
|
+
|
29
|
+
config.cache_store = :memory_store
|
30
|
+
config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{2.days.to_i}" }
|
31
|
+
else
|
32
|
+
config.action_controller.perform_caching = false
|
33
|
+
|
34
|
+
config.cache_store = :null_store
|
35
|
+
end
|
36
|
+
|
37
|
+
if defined?(ActionMailer)
|
38
|
+
# Don't care if the mailer can't send.
|
39
|
+
config.action_mailer.raise_delivery_errors = false
|
40
|
+
|
41
|
+
# Disable caching for Action Mailer templates even if Action Controller
|
42
|
+
# caching is enabled.
|
43
|
+
config.action_mailer.perform_caching = false
|
44
|
+
|
45
|
+
config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
|
46
|
+
end
|
47
|
+
|
48
|
+
# Print deprecation notices to the Rails logger.
|
49
|
+
config.active_support.deprecation = :log
|
50
|
+
|
51
|
+
# Raise exceptions for disallowed deprecations.
|
52
|
+
config.active_support.disallowed_deprecation = :raise
|
53
|
+
|
54
|
+
# Tell Active Support which deprecation messages to disallow.
|
55
|
+
config.active_support.disallowed_deprecation_warnings = []
|
56
|
+
|
57
|
+
# Highlight code that enqueued background job in logs.
|
58
|
+
config.active_job.verbose_enqueue_logs = true
|
59
|
+
|
60
|
+
# Raises error for missing translations.
|
61
|
+
# config.i18n.raise_on_missing_translations = true
|
62
|
+
|
63
|
+
# Annotate rendered view with file names.
|
64
|
+
config.action_view.annotate_rendered_view_with_filenames = true
|
65
|
+
|
66
|
+
# Raise error when a before_action's only/except options reference missing actions.
|
67
|
+
config.action_controller.raise_on_missing_callback_actions = true
|
68
|
+
|
69
|
+
config.active_job.queue_adapter = :solid_queue
|
70
|
+
config.solid_queue.silence_polling = true
|
71
|
+
config.solid_queue.connects_to = { database: { writing: :queue } }
|
72
|
+
|
73
|
+
# Apply autocorrection by RuboCop to files generated by `bin/rails generate`.
|
74
|
+
# config.generators.apply_rubocop_autocorrect_after_generate!
|
75
|
+
|
76
|
+
# establish a DEBUG environment
|
77
|
+
if ENV.fetch("DEBUG", false) && defined?(Rails::Server)
|
78
|
+
require "debug/open_nonstop"
|
79
|
+
end
|
80
|
+
|
81
|
+
# Load dotenv only in development environment
|
82
|
+
Dotenv::Rails.overwrite = true
|
83
|
+
|
84
|
+
# Let dev server run on GitHub Codespaces
|
85
|
+
config.hosts << /[a-z0-9\-]+\.githubpreview\.dev/
|
86
|
+
|
87
|
+
# Let dev server run on ngrok domains
|
88
|
+
config.hosts << /[a-z0-9\-]+\.ngrok\.io/
|
89
|
+
end
|