Skip to content

Instantly share code, notes, and snippets.

@vzsg
Created March 3, 2019 11:19
Show Gist options
  • Save vzsg/7a309d24a1a92cf706a0dd17183673b9 to your computer and use it in GitHub Desktop.
Save vzsg/7a309d24a1a92cf706a0dd17183673b9 to your computer and use it in GitHub Desktop.
Using SwiftProtobuf with Vapor 3
import Vapor
import SwiftProtobuf
import Foundation
extension Request {
public func decodeMessage<M: SwiftProtobuf.Message>(_ type: M.Type = M.self) throws -> M {
let data = http.body.data ?? Data()
if http.contentType == MediaType.json {
return try M(jsonUTF8Data: data)
}
return try M(serializedData: data)
}
}
extension SwiftProtobuf.Message {
fileprivate func toHTTPResponse(json: Bool = false) throws -> HTTPResponse {
var http = HTTPResponse(status: .ok)
if json {
http.contentType = MediaType.json
http.body = try jsonUTF8Data().convertToHTTPBody()
} else {
http.contentType = MediaType(type: "application", subType: "x-protobuf")
http.body = try serializedData().convertToHTTPBody()
}
return http
}
public func toResponse(on req: Request) throws -> Response {
let json = req.http.accept.contains(where: { $0.mediaType == MediaType.json })
return Response(http: try toHTTPResponse(json: json), using: req)
}
}
extension Future {
public func toResponse<M>(_ type: M.Type = M.self, on req: Request) -> Future<Response> where Expectation: SwiftProtobuf.Message {
return map { try $0.toResponse(on: req) }
}
}
router.get("test") { req -> Response in
var profile = Proto_UserProfile()
var user = Proto_User()
user.nickname = "FooBar"
user.firstName = "Foo"
user.lastName = "Bar"
profile.user = user
return try profile.toResponse(on: req)
}
// Calling without an accept header: curl localhost:8080/test
// --> returns binary message
// Calling with accept header: curl localhost:8080/test -H "Accept: application/json"
// --> returns JSON
// {"user":{"nickname":"FooBar","firstName":"Foo","lastName":"Bar"}}
@adirburke
Copy link

adirburke commented Jan 10, 2020

Updated for Vapor 4

import Vapor
import SwiftProtobuf
import Foundation


extension ByteBuffer {
    public func allData() -> Data {
        return getData(at: 0, length: readableBytes) ?? Data()
    }
}

extension Request {
    public func decodeMessage<M: SwiftProtobuf.Message>(_ type: M.Type = M.self) throws -> M {
        let data = self.body.data?.allData() ?? Data()

        if headers.contentType == .json {
            return try M(jsonUTF8Data: data)
        }
        return try M(serializedData: data)
    }
}



extension SwiftProtobuf.Message {
    fileprivate func toHTTPResponse(json: Bool = false) throws -> Response {
        let http = Response(status: .ok)

        if json {
            http.headers.contentType = .json
            http.body = .init(string: try jsonString())
        } else {
            http.headers.contentType = .init(type: "application", subType: "protobuf")
            http.body = .init(data: try serializedData())
        }
        return http
    }

    public func toResponse(on req: Request) throws -> Response {
        let json = req.headers.accept.contains(where: { $0.mediaType == .json })
        let response = try toHTTPResponse(json: json)
        return response
    }
}

extension EventLoopFuture {
    public func toResponse<M>(_ type: M.Type = M.self, on req: Request) -> EventLoopFuture<Response> where Value : SwiftProtobuf.Message {
        return self.flatMapThrowing { try $0.toResponse(on: req) }

    }
}

@vzsg
Copy link
Author

vzsg commented Jan 13, 2020

Thank you!

@3a4oT
Copy link

3a4oT commented Jan 25, 2020

Thanks for sharing! It is clever!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment