appmap 0.51.2 → 0.54.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.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -1
  3. data/CHANGELOG.md +40 -0
  4. data/Rakefile +21 -5
  5. data/appmap.gemspec +8 -7
  6. data/exe/appmap-agent-init +19 -0
  7. data/exe/appmap-inspect +7 -0
  8. data/lib/appmap.rb +29 -119
  9. data/lib/appmap/agent.rb +115 -0
  10. data/lib/appmap/class_map.rb +2 -2
  11. data/lib/appmap/command/agent_setup/init.rb +44 -0
  12. data/lib/appmap/command/inspect.rb +27 -0
  13. data/lib/appmap/command_error.rb +13 -0
  14. data/lib/appmap/config.rb +13 -4
  15. data/lib/appmap/handler/net_http.rb +2 -1
  16. data/lib/appmap/handler/rails/request_handler.rb +2 -1
  17. data/lib/appmap/metadata.rb +4 -2
  18. data/lib/appmap/minitest.rb +2 -0
  19. data/lib/appmap/node_cli.rb +59 -0
  20. data/lib/appmap/railtie.rb +2 -2
  21. data/lib/appmap/rspec.rb +5 -3
  22. data/lib/appmap/service/guesser.rb +2 -9
  23. data/lib/appmap/swagger.rb +2 -0
  24. data/lib/appmap/swagger/configuration.rb +70 -0
  25. data/lib/appmap/swagger/markdown_descriptions.rb +43 -0
  26. data/lib/appmap/swagger/rake_tasks.rb +43 -0
  27. data/lib/appmap/swagger/stable.rb +37 -0
  28. data/lib/appmap/util.rb +31 -2
  29. data/lib/appmap/version.rb +1 -1
  30. data/package.json +5 -7
  31. data/spec/config_spec.rb +0 -1
  32. data/spec/fixtures/hook/.gitignore +2 -0
  33. data/spec/fixtures/hook/app/controllers/api/api_keys_controller.rb +1 -0
  34. data/spec/fixtures/hook/app/controllers/organizations_controller.rb +1 -0
  35. data/spec/fixtures/hook/app/models/api_key.rb +1 -0
  36. data/spec/fixtures/hook/app/models/configuration.rb +1 -0
  37. data/spec/fixtures/hook/app/models/show.rb +1 -0
  38. data/spec/fixtures/hook/app/models/user.rb +1 -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/rails5_users_app/docker-compose.yml +1 -1
  45. data/spec/fixtures/rails6_users_app/Dockerfile +9 -1
  46. data/spec/fixtures/rails6_users_app/appmap.yml +3 -1
  47. data/spec/fixtures/rails6_users_app/config/environments/test.rb +3 -0
  48. data/spec/fixtures/rails6_users_app/docker-compose.yml +1 -1
  49. data/spec/fixtures/rails6_users_app/lib/tasks/appmap.rake +13 -0
  50. data/spec/{abstract_controller_base_spec.rb → rails_recording_spec.rb} +2 -21
  51. data/spec/rails_spec_helper.rb +22 -0
  52. data/spec/swagger/swagger_spec.rb +47 -0
  53. data/test/{cli_test.rb → agent_setup_init_test.rb} +4 -4
  54. data/test/fixtures/gem_test/Gemfile +1 -0
  55. data/test/gem_test.rb +1 -0
  56. data/test/inspect_cli_test.rb +12 -0
  57. data/yarn.lock +477 -0
  58. metadata +39 -63
  59. data/exe/appmap-agent-setup +0 -47
  60. data/lib/appmap/algorithm/prune_class_map.rb +0 -67
  61. data/lib/appmap/algorithm/stats.rb +0 -91
  62. data/lib/appmap/command/init.rb +0 -42
  63. data/lib/appmap/command/record.rb +0 -38
  64. data/lib/appmap/command/stats.rb +0 -14
  65. data/lore/pages/2019-05-21-install-and-record/index.pug +0 -51
  66. data/lore/pages/2019-05-21-install-and-record/install_example_appmap.png +0 -0
  67. data/lore/pages/2019-05-21-install-and-record/metadata.yml +0 -5
  68. data/lore/pages/layout.pug +0 -66
  69. data/lore/public/lib/bootstrap-4.1.3/css/bootstrap-grid.css +0 -1912
  70. data/lore/public/lib/bootstrap-4.1.3/css/bootstrap-grid.css.map +0 -1
  71. data/lore/public/lib/bootstrap-4.1.3/css/bootstrap-grid.min.css +0 -7
  72. data/lore/public/lib/bootstrap-4.1.3/css/bootstrap-grid.min.css.map +0 -1
  73. data/lore/public/lib/bootstrap-4.1.3/css/bootstrap-reboot.css +0 -331
  74. data/lore/public/lib/bootstrap-4.1.3/css/bootstrap-reboot.css.map +0 -1
  75. data/lore/public/lib/bootstrap-4.1.3/css/bootstrap-reboot.min.css +0 -8
  76. data/lore/public/lib/bootstrap-4.1.3/css/bootstrap-reboot.min.css.map +0 -1
  77. data/lore/public/lib/bootstrap-4.1.3/css/bootstrap.css +0 -9030
  78. data/lore/public/lib/bootstrap-4.1.3/css/bootstrap.css.map +0 -1
  79. data/lore/public/lib/bootstrap-4.1.3/css/bootstrap.min.css +0 -7
  80. data/lore/public/lib/bootstrap-4.1.3/css/bootstrap.min.css.map +0 -1
  81. data/lore/public/stylesheets/style.css +0 -8
  82. data/package-lock.json +0 -1064
