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 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
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'workato_connector_builder/cli/cli'
5
+ WorkatoConnectorBuilder::CLI::CLI.start
data/lib/output.rb ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ :test => 1,
3
+ :collision => 2,
4
+ 'dup' => {
5
+ a: 1,
6
+ b: 2
7
+ },
8
+ :otherfield => 2,
9
+ dup: {
10
+ # some comment
11
+ a: 2,
12
+ c: 3 # another_comment
13
+ },
14
+ }
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,17 @@
1
+ module WorkatoConnectorBuilder
2
+ module CLI
3
+ class ConfigStore
4
+ def initialize
5
+
6
+ end
7
+
8
+ def load_config path
9
+
10
+ end
11
+
12
+ def load_from_args args
13
+
14
+ end
15
+ end
16
+ end
17
+ 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,4 @@
1
+ module WorkatoConnectorBuilder
2
+ require_relative 'workato_connector_builder/hash_combiner'
3
+ require_relative 'workato_connector_builder/ast_generator'
4
+ 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: []