generated from ydataai/opensource-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(client): removed generic content from Request (#20)
- Loading branch information
Showing
6 changed files
with
153 additions
and
146 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,53 +1,86 @@ | ||
import Vapor | ||
|
||
public protocol InternalRequest { | ||
associatedtype Content | ||
|
||
var method: HTTPMethod { get } | ||
var path: String? { get } | ||
var headers: HTTPHeaders? { get } | ||
var query: [URLQueryItem]? { get } | ||
var content: Content? { get } | ||
var content: ContentContainer { get } | ||
var body: ByteBuffer? { get } | ||
} | ||
|
||
public extension Internal { | ||
struct NoContentRequest: InternalRequest { | ||
public typealias Content = Optional<Void> | ||
|
||
struct ContentRequest: InternalRequest { | ||
public let method: HTTPMethod | ||
public let path: String? | ||
public let headers: HTTPHeaders? | ||
public var headers: HTTPHeaders? | ||
public let query: [URLQueryItem]? | ||
public let content: Content? = nil | ||
|
||
public var body: ByteBuffer? | ||
|
||
public init(method: HTTPMethod, | ||
path: String? = nil, | ||
headers: HTTPHeaders? = nil, | ||
query: [URLQueryItem]? = nil) { | ||
public init<C: Content>(method: HTTPMethod, | ||
path: String? = nil, | ||
headers: HTTPHeaders? = nil, | ||
query: [URLQueryItem]? = nil, | ||
content: C) throws { | ||
self.method = method | ||
self.path = path | ||
self.headers = headers | ||
self.query = query | ||
try self.content.encode(content) | ||
} | ||
} | ||
|
||
struct ContentRequest<Content: Encodable>: InternalRequest { | ||
public let method: HTTPMethod | ||
public let path: String? | ||
public let headers: HTTPHeaders? | ||
public let query: [URLQueryItem]? | ||
public let content: Content? | ||
|
||
|
||
public init(method: HTTPMethod, | ||
path: String? = nil, | ||
headers: HTTPHeaders? = nil, | ||
query: [URLQueryItem]? = nil, | ||
content: Content? = nil) { | ||
path: String? = nil, | ||
headers: HTTPHeaders? = nil, | ||
query: [URLQueryItem]? = nil) { | ||
self.method = method | ||
self.path = path | ||
self.headers = headers | ||
self.query = query | ||
self.content = content | ||
} | ||
} | ||
} | ||
|
||
public extension Internal.ContentRequest { | ||
private struct _ContentContainer: ContentContainer { | ||
var body: ByteBuffer? | ||
var headers: HTTPHeaders | ||
|
||
var contentType: HTTPMediaType? { headers.contentType } | ||
|
||
func decode<D>(_ decodable: D.Type, using decoder: ContentDecoder) throws -> D where D : Decodable { | ||
guard let body = self.body else { | ||
throw Abort(.lengthRequired) | ||
} | ||
return try decoder.decode(D.self, from: body, headers: self.headers) | ||
} | ||
|
||
mutating func encode<E>(_ encodable: E, using encoder: ContentEncoder) throws where E : Encodable { | ||
var body = ByteBufferAllocator().buffer(capacity: 0) | ||
try encoder.encode(encodable, to: &body, headers: &self.headers) | ||
self.body = body | ||
} | ||
} | ||
|
||
var content: ContentContainer { | ||
get { | ||
return _ContentContainer(body: self.body, headers: self.headers ?? [:]) | ||
} | ||
set { | ||
let container = (newValue as! _ContentContainer) | ||
self.body = container.body | ||
self.headers += container.headers | ||
} | ||
} | ||
} | ||
|
||
extension Optional where Wrapped == HTTPHeaders { | ||
static func +=(left: inout Self, right: Wrapped) { | ||
if var left = left { | ||
left.add(contentsOf: right) | ||
} | ||
|
||
left = right | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import Foundation | ||
import Vapor | ||
|
||
extension ResponseEncodable where Self: InternalResponse { | ||
func encodeResponse(for request: Request) -> EventLoopFuture<Vapor.Response> { | ||
let response = Vapor.Response(status: status, headers: headers) | ||
response.body ?= body.flatMap(Vapor.Response.Body.init) | ||
return request.eventLoop.makeSucceededFuture(response) | ||
} | ||
} | ||
|
||
public extension EventLoopFuture where Value: InternalResponse { | ||
func mapContent<NewValue>(_ callback: @escaping (ContentContainer) throws -> (NewValue)) | ||
-> EventLoopFuture<NewValue> where NewValue: Content { | ||
flatMapThrowing { try callback($0.content) } | ||
} | ||
|
||
func mapToContent<R>() -> EventLoopFuture<R> where R: Decodable { | ||
flatMapThrowing { response -> R in try response.content.decode(R.self) } | ||
} | ||
|
||
func flatMapContentThrowing<NewValue>(_ callback: @escaping (ContentContainer) throws -> (NewValue)) | ||
-> EventLoopFuture<NewValue> { flatMapThrowing { try callback($0.content) } } | ||
|
||
func flatMapContentResult<NewValue, E>(_ callback: @escaping (ContentContainer) -> Result<NewValue, E>) | ||
-> EventLoopFuture<NewValue> where NewValue: Content, E: Error { flatMapResult { callback($0.content) } } | ||
} | ||
|
||
extension ClientResponse: InternalResponse { | ||
public init(headers: HTTPHeaders, status: HTTPResponseStatus, body: ByteBuffer?) { | ||
self.init(status: status, headers: headers, body: body) | ||
} | ||
|
||
@inlinable | ||
func map<NewValue>(_ callback: (ContentContainer) throws -> (NewValue)) throws -> Self where NewValue: Content { | ||
let newValue = try callback(content) | ||
|
||
var newResponse = Self.init(headers: headers, status: status, body: nil) | ||
try newResponse.content.encode(newValue) | ||
|
||
return newResponse | ||
} | ||
} | ||
|
||
public extension ClientResponse { | ||
func mapToModel<C>() throws -> C where C: Decodable { | ||
switch status.code { | ||
case (100..<400): | ||
|
||
do { | ||
return try content.decode(C.self) | ||
} catch { | ||
throw Internal.ErrorResponse(headers: [:], | ||
status: .internalServerError, | ||
message: "failed to decode response \(error)") | ||
} | ||
default: | ||
do { | ||
let contentError = try content.decode(Internal.ServiceError.self) | ||
throw Internal.ErrorResponse(headers: headers, | ||
status: status, | ||
message:contentError.message) | ||
} catch { | ||
throw Internal.ErrorResponse(headers: [:], | ||
status: .internalServerError, | ||
message: "failed to decode response with error \(error)") | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.