Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,16 @@ public actor ContainerWrapper: ContainerWrapperProtocol, Loggable {
let duplicatePrefix = "Document with same file name"

let duplicateCount = errors.filter { $0.localizedDescription.hasPrefix(duplicatePrefix) }.count
let totalCount = errors.count

let failedCount = nsError.userInfo["failedFileCount"] as? Int ?? 0
let totalCount = nsError.userInfo["totalFileCount"] as? Int ?? dataFilesPaths.count

if duplicateCount == totalCount && totalCount > 1 {
throw DigiDocError.addingFilesToContainerFailed(
ErrorDetail(message: "Multiple documents already exist", code: 4, userInfo: [
"totalFileCount": String(totalCount),
"duplicateFileCount": String(duplicateCount)
"totalFileCount": totalCount,
"failedFileCount": failedCount,
"duplicateFileCount": duplicateCount
])
)
} else {
Expand All @@ -175,7 +178,8 @@ public actor ContainerWrapper: ContainerWrapperProtocol, Loggable {

throw DigiDocError.addingFilesToContainerFailed(
ErrorDetail(
nsError: nsError
nsError: nsError,
extraInfo: ["duplicateFileCount": duplicateCount]
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ import LibdigidocLibObjC
public struct ErrorDetail: Sendable {
public let message: String
public let code: Int
public let userInfo: [String: String]
public let userInfo: [String: Sendable]

public init(message: String = "", code: Int = 0, userInfo: [String: String] = [:]) {
public init(message: String = "", code: Int = 0, userInfo: [String: Sendable] = [:]) {
self.message = message
self.code = code
self.userInfo = userInfo
Expand All @@ -34,14 +34,13 @@ public struct ErrorDetail: Sendable {
init(nsError: NSError) {
self.message = nsError.localizedDescription
self.code = nsError.code
self.userInfo = ErrorDetail
.convertUserInfoToStringDictionary(nsError.userInfo)
self.userInfo = ErrorDetail.extractInfo(from: nsError)
}

init(nsError: NSError, extraInfo: [String: String]) {
init(nsError: NSError, extraInfo: [String: Sendable]) {
self.message = nsError.localizedDescription
self.code = nsError.code
self.userInfo = ErrorDetail.convertUserInfoToStringDictionary(nsError.userInfo)
self.userInfo = ErrorDetail.extractInfo(from: nsError)
.merging(extraInfo) { (_, combined) in combined }
}

Expand All @@ -53,9 +52,41 @@ public struct ErrorDetail: Sendable {
"""
}

private static func convertUserInfoToStringDictionary(_ userInfo: [String: Any]) -> [String: String] {
userInfo.mapValues { value in
String(describing: value)
private static func extractInfo(from error: NSError) -> [String: Sendable] {
var dict: [String: Sendable] = [:]

for (key, value) in error.userInfo {
dict[key] = String(describing: value)
}

dict[NSLocalizedDescriptionKey] = error.localizedDescription

if let failedFileCount = error.userInfo["failedFileCount"] as? Int, failedFileCount > 0 {
dict["failedFileCount"] = failedFileCount
}

if let totalFileCount = error.userInfo["totalFileCount"] as? Int, totalFileCount > 0 {
dict["totalFileCount"] = totalFileCount
}

if let causes = error.userInfo["causes"] as? [String: Any],
let errors = causes["errors"] as? [NSError],
let firstError = errors.first,
let subCauses = firstError.userInfo["causes"] as? [String: Any],
let file = subCauses["fileName"] as? String, !file.isEmpty {

dict["fileName"] = file
}

if let causes = error.userInfo["causes"] as? [String: Any],
let errors = causes["errors"] as? [NSError],
let firstError = errors.first,
let subCauses = firstError.userInfo["causes"] as? [String: Any],
let ex = subCauses["exceptions"] as? [Any], !ex.isEmpty {

dict["exceptions"] = ex.map { "\($0)" }
}

return dict
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ final class DigiDocErrorTests {

#expect(errorDetail.message == retrievedDetail.message)
#expect(errorDetail.code == retrievedDetail.code)
#expect(retrievedDetail.userInfo == ["key": "value"])
#expect(retrievedDetail.userInfo as? [String : String] == ["key": "value"])
}

@Test
Expand All @@ -44,7 +44,7 @@ final class DigiDocErrorTests {

#expect(retrievedDetail.message == "Libdigidocpp is already initialized")
#expect(retrievedDetail.code == 0)
#expect(retrievedDetail.userInfo == [:])
#expect(retrievedDetail.userInfo.isEmpty)
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ final class ErrorDetailsTests {

#expect(message == errorDetail.message)
#expect(code == errorDetail.code)
#expect(userInfo == errorDetail.userInfo)
#expect(userInfo == errorDetail.userInfo as? [String: String])
}

@Test
Expand All @@ -58,7 +58,12 @@ final class ErrorDetailsTests {

#expect(nsError.localizedDescription == errorDetail.message)
#expect(nsError.code == errorDetail.code)
#expect(errorDetail.userInfo == ["key": "value", NSLocalizedDescriptionKey: "Test NSError message"])
#expect(
errorDetail.userInfo as? [String : String] == [
"key": "value",
NSLocalizedDescriptionKey: "Test NSError message"
]
)
}

@Test
Expand All @@ -74,7 +79,7 @@ final class ErrorDetailsTests {

#expect(nsError.localizedDescription == errorDetail.message)
#expect(nsError.code == errorDetail.code)
#expect(errorDetail.userInfo == [
#expect(errorDetail.userInfo as? [String : String] == [
"key": "value",
NSLocalizedDescriptionKey: "Test NSError message",
"extraKey": "extraValue"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ final class SignedContainerTests {
notExistingContainerUrl
.deletingPathExtension()
.appendingPathExtension(Constants.Extension.Asice).lastPathComponent == errorDetail
.userInfo["fileName"]
.userInfo["fileName"] as? String
)
default:
Issue.record("Unexpected error: \(error.localizedDescription)")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,15 @@ public struct MimeTypeDecoder: MimeTypeDecoderProtocol {
await withCheckedContinuation { continuation in
let parser = XMLParser(data: xmlData)
let handler = XMLParserHandler(continuation: continuation)

parser.delegate = handler
parser.parse()
} ? .ddoc : .none

let success = parser.parse()

if !success {
handler.finishIfNeeded()
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@

import Foundation

class XMLParserHandler: NSObject, XMLParserDelegate {
private var continuation: CheckedContinuation<Bool, Never>?
final class XMLParserHandler: NSObject, XMLParserDelegate {

private let continuation: CheckedContinuation<ContainerType, Never>
private var didResume = false
private var foundElement = false

init(continuation: CheckedContinuation<Bool, Never>) {
init(continuation: CheckedContinuation<ContainerType, Never>) {
self.continuation = continuation
}

Expand All @@ -36,23 +38,27 @@ class XMLParserHandler: NSObject, XMLParserDelegate {
) {
if elementName == "SignedDoc", attributeDict["format"] == "DIGIDOC-XML" {
foundElement = true
continuation?.resume(returning: true)
continuation = nil
parser.abortParsing()
}
}

func parserDidEndDocument(_: XMLParser) {
if continuation != nil {
continuation?.resume(returning: foundElement)
continuation = nil
}
// swiftlint:disable:next unused_parameter
func parserDidEndDocument(_ parser: XMLParser) {
resume(foundElement ? .ddoc : .none)
}

func parser(_: XMLParser, parseErrorOccurred _: Error) {
if continuation != nil {
continuation?.resume(returning: foundElement)
continuation = nil
}
// swiftlint:disable:next unused_parameter
func parser(_ parser: XMLParser, parseErrorOccurred error: Error) {
resume(foundElement ? .ddoc : .none)
}

func finishIfNeeded() {
resume(foundElement ? .ddoc : .none)
}

private func resume(_ value: ContainerType) {
guard !didResume else { return }
didResume = true
continuation.resume(returning: value)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

import Foundation

public enum ContainerType {
public enum ContainerType: Sendable {
case none
case asice
case cdoc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,68 +34,68 @@ struct XMLParserHandlerTests {
"""
let data = Data(xml.utf8)

let result = await withCheckedContinuation { (continuation: CheckedContinuation<Bool, Never>) in
let result = await withCheckedContinuation { (continuation: CheckedContinuation<ContainerType, Never>) in
let handler = XMLParserHandler(continuation: continuation)
let parser = XMLParser(data: data)
parser.delegate = handler
parser.parse()
}

#expect(result == true)
#expect(result == .ddoc)
}

@Test
func xmlParserHandler_parser_returnFalseWithWrongSignedDocFormat() async {
func xmlParserHandler_parser_returnNoneResultWithWrongSignedDocFormat() async {
let xml = """
<?xml version="1.0"?>
<SignedDoc format="WRONG-FORMAT"></SignedDoc>
"""
let data = Data(xml.utf8)

let result = await withCheckedContinuation { (continuation: CheckedContinuation<Bool, Never>) in
let result = await withCheckedContinuation { (continuation: CheckedContinuation<ContainerType, Never>) in
let handler = XMLParserHandler(continuation: continuation)
let parser = XMLParser(data: data)
parser.delegate = handler
parser.parse()
}

#expect(result == false)
#expect(result == .none)
}

@Test
func xmlParserHandler_parser_returnFalseWithNoSignedDocTag() async {
func xmlParserHandler_parser_returnNoneResultWithNoSignedDocTag() async {
let xml = """
<?xml version="1.0"?>
<OtherTag></OtherTag>
"""
let data = Data(xml.utf8)

let result = await withCheckedContinuation { (continuation: CheckedContinuation<Bool, Never>) in
let result = await withCheckedContinuation { (continuation: CheckedContinuation<ContainerType, Never>) in
let handler = XMLParserHandler(continuation: continuation)
let parser = XMLParser(data: data)
parser.delegate = handler
parser.parse()
}

#expect(result == false)
#expect(result == .none)
}

@Test
func xmlParserHandler_parser_returnsFalseWhenXMLIsMalformed() async {
func xmlParserHandler_parser_returnsNoneResultWhenXMLIsMalformed() async {
let xml = """
<?xml version="1.0"?>
<SignedDoc format="DIGIDOC-XML"
"""

let data = Data(xml.utf8)

let result = await withCheckedContinuation { (continuation: CheckedContinuation<Bool, Never>) in
let result = await withCheckedContinuation { (continuation: CheckedContinuation<ContainerType, Never>) in
let handler = XMLParserHandler(continuation: continuation)
let parser = XMLParser(data: data)
parser.delegate = handler
parser.parse()
}

#expect(result == false)
#expect(result == .none)
}
}
1 change: 0 additions & 1 deletion RIADigiDoc.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,6 @@
Domain/Model/EncryptionServerInfo.swift,
Domain/Model/EncryptionServerOption.swift,
Domain/Model/EncryptionServerOptionId.swift,
Domain/Model/Error/ErrorMessage.swift,
Domain/Model/Error/FileOpeningErrors.swift,
"Domain/Model/Error/My eID/MyEidCodeChangeError.swift",
Domain/Model/FileItem.swift,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

import Foundation

struct ErrorMessage: Sendable, Equatable {
let key: String
let args: [String]
struct ToastItem: Sendable {
let message: String
let duration: TimeInterval
}
9 changes: 4 additions & 5 deletions RIADigiDoc/Domain/Model/ToastMessage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,11 @@

import Foundation

struct ToastMessage: Identifiable {
let id = UUID()
let key: String?
let args: [CVarArg]
struct ToastMessage: Sendable, Equatable {
let key: String
let args: [String]

init(key: String?, args: [CVarArg] = []) {
init(key: String, args: [String] = []) {
self.key = key
self.args = args
}
Expand Down
Loading