diff --git a/.circleci/config.yml b/.circleci/config.yml index 7c34208..7d12474 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -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,7 +40,7 @@ jobs: - bin/* deploy-connector: docker: - - image: cibuilds/github:0.10 + - image: cibuilds/github:0.13 steps: - attach_workspace: at: ./ diff --git a/README.md b/README.md index 625512c..4e05dfe 100644 --- a/README.md +++ b/README.md @@ -54,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 diff --git a/Speckle.pq b/Speckle.pq index 83b2d28..80929c7 100644 --- a/Speckle.pq +++ b/Speckle.pq @@ -11,7 +11,7 @@ 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( @@ -86,10 +86,10 @@ Speckle = [ ], Key = [ KeyLabel = "Personal Access Token", - Label = "Private stream" + Label = "Private Project" ], Implicit = [ - Label = "Public stream" + Label = "Public Project" ] ], Label = "Speckle" @@ -103,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') " ] ); @@ -132,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') " ] ); diff --git a/assets/resources.resx b/assets/resources.resx index 4cc09ba..0ca4615 100644 --- a/assets/resources.resx +++ b/assets/resources.resx @@ -1,6 +1,6 @@ - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Connect to Speckle by Stream URL - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, + Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, + Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Connect to Speckle by Model URL + + Speckle - - Speckle - Get stream by URL + + Speckle - Get Model by URL - - Connect to Speckle by serer URL, stream ID and branch name + + Connect to Speckle by server URL, stream ID and branch name - + Get the latest commit from a stream's branch - + Speckle - Get Stream branch - + Connect to Speckle by server URL, stream ID and commit ID - + A label - + Speckle - Get Stream commit - + Connect to Speckle by server URL and stream ID - + Speckle - - Speckle - Get Stream by URL [Structured] + + Speckle - Get Model by URL [Structured] - + Speckle - Get Object as NavTable - + Speckle - + Returns a navigation table for a given object - + Traverse an object and populate refs - + Traverse - + Traverse help \ No newline at end of file diff --git a/speckle/GetByUrl.pqm b/speckle/GetByUrl.pqm index 80d3819..f0055ee 100644 --- a/speckle/GetByUrl.pqm +++ b/speckle/GetByUrl.pqm @@ -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" diff --git a/speckle/api/Api.GetAllObjectChildren.pqm b/speckle/api/Api.GetAllObjectChildren.pqm index 3ad98fe..3ded47f 100644 --- a/speckle/api/Api.GetAllObjectChildren.pqm +++ b/speckle/api/Api.GetAllObjectChildren.pqm @@ -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] diff --git a/speckle/helpers/CleanUpObjects.pqm b/speckle/helpers/CleanUpObjects.pqm index c1f7c12..bd5b588 100644 --- a/speckle/helpers/CleanUpObjects.pqm +++ b/speckle/helpers/CleanUpObjects.pqm @@ -14,4 +14,4 @@ ), removed = List.Select(removeTotals, each _[data][speckle_type] <> "Speckle.Core.Models.DataChunk") in - removed + try removed otherwise objects diff --git a/speckle/helpers/ParseStreamUrl.pqm b/speckle/helpers/ParseStreamUrl.pqm index bfa6fee..f858da7 100644 --- a/speckle/helpers/ParseStreamUrl.pqm +++ b/speckle/helpers/ParseStreamUrl.pqm @@ -43,6 +43,7 @@ let let streamId = segments{1}, modelList = segments{3}, + isMultimodel = Text.Contains(modelList, ","), firstModel = Text.Split(modelList, ","){0}, modelAndVersion = Text.Split(firstModel, "@"), modelId = modelAndVersion{0}, @@ -50,14 +51,22 @@ let model = if (modelId <> null) then GetModel(server, streamId, modelId) else null, urlType = GetUrlType(model[name], versionId, null) in - [ - urlType = urlType, - server = server, - id = streamId, - branch = model[name], - commit = versionId, - object = null - ] + 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