Return response instead of data
This commit is contained in:
@@ -20,7 +20,7 @@ public actor OpenFoodFactsClient {
|
|||||||
/// - barcode: The product barcode.
|
/// - barcode: The product barcode.
|
||||||
/// - fields: Optional list of fields to fetch (optimizes network usage).
|
/// - fields: Optional list of fields to fetch (optimizes network usage).
|
||||||
public func product(barcode: String, fields: [ProductField]? = nil)
|
public func product(barcode: String, fields: [ProductField]? = nil)
|
||||||
async throws -> Product?
|
async throws -> ProductResponseEnvelope
|
||||||
{
|
{
|
||||||
var url = config.apiURL.appendingPathComponent("/product/\(barcode)")
|
var url = config.apiURL.appendingPathComponent("/product/\(barcode)")
|
||||||
|
|
||||||
@@ -48,13 +48,13 @@ public actor OpenFoodFactsClient {
|
|||||||
|
|
||||||
let envelope = try JSONDecoder().decode(
|
let envelope = try JSONDecoder().decode(
|
||||||
ProductResponseEnvelope.self, from: data)
|
ProductResponseEnvelope.self, from: data)
|
||||||
return envelope.product
|
return envelope
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Search for products using V2 API.
|
/// Search for products using V2 API.
|
||||||
public func search(
|
public func search(
|
||||||
_ parameters: SearchParameter..., fields: [ProductField]? = nil
|
_ parameters: SearchParameter..., fields: [ProductField]? = nil
|
||||||
) async throws -> [Product] {
|
) async throws -> SearchResponseEnvelope {
|
||||||
guard
|
guard
|
||||||
var components = URLComponents(
|
var components = URLComponents(
|
||||||
url: config.apiURL.appendingPathComponent("/search"),
|
url: config.apiURL.appendingPathComponent("/search"),
|
||||||
@@ -106,7 +106,7 @@ public actor OpenFoodFactsClient {
|
|||||||
|
|
||||||
let envelope = try JSONDecoder().decode(
|
let envelope = try JSONDecoder().decode(
|
||||||
SearchResponseEnvelope.self, from: data)
|
SearchResponseEnvelope.self, from: data)
|
||||||
return envelope.products ?? []
|
return envelope
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Private Helpers
|
// MARK: - Private Helpers
|
||||||
@@ -124,14 +124,26 @@ public enum OFFError: Error {
|
|||||||
case invalidURL, invalidResponse
|
case invalidURL, invalidResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal Envelopes for Decoding
|
public struct ProductResponseEnvelope: Sendable, Decodable {
|
||||||
struct ProductResponseEnvelope: Decodable {
|
public let code: String?
|
||||||
let code: String?
|
public let product: Product?
|
||||||
let product: Product?
|
public let status: Int?
|
||||||
let status: Int?
|
public let statusVerbose: String?
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case code, product, status
|
||||||
|
case statusVerbose = "status_verbose"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SearchResponseEnvelope: Decodable {
|
public struct SearchResponseEnvelope: Sendable, Decodable {
|
||||||
let count: Int?
|
public let count: Int?
|
||||||
let products: [Product]?
|
public let page: Int?
|
||||||
|
public let pageSize: Int?
|
||||||
|
public let products: [Product]?
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case count, page, products
|
||||||
|
case pageSize = "page_size"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,11 +18,13 @@ final class OpenFoodFactsTests: XCTestCase {
|
|||||||
|
|
||||||
func testProductFetch() async throws {
|
func testProductFetch() async throws {
|
||||||
// Fetch specific fields only
|
// Fetch specific fields only
|
||||||
let product = try await client.product(
|
let response = try await client.product(
|
||||||
barcode: "3017620422003", // Nutella
|
barcode: "3017620422003", // Nutella
|
||||||
fields: [.code, .productName, .nutriscoreGrade, .nutriments]
|
fields: [.code, .productName, .nutriscoreGrade, .nutriments]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let product = response.product
|
||||||
|
|
||||||
XCTAssertEqual(product?.code, "3017620422003")
|
XCTAssertEqual(product?.code, "3017620422003")
|
||||||
XCTAssertNotNil(product?.productName)
|
XCTAssertNotNil(product?.productName)
|
||||||
XCTAssertNotNil(product?.nutriscoreGrade)
|
XCTAssertNotNil(product?.nutriscoreGrade)
|
||||||
@@ -31,13 +33,15 @@ final class OpenFoodFactsTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testSearch() async throws {
|
func testSearch() async throws {
|
||||||
let results = try await client.search(
|
let response = try await client.search(
|
||||||
.query("chocolate"),
|
.query("chocolate"),
|
||||||
.tag(tag: .brands, value: "milka"),
|
.tag(tag: .brands, value: "milka"),
|
||||||
.pageSize(5),
|
.pageSize(5),
|
||||||
.sort(.popularity),
|
.sort(.popularity),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let results = response.products ?? []
|
||||||
|
|
||||||
let jsonResults = try JSONEncoder().encode(results)
|
let jsonResults = try JSONEncoder().encode(results)
|
||||||
try jsonResults.write(to: .init(filePath: "./jsonResults.json"))
|
try jsonResults.write(to: .init(filePath: "./jsonResults.json"))
|
||||||
|
|
||||||
|
|||||||
1
jsonResults.json
Normal file
1
jsonResults.json
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user