EDMI: Add 3-tier database access in DatabaseWithFallback, add client config,

EDMI Service: Version 2.4.0.0
EDMI: API: Version 1.4.0.0
This commit is contained in:
Jonathan Jenne
2022-01-14 11:48:32 +01:00
parent 7d46951b60
commit bcbfba37b2
58 changed files with 1192 additions and 416 deletions

View File

@@ -0,0 +1,33 @@
Namespace Methods.IDB
Public MustInherit Class BaseAttributeValue
End Class
''' <summary>
''' Attribute values supplied by the user
''' </summary>
Public Class UserAttributeValue
Public Property Id As Integer
Public Property Name As String
Public Property Values As List(Of String)
Public Property ControlName As String
Public Overrides Function ToString() As String
Return Name
End Function
Public Overrides Function GetHashCode() As Integer
Return Name.GetHashCode()
End Function
End Class
Public Class AutoAttributeValue
Inherits BaseAttributeValue
End Class
Public Class SystemAttributeValue
Inherits BaseAttributeValue
End Class
End Namespace

View File

@@ -0,0 +1,48 @@
Imports System.Runtime.Serialization
Namespace Methods.IDB
Public Class FileProperties
''' <summary>
''' Absolute filename of the file to be imported
''' </summary>
<DataMember>
Public Property FileName As String
''' <summary>
''' Creation date of the original file from the filesystem
''' </summary>
<DataMember>
Public Property FileCreatedAt As String
''' <summary>
''' Modification date of the original file from the filesystem
''' </summary>
<DataMember>
Public Property FileChangedAt As String
''' <summary>
''' Date for which the file should be show as imported
''' </summary>
<DataMember>
Public Property FileImportedAt As Date
''' <summary>
''' The byte array representing the file contents
''' </summary>
<DataMember>
Public Property FileContents As Byte()
''' <summary>
''' The SHA256 Hash of the file contents
''' </summary>
<DataMember>
Public Property FileChecksum As String
''' <summary>
''' The Raw FileInfo Object
''' </summary>
<DataMember>
Public Property FileInfoRaw As IO.FileInfo
End Class
End Namespace

View File

@@ -0,0 +1,30 @@
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Logging
Imports DigitalData.Services.EDMIService.Methods.IDB.GetAttributeValue
Namespace Methods.IDB.GetAttributeValue
Public Class GetAttributeValueMethod
Inherits BaseMethod
Public Sub New(pLogConfig As LogConfig, pDatabaseIDB As MSSQLServer, pDatabaseECM As MSSQLServer, pGlobalState As GlobalState)
MyBase.New(pLogConfig, pDatabaseIDB, pDatabaseECM, pGlobalState)
End Sub
Public Function Run(pData As GetAttributeValueRequest) As GetAttributeValueResponse
Try
If Helpers.TestObjectIdExists(pData.ObjectId) = False Then
LogAndThrow("ObjectId does not exist!")
End If
Dim oValue As Object
' TODO: Implement GetAttributeValue
Return New GetAttributeValueResponse(pData.ObjectId, oValue)
Catch ex As Exception
Logger.Warn("Error occurred while getting attribute value!")
Return New GetAttributeValueResponse(ex)
End Try
End Function
End Class
End Namespace

View File

@@ -0,0 +1,5 @@
Namespace Methods.IDB.GetAttributeValue
Public Class GetAttributeValueRequest
Public Property ObjectId As Long
End Class
End Namespace

View File

@@ -0,0 +1,26 @@
Imports System.Runtime.Serialization
Namespace Methods.IDB.GetAttributeValue
<Serializable>
<DataContract>
Public Class GetAttributeValueResponse
Inherits Messages.BaseResponse
<DataMember>
Public Property ObjectId As Long
Public Property Value As Object
Public Sub New(pObjectId As Long, pValue As Object)
MyBase.New()
ObjectId = pObjectId
Value = pValue
End Sub
Public Sub New(pException As Exception, Optional pDetails As String = "")
MyBase.New(pException, pDetails)
End Sub
End Class
End Namespace

View File

