From bd834d358f72748e39833f8eb5a7dc8d91a7a557 Mon Sep 17 00:00:00 2001 From: 00yhsp <00yhsp@naver.com> Date: Sat, 17 Feb 2024 17:28:49 +0900 Subject: [PATCH] [#73] sponusapi-fix --- Spon-us.xcodeproj/project.pbxproj | 4 + Spon-us/Font/SponusAPI.swift | 365 ++++++++++++++++++++++++++++++ 2 files changed, 369 insertions(+) create mode 100644 Spon-us/Font/SponusAPI.swift diff --git a/Spon-us.xcodeproj/project.pbxproj b/Spon-us.xcodeproj/project.pbxproj index a283ae2..5121a00 100644 --- a/Spon-us.xcodeproj/project.pbxproj +++ b/Spon-us.xcodeproj/project.pbxproj @@ -119,6 +119,7 @@ DF5B7E922B7FA430001C009C /* PortfolioOfferViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF5B7E912B7FA430001C009C /* PortfolioOfferViewModel.swift */; }; DF5B7E942B808DF1001C009C /* EditAnnouncementViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF5B7E932B808DF0001C009C /* EditAnnouncementViewModel.swift */; }; DF5B7E962B809257001C009C /* EditAnnouncementModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF5B7E952B809257001C009C /* EditAnnouncementModel.swift */; }; + DF5B7EA62B80A59C001C009C /* SponusAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF5B7EA52B80A59C001C009C /* SponusAPI.swift */; }; DF692B162B7E39BD00BF9B75 /* RefreshTokenManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF692B152B7E39BD00BF9B75 /* RefreshTokenManager.swift */; }; DF692B182B7E3DA700BF9B75 /* RefreshTokenModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF692B172B7E3DA700BF9B75 /* RefreshTokenModel.swift */; }; DF692B1A2B7E970F00BF9B75 /* EditAnnouncementView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF692B192B7E970F00BF9B75 /* EditAnnouncementView.swift */; }; @@ -216,6 +217,7 @@ DF5B7E912B7FA430001C009C /* PortfolioOfferViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PortfolioOfferViewModel.swift; sourceTree = ""; }; DF5B7E932B808DF0001C009C /* EditAnnouncementViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditAnnouncementViewModel.swift; sourceTree = ""; }; DF5B7E952B809257001C009C /* EditAnnouncementModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditAnnouncementModel.swift; sourceTree = ""; }; + DF5B7EA52B80A59C001C009C /* SponusAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SponusAPI.swift; path = "../../test/Spon-us/SponusAPI.swift"; sourceTree = ""; }; DF692B152B7E39BD00BF9B75 /* RefreshTokenManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshTokenManager.swift; sourceTree = ""; }; DF692B172B7E3DA700BF9B75 /* RefreshTokenModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshTokenModel.swift; sourceTree = ""; }; DF692B192B7E970F00BF9B75 /* EditAnnouncementView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditAnnouncementView.swift; sourceTree = ""; }; @@ -360,6 +362,7 @@ 3B81BCB02B622E950067E9CB /* Manager */, 3BFC8D192B54D43C000D6006 /* Helper */, 100D38942B44833500498977 /* Font */, + DF5B7EA52B80A59C001C009C /* SponusAPI.swift */, 100A1E352B7348DB00AAC1E8 /* Model */, 100D38B42B4EF18C00498977 /* View */, 100D38A62B44836800498977 /* Spon_usApp.swift */, @@ -782,6 +785,7 @@ DF5B7E962B809257001C009C /* EditAnnouncementModel.swift in Sources */, DF498F272B79B45D00ADE078 /* KeyChainManager.swift in Sources */, 3B36F0A12B6FEBF60000ACFB /* Utils.swift in Sources */, + DF5B7EA62B80A59C001C009C /* SponusAPI.swift in Sources */, 100D38A72B44836800498977 /* Spon_usApp.swift in Sources */, 100A1E382B734DB300AAC1E8 /* EmailModel.swift in Sources */, 3B36F0A82B6FEC3C0000ACFB /* PubData.swift in Sources */, diff --git a/Spon-us/Font/SponusAPI.swift b/Spon-us/Font/SponusAPI.swift new file mode 100644 index 0000000..3e2d4f4 --- /dev/null +++ b/Spon-us/Font/SponusAPI.swift @@ -0,0 +1,365 @@ +// +// SponusAPI.swift +// Spon-us +// +// Created by yubin on 2/7/24. +// + +import Foundation +import Moya +import KeychainSwift +import SwiftUI + +enum SponusAPI { + case postEmail(email: String) + case postJoin(name: String, email: String, password: String, orgType: OrgType, subOrgType: SubOrgType?) + case postAnnouncement(title: String, type: String, category: String, content: String, images: [UIImage]) + case getCategory(category: String?, type: String?) + case getAnnouncement(announcementId: Int) + case propose(title: String, content: String, announcementId: Int, attachments: [URL]) + case postLogin(email: String, password: String, fcmToken: String) + case getLogout + case getMe + case getSent + case getProposalDetail(proposeId: Int) + case getOrganization(organizationId: Int) + case getReceived(announcementId: Int) + case getMyAnnouncements + case getRenewToken + case patchChangeAnnouncementStatus(announcementID: Int, status: String) + case patchChangeOfferStatus(proposeID: Int, status: String) + case deleteAnnouncement(announcementId: Int) + case patchModifyAnnouncement(announcementId: Int, title: String?, type: String?, category: String?, content: String?) +} + +extension SponusAPI: TargetType { + var baseURL: URL { + return URL(string: "http://3.38.233.66:8080")! + } + + var path: String { + switch self { + case .postEmail: + return "/api/v1/organizations/email" + case .postJoin: + return "/api/v1/organizations/join" + case .postAnnouncement(title: let title, type: let type, category: let category, content: let content, images: let images): + return "/api/v1/announcements" + case .getCategory(category: let category, type: let type): + return "/api/v1/announcements/category" + case .getAnnouncement(announcementId: let announcementId): + return "/api/v1/announcements/\(announcementId)" + case .propose(title: let title, content: let content, announcementId: let announcementId, attachments: let attachments): + return "/api/v1/proposes" + case .postLogin: + return "/api/v1/organizations/login" + case .getLogout: + return "/api/v1/organizations/logout" + case .getMe: + return "/api/v1/organizations/me" + case .getSent: + return "/api/v1/proposes/sent" + case .getProposalDetail(let proposeId): + return "/api/v1/proposes/\(proposeId)" + case .getOrganization(let organizationId): + return "/api/v1/organizations/\(organizationId)" + case .getReceived: + return "/api/v1/proposes/received" + case .getMyAnnouncements: + return "/api/v1/announcements/me/opened" + case .getRenewToken: + return "/api/v1/auth/reissue" + case .patchChangeAnnouncementStatus(let announcementID, _): + return "/api/v1/announcements/\(announcementID)/status" + case .patchChangeOfferStatus(let proposeID, let status): + return "/api/v1/propose/\(proposeID)" + case .deleteAnnouncement(let announcementId): + return "/api/v1/announcements/\(announcementId)" + case .patchModifyAnnouncement(let announcementId, _, _, _, _): + return "/api/v1/announcements/\(announcementId)" + } + } + + var method: Moya.Method { + switch self { + case .postEmail: + return .post + case .postJoin: + return .post + case .postAnnouncement(title: let title, type: let type, category: let category, content: let content, images: let images): + return .post + case .getCategory(category: let category, type: let type): + return .get + case .getAnnouncement(announcementId: let announcementId): + return .get + case .propose(title: let title, content: let content, announcementId: let announcementId, attachments: let attachments): + return .post + case .postLogin: + return .post + case .getLogout: + return .get + case .getMe: + return .get + case .getSent: + return .get + case .getProposalDetail: + return .get + case .getOrganization: + return .get + case .getReceived: + return .get + case .getMyAnnouncements: + return .get + case .getRenewToken: + return .get + case .patchChangeAnnouncementStatus: + return .patch + case .patchChangeOfferStatus: + return .patch + case .deleteAnnouncement: + return .delete + case .patchModifyAnnouncement: + return .patch + } + } + + var sampleData: Data { + switch self { + case .postEmail: + return Data() + case .postJoin: + let response: [String : Any] = [ + "statusCode": "OK", + "message": "OK", + "content": [ + "id": 0, + "email": "test@test.com", + "name": "test" + ] + ] + return try! JSONSerialization.data(withJSONObject: response, options: .prettyPrinted) + case .postAnnouncement(title: let title, type: let type, category: let category, content: let content, images: let images): + let sampleResponse: [String: Any] = [ + "statusCode": "string", + "message": "string", + "content": [ + "id": 0, + "writerId": 0, + "title": "string", + "type": "SPONSORSHIP", + "category": "IDEA", + "content": "string", + "status": "OPENED", + "viewCount": 0 + ] + ] + return try! JSONSerialization.data(withJSONObject: sampleResponse, options: .prettyPrinted) + case .getCategory(category: let category, type: let type): + return Data() + case .getAnnouncement(announcementId: let announcementId): + return Data() + case .propose(title: let title, content: let content, announcementId: let announcementId, attachments: let attachments): + return Data() + case .postLogin: + let response: [String : Any] = [ + "statusCode": "200", + "message": "OK", + "content": [ + "accessToken": "sfiulghsdkh", + "refreshToken": "alskdjfhaslkjgh" + ] + ] + return try! JSONSerialization.data(withJSONObject: response, options: .prettyPrinted) + case .getLogout: + return Data() + case .getMe: + return Data() + case .getSent: + return Data() + case .getProposalDetail: + return Data() + case .getOrganization: + return Data() + case .getReceived: + return Data() + case .getMyAnnouncements: + return Data() + case .getRenewToken: + return Data() + case .patchChangeAnnouncementStatus: + return Data() + case .patchChangeOfferStatus: + return Data() + case .deleteAnnouncement: + return Data() + case .patchModifyAnnouncement: + return Data() + } + } + + var task: Moya.Task { + switch self { + case .postEmail(let email): + let parameters: [String: Any] = ["email": email] + return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) + case .postJoin(let name, let email, let password, let orgType, let subOrgType): + switch orgType { + case .student: + switch subOrgType! { + case .studentCouncil: + let requestBody = JoinRequestBody(name: name, email: email, password: password, organizationType: "STUDENT", suborganizationType: "STUDENT_COUNCIL") + return .requestJSONEncodable(requestBody) + case .studentClub: + let requestBody = JoinRequestBody(name: name, email: email, password: password, organizationType: "STUDENT", suborganizationType: "STUDENT_CLUB") + return .requestJSONEncodable(requestBody) + } + case .company: + let requestBody = JoinRequestBody(name: name, email: email, password: password, organizationType: "STUDENT", suborganizationType: nil) + return .requestJSONEncodable(requestBody) + } + case .postAnnouncement(title: let title, type: let type, category: let category, content: let content, images: let images): + let requestParams = [ + "title": title, + "type": type, + "category": category, + "content": content + ] + let requestJSON = try? JSONEncoder().encode(requestParams) + + let imageMultipartData = images.enumerated().map { (index, image) -> MultipartFormData in + let imageData = image.jpegData(compressionQuality: 0.7) ?? Data() + return MultipartFormData(provider: .data(imageData), name: "images", fileName: "image\(index).jpg", mimeType: "image/jpeg") + } + var multipartData = [MultipartFormData]() + if let requestData = requestJSON { + multipartData.append(MultipartFormData(provider: .data(requestData), name: "request")) + } + multipartData.append(contentsOf: imageMultipartData) + + return .uploadMultipart(multipartData) + case .getCategory(category: let category, type: let type): + var parameters: [String: Any] = [:] + if let category = category { + parameters["category"] = category + } + if let type = type { + parameters["type"] = type + } + return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) + case .getAnnouncement(announcementId: let announcementId): + return .requestPlain + case .propose(let title, let content, let announcementId, let attachments): + var multipartData: [MultipartFormData] = [] + let requestParameters = [ + "title": title, + "content": content, + "announcementId": announcementId + ] as [String : Any] + if let requestData = try? JSONSerialization.data(withJSONObject: requestParameters, options: []) { + multipartData.append(MultipartFormData(provider: .data(requestData), name: "request")) + } + attachments.forEach { url in + if let data = try? Data(contentsOf: url) { + let formData = MultipartFormData(provider: .data(data), name: "attachments", fileName: url.lastPathComponent, mimeType: "application/octet-stream") //MimeType 실제로 바꾸기, 아직 구현 x + multipartData.append(formData) + } + } + if attachments.isEmpty { + let formData = MultipartFormData(provider: .data("string".data(using: .utf8)!), name: "attachments") + multipartData.append(formData) + } + return .uploadMultipart(multipartData) + case .postLogin(let email, let password, let fcmToken): + let requestBody = LoginRequestBody(email: email, password: password, fcmToken: fcmToken) + return .requestJSONEncodable(requestBody) + case .getLogout: + return .requestPlain + case .getMe: + return .requestPlain + case .getSent: + return .requestPlain + case .getProposalDetail: + return .requestPlain + case .getOrganization: + return .requestPlain + case .getReceived(let announcementId): + let parameters: [String: Any] = ["announcementId": announcementId] + return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) + case .getMyAnnouncements: + return .requestPlain + case .getRenewToken: + return .requestPlain + case .patchChangeAnnouncementStatus(_, let status): + let requestBody = ["status" : status] + return .requestJSONEncodable(requestBody) + case .patchChangeOfferStatus(_, let status): + let requestBody = ["status" : status] + return .requestJSONEncodable(requestBody) + case .deleteAnnouncement(let announcementId): + let requestBody = ["announcementId" : announcementId] + return .requestJSONEncodable(requestBody) + case .patchModifyAnnouncement(_, let title, let type, let category, let content): + let requestBody = [ + "title" : title, + "type" : type, + "category" : category, + "content" : content + ] + return .requestJSONEncodable(requestBody) + } + } + + var headers: [String: String]? { + switch self { + case .postEmail: + return nil + case .postJoin: + return nil + case .postLogin: + return nil + case .getLogout: + return ["Authorization": "Bearer \(loadAccessToken(userID: UserDefaults.standard.string(forKey: "loginAccount") ?? "loadAccessToken Error"))"] + case .getMe: + return ["Authorization": "Bearer \(loadAccessToken(userID: UserDefaults.standard.string(forKey: "loginAccount") ?? "loadAccessToken Error"))"] + case .getSent: + return ["Authorization": "Bearer \(loadAccessToken(userID: UserDefaults.standard.string(forKey: "loginAccount") ?? "loadAccessToken Error"))"] + case .getProposalDetail: + return ["Authorization": "Bearer \(loadAccessToken(userID: UserDefaults.standard.string(forKey: "loginAccount") ?? "loadAccessToken Error"))"] + case .getOrganization: + return ["Authorization": "Bearer \(loadAccessToken(userID: UserDefaults.standard.string(forKey: "loginAccount") ?? "loadAccessToken Error"))"] + /* + case .postLike: + return ["Content-Type": "application/json", + "Accept": "application/json", + "atk": KeychainSwift().get("accessToken") ?? ""] */ + case .postAnnouncement(title: let title, type: let type, category: let category, content: let content, images: let images): + return ["Authorization": "Bearer \(loadAccessToken(userID: UserDefaults.standard.string(forKey: "loginAccount") ?? "loadAccessToken Error"))"] + case .getCategory(category: let category, type: let type): + return ["Authorization": "Bearer \(loadAccessToken(userID: UserDefaults.standard.string(forKey: "loginAccount") ?? "loadAccessToken Error"))"] + case .getAnnouncement(announcementId: let announcementId): + return ["Authorization": "Bearer \(loadAccessToken(userID: UserDefaults.standard.string(forKey: "loginAccount") ?? "loadAccessToken Error"))"] + case .propose(title: let title, content: let content, announcementId: let announcementId, attachments: let attachments): + return ["Authorization": "Bearer \(loadAccessToken(userID: UserDefaults.standard.string(forKey: "loginAccount") ?? "loadAccessToken Error"))"] + case .getReceived: + return ["Authorization": "Bearer \(loadAccessToken(userID: UserDefaults.standard.string(forKey: "loginAccount") ?? "loadAccessToken Error"))"] + case .getMyAnnouncements: + return ["Authorization": "Bearer \(loadAccessToken(userID: UserDefaults.standard.string(forKey: "loginAccount") ?? "loadAccessToken Error"))"] + case .getRenewToken: + return ["RefreshToken": "\(loadRefreshToken(userID: UserDefaults.standard.string(forKey: "loginAccount") ?? "loadRefreshToken Error"))"] + case .patchChangeAnnouncementStatus: + return ["Authorization": "Bearer \(loadAccessToken(userID: UserDefaults.standard.string(forKey: "loginAccount") ?? "loadAccessToken Error"))"] + case .patchChangeOfferStatus: + return ["Authorization": "Bearer \(loadAccessToken(userID: UserDefaults.standard.string(forKey: "loginAccount") ?? "loadAccessToken Error"))"] + case .deleteAnnouncement: + return ["Authorization": "Bearer \(loadAccessToken(userID: UserDefaults.standard.string(forKey: "loginAccount") ?? "loadAccessToken Error"))"] + case .patchModifyAnnouncement: + return ["Authorization": "Bearer \(loadAccessToken(userID: UserDefaults.standard.string(forKey: "loginAccount") ?? "loadAccessToken Error"))"] + } + } +} + +extension SponusAPI { + var validationType: ValidationType { + return .successCodes + } +}