-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: connection to Analysis Services Azure (#810)
* feat: connection to Analysis Services Azure * docs: update automatically generated documentation related to schemes --------- Co-authored-by: AppVeyor bot <[email protected]>
- Loading branch information
Showing
8 changed files
with
311 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
using DubUrl.Parsing; | ||
using NUnit.Framework; | ||
using System.Data.Common; | ||
using DubUrl.Testing.Rewriting; | ||
using DubUrl.Adomd.Wrappers; | ||
using DubUrl.Adomd.Rewriting; | ||
using DubUrl.Rewriting; | ||
|
||
namespace DubUrl.Adomd.Testing.Rewriting.Implementation; | ||
|
||
public class AsAzureRewriterTest | ||
{ | ||
private const string PROVIDER_NAME = "Microsoft.AnalysisServices.AdomdClient"; | ||
|
||
private static DbConnectionStringBuilder ConnectionStringBuilder | ||
=> ConnectionStringBuilderHelper.Retrieve(PROVIDER_NAME, AdomdFactory.Instance); | ||
|
||
[Test] | ||
[TestCase("westus/server", "asazure://westus.asazure.windows.net/server")] | ||
[TestCase("westus.asazure.windows.net/server", "asazure://westus.asazure.windows.net/server")] | ||
[TestCase("friendlyname.salesapp.azurewebsites.net", "link://friendlyname.salesapp.azurewebsites.net/")] | ||
public void Map_UrlInfo_DataSource(string input, string expected) | ||
{ | ||
var urlInfo = new UrlInfo() { Host = input.Split('/')[0], Segments = input.Split('/').Skip(1).ToArray() }; | ||
var Rewriter = new AsAzureRewriter(ConnectionStringBuilder); | ||
var result = Rewriter.Execute(urlInfo); | ||
|
||
Assert.That(result, Is.Not.Null); | ||
Assert.That(result, Does.ContainKey(SsasRewriter.SERVER_KEYWORD)); | ||
Assert.That(result[SsasRewriter.SERVER_KEYWORD], Is.EqualTo(expected)); | ||
} | ||
|
||
[Test] | ||
[TestCase("westus")] | ||
[TestCase("westus.asazure.windows.net")] | ||
public void Map_InvalidUrlInfo_Throws(string input) | ||
=> Assert.That(() => | ||
new AsAzureRewriter(ConnectionStringBuilder) | ||
.Execute(new UrlInfo() { Host = input.Split('/')[0], Segments = input.Split('/').Skip(1).ToArray() }) | ||
, Throws.TypeOf<InvalidConnectionUrlException>()); | ||
|
||
[Test] | ||
[TestCase("westus/server/cube")] | ||
[TestCase("westus.asazure.windows.net/server/cube")] | ||
[TestCase("friendlyname.salesapp.azurewebsites.net/cube")] | ||
public void Map_UrlInfo_Cube(string input, string expected = "cube") | ||
{ | ||
var urlInfo = new UrlInfo() { Host = input.Split('/')[0], Segments = input.Split('/').Skip(1).ToArray() }; | ||
var Rewriter = new AsAzureRewriter(ConnectionStringBuilder); | ||
var result = Rewriter.Execute(urlInfo); | ||
|
||
Assert.That(result, Is.Not.Null); | ||
Assert.That(result, Does.ContainKey(SsasRewriter.CUBE_KEYWORD)); | ||
Assert.That(result[SsasRewriter.CUBE_KEYWORD], Is.EqualTo(expected)); | ||
} | ||
|
||
[Test] | ||
[TestCase("westus/server/cube/any")] | ||
[TestCase("westus.asazure.windows.net/server/cube/any")] | ||
[TestCase("friendlyname.salesapp.azurewebsites.net/cube/any")] | ||
public void Map_InvalidSegments_Throws(string input) | ||
=> Assert.That(() => | ||
new AsAzureRewriter(ConnectionStringBuilder) | ||
.Execute(new UrlInfo() { Host = input.Split('/')[0], Segments = input.Split('/').Skip(1).ToArray() }) | ||
, Throws.TypeOf<InvalidConnectionUrlException>()); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
using DubUrl.Parsing; | ||
using NUnit.Framework; | ||
using System.Data.Common; | ||
using DubUrl.Testing.Rewriting; | ||
using DubUrl.Adomd.Wrappers; | ||
using DubUrl.Adomd.Rewriting; | ||
using DubUrl.Rewriting; | ||
|
||
namespace DubUrl.Adomd.Testing.Rewriting.Implementation; | ||
|
||
public class SsasTabularRewriterTest | ||
{ | ||
private const string PROVIDER_NAME = "Microsoft.AnalysisServices.AdomdClient"; | ||
|
||
private static DbConnectionStringBuilder ConnectionStringBuilder | ||
=> ConnectionStringBuilderHelper.Retrieve(PROVIDER_NAME, AdomdFactory.Instance); | ||
|
||
[Test] | ||
[TestCase("localhost/db", "localhost")] | ||
[TestCase("localhost/~/db", "localhost")] | ||
[TestCase("localhost/~/db", "localhost")] | ||
[TestCase("localhost/~/db/cube", "localhost")] | ||
[TestCase("localhost:1234/~/db", "localhost:1234")] | ||
[TestCase("localhost:1234/~/db", "localhost:1234")] | ||
[TestCase("localhost:1234/~/db/cube", "localhost:1234")] | ||
[TestCase("localhost/instance/db", "localhost\\instance")] | ||
[TestCase("localhost/instance/db", "localhost\\instance")] | ||
[TestCase("localhost/instance/db/cube", "localhost\\instance")] | ||
public void Map_UrlInfo_DataSource(string input, string expected) | ||
{ | ||
var urlInfo = new UrlInfo() { Host = input.Split('/')[0], Segments = input.Split('/').Skip(1).ToArray() }; | ||
var Rewriter = new SsasTabularRewriter(ConnectionStringBuilder); | ||
var result = Rewriter.Execute(urlInfo); | ||
|
||
Assert.That(result, Is.Not.Null); | ||
Assert.That(result, Does.ContainKey(SsasRewriter.SERVER_KEYWORD)); | ||
Assert.That(result[SsasRewriter.SERVER_KEYWORD], Is.EqualTo(expected)); | ||
} | ||
|
||
[Test] | ||
[TestCase("localhost")] | ||
[TestCase("localhost:1234")] | ||
public void Map_InvalidUrlInfo_Throws(string input) | ||
=> Assert.That(() => | ||
new SsasTabularRewriter(ConnectionStringBuilder) | ||
.Execute(new UrlInfo() { Host = input.Split('/')[0], Segments = input.Split('/').Skip(1).ToArray() }) | ||
, Throws.TypeOf<InvalidConnectionUrlException>()); | ||
|
||
[Test] | ||
[TestCase("localhost/db")] | ||
[TestCase("localhost/~/db")] | ||
[TestCase("localhost/~/db/cube")] | ||
[TestCase("localhost:1234/~/db")] | ||
[TestCase("localhost:1234/~/db/cube")] | ||
[TestCase("localhost/instance/db")] | ||
[TestCase("localhost/instance/db/cube")] | ||
public void Map_UrlInfo_InitialCatalog(string input, string expected = "db") | ||
{ | ||
var urlInfo = new UrlInfo() { Host = input.Split('/')[0], Segments = input.Split('/').Skip(1).ToArray() }; | ||
var Rewriter = new SsasTabularRewriter(ConnectionStringBuilder); | ||
var result = Rewriter.Execute(urlInfo); | ||
|
||
Assert.That(result, Is.Not.Null); | ||
Assert.That(result, Does.ContainKey(SsasRewriter.DATABASE_KEYWORD)); | ||
Assert.That(result[SsasRewriter.DATABASE_KEYWORD], Is.EqualTo(expected)); | ||
} | ||
|
||
[Test] | ||
[TestCase("localhost/~")] | ||
public void Map_InvalidUrlInfo_throws(string input) | ||
=> Assert.That(() => | ||
new SsasTabularRewriter(ConnectionStringBuilder) | ||
.Execute(new UrlInfo() { Host = input.Split('/')[0], Segments = input.Split('/').Skip(1).ToArray() }) | ||
, Throws.TypeOf<InvalidConnectionUrlException>()); | ||
|
||
[Test] | ||
[TestCase("localhost/~/db/cube")] | ||
[TestCase("localhost:1234/~/db/cube")] | ||
[TestCase("localhost/instance/db/cube")] | ||
[TestCase("localhost:1234/instance/db/cube")] | ||
public void Map_UrlInfo_Cube(string input, string expected = "cube") | ||
{ | ||
var urlInfo = new UrlInfo() { Host = input.Split('/')[0], Segments = input.Split('/').Skip(1).ToArray() }; | ||
var Rewriter = new SsasTabularRewriter(ConnectionStringBuilder); | ||
var result = Rewriter.Execute(urlInfo); | ||
|
||
Assert.That(result, Is.Not.Null); | ||
Assert.That(result, Does.ContainKey(SsasRewriter.CUBE_KEYWORD)); | ||
Assert.That(result[SsasRewriter.CUBE_KEYWORD], Is.EqualTo(expected)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
using DubUrl.Adomd.Querying.Dax; | ||
using DubUrl.Mapping; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Data.Common; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace DubUrl.Adomd.Mapping; | ||
|
||
[Database<StandardDaxDialect>( | ||
"Azure Analysis Services" | ||
, ["asazure", "asa"] | ||
, DatabaseCategory.Analytics | ||
)] | ||
[Brand("microsoftazure", "#0078D4", "#FFFFFF")] | ||
public class AsAzureDatabase : IDatabase | ||
{ } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
using DubUrl.Adomd.Rewriting; | ||
using DubUrl.Mapping; | ||
using DubUrl.Querying.Dialects; | ||
using DubUrl.Querying.Parametrizing; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Data.Common; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace DubUrl.Adomd.Mapping; | ||
|
||
[Mapper<AsAzureDatabase, NamedParametrizer>( | ||
"Microsoft.AnalysisServices.AdomdClient" | ||
)] | ||
public class AsAzureMapper : PowerBiPremiumMapper | ||
{ | ||
public AsAzureMapper(DbConnectionStringBuilder csb, IDialect dialect, IParametrizer parametrizer) | ||
: base(new SsasTabularRewriter(csb), | ||
dialect, | ||
parametrizer | ||
) | ||
{ } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
using DubUrl.Parsing; | ||
using DubUrl.Rewriting; | ||
using DubUrl.Rewriting.Tokening; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Data.Common; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using System.Xml.Schema; | ||
|
||
namespace DubUrl.Adomd.Rewriting; | ||
|
||
internal class AsAzureRewriter : ConnectionStringRewriter | ||
{ | ||
protected internal const string SERVER_KEYWORD = "Data Source"; | ||
protected internal const string CUBE_KEYWORD = "Cube"; | ||
protected internal const string ASAZURE_SCHEME = "asazure"; | ||
protected internal const string LINK_SCHEME = "link"; | ||
protected internal const string DEFAULT_ASAZURE_HOST = "asazure.windows.net"; | ||
|
||
public AsAzureRewriter(DbConnectionStringBuilder csb) | ||
: base(new UniqueAssignmentSpecificator(csb), | ||
[ | ||
new DataSourceMapper(), | ||
new CubeMapper() | ||
] | ||
) | ||
{ } | ||
|
||
internal class DataSourceMapper : BaseTokenMapper | ||
{ | ||
public override void Execute(UrlInfo urlInfo) | ||
{ | ||
var fullHost = new StringBuilder(); | ||
var segments = urlInfo.Segments.ToList(); | ||
|
||
var isAsAzure = IsAsAzure(urlInfo); | ||
|
||
fullHost.Append(isAsAzure ? ASAZURE_SCHEME : LINK_SCHEME) | ||
.Append(Uri.SchemeDelimiter) | ||
.Append(urlInfo.Host); | ||
|
||
if (isAsAzure) | ||
{ | ||
if (urlInfo.Host.Split('.').Length == 1) | ||
fullHost.Append('.').Append(DEFAULT_ASAZURE_HOST); | ||
|
||
if (urlInfo.Segments.Length == 0) | ||
throw new InvalidConnectionUrlException($"Missing, at least, the name of the instance for a Azure Analysis Services."); | ||
|
||
fullHost.Append('/').Append(Encode(segments.First())); | ||
} | ||
else | ||
{ | ||
if (!urlInfo.Host.EndsWith('/')) | ||
fullHost.Append('/'); | ||
} | ||
|
||
Specificator.Execute(SERVER_KEYWORD, fullHost.ToString()); | ||
} | ||
|
||
protected virtual bool IsAsAzure(UrlInfo urlInfo) | ||
=> urlInfo.Host.Split('.').Length == 1 | ||
|| urlInfo.Host.EndsWith(DEFAULT_ASAZURE_HOST, StringComparison.InvariantCultureIgnoreCase); | ||
|
||
protected virtual string Encode(string value) | ||
=> Uri.UnescapeDataString(value).Length < value.Length | ||
? value | ||
: Uri.EscapeDataString(value); | ||
} | ||
|
||
|
||
protected internal class CubeMapper : DataSourceMapper | ||
{ | ||
public override void Execute(UrlInfo urlInfo) | ||
{ | ||
var isAsAzure = IsAsAzure(urlInfo); | ||
if (isAsAzure) | ||
{ | ||
if (urlInfo.Segments.Length > 2) | ||
throw new InvalidConnectionUrlException($"A connection-url to Azure Analysis Services must contain at least one segment and maximum two. Current value contains {urlInfo.Segments.Length} segments: '{string.Join(", ", urlInfo.Segments)}'"); | ||
if (urlInfo.Segments.Length == 2) | ||
Specificator.Execute(CUBE_KEYWORD, urlInfo.Segments.Last()); | ||
} | ||
else | ||
{ | ||
if (urlInfo.Segments.Length > 1) | ||
throw new InvalidConnectionUrlException($"A connection-url to Azure Analysis Services using server name alias must contain zero to one segment. The segment represents the cube. Current value contains {urlInfo.Segments.Length} segments: '{string.Join(", ", urlInfo.Segments)}'"); | ||
else if (urlInfo.Segments.Length == 1) | ||
Specificator.Execute(CUBE_KEYWORD, urlInfo.Segments.Last()); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.