@@ -0,0 +1,20 @@
Imports System.Runtime.Serialization
Namespace Methods.IDB.GetFileObject
<Serializable>
Public Class FileObject
<DataMember>
Public Property ObjectId As Long
<DataMember>
Public Property AccessRights As String
<DataMember>
Public Property FileExtension As String
<DataMember>
Public Property FileHash As String
<DataMember>
Public Property FileSize As Long
<DataMember>
Public Property FileContents As Byte()
End Class
End Namespace

View File

@@ -0,0 +1,90 @@
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Logging
Imports DigitalData.Modules.Language
Imports System.IO
Namespace Methods.IDB.GetFileObject
Public Class GetFileObjectMethod
Inherits BaseMethod
Public Sub New(pLogConfig As LogConfig, pDatabaseIDB As MSSQLServer, pDatabaseECM As MSSQLServer, pGlobalState As GlobalState)
MyBase.New(pLogConfig, pDatabaseIDB, pDatabaseECM, pGlobalState)
End Sub
Public Function Run(pData As GetFileObjectRequest) As GetFileObjectResponse
Try
If Helpers.TestObjectIdExists(pData.ObjectId) = False Then
LogAndThrow("ObjectId does not exist!")
End If
Dim oSQL = $"SELECT * FROM VWIDB_FILE_OBJECT WHERE IDB_OBJ_ID = {pData.ObjectId}"
Dim oTable = DatabaseIDB.GetDatatable(oSQL)
If oTable Is Nothing OrElse oTable.Rows.Count = 0 Then
LogAndThrow("Error while getting FileObject data!")
End If
Dim oRow As DataRow = oTable.First()
Dim oFileHash As String = oRow.ItemEx("FILE_HASH", "")
Dim oFileSize As Long = oRow.ItemEx(Of Long)("FILE_SIZE", 0)
Dim oFileExtension As String = oRow.ItemEx(Of String)("EXTENSION")
Dim oFileObject As New FileObject With {
.ObjectId = pData.ObjectId,
.FileHash = oFileHash,
.FileSize = oFileSize,
.FileExtension = oFileExtension
}
If pData.LoadFileContents = True Then
Dim oFilePath = oRow.ItemEx("RELPATH", "")
Dim oFileName = oRow.ItemEx("Filename", "")
Dim oFullFileName = Path.Combine(oFilePath, oFileName)
If File.Exists(oFullFileName) = False Then
Throw New FileNotFoundException("FileObject not Found!", oFullFileName)
End If
Dim oContents As Byte() = LoadFileContents(oFullFileName)
If oContents Is Nothing Then
Throw New FileNotFoundException("FileObject not Found!", oFullFileName)
End If
oFileObject.FileContents = oContents
End If
Return New GetFileObjectResponse(oFileObject)
Catch ex As FileNotFoundException
Logger.Error(ex)
Return New GetFileObjectResponse(ex, ex.FileName)
Catch ex As Exception
Logger.Error(ex)
Return New GetFileObjectResponse(ex)
End Try
End Function
Private Function LoadFileContents(pFilePath As String) As Byte()
Try
Using oFileStream As New FileStream(pFilePath, FileMode.Open, FileAccess.Read)
Using oMemoryStream As New MemoryStream()
oFileStream.CopyTo(oMemoryStream)
Dim oContents = oMemoryStream.ToArray()
Return oContents
End Using
End Using
Catch ex As Exception
Logger.Error(ex)
Return Nothing
End Try
End Function
End Class
End Namespace

View File

@@ -0,0 +1,14 @@
Imports System.Runtime.Serialization
Namespace Methods.IDB.GetFileObject
<Serializable>
<DataContract>
Public Class GetFileObjectRequest
<DataMember>
Public Property ObjectId As Long
<DataMember>
Public Property LoadFileContents As Boolean = False
End Class
End Namespace

View File

@@ -0,0 +1,21 @@
Imports System.Runtime.Serialization
Namespace Methods.IDB.GetFileObject
<Serializable>
<DataContract>
Public Class GetFileObjectResponse
Inherits Messages.BaseResponse
<DataMember>
Public Property FileObject As FileObject
Public Sub New(pFileObject As FileObject)
MyBase.New()
FileObject = pFileObject
End Sub
Public Sub New(pException As Exception, Optional pDetails As String = "")
MyBase.New(pException, pDetails)
End Sub
End Class
End Namespace

View File

