Forum Discussion
On-Premises Data Gateway sources Custom Connector
Hi ,
I'm encountering an issue with date source setting with a Custom Connector. I find my Custom Connector in the List, but when I create the data source, it shows below error.
I'm sure that the username and password is correct.
Can you help me please?
Thanks!
3 Replies
- vanessafvg
Community Champion
has this worked before or is it a new custom connector? who is the author of the connector?
- bgobbiFrequent Visitor
Hi
it's a new custom connector. The connector work correctly in Power Bi Desktop. The author are our software house but we have the source of the connector.- bgobbiFrequent Visitor
Hi
I think the problem is double "url". In the errot i see {"url":"{\"url\":\"https:\\/\\/contacts-api.finstral.cloud\\/api\\/Reports\\/LeadOverview\"}"}. Why? What is the error in connector?
// This file contains your Data Connector logic
section ContactsConnector;
// OuraCloud OAuth2 values
client_id = "xxxxxxxxxxxx"; //Text.FromBinary(Extension.Contents("client_id.txt"));
client_secret = "xxxxxxxxxxx"; // Text.FromBinary(Extension.Contents("client_secret.txt"));
redirect_uri = "https://oauth.powerbi.com/views/oauthredirect.html";
token_uri = "https://login.finstral.cloud/connect/token";
authorize_uri = "xxxxxxxxxxxxxx";
logout_uri = "https://login.microsoftonline.com/logout.srf";// Login modal window dimensions
windowWidth = 720;
windowHeight = 1024;// OAuth2 scope
scope_prefix = "";
scopes = {
"openid",
"fin_contacts",
"offline_access"
};[DataSource.Kind="ContactsConnector", Publish="ContactsConnector.Publish"]
shared ContactsConnector.Contents = (url as text) =>
let
// source = Excel.Workbook(Web.Contents(url, [Query = [ v = Number.ToText(Number.Random()) ], Timeout = #duration(0,1,0,0) ]), true)
source = Excel.Workbook(Web.Contents(url, [Timeout = #duration(0,1,0,0)]), true)
in
source;// Data Source Kind description
ContactsConnector = [
TestConnection = (dataSourcePath) => { "ContactsConnector.Contents", dataSourcePath },
Authentication = [
OAuth = [
StartLogin=StartLogin,
FinishLogin=FinishLogin,
Refresh=Refresh,
Logout=Logout
]
],
Label = Extension.LoadString("DataSourceLabel")
];// Data Source UI publishing description
ContactsConnector.Publish = [
Beta = true,
Category = "Other",
ButtonText = { Extension.LoadString("ButtonTitle"), Extension.LoadString("ButtonHelp") },
LearnMoreUrl = "https://powerbi.microsoft.com/",
SourceImage = ContactsConnector.Icons,
SourceTypeImage = ContactsConnector.Icons
];ContactsConnector.Icons = [
Icon16 = { Extension.Contents("ContactsConnector16.png"), Extension.Contents("ContactsConnector20.png"), Extension.Contents("ContactsConnector24.png"), Extension.Contents("ContactsConnector32.png") },
Icon32 = { Extension.Contents("ContactsConnector32.png"), Extension.Contents("ContactsConnector40.png"), Extension.Contents("ContactsConnector48.png"), Extension.Contents("ContactsConnector64.png") }
];Base64UrlEncodeWithoutPadding = (hash as binary) as text =>
let
base64Encoded = Binary.ToText(hash, BinaryEncoding.Base64),
base64UrlEncoded = Text.Replace(Text.Replace(base64Encoded, "+", "-"), "/", "_"),
withoutPadding = Text.TrimEnd(base64UrlEncoded, "=")
in
withoutPadding;// Helper functions for OAuth2: StartLogin, FinishLogin, Refresh, Logout
StartLogin = (resourceUrl, state, display) =>
let
codeVerifier = Text.NewGuid() & Text.NewGuid(),
codeChallenge = Base64UrlEncodeWithoutPadding(Crypto.CreateHash(CryptoAlgorithm.SHA256, Text.ToBinary(codeVerifier, TextEncoding.Ascii))),
authorizeUrl = authorize_uri & "?" & Uri.BuildQueryString([
response_type = "code",
client_id = client_id,
code_challenge = codeChallenge,
code_challenge_method = "S256",
redirect_uri = redirect_uri,
state = state,
scope = GetScopeString(scopes, scope_prefix)
])
in
[
LoginUri = authorizeUrl,
CallbackUri = redirect_uri,
WindowHeight = 720,
WindowWidth = 1024,
Context = [ CodeVerifier = codeVerifier ]
];FinishLogin = (context, callbackUri, state) =>
let
// parse the full callbackUri, and extract the Query string
parts = Uri.Parts(callbackUri)[Query],
codeVerifier = Record.FieldOrDefault(context, "CodeVerifier", null),
// if the query string contains an "error" field, raise an error
// otherwise call TokenMethod to exchange our code for an access_token
result = if (Record.HasFields(parts, {"error", "error_description"})) then
error Error.Record(parts[error], parts[error_description], parts)
else
TokenMethod("authorization_code", "code", parts[code], codeVerifier)
in
result;Refresh = (resourceUrl, refresh_token) => TokenMethod("refresh_token", "refresh_token", refresh_token, null);
Logout = (token) => logout_uri;
// see "Exchange code for access token: POST /oauth/token" at https://cloud.ouraring.com/docs/authentication for details
TokenMethod = (grantType, tokenField, code, codeVerifier) =>
let
queryString = [
grant_type = grantType,
redirect_uri = redirect_uri,
client_id = client_id,
client_secret = client_secret
],
queryWithVerifier = if (codeVerifier <> null) then Record.AddField(queryString, "code_verifier", codeVerifier) else queryString,
queryWithCode = Record.AddField(queryWithVerifier, tokenField, code),tokenResponse = Web.Contents(token_uri, [
Content = Text.ToBinary(Uri.BuildQueryString(queryWithCode)),
Headers = [
#"Content-type" = "application/x-www-form-urlencoded",
#"Accept" = "application/json"
],
ManualStatusHandling = {400}
]),
body = Json.Document(tokenResponse),
result = if (Record.HasFields(body, {"error", "error_description"})) then
error Error.Record(body[error], body[error_description], body)
else
body
in
result;Value.IfNull = (a, b) => if a <> null then a else b;
GetScopeString = (scopes as list, optional scopePrefix as text) as text =>
let
prefix = Value.IfNull(scopePrefix, ""),
addPrefix = List.Transform(scopes, each prefix & _),
asText = Text.Combine(addPrefix, " "),
finalText = Text.Combine({"offline_access", asText}, " ")
in
finalText;
shared ContactsConnector.ToNavigationTable = (
table as table,
keyColumns as list,
nameColumn as text,
dataColumn as text,
itemKindColumn as text,
itemNameColumn as text,
isLeafColumn as text
) as table =>
let
tableType = Value.Type(table),
newTableType = Type.AddTableKey(tableType, keyColumns, true) meta
[
NavigationTable.NameColumn = nameColumn,
NavigationTable.DataColumn = dataColumn,
NavigationTable.ItemKindColumn = itemKindColumn,
Preview.DelayColumn = itemNameColumn,
NavigationTable.IsLeafColumn = isLeafColumn
],
navigationTable = Value.ReplaceType(table, newTableType)
in
navigationTable;