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

[REST API] Initial support for WPCom account creation during Jetpack setup #14431

Merged
merged 11 commits into from
Nov 21, 2024
Merged
2 changes: 2 additions & 0 deletions Experiments/Experiments/DefaultFeatureFlagService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ public struct DefaultFeatureFlagService: FeatureFlagService {
return false
case .sendReceiptsForPointOfSale:
return false
case .jetpackSetupWPComAccountCreation:
return buildConfig == .localDeveloper || buildConfig == .alpha
default:
return true
}
Expand Down
4 changes: 4 additions & 0 deletions Experiments/Experiments/FeatureFlag.swift
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,8 @@ public enum FeatureFlag: Int {
/// Adds support for sending receipts after the payment for POS
///
case sendReceiptsForPointOfSale

/// Enables WPCom account creation during Jetpack setup
///
case jetpackSetupWPComAccountCreation
}
2 changes: 1 addition & 1 deletion Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ target 'WooCommerce' do
# To allow pod to pick up beta versions use -beta. E.g., 1.1.7-beta.1
# pod 'WordPressAuthenticator', '~> 9.10.0'
# pod 'WordPressAuthenticator', git: 'https://github.com/wordpress-mobile/WordPressAuthenticator-iOS.git', branch: ''
pod 'WordPressAuthenticator', git: 'https://github.com/wordpress-mobile/WordPressAuthenticator-iOS.git', commit: 'f591b6e0442f9f153ad3842efdb8c81d26235c44'
pod 'WordPressAuthenticator', git: 'https://github.com/wordpress-mobile/WordPressAuthenticator-iOS.git', commit: 'cd53ee3d6209619be6f7bb61f1707fbebf77798c'
# pod 'WordPressAuthenticator', path: '../WordPressAuthenticator-iOS'

wordpress_shared
Expand Down
19 changes: 9 additions & 10 deletions Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ PODS:
- Gridicons (~> 1.0)
- "NSURL+IDN (= 0.4)"
- SVProgressHUD (~> 2.2.5)
- WordPressKit (~> 17.0)
- WordPressKit (~> 17.3)
- WordPressShared (~> 2.1-beta)
- WordPressUI (~> 1.7-beta)
- WordPressKit (17.2.0):
- WordPressKit (17.3.0):
- NSObject-SafeExpectations (~> 0.0.4)
- UIDeviceIdentifier (~> 2.0)
- WordPressShared (~> 2.0-beta)
Expand Down Expand Up @@ -67,16 +67,14 @@ DEPENDENCIES:
- StripeTerminal (~> 3.9.1)
- SwiftLint (= 0.54.0)
- WordPress-Editor-iOS (~> 1.19)
- WordPressAuthenticator (from `https://github.com/wordpress-mobile/WordPressAuthenticator-iOS.git`, commit `f591b6e0442f9f153ad3842efdb8c81d26235c44`)
- WordPressAuthenticator (from `https://github.com/wordpress-mobile/WordPressAuthenticator-iOS.git`, commit `cd53ee3d6209619be6f7bb61f1707fbebf77798c`)
- WordPressShared (~> 2.1)
- WordPressUI (~> 1.15)
- Wormholy (~> 1.6.6)
- WPMediaPicker (~> 1.8)
- ZendeskSupportSDK (~> 9.0.0)

SPEC REPOS:
https://github.com/wordpress-mobile/cocoapods-specs.git:
- WordPressKit
trunk:
- Alamofire
- Automattic-Tracks-iOS
Expand All @@ -95,6 +93,7 @@ SPEC REPOS:
- UIDeviceIdentifier
- WordPress-Aztec-iOS
- WordPress-Editor-iOS
- WordPressKit
- WordPressShared
- WordPressUI
- Wormholy
Expand All @@ -110,12 +109,12 @@ SPEC REPOS:

EXTERNAL SOURCES:
WordPressAuthenticator:
:commit: f591b6e0442f9f153ad3842efdb8c81d26235c44
:commit: cd53ee3d6209619be6f7bb61f1707fbebf77798c
:git: https://github.com/wordpress-mobile/WordPressAuthenticator-iOS.git

CHECKOUT OPTIONS:
WordPressAuthenticator:
:commit: f591b6e0442f9f153ad3842efdb8c81d26235c44
:commit: cd53ee3d6209619be6f7bb61f1707fbebf77798c
:git: https://github.com/wordpress-mobile/WordPressAuthenticator-iOS.git

