Skip to content

Commit d3b1844

Browse files
committed
Add BookingResource in Networking layer
1 parent c39e044 commit d3b1844

File tree

7 files changed

+233
-0
lines changed

7 files changed

+233
-0
lines changed

Modules/Sources/Fakes/Networking.generated.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,23 @@ extension Networking.Booking {
344344
)
345345
}
346346
}
347+
extension Networking.BookingResource {
348+
/// Returns a "ready to use" type filled with fake values.
349+
///
350+
public static func fake() -> Networking.BookingResource {
351+
.init(
352+
id: .fake(),
353+
name: .fake(),
354+
qty: .fake(),
355+
role: .fake(),
356+
email: .fake(),
357+
phoneNumber: .fake(),
358+
imageID: .fake(),
359+
imageURL: .fake(),
360+
description: .fake()
361+
)
362+
}
363+
}
347364
extension Networking.CompositeComponentOptionType {
348365
/// Returns a "ready to use" type filled with fake values.
349366
///
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import Foundation
2+
3+
/// Mapper: BookingResource
4+
///
5+
struct BookingResourceMapper: Mapper {
6+
let siteID: Int64
7+
8+
func map(response: Data) throws -> BookingResource {
9+
let decoder = JSONDecoder()
10+
decoder.userInfo = [
11+
.siteID: siteID
12+
]
13+
if hasDataEnvelope(in: response) {
14+
return try decoder.decode(BookingResourceEnvelope.self, from: response).bookingResource
15+
} else {
16+
return try decoder.decode(BookingResource.self, from: response)
17+
}
18+
}
19+
}
20+
21+
private struct BookingResourceEnvelope: Decodable {
22+
let bookingResource: BookingResource
23+
24+
private enum CodingKeys: String, CodingKey {
25+
case bookingResource = "data"
26+
}
27+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import Codegen
2+
import Foundation
3+
4+
public struct BookingResource: Hashable, Decodable, GeneratedFakeable, GeneratedCopiable {
5+
public let siteID: Int64
6+
public let id: Int64
7+
public let name: String
8+
public let qty: Int64
9+
public let role: String
10+
public let email: String?
11+
public let phoneNumber: String?
12+
public let imageID: Int64
13+
public let imageURL: String?
14+
public let description: String?
15+
16+
public init(siteID: Int64,
17+
id: Int64,
18+
name: String,
19+
qty: Int64,
20+
role: String,
21+
email: String?,
22+
phoneNumber: String?,
23+
imageID: Int64,
24+
imageURL: String?,
25+
description: String?) {
26+
self.siteID = siteID
27+
self.id = id
28+
self.name = name
29+
self.qty = qty
30+
self.role = role
31+
self.email = email
32+
self.phoneNumber = phoneNumber
33+
self.imageID = imageID
34+
self.imageURL = imageURL
35+
self.description = description
36+
}
37+
38+
public init(from decoder: Decoder) throws {
39+
guard let siteID = decoder.userInfo[.siteID] as? Int64 else {
40+
throw BookingResourceDecodingError.missingSiteID
41+
}
42+
43+
let container = try decoder.container(keyedBy: CodingKeys.self)
44+
45+
let id = try container.decode(Int64.self, forKey: .id)
46+
let name = try container.decode(String.self, forKey: .name)
47+
let qty = try container.decode(Int64.self, forKey: .qty)
48+
let role = try container.decode(String.self, forKey: .role)
49+
let email = try container.decodeIfPresent(String.self, forKey: .email)
50+
let phoneNumber = try container.decodeIfPresent(String.self, forKey: .phoneNumber)
51+
let imageID = try container.decode(Int64.self, forKey: .imageID)
52+
let imageURL = try container.decodeIfPresent(String.self, forKey: .imageURL)
53+
let description = try container.decodeIfPresent(String.self, forKey: .description)
54+
55+
self.init(siteID: siteID,
56+
id: id,
57+
name: name,
58+
qty: qty,
59+
role: role,
60+
email: email,
61+
phoneNumber: phoneNumber,
62+
imageID: imageID,
63+
imageURL: imageURL,
64+
description: description)
65+
}
66+
}
67+
68+
private extension BookingResource {
69+
enum CodingKeys: String, CodingKey {
70+
case id
71+
case name
72+
case qty
73+
case role
74+
case email
75+
case phoneNumber = "phone_number"
76+
case imageID = "image_id"
77+
case imageURL = "image_url"
78+
case description
79+
}
80+
}
81+
82+
// MARK: - Decoding Errors
83+
//
84+
enum BookingResourceDecodingError: Error {
85+
case missingSiteID
86+
}

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,42 @@ extension Networking.Booking {
494494
}
495495
}
496496

497+
extension Networking.BookingResource {
498+
public func copy(
499+
id: CopiableProp<Int64> = .copy,
500+
name: CopiableProp<String> = .copy,
501+
qty: CopiableProp<Int64> = .copy,
502+
role: CopiableProp<String> = .copy,
503+
email: NullableCopiableProp<String> = .copy,
504+
phoneNumber: NullableCopiableProp<String> = .copy,
505+
imageID: CopiableProp<Int64> = .copy,
506+
imageURL: NullableCopiableProp<String> = .copy,
507+
description: NullableCopiableProp<String> = .copy
508+
) -> Networking.BookingResource {
509+
let id = id ?? self.id
510+
let name = name ?? self.name
511+
let qty = qty ?? self.qty
512+
let role = role ?? self.role
513+
let email = email ?? self.email
514+
let phoneNumber = phoneNumber ?? self.phoneNumber
515+
let imageID = imageID ?? self.imageID
516+
let imageURL = imageURL ?? self.imageURL
517+
let description = description ?? self.description
518+
519+
return Networking.BookingResource(
520+
id: id,
521+
name: name,
522+
qty: qty,
523+
role: role,
524+
email: email,
525+
phoneNumber: phoneNumber,
526+
imageID: imageID,
527+
imageURL: imageURL,
528+
description: description
529+
)
530+
}
531+
}
532+
497533
extension Networking.Coupon {
498534
public func copy(
499535
siteID: CopiableProp<Int64> = .copy,

Modules/Sources/Networking/Remote/BookingsRemote.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ public protocol BookingsRemoteProtocol {
1616

1717
func loadBooking(bookingID: Int64,
1818
siteID: Int64) async throws -> Booking?
19+
20+
func fetchResource(resourceID: Int64,
21+
siteID: Int64) async throws -> BookingResource?
1922
}
2023

2124
/// Booking: Remote Endpoints
@@ -84,6 +87,24 @@ public final class BookingsRemote: Remote, BookingsRemoteProtocol {
8487

8588
return try await enqueue(request, mapper: mapper)
8689
}
90+
91+
public func fetchResource(
92+
resourceID: Int64,
93+
siteID: Int64
94+
) async throws -> BookingResource? {
95+
let path = "\(Path.resources)/\(resourceID)"
96+
let request = JetpackRequest(
97+
wooApiVersion: .wcBookings,
98+
method: .get,
99+
siteID: siteID,
100+
path: path,
101+
availableAsRESTRequest: true
102+
)
103+
104+
let mapper = BookingResourceMapper(siteID: siteID)
105+
106+
return try await enqueue(request, mapper: mapper)
107+
}
87108
}
88109

89110
// MARK: - Constants
@@ -101,6 +122,7 @@ public extension BookingsRemote {
101122

102123
private enum Path {
103124
static let bookings = "bookings"
125+
static let resources = "resources"
104126
}
105127

106128
private enum ParameterKey {

Modules/Tests/NetworkingTests/Remote/BookingsRemoteTests.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,37 @@ struct BookingsRemoteTests {
9090
#expect(parameters["per_page"] != nil)
9191
#expect(parameters["order"] != nil)
9292
}
93+
94+
@Test func test_fetchResource_properly_returns_parsed_resource() async throws {
95+
// Given
96+
let remote = BookingsRemote(network: network)
97+
let resourceID: Int64 = 22
98+
network.simulateResponse(requestUrlSuffix: "resources/\(resourceID)", filename: "booking-resource")
99+
100+
// When
101+
let resource = try await remote.fetchResource(resourceID: resourceID, siteID: sampleSiteID)
102+
103+
// Then
104+
let unwrappedResource = try #require(resource)
105+
#expect(unwrappedResource.id == 22)
106+
#expect(unwrappedResource.name == "Joel (Sample resource)")
107+
#expect(unwrappedResource.qty == 1)
108+
#expect(unwrappedResource.role == "")
109+
#expect(unwrappedResource.email == "")
110+
#expect(unwrappedResource.phoneNumber == "")
111+
#expect(unwrappedResource.imageID == 0)
112+
#expect(unwrappedResource.imageURL == "")
113+
#expect(unwrappedResource.description == "")
114+
#expect(unwrappedResource.siteID == sampleSiteID)
115+
}
116+
117+
@Test func test_fetchResource_properly_relays_networking_errors() async {
118+
// Given
119+
let remote = BookingsRemote(network: network)
120+
121+
// Then
122+
await #expect(throws: NetworkError.notFound()) {
123+
_ = try await remote.fetchResource(resourceID: 22, siteID: sampleSiteID)
124+
}
125+
}
93126
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"id": 22,
3+
"name": "Joel (Sample resource)",
4+
"qty": 1,
5+
"role": "",
6+
"email": "",
7+
"phone_number": "",
8+
"image_id": 0,
9+
"image_url": "",
10+
"description": "",
11+
"note": ""
12+
}

0 commit comments

Comments
 (0)