factor 0.5.15 → 0.5.16

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 569dc92eda75eb91dfaaacba2fdfdee5087137ed
4
- data.tar.gz: c379ed9d1349d42aff4ee6705b7afecffba8a787
3
+ metadata.gz: 3df70093d41d893436a8b43a6d08f4ffe87249fb
4
+ data.tar.gz: 8b163aa8f9b1ebe4c9ddaf8c24f67d28917c2e10
5
5
  SHA512:
6
- metadata.gz: 8eab23ca046a13ae752b270059d71c07b6b52870724c813d1550afe30f4631138a7e5b8686c47163a849ac72e5dceb79cde1441f5127da6945d7fd7218ac02bf
7
- data.tar.gz: 9537cbbc57f1ce1ce52f353d4eb0c25dda422b570b1474c071fbf90bffc916f7b8e8dc3118fc5f7d91a3bd4adf0a1677d3c75cc021902e77ab8e0e87e115abb1
6
+ metadata.gz: 0e1d4fa2b2775bc2d34bc43acc8bed86f518d0b1865a56647679536454e1e0c52f0ee20db18d5cae137b256e4fafd8cf8f85b8c4c282eef88117482638c519cb
7
+ data.tar.gz: 29e9b70bf5ccf70e7cf1292c7ec4e8f11377c4a6efb1a96dc84c8b4939372a53151d486d73f9ee5464ee60b9a503f290b54ea56500101a240143f036d7ee6f87
@@ -0,0 +1,123 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'colored'
4
+ require 'configatron'
5
+ require 'yaml'
6
+ require 'fileutils'
7
+
8
+ module Factor
9
+ module Commands
10
+ # Base command with common methods used by all commands
11
+ class Command
12
+ DEFAULT_FILENAME = {
13
+ connectors: File.expand_path('./connectors.yml'),
14
+ credentials: File.expand_path('./credentials.yml')
15
+ }
16
+
17
+ attr_accessor :destination_stream
18
+
19
+ def info(options = {})
20
+ log_line :info, options
21
+ end
22
+
23
+ def error(options = {})
24
+ log_line :error, options
25
+ end
26
+
27
+ def warn(options = {})
28
+ log_line :warn, options
29
+ end
30
+
31
+ def success(options = {})
32
+ log_line :success, options
33
+ end
34
+
35
+ def exception(message, exception)
36
+ error 'message' => message
37
+ error 'message' => " #{exception.message}"
38
+ exception.backtrace.each do |line|
39
+ error 'message' => " #{line}"
40
+ end
41
+ end
42
+
43
+ def load_config(options = {})
44
+ load_config_data :credentials, options
45
+ load_config_data :connectors, options
46
+ end
47
+
48
+ def save_config(options={})
49
+ credentials_relative_path = options[:credentials] || DEFAULT_FILENAME[:credentials]
50
+ credentials_absolute_path = File.expand_path(credentials_relative_path)
51
+ connectors_relative_path = options[:connectors] || DEFAULT_FILENAME[:connectors]
52
+ connectors_absolute_path = File.expand_path(connectors_relative_path)
53
+
54
+ connectors = Hash[stringify(configatron.connectors.to_h).sort]
55
+ credentials = Hash[stringify(configatron.credentials.to_h).sort]
56
+
57
+ File.write(connectors_absolute_path,YAML.dump(connectors))
58
+ File.write(credentials_absolute_path,YAML.dump(credentials))
59
+ end
60
+
61
+ private
62
+
63
+ def stringify(hash)
64
+ hash.inject({}) do |options, (key, value)|
65
+ options[key.to_s] = value.is_a?(Hash) ? stringify(value) : value
66
+ options
67
+ end
68
+ end
69
+
70
+ def load_config_data(config_type, options = {})
71
+ relative_path = options[config_type] || DEFAULT_FILENAME[config_type]
72
+ absolute_path = File.expand_path(relative_path)
73
+ begin
74
+ data = YAML.load(File.read(absolute_path))
75
+ rescue
76
+ data = {}
77
+ end
78
+ configatron[config_type].configure_from_hash(data)
79
+ rescue => ex
80
+ exception "Couldn't load #{config_type} from #{absolute_path}", ex
81
+ end
82
+
83
+ def log_line(section, options = {})
84
+ options = { message: options } if options.is_a?(String)
85
+ tag = tag(options)
86
+ message = options['message'] || options[:message]
87
+ section_text = format_section(section)
88
+ write "[ #{section_text} ] [#{time}]#{tag} #{message}" if message
89
+ end
90
+
91
+ def format_section(section)
92
+ formated_section = section.to_s.upcase.center(10)
93
+ case section
94
+ when :error then formated_section.red
95
+ when :info then formated_section.bold
96
+ when :warn then formated_section.yellow
97
+ when :success then formated_section.green
98
+ else formated_section
99
+ end
100
+ end
101
+
102
+ def tag(options)
103
+ primary = options['service_id'] || options['instance_id']
104
+ secondary = if options['service_id'] && options['instance_id']
105
+ ":#{options['instane_id']}"
106
+ else
107
+ ''
108
+ end
109
+ primary ? "[#{primary}#{secondary}]" : ''
110
+ end
111
+
112
+ def time
113
+ Time.now.localtime.strftime('%m/%d/%y %T.%L')
114
+ end
115
+
116
+ def write(message)
117
+ stream = @destination_stream || $stdout
118
+ stream.puts(message)
119
+ stream.flush
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,186 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'yaml'
4
+ require 'rest-client'
5
+ require 'erubis'
6
+ require 'json'
7
+
8
+ require 'commands/base'
9
+
10
+ module Factor
11
+ module Commands
12
+ # Workflow is a Command to start the factor runtime from the CLI
13
+ class Registry < Factor::Commands::Command
14
+
15
+ def workflows(args, options)
16
+ list = get_yaml_data 'https://raw.githubusercontent.com/factor-io/index/master/workflows.yml'
17
+
18
+ list.each do |id, values|
19
+ puts "#{values['name'].bold} (#{id}): #{values['description']}"
20
+ end
21
+ end
22
+
23
+ def connectors(args, options)
24
+ list = get_yaml_data 'https://raw.githubusercontent.com/factor-io/index/master/connectors.yml'
25
+
26
+ list.each do |id, values|
27
+ puts "#{values['name'].bold} (#{id})"
28
+ end
29
+ end
30
+
31
+ def add_connector(args, options)
32
+ puts "Workflow ID is required (factor registry connector add --help)".red unless args[0]
33
+
34
+ setup_connector args[0].to_s, options if args[0]
35
+ end
36
+
37
+ def add_workflow(args, options)
38
+ begin
39
+ list = get_yaml_data 'https://raw.githubusercontent.com/factor-io/index/master/workflows.yml'
40
+ rescue
41
+ puts "Couldn't connect to the server to get connector information".red
42
+ exit
43
+ end
44
+
45
+ unless args[0]
46
+ puts "Workflow ID is required".red
47
+ exit
48
+ end
49
+
50
+ begin
51
+ workflow_id = args[0].to_s
52
+ workflow_info = list[workflow_id]
53
+ config_url = workflow_info['config']
54
+ workflow_name = workflow_info['name']
55
+ rescue
56
+ puts "Couldn't find information for #{workflow_id}".red
57
+ exit
58
+ end
59
+
60
+ load_config(credentials:options.credentials, connectors:options.connectors)
61
+
62
+ begin
63
+ config = get_json_data(config_url)
64
+ rescue
65
+ puts "Couldn't pull up configuration information from #{config_url}".red
66
+ exit
67
+ end
68
+
69
+ if !config['required-connectors'] || !config['required-connectors'].is_a?(Array) || !config['variables'] || !config['variables'].is_a?(Hash)
70
+ puts "Configuration information for the workflow is missing information"
71
+ exit
72
+ end
73
+
74
+ config['required-connectors'].each do |connector_id|
75
+ if configatron.credentials.to_hash[connector_id.to_sym]
76
+ puts "#{connector_id} already configured".green
77
+ else
78
+ setup_connector(connector_id,options)
79
+ end
80
+ end
81
+
82
+ variables = {}
83
+ config['variables'].each do |var_id,var_info|
84
+ puts var_info['description'] if var_info['description']
85
+ values = begin
86
+ JSON.parse(options.values)
87
+ rescue
88
+ {}
89
+ end
90
+ variables[var_id] = values[var_id]
91
+ variables[var_id] ||= ask("#{var_info['name'].bold}#{' (optional)' if var_info['optional']}: ").to_s
92
+ end
93
+
94
+ begin
95
+ template = RestClient.get(config['template'])
96
+ rescue
97
+ puts "Couldn't find a template at #{config['template']}".red
98
+ exit
99
+ end
100
+
101
+ begin
102
+ eruby = Erubis::Eruby.new(template)
103
+ workflow_content = eruby.result(variables)
104
+ rescue
105
+ puts "Failed to generate template".red
106
+ exit
107
+ end
108
+
109
+ workflow_filename = "workflow-#{workflow_id}.rb"
110
+ begin
111
+ File.write(workflow_filename, workflow_content)
112
+ rescue
113
+ puts "Failed to write the file to disk. Check permissions.".red
114
+ exit
115
+ end
116
+
117
+
118
+ puts "Created #{workflow_name} successfully".green
119
+ end
120
+
121
+ private
122
+
123
+ def setup_connector(connector_id, options)
124
+ begin
125
+ list = get_yaml_data 'https://raw.githubusercontent.com/factor-io/index/master/connectors.yml'
126
+ rescue
127
+ puts "Couldn't connect to the server to get connector information".red
128
+ exit
129
+ end
130
+
131
+
132
+ begin
133
+ connector_info = list[connector_id]
134
+ connector_name = connector_info['name']
135
+ new_connectors = connector_info['connectors']
136
+ required_credentials = connector_info['credentials']
137
+ rescue
138
+ puts "Couldn't find information for '#{connector_id}'".red
139
+ exit
140
+ end
141
+
142
+ unless connector_name && new_connectors && required_credentials
143
+ puts "Couldn't find information for '#{connector_id}'".red
144
+ exit
145
+ end
146
+
147
+ load_config(credentials:options.credentials, connectors:options.connectors)
148
+ connectors = configatron.connectors.to_hash
149
+ credentials = configatron.credentials.to_hash
150
+
151
+ connectors[connector_id] = new_connectors
152
+
153
+ required_credentials.each do |credential_id, credential_info|
154
+ puts credential_info['description'] if credential_info['description']
155
+ credentials[connector_id] ||= {}
156
+ values = begin
157
+ JSON.parse(options.values)
158
+ rescue
159
+ {}
160
+ end
161
+ credentials[connector_id][credential_id.to_s] = values[credential_id.to_s]
162
+ credentials[connector_id][credential_id.to_s] ||= ask("#{connector_name.bold} #{credential_info['name'].bold}#{' (optional)' if credential_info['optional']}: ").to_s
163
+ end
164
+
165
+ configatron[:credentials].configure_from_hash(credentials)
166
+ configatron[:connectors].configure_from_hash(connectors)
167
+
168
+ save_config(credentials:options.credentials, connectors:options.connectors)
169
+
170
+ puts "Setup #{connector_name} successfully".green
171
+ end
172
+
173
+ def get_yaml_data(url)
174
+ raw_content = RestClient.get(url)
175
+ list = YAML.parse(raw_content).to_ruby
176
+ list
177
+ end
178
+
179
+ def get_json_data(url)
180
+ raw_content = RestClient.get(url)
181
+ data = JSON.parse(raw_content)
182
+ data
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,112 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'configatron'
4
+
5
+ require 'commands/base'
6
+ require 'runtime'
7
+
8
+ module Factor
9
+ module Commands
10
+ # Workflow is a Command to start the factor runtime from the CLI
11
+ class Workflow < Factor::Commands::Command
12
+ def initialize
13
+ @workflows = {}
14
+ end
15
+
16
+ def server(_args, options)
17
+ config_settings = {}
18
+ config_settings[:credentials] = options.credentials
19
+ config_settings[:connectors] = options.connectors
20
+ workflow_filename = File.expand_path(options.path || '.')
21
+ @destination_stream = File.new(options.log, 'w+') if options.log
22
+
23
+ load_config(config_settings)
24
+ load_all_workflows(workflow_filename)
25
+ block_until_interupt
26
+ log_message 'status' => 'info', 'message' => 'Good bye!'
27
+ end
28
+
29
+ private
30
+
31
+ def load_all_workflows(workflow_filename)
32
+ glob_ending = workflow_filename[-1] == '/' ? '' : '/'
33
+ glob = "#{workflow_filename}#{glob_ending}*.rb"
34
+ file_list = Dir.glob(glob)
35
+ if !file_list.all? { |file| File.file?(file) }
36
+ error "#{workflow_filename} is neither a file or directory"
37
+ elsif file_list.count == 0
38
+ error 'No workflows in this directory to run'
39
+ else
40
+ file_list.each { |filename| load_workflow(filename) }
41
+ end
42
+ end
43
+
44
+ def block_until_interupt
45
+ log_message 'status' => 'info', 'message' => 'Ctrl-c to exit'
46
+ begin
47
+ loop do
48
+ sleep 1
49
+ end
50
+ rescue Interrupt
51
+ log_message 'status' => 'info', 'message' => 'Exiting app...'
52
+ ensure
53
+ @workflows.keys.each { |filename| unload_workflow(filename) }
54
+ end
55
+ end
56
+
57
+ def load_workflow(filename)
58
+ workflow_filename = File.expand_path(filename)
59
+ log_message(
60
+ 'status' => 'info',
61
+ 'message' => "Loading workflow from #{workflow_filename}")
62
+ begin
63
+ workflow_definition = File.read(workflow_filename)
64
+ rescue => ex
65
+ exception "Couldn't read file #{workflow_filename}", ex
66
+ return
67
+ end
68
+
69
+ log_message(
70
+ 'status' => 'info',
71
+ 'message' => 'Setting up workflow processor')
72
+ begin
73
+ connector_settings = configatron.connectors.to_hash
74
+ credential_settings = configatron.credentials.to_hash
75
+ runtime = Runtime.new(connector_settings, credential_settings)
76
+ runtime.logger = method(:log_message)
77
+ rescue => ex
78
+ message = "Couldn't setup workflow process for #{workflow_filename}"
79
+ exception message, ex
80
+ end
81
+
82
+ @workflows[workflow_filename] = fork do
83
+ begin
84
+ log_message(
85
+ 'status' => 'info',
86
+ 'message' => "Starting #{workflow_filename}")
87
+ runtime.load(workflow_definition)
88
+ rescue => ex
89
+ exception "Couldn't load #{workflow_filename}", ex
90
+ end
91
+ end
92
+ end
93
+
94
+ def unload_workflow(filename)
95
+ workflow_filename = File.expand_path(filename)
96
+ log_message(
97
+ 'status' => 'info',
98
+ 'message' => "Stopping #{workflow_filename}")
99
+ Process.kill('SIGINT', @workflows[workflow_filename])
100
+ end
101
+
102
+ def log_message(message_info)
103
+ case message_info['status']
104
+ when 'info' then info message_info
105
+ when 'success' then success message_info
106
+ when 'warn' then warn message_info
107
+ else error message_info
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
data/lib/factor.rb ADDED
@@ -0,0 +1,54 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'commander/import'
4
+
5
+ require 'factor/version'
6
+ require 'commands/workflows'
7
+ require 'commands/registry'
8
+
9
+ program :name, 'Factor.io Server'
10
+ program :version, Factor::VERSION
11
+ program :description, 'Factor.io Server to run workflows'
12
+
13
+ command 'server' do |c|
14
+ c.syntax = 'factor server [options]'
15
+ c.description = 'Start the Factor.io Server in the current local directory'
16
+ c.option '--log FILE', String, 'Log file path. Default is stdout.'
17
+ c.option '--credentials FILE', String, 'credentials.yml file path.'
18
+ c.option '--connectors FILE', String, 'connectors.yml file path'
19
+ c.option '--path FILE', String, 'Path to workflows'
20
+ c.when_called Factor::Commands::Workflow, :server
21
+ end
22
+
23
+ command 'registry workflows' do |c|
24
+ c.syntax = 'factor registry workflows'
25
+ c.description = 'Get list of available workflow jumpstarts'
26
+ c.when_called Factor::Commands::Registry, :workflows
27
+ end
28
+
29
+ command 'registry workflows add' do |c|
30
+ c.syntax = 'factor registry workflow add <id>'
31
+ c.description = 'Get list of available workflows'
32
+ c.option '--credentials FILE', String, 'credentials.yml file path.'
33
+ c.option '--connectors FILE', String, 'connectors.yml file path'
34
+ c.option '--values \'{"api_key":"foo"}\'', String, "{}"
35
+ c.when_called Factor::Commands::Registry, :add_workflow
36
+ end
37
+
38
+ command 'registry connectors' do |c|
39
+ c.syntax = 'factor registry connectors'
40
+ c.description = 'Get list of available connectors'
41
+ c.when_called Factor::Commands::Registry, :connectors
42
+ end
43
+
44
+ command 'registry connector add' do |c|
45
+ c.syntax = 'factor registry connector add <id>'
46
+ c.description = 'Get list of available connectors'
47
+ c.option '--credentials FILE', String, 'credentials.yml file path.'
48
+ c.option '--connectors FILE', String, 'connectors.yml file path'
49
+ c.option '--values \'{"api_key":"foo"}\'', String, "{}"
50
+ c.when_called Factor::Commands::Registry, :add_connector
51
+ end
52
+
53
+ alias_command 's', 'server'
54
+ alias_command 'r', 'registry'
@@ -0,0 +1,6 @@
1
+ # encoding: UTF-8
2
+
3
+ # Primary Factor.io module
4
+ module Factor
5
+ VERSION = '0.5.16'
6
+ end
data/lib/listener.rb ADDED
@@ -0,0 +1,26 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'websocket_manager'
4
+
5
+ module Factor
6
+ # Class Listener for integrating with connector service
7
+ class Listener
8
+ def initialize(url)
9
+ @url = url
10
+ end
11
+
12
+ def listener(listener_id)
13
+ listen("#{@url}/listeners/#{listener_id}")
14
+ end
15
+
16
+ def action(action_id)
17
+ listen("#{@url}/actions/#{action_id}")
18
+ end
19
+
20
+ private
21
+
22
+ def listen(uri_path)
23
+ WebSocketManager.new(uri_path)
24
+ end
25
+ end
26
+ end
data/lib/runtime.rb ADDED
@@ -0,0 +1,232 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'json'
4
+ require 'securerandom'
5
+ require 'yaml'
6
+ require 'eventmachine'
7
+ require 'uri'
8
+ require 'faye/websocket'
9
+ require 'ostruct'
10
+
11
+ require 'listener'
12
+ require 'commands/base'
13
+
14
+ module Factor
15
+ # Runtime class is the magic of the server
16
+ class Runtime
17
+ attr_accessor :logger, :name, :description, :id, :instance_id, :connectors, :credentials
18
+
19
+ def initialize(connectors, credentials)
20
+ @workflow_spec = {}
21
+ @sockets = []
22
+ @instance_id = SecureRandom.hex(3)
23
+ @reconnect = true
24
+
25
+ trap 'SIGINT' do
26
+ info "Exiting '#{@instance_id}'"
27
+ @reconnect = false
28
+ @sockets.each { |s| s.close }
29
+ exit
30
+ end
31
+
32
+ @connectors = {}
33
+ flat_hash(connectors).each do |key, connector_url|
34
+ @connectors[key] = Listener.new(connector_url)
35
+ end
36
+
37
+ @credentials = {}
38
+ credentials.each do |connector_id, credential_settings|
39
+ @credentials[connector_id] = credential_settings
40
+ end
41
+ end
42
+
43
+ def load(workflow_definition)
44
+ EM.run do
45
+ instance_eval(workflow_definition)
46
+ end
47
+ end
48
+
49
+ def listen(service_ref, params = {}, &block)
50
+ service_map = service_ref.split('::')
51
+ service_id = service_map.first
52
+ listener_id = service_map.last
53
+ service_key = service_map[0..-2].map{|k| k.to_sym}
54
+
55
+ ws = @connectors[service_key].listener(listener_id)
56
+
57
+ handle_on_open(service_ref, 'Listener', ws, params)
58
+
59
+ ws.on :close do
60
+ error 'Listener disconnected'
61
+ if @reconnect
62
+ warn 'Reconnecting...'
63
+ sleep 3
64
+ ws.open
65
+ end
66
+ end
67
+
68
+ ws.on :message do |event|
69
+ listener_response = JSON.parse(event.data)
70
+ case listener_response['type']
71
+ when'start_workflow'
72
+ success "Workflow '#{service_id}::#{listener_id}' triggered"
73
+ error_handle_call(listener_response, &block)
74
+ when 'return'
75
+ success "Workflow '#{service_ref}' started"
76
+ when 'fail'
77
+ error "Workflow '#{service_ref}' failed to start"
78
+ when 'log'
79
+ listener_response['message'] = " #{listener_response['message']}"
80
+ log_message(listener_response)
81
+ else
82
+ error "Unknown listener response: #{listener_response}"
83
+ end
84
+ end
85
+
86
+ ws.on :retry do |event|
87
+ warn event[:message]
88
+ end
89
+
90
+ ws.on :error do |event|
91
+ err = 'Error during WebSocket handshake: Unexpected response code: 401'
92
+ if event.message == err
93
+ error "Sorry but you don't have access to this listener,
94
+ | either because your token is invalid or your plan doesn't
95
+ | support this listener"
96
+ else
97
+ error 'Failure in WebSocket connection to connector service'
98
+ end
99
+ end
100
+
101
+ ws.open
102
+
103
+ @sockets << ws
104
+ end
105
+
106
+ def run(service_ref, params = {}, &block)
107
+ service_map = service_ref.split('::')
108
+ service_id = service_map.first
109
+ action_id = service_map.last
110
+ service_key = service_map[0..-2].map{|k| k.to_sym}
111
+
112
+ ws = @connectors[service_key].action(action_id)
113
+
114
+ handle_on_open(service_ref, 'Action', ws, params)
115
+
116
+ ws.on :error do
117
+ error 'Connection dropped while calling action'
118
+ end
119
+
120
+ ws.on :message do |event|
121
+ action_response = JSON.parse(event.data)
122
+ case action_response['type']
123
+ when 'return'
124
+ ws.close
125
+ success "Action '#{service_ref}' responded"
126
+ error_handle_call(action_response, &block)
127
+ when 'fail'
128
+ ws.close
129
+ error " #{action_response['message']}"
130
+ error "Action '#{service_ref}' failed"
131
+ when 'log'
132
+ action_response['message'] = " #{action_response['message']}"
133
+ log_message(action_response)
134
+ else
135
+ error "Unknown action response: #{action_response}"
136
+ end
137
+ end
138
+
139
+ ws.open
140
+
141
+ @sockets << ws
142
+ end
143
+
144
+ private
145
+
146
+ class DeepStruct < OpenStruct
147
+ def initialize(hash=nil)
148
+ @table = {}
149
+ @hash_table = {}
150
+
151
+ if hash
152
+ hash.each do |k,v|
153
+ @table[k.to_sym] = (v.is_a?(Hash) ? self.class.new(v) : v)
154
+ @hash_table[k.to_sym] = v
155
+
156
+ new_ostruct_member(k)
157
+ end
158
+ end
159
+ end
160
+
161
+ def to_h
162
+ @hash_table
163
+ end
164
+
165
+ def [](idx)
166
+ hash = marshal_dump
167
+ hash[idx.to_sym]
168
+ end
169
+ end
170
+
171
+ def simple_object_convert(item)
172
+ if item.is_a?(Hash)
173
+ DeepStruct.new(item)
174
+ elsif item.is_a?(Array)
175
+ item.map do |i|
176
+ simple_object_convert(i)
177
+ end
178
+ else
179
+ item
180
+ end
181
+ end
182
+
183
+ def flat_hash(h,f=[],g={})
184
+ return g.update({ f=>h }) unless h.is_a? Hash
185
+ h.each { |k,r| flat_hash(r,f+[k],g) }
186
+ g
187
+ end
188
+
189
+ def handle_on_open(service_ref, dsl_type, ws, params)
190
+ service_map = service_ref.split('::')
191
+ service_id = service_map.first
192
+
193
+ ws.on :open do
194
+ params.merge!(@credentials[service_id.to_sym] || {})
195
+ success "#{dsl_type.capitalize} '#{service_ref}' called"
196
+ ws.send(params.to_json)
197
+ end
198
+ end
199
+
200
+ def error_handle_call(listener_response, &block)
201
+ content = simple_object_convert(listener_response['payload'])
202
+ block.call(content) if block
203
+ rescue => ex
204
+ error "Error in workflow definition: #{ex.message}"
205
+ ex.backtrace.each do |line|
206
+ error " #{line}"
207
+ end
208
+ end
209
+
210
+ def success(msg)
211
+ log_message('type' => 'log', 'status' => 'success', 'message' => msg)
212
+ end
213
+
214
+ def warn(msg)
215
+ log_message('type' => 'log', 'status' => 'warn', 'message' => msg)
216
+ end
217
+
218
+ def error(msg)
219
+ log_message('type' => 'log', 'status' => 'error', 'message' => msg)
220
+ end
221
+
222
+ def info(msg)
223
+ log_message('type' => 'log', 'status' => 'info', 'message' => msg)
224
+ end
225
+
226
+ def log_message(message_info)
227
+ message_info['instance_id'] = @instance_id
228
+ message_info['workflow_id'] = @id
229
+ @logger.call(message_info) if @logger
230
+ end
231
+ end
232
+ end
@@ -0,0 +1,94 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'faye/websocket'
4
+ require 'uri'
5
+
6
+ module Factor
7
+ # class for managing the web socket connections
8
+ class WebSocketManager
9
+ attr_accessor :keep_open, :events, :state
10
+
11
+ def initialize(uri, headers = {})
12
+ u = URI(uri)
13
+ @uri = u.to_s
14
+ @settings = { ping: 10, retry: 5 }
15
+ @settings[:headers] = headers if headers && headers != {}
16
+ @state = :closed
17
+ @events = {}
18
+ end
19
+
20
+ def open
21
+ if closed?
22
+ @state = :opening
23
+ connect
24
+ end
25
+ @state
26
+ end
27
+
28
+ def close
29
+ if open?
30
+ @state = :closing
31
+ @ws.close
32
+ end
33
+ @state
34
+ end
35
+
36
+ def on(event, &block)
37
+ @events[event] = block
38
+ end
39
+
40
+ def open?
41
+ @state == :open
42
+ end
43
+
44
+ def opening?
45
+ @state == :opening
46
+ end
47
+
48
+ def closed?
49
+ @state == :closed
50
+ end
51
+
52
+ def closing?
53
+ @state == :closing
54
+ end
55
+
56
+ def send(msg)
57
+ @ws.send(msg)
58
+ end
59
+
60
+ private
61
+
62
+ def call_event(event, data)
63
+ @events[event].call(data) if @events[event]
64
+ end
65
+
66
+ def connect
67
+ EM.run do
68
+ begin
69
+ @ws = Faye::WebSocket::Client.new(@uri, nil, @settings)
70
+
71
+ @ws.on :close do |event|
72
+ @state = :closed
73
+ call_event :close, event
74
+ end
75
+
76
+ @ws.on :message do |msg|
77
+ call_event :message, msg
78
+ end
79
+
80
+ @ws.on :open do |event|
81
+ @state = :open
82
+ call_event :open, event
83
+ end
84
+
85
+ @ws.on :error do |event|
86
+ call_event :error, event
87
+ end
88
+ rescue => ex
89
+ call_event :fail, ex.message
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: factor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.15
4
+ version: 0.5.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maciej Skierkowski
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-21 00:00:00.000000000 Z
11
+ date: 2014-10-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: commander
@@ -150,6 +150,14 @@ files:
150
150
  - "./spec/spec_helper.rb"
151
151
  - "./spec/workflow_spec.rb"
152
152
  - bin/factor
153
+ - lib/commands/base.rb
154
+ - lib/commands/registry.rb
155
+ - lib/commands/workflows.rb
156
+ - lib/factor.rb
157
+ - lib/factor/version.rb
158
+ - lib/listener.rb
159
+ - lib/runtime.rb
160
+ - lib/websocket_manager.rb
153
161
  homepage: https://factor.io
154
162
  licenses: []
155
163
  metadata: {}