Imports System.IO Imports System.ServiceModel Imports DigitalData.Modules.EDMI.API.Constants Imports DigitalData.Modules.EDMI.API.EDMIServiceReference Imports DigitalData.Modules.EDMI.API.Rights Imports DigitalData.Modules.Language.Utils Imports DigitalData.Modules.Logging Public Class Client Private Const INVALID_OBEJCT_ID As Long = 0 Private Const KIND_TYPE_DOC = "DOC" Private ReadOnly _logger As Logger Private ReadOnly _channelFactory As ChannelFactory(Of IEDMIServiceChannel) Private ReadOnly _IPAddressServer As String Private _dummy_table_attributes As DataTable Private _channel As IEDMIServiceChannel Public Class StreamedFile Public Stream As MemoryStream Public FileName As String End Class Public Class FileList Public Datatable As DataTable End Class Public Class DocumentInfo Public FullPath As String Public AccessRight As AccessRight End Class Public Shared Function ParseServiceAddress(AddressWithOptionalPort As String) As Tuple(Of String, Integer) Dim oSplit() As String = AddressWithOptionalPort.Split(":"c) Dim oAppServerAddress As String = oSplit(0) Dim oAppServerPort As Integer If oSplit.Length = 2 Then If Integer.TryParse(oSplit(1), oAppServerPort) Then oAppServerPort = DEFAULT_SERVICE_PORT End If Else oAppServerPort = DEFAULT_SERVICE_PORT End If Return New Tuple(Of String, Integer)(oAppServerAddress, oAppServerPort) End Function ''' ''' Creates a new EDMI Client object ''' ''' LogConfig object ''' The full service url to connect to, for example: net.tcp://1.1.1.1:1111/some/path Public Sub New(LogConfig As LogConfig, ServiceAdress As String) _logger = LogConfig.GetLogger() Try Dim oBinding = Channel.GetBinding() Dim oAddress = New EndpointAddress(ServiceAdress) Dim oFactory = New ChannelFactory(Of IEDMIServiceChannel)(oBinding, oAddress) _channelFactory = oFactory Catch ex As Exception _logger.Error(ex) End Try End Sub ''' ''' Creates a new EDMI Client object ''' ''' LogConfig object ''' The IP address to connect to ''' The Port number to use for the connection Public Sub New(LogConfig As LogConfig, IPAddress As String, PortNumber As Integer) _logger = LogConfig.GetLogger() Try _IPAddressServer = IPAddress Dim oBinding = Channel.GetBinding() Dim oAddress = New EndpointAddress($"net.tcp://{IPAddress}:{PortNumber}/DigitalData/Services/Main") Dim oFactory = New ChannelFactory(Of IEDMIServiceChannel)(oBinding, oAddress) _channelFactory = oFactory Catch ex As Exception _logger.Error(ex) End Try End Sub ''' ''' Connect to the service ''' ''' True if connection was successful, false otherwise Public Function Connect() As Boolean Try _channel = GetChannel() _logger.Debug("Opening channel..") _channel.Open() _logger.Info($"Connection to AppService {_IPAddressServer} successfully established!") Return True Catch ex As Exception _logger.Error(ex) Return False End Try End Function ''' ''' TODO: Creates a new object ''' ''' Public Async Function NewObjectAsync() As Task Throw New NotImplementedException() End Function ''' ''' Import options for NewFileAsync. Contains default values for all options ''' Public Class NewFileOptions Public KeepExtension As Boolean = True Public Username As String = String.Empty Public Language As String = String.Empty End Class ''' ''' Imports a file from a filepath, creating a IDB ObjectId and Filesystem Object ''' ''' Local filepath to a file. ''' Windows username of the user responsible for the import. ''' Date when the file was imported. Can be in the past. ''' Type of ObjectStore. Can be WORK or ARCHIVE. ''' Business entity that the new file object should belong to. ''' Other file import options ''' When local filepath was not found ''' When there was a error in the Service ''' The ObjectId of the newly generated filesystem object Public Async Function NewFileAsync(pFilePath As String, pAddedWho As String, pAddedWhen As Date, pObjectStoreType As String, pBusinessEntity As String, Optional pImportOptions As NewFileOptions = Nothing) As Task(Of Long) Try ' Set default options If pImportOptions Is Nothing Then pImportOptions = New NewFileOptions() End If ' Check if file exists If File.Exists(pFilePath) = False Then Throw New FileNotFoundException("ImportFileAsync: Path does not exist") End If Dim oFileInfo As New FileInfo(pFilePath) Dim oExtension As String = oFileInfo.Extension ' Creating new ObjectId Dim oObjectIdResponse = Await _channel.NewObjectIdAsync(New NewObjectIdRequest With { .BusinessEntity = pBusinessEntity, .KindType = KIND_TYPE_DOC, .Who = pAddedWho }) If oObjectIdResponse.ObjectId = Nothing OrElse oObjectIdResponse.ObjectId = 0 Then Throw New ApplicationException("ImportFileAsync: Could not get ObjectId") End If ' Create new FileObject for ObjectId Dim oFilePathResponse = Await _channel.NewFileObjectAsync(New NewFileObjectRequest With { .DateImported = pAddedWhen, .Extension = oExtension, .KeepExtension = pImportOptions.KeepExtension, .ObjectId = oObjectIdResponse.ObjectId, .StoreType = pObjectStoreType }) If oFilePathResponse.FileObjectPath = Nothing OrElse oFilePathResponse.FileObjectPath = "" Then Throw New ApplicationException("ImportFileAsync: Could not get FileObject Path") End If ' Importing the file now Using oFileStream As New FileStream(pFilePath, FileMode.Open, FileAccess.Read) Using oMemoryStream As New MemoryStream() oFileStream.CopyTo(oMemoryStream) Dim oContents = oMemoryStream.ToArray() Dim oFileImportResponse = Await _channel.ImportFileIntoFileObjectAsync(New ImportFileIntoFileObjectRequest With { .FilePath = oFilePathResponse.FileObjectPath, .ObjectId = oObjectIdResponse.ObjectId, .ObjectStoreType = pObjectStoreType, .Who = pAddedWho, .Contents = oContents }) If oFileImportResponse.Result = False Then Throw New ApplicationException("ImportFileAsync: Could not Import File Contents") End If End Using End Using Return oObjectIdResponse.ObjectId Catch ex As Exception _logger.Error(ex) Return INVALID_OBEJCT_ID End Try End Function Public Class SetVariableValueOptions Public CheckDeleted As Boolean = False Public Username As String = String.Empty Public Language As String = String.Empty End Class ''' ''' Sets a value to an attribute ''' ''' IDB ObjectId ''' Name of the attribute ''' The type of Attribute ''' ''' ''' Public Function SetVariableValue(pObjectId As Long, pAttributeName As String, pAttributeType As AttributeType, pValue As Object, Optional pOptions As SetVariableValueOptions = Nothing) As Boolean Try ' Set default options If pOptions Is Nothing Then pOptions = New SetVariableValueOptions() End If Dim oLanguage = NotNull(pOptions.Language, GetUserLanguage()) Dim oUsername = NotNull(pOptions.Username, Environment.UserName) Try Dim oResponse = _channel.TestObjectIdExists(New TestObjectIdExistsRequest With {.ObjectId = pObjectId}) If oResponse.Exists = False Then Return False End If Catch ex As Exception _logger.Error(ex) Return False End Try ' TODO: Check if Attribute exists Dim oType = pValue.GetType.Name If oType = GetType(DataTable).Name Then Dim oValueTable As DataTable = pValue Dim oCurrentValue As Object Dim oCurrentValueType As String If pOptions.CheckDeleted = True Then Dim oOptions As New GetVariableValueOptions With { .Language = oLanguage, .Username = oUsername } ' Get current value oCurrentValue = GetVariableValue(pObjectId, pAttributeName, pAttributeType, oOptions) ' Get current type oCurrentValueType = oCurrentValue.GetType.Name ' If current value is datatable If oCurrentValueType = GetType(DataTable).Name Then ' Convert value to Datatable Dim oCurrentTable As DataTable = oCurrentValue If oCurrentTable.Rows.Count > 1 Then 'now Checking whether the old row still remains in Vector? If not it will be deleted as it cannot be replaced in multivalues For Each oRow As DataRow In oCurrentTable.Rows Dim oExists As Boolean = False For Each oNewValueRow As DataRow In oValueTable.Rows Dim oInfo = $"Checking oldValue[{oRow.Item(0)}] vs NewValue [{oNewValueRow.Item(1)}]" If oNewValueRow.Item(1).ToString.ToUpper = oRow.Item(0).ToString.ToUpper Then oExists = True Exit For End If Next If oExists = False Then _logger.Debug($"Value [{oRow.Item(0)}] no longer existing in Vector-Attribute [{pAttributeName}] - will be deleted!") DeleteTermObjectFromMetadata(pObjectId, pAttributeName, oRow.Item(0)) End If Next End If Else If oValueTable.Rows.Count > 1 Then Dim oExists As Boolean = False For Each oNewValueRow As DataRow In oValueTable.Rows _logger.Debug($"Checking oldValue[{oCurrentValue}] vs NewValue [{oNewValueRow.Item(1)}]") If oNewValueRow.Item(1).ToString.ToUpper = oCurrentValue.ToString.ToUpper Then oExists = True Exit For End If Next If oExists = False Then _logger.Debug($"Value [{oCurrentValue}] no longer existing in Vector-Attribute [{pAttributeName}] - will be deleted!") DeleteTermObjectFromMetadata(pObjectId, pAttributeName, oCurrentValue) End If Else _logger.Debug($"Value [{oCurrentValue}] of Attribute [{pAttributeName}] obviously was updated during runtime - will be deleted!") DeleteTermObjectFromMetadata(pObjectId, pAttributeName, oCurrentValue) End If End If End If For Each oNewValueRow As DataRow In oValueTable.Rows Dim oSuccess As Boolean = False Dim oSQL = $"DECLARE @NEW_OBJ_MD_ID BIGINT EXEC PRIDB_NEW_OBJ_DATA({pObjectId}, '{pAttributeName}', '{oUsername}', '{oNewValueRow.Item(1)}', '{oLanguage}', 0, @OMD_ID = @NEW_OBJ_MD_ID OUTPUT)" Dim oResult = _channel.ExecuteNonQuery_MSSQL_IDB(oSQL) If Not oResult.OK Then Return False End If Next Return True Else Dim oSql = $"DECLARE @NEW_OBJ_MD_ID BIGINT EXEC PRIDB_NEW_OBJ_DATA({pObjectId}, '{pAttributeName}', '{oUsername}', '{pValue}', '{oLanguage}', 0, @OMD_ID = @NEW_OBJ_MD_ID OUTPUT)" Dim oResult = _channel.ExecuteNonQuery_MSSQL_IDB(oSql) Return oResult.OK End If Catch ex As Exception _logger.Error(ex) Return False End Try End Function Public Class GetVariableValueOptions Public FromIDB As Boolean = False Public Username As String = String.Empty Public Language As String = String.Empty End Class Public Function GetVariableValue(pObjectId As Long, pAttributeName As String, pAttributeType As AttributeType, Optional pOptions As GetVariableValueOptions = Nothing) As Object If pOptions Is Nothing Then pOptions = New GetVariableValueOptions() End If Dim oLanguage = NotNull(pOptions.Language, GetUserLanguage()) Dim oUsername = NotNull(pOptions.Username, Environment.UserName) ' Check if ObjectId exists Try Dim oResponse = _channel.TestObjectIdExists(New TestObjectIdExistsRequest With {.ObjectId = pObjectId}) If oResponse.Exists = False Then Return Nothing End If Catch ex As Exception _logger.Error(ex) Return Nothing End Try ' Get Attributes and Values from Database Dim oTable As DataTable Try Dim oResult As TableResult = _channel.ReturnDatatable_MSSQL_IDB($"EXEC [PRIDB_GET_VALUE_DT]({pObjectId}, '{oLanguage}')") If oResult.OK = False Then Throw New ApplicationException(oResult.ErrorMessage) End If If oResult.Table Is Nothing OrElse oResult.Table.Rows.Count = 0 Then Return Nothing End If oTable = oResult.Table Catch ex As Exception _logger.Error(ex) Return Nothing End Try ' TODO: Check if Attribute exists & REfactor Try Dim oVectorAttribute As Boolean = False Select Case pAttributeType Case AttributeType.VectorInteger oVectorAttribute = True Case AttributeType.VectorString oVectorAttribute = True Case Else oVectorAttribute = False End Select Dim oAttributeValue As Object = Nothing If Not IsNothing(_dummy_table_attributes) Then If oVectorAttribute = True And _dummy_table_attributes.Rows.Count = 1 And pOptions.FromIDB = False Then Try If pAttributeName = "IDBCreatedWhen" Then pAttributeName = "ADDED_WHEN" ElseIf pAttributeName = "IDBCreatedWho" Then pAttributeName = "ADDED_WHO" ElseIf pAttributeName = "IDBChangedWhen" Then pAttributeName = "CHANGED_WHEN" ElseIf pAttributeName = "IDBChangedWho" Then pAttributeName = "CHANGED_WHO" End If oAttributeValue = _dummy_table_attributes.Rows(0).Item(pAttributeName) Catch ex As Exception _logger.Debug($"Error getting Attribute from IDB_DT_DOC_DATA: {ex.Message}") End Try End If End If If Not IsNothing(oAttributeValue) Then Return oAttributeValue Else _logger.Debug($"oAttributeValue for Attribute [{pAttributeName}] is so far nothing..Now trying FNIDB_PM_GET_VARIABLE_VALUE ") End If Dim oFNSQL = $"SELECT * FROM [dbo].[FNIDB_PM_GET_VARIABLE_VALUE] ({pObjectId},'{pAttributeName}','{oLanguage}',CONVERT(BIT,'0'))" Dim oDatatable As TableResult = _channel.ReturnDatatable_MSSQL_IDB(oFNSQL) If oDatatable.OK = False Then Throw New ApplicationException(oDatatable.ErrorMessage) End If If oDatatable.Table.Rows.Count = 1 Then oAttributeValue = oDatatable.Table.Rows.Item(0).Item(0) End If Return oAttributeValue Catch ex As Exception _logger.Error(ex) Return Nothing End Try End Function Private Function DeleteTermObjectFromMetadata(pObjectId As Long, pAttributeName As String, pTerm2Delete As String, Optional pUsername As String = "", Optional pLanguage As String = "") As Boolean Try Dim oLanguage = NotNull(pLanguage, GetUserLanguage()) Dim oUsername = NotNull(pUsername, Environment.UserName) Dim oIdIsForeign As Integer = 1 Dim oDELSQL = $"EXEC PRIDB_DELETE_TERM_OBJECT_METADATA {pObjectId},'{pAttributeName}','{pTerm2Delete}','{oUsername}','{oLanguage}',{oIdIsForeign}" Dim oResult = _channel.ExecuteNonQuery_MSSQL_IDB(oDELSQL) Return oResult.OK Catch ex As Exception _logger.Error(ex) Return False End Try End Function Public Function GetDatatableFromIDB(SQL As String) As TableResult Try Dim oResponse = _channel.ReturnDatatable_MSSQL_IDB(SQL) Return oResponse Catch ex As Exception _logger.Error(ex) Throw ex End Try End Function Public Async Function GetDatatableFromIDBAsync(SQL As String) As Task(Of TableResult) Try Dim oResponse = Await _channel.ReturnDatatable_MSSQL_IDBAsync(SQL) Return oResponse Catch ex As Exception _logger.Error(ex) Throw ex End Try End Function Public Function GetScalarValueFromIDB(SQL As String) As ScalarResult Try Dim oResponse = _channel.ReturnScalar_MSSQL_IDB(SQL) Return oResponse Catch ex As Exception _logger.Error(ex) Throw ex End Try End Function Public Async Function GetScalarValueFromIDBAsync(SQL As String) As Task(Of ScalarResult) Try Dim oResponse = Await _channel.ReturnScalar_MSSQL_IDBAsync(SQL) Return oResponse Catch ex As Exception _logger.Error(ex) Throw ex End Try End Function Public Function GetDatatableByName(DatatableName As String, Optional FilterExpression As String = "", Optional SortByColumn As String = "") As TableResult Try Dim oResponse = _channel.ReturnDatatableFromCache(DatatableName, FilterExpression, SortByColumn) Return oResponse Catch ex As Exception _logger.Error(ex) Throw ex End Try End Function ''' ''' Return infos about a file object ''' ''' ''' ''' Public Function GetDocumentInfo(UserId As Long, ObjectId As Long) As DocumentInfo Try Dim oResponse As DocumentInfoResponse = _channel.GetFileInfoByObjectId(New DocumentInfoRequest With { .ObjectId = ObjectId, .UserId = UserId }) Return New DocumentInfo With { .AccessRight = oResponse.FileRight, .FullPath = oResponse.FullPath } Catch ex As FaultException(Of ObjectDoesNotExistFault) _logger.Error(ex) Throw ex Catch ex As FaultException _logger.Error(ex) Throw ex Catch ex As Exception _logger.Error(ex) Throw ex End Try End Function Public Async Function GetDocumentInfoAsync(UserId As Long, ObjectId As Long) As Task(Of DocumentInfo) Try Dim oParams = New DocumentInfoRequest With { .ObjectId = ObjectId, .UserId = UserId } Dim oResponse As DocumentInfoResponse = Await _channel.GetFileInfoByObjectIdAsync(oParams) Return New DocumentInfo With { .AccessRight = oResponse.FileRight, .FullPath = oResponse.FullPath } Catch ex As Exception _logger.Error(ex) Throw ex End Try End Function ''' ''' Aborts the channel and creates a new connection ''' Public Sub Reconnect() _logger.Warn("Connection faulted. Trying to reconnect..") Try _channel.Abort() _channel = GetChannel() _channel.Open() Catch ex As Exception _logger.Error(ex) End Try End Sub ''' ''' Creates a channel and adds a Faulted-Handler ''' ''' A channel object Private Function GetChannel() As IEDMIServiceChannel Try _logger.Debug("...Creating channel..") Dim oChannel = _channelFactory.CreateChannel() AddHandler oChannel.Faulted, AddressOf Reconnect Return oChannel Catch ex As Exception _logger.Error(ex) Throw ex End Try End Function Private Function GetUserLanguage() As String Return Threading.Thread.CurrentThread.CurrentUICulture.Name End Function End Class