broker 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 06703642e42f471850e220882e24d2e0b3cd54b7
4
- data.tar.gz: 0996bd3519546efc94b153e53ba5e42da45e6570
3
+ metadata.gz: 616efefdc628ec749aad198ade1c21133dc7081a
4
+ data.tar.gz: c848d5e2c0460677b12f8a57e153216bf917dcfc
5
5
  SHA512:
6
- metadata.gz: 18fdb75b3817fa85cfb7f7a23b0daa644fcfcc2bf471bc0a4ee61914d253751dc5abaa813322f406481867a838687c4301feb34d737790d9167ba49fc0e63863
7
- data.tar.gz: 463b7df4a4699a1c03295579ce154b88841bfb57cf5d26c4d85afd52c577b51668b9234f6b5ddb5c5aab6eb53424056d9ddf8b5c8e48c8770785720bf78d916a
6
+ metadata.gz: 3ed6ed4619c2369e1ac87a077327c70def073b2c352b010da89fc8e09b4d1ba15493a05cdce4971234c06134adba9fac3e757e39f27aa31aa1f5ef5896aaf0c8
7
+ data.tar.gz: 75c5eaae7ccba5d8291a43fb914497f1cfeb215162b17b9b0c65cd4568365d5aa8ed355badf5fd3602bc47d70d3b8d8bf235998007bacf6dde0bc2eb3be3fd42
data/README.md CHANGED
@@ -22,7 +22,7 @@ Or install it yourself as:
22
22
  $ gem install broker
23
23
  ```
24
24
 
25
- ## Setup
25
+ ## Initial Setup
26
26
 
27
27
  Setup the initializers, config files, and structure by:
28
28
 
@@ -34,9 +34,78 @@ The following files will be generated for you:
34
34
 
35
35
  ```ruby
36
36
  /config/secrets.yml
37
- /config/quickbase_tables
37
+ /config/quickbase_tables.yml
38
38
  /config/initializers/broker.rb
