Almost done
This commit is contained in:
@@ -82,7 +82,6 @@ final class ApiClient {
|
||||
}
|
||||
|
||||
func send<T: Decodable>(_ request: ApiRequest) async throws -> T {
|
||||
|
||||
#if canImport(LCEssentials) && os(iOS)
|
||||
return try await sendWithLCEssentials(request)
|
||||
#else
|
||||
@@ -119,7 +118,7 @@ private extension ApiClient {
|
||||
do {
|
||||
return try JSONDecoder().decode(T.self, from: data)
|
||||
} catch {
|
||||
throw NetworkError.decodeError(String(data: data, encoding: .utf8))
|
||||
throw NetworkError.decodeError(sanitizedBody(data))
|
||||
}
|
||||
} catch {
|
||||
let mapped = mapError(error)
|
||||
@@ -156,6 +155,7 @@ private extension ApiClient {
|
||||
case "POST": return .post
|
||||
case "PUT": return .put
|
||||
case "DELETE": return .delete
|
||||
case "PATCH": return .patch
|
||||
default: return .get
|
||||
}
|
||||
}
|
||||
@@ -274,7 +274,7 @@ private extension ApiClient {
|
||||
do {
|
||||
return try JSONDecoder().decode(type, from: data)
|
||||
} catch {
|
||||
throw NetworkError.decodeError(String(data: data, encoding: .utf8))
|
||||
throw NetworkError.decodeError(sanitizedBody(data))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,7 +328,7 @@ private extension ApiClient {
|
||||
return msg
|
||||
}
|
||||
}
|
||||
return String(data: data, encoding: .utf8)
|
||||
return sanitizedBody(data)
|
||||
}
|
||||
|
||||
func serverPayload(from data: Data) -> ApiErrorDescriptor? {
|
||||
@@ -364,48 +364,41 @@ private extension ApiClient {
|
||||
|
||||
#if canImport(LCEssentials) && os(iOS)
|
||||
func serverMessage(from error: NSError) -> String? {
|
||||
// Tenta buscar no userInfo por chaves conhecidas
|
||||
for key in [NSLocalizedFailureReasonErrorKey, "body", "responseBody", "data", "message"] {
|
||||
if let value = error.userInfo[key] as? String,
|
||||
!value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||
if let data = value.data(using: .utf8),
|
||||
let parsed = serverMessage(from: data),
|
||||
!parsed.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||
if parsed.trimmingCharacters(in: .whitespacesAndNewlines).hasPrefix("{") {
|
||||
continue
|
||||
}
|
||||
return parsed
|
||||
}
|
||||
if !value.trimmingCharacters(in: .whitespacesAndNewlines).hasPrefix("{") {
|
||||
return value
|
||||
let t = parsed.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if t.hasPrefix("{") { continue }
|
||||
if let safe = sanitizedMessage(t) { return safe }
|
||||
}
|
||||
let t = value.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if !t.hasPrefix("{"), let safe = sanitizedMessage(t) { return safe }
|
||||
} else if let valueData = error.userInfo[key] as? Data {
|
||||
if let parsed = serverMessage(from: valueData),
|
||||
!parsed.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty,
|
||||
!parsed.trimmingCharacters(in: .whitespacesAndNewlines).hasPrefix("{") {
|
||||
return parsed
|
||||
if let parsed = serverMessage(from: valueData) {
|
||||
let t = parsed.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if !t.hasPrefix("{"), let safe = sanitizedMessage(t) { return safe }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tenta varrer qualquer chave do userInfo por strings que possam ser JSON ou mensagens
|
||||
for (_, value) in error.userInfo {
|
||||
if let str = value as? String, !str.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||
if str.trimmingCharacters(in: .whitespacesAndNewlines).hasPrefix("{") {
|
||||
if let data = str.data(using: .utf8),
|
||||
let parsed = serverMessage(from: data),
|
||||
!parsed.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty,
|
||||
!parsed.trimmingCharacters(in: .whitespacesAndNewlines).hasPrefix("{") {
|
||||
return parsed
|
||||
}
|
||||
}
|
||||
if let str = value as? String,
|
||||
str.trimmingCharacters(in: .whitespacesAndNewlines).hasPrefix("{"),
|
||||
let data = str.data(using: .utf8),
|
||||
let parsed = serverMessage(from: data) {
|
||||
let t = parsed.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if !t.hasPrefix("{"), let safe = sanitizedMessage(t) { return safe }
|
||||
}
|
||||
}
|
||||
|
||||
if let description = error.userInfo[NSLocalizedDescriptionKey] as? String,
|
||||
!description.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty,
|
||||
!description.lowercased().contains("nsurlerrordomain") {
|
||||
return description
|
||||
!description.lowercased().contains("nsurlerrordomain"),
|
||||
let safe = sanitizedMessage(description) {
|
||||
return safe
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -435,6 +428,25 @@ private extension ApiClient {
|
||||
}
|
||||
#endif
|
||||
|
||||
func sanitizedBody(_ data: Data) -> String? {
|
||||
guard let raw = String(data: data, encoding: .utf8) else { return nil }
|
||||
return sanitizedMessage(raw)
|
||||
}
|
||||
|
||||
func sanitizedMessage(_ raw: String) -> String? {
|
||||
let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard trimmed.isEmpty == false else { return nil }
|
||||
// Drop data URLs (base64 images)
|
||||
if trimmed.lowercased().hasPrefix("data:image") { return "Erro ao processar imagem." }
|
||||
// Drop fields containing base64,
|
||||
if trimmed.contains("base64,") { return "Resposta do servidor inválida." }
|
||||
// Truncate long strings (raw JSON bodies, etc.)
|
||||
if trimmed.count > 300 {
|
||||
return String(trimmed.prefix(300)) + "…"
|
||||
}
|
||||
return trimmed
|
||||
}
|
||||
|
||||
func buildURL(path: String, query: [URLQueryItem]) throws -> URL {
|
||||
guard var components = URLComponents(url: ApiConfig.baseURL, resolvingAgainstBaseURL: false) else {
|
||||
throw NetworkError.invalidURL
|
||||
|
||||
@@ -53,18 +53,23 @@ struct CustomerAddress: Decodable {
|
||||
let state: String?
|
||||
let zipCode: String?
|
||||
let latLong: [Double]?
|
||||
let isDefault: Bool?
|
||||
|
||||
init(id: String?, label: String?, address: String?, number: String?,
|
||||
complement: String?, neighborhood: String?, city: String?,
|
||||
state: String?, zipCode: String?, latLong: [Double]?, isDefault: Bool?) {
|
||||
self.id = id; self.label = label; self.address = address
|
||||
self.number = number; self.complement = complement
|
||||
self.neighborhood = neighborhood; self.city = city
|
||||
self.state = state; self.zipCode = zipCode
|
||||
self.latLong = latLong; self.isDefault = isDefault
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case label
|
||||
case address
|
||||
case number
|
||||
case complement
|
||||
case neighborhood
|
||||
case city
|
||||
case state
|
||||
case zipCode
|
||||
case id, label, address, number, complement
|
||||
case neighborhood, city, state, zipCode
|
||||
case latLong = "lat_long"
|
||||
case isDefault
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ struct CreateOrderItemPayload: Codable {
|
||||
let qty: Int
|
||||
let price: Double
|
||||
let addons: [CreateOrderAddonPayload]
|
||||
let choices: [String]?
|
||||
}
|
||||
|
||||
struct CreateOrderAddonPayload: Codable {
|
||||
|
||||
@@ -212,6 +212,13 @@ final class ApiService {
|
||||
return try await updateCustomerAddressBook(customerId: customer.id, addressBook: updatedAddressBook)
|
||||
}
|
||||
|
||||
func setDefaultAddress(addressId: String) async throws -> ApiEnvelope<EmptyResult> {
|
||||
let req = ApiRequest(path: "/api/customer/addresses/\(addressId)/default", method: "PATCH", module: .customer, requiresAuth: true)
|
||||
let envelope: ApiEnvelope<EmptyResult> = try await sendEnvelope(req)
|
||||
AppContentCache.shared.invalidate(prefix: profileCachePrefix)
|
||||
return envelope
|
||||
}
|
||||
|
||||
func lookupZipCode(_ zipCode: String) async throws -> ApiEnvelope<CepLookupResult> {
|
||||
let digits = zipCode.filter(\.isNumber)
|
||||
let normalized = String(digits.prefix(8))
|
||||
|
||||
@@ -258,7 +258,7 @@ final class FeatureControlService {
|
||||
}
|
||||
|
||||
private func featureKeys() -> [String] {
|
||||
let raw = ProcessInfo.processInfo.environment["FEATURE_CONTROL_KEYS"] ?? "at.city.aguai,at.promo,at.ios.only,at.android.only"
|
||||
let raw = ProcessInfo.processInfo.environment["FEATURE_CONTROL_KEYS"] ?? "at.city.aguai,at.promo,at.ios.only,at.android.only,at.cupons"
|
||||
let items = raw
|
||||
.split(separator: ",")
|
||||
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
|
||||
|
||||
Reference in New Issue
Block a user