trophonius 1.4.5.4 → 2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5423635ca9e7c80d1880ae65a0dec526eb4eb7ba3b56c96ed58937c07c3a0481
4
- data.tar.gz: 1db6ebebe107ba8c46e962f1ac79a46eaf22a4e82dc89831f623c8eee20593bd
3
+ metadata.gz: 386e8ae48aba1dd8e6898dcbc16dde02e4acd506658676a146ddc646d96d2929
4
+ data.tar.gz: 59c8f91b771e3175347f9fa6fc87b522878e3c2cb9f1cb468f3a78b7cf85c616
5
5
  SHA512:
6
- metadata.gz: 4532263579437b372761bd83e5c98a0bcdec5ac7552d0201056e5ce28d9c4f37cc0ad8f7f4505ccf221e62177a9498665d917bfc89c0501a30d89376b16f6f35
7
- data.tar.gz: 27cf9b5dfa0ee4098116b76bb1390f400bbf85415d5f0e23b2bd278da52e495ff3a07cecb0a48d34f1397434fd655a5f6f1367f48019d52c90e25cce4de1aa22
6
+ metadata.gz: beca517abee0fd0db44a9e1cc96f16a5014e061101b8818aaa7f368ac597eecff3aaf4968f3ddd063732a2df36f7c8fa841d322b46599062b0a45b57887ae0d3
7
+ data.tar.gz: 71b436a45681c37c4bd6b8f3ce34d35e0cb11badb4356a32b2f1be8ebf7eb29af21c88e51a41d5166f2762bee0af4773a628d28f0cd1f7371ab7c45bc4873d24
@@ -1,7 +1,12 @@
1
+ # require 'time'
2
+ # require 'date_time'
3
+ # require 'date'
4
+
1
5
  require 'active_support/configurable'
6
+ require 'ethon'
2
7
 
3
8
  module Trophonius
4
- class Trophonius::Configuration # :nodoc:
9
+ class Configuration # :nodoc:
5
10
  include ActiveSupport::Configurable
6
11
 
7
12
  config_accessor(:host) { '127.0.0.1' }
@@ -13,15 +18,16 @@ module Trophonius
13
18
  config_accessor(:username) { 'Admin' }
14
19
  config_accessor(:password) { '' }
15
20
  config_accessor(:ssl) { true }
16
- config_accessor(:fm_18) { false }
21
+ config_accessor(:fm_18) { true }
17
22
  config_accessor(:count_result_script) { '' }
18
23
  config_accessor(:layout_name) { '' }
19
24
  config_accessor(:non_modifiable_fields) { [] }
20
- config_accessor(:all_fields) { {} }
21
25
  config_accessor(:translations) { {} }
22
26
  config_accessor(:has_many_relations) { {} }
23
27
  config_accessor(:belongs_to_relations) { {} }
24
28
  config_accessor(:local_network) { false }
25
29
  config_accessor(:redis_connection) { false }
30
+ config_accessor(:pool_size) { 5 }
31
+ config_accessor(:debug) { false }
26
32
  end
27
33
  end
@@ -1,20 +1,41 @@
1
1
  require 'time'
2
2
  require 'base64'
3
- require 'typhoeus'
4
- require 'trophonius_redis_manager'
3
+ require 'securerandom'
4
+ require 'connectors/redis_manager'
5
+
5
6
  module Trophonius
6
- module Trophonius::Connection
7
+ class Connection
8
+ attr_reader :id
9
+
10
+ def initialize
11
+ @id = SecureRandom.uuid
12
+ @token = ''
13
+ connect
14
+ end
15
+
16
+ ##
17
+ # Returns the last received token
18
+ # @return [String] the last valid *token* used to connect with the FileMaker data api
19
+ def token
20
+ if valid_connection?
21
+ Trophonius.config.redis_connection ? Trophonius::RedisManager.get_key(key: 'token') : @token
22
+ else
23
+ connect
24
+ end
25
+ end
26
+
27
+ private
28
+
7
29
  ##
8
30
  # Creates a new connection to FileMaker
9
31
  #
10
32
  # @return [String] the *token* used to connect with the FileMaker data api
11
33
 
12
- def self.connect
34
+ def connect
13
35
  if Trophonius.config.redis_connection