@@ -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)
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'appmap/service/guesser'
5
+ require 'appmap/util'
6
+
7
+ module AppMap
8
+ module Command
9
+ module AgentSetup
10
+ InitStruct = Struct.new(:config_file)
11
+
12
+ class Init < InitStruct
13
+ def perform
14
+ if File.exist?(config_file)
15
+ puts AppMap::Util.color(%(The AppMap config file #{config_file} already exists.), :magenta)
16
+ return
17
+ end
18
+
19
+ ensure_directory_exists
20
+
21
+ config = {
22
+ 'name' => Service::Guesser.guess_name,
23
+ 'packages' => Service::Guesser.guess_paths.map { |path| { 'path' => path } }
24
+ }
25
+ content = YAML.dump(config).gsub("---\n", '')
26
+
27
+ File.write(config_file, content)
28
+ puts AppMap::Util.color(
29
+ %(The following AppMap config file #{config_file} has been created:),
30
+ :green
31
+ )
32
+ puts content
33
+ end
34
+
35
+ private
36
+
37
+ def ensure_directory_exists
38
+ dirname = File.dirname(config_file)
39
+ FileUtils.mkdir_p(dirname) unless File.directory?(dirname)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,27 @@
1
+ require 'appmap/version'
2
+ require 'appmap/config'
3
+ require 'appmap/node_cli'
4
+
5
+ module AppMap
6
+ module Command
7
+ class Inspect < AppMap::NodeCLI
8
+ class << self
9
+ def run
10
+ command = Inspect.new(verbose: ENV['DEBUG'] == 'true')
11
+ command.inspect(ARGV)
12
+ end
13
+ end
14
+
15
+ def inspect(arguments)
16
+ detect_nodejs
17
+ index_appmaps
18
+
19
+ arguments.unshift 'inspect'
20
+ arguments.unshift APPMAP_JS
21
+ arguments.unshift 'node'
22
+
23
+ exec(*arguments)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ module AppMap
2
+ # Raised when a system / shell command fails.
3
+ class CommandError < StandardError
4
+ attr_reader :command, :msg
5
+
6
+ def initialize(command, msg = nil)
7
+ super [ "Command failed: #{command}", msg ].compact.join('; ')
8
+
9
+ @command = command
10
+ @msg = msg
11
+ end
12
+ end
13
+ end
data/lib/appmap/config.rb CHANGED
@@ -1,9 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'yaml'
4
+ require 'appmap/util'
4
5
  require 'appmap/handler/net_http'
5
6
  require 'appmap/handler/rails/template'
6
7
  require 'appmap/service/guesser'
8
+ require 'appmap/swagger/configuration'
7
9
 
8
10
  module AppMap
9
11
  class Config
@@ -54,7 +56,7 @@ module AppMap
54
56
  if path
55
57
  Package.new(path, gem, package_name, exclude, labels, shallow)
56
58
  else
57
- warn "#{gem} is not available in the bundle" if AppMap::Hook::LOG
59
+ AppMap::Util.startup_message "#{gem} is not available in the bundle"
58
60
  end
59
61
  end
60
62
 
@@ -80,8 +82,8 @@ module AppMap
80
82
  package_name: package_name,
81
83
  gem: gem,
82
84
  handler_class: handler_class.name,
83
- exclude: exclude.blank? ? nil : exclude,
84
- labels: labels.blank? ? nil : labels,
85
+ exclude: Util.blank?(exclude) ? nil : exclude,
86
+ labels: Util.blank?(labels) ? nil : labels,
85
87
  shallow: shallow
86
88
  }.compact
87
89
  end
@@ -225,15 +227,17 @@ module AppMap
225
227
  'JSON::Ext::Generator::State' => TargetMethods.new(:generate, Package.build_from_path('json', package_name: 'json', labels: %w[format.json.generate])),
226
228
  }.freeze
227
229
 
228
- 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
229
231
 
230
232
  def initialize(name,
231
233
  packages: [],
234
+ swagger_config: Swagger::Configuration.new,
232
235
  exclude: [],
233
236
  functions: [])
234
237
  @name = name
235
238
  @appmap_dir = AppMap::DEFAULT_APPMAP_DIR
236
239
  @packages = packages
240
+ @swagger_config = swagger_config
237
241
  @hook_paths = Set.new(packages.map(&:path))
238
242
  @exclude = exclude
239
243
  @builtin_hooks = BUILTIN_HOOKS
@@ -350,6 +354,11 @@ module AppMap
350
354
  end
351
355
  end
352
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
+
353
362
  Config.new name, config_params
354
363
  end
355
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
  {
@@ -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
  {
@@ -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
@@ -0,0 +1,59 @@
1
+ require 'open3'
2
+ require 'shellwords'
3
+ require 'appmap/util'
4
+ require 'appmap/command_error'
5
+
6
+ module AppMap
7
+ # Utilities for invoking the +@appland/appmap+ CLI.
8
+ class NodeCLI
9
+ APPMAP_JS = Pathname.new(__dir__).join('../../node_modules/@appland/cli/src/cli.js').expand_path.to_s
10
+
11
+ attr_reader :verbose
12
+ # Directory to scan for AppMaps.
13
+ attr_accessor :appmap_dir
14
+
15
+ def initialize(verbose: false, appmap_dir: AppMap::DEFAULT_APPMAP_DIR)
16
+ @verbose = verbose
17
+ @appmap_dir = appmap_dir
18
+
19
+ detect_nodejs
20
+ end
21
+
22
+ def detect_nodejs
23
+ do_fail('node', 'please install NodeJS') unless system('node --version 2>&1 > /dev/null')
24
+ true
25
+ end
26
+
27
+ def index_appmaps
28
+ command [ 'index', '--appmap-dir', appmap_dir ]
29
+ true
30
+ end
31
+
32
+ def command(command, options = {})
33
+ command.unshift << '--verbose' if verbose
34
+ command.unshift APPMAP_JS
35
+ command.unshift 'node'
36
+
37
+ warn command.join(' ') if verbose
38
+ stdout, stderr, status = Open3.capture3({ 'NODE_OPTIONS' => '--trace-warnings' }, *command.map(&:shellescape), options)
39
+ stdout_msg = stdout.split("\n").map {|line| "stdout: #{line}"}.join("\n") unless Util.blank?(stdout)
40
+ stderr_msg = stderr.split("\n").map {|line| "stderr: #{line}"}.join("\n") unless Util.blank?(stderr)
41
+ if verbose
42
+ warn stdout_msg if stdout_msg
43
+ warn stderr_msg if stderr_msg
44
+ end
45
+ unless status.exitstatus == 0
46
+ raise CommandError.new(command, [ stdout_msg, stderr_msg ].compact.join("\n"))
47
+ end
48
+ [ stdout, stderr ]
49
+ end
50
+
51
+ protected
52
+
53
+ def do_fail(command, msg)
54
+ command = command.join(' ') if command.is_a?(Array)
55
+ warn [ command, msg ].join('; ') if verbose
56
+ raise CommandError.new(command, msg)
57
+ end
58
+ end
59
+ end
@@ -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
 
@@ -6,15 +6,8 @@ module AppMap
6
6
  POSSIBLE_PATHS = %w[app/controllers app/models lib]
7
7
  class << self
8
8
  def guess_name
9
- reponame = lambda do
10
- next unless File.directory?('.git')
11
-
12
- repo_name = `git config --get remote.origin.url`.strip
13
- repo_name.split('/').last.split('.').first unless repo_name == ''
14
- end
15
- dirname = -> { Dir.pwd.split('/').last }
16
-
17
- reponame.() || dirname.()
9
+ return Pathname.new(`git rev-parse --show-toplevel`.strip).basename.to_s if File.directory?('.git')
10
+ Dir.pwd.split('/').last
18
11
  end
19
12
 
20
13
  def guess_paths
@@ -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