file_sv 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 373ecafe042decab366929da1430ddb873aca264006aaa1d59c4eaa5d67b68d0
4
+ data.tar.gz: 730539bcd6461534c4ff9899abaf989518d002ffe0ab2865f87b30d08bd0b463
5
+ SHA512:
6
+ metadata.gz: f68f20ec798767c092128c50dfc6bd131d77350566b3dc37c72b2497728126bdfdb674cce6c99edf6882285761447b106895be6418cd1bf989e0692868ab4500
7
+ data.tar.gz: c3f182e5d686d715b28513dde7a72231a181312e7e9be729eedc34ff464b153f93908d84417ff340f2ff9b3a9ee4cfb8af6f867d0e5eaf58963d5cbf23e5641e
data/exe/file_sv ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "thor"
5
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), "..", "lib")
6
+
7
+ # Executable for Generic Test
8
+ class Exe < Thor
9
+ desc "plan folder", "Show plan for service virtualization based on folder"
10
+ def plan(folder)
11
+ require "file_sv"
12
+ ServiceLoader.create_plan_for folder
13
+ end
14
+
15
+ desc "serve folder", "Serve virtual service based on folder"
16
+ def serve(folder)
17
+ plan folder
18
+ ServiceLoader.serve_plan
19
+ end
20
+
21
+ desc "version", "Version of FileTest"
22
+ def version
23
+ require "file_sv"
24
+ puts "GenericTest version #{FileTest::VERSION}"
25
+ end
26
+ end
27
+
28
+ Exe.start(ARGV)
data/lib/file_sv.rb ADDED
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "file_sv/version"
4
+ require_relative "file_sv/global_settings"
5
+ require_relative "file_sv/sv_plan"
6
+ require_relative "file_sv/service_loader"
7
+ require_relative "file_sv/planned_endpoint"
8
+
9
+ # Create Service Virtualization from a simple file system
10
+ module FileSv
11
+ # General error for FileSv
12
+ class Error < StandardError; end
13
+
14
+ # Error related to incorrect format of filename
15
+ class FileNameError < Error; end
16
+
17
+ class << self
18
+ def rest_methods
19
+ {
20
+ get: GetSettings, post: PostSettings, patch: PatchSettings, options: OptionsSettings,
21
+ delete: DeleteSettings
22
+ }
23
+ end
24
+ end
25
+ end
26
+
27
+ require "yaml"
28
+ CONFIG_FILE = "file_sv.yaml"
29
+ if File.exist? CONFIG_FILE
30
+ # Set values in global settings based on config
31
+ class GlobalSettings
32
+ config = YAML.load_file CONFIG_FILE
33
+ config["global"]&.each do |key, value|
34
+ send("#{key}=", value)
35
+ end
36
+
37
+ FileSv.rest_methods.each do |method, setting_class|
38
+ config[method.to_s]&.each do |key, value|
39
+ setting_class.send("#{key}=", value)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "sv_plan"
4
+ # General render for any non special files
5
+ class FileProcessor
6
+ class << self
7
+ # Process file, adding it to plan
8
+ def process(filename)
9
+ SvPlan + PlannedEndpoint.new(filename)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Settings to configure this gem
4
+ class GlobalSettings
5
+ @default_method = "get"
6
+
7
+ @empty_body_status = 204
8
+ class << self
9
+ # @return [String] Default REST method when none specified by filename
10
+ attr_accessor :default_method
11
+ # @return [Integer] Default status of response when file is empty
12
+ attr_accessor :empty_body_status
13
+ end
14
+ end
15
+
16
+ module CommonHttpSettings
17
+ attr_accessor :default_status
18
+ end
19
+
20
+ # Settings specific to GET
21
+ class GetSettings
22
+ extend CommonHttpSettings
23
+ end
24
+
25
+ # Settings specific to POST
26
+ class PostSettings
27
+ extend CommonHttpSettings
28
+ end
29
+
30
+ # Settings specific to PATCH
31
+ class PatchSettings
32
+ extend CommonHttpSettings
33
+ end
34
+
35
+ # Settings specific to OPTIONS
36
+ class OptionsSettings
37
+ extend CommonHttpSettings
38
+ end
39
+
40
+ # Settings specific to DELETE
41
+ class DeleteSettings
42
+ extend CommonHttpSettings
43
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+ require "securerandom"
5
+
6
+ # Endpoint planned to be served
7
+ class PlannedEndpoint
8
+ # Location where endpoint will be hosted
9
+ attr_accessor :path
10
+ # @return [String] Path where content is sourced from
11
+ attr_accessor :file_path
12
+ # @return [String] REST Method
13
+ attr_accessor :method
14
+ # @return [Integer] HTTP Status code
15
+ attr_accessor :status_code
16
+
17
+ # @return [Array]
18
+ HTTP_METHODS = %w[get post patch delete options].freeze
19
+
20
+ # Represent a new endpoint
21
+ def initialize(path)
22
+ self.file_path = path
23
+ self.path = File.split(path).first
24
+ @file = true if not_text?
25
+ assign_params_from_name
26
+ self.method ||= GlobalSettings.default_method
27
+ return if status_code
28
+
29
+ set_default_status_code
30
+ end
31
+
32
+ # @return [Boolean] Whether endpoint serves a file not a string
33
+ def file?
34
+ @file
35
+ end
36
+
37
+ def default_empty_code
38
+ return false if not_text?
39
+
40
+ if content.strip.empty?
41
+ self.status_code = GlobalSettings.empty_body_status
42
+ return true
43
+ end
44
+ false
45
+ end
46
+
47
+ def set_default_status_code
48
+ return if default_empty_code
49
+
50
+ setting_class = FileSv.rest_methods[method.to_sym]
51
+ raise NotImplementedError, "Unable to interpret method #{method}" unless setting_class
52
+
53
+ self.status_code = setting_class.default_status
54
+ end
55
+
56
+ # @return [String] Filename without extension or containing folder
57
+ def filename
58
+ File.basename(file_path, ".*")
59
+ end
60
+
61
+ # Set attributes based on filename
62
+ def assign_params_from_name
63
+ file_parts = filename.split("_")
64
+ assign_method_from file_parts
65
+ assign_status_from file_parts
66
+ end
67
+
68
+ # Set status code based on filename
69
+ def assign_status_from(file_parts)
70
+ status_parts = file_parts.find_all { |part| part.to_i > 99 && part.to_i < 1000 }
71
+ if status_parts.size > 1
72
+ raise FileSv::FileNameError, "Filename #{filename} has more than 1 status code #{status_parts}"
73
+ end
74
+
75
+ self.status_code = status_parts[0].to_i if status_parts.size == 1
76
+ end
77
+
78
+ # Set REST method used based on filename
79
+ def assign_method_from(file_parts)
80
+ method_parts = file_parts.find_all { |pa| HTTP_METHODS.include? pa }
81
+ if method_parts.size > 1
82
+ raise FileSv::FileNameError, "Filename #{filename} has more than 1 REST methods #{method_parts}"
83
+ end
84
+
85
+ self.method = method_parts[0] if method_parts.size == 1
86
+ end
87
+
88
+ def serving_file_name
89
+ File.join(SvPlan.serving_folder, file_path)
90
+ end
91
+
92
+ # @return [Object] Content of file
93
+ def content
94
+ render_text
95
+ end
96
+
97
+ def not_text?
98
+ extension = File.extname(file_path).delete(".")
99
+ %w[ico png jpg mp4 mp3].include? extension
100
+ end
101
+
102
+ # @return [String] Render text
103
+ def render_text
104
+ ERB.new(File.read(serving_file_name)).result(binding)
105
+ end
106
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RenderFile
4
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Responsible for loading all files in service directory and creating service
4
+ module ServiceLoader
5
+ # @return [String] Folder where service served from
6
+ @serving_folder = ""
7
+
8
+ class << self
9
+ # Create virtual service plan based on folder
10
+ def create_plan_for(folder)
11
+ SvPlan.create folder
12
+ puts SvPlan.inspect
13
+ end
14
+
15
+ # Serve plan
16
+ def serve_plan
17
+ require "sinatra"
18
+ require_relative "virtual_server"
19
+ VirtualServer.run!
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "yaml_processor"
4
+ require_relative "file_processor"
5
+ # Holds plan of what virtual service will look like
6
+ class SvPlan
7
+ @endpoints = {}
8
+ class << self
9
+ # @return [Array]
10
+ attr_reader :endpoints
11
+ # @return [String] Folder plan is served from
12
+ attr_accessor :serving_folder
13
+
14
+ # Create a plan new plan for a virtual service
15
+ def create(folder)
16
+ self.serving_folder = folder
17
+ puts "Creating service based on files in #{folder}"
18
+ file_list = Dir.glob("#{folder}/**/*.*")
19
+ file_list.each do |file|
20
+ process_file file
21
+ end
22
+ end
23
+
24
+ def process_file(file)
25
+ file.slice! serving_folder
26
+ extension = File.extname(file)
27
+ case extension
28
+ when "yaml" then YamlProcessor.process(file)
29
+ else
30
+ FileProcessor.process(file)
31
+ end
32
+ end
33
+
34
+ def inspect
35
+ "** VIRTUAL SERVICE PLAN **
36
+ Serving based on folder: #{serving_folder}
37
+ Endpoints: #{endpoints.inspect}
38
+ "
39
+ end
40
+
41
+ def +(other)
42
+ @endpoints[other.path] ||= {}
43
+ @endpoints[other.path][other.method] ||= []
44
+ @endpoints[other.path][other.method] << other
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FileSv
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webrick"
4
+ require "sinatra"
5
+
6
+ # Virtual server hosting virtual service defined through files
7
+ class VirtualServer < Sinatra::Base
8
+ set :server, "webrick"
9
+ set :bind, "0.0.0.0"
10
+
11
+ SvPlan.endpoints.each do |_endpoint_path, endpoint_value|
12
+ endpoint_value.each do |_method_name, endpoints|
13
+ if endpoints.size < 2
14
+ endpoint = endpoints[0]
15
+ send(endpoint.method, endpoint.path) do
16
+ [endpoint.status_code, endpoint.file? ? send_file(endpoint.serving_file_name) : endpoint.content]
17
+ end
18
+ else
19
+ endpoint_base = endpoints[0]
20
+ send(endpoint_base.method, endpoint_base.path) do
21
+ my_points = endpoints
22
+ endpoint = my_points.sample
23
+ [endpoint.status_code, endpoint.file? ? send_file(endpoint.serving_file_name) : endpoint.content]
24
+ end
25
+ # Average same methods at same endpoint
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Process YAML files
4
+ class YamlProcessor
5
+ class << self
6
+ def process(filename)
7
+ puts "Overriding default config based on #{filename}" if filename == "file_sv"
8
+ end
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: file_sv
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Samuel Garratt
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-03-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sinatra
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: thor
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: webrick
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 1.7.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 1.7.0
55
+ description: |-
56
+ Create a virtual REST service through file structure. Customize it more
57
+ through ERB and special file names
58
+ email:
59
+ - samuel.garratt@integrationqa.com
60
+ executables:
61
+ - file_sv
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - exe/file_sv
66
+ - lib/file_sv.rb
67
+ - lib/file_sv/file_processor.rb
68
+ - lib/file_sv/global_settings.rb
69
+ - lib/file_sv/planned_endpoint.rb
70
+ - lib/file_sv/render_file.rb
71
+ - lib/file_sv/service_loader.rb
72
+ - lib/file_sv/sv_plan.rb
73
+ - lib/file_sv/version.rb
74
+ - lib/file_sv/virtual_server.rb
75
+ - lib/file_sv/yaml_processor.rb
76
+ homepage: https://gitlab.com/samuel-garratt/file_sv
77
+ licenses:
78
+ - MIT
79
+ metadata:
80
+ homepage_uri: https://gitlab.com/samuel-garratt/file_sv
81
+ source_code_uri: https://gitlab.com/samuel-garratt/file_sv
82
+ changelog_uri: https://gitlab.com/samuel-garratt/file_sv/CHANGELOG.md
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: 2.4.0
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubygems_version: 3.2.3
99
+ signing_key:
100
+ specification_version: 4
101
+ summary: REST service virtualisation through file structure.
102
+ test_files: []