file_sv 0.1.0

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