mirror of
https://github.com/pikami/cosmium.git
synced 2025-06-08 16:40:28 +01:00
Compare commits
No commits in common. "a4659d90a9495bdc62ba36adfc0ceaff6c2d2804" and "66ea859f34dcd75aad273d23a25c181044889293" have entirely different histories.
a4659d90a9
...
66ea859f34
@ -27,14 +27,11 @@ brews:
|
|||||||
email: git@pikami.org
|
email: git@pikami.org
|
||||||
|
|
||||||
dockers:
|
dockers:
|
||||||
- id: docker-linux-amd64
|
- image_templates:
|
||||||
goos: linux
|
- "ghcr.io/pikami/{{ .ProjectName }}:{{ .Version }}"
|
||||||
goarch: amd64
|
- "ghcr.io/pikami/{{ .ProjectName }}:latest"
|
||||||
image_templates:
|
|
||||||
- "ghcr.io/pikami/{{ .ProjectName }}:{{ .Version }}-amd64"
|
|
||||||
- "ghcr.io/pikami/{{ .ProjectName }}:latest-amd64"
|
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
use: buildx
|
use: docker
|
||||||
build_flag_templates:
|
build_flag_templates:
|
||||||
- "--platform=linux/amd64"
|
- "--platform=linux/amd64"
|
||||||
- "--pull"
|
- "--pull"
|
||||||
@ -45,38 +42,6 @@ dockers:
|
|||||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||||
- "--label=org.opencontainers.image.version={{.Version}}"
|
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||||
- id: docker-linux-arm64
|
|
||||||
goos: linux
|
|
||||||
goarch: arm64
|
|
||||||
image_templates:
|
|
||||||
- "ghcr.io/pikami/{{ .ProjectName }}:{{ .Version }}-arm64"
|
|
||||||
- "ghcr.io/pikami/{{ .ProjectName }}:latest-arm64"
|
|
||||||
- "ghcr.io/pikami/{{ .ProjectName }}:{{ .Version }}-arm64v8"
|
|
||||||
- "ghcr.io/pikami/{{ .ProjectName }}:latest-arm64v8"
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
use: buildx
|
|
||||||
build_flag_templates:
|
|
||||||
- "--platform=linux/arm64"
|
|
||||||
- "--pull"
|
|
||||||
- "--label=org.opencontainers.image.title={{.ProjectName}}"
|
|
||||||
- "--label=org.opencontainers.image.description=Lightweight Cosmos DB emulator"
|
|
||||||
- "--label=org.opencontainers.image.url=https://github.com/pikami/cosmium"
|
|
||||||
- "--label=org.opencontainers.image.source=https://github.com/pikami/cosmium"
|
|
||||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
|
||||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
|
||||||
- "--label=org.opencontainers.image.version={{.Version}}"
|
|
||||||
|
|
||||||
docker_manifests:
|
|
||||||
- name_template: 'ghcr.io/pikami/{{ .ProjectName }}:latest'
|
|
||||||
image_templates:
|
|
||||||
- "ghcr.io/pikami/{{ .ProjectName }}:latest-amd64"
|
|
||||||
- "ghcr.io/pikami/{{ .ProjectName }}:latest-arm64"
|
|
||||||
- "ghcr.io/pikami/{{ .ProjectName }}:latest-arm64v8"
|
|
||||||
- name_template: 'ghcr.io/pikami/{{ .ProjectName }}:{{ .Version }}'
|
|
||||||
image_templates:
|
|
||||||
- "ghcr.io/pikami/{{ .ProjectName }}:{{ .Version }}-amd64"
|
|
||||||
- "ghcr.io/pikami/{{ .ProjectName }}:{{ .Version }}-arm64"
|
|
||||||
- "ghcr.io/pikami/{{ .ProjectName }}:{{ .Version }}-arm64v8"
|
|
||||||
|
|
||||||
checksum:
|
checksum:
|
||||||
name_template: 'checksums.txt'
|
name_template: 'checksums.txt'
|
||||||
|
@ -8,11 +8,5 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func CosmiumExport(c *gin.Context) {
|
func CosmiumExport(c *gin.Context) {
|
||||||
repositoryState, err := repositories.GetState()
|
c.IndentedJSON(http.StatusOK, repositories.GetState())
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Data(http.StatusOK, "application/json", []byte(repositoryState))
|
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||||
"github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos"
|
"github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos"
|
||||||
@ -164,56 +162,6 @@ func Test_Documents(t *testing.T) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Should handle parallel writes", func(t *testing.T) {
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
rutineCount := 100
|
|
||||||
results := make(chan error, rutineCount)
|
|
||||||
|
|
||||||
createCall := func(i int) {
|
|
||||||
defer wg.Done()
|
|
||||||
item := map[string]interface{}{
|
|
||||||
"id": fmt.Sprintf("id-%d", i),
|
|
||||||
"pk": fmt.Sprintf("pk-%d", i),
|
|
||||||
"val": i,
|
|
||||||
}
|
|
||||||
bytes, err := json.Marshal(item)
|
|
||||||
if err != nil {
|
|
||||||
results <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
_, err = collectionClient.CreateItem(
|
|
||||||
ctx,
|
|
||||||
azcosmos.PartitionKey{},
|
|
||||||
bytes,
|
|
||||||
&azcosmos.ItemOptions{
|
|
||||||
EnableContentResponseOnWrite: false,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
results <- err
|
|
||||||
|
|
||||||
collectionClient.ReadItem(ctx, azcosmos.PartitionKey{}, fmt.Sprintf("id-%d", i), nil)
|
|
||||||
collectionClient.DeleteItem(ctx, azcosmos.PartitionKey{}, fmt.Sprintf("id-%d", i), nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < rutineCount; i++ {
|
|
||||||
wg.Add(1)
|
|
||||||
go createCall(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
close(results)
|
|
||||||
|
|
||||||
for err := range results {
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error creating item: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_Documents_Patch(t *testing.T) {
|
func Test_Documents_Patch(t *testing.T) {
|
||||||
|
@ -18,8 +18,8 @@ Cosmium strives to support the core features of Cosmos DB, including:
|
|||||||
|
|
||||||
| Feature | Implemented |
|
| Feature | Implemented |
|
||||||
| ----------------------------- | ----------- |
|
| ----------------------------- | ----------- |
|
||||||
| Subqueries | Yes |
|
| Subqueries | No |
|
||||||
| Joins | Yes |
|
| Joins | No |
|
||||||
| Computed properties | No |
|
| Computed properties | No |
|
||||||
| Coalesce operators | No |
|
| Coalesce operators | No |
|
||||||
| Bitwise operators | No |
|
| Bitwise operators | No |
|
||||||
@ -63,11 +63,9 @@ Cosmium strives to support the core features of Cosmos DB, including:
|
|||||||
### Array Functions
|
### Array Functions
|
||||||
|
|
||||||
| Function | Implemented |
|
| Function | Implemented |
|
||||||
| ------------------ | ----------- |
|
| -------------- | ----------- |
|
||||||
| ARRAY_CONCAT | Yes |
|
| ARRAY_CONCAT | Yes |
|
||||||
| ARRAY_CONTAINS | No |
|
| ARRAY_CONTAINS | No |
|
||||||
| ARRAY_CONTAINS_ANY | No |
|
|
||||||
| ARRAY_CONTAINS_ALL | No |
|
|
||||||
| ARRAY_LENGTH | Yes |
|
| ARRAY_LENGTH | Yes |
|
||||||
| ARRAY_SLICE | Yes |
|
| ARRAY_SLICE | Yes |
|
||||||
| CHOOSE | No |
|
| CHOOSE | No |
|
||||||
|
@ -12,9 +12,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func GetAllCollections(databaseId string) ([]repositorymodels.Collection, repositorymodels.RepositoryStatus) {
|
func GetAllCollections(databaseId string) ([]repositorymodels.Collection, repositorymodels.RepositoryStatus) {
|
||||||
storeState.RLock()
|
|
||||||
defer storeState.RUnlock()
|
|
||||||
|
|
||||||
if _, ok := storeState.Databases[databaseId]; !ok {
|
if _, ok := storeState.Databases[databaseId]; !ok {
|
||||||
return make([]repositorymodels.Collection, 0), repositorymodels.StatusNotFound
|
return make([]repositorymodels.Collection, 0), repositorymodels.StatusNotFound
|
||||||
}
|
}
|
||||||
@ -23,9 +20,6 @@ func GetAllCollections(databaseId string) ([]repositorymodels.Collection, reposi
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetCollection(databaseId string, collectionId string) (repositorymodels.Collection, repositorymodels.RepositoryStatus) {
|
func GetCollection(databaseId string, collectionId string) (repositorymodels.Collection, repositorymodels.RepositoryStatus) {
|
||||||
storeState.RLock()
|
|
||||||
defer storeState.RUnlock()
|
|
||||||
|
|
||||||
if _, ok := storeState.Databases[databaseId]; !ok {
|
if _, ok := storeState.Databases[databaseId]; !ok {
|
||||||
return repositorymodels.Collection{}, repositorymodels.StatusNotFound
|
return repositorymodels.Collection{}, repositorymodels.StatusNotFound
|
||||||
}
|
}
|
||||||
@ -38,9 +32,6 @@ func GetCollection(databaseId string, collectionId string) (repositorymodels.Col
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DeleteCollection(databaseId string, collectionId string) repositorymodels.RepositoryStatus {
|
func DeleteCollection(databaseId string, collectionId string) repositorymodels.RepositoryStatus {
|
||||||
storeState.Lock()
|
|
||||||
defer storeState.Unlock()
|
|
||||||
|
|
||||||
if _, ok := storeState.Databases[databaseId]; !ok {
|
if _, ok := storeState.Databases[databaseId]; !ok {
|
||||||
return repositorymodels.StatusNotFound
|
return repositorymodels.StatusNotFound
|
||||||
}
|
}
|
||||||
@ -55,9 +46,6 @@ func DeleteCollection(databaseId string, collectionId string) repositorymodels.R
|
|||||||
}
|
}
|
||||||
|
|
||||||
func CreateCollection(databaseId string, newCollection repositorymodels.Collection) (repositorymodels.Collection, repositorymodels.RepositoryStatus) {
|
func CreateCollection(databaseId string, newCollection repositorymodels.Collection) (repositorymodels.Collection, repositorymodels.RepositoryStatus) {
|
||||||
storeState.Lock()
|
|
||||||
defer storeState.Unlock()
|
|
||||||
|
|
||||||
var ok bool
|
var ok bool
|
||||||
var database repositorymodels.Database
|
var database repositorymodels.Database
|
||||||
if database, ok = storeState.Databases[databaseId]; !ok {
|
if database, ok = storeState.Databases[databaseId]; !ok {
|
||||||
|
@ -11,16 +11,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func GetAllDatabases() ([]repositorymodels.Database, repositorymodels.RepositoryStatus) {
|
func GetAllDatabases() ([]repositorymodels.Database, repositorymodels.RepositoryStatus) {
|
||||||
storeState.RLock()
|
|
||||||
defer storeState.RUnlock()
|
|
||||||
|
|
||||||
return maps.Values(storeState.Databases), repositorymodels.StatusOk
|
return maps.Values(storeState.Databases), repositorymodels.StatusOk
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDatabase(id string) (repositorymodels.Database, repositorymodels.RepositoryStatus) {
|
func GetDatabase(id string) (repositorymodels.Database, repositorymodels.RepositoryStatus) {
|
||||||
storeState.RLock()
|
|
||||||
defer storeState.RUnlock()
|
|
||||||
|
|
||||||
if database, ok := storeState.Databases[id]; ok {
|
if database, ok := storeState.Databases[id]; ok {
|
||||||
return database, repositorymodels.StatusOk
|
return database, repositorymodels.StatusOk
|
||||||
}
|
}
|
||||||
@ -29,9 +23,6 @@ func GetDatabase(id string) (repositorymodels.Database, repositorymodels.Reposit
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DeleteDatabase(id string) repositorymodels.RepositoryStatus {
|
func DeleteDatabase(id string) repositorymodels.RepositoryStatus {
|
||||||
storeState.Lock()
|
|
||||||
defer storeState.Unlock()
|
|
||||||
|
|
||||||
if _, ok := storeState.Databases[id]; !ok {
|
if _, ok := storeState.Databases[id]; !ok {
|
||||||
return repositorymodels.StatusNotFound
|
return repositorymodels.StatusNotFound
|
||||||
}
|
}
|
||||||
@ -42,9 +33,6 @@ func DeleteDatabase(id string) repositorymodels.RepositoryStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func CreateDatabase(newDatabase repositorymodels.Database) (repositorymodels.Database, repositorymodels.RepositoryStatus) {
|
func CreateDatabase(newDatabase repositorymodels.Database) (repositorymodels.Database, repositorymodels.RepositoryStatus) {
|
||||||
storeState.Lock()
|
|
||||||
defer storeState.Unlock()
|
|
||||||
|
|
||||||
if _, ok := storeState.Databases[newDatabase.ID]; ok {
|
if _, ok := storeState.Databases[newDatabase.ID]; ok {
|
||||||
return repositorymodels.Database{}, repositorymodels.Conflict
|
return repositorymodels.Database{}, repositorymodels.Conflict
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func GetAllDocuments(databaseId string, collectionId string) ([]repositorymodels.Document, repositorymodels.RepositoryStatus) {
|
func GetAllDocuments(databaseId string, collectionId string) ([]repositorymodels.Document, repositorymodels.RepositoryStatus) {
|
||||||
storeState.RLock()
|
|
||||||
defer storeState.RUnlock()
|
|
||||||
|
|
||||||
if _, ok := storeState.Databases[databaseId]; !ok {
|
if _, ok := storeState.Databases[databaseId]; !ok {
|
||||||
return make([]repositorymodels.Document, 0), repositorymodels.StatusNotFound
|
return make([]repositorymodels.Document, 0), repositorymodels.StatusNotFound
|
||||||
}
|
}
|
||||||
@ -30,9 +27,6 @@ func GetAllDocuments(databaseId string, collectionId string) ([]repositorymodels
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetDocument(databaseId string, collectionId string, documentId string) (repositorymodels.Document, repositorymodels.RepositoryStatus) {
|
func GetDocument(databaseId string, collectionId string, documentId string) (repositorymodels.Document, repositorymodels.RepositoryStatus) {
|
||||||
storeState.RLock()
|
|
||||||
defer storeState.RUnlock()
|
|
||||||
|
|
||||||
if _, ok := storeState.Databases[databaseId]; !ok {
|
if _, ok := storeState.Databases[databaseId]; !ok {
|
||||||
return repositorymodels.Document{}, repositorymodels.StatusNotFound
|
return repositorymodels.Document{}, repositorymodels.StatusNotFound
|
||||||
}
|
}
|
||||||
@ -49,9 +43,6 @@ func GetDocument(databaseId string, collectionId string, documentId string) (rep
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DeleteDocument(databaseId string, collectionId string, documentId string) repositorymodels.RepositoryStatus {
|
func DeleteDocument(databaseId string, collectionId string, documentId string) repositorymodels.RepositoryStatus {
|
||||||
storeState.Lock()
|
|
||||||
defer storeState.Unlock()
|
|
||||||
|
|
||||||
if _, ok := storeState.Databases[databaseId]; !ok {
|
if _, ok := storeState.Databases[databaseId]; !ok {
|
||||||
return repositorymodels.StatusNotFound
|
return repositorymodels.StatusNotFound
|
||||||
}
|
}
|
||||||
@ -70,9 +61,6 @@ func DeleteDocument(databaseId string, collectionId string, documentId string) r
|
|||||||
}
|
}
|
||||||
|
|
||||||
func CreateDocument(databaseId string, collectionId string, document map[string]interface{}) (repositorymodels.Document, repositorymodels.RepositoryStatus) {
|
func CreateDocument(databaseId string, collectionId string, document map[string]interface{}) (repositorymodels.Document, repositorymodels.RepositoryStatus) {
|
||||||
storeState.Lock()
|
|
||||||
defer storeState.Unlock()
|
|
||||||
|
|
||||||
var ok bool
|
var ok bool
|
||||||
var documentId string
|
var documentId string
|
||||||
var database repositorymodels.Database
|
var database repositorymodels.Database
|
||||||
|
@ -10,9 +10,6 @@ import (
|
|||||||
|
|
||||||
// I have no idea what this is tbh
|
// I have no idea what this is tbh
|
||||||
func GetPartitionKeyRanges(databaseId string, collectionId string) ([]repositorymodels.PartitionKeyRange, repositorymodels.RepositoryStatus) {
|
func GetPartitionKeyRanges(databaseId string, collectionId string) ([]repositorymodels.PartitionKeyRange, repositorymodels.RepositoryStatus) {
|
||||||
storeState.RLock()
|
|
||||||
defer storeState.RUnlock()
|
|
||||||
|
|
||||||
databaseRid := databaseId
|
databaseRid := databaseId
|
||||||
collectionRid := collectionId
|
collectionRid := collectionId
|
||||||
var timestamp int64 = 0
|
var timestamp int64 = 0
|
||||||
|
@ -66,9 +66,6 @@ func LoadStateFS(filePath string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SaveStateFS(filePath string) {
|
func SaveStateFS(filePath string) {
|
||||||
storeState.RLock()
|
|
||||||
defer storeState.RUnlock()
|
|
||||||
|
|
||||||
data, err := json.MarshalIndent(storeState, "", "\t")
|
data, err := json.MarshalIndent(storeState, "", "\t")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("Failed to save state: %v\n", err)
|
logger.Errorf("Failed to save state: %v\n", err)
|
||||||
@ -83,17 +80,8 @@ func SaveStateFS(filePath string) {
|
|||||||
logger.Infof("Documents: %d\n", getLength(storeState.Documents))
|
logger.Infof("Documents: %d\n", getLength(storeState.Documents))
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetState() (string, error) {
|
func GetState() repositorymodels.State {
|
||||||
storeState.RLock()
|
return storeState
|
||||||
defer storeState.RUnlock()
|
|
||||||
|
|
||||||
data, err := json.MarshalIndent(storeState, "", "\t")
|
|
||||||
if err != nil {
|
|
||||||
logger.Errorf("Failed to serialize state: %v\n", err)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(data), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLength(v interface{}) int {
|
func getLength(v interface{}) int {
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package repositorymodels
|
package repositorymodels
|
||||||
|
|
||||||
import "sync"
|
|
||||||
|
|
||||||
type Database struct {
|
type Database struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
TimeStamp int64 `json:"_ts"`
|
TimeStamp int64 `json:"_ts"`
|
||||||
@ -103,8 +101,6 @@ type PartitionKeyRange struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type State struct {
|
type State struct {
|
||||||
sync.RWMutex
|
|
||||||
|
|
||||||
// Map databaseId -> Database
|
// Map databaseId -> Database
|
||||||
Databases map[string]Database `json:"databases"`
|
Databases map[string]Database `json:"databases"`
|
||||||
|
|
||||||
|
@ -659,7 +659,6 @@ func compareValues(val1, val2 interface{}) int {
|
|||||||
|
|
||||||
func deduplicate[T RowType | interface{}](slice []T) []T {
|
func deduplicate[T RowType | interface{}](slice []T) []T {
|
||||||
var result []T
|
var result []T
|
||||||
result = make([]T, 0)
|
|
||||||
|
|
||||||
for i := 0; i < len(slice); i++ {
|
for i := 0; i < len(slice); i++ {
|
||||||
unique := true
|
unique := true
|
||||||
|
Loading…
x
Reference in New Issue
Block a user