workato-connector-builder 0.0.1.pre
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.
- checksums.yaml +7 -0
- data/bin/workato-connector-builder +5 -0
- data/lib/output.rb +14 -0
- data/lib/test.rb +263 -0
- data/lib/workato_connector_builder/ast_generator.rb +57 -0
- data/lib/workato_connector_builder/cli/build_command.rb +139 -0
- data/lib/workato_connector_builder/cli/cli.rb +38 -0
- data/lib/workato_connector_builder/cli/config_store.rb +17 -0
- data/lib/workato_connector_builder/constants.rb +76 -0
- data/lib/workato_connector_builder/fix_newlines_rewriter.rb +29 -0
- data/lib/workato_connector_builder/hash_combiner.rb +116 -0
- data/lib/workato_connector_builder/validator.rb +138 -0
- data/lib/workato_connector_builder.rb +4 -0
- data/workato-connector-builder.gemspec +27 -0
- metadata +127 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 23aae47128ed80aee07d7f2e12e811c2632b6b1f30137a24774b4f571415f7eb
|
4
|
+
data.tar.gz: 7d0fd63fc1d1d3274ea6036ea2d4569f59856196b51e0050eaf75ddf740e7711
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e13f417404ee807284ef4deb3eb5ba02884f3378701b934f85113896629ff440f0fcb4f0e143adf58d8c002ac40c29f106b305b2fda434e8a8659bad8992155f
|
7
|
+
data.tar.gz: d04fe57dcaa85de89dff3961e1fa5ad41b88f3cbb81efb2fa2ec451169bde49fb04a0544c15cfd62508fb9c48d6a4ed0900c3bf29d90b65781fa864e1353f333
|
data/lib/output.rb
ADDED
data/lib/test.rb
ADDED
@@ -0,0 +1,263 @@
|
|
1
|
+
require 'parser/current'
|
2
|
+
require 'unparser'
|
3
|
+
|
4
|
+
# Emitter
|
5
|
+
#
|
6
|
+
#
|
7
|
+
#
|
8
|
+
# # level 2 will get us the hash keys we care about in the files like :action, etc
|
9
|
+
# def look_for_type(ast, type, value_to_match, levels_to_traverse = 2)
|
10
|
+
# return unless Parser::AST::Node === ast
|
11
|
+
#
|
12
|
+
# if ast.type == type # when we found block
|
13
|
+
# ast.children[0] == value_to_match
|
14
|
+
# else
|
15
|
+
# if levels_to_traverse >= 0
|
16
|
+
# match =
|
17
|
+
# ast.children.select do |child|
|
18
|
+
# # otherwise let's look for blocks
|
19
|
+
# # fail early, we don't want to traverse the whole tree
|
20
|
+
# match = look_for_type(child, type, value_to_match, levels_to_traverse - 1)
|
21
|
+
# if match
|
22
|
+
# break
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
# return match
|
26
|
+
# else
|
27
|
+
# return false
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# def get_children_for_match(ast, type, value_to_match, levels_to_traverse = 2)
|
33
|
+
#
|
34
|
+
# #
|
35
|
+
# # if ast.children.any? {|child| child.type == type && child.children[0] == value_to_match}# when we found block
|
36
|
+
# # ast.children
|
37
|
+
# # else
|
38
|
+
# # if levels_to_traverse > 0
|
39
|
+
# # match = ast.children.select do |child|
|
40
|
+
# # # otherwise let's look for blocks
|
41
|
+
# # # fail early, we don't want to traverse the whole tree
|
42
|
+
# # match = get_children_for_match(child, type, value_to_match, levels_to_traverse - 1)
|
43
|
+
# # if match
|
44
|
+
# # break
|
45
|
+
# # end
|
46
|
+
# # end
|
47
|
+
# # return match
|
48
|
+
# # else
|
49
|
+
# # return []
|
50
|
+
# # end
|
51
|
+
# # end
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# # combine the asts that are the same
|
55
|
+
# $workato_hash_roots = {
|
56
|
+
# :title => [],
|
57
|
+
# :connection => [],
|
58
|
+
# :actions => [],
|
59
|
+
# :methods => [],
|
60
|
+
# :object_definitions => [],
|
61
|
+
# :pick_lists => [],
|
62
|
+
# :test => [],
|
63
|
+
# :triggers => [],
|
64
|
+
# }
|
65
|
+
|
66
|
+
require_relative 'workato_connector_builder/ast_generator'
|
67
|
+
require_relative 'workato_connector_builder/hash_combiner'
|
68
|
+
require 'rubocop-ast'
|
69
|
+
include WorkatoConnectorBuilder
|
70
|
+
|
71
|
+
# combine the source files
|
72
|
+
builder = RuboCop::AST::Builder.new
|
73
|
+
parser = Parser::CurrentRuby.new builder
|
74
|
+
|
75
|
+
buffer1 = Parser::Source::Buffer.new('test1')
|
76
|
+
buffer1.raw_source = %{{
|
77
|
+
:test => 1,
|
78
|
+
:collision => 1,
|
79
|
+
'dup' => {
|
80
|
+
a: 1,
|
81
|
+
b: 2
|
82
|
+
}
|
83
|
+
}}
|
84
|
+
buffer2 = Parser::Source::Buffer.new('test2')
|
85
|
+
# buffer2.raw_source = '{ :otherfield => 2, :collision => 2, dup: { # some comment \n c: 3}}'
|
86
|
+
buffer2.raw_source = %{{
|
87
|
+
:otherfield => 2,
|
88
|
+
:collision => 2,
|
89
|
+
dup: {
|
90
|
+
# some comment
|
91
|
+
a: 2,
|
92
|
+
c: 3 # another_comment
|
93
|
+
}
|
94
|
+
}}
|
95
|
+
|
96
|
+
require 'thor'
|
97
|
+
combine_hashes = WorkatoConnectorBuilder::Utils::HashCombiner.new Thor::Shell::Color.new
|
98
|
+
|
99
|
+
treeRewriter = Parser::Source::TreeRewriter.new buffer1
|
100
|
+
|
101
|
+
node1 = parser.parse(buffer1)
|
102
|
+
parser = Parser::CurrentRuby.new builder
|
103
|
+
parser.reset
|
104
|
+
node2 = parser.parse(buffer2)
|
105
|
+
|
106
|
+
# class MyRule < Parser::AST::Processor
|
107
|
+
# include RuboCop::AST::Traversal
|
108
|
+
#
|
109
|
+
# def on_sym(node)
|
110
|
+
# puts "I found a symbol! #{node.value}"
|
111
|
+
# end
|
112
|
+
# end
|
113
|
+
#
|
114
|
+
#
|
115
|
+
|
116
|
+
combine_hashes.combine node1, node2, treeRewriter
|
117
|
+
#
|
118
|
+
# source = RuboCop::ProcessedSource.new(Unparser.unparse(node3), 2.7)
|
119
|
+
# rule = MyRule.new
|
120
|
+
# source.ast.each_node { |n| rule.process(n) }
|
121
|
+
|
122
|
+
File.write('./output.rb', treeRewriter.process, mode: 'w+')
|
123
|
+
|
124
|
+
# puts "Correcting output file"
|
125
|
+
# Loutput = %x(rubocop --auto-correct -x ./output.rb)
|
126
|
+
# asts = walk("../", %w[bin output test])
|
127
|
+
#
|
128
|
+
#
|
129
|
+
#
|
130
|
+
# split_asts =
|
131
|
+
# asts.map { |ast|
|
132
|
+
# ast.children.map { |child|
|
133
|
+
# children = child.children
|
134
|
+
# # first element is going to be the symbol for the hash, contained in this nested structure
|
135
|
+
# symbol = children[0].children[0]
|
136
|
+
# WORKATO_HASH_ROOTS[symbol].append children[1]
|
137
|
+
# }
|
138
|
+
# }
|
139
|
+
#
|
140
|
+
# def unparse(node, comment_array = [])
|
141
|
+
# return '' if node.nil?
|
142
|
+
#
|
143
|
+
# Unparser::Buffer.new.tap do |buffer|
|
144
|
+
# Emitter.new(
|
145
|
+
# buffer,
|
146
|
+
# node,
|
147
|
+
# Unparser::Comments.new(comment_array)
|
148
|
+
# ).write_to_buffer
|
149
|
+
# end.content
|
150
|
+
# end
|
151
|
+
#
|
152
|
+
# combined_asts =
|
153
|
+
# WORKATO_HASH_ROOTS.map { |symbol, matching_sets|
|
154
|
+
# combined_hashes = matching_sets[0]
|
155
|
+
# matching_sets.drop(1).each { |set|
|
156
|
+
# combined_hashes = combined_hashes.concat set
|
157
|
+
# }
|
158
|
+
#
|
159
|
+
# s = Unparser.unparse(combined_hashes)
|
160
|
+
#
|
161
|
+
# hash = "{#{symbol}: #{s}}"
|
162
|
+
# Unparser.parse(hash)
|
163
|
+
# }
|
164
|
+
#
|
165
|
+
# final_ast = Unparser.parse('{}')
|
166
|
+
# combined_asts.each { |node|
|
167
|
+
# final_ast = final_ast.concat node
|
168
|
+
# }
|
169
|
+
# OUTPUT_PATH = 'output/core_connector.rb'
|
170
|
+
# FULL_OUTPUT_FILE_PATH = File.join(File.dirname(__FILE__), '../', OUTPUT_PATH)
|
171
|
+
#
|
172
|
+
# output = Unparser.unparse(final_ast)
|
173
|
+
# File.write(FULL_OUTPUT_FILE_PATH, output, nil, mode: 'w+')
|
174
|
+
#
|
175
|
+
# Dir.chdir('../') {
|
176
|
+
# puts %x(ls -ahl)
|
177
|
+
# value = %x(rubocop ./#{OUTPUT_PATH} --auto-correct -x - v)
|
178
|
+
# puts value
|
179
|
+
# }
|
180
|
+
# # command = Thread.new do
|
181
|
+
# # end
|
182
|
+
# # command.join
|
183
|
+
#
|
184
|
+
# # result = Array (79 elements)
|
185
|
+
# # [0] = :location
|
186
|
+
# # [1] = :assign_properties
|
187
|
+
# # [2] = :loc
|
188
|
+
# # [3] = :dup
|
189
|
+
# # [4] = :clone
|
190
|
+
# # [5] = :to_sexp
|
191
|
+
# # [6] = :updated
|
192
|
+
# # [7] = :fancy_type
|
193
|
+
# # [8] = :to_sexp_array
|
194
|
+
# # [9] = :<<
|
195
|
+
# # [10] = :to_a
|
196
|
+
# # [11] = :==
|
197
|
+
# # [12] = :to_s
|
198
|
+
# # [13] = :children
|
199
|
+
# # [14] = :eql?
|
200
|
+
# # [15] = :deconstruct
|
201
|
+
# # [16] = :+
|
202
|
+
# # [17] = :inspect
|
203
|
+
# # [18] = :append
|
204
|
+
# # [19] = :hash
|
205
|
+
# # [20] = :concat
|
206
|
+
# # [21] = :type
|
207
|
+
# # [22] = :to_ast
|
208
|
+
# # [23] = :pretty_print_inspect
|
209
|
+
# # [24] = :pretty_print_instance_variables
|
210
|
+
# # [25] = :pretty_print
|
211
|
+
# # [26] = :pretty_print_cycle
|
212
|
+
# # [27] = :itself
|
213
|
+
# # [28] = :yield_self
|
214
|
+
# # [29] = :then
|
215
|
+
# # [30] = :taint
|
216
|
+
# # [31] = :tainted?
|
217
|
+
# # [32] = :untaint
|
218
|
+
# # [33] = :untrust
|
219
|
+
# # [34] = :untrusted?
|
220
|
+
# # [35] = :trust
|
221
|
+
# # [36] = :frozen?
|
222
|
+
# # [37] = :methods
|
223
|
+
# # [38] = :singleton_methods
|
224
|
+
# # [39] = :protected_methods
|
225
|
+
# # [40] = :private_methods
|
226
|
+
# # [41] = :public_methods
|
227
|
+
# # [42] = :instance_variables
|
228
|
+
# # [43] = :instance_variable_get
|
229
|
+
# # [44] = :instance_variable_set
|
230
|
+
# # [45] = :instance_variable_defined?
|
231
|
+
# # [46] = :remove_instance_variable
|
232
|
+
# # [47] = :instance_of?
|
233
|
+
# # [48] = :kind_of?
|
234
|
+
# # [49] = :is_a?
|
235
|
+
# # [50] = :tap
|
236
|
+
# # [51] = :class
|
237
|
+
# # [52] = :display
|
238
|
+
# # [53] = :singleton_class
|
239
|
+
# # [54] = :public_send
|
240
|
+
# # [55] = :method
|
241
|
+
# # [56] = :public_method
|
242
|
+
# # [57] = :singleton_method
|
243
|
+
# # [58] = :define_singleton_method
|
244
|
+
# # [59] = :extend
|
245
|
+
# # [60] = :to_enum
|
246
|
+
# # [61] = :enum_for
|
247
|
+
# # [62] = :<=>
|
248
|
+
# # [63] = :===
|
249
|
+
# # [64] = :=~
|
250
|
+
# # [65] = :!~
|
251
|
+
# # [66] = :nil?
|
252
|
+
# # [67] = :respond_to?
|
253
|
+
# # [68] = :freeze
|
254
|
+
# # [69] = :object_id
|
255
|
+
# # [70] = :send
|
256
|
+
# # [71] = :pretty_inspect
|
257
|
+
# # [72] = :__send__
|
258
|
+
# # [73] = :!
|
259
|
+
# # [74] = :!=
|
260
|
+
# # [75] = :__id__
|
261
|
+
# # [76] = :equal?
|
262
|
+
# # [77] = :instance_eval
|
263
|
+
# # [78] = :instance_exec
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module WorkatoConnectorBuilder
|
2
|
+
class ASTGenerator
|
3
|
+
attr_reader :search_path
|
4
|
+
|
5
|
+
def initialize(search_path, parser, ignored_paths = [], ignored_files = [])
|
6
|
+
@parser = parser
|
7
|
+
@search_path = search_path
|
8
|
+
@ignored_paths = ignored_paths
|
9
|
+
@ignored_files = ignored_files
|
10
|
+
end
|
11
|
+
|
12
|
+
def get_asts
|
13
|
+
walk(search_path)
|
14
|
+
end
|
15
|
+
|
16
|
+
def get_ignored_paths
|
17
|
+
@ignored_paths.clone
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_ignored_files
|
21
|
+
@ignored_files.clone
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_writer :search_path
|
27
|
+
|
28
|
+
def walk(start)
|
29
|
+
absolute_start = File.expand_path(start)
|
30
|
+
asts = []
|
31
|
+
ignored_paths = @ignored_paths
|
32
|
+
ignored_files = @ignored_files
|
33
|
+
Dir.foreach(absolute_start) do |f|
|
34
|
+
if ignored_paths.include?(f) || ignored_files.include?(f)
|
35
|
+
next
|
36
|
+
end
|
37
|
+
|
38
|
+
path = File.join(start, f)
|
39
|
+
absolute_path = File.join(absolute_start, f)
|
40
|
+
# ignore everything starts with . (dir references, hidden dirs/files)
|
41
|
+
if f.start_with?('.')
|
42
|
+
next
|
43
|
+
elsif File.directory?(absolute_path)
|
44
|
+
asts.merge! self.walk(path)
|
45
|
+
elsif f.end_with?('.rb') and !f.start_with?('.')
|
46
|
+
ps = RuboCop::AST::ProcessedSource.from_file(path, 2.4)
|
47
|
+
comments = ps.ast_with_comments
|
48
|
+
nodes = ps.ast
|
49
|
+
|
50
|
+
asts << { ast: nodes, comments: comments, path: path }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
asts
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require_relative '../ast_generator'
|
2
|
+
require_relative '../hash_combiner'
|
3
|
+
require_relative '../constants'
|
4
|
+
require_relative '../validator'
|
5
|
+
require_relative '../fix_newlines_rewriter'
|
6
|
+
|
7
|
+
require 'parser/ruby24'
|
8
|
+
require 'rubocop-ast'
|
9
|
+
require 'unparser'
|
10
|
+
|
11
|
+
module WorkatoConnectorBuilder
|
12
|
+
module CLI
|
13
|
+
class BuildCommand
|
14
|
+
|
15
|
+
def initialize(shell, source_path, output_file, ignored_files, ignored_dirs)
|
16
|
+
@shell = shell
|
17
|
+
@source_path = source_path
|
18
|
+
@output_file = output_file
|
19
|
+
@ignored_files = ignored_files
|
20
|
+
@ignored_dirs = ignored_dirs
|
21
|
+
end
|
22
|
+
|
23
|
+
def exec
|
24
|
+
unless @ignored_files.empty?
|
25
|
+
@shell.say "Ignoring files:\n"
|
26
|
+
@ignored_files.each do |f|
|
27
|
+
@shell.say "\t#{f}\n"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
unless @ignored_dirs.empty?
|
31
|
+
@shell.say "Ignoring directories:\n"
|
32
|
+
@ignored_dirs.each do |f|
|
33
|
+
@shell.say "\t#{f}\n"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
ast_generator = ASTGenerator.new(
|
38
|
+
@source_path,
|
39
|
+
create_parser,
|
40
|
+
@ignored_dirs,
|
41
|
+
@ignored_files,
|
42
|
+
)
|
43
|
+
|
44
|
+
hash_combiner = HashCombiner.new @shell
|
45
|
+
output = process ast_generator, hash_combiner
|
46
|
+
|
47
|
+
output_file = File.absolute_path(@output_file)
|
48
|
+
|
49
|
+
@shell.say "Writing output file to : #{output_file}\n"
|
50
|
+
File.open(output_file, 'w') do |file|
|
51
|
+
file.write output
|
52
|
+
end
|
53
|
+
|
54
|
+
@shell.say "Done\n"
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
attr_reader :shell, :ignored_files, :ignored_dirs, :output_file, :source_path
|
60
|
+
|
61
|
+
def create_parser
|
62
|
+
builder = RuboCop::AST::Builder.new
|
63
|
+
Parser::Ruby24.new builder
|
64
|
+
end
|
65
|
+
|
66
|
+
def create_buffer(file_name, source)
|
67
|
+
Parser::Source::Buffer.new file_name, **{ source: source }
|
68
|
+
end
|
69
|
+
|
70
|
+
def process(ast_generator, hash_combiner)
|
71
|
+
asts = ast_generator.get_asts
|
72
|
+
require 'pry-byebug'
|
73
|
+
|
74
|
+
first_node = asts.first
|
75
|
+
tree_rewriter = FixNewlinesRewriter.new
|
76
|
+
|
77
|
+
asts.drop(1).each do |node|
|
78
|
+
combined = hash_combiner.combine(first_node[:ast], node[:ast])
|
79
|
+
first_node[:ast] = combined
|
80
|
+
end
|
81
|
+
|
82
|
+
# Check for valid workato keys
|
83
|
+
validator = WorkatoConnectorBuilder::Validator.new(@shell)
|
84
|
+
final_node = validator.process(first_node[:ast])
|
85
|
+
|
86
|
+
output = write_hash(final_node, 0, 1)
|
87
|
+
buffer = create_buffer(@output_file, output)
|
88
|
+
parser = create_parser
|
89
|
+
tree_rewriter.rewrite(buffer, parser.parse(buffer))
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
def write_hash(hash_node, indent_level, child_indent_level)
|
94
|
+
indent = indent_string(indent_level)
|
95
|
+
child_indent = indent_string(child_indent_level)
|
96
|
+
# we never indent the start
|
97
|
+
hash_string = "{\n"
|
98
|
+
hash_node.pairs.each do |pair|
|
99
|
+
unless pair.left_sibling.nil?
|
100
|
+
hash_string << "\n"
|
101
|
+
end
|
102
|
+
|
103
|
+
hash_string << child_indent
|
104
|
+
|
105
|
+
if pair.key.str_type?
|
106
|
+
hash_string << "'#{pair.key.value}'"
|
107
|
+
elsif pair.key.sym_type?
|
108
|
+
if pair.hash_rocket?
|
109
|
+
hash_string << ":#{pair.key.value}"
|
110
|
+
else
|
111
|
+
hash_string << pair.key.value.to_s
|
112
|
+
end
|
113
|
+
else
|
114
|
+
hash_string << pair.key.value.to_s
|
115
|
+
end
|
116
|
+
|
117
|
+
key_string = pair.delimiter(with_spacing: true)
|
118
|
+
hash_string << key_string
|
119
|
+
|
120
|
+
if pair.value.type == :hash
|
121
|
+
hash_string << write_hash(pair.value, child_indent_level, child_indent_level + 1)
|
122
|
+
else
|
123
|
+
hash_string << pair.value.source
|
124
|
+
end
|
125
|
+
hash_string << ','
|
126
|
+
|
127
|
+
unless pair.right_sibling.nil?
|
128
|
+
hash_string << "\n"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
hash_string << "\n#{indent}}"
|
132
|
+
end
|
133
|
+
|
134
|
+
def indent_string(indent_level)
|
135
|
+
' ' * (indent_level * 2)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require_relative './build_command'
|
3
|
+
|
4
|
+
module WorkatoConnectorBuilder
|
5
|
+
module CLI
|
6
|
+
class CLI < Thor
|
7
|
+
package_name "WorkatoConnectorBuilder"
|
8
|
+
map "-L" => :list
|
9
|
+
|
10
|
+
|
11
|
+
desc 'build <source_dir> <output_file>', ''
|
12
|
+
method_option :ignored_file, type: :string, repeatable: true
|
13
|
+
method_option :ignored_dir, type: :string, repeatable: true
|
14
|
+
method_option :help, type: :boolean, aliases: ['-h']
|
15
|
+
def build(path = nil, output_file = nil)
|
16
|
+
if options[:help]
|
17
|
+
help('build')
|
18
|
+
return 0
|
19
|
+
end
|
20
|
+
|
21
|
+
say "Beginning build of source files from #{path}, output file #{output_file}"
|
22
|
+
ignored_files = options[:ignored_file] || []
|
23
|
+
ignored_dirs = options[:ignored_dir] || []
|
24
|
+
BuildCommand.new(
|
25
|
+
@shell,
|
26
|
+
path,
|
27
|
+
output_file,
|
28
|
+
ignored_files,
|
29
|
+
ignored_dirs
|
30
|
+
).exec
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.exit_on_failure?
|
34
|
+
true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module WorkatoConnectorBuilder
|
2
|
+
module Constants
|
3
|
+
TOP_LEVEL_KEYS = [
|
4
|
+
:title,
|
5
|
+
:connection,
|
6
|
+
:test,
|
7
|
+
:actions,
|
8
|
+
:triggers,
|
9
|
+
:object_definitions,
|
10
|
+
:pick_lists,
|
11
|
+
:methods,
|
12
|
+
:secure_tunnel,
|
13
|
+
:webhook_keys,
|
14
|
+
]
|
15
|
+
|
16
|
+
# These are the only child keys that the top level key supports
|
17
|
+
FIRST_CHILD_LEVEL_KEYS = {
|
18
|
+
connection: [
|
19
|
+
:fields,
|
20
|
+
:authorization,
|
21
|
+
:base_uri,
|
22
|
+
],
|
23
|
+
}
|
24
|
+
|
25
|
+
# These are the only keys that every grandchild of these top level or first level keys support
|
26
|
+
SECOND_CHILD_LEVEL_KEYS = {
|
27
|
+
actions: [
|
28
|
+
:title,
|
29
|
+
:subtitle,
|
30
|
+
:description,
|
31
|
+
:help,
|
32
|
+
:config_fields,
|
33
|
+
:input_fields,
|
34
|
+
:execute,
|
35
|
+
:output_fields,
|
36
|
+
:sample_output,
|
37
|
+
:retry_on_response,
|
38
|
+
:retry_on_request,
|
39
|
+
:max_retries,
|
40
|
+
:summarize_input,
|
41
|
+
:summarize_output,
|
42
|
+
],
|
43
|
+
triggers: [
|
44
|
+
:title,
|
45
|
+
:subtitle,
|
46
|
+
:description,
|
47
|
+
:help,
|
48
|
+
:config_fields,
|
49
|
+
:input_fields,
|
50
|
+
:webhook_key,
|
51
|
+
:webhook_payload_type,
|
52
|
+
:webhook_subscribe,
|
53
|
+
:webhook_unsubscribe,
|
54
|
+
:webhook_notification,
|
55
|
+
:poll,
|
56
|
+
:dedup,
|
57
|
+
:output_fields,
|
58
|
+
:sample_output,
|
59
|
+
:summarize_input,
|
60
|
+
:summarize_output
|
61
|
+
],
|
62
|
+
authorization: [
|
63
|
+
:type,
|
64
|
+
:client_id,
|
65
|
+
:client_secret,
|
66
|
+
:authorization_url,
|
67
|
+
:token_url,
|
68
|
+
:acquire,
|
69
|
+
:apply,
|
70
|
+
:refresh_on,
|
71
|
+
:detect_on,
|
72
|
+
:refresh,
|
73
|
+
],
|
74
|
+
}
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'parser'
|
2
|
+
require 'byebug'
|
3
|
+
|
4
|
+
class FixNewlinesRewriter < Parser::TreeRewriter
|
5
|
+
|
6
|
+
def on_pair(node)
|
7
|
+
range = node.source_range
|
8
|
+
add_trailing_comma(range, range.source.length)
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
def add_trailing_comma(range, source_length)
|
14
|
+
unless has_trailing_comma?(range, source_length)
|
15
|
+
insert_after range.end, ','
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def remove_trailing_comma(range, source_length)
|
20
|
+
if has_trailing_comma?(range, source_length)
|
21
|
+
remove range.adjust(begin_pos: source_length, end_pos: 1)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def has_trailing_comma?(range, source_length)
|
26
|
+
next_char_range = range.adjust(begin_pos: source_length, end_pos: 1)
|
27
|
+
next_char_range.is?(',')
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module WorkatoConnectorBuilder
|
2
|
+
class HashCombiner
|
3
|
+
def initialize(shell)
|
4
|
+
@shell = shell
|
5
|
+
end
|
6
|
+
|
7
|
+
def combine(first, second)
|
8
|
+
require 'pry-byebug'
|
9
|
+
if !first.hash_type? or !second.hash_type?
|
10
|
+
return
|
11
|
+
end
|
12
|
+
|
13
|
+
concat_pairs = []
|
14
|
+
first.pairs.each do |pair|
|
15
|
+
matching_pair = second.pairs.find { |sp| sp.key == pair.key }
|
16
|
+
if matching_pair.nil?
|
17
|
+
concat_pairs.push pair
|
18
|
+
else
|
19
|
+
if pair.value.hash_type? && matching_pair.value.hash_type?
|
20
|
+
combined_node = combine(pair.value, matching_pair.value)
|
21
|
+
concat_pairs.push pair.updated(:pair, [pair.key].push(combined_node))
|
22
|
+
else
|
23
|
+
chosen_pair = replace_pair pair, matching_pair
|
24
|
+
|
25
|
+
unless chosen_pair.nil?
|
26
|
+
concat_pairs.push chosen_pair.updated(:pair, chosen_pair.children)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
concat_pairs.concat(second.pairs.select { |pair| !first.keys.include? pair.key } || [])
|
33
|
+
|
34
|
+
first.updated(:hash, concat_pairs)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
attr_reader :shell
|
40
|
+
|
41
|
+
def replace_pair(first, second)
|
42
|
+
source = first
|
43
|
+
|
44
|
+
@shell.say_status(
|
45
|
+
"Error: Mismatch found for key '#{first.key.value}'",
|
46
|
+
"Occurs at '#{first.source_range.to_s}' and '#{second.source_range.to_s}'",
|
47
|
+
:red
|
48
|
+
)
|
49
|
+
|
50
|
+
choice = @shell.ask(
|
51
|
+
%{Select a choice:
|
52
|
+
1) #{first.source}
|
53
|
+
2) #{second.source}
|
54
|
+
3) Do not include key\n},
|
55
|
+
:limited_to => %w[1 2 3]
|
56
|
+
)
|
57
|
+
|
58
|
+
case choice
|
59
|
+
when '2'
|
60
|
+
source = second
|
61
|
+
when '3'
|
62
|
+
source = nil
|
63
|
+
end
|
64
|
+
|
65
|
+
source
|
66
|
+
end
|
67
|
+
|
68
|
+
def get_pair_source(pair, include_comma = false, remove_leading_newlines = false, rstrip = false)
|
69
|
+
parent_source = pair.parent.source
|
70
|
+
parent_source_range = pair.parent.source_range
|
71
|
+
|
72
|
+
# Let's get any formatting that was in place from the second node
|
73
|
+
# Assume we're starting at the first pair, so we'll make our start position
|
74
|
+
# after the opening { and the end pos is after the }
|
75
|
+
pair_begin_pos = 1
|
76
|
+
pair_end_pos = parent_source.length - 2
|
77
|
+
# We need to get the position in relation to the parent's source range,
|
78
|
+
# since we're not using any parents ancestors when getting this source
|
79
|
+
pair_left_sibling = pair.left_sibling
|
80
|
+
unless pair_left_sibling.nil?
|
81
|
+
# Start after the left sibling's comma
|
82
|
+
pair_begin_pos = pair_left_sibling.source_range.end_pos + 1 - parent_source_range.begin_pos
|
83
|
+
end
|
84
|
+
|
85
|
+
# If we have a right sibling, we'll get to the start of it so we can capture any
|
86
|
+
# comments
|
87
|
+
pair_right_sibling = pair.right_sibling
|
88
|
+
unless pair_right_sibling.nil?
|
89
|
+
pair_end_pos = pair_right_sibling.source_range.begin_pos - 1 - parent_source_range.begin_pos
|
90
|
+
end
|
91
|
+
|
92
|
+
range = Range.new pair_begin_pos, pair_end_pos
|
93
|
+
pair_source = parent_source[range]
|
94
|
+
|
95
|
+
# trim off any newlines at the start, since we'll be inserting
|
96
|
+
# this into the other hash which might have it's own ending whitespace
|
97
|
+
if remove_leading_newlines
|
98
|
+
pair_source.sub!(/^[\n\r]*/, '')
|
99
|
+
end
|
100
|
+
|
101
|
+
if rstrip
|
102
|
+
pair_source.rstrip!
|
103
|
+
end
|
104
|
+
|
105
|
+
if include_comma
|
106
|
+
unless pair_source.rstrip.end_with?(',')
|
107
|
+
pair_source[pair.source] = "#{pair.source},"
|
108
|
+
end
|
109
|
+
else
|
110
|
+
pair_source[pair_source.rindex(',')] = ''
|
111
|
+
end
|
112
|
+
|
113
|
+
pair_source
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
module WorkatoConnectorBuilder
|
2
|
+
require 'pry-byebug'
|
3
|
+
class Validator
|
4
|
+
def initialize(shell)
|
5
|
+
@shell = shell
|
6
|
+
end
|
7
|
+
|
8
|
+
def process(node)
|
9
|
+
validate_top_level_hash(node)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def validate_top_level_hash(hash_node)
|
15
|
+
valid_keys = WorkatoConnectorBuilder::Constants::TOP_LEVEL_KEYS
|
16
|
+
process_hash(
|
17
|
+
hash_node,
|
18
|
+
valid_keys,
|
19
|
+
:first
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def validate_first_child_level(pair)
|
24
|
+
ancestor_keys = get_ancestor_keys(pair, include_self: true)
|
25
|
+
first_level_keys = WorkatoConnectorBuilder::Constants::FIRST_CHILD_LEVEL_KEYS[ancestor_keys.first] || []
|
26
|
+
|
27
|
+
processed_hash = process_hash(
|
28
|
+
pair.value,
|
29
|
+
first_level_keys,
|
30
|
+
:second
|
31
|
+
)
|
32
|
+
|
33
|
+
unless processed_hash.nil?
|
34
|
+
update_node(pair, children: [pair.key, processed_hash])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate_second_child_level(pair)
|
39
|
+
parent_keys = get_ancestor_keys(pair, include_self: true)
|
40
|
+
# check the current hash, then check the root one, this covers :connection.authorization and :triggers for example
|
41
|
+
second_level_keys = WorkatoConnectorBuilder::Constants::SECOND_CHILD_LEVEL_KEYS[parent_keys.last] ||
|
42
|
+
WorkatoConnectorBuilder::Constants::SECOND_CHILD_LEVEL_KEYS[parent_keys.first] || []
|
43
|
+
|
44
|
+
if second_level_keys.empty?
|
45
|
+
update_node(pair, copy_children: true)
|
46
|
+
end
|
47
|
+
|
48
|
+
processed_hash = process_hash(
|
49
|
+
pair.value,
|
50
|
+
second_level_keys,
|
51
|
+
nil
|
52
|
+
)
|
53
|
+
|
54
|
+
unless processed_hash.nil?
|
55
|
+
pair.updated(:pair, [pair.key, processed_hash])
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def update_node(node, copy_children: false, children: nil)
|
60
|
+
children = copy_children ? node.children : children
|
61
|
+
if children.nil?
|
62
|
+
nil
|
63
|
+
else
|
64
|
+
node.updated(node.type, children)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def process_hash(hash_node, valid_hash_keys, next_level_func)
|
69
|
+
kept_pairs = []
|
70
|
+
kept_invalid_pairs = []
|
71
|
+
hash_node.pairs.each do |pair|
|
72
|
+
parent_keys = get_ancestor_keys(pair)
|
73
|
+
message = "Error: '#{pair.key.value}' is not a valid key for "
|
74
|
+
if parent_keys.nil? || parent_keys.empty?
|
75
|
+
message << 'the root level'
|
76
|
+
else
|
77
|
+
parent_keys_label = parent_keys.join('.')
|
78
|
+
message << "'#{parent_keys_label}'"
|
79
|
+
end
|
80
|
+
if !valid_hash_keys.empty? && !valid_hash_keys.include?(pair.key.value.to_s.to_sym)
|
81
|
+
@shell.say "Error: #{message}", :red
|
82
|
+
choice = @shell.ask(
|
83
|
+
'Do you wish to keep the key => value in the final output?',
|
84
|
+
:limited_to => %w[yes no]
|
85
|
+
)
|
86
|
+
# Only valid pairs need to have their children checked
|
87
|
+
if choice == 'yes'
|
88
|
+
# This will reset the sibling relationships
|
89
|
+
kept_invalid_pairs << update_node(pair, copy_children: true)
|
90
|
+
end
|
91
|
+
|
92
|
+
next
|
93
|
+
end
|
94
|
+
|
95
|
+
if pair.value.hash_type? && !next_level_func.nil?
|
96
|
+
processed_pair = self.send("validate_#{next_level_func}_child_level", pair)
|
97
|
+
unless processed_pair.nil?
|
98
|
+
kept_pairs << processed_pair
|
99
|
+
end
|
100
|
+
else
|
101
|
+
# This will reset the sibling relationships
|
102
|
+
kept_pairs << update_node(pair, copy_children: true)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
kept_pairs.sort_by! { |p| sort_by_valid_keys(p, valid_hash_keys) }
|
107
|
+
|
108
|
+
kept_invalid_pairs.sort! { |a, b| a.key <=> b.key }
|
109
|
+
|
110
|
+
byebug
|
111
|
+
hash_node.updated(:hash, kept_pairs.concat(kept_invalid_pairs))
|
112
|
+
end
|
113
|
+
|
114
|
+
def sort_by_valid_keys(p, keys)
|
115
|
+
key = p.key.value.to_s.to_sym
|
116
|
+
keys.index(key) || key
|
117
|
+
end
|
118
|
+
|
119
|
+
def get_ancestor_keys(pair, include_self: false)
|
120
|
+
if pair.root?
|
121
|
+
return []
|
122
|
+
end
|
123
|
+
|
124
|
+
keys = include_self ? [pair.key.value.to_s.to_sym] : []
|
125
|
+
parent = pair.parent
|
126
|
+
while parent != nil
|
127
|
+
if parent.pair_type?
|
128
|
+
keys << parent.key.value.to_s.to_sym
|
129
|
+
end
|
130
|
+
parent = parent.parent
|
131
|
+
end
|
132
|
+
|
133
|
+
keys.reverse!
|
134
|
+
|
135
|
+
keys
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env gem build
|
2
|
+
# encoding: utf-8
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'workato-connector-builder'
|
7
|
+
spec.description = 'A tool for building Workato Connectors from multiple hash files'
|
8
|
+
spec.summary = spec.description
|
9
|
+
spec.authors = [ 'Steven Laroche', ]
|
10
|
+
spec.version = '0.0.1.pre'
|
11
|
+
spec.license = 'MIT'
|
12
|
+
|
13
|
+
spec.files = Dir['bin/*', 'lib/**/*.rb', 'workato-connector-builder.gemspec', 'LICENSE.txt']
|
14
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
15
|
+
spec.require_paths = [ 'lib' ]
|
16
|
+
spec.metadata = { 'issue_tracker' => 'https://www.github.com/johndoe/missing/issues' }
|
17
|
+
|
18
|
+
|
19
|
+
spec.required_ruby_version= '>= 2.0.0'
|
20
|
+
|
21
|
+
spec.add_runtime_dependency 'rubocop-ast', '~> 1.12'
|
22
|
+
spec.add_runtime_dependency 'thor', '~> 1.1'
|
23
|
+
|
24
|
+
spec.add_development_dependency 'pry-byebug', '~> 3.9'
|
25
|
+
spec.add_development_dependency 'rbs', '~> 1.6'
|
26
|
+
spec.add_development_dependency 'rspec', '~> 3.10'
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: workato-connector-builder
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.pre
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Steven Laroche
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-10-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rubocop-ast
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.12'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.12'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: thor
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry-byebug
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.9'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.9'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rbs
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.6'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.6'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.10'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.10'
|
83
|
+
description: A tool for building Workato Connectors from multiple hash files
|
84
|
+
email:
|
85
|
+
executables:
|
86
|
+
- workato-connector-builder
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- bin/workato-connector-builder
|
91
|
+
- lib/output.rb
|
92
|
+
- lib/test.rb
|
93
|
+
- lib/workato_connector_builder.rb
|
94
|
+
- lib/workato_connector_builder/ast_generator.rb
|
95
|
+
- lib/workato_connector_builder/cli/build_command.rb
|
96
|
+
- lib/workato_connector_builder/cli/cli.rb
|
97
|
+
- lib/workato_connector_builder/cli/config_store.rb
|
98
|
+
- lib/workato_connector_builder/constants.rb
|
99
|
+
- lib/workato_connector_builder/fix_newlines_rewriter.rb
|
100
|
+
- lib/workato_connector_builder/hash_combiner.rb
|
101
|
+
- lib/workato_connector_builder/validator.rb
|
102
|
+
- workato-connector-builder.gemspec
|
103
|
+
homepage:
|
104
|
+
licenses:
|
105
|
+
- MIT
|
106
|
+
metadata:
|
107
|
+
issue_tracker: https://www.github.com/johndoe/missing/issues
|
108
|
+
post_install_message:
|
109
|
+
rdoc_options: []
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: 2.0.0
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - ">"
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: 1.3.1
|
122
|
+
requirements: []
|
123
|
+
rubygems_version: 3.1.6
|
124
|
+
signing_key:
|
125
|
+
specification_version: 4
|
126
|
+
summary: A tool for building Workato Connectors from multiple hash files
|
127
|
+
test_files: []
|