Imports DigitalData.Modules.Database Imports DigitalData.Modules.Logging Imports DigitalData.Modules.Language Imports DigitalData.Modules Imports System.IO Imports System.ServiceModel Imports System.Data.SqlClient Imports System.ServiceModel.Description Imports DigitalData.Services.EDMIService.Messages Imports DigitalData.Modules.EDMI.API.Rights Imports DigitalData.Services.EDMIService.Exceptions Public Class EDMIService Implements IEDMIService Public Shared LogConfig As LogConfig Public Shared MSSQL_ECM As MSSQLServer Public Shared MSSQL_IDB As MSSQLServer Public Shared Firebird As Firebird Public Shared AppConfig As Config Public Shared Filesystem As Filesystem.File Public Shared EDMIArchive As EDMI.File.Archive Public Shared GlobalState As GlobalState Public Shared Scheduler As Scheduler Public Const TBIDB_DOC_INFO = "TBIDB_DOC_INFO" Public Const TBIDB_ACCESSRIGHT = "TBIDB_ACCESSRIGHT" Private ReadOnly _Logger As Logger Private ReadOnly _Debug As Boolean = False Private ReadOnly _Username As String Public Shared Sub Configure(Config As ServiceConfiguration) Dim oBaseAddress = Config.BaseAddresses.Item(0) Dim oBinding = EDMI.API.Channel.GetBinding() Dim oAddress = New EndpointAddress(oBaseAddress) ' See: https://stackoverflow.com/questions/42327988/addserviceendpoint-throws-key-is-null Dim oDescription = ContractDescription.GetContract(GetType(IEDMIService), GetType(EDMIService)) Dim oEndpoint As New ServiceEndpoint(oDescription, oBinding, oAddress) Config.AddServiceEndpoint(oEndpoint) Config.Description.Behaviors.Add(New ServiceDebugBehavior With {.IncludeExceptionDetailInFaults = True}) End Sub Public Sub New() Dim oOperationContext As OperationContext = OperationContext.Current Dim oInstanceContext As InstanceContext = oOperationContext.InstanceContext Dim oUsername = StripDomainFromUsername(oOperationContext.ServiceSecurityContext.WindowsIdentity.Name) _Username = oUsername _Logger = LogConfig.GetLogger() _Logger.Debug("New Request by User [{0}]", _Username) End Sub Private Function StripDomainFromUsername(UserName As String) If UserName.Contains("\") Then Return UserName.Split("\")(1) ElseIf UserName.Contains("@") Then Return UserName.Split("@")(0) Else Return UserName End If End Function #Region "=== Heartbeat ===" Public Function Heartbeat() As Boolean Implements IEDMIService.Heartbeat Return True End Function #End Region #Region "=== Database ===" Public Function ReturnDatatableFromCache(Name As String, FilterExpression As String, SortByColumn As String) As TableResult Implements IEDMIService.ReturnDatatableFromCache Try _Logger.Info($"ReturnDatatableFromCache, Datatable: {Name}") Dim oDataset As DataSet = GlobalState.TableStore Dim oDataTable As DataTable = Nothing _Logger.Debug("DataSet contains [{0}] datatables", oDataset.Tables.Count) If oDataset.Tables.Contains(Name) Then oDataTable = oDataset.Tables.Item(Name).Copy() ' Apply filter and sorting to data Dim oFilterExpression As String = Utils.NotNull(FilterExpression, String.Empty) Dim oSortByColumn As String = Utils.NotNull(SortByColumn, String.Empty) Dim oFilteredRows = oDataTable.Select(oFilterExpression, oSortByColumn) Dim oFilteredTable As DataTable = Nothing If oFilteredRows.Count > 0 Then oFilteredTable = oFilteredRows.CopyToDataTable() oFilteredTable.TableName = Name Else ' Produce empty table oFilteredTable = oDataTable.Clone() oFilteredTable.TableName = Name End If _Logger.Debug("Datatable Stats for [{0}]:", Name) _Logger.Debug("Unfiltered: [{0}] rows", oDataTable.Rows.Count) _Logger.Debug("Filtered: [{0}] rows", oFilteredTable.Rows.Count) Return New TableResult(oFilteredTable) Else _Logger.Warn($"Datatable {Name} does not exist") Dim oDetails As New DataTableDoesNotExistFault(Name) Throw New FaultException(Of DataTableDoesNotExistFault)(oDetails) End If Catch ex As FaultException _Logger.Error(ex) Throw ex Catch ex As Exception _Logger.Error(ex) Dim oDetails As New UnexpectedErrorFault(ex) Throw New FaultException(Of UnexpectedErrorFault)(oDetails, oDetails.ErrorMessage) End Try End Function #End Region #Region "=== Database (MSSQL IDB) ===" Public Function ReturnDatatable_MSSQL_IDB(SQL As String) As TableResult Implements IEDMIService.ReturnDatatable_MSSQL_IDB Try _Logger.Info($"ReturnDatatable_MSSQL_IDB, SQL: {SQL}") Dim oResult As DataTable = MSSQL_IDB.GetDatatable(SQL) oResult.TableName = "DD_RESULT" Return New TableResult(oResult) Catch ex As FaultException _Logger.Error(ex) Throw ex Catch ex As Exception _Logger.Error(ex) Dim oDetails As New UnexpectedErrorFault(ex) Throw New FaultException(Of UnexpectedErrorFault)(oDetails, oDetails.ErrorMessage) End Try End Function Public Function ReturnScalar_MSSQL_IDB(SQL As String) As ScalarResult Implements IEDMIService.ReturnScalar_MSSQL_IDB Try _Logger.Info($"ReturnScalar_MSSQL_IDB, SQL: {SQL}") Dim oResult As Object = MSSQL_IDB.GetScalarValue(SQL) Return New ScalarResult(oResult) Catch ex As FaultException _Logger.Error(ex) Throw ex Catch ex As Exception _Logger.Error(ex) Dim oDetails As New UnexpectedErrorFault(ex) Throw New FaultException(Of UnexpectedErrorFault)(oDetails, oDetails.ErrorMessage) End Try End Function Public Function ExecuteNonQuery_MSSQL_IDB(SQL As String) As NonQueryResult Implements IEDMIService.ExecuteNonQuery_MSSQL_IDB Try _Logger.Info($"ExecuteNonQuery_MSSQL_IDB, SQL: {SQL}") Dim oResult As Boolean = MSSQL_IDB.ExecuteNonQuery(SQL) Return New NonQueryResult() Catch ex As FaultException _Logger.Error(ex) Throw ex Catch ex As Exception _Logger.Error(ex) Dim oDetails As New UnexpectedErrorFault(ex) Throw New FaultException(Of UnexpectedErrorFault)(oDetails, oDetails.ErrorMessage) End Try End Function #End Region #Region "=== Database (MSSQL ECM) ===" Public Function ReturnDatatable_MSSQL_ECM(SQL As String) As TableResult Implements IEDMIService.ReturnDatatable_MSSQL_ECM Try _Logger.Info($"ReturnDatatable_MSSQL_ECM, SQL: {SQL}") Dim oResult As DataTable = MSSQL_ECM.GetDatatable(SQL) oResult.TableName = "DD_RESULT" Return New TableResult(oResult) Catch ex As FaultException _Logger.Error(ex) Throw ex Catch ex As Exception _Logger.Error(ex) Dim oDetails As New UnexpectedErrorFault(ex) Throw New FaultException(Of UnexpectedErrorFault)(oDetails, oDetails.ErrorMessage) End Try End Function Public Function ReturnScalar_MSSQL_ECM(SQL As String) As ScalarResult Implements IEDMIService.ReturnScalar_MSSQL_ECM Try _Logger.Info($"ReturnScalar_MSSQL_ECM, SQL: {SQL}") Dim oResult As Object = MSSQL_ECM.GetScalarValue(SQL) Return New ScalarResult(oResult) Catch ex As FaultException _Logger.Error(ex) Throw ex Catch ex As Exception _Logger.Error(ex) Dim oDetails As New UnexpectedErrorFault(ex) Throw New FaultException(Of UnexpectedErrorFault)(oDetails, oDetails.ErrorMessage) End Try End Function Public Function ExecuteNonQuery_MSSQL_ECM(SQL As String) As NonQueryResult Implements IEDMIService.ExecuteNonQuery_MSSQL_ECM Try _Logger.Info($"ExecuteNonQuery_MSSQL_ECM, SQL: {SQL}") Dim oResult As Boolean = MSSQL_ECM.ExecuteNonQuery(SQL) Return New NonQueryResult() Catch ex As FaultException _Logger.Error(ex) Throw ex Catch ex As Exception _Logger.Error(ex) Dim oDetails As New UnexpectedErrorFault(ex) Throw New FaultException(Of UnexpectedErrorFault)(oDetails, oDetails.ErrorMessage) End Try End Function #End Region #Region "=== Database (Firebird) ===" Public Function ReturnDatatable_Firebird(SQL As String) As TableResult Implements IEDMIService.ReturnDatatable_Firebird Try _Logger.Info($"ReturnDatatable, SQL: {SQL}") Dim oResult As DataTable = Firebird.GetDatatable(SQL) oResult.TableName = "DD_RESULT" Return New TableResult(oResult) Catch ex As FaultException _Logger.Error(ex) Throw ex Catch ex As Exception _Logger.Error(ex) Dim oDetails As New UnexpectedErrorFault(ex) Throw New FaultException(Of UnexpectedErrorFault)(oDetails, oDetails.ErrorMessage) End Try End Function Public Function ReturnScalar_Firebird(SQL As String) As ScalarResult Implements IEDMIService.ReturnScalar_Firebird Try _Logger.Info($"ReturnScalar, SQL: {SQL}") Dim oResult As Object = Firebird.GetScalarValue(SQL) Return New ScalarResult(oResult) Catch ex As FaultException _Logger.Error(ex) Throw ex Catch ex As Exception _Logger.Error(ex) Dim oDetails As New UnexpectedErrorFault(ex) Throw New FaultException(Of UnexpectedErrorFault)(oDetails, oDetails.ErrorMessage) End Try End Function Public Function ExecuteNonQuery_Firebird(SQL As String) As NonQueryResult Implements IEDMIService.ExecuteNonQuery_Firebird Try _Logger.Info($"ExecuteNonQuery, SQL: {SQL}") Dim oResult As Boolean = Firebird.ExecuteNonQuery(SQL) Return New NonQueryResult() Catch ex As FaultException _Logger.Error(ex) Throw ex Catch ex As Exception _Logger.Error(ex) Dim oDetails As New UnexpectedErrorFault(ex) Throw New FaultException(Of UnexpectedErrorFault)(oDetails, oDetails.ErrorMessage) End Try End Function #End Region #Region "=== Document ===" ''' ''' Imports a file according to ObjectStoreId ''' ''' Public Function ImportFile(Data As DocumentImportRequest) As DocumentImportResponse Implements IEDMIService.ImportFile Dim oObjectStore = GlobalState.ObjectStores.First() Dim EDMIPath = New EDMI.File.Path(LogConfig, oObjectStore.Path) ' TODO: ' - Get Object Store -> Object Catalog -> Catalog Path ' - If IS_ARCHIVE = True And RetentionDays <> Nothing: ' DoArchive! ' - Refactor EDMIPath to get ObjectStore at service start up ' and return ObjectStore Path from ObjectStoreId + RelativePath ' VWIDB_OBJECTSTORE Dim oRelativePath As String = EDMIPath.GetRelativePath(Data.DocumentType, Data.FileName) Dim oAbsolutePath As String = EDMIPath.GetFullPath(Data.DocumentType, Data.FileName) Dim oDirectoryPath = EDMIPath.GetFullPath(Data.DocumentType) Try Directory.CreateDirectory(oDirectoryPath) Dim oVersionedFileName As String = Filesystem.GetVersionedFilename(oAbsolutePath) _Logger.Info("ImportFile: Saving file [{0}] to path [{1}]", Data.FileName, oVersionedFileName) Using oStream = New FileStream(oVersionedFileName, FileMode.CreateNew) oStream.Write(Data.Contents, 0, Data.Contents.Length) oStream.Flush(True) oStream.Close() End Using ' insert into db Dim oCommand As New SqlCommand("PRIDB_NEW_DOCUMENT") oCommand.Parameters.AddWithValue("@OBJ_ST_ID", 1) oCommand.Parameters.AddWithValue("@REL_PATH", oRelativePath) oCommand.Parameters.AddWithValue("@WHO", _Username) oCommand.Parameters.AddWithValue("@REF_DOCID", 0) oCommand.Parameters.Add(New SqlParameter("@IDB_OBJ_ID", SqlDbType.BigInt)) Dim oObjectId = MSSQL_IDB.GetScalarValue(oCommand, "@IDB_OBJ_ID") Return New DocumentImportResponse() With {.ObjectId = oObjectId} Catch ex As FaultException _Logger.Error(ex) Throw ex Catch ex As Exception _Logger.Error(ex) Dim oDetails As New UnexpectedErrorFault(ex) Throw New FaultException(Of UnexpectedErrorFault)(oDetails, oDetails.ErrorMessage) End Try End Function Public Function GetFileByObjectId(Data As DocumentStreamRequest) As DocumentStreamResponse Implements IEDMIService.GetFileByObjectId Try Dim oFullPath = GetFullPathForObjectId(Data.ObjectId) If oFullPath = String.Empty Then Dim oDetails As New ObjectDoesNotExistFault(Data.ObjectId) _Logger.Warn("GetFileByObjectId: " & oDetails.ErrorMessage) Throw New FaultException(Of ObjectDoesNotExistFault)(oDetails, oDetails.ErrorMessage) End If _Logger.Debug("GetFileByObjectId: Loading file [{0}]", oFullPath) Dim oFileInfo As New FileInfo(oFullPath) If Not oFileInfo.Exists Then Dim oDetails As New ObjectDoesNotExistFault(Data.ObjectId) _Logger.Warn("GetFileByObjectId: " & oDetails.ErrorMessage) Throw New FaultException(Of ObjectDoesNotExistFault)(oDetails, oDetails.ErrorMessage) End If Dim oDestination As New MemoryStream() Using oSource As FileStream = IO.File.OpenRead(oFullPath) oSource.CopyTo(oDestination) End Using oDestination.Seek(0, SeekOrigin.Begin) Dim oMessage As New DocumentStreamResponse() With { .FileName = oFileInfo.Name, .FileContents = oDestination } Return oMessage Catch ex As FaultException _Logger.Error(ex) Throw ex Catch ex As Exception _Logger.Error(ex) Dim oDetails As New UnexpectedErrorFault(ex) Throw New FaultException(Of UnexpectedErrorFault)(oDetails, oDetails.ErrorMessage) End Try End Function Public Function GetFileInfoByObjectId(Data As DocumentInfoRequest) As DocumentInfoResponse Implements IEDMIService.GetFileInfoByObjectId Try Dim oAccessRight = GetAccessRightForObjectId(Data.UserId, Data.ObjectId) Dim oFullPath = GetFullPathForObjectId(Data.ObjectId) If oFullPath = String.Empty Then Dim oFault As New ObjectDoesNotExistFault(Data.ObjectId) Throw New FaultException(Of ObjectDoesNotExistFault)(oFault, oFault.ErrorMessage) End If Return New DocumentInfoResponse With { .FileRight = oAccessRight, .FullPath = oFullPath } Catch ex As FaultException _Logger.Warn("GetFileInfoByObjectId: " & ex.Message) _Logger.Error(ex) Throw ex Catch ex As Exception Dim oDetails As New UnexpectedErrorFault(ex) _Logger.Warn("GetFileInfoByObjectId: " & oDetails.ErrorMessage) _Logger.Error(ex) Throw New FaultException(Of UnexpectedErrorFault)(oDetails, oDetails.ErrorMessage) End Try End Function Public Function ListFilesForUser() As DocumentListResponse Implements IEDMIService.ListFilesForUser Try Dim oDatatable = GetFileList() Return New DocumentListResponse() With { .Datatable = oDatatable } Catch ex As FaultException _Logger.Error(ex) Throw ex Catch ex As Exception _Logger.Error(ex) Dim oDetails As New UnexpectedErrorFault(ex) Throw New FaultException(Of UnexpectedErrorFault)(oDetails, oDetails.ErrorMessage) End Try End Function Public Function GetFileList() As DataTable Try If Not GlobalState.TableStore.Tables.Contains(TBIDB_DOC_INFO) Then _Logger.Warn("GetFileList: Document info table does not exist!") Return Nothing End If Dim oTable As DataTable = GlobalState.TableStore.Tables.Item(TBIDB_DOC_INFO) Return oTable Catch ex As Exception _Logger.Warn("GetFileList: Unexpected Error while getting file list.") _Logger.Error(ex) Return Nothing End Try End Function Private Function GetFullPathForObjectId(ObjectId As Long) As String Try If Not GlobalState.TableStore.Tables.Contains(TBIDB_DOC_INFO) Then _Logger.Warn("GetFullPathForObjectId: Document info table does not exist!") Return String.Empty End If Dim oTable As DataTable = GlobalState.TableStore.Tables.Item(TBIDB_DOC_INFO) Dim oRows As List(Of DataRow) = oTable.Select($"IDB_OBJ_ID = {ObjectId}").ToList() Dim oFullPath As String If oRows.Count = 0 Then _Logger.Warn("GetFullPathForObjectId: Full path does not exist for object [{0}]", ObjectId) oFullPath = String.Empty Else Dim oRow As DataRow = oRows.First() oFullPath = oRow.Item("FULL_PATH") End If Return oFullPath Catch ex As Exception _Logger.Warn("GetFullPathForObjectId: Unexpected Error while getting full path for object [{0}].", ObjectId) _Logger.Error(ex) Return String.Empty End Try End Function Private Function GetAccessRightForObjectId(UserId As Long, ObjectId As Long) As AccessRight Try If Not GlobalState.TableStore.Tables.Contains(TBIDB_ACCESSRIGHT) Then _Logger.Warn("GetAccessRightForObjectId: Access right table does not exist!") Return AccessRight.VIEW_ONLY End If Dim oTable As DataTable = GlobalState.TableStore.Tables.Item(TBIDB_ACCESSRIGHT) Dim oRows As List(Of DataRow) = oTable.Select($"IDB_OBJ_ID = {ObjectId} AND USR_ID = {UserId}").ToList() Dim oRight As AccessRight If oRows.Count = 0 Then _Logger.Warn("GetAccessRightForObjectId: Access right assignment does not exist for user [{0}] on object [{1}]", UserId, ObjectId) Return AccessRight.VIEW_ONLY Else If oRows.Count > 1 Then _Logger.Warn("GetAccessRightForObjectId: More than one access right assignment found for user [{0}] on object [{1}]", UserId, ObjectId) End If Dim oRow As DataRow = oRows.First() Dim oRightAsInt = oRow.Item("ACCESSRIGHT") oRight = Utils.ToEnum(Of AccessRight)(oRightAsInt) End If Return oRight Catch ex As Exception _Logger.Warn("GetAccessRightForObjectId: Unexpected Error while getting access right for object [{0}].", ObjectId) _Logger.Error(ex) Return AccessRight.VIEW_ONLY End Try End Function Public Function New_FileStore_Object(IDB_OBJ_ID As Long, pStoreType As String, pDate As String) As String Implements IEDMIService.New_FileStore_Object Try Dim oRelpath As String If pStoreType = String.Empty Then pStoreType = ClassConstants.FileStoreWork End If oRelpath = GetFileStorePraefix(pStoreType) If pDate = String.Empty Then oRelpath = GetFolderToday(oRelpath) Else Try Dim odate = CDate(pDate) oRelpath = GetFolderDate(oRelpath, odate) Catch ex As Exception Return "" End Try End If Dim oIDB_FileObject As String If oRelpath = String.Empty Then Return "ERROR" Else _Logger.Debug($"oRelpath is [{oRelpath}]") oIDB_FileObject = Path.Combine(oRelpath, GetDigitalDataFileObject(IDB_OBJ_ID)) End If Return oIDB_FileObject Catch ex As Exception _Logger.Error(ex) Return "" End Try End Function Public Function NewIDB_Object(pKindType As String, pWho As String, pBusinessEntity As String) As String Implements IEDMIService.New_IDB_OBJECT Try Dim oSQL As String = $"DECLARE @NEW_IDB_OBJ_ID BIGINT EXEC PRIDB_NEW_OBJECT '{pKindType}','{pWho}','{pBusinessEntity}',0, @IDB_OBJ_ID = @NEW_IDB_OBJ_ID OUTPUT; SELECT @NEW_IDB_OBJ_ID" Dim oObjectId = MSSQL_IDB.GetScalarValue(oSQL) Return oObjectId Catch ex As Exception _Logger.Error(ex) Return 0 End Try End Function Public Function ImportFileIDBFO(Data As DocumentImportIDBFORequest) As DocumentImportIDBFOResponse Implements IEDMIService.ImportNewIDBFO Dim oObjectStore = GlobalState.ObjectStores.First() Dim EDMIPath = New EDMI.File.Path(LogConfig, oObjectStore.Path) Try _Logger.Info("ImportFile: Saving file to path [{0}]", Data.pIDBFilePath) Using oStream = New FileStream(Data.pIDBFilePath, FileMode.CreateNew) oStream.Write(Data.Contents, 0, Data.Contents.Length) oStream.Flush(True) oStream.Close() End Using ' insert into db Dim oSQL As String = $"EXEC PRIDB_NEW_IDBFO '{Data.pIDBFilePath}','{Data.pWho}','{Data.pIDB_OBJ_ID}',{Data.pObjectStoreID}" Dim oResult As Boolean = MSSQL_IDB.ExecuteNonQuery(oSQL) Return New DocumentImportIDBFOResponse() With {.Result = oResult} Catch ex As FaultException _Logger.Error(ex) Throw ex Catch ex As Exception _Logger.Error(ex) Dim oDetails As New UnexpectedErrorFault(ex) Throw New FaultException(Of UnexpectedErrorFault)(oDetails, oDetails.ErrorMessage) End Try End Function Private Function GetDigitalDataFileObject(IDB_OBJ_ID As Long) As String Return $"{IDB_OBJ_ID}.ddfo" End Function Private Function GetFileStorePraefix(pStoreType As String) As String Dim oObjectStore Dim EDMIPath As String If pStoreType = ClassConstants.FileStoreArchive Then oObjectStore = GlobalState.ObjectStores.Item(0) Else pStoreType = ClassConstants.FileStoreWork oObjectStore = GlobalState.ObjectStores.Item(1) End If _Logger.Debug($"oObjectStore is [{oObjectStore.Path}]") Return oObjectStore.Path End Function Private Function GetFolderToday(pRelationalPath As String) As String Dim oDateSubDirectoryName As String = Now.ToString("yyyy-MM-dd") Dim oFolderToday As String = Path.Combine(pRelationalPath, oDateSubDirectoryName) ' Create the destination directory if it does not exist If Not Directory.Exists(oFolderToday) Then Try Directory.CreateDirectory(oFolderToday) _Logger.Debug($"created NEW todayFolder [{oFolderToday}]") Catch ex As Exception _Logger.Error(ex) Return "" End Try End If Return oFolderToday End Function Private Function GetFolderDate(pRelationalPath As String, pDate As Date) As String Dim oDateSubDirectoryName As String = pDate.ToString("yyyy-MM-dd") Dim oFolderDateSepecial As String = Path.Combine(pRelationalPath, oDateSubDirectoryName) ' Create the destination directory if it does not exist If Not Directory.Exists(oFolderDateSepecial) Then Try Directory.CreateDirectory(oFolderDateSepecial) _Logger.Debug($"created NEW DateSpecialFolder [{oFolderDateSepecial}]") Catch ex As Exception _Logger.Error(ex) Return "" End Try End If Return oFolderDateSepecial End Function #End Region End Class