feat: create links - route + controller + model + migration

This commit is contained in:
Juan Rodriguez
2024-05-12 22:50:47 +02:00
parent 3e86e761b6
commit c806953b9a
13 changed files with 132 additions and 11 deletions
+1
View File
@@ -4,3 +4,4 @@
/.shards/
*.dwarf
*.db
.env.*
@@ -0,0 +1,14 @@
-- +micrate Up
-- SQL in section 'Up' is executed when this migration is applied
CREATE TABLE links (
id INTEGER PRIMARY KEY AUTOINCREMENT,
slug VARCHAR(100) UNIQUE NOT NULL,
url TEXT NOT NULL,
click_counter INTEGER DEFAULT 0,
created_at INTEGER DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at INTEGER DEFAULT CURRENT_TIMESTAMP NOT NULL
);
-- +micrate Down
-- SQL section 'Down' is executed when this migration is rolled back
DROP TABLE links;
+2 -2
View File
@@ -1,4 +1,4 @@
name: pa
name: url-shortener
version: 0.1.0
authors:
@@ -6,7 +6,7 @@ authors:
targets:
pa:
main: src/pa.cr
main: src/url-shortener.cr
dependencies:
kemal:
+25
View File
@@ -0,0 +1,25 @@
require "../lib/controller.cr"
module App::Controllers::Link
class Create < App::Lib::BaseController
include App::Models
include App::Lib
def call(env)
params = env.params.json["link"].as(Hash)
link = Link.new
link.url = params["url"].as_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.to_json)
end
App::Serializers::Link.new(link).to_json
end
end
end
+2 -2
View File
@@ -1,7 +1,7 @@
require "../lib/controller.cr"
module Pa::Controllers::Ping
class Get < Pa::Lib::BaseController
module App::Controllers::Ping
class Get < App::Lib::BaseController
def call(env)
response = {"pong" => "ok"}
response.to_json
+8 -1
View File
@@ -1,4 +1,11 @@
module Pa::Lib
module App::Lib
abstract class BaseController
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
end
end
+2 -3
View File
@@ -1,7 +1,7 @@
require "sqlite3"
require "crecto"
module Pa::Lib
module App::Lib
class Database
extend Crecto::Repo
@@ -9,8 +9,7 @@ module Pa::Lib
Multi = Crecto::Repo::Multi
config do |conf|
conf.adapter = Crecto::Adapters::SQLite3
conf.database = ENV["DATABASE_URL"]
conf.uri = ENV["DATABASE_URL"]
end
end
end
+33
View File
@@ -0,0 +1,33 @@
require "kemal"
module App
class UnauthorizedException < Kemal::Exceptions::CustomException
def initialize(context)
context.response.status_code = 401
super(context)
end
end
class ForbiddenException < Kemal::Exceptions::CustomException
def initialize(context)
context.response.status_code = 403
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, @content = "")
context.response.status_code = 422
super(context)
end
getter :content
end
end
+14
View File
@@ -0,0 +1,14 @@
require "sqlite3"
require "crecto"
module App::Models
class Link < Crecto::Model
schema :links do
field :slug, String
field :url, String
field :click_counter, Int64
end
validate_required [:slug, :url]
end
end
+9 -1
View File
@@ -1,7 +1,15 @@
require "./controllers/**"
module Pa
module App
before_all do |env|
env.response.content_type = "application/json"
end
get "/api/ping" do |env|
Controllers::Ping::Get.new.call(env)
end
post "/api/links" do |env|
Controllers::Link::Create.new.call(env)
end
end
+17
View File
@@ -0,0 +1,17 @@
require "json"
require "../models/link"
module App::Serializers
class Link
def initialize(@link : App::Models::Link)
end
def to_json(builder : JSON::Builder)
builder.object do
builder.field("origin", @link.url)
builder.field("link", "#{ENV["APP_URL"]}/#{@link.slug}")
end
end
end
end
+4 -1
View File
@@ -1,7 +1,10 @@
require "kemal"
require "./config/*"
require "./services/*"
require "./lib/*"
require "./models/*"
require "./serializers/*"
require "./routes"
error 500 { |env| {"status" => 500, "error" => "Internal Server Error"}.to_json }
+1 -1
View File
@@ -1,3 +1,3 @@
module Pa
module App
VERSION = "0.1.0"
end