Add Component to DB programmatically

NicolasArgente
Valued Contributor

Hi there!

I have a Workspace called WK1
I have a Dashboard called DB1
I have a Component called "George" (coz, why not?)

How can I programmatically add George to the DB1 dashboard in WK1 workspace?
Note that i already have other components assigned to this DB1.

Thanks

Connect with me on:
LinkedIn: https://www.linkedin.com/in/nicolas-argente/
Website: https://aiqos.io
If you want to lift yourself up, lift up someone else.
1 ACCEPTED SOLUTION

NicolasArgente
Valued Contributor

Did it! Great thanks to usual gang : @MarcusH  and @RobbSalzmann . 🤗
@MarcusH , I had a look at the solution you mentioned, it helped a lot to put me on the right path!!
Here it is : 

#Region "Imports"
Imports System
Imports System.Collections.Generic
Imports System.Data
Imports System.Data.Common
Imports System.Globalization
Imports System.IO
Imports System.Linq
Imports System.Windows.Forms
Imports Microsoft.VisualBasic
Imports OneStream.Finance.Database
Imports OneStream.Finance.Engine
Imports OneStream.Shared.Common
Imports OneStream.Shared.Database
Imports OneStream.Shared.Engine
Imports OneStream.Shared.Wcf
Imports OneStream.Stage.Database
Imports OneStream.Stage.Engine

Imports System.Deployment.Internal
Imports System.Security.Cryptography
Imports System.Text
Imports System.Text.RegularExpressions
Imports Group = OneStream.Shared.Wcf.Group
#End Region 'Imports

Namespace OneStream.BusinessRule.Extender.zTest4
	Public Class MainClass
		Public Function Main(ByVal si As SessionInfo, ByVal globals As BRGlobals, ByVal api As Object, ByVal args As ExtenderArgs) As Object
			Try
				'------ UPDATE VARIABLES ------
				Dim sWorkspace As String = "AQS Aiqos accelerators (AQS_ACC)"
				Dim sDashboardMaintenanceUnitName As String = "Aiqos Accelerators (AQS_ACC)"
				Dim sDashboard As String = "zTestDB"
				Dim sComponent As String = ""
				'------------------------------
								
				Dim gWorkspaceID As Guid = BRApi.Dashboards.Workspaces.GetWorkspaceIDFromName(si, False, sWorkspace)
				Dim dashboardWcf As New Dashboards()
				Dim gDashboardMaintenanceUnit As OneStream.Shared.Wcf.Dashboard = dashboardWcf.GetDashboardUsingName2(si, False, gWorkspaceID, sDashboardMaintenanceUnitName)
				Dim gComponentID As Guid = Guid.Parse("1c5e1494-76a3-4165-bae0-9febe9d20afb") ''zTestBtn component
				Dim workspace As DashboardWorkspace = dashboardWcf.GetWorkspaceUsingName(si, False, sWorkspace)
				Dim maintUnit As DashboardMaintUnit = BRApi.Dashboards.MaintUnits.GetMaintUnit(si, False, workspace.UniqueID, sDashboardMaintenanceUnitName)

				CreateDashboards(si, dashboardWcf, maintUnit, gWorkspaceID, sDashboard, gComponentID)

				Return Nothing
			Catch ex As Exception
				Throw ErrorHandler.LogWrite(si, New XFException(si, ex))
			End Try
		End Function

		Private Sub CreateDashboards(si As SessionInfo, ByVal dashboardWcf As Dashboards, ByVal maintUnit As DashboardMaintUnit, ByVal gWorkspaceID As Guid, ByVal sDashboard As String, ByVal gComponentID As Guid)
			Try
				'Get the list of embedded dashboard components in OPT
				Dim compSummaryInfoList As List(Of DashboardCompSummaryInfo) = dashboardWcf.GetComponentsInMaintUnit(si, False, maintUnit.UniqueID)

				Dim optDashboard As Dashboard = dashboardWcf.GetDashboardUsingName2(si, False, gWorkspaceID, sDashboard)
				If optDashboard Is Nothing Then
					Throw New Exception("Dashboard zTestDB not found.")
				End If


				Dim dashInfo As DashboardInfo = dashboardWcf.GetDashboardInfo(si, False, optDashboard.UniqueID)
				Dim componentInfoList As List(Of DashboardDbrdCompMemberInfo) = dashInfo.Components
				Dim componentNameList As New List(Of String)()
				'Fill the list of all component names to be embedded in the dashboard
				For Each component In componentInfoList
					componentNameList.Add(component.Component.Name)
				Next

				'Get the IDs of the necessary components in the correct order to be embedded
				Dim componentIDList As New List(Of Guid)()
				For Each compName In componentNameList
					For Each compInfo In compSummaryInfoList
						If compInfo.Name = compName Then
							componentIDList.Add(compInfo.UniqueID)
						End If
					Next
				Next

				componentIDList.Add(gComponentID)

				'Embedd the necessary components in the dashboard
				Dim embeddedComponentList As New List(Of DashboardDbrdCompMember)()
				For Each id In componentIDList
					Dim dashDbrdComp As New DashboardDbrdCompMember(optDashboard.UniqueID, id, 0, String.Empty, String.Empty, String.Empty, String.Empty, XFDockPosition.Left)
					embeddedComponentList.Add(dashDbrdComp)
				Next

				dashboardWcf.SaveDashboardAndComponentMembers(si, False, optDashboard, embeddedComponentList, True)

			Catch ex As Exception
				Throw ErrorHandler.LogWrite(si, New XFException(si, "Unhandled Exception in CreateDashboards() function.", ex.Message, ex.InnerException))
			End Try
		End Sub


	End Class