@@ -0,0 +1,235 @@
Imports System.Data.SqlClient
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Database.MSSQLServer.TransactionMode
Imports DigitalData.Modules.Logging
Imports DigitalData.Services.EDMIService.Methods
Imports DigitalData.Services.EDMIService.GlobalState
Namespace Methods.IDB.NewFile
Public Class NewFileMethod
Inherits BaseMethod
Private ReadOnly Connection As SqlConnection
Private ReadOnly Transaction As SqlTransaction
Public Sub New(pLogConfig As LogConfig, pDatabaseIDB As MSSQLServer, pDatabaseECM As MSSQLServer, pGlobalState As GlobalState)
MyBase.New(pLogConfig, pDatabaseIDB, pDatabaseECM, pGlobalState)
Connection = DatabaseIDB.GetConnection()
Transaction = Connection.BeginTransaction()
End Sub
Public Function Run(pData As NewFile.NewFileRequest) As Methods.IDB.NewFile.NewFileResponse
Dim oFilePath As String = Nothing
Dim oExistingObjectId = TestFileChecksumExists(pData.File.FileChecksum)
If oExistingObjectId > 0 Then
Return New NewFile.NewFileResponse(oExistingObjectId)
End If
Try
Dim oObjectId = NewObjectId(pData.KindType, pData.BusinessEntity, pData.User.UserName)
If oObjectId = 0 Then
LogAndThrow("Could not create new ObjectId!")
End If
' Find ObjectStore by Title
Logger.Debug("Checking for DataStore [{0}].", pData.StoreName)
Dim oStore = GlobalState.ObjectStores.
Where(Function(store) store.Title.Equals(pData.StoreName, StringComparison.OrdinalIgnoreCase)).
SingleOrDefault()
If oStore Is Nothing Then
LogAndThrow($"DataStore [{pData.StoreName}] does not exist. Exiting.")
End If
' Get Store base path
Dim oBasePath As String = oStore.Path
Logger.Debug("Store BasePath is [{0}]", oBasePath)
' Get directory by DateImported or, if not supplied, by current date
Dim oSubDirectory As String
If IsNothing(pData.File.FileImportedAt) Then
oSubDirectory = GetDateSubDirectory(Now)
Else
oSubDirectory = GetDateSubDirectory(pData.File.FileImportedAt)
End If
Logger.Debug("Subdirectory is [{0}]", oSubDirectory)
' Check and create final path, if necessary
Dim oFinalPath = IO.Path.Combine(oBasePath, oSubDirectory)
If Not IO.Directory.Exists(oFinalPath) Then
Try
Logger.Debug("Path does not exist, creating: [{0}]", oFinalPath)
IO.Directory.CreateDirectory(oFinalPath)
Logger.Debug("Created folder [{0}]", oFinalPath)
Catch ex As Exception
LogAndThrow(ex, $"Store Directory [{oFinalPath}] could not be created!")
End Try
End If
Logger.Debug("Final Directory is [{0}]", oFinalPath)
' Get filename
Dim oKeepFileName As Boolean = False
If oStore.IsArchive Then
Logger.Debug("Object Store is an archive: [{0}]", oStore.IsArchive)
oKeepFileName = True
End If
Dim oFileName As String = GetFileObjectFileName(oObjectId, pData.File.FileName, oKeepFileName)
Logger.Debug("Filename is [{0}]", oFileName)
oFilePath = IO.Path.Combine(oFinalPath, oFileName)
Dim oFileObjectInfo As IO.FileInfo = New IO.FileInfo(oFilePath)
Dim oFileObjectSize As Long = pData.File.FileContents.Length
Dim oFileObjectName As String = oFileObjectInfo.Name
Dim oOriginalExtension As String = pData.File.FileInfoRaw.Extension.Substring(1)
Logger.Debug("File Information for [{0}]:", oFileObjectName)
Logger.Debug("Size: [{0}]", oFileObjectSize)
Logger.Debug("Original Extension: [{0}]", oOriginalExtension)
Logger.Debug("Checksum: [{0}]", pData.File.FileChecksum)
Try
Using oStream = New IO.FileStream(oFilePath, IO.FileMode.Create, IO.FileAccess.Write)
Logger.Info("Saving file to path [{0}]", oFilePath)
oStream.Write(pData.File.FileContents, 0, oFileObjectSize)
oStream.Flush(True)
oStream.Close()
End Using
Catch ex As Exception
LogAndThrow(ex, $"Could not write file [{oFilePath}] to disk!")
End Try
'---------------------------------------------------------------------------
Logger.Info("Creating IDB FileObject for ObjectId [{0}].", oObjectId)
' Insert into DB
Dim oSQL As String = $"EXEC PRIDB_NEW_IDBFO
'{oFinalPath}',
'{oFileObjectName}',
'{oOriginalExtension}',
{oFileObjectSize},
'{pData.File.FileChecksum}' ,
'{pData.User.UserName}',
'{oObjectId}',
{oStore.Id}"
Dim oResult As Boolean = DatabaseIDB.ExecuteNonQueryWithConnectionObject(oSQL, Connection, ExternalTransaction, Transaction)
If oResult = False Then
LogAndThrow("IDB FileObject could not be created!")
End If
'---------------------------------------------------------------------------
'TODO: File dates in try catch
Dim oSystemAttributes As New Dictionary(Of String, Object) From {
{"OriginFileName", pData.File.FileName},
{"OriginCreationDatetime", pData.File.FileCreatedAt},
{"OriginChangedDatetime", pData.File.FileChangedAt}
}
For Each oAttribute As KeyValuePair(Of String, Object) In oSystemAttributes
Try
' Dont write empty attributes
If oAttribute.Value Is Nothing Then
Continue For
End If
Dim oSuccess = Helpers.SetAttributeValue(Connection, Transaction, oObjectId, oAttribute.Key, oAttribute.Value, pData.User.Language, pData.User.UserName)
If oSuccess Then
Logger.Debug("System Attribute [{0}] written with value [{1}]", oAttribute.Key, oAttribute.Value)
Else
Logger.Warn("System attribute value could not be written")
End If
Catch ex As Exception
LogAndThrow(ex, $"System attribute [{oAttribute.Key}] could not be written!")
End Try
Next
'---------------------------------------------------------------------------
' Finally, commit the transaction
Transaction?.Commit()
Return New NewFile.NewFileResponse(oObjectId)
Catch ex As Exception
Logger.Warn("Error occurred while creating file!")
Logger.Error(ex)
Logger.Info("Cleaning up files.")
If Not IsNothing(oFilePath) AndAlso IO.File.Exists(oFilePath) Then
Try
IO.File.Delete(oFilePath)
Catch exInner As Exception
Logger.Warn("Error while cleaning up files.")
Logger.Error(exInner)
End Try
End If
Logger.Info("Rolling back transaction.")
Transaction?.Rollback()
Return New NewFile.NewFileResponse(ex)
End Try
End Function
Private Function TestFileChecksumExists(pChecksum As String) As Long
Try
Dim oChecksumSQL = $"SELECT IDB_OBJ_ID FROM TBIDB_FILE_OBJECT WHERE FILE_HASH = '{pChecksum}'"
Dim oExistingObjectId As Long = DatabaseIDB.GetScalarValue(oChecksumSQL)
If oExistingObjectId > 0 Then
Logger.Info("Returning early with ObjectId [{0}] because Checksum [{1}] already exists.", oExistingObjectId, pChecksum)
Return oExistingObjectId
End If
Return Nothing
Catch ex As Exception
Logger.Error(ex)
Return Nothing
End Try
End Function
Private Function NewObjectId(pKindType As String, pBusinessEntity As String, pWho As String) As Long
Try
Dim oNewObjectIdSQL = $"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 As Long = DatabaseIDB.GetScalarValueWithConnectionObject(oNewObjectIdSQL, Connection, ExternalTransaction, Transaction)
Logger.Info("New Object with Id [{0}] created!", oObjectId)
If IsNothing(oObjectId) Then
LogAndThrow("Could not create new ObjectId!")
End If
Return oObjectId
Catch ex As Exception
Logger.Error(ex)
Return Nothing
End Try
End Function
Private Function GetFileObjectFileName(IDB_OBJ_ID As Long, pFilename As String, pKeepFilename As Boolean) As String
' TODO: save actual extensions
If pKeepFilename Then
Return pFilename
Else
Return $"{IDB_OBJ_ID}.ddfo"
End If
End Function
Private Function GetDateSubDirectory(pDate As Date) As String
Return IO.Path.Combine(pDate.ToString("yyyy"), pDate.ToString("MM"), pDate.ToString("dd"))
End Function
End Class
End Namespace

