Compare commits
83 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 85b9e88fc1 | |||
| 92fc894d4b | |||
| b033cfa82b | |||
| 86cba3ce2b | |||
| c752487f9f | |||
| 95f51b0d32 | |||
| 9a5c1283ce | |||
| da1f0c7b7e | |||
| 0bb72f0d9d | |||
| 19e64fd4b2 | |||
| 63f286cd0b | |||
| e89ccbbe1a | |||
| f77e315623 | |||
| 9db161b3f2 | |||
| 83ec9a7888 | |||
| 6128b4293f | |||
| 6f68ec9c29 | |||
| 10c1009430 | |||
| c635995bf6 | |||
| 1198450ea4 | |||
| 3d4545be64 | |||
| 363c389928 | |||
| addc3f9d86 | |||
| 44fc99ecb7 | |||
| f52d6c46cb | |||
| f558d5dce3 | |||
| f9093a34e5 | |||
| 23cb40c4ab | |||
| 9bd6140065 | |||
| a14bb06d4e | |||
| 01576e77df | |||
| f1e16e9d55 | |||
| 4f210dae04 | |||
| 8669cf59f8 | |||
| 6e6402cb38 | |||
| 7b62630fd1 | |||
| eee4f0f8c2 | |||
| 23fa79bdd9 | |||
| edc9917a20 | |||
| 99cffeba8c | |||
| 7e92e7a2aa | |||
| 15aa627d7f | |||
| 8844f1837e | |||
| 6d1b85a14f | |||
| b2d6568b66 | |||
| 72f455393b | |||
| bd3fe6a47c | |||
| e7710f0f8e | |||
| e9034027eb | |||
| 685384b147 | |||
| 5973aaea10 | |||
| 639287f69c | |||
| a43decd473 | |||
| a532061879 | |||
| 15ac7c041e | |||
| 770d45f981 | |||
| 50b59a88c3 | |||
| 0bad9914c8 | |||
| aee00f3f21 | |||
| 2abdd4b645 | |||
| 71ef1acd8b | |||
| 23114a2912 | |||
| ee989f927d | |||
| 1ff15c7b44 | |||
| 6e9fb590a0 | |||
| 9700865934 | |||
| cf9e5a70b6 | |||
| 50c360ae79 | |||
| b671945fa7 | |||
| eb4787ddfa | |||
| 3ffd6c63e0 | |||
| a5a8316ee0 | |||
| 17083c9474 | |||
| 3e6f9988d6 | |||
| fe1504d1a9 | |||
| c753e13859 | |||
| fc2ffa75ba | |||
| de43e34c11 | |||
| 70eb1059d2 | |||
| 9e5c204c8f | |||
| e0f2782c8b | |||
| baf2132d1b | |||
| eef502f42f |
@@ -0,0 +1,73 @@
|
||||
# Use the latest 2.1 version of CircleCI pipeline process engine.
|
||||
# See: https://circleci.com/docs/2.0/configuration-reference
|
||||
version: 2.1
|
||||
|
||||
orbs:
|
||||
win: circleci/windows@5.0
|
||||
|
||||
jobs:
|
||||
build-connector:
|
||||
executor:
|
||||
name: win/default
|
||||
shell: powershell.exe
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: "Set connector internal version"
|
||||
command: |
|
||||
$env:VERSION = if([string]::IsNullOrEmpty($env:CIRCLE_TAG)) { "2.0.0.$($env:WORKFLOW_NUM)" } else { $env:CIRCLE_TAG }
|
||||
(Get-Content ./Speckle.pq).replace('[Version = "2.0.0"]', '[Version = "'+$($env:VERSION)+'"]') | Set-Content ./Speckle.pq
|
||||
- run:
|
||||
name: "Build Data Connector"
|
||||
command: "msbuild Speckle.proj /restore /consoleloggerparameters:NoSummary /property:GenerateFullPaths=true"
|
||||
# - run:
|
||||
# name: Create Innosetup signing cert
|
||||
# command: |
|
||||
# echo $env:PFX_B64 > "tools\AEC Systems Ltd.txt"
|
||||
# certutil -decode "tools\AEC Systems Ltd.txt" "tools\AEC Systems Ltd.pfx"
|
||||
# - run:
|
||||
# name: Create Signed PFX file
|
||||
# command: .\tools\MakePQX\MakePQX.exe pack -mz bin/Speckle.mez -t bin/Speckle.pqx -c "tools\AEC Systems Ltd.pfx" -p $env:PFX_PSW
|
||||
# - run:
|
||||
# name: Build Installer
|
||||
# command: tools\InnoSetup\ISCC.exe tools\powerbi.iss /Sbyparam=$p
|
||||
# shell: cmd.exe #does not work in powershell
|
||||
- store_artifacts:
|
||||
path: ./bin
|
||||
- persist_to_workspace:
|
||||
root: ./
|
||||
paths:
|
||||
- bin/*
|
||||
deploy-connector:
|
||||
docker:
|
||||
- image: cibuilds/github:0.13
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ./
|
||||
- run:
|
||||
name: "Publish Release on GitHub"
|
||||
command: |
|
||||
ghr -t ${GH_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} ${CIRCLE_TAG} ./bin/Speckle.mez
|
||||
workflows:
|
||||
build:
|
||||
jobs:
|
||||
- build-connector:
|
||||
context: innosetup
|
||||
deploy:
|
||||
jobs:
|
||||
- build-connector:
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/ # For testing only: /ci\/.*/
|
||||
tags:
|
||||
only: /^([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w{1,10})?$/
|
||||
context: innosetup
|
||||
- deploy-connector:
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/ # For testing only: /ci\/.*/
|
||||
tags:
|
||||
only: /^([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w{1,10})?$/
|
||||
requires:
|
||||
- build-connector
|
||||
context: github-dev-bot
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"ms-dotnettools.csharp",
|
||||
"powerquery.vscode-powerquery-sdk"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"type": "powerquery",
|
||||
"request": "launch",
|
||||
"name": "Test powerquery file",
|
||||
"program": "${workspaceFolder}/${command:AskForPowerQueryFileName}",
|
||||
"additionalArgs": ["--logMashupEngineTraces", "user"],
|
||||
"preLaunchTask": "build"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"powerquery.general.mode": "SDK",
|
||||
"powerquery.sdk.defaultExtension": "${workspaceFolder}\\bin\\Speckle.mez",
|
||||
"powerquery.sdk.defaultQueryFile": "${workspaceFolder}\\Speckle.query.pq"
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "powerquery",
|
||||
"operation": "msbuild",
|
||||
"additionalArgs": [
|
||||
"/restore",
|
||||
"/consoleloggerparameters:NoSummary",
|
||||
"/property:GenerateFullPaths=true"
|
||||
],
|
||||
"problemMatcher": ["$msCompile"],
|
||||
"group": "build",
|
||||
"label": "build"
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"label": "tests",
|
||||
"command": "${config:powerquery.sdk.tools.location}\\PQTest.exe",
|
||||
"args": [
|
||||
"run-test",
|
||||
"--extension",
|
||||
"${config:powerquery.sdk.defaultExtension}",
|
||||
"--queryFile",
|
||||
"${workspaceFolder}\\tests",
|
||||
"--prettyPrint"
|
||||
],
|
||||
"problemMatcher": [],
|
||||
"dependsOn": ["build"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
</h1>
|
||||
<h3 align="center">
|
||||
Data Connector for Microsoft's PowerBI platform
|
||||
Expected use case is that this data be used as source for the Speckle Visualization for PowerBI (https://github.com/specklesystems/speckle-powerbi-visuals)
|
||||
</h3>
|
||||
<p align="center"><b>Speckle</b> is the data infrastructure for the AEC industry.</p><br/>
|
||||
|
||||
@@ -53,6 +54,7 @@ Go to the [Releases](https://github.com/specklesystems/speckle-powerbi/releases)
|
||||
```
|
||||
YOUR_USER_FOLDER\Documents\Power BI Desktop\Custom Connectors\
|
||||
```
|
||||
If the folder doesn't exist, create it.
|
||||
|
||||
### Allow custom extensions to run
|
||||
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
[Version = "2.15.0-rc"]
|
||||
section Speckle;
|
||||
|
||||
AuthAppId = "spklpwerbi";
|
||||
AuthAppSecret = "spklpwerbi";
|
||||
|
||||
// The data source definition, used when connecting to any speckle server
|
||||
Speckle = [
|
||||
// This is used when running the connector on an on-premises data gateway
|
||||
TestConnection = (path) => {"Speckle.Api.GetUser", path},
|
||||
// This is the custom authentication strategy for our Connector
|
||||
Authentication = [
|
||||
OAuth = [
|
||||
Label = "Speckle Account",
|
||||
StartLogin = (clientApplication, dataSourcePath, state, display) =>
|
||||
let
|
||||
server = Text.Combine(
|
||||
{Uri.Parts(dataSourcePath)[Scheme], "://", Uri.Parts(dataSourcePath)[Host]}
|
||||
)
|
||||
in
|
||||
[
|
||||
LoginUri = Text.Combine({server, "authn", "verify", AuthAppId, state}, "/"),
|
||||
CallbackUri = "https://oauth.powerbi.com/views/oauthredirect.html",
|
||||
WindowHeight = 800,
|
||||
WindowWidth = 600,
|
||||
Context = null
|
||||
],
|
||||
FinishLogin = (clientApplication, dataSourcePath, context, callbackUri, state) =>
|
||||
let
|
||||
server = Text.Combine(
|
||||
{Uri.Parts(dataSourcePath)[Scheme], "://", Uri.Parts(dataSourcePath)[Host]}
|
||||
),
|
||||
Parts = Uri.Parts(callbackUri)[Query],
|
||||
Source = Web.Contents(
|
||||
Text.Combine({server, "auth", "token"}, "/"),
|
||||
[
|
||||
Headers = [
|
||||
#"Content-Type" = "application/json"
|
||||
],
|
||||
Content = Json.FromValue(
|
||||
[
|
||||
accessCode = Parts[access_code],
|
||||
appId = AuthAppId,
|
||||
appSecret = AuthAppSecret,
|
||||
challenge = state
|
||||
]
|
||||
)
|
||||
]
|
||||
),
|
||||
json = Json.Document(Source)
|
||||
in
|
||||
[
|
||||
access_token = json[token],
|
||||
scope = null,
|
||||
token_type = "bearer",
|
||||
refresh_token = json[refreshToken]
|
||||
],
|
||||
Refresh = (dataSourcePath, refreshToken) =>
|
||||
let
|
||||
server = Text.Combine(
|
||||
{Uri.Parts(dataSourcePath)[Scheme], "://", Uri.Parts(dataSourcePath)[Host]}
|
||||
),
|
||||
Source = Web.Contents(
|
||||
Text.Combine({server, "auth", "token"}, "/"),
|
||||
[
|
||||
Headers = [
|
||||
#"Content-Type" = "application/json"
|
||||
],
|
||||
Content = Json.FromValue(
|
||||
[
|
||||
refreshToken = refreshToken,
|
||||
appId = AuthAppId,
|
||||
appSecret = AuthAppSecret
|
||||
]
|
||||
)
|
||||
]
|
||||
),
|
||||
json = Json.Document(Source)
|
||||
in
|
||||
[
|
||||
access_token = json[token],
|
||||
scope = null,
|
||||
token_type = "bearer",
|
||||
refresh_token = json[refreshToken]
|
||||
]
|
||||
],
|
||||
Key = [
|
||||
KeyLabel = "Personal Access Token",
|
||||
Label = "Private Project"
|
||||
],
|
||||
Implicit = [
|
||||
Label = "Public Project"
|
||||
]
|
||||
],
|
||||
Label = "Speckle"
|
||||
];
|
||||
|
||||
// Gets the object referenced by a specific speckle URL
|
||||
[DataSource.Kind = "Speckle", Publish = "Get.ByUrl.Publish"]
|
||||
shared Speckle.GetByUrl.Structured = Value.ReplaceType(
|
||||
Speckle.LoadFunction("Get.ByUrl.pqm"),
|
||||
type function (
|
||||
url as (
|
||||
Uri.Type meta [
|
||||
Documentation.FieldCaption = "Gets a Speckle Object preserving it's structure",
|
||||
Documentation.FieldDescription = "The url of a model in a Speckle server project. You can copy it directly from your browser.",
|
||||
Documentation.SampleValues = {"https://app.speckle.systems/projects/23401adf/models/1234568"}
|
||||
]
|
||||
)
|
||||
) as record meta [
|
||||
Documentation.Name = "Speckle - Get Structured Object by URL",
|
||||
Documentation.LongDescription = "Returns the Speckle object the URL points to, while also preserving it's structure.
|
||||
Supports all types of model url:#(lf)
|
||||
- Model: will get the latest version of the specified model (i.e. 'https://app.speckle.systems/projects/PROJECT_ID/models/MODEL_ID')#(lf)
|
||||
- Version: will get a specific version from the project (i.e. 'https://app.speckle.systems/projects/PROJECT_ID/models/MODEL_ID@VERSION_ID')
|
||||
"
|
||||
]
|
||||
);
|
||||
|
||||
// [DataSource.Kind = "Speckle", Publish = "NavTable.Publish"]
|
||||
// shared Speckle.GetObjectAsNavTable = Value.ReplaceType(
|
||||
// NavigationTable.Simple, type function (url as Uri.Type) as table
|
||||
// );
|
||||
// Get's a flat list of speckle objects from a URL
|
||||
[DataSource.Kind = "Speckle", Publish = "GetByUrl.Publish"]
|
||||
shared Speckle.GetByUrl = Value.ReplaceType(
|
||||
Speckle.LoadFunction("GetByUrl.pqm"),
|
||||
type function (
|
||||
url as (
|
||||
Uri.Type meta [
|
||||
Documentation.FieldCaption = "Model URL",
|
||||
Documentation.FieldDescription = "The url of a model in a Speckle server. You can copy it directly from your browser.",
|
||||
Documentation.SampleValues = {"https://app.speckle.systems/projects/23401adf/models/1234568"}
|
||||
]
|
||||
)
|
||||
) as table meta [
|
||||
Documentation.Name = "Speckle - Get Model by URL",
|
||||
Documentation.LongDescription = "Returns a flat list of all objects contained in a Speckle model/version of a specific a project.
|
||||
Supports all types of model url:#(lf)
|
||||
- Model: will get the latest version of the specified model (i.e. 'https://app.speckle.systems/projects/PROJECT_ID/models/MODEL_ID')#(lf)
|
||||
- Version: will get a specific version from the project (i.e. 'https://app.speckle.systems/projects/PROJECT_ID/models/MODEL_ID@VERSION_ID')
|
||||
"
|
||||
]
|
||||
);
|
||||
|
||||
// Gets the current authenticated user, if any
|
||||
[DataSource.Kind = "Speckle"]
|
||||
shared Speckle.Api.GetUser = Value.ReplaceType(
|
||||
Speckle.LoadFunction("Api.GetUser.pqm"), type function (url as Uri.Type) as record
|
||||
);
|
||||
|
||||
// Generic fetch function to our GraphQL endpoint
|
||||
[DataSource.Kind = "Speckle"]
|
||||
shared Speckle.Api.Fetch = Value.ReplaceType(
|
||||
Speckle.LoadFunction("Api.Fetch.pqm"),
|
||||
type function (url as Uri.Type, optional query as text, optional variables as record) as record
|
||||
);
|
||||
|
||||
// Parses a stream url and returns a record with the type and values
|
||||
[DataSource.Kind = "Speckle"]
|
||||
shared Speckle.ParseUrl = Value.ReplaceType(
|
||||
Speckle.LoadFunction("ParseStreamUrl.pqm"), type function (url as Uri.Type) as record
|
||||
);
|
||||
|
||||
// [DataSource.Kind = "Speckle"]
|
||||
// shared Speckle.Api.REST.GetObject = Value.ReplaceType(
|
||||
// Speckle.LoadFunction("Api.REST.GetObject.pqm"),
|
||||
// type function (url as Uri.Type, optional streamId as text, optional objectId as text) as list
|
||||
// );
|
||||
Get.ByUrl.Publish = GetPublish("GetStream");
|
||||
|
||||
NavTable.Publish = GetPublish("GetObjectAsNavTable");
|
||||
|
||||
GetByUrl.Publish = GetPublish("GetByUrl");
|
||||
|
||||
GetPublish = Speckle.LoadFunction("GetPublish.pqm");
|
||||
|
||||
// Navigation table utility function
|
||||
Table.ToNavigationTable = Speckle.LoadFunction("Table.ToNavigationTable.pqm");
|
||||
|
||||
// Function to load `pqm` files
|
||||
shared Speckle.LoadFunction = (fileName as text) =>
|
||||
let
|
||||
binary = Extension.Contents(fileName), asText = Text.FromBinary(binary)
|
||||
in
|
||||
try
|
||||
Expression.Evaluate(asText, #shared) catch (e) =>
|
||||
error
|
||||
[
|
||||
Reason = "Speckle.LoadFunction Failure",
|
||||
Message.Format = "Loading '#{0}' failed - '#{1}': '#{2}'",
|
||||
Message.Parameters = {fileName, e[Reason], e[Message]},
|
||||
Detail = [File = fileName, Error = e]
|
||||
];
|
||||
|
||||
shared Speckle.Revit.Parameters.ToNameValueRecord = (r as record, optional exclude as list) as record =>
|
||||
let
|
||||
defaultExclude = {"id", "speckle_type", "applicationId", "totalChildrenCount"},
|
||||
fullExclusion = if exclude = null then defaultExclude else List.Union(defaultExclude, exclude),
|
||||
clean = Record.RemoveFields(r, fullExclusion, MissingField.Ignore),
|
||||
recTable = Record.ToTable(clean),
|
||||
cleanTable = Table.RemoveColumns(recTable, "Name"),
|
||||
expanded = Table.ExpandRecordColumn(
|
||||
cleanTable, "Value", {"name", "value", "applicationInternalName"}, {"Name", "Value", "UID"}
|
||||
),
|
||||
joined = Table.AddColumn(expanded, "Combo", each [Name] & " [" & [UID] & "]"),
|
||||
renamed = Table.RenameColumns(joined, {{"Name", "x"}, {"Combo", "Name"}}),
|
||||
result = Record.FromTable(renamed)
|
||||
in
|
||||
result;
|
||||
|
||||
shared Speckle.Utils.DynamicColumnExpand = (tbl as table, col as text) as table =>
|
||||
let
|
||||
uniqueFields = List.Distinct(List.Combine(List.Transform(Table.Column(tbl, col), Record.FieldNames))),
|
||||
expanded = Table.ExpandRecordColumn(tbl, col, uniqueFields)
|
||||
in
|
||||
expanded;
|
||||
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
|
||||
DefaultTargets="BuildMez">
|
||||
<PropertyGroup>
|
||||
<Version Condition="'$(Version)' == ''">2.0.0-wip</Version>
|
||||
<OutputPath Condition="'$(OutputPath)' == ''">$(MSBuildProjectDirectory)\bin\</OutputPath>
|
||||
<IntermediateOutputPath Condition="'$(IntermediateOutputPath)' == ''">
|
||||
$(MSBuildProjectDirectory)\obj\</IntermediateOutputPath>
|
||||
<MezIntermediatePath>$(IntermediateOutputPath)MEZ\</MezIntermediatePath>
|
||||
<MezOutputPath>$(OutputPath)$(MsBuildProjectName).mez</MezOutputPath>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<MezContent Include="Speckle.pq" />
|
||||
<MezContent Include="utilities\**\*.pqm" />
|
||||
<MezContent Include="speckle\**\*.pqm" />
|
||||
<MezContent Include="assets\SpeckleLogo16.png" />
|
||||
<MezContent Include="assets\SpeckleLogo20.png" />
|
||||
<MezContent Include="assets\SpeckleLogo24.png" />
|
||||
<MezContent Include="assets\SpeckleLogo32.png" />
|
||||
<MezContent Include="assets\SpeckleLogo40.png" />
|
||||
<MezContent Include="assets\SpeckleLogo48.png" />
|
||||
<MezContent Include="assets\SpeckleLogo64.png" />
|
||||
<MezContent Include="assets\SpeckleLogo80.png" />
|
||||
<MezContent Include="assets\resources.resx" />
|
||||
</ItemGroup>
|
||||
<Target Name="BuildMez" AfterTargets="Build" Inputs="@(MezContent)" Outputs="$(MezOutputPath)">
|
||||
<RemoveDir Directories="$(MezIntermediatePath)" />
|
||||
<Copy SourceFiles="@(MezContent)" DestinationFolder="$(MezIntermediatePath)" />
|
||||
<MakeDir Directories="$(OutputPath)" Condition="!Exists('$(OutputPath)')" />
|
||||
<ZipDirectory SourceDirectory="$(MezIntermediatePath)" DestinationFile="$(MezOutputPath)"
|
||||
Overwrite="true" />
|
||||
</Target>
|
||||
<Target Name="CopyToConnectors" AfterTargets="BuildMez">
|
||||
<Message
|
||||
Text="Copying .mez file to: $(UserProfile)\Documents\Power BI Desktop\Custom Connectors"
|
||||
Importance="High" />
|
||||
<MakeDir Directories="$(UserProfile)\Documents\Power BI Desktop\Custom Connectors\" />
|
||||
<Copy SourceFiles="$(MezOutputPath)"
|
||||
DestinationFolder="$(UserProfile)\Documents\Power BI Desktop\Custom Connectors\" />
|
||||
</Target>
|
||||
<Target Name="Clean">
|
||||
<RemoveDir Directories="$(MezIntermediatePath)" />
|
||||
<Delete Files="$(MezOutputPath)" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -0,0 +1,2 @@
|
||||
// Use this file to write queries to test your data connector
|
||||
let result = Speckle.GetByUrl("https://app.speckle.systems/projects/e2988234fb/models/60b2300470@b1f31a351a") in result
|
||||
@@ -1,25 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30804.86
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{4DF76451-A46A-4C0B-BE03-459FAAFA07E6}") = "Speckle", "Speckle\Speckle.mproj", "{D61F9470-E614-45C7-8047-E354FF219408}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{D61F9470-E614-45C7-8047-E354FF219408}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{D61F9470-E614-45C7-8047-E354FF219408}.Debug|x86.Build.0 = Debug|x86
|
||||
{D61F9470-E614-45C7-8047-E354FF219408}.Release|x86.ActiveCfg = Release|x86
|
||||
{D61F9470-E614-45C7-8047-E354FF219408}.Release|x86.Build.0 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {765772BB-45FA-4661-9573-F0F182FEB7C1}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,121 +0,0 @@
|
||||
<Project DefaultTargets="BuildExtension" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{0b5fe03d-aad9-4bc4-9edf-b543d5e50d29}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>MyRootNamespace</RootNamespace>
|
||||
<AssemblyName>MyAssemblyName</AssemblyName>
|
||||
<EnableUnmanagedDebugging>False</EnableUnmanagedDebugging>
|
||||
<AllowNativeQuery>False</AllowNativeQuery>
|
||||
<AsAction>False</AsAction>
|
||||
<FastCombine>False</FastCombine>
|
||||
<ClearLog>False</ClearLog>
|
||||
<ShowEngineTraces>False</ShowEngineTraces>
|
||||
<ShowUserTraces>False</ShowUserTraces>
|
||||
<LegacyRedirects>False</LegacyRedirects>
|
||||
<SuppressRowErrors>False</SuppressRowErrors>
|
||||
<SuppressCellErrors>False</SuppressCellErrors>
|
||||
<MaxRows>1000</MaxRows>
|
||||
<ExtensionProject>Yes</ExtensionProject>
|
||||
<Name>Speckle</Name>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
<!--Should be true, fix this when the debugger is implemented -->
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="mscorlib" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Speckle.pq">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="SpeckleLogo16.png">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="SpeckleLogo20.png">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="SpeckleLogo24.png">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="SpeckleLogo32.png">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="SpeckleLogo40.png">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="SpeckleLogo48.png">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="SpeckleLogo64.png">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="SpeckleLogo80.png">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="resources.resx">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Content Include="Speckle.query.pq">
|
||||
<SubType>Code</SubType>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<UsingTask TaskName="BuildExtension" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v12.0.dll">
|
||||
<ParameterGroup>
|
||||
<InputDirectory ParameterType="System.String" Required="true" />
|
||||
<OutputFile ParameterType="System.String" Required="true" />
|
||||
</ParameterGroup>
|
||||
<Task>
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.IO.Compression.FileSystem" />
|
||||
<Using Namespace="System.Globalization" />
|
||||
<Using Namespace="System.IO.Compression " />
|
||||
<Code Type="Fragment" Language="cs"><![CDATA[
|
||||
using(FileStream fileStream = File.Create(OutputFile))
|
||||
using(ZipArchive archiveOut = new ZipArchive(fileStream, ZipArchiveMode.Create, false))
|
||||
{
|
||||
foreach(string fullPath in Directory.EnumerateFiles(InputDirectory))
|
||||
{
|
||||
string filename = Path.GetFileName(fullPath);
|
||||
|
||||
archiveOut.CreateEntryFromFile(fullPath, filename, CompressionLevel.Optimal);
|
||||
}
|
||||
}
|
||||
]]></Code>
|
||||
</Task>
|
||||
</UsingTask>
|
||||
<Target Name="BuildExtension" DependsOnTargets="ExtensionClean">
|
||||
<ItemGroup>
|
||||
<PQFiles Include="@(Compile)" Condition="'%(Extension)' == '.pq'" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<NonPQFiles Include="@(Compile)" Condition="'%(Extension)' != '.pq'" />
|
||||
</ItemGroup>
|
||||
<MakeDir Directories="$(IntermediateOutputPath)" />
|
||||
<MakeDir Directories="$(OutputPath)" />
|
||||
<Copy SourceFiles="@(NonPQFiles)" DestinationFolder="$(IntermediateOutputPath)" />
|
||||
<Copy SourceFiles="@(PQFiles)" DestinationFiles="@(PQFiles->'$(IntermediateOutputPath)%(RecursiveDir)%(FileName).m')" />
|
||||
<BuildExtension InputDirectory="$(IntermediateOutputPath)" OutputFile="$(OutputPath)\$(ProjectName).mez" />
|
||||
<Message Text="Copying .mez file to: $(UserProfile)\Documents\Power BI Desktop\Custom Connectors" Importance="High" />
|
||||
<MakeDir Directories="$(UserProfile)\Documents\Power BI Desktop\Custom Connectors\" />
|
||||
<Copy SourceFiles="$(OutputPath)\$(ProjectName).mez" DestinationFolder="$(UserProfile)\Documents\Power BI Desktop\Custom Connectors">
|
||||
</Copy>
|
||||
</Target>
|
||||
<Target Name="ExtensionClean">
|
||||
<!-- Remove obj folder -->
|
||||
<RemoveDir Directories="$(BaseIntermediateOutputPath)" />
|
||||
<!-- Remove bin folder -->
|
||||
<RemoveDir Directories="$(OutputPath)" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,201 +0,0 @@
|
||||
section Speckle;
|
||||
|
||||
/* This is an additional nav bar that can display branches of a stream: not using this for now
|
||||
[DataSource.Kind="Speckle", Publish="Speckle.Publish"]
|
||||
shared Speckle.Contents = Value.ReplaceType(NavigationTable.Simple, type function (url as Uri.Type) as any);
|
||||
|
||||
// set up nav table
|
||||
shared NavigationTable.Simple = (url) as table =>
|
||||
let
|
||||
baseUrl = Uri.Parts(url)[Host] as text,
|
||||
streamId = Text.Split(Uri.Parts(url)[Path], "/"){2},
|
||||
objects = Speckle.GetBranches(baseUrl, streamId),
|
||||
table = #table(
|
||||
{"Name", "Key", "Data", "ItemKind", "ItemName", "IsLeaf"},
|
||||
List.InsertRange(objects, List.Count(objects), {{"GetCommit", "GetCommit", Speckle.GetObjectFromObject(baseUrl, streamId), "Function", "Function", true}})
|
||||
),
|
||||
NavTable = Table.ToNavigationTable(table, {"Key"}, "Name", "Data", "ItemKind", "ItemName", "IsLeaf")
|
||||
in
|
||||
NavTable;
|
||||
|
||||
Speckle.GetBranches = (url, id) =>
|
||||
let
|
||||
Source = Web.Contents(
|
||||
Text.Combine({"https:/", url, "graphql"}, "/"),
|
||||
[
|
||||
Headers=[
|
||||
#"Method"="POST",
|
||||
#"Content-Type"="application/json"
|
||||
],
|
||||
Content=Text.ToBinary("{""query"": ""query { stream( id: \"""&id&"\"" ) { branches { items { name commits { items { id message sourceApplication authorName createdAt } } } } } }""}")
|
||||
]
|
||||
),
|
||||
#"JSON" = Json.Document(Source),
|
||||
branches = #"JSON"[data][stream][branches][items],
|
||||
branchList = List.Generate(
|
||||
() => [x = 0, y = Speckle.GetBranchAsList(branches{x})],
|
||||
each [x] < List.Count(branches),
|
||||
each [x = [x] + 1, y = Speckle.GetBranchAsList(branches{x})],
|
||||
each [y]
|
||||
)
|
||||
in
|
||||
branchList;
|
||||
|
||||
Speckle.GetBranchAsList = (branchRecord) =>
|
||||
let
|
||||
commits = Table.FromRecords(branchRecord[commits][items]),
|
||||
list = {branchRecord[name], branchRecord[name], commits, "Table", "Table", true}
|
||||
in
|
||||
list;
|
||||
*/
|
||||
|
||||
[DataSource.Kind="Speckle", Publish="Speckle.Publish"]
|
||||
shared Speckle.Contents = Value.ReplaceType(CommitTable, type function (StreamUrl as Uri.Type) as any);
|
||||
|
||||
shared CommitTable = (url) as table =>
|
||||
let
|
||||
// Get server and streamId, and branchName / commitId / objectid from the input url
|
||||
server = Text.Combine({"https://", Uri.Parts(url)[Host]}),
|
||||
segments = Text.Split(Text.AfterDelimiter(Uri.Parts(url)[Path], "/", 0), "/"),
|
||||
streamId = segments{1},
|
||||
branchName = if( List.Count(segments) = 4 and segments{2} = "branches" ) then segments{3} else null,
|
||||
commitId = if (List.Count(segments) = 4 and segments{2} = "commits" ) then segments{3} else null,
|
||||
objectId = if (List.Count(segments) = 4 and segments{2} = "objects" ) then segments{3} else null,
|
||||
|
||||
commitTable = if (commitId <> null) then Speckle.GetObjectFromCommit(server, streamId, commitId)
|
||||
else if (objectId <> null) then Speckle.GetObjectFromObject(server, streamId, objectId, false)
|
||||
else if (branchName <> null) then #table( { "Error" }, { { "Invalid URL, use a stream or commit or object url" } } ) // currently not implemented, see reason below
|
||||
else Speckle.GetObjectFromStream(server, streamId)
|
||||
in
|
||||
commitTable;
|
||||
|
||||
Speckle.GetObjectFromStream = (server, streamId) =>
|
||||
let
|
||||
branchName = "main",
|
||||
Source = Web.Contents(
|
||||
Text.Combine({server, "graphql"}, "/"),
|
||||
[
|
||||
Headers=[
|
||||
#"Method"="POST",
|
||||
#"Content-Type"="application/json"
|
||||
],
|
||||
Content=Text.ToBinary("{""query"": ""query { stream( id: \"""&streamId&"\"" ) { branch (name: \"""&branchName&"\""){ commits (limit: 1) { items { referencedObject } } } } }""}")
|
||||
]
|
||||
),
|
||||
#"JSON" = Json.Document(Source),
|
||||
objectId = #"JSON"[data][stream][branch][commits][items]{0}[referencedObject],
|
||||
objectsTable = Speckle.GetObjectFromObject(server, streamId, objectId, true)
|
||||
in
|
||||
objectsTable;
|
||||
|
||||
/* Not implemented since power query M uri does not have a decode method...def not manually writing a method to handle special chars and emojis
|
||||
Speckle.GetObjectFromBranch = (server, streamId, branchName) =>
|
||||
let
|
||||
Source = Web.Contents(
|
||||
Text.Combine({server, "graphql"}, "/"),
|
||||
[
|
||||
Headers=[
|
||||
#"Method"="POST",
|
||||
#"Content-Type"="application/json"
|
||||
],
|
||||
Content=Text.ToBinary("{""query"": ""query { stream( id: \"""&streamId&"\"" ) { branch (name: \"""&branchName&"\""){ commits (limit: 1) { items { referencedObject } } } } }""}")
|
||||
]
|
||||
),
|
||||
#"JSON" = Json.Document(Source),
|
||||
objectId = #"JSON"[data][stream][branch][commits][items]{0}[referencedObject],
|
||||
objectsTable = Speckle.GetObjectFromObject(server, streamId, objectId)
|
||||
in
|
||||
objectsTable;
|
||||
*/
|
||||
|
||||
shared Speckle.GetObjectFromCommit = (server, streamId, commitId) =>
|
||||
let
|
||||
Source = Web.Contents(
|
||||
Text.Combine({server, "graphql"}, "/"),
|
||||
[
|
||||
Headers=[
|
||||
#"Method"="POST",
|
||||
#"Content-Type"="application/json"
|
||||
],
|
||||
Content=Text.ToBinary("{""query"": ""query { stream( id: \"""&streamId&"\"" ) { commit (id: \"""&commitId&"\""){ referencedObject } } }""}")
|
||||
]
|
||||
),
|
||||
#"JSON" = Json.Document(Source),
|
||||
objectId = #"JSON"[data][stream][commit][referencedObject],
|
||||
objectsTable = Speckle.GetObjectFromObject(server, streamId, objectId, true)
|
||||
in
|
||||
objectsTable;
|
||||
|
||||
Speckle.GetObjectFromObject = (server, streamId, objectId, IsCommitObject) =>
|
||||
let
|
||||
query = if (IsCommitObject) then "{""query"": ""query { stream( id: \"""&streamId&"\"" ) { object (id: \"""&objectId&"\"") { children { objects { data } } } } }""}"
|
||||
else "{""query"": ""query { stream( id: \"""&streamId&"\"" ) { object (id: \"""&objectId&"\"") { data } } }""}",
|
||||
Source = Web.Contents(
|
||||
Text.Combine({server, "graphql"}, "/"),
|
||||
[
|
||||
Headers=[
|
||||
#"Method"="POST",
|
||||
#"Content-Type"="application/json"
|
||||
],
|
||||
Content=Text.ToBinary(query)
|
||||
]
|
||||
),
|
||||
#"JSON" = Json.Document(Source),
|
||||
objects = if (IsCommitObject) then #"JSON"[data][stream][object][children][objects]
|
||||
else {#"JSON"[data][stream][object][data]},
|
||||
objectsTable = Table.FromRecords(objects)
|
||||
in
|
||||
objectsTable;
|
||||
|
||||
// Data Source Kind description
|
||||
Speckle = [
|
||||
Authentication = [
|
||||
Key = [
|
||||
KeyLabel="Personal Access Token",
|
||||
Label = "Private stream"
|
||||
],
|
||||
Implicit = [
|
||||
Label = "Public stream"
|
||||
]
|
||||
],
|
||||
Label = Extension.LoadString("Speckle Connector")
|
||||
];
|
||||
|
||||
// Data Source UI publishing description
|
||||
Speckle.Publish = [
|
||||
Beta = true,
|
||||
Category = "Other",
|
||||
ButtonText = { Extension.LoadString("ButtonTitle"), Extension.LoadString("ButtonHelp") },
|
||||
LearnMoreUrl = "https://speckle.guide",
|
||||
SourceImage = Speckle.Icons,
|
||||
SourceTypeImage = Speckle.Icons
|
||||
];
|
||||
|
||||
Speckle.Icons = [
|
||||
Icon16 = { Extension.Contents("SpeckleLogo16.png"), Extension.Contents("SpeckleLogo20.png"), Extension.Contents("SpeckleLogo24.png"), Extension.Contents("SpeckleLogo32.png") },
|
||||
Icon32 = { Extension.Contents("SpeckleLogo32.png"), Extension.Contents("SpeckleLogo40.png"), Extension.Contents("SpeckleLogo48.png"), Extension.Contents("SpeckleLogo64.png") }
|
||||
];
|
||||
|
||||
// copy and pasted function from microsoft docs since it's not included yet in M standard lib
|
||||
Table.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;
|
||||
@@ -1,5 +0,0 @@
|
||||
// Use this file to write queries to test your data connector
|
||||
let
|
||||
result = Speckle.Contents("https://speckle.xyz/streams/5dfbeb49c9/objects/ed4748572b27cfe008f2592a44ab85f1")
|
||||
in
|
||||
result
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
@@ -22,8 +22,10 @@
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing"
|
||||
mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework
|
||||
object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
@@ -59,7 +61,8 @@
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
@@ -112,18 +115,65 @@
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0,
|
||||
Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0,
|
||||
Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="ButtonHelp" xml:space="preserve">
|
||||
<value>Connect to Speckle</value>
|
||||
</data>
|
||||
<data name="ButtonTitle" xml:space="preserve">
|
||||
<value>Speckle</value>
|
||||
</data>
|
||||
<data name="DataSourceLabel" xml:space="preserve">
|
||||
<value>Speckle</value>
|
||||
</data>
|
||||
<data name="GetByUrl.Help" xml:space="preserve">
|
||||
<value>Connect to Speckle by Model URL</value>
|
||||
</data>
|
||||
<data name="GetByUrl.Label" xml:space="preserve">
|
||||
<value>Speckle</value>
|
||||
</data>
|
||||
<data name="GetByUrl.Title" xml:space="preserve">
|
||||
<value>Speckle - Get Model by URL</value>
|
||||
</data>
|
||||
<data name="GetObjFromBranch.Help" xml:space="preserve">
|
||||
<value>Connect to Speckle by server URL, stream ID and branch name</value>
|
||||
</data>
|
||||
<data name="GetObjFromBranch.Label" xml:space="preserve">
|
||||
<value>Get the latest commit from a stream's branch</value>
|
||||
</data>
|
||||
<data name="GetObjFromBranch.Title" xml:space="preserve">
|
||||
<value>Speckle - Get Stream branch</value>
|
||||
</data>
|
||||
<data name="GetObjFromCommit.Help" xml:space="preserve">
|
||||
<value>Connect to Speckle by server URL, stream ID and commit ID</value>
|
||||
</data>
|
||||
<data name="GetObjFromCommit.Label" xml:space="preserve">
|
||||
<value>A label</value>
|
||||
</data>
|
||||
<data name="GetObjFromCommit.Title" xml:space="preserve">
|
||||
<value>Speckle - Get Stream commit</value>
|
||||
</data>
|
||||
<data name="GetStream.Help" xml:space="preserve">
|
||||
<value>Connect to Speckle by server URL and stream ID</value>
|
||||
</data>
|
||||
<data name="GetStream.Label" xml:space="preserve">
|
||||
<value>Speckle</value>
|
||||
</data>
|
||||
<data name="GetStream.Title" xml:space="preserve">
|
||||
<value>Speckle - Get Model by URL [Structured]</value>
|
||||
</data>
|
||||
<data name="GetObjectAsNavTable.Title" xml:space="preserve">
|
||||
<value>Speckle - Get Object as NavTable</value>
|
||||
</data>
|
||||
<data name="GetObjectAsNavTable.Label" xml:space="preserve">
|
||||
<value>Speckle</value>
|
||||
</data>
|
||||
<data name="GetObjectAsNavTable.Help" xml:space="preserve">
|
||||
<value>Returns a navigation table for a given object</value>
|
||||
</data>
|
||||
<data name="Traverse.Title" xml:space="preserve">
|
||||
<value>Traverse an object and populate refs</value>
|
||||
</data>
|
||||
<data name="Traverse.Label" xml:space="preserve">
|
||||
<value>Traverse</value>
|
||||
</data>
|
||||
<data name="Traverse.Help" xml:space="preserve">
|
||||
<value>Traverse help</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -0,0 +1,48 @@
|
||||
let
|
||||
Fetch = Extension.LoadFunction("Api.Fetch.pqm"),
|
||||
CommitReceived = Extension.LoadFunction("Api.CommitReceived.pqm"),
|
||||
Extension.LoadFunction = (fileName as text) =>
|
||||
let
|
||||
binary = Extension.Contents(fileName), asText = Text.FromBinary(binary)
|
||||
in
|
||||
try
|
||||
Expression.Evaluate(asText, #shared) catch (e) =>
|
||||
error
|
||||
[
|
||||
Reason = "Extension.LoadFunction Failure",
|
||||
Message.Format = "Loading '#{0}' failed - '#{1}': '#{2}'",
|
||||
Message.Parameters = {fileName, e[Reason], e[Message]},
|
||||
Detail = [File = fileName, Error = e]
|
||||
]
|
||||
in
|
||||
(server as text, streamId as text, branchName as text, limit as number) as list =>
|
||||
let
|
||||
decodedBranchName = Record.Field(
|
||||
Record.Field(Uri.Parts("http://www.dummy.com?" & Uri.BuildQueryString([A = branchName])), "Query"),
|
||||
"A"
|
||||
),
|
||||
// Hacky way to decode base64 strings: Put them in a url query param and parse the URL
|
||||
apiKey = try Extension.CurrentCredential()[Key] otherwise null,
|
||||
query = "query($streamId: String!, $branchName: String!, $limit: Int!) {
|
||||
stream( id: $streamId ) {
|
||||
branch (name: $branchName ){
|
||||
commits (limit: $limit) {
|
||||
items {
|
||||
id
|
||||
referencedObject
|
||||
sourceApplication
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}",
|
||||
res = Fetch(server, query, [streamId = streamId, branchName = decodedBranchName, limit = limit]),
|
||||
branch = res[stream][branch],
|
||||
commits = branch[commits][items]
|
||||
in
|
||||
if branch = null then
|
||||
error Text.Format("The branch '#{0}' does not exist in stream '#{1}'", {decodedBranchName, streamId})
|
||||
else if List.Count(branch[commits][items]) = 0 then
|
||||
error Text.Format("The branch '#{0}' in stream #{1} has no commits", {decodedBranchName, streamId})
|
||||
else
|
||||
commits
|
||||
@@ -0,0 +1,47 @@
|
||||
let
|
||||
Fetch = Extension.LoadFunction("Api.Fetch.pqm"),
|
||||
Traverse = Extension.LoadFunction("Traverse.pqm"),
|
||||
GetObject = Extension.LoadFunction("Api.GetObject.pqm"),
|
||||
GetStreamCommit = Extension.LoadFunction("Get.StreamCommit.pqm"),
|
||||
GetBranchCommits = Extension.LoadFunction("Get.BranchCommits.pqm"),
|
||||
CommitReceived = Extension.LoadFunction("Api.CommitReceived.pqm"),
|
||||
ParseStreamUrl = Extension.LoadFunction("ParseStreamUrl.pqm"),
|
||||
CleanUpObject = Extension.LoadFunction("CleanUpObject.pqm"),
|
||||
Extension.LoadFunction = (fileName as text) =>
|
||||
let
|
||||
binary = Extension.Contents(fileName), asText = Text.FromBinary(binary)
|
||||
in
|
||||
try
|
||||
Expression.Evaluate(asText, #shared) catch (e) =>
|
||||
error
|
||||
[
|
||||
Reason = "Extension.LoadFunction Failure",
|
||||
Message.Format = "Loading '#{0}' failed - '#{1}': '#{2}'",
|
||||
Message.Parameters = {fileName, e[Reason], e[Message]},
|
||||
Detail = [File = fileName, Error = e]
|
||||
]
|
||||
in
|
||||
(url as text) as record =>
|
||||
let
|
||||
// Get server and streamId, and branchName / commitId / objectid from the input url
|
||||
stream = ParseStreamUrl(url),
|
||||
id = stream[id],
|
||||
server = stream[server],
|
||||
commit =
|
||||
if (stream[urlType] = "Stream") then
|
||||
GetBranchCommits(server, id, "main", 1){0}
|
||||
else if (stream[urlType] = "Branch") then
|
||||
GetBranchCommits(server, id, stream[branch], 1){0}
|
||||
else if (stream[urlType] = "Commit") then
|
||||
GetStreamCommit(server, id, stream[commit])
|
||||
else
|
||||
//We deal with object URLs directly
|
||||
[referencedObject = stream[object]],
|
||||
object = GetObject(server, id, commit[referencedObject]),
|
||||
rr = CommitReceived(server, id, commit),
|
||||
result = Traverse(CleanUpObject(object) meta [server = server, stream = id, commit = commit])
|
||||
in
|
||||
if rr then
|
||||
result
|
||||
else
|
||||
result
|
||||
@@ -0,0 +1,36 @@
|
||||
let
|
||||
Fetch = Extension.LoadFunction("Api.Fetch.pqm"),
|
||||
Extension.LoadFunction = (fileName as text) =>
|
||||
let
|
||||
binary = Extension.Contents(fileName), asText = Text.FromBinary(binary)
|
||||
in
|
||||
try
|
||||
Expression.Evaluate(asText, #shared) catch (e) =>
|
||||
error
|
||||
[
|
||||
Reason = "Extension.LoadFunction Failure",
|
||||
Message.Format = "Loading '#{0}' failed - '#{1}': '#{2}'",
|
||||
Message.Parameters = {fileName, e[Reason], e[Message]},
|
||||
Detail = [File = fileName, Error = e]
|
||||
]
|
||||
in
|
||||
(server as text, streamId as text, commitId as text) as record =>
|
||||
let
|
||||
apiKey = try Extension.CurrentCredential()[Key] otherwise null,
|
||||
query = "query($streamId: String!, $commitId: String!) {
|
||||
stream( id: $streamId ) {
|
||||
commit (id: $commitId) {
|
||||
id
|
||||
sourceApplication
|
||||
referencedObject
|
||||
}
|
||||
}
|
||||
}",
|
||||
variables = [streamId = streamId, commitId = commitId],
|
||||
#"JSON" = Fetch(server, query, variables),
|
||||
commit = #"JSON"[stream][commit]
|
||||
in
|
||||
if commit = null then
|
||||
error "The commit did not exist on this stream"
|
||||
else
|
||||
commit
|
||||
@@ -0,0 +1,64 @@
|
||||
(appName as text) =>
|
||||
let
|
||||
replaced = Text.Replace(appName, " ", ""), name = Text.Lower(replaced)
|
||||
in
|
||||
if Text.Contains(name, "dynamo") then
|
||||
"dynamo"
|
||||
else if Text.Contains(name, "revit") then
|
||||
"revit"
|
||||
else if Text.Contains(name, "autocad") then
|
||||
"autocad"
|
||||
else if Text.Contains(name, "civil") then
|
||||
"civil"
|
||||
else if Text.Contains(name, "rhino") then
|
||||
"rhino"
|
||||
else if Text.Contains(name, "grasshopper") then
|
||||
"grasshopper"
|
||||
else if Text.Contains(name, "unity") then
|
||||
"unity"
|
||||
else if Text.Contains(name, "gsa") then
|
||||
"gsa"
|
||||
else if Text.Contains(name, "microstation") then
|
||||
"microstation"
|
||||
else if Text.Contains(name, "openroads") then
|
||||
"openroads"
|
||||
else if Text.Contains(name, "openrail") then
|
||||
"openrail"
|
||||
else if Text.Contains(name, "openbuildings") then
|
||||
"openbuildings"
|
||||
else if Text.Contains(name, "etabs") then
|
||||
"etabs"
|
||||
else if Text.Contains(name, "sap") then
|
||||
"sap"
|
||||
else if Text.Contains(name, "csibridge") then
|
||||
"csibridge"
|
||||
else if Text.Contains(name, "safe") then
|
||||
"safe"
|
||||
else if Text.Contains(name, "teklastructures") then
|
||||
"teklastructures"
|
||||
else if Text.Contains(name, "dxf") then
|
||||
"dxf"
|
||||
else if Text.Contains(name, "excel") then
|
||||
"excel"
|
||||
else if Text.Contains(name, "unreal") then
|
||||
"unreal"
|
||||
else if Text.Contains(name, "powerbi") then
|
||||
"powerbi"
|
||||
else if Text.Contains(name, "blender") then
|
||||
"blender"
|
||||
else if Text.Contains(name, "qgis") then
|
||||
"qgis"
|
||||
else if Text.Contains(name, "arcgis") then
|
||||
"arcgis"
|
||||
else if Text.Contains(name, "sketchup") then
|
||||
"sketchup"
|
||||
else if Text.Contains(name, "archicad") then
|
||||
"archicad"
|
||||
else if Text.Contains(name, "topsolid") then
|
||||
"topsolid"
|
||||
else if Text.Contains(name, "python") then
|
||||
"python"
|
||||
else if Text.Contains(name, "net") then
|
||||
"net"
|
||||
else
|
||||
"other"
|
||||
@@ -0,0 +1,58 @@
|
||||
let
|
||||
Fetch = Extension.LoadFunction("Api.Fetch.pqm"),
|
||||
GetObject = Extension.LoadFunction("Api.GetObject.pqm"),
|
||||
GetAllObjectChildren = Extension.LoadFunction("Api.GetAllObjectChildren.pqm"),
|
||||
GetObjectFromCommit = Extension.LoadFunction("GetObjectFromCommit.pqm"),
|
||||
GetObjectFromBranch = Extension.LoadFunction("GetObjectFromBranch.pqm"),
|
||||
CommitReceived = Extension.LoadFunction("Api.CommitReceived.pqm"),
|
||||
ParseStreamUrl = Extension.LoadFunction("ParseStreamUrl.pqm"),
|
||||
Extension.LoadFunction = (fileName as text) =>
|
||||
let
|
||||
binary = Extension.Contents(fileName), asText = Text.FromBinary(binary)
|
||||
in
|
||||
try
|
||||
Expression.Evaluate(asText, #shared) catch (e) =>
|
||||
error
|
||||
[
|
||||
Reason = "Extension.LoadFunction Failure",
|
||||
Message.Format = "Loading '#{0}' failed - '#{1}': '#{2}'",
|
||||
Message.Parameters = {fileName, e[Reason], e[Message]},
|
||||
Detail = [File = fileName, Error = e]
|
||||
]
|
||||
in
|
||||
(url as text) as table =>
|
||||
let
|
||||
// Get server and streamId, and branchName / commitId / objectid from the input url
|
||||
stream = ParseStreamUrl(url),
|
||||
id = stream[id],
|
||||
server = stream[server],
|
||||
commitObjectsTable =
|
||||
if (stream[urlType] = "Commit") then
|
||||
GetObjectFromCommit(server, id, stream[commit])
|
||||
else if (stream[urlType] = "Object") then
|
||||
GetAllObjectChildren(server, id, stream[object])
|
||||
else if (stream[urlType] = "Branch") then
|
||||
GetObjectFromBranch(server, id, stream[branch])
|
||||
else
|
||||
GetObjectFromBranch(server, id, "main"),
|
||||
addStreamUrl = Table.AddColumn(commitObjectsTable, "Model URL", each server & "/streams/" & id),
|
||||
addParentObjectId = Table.AddColumn(
|
||||
addStreamUrl, "Version Object ID", each Value.Metadata(commitObjectsTable)[objectId]
|
||||
),
|
||||
addUrlType = Table.AddColumn(addParentObjectId, "URL Type", each stream[urlType]),
|
||||
addObjectIdCol = Table.AddColumn(addUrlType, "Object ID", each try[data][id] otherwise null),
|
||||
addSpeckleTypeCol = Table.AddColumn(
|
||||
addObjectIdCol, "speckle_type", each try[data][speckle_type] otherwise null
|
||||
),
|
||||
final = Table.ReorderColumns(
|
||||
addSpeckleTypeCol, {
|
||||
"Model URL",
|
||||
"URL Type",
|
||||
"Version Object ID",
|
||||
"Object ID",
|
||||
"speckle_type",
|
||||
"data"
|
||||
}
|
||||
)
|
||||
in
|
||||
final
|
||||
@@ -0,0 +1,55 @@
|
||||
let
|
||||
Fetch = Extension.LoadFunction("Api.Fetch.pqm"),
|
||||
GetAllObjectChildren = Extension.LoadFunction("Api.GetAllObjectChildren.pqm"),
|
||||
CommitReceived = Extension.LoadFunction("Api.CommitReceived.pqm"),
|
||||
Extension.LoadFunction = (fileName as text) =>
|
||||
let
|
||||
binary = Extension.Contents(fileName), asText = Text.FromBinary(binary)
|
||||
in
|
||||
try
|
||||
Expression.Evaluate(asText, #shared) catch (e) =>
|
||||
error
|
||||
[
|
||||
Reason = "Extension.LoadFunction Failure",
|
||||
Message.Format = "Loading '#{0}' failed - '#{1}': '#{2}'",
|
||||
Message.Parameters = {fileName, e[Reason], e[Message]},
|
||||
Detail = [File = fileName, Error = e]
|
||||
]
|
||||
in
|
||||
(server as text, streamId as text, branchName as text) as table =>
|
||||
let
|
||||
decodedBranchName = Record.Field(
|
||||
Record.Field(Uri.Parts("http://www.dummy.com?" & Uri.BuildQueryString([A = branchName])), "Query"),
|
||||
"A"
|
||||
),
|
||||
// Hacky way to decode base64 strings: Put them in a url query param and parse the URL
|
||||
apiKey = try Extension.CurrentCredential()[Key] otherwise null,
|
||||
query = "query($streamId: String!, $branchName: String!) {
|
||||
stream( id: $streamId ) {
|
||||
branch (name: $branchName ){
|
||||
commits (limit: 1) {
|
||||
items {
|
||||
id
|
||||
referencedObject
|
||||
sourceApplication
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}",
|
||||
res = Fetch(server, query, [streamId = streamId, branchName = decodedBranchName]),
|
||||
branch = res[stream][branch],
|
||||
commit = branch[commits][items]{0},
|
||||
objectsTable = GetAllObjectChildren(server, streamId, commit[referencedObject]),
|
||||
rr = CommitReceived(server, streamId, commit)
|
||||
in
|
||||
if branch = null then
|
||||
error Text.Format("The branch '#{0}' does not exist in stream '#{1}'", {decodedBranchName, streamId})
|
||||
else if List.Count(branch[commits][items]) = 0 then
|
||||
error Text.Format("The branch '#{0}' in stream #{1} has no commits", {decodedBranchName, streamId})
|
||||
else
|
||||
// Force evaluation of read receipt (ideally it should happen after fetching, but can't find a way)
|
||||
if rr then
|
||||
objectsTable
|
||||
else
|
||||
objectsTable
|
||||
@@ -0,0 +1,43 @@
|
||||
let
|
||||
Fetch = Extension.LoadFunction("Api.Fetch.pqm"),
|
||||
GetAllObjectChildren = Extension.LoadFunction("Api.GetAllObjectChildren.pqm"),
|
||||
CommitReceived = Extension.LoadFunction("Api.CommitReceived.pqm"),
|
||||
Extension.LoadFunction = (fileName as text) =>
|
||||
let
|
||||
binary = Extension.Contents(fileName), asText = Text.FromBinary(binary)
|
||||
in
|
||||
try
|
||||
Expression.Evaluate(asText, #shared) catch (e) =>
|
||||
error
|
||||
[
|
||||
Reason = "Extension.LoadFunction Failure",
|
||||
Message.Format = "Loading '#{0}' failed - '#{1}': '#{2}'",
|
||||
Message.Parameters = {fileName, e[Reason], e[Message]},
|
||||
Detail = [File = fileName, Error = e]
|
||||
]
|
||||
in
|
||||
(server as text, streamId as text, commitId as text) as table =>
|
||||
let
|
||||
apiKey = try Extension.CurrentCredential()[Key] otherwise null,
|
||||
query = "query($streamId: String!, $commitId: String!) {
|
||||
stream( id: $streamId ) {
|
||||
commit (id: $commitId) {
|
||||
id
|
||||
sourceApplication
|
||||
referencedObject
|
||||
authorId
|
||||
}
|
||||
}
|
||||
}",
|
||||
variables = [streamId = streamId, commitId = commitId],
|
||||
#"JSON" = Fetch(server, query, variables),
|
||||
commit = #"JSON"[stream][commit],
|
||||
objectsTable = GetAllObjectChildren(server, streamId, commit[referencedObject]),
|
||||
rr = CommitReceived(server, streamId, commit)
|
||||
in
|
||||
if commit = null then
|
||||
error "The commit did not exist on this stream"
|
||||
else if rr then
|
||||
objectsTable
|
||||
else
|
||||
objectsTable
|
||||
@@ -0,0 +1,33 @@
|
||||
let
|
||||
Speckle.Api.Fetch = Extension.LoadFunction("Api.Fetch.pqm"),
|
||||
Speckle.LogEvent = Extension.LoadFunction("LogEvent.pqm"),
|
||||
Extension.LoadFunction = (fileName as text) =>
|
||||
let
|
||||
binary = Extension.Contents(fileName), asText = Text.FromBinary(binary)
|
||||
in
|
||||
try
|
||||
Expression.Evaluate(asText, #shared) catch (e) =>
|
||||
error
|
||||
[
|
||||
Reason = "Extension.LoadFunction Failure",
|
||||
Message.Format = "Loading '#{0}' failed - '#{1}': '#{2}'",
|
||||
Message.Parameters = {fileName, e[Reason], e[Message]},
|
||||
Detail = [File = fileName, Error = e]
|
||||
]
|
||||
in
|
||||
(server, streamId, commit) =>
|
||||
let
|
||||
query = "mutation($input: CommitReceivedInput!) {
|
||||
commitReceive(input: $input)
|
||||
}",
|
||||
variables = [
|
||||
input = [
|
||||
streamId = streamId,
|
||||
commitId = commit[id],
|
||||
sourceApplication = "PowerBI"
|
||||
]
|
||||
],
|
||||
s = Speckle.LogEvent(server, commit)
|
||||
in
|
||||
// Read receipts should fail gracefully no matter what
|
||||
try Speckle.Api.Fetch(s, query, variables)[commitReceive] otherwise false
|
||||
@@ -0,0 +1,33 @@
|
||||
(server as text, optional query as text, optional variables as record) as record =>
|
||||
let
|
||||
apiKey = try Extension.CurrentCredential()[Key] otherwise null,
|
||||
defaultQuery = "query {
|
||||
activeUser {
|
||||
email
|
||||
name
|
||||
}
|
||||
serverInfo {
|
||||
name
|
||||
company
|
||||
version
|
||||
}
|
||||
}",
|
||||
Source = Web.Contents(
|
||||
Text.Combine({server, "graphql"}, "/"),
|
||||
[
|
||||
Headers = [
|
||||
#"Method" = "POST",
|
||||
#"Content-Type" = "application/json",
|
||||
#"Authorization" = if apiKey = null then "" else Text.Format("Bearer #{0}", {apiKey})
|
||||
],
|
||||
ManualStatusHandling = {400},
|
||||
Content = Json.FromValue([query = Text.From(query ?? defaultQuery), variables = variables])
|
||||
]
|
||||
),
|
||||
#"JSON" = Json.Document(Source)
|
||||
in
|
||||
// Check if response contains errors, if so, return first error.
|
||||
if Record.HasFields(#"JSON", {"errors"}) then
|
||||
error #"JSON"[errors]{0}[message]
|
||||
else
|
||||
#"JSON"[data]
|
||||
@@ -0,0 +1,46 @@
|
||||
let
|
||||
Table.GenerateByPage = Extension.LoadFunction("Table.GenerateByPage.pqm"),
|
||||
Speckle.Api.GetObjectChildren = Extension.LoadFunction("Api.GetObjectChildren.pqm"),
|
||||
Speckle.Api.GetObject = Extension.LoadFunction("Api.GetObject.pqm"),
|
||||
Extension.LoadFunction = (fileName as text) =>
|
||||
let
|
||||
binary = Extension.Contents(fileName), asText = Text.FromBinary(binary)
|
||||
in
|
||||
try
|
||||
Expression.Evaluate(asText, #shared) catch (e) =>
|
||||
error
|
||||
[
|
||||
Reason = "Extension.LoadFunction Failure",
|
||||
Message.Format = "Loading '#{0}' failed - '#{1}': '#{2}'",
|
||||
Message.Parameters = {fileName, e[Reason], e[Message]},
|
||||
Detail = [File = fileName, Error = e]
|
||||
]
|
||||
in
|
||||
// Read all pages of data.
|
||||
// After every page, we check the "nextCursor" record on the metadata of the previous request.
|
||||
// Table.GenerateByPage will keep asking for more pages until we return null.
|
||||
(server as text, streamId as text, objectId as text, optional cursor as text) as table =>
|
||||
let
|
||||
parentObject = Speckle.Api.GetObject(server, streamId, objectId),
|
||||
childrenTable = Table.GenerateByPage(
|
||||
(previous) =>
|
||||
let
|
||||
// if previous is null, then this is our first page of data
|
||||
nextCursor = if (previous = null) then cursor else Value.Metadata(previous)[Cursor]?,
|
||||
// if the cursor is null but the prevous page is not, we've reached the end
|
||||
page =
|
||||
if (previous <> null and nextCursor = null) then
|
||||
null
|
||||
else
|
||||
Speckle.Api.GetObjectChildren(server, streamId, objectId, 1000, nextCursor)
|
||||
in
|
||||
page
|
||||
),
|
||||
parentTable = Table.FromRecords({[data = parentObject]}),
|
||||
resultTable =
|
||||
if (Table.ColumnCount(childrenTable) = 0) then
|
||||
parentTable
|
||||
else
|
||||
Table.Combine({parentTable, childrenTable})
|
||||
in
|
||||
resultTable meta [server = server, streamId = streamId, objectId = objectId]
|
||||
@@ -0,0 +1,32 @@
|
||||
let
|
||||
Speckle.Api.Fetch = Extension.LoadFunction("Api.Fetch.pqm"),
|
||||
Extension.LoadFunction = (fileName as text) =>
|
||||
let
|
||||
binary = Extension.Contents(fileName), asText = Text.FromBinary(binary)
|
||||
in
|
||||
try
|
||||
Expression.Evaluate(asText, #shared) catch (e) =>
|
||||
error
|
||||
[
|
||||
Reason = "Extension.LoadFunction Failure",
|
||||
Message.Format = "Loading '#{0}' failed - '#{1}': '#{2}'",
|
||||
Message.Parameters = {fileName, e[Reason], e[Message]},
|
||||
Detail = [File = fileName, Error = e]
|
||||
]
|
||||
in
|
||||
(server as text, projectId as text, modelId as text) =>
|
||||
let
|
||||
query = "query Project($projectId: String!, $modelId: String!) {
|
||||
project(id: $projectId) {
|
||||
model(id: $modelId) {
|
||||
name
|
||||
}
|
||||
}
|
||||
}",
|
||||
variables = [
|
||||
projectId = projectId,
|
||||
modelId = modelId
|
||||
]
|
||||
in
|
||||
// Read receipts should fail gracefully no matter what
|
||||
try Speckle.Api.Fetch(server, query, variables)[project][model] otherwise null
|
||||
@@ -0,0 +1,28 @@
|
||||
let
|
||||
Speckle.Api.Fetch = Extension.LoadFunction("Api.Fetch.pqm"),
|
||||
Extension.LoadFunction = (fileName as text) =>
|
||||
let
|
||||
binary = Extension.Contents(fileName), asText = Text.FromBinary(binary)
|
||||
in
|
||||
try
|
||||
Expression.Evaluate(asText, #shared) catch (e) =>
|
||||
error
|
||||
[
|
||||
Reason = "Extension.LoadFunction Failure",
|
||||
Message.Format = "Loading '#{0}' failed - '#{1}': '#{2}'",
|
||||
Message.Parameters = {fileName, e[Reason], e[Message]},
|
||||
Detail = [File = fileName, Error = e]
|
||||
]
|
||||
in
|
||||
(server as text, streamId as text, objectId as text) =>
|
||||
let
|
||||
query = "query($streamId: String!, $objectId: String!) {
|
||||
stream( id: $streamId ) {
|
||||
object (id: $objectId) {
|
||||
data
|
||||
}
|
||||
}
|
||||
}",
|
||||
#"JSON" = Speckle.Api.Fetch(server, query, [streamId = streamId, objectId = objectId])
|
||||
in
|
||||
#"JSON"[stream][object][data]
|
||||
@@ -0,0 +1,54 @@
|
||||
let
|
||||
Speckle.Api.Fetch = Extension.LoadFunction("Api.Fetch.pqm"),
|
||||
Speckle.CleanUpObjects = Extension.LoadFunction("CleanUpObjects.pqm"),
|
||||
Extension.LoadFunction = (fileName as text) =>
|
||||
let
|
||||
binary = Extension.Contents(fileName), asText = Text.FromBinary(binary)
|
||||
in
|
||||
try
|
||||
Expression.Evaluate(asText, #shared) catch (e) =>
|
||||
error
|
||||
[
|
||||
Reason = "Extension.LoadFunction Failure",
|
||||
Message.Format = "Loading '#{0}' failed - '#{1}': '#{2}'",
|
||||
Message.Parameters = {fileName, e[Reason], e[Message]},
|
||||
Detail = [File = fileName, Error = e]
|
||||
]
|
||||
in
|
||||
(
|
||||
server as text,
|
||||
streamId as text,
|
||||
objectId as text,
|
||||
optional limit as number,
|
||||
optional cursor as text,
|
||||
optional select as list
|
||||
) =>
|
||||
let
|
||||
query = "query($streamId: String!, $objectId: String!, $limit: Int, $cursor: String, $select: [String]) {
|
||||
stream( id: $streamId ) {
|
||||
object (id: $objectId) {
|
||||
children(select: $select, limit: $limit, cursor: $cursor) {
|
||||
cursor
|
||||
objects {
|
||||
data
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}",
|
||||
#"JSON" = Speckle.Api.Fetch(
|
||||
server,
|
||||
query,
|
||||
[
|
||||
streamId = streamId,
|
||||
objectId = objectId,
|
||||
limit = limit,
|
||||
cursor = cursor,
|
||||
select = select
|
||||
]
|
||||
),
|
||||
children = #"JSON"[stream][object][children],
|
||||
nextCursor = children[cursor],
|
||||
clean = Speckle.CleanUpObjects(children[objects])
|
||||
in
|
||||
Table.FromRecords(clean) meta [Cursor = nextCursor]
|
||||
@@ -0,0 +1,27 @@
|
||||
(url as text) =>
|
||||
let
|
||||
userType = type [name = text, email = text, id = text],
|
||||
query = "query {
|
||||
activeUser { name email id }
|
||||
}",
|
||||
// Imports
|
||||
Speckle.Api.Fetch = Extension.LoadFunction("Api.Fetch.pqm"),
|
||||
ParseUrl = Extension.LoadFunction("ParseStreamUrl.pqm"),
|
||||
urlObject = ParseUrl(url),
|
||||
Extension.LoadFunction = (fileName as text) =>
|
||||
let
|
||||
binary = Extension.Contents(fileName), asText = Text.FromBinary(binary)
|
||||
in
|
||||
try
|
||||
Expression.Evaluate(asText, #shared) catch (e) =>
|
||||
error
|
||||
[
|
||||
Reason = "Extension.LoadFunction Failure",
|
||||
Message.Format = "Loading '#{0}' failed - '#{1}': '#{2}'",
|
||||
Message.Parameters = {fileName, e[Reason], e[Message]},
|
||||
Detail = [File = fileName, Error = e]
|
||||
],
|
||||
user = Speckle.Api.Fetch(urlObject[server], query)[activeUser]
|
||||
in
|
||||
// Read receipts should fail gracefully no matter what
|
||||
Value.ReplaceType(user, userType)
|
||||
@@ -0,0 +1,38 @@
|
||||
(server as text, optional streamId as text, optional objectId as text) as table =>
|
||||
let
|
||||
apiKey = try Extension.CurrentCredential()[Key] otherwise null,
|
||||
Source = Web.Contents(
|
||||
Text.Combine({server, "objects", streamId, objectId}, "/"),
|
||||
[
|
||||
Headers = [
|
||||
#"Method" = "GET",
|
||||
#"Content-Type" = "application/json",
|
||||
#"Authorization" = if apiKey = null then "" else Text.Format("Bearer #{0}", {apiKey})
|
||||
],
|
||||
ManualStatusHandling = {400}
|
||||
]
|
||||
),
|
||||
json = Json.Document(Source),
|
||||
clean = List.Select(json, each _[speckle_type] <> "Speckle.Core.Models.DataChunk"),
|
||||
t = Table.FromColumns({clean}, {"data"}),
|
||||
addStreamUrl = Table.AddColumn(t, "Stream URL", each server & "/streams/" & streamId),
|
||||
addObjectIdCol = Table.AddColumn(addStreamUrl, "Object ID", each try _[data][id] otherwise null),
|
||||
addSpeckleTypeCol = Table.AddColumn(
|
||||
addObjectIdCol, "speckle_type", each try _[data][speckle_type] otherwise null
|
||||
),
|
||||
Speckle.CleanUpObjects = Extension.LoadFunction("CleanUpObjects.pqm"),
|
||||
Extension.LoadFunction = (fileName as text) =>
|
||||
let
|
||||
binary = Extension.Contents(fileName), asText = Text.FromBinary(binary)
|
||||
in
|
||||
try
|
||||
Expression.Evaluate(asText, #shared) catch (e) =>
|
||||
error
|
||||
[
|
||||
Reason = "Extension.LoadFunction Failure",
|
||||
Message.Format = "Loading '#{0}' failed - '#{1}': '#{2}'",
|
||||
Message.Parameters = {fileName, e[Reason], e[Message]},
|
||||
Detail = [File = fileName, Error = e]
|
||||
]
|
||||
in
|
||||
addSpeckleTypeCol
|
||||
@@ -0,0 +1,7 @@
|
||||
(object as record) as record =>
|
||||
let
|
||||
hiddenFields = {"__closure", "totalChildrenCount"},
|
||||
// remove closures from records
|
||||
clean = Record.RemoveFields(object, hiddenFields, MissingField.Ignore)
|
||||
in
|
||||
clean
|
||||
@@ -0,0 +1,17 @@
|
||||
(objects as list) as list =>
|
||||
let
|
||||
// remove closures from records, and remove DataChunk records
|
||||
removeClosureField = List.Transform(
|
||||
objects, each [data = Record.RemoveFields(_[data], "__closure", MissingField.Ignore)]
|
||||
),
|
||||
removeTotals = List.Transform(
|
||||
removeClosureField,
|
||||
each
|
||||
[
|
||||
data = try
|
||||
Record.RemoveFields(_[data], "totalChildrenCount", MissingField.Ignore) otherwise _[data]
|
||||
]
|
||||
),
|
||||
removed = List.Select(removeTotals, each _[data][speckle_type] <> "Speckle.Core.Models.DataChunk")
|
||||
in
|
||||
try removed otherwise objects
|
||||
@@ -0,0 +1,30 @@
|
||||
let
|
||||
beta = true,
|
||||
category = "Other",
|
||||
icons = [
|
||||
Icon16 = {
|
||||
Extension.Contents("SpeckleLogo16.png"),
|
||||
Extension.Contents("SpeckleLogo20.png"),
|
||||
Extension.Contents("SpeckleLogo24.png"),
|
||||
Extension.Contents("SpeckleLogo32.png")
|
||||
},
|
||||
Icon32 = {
|
||||
Extension.Contents("SpeckleLogo32.png"),
|
||||
Extension.Contents("SpeckleLogo40.png"),
|
||||
Extension.Contents("SpeckleLogo48.png"),
|
||||
Extension.Contents("SpeckleLogo64.png")
|
||||
}
|
||||
]
|
||||
in
|
||||
(key as text) as record =>
|
||||
[
|
||||
Beta = beta,
|
||||
Category = category,
|
||||
ButtonText = {
|
||||
Extension.LoadString(Text.Format("#{0}.Title", {key})),
|
||||
Extension.LoadString(Text.Format("#{0}.Label", {key}))
|
||||
},
|
||||
LearnMoreUrl = "https://speckle.guide",
|
||||
SourceImage = icons,
|
||||
SourceTypeImage = icons
|
||||
]
|
||||
@@ -0,0 +1,50 @@
|
||||
let
|
||||
GetApplicationSlug = Extension.LoadFunction("GetApplicationSlug.pqm"),
|
||||
GetUser = Extension.LoadFunction("Api.GetUser.pqm"),
|
||||
Hash = Extension.LoadFunction("Hash.pqm"),
|
||||
Extension.LoadFunction = (fileName as text) =>
|
||||
let
|
||||
binary = Extension.Contents(fileName), asText = Text.FromBinary(binary)
|
||||
in
|
||||
try
|
||||
Expression.Evaluate(asText, #shared) catch (e) =>
|
||||
error
|
||||
[
|
||||
Reason = "Extension.LoadFunction Failure",
|
||||
Message.Format = "Loading '#{0}' failed - '#{1}': '#{2}'",
|
||||
Message.Parameters = {fileName, e[Reason], e[Message]},
|
||||
Detail = [File = fileName, Error = e]
|
||||
]
|
||||
in
|
||||
(server as text, commit as any) =>
|
||||
let
|
||||
trackUrl = "https://analytics.speckle.systems/track?ip=1",
|
||||
user = GetUser(server),
|
||||
isMultiplayer = user[id] <> commit[authorId],
|
||||
body = [
|
||||
event = "Receive",
|
||||
properties = [
|
||||
server_id = Hash(server),
|
||||
token = "acd87c5a50b56df91a795e999812a3a4",
|
||||
hostApp = "powerbi",
|
||||
sourceHostApp = GetApplicationSlug(commit[sourceApplication]),
|
||||
sourceHostAppVersion = commit[sourceApplication],
|
||||
isMultiplayer = user[id] <> commit[authorId]
|
||||
]
|
||||
],
|
||||
Result = Web.Contents(
|
||||
trackUrl,
|
||||
[
|
||||
Headers = [
|
||||
#"Method" = "POST",
|
||||
#"Accept" = "text/plain",
|
||||
#"Content-Type" = "application/json"
|
||||
],
|
||||
Content = Text.ToBinary(Text.Combine({"data=", Text.FromBinary(Json.FromValue(body))}))
|
||||
]
|
||||
),
|
||||
// Hack to force execution
|
||||
Join = Text.Combine({server, Text.From(Json.Document(Result))}, "_____"),
|
||||
Disjoin = Text.Split(Join, "_____"){0}
|
||||
in
|
||||
Disjoin
|
||||
@@ -0,0 +1,81 @@
|
||||
let
|
||||
GetModel = Extension.LoadFunction("Api.GetModel.pqm"),
|
||||
Extension.LoadFunction = (fileName as text) =>
|
||||
let
|
||||
binary = Extension.Contents(fileName), asText = Text.FromBinary(binary)
|
||||
in
|
||||
try
|
||||
Expression.Evaluate(asText, #shared) catch (e) =>
|
||||
error
|
||||
[
|
||||
Reason = "Extension.LoadFunction Failure",
|
||||
Message.Format = "Loading '#{0}' failed - '#{1}': '#{2}'",
|
||||
Message.Parameters = {fileName, e[Reason], e[Message]},
|
||||
Detail = [File = fileName, Error = e]
|
||||
],
|
||||
IsFe2Url = (segments as list) as logical => List.Count(segments) = 4 and segments{2} = "models",
|
||||
GetUrlType = (branchName as nullable text, commitId as nullable text, objectId as nullable text) as text =>
|
||||
if (commitId <> null) then
|
||||
"Commit"
|
||||
else if (objectId <> null) then
|
||||
"Object"
|
||||
else if (branchName <> null) then
|
||||
"Branch"
|
||||
else
|
||||
"Stream",
|
||||
ParseFe1Url = (server as text, segments as list) as record =>
|
||||
let
|
||||
streamId = segments{1},
|
||||
branchName = if (List.Count(segments) = 4 and segments{2} = "branches") then segments{3} else null,
|
||||
commitId = if (List.Count(segments) = 4 and segments{2} = "commits") then segments{3} else null,
|
||||
objectId = if (List.Count(segments) = 4 and segments{2} = "objects") then segments{3} else null,
|
||||
urlType = GetUrlType(branchName, commitId, objectId)
|
||||
in
|
||||
[
|
||||
urlType = urlType,
|
||||
server = server as text,
|
||||
id = streamId as nullable text,
|
||||
branch = branchName as nullable text,
|
||||
commit = commitId as nullable text,
|
||||
object = objectId as nullable text
|
||||
],
|
||||
ParseFe2Url = (server as text, segments as list) as record =>
|
||||
let
|
||||
streamId = segments{1},
|
||||
modelList = segments{3},
|
||||
isMultimodel = Text.Contains(modelList, ","),
|
||||
firstModel = Text.Split(modelList, ","){0},
|
||||
modelAndVersion = Text.Split(firstModel, "@"),
|
||||
modelId = modelAndVersion{0},
|
||||
versionId = if (List.Count(modelAndVersion) > 1) then modelAndVersion{1} else null,
|
||||
model = if (modelId <> null) then GetModel(server, streamId, modelId) else null,
|
||||
urlType = GetUrlType(model[name], versionId, null)
|
||||
in
|
||||
if isMultimodel then
|
||||
error
|
||||
Error.Record(
|
||||
"NotSupported",
|
||||
"Multi-model URLs are not supported.",
|
||||
"Try to select just one single model in the web app and paste that in."
|
||||
)
|
||||
else
|
||||
[
|
||||
urlType = urlType,
|
||||
server = server,
|
||||
id = streamId,
|
||||
branch = model[name],
|
||||
commit = versionId,
|
||||
object = null
|
||||
]
|
||||
in
|
||||
(url as text) as record =>
|
||||
let
|
||||
// Get server and streamId, and branchName / commitId / objectid from the input url
|
||||
server = Text.Combine({Uri.Parts(url)[Scheme], "://", Uri.Parts(url)[Host]}),
|
||||
segments = Text.Split(Text.AfterDelimiter(Uri.Parts(url)[Path], "/", 0), "/"),
|
||||
isFe2 = IsFe2Url(segments)
|
||||
in
|
||||
if (isFe2) then
|
||||
ParseFe2Url(server, segments)
|
||||
else
|
||||
ParseFe1Url(server, segments)
|
||||
@@ -0,0 +1,67 @@
|
||||
let
|
||||
GetObject = Extension.LoadFunction("Api.GetObject.pqm"),
|
||||
Diagnostics.Log = Extension.LoadFunction("Diagnostics.pqm")[LogValue],
|
||||
Extension.LoadFunction = (fileName as text) =>
|
||||
let
|
||||
binary = Extension.Contents(fileName), asText = Text.FromBinary(binary)
|
||||
in
|
||||
try
|
||||
Expression.Evaluate(asText, #shared) catch (e) =>
|
||||
error
|
||||
[
|
||||
Reason = "Extension.LoadFunction Failure",
|
||||
Message.Format = "Loading '#{0}' failed - '#{1}': '#{2}'",
|
||||
Message.Parameters = {fileName, e[Reason], e[Message]},
|
||||
Detail = [File = fileName, Error = e]
|
||||
],
|
||||
//TODO: Not implemented yet
|
||||
TraverseTable = (item as table) as table => item,
|
||||
// Will traverse an undetermined value (list, table, record).
|
||||
TraverseValue = (i as any) as any =>
|
||||
let
|
||||
item = Diagnostics.Log("Traverse value", i) meta Value.Metadata(i)
|
||||
in
|
||||
if Value.Is(item, type list) then
|
||||
// Return a transformed list by traversing all items
|
||||
Diagnostics.Log(
|
||||
"List travered",
|
||||
List.Transform(item, (a) => @TraverseValue(Value.ReplaceMetadata(a, Value.Metadata(i))))
|
||||
)
|
||||
else if Value.Is(item, type record) then
|
||||
// Traverse this record individually
|
||||
TraverseRecord(item)
|
||||
else if Value.Is(item, type table) then
|
||||
// Traverse this table
|
||||
TraverseTable(item)
|
||||
else
|
||||
// If none of the above, assume it's just a primitive type and return it as-is.
|
||||
item,
|
||||
// Traverses a generic record
|
||||
TraverseRecord = (object as record) as any =>
|
||||
let
|
||||
isSpeckle = Diagnostics.Log("Is Speckle", Record.HasFields(object, {"speckle_type"})),
|
||||
isReference = Diagnostics.Log("Is Reference", object[speckle_type] = "reference"),
|
||||
// Get the names of all fields
|
||||
fields = Record.FieldNames(object),
|
||||
// Remove all known fields that don't need traversing
|
||||
cleanFields = List.RemoveItems(fields, {"id", "speckle_type", "applicationId"}),
|
||||
// Transform the list of field names into a set of transform operations
|
||||
transformOps = List.Transform(
|
||||
cleanFields, each {_, (a) => TraverseValue(Value.ReplaceMetadata(a, Value.Metadata(object)))}
|
||||
),
|
||||
// Get the object's metadata (server and stream will be saved in here)
|
||||
info = Value.Metadata(object)
|
||||
in
|
||||
// Transform all fields and return the modified object
|
||||
if (isReference) then
|
||||
// Swap reference for call to GetObject
|
||||
() =>
|
||||
TraverseValue(
|
||||
Value.ReplaceMetadata(
|
||||
GetObject(info[server], info[stream], object[referencedId]), Value.Metadata(object)
|
||||
)
|
||||
)
|
||||
else
|
||||
try Record.TransformFields(object, transformOps, MissingField.Error) otherwise error "oopsies"
|
||||
in
|
||||
TraverseValue
|
||||
@@ -0,0 +1,2 @@
|
||||
// Use this file to write queries to test your data connector
|
||||
let result = Speckle.Api.Fetch("https://latest.speckle.dev") in Record.ToTable(result)
|
||||
@@ -0,0 +1,7 @@
|
||||
// Use this file to write queries to test your data connector
|
||||
let
|
||||
result = Speckle.Api.REST.GetObject(
|
||||
"https://latest.speckle.dev", "5f284e5c70", "85e5f250fe591ea74d8d5dc1137a9341"
|
||||
)
|
||||
in
|
||||
result
|
||||
@@ -0,0 +1,30 @@
|
||||
section UnitTestingUnitTests;
|
||||
|
||||
UT = Speckle.LoadFunction("Facts.pqm");
|
||||
Fact = UT[Fact];
|
||||
|
||||
Facts.Summarize = UT[SummarizeFacts];
|
||||
|
||||
shared Speckle.UnitTest = [
|
||||
// Put any common variables here if you only want them to be evaluated once
|
||||
// Fact(<Name of the Test>, <Expected Value>, <Actual Value>)
|
||||
// <Expected Value> and <Actual Value> can be a literal or let statement
|
||||
facts = {
|
||||
Fact(
|
||||
"Check that this function returns 'ABC'",
|
||||
// name of the test
|
||||
"ABC",
|
||||
// expected value
|
||||
UnitTesting.ReturnsABC()
|
||||
// expression to evaluate (let or single statement)
|
||||
),
|
||||
Fact("Check that this function returns '123'", "123", UnitTesting.Returns123()),
|
||||
Fact("Result should contain 5 rows", 5, Table.RowCount(UnitTesting.ReturnsTableWithFiveRows())),
|
||||
Fact("Values should be equal (using a let statement)", "Hello World", let a = "Hello World" in a)
|
||||
},
|
||||
report = Facts.Summarize(facts)
|
||||
][report];
|
||||
|
||||
shared UnitTesting.ReturnsABC = () => "ABC";
|
||||
shared UnitTesting.Returns123 = () => "123";
|
||||
shared UnitTesting.ReturnsTableWithFiveRows = () => Table.Repeat(#table({"a"}, {{1}}), 5);
|
||||
@@ -0,0 +1,2 @@
|
||||
// Use this file to write queries to test your data connector
|
||||
let result = Speckle.Get.ByUrl("https://latest.speckle.dev/streams/3d25474a18") in Record.ToTable(result)
|
||||
@@ -0,0 +1,2 @@
|
||||
// Use this file to write queries to test your data connector
|
||||
let result = Speckle.GetByUrl("https://latest.speckle.dev/streams/5f284e5c70/objects/85e5f250fe591ea74d8d5dc1137a9341") in result
|
||||
@@ -0,0 +1,364 @@
|
||||
; *** Inno Setup version 6.0.3+ English messages ***
|
||||
;
|
||||
; To download user-contributed translations of this file, go to:
|
||||
; https://jrsoftware.org/files/istrans/
|
||||
;
|
||||
; Note: When translating this text, do not add periods (.) to the end of
|
||||
; messages that didn't have them already, because on those messages Inno
|
||||
; Setup adds the periods automatically (appending a period would result in
|
||||
; two periods being displayed).
|
||||
|
||||
[LangOptions]
|
||||
; The following three entries are very important. Be sure to read and
|
||||
; understand the '[LangOptions] section' topic in the help file.
|
||||
LanguageName=English
|
||||
LanguageID=$0409
|
||||
LanguageCodePage=0
|
||||
; If the language you are translating to requires special font faces or
|
||||
; sizes, uncomment any of the following entries and change them accordingly.
|
||||
;DialogFontName=
|
||||
;DialogFontSize=8
|
||||
;WelcomeFontName=Verdana
|
||||
;WelcomeFontSize=12
|
||||
;TitleFontName=Arial
|
||||
;TitleFontSize=29
|
||||
;CopyrightFontName=Arial
|
||||
;CopyrightFontSize=8
|
||||
|
||||
[Messages]
|
||||
|
||||
; *** Application titles
|
||||
SetupAppTitle=Setup
|
||||
SetupWindowTitle=Setup - %1
|
||||
UninstallAppTitle=Uninstall
|
||||
UninstallAppFullTitle=%1 Uninstall
|
||||
|
||||
; *** Misc. common
|
||||
InformationTitle=Information
|
||||
ConfirmTitle=Confirm
|
||||
ErrorTitle=Error
|
||||
|
||||
; *** SetupLdr messages
|
||||
SetupLdrStartupMessage=This will install %1. Do you wish to continue?
|
||||
LdrCannotCreateTemp=Unable to create a temporary file. Setup aborted
|
||||
LdrCannotExecTemp=Unable to execute file in the temporary directory. Setup aborted
|
||||
HelpTextNote=
|
||||
|
||||
; *** Startup error messages
|
||||
LastErrorMessage=%1.%n%nError %2: %3
|
||||
SetupFileMissing=The file %1 is missing from the installation directory. Please correct the problem or obtain a new copy of the program.
|
||||
SetupFileCorrupt=The setup files are corrupted. Please obtain a new copy of the program.
|
||||
SetupFileCorruptOrWrongVer=The setup files are corrupted, or are incompatible with this version of Setup. Please correct the problem or obtain a new copy of the program.
|
||||
InvalidParameter=An invalid parameter was passed on the command line:%n%n%1
|
||||
SetupAlreadyRunning=Setup is already running.
|
||||
WindowsVersionNotSupported=This program does not support the version of Windows your computer is running.
|
||||
WindowsServicePackRequired=This program requires %1 Service Pack %2 or later.
|
||||
NotOnThisPlatform=This program will not run on %1.
|
||||
OnlyOnThisPlatform=This program must be run on %1.
|
||||
OnlyOnTheseArchitectures=This program can only be installed on versions of Windows designed for the following processor architectures:%n%n%1
|
||||
WinVersionTooLowError=This program requires %1 version %2 or later.
|
||||
WinVersionTooHighError=This program cannot be installed on %1 version %2 or later.
|
||||
AdminPrivilegesRequired=You must be logged in as an administrator when installing this program.
|
||||
PowerUserPrivilegesRequired=You must be logged in as an administrator or as a member of the Power Users group when installing this program.
|
||||
SetupAppRunningError=Setup has detected that %1 is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit.
|
||||
UninstallAppRunningError=Uninstall has detected that %1 is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit.
|
||||
|
||||
; *** Startup questions
|
||||
PrivilegesRequiredOverrideTitle=Select Setup Install Mode
|
||||
PrivilegesRequiredOverrideInstruction=Select install mode
|
||||
PrivilegesRequiredOverrideText1=%1 can be installed for all users (requires administrative privileges), or for you only.
|
||||
PrivilegesRequiredOverrideText2=%1 can be installed for you only, or for all users (requires administrative privileges).
|
||||
PrivilegesRequiredOverrideAllUsers=Install for &all users
|
||||
PrivilegesRequiredOverrideAllUsersRecommended=Install for &all users (recommended)
|
||||
PrivilegesRequiredOverrideCurrentUser=Install for &me only
|
||||
PrivilegesRequiredOverrideCurrentUserRecommended=Install for &me only (recommended)
|
||||
|
||||
; *** Misc. errors
|
||||
ErrorCreatingDir=Setup was unable to create the directory "%1"
|
||||
ErrorTooManyFilesInDir=Unable to create a file in the directory "%1" because it contains too many files
|
||||
|
||||
; *** Setup common messages
|
||||
ExitSetupTitle=Exit Setup
|
||||
ExitSetupMessage=Setup is not complete. If you exit now, the program will not be installed.%n%nYou may run Setup again at another time to complete the installation.%n%nExit Setup?
|
||||
AboutSetupMenuItem=&About Setup...
|
||||
AboutSetupTitle=About Setup
|
||||
AboutSetupMessage=%1 version %2%n%3%n%n%1 home page:%n%4
|
||||
AboutSetupNote=
|
||||
TranslatorNote=
|
||||
|
||||
; *** Buttons
|
||||
ButtonBack=< &Back
|
||||
ButtonNext=&Next >
|
||||
ButtonInstall=&Install
|
||||
ButtonOK=OK
|
||||
ButtonCancel=Cancel
|
||||
ButtonYes=&Yes
|
||||
ButtonYesToAll=Yes to &All
|
||||
ButtonNo=&No
|
||||
ButtonNoToAll=N&o to All
|
||||
ButtonFinish=&Finish
|
||||
ButtonBrowse=&Browse...
|
||||
ButtonWizardBrowse=B&rowse...
|
||||
ButtonNewFolder=&Make New Folder
|
||||
|
||||
; *** "Select Language" dialog messages
|
||||
SelectLanguageTitle=Select Setup Language
|
||||
SelectLanguageLabel=Select the language to use during the installation.
|
||||
|
||||
; *** Common wizard text
|
||||
ClickNext=Click Next to continue, or Cancel to exit Setup.
|
||||
BeveledLabel=
|
||||
BrowseDialogTitle=Browse For Folder
|
||||
BrowseDialogLabel=Select a folder in the list below, then click OK.
|
||||
NewFolderName=New Folder
|
||||
|
||||
; *** "Welcome" wizard page
|
||||
WelcomeLabel1=Welcome to the [name] Setup Wizard
|
||||
WelcomeLabel2=This will install [name/ver] on your computer.%n%nIt is recommended that you close all other applications before continuing.
|
||||
|
||||
; *** "Password" wizard page
|
||||
WizardPassword=Password
|
||||
PasswordLabel1=This installation is password protected.
|
||||
PasswordLabel3=Please provide the password, then click Next to continue. Passwords are case-sensitive.
|
||||
PasswordEditLabel=&Password:
|
||||
IncorrectPassword=The password you entered is not correct. Please try again.
|
||||
|
||||
; *** "License Agreement" wizard page
|
||||
WizardLicense=License Agreement
|
||||
LicenseLabel=Please read the following important information before continuing.
|
||||
LicenseLabel3=Please read the following License Agreement. You must accept the terms of this agreement before continuing with the installation.
|
||||
LicenseAccepted=I &accept the agreement
|
||||
LicenseNotAccepted=I &do not accept the agreement
|
||||
|
||||
; *** "Information" wizard pages
|
||||
WizardInfoBefore=Information
|
||||
InfoBeforeLabel=Please read the following important information before continuing.
|
||||
InfoBeforeClickLabel=When you are ready to continue with Setup, click Next.
|
||||
WizardInfoAfter=Information
|
||||
InfoAfterLabel=Please read the following important information before continuing.
|
||||
InfoAfterClickLabel=When you are ready to continue with Setup, click Next.
|
||||
|
||||
; *** "User Information" wizard page
|
||||
WizardUserInfo=User Information
|
||||
UserInfoDesc=Please enter your information.
|
||||
UserInfoName=&User Name:
|
||||
UserInfoOrg=&Organization:
|
||||
UserInfoSerial=&Serial Number:
|
||||
UserInfoNameRequired=You must enter a name.
|
||||
|
||||
; *** "Select Destination Location" wizard page
|
||||
WizardSelectDir=Select Destination Location
|
||||
SelectDirDesc=Where should [name] be installed?
|
||||
SelectDirLabel3=Setup will install [name] into the following folder.
|
||||
SelectDirBrowseLabel=To continue, click Next. If you would like to select a different folder, click Browse.
|
||||
DiskSpaceGBLabel=At least [gb] GB of free disk space is required.
|
||||
DiskSpaceMBLabel=At least [mb] MB of free disk space is required.
|
||||
CannotInstallToNetworkDrive=Setup cannot install to a network drive.
|
||||
CannotInstallToUNCPath=Setup cannot install to a UNC path.
|
||||
InvalidPath=You must enter a full path with drive letter; for example:%n%nC:\APP%n%nor a UNC path in the form:%n%n\\server\share
|
||||
InvalidDrive=The drive or UNC share you selected does not exist or is not accessible. Please select another.
|
||||
DiskSpaceWarningTitle=Not Enough Disk Space
|
||||
DiskSpaceWarning=Setup requires at least %1 KB of free space to install, but the selected drive only has %2 KB available.%n%nDo you want to continue anyway?
|
||||
DirNameTooLong=The folder name or path is too long.
|
||||
InvalidDirName=The folder name is not valid.
|
||||
BadDirName32=Folder names cannot include any of the following characters:%n%n%1
|
||||
DirExistsTitle=Folder Exists
|
||||
DirExists=The folder:%n%n%1%n%nalready exists. Would you like to install to that folder anyway?
|
||||
DirDoesntExistTitle=Folder Does Not Exist
|
||||
DirDoesntExist=The folder:%n%n%1%n%ndoes not exist. Would you like the folder to be created?
|
||||
|
||||
; *** "Select Components" wizard page
|
||||
WizardSelectComponents=Select Components
|
||||
SelectComponentsDesc=Which components should be installed?
|
||||
SelectComponentsLabel2=Select the components you want to install; clear the components you do not want to install. Click Next when you are ready to continue.
|
||||
FullInstallation=Full installation
|
||||
; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language)
|
||||
CompactInstallation=Compact installation
|
||||
CustomInstallation=Custom installation
|
||||
NoUninstallWarningTitle=Components Exist
|
||||
NoUninstallWarning=Setup has detected that the following components are already installed on your computer:%n%n%1%n%nDeselecting these components will not uninstall them.%n%nWould you like to continue anyway?
|
||||
ComponentSize1=%1 KB
|
||||
ComponentSize2=%1 MB
|
||||
ComponentsDiskSpaceGBLabel=Current selection requires at least [gb] GB of disk space.
|
||||
ComponentsDiskSpaceMBLabel=Current selection requires at least [mb] MB of disk space.
|
||||
|
||||
; *** "Select Additional Tasks" wizard page
|
||||
WizardSelectTasks=Select Additional Tasks
|
||||
SelectTasksDesc=Which additional tasks should be performed?
|
||||
SelectTasksLabel2=Select the additional tasks you would like Setup to perform while installing [name], then click Next.
|
||||
|
||||
; *** "Select Start Menu Folder" wizard page
|
||||
WizardSelectProgramGroup=Select Start Menu Folder
|
||||
SelectStartMenuFolderDesc=Where should Setup place the program's shortcuts?
|
||||
SelectStartMenuFolderLabel3=Setup will create the program's shortcuts in the following Start Menu folder.
|
||||
SelectStartMenuFolderBrowseLabel=To continue, click Next. If you would like to select a different folder, click Browse.
|
||||
MustEnterGroupName=You must enter a folder name.
|
||||
GroupNameTooLong=The folder name or path is too long.
|
||||
InvalidGroupName=The folder name is not valid.
|
||||
BadGroupName=The folder name cannot include any of the following characters:%n%n%1
|
||||
NoProgramGroupCheck2=&Don't create a Start Menu folder
|
||||
|
||||
; *** "Ready to Install" wizard page
|
||||
WizardReady=Ready to Install
|
||||
ReadyLabel1=Setup is now ready to begin installing [name] on your computer.
|
||||
ReadyLabel2a=Click Install to continue with the installation, or click Back if you want to review or change any settings.
|
||||
ReadyLabel2b=Click Install to continue with the installation.
|
||||
ReadyMemoUserInfo=User information:
|
||||
ReadyMemoDir=Destination location:
|
||||
ReadyMemoType=Setup type:
|
||||
ReadyMemoComponents=Selected components:
|
||||
ReadyMemoGroup=Start Menu folder:
|
||||
ReadyMemoTasks=Additional tasks:
|
||||
|
||||
; *** "Preparing to Install" wizard page
|
||||
WizardPreparing=Preparing to Install
|
||||
PreparingDesc=Setup is preparing to install [name] on your computer.
|
||||
PreviousInstallNotCompleted=The installation/removal of a previous program was not completed. You will need to restart your computer to complete that installation.%n%nAfter restarting your computer, run Setup again to complete the installation of [name].
|
||||
CannotContinue=Setup cannot continue. Please click Cancel to exit.
|
||||
ApplicationsFound=The following applications are using files that need to be updated by Setup. It is recommended that you allow Setup to automatically close these applications.
|
||||
ApplicationsFound2=The following applications are using files that need to be updated by Setup. It is recommended that you allow Setup to automatically close these applications. After the installation has completed, Setup will attempt to restart the applications.
|
||||
CloseApplications=&Automatically close the applications
|
||||
DontCloseApplications=&Do not close the applications
|
||||
ErrorCloseApplications=Setup was unable to automatically close all applications. It is recommended that you close all applications using files that need to be updated by Setup before continuing.
|
||||
PrepareToInstallNeedsRestart=Setup must restart your computer. After restarting your computer, run Setup again to complete the installation of [name].%n%nWould you like to restart now?
|
||||
|
||||
; *** "Installing" wizard page
|
||||
WizardInstalling=Installing
|
||||
InstallingLabel=Please wait while Setup installs [name] on your computer.
|
||||
|
||||
; *** "Setup Completed" wizard page
|
||||
FinishedHeadingLabel=Completing the [name] Setup Wizard
|
||||
FinishedLabelNoIcons=Setup has finished installing [name] on your computer.
|
||||
FinishedLabel=Setup has finished installing [name] on your computer. The application may be launched by selecting the installed shortcuts.
|
||||
ClickFinish=Click Finish to exit Setup.
|
||||
FinishedRestartLabel=To complete the installation of [name], Setup must restart your computer. Would you like to restart now?
|
||||
FinishedRestartMessage=To complete the installation of [name], Setup must restart your computer.%n%nWould you like to restart now?
|
||||
ShowReadmeCheck=Yes, I would like to view the README file
|
||||
YesRadio=&Yes, restart the computer now
|
||||
NoRadio=&No, I will restart the computer later
|
||||
; used for example as 'Run MyProg.exe'
|
||||
RunEntryExec=Run %1
|
||||
; used for example as 'View Readme.txt'
|
||||
RunEntryShellExec=View %1
|
||||
|
||||
; *** "Setup Needs the Next Disk" stuff
|
||||
ChangeDiskTitle=Setup Needs the Next Disk
|
||||
SelectDiskLabel2=Please insert Disk %1 and click OK.%n%nIf the files on this disk can be found in a folder other than the one displayed below, enter the correct path or click Browse.
|
||||
PathLabel=&Path:
|
||||
FileNotInDir2=The file "%1" could not be located in "%2". Please insert the correct disk or select another folder.
|
||||
SelectDirectoryLabel=Please specify the location of the next disk.
|
||||
|
||||
; *** Installation phase messages
|
||||
SetupAborted=Setup was not completed.%n%nPlease correct the problem and run Setup again.
|
||||
AbortRetryIgnoreSelectAction=Select action
|
||||
AbortRetryIgnoreRetry=&Try again
|
||||
AbortRetryIgnoreIgnore=&Ignore the error and continue
|
||||
AbortRetryIgnoreCancel=Cancel installation
|
||||
|
||||
; *** Installation status messages
|
||||
StatusClosingApplications=Closing applications...
|
||||
StatusCreateDirs=Creating directories...
|
||||
StatusExtractFiles=Extracting files...
|
||||
StatusCreateIcons=Creating shortcuts...
|
||||
StatusCreateIniEntries=Creating INI entries...
|
||||
StatusCreateRegistryEntries=Creating registry entries...
|
||||
StatusRegisterFiles=Registering files...
|
||||
StatusSavingUninstall=Saving uninstall information...
|
||||
StatusRunProgram=Finishing installation...
|
||||
StatusRestartingApplications=Restarting applications...
|
||||
StatusRollback=Rolling back changes...
|
||||
|
||||
; *** Misc. errors
|
||||
ErrorInternal2=Internal error: %1
|
||||
ErrorFunctionFailedNoCode=%1 failed
|
||||
ErrorFunctionFailed=%1 failed; code %2
|
||||
ErrorFunctionFailedWithMessage=%1 failed; code %2.%n%3
|
||||
ErrorExecutingProgram=Unable to execute file:%n%1
|
||||
|
||||
; *** Registry errors
|
||||
ErrorRegOpenKey=Error opening registry key:%n%1\%2
|
||||
ErrorRegCreateKey=Error creating registry key:%n%1\%2
|
||||
ErrorRegWriteKey=Error writing to registry key:%n%1\%2
|
||||
|
||||
; *** INI errors
|
||||
ErrorIniEntry=Error creating INI entry in file "%1".
|
||||
|
||||
; *** File copying errors
|
||||
FileAbortRetryIgnoreSkipNotRecommended=&Skip this file (not recommended)
|
||||
FileAbortRetryIgnoreIgnoreNotRecommended=&Ignore the error and continue (not recommended)
|
||||
SourceIsCorrupted=The source file is corrupted
|
||||
SourceDoesntExist=The source file "%1" does not exist
|
||||
ExistingFileReadOnly2=The existing file could not be replaced because it is marked read-only.
|
||||
ExistingFileReadOnlyRetry=&Remove the read-only attribute and try again
|
||||
ExistingFileReadOnlyKeepExisting=&Keep the existing file
|
||||
ErrorReadingExistingDest=An error occurred while trying to read the existing file:
|
||||
FileExists=The file already exists.%n%nWould you like Setup to overwrite it?
|
||||
ExistingFileNewer=The existing file is newer than the one Setup is trying to install. It is recommended that you keep the existing file.%n%nDo you want to keep the existing file?
|
||||
ErrorChangingAttr=An error occurred while trying to change the attributes of the existing file:
|
||||
ErrorCreatingTemp=An error occurred while trying to create a file in the destination directory:
|
||||
ErrorReadingSource=An error occurred while trying to read the source file:
|
||||
ErrorCopying=An error occurred while trying to copy a file:
|
||||
ErrorReplacingExistingFile=An error occurred while trying to replace the existing file:
|
||||
ErrorRestartReplace=RestartReplace failed:
|
||||
ErrorRenamingTemp=An error occurred while trying to rename a file in the destination directory:
|
||||
ErrorRegisterServer=Unable to register the DLL/OCX: %1
|
||||
ErrorRegSvr32Failed=RegSvr32 failed with exit code %1
|
||||
ErrorRegisterTypeLib=Unable to register the type library: %1
|
||||
|
||||
; *** Uninstall display name markings
|
||||
; used for example as 'My Program (32-bit)'
|
||||
UninstallDisplayNameMark=%1 (%2)
|
||||
; used for example as 'My Program (32-bit, All users)'
|
||||
UninstallDisplayNameMarks=%1 (%2, %3)
|
||||
UninstallDisplayNameMark32Bit=32-bit
|
||||
UninstallDisplayNameMark64Bit=64-bit
|
||||
UninstallDisplayNameMarkAllUsers=All users
|
||||
UninstallDisplayNameMarkCurrentUser=Current user
|
||||
|
||||
; *** Post-installation errors
|
||||
ErrorOpeningReadme=An error occurred while trying to open the README file.
|
||||
ErrorRestartingComputer=Setup was unable to restart the computer. Please do this manually.
|
||||
|
||||
; *** Uninstaller messages
|
||||
UninstallNotFound=File "%1" does not exist. Cannot uninstall.
|
||||
UninstallOpenError=File "%1" could not be opened. Cannot uninstall
|
||||
UninstallUnsupportedVer=The uninstall log file "%1" is in a format not recognized by this version of the uninstaller. Cannot uninstall
|
||||
UninstallUnknownEntry=An unknown entry (%1) was encountered in the uninstall log
|
||||
ConfirmUninstall=Are you sure you want to completely remove %1 and all of its components?
|
||||
UninstallOnlyOnWin64=This installation can only be uninstalled on 64-bit Windows.
|
||||
OnlyAdminCanUninstall=This installation can only be uninstalled by a user with administrative privileges.
|
||||
UninstallStatusLabel=Please wait while %1 is removed from your computer.
|
||||
UninstalledAll=%1 was successfully removed from your computer.
|
||||
UninstalledMost=%1 uninstall complete.%n%nSome elements could not be removed. These can be removed manually.
|
||||
UninstalledAndNeedsRestart=To complete the uninstallation of %1, your computer must be restarted.%n%nWould you like to restart now?
|
||||
UninstallDataCorrupted="%1" file is corrupted. Cannot uninstall
|
||||
|
||||
; *** Uninstallation phase messages
|
||||
ConfirmDeleteSharedFileTitle=Remove Shared File?
|
||||
ConfirmDeleteSharedFile2=The system indicates that the following shared file is no longer in use by any programs. Would you like for Uninstall to remove this shared file?%n%nIf any programs are still using this file and it is removed, those programs may not function properly. If you are unsure, choose No. Leaving the file on your system will not cause any harm.
|
||||
SharedFileNameLabel=File name:
|
||||
SharedFileLocationLabel=Location:
|
||||
WizardUninstalling=Uninstall Status
|
||||
StatusUninstalling=Uninstalling %1...
|
||||
|
||||
; *** Shutdown block reasons
|
||||
ShutdownBlockReasonInstallingApp=Installing %1.
|
||||
ShutdownBlockReasonUninstallingApp=Uninstalling %1.
|
||||
|
||||
; The custom messages below aren't used by Setup itself, but if you make
|
||||
; use of them in your scripts, you'll want to translate them.
|
||||
|
||||
[CustomMessages]
|
||||
|
||||
NameAndVersion=%1 version %2
|
||||
AdditionalIcons=Additional shortcuts:
|
||||
CreateDesktopIcon=Create a &desktop shortcut
|
||||
CreateQuickLaunchIcon=Create a &Quick Launch shortcut
|
||||
ProgramOnTheWeb=%1 on the Web
|
||||
UninstallProgram=Uninstall %1
|
||||
LaunchProgram=Launch %1
|
||||
AssocFileExtension=&Associate %1 with the %2 file extension
|
||||
AssocingFileExtension=Associating %1 with the %2 file extension...
|
||||
AutoStartProgramGroupDescription=Startup:
|
||||
AutoStartProgram=Automatically start %1
|
||||
AddonHostProgramNotFound=%1 could not be located in the folder you selected.%n%nDo you want to continue anyway?
|
||||
@@ -0,0 +1,382 @@
|
||||
; BEGIN ISPPBUILTINS.ISS
|
||||
//
|
||||
// Inno Setup Preprocessor
|
||||
//
|
||||
// Inno Setup (C) 1997-2020 Jordan Russell. All Rights Reserved.
|
||||
// Portions Copyright (C) 2000-2020 Martijn Laan. All Rights Reserved.
|
||||
// Portions Copyright (C) 2001-2004 Alex Yackimoff. All Rights Reserved.
|
||||
//
|
||||
#if defined(ISPP_INVOKED) && !defined(_BUILTINS_ISS_)
|
||||
//
|
||||
#if PREPROCVER < 0x01000000
|
||||
# error Inno Setup Preprocessor version is outdated
|
||||
#endif
|
||||
//
|
||||
#define _BUILTINS_ISS_
|
||||
//
|
||||
// ===========================================================================
|
||||
//
|
||||
// Default states for options.
|
||||
//
|
||||
//#pragma parseroption -b+ ; short circuit boolean evaluation: on
|
||||
//#pragma parseroption -m- ; short circuit multiplication evaluation (0 * A will not eval A): off
|
||||
//#pragma parseroption -p+ ; string literals without escape sequences: on
|
||||
//#pragma parseroption -u- ; allow undeclared identifiers: off
|
||||
//#pragma option -c+ ; pass script to the compiler: on
|
||||
//#pragma option -e- ; emit empty lines to translation: off
|
||||
//#pragma option -v- ; verbose mode: off
|
||||
//
|
||||
// ---------------------------------------------------------------------------
|
||||
//
|
||||
// Verbose levels:
|
||||
// 0 - #include and #file acknowledgements
|
||||
// 1 - information about any temp files created by #file
|
||||
// 2 - #insert and #append acknowledgements
|
||||
// 3 - reserved
|
||||
// 4 - #dim, #define and #undef acknowledgements
|
||||
// 5 - reserved
|
||||
// 6 - conditional inclusion acknowledgements
|
||||
// 7 - reserved
|
||||
// 8 - show strings emitted with #emit directive
|
||||
// 9 - macro and functions successful call acknowledgements
|
||||
//10 - Local macro array allocation acknowledgements
|
||||
//
|
||||
//#pragma verboselevel 0
|
||||
//
|
||||
#ifndef __POPT_P__
|
||||
# define private CStrings
|
||||
#else
|
||||
# pragma parseroption -p-
|
||||
#endif
|
||||
//
|
||||
#define NewLine "\n"
|
||||
#define Tab "\t"
|
||||
//
|
||||
#pragma parseroption -p+
|
||||
//
|
||||
#pragma spansymbol "\"
|
||||
//
|
||||
#define True 1
|
||||
#define False 0
|
||||
#define Yes True
|
||||
#define No False
|
||||
//
|
||||
#define MaxInt 0x7FFFFFFFL
|
||||
#define MinInt 0x80000000L
|
||||
//
|
||||
#define NULL
|
||||
#define void
|
||||
//
|
||||
// TypeOf constants
|
||||
//
|
||||
#define TYPE_ERROR 0
|
||||
#define TYPE_NULL 1
|
||||
#define TYPE_INTEGER 2
|
||||
#define TYPE_STRING 3
|
||||
#define TYPE_MACRO 4
|
||||
#define TYPE_FUNC 5
|
||||
#define TYPE_ARRAY 6
|
||||
//
|
||||
// Helper macro to find out the type of an array element or expression. TypeOf
|
||||
// standard function only allows identifier as its parameter. Use this macro
|
||||
// to convert an expression to identifier.
|
||||
//
|
||||
#define TypeOf2(any Expr) TypeOf(Expr)
|
||||
//
|
||||
// ReadReg constants
|
||||
//
|
||||
#define HKEY_CLASSES_ROOT 0x80000000UL
|
||||
#define HKEY_CURRENT_USER 0x80000001UL
|
||||
#define HKEY_LOCAL_MACHINE 0x80000002UL
|
||||
#define HKEY_USERS 0x80000003UL
|
||||
#define HKEY_CURRENT_CONFIG 0x80000005UL
|
||||
#define HKEY_CLASSES_ROOT_64 0x82000000UL
|
||||
#define HKEY_CURRENT_USER_64 0x82000001UL
|
||||
#define HKEY_LOCAL_MACHINE_64 0x82000002UL
|
||||
#define HKEY_USERS_64 0x82000003UL
|
||||
#define HKEY_CURRENT_CONFIG_64 0x82000005UL
|
||||
//
|
||||
#define HKCR HKEY_CLASSES_ROOT
|
||||
#define HKCU HKEY_CURRENT_USER
|
||||
#define HKLM HKEY_LOCAL_MACHINE
|
||||
#define HKU HKEY_USERS
|
||||
#define HKCC HKEY_CURRENT_CONFIG
|
||||
#define HKCR64 HKEY_CLASSES_ROOT_64
|
||||
#define HKCU64 HKEY_CURRENT_USER_64
|
||||
#define HKLM64 HKEY_LOCAL_MACHINE_64
|
||||
#define HKU64 HKEY_USERS_64
|
||||
#define HKCC64 HKEY_CURRENT_CONFIG_64
|
||||
//
|
||||
// Exec constants
|
||||
//
|
||||
#define SW_HIDE 0
|
||||
#define SW_SHOWNORMAL 1
|
||||
#define SW_NORMAL 1
|
||||
#define SW_SHOWMINIMIZED 2
|
||||
#define SW_SHOWMAXIMIZED 3
|
||||
#define SW_MAXIMIZE 3
|
||||
#define SW_SHOWNOACTIVATE 4
|
||||
#define SW_SHOW 5
|
||||
#define SW_MINIMIZE 6
|
||||
#define SW_SHOWMINNOACTIVE 7
|
||||
#define SW_SHOWNA 8
|
||||
#define SW_RESTORE 9
|
||||
#define SW_SHOWDEFAULT 10
|
||||
#define SW_MAX 10
|
||||
//
|
||||
// Find constants
|
||||
//
|
||||
#define FIND_MATCH 0x00
|
||||
#define FIND_BEGINS 0x01
|
||||
#define FIND_ENDS 0x02
|
||||
#define FIND_CONTAINS 0x03
|
||||
#define FIND_CASESENSITIVE 0x04
|
||||
#define FIND_SENSITIVE FIND_CASESENSITIVE
|
||||
#define FIND_AND 0x00
|
||||
#define FIND_OR 0x08
|
||||
#define FIND_NOT 0x10
|
||||
#define FIND_TRIM 0x20
|
||||
//
|
||||
// FindFirst constants
|
||||
//
|
||||
#define faReadOnly 0x00000001
|
||||
#define faHidden 0x00000002
|
||||
#define faSysFile 0x00000004
|
||||
#define faVolumeID 0x00000008
|
||||
#define faDirectory 0x00000010
|
||||
#define faArchive 0x00000020
|
||||
#define faSymLink 0x00000040
|
||||
#define faAnyFile 0x0000003F
|
||||
//
|
||||
// GetStringFileInfo standard names
|
||||
//
|
||||
#define COMPANY_NAME "CompanyName"
|
||||
#define FILE_DESCRIPTION "FileDescription"
|
||||
#define FILE_VERSION "FileVersion"
|
||||
#define INTERNAL_NAME "InternalName"
|
||||
#define LEGAL_COPYRIGHT "LegalCopyright"
|
||||
#define ORIGINAL_FILENAME "OriginalFilename"
|
||||
#define PRODUCT_NAME "ProductName"
|
||||
#define PRODUCT_VERSION "ProductVersion"
|
||||
//
|
||||
// GetStringFileInfo helpers
|
||||
//
|
||||
#define GetFileCompany(str FileName) GetStringFileInfo(FileName, COMPANY_NAME)
|
||||
#define GetFileDescription(str FileName) GetStringFileInfo(FileName, FILE_DESCRIPTION)
|
||||
#define GetFileVersionString(str FileName) GetStringFileInfo(FileName, FILE_VERSION)
|
||||
#define GetFileCopyright(str FileName) GetStringFileInfo(FileName, LEGAL_COPYRIGHT)
|
||||
#define GetFileOriginalFilename(str FileName) GetStringFileInfo(FileName, ORIGINAL_FILENAME)
|
||||
#define GetFileProductVersion(str FileName) GetStringFileInfo(FileName, PRODUCT_VERSION)
|
||||
//
|
||||
// ParseVersion
|
||||
//
|
||||
// Macro internally calls GetFileVersion function and parses string returned
|
||||
// by that function (in form "0.0.0.0"). All four version elements are stored
|
||||
// in by-reference parameters Major, Minor, Rev, and Build. Macro returns
|
||||
// string returned by GetFileVersion.
|
||||
//
|
||||
#define DeleteToFirstPeriod(str *S) \
|
||||
Local[1] = Copy(S, 1, (Local[0] = Pos(".", S)) - 1), \
|
||||
S = Copy(S, Local[0] + 1), \
|
||||
Local[1]
|
||||
//
|
||||
#define ParseVersion(str FileName, *Major, *Minor, *Rev, *Build) \
|
||||
Local[1] = Local[0] = GetFileVersion(FileName), \
|
||||
Local[1] == "" ? "" : ( \
|
||||
Major = Int(DeleteToFirstPeriod(Local[1])), \
|
||||
Minor = Int(DeleteToFirstPeriod(Local[1])), \
|
||||
Rev = Int(DeleteToFirstPeriod(Local[1])), \
|
||||
Build = Int(Local[1]), \
|
||||
Local[0])
|
||||
//
|
||||
// EncodeVer
|
||||
//
|
||||
// Encodes given four version elements to a 32 bit integer number (8 bits for
|
||||
// each element, i.e. elements must be within 0...255 range).
|
||||
//
|
||||
#define EncodeVer(int Major, int Minor, int Revision = 0, int Build = -1) \
|
||||
Major << 24 | (Minor & 0xFF) << 16 | (Revision & 0xFF) << 8 | (Build >= 0 ? Build & 0xFF : 0)
|
||||
//
|
||||
// DecodeVer
|
||||
//
|
||||
// Decodes given 32 bit integer encoded version to its string representation,
|
||||
// Digits parameter indicates how many elements to show (if the fourth element
|
||||
// is 0, it won't be shown anyway).
|
||||
//
|
||||
#define DecodeVer(int Ver, int Digits = 3) \
|
||||
Str(Ver >> 0x18 & 0xFF) + (Digits > 1 ? "." : "") + \
|
||||
(Digits > 1 ? \
|
||||
Str(Ver >> 0x10 & 0xFF) + (Digits > 2 ? "." : "") : "") + \
|
||||
(Digits > 2 ? \
|
||||
Str(Ver >> 0x08 & 0xFF) + (Digits > 3 && (Local = Ver & 0xFF) ? "." : "") : "") + \
|
||||
(Digits > 3 && Local ? \
|
||||
Str(Ver & 0xFF) : "")
|
||||
//
|
||||
// FindSection
|
||||
//
|
||||
// Returns index of the line following the header of the section. This macro
|
||||
// is intended to be used with #insert directive.
|
||||
//
|
||||
#define FindSection(str Section = "Files") \
|
||||
Find(0, "[" + Section + "]", FIND_MATCH | FIND_TRIM) + 1
|
||||
//
|
||||
// FindSectionEnd
|
||||
//
|
||||
// Returns index of the line following last entry of the section. This macro
|
||||
// is intended to be used with #insert directive.
|
||||
//
|
||||
#if VER >= 0x03000000
|
||||
# define FindNextSection(int Line) \
|
||||
Find(Line, "[", FIND_BEGINS | FIND_TRIM, "]", FIND_ENDS | FIND_AND)
|
||||
# define FindSectionEnd(str Section = "Files") \
|
||||
FindNextSection(FindSection(Section))
|
||||
#else
|
||||
# define FindSectionEnd(str Section = "Files") \
|
||||
FindSection(Section) + EntryCount(Section)
|
||||
#endif
|
||||
//
|
||||
// FindCode
|
||||
//
|
||||
// Returns index of the line (of translation) following either [Code] section
|
||||
// header, or "program" keyword, if any.
|
||||
//
|
||||
#define FindCode() \
|
||||
Local[1] = FindSection("Code"), \
|
||||
Local[0] = Find(Local[1] - 1, "program", FIND_BEGINS, ";", FIND_ENDS | FIND_AND), \
|
||||
(Local[0] < 0 ? Local[1] : Local[0] + 1)
|
||||
//
|
||||
// ExtractFilePath
|
||||
//
|
||||
// Returns directory portion of the given filename without backslash (unless
|
||||
// it is a root directory). If PathName doesn't contain directory portion,
|
||||
// the result is an empty string.
|
||||
//
|
||||
#define ExtractFilePath(str PathName) \
|
||||
(Local[0] = \
|
||||
!(Local[1] = RPos("\", PathName)) ? \
|
||||
"" : \
|
||||
Copy(PathName, 1, Local[1] - 1)), \
|
||||
Local[0] + \
|
||||
((Local[2] = Len(Local[0])) == 2 && Copy(Local[0], Local[2]) == ":" ? \
|
||||
"\" : \
|
||||
"")
|
||||
#define ExtractFileDir(str PathName) \
|
||||
RemoveBackslash(ExtractFilePath(PathName))
|
||||
|
||||
#define ExtractFileExt(str PathName) \
|
||||
Local[0] = RPos(".", PathName), \
|
||||
Copy(PathName, Local[0] + 1)
|
||||
//
|
||||
// ExtractFileName
|
||||
//
|
||||
// Returns name portion of the given filename. If PathName ends with
|
||||
// a backslash, the result is an empty string.
|
||||
//
|
||||
#define ExtractFileName(str PathName) \
|
||||
!(Local[0] = RPos("\", PathName)) ? \
|
||||
PathName : \
|
||||
Copy(PathName, Local[0] + 1)
|
||||
//
|
||||
// ChangeFileExt
|
||||
//
|
||||
// Changes extension in FileName with NewExt. NewExt must not contain
|
||||
// period.
|
||||
//
|
||||
#define ChangeFileExt(str FileName, str NewExt) \
|
||||
!(Local[0] = RPos(".", FileName)) ? \
|
||||
FileName + "." + NewExt : \
|
||||
Copy(FileName, 1, Local[0]) + NewExt
|
||||
//
|
||||
// RemoveFileExt
|
||||
//
|
||||
// Removes extension in FileName.
|
||||
//
|
||||
#define RemoveFileExt(str FileName) \
|
||||
!(Local[0] = RPos(".", FileName)) ? \
|
||||
FileName : \
|
||||
Copy(FileName, 1, Local[0] - 1)
|
||||
//
|
||||
// AddBackslash
|
||||
//
|
||||
// Adds a backslash to the string, if it's not already there.
|
||||
//
|
||||
#define AddBackslash(str S) \
|
||||
Copy(S, Len(S)) == "\" ? S : S + "\"
|
||||
//
|
||||
// RemoveBackslash
|
||||
//
|
||||
// Removes trailing backslash from the string unless the string points to
|
||||
// a root directory.
|
||||
//
|
||||
#define RemoveBackslash(str S) \
|
||||
Local[0] = Len(S), \
|
||||
Local[0] > 0 ? \
|
||||
Copy(S, Local[0]) == "\" ? \
|
||||
(Local[0] == 3 && Copy(S, 2, 1) == ":" ? \
|
||||
S : \
|
||||
Copy(S, 1, Local[0] - 1)) : \
|
||||
S : \
|
||||
""
|
||||
//
|
||||
// Delete
|
||||
//
|
||||
// Deletes specified number of characters beginning with Index from S. S is
|
||||
// passed by reference (therefore is modified). Acts like Delete function in
|
||||
// Delphi (from System unit).
|
||||
//
|
||||
#define Delete(str *S, int Index, int Count = MaxInt) \
|
||||
S = Copy(S, 1, Index - 1) + Copy(S, Index + Count)
|
||||
//
|
||||
// Insert
|
||||
//
|
||||
// Inserts specified Substr at Index'th character into S. S is passed by
|
||||
// reference (therefore is modified).
|
||||
//
|
||||
#define Insert(str *S, int Index, str Substr) \
|
||||
Index > Len(S) + 1 ? \
|
||||
S : \
|
||||
S = Copy(S, 1, Index - 1) + SubStr + Copy(S, Index)
|
||||
//
|
||||
// YesNo, IsDirSet
|
||||
//
|
||||
// Returns nonzero value if given string is "yes", "true" or "1". Intended to
|
||||
// be used with SetupSetting function. This macro replaces YesNo function
|
||||
// available in previous releases.
|
||||
//
|
||||
#define YesNo(str S) \
|
||||
(S = LowerCase(S)) == "yes" || S == "true" || S == "1"
|
||||
//
|
||||
#define IsDirSet(str SetupDirective) \
|
||||
YesNo(SetupSetting(SetupDirective))
|
||||
//
|
||||
//
|
||||
#define Power(int X, int P = 2) \
|
||||
!P ? 1 : X * Power(X, P - 1)
|
||||
//
|
||||
#define Min(int A, int B, int C = MaxInt) \
|
||||
A < B ? A < C ? Int(A) : Int(C) : Int(B)
|
||||
//
|
||||
#define Max(int A, int B, int C = MinInt) \
|
||||
A > B ? A > C ? Int(A) : Int(C) : Int(B)
|
||||
//
|
||||
// SameText
|
||||
//
|
||||
// Returns True if the given strings are identical, ignoring case.
|
||||
//
|
||||
#define SameText(str S1, str S2) \
|
||||
LowerCase(S1) == LowerCase(S2)
|
||||
//
|
||||
// SameStr
|
||||
//
|
||||
// Returns True if the given strings are identical, with case-sensitivity.
|
||||
//
|
||||
#define SameStr(str S1, str S2) \
|
||||
S1 == S2
|
||||
//
|
||||
|
||||
#ifdef CStrings
|
||||
# pragma parseroption -p-
|
||||
#endif
|
||||
#endif
|
||||
; END ISPPBUILTINS.ISS
|
||||
|
||||
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 151 KiB |
|
After Width: | Height: | Size: 31 KiB |
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1"/>
|
||||
</startup>
|
||||
<runtime>
|
||||
</runtime>
|
||||
</configuration>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0"?>
|
||||
<doc>
|
||||
<assembly>
|
||||
<name>Microsoft.Extensions.CommandLineUtils</name>
|
||||
</assembly>
|
||||
<members>
|
||||
</members>
|
||||
</doc>
|
||||
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
|
||||
<assemblyIdentity
|
||||
name="Microsoft.Windows.Build.Appx.AppxPackaging.dll"
|
||||
version="0.0.0.0"/>
|
||||
|
||||
<file name="AppxPackaging.dll">
|
||||
<comClass
|
||||
clsid="{5842a140-ff9f-4166-8f5c-62f5b7b0c781}"
|
||||
threadingModel="Both"
|
||||
description="AppxFactory class"/>
|
||||
<comClass
|
||||
clsid="{DC664FDD-D868-46EE-8780-8D196CB739F7}"
|
||||
threadingModel="Both"
|
||||
description="AppxEncryptionFactory class"/>
|
||||
<comClass
|
||||
clsid="{378E0446-5384-43B7-8877-E7DBDD883446}"
|
||||
threadingModel="Both"
|
||||
description="AppxBundleFactory class"/>
|
||||
<comClass
|
||||
clsid="{48DE828C-730C-49AF-AE84-759C609911EE}"
|
||||
threadingModel="Both"
|
||||
description="AppxNoValidationFactory class"/>
|
||||
<comClass
|
||||
clsid="{F004F2CA-AEBC-4B0D-BF58-E516D5BCC0AB}"
|
||||
threadingModel="Both"
|
||||
description="AppxPackageEditor class"/>
|
||||
<comClass
|
||||
clsid="{7F00FA1E-9820-47B1-9C4F-8701F1432177}"
|
||||
threadingModel="Both"
|
||||
description="AppxPackagingLayoutReader class"/>
|
||||
<comClass
|
||||
clsid="{0CF07551-EEF2-420C-B5AB-7E4FEB2249CF}"
|
||||
threadingModel="Both"
|
||||
description="AppxFactoryInternal class"/>
|
||||
<comClass
|
||||
clsid="{50CA0A46-1588-4161-8ED2-EF9E469CED5D}"
|
||||
threadingModel="Both"
|
||||
description="AppxPackagingDiagnosticEventSinkManager class"/>
|
||||
</file>
|
||||
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
name="Microsoft.Windows.Build.Appx.OpcServices.dll"
|
||||
version="0.0.0.0"/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
|
||||
</assembly>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
|
||||
<assemblyIdentity
|
||||
name="Microsoft.Windows.Build.Appx.AppxSip.dll"
|
||||
version="0.0.0.0"/>
|
||||
|
||||
<file name="AppxSip.dll"/>
|
||||
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
name="Microsoft.Windows.Build.Appx.AppxPackaging.dll"
|
||||
version="0.0.0.0"/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
|
||||
</assembly>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
|
||||
<assemblyIdentity
|
||||
name="Microsoft.Windows.Build.Appx.OpcServices.dll"
|
||||
version="0.0.0.0"/>
|
||||
|
||||
<file name="OpcServices.dll">
|
||||
<comClass
|
||||
clsid="{6B2D6BA0-9F3E-4f27-920B-313CC426A39E}"
|
||||
threadingModel="Both"
|
||||
description="OpcFactory class"/>
|
||||
</file>
|
||||
|
||||
</assembly>
|
||||
@@ -0,0 +1,11 @@
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
|
||||
<assemblyIdentity
|
||||
name="Microsoft.Windows.Build.Signing.mssign32.dll"
|
||||
version="0.0.0.0"
|
||||
/>
|
||||
|
||||
<file name="mssign32.dll">
|
||||
</file>
|
||||
|
||||
</assembly>
|
||||
@@ -0,0 +1,11 @@
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
|
||||
<assemblyIdentity
|
||||
name="Microsoft.Windows.Build.Signing.wintrust.dll"
|
||||
version="0.0.0.0"
|
||||
/>
|
||||
|
||||
<file name="wintrust.dll">
|
||||
</file>
|
||||
|
||||
</assembly>
|
||||
@@ -0,0 +1,21 @@
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity
|
||||
name=" "
|
||||
version="0.0.0.0"
|
||||
/>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
name="Microsoft.Windows.Build.Signing.wintrust.dll"
|
||||
version="0.0.0.0"
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel level="asInvoker" />
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
</assembly>
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
|
||||
<assemblyIdentity
|
||||
name=" "
|
||||
version="0.0.0.0"
|
||||
/>
|
||||
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
name="Microsoft.Windows.Build.Signing.mssign32.dll"
|
||||
version="0.0.0.0"
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
name="Microsoft.Windows.Build.Signing.wintrust.dll"
|
||||
version="0.0.0.0"
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
name="Microsoft.Windows.Build.Appx.AppxSip.dll"
|
||||
version="0.0.0.0"
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
</assembly>
|
||||
@@ -0,0 +1,56 @@
|
||||
; Pascal Code to customize the UI
|
||||
; More info: https://jrsoftware.org/ishelp/index.php?topic=scriptclasses
|
||||
[Code]
|
||||
function RGB(r, g, b: Byte): TColor;
|
||||
begin
|
||||
Result := (Integer(r) or (Integer(g) shl 8) or (Integer(b) shl 16));
|
||||
end;
|
||||
procedure InitializeWizard;
|
||||
begin
|
||||
{ hide borders & make frameless }
|
||||
WizardForm.BorderStyle := bsNone;
|
||||
WizardForm.Bevel1.Visible := False;
|
||||
WizardForm.Bevel.Visible := False;
|
||||
|
||||
{ set height with fix for high res displays }
|
||||
WizardForm.Height := WizardForm.Font.PixelsPerInch * 140 / 96;
|
||||
|
||||
{ hide main panel (top)}
|
||||
WizardForm.MainPanel.Height := 0
|
||||
|
||||
{ chage colors }
|
||||
WizardForm.Color := RGB(8,121,243);
|
||||
WizardForm.InnerPage.Color := RGB(8,121,243);
|
||||
WizardForm.ReadyLabel.Font.Color := clWhite;
|
||||
WizardForm.StatusLabel.Font.Color := clWhite;
|
||||
WizardForm.FilenameLabel.Font.Color := clWhite;
|
||||
|
||||
{ Stretch the inner page across whole outer page }
|
||||
WizardForm.InnerNotebook.Left := 20;
|
||||
WizardForm.InnerNotebook.Top := 20;
|
||||
WizardForm.InnerNotebook.Width := WizardForm.OuterNotebook.ClientWidth - 40;
|
||||
WizardForm.InnerNotebook.Height := WizardForm.OuterNotebook.ClientHeight - 20;
|
||||
end;
|
||||
procedure InitializeUninstallProgressForm;
|
||||
begin
|
||||
{ hide borders & make frameless }
|
||||
UninstallProgressForm.BorderStyle := bsNone;
|
||||
UninstallProgressForm.Bevel1.Visible := False;
|
||||
UninstallProgressForm.Bevel.Visible := False;
|
||||
|
||||
{ set height }
|
||||
UninstallProgressForm.Height := UninstallProgressForm.Font.PixelsPerInch * 140 / 96;
|
||||
{ hide main panel (top)}
|
||||
UninstallProgressForm.MainPanel.Height := 0
|
||||
|
||||
{ change colors }
|
||||
UninstallProgressForm.Color := RGB(8,121,243);
|
||||
UninstallProgressForm.InnerPage.Color := RGB(8,121,243);
|
||||
UninstallProgressForm.StatusLabel.Font.Color := clWhite;
|
||||
|
||||
{ Stretch the inner page across whole outer page }
|
||||
UninstallProgressForm.InnerNotebook.Left := 20;
|
||||
UninstallProgressForm.InnerNotebook.Top := 20;
|
||||
UninstallProgressForm.InnerNotebook.Width := UninstallProgressForm.OuterNotebook.ClientWidth - 40;
|
||||
UninstallProgressForm.InnerNotebook.Height := UninstallProgressForm.OuterNotebook.ClientHeight - 20;
|
||||
end;
|
||||
@@ -0,0 +1,59 @@
|
||||
#include "custom-ui.iss"
|
||||
|
||||
#define AppName "Speckle for PowerBI (Data Connector)"
|
||||
#define Slug "powerbi"
|
||||
|
||||
#define BasePath "..\"
|
||||
#define Bin BasePath + "bin\"
|
||||
|
||||
#define AppVersion "2.0.0"
|
||||
#define AppInfoVersion "2.0.0.1234"
|
||||
#define AppPublisher "Speckle"
|
||||
#define AppURL "https://speckle.systems"
|
||||
#define UninstallerFolder "{autoappdata}\Speckle\Uninstallers\" + Slug
|
||||
|
||||
#define CustomConnectorFolder "{%USERPROFILE}\Documents\Power BI Desktop\Custom Connectors"
|
||||
#define PFX_PSW GetEnv('PFX_PSW')
|
||||
|
||||
[Setup]
|
||||
AppId={{6759e9e1-8c6b-4974-87c3-bb3c8b8ce619}
|
||||
; Shouldn't need to update these
|
||||
AppName={#AppName}
|
||||
AppVersion={#AppInfoVersion }
|
||||
AppVerName={#AppName} {#AppInfoVersion }
|
||||
AppPublisher={#AppPublisher}
|
||||
AppPublisherURL={#AppURL}
|
||||
AppSupportURL={#AppURL}
|
||||
AppUpdatesURL={#AppURL}
|
||||
AppCopyright=Copyright (C) 2020-2022 AEC SYSTEMS LTD
|
||||
DefaultDirName={#UninstallerFolder}
|
||||
VersionInfoVersion={#AppVersion}
|
||||
ChangesAssociations=yes
|
||||
CloseApplications=false
|
||||
PrivilegesRequired=admin
|
||||
OutputBaseFilename={#Slug}
|
||||
OutputDir={#Bin}
|
||||
; Needed so that the rhino registry key is put in the right location
|
||||
ArchitecturesInstallIn64BitMode=x64
|
||||
|
||||
; UI
|
||||
WindowShowCaption=no
|
||||
WizardSizePercent=100,100
|
||||
; SetupIconFile=.\InnoSetup\speckle.ico
|
||||
|
||||
; Disable wizard pages
|
||||
DisableDirPage=yes
|
||||
DisableProgramGroupPage=yes
|
||||
DisableWelcomePage=yes
|
||||
DisableFinishedPage=yes
|
||||
|
||||
;SignTool=byparam tools\SignTool\signtool.exe sign /f $qtools\AEC Systems Ltd.pfx$q /p {#PFX_PSW} /tr http://timestamp.digicert.com /td sha256 /fd sha256 $f
|
||||
|
||||
[Languages]
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
|
||||
[Files]
|
||||
Source: "{#Bin}Speckle.pqx"; DestDir: "{#CustomConnectorFolder}";
|
||||
|
||||
[Registry]
|
||||
Root: HKLM; Subkey: "Software\Policies\Microsoft\Power BI Desktop"; ValueType: multisz; ValueName: "TrustedCertificateThumbprints"; ValueData: "4797ACC22464ED1CF9AFF4C09C2CCF4CF1873EFB"; Flags: uninsdeletekey
|
||||
@@ -0,0 +1,376 @@
|
||||
let
|
||||
Diagnostics.LogValue = (prefix, value) =>
|
||||
Diagnostics.Trace(
|
||||
TraceLevel.Information,
|
||||
prefix & ": " & (try Diagnostics.ValueToText(value) otherwise "<error getting value>"),
|
||||
value
|
||||
),
|
||||
Diagnostics.LogValue2 = (prefix, value, result, optional delayed) =>
|
||||
Diagnostics.Trace(TraceLevel.Information, prefix & ": " & Diagnostics.ValueToText(value), result, delayed),
|
||||
Diagnostics.LogFailure = (text, function) =>
|
||||
let
|
||||
result = try function()
|
||||
in
|
||||
if result[HasError] then
|
||||
Diagnostics.LogValue2(text, result[Error], () => error result[Error], true)
|
||||
else
|
||||
result[Value],
|
||||
Diagnostics.WrapFunctionResult = (innerFunction as function, outerFunction as function) as function =>
|
||||
Function.From(Value.Type(innerFunction), (list) => outerFunction(() => Function.Invoke(innerFunction, list))),
|
||||
Diagnostics.WrapHandlers = (handlers as record) as record =>
|
||||
Record.FromList(
|
||||
List.Transform(
|
||||
Record.FieldNames(handlers),
|
||||
(h) =>
|
||||
Diagnostics.WrapFunctionResult(Record.Field(handlers, h), (fn) => Diagnostics.LogFailure(h, fn))
|
||||
),
|
||||
Record.FieldNames(handlers)
|
||||
),
|
||||
Diagnostics.ValueToText = (value) =>
|
||||
let
|
||||
_canBeIdentifier = (x) =>
|
||||
let
|
||||
keywords = {
|
||||
"and",
|
||||
"as",
|
||||
"each",
|
||||
"else",
|
||||
"error",
|
||||
"false",
|
||||
"if",
|
||||
"in",
|
||||
"is",
|
||||
"let",
|
||||
"meta",
|
||||
"not",
|
||||
"otherwise",
|
||||
"or",
|
||||
"section",
|
||||
"shared",
|
||||
"then",
|
||||
"true",
|
||||
"try",
|
||||
"type"
|
||||
},
|
||||
charAlpha = (c as number) => (c >= 65 and c <= 90) or (c >= 97 and c <= 122) or c = 95,
|
||||
charDigit = (c as number) => c >= 48 and c <= 57
|
||||
in
|
||||
try
|
||||
charAlpha(Character.ToNumber(Text.At(x, 0)))
|
||||
and List.MatchesAll(
|
||||
Text.ToList(x), (c) => let num = Character.ToNumber(c) in charAlpha(num)
|
||||
or charDigit(num)
|
||||
)
|
||||
and not List.MatchesAny(keywords, (li) => li = x) otherwise false,
|
||||
Serialize.Binary = (x) => "#binary(" & Serialize(Binary.ToList(x)) & ") ",
|
||||
Serialize.Date = (x) =>
|
||||
"#date(" & Text.From(Date.Year(x)) & ", " & Text.From(Date.Month(x)) & ", " & Text.From(Date.Day(x))
|
||||
& ") ",
|
||||
Serialize.Datetime = (x) =>
|
||||
"#datetime("
|
||||
& Text.From(Date.Year(DateTime.Date(x)))
|
||||
& ", "
|
||||
& Text.From(Date.Month(DateTime.Date(x)))
|
||||
& ", "
|
||||
& Text.From(Date.Day(DateTime.Date(x)))
|
||||
& ", "
|
||||
& Text.From(Time.Hour(DateTime.Time(x)))
|
||||
& ", "
|
||||
& Text.From(Time.Minute(DateTime.Time(x)))
|
||||
& ", "
|
||||
& Text.From(Time.Second(DateTime.Time(x)))
|
||||
& ") ",
|
||||
Serialize.Datetimezone = (x) =>
|
||||
let
|
||||
dtz = DateTimeZone.ToRecord(x)
|
||||
in
|
||||
"#datetimezone("
|
||||
& Text.From(dtz[Year])
|
||||
& ", "
|
||||
& Text.From(dtz[Month])
|
||||
& ", "
|
||||
& Text.From(dtz[Day])
|
||||
& ", "
|
||||
& Text.From(dtz[Hour])
|
||||
& ", "
|
||||
& Text.From(dtz[Minute])
|
||||
& ", "
|
||||
& Text.From(dtz[Second])
|
||||
& ", "
|
||||
& Text.From(dtz[ZoneHours])
|
||||
& ", "
|
||||
& Text.From(dtz[ZoneMinutes])
|
||||
& ") ",
|
||||
Serialize.Duration = (x) =>
|
||||
let
|
||||
dur = Duration.ToRecord(x)
|
||||
in
|
||||
"#duration("
|
||||
& Text.From(dur[Days])
|
||||
& ", "
|
||||
& Text.From(dur[Hours])
|
||||
& ", "
|
||||
& Text.From(dur[Minutes])
|
||||
& ", "
|
||||
& Text.From(dur[Seconds])
|
||||
& ") ",
|
||||
Serialize.Function = (x) =>
|
||||
_serialize_function_param_type(
|
||||
Type.FunctionParameters(Value.Type(x)), Type.FunctionRequiredParameters(Value.Type(x))
|
||||
)
|
||||
& " as "
|
||||
& _serialize_function_return_type(Value.Type(x))
|
||||
& " => (...) ",
|
||||
Serialize.List = (x) =>
|
||||
"{"
|
||||
& List.Accumulate(
|
||||
x, "", (seed, item) => if seed = "" then Serialize(item) else seed & ", " & Serialize(item)
|
||||
)
|
||||
& "} ",
|
||||
Serialize.Logical = (x) => Text.From(x),
|
||||
Serialize.Null = (x) => "null",
|
||||
Serialize.Number = (x) =>
|
||||
let
|
||||
Text.From = (i as number) as text =>
|
||||
if Number.IsNaN(i) then
|
||||
"#nan"
|
||||
else if i = Number.PositiveInfinity then
|
||||
"#infinity"
|
||||
else if i = Number.NegativeInfinity then
|
||||
"-#infinity"
|
||||
else
|
||||
Text.From(i)
|
||||
in
|
||||
Text.From(x),
|
||||
Serialize.Record = (x) =>
|
||||
"[ "
|
||||
& List.Accumulate(
|
||||
Record.FieldNames(x),
|
||||
"",
|
||||
(seed, item) =>
|
||||
(if seed = "" then Serialize.Identifier(item) else seed & ", " & Serialize.Identifier(
|
||||
item
|
||||
))
|
||||
& " = "
|
||||
& Serialize(Record.Field(x, item))
|
||||
)
|
||||
& " ] ",
|
||||
Serialize.Table = (x) =>
|
||||
"#table( type " & _serialize_table_type(Value.Type(x)) & ", " & Serialize(Table.ToRows(x)) & ") ",
|
||||
Serialize.Text = (x) => """" & _serialize_text_content(x) & """",
|
||||
_serialize_text_content = (x) =>
|
||||
let
|
||||
escapeText = (n as number) as text =>
|
||||
"#(#)(" & Text.PadStart(Number.ToText(n, "X", "en-US"), 4, "0") & ")"
|
||||
in
|
||||
List.Accumulate(
|
||||
List.Transform(
|
||||
Text.ToList(x),
|
||||
(c) =>
|
||||
let
|
||||
n = Character.ToNumber(c)
|
||||
in
|
||||
if n = 9 then
|
||||
"#(#)(tab)"
|
||||
else if n = 10 then
|
||||
"#(#)(lf)"
|
||||
else if n = 13 then
|
||||
"#(#)(cr)"
|
||||
else if n = 34 then
|
||||
""""""
|
||||
else if n = 35 then
|
||||
"#(#)(#)"
|
||||
else if n < 32 then
|
||||
escapeText(n)
|
||||
else if n < 127 then
|
||||
Character.FromNumber(n)
|
||||
else
|
||||
escapeText(n)
|
||||
),
|
||||
"",
|
||||
(s, i) => s & i
|
||||
),
|
||||
Serialize.Identifier = (x) => if _canBeIdentifier(x) then x else "#""" & _serialize_text_content(x) & """",
|
||||
Serialize.Time = (x) =>
|
||||
"#time("
|
||||
& Text.From(Time.Hour(x))
|
||||
& ", "
|
||||
& Text.From(Time.Minute(x))
|
||||
& ", "
|
||||
& Text.From(Time.Second(x))
|
||||
& ") ",
|
||||
Serialize.Type = (x) => "type " & _serialize_typename(x),
|
||||
_serialize_typename = (x, optional funtype as logical) =>
|
||||
/* Optional parameter: Is this being used as part of a function signature? */ let
|
||||
isFunctionType = (x as type) =>
|
||||
try if Type.FunctionReturn(x) is type then true else false otherwise false,
|
||||
isTableType = (x as type) =>
|
||||
try if Type.TableSchema(x) is table then true else false otherwise false,
|
||||
isRecordType = (x as type) =>
|
||||
try if Type.ClosedRecord(x) is type then true else false otherwise false,
|
||||
isListType = (x as type) => try if Type.ListItem(x) is type then true else false otherwise false
|
||||
in
|
||||
if funtype = null and isTableType(x) then
|
||||
_serialize_table_type(x)
|
||||
else if funtype = null and isListType(x) then
|
||||
"{ " & @_serialize_typename(Type.ListItem(x)) & " }"
|
||||
else if funtype = null and isFunctionType(x) then
|
||||
"function " & _serialize_function_type(x)
|
||||
else if funtype = null and isRecordType(x) then
|
||||
_serialize_record_type(x)
|
||||
else if x = type any then
|
||||
"any"
|
||||
else
|
||||
let
|
||||
base = Type.NonNullable(x)
|
||||
in
|
||||
(if Type.IsNullable(x) then "nullable " else "")
|
||||
& (
|
||||
if base = type anynonnull then
|
||||
"anynonnull"
|
||||
else if base = type binary then
|
||||
"binary"
|
||||
else if base = type date then
|
||||
"date"
|
||||
else if base = type datetime then
|
||||
"datetime"
|
||||
else if base = type datetimezone then
|
||||
"datetimezone"
|
||||
else if base = type duration then
|
||||
"duration"
|
||||
else if base = type logical then
|
||||
"logical"
|
||||
else if base = type none then
|
||||
"none"
|
||||
else if base = type null then
|
||||
"null"
|
||||
else if base = type number then
|
||||
"number"
|
||||
else if base = type text then
|
||||
"text"
|
||||
else if base = type time then
|
||||
"time"
|
||||
else if base = type type then
|
||||
"type"
|
||||
else /* Abstract types: */ if base = type function then
|
||||
"function"
|
||||
else if base = type table then
|
||||
"table"
|
||||
else if base = type record then
|
||||
"record"
|
||||
else if base = type list then
|
||||
"list"
|
||||
else
|
||||
"any /*Actually unknown type*/"
|
||||
),
|
||||
_serialize_table_type = (x) =>
|
||||
let
|
||||
schema = Type.TableSchema(x)
|
||||
in
|
||||
"table "
|
||||
& (
|
||||
if Table.IsEmpty(schema) then
|
||||
""
|
||||
else
|
||||
"["
|
||||
& List.Accumulate(
|
||||
List.Transform(
|
||||
Table.ToRecords(Table.Sort(schema, "Position")),
|
||||
each Serialize.Identifier(_[Name]) & " = " & _[Kind]
|
||||
),
|
||||
"",
|
||||
(seed, item) => (if seed = "" then item else seed & ", " & item)
|
||||
)
|
||||
& "] "
|
||||
),
|
||||
_serialize_record_type = (x) =>
|
||||
let
|
||||
flds = Type.RecordFields(x)
|
||||
in
|
||||
if Record.FieldCount(flds) = 0 then
|
||||
"record"
|
||||
else
|
||||
"["
|
||||
& List.Accumulate(
|
||||
Record.FieldNames(flds),
|
||||
"",
|
||||
(seed, item) =>
|
||||
seed
|
||||
& (if seed <> "" then ", " else "")
|
||||
& (
|
||||
Serialize.Identifier(item)
|
||||
& "="
|
||||
& _serialize_typename(Record.Field(flds, item)[Type])
|
||||
)
|
||||
)
|
||||
& (if Type.IsOpenRecord(x) then ",..." else "")
|
||||
& "]",
|
||||
_serialize_function_type = (x) =>
|
||||
_serialize_function_param_type(Type.FunctionParameters(x), Type.FunctionRequiredParameters(x))
|
||||
& " as "
|
||||
& _serialize_function_return_type(x),
|
||||
_serialize_function_param_type = (t, n) =>
|
||||
let
|
||||
funsig = Table.ToRecords(
|
||||
Table.TransformColumns(
|
||||
Table.AddIndexColumn(Record.ToTable(t), "isOptional", 1), {"isOptional", (x) => x > n}
|
||||
)
|
||||
)
|
||||
in
|
||||
"("
|
||||
& List.Accumulate(
|
||||
funsig,
|
||||
"",
|
||||
(seed, item) =>
|
||||
(if seed = "" then "" else seed & ", ")
|
||||
& (if item[isOptional] then "optional " else "")
|
||||
& Serialize.Identifier(item[Name])
|
||||
& " as "
|
||||
& _serialize_typename(item[Value], true)
|
||||
)
|
||||
& ")",
|
||||
_serialize_function_return_type = (x) => _serialize_typename(Type.FunctionReturn(x), true),
|
||||
Serialize = (x) as text =>
|
||||
if x is binary then
|
||||
try Serialize.Binary(x) otherwise "null /*serialize failed*/"
|
||||
else if x is date then
|
||||
try Serialize.Date(x) otherwise "null /*serialize failed*/"
|
||||
else if x is datetime then
|
||||
try Serialize.Datetime(x) otherwise "null /*serialize failed*/"
|
||||
else if x is datetimezone then
|
||||
try Serialize.Datetimezone(x) otherwise "null /*serialize failed*/"
|
||||
else if x is duration then
|
||||
try Serialize.Duration(x) otherwise "null /*serialize failed*/"
|
||||
else if x is function then
|
||||
try Serialize.Function(x) otherwise "null /*serialize failed*/"
|
||||
else if x is list then
|
||||
try Serialize.List(x) otherwise "null /*serialize failed*/"
|
||||
else if x is logical then
|
||||
try Serialize.Logical(x) otherwise "null /*serialize failed*/"
|
||||
else if x is null then
|
||||
try Serialize.Null(x) otherwise "null /*serialize failed*/"
|
||||
else if x is number then
|
||||
try Serialize.Number(x) otherwise "null /*serialize failed*/"
|
||||
else if x is record then
|
||||
try Serialize.Record(x) otherwise "null /*serialize failed*/"
|
||||
else if x is table then
|
||||
try Serialize.Table(x) otherwise "null /*serialize failed*/"
|
||||
else if x is text then
|
||||
try Serialize.Text(x) otherwise "null /*serialize failed*/"
|
||||
else if x is time then
|
||||
try Serialize.Time(x) otherwise "null /*serialize failed*/"
|
||||
else if x is type then
|
||||
try Serialize.Type(x) otherwise "null /*serialize failed*/"
|
||||
else
|
||||
"[#_unable_to_serialize_#]"
|
||||
in
|
||||
try Serialize(value) otherwise "<serialization failed>"
|
||||
in
|
||||
[
|
||||
LogValue = Diagnostics.LogValue,
|
||||
LogValue2 = Diagnostics.LogValue2,
|
||||
LogFailure = Diagnostics.LogFailure,
|
||||
WrapFunctionResult = Diagnostics.WrapFunctionResult,
|
||||
WrapHandlers = Diagnostics.WrapHandlers,
|
||||
ValueToText = Diagnostics.ValueToText
|
||||
]
|
||||
@@ -0,0 +1,17 @@
|
||||
// This is here as reference for copy/pasting wherever there is need for importing pqm files.
|
||||
let
|
||||
Extension.LoadFunction = (fileName as text) =>
|
||||
let
|
||||
binary = Extension.Contents(fileName), asText = Text.FromBinary(binary)
|
||||
in
|
||||
try
|
||||
Expression.Evaluate(asText, #shared) catch (e) =>
|
||||
error
|
||||
[
|
||||
Reason = "Extension.LoadFunction Failure",
|
||||
Message.Format = "Loading '#{0}' failed - '#{1}': '#{2}'",
|
||||
Message.Parameters = {fileName, e[Reason], e[Message]},
|
||||
Detail = [File = fileName, Error = e]
|
||||
]
|
||||
in
|
||||
Extension.LoadFunction
|
||||
@@ -0,0 +1,231 @@
|
||||
let
|
||||
/// COMMON UNIT TESTING CODE
|
||||
Fact = (_subject as text, _expected, _actual) as record =>
|
||||
[
|
||||
expected = try _expected,
|
||||
safeExpected = if expected[HasError] then "Expected : " & @ValueToText(expected[Error]) else expected[
|
||||
Value
|
||||
],
|
||||
actual = try _actual,
|
||||
safeActual = if actual[HasError] then "Actual : " & @ValueToText(actual[Error]) else actual[Value],
|
||||
attempt = try safeExpected = safeActual,
|
||||
result = if attempt[HasError] or not attempt[Value] then "Failure" else "Success",
|
||||
resultOp = if result = "Success" then " = " else " <> ",
|
||||
addendumEvalAttempt = if attempt[HasError] then @ValueToText(attempt[Error]) else "",
|
||||
addendumEvalExpected = try @ValueToText(safeExpected) otherwise "...",
|
||||
addendumEvalActual = try @ValueToText(safeActual) otherwise "...",
|
||||
fact = [
|
||||
Result = result & " " & addendumEvalAttempt,
|
||||
Notes = _subject,
|
||||
Details = " (" & addendumEvalExpected & resultOp & addendumEvalActual & ")"
|
||||
]
|
||||
][fact],
|
||||
Facts = (_subject as text, _predicates as list) => List.Transform(_predicates, each Fact(_subject, _{0}, _{1})),
|
||||
Facts.Summarize = (_facts as list) as table =>
|
||||
[
|
||||
Fact.CountSuccesses = (count, i) =>
|
||||
[
|
||||
result = try i[Result],
|
||||
sum = if result[HasError] or not Text.StartsWith(result[Value], "Success") then count else count + 1
|
||||
][sum],
|
||||
passed = List.Accumulate(_facts, 0, Fact.CountSuccesses),
|
||||
total = List.Count(_facts),
|
||||
format = if passed = total then "All #{0} Passed !!!" else "#{0} Passed - #{1} Failed",
|
||||
result = if passed = total then "Success" else "Failed",
|
||||
rate = Number.IntegerDivide(100 * passed, total),
|
||||
header = [
|
||||
Result = result,
|
||||
Notes = Text.Format(format, {passed, total - passed}),
|
||||
Details = Text.Format("#{0}% success rate", {rate})
|
||||
],
|
||||
report = Table.FromRecords(List.Combine({{header}, _facts}))
|
||||
][report],
|
||||
ValueToText = (value, optional depth) =>
|
||||
let
|
||||
List.TransformAndCombine = (list, transform, separator) =>
|
||||
Text.Combine(List.Transform(list, transform), separator),
|
||||
Serialize.Binary = (x) => "#binary(" & Serialize(Binary.ToList(x)) & ") ",
|
||||
Serialize.Function = (x) =>
|
||||
_serialize_function_param_type(
|
||||
Type.FunctionParameters(Value.Type(x)), Type.FunctionRequiredParameters(Value.Type(x))
|
||||
)
|
||||
& " as "
|
||||
& _serialize_function_return_type(Value.Type(x))
|
||||
& " => (...) ",
|
||||
Serialize.List = (x) => "{" & List.TransformAndCombine(x, Serialize, ", ") & "} ",
|
||||
Serialize.Record = (x) =>
|
||||
"[ "
|
||||
& List.TransformAndCombine(
|
||||
Record.FieldNames(x),
|
||||
(item) => Serialize.Identifier(item) & " = " & Serialize(Record.Field(x, item)),
|
||||
", "
|
||||
)
|
||||
& " ] ",
|
||||
Serialize.Table = (x) =>
|
||||
"#table( type " & _serialize_table_type(Value.Type(x)) & ", " & Serialize(Table.ToRows(x)) & ") ",
|
||||
Serialize.Identifier = Expression.Identifier,
|
||||
Serialize.Type = (x) => "type " & _serialize_typename(x),
|
||||
_serialize_typename = (x, optional funtype as logical) =>
|
||||
/* Optional parameter: Is this being used as part of a function signature? */ let
|
||||
isFunctionType = (x as type) =>
|
||||
try if Type.FunctionReturn(x) is type then true else false otherwise false,
|
||||
isTableType = (x as type) =>
|
||||
try if Type.TableSchema(x) is table then true else false otherwise false,
|
||||
isRecordType = (x as type) =>
|
||||
try if Type.ClosedRecord(x) is type then true else false otherwise false,
|
||||
isListType = (x as type) => try if Type.ListItem(x) is type then true else false otherwise false
|
||||
in
|
||||
if funtype = null and isTableType(x) then
|
||||
_serialize_table_type(x)
|
||||
else if funtype = null and isListType(x) then
|
||||
"{ " & @_serialize_typename(Type.ListItem(x)) & " }"
|
||||
else if funtype = null and isFunctionType(x) then
|
||||
"function " & _serialize_function_type(x)
|
||||
else if funtype = null and isRecordType(x) then
|
||||
_serialize_record_type(x)
|
||||
else if x = type any then
|
||||
"any"
|
||||
else
|
||||
let
|
||||
base = Type.NonNullable(x)
|
||||
in
|
||||
(if Type.IsNullable(x) then "nullable " else "")
|
||||
& (
|
||||
if base = type anynonnull then
|
||||
"anynonnull"
|
||||
else if base = type binary then
|
||||
"binary"
|
||||
else if base = type date then
|
||||
"date"
|
||||
else if base = type datetime then
|
||||
"datetime"
|
||||
else if base = type datetimezone then
|
||||
"datetimezone"
|
||||
else if base = type duration then
|
||||
"duration"
|
||||
else if base = type logical then
|
||||
"logical"
|
||||
else if base = type none then
|
||||
"none"
|
||||
else if base = type null then
|
||||
"null"
|
||||
else if base = type number then
|
||||
"number"
|
||||
else if base = type text then
|
||||
"text"
|
||||
else if base = type time then
|
||||
"time"
|
||||
else if base = type type then
|
||||
"type"
|
||||
else /* Abstract types: */ if base = type function then
|
||||
"function"
|
||||
else if base = type table then
|
||||
"table"
|
||||
else if base = type record then
|
||||
"record"
|
||||
else if base = type list then
|
||||
"list"
|
||||
else
|
||||
"any /*Actually unknown type*/"
|
||||
),
|
||||
_serialize_table_type = (x) =>
|
||||
let
|
||||
schema = Type.TableSchema(x)
|
||||
in
|
||||
"table "
|
||||
& (
|
||||
if Table.IsEmpty(schema) then
|
||||
""
|
||||
else
|
||||
"["
|
||||
& List.TransformAndCombine(
|
||||
Table.ToRecords(Table.Sort(schema, "Position")),
|
||||
each Serialize.Identifier(_[Name]) & " = " & _[Kind],
|
||||
", "
|
||||
)
|
||||
& "] "
|
||||
),
|
||||
_serialize_record_type = (x) =>
|
||||
let
|
||||
flds = Type.RecordFields(x)
|
||||
in
|
||||
if Record.FieldCount(flds) = 0 then
|
||||
"record"
|
||||
else
|
||||
"["
|
||||
& List.TransformAndCombine(
|
||||
Record.FieldNames(flds),
|
||||
(item) =>
|
||||
Serialize.Identifier(item)
|
||||
& "="
|
||||
& _serialize_typename(Record.Field(flds, item)[Type]),
|
||||
", "
|
||||
)
|
||||
& (if Type.IsOpenRecord(x) then ", ..." else "")
|
||||
& "]",
|
||||
_serialize_function_type = (x) =>
|
||||
_serialize_function_param_type(Type.FunctionParameters(x), Type.FunctionRequiredParameters(x))
|
||||
& " as "
|
||||
& _serialize_function_return_type(x),
|
||||
_serialize_function_param_type = (t, n) =>
|
||||
let
|
||||
funsig = Table.ToRecords(
|
||||
Table.TransformColumns(
|
||||
Table.AddIndexColumn(Record.ToTable(t), "isOptional", 1), {"isOptional", (x) => x > n}
|
||||
)
|
||||
)
|
||||
in
|
||||
"("
|
||||
& List.TransformAndCombine(
|
||||
funsig,
|
||||
(item) =>
|
||||
(if item[isOptional] then "optional " else "")
|
||||
& Serialize.Identifier(item[Name])
|
||||
& " as "
|
||||
& _serialize_typename(item[Value], true),
|
||||
", "
|
||||
)
|
||||
& ")",
|
||||
_serialize_function_return_type = (x) => _serialize_typename(Type.FunctionReturn(x), true),
|
||||
Serialize = (x) as text =>
|
||||
if x is binary then
|
||||
try Serialize.Binary(x) otherwise "null /*serialize failed*/"
|
||||
else if x is date then
|
||||
try Expression.Constant(x) otherwise "null /*serialize failed*/"
|
||||
else if x is datetime then
|
||||
try Expression.Constant(x) otherwise "null /*serialize failed*/"
|
||||
else if x is datetimezone then
|
||||
try Expression.Constant(x) otherwise "null /*serialize failed*/"
|
||||
else if x is duration then
|
||||
try Expression.Constant(x) otherwise "null /*serialize failed*/"
|
||||
else if x is function then
|
||||
try Serialize.Function(x) otherwise "null /*serialize failed*/"
|
||||
else if x is list then
|
||||
try Serialize.List(x) otherwise "null /*serialize failed*/"
|
||||
else if x is logical then
|
||||
try Expression.Constant(x) otherwise "null /*serialize failed*/"
|
||||
else if x is null then
|
||||
try Expression.Constant(x) otherwise "null /*serialize failed*/"
|
||||
else if x is number then
|
||||
try Expression.Constant(x) otherwise "null /*serialize failed*/"
|
||||
else if x is record then
|
||||
try Serialize.Record(x) otherwise "null /*serialize failed*/"
|
||||
else if x is table then
|
||||
try Serialize.Table(x) otherwise "null /*serialize failed*/"
|
||||
else if x is text then
|
||||
try Expression.Constant(x) otherwise "null /*serialize failed*/"
|
||||
else if x is time then
|
||||
try Expression.Constant(x) otherwise "null /*serialize failed*/"
|
||||
else if x is type then
|
||||
try Serialize.Type(x) otherwise "null /*serialize failed*/"
|
||||
else
|
||||
"[#_unable_to_serialize_#]"
|
||||
in
|
||||
try Serialize(value) otherwise "<serialization failed>"
|
||||
in
|
||||
[
|
||||
Fact = Fact,
|
||||
Facts = Facts,
|
||||
SummarizeFacts = Facts.Summarize,
|
||||
ValueToText = ValueToText
|
||||
]
|
||||
@@ -0,0 +1,12 @@
|
||||
(Value as text) =>
|
||||
let
|
||||
Solution = Binary.ToText(
|
||||
Binary.FromList(
|
||||
Binary.ToList(Binary.Compress(Text.ToBinary(Value, BinaryEncoding.Base64), Compression.GZip))
|
||||
)
|
||||
)
|
||||
in
|
||||
if Value = null then
|
||||
null
|
||||
else
|
||||
Solution
|
||||
@@ -0,0 +1,23 @@
|
||||
(getNextPage as function) as table =>
|
||||
let
|
||||
listOfPages = List.Generate(
|
||||
() => getNextPage(null),
|
||||
// get the first page of data
|
||||
(lastPage) => lastPage <> null,
|
||||
// stop when the function returns null
|
||||
(lastPage) => getNextPage(lastPage)
|
||||
// pass the previous page to the next function call
|
||||
),
|
||||
// concatenate the pages together
|
||||
tableOfPages = Table.FromList(listOfPages, Splitter.SplitByNothing(), {"Column1"}),
|
||||
firstRow = tableOfPages{0} ?
|
||||
in
|
||||
// if we didn't get back any pages of data, return an empty table
|
||||
// otherwise set the table type based on the columns of the first page
|
||||
if (firstRow = null) then
|
||||
Table.FromRows({})
|
||||
else
|
||||
Value.ReplaceType(
|
||||
Table.ExpandTableColumn(tableOfPages, "Column1", Table.ColumnNames(firstRow[Column1])),
|
||||
Value.Type(firstRow[Column1])
|
||||
)
|
||||
@@ -0,0 +1,21 @@
|
||||
(
|
||||
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
|
||||
@@ -0,0 +1,14 @@
|
||||
(producer as function, interval as function, optional count as number) as any =>
|
||||
let
|
||||
list = List.Generate(
|
||||
() => {0, null},
|
||||
(state) => state{0} <> null and (count = null or state{0} < count),
|
||||
(state) =>
|
||||
if state{1} <> null then
|
||||
{null, state{1}}
|
||||
else
|
||||
{1 + state{0}, Function.InvokeAfter(() => producer(state{0}), interval(state{0}))},
|
||||
(state) => state{1}
|
||||
)
|
||||
in
|
||||
List.Last(list)
|
||||