End Namespace

 

Connect with me on:
LinkedIn: https://www.linkedin.com/in/nicolas-argente/
Website: https://aiqos.io
If you want to lift yourself up, lift up someone else.

View solution in original post

10 REPLIES 10

RobbSalzmann
Valued Contributor

"George" is a great name for a dashboard component!

My approach to this in the absence of an API would be to deconstruct the ApplicationDashboartds xml into an object framework.  Then deserialize the xml into objects.  Create new component objects and add them to the object collection, serialize that back into XML and load into the application.  All of this can be done programmatically.

MarcusH
Contributor III

The OpenPlace Template market place solution might have what you want in the Dashboard Extender OPT_SolutionHelper.:

				'For each componenet that should be copied over, replace the solution code and title where necessary and create the component
				For Each componentInfo In compSummaryInfoList
					If Not doNotCopyList.Contains(componentInfo.Name) Then
						Dim optComponent As DashboardComponent = dashboardWcf.GetComponent(si, False, componentInfo.UniqueID, False)
						Dim newComponent As New DashboardComponent(optComponent) With {
							.Name = .Name.Replace("OPT", solutionCode),
							.EmbeddedDashboardName = .EmbeddedDashboardName.Replace("OPT", solutionCode),
							.BoundParameterName = .BoundParameterName.Replace("OPT", solutionCode),
							.Text = .Text.Replace("OPT", solutionCode),
							.DisplayFormat = .DisplayFormat.Replace("OPT", solutionCode),
							.ParamValueForButtonClick = .ParamValueForButtonClick.Replace("OPT", solutionCode),
							.SelectionChangedSaveArgs = .SelectionChangedSaveArgs.Replace("OPT", solutionCode),
							.SelectionChangedPovArgs = .SelectionChangedPovArgs.Replace("OPT", solutionCode),
							.SelectionChangedTaskArgs = .SelectionChangedTaskArgs.Replace("OPT", solutionCode),
							.DashboardsToRedraw = .DashboardsToRedraw.Replace("OPT", solutionCode),
							.DashboardsToShow = .DashboardsToShow.Replace("OPT", solutionCode),
							.DashboardsToHide = .DashboardsToHide.Replace("OPT", solutionCode),
							.DashboardForDialog = .DashboardForDialog.Replace("OPT", solutionCode),
							.XmlData = .XmlData.Replace("OPT", solutionCode),
							.SelectionChangedNavigationArgs = .SelectionChangedNavigationArgs.Replace("OPT", solutionCode),
							.WorkspaceID = workspace.UniqueID,
							.MaintUnitID = maintUnit.UniqueID,
							.UniqueID = Guid.NewGuid
							}
						newComponent.Text = newComponent.Text.Replace("OpenPlace Template", solutionTitle)
						
						If newComponent.Name.Equals($"fvw_HelpFileViewer_{solutionCode}H") Then
							newComponent.XmlData = newComponent.XmlData.Replace($"{solutionCode}_PV7.1.0._SV100_Instructions.pdf", $"SolutionsGuideTemplate_{solutionCode}H.pdf")
						End If

						dashboardWcf.SaveComponentAndAdapterMembers(si, False, newComponent, Nothing, False, False, TriStateBool.TrueValue)
						componentList.Add(newComponent)
					End If
				Next

 

wnindl
New Contributor III

Thanks for the research Marcus.  Do you think this could be used to extend your research on Strings to add them to a maintenance unit? Either way, I will start experimenting with it tout de suite.

RobbSalzmann
Valued Contributor

I agree with @MarcusH .  OPT_SolutionHelper will help get you there.

If you like working with the XML DOM, this will get you the XML you need:
Within the code above you can use the dashboardsWcf object (of type OneStream.Shared.Wcf.Dashboards) to extract the XML to a string:

XmlExtractOptions options = new XmlExtractOptions(){
    ExtractAllItems = true,
    ExtractUniqueIDs = true
};
String dashboardXml = dashboardWcf.ExtractDashboardsXml(si, false, options, null);

 

