Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,9 @@ public struct DataSourceDependencyAssembler: DependencyAssemblerProtocol {
DIContainer.shared.register(type: ReportRepositoryProtocol.self) { _ in
return ReportRepository()
}

DIContainer.shared.register(type: FileRepositoryProtocol.self) { _ in
return FileRepository()
}
}
}
9 changes: 9 additions & 0 deletions Projects/DataSource/Sources/Common/Enum/Endpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
// Created by 최정인 on 6/21/25.
//

import Foundation

public protocol Endpoint {
var baseURL: String { get }
var path: String { get }
Expand All @@ -13,4 +15,11 @@ public protocol Endpoint {
var queryParameters: [String: String] { get }
var bodyParameters: [String: Any] { get }
var isAuthorized: Bool { get }
var bodyType: EndpointBodyType { get }
var bodyData: Data? { get }
}

extension Endpoint {
var bodyType: EndpointBodyType { return .json }
var bodyData: Data? { return nil }
}
11 changes: 11 additions & 0 deletions Projects/DataSource/Sources/Common/Enum/EndpointBodyType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//
// EndpointBodyType.swift
// DataSource
//
// Created by 이동현 on 11/22/25.
//

public enum EndpointBodyType {
case json
case rawData
}
11 changes: 11 additions & 0 deletions Projects/DataSource/Sources/DTO/FilePresignedConditionDTO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//
// FilePresignedConditionDTO.swift
// DataSource
//
// Created by 이동현 on 11/22/25.
//

struct FilePresignedConditionDTO: Codable {
let prefix: String?
let fileName: String
}
18 changes: 18 additions & 0 deletions Projects/DataSource/Sources/DTO/FilePresignedDTO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// FilePresignedDTO.swift
// DataSource
//
// Created by 이동현 on 11/22/25.
//

struct FilePresignedDTO: Decodable {
let file1: String?
let file2: String?
let file3: String?

enum CodingKeys: String, CodingKey {
case file1 = "additionalProp1"
case file2 = "additionalProp2"
case file3 = "additionalProp3"
}
}
11 changes: 6 additions & 5 deletions Projects/DataSource/Sources/DTO/ReportDTO.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,32 @@

import Domain

struct ReportDTO: Decodable {
struct ReportDTO: Codable {
let reportId: Int?
let reportDate: String?
let reportTitle: String
let reportContent: String?
let reportLocation: String
let reportStatus: String
let reportStatus: String?
let reportCategory: String
let reportImageUrl: String?
let reportImageUrls: [String]?
let latitude: Double?
let longitude: Double?

func toReportEntity() throws -> ReportEntity {
guard let reportId else { throw NetworkError.decodingError }
return ReportEntity(
id: reportId,
title: reportTitle,
date: reportDate,
type: ReportType(rawValue: reportCategory) ?? .transportation,
progress: ReportProgress(rawValue: reportStatus) ?? .received,
progress: ReportProgress(rawValue: reportStatus ?? "") ?? .received,
content: reportContent,
location: LocationEntity(
longitude: longitude,
latitude: latitude,
address: reportLocation),
thumbnailURL: reportImageUrl,
photoUrls: reportImageUrls ?? [])
}

Expand All @@ -43,12 +43,13 @@ struct ReportDTO: Decodable {
title: reportTitle,
date: date,
type: ReportType(rawValue: reportCategory) ?? .transportation,
progress: ReportProgress(rawValue: reportStatus) ?? .received,
progress: ReportProgress(rawValue: reportStatus ?? "") ?? .received,
content: reportContent,
location: LocationEntity(
longitude: longitude,
latitude: latitude,
address: reportLocation),
thumbnailURL: reportImageUrl,
photoUrls: reportImageUrls ?? [])
}
}
68 changes: 68 additions & 0 deletions Projects/DataSource/Sources/Endpoint/FilePresignedEndpoint.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// FilePresignedEndpoint.swift
// DataSource
//
// Created by 이동현 on 11/22/25.
//

import Foundation

enum FilePresignedEndpoint {
case fetchPresignedURL(presignedConditions: [FilePresignedConditionDTO])
}

extension FilePresignedEndpoint: Endpoint {
var baseURL: String {
switch self {
case .fetchPresignedURL:
return AppProperties.baseURL + "/api/v2/files"
}
}

var path: String {
switch self {
case .fetchPresignedURL:
return baseURL + "/presigned-urls"
}
}

var method: HTTPMethod {
switch self {
case .fetchPresignedURL: .post
}
}
Comment on lines +29 to +33
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

method 프로퍼티에서 return 누락으로 컴파일 에러가 발생합니다

switch 내부에서 .post를 리턴하지 않아 현재 코드는 빌드되지 않습니다. 아래처럼 return을 명시해야 합니다.