SPEC CHECKSUMS:
Expand All @@ -136,8 +135,8 @@ SPEC CHECKSUMS:
UIDeviceIdentifier: 442b65b4ff1832d4ca9c2a157815cb29ad981b17
WordPress-Aztec-iOS: 8eaa928fb3a5694924ed3befac64beaae5656e12
WordPress-Editor-iOS: 98ce1fc542c3a09e48ddc9423405b1d1e48240f1
WordPressAuthenticator: d151cc7ebc1cfcbf5c28bb2c9afe760a75b49ba6
WordPressKit: de44094b3be8998504a3a57700bc3e96e3b46f57
WordPressAuthenticator: 0f4f47d6f71cfa763cb636876d69170a3c710002
WordPressKit: faf8c6de7c2acfe71cf95b4db896901060967089
WordPressShared: 0aa459e5257a77184db87805a998f447443c9706
WordPressUI: 700e3ec5a9f77b6920c8104c338c85788036ab3c
Wormholy: 09da0b876f9276031fd47383627cb75e194fc068
Expand All @@ -151,6 +150,6 @@ SPEC CHECKSUMS:
ZendeskSupportProvidersSDK: 281acf2bb731d2a67f913cfe653ed0da9f5b2f42
ZendeskSupportSDK: b512cfc74b6bf8490e589f02cf52e27ed4f2bebe

PODFILE CHECKSUM: 51f5cabba416d490c8f90395fa0a86fde13dd931
PODFILE CHECKSUM: f02fa3bd6b4c798923cccc4fbe03de910e60891b

