Skip to content

Commit

Permalink
Added documentation
Browse files Browse the repository at this point in the history
Added nginx CORS
Removed pgdb from handlers
  • Loading branch information
Dmytro-Hladkykh committed Jul 19, 2024
1 parent 2900451 commit d175042
Show file tree
Hide file tree
Showing 19 changed files with 5,227 additions and 3,810 deletions.
8,366 changes: 4,650 additions & 3,716 deletions docs/package-lock.json

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions docs/spec/components/schemas/CreateShortLinkRequest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
type: object
required:
- original_url
properties:
original_url:
type: string
example: "https://example.com/very/long/url"
5 changes: 5 additions & 0 deletions docs/spec/components/schemas/CreateShortLinkResponse.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type: object
properties:
short_code:
type: string
example: "abc123"
11 changes: 11 additions & 0 deletions docs/spec/components/schemas/Error404.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
type: object
properties:
error:
type: object
properties:
code:
type: string
example: "404"
message:
type: string
example: "NOT_FOUND"
11 changes: 11 additions & 0 deletions docs/spec/components/schemas/Error500.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
type: object
properties:
error:
type: object
properties:
code:
type: string
example: "500"
message:
type: string
example: "INTERNAL_SERVER_ERROR"
5 changes: 5 additions & 0 deletions docs/spec/components/schemas/GetOriginalURLResponse.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type: object
properties:
original_url:
type: string
example: "https://example.com/very/long/url"
18 changes: 18 additions & 0 deletions docs/spec/components/schemas/ShortLink.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
type: object
properties:
id:
type: integer
format: int64
example: 1
original_url:
type: string
example: "https://example.com/very/long/url"
short_code:
type: string
example: "abc123"
created_at:
type: string
format: date-time
clicks:
type: integer
example: 0
4 changes: 2 additions & 2 deletions docs/spec/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ openapi: 3.0.0
info:
version: 1.0.0
title: link-shortener-svc
description: ''
description: "This service provides URL shortening functionality. It allows users to create short links from long URLs, redirect users to original URLs when they use shortened links."
servers:
- url: 'https://api.demo.tokend.io'
- url: "https://api.demo.tokend.io"
description: TokenD Developer Environment
31 changes: 31 additions & 0 deletions docs/spec/paths/link-shortener-original.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
get:
summary: Get original URL
tags:
- ShortLinks
parameters:
- in: path
name: shortCode
required: true
schema:
type: string
example: "abc123"
description: Short code of the URL to retrieve the original URL.
responses:
"200":
description: Successful operation
content:
application/json:
schema:
$ref: "#/components/schemas/GetOriginalURLResponse"
"404":
description: Short link not found
content:
application/json:
schema:
$ref: "#/components/schemas/Error404"
"500":
description: Internal server error
content:
application/json:
schema:
$ref: "#/components/schemas/Error500"
29 changes: 29 additions & 0 deletions docs/spec/paths/linkl-shortener-shorten.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
post:
summary: Create a short link
tags:
- ShortLinks
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateShortLinkRequest"
responses:
"200":
description: Successful operation
content:
application/json:
schema:
$ref: "#/components/schemas/CreateShortLinkResponse"
"404":
description: Bad request
content:
application/json:
schema:
$ref: "#/components/schemas/Error404"
"500":
description: Internal server error
content:
application/json:
schema:
$ref: "#/components/schemas/Error500"
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ require (
github.com/Masterminds/squirrel v1.5.4
github.com/alecthomas/kingpin v2.2.6+incompatible
github.com/go-chi/chi v4.1.2+incompatible
github.com/jmoiron/sqlx v1.4.0
github.com/pkg/errors v0.9.1
github.com/rubenv/sql-migrate v1.7.0
gitlab.com/distributed_lab/ape v1.7.1
Expand All @@ -33,6 +32,7 @@ require (
github.com/google/jsonapi v0.0.0-20200226002910-c8283f632fb7 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jmoiron/sqlx v1.3.5 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/lib/pq v1.10.9 // indirect
Expand Down
12 changes: 4 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1138,8 +1138,6 @@ cloud.google.com/go/workflows v1.12.2/go.mod h1:+OmBIgNqYJPVggnMo9nqmizW0qEXHhmn
cloud.google.com/go/workflows v1.12.3/go.mod h1:fmOUeeqEwPzIU81foMjTRQIdwQHADi/vEr1cx9R1m5g=
collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM=
Expand Down Expand Up @@ -1403,9 +1401,8 @@ github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
Expand Down Expand Up @@ -1632,9 +1629,8 @@ github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+
github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
Expand Down Expand Up @@ -1747,8 +1743,8 @@ github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsO
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI=
github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mediocregopher/radix/v3 v3.8.1/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
Expand Down
110 changes: 51 additions & 59 deletions internal/service/handlers/create_short_link.go
Original file line number Diff line number Diff line change
@@ -1,70 +1,62 @@
package handlers

import (
"database/sql"
"net/http"

"github.com/Dmytro-Hladkykh/link-shortener-svc/internal/data"
"github.com/Dmytro-Hladkykh/link-shortener-svc/internal/data/pg"
"github.com/Dmytro-Hladkykh/link-shortener-svc/internal/service/requests"
"gitlab.com/distributed_lab/ape"
"gitlab.com/distributed_lab/ape/problems"
)

func CreateShortLink(w http.ResponseWriter, r *http.Request) {
request, err := requests.NewCreateShortLinkRequest(r)
if err != nil {
Log(r).WithError(err).Error("failed to create request")
ape.RenderErr(w, problems.BadRequest(err)...)
return
}

db := DB(r)
if db == nil {
Log(r).Error("database connection is nil")
ape.RenderErr(w, problems.InternalError())
return
}

shortLinkQ := pg.NewShortLinkQ(db)
if shortLinkQ == nil {
Log(r).Error("shortLinkQ is nil")
ape.RenderErr(w, problems.InternalError())
return
}

// check for existing link
existingLink, err := shortLinkQ.FilterByOriginalURL(request.OriginalURL).Get()
if err != nil && err != sql.ErrNoRows {
Log(r).WithError(err).Error("failed to check existing link")
ape.RenderErr(w, problems.InternalError())
return
}

if existingLink != nil {
// if exist then return short code
ape.Render(w, map[string]interface{}{"short_code": existingLink.ShortCode})
return
}

// if new link then generate new short code
shortCode, err := data.GenerateShortCode()
if err != nil {
Log(r).WithError(err).Error("failed to generate short code")
ape.RenderErr(w, problems.InternalError())
return
}

// create new short link in db
newLink, err := shortLinkQ.Insert(data.ShortLink{
OriginalURL: request.OriginalURL,
ShortCode: shortCode,
})
if err != nil {
Log(r).WithError(err).Error("failed to insert new short link")
ape.RenderErr(w, problems.InternalError())
return
}

ape.Render(w, map[string]interface{}{"short_code": newLink.ShortCode})
}
type CreateShortLinkHandler struct {
repo data.ShortLinkQ
}

func NewCreateShortLinkHandler(repo data.ShortLinkQ) *CreateShortLinkHandler {
return &CreateShortLinkHandler{repo: repo}
}

func (h *CreateShortLinkHandler) CreateShortLink(w http.ResponseWriter, r *http.Request) {
request, err := requests.NewCreateShortLinkRequest(r)
if err != nil {
Log(r).WithError(err).Error("failed to create request")
ape.RenderErr(w, problems.BadRequest(err)...)
return
}

// check if link already exists
existingLink, err := h.repo.FilterByOriginalURL(request.OriginalURL).Get()
if err != nil {
Log(r).WithError(err).Error("failed to check existing link")
ape.RenderErr(w, problems.InternalError())
return
}

if existingLink != nil {
// if exist then return short code
ape.Render(w, map[string]interface{}{"short_code": existingLink.ShortCode})
return
}

// if new link then generate short code
shortCode, err := data.GenerateShortCode()
if err != nil {
Log(r).WithError(err).Error("failed to generate short code")
ape.RenderErr(w, problems.InternalError())
return
}

// create new link in db
newLink, err := h.repo.Insert(data.ShortLink{
OriginalURL: request.OriginalURL,
ShortCode: shortCode,
})
if err != nil {
Log(r).WithError(err).Error("failed to insert new short link")
ape.RenderErr(w, problems.InternalError())
return
}

ape.Render(w, map[string]interface{}{"short_code": newLink.ShortCode})
}
44 changes: 25 additions & 19 deletions internal/service/handlers/get_original_url.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,34 @@ package handlers
import (
"net/http"

"github.com/Dmytro-Hladkykh/link-shortener-svc/internal/data/pg"
"github.com/Dmytro-Hladkykh/link-shortener-svc/internal/data"
"github.com/go-chi/chi"
"gitlab.com/distributed_lab/ape"
"gitlab.com/distributed_lab/ape/problems"
)

func GetOriginalURL(w http.ResponseWriter, r *http.Request) {
shortCode := chi.URLParam(r, "shortCode")

shortLinkQ := pg.NewShortLinkQ(DB(r))

shortLink, err := shortLinkQ.FilterByShortCode(shortCode).Get()
if err != nil {
Log(r).WithError(err).Error("failed to get original URL")
ape.RenderErr(w, problems.NotFound())
return
}

if shortLink == nil {
ape.RenderErr(w, problems.NotFound())
return
}

ape.Render(w, map[string]interface{}{"original_url": shortLink.OriginalURL})
type GetOriginalURLHandler struct {
repo data.ShortLinkQ
}

func NewGetOriginalURLHandler(repo data.ShortLinkQ) *GetOriginalURLHandler {
return &GetOriginalURLHandler{repo: repo}
}

func (h *GetOriginalURLHandler) GetOriginalURL(w http.ResponseWriter, r *http.Request) {
shortCode := chi.URLParam(r, "shortCode")

shortLink, err := h.repo.FilterByShortCode(shortCode).Get()
if err != nil {
Log(r).WithError(err).Error("failed to get original URL")
ape.RenderErr(w, problems.InternalError())
return
}

if shortLink == nil {
ape.RenderErr(w, problems.NotFound())
return
}

ape.Render(w, map[string]interface{}{"original_url": shortLink.OriginalURL})
}
15 changes: 14 additions & 1 deletion internal/service/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"net/http"

"github.com/Dmytro-Hladkykh/link-shortener-svc/internal/config"
"github.com/Dmytro-Hladkykh/link-shortener-svc/internal/data/pg"
"github.com/Dmytro-Hladkykh/link-shortener-svc/internal/service/handlers"
"gitlab.com/distributed_lab/kit/copus/types"
"gitlab.com/distributed_lab/logan/v3"
"gitlab.com/distributed_lab/logan/v3/errors"
Expand All @@ -18,7 +20,18 @@ type service struct {

func (s *service) run(cfg config.Config) error {
s.log.Info("Service started")
r := s.router(cfg)

// create db connection
db := cfg.DB()

// create repo
shortLinkRepo := pg.NewShortLinkQ(db)

// create handlers
createShortLinkHandler := handlers.NewCreateShortLinkHandler(shortLinkRepo)
getOriginalURLHandler := handlers.NewGetOriginalURLHandler(shortLinkRepo)

r := s.router(cfg, createShortLinkHandler, getOriginalURLHandler)

if err := s.copus.RegisterChi(r); err != nil {
return errors.Wrap(err, "cop failed")
Expand Down
Loading

0 comments on commit d175042

Please sign in to comment.