file_sv 0.1.3 → 0.1.7
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 +4 -4
- data/exe/file_sv +3 -1
- data/lib/file_sv/file_processor.rb +2 -1
- data/lib/file_sv/global_settings.rb +28 -0
- data/lib/file_sv/planned_endpoint.rb +12 -6
- data/lib/file_sv/service_loader.rb +3 -1
- data/lib/file_sv/sv_plan.rb +12 -3
- data/lib/file_sv/version.rb +1 -1
- data/lib/file_sv/virtual_server.rb +36 -18
- data/lib/file_sv/yaml_processor.rb +2 -2
- data/lib/file_sv.rb +20 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 43425956ba3f1325aa81876eea311cabd364f35ae2735adf79f4cb2daed69dd4
|
4
|
+
data.tar.gz: 629f16289572cb0407ae010a5322f96b07ab6261e591b55dd080f3c9d5e5b58c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ec1ca5bffdfcbcc83f94cde7838bc1cdccf844c6c58f45a768203f8d72f2c77e0ce49f706ba7878cb9ba414a58863fb7438e056d76c341a16b258e6de31d3125
|
7
|
+
data.tar.gz: c8e605801443bbd947769ce32bfd376daad28c98bf05dcbca4e1cbbf81a09d1b6a50b9a5f6d0dc1aab29d494f6796bf400bbda023e95ddb8e57c3e241bf16692
|
data/exe/file_sv
CHANGED
@@ -12,10 +12,12 @@ class Exe < Thor
|
|
12
12
|
ServiceLoader.create_plan_for folder
|
13
13
|
end
|
14
14
|
|
15
|
+
option :crt, default: nil, banner: "HTTPS CRT"
|
16
|
+
option :key, default: nil, banner: "HTTPS key"
|
15
17
|
desc "serve folder", "Serve virtual service based on folder"
|
16
18
|
def serve(folder)
|
17
19
|
plan folder
|
18
|
-
ServiceLoader.serve_plan
|
20
|
+
ServiceLoader.serve_plan options
|
19
21
|
end
|
20
22
|
|
21
23
|
desc "inspect folder", "Inspect details of what's served at folder"
|
@@ -6,7 +6,8 @@ class FileProcessor
|
|
6
6
|
class << self
|
7
7
|
# Process file, adding it to plan
|
8
8
|
def process(filename)
|
9
|
-
|
9
|
+
endpoint = PlannedEndpoint.new(filename)
|
10
|
+
SvPlan + endpoint unless GlobalSettings.ignored_status? endpoint.status_code
|
10
11
|
end
|
11
12
|
end
|
12
13
|
end
|
@@ -7,6 +7,10 @@ class GlobalSettings
|
|
7
7
|
@empty_body_status = 204
|
8
8
|
|
9
9
|
@ignore_files = "{*.md,Dockerfile,.*}"
|
10
|
+
|
11
|
+
@https = false
|
12
|
+
|
13
|
+
@ignore_status_codes = nil
|
10
14
|
class << self
|
11
15
|
# @return [String] Default REST method when none specified by filename
|
12
16
|
attr_accessor :default_method
|
@@ -14,9 +18,33 @@ class GlobalSettings
|
|
14
18
|
attr_accessor :empty_body_status
|
15
19
|
# @return [Array] Expression representing files to ignore
|
16
20
|
attr_accessor :ignore_files
|
21
|
+
# @return [Boolean] Whether to serve https using self signed certificate. Deprecated now
|
22
|
+
attr_accessor :https
|
23
|
+
# @return [String] Path to HTTPS cert
|
24
|
+
attr_accessor :cert
|
25
|
+
# @return [String] Path to HTTPS key
|
26
|
+
attr_accessor :key
|
27
|
+
# @return [Array] List of http status codes to ignore
|
28
|
+
attr_accessor :ignore_status_codes
|
29
|
+
|
30
|
+
# @param [Integer] status_code HTTP status code
|
31
|
+
# @return [Boolean] Whether status code is currently ignored
|
32
|
+
def ignored_status?(status_code)
|
33
|
+
return unless ignore_status_codes
|
34
|
+
|
35
|
+
ignore_status_codes.split(",").each do |code|
|
36
|
+
regex = Regexp.new code.to_s
|
37
|
+
result = status_code.to_s[regex]
|
38
|
+
next unless result
|
39
|
+
|
40
|
+
return true unless result.empty?
|
41
|
+
end
|
42
|
+
false
|
43
|
+
end
|
17
44
|
end
|
18
45
|
end
|
19
46
|
|
47
|
+
# Http settings that all HTTP method types inherit
|
20
48
|
module CommonHttpSettings
|
21
49
|
attr_accessor :default_status
|
22
50
|
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
require "erb"
|
4
4
|
require "securerandom"
|
5
5
|
require "faker"
|
6
|
+
require "pathname"
|
6
7
|
|
7
8
|
# Endpoint planned to be served
|
8
9
|
class PlannedEndpoint
|
@@ -16,12 +17,12 @@ class PlannedEndpoint
|
|
16
17
|
attr_accessor :status_code
|
17
18
|
|
18
19
|
# @return [Array]
|
19
|
-
HTTP_METHODS = %w[get post patch delete options].freeze
|
20
|
+
HTTP_METHODS = %w[get put post patch delete options].freeze
|
20
21
|
|
21
22
|
# Represent a new endpoint
|
22
23
|
def initialize(path)
|
23
24
|
self.file_path = path
|
24
|
-
self.path =
|
25
|
+
self.path = serving_loc_for path
|
25
26
|
@file = true if not_text?
|
26
27
|
assign_params_from_name
|
27
28
|
self.method ||= GlobalSettings.default_method
|
@@ -30,6 +31,11 @@ class PlannedEndpoint
|
|
30
31
|
set_default_status_code
|
31
32
|
end
|
32
33
|
|
34
|
+
def serving_loc_for(path)
|
35
|
+
loc = File.split(path).first # TODO: Handle if path has a % at beginning of a folder
|
36
|
+
loc.gsub("#{File::SEPARATOR}%", "#{File::SEPARATOR}:")
|
37
|
+
end
|
38
|
+
|
33
39
|
# @return [Boolean] Whether endpoint serves a file not a string
|
34
40
|
def file?
|
35
41
|
@file
|
@@ -39,7 +45,7 @@ class PlannedEndpoint
|
|
39
45
|
def default_empty_code
|
40
46
|
return false if not_text?
|
41
47
|
|
42
|
-
if content.strip.empty?
|
48
|
+
if content(binding).strip.empty?
|
43
49
|
self.status_code = GlobalSettings.empty_body_status
|
44
50
|
return true
|
45
51
|
end
|
@@ -93,8 +99,8 @@ class PlannedEndpoint
|
|
93
99
|
end
|
94
100
|
|
95
101
|
# @return [Object] Content of file
|
96
|
-
def content
|
97
|
-
render_text
|
102
|
+
def content(binding)
|
103
|
+
render_text(binding)
|
98
104
|
end
|
99
105
|
|
100
106
|
def not_text?
|
@@ -103,7 +109,7 @@ class PlannedEndpoint
|
|
103
109
|
end
|
104
110
|
|
105
111
|
# @return [String] Render text
|
106
|
-
def render_text
|
112
|
+
def render_text(binding)
|
107
113
|
ERB.new(File.read(serving_file_name)).result(binding)
|
108
114
|
end
|
109
115
|
end
|
@@ -19,9 +19,11 @@ module ServiceLoader
|
|
19
19
|
end
|
20
20
|
|
21
21
|
# Serve plan
|
22
|
-
def serve_plan
|
22
|
+
def serve_plan(thor_options)
|
23
23
|
require "sinatra"
|
24
24
|
require_relative "virtual_server"
|
25
|
+
GlobalSettings.key = thor_options[:key] if thor_options[:key]
|
26
|
+
GlobalSettings.cert = thor_options[:crt] if thor_options[:crt]
|
25
27
|
VirtualServer.run!
|
26
28
|
end
|
27
29
|
end
|
data/lib/file_sv/sv_plan.rb
CHANGED
@@ -15,7 +15,7 @@ class SvPlan
|
|
15
15
|
def create(folder)
|
16
16
|
self.serving_folder = folder
|
17
17
|
puts "Creating service based on files in #{folder}"
|
18
|
-
file_list = Dir.glob("#{folder}/**/*.*") - Dir.glob(GlobalSettings.ignore_files)
|
18
|
+
file_list = Dir.glob("#{folder}/**/*.*") - Dir.glob("#{folder}/#{GlobalSettings.ignore_files}")
|
19
19
|
file_list.each { |file| process_file file }
|
20
20
|
end
|
21
21
|
|
@@ -34,10 +34,10 @@ class SvPlan
|
|
34
34
|
# Show plan
|
35
35
|
def show
|
36
36
|
endpoint_desc = ""
|
37
|
-
endpoints.sort { |a, b| a[0].
|
37
|
+
endpoints.sort { |a, b| a[0].casecmp b[0] }.each do |endpoint, methods|
|
38
38
|
endpoint_desc += "#{endpoint} \n"
|
39
39
|
methods.each do |method_name, endpoints|
|
40
|
-
endpoint_desc +=
|
40
|
+
endpoint_desc += description_message method_name, endpoints
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
@@ -51,10 +51,19 @@ Serving based on folder: #{Dir.pwd}. Related to folder executed: #{serving_folde
|
|
51
51
|
"Endpoints: #{endpoints.inspect}"
|
52
52
|
end
|
53
53
|
|
54
|
+
# Add endpoint to plan
|
55
|
+
# @param [PlannedEndpoint] other Endpoint to add to plan
|
54
56
|
def +(other)
|
55
57
|
@endpoints[other.path] ||= {}
|
56
58
|
@endpoints[other.path][other.method] ||= []
|
57
59
|
@endpoints[other.path][other.method] << other
|
58
60
|
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# @return [String]
|
65
|
+
def description_message(method_name, endpoints)
|
66
|
+
" #{method_name.upcase} (#{endpoints.size} responses) #{endpoints.collect(&:status_code)}\n"
|
67
|
+
end
|
59
68
|
end
|
60
69
|
end
|
data/lib/file_sv/version.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
3
|
+
require "puma"
|
4
4
|
require "sinatra"
|
5
5
|
require "docdsl"
|
6
|
+
require "openssl"
|
6
7
|
|
7
8
|
# Virtual server hosting virtual service defined through files
|
8
9
|
class VirtualServer < Sinatra::Base
|
9
|
-
set :server,
|
10
|
+
set :server, :puma
|
11
|
+
enable :logging if ENV['debug'] == "true"
|
10
12
|
set :bind, "0.0.0.0"
|
11
13
|
|
12
14
|
register Sinatra::DocDsl
|
@@ -24,28 +26,44 @@ for more details'
|
|
24
26
|
|
25
27
|
doc_endpoint "/docs"
|
26
28
|
|
29
|
+
# Output for endpoint, either a file or text content
|
30
|
+
# @param [PlannedEndpoint] endpoint Planned endpoint to serve
|
31
|
+
def output_for(endpoint, binding)
|
32
|
+
endpoint.file? ? send_file(endpoint.serving_file_name) : endpoint.content(binding)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Log endpoint. Return content and status code defined by endpoint
|
36
|
+
# @param [PlannedEndpoint] endpoint Planned endpoint to serve
|
37
|
+
def serve(endpoint, id = nil)
|
38
|
+
@id = id
|
39
|
+
if ENV['debug'] == "true"
|
40
|
+
message = "Using endpoint based on file #{endpoint.serving_file_name}."
|
41
|
+
message += " Using param '#{@id}'" if id
|
42
|
+
puts message
|
43
|
+
end
|
44
|
+
[endpoint.status_code, output_for(endpoint, binding)]
|
45
|
+
end
|
46
|
+
|
27
47
|
SvPlan.endpoints.each do |_endpoint_path, endpoint_value|
|
28
48
|
endpoint_value.each do |_method_name, endpoints|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
49
|
+
endpoint_base = endpoints[0]
|
50
|
+
documentation "Endpoint #{endpoint_base.path}" do
|
51
|
+
response "#{endpoints.size} kinds of response"
|
52
|
+
end
|
53
|
+
if endpoint_base.path.include? "#{File::Separator}:"
|
54
|
+
send(endpoint_base.method, endpoint_base.path) do |id|
|
55
|
+
endpoint = endpoints.sample
|
56
|
+
@params = params
|
57
|
+
puts "#{endpoint_base.method} #{endpoint_base.path} ?#{@params}"
|
58
|
+
serve endpoint, id
|
37
59
|
end
|
38
60
|
else
|
39
|
-
endpoint_base = endpoints[0]
|
40
|
-
documentation "Endpoint #{endpoint_base.path}" do
|
41
|
-
response "#{endpoints.size} kinds of response"
|
42
|
-
end
|
43
61
|
send(endpoint_base.method, endpoint_base.path) do
|
44
|
-
|
45
|
-
|
46
|
-
|
62
|
+
endpoint = endpoints.sample
|
63
|
+
@params = params
|
64
|
+
puts "#{endpoint_base.method} #{endpoint_base.path} ?#{@params}" unless ENV['ignore_path'] && ("/#{ENV['ignore_path']}" == endpoint_base.path)
|
65
|
+
serve endpoint
|
47
66
|
end
|
48
|
-
# Average same methods at same endpoint
|
49
67
|
end
|
50
68
|
end
|
51
69
|
end
|
@@ -6,8 +6,8 @@ class YamlProcessor
|
|
6
6
|
# Process YAML file
|
7
7
|
def process(filename)
|
8
8
|
if filename == "/file_sv.yaml"
|
9
|
-
puts "Overriding default config based on #{File.join(Dir.pwd, filename[1
|
10
|
-
load_default_config filename[1
|
9
|
+
puts "Overriding default config based on #{File.join(Dir.pwd, filename[1..])}"
|
10
|
+
load_default_config filename[1..]
|
11
11
|
else
|
12
12
|
puts "Skipping #{filename}"
|
13
13
|
end
|
data/lib/file_sv.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "yaml"
|
3
4
|
require_relative "file_sv/version"
|
4
5
|
require_relative "file_sv/global_settings"
|
5
6
|
require_relative "file_sv/sv_plan"
|
@@ -29,17 +30,35 @@ CONFIG_FILE = "file_sv.yaml"
|
|
29
30
|
|
30
31
|
# Set values in global settings based on config
|
31
32
|
def load_default_config(file_path)
|
32
|
-
require "yaml"
|
33
33
|
return unless File.exist? file_path
|
34
34
|
|
35
35
|
config = YAML.load_file file_path
|
36
|
+
return unless config # Handle empty YAML file
|
37
|
+
|
36
38
|
config["global"]&.each do |key, value|
|
37
39
|
GlobalSettings.send("#{key}=", value)
|
38
40
|
end
|
39
41
|
|
42
|
+
load_rest_method_config config
|
43
|
+
end
|
44
|
+
|
45
|
+
# Load details of each REST method
|
46
|
+
def load_rest_method_config(config)
|
40
47
|
FileSv.rest_methods.each do |method, setting_class|
|
41
48
|
config[method.to_s]&.each { |key, value| setting_class.send("#{key}=", value) }
|
42
49
|
end
|
43
50
|
end
|
44
51
|
|
52
|
+
# Set global params based on ENV vars
|
53
|
+
def set_based_on_env_vars
|
54
|
+
GlobalSettings.instance_variables.each do |setting|
|
55
|
+
setting_name = setting.to_s[1..]
|
56
|
+
if ENV[setting_name]
|
57
|
+
puts "Setting #{setting_name} to #{ENV[setting_name]}"
|
58
|
+
GlobalSettings.send("#{setting_name}=", ENV[setting_name])
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
45
63
|
load_default_config CONFIG_FILE
|
64
|
+
set_based_on_env_vars
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: file_sv
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Garratt
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-12-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faker
|
@@ -67,19 +67,19 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: puma
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - ">="
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
75
|
+
version: '0'
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
82
|
+
version: '0'
|
83
83
|
description: |-
|
84
84
|
Create a virtual REST service through file structure. Customize it more
|
85
85
|
through ERB and special file names
|
@@ -124,7 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
124
124
|
- !ruby/object:Gem::Version
|
125
125
|
version: '0'
|
126
126
|
requirements: []
|
127
|
-
rubygems_version: 3.
|
127
|
+
rubygems_version: 3.1.4
|
128
128
|
signing_key:
|
129
129
|
specification_version: 4
|
130
130
|
summary: REST service virtualisation through file structure.
|