appmap 0.53.0 → 0.54.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +16 -10
- data/CHANGELOG.md +41 -0
- data/Rakefile +13 -4
- data/appmap.gemspec +1 -0
- data/lib/appmap.rb +31 -112
- data/lib/appmap/agent.rb +115 -0
- data/lib/appmap/class_map.rb +2 -2
- data/lib/appmap/config.rb +11 -3
- data/lib/appmap/handler/net_http.rb +3 -2
- data/lib/appmap/handler/rails/request_handler.rb +2 -1
- data/lib/appmap/handler/rails/template.rb +2 -1
- data/lib/appmap/hook.rb +7 -3
- data/lib/appmap/hook/method.rb +36 -28
- data/lib/appmap/metadata.rb +4 -2
- data/lib/appmap/minitest.rb +2 -0
- data/lib/appmap/railtie.rb +2 -2
- data/lib/appmap/rspec.rb +5 -3
- data/lib/appmap/swagger.rb +2 -0
- data/lib/appmap/swagger/configuration.rb +70 -0
- data/lib/appmap/swagger/markdown_descriptions.rb +43 -0
- data/lib/appmap/swagger/rake_tasks.rb +43 -0
- data/lib/appmap/swagger/stable.rb +37 -0
- data/lib/appmap/trace.rb +2 -0
- data/lib/appmap/util.rb +17 -1
- data/lib/appmap/version.rb +1 -1
- data/package.json +0 -1
- data/release.sh +0 -1
- data/spec/config_spec.rb +0 -1
- data/spec/fixtures/hook/.gitignore +2 -0
- data/spec/fixtures/hook/app/controllers/api/api_keys_controller.rb +1 -0
- data/spec/fixtures/hook/app/controllers/organizations_controller.rb +1 -0
- data/spec/fixtures/hook/app/models/api_key.rb +1 -0
- data/spec/fixtures/hook/app/models/configuration.rb +1 -0
- data/spec/fixtures/hook/app/models/show.rb +1 -0
- data/spec/fixtures/hook/app/models/user.rb +1 -0
- data/spec/fixtures/hook/kwargs.rb +11 -0
- data/spec/fixtures/hook/revoke_api_key.appmap.json +847 -0
- data/spec/fixtures/hook/spec/api_spec.rb +1 -0
- data/spec/fixtures/hook/spec/user_spec.rb +1 -0
- data/spec/fixtures/hook/user_page_scenario.appmap.json +1722 -0
- data/spec/fixtures/rails5_users_app/config/environments/test.rb +3 -0
- data/spec/fixtures/rails6_users_app/Dockerfile +0 -1
- data/spec/fixtures/rails6_users_app/appmap.yml +3 -1
- data/spec/fixtures/rails6_users_app/config/environments/test.rb +3 -0
- data/spec/fixtures/rails6_users_app/lib/tasks/appmap.rake +13 -0
- data/spec/hook_spec.rb +9 -0
- data/spec/rails_recording_spec.rb +1 -1
- data/spec/record_net_http_spec.rb +1 -0
- data/spec/swagger/swagger_spec.rb +47 -0
- data/test/gem_test.rb +1 -0
- metadata +36 -2
data/lib/appmap/config.rb
CHANGED
@@ -5,6 +5,7 @@ require 'appmap/util'
|
|
5
5
|
require 'appmap/handler/net_http'
|
6
6
|
require 'appmap/handler/rails/template'
|
7
7
|
require 'appmap/service/guesser'
|
8
|
+
require 'appmap/swagger/configuration'
|
8
9
|
|
9
10
|
module AppMap
|
10
11
|
class Config
|
@@ -81,8 +82,8 @@ module AppMap
|
|
81
82
|
package_name: package_name,
|
82
83
|
gem: gem,
|
83
84
|
handler_class: handler_class.name,
|
84
|
-
exclude:
|
85
|
-
labels:
|
85
|
+
exclude: Util.blank?(exclude) ? nil : exclude,
|
86
|
+
labels: Util.blank?(labels) ? nil : labels,
|
86
87
|
shallow: shallow
|
87
88
|
}.compact
|
88
89
|
end
|
@@ -226,15 +227,17 @@ module AppMap
|
|
226
227
|
'JSON::Ext::Generator::State' => TargetMethods.new(:generate, Package.build_from_path('json', package_name: 'json', labels: %w[format.json.generate])),
|
227
228
|
}.freeze
|
228
229
|
|
229
|
-
attr_reader :name, :appmap_dir, :packages, :exclude, :hooked_methods, :builtin_hooks
|
230
|
+
attr_reader :name, :appmap_dir, :packages, :exclude, :swagger_config, :hooked_methods, :builtin_hooks
|
230
231
|
|
231
232
|
def initialize(name,
|
232
233
|
packages: [],
|
234
|
+
swagger_config: Swagger::Configuration.new,
|
233
235
|
exclude: [],
|
234
236
|
functions: [])
|
235
237
|
@name = name
|
236
238
|
@appmap_dir = AppMap::DEFAULT_APPMAP_DIR
|
237
239
|
@packages = packages
|
240
|
+
@swagger_config = swagger_config
|
238
241
|
@hook_paths = Set.new(packages.map(&:path))
|
239
242
|
@exclude = exclude
|
240
243
|
@builtin_hooks = BUILTIN_HOOKS
|
@@ -351,6 +354,11 @@ module AppMap
|
|
351
354
|
end
|
352
355
|
end
|
353
356
|
|
357
|
+
if config_data['swagger']
|
358
|
+
swagger_config = Swagger::Configuration.load(config_data['swagger'])
|
359
|
+
config_params[:swagger_config] = swagger_config
|
360
|
+
end
|
361
|
+
|
354
362
|
Config.new name, config_params
|
355
363
|
end
|
356
364
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'appmap/event'
|
4
|
+
require 'appmap/util'
|
4
5
|
|
5
6
|
module AppMap
|
6
7
|
module Handler
|
@@ -38,7 +39,7 @@ module AppMap
|
|
38
39
|
headers: headers
|
39
40
|
}.compact
|
40
41
|
|
41
|
-
unless
|
42
|
+
unless Util.blank?(params)
|
42
43
|
h[:message] = params.keys.map do |key|
|
43
44
|
val = params[key]
|
44
45
|
{
|
@@ -81,7 +82,7 @@ module AppMap
|
|
81
82
|
def request_headers(request)
|
82
83
|
{}.tap do |headers|
|
83
84
|
request.each_header do |k,v|
|
84
|
-
key = [ 'HTTP',
|
85
|
+
key = [ 'HTTP', Util.underscore(k).upcase ].join('_')
|
85
86
|
headers[key] = v
|
86
87
|
end
|
87
88
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'appmap/event'
|
4
4
|
require 'appmap/hook'
|
5
|
+
require 'appmap/util'
|
5
6
|
|
6
7
|
module AppMap
|
7
8
|
module Handler
|
@@ -40,7 +41,7 @@ module AppMap
|
|
40
41
|
headers: headers,
|
41
42
|
}.compact
|
42
43
|
|
43
|
-
unless
|
44
|
+
unless Util.blank?(params)
|
44
45
|
h[:message] = params.keys.map do |key|
|
45
46
|
val = params[key]
|
46
47
|
{
|
data/lib/appmap/hook.rb
CHANGED
@@ -37,8 +37,6 @@ module AppMap
|
|
37
37
|
def initialize(config)
|
38
38
|
@config = config
|
39
39
|
@trace_enabled = []
|
40
|
-
# Paths that are known to be non-tracing
|
41
|
-
@notrace_paths = Set.new
|
42
40
|
end
|
43
41
|
|
44
42
|
# Observe class loading and hook all methods which match the config.
|
@@ -47,6 +45,11 @@ module AppMap
|
|
47
45
|
|
48
46
|
hook_builtins
|
49
47
|
|
48
|
+
# Paths that are known to be non-tracing.
|
49
|
+
@notrace_paths = Set.new
|
50
|
+
# Locations that have already been visited.
|
51
|
+
@trace_locations = Set.new
|
52
|
+
|
50
53
|
@trace_end = TracePoint.new(:end, &method(:trace_end))
|
51
54
|
@trace_end.enable(&block)
|
52
55
|
end
|
@@ -100,7 +103,8 @@ module AppMap
|
|
100
103
|
|
101
104
|
def trace_end(trace_point)
|
102
105
|
location = trace_location(trace_point)
|
103
|
-
warn "Class or module ends at location #{
|
106
|
+
warn "Class or module ends at location #{location}" if Hook::LOG || Hook::LOG_HOOK
|
107
|
+
return unless @trace_locations.add?(location)
|
104
108
|
|
105
109
|
path = trace_point.path
|
106
110
|
enabled = !@notrace_paths.member?(path) && config.path_enabled?(path)
|
data/lib/appmap/hook/method.rb
CHANGED
@@ -18,6 +18,8 @@ module AppMap
|
|
18
18
|
TIME_NOW = Time.method(:now)
|
19
19
|
private_constant :TIME_NOW
|
20
20
|
|
21
|
+
ARRAY_OF_EMPTY_HASH = [{}.freeze].freeze
|
22
|
+
|
21
23
|
def initialize(hook_package, hook_class, hook_method)
|
22
24
|
@hook_package = hook_package
|
23
25
|
@hook_class = hook_class
|
@@ -45,42 +47,48 @@ module AppMap
|
|
45
47
|
after_hook = self.method(:after_hook)
|
46
48
|
with_disabled_hook = self.method(:with_disabled_hook)
|
47
49
|
|
48
|
-
hook_method_def =
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
50
|
+
hook_method_def = Proc.new do |*args, &block|
|
51
|
+
instance_method = hook_method.bind(self).to_proc
|
52
|
+
call_instance_method = -> {
|
53
|
+
# https://github.com/applandinc/appmap-ruby/issues/153
|
54
|
+
if Util.ruby_minor_version >= 2.7 && args == ARRAY_OF_EMPTY_HASH && hook_method.arity == 1
|
55
|
+
instance_method.call({}, &block)
|
56
|
+
else
|
57
|
+
instance_method.call(*args, &block)
|
58
|
+
end
|
59
|
+
}
|
53
60
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
61
|
+
# We may not have gotten the class for the method during
|
62
|
+
# initialization (e.g. for a singleton method on an embedded
|
63
|
+
# struct), so make sure we have it now.
|
64
|
+
defined_class, = Hook.qualify_method_name(hook_method) unless defined_class
|
58
65
|
|
59
|
-
|
60
|
-
|
61
|
-
|
66
|
+
reentrant = Thread.current[HOOK_DISABLE_KEY]
|
67
|
+
disabled_by_shallow_flag = \
|
68
|
+
-> { hook_package&.shallow? && AppMap.tracing.last_package_for_current_thread == hook_package }
|
62
69
|
|
63
|
-
|
70
|
+
enabled = true if AppMap.tracing.enabled? && !reentrant && !disabled_by_shallow_flag.call
|
64
71
|
|
65
|
-
|
72
|
+
return call_instance_method.call unless enabled
|
66
73
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
end
|
74
|
+
call_event, start_time = with_disabled_hook.call do
|
75
|
+
before_hook.call(self, defined_class, args)
|
76
|
+
end
|
77
|
+
return_value = nil
|
78
|
+
exception = nil
|
79
|
+
begin
|
80
|
+
return_value = call_instance_method.call
|
81
|
+
rescue
|
82
|
+
exception = $ERROR_INFO
|
83
|
+
raise
|
84
|
+
ensure
|
85
|
+
with_disabled_hook.call do
|
86
|
+
after_hook.call(self, call_event, start_time, return_value, exception) if call_event
|
81
87
|
end
|
82
88
|
end
|
83
89
|
end
|
90
|
+
hook_method_def = hook_method_def.ruby2_keywords if hook_method_def.respond_to?(:ruby2_keywords)
|
91
|
+
|
84
92
|
hook_class.define_method_with_arity(hook_method.name, hook_method.arity, hook_method_def)
|
85
93
|
end
|
86
94
|
|
data/lib/appmap/metadata.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'appmap/util'
|
4
|
+
|
3
5
|
module AppMap
|
4
6
|
module Metadata
|
5
7
|
class << self
|
@@ -40,9 +42,9 @@ module AppMap
|
|
40
42
|
git_sha = `git rev-parse HEAD`.strip
|
41
43
|
git_status = `git status -s`.split("\n").map(&:strip)
|
42
44
|
git_last_annotated_tag = `git describe --abbrev=0 2>/dev/null`.strip
|
43
|
-
git_last_annotated_tag = nil if
|
45
|
+
git_last_annotated_tag = nil if Util.blank?(git_last_annotated_tag)
|
44
46
|
git_last_tag = `git describe --abbrev=0 --tags 2>/dev/null`.strip
|
45
|
-
git_last_tag = nil if
|
47
|
+
git_last_tag = nil if Util.blank?(git_last_tag)
|
46
48
|
git_commits_since_last_annotated_tag = `git describe`.strip =~ /-(\d+)-(\w+)$/[1] rescue 0 if git_last_annotated_tag
|
47
49
|
git_commits_since_last_tag = `git describe --tags`.strip =~ /-(\d+)-(\w+)$/[1] rescue 0 if git_last_tag
|
48
50
|
|
data/lib/appmap/minitest.rb
CHANGED
data/lib/appmap/railtie.rb
CHANGED
@@ -5,8 +5,8 @@ module AppMap
|
|
5
5
|
class Railtie < ::Rails::Railtie
|
6
6
|
initializer 'appmap.remote_recording' do
|
7
7
|
require 'appmap/middleware/remote_recording'
|
8
|
-
Rails.application.config.middleware.
|
9
|
-
|
8
|
+
Rails.application.config.middleware.insert_before \
|
9
|
+
ActionDispatch::Executor,
|
10
10
|
AppMap::Middleware::RemoteRecording
|
11
11
|
end
|
12
12
|
|
data/lib/appmap/rspec.rb
CHANGED
@@ -62,8 +62,10 @@ module AppMap
|
|
62
62
|
example_group_parent = \
|
63
63
|
if example_group.respond_to?(:module_parent)
|
64
64
|
example_group.module_parent
|
65
|
-
|
65
|
+
elsif example_group.respond_to?(:parent)
|
66
66
|
example_group.parent
|
67
|
+
elsif example_group.respond_to?(:parent_groups)
|
68
|
+
example_group.parent_groups.first
|
67
69
|
end
|
68
70
|
|
69
71
|
example_group_parent != example_group ? ScopeExampleGroup.new(example_group_parent) : nil
|
@@ -109,13 +111,13 @@ module AppMap
|
|
109
111
|
|
110
112
|
description = []
|
111
113
|
scope = ScopeExample.new(example)
|
112
|
-
|
113
114
|
while scope
|
114
115
|
description << scope.description
|
115
116
|
scope = scope.parent
|
116
117
|
end
|
117
118
|
|
118
|
-
description.reject!(&:nil?)
|
119
|
+
description.reject!(&:nil?)
|
120
|
+
description.reject!(&Util.method(:blank?))
|
119
121
|
default_description = description.last
|
120
122
|
description.reverse!
|
121
123
|
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module AppMap
|
4
|
+
module Swagger
|
5
|
+
class Configuration
|
6
|
+
DEFAULT_VERSION = '1.0'
|
7
|
+
DEFAULT_OUTPUT_DIR = 'swagger'
|
8
|
+
DEFAULT_DESCRIPTION = 'Generate Swagger from AppMaps'
|
9
|
+
|
10
|
+
attr_accessor :project_version,
|
11
|
+
:output_dir,
|
12
|
+
:description
|
13
|
+
attr_writer :project_name, :template
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def load(config_data)
|
17
|
+
Configuration.new.tap do |config|
|
18
|
+
config_data.each do |k,v|
|
19
|
+
config.send "#{k}=", v
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
@project_name = nil
|
27
|
+
@project_version = DEFAULT_VERSION
|
28
|
+
@output_dir = DEFAULT_OUTPUT_DIR
|
29
|
+
@description = DEFAULT_DESCRIPTION
|
30
|
+
end
|
31
|
+
|
32
|
+
def project_name
|
33
|
+
@project_name || default_project_name
|
34
|
+
end
|
35
|
+
|
36
|
+
def template
|
37
|
+
@template || default_template
|
38
|
+
end
|
39
|
+
|
40
|
+
def default_template
|
41
|
+
YAML.load <<~TEMPLATE
|
42
|
+
openapi: 3.0.1
|
43
|
+
info:
|
44
|
+
title: #{project_name}
|
45
|
+
version: #{project_version}
|
46
|
+
paths:
|
47
|
+
components:
|
48
|
+
servers:
|
49
|
+
- url: http://{defaultHost}
|
50
|
+
variables:
|
51
|
+
defaultHost:
|
52
|
+
default: localhost:3000
|
53
|
+
TEMPLATE
|
54
|
+
end
|
55
|
+
|
56
|
+
def default_project_name
|
57
|
+
# https://www.rubydoc.info/docs/rails/Module#module_parent_name-instance_method
|
58
|
+
module_parent_name = ->(cls) { cls.name =~ /::[^:]+\Z/ ? $`.freeze : nil }
|
59
|
+
|
60
|
+
# Lazy-evaluate this so that Rails.application will be defined.
|
61
|
+
# If this code runs too early in the lifecycle, Rails.application is nil.
|
62
|
+
if defined?(::Rails)
|
63
|
+
[module_parent_name.(::Rails.application.class).humanize.titleize, "API"].join(" ")
|
64
|
+
else
|
65
|
+
"MyProject API"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rdoc'
|
2
|
+
require 'reverse_markdown'
|
3
|
+
|
4
|
+
module AppMap
|
5
|
+
module Swagger
|
6
|
+
# Transform description fields into Markdown.
|
7
|
+
class MarkdownDescriptions
|
8
|
+
def initialize(swagger_yaml)
|
9
|
+
@swagger_yaml = swagger_yaml
|
10
|
+
end
|
11
|
+
|
12
|
+
def converter
|
13
|
+
method(:rdoc_to_markdown)
|
14
|
+
end
|
15
|
+
|
16
|
+
def perform
|
17
|
+
to_markdown = lambda do |obj|
|
18
|
+
return obj.each(&to_markdown) if obj.is_a?(Array)
|
19
|
+
return unless obj.is_a?(Hash)
|
20
|
+
|
21
|
+
description = obj['description']
|
22
|
+
obj['description'] = converter.(description) if description
|
23
|
+
|
24
|
+
obj.reject { |k,v| k == 'properties' }.each_value(&to_markdown)
|
25
|
+
|
26
|
+
obj
|
27
|
+
end
|
28
|
+
|
29
|
+
to_markdown.(Util.deep_dup(@swagger_yaml))
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def rdoc_to_markdown(comment)
|
35
|
+
# Strip tags
|
36
|
+
comment = comment.split("\n").reject { |line| line =~ /^\s*@/ }.join("\n")
|
37
|
+
converter = ::RDoc::Markup::ToHtml.new(::RDoc::Options.new)
|
38
|
+
html = converter.convert(comment).strip
|
39
|
+
::ReverseMarkdown.convert(html).strip
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'yaml'
|
3
|
+
require 'appmap/node_cli'
|
4
|
+
require 'appmap/swagger/markdown_descriptions'
|
5
|
+
require 'appmap/swagger/stable'
|
6
|
+
|
7
|
+
module AppMap
|
8
|
+
module Swagger
|
9
|
+
module RakeTasks
|
10
|
+
extend self
|
11
|
+
extend Rake::DSL
|
12
|
+
|
13
|
+
def configuration
|
14
|
+
AppMap.configuration
|
15
|
+
end
|
16
|
+
|
17
|
+
def define_tasks
|
18
|
+
generate_swagger = lambda do |t, args|
|
19
|
+
appmap_js = AppMap::NodeCLI.new(verbose: Rake.verbose == true)
|
20
|
+
|
21
|
+
FileUtils.mkdir_p configuration.swagger_config.output_dir
|
22
|
+
|
23
|
+
cmd = %w[swagger]
|
24
|
+
|
25
|
+
swagger_raw, = appmap_js.command(cmd)
|
26
|
+
|
27
|
+
gen_swagger = YAML.load(swagger_raw)
|
28
|
+
gen_swagger_full = AppMap::Swagger::MarkdownDescriptions.new(gen_swagger).perform
|
29
|
+
gen_swagger_stable = AppMap::Swagger::Stable.new(gen_swagger).perform
|
30
|
+
|
31
|
+
swagger = configuration.swagger_config.template.merge(gen_swagger_full)
|
32
|
+
File.write File.join(configuration.swagger_config.output_dir, 'openapi.yaml'), YAML.dump(swagger)
|
33
|
+
|
34
|
+
swagger = configuration.swagger_config.template.merge(gen_swagger_stable)
|
35
|
+
File.write File.join(configuration.swagger_config.output_dir, 'openapi_stable.yaml'), YAML.dump(swagger)
|
36
|
+
end
|
37
|
+
|
38
|
+
desc configuration.swagger_config.description
|
39
|
+
task :swagger, &generate_swagger
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|