dev_suite 0.2.7 → 0.2.10
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/.codeclimate.yml +12 -0
- data/.github/workflows/ci.yml +1 -1
- data/.sonarcloud.properties +23 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +17 -33
- data/README.md +216 -0
- data/dev_suite.gemspec +1 -0
- data/examples/helpers/api_helper.rb +51 -0
- data/examples/helpers/data_helper.rb +72 -0
- data/examples/helpers/helpers.rb +4 -0
- data/examples/workflow/basic_workflow.rb +15 -0
- data/examples/workflow/composite_workflow.rb +21 -0
- data/examples/workflow/conditional_workflow.rb +17 -0
- data/examples/workflow/full_workflow.rb +79 -0
- data/examples/workflow/loop_workflow.rb +17 -0
- data/examples/workflow/order_processing_workflow.rb +163 -0
- data/examples/workflow/parallel_workflow.rb +17 -0
- data/lib/dev_suite/dev_suite.rb +3 -0
- data/lib/dev_suite/method_tracer/config/config.rb +11 -0
- data/lib/dev_suite/method_tracer/config/configuration.rb +16 -0
- data/lib/dev_suite/method_tracer/config.rb +9 -0
- data/lib/dev_suite/method_tracer/helpers.rb +41 -0
- data/lib/dev_suite/method_tracer/logger.rb +46 -0
- data/lib/dev_suite/method_tracer/method_tracer.rb +20 -0
- data/lib/dev_suite/method_tracer/tracer.rb +65 -0
- data/lib/dev_suite/method_tracer.rb +7 -0
- data/lib/dev_suite/request_builder/builder/base.rb +27 -0
- data/lib/dev_suite/request_builder/builder/builder.rb +10 -0
- data/lib/dev_suite/request_builder/builder/http.rb +32 -0
- data/lib/dev_suite/request_builder/builder.rb +9 -0
- data/lib/dev_suite/request_builder/config/config.rb +11 -0
- data/lib/dev_suite/request_builder/config/configuration.rb +24 -0
- data/lib/dev_suite/request_builder/config.rb +9 -0
- data/lib/dev_suite/request_builder/formatter/base.rb +13 -0
- data/lib/dev_suite/request_builder/formatter/formatter.rb +10 -0
- data/lib/dev_suite/request_builder/formatter/graphql.rb +19 -0
- data/lib/dev_suite/request_builder/formatter.rb +9 -0
- data/lib/dev_suite/request_builder/request_builder.rb +21 -0
- data/lib/dev_suite/request_builder/tool/base.rb +19 -0
- data/lib/dev_suite/request_builder/tool/curl.rb +91 -0
- data/lib/dev_suite/request_builder/tool/tool.rb +11 -0
- data/lib/dev_suite/request_builder/tool/validator/curl.rb +38 -0
- data/lib/dev_suite/request_builder/tool/validator/validator.rb +11 -0
- data/lib/dev_suite/request_builder/tool/validator.rb +11 -0
- data/lib/dev_suite/request_builder/tool.rb +9 -0
- data/lib/dev_suite/request_builder.rb +7 -0
- data/lib/dev_suite/request_logger/adapter/adapter.rb +11 -9
- data/lib/dev_suite/request_logger/adapter/faraday.rb +12 -1
- data/lib/dev_suite/request_logger/adapter/middleware/faraday.rb +3 -3
- data/lib/dev_suite/request_logger/adapter/net_http.rb +15 -6
- data/lib/dev_suite/request_logger/config/configuration.rb +1 -0
- data/lib/dev_suite/request_logger/extractor/base.rb +8 -2
- data/lib/dev_suite/request_logger/extractor/extractor.rb +5 -6
- data/lib/dev_suite/request_logger/extractor/faraday.rb +32 -14
- data/lib/dev_suite/request_logger/extractor/net_http.rb +53 -12
- data/lib/dev_suite/request_logger/logger.rb +9 -3
- data/lib/dev_suite/request_logger/request.rb +12 -0
- data/lib/dev_suite/request_logger/response.rb +34 -9
- data/lib/dev_suite/utils/construct/component/base.rb +13 -0
- data/lib/dev_suite/utils/construct/component/component.rb +1 -0
- data/lib/dev_suite/utils/construct/component/manager.rb +27 -10
- data/lib/dev_suite/utils/construct/component/validator/base.rb +25 -0
- data/lib/dev_suite/utils/construct/component/validator/validation_error.rb +21 -0
- data/lib/dev_suite/utils/construct/component/validator/validation_rule.rb +68 -0
- data/lib/dev_suite/utils/construct/component/validator/validator.rb +15 -0
- data/lib/dev_suite/utils/construct/component/validator.rb +13 -0
- data/lib/dev_suite/utils/construct/config/dependency_handler.rb +25 -34
- data/lib/dev_suite/utils/construct/config/settings/base.rb +43 -26
- data/lib/dev_suite/utils/data/base_operations.rb +61 -0
- data/lib/dev_suite/utils/data/data.rb +19 -0
- data/lib/dev_suite/utils/data/path_access.rb +172 -0
- data/lib/dev_suite/utils/data/search_filter.rb +60 -0
- data/lib/dev_suite/utils/data/serialization.rb +29 -0
- data/lib/dev_suite/utils/data/transformations.rb +45 -0
- data/lib/dev_suite/utils/data.rb +9 -0
- data/lib/dev_suite/utils/dependency_loader.rb +2 -2
- data/lib/dev_suite/utils/emoji.rb +19 -0
- data/lib/dev_suite/utils/file_loader/file_loader.rb +1 -5
- data/lib/dev_suite/utils/file_loader/loader/json.rb +4 -1
- data/lib/dev_suite/utils/file_loader/loader/loader.rb +23 -19
- data/lib/dev_suite/utils/file_loader/loader.rb +0 -2
- data/lib/dev_suite/utils/file_writer/atomic_writer.rb +53 -0
- data/lib/dev_suite/utils/file_writer/backup_manager.rb +21 -0
- data/lib/dev_suite/utils/file_writer/file_writer.rb +24 -0
- data/lib/dev_suite/utils/file_writer/writer/base.rb +43 -0
- data/lib/dev_suite/utils/file_writer/writer/json.rb +24 -0
- data/lib/dev_suite/utils/file_writer/writer/text.rb +27 -0
- data/lib/dev_suite/utils/file_writer/writer/writer.rb +14 -0
- data/lib/dev_suite/utils/file_writer/writer/yaml.rb +44 -0
- data/lib/dev_suite/utils/file_writer/writer.rb +11 -0
- data/lib/dev_suite/utils/file_writer/writer_manager.rb +32 -0
- data/lib/dev_suite/utils/file_writer.rb +9 -0
- data/lib/dev_suite/utils/logger.rb +7 -5
- data/lib/dev_suite/utils/store/config/config.rb +13 -0
- data/lib/dev_suite/utils/store/config/configuration.rb +30 -0
- data/lib/dev_suite/utils/store/config.rb +11 -0
- data/lib/dev_suite/utils/store/driver/base.rb +35 -0
- data/lib/dev_suite/utils/store/driver/driver.rb +18 -0
- data/lib/dev_suite/utils/store/driver/file.rb +61 -0
- data/lib/dev_suite/utils/store/driver/memory.rb +42 -0
- data/lib/dev_suite/utils/store/driver.rb +11 -0
- data/lib/dev_suite/utils/store/store.rb +69 -0
- data/lib/dev_suite/utils/store.rb +9 -0
- data/lib/dev_suite/utils/utils.rb +17 -5
- data/lib/dev_suite/utils/warning_handler.rb +25 -0
- data/lib/dev_suite/version.rb +1 -1
- data/lib/dev_suite/workflow/engine.rb +27 -0
- data/lib/dev_suite/workflow/step/base.rb +56 -0
- data/lib/dev_suite/workflow/step/composite.rb +27 -0
- data/lib/dev_suite/workflow/step/conditional.rb +23 -0
- data/lib/dev_suite/workflow/step/loop.rb +21 -0
- data/lib/dev_suite/workflow/step/parallel.rb +18 -0
- data/lib/dev_suite/workflow/step/step.rb +21 -0
- data/lib/dev_suite/workflow/step.rb +9 -0
- data/lib/dev_suite/workflow/step_context.rb +46 -0
- data/lib/dev_suite/workflow/workflow.rb +39 -0
- data/lib/dev_suite/workflow.rb +7 -0
- metadata +101 -3
- data/lib/dev_suite/utils/construct/component/initializer.rb +0 -28
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
$LOAD_PATH.unshift(File.expand_path("../../lib", __dir__))
|
|
4
|
+
require "dev_suite"
|
|
5
|
+
|
|
6
|
+
# Create a workflow with a looping step
|
|
7
|
+
engine = DevSuite::Workflow::Engine.new(iteration_count: 0)
|
|
8
|
+
|
|
9
|
+
# Add a loop step
|
|
10
|
+
loop_step = DevSuite::Workflow.create_loop_step("Repeat Task", 3) do |context|
|
|
11
|
+
iteration = context.get(:iteration_count) + 1
|
|
12
|
+
context.update({ iteration_count: iteration })
|
|
13
|
+
puts "Iteration #{iteration}"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
engine.step(loop_step)
|
|
17
|
+
engine.execute
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../helpers"
|
|
4
|
+
|
|
5
|
+
$LOAD_PATH.unshift(File.expand_path("../../lib", __dir__))
|
|
6
|
+
require "dev_suite"
|
|
7
|
+
|
|
8
|
+
# Constants
|
|
9
|
+
BASE_URL = "https://ecommerce.example.com"
|
|
10
|
+
NUMBER_OF_USERS = 500
|
|
11
|
+
CHUNK_SIZE = 50
|
|
12
|
+
DEFAULT_PASSWORD = "password123"
|
|
13
|
+
STORE_PATH = "tmp/order_store.json"
|
|
14
|
+
|
|
15
|
+
# Headers for HTTP requests
|
|
16
|
+
REQUEST_HEADERS = {
|
|
17
|
+
"accept" => "*/*",
|
|
18
|
+
"content-type" => "application/x-www-form-urlencoded",
|
|
19
|
+
"user-agent" => "Mozilla/5.0",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
# Helper Functions
|
|
23
|
+
def register_user(username, phone, password)
|
|
24
|
+
endpoint = "#{BASE_URL}/api/v1/register"
|
|
25
|
+
body_data = { "username" => username, "phone" => phone, "password" => password }
|
|
26
|
+
APIHelper.make_post_request(endpoint: endpoint, body_data: body_data, success_criteria: ->(response) {
|
|
27
|
+
response["status"] == "success"
|
|
28
|
+
})
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def login_user(username, password)
|
|
32
|
+
endpoint = "#{BASE_URL}/api/v1/login"
|
|
33
|
+
body_data = { "username" => username, "password" => password }
|
|
34
|
+
APIHelper.make_post_request(endpoint: endpoint, body_data: body_data, success_criteria: ->(response) {
|
|
35
|
+
response["token"].present?
|
|
36
|
+
})
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def fetch_order(token, user_id)
|
|
40
|
+
endpoint = "#{BASE_URL}/api/v1/orders"
|
|
41
|
+
body_data = { "token" => token, "user_id" => user_id }
|
|
42
|
+
APIHelper.make_post_request(endpoint: endpoint, body_data: body_data, success_criteria: ->(response) {
|
|
43
|
+
response["order_id"].present?
|
|
44
|
+
})
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def process_payment(order_id, token, user_id)
|
|
48
|
+
endpoint = "#{BASE_URL}/api/v1/pay"
|
|
49
|
+
body_data = { "order_id" => order_id, "token" => token, "user_id" => user_id }
|
|
50
|
+
APIHelper.make_post_request(endpoint: endpoint, body_data: body_data, success_criteria: ->(response) {
|
|
51
|
+
response["status"] == "success"
|
|
52
|
+
})
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Workflow Steps
|
|
56
|
+
register_step = DevSuite::Workflow.create_step("Register Users") do |ctx|
|
|
57
|
+
users = []
|
|
58
|
+
password = ctx.get(:password)
|
|
59
|
+
|
|
60
|
+
# Generate fake users using Faker
|
|
61
|
+
user_tasks = Array.new(NUMBER_OF_USERS) do
|
|
62
|
+
{
|
|
63
|
+
username: DataHelper.generate_human_username,
|
|
64
|
+
phone: DataHelper.generate_phone_number,
|
|
65
|
+
password: password,
|
|
66
|
+
}
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
user_chunks = user_tasks.each_slice(CHUNK_SIZE).to_a
|
|
70
|
+
|
|
71
|
+
user_chunks.each do |chunk|
|
|
72
|
+
parallel_step = DevSuite::Workflow.create_parallel_step("Parallel Registration") do |ctx|
|
|
73
|
+
chunk.map do |user_data|
|
|
74
|
+
->(ctx) {
|
|
75
|
+
username = user_data[:username]
|
|
76
|
+
phone = user_data[:phone]
|
|
77
|
+
password = user_data[:password]
|
|
78
|
+
|
|
79
|
+
puts "Registering user #{username}..."
|
|
80
|
+
response = register_user(username, phone, password)
|
|
81
|
+
|
|
82
|
+
if response[:success]
|
|
83
|
+
puts "User #{username} registered successfully!"
|
|
84
|
+
users << user_data
|
|
85
|
+
else
|
|
86
|
+
puts "Failed to register #{username}: #{response&.dig(:response, "error") || "Unknown error"}"
|
|
87
|
+
end
|
|
88
|
+
}
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
parallel_step.run(ctx)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
ctx.set(:users, users)
|
|
95
|
+
ctx.data
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
save_users_step = DevSuite::Workflow.create_conditional_step("Save Users to Store", ->(ctx) {
|
|
99
|
+
ctx.get(:users)&.any?
|
|
100
|
+
}) do |ctx|
|
|
101
|
+
ctx.store.set(:users, ctx.get(:users))
|
|
102
|
+
puts "Saved #{ctx.get(:users).size} users to the store."
|
|
103
|
+
ctx.data
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
login_step = DevSuite::Workflow.create_conditional_step("Login Users", ->(ctx) {
|
|
107
|
+
ctx.get(:username) && ctx.get(:password)
|
|
108
|
+
}) do |ctx|
|
|
109
|
+
response = login_user(ctx.get(:username), ctx.get(:password))
|
|
110
|
+
|
|
111
|
+
if response[:success]
|
|
112
|
+
ctx.set(:token, response[:response]["token"])
|
|
113
|
+
ctx.set(:user_id, response[:response]["user_id"])
|
|
114
|
+
puts "User #{ctx.get(:username)} logged in successfully!"
|
|
115
|
+
else
|
|
116
|
+
puts "Failed to log in user #{ctx.get(:username)}."
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
ctx.data
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
fetch_order_step = DevSuite::Workflow.create_conditional_step("Fetch Orders", ->(ctx) {
|
|
123
|
+
ctx.get(:token) && ctx.get(:user_id)
|
|
124
|
+
}) do |ctx|
|
|
125
|
+
response = fetch_order(ctx.get(:token), ctx.get(:user_id))
|
|
126
|
+
|
|
127
|
+
if response[:success]
|
|
128
|
+
ctx.set(:order_id, response[:response]["order_id"])
|
|
129
|
+
puts "Order fetched for user #{ctx.get(:username)}: Order ID #{response[:response]["order_id"]}"
|
|
130
|
+
else
|
|
131
|
+
puts "Failed to fetch order for user #{ctx.get(:username)}."
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
ctx.data
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
process_payment_step = DevSuite::Workflow.create_conditional_step("Process Payment", ->(ctx) {
|
|
138
|
+
ctx.get(:order_id) && ctx.get(:token) && ctx.get(:user_id)
|
|
139
|
+
}) do |ctx|
|
|
140
|
+
response = process_payment(ctx.get(:order_id), ctx.get(:token), ctx.get(:user_id))
|
|
141
|
+
|
|
142
|
+
if response[:success]
|
|
143
|
+
puts "Payment processed for order #{ctx.get(:order_id)}."
|
|
144
|
+
else
|
|
145
|
+
puts "Failed to process payment for order #{ctx.get(:order_id)}."
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
ctx.data
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Main Workflow Execution
|
|
152
|
+
workflow = DevSuite::Workflow.create_engine(
|
|
153
|
+
{ password: DEFAULT_PASSWORD },
|
|
154
|
+
driver: :file,
|
|
155
|
+
path: STORE_PATH,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
workflow.step(register_step)
|
|
159
|
+
.step(save_users_step)
|
|
160
|
+
.step(login_step)
|
|
161
|
+
.step(fetch_order_step)
|
|
162
|
+
.step(process_payment_step)
|
|
163
|
+
.execute
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
$LOAD_PATH.unshift(File.expand_path("../../lib", __dir__))
|
|
4
|
+
require "dev_suite"
|
|
5
|
+
|
|
6
|
+
# Create a workflow with parallel tasks
|
|
7
|
+
engine = DevSuite::Workflow::Engine.new
|
|
8
|
+
|
|
9
|
+
parallel_step = DevSuite::Workflow.create_parallel_step("Parallel Task") do |context|
|
|
10
|
+
[
|
|
11
|
+
->(ctx) { puts "Task 1 executed" },
|
|
12
|
+
->(ctx) { puts "Task 2 executed" },
|
|
13
|
+
]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
engine.step(parallel_step)
|
|
17
|
+
engine.execute
|
data/lib/dev_suite/dev_suite.rb
CHANGED
|
@@ -7,6 +7,9 @@ module DevSuite
|
|
|
7
7
|
require_relative "performance"
|
|
8
8
|
require_relative "directory_tree"
|
|
9
9
|
require_relative "request_logger"
|
|
10
|
+
require_relative "request_builder"
|
|
11
|
+
require_relative "workflow"
|
|
12
|
+
require_relative "method_tracer"
|
|
10
13
|
|
|
11
14
|
class Error < StandardError; end
|
|
12
15
|
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DevSuite
|
|
4
|
+
module MethodTracer
|
|
5
|
+
module Config
|
|
6
|
+
class Configuration < Utils::Construct::Config::Base
|
|
7
|
+
set_default_settings(
|
|
8
|
+
show_params: false,
|
|
9
|
+
show_results: false,
|
|
10
|
+
show_execution_time: false,
|
|
11
|
+
max_depth: nil,
|
|
12
|
+
)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DevSuite
|
|
4
|
+
module MethodTracer
|
|
5
|
+
module Helpers
|
|
6
|
+
class << self
|
|
7
|
+
def internal_call?(tp)
|
|
8
|
+
tp.path&.include?("<internal:") || tp.defined_class.to_s.start_with?("TracePoint")
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def format_method_name(tp)
|
|
12
|
+
"#{tp.defined_class}##{tp.method_id}"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def format_params(tp)
|
|
16
|
+
param_values = tp.binding.local_variables.map do |var|
|
|
17
|
+
tp.binding.local_variable_get(var).inspect
|
|
18
|
+
end.join(", ")
|
|
19
|
+
"(#{param_values})"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def format_result(tp)
|
|
23
|
+
" #=> #{tp.return_value.inspect}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def format_execution_time(duration)
|
|
27
|
+
duration ? " in #{duration}ms" : ""
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def calculate_duration(tp, start_times)
|
|
31
|
+
start_time = start_times.delete(tp.method_id)
|
|
32
|
+
start_time ? ((Time.now - start_time) * 1000.0).round(2) : nil
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def calculate_indent(depth)
|
|
36
|
+
" " * [(depth - 1), 0].max
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DevSuite
|
|
4
|
+
module MethodTracer
|
|
5
|
+
module Logger
|
|
6
|
+
class << self
|
|
7
|
+
def log_method_call(tp, tracer)
|
|
8
|
+
tracer.depth += 1
|
|
9
|
+
|
|
10
|
+
method_name = Helpers.format_method_name(tp)
|
|
11
|
+
params_str = Helpers.format_params(tp) if tracer.show_params
|
|
12
|
+
indent = Helpers.calculate_indent(tracer.depth)
|
|
13
|
+
|
|
14
|
+
# Store the start time for execution time calculation
|
|
15
|
+
tracer.start_times[tp.method_id] = Time.now
|
|
16
|
+
|
|
17
|
+
message = "#{indent}#depth:#{tracer.depth} > #{method_name} at #{tp.path}:#{tp.lineno} #{params_str}"
|
|
18
|
+
Utils::Logger.log(
|
|
19
|
+
message,
|
|
20
|
+
emoji: :start,
|
|
21
|
+
color: :blue,
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def log_method_return(tp, tracer)
|
|
26
|
+
duration = Helpers.calculate_duration(tp, tracer.start_times)
|
|
27
|
+
|
|
28
|
+
method_name = Helpers.format_method_name(tp)
|
|
29
|
+
result_str = Helpers.format_result(tp) if tracer.show_results
|
|
30
|
+
exec_time_str = Helpers.format_execution_time(duration) if tracer.show_execution_time
|
|
31
|
+
indent = Helpers.calculate_indent(tracer.depth)
|
|
32
|
+
|
|
33
|
+
message = "#{indent}#depth:#{tracer.depth} < #{method_name}#{result_str} at #{tp.path}:#{tp.lineno}#{exec_time_str}"
|
|
34
|
+
Utils::Logger.log(
|
|
35
|
+
message,
|
|
36
|
+
emoji: :finish,
|
|
37
|
+
color: :cyan,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# Decrement depth safely
|
|
41
|
+
tracer.depth = [tracer.depth - 1, 0].max
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DevSuite
|
|
4
|
+
module MethodTracer
|
|
5
|
+
require_relative "tracer"
|
|
6
|
+
require_relative "logger"
|
|
7
|
+
require_relative "helpers"
|
|
8
|
+
require_relative "config"
|
|
9
|
+
|
|
10
|
+
class << self
|
|
11
|
+
def trace(options = {}, &block)
|
|
12
|
+
# Merge global configuration with options provided in the call
|
|
13
|
+
settings = Config.configuration.settings.merge(options)
|
|
14
|
+
|
|
15
|
+
# Use the merged settings to initialize the tracer
|
|
16
|
+
Tracer.new(**settings).trace(&block)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DevSuite
|
|
4
|
+
module MethodTracer
|
|
5
|
+
class Tracer
|
|
6
|
+
attr_reader :show_params, :show_results, :show_execution_time, :max_depth
|
|
7
|
+
attr_accessor :depth, :trace_point, :start_times
|
|
8
|
+
|
|
9
|
+
def initialize(
|
|
10
|
+
show_params: false,
|
|
11
|
+
show_results: false,
|
|
12
|
+
show_execution_time: false,
|
|
13
|
+
max_depth: nil
|
|
14
|
+
)
|
|
15
|
+
@show_params = show_params
|
|
16
|
+
@show_results = show_results
|
|
17
|
+
@show_execution_time = show_execution_time
|
|
18
|
+
@max_depth = max_depth
|
|
19
|
+
@depth = 0
|
|
20
|
+
@start_times = {}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def trace(&block)
|
|
24
|
+
setup_trace_point
|
|
25
|
+
trace_point.enable
|
|
26
|
+
block.call
|
|
27
|
+
ensure
|
|
28
|
+
trace_point&.disable
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def setup_trace_point
|
|
34
|
+
self.trace_point = TracePoint.new(:call, :return) do |tp|
|
|
35
|
+
next if Helpers.internal_call?(tp)
|
|
36
|
+
|
|
37
|
+
handle_trace_point_event(tp)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def handle_trace_point_event(tp)
|
|
42
|
+
case tp.event
|
|
43
|
+
when :call
|
|
44
|
+
handle_call_event(tp)
|
|
45
|
+
when :return
|
|
46
|
+
handle_return_event(tp)
|
|
47
|
+
else
|
|
48
|
+
raise "Unknown event type: #{tp.event}"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def handle_call_event(tp)
|
|
53
|
+
Logger.log_method_call(tp, self) if depth_within_limit?
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def handle_return_event(tp)
|
|
57
|
+
Logger.log_method_return(tp, self) if depth_within_limit?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def depth_within_limit?
|
|
61
|
+
max_depth.nil? || @depth < max_depth
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DevSuite
|
|
4
|
+
module RequestBuilder
|
|
5
|
+
module Builder
|
|
6
|
+
class Base
|
|
7
|
+
attr_reader :url, :headers, :tool
|
|
8
|
+
|
|
9
|
+
def initialize(url:, headers: {}, tool:)
|
|
10
|
+
@url = url
|
|
11
|
+
@headers = headers
|
|
12
|
+
@tool = tool
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def build_command
|
|
16
|
+
raise NotImplementedError, "Subclasses must implement the `build_command` method"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
protected
|
|
20
|
+
|
|
21
|
+
def format_headers
|
|
22
|
+
headers.map { |key, value| "#{key}: #{value}" }.join("\n")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DevSuite
|
|
4
|
+
module RequestBuilder
|
|
5
|
+
module Builder
|
|
6
|
+
class Http < Base
|
|
7
|
+
attr_reader :http_method, :body
|
|
8
|
+
|
|
9
|
+
def initialize(url:, http_method: "GET", headers: {}, body: nil, tool:)
|
|
10
|
+
super(url: url, headers: headers, tool: tool)
|
|
11
|
+
@http_method = http_method.upcase
|
|
12
|
+
@body = body
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def build_command
|
|
16
|
+
case tool
|
|
17
|
+
when :curl
|
|
18
|
+
Tool::Curl.new.build_command(http_method: http_method, url: url, headers: headers, body: format_body(body))
|
|
19
|
+
else
|
|
20
|
+
raise ArgumentError, "Unknown tool: #{tool}"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def format_body(body)
|
|
27
|
+
Formatter::Graphql.new.format_body(body)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DevSuite
|
|
4
|
+
module RequestBuilder
|
|
5
|
+
module Config
|
|
6
|
+
class Configuration < Utils::Construct::Config::Base
|
|
7
|
+
set_default_settings(
|
|
8
|
+
tool: {
|
|
9
|
+
curl: {
|
|
10
|
+
use_insecure: false,
|
|
11
|
+
raw_data: false,
|
|
12
|
+
verbose: false,
|
|
13
|
+
follow_redirects: true,
|
|
14
|
+
cookie: nil,
|
|
15
|
+
user_agent: "Mozilla/5.0 (compatible; RequestBuilder/1.0)",
|
|
16
|
+
max_time: nil,
|
|
17
|
+
connect_timeout: nil,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DevSuite
|
|
4
|
+
module RequestBuilder
|
|
5
|
+
module Formatter
|
|
6
|
+
class Graphql < Base
|
|
7
|
+
def format_body(body)
|
|
8
|
+
if body.is_a?(Hash) && body[:query] && body[:variables]
|
|
9
|
+
{ query: body[:query], variables: body[:variables] }.to_json
|
|
10
|
+
elsif body.is_a?(String)
|
|
11
|
+
{ query: body, variables: {} }.to_json
|
|
12
|
+
else
|
|
13
|
+
raise ArgumentError, "Invalid GraphQL body format"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DevSuite
|
|
4
|
+
module RequestBuilder
|
|
5
|
+
require_relative "config"
|
|
6
|
+
require_relative "formatter"
|
|
7
|
+
require_relative "tool"
|
|
8
|
+
require_relative "builder"
|
|
9
|
+
|
|
10
|
+
class << self
|
|
11
|
+
def build(protocol:, tool:, **options)
|
|
12
|
+
case protocol
|
|
13
|
+
when :http
|
|
14
|
+
Builder::Http.new(tool: tool, **options)
|
|
15
|
+
else
|
|
16
|
+
raise ArgumentError, "Unknown protocol: #{protocol}"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DevSuite
|
|
4
|
+
module RequestBuilder
|
|
5
|
+
module Tool
|
|
6
|
+
class Base < Utils::Construct::Component::Base
|
|
7
|
+
def build_command(http_method:, url:, headers:, body: nil)
|
|
8
|
+
raise NotImplementedError
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def fetch_setting(key, default: nil)
|
|
14
|
+
Config.configuration.settings.get(key, default: default)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|