Skip to content

Commit 2471516

Browse files
committed
Merge branch 'trunk' into feat/3157-search-products-by-sku-storage
* trunk: (166 commits) Split CustomerRemoteTests Remove unnecessary CustomerMapper asserts Remove unnecessary default optional values Remove unused dateDecodingStrategy Freeze strings for localization Update metadata strings Update release notes for 10.5 Update Podfile.lock for WordPressAuthenticator Update `WordPressAuthenticator` to stable `3.2.0` Update release notes. Update release notes Release Notes: add new section for next version (10.6) Update draft release notes for 10.5. Bump version number Update CHANGELOG.md Bump version number Revert "Bump version number" Bump version number Revert "Bump version number" Bump version number ...
2 parents b034d5f + 43597e2 commit 2471516

File tree

94 files changed

+3314
-930
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+3314
-930
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
<!--
22
Contains editorialized release notes. Raw release notes should go into `RELEASE-NOTES.txt`.
33
-->
4+
## 10.5
5+
6+
This release includes a lot of exciting features! It's now possible to duplicate a product from the app. Just tap on the More Menu available inside the Product detail screen. We have also added a new stats widget so now you can view your current day's stats without opening the app. Lastly, due to popular demand, we're bringing back the ability to add/edit customer notes and addresses from the main order screen.
7+
8+
We welcome your feedback on the app, especially the new features we’re working on.
49

510
## 10.4
611

Experiments/Experiments/DefaultFeatureFlagService.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ public struct DefaultFeatureFlagService: FeatureFlagService {
3333
return true
3434
case .promptToEnableCodInIppOnboarding:
3535
return true
36+
case .orderCreationSearchCustomers:
37+
return buildConfig == .localDeveloper || buildConfig == .alpha
3638
default:
3739
return true
3840
}

Experiments/Experiments/FeatureFlag.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,8 @@ public enum FeatureFlag: Int {
6969
/// Whether to include the Cash on Delivery enable step in In-Person Payment onboarding
7070
///
7171
case promptToEnableCodInIppOnboarding
72+
73+
/// Enables the Search Customers functionality in the Order Creation screen
74+
///
75+
case orderCreationSearchCustomers
7276
}

