shogun 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/shogun +18 -0
- data/lib/shogun.rb +29 -0
- data/lib/shogun/authorizer.rb +7 -0
- data/lib/shogun/control.rb +82 -0
- data/lib/shogun/control/null.rb +15 -0
- data/lib/shogun/database.rb +44 -0
- data/lib/shogun/denormalizer.rb +54 -0
- data/lib/shogun/dispatch.rb +67 -0
- data/lib/shogun/dispatch/lookup.rb +57 -0
- data/lib/shogun/dispatch/payload.rb +18 -0
- data/lib/shogun/dispatch/route.rb +34 -0
- data/lib/shogun/dispatch/route/null.rb +11 -0
- data/lib/shogun/endpoint.rb +11 -0
- data/lib/shogun/normalizer.rb +31 -0
- data/lib/shogun/presenter.rb +57 -0
- data/lib/shogun/presenters.rb +28 -0
- data/lib/shogun/server.rb +49 -0
- data/lib/shogun/validator.rb +15 -0
- data/lib/shogun/verifier.rb +9 -0
- data/lib/shogun/verifier/allowed.rb +29 -0
- data/lib/shogun/verifier/match.rb +31 -0
- data/lib/shogun/verifier/presence.rb +26 -0
- data/lib/shogun/verifier/range.rb +30 -0
- data/lib/shogun/verifier/uniqueness.rb +23 -0
- data/lib/shogun/version.rb +3 -0
- data/spec/lib/shogun/authorizer_spec.rb +5 -0
- data/spec/lib/shogun/control/null_spec.rb +5 -0
- data/spec/lib/shogun/control_spec.rb +5 -0
- data/spec/lib/shogun/database_spec.rb +5 -0
- data/spec/lib/shogun/denormalizer_spec.rb +5 -0
- data/spec/lib/shogun/dispatch/lookup_spec.rb +5 -0
- data/spec/lib/shogun/dispatch/payload_spec.rb +5 -0
- data/spec/lib/shogun/dispatch/route/null_spec.rb +5 -0
- data/spec/lib/shogun/dispatch/route_spec.rb +5 -0
- data/spec/lib/shogun/dispatch_spec.rb +5 -0
- data/spec/lib/shogun/endpoint_spec.rb +5 -0
- data/spec/lib/shogun/normalizer_spec.rb +5 -0
- data/spec/lib/shogun/presenter_spec.rb +5 -0
- data/spec/lib/shogun/presenters_spec.rb +5 -0
- data/spec/lib/shogun/server_spec.rb +5 -0
- data/spec/lib/shogun/validator_spec.rb +5 -0
- data/spec/lib/shogun/verifier/allowed_spec.rb +5 -0
- data/spec/lib/shogun/verifier/match_spec.rb +5 -0
- data/spec/lib/shogun/verifier/presence_spec.rb +5 -0
- data/spec/lib/shogun/verifier/range_spec.rb +5 -0
- data/spec/lib/shogun/verifier/uniqueness_spec.rb +5 -0
- data/spec/lib/shogun/verifier_spec.rb +5 -0
- data/spec/lib/shogun_spec.rb +5 -0
- data/spec/spec_helper.rb +6 -0
- metadata +385 -0
@@ -0,0 +1,11 @@
|
|
1
|
+
module Shogun
|
2
|
+
module Endpoint
|
3
|
+
private def route(verb:, matcher: nil, control:)
|
4
|
+
Dispatch::Route.new(namespace: namespace, verb: verb, matcher: matcher, control: control)
|
5
|
+
end
|
6
|
+
|
7
|
+
private def namespace
|
8
|
+
self.class.const_get("NAMESPACE")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Shogun
|
2
|
+
module Normalizer
|
3
|
+
def to_hash
|
4
|
+
attributes.inject({}) do |hash, attribute|
|
5
|
+
if public_send(attribute)
|
6
|
+
hash.merge(attribute => public_send(attribute))
|
7
|
+
else
|
8
|
+
hash
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def as_meta
|
14
|
+
metadata.inject({}) do |hash, meta|
|
15
|
+
if public_send(meta)
|
16
|
+
hash.merge(meta => public_send(meta))
|
17
|
+
else
|
18
|
+
hash
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private def attributes
|
24
|
+
self.class.const_get("ATTRIBUTES")
|
25
|
+
end
|
26
|
+
|
27
|
+
private def metadata
|
28
|
+
self.class.const_get("METADATA")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Shogun
|
2
|
+
module Presenter
|
3
|
+
def initialize(resource:, meta: {}, links: {}, linked: {})
|
4
|
+
@source = resource
|
5
|
+
@meta = meta
|
6
|
+
@links = links
|
7
|
+
@linked = linked
|
8
|
+
end
|
9
|
+
|
10
|
+
def href
|
11
|
+
[host, namespace, id].join("/")
|
12
|
+
end
|
13
|
+
|
14
|
+
def meta
|
15
|
+
@meta
|
16
|
+
end
|
17
|
+
|
18
|
+
def linked
|
19
|
+
@linked
|
20
|
+
end
|
21
|
+
|
22
|
+
def links
|
23
|
+
@links.tap do |hash|
|
24
|
+
associations.each do |name|
|
25
|
+
case association(name).macro
|
26
|
+
when :has_many
|
27
|
+
hash.store(name, source.public_send(name).pluck(:id))
|
28
|
+
when :belongs_to
|
29
|
+
hash.store(name, source.public_send(association(name).foreign_key))
|
30
|
+
when :has_one
|
31
|
+
hash.store(name, source.public_send(name).pluck(:id))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private def host
|
38
|
+
ENV["API_HOST"]
|
39
|
+
end
|
40
|
+
|
41
|
+
private def associations
|
42
|
+
self.class.const_get("ASSOCIATIONS")
|
43
|
+
end
|
44
|
+
|
45
|
+
private def namespace
|
46
|
+
self.class.const_get("NAMESPACE")
|
47
|
+
end
|
48
|
+
|
49
|
+
private def association(name)
|
50
|
+
source.class.reflections[name.to_sym]
|
51
|
+
end
|
52
|
+
|
53
|
+
private def source
|
54
|
+
@source
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Shogun
|
2
|
+
class Presenters
|
3
|
+
def initialize(resources:, cast:, meta: {}, links: {}, linked: {})
|
4
|
+
@sources = resources.map do |resource|
|
5
|
+
cast.new(resource: resource)
|
6
|
+
end
|
7
|
+
@meta = meta
|
8
|
+
@links = links
|
9
|
+
@linked = linked
|
10
|
+
end
|
11
|
+
|
12
|
+
def map(&block)
|
13
|
+
@sources.map(&block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def meta
|
17
|
+
@meta
|
18
|
+
end
|
19
|
+
|
20
|
+
def linked
|
21
|
+
@linked
|
22
|
+
end
|
23
|
+
|
24
|
+
def links
|
25
|
+
@links
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Shogun
|
2
|
+
module Server
|
3
|
+
def serve
|
4
|
+
@rack.use(Rack::Lineprof) if ENV["RACK_ENV"] == "development"
|
5
|
+
@rack.use(Rack::Log, @logger)
|
6
|
+
@rack.use(Rack::Runtime)
|
7
|
+
@rack.use(ActiveRecord::ConnectionAdapters::ConnectionManagement)
|
8
|
+
@rack.use(ActiveRecord::QueryCache)
|
9
|
+
@rack.use(Rack::Chunker)
|
10
|
+
@rack.use(Rack::AcceptSetter, accept_types)
|
11
|
+
@rack.use(Rack::ContentLengthSetter)
|
12
|
+
@rack.use(Rack::AuthenticationBearer, &bearer)
|
13
|
+
@rack.use(Rack::BodyDeserializer, deserialization_map)
|
14
|
+
@rack.use(Rack::BodySerializer, serialization_map, default_serializer)
|
15
|
+
@rack.use(Rack::Protection::RemoteReferrer)
|
16
|
+
@rack.use(Rack::Protection::HttpOrigin)
|
17
|
+
@rack.use(Rack::Protection::EscapedParams)
|
18
|
+
@rack.use(Rack::Protection::XSSHeader)
|
19
|
+
@rack.use(Rack::Protection::FrameOptions)
|
20
|
+
@rack.use(Rack::Protection::PathTraversal)
|
21
|
+
@rack.use(Rack::Protection::IPSpoofing)
|
22
|
+
@rack.run(Shogun::Dispatch.new(logger: @logger, &endpoints))
|
23
|
+
end
|
24
|
+
|
25
|
+
private def endpoints
|
26
|
+
self.class.const_get("ENDPOINTS")
|
27
|
+
end
|
28
|
+
|
29
|
+
private def deserialization_map
|
30
|
+
self.class.const_get("DESERIALIZATION_MAP")
|
31
|
+
end
|
32
|
+
|
33
|
+
private def bearer
|
34
|
+
self.class.const_get("BEARER")
|
35
|
+
end
|
36
|
+
|
37
|
+
private def serialization_map
|
38
|
+
self.class.const_get("SERIALIZATION_MAP")
|
39
|
+
end
|
40
|
+
|
41
|
+
private def default_serializer
|
42
|
+
self.class.const_get("DEFAULT_SERIALIZER")
|
43
|
+
end
|
44
|
+
|
45
|
+
private def accept_types
|
46
|
+
self.class.const_get("ACCEPT_TYPES")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Shogun
|
2
|
+
module Verifier
|
3
|
+
class Allowed
|
4
|
+
def initialize(name:, data:, list: [])
|
5
|
+
@data = data
|
6
|
+
@name = name
|
7
|
+
@list = list || []
|
8
|
+
end
|
9
|
+
|
10
|
+
def valid?
|
11
|
+
@data.all?(&method(:allowed?))
|
12
|
+
end
|
13
|
+
|
14
|
+
def id
|
15
|
+
"#{@name}_not_allowed"
|
16
|
+
end
|
17
|
+
|
18
|
+
def context
|
19
|
+
{
|
20
|
+
"unallowed" => @data.reject(&method(:allowed?))
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
private def allowed?(item)
|
25
|
+
@list.include?(item)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Shogun
|
2
|
+
module Verifier
|
3
|
+
class Match
|
4
|
+
def initialize(data:, name:, comparison:)
|
5
|
+
@data = data
|
6
|
+
@name = name
|
7
|
+
@comparison = comparison
|
8
|
+
end
|
9
|
+
|
10
|
+
def valid?
|
11
|
+
if @comparison.is_a?(Regexp)
|
12
|
+
@data =~ @comparison
|
13
|
+
else
|
14
|
+
@data == @comparison || @comparison == @data
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def id
|
19
|
+
"#{@name}_does_not_match"
|
20
|
+
end
|
21
|
+
|
22
|
+
def context
|
23
|
+
if @comparison.is_a?(Regexp)
|
24
|
+
{
|
25
|
+
"pattern" => @comparison
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Shogun
|
2
|
+
module Verifier
|
3
|
+
class Presence
|
4
|
+
def initialize(name:, data:)
|
5
|
+
@data = data
|
6
|
+
@name = name
|
7
|
+
end
|
8
|
+
|
9
|
+
def valid?
|
10
|
+
if @data.is_a?(String)
|
11
|
+
@data.split("").any?
|
12
|
+
else
|
13
|
+
!@data.nil?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def id
|
18
|
+
"#{@name}_not_present"
|
19
|
+
end
|
20
|
+
|
21
|
+
def context
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Shogun
|
2
|
+
module Verifier
|
3
|
+
class Range
|
4
|
+
def initialize(name:, data:, range:)
|
5
|
+
@data = data
|
6
|
+
@name = name
|
7
|
+
@range = range
|
8
|
+
end
|
9
|
+
|
10
|
+
def valid?
|
11
|
+
if @data.is_a?(String)
|
12
|
+
@range.cover?(@data.size)
|
13
|
+
else
|
14
|
+
@range.cover?(@data)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def id
|
19
|
+
"#{@name}_was_out_of_range"
|
20
|
+
end
|
21
|
+
|
22
|
+
def context
|
23
|
+
{
|
24
|
+
"maximum" => @range.max,
|
25
|
+
"minimum" => @range.min
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Shogun
|
2
|
+
module Verifier
|
3
|
+
class Uniqueness
|
4
|
+
def initialize(data:, name:, list: [])
|
5
|
+
@data = data
|
6
|
+
@name = name
|
7
|
+
@list = list || []
|
8
|
+
end
|
9
|
+
|
10
|
+
def valid?
|
11
|
+
!@list.include?(@data)
|
12
|
+
end
|
13
|
+
|
14
|
+
def id
|
15
|
+
"#{@name}_not_unique"
|
16
|
+
end
|
17
|
+
|
18
|
+
def context
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|