qravan 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|