Skip to content

Commit

Permalink
Validate relationship field names in config as backing columns (#2175)
Browse files Browse the repository at this point in the history
## Why make this change?

Closes #2166
Related to #2139

## What is this change?

When we validate the relationship fields in the config, ie: source
fields and target fields, we use the backing column names to validate
against. Previously we were using aliases if one existed, but instead we
will validate only against the actual names of the backing columns. So,
for example, if someone had an alias for the column "id", so that it
would display as "identifier" when using this alias, we would validate
that the relationship defined with that field was defined in the config
using the name "id", and would throw a validation error if the config
instead used, "identifier."

## How was this tested?

Current test suite passing, along with a regression test for DwSql,
MsSql, MySql, and PostreSql.

## Sample Request(s)

Please see this issue for details on samples to reproduce.

#2166

---------

Co-authored-by: Sean Leonard <sean.leonard@microsoft.com>
Co-authored-by: Ayush Agarwal <34566234+ayush3797@users.noreply.github.com>
  • Loading branch information
3 people committed Apr 30, 2024
1 parent 95c46d1 commit d2aa5aa
Show file tree
Hide file tree
Showing 26 changed files with 313 additions and 20 deletions.
4 changes: 2 additions & 2 deletions config-generators/dwsql-commands.txt
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,9 @@ update Broker --config "dab-config.DwSql.json" --permissions "authenticated:crea
update WebsiteUser --config "dab-config.DwSql.json" --permissions "authenticated:create,read,delete,update" --rest false --graphql "websiteUser:websiteUsers"
update SupportedType --config "dab-config.DwSql.json" --map "id:typeid" --permissions "authenticated:create,read,delete,update"
update Tree --config "dab-config.DwSql.json" --rest true --graphql false --permissions "authenticated:create,read,update,delete" --map "species:Scientific Name,region:United State's Region"
update Shrub --config "dab-config.DwSql.json" --permissions "authenticated:create,read,update,delete" --map "species:fancyName"
update Shrub --config "dab-config.DwSql.json" --permissions "authenticated:create,read,update,delete" --map "species:fancyName" --relationship fungus --cardinality one --target.entity Fungus --relationship.fields "species:habitat"
update Fungus --config "dab-config.DwSql.json" --permissions "authenticated:create,read,update,delete" --map "spores:hazards" --rest true
update Fungus --config "dab-config.DwSql.json" --permissions "policy_tester_01:read" --fields.include "*" --policy-database "@item.region ne 'northeast'"
update Fungus --config "dab-config.DwSql.json" --permissions "policy_tester_01:read" --fields.include "*" --policy-database "@item.region ne 'northeast'" --relationship Shrub --cardinality one --target.entity Shrub --relationship.fields "habitat:species"
update books_view_all --config "dab-config.DwSql.json" --permissions "authenticated:create,read,update,delete" --rest true --graphql true
update books_view_with_mapping --config "dab-config.DwSql.json" --map "id:book_id"
update series --config "dab-config.DwSql.json" --relationship comics --target.entity Comic --cardinality many --relationship.fields "id:series_id"
Expand Down
4 changes: 2 additions & 2 deletions config-generators/mssql-commands.txt
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,9 @@ update series --config "dab-config.MsSql.json" --relationship comics --target.en
update stocks_price --config "dab-config.MsSql.json" --relationship Stock --target.entity Stock --cardinality one
update Broker --config "dab-config.MsSql.json" --permissions "authenticated:create,update,read,delete" --graphql false
update Tree --config "dab-config.MsSql.json" --rest true --graphql false --permissions "authenticated:create,read,update,delete" --map "species:Scientific Name,region:United State's Region"
update Shrub --config "dab-config.MsSql.json" --permissions "authenticated:create,read,update,delete" --map "species:fancyName"
update Shrub --config "dab-config.MsSql.json" --permissions "authenticated:create,read,update,delete" --map "species:fancyName" --relationship fungus --cardinality one --target.entity Fungus --relationship.fields "species:habitat"
update Fungus --config "dab-config.MsSql.json" --permissions "authenticated:create,read,update,delete" --map "spores:hazards" --rest true
update Fungus --config "dab-config.MsSql.json" --permissions "policy_tester_01:read" --fields.include "*" --policy-database "@item.region ne 'northeast'"
update Fungus --config "dab-config.MsSql.json" --permissions "policy_tester_01:read" --fields.include "*" --policy-database "@item.region ne 'northeast'" --relationship Shrub --cardinality one --target.entity Shrub --relationship.fields "habitat:species"
update books_view_all --config "dab-config.MsSql.json" --permissions "authenticated:create,read,update,delete" --rest true --graphql true
update stocks_view_selected --config "dab-config.MsSql.json" --permissions "authenticated:create,read,update,delete"
update books_publishers_view_composite --config "dab-config.MsSql.json" --permissions "authenticated:create,read,update,delete"
Expand Down
4 changes: 2 additions & 2 deletions config-generators/mysql-commands.txt
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,9 @@ update Comic --config "dab-config.MySql.json" --permissions "authenticated:creat
update series --config "dab-config.MySql.json" --relationship comics --target.entity Comic --cardinality many
update Broker --config "dab-config.MySql.json" --permissions "authenticated:create,update,read,delete" --graphql false
update Tree --config "dab-config.MySql.json" --rest true --graphql false --permissions "authenticated:create,read,update,delete" --map "species:Scientific Name,region:United State's Region"
update Shrub --config "dab-config.MySql.json" --permissions "authenticated:create,read,update,delete" --map "species:fancyName"
update Shrub --config "dab-config.MySql.json" --permissions "authenticated:create,read,update,delete" --map "species:fancyName" --relationship fungus --cardinality one --target.entity Fungus --relationship.fields "species:habitat"
update Fungus --config "dab-config.MySql.json" --permissions "authenticated:create,read,update,delete" --map "spores:hazards" --rest true
update Fungus --config "dab-config.MySql.json" --permissions "policy_tester_01:read" --fields.include "*" --policy-database "@item.region ne 'northeast'"
update Fungus --config "dab-config.MySql.json" --permissions "policy_tester_01:read" --fields.include "*" --policy-database "@item.region ne 'northeast'" --relationship Shrub --cardinality one --target.entity Shrub --relationship.fields "habitat:species"
update books_view_all --config "dab-config.MySql.json" --permissions "authenticated:create,read,update,delete" --rest true --graphql true
update stocks_view_selected --config "dab-config.MySql.json" --permissions "authenticated:create,read,update,delete"
update books_publishers_view_composite --config "dab-config.MySql.json" --permissions "authenticated:create,read,update,delete"
Expand Down
4 changes: 2 additions & 2 deletions config-generators/postgresql-commands.txt
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@ update Comic --config "dab-config.PostgreSql.json" --permissions "authenticated:
update series --config "dab-config.PostgreSql.json" --relationship comics --target.entity Comic --cardinality many
update Broker --config "dab-config.PostgreSql.json" --permissions "authenticated:create,update,read,delete" --graphql false
update Tree --config "dab-config.PostgreSql.json" --rest true --graphql false --permissions "authenticated:create,read,update,delete" --map "species:Scientific Name,region:United State's Region"
update Shrub --config "dab-config.PostgreSql.json" --permissions "authenticated:create,read,update,delete" --map "species:fancyName"
update Shrub --config "dab-config.PostgreSql.json" --permissions "authenticated:create,read,update,delete" --map "species:fancyName" --relationship fungus --cardinality one --target.entity Fungus --relationship.fields "species:habitat"
update Fungus --config "dab-config.PostgreSql.json" --permissions "authenticated:create,read,update,delete" --map "spores:hazards" --rest true
update Fungus --config "dab-config.PostgreSql.json" --permissions "policy_tester_01:read" --fields.include "*" --policy-database "@item.region ne 'northeast'"
update Fungus --config "dab-config.PostgreSql.json" --permissions "policy_tester_01:read" --fields.include "*" --policy-database "@item.region ne 'northeast'" --relationship Shrub --cardinality one --target.entity Shrub --relationship.fields "habitat:species"
update books_view_with_mapping --config "dab-config.PostgreSql.json" --map "id:book_id"
update BookWebsitePlacement --config "dab-config.PostgreSql.json" --relationship books --target.entity Book --cardinality one
update SupportedType --config "dab-config.PostgreSql.json" --map "id:typeid" --permissions "authenticated:create,read,delete,update"
Expand Down
8 changes: 5 additions & 3 deletions src/Core/Configurations/RuntimeConfigValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1078,9 +1078,9 @@ public void ValidateRelationshipsInConfig(RuntimeConfig runtimeConfig, IMetadata
/// works because C# is pass by reference for referenced class types.
/// </summary>
/// <param name="invalidColumns">List in which to aggregate the invalid fields.</param>
/// <param name="fields">List of the fields to check for existence in backing DB.</param>
/// <param name="fields">List of the backing fields to check for existence in backing DB.</param>
/// <param name="entityName">The name of the entity that we check for backing columns.</param>
/// <param name="sqlMetadataProvider">The sqlMetadataProvider used to lookup if the fields are valid columns in DB.</param>
/// <param name="sqlMetadataProvider">The sqlMetadataProvider used to lookup if the backing fields are valid columns in DB.</param>
private static void GetFieldsNotBackedByColumnsInDB(
List<string> invalidColumns,
string[] fields,
Expand All @@ -1090,7 +1090,9 @@ public void ValidateRelationshipsInConfig(RuntimeConfig runtimeConfig, IMetadata
invalidColumns.Clear();
foreach (string field in fields)
{
if (!sqlMetadataProvider.TryGetBackingColumn(entityName, field, out _))
// We call this function because the keyset are the backing columns
// which is what want to validate.
if (!sqlMetadataProvider.TryGetExposedColumnName(entityName, field, out _))
{
invalidColumns.Add(field);
}
Expand Down
2 changes: 2 additions & 0 deletions src/Service.Tests/DatabaseSchema-DwSql.sql
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,9 @@ VALUES
(7, 'Journal7', null, null);
INSERT INTO aow(NoteNum, DetailAssessmentAndPlanning, WagingWar, StrategicAttack) VALUES (1, 'chapter one notes: ', 'chapter two notes: ', 'chapter three notes: ');
INSERT INTO trees(treeId, species, region, height) VALUES (1, 'Tsuga terophylla', 'Pacific Northwest', '30m'), (2, 'Pseudotsuga menziesii', 'Pacific Northwest', '40m');
INSERT INTO trees(treeId, species, region, height) VALUES (4, 'test', 'Pacific Northwest', '0m');
INSERT INTO fungi(speciesid, region, habitat) VALUES (1, 'northeast', 'forest'), (2, 'southwest', 'sand');
INSERT INTO fungi(speciesid, region, habitat) VALUES (3, 'northeast', 'test');
INSERT INTO type_table(id, short_types, int_types, long_types,
string_types, nvarchar_string_types,
single_types, float_types, decimal_types,
Expand Down
3 changes: 3 additions & 0 deletions src/Service.Tests/DatabaseSchema-MsSql.sql
Original file line number Diff line number Diff line change
Expand Up @@ -600,8 +600,11 @@ INSERT INTO stocks(categoryid, pieceid, categoryName) VALUES (1, 1, 'SciFi'), (2
INSERT INTO stocks_price(categoryid, pieceid, price, is_wholesale_price) VALUES (2, 1, 100.57, 1), (1, 1, 42.75, 0), (100, 99, NULL, NULL);
INSERT INTO stocks_price(categoryid, pieceid, instant, price, is_wholesale_price) VALUES (2, 1, '2023-08-21 15:11:04', 100.57, 1);
INSERT INTO trees(treeId, species, region, height) VALUES (1, 'Tsuga terophylla', 'Pacific Northwest', '30m'), (2, 'Pseudotsuga menziesii', 'Pacific Northwest', '40m');
INSERT INTO trees(treeId, species, region, height) VALUES (4, 'test', 'Pacific Northwest', '0m');
INSERT INTO aow(NoteNum, DetailAssessmentAndPlanning, WagingWar, StrategicAttack) VALUES (1, 'chapter one notes: ', 'chapter two notes: ', 'chapter three notes: ');
INSERT INTO fungi(speciesid, region, habitat) VALUES (1, 'northeast', 'forest'), (2, 'southwest', 'sand');
INSERT INTO fungi(speciesid, region, habitat) VALUES (3, 'northeast', 'test');


SET IDENTITY_INSERT authors_history ON
INSERT INTO authors_history(id, first_name, middle_name, last_name, year_of_publish, books_published)
Expand Down
2 changes: 2 additions & 0 deletions src/Service.Tests/DatabaseSchema-MySql.sql
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,9 @@ INSERT INTO type_table(id, byte_types, short_types, int_types, long_types, strin
(4, 255, 32767, 2147483647, 9223372036854775807, 'null', 3.4E38, 1.7E308, 2.929292E-14, true, '9999-12-31 23:59:59', 0xFFFFFFFF),
(5, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
INSERT INTO trees(treeId, species, region, height) VALUES (1, 'Tsuga terophylla', 'Pacific Northwest', '30m'), (2, 'Pseudotsuga menziesii', 'Pacific Northwest', '40m');
INSERT INTO trees(treeId, species, region, height) VALUES (4, 'test', 'Pacific Northwest', '0m');
INSERT INTO fungi(speciesid, region, habitat) VALUES (1, 'northeast', 'forest'), (2, 'southwest', 'sand');
INSERT INTO fungi(speciesid, region, habitat) VALUES (3, 'northeast', 'test');
INSERT INTO notebooks(id, notebookname, color, ownername) VALUES (1, 'Notebook1', 'red', 'Sean'), (2, 'Notebook2', 'green', 'Ani'), (3, 'Notebook3', 'blue', 'Jarupat'), (4, 'Notebook4', 'yellow', 'Aaron');
INSERT INTO journals(id, journalname, color, ownername)
VALUES
Expand Down
2 changes: 2 additions & 0 deletions src/Service.Tests/DatabaseSchema-PostgreSql.sql
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,9 @@ INSERT INTO type_table(id, short_types, int_types, long_types, string_types, sin
(5, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
INSERT INTO type_table(id, uuid_types) values(10, 'D1D021A8-47B4-4AE4-B718-98E89C41A161');
INSERT INTO trees("treeId", species, region, height) VALUES (1, 'Tsuga terophylla', 'Pacific Northwest', '30m'), (2, 'Pseudotsuga menziesii', 'Pacific Northwest', '40m');
INSERT INTO trees("treeId", species, region, height) VALUES (4, 'test', 'Pacific Northwest', '0m');
INSERT INTO fungi(speciesid, region, habitat) VALUES (1, 'northeast', 'forest'), (2, 'southwest', 'sand');
INSERT INTO fungi(speciesid, region, habitat) VALUES (3, 'northeast', 'test');
INSERT INTO notebooks(id, noteBookName, color, ownerName) VALUES (1, 'Notebook1', 'red', 'Sean'), (2, 'Notebook2', 'green', 'Ani'), (3, 'Notebook3', 'blue', 'Jarupat'), (4, 'Notebook4', 'yellow', 'Aaron');
INSERT INTO journals(id, journalname, color, ownername)
VALUES
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1829,6 +1829,17 @@
],
Mappings: {
species: fancyName
},
Relationships: {
fungus: {
TargetEntity: Fungus,
SourceFields: [
species
],
TargetFields: [
habitat
]
}
}
}
},
Expand Down Expand Up @@ -1900,6 +1911,17 @@
],
Mappings: {
spores: hazards
},
Relationships: {
Shrub: {
TargetEntity: Shrub,
SourceFields: [
habitat
],
TargetFields: [
species
]
}
}
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1279,6 +1279,17 @@
],
Mappings: {
species: fancyName
},
Relationships: {
fungus: {
TargetEntity: Fungus,
SourceFields: [
species
],
TargetFields: [
habitat
]
}
}
}
},
Expand Down Expand Up @@ -1350,6 +1361,17 @@
],
Mappings: {
spores: hazards
},
Relationships: {
Shrub: {
TargetEntity: Shrub,
SourceFields: [
habitat
],
TargetFields: [
species
]
}
}
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1376,6 +1376,17 @@
],
Mappings: {
species: fancyName
},
Relationships: {
fungus: {
TargetEntity: Fungus,
SourceFields: [
species
],
TargetFields: [
habitat
]
}
}
}
},
Expand Down Expand Up @@ -1447,6 +1458,17 @@
],
Mappings: {
spores: hazards
},
Relationships: {
Shrub: {
TargetEntity: Shrub,
SourceFields: [
habitat
],
TargetFields: [
species
]
}
}
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,31 @@ public async Task OneToOneJoinQuery()
await OneToOneJoinQuery(dwSqlQuery);
}

