Skip to content
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
04541f7
Make Jetpack tunnel requests in `CouponsRemote` available for REST AP…
selanthiraiyan Dec 23, 2022
6e8efb9
Use JSON encoding for post and put requests.
selanthiraiyan Dec 23, 2022
5b00fce
Remove comment about `fallbackRequest` parameter which no longer exists.
selanthiraiyan Dec 23, 2022
e8189f7
Add String+URL with `trimSlahses` helper method.
selanthiraiyan Dec 27, 2022
fd5156f
Make `AuthenticatedRequest` and `UnauthenticatedRequest` work with `U…
selanthiraiyan Dec 27, 2022
81731b0
`RequestConverter` for converting Jetpack tunnel requests into REST r…
selanthiraiyan Dec 27, 2022
1068172
`RequestProcessor` to authenticate and retry alamofire network requests.
selanthiraiyan Dec 27, 2022
dc94a9a
Stop handling authentication in `RESTRequest` class.
selanthiraiyan Dec 27, 2022
c243504
`RequestAuthenticator` for authenticating network requests.
selanthiraiyan Dec 27, 2022
d1b7b55
Change `AlamofireNetwork` to use request adapter and retrier for auth…
selanthiraiyan Dec 27, 2022
87752cd
Revert the changes made to `CouponsRemote` to make the requests avail…
selanthiraiyan Dec 27, 2022
8431361
Revert "Use JSON encoding for post and put requests."
selanthiraiyan Dec 27, 2022
5e35c3e
Add headers to `users_me` mock json.
selanthiraiyan Dec 27, 2022
cf8a394
Fix the missing comma in json.
selanthiraiyan Dec 27, 2022
0e16a10
Add headers for order related mock json files.
selanthiraiyan Dec 27, 2022
5262ec3
Add headers for UI testing related mock json files.
selanthiraiyan Dec 28, 2022
504fcf6
Fix JSON format error in mock json file.
selanthiraiyan Dec 28, 2022
d0cc935
Remove comment about non-existent parameter `headers`
selanthiraiyan Dec 28, 2022
00c84fb
Update comment to explain forward slash.
selanthiraiyan Dec 28, 2022
4f932ef
Move should retry request check logic to `RequestAuthenticator` and a…
selanthiraiyan Dec 28, 2022
f890c43
Validate only REST requests.
selanthiraiyan Dec 28, 2022
9824d5f
Revert "Fix JSON format error in mock json file."
selanthiraiyan Dec 28, 2022
5b8fb14
Revert "Add headers for UI testing related mock json files."
selanthiraiyan Dec 28, 2022
c157910
Revert "Add headers for order related mock json files."
selanthiraiyan Dec 28, 2022
b77161c
Revert "Fix the missing comma in json."
selanthiraiyan Dec 28, 2022
9dcee6d
Revert "Add headers to `users_me` mock json."
selanthiraiyan Dec 28, 2022
5965160
Move header setting code to `AuthenticatedRequest`.
selanthiraiyan Dec 29, 2022
f75af92
Add comment to explain why only `RESTRequest` needs to call `validate…
selanthiraiyan Dec 29, 2022
7f17011
Fix spacing violation.
selanthiraiyan Dec 29, 2022
f9f0c74
Remove unnecessary `await` call.
selanthiraiyan Dec 29, 2022
ae5b15c
Rename error to explain the scenario.
selanthiraiyan Dec 29, 2022
7433ce6
Trim comment for brevity.
selanthiraiyan Dec 29, 2022
654b529
Add tests to ensure that Jetpack request is converted into REST API r…
selanthiraiyan Dec 29, 2022
3052048
Remove unused `authenticateRequest` method.
selanthiraiyan Dec 29, 2022
f1511f8
Add `RESTRequest` to the documentation.
selanthiraiyan Dec 29, 2022
a3e21d8
Simply let unwrap statement.
selanthiraiyan Dec 29, 2022
e30bfd5
Break `AuthenticatedRequest` into two parts for handling WPCOM token …
selanthiraiyan Dec 29, 2022
9464bd1
Update docs about authenticated requests.
selanthiraiyan Dec 30, 2022
d0b7993
Enable `availableAsRESTRequest` to allow REST request conversion.
selanthiraiyan Dec 30, 2022
7ccf5a4
Remove `RequestConverter` and make `RequestAuthenticatorTests` test o…
selanthiraiyan Dec 30, 2022
c66416d
Use authentication token directly instead of `Credentials` to simplif…
selanthiraiyan Dec 30, 2022
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
28 changes: 24 additions & 4 deletions Networking/Networking.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,7 @@
DEC51AFB2769C66B009F3DF4 /* SystemStatusMapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC51AFA2769C66B009F3DF4 /* SystemStatusMapperTests.swift */; };
DEC51B02276AFB35009F3DF4 /* SystemStatus+DropinMustUsePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC51B01276AFB34009F3DF4 /* SystemStatus+DropinMustUsePlugin.swift */; };
DEFBA74E29485A7600C35BA9 /* RESTRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEFBA74D29485A7600C35BA9 /* RESTRequest.swift */; };
DEFBA7542949CE6600C35BA9 /* RequestAuthenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEFBA7532949CE6600C35BA9 /* RequestAuthenticator.swift */; };
DEFBA7542949CE6600C35BA9 /* RequestProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEFBA7532949CE6600C35BA9 /* RequestProcessor.swift */; };
DEFBA7562949D17400C35BA9 /* RequestAuthenticatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEFBA7552949D17300C35BA9 /* RequestAuthenticatorTests.swift */; };
E12552C526385B05001CEE70 /* ShippingLabelAddressValidationSuccess.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12552C426385B05001CEE70 /* ShippingLabelAddressValidationSuccess.swift */; };
E137619929151C7400FD098F /* error-wp-rest-forbidden.json in Resources */ = {isa = PBXBuildFile; fileRef = E137619829151C7400FD098F /* error-wp-rest-forbidden.json */; };
Expand All @@ -745,13 +745,18 @@
EE338A0E294AF9BD00183934 /* ApplicationPasswordMapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE338A0D294AF9BD00183934 /* ApplicationPasswordMapperTests.swift */; };
EE54C89F2947782E00A9BF61 /* ApplicationPasswordUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE54C89E2947782E00A9BF61 /* ApplicationPasswordUseCase.swift */; };
EE54C8A729486B6800A9BF61 /* ApplicationPasswordMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE54C8A629486B6800A9BF61 /* ApplicationPasswordMapper.swift */; };
EE62EE61295ACF8D009C965B /* RequestConverterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE62EE60295ACF8D009C965B /* RequestConverterTests.swift */; };
EE62EE63295AD45E009C965B /* String+URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE62EE62295AD45E009C965B /* String+URL.swift */; };
EE62EE65295AD46D009C965B /* String+URLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE62EE64295AD46D009C965B /* String+URLTests.swift */; };
EE71CC3D2951A8EA0074D908 /* ApplicationPasswordStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE71CC3C2951A8EA0074D908 /* ApplicationPasswordStorage.swift */; };
EE71CC412951CE700074D908 /* generate-application-password-using-wporg-creds-success.json in Resources */ = {isa = PBXBuildFile; fileRef = EE71CC402951CE700074D908 /* generate-application-password-using-wporg-creds-success.json */; };
EE80A24729547F8B003591E4 /* coupons-all-without-data.json in Resources */ = {isa = PBXBuildFile; fileRef = EE80A24529547F8B003591E4 /* coupons-all-without-data.json */; };
EE80A24829547F8B003591E4 /* coupon-without-data.json in Resources */ = {isa = PBXBuildFile; fileRef = EE80A24629547F8B003591E4 /* coupon-without-data.json */; };
EE80A25029556FBD003591E4 /* coupon-reports-without-data.json in Resources */ = {isa = PBXBuildFile; fileRef = EE80A24F29556FBD003591E4 /* coupon-reports-without-data.json */; };
EE8A86F1286C5226003E8AA4 /* media-update-product-id-in-wordpress-site.json in Resources */ = {isa = PBXBuildFile; fileRef = EE8A86F0286C5226003E8AA4 /* media-update-product-id-in-wordpress-site.json */; };
EE8DE432294B17CD005054E7 /* DefaultApplicationPasswordUseCaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE8DE431294B17CD005054E7 /* DefaultApplicationPasswordUseCaseTests.swift */; };
EE99814E295AA7430074AE68 /* RequestAuthenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE99814D295AA7430074AE68 /* RequestAuthenticator.swift */; };
EE998150295AACE10074AE68 /* RequestConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE99814F295AACE10074AE68 /* RequestConverter.swift */; };
EECB7EE8286555180028C888 /* media-update-product-id.json in Resources */ = {isa = PBXBuildFile; fileRef = EECB7EE7286555180028C888 /* media-update-product-id.json */; };
FE28F6E226840DED004465C7 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE28F6E126840DED004465C7 /* User.swift */; };
FE28F6E426842848004465C7 /* UserMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE28F6E326842848004465C7 /* UserMapper.swift */; };
Expand Down Expand Up @@ -1507,7 +1512,7 @@
DEC51AFA2769C66B009F3DF4 /* SystemStatusMapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemStatusMapperTests.swift; sourceTree = "<group>"; };
DEC51B01276AFB34009F3DF4 /* SystemStatus+DropinMustUsePlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SystemStatus+DropinMustUsePlugin.swift"; sourceTree = "<group>"; };
DEFBA74D29485A7600C35BA9 /* RESTRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RESTRequest.swift; sourceTree = "<group>"; };
DEFBA7532949CE6600C35BA9 /* RequestAuthenticator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestAuthenticator.swift; sourceTree = "<group>"; };
DEFBA7532949CE6600C35BA9 /* RequestProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestProcessor.swift; sourceTree = "<group>"; };
DEFBA7552949D17300C35BA9 /* RequestAuthenticatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestAuthenticatorTests.swift; sourceTree = "<group>"; };
E12552C426385B05001CEE70 /* ShippingLabelAddressValidationSuccess.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShippingLabelAddressValidationSuccess.swift; sourceTree = "<group>"; };
E137619829151C7400FD098F /* error-wp-rest-forbidden.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "error-wp-rest-forbidden.json"; sourceTree = "<group>"; };
Expand All @@ -1528,13 +1533,18 @@
EE338A0D294AF9BD00183934 /* ApplicationPasswordMapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationPasswordMapperTests.swift; sourceTree = "<group>"; };
EE54C89E2947782E00A9BF61 /* ApplicationPasswordUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationPasswordUseCase.swift; sourceTree = "<group>"; };
EE54C8A629486B6800A9BF61 /* ApplicationPasswordMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationPasswordMapper.swift; sourceTree = "<group>"; };
EE62EE60295ACF8D009C965B /* RequestConverterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestConverterTests.swift; sourceTree = "<group>"; };
EE62EE62295AD45E009C965B /* String+URL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+URL.swift"; sourceTree = "<group>"; };
EE62EE64295AD46D009C965B /* String+URLTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+URLTests.swift"; sourceTree = "<group>"; };
EE71CC3C2951A8EA0074D908 /* ApplicationPasswordStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationPasswordStorage.swift; sourceTree = "<group>"; };
EE71CC402951CE700074D908 /* generate-application-password-using-wporg-creds-success.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "generate-application-password-using-wporg-creds-success.json"; sourceTree = "<group>"; };
EE80A24529547F8B003591E4 /* coupons-all-without-data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "coupons-all-without-data.json"; sourceTree = "<group>"; };
EE80A24629547F8B003591E4 /* coupon-without-data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "coupon-without-data.json"; sourceTree = "<group>"; };
EE80A24F29556FBD003591E4 /* coupon-reports-without-data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "coupon-reports-without-data.json"; sourceTree = "<group>"; };
EE8A86F0286C5226003E8AA4 /* media-update-product-id-in-wordpress-site.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "media-update-product-id-in-wordpress-site.json"; sourceTree = "<group>"; };
EE8DE431294B17CD005054E7 /* DefaultApplicationPasswordUseCaseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultApplicationPasswordUseCaseTests.swift; sourceTree = "<group>"; };
EE99814D295AA7430074AE68 /* RequestAuthenticator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestAuthenticator.swift; sourceTree = "<group>"; };
EE99814F295AACE10074AE68 /* RequestConverter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestConverter.swift; sourceTree = "<group>"; };
EECB7EE7286555180028C888 /* media-update-product-id.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "media-update-product-id.json"; sourceTree = "<group>"; };
F3F25DC15EC1D7C631169CB5 /* Pods_Networking.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Networking.framework; sourceTree = BUILT_PRODUCTS_DIR; };
F6CEE1CA2AD376C0C28AE9F6 /* Pods-NetworkingTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NetworkingTests.release.xcconfig"; path = "../Pods/Target Support Files/Pods-NetworkingTests/Pods-NetworkingTests.release.xcconfig"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1746,7 +1756,6 @@
B518662320A099BF00037A38 /* AlamofireNetwork.swift */,
B518662620A09BCC00037A38 /* MockNetwork.swift */,
D87F6150226591E10031A13B /* NullNetwork.swift */,
DEFBA7532949CE6600C35BA9 /* RequestAuthenticator.swift */,
);
path = Network;
sourceTree = "<group>";
Expand Down Expand Up @@ -2438,6 +2447,7 @@
children = (
B57B1E6621C916850046E764 /* NetworkErrorTests.swift */,
DEFBA7552949D17300C35BA9 /* RequestAuthenticatorTests.swift */,
EE62EE60295ACF8D009C965B /* RequestConverterTests.swift */,
);
path = Network;
sourceTree = "<group>";
Expand Down Expand Up @@ -2474,6 +2484,7 @@
57E8FED2246616AC0057CD68 /* Result+Extensions.swift */,
265EFBDB285257950033BD33 /* Order+Fallbacks.swift */,
DE2E8EB0295464C5002E4B14 /* URLRequest+Request.swift */,
EE62EE62295AD45E009C965B /* String+URL.swift */,
);
path = Extensions;
sourceTree = "<group>";
Expand All @@ -2487,6 +2498,7 @@
02BDB83623EA9C4D00BCC63E /* String+HTMLTests.swift */,
0212683424C046CB00F8A892 /* MockNetwork+Path.swift */,
CC851D1325E52AB500249E9C /* Decimal+ExtensionsTests.swift */,
EE62EE64295AD46D009C965B /* String+URLTests.swift */,
);
path = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -2646,8 +2658,11 @@
EE54C899294777D000A9BF61 /* ApplicationPassword */ = {
isa = PBXGroup;
children = (
DEFBA7532949CE6600C35BA9 /* RequestProcessor.swift */,
EE54C89E2947782E00A9BF61 /* ApplicationPasswordUseCase.swift */,
EE71CC3C2951A8EA0074D908 /* ApplicationPasswordStorage.swift */,
EE99814D295AA7430074AE68 /* RequestAuthenticator.swift */,
EE99814F295AACE10074AE68 /* RequestConverter.swift */,
);
path = ApplicationPassword;
sourceTree = "<group>";
Expand Down Expand Up @@ -3160,6 +3175,7 @@
457A574025D1817E000797AD /* ShippingLabelAddressVerification.swift in Sources */,
74ABA1D1213F22CA00FFAD30 /* TopEarnersStatsRemote.swift in Sources */,
DEC51AF127699E7A009F3DF4 /* SystemStatus+Page.swift in Sources */,
EE99814E295AA7430074AE68 /* RequestAuthenticator.swift in Sources */,
025CA2C0238EB8CB00B05C81 /* ProductShippingClass.swift in Sources */,
02C1CEF424C6A02B00703EBA /* ProductVariationMapper.swift in Sources */,
3105470C262E27F000C5C02B /* WCPayPaymentIntentStatusEnum.swift in Sources */,
Expand Down Expand Up @@ -3190,7 +3206,7 @@
020D07B823D852BB00FD9580 /* Media.swift in Sources */,
B5BB1D0C20A2050300112D92 /* DateFormatter+Woo.swift in Sources */,
743E84EE2217244C00FAC9D7 /* ShipmentTrackingListMapper.swift in Sources */,
DEFBA7542949CE6600C35BA9 /* RequestAuthenticator.swift in Sources */,
DEFBA7542949CE6600C35BA9 /* RequestProcessor.swift in Sources */,
451A97E5260B631E0059D135 /* ShippingLabelPredefinedPackage.swift in Sources */,
BAB373722795A1FB00837B4A /* OrderTaxLine.swift in Sources */,
EE54C89F2947782E00A9BF61 /* ApplicationPasswordUseCase.swift in Sources */,
Expand All @@ -3214,6 +3230,7 @@
7452387221124B7700A973CD /* AnyEncodable.swift in Sources */,
740CF89921937A030023ED3A /* CommentRemote.swift in Sources */,
31054702262E04F700C5C02B /* RemotePaymentIntentMapper.swift in Sources */,
EE62EE63295AD45E009C965B /* String+URL.swift in Sources */,
025CA2C2238EBBAA00B05C81 /* ProductShippingClassListMapper.swift in Sources */,
74ABA1CD213F1B6B00FFAD30 /* TopEarnerStats.swift in Sources */,
CCAAD10F2683974000909664 /* ShippingLabelPackagePurchase.swift in Sources */,
Expand Down Expand Up @@ -3364,6 +3381,7 @@
CE583A0E2109154500D73C1C /* OrderNoteMapper.swift in Sources */,
D8FBFF0D22D3AF4A006E3336 /* StatsGranularityV4.swift in Sources */,
261870782540A252006522A1 /* ShippingLineTax.swift in Sources */,
EE998150295AACE10074AE68 /* RequestConverter.swift in Sources */,
74046E1B217A684D007DD7BF /* SiteSettingsRemote.swift in Sources */,
0359EA1D27AADE000048DE2D /* WCPayChargeMapper.swift in Sources */,
B5C6FCCF20A3592900A4F8E4 /* OrderItem.swift in Sources */,
Expand Down Expand Up @@ -3505,6 +3523,8 @@
45150AA026837357006922EA /* CountryListMapperTests.swift in Sources */,
74D5BECE217E0F98007B0348 /* SiteSettingsRemoteTests.swift in Sources */,
D8FBFF1C22D51C34006E3336 /* OrderStatsRemoteV4Tests.swift in Sources */,
EE62EE61295ACF8D009C965B /* RequestConverterTests.swift in Sources */,
EE62EE65295AD46D009C965B /* String+URLTests.swift in Sources */,
CE6D666F2379E82A007835A1 /* ArrayWooTests.swift in Sources */,
DE2E8EAD295418D8002E4B14 /* WordPressSiteRemoteTests.swift in Sources */,
45D685FC23D0C739005F87D0 /* ProductSkuMapperTests.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import enum Alamofire.AFError
enum ApplicationPasswordUseCaseError: Error {
case duplicateName
case applicationPasswordsDisabled
case invalidSiteAddress
case failedToConstructLoginOrAdminURLUsingSiteAddress
}

struct ApplicationPassword {
Expand Down Expand Up @@ -77,7 +77,7 @@ final class DefaultApplicationPasswordUseCase: ApplicationPasswordUseCase {
guard let loginURL = URL(string: siteAddress + Constants.loginPath),
let adminURL = URL(string: siteAddress + Constants.adminPath) else {
DDLogWarn("⚠️ Cannot construct login URL and admin URL for site \(siteAddress)")
throw ApplicationPasswordUseCaseError.invalidSiteAddress
throw ApplicationPasswordUseCaseError.failedToConstructLoginOrAdminURLUsingSiteAddress
}
// Prepares the authenticator with username and password
let authenticator = CookieNonceAuthenticator(username: username,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
enum RequestAuthenticatorError: Error {
case applicationPasswordUseCaseNotAvailable
case applicationPasswordNotAvailable
}

/// Authenticates request
///
public struct RequestAuthenticator {
/// Credentials.
///
let credentials: Credentials?

/// The use case to handle authentication with application passwords.
///
private let applicationPasswordUseCase: ApplicationPasswordUseCase?

/// Sets up the authenticator with optional credentials and application password use case.
/// `applicationPasswordUseCase` can be injected for unit tests.
///
init(credentials: Credentials?, applicationPasswordUseCase: ApplicationPasswordUseCase? = nil) {
self.credentials = credentials
let useCase: ApplicationPasswordUseCase? = {
if let applicationPasswordUseCase {
return applicationPasswordUseCase
} else if case let .wporg(username, password, siteAddress) = credentials {
return try? DefaultApplicationPasswordUseCase(username: username,
password: password,
siteAddress: siteAddress)
Comment on lines +26 to +28
Copy link
Contributor

Choose a reason for hiding this comment

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

Not directly related to this PR: I took a look at what might cause the use case to throw an error, and it looks like it could be from an invalid site address ApplicationPasswordUseCaseError.invalidSiteAddress. Can we validate the site address when the user signs in with site credentials so that the use case can be non-nil here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The DefaultApplicationPasswordUseCase initialization can throw an error when we are not able to construct login/admin URL using the site address, here

I have renamed the error in this commit to explain the scenario better. ae5b15c

Can we validate the site address when the user signs in with site credentials so that the use case can be non-nil here?

Sorry I don't understand this. Could you kindly explain how we can improve it? Thank you!

Copy link
Contributor

Choose a reason for hiding this comment

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

During the login flow where the user signs in with site credentials, do we check if the login and admin URLs are valid? If so, maybe we don't need to check again so that the initialization always succeeds. I assume the API requests here are after the login flow, please correct me if this isn't the case.

Copy link
Contributor Author

@selanthiraiyan selanthiraiyan Dec 29, 2022

Choose a reason for hiding this comment

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

We validate loginURL and adminURL only while sending WordPressOrgNetwork, which currently happens during Jetpack setup here. This validation doesn't happen during site address login step. So we need to validate the loginURL and adminURL here again.

Copy link
Contributor

Choose a reason for hiding this comment

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

does it make sense to validate these two URLs during the login step so that we don't have to handle this error scenario here? If the URLs are invalid from the beginning, the app doesn't seem usable since it can never generate an application password.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

does it make sense to validate these two URLs during the login step so that we don't have to handle this error scenario here?

I agree. Created an issue #8506

} else {
return nil
}
}()
self.applicationPasswordUseCase = useCase
}

func authenticate(_ urlRequest: URLRequest) throws -> URLRequest {
if isRestAPIRequest(urlRequest) {
return try authenticateUsingApplicationPasswordIfPossible(urlRequest)
} else {
return try authenticateUsingWPCOMTokenIfPossible(urlRequest)
}
}

func generateApplicationPassword() async throws {
guard let applicationPasswordUseCase = applicationPasswordUseCase else {
throw RequestAuthenticatorError.applicationPasswordUseCaseNotAvailable
}
let _ = try await applicationPasswordUseCase.generateNewPassword()
return
}

/// Checks whether the given URLRequest is eligible for retyring
///
func shouldRetry(_ urlRequest: URLRequest) -> Bool {
isRestAPIRequest(urlRequest)
}
}

private extension RequestAuthenticator {
/// To check whether the given URLRequest is a REST API request
///
func isRestAPIRequest(_ urlRequest: URLRequest) -> Bool {
guard case let .wporg(_, _, siteAddress) = credentials,
let url = urlRequest.url,
url.absoluteString.hasPrefix(siteAddress.trimSlashes() + "/" + RESTRequest.Settings.basePath) else {
return false
}
return true
}

/// Attempts creating a request with WPCOM token if possible.
///
func authenticateUsingWPCOMTokenIfPossible(_ urlRequest: URLRequest) throws -> URLRequest {
guard case let .wpcom(_, authToken, _) = credentials else {
return UnauthenticatedRequest(request: urlRequest).asURLRequest()
}

return AuthenticatedRequest(authToken: authToken, request: urlRequest).asURLRequest()
}

/// Attempts creating a request with application password if possible.
///
func authenticateUsingApplicationPasswordIfPossible(_ urlRequest: URLRequest) throws -> URLRequest {
guard let applicationPassword = applicationPasswordUseCase?.applicationPassword else {
throw RequestAuthenticatorError.applicationPasswordNotAvailable
}

return AuthenticatedRequest(applicationPassword: applicationPassword, request: urlRequest).asURLRequest()
}
}
17 changes: 17 additions & 0 deletions Networking/Networking/ApplicationPassword/RequestConverter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Alamofire

/// Converter to convert Jetpack tunnel requests into REST API requests if needed
///
struct RequestConverter {
let credentials: Credentials?

func convert(_ request: URLRequestConvertible) -> URLRequestConvertible {
guard let jetpackRequest = request as? JetpackRequest,
Copy link
Contributor

Choose a reason for hiding this comment

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

just wondering, do we only do the conversation from JetpackRequest? How about DotcomRequest and WordPressOrgRequest?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

  1. DotcomRequest are sent to WordPress.com servers, and it needs WPCOM authentication token. We cannot talk to WPCOM servers using the application password.
  2. WordPressOrgRequest will be sent through WordPressOrgNetwork instead of AlamofireNetwork.
    • WordPressOrgNetwork solely handles WordPressOrgRequest, and it doesn't use RequestConverter.

Copy link
Contributor

Choose a reason for hiding this comment

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

TIL, thanks for the explanation. Now I have another question 😅 what are the differences between WordPressOrgRequest and RESTRequest? from the asURLRequest implementation in both structs, the way it sets the URL feels pretty similar 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

WordPressOrgRequest talks to WordPress in general. https://developer.wordpress.org/rest-api/
RESTRequest talks to WooCommerce plugin. https://woocommerce.com/document/woocommerce-rest-api/

Just chiming in to clarify: I believe RESTRequest can be used to handle requests to all WordPress org and Woo requests. The difference between these two types is the way authentication is handled:

  • WordPressOrgRequest uses cookie authentication.
  • RESTRequest uses application password.
    We should probably come up with better naming for these and update the documentation for clearer explanations.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the details, Huong!

RESTRequest has this extra parameter/value let wooApiVersion: WooAPIVersion when compared to WordPressOrgRequest.
I thought the wooApiVersion parameter makes it clear that we should use RESTRequest for the WooCommerce plugin's REST API-related requests. I looked again now and noticed that WooAPIVersion has a case none which is an empty string.

We should probably come up with better naming for these and update the documentation for clearer explanations.

Maybe we could have a single struct instead of two and use the WooAPIVersion.none case. What do you think?

I will create an issue after we finish discussing it here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we could have a single struct instead of two and use the WooAPIVersion.none case. What do you think?

This makes sense to me! A few things I notice:

  • We're using WordPressApiValidator in WordPressOrgRequest but this is not necessary. Since WordPressOrgRequest is required to conform to Request, we can use PlaceholderDataValidator instead.
  • WordPressOrgRequest is not using JSON encoding for POST and PUT requests like RESTRequest does, so unifying them with RESTRequest implementation will fix this too.

Let's use RESTRequest for both then.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Created an issue here #8512

case let .wporg(_, _, siteAddress) = credentials,
let restRequest = jetpackRequest.asRESTRequest(with: siteAddress) else {
return request
}

return restRequest
}
}
Loading