MONSTER: Rename Monorepo to Modules, only keep Projects under Modules.*

This commit is contained in:
Jonathan Jenne
2022-09-29 13:46:00 +02:00
parent e87b97bfec
commit 042bbce9f4
1557 changed files with 380 additions and 160017 deletions

View File

@@ -0,0 +1,13 @@
Public Class Exceptions
Public Class ZUGFeRDExecption
Inherits ApplicationException
Public ReadOnly Property ErrorType() As ZUGFeRDInterface.ErrorType
Public Sub New(ErrorType As ZUGFeRDInterface.ErrorType, Message As String)
MyBase.New(Message)
_ErrorType = ErrorType
End Sub
End Class
End Class

View File

@@ -0,0 +1,82 @@
Imports System.IO
Imports System.Text.RegularExpressions
Imports DigitalData.Modules.Logging
Public Class FileGroups
Private _logger As Logger
Public Sub New(LogConfig As LogConfig)
_logger = LogConfig.GetLogger()
End Sub
''' <summary>
''' Group files by message id. Message id is extracted from filename.
''' Filename is expected to be in the form: 1234@subdomain.company.com
''' <param name="Files">The list of files to process</param>
''' </summary>
Public Function GroupFiles(Files As List(Of FileInfo)) As Dictionary(Of String, List(Of FileInfo))
Dim oGrouped As New Dictionary(Of String, List(Of FileInfo))
If Files.Count = 0 Then
Return oGrouped
End If
For Each oFile In Files
Dim oMessageId = GetMessageIdFromFileName(oFile.Name)
If oMessageId Is Nothing Then
_logger.Warn("File {0} did not have the required filename-format!", oMessageId)
Continue For
End If
If oGrouped.ContainsKey(oMessageId) Then
oGrouped.Item(oMessageId).Add(oFile)
Else
oGrouped.Add(oMessageId, New List(Of FileInfo) From {oFile})
End If
Next
Return oGrouped
End Function
''' <summary>
''' Group files by message id. Message id is created from `FakeMessageIdDomain` and a random string
''' </summary>
''' <param name="Files">The list of files to process</param>
''' <param name="FakeMessageIdDomain">Arbitrary domain for message id generation. Example: sub.company.com</param>
''' <returns></returns>
Public Function GroupFiles(Files As List(Of FileInfo), FakeMessageIdDomain As String) As Dictionary(Of String, List(Of FileInfo))
Dim oGrouped As New Dictionary(Of String, List(Of FileInfo))
If Files.Count = 0 Then
Return oGrouped
End If
For Each oFile In Files
Dim oIdentifier = Guid.NewGuid().ToString()
Dim oMessageId = $"{oIdentifier}@{FakeMessageIdDomain}"
If oGrouped.ContainsKey(oMessageId) Then
oGrouped.Item(oMessageId).Add(oFile)
Else
oGrouped.Add(oMessageId, New List(Of FileInfo) From {oFile})
End If
Next
Return oGrouped
End Function
Private Function GetMessageIdFromFileName(Filename As String) As String
' Regex to find MessageId
' See also: https://stackoverflow.com/questions/3968500/regex-to-validate-a-message-id-as-per-rfc2822
Dim oRegex = "(((([a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*)|(""(([\x01-\x08\x0B\x0C\x0E-\x1F\x7F]|[\x21\x23-\x5B\x5D-\x7E])|(\\[\x01-\x09\x0B\x0C\x0E-\x7F]))*""))@(([a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*)|(\[(([\x01-\x08\x0B\x0C\x0E-\x1F\x7F]|[\x21-\x5A\x5E-\x7E])|(\\[\x01-\x09\x0B\x0C\x0E-\x7F]))*\]))))~.+"
Dim oMatch = Regex.Match(Filename, oRegex, RegexOptions.IgnoreCase)
If oMatch.Success Then
Dim oMessageId = oMatch.Groups(1).Value
Return oMessageId
Else
Return Nothing
End If
End Function
End Class

View File

@@ -0,0 +1,150 @@
Imports System.Collections.Generic
Imports System.IO
Imports DigitalData.Modules.Logging
Imports GdPicture14
Public Class PDFEmbeds
Private ReadOnly Logger As Logger
Public Const ZUGFERD_XML_FILENAME = "ZUGFeRD-invoice.xml"
Public Const FACTUR_X_XML_FILENAME_FR = "factur-x.xml"
Public Const FACTUR_X_XML_FILENAME_DE = "xrechnung.xml"
Public Class EmbeddedFile
Public FileName As String
Public FileContents As Byte()
End Class
Public Sub New(LogConfig As LogConfig)
Logger = LogConfig.GetLogger
End Sub
''' <summary>
''' Extracts all embedded files from a PDF file.
''' Note: This does NOT filter out `ZUGFeRD-invoice.xml` anymore to allow for a more generic use.
''' </summary>
''' <param name="FilePath">Filepath of the pdf</param>
''' <param name="AllowedExtensions">List of allowed extensions to be extracted</param>
Public Function Extract(FilePath As String, AllowedExtensions As List(Of String)) As List(Of EmbeddedFile)
Dim oFile As New List(Of EmbeddedFile)
Dim oFileInfo As FileInfo
Dim oExtensions = AllowedExtensions.ConvertAll(New Converter(Of String, String)(Function(ext) ext.ToUpper))
Logger.Debug("Extracting embedded files from [{0}]", FilePath)
Try
oFileInfo = New FileInfo(FilePath)
Logger.Debug("Filename: {0}", oFileInfo.Name)
Logger.Debug("Filesize: {0} bytes", oFileInfo.Length)
Logger.Debug("Exists: {0}", oFileInfo.Exists)
Catch ex As Exception
Logger.Warn("File information for [{0}] could not be read!", FilePath)
Logger.Error(ex)
End Try
Try
Using oGDPicturePDF As New GdPicturePDF()
If oGDPicturePDF.LoadFromFile(FilePath, False) = GdPictureStatus.OK Then
oFile = DoExtract(oGDPicturePDF, oExtensions)
Else
Dim oMessage = String.Format("The file [{0}] can't be loaded. Status: [{1}]", FilePath, oGDPicturePDF.GetStat().ToString())
Throw New ApplicationException(oMessage)
End If
End Using
Return oFile
Catch ex As Exception
Logger.Warn("Unexpected Error while Extracting attachments from File [{0}]", FilePath)
Logger.Error(ex)
Return Nothing
End Try
End Function
''' <summary>
''' Extracts all embedded files from a PDF file.
''' Note: This does NOT filter out `ZUGFeRD-invoice.xml` anymore to allow for a more generic use.
''' </summary>
''' <param name="Stream">Filestream of the pdf</param>
''' <param name="AllowedExtensions">List of allowed extensions to be extracted</param>
Public Function Extract(Stream As Stream, AllowedExtensions As List(Of String)) As List(Of EmbeddedFile)
Dim oResults As New List(Of EmbeddedFile)
Dim oExtensions = AllowedExtensions.ConvertAll(New Converter(Of String, String)(Function(ext) ext.ToUpper))
Logger.Debug("Extracting embedded files from stream")
Try
Using oGDPicturePDF As New GdPicturePDF()
If oGDPicturePDF.LoadFromStream(Stream, False) = GdPictureStatus.OK Then
oResults = DoExtract(oGDPicturePDF, oExtensions)
Else
Dim oMessage = String.Format("The filestream can't be loaded. Status: [{0}]", oGDPicturePDF.GetStat().ToString())
Throw New ApplicationException(oMessage)
End If
End Using
Return oResults
Catch ex As Exception
Logger.Warn("Unexpected Error while Extracting attachments from Filestream")
Logger.Error(ex)
Return Nothing
End Try
End Function
Private Function DoExtract(GDPicturePDF As GdPicturePDF, pExtensions As List(Of String)) As List(Of EmbeddedFile)
Dim oResults As New List(Of EmbeddedFile)
Dim oEmbeddedFileCount As Integer = GDPicturePDF.GetEmbeddedFileCount()
If GDPicturePDF.GetStat() = GdPictureStatus.OK Then
Logger.Debug("Embedded file count is: [{0}]", oEmbeddedFileCount)
If oEmbeddedFileCount > 0 Then
For oIndex = 0 To oEmbeddedFileCount - 1
Dim oFileName As String = GDPicturePDF.GetEmbeddedFileName(oIndex)
If GDPicturePDF.GetStat() = GdPictureStatus.OK Then
Logger.Debug("Extracting embedded file [{0}]", oFileName)
Dim oExtension = New FileInfo(oFileName).Extension.ToUpper.Substring(1)
If pExtensions.Contains(oExtension) Then
Dim oFileSize As Integer = GDPicturePDF.GetEmbeddedFileSize(oIndex)
If GDPicturePDF.GetStat() = GdPictureStatus.OK Then
Logger.Debug("Filesize of embedded file is [{0}]", oFileSize)
Dim oFileData As Byte() = New Byte(oFileSize) {}
Dim oStatus As GdPictureStatus = GDPicturePDF.ExtractEmbeddedFile(oIndex, oFileData)
If oStatus = GdPictureStatus.OK Then
Logger.Debug("Embedded file [{0}] extracted sucessfully!", oFileName)
oResults.Add(New EmbeddedFile() With {
.FileContents = oFileData,
.FileName = oFileName
})
Else
Logger.Error("The embedded file [{0}] has failed to extract. Status: {1}", oFileName, GDPicturePDF.GetStat().ToString())
Continue For
End If
Else
Logger.Error("An error occurred getting the file size for [{0}]. Status: {1}", oFileName, GDPicturePDF.GetStat().ToString())
Continue For
End If
Else
Logger.Debug("File [{0}] was skipped because its extension [{1}] is not allowed.", oFileName, oExtension)
Continue For
End If
Else
Logger.Error("An error occurred getting the file name for [{0}]. Status: {1}", oFileName, GDPicturePDF.GetStat().ToString())
Continue For
End If
Next
End If
Return oResults
Else
Dim oMessage = String.Format("An error occurred getting the number of embedded files. Status: {0}", GDPicturePDF.GetStat().ToString())
Throw New ApplicationException(oMessage)
End If
End Function
End Class

View File

@@ -0,0 +1,325 @@
Imports System.Reflection
Imports System.Text.RegularExpressions
Imports DigitalData.Modules.Logging
Public Class PropertyValues
Private _logger As Logger
Private _logConfig As LogConfig
Private _indexPattern = "\((\d+)\)"
Private _indexRegex As New Regex(_indexPattern)
Public Sub New(LogConfig As LogConfig)
_logConfig = LogConfig
_logger = LogConfig.GetLogger()
End Sub
Public Class CheckPropertyValuesResult
Public MissingProperties As New List(Of String)
Public ValidProperties As New List(Of ValidProperty)
End Class
Public Class ValidProperty
Public MessageId As String
Public TableName As String
Public TableColumn As String
Public ISRequired As Boolean
Public GroupCounter As Integer = -1
Public Description As String
Public Value As String
End Class
Public Function CheckPropertyValues(Document As Object, PropertyMap As Dictionary(Of String, XmlItemProperty), MessageId As String) As CheckPropertyValuesResult
Dim oGlobalGroupCounter = 0
Dim oMissingProperties As New List(Of String)
Dim oResult As New CheckPropertyValuesResult()
' PropertyMap items with `IsGrouped = False` are handled normally
Dim oDefaultProperties As Dictionary(Of String, XmlItemProperty) = PropertyMap.
Where(Function(Item) Item.Value.IsGrouped = False).
ToDictionary(Function(Item) Item.Key,
Function(Item) Item.Value)
_logger.Debug("Found {0} default properties.", oDefaultProperties.Count)
' PropertyMap items with `IsGrouped = True` are grouped by group scope
Dim oGroupedProperties = PropertyMap.
Where(Function(Item) Item.Value.IsGrouped = True).
ToLookup(Function(Item) Item.Value.GroupScope, ' Lookup key is group scope
Function(Item) Item)
_logger.Debug($"Found [{PropertyMap.Count - oDefaultProperties.Count}] properties grouped in [{oGroupedProperties.Count}] group(s)")
' Iterate through groups to get group scope and group items
For Each oGroup In oGroupedProperties
Dim oGroupScope As String = oGroup.Key
Dim oPropertyList As New Dictionary(Of XmlItemProperty, List(Of Object))
Dim oRowCount = 0
_logger.Debug($"Fetching Property values for group [{oGroupScope}].")
' get properties as a nested object, see `oPropertyList`
For Each oProperty As KeyValuePair(Of String, XmlItemProperty) In oGroup
Dim oPropertyValues As List(Of Object)
_logger.Debug($"Fetching value for itemSpecification [{oProperty.Value.TableColumn}].")
Try
oPropertyValues = GetPropValue(Document, oProperty.Key)
Catch ex As Exception
_logger.Warn($"{MessageId} - Unknown error occurred while fetching property/TColumn [{0}] in group [{1}]:", oProperty.Value.TableColumn, oGroupScope)
_logger.Error(ex)
oPropertyValues = New List(Of Object)
End Try
' Flatten result value
oPropertyValues = GetFinalPropValue(oPropertyValues)
' Add to list
oPropertyList.Add(oProperty.Value, oPropertyValues)
' check the first batch of values to determine the row count
If oRowCount = 0 Then
oRowCount = oPropertyValues.Count
End If
Next
' Structure of oPropertyList
' [ # Propertyname # Row 1 # Row 2
' PositionsMenge: [BilledQuantity1, BilledQuantity2, ...],
' PositionsSteuersatz: [ApplicablePercent1, ApplicablePercent2, ...],
' ...
' ]
For oRowIndex = 0 To oRowCount - 1
_logger.Debug("Processing row {0}", oRowIndex)
For Each oColumn As KeyValuePair(Of XmlItemProperty, List(Of Object)) In oPropertyList
Dim oTableName As String = oColumn.Key.TableName
Dim oTableColumn As String = oColumn.Key.TableColumn
Dim oIsRequired As Boolean = oColumn.Key.IsRequired
Dim oPropertyDescription As String = oColumn.Key.Description
Dim oRowCounter = oRowIndex + oGlobalGroupCounter + 1
If IsNothing(oRowCounter) Then
End If
' Returns nothing if oColumn.Value contains an empty list
Dim oPropertyValue = oColumn.Value.ElementAtOrDefault(oRowIndex)
_logger.Debug("Processing itemSpecification *TableColumn* [{0}].", oTableColumn)
If IsNothing(oPropertyValue) OrElse String.IsNullOrEmpty(oPropertyValue) Then
If oColumn.Key.IsRequired Then
_logger.Warn($"{MessageId} # oPropertyValue for specification [{oTableColumn}] is empty or not found but is required. Continuing with Empty String.")
oResult.MissingProperties.Add(oPropertyDescription)
Else
_logger.Debug($"{MessageId} # oPropertyValue for specification [{oTableColumn}] is empty or not found. Continuing with Empty String.")
End If
oPropertyValue = String.Empty
End If
_logger.Debug("ItemSpecification [{0}] has value '{1}'", oTableColumn, oPropertyValue)
oResult.ValidProperties.Add(New ValidProperty() With {
.MessageId = MessageId,
.Description = oPropertyDescription,
.Value = oPropertyValue,
.GroupCounter = oRowCounter,
.TableName = oTableName,
.TableColumn = oTableColumn,
.ISRequired = oIsRequired
})
Next
Next
oGlobalGroupCounter += oRowCount
Next
' Iterate through default properties
For Each oItem As KeyValuePair(Of String, XmlItemProperty) In oDefaultProperties
Dim oPropertyValueList As List(Of Object)
Dim oTableColumn As String = oItem.Value.TableColumn
Dim oPropertyValue As Object = Nothing
Dim oTableName = oItem.Value.TableName
Dim oIsRequired = oItem.Value.IsRequired
Dim oDescription = oItem.Value.Description
Try
oPropertyValueList = GetPropValue(Document, oItem.Key)
Catch ex As Exception
_logger.Warn("{2} # Unknown error occurred while fetching specification [{0}] in group [{1}]:", oTableColumn, oItem.Value.GroupScope, MessageId)
_logger.Error(ex)
oPropertyValueList = New List(Of Object)
End Try
Try
If IsNothing(oPropertyValueList) Then
oPropertyValue = Nothing
ElseIf TypeOf oPropertyValueList Is List(Of Object) Then
Select Case oPropertyValueList.Count
Case 0
oPropertyValue = Nothing
Case Else
Dim oList As List(Of Object) = DirectCast(oPropertyValueList, List(Of Object))
oPropertyValue = oList.Item(0)
' This should hopefully show config errors
If TypeOf oPropertyValue Is List(Of Object) Then
_logger.Warn("Item with TableColumn [{0}] may be configured incorrectly", oTableColumn)
oPropertyValue = Nothing
End If
End Select
End If
Catch ex As Exception
_logger.Warn("Unknown error occurred while processing specification [{0}]:", oTableColumn)
_logger.Error(ex)
oPropertyValue = Nothing
End Try
If IsNothing(oPropertyValue) OrElse String.IsNullOrEmpty(oPropertyValue) Then
If oItem.Value.IsRequired Then
_logger.Warn("{0} # Specification [{1}] is empty, but marked as required! Skipping.", MessageId, oTableColumn)
oResult.MissingProperties.Add(oTableColumn)
Continue For
Else
_logger.Debug("{0} # oPropertyValue for specification [{1}] is empty or not found. Skipping.", MessageId, oTableColumn)
Continue For
End If
End If
oResult.ValidProperties.Add(New ValidProperty() With {
.MessageId = MessageId,
.Description = oDescription,
.Value = oPropertyValue,
.TableName = oTableName,
.TableColumn = oTableColumn,
.ISRequired = oIsRequired
})
Next
Return oResult
End Function
Public Function GetPropValue(Obj As Object, PropertyName As String) As List(Of Object)
Dim oNameParts As String() = PropertyName.Split("."c)
If IsNothing(Obj) Then
_logger.Debug("`Obj` is Nothing. Exiting.")
Return New List(Of Object)
End If
If oNameParts.Length = 1 Then
Dim oPropInfo As PropertyInfo = Obj.GetType().GetProperty(PropertyName)
If IsNothing(oPropInfo) Then
_logger.Debug("Property [{0}] does not exist(1).", PropertyName)
Return New List(Of Object)
Else
Dim oPropValue = oPropInfo.GetValue(Obj, Nothing)
Return New List(Of Object) From {oPropValue}
End If
End If
For Each oPart As String In oNameParts
Dim oType As Type = Obj.GetType()
Dim oPartName = oPart
Dim oIndex As Integer = Nothing
Dim oHasIndex As Boolean = HasIndex(oPartName)
If oHasIndex Then
oPartName = StripIndex(oPart)
oIndex = GetIndex(oPart)
End If
Dim oInfo As PropertyInfo = oType.GetProperty(oPartName)
If IsNothing(oInfo) OrElse IsNothing(oInfo.GetValue(Obj, Nothing)) Then
_logger.Debug("Property [{0}] does not exist(2).", oPartName)
Return New List(Of Object)
End If
Obj = oInfo.GetValue(Obj, Nothing)
If oHasIndex Then
Obj = Obj(0)
End If
If IsArray(Obj) And Not oHasIndex Then
Dim oCurrentPart As String = oPart
Dim oSplitString As String() = New String() {oCurrentPart & "."}
Dim oPathFragments = PropertyName.Split(oSplitString, StringSplitOptions.None)
Dim oResults As New List(Of Object)
' if path has no more subitems, return an empty list
If oPathFragments.Length = 1 Then
Return oResults
End If
For Each oArrayItem In Obj
Dim oResult As List(Of Object) = GetPropValue(oArrayItem, oPathFragments(1))
If Not IsNothing(oResult) Then
oResults.Add(oResult)
End If
Next
Return oResults
End If
Next
Return New List(Of Object) From {Obj}
End Function
Public Function GetFinalPropValue(List As List(Of Object)) As List(Of Object)
Dim oResult As New List(Of Object)
For Each Item In List
Dim oItemValue = DoGetFinalPropValue(Item)
If Not IsNothing(oItemValue) Then
oResult.Add(oItemValue)
End If
Next
Return oResult
End Function
Private Function DoGetFinalPropValue(Value As Object) As String
If TypeOf Value Is List(Of Object) Then
Dim oList = DirectCast(Value, List(Of Object))
Dim oCount = oList.Count
Select Case oCount
Case 0
Return Nothing
Case Else
Return DoGetFinalPropValue(oList.First())
End Select
Return DoGetFinalPropValue(Value)
Else
Return Value.ToString
End If
End Function
Private Function GetIndex(Prop As String) As Integer
If Regex.IsMatch(Prop, _indexPattern) Then
Dim oMatch = _indexRegex.Match(Prop)
Dim oGroup = oMatch.Groups.Item(1)
Dim oValue = oGroup.Value
Return Integer.Parse(oValue)
End If
Return Nothing
End Function
Private Function StripIndex(Prop As String) As String
Return Regex.Replace(Prop, _indexPattern, "")
End Function
Private Function HasIndex(Prop As String) As Boolean
Return Regex.IsMatch(Prop, _indexPattern)
End Function
End Class

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
Public Class XmlItemProperty
Public TableName As String
Public TableColumn As String
Public Description As String
Public IsRequired As Boolean
Public IsGrouped As Boolean
Public GroupScope As String
End Class