factor 0.1.10 → 0.3.1

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.
@@ -5,7 +5,7 @@ module Factor
5
5
  class Listener
6
6
  attr_accessor :workflow_id, :event
7
7
 
8
- def start(params,&code)
8
+ def start(user_id,channel_name,listener_name,event,workflow_name,params,&code)
9
9
  end
10
10
 
11
11
  def stop()
@@ -1,36 +1,34 @@
1
1
  require 'rubygems'
2
2
  require 'cli/command'
3
- require 'zip/zip'
4
- require 'zip/zipfilesystem'
5
3
 
6
4
  module Factor
7
5
  module CLI
8
6
  class ChannelTask < Command
9
-
10
- desc "call CHANNEL METHOD TARGET","start a workflow"
11
- method_option :parameters, :type=>:hash, :default=>{}, :required=>false
12
- def call(channel, method, target)
13
-
14
- puts "not implemented"
15
-
16
- end
17
-
18
-
19
-
20
7
  desc "list", "list all the channels"
21
8
  #method_option :key, :alias=>"-k", :type=>:string, :desc=>"key reference"
22
9
  def list
23
- puts @client.get_channels
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
24
21
  end
25
22
 
26
- desc "add DIRECTORY DEFINITION", "add a key and value for the credential"
27
- def add(directory,definition_file)
28
- puts @client.add_channel(directory,definition_file)
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"]
29
27
  end
30
28
 
31
- desc "remove NAME", "remove a workflow"
32
- def remove(name)
33
- puts @client.remove_channel(name)
29
+ desc "delete NAME", "remove a workflow"
30
+ def delete(name)
31
+ puts @client.delete_channel(name)["notice"]
34
32
  end
35
33
 
36
34
 
data/lib/cli/command.rb CHANGED
@@ -16,7 +16,7 @@ module Factor
16
16
  def initialize(*vals)
17
17
  @config_file_dir = File.expand_path("~/.factor")
18
18
  @client = Factor::Client::Client.new
19
- @client.login_token(get_config[:token])
19
+ @client.login_token(get_config['token'])
20
20
  super(*vals)
21
21
  end
22
22
 
@@ -24,7 +24,7 @@ module Factor
24
24
  File.open(@config_file_dir,'w') do |file|
25
25
  YAML::dump(config,file)
26
26
  end
27
- @client.login_token(get_config[:token])
27
+ @client.login_token(get_config['token'])
28
28
  end
29
29
 
30
30
  def get_config
@@ -33,4 +33,4 @@ module Factor
33
33
  end
34
34
  end
35
35
  end
36
- end
36
+ end
@@ -6,24 +6,32 @@ module Factor
6
6
  class CredentialTask < Command
7
7
 
8
8
 
9
- desc "set SERVICE NAME VALUE", "add a key and value for the credential"
9
+ desc "create SERVICE NAME VALUE", "add a key and value for the credential"
10
10
  method_option :key, :type=>:string, :desc=>"File reference containing the symmetric key for encryption"
11
- def set(service,name,value)
12
- securet=nil
11
+ method_option :organization, :type=>:string, :desc=>"Organizoation to which this credential belongs"
12
+ def create(service,name,value)
13
+ secret=nil
13
14
  if options[:key]
14
15
  secret=File.read(options[:key])
15
16
  end
16
- puts @client.set_credential(service,name,value,secret)
17
+ puts @client.create_credential(service,name,value,secret,options[:organization])
17
18
  end
18
19
 
19
20
  desc "list", "get all of the credential"
20
21
  def list()
21
- puts @client.get_credentials()
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
22
30
  end
23
31
 
24
- desc "remove SERVICE NAME", "remove a value from the credentials bag"
25
- def remove(service,name)
26
- puts @client.remove_credential(service,name)
32
+ desc "delete SERVICE NAME", "remove a value from the credentials bag"
33
+ def delete(service,name)
34
+ puts @client.delete_credential(service,name)
27
35
  end
28
36
 
29
37
  end
@@ -1,6 +1,5 @@
1
1
  require 'rubygems'
2
2
  require 'cli/command'
3
- require 'cli/server_task'
4
3
  require 'cli/workflow_task'
5
4
  require 'cli/credential_task'
6
5
  require 'cli/channel_task'
@@ -14,15 +13,14 @@ module Factor
14
13
  # method_option :token, :alias=>"-t", :type=>:string, :desc=>"Token value to set", :required=>true
15
14
  def login(email,token)
16
15
  config = get_config
17
- config[:email]=email
18
- config[:token]=token
16
+ config['email']=email
17
+ config['token']=token
19
18
  save_config(config)
20
19
  end
21
20
  end
22
21
  end
23
22
  end
24
23
 
25
- Factor::CLI::FactorTask.register(Factor::CLI::ServerTask, "server","server","start and list servers")
26
24
  Factor::CLI::FactorTask.register(Factor::CLI::WorkflowTask,"workflow","workflow","start and list workflows")
