Compare commits

..

1 Commits

Author SHA1 Message Date
Jedd Morgan b5b40c8a5c Update deploy.yml 2025-10-20 16:35:22 +01:00
4 changed files with 101 additions and 111 deletions
+1 -1
View File
@@ -81,7 +81,7 @@ jobs:
run: |
TAG=${{ github.ref_name }}
if [[ "${{ github.ref }}" != refs/tags/* ]]; then
TAG="v3.0.99"
TAG="v3.0.99.${{ github.run_number }}"
fi
SEMVER="${TAG#v}"
FILE_VERSION=$(echo "$TAG" | sed -E 's/^v([0-9]+\.[0-9]+\.[0-9]+).*/\1/')
@@ -1,12 +1,13 @@
(url as text) as list =>
let
// Import required functions
GetModel = Extension.LoadFunction("GetModel.pqm"),
Parser = Extension.LoadFunction("Parser.pqm"),
GetUser = Extension.LoadFunction("GetUser.pqm"),
GetVersion = Extension.LoadFunction("GetVersion.pqm"),
GetWorkspace = Extension.LoadFunction("GetWorkspace.pqm"),
// helper function to load .pqm modules dynamically
// the logic for importing functions from other files
Extension.LoadFunction = (fileName as text) =>
let
binary = Extension.Contents(fileName),
@@ -23,6 +24,7 @@
Detail = [File = fileName, Error = e]
],
// Get required information
modelInfo = GetModel(url),
parsedUrl = Parser(url),
userInfo = GetUser(url),
@@ -31,144 +33,136 @@
connectorVersion = GetVersion(),
workspaceInfo = GetWorkspace(url),
// attempts to exchange powerful token for weak token via desktop service
// returns [Success = true/false, Token = weak_token/null]
TryTokenExchange = () =>
// Function to check if Desktop Service is available
IsDesktopServiceAvailable = () =>
try
let
tokenExchangeData = Json.FromValue([
PowerfulToken = apiKey,
Scopes = {"profile:read", "streams:read", "users:read"},
ProjectId = parsedUrl[projectId],
ServerUrl = parsedUrl[baseUrl]
]),
tokenExchangeResponse = Web.Contents(
"http://127.0.0.1:29364/auth/exchange-token",
PingResponse = Web.Contents(
"http://127.0.0.1:29364/ping",
[
Headers = [
#"Content-Type" = "application/json",
#"Method" = "POST"
],
Content = tokenExchangeData,
Headers = [#"Method" = "GET"],
ManualStatusHandling = {400, 401, 403, 404, 500, 502, 503, 504},
Timeout = #duration(0, 0, 0, 5)
Timeout = #duration(0, 0, 0, 2) // 2 second timeout for ping
]
),
StatusCode = Value.Metadata(tokenExchangeResponse)[Response.Status],
Result = if StatusCode >= 200 and StatusCode < 300 then
let
tokenExchangeJson = Json.Document(tokenExchangeResponse),
weakToken = tokenExchangeJson[token]
in
[Success = true, Token = weakToken]
else
[Success = false, Token = null]
StatusCode = Value.Metadata(PingResponse)[Response.Status]
in
Result
StatusCode = 200
otherwise
[Success = false, Token = null],
false,
// stores user info to desktop service for power bi visual consumption
// returns status code (or 0 on failure)
SendTelemetry = (token as text) =>
try
let
userInfoData = Json.FromValue([
Url = url,
Server = parsedUrl[baseUrl],
Email = userEmail,
ProjectId = parsedUrl[projectId],
RootObjectId = modelInfo[rootObjectId],
SourceApplication = modelInfo[sourceApplication],
Token = token,
Version = connectorVersion,
VersionId = parsedUrl[versionId],
WorkspaceId = workspaceInfo[workspaceId],
WorkspaceName = workspaceInfo[workspaceName],
WorkspaceLogo = workspaceInfo[workspaceLogo],
CanHideBranding = workspaceInfo[canHideBranding]
]),
userInfoResponse = Web.Contents(
"http://127.0.0.1:29364/store-user-info",
[
Headers = [
#"Content-Type" = "application/json",
#"Method" = "POST"
],
Content = userInfoData,
ManualStatusHandling = {400, 401, 403, 404, 500, 502, 503, 504},
Timeout = #duration(0, 0, 0, 3)
]
),
statusCode = Value.Metadata(userInfoResponse)[Response.Status]
in
statusCode
otherwise
0,
// downloads data directly from server without desktop service
DirectDownload = (token as text) =>
// Function to use Desktop Service approach (only called if available)
UseDesktopService = () =>
let
// exchange powerful token for weak token via ds
tokenExchangeData = Json.FromValue([
PowerfulToken = apiKey,
Scopes = {"profile:read", "streams:read", "users:read"},
ProjectId = parsedUrl[projectId],
ServerUrl = parsedUrl[baseUrl]
]),
tokenExchangeResponse = Web.Contents(
"http://127.0.0.1:29364/auth/exchange-token",
[
Headers = [
#"Content-Type" = "application/json",
#"Method" = "POST"
],
Content = tokenExchangeData,
ManualStatusHandling = {400, 401, 403, 404, 500}
]
),
tokenExchangeJson = Json.Document(tokenExchangeResponse),
weakToken = tokenExchangeJson[token],
// prepare request data with weak token
requestData = Json.FromValue([
Url = url,
Server = parsedUrl[baseUrl],
Email = userEmail,
ProjectId = parsedUrl[projectId],
RootObjectId = modelInfo[rootObjectId],
SourceApplication = modelInfo[sourceApplication],
Token = weakToken,
Version = connectorVersion,
VersionId = parsedUrl[versionId],
WorkspaceId = workspaceInfo[workspaceId],
WorkspaceName = workspaceInfo[workspaceName],
WorkspaceLogo = workspaceInfo[workspaceLogo],
CanHideBranding = workspaceInfo[canHideBranding]
]),
// Send request to local server
Response = Web.Contents(
"http://127.0.0.1:29364/download",
[
Headers = [
#"Content-Type" = "application/json",
#"Method" = "POST"
],
Content = requestData,
ManualStatusHandling = {400, 401, 403, 404, 500}
]
),
// Parse response
JsonResponse = Json.Document(Response)
in
JsonResponse,
// Function to fallback to direct JSON download from Speckle server
FallbackToDirectDownload = () =>
let
// Construct the direct object URL: {baseUrl}/objects/{projectId}/{rootObjectId}
objectUrl = Text.Combine({
parsedUrl[baseUrl],
"/objects/",
parsedUrl[projectId],
"/",
parsedUrl[baseUrl],
"/objects/",
parsedUrl[projectId],
"/",
modelInfo[rootObjectId]
}),
// Download JSON directly from Speckle server
Response = Web.Contents(
objectUrl,
[
Headers = [
#"Authorization" = "Bearer " & token,
#"Authorization" = "Bearer " & apiKey,
#"Accept" = "application/json"
],
ManualStatusHandling = {400, 401, 403, 404, 500, 502, 503, 504}
]
),
// Check response status
StatusCode = Value.Metadata(Response)[Response.Status],
// Parse JSON response if successful
JsonResponse = if StatusCode >= 200 and StatusCode < 300 then
Json.Document(Response)
else
error [
Reason = "DirectDownloadFailed",
Message.Format = "Failed to download model data from Speckle server (Status: #{0})",
Message.Parameters = {Text.From(StatusCode)},
Message = "Failed to download model data directly from Speckle server",
Detail = [
StatusCode = StatusCode,
StatusCode = StatusCode,
ObjectUrl = objectUrl,
ProjectId = parsedUrl[projectId],
RootObjectId = modelInfo[rootObjectId],
UsedWeakToken = token <> apiKey
RootObjectId = modelInfo[rootObjectId]
]
]
in
JsonResponse,
// try token exchange, use weak token if successful, otherwise use powerful token
// powerful token just for data connector, never stored in visual
TokenExchangeResult = TryTokenExchange(),
TokenToUse = if TokenExchangeResult[Success] then
TokenExchangeResult[Token]
// Check Desktop Service availability and choose approach
DesktopServiceAvailable = IsDesktopServiceAvailable(),
FinalResult = if DesktopServiceAvailable then
UseDesktopService()
else
apiKey,
// send user info to desktop service
TelemetryStatusCode = SendTelemetry(TokenToUse),
// download data
FinalResult = if TelemetryStatusCode >= 0 then
DirectDownload(TokenToUse)
else
DirectDownload(TokenToUse)
FallbackToDirectDownload()
in
FinalResult
FinalResult
@@ -64,7 +64,7 @@
field is needed for interactivity with other visuals.
</div>
<div v-if="visualStore.isNavbarHidden" class="fixed top-4 right-2 z-20">
<div v-if="visualStore.isNavbarHidden" class="fixed top-0 right-0 z-20">
<button
class="transition opacity-50 hover:opacity-100"
title="Show navbar"
@@ -41,12 +41,8 @@ export function useUpdateConnector() {
return new Date(b.Date).getTime() - new Date(a.Date).getTime()
})
versions.value = sortedVersions
// Filter out prerelease versions
const stableVersions = sortedVersions.filter((v) => !v.Prerelease)
const latestVersion = stableVersions[0]
const sanitizedVersion = sanitizeVersion(latestVersion.Number)
latestAvailableVersion.value = { ...latestVersion, Number: sanitizedVersion }
const sanitizedVersion = sanitizeVersion(sortedVersions[0].Number)
latestAvailableVersion.value = { ...sortedVersions[0], Number: sanitizedVersion }
visualStore.setLatestAvailableVersion(latestAvailableVersion.value)
}