Monorepo/Service.EDMIService/EDMIService.vb
2020-04-17 11:57:18 +02:00

458 lines
17 KiB
VB.net

Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Logging
Imports DigitalData.Modules.Filesystem
Imports DigitalData.Modules
Imports System.IO
Imports System.ServiceModel
Imports System.Data.SqlClient
<ServiceBehavior(InstanceContextMode:=InstanceContextMode.PerSession)>
Public Class EDMIService
Implements IEDMIService
Public Shared LogConfig As LogConfig
Public Shared MSSQL As MSSQLServer
Public Shared Firebird As Firebird
Public Shared AppConfig As AppConfig
Public Shared Filesystem As Filesystem.File
Public Shared EDMIArchive As EDMI.File.Archive
Public Shared GlobalState As GlobalState
Private ReadOnly _logger As Logger
Private _request As Request = Nothing
Private _debug As Boolean = False
Private _username As String
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
Public 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 "Auth"
Private Function TestUserAuth() As Boolean
Try
'Dim oSQL As String = $"SELECT FNIDB_AUTH_USER('{_username}') FROM RDB$DATABASE;"
'Dim oResult As Boolean = Database.GetScalarValue(oSQL)
'Return oResult
Return True
Catch ex As Exception
_logger.Error(ex)
Return False
End Try
End Function
#End Region
#Region "Heartbeat"
Public Function Heartbeat() As Boolean Implements IEDMIService.Heartbeat
Return True
End Function
#End Region
#Region "Request"
Public Sub CreateRequest(Name As String, Optional Debug As Boolean = False)
_request = New Request(Name, _username, Firebird, Debug)
_debug = Debug
_logger.Info("Creating request {0}/{1}", _request.Name, _request.RequestId)
End Sub
Public Function CreateDatabaseRequest(Name As String, Optional Debug As Boolean = False) As String Implements IEDMIService.CreateDatabaseRequest
CreateRequest(Name, Debug)
Return _request.Name
End Function
Public Sub CloseDatabaseRequest() Implements IEDMIService.CloseDatabaseRequest
If IsNothing(_request) Then
Exit Sub
End If
_logger.Info("Closing request {0}", _request.ToString)
_request.Connection.Close()
_request = Nothing
End Sub
#End Region
#Region "Database"
Private Sub TestRequestCreated()
If IsNothing(_request) Then
Throw New Exceptions.NoRequestException()
End If
End Sub
Public Function ReturnDatatable(SQL As String) As TableResult Implements IEDMIService.ReturnDatatable
Try
TestRequestCreated()
_logger.Info($"ReturnDatatable, SQL: {SQL}")
_request.LogDebug($"ReturnDatatable, SQL: {SQL}")
Dim oResult As DataTable = Firebird.GetDatatableWithConnection(SQL, _request.Connection)
Return New TableResult(oResult)
Catch ex As Exception
_logger.Error(ex)
_request.LogError(ex.Message)
Return New TableResult(ex.Message)
End Try
End Function
Public Function ReturnScalar(SQL As String) As ScalarResult Implements IEDMIService.ReturnScalar
Try
TestRequestCreated()
_logger.Info($"ReturnScalar, SQL: {SQL}")
_request.LogDebug($"ReturnScalar, SQL: {SQL}")
Dim oResult As Object = Firebird.GetScalarValueWithConnection(SQL, _request.Connection)
Return New ScalarResult(oResult)
Catch ex As Exception
_logger.Error(ex)
_request.LogError(ex.Message)
Return New ScalarResult(ex.Message)
End Try
End Function
Public Function ExecuteNonQuery(SQL As String) As NonQueryResult Implements IEDMIService.ExecuteNonQuery
Try
TestRequestCreated()
_logger.Info($"ExecuteNonQuery, SQL: {SQL}")
_request.LogDebug($"ExecuteNonQuery, SQL: {SQL}")
Dim oResult As Boolean = Firebird.ExecuteNonQueryWithConnection(SQL, _request.Connection)
Return New NonQueryResult()
Catch ex As Exception
_logger.Error(ex)
_request.LogError(ex.Message)
Return New NonQueryResult(ex.Message)
End Try
End Function
#End Region
#Region "Document (with FileContainer)"
Public Function NewFile(FileName As String, Contents() As Byte) As DocumentResultOld Implements IEDMIService.NewFile
Try
Dim oContainer As FileContainer
Dim oContainerId As String
If Not TestUserAuth() Then
Throw New Exception($"User {_username} not authorized.")
End If
oContainer = FileContainer.Create(LogConfig, AppConfig.ContainerPassword)
oContainerId = oContainer.ContainerId
_logger.Debug("Container created with id {0}", oContainerId)
Dim oExtension As String = Path.GetExtension(FileName).Substring(1)
_logger.Debug("File extension of file {0} is {1}", FileName, oExtension)
Dim oSQL = $"SELECT FNICM_NEW_DOC('010', '{oContainerId}', '{GetContainerName(oContainerId)}', '{FileName}', '{oExtension}', '{_username}') FROM RDB$DATABASE;"
Dim oDocId As Int64 = Firebird.GetScalarValue(oSQL)
If oDocId = -1 Then
_logger.Warn("Database returned -1 while creating Document Entry. File was not saved!")
Return Nothing
End If
_logger.Debug("Database Entry created with DocId {0}", oDocId)
oContainer.SetFile(Contents, FileName)
oContainer.SaveAs(GetContainerPath(oContainerId))
_logger.Debug("File saved in Container!", FileName)
Dim oDocument = New DocumentObject(oContainerId, oDocId, FileName)
Return New DocumentResultOld(oDocument)
Catch ex As Exception
_logger.Error(ex)
Return New DocumentResultOld(ex.Message)
End Try
End Function
Public Function UpdateFile(DocObject As DocumentObject, Contents() As Byte) As DocumentResultOld Implements IEDMIService.UpdateFile
Try
TestFileExists(DocObject.ContainerId)
' TODO: update db
Dim oFilePath = GetContainerPath(DocObject.ContainerId)
Dim oFileContainer As FileContainer = FileContainer.Load(LogConfig, AppConfig.ContainerPassword, oFilePath)
oFileContainer.SetFile(Contents, oFileContainer.GetFile.FileName)
oFileContainer.Save()
Return New DocumentResultOld(DocObject)
Catch ex As Exception
_logger.Error(ex)
Return Nothing
End Try
End Function
Public Function GetFile(DocObject As DocumentObject) As DocumentResultOld Implements IEDMIService.GetFile
Try
TestFileExists(DocObject.ContainerId)
Dim oContainerPath = GetContainerPath(DocObject.ContainerId)
Dim oContainer As FileContainer = FileContainer.Load(LogConfig, AppConfig.ContainerPassword, oContainerPath)
Dim oContents As Byte() = oContainer.GetFile().Contents
Return New DocumentResultOld(DocObject, oContents)
Catch ex As Exception
_logger.Error(ex)
Return New DocumentResultOld(ex.Message)
End Try
End Function
Public Function DeleteFile(DocObject As DocumentObject) As Boolean Implements IEDMIService.DeleteFile
Try
TestFileExists(DocObject.ContainerId)
Dim oFilePath = GetContainerPath(DocObject.ContainerId)
IO.File.Delete(oFilePath)
'TODO: Delete doc from db
Return True
Catch ex As Exception
_logger.Error(ex)
Return False
End Try
End Function
Private Function GetContainerPath(ContainerId As String) As String
Return Path.Combine(AppConfig.ContainerPath, GetContainerName(ContainerId))
End Function
Private Function GetContainerName(ContainerId As String) As String
Return ContainerId & ".enc"
End Function
Private Sub TestFileExists(ContainerId)
Dim oContainerPath = GetContainerPath(ContainerId)
If Not IO.File.Exists(oContainerPath) Then
Throw New FileNotFoundException("Container existiert nicht", oContainerPath)
End If
End Sub
Public Function GetDocumentByDocumentId(DocumentId As Long) As DocumentResultOld Implements IEDMIService.GetDocumentByDocumentId
Try
Dim oSQL = $"SELECT GUID, CONTAINER_ID, ORIGINAL_FILENAME FROM TBIDB_DOCUMENT WHERE GUID = {DocumentId}"
Dim oTable = Firebird.GetDatatable(oSQL)
If oTable.Rows.Count = 0 Then
Return New DocumentResultOld("Document not found")
End If
Dim oRow As DataRow = oTable.Rows.Item(0)
Dim oDocument As New DocumentObject(
oRow.Item("CONTAINER_ID"),
oRow.Item("GUID"),
oRow.Item("ORIGINAL_FILENAME")
)
TestFileExists(oDocument.ContainerId)
Dim oContainerPath = GetContainerPath(oDocument.ContainerId)
Dim oContainer As FileContainer = FileContainer.Load(LogConfig, AppConfig.ContainerPassword, oContainerPath)
Dim oContents As Byte() = oContainer.GetFile().Contents
Return New DocumentResultOld(oDocument, oContents)
Catch ex As Exception
Return New DocumentResultOld(ex.Message)
End Try
End Function
Public Function GetDocumentByContainerId(ContainerId As String) As DocumentResultOld Implements IEDMIService.GetDocumentByContainerId
Try
Dim oSQL = $"SELECT GUID, CONTAINER_ID, ORIGINAL_FILENAME FROM TBIDB_DOCUMENT WHERE CONTAINER_ID = '{ContainerId}'"
Dim oTable = Firebird.GetDatatable(oSQL)
If oTable.Rows.Count = 0 Then
Return New DocumentResultOld("Document not found")
End If
Dim oRow As DataRow = oTable.Rows.Item(0)
Dim oDocument As New DocumentObject(
oRow.Item("CONTAINER_ID"),
oRow.Item("GUID"),
oRow.Item("ORIGINAL_FILENAME")
)
TestFileExists(oDocument.ContainerId)
Dim oContainerPath = GetContainerPath(oDocument.ContainerId)
Dim oContainer As FileContainer = FileContainer.Load(LogConfig, AppConfig.ContainerPassword, oContainerPath)
Dim oContents As Byte() = oContainer.GetFile().Contents
Return New DocumentResultOld(oDocument, oContents)
Catch ex As Exception
Return New DocumentResultOld(ex.Message)
End Try
End Function
#End Region
#Region "Document"
''' <summary>
''' Imports a file according to ObjectStoreId
''' </summary>
''' <param name="FileName"></param>
''' <param name="Contents"></param>
''' <returns></returns>
Public Function ImportFile(FileName As String, Contents() As Byte, ObjectStoreId As Int64, DocumentType As String, Optional RetentionDays As Int64 = Nothing) As DocumentResult Implements IEDMIService.ImportFile
Dim oObjectStore = GlobalState.ObjectStores.
Where(Function(s) s.Id = ObjectStoreId).
FirstOrDefault()
If oObjectStore Is Nothing Then
Return New DocumentResult($"Object Store with Id [{ObjectStoreId}] does not exist!")
End If
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(DocumentType, FileName)
Dim oAbsolutePath As String = EDMIPath.GetFullPath(DocumentType, FileName)
Dim oDirectoryPath = EDMIPath.GetFullPath(DocumentType)
Dim oDocument = New DocumentResult.DocumentObject With {.FileName = FileName}
Try
Directory.CreateDirectory(oDirectoryPath)
Catch ex As Exception
_logger.Error(ex)
Return New DocumentResult(ex.Message)
End Try
Try
Dim oVersionedFileName As String = Filesystem.GetVersionedFilename(oAbsolutePath)
_logger.Info("ImportFile: Saving file [{0}] to path [{1}]", FileName, oVersionedFileName)
Using oStream = New FileStream(oVersionedFileName, FileMode.CreateNew)
oStream.Write(Contents, 0, 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.GetScalarValue(oCommand, "@IDB_OBJ_ID")
oDocument.FileId = oObjectId
Return New DocumentResult(oDocument)
Catch ex As Exception
_logger.Error(ex)
Return New DocumentResult(ex.Message)
End Try
End Function
Public Function GetFileByObjectId(Data As Messages.DocumentStreamRequest) As Messages.DocumentStreamResponse Implements IEDMIService.GetFileByObjectId
Try
Dim oSQL As String = $"SELECT ObjectStoreId FROM VWIDB_DOC_DATA WHERE IDB_OBJ_ID = {Data.ObjectId}"
Dim oObjectStoreId = MSSQL.GetScalarValue(oSQL)
Dim oObjectStore = GlobalState.ObjectStores.
Where(Function(s) s.Id = oObjectStoreId).
FirstOrDefault()
Dim oSQL2 As String = $"SELECT DocRelativePath FROM VWIDB_DOC_DATA WHERE IDB_OBJ_ID = {Data.ObjectId}"
Dim oPath As String = MSSQL.GetScalarValue(oSQL2)
If IsNothing(oPath) Then
Throw New FaultException($"Object [{Data.ObjectId}] does not exist in database!")
End If
Dim EDMIPath As New EDMI.File.Path(LogConfig, oObjectStore.Path)
Dim oFullPath = EDMIPath.GetFullPathFromRelativePath(oPath)
_logger.Debug("GetFileByObjectId: Loading file [{0}]", oFullPath)
Dim oFileInfo As New FileInfo(oFullPath)
If Not oFileInfo.Exists Then
Throw New FaultException($"Object [{Data.ObjectId}] does not exist on filesystem!")
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 Messages.DocumentStreamResponse() With {
.FileName = oFileInfo.Name,
.FileContents = oDestination
}
Return oMessage
Catch ex As IOException
_logger.Error(ex)
Throw New FaultException($"Object [{Data.ObjectId}] could not be streamed!")
Catch ex As Exception
_logger.Error(ex)
Throw New FaultException(ex.Message)
End Try
End Function
Public Function ListFilesForUser() As Messages.DocumentListResponse Implements IEDMIService.ListFilesForUser
Try
Dim oSQL = $"SELECT * FROM VWIDB_DOC_DATA WHERE ADDED_WHO = UPPER('{_username}')"
Dim oDatatable As DataTable = MSSQL.GetDatatable(oSQL)
oDatatable.TableName = "DocumentList"
Return New Messages.DocumentListResponse() With {
.Datatable = oDatatable
}
Catch ex As Exception
_logger.Error(ex)
Throw New FaultException(ex.Message)
End Try
End Function
#End Region
#Region "Index"
Public Function NewFileIndex(DocObject As DocumentObject, Syskey As String, LanguageCode As String, Value As String) As IndexResult Implements IEDMIService.NewFileIndex
Try
Dim oSQL = $"SELECT FNIDB_NEW_DOC_VALUE({DocObject.DocumentId},'{Syskey}','{LanguageCode}','{Value}','{_username}') FROM RDB$DATABASE;"
Dim oIndexId As Int64 = Firebird.GetScalarValue(oSQL)
Return New IndexResult(oIndexId)
Catch ex As Exception
_logger.Error(ex)
Return New IndexResult(ex.Message)
End Try
End Function
#End Region
End Class