Skip to content

Commit 7d2c2d7

Browse files
authored
[Woo POS][Local catalog] Set up GRDB database with initial schema (#16047)
2 parents f5af05b + 9f9b8cc commit 7d2c2d7

File tree

6 files changed

+759
-3
lines changed

6 files changed

+759
-3
lines changed

Modules/Package.resolved

Lines changed: 10 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/Package.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ let package = Package(
8383
.package(url: "https://github.com/envoy/Embassy", from: "4.1.2"),
8484
// FIXME: This should be available via Tracks, but Tracks does not compile for watchOS
8585
.package(url: "https://github.com/getsentry/sentry-cocoa", from: "8.46.0"),
86+
.package(url: "https://github.com/groue/GRDB.swift", from: "7.0.0"),
8687
.package(url: "https://github.com/jonreid/ViewControllerPresentationSpy", from: "7.0.0"),
8788
.package(url: "https://github.com/kishikawakatsumi/KeychainAccess", from: "4.2.2"),
8889
.package(url: "https://github.com/krzysztofzablocki/Difference.git", branch: "master"),
@@ -159,7 +160,8 @@ let package = Package(
159160
name: "Storage",
160161
dependencies: [
161162
"Codegen",
162-
"WooFoundation"
163+
"WooFoundation",
164+
.product(name: "GRDB", package: "GRDB.swift")
163165
],
164166
exclude: ["Model/Migrations.md"],
165167
resources: [.process("Resources")]
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import Foundation
2+
import GRDB
3+
4+
// TODO: remove ignore when we start using this
5+
// periphery: ignore
6+
public final class GRDBManager {
7+
8+
let databaseQueue: DatabaseQueue
9+
private let databasePath: String
10+
11+
public init(databasePath: String) throws {
12+
self.databasePath = databasePath
13+
14+
let databaseURL = URL(fileURLWithPath: databasePath)
15+
let directoryURL = databaseURL.deletingLastPathComponent()
16+
17+
try FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
18+
19+
self.databaseQueue = try DatabaseQueue(path: databasePath)
20+
21+
try migrateIfNeeded()
22+
}
23+
24+
init() throws {
25+
self.databasePath = "in-memory"
26+
self.databaseQueue = try DatabaseQueue()
27+
try migrateIfNeeded()
28+
}
29+
}
30+
31+
// TODO: remove ignore when we start using this
32+
// periphery: ignore
33+
private extension GRDBManager {
34+
func migrateIfNeeded() throws {
35+
var migrator = DatabaseMigrator()
36+
37+
#if DEBUG
38+
// Speed up development by dropping the database when migrations change
39+
migrator.eraseDatabaseOnSchemaChange = true
40+
#endif
41+
42+
migrator.registerMigration("V001InitialSchema") { db in
43+
try V001InitialSchema.migrate(db)
44+
}
45+
46+
try migrator.migrate(databaseQueue)
47+
}
48+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import Foundation
2+
import GRDB
3+
4+
// TODO: remove ignore when we start using this
5+
// periphery: ignore
6+
struct V001InitialSchema {
7+
// This migration is under development and not released yet.
8+
// It's still open for modification, until we ship.
9+
// TODO: Mark this as final when we enable the pointOfSaleLocalCatalogi1 feature flag
10+
11+
static func migrate(_ db: Database) throws {
12+
try createSiteTable(db)
13+
try createProductTable(db)
14+
try createProductAttributeTable(db)
15+
try createProductImageTable(db)
16+
try createProductVariationTable(db)
17+
try createProductVariationAttributeTable(db)
18+
try createProductVariationImageTable(db)
19+
}
20+
21+
static func createSiteTable(_ db: Database) throws {
22+
try db.create(table: "site") { siteTable in
23+
siteTable.primaryKey("id", .integer).notNull()
24+
}
25+
}
26+
27+
static func createProductTable(_ db: Database) throws {
28+
// Note that it's best to use strings for names in the database
29+
// not derive them from a Swift class.
30+
// https://swiftpackageindex.com/groue/grdb.swift/v7.6.1/documentation/grdb/migrations#Good-Practices-for-Defining-Migrations
31+
try db.create(table: "product") { productTable in
32+
productTable.primaryKey("id", .integer).notNull()
33+
productTable.belongsTo("site", onDelete: .cascade).notNull()
34+
35+
productTable.column("name", .text).notNull()
36+
productTable.column("productTypeKey", .text).notNull()
37+
38+
productTable.column("fullDescription", .text)
39+
productTable.column("shortDescription", .text)
40+
41+
productTable.column("sku", .text)
42+
productTable.column("globalUniqueID", .text)
43+
productTable.column("price", .text).notNull()
44+
45+
productTable.column("downloadable", .boolean).notNull()
46+
47+
productTable.column("parentID", .integer).notNull()
48+
}
49+
}
50+
51+
private static func createProductAttributeTable(_ db: Database) throws {
52+
try db.create(table: "productAttribute") { productAttributeTable in
53+
// This table holds local product attributes only. Global attributes belong to a site.
54+
productAttributeTable.autoIncrementedPrimaryKey("id").notNull()
55+
productAttributeTable.belongsTo("product", onDelete: .cascade).notNull()
56+
57+
productAttributeTable.column("name", .text).notNull()
58+
productAttributeTable.column("position", .integer).notNull()
59+
productAttributeTable.column("visible", .boolean).notNull()
60+
productAttributeTable.column("variation", .boolean).notNull()
61+
productAttributeTable.column("options", .jsonText).notNull()
62+
}
63+
}
64+
65+
private static func createProductImageTable(_ db: Database) throws {
66+
try db.create(table: "productImage") { productImageTable in
67+
productImageTable.primaryKey("id", .integer).notNull()
68+
productImageTable.belongsTo("product", onDelete: .cascade).notNull()
69+
70+
productImageTable.column("dateCreated", .datetime).notNull()
71+
productImageTable.column("dateModified", .datetime)
72+
73+
productImageTable.column("src", .text).notNull()
74+
productImageTable.column("name", .text)
75+
productImageTable.column("alt", .text)
76+
}
77+
}
78+
79+
private static func createProductVariationTable(_ db: Database) throws {
80+
try db.create(table: "productVariation") { productVariationTable in
81+
productVariationTable.primaryKey("id", .integer).notNull()
82+
productVariationTable.belongsTo("site", onDelete: .cascade).notNull()
83+
productVariationTable.belongsTo("product", onDelete: .cascade).notNull()
84+
85+
productVariationTable.column("sku", .text)
86+
productVariationTable.column("globalUniqueID", .text)
87+
productVariationTable.column("price", .text).notNull()
88+
89+
productVariationTable.column("downloadable", .boolean).notNull()
90+
91+
productVariationTable.column("fullDescription", .text)
92+
}
93+
}
94+
95+
private static func createProductVariationAttributeTable(_ db: Database) throws {
96+
try db.create(table: "productVariationAttribute") { productVariationAttributeTable in
97+
// This table holds local variation attributes only. Global attributes belong to a site.
98+
productVariationAttributeTable.autoIncrementedPrimaryKey("id").notNull()
99+
productVariationAttributeTable.belongsTo("productVariation", onDelete: .cascade).notNull()
100+
101+
productVariationAttributeTable.column("name", .text).notNull()
102+
productVariationAttributeTable.column("option", .text).notNull()
103+
}
104+
}
105+
106+
private static func createProductVariationImageTable(_ db: Database) throws {
107+
try db.create(table: "productVariationImage") { productVariationImageTable in
108+
productVariationImageTable.primaryKey("id", .integer).notNull()
109+
productVariationImageTable.belongsTo("productVariation").notNull()
110+
111+
productVariationImageTable.column("dateCreated", .datetime).notNull()
112+
productVariationImageTable.column("dateModified", .datetime)
113+
114+
productVariationImageTable.column("src", .text).notNull()
115+
productVariationImageTable.column("name", .text)
116+
productVariationImageTable.column("alt", .text)
117+
}
118+
}
119+
}

0 commit comments

Comments
 (0)