From 23e7e3aca5ba8f0ea95f015d542a41d1c296dc6f Mon Sep 17 00:00:00 2001 From: jeffrey-elliott Date: Wed, 5 Feb 2025 17:31:29 -0800 Subject: [PATCH] added DoesDatabaseExist by string and by guid --- .../Admin/AstraDatabasesAdmin.cs | 430 +++++++++--------- .../Tests/AdminTests.cs | 59 ++- 2 files changed, 282 insertions(+), 207 deletions(-) diff --git a/src/DataStax.AstraDB.DataApi/Admin/AstraDatabasesAdmin.cs b/src/DataStax.AstraDB.DataApi/Admin/AstraDatabasesAdmin.cs index f4ac141..812e6eb 100644 --- a/src/DataStax.AstraDB.DataApi/Admin/AstraDatabasesAdmin.cs +++ b/src/DataStax.AstraDB.DataApi/Admin/AstraDatabasesAdmin.cs @@ -28,211 +28,229 @@ namespace DataStax.AstraDB.DataApi.Admin; public class AstraDatabasesAdmin { - private const int WAIT_IN_SECONDS = 600; - private const CloudProviderType FREE_TIER_CLOUD = CloudProviderType.GCP; - private const string FREE_TIER_CLOUD_REGION = "us-east1"; - - private readonly CommandOptions _adminOptions; - private readonly DataApiClient _client; - - private CommandOptions[] OptionsTree => new CommandOptions[] { _client.ClientOptions, _adminOptions }; - - internal AstraDatabasesAdmin(DataApiClient client, CommandOptions adminOptions) - { - Guard.NotNull(client, nameof(client)); - _client = client; - Guard.NotNull(adminOptions, nameof(adminOptions)); - _adminOptions = adminOptions; - } - - public List ListDatabaseNames() - { - return ListDatabases().Select(db => db.Info.Name).ToList(); - } - - public async Task> ListDatabaseNamesAsync() - { - var databases = await ListDatabasesAsync().ConfigureAwait(false); - return databases.Select(db => db.Info.Name).ToList(); - } - - public List ListDatabases() - { - return ListDatabasesAsync(true).ResultSync(); - } - - public async Task> ListDatabasesAsync() - { - return await ListDatabasesAsync(false).ConfigureAwait(false); - } - - internal async Task> ListDatabasesAsync(bool runSynchronously) - { - var command = CreateCommand().AddUrlPath("databases"); - var response = await command.RunAsyncRaw>(HttpMethod.Get, runSynchronously).ConfigureAwait(false); - return response; - } - - public bool DatabaseExists(string name) - { - Guard.NotNullOrEmpty(name, nameof(name)); - return ListDatabaseNames().Contains(name); - } - - public bool DatabaseExists(Guid id) - { - Guard.NotEmpty(id, nameof(id)); - throw new NotImplementedException(); - } - - public IDatabaseAdmin CreateDatabase(string name) - { - Guard.NotNullOrEmpty(name, nameof(name)); - throw new NotImplementedException(); - //return CreateDatabase(name, FREE_TIER_CLOUD, FREE_TIER_CLOUD_REGION); - } - - public IDatabaseAdmin CreateDatabase(string name, CloudProviderType cloudProviderType, string cloudRegion, bool waitForDb = true) - { - Guard.NotNullOrEmpty(name, nameof(name)); - Guard.NotNullOrEmpty(cloudRegion, nameof(cloudRegion)); - - var dbInfo = ListDatabases().FirstOrDefault(db => name.Equals(db.Info.Name)); - if (dbInfo != null) - { - // switch (optDb.Status) - // { - // case DatabaseStatusType.ACTIVE: - // Console.WriteLine($"Database {AnsiUtils.Green(name)} already exists and is ACTIVE."); - // return GetDatabaseAdmin(Guid.Parse(optDb.Id)); - // default: - // throw new InvalidOperationException("Database already exists but is not in expected state."); - // } - } - - // var newDbId = Guid.Parse(devopsDbClient.Create(new DatabaseCreationRequest - // { - // Name = name, - // CloudProvider = cloud, - // CloudRegion = cloudRegion, - // Keyspace = DataApiClientOptions.DEFAULT_KEYSPACE, - // WithVector = true - // })); - - //Console.WriteLine($"Database {name} is starting (id={newDbId}): it will take about a minute please wait..."); - if (waitForDb) - { - //WaitForDatabase(devopsDbClient.Database(newDbId.ToString())); - } - //return GetDatabaseAdmin(newDbId); - throw new NotImplementedException(); - } - - public bool DropDatabase(Guid databaseId) - { - Guard.NotEmpty(databaseId, nameof(databaseId)); - bool exists = DatabaseExists(databaseId); - throw new NotImplementedException(); - } - - public bool DropDatabase(string databaseName) - { - Guard.NotNullOrEmpty(databaseName, nameof(databaseName)); - var db = ListDatabases().FirstOrDefault(d => d.Info.Name.Equals(databaseName)); - if (db != null) - { - throw new NotImplementedException(); - //devopsDbClient.Database(db.Id.ToString()).Delete(); - return true; - } - return false; - } - - public DatabaseInfo GetDatabaseInfo(Guid id) - { - return GetDatabaseInfoAsync(id, true).ResultSync(); - } - - public async Task GetDatabaseInfoAsync(Guid id) - { - return await GetDatabaseInfoAsync(id, false).ConfigureAwait(false); - } - - internal async Task GetDatabaseInfoAsync(Guid id, bool runSynchronously) - { - Guard.NotEmpty(id, nameof(id)); - var command = CreateCommand().AddUrlPath("databases").AddUrlPath(id.ToString()); - var response = await command.RunAsyncRaw(HttpMethod.Get, runSynchronously).ConfigureAwait(false); - return response; - } - - private Command CreateCommand() - { - return new Command(_client, OptionsTree, new AdminCommandUrlBuilder(OptionsTree)); - } - - //TODO: skip, these are available via DataApiClient - // public Database GetDatabase(Guid databaseId, DatabaseOptions dbOptions) - // { - // Guard.NotEmpty(databaseId, nameof(databaseId)); - // throw new NotImplementedException(); - // // if (!adminOptions.DataApiClientOptions.IsAstra) - // // { - // // throw new InvalidEnvironmentException("getDatabase(id, keyspace)", adminOptions.DataApiClientOptions.Destination); - // // } - - // // var databaseRegion = devopsDbClient.FindById(databaseId.ToString()).ValueOr(() => throw new DatabaseNotFoundException(databaseId.ToString())).Info.Region; - // // var astraApiEndpoint = new AstraApiEndpoint(databaseId, databaseRegion, adminOptions.DataApiClientOptions.AstraEnvironment); - - // // return new Database(astraApiEndpoint.ApiEndPoint, dbOptions); - // } - - //TODO: skip, these are available via DataApiClient - // public Database GetDatabase(Guid databaseId, string keyspace) - // { - // throw new NotImplementedException(); - // //return GetDatabase(databaseId, new DatabaseOptions(adminOptions.Token, adminOptions.DataApiClientOptions) { Keyspace = keyspace }); - // } - - //TODO: skip, these are available via DataApiClient - // public Database GetDatabase(Guid databaseId) - // { - // return GetDatabase(databaseId, DatabaseOptions.DefaultKeyspace); - // } - - //TODO: is this used? I'd expect to get this from the Database itself. - // public IDatabaseAdmin GetDatabaseAdmin(Guid databaseId) - // { - // Guard.NotEmpty(databaseId, nameof(databaseId)); - // throw new NotImplementedException(); - // //return new DatabaseAdminForAstra(adminOptions.Token, databaseId, adminOptions.DataApiClientOptions); - // } - - // private void WaitForDatabase(DbOpsClient dbc) - // { - // var top = DateTimeOffset.UtcNow; - // while (GetStatus(dbc) != DatabaseStatusType.ACTIVE && (DateTimeOffset.UtcNow - top).TotalSeconds < WAIT_IN_SECONDS) - // { - // try - // { - // Thread.Sleep(5000); - // Console.WriteLine($"...waiting for database '{dbc.Get().Info.Name}' to become active..."); - // } - // catch (ThreadInterruptedException e) - // { - // Console.WriteLine($"Interrupted {e.Message}"); - // Thread.CurrentThread.Interrupt(); - // } - // } - // if (GetStatus(dbc) != DatabaseStatusType.ACTIVE) - // { - // throw new InvalidOperationException($"Database is not in expected state after timeouts of {WAIT_IN_SECONDS} seconds."); - // } - // } - - // private DatabaseStatusType GetStatus(DbOpsClient dbc) - // { - // return dbc.Find().ValueOr(() => throw new DatabaseNotFoundException(dbc.DatabaseId)).Status; - // } + private const int WAIT_IN_SECONDS = 600; + private const CloudProviderType FREE_TIER_CLOUD = CloudProviderType.GCP; + private const string FREE_TIER_CLOUD_REGION = "us-east1"; + + private readonly CommandOptions _adminOptions; + private readonly DataApiClient _client; + + private CommandOptions[] OptionsTree => new CommandOptions[] { _client.ClientOptions, _adminOptions }; + + internal AstraDatabasesAdmin(DataApiClient client, CommandOptions adminOptions) + { + Guard.NotNull(client, nameof(client)); + _client = client; + Guard.NotNull(adminOptions, nameof(adminOptions)); + _adminOptions = adminOptions; + } + + public List ListDatabaseNames() + { + return ListDatabases().Select(db => db.Info.Name).ToList(); + } + + public async Task> ListDatabaseNamesAsync() + { + var databases = await ListDatabasesAsync().ConfigureAwait(false); + return databases.Select(db => db.Info.Name).ToList(); + } + + public List ListDatabases() + { + return ListDatabasesAsync(true).ResultSync(); + } + + public async Task> ListDatabasesAsync() + { + return await ListDatabasesAsync(false).ConfigureAwait(false); + } + + internal async Task> ListDatabasesAsync(bool runSynchronously) + { + var command = CreateCommand().AddUrlPath("databases"); + var response = await command.RunAsyncRaw>(HttpMethod.Get, runSynchronously).ConfigureAwait(false); + return response; + } + + public bool DoesDatabaseExist(string name) + { + Guard.NotNullOrEmpty(name, nameof(name)); + List list = ListDatabaseNames(); + return list.Contains(name); + } + + public async Task DoesDatabaseExistAsync(string name) + { + Guard.NotNullOrEmpty(name, nameof(name)); + List list = await ListDatabaseNamesAsync(); + return list.Contains(name); + } + + public bool DoesDatabaseExist(Guid id) + { + Guard.NotEmpty(id, nameof(id)); + string guid = id.ToString(); + List databases = ListDatabases(); + return databases.Any(db => db.Id == guid); + } + + public async Task DoesDatabaseExistAsync(Guid id) + { + Guard.NotEmpty(id, nameof(id)); + string guid = id.ToString(); + List databases = await ListDatabasesAsync(); + return databases.Any(db => db.Id == guid); + } + + public IDatabaseAdmin CreateDatabase(string name) + { + Guard.NotNullOrEmpty(name, nameof(name)); + throw new NotImplementedException(); + //return CreateDatabase(name, FREE_TIER_CLOUD, FREE_TIER_CLOUD_REGION); + } + + public IDatabaseAdmin CreateDatabase(string name, CloudProviderType cloudProviderType, string cloudRegion, bool waitForDb = true) + { + Guard.NotNullOrEmpty(name, nameof(name)); + Guard.NotNullOrEmpty(cloudRegion, nameof(cloudRegion)); + + var dbInfo = ListDatabases().FirstOrDefault(db => name.Equals(db.Info.Name)); + if (dbInfo != null) + { + // switch (optDb.Status) + // { + // case DatabaseStatusType.ACTIVE: + // Console.WriteLine($"Database {AnsiUtils.Green(name)} already exists and is ACTIVE."); + // return GetDatabaseAdmin(Guid.Parse(optDb.Id)); + // default: + // throw new InvalidOperationException("Database already exists but is not in expected state."); + // } + } + + // var newDbId = Guid.Parse(devopsDbClient.Create(new DatabaseCreationRequest + // { + // Name = name, + // CloudProvider = cloud, + // CloudRegion = cloudRegion, + // Keyspace = DataApiClientOptions.DEFAULT_KEYSPACE, + // WithVector = true + // })); + + //Console.WriteLine($"Database {name} is starting (id={newDbId}): it will take about a minute please wait..."); + if (waitForDb) + { + //WaitForDatabase(devopsDbClient.Database(newDbId.ToString())); + } + //return GetDatabaseAdmin(newDbId); + throw new NotImplementedException(); + } + + public bool DropDatabase(Guid databaseId) + { + // Guard.NotEmpty(databaseId, nameof(databaseId)); + // bool exists = DatabaseExists(databaseId); + throw new NotImplementedException(); + } + + public bool DropDatabase(string databaseName) + { + Guard.NotNullOrEmpty(databaseName, nameof(databaseName)); + var db = ListDatabases().FirstOrDefault(d => d.Info.Name.Equals(databaseName)); + if (db != null) + { + throw new NotImplementedException(); + //devopsDbClient.Database(db.Id.ToString()).Delete(); + //return true; + } + return false; + } + + public DatabaseInfo GetDatabaseInfo(Guid id) + { + return GetDatabaseInfoAsync(id, true).ResultSync(); + } + + public async Task GetDatabaseInfoAsync(Guid id) + { + return await GetDatabaseInfoAsync(id, false).ConfigureAwait(false); + } + + internal async Task GetDatabaseInfoAsync(Guid id, bool runSynchronously) + { + Guard.NotEmpty(id, nameof(id)); + var command = CreateCommand().AddUrlPath("databases").AddUrlPath(id.ToString()); + var response = await command.RunAsyncRaw(HttpMethod.Get, runSynchronously).ConfigureAwait(false); + return response; + } + + private Command CreateCommand() + { + return new Command(_client, OptionsTree, new AdminCommandUrlBuilder(OptionsTree)); + } + + //TODO: skip, these are available via DataApiClient + // public Database GetDatabase(Guid databaseId, DatabaseOptions dbOptions) + // { + // Guard.NotEmpty(databaseId, nameof(databaseId)); + // throw new NotImplementedException(); + // // if (!adminOptions.DataApiClientOptions.IsAstra) + // // { + // // throw new InvalidEnvironmentException("getDatabase(id, keyspace)", adminOptions.DataApiClientOptions.Destination); + // // } + + // // var databaseRegion = devopsDbClient.FindById(databaseId.ToString()).ValueOr(() => throw new DatabaseNotFoundException(databaseId.ToString())).Info.Region; + // // var astraApiEndpoint = new AstraApiEndpoint(databaseId, databaseRegion, adminOptions.DataApiClientOptions.AstraEnvironment); + + // // return new Database(astraApiEndpoint.ApiEndPoint, dbOptions); + // } + + //TODO: skip, these are available via DataApiClient + // public Database GetDatabase(Guid databaseId, string keyspace) + // { + // throw new NotImplementedException(); + // //return GetDatabase(databaseId, new DatabaseOptions(adminOptions.Token, adminOptions.DataApiClientOptions) { Keyspace = keyspace }); + // } + + //TODO: skip, these are available via DataApiClient + // public Database GetDatabase(Guid databaseId) + // { + // return GetDatabase(databaseId, DatabaseOptions.DefaultKeyspace); + // } + + //TODO: is this used? I'd expect to get this from the Database itself. + // public IDatabaseAdmin GetDatabaseAdmin(Guid databaseId) + // { + // Guard.NotEmpty(databaseId, nameof(databaseId)); + // throw new NotImplementedException(); + // //return new DatabaseAdminForAstra(adminOptions.Token, databaseId, adminOptions.DataApiClientOptions); + // } + + // private void WaitForDatabase(DbOpsClient dbc) + // { + // var top = DateTimeOffset.UtcNow; + // while (GetStatus(dbc) != DatabaseStatusType.ACTIVE && (DateTimeOffset.UtcNow - top).TotalSeconds < WAIT_IN_SECONDS) + // { + // try + // { + // Thread.Sleep(5000); + // Console.WriteLine($"...waiting for database '{dbc.Get().Info.Name}' to become active..."); + // } + // catch (ThreadInterruptedException e) + // { + // Console.WriteLine($"Interrupted {e.Message}"); + // Thread.CurrentThread.Interrupt(); + // } + // } + // if (GetStatus(dbc) != DatabaseStatusType.ACTIVE) + // { + // throw new InvalidOperationException($"Database is not in expected state after timeouts of {WAIT_IN_SECONDS} seconds."); + // } + // } + + // private DatabaseStatusType GetStatus(DbOpsClient dbc) + // { + // return dbc.Find().ValueOr(() => throw new DatabaseNotFoundException(dbc.DatabaseId)).Status; + // } } diff --git a/test/DataStax.AstraDB.DataApi.IntegrationTests/Tests/AdminTests.cs b/test/DataStax.AstraDB.DataApi.IntegrationTests/Tests/AdminTests.cs index fda24ed..29f610a 100644 --- a/test/DataStax.AstraDB.DataApi.IntegrationTests/Tests/AdminTests.cs +++ b/test/DataStax.AstraDB.DataApi.IntegrationTests/Tests/AdminTests.cs @@ -44,7 +44,7 @@ public async Task GetDatabasesList() Console.WriteLine($"GetDatabasesList: {list.Count} items"); } - [Fact] + [Fact] public async Task GetDatabasesNamesList() { var list = await fixture.Client.GetAstraAdmin().ListDatabaseNamesAsync(); @@ -56,4 +56,61 @@ public async Task GetDatabasesNamesList() Console.WriteLine($"GetDatabasesNamesList: {list.Count} items"); Console.WriteLine(string.Join(", ", list)); } + + [Fact] + public async Task CheckDatabaseExistsByName() + { + var dbName = "test-1"; + + var found = await fixture.Client.GetAstraAdmin().DoesDatabaseExistAsync(dbName); + Assert.True(found); + + found = fixture.Client.GetAstraAdmin().DoesDatabaseExist(dbName); + Assert.True(found); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + public void CheckDatabaseExistsByName_ExpectedError(string invalidName) + { + var ex = Assert.Throws(() => fixture.Client.GetAstraAdmin().DoesDatabaseExist(invalidName)); + Assert.Contains("Value cannot be null or empty", ex.Message); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + public async Task CheckDatabaseExistsByNameAsync_ExpectedError(string invalidName) + { + var ex = await Assert.ThrowsAsync( + () => fixture.Client.GetAstraAdmin().DoesDatabaseExistAsync(invalidName) + ); + Assert.Contains("Value cannot be null or empty", ex.Message); + } + + [Fact] + public async Task CheckDatabaseExistsByName_ExpectedFalse() + { + var dbName = "this-is-not-the-greatest-db-in-the-world-this-is-a-tribute"; + + var found = await fixture.Client.GetAstraAdmin().DoesDatabaseExistAsync(dbName); + Assert.False(found); + + found = fixture.Client.GetAstraAdmin().DoesDatabaseExist(dbName); + Assert.False(found); + } + + [Fact] + public async Task CheckDatabaseExistsById() + { + // todo: get this value from an expected named DB produced by testing CreateDatabase() + var dbId = fixture.DatabaseId; + + var found = await fixture.Client.GetAstraAdmin().DoesDatabaseExistAsync(dbId); + Assert.True(found); + + found = fixture.Client.GetAstraAdmin().DoesDatabaseExist(dbId); + Assert.True(found); + } }