From ecf2743c1d1d6dbc530158b33eda3a9f46b4eca3 Mon Sep 17 00:00:00 2001 From: pjechris Date: Wed, 23 Nov 2022 10:03:06 +0100 Subject: [PATCH] tech(swiftlint): Use SwiftLint to lint code (#19) --- .github/workflows/test.yml | 3 + .swift-format | 56 ------------------- .swiftlint.yml | 20 +++++++ Package.swift | 3 - .../Interceptor/CompositeInterceptor.swift | 16 +++--- .../SimpleHTTP/Interceptor/Interceptor.swift | 8 +-- .../MultipartForm/MultipartFormData.swift | 2 +- .../Query/Dictionary+QueryParam.swift | 4 +- Sources/SimpleHTTP/Query/QueryParam.swift | 2 +- Sources/SimpleHTTP/Request/Path.swift | 5 +- .../Request/Request+URLRequest.swift | 10 ++-- Sources/SimpleHTTP/Request/Request.swift | 30 +++++----- Sources/SimpleHTTP/Request/URL+Request.swift | 10 ++-- .../SimpleHTTP/Response/DataResponse.swift | 2 +- .../SimpleHTTP/Session/Session+Combine.swift | 10 ++-- Sources/SimpleHTTP/Session/Session.swift | 2 +- .../Session/SessionConfiguration.swift | 4 +- .../URLRequest/URLRequest+Encode.swift | 8 +-- .../URLRequest/URLRequest+HTTPHeader.swift | 2 +- .../URLRequest/URLRequest+URL.swift | 6 +- .../Foundation/URLSession+Async.swift | 4 +- .../HTTP/HTTPContentType.swift | 4 +- .../SimpleHTTPFoundation/HTTP/HTTPError.swift | 20 +++---- .../HTTP/HTTPHeader.swift | 2 +- .../URLRequest/URLRequestTests+URL.swift | 18 +++--- .../URLRequest/URLRequestsTests+Encode.swift | 6 +- .../Foundation/URLResponseValidateTests.swift | 6 +- .../MultipartFormDataEncoderTests.swift | 2 - .../Request/QueryParamTests.swift | 2 +- .../Request/RequestTests.swift | 34 +++++------ .../Request/URLTests+Request.swift | 4 +- .../Response/URLDataResponseTests.swift | 10 ++-- .../Ressources/URL+fromBundle.swift | 2 +- .../Session/Session+CombineTests.swift | 10 ++-- .../Session/SessionTests.swift | 52 ++++++++--------- 35 files changed, 169 insertions(+), 210 deletions(-) delete mode 100644 .swift-format create mode 100644 .swiftlint.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f32d2aa..9789802 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,5 +16,8 @@ jobs: - name: Checkout repo uses: actions/checkout@v2 + - name: Lint code + uses: swiftlint lint + - name: Run tests run: swift test --enable-test-discovery diff --git a/.swift-format b/.swift-format deleted file mode 100644 index 5806d92..0000000 --- a/.swift-format +++ /dev/null @@ -1,56 +0,0 @@ -{ - "fileScopedDeclarationPrivacy" : { - "accessLevel" : "private" - }, - "indentation" : { - "spaces" : 4 - }, - "indentConditionalCompilationBlocks" : true, - "indentSwitchCaseLabels" : false, - "lineBreakAroundMultilineExpressionChainComponents" : false, - "lineBreakBeforeControlFlowKeywords" : false, - "lineBreakBeforeEachArgument" : false, - "lineBreakBeforeEachGenericRequirement" : false, - "lineLength" : 100, - "maximumBlankLines" : 1, - "prioritizeKeepingFunctionOutputTogether" : false, - "respectsExistingLineBreaks" : true, - "rules" : { - "AllPublicDeclarationsHaveDocumentation" : false, - "AlwaysUseLowerCamelCase" : true, - "AmbiguousTrailingClosureOverload" : true, - "BeginDocumentationCommentWithOneLineSummary" : false, - "DoNotUseSemicolons" : true, - "DontRepeatTypeInStaticProperties" : true, - "FileScopedDeclarationPrivacy" : true, - "FullyIndirectEnum" : true, - "GroupNumericLiterals" : true, - "IdentifiersMustBeASCII" : true, - "NeverForceUnwrap" : false, - "NeverUseForceTry" : false, - "NeverUseImplicitlyUnwrappedOptionals" : false, - "NoAccessLevelOnExtensionDeclaration" : true, - "NoBlockComments" : true, - "NoCasesWithOnlyFallthrough" : true, - "NoEmptyTrailingClosureParentheses" : true, - "NoLabelsInCasePatterns" : true, - "NoLeadingUnderscores" : false, - "NoParensAroundConditions" : true, - "NoVoidReturnOnFunctionSignature" : true, - "OneCasePerLine" : true, - "OneVariableDeclarationPerLine" : true, - "OnlyOneTrailingClosureArgument" : true, - "OrderedImports" : true, - "ReturnVoidInsteadOfEmptyTuple" : true, - "UseEarlyExits" : false, - "UseLetInEveryBoundCaseVariable" : true, - "UseShorthandTypeNames" : true, - "UseSingleLinePropertyGetter" : true, - "UseSynthesizedInitializer" : true, - "UseTripleSlashForDocumentationComments" : true, - "UseWhereClausesInForLoops" : false, - "ValidateDocumentationComments" : false - }, - "tabWidth" : 8, - "version" : 1 -} diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..be23d4c --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,20 @@ +included: + - Sources + - Tests + +excluded: + - build + - .build + +disabled_rules: + - statement_position + - type_name + +opt_in_rules: + - closure_end_indentation + - conditional_returns_on_newline + - empty_count + - indentation_width + +line_length: 120 +warning_threshold: 0 diff --git a/Package.swift b/Package.swift index 258d55a..8b1d861 100644 --- a/Package.swift +++ b/Package.swift @@ -11,9 +11,6 @@ let package = Package( .library(name: "SimpleHTTP", targets: ["SimpleHTTP"]) ], dependencies: [ - // Dependencies declare other packages that this package depends on. - // .package(url: /* package url */, from: "1.0.0"), - .package(url: "https://github.com/apple/swift-format", branch: "main") ], targets: [ .target(name: "SimpleHTTPFoundation", dependencies: []), diff --git a/Sources/SimpleHTTP/Interceptor/CompositeInterceptor.swift b/Sources/SimpleHTTP/Interceptor/CompositeInterceptor.swift index 89cbdb3..8c15b52 100644 --- a/Sources/SimpleHTTP/Interceptor/CompositeInterceptor.swift +++ b/Sources/SimpleHTTP/Interceptor/CompositeInterceptor.swift @@ -4,39 +4,39 @@ import Combine /// Use an Array of `Interceptor` as a single `Interceptor` public struct CompositeInterceptor: ExpressibleByArrayLiteral, Sequence { let interceptors: [Interceptor] - + public init(arrayLiteral interceptors: Interceptor...) { self.interceptors = interceptors } - + public func makeIterator() -> Array.Iterator { interceptors.makeIterator() } } - + extension CompositeInterceptor: Interceptor { public func adaptRequest(_ request: Request) -> Request { reduce(request) { request, interceptor in interceptor.adaptRequest(request) } } - + public func rescueRequest(_ request: Request, error: Error) -> AnyPublisher? { let publishers = compactMap { $0.rescueRequest(request, error: error) } - + guard !publishers.isEmpty else { return nil } - + return Publishers.MergeMany(publishers).eraseToAnyPublisher() } - + public func adaptOutput(_ response: Output, for request: Request) throws -> Output { try reduce(response) { response, interceptor in try interceptor.adaptOutput(response, for: request) } } - + public func receivedResponse(_ result: Result, for request: Request) { forEach { interceptor in interceptor.receivedResponse(result, for: request) diff --git a/Sources/SimpleHTTP/Interceptor/Interceptor.swift b/Sources/SimpleHTTP/Interceptor/Interceptor.swift index 1157f87..acec16e 100644 --- a/Sources/SimpleHTTP/Interceptor/Interceptor.swift +++ b/Sources/SimpleHTTP/Interceptor/Interceptor.swift @@ -7,7 +7,7 @@ public typealias Interceptor = RequestInterceptor & ResponseInterceptor public protocol RequestInterceptor { /// Should be called before making the request to provide modifications to `request` func adaptRequest(_ request: Request) -> Request - + /// catch and retry a failed request /// - Returns: nil if the request should not be retried. Otherwise a publisher that will be executed before /// retrying the request @@ -20,7 +20,7 @@ public protocol ResponseInterceptor { /// optionally throwing an error instead if needed /// - Parameter request: the request that was sent to the server func adaptOutput(_ output: Output, for request: Request) throws -> Output - + /// Notify of received response for `request` /// - Parameter request: the request that was sent to the server func receivedResponse(_ result: Result, for request: Request) @@ -30,11 +30,11 @@ extension RequestInterceptor { func shouldRescueRequest(_ request: Request, error: Error) async throws -> Bool { var cancellable: Set = [] let onCancel = { cancellable.removeAll() } - + guard let rescuePublisher = rescueRequest(request, error: error) else { return false } - + return try await withTaskCancellationHandler( handler: { onCancel() }, operation: { diff --git a/Sources/SimpleHTTP/MultipartForm/MultipartFormData.swift b/Sources/SimpleHTTP/MultipartForm/MultipartFormData.swift index de13018..6cdd233 100644 --- a/Sources/SimpleHTTP/MultipartForm/MultipartFormData.swift +++ b/Sources/SimpleHTTP/MultipartForm/MultipartFormData.swift @@ -152,7 +152,7 @@ public struct MultipartFormData { let headers = defineBodyPartHeader(name: name, fileName: fileName, mimeType: mimeType) let stream = InputStream(data: data) let length = data.count - + bodyParts.append(BodyPart(headers: headers, stream: stream, length: length)) } diff --git a/Sources/SimpleHTTP/Query/Dictionary+QueryParam.swift b/Sources/SimpleHTTP/Query/Dictionary+QueryParam.swift index d1b8bf9..99cb687 100644 --- a/Sources/SimpleHTTP/Query/Dictionary+QueryParam.swift +++ b/Sources/SimpleHTTP/Query/Dictionary+QueryParam.swift @@ -2,7 +2,7 @@ import Foundation extension Dictionary where Key == String, Value == QueryParam { /// transform query params into URLQueryItem` - var queryItems: [URLQueryItem] { + var queryItems: [URLQueryItem] { self.flatMap { key, value -> [URLQueryItem] in switch value.queryValue { case .single(let value): @@ -14,5 +14,5 @@ extension Dictionary where Key == String, Value == QueryParam { } } } - + } diff --git a/Sources/SimpleHTTP/Query/QueryParam.swift b/Sources/SimpleHTTP/Query/QueryParam.swift index 0bd49d0..64fc9b7 100644 --- a/Sources/SimpleHTTP/Query/QueryParam.swift +++ b/Sources/SimpleHTTP/Query/QueryParam.swift @@ -50,7 +50,7 @@ extension Array: QueryParam where Element: QueryParam { return values } } - + return .collection(values) } } diff --git a/Sources/SimpleHTTP/Request/Path.swift b/Sources/SimpleHTTP/Request/Path.swift index c041788..3f4fc74 100644 --- a/Sources/SimpleHTTP/Request/Path.swift +++ b/Sources/SimpleHTTP/Request/Path.swift @@ -32,13 +32,12 @@ public struct Path: Equatable, ExpressibleByStringLiteral, ExpressibleByStringIn public init(stringLiteral value: StringLiteralType) { self.init(value: value) } - + public init(stringInterpolation: DefaultStringInterpolation) { self.init(value: stringInterpolation.description) } - + public static func ==(lhs: Path, rhs: String) -> Bool { lhs.value == rhs } } - diff --git a/Sources/SimpleHTTP/Request/Request+URLRequest.swift b/Sources/SimpleHTTP/Request/Request+URLRequest.swift index 2dd9dea..f3c15ff 100644 --- a/Sources/SimpleHTTP/Request/Request+URLRequest.swift +++ b/Sources/SimpleHTTP/Request/Request+URLRequest.swift @@ -16,17 +16,17 @@ extension Request { if let decoder = accepting { return request.settingHeaders([.accept: type(of: decoder).contentType.value]) } - + return request } private func toURLRequest(encoder: ContentDataEncoder) throws -> URLRequest { var urlRequest = try URLRequest(url: URL(from: self)) - + urlRequest.httpMethod = method.rawValue.uppercased() urlRequest.cachePolicy = cachePolicy urlRequest.setHeaders(headers) - + if let body = body { switch body { case .encodable(let body): @@ -35,10 +35,8 @@ extension Request { try urlRequest.multipartBody(multipart) } } - + return urlRequest } - } - diff --git a/Sources/SimpleHTTP/Request/Request.swift b/Sources/SimpleHTTP/Request/Request.swift index febdcdb..c5d44de 100644 --- a/Sources/SimpleHTTP/Request/Request.swift +++ b/Sources/SimpleHTTP/Request/Request.swift @@ -24,31 +24,31 @@ public struct Request { public let query: [String: QueryParam] public var cachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy public var headers: HTTPHeaderFields = [:] - + /// Creates a request suitable for a HTTP GET public static func get(_ path: Path, query: [String: QueryParam] = [:]) -> Self { self.init(path: path, method: .get, query: query, body: nil) } - + /// Creates a request suitable for a HTTP POST with a `Encodable` body public static func post(_ path: Path, body: Encodable?, query: [String: QueryParam] = [:]) -> Self { self.init(path: path, method: .post, query: query, body: body.map(Body.encodable)) } - + /// Creates a request suitable for a HTTP POST with a `MultipartFormData` body @_disfavoredOverload public static func post(_ path: Path, body: MultipartFormData?, query: [String: QueryParam] = [:]) -> Self { self.init(path: path, method: .post, query: query, body: body.map(Body.multipart)) } - + /// Creates a request suitable for a HTTP PUT with a `Encodable` body public static func put(_ path: Path, body: Encodable, query: [String: QueryParam] = [:]) -> Self { self.init(path: path, method: .put, query: query, body: .encodable(body)) } - + /// Creates a request suitable for a HTTP PUT with a `MultipartFormData` body public static func put(_ path: Path, body: MultipartFormData, query: [String: QueryParam] = [:]) -> Self { @@ -59,19 +59,19 @@ public struct Request { public static func put(_ path: Path, query: [String: QueryParam] = [:]) -> Self { self.init(path: path, method: .put, query: query, body: nil) } - + /// Creates a request suitable for a HTTP PATCH with a `Encodable` body public static func patch(_ path: Path, body: Encodable, query: [String: QueryParam] = [:]) -> Self { self.init(path: path, method: .patch, query: query, body: .encodable(body)) } - + /// Creates a request suitable for a HTTP PATCH with a `MultipartFormData` body public static func patch(_ path: Path, body: MultipartFormData, query: [String: QueryParam] = [:]) -> Self { self.init(path: path, method: .patch, query: query, body: .multipart(body)) } - + /// Creates a request suitable for a HTTP DELETE public static func delete(_ path: Path, query: [String: QueryParam] = [:]) -> Self { self.init(path: path, method: .delete, query: query, body: nil) @@ -81,7 +81,7 @@ public struct Request { public static func delete(_ path: Path, body: Encodable, query: [String: QueryParam] = [:]) -> Self { self.init(path: path, method: .delete, query: query, body: nil) } - + /// Creates a Request. /// /// Use this init only if default provided static initializers (`.get`, `.post`, `.put`, `patch`, `.delete`) do not suit your needs. @@ -91,22 +91,22 @@ public struct Request { self.body = body self.query = query } - + /// Adds headers to the request public func headers(_ newHeaders: [HTTPHeader: String]) -> Self { var request = self - + request.headers.merge(newHeaders) { $1 } - + return request } - + /// Configures request cache policy public func cachePolicy(_ policy: URLRequest.CachePolicy) -> Self { var request = self - + request.cachePolicy = policy - + return request } } diff --git a/Sources/SimpleHTTP/Request/URL+Request.swift b/Sources/SimpleHTTP/Request/URL+Request.swift index 945eca5..6546fcd 100644 --- a/Sources/SimpleHTTP/Request/URL+Request.swift +++ b/Sources/SimpleHTTP/Request/URL+Request.swift @@ -9,15 +9,15 @@ extension URL { guard var components = URLComponents(string: request.path.value) else { throw URLComponents.Error.invalid(path: request.path) } - + let queryItems = (components.queryItems ?? []) + request.query.queryItems - + components.queryItems = queryItems.isEmpty ? nil : queryItems - + guard let url = components.url else { throw URLComponents.Error.cannotGenerateURL(components: components) } - + self = url } } @@ -30,7 +30,7 @@ extension URLComponents { } extension Dictionary where Key == String, Value == String { - fileprivate var queryItems: [URLQueryItem] { + fileprivate var queryItems: [URLQueryItem] { map { URLQueryItem(name: $0.key, value: $0.value) } } } diff --git a/Sources/SimpleHTTP/Response/DataResponse.swift b/Sources/SimpleHTTP/Response/DataResponse.swift index a4bb8fc..def8101 100644 --- a/Sources/SimpleHTTP/Response/DataResponse.swift +++ b/Sources/SimpleHTTP/Response/DataResponse.swift @@ -14,7 +14,7 @@ extension URLDataResponse { guard let decoder = errorDecoder, !data.isEmpty else { throw error } - + throw try decoder(data) } } diff --git a/Sources/SimpleHTTP/Session/Session+Combine.swift b/Sources/SimpleHTTP/Session/Session+Combine.swift index 5b5eb8e..67933ff 100644 --- a/Sources/SimpleHTTP/Session/Session+Combine.swift +++ b/Sources/SimpleHTTP/Session/Session+Combine.swift @@ -10,7 +10,7 @@ extension Session { /// - Returns: a Publisher emitting Output on success, an error otherwise public func publisher(for request: Request) -> AnyPublisher { let subject = PassthroughSubject() - + Task { do { subject.send(try await response(for: request)) @@ -20,13 +20,13 @@ extension Session { subject.send(completion: .failure(error)) } } - + return subject.eraseToAnyPublisher() } - + public func publisher(for request: Request) -> AnyPublisher { let subject = PassthroughSubject() - + Task { do { subject.send(try await response(for: request)) @@ -36,7 +36,7 @@ extension Session { subject.send(completion: .failure(error)) } } - + return subject.eraseToAnyPublisher() } } diff --git a/Sources/SimpleHTTP/Session/Session.swift b/Sources/SimpleHTTP/Session/Session.swift index c29d994..9fb5873 100644 --- a/Sources/SimpleHTTP/Session/Session.swift +++ b/Sources/SimpleHTTP/Session/Session.swift @@ -72,7 +72,7 @@ extension Session { do { let result = try await dataTask(urlRequest) - + try result.validate(errorDecoder: config.errorConverter) return Response(data: result.data, request: modifiedRequest) diff --git a/Sources/SimpleHTTP/Session/SessionConfiguration.swift b/Sources/SimpleHTTP/Session/SessionConfiguration.swift index 05019dd..6797548 100644 --- a/Sources/SimpleHTTP/Session/SessionConfiguration.swift +++ b/Sources/SimpleHTTP/Session/SessionConfiguration.swift @@ -13,7 +13,7 @@ public struct SessionConfiguration { let interceptor: Interceptor /// a function decoding data (using `decoder`) as a custom error private(set) var errorConverter: DataErrorDecoder? - + /// - Parameter encoder to use for request bodies /// - Parameter decoder used to decode http responses /// - Parameter decodeQueue: queue on which to decode data @@ -28,7 +28,7 @@ public struct SessionConfiguration { self.decodingQueue = decodingQueue self.interceptor = interceptors } - + /// - Parameter dataError: Error type to use when having error with data public init( encoder: ContentDataEncoder = JSONEncoder(), diff --git a/Sources/SimpleHTTPFoundation/Foundation/URLRequest/URLRequest+Encode.swift b/Sources/SimpleHTTPFoundation/Foundation/URLRequest/URLRequest+Encode.swift index 39243e0..cfe899e 100644 --- a/Sources/SimpleHTTPFoundation/Foundation/URLRequest/URLRequest+Encode.swift +++ b/Sources/SimpleHTTPFoundation/Foundation/URLRequest/URLRequest+Encode.swift @@ -7,16 +7,16 @@ import FoundationNetworking extension URLRequest { public func encodedBody(_ body: Encodable, encoder: ContentDataEncoder) throws -> Self { var request = self - + try request.encodeBody(body, encoder: encoder) - + return request } - + /// Use a `Encodable` object as request body and set the "Content-Type" header associated to the encoder public mutating func encodeBody(_ body: Encodable, encoder: ContentDataEncoder) throws { httpBody = try body.encoded(with: encoder) setHeaders([.contentType: type(of: encoder).contentType.value]) } - + } diff --git a/Sources/SimpleHTTPFoundation/Foundation/URLRequest/URLRequest+HTTPHeader.swift b/Sources/SimpleHTTPFoundation/Foundation/URLRequest/URLRequest+HTTPHeader.swift index 5c9a4c5..f95ba42 100644 --- a/Sources/SimpleHTTPFoundation/Foundation/URLRequest/URLRequest+HTTPHeader.swift +++ b/Sources/SimpleHTTPFoundation/Foundation/URLRequest/URLRequest+HTTPHeader.swift @@ -11,7 +11,7 @@ extension URLRequest { setValue(value, forHTTPHeaderField: header.key) } } - + /// Return a new `URLRequest`` with added `headers`` public func settingHeaders(_ headers: HTTPHeaderFields) -> Self { var urlRequest = self diff --git a/Sources/SimpleHTTPFoundation/Foundation/URLRequest/URLRequest+URL.swift b/Sources/SimpleHTTPFoundation/Foundation/URLRequest/URLRequest+URL.swift index e6d5cf9..2e69001 100644 --- a/Sources/SimpleHTTPFoundation/Foundation/URLRequest/URLRequest+URL.swift +++ b/Sources/SimpleHTTPFoundation/Foundation/URLRequest/URLRequest+URL.swift @@ -5,11 +5,11 @@ extension URLRequest { public func relativeTo(_ baseURL: URL) -> URLRequest { var urlRequest = self var components = URLComponents(string: baseURL.appendingPathComponent(url?.path ?? "").absoluteString) - + components?.percentEncodedQuery = url?.query - + urlRequest.url = components?.url - + return urlRequest } } diff --git a/Sources/SimpleHTTPFoundation/Foundation/URLSession+Async.swift b/Sources/SimpleHTTPFoundation/Foundation/URLSession+Async.swift index e2540ed..db5a0b7 100644 --- a/Sources/SimpleHTTPFoundation/Foundation/URLSession+Async.swift +++ b/Sources/SimpleHTTPFoundation/Foundation/URLSession+Async.swift @@ -8,11 +8,11 @@ extension URLSession { if let error = error { return promise.resume(throwing: error) } - + guard let data = data, let response = response else { return promise.resume(throwing: URLError(.badServerResponse)) } - + promise.resume(returning: (data, response)) } .resume() diff --git a/Sources/SimpleHTTPFoundation/HTTP/HTTPContentType.swift b/Sources/SimpleHTTPFoundation/HTTP/HTTPContentType.swift index de2fe86..7eaf3bb 100644 --- a/Sources/SimpleHTTPFoundation/HTTP/HTTPContentType.swift +++ b/Sources/SimpleHTTPFoundation/HTTP/HTTPContentType.swift @@ -3,11 +3,11 @@ import Foundation /// A struct representing a http header content type value public struct HTTPContentType: Hashable, ExpressibleByStringLiteral { public let value: String - + public init(value: String) { self.value = value } - + public init(stringLiteral value: StringLiteralType) { self.value = value } diff --git a/Sources/SimpleHTTPFoundation/HTTP/HTTPError.swift b/Sources/SimpleHTTPFoundation/HTTP/HTTPError.swift index 649dd86..89b4ff7 100644 --- a/Sources/SimpleHTTPFoundation/HTTP/HTTPError.swift +++ b/Sources/SimpleHTTPFoundation/HTTP/HTTPError.swift @@ -3,11 +3,11 @@ import Foundation /// An error generated by a HTTP response public struct HTTPError: Error, Equatable, ExpressibleByIntegerLiteral { public let statusCode: Int - + public init(statusCode: Int) { self.statusCode = statusCode } - + public init(integerLiteral value: IntegerLiteralType) { self.init(statusCode: value) } @@ -15,25 +15,25 @@ public struct HTTPError: Error, Equatable, ExpressibleByIntegerLiteral { public extension HTTPError { static let badRequest: Self = 400 - + static let unauthorized: Self = 401 - + /// Request contained valid data and was understood by the server, but the server is refusing action static let forbidden: Self = 403 - + static let notFound: Self = 404 - + static let requestTimeout: Self = 408 - + /// Generic error message when an unexpected condition was encountered and no more specific message is suitable static let serverError: Self = 500 - + /// Server was acting as a gateway or proxy and received an invalid response from the upstream server static let badGateway: Self = 502 - + /// Server cannot handle the request (because it is overloaded or down for maintenance) static let serviceUnavailable: Self = 503 - + /// Server was acting as a gateway or proxy and did not receive a timely response from the upstream server static let gatewayTimeout: Self = 504 } diff --git a/Sources/SimpleHTTPFoundation/HTTP/HTTPHeader.swift b/Sources/SimpleHTTPFoundation/HTTP/HTTPHeader.swift index e26dc5d..e548bd9 100644 --- a/Sources/SimpleHTTPFoundation/HTTP/HTTPHeader.swift +++ b/Sources/SimpleHTTPFoundation/HTTP/HTTPHeader.swift @@ -6,7 +6,7 @@ public typealias HTTPHeaderFields = [HTTPHeader: String] /// A struct representing a http request header key public struct HTTPHeader: Hashable, ExpressibleByStringLiteral { public let key: String - + public init(stringLiteral value: StringLiteralType) { self.key = value } diff --git a/Tests/SimpleHTTPFoundationTests/Foundation/URLRequest/URLRequestTests+URL.swift b/Tests/SimpleHTTPFoundationTests/Foundation/URLRequest/URLRequestTests+URL.swift index 1d753e7..3d18b40 100644 --- a/Tests/SimpleHTTPFoundationTests/Foundation/URLRequest/URLRequestTests+URL.swift +++ b/Tests/SimpleHTTPFoundationTests/Foundation/URLRequest/URLRequestTests+URL.swift @@ -6,35 +6,35 @@ class URLRequestURLTests: XCTestCase { func test_relativeTo_requestURLHasBaseURL() { let request = URLRequest(url: URL(string: "path")!) let url = request.relativeTo(URL(string: "https://google.com")!).url - + XCTAssertEqual(url?.absoluteString, "https://google.com/path") } - + func test_relativeTo_urlStartWithSlash_requestPathContainBothPaths() { let request = URLRequest(url: URL(string: "/path")!) let url = request.relativeTo(URL(string: "https://google.com/lostAndFound")!).url - + XCTAssertEqual(url?.absoluteString, "https://google.com/lostAndFound/path") } - + func test_relativeTo_baseURLHasPath_requestContainBaseURLPath() { let request = URLRequest(url: URL(string: "concatenated")!) let url = request.relativeTo(URL(string: "https://google.com/firstPath")!).url - + XCTAssertEqual(url?.absoluteString, "https://google.com/firstPath/concatenated") } - + func test_relativeTo_baseURLHasQuery_requestHasNoQuery() { let request = URLRequest(url: URL(string: "concatenated")!) let url = request.relativeTo(URL(string: "https://google.com?param=1")!).url - + XCTAssertEqual(url?.absoluteString, "https://google.com/concatenated") } - + func test_relativeTo_urlHasQuery_requestHasQuery() { let request = URLRequest(url: URL(string: "concatenated?toKeep=1")!) let url = request.relativeTo(URL(string: "https://google.com?param=1")!).url - + XCTAssertEqual(url?.absoluteString, "https://google.com/concatenated?toKeep=1") } } diff --git a/Tests/SimpleHTTPFoundationTests/Foundation/URLRequest/URLRequestsTests+Encode.swift b/Tests/SimpleHTTPFoundationTests/Foundation/URLRequest/URLRequestsTests+Encode.swift index e039377..aa41852 100644 --- a/Tests/SimpleHTTPFoundationTests/Foundation/URLRequest/URLRequestsTests+Encode.swift +++ b/Tests/SimpleHTTPFoundationTests/Foundation/URLRequest/URLRequestsTests+Encode.swift @@ -6,12 +6,12 @@ import FoundationNetworking #endif class URLRequestEncodeTests: XCTest { - + func test_encodedBody_itSetContentTypeHeader() throws { - let body: [String:String] = [:] + let body: [String: String] = [:] let request = try URLRequest(url: URL(string: "/")!) .encodedBody(body, encoder: JSONEncoder()) - + XCTAssertEqual(request.allHTTPHeaderFields?["Content-Type"], "application/json") } } diff --git a/Tests/SimpleHTTPFoundationTests/Foundation/URLResponseValidateTests.swift b/Tests/SimpleHTTPFoundationTests/Foundation/URLResponseValidateTests.swift index 0a4f3d7..14ff95b 100644 --- a/Tests/SimpleHTTPFoundationTests/Foundation/URLResponseValidateTests.swift +++ b/Tests/SimpleHTTPFoundationTests/Foundation/URLResponseValidateTests.swift @@ -7,18 +7,18 @@ import FoundationNetworking class URLResponseValidateTests: XCTest { let url = URL(string: "/")! - + func test_validate_statusCodeIsOK_itThrowNoError() throws { try HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: nil)!.validate() } - + // we should never have redirection that's why we consider it as an error func test_validate_statusCodeIsRedirection_itThrow() { XCTAssertThrowsError( try HTTPURLResponse(url: url, statusCode: 302, httpVersion: nil, headerFields: nil)!.validate() ) } - + func test_validate_statusCodeIsClientError_itThrow() { XCTAssertThrowsError( try HTTPURLResponse(url: url, statusCode: 404, httpVersion: nil, headerFields: nil)!.validate() diff --git a/Tests/SimpleHTTPTests/MultipartForm/MultipartFormDataEncoderTests.swift b/Tests/SimpleHTTPTests/MultipartForm/MultipartFormDataEncoderTests.swift index 309ab28..2f37913 100644 --- a/Tests/SimpleHTTPTests/MultipartForm/MultipartFormDataEncoderTests.swift +++ b/Tests/SimpleHTTPTests/MultipartForm/MultipartFormDataEncoderTests.swift @@ -30,7 +30,6 @@ class MultipartFormDataEncoderTests: XCTestCase { XCTAssertEqual(encodedData, expectedData) } - func test_encode_data_multipleBodyPart() throws { let boundary = "boundary" var multipart = MultipartFormData(boundary: boundary) @@ -159,5 +158,4 @@ class MultipartFormDataEncoderTests: XCTestCase { XCTAssertEqual(encodedData, expectedData) } - } diff --git a/Tests/SimpleHTTPTests/Request/QueryParamTests.swift b/Tests/SimpleHTTPTests/Request/QueryParamTests.swift index b30cd89..51065d6 100644 --- a/Tests/SimpleHTTPTests/Request/QueryParamTests.swift +++ b/Tests/SimpleHTTPTests/Request/QueryParamTests.swift @@ -4,7 +4,7 @@ import SimpleHTTP class QueryParamTests: XCTestCase { func test_queryValue_multidimenstionalArray_returnFlattenCollection() { - let array: [[Int]] = [[1, 2, 3],[4,5,6]] + let array: [[Int]] = [[1, 2, 3], [4, 5, 6]] XCTAssertEqual(array.queryValue, .collection(["1", "2", "3", "4", "5", "6"])) } diff --git a/Tests/SimpleHTTPTests/Request/RequestTests.swift b/Tests/SimpleHTTPTests/Request/RequestTests.swift index 4001afa..63730ba 100644 --- a/Tests/SimpleHTTPTests/Request/RequestTests.swift +++ b/Tests/SimpleHTTPTests/Request/RequestTests.swift @@ -7,34 +7,34 @@ extension Path { class RequestTests: XCTestCase { let baseURL = URL(string: "https://google.fr")! - + func test_init_withPathAsString() { XCTAssertEqual(Request.get("hello_world").path, "hello_world") } - + func test_toURLRequest_setHttpMethod() throws { let request = try Request.post(.test, body: nil) .toURLRequest(encoder: JSONEncoder(), relativeTo: baseURL) - + XCTAssertEqual(request.httpMethod, "POST") } - + func test_toURLRequest_encodeBody() throws { let request = try Request.post(.test, body: BodyMock()) .toURLRequest(encoder: JSONEncoder(), relativeTo: baseURL) - + XCTAssertEqual(request.httpBody, try JSONEncoder().encode(BodyMock())) } - + func test_toURLRequest_setCachePolicy() throws { let request = try Request .get(.test) .cachePolicy(.returnCacheDataDontLoad) .toURLRequest(encoder: JSONEncoder(), relativeTo: baseURL) - + XCTAssertEqual(request.cachePolicy, .returnCacheDataDontLoad) } - + func test_toURLRequest_encodeMultipartBody() throws { let crlf = EncodingCharacters.crlf let boundary = "boundary" @@ -42,10 +42,10 @@ class RequestTests: XCTestCase { let url = try url(forResource: "swift", withExtension: "png") let name = "swift" try multipart.add(url: url, name: name) - + let request = try Request.post(.test, body: multipart) .toURLRequest(encoder: JSONEncoder(), relativeTo: baseURL) - + /// We can't use `XCTAssertEqual(request.httpBody, try multipart.encode)` /// The `encode` method is executed to fast and rase and error var body = Data() @@ -60,29 +60,29 @@ class RequestTests: XCTestCase { body.append(Boundary.data(for: .final, boundary: boundary)) XCTAssertEqual(request.httpBody, body) } - + func test_toURLRequest_bodyIsEncodable_fillContentTypeHeader() throws { let request = try Request.post(.test, body: BodyMock()) .toURLRequest(encoder: JSONEncoder(), relativeTo: baseURL) - + XCTAssertEqual(request.allHTTPHeaderFields?["Content-Type"], "application/json") } - + func test_toURLRequest_bodyIsMultipart_fillContentTypeHeader() throws { let boundary = "boundary" var multipart = MultipartFormData(boundary: boundary) let url = try url(forResource: "swift", withExtension: "png") let name = "swift" try multipart.add(url: url, name: name) - + let request = try Request.post(.test, body: multipart) .toURLRequest(encoder: JSONEncoder(), relativeTo: baseURL) - + XCTAssertEqual(request.allHTTPHeaderFields?["Content-Type"], HTTPContentType.multipart(boundary: multipart.boundary).value) } - + } private struct BodyMock: Encodable { - + } diff --git a/Tests/SimpleHTTPTests/Request/URLTests+Request.swift b/Tests/SimpleHTTPTests/Request/URLTests+Request.swift index 00fdc17..34e043d 100644 --- a/Tests/SimpleHTTPTests/Request/URLTests+Request.swift +++ b/Tests/SimpleHTTPTests/Request/URLTests+Request.swift @@ -9,14 +9,14 @@ class URLRequestTests: XCTestCase { "test" ) } - + func test_initFromRequest_pathHasQueryItems_urlQueryIsSetted() throws { XCTAssertEqual( try URL(from: Request.get("hello/world?test=1")).query, "test=1" ) } - + func test_initFromRequest_whenPathHasQueryItems_urlPathHasNoQuery() throws { XCTAssertEqual( try URL(from: Request.get("hello/world?test=1")).path, diff --git a/Tests/SimpleHTTPTests/Response/URLDataResponseTests.swift b/Tests/SimpleHTTPTests/Response/URLDataResponseTests.swift index 82e5ce6..635a56c 100644 --- a/Tests/SimpleHTTPTests/Response/URLDataResponseTests.swift +++ b/Tests/SimpleHTTPTests/Response/URLDataResponseTests.swift @@ -4,21 +4,21 @@ import Combine class URLDataResponseTests: XCTestCase { var cancellables: Set = [] - + override func tearDown() { cancellables = [] } - + func test_validate_responseIsError_dataIsEmpty_converterIsNotCalled() throws { let response = URLDataResponse(data: Data(), response: HTTPURLResponse.notFound) let transformer: DataErrorDecoder = { _ in XCTFail("transformer should not be called when data is empty") throw NSError() } - + XCTAssertThrowsError(try response.validate(errorDecoder: transformer)) } - + func test_validate_responseIsError_dataIsNotEmpty_returnCustomError() throws { let customError = CustomError(code: 22, message: "custom message") let response = URLDataResponse( @@ -28,7 +28,7 @@ class URLDataResponseTests: XCTestCase { let transformer: DataErrorDecoder = { data in return try JSONDecoder().decode(CustomError.self, from: data) } - + XCTAssertThrowsError(try response.validate(errorDecoder: transformer)) { XCTAssertEqual($0 as? CustomError, customError) } diff --git a/Tests/SimpleHTTPTests/Ressources/URL+fromBundle.swift b/Tests/SimpleHTTPTests/Ressources/URL+fromBundle.swift index 40072ef..8ab3bb6 100644 --- a/Tests/SimpleHTTPTests/Ressources/URL+fromBundle.swift +++ b/Tests/SimpleHTTPTests/Ressources/URL+fromBundle.swift @@ -8,7 +8,7 @@ extension URL { } -// MARK - Foundation.Bundle +// MARK: - Foundation.Bundle #if XCODE_BUILD extension Foundation.Bundle { diff --git a/Tests/SimpleHTTPTests/Session/Session+CombineTests.swift b/Tests/SimpleHTTPTests/Session/Session+CombineTests.swift index 26923f1..d817ef1 100644 --- a/Tests/SimpleHTTPTests/Session/Session+CombineTests.swift +++ b/Tests/SimpleHTTPTests/Session/Session+CombineTests.swift @@ -8,19 +8,19 @@ import Combine class SessionCombineTests: XCTestCase { var cancellables: Set = [] - + override func tearDown() { cancellables.removeAll() } - + func test_publisher_returnOutput() { let output = CombineTest(value: "hello world") let expectation = XCTestExpectation() - + let session = Session(baseURL: URL(string: "/")!) { _ in URLDataResponse(data: try! JSONEncoder().encode(output), response: .success) } - + session.publisher(for: Request.get("test")) .sink( receiveCompletion: { _ in }, @@ -30,7 +30,7 @@ class SessionCombineTests: XCTestCase { } ) .store(in: &cancellables) - + wait(for: [expectation], timeout: 1) } } diff --git a/Tests/SimpleHTTPTests/Session/SessionTests.swift b/Tests/SimpleHTTPTests/Session/SessionTests.swift index 6a35047..cf226aa 100644 --- a/Tests/SimpleHTTPTests/Session/SessionTests.swift +++ b/Tests/SimpleHTTPTests/Session/SessionTests.swift @@ -6,17 +6,17 @@ class SessionAsyncTests: XCTestCase { let baseURL = URL(string: "https://sessionTests.io")! let encoder = JSONEncoder() let decoder = JSONDecoder() - + func test_response_responseIsValid_decodedOutputIsReturned() async throws { let expectedResponse = Content(value: "response") - let session = sesssionStub() { + let session = sesssionStub { URLDataResponse(data: try! JSONEncoder().encode(expectedResponse), response: .success) } let response = try await session.response(for: Request.test()) - + XCTAssertEqual(response, expectedResponse) } - + func test_response_responseIsValid_adaptResponseThrow_itReturnAnError() async { let output = Content(value: "adapt throw") let interceptor = InterceptorStub() @@ -24,11 +24,11 @@ class SessionAsyncTests: XCTestCase { interceptor: [interceptor], data: { URLDataResponse(data: try! JSONEncoder().encode(output), response: .success) } ) - + interceptor.adaptResponseMock = { _, _ in throw CustomError() } - + do { _ = try await session.response(for: .test()) XCTFail() @@ -37,40 +37,40 @@ class SessionAsyncTests: XCTestCase { XCTAssertEqual(error as? CustomError, CustomError()) } } - + func test_response_rescue_rescueIsSuccess_itRetryRequest() async throws { var isRescued = false let interceptor = InterceptorStub() let session = sesssionStub(interceptor: [interceptor]) { URLDataResponse(data: Data(), response: isRescued ? .success : .unauthorized) } - + interceptor.rescueRequestErrorMock = { _ in isRescued.toggle() return Just(()).setFailureType(to: Error.self).eraseToAnyPublisher() } - + _ = try await session.response(for: .void()) - + XCTAssertTrue(isRescued) } - + func test_response_outputIsDecoded_itCallInterceptorReceivedResponse() async throws { let output = Content(value: "hello") let interceptor = InterceptorStub() let session = sesssionStub(interceptor: [interceptor]) { URLDataResponse(data: try! JSONEncoder().encode(output), response: .success) } - + interceptor.receivedResponseMock = { response, _ in let response = response as? Result - + XCTAssertEqual(try? response?.get(), output) } - + _ = try await session.response(for: .test()) } - + func test_response_httpDataHasCustomError_returnCustomError() async throws { let session = Session( baseURL: baseURL, @@ -78,7 +78,7 @@ class SessionAsyncTests: XCTestCase { dataTask: { _ in URLDataResponse(data: try! JSONEncoder().encode(CustomError()), response: .unauthorized) }) - + do { _ = try await session.response(for: .test()) XCTFail() @@ -87,12 +87,12 @@ class SessionAsyncTests: XCTestCase { XCTAssertEqual(error as? CustomError, CustomError()) } } - + /// helper to create a session for testing private func sesssionStub(interceptor: CompositeInterceptor = [], data: @escaping () throws -> URLDataResponse) -> Session { let config = SessionConfiguration(encoder: encoder, decoder: decoder, interceptors: interceptor) - + return Session(baseURL: baseURL, configuration: config, dataTask: { _ in try data() }) } } @@ -106,14 +106,14 @@ private struct Content: Codable, Equatable { } private struct CustomError: Error, Codable, Equatable { - + } private extension Request { static func test() -> Self where Output == Content { .get(.test) } - + static func void() -> Self where Output == Void { .get(.test) } @@ -121,25 +121,25 @@ private extension Request { private class InterceptorStub: Interceptor { var rescueRequestErrorMock: ((Error) -> AnyPublisher?)? - var receivedResponseMock: ((Any, Any) -> ())? + var receivedResponseMock: ((Any, Any) -> Void)? var adaptResponseMock: ((Any, Any) throws -> Any)? - + func adaptRequest(_ request: Request) -> Request { request } - + func rescueRequest(_ request: Request, error: Error) -> AnyPublisher? { rescueRequestErrorMock?(error) } - + func adaptOutput(_ output: Output, for request: Request) throws -> Output { guard let mock = adaptResponseMock else { return output } - + return try mock(output, request) as! Output } - + func receivedResponse(_ result: Result, for request: Request) { receivedResponseMock?(result, request) }