Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions Modules/Sources/Storage/GRDB/GRDBManager.swift
Original file line number Diff line number Diff line change
@@ -1,35 +1,34 @@
import Foundation
import GRDB

// TODO: remove ignore when we start using this
// periphery: ignore
public final class GRDBManager {
public protocol GRDBManagerProtocol {
var databaseConnection: GRDBDatabaseConnection { get }
}

let databaseQueue: DatabaseQueue
private let databasePath: String
public protocol GRDBDatabaseConnection: DatabaseReader & DatabaseWriter {}

public init(databasePath: String) throws {
self.databasePath = databasePath
public final class GRDBManager: GRDBManagerProtocol {

public var databaseConnection: GRDBDatabaseConnection
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could this be let? do we plan to change the connection once it's initialized?


public init(databasePath: String) throws {
let databaseURL = URL(fileURLWithPath: databasePath)
let directoryURL = databaseURL.deletingLastPathComponent()

try FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)

self.databaseQueue = try DatabaseQueue(path: databasePath)
self.databaseConnection = try DatabaseQueue(path: databasePath)
Comment on lines -19 to +20
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just curious, is this renaming to make it more flexible if we ever decide to switch to DatabasePool?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, and to make it mockable – the protocol requirement is DatabaseReader & DatabaseWriter, which both DatabasePool and DatabaseQueue implement. GRDB refers to them as database connections for the general case.


try migrateIfNeeded()
}

// Creates an in-memory database, intended for use in tests.
init() throws {
self.databasePath = "in-memory"
self.databaseQueue = try DatabaseQueue()
self.databaseConnection = try DatabaseQueue()
try migrateIfNeeded()
}
}

// TODO: remove ignore when we start using this
// periphery: ignore
private extension GRDBManager {
func migrateIfNeeded() throws {
var migrator = DatabaseMigrator()
Expand All @@ -43,6 +42,8 @@ private extension GRDBManager {
try V001InitialSchema.migrate(db)
}

try migrator.migrate(databaseQueue)
try migrator.migrate(databaseConnection)
}
}

extension DatabaseQueue: GRDBDatabaseConnection {}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import Foundation
import GRDB

// TODO: remove ignore when we start using this
// periphery: ignore
struct V001InitialSchema {
// This migration is under development and not released yet.
// It's still open for modification, until we ship.
Expand Down
56 changes: 28 additions & 28 deletions Modules/Tests/StorageTests/GRDB/GRDBManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ struct GRDBManagerTests {
let manager = try GRDBManager()

// When
let tableExists = try manager.databaseQueue.read { db in
let tableExists = try manager.databaseConnection.read { db in
return (
try db.tableExists("site"),
try db.tableExists("product"),
Expand Down Expand Up @@ -49,7 +49,7 @@ struct GRDBManagerTests {

init() throws {
self.manager = try GRDBManager()
try manager.databaseQueue.write { db in
try manager.databaseConnection.write { db in
let record = TestSite(id: sampleSiteID)
try record.insert(db)
}
Expand All @@ -60,7 +60,7 @@ struct GRDBManagerTests {
// Given

// When
try manager.databaseQueue.write { db in
try manager.databaseConnection.write { db in
let record = TestProduct(
siteID: 1,
id: 100,
Expand All @@ -74,7 +74,7 @@ struct GRDBManagerTests {
}

// Then
let productCount = try manager.databaseQueue.read { db in
let productCount = try manager.databaseConnection.read { db in
try TestProduct.fetchCount(db)
}

Expand All @@ -84,7 +84,7 @@ struct GRDBManagerTests {
@Test("Insert product variation with a relationship to a product")
func test_after_init_can_insert_productVariation_with_foreign_key() throws {
// Given – parent product
try manager.databaseQueue.write { db in
try manager.databaseConnection.write { db in
let product = TestProduct(
siteID: 1,
id: 100,
Expand All @@ -98,7 +98,7 @@ struct GRDBManagerTests {
}

// When - Insert variation
try manager.databaseQueue.write { db in
try manager.databaseConnection.write { db in
let variation = TestProductVariation(
siteID: 1,
id: 200,
Expand All @@ -110,7 +110,7 @@ struct GRDBManagerTests {
}

// Then
let variations = try manager.databaseQueue.read { db in
let variations = try manager.databaseConnection.read { db in
try TestProductVariation.fetchAll(db)
}

Expand All @@ -121,7 +121,7 @@ struct GRDBManagerTests {
@Test("Fetch variations by product ID")
func test_after_init_and_insert_can_query_productVariation_using_foreign_key() throws {
// Given parent product and some variations
try manager.databaseQueue.write { db in
try manager.databaseConnection.write { db in
// Insert product
let product = TestProduct(
siteID: 1,
Expand All @@ -148,7 +148,7 @@ struct GRDBManagerTests {
}

// When
let variations = try manager.databaseQueue.read { db in
let variations = try manager.databaseConnection.read { db in
try TestProductVariation
.filter(Column("productID") == 100)
.fetchAll(db)
Expand All @@ -162,7 +162,7 @@ struct GRDBManagerTests {
@Test("Insert product attribute with options array (JSON)")
func test_after_init_can_insert_productAttribute_with_options_as_JSON_array() throws {
// Given parent product
try manager.databaseQueue.write { db in
try manager.databaseConnection.write { db in
// Insert product first
let product = TestProduct(
siteID: 1,
Expand All @@ -177,7 +177,7 @@ struct GRDBManagerTests {
}

// When
try manager.databaseQueue.write { db in
try manager.databaseConnection.write { db in
let attribute = TestProductAttribute(
productID: 100,
name: "Color",
Expand All @@ -190,7 +190,7 @@ struct GRDBManagerTests {
}

// Then
let attribute = try manager.databaseQueue.read { db in
let attribute = try manager.databaseConnection.read { db in
try TestProductAttribute.fetchOne(db)
}

Expand All @@ -201,7 +201,7 @@ struct GRDBManagerTests {
@Test("Insert variation attributes")
func test_after_init_can_insert_variation_attributes() throws {
// Given parent product and variation
try manager.databaseQueue.write { db in
try manager.databaseConnection.write { db in
// Insert product
let product = TestProduct(
siteID: 1,
Expand All @@ -226,7 +226,7 @@ struct GRDBManagerTests {
}

// When
try manager.databaseQueue.write { db in
try manager.databaseConnection.write { db in
let variationAttribute = TestProductVariationAttribute(
productVariationID: 200,
name: "Color",
Expand All @@ -236,7 +236,7 @@ struct GRDBManagerTests {
}

// Then
let attributes = try manager.databaseQueue.read { db in
let attributes = try manager.databaseConnection.read { db in
try TestProductVariationAttribute.fetchAll(db)
}

Expand All @@ -247,14 +247,14 @@ struct GRDBManagerTests {
@Test("Deleting site cascades to all related entities")
func test_deleting_site_cascades_to_all_related_entities() throws {
// Given - Create a separate site for this test to avoid interfering with other tests
let testSiteId = try manager.databaseQueue.write { db -> Int64 in
let testSiteId = try manager.databaseConnection.write { db -> Int64 in
let site = TestSite(id: 999)
try site.insert(db)
return 999
}

// Insert full entity hierarchy
try manager.databaseQueue.write { db in
try manager.databaseConnection.write { db in
let product = TestProduct(
siteID: testSiteId,
id: 100,
Expand Down Expand Up @@ -294,7 +294,7 @@ struct GRDBManagerTests {
}

// Verify entities exist for test site
let countsBefore = try manager.databaseQueue.read { db in
let countsBefore = try manager.databaseConnection.read { db in
return (
products: try TestProduct.filter(Column("siteID") == testSiteId).fetchCount(db),
variations: try TestProductVariation.filter(Column("siteID") == testSiteId).fetchCount(db),
Expand All @@ -309,12 +309,12 @@ struct GRDBManagerTests {
#expect(countsBefore.variationAttributes == 1)

// When - Delete the site
_ = try manager.databaseQueue.write { db in
_ = try manager.databaseConnection.write { db in
try TestSite.filter(Column("id") == testSiteId).deleteAll(db)
}

// Then - All related entities should be deleted
let countsAfter = try manager.databaseQueue.read { db in
let countsAfter = try manager.databaseConnection.read { db in
return (
sites: try TestSite.filter(Column("id") == testSiteId).fetchCount(db),
products: try TestProduct.filter(Column("siteID") == testSiteId).fetchCount(db),
Expand All @@ -334,13 +334,13 @@ struct GRDBManagerTests {
@Test("Fetch products by site")
func test_can_fetch_products_by_site() throws {
// Given - Create second site and products for both sites
let site2Id = try manager.databaseQueue.write { db -> Int64 in
let site2Id = try manager.databaseConnection.write { db -> Int64 in
let site = TestSite(id: 2)
try site.insert(db)
return 2
}

try manager.databaseQueue.write { db in
try manager.databaseConnection.write { db in
// Site 1 products
for i in 1...3 {
let product = TestProduct(
Expand Down Expand Up @@ -371,13 +371,13 @@ struct GRDBManagerTests {
}

// When
let site1Products = try manager.databaseQueue.read { db in
let site1Products = try manager.databaseConnection.read { db in
try TestProduct
.filter(Column("siteID") == sampleSiteID)
.fetchAll(db)
}

let site2Products = try manager.databaseQueue.read { db in
let site2Products = try manager.databaseConnection.read { db in
try TestProduct
.filter(Column("siteID") == site2Id)
.fetchAll(db)
Expand All @@ -397,7 +397,7 @@ struct GRDBManagerTests {
@Test("Fetch variations by site")
func test_can_fetch_variations_by_site() throws {
// Given - Product and variations
try manager.databaseQueue.write { db in
try manager.databaseConnection.write { db in
let product = TestProduct(
siteID: sampleSiteID,
id: 100,
Expand All @@ -422,7 +422,7 @@ struct GRDBManagerTests {
}

// When
let variations = try manager.databaseQueue.read { db in
let variations = try manager.databaseConnection.read { db in
try TestProductVariation
.filter(Column("siteID") == sampleSiteID)
.fetchAll(db)
Expand All @@ -438,7 +438,7 @@ struct GRDBManagerTests {
func test_cannot_insert_product_without_valid_site() throws {
// When/Then
#expect(throws: DatabaseError.self) {
try manager.databaseQueue.write { db in
try manager.databaseConnection.write { db in
let product = TestProduct(
siteID: 999, // Non-existent site
id: 100,
Expand All @@ -457,7 +457,7 @@ struct GRDBManagerTests {
func test_cannot_insert_variation_without_valid_product() throws {
// When/Then
#expect(throws: DatabaseError.self) {
try manager.databaseQueue.write { db in
try manager.databaseConnection.write { db in
let variation = TestProductVariation(
siteID: sampleSiteID,
id: 200,
Expand Down
17 changes: 17 additions & 0 deletions WooCommerce/Classes/POS/TabBar/POSTabCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import SwiftUI
import Yosemite
import class WooFoundation.CurrencySettings
import protocol Storage.StorageManagerType
import protocol Storage.GRDBManagerProtocol

/// View controller that provides the tab bar item for the Point of Sale tab.
/// It is never visible on the screen, only used to provide the tab bar item as all POS UI is full-screen.
Expand All @@ -26,6 +27,7 @@ final class POSTabCoordinator {
private let storesManager: StoresManager
private let credentials: Credentials?
private let storageManager: StorageManagerType
private let grdbManager: GRDBManagerProtocol?
private let currencySettings: CurrencySettings
private let pushNotesManager: PushNotesManager
private let eligibilityChecker: POSEntryPointEligibilityCheckerProtocol
Expand Down Expand Up @@ -77,6 +79,13 @@ final class POSTabCoordinator {
self.eligibilityChecker = eligibilityChecker

tabContainerController.wrappedController = POSTabViewController()

if ServiceLocator.featureFlagService.isFeatureFlagEnabled(.pointOfSaleLocalCatalogi1) {
self.grdbManager = ServiceLocator.grdbManager
logDatabaseSchema()
} else {
self.grdbManager = nil
}
}

func onTabSelected() {
Expand Down Expand Up @@ -155,3 +164,11 @@ private extension POSTabCoordinator {
TracksProvider.setPOSMode(isPointOfSaleActive)
}
}

private extension POSTabCoordinator {
func logDatabaseSchema() {
try? grdbManager?.databaseConnection.read { db in
return try db.dumpSchema()
}
}
}
Loading