Imports System.Net.Http Imports Newtonsoft.Json Imports DigitalData.Modules.Base Imports DigitalData.Modules.Base.ModuleExtensions Imports DigitalData.Modules.Database Imports DigitalData.Modules.Logging Imports Connectors.Common.slt.Constants Imports Connectors.Common.slt.Responses Imports Connectors.Common.slt.Entities Imports System.Threading.Tasks Imports System.IO Namespace slt Public Class sltSync Inherits BaseModule Implements ISync Private ReadOnly MimeEx As MimeEx Public Overrides Property Name As String = "slt Sync" Public Overrides Property IsLoggedIn As Boolean = False Public SessionId As String = Nothing Public AvailableSystems As New List(Of sltAvailableSystem) Public Sub New(pLogConfig As LogConfig, pDatabase As MSSQLServer, pConfig As Config) MyBase.New(pLogConfig, pDatabase, pConfig) MimeEx = New MimeEx(pLogConfig) End Sub Public Overrides Async Function Run() As Threading.Tasks.Task Implements ISync.Run Try AddInfoEntry("Starting sltSync.") AddDivider() EnsureOutputDirectoryExists() RefreshDirectoryGuid() Dim oExtDocIds = Await FetchDocIds() If oExtDocIds Is Nothing Then Throw New ApplicationException($"Document Ids could not be fetched!") End If AddInfoEntry("Logging in..") Await GetAvailableSystems() Await Login(Config.sltConfiguration.SystemId) For Each oDocId As String In oExtDocIds Try Logger.Debug("Fetching document from API..") Dim oDocument As sltDocument = Await GetDocumentContent(oDocId) oDocument = Await GetDocumentDetails(oDocument) Logger.Debug("Document fetched!") AddInfoEntry("Document: [{0}]", oDocument.Name) Logger.Info("ExtDocId: [{0}]", oDocument.ExtDocId) Dim oFileName = oDocument.GetUniqueFilename() If oFileName Is Nothing Then Throw New ApplicationException($"Filename or extension for ExDocId [{oDocId}] could not be determined!") End If Dim oFilePath = GetFinalFilePath(oFileName) If CopyFileToOutputPath(oDocument.Data, oFilePath) = False Then Throw New ApplicationException("File could not be created in output path!") End If Dim oSQL = String.Format(Config.SQLQueryExport, oDocument.ExtDocId, oFileName) If Await Database.ExecuteNonQueryAsync(oSQL) = True Then RaiseFileProcessed(oFilePath) Else Throw New ApplicationException("Database entry could not be written!") End If Catch ex As Exception RaiseFileError(oDocId) Logger.Error(ex) AddWarnEntry("Error while running Sync: " & ex.Message) End Try Next Catch ex As Exception Logger.Error(ex) AddWarnEntry("Error while running Sync: " & ex.Message) End Try Try AddInfoEntry("Finished Sync.") AddInfoEntry("Logging Out..") Await Logout() Catch ex As Exception Logger.Error(ex) AddWarnEntry("Error while logging out: " & ex.Message) Finally AddDivider() End Try End Function Public Overrides Async Function Cleanup() As Task Implements ISync.Cleanup Await Logout() End Function Public Overrides Function TestConfigIsComplete() As Boolean Implements ISync.TestConfigIsComplete Dim oComplete = TestConfigIsCompleteBase() If Config.sltConfiguration.Hostname = String.Empty Then AddWarnEntry("Configuration for 'Hostname' is empty.") oComplete = False End If If Config.sltConfiguration.Port = String.Empty Then AddWarnEntry("Configuration for 'Port' is empty.") oComplete = False End If If Config.sltConfiguration.Username = String.Empty Then AddWarnEntry("Configuration for 'Username' is empty.") oComplete = False End If If Config.sltConfiguration.Password = String.Empty Then AddWarnEntry("Configuration for 'Password' is empty.") oComplete = False End If If Config.sltConfiguration.sltDatabase = String.Empty Then AddWarnEntry("Configuration for 'sltDatabase' is empty.") oComplete = False End If Return oComplete End Function Private Function GetFilenameWithExtensionFromMimeType(pFilename As String, pMimetype As String) As String Try If pMimetype = "application/outlook" Then pMimetype = "application/vnd.ms-outlook" End If Dim oExtension = MimeEx.GetExtension(pMimetype) Return StringEx.ConvertTextToSlug(pFilename) & oExtension Catch ex As ArgumentException Logger.Error(ex) Logger.Warn("File [{0}] does not have a valid mimetype [{1}]. Returning null.", pFilename, pMimetype) Return Nothing Catch ex As Exception Logger.Error(ex) AddWarnEntry("Unexpected error while getting extension. Returning original filename.", pFilename, pMimetype) Return pFilename End Try End Function Private Async Function GetAvailableSystems() As Threading.Tasks.Task Try Logger.Debug("Fetching available systems..") Dim oUrl = "/slt/External/System/Authentication/Json/AvailableSystems" Dim oJson As String = Await SendRequest(oUrl) Dim oResponse = JsonConvert.DeserializeObject(Of sltAvailableSystemResponse)(oJson) TestRequestSuccessful(oUrl, oResponse, ErrorType.AvailableSystemError) AvailableSystems = oResponse.Value Logger.Debug("Fetched [{0}] available systems!", oResponse.Value.Count) Catch ex As Exception Logger.Error(ex) Throw New sltException(ErrorType.AvailableSystemError, ex.Message) End Try End Function Private Async Function Login(pSystemId As String) As Threading.Tasks.Task Try Logger.Debug("Logging in..") If AvailableSystems.Any(Function(s) s.SystemId = pSystemId) = False Then Dim oMessage = String.Format("SystemId [{0}] does not match any System returned from API.", pSystemId) Logger.Warn(oMessage) Throw New sltException(ErrorType.LoginError, oMessage) End If Dim oUrl = "/slt/External/System/Authentication/Json/Login" Dim oParams = New Dictionary(Of String, String) From { {"systemid", pSystemId}, {"user", Config.sltConfiguration.Username}, {"password", Config.sltConfiguration.Password} } Logger.Debug("Username: [{0}]", Config.sltConfiguration.Username) Logger.Debug("SystemId: [{0}]", pSystemId) Dim oJson As String = Await SendRequest(oUrl, oParams) Dim oResponse = JsonConvert.DeserializeObject(Of sltLoginResponse)(oJson) TestRequestSuccessful(oUrl, oResponse, ErrorType.LoginError) SessionId = oResponse.Value IsLoggedIn = True Logger.Debug("Login successful!") Catch ex As Exception Logger.Error(ex) Throw New sltException(ErrorType.LoginError, ex.Message) End Try End Function Private Async Function Logout() As Threading.Tasks.Task If Not IsLoggedIn Then Throw New sltException(ErrorType.NotLoggedInError, "No session found") End If Logger.Debug("Logging out..") Try Dim oUrl = "/slt/External/System/Authentication/Json/Logout" Dim oParams = New Dictionary(Of String, String) From { {"SessionId", SessionId} } Dim oJson As String = Await SendRequest(oUrl, oParams) Dim oResponse = JsonConvert.DeserializeObject(Of sltLogoutResponse)(oJson) TestRequestSuccessful(oUrl, oResponse, ErrorType.LogoutError) SessionId = Nothing IsLoggedIn = False Logger.Debug("Login successful!") Catch ex As Exception Logger.Error(ex) Throw New sltException(ErrorType.LogoutError, ex.Message) End Try End Function Private Async Function GetDocumentDetails(pDocument As sltDocument) As Task(Of sltDocument) If Not IsLoggedIn Then Throw New sltException(ErrorType.NotLoggedInError, "No session found") End If Try Logger.Debug("Fetching document details with ExtDocId [{0}]", pDocument.ExtDocId) Dim oSQL = $"SELECT DOCORIGINALNAME FROM {Config.sltConfiguration.sltDatabase}.[EXTDOCS] WHERE EXTDOCID = '{pDocument.ExtDocId}'" Dim oResult As Object = Await Database.GetScalarValueAsync(oSQL) Dim oFileNameOriginal As String = ObjectEx.NotNull(Of String)(oResult.ToString, "") If Not String.IsNullOrEmpty(oFileNameOriginal) Then pDocument.DocOriginalFilename = oFileNameOriginal End If Return pDocument Catch ex As Exception Logger.Error(ex) Return pDocument End Try End Function Private Async Function GetDocumentContent(pExternalDocumentId As String) As Threading.Tasks.Task(Of slt.Entities.sltDocument) If Not IsLoggedIn Then Throw New sltException(ErrorType.NotLoggedInError, "No session found") End If Try Logger.Debug("Fetching document with ExtDocId [{0}]", pExternalDocumentId) Dim oUrl = "/slt/External/Services/Allgemein/ExtDocs/JSon/GetDocument" Dim oParams As New Dictionary(Of String, String) From { {"extdocid", pExternalDocumentId}, {"sessionid", SessionId} } Dim oJson = Await SendRequest(oUrl, oParams) Dim oResponse = JsonConvert.DeserializeObject(Of sltDocumentResponse)(oJson) Logger.Debug("Document Fetched!") Return oResponse.Value Catch ex As Exception Logger.Error(ex) Throw New sltException(ErrorType.GetDocumentError, ex.Message) End Try End Function Private Async Function SendRequest(pUrl As String) As Threading.Tasks.Task(Of String) Return Await SendRequest(pUrl, New Dictionary(Of String, String)) End Function Private Async Function SendRequest(pUrl As String, pQueryParams As Dictionary(Of String, String)) As Threading.Tasks.Task(Of String) Try Dim oUrl = GetUrl(pUrl, pQueryParams) Logger.Debug("Preparing request to: [{0}]", oUrl) Using oClient As New HttpClient() oClient.DefaultRequestHeaders.Accept.Clear() oClient.DefaultRequestHeaders.Accept.Add(New Headers.MediaTypeWithQualityHeaderValue("application/json")) oClient.DefaultRequestHeaders.Add("User-Agent", "Digital Data sltSync") Logger.Debug("Sending request.") Return Await oClient.GetStringAsync(oUrl) End Using Catch ex As Exception Logger.Error(ex) Return Nothing End Try End Function Private Function GetUrl(pPath As String, pQueryParams As Dictionary(Of String, String)) As String Dim oUrl As String = $"http://{Config.sltConfiguration.Hostname}:{Config.sltConfiguration.Port}" If Not pPath.StartsWith("/") Then pPath &= "/" Dim queryString = pQueryParams.ToURLQueryString() Return $"{oUrl}{pPath}?{queryString}" End Function Private Sub TestRequestSuccessful(pUrl As String, pResponse As Responses.sltResponse, pErrorType As Constants.ErrorType) If pResponse.State = False Then Logger.Warn("Request to Url [{0}] returned error.", pUrl) Logger.Error(pResponse.Message) Throw New sltException(pErrorType, pResponse.Message) End If End Sub End Class End Namespace