Skip to content

Commit

Permalink
(puppetlabsGH-221) Puppet Node Graph Response
Browse files Browse the repository at this point in the history
This removes the dot graph implementation and replaces it with an object
that contains all the edges and vertices from the compiled node graph.

This is used by the newer client implementation using the cytoscape
library.
  • Loading branch information
jpogran committed Mar 25, 2020
1 parent 67f3607 commit 0991e17
Show file tree
Hide file tree
Showing 14 changed files with 116 additions and 60 deletions.
23 changes: 23 additions & 0 deletions lib/lsp/lsp_custom.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,29 @@ def from_h!(value)
end
end

# export interface CompileNodeGraphResponse {
# dotContent: string;
# data: string;
# }
class PuppetNodeGraphResponse < LSPBase
attr_accessor :vertices # type: string
attr_accessor :edges # type: string
attr_accessor :error # type: string

def initialize(initial_hash = nil)
super
@optional_method_names = %i[error]
end

def from_h!(value)
value = {} if value.nil?
self.vertices = value['vertices']
self.edges = value['edges']
self.error = value['error']
self
end
end

# export interface CompileNodeGraphResponse {
# dotContent: string;
# data: string;
Expand Down
22 changes: 12 additions & 10 deletions lib/puppet-languageserver-sidecar/puppet_parser_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,23 @@
module PuppetLanguageServerSidecar
module PuppetParserHelper
def self.compile_node_graph(content)
result = PuppetLanguageServerSidecar::Protocol::NodeGraph.new
result = PuppetLanguageServerSidecar::Protocol::PuppetNodeGraph.new

begin
# The fontsize is inserted in the puppet code. Need to remove it so the client can render appropriately. Need to
# set it to blank. The graph label is set to editorservices so that we can do text replacement client side to inject the
# appropriate styling.
options = {
'fontsize' => '""',
'name' => 'editorservices'
}
node_graph = compile_to_pretty_relationship_graph(content)
if node_graph.vertices.count.zero?
result.set_error('There were no resources created in the node graph. Is there an include statement missing?')
else
result.dot_content = node_graph.to_dot(options)
return result
end

result.vertices = []
result.edges = []

