diff --git a/Networking/Networking.xcodeproj/project.pbxproj b/Networking/Networking.xcodeproj/project.pbxproj index bcbd1c6afb2..e9f338ddaf6 100644 --- a/Networking/Networking.xcodeproj/project.pbxproj +++ b/Networking/Networking.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 53; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -2887,113 +2887,113 @@ B567AF2720A0FA0A00AB6C62 /* Mapper */ = { isa = PBXGroup; children = ( - EE57C10F297927A000BC31E7 /* ApplicationPassword */, - B567AF2820A0FA1E00AB6C62 /* Mapper.swift */, B505F6CC20BEE37E00BB1B69 /* AccountMapper.swift */, 93D8BBFC226BBEE800AD2EB3 /* AccountSettingsMapper.swift */, 2685C0F9263B5D5300D9EE97 /* AddOnGroupMapper.swift */, + EE839C252ABEF9C900049545 /* AIProductMapper.swift */, DE4D23BB29B5FC0D003A4B5D /* AnnouncementListMapper.swift */, + EE57C10F297927A000BC31E7 /* ApplicationPassword */, 740211E221939C83002248DA /* CommentResultMapper.swift */, + 45150A9D26836A57006922EA /* CountryListMapper.swift */, 03DCB73F2624AD7D00C8953D /* CouponListMapper.swift */, 03DCB785262739D200C8953D /* CouponMapper.swift */, DE2095BE279583A100171F1C /* CouponReportListMapper.swift */, - 45150A9D26836A57006922EA /* CountryListMapper.swift */, + 68CB800D28D8901B00E169F8 /* CustomerMapper.swift */, 035BA3A929113CBD0056F0AD /* DataBoolMapper.swift */, B524193E21AC5FE400D6FC0A /* DotcomDeviceMapper.swift */, CE865A9C2A41E1480049B03C /* EntityDateModifiedMapper.swift */, 2676F4CD290AE6BB00C7A15B /* EntityIDMapper.swift */, - DE50295C28C6068B00551736 /* JetpackUserMapper.swift */, AEF9458A27297FF6001DCCFB /* IgnoringResponseMapper.swift */, - E18152BF28F85D4A0011A0EC /* InAppPurchasesProductMapper.swift */, E16C59B428F8640B007D55BB /* InAppPurchaseOrderResultMapper.swift */, + E18152BF28F85D4A0011A0EC /* InAppPurchasesProductMapper.swift */, 6846B0142A619A5C008EB143 /* InAppPurchasesTransactionMapper.swift */, - 4513382327A951B300AE5E78 /* InboxNoteMapper.swift */, 45CCFCE527A2E3710012E8CB /* InboxNoteListMapper.swift */, + 4513382327A951B300AE5E78 /* InboxNoteMapper.swift */, + DE34051828BDEE6A00CF0D97 /* JetpackConnectionURLMapper.swift */, + DE50295C28C6068B00551736 /* JetpackUserMapper.swift */, 036563DC29069BE400D84BFD /* JustInTimeMessageListMapper.swift */, + B567AF2820A0FA1E00AB6C62 /* Mapper.swift */, 020D07BD23D8570800FD9580 /* MediaListMapper.swift */, D823D904223746CE00C90817 /* NewShipmentTrackingMapper.swift */, B554FA8E2180BC7000C54DFF /* NoteHashListMapper.swift */, B59325CB217E2B4C000B0E8E /* NoteListMapper.swift */, - B5C6FCD320A373BA00A4F8E4 /* OrderMapper.swift */, B567AF2A20A0FA4200AB6C62 /* OrderListMapper.swift */, - 74C8F06720EEB7BC00B6EDC9 /* OrderNotesMapper.swift */, + B5C6FCD320A373BA00A4F8E4 /* OrderMapper.swift */, CE583A0D2109154500D73C1C /* OrderNoteMapper.swift */, + 74C8F06720EEB7BC00B6EDC9 /* OrderNotesMapper.swift */, 02C254B825637BA000A04423 /* OrderShippingLabelListMapper.swift */, D8FBFF1022D3B3FC006E3336 /* OrderStatsV4Mapper.swift */, 26731336255ACA850026F7EF /* PaymentGatewayListMapper.swift */, 0313651828AE559D00EEE571 /* PaymentGatewayMapper.swift */, - 74749B96224134FF005C4CF2 /* ProductMapper.swift */, - AE1950F2296DB2C2004D37D2 /* ProductsBulkUpdateMapper.swift */, - 45152810257A81730076B03C /* ProductAttributeMapper.swift */, + 453305E82459DF2100264E50 /* PostMapper.swift */, 4515280C257A7EEC0076B03C /* ProductAttributeListMapper.swift */, - 26B6453F259BCDFE00EF3FB3 /* ProductAttributeTermMapper.swift */, + 45152810257A81730076B03C /* ProductAttributeMapper.swift */, 26B64543259BCE0F00EF3FB3 /* ProductAttributeTermListMapper.swift */, + 26B6453F259BCDFE00EF3FB3 /* ProductAttributeTermMapper.swift */, + 26615474242D7C9500A31661 /* ProductCategoryListMapper.swift */, + 45B204B72489095100FE6526 /* ProductCategoryMapper.swift */, CCF434632906BD7200B4475A /* ProductIDMapper.swift */, CE0A0F18223987DF0075ED8D /* ProductListMapper.swift */, - 45B204B72489095100FE6526 /* ProductCategoryMapper.swift */, - 26615474242D7C9500A31661 /* ProductCategoryListMapper.swift */, + 74749B96224134FF005C4CF2 /* ProductMapper.swift */, D88D5A4A230BCF0A007B6E01 /* ProductReviewListMapper.swift */, D8C251CF230BD72700F49782 /* ProductReviewMapper.swift */, - 4599FC5724A624BD0056157A /* ProductTagListMapper.swift */, - 0219B03823964BB3007DCD5E /* ProductShippingClassMapper.swift */, + AE1950F2296DB2C2004D37D2 /* ProductsBulkUpdateMapper.swift */, 025CA2C1238EBBAA00B05C81 /* ProductShippingClassListMapper.swift */, + 0219B03823964BB3007DCD5E /* ProductShippingClassMapper.swift */, 45D685F723D0BC78005F87D0 /* ProductSkuMapper.swift */, CE71E2302A4C3DDA00DB5376 /* ProductsReportMapper.swift */, - CE430673234BA6AD0073CBFF /* RefundMapper.swift */, + 020EF5E82A8BC957009D2169 /* ProductsTotalMapper.swift */, + 4599FC5724A624BD0056157A /* ProductTagListMapper.swift */, + 026CF61D237D6985009563D4 /* ProductVariationListMapper.swift */, + 02C1CEF324C6A02B00703EBA /* ProductVariationMapper.swift */, + 26BD9FCE2965EE71004E0D15 /* ProductVariationsBulkCreateMapper.swift */, + 09EA564A27C75FCE00407D40 /* ProductVariationsBulkUpdateMapper.swift */, + D8EDFE2525EE8A60003D2213 /* ReaderConnectionTokenMapper.swift */, CE430675234BA7920073CBFF /* RefundListMapper.swift */, + CE430673234BA6AD0073CBFF /* RefundMapper.swift */, + 31054701262E04F700C5C02B /* RemotePaymentIntentMapper.swift */, + 3178A49E2703E5CF00A8B4CA /* RemoteReaderLocationMapper.swift */, 7412A8EB21B6E286005D182A /* ReportOrderTotalsMapper.swift */, 743E84ED2217244C00FAC9D7 /* ShipmentTrackingListMapper.swift */, D823D91122377DF200C90817 /* ShipmentTrackingProviderListMapper.swift */, + CCF48B272628A4EB0034EA83 /* ShippingLabelAccountSettingsMapper.swift */, + 45A4B84D25D2E11300776FB4 /* ShippingLabelAddressValidationSuccessMapper.swift */, + 456930AA264EB85A009ED69D /* ShippingLabelCarriersAndRatesMapper.swift */, + CC9A24F32642CF37005DE56E /* ShippingLabelCreationEligibilityMapper.swift */, + 451A9831260B9D2D0059D135 /* ShippingLabelPackagesMapper.swift */, + 029BA53A255DFABD006171FD /* ShippingLabelPrintDataMapper.swift */, + CC0786602677B2DA00BA9AC1 /* ShippingLabelPurchaseMapper.swift */, + 021A84D9257DF92800BC71D1 /* ShippingLabelRefundMapper.swift */, + CC0786C4267BAF0F00BA9AC1 /* ShippingLabelStatusMapper.swift */, 7426CA1021AF30BD004E9FFC /* SiteAPIMapper.swift */, B56C1EB520EA757B00D749F9 /* SiteListMapper.swift */, CE50345D21B571A7007573C6 /* SitePlanMapper.swift */, - 453305E82459DF2100264E50 /* PostMapper.swift */, - 31D27C8626028CE9002EDB1D /* SitePluginsMapper.swift */, DEC51A94274CDA52009F3DF4 /* SitePluginMapper.swift */, - 74046E1E217A6B70007DD7BF /* SiteSettingsMapper.swift */, + 31D27C8626028CE9002EDB1D /* SitePluginsMapper.swift */, DE74F29927E08F5A0002FE59 /* SiteSettingMapper.swift */, - CE12AE9629F2AB3F0056DD17 /* SubscriptionMapper.swift */, - CCE5F38C29EFFBC400087332 /* SubscriptionListMapper.swift */, - B53EF53B21814900003E146F /* SuccessResultMapper.swift */, - 74A1D26C21189DFE00931DFA /* SiteVisitStatsMapper.swift */, + 74046E1E217A6B70007DD7BF /* SiteSettingsMapper.swift */, CCA1D60529437D8F00B40560 /* SiteSummaryStatsMapper.swift */, + 74A1D26C21189DFE00931DFA /* SiteVisitStatsMapper.swift */, + EE2C09C129AF26B2009396F9 /* StoreOnboardingTaskListMapper.swift */, + 311D412D2783C07D00052F64 /* StripeAccountMapper.swift */, + CCE5F38C29EFFBC400087332 /* SubscriptionListMapper.swift */, + CE12AE9629F2AB3F0056DD17 /* SubscriptionMapper.swift */, CCB2CA9D262091CB00285CA0 /* SuccessDataResultMapper.swift */, + B53EF53B21814900003E146F /* SuccessResultMapper.swift */, + DEC51AE827687AAF009F3DF4 /* SystemPluginMapper.swift */, + 077F39D326A58DE700ABEADC /* SystemStatusMapper.swift */, 450106902399B2C800E24722 /* TaxClassListMapper.swift */, + B9DFE4BD2AA2057B00174004 /* TaxRateListMapper.swift */, + B9CFF6512AB2118900C2F616 /* TaxRateMapper.swift */, 74ABA1D2213F25AE00FFAD30 /* TopEarnerStatsMapper.swift */, - 026CF61D237D6985009563D4 /* ProductVariationListMapper.swift */, - 02C1CEF324C6A02B00703EBA /* ProductVariationMapper.swift */, - 09EA564A27C75FCE00407D40 /* ProductVariationsBulkUpdateMapper.swift */, - 26BD9FCE2965EE71004E0D15 /* ProductVariationsBulkCreateMapper.swift */, - 451A9831260B9D2D0059D135 /* ShippingLabelPackagesMapper.swift */, - 029BA53A255DFABD006171FD /* ShippingLabelPrintDataMapper.swift */, - 021A84D9257DF92800BC71D1 /* ShippingLabelRefundMapper.swift */, - 311D412D2783C07D00052F64 /* StripeAccountMapper.swift */, - 3192F21B260D32550067FEF9 /* WCPayAccountMapper.swift */, - D8EDFE2525EE8A60003D2213 /* ReaderConnectionTokenMapper.swift */, - 3178A49E2703E5CF00A8B4CA /* RemoteReaderLocationMapper.swift */, - 31054701262E04F700C5C02B /* RemotePaymentIntentMapper.swift */, - 45A4B84D25D2E11300776FB4 /* ShippingLabelAddressValidationSuccessMapper.swift */, - CCF48B272628A4EB0034EA83 /* ShippingLabelAccountSettingsMapper.swift */, - 456930AA264EB85A009ED69D /* ShippingLabelCarriersAndRatesMapper.swift */, - CC9A24F32642CF37005DE56E /* ShippingLabelCreationEligibilityMapper.swift */, - CC0786602677B2DA00BA9AC1 /* ShippingLabelPurchaseMapper.swift */, - CC0786C4267BAF0F00BA9AC1 /* ShippingLabelStatusMapper.swift */, FE28F6E326842848004465C7 /* UserMapper.swift */, - 077F39D326A58DE700ABEADC /* SystemStatusMapper.swift */, - DEC51AE827687AAF009F3DF4 /* SystemPluginMapper.swift */, + 68F48B0C28E3B2E80045C15B /* WCAnalyticsCustomerMapper.swift */, + 3192F21B260D32550067FEF9 /* WCPayAccountMapper.swift */, + 0359EA1C27AADE000048DE2D /* WCPayChargeMapper.swift */, 02C11275274285FF00F4F0B4 /* WooCommerceAvailabilityMapper.swift */, 02BE0A7A274B695F001176D2 /* WordPressMediaMapper.swift */, - 02C112772742862600F4F0B4 /* WordPressSiteSettingsMapper.swift */, - 0359EA1C27AADE000048DE2D /* WCPayChargeMapper.swift */, - DE34051828BDEE6A00CF0D97 /* JetpackConnectionURLMapper.swift */, - 68CB800D28D8901B00E169F8 /* CustomerMapper.swift */, - 68F48B0C28E3B2E80045C15B /* WCAnalyticsCustomerMapper.swift */, DE2E8E9E295310C5002E4B14 /* WordPressSiteMapper.swift */, - EE2C09C129AF26B2009396F9 /* StoreOnboardingTaskListMapper.swift */, - 020EF5E82A8BC957009D2169 /* ProductsTotalMapper.swift */, - B9DFE4BD2AA2057B00174004 /* TaxRateListMapper.swift */, - B9CFF6512AB2118900C2F616 /* TaxRateMapper.swift */, - EE839C252ABEF9C900049545 /* AIProductMapper.swift */, + 02C112772742862600F4F0B4 /* WordPressSiteSettingsMapper.swift */, ); path = Mapper; sourceTree = "<group>"; diff --git a/Networking/Networking/Extensions/Mapper+DataEnvelope.swift b/Networking/Networking/Extensions/Mapper+DataEnvelope.swift index 8f94ef013eb..4a02f723208 100644 --- a/Networking/Networking/Extensions/Mapper+DataEnvelope.swift +++ b/Networking/Networking/Extensions/Mapper+DataEnvelope.swift @@ -1,25 +1,12 @@ -import Foundation - extension Mapper { + /// Checks whether the JSON data has a `data` key at the root. - /// func hasDataEnvelope(in response: Data) -> Bool { - let decoder = JSONDecoder() do { - _ = try decoder.decode(ContentEnvelope.self, from: response) + _ = try JSONDecoder().decode(Envelope<AnyDecodable>.self, from: response) return true } catch { return false } } } - -/// Helper struct to attempt parsing some JSON data with a `data` key at the root. -/// -private struct ContentEnvelope: Decodable { - let content: AnyDecodable - - private enum CodingKeys: String, CodingKey { - case content = "data" - } -} diff --git a/Networking/Networking/Mapper/AccountMapper.swift b/Networking/Networking/Mapper/AccountMapper.swift index 20411c9b503..b7b886cdec9 100644 --- a/Networking/Networking/Mapper/AccountMapper.swift +++ b/Networking/Networking/Mapper/AccountMapper.swift @@ -1,6 +1,3 @@ -import Foundation - - /// Mapper: Account /// class AccountMapper: Mapper { diff --git a/Networking/Networking/Mapper/AccountSettingsMapper.swift b/Networking/Networking/Mapper/AccountSettingsMapper.swift index f98a369bb60..703f41f99d5 100644 --- a/Networking/Networking/Mapper/AccountSettingsMapper.swift +++ b/Networking/Networking/Mapper/AccountSettingsMapper.swift @@ -1,6 +1,3 @@ -import Foundation - - /// Mapper: AccountSettings /// struct AccountSettingsMapper: Mapper { diff --git a/Networking/Networking/Mapper/AddOnGroupMapper.swift b/Networking/Networking/Mapper/AddOnGroupMapper.swift index faaa7f4fe70..464d23d3979 100644 --- a/Networking/Networking/Mapper/AddOnGroupMapper.swift +++ b/Networking/Networking/Mapper/AddOnGroupMapper.swift @@ -1,27 +1 @@ -import Foundation - -/// Maps between a raw json response to an array of `AddOnGroups` -/// -struct AddOnGroupMapper: Mapper { - /// Site Identifier associated to the `AddOnGroup` that will be parsed. - /// We're injecting this field via `JSONDecoder.userInfo` because `SiteID` is not returned in any of the `AddOnGroup` endpoints. - /// - let siteID: Int64 - - func map(response: Data) throws -> [AddOnGroup] { - let decoder = JSONDecoder() - decoder.userInfo = [.siteID: siteID] - if hasDataEnvelope(in: response) { - return try decoder.decode(AddOnGroupEnvelope.self, from: response).data - } else { - return try decoder.decode([AddOnGroup].self, from: response) - } - } -} - -/// `AddOnGroupEnvelope` Disposable Entity: -/// `AddOnGroup` endpoints returns it's add-on-groups json in the `data` key. -/// -private struct AddOnGroupEnvelope: Decodable { - let data: [AddOnGroup] -} +typealias AddOnGroupMapper = SiteIDMapper<[AddOnGroup]> diff --git a/Networking/Networking/Mapper/AnnouncementListMapper.swift b/Networking/Networking/Mapper/AnnouncementListMapper.swift index e1ded913642..44442becaec 100644 --- a/Networking/Networking/Mapper/AnnouncementListMapper.swift +++ b/Networking/Networking/Mapper/AnnouncementListMapper.swift @@ -1,5 +1,3 @@ -import Foundation - /// Mapper for `[Announcement]` struct AnnouncementListMapper: Mapper { diff --git a/Networking/Networking/Mapper/CommentResultMapper.swift b/Networking/Networking/Mapper/CommentResultMapper.swift index 11e03ed320e..7c48e8aaa6f 100644 --- a/Networking/Networking/Mapper/CommentResultMapper.swift +++ b/Networking/Networking/Mapper/CommentResultMapper.swift @@ -1,6 +1,3 @@ -import Foundation - - /// Mapper: Comment Moderation Result /// struct CommentResultMapper: Mapper { @@ -8,7 +5,6 @@ struct CommentResultMapper: Mapper { /// (Attempts) to extract the updated `status` field from a given JSON Encoded response. /// func map(response: Data) throws -> CommentStatus { - let dictionary = try JSONDecoder().decode([String: AnyDecodable].self, from: response) let status = (dictionary[Constants.statusKey]?.value as? String) ?? "" return CommentStatus(rawValue: status) ?? .unknown diff --git a/Networking/Networking/Mapper/CountryListMapper.swift b/Networking/Networking/Mapper/CountryListMapper.swift index dc7e7f00934..92916f96d49 100644 --- a/Networking/Networking/Mapper/CountryListMapper.swift +++ b/Networking/Networking/Mapper/CountryListMapper.swift @@ -1,29 +1,8 @@ -import Foundation - - -/// Mapper: Country Collection -/// struct CountryListMapper: Mapper { /// (Attempts) to convert an instance of Data into an array of Country Entities. /// func map(response: Data) throws -> [Country] { - if hasDataEnvelope(in: response) { - return try JSONDecoder().decode(CountryListEnvelope.self, from: response).data - } else { - return try JSONDecoder().decode([Country].self, from: response) - } - } -} - - -/// CountryListEnvelope Disposable Entity: -/// This entity allows us to parse [Country] with JSONDecoder. -/// -private struct CountryListEnvelope: Decodable { - let data: [Country] - - private enum CodingKeys: String, CodingKey { - case data + try extract(from: response) } } diff --git a/Networking/Networking/Mapper/CouponListMapper.swift b/Networking/Networking/Mapper/CouponListMapper.swift index 757ccb3ab23..51c94e3a2e6 100644 --- a/Networking/Networking/Mapper/CouponListMapper.swift +++ b/Networking/Networking/Mapper/CouponListMapper.swift @@ -1,5 +1,3 @@ -import Foundation - /// Mapper: `Coupon` List /// struct CouponListMapper: Mapper { @@ -13,7 +11,7 @@ struct CouponListMapper: Mapper { func map(response: Data) throws -> [Coupon] { let decoder = Coupon.decoder if hasDataEnvelope(in: response) { - let coupons = try decoder.decode(CouponListEnvelope.self, from: response).coupons + let coupons = try decoder.decode(Envelope<[Coupon]>.self, from: response).data return coupons.map { $0.copy(siteID: siteID) } } else { return try decoder.decode([Coupon].self, from: response) @@ -21,16 +19,3 @@ struct CouponListMapper: Mapper { } } } - - -/// CouponListEnvelope Disposable Entity: -/// Load All Coupons endpoint returns the coupons in the `data` key. -/// This entity allows us to parse all the things with JSONDecoder. -/// -private struct CouponListEnvelope: Decodable { - let coupons: [Coupon] - - private enum CodingKeys: String, CodingKey { - case coupons = "data" - } -} diff --git a/Networking/Networking/Mapper/CouponMapper.swift b/Networking/Networking/Mapper/CouponMapper.swift index adb3927fc33..8b0dd0d531b 100644 --- a/Networking/Networking/Mapper/CouponMapper.swift +++ b/Networking/Networking/Mapper/CouponMapper.swift @@ -1,5 +1,3 @@ -import Foundation - /// Mapper: `Coupon` /// struct CouponMapper: Mapper { @@ -13,23 +11,10 @@ struct CouponMapper: Mapper { func map(response: Data) throws -> Coupon { let decoder = Coupon.decoder if hasDataEnvelope(in: response) { - let coupon = try decoder.decode(CouponEnvelope.self, from: response).coupon + let coupon = try decoder.decode(Envelope<Coupon>.self, from: response).data return coupon.copy(siteID: siteID) } else { return try decoder.decode(Coupon.self, from: response).copy(siteID: siteID) } } } - - -/// CouponEnvelope Disposable Entity: -/// Load Coupon endpoint returns the coupon in the `data` key. -/// This entity allows us to parse all the things with JSONDecoder. -/// -private struct CouponEnvelope: Decodable { - let coupon: Coupon - - private enum CodingKeys: String, CodingKey { - case coupon = "data" - } -} diff --git a/Networking/Networking/Mapper/CouponReportListMapper.swift b/Networking/Networking/Mapper/CouponReportListMapper.swift index 0dbdec1a45d..d73ff139dde 100644 --- a/Networking/Networking/Mapper/CouponReportListMapper.swift +++ b/Networking/Networking/Mapper/CouponReportListMapper.swift @@ -1,30 +1,8 @@ -import Foundation - -/// Mapper: `CouponReport` -/// struct CouponReportListMapper: Mapper { /// (Attempts) to convert a dictionary into `[CouponReport]`. /// func map(response: Data) throws -> [CouponReport] { - let decoder = JSONDecoder() - if hasDataEnvelope(in: response) { - return try decoder.decode(CouponReportsEnvelope.self, from: response).reports - } else { - return try decoder.decode([CouponReport].self, from: response) - } - } -} - - -/// CouponReportsEnvelope Disposable Entity: -/// Load Coupon endpoint returns the coupon in the `data` key. -/// This entity allows us to parse all the things with JSONDecoder. -/// -private struct CouponReportsEnvelope: Decodable { - let reports: [CouponReport] - - private enum CodingKeys: String, CodingKey { - case reports = "data" + try extract(from: response) } } diff --git a/Networking/Networking/Mapper/CustomerMapper.swift b/Networking/Networking/Mapper/CustomerMapper.swift index cef9b807b5d..84044c1c5bb 100644 --- a/Networking/Networking/Mapper/CustomerMapper.swift +++ b/Networking/Networking/Mapper/CustomerMapper.swift @@ -1,29 +1 @@ -import Foundation - -/// Mapper: Customer -/// -struct CustomerMapper: Mapper { - /// We're injecting this field by copying it in after parsing responses, because `siteID` is not returned in any of the Customer endpoints. - /// - let siteID: Int64 - - /// (Attempts) to convert a dictionary into a `Customer` entity - /// - func map(response: Data) throws -> Customer { - let decoder = JSONDecoder() - decoder.userInfo = [.siteID: siteID] - if hasDataEnvelope(in: response) { - return try decoder.decode(CustomerEnvelope.self, from: response).customer - } else { - return try decoder.decode(Customer.self, from: response) - } - } -} - -private struct CustomerEnvelope: Decodable { - let customer: Customer - - private enum CodingKeys: String, CodingKey { - case customer = "data" - } -} +typealias CustomerMapper = SiteIDMapper<Customer> diff --git a/Networking/Networking/Mapper/DataBoolMapper.swift b/Networking/Networking/Mapper/DataBoolMapper.swift index d731855ab77..f5ee46fcfd4 100644 --- a/Networking/Networking/Mapper/DataBoolMapper.swift +++ b/Networking/Networking/Mapper/DataBoolMapper.swift @@ -1,29 +1,8 @@ -import Foundation - -/// Mapper: Bool Result, Wrapped in `data` Key or not -/// struct DataBoolMapper: Mapper { /// (Attempts) to extract the boolean flag from a given JSON Encoded response. /// func map(response: Data) throws -> Bool { - if hasDataEnvelope(in: response) { - return try JSONDecoder().decode(DataBool.self, from: response).data - } else { - return try JSONDecoder().decode(Bool.self, from: response) - } - } -} - -/// DataBoolResultEnvelope Disposable Entity -/// -/// Some endpoints return a Bool response in the `data` key. This entity -/// allows us to parse that response with JSONDecoder. -/// -private struct DataBool: Decodable { - let data: Bool - - private enum CodingKeys: String, CodingKey { - case data + try extract(from: response) } } diff --git a/Networking/Networking/Mapper/DotcomDeviceMapper.swift b/Networking/Networking/Mapper/DotcomDeviceMapper.swift index 21d7779af1a..67be99d702d 100644 --- a/Networking/Networking/Mapper/DotcomDeviceMapper.swift +++ b/Networking/Networking/Mapper/DotcomDeviceMapper.swift @@ -1,6 +1,3 @@ -import Foundation - - /// Mapper: Dotcom Device /// struct DotcomDeviceMapper: Mapper { diff --git a/Networking/Networking/Mapper/EntityDateModifiedMapper.swift b/Networking/Networking/Mapper/EntityDateModifiedMapper.swift index 5ed33ce670b..be3e13038b1 100644 --- a/Networking/Networking/Mapper/EntityDateModifiedMapper.swift +++ b/Networking/Networking/Mapper/EntityDateModifiedMapper.swift @@ -1,5 +1,3 @@ -import Foundation - /// Mapper: Date Modified for an entity, Wrapped in `data` Key or not /// struct EntityDateModifiedMapper: Mapper { @@ -10,22 +8,14 @@ struct EntityDateModifiedMapper: Mapper { let decoder = JSONDecoder() decoder.dateDecodingStrategy = .formatted(DateFormatter.Defaults.dateTimeFormatter) + let entity: ModifiedEntity if hasDataEnvelope(in: response) { - return try decoder.decode(ModifiedEntityEnvelope.self, from: response).modifiedEntity.dateModified + entity = try decoder.decode(Envelope<ModifiedEntity>.self, from: response).data } else { - return try decoder.decode(ModifiedEntity.self, from: response).dateModified + entity = try decoder.decode(ModifiedEntity.self, from: response) } - } -} - -/// Disposable Entity: -/// Allows us to parse the date modified with JSONDecoder. -/// -private struct ModifiedEntityEnvelope: Decodable { - let modifiedEntity: ModifiedEntity - private enum CodingKeys: String, CodingKey { - case modifiedEntity = "data" + return entity.dateModified } } diff --git a/Networking/Networking/Mapper/EntityIDMapper.swift b/Networking/Networking/Mapper/EntityIDMapper.swift index a061df86aed..a6a902ea296 100644 --- a/Networking/Networking/Mapper/EntityIDMapper.swift +++ b/Networking/Networking/Mapper/EntityIDMapper.swift @@ -1,4 +1,4 @@ -import Foundation +private typealias EntityIDDictionary = [String: Int64] /// Mapper: Single Entity ID /// @@ -9,37 +9,13 @@ struct EntityIDMapper: Mapper { func map(response: Data) throws -> Int64 { let decoder = JSONDecoder() + let idDictionary: EntityIDDictionary if hasDataEnvelope(in: response) { - return try decoder.decode(EntityIDEnvelope.self, from: response).id + idDictionary = try decoder.decode(Envelope<EntityIDDictionary>.self, from: response).data } else { - let idDictionary = try decoder.decode(EntityIDEnvelope.EntityIDDictionaryType.self, from: response) - return idDictionary[Constants.idKey] ?? .zero + idDictionary = try decoder.decode(EntityIDDictionary.self, from: response) } - } -} - -// MARK: Constants -// -private extension EntityIDMapper { - enum Constants { - static let idKey = "id" - } -} - -/// Disposable Entity: -/// Allows us to parse a product ID with JSONDecoder. -/// -private struct EntityIDEnvelope: Decodable { - typealias EntityIDDictionaryType = [String: Int64] - - private let data: EntityIDDictionaryType - - // Extracts the entity ID from the underlying data - var id: Int64 { - data[EntityIDMapper.Constants.idKey] ?? .zero - } - private enum CodingKeys: String, CodingKey { - case data = "data" + return idDictionary["id"] ?? .zero } } diff --git a/Networking/Networking/Mapper/InboxNoteListMapper.swift b/Networking/Networking/Mapper/InboxNoteListMapper.swift index d6d027fad58..39caacbda0e 100644 --- a/Networking/Networking/Mapper/InboxNoteListMapper.swift +++ b/Networking/Networking/Mapper/InboxNoteListMapper.swift @@ -18,20 +18,9 @@ struct InboxNoteListMapper: Mapper { .siteID: siteID ] if hasDataEnvelope(in: response) { - return try decoder.decode(InboxNoteListEnvelope.self, from: response).data + return try decoder.decode(Envelope<[InboxNote]>.self, from: response).data } else { return try decoder.decode([InboxNote].self, from: response) } } } - -/// InboxNoteListEnvelope Disposable Entity: -/// This entity allows us to parse [InboxNote] with JSONDecoder. -/// -private struct InboxNoteListEnvelope: Decodable { - let data: [InboxNote] - - private enum CodingKeys: String, CodingKey { - case data - } -} diff --git a/Networking/Networking/Mapper/InboxNoteMapper.swift b/Networking/Networking/Mapper/InboxNoteMapper.swift index 72f14b88e37..e917fe7c094 100644 --- a/Networking/Networking/Mapper/InboxNoteMapper.swift +++ b/Networking/Networking/Mapper/InboxNoteMapper.swift @@ -18,20 +18,9 @@ struct InboxNoteMapper: Mapper { .siteID: siteID ] if hasDataEnvelope(in: response) { - return try decoder.decode(InboxNoteEnvelope.self, from: response).data + return try decoder.decode(Envelope<InboxNote>.self, from: response).data } else { return try decoder.decode(InboxNote.self, from: response) } } } - -/// InboxNoteEnvelope Disposable Entity: -/// This entity allows us to parse InboxNote with JSONDecoder. -/// -private struct InboxNoteEnvelope: Decodable { - let data: InboxNote - - private enum CodingKeys: String, CodingKey { - case data - } -} diff --git a/Networking/Networking/Mapper/JustInTimeMessageListMapper.swift b/Networking/Networking/Mapper/JustInTimeMessageListMapper.swift index 9e62b869161..1431d0b56f3 100644 --- a/Networking/Networking/Mapper/JustInTimeMessageListMapper.swift +++ b/Networking/Networking/Mapper/JustInTimeMessageListMapper.swift @@ -17,20 +17,9 @@ struct JustInTimeMessageListMapper: Mapper { .siteID: siteID ] if hasDataEnvelope(in: response) { - return try decoder.decode(JustInTimeMessageListEnvelope.self, from: response).data + return try decoder.decode(Envelope<[JustInTimeMessage]>.self, from: response).data } else { return try decoder.decode([JustInTimeMessage].self, from: response) } } } - -/// JustInTimeMessageEnvelope Disposable Entity: -/// This entity allows us to parse JustInTimeMessage with JSONDecoder. -/// -private struct JustInTimeMessageListEnvelope: Decodable { - let data: [JustInTimeMessage] - - private enum CodingKeys: String, CodingKey { - case data - } -} diff --git a/Networking/Networking/Mapper/Mapper.swift b/Networking/Networking/Mapper/Mapper.swift index 6f062d95691..259b24cd04c 100644 --- a/Networking/Networking/Mapper/Mapper.swift +++ b/Networking/Networking/Mapper/Mapper.swift @@ -1,6 +1,3 @@ -import Foundation - - /// Defines a Mapping Entity that will be used to parse a Backend Response. /// protocol Mapper { @@ -13,3 +10,49 @@ protocol Mapper { /// func map(response: Data) throws -> Output } + +extension Mapper where Output: Decodable { + + func extract(from response: Data, siteID: Int64, dateFormatter: DateFormatter? = .none) throws -> Output { + return try extract( + from: response, + decodingUserInfo: [.siteID: siteID], + dateFormatter: dateFormatter + ) + } + + func extract(from response: Data, decodingUserInfo: [CodingUserInfoKey: Any], dateFormatter: DateFormatter? = .none) throws -> Output { + let decoder = JSONDecoder() + decoder.userInfo = decodingUserInfo + if let dateFormatter { + decoder.dateDecodingStrategy = .formatted(dateFormatter) + } + + return try extract(from: response, using: decoder) + } + + func extract(from response: Data, using decoder: JSONDecoder = JSONDecoder()) throws -> Output { + if hasDataEnvelope(in: response) { + return try decoder.decode(Envelope<Output>.self, from: response).data + } else { + return try decoder.decode(Output.self, from: response) + } + } +} + +/// A `Mapper` implementation for resources using a site id and default date formatter +struct SiteIDMapper<Resource: Decodable>: Mapper { + + /// Site Identifier associated to the `Resource`s that will be parsed. + /// + /// We're injecting this field via `JSONDecoder.userInfo` because SiteID is not returned in any of the endpoints. + let siteID: Int64 + + func map(response: Data) throws -> Resource { + try extract( + from: response, + siteID: siteID, + dateFormatter: DateFormatter.Defaults.dateTimeFormatter + ) + } +} diff --git a/Networking/Networking/Mapper/NewShipmentTrackingMapper.swift b/Networking/Networking/Mapper/NewShipmentTrackingMapper.swift index 03565383f9c..0abbb826b90 100644 --- a/Networking/Networking/Mapper/NewShipmentTrackingMapper.swift +++ b/Networking/Networking/Mapper/NewShipmentTrackingMapper.swift @@ -1,5 +1,3 @@ -/// Mapper: NewShipmentTrackingMapper -/// struct NewShipmentTrackingMapper: Mapper { /// Site Identifier associated to the shipment trackings that will be parsed. /// We're injecting this field via `JSONDecoder.userInfo` because the remote endpoints don't @@ -16,25 +14,13 @@ struct NewShipmentTrackingMapper: Mapper { /// (Attempts) to convert a dictionary into an ShipmentTracking entity. /// func map(response: Data) throws -> ShipmentTracking { - let decoder = JSONDecoder() - decoder.dateDecodingStrategy = .formatted(DateFormatter.Defaults.yearMonthDayDateFormatter) - decoder.userInfo = [ - .siteID: siteID, - .orderID: orderID - ] - - if hasDataEnvelope(in: response) { - return try decoder.decode(NewShipmentTrackingMapperEnvelope.self, from: response).shipmentTracking - } else { - return try decoder.decode(ShipmentTracking.self, from: response) - } - } -} - -private struct NewShipmentTrackingMapperEnvelope: Decodable { - let shipmentTracking: ShipmentTracking - - private enum CodingKeys: String, CodingKey { - case shipmentTracking = "data" + return try extract( + from: response, + decodingUserInfo: [ + .siteID: siteID, + .orderID: orderID + ], + dateFormatter: DateFormatter.Defaults.yearMonthDayDateFormatter + ) } } diff --git a/Networking/Networking/Mapper/OrderListMapper.swift b/Networking/Networking/Mapper/OrderListMapper.swift index b269d3f0f84..90c0c260e63 100644 --- a/Networking/Networking/Mapper/OrderListMapper.swift +++ b/Networking/Networking/Mapper/OrderListMapper.swift @@ -1,43 +1 @@ -import Foundation - - -/// Mapper: OrderList -/// -struct OrderListMapper: Mapper { - - /// Site Identifier associated to the orders that will be parsed. - /// - /// We're injecting this field via `JSONDecoder.userInfo` because SiteID is not returned in any of the Order Endpoints. - /// - let siteID: Int64 - - - /// (Attempts) to convert a dictionary into [Order]. - /// - func map(response: Data) throws -> [Order] { - let decoder = JSONDecoder() - decoder.dateDecodingStrategy = .formatted(DateFormatter.Defaults.dateTimeFormatter) - decoder.userInfo = [ - .siteID: siteID - ] - - if hasDataEnvelope(in: response) { - return try decoder.decode(OrderListEnvelope.self, from: response).orders - } else { - return try decoder.decode([Order].self, from: response) - } - } -} - - -/// OrderList Disposable Entity: -/// `Load All Orders` endpoint returns all of its orders within the `data` key. This entity -/// allows us to do parse all the things with JSONDecoder. -/// -private struct OrderListEnvelope: Decodable { - let orders: [Order] - - private enum CodingKeys: String, CodingKey { - case orders = "data" - } -} +typealias OrderListMapper = SiteIDMapper<[Order]> diff --git a/Networking/Networking/Mapper/OrderMapper.swift b/Networking/Networking/Mapper/OrderMapper.swift index addf4440aa7..68085f64e28 100644 --- a/Networking/Networking/Mapper/OrderMapper.swift +++ b/Networking/Networking/Mapper/OrderMapper.swift @@ -1,43 +1 @@ -import Foundation - - -/// Mapper: Order -/// -struct OrderMapper: Mapper { - - /// Site Identifier associated to the order that will be parsed. - /// - /// We're injecting this field via `JSONDecoder.userInfo` because SiteID is not returned in any of the Order Endpoints. - /// - let siteID: Int64 - - - /// (Attempts) to convert a dictionary into [Order]. - /// - func map(response: Data) throws -> Order { - let decoder = JSONDecoder() - decoder.dateDecodingStrategy = .formatted(DateFormatter.Defaults.dateTimeFormatter) - decoder.userInfo = [ - .siteID: siteID - ] - if hasDataEnvelope(in: response) { - return try decoder.decode(OrderEnvelope.self, from: response).order - } else { - return try decoder.decode(Order.self, from: response) - } - } -} - - -/// OrdersEnvelope Disposable Entity -/// -/// `Load Order` endpoint returns the requested order document in the `data` key. This entity -/// allows us to do parse all the things with JSONDecoder. -/// -private struct OrderEnvelope: Decodable { - let order: Order - - private enum CodingKeys: String, CodingKey { - case order = "data" - } -} +typealias OrderMapper = SiteIDMapper<Order> diff --git a/Networking/Networking/Mapper/OrderNoteMapper.swift b/Networking/Networking/Mapper/OrderNoteMapper.swift index d93fae1b0fd..dc099bb2a45 100644 --- a/Networking/Networking/Mapper/OrderNoteMapper.swift +++ b/Networking/Networking/Mapper/OrderNoteMapper.swift @@ -1,6 +1,3 @@ -import Foundation - - /// Mapper: OrderNote (Singular) /// class OrderNoteMapper: Mapper { @@ -12,22 +9,9 @@ class OrderNoteMapper: Mapper { decoder.dateDecodingStrategy = .formatted(DateFormatter.Defaults.dateTimeFormatter) if hasDataEnvelope(in: response) { - return try decoder.decode(OrderNoteEnvelope.self, from: response).orderNote + return try decoder.decode(Envelope<OrderNote>.self, from: response).data } else { return try decoder.decode(OrderNote.self, from: response) } } } - - -/// OrderNote Disposable Entity: -/// `Add Order Note` endpoint the single added note within the `data` key. This entity -/// allows us to parse all the things with JSONDecoder. -/// -private struct OrderNoteEnvelope: Decodable { - let orderNote: OrderNote - - private enum CodingKeys: String, CodingKey { - case orderNote = "data" - } -} diff --git a/Networking/Networking/Mapper/OrderNotesMapper.swift b/Networking/Networking/Mapper/OrderNotesMapper.swift index 4401ad87c3f..4f923c10ded 100644 --- a/Networking/Networking/Mapper/OrderNotesMapper.swift +++ b/Networking/Networking/Mapper/OrderNotesMapper.swift @@ -1,6 +1,3 @@ -import Foundation - - /// Mapper: OrderNotes /// class OrderNotesMapper: Mapper { @@ -12,22 +9,9 @@ class OrderNotesMapper: Mapper { decoder.dateDecodingStrategy = .formatted(DateFormatter.Defaults.dateTimeFormatter) if hasDataEnvelope(in: response) { - return try decoder.decode(OrderNotesEnvelope.self, from: response).orderNotes + return try decoder.decode(Envelope<[OrderNote]>.self, from: response).data } else { return try decoder.decode([OrderNote].self, from: response) } } } - - -/// OrderNote Disposable Entity: -/// `Load Order Notes` endpoint returns all of its notes within the `data` key. This entity -/// allows us to do parse all the things with JSONDecoder. -/// -private struct OrderNotesEnvelope: Decodable { - let orderNotes: [OrderNote] - - private enum CodingKeys: String, CodingKey { - case orderNotes = "data" - } -} diff --git a/Networking/Networking/Mapper/OrderShippingLabelListMapper.swift b/Networking/Networking/Mapper/OrderShippingLabelListMapper.swift index 8cdfd1e6ca7..baededaef74 100644 --- a/Networking/Networking/Mapper/OrderShippingLabelListMapper.swift +++ b/Networking/Networking/Mapper/OrderShippingLabelListMapper.swift @@ -1,5 +1,3 @@ -import Foundation - /// A wrapper of shipping labels and settings from `Load Shipping Labels` response. public struct OrderShippingLabelListResponse { /// A list of shipping labels. @@ -32,25 +30,14 @@ struct OrderShippingLabelListMapper: Mapper { .orderID: orderID ] - let data: OrderShippingLabelListData = try { - if hasDataEnvelope(in: response) { - return try decoder.decode(OrderShippingLabelListEnvelope.self, from: response).data - } else { - return try decoder.decode(OrderShippingLabelListData.self, from: response) - } - }() - return OrderShippingLabelListResponse(shippingLabels: data.shippingLabels, settings: data.settings) - } -} - -/// Disposable Entity: -/// `Load Shipping Labels` endpoint returns the data in the `data` key. -/// -private struct OrderShippingLabelListEnvelope: Decodable { - let data: OrderShippingLabelListData + let data: OrderShippingLabelListData + if hasDataEnvelope(in: response) { + data = try decoder.decode(Envelope<OrderShippingLabelListData>.self, from: response).data + } else { + data = try decoder.decode(OrderShippingLabelListData.self, from: response) + } - private enum CodingKeys: String, CodingKey { - case data = "data" + return OrderShippingLabelListResponse(shippingLabels: data.shippingLabels, settings: data.settings) } } diff --git a/Networking/Networking/Mapper/OrderStatsV4Mapper.swift b/Networking/Networking/Mapper/OrderStatsV4Mapper.swift index 47f6849a56b..40bc8b695ab 100644 --- a/Networking/Networking/Mapper/OrderStatsV4Mapper.swift +++ b/Networking/Networking/Mapper/OrderStatsV4Mapper.swift @@ -1,6 +1,3 @@ -import Foundation - - /// Mapper: OrderStats /// struct OrderStatsV4Mapper: Mapper { @@ -19,29 +16,9 @@ struct OrderStatsV4Mapper: Mapper { /// (Attempts) to convert a dictionary into an OrderStats entity. /// func map(response: Data) throws -> OrderStatsV4 { - let decoder = JSONDecoder() - decoder.userInfo = [ - .siteID: siteID, - .granularity: granularity - ] - if hasDataEnvelope(in: response) { - return try decoder.decode(OrderStatsV4Envelope.self, from: response).orderStats - } else { - return try decoder.decode(OrderStatsV4.self, from: response) - } - } -} - - -/// OrderStatsV4Envelope Disposable Entity -/// -/// `Order Stats` endpoint returns the requested stats in the `data` key. This entity -/// allows us to parse all the things with JSONDecoder. -/// -private struct OrderStatsV4Envelope: Decodable { - let orderStats: OrderStatsV4 - - private enum CodingKeys: String, CodingKey { - case orderStats = "data" + try extract( + from: response, + decodingUserInfo: [.siteID: siteID, .granularity: granularity] + ) } } diff --git a/Networking/Networking/Mapper/PaymentGatewayListMapper.swift b/Networking/Networking/Mapper/PaymentGatewayListMapper.swift index 02d3ec9b366..064077a57cc 100644 --- a/Networking/Networking/Mapper/PaymentGatewayListMapper.swift +++ b/Networking/Networking/Mapper/PaymentGatewayListMapper.swift @@ -1,5 +1,3 @@ -import Foundation - /// Mapper for an array of `PaymentGateway` JSON objects /// struct PaymentGatewayListMapper: Mapper { @@ -13,26 +11,6 @@ struct PaymentGatewayListMapper: Mapper { /// (Attempts) to convert a dictionary into `[PaymentGateway]` /// func map(response: Data) throws -> [PaymentGateway] { - let decoder = JSONDecoder() - decoder.userInfo = [ - .siteID: siteID, - ] - if hasDataEnvelope(in: response) { - return try decoder.decode(PaymentGatewayListEnvelope.self, from: response).paymentGateways - } else { - return try decoder.decode([PaymentGateway].self, from: response) - } - } -} - -/// PaymentGateway list disposable entity: -/// `Load Payment Gateways` endpoint returns all of the gateway information within a `body` obejcts in the `data` key. This entity -/// allows us to parse all the things with JSONDecoder. -/// -private struct PaymentGatewayListEnvelope: Decodable { - private enum CodingKeys: String, CodingKey { - case paymentGateways = "data" + try extract(from: response, siteID: siteID) } - - let paymentGateways: [PaymentGateway] } diff --git a/Networking/Networking/Mapper/PaymentGatewayMapper.swift b/Networking/Networking/Mapper/PaymentGatewayMapper.swift index b5ec5d39d03..88db9c5ec2b 100644 --- a/Networking/Networking/Mapper/PaymentGatewayMapper.swift +++ b/Networking/Networking/Mapper/PaymentGatewayMapper.swift @@ -1,5 +1,3 @@ -import Foundation - /// Mapper for a `PaymentGateway` JSON object /// struct PaymentGatewayMapper: Mapper { @@ -18,21 +16,9 @@ struct PaymentGatewayMapper: Mapper { .siteID: siteID, ] if hasDataEnvelope(in: response) { - return try decoder.decode(PaymentGatewayEnvelope.self, from: response).paymentGateway + return try decoder.decode(Envelope<PaymentGateway>.self, from: response).data } else { return try decoder.decode(PaymentGateway.self, from: response) } } } - -/// PaymentGateway list disposable entity: -/// `Load Payment Gateway` endpoint returns all of the gateway information within a `body` obejcts in the `data` key. This entity -/// allows us to parse all the things with JSONDecoder. -/// -private struct PaymentGatewayEnvelope: Decodable { - private enum CodingKeys: String, CodingKey { - case paymentGateway = "data" - } - - let paymentGateway: PaymentGateway -} diff --git a/Networking/Networking/Mapper/ProductAttributeListMapper.swift b/Networking/Networking/Mapper/ProductAttributeListMapper.swift index 14042c643f6..9c35e497cb6 100644 --- a/Networking/Networking/Mapper/ProductAttributeListMapper.swift +++ b/Networking/Networking/Mapper/ProductAttributeListMapper.swift @@ -1,39 +1 @@ -import Foundation - -/// Mapper: ProductAttribute List -/// -struct ProductAttributeListMapper: Mapper { - /// Site Identifier associated to the `ProductAttribute`s that will be parsed. - /// - /// We're injecting this field via `JSONDecoder.userInfo` because SiteID is not returned in any of the ProductAttribute Endpoints. - /// - let siteID: Int64 - - /// (Attempts) to convert a dictionary into [ProductAttribute]. - /// - func map(response: Data) throws -> [ProductAttribute] { - let decoder = JSONDecoder() - decoder.userInfo = [ - .siteID: siteID - ] - - if hasDataEnvelope(in: response) { - return try decoder.decode(ProductAttributeListEnvelope.self, from: response).productAttributes - } else { - return try decoder.decode([ProductAttribute].self, from: response) - } - } -} - - -/// ProductAttributeListEnvelope Disposable Entity: -/// `Load All Products Attributes` endpoint returns the updated products document in the `data` key. -/// This entity allows us to do parse all the things with JSONDecoder. -/// -private struct ProductAttributeListEnvelope: Decodable { - let productAttributes: [ProductAttribute] - - private enum CodingKeys: String, CodingKey { - case productAttributes = "data" - } -} +typealias ProductAttributeListMapper = SiteIDMapper<[ProductAttribute]> diff --git a/Networking/Networking/Mapper/ProductAttributeMapper.swift b/Networking/Networking/Mapper/ProductAttributeMapper.swift index 7d9ce2a543a..19e4398616c 100644 --- a/Networking/Networking/Mapper/ProductAttributeMapper.swift +++ b/Networking/Networking/Mapper/ProductAttributeMapper.swift @@ -1,6 +1,3 @@ -import Foundation - - /// Mapper: ProductAttribute /// struct ProductAttributeMapper: Mapper { @@ -20,23 +17,6 @@ struct ProductAttributeMapper: Mapper { .siteID: siteID ] - if hasDataEnvelope(in: response) { - return try decoder.decode(ProductAttributeEnvelope.self, from: response).productAttribute - } else { - return try decoder.decode(ProductAttribute.self, from: response) - } - } -} - - -/// ProductAttributeEnvelope Disposable Entity: -/// `Load Product Attribute` endpoint returns the updated products document in the `data` key. -/// This entity allows us to do parse all the things with JSONDecoder. -/// -private struct ProductAttributeEnvelope: Decodable { - let productAttribute: ProductAttribute - - private enum CodingKeys: String, CodingKey { - case productAttribute = "data" + return try extract(from: response, using: decoder) } } diff --git a/Networking/Networking/Mapper/ProductAttributeTermListMapper.swift b/Networking/Networking/Mapper/ProductAttributeTermListMapper.swift index ce79ed9c5b5..468df9830c9 100644 --- a/Networking/Networking/Mapper/ProductAttributeTermListMapper.swift +++ b/Networking/Networking/Mapper/ProductAttributeTermListMapper.swift @@ -1,38 +1 @@ -import Foundation - -/// Mapper: ProductAttributeTerm List -/// -struct ProductAttributeTermListMapper: Mapper { - /// Site Identifier associated to the `ProductAttributeTermList`s that will be parsed. - /// - /// We're injecting this field via `JSONDecoder.userInfo` because SiteID is not returned in any of the ProductAttributeTerm Endpoints. - /// - let siteID: Int64 - - /// (Attempts) to convert a dictionary into `[ProductAttributeTerm]`. - /// - func map(response: Data) throws -> [ProductAttributeTerm] { - let decoder = JSONDecoder() - decoder.userInfo = [ - .siteID: siteID - ] - - if hasDataEnvelope(in: response) { - return try decoder.decode(ProductAttributeTermListEnvelope.self, from: response).productAttributeTerms - } else { - return try decoder.decode([ProductAttributeTerm].self, from: response) - } - } -} - -/// ProductAttributeTermListEnvelope Disposable Entity: -/// `Load All ProductsAttributeTerm` endpoint returns the updated products document in the `data` key. -/// This entity allows us to do parse all the things with JSONDecoder. -/// -private struct ProductAttributeTermListEnvelope: Decodable { - let productAttributeTerms: [ProductAttributeTerm] - - private enum CodingKeys: String, CodingKey { - case productAttributeTerms = "data" - } -} +typealias ProductAttributeTermListMapper = SiteIDMapper<[ProductAttributeTerm]> diff --git a/Networking/Networking/Mapper/ProductAttributeTermMapper.swift b/Networking/Networking/Mapper/ProductAttributeTermMapper.swift index fc3ae5b3708..018ecb2c425 100644 --- a/Networking/Networking/Mapper/ProductAttributeTermMapper.swift +++ b/Networking/Networking/Mapper/ProductAttributeTermMapper.swift @@ -1,40 +1 @@ -import Foundation - -/// Mapper: ProductAttributeTerm -/// -struct ProductAttributeTermMapper: Mapper { - - /// Site Identifier associated to the `ProductAttributeTerm`s that will be parsed. - /// - /// We're injecting this field via `JSONDecoder.userInfo` because SiteID is not returned in any of the `ProductAttributeTerm` Endpoints. - /// - let siteID: Int64 - - /// (Attempts) to convert a dictionary into `ProductAttributeTerm`. - /// - func map(response: Data) throws -> ProductAttributeTerm { - let decoder = JSONDecoder() - decoder.userInfo = [ - .siteID: siteID - ] - - if hasDataEnvelope(in: response) { - return try decoder.decode(ProductAttributeTermEnvelope.self, from: response).productAttributeTerm - } else { - return try decoder.decode(ProductAttributeTerm.self, from: response) - } - } -} - - -/// ProductAttributeTermEnvelope Disposable Entity: -/// `Load ProductProductAttributeTerm` endpoint returns the updated products document in the `data` key. -/// This entity allows us to do parse all the things with JSONDecoder. -/// -private struct ProductAttributeTermEnvelope: Decodable { - let productAttributeTerm: ProductAttributeTerm - - private enum CodingKeys: String, CodingKey { - case productAttributeTerm = "data" - } -} +typealias ProductAttributeTermMapper = SiteIDMapper<ProductAttributeTerm> diff --git a/Networking/Networking/Mapper/ProductCategoryListMapper.swift b/Networking/Networking/Mapper/ProductCategoryListMapper.swift index a710abab2d6..603328b50a7 100644 --- a/Networking/Networking/Mapper/ProductCategoryListMapper.swift +++ b/Networking/Networking/Mapper/ProductCategoryListMapper.swift @@ -1,39 +1 @@ -import Foundation - -/// Mapper: ProductCategory List -/// -struct ProductCategoryListMapper: Mapper { - /// Site Identifier associated to the `ProductCategories`s that will be parsed. - /// - /// We're injecting this field via `JSONDecoder.userInfo` because SiteID is not returned in any of the ProductCategory Endpoints. - /// - let siteID: Int64 - - /// (Attempts) to convert a dictionary into [ProductCategory]. - /// - func map(response: Data) throws -> [ProductCategory] { - let decoder = JSONDecoder() - decoder.userInfo = [ - .siteID: siteID - ] - - if hasDataEnvelope(in: response) { - return try decoder.decode(ProductCategoryListEnvelope.self, from: response).productCategories - } else { - return try decoder.decode([ProductCategory].self, from: response) - } - } -} - - -/// ProductCategoryListEnvelope Disposable Entity: -/// `Load All Products Categories` endpoint returns the updated products document in the `data` key. -/// This entity allows us to do parse all the things with JSONDecoder. -/// -private struct ProductCategoryListEnvelope: Decodable { - let productCategories: [ProductCategory] - - private enum CodingKeys: String, CodingKey { - case productCategories = "data" - } -} +typealias ProductCategoryListMapper = SiteIDMapper<[ProductCategory]> diff --git a/Networking/Networking/Mapper/ProductCategoryMapper.swift b/Networking/Networking/Mapper/ProductCategoryMapper.swift index b1c8b23c09b..c040e7dff76 100644 --- a/Networking/Networking/Mapper/ProductCategoryMapper.swift +++ b/Networking/Networking/Mapper/ProductCategoryMapper.swift @@ -1,42 +1 @@ -import Foundation - - -/// Mapper: ProductCategory -/// -struct ProductCategoryMapper: Mapper { - - /// Site Identifier associated to the `ProductCategory`s that will be parsed. - /// - /// We're injecting this field via `JSONDecoder.userInfo` because SiteID is not returned in any of the ProductCategory Endpoints. - /// - let siteID: Int64 - - - /// (Attempts) to convert a dictionary into ProductCategory. - /// - func map(response: Data) throws -> ProductCategory { - let decoder = JSONDecoder() - decoder.userInfo = [ - .siteID: siteID - ] - - if hasDataEnvelope(in: response) { - return try decoder.decode(ProductCategoryEnvelope.self, from: response).productCategory - } else { - return try decoder.decode(ProductCategory.self, from: response) - } - } -} - - -/// ProductCategoryEnvelope Disposable Entity: -/// `Load Product Category` endpoint returns the updated products document in the `data` key. -/// This entity allows us to do parse all the things with JSONDecoder. -/// -private struct ProductCategoryEnvelope: Decodable { - let productCategory: ProductCategory - - private enum CodingKeys: String, CodingKey { - case productCategory = "data" - } -} +typealias ProductCategoryMapper = SiteIDMapper<ProductCategory> diff --git a/Networking/Networking/Mapper/ProductIDMapper.swift b/Networking/Networking/Mapper/ProductIDMapper.swift index 53522b4724e..3ec710a5178 100644 --- a/Networking/Networking/Mapper/ProductIDMapper.swift +++ b/Networking/Networking/Mapper/ProductIDMapper.swift @@ -1,4 +1,4 @@ -import Foundation +private typealias ProductIDs = [[String: Int64]] /// Mapper: Product IDs /// @@ -9,32 +9,13 @@ struct ProductIDMapper: Mapper { func map(response: Data) throws -> [Int64] { let decoder = JSONDecoder() + let ids: ProductIDs if hasDataEnvelope(in: response) { - return try decoder.decode(ProductIDEnvelope.self, from: response).productIDs.compactMap { $0[Constants.idKey] } + ids = try decoder.decode(Envelope<ProductIDs>.self, from: response).data } else { - return try decoder.decode(ProductIDEnvelope.ProductIDs.self, from: response).compactMap { $0[Constants.idKey] } + ids = try decoder.decode(ProductIDs.self, from: response) } - } -} - -// MARK: Constants -// -private extension ProductIDMapper { - enum Constants { - static let idKey = "id" - } -} - -/// ProductIDEnvelope Disposable Entity: -/// `Products` endpoint returns if a product exists. This entity -/// allows us to parse the product IDs with JSONDecoder. -/// -private struct ProductIDEnvelope: Decodable { - typealias ProductIDs = [[String: Int64]] - - let productIDs: ProductIDs - private enum CodingKeys: String, CodingKey { - case productIDs = "data" + return ids.compactMap { $0["id"] } } } diff --git a/Networking/Networking/Mapper/ProductListMapper.swift b/Networking/Networking/Mapper/ProductListMapper.swift index c85fcd609e9..eb5629c2d8e 100644 --- a/Networking/Networking/Mapper/ProductListMapper.swift +++ b/Networking/Networking/Mapper/ProductListMapper.swift @@ -1,41 +1 @@ -import Foundation - - -/// Mapper: Product List -/// -struct ProductListMapper: Mapper { - /// Site Identifier associated to the products that will be parsed. - /// - /// We're injecting this field via `JSONDecoder.userInfo` because SiteID is not returned in any of the Product Endpoints. - /// - let siteID: Int64 - - /// (Attempts) to convert a dictionary into [Product]. - /// - func map(response: Data) throws -> [Product] { - let decoder = JSONDecoder() - decoder.dateDecodingStrategy = .formatted(DateFormatter.Defaults.dateTimeFormatter) - decoder.userInfo = [ - .siteID: siteID - ] - - if hasDataEnvelope(in: response) { - return try decoder.decode(ProductListEnvelope.self, from: response).products - } else { - return try decoder.decode([Product].self, from: response) - } - } -} - - -/// ProductEnvelope Disposable Entity: -/// `Load All Products` endpoint returns the updated products document in the `data` key. -/// This entity allows us to do parse all the things with JSONDecoder. -/// -private struct ProductListEnvelope: Decodable { - let products: [Product] - - private enum CodingKeys: String, CodingKey { - case products = "data" - } -} +typealias ProductListMapper = SiteIDMapper<[Product]> diff --git a/Networking/Networking/Mapper/ProductMapper.swift b/Networking/Networking/Mapper/ProductMapper.swift index 933fddfdca1..525d14c9e1e 100644 --- a/Networking/Networking/Mapper/ProductMapper.swift +++ b/Networking/Networking/Mapper/ProductMapper.swift @@ -1,6 +1,3 @@ -import Foundation - - /// Mapper: Product /// struct ProductMapper: Mapper { @@ -21,24 +18,6 @@ struct ProductMapper: Mapper { .siteID: siteID ] - if hasDataEnvelope(in: response) { - return try decoder.decode(ProductEnvelope.self, from: response).product - } else { - return try decoder.decode(Product.self, from: response) - } - } -} - - -/// ProductEnvelope Disposable Entity -/// -/// `Load Product` endpoint returns the requested product document in the `data` key. This entity -/// allows us to do parse all the things with JSONDecoder. -/// -private struct ProductEnvelope: Decodable { - let product: Product - - private enum CodingKeys: String, CodingKey { - case product = "data" + return try extract(from: response, using: decoder) } } diff --git a/Networking/Networking/Mapper/ProductReviewListMapper.swift b/Networking/Networking/Mapper/ProductReviewListMapper.swift index f7b5729c257..5723fafac5f 100644 --- a/Networking/Networking/Mapper/ProductReviewListMapper.swift +++ b/Networking/Networking/Mapper/ProductReviewListMapper.swift @@ -1,40 +1 @@ -import Foundation - -/// Mapper: Product Reviews List -/// -struct ProductReviewListMapper: Mapper { - /// Site Identifier associated to the product reviews that will be parsed. - /// - /// We're injecting this field via `JSONDecoder.userInfo` because SiteID is not returned in any of the Product Endpoints. - /// - let siteID: Int64 - - /// (Attempts) to convert a dictionary into [Product]. - /// - func map(response: Data) throws -> [ProductReview] { - let decoder = JSONDecoder() - decoder.dateDecodingStrategy = .formatted(DateFormatter.Defaults.dateTimeFormatter) - decoder.userInfo = [ - .siteID: siteID - ] - - if hasDataEnvelope(in: response) { - return try decoder.decode(ProductReviewListEnvelope.self, from: response).productReviews - } else { - return try decoder.decode([ProductReview].self, from: response) - } - } -} - - -/// ProductReviewListEnvelope Disposable Entity: -/// `Load All Products Reviews` endpoint returns the updated products document in the `data` key. -/// This entity allows us to do parse all the things with JSONDecoder. -/// -private struct ProductReviewListEnvelope: Decodable { - let productReviews: [ProductReview] - - private enum CodingKeys: String, CodingKey { - case productReviews = "data" - } -} +typealias ProductReviewListMapper = SiteIDMapper<[ProductReview]> diff --git a/Networking/Networking/Mapper/ProductReviewMapper.swift b/Networking/Networking/Mapper/ProductReviewMapper.swift index 4522045435c..d91899dd0d0 100644 --- a/Networking/Networking/Mapper/ProductReviewMapper.swift +++ b/Networking/Networking/Mapper/ProductReviewMapper.swift @@ -1,44 +1 @@ -import Foundation - - -/// Mapper: ProductReview -/// -struct ProductReviewMapper: Mapper { - - /// Site Identifier associated to the product review that will be parsed. - /// - /// We're injecting this field via `JSONDecoder.userInfo` because SiteID is not returned in any of the Product Endpoints. - /// - let siteID: Int64 - - - /// (Attempts) to convert a dictionary into ProductReview. - /// - func map(response: Data) throws -> ProductReview { - let decoder = JSONDecoder() - decoder.dateDecodingStrategy = .formatted(DateFormatter.Defaults.dateTimeFormatter) - decoder.userInfo = [ - .siteID: siteID - ] - - if hasDataEnvelope(in: response) { - return try decoder.decode(ProductReviewEnvelope.self, from: response).productReview - } else { - return try decoder.decode(ProductReview.self, from: response) - } - } -} - - -/// ProductReviewEnvelope Disposable Entity -/// -/// `Load Product Review` endpoint returns the requested product document in the `data` key. This entity -/// allows us to parse all the things with JSONDecoder. -/// -private struct ProductReviewEnvelope: Decodable { - let productReview: ProductReview - - private enum CodingKeys: String, CodingKey { - case productReview = "data" - } -} +typealias ProductReviewMapper = SiteIDMapper<ProductReview> diff --git a/Networking/Networking/Mapper/ProductShippingClassListMapper.swift b/Networking/Networking/Mapper/ProductShippingClassListMapper.swift index 6cfa35415fa..d42eaf1f8bf 100644 --- a/Networking/Networking/Mapper/ProductShippingClassListMapper.swift +++ b/Networking/Networking/Mapper/ProductShippingClassListMapper.swift @@ -1,40 +1 @@ -import Foundation - -/// Mapper: ProductShippingClass List -/// -struct ProductShippingClassListMapper: Mapper { - /// Site Identifier associated to the `ProductShippingClass`s that will be parsed. - /// - /// We're injecting this field via `JSONDecoder.userInfo` because SiteID is not returned in any of the ProductShippingClass Endpoints. - /// - let siteID: Int64 - - /// (Attempts) to convert a dictionary into [ProductShippingClass]. - /// - func map(response: Data) throws -> [ProductShippingClass] { - let decoder = JSONDecoder() - decoder.userInfo = [ - .siteID: siteID - ] - - if hasDataEnvelope(in: response) { - return try decoder.decode(ProductShippingClassListEnvelope.self, from: response).data - } else { - return try decoder.decode([ProductShippingClass].self, from: response) - } - } -} - - -/// ProductShippingClassListEnvelope Disposable Entity -/// -/// `Load All ProductShippingClass` endpoint returns the requested data in the `data` key. This entity -/// allows us to parse all the things with JSONDecoder. -/// -private struct ProductShippingClassListEnvelope: Decodable { - let data: [ProductShippingClass] - - private enum CodingKeys: String, CodingKey { - case data = "data" - } -} +typealias ProductShippingClassListMapper = SiteIDMapper<[ProductShippingClass]> diff --git a/Networking/Networking/Mapper/ProductShippingClassMapper.swift b/Networking/Networking/Mapper/ProductShippingClassMapper.swift index 24104267255..0d64b21fd87 100644 --- a/Networking/Networking/Mapper/ProductShippingClassMapper.swift +++ b/Networking/Networking/Mapper/ProductShippingClassMapper.swift @@ -1,40 +1,6 @@ -import Foundation +typealias ProductShippingClassMapper = SiteIDMapper<ProductShippingClass> -/// Mapper: ProductShippingClass -/// -struct ProductShippingClassMapper: Mapper { - /// Site Identifier associated to the ProductShippingClass that will be parsed. - /// - /// We're injecting this field via `JSONDecoder.userInfo` because SiteID is not returned in any of the ProductShippingClass Endpoints. - /// - let siteID: Int64 +struct Envelope<Resource>: Decodable where Resource: Decodable { - /// (Attempts) to convert a dictionary into ProductShippingClass. - /// - func map(response: Data) throws -> ProductShippingClass { - let decoder = JSONDecoder() - decoder.userInfo = [ - .siteID: siteID - ] - - if hasDataEnvelope(in: response) { - return try decoder.decode(ProductShippingClassEnvelope.self, from: response).productShippingClass - } else { - return try decoder.decode(ProductShippingClass.self, from: response) - } - } -} - - -/// ProductShippingClassEnvelope Disposable Entity -/// -/// `Load ProductShippingClass` endpoint returns the requested data in the `data` key. This entity -/// allows us to parse all the things with JSONDecoder. -/// -private struct ProductShippingClassEnvelope: Decodable { - let productShippingClass: ProductShippingClass - - private enum CodingKeys: String, CodingKey { - case productShippingClass = "data" - } + let data: Resource } diff --git a/Networking/Networking/Mapper/ProductSkuMapper.swift b/Networking/Networking/Mapper/ProductSkuMapper.swift index 922a92e1b53..86c16b68b9d 100644 --- a/Networking/Networking/Mapper/ProductSkuMapper.swift +++ b/Networking/Networking/Mapper/ProductSkuMapper.swift @@ -1,6 +1,3 @@ -import Foundation - - /// Mapper: Product Sku String /// struct ProductSkuMapper: Mapper { @@ -10,32 +7,15 @@ struct ProductSkuMapper: Mapper { func map(response: Data) throws -> String { let decoder = JSONDecoder() + let skus: ProductsSKUs if hasDataEnvelope(in: response) { - return try decoder.decode(ProductSkuEnvelope.self, from: response).productsSkus.first?[Constants.skuKey] ?? "" + skus = try decoder.decode(Envelope<ProductsSKUs>.self, from: response).data } else { - return try decoder.decode(ProductSkuEnvelope.ProductsSkus.self, from: response).first?[Constants.skuKey] ?? "" + skus = try decoder.decode(ProductsSKUs.self, from: response) } - } -} -// MARK: Constants -// -private extension ProductSkuMapper { - enum Constants { - static let skuKey = "sku" + return skus.first?["sku"] ?? "" } } -/// ProductSkuEnvelope Disposable Entity: -/// `Products` endpoint returns if a sku exists. This entity -/// allows us to do parse all the things with JSONDecoder. -/// -private struct ProductSkuEnvelope: Decodable { - typealias ProductsSkus = [[String: String]] - - let productsSkus: ProductsSkus - - private enum CodingKeys: String, CodingKey { - case productsSkus = "data" - } -} +typealias ProductsSKUs = [[String: String]] diff --git a/Networking/Networking/Mapper/ProductTagListMapper.swift b/Networking/Networking/Mapper/ProductTagListMapper.swift index 359f164c4d2..d5f29fbf33a 100644 --- a/Networking/Networking/Mapper/ProductTagListMapper.swift +++ b/Networking/Networking/Mapper/ProductTagListMapper.swift @@ -22,20 +22,16 @@ struct ProductTagListMapper: Mapper { switch responseType { case .load: + return try extract(from: response, siteID: siteID) + case .create: + let container: ProductTagListBatchCreateContainer if hasDataEnvelope { - return try decoder.decode(ProductTagListEnvelope.self, from: response).tags + container = try decoder.decode(Envelope<ProductTagListBatchCreateContainer>.self, from: response).data } else { - return try decoder.decode([ProductTag].self, from: response) + container = try decoder.decode(ProductTagListBatchCreateContainer.self, from: response) } - case .create: - let tags: [ProductTagFromBatchCreation] = try { - if hasDataEnvelope { - return try decoder.decode(ProductTagListBatchCreateEnvelope.self, from: response).data.tags - } else { - return try decoder.decode(ProductTagListBatchCreateContainer.self, from: response).tags - } - }() - return tags + + return container.tags .filter { $0.error == nil } .compactMap { (tagCreated) -> ProductTag? in if let name = tagCreated.name, let slug = tagCreated.slug { @@ -45,11 +41,14 @@ struct ProductTagListMapper: Mapper { } case .delete: + let container: ProductTagListBatchDeleteContainer if hasDataEnvelope { - return try decoder.decode(ProductTagListBatchDeleteEnvelope.self, from: response).data.tags + container = try decoder.decode(Envelope<ProductTagListBatchDeleteContainer>.self, from: response).data } else { - return try decoder.decode(ProductTagListBatchDeleteContainer.self, from: response).tags + container = try decoder.decode(ProductTagListBatchDeleteContainer.self, from: response) } + + return container.tags } } @@ -60,32 +59,6 @@ struct ProductTagListMapper: Mapper { } } - -/// ProductTagListEnvelope Disposable Entity: -/// `Load All Products Tags` endpoint returns the products tags in the `data` key. -/// This entity allows us to do parse all the things with JSONDecoder. -/// -private struct ProductTagListEnvelope: Decodable { - let tags: [ProductTag] - - private enum CodingKeys: String, CodingKey { - case tags = "data" - } -} - - -/// ProductTagListBatchCreateEnvelope Disposable Entity: -/// `Batch Create Products Tags` endpoint returns the products tags under the `data` key, nested under `create` key. -/// This entity allows us to do parse all the things with JSONDecoder. -/// -private struct ProductTagListBatchCreateEnvelope: Decodable { - let data: ProductTagListBatchCreateContainer - - private enum CodingKeys: String, CodingKey { - case data - } -} - private struct ProductTagListBatchCreateContainer: Decodable { let tags: [ProductTagFromBatchCreation] @@ -94,18 +67,6 @@ private struct ProductTagListBatchCreateContainer: Decodable { } } -/// ProductTagListBatchDeleteEnvelope Disposable Entity: -/// `Batch Delete Products Tags` endpoint returns the products tags under the `data` key, nested under `delete` key. -/// This entity allows us to do parse all the things with JSONDecoder. -/// -private struct ProductTagListBatchDeleteEnvelope: Decodable { - let data: ProductTagListBatchDeleteContainer - - private enum CodingKeys: String, CodingKey { - case data - } -} - private struct ProductTagListBatchDeleteContainer: Decodable { let tags: [ProductTag] diff --git a/Networking/Networking/Mapper/ProductVariationListMapper.swift b/Networking/Networking/Mapper/ProductVariationListMapper.swift index 08aaf486834..af9c642d72c 100644 --- a/Networking/Networking/Mapper/ProductVariationListMapper.swift +++ b/Networking/Networking/Mapper/ProductVariationListMapper.swift @@ -1,6 +1,3 @@ -import Foundation - - /// Mapper: ProductVariation List /// struct ProductVariationListMapper: Mapper { @@ -26,23 +23,6 @@ struct ProductVariationListMapper: Mapper { .productID: productID ] - if hasDataEnvelope(in: response) { - return try decoder.decode(ProductVariationsEnvelope.self, from: response).productVariations - } else { - return try decoder.decode([ProductVariation].self, from: response) - } - } -} - -/// ProductVariationsEnvelope Disposable Entity -/// -/// `Load Product Variations` endpoint returns the requested objects in the `data` key. This entity -/// allows us to parse all the things with JSONDecoder. -/// -private struct ProductVariationsEnvelope: Decodable { - let productVariations: [ProductVariation] - - private enum CodingKeys: String, CodingKey { - case productVariations = "data" + return try extract(from: response, using: decoder) } } diff --git a/Networking/Networking/Mapper/ProductVariationMapper.swift b/Networking/Networking/Mapper/ProductVariationMapper.swift index 11537863dc0..f33b2aed5fa 100644 --- a/Networking/Networking/Mapper/ProductVariationMapper.swift +++ b/Networking/Networking/Mapper/ProductVariationMapper.swift @@ -1,5 +1,3 @@ -import Foundation - /// Mapper: ProductVariation /// struct ProductVariationMapper: Mapper { @@ -25,23 +23,6 @@ struct ProductVariationMapper: Mapper { .productID: productID ] - if hasDataEnvelope(in: response) { - return try decoder.decode(ProductVariationEnvelope.self, from: response).productVariation - } else { - return try decoder.decode(ProductVariation.self, from: response) - } - } -} - -/// ProductVariationEnvelope Disposable Entity -/// -/// `ProductVariation` endpoint returns the requested product variation document in the `data` key. This entity -/// allows us to do parse all the things with JSONDecoder. -/// -private struct ProductVariationEnvelope: Decodable { - let productVariation: ProductVariation - - private enum CodingKeys: String, CodingKey { - case productVariation = "data" + return try extract(from: response, using: decoder) } } diff --git a/Networking/Networking/Mapper/ProductVariationsBulkCreateMapper.swift b/Networking/Networking/Mapper/ProductVariationsBulkCreateMapper.swift index d92643db1db..9143867b097 100644 --- a/Networking/Networking/Mapper/ProductVariationsBulkCreateMapper.swift +++ b/Networking/Networking/Mapper/ProductVariationsBulkCreateMapper.swift @@ -1,5 +1,3 @@ -import Foundation - /// Mapper: ProductVariationsBulkCreateMapper /// struct ProductVariationsBulkCreateMapper: Mapper { @@ -24,24 +22,15 @@ struct ProductVariationsBulkCreateMapper: Mapper { .siteID: siteID, .productID: productID ] + + let container: ProductVariationsContainer if hasDataEnvelope(in: response) { - return try decoder.decode(ProductVariationsContainerEnvelope.self, from: response).data.createdProductVariations + container = try decoder.decode(Envelope<ProductVariationsContainer>.self, from: response).data } else { - return try decoder.decode(ProductVariationsContainer.self, from: response).createdProductVariations + container = try decoder.decode(ProductVariationsContainer.self, from: response) } - } -} -/// ProductVariationsEnvelope Disposable Entity -/// -/// `Variations/batch` endpoint returns the requested create product variations document in a `create` key, nested in a `data` key. -/// This entity allows us to do parse all the things with JSONDecoder. -/// -private struct ProductVariationsContainerEnvelope: Decodable { - let data: ProductVariationsContainer - - private enum CodingKeys: String, CodingKey { - case data + return container.createdProductVariations } } diff --git a/Networking/Networking/Mapper/ProductVariationsBulkUpdateMapper.swift b/Networking/Networking/Mapper/ProductVariationsBulkUpdateMapper.swift index f7283f6f18c..08948d297d3 100644 --- a/Networking/Networking/Mapper/ProductVariationsBulkUpdateMapper.swift +++ b/Networking/Networking/Mapper/ProductVariationsBulkUpdateMapper.swift @@ -24,24 +24,15 @@ struct ProductVariationsBulkUpdateMapper: Mapper { .siteID: siteID, .productID: productID ] + + let container: ProductVariationsContainer if hasDataEnvelope(in: response) { - return try decoder.decode(ProductVariationsContainerEnvelope.self, from: response).data.updatedProductVariations + container = try decoder.decode(Envelope<ProductVariationsContainer>.self, from: response).data } else { - return try decoder.decode(ProductVariationsContainer.self, from: response).updatedProductVariations + container = try decoder.decode(ProductVariationsContainer.self, from: response) } - } -} -/// ProductVariationsEnvelope Disposable Entity -/// -/// `Variations/batch` endpoint returns the requested update product variations document in a `update` key, nested in a `data` key. -/// This entity allows us to do parse all the things with JSONDecoder. -/// -private struct ProductVariationsContainerEnvelope: Decodable { - let data: ProductVariationsContainer - - private enum CodingKeys: String, CodingKey { - case data + return container.updatedProductVariations } } diff --git a/Networking/Networking/Mapper/ProductsBulkUpdateMapper.swift b/Networking/Networking/Mapper/ProductsBulkUpdateMapper.swift index b883e89f1b6..249596d811e 100644 --- a/Networking/Networking/Mapper/ProductsBulkUpdateMapper.swift +++ b/Networking/Networking/Mapper/ProductsBulkUpdateMapper.swift @@ -1,5 +1,3 @@ -import Foundation - /// Mapper: ProductsBulkUpdateMapper /// struct ProductsBulkUpdateMapper: Mapper { @@ -17,27 +15,15 @@ struct ProductsBulkUpdateMapper: Mapper { decoder.userInfo = [ .siteID: siteID ] + if hasDataEnvelope(in: response) { - return try decoder.decode(ProductsContainerEnvelope.self, from: response).data.updatedProducts + return try decoder.decode(Envelope<ProductsContainer>.self, from: response).data.updatedProducts } else { return try decoder.decode(ProductsContainer.self, from: response).updatedProducts } } } -/// ProductsEnvelope Disposable Entity -/// -/// `products/batch` endpoint returns the requested updated products in a `update` key, nested in a `data` key. -/// This entity allows us to do parse all the things with JSONDecoder. -/// -private struct ProductsContainerEnvelope: Decodable { - let data: ProductsContainer - - private enum CodingKeys: String, CodingKey { - case data - } -} - private struct ProductsContainer: Decodable { let updatedProducts: [Product] diff --git a/Networking/Networking/Mapper/ProductsReportMapper.swift b/Networking/Networking/Mapper/ProductsReportMapper.swift index f41fb613fe2..be63b10d5d5 100644 --- a/Networking/Networking/Mapper/ProductsReportMapper.swift +++ b/Networking/Networking/Mapper/ProductsReportMapper.swift @@ -1,5 +1,3 @@ -import Foundation - /// Mapper: `[ProductsReportItem]` /// struct ProductsReportMapper: Mapper { @@ -9,22 +7,9 @@ struct ProductsReportMapper: Mapper { func map(response: Data) throws -> [ProductsReportItem] { let decoder = JSONDecoder() if hasDataEnvelope(in: response) { - return try decoder.decode(ProductsReportEnvelope.self, from: response).items + return try decoder.decode(Envelope<[ProductsReportItem]>.self, from: response).data } else { return try decoder.decode([ProductsReportItem].self, from: response) } } } - - -/// ProductsReportEnvelope Disposable Entity: -/// Load Products Report endpoint returns the coupon in the `data` key. -/// This entity allows us to parse all the things with JSONDecoder. -/// -private struct ProductsReportEnvelope: Decodable { - let items: [ProductsReportItem] - - private enum CodingKeys: String, CodingKey { - case items = "data" - } -} diff --git a/Networking/Networking/Mapper/ProductsTotalMapper.swift b/Networking/Networking/Mapper/ProductsTotalMapper.swift index 3d01f5509fe..23311e0c936 100644 --- a/Networking/Networking/Mapper/ProductsTotalMapper.swift +++ b/Networking/Networking/Mapper/ProductsTotalMapper.swift @@ -1,19 +1,16 @@ -import Foundation - /// Mapper: ProductsTotal /// struct ProductsTotalMapper: Mapper { func map(response: Data) throws -> Int64 { let decoder = JSONDecoder() - let totals: [ProductTypeTotal] + let totals: [ProductTypeTotal] if hasDataEnvelope(in: response) { - totals = try decoder - .decode(ProductTypeTotalListEnvelope.self, from: response) - .totals + totals = try decoder .decode(Envelope<[ProductTypeTotal]>.self, from: response).data } else { totals = try decoder.decode([ProductTypeTotal].self, from: response) } + return totals.map { $0.total }.reduce(0, +) } } @@ -25,11 +22,3 @@ private struct ProductTypeTotal: Decodable { case total } } - -private struct ProductTypeTotalListEnvelope: Decodable { - let totals: [ProductTypeTotal] - - private enum CodingKeys: String, CodingKey { - case totals = "data" - } -} diff --git a/Networking/Networking/Mapper/ReaderConnectionTokenMapper.swift b/Networking/Networking/Mapper/ReaderConnectionTokenMapper.swift index a3e9eb8d491..2a1436c8c87 100644 --- a/Networking/Networking/Mapper/ReaderConnectionTokenMapper.swift +++ b/Networking/Networking/Mapper/ReaderConnectionTokenMapper.swift @@ -1,30 +1,8 @@ -/// Mapper: Card reader connection token -/// struct ReaderConnectionTokenMapper: Mapper { /// (Attempts) to convert a dictionary into a connection token. /// func map(response: Data) throws -> ReaderConnectionToken { - let decoder = JSONDecoder() - - if hasDataEnvelope(in: response) { - return try decoder.decode(ReaderConnectionTokenEnvelope.self, from: response).token - } else { - return try decoder.decode(ReaderConnectionToken.self, from: response) - } - } -} - - -/// ReaderConnectionTokenEnvelope Disposable Entity -/// -/// `Load connection Token` endpoint returns the requested connection token and test mode in the `data` key. This entity -/// allows us to parse all the things with JSONDecoder. -/// -private struct ReaderConnectionTokenEnvelope: Decodable { - let token: ReaderConnectionToken - - private enum CodingKeys: String, CodingKey { - case token = "data" + return try extract(from: response) } } diff --git a/Networking/Networking/Mapper/RefundListMapper.swift b/Networking/Networking/Mapper/RefundListMapper.swift index 75c78d69b6f..6ed4803b426 100644 --- a/Networking/Networking/Mapper/RefundListMapper.swift +++ b/Networking/Networking/Mapper/RefundListMapper.swift @@ -1,6 +1,3 @@ -import Foundation - - /// Mapper: Refund List /// struct RefundListMapper: Mapper { @@ -16,7 +13,6 @@ struct RefundListMapper: Mapper { /// let orderID: Int64 - /// (Attempts) to convert a dictionary into [Refund]. /// func map(response: Data) throws -> [Refund] { @@ -27,24 +23,6 @@ struct RefundListMapper: Mapper { .orderID: orderID ] - if hasDataEnvelope(in: response) { - return try decoder.decode(RefundsEnvelope.self, from: response).refunds - } else { - return try decoder.decode([Refund].self, from: response) - } - } -} - - -/// RefundsEnvelope Disposable Entity -/// -/// `Load Refunds` endpoint returns the requested order refunds document in the `data` key. This entity -/// allows us to parse all the things with JSONDecoder. -/// -private struct RefundsEnvelope: Decodable { - let refunds: [Refund] - - private enum CodingKeys: String, CodingKey { - case refunds = "data" + return try extract(from: response, using: decoder) } } diff --git a/Networking/Networking/Mapper/RefundMapper.swift b/Networking/Networking/Mapper/RefundMapper.swift index f9b1f52ca8f..72479ce0b9e 100644 --- a/Networking/Networking/Mapper/RefundMapper.swift +++ b/Networking/Networking/Mapper/RefundMapper.swift @@ -1,6 +1,3 @@ -import Foundation - - /// Mapper: Refund /// struct RefundMapper: Mapper { @@ -16,7 +13,6 @@ struct RefundMapper: Mapper { /// let orderID: Int64 - /// (Attempts) to convert a dictionary into a single Refund. /// func map(response: Data) throws -> Refund { @@ -28,7 +24,7 @@ struct RefundMapper: Mapper { ] if hasDataEnvelope(in: response) { - return try decoder.decode(RefundEnvelope.self, from: response).refund + return try decoder.decode(Envelope<Refund>.self, from: response).data } else { return try decoder.decode(Refund.self, from: response) } @@ -42,17 +38,3 @@ struct RefundMapper: Mapper { return try encoder.encode(refund) } } - - -/// RefundEnvelope Disposable Entity -/// -/// `Load Refund` endpoint returns the requested order refund document in the `data` key. This entity -/// allows us to parse all the things with JSONDecoder. -/// -private struct RefundEnvelope: Decodable { - let refund: Refund - - private enum CodingKeys: String, CodingKey { - case refund = "data" - } -} diff --git a/Networking/Networking/Mapper/RemotePaymentIntentMapper.swift b/Networking/Networking/Mapper/RemotePaymentIntentMapper.swift index 8b1282a16c1..c0cf14ce862 100644 --- a/Networking/Networking/Mapper/RemotePaymentIntentMapper.swift +++ b/Networking/Networking/Mapper/RemotePaymentIntentMapper.swift @@ -1,31 +1,8 @@ -import Foundation - -/// Mapper: WCPay Payment Intent -/// struct RemotePaymentIntentMapper: Mapper { /// (Attempts) to convert a dictionary into an payment intent. /// func map(response: Data) throws -> RemotePaymentIntent { - let decoder = JSONDecoder() - - if hasDataEnvelope(in: response) { - return try decoder.decode(WCPayPaymentIntentEnvelope.self, from: response).paymentIntent - } else { - return try decoder.decode(RemotePaymentIntent.self, from: response) - } - } -} - -/// WCPayPaymentIntentEnvelope Disposable Entity -/// -/// Endpoint returns the payment intent in the `data` key. This entity -/// allows us to parse it with JSONDecoder. -/// -private struct WCPayPaymentIntentEnvelope: Decodable { - let paymentIntent: RemotePaymentIntent - - private enum CodingKeys: String, CodingKey { - case paymentIntent = "data" + return try extract(from: response) } } diff --git a/Networking/Networking/Mapper/RemoteReaderLocationMapper.swift b/Networking/Networking/Mapper/RemoteReaderLocationMapper.swift index d6c1bca8f74..3576ab52148 100644 --- a/Networking/Networking/Mapper/RemoteReaderLocationMapper.swift +++ b/Networking/Networking/Mapper/RemoteReaderLocationMapper.swift @@ -1,5 +1,3 @@ -import Foundation - /// Mapper: WCPay Reader Location /// struct RemoteReaderLocationMapper: Mapper { @@ -9,23 +7,6 @@ struct RemoteReaderLocationMapper: Mapper { func map(response: Data) throws -> RemoteReaderLocation { let decoder = JSONDecoder() - if hasDataEnvelope(in: response) { - return try decoder.decode(RemoteReaderLocationEnvelope.self, from: response).location - } else { - return try decoder.decode(RemoteReaderLocation.self, from: response) - } - } -} - -/// WCPayLocationEnvelope Disposable Entity -/// -/// Endpoint returns the location in the `data` key. This entity -/// allows us to parse it with JSONDecoder. -/// -private struct RemoteReaderLocationEnvelope: Decodable { - let location: RemoteReaderLocation - - private enum CodingKeys: String, CodingKey { - case location = "data" + return try extract(from: response, using: decoder) } } diff --git a/Networking/Networking/Mapper/ReportOrderTotalsMapper.swift b/Networking/Networking/Mapper/ReportOrderTotalsMapper.swift index e2170f028c2..e2c8f9cabf0 100644 --- a/Networking/Networking/Mapper/ReportOrderTotalsMapper.swift +++ b/Networking/Networking/Mapper/ReportOrderTotalsMapper.swift @@ -1,39 +1 @@ -import Foundation - - -/// Mapper: Order totals report -/// -struct ReportOrderTotalsMapper: Mapper { - - /// Site Identifier associated to the settings that will be parsed. - /// We're injecting this field via `JSONDecoder.userInfo` because - /// the remote endpoints don't really return the SiteID in any of the - /// settings endpoints. - /// - let siteID: Int64 - - /// (Attempts) to extract order totals report from a given JSON Encoded response. - /// - func map(response: Data) throws -> [OrderStatus] { - let decoder = JSONDecoder() - decoder.userInfo = [ - .siteID: siteID - ] - if hasDataEnvelope(in: response) { - return try decoder.decode(ReportOrderTotalsEnvelope.self, from: response).data - } else { - return try decoder.decode([OrderStatus].self, from: response) - } - } -} - -/// The report endpoint returns the totals document within a `data` key. -/// This entity allows us to parse all the things with JSONDecoder. -/// -private struct ReportOrderTotalsEnvelope: Decodable { - let data: [OrderStatus] - - private enum CodingKeys: String, CodingKey { - case data - } -} +typealias ReportOrderTotalsMapper = SiteIDMapper<[OrderStatus]> diff --git a/Networking/Networking/Mapper/ShipmentTrackingListMapper.swift b/Networking/Networking/Mapper/ShipmentTrackingListMapper.swift index 101064297ac..e3edfbd8691 100644 --- a/Networking/Networking/Mapper/ShipmentTrackingListMapper.swift +++ b/Networking/Networking/Mapper/ShipmentTrackingListMapper.swift @@ -1,6 +1,5 @@ import Foundation - /// Mapper for an array of `ShipmentTracking` JSON objects /// struct ShipmentTrackingListMapper: Mapper { @@ -28,22 +27,9 @@ struct ShipmentTrackingListMapper: Mapper { ] if hasDataEnvelope(in: response) { - return try decoder.decode(ShipmentTrackingListEnvelope.self, from: response).shipmentTrackings + return try decoder.decode(Envelope<[ShipmentTracking]>.self, from: response).data } else { return try decoder.decode([ShipmentTracking].self, from: response) } } } - - -/// ShipmentTracking list disposable entity: -/// `Load Shipment Trackings` endpoint returns all of its tracking details within the `data` key. This entity -/// allows us to parse all the things with JSONDecoder. -/// -private struct ShipmentTrackingListEnvelope: Decodable { - let shipmentTrackings: [ShipmentTracking] - - private enum CodingKeys: String, CodingKey { - case shipmentTrackings = "data" - } -} diff --git a/Networking/Networking/Mapper/ShipmentTrackingProviderListMapper.swift b/Networking/Networking/Mapper/ShipmentTrackingProviderListMapper.swift index 9583fd46969..e54af99e128 100644 --- a/Networking/Networking/Mapper/ShipmentTrackingProviderListMapper.swift +++ b/Networking/Networking/Mapper/ShipmentTrackingProviderListMapper.swift @@ -3,34 +3,23 @@ import Foundation /// (Attempts) to convert a dictionary into an ShipmentTrackingProviderGroup entity. /// struct ShipmentTrackingProviderListMapper: Mapper { + private let siteID: Int64 public init(siteID: Int64) { self.siteID = siteID } + private typealias RawData = [String: [String: String]] + func map(response: Data) throws -> [ShipmentTrackingProviderGroup] { let decoder = JSONDecoder() - let rawDictionary: ShipmentTrackingProviderListEnvelope.RawData + let rawDictionary: RawData if hasDataEnvelope(in: response) { - rawDictionary = try decoder.decode(ShipmentTrackingProviderListEnvelope.self, from: response).rawData + rawDictionary = try decoder.decode(Envelope<RawData>.self, from: response).data } else { - rawDictionary = try decoder.decode(ShipmentTrackingProviderListEnvelope.RawData.self, from: response) + rawDictionary = try decoder.decode(RawData.self, from: response) } return rawDictionary.map({ ShipmentTrackingProviderGroup(name: $0.key, siteID: siteID, dictionary: $0.value) }) } } - - -/// ShipmentTrackingProviderListEnvelope Disposable Entity: The shipment tracking provider endpoint returns -/// the providers within a `data` key. -/// -private struct ShipmentTrackingProviderListEnvelope: Decodable { - typealias RawData = [String: [String: String]] - - let rawData: RawData - - private enum CodingKeys: String, CodingKey { - case rawData = "data" - } -} diff --git a/Networking/Networking/Mapper/ShippingLabelAccountSettingsMapper.swift b/Networking/Networking/Mapper/ShippingLabelAccountSettingsMapper.swift index b5e2c6397b5..50807634dd7 100644 --- a/Networking/Networking/Mapper/ShippingLabelAccountSettingsMapper.swift +++ b/Networking/Networking/Mapper/ShippingLabelAccountSettingsMapper.swift @@ -1,5 +1,3 @@ -import Foundation - /// Mapper: Shipping Label Account Settings /// struct ShippingLabelAccountSettingsMapper: Mapper { @@ -17,22 +15,6 @@ struct ShippingLabelAccountSettingsMapper: Mapper { .siteID: siteID ] - if hasDataEnvelope(in: response) { - return try decoder.decode(ShippingLabelAccountSettingsMapperEnvelope.self, from: response).data - } else { - return try decoder.decode(ShippingLabelAccountSettings.self, from: response) - } - } -} - -/// ShippingLabelAccountSettingsMapperEnvelope Disposable Entity: -/// `Shipping Label Account Settings` endpoint returns the shipping label account settings in the `data` key. -/// This entity allows us to parse all the things with JSONDecoder. -/// -private struct ShippingLabelAccountSettingsMapperEnvelope: Decodable { - let data: ShippingLabelAccountSettings - - private enum CodingKeys: String, CodingKey { - case data + return try extract(from: response, using: decoder) } } diff --git a/Networking/Networking/Mapper/ShippingLabelAddressValidationSuccessMapper.swift b/Networking/Networking/Mapper/ShippingLabelAddressValidationSuccessMapper.swift index 402e980727a..92c9b7b0403 100644 --- a/Networking/Networking/Mapper/ShippingLabelAddressValidationSuccessMapper.swift +++ b/Networking/Networking/Mapper/ShippingLabelAddressValidationSuccessMapper.swift @@ -1,6 +1,3 @@ -import Foundation - - /// Mapper: Shipping Label Address Validation Response /// struct ShippingLabelAddressValidationSuccessMapper: Mapper { @@ -8,25 +5,14 @@ struct ShippingLabelAddressValidationSuccessMapper: Mapper { /// func map(response: Data) throws -> ShippingLabelAddressValidationSuccess { let decoder = JSONDecoder() - let data: ShippingLabelAddressValidationResponse = try { - if hasDataEnvelope(in: response) { - return try decoder.decode(ShippingLabelAddressValidationResponseEnvelope.self, from: response).data - } else { - return try decoder.decode(ShippingLabelAddressValidationResponse.self, from: response) - } - }() - return try data.result.get() - } -} -/// ShippingLabelAddressValidationResponseEnvelope Disposable Entity: -/// `Normalize Address` endpoint returns the shipping label address document in the `data` key. -/// This entity allows us to do parse all the things with JSONDecoder. -/// -private struct ShippingLabelAddressValidationResponseEnvelope: Decodable { - let data: ShippingLabelAddressValidationResponse + let data: ShippingLabelAddressValidationResponse + if hasDataEnvelope(in: response) { + data = try decoder.decode(Envelope<ShippingLabelAddressValidationResponse>.self, from: response).data + } else { + data = try decoder.decode(ShippingLabelAddressValidationResponse.self, from: response) + } - private enum CodingKeys: String, CodingKey { - case data = "data" + return try data.result.get() } } diff --git a/Networking/Networking/Mapper/ShippingLabelCarriersAndRatesMapper.swift b/Networking/Networking/Mapper/ShippingLabelCarriersAndRatesMapper.swift index c0299c73c10..9c27134f8ea 100644 --- a/Networking/Networking/Mapper/ShippingLabelCarriersAndRatesMapper.swift +++ b/Networking/Networking/Mapper/ShippingLabelCarriersAndRatesMapper.swift @@ -1,6 +1,3 @@ -import Foundation - - /// Mapper: Shipping Label Carriers and Rates Data /// struct ShippingLabelCarriersAndRatesMapper: Mapper { @@ -8,23 +5,15 @@ struct ShippingLabelCarriersAndRatesMapper: Mapper { /// func map(response: Data) throws -> [ShippingLabelCarriersAndRates] { let decoder = JSONDecoder() + + let container: ShippingLabelRatesEnvelope if hasDataEnvelope(in: response) { - return try decoder.decode(ShippingLabelDataEnvelope.self, from: response).data.rates.boxes + container = try decoder.decode(Envelope<ShippingLabelRatesEnvelope>.self, from: response).data } else { - return try decoder.decode(ShippingLabelRatesEnvelope.self, from: response).rates.boxes + container = try decoder.decode(ShippingLabelRatesEnvelope.self, from: response) } - } -} - -/// ShippingLabelDataEnvelope Disposable Entity: -/// `Carriers and Rates Shipping Label` endpoint returns the shipping label document under `data` -> `rates` -> `default_box` key. -/// This entity allows us to do parse all the things with JSONDecoder. -/// -private struct ShippingLabelDataEnvelope: Decodable { - let data: ShippingLabelRatesEnvelope - private enum CodingKeys: String, CodingKey { - case data + return container.rates.boxes } } diff --git a/Networking/Networking/Mapper/ShippingLabelCreationEligibilityMapper.swift b/Networking/Networking/Mapper/ShippingLabelCreationEligibilityMapper.swift index d3f9607f120..0ed32e2ea8c 100644 --- a/Networking/Networking/Mapper/ShippingLabelCreationEligibilityMapper.swift +++ b/Networking/Networking/Mapper/ShippingLabelCreationEligibilityMapper.swift @@ -1,28 +1,7 @@ -import Foundation - -/// Mapper: Shipping Label Creation Eligibility -/// struct ShippingLabelCreationEligibilityMapper: Mapper { /// (Attempts) to convert a dictionary into ShippingLabelAccountSettings. /// func map(response: Data) throws -> ShippingLabelCreationEligibilityResponse { - let decoder = JSONDecoder() - if hasDataEnvelope(in: response) { - return try decoder.decode(ShippingLabelCreationEligibilityMapperEnvelope.self, from: response).eligibility - } else { - return try decoder.decode(ShippingLabelCreationEligibilityResponse.self, from: response) - } - } -} - -/// ShippingLabelCreationEligibilityMapperEnvelope Disposable Entity: -/// `Shipping Label Creation Eligibility` endpoint returns the shipping label account settings in the `data` key. -/// This entity allows us to parse all the things with JSONDecoder. -/// -private struct ShippingLabelCreationEligibilityMapperEnvelope: Decodable { - let eligibility: ShippingLabelCreationEligibilityResponse - - private enum CodingKeys: String, CodingKey { - case eligibility = "data" + try extract(from: response) } } diff --git a/Networking/Networking/Mapper/ShippingLabelPackagesMapper.swift b/Networking/Networking/Mapper/ShippingLabelPackagesMapper.swift index f8cc2c78fd5..e29df0a8b52 100644 --- a/Networking/Networking/Mapper/ShippingLabelPackagesMapper.swift +++ b/Networking/Networking/Mapper/ShippingLabelPackagesMapper.swift @@ -1,29 +1,7 @@ -import Foundation - - -/// Mapper: Shipping Label Packages -/// struct ShippingLabelPackagesMapper: Mapper { /// (Attempts) to convert a dictionary into ShippingLabelPackagesResponse. /// func map(response: Data) throws -> ShippingLabelPackagesResponse { - let decoder = JSONDecoder() - if hasDataEnvelope(in: response) { - return try decoder.decode(ShippingLabelPackagesMapperEnvelope.self, from: response).data - } else { - return try decoder.decode(ShippingLabelPackagesResponse.self, from: response) - } - } -} - -/// ShippingLabelPackagesMapperEnvelope Disposable Entity: -/// `Shipping Label Packages` endpoint returns the shipping label packages in the `data` key. -/// This entity allows us to do parse all the things with JSONDecoder. -/// -private struct ShippingLabelPackagesMapperEnvelope: Decodable { - let data: ShippingLabelPackagesResponse - - private enum CodingKeys: String, CodingKey { - case data = "data" + try extract(from: response) } } diff --git a/Networking/Networking/Mapper/ShippingLabelPrintDataMapper.swift b/Networking/Networking/Mapper/ShippingLabelPrintDataMapper.swift index 56a055943af..b2e1e3bf0b9 100644 --- a/Networking/Networking/Mapper/ShippingLabelPrintDataMapper.swift +++ b/Networking/Networking/Mapper/ShippingLabelPrintDataMapper.swift @@ -1,29 +1,7 @@ -import Foundation - - -/// Mapper: Shipping Label Print Data -/// struct ShippingLabelPrintDataMapper: Mapper { /// (Attempts) to convert a dictionary into ShippingLabelPrintData. /// func map(response: Data) throws -> ShippingLabelPrintData { - let decoder = JSONDecoder() - if hasDataEnvelope(in: response) { - return try decoder.decode(ShippingLabelPrintDataEnvelope.self, from: response).printData - } else { - return try decoder.decode(ShippingLabelPrintData.self, from: response) - } - } -} - -/// ShippingLabelPrintDataEnvelope Disposable Entity: -/// `Print Shipping Label` endpoint returns the shipping label document in the `data` key. -/// This entity allows us to do parse all the things with JSONDecoder. -/// -private struct ShippingLabelPrintDataEnvelope: Decodable { - let printData: ShippingLabelPrintData - - private enum CodingKeys: String, CodingKey { - case printData = "data" + try extract(from: response) } } diff --git a/Networking/Networking/Mapper/ShippingLabelPurchaseMapper.swift b/Networking/Networking/Mapper/ShippingLabelPurchaseMapper.swift index 770b547d426..495f50a6998 100644 --- a/Networking/Networking/Mapper/ShippingLabelPurchaseMapper.swift +++ b/Networking/Networking/Mapper/ShippingLabelPurchaseMapper.swift @@ -25,23 +25,14 @@ struct ShippingLabelPurchaseMapper: Mapper { .orderID: orderID ] + let container: ShippingLabelPurchaseEnvelope if hasDataEnvelope(in: response) { - return try decoder.decode(ShippingLabelPurchaseResponse.self, from: response).data.labels + container = try decoder.decode(Envelope<ShippingLabelPurchaseEnvelope>.self, from: response).data } else { - return try decoder.decode(ShippingLabelPurchaseEnvelope.self, from: response).labels + container = try decoder.decode(ShippingLabelPurchaseEnvelope.self, from: response) } - } -} - -/// ShippingLabelPurchaseResponse Disposable Entity -/// -/// `Purchase Shipping Labels` endpoint returns the data wrapper in the `data` key. -/// -private struct ShippingLabelPurchaseResponse: Decodable { - let data: ShippingLabelPurchaseEnvelope - private enum CodingKeys: String, CodingKey { - case data + return container.labels } } diff --git a/Networking/Networking/Mapper/ShippingLabelRefundMapper.swift b/Networking/Networking/Mapper/ShippingLabelRefundMapper.swift index a3fe963d31a..216f918cfc0 100644 --- a/Networking/Networking/Mapper/ShippingLabelRefundMapper.swift +++ b/Networking/Networking/Mapper/ShippingLabelRefundMapper.swift @@ -1,6 +1,3 @@ -import Foundation - - /// Mapper: Shipping Label Refund Mapper /// struct ShippingLabelRefundMapper: Mapper { @@ -10,25 +7,13 @@ struct ShippingLabelRefundMapper: Mapper { let decoder = JSONDecoder() decoder.dateDecodingStrategy = .millisecondsSince1970 if hasDataEnvelope(in: response) { - return try decoder.decode(ShippingLabelRefundResponse.self, from: response).data.refund + return try decoder.decode(Envelope<ShippingLabelRefundEnvelope>.self, from: response).data.refund } else { return try decoder.decode(ShippingLabelRefundEnvelope.self, from: response).refund } } } -/// ShippingLabelRefundResponse Disposable Entity: -/// `Refund Shipping Label` endpoint returns the refund data wrapper in the `data` key. -/// This entity allows us to do parse all the things with JSONDecoder. -/// -private struct ShippingLabelRefundResponse: Decodable { - let data: ShippingLabelRefundEnvelope - - private enum CodingKeys: String, CodingKey { - case data - } -} - /// ShippingLabelRefundEnvelope Disposable Entity: /// `Refund Shipping Label` endpoint returns the refund in the `data.refund` key. /// This entity allows us to do parse all the things with JSONDecoder. diff --git a/Networking/Networking/Mapper/ShippingLabelStatusMapper.swift b/Networking/Networking/Mapper/ShippingLabelStatusMapper.swift index ad6444a131a..25a1715159f 100644 --- a/Networking/Networking/Mapper/ShippingLabelStatusMapper.swift +++ b/Networking/Networking/Mapper/ShippingLabelStatusMapper.swift @@ -1,5 +1,3 @@ -import Foundation - /// Mapper: Check Status of Shipping Labels /// struct ShippingLabelStatusMapper: Mapper { @@ -26,25 +24,13 @@ struct ShippingLabelStatusMapper: Mapper { ] if hasDataEnvelope(in: response) { - return try decoder.decode(ShippingLabelStatusResponse.self, from: response).data.labels + return try decoder.decode(Envelope<ShippingLabelStatusEnvelope>.self, from: response).data.labels } else { return try decoder.decode(ShippingLabelStatusEnvelope.self, from: response).labels } } } -/// ShippingLabelPurchaseResponse Disposable Entity -/// -/// `Check Shipping Labels Status` endpoint returns the data wrapper in the `data` key. -/// -private struct ShippingLabelStatusResponse: Decodable { - let data: ShippingLabelStatusEnvelope - - private enum CodingKeys: String, CodingKey { - case data - } -} - /// ShippingLabelPurchaseEnvelope Disposable Entity /// /// `Check Shipping Labels Status` endpoint returns the shipping label purchases in the `data.labels` key. diff --git a/Networking/Networking/Mapper/SiteAPIMapper.swift b/Networking/Networking/Mapper/SiteAPIMapper.swift index e1403b127ca..888918dfe66 100644 --- a/Networking/Networking/Mapper/SiteAPIMapper.swift +++ b/Networking/Networking/Mapper/SiteAPIMapper.swift @@ -1,41 +1 @@ -import Foundation - - -/// Mapper: SiteAPI -/// -struct SiteAPIMapper: Mapper { - - /// Site Identifier associated to the API information that will be parsed. - /// We're injecting this field via `JSONDecoder.userInfo` because the remote endpoints don't return the SiteID. - /// - let siteID: Int64 - - /// (Attempts) to convert a dictionary into [SiteSetting]. - /// - func map(response: Data) throws -> SiteAPI { - let decoder = JSONDecoder() - decoder.dateDecodingStrategy = .formatted(DateFormatter.Defaults.dateTimeFormatter) - decoder.userInfo = [ - .siteID: siteID - ] - - if hasDataEnvelope(in: response) { - return try decoder.decode(SiteAPIEnvelope.self, from: response).siteAPI - } else { - return try decoder.decode(SiteAPI.self, from: response) - } - } -} - - -/// SiteAPIEnvelope Disposable Entity: -/// The settings endpoint returns the settings document within a `data` key. This entity -/// allows us to do parse all the things with JSONDecoder. -/// -private struct SiteAPIEnvelope: Decodable { - let siteAPI: SiteAPI - - private enum CodingKeys: String, CodingKey { - case siteAPI = "data" - } -} +typealias SiteAPIMapper = SiteIDMapper<SiteAPI> diff --git a/Networking/Networking/Mapper/SitePluginMapper.swift b/Networking/Networking/Mapper/SitePluginMapper.swift index 0ed9cc34866..6949c976b96 100644 --- a/Networking/Networking/Mapper/SitePluginMapper.swift +++ b/Networking/Networking/Mapper/SitePluginMapper.swift @@ -1,5 +1,3 @@ -import Foundation - /// Mapper: SitePlugin /// struct SitePluginMapper: Mapper { @@ -20,28 +18,6 @@ struct SitePluginMapper: Mapper { /// (Attempts) to convert a dictionary into SitePlugin. /// func map(response: Data) throws -> SitePlugin { - let decoder = JSONDecoder() - decoder.userInfo = [ - .siteID: siteID - ] - - if hasDataEnvelope(in: response) { - return try decoder.decode(SitePluginEnvelope.self, from: response).plugin - } else { - return try decoder.decode(SitePlugin.self, from: response) - } - } -} - - -/// SitePluginEnvelope Disposable Entity: -/// The plugins endpoint returns the document within a `data` key. This entity -/// allows us to do parse the returned plugin model with JSONDecoder. -/// -private struct SitePluginEnvelope: Decodable { - let plugin: SitePlugin - - private enum CodingKeys: String, CodingKey { - case plugin = "data" + try extract(from: response, siteID: siteID) } } diff --git a/Networking/Networking/Mapper/SitePluginsMapper.swift b/Networking/Networking/Mapper/SitePluginsMapper.swift index 78c3b6c71af..72a7b4bbebc 100644 --- a/Networking/Networking/Mapper/SitePluginsMapper.swift +++ b/Networking/Networking/Mapper/SitePluginsMapper.swift @@ -1,39 +1 @@ -import Foundation - -/// Mapper: SitePlugins -/// -struct SitePluginsMapper: Mapper { - - /// Site Identifier associated to the plugins that will be parsed. - /// We're injecting this field via `JSONDecoder.userInfo` because the remote endpoints don't return the SiteID in the plugin endpoint. - /// - let siteID: Int64 - - /// (Attempts) to convert a dictionary into [SitePlugin]. - /// - func map(response: Data) throws -> [SitePlugin] { - let decoder = JSONDecoder() - decoder.userInfo = [ - .siteID: siteID - ] - - if hasDataEnvelope(in: response) { - return try decoder.decode(SitePluginsEnvelope.self, from: response).plugins - } else { - return try decoder.decode([SitePlugin].self, from: response) - } - } -} - - -/// SitePluginsEnvelope Disposable Entity: -/// The plugins endpoint returns the document within a `data` key. This entity -/// allows us to do parse all the things with JSONDecoder. -/// -private struct SitePluginsEnvelope: Decodable { - let plugins: [SitePlugin] - - private enum CodingKeys: String, CodingKey { - case plugins = "data" - } -} +typealias SitePluginsMapper = SiteIDMapper<[SitePlugin]> diff --git a/Networking/Networking/Mapper/SiteSettingMapper.swift b/Networking/Networking/Mapper/SiteSettingMapper.swift index 3266cd1ecee..00579546f93 100644 --- a/Networking/Networking/Mapper/SiteSettingMapper.swift +++ b/Networking/Networking/Mapper/SiteSettingMapper.swift @@ -1,5 +1,3 @@ -import Foundation - /// Mapper for a single SiteSetting /// struct SiteSettingMapper: Mapper { @@ -25,23 +23,6 @@ struct SiteSettingMapper: Mapper { .settingGroupKey: settingsGroup.rawValue ] - if hasDataEnvelope(in: response) { - return try decoder.decode(SiteSettingEnvelope.self, from: response).setting - } else { - return try decoder.decode(SiteSetting.self, from: response) - } - } -} - - -/// SiteSettingEnvelope Disposable Entity: -/// The plugins endpoint returns the document within a `data` key. This entity -/// allows us to do parse the returned plugin model with JSONDecoder. -/// -private struct SiteSettingEnvelope: Decodable { - let setting: SiteSetting - - private enum CodingKeys: String, CodingKey { - case setting = "data" + return try extract(from: response, using: decoder) } } diff --git a/Networking/Networking/Mapper/SiteSettingsMapper.swift b/Networking/Networking/Mapper/SiteSettingsMapper.swift index 57383d87b80..69cc6361916 100644 --- a/Networking/Networking/Mapper/SiteSettingsMapper.swift +++ b/Networking/Networking/Mapper/SiteSettingsMapper.swift @@ -1,6 +1,3 @@ -import Foundation - - /// Mapper: SiteSettings /// struct SiteSettingsMapper: Mapper { @@ -26,23 +23,6 @@ struct SiteSettingsMapper: Mapper { .settingGroupKey: settingsGroup.rawValue ] - if hasDataEnvelope(in: response) { - return try decoder.decode(SiteSettingsEnvelope.self, from: response).settings - } else { - return try decoder.decode([SiteSetting].self, from: response) - } - } -} - - -/// SiteSettingsEnvelope Disposable Entity: -/// The settings endpoint returns the settings document within a `data` key. This entity -/// allows us to do parse all the things with JSONDecoder. -/// -private struct SiteSettingsEnvelope: Decodable { - let settings: [SiteSetting] - - private enum CodingKeys: String, CodingKey { - case settings = "data" + return try extract(from: response, using: decoder) } } diff --git a/Networking/Networking/Mapper/StoreOnboardingTaskListMapper.swift b/Networking/Networking/Mapper/StoreOnboardingTaskListMapper.swift index e5bf92d7e3b..0facc64dd50 100644 --- a/Networking/Networking/Mapper/StoreOnboardingTaskListMapper.swift +++ b/Networking/Networking/Mapper/StoreOnboardingTaskListMapper.swift @@ -1,5 +1,3 @@ -import Foundation - /// Mapper: StoreOnboardingTask /// struct StoreOnboardingTaskListMapper: Mapper { @@ -9,8 +7,8 @@ struct StoreOnboardingTaskListMapper: Mapper { if hasDataEnvelope(in: response) { taskGroup = try decoder - .decode(StoreOnboardingTaskEnvelope.self, from: response) - .group + .decode(Envelope<[StoreOnboardingTaskGroup]>.self, from: response) + .data } else { taskGroup = try decoder.decode([StoreOnboardingTaskGroup].self, from: response) } @@ -37,11 +35,3 @@ private struct StoreOnboardingTaskGroup: Decodable { case tasks } } - -private struct StoreOnboardingTaskEnvelope: Decodable { - let group: [StoreOnboardingTaskGroup] - - private enum CodingKeys: String, CodingKey { - case group = "data" - } -} diff --git a/Networking/Networking/Mapper/StripeAccountMapper.swift b/Networking/Networking/Mapper/StripeAccountMapper.swift index 04673fce20b..27a121d0bfc 100644 --- a/Networking/Networking/Mapper/StripeAccountMapper.swift +++ b/Networking/Networking/Mapper/StripeAccountMapper.swift @@ -1,5 +1,3 @@ -import Foundation - /// Mapper: Stripe Account /// struct StripeAccountMapper: Mapper { @@ -9,28 +7,11 @@ struct StripeAccountMapper: Mapper { func map(response: Data) throws -> StripeAccount { let decoder = JSONDecoder() - /// Needed for currentDeadline, which is given as a UNIX timestamp. - /// Unfortunately other properties use other formats for dates, but we - /// can cross that bridge when we need those decoded. + // Needed for currentDeadline, which is given as a UNIX timestamp. + // Unfortunately other properties use other formats for dates, but we + // can cross that bridge when we need those decoded. decoder.dateDecodingStrategy = .secondsSince1970 - if hasDataEnvelope(in: response) { - return try decoder.decode(StripeAccountEnvelope.self, from: response).account - } else { - return try decoder.decode(StripeAccount.self, from: response) - } - } -} - -/// StripeAccountEnvelope Disposable Entity -/// -/// Account endpoint returns the requested account in the `data` key. This entity -/// allows us to parse it with JSONDecoder. -/// -private struct StripeAccountEnvelope: Decodable { - let account: StripeAccount - - private enum CodingKeys: String, CodingKey { - case account = "data" + return try extract(from: response, using: decoder) } } diff --git a/Networking/Networking/Mapper/SubscriptionListMapper.swift b/Networking/Networking/Mapper/SubscriptionListMapper.swift index 3ed865c3be4..8ebcc4e8595 100644 --- a/Networking/Networking/Mapper/SubscriptionListMapper.swift +++ b/Networking/Networking/Mapper/SubscriptionListMapper.swift @@ -1,39 +1 @@ -import Foundation - -/// Mapper: `Subscription` List -/// -struct SubscriptionListMapper: Mapper { - /// Site we're parsing `Subscription`s for - /// We're injecting this field by copying it in after parsing responses, because `siteID` is not returned in any of the Subscription endpoints. - /// - let siteID: Int64 - - /// (Attempts) to convert a dictionary into `[Subscription]`. - /// - func map(response: Data) throws -> [Subscription] { - let decoder = JSONDecoder() - decoder.dateDecodingStrategy = .formatted(DateFormatter.Defaults.dateTimeFormatter) - decoder.userInfo = [ - .siteID: siteID - ] - - if hasDataEnvelope(in: response) { - return try decoder.decode(SubscriptionListEnvelope.self, from: response).subscriptions - } else { - return try decoder.decode([Subscription].self, from: response) - } - } -} - - -/// SubscriptionListEnvelope Disposable Entity: -/// Load Subscriptions endpoint returns the subscriptions in the `data` key. -/// This entity allows us to parse all the things with JSONDecoder. -/// -private struct SubscriptionListEnvelope: Decodable { - let subscriptions: [Subscription] - - private enum CodingKeys: String, CodingKey { - case subscriptions = "data" - } -} +typealias SubscriptionListMapper = SiteIDMapper<[Subscription]> diff --git a/Networking/Networking/Mapper/SubscriptionMapper.swift b/Networking/Networking/Mapper/SubscriptionMapper.swift index 3db02705c76..6b38d57d6da 100644 --- a/Networking/Networking/Mapper/SubscriptionMapper.swift +++ b/Networking/Networking/Mapper/SubscriptionMapper.swift @@ -1,39 +1 @@ -import Foundation - -/// Mapper: `Subscription` -/// -struct SubscriptionMapper: Mapper { - /// Site we're parsing `Subscription` for - /// We're injecting this field by copying it in after parsing responses, because `siteID` is not returned in any of the Subscription endpoints. - /// - let siteID: Int64 - - /// (Attempts) to convert a dictionary into `Subscription`. - /// - func map(response: Data) throws -> Subscription { - let decoder = JSONDecoder() - decoder.dateDecodingStrategy = .formatted(DateFormatter.Defaults.dateTimeFormatter) - decoder.userInfo = [ - .siteID: siteID - ] - - if hasDataEnvelope(in: response) { - return try decoder.decode(SubscriptionEnvelope.self, from: response).subscription - } else { - return try decoder.decode(Subscription.self, from: response) - } - } -} - - -/// SubscriptionEnvelope Disposable Entity: -/// Load Subscription endpoint returns the subscription in the `data` key. -/// This entity allows us to parse all the things with JSONDecoder. -/// -private struct SubscriptionEnvelope: Decodable { - let subscription: Subscription - - private enum CodingKeys: String, CodingKey { - case subscription = "data" - } -} +typealias SubscriptionMapper = SiteIDMapper<Subscription> diff --git a/Networking/Networking/Mapper/SuccessDataResultMapper.swift b/Networking/Networking/Mapper/SuccessDataResultMapper.swift index a2f84fadb35..9d07484cb60 100644 --- a/Networking/Networking/Mapper/SuccessDataResultMapper.swift +++ b/Networking/Networking/Mapper/SuccessDataResultMapper.swift @@ -1,6 +1,3 @@ -import Foundation - - /// Mapper: Success Result Wrapped in `data` Key /// struct SuccessDataResultMapper: Mapper { @@ -9,27 +6,16 @@ struct SuccessDataResultMapper: Mapper { /// func map(response: Data) throws -> Bool { let decoder = JSONDecoder() - let rawData: [String: Bool] = try { - if hasDataEnvelope(in: response) { - return try decoder.decode(SuccessDataResultEnvelope.self, from: response).rawData - } else { - return try decoder.decode([String: Bool].self, from: response) - } - }() - return rawData["success"] ?? false + let successResponse: SuccessResponse + if hasDataEnvelope(in: response) { + successResponse = try decoder.decode(Envelope<SuccessResponse>.self, from: response).data + } else { + successResponse = try decoder.decode(SuccessResponse.self, from: response) + } + return successResponse.success ?? false } } - -/// SuccessDataResultEnvelope Disposable Entity -/// -/// Some endpoints return a "success" response in the `data` key. This entity -/// allows us to parse that response with JSONDecoder. -/// -private struct SuccessDataResultEnvelope: Decodable { - let rawData: [String: Bool] - - private enum CodingKeys: String, CodingKey { - case rawData = "data" - } +private struct SuccessResponse: Decodable { + let success: Bool? } diff --git a/Networking/Networking/Mapper/SystemPluginMapper.swift b/Networking/Networking/Mapper/SystemPluginMapper.swift index f4e426a2f21..f3f8e2b1cdb 100644 --- a/Networking/Networking/Mapper/SystemPluginMapper.swift +++ b/Networking/Networking/Mapper/SystemPluginMapper.swift @@ -19,7 +19,7 @@ struct SystemPluginMapper: Mapper { let systemStatus: SystemStatus = try { if hasDataEnvelope(in: response) { - return try decoder.decode(SystemStatusEnvelope.self, from: response).systemStatus + return try decoder.decode(Envelope<SystemStatus>.self, from: response).data } else { return try decoder.decode(SystemStatus.self, from: response) } diff --git a/Networking/Networking/Mapper/SystemStatusMapper.swift b/Networking/Networking/Mapper/SystemStatusMapper.swift index 1145352e23f..939d1115542 100644 --- a/Networking/Networking/Mapper/SystemStatusMapper.swift +++ b/Networking/Networking/Mapper/SystemStatusMapper.swift @@ -1,37 +1 @@ -import Foundation - -/// Mapper: System Status -/// -struct SystemStatusMapper: Mapper { - - /// Site Identifier associated to the system status that will be parsed. - /// We're injecting this field via `JSONDecoder.userInfo` because the remote endpoints don't return the SiteID in the system plugin endpoint. - /// - let siteID: Int64 - - /// (Attempts) to convert a dictionary into SystemStatus - /// - func map(response: Data) throws -> SystemStatus { - let decoder = JSONDecoder() - decoder.userInfo = [ - .siteID: siteID - ] - - if hasDataEnvelope(in: response) { - return try decoder.decode(SystemStatusEnvelope.self, from: response).systemStatus - } else { - return try decoder.decode(SystemStatus.self, from: response) - } - } -} - -/// System Status endpoint returns the requested account in the `data` key. This entity -/// allows us to parse it with JSONDecoder. -/// -struct SystemStatusEnvelope: Decodable { - let systemStatus: SystemStatus - - private enum CodingKeys: String, CodingKey { - case systemStatus = "data" - } -} +typealias SystemStatusMapper = SiteIDMapper<SystemStatus> diff --git a/Networking/Networking/Mapper/TaxClassListMapper.swift b/Networking/Networking/Mapper/TaxClassListMapper.swift index 555522d4e7a..8e7f9d9f51f 100644 --- a/Networking/Networking/Mapper/TaxClassListMapper.swift +++ b/Networking/Networking/Mapper/TaxClassListMapper.swift @@ -1,40 +1 @@ -import Foundation - - -/// Mapper: TaxClass List -/// -struct TaxClassListMapper: Mapper { - - /// Site Identifier associated to the API information that will be parsed. - /// We're injecting this field via `JSONDecoder.userInfo` because the remote endpoints don't return the SiteID. - /// - let siteID: Int64 - - /// (Attempts) to convert a dictionary into [TaxClass]. - /// - func map(response: Data) throws -> [TaxClass] { - let decoder = JSONDecoder() - decoder.userInfo = [ - .siteID: siteID - ] - - if hasDataEnvelope(in: response) { - return try decoder.decode(TaxClassListEnvelope.self, from: response).taxClasses - } else { - return try decoder.decode([TaxClass].self, from: response) - } - } -} - - -/// TaxClassListEnvelope Disposable Entity: -/// `Load All Tax Classes` endpoint returns the tax classes document in the `data` key. -/// This entity allows us to do parse all the things with JSONDecoder. -/// -private struct TaxClassListEnvelope: Decodable { - let taxClasses: [TaxClass] - - private enum CodingKeys: String, CodingKey { - case taxClasses = "data" - } -} +typealias TaxClassListMapper = SiteIDMapper<[TaxClass]> diff --git a/Networking/Networking/Mapper/TaxRateListMapper.swift b/Networking/Networking/Mapper/TaxRateListMapper.swift index 2033639212d..d8a4e49470f 100644 --- a/Networking/Networking/Mapper/TaxRateListMapper.swift +++ b/Networking/Networking/Mapper/TaxRateListMapper.swift @@ -1,38 +1 @@ -import Foundation - -/// Mapper: TaxRate List -/// -struct TaxRateListMapper: Mapper { - - /// Site Identifier associated to the API information that will be parsed. - /// We're injecting this field via `JSONDecoder.userInfo` because the remote endpoints don't return the SiteID. - /// - let siteID: Int64 - - /// (Attempts) to convert a dictionary into [TaxRate]. - /// - func map(response: Data) throws -> [TaxRate] { - let decoder = JSONDecoder() - decoder.userInfo = [ - .siteID: siteID - ] - - if hasDataEnvelope(in: response) { - return try decoder.decode(TaxRateListEnvelope.self, from: response).taxClasses - } else { - return try decoder.decode([TaxRate].self, from: response) - } - } -} - -/// TaxRateListEnvelope Disposable Entity: -/// `Load All Tax Rates` endpoint returns the tax rates document in the `data` key. -/// This entity allows us to do parse all the things with JSONDecoder. -/// -private struct TaxRateListEnvelope: Decodable { - let taxClasses: [TaxRate] - - private enum CodingKeys: String, CodingKey { - case taxClasses = "data" - } -} +typealias TaxRateListMapper = SiteIDMapper<[TaxRate]> diff --git a/Networking/Networking/Mapper/TaxRateMapper.swift b/Networking/Networking/Mapper/TaxRateMapper.swift index 9d76acca431..10232e85923 100644 --- a/Networking/Networking/Mapper/TaxRateMapper.swift +++ b/Networking/Networking/Mapper/TaxRateMapper.swift @@ -1,43 +1 @@ -import Foundation - -/// Mapper: TaxRate -/// -struct TaxRateMapper: Mapper { - - /// Site Identifier associated to the taxRate that will be parsed. - /// - /// We're injecting this field via `JSONDecoder.userInfo` because SiteID is not returned in any of the TaxRate Endpoints. - /// - let siteID: Int64 - - - /// (Attempts) to convert a dictionary into TaxRate. - /// - func map(response: Data) throws -> TaxRate { - let decoder = JSONDecoder() - decoder.dateDecodingStrategy = .formatted(DateFormatter.Defaults.dateTimeFormatter) - decoder.userInfo = [ - .siteID: siteID - ] - - if hasDataEnvelope(in: response) { - return try decoder.decode(TaxRateEnvelope.self, from: response).taxRate - } else { - return try decoder.decode(TaxRate.self, from: response) - } - } -} - - -/// TaxRate Envelope Disposable Entity -/// -/// `Load TaxRate` endpoint returns the requested taxRate document in the `data` key. This entity -/// allows us to do parse all the things with JSONDecoder. -/// -private struct TaxRateEnvelope: Decodable { - let taxRate: TaxRate - - private enum CodingKeys: String, CodingKey { - case taxRate = "data" - } -} +typealias TaxRateMapper = SiteIDMapper<TaxRate> diff --git a/Networking/Networking/Mapper/UserMapper.swift b/Networking/Networking/Mapper/UserMapper.swift index c5f032ec12b..3d3126efa41 100644 --- a/Networking/Networking/Mapper/UserMapper.swift +++ b/Networking/Networking/Mapper/UserMapper.swift @@ -1,38 +1 @@ -import Foundation - -/// Mapper: User -/// -struct UserMapper: Mapper { - /// Site Identifier associated to the order that will be parsed. - /// We're injecting this field via `JSONDecoder.userInfo` because SiteID is not returned in the endpoints used to retrieve User models. - /// - let siteID: Int64 - - /// (Attempts) to convert a dictionary into User. - /// - func map(response: Data) throws -> User { - let decoder = JSONDecoder() - decoder.userInfo = [ - .siteID: siteID - ] - - if hasDataEnvelope(in: response) { - return try decoder.decode(UserEnvelope.self, from: response).user - } else { - return try decoder.decode(User.self, from: response) - } - } -} - -/// UserEnvelope Disposable Entity -/// -/// `Load User` endpoint returns the requested objects in the `data` key. This entity -/// allows us to parse all the things with JSONDecoder. -/// -private struct UserEnvelope: Decodable { - let user: User - - private enum CodingKeys: String, CodingKey { - case user = "data" - } -} +typealias UserMapper = SiteIDMapper<User> diff --git a/Networking/Networking/Mapper/WCAnalyticsCustomerMapper.swift b/Networking/Networking/Mapper/WCAnalyticsCustomerMapper.swift index d744f033658..e8d227d765d 100644 --- a/Networking/Networking/Mapper/WCAnalyticsCustomerMapper.swift +++ b/Networking/Networking/Mapper/WCAnalyticsCustomerMapper.swift @@ -1,30 +1 @@ -import Foundation - -/// Mapper: WCAnalyticsCustomer -/// -struct WCAnalyticsCustomerMapper: Mapper { - /// We're injecting this field by copying it in after parsing responses, because `siteID` is not returned in any of the Customer endpoints. - /// - let siteID: Int64 - - /// (Attempts) to convert a dictionary into a `[WCAnalyticsCustomer]` entity - /// - func map(response: Data) throws -> [WCAnalyticsCustomer] { - let decoder = JSONDecoder() - decoder.dateDecodingStrategy = .formatted(DateFormatter.Defaults.dateTimeFormatter) - decoder.userInfo = [.siteID: siteID] - if hasDataEnvelope(in: response) { - return try decoder.decode(WCAnalyticsCustomerEnvelope.self, from: response).customer - } else { - return try decoder.decode([WCAnalyticsCustomer].self, from: response) - } - } -} - -private struct WCAnalyticsCustomerEnvelope: Decodable { - let customer: [WCAnalyticsCustomer] - - private enum CodingKeys: String, CodingKey { - case customer = "data" - } -} +typealias WCAnalyticsCustomerMapper = SiteIDMapper<[WCAnalyticsCustomer]> diff --git a/Networking/Networking/Mapper/WCPayAccountMapper.swift b/Networking/Networking/Mapper/WCPayAccountMapper.swift index 5aed6bb5115..26c2c70e259 100644 --- a/Networking/Networking/Mapper/WCPayAccountMapper.swift +++ b/Networking/Networking/Mapper/WCPayAccountMapper.swift @@ -1,5 +1,3 @@ -import Foundation - /// Mapper: WCPay account /// struct WCPayAccountMapper: Mapper { @@ -16,37 +14,12 @@ struct WCPayAccountMapper: Mapper { /// Prior to WooCommerce Payments plugin version 2.9.0 (Aug 2021) `data` could contain an empty array [] /// indicating that the plugin was active but the merchant had not on-boarded (and therefore has no account.) - if let _ = try? decoder.decode(WCPayNullAccountEnvelope.self, from: response) { + if let _ = try? decoder.decode(Envelope<[String]>.self, from: response) { return WCPayAccount.noAccount } else if let _ = try? decoder.decode([String].self, from: response) { return WCPayAccount.noAccount } - if hasDataEnvelope(in: response) { - return try decoder.decode(WCPayAccountEnvelope.self, from: response).account - } else { - return try decoder.decode(WCPayAccount.self, from: response) - } - } -} - -private struct WCPayNullAccountEnvelope: Decodable { - let emptyArray: [String] - - private enum CodingKeys: String, CodingKey { - case emptyArray = "data" - } -} - -/// WCPayAccountEnvelope Disposable Entity -/// -/// Account endpoint returns the requested account in the `data` key. This entity -/// allows us to parse it with JSONDecoder. -/// -private struct WCPayAccountEnvelope: Decodable { - let account: WCPayAccount - - private enum CodingKeys: String, CodingKey { - case account = "data" + return try extract(from: response, using: decoder) } } diff --git a/Networking/Networking/Mapper/WCPayChargeMapper.swift b/Networking/Networking/Mapper/WCPayChargeMapper.swift index 9e460f38dec..f8959fcc350 100644 --- a/Networking/Networking/Mapper/WCPayChargeMapper.swift +++ b/Networking/Networking/Mapper/WCPayChargeMapper.swift @@ -1,5 +1,3 @@ -import Foundation - /// Mapper: WCPayCharge /// struct WCPayChargeMapper: Mapper { @@ -16,23 +14,9 @@ struct WCPayChargeMapper: Mapper { /// can cross that bridge when we need those decoded. decoder.dateDecodingStrategy = .secondsSince1970 - if hasDataEnvelope(in: response) { - return try decoder.decode(WCPayChargeEnvelope.self, from: response).charge - } else { - return try decoder.decode(WCPayCharge.self, from: response) - } - } -} - -/// WCPayChargeEnvelope Disposable Entity -/// -/// Account endpoint returns the requested account in the `data` key. This entity -/// allows us to parse it with JSONDecoder. -/// -private struct WCPayChargeEnvelope: Decodable { - let charge: WCPayCharge - - private enum CodingKeys: String, CodingKey { - case charge = "data" + return try extract( + from: response, + using: decoder + ) } }