mirror of
https://github.com/pikami/cosmium.git
synced 2026-04-21 05:48:51 +01:00
Compare commits
9 Commits
363f822e5a
...
v0.1.13
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b8b087aab | ||
|
|
c2c9dc03b3 | ||
|
|
d86bac7d79 | ||
|
|
69b76c1c3e | ||
|
|
8e3db3e44d | ||
|
|
f5b8453995 | ||
|
|
928ca29fe4 | ||
|
|
39cd9e2357 | ||
|
|
bcf4b513b6 |
@@ -78,7 +78,7 @@ To disable SSL and run Cosmium on HTTP instead, you can use the `-DisableTls` fl
|
|||||||
- **-InitialData**: Path to JSON containing initial state
|
- **-InitialData**: Path to JSON containing initial state
|
||||||
- **-Persist**: Saves data to the given path on application exit (When `-InitialData` argument is not supplied, it will try to load data from path supplied in `-Persist`)
|
- **-Persist**: Saves data to the given path on application exit (When `-InitialData` argument is not supplied, it will try to load data from path supplied in `-Persist`)
|
||||||
- **-Port**: Listen port (default 8081)
|
- **-Port**: Listen port (default 8081)
|
||||||
- **-Debug**: Runs application in debug mode, this provides additional logging
|
- **-LogLevel**: Sets the logging level (one of: debug, info, error, silent) (default info)
|
||||||
|
|
||||||
These arguments allow you to configure various aspects of Cosmium's behavior according to your requirements.
|
These arguments allow you to configure various aspects of Cosmium's behavior according to your requirements.
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ All mentioned arguments can also be set using environment variables:
|
|||||||
- **COSMIUM_INITIALDATA** for `-InitialData`
|
- **COSMIUM_INITIALDATA** for `-InitialData`
|
||||||
- **COSMIUM_PERSIST** for `-Persist`
|
- **COSMIUM_PERSIST** for `-Persist`
|
||||||
- **COSMIUM_PORT** for `-Port`
|
- **COSMIUM_PORT** for `-Port`
|
||||||
- **COSMIUM_DEBUG** for `-Debug`
|
- **COSMIUM_LOGLEVEL** for `-LogLevel`
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ func ParseFlags() ServerConfig {
|
|||||||
disableAuthentication := flag.Bool("DisableAuth", false, "Disable authentication")
|
disableAuthentication := flag.Bool("DisableAuth", false, "Disable authentication")
|
||||||
disableTls := flag.Bool("DisableTls", false, "Disable TLS, serve over HTTP")
|
disableTls := flag.Bool("DisableTls", false, "Disable TLS, serve over HTTP")
|
||||||
persistDataPath := flag.String("Persist", "", "Saves data to given path on application exit")
|
persistDataPath := flag.String("Persist", "", "Saves data to given path on application exit")
|
||||||
debug := flag.Bool("Debug", false, "Runs application in debug mode, this provides additional logging")
|
logLevel := NewEnumValue("info", []string{"debug", "info", "error", "silent"})
|
||||||
|
flag.Var(logLevel, "LogLevel", fmt.Sprintf("Sets the logging level %s", logLevel.AllowedValuesList()))
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
setFlagsFromEnvironment()
|
setFlagsFromEnvironment()
|
||||||
@@ -41,8 +42,8 @@ func ParseFlags() ServerConfig {
|
|||||||
config.PersistDataFilePath = *persistDataPath
|
config.PersistDataFilePath = *persistDataPath
|
||||||
config.DisableAuth = *disableAuthentication
|
config.DisableAuth = *disableAuthentication
|
||||||
config.DisableTls = *disableTls
|
config.DisableTls = *disableTls
|
||||||
config.Debug = *debug
|
|
||||||
config.AccountKey = *accountKey
|
config.AccountKey = *accountKey
|
||||||
|
config.LogLevel = logLevel.value
|
||||||
|
|
||||||
config.PopulateCalculatedFields()
|
config.PopulateCalculatedFields()
|
||||||
|
|
||||||
@@ -54,7 +55,19 @@ func (c *ServerConfig) PopulateCalculatedFields() {
|
|||||||
c.DatabaseDomain = c.Host
|
c.DatabaseDomain = c.Host
|
||||||
c.DatabaseEndpoint = fmt.Sprintf("https://%s:%d/", c.Host, c.Port)
|
c.DatabaseEndpoint = fmt.Sprintf("https://%s:%d/", c.Host, c.Port)
|
||||||
c.ExplorerBaseUrlLocation = ExplorerBaseUrlLocation
|
c.ExplorerBaseUrlLocation = ExplorerBaseUrlLocation
|
||||||
logger.EnableDebugOutput = c.Debug
|
|
||||||
|
switch c.LogLevel {
|
||||||
|
case "debug":
|
||||||
|
logger.LogLevel = logger.LogLevelDebug
|
||||||
|
case "info":
|
||||||
|
logger.LogLevel = logger.LogLevelInfo
|
||||||
|
case "error":
|
||||||
|
logger.LogLevel = logger.LogLevelError
|
||||||
|
case "silent":
|
||||||
|
logger.LogLevel = logger.LogLevelSilent
|
||||||
|
default:
|
||||||
|
logger.LogLevel = logger.LogLevelInfo
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ServerConfig) ApplyDefaultsToEmptyFields() {
|
func (c *ServerConfig) ApplyDefaultsToEmptyFields() {
|
||||||
|
|||||||
36
api/config/enumFlag.go
Normal file
36
api/config/enumFlag.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EnumValue struct {
|
||||||
|
allowedValues []string
|
||||||
|
value string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EnumValue) String() string {
|
||||||
|
return e.value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EnumValue) Set(v string) error {
|
||||||
|
for _, allowed := range e.allowedValues {
|
||||||
|
if v == allowed {
|
||||||
|
e.value = v
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("invalid value %q, must be one of: %s", v, strings.Join(e.allowedValues, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEnumValue(defaultValue string, allowedValues []string) *EnumValue {
|
||||||
|
return &EnumValue{
|
||||||
|
allowedValues: allowedValues,
|
||||||
|
value: defaultValue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EnumValue) AllowedValuesList() string {
|
||||||
|
return fmt.Sprintf("(one of: %s)", strings.Join(e.allowedValues, ", "))
|
||||||
|
}
|
||||||
@@ -15,6 +15,6 @@ type ServerConfig struct {
|
|||||||
PersistDataFilePath string `json:"persistDataFilePath"`
|
PersistDataFilePath string `json:"persistDataFilePath"`
|
||||||
DisableAuth bool `json:"disableAuth"`
|
DisableAuth bool `json:"disableAuth"`
|
||||||
DisableTls bool `json:"disableTls"`
|
DisableTls bool `json:"disableTls"`
|
||||||
Debug bool `json:"debug"`
|
LogLevel string `json:"logLevel"`
|
||||||
ExplorerBaseUrlLocation string `json:"explorerBaseUrlLocation"`
|
ExplorerBaseUrlLocation string `json:"explorerBaseUrlLocation"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
jsonpatch "github.com/evanphx/json-patch/v5"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/pikami/cosmium/internal/constants"
|
"github.com/pikami/cosmium/internal/constants"
|
||||||
"github.com/pikami/cosmium/internal/logger"
|
"github.com/pikami/cosmium/internal/logger"
|
||||||
repositorymodels "github.com/pikami/cosmium/internal/repository_models"
|
repositorymodels "github.com/pikami/cosmium/internal/repository_models"
|
||||||
|
jsonpatch "github.com/pikami/json-patch/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (h *Handlers) GetAllDocuments(c *gin.Context) {
|
func (h *Handlers) GetAllDocuments(c *gin.Context) {
|
||||||
@@ -135,7 +135,7 @@ func (h *Handlers) PatchDocument(c *gin.Context) {
|
|||||||
|
|
||||||
currentDocumentBytes, err := json.Marshal(document)
|
currentDocumentBytes, err := json.Marshal(document)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Failed to marshal existing document:", err)
|
logger.ErrorLn("Failed to marshal existing document:", err)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to marshal existing document"})
|
c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to marshal existing document"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -149,7 +149,7 @@ func (h *Handlers) PatchDocument(c *gin.Context) {
|
|||||||
var modifiedDocument map[string]interface{}
|
var modifiedDocument map[string]interface{}
|
||||||
err = json.Unmarshal(modifiedDocumentBytes, &modifiedDocument)
|
err = json.Unmarshal(modifiedDocumentBytes, &modifiedDocument)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Failed to unmarshal modified document:", err)
|
logger.ErrorLn("Failed to unmarshal modified document:", err)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to unmarshal modified document"})
|
c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to unmarshal modified document"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,8 @@ func requestToResourceId(c *gin.Context) string {
|
|||||||
|
|
||||||
isFeed := c.Request.Header.Get("A-Im") == "Incremental Feed"
|
isFeed := c.Request.Header.Get("A-Im") == "Incremental Feed"
|
||||||
if resourceType == "pkranges" && isFeed {
|
if resourceType == "pkranges" && isFeed {
|
||||||
resourceId = collId
|
// CosmosSDK replaces '/' with '-' in resource id requests
|
||||||
|
resourceId = strings.Replace(collId, "-", "/", -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
return resourceId
|
return resourceId
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func RequestLogger() gin.HandlerFunc {
|
|||||||
|
|
||||||
bodyStr := readBody(rdr1)
|
bodyStr := readBody(rdr1)
|
||||||
if bodyStr != "" {
|
if bodyStr != "" {
|
||||||
logger.Debug(bodyStr)
|
logger.DebugLn(bodyStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Request.Body = rdr2
|
c.Request.Body = rdr2
|
||||||
|
|||||||
@@ -22,3 +22,97 @@ func (h *Handlers) GetAllStoredProcedures(c *gin.Context) {
|
|||||||
|
|
||||||
c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"})
|
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"})
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,3 +22,97 @@ func (h *Handlers) GetAllTriggers(c *gin.Context) {
|
|||||||
|
|
||||||
c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"})
|
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"})
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,3 +22,97 @@ func (h *Handlers) GetAllUserDefinedFunctions(c *gin.Context) {
|
|||||||
|
|
||||||
c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unknown error"})
|
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"})
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,7 +16,10 @@ import (
|
|||||||
func (s *ApiServer) CreateRouter(repository *repositories.DataRepository) {
|
func (s *ApiServer) CreateRouter(repository *repositories.DataRepository) {
|
||||||
routeHandlers := handlers.NewHandlers(repository, s.config)
|
routeHandlers := handlers.NewHandlers(repository, s.config)
|
||||||
|
|
||||||
if !s.config.Debug {
|
gin.DefaultWriter = logger.InfoWriter()
|
||||||
|
gin.DefaultErrorWriter = logger.ErrorWriter()
|
||||||
|
|
||||||
|
if s.config.LogLevel != "debug" {
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,7 +27,7 @@ func (s *ApiServer) CreateRouter(repository *repositories.DataRepository) {
|
|||||||
e.RedirectTrailingSlash = false
|
e.RedirectTrailingSlash = false
|
||||||
})
|
})
|
||||||
|
|
||||||
if s.config.Debug {
|
if s.config.LogLevel == "debug" {
|
||||||
router.Use(middleware.RequestLogger())
|
router.Use(middleware.RequestLogger())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,9 +53,23 @@ func (s *ApiServer) CreateRouter(repository *repositories.DataRepository) {
|
|||||||
router.GET("/dbs/:databaseId", routeHandlers.GetDatabase)
|
router.GET("/dbs/:databaseId", routeHandlers.GetDatabase)
|
||||||
router.DELETE("/dbs/:databaseId", routeHandlers.DeleteDatabase)
|
router.DELETE("/dbs/:databaseId", routeHandlers.DeleteDatabase)
|
||||||
|
|
||||||
router.GET("/dbs/:databaseId/colls/:collId/udfs", routeHandlers.GetAllUserDefinedFunctions)
|
router.POST("/dbs/:databaseId/colls/:collId/triggers", routeHandlers.CreateTrigger)
|
||||||
router.GET("/dbs/:databaseId/colls/:collId/sprocs", routeHandlers.GetAllStoredProcedures)
|
|
||||||
router.GET("/dbs/:databaseId/colls/:collId/triggers", routeHandlers.GetAllTriggers)
|
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("/offers", handlers.GetOffers)
|
||||||
router.GET("/", routeHandlers.GetServerInfo)
|
router.GET("/", routeHandlers.GetServerInfo)
|
||||||
@@ -75,10 +92,10 @@ func (s *ApiServer) Start() {
|
|||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
<-s.stopServer
|
<-s.stopServer
|
||||||
logger.Info("Shutting down server...")
|
logger.InfoLn("Shutting down server...")
|
||||||
err := server.Shutdown(context.TODO())
|
err := server.Shutdown(context.TODO())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Failed to shutdown server:", err)
|
logger.ErrorLn("Failed to shutdown server:", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -87,7 +104,7 @@ func (s *ApiServer) Start() {
|
|||||||
logger.Infof("Listening and serving HTTP on %s\n", server.Addr)
|
logger.Infof("Listening and serving HTTP on %s\n", server.Addr)
|
||||||
err := server.ListenAndServe()
|
err := server.ListenAndServe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Failed to start HTTP server:", err)
|
logger.ErrorLn("Failed to start HTTP server:", err)
|
||||||
}
|
}
|
||||||
s.isActive = false
|
s.isActive = false
|
||||||
} else if s.config.TLS_CertificatePath != "" && s.config.TLS_CertificateKey != "" {
|
} else if s.config.TLS_CertificatePath != "" && s.config.TLS_CertificateKey != "" {
|
||||||
@@ -96,7 +113,7 @@ func (s *ApiServer) Start() {
|
|||||||
s.config.TLS_CertificatePath,
|
s.config.TLS_CertificatePath,
|
||||||
s.config.TLS_CertificateKey)
|
s.config.TLS_CertificateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Failed to start HTTPS server:", err)
|
logger.ErrorLn("Failed to start HTTPS server:", err)
|
||||||
}
|
}
|
||||||
s.isActive = false
|
s.isActive = false
|
||||||
} else {
|
} else {
|
||||||
@@ -106,7 +123,7 @@ func (s *ApiServer) Start() {
|
|||||||
logger.Infof("Listening and serving HTTPS on %s\n", server.Addr)
|
logger.Infof("Listening and serving HTTPS on %s\n", server.Addr)
|
||||||
err := server.ListenAndServeTLS("", "")
|
err := server.ListenAndServeTLS("", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Failed to start HTTPS server:", err)
|
logger.ErrorLn("Failed to start HTTPS server:", err)
|
||||||
}
|
}
|
||||||
s.isActive = false
|
s.isActive = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -147,6 +147,21 @@ func Test_Documents(t *testing.T) {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Should query document with query parameters as accessor", func(t *testing.T) {
|
||||||
|
testCosmosQuery(t, collectionClient,
|
||||||
|
`select c.id
|
||||||
|
FROM c
|
||||||
|
WHERE c[@param]="67890"
|
||||||
|
ORDER BY c.id`,
|
||||||
|
[]azcosmos.QueryParameter{
|
||||||
|
{Name: "@param", Value: "id"},
|
||||||
|
},
|
||||||
|
[]interface{}{
|
||||||
|
map[string]interface{}{"id": "67890"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("Should query array accessor", func(t *testing.T) {
|
t.Run("Should query array accessor", func(t *testing.T) {
|
||||||
testCosmosQuery(t, collectionClient,
|
testCosmosQuery(t, collectionClient,
|
||||||
`SELECT c.id,
|
`SELECT c.id,
|
||||||
@@ -220,11 +235,14 @@ func Test_Documents_Patch(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("Should PATCH document", func(t *testing.T) {
|
t.Run("Should PATCH document", func(t *testing.T) {
|
||||||
context := context.TODO()
|
context := context.TODO()
|
||||||
expectedData := map[string]interface{}{"id": "67890", "pk": "456", "newField": "newValue"}
|
expectedData := map[string]interface{}{"id": "67890", "pk": "666", "newField": "newValue", "incr": 15., "setted": "isSet"}
|
||||||
|
|
||||||
patch := azcosmos.PatchOperations{}
|
patch := azcosmos.PatchOperations{}
|
||||||
patch.AppendAdd("/newField", "newValue")
|
patch.AppendAdd("/newField", "newValue")
|
||||||
|
patch.AppendIncrement("/incr", 15)
|
||||||
patch.AppendRemove("/isCool")
|
patch.AppendRemove("/isCool")
|
||||||
|
patch.AppendReplace("/pk", "666")
|
||||||
|
patch.AppendSet("/setted", "isSet")
|
||||||
|
|
||||||
itemResponse, err := collectionClient.PatchItem(
|
itemResponse, err := collectionClient.PatchItem(
|
||||||
context,
|
context,
|
||||||
@@ -237,13 +255,15 @@ func Test_Documents_Patch(t *testing.T) {
|
|||||||
)
|
)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
var itemResponseBody map[string]string
|
var itemResponseBody map[string]interface{}
|
||||||
json.Unmarshal(itemResponse.Value, &itemResponseBody)
|
json.Unmarshal(itemResponse.Value, &itemResponseBody)
|
||||||
|
|
||||||
assert.Equal(t, expectedData["id"], itemResponseBody["id"])
|
assert.Equal(t, expectedData["id"], itemResponseBody["id"])
|
||||||
assert.Equal(t, expectedData["pk"], itemResponseBody["pk"])
|
assert.Equal(t, expectedData["pk"], itemResponseBody["pk"])
|
||||||
assert.Empty(t, itemResponseBody["isCool"])
|
assert.Empty(t, itemResponseBody["isCool"])
|
||||||
assert.Equal(t, expectedData["newField"], itemResponseBody["newField"])
|
assert.Equal(t, expectedData["newField"], itemResponseBody["newField"])
|
||||||
|
assert.Equal(t, expectedData["incr"], itemResponseBody["incr"])
|
||||||
|
assert.Equal(t, expectedData["setted"], itemResponseBody["setted"])
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Should not allow to PATCH document ID", func(t *testing.T) {
|
t.Run("Should not allow to PATCH document ID", func(t *testing.T) {
|
||||||
|
|||||||
@@ -65,9 +65,9 @@ Cosmium strives to support the core features of Cosmos DB, including:
|
|||||||
| Function | Implemented |
|
| Function | Implemented |
|
||||||
| ------------------ | ----------- |
|
| ------------------ | ----------- |
|
||||||
| ARRAY_CONCAT | Yes |
|
| ARRAY_CONCAT | Yes |
|
||||||
| ARRAY_CONTAINS | No |
|
| ARRAY_CONTAINS | Yes |
|
||||||
| ARRAY_CONTAINS_ANY | No |
|
| ARRAY_CONTAINS_ANY | Yes |
|
||||||
| ARRAY_CONTAINS_ALL | No |
|
| ARRAY_CONTAINS_ALL | Yes |
|
||||||
| ARRAY_LENGTH | Yes |
|
| ARRAY_LENGTH | Yes |
|
||||||
| ARRAY_SLICE | Yes |
|
| ARRAY_SLICE | Yes |
|
||||||
| CHOOSE | No |
|
| CHOOSE | No |
|
||||||
|
|||||||
4
go.mod
4
go.mod
@@ -5,9 +5,9 @@ go 1.22.0
|
|||||||
require (
|
require (
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos v0.3.6
|
github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos v0.3.6
|
||||||
github.com/evanphx/json-patch/v5 v5.9.0
|
|
||||||
github.com/gin-gonic/gin v1.10.0
|
github.com/gin-gonic/gin v1.10.0
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
|
github.com/pikami/json-patch/v5 v5.9.2
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67
|
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67
|
||||||
)
|
)
|
||||||
@@ -39,7 +39,7 @@ require (
|
|||||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||||
golang.org/x/arch v0.12.0 // indirect
|
golang.org/x/arch v0.12.0 // indirect
|
||||||
golang.org/x/crypto v0.31.0 // indirect
|
golang.org/x/crypto v0.31.0 // indirect
|
||||||
golang.org/x/net v0.32.0 // indirect
|
golang.org/x/net v0.33.0 // indirect
|
||||||
golang.org/x/sys v0.28.0 // indirect
|
golang.org/x/sys v0.28.0 // indirect
|
||||||
golang.org/x/text v0.21.0 // indirect
|
golang.org/x/text v0.21.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.0 // indirect
|
google.golang.org/protobuf v1.36.0 // indirect
|
||||||
|
|||||||
8
go.sum
8
go.sum
@@ -22,8 +22,6 @@ github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQ
|
|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
|
|
||||||
github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
|
|
||||||
github.com/gabriel-vasile/mimetype v1.4.7 h1:SKFKl7kD0RiPdbht0s7hFtjl489WcQ1VyPW8ZzUMYCA=
|
github.com/gabriel-vasile/mimetype v1.4.7 h1:SKFKl7kD0RiPdbht0s7hFtjl489WcQ1VyPW8ZzUMYCA=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.7/go.mod h1:GDlAgAyIRT27BhFl53XNAFtfjzOkLaF35JdEG0P7LtU=
|
github.com/gabriel-vasile/mimetype v1.4.7/go.mod h1:GDlAgAyIRT27BhFl53XNAFtfjzOkLaF35JdEG0P7LtU=
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
@@ -66,6 +64,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
|||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||||
|
github.com/pikami/json-patch/v5 v5.9.2 h1:ciTlocWccYVE3DEa45dsMm02c/tOvcaBY7PpEUNZhrU=
|
||||||
|
github.com/pikami/json-patch/v5 v5.9.2/go.mod h1:eJIScZ4xgf2aBHLi2UMzYtjlWESUBDOBf7EAx3JW0nI=
|
||||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI=
|
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI=
|
||||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
|
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
@@ -92,8 +92,8 @@ golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
|||||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||||
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo=
|
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo=
|
||||||
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
||||||
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
|
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||||
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
|
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
|||||||
@@ -5,36 +5,100 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
var EnableDebugOutput = false
|
type LogLevelType int
|
||||||
|
|
||||||
|
var (
|
||||||
|
LogLevelDebug LogLevelType = 0
|
||||||
|
LogLevelInfo LogLevelType = 1
|
||||||
|
LogLevelError LogLevelType = 2
|
||||||
|
LogLevelSilent LogLevelType = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
type LogWriter struct {
|
||||||
|
WriterLevel LogLevelType
|
||||||
|
}
|
||||||
|
|
||||||
|
var LogLevel = LogLevelInfo
|
||||||
|
|
||||||
var DebugLogger = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile)
|
var DebugLogger = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile)
|
||||||
var InfoLogger = log.New(os.Stdout, "", log.Ldate|log.Ltime)
|
var InfoLogger = log.New(os.Stdout, "", log.Ldate|log.Ltime)
|
||||||
var ErrorLogger = log.New(os.Stderr, "", log.Ldate|log.Ltime|log.Lshortfile)
|
var ErrorLogger = log.New(os.Stderr, "", log.Ldate|log.Ltime|log.Lshortfile)
|
||||||
|
|
||||||
func Debug(v ...any) {
|
func DebugLn(v ...any) {
|
||||||
if EnableDebugOutput {
|
if LogLevel <= LogLevelDebug {
|
||||||
DebugLogger.Println(v...)
|
DebugLogger.Println(v...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Debug(v ...any) {
|
||||||
|
if LogLevel <= LogLevelDebug {
|
||||||
|
DebugLogger.Print(v...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Debugf(format string, v ...any) {
|
func Debugf(format string, v ...any) {
|
||||||
if EnableDebugOutput {
|
if LogLevel <= LogLevelDebug {
|
||||||
DebugLogger.Printf(format, v...)
|
DebugLogger.Printf(format, v...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Info(v ...any) {
|
func InfoLn(v ...any) {
|
||||||
|
if LogLevel <= LogLevelInfo {
|
||||||
InfoLogger.Println(v...)
|
InfoLogger.Println(v...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Info(v ...any) {
|
||||||
|
if LogLevel <= LogLevelInfo {
|
||||||
|
InfoLogger.Print(v...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Infof(format string, v ...any) {
|
func Infof(format string, v ...any) {
|
||||||
|
if LogLevel <= LogLevelInfo {
|
||||||
InfoLogger.Printf(format, v...)
|
InfoLogger.Printf(format, v...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrorLn(v ...any) {
|
||||||
|
if LogLevel <= LogLevelError {
|
||||||
|
ErrorLogger.Println(v...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Error(v ...any) {
|
func Error(v ...any) {
|
||||||
ErrorLogger.Println(v...)
|
if LogLevel <= LogLevelError {
|
||||||
|
ErrorLogger.Print(v...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Errorf(format string, v ...any) {
|
func Errorf(format string, v ...any) {
|
||||||
|
if LogLevel <= LogLevelError {
|
||||||
ErrorLogger.Printf(format, v...)
|
ErrorLogger.Printf(format, v...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lw *LogWriter) Write(p []byte) (n int, err error) {
|
||||||
|
switch lw.WriterLevel {
|
||||||
|
case LogLevelDebug:
|
||||||
|
Debug(string(p))
|
||||||
|
case LogLevelInfo:
|
||||||
|
Info(string(p))
|
||||||
|
case LogLevelError:
|
||||||
|
Error(string(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrorWriter() *LogWriter {
|
||||||
|
return &LogWriter{WriterLevel: LogLevelError}
|
||||||
|
}
|
||||||
|
|
||||||
|
func InfoWriter() *LogWriter {
|
||||||
|
return &LogWriter{WriterLevel: LogLevelInfo}
|
||||||
|
}
|
||||||
|
|
||||||
|
func DebugWriter() *LogWriter {
|
||||||
|
return &LogWriter{WriterLevel: LogLevelDebug}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,6 +77,9 @@ func (r *DataRepository) CreateCollection(databaseId string, newCollection repos
|
|||||||
|
|
||||||
r.storeState.Collections[databaseId][newCollection.ID] = newCollection
|
r.storeState.Collections[databaseId][newCollection.ID] = newCollection
|
||||||
r.storeState.Documents[databaseId][newCollection.ID] = make(map[string]repositorymodels.Document)
|
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
|
return newCollection, repositorymodels.StatusOk
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,9 @@ func (r *DataRepository) CreateDatabase(newDatabase repositorymodels.Database) (
|
|||||||
r.storeState.Databases[newDatabase.ID] = newDatabase
|
r.storeState.Databases[newDatabase.ID] = newDatabase
|
||||||
r.storeState.Collections[newDatabase.ID] = make(map[string]repositorymodels.Collection)
|
r.storeState.Collections[newDatabase.ID] = make(map[string]repositorymodels.Collection)
|
||||||
r.storeState.Documents[newDatabase.ID] = make(map[string]map[string]repositorymodels.Document)
|
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
|
return newDatabase, repositorymodels.StatusOk
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,6 @@ package repositories
|
|||||||
import repositorymodels "github.com/pikami/cosmium/internal/repository_models"
|
import repositorymodels "github.com/pikami/cosmium/internal/repository_models"
|
||||||
|
|
||||||
type DataRepository struct {
|
type DataRepository struct {
|
||||||
storedProcedures []repositorymodels.StoredProcedure
|
|
||||||
triggers []repositorymodels.Trigger
|
|
||||||
userDefinedFunctions []repositorymodels.UserDefinedFunction
|
|
||||||
storeState repositorymodels.State
|
storeState repositorymodels.State
|
||||||
|
|
||||||
initialDataFilePath string
|
initialDataFilePath string
|
||||||
@@ -19,13 +16,13 @@ type RepositoryOptions struct {
|
|||||||
|
|
||||||
func NewDataRepository(options RepositoryOptions) *DataRepository {
|
func NewDataRepository(options RepositoryOptions) *DataRepository {
|
||||||
repository := &DataRepository{
|
repository := &DataRepository{
|
||||||
storedProcedures: []repositorymodels.StoredProcedure{},
|
|
||||||
triggers: []repositorymodels.Trigger{},
|
|
||||||
userDefinedFunctions: []repositorymodels.UserDefinedFunction{},
|
|
||||||
storeState: repositorymodels.State{
|
storeState: repositorymodels.State{
|
||||||
Databases: make(map[string]repositorymodels.Database),
|
Databases: make(map[string]repositorymodels.Database),
|
||||||
Collections: make(map[string]map[string]repositorymodels.Collection),
|
Collections: make(map[string]map[string]repositorymodels.Collection),
|
||||||
Documents: make(map[string]map[string]map[string]repositorymodels.Document),
|
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,
|
initialDataFilePath: options.InitialDataFilePath,
|
||||||
persistDataFilePath: options.PersistDataFilePath,
|
persistDataFilePath: options.PersistDataFilePath,
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ func (r *DataRepository) InitializeRepository() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if stat.IsDir() {
|
if stat.IsDir() {
|
||||||
logger.Error("Argument '-Persist' must be a path to file, not a directory.")
|
logger.ErrorLn("Argument '-Persist' must be a path to file, not a directory.")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,10 +60,13 @@ func (r *DataRepository) LoadStateJSON(jsonData string) error {
|
|||||||
|
|
||||||
r.ensureStoreStateNoNullReferences()
|
r.ensureStoreStateNoNullReferences()
|
||||||
|
|
||||||
logger.Info("Loaded state:")
|
logger.InfoLn("Loaded state:")
|
||||||
logger.Infof("Databases: %d\n", getLength(r.storeState.Databases))
|
logger.Infof("Databases: %d\n", getLength(r.storeState.Databases))
|
||||||
logger.Infof("Collections: %d\n", getLength(r.storeState.Collections))
|
logger.Infof("Collections: %d\n", getLength(r.storeState.Collections))
|
||||||
logger.Infof("Documents: %d\n", getLength(r.storeState.Documents))
|
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
|
return nil
|
||||||
}
|
}
|
||||||
@@ -80,10 +83,13 @@ func (r *DataRepository) SaveStateFS(filePath string) {
|
|||||||
|
|
||||||
os.WriteFile(filePath, data, os.ModePerm)
|
os.WriteFile(filePath, data, os.ModePerm)
|
||||||
|
|
||||||
logger.Info("Saved state:")
|
logger.InfoLn("Saved state:")
|
||||||
logger.Infof("Databases: %d\n", getLength(r.storeState.Databases))
|
logger.Infof("Databases: %d\n", getLength(r.storeState.Databases))
|
||||||
logger.Infof("Collections: %d\n", getLength(r.storeState.Collections))
|
logger.Infof("Collections: %d\n", getLength(r.storeState.Collections))
|
||||||
logger.Infof("Documents: %d\n", getLength(r.storeState.Documents))
|
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) {
|
func (r *DataRepository) GetState() (string, error) {
|
||||||
@@ -103,7 +109,10 @@ func getLength(v interface{}) int {
|
|||||||
switch v.(type) {
|
switch v.(type) {
|
||||||
case repositorymodels.Database,
|
case repositorymodels.Database,
|
||||||
repositorymodels.Collection,
|
repositorymodels.Collection,
|
||||||
repositorymodels.Document:
|
repositorymodels.Document,
|
||||||
|
repositorymodels.Trigger,
|
||||||
|
repositorymodels.StoredProcedure,
|
||||||
|
repositorymodels.UserDefinedFunction:
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,6 +146,18 @@ func (r *DataRepository) ensureStoreStateNoNullReferences() {
|
|||||||
r.storeState.Documents = make(map[string]map[string]map[string]repositorymodels.Document)
|
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 {
|
for database := range r.storeState.Databases {
|
||||||
if r.storeState.Collections[database] == nil {
|
if r.storeState.Collections[database] == nil {
|
||||||
r.storeState.Collections[database] = make(map[string]repositorymodels.Collection)
|
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)
|
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] {
|
for collection := range r.storeState.Collections[database] {
|
||||||
if r.storeState.Documents[database][collection] == nil {
|
if r.storeState.Documents[database][collection] == nil {
|
||||||
r.storeState.Documents[database][collection] = make(map[string]repositorymodels.Document)
|
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)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,91 @@
|
|||||||
package repositories
|
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) {
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,91 @@
|
|||||||
package repositories
|
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) {
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,91 @@
|
|||||||
package repositories
|
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) {
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,22 @@ const (
|
|||||||
BadRequest = 4
|
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 {
|
type Collection struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
IndexingPolicy CollectionIndexingPolicy `json:"indexingPolicy"`
|
IndexingPolicy CollectionIndexingPolicy `json:"indexingPolicy"`
|
||||||
@@ -60,29 +76,29 @@ type UserDefinedFunction struct {
|
|||||||
Body string `json:"body"`
|
Body string `json:"body"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
ResourceID string `json:"_rid"`
|
ResourceID string `json:"_rid"`
|
||||||
TimeStamp int `json:"_ts"`
|
TimeStamp int64 `json:"_ts"`
|
||||||
Self string `json:"_self"`
|
Self string `json:"_self"`
|
||||||
Etag string `json:"_etag"`
|
ETag string `json:"_etag"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type StoredProcedure struct {
|
type StoredProcedure struct {
|
||||||
Body string `json:"body"`
|
Body string `json:"body"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
ResourceID string `json:"_rid"`
|
ResourceID string `json:"_rid"`
|
||||||
TimeStamp int `json:"_ts"`
|
TimeStamp int64 `json:"_ts"`
|
||||||
Self string `json:"_self"`
|
Self string `json:"_self"`
|
||||||
Etag string `json:"_etag"`
|
ETag string `json:"_etag"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Trigger struct {
|
type Trigger struct {
|
||||||
Body string `json:"body"`
|
Body string `json:"body"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
TriggerOperation string `json:"triggerOperation"`
|
TriggerOperation TriggerOperation `json:"triggerOperation"`
|
||||||
TriggerType string `json:"triggerType"`
|
TriggerType TriggerType `json:"triggerType"`
|
||||||
ResourceID string `json:"_rid"`
|
ResourceID string `json:"_rid"`
|
||||||
TimeStamp int `json:"_ts"`
|
TimeStamp int64 `json:"_ts"`
|
||||||
Self string `json:"_self"`
|
Self string `json:"_self"`
|
||||||
Etag string `json:"_etag"`
|
ETag string `json:"_etag"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Document map[string]interface{}
|
type Document map[string]interface{}
|
||||||
@@ -113,4 +129,13 @@ type State struct {
|
|||||||
|
|
||||||
// Map databaseId -> collectionId -> documentId -> Documents
|
// Map databaseId -> collectionId -> documentId -> Documents
|
||||||
Documents map[string]map[string]map[string]Document `json:"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"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
func GetDefaultTlsConfig() *tls.Config {
|
func GetDefaultTlsConfig() *tls.Config {
|
||||||
cert, err := tls.X509KeyPair([]byte(certificate), []byte(certificateKey))
|
cert, err := tls.X509KeyPair([]byte(certificate), []byte(certificateKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Failed to parse certificate and key:", err)
|
logger.ErrorLn("Failed to parse certificate and key:", err)
|
||||||
return &tls.Config{}
|
return &tls.Config{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -124,6 +124,9 @@ const (
|
|||||||
FunctionCallIsString FunctionCallType = "IsString"
|
FunctionCallIsString FunctionCallType = "IsString"
|
||||||
|
|
||||||
FunctionCallArrayConcat FunctionCallType = "ArrayConcat"
|
FunctionCallArrayConcat FunctionCallType = "ArrayConcat"
|
||||||
|
FunctionCallArrayContains FunctionCallType = "ArrayContains"
|
||||||
|
FunctionCallArrayContainsAny FunctionCallType = "ArrayContainsAny"
|
||||||
|
FunctionCallArrayContainsAll FunctionCallType = "ArrayContainsAll"
|
||||||
FunctionCallArrayLength FunctionCallType = "ArrayLength"
|
FunctionCallArrayLength FunctionCallType = "ArrayLength"
|
||||||
FunctionCallArraySlice FunctionCallType = "ArraySlice"
|
FunctionCallArraySlice FunctionCallType = "ArraySlice"
|
||||||
FunctionCallSetIntersect FunctionCallType = "SetIntersect"
|
FunctionCallSetIntersect FunctionCallType = "SetIntersect"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/pikami/cosmium/parsers"
|
"github.com/pikami/cosmium/parsers"
|
||||||
|
testutils "github.com/pikami/cosmium/test_utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_Parse_ArrayFunctions(t *testing.T) {
|
func Test_Parse_ArrayFunctions(t *testing.T) {
|
||||||
@@ -36,6 +37,119 @@ func Test_Parse_ArrayFunctions(t *testing.T) {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Should parse function ARRAY_CONTAINS()", func(t *testing.T) {
|
||||||
|
testQueryParse(
|
||||||
|
t,
|
||||||
|
`SELECT ARRAY_CONTAINS(c.a1, "value") FROM c`,
|
||||||
|
parsers.SelectStmt{
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
{
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallArrayContains,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
parsers.SelectItem{
|
||||||
|
Path: []string{"c", "a1"},
|
||||||
|
Type: parsers.SelectItemTypeField,
|
||||||
|
},
|
||||||
|
testutils.SelectItem_Constant_String("value"),
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Table: parsers.Table{Value: "c"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Should parse function ARRAY_CONTAINS() with partial match", func(t *testing.T) {
|
||||||
|
testQueryParse(
|
||||||
|
t,
|
||||||
|
`SELECT ARRAY_CONTAINS(["a", "b"], "value", true) FROM c`,
|
||||||
|
parsers.SelectStmt{
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
{
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallArrayContains,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
parsers.SelectItem{
|
||||||
|
Type: parsers.SelectItemTypeArray,
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
testutils.SelectItem_Constant_String("a"),
|
||||||
|
testutils.SelectItem_Constant_String("b"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testutils.SelectItem_Constant_String("value"),
|
||||||
|
testutils.SelectItem_Constant_Bool(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Table: parsers.Table{Value: "c"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Should parse function ARRAY_CONTAINS_ANY()", func(t *testing.T) {
|
||||||
|
testQueryParse(
|
||||||
|
t,
|
||||||
|
`SELECT ARRAY_CONTAINS_ANY(["a", "b"], "value", true) FROM c`,
|
||||||
|
parsers.SelectStmt{
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
{
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallArrayContainsAny,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
parsers.SelectItem{
|
||||||
|
Type: parsers.SelectItemTypeArray,
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
testutils.SelectItem_Constant_String("a"),
|
||||||
|
testutils.SelectItem_Constant_String("b"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testutils.SelectItem_Constant_String("value"),
|
||||||
|
testutils.SelectItem_Constant_Bool(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Table: parsers.Table{Value: "c"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Should parse function ARRAY_CONTAINS_ALL()", func(t *testing.T) {
|
||||||
|
testQueryParse(
|
||||||
|
t,
|
||||||
|
`SELECT ARRAY_CONTAINS_ALL(["a", "b"], "value", true) FROM c`,
|
||||||
|
parsers.SelectStmt{
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
{
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallArrayContainsAll,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
parsers.SelectItem{
|
||||||
|
Type: parsers.SelectItemTypeArray,
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
testutils.SelectItem_Constant_String("a"),
|
||||||
|
testutils.SelectItem_Constant_String("b"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testutils.SelectItem_Constant_String("value"),
|
||||||
|
testutils.SelectItem_Constant_Bool(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Table: parsers.Table{Value: "c"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("Should parse function ARRAY_LENGTH()", func(t *testing.T) {
|
t.Run("Should parse function ARRAY_LENGTH()", func(t *testing.T) {
|
||||||
testQueryParse(
|
testQueryParse(
|
||||||
t,
|
t,
|
||||||
@@ -75,20 +189,8 @@ func Test_Parse_ArrayFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "array"},
|
Path: []string{"c", "array"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_Int(0),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
testutils.SelectItem_Constant_Int(2),
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeInteger,
|
|
||||||
Value: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
parsers.SelectItem{
|
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeInteger,
|
|
||||||
Value: 2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/pikami/cosmium/parsers"
|
"github.com/pikami/cosmium/parsers"
|
||||||
"github.com/pikami/cosmium/parsers/nosql"
|
"github.com/pikami/cosmium/parsers/nosql"
|
||||||
|
testutils "github.com/pikami/cosmium/test_utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// For Parser Debugging
|
// For Parser Debugging
|
||||||
@@ -102,20 +103,8 @@ func Test_Parse(t *testing.T) {
|
|||||||
Path: []string{"c", "id"},
|
Path: []string{"c", "id"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_String("123"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
testutils.SelectItem_Constant_String("456"),
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeString,
|
|
||||||
Value: "123",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
parsers.SelectItem{
|
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeString,
|
|
||||||
Value: "456",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -325,6 +325,7 @@ DotFieldAccess <- "." id:Identifier {
|
|||||||
|
|
||||||
ArrayFieldAccess <- "[\"" id:Identifier "\"]" { return id, nil }
|
ArrayFieldAccess <- "[\"" id:Identifier "\"]" { return id, nil }
|
||||||
/ "[" id:Integer "]" { return strconv.Itoa(id.(int)), nil }
|
/ "[" id:Integer "]" { return strconv.Itoa(id.(int)), nil }
|
||||||
|
/ "[" id:ParameterConstant "]" { return id.(parsers.Constant).Value.(string), nil }
|
||||||
|
|
||||||
Identifier <- [a-zA-Z_][a-zA-Z0-9_]* {
|
Identifier <- [a-zA-Z_][a-zA-Z0-9_]* {
|
||||||
return string(c.text), nil
|
return string(c.text), nil
|
||||||
@@ -456,6 +457,9 @@ AggregateFunctions <- AvgAggregateExpression
|
|||||||
/ SumAggregateExpression
|
/ SumAggregateExpression
|
||||||
|
|
||||||
ArrayFunctions <- ArrayConcatExpression
|
ArrayFunctions <- ArrayConcatExpression
|
||||||
|
/ ArrayContainsExpression
|
||||||
|
/ ArrayContainsAnyExpression
|
||||||
|
/ ArrayContainsAllExpression
|
||||||
/ ArrayLengthExpression
|
/ ArrayLengthExpression
|
||||||
/ ArraySliceExpression
|
/ ArraySliceExpression
|
||||||
/ SetIntersectExpression
|
/ SetIntersectExpression
|
||||||
@@ -625,6 +629,18 @@ ArrayConcatExpression <- "ARRAY_CONCAT"i ws "(" ws arrays:SelectItem others:(ws
|
|||||||
return createFunctionCall(parsers.FunctionCallArrayConcat, append([]interface{}{arrays}, others.([]interface{})...))
|
return createFunctionCall(parsers.FunctionCallArrayConcat, append([]interface{}{arrays}, others.([]interface{})...))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArrayContainsExpression <- "ARRAY_CONTAINS"i ws "(" ws array:SelectItem ws "," ws item:SelectItem partialMatch:(ws "," ws ex:SelectItem { return ex, nil })? ws ")" {
|
||||||
|
return createFunctionCall(parsers.FunctionCallArrayContains, []interface{}{array, item, partialMatch})
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayContainsAnyExpression <- "ARRAY_CONTAINS_ANY"i ws "(" ws array:SelectItem items:(ws "," ws ex:SelectItem { return ex, nil })+ ws ")" {
|
||||||
|
return createFunctionCall(parsers.FunctionCallArrayContainsAny, append([]interface{}{array}, items.([]interface{})...))
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayContainsAllExpression <- "ARRAY_CONTAINS_ALL"i ws "(" ws array:SelectItem items:(ws "," ws ex:SelectItem { return ex, nil })+ ws ")" {
|
||||||
|
return createFunctionCall(parsers.FunctionCallArrayContainsAll, append([]interface{}{array}, items.([]interface{})...))
|
||||||
|
}
|
||||||
|
|
||||||
ArrayLengthExpression <- "ARRAY_LENGTH"i ws "(" ws array:SelectItem ws ")" {
|
ArrayLengthExpression <- "ARRAY_LENGTH"i ws "(" ws array:SelectItem ws ")" {
|
||||||
return createFunctionCall(parsers.FunctionCallArrayLength, []interface{}{array})
|
return createFunctionCall(parsers.FunctionCallArrayLength, []interface{}{array})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,20 @@ func Test_Parse_Select(t *testing.T) {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Should parse SELECT with query parameters as accessor", func(t *testing.T) {
|
||||||
|
testQueryParse(
|
||||||
|
t,
|
||||||
|
`SELECT c.id, c[@param] FROM c`,
|
||||||
|
parsers.SelectStmt{
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
{Path: []string{"c", "id"}},
|
||||||
|
{Path: []string{"c", "@param"}},
|
||||||
|
},
|
||||||
|
Table: parsers.Table{Value: "c"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("Should parse SELECT DISTINCT", func(t *testing.T) {
|
t.Run("Should parse SELECT DISTINCT", func(t *testing.T) {
|
||||||
testQueryParse(
|
testQueryParse(
|
||||||
t,
|
t,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/pikami/cosmium/parsers"
|
"github.com/pikami/cosmium/parsers"
|
||||||
|
testutils "github.com/pikami/cosmium/test_utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_Execute_StringFunctions(t *testing.T) {
|
func Test_Execute_StringFunctions(t *testing.T) {
|
||||||
@@ -23,20 +24,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "id"},
|
Path: []string{"c", "id"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_String("123"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
testutils.SelectItem_Constant_Bool(true),
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeString,
|
|
||||||
Value: "123",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
parsers.SelectItem{
|
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeBoolean,
|
|
||||||
Value: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -61,13 +50,7 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "id"},
|
Path: []string{"c", "id"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_String("123"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeString,
|
|
||||||
Value: "123",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -93,13 +76,7 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "id"},
|
Path: []string{"c", "id"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_String("123"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeString,
|
|
||||||
Value: "123",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
parsers.SelectItem{
|
parsers.SelectItem{
|
||||||
Path: []string{"c", "pk"},
|
Path: []string{"c", "pk"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
@@ -128,20 +105,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "id"},
|
Path: []string{"c", "id"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_String("123"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
testutils.SelectItem_Constant_Bool(true),
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeString,
|
|
||||||
Value: "123",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
parsers.SelectItem{
|
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeBoolean,
|
|
||||||
Value: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -166,20 +131,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "id"},
|
Path: []string{"c", "id"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_String("123"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
testutils.SelectItem_Constant_Bool(true),
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeString,
|
|
||||||
Value: "123",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
parsers.SelectItem{
|
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeBoolean,
|
|
||||||
Value: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -204,20 +157,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "id"},
|
Path: []string{"c", "id"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_String("123"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
testutils.SelectItem_Constant_Bool(true),
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeString,
|
|
||||||
Value: "123",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
parsers.SelectItem{
|
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeBoolean,
|
|
||||||
Value: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -242,20 +183,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "id"},
|
Path: []string{"c", "id"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_String("2"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
testutils.SelectItem_Constant_Int(1),
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeString,
|
|
||||||
Value: "2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
parsers.SelectItem{
|
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeInteger,
|
|
||||||
Value: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -352,13 +281,7 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "id"},
|
Path: []string{"c", "id"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_Int(5),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeInteger,
|
|
||||||
Value: 5,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -431,20 +354,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "id"},
|
Path: []string{"c", "id"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_String("old"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
testutils.SelectItem_Constant_String("new"),
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeString,
|
|
||||||
Value: "old",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
parsers.SelectItem{
|
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeString,
|
|
||||||
Value: "new",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -469,13 +380,7 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "id"},
|
Path: []string{"c", "id"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_Int(3),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeInteger,
|
|
||||||
Value: 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -524,13 +429,7 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "id"},
|
Path: []string{"c", "id"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_Int(3),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeInteger,
|
|
||||||
Value: 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -579,20 +478,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "id"},
|
Path: []string{"c", "id"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_Int(1),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
testutils.SelectItem_Constant_Int(5),
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeInteger,
|
|
||||||
Value: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
parsers.SelectItem{
|
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeInteger,
|
|
||||||
Value: 5,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/pikami/cosmium/parsers"
|
"github.com/pikami/cosmium/parsers"
|
||||||
|
testutils "github.com/pikami/cosmium/test_utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_Parse_Were(t *testing.T) {
|
func Test_Parse_Were(t *testing.T) {
|
||||||
@@ -22,10 +23,7 @@ func Test_Parse_Were(t *testing.T) {
|
|||||||
Filters: parsers.ComparisonExpression{
|
Filters: parsers.ComparisonExpression{
|
||||||
Operation: "=",
|
Operation: "=",
|
||||||
Left: parsers.SelectItem{Path: []string{"c", "isCool"}},
|
Left: parsers.SelectItem{Path: []string{"c", "isCool"}},
|
||||||
Right: parsers.SelectItem{
|
Right: testutils.SelectItem_Constant_Bool(true),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: true},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -51,18 +49,12 @@ func Test_Parse_Were(t *testing.T) {
|
|||||||
parsers.ComparisonExpression{
|
parsers.ComparisonExpression{
|
||||||
Operation: "=",
|
Operation: "=",
|
||||||
Left: parsers.SelectItem{Path: []string{"c", "id"}},
|
Left: parsers.SelectItem{Path: []string{"c", "id"}},
|
||||||
Right: parsers.SelectItem{
|
Right: testutils.SelectItem_Constant_String("12345"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{Type: parsers.ConstantTypeString, Value: "12345"},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
parsers.ComparisonExpression{
|
parsers.ComparisonExpression{
|
||||||
Operation: "=",
|
Operation: "=",
|
||||||
Left: parsers.SelectItem{Path: []string{"c", "pk"}},
|
Left: parsers.SelectItem{Path: []string{"c", "pk"}},
|
||||||
Right: parsers.SelectItem{
|
Right: testutils.SelectItem_Constant_Int(123),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{Type: parsers.ConstantTypeInteger, Value: 123},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -87,10 +79,7 @@ func Test_Parse_Were(t *testing.T) {
|
|||||||
parsers.ComparisonExpression{
|
parsers.ComparisonExpression{
|
||||||
Operation: "=",
|
Operation: "=",
|
||||||
Left: parsers.SelectItem{Path: []string{"c", "isCool"}},
|
Left: parsers.SelectItem{Path: []string{"c", "isCool"}},
|
||||||
Right: parsers.SelectItem{
|
Right: testutils.SelectItem_Constant_Bool(true),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: true},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
parsers.LogicalExpression{
|
parsers.LogicalExpression{
|
||||||
Operation: parsers.LogicalExpressionTypeOr,
|
Operation: parsers.LogicalExpressionTypeOr,
|
||||||
@@ -98,18 +87,12 @@ func Test_Parse_Were(t *testing.T) {
|
|||||||
parsers.ComparisonExpression{
|
parsers.ComparisonExpression{
|
||||||
Operation: "=",
|
Operation: "=",
|
||||||
Left: parsers.SelectItem{Path: []string{"c", "id"}},
|
Left: parsers.SelectItem{Path: []string{"c", "id"}},
|
||||||
Right: parsers.SelectItem{
|
Right: testutils.SelectItem_Constant_String("123"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{Type: parsers.ConstantTypeString, Value: "123"},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
parsers.ComparisonExpression{
|
parsers.ComparisonExpression{
|
||||||
Operation: "=",
|
Operation: "=",
|
||||||
Left: parsers.SelectItem{Path: []string{"c", "id"}},
|
Left: parsers.SelectItem{Path: []string{"c", "id"}},
|
||||||
Right: parsers.SelectItem{
|
Right: testutils.SelectItem_Constant_String("456"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{Type: parsers.ConstantTypeString, Value: "456"},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -136,42 +119,27 @@ func Test_Parse_Were(t *testing.T) {
|
|||||||
Expressions: []interface{}{
|
Expressions: []interface{}{
|
||||||
parsers.ComparisonExpression{
|
parsers.ComparisonExpression{
|
||||||
Left: parsers.SelectItem{Path: []string{"c", "boolean"}},
|
Left: parsers.SelectItem{Path: []string{"c", "boolean"}},
|
||||||
Right: parsers.SelectItem{
|
Right: testutils.SelectItem_Constant_Bool(true),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: true},
|
|
||||||
},
|
|
||||||
Operation: "=",
|
Operation: "=",
|
||||||
},
|
},
|
||||||
parsers.ComparisonExpression{
|
parsers.ComparisonExpression{
|
||||||
Left: parsers.SelectItem{Path: []string{"c", "integer"}},
|
Left: parsers.SelectItem{Path: []string{"c", "integer"}},
|
||||||
Right: parsers.SelectItem{
|
Right: testutils.SelectItem_Constant_Int(1),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{Type: parsers.ConstantTypeInteger, Value: 1},
|
|
||||||
},
|
|
||||||
Operation: "=",
|
Operation: "=",
|
||||||
},
|
},
|
||||||
parsers.ComparisonExpression{
|
parsers.ComparisonExpression{
|
||||||
Left: parsers.SelectItem{Path: []string{"c", "float"}},
|
Left: parsers.SelectItem{Path: []string{"c", "float"}},
|
||||||
Right: parsers.SelectItem{
|
Right: testutils.SelectItem_Constant_Float(6.9),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{Type: parsers.ConstantTypeFloat, Value: 6.9},
|
|
||||||
},
|
|
||||||
Operation: "=",
|
Operation: "=",
|
||||||
},
|
},
|
||||||
parsers.ComparisonExpression{
|
parsers.ComparisonExpression{
|
||||||
Left: parsers.SelectItem{Path: []string{"c", "string"}},
|
Left: parsers.SelectItem{Path: []string{"c", "string"}},
|
||||||
Right: parsers.SelectItem{
|
Right: testutils.SelectItem_Constant_String("hello"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{Type: parsers.ConstantTypeString, Value: "hello"},
|
|
||||||
},
|
|
||||||
Operation: "=",
|
Operation: "=",
|
||||||
},
|
},
|
||||||
parsers.ComparisonExpression{
|
parsers.ComparisonExpression{
|
||||||
Left: parsers.SelectItem{Path: []string{"c", "param"}},
|
Left: parsers.SelectItem{Path: []string{"c", "param"}},
|
||||||
Right: parsers.SelectItem{
|
Right: testutils.SelectItem_Constant_Parameter("@param_id1"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{Type: parsers.ConstantTypeParameterConstant, Value: "@param_id1"},
|
|
||||||
},
|
|
||||||
Operation: "=",
|
Operation: "=",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,6 +16,86 @@ func (r rowContext) array_Concat(arguments []interface{}) []interface{} {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r rowContext) array_Contains(arguments []interface{}) bool {
|
||||||
|
array := r.parseArray(arguments[0])
|
||||||
|
if array == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
exprToSearch := r.resolveSelectItem(arguments[1].(parsers.SelectItem))
|
||||||
|
|
||||||
|
partialSearch := false
|
||||||
|
if len(arguments) > 2 {
|
||||||
|
boolExpr := r.resolveSelectItem(arguments[2].(parsers.SelectItem))
|
||||||
|
if boolValue, ok := boolExpr.(bool); ok {
|
||||||
|
partialSearch = boolValue
|
||||||
|
} else {
|
||||||
|
logger.ErrorLn("array_Contains - got parameters of wrong type")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range array {
|
||||||
|
if partialSearch {
|
||||||
|
if r.partialMatch(item, exprToSearch) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if reflect.DeepEqual(item, exprToSearch) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r rowContext) array_Contains_Any(arguments []interface{}) bool {
|
||||||
|
array := r.parseArray(arguments[0])
|
||||||
|
if array == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
valueSelectItems := arguments[1:]
|
||||||
|
|
||||||
|
for _, valueSelectItem := range valueSelectItems {
|
||||||
|
value := r.resolveSelectItem(valueSelectItem.(parsers.SelectItem))
|
||||||
|
for _, item := range array {
|
||||||
|
if reflect.DeepEqual(item, value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r rowContext) array_Contains_All(arguments []interface{}) bool {
|
||||||
|
array := r.parseArray(arguments[0])
|
||||||
|
if array == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
valueSelectItems := arguments[1:]
|
||||||
|
|
||||||
|
for _, valueSelectItem := range valueSelectItems {
|
||||||
|
value := r.resolveSelectItem(valueSelectItem.(parsers.SelectItem))
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, item := range array {
|
||||||
|
if reflect.DeepEqual(item, value) {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (r rowContext) array_Length(arguments []interface{}) int {
|
func (r rowContext) array_Length(arguments []interface{}) int {
|
||||||
array := r.parseArray(arguments[0])
|
array := r.parseArray(arguments[0])
|
||||||
if array == nil {
|
if array == nil {
|
||||||
@@ -36,13 +116,13 @@ func (r rowContext) array_Slice(arguments []interface{}) []interface{} {
|
|||||||
lengthEx := r.resolveSelectItem(arguments[2].(parsers.SelectItem))
|
lengthEx := r.resolveSelectItem(arguments[2].(parsers.SelectItem))
|
||||||
|
|
||||||
if length, ok = lengthEx.(int); !ok {
|
if length, ok = lengthEx.(int); !ok {
|
||||||
logger.Error("array_Slice - got length parameters of wrong type")
|
logger.ErrorLn("array_Slice - got length parameters of wrong type")
|
||||||
return []interface{}{}
|
return []interface{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if start, ok = startEx.(int); !ok {
|
if start, ok = startEx.(int); !ok {
|
||||||
logger.Error("array_Slice - got start parameters of wrong type")
|
logger.ErrorLn("array_Slice - got start parameters of wrong type")
|
||||||
return []interface{}{}
|
return []interface{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +197,7 @@ func (r rowContext) parseArray(argument interface{}) []interface{} {
|
|||||||
|
|
||||||
arrValue := reflect.ValueOf(ex)
|
arrValue := reflect.ValueOf(ex)
|
||||||
if arrValue.Kind() != reflect.Slice {
|
if arrValue.Kind() != reflect.Slice {
|
||||||
logger.Error("parseArray got parameters of wrong type")
|
logger.ErrorLn("parseArray got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,3 +209,21 @@ func (r rowContext) parseArray(argument interface{}) []interface{} {
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r rowContext) partialMatch(item interface{}, exprToSearch interface{}) bool {
|
||||||
|
itemValue := reflect.ValueOf(item)
|
||||||
|
exprValue := reflect.ValueOf(exprToSearch)
|
||||||
|
|
||||||
|
if itemValue.Kind() != reflect.Map || exprValue.Kind() != reflect.Map {
|
||||||
|
logger.ErrorLn("partialMatch got parameters of wrong type")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range exprValue.MapKeys() {
|
||||||
|
if itemValue.MapIndex(key).Interface() != exprValue.MapIndex(key).Interface() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/pikami/cosmium/parsers"
|
"github.com/pikami/cosmium/parsers"
|
||||||
memoryexecutor "github.com/pikami/cosmium/query_executors/memory_executor"
|
memoryexecutor "github.com/pikami/cosmium/query_executors/memory_executor"
|
||||||
|
testutils "github.com/pikami/cosmium/test_utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_Execute_ArrayFunctions(t *testing.T) {
|
func Test_Execute_ArrayFunctions(t *testing.T) {
|
||||||
@@ -52,6 +53,286 @@ func Test_Execute_ArrayFunctions(t *testing.T) {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Should execute function ARRAY_CONTAINS()", func(t *testing.T) {
|
||||||
|
testQueryExecute(
|
||||||
|
t,
|
||||||
|
parsers.SelectStmt{
|
||||||
|
Parameters: map[string]interface{}{
|
||||||
|
"@categories": []interface{}{"coats", "jackets", "sweatshirts"},
|
||||||
|
"@objectArray": []interface{}{map[string]interface{}{"category": "shirts", "color": "blue"}},
|
||||||
|
"@fullMatchObject": map[string]interface{}{"category": "shirts", "color": "blue"},
|
||||||
|
"@partialMatchObject": map[string]interface{}{"category": "shirts"},
|
||||||
|
"@missingPartialMatchObject": map[string]interface{}{"category": "shorts", "color": "blue"},
|
||||||
|
},
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
{
|
||||||
|
Alias: "ContainsItem",
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallArrayContains,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
testutils.SelectItem_Constant_Parameter("@categories"),
|
||||||
|
testutils.SelectItem_Constant_String("coats"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Alias: "MissingItem",
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallArrayContains,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
testutils.SelectItem_Constant_Parameter("@categories"),
|
||||||
|
testutils.SelectItem_Constant_String("hoodies"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Alias: "ContainsFullMatchObject",
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallArrayContains,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
testutils.SelectItem_Constant_Parameter("@objectArray"),
|
||||||
|
testutils.SelectItem_Constant_Parameter("@fullMatchObject"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Alias: "MissingFullMatchObject",
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallArrayContains,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
testutils.SelectItem_Constant_Parameter("@objectArray"),
|
||||||
|
testutils.SelectItem_Constant_Parameter("@partialMatchObject"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Alias: "ContainsPartialMatchObject",
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallArrayContains,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
testutils.SelectItem_Constant_Parameter("@objectArray"),
|
||||||
|
testutils.SelectItem_Constant_Parameter("@partialMatchObject"),
|
||||||
|
testutils.SelectItem_Constant_Bool(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Alias: "MissingPartialMatchObject",
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallArrayContains,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
testutils.SelectItem_Constant_Parameter("@objectArray"),
|
||||||
|
testutils.SelectItem_Constant_Parameter("@missingPartialMatchObject"),
|
||||||
|
testutils.SelectItem_Constant_Bool(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]memoryexecutor.RowType{map[string]interface{}{"id": "123"}},
|
||||||
|
[]memoryexecutor.RowType{
|
||||||
|
map[string]interface{}{
|
||||||
|
"ContainsItem": true,
|
||||||
|
"MissingItem": false,
|
||||||
|
"ContainsFullMatchObject": true,
|
||||||
|
"MissingFullMatchObject": false,
|
||||||
|
"ContainsPartialMatchObject": true,
|
||||||
|
"MissingPartialMatchObject": false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Should execute function ARRAY_CONTAINS_ANY()", func(t *testing.T) {
|
||||||
|
testQueryExecute(
|
||||||
|
t,
|
||||||
|
parsers.SelectStmt{
|
||||||
|
Parameters: map[string]interface{}{
|
||||||
|
"@mixedArray": []interface{}{1, true, "3", []int{1, 2, 3}},
|
||||||
|
"@numbers": []interface{}{1, 2, 3, 4},
|
||||||
|
"@emptyArray": []interface{}{},
|
||||||
|
"@arr123": []interface{}{1, 2, 3},
|
||||||
|
},
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
{
|
||||||
|
Alias: "matchesEntireArray",
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallArrayContainsAny,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
testutils.SelectItem_Constant_Parameter("@mixedArray"),
|
||||||
|
testutils.SelectItem_Constant_Int(1),
|
||||||
|
testutils.SelectItem_Constant_Bool(true),
|
||||||
|
testutils.SelectItem_Constant_String("3"),
|
||||||
|
testutils.SelectItem_Constant_Parameter("@arr123"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Alias: "matchesSomeValues",
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallArrayContainsAny,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
testutils.SelectItem_Constant_Parameter("@numbers"),
|
||||||
|
testutils.SelectItem_Constant_Int(2),
|
||||||
|
testutils.SelectItem_Constant_Int(3),
|
||||||
|
testutils.SelectItem_Constant_Int(4),
|
||||||
|
testutils.SelectItem_Constant_Int(5),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Alias: "matchSingleValue",
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallArrayContainsAny,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
testutils.SelectItem_Constant_Parameter("@numbers"),
|
||||||
|
testutils.SelectItem_Constant_Int(1),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Alias: "noMatches",
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallArrayContainsAny,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
testutils.SelectItem_Constant_Parameter("@numbers"),
|
||||||
|
testutils.SelectItem_Constant_Int(5),
|
||||||
|
testutils.SelectItem_Constant_Int(6),
|
||||||
|
testutils.SelectItem_Constant_Int(7),
|
||||||
|
testutils.SelectItem_Constant_Int(8),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Alias: "emptyArray",
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallArrayContainsAny,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
testutils.SelectItem_Constant_Parameter("@emptyArray"),
|
||||||
|
testutils.SelectItem_Constant_Int(1),
|
||||||
|
testutils.SelectItem_Constant_Int(2),
|
||||||
|
testutils.SelectItem_Constant_Int(3),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]memoryexecutor.RowType{map[string]interface{}{"id": "123"}},
|
||||||
|
[]memoryexecutor.RowType{
|
||||||
|
map[string]interface{}{
|
||||||
|
"matchesEntireArray": true,
|
||||||
|
"matchesSomeValues": true,
|
||||||
|
"matchSingleValue": true,
|
||||||
|
"noMatches": false,
|
||||||
|
"emptyArray": false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Should execute function ARRAY_CONTAINS_ALL()", func(t *testing.T) {
|
||||||
|
testQueryExecute(
|
||||||
|
t,
|
||||||
|
parsers.SelectStmt{
|
||||||
|
Parameters: map[string]interface{}{
|
||||||
|
"@mixedArray": []interface{}{1, true, "3", []interface{}{1, 2, 3}},
|
||||||
|
"@numbers": []interface{}{1, 2, 3, 4},
|
||||||
|
"@emptyArray": []interface{}{},
|
||||||
|
"@arr123": []interface{}{1, 2, 3},
|
||||||
|
},
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
{
|
||||||
|
Alias: "matchesEntireArray",
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallArrayContainsAll,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
testutils.SelectItem_Constant_Parameter("@mixedArray"),
|
||||||
|
testutils.SelectItem_Constant_Int(1),
|
||||||
|
testutils.SelectItem_Constant_Bool(true),
|
||||||
|
testutils.SelectItem_Constant_String("3"),
|
||||||
|
testutils.SelectItem_Constant_Parameter("@arr123"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Alias: "matchesSomeValues",
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallArrayContainsAll,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
testutils.SelectItem_Constant_Parameter("@numbers"),
|
||||||
|
testutils.SelectItem_Constant_Int(2),
|
||||||
|
testutils.SelectItem_Constant_Int(3),
|
||||||
|
testutils.SelectItem_Constant_Int(4),
|
||||||
|
testutils.SelectItem_Constant_Int(5),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Alias: "matchSingleValue",
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallArrayContainsAll,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
testutils.SelectItem_Constant_Parameter("@numbers"),
|
||||||
|
testutils.SelectItem_Constant_Int(1),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Alias: "noMatches",
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallArrayContainsAll,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
testutils.SelectItem_Constant_Parameter("@numbers"),
|
||||||
|
testutils.SelectItem_Constant_Int(5),
|
||||||
|
testutils.SelectItem_Constant_Int(6),
|
||||||
|
testutils.SelectItem_Constant_Int(7),
|
||||||
|
testutils.SelectItem_Constant_Int(8),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Alias: "emptyArray",
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallArrayContainsAll,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
testutils.SelectItem_Constant_Parameter("@emptyArray"),
|
||||||
|
testutils.SelectItem_Constant_Int(1),
|
||||||
|
testutils.SelectItem_Constant_Int(2),
|
||||||
|
testutils.SelectItem_Constant_Int(3),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]memoryexecutor.RowType{map[string]interface{}{"id": "123"}},
|
||||||
|
[]memoryexecutor.RowType{
|
||||||
|
map[string]interface{}{
|
||||||
|
"matchesEntireArray": true,
|
||||||
|
"matchesSomeValues": false,
|
||||||
|
"matchSingleValue": true,
|
||||||
|
"noMatches": false,
|
||||||
|
"emptyArray": false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("Should execute function ARRAY_LENGTH()", func(t *testing.T) {
|
t.Run("Should execute function ARRAY_LENGTH()", func(t *testing.T) {
|
||||||
testQueryExecute(
|
testQueryExecute(
|
||||||
t,
|
t,
|
||||||
@@ -105,20 +386,8 @@ func Test_Execute_ArrayFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "arr2"},
|
Path: []string{"c", "arr2"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_Int(1),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
testutils.SelectItem_Constant_Int(2),
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeInteger,
|
|
||||||
Value: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
parsers.SelectItem{
|
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeInteger,
|
|
||||||
Value: 2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ func (r rowContext) math_Abs(arguments []interface{}) interface{} {
|
|||||||
}
|
}
|
||||||
return val
|
return val
|
||||||
default:
|
default:
|
||||||
logger.Debug("math_Abs - got parameters of wrong type")
|
logger.DebugLn("math_Abs - got parameters of wrong type")
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -32,12 +32,12 @@ func (r rowContext) math_Acos(arguments []interface{}) interface{} {
|
|||||||
|
|
||||||
val, valIsNumber := numToFloat64(ex)
|
val, valIsNumber := numToFloat64(ex)
|
||||||
if !valIsNumber {
|
if !valIsNumber {
|
||||||
logger.Debug("math_Acos - got parameters of wrong type")
|
logger.DebugLn("math_Acos - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if val < -1 || val > 1 {
|
if val < -1 || val > 1 {
|
||||||
logger.Debug("math_Acos - value out of domain for acos")
|
logger.DebugLn("math_Acos - value out of domain for acos")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,12 +50,12 @@ func (r rowContext) math_Asin(arguments []interface{}) interface{} {
|
|||||||
|
|
||||||
val, valIsNumber := numToFloat64(ex)
|
val, valIsNumber := numToFloat64(ex)
|
||||||
if !valIsNumber {
|
if !valIsNumber {
|
||||||
logger.Debug("math_Asin - got parameters of wrong type")
|
logger.DebugLn("math_Asin - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if val < -1 || val > 1 {
|
if val < -1 || val > 1 {
|
||||||
logger.Debug("math_Asin - value out of domain for acos")
|
logger.DebugLn("math_Asin - value out of domain for acos")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ func (r rowContext) math_Atan(arguments []interface{}) interface{} {
|
|||||||
|
|
||||||
val, valIsNumber := numToFloat64(ex)
|
val, valIsNumber := numToFloat64(ex)
|
||||||
if !valIsNumber {
|
if !valIsNumber {
|
||||||
logger.Debug("math_Atan - got parameters of wrong type")
|
logger.DebugLn("math_Atan - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ func (r rowContext) math_Ceiling(arguments []interface{}) interface{} {
|
|||||||
case int:
|
case int:
|
||||||
return val
|
return val
|
||||||
default:
|
default:
|
||||||
logger.Debug("math_Ceiling - got parameters of wrong type")
|
logger.DebugLn("math_Ceiling - got parameters of wrong type")
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,7 +96,7 @@ func (r rowContext) math_Cos(arguments []interface{}) interface{} {
|
|||||||
|
|
||||||
val, valIsNumber := numToFloat64(ex)
|
val, valIsNumber := numToFloat64(ex)
|
||||||
if !valIsNumber {
|
if !valIsNumber {
|
||||||
logger.Debug("math_Cos - got parameters of wrong type")
|
logger.DebugLn("math_Cos - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,12 +109,12 @@ func (r rowContext) math_Cot(arguments []interface{}) interface{} {
|
|||||||
|
|
||||||
val, valIsNumber := numToFloat64(ex)
|
val, valIsNumber := numToFloat64(ex)
|
||||||
if !valIsNumber {
|
if !valIsNumber {
|
||||||
logger.Debug("math_Cot - got parameters of wrong type")
|
logger.DebugLn("math_Cot - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if val == 0 {
|
if val == 0 {
|
||||||
logger.Debug("math_Cot - cotangent undefined for zero")
|
logger.DebugLn("math_Cot - cotangent undefined for zero")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ func (r rowContext) math_Degrees(arguments []interface{}) interface{} {
|
|||||||
|
|
||||||
val, valIsNumber := numToFloat64(ex)
|
val, valIsNumber := numToFloat64(ex)
|
||||||
if !valIsNumber {
|
if !valIsNumber {
|
||||||
logger.Debug("math_Degrees - got parameters of wrong type")
|
logger.DebugLn("math_Degrees - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ func (r rowContext) math_Exp(arguments []interface{}) interface{} {
|
|||||||
|
|
||||||
val, valIsNumber := numToFloat64(ex)
|
val, valIsNumber := numToFloat64(ex)
|
||||||
if !valIsNumber {
|
if !valIsNumber {
|
||||||
logger.Debug("math_Exp - got parameters of wrong type")
|
logger.DebugLn("math_Exp - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +157,7 @@ func (r rowContext) math_Floor(arguments []interface{}) interface{} {
|
|||||||
case int:
|
case int:
|
||||||
return val
|
return val
|
||||||
default:
|
default:
|
||||||
logger.Debug("math_Floor - got parameters of wrong type")
|
logger.DebugLn("math_Floor - got parameters of wrong type")
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,7 +170,7 @@ func (r rowContext) math_IntBitNot(arguments []interface{}) interface{} {
|
|||||||
case int:
|
case int:
|
||||||
return ^val
|
return ^val
|
||||||
default:
|
default:
|
||||||
logger.Debug("math_IntBitNot - got parameters of wrong type")
|
logger.DebugLn("math_IntBitNot - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -181,12 +181,12 @@ func (r rowContext) math_Log10(arguments []interface{}) interface{} {
|
|||||||
|
|
||||||
val, valIsNumber := numToFloat64(ex)
|
val, valIsNumber := numToFloat64(ex)
|
||||||
if !valIsNumber {
|
if !valIsNumber {
|
||||||
logger.Debug("math_Log10 - got parameters of wrong type")
|
logger.DebugLn("math_Log10 - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if val <= 0 {
|
if val <= 0 {
|
||||||
logger.Debug("math_Log10 - value must be greater than 0")
|
logger.DebugLn("math_Log10 - value must be greater than 0")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +199,7 @@ func (r rowContext) math_Radians(arguments []interface{}) interface{} {
|
|||||||
|
|
||||||
val, valIsNumber := numToFloat64(ex)
|
val, valIsNumber := numToFloat64(ex)
|
||||||
if !valIsNumber {
|
if !valIsNumber {
|
||||||
logger.Debug("math_Radians - got parameters of wrong type")
|
logger.DebugLn("math_Radians - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,7 +216,7 @@ func (r rowContext) math_Round(arguments []interface{}) interface{} {
|
|||||||
case int:
|
case int:
|
||||||
return val
|
return val
|
||||||
default:
|
default:
|
||||||
logger.Debug("math_Round - got parameters of wrong type")
|
logger.DebugLn("math_Round - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -243,7 +243,7 @@ func (r rowContext) math_Sign(arguments []interface{}) interface{} {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
logger.Debug("math_Sign - got parameters of wrong type")
|
logger.DebugLn("math_Sign - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -254,7 +254,7 @@ func (r rowContext) math_Sin(arguments []interface{}) interface{} {
|
|||||||
|
|
||||||
val, valIsNumber := numToFloat64(ex)
|
val, valIsNumber := numToFloat64(ex)
|
||||||
if !valIsNumber {
|
if !valIsNumber {
|
||||||
logger.Debug("math_Sin - got parameters of wrong type")
|
logger.DebugLn("math_Sin - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,7 +267,7 @@ func (r rowContext) math_Sqrt(arguments []interface{}) interface{} {
|
|||||||
|
|
||||||
val, valIsNumber := numToFloat64(ex)
|
val, valIsNumber := numToFloat64(ex)
|
||||||
if !valIsNumber {
|
if !valIsNumber {
|
||||||
logger.Debug("math_Sqrt - got parameters of wrong type")
|
logger.DebugLn("math_Sqrt - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,7 +280,7 @@ func (r rowContext) math_Square(arguments []interface{}) interface{} {
|
|||||||
|
|
||||||
val, valIsNumber := numToFloat64(ex)
|
val, valIsNumber := numToFloat64(ex)
|
||||||
if !valIsNumber {
|
if !valIsNumber {
|
||||||
logger.Debug("math_Square - got parameters of wrong type")
|
logger.DebugLn("math_Square - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,7 +293,7 @@ func (r rowContext) math_Tan(arguments []interface{}) interface{} {
|
|||||||
|
|
||||||
val, valIsNumber := numToFloat64(ex)
|
val, valIsNumber := numToFloat64(ex)
|
||||||
if !valIsNumber {
|
if !valIsNumber {
|
||||||
logger.Debug("math_Tan - got parameters of wrong type")
|
logger.DebugLn("math_Tan - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,7 +310,7 @@ func (r rowContext) math_Trunc(arguments []interface{}) interface{} {
|
|||||||
case int:
|
case int:
|
||||||
return float64(val)
|
return float64(val)
|
||||||
default:
|
default:
|
||||||
logger.Debug("math_Trunc - got parameters of wrong type")
|
logger.DebugLn("math_Trunc - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -325,7 +325,7 @@ func (r rowContext) math_Atn2(arguments []interface{}) interface{} {
|
|||||||
x, xIsNumber := numToFloat64(ex2)
|
x, xIsNumber := numToFloat64(ex2)
|
||||||
|
|
||||||
if !yIsNumber || !xIsNumber {
|
if !yIsNumber || !xIsNumber {
|
||||||
logger.Debug("math_Atn2 - got parameters of wrong type")
|
logger.DebugLn("math_Atn2 - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,7 +342,7 @@ func (r rowContext) math_IntAdd(arguments []interface{}) interface{} {
|
|||||||
ex2Number, ex2IsNumber := numToInt(ex2)
|
ex2Number, ex2IsNumber := numToInt(ex2)
|
||||||
|
|
||||||
if !ex1IsNumber || !ex2IsNumber {
|
if !ex1IsNumber || !ex2IsNumber {
|
||||||
logger.Debug("math_IntAdd - got parameters of wrong type")
|
logger.DebugLn("math_IntAdd - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,7 +359,7 @@ func (r rowContext) math_IntBitAnd(arguments []interface{}) interface{} {
|
|||||||
ex2Int, ex2IsInt := numToInt(ex2)
|
ex2Int, ex2IsInt := numToInt(ex2)
|
||||||
|
|
||||||
if !ex1IsInt || !ex2IsInt {
|
if !ex1IsInt || !ex2IsInt {
|
||||||
logger.Debug("math_IntBitAnd - got parameters of wrong type")
|
logger.DebugLn("math_IntBitAnd - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,7 +376,7 @@ func (r rowContext) math_IntBitLeftShift(arguments []interface{}) interface{} {
|
|||||||
num2, num2IsInt := numToInt(ex2)
|
num2, num2IsInt := numToInt(ex2)
|
||||||
|
|
||||||
if !num1IsInt || !num2IsInt {
|
if !num1IsInt || !num2IsInt {
|
||||||
logger.Debug("math_IntBitLeftShift - got parameters of wrong type")
|
logger.DebugLn("math_IntBitLeftShift - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,7 +393,7 @@ func (r rowContext) math_IntBitOr(arguments []interface{}) interface{} {
|
|||||||
num2, num2IsInt := ex2.(int)
|
num2, num2IsInt := ex2.(int)
|
||||||
|
|
||||||
if !num1IsInt || !num2IsInt {
|
if !num1IsInt || !num2IsInt {
|
||||||
logger.Debug("math_IntBitOr - got parameters of wrong type")
|
logger.DebugLn("math_IntBitOr - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,7 +410,7 @@ func (r rowContext) math_IntBitRightShift(arguments []interface{}) interface{} {
|
|||||||
num2, num2IsInt := numToInt(ex2)
|
num2, num2IsInt := numToInt(ex2)
|
||||||
|
|
||||||
if !num1IsInt || !num2IsInt {
|
if !num1IsInt || !num2IsInt {
|
||||||
logger.Debug("math_IntBitRightShift - got parameters of wrong type")
|
logger.DebugLn("math_IntBitRightShift - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,7 +427,7 @@ func (r rowContext) math_IntBitXor(arguments []interface{}) interface{} {
|
|||||||
num2, num2IsInt := ex2.(int)
|
num2, num2IsInt := ex2.(int)
|
||||||
|
|
||||||
if !num1IsInt || !num2IsInt {
|
if !num1IsInt || !num2IsInt {
|
||||||
logger.Debug("math_IntBitXor - got parameters of wrong type")
|
logger.DebugLn("math_IntBitXor - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,7 +444,7 @@ func (r rowContext) math_IntDiv(arguments []interface{}) interface{} {
|
|||||||
num2, num2IsInt := ex2.(int)
|
num2, num2IsInt := ex2.(int)
|
||||||
|
|
||||||
if !num1IsInt || !num2IsInt || num2 == 0 {
|
if !num1IsInt || !num2IsInt || num2 == 0 {
|
||||||
logger.Debug("math_IntDiv - got parameters of wrong type or divide by zero")
|
logger.DebugLn("math_IntDiv - got parameters of wrong type or divide by zero")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -461,7 +461,7 @@ func (r rowContext) math_IntMul(arguments []interface{}) interface{} {
|
|||||||
num2, num2IsInt := ex2.(int)
|
num2, num2IsInt := ex2.(int)
|
||||||
|
|
||||||
if !num1IsInt || !num2IsInt {
|
if !num1IsInt || !num2IsInt {
|
||||||
logger.Debug("math_IntMul - got parameters of wrong type")
|
logger.DebugLn("math_IntMul - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -478,7 +478,7 @@ func (r rowContext) math_IntSub(arguments []interface{}) interface{} {
|
|||||||
num2, num2IsInt := ex2.(int)
|
num2, num2IsInt := ex2.(int)
|
||||||
|
|
||||||
if !num1IsInt || !num2IsInt {
|
if !num1IsInt || !num2IsInt {
|
||||||
logger.Debug("math_IntSub - got parameters of wrong type")
|
logger.DebugLn("math_IntSub - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -495,7 +495,7 @@ func (r rowContext) math_IntMod(arguments []interface{}) interface{} {
|
|||||||
num2, num2IsInt := ex2.(int)
|
num2, num2IsInt := ex2.(int)
|
||||||
|
|
||||||
if !num1IsInt || !num2IsInt || num2 == 0 {
|
if !num1IsInt || !num2IsInt || num2 == 0 {
|
||||||
logger.Debug("math_IntMod - got parameters of wrong type or divide by zero")
|
logger.DebugLn("math_IntMod - got parameters of wrong type or divide by zero")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,7 +512,7 @@ func (r rowContext) math_Power(arguments []interface{}) interface{} {
|
|||||||
exponent, exponentIsNumber := numToFloat64(ex2)
|
exponent, exponentIsNumber := numToFloat64(ex2)
|
||||||
|
|
||||||
if !baseIsNumber || !exponentIsNumber {
|
if !baseIsNumber || !exponentIsNumber {
|
||||||
logger.Debug("math_Power - got parameters of wrong type")
|
logger.DebugLn("math_Power - got parameters of wrong type")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -530,21 +530,21 @@ func (r rowContext) math_Log(arguments []interface{}) interface{} {
|
|||||||
baseValue, baseValueIsNumber := numToFloat64(baseValueObject)
|
baseValue, baseValueIsNumber := numToFloat64(baseValueObject)
|
||||||
|
|
||||||
if !baseValueIsNumber {
|
if !baseValueIsNumber {
|
||||||
logger.Debug("math_Log - base parameter must be a numeric value")
|
logger.DebugLn("math_Log - base parameter must be a numeric value")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if baseValue > 0 && baseValue != 1 {
|
if baseValue > 0 && baseValue != 1 {
|
||||||
base = baseValue
|
base = baseValue
|
||||||
} else {
|
} else {
|
||||||
logger.Debug("math_Log - base must be greater than 0 and not equal to 1")
|
logger.DebugLn("math_Log - base must be greater than 0 and not equal to 1")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
num, numIsNumber := numToFloat64(ex)
|
num, numIsNumber := numToFloat64(ex)
|
||||||
if !numIsNumber || num <= 0 {
|
if !numIsNumber || num <= 0 {
|
||||||
logger.Debug("math_Log - parameter must be a positive numeric value")
|
logger.DebugLn("math_Log - parameter must be a positive numeric value")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -563,21 +563,21 @@ func (r rowContext) math_NumberBin(arguments []interface{}) interface{} {
|
|||||||
binSizeValue, binSizeValueIsNumber := numToFloat64(binSizeValueObject)
|
binSizeValue, binSizeValueIsNumber := numToFloat64(binSizeValueObject)
|
||||||
|
|
||||||
if !binSizeValueIsNumber {
|
if !binSizeValueIsNumber {
|
||||||
logger.Debug("math_NumberBin - base parameter must be a numeric value")
|
logger.DebugLn("math_NumberBin - base parameter must be a numeric value")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if binSizeValue != 0 {
|
if binSizeValue != 0 {
|
||||||
binSize = binSizeValue
|
binSize = binSizeValue
|
||||||
} else {
|
} else {
|
||||||
logger.Debug("math_NumberBin - base must not be equal to 0")
|
logger.DebugLn("math_NumberBin - base must not be equal to 0")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
num, numIsNumber := numToFloat64(ex)
|
num, numIsNumber := numToFloat64(ex)
|
||||||
if !numIsNumber {
|
if !numIsNumber {
|
||||||
logger.Debug("math_NumberBin - parameter must be a numeric value")
|
logger.DebugLn("math_NumberBin - parameter must be a numeric value")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ func (r rowContext) filters_ComparisonExpression(expression parsers.ComparisonEx
|
|||||||
rightExpression, rightExpressionOk := expression.Right.(parsers.SelectItem)
|
rightExpression, rightExpressionOk := expression.Right.(parsers.SelectItem)
|
||||||
|
|
||||||
if !leftExpressionOk || !rightExpressionOk {
|
if !leftExpressionOk || !rightExpressionOk {
|
||||||
logger.Error("ComparisonExpression has incorrect Left or Right type")
|
logger.ErrorLn("ComparisonExpression has incorrect Left or Right type")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,6 +317,10 @@ func (r rowContext) applyProjection(selectItems []parsers.SelectItem) RowType {
|
|||||||
} else {
|
} else {
|
||||||
destinationName = fmt.Sprintf("$%d", index+1)
|
destinationName = fmt.Sprintf("$%d", index+1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if destinationName[0] == '@' {
|
||||||
|
destinationName = r.parameters[destinationName].(string)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
row[destinationName] = r.resolveSelectItem(selectItem)
|
row[destinationName] = r.resolveSelectItem(selectItem)
|
||||||
@@ -347,7 +351,7 @@ func (r rowContext) resolveSelectItem(selectItem parsers.SelectItem) interface{}
|
|||||||
return r.selectItem_SelectItemTypeFunctionCall(typedFunctionCall)
|
return r.selectItem_SelectItemTypeFunctionCall(typedFunctionCall)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Error("parsers.SelectItem has incorrect Value type (expected parsers.FunctionCall)")
|
logger.ErrorLn("parsers.SelectItem has incorrect Value type (expected parsers.FunctionCall)")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,7 +379,7 @@ func (r rowContext) selectItem_SelectItemTypeConstant(selectItem parsers.SelectI
|
|||||||
var ok bool
|
var ok bool
|
||||||
if typedValue, ok = selectItem.Value.(parsers.Constant); !ok {
|
if typedValue, ok = selectItem.Value.(parsers.Constant); !ok {
|
||||||
// TODO: Handle error
|
// TODO: Handle error
|
||||||
logger.Error("parsers.Constant has incorrect Value type")
|
logger.ErrorLn("parsers.Constant has incorrect Value type")
|
||||||
}
|
}
|
||||||
|
|
||||||
if typedValue.Type == parsers.ConstantTypeParameterConstant &&
|
if typedValue.Type == parsers.ConstantTypeParameterConstant &&
|
||||||
@@ -466,6 +470,12 @@ func (r rowContext) selectItem_SelectItemTypeFunctionCall(functionCall parsers.F
|
|||||||
|
|
||||||
case parsers.FunctionCallArrayConcat:
|
case parsers.FunctionCallArrayConcat:
|
||||||
return r.array_Concat(functionCall.Arguments)
|
return r.array_Concat(functionCall.Arguments)
|
||||||
|
case parsers.FunctionCallArrayContains:
|
||||||
|
return r.array_Contains(functionCall.Arguments)
|
||||||
|
case parsers.FunctionCallArrayContainsAny:
|
||||||
|
return r.array_Contains_Any(functionCall.Arguments)
|
||||||
|
case parsers.FunctionCallArrayContainsAll:
|
||||||
|
return r.array_Contains_All(functionCall.Arguments)
|
||||||
case parsers.FunctionCallArrayLength:
|
case parsers.FunctionCallArrayLength:
|
||||||
return r.array_Length(functionCall.Arguments)
|
return r.array_Length(functionCall.Arguments)
|
||||||
case parsers.FunctionCallArraySlice:
|
case parsers.FunctionCallArraySlice:
|
||||||
@@ -572,6 +582,9 @@ func (r rowContext) selectItem_SelectItemTypeField(selectItem parsers.SelectItem
|
|||||||
|
|
||||||
if len(selectItem.Path) > 1 {
|
if len(selectItem.Path) > 1 {
|
||||||
for _, pathSegment := range selectItem.Path[1:] {
|
for _, pathSegment := range selectItem.Path[1:] {
|
||||||
|
if pathSegment[0] == '@' {
|
||||||
|
pathSegment = r.parameters[pathSegment].(string)
|
||||||
|
}
|
||||||
|
|
||||||
switch nestedValue := value.(type) {
|
switch nestedValue := value.(type) {
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/pikami/cosmium/parsers"
|
"github.com/pikami/cosmium/parsers"
|
||||||
memoryexecutor "github.com/pikami/cosmium/query_executors/memory_executor"
|
memoryexecutor "github.com/pikami/cosmium/query_executors/memory_executor"
|
||||||
|
testutils "github.com/pikami/cosmium/test_utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testQueryExecute(
|
func testQueryExecute(
|
||||||
@@ -111,20 +112,8 @@ func Test_Execute(t *testing.T) {
|
|||||||
Path: []string{"c", "id"},
|
Path: []string{"c", "id"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_String("123"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
testutils.SelectItem_Constant_String("456"),
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeString,
|
|
||||||
Value: "123",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
parsers.SelectItem{
|
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeString,
|
|
||||||
Value: "456",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -35,6 +35,29 @@ func Test_Execute_Select(t *testing.T) {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Should execute SELECT with query parameters as accessor", func(t *testing.T) {
|
||||||
|
testQueryExecute(
|
||||||
|
t,
|
||||||
|
parsers.SelectStmt{
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
{Path: []string{"c", "id"}},
|
||||||
|
{Path: []string{"c", "@param"}},
|
||||||
|
},
|
||||||
|
Table: parsers.Table{Value: "c"},
|
||||||
|
Parameters: map[string]interface{}{
|
||||||
|
"@param": "pk",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mockData,
|
||||||
|
[]memoryexecutor.RowType{
|
||||||
|
map[string]interface{}{"id": "12345", "pk": 123},
|
||||||
|
map[string]interface{}{"id": "67890", "pk": 456},
|
||||||
|
map[string]interface{}{"id": "456", "pk": 456},
|
||||||
|
map[string]interface{}{"id": "123", "pk": 456},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("Should execute SELECT DISTINCT", func(t *testing.T) {
|
t.Run("Should execute SELECT DISTINCT", func(t *testing.T) {
|
||||||
testQueryExecute(
|
testQueryExecute(
|
||||||
t,
|
t,
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ func (r rowContext) strings_Left(arguments []interface{}) string {
|
|||||||
lengthEx := r.resolveSelectItem(arguments[1].(parsers.SelectItem))
|
lengthEx := r.resolveSelectItem(arguments[1].(parsers.SelectItem))
|
||||||
|
|
||||||
if length, ok = lengthEx.(int); !ok {
|
if length, ok = lengthEx.(int); !ok {
|
||||||
logger.Error("strings_Left - got parameters of wrong type")
|
logger.ErrorLn("strings_Left - got parameters of wrong type")
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,7 +158,7 @@ func (r rowContext) strings_Replicate(arguments []interface{}) string {
|
|||||||
timesEx := r.resolveSelectItem(arguments[1].(parsers.SelectItem))
|
timesEx := r.resolveSelectItem(arguments[1].(parsers.SelectItem))
|
||||||
|
|
||||||
if times, ok = timesEx.(int); !ok {
|
if times, ok = timesEx.(int); !ok {
|
||||||
logger.Error("strings_Replicate - got parameters of wrong type")
|
logger.ErrorLn("strings_Replicate - got parameters of wrong type")
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +191,7 @@ func (r rowContext) strings_Right(arguments []interface{}) string {
|
|||||||
lengthEx := r.resolveSelectItem(arguments[1].(parsers.SelectItem))
|
lengthEx := r.resolveSelectItem(arguments[1].(parsers.SelectItem))
|
||||||
|
|
||||||
if length, ok = lengthEx.(int); !ok {
|
if length, ok = lengthEx.(int); !ok {
|
||||||
logger.Error("strings_Right - got parameters of wrong type")
|
logger.ErrorLn("strings_Right - got parameters of wrong type")
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,11 +220,11 @@ func (r rowContext) strings_Substring(arguments []interface{}) string {
|
|||||||
lengthEx := r.resolveSelectItem(arguments[2].(parsers.SelectItem))
|
lengthEx := r.resolveSelectItem(arguments[2].(parsers.SelectItem))
|
||||||
|
|
||||||
if startPos, ok = startPosEx.(int); !ok {
|
if startPos, ok = startPosEx.(int); !ok {
|
||||||
logger.Error("strings_Substring - got start parameters of wrong type")
|
logger.ErrorLn("strings_Substring - got start parameters of wrong type")
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
if length, ok = lengthEx.(int); !ok {
|
if length, ok = lengthEx.(int); !ok {
|
||||||
logger.Error("strings_Substring - got length parameters of wrong type")
|
logger.ErrorLn("strings_Substring - got length parameters of wrong type")
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,7 +264,7 @@ func (r rowContext) parseString(argument interface{}) string {
|
|||||||
return str1
|
return str1
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Error("StringEquals got parameters of wrong type")
|
logger.ErrorLn("StringEquals got parameters of wrong type")
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/pikami/cosmium/parsers"
|
"github.com/pikami/cosmium/parsers"
|
||||||
memoryexecutor "github.com/pikami/cosmium/query_executors/memory_executor"
|
memoryexecutor "github.com/pikami/cosmium/query_executors/memory_executor"
|
||||||
|
testutils "github.com/pikami/cosmium/test_utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_Execute_StringFunctions(t *testing.T) {
|
func Test_Execute_StringFunctions(t *testing.T) {
|
||||||
@@ -33,20 +34,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "pk"},
|
Path: []string{"c", "pk"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_String("aaa"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
testutils.SelectItem_Constant_Bool(true),
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeString,
|
|
||||||
Value: "aaa",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
parsers.SelectItem{
|
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeBoolean,
|
|
||||||
Value: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -81,13 +70,7 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "pk"},
|
Path: []string{"c", "pk"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_String("aaa"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeString,
|
|
||||||
Value: "aaa",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -119,20 +102,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "id"},
|
Path: []string{"c", "id"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_String(" "),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
testutils.SelectItem_Constant_Int(123),
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeString,
|
|
||||||
Value: " ",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
parsers.SelectItem{
|
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeString,
|
|
||||||
Value: 123,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
parsers.SelectItem{
|
parsers.SelectItem{
|
||||||
Path: []string{"c", "pk"},
|
Path: []string{"c", "pk"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
@@ -171,20 +142,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "id"},
|
Path: []string{"c", "id"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_String("2"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
testutils.SelectItem_Constant_Bool(true),
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeString,
|
|
||||||
Value: "2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
parsers.SelectItem{
|
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeBoolean,
|
|
||||||
Value: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -219,20 +178,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "id"},
|
Path: []string{"c", "id"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_String("3"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
testutils.SelectItem_Constant_Bool(true),
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeString,
|
|
||||||
Value: "3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
parsers.SelectItem{
|
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeBoolean,
|
|
||||||
Value: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -267,20 +214,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "id"},
|
Path: []string{"c", "id"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_String("1"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
testutils.SelectItem_Constant_Bool(true),
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeString,
|
|
||||||
Value: "1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
parsers.SelectItem{
|
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeBoolean,
|
|
||||||
Value: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -315,20 +250,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "str"},
|
Path: []string{"c", "str"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_String("o"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
testutils.SelectItem_Constant_Int(4),
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeString,
|
|
||||||
Value: "o",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
parsers.SelectItem{
|
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeInteger,
|
|
||||||
Value: 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -397,13 +320,7 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "str"},
|
Path: []string{"c", "str"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_Int(3),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeInteger,
|
|
||||||
Value: 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -506,20 +423,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "str"},
|
Path: []string{"c", "str"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_String("world"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
testutils.SelectItem_Constant_String("universe"),
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeString,
|
|
||||||
Value: "world",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
parsers.SelectItem{
|
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeString,
|
|
||||||
Value: "universe",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -554,13 +459,7 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "str"},
|
Path: []string{"c", "str"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_Int(3),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeInteger,
|
|
||||||
Value: 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -629,13 +528,7 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "str"},
|
Path: []string{"c", "str"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_Int(3),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeInteger,
|
|
||||||
Value: 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -704,20 +597,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
|||||||
Path: []string{"c", "str"},
|
Path: []string{"c", "str"},
|
||||||
Type: parsers.SelectItemTypeField,
|
Type: parsers.SelectItemTypeField,
|
||||||
},
|
},
|
||||||
parsers.SelectItem{
|
testutils.SelectItem_Constant_Int(2),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
testutils.SelectItem_Constant_Int(4),
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeInteger,
|
|
||||||
Value: 2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
parsers.SelectItem{
|
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{
|
|
||||||
Type: parsers.ConstantTypeInteger,
|
|
||||||
Value: 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/pikami/cosmium/parsers"
|
"github.com/pikami/cosmium/parsers"
|
||||||
memoryexecutor "github.com/pikami/cosmium/query_executors/memory_executor"
|
memoryexecutor "github.com/pikami/cosmium/query_executors/memory_executor"
|
||||||
|
testutils "github.com/pikami/cosmium/test_utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_Execute_Where(t *testing.T) {
|
func Test_Execute_Where(t *testing.T) {
|
||||||
@@ -26,10 +27,7 @@ func Test_Execute_Where(t *testing.T) {
|
|||||||
Filters: parsers.ComparisonExpression{
|
Filters: parsers.ComparisonExpression{
|
||||||
Operation: "=",
|
Operation: "=",
|
||||||
Left: parsers.SelectItem{Path: []string{"c", "isCool"}},
|
Left: parsers.SelectItem{Path: []string{"c", "isCool"}},
|
||||||
Right: parsers.SelectItem{
|
Right: testutils.SelectItem_Constant_Bool(true),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: true},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mockData,
|
mockData,
|
||||||
@@ -52,10 +50,7 @@ func Test_Execute_Where(t *testing.T) {
|
|||||||
Filters: parsers.ComparisonExpression{
|
Filters: parsers.ComparisonExpression{
|
||||||
Operation: "=",
|
Operation: "=",
|
||||||
Left: parsers.SelectItem{Path: []string{"c", "id"}},
|
Left: parsers.SelectItem{Path: []string{"c", "id"}},
|
||||||
Right: parsers.SelectItem{
|
Right: testutils.SelectItem_Constant_Parameter("@param_id"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{Type: parsers.ConstantTypeParameterConstant, Value: "@param_id"},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Parameters: map[string]interface{}{
|
Parameters: map[string]interface{}{
|
||||||
"@param_id": "456",
|
"@param_id": "456",
|
||||||
@@ -83,18 +78,12 @@ func Test_Execute_Where(t *testing.T) {
|
|||||||
parsers.ComparisonExpression{
|
parsers.ComparisonExpression{
|
||||||
Operation: "=",
|
Operation: "=",
|
||||||
Left: parsers.SelectItem{Path: []string{"c", "id"}},
|
Left: parsers.SelectItem{Path: []string{"c", "id"}},
|
||||||
Right: parsers.SelectItem{
|
Right: testutils.SelectItem_Constant_String("67890"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{Type: parsers.ConstantTypeString, Value: "67890"},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
parsers.ComparisonExpression{
|
parsers.ComparisonExpression{
|
||||||
Operation: "=",
|
Operation: "=",
|
||||||
Left: parsers.SelectItem{Path: []string{"c", "pk"}},
|
Left: parsers.SelectItem{Path: []string{"c", "pk"}},
|
||||||
Right: parsers.SelectItem{
|
Right: testutils.SelectItem_Constant_Int(456),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{Type: parsers.ConstantTypeInteger, Value: 456},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -120,10 +109,7 @@ func Test_Execute_Where(t *testing.T) {
|
|||||||
parsers.ComparisonExpression{
|
parsers.ComparisonExpression{
|
||||||
Operation: "=",
|
Operation: "=",
|
||||||
Left: parsers.SelectItem{Path: []string{"c", "isCool"}},
|
Left: parsers.SelectItem{Path: []string{"c", "isCool"}},
|
||||||
Right: parsers.SelectItem{
|
Right: testutils.SelectItem_Constant_Bool(true),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: true},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
parsers.LogicalExpression{
|
parsers.LogicalExpression{
|
||||||
Operation: parsers.LogicalExpressionTypeOr,
|
Operation: parsers.LogicalExpressionTypeOr,
|
||||||
@@ -131,18 +117,12 @@ func Test_Execute_Where(t *testing.T) {
|
|||||||
parsers.ComparisonExpression{
|
parsers.ComparisonExpression{
|
||||||
Operation: "=",
|
Operation: "=",
|
||||||
Left: parsers.SelectItem{Path: []string{"c", "id"}},
|
Left: parsers.SelectItem{Path: []string{"c", "id"}},
|
||||||
Right: parsers.SelectItem{
|
Right: testutils.SelectItem_Constant_String("123"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{Type: parsers.ConstantTypeString, Value: "123"},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
parsers.ComparisonExpression{
|
parsers.ComparisonExpression{
|
||||||
Operation: "=",
|
Operation: "=",
|
||||||
Left: parsers.SelectItem{Path: []string{"c", "id"}},
|
Left: parsers.SelectItem{Path: []string{"c", "id"}},
|
||||||
Right: parsers.SelectItem{
|
Right: testutils.SelectItem_Constant_String("456"),
|
||||||
Type: parsers.SelectItemTypeConstant,
|
|
||||||
Value: parsers.Constant{Type: parsers.ConstantTypeString, Value: "456"},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
89
sharedlibrary/collections.go
Normal file
89
sharedlibrary/collections.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
repositorymodels "github.com/pikami/cosmium/internal/repository_models"
|
||||||
|
)
|
||||||
|
|
||||||
|
//export CreateCollection
|
||||||
|
func CreateCollection(serverName *C.char, databaseId *C.char, collectionJson *C.char) int {
|
||||||
|
serverNameStr := C.GoString(serverName)
|
||||||
|
databaseIdStr := C.GoString(databaseId)
|
||||||
|
collectionStr := C.GoString(collectionJson)
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
var serverInstance *ServerInstance
|
||||||
|
if serverInstance, ok = getInstance(serverNameStr); !ok {
|
||||||
|
return ResponseServerInstanceNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
var collection repositorymodels.Collection
|
||||||
|
err := json.Unmarshal([]byte(collectionStr), &collection)
|
||||||
|
if err != nil {
|
||||||
|
return ResponseFailedToParseRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
_, code := serverInstance.repository.CreateCollection(databaseIdStr, collection)
|
||||||
|
|
||||||
|
return repositoryStatusToResponseCode(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export GetCollection
|
||||||
|
func GetCollection(serverName *C.char, databaseId *C.char, collectionId *C.char) *C.char {
|
||||||
|
serverNameStr := C.GoString(serverName)
|
||||||
|
databaseIdStr := C.GoString(databaseId)
|
||||||
|
collectionIdStr := C.GoString(collectionId)
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
var serverInstance *ServerInstance
|
||||||
|
if serverInstance, ok = getInstance(serverNameStr); !ok {
|
||||||
|
return C.CString("")
|
||||||
|
}
|
||||||
|
|
||||||
|
collection, code := serverInstance.repository.GetCollection(databaseIdStr, collectionIdStr)
|
||||||
|
if code != repositorymodels.StatusOk {
|
||||||
|
return C.CString("")
|
||||||
|
}
|
||||||
|
|
||||||
|
collectionJson, _ := json.Marshal(collection)
|
||||||
|
return C.CString(string(collectionJson))
|
||||||
|
}
|
||||||
|
|
||||||
|
//export GetAllCollections
|
||||||
|
func GetAllCollections(serverName *C.char, databaseId *C.char) *C.char {
|
||||||
|
serverNameStr := C.GoString(serverName)
|
||||||
|
databaseIdStr := C.GoString(databaseId)
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
var serverInstance *ServerInstance
|
||||||
|
if serverInstance, ok = getInstance(serverNameStr); !ok {
|
||||||
|
return C.CString("")
|
||||||
|
}
|
||||||
|
|
||||||
|
collections, code := serverInstance.repository.GetAllCollections(databaseIdStr)
|
||||||
|
if code != repositorymodels.StatusOk {
|
||||||
|
return C.CString("")
|
||||||
|
}
|
||||||
|
|
||||||
|
collectionsJson, _ := json.Marshal(collections)
|
||||||
|
return C.CString(string(collectionsJson))
|
||||||
|
}
|
||||||
|
|
||||||
|
//export DeleteCollection
|
||||||
|
func DeleteCollection(serverName *C.char, databaseId *C.char, collectionId *C.char) int {
|
||||||
|
serverNameStr := C.GoString(serverName)
|
||||||
|
databaseIdStr := C.GoString(databaseId)
|
||||||
|
collectionIdStr := C.GoString(collectionId)
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
var serverInstance *ServerInstance
|
||||||
|
if serverInstance, ok = getInstance(serverNameStr); !ok {
|
||||||
|
return ResponseServerInstanceNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
code := serverInstance.repository.DeleteCollection(databaseIdStr, collectionIdStr)
|
||||||
|
|
||||||
|
return repositoryStatusToResponseCode(code)
|
||||||
|
}
|
||||||
89
sharedlibrary/databases.go
Normal file
89
sharedlibrary/databases.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
repositorymodels "github.com/pikami/cosmium/internal/repository_models"
|
||||||
|
)
|
||||||
|
|
||||||
|
//export CreateDatabase
|
||||||
|
func CreateDatabase(serverName *C.char, databaseJson *C.char) int {
|
||||||
|
serverNameStr := C.GoString(serverName)
|
||||||
|
databaseStr := C.GoString(databaseJson)
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
var serverInstance *ServerInstance
|
||||||
|
if serverInstance, ok = getInstance(serverNameStr); !ok {
|
||||||
|
return ResponseServerInstanceNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
var database repositorymodels.Database
|
||||||
|
err := json.Unmarshal([]byte(databaseStr), &database)
|
||||||
|
if err != nil {
|
||||||
|
return ResponseFailedToParseRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
_, code := serverInstance.repository.CreateDatabase(database)
|
||||||
|
|
||||||
|
return repositoryStatusToResponseCode(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export GetDatabase
|
||||||
|
func GetDatabase(serverName *C.char, databaseId *C.char) *C.char {
|
||||||
|
serverNameStr := C.GoString(serverName)
|
||||||
|
databaseIdStr := C.GoString(databaseId)
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
var serverInstance *ServerInstance
|
||||||
|
if serverInstance, ok = getInstance(serverNameStr); !ok {
|
||||||
|
return C.CString("")
|
||||||
|
}
|
||||||
|
|
||||||
|
database, code := serverInstance.repository.GetDatabase(databaseIdStr)
|
||||||
|
if code != repositorymodels.StatusOk {
|
||||||
|
return C.CString("")
|
||||||
|
}
|
||||||
|
|
||||||
|
databaseJson, _ := json.Marshal(database)
|
||||||
|
return C.CString(string(databaseJson))
|
||||||
|
}
|
||||||
|
|
||||||
|
//export GetAllDatabases
|
||||||
|
func GetAllDatabases(serverName *C.char) *C.char {
|
||||||
|
serverNameStr := C.GoString(serverName)
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
var serverInstance *ServerInstance
|
||||||
|
if serverInstance, ok = getInstance(serverNameStr); !ok {
|
||||||
|
return C.CString("")
|
||||||
|
}
|
||||||
|
|
||||||
|
databases, code := serverInstance.repository.GetAllDatabases()
|
||||||
|
if code != repositorymodels.StatusOk {
|
||||||
|
return C.CString("")
|
||||||
|
}
|
||||||
|
|
||||||
|
databasesJson, err := json.Marshal(databases)
|
||||||
|
if err != nil {
|
||||||
|
return C.CString("")
|
||||||
|
}
|
||||||
|
|
||||||
|
return C.CString(string(databasesJson))
|
||||||
|
}
|
||||||
|
|
||||||
|
//export DeleteDatabase
|
||||||
|
func DeleteDatabase(serverName *C.char, databaseId *C.char) int {
|
||||||
|
serverNameStr := C.GoString(serverName)
|
||||||
|
databaseIdStr := C.GoString(databaseId)
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
var serverInstance *ServerInstance
|
||||||
|
if serverInstance, ok = getInstance(serverNameStr); !ok {
|
||||||
|
return ResponseServerInstanceNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
code := serverInstance.repository.DeleteDatabase(databaseIdStr)
|
||||||
|
|
||||||
|
return repositoryStatusToResponseCode(code)
|
||||||
|
}
|
||||||
122
sharedlibrary/documents.go
Normal file
122
sharedlibrary/documents.go
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
repositorymodels "github.com/pikami/cosmium/internal/repository_models"
|
||||||
|
)
|
||||||
|
|
||||||
|
//export CreateDocument
|
||||||
|
func CreateDocument(serverName *C.char, databaseId *C.char, collectionId *C.char, documentJson *C.char) int {
|
||||||
|
serverNameStr := C.GoString(serverName)
|
||||||
|
databaseIdStr := C.GoString(databaseId)
|
||||||
|
collectionIdStr := C.GoString(collectionId)
|
||||||
|
documentStr := C.GoString(documentJson)
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
var serverInstance *ServerInstance
|
||||||
|
if serverInstance, ok = getInstance(serverNameStr); !ok {
|
||||||
|
return ResponseServerInstanceNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
var document repositorymodels.Document
|
||||||
|
err := json.Unmarshal([]byte(documentStr), &document)
|
||||||
|
if err != nil {
|
||||||
|
return ResponseFailedToParseRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
_, code := serverInstance.repository.CreateDocument(databaseIdStr, collectionIdStr, document)
|
||||||
|
|
||||||
|
return repositoryStatusToResponseCode(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export GetDocument
|
||||||
|
func GetDocument(serverName *C.char, databaseId *C.char, collectionId *C.char, documentId *C.char) *C.char {
|
||||||
|
serverNameStr := C.GoString(serverName)
|
||||||
|
databaseIdStr := C.GoString(databaseId)
|
||||||
|
collectionIdStr := C.GoString(collectionId)
|
||||||
|
documentIdStr := C.GoString(documentId)
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
var serverInstance *ServerInstance
|
||||||
|
if serverInstance, ok = getInstance(serverNameStr); !ok {
|
||||||
|
return C.CString("")
|
||||||
|
}
|
||||||
|
|
||||||
|
document, code := serverInstance.repository.GetDocument(databaseIdStr, collectionIdStr, documentIdStr)
|
||||||
|
if code != repositorymodels.StatusOk {
|
||||||
|
return C.CString("")
|
||||||
|
}
|
||||||
|
|
||||||
|
documentJson, _ := json.Marshal(document)
|
||||||
|
return C.CString(string(documentJson))
|
||||||
|
}
|
||||||
|
|
||||||
|
//export GetAllDocuments
|
||||||
|
func GetAllDocuments(serverName *C.char, databaseId *C.char, collectionId *C.char) *C.char {
|
||||||
|
serverNameStr := C.GoString(serverName)
|
||||||
|
databaseIdStr := C.GoString(databaseId)
|
||||||
|
collectionIdStr := C.GoString(collectionId)
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
var serverInstance *ServerInstance
|
||||||
|
if serverInstance, ok = getInstance(serverNameStr); !ok {
|
||||||
|
return C.CString("")
|
||||||
|
}
|
||||||
|
|
||||||
|
documents, code := serverInstance.repository.GetAllDocuments(databaseIdStr, collectionIdStr)
|
||||||
|
if code != repositorymodels.StatusOk {
|
||||||
|
return C.CString("")
|
||||||
|
}
|
||||||
|
|
||||||
|
documentsJson, _ := json.Marshal(documents)
|
||||||
|
return C.CString(string(documentsJson))
|
||||||
|
}
|
||||||
|
|
||||||
|
//export UpdateDocument
|
||||||
|
func UpdateDocument(serverName *C.char, databaseId *C.char, collectionId *C.char, documentId *C.char, documentJson *C.char) int {
|
||||||
|
serverNameStr := C.GoString(serverName)
|
||||||
|
databaseIdStr := C.GoString(databaseId)
|
||||||
|
collectionIdStr := C.GoString(collectionId)
|
||||||
|
documentIdStr := C.GoString(documentId)
|
||||||
|
documentStr := C.GoString(documentJson)
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
var serverInstance *ServerInstance
|
||||||
|
if serverInstance, ok = getInstance(serverNameStr); !ok {
|
||||||
|
return ResponseServerInstanceNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
var document repositorymodels.Document
|
||||||
|
err := json.Unmarshal([]byte(documentStr), &document)
|
||||||
|
if err != nil {
|
||||||
|
return ResponseFailedToParseRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
code := serverInstance.repository.DeleteDocument(databaseIdStr, collectionIdStr, documentIdStr)
|
||||||
|
if code != repositorymodels.StatusOk {
|
||||||
|
return repositoryStatusToResponseCode(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, code = serverInstance.repository.CreateDocument(databaseIdStr, collectionIdStr, document)
|
||||||
|
return repositoryStatusToResponseCode(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export DeleteDocument
|
||||||
|
func DeleteDocument(serverName *C.char, databaseId *C.char, collectionId *C.char, documentId *C.char) int {
|
||||||
|
serverNameStr := C.GoString(serverName)
|
||||||
|
databaseIdStr := C.GoString(databaseId)
|
||||||
|
collectionIdStr := C.GoString(collectionId)
|
||||||
|
documentIdStr := C.GoString(documentId)
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
var serverInstance *ServerInstance
|
||||||
|
if serverInstance, ok = getInstance(serverNameStr); !ok {
|
||||||
|
return ResponseServerInstanceNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
code := serverInstance.repository.DeleteDocument(databaseIdStr, collectionIdStr, documentIdStr)
|
||||||
|
|
||||||
|
return repositoryStatusToResponseCode(code)
|
||||||
|
}
|
||||||
86
sharedlibrary/shared.go
Normal file
86
sharedlibrary/shared.go
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/pikami/cosmium/api"
|
||||||
|
"github.com/pikami/cosmium/internal/repositories"
|
||||||
|
repositorymodels "github.com/pikami/cosmium/internal/repository_models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerInstance struct {
|
||||||
|
server *api.ApiServer
|
||||||
|
repository *repositories.DataRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
var serverInstances map[string]*ServerInstance
|
||||||
|
var mutex sync.RWMutex
|
||||||
|
|
||||||
|
const (
|
||||||
|
ResponseSuccess = 0
|
||||||
|
|
||||||
|
ResponseUnknown = 100
|
||||||
|
ResponseFailedToParseConfiguration = 101
|
||||||
|
ResponseFailedToLoadState = 102
|
||||||
|
ResponseFailedToParseRequest = 103
|
||||||
|
ResponseServerInstanceAlreadyExists = 104
|
||||||
|
ResponseServerInstanceNotFound = 105
|
||||||
|
|
||||||
|
ResponseRepositoryNotFound = 200
|
||||||
|
ResponseRepositoryConflict = 201
|
||||||
|
ResponseRepositoryBadRequest = 202
|
||||||
|
)
|
||||||
|
|
||||||
|
func getInstance(serverName string) (*ServerInstance, bool) {
|
||||||
|
mutex.RLock()
|
||||||
|
defer mutex.RUnlock()
|
||||||
|
|
||||||
|
if serverInstances == nil {
|
||||||
|
serverInstances = make(map[string]*ServerInstance)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
var serverInstance *ServerInstance
|
||||||
|
if serverInstance, ok = serverInstances[serverName]; !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return serverInstance, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func addInstance(serverName string, serverInstance *ServerInstance) {
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
|
||||||
|
if serverInstances == nil {
|
||||||
|
serverInstances = make(map[string]*ServerInstance)
|
||||||
|
}
|
||||||
|
|
||||||
|
serverInstances[serverName] = serverInstance
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeInstance(serverName string) {
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
|
||||||
|
if serverInstances == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(serverInstances, serverName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func repositoryStatusToResponseCode(status repositorymodels.RepositoryStatus) int {
|
||||||
|
switch status {
|
||||||
|
case repositorymodels.StatusOk:
|
||||||
|
return ResponseSuccess
|
||||||
|
case repositorymodels.StatusNotFound:
|
||||||
|
return ResponseRepositoryNotFound
|
||||||
|
case repositorymodels.Conflict:
|
||||||
|
return ResponseRepositoryConflict
|
||||||
|
case repositorymodels.BadRequest:
|
||||||
|
return ResponseRepositoryBadRequest
|
||||||
|
default:
|
||||||
|
return ResponseUnknown
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,32 +9,12 @@ import (
|
|||||||
"github.com/pikami/cosmium/internal/repositories"
|
"github.com/pikami/cosmium/internal/repositories"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServerInstance struct {
|
|
||||||
server *api.ApiServer
|
|
||||||
repository *repositories.DataRepository
|
|
||||||
}
|
|
||||||
|
|
||||||
var serverInstances map[string]*ServerInstance
|
|
||||||
|
|
||||||
const (
|
|
||||||
ResponseSuccess = 0
|
|
||||||
ResponseUnknown = 1
|
|
||||||
ResponseServerInstanceAlreadyExists = 2
|
|
||||||
ResponseFailedToParseConfiguration = 3
|
|
||||||
ResponseServerInstanceNotFound = 4
|
|
||||||
ResponseFailedToLoadState = 5
|
|
||||||
)
|
|
||||||
|
|
||||||
//export CreateServerInstance
|
//export CreateServerInstance
|
||||||
func CreateServerInstance(serverName *C.char, configurationJSON *C.char) int {
|
func CreateServerInstance(serverName *C.char, configurationJSON *C.char) int {
|
||||||
configStr := C.GoString(configurationJSON)
|
configStr := C.GoString(configurationJSON)
|
||||||
serverNameStr := C.GoString(serverName)
|
serverNameStr := C.GoString(serverName)
|
||||||
|
|
||||||
if serverInstances == nil {
|
if _, ok := getInstance(serverNameStr); ok {
|
||||||
serverInstances = make(map[string]*ServerInstance)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := serverInstances[serverNameStr]; ok {
|
|
||||||
return ResponseServerInstanceAlreadyExists
|
return ResponseServerInstanceAlreadyExists
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,19 +35,21 @@ func CreateServerInstance(serverName *C.char, configurationJSON *C.char) int {
|
|||||||
server := api.NewApiServer(repository, configuration)
|
server := api.NewApiServer(repository, configuration)
|
||||||
server.Start()
|
server.Start()
|
||||||
|
|
||||||
serverInstances[serverNameStr] = &ServerInstance{
|
addInstance(serverNameStr, &ServerInstance{
|
||||||
server: server,
|
server: server,
|
||||||
repository: repository,
|
repository: repository,
|
||||||
}
|
})
|
||||||
|
|
||||||
return ResponseSuccess
|
return ResponseSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
//export StopServerInstance
|
//export StopServerInstance
|
||||||
func StopServerInstance(serverName *C.char) int {
|
func StopServerInstance(serverName *C.char) int {
|
||||||
if serverInstance, ok := serverInstances[C.GoString(serverName)]; ok {
|
serverNameStr := C.GoString(serverName)
|
||||||
|
|
||||||
|
if serverInstance, ok := getInstance(serverNameStr); ok {
|
||||||
serverInstance.server.Stop()
|
serverInstance.server.Stop()
|
||||||
delete(serverInstances, C.GoString(serverName))
|
removeInstance(serverNameStr)
|
||||||
return ResponseSuccess
|
return ResponseSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +58,9 @@ func StopServerInstance(serverName *C.char) int {
|
|||||||
|
|
||||||
//export GetServerInstanceState
|
//export GetServerInstanceState
|
||||||
func GetServerInstanceState(serverName *C.char) *C.char {
|
func GetServerInstanceState(serverName *C.char) *C.char {
|
||||||
if serverInstance, ok := serverInstances[C.GoString(serverName)]; ok {
|
serverNameStr := C.GoString(serverName)
|
||||||
|
|
||||||
|
if serverInstance, ok := getInstance(serverNameStr); ok {
|
||||||
stateJSON, err := serverInstance.repository.GetState()
|
stateJSON, err := serverInstance.repository.GetState()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -92,7 +76,7 @@ func LoadServerInstanceState(serverName *C.char, stateJSON *C.char) int {
|
|||||||
serverNameStr := C.GoString(serverName)
|
serverNameStr := C.GoString(serverName)
|
||||||
stateJSONStr := C.GoString(stateJSON)
|
stateJSONStr := C.GoString(stateJSON)
|
||||||
|
|
||||||
if serverInstance, ok := serverInstances[serverNameStr]; ok {
|
if serverInstance, ok := getInstance(serverNameStr); ok {
|
||||||
err := serverInstance.repository.LoadStateJSON(stateJSONStr)
|
err := serverInstance.repository.LoadStateJSON(stateJSONStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ResponseFailedToLoadState
|
return ResponseFailedToLoadState
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
#include "shared.h"
|
#include "shared.h"
|
||||||
|
|
||||||
void test_CreateServerInstance();
|
int test_CreateServerInstance();
|
||||||
void test_StopServerInstance();
|
int test_StopServerInstance();
|
||||||
void test_ServerInstanceStateMethods();
|
int test_ServerInstanceStateMethods();
|
||||||
|
int test_Databases();
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
@@ -21,9 +22,24 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
printf("Running tests for library: %s\n", libPath);
|
printf("Running tests for library: %s\n", libPath);
|
||||||
test_CreateServerInstance();
|
int results[] = {
|
||||||
test_ServerInstanceStateMethods();
|
test_CreateServerInstance(),
|
||||||
test_StopServerInstance();
|
test_Databases(),
|
||||||
|
test_ServerInstanceStateMethods(),
|
||||||
|
test_StopServerInstance(),
|
||||||
|
};
|
||||||
|
|
||||||
|
int numTests = sizeof(results) / sizeof(results[0]);
|
||||||
|
int numPassed = 0;
|
||||||
|
for (int i = 0; i < numTests; i++)
|
||||||
|
{
|
||||||
|
if (results[i])
|
||||||
|
{
|
||||||
|
numPassed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Tests passed: %d/%d\n", numPassed, numTests);
|
||||||
|
|
||||||
dlclose(handle);
|
dlclose(handle);
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "shared.h"
|
#include "shared.h"
|
||||||
|
|
||||||
void test_CreateServerInstance()
|
int test_CreateServerInstance()
|
||||||
{
|
{
|
||||||
typedef int (*CreateServerInstanceFn)(char *, char *);
|
typedef int (*CreateServerInstanceFn)(char *, char *);
|
||||||
CreateServerInstanceFn CreateServerInstance = (CreateServerInstanceFn)load_function("CreateServerInstance");
|
CreateServerInstanceFn CreateServerInstance = (CreateServerInstanceFn)load_function("CreateServerInstance");
|
||||||
@@ -8,7 +8,7 @@ void test_CreateServerInstance()
|
|||||||
if (!CreateServerInstance)
|
if (!CreateServerInstance)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Failed to find CreateServerInstance function\n");
|
fprintf(stderr, "Failed to find CreateServerInstance function\n");
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *serverName = "TestServer";
|
char *serverName = "TestServer";
|
||||||
@@ -22,5 +22,8 @@ void test_CreateServerInstance()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
printf("CreateServerInstance: FAILED (result = %d)\n", result);
|
printf("CreateServerInstance: FAILED (result = %d)\n", result);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
47
sharedlibrary/tests/test_databases.c
Normal file
47
sharedlibrary/tests/test_databases.c
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#include "shared.h"
|
||||||
|
|
||||||
|
int test_Databases()
|
||||||
|
{
|
||||||
|
typedef int (*CreateDatabaseFn)(char *, char *);
|
||||||
|
CreateDatabaseFn CreateDatabase = (CreateDatabaseFn)load_function("CreateDatabase");
|
||||||
|
if (!CreateDatabase)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Failed to find CreateDatabase function\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *serverName = "TestServer";
|
||||||
|
char *configJSON = "{\"id\":\"test-db\"}";
|
||||||
|
|
||||||
|
int result = CreateDatabase(serverName, configJSON);
|
||||||
|
if (result == 0)
|
||||||
|
{
|
||||||
|
printf("CreateDatabase: SUCCESS\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("CreateDatabase: FAILED (result = %d)\n", result);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef char *(*GetDatabaseFn)(char *, char *);
|
||||||
|
GetDatabaseFn GetDatabase = (GetDatabaseFn)load_function("GetDatabase");
|
||||||
|
if (!GetDatabase)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Failed to find GetDatabase function\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *database = GetDatabase(serverName, "test-db");
|
||||||
|
if (database)
|
||||||
|
{
|
||||||
|
printf("GetDatabase: SUCCESS (database = %s)\n", database);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("GetDatabase: FAILED\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
#include "shared.h"
|
#include "shared.h"
|
||||||
|
|
||||||
void test_ServerInstanceStateMethods()
|
int test_ServerInstanceStateMethods()
|
||||||
{
|
{
|
||||||
typedef int (*LoadServerInstanceStateFn)(char *, char *);
|
typedef int (*LoadServerInstanceStateFn)(char *, char *);
|
||||||
LoadServerInstanceStateFn LoadServerInstanceState = (LoadServerInstanceStateFn)load_function("LoadServerInstanceState");
|
LoadServerInstanceStateFn LoadServerInstanceState = (LoadServerInstanceStateFn)load_function("LoadServerInstanceState");
|
||||||
if (!LoadServerInstanceState)
|
if (!LoadServerInstanceState)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Failed to find LoadServerInstanceState function\n");
|
fprintf(stderr, "Failed to find LoadServerInstanceState function\n");
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *serverName = "TestServer";
|
char *serverName = "TestServer";
|
||||||
@@ -20,6 +20,7 @@ void test_ServerInstanceStateMethods()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
printf("LoadServerInstanceState: FAILED (result = %d)\n", result);
|
printf("LoadServerInstanceState: FAILED (result = %d)\n", result);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef char *(*GetServerInstanceStateFn)(char *);
|
typedef char *(*GetServerInstanceStateFn)(char *);
|
||||||
@@ -27,7 +28,7 @@ void test_ServerInstanceStateMethods()
|
|||||||
if (!GetServerInstanceState)
|
if (!GetServerInstanceState)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Failed to find GetServerInstanceState function\n");
|
fprintf(stderr, "Failed to find GetServerInstanceState function\n");
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *state = GetServerInstanceState(serverName);
|
char *state = GetServerInstanceState(serverName);
|
||||||
@@ -38,6 +39,7 @@ void test_ServerInstanceStateMethods()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
printf("GetServerInstanceState: FAILED\n");
|
printf("GetServerInstanceState: FAILED\n");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *expected_state = "{\"databases\":{\"test-db\":{\"id\":\"test-db\",\"_ts\":0,\"_rid\":\"\",\"_etag\":\"\",\"_self\":\"\"}},\"collections\":{\"test-db\":{}},\"documents\":{\"test-db\":{}}}";
|
const char *expected_state = "{\"databases\":{\"test-db\":{\"id\":\"test-db\",\"_ts\":0,\"_rid\":\"\",\"_etag\":\"\",\"_self\":\"\"}},\"collections\":{\"test-db\":{}},\"documents\":{\"test-db\":{}}}";
|
||||||
@@ -45,7 +47,7 @@ void test_ServerInstanceStateMethods()
|
|||||||
if (!compact_state)
|
if (!compact_state)
|
||||||
{
|
{
|
||||||
free(state);
|
free(state);
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(compact_state, expected_state) == 0)
|
if (strcmp(compact_state, expected_state) == 0)
|
||||||
@@ -57,8 +59,10 @@ void test_ServerInstanceStateMethods()
|
|||||||
printf("GetServerInstanceState: State does not match expected value.\n");
|
printf("GetServerInstanceState: State does not match expected value.\n");
|
||||||
printf("Expected: %s\n", expected_state);
|
printf("Expected: %s\n", expected_state);
|
||||||
printf("Actual: %s\n", compact_state);
|
printf("Actual: %s\n", compact_state);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(state);
|
free(state);
|
||||||
free(compact_state);
|
free(compact_state);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "shared.h"
|
#include "shared.h"
|
||||||
|
|
||||||
void test_StopServerInstance()
|
int test_StopServerInstance()
|
||||||
{
|
{
|
||||||
typedef int (*StopServerInstanceFn)(char *);
|
typedef int (*StopServerInstanceFn)(char *);
|
||||||
StopServerInstanceFn StopServerInstance = (StopServerInstanceFn)load_function("StopServerInstance");
|
StopServerInstanceFn StopServerInstance = (StopServerInstanceFn)load_function("StopServerInstance");
|
||||||
@@ -8,7 +8,7 @@ void test_StopServerInstance()
|
|||||||
if (!StopServerInstance)
|
if (!StopServerInstance)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Failed to find StopServerInstance function\n");
|
fprintf(stderr, "Failed to find StopServerInstance function\n");
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *serverName = "TestServer";
|
char *serverName = "TestServer";
|
||||||
@@ -20,5 +20,8 @@ void test_StopServerInstance()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
printf("StopServerInstance: FAILED (result = %d)\n", result);
|
printf("StopServerInstance: FAILED (result = %d)\n", result);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
53
test_utils/parser_model_gen.go
Normal file
53
test_utils/parser_model_gen.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package testutils
|
||||||
|
|
||||||
|
import "github.com/pikami/cosmium/parsers"
|
||||||
|
|
||||||
|
func SelectItem_Constant_String(value string) parsers.SelectItem {
|
||||||
|
return parsers.SelectItem{
|
||||||
|
Type: parsers.SelectItemTypeConstant,
|
||||||
|
Value: parsers.Constant{
|
||||||
|
Type: parsers.ConstantTypeString,
|
||||||
|
Value: value,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SelectItem_Constant_Int(value int) parsers.SelectItem {
|
||||||
|
return parsers.SelectItem{
|
||||||
|
Type: parsers.SelectItemTypeConstant,
|
||||||
|
Value: parsers.Constant{
|
||||||
|
Type: parsers.ConstantTypeInteger,
|
||||||
|
Value: value,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SelectItem_Constant_Float(value float64) parsers.SelectItem {
|
||||||
|
return parsers.SelectItem{
|
||||||
|
Type: parsers.SelectItemTypeConstant,
|
||||||
|
Value: parsers.Constant{
|
||||||
|
Type: parsers.ConstantTypeFloat,
|
||||||
|
Value: value,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SelectItem_Constant_Bool(value bool) parsers.SelectItem {
|
||||||
|
return parsers.SelectItem{
|
||||||
|
Type: parsers.SelectItemTypeConstant,
|
||||||
|
Value: parsers.Constant{
|
||||||
|
Type: parsers.ConstantTypeBoolean,
|
||||||
|
Value: value,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SelectItem_Constant_Parameter(name string) parsers.SelectItem {
|
||||||
|
return parsers.SelectItem{
|
||||||
|
Type: parsers.SelectItemTypeConstant,
|
||||||
|
Value: parsers.Constant{
|
||||||
|
Type: parsers.ConstantTypeParameterConstant,
|
||||||
|
Value: name,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user