factor 0.3.1 → 0.5.04
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.
- data/.gitignore +19 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +32 -0
- data/README.md +10 -0
- data/bin/factor +2 -10
- data/factor.gemspec +24 -0
- data/lib/cli.rb +21 -0
- data/lib/commands/base.rb +106 -0
- data/lib/commands/workflows.rb +115 -0
- data/lib/factor.rb +4 -6
- data/lib/factor/base.rb +17 -0
- data/lib/factor/version.rb +6 -0
- data/lib/listener.rb +48 -0
- data/lib/runtime.rb +192 -0
- data/lib/websocket_manager.rb +94 -0
- metadata +57 -39
- data/lib/channel/activity.rb +0 -10
- data/lib/channel/channel.rb +0 -8
- data/lib/channel/event.rb +0 -13
- data/lib/channel/listener.rb +0 -20
- data/lib/channel/trigger.rb +0 -16
- data/lib/cli/channel_task.rb +0 -37
- data/lib/cli/command.rb +0 -36
- data/lib/cli/credential_task.rb +0 -39
- data/lib/cli/factor_task.rb +0 -26
- data/lib/cli/workflow_task.rb +0 -49
- data/lib/client/client.rb +0 -151
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
factor (0.5.04)
|
5
|
+
colored (~> 1.2)
|
6
|
+
commander (~> 4.1.5)
|
7
|
+
configatron (~> 4.2.0)
|
8
|
+
faye-websocket (~> 0.7.2)
|
9
|
+
rest_client (~> 1.7.3)
|
10
|
+
|
11
|
+
GEM
|
12
|
+
remote: https://rubygems.org/
|
13
|
+
specs:
|
14
|
+
colored (1.2)
|
15
|
+
commander (4.1.6)
|
16
|
+
highline (~> 1.6.11)
|
17
|
+
configatron (4.2.0)
|
18
|
+
eventmachine (1.0.3)
|
19
|
+
faye-websocket (0.7.2)
|
20
|
+
eventmachine (>= 0.12.0)
|
21
|
+
websocket-driver (>= 0.3.1)
|
22
|
+
highline (1.6.20)
|
23
|
+
netrc (0.7.7)
|
24
|
+
rest_client (1.7.3)
|
25
|
+
netrc (~> 0.7.7)
|
26
|
+
websocket-driver (0.3.2)
|
27
|
+
|
28
|
+
PLATFORMS
|
29
|
+
ruby
|
30
|
+
|
31
|
+
DEPENDENCIES
|
32
|
+
factor!
|
data/README.md
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
[](https://codeclimate.com/github/factor-io/factor)
|
2
|
+
[](https://codeclimate.com/github/factor-io/factor)
|
3
|
+
[](https://travis-ci.org/factor-io/factor)
|
4
|
+
Factor.io Server Runtime
|
5
|
+
==========
|
6
|
+
|
7
|
+
gem install factor
|
8
|
+
git clone git@github.com/factor-io/example-workflows.git
|
9
|
+
cd example-workflows
|
10
|
+
factor s
|
data/bin/factor
CHANGED
@@ -1,12 +1,4 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
2
|
+
# -*- mode: ruby -*-
|
3
3
|
|
4
|
-
require
|
5
|
-
bin_file = Pathname.new(__FILE__).realpath
|
6
|
-
|
7
|
-
# add self to libpath
|
8
|
-
$:.unshift File.expand_path("../../lib", bin_file)
|
9
|
-
require 'factor'
|
10
|
-
|
11
|
-
|
12
|
-
Factor::CLI::FactorTask.start
|
4
|
+
require File.expand_path('../../lib/factor', __FILE__)
|
data/factor.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
require 'factor/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "factor"
|
6
|
+
s.version = Factor::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = ["Maciej Skierkowski"]
|
9
|
+
s.email = ["maciej@factor.io"]
|
10
|
+
s.homepage = "https://factor.io"
|
11
|
+
s.summary = %q{CLI to manager workflows on Factor.io}
|
12
|
+
s.description = %q{CLI to manager workflows on Factor.io}
|
13
|
+
|
14
|
+
s.files = %x{git ls-files}.split("\n")
|
15
|
+
s.test_files = %x{git ls-files -- {test,spec,features}/*}.split("\n")
|
16
|
+
s.executables = %x{git ls-files -- bin/*}.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_runtime_dependency 'commander', '~> 4.1.5'
|
20
|
+
s.add_runtime_dependency 'rest_client', '~> 1.7.3'
|
21
|
+
s.add_runtime_dependency 'faye-websocket', '~> 0.7.2'
|
22
|
+
s.add_runtime_dependency 'colored', '~> 1.2'
|
23
|
+
s.add_runtime_dependency 'configatron', '~> 4.2.0'
|
24
|
+
end
|
data/lib/cli.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'commander/import'
|
4
|
+
|
5
|
+
require 'commands/workflows'
|
6
|
+
|
7
|
+
program :name, 'Factor.io Server'
|
8
|
+
program :version, Factor::VERSION
|
9
|
+
program :description, 'Factor.io Server to run workflows'
|
10
|
+
|
11
|
+
command 'server' do |c|
|
12
|
+
c.syntax = 'factor server [options]'
|
13
|
+
c.description = 'Start the Factor.io Server in the current local directory'
|
14
|
+
c.option '--log FILE', String, 'Log file path. Default is stdout.'
|
15
|
+
c.option '--credentials FILE', String, 'credentials.yml file path.'
|
16
|
+
c.option '--connectors FILE', String, 'connectors.yml file path'
|
17
|
+
c.option '--path FILE', String, 'Path to workflows'
|
18
|
+
c.when_called Factor::Commands::Workflow, :server
|
19
|
+
end
|
20
|
+
|
21
|
+
alias_command 's', 'server'
|
@@ -0,0 +1,106 @@
|
|
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
|
+
|
11
|
+
# Base command with common methods used by all commands
|
12
|
+
class Command
|
13
|
+
DEFAULT_FILENAME = {
|
14
|
+
connectors: File.expand_path('./connectors.yml'),
|
15
|
+
credentials: File.expand_path('./credentials.yml')
|
16
|
+
}
|
17
|
+
|
18
|
+
attr_accessor :destination_stream
|
19
|
+
|
20
|
+
def info(options = {})
|
21
|
+
log_line :info, options
|
22
|
+
end
|
23
|
+
|
24
|
+
def error(options = {})
|
25
|
+
log_line :error, options
|
26
|
+
end
|
27
|
+
|
28
|
+
def warn(options = {})
|
29
|
+
log_line :warn, options
|
30
|
+
end
|
31
|
+
|
32
|
+
def success(options = {})
|
33
|
+
log_line :success, options
|
34
|
+
end
|
35
|
+
|
36
|
+
def debug(options = {})
|
37
|
+
end
|
38
|
+
|
39
|
+
def exception(message, exception)
|
40
|
+
error 'message' => message
|
41
|
+
error 'message' => " #{exception.message}"
|
42
|
+
exception.backtrace.each do |line|
|
43
|
+
error 'message' => " #{line}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def load_config(options = {})
|
48
|
+
load_config_data :credentials, options
|
49
|
+
load_config_data :connectors, options
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def load_config_data(config_type, options = {})
|
55
|
+
relative_path = options[config_type] || DEFAULT_FILENAME[config_type]
|
56
|
+
absolute_path = File.expand_path(relative_path)
|
57
|
+
info message: "Loading #{config_type.to_s} from #{absolute_path}"
|
58
|
+
data = YAML.load(File.read(absolute_path))
|
59
|
+
configatron[config_type].configure_from_hash(data)
|
60
|
+
rescue => ex
|
61
|
+
exception "Couldn't load #{config_type.to_s} from #{absolute_path}", ex
|
62
|
+
end
|
63
|
+
|
64
|
+
def log_line(section, options = {})
|
65
|
+
options = { message: options } if options.is_a?(String)
|
66
|
+
tag = tag(options)
|
67
|
+
message = options['message'] || options[:message]
|
68
|
+
section_text = format_section(section)
|
69
|
+
write "[ #{section_text} ] [#{time}]#{tag} #{message}" if message
|
70
|
+
end
|
71
|
+
|
72
|
+
def format_section(section)
|
73
|
+
formated_section = section.to_s.upcase.center(10)
|
74
|
+
case section
|
75
|
+
when :error then formated_section.red
|
76
|
+
when :info then formated_section.bold
|
77
|
+
when :warn then formated_section.yellow
|
78
|
+
when :success then formated_section.green
|
79
|
+
else formated_section
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def tag(options)
|
84
|
+
tag = ''
|
85
|
+
if options['workflow_id'] && options['instance_id']
|
86
|
+
tag = "[#{options['workflow_id']}:#{options['instance_id']}]"
|
87
|
+
elsif options['workflow_id'] && !options['instance_id']
|
88
|
+
tag = "[#{options['workflow_id']}]"
|
89
|
+
elsif !options['workflow_id'] && options['instance_id']
|
90
|
+
tag = "[#{options['instance_id']}]"
|
91
|
+
end
|
92
|
+
tag
|
93
|
+
end
|
94
|
+
|
95
|
+
def time
|
96
|
+
Time.now.localtime.strftime('%m/%d/%y %T.%L')
|
97
|
+
end
|
98
|
+
|
99
|
+
def write(message)
|
100
|
+
stream = @destination_stream || $stdout
|
101
|
+
stream.puts(message)
|
102
|
+
stream.flush
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'configatron'
|
4
|
+
|
5
|
+
require 'commands/base'
|
6
|
+
require 'runtime'
|
7
|
+
|
8
|
+
module Factor
|
9
|
+
module Commands
|
10
|
+
|
11
|
+
# Workflow is a Command to start the factor runtime from the CLI
|
12
|
+
class Workflow < Factor::Commands::Command
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@workflows = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def server(args, options)
|
19
|
+
config_settings = {}
|
20
|
+
config_settings[:credentials] = options.credentials
|
21
|
+
config_settings[:connectors] = options.connectors
|
22
|
+
workflow_filename = File.expand_path(options.path || '.')
|
23
|
+
@destination_stream = File.new(options.log, 'w+') if options.log
|
24
|
+
|
25
|
+
load_config(config_settings)
|
26
|
+
load_all_workflows(workflow_filename)
|
27
|
+
block_until_interupt
|
28
|
+
log_message 'status' => 'info', 'message' => 'Good bye!'
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def load_all_workflows(workflow_filename)
|
34
|
+
glob_ending = workflow_filename[-1] == '/' ? '' : '/'
|
35
|
+
glob = "#{workflow_filename}#{glob_ending}*.rb"
|
36
|
+
file_list = Dir.glob(glob)
|
37
|
+
if !file_list.all? { |file| File.file?(file) }
|
38
|
+
error "#{workflow_filename} is neither a file or directory"
|
39
|
+
elsif file_list.count == 0
|
40
|
+
error 'No workflows in this directory to run'
|
41
|
+
else
|
42
|
+
file_list.each { |filename| load_workflow(filename) }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def block_until_interupt
|
47
|
+
log_message 'status' => 'info', 'message' => 'Ctrl-c to exit'
|
48
|
+
begin
|
49
|
+
while true
|
50
|
+
sleep 1
|
51
|
+
end
|
52
|
+
rescue Interrupt
|
53
|
+
log_message 'status' => 'info', 'message' => 'Exiting app...'
|
54
|
+
ensure
|
55
|
+
@workflows.keys.each { |filename| unload_workflow(filename) }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def load_workflow(filename)
|
60
|
+
workflow_filename = File.expand_path(filename)
|
61
|
+
log_message(
|
62
|
+
'status' => 'info',
|
63
|
+
'message' => "Loading workflow from #{workflow_filename}")
|
64
|
+
begin
|
65
|
+
workflow_definition = File.read(workflow_filename)
|
66
|
+
rescue => ex
|
67
|
+
exception "Couldn't read file #{workflow_filename}", ex
|
68
|
+
return
|
69
|
+
end
|
70
|
+
|
71
|
+
log_message(
|
72
|
+
'status' => 'info',
|
73
|
+
'message' => 'Setting up workflow processor')
|
74
|
+
begin
|
75
|
+
connector_settings = configatron.connectors.to_hash
|
76
|
+
credential_settings = configatron.credentials.to_hash
|
77
|
+
runtime = Runtime.new(connector_settings, credential_settings)
|
78
|
+
runtime.logger = self.method(:log_message)
|
79
|
+
rescue => ex
|
80
|
+
message = "Couldn't setup workflow process for #{workflow_filename}"
|
81
|
+
exception message, ex
|
82
|
+
end
|
83
|
+
|
84
|
+
@workflows[workflow_filename] = fork do
|
85
|
+
begin
|
86
|
+
log_message(
|
87
|
+
'status' => 'info',
|
88
|
+
'message' => "Starting #{workflow_filename}")
|
89
|
+
runtime.load(workflow_definition)
|
90
|
+
rescue => ex
|
91
|
+
exception "Couldn't load #{workflow_filename}", ex
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def unload_workflow(filename)
|
97
|
+
workflow_filename = File.expand_path(filename)
|
98
|
+
log_message(
|
99
|
+
'status' => 'info',
|
100
|
+
'message' => "Stopping #{workflow_filename}")
|
101
|
+
Process.kill('SIGINT', @workflows[workflow_filename])
|
102
|
+
end
|
103
|
+
|
104
|
+
def log_message(message_info)
|
105
|
+
case message_info['status']
|
106
|
+
when 'info' then info message_info
|
107
|
+
when 'success' then success message_info
|
108
|
+
when 'warn' then warn message_info
|
109
|
+
when 'debug' then debug message_info
|
110
|
+
else error message_info
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
data/lib/factor.rb
CHANGED
data/lib/factor/base.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# Primary Factor.io module
|
4
|
+
module Factor
|
5
|
+
|
6
|
+
String.send :define_method, :classify do
|
7
|
+
self.split('_').collect! { |w| w.capitalize }.join
|
8
|
+
end
|
9
|
+
|
10
|
+
String.send :define_method, :underscore do
|
11
|
+
self.gsub(/::/, '/')
|
12
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
13
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
14
|
+
.tr('-', '_')
|
15
|
+
.downcase
|
16
|
+
end
|
17
|
+
end
|
data/lib/listener.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'rest_client'
|
4
|
+
require 'websocket_manager'
|
5
|
+
|
6
|
+
module Factor
|
7
|
+
|
8
|
+
# Class Listener for integrating with connector service
|
9
|
+
class Listener
|
10
|
+
|
11
|
+
def initialize(url)
|
12
|
+
@url = url
|
13
|
+
end
|
14
|
+
|
15
|
+
def definition
|
16
|
+
get("#{@url}/definition")
|
17
|
+
end
|
18
|
+
|
19
|
+
def listener(listener_id, &block)
|
20
|
+
ws = listen("#{@url}/listeners/#{listener_id}")
|
21
|
+
ws
|
22
|
+
end
|
23
|
+
|
24
|
+
def action(action_id, &block)
|
25
|
+
ws = listen("#{@url}/actions/#{action_id}")
|
26
|
+
ws
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def post(uri_path, payload)
|
32
|
+
content = { 'payload' => MultiJson.dump(payload) }
|
33
|
+
JSON.parse(RestClient.post(uri_path, content))
|
34
|
+
end
|
35
|
+
|
36
|
+
def get(uri_path)
|
37
|
+
JSON.parse(RestClient.get(uri_path))
|
38
|
+
end
|
39
|
+
|
40
|
+
def delete(uri_path)
|
41
|
+
JSON.parse(RestClient.delete(uri_path))
|
42
|
+
end
|
43
|
+
|
44
|
+
def listen(uri_path)
|
45
|
+
WebSocketManager.new(uri_path)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/runtime.rb
ADDED
@@ -0,0 +1,192 @@
|
|
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
|
+
|
10
|
+
require 'listener'
|
11
|
+
require 'commands/base'
|
12
|
+
|
13
|
+
module Factor
|
14
|
+
# Runtime class is the magic of the server
|
15
|
+
class Runtime
|
16
|
+
attr_accessor :logger, :name, :description, :id, :instance_id, :connectors, :credentials
|
17
|
+
|
18
|
+
def initialize(connectors, credentials)
|
19
|
+
@workflow_spec = {}
|
20
|
+
@sockets = []
|
21
|
+
@instance_id = SecureRandom.hex(3)
|
22
|
+
@reconnect = true
|
23
|
+
|
24
|
+
trap 'SIGINT' do
|
25
|
+
info "Exiting '#{@instance_id}'"
|
26
|
+
@reconnect = false
|
27
|
+
@sockets.each { |s| s.close }
|
28
|
+
exit
|
29
|
+
end
|
30
|
+
|
31
|
+
@connectors = {}
|
32
|
+
connectors.each do |connector_id, connector_url|
|
33
|
+
@connectors[connector_id] = Listener.new(connector_url)
|
34
|
+
end
|
35
|
+
|
36
|
+
@credentials = {}
|
37
|
+
credentials.each do |connector_id, credential_settings|
|
38
|
+
@credentials[connector_id] = credential_settings
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def load(workflow_definition)
|
43
|
+
EM.run do
|
44
|
+
self.instance_eval(workflow_definition)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def workflow(key, value)
|
49
|
+
@workflow_spec[key] = value
|
50
|
+
@name = value if @key == 'name'
|
51
|
+
@id = value if @key == 'id'
|
52
|
+
@description = value if @key == 'description'
|
53
|
+
end
|
54
|
+
|
55
|
+
def listen(service_id, listener_id, params = {}, &block)
|
56
|
+
|
57
|
+
ws = @connectors[service_id.to_sym].listener(listener_id)
|
58
|
+
|
59
|
+
handle_on_open(service_id, listener_id, 'Listener', ws, params)
|
60
|
+
|
61
|
+
ws.on :close do |event|
|
62
|
+
error 'Listener disconnected'
|
63
|
+
if @reconnect
|
64
|
+
warn 'Reconnecting...'
|
65
|
+
sleep 3
|
66
|
+
ws.open
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
ws.on :message do |event|
|
71
|
+
listener_response = JSON.parse(event.data)
|
72
|
+
case listener_response['type']
|
73
|
+
when'start_workflow'
|
74
|
+
success "Workflow '#{service_id}::#{listener_id}' triggered"
|
75
|
+
error_handle_call(listener_response, &block)
|
76
|
+
when 'started'
|
77
|
+
success "Workflow '#{service_id}::#{listener_id}' started"
|
78
|
+
when 'fail'
|
79
|
+
error "Workflow '#{service_id}::#{listener_id}' failed to start"
|
80
|
+
when 'log'
|
81
|
+
listener_response['message'] = " #{listener_response['message']}"
|
82
|
+
log_message(listener_response)
|
83
|
+
else
|
84
|
+
error "Unknown listener response: #{listener_response}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
ws.on :retry do |event|
|
89
|
+
warn event[:message]
|
90
|
+
end
|
91
|
+
|
92
|
+
ws.on :error do |event|
|
93
|
+
err = 'Error during WebSocket handshake: Unexpected response code: 401'
|
94
|
+
if event.message == err
|
95
|
+
error "Sorry but you don't have access to this listener,
|
96
|
+
| either because your token is invalid or your plan doesn't
|
97
|
+
| support this listener"
|
98
|
+
else
|
99
|
+
error 'Failure in WebSocket connection to connector service'
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
ws.open
|
104
|
+
|
105
|
+
@sockets << ws
|
106
|
+
end
|
107
|
+
|
108
|
+
def run(service_id, action_id, params = {}, &block)
|
109
|
+
ws = @connectors[service_id.to_sym].action(action_id)
|
110
|
+
|
111
|
+
handle_on_open(service_id, action_id, 'Action', ws, params)
|
112
|
+
|
113
|
+
ws.on :error do |event|
|
114
|
+
error 'Connection dropped while calling action'
|
115
|
+
end
|
116
|
+
|
117
|
+
ws.on :message do |event|
|
118
|
+
action_response = JSON.parse(event.data)
|
119
|
+
case action_response['type']
|
120
|
+
when 'return'
|
121
|
+
ws.close
|
122
|
+
success "Action '#{service_id}::#{action_id}' responded"
|
123
|
+
error_handle_call(action_response, &block)
|
124
|
+
when 'fail'
|
125
|
+
ws.close
|
126
|
+
error " #{action_response['message']}"
|
127
|
+
error "Action '#{service_id}::#{action_id}' failed"
|
128
|
+
when 'log'
|
129
|
+
action_response['message'] = " #{action_response['message']}"
|
130
|
+
log_message(action_response)
|
131
|
+
else
|
132
|
+
error "Unknown action response: #{action_response}"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
ws.open
|
137
|
+
|
138
|
+
@sockets << ws
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
def handle_on_open(service_id, action_id, dsl_type, ws, params)
|
144
|
+
ws.on :open do |event|
|
145
|
+
params.merge!(@credentials[service_id.to_sym] || {})
|
146
|
+
success "#{dsl_type.capitalize} '#{service_id}::#{action_id}' called"
|
147
|
+
ws.send(params.to_json)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def error_handle_call(listener_response, &block)
|
152
|
+
block.call(listener_response['payload']) if block
|
153
|
+
rescue => ex
|
154
|
+
error "Error in workflow definition: #{ex.message}"
|
155
|
+
ex.backtrace.each do |line|
|
156
|
+
error " #{line}"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def success(msg)
|
161
|
+
log_message('type' => 'log', 'status' => 'success', 'message' => msg)
|
162
|
+
end
|
163
|
+
|
164
|
+
def warn(msg)
|
165
|
+
log_message('type' => 'log', 'status' => 'warn', 'message' => msg)
|
166
|
+
end
|
167
|
+
|
168
|
+
def error(msg)
|
169
|
+
log_message('type' => 'log', 'status' => 'error', 'message' => msg)
|
170
|
+
end
|
171
|
+
|
172
|
+
def info(msg)
|
173
|
+
log_message('type' => 'log', 'status' => 'info', 'message' => msg)
|
174
|
+
end
|
175
|
+
|
176
|
+
def log_message(message_info)
|
177
|
+
message_info['instance_id'] = @instance_id
|
178
|
+
message_info['workflow_id'] = @id
|
179
|
+
@logger.call(message_info) if @logger
|
180
|
+
end
|
181
|
+
|
182
|
+
def define_method_in_class(class_ref, class_id, method_id, &block)
|
183
|
+
class_name = class_id.classify
|
184
|
+
method_name = method_id.underscore
|
185
|
+
class_ref.class.instance_eval do
|
186
|
+
define_method(method_name) do |params = {}, &passed_block|
|
187
|
+
block.call(class_name, method_name, params, &passed_block)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'faye/websocket'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
# class for managing the web socket connections
|
7
|
+
class WebSocketManager
|
8
|
+
attr_accessor :keep_open, :events, :state
|
9
|
+
|
10
|
+
def initialize(uri, headers = {})
|
11
|
+
u = URI(uri)
|
12
|
+
u.scheme = 'wss' if u.scheme == 'https'
|
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
|
+
|
94
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: factor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.04
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,99 +9,117 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2014-07-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
15
|
+
name: commander
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
|
-
- -
|
19
|
+
- - ~>
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version:
|
21
|
+
version: 4.1.5
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
none: false
|
26
26
|
requirements:
|
27
|
-
- -
|
27
|
+
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version:
|
29
|
+
version: 4.1.5
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
|
-
name:
|
31
|
+
name: rest_client
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
33
33
|
none: false
|
34
34
|
requirements:
|
35
|
-
- -
|
35
|
+
- - ~>
|
36
36
|
- !ruby/object:Gem::Version
|
37
|
-
version: 1.
|
37
|
+
version: 1.7.3
|
38
38
|
type: :runtime
|
39
39
|
prerelease: false
|
40
40
|
version_requirements: !ruby/object:Gem::Requirement
|
41
41
|
none: false
|
42
42
|
requirements:
|
43
|
-
- -
|
43
|
+
- - ~>
|
44
44
|
- !ruby/object:Gem::Version
|
45
|
-
version: 1.
|
45
|
+
version: 1.7.3
|
46
46
|
- !ruby/object:Gem::Dependency
|
47
|
-
name:
|
47
|
+
name: faye-websocket
|
48
48
|
requirement: !ruby/object:Gem::Requirement
|
49
49
|
none: false
|
50
50
|
requirements:
|
51
|
-
- -
|
51
|
+
- - ~>
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version:
|
53
|
+
version: 0.7.2
|
54
54
|
type: :runtime
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
57
|
none: false
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ~>
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 0.7.2
|
62
62
|
- !ruby/object:Gem::Dependency
|
63
|
-
name:
|
63
|
+
name: colored
|
64
64
|
requirement: !ruby/object:Gem::Requirement
|
65
65
|
none: false
|
66
66
|
requirements:
|
67
|
-
- -
|
67
|
+
- - ~>
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version: 1.
|
69
|
+
version: '1.2'
|
70
70
|
type: :runtime
|
71
71
|
prerelease: false
|
72
72
|
version_requirements: !ruby/object:Gem::Requirement
|
73
73
|
none: false
|
74
74
|
requirements:
|
75
|
-
- -
|
75
|
+
- - ~>
|
76
76
|
- !ruby/object:Gem::Version
|
77
|
-
version: 1.
|
78
|
-
|
79
|
-
|
77
|
+
version: '1.2'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: configatron
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 4.2.0
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 4.2.0
|
94
|
+
description: CLI to manager workflows on Factor.io
|
95
|
+
email:
|
96
|
+
- maciej@factor.io
|
80
97
|
executables:
|
81
98
|
- factor
|
82
99
|
extensions: []
|
83
100
|
extra_rdoc_files: []
|
84
101
|
files:
|
85
|
-
-
|
86
|
-
-
|
87
|
-
-
|
88
|
-
-
|
89
|
-
- lib/channel/trigger.rb
|
90
|
-
- lib/cli/channel_task.rb
|
91
|
-
- lib/cli/command.rb
|
92
|
-
- lib/cli/credential_task.rb
|
93
|
-
- lib/cli/factor_task.rb
|
94
|
-
- lib/cli/workflow_task.rb
|
95
|
-
- lib/client/client.rb
|
96
|
-
- lib/factor.rb
|
102
|
+
- .gitignore
|
103
|
+
- Gemfile
|
104
|
+
- Gemfile.lock
|
105
|
+
- README.md
|
97
106
|
- bin/factor
|
98
|
-
|
107
|
+
- factor.gemspec
|
108
|
+
- lib/cli.rb
|
109
|
+
- lib/commands/base.rb
|
110
|
+
- lib/commands/workflows.rb
|
111
|
+
- lib/factor.rb
|
112
|
+
- lib/factor/base.rb
|
113
|
+
- lib/factor/version.rb
|
114
|
+
- lib/listener.rb
|
115
|
+
- lib/runtime.rb
|
116
|
+
- lib/websocket_manager.rb
|
117
|
+
homepage: https://factor.io
|
99
118
|
licenses: []
|
100
119
|
post_install_message:
|
101
120
|
rdoc_options: []
|
102
121
|
require_paths:
|
103
122
|
- lib
|
104
|
-
- lib
|
105
123
|
required_ruby_version: !ruby/object:Gem::Requirement
|
106
124
|
none: false
|
107
125
|
requirements:
|
@@ -119,5 +137,5 @@ rubyforge_project:
|
|
119
137
|
rubygems_version: 1.8.25
|
120
138
|
signing_key:
|
121
139
|
specification_version: 3
|
122
|
-
summary: Factor.io
|
140
|
+
summary: CLI to manager workflows on Factor.io
|
123
141
|
test_files: []
|
data/lib/channel/activity.rb
DELETED
data/lib/channel/channel.rb
DELETED
data/lib/channel/event.rb
DELETED
data/lib/channel/listener.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
|
3
|
-
module Factor
|
4
|
-
module Channel
|
5
|
-
class Listener
|
6
|
-
attr_accessor :workflow_id, :event
|
7
|
-
|
8
|
-
def start(user_id,channel_name,listener_name,event,workflow_name,params,&code)
|
9
|
-
end
|
10
|
-
|
11
|
-
def stop()
|
12
|
-
end
|
13
|
-
|
14
|
-
def initialize(workflow_id,event)
|
15
|
-
@workflow_id=workflow_id
|
16
|
-
@event=event
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
data/lib/channel/trigger.rb
DELETED
data/lib/cli/channel_task.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'cli/command'
|
3
|
-
|
4
|
-
module Factor
|
5
|
-
module CLI
|
6
|
-
class ChannelTask < Command
|
7
|
-
desc "list", "list all the channels"
|
8
|
-
#method_option :key, :alias=>"-k", :type=>:string, :desc=>"key reference"
|
9
|
-
def list
|
10
|
-
@client.get_channels().each do |channel|
|
11
|
-
puts "#{channel['name']} (#{channel['slug']})"
|
12
|
-
puts " Listeners:" if channel['listeners'].count>0
|
13
|
-
channel['listeners'].each do |listener|
|
14
|
-
puts " #{listener['name']}: #{listener['description']}"
|
15
|
-
end
|
16
|
-
puts " Actions:" if channel['actions'].count>0
|
17
|
-
channel['actions'].each do |action|
|
18
|
-
puts " #{action['name']}: #{action['description']}"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
desc "create DIRECTORY DEFINITION", "add a key and value for the credential"
|
24
|
-
method_option :organization, :alias=>"-o", :type=>:string, :desc=>"Organizoation to which this workflow belongs"
|
25
|
-
def create(directory,definition_file)
|
26
|
-
puts @client.create_channel(directory,definition_file,options[:organization])["notice"]
|
27
|
-
end
|
28
|
-
|
29
|
-
desc "delete NAME", "remove a workflow"
|
30
|
-
def delete(name)
|
31
|
-
puts @client.delete_channel(name)["notice"]
|
32
|
-
end
|
33
|
-
|
34
|
-
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
data/lib/cli/command.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'thor'
|
3
|
-
require 'thor/group'
|
4
|
-
require 'yaml'
|
5
|
-
require 'rest_client'
|
6
|
-
|
7
|
-
|
8
|
-
module Factor
|
9
|
-
module CLI
|
10
|
-
class Command < Thor
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
no_tasks do
|
15
|
-
|
16
|
-
def initialize(*vals)
|
17
|
-
@config_file_dir = File.expand_path("~/.factor")
|
18
|
-
@client = Factor::Client::Client.new
|
19
|
-
@client.login_token(get_config['token'])
|
20
|
-
super(*vals)
|
21
|
-
end
|
22
|
-
|
23
|
-
def save_config(config)
|
24
|
-
File.open(@config_file_dir,'w') do |file|
|
25
|
-
YAML::dump(config,file)
|
26
|
-
end
|
27
|
-
@client.login_token(get_config['token'])
|
28
|
-
end
|
29
|
-
|
30
|
-
def get_config
|
31
|
-
File.exists?(@config_file_dir) ? YAML::load_file(@config_file_dir) : {}
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
data/lib/cli/credential_task.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'cli/command'
|
3
|
-
|
4
|
-
module Factor
|
5
|
-
module CLI
|
6
|
-
class CredentialTask < Command
|
7
|
-
|
8
|
-
|
9
|
-
desc "create SERVICE NAME VALUE", "add a key and value for the credential"
|
10
|
-
method_option :key, :type=>:string, :desc=>"File reference containing the symmetric key for encryption"
|
11
|
-
method_option :organization, :type=>:string, :desc=>"Organizoation to which this credential belongs"
|
12
|
-
def create(service,name,value)
|
13
|
-
secret=nil
|
14
|
-
if options[:key]
|
15
|
-
secret=File.read(options[:key])
|
16
|
-
end
|
17
|
-
puts @client.create_credential(service,name,value,secret,options[:organization])
|
18
|
-
end
|
19
|
-
|
20
|
-
desc "list", "get all of the credential"
|
21
|
-
def list()
|
22
|
-
@client.get_credentials["value"].each do |service,values|
|
23
|
-
# puts "#{credential['service']} : #{credential['name']} (#{credential['slug']}): #{credential['value']}"
|
24
|
-
puts "#{service}:"
|
25
|
-
values.each do |key,values|
|
26
|
-
# puts "#{key['value']} (#{value['slug']}) : #{value}"
|
27
|
-
puts " #{key} (#{values['slug']}): ***"
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
desc "delete SERVICE NAME", "remove a value from the credentials bag"
|
33
|
-
def delete(service,name)
|
34
|
-
puts @client.delete_credential(service,name)
|
35
|
-
end
|
36
|
-
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
data/lib/cli/factor_task.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'cli/command'
|
3
|
-
require 'cli/workflow_task'
|
4
|
-
require 'cli/credential_task'
|
5
|
-
require 'cli/channel_task'
|
6
|
-
|
7
|
-
module Factor
|
8
|
-
module CLI
|
9
|
-
class FactorTask < Command
|
10
|
-
|
11
|
-
desc "login EMAIL TOKEN", "login with token"
|
12
|
-
# method_option :email, :alias=>"-e", :type=>:string, :desc=>"Email address for Factor account", :required=>true
|
13
|
-
# method_option :token, :alias=>"-t", :type=>:string, :desc=>"Token value to set", :required=>true
|
14
|
-
def login(email,token)
|
15
|
-
config = get_config
|
16
|
-
config['email']=email
|
17
|
-
config['token']=token
|
18
|
-
save_config(config)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
Factor::CLI::FactorTask.register(Factor::CLI::WorkflowTask,"workflow","workflow","start and list workflows")
|
25
|
-
Factor::CLI::FactorTask.register(Factor::CLI::ChannelTask,"channel","channel","install,uninstall, list channels")
|
26
|
-
Factor::CLI::FactorTask.register(Factor::CLI::CredentialTask,"credential","credential","manage remote credential store")
|
data/lib/cli/workflow_task.rb
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'cli/command'
|
3
|
-
|
4
|
-
module Factor
|
5
|
-
module CLI
|
6
|
-
class WorkflowTask < Command
|
7
|
-
|
8
|
-
desc "start WORKFLOW","start listener for workflow"
|
9
|
-
def start(workflow_name)
|
10
|
-
puts @client.start_workflow(workflow_name)["notice"]
|
11
|
-
end
|
12
|
-
|
13
|
-
desc "stop WORKFLOW","stop listener for workflow"
|
14
|
-
def stop(workflow_name)
|
15
|
-
puts @client.stop_workflow(workflow_name)["notice"]
|
16
|
-
end
|
17
|
-
|
18
|
-
desc "call WORKFLOW","call workflow"
|
19
|
-
method_option :parameters, :type=>:hash, :default=>{}, :required=>false
|
20
|
-
def call(workflow_name)
|
21
|
-
puts @client.call_workflow(workflow_name,options[:parameters])["notice"]
|
22
|
-
end
|
23
|
-
|
24
|
-
|
25
|
-
desc "list", "list all the workflows"
|
26
|
-
def list
|
27
|
-
@client.get_workflows().each do |workflow|
|
28
|
-
puts "#{workflow['name']} (#{workflow['slug']})"
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
desc "create NAME FILENAME", "add a key and value for the credential"
|
33
|
-
#method_option :key, :alias=>"-k", :type=>:string, :desc=>"key reference"
|
34
|
-
method_option :organization, :alias=>"-v", :type=>:string, :desc=>"Organizoation to which this workflow belongs"
|
35
|
-
def create(name,filename)
|
36
|
-
contents=File.open(File.expand_path(filename), "rb") {|f| f.read}
|
37
|
-
puts @client.create_workflow(name,contents,options[:organization])["notice"]
|
38
|
-
end
|
39
|
-
|
40
|
-
desc "delete NAME", "remove a workflow"
|
41
|
-
def delete(name)
|
42
|
-
puts @client.delete_workflow(name)
|
43
|
-
end
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
data/lib/client/client.rb
DELETED
@@ -1,151 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'rest_client'
|
3
|
-
require 'zip'
|
4
|
-
require 'zip/zipfilesystem'
|
5
|
-
require 'zip/zip'
|
6
|
-
require 'open-uri'
|
7
|
-
require 'digest/sha2'
|
8
|
-
require 'openssl'
|
9
|
-
require 'base64'
|
10
|
-
require 'json'
|
11
|
-
|
12
|
-
|
13
|
-
module Factor
|
14
|
-
module Client
|
15
|
-
class Client
|
16
|
-
attr_accessor :host
|
17
|
-
FACTOR_HOST = ENV["FACTOR_HOST"] || "factor.io"
|
18
|
-
|
19
|
-
def initialize(host=FACTOR_HOST)
|
20
|
-
@host=host
|
21
|
-
end
|
22
|
-
|
23
|
-
def login_token(token)
|
24
|
-
@token=token
|
25
|
-
end
|
26
|
-
|
27
|
-
def create_credential(service,name,value,secret=nil,organization=nil)
|
28
|
-
# this is a PUT not POST because it is technically editing, not creating a new one
|
29
|
-
credential = {:service=>service,:name=>name,:value=>value}
|
30
|
-
|
31
|
-
if secret
|
32
|
-
payload=value
|
33
|
-
sha256= Digest::SHA2.new(256)
|
34
|
-
encrypter = OpenSSL::Cipher.new("AES-256-CFB")
|
35
|
-
encrypter.encrypt
|
36
|
-
encrypter.key=Base64.encode64(sha256.digest(secret))
|
37
|
-
|
38
|
-
encrypted = Base64.encode64(encrypter.update(value) + encrypter.final)
|
39
|
-
credential[:value]=encrypted
|
40
|
-
credential[:encrypted]=true
|
41
|
-
end
|
42
|
-
path = !!organization ? "organizations/#{organization}/credentials" : "credentials"
|
43
|
-
rest_post(path,credential)
|
44
|
-
end
|
45
|
-
|
46
|
-
def get_credentials()
|
47
|
-
rest_get("credentials")
|
48
|
-
end
|
49
|
-
|
50
|
-
def delete_credential(service,name)
|
51
|
-
rest_delete("credentials",{:service=>service,:name=>name})
|
52
|
-
end
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
# untested
|
58
|
-
def create_workflow(key,definition,organization=nil)
|
59
|
-
path = !!organization ? "organizations/#{organization}/workflows" : "workflows"
|
60
|
-
rest_post(path,{:workflow=>{:name=>key,:definition=>definition}})
|
61
|
-
end
|
62
|
-
|
63
|
-
def get_workflows()
|
64
|
-
rest_get("workflows")
|
65
|
-
end
|
66
|
-
|
67
|
-
def delete_workflow(workflow_name)
|
68
|
-
rest_delete("workflows/#{workflow_name}")
|
69
|
-
end
|
70
|
-
|
71
|
-
def start_workflow(workflow_name)
|
72
|
-
rest_get("workflows/#{workflow_name}/state")
|
73
|
-
end
|
74
|
-
|
75
|
-
def stop_workflow(workflow_name)
|
76
|
-
rest_delete("workflows/#{workflow_name}/state")
|
77
|
-
end
|
78
|
-
|
79
|
-
def call_workflow(workflow_name,params)
|
80
|
-
rest_get("workflows/#{workflow_name}/call",params)
|
81
|
-
end
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
# channels
|
86
|
-
def create_channel(path,definition_file,organization=nil)
|
87
|
-
file=zip(File.expand_path(path))
|
88
|
-
definition = File.read(definition_file)
|
89
|
-
|
90
|
-
path = !!organization ? "organizations/#{organization}/channels" : "channels"
|
91
|
-
rest_post(path,{:zip=>file,:channel=>{:definition=>definition}})
|
92
|
-
end
|
93
|
-
|
94
|
-
def get_channels()
|
95
|
-
rest_get("channels")
|
96
|
-
end
|
97
|
-
|
98
|
-
def delete_channel(channel_name)
|
99
|
-
rest_delete("channels/#{channel_name}")
|
100
|
-
end
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
private
|
108
|
-
|
109
|
-
def zip(directory)
|
110
|
-
path=directory.dup
|
111
|
-
path.sub!(%r[/$],'')
|
112
|
-
zip_path = File.join(path,"..",File.basename(path))+'.zip'
|
113
|
-
FileUtils.rm zip_path, :force=>true
|
114
|
-
Zip::ZipFile.open(zip_path, 'w') do |zip|
|
115
|
-
Dir["#{path}/**/**"].reject{|f|f==zip_path}.each do |file|
|
116
|
-
zip.add(file.sub(path+'/',''),file)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
File.open(zip_path,'r')
|
120
|
-
end
|
121
|
-
|
122
|
-
def rest_get(path,params={})
|
123
|
-
params["auth_token"]=@token
|
124
|
-
|
125
|
-
param_string=params.map{|key,value| "#{key}=#{value}"}.join("&")
|
126
|
-
response=RestClient.get("http://#{@host}/#{path}.json?#{param_string}")
|
127
|
-
JSON.parse(response)
|
128
|
-
end
|
129
|
-
|
130
|
-
def rest_put(path,params={})
|
131
|
-
params["auth_token"]=@token
|
132
|
-
response=RestClient.put("http://#{@host}/#{path}.json",params)
|
133
|
-
JSON.parse(response)
|
134
|
-
end
|
135
|
-
|
136
|
-
def rest_post(path,params={})
|
137
|
-
response=RestClient.post("http://#{@host}/#{path}.json?auth_token=#{@token}",params)
|
138
|
-
JSON.parse(response)
|
139
|
-
end
|
140
|
-
|
141
|
-
def rest_delete(path,params={})
|
142
|
-
params["auth_token"]=@token
|
143
|
-
|
144
|
-
param_string=params.map{|key,value| "#{key}=#{value}"}.join("&")
|
145
|
-
response=RestClient.delete("http://#{@host}/#{path}.json?#{param_string}")
|
146
|
-
JSON.parse(response)
|
147
|
-
end
|
148
|
-
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|