Skip to content

Commit

Permalink
Merge pull request #132 from Team-Ampersand/122-notice-detail-feature
Browse files Browse the repository at this point in the history
🔀 :: [#122] 공지 상세 Feature 구현
  • Loading branch information
baekteun authored Jul 30, 2023
2 parents ad83fe5 + cf1b396 commit 59d22fa
Show file tree
Hide file tree
Showing 48 changed files with 966 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public extension TargetDependency {
}

public extension TargetDependency.SPM {
static let Nuke = TargetDependency.external(name: "Nuke")
static let Anim = TargetDependency.external(name: "Anim")
static let CombineMiniature = TargetDependency.external(name: "CombineMiniature")
static let AsyncNeiSwift = TargetDependency.external(name: "AsyncNeiSwift")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public extension ModulePaths {
public extension ModulePaths {
enum Feature: String, MicroTargetPathConvertable {
case ProposeMusicFeature
case DetailNoticeFeature
case MyViolationListFeature
case SplashFeature
case ConfirmationDialogFeature
Expand Down
2 changes: 2 additions & 0 deletions Projects/App/Sources/Application/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import AuthDomain
import ConfirmationDialogFeature
import Database
import DetailNoticeFeature
import HomeFeature
import IQKeyboardManagerSwift
import JwtStore
Expand Down Expand Up @@ -48,6 +49,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
HomeAssembly(),
MyViolationListAssembly(),
NoticeAssembly(),
DetailNoticeAssembly(),
SelfStudyAssembly(),
ConfirmationDialogAssembly(),
MassageAssembly(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
public protocol RemoteNoticeDataSource {
func fetchNoticeList() async throws -> [NoticeEntity]
func fetchNotice(id: Int) async throws -> DetailNoticeEntity
func removeNotice(id: Int) async throws
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import BaseDomainInterface
import Foundation

public struct DetailNoticeEntity: Equatable {
public let id: Int
public let title: String
public let content: String
public let role: UserRoleType
public let images: [NoticeImage]
public let createdDate: Date
public let modifiedDate: Date?

public init(
id: Int,
title: String,
content: String,
role: UserRoleType,
images: [DetailNoticeEntity.NoticeImage],
createdDate: Date,
modifiedDate: Date? = nil
) {
self.id = id
self.title = title
self.content = content
self.role = role
self.images = images
self.createdDate = createdDate
self.modifiedDate = modifiedDate
}

public struct NoticeImage: Equatable {
public let id: Int
public let imageURL: String

public init(id: Int, imageURL: String) {
self.id = id
self.imageURL = imageURL
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
public typealias DetailNoticeModel = DetailNoticeEntity
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
public protocol NoticeRepository {
func fetchNoticeList() async throws -> [NoticeEntity]
func fetchNotice(id: Int) async throws -> DetailNoticeEntity
func removeNotice(id: Int) async throws
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
public protocol FetchNoticeUseCase {
func callAsFunction(id: Int) async throws -> DetailNoticeModel
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
public protocol RemoveNoticeUseCase {
func callAsFunction(id: Int) async throws
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,13 @@ public final class NoticeDomainAssembly: Assembly {
container.register(FetchNoticeListUseCase.self) { resolver in
FetchNoticeListUseCaseImpl(noticeRepository: resolver.resolve(NoticeRepository.self)!)
}

container.register(FetchNoticeUseCase.self) { resolver in
FetchNoticeUseCaseImpl(noticeRepository: resolver.resolve(NoticeRepository.self)!)
}

container.register(RemoveNoticeUseCase.self) { resolver in
RemoveNoticeUseCaseImpl(noticeRepository: resolver.resolve(NoticeRepository.self)!)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import BaseDomainInterface
import DateUtility
import Foundation
import NoticeDomainInterface

struct FetchNoticeResponseDTO: Decodable {
let id: Int
let title: String
let content: String
let role: UserRoleType
let boardImage: [BoardImage]
let createdDate: String
let modifiedDate: String?

struct BoardImage: Decodable {
let id: Int
let url: String

init(id: Int, url: String) {
self.id = id
self.url = url
}
}
}

extension FetchNoticeResponseDTO {
func toDomain() -> DetailNoticeEntity {
DetailNoticeEntity(
id: id,
title: title,
content: content,
role: role,
images: boardImage.map { $0.toDomain() },
createdDate: createdDate.toDateWithCustomFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"),
modifiedDate: modifiedDate?.toDateWithCustomFormat("yyyy-MM-dd'T'HH:mm:ss.SSS")
)
}
}

extension FetchNoticeResponseDTO.BoardImage {
func toDomain() -> DetailNoticeEntity.NoticeImage {
.init(id: id, imageURL: url)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import NetworkingInterface

enum NoticeEndpoint {
case fetchNoticeList
case fetchNotice(id: Int)
case removeNotice(id: Int)
}

extension NoticeEndpoint: DotoriEndpoint {
Expand All @@ -14,6 +16,12 @@ extension NoticeEndpoint: DotoriEndpoint {
switch self {
case .fetchNoticeList:
return .get("")

case let .fetchNotice(id):
return .get("/\(id)")

case let .removeNotice(id):
return .delete("/\(id)")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,16 @@ final class RemoteNoticeDataSourceImpl: RemoteNoticeDataSource {
)
.toDomain()
}

func fetchNotice(id: Int) async throws -> DetailNoticeEntity {
try await networking.request(
NoticeEndpoint.fetchNotice(id: id),
dto: FetchNoticeResponseDTO.self
)
.toDomain()
}

func removeNotice(id: Int) async throws {
try await networking.request(NoticeEndpoint.removeNotice(id: id))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,12 @@ final class NoticeRepositoryImpl: NoticeRepository {
func fetchNoticeList() async throws -> [NoticeEntity] {
try await remoteNoticeDataSource.fetchNoticeList()
}

func fetchNotice(id: Int) async throws -> DetailNoticeEntity {
try await remoteNoticeDataSource.fetchNotice(id: id)
}

func removeNotice(id: Int) async throws {
try await remoteNoticeDataSource.removeNotice(id: id)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import NoticeDomainInterface

struct FetchNoticeUseCaseImpl: FetchNoticeUseCase {
private let noticeRepository: any NoticeRepository

init(noticeRepository: any NoticeRepository) {
self.noticeRepository = noticeRepository
}

func callAsFunction(id: Int) async throws -> DetailNoticeModel {
try await noticeRepository.fetchNotice(id: id)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import NoticeDomainInterface

struct RemoveNoticeUseCaseImpl: RemoveNoticeUseCase {
private let noticeRepository: any NoticeRepository

init(noticeRepository: any NoticeRepository) {
self.noticeRepository = noticeRepository
}

func callAsFunction(id: Int) async throws {
try await noticeRepository.removeNotice(id: id)
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import BaseDomainInterface
import NoticeDomainInterface

final class RemoteNoticeDataSourceSpy: RemoteNoticeDataSource {
Expand All @@ -7,4 +8,27 @@ final class RemoteNoticeDataSourceSpy: RemoteNoticeDataSource {
fetchNoticeListCallCount += 1
return fetchNoticeListReturn
}

var fetchNoticeCallCount = 0
var fetchNoticeHandler: (Int) async throws -> DetailNoticeEntity = { _ in
.init(
id: 1,
title: "",
content: "",
role: .member,
images: [],
createdDate: .init()
)
}
func fetchNotice(id: Int) async throws -> DetailNoticeEntity {
fetchNoticeCallCount += 1
return try await fetchNoticeHandler(id)
}

var removeNoticeCallCount = 0
var removeNoticeHandler: (Int) async throws -> Void = { _ in }
func removeNotice(id: Int) async throws {
removeNoticeCallCount += 1
try await removeNoticeHandler(id)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,27 @@ final class NoticeRepositorySpy: NoticeRepository {
fetchNoticeListCallCount += 1
return fetchNoticeListReturn
}

var fetchNoticeCallCount = 0
var fetchNoticeHandler: (Int) async throws -> DetailNoticeEntity = { _ in
.init(
id: 1,
title: "",
content: "",
role: .member,
images: [],
createdDate: .init()
)
}
func fetchNotice(id: Int) async throws -> DetailNoticeEntity {
fetchNoticeCallCount += 1
return try await fetchNoticeHandler(id)
}

var removeNoticeCallCount = 0
var removeNoticeHandler: (Int) async throws -> Void = { _ in }
func removeNotice(id: Int) async throws {
removeNoticeCallCount += 1
try await removeNoticeHandler(id)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import NoticeDomainInterface

final class FetchNoticeUseCaseSpy: FetchNoticeUseCase {
var fetchNoticeCallCount = 0
var fetchNoticeHandler: (Int) async throws -> DetailNoticeModel = { _ in
.init(
id: 1,
title: "",
content: "",
role: .member,
images: [],
createdDate: .init()
)
}
func callAsFunction(id: Int) async throws -> DetailNoticeModel {
fetchNoticeCallCount += 1
return try await fetchNoticeHandler(id)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import NoticeDomainInterface

final class RemoveNoticeUseCaseSpy: RemoveNoticeUseCase {
var removeNoticeCallCount = 0
var removeNoticeHandler: (Int) async throws -> Void = { _ in }
func callAsFunction(id: Int) async throws {
removeNoticeCallCount += 1
try await removeNoticeHandler(id)
}
}
39 changes: 39 additions & 0 deletions Projects/Domain/NoticeDomain/Tests/FetchNoticeUseCaseTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import NoticeDomainInterface
import XCTest
@testable import NoticeDomain
@testable import NoticeDomainTesting

final class FetchNoticeUseCaseTests: XCTestCase {
var noticeRepository: NoticeRepositorySpy!
var sut: FetchNoticeUseCaseImpl!

override func setUp() {
noticeRepository = .init()
sut = .init(noticeRepository: noticeRepository)
}

override func tearDown() {
noticeRepository = nil
sut = nil
}

func testCallAsFunction() async throws {
let date = Date()
XCTAssertEqual(noticeRepository.fetchNoticeCallCount, 0)
let expected = DetailNoticeEntity(
id: 1,
title: "b",
content: "aj",
role: .councillor,
images: [],
createdDate: .init(),
modifiedDate: nil
)
noticeRepository.fetchNoticeHandler = { _ in expected }

let actual = try await sut(id: 1)

XCTAssertEqual(noticeRepository.fetchNoticeCallCount, 1)
XCTAssertEqual(actual, expected)
}
}
22 changes: 21 additions & 1 deletion Projects/Domain/NoticeDomain/Tests/NoticeRepositoryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,32 @@ final class NoticeRepositoryTests: XCTestCase {
func testFetchNoticeList() async throws {
let date = Date()
XCTAssertEqual(remoteNoticeDataSource.fetchNoticeListCallCount, 0)
let expected = [NoticeModel(id: 1, title: "title2", content: "contents", roles: .member, createdTime: date)]
let expected = [NoticeEntity(id: 1, title: "title2", content: "contents", roles: .member, createdTime: date)]
remoteNoticeDataSource.fetchNoticeListReturn = expected

let actual = try await sut.fetchNoticeList()

XCTAssertEqual(remoteNoticeDataSource.fetchNoticeListCallCount, 1)
XCTAssertEqual(actual, expected)
}

func testFetchNotice() async throws {
let date = Date()
XCTAssertEqual(remoteNoticeDataSource.fetchNoticeCallCount, 0)
let expected = DetailNoticeEntity(id: 1, title: "title2", content: "contents", role: .member, images: [], createdDate: date)
remoteNoticeDataSource.fetchNoticeHandler = { _ in expected }

let actual = try await sut.fetchNotice(id: 1)

XCTAssertEqual(remoteNoticeDataSource.fetchNoticeCallCount, 1)
XCTAssertEqual(actual, expected)
}

func testRemoveNotice() async throws {
XCTAssertEqual(remoteNoticeDataSource.removeNoticeCallCount, 0)

try await sut.removeNotice(id: 1)

XCTAssertEqual(remoteNoticeDataSource.removeNoticeCallCount, 1)
}
}
Loading

0 comments on commit 59d22fa

Please sign in to comment.