14
36
  Trophonius::RedisManager.set_key(key: 'token', value: setup_connection)
15
37
  Trophonius::RedisManager.set_key(key: 'last_connection', value: Time.now)
16
38
  Trophonius::RedisManager.get_key(key: 'token')
17
-
18
39
  else
19
40
  @token = setup_connection
20
41
  @last_connection = Time.now
@@ -22,20 +43,40 @@ module Trophonius
22
43
  end
23
44
  end
24
45
 
25
- ##
26
- # Creates and runs a HTTP request to create a new data api connection
27
- # This method throws an error when the request returns with a HTTP error or a FileMaker error
28
- # @return [String] the *token* used to connect with the FileMaker data api if successful
29
-
30
- def self.setup_connection
46
+ def reset_token
31
47
  if Trophonius.config.redis_connection
32
48
  Trophonius::RedisManager.set_key(key: 'token', value: '')
33
49
  Trophonius::RedisManager.set_key(key: 'last_connection', value: nil)
34
50
  else
35
51
  @token = ''
52
+ @last_connection = nil
36
53
  end
37
- ssl_verifyhost = Trophonius.config.local_network ? 0 : 2
38
- ssl_verifypeer = !Trophonius.config.local_network
54
+ end
55
+
56
+ def fm_external_data_source
57
+ if Trophonius.config.external_name.empty?
58
+ {}
59
+ else
60
+ {
61
+ fmDataSource: [
62
+ {
63
+ database: Trophonius.config.external_name,
64
+ username: Trophonius.config.external_username,
65
+ password: Trophonius.config.external_password
66
+ }
67
+ ]
68
+ }.to_json
69
+ end
70
+ end
71
+
72
+ ##
73
+ # Creates and runs a HTTP request to create a new data api connection
74
+ # This method throws an error when the request returns with a HTTP error or a FileMaker error
75
+ # @return [String] the *token* used to connect with the FileMaker data api if successful
76
+
77
+ def setup_connection
78
+ reset_token
79
+
39
80
  uri = URI::RFC2396_Parser.new
40
81
  url =
41
82
  URI(
@@ -43,24 +84,13 @@ module Trophonius
43
84
  "http#{Trophonius.config.ssl == true ? 's' : ''}://#{Trophonius.config.host}/fmi/data/v1/databases/#{Trophonius.config.database}/sessions"
44
85
  )
45
86
  )
87
+ ssl_verifyhost = Trophonius.config.local_network ? 0 : 2
88
+ ssl_verifypeer = !Trophonius.config.local_network
46
89
  request =
47
90
  Typhoeus::Request.new(
48
91
  url,
49
92
  method: :post,
50
- body:
51
- if Trophonius.config.external_name.empty?
52
- {}
53
- else
54
- {
55
- fmDataSource: [
56
- {
57
- database: Trophonius.config.external_name,
58
- username: Trophonius.config.external_username,
59
- password: Trophonius.config.external_password
60
- }
61
- ]
62
- }.to_json
63
- end,
93
+ body: fm_external_data_source,
64
94
  params: {},
65
95
  ssl_verifyhost: ssl_verifyhost,
66
96
  ssl_verifypeer: ssl_verifypeer,
@@ -71,12 +101,10 @@ module Trophonius
71
101
  )
72
102
  temp = request.run
73
103
  body = temp.response_body
74
- headers = temp.headers
75
- code = temp.code
76
104
 
77
105
  begin
78
106
  parsed = JSON.parse(body)
79
- rescue Exception => e
107
+ rescue StandardError => e
80
108
  puts e
81
109
  puts e.backtrace
82
110
  Error.throw_error('1631')
@@ -88,7 +116,7 @@ module Trophonius
88
116
  ##
89
117
  # Disconnects from the FileMaker server
90
118
  #
91
- def self.disconnect
119
+ def disconnect
92
120
  uri = URI::RFC2396_Parser.new
93
121
  url =
