hephaestus 0.3.1 → 0.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2 -0
- data/lib/hephaestus/app_builder.rb +5 -2
- data/lib/hephaestus/generators/app_generator.rb +28 -28
- data/lib/hephaestus/generators/config_generator.rb +2 -3
- data/lib/hephaestus/version.rb +1 -1
- data/templates/.github/workflows/licenses.yml +1 -1
- data/templates/.github/workflows/lint.yml +1 -1
- data/templates/.github/workflows/security.yml +1 -1
- data/templates/.github/workflows/sorbet.yml +1 -1
- data/templates/Gemfile.erb +3 -2
- data/templates/app/controllers/app_controller.rb +3 -3
- data/templates/app/controllers/concerns/authable.rb +2 -2
- data/templates/app/controllers/yetto_controller.rb +11 -0
- data/templates/app/jobs/update_yetto_job.rb +6 -4
- data/templates/app/lib/body_parameter/yetto_parameters.rb +93 -9
- data/templates/app/lib/headers/yetto.rb +3 -1
- data/templates/app/lib/path_parameter/yetto_parameters.rb +6 -3
- data/templates/app/lib/plug_app/http.rb +3 -0
- data/templates/app/lib/plug_app/switches/message_created.jsonc +25 -0
- data/templates/app/services/yetto_service.rb +19 -14
- data/templates/compose.yml +1 -1
- data/templates/config/initializers/000-oj.rb +6 -0
- data/templates/config/initializers/environment.rb +5 -1
- data/templates/lib/plug_app/schemas/api/2023-03-06/components/parameters/headers/yetto.json +1 -1
- data/templates/lib/plug_app/schemas/api/2023-03-06/components/schemas/yetto.json +1 -1
- data/templates/lib/plug_app/schemas/api/2023-03-06/openapi.json +4 -4
- data/templates/lib/plug_app/schemas/api/2023-03-06/paths/yetto/{after_create_message.json → message_created.json} +1 -1
- data/templates/lib/plug_app/schemas/api/2023-03-06/paths/yetto/{after_create_plug_installation.json → plug_installation_created.json} +1 -1
- data/templates/test/controllers/yetto_controller_test.rb +7 -7
- data/templates/test/fixtures/files/plug_installation_settings/invalid.json +3 -0
- data/templates/test/fixtures/files/plug_installation_settings/valid.json +3 -0
- data/templates/test/fixtures/plug_installation_settings/invalid.json +3 -0
- data/templates/test/fixtures/plug_installation_settings/valid.json +3 -0
- data/templates/test/support/api.rb +6 -4
- data/templates/test/support/webmocks/yetto_webmock.rb +36 -17
- data/templates/vendor/fly/fly-production.toml +24 -21
- data/templates/vendor/fly/fly-staging.toml +24 -21
- metadata +11 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 514b3a3d6af4cd4f265334e540510abeba70de3758c4c92e6dd3a6297c6e428b
|
4
|
+
data.tar.gz: d852903702132b3add71f638494809ad6bc167bbbb3f83ec625bd21999cbd081
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35438e55bf05ced5fb9e90002f01ada1ee1b3ae9d8af941bf910a169c9d0ac45ace87f0ec8c086640b2c5c50d68bee7db8569c33bc92fc56cac5d83abe05fd77
|
7
|
+
data.tar.gz: 0b8d7b5cd5b5aa26328c704e44ed0385ba0865719d5e48440a50484475b7e0dc0644939ad94944111d9e1cdcbf3ba7ac560672675772ea8a0e6b6265c1206eb1
|
data/CHANGELOG.md
CHANGED
@@ -158,11 +158,14 @@ module Hephaestus
|
|
158
158
|
def replace_generic_variables
|
159
159
|
say(set_color("Replacing generic variable names...", :cyan))
|
160
160
|
replace_in_files(destination_root, /App(?!lication)/, short_app_name.capitalize)
|
161
|
-
replace_in_files(destination_root, %r{(?<!yetto)app(?!lication|/|roximate)}, short_app_name.downcase)
|
161
|
+
replace_in_files(destination_root, %r{(?<!yetto|plugs\.yetto\.)app(?!lication|/|roximate)}, short_app_name.downcase)
|
162
162
|
replace_in_files(destination_root, "PlugApp", app_name.underscore.camelcase)
|
163
163
|
replace_in_files(destination_root, "plug-app", app_name.dasherize)
|
164
|
+
replace_in_files("destination_root}/.github/workflows/test.yml", "plug-app", app_name.dasherize)
|
164
165
|
replace_in_files(destination_root, "PLUG_APP", app_name.underscore.upcase)
|
165
|
-
|
166
|
+
# these usually take the form of "plug_app", as a hack to prevent them from changing by the other
|
167
|
+
# rename methods...I think. I actually can't say for sure I remember what it's for.
|
168
|
+
replace_in_files(destination_root, "plug_", "")
|
166
169
|
|
167
170
|
# this is erroneously changed
|
168
171
|
replace_in_file("script/server", "/usr/src/#{short_app_name.downcase}", "/usr/src/app")
|
@@ -28,37 +28,10 @@ module Hephaestus
|
|
28
28
|
super
|
29
29
|
end
|
30
30
|
|
31
|
-
# NOTE: this function name is important as it overrides
|
32
|
-
# a Rails function of the same name (that function performs work
|
33
|
-
# that we don't want to do)
|
34
|
-
def leftovers
|
35
|
-
build(:replace_generic_variables)
|
36
|
-
|
37
|
-
say(set_color("Generating `hephaestus:license`...", :cyan))
|
38
|
-
capture_stdout do
|
39
|
-
generate("hephaestus:license")
|
40
|
-
end
|
41
|
-
say(set_color("Generating `hephaestus:rubocop`...", :cyan))
|
42
|
-
capture_stdout do
|
43
|
-
generate("hephaestus:rubocop")
|
44
|
-
end
|
45
|
-
say(set_color("Generating `hephaestus:sorbet`...", :cyan))
|
46
|
-
capture_stdout do
|
47
|
-
generate("hephaestus:sorbet")
|
48
|
-
end
|
49
|
-
say(set_color("Creating first commit...", :cyan))
|
50
|
-
capture_stdout do
|
51
|
-
invoke(:commit)
|
52
|
-
end
|
53
|
-
|
54
|
-
build(:restore_gemfile)
|
55
|
-
invoke(:outro)
|
56
|
-
end
|
57
|
-
|
58
31
|
def hephaestus_customization
|
59
32
|
say(set_color("Invoking Hephaestus customizations...", :cyan))
|
60
33
|
|
61
|
-
run("rails app:update:bin"
|
34
|
+
run("rails app:update:bin")
|
62
35
|
invoke(:customize_gemfile)
|
63
36
|
|
64
37
|
invoke(:copy_github_actions)
|
@@ -171,6 +144,33 @@ module Hephaestus
|
|
171
144
|
end
|
172
145
|
end
|
173
146
|
|
147
|
+
# NOTE: this function name is important as it overrides
|
148
|
+
# a Rails function of the same name (that function performs work
|
149
|
+
# that we don't want to do)
|
150
|
+
def leftovers
|
151
|
+
build(:replace_generic_variables)
|
152
|
+
|
153
|
+
say(set_color("Generating `hephaestus:license`...", :cyan))
|
154
|
+
capture_stdout do
|
155
|
+
generate("hephaestus:license")
|
156
|
+
end
|
157
|
+
say(set_color("Generating `hephaestus:rubocop`...", :cyan))
|
158
|
+
capture_stdout do
|
159
|
+
generate("hephaestus:rubocop")
|
160
|
+
end
|
161
|
+
say(set_color("Generating `hephaestus:sorbet`...", :cyan))
|
162
|
+
capture_stdout do
|
163
|
+
generate("hephaestus:sorbet")
|
164
|
+
end
|
165
|
+
say(set_color("Creating first commit...", :cyan))
|
166
|
+
capture_stdout do
|
167
|
+
invoke(:commit)
|
168
|
+
end
|
169
|
+
|
170
|
+
build(:restore_gemfile)
|
171
|
+
invoke(:outro)
|
172
|
+
end
|
173
|
+
|
174
174
|
def commit
|
175
175
|
run("git add .")
|
176
176
|
run("git commit -m 'Initial commit'", capture: true)
|
@@ -89,11 +89,10 @@ module Hephaestus
|
|
89
89
|
|
90
90
|
# events into the plug, usually from yetto
|
91
91
|
get "/api/2023-03-06/settings", to: "settings#new"
|
92
|
-
post "/api/2023-03-06/:event
|
92
|
+
post "/api/2023-03-06/:record_type/:event", to: "yetto#event"
|
93
93
|
|
94
94
|
# inbound message
|
95
|
-
post "
|
96
|
-
# post "/app/2023-03-06/webhook/inbound", to: "app#process_inbound" # for generic inbound pings
|
95
|
+
post "/$app/2023-03-06/webhook/inbound", to: "app#webhook"
|
97
96
|
|
98
97
|
# Staff pages
|
99
98
|
get "staff", to: "staff#index"
|
data/lib/hephaestus/version.rb
CHANGED
data/templates/Gemfile.erb
CHANGED
@@ -44,6 +44,9 @@ gem "opentelemetry-instrumentation-active_job", "~> 0.5"
|
|
44
44
|
gem "opentelemetry-instrumentation-redis", "~> 0.25"
|
45
45
|
gem "opentelemetry-instrumentation-sidekiq", "~> 0.23"
|
46
46
|
|
47
|
+
# massively improved JSON parsing
|
48
|
+
gem "oj", "~> 3.16"
|
49
|
+
|
47
50
|
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
|
48
51
|
gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby]
|
49
52
|
|
@@ -113,5 +116,3 @@ group :ci do
|
|
113
116
|
gem "brakeman", "~> 5.3"
|
114
117
|
gem "bundle-audit", "~> 0.1"
|
115
118
|
end
|
116
|
-
|
117
|
-
gem "hephaestus", group: [:development, :test]
|
@@ -8,12 +8,12 @@ class AppController < ApplicationController
|
|
8
8
|
include PathParameter::AppParameters
|
9
9
|
include BodyParameter::AppParameters
|
10
10
|
|
11
|
-
before_action :
|
11
|
+
before_action :from_app?
|
12
12
|
|
13
13
|
# Inbound message from ${App}
|
14
14
|
def webhook
|
15
|
-
# Error if
|
16
|
-
return bad_request unless
|
15
|
+
# Error if necessary parameters from ${App} are missing
|
16
|
+
return bad_request unless has_inbound_app_params?
|
17
17
|
|
18
18
|
response = YettoService.get_plug_installation(pparam_organization_id, pparam_inbox_id, pparam_plug_installation_id)
|
19
19
|
|
@@ -26,8 +26,8 @@ module Authable
|
|
26
26
|
|
27
27
|
# status is annoyingly set to 401, but we want
|
28
28
|
# to hide that an issue exists
|
29
|
-
|
30
|
-
|
29
|
+
self.status = PlugApp::HTTP::BAD_REQUEST_I
|
30
|
+
self.response_body = ::ErrorSerializer.format(PlugApp::HTTP::BAD_REQUEST)
|
31
31
|
end
|
32
32
|
|
33
33
|
sig { void }
|
@@ -3,6 +3,7 @@
|
|
3
3
|
|
4
4
|
class YettoController < ApplicationController
|
5
5
|
include BodyParameter::YettoParameters
|
6
|
+
include PathParameter
|
6
7
|
include PathParameter::YettoParameters
|
7
8
|
include Authable
|
8
9
|
|
@@ -10,11 +11,21 @@ class YettoController < ApplicationController
|
|
10
11
|
|
11
12
|
before_action :from_yetto?
|
12
13
|
|
14
|
+
AFTER_CREATE_MESSAGE_SWITCH = JSON.parse(Rails.root.join("app/lib/plug_app/switches/message_created.jsonc").read)
|
15
|
+
|
13
16
|
def event
|
14
17
|
case pparam_yetto_event
|
15
18
|
when Headers::Yetto::EVENT_AFTER_CREATE
|
16
19
|
case pparam_yetto_record_type
|
17
20
|
when Headers::Yetto::RECORD_TYPE_PLUG_INSTALLATION
|
21
|
+
create_inbox_switch_data = {
|
22
|
+
type: "create_inbox_switch",
|
23
|
+
inbox: { id: bparam_inbox_id },
|
24
|
+
plug_installation: { id: bparam_plug_installation_id },
|
25
|
+
payload: AFTER_CREATE_MESSAGE_SWITCH,
|
26
|
+
}
|
27
|
+
|
28
|
+
UpdateYettoJob.perform_later(create_inbox_switch_data)
|
18
29
|
|
19
30
|
no_content
|
20
31
|
when Headers::Yetto::RECORD_TYPE_MESSAGE
|
@@ -5,22 +5,24 @@
|
|
5
5
|
# This can be used to update installation data or message data
|
6
6
|
|
7
7
|
class UpdateYettoJob < ApplicationJob
|
8
|
-
queue_as :
|
8
|
+
queue_as :update_yetto
|
9
9
|
|
10
10
|
def perform(params)
|
11
11
|
type = params.delete(:type)
|
12
12
|
|
13
|
-
organization_id = params.fetch(:organization, {}).fetch(:id, nil)
|
14
13
|
inbox_id = params.fetch(:inbox, {}).fetch(:id, nil)
|
15
14
|
plug_installation_id = params.fetch(:plug_installation, {}).fetch(:id, nil)
|
15
|
+
message_id = params.fetch(:message, {}).fetch(:id, nil)
|
16
16
|
|
17
17
|
case type
|
18
18
|
when "update_plug_installation"
|
19
19
|
YettoService.update_installation(plug_installation_id, params)
|
20
20
|
when "create_inbox_switch"
|
21
21
|
YettoService.create_inbox_switch(inbox_id, plug_installation_id, params)
|
22
|
-
when "
|
23
|
-
YettoService.
|
22
|
+
when "create_message_reply"
|
23
|
+
YettoService.create_message_reply(message_id, plug_installation_id, params)
|
24
|
+
when "add_message_metadata"
|
25
|
+
YettoService.update_message(message_id, plug_installation_id, params)
|
24
26
|
end
|
25
27
|
end
|
26
28
|
end
|
@@ -5,25 +5,47 @@ module BodyParameter
|
|
5
5
|
module YettoParameters
|
6
6
|
extend T::Sig
|
7
7
|
|
8
|
-
sig { returns(
|
9
|
-
def
|
10
|
-
|
8
|
+
sig { returns(String) }
|
9
|
+
def bparam_organization_id
|
10
|
+
plug_installation_params[:organization][:id]
|
11
|
+
end
|
12
|
+
|
13
|
+
sig { returns(String) }
|
14
|
+
def bparam_inbox_id
|
15
|
+
plug_installation_params[:inbox][:id]
|
16
|
+
end
|
17
|
+
|
18
|
+
sig { returns(String) }
|
19
|
+
def bparam_plug_installation_id
|
20
|
+
plug_installation_params[:plug_installation][:id]
|
21
|
+
end
|
22
|
+
|
23
|
+
sig { returns(String) }
|
24
|
+
def bparam_plug_id
|
25
|
+
plug_installation_params[:plug][:id]
|
26
|
+
end
|
11
27
|
|
12
|
-
|
28
|
+
sig { returns(T::Hash[Symbol, String]) }
|
29
|
+
def bparam_plug_installation_settings
|
30
|
+
plug_installation_params[:plug_installation][:settings]
|
13
31
|
end
|
14
32
|
|
15
33
|
sig { returns(T::Hash[Symbol, String]) }
|
16
|
-
def
|
34
|
+
def plug_installation_params
|
17
35
|
return {} if params.blank?
|
18
36
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
37
|
+
plug_installation = params.fetch(:plug_installation, {})
|
38
|
+
plug_installation_id = plug_installation.fetch(:id, "")
|
39
|
+
settings = plug_installation.fetch(:settings, {}).to_unsafe_hash
|
40
|
+
|
41
|
+
organization_id = params.fetch(:organization, {}).fetch(:id, "")
|
42
|
+
plug_id = params.fetch(:plug, {}).fetch(:id, "")
|
43
|
+
inbox_id = params.fetch(:inbox, {}).fetch(:id, "")
|
23
44
|
|
24
45
|
{
|
25
46
|
plug_installation: {
|
26
47
|
id: plug_installation_id,
|
48
|
+
settings: settings,
|
27
49
|
},
|
28
50
|
organization: {
|
29
51
|
id: organization_id,
|
@@ -36,5 +58,67 @@ module BodyParameter
|
|
36
58
|
},
|
37
59
|
}
|
38
60
|
end
|
61
|
+
|
62
|
+
sig { returns(String) }
|
63
|
+
def bparam_message_id
|
64
|
+
message_params[:message][:id]
|
65
|
+
end
|
66
|
+
|
67
|
+
sig { returns(String) }
|
68
|
+
def bparam_message_text_content
|
69
|
+
message_params[:message][:text_content]
|
70
|
+
end
|
71
|
+
|
72
|
+
sig { returns(String) }
|
73
|
+
def bparam_message_conversation_id
|
74
|
+
message_params[:message][:conversation][:id]
|
75
|
+
end
|
76
|
+
|
77
|
+
sig { returns(String) }
|
78
|
+
def bparam_message_conversation_title
|
79
|
+
message_params[:message][:conversation][:title]
|
80
|
+
end
|
81
|
+
|
82
|
+
sig { returns(String) }
|
83
|
+
def bparam_message_author_name
|
84
|
+
message_params[:message].fetch(:author, {}).fetch(:name, "Noone")
|
85
|
+
end
|
86
|
+
|
87
|
+
sig { returns(T::Hash[Symbol, String]) }
|
88
|
+
def bparam_message_metadata
|
89
|
+
message_params[:message][:metadata]
|
90
|
+
end
|
91
|
+
|
92
|
+
sig { returns(T::Hash[Symbol, String]) }
|
93
|
+
def message_params
|
94
|
+
return {} if params.blank?
|
95
|
+
|
96
|
+
message = params.fetch(:message, {})
|
97
|
+
message_id = message.fetch(:id, "")
|
98
|
+
text_content = message.fetch(:text_content, "")
|
99
|
+
conversation = message.fetch(:conversation, {})
|
100
|
+
conversation_id = conversation.fetch(:id, "")
|
101
|
+
title = conversation.fetch(:title, "")
|
102
|
+
created_by_user = message.fetch(:created_by_user, {})
|
103
|
+
created_by_plug = message.fetch(:created_by_plug, {})
|
104
|
+
author = (created_by_user.presence || created_by_plug)
|
105
|
+
name = author.fetch(:name, "")
|
106
|
+
metadata = message.fetch(:metadata, {}).to_unsafe_hash
|
107
|
+
|
108
|
+
{
|
109
|
+
message: {
|
110
|
+
id: message_id,
|
111
|
+
text_content: text_content,
|
112
|
+
conversation: {
|
113
|
+
id: conversation_id,
|
114
|
+
title: title,
|
115
|
+
},
|
116
|
+
author: {
|
117
|
+
name: name,
|
118
|
+
},
|
119
|
+
metadata: metadata,
|
120
|
+
},
|
121
|
+
}
|
122
|
+
end
|
39
123
|
end
|
40
124
|
end
|
@@ -6,7 +6,9 @@ module Headers
|
|
6
6
|
YETTO_DELIVERY_ID = "HTTP_X_YETTO_DELIVERY_ID"
|
7
7
|
|
8
8
|
HEADER_EVENT = "HTTP_X_YETTO_EVENT"
|
9
|
-
EVENT_AFTER_CREATE = "
|
9
|
+
EVENT_AFTER_CREATE = "created"
|
10
|
+
EVENT_AFTER_UPDATE = "updated"
|
11
|
+
EVENT_AFTER_DESTROY = "destroyed"
|
10
12
|
|
11
13
|
HEADER_RECORD_TYPE = "HTTP_X_YETTO_RECORD_TYPE"
|
12
14
|
RECORD_TYPE_PLUG_INSTALLATION = "plug_installation"
|
@@ -15,11 +15,14 @@ module PathParameter
|
|
15
15
|
yetto_path_params.fetch(:record_type, "")
|
16
16
|
end
|
17
17
|
|
18
|
-
sig { returns(
|
18
|
+
sig { returns(T::Hash[String, String]) }
|
19
19
|
def yetto_path_params
|
20
|
-
return
|
20
|
+
return {} if path_parameters.blank?
|
21
21
|
|
22
|
-
|
22
|
+
{
|
23
|
+
event: path_parameters.fetch(:event, ""),
|
24
|
+
record_type: path_parameters.fetch(:record_type, ""),
|
25
|
+
}
|
23
26
|
end
|
24
27
|
end
|
25
28
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
{
|
2
|
+
"name": "Send internal comments to App",
|
3
|
+
"configuration": {
|
4
|
+
"version": "2023-03-06",
|
5
|
+
"events": {
|
6
|
+
"message.created": {
|
7
|
+
"conditions": {
|
8
|
+
"if": "{% data.message.visibility == 'internal' and data.message.text_content contains '@App' %}"
|
9
|
+
},
|
10
|
+
"actions": [
|
11
|
+
{
|
12
|
+
"name": "Send to App",
|
13
|
+
"uses": "send_to_plug",
|
14
|
+
"with": {
|
15
|
+
"plug_id": "{{ data.switch.created_by_plug.id }}",
|
16
|
+
"message": "{{ data.message }}",
|
17
|
+
"plug_installation": "{{ data.plug_installation }}",
|
18
|
+
"inbox": "{{ data.inbox }}"
|
19
|
+
}
|
20
|
+
}
|
21
|
+
]
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}
|
25
|
+
}
|
@@ -6,48 +6,53 @@ class YettoService
|
|
6
6
|
Httpsensible::Client.new(user_agent: "PlugApp/#{PlugApp::Application::GIT_SHA}")
|
7
7
|
end
|
8
8
|
|
9
|
-
|
9
|
+
# explicitly different than what's in environment.rb, because local
|
10
|
+
# Yetto expects HTTP; environment.rb can use HTTPS because it passes through ngrok.io
|
11
|
+
PROTOCOL = Rails.env.development? ? "http://" : "https://"
|
10
12
|
YETTO_API_VERSION_TLD = "#{PROTOCOL}://#{YETTO_API_TLD}/#{YETTO_API_VERSION}"
|
11
13
|
JWT_ALGORITHM = "RS256"
|
12
14
|
|
13
15
|
class << self
|
14
16
|
def perform_token_exchange(plug_installation_id)
|
15
17
|
encoded_jwt = Httpsensible::JWT.encode_jwt(YETTO_PLUG_PEM, YETTO_PLUG_ID)
|
16
|
-
response = yetto_client.
|
18
|
+
response = yetto_client.with_headers({ "Authorization" => "Bearer #{encoded_jwt}" }).post("#{YETTO_API_VERSION_TLD}/plug/installations/#{plug_installation_id}/access_tokens")
|
17
19
|
body = response.parsed_json_body
|
18
20
|
body["token"]
|
19
21
|
end
|
20
22
|
|
21
23
|
def get_plug_installation(plug_installation_id)
|
22
24
|
token = perform_token_exchange(plug_installation_id)
|
23
|
-
yetto_client.
|
25
|
+
yetto_client.with_headers("Authorization" => "Bearer #{token}").get("#{YETTO_API_VERSION_TLD}/installations/#{plug_installation_id}")
|
24
26
|
end
|
25
27
|
|
26
28
|
def update_installation(plug_installation_id, params)
|
27
29
|
plug_installation = {}
|
28
|
-
plug_installation[:settings] = params
|
29
|
-
plug_installation[:credentials] = params
|
30
|
+
plug_installation[:settings] = params.fetch(:settings, {})
|
31
|
+
plug_installation[:credentials] = params.fetch(:credentials, {})
|
30
32
|
|
31
33
|
token = perform_token_exchange(plug_installation_id)
|
32
|
-
yetto_client.
|
34
|
+
yetto_client.with_headers("Authorization" => "Bearer #{token}").patch("#{YETTO_API_VERSION_TLD}/installations/#{plug_installation_id}", json: plug_installation)
|
33
35
|
end
|
34
36
|
|
35
37
|
def create_inbox_switch(inbox_id, plug_installation_id, params)
|
36
|
-
|
38
|
+
payload = params[:payload]
|
39
|
+
token = perform_token_exchange(plug_installation_id)
|
37
40
|
|
38
|
-
|
39
|
-
|
40
|
-
}
|
41
|
+
yetto_client.with_headers("Authorization" => "Bearer #{token}").post("#{YETTO_API_VERSION_TLD}/inboxes/#{inbox_id}/switches", json: payload)
|
42
|
+
end
|
41
43
|
|
44
|
+
def update_message(message_id, plug_installation_id, params)
|
45
|
+
payload = params[:payload]
|
42
46
|
token = perform_token_exchange(plug_installation_id)
|
43
|
-
|
47
|
+
|
48
|
+
yetto_client.with_headers("Authorization" => "Bearer #{token}").patch("#{YETTO_API_VERSION_TLD}/messages/#{message_id}", json: payload)
|
44
49
|
end
|
45
50
|
|
46
|
-
def
|
51
|
+
def create_message_reply(message_id, plug_installation_id, params)
|
47
52
|
payload = params[:payload]
|
48
|
-
|
49
53
|
token = perform_token_exchange(plug_installation_id)
|
50
|
-
|
54
|
+
|
55
|
+
yetto_client.with_headers("Authorization" => "Bearer #{token}").post("#{YETTO_API_VERSION_TLD}/messages/#{message_id}/replies", json: payload)
|
51
56
|
end
|
52
57
|
end
|
53
58
|
end
|
data/templates/compose.yml
CHANGED
@@ -16,7 +16,7 @@ YETTO_API_VERSION = ENV.fetch("YETTO_API_VERSION", "2023-03-06")
|
|
16
16
|
|
17
17
|
SLACK_LOG_URL = Rails.application.credentials.fetch(:SLACK_LOG_URL, ENV.fetch("SLACK_LOG_URL", "https://slack.com/the_log_room"))
|
18
18
|
|
19
|
-
PROTOCOL =
|
19
|
+
PROTOCOL = "https"
|
20
20
|
PLUG_APP_URL = if Rails.env.production?
|
21
21
|
"app.plugs.yetto.app"
|
22
22
|
elsif Rails.env.staging?
|
@@ -34,3 +34,7 @@ YETTO_PLUG_ID = Rails.application.credentials.fetch(:YETTO_PLUG_ID, ENV.fetch("Y
|
|
34
34
|
def productionish?
|
35
35
|
Rails.env.production? || Rails.env.staging?
|
36
36
|
end
|
37
|
+
|
38
|
+
def print_user_api_errors?
|
39
|
+
(Rails.env.development? || Rails.env.staging?) || ENV.fetch("DEBUG", false)
|
40
|
+
end
|
@@ -17,11 +17,11 @@
|
|
17
17
|
}
|
18
18
|
],
|
19
19
|
"paths": {
|
20
|
-
"/api/2023-03-06/
|
21
|
-
"$ref": "paths/yetto/
|
20
|
+
"/api/2023-03-06/plug_installation/created": {
|
21
|
+
"$ref": "paths/yetto/plug_installation_created.json"
|
22
22
|
},
|
23
|
-
"/api/2023-03-06/
|
24
|
-
"$ref": "paths/yetto/
|
23
|
+
"/api/2023-03-06/message/created": {
|
24
|
+
"$ref": "paths/yetto/message_created.json"
|
25
25
|
},
|
26
26
|
"/app/2023-03-06/{plugInstallationId}": {
|
27
27
|
"$ref": "paths/app.json"
|
@@ -60,37 +60,37 @@ class YettoControllerTest < ActionDispatch::IntegrationTest
|
|
60
60
|
|
61
61
|
def headers(body)
|
62
62
|
{
|
63
|
-
Headers::Yetto::HEADER_EVENT => "
|
63
|
+
Headers::Yetto::HEADER_EVENT => "created",
|
64
64
|
Headers::Yetto::HEADER_RECORD_TYPE => "plug_installation",
|
65
65
|
Headers::Yetto::HEADER_SIGNATURE => yetto_auth_header(body),
|
66
66
|
}
|
67
67
|
end
|
68
68
|
|
69
69
|
test "it handles null headers" do
|
70
|
-
api(:post, "/
|
70
|
+
api(:post, "/plug_installation/created", headers: nil)
|
71
71
|
|
72
72
|
assert_response :bad_request
|
73
73
|
end
|
74
74
|
|
75
75
|
test "it handles missing headers" do
|
76
|
-
api(:post, "/
|
76
|
+
api(:post, "/plug_installation/created", headers: {})
|
77
77
|
|
78
78
|
assert_response :bad_request
|
79
79
|
end
|
80
80
|
|
81
81
|
test "it handles incorrect headers" do
|
82
|
-
api(:post, "/
|
82
|
+
api(:post, "/plug_installation/created", headers: { "X-Yetto-Signature" => "Basic jabroni:lies" })
|
83
83
|
|
84
84
|
assert_response :bad_request
|
85
85
|
|
86
|
-
api(:post, "/
|
86
|
+
api(:post, "/plug_installation/created", headers: { "X-Yetto-Signature" => "sha256=123456" })
|
87
87
|
|
88
88
|
assert_response :bad_request
|
89
89
|
end
|
90
90
|
|
91
91
|
test "it handles missing body" do
|
92
92
|
body = {}
|
93
|
-
api(:post, "/
|
93
|
+
api(:post, "/plug_installation/created", headers: headers(body), body: body)
|
94
94
|
|
95
95
|
assert_response :bad_request
|
96
96
|
end
|
@@ -122,7 +122,7 @@ class YettoControllerTest < ActionDispatch::IntegrationTest
|
|
122
122
|
id: "inbx_#{Faker::Alphanumeric.alphanumeric(number: 26).upcase}",
|
123
123
|
},
|
124
124
|
}
|
125
|
-
api(:post, "/
|
125
|
+
api(:post, "/plug_installation/created", headers: headers_with_one_body, body: body_two)
|
126
126
|
|
127
127
|
assert_response :bad_request
|
128
128
|
end
|
@@ -5,11 +5,11 @@ module API
|
|
5
5
|
module TestHelpers
|
6
6
|
include Rack::Test::Methods
|
7
7
|
|
8
|
-
def plug(method, path, headers: {}, version: nil, body: {})
|
8
|
+
def plug(method, path, headers: {}, version: nil, body: {}, parse: true)
|
9
9
|
version ||= PlugApp::CURRENT_VERSION
|
10
10
|
prepended_path = prepend_plug_path(version, path)
|
11
11
|
|
12
|
-
http_call(method, prepended_path, headers: headers, version: version, body: body)
|
12
|
+
http_call(method, prepended_path, headers: headers, version: version, body: body, parse: parse)
|
13
13
|
end
|
14
14
|
|
15
15
|
def api(method, path, headers: {}, version: nil, body: {})
|
@@ -19,7 +19,7 @@ module API
|
|
19
19
|
http_call(method, prepended_path, headers: headers, version: version, body: body)
|
20
20
|
end
|
21
21
|
|
22
|
-
def http_call(method, path, headers: {}, version: nil, body: {})
|
22
|
+
def http_call(method, path, headers: {}, version: nil, body: {}, parse: true)
|
23
23
|
# explicitly assert headers cannot be nil
|
24
24
|
if headers.nil?
|
25
25
|
send(method, path, body.to_json)
|
@@ -29,7 +29,7 @@ module API
|
|
29
29
|
send(method, path, body.to_json, headers)
|
30
30
|
end
|
31
31
|
|
32
|
-
JSON.parse(last_response.body) if last_response.body.present?
|
32
|
+
JSON.parse(last_response.body) if last_response.body.present? && parse
|
33
33
|
end
|
34
34
|
|
35
35
|
def assert_response(expected_status, expected_body = nil)
|
@@ -42,6 +42,8 @@ module API
|
|
42
42
|
PlugApp::HTTP::OK_I
|
43
43
|
when :created
|
44
44
|
PlugApp::HTTP::CREATED_I
|
45
|
+
when :redirect
|
46
|
+
PlugApp::HTTP::FOUND_I
|
45
47
|
when :no_content
|
46
48
|
PlugApp::HTTP::NO_CONTENT_I
|
47
49
|
when :bad_request
|
@@ -8,28 +8,30 @@ module Webmocks
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def assert_requested_post_access_token(plug_installation_id)
|
11
|
-
assert_requested(:post, "#{::YettoService::YETTO_API_VERSION_TLD}/
|
11
|
+
assert_requested(:post, "#{::YettoService::YETTO_API_VERSION_TLD}/plug/installations/#{plug_installation_id}/access_tokens")
|
12
12
|
end
|
13
13
|
|
14
14
|
def stub_post_access_token(plug_installation_id)
|
15
15
|
response = {
|
16
16
|
token: Faker::Alphanumeric.alphanumeric(number: 26).upcase,
|
17
17
|
}
|
18
|
-
stub_request(:post, "#{::YettoService::YETTO_API_VERSION_TLD}/
|
18
|
+
stub_request(:post, "#{::YettoService::YETTO_API_VERSION_TLD}/plug/installations/#{plug_installation_id}/access_tokens")
|
19
19
|
.to_return(
|
20
20
|
body: response.to_json,
|
21
21
|
)
|
22
22
|
end
|
23
23
|
|
24
|
-
def
|
24
|
+
def assert_requested_get_inbox_plug_installation(plug_installation_id)
|
25
25
|
assert_requested_post_access_token(plug_installation_id)
|
26
26
|
assert_requested(:get, "#{::YettoService::YETTO_API_VERSION_TLD}/installations/#{plug_installation_id}")
|
27
27
|
end
|
28
28
|
|
29
|
-
def
|
29
|
+
def stub_get_inbox_plug_installation(organization_id, inbox_id, plug_installation_id, response = {}, valid: true, status: 200, expires_at: 8.hours.from_now)
|
30
30
|
stub_post_access_token(plug_installation_id)
|
31
31
|
|
32
|
-
|
32
|
+
github_installations = JSON.parse(file_fixture_path("plug_installation_settings", valid ? "valid.json" : "invalid.json").read).deep_symbolize_keys
|
33
|
+
|
34
|
+
response = {
|
33
35
|
installed_on_inbox: {
|
34
36
|
id: inbox_id,
|
35
37
|
organization: {
|
@@ -40,10 +42,13 @@ module Webmocks
|
|
40
42
|
plug: {
|
41
43
|
id: "plg_#{Faker::Alphanumeric.alphanumeric(number: 26).upcase}",
|
42
44
|
},
|
43
|
-
settings:
|
44
|
-
|
45
|
+
settings: github_installations,
|
46
|
+
credentials: {
|
47
|
+
access_token: Faker::Alphanumeric.alphanumeric(number: 26).upcase,
|
48
|
+
refresh_access_token: "refresher",
|
49
|
+
expires_at: expires_at.iso8601,
|
45
50
|
},
|
46
|
-
}
|
51
|
+
}.merge(response)
|
47
52
|
|
48
53
|
stub_request(:get, "#{::YettoService::YETTO_API_VERSION_TLD}/installations/#{plug_installation_id}")
|
49
54
|
.to_return(
|
@@ -73,16 +78,34 @@ module Webmocks
|
|
73
78
|
)
|
74
79
|
end
|
75
80
|
|
76
|
-
def assert_requested_create_message(
|
81
|
+
def assert_requested_create_message(plug_installation_id, message_id)
|
77
82
|
assert_requested_post_access_token(plug_installation_id)
|
78
83
|
|
79
|
-
assert_requested(:post, "#{::YettoService::YETTO_API_VERSION_TLD}/
|
84
|
+
assert_requested(:post, "#{::YettoService::YETTO_API_VERSION_TLD}/messages/#{message_id}/replies")
|
80
85
|
end
|
81
86
|
|
82
|
-
def stub_create_message(
|
87
|
+
def stub_create_message(plug_installation_id, message_id, payload)
|
83
88
|
stub_post_access_token(plug_installation_id)
|
84
89
|
|
85
|
-
stub_request(:post, "#{::YettoService::YETTO_API_VERSION_TLD}/
|
90
|
+
stub_request(:post, "#{::YettoService::YETTO_API_VERSION_TLD}/messages/#{message_id}/replies")
|
91
|
+
# .with(
|
92
|
+
# body: payload,
|
93
|
+
# )
|
94
|
+
# .to_return(
|
95
|
+
# status: 200,
|
96
|
+
# headers: { content_type: "application/json; charset=utf-8" },
|
97
|
+
# body: {}.to_json,
|
98
|
+
# )
|
99
|
+
end
|
100
|
+
|
101
|
+
def assert_requested_update_message_metadata(plug_installation_id, message_id)
|
102
|
+
assert_requested_post_access_token(plug_installation_id)
|
103
|
+
assert_requested(:patch, "#{::YettoService::YETTO_API_VERSION_TLD}/messages/#{message_id}")
|
104
|
+
end
|
105
|
+
|
106
|
+
def stub_update_message_metadata(plug_installation_id, message_id, payload)
|
107
|
+
stub_post_access_token(plug_installation_id)
|
108
|
+
stub_request(:patch, "#{::YettoService::YETTO_API_VERSION_TLD}/messages/#{message_id}")
|
86
109
|
.with(
|
87
110
|
body: payload,
|
88
111
|
)
|
@@ -98,13 +121,9 @@ module Webmocks
|
|
98
121
|
assert_requested(:post, "#{::YettoService::YETTO_API_VERSION_TLD}/inboxes/#{inbox_id}/switches")
|
99
122
|
end
|
100
123
|
|
101
|
-
def stub_create_inbox_switch(inbox_id, plug_installation_id,
|
124
|
+
def stub_create_inbox_switch(inbox_id, plug_installation_id, payload)
|
102
125
|
stub_post_access_token(plug_installation_id)
|
103
126
|
|
104
|
-
payload = {
|
105
|
-
name: "After install",
|
106
|
-
}
|
107
|
-
|
108
127
|
stub_request(:post, "#{::YettoService::YETTO_API_VERSION_TLD}/inboxes/#{inbox_id}/switches")
|
109
128
|
.with(
|
110
129
|
body: payload,
|
@@ -7,32 +7,35 @@ app = "plug-app-production"
|
|
7
7
|
primary_region = "iad"
|
8
8
|
|
9
9
|
[env]
|
10
|
-
|
11
|
-
|
10
|
+
RAILS_ENV = "production"
|
11
|
+
LD_PRELOAD_PATH = "/usr/lib/x86_64-linux-gnu/libjemalloc.so.2"
|
12
12
|
|
13
13
|
[processes]
|
14
|
-
|
15
|
-
|
14
|
+
web = "./bin/rails server"
|
15
|
+
worker = "bundle exec sidekiq"
|
16
16
|
|
17
17
|
[http_service]
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
18
|
+
internal_port = 3000
|
19
|
+
force_https = true
|
20
|
+
auto_stop_machines = true
|
21
|
+
auto_start_machines = true
|
22
|
+
min_machines_running = 1
|
23
|
+
processes = ["web"]
|
24
|
+
[http_service.concurrency]
|
25
|
+
type = "requests"
|
26
|
+
soft_limit = 200
|
27
|
+
hard_limit = 250
|
28
28
|
|
29
29
|
[checks]
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
30
|
+
[checks.alive]
|
31
|
+
type = "tcp"
|
32
|
+
interval = "15s"
|
33
|
+
timeout = "2s"
|
34
|
+
grace_period = "5s"
|
35
35
|
|
36
36
|
[[statics]]
|
37
|
-
|
38
|
-
|
37
|
+
guest_path = "/plug-app/public"
|
38
|
+
url_prefix = "/"
|
39
|
+
|
40
|
+
[deploy]
|
41
|
+
strategy = "bluegreen"
|
@@ -2,32 +2,35 @@ app = "plug-app-staging"
|
|
2
2
|
primary_region = "iad"
|
3
3
|
|
4
4
|
[env]
|
5
|
-
|
6
|
-
|
5
|
+
RAILS_ENV = "plug-app-staging"
|
6
|
+
LD_PRELOAD_PATH = "/usr/lib/x86_64-linux-gnu/libjemalloc.so.2"
|
7
7
|
|
8
8
|
[processes]
|
9
|
-
|
10
|
-
|
9
|
+
web = "./bin/rails server"
|
10
|
+
worker = "bundle exec sidekiq"
|
11
11
|
|
12
12
|
[http_service]
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
13
|
+
internal_port = 3000
|
14
|
+
force_https = true
|
15
|
+
auto_stop_machines = true
|
16
|
+
auto_start_machines = true
|
17
|
+
min_machines_running = 0
|
18
|
+
processes = ["web"]
|
19
|
+
[http_service.concurrency]
|
20
|
+
type = "requests"
|
21
|
+
soft_limit = 200
|
22
|
+
hard_limit = 250
|
23
23
|
|
24
24
|
[checks]
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
[checks.alive]
|
26
|
+
type = "tcp"
|
27
|
+
interval = "15s"
|
28
|
+
timeout = "2s"
|
29
|
+
grace_period = "5s"
|
30
30
|
|
31
31
|
[[statics]]
|
32
|
-
|
33
|
-
|
32
|
+
guest_path = "/plug-app/public"
|
33
|
+
url_prefix = "/"
|
34
|
+
|
35
|
+
[deploy]
|
36
|
+
strategy = "immediate"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hephaestus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Garen Torikian
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-09-
|
11
|
+
date: 2023-09-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -171,6 +171,7 @@ files:
|
|
171
171
|
- templates/app/lib/plug_app/middleware/not_found.rb
|
172
172
|
- templates/app/lib/plug_app/middleware/openapi_validation.rb
|
173
173
|
- templates/app/lib/plug_app/middleware/tracing_attributes.rb
|
174
|
+
- templates/app/lib/plug_app/switches/message_created.jsonc
|
174
175
|
- templates/app/lib/query_parameter.rb
|
175
176
|
- templates/app/serializers/error_serializer.rb
|
176
177
|
- templates/app/services/http_service.rb
|
@@ -178,6 +179,7 @@ files:
|
|
178
179
|
- templates/app/views/settings/new.json.jbuilder
|
179
180
|
- templates/bin/docker-entrypoint
|
180
181
|
- templates/compose.yml
|
182
|
+
- templates/config/initializers/000-oj.rb
|
181
183
|
- templates/config/initializers/cors.rb
|
182
184
|
- templates/config/initializers/environment.rb
|
183
185
|
- templates/config/initializers/filter_parameter_logging.rb
|
@@ -197,8 +199,8 @@ files:
|
|
197
199
|
- templates/lib/plug_app/schemas/api/2023-03-06/components/schemas/yetto.json
|
198
200
|
- templates/lib/plug_app/schemas/api/2023-03-06/openapi.json
|
199
201
|
- templates/lib/plug_app/schemas/api/2023-03-06/paths/plug.json
|
200
|
-
- templates/lib/plug_app/schemas/api/2023-03-06/paths/yetto/
|
201
|
-
- templates/lib/plug_app/schemas/api/2023-03-06/paths/yetto/
|
202
|
+
- templates/lib/plug_app/schemas/api/2023-03-06/paths/yetto/message_created.json
|
203
|
+
- templates/lib/plug_app/schemas/api/2023-03-06/paths/yetto/plug_installation_created.json
|
202
204
|
- templates/lib/tasks/test_tasks.rake
|
203
205
|
- templates/script/ci
|
204
206
|
- templates/script/edit-credentials
|
@@ -214,6 +216,10 @@ files:
|
|
214
216
|
- templates/test/controllers/settings_controller_test.rb
|
215
217
|
- templates/test/controllers/yetto_controller_test.rb
|
216
218
|
- templates/test/fixtures/files/fake_pem_file/fake.pem
|
219
|
+
- templates/test/fixtures/files/plug_installation_settings/invalid.json
|
220
|
+
- templates/test/fixtures/files/plug_installation_settings/valid.json
|
221
|
+
- templates/test/fixtures/plug_installation_settings/invalid.json
|
222
|
+
- templates/test/fixtures/plug_installation_settings/valid.json
|
217
223
|
- templates/test/integration/.keep
|
218
224
|
- templates/test/jobs/update_yetto_job_test.rb
|
219
225
|
- templates/test/mailers/.keep
|
@@ -245,7 +251,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
245
251
|
- !ruby/object:Gem::Version
|
246
252
|
version: 3.4.7
|
247
253
|
requirements: []
|
248
|
-
rubygems_version: 3.4.
|
254
|
+
rubygems_version: 3.4.20
|
249
255
|
signing_key:
|
250
256
|
specification_version: 4
|
251
257
|
summary: Generate a Rails app that can be used to create plugs for Yetto.
|