NicolasArgente
Valued Contributor

Thanks guys, but it is too dirty to do it that way.
The code below is close, but not working on my server. No clue why.

Dim sdashboardID As Guid = Guid.Parse("7373d1f2-97cf-4c15-8f0b-0ee3f956f713")
Dim sComponentID As Guid = Guid.Parse("1c5e1494-76a3-4165-bae0-9febe9d20afb")
				
				
Dim newRow As New OneStream.Shared.Database.DashboardDbrdCompMemberRow(sdashboardID, sComponentID, 0, "", "", "", "", 0)
Dim newMember As OneStream.Shared.Wcf.DashboardDbrdCompMember = newRow.CreateDashboardDbrdCompMemberObject()

 

Connect with me on:
LinkedIn: https://www.linkedin.com/in/nicolas-argente/
Website: https://aiqos.io
If you want to lift yourself up, lift up someone else.

RobbSalzmann
Valued Contributor

@NicolasArgente this code from above seems fine.  

Dim sdashboardID As Guid = Guid.Parse("7373d1f2-97cf-4c15-8f0b-0ee3f956f713")
Dim sComponentID As Guid = Guid.Parse("1c5e1494-76a3-4165-bae0-9febe9d20afb")
				
				
Dim newRow As New OneStream.Shared.Database.DashboardDbrdCompMemberRow(sdashboardID, sComponentID, 0, "", "", "", "", 0)
Dim newMember As OneStream.Shared.Wcf.DashboardDbrdCompMember = newRow.CreateDashboardDbrdCompMemberObject()

If you Deserialize the XML into simple objects, you can write your own API to do all this and not have to guess at how it's done under the covers.

 

@RobbSalzmann  Have you tested it on your side? On my side, the component does not get assigned into a non default workspace dashboard. 😢 

Connect with me on:
LinkedIn: https://www.linkedin.com/in/nicolas-argente/
Website: https://aiqos.io
If you want to lift yourself up, lift up someone else.

The way its written, your component will always be attached to the dashboard having sdashboardID as its ID.  if you want to move it to another dashboard, use the other dashboard's guid. Is this the dashboard in the not default workspace?

Your code doesn't show a dashboard being added to a workspace so I can't see why it might not work for that.

Also, Workspaces are still a work in progress - you don't mention what version you're working with, with most versions some of the aspects around workspaces is not fully working yet.  This is getting deep into the libraries, and without an API we could be using the objects wrong.  Its why I suggested using the XML approach, We know what you put in the XML will work.  Its just some extra work to write your own API for it.

Are you doing this in a business rule or an assembly?

RobbSalzmann
Valued Contributor

@NicolasArgente I tried out various iterations of your code trying to move or add a button without success.
Maybe the code below can help you get started, I used it to modify the XML and was able to both add and move a button.

before modify xml:

RobbSalzmann_0-1711201261899.png

after modify xml:

RobbSalzmann_1-1711201284312.png

The dashboards XML is well constructed and easy to work with programmatically using standard DOM objects and methods that are actually documented with existing examples.  It's why I suggested go the XML route.

string updatedXML = DashboardModifier.MoveButton(xmlContent, "btn_MoveButton", "FromDashboard", "ToDashboard");
using System;
using System.Linq;
using System.Xml.Linq;

public class DashboardModifier
{
    public static string MoveButton(string xmlContent, string buttonName, string sourceDashboardName, string targetDashboardName)
    {
        XDocument doc = XDocument.Parse(xmlContent);

        var sourceDashboard = doc.Descendants("dashboard")
            .FirstOrDefault(d => (string)d.Attribute("name") == sourceDashboardName);
        var targetDashboard = doc.Descendants("dashboard")
            .FirstOrDefault(d => (string)d.Attribute("name") == targetDashboardName);

        if (sourceDashboard == null || targetDashboard == null)
        {
            return xmlContent; // Return the original content if dashboards are not found
        }

        var buttonToMove = sourceDashboard.Descendants("component")
            .FirstOrDefault(c => (string)c.Attribute("name") == buttonName);

        if (buttonToMove == null)
        {
            return xmlContent; // Return the original content if the button is not found
        }

        buttonToMove.Remove();
        targetDashboard.Element("components").Add(buttonToMove);

        return doc.ToString();
    }
}

 



NicolasArgente
Valued Contributor

Did it! Great thanks to usual gang : @MarcusH  and @RobbSalzmann . 🤗
@MarcusH , I had a look at the solution you mentioned, it helped a lot to put me on the right path!!
Here it is : 

