qravan 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.
- checksums.yaml +7 -0
- data/.gitattributes +2 -0
- data/.gitignore +61 -0
- data/.rspec +3 -0
- data/.rspec_status +4 -0
- data/.rubocop.yml +13 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +143 -0
- data/LICENSE +5 -0
- data/README.md +191 -0
- data/Rakefile +39 -0
- data/assets/images/qravan.png +0 -0
- data/assets/images/qravan_repo.png +0 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/config.ru +46 -0
- data/examples/queries.md +114 -0
- data/exe/qravan +7 -0
- data/ext/qravan/extconf.rb +5 -0
- data/ext/qravan/qravan.c +9 -0
- data/ext/qravan/qravan.h +6 -0
- data/falcon.rb +12 -0
- data/lib/qravan/async_loader.rb +33 -0
- data/lib/qravan/cargo.rb +80 -0
- data/lib/qravan/credentials/request.rb +54 -0
- data/lib/qravan/credentials/response.rb +41 -0
- data/lib/qravan/credentials/signature.rb +30 -0
- data/lib/qravan/credentials/system.rb +33 -0
- data/lib/qravan/credentials.rb +29 -0
- data/lib/qravan/model.rb +20 -0
- data/lib/qravan/qravan.bundle +0 -0
- data/lib/qravan/query/request.rb +40 -0
- data/lib/qravan/query/response.rb +26 -0
- data/lib/qravan/query.rb +30 -0
- data/lib/qravan/resource/resource.rb +35 -0
- data/lib/qravan/source/db.rb +35 -0
- data/lib/qravan/source/rest.rb +36 -0
- data/lib/qravan/source.rb +27 -0
- data/lib/qravan/version.rb +35 -0
- data/lib/qravan.rb +25 -0
- data/logs/db.log +2660 -0
- data/models/base/1.0/model.yaml +32 -0
- data/models/base/current +1 -0
- data/models/current/.keep +0 -0
- data/models/current/model.yaml +158 -0
- data/models/vehicles/1.0/model.yaml +126 -0
- data/qravan.gemspec +48 -0
- data/sources/base/1.0/source.yaml +9 -0
- data/sources/base/current +1 -0
- data/sources/postgres/1.0/source.yaml +10 -0
- data/sources/postgres/current +1 -0
- data/sources/prostore/1.0/source.yaml +13 -0
- data/sources/prostore/current +1 -0
- metadata +296 -0
data/ext/qravan/qravan.c
ADDED
data/ext/qravan/qravan.h
ADDED
data/falcon.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env -S falcon host
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
load :rack, :lets_encrypt_tls, :supervisor
|
5
|
+
|
6
|
+
hostname = File.basename(__dir__)
|
7
|
+
rack hostname, :lets_encrypt_tls do
|
8
|
+
cache true
|
9
|
+
endpoint Async::HTTP::Endpoint.parse("http://localhost:3300").with(protocol: Async::HTTP::Protocol::HTTP1)
|
10
|
+
end
|
11
|
+
|
12
|
+
supervisor
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "async"
|
2
|
+
require "async/barrier"
|
3
|
+
require "async/http/internet"
|
4
|
+
|
5
|
+
class AsyncLoader
|
6
|
+
def self.perform(urls)
|
7
|
+
internet = Async::HTTP::Internet.new
|
8
|
+
barrier = Async::Barrier.new
|
9
|
+
time = Time.now
|
10
|
+
requests = 0
|
11
|
+
result = []
|
12
|
+
urls.each do |url|
|
13
|
+
barrier.async do
|
14
|
+
Console.logger.info "AsyncHttp#get: #{url}"
|
15
|
+
begin
|
16
|
+
response = internet.get(url)
|
17
|
+
body = JSON.parse(response.read)
|
18
|
+
result << { url: url, body: body }
|
19
|
+
requests += 1
|
20
|
+
ensure
|
21
|
+
response.finish
|
22
|
+
end
|
23
|
+
Console.logger.info "AsyncHttp#fulfill: #{url}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
Console.logger.info "AsyncHttp#wait"
|
28
|
+
barrier.wait
|
29
|
+
total = Time.now - time
|
30
|
+
result.unshift({time: "Duration: #{Time.now - time}s for #{requests} (RPS: #{(requests / total).to_i} r/s)"})
|
31
|
+
result.to_json
|
32
|
+
end
|
33
|
+
end
|
data/lib/qravan/cargo.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Qravan
|
4
|
+
# Load models and sources to cargo
|
5
|
+
class Cargo
|
6
|
+
attr_accessor :sources, :models, :connections
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
Console.logger.info "Cargo initialized:"
|
10
|
+
Console.logger.info "Sources: #{sources.count}"
|
11
|
+
Console.logger.info "Models: #{models.count}"
|
12
|
+
Console.logger.info "Connections: #{connections.count}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def sources
|
16
|
+
@sources ||= preload_sources
|
17
|
+
end
|
18
|
+
|
19
|
+
def models
|
20
|
+
@models ||= preload_models
|
21
|
+
end
|
22
|
+
|
23
|
+
def connections
|
24
|
+
@connections ||= connect_sources
|
25
|
+
end
|
26
|
+
|
27
|
+
def preload_sources
|
28
|
+
@sources = {}
|
29
|
+
list("sources").each do |source|
|
30
|
+
Console.logger.info "Reading source #{source}"
|
31
|
+
preloaded_source = YAML.load_file("#{source}/1.0/source.yaml")
|
32
|
+
@sources[preloaded_source.keys.first] = preloaded_source.first.last
|
33
|
+
end
|
34
|
+
@sources
|
35
|
+
end
|
36
|
+
|
37
|
+
def concat_models
|
38
|
+
model_files = {}
|
39
|
+
(list("models") - %w[models/current models/base]).each do |model|
|
40
|
+
Console.logger.info "Reading model file #{model}/1.0/model.yaml"
|
41
|
+
model_files[model] = File.readlines("#{model}/1.0/model.yaml").map(&:chomp)
|
42
|
+
end
|
43
|
+
|
44
|
+
File.open("models/current/model.yaml", "w") do |output_file|
|
45
|
+
output_file.puts File.readlines("models/base/1.0/model.yaml").map(&:chomp)
|
46
|
+
model_files.each { |elem| output_file.puts elem[1] }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def preload_models
|
51
|
+
concat_models
|
52
|
+
@models = {}
|
53
|
+
Console.logger.info "Preparing models"
|
54
|
+
preloaded_models = YAML.load_file("models/current/model.yaml")
|
55
|
+
preloaded_models["resources"].each do |model|
|
56
|
+
@models[model.keys.first] = model
|
57
|
+
end
|
58
|
+
@models
|
59
|
+
end
|
60
|
+
|
61
|
+
def connect_sources
|
62
|
+
@connections = {}
|
63
|
+
sources.each do |source|
|
64
|
+
case source[1]["type"]
|
65
|
+
when "rest"
|
66
|
+
@connections[source[0]] = Qravan::Rest.new(source[1])
|
67
|
+
when "db"
|
68
|
+
@connections[source[0]] = Qravan::Db.new(source[1])
|
69
|
+
else
|
70
|
+
Console.logger.warn "Not supported resource"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
@connections
|
74
|
+
end
|
75
|
+
|
76
|
+
def list(what = "sources")
|
77
|
+
Dir["#{what}/*"]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Qravan
|
4
|
+
module Credentials
|
5
|
+
# Request credentials
|
6
|
+
class Request
|
7
|
+
attr_accessor :credentials
|
8
|
+
|
9
|
+
def initialize(credentials)
|
10
|
+
@credentials = credentials["request"]
|
11
|
+
end
|
12
|
+
|
13
|
+
def prepare
|
14
|
+
{
|
15
|
+
id: id,
|
16
|
+
sub_id: sub_id,
|
17
|
+
name: name,
|
18
|
+
purpose_id: purpose,
|
19
|
+
audit: audit,
|
20
|
+
audit_id: audit_id,
|
21
|
+
audit_token: audit_token
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def id
|
26
|
+
@credentials["id"]
|
27
|
+
end
|
28
|
+
|
29
|
+
def sub_id
|
30
|
+
@credentials["sub_id"]
|
31
|
+
end
|
32
|
+
|
33
|
+
def name
|
34
|
+
@credentials["name"]
|
35
|
+
end
|
36
|
+
|
37
|
+
def purpose
|
38
|
+
@credentials["purpose_id"]
|
39
|
+
end
|
40
|
+
|
41
|
+
def audit
|
42
|
+
@credentials["audit"]
|
43
|
+
end
|
44
|
+
|
45
|
+
def audit_id
|
46
|
+
@credentials["audit_id"]
|
47
|
+
end
|
48
|
+
|
49
|
+
def audit_token
|
50
|
+
@credentials["audit_token"]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "securerandom"
|
4
|
+
|
5
|
+
module Qravan
|
6
|
+
module Credentials
|
7
|
+
# Response credentials
|
8
|
+
class Response
|
9
|
+
attr_accessor :credentials
|
10
|
+
|
11
|
+
def initialize(response_credentials = {})
|
12
|
+
@credentials = response_credentials
|
13
|
+
end
|
14
|
+
|
15
|
+
def prepare
|
16
|
+
{
|
17
|
+
id: id,
|
18
|
+
sub_id: sub_id,
|
19
|
+
started_at: started_at,
|
20
|
+
finished_at: finished_at
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def id
|
25
|
+
SecureRandom.uuid
|
26
|
+
end
|
27
|
+
|
28
|
+
def sub_id
|
29
|
+
SecureRandom.uuid
|
30
|
+
end
|
31
|
+
|
32
|
+
def started_at
|
33
|
+
Time.now
|
34
|
+
end
|
35
|
+
|
36
|
+
def finished_at
|
37
|
+
Time.now
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Qravan
|
4
|
+
module Credentials
|
5
|
+
# System credentials
|
6
|
+
class Signature
|
7
|
+
attr_accessor :credentials
|
8
|
+
|
9
|
+
def initialize(credentials)
|
10
|
+
@credentials = credentials["signature"]
|
11
|
+
end
|
12
|
+
|
13
|
+
def prepare
|
14
|
+
{
|
15
|
+
digest: digest,
|
16
|
+
signature: signature
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def digest
|
21
|
+
@credentials["digest"]
|
22
|
+
end
|
23
|
+
|
24
|
+
def signature
|
25
|
+
@credentials["signature"]
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Qravan
|
3
|
+
module Credentials
|
4
|
+
# System credentials
|
5
|
+
class System
|
6
|
+
attr_accessor :credentials
|
7
|
+
|
8
|
+
def initialize(credentials)
|
9
|
+
@credentials = credentials["system"]
|
10
|
+
end
|
11
|
+
|
12
|
+
def prepare
|
13
|
+
{
|
14
|
+
mnemonic: mnemonic,
|
15
|
+
instance_id: instance,
|
16
|
+
user_id: user
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def mnemonic
|
21
|
+
@credentials["mnemonic"]
|
22
|
+
end
|
23
|
+
|
24
|
+
def instance
|
25
|
+
@credentials["instance_id"]
|
26
|
+
end
|
27
|
+
|
28
|
+
def user
|
29
|
+
@credentials["user_id"]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative "credentials/system"
|
2
|
+
require_relative "credentials/request"
|
3
|
+
require_relative "credentials/response"
|
4
|
+
require_relative "credentials/signature"
|
5
|
+
|
6
|
+
module Qravan
|
7
|
+
BANNER = %(
|
8
|
+
Data API Qravan Server
|
9
|
+
Developed at CAPAA by Alexander Panasenkov, 2022
|
10
|
+
).freeze
|
11
|
+
module Credentials
|
12
|
+
class Query
|
13
|
+
attr_accessor :query_credentials
|
14
|
+
|
15
|
+
def initialize(query_credentials)
|
16
|
+
@query_credentials ||= query_credentials
|
17
|
+
end
|
18
|
+
|
19
|
+
def credentials
|
20
|
+
{
|
21
|
+
response: Qravan::Credentials::Response.new(@query_credentials).prepare,
|
22
|
+
system: Qravan::Credentials::System.new(@query_credentials).prepare,
|
23
|
+
request: Qravan::Credentials::Request.new(@query_credentials).prepare,
|
24
|
+
signature: Qravan::Credentials::Signature.new(@query_credentials).prepare
|
25
|
+
}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/qravan/model.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Qravan
|
4
|
+
# Models for data extractions class
|
5
|
+
class Model
|
6
|
+
attr_accessor :models
|
7
|
+
|
8
|
+
def initialize(cargo = {})
|
9
|
+
@models ||= cargo.models
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
body = [models.to_json]
|
14
|
+
status = 200
|
15
|
+
headers = { "content-type" => "application/json" }
|
16
|
+
|
17
|
+
[status, headers, body]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
Binary file
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "async"
|
3
|
+
require "async/barrier"
|
4
|
+
|
5
|
+
module Qravan
|
6
|
+
# Requests processing class
|
7
|
+
class Request
|
8
|
+
attr_accessor :resources, :sources, :cargo
|
9
|
+
|
10
|
+
def initialize(request, cargo = {})
|
11
|
+
@query = request["query"]
|
12
|
+
@credentials = request["credentials"]
|
13
|
+
@resources = []
|
14
|
+
@cargo ||= cargo
|
15
|
+
|
16
|
+
validate
|
17
|
+
extract_resources
|
18
|
+
end
|
19
|
+
|
20
|
+
def validate
|
21
|
+
@query
|
22
|
+
end
|
23
|
+
|
24
|
+
def extract_resources
|
25
|
+
barrier = Async::Barrier.new
|
26
|
+
barrier.async do
|
27
|
+
@query.each do |key, resource|
|
28
|
+
@resources << { key => Qravan::Resource.new(key, resource, cargo).extract }
|
29
|
+
end
|
30
|
+
ensure
|
31
|
+
@resources
|
32
|
+
end
|
33
|
+
barrier.wait
|
34
|
+
end
|
35
|
+
|
36
|
+
def credentials
|
37
|
+
@credentials
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Qravan
|
4
|
+
class Response
|
5
|
+
attr_accessor :request
|
6
|
+
attr_accessor :resources
|
7
|
+
attr_accessor :credentials
|
8
|
+
|
9
|
+
def initialize(request)
|
10
|
+
@request ||= request
|
11
|
+
@resources = request.resources
|
12
|
+
@credentials = request.credentials
|
13
|
+
end
|
14
|
+
|
15
|
+
def validate
|
16
|
+
@request
|
17
|
+
end
|
18
|
+
|
19
|
+
def answer
|
20
|
+
{
|
21
|
+
"response": @resources,
|
22
|
+
"credentials": Qravan::Credentials::Query.new(@credentials).credentials
|
23
|
+
}.to_json
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/qravan/query.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative "query/request"
|
3
|
+
require_relative "query/response"
|
4
|
+
require_relative "resource/resource"
|
5
|
+
require_relative "source/db"
|
6
|
+
require_relative "source/rest"
|
7
|
+
|
8
|
+
module Qravan
|
9
|
+
|
10
|
+
# Sources for data extractions class
|
11
|
+
class Query
|
12
|
+
attr_accessor :cargo
|
13
|
+
|
14
|
+
def initialize(cargo)
|
15
|
+
@cargo ||= cargo
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(env)
|
19
|
+
query = Rack::Request.new(env)
|
20
|
+
request = Qravan::Request.new(JSON.parse(query.body.read), cargo)
|
21
|
+
response = Qravan::Response.new(request)
|
22
|
+
|
23
|
+
body = [response.answer]
|
24
|
+
status = 200
|
25
|
+
headers = { "content-type" => "application/json" }
|
26
|
+
|
27
|
+
[status, headers, body]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Qravan
|
3
|
+
# Resource processing class
|
4
|
+
class Resource
|
5
|
+
attr_accessor :connections, :cargo
|
6
|
+
|
7
|
+
def initialize(name, data, cargo = {})
|
8
|
+
@resource = name
|
9
|
+
@resource_data = data
|
10
|
+
@cargo ||= cargo
|
11
|
+
@connections = cargo.connections
|
12
|
+
end
|
13
|
+
|
14
|
+
def validate
|
15
|
+
@resource_data
|
16
|
+
end
|
17
|
+
|
18
|
+
def extract
|
19
|
+
source_name = "#{cargo.models[@resource]["extract"]["source"]["name"]}_source"
|
20
|
+
@connections[source_name.to_s].extract(sql)
|
21
|
+
end
|
22
|
+
|
23
|
+
def sql
|
24
|
+
[
|
25
|
+
:select,
|
26
|
+
@resource_data["attributes"].join(","),
|
27
|
+
:from,
|
28
|
+
cargo.models[@resource]["extract"]["source"]["table"],
|
29
|
+
:where,
|
30
|
+
@resource_data["conditions"].map { |k, v| "#{k}='#{v}'" }
|
31
|
+
.join(" and ")
|
32
|
+
].join(" ")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "async"
|
4
|
+
require "sequel"
|
5
|
+
require "logger"
|
6
|
+
module Qravan
|
7
|
+
# Database class
|
8
|
+
class Db
|
9
|
+
attr_accessor :result, :connection
|
10
|
+
|
11
|
+
def initialize(source)
|
12
|
+
@connection = Sequel.connect(adapter: source["adapter"],
|
13
|
+
user: source["user"],
|
14
|
+
password: source["password"],
|
15
|
+
host: source["host"],
|
16
|
+
port: source["port"],
|
17
|
+
database: source["database"],
|
18
|
+
max_connections: source["max_connections"],
|
19
|
+
logger: Logger.new("logs/db.log"))
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
def extract(sql)
|
24
|
+
Console.logger.info "DB EXTRACT: #{sql}"
|
25
|
+
@result = []
|
26
|
+
time = Time.now
|
27
|
+
@connection[sql].each do |row|
|
28
|
+
@result << row
|
29
|
+
end
|
30
|
+
total = Time.now - time
|
31
|
+
@result << { time: "DB Duration: #{Time.now - time}s" }
|
32
|
+
@result
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "async"
|
4
|
+
require "async/barrier"
|
5
|
+
require "logger"
|
6
|
+
module Qravan
|
7
|
+
# Database class
|
8
|
+
class Rest
|
9
|
+
attr_accessor :result, :connection
|
10
|
+
|
11
|
+
def initialize(source)
|
12
|
+
@connection = source
|
13
|
+
end
|
14
|
+
|
15
|
+
def extract(sql = "select * from smevql1.driverlicenseql3;")
|
16
|
+
Console.logger.info "REST EXTRACT: #{sql}"
|
17
|
+
internet = Async::HTTP::Internet.new
|
18
|
+
barrier = Async::Barrier.new
|
19
|
+
time = Time.now
|
20
|
+
@result = []
|
21
|
+
url = "#{@connection["protocol"]}://#{@connection["host"]}:#{@connection["port"]}/#{@connection["path"]}"
|
22
|
+
headers = [%w[accept application/json], %w[content-type application/json]]
|
23
|
+
body = [@connection["template"] % { request: sql }]
|
24
|
+
barrier.async do
|
25
|
+
response = internet.post(url, headers, body)
|
26
|
+
response_body = JSON.parse(response.read)
|
27
|
+
@result << response_body[@connection["payload-path"]]
|
28
|
+
ensure
|
29
|
+
response.finish
|
30
|
+
end
|
31
|
+
barrier.wait
|
32
|
+
@result << { time: "REST Duration: #{Time.now - time}s " }
|
33
|
+
@result
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
|
5
|
+
module Qravan
|
6
|
+
# Sources for data extractions class
|
7
|
+
class Source
|
8
|
+
attr_accessor :sources
|
9
|
+
|
10
|
+
def initialize(cargo = {})
|
11
|
+
@sources ||= cargo.sources
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(env)
|
15
|
+
body = [unpassworded.to_json]
|
16
|
+
status = 200
|
17
|
+
headers = { "content-type" => "application/json" }
|
18
|
+
|
19
|
+
[status, headers, body]
|
20
|
+
end
|
21
|
+
|
22
|
+
def unpassworded
|
23
|
+
sources.map { |key, value| sources[key]["password"] = "******" if sources[key]["password"] }
|
24
|
+
sources
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Qravan
|
4
|
+
VERSION = "0.1.0"
|
5
|
+
ENV = "development"
|
6
|
+
|
7
|
+
class Spec
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
body = [Qravan::Spec.collect_spec.to_json]
|
11
|
+
status = 200
|
12
|
+
headers = { "content-type" => "application/json" }
|
13
|
+
|
14
|
+
[status, headers, body]
|
15
|
+
end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def collect_spec
|
19
|
+
{
|
20
|
+
"spec": {
|
21
|
+
"server": {
|
22
|
+
"type": "Qravan Server",
|
23
|
+
"version": Qravan::VERSION,
|
24
|
+
"env": Qravan::ENV
|
25
|
+
},
|
26
|
+
"protocol": {
|
27
|
+
"type": "Qravan Spec",
|
28
|
+
"version": "1.0"
|
29
|
+
}
|
30
|
+
}
|
31
|
+
}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/qravan.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "qravan/version"
|
4
|
+
require_relative "qravan/cargo"
|
5
|
+
require_relative "qravan/source"
|
6
|
+
require_relative "qravan/model"
|
7
|
+
require_relative "qravan/query"
|
8
|
+
require_relative "qravan/credentials"
|
9
|
+
|
10
|
+
require "async"
|
11
|
+
require "async/http/internet"
|
12
|
+
require "redis"
|
13
|
+
|
14
|
+
module Qravan
|
15
|
+
class Error < StandardError; end
|
16
|
+
|
17
|
+
def self.root
|
18
|
+
File.dirname __dir__
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.logs
|
22
|
+
File.join root, "logs"
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|