94
122
  URI(
@@ -114,7 +142,9 @@ module Trophonius
114
142
 
115
143
  begin
116
144
  parsed = JSON.parse(temp.response_body)
117
- rescue Exception => e
145
+ rescue StandardError => e
146
+ puts e
147
+ puts e.backtrace
118
148
  Error.throw_error('1631')
119
149
  end
120
150
  Error.throw_error(parsed['messages'][0]['code']) if parsed['messages'][0]['code'] != '0'
@@ -124,17 +154,10 @@ module Trophonius
124
154
  true
125
155
  end
126
156
 
127
- ##
128
- # Returns the last received token
129
- # @return [String] the last valid *token* used to connect with the FileMaker data api
130
- def self.token
131
- Trophonius.config.redis_connection ? Trophonius::RedisManager.get_key(key: 'token') : @token
132
- end
133
-
134
157
  ##
135
158
  # Returns the receive time of the last received token
136
159
  # @return [Time] Returns the receive time of the last received token
137
- def self.last_connection
160
+ def last_connection
138
161
  last = Trophonius.config.redis_connection ? Trophonius::RedisManager.get_key(key: 'last_connection') : nil
139
162
  last = last.nil? ? nil : Time.parse(last)
140
163
  Trophonius.config.redis_connection ? last : @last_connection
@@ -143,33 +166,23 @@ module Trophonius
143
166
  ##
144
167
  # Tests whether the FileMaker token is still valid
145
168
  # @return [Boolean] True if the token is valid False if invalid
146
- def self.test_connection
147
- uri = URI::RFC2396_Parser.new
148
- url =
149
- URI(
150
- uri.escape(
151
- "http#{Trophonius.config.ssl == true ? 's' : ''}://#{Trophonius.config.host}/fmi/data/v1/databases/#{
152
- Trophonius.config.database
153
- }/layouts/#{Trophonius.config.layout_name}/records?_limit=1"
154
- )
155
- )
156
- begin
157
- request =
158
- Typhoeus::Request.new(
159
- url,
160
- method: :get, body: {}, params: {}, headers: { 'Content-Type' => 'application/json', Authorization: "Bearer #{@token}" }
161
- )
162
- temp = request.run
163
- JSON.parse(temp.response_body)['messages'][0]['code'] == '0'
164
- rescue StandardError
165
- false
166
- end
169
+ def test_connection
170
+ return last_connection.nil? || last_connection < Time.now - (15 * 60) if Trophonius.config.layout_name == ''
171
+
172
+ path = "/layouts/#{Trophonius.config.layout_name}/records?_limit=1"
173
+ response =
174
+ Trophonius::DatabaseRequest.make_request(path, :get, {}, bypass_queue_with: "Bearer #{@token}")
175
+ response['messages'][0]['code'] == '0'
176
+ rescue StandardError => e
177
+ puts e
178
+ puts e.backtrace
179
+ false
167
180
  end
168
181
 
169
182
  ##
170
183
  # Returns whether the current connection is still valid
171
184
  # @return [Boolean] True if the connection is valid False if invalid
172
- def self.valid_connection?
185
+ def valid_connection?
173
186
  if Trophonius.config.layout_name != '' && test_connection == false
174
187
  false
175
188
  else
