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

SPT-1998 Мелкий рефакторинг + моки на билдер (1) #133

Merged
merged 7 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
18 changes: 18 additions & 0 deletions Example/Modules/MockServer/MockServer/FakeChainBuilder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// FakeChainBuilder.swift
//
//
// Created by Andrei Frolov on 02.05.24.
//

import NodeKit
import NodeKitMock

public final class FakeChainBuilder<Route: URLRouteProvider>: URLChainBuilder<Route> {

public init() {
super.init(
serviceChainProvider: URLServiceChainProvider(session: NetworkMock().urlSession)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import Foundation
import Models
import NodeKit
import NodeKitMock
import MockServer

public protocol AuthServiceProtocol {
func auth(by email: String, and passwod: String) async -> NodeResult<Void>
Expand All @@ -18,8 +18,7 @@ public struct AuthService: AuthServiceProtocol {
public init() { }

public func auth(by email: String, and passwod: String) async -> NodeResult<Void> {
return await UrlChainsBuilder<AuthURLProvider>()
.set(session: NetworkMock().urlSession)
return await FakeChainBuilder<AuthURLProvider>()
.encode(as: .urlQuery)
.route(.post, .login)
.build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
import Foundation
import NodeKit

enum AuthURLProvider: UrlRouteProvider {
private static var base: UrlRouteProvider = NavigationURLProvider.auth
enum AuthURLProvider: URLRouteProvider {
private static var base: URLRouteProvider = NavigationURLProvider.auth

case login
case logout
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

import Foundation
import NodeKit
import NodeKitMock
import Models
import MockServer

public protocol GroupServiceProtocol {
func header() async -> NodeResult<GroupHeaderResponseEntity>
Expand All @@ -35,8 +35,7 @@ public final class GroupService: GroupServiceProtocol {
private func result<T: DTODecodable>(
from route: GroupURLProvider
) async -> NodeResult<T> where T.DTO.Raw == Json {
return await UrlChainsBuilder<GroupURLProvider>()
.set(session: NetworkMock().urlSession)
return await FakeChainBuilder<GroupURLProvider>()
.route(.get, route)
.build()
.process()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
import Foundation
import NodeKit

enum GroupURLProvider: UrlRouteProvider {
private static var base: UrlRouteProvider = NavigationURLProvider.group
enum GroupURLProvider: URLRouteProvider {
private static var base: URLRouteProvider = NavigationURLProvider.group

case header
case body
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,15 @@

import Models
import NodeKit
import NodeKitMock
import MockServer

public struct PaginationContentDataProvider: AsyncPagerDataProvider {
public typealias Value = [PaginationResponseEntity]

public init() { }

public func provide(for index: Int, with pageSize: Int) async -> NodeResult<AsyncPagerData<[PaginationResponseEntity]>> {
return await UrlChainsBuilder<PaginationURLProvider>()
.set(session: NetworkMock().urlSession)
return await FakeChainBuilder<PaginationURLProvider>()
.encode(as: .urlQuery)
.route(.get, .list)
.build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
import Foundation
import NodeKit

enum PaginationURLProvider: UrlRouteProvider {
private static var base: UrlRouteProvider = NavigationURLProvider.pagination
enum PaginationURLProvider: URLRouteProvider {
private static var base: URLRouteProvider = NavigationURLProvider.pagination

case list

Expand Down
6 changes: 3 additions & 3 deletions Example/Modules/Services/Services/URLProviders.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@
import Foundation
import NodeKit

enum RootURLProvider: UrlRouteProvider {
enum RootURLProvider: URLRouteProvider {
case root

func url() throws -> URL {
return URL(string: "http://www.mockurl.com")!
}
}

enum NavigationURLProvider: UrlRouteProvider {
private static var base: UrlRouteProvider = RootURLProvider.root
enum NavigationURLProvider: URLRouteProvider {
private static var base: URLRouteProvider = RootURLProvider.root

case auth
case pagination
Expand Down
364 changes: 200 additions & 164 deletions NodeKit/NodeKit.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import Foundation

/// Этот узел читает eTag-токен из хранилища и добавляет его к запросу.
open class UrlETagReaderNode: AsyncNode {
open class URLETagReaderNode: AsyncNode {

// Следующий узел для обработки.
public var next: any TransportLayerNode
Expand All @@ -32,7 +32,7 @@ open class UrlETagReaderNode: AsyncNode {
/// Пытается прочесть eTag-токен из хранилища и добавить его к запросу.
/// В случае, если прочесть токен не удалось, то управление просто передается дальше.
open func process(
_ data: TransportUrlRequest,
_ data: TransportURLRequest,
logContext: LoggingContextProtocol
) async -> NodeResult<Json> {
guard
Expand All @@ -45,11 +45,11 @@ open class UrlETagReaderNode: AsyncNode {
var headers = data.headers
headers[self.etagHeaderKey] = tag

let params = TransportUrlParameters(method: data.method,
let params = TransportURLParameters(method: data.method,
url: data.url,
headers: headers)

let newData = TransportUrlRequest(with: params, raw: data.raw)
let newData = TransportURLRequest(with: params, raw: data.raw)

return await next.process(newData, logContext: logContext)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@

import Foundation

public

// MARK: - UserDefaults eTag storage

/// Содержит указатель на UserDefaults-хранилище для eTag токенов.
Expand All @@ -20,7 +18,7 @@ extension UserDefaults {

/// Этот узел сохраняет пришедшие eTag-токены.
/// В качестве ключа используется абсолютный URL до endpoint-a.
open class UrlETagSaverNode: AsyncNode {
open class URLETagSaverNode: AsyncNode {

/// Следующий узел для обработки.
public var next: (any ResponsePostprocessorLayerNode)?
Expand All @@ -42,7 +40,7 @@ open class UrlETagSaverNode: AsyncNode {
/// Пытается получить eTag-токен по ключу.
/// В любом случае передает управление дальше.
open func process(
_ data: UrlProcessedResponse,
_ data: URLProcessedResponse,
logContext: LoggingContextProtocol
) async -> NodeResult<Void> {
guard let tag = data.response.allHeaderFields[self.eTagHeaderKey] as? String,
Expand Down Expand Up @@ -70,7 +68,7 @@ public extension URL {
/// **ВАЖНО**
///
/// Полученная строка может быть невалидным URL - т.к. задача этого метода - получить уникальный идентификатор из URL
/// Причем так, чтобы порядок перечисления query парамтеров был не важен.
/// Причем так, чтобы порядок перечисления query парамтеров был не важен.
func withOrderedQuery() -> String? {
guard var comp = URLComponents(string: self.absoluteString) else {
return nil
Expand Down
14 changes: 7 additions & 7 deletions NodeKit/NodeKit/CacheNode/FirstCachePolicyNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,22 @@ import Foundation
///
/// - SeeAlso: `FirstCachePolicyNode`
///
/// - cantGetUrlRequest: Возникает в случае, если запрос отправленный в сеть не содержит `UrlRequest`
/// - cantGetURLRequest: Возникает в случае, если запрос отправленный в сеть не содержит `URLRequest`
public enum BaseFirstCachePolicyNodeError: Error {
case cantGetUrlRequest
case cantGetURLRequest
}

/// Этот узел реализует политику кэширования
/// "Сначала читаем из кэша, а затем запрашиваем у сервера"
/// - Important: В ообщем случае слушатель может быть оповещен дважды. Первый раз, когда ответ прочитан из кэша, а второй раз, когда он был получен с сервера.
/// - Important: В общем случае слушатель может быть оповещен дважды. Первый раз, когда ответ прочитан из кэша, а второй раз, когда он был получен с сервера.
open class FirstCachePolicyNode: AsyncStreamNode {
// MARK: - Nested

/// Тип для читающего из URL кэша узла
public typealias CacheReaderNode = AsyncNode<UrlNetworkRequest, Json>
public typealias CacheReaderNode = AsyncNode<URLNetworkRequest, Json>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

С новымMergedAsyncStreamNode эту ноду можно упразднить и встраивать кэширование как mergedNode где первый это CacheReader, а второй это NextProcessor.


/// Тип для следующего узла
public typealias NextProcessorNode = AsyncNode<RawUrlRequest, Json>
public typealias NextProcessorNode = AsyncNode<RawURLRequest, Json>

// MARK: - Properties

Expand Down Expand Up @@ -56,12 +56,12 @@ open class FirstCachePolicyNode: AsyncStreamNode {
/// В случае, если получить `URLRequest` не удалось,
/// то управление просто передается следующему узлу
public func process(
_ data: RawUrlRequest,
_ data: RawURLRequest,
logContext: LoggingContextProtocol
) -> AsyncStream<NodeResult<Json>> {
return AsyncStream { continuation in
Task {
if let request = data.toUrlRequest() {
if let request = data.toURLRequest() {
let cacheResult = await cacheReaderNode.process(request, logContext: logContext)
continuation.yield(cacheResult)
}
Expand Down
8 changes: 4 additions & 4 deletions NodeKit/NodeKit/CacheNode/IfServerFailsFromCacheNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ open class IfConnectionFailedFromCacheNode: AsyncNode {
/// Следующий узел для обработки.
public var next: any AsyncNode<URLRequest, Json>
/// Узел, считывающий данные из URL кэша.
public var cacheReaderNode: any AsyncNode<UrlNetworkRequest, Json>
public var cacheReaderNode: any AsyncNode<URLNetworkRequest, Json>

/// Инициаллизирует узел.
///
/// - Parameters:
/// - next: Следующий узел для обработки.
/// - cacheReaderNode: Узел, считывающий данные из URL кэша.
public init(next: any AsyncNode<URLRequest, Json>, cacheReaderNode: any AsyncNode<UrlNetworkRequest, Json>) {
public init(next: any AsyncNode<URLRequest, Json>, cacheReaderNode: any AsyncNode<URLNetworkRequest, Json>) {
self.next = next
self.cacheReaderNode = cacheReaderNode
}
Expand All @@ -36,7 +36,7 @@ open class IfConnectionFailedFromCacheNode: AsyncNode {
) async -> NodeResult<Json> {
return await next.process(data, logContext: logContext)
.asyncFlatMapError { error in
let request = UrlNetworkRequest(urlRequest: data)
let request = URLNetworkRequest(urlRequest: data)
if error is BaseTechnicalError {
await logContext.add(makeBaseTechinalLog(with: error))
return await cacheReaderNode.process(request, logContext: logContext)
Expand All @@ -57,7 +57,7 @@ open class IfConnectionFailedFromCacheNode: AsyncNode {
)
}

private func makeLog(with error: Error, from request: UrlNetworkRequest) -> Log {
private func makeLog(with error: Error, from request: URLNetworkRequest) -> Log {
return Log(
logViewObjectName +
"Catching \(error)" + .lineTabDeilimeter +
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// UrlCacheReaderNode.swift
// URLCacheReaderNode.swift
// CoreNetKitWithExample
//
// Created by Александр Кравченков on 28/11/2018.
Expand All @@ -8,50 +8,54 @@

import Foundation

/// Ошибки для узла `UrlCacheReaderNode`
/// Ошибки для узла `URLCacheReaderNode`
///
/// - cantLoadDataFromCache: Случается, если запрос в кэш завершился с ошибкой
/// - cantSerializeJson: Случается, если запрос в кэш завершился успешно, но не удалось сериализовать ответ в JSON
/// - cantCastToJson: Случается, если сериализовать ответ удалось, но каст к `Json` или к `[Json]` завершился с ошибкой
public enum BaseUrlCacheReaderError: Error {
public enum BaseURLCacheReaderError: Error {
case cantLoadDataFromCache
case cantSerializeJson
case cantCastToJson
}

/// Этот узел отвечает за чтение данных из URL кэша.
/// Сам по себе узел является листом и не может быть встроен в сквозную цепочку.
open class UrlCacheReaderNode: AsyncNode {
open class URLCacheReaderNode: AsyncNode {

public init() { }
public var needsToThrowError: Bool

public init(needsToThrowError: Bool) {
self.needsToThrowError = needsToThrowError
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

а зачем оно добавилось, если никак не используется?

}

/// Посылает запрос в кэш и пытается сериализовать данные в JSON.
open func process(
_ data: UrlNetworkRequest,
_ data: URLNetworkRequest,
logContext: LoggingContextProtocol
) async -> NodeResult<Json> {
guard let cachedResponse = extractCachedUrlResponse(data.urlRequest) else {
return .failure(BaseUrlCacheReaderError.cantLoadDataFromCache)
guard let cachedResponse = extractCachedURLResponse(data.urlRequest) else {
return .failure(BaseURLCacheReaderError.cantLoadDataFromCache)
}

guard let jsonObjsect = try? JSONSerialization.jsonObject(
with: cachedResponse.data,
options: .allowFragments
) else {
return .failure(BaseUrlCacheReaderError.cantSerializeJson)
return .failure(BaseURLCacheReaderError.cantSerializeJson)
}

guard let json = jsonObjsect as? Json else {
guard let json = jsonObjsect as? [Json] else {
return .failure(BaseUrlCacheReaderError.cantCastToJson)
return .failure(BaseURLCacheReaderError.cantCastToJson)
}
return .success([MappingUtils.arrayJsonKey: json])
}

return .success(json)
}

private func extractCachedUrlResponse(_ request: URLRequest) -> CachedURLResponse? {
private func extractCachedURLResponse(_ request: URLRequest) -> CachedURLResponse? {
if let response = URLCache.shared.cachedResponse(for: request) {
return response
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// UrlCacheWriterNode.swift
// URLCacheWriterNode.swift
// CoreNetKitWithExample
//
// Created by Александр Кравченков on 28/11/2018.
Expand All @@ -12,12 +12,12 @@ import Foundation
/// - Important: это "глупая" реализация,
/// в которой не учитываются server-side политики и прочее.
/// Подразумечается, что этот узел не входит в цепочку, а является листом одного из узлов.
open class UrlCacheWriterNode: AsyncNode {
open class URLCacheWriterNode: AsyncNode {

/// Формирует `CachedURLResponse` с политикой `.allowed`, сохраняет его в кэш,
/// а затем возвращает сообщение об успешной операции.
open func process(
_ data: UrlProcessedResponse,
_ data: URLProcessedResponse,
logContext: LoggingContextProtocol
) async -> NodeResult<Void> {
let cached = CachedURLResponse(
Expand Down
Loading
Loading