monitorsys-tortoise 0.1.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a22b515d2230d4ac946ff83c32959e1134c75fdc195d8f0cef84bdb996188094
4
+ data.tar.gz: ca75ae43d89a6269ca8ac0b0c141f1c31d3560d44398f0829428fb5e0e5629be
5
+ SHA512:
6
+ metadata.gz: b8dd9736b5ae0aec278e9956ac467ac692b250807314653dcb20fb76dfda0a74a319d864752c60bc023109cbb73cd578bc9847791e191c9d2bd97ffea406da0b
7
+ data.tar.gz: 5800935537a43d9018965d222565a6af0415626a7e48023cad1eab9c541d2a779789098e509ab820b33248b7d4b677d0a54c76df58afcd7484cbd11d320b2241
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ /bin/setup
10
+ tortoise_production
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in tortoise.gemspec
4
+ gemspec
5
+
6
+ gem 'rake', '~> 12.0'
7
+
8
+ gem 'listen'
9
+ gem 'rest-client'
10
+ gem 'sqlite3'
11
+ gem 'activerecord'
12
+ gem 'fileutils'
13
+ gem 'mini_cache'
14
+ gem 'honeybadger'
@@ -0,0 +1,72 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ tortoise (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ activemodel (6.1.0)
10
+ activesupport (= 6.1.0)
11
+ activerecord (6.1.0)
12
+ activemodel (= 6.1.0)
13
+ activesupport (= 6.1.0)
14
+ activesupport (6.1.0)
15
+ concurrent-ruby (~> 1.0, >= 1.0.2)
16
+ i18n (>= 1.6, < 2)
17
+ minitest (>= 5.1)
18
+ tzinfo (~> 2.0)
19
+ zeitwerk (~> 2.3)
20
+ concurrent-ruby (1.1.7)
21
+ domain_name (0.5.20190701)
22
+ unf (>= 0.0.5, < 1.0.0)
23
+ ffi (1.14.1)
24
+ fileutils (1.4.1)
25
+ honeybadger (4.7.2)
26
+ http-accept (1.7.0)
27
+ http-cookie (1.0.3)
28
+ domain_name (~> 0.5)
29
+ i18n (1.8.5)
30
+ concurrent-ruby (~> 1.0)
31
+ listen (3.3.3)
32
+ rb-fsevent (~> 0.10, >= 0.10.3)
33
+ rb-inotify (~> 0.9, >= 0.9.10)
34
+ mime-types (3.3.1)
35
+ mime-types-data (~> 3.2015)
36
+ mime-types-data (3.2020.1104)
37
+ mini_cache (1.1.0)
38
+ minitest (5.14.2)
39
+ netrc (0.11.0)
40
+ rake (12.3.3)
41
+ rb-fsevent (0.10.4)
42
+ rb-inotify (0.10.1)
43
+ ffi (~> 1.0)
44
+ rest-client (2.1.0)
45
+ http-accept (>= 1.7.0, < 2.0)
46
+ http-cookie (>= 1.0.2, < 2.0)
47
+ mime-types (>= 1.16, < 4.0)
48
+ netrc (~> 0.8)
49
+ sqlite3 (1.4.2)
50
+ tzinfo (2.0.4)
51
+ concurrent-ruby (~> 1.0)
52
+ unf (0.1.4)
53
+ unf_ext
54
+ unf_ext (0.0.7.7)
55
+ zeitwerk (2.4.2)
56
+
57
+ PLATFORMS
58
+ ruby
59
+
60
+ DEPENDENCIES
61
+ activerecord
62
+ fileutils
63
+ honeybadger
64
+ listen
65
+ mini_cache
66
+ rake (~> 12.0)
67
+ rest-client
68
+ sqlite3
69
+ tortoise!
70
+
71
+ BUNDLED WITH
72
+ 2.1.4
@@ -0,0 +1,36 @@
1
+ # Tortoise
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/tortoise`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'tortoise'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle install
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install tortoise
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/joaomangilli/tortoise.
36
+
@@ -0,0 +1,4 @@
1
+ require 'bundler/gem_tasks'
2
+ import 'lib/tasks/migrate.rake'
3
+
4
+ task :default => :spec
@@ -0,0 +1,43 @@
1
+ require 'bundler/setup'
2
+ require 'irb'
3
+ require 'tortoise'
4
+ require 'io/console'
5
+
6
+ def get_response(answer, default: nil, password: false)
7
+ puts "#{answer} (#{default || 'n/a'}): "
8
+ chomp(password).presence || default
9
+ end
10
+
11
+ def chomp(password)
12
+ password ? STDIN.noecho(&:gets).chomp : gets.chomp
13
+ end
14
+
15
+ def create_directory
16
+ Tortoise::Directory.create!(folder_path: get_response('Digite o diretorio da pasta'))
17
+
18
+ create_directory if get_response('Deseja cadastrar outro diretorio?(S/N)').to_s.upcase == 'S'
19
+ end
20
+
21
+ def create_configuration
22
+ Tortoise::Configuration.create!(
23
+ api_path: ENV['TORTOISE_API_PATH'].to_s.strip,
24
+ token_path: ENV['TORTOISE_TOKEN_PATH'].to_s.strip,
25
+ upload_path: ENV['TORTOISE_UPLOAD_PATH'].to_s.strip,
26
+ public_informations_path: ENV['TORTOISE_PUBLIC_INFORMATIONS_PATH'].to_s.strip,
27
+ user: get_response('Digite o usuario'),
28
+ password: get_response('Digite a senha', password: true),
29
+ retry_time: ENV['TORTOISE_RETRY_TIME'].to_s.strip,
30
+ app_user: ENV['TORTOISE_APP_USER'].to_s.strip,
31
+ app_password: ENV['TORTOISE_APP_PASSWORD'].to_s.strip
32
+ )
33
+
34
+ Tortoise::InitialLoader.new.load
35
+ end
36
+
37
+ create_directory unless Tortoise::Configuration.exists?
38
+ create_configuration unless Tortoise::Configuration.exists?
39
+
40
+ Tortoise::Listener.new.start
41
+
42
+ # IRB.start(__FILE__)
43
+ sleep
@@ -0,0 +1,9 @@
1
+ require 'sqlite3'
2
+ require 'active_record'
3
+
4
+ ActiveRecord::Base.establish_connection(
5
+ adapter: 'sqlite3',
6
+ database: 'tortoise_production',
7
+ pool: 100,
8
+ timeout: 50000
9
+ )
@@ -0,0 +1,31 @@
1
+ require_relative '../../config/sqllite3'
2
+
3
+ namespace :tortoise do
4
+ namespace :db do
5
+ task :migrate do
6
+ ActiveRecord::Schema.define do
7
+ create_table :directories do |t|
8
+ t.string :folder_path
9
+ t.timestamps
10
+ end
11
+ create_table :configurations do |t|
12
+ t.string :api_path
13
+ t.string :token_path
14
+ t.string :upload_path
15
+ t.string :public_informations_path
16
+ t.string :user
17
+ t.string :password
18
+ t.integer :retry_time
19
+ t.string :app_user
20
+ t.string :app_password
21
+ t.timestamps
22
+ end
23
+ create_table :invoices do |t|
24
+ t.string :file
25
+ t.string :status
26
+ t.timestamps
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,22 @@
1
+ require 'listen'
2
+ require 'rest-client'
3
+ require 'fileutils'
4
+ require 'mini_cache'
5
+ require 'honeybadger' unless ENV['HONEYBADGER_API_KEY'].to_s.empty?
6
+
7
+ require_relative '../config/sqllite3'
8
+ require_relative 'tortoise/jobs/upload_job'
9
+ require_relative 'tortoise/models/application_record'
10
+ require_relative 'tortoise/models/cache'
11
+ require_relative 'tortoise/models/configuration'
12
+ require_relative 'tortoise/models/directory'
13
+ require_relative 'tortoise/models/invoice'
14
+ require_relative 'tortoise/services/api_helper'
15
+ require_relative 'tortoise/services/bulk_loader'
16
+ require_relative 'tortoise/services/initial_loader'
17
+ require_relative 'tortoise/services/listener'
18
+ require_relative 'tortoise/services/loader'
19
+
20
+ module Tortoise
21
+
22
+ end
@@ -0,0 +1,37 @@
1
+ module Tortoise
2
+ class UploadJob
3
+ def perform(invoice_id, delay=0)
4
+ Thread.new do
5
+ sleep delay
6
+
7
+ invoice = Tortoise::Invoice.find(invoice_id)
8
+ invoice.update(status: Invoice::STARTED)
9
+
10
+ begin
11
+ api_helper.upload(invoice.file)
12
+ invoice.update(status: Invoice::DONE)
13
+
14
+ puts "A nota #{invoice.file} foi enviada com sucesso!"
15
+ rescue StandardError => e
16
+ puts "Um erro aconteceu!", e
17
+
18
+ Honeybadger.notify(e) unless ENV['HONEYBADGER_API_KEY'].to_s.empty?
19
+
20
+ invoice.update(status: Invoice::FAILED)
21
+
22
+ UploadJob.new.perform(invoice.id, configuration.default_retry_time)
23
+ end
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def api_helper
30
+ @api_helper ||= Tortoise::ApiHelper.new
31
+ end
32
+
33
+ def configuration
34
+ @configuration ||= Tortoise::Configuration.first_or_initialize
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,5 @@
1
+ module Tortoise
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ module Tortoise
2
+ class Cache
3
+ def self.get
4
+ @cache ||= MiniCache::Store.new
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,33 @@
1
+ module Tortoise
2
+ class Configuration < ApplicationRecord
3
+ def password=(password)
4
+ write_attribute(:password, crypt.encrypt_and_sign(password)) unless password.nil?
5
+ end
6
+
7
+ def password
8
+ crypt.decrypt_and_verify(read_attribute(:password)) unless read_attribute(:password).nil?
9
+ end
10
+
11
+ def app_password=(password)
12
+ write_attribute(:app_password, crypt.encrypt_and_sign(password)) unless password.nil?
13
+ end
14
+
15
+ def app_password
16
+ crypt.decrypt_and_verify(read_attribute(:app_password)) unless read_attribute(:app_password).nil?
17
+ end
18
+
19
+ def default_retry_time
20
+ (retry_time || 5).minutes
21
+ end
22
+
23
+ private
24
+
25
+ def crypt
26
+ @crypt ||= ActiveSupport::MessageEncryptor.new(crypt_key)
27
+ end
28
+
29
+ def crypt_key
30
+ [ENV['ENCRYPTOR_KEY'].to_s.strip].pack('H*')
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,15 @@
1
+ module Tortoise
2
+ class Directory < ApplicationRecord
3
+ after_save :create_folder
4
+
5
+ private
6
+
7
+ def create_folder
8
+ return if folder_path.nil?
9
+
10
+ unless File.directory?(folder_path)
11
+ FileUtils.mkdir_p(folder_path)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ module Tortoise
2
+ class Invoice < ApplicationRecord
3
+ STARTED = 'started'
4
+ DONE = 'done'
5
+ FAILED = 'failed'
6
+ end
7
+ end
@@ -0,0 +1,108 @@
1
+ module Tortoise
2
+ class ApiHelper
3
+ ACCESS_TOKEN_CACHE_KEY = 'access_token'
4
+ DATABASE_CACHE_KEY = 'database'
5
+ CACHE_EXPIRES = 60
6
+
7
+ def token
8
+ return cached_token unless cached_token.nil?
9
+ return if token_path.nil? || token_params.nil?
10
+
11
+ response = RestClient.post(
12
+ token_path,
13
+ token_params,
14
+ token_header
15
+ )
16
+
17
+ cache.set(
18
+ ACCESS_TOKEN_CACHE_KEY,
19
+ JSON.parse(response.body)['access_token'],
20
+ expires_in: CACHE_EXPIRES
21
+ )
22
+ end
23
+
24
+ def database
25
+ return cached_database unless cached_database.nil?
26
+ return if public_informations_path.nil?
27
+
28
+ response = RestClient.get(public_informations_path)
29
+ cache.set(
30
+ DATABASE_CACHE_KEY,
31
+ JSON.parse(response.body)['licensa']['database'],
32
+ expires_in: CACHE_EXPIRES
33
+ )
34
+ end
35
+
36
+ def upload(file)
37
+ return if upload_path.nil? || token.nil?
38
+
39
+ RestClient.post(
40
+ upload_path,
41
+ {
42
+ multipart: true,
43
+ file: File.new(file)
44
+ },
45
+ {
46
+ 'User-access' => database,
47
+ Authorization: "Bearer #{token}"
48
+ }
49
+ )
50
+ rescue RestClient::Exception => e
51
+ e.response
52
+ end
53
+
54
+ private
55
+
56
+ def cached_token
57
+ cache.get(ACCESS_TOKEN_CACHE_KEY)
58
+ end
59
+
60
+ def cached_database
61
+ cache.get(DATABASE_CACHE_KEY)
62
+ end
63
+
64
+ def cache
65
+ Tortoise::Cache.get
66
+ end
67
+
68
+ def token_params
69
+ return if configuration.user.nil? || configuration.password.nil?
70
+
71
+ {
72
+ username: configuration.user,
73
+ password: configuration.password,
74
+ grant_type: 'password'
75
+ }
76
+ end
77
+
78
+ def token_header
79
+ { Authorization: "Basic #{Base64::encode64(basic_auth)}" }
80
+ end
81
+
82
+ def basic_auth
83
+ "#{configuration.app_user}:#{configuration.app_password}"
84
+ end
85
+
86
+ def token_path
87
+ return if configuration.api_path.nil? || configuration.token_path.nil?
88
+
89
+ "#{configuration.api_path}#{configuration.token_path}"
90
+ end
91
+
92
+ def upload_path
93
+ return if configuration.api_path.nil? || configuration.upload_path.nil?
94
+
95
+ "#{configuration.api_path}#{configuration.upload_path}"
96
+ end
97
+
98
+ def public_informations_path
99
+ return if configuration.api_path.nil? || configuration.public_informations_path.nil?
100
+
101
+ "#{configuration.api_path}#{configuration.public_informations_path}"
102
+ end
103
+
104
+ def configuration
105
+ @configuration ||= Tortoise::Configuration.first_or_initialize
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,21 @@
1
+ module Tortoise
2
+ class BulkLoader
3
+ attr_reader :files
4
+
5
+ def initialize(files)
6
+ @files = files
7
+ end
8
+
9
+ def self.load(files)
10
+ new(files).load
11
+ rescue StandardError => e
12
+ Honeybadger.notify(e) unless ENV['HONEYBADGER_API_KEY'].to_s.empty?
13
+ end
14
+
15
+ def load
16
+ files.each do |file|
17
+ Tortoise::Loader.load(file)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ module Tortoise
2
+ class InitialLoader
3
+ def load
4
+ return if directories.empty?
5
+
6
+ Tortoise::BulkLoader.load(files)
7
+ end
8
+
9
+ private
10
+
11
+ def files
12
+ @files ||= directories.map do |directory|
13
+ Dir["#{directory}/*"].select { |f| File.file? f }
14
+ end.flatten.compact
15
+ end
16
+
17
+ def directories
18
+ @directories ||= Tortoise::Directory.pluck(:folder_path)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ module Tortoise
2
+ class Listener
3
+ def start
4
+ return if directories.empty?
5
+
6
+ listener.start
7
+ end
8
+
9
+ private
10
+
11
+ def listener
12
+ @listener ||= Listen.to(*directories) do |_m, added, _r|
13
+ Tortoise::BulkLoader.load(added)
14
+ end
15
+ end
16
+
17
+ def directories
18
+ @directories ||= Tortoise::Directory.pluck(:folder_path)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,29 @@
1
+ module Tortoise
2
+ class Loader
3
+ attr_reader :file
4
+
5
+ def initialize(file)
6
+ @file = file
7
+ end
8
+
9
+ def self.load(file)
10
+ new(file).load
11
+ end
12
+
13
+ def load
14
+ return if exists?
15
+
16
+ Tortoise::UploadJob.new.perform(invoice.id)
17
+ end
18
+
19
+ private
20
+
21
+ def invoice
22
+ @invoice ||= Tortoise::Invoice.create!(file: file, status: Invoice::STARTED)
23
+ end
24
+
25
+ def exists?
26
+ Tortoise::Invoice.where(file: file).exists?
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ module Tortoise
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,27 @@
1
+ require_relative 'lib/tortoise/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'monitorsys-tortoise'
5
+ spec.version = Tortoise::VERSION
6
+ spec.authors = ['João Mangilli']
7
+ spec.email = ['joao-luis-mangilli@hotmail.com']
8
+
9
+ spec.summary = 'It is a gem for Fabio'
10
+ spec.homepage = 'http://monitorsys.com.br'
11
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
12
+
13
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
14
+
15
+ spec.metadata['homepage_uri'] = spec.homepage
16
+ # spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
17
+ # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
18
+
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
+ end
24
+ spec.bindir = "exe"
25
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ["lib"]
27
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: monitorsys-tortoise
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - João Mangilli
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-01-09 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email:
15
+ - joao-luis-mangilli@hotmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".gitignore"
21
+ - Gemfile
22
+ - Gemfile.lock
23
+ - README.md
24
+ - Rakefile
25
+ - bin/console
26
+ - config/sqllite3.rb
27
+ - lib/tasks/migrate.rake
28
+ - lib/tortoise.rb
29
+ - lib/tortoise/jobs/upload_job.rb
30
+ - lib/tortoise/models/application_record.rb
31
+ - lib/tortoise/models/cache.rb
32
+ - lib/tortoise/models/configuration.rb
33
+ - lib/tortoise/models/directory.rb
34
+ - lib/tortoise/models/invoice.rb
35
+ - lib/tortoise/services/api_helper.rb
36
+ - lib/tortoise/services/bulk_loader.rb
37
+ - lib/tortoise/services/initial_loader.rb
38
+ - lib/tortoise/services/listener.rb
39
+ - lib/tortoise/services/loader.rb
40
+ - lib/tortoise/version.rb
41
+ - tortoise.gemspec
42
+ homepage: http://monitorsys.com.br
43
+ licenses: []
44
+ metadata:
45
+ homepage_uri: http://monitorsys.com.br
46
+ post_install_message:
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 2.3.0
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ requirements: []
61
+ rubygems_version: 3.1.4
62
+ signing_key:
63
+ specification_version: 4
64
+ summary: It is a gem for Fabio
65
+ test_files: []