Skip to content

Commit 2c21e64

Browse files
authored
Merge pull request #6022 from woocommerce/issue/5951-get-inbox-notes
Fetch Inbox Notes from `GET /wc-analytics/admin/notes` - Networking layer
2 parents 7bdefa5 + 9b0c446 commit 2c21e64

File tree

3 files changed

+233
-0
lines changed

3 files changed

+233
-0
lines changed

Networking/Networking.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,8 @@
217217
4501068F2399B19500E24722 /* TaxClassRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4501068E2399B19500E24722 /* TaxClassRemote.swift */; };
218218
450106912399B2C800E24722 /* TaxClassListMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450106902399B2C800E24722 /* TaxClassListMapper.swift */; };
219219
451274A625276C82009911FF /* product-variation.json in Resources */ = {isa = PBXBuildFile; fileRef = 451274A525276C82009911FF /* product-variation.json */; };
220+
4513382027A8227F00AE5E78 /* InboxNotesRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4513381F27A8227F00AE5E78 /* InboxNotesRemote.swift */; };
221+
4513382227A8409000AE5E78 /* InboxNotesRemoteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4513382127A8409000AE5E78 /* InboxNotesRemoteTests.swift */; };
220222
45150A9A268340D2006922EA /* Country.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45150A99268340D2006922EA /* Country.swift */; };
221223
45150A9C2683417A006922EA /* StateOfACountry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45150A9B2683417A006922EA /* StateOfACountry.swift */; };
222224
45150A9E26836A57006922EA /* CountryListMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45150A9D26836A57006922EA /* CountryListMapper.swift */; };
@@ -869,6 +871,8 @@
869871
4501068E2399B19500E24722 /* TaxClassRemote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaxClassRemote.swift; sourceTree = "<group>"; };
870872
450106902399B2C800E24722 /* TaxClassListMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaxClassListMapper.swift; sourceTree = "<group>"; };
871873
451274A525276C82009911FF /* product-variation.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "product-variation.json"; sourceTree = "<group>"; };
874+
4513381F27A8227F00AE5E78 /* InboxNotesRemote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InboxNotesRemote.swift; sourceTree = "<group>"; };
875+
4513382127A8409000AE5E78 /* InboxNotesRemoteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InboxNotesRemoteTests.swift; sourceTree = "<group>"; };
872876
45150A99268340D2006922EA /* Country.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Country.swift; sourceTree = "<group>"; };
873877
45150A9B2683417A006922EA /* StateOfACountry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateOfACountry.swift; sourceTree = "<group>"; };
874878
45150A9D26836A57006922EA /* CountryListMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountryListMapper.swift; sourceTree = "<group>"; };
@@ -1497,6 +1501,7 @@
14971501
45E461BD26837DB900011BF2 /* DataRemoteTests.swift */,
14981502
B524194821AC659500D6FC0A /* DevicesRemoteTests.swift */,
14991503
24F98C5F2502EF8200F49B68 /* FeatureFlagRemoteTests.swift */,
1504+
4513382127A8409000AE5E78 /* InboxNotesRemoteTests.swift */,
15001505
26B2F74824C55ACE0065CCC8 /* LeaderboardsRemoteTests.swift */,
15011506
020D07BF23D8587700FD9580 /* MediaRemoteTests.swift */,
15021507
B554FA8A2180B1D500C54DFF /* NotificationsRemoteTests.swift */,
@@ -1624,6 +1629,7 @@
16241629
45E461BB26837CC500011BF2 /* DataRemote.swift */,
16251630
B572F69921AC475C003EEFF0 /* DevicesRemote.swift */,
16261631
24F98C512502E79800F49B68 /* FeatureFlagsRemote.swift */,
1632+
4513381F27A8227F00AE5E78 /* InboxNotesRemote.swift */,
16271633
26B2F74024C1F2C10065CCC8 /* LeaderboardsRemote.swift */,
16281634
B5DAEFEF2180DD5A0002356A /* NotificationsRemote.swift */,
16291635
B557DA0120975500005962F4 /* OrdersRemote.swift */,
@@ -2801,6 +2807,7 @@
28012807
7426CA0F21AF2C90004E9FFC /* SiteAPI.swift in Sources */,
28022808
D87F6151226591E10031A13B /* NullNetwork.swift in Sources */,
28032809
CE43A8F9229F463000A4FF29 /* ProductDownload.swift in Sources */,
2810+
4513382027A8227F00AE5E78 /* InboxNotesRemote.swift in Sources */,
28042811
B53EF5322180F21C003E146F /* Dictionary+Woo.swift in Sources */,
28052812
24F98C522502E79800F49B68 /* FeatureFlagsRemote.swift in Sources */,
28062813
74A1D26D21189DFF00931DFA /* SiteVisitStatsMapper.swift in Sources */,
@@ -2943,6 +2950,7 @@
29432950
74A7B4BC217A807400E85A8B /* SiteSettingsMapperTests.swift in Sources */,
29442951
311976E02602BD4B006AC56C /* SitePluginsMapperTests.swift in Sources */,
29452952
2661547B242DAC1C00A31661 /* ProductCategoriesRemoteTests.swift in Sources */,
2953+
4513382227A8409000AE5E78 /* InboxNotesRemoteTests.swift in Sources */,
29462954
45E461BE26837DB900011BF2 /* DataRemoteTests.swift in Sources */,
29472955
CEC4BF8F234E382F008D9195 /* RefundMapperTests.swift in Sources */,
29482956
24F98C5E2502EDCF00F49B68 /* BundleWooTests.swift in Sources */,
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import Foundation
2+
3+
/// Protocol for `InboxNotesRemote` mainly used for mocking.
4+
///
5+
/// The required methods are intentionally incomplete. Feel free to add the other ones.
6+
///
7+
public protocol InboxNotesRemoteProtocol {
8+
func loadAllInboxNotes(for siteID: Int64,
9+
pageNumber: Int,
10+
pageSize: Int,
11+
orderBy: InboxNotesRemote.OrderBy,
12+
type: [InboxNotesRemote.NoteType]?,
13+
status: [InboxNotesRemote.Status]?,
14+
completion: @escaping (Result<[InboxNote], Error>) -> ())
15+
}
16+
17+
18+
/// Inbox Notes: Remote endpoints
19+
///
20+
public final class InboxNotesRemote: Remote, InboxNotesRemoteProtocol {
21+
22+
// MARK: - Get Inbox Notes
23+
24+
/// Retrieves all of the `InboxNote`s from the API.
25+
///
26+
/// - Parameters:
27+
/// - siteID: The site for which we'll fetch InboxNotes.
28+
/// - pageNumber: The page number of the Inbox Notes list to be fetched.
29+
/// - pageSize: The maximum number of Inbox Notes to be fetched for the current page.
30+
/// - orderBy: The type of sorting that the Inbox Notes list will follow.
31+
/// - type: The array of Inbox Notes Types.
32+
/// - status: The array of Inbox Notes with a specific array of status that will be fetched.
33+
/// - completion: Closure to be executed upon completion.
34+
///
35+
public func loadAllInboxNotes(for siteID: Int64,
36+
pageNumber: Int = Default.pageNumber,
37+
pageSize: Int = Default.pageSize,
38+
orderBy: InboxNotesRemote.OrderBy = .date,
39+
type: [InboxNotesRemote.NoteType]? = nil,
40+
status: [InboxNotesRemote.Status]? = nil,
41+
completion: @escaping (Result<[InboxNote], Error>) -> ()) {
42+
var parameters = [
43+
ParameterKey.orderBy: orderBy.rawValue,
44+
ParameterKey.page: pageNumber,
45+
ParameterKey.pageSize: pageSize
46+
] as [String: Any]
47+
48+
if let type = type {
49+
let stringOfTypes = type.map { $0.rawValue }
50+
parameters[ParameterKey.type] = stringOfTypes.joined(separator: ",")
51+
}
52+
if let status = status {
53+
let stringOfStatuses = status.map { $0.rawValue }
54+
parameters[ParameterKey.status] = stringOfStatuses.joined(separator: ",")
55+
}
56+
57+
let request = JetpackRequest(wooApiVersion: .wcAnalytics,
58+
method: .get,
59+
siteID: siteID,
60+
path: Path.notes,
61+
parameters: parameters)
62+
63+
let mapper = InboxNoteListMapper(siteID: siteID)
64+
65+
enqueue(request, mapper: mapper, completion: completion)
66+
}
67+
}
68+
69+
// MARK: - Constants
70+
//
71+
public extension InboxNotesRemote {
72+
73+
enum Default {
74+
public static let pageSize = 25
75+
public static let pageNumber = 1
76+
}
77+
78+
private enum Path {
79+
static let notes = "admin/notes"
80+
static let deleteNote = "admin/notes/delete"
81+
}
82+
83+
private enum ParameterKey {
84+
static let orderBy = "order_by"
85+
static let page = "page"
86+
static let pageSize = "per_page"
87+
static let type = "type"
88+
static let status = "status"
89+
}
90+
91+
/// Order By parameter
92+
///
93+
enum OrderBy: String {
94+
case noteID = "note_id"
95+
case date = "date"
96+
case type = "type"
97+
case title = "title"
98+
case status = "status"
99+
}
100+
101+
/// Type parameter
102+
///
103+
enum NoteType: String {
104+
case info = "info"
105+
case marketing = "marketing"
106+
case survey = "survey"
107+
case update = "update"
108+
case error = "error"
109+
case warning = "warning"
110+
case email = "email"
111+
}
112+
113+
/// Status parameter
114+
///
115+
enum Status: String {
116+
case pending = "pending"
117+
case unactioned = "unactioned"
118+
case actioned = "actioned"
119+
case snoozed = "snoozed"
120+
case sent = "sent"
121+
}
122+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import XCTest
2+
@testable import Networking
3+
4+
final class InboxNotesRemoteTests: XCTestCase {
5+
6+
/// Dummy Network Wrapper
7+
///
8+
private var network: MockNetwork!
9+
10+
/// Dummy Site ID
11+
///
12+
private let sampleSiteID: Int64 = 1234
13+
14+
override func setUp() {
15+
super.setUp()
16+
network = MockNetwork()
17+
}
18+
19+
override func tearDown() {
20+
network = nil
21+
super.tearDown()
22+
}
23+
24+
// MARK: - Load all Inbox Notes tests
25+
26+
/// Verifies that loadAllInboxNotes properly parses the `inbox-note-list` sample response.
27+
///
28+
func test_loadAllInboxNotes_returns_parsed_inbox_notes() throws {
29+
// Given
30+
let remote = InboxNotesRemote(network: network)
31+
32+
network.simulateResponse(requestUrlSuffix: "admin/notes", filename: "inbox-note-list")
33+
34+
// When
35+
let result = waitFor { promise in
36+
remote.loadAllInboxNotes(for: self.sampleSiteID, pageNumber: 0, pageSize: 25) { result in
37+
promise(result)
38+
}
39+
}
40+
41+
// Then
42+
XCTAssert(result.isSuccess)
43+
let inboxNotes = try XCTUnwrap(result.get())
44+
XCTAssertEqual(inboxNotes.count, 24)
45+
}
46+
47+
/// Verifies that loadAllInboxNotes uses the SiteID passed in for the request.
48+
///
49+
func test_loadAllInboxNotes_uses_passed_siteID_for_request() throws {
50+
// Given
51+
let remote = InboxNotesRemote(network: network)
52+
53+
// When
54+
remote.loadAllInboxNotes(for: sampleSiteID) { _ in }
55+
56+
// Then
57+
let request = try XCTUnwrap(network.requestsForResponseData.first as? JetpackRequest)
58+
XCTAssertEqual(request.siteID, sampleSiteID)
59+
}
60+
61+
/// Verifies that loadAllInboxNotes uses the SiteID passed in to build the models.
62+
///
63+
func test_loadAllInboxNotes_uses_passed_siteID_for_model_creation() throws {
64+
// Given
65+
let remote = InboxNotesRemote(network: network)
66+
67+
network.simulateResponse(requestUrlSuffix: "admin/notes", filename: "inbox-note-list")
68+
69+
// When
70+
let result = waitFor { promise in
71+
remote.loadAllInboxNotes(for: self.sampleSiteID) { result in
72+
promise(result)
73+
}
74+
}
75+
76+
// Then
77+
let inboxNotes = try result.get()
78+
XCTAssertEqual(inboxNotes.first?.siteID, sampleSiteID)
79+
}
80+
81+
/// Verifies that loadAllInboxNotes properly relays Networking Layer errors.
82+
///
83+
func test_loadAllInboxNotes_properly_relays_networking_errors() throws {
84+
// Given
85+
let remote = InboxNotesRemote(network: network)
86+
87+
let error = NetworkError.unacceptableStatusCode(statusCode: 403)
88+
network.simulateError(requestUrlSuffix: "admin/notes", error: error)
89+
90+
// When
91+
let result = waitFor { promise in
92+
remote.loadAllInboxNotes(for: self.sampleSiteID,
93+
completion: { (result) in
94+
promise(result)
95+
})
96+
}
97+
98+
// Then
99+
XCTAssertTrue(result.isFailure)
100+
let resultError = try XCTUnwrap(result.failure as? NetworkError)
101+
XCTAssertNotNil(resultError)
102+
}
103+
}

0 commit comments

Comments
 (0)