Fakes/Fakes/Networking.generated.swift

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,13 +181,40 @@ extension CreateProductVariation {
181181
)
182182
}
183183
}
184+
extension Customer {
185+
/// Returns a "ready to use" type filled with fake values.
186+
///
187+
public static func fake() -> Customer {
188+
.init(
189+
customerID: .fake(),
190+
email: .fake(),
191+
firstName: .fake(),
192+
lastName: .fake(),
193+
billing: .fake(),
194+
shipping: .fake()
195+
)
196+
}
197+
}
184198
extension DotcomError {
185199
/// Returns a "ready to use" type filled with fake values.
186200
///
187201
public static func fake() -> DotcomError {
188202
.empty
189203
}
190204
}
205+
extension DotcomUser {
206+
/// Returns a "ready to use" type filled with fake values.
207+
///
208+
public static func fake() -> DotcomUser {
209+
.init(
210+
id: .fake(),
211+
username: .fake(),
212+
email: .fake(),
213+
displayName: .fake(),
214+
avatar: .fake()
215+
)
216+
}
217+
}
191218
extension InboxAction {
192219
/// Returns a "ready to use" type filled with fake values.
193220
///
@@ -220,6 +247,19 @@ extension InboxNote {
220247
)
221248
}
222249
}
250+
extension JetpackUser {
251+
/// Returns a "ready to use" type filled with fake values.
252+
///
253+
public static func fake() -> JetpackUser {
254+
.init(
255+
isConnected: .fake(),
256+
isPrimary: .fake(),
257+
username: .fake(),
258+
wpcomUser: .fake(),
259+
gravatar: .fake()
260+
)
261+
}
262+
}
223263
extension Leaderboard {
224264
/// Returns a "ready to use" type filled with fake values.
225265
///

Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ GEM
165165
xcpretty-travis-formatter (>= 0.0.3)
166166
fastlane-plugin-appcenter (1.11.1)
167167
fastlane-plugin-sentry (1.11.1)
168-
fastlane-plugin-wpmreleasetoolkit (5.4.0)
168+
fastlane-plugin-wpmreleasetoolkit (5.5.0)
169169
activesupport (~> 5)
170170
bigdecimal (~> 1.4)
171171
buildkit (~> 1.5)

Networking/Networking.xcodeproj/project.pbxproj

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,13 @@
323323
57BE08D82409B63800F6DCED /* reviews-missing-avatar-urls.json in Resources */ = {isa = PBXBuildFile; fileRef = 57BE08D72409B63700F6DCED /* reviews-missing-avatar-urls.json */; };
324324
57E8FED3246616AC0057CD68 /* Result+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E8FED2246616AC0057CD68 /* Result+Extensions.swift */; };
325325
6647C0161DAC6AB6570C53A7 /* Pods_Networking.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F3F25DC15EC1D7C631169CB5 /* Pods_Networking.framework */; };
326+
68BD37B328D9B8BD00C2A517 /* CustomerRemoteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68BD37B228D9B8BD00C2A517 /* CustomerRemoteTests.swift */; };
326327
68C87B342862D40E00A99054 /* setting-all-except-countries.json in Resources */ = {isa = PBXBuildFile; fileRef = 68C87B332862D40E00A99054 /* setting-all-except-countries.json */; };
328+
68CB800C28D87BC800E169F8 /* Customer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68CB800B28D87BC800E169F8 /* Customer.swift */; };
329+
68CB800E28D8901B00E169F8 /* CustomerMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68CB800D28D8901B00E169F8 /* CustomerMapper.swift */; };
330+
68CB801028D89A0400E169F8 /* CustomerRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68CB800F28D89A0400E169F8 /* CustomerRemote.swift */; };
331+
68CB801428D8A05200E169F8 /* CustomerMapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68CB801328D8A05200E169F8 /* CustomerMapperTests.swift */; };
332+
68CB801628D8A39700E169F8 /* customer.json in Resources */ = {isa = PBXBuildFile; fileRef = 68CB801528D8A39700E169F8 /* customer.json */; };
327333
68FBC5B828928C8C00A05461 /* WooFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 68FBC5B728928C8C00A05461 /* WooFoundation.framework */; };
328334
74002D6A2118B26100A63C19 /* SiteVisitStatsMapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74002D692118B26000A63C19 /* SiteVisitStatsMapperTests.swift */; };
329335
74002D6C2118B88200A63C19 /* SiteVisitStatsRemoteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74002D6B2118B88200A63C19 /* SiteVisitStatsRemoteTests.swift */; };
@@ -1010,7 +1016,13 @@
10101016
5726F7332460A8F00031CAAC /* CopiableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopiableTests.swift; sourceTree = "<group>"; };
10111017
57BE08D72409B63700F6DCED /* reviews-missing-avatar-urls.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "reviews-missing-avatar-urls.json"; sourceTree = "<group>"; };
10121018
57E8FED2246616AC0057CD68 /* Result+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Result+Extensions.swift"; sourceTree = "<group>"; };
1019+
68BD37B228D9B8BD00C2A517 /* CustomerRemoteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomerRemoteTests.swift; sourceTree = "<group>"; };
10131020
68C87B332862D40E00A99054 /* setting-all-except-countries.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "setting-all-except-countries.json"; sourceTree = "<group>"; };
1021+
68CB800B28D87BC800E169F8 /* Customer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Customer.swift; sourceTree = "<group>"; };
1022+
68CB800D28D8901B00E169F8 /* CustomerMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomerMapper.swift; sourceTree = "<group>"; };
1023+
68CB800F28D89A0400E169F8 /* CustomerRemote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomerRemote.swift; sourceTree = "<group>"; };
1024+
68CB801328D8A05200E169F8 /* CustomerMapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomerMapperTests.swift; sourceTree = "<group>"; };
1025+
68CB801528D8A39700E169F8 /* customer.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = customer.json; sourceTree = "<group>"; };
10141026
68FBC5B728928C8C00A05461 /* WooFoundation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = WooFoundation.framework; sourceTree = BUILT_PRODUCTS_DIR; };
10151027
69314EDE650855CAF927057E /* Pods_NetworkingTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_NetworkingTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
10161028
74002D692118B26000A63C19 /* SiteVisitStatsMapperTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SiteVisitStatsMapperTests.swift; sourceTree = "<group>"; };
@@ -1616,6 +1628,7 @@
16161628
FE28F6EB268436C9004465C7 /* UserRemoteTests.swift */,
16171629
077F39D926A58ED700ABEADC /* SystemStatusRemoteTests.swift */,
16181630
DE34051E28BDFB0B00CF0D97 /* JetpackConnectionRemoteTests.swift */,
1631+
68BD37B228D9B8BD00C2A517 /* CustomerRemoteTests.swift */,
16191632
);
16201633
path = Remote;
16211634
sourceTree = "<group>";
@@ -1748,6 +1761,7 @@
17481761
FE28F6E5268429B6004465C7 /* UserRemote.swift */,
17491762
077F39D526A58E4500ABEADC /* SystemStatusRemote.swift */,
17501763
AEF94584272974F2001DCCFB /* TelemetryRemote.swift */,
1764+
68CB800F28D89A0400E169F8 /* CustomerRemote.swift */,
17511765
);
17521766
path = Remote;
17531767
sourceTree = "<group>";
@@ -1855,6 +1869,7 @@
18551869
FE28F6E126840DED004465C7 /* User.swift */,
18561870
DE50295828C5BD0200551736 /* JetpackUser.swift */,
18571871
DE50295A28C5F99700551736 /* DotcomUser.swift */,
1872+
68CB800B28D87BC800E169F8 /* Customer.swift */,
18581873
);
18591874
path = Model;
18601875
sourceTree = "<group>";
@@ -2089,6 +2104,7 @@
20892104
4513382727A96DE700AE5E78 /* inbox-note.json */,
20902105
0205021B27C86B9700FB1C6B /* inbox-note-without-isRead.json */,
20912106
68C87B332862D40E00A99054 /* setting-all-except-countries.json */,
2107+
68CB801528D8A39700E169F8 /* customer.json */,
20922108
);
20932109
path = Responses;
20942110
sourceTree = "<group>";
@@ -2181,6 +2197,7 @@
21812197
02C112772742862600F4F0B4 /* WordPressSiteSettingsMapper.swift */,
21822198
0359EA1C27AADE000048DE2D /* WCPayChargeMapper.swift */,
21832199
DE34051828BDEE6A00CF0D97 /* JetpackConnectionURLMapper.swift */,
2200+
68CB800D28D8901B00E169F8 /* CustomerMapper.swift */,
21842201
);
21852202
path = Mapper;
21862203
sourceTree = "<group>";
@@ -2303,6 +2320,7 @@
23032320
DE34051C28BDF1C900CF0D97 /* JetpackConnectionURLMapperTests.swift */,
23042321
DE50296428C60A8000551736 /* JetpackUserMapperTests.swift */,
23052322
0359EA1E27AAE4680048DE2D /* WCPayChargeMapperTests.swift */,
2323+
68CB801328D8A05200E169F8 /* CustomerMapperTests.swift */,
23062324
);
23072325
path = Mapper;
23082326
sourceTree = "<group>";
@@ -2684,6 +2702,7 @@
26842702
7497376A2141F2BE0008C490 /* top-performers-week-alt.json in Resources */,
26852703
D865CE61278CA1AE002C8520 /* stripe-payment-intent-processing.json in Resources */,
26862704
743E84F222172D0A00FAC9D7 /* shipment_tracking_plugin_not_active.json in Resources */,
2705+
68CB801628D8A39700E169F8 /* customer.json in Resources */,
26872706
451A97DE260B59870059D135 /* shipping-label-packages-success.json in Resources */,
26882707
31D27C8F2602B553002EDB1D /* plugins.json in Resources */,
26892708
261CF1B4255AD6B30090D8D3 /* payment-gateway-list.json in Resources */,
@@ -2982,6 +3001,7 @@
29823001
B518662220A097C200037A38 /* Network.swift in Sources */,
29833002
B572F69A21AC475C003EEFF0 /* DevicesRemote.swift in Sources */,
29843003
3192F220260D33BB0067FEF9 /* WCPayAccount.swift in Sources */,
3004+
68CB800E28D8901B00E169F8 /* CustomerMapper.swift in Sources */,
29853005
45CCFCE227A2C9BF0012E8CB /* InboxNote.swift in Sources */,
29863006
311D412C2783BF7400052F64 /* StripeAccount.swift in Sources */,
29873007
B518662420A099BF00037A38 /* AlamofireNetwork.swift in Sources */,
@@ -3030,6 +3050,7 @@
30303050
020D07BE23D8570800FD9580 /* MediaListMapper.swift in Sources */,
30313051
0359EA1327AAC6D00048DE2D /* WCPayCardPaymentDetails.swift in Sources */,
30323052
CCB2CA9E262091CB00285CA0 /* SuccessDataResultMapper.swift in Sources */,
3053+
68CB801028D89A0400E169F8 /* CustomerRemote.swift in Sources */,
30333054
DE50295B28C5F99700551736 /* DotcomUser.swift in Sources */,
30343055
74C8F06820EEB7BD00B6EDC9 /* OrderNotesMapper.swift in Sources */,
30353056
24F98C582502EA8800F49B68 /* FeatureFlagMapper.swift in Sources */,
@@ -3039,6 +3060,7 @@
30393060
74046E1D217A6989007DD7BF /* SiteSetting.swift in Sources */,
30403061
B5BB1D1020A237FB00112D92 /* Address.swift in Sources */,
30413062
CE43066A23465F340073CBFF /* Refund.swift in Sources */,
3063+
68CB800C28D87BC800E169F8 /* Customer.swift in Sources */,
30423064
DE50295D28C6068B00551736 /* JetpackUserMapper.swift in Sources */,
30433065
B524194121AC60A700D6FC0A /* DotcomDevice.swift in Sources */,
30443066
D8EDFE2225EE88C9003D2213 /* ReaderConnectionToken.swift in Sources */,
@@ -3124,6 +3146,7 @@
31243146
CEC4BF8F234E382F008D9195 /* RefundMapperTests.swift in Sources */,
31253147
24F98C5E2502EDCF00F49B68 /* BundleWooTests.swift in Sources */,
31263148
74AB0ACA21948CE4008220CD /* CommentResultMapperTests.swift in Sources */,
3149+
68CB801428D8A05200E169F8 /* CustomerMapperTests.swift in Sources */,
31273150
02698CF824C183A5005337C4 /* ProductVariationListMapperTests.swift in Sources */,
31283151
B524194921AC659500D6FC0A /* DevicesRemoteTests.swift in Sources */,
31293152
2685C0DA263B551300D9EE97 /* AddOnGroupMapperTests.swift in Sources */,
@@ -3198,6 +3221,7 @@
31983221
45CCFCE827A2E5020012E8CB /* InboxNoteListMapperTests.swift in Sources */,
31993222
74002D6C2118B88200A63C19 /* SiteVisitStatsRemoteTests.swift in Sources */,
32003223
0212683524C046CB00F8A892 /* MockNetwork+Path.swift in Sources */,
3224+
68BD37B328D9B8BD00C2A517 /* CustomerRemoteTests.swift in Sources */,
32013225
B554FA932180C17200C54DFF /* NoteHashListMapperTests.swift in Sources */,
32023226
CC07866526790B1100BA9AC1 /* ShippingLabelPurchaseMapperTests.swift in Sources */,
32033227
74002D6A2118B26100A63C19 /* SiteVisitStatsMapperTests.swift in Sources */,
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import Foundation
2+
3+
/// Mapper: Customer
4+
///
5+
struct CustomerMapper: Mapper {
6+
/// We're injecting this field by copying it in after parsing responses, because `siteID` is not returned in any of the Customer endpoints.
7+
///
8+
let siteID: Int64
9+
10+
/// (Attempts) to convert a dictionary into a `Customer` entity
11+
///
12+
func map(response: Data) throws -> Customer {
13+
let decoder = JSONDecoder()
14+
decoder.userInfo = [.siteID: siteID]
15+
let customer = try decoder.decode(CustomerEnvelope.self, from: response).customer
16+
return customer
17+
}
18+
}
19+
20+
private struct CustomerEnvelope: Decodable {
21+
let customer: Customer
22+
23+
private enum CodingKeys: String, CodingKey {
24+
case customer = "data"
25+
}
26+
}