COCOAPODS: 1.16.1
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ struct WPComEmailLoginView_Previews: PreviewProvider {
static var previews: some View {
WPComEmailLoginView(viewModel: .init(siteURL: "https://example.com",
requiresConnectionOnly: true,
allowAccountCreation: false,
onPasswordUIRequest: { _ in },
onMagicLinkUIRequest: { _ in },
onError: { _ in }))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@ import Combine
import UIKit
import WordPressAuthenticator
import protocol WooFoundation.Analytics
import enum WordPressKit.WordPressAPIError
import struct WordPressKit.WordPressComRestApiEndpointError

/// A protocol used to mock `WordPressComAccountService` for unit tests.
protocol WordPressComAccountServiceProtocol {
func isPasswordlessAccount(username: String, success: @escaping (Bool) -> Void, failure: @escaping (Error) -> Void)
func requestAuthenticationLink(for email: String, jetpackLogin: Bool, success: @escaping () -> Void, failure: @escaping (Error) -> Void)
func requestAuthenticationLink(for email: String,
jetpackLogin: Bool,
createAccountIfNotFound: Bool,
success: @escaping () -> Void,
failure: @escaping (Error) -> Void)
}

/// Conformance
Expand All @@ -21,6 +27,7 @@ final class WPComEmailLoginViewModel: ObservableObject {

let termsAttributedString: NSAttributedString

private let allowAccountCreation: Bool
private let accountService: WordPressComAccountServiceProtocol
private let analytics: Analytics
private let onPasswordUIRequest: (String) -> Void
Expand All @@ -31,12 +38,14 @@ final class WPComEmailLoginViewModel: ObservableObject {

init(siteURL: String,
requiresConnectionOnly: Bool,
allowAccountCreation: Bool,
debounceDuration: Double = Constants.fieldDebounceDuration,
accountService: WordPressComAccountServiceProtocol = WordPressComAccountService(),
analytics: Analytics = ServiceLocator.analytics,
onPasswordUIRequest: @escaping (String) -> Void,
onMagicLinkUIRequest: @escaping (String) -> Void,
onError: @escaping (String) -> Void) {
self.allowAccountCreation = allowAccountCreation
self.analytics = analytics
self.accountService = accountService
self.onPasswordUIRequest = onPasswordUIRequest
Expand Down Expand Up @@ -78,8 +87,16 @@ final class WPComEmailLoginViewModel: ObservableObject {
}
await startAuthentication(email: email, isPasswordlessAccount: passwordless)
} catch {
analytics.track(event: .JetpackSetup.loginFlow(step: .emailAddress, failure: error))
onError(error.localizedDescription)
guard allowAccountCreation,
let apiError = error as? WordPressAPIError<WordPressComRestApiEndpointError>,
case .endpointError(let endpointError) = apiError,
endpointError.apiErrorCode == Constants.unknownUserErrorCode else {
analytics.track(event: .JetpackSetup.loginFlow(step: .emailAddress, failure: error))
onError(error.localizedDescription)
return
}

await requestAuthenticationLink(email: email, forAccountCreation: true)
}
}

Expand All @@ -93,10 +110,13 @@ final class WPComEmailLoginViewModel: ObservableObject {
}

@MainActor
func requestAuthenticationLink(email: String) async {
func requestAuthenticationLink(email: String, forAccountCreation: Bool = false) async {
do {
try await withCheckedThrowingContinuation { continuation in
accountService.requestAuthenticationLink(for: email, jetpackLogin: false, success: {
accountService.requestAuthenticationLink(for: email,
jetpackLogin: false,
createAccountIfNotFound: forAccountCreation,
success: {
continuation.resume()
}, failure: { error in
continuation.resume(throwing: error)
Expand All @@ -116,6 +136,7 @@ extension WPComEmailLoginViewModel {
static let jetpackTermsURL = "https://jetpack.com/redirect/?source=wpcom-tos&site="
static let jetpackShareDetailsURL = "https://jetpack.com/redirect/?source=jetpack-support-what-data-does-jetpack-sync&site="
static let wpcomErrorCodeKey = "WordPressComRestApiErrorCodeKey"
static let unknownUserErrorCode = "unknown_user"
}

enum Localization {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,10 @@ private extension WPComLoginCoordinator {
@MainActor
func requestAuthenticationLink(email: String) async throws {
try await withCheckedThrowingContinuation { continuation in
accountService.requestAuthenticationLink(for: email, jetpackLogin: false, success: {
accountService.requestAuthenticationLink(for: email,
jetpackLogin: false,
createAccountIfNotFound: false,
success: {
continuation.resume()
}, failure: { error in
continuation.resume(throwing: error)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import UIKit
import Experiments
import Yosemite
import enum Networking.NetworkError
import class Networking.AlamofireNetwork
Expand All @@ -16,6 +17,7 @@ final class JetpackSetupCoordinator {
private var jetpackConnectedEmail: String?
private let stores: StoresManager
private let analytics: Analytics
private let featureFlagService: FeatureFlagService
private let dotcomAuthScheme: String

private var loginNavigationController: LoginNavigationController?
Expand All @@ -24,6 +26,7 @@ final class JetpackSetupCoordinator {
private lazy var emailLoginViewModel: WPComEmailLoginViewModel = {
.init(siteURL: site.url,
requiresConnectionOnly: requiresConnectionOnly,
allowAccountCreation: featureFlagService.isFeatureFlagEnabled(.jetpackSetupWPComAccountCreation),
onPasswordUIRequest: showPasswordUI(email:),
onMagicLinkUIRequest: showMagicLinkUI(email:),
onError: { [weak self] message in
Expand All @@ -40,13 +43,15 @@ final class JetpackSetupCoordinator {
dotcomAuthScheme: String = ApiCredentials.dotcomAuthScheme,
rootViewController: UIViewController,
stores: StoresManager = ServiceLocator.stores,
analytics: Analytics = ServiceLocator.analytics) {
analytics: Analytics = ServiceLocator.analytics,
featureFlagService: FeatureFlagService = ServiceLocator.featureFlagService) {
self.site = site
self.dotcomAuthScheme = dotcomAuthScheme
self.requiresConnectionOnly = false // to be updated later after fetching Jetpack status
self.rootViewController = rootViewController
self.stores = stores
self.analytics = analytics
self.featureFlagService = featureFlagService

/// the authenticator needs to be initialized with configs
/// to be used for requesting authentication link and handle login later.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ final class MockWordPressComAccountService: WordPressComAccountServiceProtocol {
failure(passwordlessAccountCheckError)
}

func requestAuthenticationLink(for email: String, jetpackLogin: Bool, success: @escaping () -> Void, failure: @escaping (Error) -> Void) {
func requestAuthenticationLink(for email: String,
jetpackLogin: Bool,
createAccountIfNotFound: Bool,
success: @escaping () -> Void,
failure: @escaping (Error) -> Void) {
triggeredRequestAuthenticationLink = true
guard let authenticationLinkRequestError else {
return success()
Expand Down
Loading