Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Passage App class #5

Merged
merged 5 commits into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion Package.resolved
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
{
"originHash" : "65db2e61c18e81132241ea93ca8a61c58429030954e6c853d3720afa9aeab01c",
"originHash" : "090c675500195d294a5f4987351db53f4409bd98981b45c7a1f34a60685e39b7",
"pins" : [
{
"identity" : "anycodable",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Flight-School/AnyCodable",
"state" : {
"revision" : "69261f239f0fffaf51495dadc4f8483fbfe97025",
"version" : "0.6.1"
}
},
{
"identity" : "swiftlintplugins",
"kind" : "remoteSourceControl",
Expand Down
2 changes: 2 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ let package = Package(
],
dependencies: [
// Dependencies declare other packages that this package depends on.
.package(url: "https://github.com/Flight-School/AnyCodable", exact: "0.6.1"),
.package(url: "https://github.com/SimplyDanny/SwiftLintPlugins", exact: "0.55.1")
],
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: "Passage",
dependencies: ["AnyCodable"],
plugins: [.plugin(name: "SwiftLintBuildToolPlugin", package: "SwiftLintPlugins")]
),
.testTarget(
Expand Down
38 changes: 38 additions & 0 deletions Sources/Passage/errors/PassageAppError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Foundation

public enum PassageAppError: PassageError {

case appNotFound(message: String)
case invalidRequest(message: String)
case unspecified(message: String)

public var errorDescription: String {
switch self {
case .appNotFound(let message),
.invalidRequest(let message),
.unspecified(let message):
return message
}
}

public static func convert(error: Error) -> PassageAppError {
// Check if error is already proper
if let passageAppError = error as? PassageAppError {
return passageAppError
}
// Handle client error
if let errorResponse = error as? ErrorResponse,
let (_, errorData) = PassageErrorData.getData(from: errorResponse)
{
switch errorData.code {
case Model404Code.appNotFound.rawValue:
return .appNotFound(message: errorData.error)
case Model400Code.request.rawValue:
return .invalidRequest(message: errorData.error)
default: ()
}
}
return .unspecified(message: "unspecified error")
}

}
21 changes: 21 additions & 0 deletions Sources/Passage/errors/PassageError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Foundation

public protocol PassageError: Error, LocalizedError {
static func convert(error: Error) -> Self
var errorDescription: String { get }
}

struct PassageErrorData: Codable {
let code: String
let error: String

static func getData(from errorResponse: ErrorResponse) -> (Int, PassageErrorData)? {
guard
case let ErrorResponse.error(statusCode, data?, _, _) = errorResponse,
let errorData = try? JSONDecoder().decode(PassageErrorData.self, from: data)
else {
return nil
}
return (statusCode, errorData)
}
}
75 changes: 75 additions & 0 deletions Sources/Passage/interfaces/PassageApp.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import AnyCodable

/// A class representing the Passage application, allowing interaction with the application's API.
public class PassageApp {

private let appId: String

init(appId: String) {
self.appId = appId
}

/// Fetches information about the Passage app.
/// - Returns: A PassageAppInfo struct containing the app's details.
/// - Throws: `PassageAppError` if the API call fails.
public func info() async throws -> PassageAppInfo {
do {
let appInfoResponse = try await AppsAPI.getApp(appId: appId)
return appInfoResponse.app
} catch {
throw PassageAppError.convert(error: error)
}
}

/// Checks if a user exists for a given identifier.
///
/// - Parameter identifier: The identifier (e.g., email or phone number) to check for existence.
/// - Returns: A PublicUserInfo struct if the user exists, or `nil` if not.
/// - Throws: `PassageAppError` if the request is invalid or an error occurs during the API call.
public func userExists(identifier: String) async throws -> PublicUserInfo? {
do {
guard let safeId = identifier
.addingPercentEncoding(withAllowedCharacters: .alphanumerics)
else {
throw PassageAppError.invalidRequest(message: "invalid identifier")
}
let response = try await UsersAPI
.checkUserIdentifier(
appId: appId,
identifier: safeId
)
return response.user
} catch {
throw PassageAppError.convert(error: error)
}
}

/// Creates a new user with the specified identifier and optional metadata.
/// - Parameters:
/// - identifier: The identifier (e.g., email or phone number) for the new user.
/// - userMetadata: Optional metadata to associate with the user.
/// - Returns: A PublicUserInfo struct representing the newly created user.
/// - Throws: `PassageAppError` if an error occurs during user creation or the API call fails.
public func createUser(
identifier: String,
userMetadata: AnyCodable? = nil
) async throws -> PublicUserInfo {
do {
let params = CreateUserParams(
identifier: identifier,
userMetadata: userMetadata
)
let resposne = try await UsersAPI.createUser(
appId: appId,
createUserParams: params
)
guard let user = resposne.user else {
throw PassageAppError.invalidRequest(message: "invalid request")
}
return user
} catch {
throw PassageAppError.convert(error: error)
}
}

}
1 change: 1 addition & 0 deletions Sources/Passage/typealiases/PassageAppInfo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
public typealias PassageAppInfo = App
1 change: 1 addition & 0 deletions Sources/Passage/typealiases/PublicUserInfo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
public typealias PublicUserInfo = User