Refactor to support multiple server instances in shared library

This commit is contained in:
Pijus Kamandulis
2024-12-18 19:39:57 +02:00
parent 84c33e3c8e
commit 777034181f
34 changed files with 503 additions and 369 deletions

View File

@@ -5,16 +5,15 @@ import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/pikami/cosmium/internal/repositories"
repositorymodels "github.com/pikami/cosmium/internal/repository_models"
)
func GetAllCollections(c *gin.Context) {
func (h *Handlers) GetAllCollections(c *gin.Context) {
databaseId := c.Param("databaseId")
collections, status := repositories.GetAllCollections(databaseId)
collections, status := h.repository.GetAllCollections(databaseId)
if status == repositorymodels.StatusOk {
database, _ := repositories.GetDatabase(databaseId)
database, _ := h.repository.GetDatabase(databaseId)
c.Header("x-ms-item-count", fmt.Sprintf("%d", len(collections)))
c.IndentedJSON(http.StatusOK, gin.H{
@@ -28,11 +27,11 @@ func GetAllCollections(c *gin.Context) {
c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"})
}
func GetCollection(c *gin.Context) {
func (h *Handlers) GetCollection(c *gin.Context) {
databaseId := c.Param("databaseId")
id := c.Param("collId")
collection, status := repositories.GetCollection(databaseId, id)
collection, status := h.repository.GetCollection(databaseId, id)
if status == repositorymodels.StatusOk {
c.IndentedJSON(http.StatusOK, collection)
return
@@ -46,11 +45,11 @@ func GetCollection(c *gin.Context) {
c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"})
}
func DeleteCollection(c *gin.Context) {
func (h *Handlers) DeleteCollection(c *gin.Context) {
databaseId := c.Param("databaseId")
id := c.Param("collId")
status := repositories.DeleteCollection(databaseId, id)
status := h.repository.DeleteCollection(databaseId, id)
if status == repositorymodels.StatusOk {
c.Status(http.StatusNoContent)
return
@@ -64,7 +63,7 @@ func DeleteCollection(c *gin.Context) {
c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"})
}
func CreateCollection(c *gin.Context) {
func (h *Handlers) CreateCollection(c *gin.Context) {
databaseId := c.Param("databaseId")
var newCollection repositorymodels.Collection
@@ -78,7 +77,7 @@ func CreateCollection(c *gin.Context) {
return
}
createdCollection, status := repositories.CreateCollection(databaseId, newCollection)
createdCollection, status := h.repository.CreateCollection(databaseId, newCollection)
if status == repositorymodels.Conflict {
c.IndentedJSON(http.StatusConflict, gin.H{"message": "Conflict"})
return

View File

@@ -4,11 +4,10 @@ import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/pikami/cosmium/internal/repositories"
)
func CosmiumExport(c *gin.Context) {
repositoryState, err := repositories.GetState()
func (h *Handlers) CosmiumExport(c *gin.Context) {
repositoryState, err := h.repository.GetState()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return

View File

@@ -5,12 +5,11 @@ import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/pikami/cosmium/internal/repositories"
repositorymodels "github.com/pikami/cosmium/internal/repository_models"
)
func GetAllDatabases(c *gin.Context) {
databases, status := repositories.GetAllDatabases()
func (h *Handlers) GetAllDatabases(c *gin.Context) {
databases, status := h.repository.GetAllDatabases()
if status == repositorymodels.StatusOk {
c.Header("x-ms-item-count", fmt.Sprintf("%d", len(databases)))
c.IndentedJSON(http.StatusOK, gin.H{
@@ -24,10 +23,10 @@ func GetAllDatabases(c *gin.Context) {
c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"})
}
func GetDatabase(c *gin.Context) {
func (h *Handlers) GetDatabase(c *gin.Context) {
id := c.Param("databaseId")
database, status := repositories.GetDatabase(id)
database, status := h.repository.GetDatabase(id)
if status == repositorymodels.StatusOk {
c.IndentedJSON(http.StatusOK, database)
return
@@ -41,10 +40,10 @@ func GetDatabase(c *gin.Context) {
c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"})
}
func DeleteDatabase(c *gin.Context) {
func (h *Handlers) DeleteDatabase(c *gin.Context) {
id := c.Param("databaseId")
status := repositories.DeleteDatabase(id)
status := h.repository.DeleteDatabase(id)
if status == repositorymodels.StatusOk {
c.Status(http.StatusNoContent)
return
@@ -58,7 +57,7 @@ func DeleteDatabase(c *gin.Context) {
c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"})
}
func CreateDatabase(c *gin.Context) {
func (h *Handlers) CreateDatabase(c *gin.Context) {
var newDatabase repositorymodels.Database
if err := c.BindJSON(&newDatabase); err != nil {
@@ -71,7 +70,7 @@ func CreateDatabase(c *gin.Context) {
return
}
createdDatabase, status := repositories.CreateDatabase(newDatabase)
createdDatabase, status := h.repository.CreateDatabase(newDatabase)
if status == repositorymodels.Conflict {
c.IndentedJSON(http.StatusConflict, gin.H{"message": "Conflict"})
return

View File

@@ -10,17 +10,16 @@ import (
"github.com/gin-gonic/gin"
"github.com/pikami/cosmium/internal/constants"
"github.com/pikami/cosmium/internal/logger"
"github.com/pikami/cosmium/internal/repositories"
repositorymodels "github.com/pikami/cosmium/internal/repository_models"
)
func GetAllDocuments(c *gin.Context) {
func (h *Handlers) GetAllDocuments(c *gin.Context) {
databaseId := c.Param("databaseId")
collectionId := c.Param("collId")
documents, status := repositories.GetAllDocuments(databaseId, collectionId)
documents, status := h.repository.GetAllDocuments(databaseId, collectionId)
if status == repositorymodels.StatusOk {
collection, _ := repositories.GetCollection(databaseId, collectionId)
collection, _ := h.repository.GetCollection(databaseId, collectionId)
c.Header("x-ms-item-count", fmt.Sprintf("%d", len(documents)))
c.IndentedJSON(http.StatusOK, gin.H{
@@ -34,12 +33,12 @@ func GetAllDocuments(c *gin.Context) {
c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"})
}
func GetDocument(c *gin.Context) {
func (h *Handlers) GetDocument(c *gin.Context) {
databaseId := c.Param("databaseId")
collectionId := c.Param("collId")
documentId := c.Param("docId")
document, status := repositories.GetDocument(databaseId, collectionId, documentId)
document, status := h.repository.GetDocument(databaseId, collectionId, documentId)
if status == repositorymodels.StatusOk {
c.IndentedJSON(http.StatusOK, document)
return
@@ -53,12 +52,12 @@ func GetDocument(c *gin.Context) {
c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"})
}
func DeleteDocument(c *gin.Context) {
func (h *Handlers) DeleteDocument(c *gin.Context) {
databaseId := c.Param("databaseId")
collectionId := c.Param("collId")
documentId := c.Param("docId")
status := repositories.DeleteDocument(databaseId, collectionId, documentId)
status := h.repository.DeleteDocument(databaseId, collectionId, documentId)
if status == repositorymodels.StatusOk {
c.Status(http.StatusNoContent)
return
@@ -73,7 +72,7 @@ func DeleteDocument(c *gin.Context) {
}
// TODO: Maybe move "replace" logic to repository
func ReplaceDocument(c *gin.Context) {
func (h *Handlers) ReplaceDocument(c *gin.Context) {
databaseId := c.Param("databaseId")
collectionId := c.Param("collId")
documentId := c.Param("docId")
@@ -84,13 +83,13 @@ func ReplaceDocument(c *gin.Context) {
return
}
status := repositories.DeleteDocument(databaseId, collectionId, documentId)
status := h.repository.DeleteDocument(databaseId, collectionId, documentId)
if status == repositorymodels.StatusNotFound {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"})
return
}
createdDocument, status := repositories.CreateDocument(databaseId, collectionId, requestBody)
createdDocument, status := h.repository.CreateDocument(databaseId, collectionId, requestBody)
if status == repositorymodels.Conflict {
c.IndentedJSON(http.StatusConflict, gin.H{"message": "Conflict"})
return
@@ -104,12 +103,12 @@ func ReplaceDocument(c *gin.Context) {
c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"})
}
func PatchDocument(c *gin.Context) {
func (h *Handlers) PatchDocument(c *gin.Context) {
databaseId := c.Param("databaseId")
collectionId := c.Param("collId")
documentId := c.Param("docId")
document, status := repositories.GetDocument(databaseId, collectionId, documentId)
document, status := h.repository.GetDocument(databaseId, collectionId, documentId)
if status == repositorymodels.StatusNotFound {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"})
return
@@ -160,13 +159,13 @@ func PatchDocument(c *gin.Context) {
return
}
status = repositories.DeleteDocument(databaseId, collectionId, documentId)
status = h.repository.DeleteDocument(databaseId, collectionId, documentId)
if status == repositorymodels.StatusNotFound {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"})
return
}
createdDocument, status := repositories.CreateDocument(databaseId, collectionId, modifiedDocument)
createdDocument, status := h.repository.CreateDocument(databaseId, collectionId, modifiedDocument)
if status == repositorymodels.Conflict {
c.IndentedJSON(http.StatusConflict, gin.H{"message": "Conflict"})
return
@@ -180,7 +179,7 @@ func PatchDocument(c *gin.Context) {
c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"})
}
func DocumentsPost(c *gin.Context) {
func (h *Handlers) DocumentsPost(c *gin.Context) {
databaseId := c.Param("databaseId")
collectionId := c.Param("collId")
@@ -202,14 +201,14 @@ func DocumentsPost(c *gin.Context) {
queryParameters = parametersToMap(paramsArray)
}
docs, status := repositories.ExecuteQueryDocuments(databaseId, collectionId, query.(string), queryParameters)
docs, status := h.repository.ExecuteQueryDocuments(databaseId, collectionId, query.(string), queryParameters)
if status != repositorymodels.StatusOk {
// TODO: Currently we return everything if the query fails
GetAllDocuments(c)
h.GetAllDocuments(c)
return
}
collection, _ := repositories.GetCollection(databaseId, collectionId)
collection, _ := h.repository.GetCollection(databaseId, collectionId)
c.Header("x-ms-item-count", fmt.Sprintf("%d", len(docs)))
c.IndentedJSON(http.StatusOK, gin.H{
"_rid": collection.ResourceID,
@@ -226,10 +225,10 @@ func DocumentsPost(c *gin.Context) {
isUpsert, _ := strconv.ParseBool(c.GetHeader("x-ms-documentdb-is-upsert"))
if isUpsert {
repositories.DeleteDocument(databaseId, collectionId, requestBody["id"].(string))
h.repository.DeleteDocument(databaseId, collectionId, requestBody["id"].(string))
}
createdDocument, status := repositories.CreateDocument(databaseId, collectionId, requestBody)
createdDocument, status := h.repository.CreateDocument(databaseId, collectionId, requestBody)
if status == repositorymodels.Conflict {
c.IndentedJSON(http.StatusConflict, gin.H{"message": "Conflict"})
return

View File

@@ -4,15 +4,14 @@ import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/pikami/cosmium/api/config"
)
func RegisterExplorerHandlers(router *gin.Engine) {
explorer := router.Group(config.Config.ExplorerBaseUrlLocation)
func (h *Handlers) RegisterExplorerHandlers(router *gin.Engine) {
explorer := router.Group(h.config.ExplorerBaseUrlLocation)
{
explorer.Use(func(ctx *gin.Context) {
if ctx.Param("filepath") == "/config.json" {
endpoint := fmt.Sprintf("https://%s:%d", config.Config.Host, config.Config.Port)
endpoint := fmt.Sprintf("https://%s:%d", h.config.Host, h.config.Port)
ctx.JSON(200, gin.H{
"BACKEND_ENDPOINT": endpoint,
"MONGO_BACKEND_ENDPOINT": endpoint,
@@ -25,8 +24,8 @@ func RegisterExplorerHandlers(router *gin.Engine) {
}
})
if config.Config.ExplorerPath != "" {
explorer.Static("/", config.Config.ExplorerPath)
if h.config.ExplorerPath != "" {
explorer.Static("/", h.config.ExplorerPath)
}
}
}

18
api/handlers/handlers.go Normal file
View File

@@ -0,0 +1,18 @@
package handlers
import (
"github.com/pikami/cosmium/api/config"
"github.com/pikami/cosmium/internal/repositories"
)
type Handlers struct {
repository *repositories.DataRepository
config config.ServerConfig
}
func NewHandlers(dataRepository *repositories.DataRepository, config config.ServerConfig) *Handlers {
return &Handlers{
repository: dataRepository,
config: config,
}
}

View File

@@ -10,11 +10,11 @@ import (
"github.com/pikami/cosmium/internal/logger"
)
func Authentication() gin.HandlerFunc {
func Authentication(config config.ServerConfig) gin.HandlerFunc {
return func(c *gin.Context) {
requestUrl := c.Request.URL.String()
if config.Config.DisableAuth ||
strings.HasPrefix(requestUrl, config.Config.ExplorerBaseUrlLocation) ||
if config.DisableAuth ||
strings.HasPrefix(requestUrl, config.ExplorerBaseUrlLocation) ||
strings.HasPrefix(requestUrl, "/cosmium") {
return
}
@@ -25,7 +25,7 @@ func Authentication() gin.HandlerFunc {
authHeader := c.Request.Header.Get("authorization")
date := c.Request.Header.Get("x-ms-date")
expectedSignature := authentication.GenerateSignature(
c.Request.Method, resourceType, resourceId, date, config.Config.AccountKey)
c.Request.Method, resourceType, resourceId, date, config.AccountKey)
decoded, _ := url.QueryUnescape(authHeader)
params, _ := url.ParseQuery(decoded)

View File

@@ -7,10 +7,10 @@ import (
"github.com/pikami/cosmium/api/config"
)
func StripTrailingSlashes(r *gin.Engine) gin.HandlerFunc {
func StripTrailingSlashes(r *gin.Engine, config config.ServerConfig) gin.HandlerFunc {
return func(c *gin.Context) {
path := c.Request.URL.Path
if len(path) > 1 && path[len(path)-1] == '/' && !strings.Contains(path, config.Config.ExplorerBaseUrlLocation) {
if len(path) > 1 && path[len(path)-1] == '/' && !strings.Contains(path, config.ExplorerBaseUrlLocation) {
c.Request.URL.Path = path[:len(path)-1]
r.HandleContext(c)
c.Abort()

View File

@@ -5,11 +5,10 @@ import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/pikami/cosmium/internal/repositories"
repositorymodels "github.com/pikami/cosmium/internal/repository_models"
)
func GetPartitionKeyRanges(c *gin.Context) {
func (h *Handlers) GetPartitionKeyRanges(c *gin.Context) {
databaseId := c.Param("databaseId")
collectionId := c.Param("collId")
@@ -18,7 +17,7 @@ func GetPartitionKeyRanges(c *gin.Context) {
return
}
partitionKeyRanges, status := repositories.GetPartitionKeyRanges(databaseId, collectionId)
partitionKeyRanges, status := h.repository.GetPartitionKeyRanges(databaseId, collectionId)
if status == repositorymodels.StatusOk {
c.Header("etag", "\"420\"")
c.Header("lsn", "420")
@@ -27,7 +26,7 @@ func GetPartitionKeyRanges(c *gin.Context) {
c.Header("x-ms-item-count", fmt.Sprintf("%d", len(partitionKeyRanges)))
collectionRid := collectionId
collection, _ := repositories.GetCollection(databaseId, collectionId)
collection, _ := h.repository.GetCollection(databaseId, collectionId)
if collection.ResourceID != "" {
collectionRid = collection.ResourceID
}

View File

@@ -5,27 +5,26 @@ import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/pikami/cosmium/api/config"
)
func GetServerInfo(c *gin.Context) {
func (h *Handlers) GetServerInfo(c *gin.Context) {
c.IndentedJSON(http.StatusOK, gin.H{
"_self": "",
"id": config.Config.DatabaseAccount,
"_rid": fmt.Sprintf("%s.%s", config.Config.DatabaseAccount, config.Config.DatabaseDomain),
"id": h.config.DatabaseAccount,
"_rid": fmt.Sprintf("%s.%s", h.config.DatabaseAccount, h.config.DatabaseDomain),
"media": "//media/",
"addresses": "//addresses/",
"_dbs": "//dbs/",
"writableLocations": []map[string]interface{}{
{
"name": "South Central US",
"databaseAccountEndpoint": config.Config.DatabaseEndpoint,
"databaseAccountEndpoint": h.config.DatabaseEndpoint,
},
},
"readableLocations": []map[string]interface{}{
{
"name": "South Central US",
"databaseAccountEndpoint": config.Config.DatabaseEndpoint,
"databaseAccountEndpoint": h.config.DatabaseEndpoint,
},
},
"enableMultipleWriteLocations": false,

View File

@@ -5,15 +5,14 @@ import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/pikami/cosmium/internal/repositories"
repositorymodels "github.com/pikami/cosmium/internal/repository_models"
)
func GetAllStoredProcedures(c *gin.Context) {
func (h *Handlers) GetAllStoredProcedures(c *gin.Context) {
databaseId := c.Param("databaseId")
collectionId := c.Param("collId")
sps, status := repositories.GetAllStoredProcedures(databaseId, collectionId)
sps, status := h.repository.GetAllStoredProcedures(databaseId, collectionId)
if status == repositorymodels.StatusOk {
c.Header("x-ms-item-count", fmt.Sprintf("%d", len(sps)))

View File

@@ -5,15 +5,14 @@ import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/pikami/cosmium/internal/repositories"
repositorymodels "github.com/pikami/cosmium/internal/repository_models"
)
func GetAllTriggers(c *gin.Context) {
func (h *Handlers) GetAllTriggers(c *gin.Context) {
databaseId := c.Param("databaseId")
collectionId := c.Param("collId")
triggers, status := repositories.GetAllTriggers(databaseId, collectionId)
triggers, status := h.repository.GetAllTriggers(databaseId, collectionId)
if status == repositorymodels.StatusOk {
c.Header("x-ms-item-count", fmt.Sprintf("%d", len(triggers)))

View File

@@ -5,15 +5,14 @@ import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/pikami/cosmium/internal/repositories"
repositorymodels "github.com/pikami/cosmium/internal/repository_models"
)
func GetAllUserDefinedFunctions(c *gin.Context) {
func (h *Handlers) GetAllUserDefinedFunctions(c *gin.Context) {
databaseId := c.Param("databaseId")
collectionId := c.Param("collId")
udfs, status := repositories.GetAllUserDefinedFunctions(databaseId, collectionId)
udfs, status := h.repository.GetAllUserDefinedFunctions(databaseId, collectionId)
if status == repositorymodels.StatusOk {
c.Header("x-ms-item-count", fmt.Sprintf("%d", len(udfs)))