Skip to content

Instantly share code, notes, and snippets.

@dscottboggs
Created June 20, 2019 23:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dscottboggs/3477cfa4a1a5f9af44c32f203fde5b71 to your computer and use it in GitHub Desktop.
Save dscottboggs/3477cfa4a1a5f9af44c32f203fde5b71 to your computer and use it in GitHub Desktop.
Kemal won't work with these routes
require "kemal"
require "spec-kemal"
# From here until the note denoting the end of this section is just an error-
# handling solution. It is only here to better demonstrate/replicate the issue.
# ERROR HANDLING SECTION
# Base error handler exception type.
abstract class HTTPStatusError < Exception
abstract def status_code
end
# The relevant data in one exception that has been raised
struct ErrorData
include JSON::Serializable
property type : String
property message : String?
property status_code : HTTP::Status?
def initialize(@type : String,
@message : String?,
@status_code : HTTP::Status? = nil)
end
def self.new(error : Exception)
if error.is_a? HTTPStatusError
# expected error handler type
new(
type: error.class.to_s,
message: error.message,
status_code: error.status_code,
)
else
# any generic exception
new type: error.class.to_s, message: error.message
end
end
end
# A list of all exceptions that have been raised and caught, along with their
# causes.
struct ErrorResponse
include JSON::Serializable
property errors : Array(ErrorData)
def initialize(@errors : Array(ErrorData))
end
def self.new(error : Exception)
messages = [ErrorData.new error]
while error = error.cause
messages << ErrorData.new error
end
new messages
end
end
{% for status_code in HTTP::Status.constants %}
{%
type_name = status_code.stringify.downcase.camelcase.id
%}
# An exception for the {{status_code}} status
class {{type_name.id}} < HTTPStatusError
STATUS_CODE = HTTP::Status::{{status_code}}
def status_code
STATUS_CODE
end
getter context : HTTP::Server::Context
def initialize(@context : HTTP::Server::Context, message : String? = nil)
@context.response.status_code = self.status_code.value
super message || HTTP::Status::{{status_code}}.description || {{status_code.stringify.capitalize.gsub /_/, " "}}
end
end
# Register the error handler with Kemal
if HTTP::Status::{{status_code}}.value >= 400
Kemal.config.add_error_handler HTTP::Status::{{status_code.id}}.value do |context, exception|
context.response.status_code = exception.status_code.value if exception.responds_to? :status_code
response_data = ErrorResponse.new exception
response_data.to_json context.response
context.response.flush
nil
end
end
{% end %}
ROUTE_MAPPING = [
{"get", "/dppmrestapi/actions/app/:app_name/config/:key"},
{"post", "/dppmrestapi/actions/app/:app_name/config-add/:key"},
{"put", "/dppmrestapi/actions/app/:app_name/config-set/:key"}, # This one
{"delete", "/dppmrestapi/actions/app/:app_name/config-delete/:key"},
{"get", "/dppmrestapi/actions/app/:app_name/config-get/:key"},
{"get", "/dppmrestapi/actions/app/:app_name/config"},
{"put", "/dppmrestapi/actions/app/:app_name/service/boot"}, # This one
{"put", "/dppmrestapi/actions/app/:app_name/service/reload"}, # This one
{"put", "/dppmrestapi/actions/app/:app_name/service/restart"}, # This one
{"put", "/dppmrestapi/actions/app/:app_name/service/start"}, # This one
{"put", "/dppmrestapi/actions/app/:app_name/service/status"}, # This one
{"put", "/dppmrestapi/actions/app/:app_name/service/stop"}, # This one
{"get", "/dppmrestapi/actions/app/:app_name/libs"},
{"get", "/dppmrestapi/actions/app/:app_name/app"},
{"get", "/dppmrestapi/actions/app/:app_name/pkg"},
{"get", "/dppmrestapi/actions/app/:app_name/logs"},
{"put", "/dppmrestapi/actions/app/:package_name/install"}, # This one
{"delete", "/dppmrestapi/actions/app/:app_name/remove"},
{"get", "/dppmrestapi/actions/pkg"},
{"delete", "/dppmrestapi/actions/pkg/clean"},
{"get", "/dppmrestapi/actions/pkg/:id/query"},
{"delete", "/dppmrestapi/actions/pkg/:id/delete"},
{"post", "/dppmrestapi/actions/pkg/:id/build"},
{"get", "/dppmrestapi/actions/service"},
{"get", "/dppmrestapi/actions/service/status"},
{"put", "/dppmrestapi/actions/service/:service/boot"},
{"put", "/dppmrestapi/actions/service/:service/reload"},
{"put", "/dppmrestapi/actions/service/:service/restart"},
{"put", "/dppmrestapi/actions/service/:service/start"},
{"get", "/dppmrestapi/actions/service/:service/status"},
{"put", "/dppmrestapi/actions/service/:service/stop"},
{"get", "/dppmrestapi/actions/src"},
{"get", "/dppmrestapi/actions/src/:type"},
# {"options", "/dppmrestapi/actions/api"},
# options ^^^ method doesn't work with spec-kemal
]
{% for mapping in ROUTE_MAPPING %}
{% method = mapping[0]; route = mapping[1] %}
{{method.id}} {{route}} do |env|
%[received {{method.upcase.id}} request {{route}}]
end
{% end %}
Kemal.config.env = "test"
Kemal.run
{% for mapping in ROUTE_MAPPING %}
{% method = mapping[0]; route = mapping[1] %}
describe %[{{method.upcase.id}} -- {{route}}] do
it "responds with 200" do
{{method.id}} {{route}}
if response.status_code != 200
fail ErrorResponse.from_json(response.body).errors.to_s
end
end
end
{% end %}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment