This commit is contained in:
Justin Mazzocchi 2020-09-28 23:06:25 -07:00
parent 7335615b64
commit f5dcb762d1
No known key found for this signature in database
GPG key ID: E223E6937AAFB01C
14 changed files with 195 additions and 71 deletions

View file

@ -14,8 +14,8 @@ public struct AccountList: Codable, FetchableRecord, PersistableRecord {
extension AccountList {
static let joins = hasMany(
AccountListJoin.self,
using: ForeignKey([Column("listId")]))
.order(Column("index"))
using: ForeignKey([AccountListJoin.Columns.listId]))
.order(AccountListJoin.Columns.index)
static let accounts = hasMany(
AccountRecord.self,
through: joins,

View file

@ -8,5 +8,13 @@ struct AccountListJoin: Codable, FetchableRecord, PersistableRecord {
let listId: UUID
let index: Int
static let account = belongsTo(AccountRecord.self, using: ForeignKey([Column("accountId")]))
static let account = belongsTo(AccountRecord.self, using: ForeignKey([Columns.accountId]))
}
extension AccountListJoin {
enum Columns {
static let accountId = Column(AccountListJoin.CodingKeys.accountId)
static let listId = Column(AccountListJoin.CodingKeys.listId)
static let index = Column(AccountListJoin.CodingKeys.index)
}
}

View file

@ -8,5 +8,13 @@ struct AccountPinnedStatusJoin: Codable, FetchableRecord, PersistableRecord {
let statusId: String
let index: Int
static let status = belongsTo(StatusRecord.self, using: ForeignKey([Column("statusId")]))
static let status = belongsTo(StatusRecord.self, using: ForeignKey([Columns.statusId]))
}
extension AccountPinnedStatusJoin {
enum Columns {
static let accountId = Column(AccountPinnedStatusJoin.CodingKeys.accountId)
static let statusId = Column(AccountPinnedStatusJoin.CodingKeys.statusId)
static let index = Column(AccountPinnedStatusJoin.CodingKeys.index)
}
}

View file

@ -27,6 +27,31 @@ struct AccountRecord: Codable, Hashable {
let movedId: String?
}
extension AccountRecord {
enum Columns {
static let id = Column(AccountRecord.CodingKeys.id)
static let username = Column(AccountRecord.CodingKeys.username)
static let acct = Column(AccountRecord.CodingKeys.acct)
static let displayName = Column(AccountRecord.CodingKeys.displayName)
static let locked = Column(AccountRecord.CodingKeys.locked)
static let createdAt = Column(AccountRecord.CodingKeys.createdAt)
static let followersCount = Column(AccountRecord.CodingKeys.followersCount)
static let followingCount = Column(AccountRecord.CodingKeys.followingCount)
static let statusesCount = Column(AccountRecord.CodingKeys.statusesCount)
static let note = Column(AccountRecord.CodingKeys.note)
static let url = Column(AccountRecord.CodingKeys.url)
static let avatar = Column(AccountRecord.CodingKeys.avatar)
static let avatarStatic = Column(AccountRecord.CodingKeys.avatarStatic)
static let header = Column(AccountRecord.CodingKeys.header)
static let headerStatic = Column(AccountRecord.CodingKeys.headerStatic)
static let fields = Column(AccountRecord.CodingKeys.fields)
static let emojis = Column(AccountRecord.CodingKeys.emojis)
static let bot = Column(AccountRecord.CodingKeys.bot)
static let discoverable = Column(AccountRecord.CodingKeys.discoverable)
static let movedId = Column(AccountRecord.CodingKeys.movedId)
}
}
extension AccountRecord: FetchableRecord, PersistableRecord {
static func databaseJSONDecoder(for column: String) -> JSONDecoder {
MastodonDecoder()
@ -41,8 +66,8 @@ extension AccountRecord {
static let moved = belongsTo(AccountRecord.self, key: "moved")
static let pinnedStatusJoins = hasMany(
AccountPinnedStatusJoin.self,
using: ForeignKey([Column("accountId")]))
.order(Column("index"))
using: ForeignKey([AccountPinnedStatusJoin.Columns.accountId]))
.order(AccountPinnedStatusJoin.Columns.index)
static let pinnedStatuses = hasMany(
StatusRecord.self,
through: pinnedStatusJoins,

View file

@ -8,5 +8,13 @@ struct AccountStatusJoin: Codable, FetchableRecord, PersistableRecord {
let statusId: String
let collection: ProfileCollection
static let status = belongsTo(StatusRecord.self, using: ForeignKey([Column("statusId")]))
static let status = belongsTo(StatusRecord.self, using: ForeignKey([Columns.statusId]))
}
extension AccountStatusJoin {
enum Columns {
static let accountId = Column(AccountStatusJoin.CodingKeys.accountId)
static let statusId = Column(AccountStatusJoin.CodingKeys.statusId)
static let collection = Column(AccountStatusJoin.CodingKeys.collection)
}
}

View file

@ -73,9 +73,9 @@ public extension ContentDatabase {
}
try StatusContextJoin.filter(
Column("parentId") == parentID
&& Column("section") == section.rawValue
&& !statuses.map(\.id).contains(Column("statusId")))
StatusContextJoin.Columns.parentId == parentID
&& StatusContextJoin.Columns.section == section.rawValue
&& !statuses.map(\.id).contains(StatusContextJoin.Columns.statusId))
.deleteAll($0)
}
}
@ -92,8 +92,8 @@ public extension ContentDatabase {
}
try AccountPinnedStatusJoin.filter(
Column("accountId") == accountID
&& !pinnedStatuses.map(\.id).contains(Column("statusId")))
AccountPinnedStatusJoin.Columns.accountId == accountID
&& !pinnedStatuses.map(\.id).contains(AccountPinnedStatusJoin.Columns.statusId))
.deleteAll($0)
}
.ignoreOutput()
@ -137,8 +137,8 @@ public extension ContentDatabase {
}
try Timeline
.filter(!(Timeline.authenticatedDefaults.map(\.id) + lists.map(\.id)).contains(Column("id"))
&& Column("listTitle") != nil)
.filter(!(Timeline.authenticatedDefaults.map(\.id) + lists.map(\.id)).contains(Timeline.Columns.id)
&& Timeline.Columns.listTitle != nil)
.deleteAll($0)
}
.ignoreOutput()
@ -152,7 +152,7 @@ public extension ContentDatabase {
}
func deleteList(id: String) -> AnyPublisher<Never, Error> {
databaseWriter.writePublisher(updates: Timeline.filter(Column("id") == id).deleteAll)
databaseWriter.writePublisher(updates: Timeline.filter(Timeline.Columns.id == id).deleteAll)
.ignoreOutput()
.eraseToAnyPublisher()
}
@ -163,7 +163,7 @@ public extension ContentDatabase {
try filter.save($0)
}
try Filter.filter(!filters.map(\.id).contains(Column("id"))).deleteAll($0)
try Filter.filter(!filters.map(\.id).contains(Filter.Columns.id)).deleteAll($0)
}
.ignoreOutput()
.eraseToAnyPublisher()
@ -176,7 +176,7 @@ public extension ContentDatabase {
}
func deleteFilter(id: String) -> AnyPublisher<Never, Error> {
databaseWriter.writePublisher(updates: Filter.filter(Column("id") == id).deleteAll)
databaseWriter.writePublisher(updates: Filter.filter(Filter.Columns.id == id).deleteAll)
.ignoreOutput()
.eraseToAnyPublisher()
}
@ -191,7 +191,9 @@ public extension ContentDatabase {
func contextObservation(parentID: String) -> AnyPublisher<[[Status]], Error> {
ValueObservation.tracking { db -> [[StatusResult]] in
guard let parent = try StatusRecord.filter(Column("id") == parentID).statusResultRequest.fetchOne(db) else {
guard let parent = try StatusRecord.filter(StatusRecord.Columns.id == parentID)
.statusResultRequest
.fetchOne(db) else {
return [[]]
}
@ -212,16 +214,16 @@ public extension ContentDatabase {
ValueObservation.tracking { db -> [[StatusResult]] in
let statuses = try StatusRecord.filter(
AccountStatusJoin
.select(Column("statusId"), as: String.self)
.select(AccountStatusJoin.Columns.statusId, as: String.self)
.filter(sql: "accountId = ? AND collection = ?", arguments: [accountID, collection.rawValue])
.contains(Column("id")))
.order(Column("createdAt").desc)
.contains(StatusRecord.Columns.id))
.order(StatusRecord.Columns.createdAt.desc)
.statusResultRequest
.fetchAll(db)
if
case .statuses = collection,
let accountRecord = try AccountRecord.filter(Column("id") == accountID).fetchOne(db) {
let accountRecord = try AccountRecord.filter(AccountRecord.Columns.id == accountID).fetchOne(db) {
let pinnedStatuses = try accountRecord.pinnedStatuses.fetchAll(db)
return [pinnedStatuses, statuses]
@ -236,8 +238,8 @@ public extension ContentDatabase {
}
func listsObservation() -> AnyPublisher<[Timeline], Error> {
ValueObservation.tracking(Timeline.filter(Column("listTitle") != nil)
.order(Column("listTitle").asc)
ValueObservation.tracking(Timeline.filter(Timeline.Columns.listTitle != nil)
.order(Timeline.Columns.listTitle.asc)
.fetchAll)
.removeDuplicates()
.publisher(in: databaseWriter)
@ -245,7 +247,8 @@ public extension ContentDatabase {
}
func activeFiltersObservation(date: Date, context: Filter.Context? = nil) -> AnyPublisher<[Filter], Error> {
ValueObservation.tracking(Filter.filter(Column("expiresAt") == nil || Column("expiresAt") > date).fetchAll)
ValueObservation.tracking(
Filter.filter(Filter.Columns.expiresAt == nil || Filter.Columns.expiresAt > date).fetchAll)
.removeDuplicates()
.publisher(in: databaseWriter)
.map {
@ -257,14 +260,14 @@ public extension ContentDatabase {
}
func expiredFiltersObservation(date: Date) -> AnyPublisher<[Filter], Error> {
ValueObservation.tracking(Filter.filter(Column("expiresAt") < date).fetchAll)
ValueObservation.tracking(Filter.filter(Filter.Columns.expiresAt < date).fetchAll)
.removeDuplicates()
.publisher(in: databaseWriter)
.eraseToAnyPublisher()
}
func accountObservation(id: String) -> AnyPublisher<Account?, Error> {
ValueObservation.tracking(AccountRecord.filter(Column("id") == id).accountResultRequest.fetchOne)
ValueObservation.tracking(AccountRecord.filter(AccountRecord.Columns.id == id).accountResultRequest.fetchOne)
.removeDuplicates()
.publisher(in: databaseWriter)
.map {
@ -424,7 +427,7 @@ private extension ContentDatabase {
func clean() {
databaseWriter.asyncWrite {
try TimelineStatusJoin.filter(Column("timelineId") != Timeline.home.id).deleteAll($0)
try TimelineStatusJoin.filter(TimelineStatusJoin.Columns.timelineId != Timeline.home.id).deleteAll($0)
try StatusContextJoin.deleteAll($0)
try AccountPinnedStatusJoin.deleteAll($0)
try AccountStatusJoin.deleteAll($0)

View file

@ -14,5 +14,14 @@ struct StatusContextJoin: Codable, FetchableRecord, PersistableRecord {
let section: Section
let index: Int
static let status = belongsTo(StatusRecord.self, using: ForeignKey([Column("statusId")]))
static let status = belongsTo(StatusRecord.self, using: ForeignKey([Columns.statusId]))
}
extension StatusContextJoin {
enum Columns {
static let parentId = Column(StatusContextJoin.CodingKeys.parentId)
static let statusId = Column(StatusContextJoin.CodingKeys.statusId)
static let section = Column(StatusContextJoin.CodingKeys.section)
static let index = Column(StatusContextJoin.CodingKeys.index)
}
}

View file

@ -36,6 +36,40 @@ struct StatusRecord: Codable, Hashable {
let pinned: Bool?
}
extension StatusRecord {
enum Columns {
static let id = Column(StatusRecord.CodingKeys.id)
static let uri = Column(StatusRecord.CodingKeys.uri)
static let createdAt = Column(StatusRecord.CodingKeys.createdAt)
static let accountId = Column(StatusRecord.CodingKeys.accountId)
static let content = Column(StatusRecord.CodingKeys.content)
static let visibility = Column(StatusRecord.CodingKeys.visibility)
static let sensitive = Column(StatusRecord.CodingKeys.sensitive)
static let spoilerText = Column(StatusRecord.CodingKeys.spoilerText)
static let mediaAttachments = Column(StatusRecord.CodingKeys.mediaAttachments)
static let mentions = Column(StatusRecord.CodingKeys.mentions)
static let tags = Column(StatusRecord.CodingKeys.tags)
static let emojis = Column(StatusRecord.CodingKeys.emojis)
static let reblogsCount = Column(StatusRecord.CodingKeys.reblogsCount)
static let favouritesCount = Column(StatusRecord.CodingKeys.favouritesCount)
static let repliesCount = Column(StatusRecord.CodingKeys.repliesCount)
static let application = Column(StatusRecord.CodingKeys.application)
static let url = Column(StatusRecord.CodingKeys.url)
static let inReplyToId = Column(StatusRecord.CodingKeys.inReplyToId)
static let inReplyToAccountId = Column(StatusRecord.CodingKeys.inReplyToAccountId)
static let reblogId = Column(StatusRecord.CodingKeys.reblogId)
static let poll = Column(StatusRecord.CodingKeys.poll)
static let card = Column(StatusRecord.CodingKeys.card)
static let language = Column(StatusRecord.CodingKeys.language)
static let text = Column(StatusRecord.CodingKeys.text)
static let favourited = Column(StatusRecord.CodingKeys.favourited)
static let reblogged = Column(StatusRecord.CodingKeys.reblogged)
static let muted = Column(StatusRecord.CodingKeys.muted)
static let bookmarked = Column(StatusRecord.CodingKeys.bookmarked)
static let pinned = Column(StatusRecord.CodingKeys.pinned)
}
}
extension StatusRecord: FetchableRecord, PersistableRecord {
static func databaseJSONDecoder(for column: String) -> JSONDecoder {
MastodonDecoder()
@ -47,7 +81,8 @@ extension StatusRecord: FetchableRecord, PersistableRecord {
}
extension StatusRecord {
static let account = belongsTo(AccountRecord.self, key: "account", using: ForeignKey([Column("accountId")]))
static let account = belongsTo(AccountRecord.self, key: "account",
using: ForeignKey([StatusRecord.Columns.accountId]))
static let accountMoved = hasOne(AccountRecord.self,
through: Self.account,
using: AccountRecord.moved,
@ -61,12 +96,16 @@ extension StatusRecord {
using: AccountRecord.moved,
key: "reblogAccountMoved")
static let reblog = belongsTo(StatusRecord.self, key: "reblog")
static let ancestorJoins = hasMany(StatusContextJoin.self, using: ForeignKey([Column("parentID")]))
.filter(Column("section") == StatusContextJoin.Section.ancestors.rawValue)
.order(Column("index"))
static let descendantJoins = hasMany(StatusContextJoin.self, using: ForeignKey([Column("parentID")]))
.filter(Column("section") == StatusContextJoin.Section.descendants.rawValue)
.order(Column("index"))
static let ancestorJoins = hasMany(
StatusContextJoin.self,
using: ForeignKey([StatusContextJoin.Columns.parentId]))
.filter(StatusContextJoin.Columns.section == StatusContextJoin.Section.ancestors.rawValue)
.order(StatusContextJoin.Columns.index)
static let descendantJoins = hasMany(
StatusContextJoin.self,
using: ForeignKey([StatusContextJoin.Columns.parentId]))
.filter(StatusContextJoin.Columns.section == StatusContextJoin.Section.descendants.rawValue)
.order(StatusContextJoin.Columns.index)
static let ancestors = hasMany(StatusRecord.self,
through: ancestorJoins,
using: StatusContextJoin.status)

View file

@ -9,3 +9,10 @@ struct TimelineStatusJoin: Codable, FetchableRecord, PersistableRecord {
static let status = belongsTo(StatusRecord.self)
}
extension TimelineStatusJoin {
enum Columns {
static let timelineId = Column(TimelineStatusJoin.CodingKeys.timelineId)
static let statusId = Column(TimelineStatusJoin.CodingKeys.statusId)
}
}

View file

@ -5,6 +5,15 @@ import GRDB
import Mastodon
extension Filter: FetchableRecord, PersistableRecord {
enum Columns: String, ColumnExpression {
case id
case phrase
case context
case expiresAt
case irreversible
case wholeWord
}
public static func databaseJSONDecoder(for column: String) -> JSONDecoder {
MastodonDecoder()
}

View file

@ -1,10 +0,0 @@
// Copyright © 2020 Metabolist. All rights reserved.
import Foundation
import GRDB
extension OrderedRequest {
func orderedByIDSequence(_ ids: [String]) -> Self {
fatalError()
}
}

View file

@ -6,7 +6,8 @@ import Mastodon
extension Timeline: FetchableRecord, PersistableRecord {
enum Columns: String, ColumnExpression {
case id, listTitle
case id
case listTitle
}
public init(row: Row) {
@ -42,7 +43,7 @@ extension Timeline {
StatusRecord.self,
through: statusJoins,
using: TimelineStatusJoin.status)
.order(Column("createdAt").desc)
.order(StatusRecord.Columns.createdAt.desc)
var statuses: QueryInterfaceRequest<StatusResult> {
request(for: Self.statuses).statusResultRequest

View file

@ -51,7 +51,7 @@ public extension IdentityDatabase {
}
func deleteIdentity(id: UUID) -> AnyPublisher<Never, Error> {
databaseWriter.writePublisher(updates: IdentityRecord.filter(Column("id") == id).deleteAll)
databaseWriter.writePublisher(updates: IdentityRecord.filter(IdentityRecord.Columns.id == id).deleteAll)
.ignoreOutput()
.eraseToAnyPublisher()
}
@ -59,8 +59,8 @@ public extension IdentityDatabase {
func updateLastUsedAt(identityID: UUID) -> AnyPublisher<Never, Error> {
databaseWriter.writePublisher {
try IdentityRecord
.filter(Column("id") == identityID)
.updateAll($0, Column("lastUsedAt").set(to: Date()))
.filter(IdentityRecord.Columns.id == identityID)
.updateAll($0, IdentityRecord.Columns.lastUsedAt.set(to: Date()))
}
.ignoreOutput()
.eraseToAnyPublisher()
@ -75,8 +75,8 @@ public extension IdentityDatabase {
thumbnail: instance.thumbnail)
.save($0)
try IdentityRecord
.filter(Column("id") == identityID)
.updateAll($0, Column("instanceURI").set(to: instance.uri))
.filter(IdentityRecord.Columns.id == identityID)
.updateAll($0, IdentityRecord.Columns.instanceURI.set(to: instance.uri))
}
.ignoreOutput()
.eraseToAnyPublisher()
@ -103,8 +103,8 @@ public extension IdentityDatabase {
func confirmIdentity(id: UUID) -> AnyPublisher<Never, Error> {
databaseWriter.writePublisher {
try IdentityRecord
.filter(Column("id") == id)
.updateAll($0, Column("pending").set(to: false))
.filter(IdentityRecord.Columns.id == id)
.updateAll($0, IdentityRecord.Columns.pending.set(to: false))
}
.ignoreOutput()
.eraseToAnyPublisher()
@ -113,7 +113,7 @@ public extension IdentityDatabase {
func updatePreferences(_ preferences: Mastodon.Preferences,
forIdentityID identityID: UUID) -> AnyPublisher<Never, Error> {
databaseWriter.writePublisher {
guard let storedPreferences = try IdentityRecord.filter(Column("id") == identityID)
guard let storedPreferences = try IdentityRecord.filter(IdentityRecord.Columns.id == identityID)
.fetchOne($0)?
.preferences else {
throw IdentityDatabaseError.identityNotFound
@ -136,16 +136,18 @@ public extension IdentityDatabase {
deviceToken: Data? = nil,
forIdentityID identityID: UUID) -> AnyPublisher<Never, Error> {
databaseWriter.writePublisher {
let data = try IdentityRecord.databaseJSONEncoder(for: "pushSubscriptionAlerts").encode(alerts)
let data = try IdentityRecord.databaseJSONEncoder(
for: IdentityRecord.Columns.pushSubscriptionAlerts.name)
.encode(alerts)
try IdentityRecord
.filter(Column("id") == identityID)
.updateAll($0, Column("pushSubscriptionAlerts").set(to: data))
.filter(IdentityRecord.Columns.id == identityID)
.updateAll($0, IdentityRecord.Columns.pushSubscriptionAlerts.set(to: data))
if let deviceToken = deviceToken {
try IdentityRecord
.filter(Column("id") == identityID)
.updateAll($0, Column("lastRegisteredDeviceToken").set(to: deviceToken))
.filter(IdentityRecord.Columns.id == identityID)
.updateAll($0, IdentityRecord.Columns.lastRegisteredDeviceToken.set(to: deviceToken))
}
}
.ignoreOutput()
@ -155,7 +157,7 @@ public extension IdentityDatabase {
func identityObservation(id: UUID, immediate: Bool) -> AnyPublisher<Identity, Error> {
ValueObservation.tracking(
IdentityRecord
.filter(Column("id") == id)
.filter(IdentityRecord.Columns.id == id)
.identityResultRequest
.fetchOne)
.removeDuplicates()
@ -171,7 +173,7 @@ public extension IdentityDatabase {
func identitiesObservation() -> AnyPublisher<[Identity], Error> {
ValueObservation.tracking(
IdentityRecord
.order(Column("lastUsedAt").desc)
.order(IdentityRecord.Columns.lastUsedAt.desc)
.identityResultRequest
.fetchAll)
.removeDuplicates()
@ -183,9 +185,9 @@ public extension IdentityDatabase {
func recentIdentitiesObservation(excluding: UUID) -> AnyPublisher<[Identity], Error> {
ValueObservation.tracking(
IdentityRecord
.order(Column("lastUsedAt").desc)
.order(IdentityRecord.Columns.lastUsedAt.desc)
.identityResultRequest
.filter(Column("id") != excluding)
.filter(IdentityRecord.Columns.id != excluding)
.limit(9)
.fetchAll)
.removeDuplicates()
@ -195,7 +197,9 @@ public extension IdentityDatabase {
}
func immediateMostRecentlyUsedIdentityIDObservation() -> AnyPublisher<UUID?, Error> {
ValueObservation.tracking(IdentityRecord.select(Column("id")).order(Column("lastUsedAt").desc).fetchOne)
ValueObservation.tracking(
IdentityRecord.select(IdentityRecord.Columns.id)
.order(IdentityRecord.Columns.lastUsedAt.desc).fetchOne)
.removeDuplicates()
.publisher(in: databaseWriter, scheduling: .immediate)
.eraseToAnyPublisher()
@ -204,9 +208,9 @@ public extension IdentityDatabase {
func identitiesWithOutdatedDeviceTokens(deviceToken: Data) -> AnyPublisher<[Identity], Error> {
databaseWriter.readPublisher(
value: IdentityRecord
.order(Column("lastUsedAt").desc)
.order(IdentityRecord.Columns.lastUsedAt.desc)
.identityResultRequest
.filter(Column("lastRegisteredDeviceToken") != deviceToken)
.filter(IdentityRecord.Columns.lastRegisteredDeviceToken != deviceToken)
.fetchAll)
.map { $0.map(Identity.init(result:)) }
.eraseToAnyPublisher()
@ -218,11 +222,12 @@ private extension IdentityDatabase {
static func writePreferences(_ preferences: Identity.Preferences, id: UUID) -> (Database) throws -> Void {
{
let data = try IdentityRecord.databaseJSONEncoder(for: "preferences").encode(preferences)
let data = try IdentityRecord.databaseJSONEncoder(
for: IdentityRecord.Columns.preferences.name).encode(preferences)
try IdentityRecord
.filter(Column("id") == id)
.updateAll($0, Column("preferences").set(to: data))
.filter(IdentityRecord.Columns.id == id)
.updateAll($0, IdentityRecord.Columns.preferences.set(to: data))
}
}

View file

@ -17,6 +17,18 @@ struct IdentityRecord: Codable, Hashable, FetchableRecord, PersistableRecord {
}
extension IdentityRecord {
enum Columns {
static let id = Column(IdentityRecord.CodingKeys.id)
static let url = Column(IdentityRecord.CodingKeys.url)
static let authenticated = Column(IdentityRecord.CodingKeys.authenticated)
static let pending = Column(IdentityRecord.CodingKeys.pending)
static let lastUsedAt = Column(IdentityRecord.CodingKeys.lastUsedAt)
static let preferences = Column(IdentityRecord.CodingKeys.preferences)
static let instanceURI = Column(IdentityRecord.CodingKeys.instanceURI)
static let lastRegisteredDeviceToken = Column(IdentityRecord.CodingKeys.lastRegisteredDeviceToken)
static let pushSubscriptionAlerts = Column(IdentityRecord.CodingKeys.pushSubscriptionAlerts)
}
static let instance = belongsTo(Identity.Instance.self, key: "instance")
static let account = hasOne(Identity.Account.self, key: "account")