file_sv 0.1.3 → 0.1.7

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