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

Multiple db support for REST scenario #2169

Merged
merged 16 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 14 additions & 1 deletion src/Config/ObjectModel/RuntimeConfig.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Diagnostics.CodeAnalysis;
using System.IO.Abstractions;
using System.Net;
using System.Text.Json;
Expand Down Expand Up @@ -135,7 +136,9 @@ public bool AllowIntrospection

private Dictionary<string, DataSource> _dataSourceNameToDataSource;

private Dictionary<string, string> _entityNameToDataSourceName;
private Dictionary<string, string> _entityNameToDataSourceName = new();

private Dictionary<string, string> _entityPathNameToEntityName = new();

/// <summary>
/// List of all datasources.
Expand All @@ -154,6 +157,16 @@ public IEnumerable<DataSource> ListAllDataSources()
return _dataSourceNameToDataSource.AsEnumerable();
}

public bool TryAddEntityPathNameToEntityName(string entityPathName, string entityName)
rohkhann marked this conversation as resolved.
Show resolved Hide resolved
{
return _entityPathNameToEntityName.TryAdd(entityPathName, entityName);
}

public bool TryGetEntityNameFromPath(string entityPathName, [NotNullWhen(true)] out string? entityName)
{
return _entityPathNameToEntityName.TryGetValue(entityPathName, out entityName);
}

