factor 0.3.1 → 0.5.04
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Code Climate](https://codeclimate.com/github/factor-io/factor.png)](https://codeclimate.com/github/factor-io/factor)
|
2
|
+
[![Test Coverage](https://codeclimate.com/github/factor-io/factor/coverage.png)](https://codeclimate.com/github/factor-io/factor)
|
3
|
+
[![Build Status](https://travis-ci.org/factor-io/factor.svg)](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
|