Skip to content

Commit

Permalink
Integerate notification settings endpoint to disable notifications fo…
Browse files Browse the repository at this point in the history
…r hidden stores (#14730)
  • Loading branch information
itsmeichigo authored Dec 23, 2024
2 parents 4432322 + eb3d325 commit 6019502
Show file tree
Hide file tree
Showing 10 changed files with 345 additions and 0 deletions.
8 changes: 8 additions & 0 deletions Networking/Networking.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1053,6 +1053,8 @@
DE78DE4A2B2AEC7F002E58DE /* wp-page-list-success.json in Resources */ = {isa = PBXBuildFile; fileRef = DE78DE492B2AEC7F002E58DE /* wp-page-list-success.json */; };
DE78DE4C2B2AED4C002E58DE /* WordPressPageMapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE78DE4B2B2AED4C002E58DE /* WordPressPageMapperTests.swift */; };
DE78DE4E2B2BF0EA002E58DE /* product-subscription-alternative-types.json in Resources */ = {isa = PBXBuildFile; fileRef = DE78DE4D2B2BF0EA002E58DE /* product-subscription-alternative-types.json */; };
DE8BEF0B2D141DD9008B3A3F /* NotificationSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE8BEF0A2D141DD9008B3A3F /* NotificationSettings.swift */; };
DE8BEF0D2D151396008B3A3F /* notification-settings.json in Resources */ = {isa = PBXBuildFile; fileRef = DE8BEF0C2D151396008B3A3F /* notification-settings.json */; };
DE970D842C23E3F60019EF42 /* product-report-string-stock-quantity.json in Resources */ = {isa = PBXBuildFile; fileRef = DE970D832C23E3F60019EF42 /* product-report-string-stock-quantity.json */; };
DE97C3922861B8E20042E973 /* CouponEncoderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE97C3912861B8E20042E973 /* CouponEncoderTests.swift */; };
DE9D6BCC270D769C00BA6562 /* shipping-label-address-without-name-validation-success.json in Resources */ = {isa = PBXBuildFile; fileRef = DE9D6BCB270D769B00BA6562 /* shipping-label-address-without-name-validation-success.json */; };
Expand Down Expand Up @@ -2239,6 +2241,8 @@
DE78DE492B2AEC7F002E58DE /* wp-page-list-success.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "wp-page-list-success.json"; sourceTree = "<group>"; };
DE78DE4B2B2AED4C002E58DE /* WordPressPageMapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordPressPageMapperTests.swift; sourceTree = "<group>"; };
DE78DE4D2B2BF0EA002E58DE /* product-subscription-alternative-types.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "product-subscription-alternative-types.json"; sourceTree = "<group>"; };
DE8BEF0A2D141DD9008B3A3F /* NotificationSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettings.swift; sourceTree = "<group>"; };
DE8BEF0C2D151396008B3A3F /* notification-settings.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "notification-settings.json"; sourceTree = "<group>"; };
DE970D832C23E3F60019EF42 /* product-report-string-stock-quantity.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "product-report-string-stock-quantity.json"; sourceTree = "<group>"; };
DE97C3912861B8E20042E973 /* CouponEncoderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CouponEncoderTests.swift; sourceTree = "<group>"; };
DE9D6BCB270D769B00BA6562 /* shipping-label-address-without-name-validation-success.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "shipping-label-address-without-name-validation-success.json"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3005,6 +3009,7 @@
B59325CF217E4206000B0E8E /* NoteMedia.swift */,
B59325D1217E4206000B0E8E /* NoteRange.swift */,
B554FA902180BCFC00C54DFF /* NoteHash.swift */,
DE8BEF0A2D141DD9008B3A3F /* NotificationSettings.swift */,
B557DA1C20979E7D005962F4 /* Order.swift */,
741B950020EBC8A700DD6E2D /* OrderCouponLine.swift */,
D88E228F25AC990A0023F3B1 /* OrderFeeLine.swift */,
Expand Down Expand Up @@ -3082,6 +3087,7 @@
B559EBA820A0B5B100836CD4 /* Responses */ = {
isa = PBXGroup;
children = (
DE8BEF0C2D151396008B3A3F /* notification-settings.json */,
EED25B1C2CF74B9800503657 /* media-upload.json */,
EE6C6B6F2C6A190500632BDA /* systemStatus-inconsistent-page-id-data-type.json */,
DEB3878E2C2D71A10025256E /* gla-campaign-list-with-data-envelope.json */,
Expand Down Expand Up @@ -4410,6 +4416,7 @@
743E84F422172D0A00FAC9D7 /* shipment_tracking_multiple.json in Resources */,
DEA493722B3997ED00EED015 /* blaze-target-languages.json in Resources */,
02698CF624C17FC1005337C4 /* product-alternative-types.json in Resources */,
DE8BEF0D2D151396008B3A3F /* notification-settings.json in Resources */,
03EB99962907F03000F06A39 /* empty-data-array.json in Resources */,
CE070A342BBC52B200017578 /* gift-card-stats-without-data.json in Resources */,
57BE08D82409B63800F6DCED /* reviews-missing-avatar-urls.json in Resources */,
Expand Down Expand Up @@ -5324,6 +5331,7 @@
B5C6FCD420A373BB00A4F8E4 /* OrderMapper.swift in Sources */,
CE606D912BE396D7001CB424 /* ShippingMethodsRemote.swift in Sources */,
EE1CB90B2B4BC8C500AD24D5 /* BlazeImpressionsMapper.swift in Sources */,
DE8BEF0B2D141DD9008B3A3F /* NotificationSettings.swift in Sources */,
D88D5A49230BC8C7007B6E01 /* ProductReviewStatus.swift in Sources */,
036563DB2906938600D84BFD /* JustInTimeMessage.swift in Sources */,
CCA1D60429437B2C00B40560 /* SiteSummaryStats.swift in Sources */,
Expand Down
69 changes: 69 additions & 0 deletions Networking/Networking/Model/NotificationSettings.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import Foundation

/// Notification settings for a user
///
public struct NotificationSettings: Equatable, Encodable {

/// Settings for different blogs connected to the user.
public let blogs: [Blog]

/// Convenience init to create notification settings for a given device ID.
///
public init(deviceID: Int64, enabledSites: [Int64], disabledSites: [Int64]) {
let enabledSiteSettings = enabledSites.map { siteID in
Blog(blogID: siteID, devices: [
Device(deviceID: deviceID,
newComment: true,
storeOrder: true)
])
}

let disabledSiteSettings = disabledSites.map { siteID in
Blog(blogID: siteID, devices: [
Device(deviceID: deviceID,
newComment: false,
storeOrder: false)
])
}

self.init(blogs: (enabledSiteSettings + disabledSiteSettings))
}

public init(blogs: [Blog]) {
self.blogs = blogs
}
}

public extension NotificationSettings {
/// Notification settings for a blog
struct Blog: Equatable, Encodable {
/// ID of the blog
public let blogID: Int64

/// List of settings for registered devices
public let devices: [Device]

enum CodingKeys: String, CodingKey {
case blogID = "blog_id"
case devices
}
}

/// Notification settings for a device
struct Device: Equatable, Encodable {
/// Unique ID of the device
public let deviceID: Int64

/// Whether a notification should be sent when there is a new comment on the blog
public let newComment: Bool

/// Whether a notification should be sent when there is a new order on the store.
public let storeOrder: Bool

enum CodingKeys: String, CodingKey {
case deviceID = "device_id"
case newComment = "new_comment"
case storeOrder = "store_order"
}
}
}
11 changes: 11 additions & 0 deletions Networking/Networking/Remote/AccountRemote.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Combine
import Alamofire
import Foundation

/// Protocol for `AccountRemote` mainly used for mocking.
Expand All @@ -15,6 +16,8 @@ public protocol AccountRemoteProtocol {
func loadSitePlan(for siteID: Int64, completion: @escaping (Result<SitePlan, Error>) -> Void)
func loadUsernameSuggestions(from text: String) async throws -> [String]

func updateNotificationSettings(with settings: NotificationSettings) async throws

/// Creates a WPCOM account with the given email and password.
/// - Parameters:
/// - email: user input email.
Expand Down Expand Up @@ -144,6 +147,13 @@ public class AccountRemote: Remote, AccountRemoteProtocol {
return suggestions
}

public func updateNotificationSettings(with settings: NotificationSettings) async throws {
let path = Path.notificationSettings
let parameters = try settings.toDictionary()
let request = DotcomRequest(wordpressApiVersion: .mark1_1, method: .post, path: path, parameters: parameters, encoding: JSONEncoding.default)
return try await enqueue(request)
}

public func createAccount(email: String,
username: String,
password: String,
Expand Down Expand Up @@ -201,6 +211,7 @@ private extension AccountRemote {
static let usernameSuggestions = "users/username/suggestions"
static let accountCreation = "users/new"
static let closeAccount = "me/account/close"
static let notificationSettings = "me/notifications/settings"
}
}

Expand Down
61 changes: 61 additions & 0 deletions Networking/NetworkingTests/Remote/AccountRemoteTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -272,4 +272,65 @@ final class AccountRemoteTests: XCTestCase {
// Then
XCTAssertEqual(expectedError, errorCaught as? NetworkError)
}

// MARK: - Notification settings

func test_updateNotificationSettings_sends_correct_parameters() async throws {
// Given
let remote = AccountRemote(network: network)
network.simulateResponse(requestUrlSuffix: "me/notifications/settings", filename: "notification-settings")

// When
let notificationSettings = NotificationSettings(deviceID: 58089781, enabledSites: [], disabledSites: [194373765])
_ = try await remote.updateNotificationSettings(with: notificationSettings)

// Then
let request = try XCTUnwrap(network.requestsForResponseData.first as? DotcomRequest)

let actualParam = try XCTUnwrap(request.parameters?["blogs"] as? [[String: Any]])
XCTAssertEqual(actualParam.count, 1)
XCTAssertEqual(actualParam.first?["blog_id"] as? Int64, 194373765)

let deviceSettings = try XCTUnwrap(actualParam.first?["devices"] as? [[String: Any]])
XCTAssertEqual(deviceSettings.first?["device_id"] as? Int64, 58089781)
XCTAssertEqual(deviceSettings.first?["new_comment"] as? Bool, false)
XCTAssertEqual(deviceSettings.first?["store_order"] as? Bool, false)
}

func test_updateNotificationSettings_succeeds_on_request_success() async {
// Given
let remote = AccountRemote(network: network)
network.simulateResponse(requestUrlSuffix: "me/notifications/settings", filename: "notification-settings")

// When
var errorCaught: Error?
do {
let notificationSettings = NotificationSettings(deviceID: 58089781, enabledSites: [194373765], disabledSites: [])
try await remote.updateNotificationSettings(with: notificationSettings)
} catch {
errorCaught = error
}

// Then
XCTAssertNil(errorCaught)
}

func test_updateNotificationSettings_relays_error_on_request_failure() async {
// Given
let remote = AccountRemote(network: network)
let expectedError = NetworkError.timeout()
network.simulateError(requestUrlSuffix: "me/notifications/settings", error: expectedError)

// When
var errorCaught: Error?
do {
let notificationSettings = NotificationSettings(deviceID: 58089781, enabledSites: [194373765], disabledSites: [])
try await remote.updateNotificationSettings(with: notificationSettings)
} catch {
errorCaught = error
}

// Then
XCTAssertEqual(expectedError, errorCaught as? NetworkError)
}
}
114 changes: 114 additions & 0 deletions Networking/NetworkingTests/Responses/notification-settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
{
"blogs": [
{
"blog_id": 190864441,
"timeline": {
"new_comment": true,
"comment_like": true,
"post_like": true,
"follow": true,
"achievement": true,
"mentions": true,
"scheduled_publicize": true,
"store_order": true,
"blogging_prompt": false,
"draft_post_prompt": true
},
"email": {
"new_comment": true,
"comment_like": true,
"post_like": true,
"follow": true,
"achievement": true,
"mentions": true,
"scheduled_publicize": true,
"store_order": true,
"blogging_prompt": false,
"draft_post_prompt": true
},
"devices": [
{
"device_id": 58089781,
"new_comment": true,
"comment_like": true,
"post_like": true,
"follow": true,
"achievement": true,
"mentions": true,
"scheduled_publicize": true,
"store_order": true,
"blogging_prompt": false,
"draft_post_prompt": true
}
]
},
{
"blog_id": 194373765,
"timeline": {
"new_comment": true,
"comment_like": true,
"post_like": true,
"follow": true,
"achievement": true,
"mentions": true,
"scheduled_publicize": true,
"store_order": true,
"blogging_prompt": false,
"draft_post_prompt": true
},
"email": {
"new_comment": true,
"comment_like": true,
"post_like": true,
"follow": true,
"achievement": true,
"mentions": true,
"scheduled_publicize": true,
"store_order": true,
"blogging_prompt": false,
"draft_post_prompt": true
},
"devices": [
{
"device_id": 58089781,
"new_comment": false,
"comment_like": true,
"post_like": true,
"follow": true,
"achievement": true,
"mentions": true,
"scheduled_publicize": true,
"store_order": true,
"blogging_prompt": false,
"draft_post_prompt": true
}
]
},
]
},
"wpcom": {
"marketing": false,
"research": true,
"affiliates": true,
"community": true,
"promotion": false,
"news": true,
"digest": true,
"reports": true,
"news_developer": true,
"wpcom_spain": false,
"scheduled_updates": true,
"learn": false,
"a4a_agencies": true,
"jetpack_agencies": true,
"jetpack_manage_onboarding": true,
"jetpack_marketing": false,
"jetpack_research": true,
"jetpack_promotion": false,
"jetpack_news": true,
"jetpack_reports": true,
"akismet_marketing": false,
"woopay_marketing": true,
"gravatar_onboarding": true
}
}
1 change: 1 addition & 0 deletions Yosemite/Yosemite/Actions/AccountAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ public enum AccountAction: Action {
case synchronizeSitesAndReturnSelectedSiteInfo(siteAddress: String, onCompletion: (Result<Site, Error>) -> Void)
case synchronizeSitePlan(siteID: Int64, onCompletion: (Result<Void, Error>) -> Void)
case updateAccountSettings(userID: Int64, tracksOptOut: Bool, onCompletion: (Result<Void, Error>) -> Void)
case updateNotificationSettings(notificationSettings: NotificationSettings, onCompletion: (Result<Void, Error>) -> Void)
case closeAccount(onCompletion: (Result<Void, Error>) -> Void)
}
1 change: 1 addition & 0 deletions Yosemite/Yosemite/Model/Model.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public typealias Note = Networking.Note
public typealias NoteBlock = Networking.NoteBlock
public typealias NoteMedia = Networking.NoteMedia
public typealias NoteRange = Networking.NoteRange
public typealias NotificationSettings = Networking.NotificationSettings
public typealias Order = Networking.Order
public typealias OrderItem = Networking.OrderItem
public typealias OrderItemAttribute = Networking.OrderItemAttribute
Expand Down
13 changes: 13 additions & 0 deletions Yosemite/Yosemite/Stores/AccountStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ public class AccountStore: Store {
synchronizeSitePlan(siteID: siteID, onCompletion: onCompletion)
case .updateAccountSettings(let userID, let tracksOptOut, let onCompletion):
updateAccountSettings(userID: userID, tracksOptOut: tracksOptOut, onCompletion: onCompletion)
case .updateNotificationSettings(let notificationSettings, let onCompletion):
updateNotificationSettings(notificationSettings: notificationSettings, onCompletion: onCompletion)
case .closeAccount(let onCompletion):
closeAccount(onCompletion: onCompletion)
}
Expand Down Expand Up @@ -223,6 +225,17 @@ private extension AccountStore {
}
}

func updateNotificationSettings(notificationSettings: NotificationSettings, onCompletion: @escaping (Result<Void, Error>) -> Void) {
Task {
do {
try await remote.updateNotificationSettings(with: notificationSettings)
onCompletion(.success(()))
} catch {
onCompletion(.failure(error))
}
}
}

func closeAccount(onCompletion: @escaping (Result<Void, Error>) -> Void) {
Task {
do {
Expand Down
Loading

0 comments on commit 6019502

Please sign in to comment.