shogun 1.0.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/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
|