/// <summary>
/// Test query on One-To-One relationship when the fields defining
/// the relationship in the entity include fields that are mapped in
/// that same entity.
/// </summary>
/// <exception cref="NotImplementedException"></exception>
[TestMethod]
public async Task OneToOneJoinQueryWithMappedFieldNamesInRelationship()
{
string dwSqlQuery = @"
SELECT COALESCE('['+STRING_AGG('{'+N'""fancyName"":' + ISNULL('""' + STRING_ESCAPE([fancyName],'json') + '""','null')+','+N'""fungus"":' + ISNULL([fungus],'null')+'}',', ')+']','[]')
FROM (
SELECT TOP 100 [table0].[species] AS [fancyName],
(SELECT TOP 1 '{""habitat"":""' + STRING_ESCAPE([table1].[habitat], 'json') + '""}'
FROM [dbo].[fungi] AS [table1]
WHERE [table0].[species] = [table1].[habitat] AND [table1].[habitat] = [table0].[species]
ORDER BY [table1].[speciesid] ASC) AS [fungus]
FROM [dbo].[trees] AS [table0]
WHERE 1 = 1
ORDER BY [table0].[treeId] ASC
) AS [table0]";

await OneToOneJoinQueryWithMappedFieldNamesInRelationship(dwSqlQuery);
}

