Skip to content

Commit

Permalink
Various patch changes (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
fpseverino authored Oct 21, 2024
1 parent 1b2d98b commit f739d45
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 122 deletions.
9 changes: 4 additions & 5 deletions Sources/AppleMapsKit/AppleMapsClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -456,11 +456,11 @@ public struct AppleMapsClient: Sendable {
) async throws -> [Eta] {
var destinationCoordinates: [(latitude: Double, longitude: Double)] = []
for destination in destinations {
try await destinationCoordinates.append(self.getCoordinate(from: destination))
try await destinationCoordinates.append(getCoordinate(from: destination))
}

return try await self.eta(
from: self.getCoordinate(from: origin),
return try await eta(
from: getCoordinate(from: origin),
to: destinationCoordinates,
transportType: transportType,
departureDate: departureDate,
Expand Down Expand Up @@ -520,8 +520,7 @@ public struct AppleMapsClient: Sendable {
/// - Throws: Error response object.
private func httpGet(url: URL) async throws -> ByteBuffer {
var headers = HTTPHeaders()
let accessToken = try await authorizationProvider.validToken().accessToken
headers.add(name: "Authorization", value: "Bearer \(accessToken)")
headers.add(name: "Authorization", value: "Bearer \(try await authorizationProvider.accessToken)")

var request = HTTPClientRequest(url: url.absoluteString)
request.headers = headers
Expand Down
2 changes: 1 addition & 1 deletion Sources/AppleMapsKit/AppleMapsKitError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ public struct AppleMapsKitError: Error, Sendable {

extension AppleMapsKitError: CustomStringConvertible {
public var description: String {
"AppleMapsKitError(errorType: \(self.errorType))"
"AppleMapsKitError(errorType: \(errorType))"
}
}
150 changes: 71 additions & 79 deletions Sources/AppleMapsKit/Authorization/AuthorizationProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,101 +28,93 @@ actor AuthorizationProvider {
self.key = key
}

func validToken() async throws -> TokenResponse {
// If we're currently refreshing a token, await the value for our refresh task to make sure we return the refreshed token.
if let refreshTask {
return try await refreshTask.value
}
var accessToken: String {
get async throws {
// If we're currently refreshing a token, await the value for our refresh task to make sure we return the refreshed token.
if let refreshTask {
return try await refreshTask.value.accessToken
}

// If we don't have a current token, we request a new one.
guard let currentToken else {
return try await refreshToken()
}
// If we don't have a current token, we request a new one.
guard let currentToken else {
return try await newToken
}

if currentToken.isValid {
return currentToken
}
if currentToken.isValid {
return currentToken.accessToken
}

// None of the above applies so we'll need to refresh the token.
return try await refreshToken()
// None of the above applies so we'll need to refresh the token.
return try await newToken
}
}

private func refreshToken() async throws -> TokenResponse {
if let refreshTask {
return try await refreshTask.value
}
private var newToken: String {
get async throws {
// If we're currently refreshing a token, await the value for our refresh task to make sure we return the refreshed token.
if let refreshTask {
return try await refreshTask.value.accessToken
}

let task = Task { () throws -> TokenResponse in
defer { refreshTask = nil }
let authToken = try await createJWT(teamID: teamID, keyID: keyID, key: key)
let newToken = try await getAccessToken(authToken: authToken)
currentToken = newToken
return newToken
}
// If we don't have a current token, we request a new one.
let task = Task { () throws -> TokenResponse in
defer { refreshTask = nil }
let newToken = try await tokenResponse
currentToken = newToken
return newToken
}

self.refreshTask = task
return try await task.value
refreshTask = task
return try await task.value.accessToken
}
}
}

extension AuthorizationProvider {
/// Makes an HTTP request to exchange Auth token for Access token.
///
/// - Parameters:
/// - httpClient: The HTTP client to use.
/// - authToken: The authorization token.
///
/// - Throws: Error response object.
///
/// - Returns: An access token.
private func getAccessToken(authToken: String) async throws -> TokenResponse {
var headers = HTTPHeaders()
headers.add(name: "Authorization", value: "Bearer \(authToken)")

var request = HTTPClientRequest(url: "\(apiServer)/v1/token")
request.headers = headers

let response = try await httpClient.execute(request, timeout: .seconds(30))

if response.status == .ok {
return try await JSONDecoder().decode(TokenResponse.self, from: response.body.collect(upTo: 1024 * 1024))
} else {
throw try await JSONDecoder().decode(ErrorResponse.self, from: response.body.collect(upTo: 1024 * 1024))
private var tokenResponse: TokenResponse {
get async throws {
var headers = HTTPHeaders()
headers.add(name: "Authorization", value: "Bearer \(try await jwtToken)")

var request = HTTPClientRequest(url: "\(apiServer)/v1/token")
request.headers = headers

let response = try await httpClient.execute(request, timeout: .seconds(30))

if response.status == .ok {
return try await JSONDecoder().decode(TokenResponse.self, from: response.body.collect(upTo: 1024 * 1024))
} else {
throw try await JSONDecoder().decode(ErrorResponse.self, from: response.body.collect(upTo: 1024 * 1024))
}
}
}

/// Creates a JWT token, which is auth token in this context.
///
/// - Parameters:
/// - teamID: A 10-character Team ID obtained from your Apple Developer account.
/// - keyID: A 10-character key identifier that provides the ID of the private key that you obtain from your Apple Developer account.
/// - key: A MapKit JS private key.
///
/// - Returns: A JWT token represented as `String`.
private func createJWT(teamID: String, keyID: String, key: String) async throws -> String {
let keys = try await JWTKeyCollection().add(ecdsa: ES256PrivateKey(pem: key))

var header = JWTHeader()
header.alg = "ES256"
header.kid = keyID
header.typ = "JWT"

struct Payload: JWTPayload {
let iss: IssuerClaim
let iat: IssuedAtClaim
let exp: ExpirationClaim

func verify(using key: some JWTAlgorithm) throws {
try self.exp.verifyNotExpired()
private var jwtToken: String {
get async throws {
let keys = try await JWTKeyCollection().add(ecdsa: ES256PrivateKey(pem: key))

var header = JWTHeader()
header.alg = "ES256"
header.kid = keyID
header.typ = "JWT"

struct Payload: JWTPayload {
let iss: IssuerClaim
let iat: IssuedAtClaim
let exp: ExpirationClaim

func verify(using key: some JWTAlgorithm) throws {
try exp.verifyNotExpired()
}
}
}

let payload = Payload(
iss: IssuerClaim(value: teamID),
iat: IssuedAtClaim(value: Date()),
exp: ExpirationClaim(value: Date().addingTimeInterval(30 * 60))
)
let payload = Payload(
iss: IssuerClaim(value: teamID),
iat: IssuedAtClaim(value: Date()),
exp: ExpirationClaim(value: Date().addingTimeInterval(30 * 60))
)

return try await keys.sign(payload, header: header)
return try await keys.sign(payload, header: header)
}
}
}
2 changes: 1 addition & 1 deletion Sources/AppleMapsKit/DTOs/ErrorResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public struct ErrorResponse: Error, Codable, Sendable {

extension ErrorResponse: CustomStringConvertible {
public var description: String {
var result = #"AppleMapsKitError(message: \#(self.message ?? "nil")"#
var result = #"AppleMapsKitError(message: \#(message ?? "nil")"#

if let details {
result.append(", details: \(details)")
Expand Down
46 changes: 10 additions & 36 deletions Tests/AppleMapsKitTests/AppleMapsKitTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,7 @@ struct AppleMapsKitTests {
"Geocode",
arguments: zip(
[(37.78, -122.42), nil],
[
nil,
MapRegion(
northLatitude: 38, eastLongitude: -122.1, southLatitude: 37.5,
westLongitude: -122.5
),
]
[nil, MapRegion(northLatitude: 38, eastLongitude: -122.1, southLatitude: 37.5, westLongitude: -122.5)]
)
)
func geocode(searchLocation: (latitude: Double, longitude: Double)?, searchRegion: MapRegion?) async throws {
Expand Down Expand Up @@ -69,13 +63,7 @@ struct AppleMapsKitTests {
"Search",
arguments: zip(
[(37.78, -122.42), nil],
[
nil,
MapRegion(
northLatitude: 38, eastLongitude: -122.1, southLatitude: 37.5,
westLongitude: -122.5
),
]
[nil, MapRegion(northLatitude: 38, eastLongitude: -122.1, southLatitude: 37.5, westLongitude: -122.5)]
)
)
func search(searchLocation: (latitude: Double, longitude: Double)?, searchRegion: MapRegion?) async throws {
Expand Down Expand Up @@ -103,19 +91,19 @@ struct AppleMapsKitTests {
}

@Test("Search with invalid Result Type") func searchWithInvalidResultType() async throws {
do {
let _ = try await client.search(
await #expect {
try await client.search(
for: "eiffel tower",
resultTypeFilter: [.pointOfInterest, .physicalFeature, .poi, .address, .query]
)
Issue.record("This call should throw an error")
} catch let error as AppleMapsKitError {
#expect(error.errorType.base == .invalidSearchResultType)
} throws: { error in
guard let error = error as? AppleMapsKitError else { return false }
return error.errorType.base == .invalidSearchResultType
}
}

@Test("Search with Page Token") func searchWithPageToken() async throws {
try await withKnownIssue {
await withKnownIssue {
let searchResponse = try await client.search(
for: "eiffel tower",
resultTypeFilter: [.pointOfInterest, .physicalFeature, .poi, .address],
Expand All @@ -125,22 +113,14 @@ struct AppleMapsKitTests {
)
let results = try #require(searchResponse.results)
#expect(!results.isEmpty)
} when: {
credentialsAreInvalid
}
}

@Test(
"Search Auto Complete",
arguments: zip(
[(37.78, -122.42), nil],
[
nil,
MapRegion(
northLatitude: 38, eastLongitude: -122.1, southLatitude: 37.5,
westLongitude: -122.5
),
]
[nil, MapRegion(northLatitude: 38, eastLongitude: -122.1, southLatitude: 37.5, westLongitude: -122.5)]
)
)
func searchAutoComplete(searchLocation: (latitude: Double, longitude: Double)?, searchRegion: MapRegion?) async throws {
Expand Down Expand Up @@ -169,13 +149,7 @@ struct AppleMapsKitTests {
"Directions",
arguments: zip(
[(37.7857, -122.4011), nil],
[
nil,
MapRegion(
northLatitude: 38, eastLongitude: -122.1, southLatitude: 37.5,
westLongitude: -122.5
),
]
[nil, MapRegion(northLatitude: 38, eastLongitude: -122.1, southLatitude: 37.5, westLongitude: -122.5)]
)
)
func directions(searchLocation: (latitude: Double, longitude: Double)?, searchRegion: MapRegion?) async throws {
Expand Down

0 comments on commit f739d45

Please sign in to comment.