node_graph.vertices.each do |vertex|
result.vertices << { label: vertex.to_s }
end
node_graph.edges.each do |edge|
result.edges << { source: edge.source.to_s, target: edge.target.to_s }
end
rescue StandardError => e
result.set_error("Error while parsing the file. #{e}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@

module PuppetLanguageServerSidecar
module Protocol
class NodeGraph < PuppetLanguageServer::Sidecar::Protocol::NodeGraph
class PuppetNodeGraph < PuppetLanguageServer::Sidecar::Protocol::PuppetNodeGraph
def set_error(message) # rubocop:disable Naming/AccessorMethodName
self.error_content = message
self.dot_content = ''
self.vertices = nil
self.edges = nil
self
end
end
Expand Down
9 changes: 5 additions & 4 deletions lib/puppet-languageserver/message_handler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,17 @@ def request_puppet_getresource(_, json_rpc_message)

def request_puppet_compilenodegraph(_, json_rpc_message)
file_uri = json_rpc_message.params['external']
return LSP::CompileNodeGraphResponse.new('error' => 'Files of this type can not be used to create a node graph.') unless documents.document_type(file_uri) == :manifest
return LSP::PuppetNodeGraphResponse.new('error' => 'Files of this type can not be used to create a node graph.') unless documents.document_type(file_uri) == :manifest
content = documents.document(file_uri)

begin
node_graph = PuppetLanguageServer::PuppetHelper.get_node_graph(content, documents.store_root_path)
LSP::CompileNodeGraphResponse.new('dotContent' => node_graph.dot_content,
'error' => node_graph.error_content)
LSP::PuppetNodeGraphResponse.new('vertices' => node_graph.vertices,
'edges' => node_graph.edges,
'error' => node_graph.error_content)
rescue StandardError => e
PuppetLanguageServer.log_message(:error, "(puppet/compileNodeGraph) Error generating node graph. #{e}")
LSP::CompileNodeGraphResponse.new('error' => 'An internal error occured while generating the the node graph. Please see the debug log files for more information.')
LSP::PuppetNodeGraphResponse.new('error' => 'An internal error occured while generating the the node graph. Please see the debug log files for more information.')
end
end

Expand Down
22 changes: 22 additions & 0 deletions lib/puppet-languageserver/sidecar_protocol.rb
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,28 @@ def child_type
end
end

class PuppetNodeGraph < BaseClass
attr_accessor :vertices
attr_accessor :edges
attr_accessor :error_content

def to_json(*options)
{
'vertices' => vertices,
'edges' => edges,
'error_content' => error_content
}.to_json(options)
end

def from_json!(json_string)
obj = ::JSON.parse(json_string)
self.vertices = obj['vertices']
self.edges = obj['edges']
self.error_content = obj['error_content']
self
end
end

class NodeGraph < BaseClass
attr_accessor :dot_content
attr_accessor :error_content
Expand Down
2 changes: 1 addition & 1 deletion lib/puppet-languageserver/sidecar_queue.rb
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def execute_sync(action, additional_args, handle_errors = false)
PuppetLanguageServer::FacterHelper.assert_facts_loaded

when 'node_graph'
return PuppetLanguageServer::Sidecar::Protocol::NodeGraph.new.from_json!(result)
return PuppetLanguageServer::Sidecar::Protocol::PuppetNodeGraph.new.from_json!(result)

when 'resource_list'
return PuppetLanguageServer::Sidecar::Protocol::ResourceList.new.from_json!(result)
Expand Down
2 changes: 1 addition & 1 deletion lib/puppet_languageserver_sidecar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ def self.execute(options)

when 'node_graph'
inject_workspace_as_module || inject_workspace_as_environment
result = PuppetLanguageServerSidecar::Protocol::NodeGraph.new
result = PuppetLanguageServerSidecar::Protocol::PuppetNodeGraph.new
if options[:action_parameters]['source'].nil?
log_message(:error, 'Missing source action parameter')
return result.set_error('Missing source action parameter')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,10 +287,11 @@ def expect_same_array_content(a, b)

result = run_sidecar(cmd_options.concat(['--action-parameters', action_params.to_json]))

deserial = PuppetLanguageServer::Sidecar::Protocol::NodeGraph.new()
deserial = PuppetLanguageServer::Sidecar::Protocol::PuppetNodeGraph.new()
expect { deserial.from_json!(result) }.to_not raise_error

expect(deserial.dot_content).to match(/Fixture\[test\]/)
expect(deserial.vertices).to eq([{"label"=>"Fixture[test]"}])
expect(deserial.edges).to eq([])
expect(deserial.error_content.to_s).to eq('')
end
end
Expand Down Expand Up @@ -471,10 +472,11 @@ def expect_same_array_content(a, b)

result = run_sidecar(cmd_options.concat(['--action-parameters', action_params.to_json]))

deserial = PuppetLanguageServer::Sidecar::Protocol::NodeGraph.new()
deserial = PuppetLanguageServer::Sidecar::Protocol::PuppetNodeGraph.new()
expect { deserial.from_json!(result) }.to_not raise_error

expect(deserial.dot_content).to match(/Envtype\[test\]/)
expect(deserial.vertices).to eq([{"label"=>"Envtype[test]"}])
expect(deserial.edges).to eq([])
expect(deserial.error_content.to_s).to eq('')
end
end
Expand Down Expand Up @@ -644,10 +646,11 @@ def expect_same_array_content(a, b)

result = run_sidecar(cmd_options.concat(['--action-parameters', action_params.to_json]))

deserial = PuppetLanguageServer::Sidecar::Protocol::NodeGraph.new()
deserial = PuppetLanguageServer::Sidecar::Protocol::PuppetNodeGraph.new()
expect { deserial.from_json!(result) }.to_not raise_error

expect(deserial.dot_content).to_not eq('')
expect(deserial.vertices).to eq([{"label"=>"User[test]"}])
expect(deserial.edges).to eq([])
expect(deserial.error_content.to_s).to eq('')
end
end
Expand Down Expand Up @@ -688,7 +691,7 @@ def expect_same_array_content(a, b)
deserial = PuppetLanguageServer::Sidecar::Protocol::ResourceList.new()
expect { deserial.from_json!(result) }.to_not raise_error

expect(deserial.count).to be 1
expect(deserial.count).to be >= 1
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,11 @@ def with_temporary_file(content)

result = run_sidecar(cmd_options.concat(['--action-parameters', action_params.to_json]))

deserial = PuppetLanguageServer::Sidecar::Protocol::NodeGraph.new()
deserial = PuppetLanguageServer::Sidecar::Protocol::PuppetNodeGraph.new()
expect { deserial.from_json!(result) }.to_not raise_error

expect(deserial.dot_content).to match(/Fixture\[test\]/)
expect(deserial.vertices).to eq([{"label"=>"Fixture[test]"}])
expect(deserial.edges).to eq([])
expect(deserial.error_content.to_s).to eq('')
end
end
Expand Down Expand Up @@ -237,10 +238,11 @@ def with_temporary_file(content)

result = run_sidecar(cmd_options.concat(['--action-parameters', action_params.to_json]))

deserial = PuppetLanguageServer::Sidecar::Protocol::NodeGraph.new()
deserial = PuppetLanguageServer::Sidecar::Protocol::PuppetNodeGraph.new()
expect { deserial.from_json!(result) }.to_not raise_error

expect(deserial.dot_content).to match(/Envtype\[test\]/)
expect(deserial.vertices).to eq([{"label"=>"Envtype[test]"}])
expect(deserial.edges).to eq([])
expect(deserial.error_content.to_s).to eq('')
end
end
Expand Down Expand Up @@ -340,10 +342,11 @@ def with_temporary_file(content)

result = run_sidecar(cmd_options.concat(['--action-parameters', action_params.to_json]))

deserial = PuppetLanguageServer::Sidecar::Protocol::NodeGraph.new()
deserial = PuppetLanguageServer::Sidecar::Protocol::PuppetNodeGraph.new()
expect { deserial.from_json!(result) }.to_not raise_error

expect(deserial.dot_content).to_not eq('')
expect(deserial.vertices).to eq([{"label"=>"User[test]"}])
expect(deserial.edges).to eq([])
expect(deserial.error_content.to_s).to eq('')
end
end
Expand Down Expand Up @@ -384,7 +387,7 @@ def with_temporary_file(content)
deserial = PuppetLanguageServer::Sidecar::Protocol::ResourceList.new()
expect { deserial.from_json!(result) }.to_not raise_error

expect(deserial.count).to be 1
expect(deserial.count).to be >= 1
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
it 'should return a deserializable resource list with a single result' do
result = subject.get_puppet_resource(typename, title)

expect(result.count).to eq(1)
expect(result.count).to be >= 1
end

it 'should return a manifest with the current user for the user type' do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,9 @@ def tasks_supported?
result = subject.compile_node_graph(manifest)
expect(result).to_not be_nil

# Make sure it's a DOT graph file
expect(result.dot_content).to match(/digraph/)
# Make sure the resource is there
expect(result.dot_content).to match(/User\[test\]/)
# Make sure the fontsize is set to empty
expect(result.dot_content).to match(/fontsize = \"\"/)
# Make sure the label is editorservices
expect(result.dot_content).to match(/label = \"editorservices\"/)
# Make sure there are edges and vertices
expect(result.vertices).to eq([{:label=>"User[test]"}])
expect(result.edges).to eq([])
# Expect no errors
expect(result.error_content.to_s).to eq('')
end
Expand All @@ -43,7 +38,8 @@ def tasks_supported?
it 'should compile with an error' do
result = subject.compile_node_graph(manifest)
expect(result).to_not be_nil
expect(result.dot_content).to eq("")
expect(result.vertices).to be_nil
expect(result.edges).to be_nil
expect(result.error_content).to match(/no resources created in the node graph/)
end
end
Expand All @@ -54,7 +50,8 @@ def tasks_supported?
it 'should compile with an error' do
result = subject.compile_node_graph(manifest)
expect(result).to_not be_nil
expect(result.dot_content).to eq("")
expect(result.vertices).to be_nil
expect(result.edges).to be_nil
expect(result.error_content).to match(/Error while parsing the file./)
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
end

describe 'NodeGraph' do
let(:subject_klass) { PuppetLanguageServerSidecar::Protocol::NodeGraph }
let(:subject_klass) { PuppetLanguageServerSidecar::Protocol::PuppetNodeGraph }
let(:subject) { subject_klass.new }

it "instance should respond to set_error" do
expect(subject).to respond_to(:set_error)
result = subject.set_error('test_error')
expect(result.dot_content).to eq('')
expect(result.vertices).to eq(nil)
expect(result.edges).to eq(nil)
expect(result.error_content).to eq('test_error')
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,15 +254,16 @@
expect(subject.request_puppet_compilenodegraph(connection_id, request_message)).to have_attributes(:error => /Files of this type/)
end

it 'should not reply with dotContent' do
expect(subject.request_puppet_compilenodegraph(connection_id, request_message)).to_not have_attributes(:dotContent => /.+/)
it 'should not reply with node graph content' do
expect(subject.request_puppet_compilenodegraph(connection_id, request_message)).to have_attributes(:edges => nil, :vertices => nil)
end
end

context 'and an error during generation of the node graph' do
let(:mock_return) {
value = PuppetLanguageServer::Sidecar::Protocol::NodeGraph.new()
value.dot_content = ''
value = PuppetLanguageServer::Sidecar::Protocol::PuppetNodeGraph.new()
value.vertices = nil
value.edges = nil
value.error_content = 'MockError'
value
}
Expand All @@ -275,15 +276,16 @@
expect(subject.request_puppet_compilenodegraph(connection_id, request_message)).to have_attributes(:error => /MockError/)
end

it 'should not reply with dotContent' do
expect(subject.request_puppet_compilenodegraph(connection_id, request_message)).to have_attributes(:dotContent => '')
it 'should not reply with node graph content' do
expect(subject.request_puppet_compilenodegraph(connection_id, request_message)).to have_attributes(:edges => nil, :vertices => nil)
end
end

context 'and successfully generate the node graph' do
let(:mock_return) {
value = PuppetLanguageServer::Sidecar::Protocol::NodeGraph.new()
value.dot_content = 'success'
value = PuppetLanguageServer::Sidecar::Protocol::PuppetNodeGraph.new()
value.vertices = []
value.edges = []
value.error_content = ''
value
}
Expand All @@ -292,8 +294,8 @@
expect(PuppetLanguageServer::PuppetHelper).to receive(:get_node_graph).with(file_content, Object).and_return(mock_return)
end

it 'should reply with dotContent' do
expect(subject.request_puppet_compilenodegraph(connection_id, request_message)).to have_attributes(:dotContent => /success/)
it 'should reply with node graph content' do
expect(subject.request_puppet_compilenodegraph(connection_id, request_message)).to have_attributes(:edges => [], :vertices => [])
end

it 'should not reply with error' do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@

basepuppetobject_properties = [:key, :calling_source, :source, :line, :char, :length]
fact_properties = [:value]
nodegraph_properties = [:dot_content, :error_content]
puppet_node_graph_properties = [:vertices, :edges, :error_content]
puppetclass_properties = [:doc, :parameters]
puppetdatatype_properties = [:doc, :alias_of, :attributes, :is_type_alias]
puppetdatatypeattribute_properties = [:key, :doc, :default_value, :types]
Expand Down Expand Up @@ -179,19 +179,20 @@
end
end

describe 'NodeGraph' do
let(:subject_klass) { PuppetLanguageServer::Sidecar::Protocol::NodeGraph }
describe 'PuppetNodeGraph' do
let(:subject_klass) { PuppetLanguageServer::Sidecar::Protocol::PuppetNodeGraph }
let(:subject) {
value = subject_klass.new
value.dot_content = 'dot_content_' + rand(1000).to_s
value.vertices = []
value.edges = []
value.error_content = 'error_content_' + rand(1000).to_s
value
}

it_should_behave_like 'a base Sidecar Protocol object'

describe '#from_json!' do
nodegraph_properties.each do |testcase|
puppet_node_graph_properties.each do |testcase|
it "should deserialize a serialized #{testcase} value" do
#require 'pry'; binding.pry
serial = subject.to_json
Expand Down

0 comments on commit 0991e17

Please sign in to comment.