@@ -0,0 +1,31 @@
1
+ module Trophonius
2
+ class ConnectionManager
3
+ def initialize
4
+ @connections = {}
5
+ Trophonius.config.pool_size.times do
6
+ connection = Connection.new
7
+ @connections[connection.id] = { connection: connection, queue: [] }
8
+ end
9
+ end
10
+
11
+ def enqueue(id)
12
+ connection = @connections.values.min_by { |c| c[:queue].length }
13
+ connection[:queue].push(id)
14
+ puts "in,#{connection[:connection].id},#{connection[:connection].token},#{connection[:queue].length}" if Trophonius.config.debug == true
15
+ auth_header_bearer(connection[:connection].id)
16
+ end
17
+
18
+ def dequeue(id)
19
+ connection = @connections.values.find { |c| c[:queue].include?(id) }
20
+ connection[:queue].delete_if { |q_id| q_id == id }
21
+ puts "out,#{connection[:connection].id},#{connection[:connection].token},#{connection[:queue].length}" if Trophonius.config.debug == true
22
+ nil
23
+ end
24
+
25
+ private
26
+
27
+ def auth_header_bearer(id)
28
+ "Bearer #{@connections.dig(id, :connection).token}"
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+ require 'connectors/connection_manager'
5
+ require 'debug_printer'
6
+ require 'typhoeus'
7
+ require 'uri'
8
+ require 'securerandom'
9
+ require 'net/http'
10
+
11
+ module Trophonius
12
+ module DatabaseRequest
13
+ include DebugPrinter
14
+ ##
15
+ # Crafts and runs a HTTP request of any type
16
+ #
17
+ # @param [URI] url_path: the url to make the request to
18
+ #
19
+ # @param [String] method: the type of HTTP request to make (i.e. get)
20
+ #
21
+ # @param [JSONString] body: the body of the HTTP request
22
+ #
23
+ # @param [String] params: optional parameters added to the request
24
+ #
25
+ # @param [String] bypass_queue_with: optional way to bypass the ConnectionManager
26
+ #
27
+ # @return [JSON] parsed json of the response
28
+ def self.make_request(url_path, method, body, params = '', bypass_queue_with: '')
29
+ ssl_verifyhost = Trophonius.config.local_network ? 0 : 2
30
+ ssl_verifypeer = !Trophonius.config.local_network
31
+ base_url = "http#{Trophonius.config.ssl == true ? 's' : ''}://#{Trophonius.config.host}/fmi/data/v1/databases/#{Trophonius.config.database}"
32
+ uri = URI::RFC2396_Parser.new
33
+ url =
34
+ URI(
35
+ uri.escape("#{base_url}/#{url_path}")
36
+ )
37
+
38
+ id = SecureRandom.uuid
39
+ auth = bypass_queue_with.empty? ? auth_header_bearer(id) : bypass_queue_with
40
+
41
+ request =
42
+ Typhoeus::Request.new(
43
+ url,
44
+ method: method.to_sym,
45
+ body: body,
46
+ params: params,
47
+ ssl_verifyhost: ssl_verifyhost,
48
+ ssl_verifypeer: ssl_verifypeer,
49
+ headers: { 'Content-Type' => 'application/json', Authorization: auth }
50
+ )
51
+
52
+ DebugPrinter.print_debug('USED URL', url)
53
+ DebugPrinter.print_debug('SENT BODY', body)
54
+
55
+ temp = request.run
56
+
57
+ Trophonius.connection_manager.dequeue(id) if bypass_queue_with.empty?
58
+
59
+ begin
60
+ response_body = JSON.parse(temp.response_body)
61
+ DebugPrinter.print_debug('RECEIVED BODY', JSON.pretty_generate(response_body))
62
+ response_body
63
+ rescue StandardError => e
64
+ puts e
65
+ puts e.backtrace
66
+ Error.throw_error('1631')
67
+ end
68
+ end
69
+
70
+ ##
71
+ # Crafts and runs a HTTP request for uploading a file to a container
72
+ #
73
+ # @param [URI] urlparam: the url to make the request to
74
+ #
75
+ # @param [String] auth: the authentication required for the request
76
+ #
77
+ # @param [Tempfile or File] file: file to upload
78
+ #
79
+ # @return [JSON] parsed json of the response
80
+ def self.upload_file_request(url_param, file)
81
+ base_url = "http#{Trophonius.config.ssl == true ? 's' : ''}://#{Trophonius.config.host}/fmi/data/v1/databases/#{Trophonius.config.database}"
82
+ url = URI("#{base_url}/#{url_param}")
83
+
84
+ https = Net::HTTP.new(url.host, url.port)
85
+ https.use_ssl = true
86
+
87
+ id = SecureRandom.uuid
88
+ request = Net::HTTP::Post.new(url)
89
+ request['Authorization'] = auth_header_bearer(id)
90
+ request['Content-Type'] = 'multipart/form-data;'
91
+ form_data = [['upload', file]]
92
+ request.set_form form_data, 'multipart/form-data'
93
+ response = https.request(request)
94
+ Trophonius.connection_manager.dequeue(id)
95
+ begin
96
+ JSON.parse(response.read_body)
97
+ rescue StandardError
98
+ Error.throw_error('1631')
99
+ end
100
+ end
101
+
102
+ ##
103
+ # Gets a valid auth header containing the access token
104
+ #
105
+ # @return [String] a valid auth header containing the access token
106
+ def self.auth_header_bearer(id)
107
+ Trophonius.connection_manager.enqueue(id)
108
+ end
109
+
110
+ ##
111
+ # Retrieves the first record from FileMaker
112
+ #
113
+ # @return [JSON] The first record from FileMaker
114
+ def self.retrieve_first(layout_name)
115
+ make_request("layouts/#{layout_name}/records?_limit=1", 'get', '{}')
116
+ end
117
+
118
+ ##
119
+ # Retrieves the fieldnames of a layout
120
+ #
121
+ # @return [JSON] The fieldnames of a layout
122
+ def self.get_layout_field_names(layout_name)
123
+ make_request("layouts/#{layout_name}", 'get', '{}')['response']['fieldMetaData'].map { |field| field['name'] }
124
+ rescue StandardError => e
125
+ puts e
126
+ puts e.backtrace
127
+ Error.throw_error('1631')
128
+ end
129
+
130
+ ##
131
+ # Runs a FileMaker script
132
+ #
133
+ # @return [JSON] The script result from FileMaker
134
+ def self.run_script(script, scriptparameter, layout_name)
135
+ make_request("/layouts/#{layout_name}/records?_limit=1&script=#{script}&script.param=#{scriptparameter}", 'get', '{}')
136
+ end
137
+
138
+ ##
139
+ # Retrieves the 10000000 records from FileMaker
140
+ #
141
+ # @return [JSON] The first 10000000 records from FileMaker
142
+ def self.retrieve_all(layout_name, sort)
143
+ path = "layouts/#{layout_name}/records?_limit=10000000#{
144
+ Trophonius.config.count_result_script == '' ? '' : "&script=#{Trophonius.config.count_result_script}"
145
+ }"
146
+ path += "&_sort=#{sort_order}" if sort.present?
147
+ path += "&script=#{Trophonius.config.count_result_script}" if Trophonius.config.count_result_script.present?
148
+ make_request(path, 'get', '{}')
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,11 @@
1
+ module Trophonius
2
+ module DebugPrinter
3
+ def self.print_debug(open_close_message, message)
4
+ return unless Trophonius.config.debug == true
5
+
6
+ puts "======== #{open_close_message} ========"
7
+ puts message
8
+ puts "======== #{open_close_message} ========"
9
+ end
10
+ end
11
+ end
@@ -1,8 +1,8 @@
1
1
  require 'json'
