feat: create links - route + controller + model + migration
This commit is contained in:
@@ -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;
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -1,3 +1,3 @@
|
||||
module Pa
|
||||
module App
|
||||
VERSION = "0.1.0"
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user