27
25
  Factor::CLI::FactorTask.register(Factor::CLI::ChannelTask,"channel","channel","install,uninstall, list channels")
28
26
  Factor::CLI::FactorTask.register(Factor::CLI::CredentialTask,"credential","credential","manage remote credential store")
@@ -4,53 +4,43 @@ require 'cli/command'
4
4
  module Factor
5
5
  module CLI
6
6
  class WorkflowTask < Command
7
-
8
- desc "call WORKFLOW","start a workflow"
9
- method_option :parameters, :type=>:hash, :default=>{}, :required=>false
10
- def call(workflow_name)
11
- puts "starting workflow #{workflow_name} with options #{options.parameters.to_s}"
12
-
13
- engine = Factor::Runtime::Engine.new(get_config[:email],get_config[:token])
14
- id = engine.send_start_workflow(workflow_name,options.parameters)
15
-
16
- puts "workflow executed with id #{id}"
17
- end
18
-
19
- desc "listen WORKFLOW","start listener for workflow"
20
- def listen(workflow_name)
21
- puts "starting listener #{workflow_name}"
22
-
23
- engine = Factor::Runtime::Engine.new(get_config[:email],get_config[:token])
24
- engine.send_start_listener(workflow_name)
7
+
8
+ desc "start WORKFLOW","start listener for workflow"
9
+ def start(workflow_name)
10
+ puts @client.start_workflow(workflow_name)["notice"]
25
11
  end
26
12
 
27
13
  desc "stop WORKFLOW","stop listener for workflow"
28
14
  def stop(workflow_name)
29
- puts "starting listener #{workflow_name}"
30
-
31
- engine = Factor::Runtime::Engine.new(get_config[:email],get_config[:token])
32
- engine.send_stop_listener(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"]
33
22
  end
34
23
 
35
24
 
36
25
  desc "list", "list all the workflows"
37
- #method_option :key, :alias=>"-k", :type=>:string, :desc=>"key reference"
38
26
  def list
39
- puts @client.get_workflows
27
+ @client.get_workflows().each do |workflow|
28
+ puts "#{workflow['name']} (#{workflow['slug']})"
29
+ end
40
30
  end
41
31
 
42
- desc "add NAME FILENAME", "add a key and value for the credential"
32
+ desc "create NAME FILENAME", "add a key and value for the credential"
43
33
  #method_option :key, :alias=>"-k", :type=>:string, :desc=>"key reference"
44
- #method_option :value, :alias=>"-v", :type=>:string, :desc=>"values"
45
- def add(name,filename)
34
+ method_option :organization, :alias=>"-v", :type=>:string, :desc=>"Organizoation to which this workflow belongs"
35
+ def create(name,filename)
46
36
  contents=File.open(File.expand_path(filename), "rb") {|f| f.read}
47
- puts @client.add_workflow(name,contents)
37
+ puts @client.create_workflow(name,contents,options[:organization])["notice"]
48
38
  end
49
39
 
50
- # desc "remove NAME", "remove a workflow"
51
- # def remove(name)
52
- # puts @client.remove_workflow(name)
53
- # end
40
+ desc "delete NAME", "remove a workflow"
41
+ def delete(name)
42
+ puts @client.delete_workflow(name)
43
+ end
54
44
 
55
45
 
56
46
 
data/lib/client/client.rb CHANGED
@@ -7,6 +7,7 @@ require 'open-uri'
7
7
  require 'digest/sha2'
8
8
  require 'openssl'
9
9
  require 'base64'
10
+ require 'json'
10
11
 
11
12
 
12
13
  module Factor
@@ -19,102 +20,11 @@ module Factor
19
20
  @host=host
20
21
  end
21
22
 
22
- def register(email,password)
23
- end
24
-
25
- def login(email, password)
26
- end
27
-
28
23
  def login_token(token)
29
24
  @token=token
30
25
  end
31
26
 
32
- def load_workflows(engine,&code)
33
- code.call("downloading workflow list")
34
- workflows = rest_get("workflows")
35
- code.call("downloaded #{workflows.size} workflows")
36
- workflows.each do |workflow_info|
37
- code.call("parsing '#{workflow_info['name']}'")
38
- workflow_definition = JSON.parse(workflow_info['definition'])
39
- code.call("loading '#{workflow_info['name']}' into runtime")
40
- workflow=Factor::Runtime::Workflow.new(workflow_definition)
41
- engine.load_workflow(workflow)
42
- code.call("loading complete for '#{workflow_info['name']}'")
43
- end
44
- engine
45
- end
46
-
47
- def load_credentials(engine,secret=nil,&code)
48
- code.call("downloading credential list")
49
- credentials = rest_get("credentials")["value"]
50
- code.call("loading credentials")
51
-
52
- if secret
53
- code.call("decrypting credentials")
54
- decrypter = OpenSSL::Cipher.new("AES-256-CFB")
55
- sha256= Digest::SHA2.new(256)
56
- decrypter.key=Base64.encode64(sha256.digest(secret))
57
- decrypter.decrypt
58
- credentials.each do |service,creds|
59
- creds.each do |credential,value|
60
- if value["encrypted"]
61
- decrypted = decrypter.update(Base64.decode64(value["value"])) + decrypter.final
62
- credentials[service][credential]["value"]=decrypted
63
- credentials[service][credential]["encrypted"]=false
64
- end
65
- end
66
- end
67
- code.call("decrypting credentials complete")
68
- end
69
-
70
- engine.load_credentials(credentials)
71
-
72
- engine
73
- end
74
-
75
- def load_channels(engine,&code)
76
-
77
- # get list of channels
78
- code.call("downloading channels list")
79
- channels = rest_get("channels")
80
-
81
- #load each channel
82
- channels.each do |channel|
83
- if !channel['zip_url'].empty?
84
- code.call("downloading '#{channel['zip_url']}'")
85
- # URI of the zip file containing the module
86
- uri = URI.parse(channel['zip_url'])
87
-
88
- # temp file to store the zip for download
89
- #temp_file = Tempfile.new([uri.path.gsub(/\W+/,'-'), '.zip'], :encoding => 'ascii-8bit')
90
- temp_file = Tempfile.new([uri.to_s.gsub(/\W+/,'-'), '.zip'])
91
-
92
- # temp directory where zip will be decompressed
93
- unzip_dir=temp_file.path[0..-5]
94
-
95
- # download
96
- open(uri.to_s,"rb") do |bin|
97
- temp_file.print bin.read
98
- end
99
- temp_file.close
100
-
101
- code.call("unzipping '#{channel['name']}'")
102
- # unzip download zip into unzipped directory
103
- unzip(temp_file.path,unzip_dir)
104
-
105
- filename = unzip_dir + "/" + channel['module_name'].split(/(?=[A-Z])/).map{ |x| x.downcase }.join('_') + ".rb"
106
-
107
- code.call("loading '#{channel["name"]}' into engine")
108
- engine.load_channel(filename, channel)
109
- end
110
- end
111
-
112
-
113
- engine
114
- end
115
-
116
-
117
- def set_credential(service,name,value,secret=nil)
27
+ def create_credential(service,name,value,secret=nil,organization=nil)
118
28
  # this is a PUT not POST because it is technically editing, not creating a new one
119
29
  credential = {:service=>service,:name=>name,:value=>value}
120
30
 
@@ -129,49 +39,65 @@ module Factor
129
39
  credential[:value]=encrypted
130
40
  credential[:encrypted]=true
131
41
  end
132
-
133
- rest_post("credentials",credential)
42
+ path = !!organization ? "organizations/#{organization}/credentials" : "credentials"
43
+ rest_post(path,credential)
134
44
  end
135
45
 
136
46
  def get_credentials()
137
47
  rest_get("credentials")
138
48
  end
139
49
 
140
- def remove_credential(service,name)
50
+ def delete_credential(service,name)
141
51
  rest_delete("credentials",{:service=>service,:name=>name})
142
52
  end
143
53
 
144
54
 
145
55
 
146
56
 
147
- # workflows
148
- def add_workflow(key,definition)
149
- rest_post("workflows",{:workflow=>{:name=>key,:definition=>definition}})
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}})
150
61
  end
