Skip to content

Commit

Permalink
feat: introduce specific exceptions when segments are unexpected or m…
Browse files Browse the repository at this point in the history
…issing (#648)
  • Loading branch information
Seddryck authored Nov 6, 2023
1 parent 1f8cdb0 commit ea40a08
Show file tree
Hide file tree
Showing 18 changed files with 85 additions and 39 deletions.
14 changes: 13 additions & 1 deletion DubUrl.Adomd.Testing/Rewriting/PowerBiDesktopRewriterTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ public void Map_UrlInfoWithPortAndSegment_DataSource(string input)

[Test]
[TestCase("foo/bar")]
[TestCase("./foo/bar")]
[TestCase("./foo?bar=brz")]
public void Map_InvalidUrlInfo_DataSource(string input)
{
Expand All @@ -80,6 +79,19 @@ public void Map_InvalidUrlInfo_DataSource(string input)
Assert.Throws<InvalidConnectionUrlException>(() => Rewriter.Execute(urlInfo));
}

[Test]
[TestCase("./foo/bar")]
public void Map_InvalidUrlInfo_TooManySegments(string input)
{
var discoverer = new Mock<IPowerBiDiscoverer>();
discoverer.Setup(x => x.GetPowerBiProcesses(false))
.Returns(new[] { new PowerBiProcess("foo", 12345, PowerBiType.PowerBI) });

var urlInfo = new UrlInfo() { Host = input.Split('/')[0], Segments = input.Split('/').Skip(1).ToArray() };
var Rewriter = new PowerBiDesktopRewriter(ConnectionStringBuilder, discoverer.Object);
Assert.Throws<InvalidConnectionUrlTooManySegmentsException>(() => Rewriter.Execute(urlInfo));
}

[Test]
public void Map_UrlInfo_DiscovererInteraction()
{
Expand Down
7 changes: 5 additions & 2 deletions DubUrl.Adomd/Rewriting/PowerBiDesktopRewriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ namespace DubUrl.Adomd.Rewriting
{
internal class PowerBiDesktopRewriter : ConnectionStringRewriter
{
private const string EXCEPTION_DATABASE_NAME = "Power BI Desktop";
protected internal const string SERVER_KEYWORD = "Data Source";

protected internal const string DEFAULT_LOCALHOST = "localhost";
protected internal static readonly string[] VALID_HOSTS =
new[] { "127.0.0.1", ".", string.Empty, DEFAULT_LOCALHOST };
Expand Down Expand Up @@ -56,9 +56,12 @@ public override void Execute(UrlInfo urlInfo)

public int GetPortFromSegments(string[] segments)
{
if(segments==null || !segments.Any())
throw new InvalidConnectionUrlMissingSegmentsException(EXCEPTION_DATABASE_NAME);

var pbiName = segments.Length == 1
? segments[0]
: throw new ArgumentOutOfRangeException(nameof(segments));
: throw new InvalidConnectionUrlTooManySegmentsException(EXCEPTION_DATABASE_NAME, segments);

var processes = Discoverer.GetPowerBiProcesses();
if (processes.Any(x => x.Name == pbiName))
Expand Down
3 changes: 2 additions & 1 deletion DubUrl.Core/Parsing/Parser.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using DubUrl.Mapping;
using DubUrl.Rewriting;
using System;
using System.Collections.Generic;
using System.Data.Common;
Expand Down Expand Up @@ -52,7 +53,7 @@ public virtual UrlInfo Parse(string url)
if (key != null)
{
if (options.ContainsKey(key))
throw new ArgumentException();
throw new InvalidConnectionUrlException("The query string cannot contains twice the same keyword.");
var value = nameValues[key];
if (value != null)
options.Add(key, value);
Expand Down
7 changes: 5 additions & 2 deletions DubUrl.Core/Rewriting/Implementation/Db2Rewriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace DubUrl.Rewriting.Implementation
{
internal class Db2Rewriter : ConnectionStringRewriter
{
private const string EXCEPTION_DATABASE_NAME = "DB2";
internal const string SERVER_KEYWORD = "Server";
internal const string DATABASE_KEYWORD = "Database";
internal const string USERNAME_KEYWORD = "User ID";
Expand Down Expand Up @@ -42,10 +43,12 @@ internal class DatabaseMapper : BaseTokenMapper
{
public override void Execute(UrlInfo urlInfo)
{
if (urlInfo.Segments.Length == 1)
if (urlInfo.Segments == null || !urlInfo.Segments.Any())
throw new InvalidConnectionUrlMissingSegmentsException(EXCEPTION_DATABASE_NAME);
else if (urlInfo.Segments.Length == 1)
Specificator.Execute(DATABASE_KEYWORD, urlInfo.Segments.First());
else
throw new ArgumentOutOfRangeException();
throw new InvalidConnectionUrlTooManySegmentsException(EXCEPTION_DATABASE_NAME, urlInfo.Segments);
}
}

Expand Down
5 changes: 4 additions & 1 deletion DubUrl.Core/Rewriting/Implementation/DrillOdbcRewriter.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using DubUrl.Locating.OdbcDriver;
using DubUrl.Locating.Options;
using DubUrl.Mapping;
using DubUrl.Mapping.Database;
using DubUrl.Parsing;
using DubUrl.Rewriting.Tokening;
using System;
Expand All @@ -15,6 +17,7 @@ namespace DubUrl.Rewriting.Implementation
{
public class DrillOdbcRewriter : OdbcRewriter, IOdbcConnectionStringRewriter
{
private const string EXCEPTION_DATABASE_NAME = "ODBC for Apache Drill";
protected internal const string PORT_KEYWORD = "Port";
protected internal const string SCHEMA_KEYWORD = "Schema";
protected internal const string AUTHENTICATION_KEYWORD = "AuthenticationType";
Expand Down Expand Up @@ -80,7 +83,7 @@ public override void Execute(UrlInfo urlInfo)
if (urlInfo.Segments.Length == 1 || (urlInfo.Segments.Length == 2 && urlInfo.Segments.First() == "DRILL"))
Specificator.Execute(SCHEMA_KEYWORD, urlInfo.Segments.Last());
else if (urlInfo.Segments.Length > 1)
throw new ArgumentOutOfRangeException();
throw new InvalidConnectionUrlTooManySegmentsException(EXCEPTION_DATABASE_NAME, urlInfo.Segments);
}
}

Expand Down
4 changes: 2 additions & 2 deletions DubUrl.Core/Rewriting/Implementation/DuckdbRewriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public override void Execute(UrlInfo urlInfo)
if (!urlInfo.Segments.Any())
segments.Add(":memory:");
else
throw new ArgumentOutOfRangeException($"Expecting no segment in the connectionUrl because the InMemory mode was activated by specifying the host '{urlInfo.Host}' but get {urlInfo.Segments.Length} segments. The list of segments was '{string.Join("', '", urlInfo.Segments.ToArray())}'");
throw new InvalidConnectionUrlException($"Expecting no segment in the connectionUrl because the InMemory mode was activated by specifying the host '{urlInfo.Host}' but get {urlInfo.Segments.Length} segments. The list of segments was '{string.Join("', '", urlInfo.Segments.ToArray())}'");
}
else if (string.IsNullOrEmpty(urlInfo.Host) && urlInfo.Segments.Length > 1 && string.IsNullOrEmpty(urlInfo.Segments[0]))
segments = urlInfo.Segments.Skip(1).ToList();
Expand All @@ -52,7 +52,7 @@ public override void Execute(UrlInfo urlInfo)
private static string BuildPath(IEnumerable<string> segments)
{
if (segments == null || !segments.Any())
throw new ArgumentException();
throw new InvalidConnectionUrlMissingSegmentsException("DuckDB");

var path = new StringBuilder();
foreach (var segment in segments)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public override void Execute(UrlInfo urlInfo)
if (urlInfo.Segments.Length > 0 && urlInfo.Segments.Length <= 2)
Specificator.Execute(DATABASE_KEYWORD, urlInfo.Segments.Last());
else
throw new ArgumentOutOfRangeException($"Expecting one or two segments in the connectionUrl but was {urlInfo.Segments.Length} segments. The list of segments was '{string.Join("', '", urlInfo.Segments.ToArray())}'");
throw new InvalidConnectionUrlException($"The connection-url for Microsoft SQL Server is expecting one or two segments. This connection-url is containing {urlInfo.Segments.Length} segments: '{string.Join("', '", urlInfo.Segments.ToArray())}'");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace DubUrl.Rewriting.Implementation
{
internal class MySqlConnectorRewriter : ConnectionStringRewriter
{
private const string EXCEPTION_DATABASE_NAME = "MySQL";
protected internal const string SERVER_KEYWORD = "Server";
protected internal const string PORT_KEYWORD = "Port";
protected internal const string DATABASE_KEYWORD = "Database";
Expand Down Expand Up @@ -59,12 +60,12 @@ internal class DatabaseMapper : BaseTokenMapper
{
public override void Execute(UrlInfo urlInfo)
{
if (urlInfo.Segments.Length == 1)
if (urlInfo.Segments==null || !urlInfo.Segments.Any())
throw new InvalidConnectionUrlMissingSegmentsException(EXCEPTION_DATABASE_NAME);
else if (urlInfo.Segments.Length == 1)
Specificator.Execute(DATABASE_KEYWORD, urlInfo.Segments.First());
else if (urlInfo.Segments.Length > 1)
throw new ArgumentOutOfRangeException(nameof(urlInfo), $"The connection-url contains more than one segment '{string.Join("', '", urlInfo.Segments)}'. For MySQL, exactly one segment is expected.");
else
throw new ArgumentOutOfRangeException(nameof(urlInfo), $"The connection-url contains no segment. For MySQL, exactly one segment is expected.");
throw new InvalidConnectionUrlTooManySegmentsException(EXCEPTION_DATABASE_NAME, urlInfo.Segments);
}
}
}
Expand Down
5 changes: 2 additions & 3 deletions DubUrl.Core/Rewriting/Implementation/OdbcDbqRewriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public override void Execute(UrlInfo urlInfo)
private static string BuildPath(IEnumerable<string> segments)
{
if (segments == null || !segments.Any())
throw new ArgumentException();
throw new InvalidConnectionUrlMissingSegmentsException("ODBC DBQ");

var path = new StringBuilder();
foreach (var segment in segments)
Expand All @@ -72,7 +72,6 @@ public DriverMapper(DriverLocatorFactory driverLocatorFactory)

public override void Execute(UrlInfo urlInfo)
{

if (!urlInfo.Options.ContainsKey(DRIVER_KEYWORD))
{
var otherSchemes = urlInfo.Schemes.Where(x => x != "odbc");
Expand Down Expand Up @@ -112,7 +111,7 @@ public override void Execute(UrlInfo urlInfo)
foreach (var remainingOption in remainingOptions)
{
if (Enum.TryParse(remainingOption, scheme, out var value))
options.Add(remainingOption, value ?? throw new ArgumentNullException());
options.Add(remainingOption, value ?? throw new InvalidConnectionUrlException($"Connection Url for ODBC DBQ is specifying an unexpected value 'null' for option '{remainingOption.Name}'"));
}
}
var driverLocator = DriverLocatorFactory.Instantiate(secondScheme, options);
Expand Down
11 changes: 5 additions & 6 deletions DubUrl.Core/Rewriting/Implementation/OdbcRewriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
using System.Text;
using System.Threading.Tasks;


namespace DubUrl.Rewriting.Implementation
{
public class OdbcRewriter : ConnectionStringRewriter, IOdbcConnectionStringRewriter
{
private const string EXCEPTION_DATABASE_NAME = "ODBC";
protected internal const string SERVER_KEYWORD = "Server";
protected internal const string DATABASE_KEYWORD = "Database";
protected internal const string USERNAME_KEYWORD = "Uid";
Expand All @@ -39,7 +39,7 @@ protected OdbcRewriter(DbConnectionStringBuilder csb, BaseTokenMapper[] tokenMap

internal DriverLocatorFactory DriverLocatorFactory
=> (TokenMappers.Single(x => x is DriverMapper) as DriverMapper)?.DriverLocatorFactory
?? throw new ArgumentNullException();
?? throw new NullReferenceException();

protected internal class HostMapper : BaseTokenMapper
{
Expand Down Expand Up @@ -67,7 +67,6 @@ public DriverMapper(DriverLocatorFactory driverLocatorFactory)

public override void Execute(UrlInfo urlInfo)
{

if (!urlInfo.Options.ContainsKey(DRIVER_KEYWORD))
{
var otherSchemes = urlInfo.Schemes.Where(x => x != "odbc");
Expand Down Expand Up @@ -107,7 +106,7 @@ public override void Execute(UrlInfo urlInfo)
foreach (var remainingOption in remainingOptions)
{
if (Enum.TryParse(remainingOption, scheme, out var value))
options.Add(remainingOption, value ?? throw new ArgumentNullException());
options.Add(remainingOption, value ?? throw new InvalidConnectionUrlException($"Connection Url for ODBC is specifying an unexpected value 'null' for option '{remainingOption.Name}'"));
}
}
var driverLocator = DriverLocatorFactory.Instantiate(secondScheme, options);
Expand Down Expand Up @@ -146,7 +145,7 @@ public override void Execute(UrlInfo urlInfo)
if (urlInfo.Segments.Length <= 2)
Specificator.Execute(DATABASE_KEYWORD, urlInfo.Segments.Last());
else
throw new ArgumentOutOfRangeException();
throw new InvalidConnectionUrlTooManySegmentsException(EXCEPTION_DATABASE_NAME, urlInfo.Segments);
}
}

Expand All @@ -160,7 +159,7 @@ public override void Execute(UrlInfo urlInfo)
{
if (!option.Value.StartsWith("{") || !option.Value.EndsWith("}"))
if (option.Value.StartsWith("{") ^ option.Value.EndsWith("}"))
throw new ArgumentOutOfRangeException($"The value of the option '{DRIVER_KEYWORD}' must start with a '{{' and end with '}}' or both should be missing. The value was '{option.Value}'");
throw new InvalidConnectionUrlException($"The value of the option '{DRIVER_KEYWORD}' must start with a '{{' and end with '}}' or both should be missing. The value was '{option.Value}'");
else
Specificator.Execute(option.Key, $"{{{option.Value}}}");
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace DubUrl.Rewriting.Implementation
{
internal class OracleManagedDataAccessRewriter : ConnectionStringRewriter
{
private const string EXCEPTION_DATABASE_NAME = "Oracle Managed Data Access";
protected internal const string DATASOURCE_KEYWORD = "DATA SOURCE";
protected internal const string SERVER_KEYWORD = "HOST";
protected internal const string PORT_KEYWORD = "PORT";
Expand Down Expand Up @@ -61,7 +62,7 @@ public override void Execute(UrlInfo urlInfo)
$"({SERVER_KEYWORD}={urlInfo.Host})({PORT_KEYWORD}={(urlInfo.Port > 0 ? urlInfo.Port : 1521)}))(CONNECT_DATA=" +
$"({DATABASE_KEYWORD}={urlInfo.Segments.First()})))");
else
throw new ArgumentOutOfRangeException();
throw new InvalidConnectionUrlTooManySegmentsException(EXCEPTION_DATABASE_NAME, urlInfo.Segments);
}
}
}
Expand Down
7 changes: 5 additions & 2 deletions DubUrl.Core/Rewriting/Implementation/PostgresqlRewriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace DubUrl.Rewriting.Implementation
{
internal class PostgresqlRewriter : ConnectionStringRewriter
{
private const string EXCEPTION_DATABASE_NAME = "Postgresql";
protected internal const string SERVER_KEYWORD = "Host";
protected internal const string PORT_KEYWORD = "Port";
protected internal const string DATABASE_KEYWORD = "Database";
Expand Down Expand Up @@ -68,10 +69,12 @@ internal class DatabaseMapper : BaseTokenMapper
{
public override void Execute(UrlInfo urlInfo)
{
if (urlInfo.Segments.Length == 1)
if (urlInfo.Segments==null || !urlInfo.Segments.Any())
throw new InvalidConnectionUrlMissingSegmentsException(EXCEPTION_DATABASE_NAME);
else if (urlInfo.Segments.Length == 1)
Specificator.Execute(DATABASE_KEYWORD, urlInfo.Segments.First());
else
throw new ArgumentOutOfRangeException($"Expecting a single segment in the connectionUrl but found {urlInfo.Segments.Length} segments. The list of segments was '{string.Join("', '", urlInfo.Segments.ToArray())}'");
throw new InvalidConnectionUrlTooManySegmentsException(EXCEPTION_DATABASE_NAME, urlInfo.Segments);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public override void Execute(UrlInfo urlInfo)
public override void Execute(UrlInfo urlInfo)
{
if (string.IsNullOrEmpty(urlInfo.Username) || string.IsNullOrEmpty(urlInfo.Password))
throw new ArgumentOutOfRangeException($"Username and Password are mandatory for QuestDb.");
throw new InvalidConnectionUrlException($"Username and Password are mandatory for QuestDb.");

Specificator.Execute(USERNAME_KEYWORD, urlInfo.Username);
Specificator.Execute(PASSWORD_KEYWORD, urlInfo.Password);
Expand Down
Loading

0 comments on commit ea40a08

Please sign in to comment.