/// <summary>
/// Constructor for runtimeConfig.
/// To be used when setting up from cli json scenario.
Expand Down
3 changes: 1 addition & 2 deletions src/Core/Resolvers/SqlQueryEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,7 @@ public class SqlQueryEngine : IQueryEngine
// </summary>
public async Task<JsonDocument?> ExecuteAsync(FindRequestContext context)
{
// for REST API scenarios, use the default datasource
string dataSourceName = _runtimeConfigProvider.GetConfig().DefaultDataSourceName;
string dataSourceName = _runtimeConfigProvider.GetConfig().GetDataSourceNameFromEntityName(context.EntityName);

ISqlMetadataProvider sqlMetadataProvider = _sqlMetadataProviderFactory.GetMetadataProvider(dataSourceName);
SqlQueryStructure structure = new(
Expand Down
9 changes: 0 additions & 9 deletions src/Core/Services/MetadataProviders/ISqlMetadataProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,6 @@ public interface ISqlMetadataProvider
/// <returns>True if exists, false otherwise.</returns>
bool TryGetBackingColumn(string entityName, string field, [NotNullWhen(true)] out string? name);

/// <summary>
/// Try to obtain the name of the Entity that has the provided Path. If It
/// exists save in out param, and return true, otherwise return false.
/// </summary>
/// <param name="entityPathName">Entity's path as seen in a request.</param>
/// <param name="entityName">Name of the associated entity.</param>
/// <returns>True if exists, otherwise false.</returns>
bool TryGetEntityNameFromPath(string entityPathName, [NotNullWhen(true)] out string? entityName);

/// <summary>
/// Obtains the underlying database type.
/// </summary>
Expand Down
11 changes: 2 additions & 9 deletions src/Core/Services/MetadataProviders/SqlMetadataProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ public abstract class SqlMetadataProvider<ConnectionT, DataAdapterT, CommandT> :

private Dictionary<string, Dictionary<string, string>> EntityExposedNamesToBackingColumnNames { get; } = new();

private Dictionary<string, string> EntityPathToEntityName { get; } = new();

protected IAbstractQueryManagerFactory QueryManagerFactory { get; init; }

/// <summary>
Expand Down Expand Up @@ -220,12 +218,6 @@ public bool TryGetBackingColumn(string entityName, string field, [NotNullWhen(tr
return EntityExposedNamesToBackingColumnNames[entityName].TryGetValue(field, out name);
}

/// <inheritdoc />
public virtual bool TryGetEntityNameFromPath(string entityPathName, [NotNullWhen(true)] out string? entityName)
{
return EntityPathToEntityName.TryGetValue(entityPathName, out entityName);
}

/// <inheritdoc />
public IReadOnlyDictionary<string, DatabaseObject> GetEntityNamesAndDbObjects()
{
Expand Down Expand Up @@ -503,7 +495,8 @@ private void GenerateRestPathToEntityMap()

if (!string.IsNullOrEmpty(path))
{
EntityPathToEntityName[path] = entityName;
// add the entity path name to the entity name mapping to the runtime config for multi-db resolution.
runtimeConfig.TryAddEntityPathNameToEntityName(path, entityName);
}
}
catch (Exception e)
Expand Down
20 changes: 10 additions & 10 deletions src/Core/Services/RestService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -212,17 +212,18 @@ RequestValidator requestValidator
private async Task<IActionResult> DispatchQuery(RestRequestContext context, DatabaseType databaseType)
{
IQueryEngine queryEngine = _queryEngineFactory.GetQueryEngine(databaseType);
string defaultDataSourceName = _runtimeConfigProvider.GetConfig().DefaultDataSourceName;

string dataSourceName = _runtimeConfigProvider.GetConfig().GetDataSourceNameFromEntityName(context.EntityName);
rohkhann marked this conversation as resolved.
Show resolved Hide resolved

if (context is FindRequestContext findRequestContext)
{
using JsonDocument? restApiResponse = await queryEngine.ExecuteAsync(findRequestContext);
return restApiResponse is null ? SqlResponseHelpers.FormatFindResult(JsonDocument.Parse("[]").RootElement.Clone(), findRequestContext, _sqlMetadataProviderFactory.GetMetadataProvider(defaultDataSourceName), _runtimeConfigProvider.GetConfig(), GetHttpContext())
: SqlResponseHelpers.FormatFindResult(restApiResponse.RootElement.Clone(), findRequestContext, _sqlMetadataProviderFactory.GetMetadataProvider(defaultDataSourceName), _runtimeConfigProvider.GetConfig(), GetHttpContext());
return restApiResponse is null ? SqlResponseHelpers.FormatFindResult(JsonDocument.Parse("[]").RootElement.Clone(), findRequestContext, _sqlMetadataProviderFactory.GetMetadataProvider(dataSourceName), _runtimeConfigProvider.GetConfig(), GetHttpContext())
: SqlResponseHelpers.FormatFindResult(restApiResponse.RootElement.Clone(), findRequestContext, _sqlMetadataProviderFactory.GetMetadataProvider(dataSourceName), _runtimeConfigProvider.GetConfig(), GetHttpContext());
}
else if (context is StoredProcedureRequestContext storedProcedureRequestContext)
{
return await queryEngine.ExecuteAsync(storedProcedureRequestContext, defaultDataSourceName);
return await queryEngine.ExecuteAsync(storedProcedureRequestContext, dataSourceName);
}
else
{
Expand All @@ -237,10 +238,10 @@ private async Task<IActionResult> DispatchQuery(RestRequestContext context, Data
private Task<IActionResult?> DispatchMutation(RestRequestContext context, DatabaseType databaseType)
{
IMutationEngine mutationEngine = _mutationEngineFactory.GetMutationEngine(databaseType);
string defaultDataSourceName = _runtimeConfigProvider.GetConfig().DefaultDataSourceName;
string dataSourceName = _runtimeConfigProvider.GetConfig().GetDataSourceNameFromEntityName(context.EntityName);
return context switch
{
StoredProcedureRequestContext => mutationEngine.ExecuteAsync((StoredProcedureRequestContext)context, defaultDataSourceName),
StoredProcedureRequestContext => mutationEngine.ExecuteAsync((StoredProcedureRequestContext)context, dataSourceName),
_ => mutationEngine.ExecuteAsync(context)
};
}
Expand Down Expand Up @@ -437,8 +438,7 @@ public bool TryGetRestRouteFromConfig([NotNullWhen(true)] out string? configured
public (string, string) GetEntityNameAndPrimaryKeyRouteFromRoute(string routeAfterPathBase)
{

string dataSourceName = _runtimeConfigProvider.GetConfig().DefaultDataSourceName;
ISqlMetadataProvider sqlMetadataProvider = _sqlMetadataProviderFactory.GetMetadataProvider(dataSourceName);
RuntimeConfig runtimeConfig = _runtimeConfigProvider.GetConfig();

// Split routeAfterPath on the first occurrence of '/', if we get back 2 elements
// this means we have a non empty primary key route which we save. Otherwise, save
Expand All @@ -451,15 +451,15 @@ public bool TryGetRestRouteFromConfig([NotNullWhen(true)] out string? configured
string entityPath = entityPathAndPKRoute[0];
string primaryKeyRoute = entityPathAndPKRoute.Length == maxNumberOfElementsFromSplit ? entityPathAndPKRoute[1] : string.Empty;

if (!sqlMetadataProvider.TryGetEntityNameFromPath(entityPath, out string? entityName))
if (!runtimeConfig.TryGetEntityNameFromPath(entityPath, out string? entityName))
rohkhann marked this conversation as resolved.
Show resolved Hide resolved
{
throw new DataApiBuilderException(
message: $"Invalid Entity path: {entityPath}.",
statusCode: HttpStatusCode.NotFound,
subStatusCode: DataApiBuilderException.SubStatusCodes.EntityNotFound);
}

return (entityName, primaryKeyRoute);
return (entityName!, primaryKeyRoute);
}

/// <summary>
Expand Down