diff --git a/Projects/Domain/AuthDomain/Sources/API/AuthEndpoint.swift b/Projects/Domain/AuthDomain/Sources/Endpoint/AuthEndpoint.swift similarity index 100% rename from Projects/Domain/AuthDomain/Sources/API/AuthEndpoint.swift rename to Projects/Domain/AuthDomain/Sources/Endpoint/AuthEndpoint.swift diff --git a/Projects/Domain/BaseDomain/Sources/Base/BaseRemoteDataSource.swift b/Projects/Domain/BaseDomain/Sources/Base/BaseRemoteDataSource.swift index f291c20a..c702f5d5 100644 --- a/Projects/Domain/BaseDomain/Sources/Base/BaseRemoteDataSource.swift +++ b/Projects/Domain/BaseDomain/Sources/Base/BaseRemoteDataSource.swift @@ -29,7 +29,7 @@ open class BaseRemoteDataSource { } public func request(_ endpoint: Endpoint, dto: T.Type) -> AnyPublisher { - requestPublisher(endpoint) + performRequest(endpoint) .map(\.data) .decode(type: dto, decoder: decoder) .mapError { $0 as Error } @@ -37,27 +37,19 @@ open class BaseRemoteDataSource { } public func request(_ endpoint: Endpoint) -> AnyPublisher { - requestPublisher(endpoint) + performRequest(endpoint) .map { _ in return } .eraseToAnyPublisher() } - - private func requestPublisher(_ endpoint: Endpoint) -> AnyPublisher { - return checkIsEndpointNeedsAuthorization(endpoint) ? - authorizedRequest(endpoint) : - defaultRequest(endpoint) - } } private extension BaseRemoteDataSource { - func defaultRequest(_ endpoint: Endpoint) -> AnyPublisher { + func performRequest(_ endpoint: Endpoint) -> AnyPublisher { client.requestPublisher(endpoint) .retry(maxRetryCount) - .timeout(45, scheduler: RunLoop.main) + .timeout(RunLoop.SchedulerTimeType.Stride(endpoint.timeout), scheduler: RunLoop.main) .mapError { - if case let .underlying(err) = $0, - let emdpointError = err as? EmdpointError, - case let .statusCode(response) = emdpointError, + if case let .statusCode(response) = $0, let httpResponse = response.response as? HTTPURLResponse { return endpoint.errorMapper?[httpResponse.statusCode] ?? $0 as Error } @@ -65,38 +57,4 @@ private extension BaseRemoteDataSource { } .eraseToAnyPublisher() } - - func authorizedRequest(_ endpoint: Endpoint) -> AnyPublisher { - if checkTokenIsExpired() { - return reissueToken() - .retry(maxRetryCount) - .flatMap { self.defaultRequest(endpoint) } - .mapError { $0 as Error } - .eraseToAnyPublisher() - } else { - return defaultRequest(endpoint) - .retry(maxRetryCount) - .eraseToAnyPublisher() - } - } - - func checkTokenIsExpired() -> Bool { - let expired = jwtStore.load(property: .accessExpiresAt) - .toDateWithCustomFormat("yyyy-MM-dd'T'HH:mm:ss") - return Date() > expired - } - - func checkIsEndpointNeedsAuthorization(_ endpoint: Endpoint) -> Bool { - endpoint.jwtTokenType == .accessToken - } - - func reissueToken() -> AnyPublisher { - let client = EmdpointClient( - interceptors: [JwtInterceptor(jwtStore: jwtStore)] - ) - return client.requestPublisher(.refresh) - .map { _ in } - .mapError { $0 as Error } - .eraseToAnyPublisher() - } } diff --git a/Projects/Domain/BaseDomain/Sources/DotoriEndpoint.swift b/Projects/Domain/BaseDomain/Sources/DotoriEndpoint.swift index 5da4c327..b414f85f 100644 --- a/Projects/Domain/BaseDomain/Sources/DotoriEndpoint.swift +++ b/Projects/Domain/BaseDomain/Sources/DotoriEndpoint.swift @@ -13,9 +13,7 @@ extension DotoriEndpoint { ) ?? URL(string: "https://www.google.com")! } - public var validationCode: ClosedRange { - 200...300 - } + public var validationCode: ClosedRange { 200...300 } public var headers: [String: String]? { switch self { @@ -24,6 +22,8 @@ extension DotoriEndpoint { return ["Content-Type": "application/json"] } } + + public var timeout: TimeInterval { 60 } } private class BundleFinder {} diff --git a/Projects/Domain/BaseDomain/Sources/API/RefreshAPI.swift b/Projects/Domain/BaseDomain/Sources/Endpoint/RefreshEndpoint.swift similarity index 100% rename from Projects/Domain/BaseDomain/Sources/API/RefreshAPI.swift rename to Projects/Domain/BaseDomain/Sources/Endpoint/RefreshEndpoint.swift diff --git a/Projects/Domain/BaseDomain/Sources/Interceptor/Jwt/JwtInterceptor.swift b/Projects/Domain/BaseDomain/Sources/Interceptor/Jwt/JwtInterceptor.swift index 892c9411..561f998f 100644 --- a/Projects/Domain/BaseDomain/Sources/Interceptor/Jwt/JwtInterceptor.swift +++ b/Projects/Domain/BaseDomain/Sources/Interceptor/Jwt/JwtInterceptor.swift @@ -1,3 +1,4 @@ +import DateUtility import Emdpoint import Foundation import JwtStoreInterface @@ -12,7 +13,7 @@ public struct JwtInterceptor: InterceptorType { public func prepare( _ request: URLRequest, endpoint: EndpointType, - completion: (Result) -> Void + completion: @escaping (Result) -> Void ) { guard let jwtTokenType = (endpoint as? JwtAuthorizable)?.jwtTokenType, jwtTokenType != .none @@ -22,8 +23,12 @@ public struct JwtInterceptor: InterceptorType { } var req = request let token = getToken(jwtTokenType: jwtTokenType) - req.addValue(jwtTokenType.rawValue, forHTTPHeaderField: token) - completion(.success(request)) + req.setValue(token, forHTTPHeaderField: jwtTokenType.rawValue) + if checkTokenIsExpired() { + reissueToken(req, jwtType: jwtTokenType, completion: completion) + } else { + completion(.success(request)) + } } public func didReceive( @@ -61,4 +66,36 @@ private extension JwtInterceptor { jwtStore.save(property: .refreshToken, value: tokenDTO.refreshToken) jwtStore.save(property: .accessExpiresAt, value: tokenDTO.expiresAt) } + + func checkTokenIsExpired() -> Bool { + let expired = jwtStore.load(property: .accessExpiresAt) + .toDateWithCustomFormat("yyyy-MM-dd'T'HH:mm:ss") + return Date() > expired + } + + func reissueToken( + _ request: URLRequest, + jwtType: JwtTokenType, + completion: @escaping (Result) -> Void + ) { + #if DEV || STAGE + let client = EmdpointClient(interceptors: [DotoriLoggingInterceptor()]) + #else + let client = EmdpointClient() + #endif + client.request(.refresh) { result in + switch result { + case let .success(response): + var request = request + if let tokenDTO = try? JSONDecoder().decode(JwtTokenDTO.self, from: response.data) { + saveToken(tokenDTO: tokenDTO) + request.setValue(getToken(jwtTokenType: jwtType), forHTTPHeaderField: jwtType.rawValue) + } + completion(.success(request)) + + case let .failure(error): + completion(.failure(error)) + } + } + } } diff --git a/Tuist/Dependencies.swift b/Tuist/Dependencies.swift index 18ba552c..8e3f90fb 100644 --- a/Tuist/Dependencies.swift +++ b/Tuist/Dependencies.swift @@ -6,7 +6,7 @@ let dependencies = Dependencies( swiftPackageManager: SwiftPackageManagerDependencies( [ .remote(url: "https://github.com/GSM-MSG/Moordinator.git", requirement: .exact("1.1.1")), - .remote(url: "https://github.com/GSM-MSG/Emdpoint.git", requirement: .exact("3.2.2")), + .remote(url: "https://github.com/GSM-MSG/Emdpoint.git", requirement: .exact("3.2.4")), .remote(url: "https://github.com/GSM-MSG/MSGLayout.git", requirement: .exact("1.1.0")), .remote(url: "https://github.com/Swinject/Swinject.git", requirement: .exact("2.8.3")),