From 680d02ac50613a500098f4251adccc2f9db013e9 Mon Sep 17 00:00:00 2001 From: Burkay Durdu Date: Sat, 5 Mar 2022 17:15:35 +0300 Subject: [PATCH] Created swagger in project for API doc --- docs/docs.go | 287 +++++++++++++++++++++++++++++ docs/swagger.yaml | 172 +++++++++++++++++ go.mod | 27 ++- go.sum | 116 +++++++++++- internal/domain/shortly/handler.go | 83 +++++++-- internal/domain/shortly/service.go | 4 +- internal/server/server.go | 29 ++- main.go | 12 ++ 8 files changed, 705 insertions(+), 25 deletions(-) create mode 100644 docs/docs.go create mode 100644 docs/swagger.yaml diff --git a/docs/docs.go b/docs/docs.go new file mode 100644 index 0000000..be72c29 --- /dev/null +++ b/docs/docs.go @@ -0,0 +1,287 @@ +// Package docs GENERATED BY THE COMMAND ABOVE; DO NOT EDIT +// This file was generated by swaggo/swag +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "API Support", + "url": "http://www.shortly.io/support", + "email": "support@shortly.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/api/v1/create": { + "post": { + "description": "It gives short URL", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Create Short URL" + ], + "summary": "Generate short URL", + "parameters": [ + { + "description": "Original URL is required", + "name": "original_url", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/shortly.SaveRequestDTO" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/shortly.SaveResponseDTO" + } + }, + "400": { + "description": "Request body is not valid", + "schema": { + "$ref": "#/definitions/shortly.ErrResponseDTO" + } + }, + "409": { + "description": "URL is not valid", + "schema": { + "$ref": "#/definitions/shortly.ErrResponseDTO" + } + } + } + } + }, + "/api/v1/health": { + "get": { + "description": "It helps server tracking", + "produces": [ + "text/plain" + ], + "tags": [ + "Health" + ], + "summary": "Server Health", + "responses": { + "200": { + "description": "" + } + } + } + }, + "/api/v1/list": { + "get": { + "description": "It gives all shortly data", + "tags": [ + "All Shortly List" + ], + "summary": "Get All Shortly List", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/db.Shortly" + } + } + } + } + } + }, + "/{code}": { + "get": { + "description": "It redirects from short URL to original URL", + "tags": [ + "Redirect" + ], + "summary": "Short URL", + "parameters": [ + { + "type": "string", + "description": "Shortly Code", + "name": "code", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "" + }, + "404": { + "description": "Not found", + "schema": { + "type": "string" + } + } + } + }, + "put": { + "description": "It redirects from short URL to original URL", + "tags": [ + "Redirect" + ], + "summary": "Short URL", + "parameters": [ + { + "type": "string", + "description": "Shortly Code", + "name": "code", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "" + }, + "404": { + "description": "Not found", + "schema": { + "type": "string" + } + } + } + }, + "post": { + "description": "It redirects from short URL to original URL", + "tags": [ + "Redirect" + ], + "summary": "Short URL", + "parameters": [ + { + "type": "string", + "description": "Shortly Code", + "name": "code", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "" + }, + "404": { + "description": "Not found", + "schema": { + "type": "string" + } + } + } + }, + "delete": { + "description": "It redirects from short URL to original URL", + "tags": [ + "Redirect" + ], + "summary": "Short URL", + "parameters": [ + { + "type": "string", + "description": "Shortly Code", + "name": "code", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "" + }, + "404": { + "description": "Not found", + "schema": { + "type": "string" + } + } + } + } + } + }, + "definitions": { + "db.Shortly": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "original_url": { + "type": "string" + }, + "short_url": { + "type": "string" + }, + "visit_count": { + "type": "integer" + } + } + }, + "shortly.ErrResponseDTO": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + } + }, + "shortly.SaveRequestDTO": { + "type": "object", + "properties": { + "original_url": { + "type": "string" + } + } + }, + "shortly.SaveResponseDTO": { + "type": "object", + "properties": { + "short_url": { + "type": "string" + } + } + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "1.0", + Host: "", + BasePath: "", + Schemes: []string{}, + Title: "Shortly API DOC", + Description: "Shortly is URL Shortener", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/docs/swagger.yaml b/docs/swagger.yaml new file mode 100644 index 0000000..32fd262 --- /dev/null +++ b/docs/swagger.yaml @@ -0,0 +1,172 @@ +definitions: + db.Shortly: + properties: + code: + type: string + original_url: + type: string + short_url: + type: string + visit_count: + type: integer + type: object + shortly.ErrResponseDTO: + properties: + code: + type: integer + error: + type: string + message: + type: string + type: object + shortly.SaveRequestDTO: + properties: + original_url: + type: string + type: object + shortly.SaveResponseDTO: + properties: + short_url: + type: string + type: object +info: + contact: + email: support@shortly.io + name: API Support + url: http://www.shortly.io/support + description: Shortly is URL Shortener + license: + name: Apache 2.0 + url: http://www.apache.org/licenses/LICENSE-2.0.html + termsOfService: http://swagger.io/terms/ + title: Shortly API DOC + version: "1.0" +paths: + /{code}: + delete: + description: It redirects from short URL to original URL + parameters: + - description: Shortly Code + in: path + name: code + required: true + type: string + responses: + "200": + description: "" + "404": + description: Not found + schema: + type: string + summary: Short URL + tags: + - Redirect + get: + description: It redirects from short URL to original URL + parameters: + - description: Shortly Code + in: path + name: code + required: true + type: string + responses: + "200": + description: "" + "404": + description: Not found + schema: + type: string + summary: Short URL + tags: + - Redirect + post: + description: It redirects from short URL to original URL + parameters: + - description: Shortly Code + in: path + name: code + required: true + type: string + responses: + "200": + description: "" + "404": + description: Not found + schema: + type: string + summary: Short URL + tags: + - Redirect + put: + description: It redirects from short URL to original URL + parameters: + - description: Shortly Code + in: path + name: code + required: true + type: string + responses: + "200": + description: "" + "404": + description: Not found + schema: + type: string + summary: Short URL + tags: + - Redirect + /api/v1/create: + post: + consumes: + - application/json + description: It gives short URL + parameters: + - description: Original URL is required + in: body + name: original_url + required: true + schema: + $ref: '#/definitions/shortly.SaveRequestDTO' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/shortly.SaveResponseDTO' + "400": + description: Request body is not valid + schema: + $ref: '#/definitions/shortly.ErrResponseDTO' + "409": + description: URL is not valid + schema: + $ref: '#/definitions/shortly.ErrResponseDTO' + summary: Generate short URL + tags: + - Create Short URL + /api/v1/health: + get: + description: It helps server tracking + produces: + - text/plain + responses: + "200": + description: "" + summary: Server Health + tags: + - Health + /api/v1/list: + get: + description: It gives all shortly data + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/db.Shortly' + type: array + summary: Get All Shortly List + tags: + - All Shortly List +swagger: "2.0" diff --git a/go.mod b/go.mod index 404ddd4..c2cd1d1 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,28 @@ module github.com/burkaydurdu/shortly go 1.17 require ( - github.com/davecgh/go-spew v1.1.0 // indirect + github.com/stretchr/testify v1.7.0 + github.com/swaggo/http-swagger v1.2.5 + github.com/swaggo/swag v1.7.9 +) + +require ( + github.com/KyleBanks/depth v1.2.1 // indirect + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.19.6 // indirect + github.com/go-openapi/spec v0.20.4 // indirect + github.com/go-openapi/swag v0.21.1 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/objx v0.1.0 // indirect - github.com/stretchr/testify v1.7.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect + github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2 // indirect + golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect + golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect + golang.org/x/text v0.3.7 // indirect + golang.org/x/tools v0.1.9 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect ) diff --git a/go.sum b/go.sum index 7b4dc41..b4dd54b 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,121 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/agiledragon/gomonkey/v2 v2.3.1 h1:k+UnUY0EMNYUFUAQVETGY9uUTxjMdnUkP0ARyJS1zzs= +github.com/agiledragon/gomonkey/v2 v2.3.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/otiai10/copy v1.7.0 h1:hVoPiN+t+7d2nzzwMiDHPSOogsWAStewq3TwU05+clE= +github.com/otiai10/copy v1.7.0/go.mod h1:rmRl6QPdJj6EiUqXQ/4Nn2lLXoNQjFCQbbNrxgc/t3U= +github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= +github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= +github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= +github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2 h1:+iNTcqQJy0OZ5jk6a5NLib47eqXK8uYcPX+O4+cBpEM= +github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w= +github.com/swaggo/http-swagger v1.2.5 h1:iDWoHpJMLNo4nwGOPXsOoqlB9wB6M4xgjhws8x3KQcs= +github.com/swaggo/http-swagger v1.2.5/go.mod h1:CcoICgY3yVDk2u1LQUCMHbAj0fjlxIX+873psXlIKNA= +github.com/swaggo/swag v1.7.9 h1:6vCG5mm43ebDzGlZPMGYrYI4zKFfOr5kicQX8qjeDwc= +github.com/swaggo/swag v1.7.9/go.mod h1:gZ+TJ2w/Ve1RwQsA2IRoSOTidHz6DX+PIG8GWvbnoLU= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.9 h1:j9KsMiaP1c3B0OTQGth0/k+miLGTgLsAFUCrF2vLcF8= +golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/domain/shortly/handler.go b/internal/domain/shortly/handler.go index 33951fe..2cebbc0 100644 --- a/internal/domain/shortly/handler.go +++ b/internal/domain/shortly/handler.go @@ -4,6 +4,10 @@ import ( "encoding/json" "net/http" + "github.com/burkaydurdu/shortly/internal/db" + + "github.com/burkaydurdu/shortly/pkg/util" + "github.com/burkaydurdu/shortly/pkg/log" shortlyError "github.com/burkaydurdu/shortly/pkg/error" @@ -11,7 +15,7 @@ import ( type HTTPHandler interface { RedirectURL(w http.ResponseWriter, r *http.Request) - SaveShortURL(w http.ResponseWriter, r *http.Request) + CreateShortURL(w http.ResponseWriter, r *http.Request) GetShortList(w http.ResponseWriter, r *http.Request) } @@ -20,6 +24,17 @@ type shortlyHandler struct { l *log.ShortlyLog } +// RedirectURL Handler +// @Summary Short URL +// @Description It redirects from short URL to original URL +// @Tags Redirect +// @Success 200 "Redirect URL" +// @Failure 404 {string} string "Not found" +// @Param code path string true "Shortly Code" +// @Router /{code} [get] +// @Router /{code} [post] +// @Router /{code} [put] +// @Router /{code} [delete] func (s *shortlyHandler) RedirectURL(w http.ResponseWriter, r *http.Request) { // Remove first character `` / `` code := r.URL.Path[1:] @@ -35,7 +50,18 @@ func (s *shortlyHandler) RedirectURL(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, redirectURL, http.StatusSeeOther) } -func (s *shortlyHandler) SaveShortURL(w http.ResponseWriter, r *http.Request) { +// CreateShortURL +// @Summary Generate short URL +// @Description It gives short URL +// @Tags Create Short URL +// @Accept json +// @Produce json +// @Param original_url body shortly.SaveRequestDTO true "Original URL is required" +// @Success 200 {object} shortly.SaveResponseDTO +// @failure 400 {object} shortly.ErrResponseDTO object "Request body is not valid" +// @failure 409 {object} shortly.ErrResponseDTO object "URL is not valid" +// @Router /api/v1/create [post] +func (s *shortlyHandler) CreateShortURL(w http.ResponseWriter, r *http.Request) { var requestBody SaveRequestDTO defer r.Body.Close() @@ -43,36 +69,69 @@ func (s *shortlyHandler) SaveShortURL(w http.ResponseWriter, r *http.Request) { err := json.NewDecoder(r.Body).Decode(&requestBody) if err != nil { - w.WriteHeader(http.StatusBadRequest) - httpResponse(w, []byte(shortlyError.ParserError), s.l) + body := responseErrorBody(err, shortlyError.ParserError, shortlyError.ParserErrCode) + + shortlyResponse(w, body, s.l, http.StatusBadRequest) return } - responseBody := s.s.SaveShortURL(r.Host, &requestBody) + if err = util.IsURL(requestBody.OriginalURL); err != nil { + body := responseErrorBody(err, shortlyError.InvalidURLError, shortlyError.InvalidParams) - responseByteBody, _ := json.Marshal(responseBody) + shortlyResponse(w, body, s.l, http.StatusConflict) - w.Header().Set("Content-Type", "application/json") - httpResponse(w, responseByteBody, s.l) + return + } + + shortlyURL := s.s.CreateShortURL(r.Host, &requestBody) + responseBody, _ := json.Marshal(shortlyURL) + + shortlyResponse(w, responseBody, s.l, http.StatusOK) } +// GetShortList +// @Summary Get All Shortly List +// @Description It gives all shortly data +// @Tags All Shortly List +// @Success 200 {array} db.Shortly +// @Router /api/v1/list [get] func (s *shortlyHandler) GetShortList(w http.ResponseWriter, _ *http.Request) { shortlyURL := s.s.GetShortList() - responseByteBody, _ := json.Marshal(shortlyURL) + // when the list is empty it returns null therefore we should create this object. + if len(shortlyURL) <= 0 { + shortlyURL = make([]db.Shortly, 0) + } - w.Header().Set("Content-Type", "application/json") - httpResponse(w, responseByteBody, s.l) + responseBody, _ := json.Marshal(shortlyURL) + + shortlyResponse(w, responseBody, s.l, http.StatusOK) } -func httpResponse(w http.ResponseWriter, response []byte, l *log.ShortlyLog) { +func shortlyResponse(w http.ResponseWriter, response []byte, l *log.ShortlyLog, code int) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(code) + _, err := w.Write(response) + if err != nil { l.ZapFatal(shortlyError.ResponseError, err) } } +func responseErrorBody(err error, message string, code int) []byte { + errResponse := ErrResponseDTO{ + Error: err.Error(), + Message: message, + Code: code, + } + + result, _ := json.Marshal(errResponse) + + return result +} + func NewShortlyHandler(s Service, l *log.ShortlyLog) HTTPHandler { return &shortlyHandler{ s: s, diff --git a/internal/domain/shortly/service.go b/internal/domain/shortly/service.go index bbd1a03..fcdc3b5 100644 --- a/internal/domain/shortly/service.go +++ b/internal/domain/shortly/service.go @@ -10,7 +10,7 @@ import ( type Service interface { RedirectURL(code string) (string, error) - SaveShortURL(host string, requestBody *SaveRequestDTO) *SaveResponseDTO + CreateShortURL(host string, requestBody *SaveRequestDTO) *SaveResponseDTO GetShortList() []db.Shortly } @@ -31,7 +31,7 @@ func (s *shortlyService) RedirectURL(code string) (string, error) { return "", shortlyError.ErrAddressNotFound } -func (s *shortlyService) SaveShortURL(host string, requestBody *SaveRequestDTO) *SaveResponseDTO { +func (s *shortlyService) CreateShortURL(host string, requestBody *SaveRequestDTO) *SaveResponseDTO { code := generateShortlyCode(s.db.ShortURL, s.lengthOfCode) shortly := db.Shortly{ diff --git a/internal/server/server.go b/internal/server/server.go index 7c5b723..33e8492 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -15,6 +15,7 @@ import ( "github.com/burkaydurdu/shortly/config" shortlyError "github.com/burkaydurdu/shortly/pkg/error" + httpSwagger "github.com/swaggo/http-swagger" ) type route struct { @@ -96,26 +97,39 @@ func (s *Server) Start() error { shortlyService := shortly.NewShortlyService(db, s.config.LengthOfCode) shortlyHandler := shortly.NewShortlyHandler(shortlyService, s.l) + // For Swagger API Documentation + // We want to serve our api docs + fs := http.FileServer(http.Dir("./docs")) + s.s.Handler(regexp.MustCompile("/docs/"), http.StripPrefix("/docs/", fs)) + s.s.Handler( - regexp.MustCompile("/api/health"), + regexp.MustCompile("/api/v1/health"), HTTPLogMiddleware(s.s.l, http.HandlerFunc(s.healthCheck)), ) s.s.Handler( - regexp.MustCompile("/api/save"), - HTTPLogMiddleware(s.s.l, http.HandlerFunc(shortlyHandler.SaveShortURL)), + regexp.MustCompile("/api/v1/create"), + HTTPLogMiddleware(s.s.l, http.HandlerFunc(shortlyHandler.CreateShortURL)), ) s.s.Handler( - regexp.MustCompile("api/list"), + regexp.MustCompile("api/v1/list"), HTTPLogMiddleware(s.s.l, http.HandlerFunc(shortlyHandler.GetShortList)), ) + // Short URL End-point. s.s.Handler( regexp.MustCompile("^/[^/]*$"), HTTPLogMiddleware(s.s.l, http.HandlerFunc(shortlyHandler.RedirectURL)), ) + // Serve Swagger UI + s.s.Handler( + regexp.MustCompile("/swagger/"), + httpSwagger.Handler( + httpSwagger.URL("/docs/swagger.json"), + httpSwagger.DomID("#swagger-ui"))) + err = http.ListenAndServe( fmt.Sprintf(":%d", s.config.Server.Port), s.s, @@ -124,6 +138,13 @@ func (s *Server) Start() error { return err } +// Health Handler +// @Summary Server Health +// @Description It helps server tracking +// @Tags Health +// @Produce text/plain +// @Success 200 +// @Router /api/v1/health [get] func (s *Server) healthCheck(w http.ResponseWriter, r *http.Request) { _, err := w.Write([]byte("OK")) diff --git a/main.go b/main.go index f6bf4b4..0c62145 100644 --- a/main.go +++ b/main.go @@ -1,3 +1,15 @@ +// @title Shortly API DOC +// @version 1.0 +// @description Shortly is URL Shortener +// @termsOfService http://swagger.io/terms/ + +// @contact.name API Support +// @contact.url http://www.shortly.io/support +// @contact.email support@shortly.io + +// @license.name Apache 2.0 +// @license.url http://www.apache.org/licenses/LICENSE-2.0.html + package main import (