Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add endpoint for getting data about a given document #81

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ require (
github.com/gin-gonic/contrib v0.0.0-20221130124618-7e01895a63f2
github.com/gin-gonic/gin v1.9.1
github.com/glebarez/sqlite v1.10.0
github.com/google/uuid v1.5.0
github.com/joho/godotenv v1.5.1
github.com/stretchr/testify v1.8.4
gorm.io/gorm v1.25.5
)

require (
github.com/bytedance/sonic v1.10.2 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
Expand All @@ -24,7 +27,6 @@ require (
github.com/go-playground/validator/v10 v10.16.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
Expand All @@ -35,6 +37,7 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
Expand Down
45 changes: 45 additions & 0 deletions handlers/docs/handleDocMetadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package handlers

import (
"errors"
"log"
"net/http"
"os"
"path/filepath"

"github.com/kevinanielsen/go-fast-cdn/util"

"github.com/gin-gonic/gin"
)

func HandleDocMetadata(c *gin.Context) {
fileName := c.Param("filename")
if fileName == "" {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Doc name is required",
})
return
}

filePath := filepath.Join(util.ExPath, "uploads", "docs", fileName)
stat, err := os.Stat(filePath)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
c.JSON(http.StatusNotFound, gin.H{
"error": "Doc does not exist",
})
} else {
log.Printf("Failed to get document %s: %s\n", fileName, err.Error())
c.JSON(http.StatusInternalServerError, gin.H{
"error": "Internal error",
})
}
return
}

c.JSON(http.StatusOK, gin.H{
"filename": fileName,
"download_url": c.Request.Host + "/api/cdn/download/docs/" + fileName,
"file_size": stat.Size(),
})
}
92 changes: 92 additions & 0 deletions handlers/docs/handleDocMetadata_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package handlers

import (
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"

"github.com/kevinanielsen/go-fast-cdn/util"

"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
)

func TestHandleDocMetadata_NoError(t *testing.T) {
// Arrange
testFileName := uuid.NewString()
testFileDir := filepath.Join(util.ExPath, "uploads", "docs")
defer os.RemoveAll(filepath.Join(util.ExPath, "uploads"))
err := os.MkdirAll(testFileDir, 0766)
require.NoError(t, err)
testFilePath := filepath.Join(testFileDir, testFileName)
testFileContents := uuid.NewString()
err = os.WriteFile(testFilePath, []byte(testFileContents), 0666)
require.NoError(t, err)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request = httptest.NewRequest(http.MethodGet, "/test", nil)
c.Params = []gin.Param{{
Key: "filename",
Value: testFileName,
}}

// Act
HandleDocMetadata(c)

// Assert
require.Equal(t, http.StatusOK, w.Result().StatusCode)
result := map[string]interface{}{}
err = json.NewDecoder(w.Body).Decode(&result)
require.NoError(t, err)
require.Contains(t, result, "filename")
require.Equal(t, result["filename"], testFileName)
require.Contains(t, result, "download_url")
require.NotEmpty(t, result["download_url"])
require.Contains(t, result, "file_size")
require.Equal(t, float64(len(testFileContents)), result["file_size"])
}

func TestHandleDocMetadata_NotFound(t *testing.T) {
// Arrange
testFileName := uuid.NewString()
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request = httptest.NewRequest(http.MethodGet, "/test", nil)
c.Params = []gin.Param{{
Key: "filename",
Value: testFileName,
}}

// Act
HandleDocMetadata(c)

// Assert
require.Equal(t, http.StatusNotFound, w.Result().StatusCode)
result := map[string]interface{}{}
err := json.NewDecoder(w.Body).Decode(&result)
require.NoError(t, err)
require.Contains(t, result, "error")
require.Equal(t, result["error"], "Doc does not exist")
}

func TestHandleDocMetadata_NameNotProvided(t *testing.T) {
// Arrange
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request = httptest.NewRequest(http.MethodGet, "/test", nil)

// Act
HandleDocMetadata(c)

// Assert
require.Equal(t, http.StatusBadRequest, w.Result().StatusCode)
result := map[string]interface{}{}
err := json.NewDecoder(w.Body).Decode(&result)
require.NoError(t, err)
require.Contains(t, result, "error")
require.Equal(t, result["error"], "Doc name is required")
}
1 change: 1 addition & 0 deletions router/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func AddApiRoutes(r *gin.Engine) {
{
cdn.GET("/size", handlers.GetSizeHandler)
cdn.GET("/doc/all", dHandlers.HandleAllDocs)
cdn.GET("/doc/:filename", dHandlers.HandleDocMetadata)
cdn.GET("/image/all", iHandlers.HandleAllImages)
cdn.POST("/drop/database", dbHandlers.HandleDropDB)
cdn.Static("/download/images", util.ExPath+"/uploads/images")
Expand Down
61 changes: 61 additions & 0 deletions static/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,67 @@
}
}
},
"/api/cdn/doc/{fileName}": {
"get": {
"description": "Retrieves metadata about the document",
"responses": {
"400": {
"description": "Document filename was not provided",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": { "type": "string" }
}
}
}
}
},
"404": {
"description": "Document was not found",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": { "type": "string" }
}
}
}
}
},
"500": {
"description": "Unknown error while trying to retrieve the document",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": { "type": "string" }
}
}
}
}
},
"200": {
"description": "Metadata about the document",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"filename": { "type": "string" },
"download_url":{ "type": "string" },
"file_size": { "type": "number" }
}
}
}
}
}
}
}
},
"/api/cdn/image/all": {
"get": {
"summary": "Get all images",
Expand Down