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 06588f9
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
@@ -55,6 +55,29 @@ def from_h!(value)
end
end

# export interface PuppetNodeGraphResponse {
# 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;
22 changes: 12 additions & 10 deletions lib/puppet-languageserver-sidecar/puppet_parser_helper.rb
Original file line number Diff line number Diff line change
@@ -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}")
Original file line number Diff line number Diff line change
@@ -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
9 changes: 5 additions & 4 deletions lib/puppet-languageserver/message_handler.rb
Original file line number Diff line number Diff line change
@@ -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

22 changes: 22 additions & 0 deletions lib/puppet-languageserver/sidecar_protocol.rb
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion lib/puppet-languageserver/sidecar_queue.rb
Original file line number Diff line number Diff line change
@@ -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)
2 changes: 1 addition & 1 deletion lib/puppet_languageserver_sidecar.rb
Original file line number Diff line number Diff line change
@@ -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')
Original file line number Diff line number Diff line change
@@ -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
@@ -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
@@ -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
@@ -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
Original file line number Diff line number Diff line change
@@ -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
@@ -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
@@ -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
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
@@ -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
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
}
@@ -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
}
@@ -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
Original file line number Diff line number Diff line change
@@ -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]
@@ -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

0 comments on commit 06588f9

Please sign in to comment.