Networking/Networking/Model/Copiable/Models+Copiable.generated.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,33 @@ extension CouponReport {
169169
}
170170
}
171171

172+
extension Customer {
173+
func copy(
174+
customerID: CopiableProp<Int64> = .copy,
175+
email: CopiableProp<String> = .copy,
176+
firstName: NullableCopiableProp<String> = .copy,
177+
lastName: NullableCopiableProp<String> = .copy,
178+
billing: NullableCopiableProp<Address> = .copy,
179+
shipping: NullableCopiableProp<Address> = .copy
180+
) -> Customer {
181+
let customerID = customerID ?? self.customerID
182+
let email = email ?? self.email
183+
let firstName = firstName ?? self.firstName
184+
let lastName = lastName ?? self.lastName
185+
let billing = billing ?? self.billing
186+
let shipping = shipping ?? self.shipping
187+
188+
return Customer(
189+
customerID: customerID,
190+
email: email,
191+
firstName: firstName,
192+
lastName: lastName,
193+
billing: billing,
194+
shipping: shipping
195+
)
196+
}
197+
}
198+
172199
extension DotcomUser {
173200
public func copy(
174201
id: CopiableProp<Int64> = .copy,
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import Foundation
2+
import Codegen
3+
4+
/// Represents a Customer entity:
5+
/// https://woocommerce.github.io/woocommerce-rest-api-docs/#customer-properties
6+
///
7+
public struct Customer: Codable, GeneratedCopiable, GeneratedFakeable {
8+
9+
/// Unique identifier for the customer
10+
public let customerID: Int64
11+
12+
/// The email address for the customer
13+
public let email: String
14+
15+
/// Customer first name
16+
public let firstName: String?
17+
18+
/// Customer last name
19+
public let lastName: String?
20+
21+
/// List of billing address data
22+
public let billing: Address?
23+
24+
/// List of shipping address data
25+
public let shipping: Address?
26+
27+
/// Customer struct initializer
28+
///
29+
public init(customerID: Int64,
30+
email: String,
31+
firstName: String?,
32+
lastName: String?,
33+
billing: Address?,
34+
shipping: Address?) {
35+
self.customerID = customerID
36+
self.email = email
37+
self.firstName = firstName
38+
self.lastName = lastName
39+
self.billing = billing
40+
self.shipping = shipping
41+
}
42+
43+
/// Public initializer for the Customer
44+
///
45+
public init(from decoder: Decoder) throws {
46+
let container = try decoder.container(keyedBy: CodingKeys.self)
47+
48+
let customerID = try container.decode(Int64.self, forKey: .customerID)
49+
let email = try container.decode(String.self, forKey: .email)
50+
let firstName = try container.decodeIfPresent(String.self, forKey: .firstName)
51+
let lastName = try container.decodeIfPresent(String.self, forKey: .lastName)
52+
let billing = try? container.decode(Address.self, forKey: .billing)
53+
let shipping = try? container.decode(Address.self, forKey: .shipping)
54+
55+
self.init(customerID: customerID,
56+
email: email,
57+
firstName: firstName,
58+
lastName: lastName,
59+
billing: billing,
60+
shipping: shipping
61+
)
62+
}
63+
}
64+
65+
/// Defines all of the Customer CodingKeys
66+
///
67+
extension Customer {
68+
enum CodingKeys: String, CodingKey {
69+
case customerID = "id"
70+
case email
71+
case firstName = "first_name"
72+
case lastName = "last_name"
73+
case billing
74+
case shipping
75+
}
76+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import Foundation
2+
3+
public class CustomerRemote: Remote {
4+
/// Retrieves a `Customer`
5+
///
6+
/// - Parameters:
7+
/// - customerID: ID of the customer that will be retrieved
8+
/// - siteID: Site for which we'll fetch the customer.
9+
/// - completion: Closure to be executed upon completion.
10+
///
11+
func retrieveCustomer(for siteID: Int64, with customerID: Int64, completion: @escaping (Result<Customer, Error>) -> Void) {
12+
let path = "/customers/\(customerID)"
13+
let request = JetpackRequest(wooApiVersion: .mark3,
14+
method: .get,
15+
siteID: siteID,
16+
path: path,
17+
parameters: nil
18+
)
19+
20+
let mapper = CustomerMapper(siteID: siteID)
21+
enqueue(request, mapper: mapper, completion: completion)
22+
}
23+
}

0 commit comments

Comments
 (0)