Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
bfaf9cc
Build ColumnInfo
Aniruddh25 Feb 18, 2022
c336d7a
Merge origin/main
Aniruddh25 Feb 23, 2022
fb1eded
First draft of reading metadata from database
Aniruddh25 Mar 2, 2022
f5e2418
Set a default value for testing
Aniruddh25 Mar 2, 2022
998c8b9
Merge remote-tracking branch 'origin/main' into dev/anmunde/getDataba…
Aniruddh25 Mar 2, 2022
3934f89
MsSqlMetadataProvider and unit tests
Aniruddh25 Mar 3, 2022
5e7bb1f
fix sql-config
Aniruddh25 Mar 3, 2022
0a64aa9
Remove columnInfo builder query
Aniruddh25 Mar 3, 2022
f6cb272
Fix formatting
Aniruddh25 Mar 3, 2022
c6cb554
Fix test for Cosmos
Aniruddh25 Mar 3, 2022
2a7ff88
Create DatabaseMetadataProvider only if its MsSql for now
Aniruddh25 Mar 3, 2022
1879eaf
Only if MsSql
Aniruddh25 Mar 3, 2022
7e8a26b
Merge remote-tracking branch 'origin/main' into dev/anmunde/getDataba…
Aniruddh25 Mar 4, 2022
84e126e
Fix error message
Aniruddh25 Mar 4, 2022
163a5e6
Specify resolver config file in the test appsettings.json so the cons…
Aniruddh25 Mar 4, 2022
58ed4fd
Update DataGateway.Service/Services/MsSqlMetadataProvider.cs
Aniruddh25 Mar 4, 2022
50db729
Fix review comments
Aniruddh25 Mar 4, 2022
ad01393
Merge branch 'dev/anmunde/getDatabaseSchema' of https://github.com/Az…
Aniruddh25 Mar 4, 2022
1864aa3
Remove integration table name in InitializeTestFixture
Aniruddh25 Mar 7, 2022
5010c90
Generalize MetadataProvider to PgSql and MySql
Aniruddh25 Mar 7, 2022
c3d0e29
Update DataGateway.Service/Services/MsSqlMetadataProvider.cs
Aniruddh25 Mar 7, 2022
081758d
Update DataGateway.Service/Services/MsSqlMetadataProvider.cs
Aniruddh25 Mar 7, 2022
abf351b
Update DataGateway.Service.Tests/SqlTests/MsSqlMetadataProviderTests.cs
Aniruddh25 Mar 7, 2022
3be1a18
Update DataGateway.Service.Tests/SqlTests/MsSqlMetadataProviderTests.cs
Aniruddh25 Mar 7, 2022
d902917
explicit string cast and async
Aniruddh25 Mar 7, 2022
361381e
Add metadata provider
Aniruddh25 Mar 7, 2022
6a8f639
Merge remote-tracking branch 'origin/main' into dev/anmunde/getDataba…
Aniruddh25 Mar 7, 2022
21f47ec
Use CollectionAssert
Aniruddh25 Mar 7, 2022
71de42d
Honor schemaName
Aniruddh25 Mar 7, 2022
1dd1386
Fix MySqlTest
Aniruddh25 Mar 7, 2022
f2aaeb9
Fix formatting
Aniruddh25 Mar 8, 2022
a4aa11e
Remove ArgumentException
Aniruddh25 Mar 8, 2022
2fec3ae
Revert "Remove ArgumentException"
Aniruddh25 Mar 8, 2022
7bf8ccd
Ignore same table name from different schema
Aniruddh25 Mar 8, 2022
6a0a739
Update DataGateway.Service/Services/FileMetadataStoreProvider.cs
Aniruddh25 Mar 8, 2022
32e9994
Update DataGateway.Service/Services/FileMetadataStoreProvider.cs
Aniruddh25 Mar 8, 2022
a914a9d
Remove nullable usage, new lines and consistent using
Aniruddh25 Mar 8, 2022
7e5e320
Use text
Aniruddh25 Mar 9, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Azure.DataGateway.Service.Models;
using Azure.DataGateway.Service.Services;

Expand Down Expand Up @@ -55,5 +56,10 @@ public FilterParser GetFilterParser()
{
return _filterParser;
}