/// <summary>
/// Test getting a single item by use of primary key
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,32 @@ public async Task OneToOneJoinQuery(string dbQuery)
SqlTestHelper.PerformTestEqualJsonStrings(expected, actual.GetProperty("items").ToString());
}

/// <summary>
/// Test One-To-One relationship when the fields defining
/// the relationship in the entity include fields that are mapped in
/// that same entity.
/// <summary>
[TestMethod]
public async Task OneToOneJoinQueryWithMappedFieldNamesInRelationship(string dbQuery)
{
string graphQLQueryName = "shrubs";
string graphQLQuery = @"query {
shrubs {
items {
fancyName
fungus {
habitat
}
}
}
}";

JsonElement actual = await base.ExecuteGraphQLRequestAsync(graphQLQuery, graphQLQueryName, isAuthenticated: false);
string expected = await GetDatabaseResultAsync(dbQuery);

SqlTestHelper.PerformTestEqualJsonStrings(expected, actual.GetProperty("items").ToString());
}

/// <summary>
/// This deeply nests a many-to-one/one-to-many join multiple times to
/// show that it still results in a valid query.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,34 @@ public async Task OneToOneJoinQuery()
await OneToOneJoinQuery(msSqlQuery);
}

/// <summary>
/// Test query on One-To-One relationship when the fields defining
/// the relationship in the entity include fields that are mapped in
/// that same entity.
/// <summary>
[TestMethod]
public async Task OneToOneJoinQueryWithMappedFieldNamesInRelationship()
{
string msSqlQuery = @"
SELECT TOP 100 [table0].[species] AS [fancyName]
,JSON_QUERY([table1_subq].[data]) AS [fungus]
FROM [dbo].[trees] AS [table0]
OUTER APPLY (
SELECT TOP 1 [table1].[habitat] AS [habitat]
FROM [dbo].[fungi] AS [table1]
WHERE [table1].[habitat] = [table0].[species]
ORDER BY [table1].[habitat] ASC
FOR JSON PATH
,INCLUDE_NULL_VALUES
,WITHOUT_ARRAY_WRAPPER
) AS [table1_subq]([data])
WHERE 1 = 1
FOR JSON PATH
,INCLUDE_NULL_VALUES";

await OneToOneJoinQueryWithMappedFieldNamesInRelationship(msSqlQuery);
}

[TestMethod]
public async Task QueryWithSingleColumnPrimaryKey()
{
Expand Down

0 comments on commit d2aa5aa

Please sign in to comment.