Skip to content

Commit

Permalink
Extend User api (#199)
Browse files Browse the repository at this point in the history
* Extend User api

- allow fetching user by its unique id

* add tests for new user API

---------

Co-authored-by: Mihai Arosoaie <[email protected]>
  • Loading branch information
marosoaie and Mihai Arosoaie authored Jan 7, 2025
1 parent 79b5ece commit 0d2ae3e
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 12 deletions.
45 changes: 40 additions & 5 deletions OctoKit/User.swift
Original file line number Diff line number Diff line change
Expand Up @@ -169,14 +169,45 @@ open class User: Codable {
// MARK: request

public extension Octokit {
/**
Fetches a user or organization
- parameter id: The id of the user or organization.
- parameter completion: Callback for the outcome of the fetch.
*/
@discardableResult
func user(id: Int, completion: @escaping (_ response: Result<User, Error>) -> Void) -> URLSessionDataTaskProtocol? {
let router = UserRouter.readUserById(id, configuration)
return router.load(session, dateDecodingStrategy: .formatted(Time.rfc3339DateFormatter), expectedResultType: User.self) { user, error in
if let error = error {
completion(.failure(error))
} else {
if let user = user {
completion(.success(user))
}
}
}
}

#if compiler(>=5.5.2) && canImport(_Concurrency)
/**
Fetches a user or organization
- parameter id: The identifier of the user or organization.
*/
@available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)
func user(id: Int) async throws -> User {
let router = UserRouter.readUserById(id, configuration)
return try await router.load(session, dateDecodingStrategy: .formatted(Time.rfc3339DateFormatter), expectedResultType: User.self)
}
#endif

/**
Fetches a user or organization
- parameter name: The name of the user or organization.
- parameter completion: Callback for the outcome of the fetch.
*/
@discardableResult
func user(name: String, completion: @escaping (_ response: Result<User, Error>) -> Void) -> URLSessionDataTaskProtocol? {
let router = UserRouter.readUser(name, configuration)
let router = UserRouter.readUserByName(name, configuration)
return router.load(session, dateDecodingStrategy: .formatted(Time.rfc3339DateFormatter), expectedResultType: User.self) { user, error in
if let error = error {
completion(.failure(error))
Expand All @@ -195,7 +226,7 @@ public extension Octokit {
*/
@available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)
func user(name: String) async throws -> User {
let router = UserRouter.readUser(name, configuration)
let router = UserRouter.readUserByName(name, configuration)
return try await router.load(session, dateDecodingStrategy: .formatted(Time.rfc3339DateFormatter), expectedResultType: User.self)
}
#endif
Expand Down Expand Up @@ -234,12 +265,14 @@ public extension Octokit {

enum UserRouter: Router {
case readAuthenticatedUser(Configuration)
case readUser(String, Configuration)
case readUserById(Int, Configuration)
case readUserByName(String, Configuration)

var configuration: Configuration {
switch self {
case let .readAuthenticatedUser(config): return config
case let .readUser(_, config): return config
case let .readUserById(_, config): return config
case let .readUserByName(_, config): return config
}
}

Expand All @@ -255,7 +288,9 @@ enum UserRouter: Router {
switch self {
case .readAuthenticatedUser:
return "user"
case let .readUser(username, _):
case let .readUserById(id, _):
return "user/\(id)"
case let .readUserByName(username, _):
return "users/\(username)"
}
}
Expand Down
58 changes: 51 additions & 7 deletions Tests/OctoKitTests/UserTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import XCTest
class UserTests: XCTestCase {
// MARK: Actual Request tests

func testGetUser() {
func testGetUserByName() {
let session = OctoKitURLTestSession(expectedURL: "https://api.github.com/users/mietzmithut", expectedHTTPMethod: "GET", jsonFile: "user_mietzmithut", statusCode: 200)
let username = "mietzmithut"
let task = Octokit(session: session).user(name: username) { response in
Expand All @@ -20,7 +20,7 @@ class UserTests: XCTestCase {
XCTAssertTrue(session.wasCalled)
}

func testFailingToGetUser() {
func testFailingToGetUserByName() {
let username = "notexisting"
let session = OctoKitURLTestSession(expectedURL: "https://api.github.com/users/notexisting", expectedHTTPMethod: "GET", jsonFile: nil, statusCode: 404)
let task = Octokit(session: session).user(name: username) { response in
Expand All @@ -38,11 +38,55 @@ class UserTests: XCTestCase {

#if compiler(>=5.5.2) && canImport(_Concurrency)
@available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)
func testGetUserAsync() async throws {
let session = OctoKitURLTestSession(expectedURL: "https://api.github.com/users/mietzmithut", expectedHTTPMethod: "GET", jsonFile: "user_mietzmithut", statusCode: 200)
let username = "mietzmithut"
let user = try await Octokit(session: session).user(name: username)
XCTAssertEqual(user.login, username)
func testGetUserByNameAsync() async throws {
let expectedUserId = 4672699
let session = OctoKitURLTestSession(expectedURL: "https://api.github.com/user/\(expectedUserId)", expectedHTTPMethod: "GET", jsonFile: "user_mietzmithut", statusCode: 200)
let user = try await Octokit(session: session).user(id: expectedUserId)
XCTAssertEqual(user.id, expectedUserId)
XCTAssertNotNil(user.createdAt)
XCTAssertTrue(session.wasCalled)
}
#endif

func testGetUserById() {
let expectedUserId = 4672699
let session = OctoKitURLTestSession(expectedURL: "https://api.github.com/user/\(expectedUserId)", expectedHTTPMethod: "GET", jsonFile: "user_mietzmithut", statusCode: 200)
let task = Octokit(session: session).user(id: expectedUserId) { response in
switch response {
case let .success(user):
XCTAssertEqual(user.id, expectedUserId)
XCTAssertNotNil(user.createdAt)
case .failure:
XCTFail("should get a user")
}
}
XCTAssertNotNil(task)
XCTAssertTrue(session.wasCalled)
}

func testFailingToGetUserById() {
let userId = 123456
let session = OctoKitURLTestSession(expectedURL: "https://api.github.com/user/\(userId)", expectedHTTPMethod: "GET", jsonFile: nil, statusCode: 404)
let task = Octokit(session: session).user(id: userId) { response in
switch response {
case .success:
XCTAssert(false, "should not retrieve user")
case let .failure(error as NSError):
XCTAssertEqual(error.code, 404)
XCTAssertEqual(error.domain, OctoKitErrorDomain)
}
}
XCTAssertNotNil(task)
XCTAssertTrue(session.wasCalled)
}

#if compiler(>=5.5.2) && canImport(_Concurrency)
@available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)
func testGetUserByIdAsync() async throws {
let expectedUserId = 4672699
let session = OctoKitURLTestSession(expectedURL: "https://api.github.com/user/\(expectedUserId)", expectedHTTPMethod: "GET", jsonFile: "user_mietzmithut", statusCode: 200)
let user = try await Octokit(session: session).user(id: expectedUserId)
XCTAssertEqual(user.id, expectedUserId)
XCTAssertNotNil(user.createdAt)
XCTAssertTrue(session.wasCalled)
}
Expand Down

0 comments on commit 0d2ae3e

Please sign in to comment.