#Region "Imports"
Imports System
Imports System.Collections.Generic
Imports System.Data
Imports System.Data.Common
Imports System.Globalization
Imports System.IO
Imports System.Linq
Imports System.Windows.Forms
Imports Microsoft.VisualBasic
Imports OneStream.Finance.Database
Imports OneStream.Finance.Engine
Imports OneStream.Shared.Common
Imports OneStream.Shared.Database
Imports OneStream.Shared.Engine
Imports OneStream.Shared.Wcf
Imports OneStream.Stage.Database
Imports OneStream.Stage.Engine

Imports System.Deployment.Internal
Imports System.Security.Cryptography
Imports System.Text
Imports System.Text.RegularExpressions
Imports Group = OneStream.Shared.Wcf.Group
#End Region 'Imports

Namespace OneStream.BusinessRule.Extender.zTest4
	Public Class MainClass
		Public Function Main(ByVal si As SessionInfo, ByVal globals As BRGlobals, ByVal api As Object, ByVal args As ExtenderArgs) As Object
			Try
				'------ UPDATE VARIABLES ------
				Dim sWorkspace As String = "AQS Aiqos accelerators (AQS_ACC)"
				Dim sDashboardMaintenanceUnitName As String = "Aiqos Accelerators (AQS_ACC)"
				Dim sDashboard As String = "zTestDB"
				Dim sComponent As String = ""
				'------------------------------
								
				Dim gWorkspaceID As Guid = BRApi.Dashboards.Workspaces.GetWorkspaceIDFromName(si, False, sWorkspace)
				Dim dashboardWcf As New Dashboards()
				Dim gDashboardMaintenanceUnit As OneStream.Shared.Wcf.Dashboard = dashboardWcf.GetDashboardUsingName2(si, False, gWorkspaceID, sDashboardMaintenanceUnitName)
				Dim gComponentID As Guid = Guid.Parse("1c5e1494-76a3-4165-bae0-9febe9d20afb") ''zTestBtn component
				Dim workspace As DashboardWorkspace = dashboardWcf.GetWorkspaceUsingName(si, False, sWorkspace)
				Dim maintUnit As DashboardMaintUnit = BRApi.Dashboards.MaintUnits.GetMaintUnit(si, False, workspace.UniqueID, sDashboardMaintenanceUnitName)

				CreateDashboards(si, dashboardWcf, maintUnit, gWorkspaceID, sDashboard, gComponentID)

				Return Nothing
			Catch ex As Exception
				Throw ErrorHandler.LogWrite(si, New XFException(si, ex))
			End Try
		End Function

		Private Sub CreateDashboards(si As SessionInfo, ByVal dashboardWcf As Dashboards, ByVal maintUnit As DashboardMaintUnit, ByVal gWorkspaceID As Guid, ByVal sDashboard As String, ByVal gComponentID As Guid)
			Try
				'Get the list of embedded dashboard components in OPT
				Dim compSummaryInfoList As List(Of DashboardCompSummaryInfo) = dashboardWcf.GetComponentsInMaintUnit(si, False, maintUnit.UniqueID)

				Dim optDashboard As Dashboard = dashboardWcf.GetDashboardUsingName2(si, False, gWorkspaceID, sDashboard)
				If optDashboard Is Nothing Then
					Throw New Exception("Dashboard zTestDB not found.")
				End If


				Dim dashInfo As DashboardInfo = dashboardWcf.GetDashboardInfo(si, False, optDashboard.UniqueID)
				Dim componentInfoList As List(Of DashboardDbrdCompMemberInfo) = dashInfo.Components
				Dim componentNameList As New List(Of String)()
				'Fill the list of all component names to be embedded in the dashboard
				For Each component In componentInfoList
					componentNameList.Add(component.Component.Name)
				Next

				'Get the IDs of the necessary components in the correct order to be embedded
				Dim componentIDList As New List(Of Guid)()
				For Each compName In componentNameList
					For Each compInfo In compSummaryInfoList
						If compInfo.Name = compName Then
							componentIDList.Add(compInfo.UniqueID)
						End If
					Next
				Next

				componentIDList.Add(gComponentID)

				'Embedd the necessary components in the dashboard
				Dim embeddedComponentList As New List(Of DashboardDbrdCompMember)()
				For Each id In componentIDList
					Dim dashDbrdComp As New DashboardDbrdCompMember(optDashboard.UniqueID, id, 0, String.Empty, String.Empty, String.Empty, String.Empty, XFDockPosition.Left)
					embeddedComponentList.Add(dashDbrdComp)
				Next

				dashboardWcf.SaveDashboardAndComponentMembers(si, False, optDashboard, embeddedComponentList, True)

			Catch ex As Exception
				Throw ErrorHandler.LogWrite(si, New XFException(si, "Unhandled Exception in CreateDashboards() function.", ex.Message, ex.InnerException))
			End Try
		End Sub


	End Class
End Namespace

 

Connect with me on:
LinkedIn: https://www.linkedin.com/in/nicolas-argente/
Website: https://aiqos.io
If you want to lift yourself up, lift up someone else.