migration

This commit is contained in:
Daniel Arantes Loverde
2026-08-11 13:22:02 -03:00
parent c4d6998595
commit 7702836fe7
231 changed files with 4329 additions and 1958 deletions

View File

@@ -0,0 +1,280 @@
import Foundation
enum StoreCatalogNormalizer {
static func sanitize(categories: [StoreCatalogCategory], storeId: String) -> [StoreCatalogCategory] {
var seenCategoryIds: Set<String> = []
return categories.enumerated().map { categoryIndex, category in
let categoryId = makeUniqueId(
rawValue: category.id,
fallback: "\(storeId)-category-\(categoryIndex)",
seenIds: &seenCategoryIds
)
let normalizedPizzaConfig = sanitize(
pizzaConfig: category.pizzaConfig,
categoryId: categoryId
)
var seenProductIds: Set<String> = []
let normalizedProducts = category.products.enumerated().map { productIndex, product in
sanitize(
product: product,
categoryId: categoryId,
productIndex: productIndex,
seenProductIds: &seenProductIds
)
}
return StoreCatalogCategory(
id: categoryId,
name: category.name,
isPizzaCategory: category.isPizzaCategory,
pizzaConfig: normalizedPizzaConfig,
products: normalizedProducts
)
}
}
static func preferredCategoryId(
from categories: [StoreCatalogCategory],
preferredId: String?
) -> String? {
guard let preferredId, preferredId.isEmpty == false else {
return categories.first?.id
}
if categories.contains(where: { $0.id == preferredId }) {
return preferredId
}
return categories.first?.id
}
private static func sanitize(
pizzaConfig: StorePizzaConfig?,
categoryId: String
) -> StorePizzaConfig? {
guard let pizzaConfig else { return nil }
var seenSizeIds: Set<String> = []
let normalizedSizes = pizzaConfig.sizes.enumerated().map { index, size in
StorePizzaSize(
id: makeUniqueId(
rawValue: size.id,
fallback: "\(categoryId)-size-\(index)",
seenIds: &seenSizeIds
),
name: size.name,
slices: size.slices,
maxFlavors: size.maxFlavors
)
}
var seenDoughIds: Set<String> = []
let normalizedDoughs = pizzaConfig.doughs.enumerated().map { index, dough in
StorePizzaDough(
id: makeUniqueId(
rawValue: dough.id,
fallback: "\(categoryId)-dough-\(index)",
seenIds: &seenDoughIds
),
name: dough.name,
active: dough.active
)
}
var seenCrustIds: Set<String> = []
let normalizedCrusts = pizzaConfig.crusts.enumerated().map { index, crust in
StorePizzaCrust(
id: makeUniqueId(
rawValue: crust.id,
fallback: "\(categoryId)-crust-\(index)",
seenIds: &seenCrustIds
),
name: crust.name,
active: crust.active,
priceModifier: crust.priceModifier
)
}
return StorePizzaConfig(
sizes: normalizedSizes,
doughs: normalizedDoughs,
crusts: normalizedCrusts
)
}
private static func sanitize(
product: StoreCatalogProduct,
categoryId: String,
productIndex: Int,
seenProductIds: inout Set<String>
) -> StoreCatalogProduct {
let productId = makeUniqueId(
rawValue: product.id,
fallback: "\(categoryId)-product-\(productIndex)",
seenIds: &seenProductIds
)
var seenGroupIds: Set<String> = []
var seenAddonItemIds: Set<String> = []
let normalizedAddonGroups = product.addonGroups.enumerated().map { groupIndex, group in
let groupId = makeUniqueId(
rawValue: group.id,
fallback: "\(productId)-group-\(groupIndex)",
seenIds: &seenGroupIds
)
let normalizedItems = group.items.enumerated().map { itemIndex, item in
StoreAddonItem(
id: makeUniqueId(
rawValue: item.id,
fallback: "\(groupId)-item-\(itemIndex)",
seenIds: &seenAddonItemIds
),
name: item.name,
price: item.price
)
}
return StoreAddonGroup(
id: groupId,
name: group.name,
minSelectors: group.minSelectors,
maxSelectors: group.maxSelectors,
items: normalizedItems
)
}
return StoreCatalogProduct(
id: productId,
type: product.type,
name: product.name,
description: product.description,
image: product.image,
price: product.price,
originalPrice: product.originalPrice,
pizzaPrices: product.pizzaPrices,
addonGroups: normalizedAddonGroups
)
}
private static func makeUniqueId(
rawValue: String,
fallback: String,
seenIds: inout Set<String>
) -> String {
let trimmedRawValue = rawValue.trimmingCharacters(in: .whitespacesAndNewlines)
let baseId = trimmedRawValue.isEmpty ? fallback : trimmedRawValue
if seenIds.contains(baseId) == false {
seenIds.insert(baseId)
return baseId
}
var suffix = 1
while true {
let candidate = "\(baseId)-\(suffix)"
if seenIds.contains(candidate) == false {
seenIds.insert(candidate)
return candidate
}
suffix += 1
}
}
}
extension StoreCatalogCategory {
init(
id: String,
name: String,
isPizzaCategory: Bool,
pizzaConfig: StorePizzaConfig?,
products: [StoreCatalogProduct]
) {
self.id = id
self.name = name
self.isPizzaCategory = isPizzaCategory
self.pizzaConfig = pizzaConfig
self.products = products
}
}
extension StoreCatalogProduct {
init(
id: String,
type: String?,
name: String,
description: String?,
image: String?,
price: Double?,
originalPrice: Double?,
pizzaPrices: [String: Double],
addonGroups: [StoreAddonGroup]
) {
self.id = id
self.type = type
self.name = name
self.description = description
self.image = image
self.price = price
self.originalPrice = originalPrice
self.pizzaPrices = pizzaPrices
self.addonGroups = addonGroups
}
}
extension StoreAddonGroup {
init(
id: String,
name: String,
minSelectors: Int?,
maxSelectors: Int?,
items: [StoreAddonItem]
) {
self.id = id
self.name = name
self.minSelectors = minSelectors
self.maxSelectors = maxSelectors
self.items = items
}
}
extension StoreAddonItem {
init(
id: String,
name: String,
price: Double?
) {
self.id = id
self.name = name
self.price = price
}
}
extension StorePizzaConfig {
init(
sizes: [StorePizzaSize],
doughs: [StorePizzaDough],
crusts: [StorePizzaCrust]
) {
self.sizes = sizes
self.doughs = doughs
self.crusts = crusts
}
}
extension StorePizzaCrust {
init(
id: String,
name: String?,
active: Bool?,
priceModifier: Double?
) {
self.id = id
self.name = name
self.active = active
self.priceModifier = priceModifier
}
}