feat: delete link
This commit is contained in:
+1
-1
@@ -1,2 +1,2 @@
|
||||
DATABASE_URL=sqlite3://./sqlite/data.db
|
||||
DATABASE_URL=sqlite3://./sqlite/data.db?journal_mode=wal&synchronous=normal
|
||||
APP_URL=http://localhost:4000
|
||||
|
||||
+4
-3
@@ -3,6 +3,7 @@
|
||||
/bin/
|
||||
/.shards/
|
||||
*.dwarf
|
||||
*.db
|
||||
.env.*
|
||||
url-shortener
|
||||
.DS_Store
|
||||
|
||||
./sqlite/*
|
||||
.env.production
|
||||
|
||||
+32
-20
@@ -8,20 +8,16 @@ module App::Controllers::Link
|
||||
include App::Lib
|
||||
|
||||
def call(env)
|
||||
json_params = env.params.json.to_h
|
||||
url = json_params.has_key?("url") ? json_params["url"] : nil
|
||||
raise App::BadRequestException.new(env, {"url" => "Required field"}) if !url
|
||||
body = parse_body(env, ["url"])
|
||||
|
||||
link = Link.new
|
||||
link.id = UUID.v4.to_s
|
||||
link.url = url.to_s
|
||||
link.url = body["url"].to_s
|
||||
link.slug = Random::Secure.urlsafe_base64(4)
|
||||
|
||||
changeset = Database.insert(link)
|
||||
|
||||
if !changeset.valid?
|
||||
errors = {"errors" => map_changeset_errors(changeset.errors)}
|
||||
raise App::UnprocessableEntityException.new(env, errors)
|
||||
raise App::UnprocessableEntityException.new(env, map_changeset_errors(changeset.errors))
|
||||
end
|
||||
|
||||
response = {"data" => App::Serializers::Link.new(link)}
|
||||
@@ -36,7 +32,8 @@ module App::Controllers::Link
|
||||
def call(env)
|
||||
slug = env.params.url["slug"]
|
||||
|
||||
link = Database.get_by!(Link, slug: slug)
|
||||
link = Database.get_by(Link, slug: slug)
|
||||
raise App::NotFoundException.new(env) if !link
|
||||
|
||||
spawn do
|
||||
link.click_counter = link.click_counter! + 1
|
||||
@@ -47,18 +44,19 @@ module App::Controllers::Link
|
||||
end
|
||||
end
|
||||
|
||||
env.redirect link.url!
|
||||
env.redirect link.url!, 301
|
||||
end
|
||||
end
|
||||
|
||||
class Get < App::Lib::BaseController
|
||||
class Read < App::Lib::BaseController
|
||||
include App::Models
|
||||
include App::Lib
|
||||
|
||||
def call(env)
|
||||
id = env.params.url["id"]
|
||||
|
||||
link = Database.get!(Link, id)
|
||||
link = Database.get(Link, id)
|
||||
raise App::NotFoundException.new(env) if !link
|
||||
|
||||
response = {"data" => App::Serializers::Link.new(link)}
|
||||
response.to_json
|
||||
@@ -71,20 +69,17 @@ module App::Controllers::Link
|
||||
|
||||
def call(env)
|
||||
id = env.params.url["id"]
|
||||
body = parse_body(env, ["url"])
|
||||
|
||||
json_params = env.params.json.to_h
|
||||
url = json_params.has_key?("url") ? json_params["url"] : nil
|
||||
raise App::BadRequestException.new(env, {"url" => "Required field"}) if !url
|
||||
link = Database.get(Link, id)
|
||||
raise App::NotFoundException.new(env) if !link
|
||||
|
||||
link = Database.get!(Link, id)
|
||||
link.url = url.to_s
|
||||
link.url = body["url"].to_s
|
||||
link.click_counter = 0
|
||||
|
||||
changeset = Database.update(link)
|
||||
|
||||
if !changeset.valid?
|
||||
errors = {"errors" => map_changeset_errors(changeset.errors)}
|
||||
raise App::UnprocessableEntityException.new(env, errors)
|
||||
raise App::UnprocessableEntityException.new(env, map_changeset_errors(changeset.errors))
|
||||
end
|
||||
|
||||
response = {"data" => App::Serializers::Link.new(link)}
|
||||
@@ -92,5 +87,22 @@ module App::Controllers::Link
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: delete
|
||||
class Delete < App::Lib::BaseController
|
||||
include App::Models
|
||||
include App::Lib
|
||||
|
||||
def call(env)
|
||||
id = env.params.url["id"]
|
||||
|
||||
link = Database.get(Link, id)
|
||||
raise App::NotFoundException.new(env) if !link
|
||||
|
||||
result = Database.raw_exec("DELETE FROM links WHERE id = (?)", link.id) # tempfix: Database.delete does not work
|
||||
if result.rows_affected == 0
|
||||
raise App::UnprocessableEntityException.new(env, { "id" => ["Row delete failed"] })
|
||||
end
|
||||
|
||||
env.response.status_code = 204
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+19
-1
@@ -1,11 +1,29 @@
|
||||
module App::Lib
|
||||
abstract class BaseController
|
||||
def map_changeset_errors(errors)
|
||||
def map_changeset_errors(errors)
|
||||
errors.reduce({} of String => Array(String)) do |memo, error|
|
||||
memo[error[:field]] = memo[error[:field]]? || [] of String
|
||||
memo[error[:field]] << error[:message]
|
||||
memo
|
||||
end
|
||||
end
|
||||
|
||||
def parse_body(env, fields)
|
||||
json_params = env.params.json.to_h
|
||||
missing_fields = [] of String
|
||||
|
||||
fields.each do |field|
|
||||
unless json_params.has_key?(field)
|
||||
missing_fields << field
|
||||
end
|
||||
end
|
||||
|
||||
unless missing_fields.empty?
|
||||
error_message = missing_fields.map { |field| "#{field}: Required field" }.join(", ")
|
||||
raise App::BadRequestException.new(env, error_message)
|
||||
end
|
||||
|
||||
json_params
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+2
-3
@@ -5,11 +5,10 @@ module App::Lib
|
||||
class Database
|
||||
extend Crecto::Repo
|
||||
|
||||
Query = Crecto::Repo::Query
|
||||
Multi = Crecto::Repo::Multi
|
||||
|
||||
config do |conf|
|
||||
conf.uri = ENV["DATABASE_URL"]
|
||||
end
|
||||
|
||||
Crecto::DbLogger.set_handler(STDOUT)
|
||||
end
|
||||
end
|
||||
|
||||
+10
-3
@@ -2,17 +2,24 @@ require "kemal"
|
||||
|
||||
module App
|
||||
class BadRequestException < Kemal::Exceptions::CustomException
|
||||
def initialize(context, message = Hash(String, String))
|
||||
def initialize(context, message : String)
|
||||
context.response.status_code = 400
|
||||
context.response.print({ "error" => message }.to_json)
|
||||
super(context)
|
||||
end
|
||||
end
|
||||
|
||||
class NotFoundException < Kemal::Exceptions::CustomException
|
||||
def initialize(context)
|
||||
context.response.status_code = 404
|
||||
super(context)
|
||||
end
|
||||
end
|
||||
|
||||
class UnprocessableEntityException < Kemal::Exceptions::CustomException
|
||||
def initialize(context, message = Hash(String, String))
|
||||
def initialize(context, message : Hash(String, Array(String)))
|
||||
context.response.status_code = 422
|
||||
context.response.print({ "error" => message }.to_json)
|
||||
context.response.print({ "errors" => message }.to_json)
|
||||
super(context)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,6 +10,7 @@ module App::Models
|
||||
field :click_counter, Int64, default: 0
|
||||
end
|
||||
|
||||
unique_constraint :slug
|
||||
validate_required [:slug, :url]
|
||||
end
|
||||
end
|
||||
|
||||
+5
-1
@@ -14,7 +14,7 @@ module App
|
||||
end
|
||||
|
||||
get "/api/links/:id" do |env|
|
||||
Controllers::Link::Get.new.call(env)
|
||||
Controllers::Link::Read.new.call(env)
|
||||
end
|
||||
|
||||
post "/api/links" do |env|
|
||||
@@ -24,4 +24,8 @@ module App
|
||||
put "/api/links/:id" do |env|
|
||||
Controllers::Link::Update.new.call(env)
|
||||
end
|
||||
|
||||
delete "/api/links/:id" do |env|
|
||||
Controllers::Link::Delete.new.call(env)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
meta {
|
||||
name: Delete Link
|
||||
type: http
|
||||
seq: 7
|
||||
}
|
||||
|
||||
delete {
|
||||
url: {{baseUrl}}/api/links/ad9fb116-9e5b-45b7-b272-5285b579d2e4
|
||||
body: none
|
||||
auth: none
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@ meta {
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{baseUrl}}/api/links/0da9dc9d-c56c-4d4e-942e-1ebf05ed7090
|
||||
url: {{baseUrl}}/api/links/d017c966-c28b-4c4c-a83e-97bce81ee6e7
|
||||
body: none
|
||||
auth: none
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ require "./app/routes"
|
||||
|
||||
error 500 { |env| { "error" => "Internal Server Error" }.to_json }
|
||||
error 401 { |env| { "error" => "Unauthorized" }.to_json }
|
||||
error 403 { |env| { "error" => "Forbidden" }.to_json }
|
||||
error 404 { |env| { "error" => "Not Found" }.to_json }
|
||||
|
||||
Kemal.run
|
||||
|
||||
Reference in New Issue
Block a user