diff --git a/api/handlers/stored_procedures.go b/api/handlers/stored_procedures.go index 0c6639b..1defa90 100644 --- a/api/handlers/stored_procedures.go +++ b/api/handlers/stored_procedures.go @@ -22,3 +22,97 @@ func (h *Handlers) GetAllStoredProcedures(c *gin.Context) { c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"}) } + +func (h *Handlers) GetStoredProcedure(c *gin.Context) { + databaseId := c.Param("databaseId") + collectionId := c.Param("collId") + spId := c.Param("spId") + + sp, status := h.repository.GetStoredProcedure(databaseId, collectionId, spId) + + if status == repositorymodels.StatusOk { + c.IndentedJSON(http.StatusOK, sp) + return + } + + if status == repositorymodels.StatusNotFound { + c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"}) + return + } + + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"}) +} + +func (h *Handlers) DeleteStoredProcedure(c *gin.Context) { + databaseId := c.Param("databaseId") + collectionId := c.Param("collId") + spId := c.Param("spId") + + status := h.repository.DeleteStoredProcedure(databaseId, collectionId, spId) + if status == repositorymodels.StatusOk { + c.Status(http.StatusNoContent) + return + } + + if status == repositorymodels.StatusNotFound { + c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"}) + return + } + + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"}) +} + +func (h *Handlers) ReplaceStoredProcedure(c *gin.Context) { + databaseId := c.Param("databaseId") + collectionId := c.Param("collId") + spId := c.Param("spId") + + var sp repositorymodels.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 { + c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"}) + return + } + + createdSP, status := h.repository.CreateStoredProcedure(databaseId, collectionId, sp) + if status == repositorymodels.Conflict { + c.IndentedJSON(http.StatusConflict, gin.H{"message": "Conflict"}) + return + } + + if status == repositorymodels.StatusOk { + c.IndentedJSON(http.StatusOK, createdSP) + return + } + + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"}) +} + +func (h *Handlers) CreateStoredProcedure(c *gin.Context) { + databaseId := c.Param("databaseId") + collectionId := c.Param("collId") + + var sp repositorymodels.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 { + c.IndentedJSON(http.StatusConflict, gin.H{"message": "Conflict"}) + return + } + + if status == repositorymodels.StatusOk { + c.IndentedJSON(http.StatusCreated, createdSP) + return + } + + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"}) +} diff --git a/api/handlers/triggers.go b/api/handlers/triggers.go index 3ea0c9c..9a6063e 100644 --- a/api/handlers/triggers.go +++ b/api/handlers/triggers.go @@ -22,3 +22,97 @@ func (h *Handlers) GetAllTriggers(c *gin.Context) { c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"}) } + +func (h *Handlers) GetTrigger(c *gin.Context) { + databaseId := c.Param("databaseId") + collectionId := c.Param("collId") + triggerId := c.Param("triggerId") + + trigger, status := h.repository.GetTrigger(databaseId, collectionId, triggerId) + + if status == repositorymodels.StatusOk { + c.IndentedJSON(http.StatusOK, trigger) + return + } + + if status == repositorymodels.StatusNotFound { + c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"}) + return + } + + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"}) +} + +func (h *Handlers) DeleteTrigger(c *gin.Context) { + databaseId := c.Param("databaseId") + collectionId := c.Param("collId") + triggerId := c.Param("triggerId") + + status := h.repository.DeleteTrigger(databaseId, collectionId, triggerId) + if status == repositorymodels.StatusOk { + c.Status(http.StatusNoContent) + return + } + + if status == repositorymodels.StatusNotFound { + c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"}) + return + } + + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"}) +} + +func (h *Handlers) ReplaceTrigger(c *gin.Context) { + databaseId := c.Param("databaseId") + collectionId := c.Param("collId") + triggerId := c.Param("triggerId") + + var trigger repositorymodels.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 { + c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"}) + return + } + + createdTrigger, status := h.repository.CreateTrigger(databaseId, collectionId, trigger) + if status == repositorymodels.Conflict { + c.IndentedJSON(http.StatusConflict, gin.H{"message": "Conflict"}) + return + } + + if status == repositorymodels.StatusOk { + c.IndentedJSON(http.StatusOK, createdTrigger) + return + } + + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"}) +} + +func (h *Handlers) CreateTrigger(c *gin.Context) { + databaseId := c.Param("databaseId") + collectionId := c.Param("collId") + + var trigger repositorymodels.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 { + c.IndentedJSON(http.StatusConflict, gin.H{"message": "Conflict"}) + return + } + + if status == repositorymodels.StatusOk { + c.IndentedJSON(http.StatusCreated, createdTrigger) + return + } + + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"}) +} diff --git a/api/handlers/user_defined_functions.go b/api/handlers/user_defined_functions.go index 688c38f..af734d8 100644 --- a/api/handlers/user_defined_functions.go +++ b/api/handlers/user_defined_functions.go @@ -22,3 +22,97 @@ func (h *Handlers) GetAllUserDefinedFunctions(c *gin.Context) { c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"}) } + +func (h *Handlers) GetUserDefinedFunction(c *gin.Context) { + databaseId := c.Param("databaseId") + collectionId := c.Param("collId") + udfId := c.Param("udfId") + + udf, status := h.repository.GetUserDefinedFunction(databaseId, collectionId, udfId) + + if status == repositorymodels.StatusOk { + c.IndentedJSON(http.StatusOK, udf) + return + } + + if status == repositorymodels.StatusNotFound { + c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"}) + return + } + + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"}) +} + +func (h *Handlers) DeleteUserDefinedFunction(c *gin.Context) { + databaseId := c.Param("databaseId") + collectionId := c.Param("collId") + udfId := c.Param("udfId") + + status := h.repository.DeleteUserDefinedFunction(databaseId, collectionId, udfId) + if status == repositorymodels.StatusOk { + c.Status(http.StatusNoContent) + return + } + + if status == repositorymodels.StatusNotFound { + c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"}) + return + } + + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"}) +} + +func (h *Handlers) ReplaceUserDefinedFunction(c *gin.Context) { + databaseId := c.Param("databaseId") + collectionId := c.Param("collId") + udfId := c.Param("udfId") + + var udf repositorymodels.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 { + c.IndentedJSON(http.StatusNotFound, gin.H{"message": "NotFound"}) + return + } + + createdUdf, status := h.repository.CreateUserDefinedFunction(databaseId, collectionId, udf) + if status == repositorymodels.Conflict { + c.IndentedJSON(http.StatusConflict, gin.H{"message": "Conflict"}) + return + } + + if status == repositorymodels.StatusOk { + c.IndentedJSON(http.StatusOK, createdUdf) + return + } + + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"}) +} + +func (h *Handlers) CreateUserDefinedFunction(c *gin.Context) { + databaseId := c.Param("databaseId") + collectionId := c.Param("collId") + + var udf repositorymodels.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 { + c.IndentedJSON(http.StatusConflict, gin.H{"message": "Conflict"}) + return + } + + if status == repositorymodels.StatusOk { + c.IndentedJSON(http.StatusCreated, createdUdf) + return + } + + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"}) +} diff --git a/api/router.go b/api/router.go index 6fdf729..b5c4ce8 100644 --- a/api/router.go +++ b/api/router.go @@ -50,9 +50,23 @@ func (s *ApiServer) CreateRouter(repository *repositories.DataRepository) { router.GET("/dbs/:databaseId", routeHandlers.GetDatabase) router.DELETE("/dbs/:databaseId", routeHandlers.DeleteDatabase) - router.GET("/dbs/:databaseId/colls/:collId/udfs", routeHandlers.GetAllUserDefinedFunctions) - router.GET("/dbs/:databaseId/colls/:collId/sprocs", routeHandlers.GetAllStoredProcedures) + router.POST("/dbs/:databaseId/colls/:collId/triggers", routeHandlers.CreateTrigger) router.GET("/dbs/:databaseId/colls/:collId/triggers", routeHandlers.GetAllTriggers) + router.GET("/dbs/:databaseId/colls/:collId/triggers/:triggerId", routeHandlers.GetTrigger) + router.PUT("/dbs/:databaseId/colls/:collId/triggers/:triggerId", routeHandlers.ReplaceTrigger) + router.DELETE("/dbs/:databaseId/colls/:collId/triggers/:triggerId", routeHandlers.DeleteTrigger) + + router.POST("/dbs/:databaseId/colls/:collId/sprocs", routeHandlers.CreateStoredProcedure) + router.GET("/dbs/:databaseId/colls/:collId/sprocs", routeHandlers.GetAllStoredProcedures) + router.GET("/dbs/:databaseId/colls/:collId/sprocs/:sprocId", routeHandlers.GetStoredProcedure) + router.PUT("/dbs/:databaseId/colls/:collId/sprocs/:sprocId", routeHandlers.ReplaceStoredProcedure) + router.DELETE("/dbs/:databaseId/colls/:collId/sprocs/:sprocId", routeHandlers.DeleteStoredProcedure) + + router.POST("/dbs/:databaseId/colls/:collId/udfs", routeHandlers.CreateUserDefinedFunction) + router.GET("/dbs/:databaseId/colls/:collId/udfs", routeHandlers.GetAllUserDefinedFunctions) + router.GET("/dbs/:databaseId/colls/:collId/udfs/:udfId", routeHandlers.GetUserDefinedFunction) + router.PUT("/dbs/:databaseId/colls/:collId/udfs/:udfId", routeHandlers.ReplaceUserDefinedFunction) + router.DELETE("/dbs/:databaseId/colls/:collId/udfs/:udfId", routeHandlers.DeleteUserDefinedFunction) router.GET("/offers", handlers.GetOffers) router.GET("/", routeHandlers.GetServerInfo) diff --git a/internal/repositories/collections.go b/internal/repositories/collections.go index 95bd4b3..b8d9fdb 100644 --- a/internal/repositories/collections.go +++ b/internal/repositories/collections.go @@ -77,6 +77,9 @@ func (r *DataRepository) CreateCollection(databaseId string, newCollection repos r.storeState.Collections[databaseId][newCollection.ID] = newCollection r.storeState.Documents[databaseId][newCollection.ID] = make(map[string]repositorymodels.Document) + r.storeState.Triggers[databaseId][newCollection.ID] = make(map[string]repositorymodels.Trigger) + r.storeState.StoredProcedures[databaseId][newCollection.ID] = make(map[string]repositorymodels.StoredProcedure) + r.storeState.UserDefinedFunctions[databaseId][newCollection.ID] = make(map[string]repositorymodels.UserDefinedFunction) return newCollection, repositorymodels.StatusOk } diff --git a/internal/repositories/databases.go b/internal/repositories/databases.go index 6a5b7be..d094911 100644 --- a/internal/repositories/databases.go +++ b/internal/repositories/databases.go @@ -57,6 +57,9 @@ func (r *DataRepository) CreateDatabase(newDatabase repositorymodels.Database) ( r.storeState.Databases[newDatabase.ID] = newDatabase r.storeState.Collections[newDatabase.ID] = make(map[string]repositorymodels.Collection) r.storeState.Documents[newDatabase.ID] = make(map[string]map[string]repositorymodels.Document) + r.storeState.Triggers[newDatabase.ID] = make(map[string]map[string]repositorymodels.Trigger) + r.storeState.StoredProcedures[newDatabase.ID] = make(map[string]map[string]repositorymodels.StoredProcedure) + r.storeState.UserDefinedFunctions[newDatabase.ID] = make(map[string]map[string]repositorymodels.UserDefinedFunction) return newDatabase, repositorymodels.StatusOk } diff --git a/internal/repositories/repositories.go b/internal/repositories/repositories.go index 34fae53..ada4dc5 100644 --- a/internal/repositories/repositories.go +++ b/internal/repositories/repositories.go @@ -3,10 +3,7 @@ package repositories import repositorymodels "github.com/pikami/cosmium/internal/repository_models" type DataRepository struct { - storedProcedures []repositorymodels.StoredProcedure - triggers []repositorymodels.Trigger - userDefinedFunctions []repositorymodels.UserDefinedFunction - storeState repositorymodels.State + storeState repositorymodels.State initialDataFilePath string persistDataFilePath string @@ -19,13 +16,13 @@ type RepositoryOptions struct { func NewDataRepository(options RepositoryOptions) *DataRepository { repository := &DataRepository{ - storedProcedures: []repositorymodels.StoredProcedure{}, - triggers: []repositorymodels.Trigger{}, - userDefinedFunctions: []repositorymodels.UserDefinedFunction{}, storeState: repositorymodels.State{ - Databases: make(map[string]repositorymodels.Database), - Collections: make(map[string]map[string]repositorymodels.Collection), - Documents: make(map[string]map[string]map[string]repositorymodels.Document), + Databases: make(map[string]repositorymodels.Database), + Collections: make(map[string]map[string]repositorymodels.Collection), + Documents: make(map[string]map[string]map[string]repositorymodels.Document), + Triggers: make(map[string]map[string]map[string]repositorymodels.Trigger), + StoredProcedures: make(map[string]map[string]map[string]repositorymodels.StoredProcedure), + UserDefinedFunctions: make(map[string]map[string]map[string]repositorymodels.UserDefinedFunction), }, initialDataFilePath: options.InitialDataFilePath, persistDataFilePath: options.PersistDataFilePath, diff --git a/internal/repositories/state.go b/internal/repositories/state.go index 79c5757..bd560c7 100644 --- a/internal/repositories/state.go +++ b/internal/repositories/state.go @@ -64,6 +64,9 @@ func (r *DataRepository) LoadStateJSON(jsonData string) error { logger.Infof("Databases: %d\n", getLength(r.storeState.Databases)) logger.Infof("Collections: %d\n", getLength(r.storeState.Collections)) logger.Infof("Documents: %d\n", getLength(r.storeState.Documents)) + logger.Infof("Triggers: %d\n", getLength(r.storeState.Triggers)) + logger.Infof("Stored procedures: %d\n", getLength(r.storeState.StoredProcedures)) + logger.Infof("User defined functions: %d\n", getLength(r.storeState.UserDefinedFunctions)) return nil } @@ -84,6 +87,9 @@ func (r *DataRepository) SaveStateFS(filePath string) { logger.Infof("Databases: %d\n", getLength(r.storeState.Databases)) logger.Infof("Collections: %d\n", getLength(r.storeState.Collections)) logger.Infof("Documents: %d\n", getLength(r.storeState.Documents)) + logger.Infof("Triggers: %d\n", getLength(r.storeState.Triggers)) + logger.Infof("Stored procedures: %d\n", getLength(r.storeState.StoredProcedures)) + logger.Infof("User defined functions: %d\n", getLength(r.storeState.UserDefinedFunctions)) } func (r *DataRepository) GetState() (string, error) { @@ -103,7 +109,10 @@ func getLength(v interface{}) int { switch v.(type) { case repositorymodels.Database, repositorymodels.Collection, - repositorymodels.Document: + repositorymodels.Document, + repositorymodels.Trigger, + repositorymodels.StoredProcedure, + repositorymodels.UserDefinedFunction: return 1 } @@ -137,6 +146,18 @@ func (r *DataRepository) ensureStoreStateNoNullReferences() { r.storeState.Documents = make(map[string]map[string]map[string]repositorymodels.Document) } + if r.storeState.Triggers == nil { + r.storeState.Triggers = make(map[string]map[string]map[string]repositorymodels.Trigger) + } + + if r.storeState.StoredProcedures == nil { + r.storeState.StoredProcedures = make(map[string]map[string]map[string]repositorymodels.StoredProcedure) + } + + if r.storeState.UserDefinedFunctions == nil { + r.storeState.UserDefinedFunctions = make(map[string]map[string]map[string]repositorymodels.UserDefinedFunction) + } + for database := range r.storeState.Databases { if r.storeState.Collections[database] == nil { r.storeState.Collections[database] = make(map[string]repositorymodels.Collection) @@ -146,6 +167,18 @@ func (r *DataRepository) ensureStoreStateNoNullReferences() { r.storeState.Documents[database] = make(map[string]map[string]repositorymodels.Document) } + if r.storeState.Triggers[database] == nil { + r.storeState.Triggers[database] = make(map[string]map[string]repositorymodels.Trigger) + } + + if r.storeState.StoredProcedures[database] == nil { + r.storeState.StoredProcedures[database] = make(map[string]map[string]repositorymodels.StoredProcedure) + } + + if r.storeState.UserDefinedFunctions[database] == nil { + r.storeState.UserDefinedFunctions[database] = make(map[string]map[string]repositorymodels.UserDefinedFunction) + } + for collection := range r.storeState.Collections[database] { if r.storeState.Documents[database][collection] == nil { r.storeState.Documents[database][collection] = make(map[string]repositorymodels.Document) @@ -156,6 +189,18 @@ func (r *DataRepository) ensureStoreStateNoNullReferences() { delete(r.storeState.Documents[database][collection], document) } } + + if r.storeState.Triggers[database][collection] == nil { + r.storeState.Triggers[database][collection] = make(map[string]repositorymodels.Trigger) + } + + if r.storeState.StoredProcedures[database][collection] == nil { + r.storeState.StoredProcedures[database][collection] = make(map[string]repositorymodels.StoredProcedure) + } + + if r.storeState.UserDefinedFunctions[database][collection] == nil { + r.storeState.UserDefinedFunctions[database][collection] = make(map[string]repositorymodels.UserDefinedFunction) + } } } } diff --git a/internal/repositories/stored_procedures.go b/internal/repositories/stored_procedures.go index 64d2e69..5cfd060 100644 --- a/internal/repositories/stored_procedures.go +++ b/internal/repositories/stored_procedures.go @@ -1,7 +1,91 @@ package repositories -import repositorymodels "github.com/pikami/cosmium/internal/repository_models" +import ( + "fmt" + "time" + + "github.com/google/uuid" + repositorymodels "github.com/pikami/cosmium/internal/repository_models" + "github.com/pikami/cosmium/internal/resourceid" + "golang.org/x/exp/maps" +) func (r *DataRepository) GetAllStoredProcedures(databaseId string, collectionId string) ([]repositorymodels.StoredProcedure, repositorymodels.RepositoryStatus) { - return r.storedProcedures, repositorymodels.StatusOk + r.storeState.RLock() + defer r.storeState.RUnlock() + + return maps.Values(r.storeState.StoredProcedures[databaseId][collectionId]), repositorymodels.StatusOk +} + +func (r *DataRepository) GetStoredProcedure(databaseId string, collectionId string, spId string) (repositorymodels.StoredProcedure, repositorymodels.RepositoryStatus) { + r.storeState.RLock() + defer r.storeState.RUnlock() + + if _, ok := r.storeState.Databases[databaseId]; !ok { + return repositorymodels.StoredProcedure{}, repositorymodels.StatusNotFound + } + + if _, ok := r.storeState.Collections[databaseId][collectionId]; !ok { + return repositorymodels.StoredProcedure{}, repositorymodels.StatusNotFound + } + + if sp, ok := r.storeState.StoredProcedures[databaseId][collectionId][spId]; ok { + return sp, repositorymodels.StatusOk + } + + return repositorymodels.StoredProcedure{}, repositorymodels.StatusNotFound +} + +func (r *DataRepository) DeleteStoredProcedure(databaseId string, collectionId string, spId string) repositorymodels.RepositoryStatus { + r.storeState.Lock() + defer r.storeState.Unlock() + + if _, ok := r.storeState.Databases[databaseId]; !ok { + return repositorymodels.StatusNotFound + } + + if _, ok := r.storeState.Collections[databaseId][collectionId]; !ok { + return repositorymodels.StatusNotFound + } + + if _, ok := r.storeState.StoredProcedures[databaseId][collectionId][spId]; !ok { + return repositorymodels.StatusNotFound + } + + delete(r.storeState.StoredProcedures[databaseId][collectionId], spId) + + return repositorymodels.StatusOk +} + +func (r *DataRepository) CreateStoredProcedure(databaseId string, collectionId string, sp repositorymodels.StoredProcedure) (repositorymodels.StoredProcedure, repositorymodels.RepositoryStatus) { + r.storeState.Lock() + defer r.storeState.Unlock() + + var ok bool + var database repositorymodels.Database + var collection repositorymodels.Collection + if sp.ID == "" { + return repositorymodels.StoredProcedure{}, repositorymodels.BadRequest + } + + if database, ok = r.storeState.Databases[databaseId]; !ok { + return repositorymodels.StoredProcedure{}, repositorymodels.StatusNotFound + } + + if collection, ok = r.storeState.Collections[databaseId][collectionId]; !ok { + return repositorymodels.StoredProcedure{}, repositorymodels.StatusNotFound + } + + if _, ok = r.storeState.StoredProcedures[databaseId][collectionId][sp.ID]; ok { + return repositorymodels.StoredProcedure{}, repositorymodels.Conflict + } + + sp.TimeStamp = time.Now().Unix() + sp.ResourceID = resourceid.NewCombined(database.ResourceID, collection.ResourceID, resourceid.New()) + sp.ETag = fmt.Sprintf("\"%s\"", uuid.New()) + sp.Self = fmt.Sprintf("dbs/%s/colls/%s/sprocs/%s/", database.ResourceID, collection.ResourceID, sp.ResourceID) + + r.storeState.StoredProcedures[databaseId][collectionId][sp.ID] = sp + + return sp, repositorymodels.StatusOk } diff --git a/internal/repositories/triggers.go b/internal/repositories/triggers.go index 4b0a75b..3ef6535 100644 --- a/internal/repositories/triggers.go +++ b/internal/repositories/triggers.go @@ -1,7 +1,91 @@ package repositories -import repositorymodels "github.com/pikami/cosmium/internal/repository_models" +import ( + "fmt" + "time" + + "github.com/google/uuid" + repositorymodels "github.com/pikami/cosmium/internal/repository_models" + "github.com/pikami/cosmium/internal/resourceid" + "golang.org/x/exp/maps" +) func (r *DataRepository) GetAllTriggers(databaseId string, collectionId string) ([]repositorymodels.Trigger, repositorymodels.RepositoryStatus) { - return r.triggers, repositorymodels.StatusOk + r.storeState.RLock() + defer r.storeState.RUnlock() + + return maps.Values(r.storeState.Triggers[databaseId][collectionId]), repositorymodels.StatusOk +} + +func (r *DataRepository) GetTrigger(databaseId string, collectionId string, triggerId string) (repositorymodels.Trigger, repositorymodels.RepositoryStatus) { + r.storeState.RLock() + defer r.storeState.RUnlock() + + if _, ok := r.storeState.Databases[databaseId]; !ok { + return repositorymodels.Trigger{}, repositorymodels.StatusNotFound + } + + if _, ok := r.storeState.Collections[databaseId][collectionId]; !ok { + return repositorymodels.Trigger{}, repositorymodels.StatusNotFound + } + + if trigger, ok := r.storeState.Triggers[databaseId][collectionId][triggerId]; ok { + return trigger, repositorymodels.StatusOk + } + + return repositorymodels.Trigger{}, repositorymodels.StatusNotFound +} + +func (r *DataRepository) DeleteTrigger(databaseId string, collectionId string, triggerId string) repositorymodels.RepositoryStatus { + r.storeState.Lock() + defer r.storeState.Unlock() + + if _, ok := r.storeState.Databases[databaseId]; !ok { + return repositorymodels.StatusNotFound + } + + if _, ok := r.storeState.Collections[databaseId][collectionId]; !ok { + return repositorymodels.StatusNotFound + } + + if _, ok := r.storeState.Triggers[databaseId][collectionId][triggerId]; !ok { + return repositorymodels.StatusNotFound + } + + delete(r.storeState.Triggers[databaseId][collectionId], triggerId) + + return repositorymodels.StatusOk +} + +func (r *DataRepository) CreateTrigger(databaseId string, collectionId string, trigger repositorymodels.Trigger) (repositorymodels.Trigger, repositorymodels.RepositoryStatus) { + r.storeState.Lock() + defer r.storeState.Unlock() + + var ok bool + var database repositorymodels.Database + var collection repositorymodels.Collection + if trigger.ID == "" { + return repositorymodels.Trigger{}, repositorymodels.BadRequest + } + + if database, ok = r.storeState.Databases[databaseId]; !ok { + return repositorymodels.Trigger{}, repositorymodels.StatusNotFound + } + + if collection, ok = r.storeState.Collections[databaseId][collectionId]; !ok { + return repositorymodels.Trigger{}, repositorymodels.StatusNotFound + } + + if _, ok = r.storeState.Triggers[databaseId][collectionId][trigger.ID]; ok { + return repositorymodels.Trigger{}, repositorymodels.Conflict + } + + trigger.TimeStamp = time.Now().Unix() + trigger.ResourceID = resourceid.NewCombined(database.ResourceID, collection.ResourceID, resourceid.New()) + trigger.ETag = fmt.Sprintf("\"%s\"", uuid.New()) + trigger.Self = fmt.Sprintf("dbs/%s/colls/%s/triggers/%s/", database.ResourceID, collection.ResourceID, trigger.ResourceID) + + r.storeState.Triggers[databaseId][collectionId][trigger.ID] = trigger + + return trigger, repositorymodels.StatusOk } diff --git a/internal/repositories/user_defined_functions.go b/internal/repositories/user_defined_functions.go index 3bc6ca7..aee22b4 100644 --- a/internal/repositories/user_defined_functions.go +++ b/internal/repositories/user_defined_functions.go @@ -1,7 +1,91 @@ package repositories -import repositorymodels "github.com/pikami/cosmium/internal/repository_models" +import ( + "fmt" + "time" + + "github.com/google/uuid" + repositorymodels "github.com/pikami/cosmium/internal/repository_models" + "github.com/pikami/cosmium/internal/resourceid" + "golang.org/x/exp/maps" +) func (r *DataRepository) GetAllUserDefinedFunctions(databaseId string, collectionId string) ([]repositorymodels.UserDefinedFunction, repositorymodels.RepositoryStatus) { - return r.userDefinedFunctions, repositorymodels.StatusOk + r.storeState.RLock() + defer r.storeState.RUnlock() + + return maps.Values(r.storeState.UserDefinedFunctions[databaseId][collectionId]), repositorymodels.StatusOk +} + +func (r *DataRepository) GetUserDefinedFunction(databaseId string, collectionId string, udfId string) (repositorymodels.UserDefinedFunction, repositorymodels.RepositoryStatus) { + r.storeState.RLock() + defer r.storeState.RUnlock() + + if _, ok := r.storeState.Databases[databaseId]; !ok { + return repositorymodels.UserDefinedFunction{}, repositorymodels.StatusNotFound + } + + if _, ok := r.storeState.Collections[databaseId][collectionId]; !ok { + return repositorymodels.UserDefinedFunction{}, repositorymodels.StatusNotFound + } + + if udf, ok := r.storeState.UserDefinedFunctions[databaseId][collectionId][udfId]; ok { + return udf, repositorymodels.StatusOk + } + + return repositorymodels.UserDefinedFunction{}, repositorymodels.StatusNotFound +} + +func (r *DataRepository) DeleteUserDefinedFunction(databaseId string, collectionId string, udfId string) repositorymodels.RepositoryStatus { + r.storeState.Lock() + defer r.storeState.Unlock() + + if _, ok := r.storeState.Databases[databaseId]; !ok { + return repositorymodels.StatusNotFound + } + + if _, ok := r.storeState.Collections[databaseId][collectionId]; !ok { + return repositorymodels.StatusNotFound + } + + if _, ok := r.storeState.UserDefinedFunctions[databaseId][collectionId][udfId]; !ok { + return repositorymodels.StatusNotFound + } + + delete(r.storeState.UserDefinedFunctions[databaseId][collectionId], udfId) + + return repositorymodels.StatusOk +} + +func (r *DataRepository) CreateUserDefinedFunction(databaseId string, collectionId string, udf repositorymodels.UserDefinedFunction) (repositorymodels.UserDefinedFunction, repositorymodels.RepositoryStatus) { + r.storeState.Lock() + defer r.storeState.Unlock() + + var ok bool + var database repositorymodels.Database + var collection repositorymodels.Collection + if udf.ID == "" { + return repositorymodels.UserDefinedFunction{}, repositorymodels.BadRequest + } + + if database, ok = r.storeState.Databases[databaseId]; !ok { + return repositorymodels.UserDefinedFunction{}, repositorymodels.StatusNotFound + } + + if collection, ok = r.storeState.Collections[databaseId][collectionId]; !ok { + return repositorymodels.UserDefinedFunction{}, repositorymodels.StatusNotFound + } + + if _, ok := r.storeState.UserDefinedFunctions[databaseId][collectionId][udf.ID]; ok { + return repositorymodels.UserDefinedFunction{}, repositorymodels.Conflict + } + + udf.TimeStamp = time.Now().Unix() + udf.ResourceID = resourceid.NewCombined(database.ResourceID, collection.ResourceID, resourceid.New()) + udf.ETag = fmt.Sprintf("\"%s\"", uuid.New()) + udf.Self = fmt.Sprintf("dbs/%s/colls/%s/udfs/%s/", database.ResourceID, collection.ResourceID, udf.ResourceID) + + r.storeState.UserDefinedFunctions[databaseId][collectionId][udf.ID] = udf + + return udf, repositorymodels.StatusOk } diff --git a/internal/repository_models/models.go b/internal/repository_models/models.go index 490cf52..8762ff2 100644 --- a/internal/repository_models/models.go +++ b/internal/repository_models/models.go @@ -19,6 +19,22 @@ const ( BadRequest = 4 ) +type TriggerOperation string + +const ( + All TriggerOperation = "All" + Create TriggerOperation = "Create" + Delete TriggerOperation = "Delete" + Replace TriggerOperation = "Replace" +) + +type TriggerType string + +const ( + Pre TriggerType = "Pre" + Post TriggerType = "Post" +) + type Collection struct { ID string `json:"id"` IndexingPolicy CollectionIndexingPolicy `json:"indexingPolicy"` @@ -60,29 +76,29 @@ type UserDefinedFunction struct { Body string `json:"body"` ID string `json:"id"` ResourceID string `json:"_rid"` - TimeStamp int `json:"_ts"` + TimeStamp int64 `json:"_ts"` Self string `json:"_self"` - Etag string `json:"_etag"` + ETag string `json:"_etag"` } type StoredProcedure struct { Body string `json:"body"` ID string `json:"id"` ResourceID string `json:"_rid"` - TimeStamp int `json:"_ts"` + TimeStamp int64 `json:"_ts"` Self string `json:"_self"` - Etag string `json:"_etag"` + ETag string `json:"_etag"` } type Trigger struct { - Body string `json:"body"` - ID string `json:"id"` - TriggerOperation string `json:"triggerOperation"` - TriggerType string `json:"triggerType"` - ResourceID string `json:"_rid"` - TimeStamp int `json:"_ts"` - Self string `json:"_self"` - Etag string `json:"_etag"` + Body string `json:"body"` + ID string `json:"id"` + TriggerOperation TriggerOperation `json:"triggerOperation"` + TriggerType TriggerType `json:"triggerType"` + ResourceID string `json:"_rid"` + TimeStamp int64 `json:"_ts"` + Self string `json:"_self"` + ETag string `json:"_etag"` } type Document map[string]interface{} @@ -113,4 +129,13 @@ type State struct { // Map databaseId -> collectionId -> documentId -> Documents Documents map[string]map[string]map[string]Document `json:"documents"` + + // Map databaseId -> collectionId -> triggerId -> Trigger + Triggers map[string]map[string]map[string]Trigger `json:"triggers"` + + // Map databaseId -> collectionId -> spId -> StoredProcedure + StoredProcedures map[string]map[string]map[string]StoredProcedure `json:"sprocs"` + + // Map databaseId -> collectionId -> udfId -> UserDefinedFunction + UserDefinedFunctions map[string]map[string]map[string]UserDefinedFunction `json:"udfs"` }