diff --git a/sharedlibrary/collections.go b/sharedlibrary/collections.go new file mode 100644 index 0000000..70ca6f0 --- /dev/null +++ b/sharedlibrary/collections.go @@ -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) +} diff --git a/sharedlibrary/databases.go b/sharedlibrary/databases.go new file mode 100644 index 0000000..2ad557f --- /dev/null +++ b/sharedlibrary/databases.go @@ -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) +} diff --git a/sharedlibrary/documents.go b/sharedlibrary/documents.go new file mode 100644 index 0000000..995d59f --- /dev/null +++ b/sharedlibrary/documents.go @@ -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) +} diff --git a/sharedlibrary/shared.go b/sharedlibrary/shared.go new file mode 100644 index 0000000..9dfcbf0 --- /dev/null +++ b/sharedlibrary/shared.go @@ -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 + } +} diff --git a/sharedlibrary/sharedlibrary.go b/sharedlibrary/sharedlibrary.go index 768667e..418e3ae 100644 --- a/sharedlibrary/sharedlibrary.go +++ b/sharedlibrary/sharedlibrary.go @@ -9,32 +9,12 @@ import ( "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 func CreateServerInstance(serverName *C.char, configurationJSON *C.char) int { configStr := C.GoString(configurationJSON) serverNameStr := C.GoString(serverName) - if serverInstances == nil { - serverInstances = make(map[string]*ServerInstance) - } - - if _, ok := serverInstances[serverNameStr]; ok { + if _, ok := getInstance(serverNameStr); ok { return ResponseServerInstanceAlreadyExists } @@ -55,19 +35,21 @@ func CreateServerInstance(serverName *C.char, configurationJSON *C.char) int { server := api.NewApiServer(repository, configuration) server.Start() - serverInstances[serverNameStr] = &ServerInstance{ + addInstance(serverNameStr, &ServerInstance{ server: server, repository: repository, - } + }) return ResponseSuccess } //export StopServerInstance 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() - delete(serverInstances, C.GoString(serverName)) + removeInstance(serverNameStr) return ResponseSuccess } @@ -76,7 +58,9 @@ func StopServerInstance(serverName *C.char) int { //export GetServerInstanceState 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() if err != nil { return nil @@ -92,7 +76,7 @@ func LoadServerInstanceState(serverName *C.char, stateJSON *C.char) int { serverNameStr := C.GoString(serverName) stateJSONStr := C.GoString(stateJSON) - if serverInstance, ok := serverInstances[serverNameStr]; ok { + if serverInstance, ok := getInstance(serverNameStr); ok { err := serverInstance.repository.LoadStateJSON(stateJSONStr) if err != nil { return ResponseFailedToLoadState diff --git a/sharedlibrary/tests/main.c b/sharedlibrary/tests/main.c index bf226ee..4315215 100644 --- a/sharedlibrary/tests/main.c +++ b/sharedlibrary/tests/main.c @@ -1,8 +1,9 @@ #include "shared.h" -void test_CreateServerInstance(); -void test_StopServerInstance(); -void test_ServerInstanceStateMethods(); +int test_CreateServerInstance(); +int test_StopServerInstance(); +int test_ServerInstanceStateMethods(); +int test_Databases(); int main(int argc, char *argv[]) { @@ -21,9 +22,24 @@ int main(int argc, char *argv[]) } printf("Running tests for library: %s\n", libPath); - test_CreateServerInstance(); - test_ServerInstanceStateMethods(); - test_StopServerInstance(); + int results[] = { + test_CreateServerInstance(), + 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); return EXIT_SUCCESS; diff --git a/sharedlibrary/tests/test_create.c b/sharedlibrary/tests/test_create.c index aff2fb6..3b03df3 100644 --- a/sharedlibrary/tests/test_create.c +++ b/sharedlibrary/tests/test_create.c @@ -1,6 +1,6 @@ #include "shared.h" -void test_CreateServerInstance() +int test_CreateServerInstance() { typedef int (*CreateServerInstanceFn)(char *, char *); CreateServerInstanceFn CreateServerInstance = (CreateServerInstanceFn)load_function("CreateServerInstance"); @@ -8,7 +8,7 @@ void test_CreateServerInstance() if (!CreateServerInstance) { fprintf(stderr, "Failed to find CreateServerInstance function\n"); - return; + return 0; } char *serverName = "TestServer"; @@ -22,5 +22,8 @@ void test_CreateServerInstance() else { printf("CreateServerInstance: FAILED (result = %d)\n", result); + return 0; } + + return 1; } diff --git a/sharedlibrary/tests/test_databases.c b/sharedlibrary/tests/test_databases.c new file mode 100644 index 0000000..07ed868 --- /dev/null +++ b/sharedlibrary/tests/test_databases.c @@ -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; +} diff --git a/sharedlibrary/tests/test_instance_state.c b/sharedlibrary/tests/test_instance_state.c index cc70d69..34846e9 100644 --- a/sharedlibrary/tests/test_instance_state.c +++ b/sharedlibrary/tests/test_instance_state.c @@ -1,13 +1,13 @@ #include "shared.h" -void test_ServerInstanceStateMethods() +int test_ServerInstanceStateMethods() { typedef int (*LoadServerInstanceStateFn)(char *, char *); LoadServerInstanceStateFn LoadServerInstanceState = (LoadServerInstanceStateFn)load_function("LoadServerInstanceState"); if (!LoadServerInstanceState) { fprintf(stderr, "Failed to find LoadServerInstanceState function\n"); - return; + return 0; } char *serverName = "TestServer"; @@ -20,6 +20,7 @@ void test_ServerInstanceStateMethods() else { printf("LoadServerInstanceState: FAILED (result = %d)\n", result); + return 0; } typedef char *(*GetServerInstanceStateFn)(char *); @@ -27,7 +28,7 @@ void test_ServerInstanceStateMethods() if (!GetServerInstanceState) { fprintf(stderr, "Failed to find GetServerInstanceState function\n"); - return; + return 0; } char *state = GetServerInstanceState(serverName); @@ -38,6 +39,7 @@ void test_ServerInstanceStateMethods() else { 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\":{}}}"; @@ -45,7 +47,7 @@ void test_ServerInstanceStateMethods() if (!compact_state) { free(state); - return; + return 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("Expected: %s\n", expected_state); printf("Actual: %s\n", compact_state); + return 0; } free(state); free(compact_state); + return 1; } diff --git a/sharedlibrary/tests/test_stop.c b/sharedlibrary/tests/test_stop.c index bfaf646..d667d74 100644 --- a/sharedlibrary/tests/test_stop.c +++ b/sharedlibrary/tests/test_stop.c @@ -1,6 +1,6 @@ #include "shared.h" -void test_StopServerInstance() +int test_StopServerInstance() { typedef int (*StopServerInstanceFn)(char *); StopServerInstanceFn StopServerInstance = (StopServerInstanceFn)load_function("StopServerInstance"); @@ -8,7 +8,7 @@ void test_StopServerInstance() if (!StopServerInstance) { fprintf(stderr, "Failed to find StopServerInstance function\n"); - return; + return 0; } char *serverName = "TestServer"; @@ -20,5 +20,8 @@ void test_StopServerInstance() else { printf("StopServerInstance: FAILED (result = %d)\n", result); + return 0; } + + return 1; }