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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b8c717829a4044f5970f099311a51d86317e009cb04d01e2b596e2ac24ee5296
4
- data.tar.gz: 64a21579ccd9c87ea14a0e8b634f9095450cea66167ef0d414bff97e77f7ce3f
3
+ metadata.gz: 43425956ba3f1325aa81876eea311cabd364f35ae2735adf79f4cb2daed69dd4
4
+ data.tar.gz: 629f16289572cb0407ae010a5322f96b07ab6261e591b55dd080f3c9d5e5b58c
5
5
  SHA512:
6
- metadata.gz: 594bfe0862c82585f8568cf3a46d2952bd631bf6795fd7b518a74a4b578eb9eab500523016602110f5ca60094f9a53bfe91c395970ad3ca5774d3d51f2a8427e
7
- data.tar.gz: dd153f38c16a882d3ff806dcec71a7b798e7578aea0fe5322f868ea77bd6af7958e7eefc23b47e31f18bb35a78c869e0386155b7936e62086e07411734a2a9ab
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
- SvPlan + PlannedEndpoint.new(filename)
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 = File.split(path).first
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
@@ -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].length - b[0].length }.each do |endpoint, methods|
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 += " #{method_name.upcase} (#{endpoints.size} responses)\n"
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FileSv
4
- VERSION = "0.1.3"
4
+ VERSION = "0.1.7"
5
5
  end
@@ -1,12 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "webrick"
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, "webrick"
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
- if endpoints.size < 2
30
- endpoint = endpoints[0]
31
- documentation "Endpoint #{endpoint.path}" do
32
- response "1 kind of response"
33
- status endpoint.status_code
34
- end
35
- send(endpoint.method, endpoint.path) do
36
- [endpoint.status_code, endpoint.file? ? send_file(endpoint.serving_file_name) : endpoint.content]
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
- my_points = endpoints
45
- endpoint = my_points.sample
46
- [endpoint.status_code, endpoint.file? ? send_file(endpoint.serving_file_name) : endpoint.content]
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..-1])}"
10
- load_default_config filename[1..-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.3
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-03-05 00:00:00.000000000 Z
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: webrick
70
+ name: puma
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: 1.7.0
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: 1.7.0
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.2.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.