DataStore is interface now. Liskov would be proud.

This commit is contained in:
Pijus Kamandulis
2025-03-09 18:34:07 +02:00
parent bd4fe5abec
commit 221f029a1d
41 changed files with 836 additions and 747 deletions

View File

@@ -3,7 +3,7 @@ package api
import (
"github.com/gin-gonic/gin"
"github.com/pikami/cosmium/api/config"
"github.com/pikami/cosmium/internal/repositories"
"github.com/pikami/cosmium/internal/datastore"
)
type ApiServer struct {
@@ -14,7 +14,7 @@ type ApiServer struct {
config *config.ServerConfig
}
func NewApiServer(dataRepository *repositories.DataRepository, config *config.ServerConfig) *ApiServer {
func NewApiServer(dataStore datastore.DataStore, config *config.ServerConfig) *ApiServer {
stopChan := make(chan interface{})
onServerShutdownChan := make(chan interface{})
@@ -24,7 +24,7 @@ func NewApiServer(dataRepository *repositories.DataRepository, config *config.Se
config: config,
}
apiServer.CreateRouter(dataRepository)
apiServer.CreateRouter(dataStore)
return apiServer
}

View File

@@ -5,15 +5,15 @@ import (
"net/http"
"github.com/gin-gonic/gin"
repositorymodels "github.com/pikami/cosmium/internal/repository_models"
"github.com/pikami/cosmium/internal/datastore"
)
func (h *Handlers) GetAllCollections(c *gin.Context) {
databaseId := c.Param("databaseId")
collections, status := h.repository.GetAllCollections(databaseId)
if status == repositorymodels.StatusOk {
database, _ := h.repository.GetDatabase(databaseId)
collections, status := h.dataStore.GetAllCollections(databaseId)
if status == datastore.StatusOk {
database, _ := h.dataStore.GetDatabase(databaseId)
c.Header("x-ms-item-count", fmt.Sprintf("%d", len(collections)))
c.IndentedJSON(http.StatusOK, gin.H{
@@ -31,13 +31,13 @@ func (h *Handlers) GetCollection(c *gin.Context) {
databaseId := c.Param("databaseId")
id := c.Param("collId")
collection, status := h.repository.GetCollection(databaseId, id)
if status == repositorymodels.StatusOk {
collection, status := h.dataStore.GetCollection(databaseId, id)
if status == datastore.StatusOk {
c.IndentedJSON(http.StatusOK, collection)
return
}
if status == repositorymodels.StatusNotFound {
if status == datastore.StatusNotFound {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"})
return
}
@@ -49,13 +49,13 @@ func (h *Handlers) DeleteCollection(c *gin.Context) {
databaseId := c.Param("databaseId")
id := c.Param("collId")
status := h.repository.DeleteCollection(databaseId, id)
if status == repositorymodels.StatusOk {
status := h.dataStore.DeleteCollection(databaseId, id)
if status == datastore.StatusOk {
c.Status(http.StatusNoContent)
return
}
if status == repositorymodels.StatusNotFound {
if status == datastore.StatusNotFound {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"})
return
}
@@ -65,7 +65,7 @@ func (h *Handlers) DeleteCollection(c *gin.Context) {
func (h *Handlers) CreateCollection(c *gin.Context) {
databaseId := c.Param("databaseId")
var newCollection repositorymodels.Collection
var newCollection datastore.Collection
if err := c.BindJSON(&newCollection); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"message": err.Error()})
@@ -77,13 +77,13 @@ func (h *Handlers) CreateCollection(c *gin.Context) {
return
}
createdCollection, status := h.repository.CreateCollection(databaseId, newCollection)
if status == repositorymodels.Conflict {
createdCollection, status := h.dataStore.CreateCollection(databaseId, newCollection)
if status == datastore.Conflict {
c.IndentedJSON(http.StatusConflict, gin.H{"message": "Conflict"})
return
}
if status == repositorymodels.StatusOk {
if status == datastore.StatusOk {
c.IndentedJSON(http.StatusCreated, createdCollection)
return
}

View File

@@ -7,11 +7,11 @@ import (
)
func (h *Handlers) CosmiumExport(c *gin.Context) {
repositoryState, err := h.repository.GetState()
dataStoreState, err := h.dataStore.DumpToJson()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.Data(http.StatusOK, "application/json", []byte(repositoryState))
c.Data(http.StatusOK, "application/json", []byte(dataStoreState))
}

View File

@@ -5,12 +5,12 @@ import (
"net/http"
"github.com/gin-gonic/gin"
repositorymodels "github.com/pikami/cosmium/internal/repository_models"
"github.com/pikami/cosmium/internal/datastore"
)
func (h *Handlers) GetAllDatabases(c *gin.Context) {
databases, status := h.repository.GetAllDatabases()
if status == repositorymodels.StatusOk {
databases, status := h.dataStore.GetAllDatabases()
if status == datastore.StatusOk {
c.Header("x-ms-item-count", fmt.Sprintf("%d", len(databases)))
c.IndentedJSON(http.StatusOK, gin.H{
"_rid": "",
@@ -26,13 +26,13 @@ func (h *Handlers) GetAllDatabases(c *gin.Context) {
func (h *Handlers) GetDatabase(c *gin.Context) {
id := c.Param("databaseId")
database, status := h.repository.GetDatabase(id)
if status == repositorymodels.StatusOk {
database, status := h.dataStore.GetDatabase(id)
if status == datastore.StatusOk {
c.IndentedJSON(http.StatusOK, database)
return
}
if status == repositorymodels.StatusNotFound {
if status == datastore.StatusNotFound {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"})
return
}
@@ -43,13 +43,13 @@ func (h *Handlers) GetDatabase(c *gin.Context) {
func (h *Handlers) DeleteDatabase(c *gin.Context) {
id := c.Param("databaseId")
status := h.repository.DeleteDatabase(id)
if status == repositorymodels.StatusOk {
status := h.dataStore.DeleteDatabase(id)
if status == datastore.StatusOk {
c.Status(http.StatusNoContent)
return
}
if status == repositorymodels.StatusNotFound {
if status == datastore.StatusNotFound {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"})
return
}
@@ -58,7 +58,7 @@ func (h *Handlers) DeleteDatabase(c *gin.Context) {
}
func (h *Handlers) CreateDatabase(c *gin.Context) {
var newDatabase repositorymodels.Database
var newDatabase datastore.Database
if err := c.BindJSON(&newDatabase); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"message": err.Error()})
@@ -70,13 +70,13 @@ func (h *Handlers) CreateDatabase(c *gin.Context) {
return
}
createdDatabase, status := h.repository.CreateDatabase(newDatabase)
if status == repositorymodels.Conflict {
createdDatabase, status := h.dataStore.CreateDatabase(newDatabase)
if status == datastore.Conflict {
c.IndentedJSON(http.StatusConflict, gin.H{"message": "Conflict"})
return
}
if status == repositorymodels.StatusOk {
if status == datastore.StatusOk {
c.IndentedJSON(http.StatusCreated, createdDatabase)
return
}

View File

@@ -10,17 +10,20 @@ import (
"github.com/gin-gonic/gin"
apimodels "github.com/pikami/cosmium/api/api_models"
"github.com/pikami/cosmium/internal/constants"
"github.com/pikami/cosmium/internal/datastore"
"github.com/pikami/cosmium/internal/logger"
repositorymodels "github.com/pikami/cosmium/internal/repository_models"
"github.com/pikami/cosmium/parsers"
"github.com/pikami/cosmium/parsers/nosql"
memoryexecutor "github.com/pikami/cosmium/query_executors/memory_executor"
)
func (h *Handlers) GetAllDocuments(c *gin.Context) {
databaseId := c.Param("databaseId")
collectionId := c.Param("collId")
documents, status := h.repository.GetAllDocuments(databaseId, collectionId)
if status == repositorymodels.StatusOk {
collection, _ := h.repository.GetCollection(databaseId, collectionId)
documents, status := h.dataStore.GetAllDocuments(databaseId, collectionId)
if status == datastore.StatusOk {
collection, _ := h.dataStore.GetCollection(databaseId, collectionId)
c.Header("x-ms-item-count", fmt.Sprintf("%d", len(documents)))
c.IndentedJSON(http.StatusOK, gin.H{
@@ -39,13 +42,13 @@ func (h *Handlers) GetDocument(c *gin.Context) {
collectionId := c.Param("collId")
documentId := c.Param("docId")
document, status := h.repository.GetDocument(databaseId, collectionId, documentId)
if status == repositorymodels.StatusOk {
document, status := h.dataStore.GetDocument(databaseId, collectionId, documentId)
if status == datastore.StatusOk {
c.IndentedJSON(http.StatusOK, document)
return
}
if status == repositorymodels.StatusNotFound {
if status == datastore.StatusNotFound {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"})
return
}
@@ -58,13 +61,13 @@ func (h *Handlers) DeleteDocument(c *gin.Context) {
collectionId := c.Param("collId")
documentId := c.Param("docId")
status := h.repository.DeleteDocument(databaseId, collectionId, documentId)
if status == repositorymodels.StatusOk {
status := h.dataStore.DeleteDocument(databaseId, collectionId, documentId)
if status == datastore.StatusOk {
c.Status(http.StatusNoContent)
return
}
if status == repositorymodels.StatusNotFound {
if status == datastore.StatusNotFound {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"})
return
}
@@ -72,7 +75,7 @@ func (h *Handlers) DeleteDocument(c *gin.Context) {
c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"})
}
// TODO: Maybe move "replace" logic to repository
// TODO: Maybe move "replace" logic to data store
func (h *Handlers) ReplaceDocument(c *gin.Context) {
databaseId := c.Param("databaseId")
collectionId := c.Param("collId")
@@ -84,19 +87,19 @@ func (h *Handlers) ReplaceDocument(c *gin.Context) {
return
}
status := h.repository.DeleteDocument(databaseId, collectionId, documentId)
if status == repositorymodels.StatusNotFound {
status := h.dataStore.DeleteDocument(databaseId, collectionId, documentId)
if status == datastore.StatusNotFound {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"})
return
}
createdDocument, status := h.repository.CreateDocument(databaseId, collectionId, requestBody)
if status == repositorymodels.Conflict {
createdDocument, status := h.dataStore.CreateDocument(databaseId, collectionId, requestBody)
if status == datastore.Conflict {
c.IndentedJSON(http.StatusConflict, gin.H{"message": "Conflict"})
return
}
if status == repositorymodels.StatusOk {
if status == datastore.StatusOk {
c.IndentedJSON(http.StatusCreated, createdDocument)
return
}
@@ -109,8 +112,8 @@ func (h *Handlers) PatchDocument(c *gin.Context) {
collectionId := c.Param("collId")
documentId := c.Param("docId")
document, status := h.repository.GetDocument(databaseId, collectionId, documentId)
if status == repositorymodels.StatusNotFound {
document, status := h.dataStore.GetDocument(databaseId, collectionId, documentId)
if status == datastore.StatusNotFound {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"})
return
}
@@ -160,19 +163,19 @@ func (h *Handlers) PatchDocument(c *gin.Context) {
return
}
status = h.repository.DeleteDocument(databaseId, collectionId, documentId)
if status == repositorymodels.StatusNotFound {
status = h.dataStore.DeleteDocument(databaseId, collectionId, documentId)
if status == datastore.StatusNotFound {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"})
return
}
createdDocument, status := h.repository.CreateDocument(databaseId, collectionId, modifiedDocument)
if status == repositorymodels.Conflict {
createdDocument, status := h.dataStore.CreateDocument(databaseId, collectionId, modifiedDocument)
if status == datastore.Conflict {
c.IndentedJSON(http.StatusConflict, gin.H{"message": "Conflict"})
return
}
if status == repositorymodels.StatusOk {
if status == datastore.StatusOk {
c.IndentedJSON(http.StatusCreated, createdDocument)
return
}
@@ -210,16 +213,16 @@ func (h *Handlers) DocumentsPost(c *gin.Context) {
isUpsert, _ := strconv.ParseBool(c.GetHeader("x-ms-documentdb-is-upsert"))
if isUpsert {
h.repository.DeleteDocument(databaseId, collectionId, requestBody["id"].(string))
h.dataStore.DeleteDocument(databaseId, collectionId, requestBody["id"].(string))
}
createdDocument, status := h.repository.CreateDocument(databaseId, collectionId, requestBody)
if status == repositorymodels.Conflict {
createdDocument, status := h.dataStore.CreateDocument(databaseId, collectionId, requestBody)
if status == datastore.Conflict {
c.IndentedJSON(http.StatusConflict, gin.H{"message": "Conflict"})
return
}
if status == repositorymodels.StatusOk {
if status == datastore.StatusOk {
c.IndentedJSON(http.StatusCreated, createdDocument)
return
}
@@ -253,14 +256,15 @@ func (h *Handlers) handleDocumentQuery(c *gin.Context, requestBody map[string]in
queryParameters = parametersToMap(paramsArray)
}
docs, status := h.repository.ExecuteQueryDocuments(databaseId, collectionId, requestBody["query"].(string), queryParameters)
if status != repositorymodels.StatusOk {
queryText := requestBody["query"].(string)
docs, status := h.executeQueryDocuments(databaseId, collectionId, queryText, queryParameters)
if status != datastore.StatusOk {
// TODO: Currently we return everything if the query fails
h.GetAllDocuments(c)
return
}
collection, _ := h.repository.GetCollection(databaseId, collectionId)
collection, _ := h.dataStore.GetCollection(databaseId, collectionId)
c.Header("x-ms-item-count", fmt.Sprintf("%d", len(docs)))
c.IndentedJSON(http.StatusOK, gin.H{
"_rid": collection.ResourceID,
@@ -283,9 +287,9 @@ func (h *Handlers) handleBatchRequest(c *gin.Context) {
for idx, operation := range batchOperations {
switch operation.OperationType {
case apimodels.BatchOperationTypeCreate:
createdDocument, status := h.repository.CreateDocument(databaseId, collectionId, operation.ResourceBody)
responseCode := repositoryStatusToResponseCode(status)
if status == repositorymodels.StatusOk {
createdDocument, status := h.dataStore.CreateDocument(databaseId, collectionId, operation.ResourceBody)
responseCode := dataStoreStatusToResponseCode(status)
if status == datastore.StatusOk {
responseCode = http.StatusCreated
}
batchOperationResults[idx] = apimodels.BatchOperationResult{
@@ -293,25 +297,25 @@ func (h *Handlers) handleBatchRequest(c *gin.Context) {
ResourceBody: createdDocument,
}
case apimodels.BatchOperationTypeDelete:
status := h.repository.DeleteDocument(databaseId, collectionId, operation.Id)
responseCode := repositoryStatusToResponseCode(status)
if status == repositorymodels.StatusOk {
status := h.dataStore.DeleteDocument(databaseId, collectionId, operation.Id)
responseCode := dataStoreStatusToResponseCode(status)
if status == datastore.StatusOk {
responseCode = http.StatusNoContent
}
batchOperationResults[idx] = apimodels.BatchOperationResult{
StatusCode: responseCode,
}
case apimodels.BatchOperationTypeReplace:
deleteStatus := h.repository.DeleteDocument(databaseId, collectionId, operation.Id)
if deleteStatus == repositorymodels.StatusNotFound {
deleteStatus := h.dataStore.DeleteDocument(databaseId, collectionId, operation.Id)
if deleteStatus == datastore.StatusNotFound {
batchOperationResults[idx] = apimodels.BatchOperationResult{
StatusCode: http.StatusNotFound,
}
continue
}
createdDocument, createStatus := h.repository.CreateDocument(databaseId, collectionId, operation.ResourceBody)
responseCode := repositoryStatusToResponseCode(createStatus)
if createStatus == repositorymodels.StatusOk {
createdDocument, createStatus := h.dataStore.CreateDocument(databaseId, collectionId, operation.ResourceBody)
responseCode := dataStoreStatusToResponseCode(createStatus)
if createStatus == datastore.StatusOk {
responseCode = http.StatusCreated
}
batchOperationResults[idx] = apimodels.BatchOperationResult{
@@ -320,10 +324,10 @@ func (h *Handlers) handleBatchRequest(c *gin.Context) {
}
case apimodels.BatchOperationTypeUpsert:
documentId := operation.ResourceBody["id"].(string)
h.repository.DeleteDocument(databaseId, collectionId, documentId)
createdDocument, createStatus := h.repository.CreateDocument(databaseId, collectionId, operation.ResourceBody)
responseCode := repositoryStatusToResponseCode(createStatus)
if createStatus == repositorymodels.StatusOk {
h.dataStore.DeleteDocument(databaseId, collectionId, documentId)
createdDocument, createStatus := h.dataStore.CreateDocument(databaseId, collectionId, operation.ResourceBody)
responseCode := dataStoreStatusToResponseCode(createStatus)
if createStatus == datastore.StatusOk {
responseCode = http.StatusCreated
}
batchOperationResults[idx] = apimodels.BatchOperationResult{
@@ -331,9 +335,9 @@ func (h *Handlers) handleBatchRequest(c *gin.Context) {
ResourceBody: createdDocument,
}
case apimodels.BatchOperationTypeRead:
document, status := h.repository.GetDocument(databaseId, collectionId, operation.Id)
document, status := h.dataStore.GetDocument(databaseId, collectionId, operation.Id)
batchOperationResults[idx] = apimodels.BatchOperationResult{
StatusCode: repositoryStatusToResponseCode(status),
StatusCode: dataStoreStatusToResponseCode(status),
ResourceBody: document,
}
case apimodels.BatchOperationTypePatch:
@@ -352,17 +356,43 @@ func (h *Handlers) handleBatchRequest(c *gin.Context) {
c.JSON(http.StatusOK, batchOperationResults)
}
func repositoryStatusToResponseCode(status repositorymodels.RepositoryStatus) int {
func dataStoreStatusToResponseCode(status datastore.DataStoreStatus) int {
switch status {
case repositorymodels.StatusOk:
case datastore.StatusOk:
return http.StatusOK
case repositorymodels.StatusNotFound:
case datastore.StatusNotFound:
return http.StatusNotFound
case repositorymodels.Conflict:
case datastore.Conflict:
return http.StatusConflict
case repositorymodels.BadRequest:
case datastore.BadRequest:
return http.StatusBadRequest
default:
return http.StatusInternalServerError
}
}
func (h *Handlers) executeQueryDocuments(databaseId string, collectionId string, query string, queryParameters map[string]interface{}) ([]memoryexecutor.RowType, datastore.DataStoreStatus) {
parsedQuery, err := nosql.Parse("", []byte(query))
if err != nil {
logger.Errorf("Failed to parse query: %s\nerr: %v", query, err)
return nil, datastore.BadRequest
}
collectionDocuments, status := h.dataStore.GetAllDocuments(databaseId, collectionId)
if status != datastore.StatusOk {
return nil, status
}
// TODO: Investigate, this could cause unnecessary memory usage
covDocs := make([]memoryexecutor.RowType, 0)
for _, doc := range collectionDocuments {
covDocs = append(covDocs, map[string]interface{}(doc))
}
if typedQuery, ok := parsedQuery.(parsers.SelectStmt); ok {
typedQuery.Parameters = queryParameters
return memoryexecutor.ExecuteQuery(typedQuery, covDocs), datastore.StatusOk
}
return nil, datastore.BadRequest
}

View File

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

View File

@@ -5,7 +5,7 @@ import (
"net/http"
"github.com/gin-gonic/gin"
repositorymodels "github.com/pikami/cosmium/internal/repository_models"
"github.com/pikami/cosmium/internal/datastore"
"github.com/pikami/cosmium/internal/resourceid"
)
@@ -18,8 +18,8 @@ func (h *Handlers) GetPartitionKeyRanges(c *gin.Context) {
return
}
partitionKeyRanges, status := h.repository.GetPartitionKeyRanges(databaseId, collectionId)
if status == repositorymodels.StatusOk {
partitionKeyRanges, status := h.dataStore.GetPartitionKeyRanges(databaseId, collectionId)
if status == datastore.StatusOk {
c.Header("etag", "\"420\"")
c.Header("lsn", "420")
c.Header("x-ms-cosmos-llsn", "420")
@@ -27,7 +27,7 @@ func (h *Handlers) GetPartitionKeyRanges(c *gin.Context) {
c.Header("x-ms-item-count", fmt.Sprintf("%d", len(partitionKeyRanges)))
collectionRid := collectionId
collection, _ := h.repository.GetCollection(databaseId, collectionId)
collection, _ := h.dataStore.GetCollection(databaseId, collectionId)
if collection.ResourceID != "" {
collectionRid = collection.ResourceID
}
@@ -41,7 +41,7 @@ func (h *Handlers) GetPartitionKeyRanges(c *gin.Context) {
return
}
if status == repositorymodels.StatusNotFound {
if status == datastore.StatusNotFound {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"})
return
}

View File

@@ -5,16 +5,16 @@ import (
"net/http"
"github.com/gin-gonic/gin"
repositorymodels "github.com/pikami/cosmium/internal/repository_models"
"github.com/pikami/cosmium/internal/datastore"
)
func (h *Handlers) GetAllStoredProcedures(c *gin.Context) {
databaseId := c.Param("databaseId")
collectionId := c.Param("collId")
sps, status := h.repository.GetAllStoredProcedures(databaseId, collectionId)
sps, status := h.dataStore.GetAllStoredProcedures(databaseId, collectionId)
if status == repositorymodels.StatusOk {
if status == datastore.StatusOk {
c.Header("x-ms-item-count", fmt.Sprintf("%d", len(sps)))
c.IndentedJSON(http.StatusOK, gin.H{"_rid": "", "StoredProcedures": sps, "_count": len(sps)})
return
@@ -28,14 +28,14 @@ func (h *Handlers) GetStoredProcedure(c *gin.Context) {
collectionId := c.Param("collId")
spId := c.Param("spId")
sp, status := h.repository.GetStoredProcedure(databaseId, collectionId, spId)
sp, status := h.dataStore.GetStoredProcedure(databaseId, collectionId, spId)
if status == repositorymodels.StatusOk {
if status == datastore.StatusOk {
c.IndentedJSON(http.StatusOK, sp)
return
}
if status == repositorymodels.StatusNotFound {
if status == datastore.StatusNotFound {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"})
return
}
@@ -48,13 +48,13 @@ func (h *Handlers) DeleteStoredProcedure(c *gin.Context) {
collectionId := c.Param("collId")
spId := c.Param("spId")
status := h.repository.DeleteStoredProcedure(databaseId, collectionId, spId)
if status == repositorymodels.StatusOk {
status := h.dataStore.DeleteStoredProcedure(databaseId, collectionId, spId)
if status == datastore.StatusOk {
c.Status(http.StatusNoContent)
return
}
if status == repositorymodels.StatusNotFound {
if status == datastore.StatusNotFound {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"})
return
}
@@ -67,25 +67,25 @@ func (h *Handlers) ReplaceStoredProcedure(c *gin.Context) {
collectionId := c.Param("collId")
spId := c.Param("spId")
var sp repositorymodels.StoredProcedure
var sp datastore.StoredProcedure
if err := c.BindJSON(&sp); err != nil {
c.IndentedJSON(http.StatusBadRequest, gin.H{"message": "Invalid body"})
return
}
status := h.repository.DeleteStoredProcedure(databaseId, collectionId, spId)
if status == repositorymodels.StatusNotFound {
status := h.dataStore.DeleteStoredProcedure(databaseId, collectionId, spId)
if status == datastore.StatusNotFound {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"})
return
}
createdSP, status := h.repository.CreateStoredProcedure(databaseId, collectionId, sp)
if status == repositorymodels.Conflict {
createdSP, status := h.dataStore.CreateStoredProcedure(databaseId, collectionId, sp)
if status == datastore.Conflict {
c.IndentedJSON(http.StatusConflict, gin.H{"message": "Conflict"})
return
}
if status == repositorymodels.StatusOk {
if status == datastore.StatusOk {
c.IndentedJSON(http.StatusOK, createdSP)
return
}
@@ -97,19 +97,19 @@ func (h *Handlers) CreateStoredProcedure(c *gin.Context) {
databaseId := c.Param("databaseId")
collectionId := c.Param("collId")
var sp repositorymodels.StoredProcedure
var sp datastore.StoredProcedure
if err := c.BindJSON(&sp); err != nil {
c.IndentedJSON(http.StatusBadRequest, gin.H{"message": "Invalid body"})
return
}
createdSP, status := h.repository.CreateStoredProcedure(databaseId, collectionId, sp)
if status == repositorymodels.Conflict {
createdSP, status := h.dataStore.CreateStoredProcedure(databaseId, collectionId, sp)
if status == datastore.Conflict {
c.IndentedJSON(http.StatusConflict, gin.H{"message": "Conflict"})
return
}
if status == repositorymodels.StatusOk {
if status == datastore.StatusOk {
c.IndentedJSON(http.StatusCreated, createdSP)
return
}

View File

@@ -5,16 +5,16 @@ import (
"net/http"
"github.com/gin-gonic/gin"
repositorymodels "github.com/pikami/cosmium/internal/repository_models"
"github.com/pikami/cosmium/internal/datastore"
)
func (h *Handlers) GetAllTriggers(c *gin.Context) {
databaseId := c.Param("databaseId")
collectionId := c.Param("collId")
triggers, status := h.repository.GetAllTriggers(databaseId, collectionId)
triggers, status := h.dataStore.GetAllTriggers(databaseId, collectionId)
if status == repositorymodels.StatusOk {
if status == datastore.StatusOk {
c.Header("x-ms-item-count", fmt.Sprintf("%d", len(triggers)))
c.IndentedJSON(http.StatusOK, gin.H{"_rid": "", "Triggers": triggers, "_count": len(triggers)})
return
@@ -28,14 +28,14 @@ func (h *Handlers) GetTrigger(c *gin.Context) {
collectionId := c.Param("collId")
triggerId := c.Param("triggerId")
trigger, status := h.repository.GetTrigger(databaseId, collectionId, triggerId)
trigger, status := h.dataStore.GetTrigger(databaseId, collectionId, triggerId)
if status == repositorymodels.StatusOk {
if status == datastore.StatusOk {
c.IndentedJSON(http.StatusOK, trigger)
return
}
if status == repositorymodels.StatusNotFound {
if status == datastore.StatusNotFound {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"})
return
}
@@ -48,13 +48,13 @@ func (h *Handlers) DeleteTrigger(c *gin.Context) {
collectionId := c.Param("collId")
triggerId := c.Param("triggerId")
status := h.repository.DeleteTrigger(databaseId, collectionId, triggerId)
if status == repositorymodels.StatusOk {
status := h.dataStore.DeleteTrigger(databaseId, collectionId, triggerId)
if status == datastore.StatusOk {
c.Status(http.StatusNoContent)
return
}
if status == repositorymodels.StatusNotFound {
if status == datastore.StatusNotFound {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"})
return
}
@@ -67,25 +67,25 @@ func (h *Handlers) ReplaceTrigger(c *gin.Context) {
collectionId := c.Param("collId")
triggerId := c.Param("triggerId")
var trigger repositorymodels.Trigger
var trigger datastore.Trigger
if err := c.BindJSON(&trigger); err != nil {
c.IndentedJSON(http.StatusBadRequest, gin.H{"message": "Invalid body"})
return
}
status := h.repository.DeleteTrigger(databaseId, collectionId, triggerId)
if status == repositorymodels.StatusNotFound {
status := h.dataStore.DeleteTrigger(databaseId, collectionId, triggerId)
if status == datastore.StatusNotFound {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"})
return
}
createdTrigger, status := h.repository.CreateTrigger(databaseId, collectionId, trigger)
if status == repositorymodels.Conflict {
createdTrigger, status := h.dataStore.CreateTrigger(databaseId, collectionId, trigger)
if status == datastore.Conflict {
c.IndentedJSON(http.StatusConflict, gin.H{"message": "Conflict"})
return
}
if status == repositorymodels.StatusOk {
if status == datastore.StatusOk {
c.IndentedJSON(http.StatusOK, createdTrigger)
return
}
@@ -97,19 +97,19 @@ func (h *Handlers) CreateTrigger(c *gin.Context) {
databaseId := c.Param("databaseId")
collectionId := c.Param("collId")
var trigger repositorymodels.Trigger
var trigger datastore.Trigger
if err := c.BindJSON(&trigger); err != nil {
c.IndentedJSON(http.StatusBadRequest, gin.H{"message": "Invalid body"})
return
}
createdTrigger, status := h.repository.CreateTrigger(databaseId, collectionId, trigger)
if status == repositorymodels.Conflict {
createdTrigger, status := h.dataStore.CreateTrigger(databaseId, collectionId, trigger)
if status == datastore.Conflict {
c.IndentedJSON(http.StatusConflict, gin.H{"message": "Conflict"})
return
}
if status == repositorymodels.StatusOk {
if status == datastore.StatusOk {
c.IndentedJSON(http.StatusCreated, createdTrigger)
return
}

View File

@@ -5,16 +5,16 @@ import (
"net/http"
"github.com/gin-gonic/gin"
repositorymodels "github.com/pikami/cosmium/internal/repository_models"
"github.com/pikami/cosmium/internal/datastore"
)
func (h *Handlers) GetAllUserDefinedFunctions(c *gin.Context) {
databaseId := c.Param("databaseId")
collectionId := c.Param("collId")
udfs, status := h.repository.GetAllUserDefinedFunctions(databaseId, collectionId)
udfs, status := h.dataStore.GetAllUserDefinedFunctions(databaseId, collectionId)
if status == repositorymodels.StatusOk {
if status == datastore.StatusOk {
c.Header("x-ms-item-count", fmt.Sprintf("%d", len(udfs)))
c.IndentedJSON(http.StatusOK, gin.H{"_rid": "", "UserDefinedFunctions": udfs, "_count": len(udfs)})
return
@@ -28,14 +28,14 @@ func (h *Handlers) GetUserDefinedFunction(c *gin.Context) {
collectionId := c.Param("collId")
udfId := c.Param("udfId")
udf, status := h.repository.GetUserDefinedFunction(databaseId, collectionId, udfId)
udf, status := h.dataStore.GetUserDefinedFunction(databaseId, collectionId, udfId)
if status == repositorymodels.StatusOk {
if status == datastore.StatusOk {
c.IndentedJSON(http.StatusOK, udf)
return
}
if status == repositorymodels.StatusNotFound {
if status == datastore.StatusNotFound {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"})
return
}
@@ -48,13 +48,13 @@ func (h *Handlers) DeleteUserDefinedFunction(c *gin.Context) {
collectionId := c.Param("collId")
udfId := c.Param("udfId")
status := h.repository.DeleteUserDefinedFunction(databaseId, collectionId, udfId)
if status == repositorymodels.StatusOk {
status := h.dataStore.DeleteUserDefinedFunction(databaseId, collectionId, udfId)
if status == datastore.StatusOk {
c.Status(http.StatusNoContent)
return
}
if status == repositorymodels.StatusNotFound {
if status == datastore.StatusNotFound {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"})
return
}
@@ -67,25 +67,25 @@ func (h *Handlers) ReplaceUserDefinedFunction(c *gin.Context) {
collectionId := c.Param("collId")
udfId := c.Param("udfId")
var udf repositorymodels.UserDefinedFunction
var udf datastore.UserDefinedFunction
if err := c.BindJSON(&udf); err != nil {
c.IndentedJSON(http.StatusBadRequest, gin.H{"message": "Invalid body"})
return
}
status := h.repository.DeleteUserDefinedFunction(databaseId, collectionId, udfId)
if status == repositorymodels.StatusNotFound {
status := h.dataStore.DeleteUserDefinedFunction(databaseId, collectionId, udfId)
if status == datastore.StatusNotFound {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"})
return
}
createdUdf, status := h.repository.CreateUserDefinedFunction(databaseId, collectionId, udf)
if status == repositorymodels.Conflict {
createdUdf, status := h.dataStore.CreateUserDefinedFunction(databaseId, collectionId, udf)
if status == datastore.Conflict {
c.IndentedJSON(http.StatusConflict, gin.H{"message": "Conflict"})
return
}
if status == repositorymodels.StatusOk {
if status == datastore.StatusOk {
c.IndentedJSON(http.StatusOK, createdUdf)
return
}
@@ -97,19 +97,19 @@ func (h *Handlers) CreateUserDefinedFunction(c *gin.Context) {
databaseId := c.Param("databaseId")
collectionId := c.Param("collId")
var udf repositorymodels.UserDefinedFunction
var udf datastore.UserDefinedFunction
if err := c.BindJSON(&udf); err != nil {
c.IndentedJSON(http.StatusBadRequest, gin.H{"message": "Invalid body"})
return
}
createdUdf, status := h.repository.CreateUserDefinedFunction(databaseId, collectionId, udf)
if status == repositorymodels.Conflict {
createdUdf, status := h.dataStore.CreateUserDefinedFunction(databaseId, collectionId, udf)
if status == datastore.Conflict {
c.IndentedJSON(http.StatusConflict, gin.H{"message": "Conflict"})
return
}
if status == repositorymodels.StatusOk {
if status == datastore.StatusOk {
c.IndentedJSON(http.StatusCreated, createdUdf)
return
}

View File

@@ -10,15 +10,15 @@ import (
"github.com/gin-gonic/gin"
"github.com/pikami/cosmium/api/handlers"
"github.com/pikami/cosmium/api/handlers/middleware"
"github.com/pikami/cosmium/internal/datastore"
"github.com/pikami/cosmium/internal/logger"
"github.com/pikami/cosmium/internal/repositories"
tlsprovider "github.com/pikami/cosmium/internal/tls_provider"
)
var ginMux sync.Mutex
func (s *ApiServer) CreateRouter(repository *repositories.DataRepository) {
routeHandlers := handlers.NewHandlers(repository, s.config)
func (s *ApiServer) CreateRouter(dataStore datastore.DataStore) {
routeHandlers := handlers.NewHandlers(dataStore, s.config)
ginMux.Lock()
gin.DefaultWriter = logger.InfoWriter()

View File

@@ -17,7 +17,7 @@ func Test_Authentication(t *testing.T) {
defer ts.Server.Close()
t.Run("Should get 200 when correct account key is used", func(t *testing.T) {
ts.Repository.DeleteDatabase(testDatabaseName)
ts.DataStore.DeleteDatabase(testDatabaseName)
client, err := azcosmos.NewClientFromConnectionString(
fmt.Sprintf("AccountEndpoint=%s;AccountKey=%s", ts.URL, config.DefaultAccountKey),
&azcosmos.ClientOptions{},
@@ -33,7 +33,7 @@ func Test_Authentication(t *testing.T) {
})
t.Run("Should get 401 when wrong account key is used", func(t *testing.T) {
ts.Repository.DeleteDatabase(testDatabaseName)
ts.DataStore.DeleteDatabase(testDatabaseName)
client, err := azcosmos.NewClientFromConnectionString(
fmt.Sprintf("AccountEndpoint=%s;AccountKey=%s", ts.URL, "AAAA"),
&azcosmos.ClientOptions{},
@@ -70,7 +70,7 @@ func Test_Authentication_Disabled(t *testing.T) {
defer ts.Server.Close()
t.Run("Should get 200 when wrong account key is used, but authentication is dissabled", func(t *testing.T) {
ts.Repository.DeleteDatabase(testDatabaseName)
ts.DataStore.DeleteDatabase(testDatabaseName)
client, err := azcosmos.NewClientFromConnectionString(
fmt.Sprintf("AccountEndpoint=%s;AccountKey=%s", ts.URL, "AAAA"),
&azcosmos.ClientOptions{},

View File

@@ -10,7 +10,7 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos"
"github.com/pikami/cosmium/api/config"
repositorymodels "github.com/pikami/cosmium/internal/repository_models"
"github.com/pikami/cosmium/internal/datastore"
"github.com/stretchr/testify/assert"
)
@@ -24,7 +24,7 @@ func Test_Collections(t *testing.T) {
)
assert.Nil(t, err)
ts.Repository.CreateDatabase(repositorymodels.Database{ID: testDatabaseName})
ts.DataStore.CreateDatabase(datastore.Database{ID: testDatabaseName})
databaseClient, err := client.NewDatabase(testDatabaseName)
assert.Nil(t, err)
@@ -39,7 +39,7 @@ func Test_Collections(t *testing.T) {
})
t.Run("Should return conflict when collection exists", func(t *testing.T) {
ts.Repository.CreateCollection(testDatabaseName, repositorymodels.Collection{
ts.DataStore.CreateCollection(testDatabaseName, datastore.Collection{
ID: testCollectionName,
})
@@ -59,7 +59,7 @@ func Test_Collections(t *testing.T) {
t.Run("Collection Read", func(t *testing.T) {
t.Run("Should read collection", func(t *testing.T) {
ts.Repository.CreateCollection(testDatabaseName, repositorymodels.Collection{
ts.DataStore.CreateCollection(testDatabaseName, datastore.Collection{
ID: testCollectionName,
})
@@ -73,7 +73,7 @@ func Test_Collections(t *testing.T) {
})
t.Run("Should return not found when collection does not exist", func(t *testing.T) {
ts.Repository.DeleteCollection(testDatabaseName, testCollectionName)
ts.DataStore.DeleteCollection(testDatabaseName, testCollectionName)
collectionResponse, err := databaseClient.NewContainer(testCollectionName)
assert.Nil(t, err)
@@ -92,7 +92,7 @@ func Test_Collections(t *testing.T) {
t.Run("Collection Delete", func(t *testing.T) {
t.Run("Should delete collection", func(t *testing.T) {
ts.Repository.CreateCollection(testDatabaseName, repositorymodels.Collection{
ts.DataStore.CreateCollection(testDatabaseName, datastore.Collection{
ID: testCollectionName,
})
@@ -105,7 +105,7 @@ func Test_Collections(t *testing.T) {
})
t.Run("Should return not found when collection does not exist", func(t *testing.T) {
ts.Repository.DeleteCollection(testDatabaseName, testCollectionName)
ts.DataStore.DeleteCollection(testDatabaseName, testCollectionName)
collectionResponse, err := databaseClient.NewContainer(testCollectionName)
assert.Nil(t, err)

View File

@@ -5,29 +5,30 @@ import (
"github.com/pikami/cosmium/api"
"github.com/pikami/cosmium/api/config"
"github.com/pikami/cosmium/internal/datastore"
mapdatastore "github.com/pikami/cosmium/internal/datastore/map_datastore"
"github.com/pikami/cosmium/internal/logger"
"github.com/pikami/cosmium/internal/repositories"
)
type TestServer struct {
Server *httptest.Server
Repository *repositories.DataRepository
URL string
Server *httptest.Server
DataStore datastore.DataStore
URL string
}
func runTestServerCustomConfig(config *config.ServerConfig) *TestServer {
repository := repositories.NewDataRepository(repositories.RepositoryOptions{})
dataStore := mapdatastore.NewMapDataStore(mapdatastore.MapDataStoreOptions{})
api := api.NewApiServer(repository, config)
api := api.NewApiServer(dataStore, config)
server := httptest.NewServer(api.GetRouter())
config.DatabaseEndpoint = server.URL
return &TestServer{
Server: server,
Repository: repository,
URL: server.URL,
Server: server,
DataStore: dataStore,
URL: server.URL,
}
}

View File

@@ -10,7 +10,7 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos"
"github.com/pikami/cosmium/api/config"
repositorymodels "github.com/pikami/cosmium/internal/repository_models"
"github.com/pikami/cosmium/internal/datastore"
"github.com/stretchr/testify/assert"
)
@@ -26,7 +26,7 @@ func Test_Databases(t *testing.T) {
t.Run("Database Create", func(t *testing.T) {
t.Run("Should create database", func(t *testing.T) {
ts.Repository.DeleteDatabase(testDatabaseName)
ts.DataStore.DeleteDatabase(testDatabaseName)
createResponse, err := client.CreateDatabase(context.TODO(), azcosmos.DatabaseProperties{
ID: testDatabaseName,
@@ -37,7 +37,7 @@ func Test_Databases(t *testing.T) {
})
t.Run("Should return conflict when database exists", func(t *testing.T) {
ts.Repository.CreateDatabase(repositorymodels.Database{
ts.DataStore.CreateDatabase(datastore.Database{
ID: testDatabaseName,
})
@@ -57,7 +57,7 @@ func Test_Databases(t *testing.T) {
t.Run("Database Read", func(t *testing.T) {
t.Run("Should read database", func(t *testing.T) {
ts.Repository.CreateDatabase(repositorymodels.Database{
ts.DataStore.CreateDatabase(datastore.Database{
ID: testDatabaseName,
})
@@ -71,7 +71,7 @@ func Test_Databases(t *testing.T) {
})
t.Run("Should return not found when database does not exist", func(t *testing.T) {
ts.Repository.DeleteDatabase(testDatabaseName)
ts.DataStore.DeleteDatabase(testDatabaseName)
databaseResponse, err := client.NewDatabase(testDatabaseName)
assert.Nil(t, err)
@@ -90,7 +90,7 @@ func Test_Databases(t *testing.T) {
t.Run("Database Delete", func(t *testing.T) {
t.Run("Should delete database", func(t *testing.T) {
ts.Repository.CreateDatabase(repositorymodels.Database{
ts.DataStore.CreateDatabase(datastore.Database{
ID: testDatabaseName,
})
@@ -103,7 +103,7 @@ func Test_Databases(t *testing.T) {
})
t.Run("Should return not found when database does not exist", func(t *testing.T) {
ts.Repository.DeleteDatabase(testDatabaseName)
ts.DataStore.DeleteDatabase(testDatabaseName)
databaseResponse, err := client.NewDatabase(testDatabaseName)
assert.Nil(t, err)

View File

@@ -14,7 +14,7 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos"
"github.com/pikami/cosmium/api/config"
repositorymodels "github.com/pikami/cosmium/internal/repository_models"
"github.com/pikami/cosmium/internal/datastore"
"github.com/stretchr/testify/assert"
)
@@ -56,8 +56,8 @@ func testCosmosQuery(t *testing.T,
func documents_InitializeDb(t *testing.T) (*TestServer, *azcosmos.ContainerClient) {
ts := runTestServer()
ts.Repository.CreateDatabase(repositorymodels.Database{ID: testDatabaseName})
ts.Repository.CreateCollection(testDatabaseName, repositorymodels.Collection{
ts.DataStore.CreateDatabase(datastore.Database{ID: testDatabaseName})
ts.DataStore.CreateCollection(testDatabaseName, datastore.Collection{
ID: testCollectionName,
PartitionKey: struct {
Paths []string "json:\"paths\""
@@ -67,8 +67,8 @@ func documents_InitializeDb(t *testing.T) (*TestServer, *azcosmos.ContainerClien
Paths: []string{"/pk"},
},
})
ts.Repository.CreateDocument(testDatabaseName, testCollectionName, map[string]interface{}{"id": "12345", "pk": "123", "isCool": false, "arr": []int{1, 2, 3}})
ts.Repository.CreateDocument(testDatabaseName, testCollectionName, map[string]interface{}{"id": "67890", "pk": "456", "isCool": true, "arr": []int{6, 7, 8}})
ts.DataStore.CreateDocument(testDatabaseName, testCollectionName, map[string]interface{}{"id": "12345", "pk": "123", "isCool": false, "arr": []int{1, 2, 3}})
ts.DataStore.CreateDocument(testDatabaseName, testCollectionName, map[string]interface{}{"id": "67890", "pk": "456", "isCool": true, "arr": []int{6, 7, 8}})
client, err := azcosmos.NewClientFromConnectionString(
fmt.Sprintf("AccountEndpoint=%s;AccountKey=%s", ts.URL, config.DefaultAccountKey),
@@ -408,7 +408,7 @@ func Test_Documents_TransactionalBatch(t *testing.T) {
json.Unmarshal(operationResponse.ResourceBody, &itemResponseBody)
assert.Equal(t, newItem["id"], itemResponseBody["id"])
createdDoc, _ := ts.Repository.GetDocument(testDatabaseName, testCollectionName, newItem["id"].(string))
createdDoc, _ := ts.DataStore.GetDocument(testDatabaseName, testCollectionName, newItem["id"].(string))
assert.Equal(t, newItem["id"], createdDoc["id"])
})
@@ -426,8 +426,8 @@ func Test_Documents_TransactionalBatch(t *testing.T) {
assert.NotNil(t, operationResponse)
assert.Equal(t, int32(http.StatusNoContent), operationResponse.StatusCode)
_, status := ts.Repository.GetDocument(testDatabaseName, testCollectionName, "12345")
assert.Equal(t, repositorymodels.StatusNotFound, int(status))
_, status := ts.DataStore.GetDocument(testDatabaseName, testCollectionName, "12345")
assert.Equal(t, datastore.StatusNotFound, int(status))
})
t.Run("Should execute REPLACE transactional batch", func(t *testing.T) {
@@ -457,7 +457,7 @@ func Test_Documents_TransactionalBatch(t *testing.T) {
assert.Equal(t, newItem["id"], itemResponseBody["id"])
assert.Equal(t, newItem["pk"], itemResponseBody["pk"])
updatedDoc, _ := ts.Repository.GetDocument(testDatabaseName, testCollectionName, newItem["id"].(string))
updatedDoc, _ := ts.DataStore.GetDocument(testDatabaseName, testCollectionName, newItem["id"].(string))
assert.Equal(t, newItem["id"], updatedDoc["id"])
assert.Equal(t, newItem["pk"], updatedDoc["pk"])
})
@@ -489,7 +489,7 @@ func Test_Documents_TransactionalBatch(t *testing.T) {
assert.Equal(t, newItem["id"], itemResponseBody["id"])
assert.Equal(t, newItem["pk"], itemResponseBody["pk"])
updatedDoc, _ := ts.Repository.GetDocument(testDatabaseName, testCollectionName, newItem["id"].(string))
updatedDoc, _ := ts.DataStore.GetDocument(testDatabaseName, testCollectionName, newItem["id"].(string))
assert.Equal(t, newItem["id"], updatedDoc["id"])
assert.Equal(t, newItem["pk"], updatedDoc["pk"])
})