Starting December 3, join live sessions with database experts and the Microsoft product team to learn just how easy it is to get started
Learn moreShape the future of the Fabric Community! Your insights matter. That’s why we created a quick survey to learn about your experience finding answers to technical questions. Take survey.
Hi Folks,
I'm new to Power BI and was hoping I could ask someone over here for some advice. I'm working with the Rest API. And well, I've got most of what I need working within the Power Query when I hardcode an access token (retrieved from PostMan). However, when I try to replicate the PostMan code and retrieve the key within PowerBI it fails.
If the value isn't hardcoded it returns an error about converting a value of type function to type logical. I'm a bit at a lost. Maybe I need to parse/decode the data being passed back? PostMan does this automatically. Any guidance or point of direction would be much appreciated!
The snippet of code in question is the following:
GetPages = () =>
let
Source =
Json.Document(
Web.Contents(
"https://REMOVED/api/Token",
[
Headers =
[
#"Content-Type"="application/x-www-form-urlencoded" //,
],
Content = Text.ToBinary("Username=REMOVED&Password=REMOVED&grant_type=password&Integrated=N&database=REMOVED&Client_Id=REMOVED=&client_secret=REMOVED",BinaryEncoding.Base64,Binary.Compress) ]
)
),
//This fails
apikey = Source[access_token],
//This works (copied and pasted from PostMan
apikey = "INSERT_POSTMAN_CODE",
Solved! Go to Solution.
For those curious this eventually was determined to be a server bug. However, I did clean up the final code. Attached is the working code for anyone that wants it...
(environment as text) as text =>
let
sand_UserID = [REMOVED],
sand_Database = [REMOVED],
sand_Password = Uri.EscapeDataString([REMOVED]),
sand_Client_Secret = Uri.EscapeDataString([REMOVED]),
//EscapeDataString is required for any phrase that includes special characters, although probably doesn't hurt to have on all of them.
prod_UserID = [REMOVED],
prod_Database = [REMOVED],
prod_Password = Uri.EscapeDataString([REMOVED]),
prod_Client_Secret = Uri.EscapeDataString([REMOVED]),
Client_ID = Uri.EscapeDataString([REMOVED]),
UserID = if environment = "sand" then sand_UserID else prod_UserID,
Database = if environment = "sand" then sand_Database else prod_Database,
Password = if environment = "sand" then sand_Password else prod_Password,
Client_Secret = if environment = "sand" then sand_Client_Secret else prod_Client_Secret,
token_url = "https://[REMOVED]/api/",
body="Username="&UserID&"&Password="&Password&"&grant_type=password&Integrated=N&database="&Database&"&Client_Id=" & Client_ID & "&Client_Secret=" & Client_Secret,
params =
[
Content = Text.ToBinary(body),
RelativePath = "Token",
Headers = [#"Content-Type"="application/x-www-form-urlencoded"]
],
WebSource = Json.Document(Web.Contents(token_url,params)),
apikey = WebSource[access_token]
in
apikey
For those curious this eventually was determined to be a server bug. However, I did clean up the final code. Attached is the working code for anyone that wants it...
(environment as text) as text =>
let
sand_UserID = [REMOVED],
sand_Database = [REMOVED],
sand_Password = Uri.EscapeDataString([REMOVED]),
sand_Client_Secret = Uri.EscapeDataString([REMOVED]),
//EscapeDataString is required for any phrase that includes special characters, although probably doesn't hurt to have on all of them.
prod_UserID = [REMOVED],
prod_Database = [REMOVED],
prod_Password = Uri.EscapeDataString([REMOVED]),
prod_Client_Secret = Uri.EscapeDataString([REMOVED]),
Client_ID = Uri.EscapeDataString([REMOVED]),
UserID = if environment = "sand" then sand_UserID else prod_UserID,
Database = if environment = "sand" then sand_Database else prod_Database,
Password = if environment = "sand" then sand_Password else prod_Password,
Client_Secret = if environment = "sand" then sand_Client_Secret else prod_Client_Secret,
token_url = "https://[REMOVED]/api/",
body="Username="&UserID&"&Password="&Password&"&grant_type=password&Integrated=N&database="&Database&"&Client_Id=" & Client_ID & "&Client_Secret=" & Client_Secret,
params =
[
Content = Text.ToBinary(body),
RelativePath = "Token",
Headers = [#"Content-Type"="application/x-www-form-urlencoded"]
],
WebSource = Json.Document(Web.Contents(token_url,params)),
apikey = WebSource[access_token]
in
apikey
The bug is not in access_token, it is in your source variable. When you reference Source it is evaluated, as such when you hard code the access token in Source is never evaluated. The actual bug is in Text.ToBinary where you pass the 3rd parameter as a function (without invoking it), but what it expects is a byte order either ByteOrder.BigEndian or ByteOrder.LittleEndian.
Thank you for the prompt follow-up!
I'm a bit rusty to programming, and not at all familar with Power Query / M. However, I tried your recommendation.
When I invoked:
Content=Text.ToBinary(body,BinaryEncoding.Base64, ByteOrder.BigEndian)
It responded:
Expression.Error: We cannot convert the value 1 to type Logical.
Details:
Value=1
Type=[Type]
Likewise, LittleEndian, responded with "value 0" and, as you noted, Binary.Compress, responds with "Function" in place of the value in the error message. I think we are on the right path, as this change definitely updated the error. However, going this path, I guess I'm a bit thrown by why the error happens so far upstream (although it sounds like the language just ignores "Source" if I don't reference the value after storing it, that's a bit wierd?!?). The next line is to set the session's API to the token returned. If I try just random garbage, the system realizes the credentials have failed and will not pull the API call for the data request. However, if I set the string to the value obtained from PostMan, it works just fine and pulls the data. I guess I'm just surprised, it isn't responding saying bogus credidentials and/or with that 400 error message.
Definitely heading the right direction... any other thoughts or advice? Seriously... been pulling my hair out looking at this one all day... Thank you so much in advance.
I'll add if I hardcode either false or true as that third parameter, then I do get the 400: Bad Request. Again, it all matches with PostMan.
Ok... getting a little warmer...
I'm getting the (400): Bad Request response when I execute the following code. I cleaned it up a bit since my first post. I also, have (for now) put true after Base64, but not sure what whether that is right or wrong. As far as I'm aware this is identical to Post-Man (which works), but I don't know either tool well, so perhaps I'm missing something?!?!?
token_url = "https://DOMAIN/api/Token",
body="Username=VALUE1&Password=VALUE2&grant_type=password&Integrated=N&database=VALUE3&Client_Id=VALUE4=&client_secret=VALUE5",
params =
[
Headers = [#"Content-Type"="application/x-www-form-urlencoded", #"Accept"="*/*", #"Accept-Encoding"="gzip, deflate"],
Content = Text.ToBinary(body,BinaryEncoding.Base64,true)
],
WebSource = Json.Document(Web.Contents(token_url,params)),
apikey = WebSource[access_token]
WIth regards to Power BI, I tried with and without the cookie. Post-Man sends Cookie, Postman-Token, Content-Length, and Host within the Header. If I "uncheck" Host, Post-Man fails (not with 400, just in general), but if I try to override Host in my code, it says I'm not allowed (and to use the appropriate method). I read it might be sending it automatically, or something?!?
curl --location --request POST 'https://DOMAIN/midtowncg/api/token' \
--header 'Cookie: AWSELB=VALUEA; AWSELBCORS=VALUEB' \
--data-urlencode 'Username=VALUE1' \
--data-urlencode 'Password=VALUE2' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'Integrated=N' \
--data-urlencode 'database=VALUE3' \
--data-urlencode 'Client_Id=VALUE4' \
--data-urlencode 'client_secret=VALUE5'
I think the issue is probably your second parameter:
BinaryEncoding.Base64
I think you want a text encoding, not a binary encoding there. Maybe TextEncoding.Ascii is fine? BinaryEncoding is for converting binary to text.
@artemus, thank you for the reply! I tried that and same issue. Honestly, inheirted this code, not truly entirely sure what all should be there. Kind of learning along the way... Although, something about this is jacking up with the request. If I try just removing the conversion all together (and send body), it says it is unable to convert to binary?!?!? Not sure why it is even trying to do so...
How about just removing the 2nd and 3rd parameter. If you want to see what PowerBi is sending you can just create a new query with:
Text.FromBinary(<code after Content=>)
and then put that into your favorate base64 decoder.
As for removing it all, Content expects a binary type, not a text type.
As to what should be there is described here: https://docs.microsoft.com/en-us/powerquery-m/web-contents
Removing the 2nd and 3rd parameters results in the same issue. It says Bad Request.
Is there something about my code verse the Post-Man code that is different? I'm just trying to follow why it would work in PostMan, but not here. PostMan doesn't mention anything about converting it to Binary (although, I trust you when you say it just expects it there).
Might be an issue with the password using a special character.
Try:
body = Uri.BuildQueryString(
[
Username="VALUE1",
Password="VALUE2",
grant_type="password",
Integrated="N",
database="VALUE3",
Client_Id="VALUE4",
client_secret="VALUE5"
])
@artemus, I gave that a try and no luck either. I'd love to know if there was a way I could see what exactly is based along to the system? It is responding with the 400 error now, suggesting we are making a "valid" call, but with "invalid" parameters. If something were wrong (i.e. User) it would error here. But I'm guessing the 400 is a catch-all that occurs before validating the fields like User, etc.
Thank you for the true dedication here! I feel we are so freaken close and it something mindnumbingly stupid that's being overlooked like a period or something. Just losing my mind trying to figure it out...
One thing you mentioned about the "password having a special character", got tme thinking. The Client ID string does end in a "=". I can't change this value, but is there a way I need to feed it into the system perhaps?
Per chance, any more thoughts?
Adding more details...
Perhaps I should clarify this is a OAuth 2.0 connection (not sure if that makes a difference). I also noticed in PostMan that I'm requesting the header back, but I think I overheard by default it requests the URL back?!?!? How do you force it to request header in Power BI?
Your insights matter. That’s why we created a quick survey to learn about your experience finding answers to technical questions.
User | Count |
---|---|
25 | |
13 | |
12 | |
11 | |
8 |
User | Count |
---|---|
44 | |
27 | |
19 | |
16 | |
12 |