public Task<DatabaseSchema> RefreshDatabaseSchemaWithTablesAsync(string? schemaName)
{
throw new System.NotImplementedException();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Azure.DataGateway.Service.Tests.SqlTests
{
[TestClass, TestCategory(TestCategory.MSSQL)]
public class MsSqlMetadataProviderTests : SqlMetadataProviderTests
{
[ClassInitialize]
public static async Task InitializeTestFixture(TestContext context)
{
await InitializeTestFixture(context, TestCategory.MSSQL);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Azure.DataGateway.Service.Tests.SqlTests
{
[TestClass, TestCategory(TestCategory.MYSQL)]
public class MySqlMetadataProviderTests : SqlMetadataProviderTests
{
[ClassInitialize]
public static async Task InitializeTestFixture(TestContext context)
{
await InitializeTestFixture(context, TestCategory.MYSQL);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Azure.DataGateway.Service.Tests.SqlTests
{
[TestClass, TestCategory(TestCategory.POSTGRESQL)]
public class PostgreSqlMetadataProviderTests : SqlMetadataProviderTests
{
[ClassInitialize]
public static async Task InitializeTestFixture(TestContext context)
{
await InitializeTestFixture(context, TestCategory.POSTGRESQL);
}
}
}
2 changes: 1 addition & 1 deletion DataGateway.Service.Tests/SqlTests/RestApiTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,7 @@ await SetupAndRunRestApiTest(
operationType: Operation.Insert,
requestBody: requestBody,
exception: true,
expectedErrorMessage: "Invalid request body. Missing field in body: publisher_id.",
expectedErrorMessage: "Invalid request body. Missing field in body: title.",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: probably worth separate issue/discussion. If no body provided in request, we would for this case, not want to expose table columns. Probably a more generic: "Body empty. Missing required field(s),"

expectedStatusCode: HttpStatusCode.BadRequest
);
}
Expand Down
45 changes: 45 additions & 0 deletions DataGateway.Service.Tests/SqlTests/SqlMetadataProviderTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System.Threading.Tasks;
using Azure.DataGateway.Service.Models;
using Azure.DataGateway.Service.Services;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Azure.DataGateway.Service.Tests.SqlTests
{
[TestClass]
public abstract class SqlMetadataProviderTests : SqlTestBase
{
[TestMethod]
public async Task TestDerivedDatabaseSchemaIsValid()
{
ResolverConfig runtimeConfig = _metadataStoreProvider.GetResolvedConfig();
DatabaseSchema expectedSchema = runtimeConfig.DatabaseSchema;
DatabaseSchema derivedDatabaseSchema =
await _sqlMetadataProvider.RefreshDatabaseSchemaWithTablesAsync(_defaultSchemaName);

foreach ((string tableName, TableDefinition expectedTableDefinition) in expectedSchema.Tables)
{
TableDefinition actualTableDefinition;
Assert.IsTrue(derivedDatabaseSchema.Tables.TryGetValue(tableName, out actualTableDefinition),
$"Could not find table definition for table '{tableName}'");

CollectionAssert.AreEqual(
expectedTableDefinition.PrimaryKey,
actualTableDefinition.PrimaryKey,
$"Did not find the expected primary keys for table {tableName}");

foreach ((string columnName, ColumnDefinition expectedColumnDefinition) in expectedTableDefinition.Columns)
{
ColumnDefinition actualColumnDefinition;
Assert.IsTrue(actualTableDefinition.Columns.TryGetValue(columnName, out actualColumnDefinition),
$"Could not find column definition for column '{columnName}' of table '{tableName}'");

Assert.AreEqual(expectedColumnDefinition.IsAutoGenerated, actualColumnDefinition.IsAutoGenerated);
Assert.AreEqual(expectedColumnDefinition.HasDefault, actualColumnDefinition.HasDefault,
$"Expected HasDefault property of column '{columnName}' of table '{tableName}' does not match actual.");
Assert.AreEqual(expectedColumnDefinition.IsNullable, actualColumnDefinition.IsNullable,
$"Expected IsNullable property of column '{columnName}' of table '{tableName}' does not match actual.");
}
}
}
}
}
17 changes: 16 additions & 1 deletion DataGateway.Service.Tests/SqlTests/SqlTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ public abstract class SqlTestBase
protected static IMetadataStoreProvider _metadataStoreProvider;
protected static Mock<IAuthorizationService> _authorizationService;
protected static Mock<IHttpContextAccessor> _httpContextAccessor;
protected static IMetadataStoreProvider _sqlMetadataProvider;
protected static string _defaultSchemaName;

/// <summary>
/// Sets up test fixture for class, only to be run once per test run.
Expand All @@ -52,20 +54,30 @@ protected static async Task InitializeTestFixture(TestContext context, string te
_testCategory = testCategory;

IOptions<DataGatewayConfig> config = SqlTestHelper.LoadConfig($"{_testCategory}IntegrationTest");
string connectionString = config.Value.DatabaseConnection.ConnectionString;

switch (_testCategory)
{
case TestCategory.POSTGRESQL:
_queryExecutor = new QueryExecutor<NpgsqlConnection>(config);
_queryBuilder = new PostgresQueryBuilder();
_sqlMetadataProvider =
SqlMetadataProvider<NpgsqlConnection, NpgsqlDataAdapter, NpgsqlCommand>.GetSqlMetadataProvider(connectionString);
_defaultSchemaName = "public";
break;
case TestCategory.MSSQL:
_queryExecutor = new QueryExecutor<SqlConnection>(config);
_queryBuilder = new MsSqlQueryBuilder();
_sqlMetadataProvider =
SqlMetadataProvider<SqlConnection, SqlDataAdapter, SqlCommand>.GetSqlMetadataProvider(connectionString);
_defaultSchemaName = "dbo";
break;
case TestCategory.MYSQL:
_queryExecutor = new QueryExecutor<MySqlConnection>(config);
_queryBuilder = new MySqlQueryBuilder();
_sqlMetadataProvider =
SqlMetadataProvider<MySqlConnection, MySqlDataAdapter, MySqlCommand>.GetSqlMetadataProvider(connectionString);
_defaultSchemaName = "mysql";
break;
}

Expand All @@ -81,7 +93,10 @@ protected static async Task InitializeTestFixture(TestContext context, string te
_httpContextAccessor = new Mock<IHttpContextAccessor>();
_httpContextAccessor.Setup(x => x.HttpContext.User).Returns(new ClaimsPrincipal());

_metadataStoreProvider = new FileMetadataStoreProvider("sql-config.json");
_metadataStoreProvider = new FileMetadataStoreProvider(
"sql-config.json",
config.Value.DatabaseType,
config.Value.DatabaseConnection.ConnectionString);
_queryEngine = new SqlQueryEngine(_metadataStoreProvider, _queryExecutor, _queryBuilder);
_mutationEngine = new SqlMutationEngine(_queryEngine, _metadataStoreProvider, _queryExecutor, _queryBuilder);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ private void ValidateNoMutationResolvers()
/// </summary>
private void ValidateDatabaseHasTables()
{
if (_config.DatabaseSchema.Tables == null || _config.DatabaseSchema.Tables.Count == 0)
if (_config.DatabaseSchema!.Tables == null || _config.DatabaseSchema!.Tables.Count == 0)
{
throw new ConfigValidationException(
"Database schema must have a non empty \"Tables\" element.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ private static Dictionary<string, FieldDefinitionNode> GetObjTypeDefFields(Objec
/// </summary>
private Dictionary<string, TableDefinition> GetDatabaseTables()
{
return _config.DatabaseSchema.Tables;
return _config.DatabaseSchema!.Tables;
}

/// <summary>
Expand All @@ -194,7 +194,7 @@ private Dictionary<string, GraphQLType> GetGraphQLTypes()
/// </summary>
private bool ExistsTableWithName(string tableName)
{
return _config.DatabaseSchema.Tables.ContainsKey(tableName);
return _config.DatabaseSchema!.Tables.ContainsKey(tableName);
}

/// <summary>
Expand All @@ -211,7 +211,7 @@ private TableDefinition GetTableWithName(string tableName)
throw new ArgumentException("Invalid table name was provided.");
}

return _config.DatabaseSchema.Tables[tableName];
return _config.DatabaseSchema!.Tables[tableName];
}

/// <summary>
Expand Down Expand Up @@ -540,7 +540,7 @@ private bool TableContainsForeignKey(string tableName, string foreignKeyName)
/// <exception cref="KeyNotFoundException" />
private ForeignKeyDefinition GetFkFromTable(string tableName, string fkName)
{
return _config.DatabaseSchema.Tables[tableName].ForeignKeys[fkName];
return _config.DatabaseSchema!.Tables[tableName].ForeignKeys[fkName];
}

/// <summary>
Expand Down
6 changes: 4 additions & 2 deletions DataGateway.Service/Models/DatabaseSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ namespace Azure.DataGateway.Service.Models
/// </summary>
public class DatabaseSchema
{
public Dictionary<string, TableDefinition> Tables { get; set; } = new();
public Dictionary<string, TableDefinition> Tables { get; set; } =
new(StringComparer.InvariantCultureIgnoreCase);
}

public class TableDefinition
Expand All @@ -18,7 +19,8 @@ public class TableDefinition
/// The list of columns that together form the primary key of the table.
/// </summary>
public List<string> PrimaryKey { get; set; } = new();
public Dictionary<string, ColumnDefinition> Columns { get; set; } = new();
public Dictionary<string, ColumnDefinition> Columns { get; set; } =
new(StringComparer.InvariantCultureIgnoreCase);
public Dictionary<string, ForeignKeyDefinition> ForeignKeys { get; set; } = new();
public Dictionary<string, AuthorizationRule> HttpVerbs { get; set; } = new();
}
Expand Down
4 changes: 2 additions & 2 deletions DataGateway.Service/MsSqlBooks.sql
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ CREATE TABLE authors(
CREATE TABLE reviews(
book_id bigint,
id bigint IDENTITY(5001, 1),
content varchar(max),
content varchar(max) DEFAULT('Its a classic') NOT NULL,
PRIMARY KEY(book_id, id)
);

Expand Down Expand Up @@ -93,4 +93,4 @@ INSERT INTO book_author_link(book_id, author_id) VALUES (1, 123), (2, 124), (3,
SET IDENTITY_INSERT reviews ON
INSERT INTO reviews(id, book_id, content) VALUES (567, 1, 'Indeed a great book'), (568, 1, 'I loved it'), (569, 1, 'best book I read in years');
SET IDENTITY_INSERT reviews OFF

2 changes: 1 addition & 1 deletion DataGateway.Service/MySqlBooks.sql
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ CREATE TABLE authors(
CREATE TABLE reviews(
book_id bigint NOT NULL,
id bigint AUTO_INCREMENT,
content text NOT NULL,
content text DEFAULT ('Its a classic') NOT NULL,
PRIMARY KEY(book_id, id),
INDEX (id)
);
Expand Down
9 changes: 8 additions & 1 deletion DataGateway.Service/PostgreSqlBooks.sql
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ DROP TABLE IF EXISTS authors;
DROP TABLE IF EXISTS books;
DROP TABLE IF EXISTS publishers;
DROP TABLE IF EXISTS magazines;
DROP TABLE IF EXISTS comics;

CREATE TABLE publishers(
id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
Expand All @@ -25,7 +26,7 @@ CREATE TABLE authors(
CREATE TABLE reviews(
book_id bigint,
id bigint GENERATED BY DEFAULT AS IDENTITY,
content text,
content text DEFAULT 'Its a classic' NOT NULL,
PRIMARY KEY(book_id, id)
);

Expand All @@ -41,6 +42,12 @@ CREATE TABLE magazines(
issueNumber bigint NULL
);

CREATE TABLE comics(
id bigint PRIMARY KEY,
title text NOT NULL,
volume bigint GENERATED BY DEFAULT AS IDENTITY
);

ALTER TABLE books
ADD CONSTRAINT book_publisher_fk
FOREIGN KEY (publisher_id)
Expand Down
2 changes: 1 addition & 1 deletion DataGateway.Service/Resolvers/PostgresQueryBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public string Build(SqlUpsertQueryStructure structure)
}

/// <inheritdoc />
protected override string Build(KeysetPaginationPredicate? predicate)
protected override string Build(KeysetPaginationPredicate predicate)
{
if (predicate == null)
{
Expand Down
Loading