View File

@@ -0,0 +1,38 @@
Imports System.Runtime.Serialization
Imports DigitalData.Modules.ZooFlow.State
Namespace Methods.IDB.NewFile
<Serializable>
<DataContract>
Public Class NewFileRequest
<DataMember>
Public Property File As FileProperties
''' <summary>
''' Name/title of the ObjectStore to save the file to, ex. Work
''' </summary>
<DataMember>
Public Property StoreName As String
''' <summary>
''' The business entity of the file, ex DEFAULT
''' </summary>
<DataMember>
Public Property BusinessEntity As String
''' <summary>
''' The kind of object to be created, ex. DOC
''' </summary>
<DataMember>
Public Property KindType As String
''' <summary>
''' User Importing the file
''' </summary>
''' <returns></returns>
<DataMember>
Public Property User As UserState
End Class
End Namespace

View File

@@ -0,0 +1,22 @@
Imports System.Runtime.Serialization
Namespace Methods.IDB.NewFile
<Serializable>
<DataContract>
Public Class NewFileResponse
Inherits Messages.BaseResponse
<DataMember>
Public Property ObjectId As Long
Public Sub New(pObjectId As Long)
MyBase.New()
ObjectId = pObjectId
End Sub
Public Sub New(pException As Exception, Optional pDetails As String = "")
MyBase.New(pException, pDetails)
End Sub
End Class
End Namespace

View File

@@ -0,0 +1,41 @@
Imports DigitalData.Modules.Logging
Imports DigitalData.Modules.Database
Imports DigitalData.Services.EDMIService.IDB
Imports System.Data.SqlClient
Namespace Methods.IDB.SetAttributeValue
Public Class SetAttributeValueMethod
Inherits BaseMethod
Private Connection As SqlConnection
Private Transaction As SqlTransaction
Public Sub New(pLogConfig As LogConfig, pDatabaseIDB As MSSQLServer, pDatabaseECM As MSSQLServer, pGlobalState As GlobalState)
MyBase.New(pLogConfig, pDatabaseIDB, pDatabaseECM, pGlobalState)
Connection = DatabaseIDB.GetConnection()
Transaction = Connection.BeginTransaction()
End Sub
Public Function Run(pData As SetAttributeValueRequest) As SetAttributeValueResponse
Try
If Helpers.TestObjectIdExists(pData.ObjectId) = False Then
LogAndThrow("ObjectId does not exist!")
End If
Dim oResult = Helpers.SetAttributeValue(Connection, Transaction,
pData.ObjectId, pData.AttributeName, pData.AttributeValue, pData.Language, pData.Who)
If oResult = False Then
LogAndThrow("Attribute value could not be set!")
End If
Return New SetAttributeValueResponse(pData.ObjectId)
Catch ex As Exception
Logger.Warn("Error occurred while setting attribute value!")
Return New SetAttributeValueResponse(ex)
End Try
End Function
End Class
End Namespace

View File

@@ -0,0 +1,25 @@
Imports System.Runtime.Serialization
Namespace Methods.IDB.SetAttributeValue
<Serializable>
<DataContract>
Public Class SetAttributeValueRequest
''' <summary>
''' Object Id
''' </summary>
<DataMember>
Public Property ObjectId As Long
<DataMember>
Public Property AttributeName As String
<DataMember>
Public Property AttributeValue As String
<DataMember>
Public Property Who As Long
<DataMember>
Public Property Language As String
End Class
End Namespace

View File

@@ -0,0 +1,22 @@
Imports System.Runtime.Serialization
Namespace Methods.IDB.SetAttributeValue
<Serializable>
<DataContract>
Public Class SetAttributeValueResponse
Inherits Messages.BaseResponse
<DataMember>
Public Property ObjectId As Long
Public Sub New(pObjectId As Long)
MyBase.New()
ObjectId = pObjectId
End Sub
Public Sub New(pException As Exception, Optional pDetails As String = "")
MyBase.New(pException, pDetails)
End Sub
End Class
End Namespace