Skip to content

Commit 7140457

Browse files
authored
Merge pull request #52 from makinosp/develop
refact: change how URLs are passed to HTTP clients to relative paths
2 parents 0b349a7 + 520c7e5 commit 7140457

File tree

9 files changed

+60
-110
lines changed

9 files changed

+60
-110
lines changed

Sources/VRCKit/APIServices/AuthenticationService.swift

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,13 @@ extension VerifyType: UserOrRequires {}
1818
@available(macOS 12.0, *)
1919
@available(iOS 15.0, *)
2020
public struct AuthenticationService {
21-
private static let authUrl = "\(baseUrl)/auth"
22-
private static let auth2FAUrl = "\(authUrl)/twofactorauth"
21+
private static let authPath = "auth"
2322

2423
/// Check User Exists
2524
public static func isExists(_ client: APIClient, userId: String) async throws -> Bool {
26-
var request = URLComponents(string: "\(authUrl)/exists")!
27-
request.queryItems = [URLQueryItem(name: "username", value: userId.description)]
28-
guard let url = request.url else { return false }
29-
let response = try await client.request(
30-
url: url,
31-
httpMethod: .get
32-
)
25+
let path = "\(authPath)/exists"
26+
let queryItems = [URLQueryItem(name: "username", value: userId.description)]
27+
let response = try await client.request(path: path, httpMethod: .get, queryItems: queryItems)
3328
let result: ExistsResponse = try Util.shared.decode(response.data)
3429
return result.userExists
3530
}
@@ -38,11 +33,8 @@ public struct AuthenticationService {
3833
public static func loginUserInfo(
3934
_ client: APIClient
4035
) async throws -> UserOrRequires {
41-
let response = try await client.request(
42-
url: URL(string: "\(authUrl)/user")!,
43-
httpMethod: .get,
44-
basic: true
45-
)
36+
let path = "\(authPath)/user"
37+
let response = try await client.request(path: path, httpMethod: .get, basic: true)
4638
do {
4739
let user: User = try Util.shared.decode(response.data)
4840
return user
@@ -61,9 +53,10 @@ public struct AuthenticationService {
6153
verifyType: VerifyType,
6254
code: String
6355
) async throws -> Bool {
56+
let path = "\(authPath)/twofactorauth/\(verifyType.rawValue.lowercased())/verify"
6457
let requestData = try Util.shared.encode(VerifyRequest(code: code))
6558
let response = try await client.request(
66-
url: URL(string: "\(auth2FAUrl)/\(verifyType.rawValue.lowercased())/verify")!,
59+
path: path,
6760
httpMethod: .post,
6861
httpBody: requestData
6962
)
@@ -73,20 +66,14 @@ public struct AuthenticationService {
7366

7467
/// Verify Auth Token
7568
public static func verifyAuthToken(_ client: APIClient) async throws -> Bool {
76-
let response = try await client.request(
77-
url: URL(string: authUrl)!,
78-
httpMethod: .get
79-
)
69+
let response = try await client.request(path: authPath, httpMethod: .get)
8070
let result: VerifyAuthTokenResponse = try Util.shared.decode(response.data)
8171
return result.ok
8272
}
8373

8474
/// Logout
8575
public static func logout(_ client: APIClient) async throws {
86-
_ = try await client.request(
87-
url: URL(string: "\(baseUrl)/logout")!,
88-
httpMethod: .put
89-
)
76+
_ = try await client.request(path: "logout", httpMethod: .put)
9077
client.deleteCookies()
9178
}
9279
}

Sources/VRCKit/APIServices/FavoriteService.swift

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ public typealias FavoriteFriendDetail = (favoriteGroupId: String, friends: [User
1919
@available(macOS 12.0, *)
2020
@available(iOS 15.0, *)
2121
public struct FavoriteService {
22-
private static let favoriteUrl = "\(baseUrl)/favorites"
23-
private static let favoriteGroupUrl = "\(baseUrl)/favorite/groups"
22+
private static let path = "favorites"
2423

2524
/// Asynchronously retrieves a list of favorite groups from the server.
2625
/// - Parameter client: The API client used to make the network request.
@@ -29,10 +28,8 @@ public struct FavoriteService {
2928
public static func listFavoriteGroups(
3029
_ client: APIClient
3130
) async throws -> [FavoriteGroup] {
32-
let response = try await client.request(
33-
url: URL(string: favoriteGroupUrl)!,
34-
httpMethod: .get
35-
)
31+
let path = "favorite/groups"
32+
let response = try await client.request(path: path, httpMethod: .get)
3633
return try Util.shared.decode(response.data)
3734
}
3835

@@ -42,22 +39,14 @@ public struct FavoriteService {
4239
type: FavoriteType,
4340
tag: String? = nil
4441
) async throws -> [Favorite] {
45-
var request = URLComponents(string: favoriteUrl)!
46-
request.queryItems = [
42+
var queryItems = [
4743
URLQueryItem(name: "n", value: n.description),
4844
URLQueryItem(name: "type", value: type.rawValue)
4945
]
5046
if let tag = tag {
51-
request.queryItems?.append(URLQueryItem(name: "tag", value: tag.description))
52-
}
53-
guard let url = request.url else {
54-
throw URLError(.badURL, userInfo: [NSLocalizedDescriptionKey: "Invalid URL: \(favoriteUrl)"])
47+
queryItems.append(URLQueryItem(name: "tag", value: tag.description))
5548
}
56-
57-
let response = try await client.request(
58-
url: url,
59-
httpMethod: .get
60-
)
49+
let response = try await client.request(path: path, httpMethod: .get, queryItems: queryItems)
6150
return try Util.shared.decode(response.data)
6251
}
6352

@@ -121,22 +110,15 @@ public struct FavoriteService {
121110
let requestData = try Util.shared.encode(
122111
RequestToAddFavorite(type: type, favoriteId: favoriteId, tags: [tag])
123112
)
124-
let response = try await client.request(
125-
url: URL(string: "\(favoriteUrl)")!,
126-
httpMethod: .post,
127-
httpBody: requestData
128-
)
113+
let response = try await client.request(path: path, httpMethod: .post, httpBody: requestData)
129114
return try Util.shared.decode(response.data)
130115
}
131116

132117
public static func removeFavorite(
133118
_ client: APIClient,
134119
favoriteId: String
135120
) async throws -> SuccessResponse {
136-
let response = try await client.request(
137-
url: URL(string: "\(favoriteUrl)/\(favoriteId)")!,
138-
httpMethod: .delete
139-
)
121+
let response = try await client.request(path: path, httpMethod: .delete)
140122
return try Util.shared.decode(response.data)
141123
}
142124
}

Sources/VRCKit/APIServices/FriendService.swift

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import Foundation
1414
@available(macOS 12.0, *)
1515
@available(iOS 15.0, *)
1616
public struct FriendService {
17-
private static let url = "\(baseUrl)/auth/user/friends"
17+
private static let path = "auth/user/friends"
1818

1919
/// List information about friends.
2020
public static func fetchFriends(
@@ -23,19 +23,12 @@ public struct FriendService {
2323
n: Int = 60,
2424
offline: Bool = false
2525
) async throws -> [Friend] {
26-
var request = try Util.shared.urlComponents(url)
27-
request.queryItems = [
26+
let queryItems = [
2827
URLQueryItem(name: "offset", value: offset.description),
2928
URLQueryItem(name: "n", value: n.description),
3029
URLQueryItem(name: "offline", value: offline.description)
3130
]
32-
guard let url = request.url else {
33-
throw VRCKitError.invalidRequest("Invalid Request: \(request)")
34-
}
35-
let response = try await client.request(
36-
url: url,
37-
httpMethod: .get
38-
)
31+
let response = try await client.request(path: path, httpMethod: .get)
3932
return try Util.shared.decode(response.data)
4033
}
4134

@@ -82,11 +75,7 @@ public struct FriendService {
8275
}
8376

8477
public static func unfriend(_ client: APIClient, id: String) async throws {
85-
let url = URL(string: "\(url)/\(id)")!
86-
try await client.request(
87-
url: url,
88-
httpMethod: .delete
89-
)
78+
try await client.request(path: "\(path)/\(id)", httpMethod: .delete)
9079
}
9180

9281
public static func friendsGroupedByLocation(_ friends: [Friend]) -> [FriendsLocation] {

Sources/VRCKit/APIServices/InstanceService.swift

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,15 @@ import Foundation
1414
@available(macOS 12.0, *)
1515
@available(iOS 15.0, *)
1616
public struct InstanceService {
17-
static let instanceUrl = "\(baseUrl)/instances"
17+
static let path = "instances"
1818

1919
public static func fetchInstance(
2020
_ client: APIClient,
21-
worldID: String,
22-
instanceID: String
21+
worldId: String,
22+
instanceId: String
2323
) async throws -> Instance {
24-
let url = URL(string: "\(instanceUrl)/\(worldID):\(instanceID)")!
25-
2624
let response = try await client.request(
27-
url: url,
25+
path: "\(path)/\(worldId):\(instanceId)",
2826
httpMethod: .get
2927
)
3028
return try Util.shared.decode(response.data)
@@ -34,12 +32,7 @@ public struct InstanceService {
3432
_ client: APIClient,
3533
location: String
3634
) async throws -> Instance {
37-
let url = URL(string: "\(instanceUrl)/\(location)")!
38-
39-
let response = try await client.request(
40-
url: url,
41-
httpMethod: .get
42-
)
35+
let response = try await client.request(path: "\(path)/\(location)", httpMethod: .get)
4336
return try Util.shared.decode(response.data)
4437
}
4538
}

Sources/VRCKit/APIServices/UserNoteService.swift

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,17 @@ import Foundation
1010
@available(macOS 12.0, *)
1111
@available(iOS 15.0, *)
1212
public struct UserNoteService {
13-
static let url = "\(baseUrl)/userNotes"
13+
static let path = "userNotes"
1414

1515
/// Update user's note
1616
public static func updateUserNote(
1717
_ client: APIClient,
1818
targetUserId: String,
1919
note: String
2020
) async throws -> UserNoteResponse {
21-
let url = URL(string: url)!
2221
let userNoteRequest = UserNoteRequest(targetUserId: targetUserId, note: note)
2322
let requestData = try Util.shared.encode(userNoteRequest)
24-
let response = try await client.request(
25-
url: url,
26-
httpMethod: .post,
27-
httpBody: requestData
28-
)
23+
let response = try await client.request(path: path, httpMethod: .post, httpBody: requestData)
2924
return try Util.shared.decode(response.data)
3025
}
3126
}

Sources/VRCKit/APIServices/UserService.swift

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,14 @@ import Foundation
1414
@available(macOS 12.0, *)
1515
@available(iOS 15.0, *)
1616
public struct UserService {
17-
static let url = "\(baseUrl)/users"
17+
static let path = "users"
1818

1919
/// Fetch a user
2020
public static func fetchUser(
2121
_ client: APIClient,
2222
userId: String
2323
) async throws -> UserDetail {
24-
let url = URL(string: "\(url)/\(userId)")!
25-
let response = try await client.request(
26-
url: url,
27-
httpMethod: .get
28-
)
24+
let response = try await client.request(path: "\(path)/\(userId)", httpMethod: .get)
2925
return try Util.shared.decode(response.data)
3026
}
3127

@@ -64,11 +60,7 @@ public struct UserService {
6460
_ client: APIClient,
6561
id: String
6662
) async throws -> User {
67-
let url = URL(string: "\(url)/\(id)")!
68-
let response = try await client.request(
69-
url: url,
70-
httpMethod: .put
71-
)
63+
let response = try await client.request(path: "\(path)/\(id)", httpMethod: .put)
7264
return try Util.shared.decode(response.data)
7365
}
7466
}

Sources/VRCKit/APIServices/WorldService.swift

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,10 @@ import Foundation
1414
@available(macOS 12.0, *)
1515
@available(iOS 15.0, *)
1616
public struct WorldService {
17-
static let worldUrl = "\(baseUrl)/worlds"
17+
static let path = "worlds"
1818

19-
public static func fetchWorld(_ client: APIClient, worldID: String) async throws -> World {
20-
let url = URL(string: "\(worldUrl)/\(worldID)")!
21-
22-
let response = try await client.request(
23-
url: url,
24-
httpMethod: .get
25-
)
19+
public static func fetchWorld(_ client: APIClient, worldId: String) async throws -> World {
20+
let response = try await client.request(path: "\(path)/\(worldId)", httpMethod: .get)
2621
return try Util.shared.decode(response.data)
2722
}
2823
}

Sources/VRCKit/Client.swift

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import Foundation
66
// Created by makinosp on 2024/02/12.
77
//
88

9-
let baseUrl = "https://api.vrchat.cloud/api/1"
10-
119
public struct ResponseMessage: Codable {
1210
let message: String
1311
let statusCode: Int
@@ -28,9 +26,10 @@ public struct ErrorResponse: Codable, Error {
2826
public final class APIClient {
2927
typealias HTTPResponse = (data: Data, response: HTTPURLResponse)
3028

31-
private var username: String?
32-
private var password: String?
33-
private let domainUrl = URL(string: "https://api.vrchat.cloud")!
29+
private let username: String?
30+
private let password: String?
31+
private let domainUrl: String
32+
private let baseUrl: String
3433

3534
enum HttpMethod: String {
3635
case get, post, patch, put, delete
@@ -47,12 +46,15 @@ public final class APIClient {
4746
public init(username: String? = nil, password: String? = nil) {
4847
self.username = username
4948
self.password = password
49+
domainUrl = "https://api.vrchat.cloud"
50+
baseUrl = "\(domainUrl)/api/1"
5051
}
5152

5253
/// Retrieves the cookies stored for the VRChat API domain.
5354
/// - Returns: An array of `HTTPCookie` objects.
5455
public var cookies: [HTTPCookie] {
55-
guard let cookies = HTTPCookieStorage.shared.cookies(for: domainUrl) else { return [] }
56+
guard let url = URL(string: domainUrl),
57+
let cookies = HTTPCookieStorage.shared.cookies(for: url) else { return [] }
5658
return cookies
5759
}
5860

@@ -77,18 +79,28 @@ public final class APIClient {
7779

7880
/// Sends a request to the API.
7981
/// - Parameters:
80-
/// - url: The URL for the request.
82+
/// - path: The path for the request.
8183
/// - httpMethod: The HTTP method to use for the request.
8284
/// - basic: Whether to include basic authorization.
8385
/// - httpBody: The HTTP body to include in the request.
8486
/// - Returns: A tuple containing the data and the HTTP response.
8587
/// - Throws: `VRCKitError` if an error occurs during the request.
8688
func request(
87-
url: URL,
89+
path: String,
8890
httpMethod: HttpMethod,
8991
basic: Bool = false,
92+
queryItems: [URLQueryItem] = [],
9093
httpBody: Data? = nil
9194
) async throws -> HTTPResponse {
95+
guard var urlComponents = URLComponents(string: "\(baseUrl)/\(path)") else {
96+
throw VRCKitError.urlError
97+
}
98+
if !queryItems.isEmpty {
99+
urlComponents.queryItems = queryItems
100+
}
101+
guard let url = urlComponents.url else {
102+
throw VRCKitError.urlError
103+
}
92104
var request = URLRequest(url: url)
93105
request.httpMethod = httpMethod.description
94106

Sources/VRCKit/Errors.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ public enum VRCKitError: Error, LocalizedError {
2727
/// Represents an unexpected error.
2828
case unexpectedError
2929

30+
/// Represents an url error.
31+
case urlError
32+
3033
/// Provides a localized description of the error.
3134
public var errorDescription: String? {
3235
switch self {
@@ -40,6 +43,8 @@ public enum VRCKitError: Error, LocalizedError {
4043
"Invalid Request"
4144
case .unexpectedError:
4245
"Unexpected Error"
46+
case .urlError:
47+
"URL Error"
4348
}
4449
}
4550

0 commit comments

Comments
 (0)