factor 0.5.15 → 0.5.16

Sign up to get free protection for your applications and to get access to all the features.
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: {}