Compare commits

...

13 Commits

Author SHA1 Message Date
Alan Rynne 85b9e88fc1 Merge pull request #65 from specklesystems/dev
Update `dev` with changes from `main`
2024-03-14 11:40:48 +01:00
Alan Rynne 92fc894d4b fix: Throw error when multi-model URL is input (#64) 2024-02-26 18:21:23 +01:00
Alan Rynne b033cfa82b CNX-9076: first round of new web app terminology rename (#63)
* fix: first round of new web app terminology rename

* fix: Last refs to commit/branch/stream gone

* fix: Table headers

* ci: Removed unnecessary step and bumped ci ghr image

* Modified publicly visible names on resource file
2024-02-26 17:03:20 +01:00
KatKatKateryna 86cba3ce2b Update README.md (#62)
Updated install instructions as here: https://learn.microsoft.com/en-gb/power-bi/connect-data/desktop-connector-extensibility#certified-connectors
2024-02-26 10:44:10 +01:00
Alan Rynne c752487f9f fix: CNX-7606 Fixes missing Data column error (#61)
* fix: Result will now include parent commit object and no longer assume it will have children

* fix: Keep metadata in joined table
2024-01-12 13:29:42 +01:00
Alan Rynne 95f51b0d32 fix(CNX-8322): Separated completely frontend2 parsing (#59)
* fix: Separated completely frontend2 parsing

This prevents logic from Fe2 leaking into Fe1 and viceversa, which could cause `null` exceptions in some cases.

* CNX-8322 Track in Jira
2023-11-30 10:26:53 +01:00
Alan Rynne 9a5c1283ce Adds Frontend2 URL Support for single model urls (#58)
* feat: Adds FE2 Url support for single model urls

* fix: Minor formatting changes
2023-11-09 16:48:36 +01:00
Alan Rynne da1f0c7b7e feat: Added extensions.json (#57) 2023-10-30 10:58:17 +01:00
Alan Rynne 0bb72f0d9d fix: Get back metrics by using old specific Receive log method (#56) 2023-10-30 10:56:04 +01:00
Ryan Haunfelder 19e64fd4b2 Parses the stream URL in a slightly more robust way. (#55) 2023-10-25 11:26:46 +02:00
Alan Rynne 63f286cd0b bump: 2.15.0-rc 2023-07-07 14:54:01 +02:00
Alan Rynne e89ccbbe1a feat: Adds new ToNameValueRecord function for revit params and DynamicExpand for table columns (#49) 2023-07-07 10:35:38 +02:00
garylzimmer f77e315623 Update README.md (#47)
added note about expected use case and link to relevant github
2023-06-14 13:11:13 +02:00
15 changed files with 324 additions and 210 deletions
+14 -14
View File
@@ -20,18 +20,18 @@ jobs:
- 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
# - 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:
@@ -40,14 +40,14 @@ jobs:
- bin/*
deploy-connector:
docker:
- image: cibuilds/github:0.10
- 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/
ghr -t ${GH_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} ${CIRCLE_TAG} ./bin/Speckle.mez
workflows:
build:
jobs:
+6
View File
@@ -0,0 +1,6 @@
{
"recommendations": [
"ms-dotnettools.csharp",
"powerquery.vscode-powerquery-sdk"
]
}
+2 -1
View File
@@ -25,7 +25,8 @@
"${workspaceFolder}\\tests",
"--prettyPrint"
],
"problemMatcher": []
"problemMatcher": [],
"dependsOn": ["build"]
}
]
}
+2
View File
@@ -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
+53 -29
View File
@@ -1,4 +1,4 @@
[Version = "2.0.0"]
[Version = "2.15.0-rc"]
section Speckle;
AuthAppId = "spklpwerbi";
@@ -11,10 +11,12 @@ Speckle = [
// This is the custom authentication strategy for our Connector
Authentication = [
OAuth = [
Label = "Speckle.xyz",
Label = "Speckle Account",
StartLogin = (clientApplication, dataSourcePath, state, display) =>
let
server = Text.Combine({"https://", Uri.Parts(dataSourcePath)[Host]})
server = Text.Combine(
{Uri.Parts(dataSourcePath)[Scheme], "://", Uri.Parts(dataSourcePath)[Host]}
)
in
[
LoginUri = Text.Combine({server, "authn", "verify", AuthAppId, state}, "/"),
@@ -25,7 +27,9 @@ Speckle = [
],
FinishLogin = (clientApplication, dataSourcePath, context, callbackUri, state) =>
let
server = Text.Combine({"https://", Uri.Parts(dataSourcePath)[Host]}),
server = Text.Combine(
{Uri.Parts(dataSourcePath)[Scheme], "://", Uri.Parts(dataSourcePath)[Host]}
),
Parts = Uri.Parts(callbackUri)[Query],
Source = Web.Contents(
Text.Combine({server, "auth", "token"}, "/"),
@@ -53,7 +57,9 @@ Speckle = [
],
Refresh = (dataSourcePath, refreshToken) =>
let
server = Text.Combine({"https://", Uri.Parts(dataSourcePath)[Host]}),
server = Text.Combine(
{Uri.Parts(dataSourcePath)[Scheme], "://", Uri.Parts(dataSourcePath)[Host]}
),
Source = Web.Contents(
Text.Combine({server, "auth", "token"}, "/"),
[
@@ -80,10 +86,10 @@ Speckle = [
],
Key = [
KeyLabel = "Personal Access Token",
Label = "Private stream"
Label = "Private Project"
],
Implicit = [
Label = "Public stream"
Label = "Public Project"
]
],
Label = "Speckle"
@@ -97,20 +103,16 @@ shared Speckle.GetByUrl.Structured = Value.ReplaceType(
url as (
Uri.Type meta [
Documentation.FieldCaption = "Gets a Speckle Object preserving it's structure",
Documentation.FieldDescription = "The url of a stream in a Speckle server. You can copy it directly from your browser.",
Documentation.SampleValues = {
"https://speckle.xyz/streams/23401adf",
"https://speckle.xyz/streams/23401adf/branches/main"
}
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 stream url:#(lf)
- Stream: will get the latest commit on the 'main' branch (i.e. 'https://speckle.xyz/streams/STREAM_ID')#(lf)
- Branch: will get the latest commit on the specified branch (i.e. 'https://speckle.xyz/streams/STREAM_ID/branches/BRANCH_NAME')#(lf)
- Commit: will get a specific commit from the stream (i.e. 'https://speckle.xyz/streams/STREAM_ID/commits/COMMIT_ID')
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')
"
]
);
@@ -126,21 +128,17 @@ shared Speckle.GetByUrl = Value.ReplaceType(
type function (
url as (
Uri.Type meta [
Documentation.FieldCaption = "Stream URL",
Documentation.FieldDescription = "The url of a stream in a Speckle server. You can copy it directly from your browser.",
Documentation.SampleValues = {
"https://speckle.xyz/streams/23401adf",
"https://speckle.xyz/streams/23401adf/branches/main"
}
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 stream by URL",
Documentation.LongDescription = "Returns a flat list of all objects contained in a specific Speckle stream/branch/commit/object.
Supports all types of stream url:#(lf)
- Stream: will get the latest commit on the 'main' branch (i.e. 'https://speckle.xyz/streams/STREAM_ID')#(lf)
- Branch: will get the latest commit on the specified branch (i.e. 'https://speckle.xyz/streams/STREAM_ID/branches/BRANCH_NAME')#(lf)
- Commit: will get a specific commit from the stream (i.e. 'https://speckle.xyz/streams/STREAM_ID/commits/COMMIT_ID')
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')
"
]
);
@@ -159,7 +157,10 @@ shared Speckle.Api.Fetch = Value.ReplaceType(
);
// Parses a stream url and returns a record with the type and values
shared Speckle.ParseUrl = Speckle.LoadFunction("ParseStreamUrl.pqm");
[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(
@@ -191,3 +192,26 @@ shared Speckle.LoadFunction = (fileName as text) =>
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;
+1 -1
View File
@@ -1,2 +1,2 @@
// Use this file to write queries to test your data connector
let result = Speckle.GetByUrl("https://latest.speckle.dev/streams/c1faab5c62/commits/aefb7a5fff") in result
let result = Speckle.GetByUrl("https://app.speckle.systems/projects/e2988234fb/models/60b2300470@b1f31a351a") in result
+86 -81
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
<!--
Microsoft ResX Schema
Version 2.0
@@ -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,116 +61,119 @@
: 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:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:element name="root" msdata:IsDataSet="true">
<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>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:choice>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<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>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="GetByUrl.Help" xml:space="preserve">
<value>Connect to Speckle by Stream URL</value>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<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>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="GetByUrl.Help" xml:space="preserve">
<value>Connect to Speckle by Model URL</value>
</data>
<data name="GetByUrl.Label" xml:space="preserve">
<data name="GetByUrl.Label" xml:space="preserve">
<value>Speckle</value>
</data>
<data name="GetByUrl.Title" xml:space="preserve">
<value>Speckle - Get stream by URL</value>
<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 serer URL, stream ID and branch name</value>
<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">
<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">
<data name="GetObjFromBranch.Title" xml:space="preserve">
<value>Speckle - Get Stream branch</value>
</data>
<data name="GetObjFromCommit.Help" xml:space="preserve">
<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">
<data name="GetObjFromCommit.Label" xml:space="preserve">
<value>A label</value>
</data>
<data name="GetObjFromCommit.Title" xml:space="preserve">
<data name="GetObjFromCommit.Title" xml:space="preserve">
<value>Speckle - Get Stream commit</value>
</data>
<data name="GetStream.Help" xml:space="preserve">
<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">
<data name="GetStream.Label" xml:space="preserve">
<value>Speckle</value>
</data>
<data name="GetStream.Title" xml:space="preserve">
<value>Speckle - Get Stream by URL [Structured]</value>
<data name="GetStream.Title" xml:space="preserve">
<value>Speckle - Get Model by URL [Structured]</value>
</data>
<data name="GetObjectAsNavTable.Title" xml:space="preserve">
<data name="GetObjectAsNavTable.Title" xml:space="preserve">
<value>Speckle - Get Object as NavTable</value>
</data>
<data name="GetObjectAsNavTable.Label" xml:space="preserve">
<data name="GetObjectAsNavTable.Label" xml:space="preserve">
<value>Speckle</value>
</data>
<data name="GetObjectAsNavTable.Help" xml:space="preserve">
<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">
<data name="Traverse.Title" xml:space="preserve">
<value>Traverse an object and populate refs</value>
</data>
<data name="Traverse.Label" xml:space="preserve">
<data name="Traverse.Label" xml:space="preserve">
<value>Traverse</value>
</data>
<data name="Traverse.Help" xml:space="preserve">
<data name="Traverse.Help" xml:space="preserve">
<value>Traverse help</value>
</data>
</root>
+5 -5
View File
@@ -1,5 +1,6 @@
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"),
@@ -34,10 +35,9 @@ in
GetObjectFromBranch(server, id, stream[branch])
else
GetObjectFromBranch(server, id, "main"),
removeEmpty = Table.RemoveLastN(commitObjectsTable, 1),
addStreamUrl = Table.AddColumn(removeEmpty, "Stream URL", each server & "/streams/" & id),
addStreamUrl = Table.AddColumn(commitObjectsTable, "Model URL", each server & "/streams/" & id),
addParentObjectId = Table.AddColumn(
addStreamUrl, "Commit Object ID", each Value.Metadata(commitObjectsTable)[objectId]
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),
@@ -46,9 +46,9 @@ in
),
final = Table.ReorderColumns(
addSpeckleTypeCol, {
"Stream URL",
"Model URL",
"URL Type",
"Commit Object ID",
"Version Object ID",
"Object ID",
"speckle_type",
"data"
+2 -2
View File
@@ -1,6 +1,6 @@
let
Speckle.Api.Fetch = Extension.LoadFunction("Api.Fetch.pqm"),
Speckle.Log.Receive = Extension.LoadFunction("Log.Receive.pqm"),
Speckle.LogEvent = Extension.LoadFunction("LogEvent.pqm"),
Extension.LoadFunction = (fileName as text) =>
let
binary = Extension.Contents(fileName), asText = Text.FromBinary(binary)
@@ -27,7 +27,7 @@ in
sourceApplication = "PowerBI"
]
],
s = Speckle.Log.Receive(server, commit)
s = Speckle.LogEvent(server, commit)
in
// Read receipts should fail gracefully no matter what
try Speckle.Api.Fetch(s, query, variables)[commitReceive] otherwise false
+25 -14
View File
@@ -1,6 +1,7 @@
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)
@@ -19,17 +20,27 @@ in
// 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 =>
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
) meta [server = server, streamId = streamId, objectId = objectId]
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]
+32
View File
@@ -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
+1 -1
View File
@@ -14,4 +14,4 @@
),
removed = List.Select(removeTotals, each _[data][speckle_type] <> "Speckle.Core.Models.DataChunk")
in
removed
try removed otherwise objects
@@ -1,4 +1,7 @@
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)
@@ -13,12 +16,21 @@ let
Detail = [File = fileName, Error = e]
]
in
(server as text, event as text, properties as record) =>
(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 = event,
properties = properties
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,
+80 -26
View File
@@ -1,27 +1,81 @@
(url as text) as record =>
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,
urlType =
if (commitId <> null) then
"Commit"
else if (objectId <> null) then
"Object"
else if (branchName <> null) then
"Branch"
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
"Stream"
in
[
urlType = urlType,
server = server,
id = streamId,
branch = branchName,
commit = commitId,
object = objectId
]
[
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)
-33
View File
@@ -1,33 +0,0 @@
let
GetApplicationSlug = Extension.LoadFunction("GetApplicationSlug.pqm"),
GetUser = Extension.LoadFunction("Api.GetUser.pqm"),
LogEvent = Extension.LoadFunction("LogEvent.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 text) =>
let
user = GetUser(server),
event = "Receive",
properties = [
server_id = Hash(server),
token = "acd87c5a50b56df91a795e999812a3a4",
hostApp = "powerbi",
sourceHostApp = GetApplicationSlug(commit[sourceApplication]),
sourceHostAppVersion = commit[sourceApplication],
isMultiplayer = commit[authorId] <> user[id]
]
in
LogEvent(server, event, properties)