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 +7 -0
- data/exe/file_sv +28 -0
- data/lib/file_sv.rb +43 -0
- data/lib/file_sv/file_processor.rb +12 -0
- data/lib/file_sv/global_settings.rb +43 -0
- data/lib/file_sv/planned_endpoint.rb +106 -0
- data/lib/file_sv/render_file.rb +4 -0
- data/lib/file_sv/service_loader.rb +22 -0
- data/lib/file_sv/sv_plan.rb +47 -0
- data/lib/file_sv/version.rb +5 -0
- data/lib/file_sv/virtual_server.rb +29 -0
- data/lib/file_sv/yaml_processor.rb +10 -0
- metadata +102 -0
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,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,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
|
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: []
|