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

[IDLE-180] 센터프로필 조회및 수정 API 연동 #22

Merged
merged 3 commits into from
Jul 22, 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
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ public enum IdleInfoPlist {

public static let exampleAppDefault: InfoPlist = .extendingDefault(with: [
"Privacy - Photo Library Usage Description" : "프로필 사진 설정을 위해 사진 라이브러리에 접근합니다.",
"NSAppTransportSecurity" : [
"NSAllowsArbitraryLoads" : true
],
"UILaunchStoryboardName": "LaunchScreen.storyboard",
"CFBundleDisplayName" : "$(BUNDLE_DISPLAY_NAME)",
"UIApplicationSceneManifest": [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//
// DefaultUserProfileRepository.swift
// ConcreteRepository
//
// Created by choijunios on 7/20/24.
//

import Foundation
import RxSwift
import Entity
import RepositoryInterface
import NetworkDataSource

public class DefaultUserProfileRepository: UserProfileRepository {

let userInformationService: UserInformationService
let externalRequestService: ExternalRequestService

public init(_ keyValueStore: KeyValueStore? = nil) {

if let keyValueStore {
self.userInformationService = .init(keyValueStore: keyValueStore)
self.externalRequestService = .init(keyValueStore: keyValueStore)
} else {
self.userInformationService = .init()
self.externalRequestService = .init()
}
}

public func getCenterProfile() -> Single<CenterProfileVO> {

userInformationService
.requestDecodable(api: .getCenterProfile, with: .withToken)
.map { (dto: CenterProfileDTO) in dto.toEntity() }
}

public func updateCenterProfileForText(phoneNumber: String, introduction: String?) -> Single<Void> {
userInformationService
.request(api: .updateCenterProfile(
officeNumber: phoneNumber,
introduce: introduction
), with: .withToken)
.map { _ in return () }
}

/// 이미지 업로드
public func uploadImage(_ userType: UserType, imageInfo: ImageUploadInfo) -> Single<Void> {

getPreSignedUrl(userType, ext: imageInfo.ext)
.flatMap { [unowned self] dto in
self.uploadImageToPreSignedUrl(url: dto.uploadUrl, data: imageInfo.data)
.map { _ in (id: dto.imageId, ext: dto.imageFileExtension) }
}
.flatMap { (id, ext) in
self.callbackToServerForUploadImageSuccess(userType, imageId: id, ext: ext)
}
}

private func getPreSignedUrl(_ userType: UserType, ext: String) -> Single<ProfileImageUploadInfoDTO> {
userInformationService
.request(api: .getPreSignedUrl(userType: userType, imageExt: ext), with: .withToken)
.map(ProfileImageUploadInfoDTO.self)
}

private func uploadImageToPreSignedUrl(url: String, data: Data) -> Single<Void> {
externalRequestService
.request(api: .uploadImageToS3(url: url, data: data), with: .plain)
.map { _ in () }
}

private func callbackToServerForUploadImageSuccess(_ userType: UserType, imageId: String, ext: String) -> Single<Void> {
userInformationService
.request(api: .imageUploadSuccessCallback(
userType: userType,
imageId: imageId,
imageExt: ext), with: .withToken
)
.map { _ in () }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import RxSwift
@testable import NetworkDataSource

// TestKeyValueStore
class TestKeyValueStore: KeyValueStore {
public class TestKeyValueStore: KeyValueStore {

init(testStore: [String : String] = [:]) {
public init(testStore: [String : String] = [:]) {
self.testStore = [
Key.Auth.kaccessToken: "access_token",
Key.Auth.krefreshToken: "refresh_token",
Expand All @@ -21,22 +21,22 @@ class TestKeyValueStore: KeyValueStore {

var testStore: [String: String] = [:]

func save(key: String, value: String) throws {
public func save(key: String, value: String) throws {

testStore[key] = value
}

func get(key: String) -> String? {
public func get(key: String) -> String? {

return testStore[key]
}

func delete(key: String) throws {
public func delete(key: String) throws {

testStore.removeValue(forKey: key)
}

func removeAll() throws {
public func removeAll() throws {

testStore.removeAll()
}
Expand Down
15 changes: 10 additions & 5 deletions project/Projects/Data/NetworkDataSource/API/BaseAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import Moya
public enum APIType {

case auth
case users
case external(url: String)
}

// MARK: BaseAPI
Expand All @@ -25,13 +27,13 @@ public extension BaseAPI {

var baseStr = NetworkConfig.baseUrl

let apiVersion = "v1"

baseStr += "/api/\(apiVersion)"

switch apiType {
case .auth:
baseStr += "/auth"
case .users:
baseStr += "/users"
case .external(let url):
baseStr = url
}

return URL(string: baseStr)!
Expand All @@ -40,7 +42,10 @@ public extension BaseAPI {
/// Default header
var headers: [String : String]? {

return ["Content-Type": "application/json"]
switch apiType {
default:
["Content-Type": "application/json"]
}
}

var validationType: ValidationType { .successCodes }
Expand Down
48 changes: 48 additions & 0 deletions project/Projects/Data/NetworkDataSource/API/ExtenalUrlAPI.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// ExtenalUrlAPI.swift
// NetworkDataSource
//
// Created by choijunios on 7/20/24.
//

import Foundation
import Moya
import Alamofire

public enum ExtenalUrlAPI {

case uploadImageToS3(url: String, data: Data)
}

extension ExtenalUrlAPI: BaseAPI {

public var apiType: APIType {
var baseUrl: String!
switch self {
case .uploadImageToS3(let url, _):
baseUrl = url
}
return .external(url: baseUrl)
}

public var path: String {
switch self {
default:
""
}
}

public var method: Moya.Method {
switch self {
case .uploadImageToS3:
.put
}
}

public var task: Moya.Task {
switch self {
case .uploadImageToS3(_, let data):
.requestData(data)
}
}
}
101 changes: 101 additions & 0 deletions project/Projects/Data/NetworkDataSource/API/UserInformationAPI.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//
// UserInformationAPI.swift
// NetworkDataSource
//
// Created by choijunios on 7/20/24.
//

import Foundation
import Moya
import Entity
import Alamofire

extension UserType {
var pathUri: String {
switch self {
case .center:
"center"
case .worker:
"carer"
}
}
}

public enum UserInformationAPI {

// 프로필 조회
case getCenterProfile
case updateCenterProfile(officeNumber: String, introduce: String?)

// 프로필 사진 업로드
case getPreSignedUrl(userType: UserType, imageExt: String)
case imageUploadSuccessCallback(userType: UserType, imageId: String, imageExt: String)

// case getPreSignedUrlForProfile(type: UserType)
// case callbackForUpdateProfileImage(type: UserType)
}

extension UserInformationAPI: BaseAPI {

public var apiType: APIType {
.users
}

public var path: String {
switch self {
case .getCenterProfile:
"center/my/profile"
case .updateCenterProfile:
"center/my/profile"
case .getPreSignedUrl(let type, _):
"\(type.pathUri)/my/profile-image/upload-url"
case .imageUploadSuccessCallback(let type, _, _):
"\(type.pathUri)/my/profile-image/upload-callback"
}
}

public var method: Moya.Method {
switch self {
case .getCenterProfile:
.get
case .updateCenterProfile:
.post
case .getPreSignedUrl:
.get
case .imageUploadSuccessCallback:
.post
}
}

var parameterEncoding: ParameterEncoding {
switch self {
default:
return JSONEncoding.default
}
}

public var task: Moya.Task {
switch self {
case .getCenterProfile:
return .requestPlain
case .updateCenterProfile(let officeNumber, let introduce):
var bodyData: [String: String] = ["officeNumber": officeNumber]
if let introduce {
bodyData["introduce"] = introduce
}
return .requestParameters(parameters: bodyData, encoding: parameterEncoding)
case .getPreSignedUrl(_, let imageExt):
let params: [String: String] = [
"imageFileExtension": imageExt
]
return .requestParameters(parameters: params, encoding: URLEncoding.queryString)
case.imageUploadSuccessCallback(_, let imageId, let imageExt):
let params: [String: String] = [
"imageId": imageId,
"imageFileExtension": imageExt
]
return .requestParameters(parameters: params, encoding: parameterEncoding)
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// CenterProfileDTO.swift
// NetworkDataSource
//
// Created by choijunios on 7/20/24.
//

import Foundation
import Entity

public struct CenterProfileDTO: Codable {
let centerName: String?
let officeNumber: String?
let roadNameAddress: String?
let lotNumberAddress: String?
let detailedAddress: String?
let longitude: String?
let latitude: String?
let introduce: String?
let profileImageUrl: String?
Comment on lines +12 to +20
Copy link
Collaborator

Choose a reason for hiding this comment

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

@J0onYEong 이 값들이 모두 정말로 Optional인가요~~~?
적어도 몇개는 required로 갖고 있어야 할 값들로 보입니다. 파싱 오류 방지를 위해서 무조건 Optional로 가는건 좋지 않습니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

넵! 저렇게 처리하고 랩핑을 벗길때 기본값을 고민했어야 했는데.. 앞으로는 요구사항을 좀더 명확히 인지해 보겠습니다!

}

public extension CenterProfileDTO {

func toEntity() -> CenterProfileVO {
CenterProfileVO(
centerName: centerName ?? "",
officeNumber: officeNumber ?? "",
roadNameAddress: roadNameAddress ?? "",
lotNumberAddress: lotNumberAddress ?? "",
detailedAddress: detailedAddress ?? "",
longitude: longitude ?? "",
latitude: latitude ?? "",
introduce: introduce ?? "",
profileImageURL: URL(string: profileImageUrl ?? "")
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// ProfileImageUploadInfoDTO.swift
// NetworkDataSource
//
// Created by choijunios on 7/20/24.
//

import Foundation

public struct ProfileImageUploadInfoDTO: Decodable {

public let imageId: String
public let imageFileExtension: String
public let uploadUrl: String
}
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ public extension BaseNetworkService {

_request(
api: api,
provider: with == .plain ? self.providerWithoutToken : self.providerWithoutToken
provider: with == .plain ? self.providerWithoutToken : self.providerWithToken
)
}

Expand Down Expand Up @@ -243,6 +243,7 @@ public extension BaseNetworkService {
// }
}

// MARK: HTTPResponseException+Extension
extension HTTPResponseException {
init(response: Response) {

Expand Down
Loading