broker 0.0.9
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 +7 -0
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +47 -0
- data/Rakefile +1 -0
- data/bin/broker +16 -0
- data/broker.gemspec +23 -0
- data/config.ru +5 -0
- data/lib/broker/application.rb +16 -0
- data/lib/broker/cli.rb +198 -0
- data/lib/broker/client/QuickBaseClient.rb +5242 -0
- data/lib/broker/client/QuickBaseMisc.rb +117 -0
- data/lib/broker/client/quickbase_client.rb +1 -0
- data/lib/broker/export.rb +16 -0
- data/lib/broker/import.rb +11 -0
- data/lib/broker/launcher.rb +66 -0
- data/lib/broker/session.rb +34 -0
- data/lib/broker/templates/broker.rb +21 -0
- data/lib/broker/templates/quickbase_tables.yml +22 -0
- data/lib/broker/templates/secrets.yml +10 -0
- data/lib/broker/utility.rb +17 -0
- data/lib/broker/version.rb +3 -0
- data/lib/broker/web.rb +33 -0
- data/lib/broker/web_helpers.rb +12 -0
- data/lib/broker/web_routes.rb +34 -0
- data/lib/broker.rb +115 -0
- data/web/assets/images/bootstrap/glyphicons-halflings-white.png +0 -0
- data/web/assets/images/bootstrap/glyphicons-halflings.png +0 -0
- data/web/assets/javascripts/application.js +0 -0
- data/web/assets/stylesheets/application.css +754 -0
- data/web/assets/stylesheets/bootstrap.css +9 -0
- data/web/views/_nav.erb +20 -0
- data/web/views/api.erb +26 -0
- data/web/views/exports.erb +10 -0
- data/web/views/imports.erb +5 -0
- data/web/views/index.erb +18 -0
- data/web/views/layout.erb +52 -0
- metadata +126 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#--#####################################################################
|
|
2
|
+
# Copyright (c) 2009-2012 Gareth Lewis and Intuit, Inc.
|
|
3
|
+
#
|
|
4
|
+
# All rights reserved. This program and the accompanying materials
|
|
5
|
+
# are made available under the terms of the Eclipse Public License v1.0
|
|
6
|
+
# which accompanies this distribution, and is available at
|
|
7
|
+
# http://www.opensource.org/licenses/eclipse-1.0.php
|
|
8
|
+
#
|
|
9
|
+
# Contributors:
|
|
10
|
+
# Gareth Lewis - Initial contribution.
|
|
11
|
+
# Intuit Partner Platform.
|
|
12
|
+
#++#####################################################################
|
|
13
|
+
|
|
14
|
+
module QuickBase
|
|
15
|
+
|
|
16
|
+
# Miscellaneous static helper methods
|
|
17
|
+
class Misc
|
|
18
|
+
|
|
19
|
+
def Misc.ruby19?
|
|
20
|
+
RUBY_VERSION >= "1.9"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def Misc.createBase32ConversionHash
|
|
24
|
+
base32Symbols = {}
|
|
25
|
+
decimal = 0
|
|
26
|
+
("a".."z").each{|letter|
|
|
27
|
+
unless letter == "l" or letter == "o"
|
|
28
|
+
base32Symbols[decimal.to_s]=letter
|
|
29
|
+
decimal += 1
|
|
30
|
+
end
|
|
31
|
+
}
|
|
32
|
+
(2..9).each{|number|
|
|
33
|
+
base32Symbols[decimal.to_s]=number.to_s
|
|
34
|
+
decimal += 1
|
|
35
|
+
}
|
|
36
|
+
base32Symbols
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def Misc.decimalToBase32(decimalNumber)
|
|
40
|
+
@base32Symbols ||= Misc.createBase32ConversionHash
|
|
41
|
+
base32Num = ""
|
|
42
|
+
decimalNumber = decimalNumber.to_i
|
|
43
|
+
if decimalNumber < 32
|
|
44
|
+
base32Num = @base32Symbols[decimalNumber.to_s]
|
|
45
|
+
else
|
|
46
|
+
power = 10
|
|
47
|
+
power -= 1 while (decimalNumber/(32**power)) < 1
|
|
48
|
+
while decimalNumber > 0
|
|
49
|
+
n = (decimalNumber/(32**power))
|
|
50
|
+
base32Num << @base32Symbols[n.to_s] if @base32Symbols[n.to_s]
|
|
51
|
+
decimalNumber = (decimalNumber-((32**power)*n))
|
|
52
|
+
power -= 1
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
base32Num
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def Misc.isBase32Number?(string)
|
|
59
|
+
ret = true
|
|
60
|
+
if string
|
|
61
|
+
@base32Symbols ||= Misc.createBase32ConversionHash
|
|
62
|
+
base32SymbolsValues = @base32Symbols.values
|
|
63
|
+
stringCopy = string.to_s
|
|
64
|
+
stringCopy.split(//).each{|char|
|
|
65
|
+
if !base32SymbolsValues.include?(char)
|
|
66
|
+
ret = false
|
|
67
|
+
break
|
|
68
|
+
end
|
|
69
|
+
}
|
|
70
|
+
else
|
|
71
|
+
ret = false
|
|
72
|
+
end
|
|
73
|
+
ret
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def Misc.isDbidString?(string)
|
|
77
|
+
Misc.isBase32Number?(string)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def Misc.time_in_milliseconds(time = nil)
|
|
81
|
+
ret = 0
|
|
82
|
+
time ||= Time.now
|
|
83
|
+
if time.is_a?(Time)
|
|
84
|
+
ret = (time.to_f * 1000).to_i
|
|
85
|
+
elsif time.is_a?(DateTime)
|
|
86
|
+
t = Time.mktime(time.year,time.month,time.day,time.hour,time.min,time.sec,0)
|
|
87
|
+
ret = (t.to_f * 1000).to_i
|
|
88
|
+
elsif time.is_a?(Date)
|
|
89
|
+
t = Time.mktime(time.year,time.month,time.day,0,0,0,0)
|
|
90
|
+
ret = (t.to_f * 1000).to_i
|
|
91
|
+
end
|
|
92
|
+
ret
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def Misc.listUserToArray(listUser)
|
|
96
|
+
listUser.split(/;/)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def Misc.arrayToListUser(array)
|
|
100
|
+
array.join(";")
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def Misc.save_file(filename, contents, mode="wb")
|
|
104
|
+
File.open(filename, mode){|f|
|
|
105
|
+
f.write(contents)
|
|
106
|
+
f.flush
|
|
107
|
+
}
|
|
108
|
+
rescue StandardError => error
|
|
109
|
+
new_filename = filename.gsub(/\W/,"_")
|
|
110
|
+
File.open(new_filename, mode){|f|
|
|
111
|
+
f.write(contents)
|
|
112
|
+
f.flush
|
|
113
|
+
}
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
end
|
|
117
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require_relative 'QuickBaseClient'
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require 'broker/utility'
|
|
2
|
+
|
|
3
|
+
module Broker
|
|
4
|
+
class Launcher
|
|
5
|
+
|
|
6
|
+
def initialize
|
|
7
|
+
@poller = Broker::Poller.new
|
|
8
|
+
@finished = false
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def run
|
|
12
|
+
puts "launcher is running"
|
|
13
|
+
@poller.start
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def stop
|
|
17
|
+
@finished = true
|
|
18
|
+
@poller.terminate
|
|
19
|
+
puts "launcher stopped"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class Poller
|
|
25
|
+
include Utility
|
|
26
|
+
|
|
27
|
+
def initialize
|
|
28
|
+
@finished = false
|
|
29
|
+
@wait_time = Broker.poll_interval
|
|
30
|
+
@folder = Broker.queue
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def terminate
|
|
34
|
+
@finished = true
|
|
35
|
+
if @thread
|
|
36
|
+
t = @thread
|
|
37
|
+
@thread = nil
|
|
38
|
+
wait 1
|
|
39
|
+
t.value
|
|
40
|
+
puts "Polling thread terminated"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def start
|
|
45
|
+
@thread ||= safe_thread("poller") do
|
|
46
|
+
pause_first
|
|
47
|
+
|
|
48
|
+
while !@finished
|
|
49
|
+
puts "Checking for new files in #{@folder}"
|
|
50
|
+
wait
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def pause_first
|
|
58
|
+
sleep 5
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def wait(dur=nil)
|
|
62
|
+
sleep dur || @wait_time
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'broker'
|
|
2
|
+
require 'broker/client/quickbase_client'
|
|
3
|
+
|
|
4
|
+
module Broker
|
|
5
|
+
class Session
|
|
6
|
+
|
|
7
|
+
attr_reader :client, :app
|
|
8
|
+
|
|
9
|
+
def initialize(opt={})
|
|
10
|
+
@app = opt[:app]
|
|
11
|
+
|
|
12
|
+
credentials = {
|
|
13
|
+
"username" => Broker.secrets['USER'],
|
|
14
|
+
"password" => Broker.secrets['PASSWORD'],
|
|
15
|
+
"appname" => @app,
|
|
16
|
+
"org" => Broker.secrets['ORG'],
|
|
17
|
+
"apptoken" => opt[:token] || Broker.tables[@app]['token']
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@client = QuickBase::Client.init(credentials)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def get_field_names(table)
|
|
24
|
+
table &&= table.to_s
|
|
25
|
+
db = Broker.tables[@app]['tables'][table]
|
|
26
|
+
db && @client.getFieldNames(db, "", true)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def sign_out
|
|
30
|
+
@client.signOut
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Use this hook to configure Broker
|
|
2
|
+
|
|
3
|
+
Broker.setup do |config|
|
|
4
|
+
|
|
5
|
+
# Store private quickbase credentials for authenticating via the api
|
|
6
|
+
config.secrets_path = 'config/secrets.yml'
|
|
7
|
+
|
|
8
|
+
# Store a table mapping for all quickbase apps you want to connect with
|
|
9
|
+
config.tables_path = 'config/quickbase_tables.yml'
|
|
10
|
+
|
|
11
|
+
# Directory where your import files will be queued for import
|
|
12
|
+
config.queue = 'broker_queue'
|
|
13
|
+
|
|
14
|
+
# Uncomment to change the default file type to use for importing
|
|
15
|
+
# [:csv, :tab]
|
|
16
|
+
#config.file_ext = :csv
|
|
17
|
+
|
|
18
|
+
# Sets polling wait time before checking for new import files
|
|
19
|
+
config.poll_interval = 10
|
|
20
|
+
|
|
21
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Add Quickbase Table Configuration here
|
|
2
|
+
#
|
|
3
|
+
# Give each app an easy to remember/type name for broker to use
|
|
4
|
+
# Tables should also be given a simple short name
|
|
5
|
+
#
|
|
6
|
+
# This config file will be used to generate the sync queue and to connect to the corresponding apps in quickbase
|
|
7
|
+
# Don't check this into version control, keep this private
|
|
8
|
+
#
|
|
9
|
+
# EXAMPLES
|
|
10
|
+
#
|
|
11
|
+
tracker:
|
|
12
|
+
name: Job Tracker
|
|
13
|
+
token: your_apps_token_for_tracker
|
|
14
|
+
tables:
|
|
15
|
+
main: table_dbid
|
|
16
|
+
people: table_dbid
|
|
17
|
+
billings:
|
|
18
|
+
name: Financial Billings
|
|
19
|
+
token: your_apps_token_for_billings
|
|
20
|
+
tables:
|
|
21
|
+
projects: table_dbid
|
|
22
|
+
pace: table_dbid
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Place your quickbase credentials here
|
|
2
|
+
# Do not check into version control, keep your secrets safe
|
|
3
|
+
#
|
|
4
|
+
# ORG -> if you are using a custom subdomain such as mycompany.quickbase.com,
|
|
5
|
+
# you will set ORG: mycompany, otherwise www is the default
|
|
6
|
+
#
|
|
7
|
+
#
|
|
8
|
+
ORG: www
|
|
9
|
+
USERNAME: billy_the_kid@user.com
|
|
10
|
+
PASSWORD: mickeymouse
|
data/lib/broker/web.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require 'erb'
|
|
2
|
+
require 'sinatra/base'
|
|
3
|
+
|
|
4
|
+
require 'broker/web_helpers'
|
|
5
|
+
require 'broker/web_routes'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
module Broker
|
|
9
|
+
class Web < Sinatra::Base
|
|
10
|
+
|
|
11
|
+
set :root, File.expand_path(File.dirname(__FILE__) + "/../../web")
|
|
12
|
+
set :public_folder, Proc.new { "#{root}/assets" }
|
|
13
|
+
set :views, Proc.new { "#{root}/views" }
|
|
14
|
+
|
|
15
|
+
helpers WebHelpers
|
|
16
|
+
|
|
17
|
+
register Broker::WebRoutes
|
|
18
|
+
|
|
19
|
+
DEFAULT_TABS = {
|
|
20
|
+
"Dashboard" => '',
|
|
21
|
+
"Imports" => 'imports',
|
|
22
|
+
"Exports" => 'exports',
|
|
23
|
+
"API" => 'api'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class << self
|
|
27
|
+
def default_tabs
|
|
28
|
+
DEFAULT_TABS
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'broker/import'
|
|
2
|
+
require 'broker/export'
|
|
3
|
+
|
|
4
|
+
module Broker
|
|
5
|
+
module WebRoutes
|
|
6
|
+
def self.registered(app)
|
|
7
|
+
app.get "/" do
|
|
8
|
+
erb :index
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
app.get '/imports' do
|
|
12
|
+
erb :imports
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
app.get '/exports' do
|
|
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
|
|
26
|
+
erb :exports
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
app.get '/api' do
|
|
30
|
+
erb :api
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
data/lib/broker.rb
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
require 'broker/version'
|
|
2
|
+
require 'yaml'
|
|
3
|
+
|
|
4
|
+
module Broker
|
|
5
|
+
NAME = "Broker: Quickbase Data Intake"
|
|
6
|
+
|
|
7
|
+
DEFAULTS = {
|
|
8
|
+
secrets_path: 'config/secrets.yml',
|
|
9
|
+
tables_path: 'config/quickbase_tables.yml',
|
|
10
|
+
initializer: 'config/initializers/broker.rb',
|
|
11
|
+
poll_interval: 300,
|
|
12
|
+
queue: 'broker_queue',
|
|
13
|
+
file_ext: :csv
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@@secrets_path = @@secrets_path ||= DEFAULTS[:secrets_path]
|
|
17
|
+
@@tables_path = @@tables_path ||= DEFAULTS[:tables_path]
|
|
18
|
+
@@initializer = @@initializer ||= DEFAULTS[:initializer]
|
|
19
|
+
@@poll_interval = @poll_interval ||= DEFAULTS[:poll_interval]
|
|
20
|
+
@@queue = @@queue ||= DEFAULTS[:queue]
|
|
21
|
+
@@file_ext = @@file_ext ||= DEFAULTS[:file_ext]
|
|
22
|
+
|
|
23
|
+
@@secrets = YAML.load_file(@@secrets_path) rescue {}
|
|
24
|
+
@@tables = YAML.load_file(@@tables_path) rescue {}
|
|
25
|
+
|
|
26
|
+
def self.lookup_tbid(opt={})
|
|
27
|
+
tables[opt[:app]]['tables'][opt[:table]]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.path
|
|
31
|
+
Dir.pwd
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.setup
|
|
35
|
+
yield self unless launched?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.launched?
|
|
39
|
+
defined?(Broker::Launcher)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.config_files
|
|
43
|
+
[secrets_path, tables_path, initializer]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# def self.options
|
|
47
|
+
# @options ||= DEFAULTS.dup
|
|
48
|
+
# end
|
|
49
|
+
#
|
|
50
|
+
# def self.options=(opt)
|
|
51
|
+
# @options = opt
|
|
52
|
+
# end
|
|
53
|
+
|
|
54
|
+
def self.poll_interval
|
|
55
|
+
@@poll_interval
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.poll_interval=(val)
|
|
59
|
+
@@poll_interval = val
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.queue
|
|
63
|
+
@@queue
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.queue=(val)
|
|
67
|
+
@@queue = val
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def file_ext
|
|
71
|
+
@@file_ext
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def file_ext=(val)
|
|
75
|
+
@@file_ext = val if [:csv, :tab].include? val
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def self.secrets
|
|
79
|
+
@@secrets
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def self.secrets_path
|
|
83
|
+
@@secrets_path
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def self.secrets_path=(val)
|
|
87
|
+
@@secrets_path = val
|
|
88
|
+
@@secrets = YAML.load_file(@@secrets_path) rescue {}
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def self.tables
|
|
92
|
+
@@tables
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def self.tables_path
|
|
96
|
+
@@tables_path
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def self.tables_path=(val)
|
|
100
|
+
@@tables_path = val
|
|
101
|
+
@@tables = YAML.load_file(@@tables_path) rescue {}
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def self.initializer
|
|
105
|
+
@@initializer
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def self.initializer=(val)
|
|
109
|
+
@@initializer = val
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
require 'broker/application'
|
|
115
|
+
|
|
Binary file
|
|
Binary file
|
|
File without changes
|