Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
fca2d24
Add Customer+ReadOnlyConvertible
iamgabrielma Oct 13, 2022
6cd8728
Add initial upsertCustomer logic & StorageTypeExension test
iamgabrielma Oct 13, 2022
1d89a6c
Add upsertSearchCustomerResults & ReadOnlyConvertible
iamgabrielma Oct 13, 2022
60f2f65
Fix whitespace & line length
iamgabrielma Oct 13, 2022
a622ae4
Merge branch 'trunk' into issue/7741-add-customer-upsert-logic
iamgabrielma Oct 18, 2022
b58f7b7
Merge branch 'trunk' into issue/7741-add-customer-upsert-logic
iamgabrielma Oct 18, 2022
1d40075
Add siteID to loadCustomer & loadCustomerResult
iamgabrielma Oct 18, 2022
b25fd0f
Modify loadCustomer tests for siteID and keyword params
iamgabrielma Oct 18, 2022
6c3d363
Update Customer Copiable with siteID
iamgabrielma Oct 18, 2022
08dbbbe
Use storage.perform when upsertCustomer
iamgabrielma Oct 18, 2022
8bb6b9d
Unit test for retrieveCustomer upserts data
iamgabrielma Oct 18, 2022
0043213
Make WCAnalytics Copiable & Fakeable
iamgabrielma Oct 18, 2022
e57ddbc
Add upsertCustomerSearchResult to Storage
iamgabrielma Oct 18, 2022
263dc79
Update WCAnalyticsCustomer with siteID param
iamgabrielma Oct 19, 2022
caf7b81
Add customer-2 json response
iamgabrielma Oct 19, 2022
7d67287
Add upsert customerSearchResults to Storage
iamgabrielma Oct 19, 2022
ec11323
Add temporary method to debug responses
iamgabrielma Oct 19, 2022
5dca4ec
Add comments. Fix typo.
iamgabrielma Oct 21, 2022
457ae8a
Rename DecodingError
iamgabrielma Oct 21, 2022
a212847
Remove unnecessary XCTAsserts
iamgabrielma Oct 21, 2022
470010e
Make CustomerStore StorageType lazy
iamgabrielma Oct 21, 2022
27d82f2
Remove passing storage directly to upsertSearchCustomerResult
iamgabrielma Oct 21, 2022
36f2e66
Merge branch 'trunk' into issue/7741-add-customer-upsert-logic
iamgabrielma Oct 21, 2022
0a79129
capture weak self
iamgabrielma Oct 21, 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
12 changes: 12 additions & 0 deletions Fakes/Fakes/Networking.generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ extension Customer {
///
public static func fake() -> Customer {
.init(
siteID: .fake(),
customerID: .fake(),
email: .fake(),
firstName: .fake(),
Expand Down Expand Up @@ -1694,6 +1695,17 @@ extension User {
)
}
}
extension WCAnalyticsCustomer {
/// Returns a "ready to use" type filled with fake values.
///
public static func fake() -> WCAnalyticsCustomer {
.init(
siteID: .fake(),
userID: .fake(),
name: .fake()
)
}
}
extension WCPayAccountStatusEnum {
/// Returns a "ready to use" type filled with fake values.
///
Expand Down
4 changes: 4 additions & 0 deletions Networking/Networking.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@
57BE08D82409B63800F6DCED /* reviews-missing-avatar-urls.json in Resources */ = {isa = PBXBuildFile; fileRef = 57BE08D72409B63700F6DCED /* reviews-missing-avatar-urls.json */; };
57E8FED3246616AC0057CD68 /* Result+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E8FED2246616AC0057CD68 /* Result+Extensions.swift */; };
6647C0161DAC6AB6570C53A7 /* Pods_Networking.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F3F25DC15EC1D7C631169CB5 /* Pods_Networking.framework */; };
688908AE28FF920C0081A07E /* customer-2.json in Resources */ = {isa = PBXBuildFile; fileRef = 688908AD28FF920C0081A07E /* customer-2.json */; };
68BD37B328D9B8BD00C2A517 /* CustomerRemoteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68BD37B228D9B8BD00C2A517 /* CustomerRemoteTests.swift */; };
68C87B342862D40E00A99054 /* setting-all-except-countries.json in Resources */ = {isa = PBXBuildFile; fileRef = 68C87B332862D40E00A99054 /* setting-all-except-countries.json */; };
68CB800C28D87BC800E169F8 /* Customer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68CB800B28D87BC800E169F8 /* Customer.swift */; };
Expand Down Expand Up @@ -1024,6 +1025,7 @@
5726F7332460A8F00031CAAC /* CopiableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopiableTests.swift; sourceTree = "<group>"; };
57BE08D72409B63700F6DCED /* reviews-missing-avatar-urls.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "reviews-missing-avatar-urls.json"; sourceTree = "<group>"; };
57E8FED2246616AC0057CD68 /* Result+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Result+Extensions.swift"; sourceTree = "<group>"; };
688908AD28FF920C0081A07E /* customer-2.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "customer-2.json"; sourceTree = "<group>"; };
68BD37B228D9B8BD00C2A517 /* CustomerRemoteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomerRemoteTests.swift; sourceTree = "<group>"; };
68C87B332862D40E00A99054 /* setting-all-except-countries.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "setting-all-except-countries.json"; sourceTree = "<group>"; };
68CB800B28D87BC800E169F8 /* Customer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Customer.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2124,6 +2126,7 @@
68C87B332862D40E00A99054 /* setting-all-except-countries.json */,
68CB801528D8A39700E169F8 /* customer.json */,
68F48B1228E3E5750045C15B /* wc-analytics-customers.json */,
688908AD28FF920C0081A07E /* customer-2.json */,
);
path = Responses;
sourceTree = "<group>";
Expand Down Expand Up @@ -2553,6 +2556,7 @@
D823D91422377EE600C90817 /* shipment_tracking_providers.json in Resources */,
4599FC5C24A6276F0056157A /* product-tags-all.json in Resources */,
03DCB77E262738E300C8953D /* coupon.json in Resources */,
688908AE28FF920C0081A07E /* customer-2.json in Resources */,
034480C327A42F9100DFACD2 /* order-with-charge.json in Resources */,
74A7B4BE217A841400E85A8B /* broken-settings-general.json in Resources */,
026CF624237D839B009563D4 /* product-variations-load-all.json in Resources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,14 +170,16 @@ extension CouponReport {
}

extension Customer {
func copy(
public func copy(
siteID: CopiableProp<Int64> = .copy,
customerID: CopiableProp<Int64> = .copy,
email: CopiableProp<String> = .copy,
firstName: NullableCopiableProp<String> = .copy,
lastName: NullableCopiableProp<String> = .copy,
billing: NullableCopiableProp<Address> = .copy,
shipping: NullableCopiableProp<Address> = .copy
) -> Customer {
let siteID = siteID ?? self.siteID
let customerID = customerID ?? self.customerID
let email = email ?? self.email
let firstName = firstName ?? self.firstName
Expand All @@ -186,6 +188,7 @@ extension Customer {
let shipping = shipping ?? self.shipping

return Customer(
siteID: siteID,
customerID: customerID,
email: email,
firstName: firstName,
Expand Down Expand Up @@ -1948,6 +1951,24 @@ extension TopEarnerStatsItem {
}
}

extension WCAnalyticsCustomer {
public func copy(
siteID: CopiableProp<Int64> = .copy,
userID: CopiableProp<Int64> = .copy,
name: NullableCopiableProp<String> = .copy
) -> WCAnalyticsCustomer {
let siteID = siteID ?? self.siteID
let userID = userID ?? self.userID
let name = name ?? self.name

return WCAnalyticsCustomer(
siteID: siteID,
userID: userID,
name: name
)
}
}

extension WCPayCardPaymentDetails {
public func copy(
brand: CopiableProp<WCPayCardBrand> = .copy,
Expand Down
17 changes: 15 additions & 2 deletions Networking/Networking/Model/Customer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import Codegen
/// https://woocommerce.github.io/woocommerce-rest-api-docs/#customer-properties
///
public struct Customer: Codable, GeneratedCopiable, GeneratedFakeable {
/// The siteID for the customer
public let siteID: Int64

/// Unique identifier for the customer
public let customerID: Int64
Expand All @@ -26,12 +28,14 @@ public struct Customer: Codable, GeneratedCopiable, GeneratedFakeable {

/// Customer struct initializer
///
public init(customerID: Int64,
public init(siteID: Int64,
customerID: Int64,
email: String,
firstName: String?,
lastName: String?,
billing: Address?,
shipping: Address?) {
self.siteID = siteID
self.customerID = customerID
self.email = email
self.firstName = firstName
Expand All @@ -43,6 +47,10 @@ public struct Customer: Codable, GeneratedCopiable, GeneratedFakeable {
/// Public initializer for the Customer
///
public init(from decoder: Decoder) throws {
guard let siteID = decoder.userInfo[.siteID] as? Int64 else {
throw CustomerDecodingError.missingSiteID
}

let container = try decoder.container(keyedBy: CodingKeys.self)

let customerID = try container.decode(Int64.self, forKey: .customerID)
Expand All @@ -52,7 +60,8 @@ public struct Customer: Codable, GeneratedCopiable, GeneratedFakeable {
let billing = try? container.decode(Address.self, forKey: .billing)
let shipping = try? container.decode(Address.self, forKey: .shipping)

self.init(customerID: customerID,
self.init(siteID: siteID,
customerID: customerID,
email: email,
firstName: firstName,
lastName: lastName,
Expand All @@ -73,4 +82,8 @@ extension Customer {
case billing
case shipping
}

enum CustomerDecodingError: Error {
case missingSiteID
}
}
18 changes: 15 additions & 3 deletions Networking/Networking/Model/WCAnalyticsCustomer.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import Foundation
import Codegen

public struct WCAnalyticsCustomer: Codable {
public struct WCAnalyticsCustomer: Codable, GeneratedCopiable, GeneratedFakeable {
/// The siteID for the WCAnalyticsCustomer
public let siteID: Int64
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are we adding a siteID property to WCAnalyticsCustomer? It's very possible I missed it, but I didn't see where we're using this. It looks like we may only need it for the Customer model.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right that we're not actively using it at the moment. That said I'd prefer to leave it for consistency with the rest of the models, and at some point we may need to save this to storage directly and siteID will be necessary ( I just haven't reached that point yet :D ). I can always circle back once the feature is completed and remove it if needed 👍

Copy link
Contributor

Choose a reason for hiding this comment

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

I generally prefer to wait to make changes until they're definitely needed, but you have more context about the overall changes needed for this project so I'd say go with what makes most sense for your plans here!


/// Unique identifier for the user
public let userID: Int64
Expand All @@ -10,20 +13,25 @@ public struct WCAnalyticsCustomer: Codable {

/// WCAnalyticsCustomer struct Initializer
///
public init(userID: Int64, name: String?) {
public init(siteID: Int64, userID: Int64, name: String?) {
self.siteID = siteID
self.userID = userID
self.name = name
}

/// Public initializer for WCAnalyticsCustomer
///
public init(from decoder: Decoder) throws {
guard let siteID = decoder.userInfo[.siteID] as? Int64 else {
throw WCAnalyticsCustomerDecodingError.missingSiteID
}

let container = try decoder.container(keyedBy: CodingKeys.self)

let userID = try container.decode(Int64.self, forKey: .userID)
let name = try container.decode(String.self, forKey: .name)

self.init(userID: userID, name: name)
self.init(siteID: siteID, userID: userID, name: name)
}
}

Expand All @@ -32,4 +40,8 @@ extension WCAnalyticsCustomer {
case userID = "user_id"
case name = "name"
}

enum WCAnalyticsCustomerDecodingError: Error {
case missingSiteID
}
}
55 changes: 55 additions & 0 deletions Networking/NetworkingTests/Responses/customer-2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"data": {
"id": 26,
"date_created": "2017-03-21T16:09:28",
"date_created_gmt": "2017-03-21T19:09:28",
"date_modified": "2017-03-21T16:09:30",
"date_modified_gmt": "2017-03-21T19:09:30",
"email": "[email protected]",
"first_name": "Johnny",
"last_name": "Doe",
"role": "customer",
"username": "johnny.doe",
"billing": {
"first_name": "John",
"last_name": "Doe",
"company": "",
"address_1": "969 Market",
"address_2": "",
"city": "San Francisco",
"state": "CA",
"postcode": "94103",
"country": "US",
"email": "[email protected]",
"phone": "(555) 555-5555"
},
"shipping": {
"first_name": "John",
"last_name": "Doe",
"company": "",
"address_1": "969 Market",
"address_2": "",
"city": "San Francisco",
"state": "CA",
"postcode": "94103",
"country": "US"
},
"is_paying_customer": false,
"avatar_url": "https://secure.gravatar.com/avatar/8eb1b522f60d11fa897de1dc6351b7e8?s=96",
"meta_data": [],
"_links": {
"self": [
{
"href": "https://example.com/wp-json/wc/v3/customers/25"
}
],
"collection": [
{
"href": "https://example.com/wp-json/wc/v3/customers"
}
]
}
}
}


15 changes: 15 additions & 0 deletions Storage/Storage/Tools/StorageType+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,21 @@ public extension StorageType {
return firstObject(ofType: WCPayCharge.self, matching: predicate)
}

// MARK: - Customers

/// Returns a single Customer given a `CustomerID`
///
func loadCustomer(siteID: Int64, customerID: Int64) -> Customer? {
let predicate = \Customer.siteID == siteID && \Customer.customerID == customerID
return firstObject(ofType: Customer.self, matching: predicate)
}
/// Returns a CustomerSearchResult given a `CustomerID`
///
func loadCustomerSearchResult(siteID: Int64, keyword: String) -> CustomerSearchResult? {
let predicate = \CustomerSearchResult.siteID == siteID && \CustomerSearchResult.keyword == keyword
return firstObject(ofType: CustomerSearchResult.self, matching: predicate)
}

// MARK: - System plugins

/// Returns all stored system plugins for a provided `siteID`.
Expand Down
30 changes: 30 additions & 0 deletions Storage/StorageTests/Tools/StorageTypeExtensionsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,36 @@ final class StorageTypeExtensionsTests: XCTestCase {
XCTAssertEqual(coupon, storedCoupon)
}

func test_loadCustomer_by_siteID_and_customerID() throws {
// Given
let customerID: Int64 = 123
let customer = storage.insertNewObject(ofType: Customer.self)
customer.siteID = sampleSiteID
customer.customerID = customerID

// When
let storedCustomer = try XCTUnwrap(storage.loadCustomer(siteID: sampleSiteID, customerID: customerID))

// Then
XCTAssertEqual(storedCustomer.siteID, sampleSiteID)
XCTAssertEqual(customer, storedCustomer)
}

func test_loadCustomerSearchResult_by_siteID_and_keyword() throws {
// Given
let keyword: String = "some keyword"
let customerSearchResult = storage.insertNewObject(ofType: CustomerSearchResult.self)
customerSearchResult.siteID = sampleSiteID
customerSearchResult.keyword = keyword

// When
let storedCustomerSearchResult = try XCTUnwrap(storage.loadCustomerSearchResult(siteID: sampleSiteID, keyword: keyword ))

// Then
XCTAssertEqual(customerSearchResult.siteID, sampleSiteID)
XCTAssertEqual(customerSearchResult, storedCustomerSearchResult)
}

func test_loadOrderFeeLine_by_siteID_feeID() throws {
// Given
let feeID: Int64 = 123
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,19 @@ final class CreateOrderAddressFormViewModel: AddressFormViewModel, AddressFormVi
siteID: siteID,
keyword: "hello") { result in
switch result {
case .success(_):
print("Success!")
case .success(let customers):
let storage = ServiceLocator.storageManager
guard let result = storage.viewStorage.loadCustomerSearchResult(siteID: self.siteID, keyword: "hello") else {
return
}
print("Site ID: \(result.siteID), keyword: \(result.keyword), Customers: \(result.customers?.count as Any)")
for eachCustomer in customers {
let output = """
Customer: \(eachCustomer.customerID),
Name: \(String(describing: eachCustomer.firstName)) \(String(describing: eachCustomer.lastName))
"""
print(output)
}
case .failure(let error):
print(error)
}
Expand Down
4 changes: 4 additions & 0 deletions Yosemite/Yosemite.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@
578CE7972475FD8200492EBF /* MockProductReview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 578CE7962475FD8200492EBF /* MockProductReview.swift */; };
57DFCC7925003C4000251E0C /* FetchResultSnapshotsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57DFCC7825003C4000251E0C /* FetchResultSnapshotsProvider.swift */; };
681D952B28E0F62B00C4039E /* CustomerAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 681D952A28E0F62B00C4039E /* CustomerAction.swift */; };
6889089F28F7B8540081A07E /* Customer+ReadOnlyConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6889089E28F7B8540081A07E /* Customer+ReadOnlyConvertible.swift */; };
689D11D52891B9A400F6A83F /* WooFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 689D11D42891B9A400F6A83F /* WooFoundation.framework */; };
68BD37B528DB2E9800C2A517 /* CustomerStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68BD37B428DB2E9800C2A517 /* CustomerStore.swift */; };
68BD37B928DB323D00C2A517 /* CustomerStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68BD37B828DB323D00C2A517 /* CustomerStoreTests.swift */; };
Expand Down Expand Up @@ -613,6 +614,7 @@
57DFCC7825003C4000251E0C /* FetchResultSnapshotsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchResultSnapshotsProvider.swift; sourceTree = "<group>"; };
585B973F61632665297738A3 /* Pods-Yosemite.release-alpha.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Yosemite.release-alpha.xcconfig"; path = "../Pods/Target Support Files/Pods-Yosemite/Pods-Yosemite.release-alpha.xcconfig"; sourceTree = "<group>"; };
681D952A28E0F62B00C4039E /* CustomerAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomerAction.swift; sourceTree = "<group>"; };
6889089E28F7B8540081A07E /* Customer+ReadOnlyConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Customer+ReadOnlyConvertible.swift"; sourceTree = "<group>"; };
689D11D42891B9A400F6A83F /* WooFoundation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = WooFoundation.framework; sourceTree = BUILT_PRODUCTS_DIR; };
68BD37B428DB2E9800C2A517 /* CustomerStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomerStore.swift; sourceTree = "<group>"; };
68BD37B828DB323D00C2A517 /* CustomerStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomerStoreTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1262,6 +1264,7 @@
031C1EAB27B1873200298699 /* WCPayCardPresentReceiptDetails+ReadOnlyConvertible.swift */,
031C1EAF27B1879C00298699 /* WCPayCardPaymentDetails+ReadOnlyConvertible.swift */,
031C1EAD27B1877000298699 /* WCPayCardPresentPaymentDetails+ReadOnlyConvertible.swift */,
6889089E28F7B8540081A07E /* Customer+ReadOnlyConvertible.swift */,
);
path = Storage;
sourceTree = "<group>";
Expand Down Expand Up @@ -2075,6 +2078,7 @@
7499936420EFBC1B00CF01CD /* OrderNoteAction.swift in Sources */,
7455D4692141B59E00FA8C1F /* TopEarnerStatsItem+ReadOnlyConvertible.swift in Sources */,
D8C11A5222DF2DA200D4A88D /* StatsActionV4.swift in Sources */,
6889089F28F7B8540081A07E /* Customer+ReadOnlyConvertible.swift in Sources */,
68BD37B528DB2E9800C2A517 /* CustomerStore.swift in Sources */,
CE179D55235F4E1700C24EB3 /* RefundAction.swift in Sources */,
CE5F9A7A22B2D455001755E8 /* Array+Helpers.swift in Sources */,
Expand Down
2 changes: 2 additions & 0 deletions Yosemite/Yosemite/Model/Model.swift
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ public typealias StorageAccount = Storage.Account
public typealias StorageAccountSettings = Storage.AccountSettings
public typealias StorageAttribute = Storage.GenericAttribute
public typealias StorageCoupon = Storage.Coupon
public typealias StorageCustomer = Storage.Customer
public typealias StorageCustomerSearchResult = Storage.CustomerSearchResult
public typealias StorageCouponSearchResult = Storage.CouponSearchResult
public typealias StorageAddOnGroup = Storage.AddOnGroup
public typealias StorageAnnouncement = Storage.Announcement
Expand Down
Loading