feat: move from embed token api to share token api (#230)
* new mutation for token exchange * variable based
This commit is contained in:
@@ -66,7 +66,8 @@
|
||||
powerfulToken,
|
||||
{"profile:read", "streams:read", "users:read"},
|
||||
parsedUrl[projectId],
|
||||
parsedUrl[baseUrl]
|
||||
parsedUrl[baseUrl],
|
||||
parsedUrl[resourceIdString]
|
||||
),
|
||||
|
||||
// throw error if token exchange failed - do NOT use powerful token as fallback
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
// Function to exchange powerful token for weak limited token
|
||||
(powerfulToken as text, scopes as list, projectId as text, serverUrl as text) as record =>
|
||||
(powerfulToken as text, scopes as list, projectId as text, serverUrl as text, optional resourceIdString as text) as record =>
|
||||
let
|
||||
// Import the parser function for URL handling
|
||||
Parser = Extension.LoadFunction("Parser.pqm"),
|
||||
|
||||
// Helper function to load .pqm modules dynamically
|
||||
Extension.LoadFunction = (fileName as text) =>
|
||||
let
|
||||
@@ -33,114 +30,106 @@
|
||||
else
|
||||
null,
|
||||
|
||||
// Token lifetime: 10 years (315,360,000,000 milliseconds)
|
||||
TokenLifespanMs = 10 * 365 * 24 * 3600 * 1000,
|
||||
|
||||
// Generate token name with timestamp
|
||||
TokenName = "Limited Power BI Visual Token - " & DateTime.ToText(DateTime.LocalNow(), "yyyy-MM-dd HH:mm"),
|
||||
|
||||
// Build scopes array string for GraphQL (e.g., ["profile:read", "streams:read"])
|
||||
ScopesArray = Text.Combine(
|
||||
List.Transform(scopes, each """" & _ & """"),
|
||||
", "
|
||||
),
|
||||
|
||||
// Build GraphQL mutation
|
||||
GraphQLMutation = "
|
||||
mutation {
|
||||
apiTokenCreate(token: {
|
||||
name: """ & TokenName & """,
|
||||
scopes: [" & ScopesArray & "],
|
||||
lifespan: " & Number.ToText(TokenLifespanMs) & ",
|
||||
limitResources: [{
|
||||
type: project,
|
||||
id: """ & projectId & """
|
||||
}]
|
||||
})
|
||||
}",
|
||||
|
||||
// Execute token exchange if validation passes
|
||||
Result = if ValidationError <> null then
|
||||
[
|
||||
Success = false,
|
||||
Token = null,
|
||||
ErrorMessage = ValidationError
|
||||
]
|
||||
// Ensure serverUrl ends with /
|
||||
NormalizedServerUrl = if Text.End(serverUrl, 1) = "/" then
|
||||
serverUrl
|
||||
else
|
||||
try
|
||||
let
|
||||
// Ensure serverUrl ends with /
|
||||
NormalizedServerUrl = if Text.End(serverUrl, 1) = "/" then
|
||||
serverUrl
|
||||
else
|
||||
serverUrl & "/",
|
||||
serverUrl & "/",
|
||||
|
||||
// Make GraphQL request
|
||||
Response = Web.Contents(
|
||||
NormalizedServerUrl & "graphql",
|
||||
[
|
||||
Headers = [
|
||||
#"Method" = "POST",
|
||||
#"Content-Type" = "application/json",
|
||||
#"Authorization" = "Bearer " & powerfulToken
|
||||
],
|
||||
Content = Json.FromValue([
|
||||
query = GraphQLMutation
|
||||
]),
|
||||
ManualStatusHandling = {400, 401, 403, 404, 500, 502, 503, 504},
|
||||
Timeout = #duration(0, 0, 0, 10)
|
||||
]
|
||||
),
|
||||
// New Share Token API mutation with variables
|
||||
NewGraphQLQuery = "mutation CreateEmbedShareToken($input: CreateEmbedShareTokenInput!) {
|
||||
sharingMutations {
|
||||
createEmbedShareToken(input: $input) {
|
||||
token
|
||||
}
|
||||
}
|
||||
}",
|
||||
NewGraphQLVariables = [
|
||||
input = [
|
||||
projectId = projectId,
|
||||
resourceIdString = resourceIdString
|
||||
]
|
||||
],
|
||||
|
||||
StatusCode = Value.Metadata(Response)[Response.Status],
|
||||
// Legacy apiTokenCreate mutation with variables
|
||||
TokenLifespanMs = 10 * 365 * 24 * 3600 * 1000,
|
||||
TokenName = "Limited Power BI Visual Token - " & DateTime.ToText(DateTime.LocalNow(), "yyyy-MM-dd HH:mm"),
|
||||
LegacyGraphQLQuery = "mutation CreateApiToken($token: ApiTokenCreateInput!) {
|
||||
apiTokenCreate(token: $token)
|
||||
}",
|
||||
LegacyGraphQLVariables = [
|
||||
token = [
|
||||
name = TokenName,
|
||||
scopes = scopes,
|
||||
lifespan = TokenLifespanMs,
|
||||
limitResources = {[
|
||||
type = "project",
|
||||
id = projectId
|
||||
]}
|
||||
]
|
||||
],
|
||||
|
||||
// Parse response if successful
|
||||
ParsedResult = if StatusCode >= 200 and StatusCode < 300 then
|
||||
let
|
||||
JsonResponse = Json.Document(Response),
|
||||
// Helper: execute a GraphQL query with variables and extract token
|
||||
ExecuteGraphQL = (query as text, variables as record, extractToken as function) =>
|
||||
let
|
||||
Response = Web.Contents(
|
||||
NormalizedServerUrl & "graphql",
|
||||
[
|
||||
Headers = [
|
||||
#"Method" = "POST",
|
||||
#"Content-Type" = "application/json",
|
||||
#"Authorization" = "Bearer " & powerfulToken
|
||||
],
|
||||
Content = Json.FromValue([
|
||||
query = query,
|
||||
variables = variables
|
||||
]),
|
||||
ManualStatusHandling = {400, 401, 403, 404, 500, 502, 503, 504},
|
||||
Timeout = #duration(0, 0, 0, 10)
|
||||
]
|
||||
),
|
||||
StatusCode = Value.Metadata(Response)[Response.Status],
|
||||
JsonResponse = if StatusCode >= 200 and StatusCode < 300 then
|
||||
Json.Document(Response)
|
||||
else
|
||||
null,
|
||||
HasErrors = JsonResponse <> null and Record.HasFields(JsonResponse, {"errors"}),
|
||||
Token = if JsonResponse <> null and not HasErrors then
|
||||
try extractToken(JsonResponse) otherwise null
|
||||
else
|
||||
null,
|
||||
ErrorMsg = if HasErrors then
|
||||
try JsonResponse[errors]{0}[message] otherwise "GraphQL mutation failed"
|
||||
else if JsonResponse = null then
|
||||
"Request failed with status " & Number.ToText(StatusCode)
|
||||
else
|
||||
null
|
||||
in
|
||||
[Success = Token <> null, Token = Token, ErrorMessage = ErrorMsg],
|
||||
|
||||
// Check for GraphQL errors
|
||||
HasErrors = Record.HasFields(JsonResponse, {"errors"}),
|
||||
// Try new API first, fall back to legacy
|
||||
Result = if ValidationError <> null then
|
||||
[Success = false, Token = null, ErrorMessage = ValidationError]
|
||||
else
|
||||
let
|
||||
newResult = if resourceIdString <> null then
|
||||
try ExecuteGraphQL(
|
||||
NewGraphQLQuery,
|
||||
NewGraphQLVariables,
|
||||
each [data][sharingMutations][createEmbedShareToken][token]
|
||||
) otherwise [Success = false, Token = null, ErrorMessage = "New API request failed"]
|
||||
else
|
||||
[Success = false, Token = null, ErrorMessage = null],
|
||||
|
||||
// Extract token from response
|
||||
WeakToken = if not HasErrors then
|
||||
JsonResponse[data][apiTokenCreate]
|
||||
else
|
||||
null,
|
||||
|
||||
ErrorMsg = if HasErrors then
|
||||
try
|
||||
JsonResponse[errors]{0}[message]
|
||||
otherwise
|
||||
"GraphQL mutation failed with unknown error"
|
||||
else
|
||||
null
|
||||
in
|
||||
if WeakToken <> null then
|
||||
[
|
||||
Success = true,
|
||||
Token = WeakToken,
|
||||
ErrorMessage = null
|
||||
]
|
||||
else
|
||||
[
|
||||
Success = false,
|
||||
Token = null,
|
||||
ErrorMessage = ErrorMsg
|
||||
]
|
||||
else
|
||||
[
|
||||
Success = false,
|
||||
Token = null,
|
||||
ErrorMessage = "GraphQL request failed with status " & Number.ToText(StatusCode)
|
||||
]
|
||||
in
|
||||
ParsedResult
|
||||
otherwise
|
||||
[
|
||||
Success = false,
|
||||
Token = null,
|
||||
ErrorMessage = "Token exchange request failed with exception"
|
||||
]
|
||||
finalResult = if newResult[Success] then
|
||||
newResult
|
||||
else
|
||||
try ExecuteGraphQL(
|
||||
LegacyGraphQLQuery,
|
||||
LegacyGraphQLVariables,
|
||||
each [data][apiTokenCreate]
|
||||
) otherwise [Success = false, Token = null, ErrorMessage = "Token exchange request failed"]
|
||||
in
|
||||
finalResult
|
||||
in
|
||||
Result
|
||||
|
||||
@@ -54,5 +54,6 @@
|
||||
modelId = if isFederated then null else processedModels{0}[modelId],
|
||||
versionId = if isFederated then null else processedModels{0}[versionId],
|
||||
isFederated = isFederated,
|
||||
federatedModels = if isFederated then processedModels else null
|
||||
federatedModels = if isFederated then processedModels else null,
|
||||
resourceIdString = rawModelSegment
|
||||
]
|
||||
|
||||
@@ -33,7 +33,8 @@
|
||||
powerfulToken,
|
||||
{"profile:read", "streams:read", "users:read"},
|
||||
parsedUrl[projectId],
|
||||
parsedUrl[baseUrl]
|
||||
parsedUrl[baseUrl],
|
||||
parsedUrl[resourceIdString]
|
||||
),
|
||||
|
||||
// throw error if token exchange failed - do NOT use powerful token as fallback
|
||||
|
||||
Reference in New Issue
Block a user