2
- require 'trophonius_config'
2
+ require 'config'
3
3
 
4
4
  module Trophonius
5
- module Trophonius::Error
5
+ module Error
6
6
  class RecordNotFoundError < StandardError; end # :nodoc:
7
7
  class FieldUnexistingError < NoMethodError; end # :nodoc:
8
8
  class ScriptUnexistingError < NoMethodError; end # :nodoc:
@@ -89,7 +89,7 @@ module Trophonius
89
89
  when '101'
90
90
  raise RecordNotFoundError.new, "Record #{more_info} was not found"
91
91
  when '102'
92
- raise FieldUnexistingError.new, 'Field does not exist' if more_info.zero?
92
+ raise FieldUnexistingError.new, 'Field does not exist' if more_info.is_a?(Integer) && more_info.zero?
93
93
 
94
94
  raise FieldUnexistingError.new, "Following field(s) #{more_info} do not exist on layout #{layout_info}"
95
95
  when '103'
@@ -1,6 +1,6 @@
1
1
  class Date
2
2
  def to_fm
3
- self.strftime('%m/%d/%Y')
3
+ strftime('%m/%d/%Y')
4
4
  end
5
5
 
6
6
  def self.from_fm(fm_date)
data/lib/fm_time.rb ADDED
@@ -0,0 +1,11 @@
1
+ class Time
2
+ def to_fm
3
+ strftime('%m-%d-%Y %H:%M:%S')
4
+ end
5
+
6
+ def self.from_fm(time)
7
+ Time.strptime(time, '%m/%d/%Y %H:%M:%S')
8
+ end
9
+
10
+ alias convert_to_fm to_fm
11
+ end
@@ -22,6 +22,7 @@ module Trophonius
22
22
  config.password = Rails.application.credentials.dig(:password) # (requires >= Rails 5.2) otherwise use old secrets
23
23
  config.redis_connection = false # default false, true if you want to store the token in redis
24
24
  config.ssl = true # or false depending on whether https or http should be used
25
+ config.fm_18 = true
25
26
  # USE THE NEXT OPTION WITH CAUTION
26
27
  config.local_network = false # if true the ssl certificate will not be verified to allow for self-signed certificates
27
28
  end"