    var method: HTTPMethod {
        switch self {
-        case .fetchPresignedURL: .post
+        case .fetchPresignedURL:
+            return .post
        }
    }

추가로, 이 케이스가 하나뿐이라면 switch 대신 단순히 return .post로 구현해도 충분합니다.

🤖 Prompt for AI Agents
In Projects/DataSource/Sources/Endpoint/FilePresignedEndpoint.swift around lines
29 to 33, the computed property `method` currently uses a `switch` but doesn't
return the `.post` value, causing a compile error; fix it by either adding an
explicit `return .post` inside the `case .fetchPresignedURL:` branch or simplify
the implementation to directly `return .post` (preferred if this enum has only
that single case), ensuring the property always returns an HTTPMethod.


var headers: [String : String] {
let headers: [String: String] = [
"Content-Type": "application/json",
"accept": "*/*"
]
return headers
}

var queryParameters: [String : String] {
return [:]
}

var bodyParameters: [String : Any] {
switch self {
case .fetchPresignedURL(let presignedConditions):
return [:]
}
}

var isAuthorized: Bool {
return true
}

var bodyType: EndpointBodyType {
return .rawData
}

var bodyData: Data? {
switch self {
case .fetchPresignedURL(let presignedConditions):
return try? JSONEncoder().encode(presignedConditions)
}
}
}
19 changes: 12 additions & 7 deletions Projects/DataSource/Sources/Endpoint/ReportEndpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,19 @@
//

enum ReportEndpoint {
case register(report: ReportDTO)
case fetchReports
case fetchReportDetail(reportId: Int)
}

extension ReportEndpoint: Endpoint {
var baseURL: String {
switch self {
case .fetchReports, .fetchReportDetail:
return AppProperties.baseURL + "/api/v2/reports"
}
return AppProperties.baseURL + "/api/v2/reports"
}

var path: String {
switch self {
case .fetchReports:
case .register, .fetchReports:
return baseURL
case .fetchReportDetail(let reportId):
return "\(baseURL)/\(reportId)"
Expand All @@ -29,8 +27,10 @@ extension ReportEndpoint: Endpoint {

var method: HTTPMethod {
switch self {
case.register:
return .post
case .fetchReports, .fetchReportDetail:
.get
return .get
}
}

Expand All @@ -47,7 +47,12 @@ extension ReportEndpoint: Endpoint {
}

var bodyParameters: [String : Any] {
return [:]
switch self {
case .register(let report):
return report.dictionary
case .fetchReports, .fetchReportDetail:
return [:]
}
}

var isAuthorized: Bool {
Expand Down
63 changes: 63 additions & 0 deletions Projects/DataSource/Sources/Endpoint/S3UploadEndpoint.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//
// S3UploadEndpoint.swift
// DataSource
//
// Created by 이동현 on 11/22/25.
//

import Foundation

enum S3Endpoint {
case uploadImage(uploadURL: String, data: Data)
}

extension S3Endpoint: Endpoint {
var baseURL: String {
return ""
}

var path: String {
switch self {
case .uploadImage(let uploadURL, _):
return uploadURL
}
}

var method: HTTPMethod {
switch self {
case .uploadImage:
return .put
}
}

var headers: [String : String] {
switch self {
case .uploadImage:
return [:]
}
}

var queryParameters: [String : String] {

return [:]
}

var bodyParameters: [String : Any] {
return [:]
}

var isAuthorized: Bool {
return false
}

var bodyType: EndpointBodyType {
return .rawData
}

var bodyData: Data? {
switch self {
case .uploadImage(_, let data):
return data
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ extension Endpoint {
var request = try URLRequest(urlString: path, queryParameters: queryParameters)
request.httpMethod = method.rawValue
request.makeHeaders(headers: headers)
try request.makeBodyParameter(with: bodyParameters)
switch bodyType {
case .json:
try request.makeBodyParameter(with: bodyParameters)
case .rawData:
if let data = bodyData {
request.httpBody = data
}
}
request.cachePolicy = .reloadIgnoringLocalCacheData
return request
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ final class NetworkService {
throw NetworkError.invalidStatusCode(statusCode: httpResponse.statusCode)
}

guard !data.isEmpty else { throw NetworkError.emptyData }
if T.self == EmptyResponseDTO.self {
return EmptyResponseDTO() as? T
}

do {
let bitnagilResponse = try decoder.decode(BaseResponse<T>.self, from: data)
Expand Down
25 changes: 25 additions & 0 deletions Projects/DataSource/Sources/Repository/FileRepository.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// FileRepository.swift
// DataSource
//
// Created by 이동현 on 11/22/25.
//

import Domain
import Foundation

final class FileRepository: FileRepositoryProtocol {
private let networkService = NetworkService.shared

func fetchPresignedURL(prefix: String?, fileNames: [String]) async throws -> [String : String]? {
let dtos = fileNames.map { FilePresignedConditionDTO(prefix: prefix, fileName: $0) }
let endpoint = FilePresignedEndpoint.fetchPresignedURL(presignedConditions: dtos)

return try await networkService.request(endpoint: endpoint, type: [String:String].self)
}

func uploadFile(url: String, data: Data) async throws {
let endPoint = S3Endpoint.uploadImage(uploadURL: url, data: data)
_ = try await networkService.request(endpoint: endPoint, type: EmptyResponseDTO.self)
}
}
Loading