Skip to content

Commit

Permalink
make swift client http framework agnostic
Browse files Browse the repository at this point in the history
  • Loading branch information
joshmossas committed Aug 11, 2024
1 parent 9aacc70 commit 7faf580
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 158 deletions.
110 changes: 1 addition & 109 deletions languages/swift/swift-client/Package.resolved
Original file line number Diff line number Diff line change
@@ -1,42 +1,6 @@
{
"originHash" : "7491af28128e712dc018dbc739c45b336ab52e3b56b9913668a49a1b43a2ef00",
"originHash" : "c0eaa29b66194727c7032b4e5c391e30bb11f4f42a4f6cff71845495c41c98f9",
"pins" : [
{
"identity" : "async-http-client",
"kind" : "remoteSourceControl",
"location" : "https://github.com/swift-server/async-http-client.git",
"state" : {
"revision" : "0ae99db85b2b9d1e79b362bd31fd1ffe492f7c47",
"version" : "1.21.2"
}
},
{
"identity" : "swift-algorithms",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-algorithms",
"state" : {
"revision" : "f6919dfc309e7f1b56224378b11e28bab5bccc42",
"version" : "1.2.0"
}
},
{
"identity" : "swift-atomics",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-atomics.git",
"state" : {
"revision" : "cd142fd2f64be2100422d658e7411e39489da985",
"version" : "1.2.0"
}
},
{
"identity" : "swift-collections",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-collections.git",
"state" : {
"revision" : "3d2dc41a01f9e49d84f0a3925fb858bed64f702d",
"version" : "1.1.2"
}
},
{
"identity" : "swift-http-types",
"kind" : "remoteSourceControl",
Expand All @@ -45,78 +9,6 @@
"revision" : "ae67c8178eb46944fd85e4dc6dd970e1f3ed6ccd",
"version" : "1.3.0"
}
},
{
"identity" : "swift-log",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-log.git",
"state" : {
"revision" : "9cb486020ebf03bfa5b5df985387a14a98744537",
"version" : "1.6.1"
}
},
{
"identity" : "swift-nio",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio",
"state" : {
"revision" : "4c4453b489cf76e6b3b0f300aba663eb78182fad",
"version" : "2.70.0"
}
},
{
"identity" : "swift-nio-extras",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-extras.git",
"state" : {
"revision" : "05c36b57453d23ea63785d58a7dbc7b70ba1745e",
"version" : "1.23.0"
}
},
{
"identity" : "swift-nio-http2",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-http2.git",
"state" : {
"revision" : "b5f7062b60e4add1e8c343ba4eb8da2e324b3a94",
"version" : "1.34.0"
}
},
{
"identity" : "swift-nio-ssl",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-ssl.git",
"state" : {
"revision" : "a9fa5efd86e7ce2e5c1b6de113262e58035ca251",
"version" : "2.27.1"
}
},
{
"identity" : "swift-nio-transport-services",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-transport-services.git",
"state" : {
"revision" : "38ac8221dd20674682148d6451367f89c2652980",
"version" : "1.21.0"
}
},
{
"identity" : "swift-numerics",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-numerics.git",
"state" : {
"revision" : "0a5bc04095a675662cf24757cc0640aa2204253b",
"version" : "1.0.2"
}
},
{
"identity" : "swift-system",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-system.git",
"state" : {
"revision" : "d2ba781702a1d8285419c15ee62fd734a9437ff5",
"version" : "1.3.2"
}
}
],
"version" : 3
Expand Down
4 changes: 2 additions & 2 deletions languages/swift/swift-client/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ let package = Package(
targets: ["ArriClient"]),
],
dependencies: [
.package(url: "https://github.com/swift-server/async-http-client.git", from: "1.9.0")
.package(url: "https://github.com/apple/swift-http-types.git", from: "1.0.0")
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.target(
name: "ArriClient",
dependencies: [
.product(name: "AsyncHTTPClient", package: "async-http-client")
.product(name: "HTTPTypes", package: "swift-http-types")
]
),
.testTarget(
Expand Down
77 changes: 33 additions & 44 deletions languages/swift/swift-client/Sources/ArriClient/ArriClient.swift
Original file line number Diff line number Diff line change
@@ -1,83 +1,69 @@
// The Swift Programming Language
// https://docs.swift.org/swift-book
import Foundation
import AsyncHTTPClient
import NIOCore
import HTTPTypes

let jsonEncoder = JSONEncoder()
let jsonDecoder = JSONDecoder()

extension HTTPField.Name {
static let clientVersionHeader = Self("client-version")!
}


public func parsedArriHttpRequest<TParams: ArriClientModel, TResponse: ArriClientModel>(
http: ArriHTTPClient,
url: String,
method: ArriHTTPMethod,
method: HTTPRequest.Method,
headers: () -> Dictionary<String, String>,
clientVersion: String,
params: TParams?
params: TParams?,
timeoutSeconds: Int64 = 60
) async throws -> TResponse {
var request = HTTPClientRequest(url: url)
let parsedURL = URL(string: url)
if parsedURL == nil {
throw ArriRequestError.invalidURLError
}
var request = HTTPRequest(method: method, scheme: parsedURL!.scheme, authority: parsedURL!.host, path: parsedURL!.path)
if !clientVersion.isEmpty {
request.headers.add(name: "client-version", value: clientVersion)
request.headerFields[.clientVersionHeader] = clientVersion
}
let headerDict = headers()
for (key, value) in headerDict {
request.headers.add(name: key, value: value)
}
switch method {
case .get:
request.method = .GET
break;
case .patch:
request.method = .PATCH
break;
case .post:
request.method = .POST
break;
case .put:
request.method = .PUT
break;
case .delete:
request.method = .DELETE
break;
let headerName = HTTPField.Name(key)
if headerName != nil {
request.headerFields[headerName!] = value
}
}
var body: String?
switch method {
case .get:
if params != nil {
request.url = request.url + "?\(params!.toQueryString())"
request.path = request.path! + "?\(params!.toQueryString())"
}
break;
default:
if params != nil {
request.headers.add(name: "Content-Type", value: "application/json")
request.body = .bytes(ByteBuffer(string: params!.toJSONString()))
request.headerFields[.contentType] = "application/json"
body = params!.toJSONString()
}
break;
}
let response = try await HTTPClient.shared.execute(request, timeout: .seconds(30))
let (response, bodyResponse) = try await http.handleHTTPRequest(request: request, body: body)
if response.status == .ok {
let body = try await response.body.collect(upTo: 1024 * 1024)
let jsonData = String(buffer: body)
let result = TResponse.init(JSONString: jsonData)
let result = TResponse.init(JSONString: bodyResponse ?? "")
return result
}
let body = try await response.body.collect(upTo: 1024 * 1024)
let jsonData = String(buffer: body)
var error = ArriResponseError(JSONString: jsonData)
var error = ArriResponseError(JSONString: bodyResponse ?? "")
if error.code == 0 {
error.code = response.status.code
error.code = UInt(response.status.code)
}
throw error
}

public enum ArriHTTPMethod {
case get
case post
case put
case patch
case delete
}

public enum ArriRequestError: Error {
case invalidUrl
case invalidURLError
case notImplementedError
}

public struct ArriResponseError: ArriClientModel, Error {
Expand Down Expand Up @@ -152,6 +138,9 @@ public func serializeAny(input: JSON) -> String {
return "null"
}
}
public protocol ArriHTTPClient {
func handleHTTPRequest(request: HTTPRequest, body: String?) async throws -> (response: HTTPResponse, body: String?)
}
public protocol ArriClientModel: Equatable {
init()
init(json: JSON)
Expand Down
4 changes: 1 addition & 3 deletions languages/swift/swift-codegen-reference/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ let package = Package(
// Targets can depend on other targets in this package and products from dependencies.
.target(
name: "SwiftCodegenReference",
dependencies: [
"ArriClient"
]
dependencies: ["ArriClient"]
),
.testTarget(
name: "SwiftCodegenReferenceTests",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,99 @@
import Foundation
import ArriClient
import HTTPTypes

public class ExampleClient {
var baseURL: String
var HTTPClient: ArriHTTPClient
var headers: () -> Dictionary<String, String>
var books: ExampleClientBooksService

public init(
baseURL: String,
HTTPClient: ArriHTTPClient,
headers: @escaping () -> Dictionary<String, String>
) {
self.baseURL = baseURL
self.HTTPClient = HTTPClient
self.headers = headers
self.books = ExampleClientBooksService(
baseURL: baseURL,
HTTPClient: HTTPClient,
headers: headers
)
}

func sendObject(_ params: NestedObject) async throws -> NestedObject {
let result: NestedObject = try await parsedArriHttpRequest(
http: self.HTTPClient,
url: "\(self.baseURL)/send-object",
method: HTTPRequest.Method.post,
headers: self.headers,
clientVersion: "20",
params: params
)
return result
}
}

public class ExampleClientBooksService {
var baseURL: String
var HTTPClient: ArriHTTPClient
var headers: () -> Dictionary<String, String>

public init(
baseURL: String,
HTTPClient: ArriHTTPClient,
headers: @escaping () -> Dictionary<String, String>
) {
self.baseURL = baseURL
self.HTTPClient = HTTPClient
self.headers = headers
}
/// Get a book
func getBook(_ params: BookParams) async throws -> Book {
let result: Book = try await parsedArriHttpRequest(
http: self.HTTPClient,
url: "\(self.baseURL)/books/get-book",
method: HTTPRequest.Method.get,
headers: self.headers,
clientVersion: "20",
params: params
)
return result
}
/// Create a book
@available(*, deprecated)
func createBook(_ params: Book) async throws -> Book {
let result: Book = try await parsedArriHttpRequest(
http: self.HTTPClient,
url: "\(self.baseURL)/books/create-book",
method: HTTPRequest.Method.post,
headers: self.headers,
clientVersion: "20",
params: params
)
return result
}
@available(*, deprecated)
func watchBook(_ params: Book) async throws -> Book {
throw ArriRequestError.notImplementedError
}
func createConnection(_ params: BookParams) async throws -> Book {
throw ArriRequestError.notImplementedError
}
}

/// This is a book
public struct Book: ArriClientModel {
/// The book ID
public var id: String = ""
/// The book title
public var name: String = ""
/// When the book was created
@available(*, deprecated)
public var createdAt: Date = Date.now
@available(*, deprecated)
public var updatedAt: Date = Date.now

public init(
Expand Down

0 comments on commit 7faf580

Please sign in to comment.