Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix DbConnectionValidator when Shell Settings from database #12342

Merged
merged 15 commits into from
Sep 9, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ private async Task AssertConnectionValidityAndApplyErrorsAsync(string databasePr
case DbConnectionValidatorResult.InvalidConnection:
errors.Add(new ModelError(nameof(TenantViewModel.ConnectionString), S["The provided connection string is invalid or server is unreachable."]));
break;
case DbConnectionValidatorResult.DocumentFound:
case DbConnectionValidatorResult.ShellDescriptorDocumentFound:
case DbConnectionValidatorResult.DocumentTableFound:
errors.Add(new ModelError(nameof(TenantViewModel.TablePrefix), S["The provided table prefix already exists."]));
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,39 @@ namespace OrchardCore.Data;

public enum DbConnectionValidatorResult
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
{
// Unknown indicates that the connection string status is unknown or was not yet validated
/// <summary>
/// 'Unknown' indicates that the connection string status is unknown or was not validated.
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
Unknown,

// NoProvider indicated that the provider is missing
/// <summary>
/// 'NoProvider' indicated that the database provider is missing.
/// </summary>
NoProvider,

// DocumentNotFound indicates that the connection string was valid, yet the Document table does not exist
DocumentNotFound,
/// <summary>
/// 'DocumentTableNotFound' indicates that the connection string was valid, while the Document table does not exists.
/// </summary>
DocumentTableNotFound,

// DocumentFound indicates that the connection string was valid, yet the Document table exist
DocumentFound,
/// <summary>
/// 'DocumentTableFound' indicates that the connection string was valid, while the Document table exists.
/// </summary>
DocumentTableFound,

// InvalidConnection unable to open a connection to the given connection string
/// <summary>
/// 'ShellDescriptorDocumentFound' indicates that the connection string was valid and the document table exists with at least one shell-descriptor document.
/// This also means that the Document table is used by a tenant.
/// </summary>
ShellDescriptorDocumentFound,
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// 'InvalidConnection' unable to open a connection to the given connection string.
/// </summary>
hishamco marked this conversation as resolved.
Show resolved Hide resolved
InvalidConnection,

// UnsupportedProvider indicated invalid or unsupported database provider
/// <summary>
/// 'UnsupportedProvider' indicates invalid or unsupported database provider.
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
UnsupportedProvider
}
27 changes: 18 additions & 9 deletions src/OrchardCore/OrchardCore.Data.YesSql/DbConnectionValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using MySqlConnector;
using Npgsql;
using OrchardCore.Data.YesSql.Abstractions;
using OrchardCore.Environment.Shell.Descriptor.Models;
using YesSql;
using YesSql.Provider.MySql;
using YesSql.Provider.PostgreSql;
Expand Down Expand Up @@ -50,7 +51,7 @@ public async Task<DbConnectionValidatorResult> ValidateAsync(string databaseProv

if (provider != null && !provider.HasConnectionString)
{
return DbConnectionValidatorResult.DocumentNotFound;
return DbConnectionValidatorResult.DocumentTableNotFound;
}

if (String.IsNullOrWhiteSpace(connectionString))
Expand Down Expand Up @@ -80,24 +81,32 @@ public async Task<DbConnectionValidatorResult> ValidateAsync(string databaseProv

using var result = await selectCommand.ExecuteReaderAsync();

// at this point the query succeeded and the table exists
return DbConnectionValidatorResult.DocumentFound;
// At this point, the query work and the 'Document' table exists.
if (result.HasRows)
{
// At this point we know that the Document table and the ShellDescriptor record exists.
// This also means that this table is used for a tenant.
return DbConnectionValidatorResult.ShellDescriptorDocumentFound;
}

// At this point the Document table exists with no ShellDescriptor document.
// This also means that this table is used for other purposes that a tenant (ex., Database Shells Configuration.)
return DbConnectionValidatorResult.DocumentTableFound;
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
}
catch
{
// at this point we know that the document table does not exist

return DbConnectionValidatorResult.DocumentNotFound;
// At this point we know that the document table does not exist.
return DbConnectionValidatorResult.DocumentTableNotFound;
}
}

private ISqlBuilder GetSelectBuilderForDocumentTable(string tablePrefix, DatabaseProviderName providerName)
{
var selectBuilder = GetSqlBuilder(providerName, tablePrefix);

selectBuilder.Select();
selectBuilder.AddSelector("*");
selectBuilder.Table(_tableNameConvention.GetDocumentTable());
selectBuilder.WhereAnd($"Type = '{typeof(ShellDescriptor).FullName}, {typeof(ShellDescriptor).Assembly.GetName().Name}'");
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
selectBuilder.Take("1");

return selectBuilder;
Expand All @@ -111,7 +120,7 @@ private static IConnectionFactory GetFactory(DatabaseProviderName providerName,
DatabaseProviderName.MySql => new DbConnectionFactory<MySqlConnection>(connectionString),
DatabaseProviderName.Sqlite => new DbConnectionFactory<SqliteConnection>(connectionString),
DatabaseProviderName.Postgres => new DbConnectionFactory<NpgsqlConnection>(connectionString),
_ => throw new ArgumentOutOfRangeException("Unsupported Database Provider"),
_ => throw new ArgumentOutOfRangeException(nameof(providerName), "Unsupported Database Provider"),
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
};
}

Expand All @@ -123,7 +132,7 @@ private ISqlBuilder GetSqlBuilder(DatabaseProviderName providerName, string tabl
DatabaseProviderName.MySql => new MySqlDialect(),
DatabaseProviderName.Sqlite => new SqliteDialect(),
DatabaseProviderName.Postgres => new PostgreSqlDialect(),
_ => throw new ArgumentOutOfRangeException("Unsupported Database Provider"),
_ => throw new ArgumentOutOfRangeException(nameof(providerName), "Unsupported Database Provider"),
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
};

var prefix = String.Empty;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using OrchardCore.Data;

namespace OrchardCore.Shells.Database.Configuration
{
public class DatabaseShellsStorageOptions
{
public bool MigrateFromFiles { get; set; }
public string DatabaseProvider { get; set; }
public DatabaseProviderName DatabaseProvider { get; set; }
public string ConnectionString { get; set; }
public string TablePrefix { get; set; }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Threading.Tasks;
using OrchardCore.Data;
using OrchardCore.Environment.Shell;
using OrchardCore.Environment.Shell.Builders;
using OrchardCore.Environment.Shell.Descriptor.Models;
Expand All @@ -12,7 +13,7 @@ public static class DatabaseShellContextFactoryExtensions
{
internal static Task<ShellContext> GetDatabaseContextAsync(this IShellContextFactory shellContextFactory, DatabaseShellsStorageOptions options)
{
if (options.DatabaseProvider == null)
if (options.DatabaseProvider == DatabaseProviderName.None)
{
throw new ArgumentNullException(nameof(options.DatabaseProvider),
"The 'OrchardCore.Shells.Database' configuration section should define a 'DatabaseProvider'");
Expand All @@ -24,7 +25,7 @@ internal static Task<ShellContext> GetDatabaseContextAsync(this IShellContextFac
State = TenantState.Running
};

settings["DatabaseProvider"] = options.DatabaseProvider;
settings["DatabaseProvider"] = options.DatabaseProvider.ToString();
settings["ConnectionString"] = options.ConnectionString;
settings["TablePrefix"] = options.TablePrefix;

Expand Down
2 changes: 1 addition & 1 deletion src/OrchardCore/OrchardCore.Setup.Core/SetupService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ private async Task<string> SetupInternalAsync(SetupContext context)
case DbConnectionValidatorResult.InvalidConnection:
context.Errors.Add(String.Empty, S["The provided connection string is invalid or server is unreachable."]);
break;
case DbConnectionValidatorResult.DocumentFound:
case DbConnectionValidatorResult.ShellDescriptorDocumentFound:
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
context.Errors.Add(String.Empty, S["The provided database table is already in use."]);
break;
}
Expand Down