39
- ````
39
+ ```
40
+
41
+ Enter your Quickbase connection credentials in `/config/secrets.yml` file.
42
+
43
+ ```ruby
44
+ # Place your quickbase credentials here
45
+ # Do not check into version control, keep your secrets safe
46
+ #
47
+ # ORG -> if you are using a custom subdomain such as mycompany.quickbase.com,
48
+ # you will set ORG: mycompany, otherwise www is the default
49
+ #
50
+ #
51
+ ORG: www
52
+ USERNAME: billy_the_kid@user.com
53
+ PASSWORD: mickeymouse
54
+ ```
55
+
56
+ ## Generate the Queue
57
+
58
+ Once running, Broker will poll for new files in a directory structure that resembles your Quickbase apps and tables. Before starting Broker up, you need to generate the Queue.
59
+
60
+ 1. Edit the initializer file in `/config/initializers/broker.rb` to customize the location you want the Queue to be generated, along with changing the default locations for your secrets.yml and quickbase_tables.yml to be.
61
+
62
+ 2. Open up your `quickbase_tables.yml` configuration file and enter your quickbase app/table structure. This convention must be strictly followed as it is the way Broker is able to connect with Quickbase. Create as many app config blocks as you need.
63
+
64
+ ```ruby
65
+ tracker: # Give each of your apps a simple unique key name
66
+ name: Job Tracker # This is the official Quickbase App Name
67
+ token: your_apps_token_for_tracker # Quickbase API token you assigned to the app
68
+ tables: # Generic tables key that points to your tables
69
+ main: table_dbid # Give each of your tables a simple unique key name, pointing to the Quickbase table dbid
70
+ people: table_dbid
71
+ ```
72
+
73
+ 3. Once your `quickbase_table.yml` configuration is done, you are ready to generate the Broker Queue.
74
+
75
+ ```ruby
76
+ $ broker queue -c
77
+
78
+ $ broker queue -u
79
+ ```
80
+
81
+ Your Queue has been created using your `quickbase_tables.yml` settings.
82
+
83
+ ```ruby
84
+ broker_queue/tracker/main
85
+ broker_queue/tracker/people
86
+
87
+ broker_processed
88
+ ```
89
+
90
+ ## Boot Broker
91
+
92
+ ```ruby
93
+ $ broker start -s
94
+ ```
95
+
96
+ Broker will be running and watching for new files inside your Queue. New files will be swept up, imported to Quickbase, and then moved out to the Processed folder.
97
+
98
+
99
+ ## In the Works
100
+
101
+ 1. Broker activity - successful/failed import records save to a MongoDB
102
+
103
+ 2. Web UI to monitor Broker activity
104
+
105
+ 3. Mount Broker to a Rails App
106
+
107
+ 4.
108
+
40
109
 
41
110
  ## Contributing
42
111
 
@@ -3,7 +3,7 @@ require 'broker/version'
3
3
  require 'yaml'
4
4
 
5
5
  module Broker
6
- NAME = "Broker: Quickbase Data Intake"
6
+ NAME = "Broker"
7
7
 
8
8
  DEFAULTS = {
9
9
  secrets_path: 'config/secrets.yml',
@@ -12,6 +12,7 @@ module Broker
12
12
  poll_interval: 300,
13
13
  queue: 'broker_queue',
14
14
  processed_path: 'broker_processed',
15
+ failed_path: 'broker_failed',
15
16
  file_ext: :csv,
16
17
  enqueued: []
17
18
  }
@@ -96,12 +97,16 @@ module Broker
96
97
  options[:processed_path] = pa
97
98
  end
98
99
 
100
+ def self.failed_path=(pa)
101
+ options[:failed_path] = pa
102
+ end
103
+
99
104
  def self.poll_interval=(val)
100
105
  options[:poll_interval] = val
101
106
  end
102
107
 
103
- def file_ext=(val)
104
- options[:file_ext] = val if [:csv, :tab].include? val
108
+ def self.file_ext=(val)
109
+ options[:file_ext] = val if [:csv, :txt].include? val
105
110
  end
106
111
 
107
112
  end
@@ -6,11 +6,11 @@ end
6
6
 
7
7
  module Broker
8
8
  class Application
9
-
9
+
10
10
  def self.boot!
11
11
  puts "starting up"
12
12
  puts "watching for files in #{Broker.options[:queue]}"
13
13
  end
14
14
 
15
- end
16
- end
15
+ end
16
+ end
@@ -154,9 +154,17 @@ module Broker
154
154
  say ""
155
155
  say "*************************************************"
156
156
  say ""
157
- say "Update your Broker Q by running:"
157
+ say "Create your Broker Q by running:"
158
158
  say ""
159
- say "$ broker update_q"
159
+ say "$ broker queue -c"
160
+ say ""
161
+ say "Populate your Broker Q by running:"
162
+ say ""
163
+ say "$ broker queue -u"
164
+ say ""
165
+ say "Boot Broker"
166
+ say ""
167
+ say "$ broker start -s"
160
168
  say ""
161
169
  end
162
170
 
@@ -194,6 +202,5 @@ module Broker
194
202
  end && true
195
203
  end
196
204
 
197
-
198
205
  end
199
206
  end
@@ -1499,6 +1499,7 @@ class Client
1499
1499
 
1500
1500
  # shenley - gem's method was flawed and removed nil values, used CSV lib to separate correctly
1501
1501
  def splitString(string, fieldSeparator = ",")
1502
+ string &&= string.encode("UTF-8", "binary", :invalid => :replace, :undef => :replace, :replace => "")
1502
1503
  CSV.parse(string, :col_sep => fieldSeparator).shift
1503
1504
  end
1504
1505
 
@@ -4475,8 +4476,12 @@ class Client
4475
4476
  csvdata = ""
4476
4477
  validLines.each{ |line|
4477
4478
  if line
4478
- csvdata << line.join( ',' )
4479
- csvdata << "\n"
4479
+ csvdata << line.to_csv
4480
+ # these 2 lines don't handle columns with commas, ex "My field, needs escaped"
4481
+ # using CSV library's to_csv will correctly escape any quoted fields to preserve the commas
4482
+ # and not mistake them for wrong fields
4483
+ #csvdata << line.join( ',' )
4484
+ #csvdata << "\n"
4480
4485
  end
4481
4486
  }
4482
4487
  clist = targetFieldIDs.join( '.' )
@@ -1,7 +1,7 @@
1
1
  require 'broker/qb/session'
2
2
 
3
3
  module Broker
4
- class Export < Broker::QB::Session
4
+ class Export < Broker::Session
5
5
 
6
6
  def initialize(opt={})
7
7
  super(opt)
@@ -25,15 +25,18 @@ module Broker
25
25
  while !@queue.empty?
26
26
  payload = @queue.next
27
27
  results = payload.commit(session)
28
- puts "#{results.inspect}"
29
- @queue.success(payload)
28
+ results ? @queue.success(payload) : @queue.failure(payload)
30
29
  # Need to handle failure and add payload to queues failed
31
30
  # Need to handle success and move file out and alert queue
32
31
  end
33
32
  puts "Session Terminated: #{session.sign_out}"
33
+ puts "****************************"
34
+ puts "Queue Status"
35
+ puts "****************************"
34
36
  puts "Processed: #{@queue.processed}"
35
37
  puts "Pending: #{@queue.pending.inspect}"
36
38
  puts "Failed: #{@queue.failed.inspect}"
39
+ puts "****************************"
37
40
  end
38
41
 
39
42
  end
@@ -3,7 +3,7 @@ require 'ostruct'
3
3
  module Broker
4
4
  class Payload
5
5
 
6
- attr_reader :pkg
6
+ attr_accessor :pkg, :results
7
7
 
8
8
  class << self
9
9
 
@@ -36,6 +36,7 @@ module Broker
36
36
  end
37
37
 
38
38
  def initialize(opt={})
39
+ @response = nil
39
40
  @pkg = OpenStruct.new(file: opt[:file],
40
41
  dbid: opt[:dbid],
41
42
  app: opt[:app],
@@ -44,7 +45,42 @@ module Broker
44
45
  end
45
46
 
46
47
  def commit(session)
47
- session.fire_event(self)
48
+ begin
49
+ capture_response(session.fire_event(self))
50
+ rescue => e
51
+ # Need to log the failed attempt somewhere
52
+ puts "#{pkg.file} failed to import!"
53
+ puts e.message
54
+ puts e.backtrace.join("\n")
55
+ capture_response("Failed to Import")
56
+ return nil
57
+ end
58
+ true
59
+ end
60
+
61
+ def capture_response(results)
62
+ if results.respond_to?(:capitalize)
63
+ @response = results
64
+ else
65
+ parse_response(results)
66
+ puts "Captured Payload Result: #{@response.inspect}"
67
+ end
68
+ end
69
+
70
+ # Quickbase API returns 2D array with results
71
+ # First Element -> [num created, num imported, num updated, raw xml, update_id]
72
+ # Second Element -> [invalid records that didn't get imported]
73
+
74
+ def parse_response(results)
75
+ details = results.shift
76
+ res = {
77
+ records_imported: details[1],
78
+ records_created: details[0],
79
+ records_updated: details[2],
80
+ update_id: details[4],
81
+ invalid_records: results.shift
82
+ }
83
+ @response = res
48
84
  end
49
85
 
50
86
  end
@@ -1,7 +1,9 @@
1
1
  require 'broker/payload'
2
+ require 'broker/utility'
2
3
 
3
4
  module Broker
4
5
  class Queue
6
+ include Broker::Utility
5
7
 
6
8
  attr_reader :processed, :pending, :failed
7
9
 
@@ -25,18 +27,19 @@ module Broker
25
27
 
26
28
  def failure(payload)
27
29
  @failed << payload
30
+ move(payload, Broker.options[:failed_path])
28
31
  end
29
32
 
30
33
  def success(payload)
31
34
  @processed +=1
32
- move payload
35
+ move(payload, Broker.options[:processed_path])
33
36
  end
34
37
 
35
38
  private
36
39
 
37
- def move(payload)
38
- f = File.basename(payload.pkg.file)
39
- FileUtils.mv payload.pkg.file, File.join(Broker.options[:processed_path],"/", f)
40
+ def move(payload, dest)
41
+ f = "#{timestamp}_#{File.basename(payload.pkg.file)}"
42
+ FileUtils.mv payload.pkg.file, File.join(dest, "/", f)
40
43
  end
41
44
 
42
45
  end
@@ -1,66 +1,64 @@
1
1
  require 'broker/client/quickbase_client'
2
2
 
3
3
  module Broker
4
- # module QB
5
- class Session
6
-
7
- attr_reader :client, :app, :ext
4
+ class Session
8
5
 
9
- def initialize(opt={})
10
- @app = Broker.lookup_appname(opt[:app])
11
- @ext = Broker.options[:file_ext]
12
-
13
- credentials = {
14
- "username" => Broker.secrets['USERNAME'],
15
- "password" => Broker.secrets['PASSWORD'],
16
- "appname" => @app,
17
- "org" => Broker.secrets['ORG'],
18
- "apptoken" => opt[:token] || Broker.secrets['TOKEN']
19
- }
20
-
21
- begin
22
- @client = QuickBase::Client.init(credentials)
23
- rescue => e
24
- puts e.message
25
- #raise ArgumentError
26
- ensure
27
- # We successfully logged into quickbase, but supplied an invalid app name
28
- if @client && @client.errcode == "32"
29
- @client.signOut
30
- raise ArgumentError
31
- end
32
- end
33
- end
34
-
35
- def fire_event
36
- raise NotImplementedError
37
- end
38
-
39
- def sign_out
40
- @client.signOut
41
- end
6
+ attr_reader :client, :app, :ext
7
+
8
+ def initialize(opt={})
9
+ @app = Broker.lookup_appname(opt[:app])
10
+ @ext = Broker.options[:file_ext]
11
+
12
+ credentials = {
13
+ "username" => Broker.secrets['USERNAME'],
14
+ "password" => Broker.secrets['PASSWORD'],
15
+ "appname" => @app,
16
+ "org" => Broker.secrets['ORG'],
17
+ "apptoken" => opt[:token] || Broker.secrets['TOKEN']
18
+ }
42
19
 
43
- def qb_ready?(name)
44
- app_name = Broker.lookup_appname(name)
45
- unless app_name == @client.dbname
46
- return app_name && change_app(app_name, name)
20
+ begin
21
+ @client = QuickBase::Client.init(credentials)
22
+ rescue => e
23
+ puts e.message
24
+ #raise ArgumentError
25
+ ensure
26
+ # We successfully logged into quickbase, but supplied an invalid app name
27
+ if @client && @client.errcode == "32"
28
+ @client.signOut
29
+ raise ArgumentError
47
30
  end
48
- true
49
31
  end
32
+ end
50
33
 
51
- def field_names(table)
52
- table &&= table.to_s
53
- db = Broker.tables[@app]['tables'][table]
54
- db && @client.getFieldNames(db, "", true)
55
- end
56
-
57
- private
58
-
59
- def change_app(app_name, app_key)
60
- @app = app_key.to_s
61
- @client.findDBByname(app_name)
62
- @client.dbname == app_name
34
+ def fire_event
35
+ raise NotImplementedError
36
+ end
37
+
38
+ def sign_out
39
+ @client.signOut
40
+ end
41
+
42
+ def qb_ready?(name)
43
+ app_name = Broker.lookup_appname(name)
44
+ unless app_name == @client.dbname
45
+ return app_name && change_app(app_name, name)
63
46
  end
47
+ true
48
+ end
49
+
50
+ def field_names(table)
51
+ table &&= table.to_s
52
+ db = Broker.tables[@app]['tables'][table]
53
+ db && @client.getFieldNames(db, "", true)
54
+ end
55
+
56
+ private
57
+
58
+ def change_app(app_name, app_key)
59
+ @app = app_key.to_s
60
+ @client.findDBByname(app_name)
61
+ @client.dbname == app_name
64
62
  end
65
- # end
63
+ end
66
64
  end
@@ -14,8 +14,11 @@ Broker.setup do |config|
14
14
  # Directory where your successful imported files will be moved
15
15
  config.processed_path = 'broker_processed'
16
16
 
17
+ # Directory where your failed imported files will be moved
18
+ config.failed_path = 'broker_failed'
19
+
17
20
  # Uncomment to change the default file type to use for importing
18
- # [:csv, :tab]
21
+ # [:csv, :txt]
19
22
  #config.file_ext = :csv
20
23
 
21
24
  # Sets polling wait time before checking for new import files
@@ -8,3 +8,4 @@
8
8
  ORG: www
9
9
  USERNAME: billy_the_kid@user.com
10
10
  PASSWORD: mickeymouse
11
+ TOKEN: your_main_app_token_in_quickbase
@@ -12,6 +12,10 @@ module Broker
12
12
  watcher(name, &block)
13
13
  end
14
14
  end
15
+
16
+ def timestamp
17
+ Time.now.strftime("%Y-%m-%dT%H.%M.%S")
18
+ end
15
19
 
16
20
  end
17
21
  end
@@ -1,3 +1,3 @@
1
1
  module Broker
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -1,5 +1,5 @@
1
- require 'broker/import'
2
- require 'broker/export'
1
+ # require 'broker/import'
2
+ # require 'broker/export'
3
3
 
4
4
  module Broker
5
5
  module WebRoutes
@@ -13,16 +13,17 @@ module Broker
13
13
  end
14
14
 
15
15
  app.get '/exports' do
16
- db = params['table']
17
- app = params['app']
16
+ # db = params['table']
17
+ # app = params['app']
18
+ # @fields = []
19
+ # @header = "Exports"
20
+ # if db && app
21
+ # qb = Broker::Export.new(:app => app)
22
+ # @fields = qb.get_field_names(db)
23
+ # @header = "App: #{app.downcase} | Table: #{db.capitalize}"
24
+ # qb.sign_out
25
+ # end
18
26
  @fields = []
19
- @header = "Exports"
20
- if db && app
21
- qb = Broker::Export.new(:app => app)
22
- @fields = qb.get_field_names(db)
23
- @header = "App: #{app.downcase} | Table: #{db.capitalize}"
24
- qb.sign_out
25
- end
26
27
  erb :exports
27
28
  end
28
29
 
@@ -1,14 +1,14 @@
1
1
  <% Broker.tables.each do |table, vals| %>
2
2
  <div class="row header">
3
3
  <div class="col-sm-12">
4
- <h3><span class="title"><%= vals['name'] %></span> URL: <%= "/import/#{table}/[table_name]" %></h3>
4
+ <h3><span class="title"><%= vals['name'] %></span></h3>
5
5
  <h4>Token: <%= vals['token'] %></h4>
6
6
  <table class="workers table table-hover table-bordered table-striped table-white">
7
7
  <thead>
8
8
  <tr>
9
9
  <th>Table Name</th>
10
10
  <th>ID #</th>
11
- <th>View Fields</th>
11
+ <th>Broker Path</th>
12
12
  </tr>
13
13
  </thead>
14
14
  <tbody>
@@ -16,7 +16,7 @@
16
16
  <tr>
17
17
  <td><%= key %></td>
18
18
  <td><%= val %></td>
19
- <td><a href="/exports?app=<%= table %>&table=<%= key %>">View</a></td>
19
+ <td><%= "#{Broker.options[:queue]}/#{table}/#{key}" %></td>
20
20
  </tr>
21
21
  <% end %>
22
22
  </tbody>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: broker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shawn Henley
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-11-21 00:00:00.000000000 Z
11
+ date: 2015-11-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler