From 3bca8bfa994163635e1128f0404007c6d0d4761f Mon Sep 17 00:00:00 2001 From: Volodymyr Chekyrta <127732735+volodymyr-chekyrta@users.noreply.github.com> Date: Fri, 5 Jan 2024 10:23:54 +0100 Subject: [PATCH] feat: api migration (#213) * change api endpoints New endpoints: Delete account api/user/v1/accounts/deactivate_logout/ Dashboard Courses api/mobile/v3/users/{USERNAME}/course_enrollments Add Comment / Response api/discussion/v1/comments/ Discovery And Search api/courses/v1/courses/ Get Course Details api/mobile/v3/course_info/{COURSE_ID}/info Get Course Structure api/mobile/v3/course_info/blocks/ * Added mobile_search to the search endpoint * Remove unused Data_Mycourse.swift Rename DashCourse -> DashboardCourse * Fix CI? * refactor: address review feedback * fix: SignInViewModel DI missed argument * Change CourseEndpoint.getCourseDetail endpoint * Fix Data_Dashboard parsing CourseMode * Add verified mode to the CourseMode * Fix: Parse enums with fallback to the unknown value * Added the string value to the StartType enum * Remove unused enums --- Core/Core.xcodeproj/project.pbxproj | 8 +- Core/Core/Data/Model/Data_Certificate.swift | 24 ++ Core/Core/Data/Model/Data_Dashboard.swift | 268 +++++++++++++++--- Core/Core/Data/Model/Data_Discovery.swift | 9 +- Core/Core/Data/Model/Data_MyCourse.swift | 159 ----------- Course/Course/Data/CourseRepository.swift | 27 +- .../Course/Data/Network/CourseEndpoint.swift | 19 +- .../Data/Network/DashboardEndpoint.swift | 2 +- .../Data/Network/DiscoveryEndpoint.swift | 3 +- .../Data/Network/DiscussionEndpoint.swift | 2 +- OpenEdX/DI/ScreenAssembly.swift | 2 +- OpenEdX/Router.swift | 8 +- .../Data/Network/ProfileEndpoint.swift | 2 +- ci_scripts/ci_prepare_env.sh | 2 + 14 files changed, 299 insertions(+), 236 deletions(-) create mode 100644 Core/Core/Data/Model/Data_Certificate.swift delete mode 100644 Core/Core/Data/Model/Data_MyCourse.swift diff --git a/Core/Core.xcodeproj/project.pbxproj b/Core/Core.xcodeproj/project.pbxproj index d7552335e..e055bfc89 100644 --- a/Core/Core.xcodeproj/project.pbxproj +++ b/Core/Core.xcodeproj/project.pbxproj @@ -55,7 +55,6 @@ 027BD3BD2909478B00392132 /* UIView+EnclosingScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 027BD3BA2909478B00392132 /* UIView+EnclosingScrollView.swift */; }; 027BD3BE2909478B00392132 /* UIResponder+CurrentResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 027BD3BB2909478B00392132 /* UIResponder+CurrentResponder.swift */; }; 027BD3C52909707700392132 /* Shake.swift in Sources */ = {isa = PBXBuildFile; fileRef = 027BD3C42909707700392132 /* Shake.swift */; }; - 027DB33528D8C8FE002B6862 /* Data_MyCourse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 027DB33428D8C8FE002B6862 /* Data_MyCourse.swift */; }; 0282DA7328F98CC9003C3F07 /* WebUnitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0282DA7228F98CC9003C3F07 /* WebUnitView.swift */; }; 0283347D28D4D3DE00C828FC /* Data_Discovery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0283347C28D4D3DE00C828FC /* Data_Discovery.swift */; }; 0283348028D4DCD200C828FC /* ViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0283347F28D4DCD200C828FC /* ViewExtension.swift */; }; @@ -118,6 +117,7 @@ 0770DE5F28D0B22C006D8A5D /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0770DE5E28D0B22C006D8A5D /* Strings.swift */; }; 0770DE6128D0B2CB006D8A5D /* Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0770DE6028D0B2CB006D8A5D /* Assets.swift */; }; 07DDFCBD29A780BB00572595 /* UINavigationController+Animation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07DDFCBC29A780BB00572595 /* UINavigationController+Animation.swift */; }; + 07E0939F2B308D2800F1E4B2 /* Data_Certificate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E0939E2B308D2800F1E4B2 /* Data_Certificate.swift */; }; A53A32352B233DEC005FE38A /* ThemeConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53A32342B233DEC005FE38A /* ThemeConfig.swift */; }; BA30427F2B20B320009B64B7 /* SocialAuthError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA30427D2B20B299009B64B7 /* SocialAuthError.swift */; }; BA76135C2B21BC7300B599B7 /* SocialAuthResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA76135B2B21BC7300B599B7 /* SocialAuthResponse.swift */; }; @@ -219,7 +219,6 @@ 027BD3BA2909478B00392132 /* UIView+EnclosingScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+EnclosingScrollView.swift"; sourceTree = ""; }; 027BD3BB2909478B00392132 /* UIResponder+CurrentResponder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIResponder+CurrentResponder.swift"; sourceTree = ""; }; 027BD3C42909707700392132 /* Shake.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shake.swift; sourceTree = ""; }; - 027DB33428D8C8FE002B6862 /* Data_MyCourse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Data_MyCourse.swift; sourceTree = ""; }; 0282DA7228F98CC9003C3F07 /* WebUnitView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebUnitView.swift; sourceTree = ""; }; 0283347C28D4D3DE00C828FC /* Data_Discovery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Data_Discovery.swift; sourceTree = ""; }; 0283347F28D4DCD200C828FC /* ViewExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewExtension.swift; sourceTree = ""; }; @@ -285,6 +284,7 @@ 0770DE5E28D0B22C006D8A5D /* Strings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = ""; }; 0770DE6028D0B2CB006D8A5D /* Assets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Assets.swift; sourceTree = ""; }; 07DDFCBC29A780BB00572595 /* UINavigationController+Animation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationController+Animation.swift"; sourceTree = ""; }; + 07E0939E2B308D2800F1E4B2 /* Data_Certificate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Data_Certificate.swift; sourceTree = ""; }; 0E13E9173C9C4CFC19F8B6F2 /* Pods-App-Core.debugstage.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Core.debugstage.xcconfig"; path = "Target Support Files/Pods-App-Core/Pods-App-Core.debugstage.xcconfig"; sourceTree = ""; }; 1A154A95AF4EE85A4A1C083B /* Pods-App-Core.releasedev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Core.releasedev.xcconfig"; path = "Target Support Files/Pods-App-Core/Pods-App-Core.releasedev.xcconfig"; sourceTree = ""; }; 2B7E6FE7843FC4CF2BFA712D /* Pods-App-Core.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Core.debug.xcconfig"; path = "Target Support Files/Pods-App-Core/Pods-App-Core.debug.xcconfig"; sourceTree = ""; }; @@ -495,13 +495,13 @@ 0727878428D31657002E9142 /* Data_User.swift */, 0283347C28D4D3DE00C828FC /* Data_Discovery.swift */, 02C917EF29CDA99E00DBB8BD /* Data_Dashboard.swift */, - 027DB33428D8C8FE002B6862 /* Data_MyCourse.swift */, 021D924728DC860C00ACC565 /* Data_UserProfile.swift */, 0259104929C4A5B6004B5A55 /* UserSettings.swift */, 070019A428F6F17900D5FC78 /* Data_Media.swift */, 0236961C28F9A2D200EEF206 /* Data_AuthResponse.swift */, 027BD3912907D88F00392132 /* Data_RegistrationFields.swift */, 028F9F36293A44C700DE65D0 /* Data_ResetPassword.swift */, + 07E0939E2B308D2800F1E4B2 /* Data_Certificate.swift */, ); path = Model; sourceTree = ""; @@ -1003,7 +1003,6 @@ 0233D5712AF13EC800BAC8BD /* SelectMailClientView.swift in Sources */, BAFB99842B0E282E007D09F9 /* MicrosoftConfig.swift in Sources */, 02B2B594295C5C7A00914876 /* Thread.swift in Sources */, - 027DB33528D8C8FE002B6862 /* Data_MyCourse.swift in Sources */, 027BD3BD2909478B00392132 /* UIView+EnclosingScrollView.swift in Sources */, BA8FA6682AD59A5700EA029A /* SocialAuthButton.swift in Sources */, 02D400612B0678190029D168 /* SKStoreReviewControllerExtension.swift in Sources */, @@ -1042,6 +1041,7 @@ 027BD3B82909476200392132 /* DismissKeyboardTapViewModifier.swift in Sources */, 024BE3DF29B2615500BCDEE2 /* CGColorExtension.swift in Sources */, 0770DE6128D0B2CB006D8A5D /* Assets.swift in Sources */, + 07E0939F2B308D2800F1E4B2 /* Data_Certificate.swift in Sources */, 0727878928D31734002E9142 /* User.swift in Sources */, A53A32352B233DEC005FE38A /* ThemeConfig.swift in Sources */, 02280F5B294B4E6F0032823A /* Connectivity.swift in Sources */, diff --git a/Core/Core/Data/Model/Data_Certificate.swift b/Core/Core/Data/Model/Data_Certificate.swift new file mode 100644 index 000000000..0b6475ed1 --- /dev/null +++ b/Core/Core/Data/Model/Data_Certificate.swift @@ -0,0 +1,24 @@ +// +// Data_Certificate.swift +// Core +// +// Created by Vladimir Chekyrta on 18.12.2023. +// + +import Foundation + +public extension DataLayer { + struct Certificate: Codable { + public let url: String? + + public init(url: String?) { + self.url = url + } + } +} + +public extension DataLayer.Certificate { + var domain: Certificate { + return Certificate(url: url ?? "") + } +} diff --git a/Core/Core/Data/Model/Data_Dashboard.swift b/Core/Core/Data/Model/Data_Dashboard.swift index 4133670a9..603af54b2 100644 --- a/Core/Core/Data/Model/Data_Dashboard.swift +++ b/Core/Core/Data/Model/Data_Dashboard.swift @@ -8,32 +8,48 @@ import Foundation public extension DataLayer { + // MARK: - CourseEnrollments struct CourseEnrollments: Codable { + public let enrollments: Enrollments + + enum CodingKeys: String, CodingKey { + case enrollments + } + + public init(enrollments: Enrollments) { + self.enrollments = enrollments + } + } + + // MARK: - Enrollments + struct Enrollments: Codable { public let next: String? public let previous: String? - public let count: Int - public let numPages: Int - public let currentPage: Int - public let start: Int + public let count: Int? + public let numPages: Int? + public let currentPage: Int? + public let start: Int? public let results: [Result] - + enum CodingKeys: String, CodingKey { - case next = "next" - case previous = "previous" - case count = "count" + case next + case previous + case count case numPages = "num_pages" case currentPage = "current_page" - case start = "start" - case results = "results" + case start + case results } - - public init(next: String?, - previous: String?, - count: Int, - numPages: Int, - currentPage: Int, - start: Int, - results: [Result]) { + + public init( + next: String?, + previous: String?, + count: Int?, + numPages: Int?, + currentPage: Int?, + start: Int?, + results: [Result] + ) { self.next = next self.previous = previous self.count = count @@ -43,57 +59,219 @@ public extension DataLayer { self.results = results } } - + // MARK: - Result struct Result: Codable { public let auditAccessExpires: String? public let created: String + public let mode: Mode public let isActive: Bool - public let course: Course - public let certificate: Certificate - + public let course: DashboardCourse + public let courseModes: [CourseMode] + enum CodingKeys: String, CodingKey { case auditAccessExpires = "audit_access_expires" - case created = "created" + case created + case mode case isActive = "is_active" - case course = "course" - case certificate = "certificate" + case course + case courseModes = "course_modes" } - - public init(auditAccessExpires: String?, created: String,// mode: Mode, - isActive: Bool, course: Course, certificate: Certificate) { + + public init( + auditAccessExpires: String?, + created: String, + mode: Mode, + isActive: Bool, + course: DashboardCourse, + courseModes: [CourseMode] + ) { self.auditAccessExpires = auditAccessExpires self.created = created + self.mode = mode self.isActive = isActive self.course = course - self.certificate = certificate + self.courseModes = courseModes + } + } + + // MARK: - Course + struct DashboardCourse: Codable { + public let id: String + public let name: String + public let number: String + public let org: String + public let start: String? + public let startDisplay: String + public let startType: StartType + public let end: String? + public let dynamicUpgradeDeadline: String? + public let subscriptionID: String + public let coursewareAccess: CoursewareAccess + public let media: Media + public let courseImage: String + public let courseAbout: String + public let courseSharingUtmParameters: CourseSharingUtmParameters + public let courseUpdates: String + public let courseHandouts: String + public let discussionURL: String + public let videoOutline: String? + public let isSelfPaced: Bool + + enum CodingKeys: String, CodingKey { + case id + case name + case number + case org + case start + case startDisplay = "start_display" + case startType = "start_type" + case end + case dynamicUpgradeDeadline = "dynamic_upgrade_deadline" + case subscriptionID = "subscription_id" + case coursewareAccess = "courseware_access" + case media + case courseImage = "course_image" + case courseAbout = "course_about" + case courseSharingUtmParameters = "course_sharing_utm_parameters" + case courseUpdates = "course_updates" + case courseHandouts = "course_handouts" + case discussionURL = "discussion_url" + case videoOutline = "video_outline" + case isSelfPaced = "is_self_paced" + } + + public init( + id: String, + name: String, + number: String, + org: String, + start: String?, + startDisplay: String, + startType: StartType, + end: String?, + dynamicUpgradeDeadline: String?, + subscriptionID: String, + coursewareAccess: CoursewareAccess, + media: Media, + courseImage: String, + courseAbout: String, + courseSharingUtmParameters: CourseSharingUtmParameters, + courseUpdates: String, + courseHandouts: String, + discussionURL: String, + videoOutline: String?, + isSelfPaced: Bool + ) { + self.id = id + self.name = name + self.number = number + self.org = org + self.start = start + self.startDisplay = startDisplay + self.startType = startType + self.end = end + self.dynamicUpgradeDeadline = dynamicUpgradeDeadline + self.subscriptionID = subscriptionID + self.coursewareAccess = coursewareAccess + self.media = media + self.courseImage = courseImage + self.courseAbout = courseAbout + self.courseSharingUtmParameters = courseSharingUtmParameters + self.courseUpdates = courseUpdates + self.courseHandouts = courseHandouts + self.discussionURL = discussionURL + self.videoOutline = videoOutline + self.isSelfPaced = isSelfPaced + } + } + + // MARK: - CourseMode + struct CourseMode: Codable { + public let slug: Mode? + public let sku: String? + public let androidSku: String? + public let iosSku: String? + + enum CodingKeys: String, CodingKey { + case slug + case sku + case androidSku = "android_sku" + case iosSku = "ios_sku" + } + + public init(slug: Mode?, sku: String?, androidSku: String?, iosSku: String?) { + self.slug = slug + self.sku = sku + self.androidSku = androidSku + self.iosSku = iosSku + } + } + + enum Mode: String, Codable { + case audit + case honor + case verified + case unknown + + public init(from decoder: Decoder) throws { + let rawValue = try decoder.singleValueContainer().decode(RawValue.self) + self = Mode(rawValue: rawValue) ?? .unknown + } + } + + // MARK: - CourseSharingUtmParameters + struct CourseSharingUtmParameters: Codable { + public let facebook: String + public let twitter: String + } + + // MARK: - CoursewareAccess + struct CoursewareAccess: Codable { + public let hasAccess: Bool + public let errorCode: String? + public let developerMessage: String? + public let userMessage: String? + public let additionalContextUserMessage: String? + public let userFragment: String? + + enum CodingKeys: String, CodingKey { + case hasAccess = "has_access" + case errorCode = "error_code" + case developerMessage = "developer_message" + case userMessage = "user_message" + case additionalContextUserMessage = "additional_context_user_message" + case userFragment = "user_fragment" } } } public extension DataLayer.CourseEnrollments { func domain(baseURL: String) -> [CourseItem] { - - return results.map { course in - let imageURL = baseURL + (course.course.media.courseImage?.url?.addingPercentEncoding( - withAllowedCharacters: .urlQueryAllowed) ?? "") + return enrollments.results.map { result in + let course = result.course + + let imageUrl = course.media.courseImage?.url ?? "" + let encodedUrl = imageUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "" + let fullImageURL = baseURL + encodedUrl + return CourseItem( - name: course.course.name, - org: course.course.org, + name: course.name, + org: course.org, shortDescription: "", - imageURL: imageURL, + imageURL: fullImageURL, isActive: true, - courseStart: course.course.start != nil ? Date(iso8601: course.course.start!) : nil, - courseEnd: course.course.end != nil ? Date(iso8601: course.course.end!) : nil, - enrollmentStart: course.course.enrollmentStart != nil - ? Date(iso8601: course.course.enrollmentStart!) + courseStart: course.start != nil ? Date(iso8601: course.start!) : nil, + courseEnd: course.end != nil ? Date(iso8601: course.end!) : nil, + enrollmentStart: course.start != nil + ? Date(iso8601: course.start!) : nil, - enrollmentEnd: course.course.enrollmentEnd != nil - ? Date(iso8601: course.course.enrollmentEnd!) + enrollmentEnd: course.end != nil + ? Date(iso8601: course.end!) : nil, - courseID: course.course.id, - numPages: numPages, - coursesCount: count + courseID: course.id, + numPages: enrollments.numPages ?? 1, + coursesCount: enrollments.count ?? 0 ) } } diff --git a/Core/Core/Data/Model/Data_Discovery.swift b/Core/Core/Data/Model/Data_Discovery.swift index 6bbc014b5..cb8dd9be8 100644 --- a/Core/Core/Data/Model/Data_Discovery.swift +++ b/Core/Core/Data/Model/Data_Discovery.swift @@ -90,8 +90,15 @@ public extension DataLayer { } enum StartType: String, Codable { - case empty case timestamp + case string + case empty + case unknown + + public init(from decoder: Decoder) throws { + let rawValue = try decoder.singleValueContainer().decode(RawValue.self) + self = StartType(rawValue: rawValue) ?? .unknown + } } } diff --git a/Core/Core/Data/Model/Data_MyCourse.swift b/Core/Core/Data/Model/Data_MyCourse.swift deleted file mode 100644 index f11e27bd7..000000000 --- a/Core/Core/Data/Model/Data_MyCourse.swift +++ /dev/null @@ -1,159 +0,0 @@ -// -// Data_Dashboard.swift -// Core -// -// Created by  Stepanok Ivan on 19.09.2022. -// - -import Foundation - -// MARK: "/api/mobile/v1/users/\(username)/course_enrollments/" - -public extension DataLayer { - - struct MyCourse: Codable { - public let auditAccessExpires: String? - public let created: String - public let mode: String - public let isActive: Bool - public let course: DashboardCourse - public let certificate: Certificate? - - enum CodingKeys: String, CodingKey { - case auditAccessExpires = "audit_access_expires" - case created - case mode - case isActive = "is_active" - case course - case certificate = "certificate" - } - } - - // MARK: - Certificate - struct Certificate: Codable { - public let url: String? - - public init(url: String?) { - self.url = url - } - } - - // MARK: - Course - struct DashboardCourse: Codable { - public let id: String - public let name: String - public let number: String - public let org: String - public let start: String? - public let startDisplay: String? - public let startType: String? - public let end: String? - public let dynamicUpgradeDeadline: String? - public let subscriptionID: String - public let coursewareAccess: CoursewareAccess - public let media: DashboardMedia - public let courseImage: String - public let courseAbout: String - public let courseSharingUtmParameters: CourseSharingUtmParameters - public let courseUpdates: String - public let courseHandouts: String - public let discussionURL: String - public let videoOutline: String? - public let isSelfPaced: Bool - - enum CodingKeys: String, CodingKey { - case id - case name - case number - case org - case start - case startDisplay = "start_display" - case startType = "start_type" - case end - case dynamicUpgradeDeadline = "dynamic_upgrade_deadline" - case subscriptionID = "subscription_id" - case coursewareAccess = "courseware_access" - case media - case courseImage = "course_image" - case courseAbout = "course_about" - case courseSharingUtmParameters = "course_sharing_utm_parameters" - case courseUpdates = "course_updates" - case courseHandouts = "course_handouts" - case discussionURL = "discussion_url" - case videoOutline = "video_outline" - case isSelfPaced = "is_self_paced" - } - } - - // MARK: - CourseSharingUtmParameters - struct CourseSharingUtmParameters: Codable { - public let facebook: String - public let twitter: String - } - - // MARK: - CoursewareAccess - struct CoursewareAccess: Codable { - public let hasAccess: Bool - public let errorCode: String? - public let developerMessage: String? - public let userMessage: String? - public let additionalContextUserMessage: String? - public let userFragment: String? - - enum CodingKeys: String, CodingKey { - case hasAccess = "has_access" - case errorCode = "error_code" - case developerMessage = "developer_message" - case userMessage = "user_message" - case additionalContextUserMessage = "additional_context_user_message" - case userFragment = "user_fragment" - } - } - - // MARK: - Media - struct DashboardMedia: Codable { - public let courseImage: CourseImage - - enum CodingKeys: String, CodingKey { - case courseImage = "course_image" - } - } - - // MARK: - CourseImage - struct CourseImage: Codable { - public let url: String - public let name: String - - enum CodingKeys: String, CodingKey { - case url = "uri" - case name = "name" - } - } -} - -public extension DataLayer.Certificate { - var domain: Certificate { - return Certificate(url: url ?? "") - } -} - -public extension DataLayer.MyCourse { - func domain(baseURL: String) -> CourseItem { - let imageURL = baseURL + (course.media.courseImage.url.addingPercentEncoding( - withAllowedCharacters: .urlQueryAllowed) ?? "") - return CourseItem( - name: course.name, - org: course.org, - shortDescription: course.courseAbout, - imageURL: imageURL, - isActive: isActive, - courseStart: course.start != nil ? Date(iso8601: course.start!) : nil, - courseEnd: course.end != nil ? Date(iso8601: course.end!) : nil, - enrollmentStart: nil, - enrollmentEnd: nil, - courseID: course.id, - numPages: 1, - coursesCount: 0 - ) - } -} diff --git a/Course/Course/Data/CourseRepository.swift b/Course/Course/Data/CourseRepository.swift index 39903dc87..e21456a1f 100644 --- a/Course/Course/Data/CourseRepository.swift +++ b/Course/Course/Data/CourseRepository.swift @@ -26,25 +26,30 @@ public protocol CourseRepositoryProtocol { public class CourseRepository: CourseRepositoryProtocol { private let api: API - private let appStorage: CoreStorage + private let coreStorage: CoreStorage private let config: ConfigProtocol private let persistence: CoursePersistenceProtocol - public init(api: API, - appStorage: CoreStorage, - config: ConfigProtocol, - persistence: CoursePersistenceProtocol) { + public init( + api: API, + coreStorage: CoreStorage, + config: ConfigProtocol, + persistence: CoursePersistenceProtocol + ) { self.api = api - self.appStorage = appStorage + self.coreStorage = coreStorage self.config = config self.persistence = persistence } public func getCourseDetails(courseID: String) async throws -> CourseDetails { - let response = try await api.requestData(CourseEndpoint.getCourseDetail(courseID: courseID)) - .mapResponse(DataLayer.CourseDetailsResponse.self) + let response = try await api.requestData( + CourseEndpoint.getCourseDetail(courseID: courseID, username: coreStorage.user?.username ?? "") + ).mapResponse(DataLayer.CourseDetailsResponse.self) .domain(baseURL: config.baseURL.absoluteString) + persistence.saveCourseDetails(course: response) + return response } @@ -54,7 +59,7 @@ public class CourseRepository: CourseRepositoryProtocol { public func getCourseBlocks(courseID: String) async throws -> CourseStructure { let course = try await api.requestData( - CourseEndpoint.getCourseBlocks(courseID: courseID, userName: appStorage.user?.username ?? "") + CourseEndpoint.getCourseBlocks(courseID: courseID, userName: coreStorage.user?.username ?? "") ).mapResponse(DataLayer.CourseStructure.self) persistence.saveCourseStructure(structure: course) let parsedStructure = parseCourseStructure(course: course) @@ -77,7 +82,7 @@ public class CourseRepository: CourseRepositoryProtocol { public func blockCompletionRequest(courseID: String, blockID: String) async throws { try await api.requestData(CourseEndpoint.blockCompletionRequest( - username: appStorage.user?.username ?? "", + username: coreStorage.user?.username ?? "", courseID: courseID, blockID: blockID) ) @@ -96,7 +101,7 @@ public class CourseRepository: CourseRepositoryProtocol { public func resumeBlock(courseID: String) async throws -> ResumeBlock { return try await api.requestData(CourseEndpoint - .resumeBlock(userName: appStorage.user?.username ?? "", courseID: courseID)) + .resumeBlock(userName: coreStorage.user?.username ?? "", courseID: courseID)) .mapResponse(DataLayer.ResumeBlock.self).domain } diff --git a/Course/Course/Data/Network/CourseEndpoint.swift b/Course/Course/Data/Network/CourseEndpoint.swift index 63ef3b4e1..3253501e7 100644 --- a/Course/Course/Data/Network/CourseEndpoint.swift +++ b/Course/Course/Data/Network/CourseEndpoint.swift @@ -10,7 +10,7 @@ import Core import Alamofire enum CourseEndpoint: EndPointType { - case getCourseDetail(courseID: String) + case getCourseDetail(courseID: String, username: String) case getCourseBlocks(courseID: String, userName: String) case pageHTML(pageUrlString: String) case enrollToCourse(courseID: String) @@ -23,11 +23,11 @@ enum CourseEndpoint: EndPointType { var path: String { switch self { - case .getCourseDetail(let courseID): - return "/mobile_api_extensions/v1/courses/\(courseID)" + case .getCourseDetail(let courseID, _): + return "/api/courses/v1/courses/\(courseID)" case .getCourseBlocks: - return "/mobile_api_extensions/v1/blocks/" - case .pageHTML(pageUrlString: let url): + return "/api/mobile/v3/course_info/blocks/" + case .pageHTML(let url): return "/xblock/\(url)" case .enrollToCourse: return "/api/enrollment/v1/enrollment" @@ -35,13 +35,13 @@ enum CourseEndpoint: EndPointType { return "/api/completion/v1/completion-batch" case let .getHandouts(courseID): return "/api/mobile/v1/course_info/\(courseID)/handouts" - case .getUpdates(courseID: let courseID): + case .getUpdates(let courseID): return "/api/mobile/v1/course_info/\(courseID)/updates" case let .resumeBlock(userName, courseID): return "/api/mobile/v1/users/\(userName)/course_status_info/\(courseID)" case let .getSubtitles(url, _): return url - case .getCourseDates(courseID: let courseID): + case .getCourseDates(let courseID): return "/api/course_home/v1/dates/\(courseID)" } } @@ -77,8 +77,9 @@ enum CourseEndpoint: EndPointType { var task: HTTPTask { switch self { - case .getCourseDetail: - return .requestParameters(encoding: URLEncoding.queryString) + case let .getCourseDetail(_, username): + let params: [String: Encodable] = ["username": username] + return .requestParameters(parameters: params, encoding: URLEncoding.queryString) case let .getCourseBlocks(courseID, userName): let params: [String: Encodable] = [ "username": userName, diff --git a/Dashboard/Dashboard/Data/Network/DashboardEndpoint.swift b/Dashboard/Dashboard/Data/Network/DashboardEndpoint.swift index d9e4dec06..1d6845214 100644 --- a/Dashboard/Dashboard/Data/Network/DashboardEndpoint.swift +++ b/Dashboard/Dashboard/Data/Network/DashboardEndpoint.swift @@ -15,7 +15,7 @@ enum DashboardEndpoint: EndPointType { var path: String { switch self { case let .getMyCourses(username, _): - return "/mobile_api_extensions/v1/users/\(username)/course_enrollments" + return "/api/mobile/v3/users/\(username)/course_enrollments" } } diff --git a/Discovery/Discovery/Data/Network/DiscoveryEndpoint.swift b/Discovery/Discovery/Data/Network/DiscoveryEndpoint.swift index 7ce334202..35bfeb384 100644 --- a/Discovery/Discovery/Data/Network/DiscoveryEndpoint.swift +++ b/Discovery/Discovery/Data/Network/DiscoveryEndpoint.swift @@ -18,7 +18,7 @@ enum DiscoveryEndpoint: EndPointType { case .getDiscovery: return "/api/courses/v1/courses/" case .searchCourses: - return "/mobile_api_extensions/courses/v1/courses/" + return "/api/courses/v1/courses/" } } @@ -48,6 +48,7 @@ enum DiscoveryEndpoint: EndPointType { let params: Parameters = [ "username": username, "mobile": true, + "mobile_search": true, "page": page, "search_term": searchTerm ] diff --git a/Discussion/Discussion/Data/Network/DiscussionEndpoint.swift b/Discussion/Discussion/Data/Network/DiscussionEndpoint.swift index 88e8d698f..1cf9ade1e 100644 --- a/Discussion/Discussion/Data/Network/DiscussionEndpoint.swift +++ b/Discussion/Discussion/Data/Network/DiscussionEndpoint.swift @@ -41,7 +41,7 @@ enum DiscussionEndpoint: EndPointType { case let .getCommentResponses(commentID, _): return "/api/discussion/v1/comments/\(commentID)" case .addCommentTo: - return "/mobile_api_extensions/discussion/v1/comments/" + return "/api/discussion/v1/comments/" case let .voteThread(_, threadID): return "/api/discussion/v1/threads/\(threadID)/" case let .voteResponse(_, responseID): diff --git a/OpenEdX/DI/ScreenAssembly.swift b/OpenEdX/DI/ScreenAssembly.swift index 49be31f3d..710764940 100644 --- a/OpenEdX/DI/ScreenAssembly.swift +++ b/OpenEdX/DI/ScreenAssembly.swift @@ -203,7 +203,7 @@ class ScreenAssembly: Assembly { container.register(CourseRepositoryProtocol.self) { r in CourseRepository( api: r.resolve(API.self)!, - appStorage: r.resolve(CoreStorage.self)!, + coreStorage: r.resolve(CoreStorage.self)!, config: r.resolve(ConfigProtocol.self)!, persistence: r.resolve(CoursePersistenceProtocol.self)! ) diff --git a/OpenEdX/Router.swift b/OpenEdX/Router.swift index de07dd027..5e69b64ad 100644 --- a/OpenEdX/Router.swift +++ b/OpenEdX/Router.swift @@ -80,7 +80,6 @@ public class Router: AuthorizationRouter, argument: sourceScreen )! - let controller = UIHostingController(rootView: MainScreenView(viewModel: viewModel)) navigationController.viewControllers = [controller] navigationController.setViewControllers([controller], animated: true) @@ -104,7 +103,12 @@ public class Router: AuthorizationRouter, let controller = UIHostingController(rootView: view) navigationController.setViewControllers([controller], animated: true) } else { - let view = SignInView(viewModel: Container.shared.resolve(SignInViewModel.self)!) + let view = SignInView( + viewModel: Container.shared.resolve( + SignInViewModel.self, + argument: LogistrationSourceScreen.default + )! + ) let controller = UIHostingController(rootView: view) navigationController.setViewControllers([controller], animated: false) } diff --git a/Profile/Profile/Data/Network/ProfileEndpoint.swift b/Profile/Profile/Data/Network/ProfileEndpoint.swift index b7fa6bf19..bf3b330ca 100644 --- a/Profile/Profile/Data/Network/ProfileEndpoint.swift +++ b/Profile/Profile/Data/Network/ProfileEndpoint.swift @@ -30,7 +30,7 @@ enum ProfileEndpoint: EndPointType { case .deleteProfilePicture(username: let username): return "/api/user/v1/accounts/\(username)/image" case .deleteAccount: - return "/mobile_api_extensions/user/v1/accounts/deactivate_logout/" + return "/api/user/v1/accounts/deactivate_logout/" } } diff --git a/ci_scripts/ci_prepare_env.sh b/ci_scripts/ci_prepare_env.sh index a340aabb9..030de4198 100644 --- a/ci_scripts/ci_prepare_env.sh +++ b/ci_scripts/ci_prepare_env.sh @@ -27,6 +27,7 @@ setup_xcode_cloud_environment () { bundle config path vendor/bundle bundle install --jobs 4 --retry 3 + bundle update fastlane } install_xcode_cloud_brew_dependencies () { @@ -39,6 +40,7 @@ setup_github_actions_environment() { bundle config path vendor/bundle bundle install --jobs 4 --retry 3 + bundle update fastlane pod install }