appmap 0.52.1 → 0.54.3

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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +16 -10
  3. data/CHANGELOG.md +41 -0
  4. data/Rakefile +13 -4
  5. data/appmap.gemspec +1 -1
  6. data/exe/appmap-agent-init +19 -0
  7. data/lib/appmap.rb +31 -112
  8. data/lib/appmap/agent.rb +115 -0
  9. data/lib/appmap/class_map.rb +2 -2
  10. data/lib/appmap/config.rb +11 -3
  11. data/lib/appmap/handler/net_http.rb +3 -2
  12. data/lib/appmap/handler/rails/request_handler.rb +2 -1
  13. data/lib/appmap/handler/rails/template.rb +2 -1
  14. data/lib/appmap/hook.rb +0 -1
  15. data/lib/appmap/hook/method.rb +36 -28
  16. data/lib/appmap/metadata.rb +4 -2
  17. data/lib/appmap/minitest.rb +2 -0
  18. data/lib/appmap/railtie.rb +2 -2
  19. data/lib/appmap/rspec.rb +5 -3
  20. data/lib/appmap/swagger.rb +2 -0
  21. data/lib/appmap/swagger/configuration.rb +70 -0
  22. data/lib/appmap/swagger/markdown_descriptions.rb +43 -0
  23. data/lib/appmap/swagger/rake_tasks.rb +43 -0
  24. data/lib/appmap/swagger/stable.rb +37 -0
  25. data/lib/appmap/trace.rb +2 -0
  26. data/lib/appmap/util.rb +17 -1
  27. data/lib/appmap/version.rb +1 -1
  28. data/package.json +0 -1
  29. data/release.sh +0 -1
  30. data/spec/config_spec.rb +0 -1
  31. data/spec/fixtures/hook/.gitignore +2 -0
  32. data/spec/fixtures/hook/app/controllers/api/api_keys_controller.rb +1 -0
  33. data/spec/fixtures/hook/app/controllers/organizations_controller.rb +1 -0
  34. data/spec/fixtures/hook/app/models/api_key.rb +1 -0
  35. data/spec/fixtures/hook/app/models/configuration.rb +1 -0
  36. data/spec/fixtures/hook/app/models/show.rb +1 -0
  37. data/spec/fixtures/hook/app/models/user.rb +1 -0
  38. data/spec/fixtures/hook/kwargs.rb +11 -0
  39. data/spec/fixtures/hook/revoke_api_key.appmap.json +847 -0
  40. data/spec/fixtures/hook/spec/api_spec.rb +1 -0
  41. data/spec/fixtures/hook/spec/user_spec.rb +1 -0
  42. data/spec/fixtures/hook/user_page_scenario.appmap.json +1722 -0
  43. data/spec/fixtures/rails5_users_app/config/environments/test.rb +3 -0
  44. data/spec/fixtures/rails6_users_app/Dockerfile +0 -1
  45. data/spec/fixtures/rails6_users_app/appmap.yml +3 -1
  46. data/spec/fixtures/rails6_users_app/config/environments/test.rb +3 -0
  47. data/spec/fixtures/rails6_users_app/lib/tasks/appmap.rake +13 -0
  48. data/spec/hook_spec.rb +9 -0
  49. data/spec/rails_recording_spec.rb +1 -1
  50. data/spec/record_net_http_spec.rb +1 -0
  51. data/spec/swagger/swagger_spec.rb +47 -0
  52. data/test/{agent_setup_cli_test.rb → agent_setup_init_test.rb} +4 -4
  53. data/test/gem_test.rb +1 -0
  54. metadata +39 -19
  55. data/exe/appmap-agent-setup +0 -47
@@ -111,7 +111,7 @@ module AppMap
111
111
  end
112
112
 
113
113
  comment = method.comment
114
- function_info[:comment] = comment unless comment.blank?
114
+ function_info[:comment] = comment unless Util.blank?(comment)
115
115
 
116
116
  function_info[:labels] = parse_labels(comment) + (method.labels || [])
117
117
  object_infos << function_info
@@ -119,7 +119,7 @@ module AppMap
119
119
  parent = root
120
120
  object_infos.each do |info|
121
121
  parent = find_or_create parent.children, info do
122
- Types.const_get(info[:type].classify).new(info[:name].to_s).tap do |type|
122
+ Types.const_get(Util.classify(info[:type])).new(info[:name].to_s).tap do |type|
123
123
  info.keys.tap do |keys|
124
124
  keys.delete(:name)
125
125
  keys.delete(:type)
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: exclude.blank? ? nil : exclude,
85
- labels: labels.blank? ? nil : 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 params.blank?
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', k.underscore.upcase ].join('_')
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 params.blank?
44
+ unless Util.blank?(params)
44
45
  h[:message] = params.keys.map do |key|
45
46
  val = params[key]
46
47
  {
@@ -146,7 +146,8 @@ module AppMap
146
146
  class RenderHandler
147
147
  class << self
148
148
  def handle_call(defined_class, hook_method, receiver, args)
149
- context, options = args
149
+ # context, options
150
+ _, options = args
150
151
 
151
152
  warn "Renderer: #{options}" if LOG
152
153
 
data/lib/appmap/hook.rb CHANGED
@@ -99,7 +99,6 @@ module AppMap
99
99
  end
100
100
 
101
101
  def trace_end(trace_point)
102
- location = trace_location(trace_point)
103
102
  warn "Class or module ends at location #{trace_location(trace_point)}" if Hook::LOG || Hook::LOG_HOOK
104
103
 
105
104
  path = trace_point.path
@@ -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 = nil
49
- hook_class.instance_eval do
50
- hook_method_def = Proc.new do |*args, &block|
51
- instance_method = hook_method.bind(self).to_proc
52
- call_instance_method = -> { instance_method.call(*args, &block) }
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
- # We may not have gotten the class for the method during
55
- # initialization (e.g. for a singleton method on an embedded
56
- # struct), so make sure we have it now.
57
- defined_class, = Hook.qualify_method_name(hook_method) unless defined_class
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
- reentrant = Thread.current[HOOK_DISABLE_KEY]
60
- disabled_by_shallow_flag = \
61
- -> { hook_package&.shallow? && AppMap.tracing.last_package_for_current_thread == hook_package }
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
- enabled = true if AppMap.tracing.enabled? && !reentrant && !disabled_by_shallow_flag.call
70
+ enabled = true if AppMap.tracing.enabled? && !reentrant && !disabled_by_shallow_flag.call
64
71
 
65
- return call_instance_method.call unless enabled
72
+ return call_instance_method.call unless enabled
66
73
 
67
- call_event, start_time = with_disabled_hook.call do
68
- before_hook.call(self, defined_class, args)
69
- end
70
- return_value = nil
71
- exception = nil
72
- begin
73
- return_value = call_instance_method.call
74
- rescue
75
- exception = $ERROR_INFO
76
- raise
77
- ensure
78
- with_disabled_hook.call do
79
- after_hook.call(self, call_event, start_time, return_value, exception) if call_event
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
 
@@ -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 git_last_annotated_tag.blank?
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 git_last_tag.blank?
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
 
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'appmap/util'
4
+ require 'active_support'
5
+ require 'active_support/core_ext'
4
6
 
5
7
  module AppMap
6
8
  # Integration of AppMap with Minitest. When enabled with APPMAP=true, the AppMap tracer will
@@ -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.insert_after \
9
- Rails::Rack::Logger,
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
- else
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?).reject!(&:blank?)
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,2 @@
1
+ require 'appmap/swagger/configuration'
2
+ require 'appmap/swagger/rake_tasks'
@@ -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