Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 5d9b4ec

Browse files
calebklevetertanner0101
authored andcommitted
Custom JSON Coder for Postgres Coders (vapor#129)
* Changed dependencies to Skelpo forks * Added .encoder and .decoder properties to FluentPostgresDatabase * Replaced PostgresRow:DatabaseRow with .databaseRow(using:) methods * Pass custom PostgresData<Coder> instances as properties of _FluentPostgresDatabase * Made PostgresRow.databaseRow(using:) method internal * Created FluentPostgresDriverTests.testCustomJSON test case * Replaced uses of PostgresRow.sqlRow with .sql * Link to vapor/postgres-kit instead of Skelpo fork * Fixed parameter name for PostgreDataDecoder parameter in .sql call
1 parent e348183 commit 5d9b4ec

File tree

4 files changed

+94
-14
lines changed

4 files changed

+94
-14
lines changed

β€ŽSources/FluentPostgresDriver/FluentPostgresDatabase.swiftβ€Ž

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import FluentSQL
33
struct _FluentPostgresDatabase {
44
let database: PostgresDatabase
55
let context: DatabaseContext
6+
7+
let encoder: PostgresDataEncoder
8+
let decoder: PostgresDataDecoder
69
}
710

811
extension _FluentPostgresDatabase: Database {
@@ -16,8 +19,8 @@ extension _FluentPostgresDatabase: Database {
1619
}
1720
let (sql, binds) = self.serialize(expression)
1821
do {
19-
return try self.query(sql, binds.map { try PostgresDataEncoder().encode(0ドル) }) {
20-
onRow(0ドル)
22+
return try self.query(sql, binds.map { try self.encoder.encode(0ドル) }) {
23+
onRow(0ドル.databaseRow(using:self.decoder))
2124
}
2225
} catch {
2326
return self.eventLoop.makeFailedFuture(error)
@@ -29,7 +32,7 @@ extension _FluentPostgresDatabase: Database {
2932
.convert(schema)
3033
let (sql, binds) = self.serialize(expression)
3134
do {
32-
return try self.query(sql, binds.map { try PostgresDataEncoder().encode(0ドル) }) {
35+
return try self.query(sql, binds.map { try self.encoder.encode(0ドル) }) {
3336
fatalError("unexpected row: \(0ドル)")
3437
}
3538
} catch {
@@ -39,7 +42,7 @@ extension _FluentPostgresDatabase: Database {
3942

4043
func withConnection<T>(_ closure: @escaping (Database) -> EventLoopFuture<T>) -> EventLoopFuture<T> {
4144
self.database.withConnection {
42-
closure(_FluentPostgresDatabase(database: 0ドル, context: self.context))
45+
closure(_FluentPostgresDatabase(database: 0ドル, context: self.context, encoder:self.encoder, decoder:self.decoder))
4346
}
4447
}
4548
}
@@ -53,7 +56,7 @@ extension _FluentPostgresDatabase: SQLDatabase {
5356
sql query: SQLExpression,
5457
_ onRow: @escaping (SQLRow) -> ()
5558
) -> EventLoopFuture<Void> {
56-
self.sql().execute(sql: query, onRow)
59+
self.sql(encoder: encoder, decoder: decoder).execute(sql: query, onRow)
5760
}
5861
}
5962

β€ŽSources/FluentPostgresDriver/FluentPostgresDriver.swiftβ€Ž

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ struct _FluentPostgresDriver: DatabaseDriver {
6666
func makeDatabase(with context: DatabaseContext) -> Database {
6767
_FluentPostgresDatabase(
6868
database: self.pool.pool(for: context.eventLoop).database(logger: context.logger),
69-
context: context
69+
context: context,
70+
encoder: self.pool.source.configuration.encoder,
71+
decoder: self.pool.source.configuration.decoder
7072
)
7173
}
7274

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,26 @@
1-
extension PostgresRow: DatabaseRow {
2-
public func contains(field: String) -> Bool {
3-
return self.column(field) != nil
1+
import class Foundation.JSONDecoder
2+
3+
extension PostgresRow {
4+
internal func databaseRow(using decoder: PostgresDataDecoder) -> DatabaseRow {
5+
return _PostgresDatabaseRow(row: self, decoder: decoder)
6+
}
7+
}
8+
9+
private struct _PostgresDatabaseRow: DatabaseRow {
10+
let row: PostgresRow
11+
let decoder: PostgresDataDecoder
12+
13+
var description: String { self.row.description }
14+
15+
func contains(field: String) -> Bool {
16+
return self.row.column(field) != nil
417
}
518

6-
publicfunc decode<T>(
19+
func decode<T>(
720
field: String,
821
as type: T.Type,
922
for database: Database
1023
) throws -> T where T : Decodable {
11-
return try self.decode(column: field, as: T.self)
24+
return try self.row.sql(decoder:self.decoder).decode(column: field, as: T.self)
1225
}
1326
}

β€ŽTests/FluentPostgresDriverTests/FluentPostgresDriverTests.swiftβ€Ž

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,52 @@ final class FluentPostgresDriverTests: XCTestCase {
230230
try new.save(on: self.db).wait()
231231
}
232232

233+
func testCustomJSON() throws {
234+
struct Metadata: Codable {
235+
let createdAt: Date
236+
}
237+
238+
final class Event: Model {
239+
static let schema = "events"
240+
241+
@ID(key: "id") var id: Int?
242+
@Field(key: "metadata") var metadata: Metadata
243+
}
244+
245+
struct EventMigration: Migration {
246+
func prepare(on database: Database) -> EventLoopFuture<Void> {
247+
return database.schema(Event.schema)
248+
.field("id", .int, .identifier(auto: true))
249+
.field("metadata", .json, .required)
250+
.create()
251+
}
252+
253+
func revert(on database: Database) -> EventLoopFuture<Void> {
254+
return database.schema(Event.schema).delete()
255+
}
256+
}
257+
258+
try EventMigration().prepare(on: self.db).wait()
259+
defer { try! EventMigration().revert(on: self.db).wait() }
260+
261+
let date = Date()
262+
let event = Event()
263+
event.id = 1
264+
event.metadata = Metadata(createdAt: date)
265+
try event.save(on: self.db).wait()
266+
267+
let orm = Event.query(on: self.db).filter(\.$id == 1)
268+
try self.db.execute(query: orm.query, onRow: { row in
269+
do {
270+
let metadata = try row.decode(field: "metadata", as: [String: String].self, for: self.db)
271+
let expected = ISO8601DateFormatter().string(from: date)
272+
XCTAssertEqual(metadata["createdAt"], expected)
273+
} catch let error {
274+
XCTFail(error.localizedDescription)
275+
}
276+
}).wait()
277+
}
278+
233279

234280
var benchmarker: FluentBenchmarker {
235281
return .init(database: self.db)
@@ -242,17 +288,33 @@ final class FluentPostgresDriverTests: XCTestCase {
242288
}
243289

244290
override func setUp() {
245-
XCTAssert(isLoggingConfigured)
246-
self.eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
291+
let jsonEncoder = JSONEncoder()
292+
jsonEncoder.dateEncodingStrategy = .iso8601
293+
294+
let jsonDecoder = JSONDecoder()
295+
jsonDecoder.dateDecodingStrategy = .iso8601
296+
247297
let hostname: String
248298
#if os(Linux)
249299
hostname = "psql"
250300
#else
251301
hostname = "localhost"
252302
#endif
303+
304+
let configuration = PostgresConfiguration(
305+
hostname: hostname,
306+
username: "vapor_username",
307+
password: "vapor_password",
308+
database: "vapor_database",
309+
encoder: PostgresDataEncoder(json: jsonEncoder),
310+
decoder: PostgresDataDecoder(json: jsonDecoder)
311+
)
312+
313+
XCTAssert(isLoggingConfigured)
314+
self.eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
253315
self.threadPool = NIOThreadPool(numberOfThreads: 1)
254316
self.dbs = Databases(threadPool: threadPool, on: self.eventLoopGroup)
255-
self.dbs.use(.postgres(hostname: hostname, username:"vapor_username", password:"vapor_password", database:"vapor_database"), as: .psql)
317+
self.dbs.use(.postgres(configuration: configuration), as: .psql)
256318
}
257319

258320
override func tearDown() {

0 commit comments

Comments
(0)

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /