[Bug]: Validating entity relationships throws unhandled exception
Aniruddh25 opened this issue · 7 comments
What happened?
Cannot figure out what's wrong with this sample config. Doing dab validate
causes an Unhandled Exception
with the stack trace as provided in the log output.
{
"$schema": "https://dataapibuilder.azureedge.net/schemas/v0.5.34/dab.draft.schema.json",
"data-source": {
"database-type": "mssql",
"options": {
"set-session-context": false
},
"connection-string": "Server=XXXXX;Persist Security Info=False;User ID=<USERHERE>;Password=<PWD HERE> ;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"
},
"runtime": {
"rest": {
"enabled": true,
"path": "/api"
},
"graphql": {
"allow-introspection": true,
"enabled": true,
"path": "/graphql"
},
"host": {
"mode": "development",
"cors": {
"origins": [],
"allow-credentials": false
},
"authentication": {
"provider": "StaticWebApps"
}
}
},
"entities": {
"Publisher": {
"source": {
"object": "publishers",
"type": "table",
"key-fields": ["id"]
},
"graphql": {
"enabled": true,
"type": {
"singular": "Publisher",
"plural": "Publishers"
}
},
"rest": {
"enabled": true
},
"permissions": [
{
"role": "anonymous",
"actions": [
{
"action": "read"
}
]
},
{
"role": "authenticated",
"actions": [
{
"action": "create"
},
{
"action": "read"
},
{
"action": "update"
},
{
"action": "delete"
}
]
},
{
"role": "policy_tester_01",
"actions": [
{
"action": "read",
"fields": {
"exclude": [],
"include": [
"*"
]
},
"policy": {
"database": "@item.id eq 1940"
}
},
{
"action": "update",
"fields": {
"exclude": [],
"include": [
"*"
]
}
},
{
"action": "create"
},
{
"action": "delete"
}
]
},
{
"role": "policy_tester_02",
"actions": [
{
"action": "read",
"fields": {
"exclude": [],
"include": [
"*"
]
},
"policy": {
"database": "@item.id ne 1940"
}
},
{
"action": "update",
"fields": {
"exclude": [],
"include": [
"*"
]
}
},
{
"action": "create"
},
{
"action": "delete"
}
]
},
{
"role": "policy_tester_03",
"actions": [
{
"action": "read",
"fields": {
"exclude": [],
"include": [
"*"
]
},
"policy": {
"database": "@item.id ne 1940"
}
},
{
"action": "update",
"fields": {
"exclude": [],
"include": [
"*"
]
}
},
{
"action": "create"
},
{
"action": "delete"
}
]
},
{
"role": "policy_tester_04",
"actions": [
{
"action": "read",
"fields": {
"exclude": [],
"include": [
"*"
]
},
"policy": {
"database": "@item.id eq 1940"
}
},
{
"action": "update",
"fields": {
"exclude": [],
"include": [
"*"
]
}
},
{
"action": "create"
},
{
"action": "delete"
}
]
},
{
"role": "policy_tester_06",
"actions": [
{
"action": "read",
"fields": {
"exclude": [],
"include": [
"*"
]
},
"policy": {
"database": "@item.id eq 1940"
}
},
{
"action": "update",
"fields": {
"exclude": [],
"include": [
"*"
]
}
},
{
"action": "create"
},
{
"action": "delete"
}
]
},
{
"role": "database_policy_tester",
"actions": [
{
"action": "create",
"policy": {
"database": "@item.name ne 'New publisher'"
}
},
{
"action": "update",
"policy": {
"database": "@item.id ne 1234"
}
},
{
"action": "read",
"policy": {
"database": "@item.id ne 1234 or @item.id gt 1940"
}
}
]
}
],
"relationships": {
"books": {
"cardinality": "many",
"target.entity": "Book",
"source.fields": ["id"],
"target.fields": ["publisher_id"],
"linking.source.fields": [],
"linking.target.fields": []
}
}
},
"Book": {
"source": {
"object": "books",
"type": "table",
"key-fields": ["id"]
},
"graphql": {
"enabled": true,
"type": {
"singular": "book",
"plural": "books"
}
},
"rest": {
"enabled": true
},
"permissions": [
{
"role": "anonymous",
"actions": [
{
"action": "create"
},
{
"action": "read"
},
{
"action": "update"
},
{
"action": "delete"
}
]
},
{
"role": "authenticated",
"actions": [
{
"action": "create"
},
{
"action": "read"
},
{
"action": "update"
},
{
"action": "delete"
}
]
},
{
"role": "policy_tester_01",
"actions": [
{
"action": "read",
"fields": {
"exclude": [],
"include": [
"*"
]
},
"policy": {
"database": "@item.title eq 'Policy-Test-01'"
}
},
{
"action": "update",
"fields": {
"exclude": [],
"include": [
"*"
]
}
},
{
"action": "create"
},
{
"action": "delete"
}
]
},
{
"role": "policy_tester_02",
"actions": [
{
"action": "read",
"fields": {
"exclude": [],
"include": [
"*"
]
},
"policy": {
"database": "@item.title ne 'Policy-Test-01'"
}
},
{
"action": "update",
"fields": {
"exclude": [],
"include": [
"*"
]
}
},
{
"action": "create"
},
{
"action": "delete"
}
]
},
{
"role": "policy_tester_03",
"actions": [
{
"action": "read",
"fields": {
"exclude": [],
"include": [
"*"
]
},
"policy": {
"database": "@item.title eq 'Policy-Test-01'"
}
},
{
"action": "update",
"fields": {
"exclude": [],
"include": [
"*"
]
}
},
{
"action": "create"
},
{
"action": "delete"
}
]
},
{
"role": "policy_tester_04",
"actions": [
{
"action": "read",
"fields": {
"exclude": [],
"include": [
"*"
]
},
"policy": {
"database": "@item.title ne 'Policy-Test-01'"
}
},
{
"action": "update",
"fields": {
"exclude": [],
"include": [
"*"
]
}
},
{
"action": "create"
},
{
"action": "delete"
}
]
},
{
"role": "policy_tester_05",
"actions": [
{
"action": "read",
"fields": {
"exclude": [],
"include": [
"*"
]
},
"policy": {
"database": "@item.id ne 9"
}
},
{
"action": "update",
"fields": {
"exclude": [],
"include": [
"*"
]
}
},
{
"action": "create"
},
{
"action": "delete"
}
]
},
{
"role": "policy_tester_06",
"actions": [
{
"action": "read",
"fields": {
"exclude": [],
"include": [
"*"
]
},
"policy": {
"database": "@item.id ne 10"
}
},
{
"action": "create"
},
{
"action": "delete"
},
{
"action": "update",
"fields": {
"exclude": [],
"include": [
"*"
]
}
}
]
},
{
"role": "policy_tester_07",
"actions": [
{
"action": "delete",
"fields": {
"exclude": [],
"include": [
"*"
]
},
"policy": {
"database": "@item.id ne 9"
}
},
{
"action": "read",
"fields": {
"exclude": [],
"include": [
"*"
]
}
},
{
"action": "update",
"fields": {
"exclude": [],
"include": [
"*"
]
},
"policy": {
"database": "@item.id ne 9"
}
},
{
"action": "create"
}
]
},
{
"role": "policy_tester_08",
"actions": [
{
"action": "read",
"fields": {
"exclude": [],
"include": [
"*"
]
}
},
{
"action": "delete",
"fields": {
"exclude": [],
"include": [
"*"
]
},
"policy": {
"database": "@item.id eq 9"
}
},
{
"action": "update",
"fields": {
"exclude": [],
"include": [
"*"
]
},
"policy": {
"database": "@item.id eq 9"
}
},
{
"action": "create"
}
]
},
{
"role": "test_role_with_noread",
"actions": [
{
"action": "create"
},
{
"action": "update"
},
{
"action": "delete"
}
]
},
{
"role": "test_role_with_excluded_fields",
"actions": [
{
"action": "read",
"fields": {
"exclude": [
"publisher_id"
]
}
},
{
"action": "create"
},
{
"action": "update"
},
{
"action": "delete"
}
]
},
{
"role": "test_role_with_policy_excluded_fields",
"actions": [
{
"action": "read",
"fields": {
"exclude": [
"publisher_id"
]
},
"policy": {
"database": "@item.title ne 'Test'"
}
},
{
"action": "create"
},
{
"action": "update"
},
{
"action": "delete"
}
]
}
],
"mappings": {
"id": "id",
"title": "title"
},
"relationships": {
"publishers": {
"cardinality": "one",
"target.entity": "Publisher",
"source.fields": ["publisher_id"],
"target.fields": ["id"],
"linking.source.fields": [],
"linking.target.fields": []
}
}
}
}
}
Version
0.12.0-rc
What database are you using?
Azure SQL
What hosting model are you using?
Local (including CLI)
Which API approach are you accessing DAB through?
No response
Relevant log output
Information: Validating entity relationships.
Unhandled exception. System.AggregateException: One or more errors occurred. (Value cannot be null. (Parameter 'key'))
---> System.ArgumentNullException: Value cannot be null. (Parameter 'key')
at System.Collections.Generic.Dictionary`2.FindValue(TKey key)
at System.Collections.Generic.Dictionary`2.TryGetValue(TKey key, TValue& value)
at Azure.DataApiBuilder.Core.Services.SqlMetadataProvider`3.TryGetBackingColumn(String entityName, String field, String& name) in /_/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs:line 220
at Azure.DataApiBuilder.Core.Configurations.RuntimeConfigValidator.GetFieldsNotBackedByColumnsInDB(List`1 invalidColumns, String[] fields, String entityName, ISqlMetadataProvider sqlMetadataProvider) in /_/src/Core/Configurations/RuntimeConfigValidator.cs:line 1093
at Azure.DataApiBuilder.Core.Configurations.RuntimeConfigValidator.ValidateRelationshipsInConfig(RuntimeConfig runtimeConfig, IMetadataProviderFactory sqlMetadataProviderFactory) in /_/src/Core/Configurations/RuntimeConfigValidator.cs:line 875
at Azure.DataApiBuilder.Core.Configurations.RuntimeConfigValidator.ValidateEntitiesMetadata(RuntimeConfig runtimeConfig, ILoggerFactory loggerFactory) in /_/src/Core/Configurations/RuntimeConfigValidator.cs:line 232
at Azure.DataApiBuilder.Core.Configurations.RuntimeConfigValidator.TryValidateConfig(String configFilePath, ILoggerFactory loggerFactory) in /_/src/Core/Configurations/RuntimeConfigValidator.cs:line 169
--- End of inner exception stack trace ---
at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
at System.Threading.Tasks.Task`1.get_Result()
at Cli.ConfigGenerator.IsConfigValid(ValidateOptions options, FileSystemRuntimeConfigLoader loader, IFileSystem fileSystem) in /_/src/Cli/ConfigGenerator.cs:line 1132
at Cli.Commands.ValidateOptions.Handler(ILogger logger, FileSystemRuntimeConfigLoader loader, IFileSystem fileSystem) in /_/src/Cli/Commands/ValidateOptions.cs:line 31
at Cli.Program.<>c__DisplayClass2_0.<Execute>b__5(ValidateOptions options) in /_/src/Cli/Program.cs:line 67
at CommandLine.ParserResultExtensions.MapResult[T1,T2,T3,T4,T5,T6,T7,TResult](ParserResult`1 result, Func`2 parsedFunc1, Func`2 parsedFunc2, Func`2 parsedFunc3, Func`2 parsedFunc4, Func`2 parsedFunc5, Func`2 parsedFunc6, Func`2 parsedFunc7, Func`2 notParsedFunc)
at Cli.Program.Execute(String[] args, ILogger cliLogger, IFileSystem fileSystem, FileSystemRuntimeConfigLoader loader) in /_/src/Cli/Program.cs:line 61
at Cli.Program.Main(String[] args) in /_/src/Cli/Program.cs:line 41
Code of Conduct
- I agree to follow this project's Code of Conduct
Does "$schema": "https://dataapibuilder.azureedge.net/schemas/v0.5.34/dab.draft.schema.json",
not cause issues with JSON validation given that the version used is 0.5.34 instead of "$schema": "https://github.com/Azure/data-api-builder/releases/download/v0.12.0-rc/dab.draft.schema.json",
I get a different stack trace:
Information: Validating entity relationships.
Unhandled exception. System.AggregateException: One or more errors occurred. (The given key 'Publisher' was not present in the dictionary.)
---> System.Collections.Generic.KeyNotFoundException: The given key 'Publisher' was not present in the dictionary.
at System.Collections.Generic.Dictionary2.get_Item(TKey key) at Azure.DataApiBuilder.Core.Services.SqlMetadataProvider
3.TryGetBackingColumn(String entityName, String field, String& name) in //src/Core/Services/MetadataProviders/SqlMetadataProvider.cs:line 220
at Azure.DataApiBuilder.Core.Configurations.RuntimeConfigValidator.GetFieldsNotBackedByColumnsInDB(List`1 invalidColumns, String[] fields, String entityName, ISqlMetadataProvider sqlMetadataProvider) in //src/Core/Configurations/RuntimeConfigValidator.cs:line 1093
at Azure.DataApiBuilder.Core.Configurations.RuntimeConfigValidator.ValidateRelationshipsInConfig(RuntimeConfig runtimeConfig, IMetadataProviderFactory sqlMetadataProviderFactory) in //src/Core/Configurations/RuntimeConfigValidator.cs:line 875
at Azure.DataApiBuilder.Core.Configurations.RuntimeConfigValidator.ValidateEntitiesMetadata(RuntimeConfig runtimeConfig, ILoggerFactory loggerFactory) in //src/Core/Configurations/RuntimeConfigValidator.cs:line 232
at Azure.DataApiBuilder.Core.Configurations.RuntimeConfigValidator.TryValidateConfig(String configFilePath, ILoggerFactory loggerFactory) in /_/src/Core/Configurations/RuntimeConfigValidator.cs:line 169
this was not caught presumably because it doesn't occur on dab start
and only appears during dab validate
The above comment holds true when the dab-config.json
you provide to dab validate --config "path"
has an invalid connection string. Because dab validate
accumulates all errors instead of halting at first error, it attempts to find out everything wrong with the config. That won't work when dab can't connect to the database and proceed to validate your config against db metadata.
@Aniruddh25 , can you confirm whether your config had a valid connection string or if you were using an environment variable placeholder for your connection string?
Agreed that the error message isn't helpful so one path forward here is:
- halt validation when the connection string is invalid/can't connect to DB. When DAB can't connect to DB, DAB can't confirm whether config is correct. (@abhishekkumams , thoughts?
The above comment holds true when the
dab-config.json
you provide todab validate --config "path"
has an invalid connection string. Becausedab validate
accumulates all errors instead of halting at first error, it attempts to find out everything wrong with the config. That won't work when dab can't connect to the database and proceed to validate your config against db metadata.@Aniruddh25 , can you confirm whether your config had a valid connection string or if you were using an environment variable placeholder for your connection string?
Agreed that the error message isn't helpful so one path forward here is:
- halt validation when the connection string is invalid/can't connect to DB. When DAB can't connect to DB, DAB can't confirm whether config is correct. (@abhishekkumams , thoughts?
I found the same in my investiation. This only arises when the connection string is invalid. My thought was to add a try catch to the validation steps that rely on the connection string and then some messaging to help explain the problem.