151
62
 
152
63
  def get_workflows()
153
64
  rest_get("workflows")
154
65
  end
155
66
 
156
- # def remove_workflow(name="")
157
- # rest_delete("workflows",{:name=>name})
158
- # end
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
+
159
83
 
160
84
 
161
85
  # channels
162
- def add_channel(path,definition_file)
86
+ def create_channel(path,definition_file,organization=nil)
163
87
  file=zip(File.expand_path(path))
164
88
  definition = File.read(definition_file)
165
- rest_post("channels",{:zip=>file,:channel=>{:definition=>definition}})
89
+
90
+ path = !!organization ? "organizations/#{organization}/channels" : "channels"
91
+ rest_post(path,{:zip=>file,:channel=>{:definition=>definition}})
166
92
  end
167
93
 
168
94
  def get_channels()
169
95
  rest_get("channels")
170
96
  end
171
97
 
172
- # def remove_channel(name="")
173
- # rest_delete("channels",{:name=>name})
174
- # end
98
+ def delete_channel(channel_name)
99
+ rest_delete("channels/#{channel_name}")
100
+ end
175
101
 
176
102
 
177
103
 
@@ -180,37 +106,6 @@ module Factor
180
106
 
181
107
  private
182
108
 
183
- def fix_github_zip_archive(file)
184
- Zip::ZipFile.open(file.path) do |zip|
185
- basedir = zip.entries.first.name
186
- zip.remove zip.entries.first
187
- zip.each do |entry|
188
- target = entry.name.sub(/^#{basedir}/,'')
189
- if entry.directory?
190
- zip.dir.mkdir(target)
191
- else
192
- zip.file.open(entry.name) do |istream|
193
- zip.file.open(target, 'w') do |ostream|
194
- ostream.write istream.read
195
- end
196
- end
197
- end
198
- zip.remove entry
199
- end
200
- end
201
- end
202
-
203
- def unzip(zip, unzip_dir, remove_after = false)
204
- Zip::ZipFile.open(zip) do |zip_file|
205
- zip_file.each do |f|
206
- f_path=File.join(unzip_dir, f.name)
207
- FileUtils.mkdir_p(File.dirname(f_path))
208
- zip_file.extract(f, f_path) unless File.exist?(f_path)
209
- end
210
- end
211
- FileUtils.rm(zip) if remove_after
212
- end
213
-
214
109
  def zip(directory)
215
110
  path=directory.dup
216
111
  path.sub!(%r[/$],'')
@@ -223,35 +118,34 @@ module Factor
223
118
  end
224
119
  File.open(zip_path,'r')
225
120
  end
226
-
121
+
227
122
  def rest_get(path,params={})
228
123
  params["auth_token"]=@token
229
124
 
230
125
  param_string=params.map{|key,value| "#{key}=#{value}"}.join("&")
231
-
232
- JSON.parse(RestClient.get("http://#{@host}/#{path}.json?#{param_string}"))
126
+ response=RestClient.get("http://#{@host}/#{path}.json?#{param_string}")
127
+ JSON.parse(response)
233
128
  end
234
129
 
235
130
  def rest_put(path,params={})
236
131
  params["auth_token"]=@token
237
-
238
- JSON.parse(RestClient.put("http://#{@host}/#{path}.json",params))
132
+ response=RestClient.put("http://#{@host}/#{path}.json",params)
133
+ JSON.parse(response)
239
134
  end
240
135
 
241
136
  def rest_post(path,params={})
242
- #params["auth_token"]=@token
243
-
244
- JSON.parse(RestClient.post("http://#{@host}/#{path}.json?auth_token=#{@token}",params))
137
+ response=RestClient.post("http://#{@host}/#{path}.json?auth_token=#{@token}",params)
138
+ JSON.parse(response)
245
139
  end
246
140
 
247
141
  def rest_delete(path,params={})
248
142
  params["auth_token"]=@token
249
143
 
250
144
  param_string=params.map{|key,value| "#{key}=#{value}"}.join("&")
251
-
252
- JSON.parse(RestClient.delete("http://#{@host}/#{path}.json?#{param_string}"))
145
+ response=RestClient.delete("http://#{@host}/#{path}.json?#{param_string}")
146
+ JSON.parse(response)
253
147
  end
254
148
 
255
149
  end
256
150
  end
257
- end
151
+ 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.1.10
4
+ version: 0.3.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -59,38 +59,6 @@ dependencies:
59
59
  - - ! '>='
60
60
  - !ruby/object:Gem::Version
61
61
  version: 2.0.2
62
- - !ruby/object:Gem::Dependency
63
- name: mustache
64
- requirement: !ruby/object:Gem::Requirement
65
- none: false
66
- requirements:
67
- - - ! '>='
68
- - !ruby/object:Gem::Version
69
- version: 0.99.4
70
- type: :runtime
71
- prerelease: false
72
- version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
- requirements:
75
- - - ! '>='
76
- - !ruby/object:Gem::Version
77
- version: 0.99.4
78
- - !ruby/object:Gem::Dependency
79
- name: eventmachine
80
- requirement: !ruby/object:Gem::Requirement
81
- none: false
82
- requirements:
83
- - - ! '>='
84
- - !ruby/object:Gem::Version
85
- version: 0.12.10
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: 0.12.10
94
62
  - !ruby/object:Gem::Dependency
95
63
  name: json
96
64
  requirement: !ruby/object:Gem::Requirement
@@ -107,22 +75,6 @@ dependencies:
107
75
  - - ! '>='
108
76
  - !ruby/object:Gem::Version
109
77
  version: 1.7.6
110
- - !ruby/object:Gem::Dependency
111
- name: amqp
112
- requirement: !ruby/object:Gem::Requirement
113
- none: false
114
- requirements:
115
- - - ! '>='
116
- - !ruby/object:Gem::Version
117
- version: 0.9.8
118
- type: :runtime
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- none: false
122
- requirements:
123
- - - ! '>='
124
- - !ruby/object:Gem::Version
125
- version: 0.9.8
126
78
  description: Friendly command-line interface and library for Factor
127
79
  email: maciej@factor.io
128
80
  executables:
@@ -139,17 +91,9 @@ files:
139
91
  - lib/cli/command.rb
140
92
  - lib/cli/credential_task.rb
141
93
  - lib/cli/factor_task.rb
142
- - lib/cli/server_task.rb
143
94
  - lib/cli/workflow_task.rb
144
95
  - lib/client/client.rb
145
96
  - lib/factor.rb
146
- - lib/runtime/attributes.rb
147
- - lib/runtime/engine.rb
148
- - lib/runtime/listener_message.rb
149
- - lib/runtime/message_bus.rb
150
- - lib/runtime/workflow.rb
151
- - lib/runtime/workflow_instance.rb
152
- - lib/runtime/workflow_step_messsage.rb
153
97
  - bin/factor
154
98
  homepage: http://rubygems.org/gems/factor
155
99
  licenses: []
@@ -172,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
172
116
  version: '0'
173
117
  requirements: []
174
118
  rubyforge_project:
175
- rubygems_version: 1.8.24
119
+ rubygems_version: 1.8.25
176
120
  signing_key:
177
121
  specification_version: 3
178
122
  summary: Factor.io Library and CLI
@@ -1,82 +0,0 @@
1
- require 'rubygems'
2
- require 'cli/command'
3
-
4
- module Factor
5
- module CLI
6
- class ServerTask < Command
7
- desc "start", "start the server"
8
- method_option :tags, :alias=>"-t", :type=>:hash, :desc=>"Optional tags to identify from workflow"
9
- method_option :channels, :type=>:array, :desc=>"Optional channel ruby file list for development"
10
- method_option :verbose, :type=>:boolean, :desc=>"Display everything"
11
- method_option :key, :type=>:string, :desc=>"File reference containing the symmetric key for encryption"
12
- def start
13
- engine = Factor::Runtime::Engine.new(get_config[:email],get_config[:token])
14
-
15
- options.tags.each {|tag,value| engine.tag(tag,value)} if options.tags?
16
-
17
- # load channels from server
18
- say "loading channels from server" if options.verbose?
19
- engine = @client.load_channels(engine) do |message|
20
- say " #{message}" if options.verbose?
21
- end
22
-
23
- # load channels from files
24
- if options.channels?
25
- options.channels.each do |file|
26
- full_file=File.expand_path(file)
27
- say " loading '#{full_file}'" if options.verbose?
28
- engine.load_channel(full_file)
29
- say " loaded '#{full_file}'" if options.verbose?
30
- end
31
- end
32
-
33
- say "loading channels complete" if options.verbose?
34
-
35
-
36
- # load workflows from server
37
- say "loading workflows from server" if options.verbose?
38
- engine = @client.load_workflows(engine) do |message|
39
- say " #{message}" if options.verbose?
40
- end
41
- say "loading workflows complete" if options.verbose?
42
-
43
- say "loading credentials from server" if options.verbose?
44
- securet=nil
45
- if options[:key]
46
- secret=File.read(options[:key])
47
- end
48
- engine = @client.load_credentials(engine,secret) do |message|
49
- say " #{message}" if options.verbose?
50
- end
51
- say "loading credentials complete" if options.verbose?
52
-
53
- say "starting the server...", :green
54
- message = engine.start
55
- if message.is_a?(Exception)
56
- say "[Unknown exception]", :red
57
- say " message:#{message}", :red
58
- say " backtrace:", :red
59
- message.backtrace.each {|line| say " #{line}", :red}
60
- else
61
- say "disconnecting...", :green
62
- end
63
-
64
- end
65
-
66
- desc "list", "list all the running servers"
67
- def list
68
- say "listing all servers"
69
- end
70
-
71
- desc "logs", "listen to incoming logs"
72
- def logs
73
- engine = Factor::Runtime::Engine.new(get_config[:email],get_config[:token])
74
- say "Listening...", :green
75
- engine.logs do |message|
76
- say "[#{message.route}] #{JSON.pretty_generate(message.body)}"
77
- end
78
- end
79
-
80
- end
81
- end
82
- end
@@ -1,26 +0,0 @@
1
- require 'rubygems'
2
-
3
- module Factor
4
- module Runtime
5
-
6
- class Attributes < Hash
7
-
8
- private
9
- def map_values(map,values)
10
- params=Hash.new
11
- map.each do |key,reference|
12
- params[key]=values[reference]
13
- end
14
- params
15
- end
16
-
17
- def format_map(parent,map)
18
- newmap=Hash.new
19
- map.each do |key,value|
20
- newmap["#{parent}::#{key}"]=value
21
- end
22
- newmap
23
- end
24
- end
25
- end
26
- end
@@ -1,256 +0,0 @@
1
- require 'rubygems'
2
- require 'factor'
3
- require 'mustache'
4
- #require 'facter'
5
-
6
-
7
- module Factor
8
- module Runtime
9
- class Engine
10
- attr_accessor :channel_modules, :workflows, :message_bus, :listeners
11
-
12
- # Engine needs modules that contain the code, workflows to run, and message bus for communication
13
- def initialize(username,token)
14
- @channel_modules={}
15
- @channel_definitions=[]
16
- @workflows = {}
17
- @message_bus = MessageBus.new(username,token)
18
- @credentials = {}
19
- @tags = {}
20
- @listeners={}
21
- end
22
-
23
- def tag(key,value)
24
- @tags[key]=value
25
- end
26
-
27
- # load the channel by referencing the .rb file
28
- # the filename is lowercase with "_" for spaces
29
- # and the module inside must be camal cased to match
30
- # once loaded it is in the channel_modules Hash
31
- def load_channel filename, definition
32
- filename = File.absolute_path(File.expand_path(filename.path)) if filename.is_a?(File) # just in case someone passes in a File not a String (i.e. me)
33
- require filename
34
- # channel_module_name = File.basename(filename).gsub('.rb','').split('_').map{|ea| ea.capitalize}.join('')
35
- channel_module_name = definition['module_name']
36
- channel_module= self.class.const_get(channel_module_name)
37
- @channel_modules[channel_module_name]=channel_module
38
- @channel_definitions << definition
39
- end
40
-
41
- def load_credentials credentials,secret=nil
42
- credentials.each do |service_name,services|
43
- @credentials[service_name] = {} if !@credentials.include?(service_name)
44
- services.each do |credential_name,credential|
45
- @credentials[service_name][credential_name] = credential["value"]
46
- end
47
- end
48
-
49
- # @credentials["credentials"] = credentials
50
- end
51
-
52
- # adds the workflow to the workflows list
53
- # the object must be a Workflow type
54
- def load_workflow workflow
55
- @workflows[workflow.name] = workflow
56
- end
57
-
58
- def logs routing_key="#", &code
59
- @message_bus.start do
60
- @message_bus.listen routing_key do |message|
61
- code.call message
62
- end
63
- end
64
- end
65
-
66
- def send_start_workflow workflow, params, keep_open=false
67
- instance_id=SecureRandom.hex
68
-
69
- @message_bus.start do
70
- message = WorkflowStepMessage.new
71
- message.position << "start"
72
- message.workflow=workflow
73
- message.add_values params
74
- message.workflow_instance_id= instance_id
75
- @message_bus.send message, !keep_open
76
- end
77
- instance_id
78
- end
79
-
80
- def send_start_listener workflow_name, keep_open=false
81
- @message_bus.start do
82
- message = ListenerMessage.new("start",workflow_name)
83
- @message_bus.send message, !keep_open
84
- end
85
- end
86
-
87
- def send_stop_listener workflow_name, keep_open=false
88
- @message_bus.start do
89
- message = ListenerMessage.new("stop",workflow_name)
90
- @message_bus.send message, !keep_open
91
- end
92
- end
93
-
94
- # start your engines. vroom vrooooom!
95
- def start
96
- begin
97
- @message_bus.start do
98
- @message_bus.listen("listener") do |payload|
99
- message = ListenerMessage.new
100
- message.from_queue(payload)
101
-
102
- listener=@workflows[message.workflow].definition["listener"]
103
- params = render_template(listener["params"],@credentials)
104
-
105
- if message.command=="start"
106
- start_listening(listener["channel"],listener["name"],listener["event"],message.workflow,params)
107
- else
108
- stop_listening(listener["channel"],listener["name"],listener["event"],message.workflow)
109
- end
110
- end
111
-
112
- @message_bus.listen("workflow") do |payload|
113
- step = WorkflowStepMessage.new
114
- step.from_queue(payload)
115
- if @workflows.include? step.workflow
116
- workflow = @workflows[step.workflow]
117
- activity = workflow.get_activity(step.position)
118
- if !activity.nil?
119
- action = activity["action"]
120
- channel = activity["channel"]
121
- target = activity["target"]
122
- params_template = activity["params"]
123
-
124
- # if match(target)
125
- values = step.body.merge(@credentials)
126
- # puts "Values: #{values.inspect}"
127
-
128
- # this maps the input values passed in with the templated defined in the workflow
129
- params = render_template(params_template,values)
130
- puts "content: #{params}"
131
- event = call_channel_method(channel,action,params)
132
- response_message = step.respond(event.params,event.class.name.split("::").last)
133
-
134
- @message_bus.send response_message
135
- # end
136
- else
137
- puts "[error] no activity found for position '#{step.position}'"
138
- end
139
- else
140
- # workflow doesn't exist
141
- puts "[warning] '#{step.workflow}' workflow wasn't found"
142
- end
143
- end
144
- end
145
- rescue SystemExit, Interrupt
146
- puts "done"
147
- rescue Exception => ex
148
- puts ex
149
- end
150
- end
151
-
152
- def call_channel_method(channel_name,action_name,params)
153
- channel_module_name = get_channel_module(channel_name)
154
- channel_module = @channel_modules[channel_module_name]
155
- action_class_name = get_class_name("actions",channel_name,action_name)
156
- command = channel_module.const_get(action_class_name)
157
- command.new.do_work(params)
158
- end
159
-
160
- def start_listening(channel_name,listener_name,event,workflow_name,params)
161
- channel_module_name = get_channel_module(channel_name)
162
- channel_module = @channel_modules[channel_module_name]
163
- listener_class_name = get_class_name("listeners",channel_name,listener_name)
164
- command = channel_module.const_get(listener_class_name)
165
- job_info = {:channel_name=>channel_name,:listener_name=>listener_name,:workflow_name=>workflow_name,:event=>event}.to_json
166
-
167
- @job = Thread.new do
168
- puts "Creating listener #{channel_name}::#{listener_name} on event '#{event}' => #{workflow_name}"
169
- listener=command.new(workflow_name,event)
170
-
171
- puts "Starting listener thread #{params}"
172
- listener.start params do |event_params|
173
- send_start_workflow workflow_name, event_params
174
- end
175
- end
176
- @listeners[job_info]=@job
177
- end
178
-
179
- def stop_listening(channel_name,listener_name,event,workflow_name)
180
- # channel_module_name = get_channel_module(channel_name)
181
- # channel_module = @channel_modules[channel_module_name]
182
- # listener_class = get_class_name("listeners",channel_name,listener_name)
183
- # command = channel_module.const_get(listener_name)
184
- job_info = {:channel_name=>channel_name,:listener_name=>listener_name,:workflow_name=>workflow_name,:event=>event}.to_json
185
-
186
- @listeners[job_info].kill if @listeners.include?(job_info)
187
-
188
- end
189
-
190
- private
191
-
192
- def get_channel_module(channel_name)
193
- @channel_definitions.select { |channel_definition| channel_definition['name']==channel_name }.first['module_name']
194
- end
195
-
196
- # def get_action_class(channel_name,action_name)
197
- # @channel_definitions.each do |channel_definition|
198
- # if channel_definition['name']==channel_name
199
- # channel_definition['actions'].each do |action|
200
- # return action["class_name"] if action['name']==action_name
201
- # end
202
- # end
203
- # end
204
- # end
205
-
206
- # def get_listener_class(channel_name,listener_name)
207
- # @channel_definitions.each do |channel_definition|
208
- # if channel_definition['name']==channel_name
209
- # channel_definition['listeners'].each do |listener|
210
- # return listener["class_name"] if listener['name']==listener_name
211
- # end
212
- # end
213
- # end
214
- # end
215
-
216
- def get_class_name(type,channel_name,class_name)
217
- @channel_definitions.each do |channel_definition|
218
- if channel_definition['name']==channel_name
219
- channel_definition[type].each do |definition|
220
- return definition["class_name"] if definition['name']==class_name
221
- end
222
- end
223
- end
224
- end
225
-
226
- def render_template(source,values)
227
- #params = Hash.new
228
-
229
- if source.is_a?(Hash)
230
- params = Hash.new
231
- source.each do |key,template|
232
- params[key]=render_template(template,values)
233
- end
234
- else
235
- params = Mustache.render(source,values)
236
- end
237
-
238
- # activity_params.each do |key,template|
239
- # params[key]=Mustache.render(template,values)
240
- # end
241
- params
242
- end
243
-
244
- # def match(target)
245
- # return true if target==nil || target==""
246
- # facts={}
247
- # #Facter.each {|k,v| facts[k]=v}
248
- # @tags.each {|k,v| facts[k]=v}
249
- # key,value=target.split("=")
250
- # facts[key]==value
251
- # end
252
-
253
-
254
- end
255
- end
256
- end
@@ -1,42 +0,0 @@
1
- require 'rubygems'
2
- require 'json/ext'
3
-
4
- module Factor
5
- module Runtime
6
- class ListenerMessage
7
- attr_accessor :command,:workflow
8
- def initialize(command=nil,workflow=nil)
9
- @command=command
10
- @workflow=workflow
11
- end
12
-
13
-
14
- def route
15
- # "#{workflow}.#{position.join('.')}"
16
- "listener"
17
- end
18
-
19
- def payload
20
- JSON.generator = JSON::Ext::Generator
21
- obj = {"command"=>@command, "workflow"=>@workflow}
22
- JSON.generate(obj)
23
- end
24
-
25
- def from_queue payload
26
- message=JSON.parse(payload)
27
- @command=message["command"]
28
- @workflow=message["workflow"]
29
- end
30
- # def from_queue payload
31
- #
32
- # @workflow = message["workflow"]
33
- # @position=message["position"]
34
- # @body=message["body"]
35
- # @workflow_instance_id=message["workflow_instance_id"]
36
- # @step_id=message["step_id"]
37
- # @last_step_id=message["last_step_id"]
38
- # end
39
-
40
- end
41
- end
42
- end
@@ -1,59 +0,0 @@
1
- require 'rubygems'
2
- require 'eventmachine'
3
- require 'amqp'
4
-
5
- module Factor
6
- module Runtime
7
- class MessageBus
8
- attr_accessor :host, :vhost, :username, :token, :connection, :channel, :exchange, :queue
9
-
10
- def initialize(email,token)
11
- @host = "queue.factor.io"
12
- @vhost = email
13
- @username=email
14
- @token=token
15
- end
16
-
17
-
18
- # Creates the connection and creates a topic exchange
19
- # An exchange references a place to send messages to
20
- # the exchange routes it to the queues based on the route_key
21
- def start(topic="factor",&code)
22
- EventMachine.run do
23
- #connection_settings={:host=>@host,:user=>@username,:password=>@token,:vhost=>@vhost}
24
- connection_settings={:host=>@host}
25
- @connection = AMQP.connect(connection_settings)
26
- @channel = AMQP::Channel.new(connection)
27
- @exchange = @channel.topic(topic,:auto_delete=>true) # new topic exchange
28
- code.call
29
- end
30
- end
31
-
32
- # creates a new queue to listen to the topic exchange
33
- def listen(routing_key="#",&code)
34
- queue_name=SecureRandom.hex
35
- @queue = @channel.queue(queue_name)
36
- @queue.bind(@exchange, :routing_key=>routing_key) # bind queue to the Exchange
37
-
38
- @queue.subscribe do |headers,payload|
39
- # code.call(headers.routing_key,payload)
40
- puts "[Received Message (#{headers.routing_key})] #{payload.inspect}"
41
- code.call(payload)
42
- end
43
- end
44
-
45
- def send(message,close=false)
46
- puts "[Sending Message (#{message.route})] #{message.payload.inspect}"
47
- @exchange.publish(message.payload,:routing_key => message.route)
48
- EM.add_timer(1, Proc.new { close}) if close
49
- end
50
-
51
- def close
52
- @connection.close{ EventMachine.stop }
53
-
54
- end
55
-
56
- end
57
-
58
- end
59
- end
@@ -1,25 +0,0 @@
1
- require 'rubygems'
2
-
3
- module Factor
4
- module Runtime
5
- class Workflow
6
- attr_accessor :definition
7
- attr_reader :name
8
-
9
- def initialize(definition)
10
- @definition=definition
11
- @name = @definition["name"]
12
- end
13
-
14
- def get_activity(position)
15
- last_position=@definition
16
- position.each do |section|
17
- return nil if last_position[section].nil?
18
- last_position=last_position[section]
19
- end
20
- last_position
21
- end
22
-
23
- end
24
- end
25
- end
@@ -1,28 +0,0 @@
1
- require 'rubygems'
2
-
3
- module Factor
4
- module Runtime
5
- class WorkflowInstance
6
- attr_accessor :id, :attributes, :start_message, :workflow
7
-
8
- def initialize(workflow,attributes)
9
- @id=SecureRandom.hex
10
- @attributes = attributes
11
- @workflow = workflow
12
- @start_message = Message.new(@attributes)
13
- @start_message.workflow_instance_id=@id
14
- @start_message.position=["start"]
15
- end
16
-
17
- def get_activity(position)
18
- last_position=@workflow
19
- position.each do |section|
20
- return nil if last_position[section].nil?
21
- last_position=last_position[section]
22
- end
23
- last_position
24
- end
25
-
26
- end
27
- end
28
- end
@@ -1,63 +0,0 @@
1
- require 'rubygems'
2
- require 'json/ext'
3
-
4
- module Factor
5
- module Runtime
6
- class WorkflowStepMessage
7
- attr_accessor :body, :workflow, :workflow_instance_id, :step_id, :last_step_id, :position
8
- def initialize
9
- @step_id=SecureRandom.hex
10
- # @workflow = workflow
11
- @body = Hash.new
12
- @position = Array.new
13
- end
14
-
15
- def respond(params, event)
16
- m = WorkflowStepMessage.new
17
- m.body = @body
18
- m.workflow_instance_id=@workflow_instance_id
19
- m.last_step_id = @step_id
20
- m.workflow = @workflow
21
- m.position = @position
22
- m.position << "on"
23
- m.position << event
24
- m.add_values(params)
25
- m
26
- end
27
-
28
- def add_values(values)
29
- current=@body
30
- position.each do |key|
31
- current[key]={} if !current.include?(key)
32
- current=current[key]
33
- end
34
- values.each do |key,value|
35
- current[key]=value
36
- end
37
-
38
- end
39
-
40
- def route
41
- # "#{workflow}.#{position.join('.')}"
42
- "workflow"
43
- end
44
-
45
- def payload
46
- JSON.generator = JSON::Ext::Generator
47
- obj = {"body"=>@body, "workflow_instance_id"=>@workflow_instance_id, "step_id"=>@step_id, "last_step_id"=>@last_step_id, "workflow"=>@workflow,"position"=>@position}
48
- JSON.generate(obj)
49
- end
50
-
51
- def from_queue payload
52
- message=JSON.parse(payload)
53
- @workflow = message["workflow"]
54
- @position=message["position"]
55
- @body=message["body"]
56
- @workflow_instance_id=message["workflow_instance_id"]
57
- @step_id=message["step_id"]
58
- @last_step_id=message["last_step_id"]
59
- end
60
-
61
- end
62
- end
63
- end