Skip to content

Commit

Permalink
feat: update documentation
Browse files Browse the repository at this point in the history
Resolves: none.
  • Loading branch information
loay-ashraf committed Oct 1, 2024
1 parent b3c2e9e commit 0b5ac1a
Show file tree
Hide file tree
Showing 11 changed files with 324 additions and 95 deletions.
10 changes: 8 additions & 2 deletions Source/Common/Request/Logger/Factory/CURLCommandFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@

import Foundation

/// A factory class responsible for making cURL command representations
/// for HTTP requests.
///
/// This class provides methods to create a cURL command string from a given
/// `URLRequest`, including handling different body options like plain data,
/// file uploads, and multipart form data.
final class CURLCommandFactory {

/// Makes cURL command representation for a given request.
///
/// - Parameters:
/// - request: `URLRequest` used to make cURL command representation.
/// - body: `CURLCommandBody` that is used to make the cURL command body option.
/// - request: `URLRequest` used to make the cURL command representation.
/// - bodyOption: `HTTPLogBodyOption` that determines how the body is represented in the cURL command.
///
/// - Returns: cURL command representation for the given request.
func make(for request: URLRequest, bodyOption: HTTPLogBodyOption) -> String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,20 @@

import Foundation

/// A factory class responsible for making console messages
/// for the body of outgoing HTTP requests based on different logging options.
///
/// This class provides methods to create log messages for plain data,
/// file uploads, and multipart form data. It is utilized by the
/// `HTTPLogger` to format and present the body of HTTP requests in a readable manner.
final class HTTPLogBodyMessageFactory {

/// Makes console body message for outgoing request.
/// Makes console body message for the specified body option.
///
/// - Parameters:
/// - bodyOption: `HTTPLogBodyOption` option for how body is represented in the log message.
/// - bodyOption: `HTTPLogBodyOption` option for how the body is represented in the log message.
///
/// - Returns: `String` of outgoing request body message.
/// - Returns: `String` of the outgoing request body message.
func make(bodyOption: HTTPLogBodyOption) -> String {
switch bodyOption {
case .plain(let body):
Expand All @@ -26,6 +32,11 @@ final class HTTPLogBodyMessageFactory {
}
}

/// Makes console message for raw data body.
///
/// - Parameter rawData: Optional `Data` object representing the body.
///
/// - Returns: `String` representation of the raw data body.
private func make(_ rawData: Data?) -> String {
var logBodyMessage = ""
if let rawData = rawData {
Expand All @@ -38,12 +49,12 @@ final class HTTPLogBodyMessageFactory {
return logBodyMessage
}

/// Makes console body message for outgoing request.
/// Makes console message for a file body.
///
/// - Parameters:
/// - file: `FileType` file details to be printed in place of body.
///
/// - Returns: `String` of outgoing request body message.
/// - Returns: `String` of the outgoing request body message.
private func make(_ file: FileType) -> String {
var logBodyMessage: String = ""

Expand All @@ -69,12 +80,12 @@ final class HTTPLogBodyMessageFactory {
return logBodyMessage
}

/// Makes console body message for outgoing request.
/// Makes console message for multipart form data body.
///
/// - Parameters:
/// - formData: `FormData` form data details to be printed in place of body.
///
/// - Returns: `String` of outgoing request body message.
/// - Returns: `String` of the outgoing request body message.
private func make(_ formData: FormData) -> String {
var logBodyMessage: String = "\n"
let boundary = formData.boundary
Expand Down
21 changes: 14 additions & 7 deletions Source/Common/Request/Logger/Factory/HTTPLogMessageFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,27 @@

import Foundation

/// A singleton class responsible for making log messages for outgoing HTTP requests
/// and incoming HTTP responses.
///
/// The `HTTPLogMessageFactory` provides formatted output
/// for monitoring and debugging HTTP communications, including details such as headers,
/// body content, and CURL commands.
final class HTTPLogMessageFactory {

/// Factory responsible for maling body log messages.
private let logBodyMessageFactory: HTTPLogBodyMessageFactory = .init()

/// Factory responsible for making CURL commands.
private let curlCommandFactory: CURLCommandFactory = .init()

/// Makes console message for outgoing request.
///
/// - Parameters:
/// - request: `URLRequest` to be included in message.
/// - request: `URLRequest` to be included in the message.
/// - bodyOption: `HTTPLogBodyOption` option for how body is represented in the log message.
///
/// - Returns: `String` of outgoing request message.
/// - Returns: `String` of the outgoing request message.
func make(for request: URLRequest, bodyOption: HTTPLogBodyOption) -> String {
var logMessage: String = ""

Expand Down Expand Up @@ -60,10 +69,10 @@ final class HTTPLogMessageFactory {
/// Makes console message for incoming response.
///
/// - Parameters:
/// - responseArguments: `(URL?, URLResponse?, Data?, Error?)` to be included in message.
/// - bodyLogMessage: `String?` placeholder to be included in message in place of actual body.
/// - responseArguments: `(URL?, Data?, URLResponse?, Error?)` to be included in the message.
/// - bodyLogMessage: `String?` placeholder to be included in the message in place of actual body.
///
/// - Returns: `String` of incoming response message.
/// - Returns: `String` of the incoming response message.
func make(for responseArguments: (URL?, Data?, URLResponse?, Error?), bodyLogMessage: String?) -> String {
var logMessage: String = ""

Expand Down Expand Up @@ -115,13 +124,11 @@ final class HTTPLogMessageFactory {
} else {
responseDetails += "\nError: \(responseError.localizedDescription)\n"
}

}

logMessage += responseDetails
logMessage += "\n* * * * * * * * * * * * * END * * * * * * * * * * * * *\n"

return logMessage
}

}
14 changes: 14 additions & 0 deletions Source/Common/Request/Logger/HTTPLogBodyOption.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,22 @@

import Foundation

/// Represents the options for logging the body of an HTTP request or response.
///
/// This enum provides different methods to log the body content, which can be plain data,
/// a file, or multipart form data. Each case allows for flexible logging depending on
/// the specific needs of the request or response being handled.
enum HTTPLogBodyOption {

/// Logs the body as plain data.
/// - Parameter body: Optional `Data` object to be logged. Default is `nil`.
case plain(_ body: Data? = nil)

/// Logs the body as a file.
/// - Parameter file: The `FileType` object representing the file to be logged.
case file(_ file: FileType)

/// Logs the body as multipart form data.
/// - Parameter formData: The `FormData` object containing the form data to be logged.
case formData(_ formData: FormData)
}
33 changes: 21 additions & 12 deletions Source/Common/Request/Logger/HTTPLogger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,45 @@

import Foundation

/// Object responsible for logging outgoing requests and incoming responses.
/// A singleton class responsible for logging outgoing HTTP requests and incoming HTTP responses.
///
/// The `HTTPLogger` provides a shared instance for logging purposes, enabling developers
/// to monitor and debug HTTP communications in their applications. It includes methods
/// for logging both requests and responses with customizable logging options.
final class HTTPLogger {

/// Shared `HTTPLogger` instance.
/// Shared `HTTPLogger` instance, implementing the singleton pattern.
static let shared: HTTPLogger = .init()

/// Factory object used to make log messages
/// A factory object used to create log messages for requests and responses.
private let messageFactory: HTTPLogMessageFactory = .init()

/// Private initializer to ensure only one instance is created.
/// Private initializer to ensure that only one instance of `HTTPLogger` can be created.
private init() { }

/// Prints outgoing request to console.
/// Logs an outgoing HTTP request to the console.
///
/// - Parameters:
/// - request: `URLRequest` to be printed to console.
/// - bodyOption: `HTTPLogBodyOption` option for how body is represented in the log message.
/// - request: The `URLRequest` to be logged, containing information about the HTTP request.
/// - bodyOption: An `HTTPLogBodyOption` indicating how the request body should be represented in the log message.
/// Defaults to `.plain()`.
func log(request: URLRequest, bodyOption: HTTPLogBodyOption = .plain()) {
let logMessage = messageFactory.make(for: request, bodyOption: bodyOption)
print(logMessage)
print(logMessage) // Print the log message to the console.
}

/// Prints incoming response to console.
/// Logs an incoming HTTP response to the console.
///
/// - Parameters:
/// - responseArguments: `(URL?, URLResponse?, Data?, Error?)` to be printed to console.
/// - bodyLogMessage: `String?` placeholder to be printed in place of actual body.
/// - responseArguments: A tuple containing optional values:
/// - `URL?`: The URL of the response, if available.
/// - `Data?`: The data received in the response, if available.
/// - `URLResponse?`: The metadata associated with the response, if available.
/// - `Error?`: An optional error that occurred during the request, if applicable.
/// - bodyLogMessage: An optional `String?` placeholder to be printed in place of the actual body data.
func log(responseArguments: (URL?, Data?, URLResponse?, Error?), bodyLogMessage: String? = nil) {
let logMessage = messageFactory.make(for: responseArguments, bodyLogMessage: bodyLogMessage)
print(logMessage)
print(logMessage) // Print the log message to the console.
}

}
17 changes: 15 additions & 2 deletions Source/Common/Request/Logger/MemoryLogger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,31 @@

import Foundation

/// A singleton class responsible for logging memory warnings related to large file uploads.
/// The `MemoryLogger` logs messages when files larger than 10 MB are held in memory,
/// providing details about the file to help developers understand potential performance impacts.
final class MemoryLogger {

/// The shared instance of `MemoryLogger`, implementing the singleton pattern.
static let shared: MemoryLogger = .init()

/// Private initializer to ensure that `MemoryLogger` cannot be instantiated from outside.
private init() { }

/// Logs a warning message for a large file held in memory during upload.
///
/// - Parameter file: An instance conforming to the `FileType` protocol,
/// representing the large file for upload.
func logLargeUploadFile(file: FileType) {
let logMessage = makeLogMessage(for: file)
print(logMessage)
print(logMessage) // Print the log message to the console.
}

/// Creates a formatted log message for the specified file.
///
/// - Parameter file: An instance conforming to the `FileType` protocol,
/// from which details will be extracted for logging.
/// - Returns: A string containing the formatted log message with file details.
private func makeLogMessage(for file: FileType) -> String {
var logMessage = ""

Expand All @@ -31,5 +45,4 @@ final class MemoryLogger {

return logMessage
}

}
51 changes: 35 additions & 16 deletions Source/HTTP/Types/Request/Parameters/File/File.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,63 +7,82 @@

import Foundation

/// Holds file details for upload request.
/// Holds file details for upload requests, conforming to the `FileType` protocol.
/// This struct is used to represent a file that can be uploaded,
/// including its name, path, data, input stream, MIME type, and size.
public struct File: FileType {

/// name of the file.
/// The name of the file, including its extension.
let name: String
/// absolute path of the file.

/// The absolute path of the file, if available.
let path: String?
/// data of the file.

/// The data of the file, if available.
/// This property holds the contents of the file as a `Data` object.
let data: Data?
/// input stream of the file.

/// The input stream of the file, if available.
/// This property allows reading the file's contents in a streaming manner,
/// which is useful for larger files.
let inputStream: InputStream?
/// MIME type of the file.

/// The MIME type of the file, indicating its format.
let mimeType: HTTPMIMEType
/// size of the file.

/// The size of the file in bytes.
let size: Int64

/// Creates `File` instance, use this initializer for relativley small files (< 20MB).
/// Creates a `File` instance for relatively small files (less than 20MB).
///
/// - Parameters:
/// - name: file name.
/// - extension: file extension.
/// - data: `Data` object for file.
/// - name: The name of the file, including its extension.
/// - extension: The file extension, used to determine its MIME type.
/// - data: A `Data` object containing the file's contents.
///
/// - Returns: A `File` instance if successful; `nil` if the MIME type could not be determined.
public init?(withName name: String, withExtension `extension`: String, withData data: Data) {
self.name = name
self.path = nil
self.data = data
self.inputStream = nil

guard let mime = HTTPMIMEType(fileExtension: `extension`) else { return nil }
self.mimeType = mime
self.size = Int64(data.count)

#if DEBUG
if size > 10_485_760 {
if size > 10_485_760 { // Log large file uploads for debugging.
MemoryLogger.shared.logLargeUploadFile(file: self)
}
#endif
}

/// Creates `File` instance, use this initializer for relativley large files (> 20MB).
/// Creates a `File` instance for relatively large files (greater than 20MB).
///
/// - Parameters:
/// - url: local `URL` for the file.
/// - url: A local `URL` pointing to the file to be uploaded.
///
/// - Returns: A `File` instance if successful; `nil` if the MIME type or size could not be determined.
public init?(withURL url: URL) {
let fileName = url.lastPathComponent
let (name, `extension`) = fileName.splitNameAndExtension()

self.name = name

if #available(iOS 16, macOS 13, tvOS 16, watchOS 9, *) {
self.path = url.path()
} else {
self.path = url.path
}

self.data = nil
self.inputStream = .init(url: url)

guard let mime = HTTPMIMEType(fileExtension: `extension`) else { return nil }
self.mimeType = mime

guard let size = FileManager.default.sizeOfFile(atURL: url) else { return nil }
self.size = size
}

}

Loading

0 comments on commit 0b5ac1a

Please sign in to comment.