feat(server): cards and their append-only revisions, synced
Card belongs to a studyset and carries only its position, a fractional index (last writer wins), so cards can be reordered and bulk imports keep their order. Its content lives in CardRevision rows, which are never updated except to blank them (ADR 0001, rule 2): parents lists the revisions a revision replaces (decided: a list, so a revision with two parents resolves a conflict), authorId the writer, reportId the report it answers once reports exist, and the envelope holds the content. Deciding the list of parents and the position now matters: changing a synced table later locks out older app versions. The migration (created by CLI) only adds: the two tables, an index on spaceId for each (space deletion cascades into them), one for a studyset's cards in order and one for a card's revisions, and the foreign keys, from the space with ON DELETE CASCADE and from the studyset and the card with ON DELETE CASCADE, DEFERRABLE INITIALLY DEFERRED, since synced rows can arrive in any order. The client migration creates the same tables on each device. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
312b18be5a
commit
8fe1187ad5
12 changed files with 16820 additions and 2 deletions
20
halkyon_learn_server/lib/src/card/card.spy.yaml
Normal file
20
halkyon_learn_server/lib/src/card/card.spy.yaml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
### A flashcard in a studyset. Its content lives in its revisions
|
||||
### (CardRevision), never in this row: content is append-only (ADR 0001,
|
||||
### rule 2). Synced; its space owns it.
|
||||
class: Card
|
||||
table: card
|
||||
database: sync
|
||||
fields:
|
||||
### The studyset the card belongs to; the card goes with it.
|
||||
studyset: Studyset?, relation(onDelete=Cascade, deferred)
|
||||
### Where the card stands in its studyset: a fractional index, so a card can
|
||||
### move between two others without renumbering the rest; last writer wins
|
||||
### (decided 2026-09-30).
|
||||
position: String
|
||||
indexes:
|
||||
# Deleting a space finds its rows through this index (see studyset.spy.yaml).
|
||||
card_space_idx:
|
||||
fields: spaceId
|
||||
# A studyset's cards, in order.
|
||||
card_studyset_idx:
|
||||
fields: studysetId, position
|
||||
33
halkyon_learn_server/lib/src/card/card_revision.spy.yaml
Normal file
33
halkyon_learn_server/lib/src/card/card_revision.spy.yaml
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
### One version of a card's content, never changed once written (ADR 0001,
|
||||
### rule 2): editing appends a revision whose parents are the versions it
|
||||
### replaces, and a card's current versions are the revisions no other
|
||||
### replaces. Deleting a card blanks every revision's payload first (ADR 0002,
|
||||
### rule 2).
|
||||
class: CardRevision
|
||||
table: card_revision
|
||||
database: sync
|
||||
fields:
|
||||
### The card this is a version of; its revisions go with it.
|
||||
card: Card?, relation(onDelete=Cascade, deferred)
|
||||
### The revisions this one replaces: none for a card's first, several when it
|
||||
### resolves a conflict (decided 2026-09-30). Plain ids, since revisions can
|
||||
### arrive in any order.
|
||||
parents: List<UuidValue>
|
||||
### Who wrote it: the AuthUser's id (a synced table can't relate to the
|
||||
### server-only AuthUser).
|
||||
authorId: UuidValue
|
||||
### The report this revision answers, once reports exist; null otherwise.
|
||||
reportId: UuidValue?
|
||||
### The encryption key's id; always null (ADR 0002, rule 6).
|
||||
keyId: String?
|
||||
### The content as UTF-8 JSON: {"schemaVersion", "front", "back"}.
|
||||
payload: ByteData
|
||||
### The encryption nonce; always null, like keyId.
|
||||
nonce: ByteData?
|
||||
indexes:
|
||||
# Deleting a space finds its rows through this index (see studyset.spy.yaml).
|
||||
card_revision_space_idx:
|
||||
fields: spaceId
|
||||
# A card's revisions.
|
||||
card_revision_card_idx:
|
||||
fields: cardId
|
||||
726
halkyon_learn_server/lib/src/generated/card/card.dart
Normal file
726
halkyon_learn_server/lib/src/generated/card/card.dart
Normal file
|
|
@ -0,0 +1,726 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
// ignore_for_file: dead_code, unnecessary_null_comparison
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:halkyon_learn_server/src/generated/protocol.dart' as _i5lsenq5;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
import '../studyset/studyset.dart' as _ie5k1jc2;
|
||||
|
||||
/// A flashcard in a studyset. Its content lives in its revisions
|
||||
/// (CardRevision), never in this row: content is append-only (ADR 0001,
|
||||
/// rule 2). Synced; its space owns it.
|
||||
abstract class Card
|
||||
implements _is.TableRow<_is.UuidValue?>, _is.ProtocolSerialization {
|
||||
Card._({
|
||||
this.id,
|
||||
this.spaceId,
|
||||
required this.studysetId,
|
||||
this.studyset,
|
||||
required this.position,
|
||||
});
|
||||
|
||||
factory Card({
|
||||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
required _is.UuidValue studysetId,
|
||||
_ie5k1jc2.Studyset? studyset,
|
||||
required String position,
|
||||
}) = _CardImpl;
|
||||
|
||||
factory Card.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return Card(
|
||||
id: jsonSerialization['id'] == null
|
||||
? null
|
||||
: _is.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
|
||||
spaceId: jsonSerialization['spaceId'] as int?,
|
||||
studysetId: _is.UuidValueJsonExtension.fromJson(
|
||||
jsonSerialization['studysetId'],
|
||||
),
|
||||
studyset: jsonSerialization['studyset'] == null
|
||||
? null
|
||||
: _i5lsenq5.Protocol().deserialize<_ie5k1jc2.Studyset>(
|
||||
jsonSerialization['studyset'],
|
||||
),
|
||||
position: jsonSerialization['position'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
static final t = CardTable();
|
||||
|
||||
static const db = CardRepository._();
|
||||
|
||||
@override
|
||||
_is.UuidValue? id;
|
||||
|
||||
/// The space owning this row. Maintained by the sync engine.
|
||||
int? spaceId;
|
||||
|
||||
_is.UuidValue studysetId;
|
||||
|
||||
/// The studyset the card belongs to; the card goes with it.
|
||||
_ie5k1jc2.Studyset? studyset;
|
||||
|
||||
/// Where the card stands in its studyset: a fractional index, so a card can
|
||||
/// move between two others without renumbering the rest; last writer wins
|
||||
/// (decided 2026-09-30).
|
||||
String position;
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => t;
|
||||
|
||||
/// Returns a shallow copy of this [Card]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
Card copyWith({
|
||||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
_is.UuidValue? studysetId,
|
||||
_ie5k1jc2.Studyset? studyset,
|
||||
String? position,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'Card',
|
||||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
'studysetId': studysetId.toJson(),
|
||||
if (studyset != null) 'studyset': studyset?.toJson(),
|
||||
'position': position,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'Card',
|
||||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
'studysetId': studysetId.toJson(),
|
||||
if (studyset != null) 'studyset': studyset?.toJsonForProtocol(),
|
||||
'position': position,
|
||||
};
|
||||
}
|
||||
|
||||
static CardInclude include({_ie5k1jc2.StudysetInclude? studyset}) {
|
||||
return CardInclude._(studyset: studyset);
|
||||
}
|
||||
|
||||
static CardIncludeList includeList({
|
||||
_is.WhereExpressionBuilder<CardTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<CardTable>? orderBy,
|
||||
_is.OrderByListBuilder<CardTable>? orderByList,
|
||||
CardInclude? include,
|
||||
}) {
|
||||
return CardIncludeList._(
|
||||
where: where,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(Card.t),
|
||||
orderByList: orderByList?.call(Card.t),
|
||||
include: include,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _is.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _Undefined {}
|
||||
|
||||
class _CardImpl extends Card {
|
||||
_CardImpl({
|
||||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
required _is.UuidValue studysetId,
|
||||
_ie5k1jc2.Studyset? studyset,
|
||||
required String position,
|
||||
}) : super._(
|
||||
id: id,
|
||||
spaceId: spaceId,
|
||||
studysetId: studysetId,
|
||||
studyset: studyset,
|
||||
position: position,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [Card]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
@override
|
||||
Card copyWith({
|
||||
Object? id = _Undefined,
|
||||
Object? spaceId = _Undefined,
|
||||
_is.UuidValue? studysetId,
|
||||
Object? studyset = _Undefined,
|
||||
String? position,
|
||||
}) {
|
||||
return Card(
|
||||
id: id is _is.UuidValue? ? id : this.id,
|
||||
spaceId: spaceId is int? ? spaceId : this.spaceId,
|
||||
studysetId: studysetId ?? this.studysetId,
|
||||
studyset: studyset is _ie5k1jc2.Studyset?
|
||||
? studyset
|
||||
: this.studyset?.copyWith(),
|
||||
position: position ?? this.position,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CardUpdateTable extends _is.UpdateTable<CardTable> {
|
||||
CardUpdateTable(super.table);
|
||||
|
||||
_is.ColumnValue<int, int> spaceId(int? value) => _is.ColumnValue(
|
||||
table.spaceId,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<_is.UuidValue, _is.UuidValue> studysetId(
|
||||
_is.UuidValue value,
|
||||
) => _is.ColumnValue(
|
||||
table.studysetId,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> position(String value) => _is.ColumnValue(
|
||||
table.position,
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
class CardTable extends _is.Table<_is.UuidValue?> {
|
||||
CardTable({super.tableRelation}) : super(tableName: 'card') {
|
||||
updateTable = CardUpdateTable(this);
|
||||
spaceId = _is.ColumnInt(
|
||||
'spaceId',
|
||||
this,
|
||||
);
|
||||
studysetId = _is.ColumnUuid(
|
||||
'studysetId',
|
||||
this,
|
||||
);
|
||||
position = _is.ColumnString(
|
||||
'position',
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
late final CardUpdateTable updateTable;
|
||||
|
||||
/// The space owning this row. Maintained by the sync engine.
|
||||
late final _is.ColumnInt spaceId;
|
||||
|
||||
late final _is.ColumnUuid studysetId;
|
||||
|
||||
/// The studyset the card belongs to; the card goes with it.
|
||||
_ie5k1jc2.StudysetTable? _studyset;
|
||||
|
||||
/// Where the card stands in its studyset: a fractional index, so a card can
|
||||
/// move between two others without renumbering the rest; last writer wins
|
||||
/// (decided 2026-09-30).
|
||||
late final _is.ColumnString position;
|
||||
|
||||
_ie5k1jc2.StudysetTable get studyset {
|
||||
if (_studyset != null) return _studyset!;
|
||||
_studyset = _is.createRelationTable(
|
||||
relationFieldName: 'studyset',
|
||||
field: Card.t.studysetId,
|
||||
foreignField: _ie5k1jc2.Studyset.t.id,
|
||||
tableRelation: tableRelation,
|
||||
createTable: (foreignTableRelation) =>
|
||||
_ie5k1jc2.StudysetTable(tableRelation: foreignTableRelation),
|
||||
);
|
||||
return _studyset!;
|
||||
}
|
||||
|
||||
@override
|
||||
List<_is.Column> get columns => [
|
||||
id,
|
||||
spaceId,
|
||||
studysetId,
|
||||
position,
|
||||
];
|
||||
|
||||
@override
|
||||
_is.Table? getRelationTable(String relationField) {
|
||||
if (relationField == 'studyset') {
|
||||
return studyset;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class CardInclude extends _is.IncludeObject {
|
||||
CardInclude._({_ie5k1jc2.StudysetInclude? studyset}) {
|
||||
_studyset = studyset;
|
||||
}
|
||||
|
||||
_ie5k1jc2.StudysetInclude? _studyset;
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => {'studyset': _studyset};
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => Card.t;
|
||||
}
|
||||
|
||||
class CardIncludeList extends _is.IncludeList {
|
||||
CardIncludeList._({
|
||||
_is.WhereExpressionBuilder<CardTable>? where,
|
||||
super.limit,
|
||||
super.offset,
|
||||
super.orderBy,
|
||||
super.orderByList,
|
||||
super.include,
|
||||
}) {
|
||||
super.where = where?.call(Card.t);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => include?.includes ?? {};
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => Card.t;
|
||||
}
|
||||
|
||||
class CardRepository {
|
||||
const CardRepository._();
|
||||
|
||||
final attachRow = const CardAttachRowRepository._();
|
||||
|
||||
/// Returns a list of [Card]s matching the given query parameters.
|
||||
///
|
||||
/// Use [where] to specify which items to include in the return value.
|
||||
/// If none is specified, all items will be returned.
|
||||
///
|
||||
/// To specify the order of the items use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// The maximum number of items can be set by [limit]. If no limit is set,
|
||||
/// all items matching the query will be returned.
|
||||
///
|
||||
/// [offset] defines how many items to skip, after which [limit] (or all)
|
||||
/// items are read from the database.
|
||||
///
|
||||
/// ```dart
|
||||
/// var persons = await Persons.db.find(
|
||||
/// session,
|
||||
/// where: (t) => t.lastName.equals('Jones'),
|
||||
/// orderBy: (t) => t.firstName,
|
||||
/// limit: 100,
|
||||
/// );
|
||||
/// ```
|
||||
Future<List<Card>> find(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<CardTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<CardTable>? orderBy,
|
||||
_is.OrderByListBuilder<CardTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
CardInclude? include,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.find<Card>(
|
||||
where: where?.call(Card.t),
|
||||
orderBy: orderBy?.call(Card.t),
|
||||
orderByList: orderByList?.call(Card.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
include: include,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns the first matching [Card] matching the given query parameters.
|
||||
///
|
||||
/// Use [where] to specify which items to include in the return value.
|
||||
/// If none is specified, all items will be returned.
|
||||
///
|
||||
/// To specify the order use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// [offset] defines how many items to skip, after which the next one will be picked.
|
||||
///
|
||||
/// ```dart
|
||||
/// var youngestPerson = await Persons.db.findFirstRow(
|
||||
/// session,
|
||||
/// where: (t) => t.lastName.equals('Jones'),
|
||||
/// orderBy: (t) => t.age,
|
||||
/// );
|
||||
/// ```
|
||||
Future<Card?> findFirstRow(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<CardTable>? where,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<CardTable>? orderBy,
|
||||
_is.OrderByListBuilder<CardTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
CardInclude? include,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findFirstRow<Card>(
|
||||
where: where?.call(Card.t),
|
||||
orderBy: orderBy?.call(Card.t),
|
||||
orderByList: orderByList?.call(Card.t),
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
include: include,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Finds a single [Card] by its [id] or null if no such row exists.
|
||||
Future<Card?> findById(
|
||||
_is.DatabaseSession session,
|
||||
_is.UuidValue id, {
|
||||
_is.Transaction? transaction,
|
||||
CardInclude? include,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findById<Card>(
|
||||
id,
|
||||
transaction: transaction,
|
||||
include: include,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts all [Card]s in the list and returns the inserted rows.
|
||||
///
|
||||
/// The returned [Card]s will have their `id` fields set.
|
||||
///
|
||||
/// This is an atomic operation, meaning that if one of the rows fails to
|
||||
/// insert, none of the rows will be inserted.
|
||||
///
|
||||
/// If [ignoreConflicts] is set to `true`, rows that conflict with existing
|
||||
/// rows are silently skipped, and only the successfully inserted rows are
|
||||
/// returned.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the inserted rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<Card>> insert(
|
||||
_is.DatabaseSession session,
|
||||
List<Card> rows, {
|
||||
_is.Transaction? transaction,
|
||||
bool ignoreConflicts = false,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.insert<Card>(
|
||||
rows,
|
||||
transaction: transaction,
|
||||
ignoreConflicts: ignoreConflicts,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts a single [Card] and returns the inserted row.
|
||||
///
|
||||
/// The returned [Card] will have its `id` field set.
|
||||
Future<Card> insertRow(
|
||||
_is.DatabaseSession session,
|
||||
Card row, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.insertRow<Card>(
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts all [Card]s in the list and returns the resulting rows.
|
||||
///
|
||||
/// If a row conflicts on the given [conflictColumns], the existing row is
|
||||
/// updated with the new values. Otherwise, a new row is inserted.
|
||||
///
|
||||
/// If [updateColumns] is provided, only those columns will be updated on
|
||||
/// conflict. If null, all non-conflict, non-id columns are updated.
|
||||
///
|
||||
/// If [updateWhere] is provided, the update only applies to rows matching the
|
||||
/// given expression. Conflicting rows that don't match are skipped and not
|
||||
/// returned, so the resulting list may be shorter than [rows].
|
||||
///
|
||||
/// The returned [Card]s will have their `id` fields set.
|
||||
///
|
||||
/// This is an atomic operation, meaning that if one of the rows fails,
|
||||
/// none of the rows will be affected.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the resulting rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<Card>> upsert(
|
||||
_is.DatabaseSession session,
|
||||
List<Card> rows, {
|
||||
required _is.ColumnSelections<CardTable> conflictColumns,
|
||||
_is.ColumnSelections<CardTable>? updateColumns,
|
||||
_is.WhereExpressionBuilder<CardTable>? updateWhere,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.upsert<Card>(
|
||||
rows,
|
||||
conflictColumns: conflictColumns(Card.t),
|
||||
updateColumns: updateColumns?.call(Card.t),
|
||||
updateWhere: updateWhere?.call(Card.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts a single [Card] and returns the resulting row.
|
||||
///
|
||||
/// If the row conflicts on the given [conflictColumns], the existing row is
|
||||
/// updated. Otherwise, a new row is inserted.
|
||||
///
|
||||
/// If [updateColumns] is provided, only those columns will be updated on
|
||||
/// conflict. If null, all non-conflict, non-id columns are updated.
|
||||
///
|
||||
/// If [updateWhere] is provided, the update only applies when the existing
|
||||
/// row matches the expression. Returns `null` if no row was affected — for
|
||||
/// example when [updateWhere] does not match the conflicting row.
|
||||
///
|
||||
/// The returned [Card] will have its `id` field set.
|
||||
Future<Card?> upsertRow(
|
||||
_is.DatabaseSession session,
|
||||
Card row, {
|
||||
required _is.ColumnSelections<CardTable> conflictColumns,
|
||||
_is.ColumnSelections<CardTable>? updateColumns,
|
||||
_is.WhereExpressionBuilder<CardTable>? updateWhere,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.upsertRow<Card>(
|
||||
row,
|
||||
conflictColumns: conflictColumns(Card.t),
|
||||
updateColumns: updateColumns?.call(Card.t),
|
||||
updateWhere: updateWhere?.call(Card.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [Card]s in the list and returns the updated rows. If
|
||||
/// [columns] is provided, only those columns will be updated. Defaults to
|
||||
/// all columns.
|
||||
/// This is an atomic operation, meaning that if one of the rows fails to
|
||||
/// update, none of the rows will be updated.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the updated rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<Card>> update(
|
||||
_is.DatabaseSession session,
|
||||
List<Card> rows, {
|
||||
_is.ColumnSelections<CardTable>? columns,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.update<Card>(
|
||||
rows,
|
||||
columns: columns?.call(Card.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [Card]. The row needs to have its id set.
|
||||
/// Optionally, a list of [columns] can be provided to only update those
|
||||
/// columns. Defaults to all columns.
|
||||
Future<Card> updateRow(
|
||||
_is.DatabaseSession session,
|
||||
Card row, {
|
||||
_is.ColumnSelections<CardTable>? columns,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateRow<Card>(
|
||||
row,
|
||||
columns: columns?.call(Card.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [Card] by its [id] with the specified [columnValues].
|
||||
/// Returns the updated row or null if no row with the given id exists.
|
||||
Future<Card?> updateById(
|
||||
_is.DatabaseSession session,
|
||||
_is.UuidValue id, {
|
||||
required _is.ColumnValueListBuilder<CardUpdateTable> columnValues,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateById<Card>(
|
||||
id,
|
||||
columnValues: columnValues(Card.t.updateTable),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [Card]s matching the [where] expression with the specified [columnValues].
|
||||
/// Returns the list of updated rows.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the updated rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<Card>> updateWhere(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.ColumnValueListBuilder<CardUpdateTable> columnValues,
|
||||
required _is.WhereExpressionBuilder<CardTable> where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<CardTable>? orderBy,
|
||||
_is.OrderByListBuilder<CardTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.updateWhere<Card>(
|
||||
columnValues: columnValues(Card.t.updateTable),
|
||||
where: where(Card.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(Card.t),
|
||||
orderByList: orderByList?.call(Card.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes all [Card]s in the list and returns the deleted rows.
|
||||
///
|
||||
/// To specify the order of the returned rows use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// This is an atomic operation, meaning that if one of the rows fail to
|
||||
/// be deleted, none of the rows will be deleted.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the deleted rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<Card>> delete(
|
||||
_is.DatabaseSession session,
|
||||
List<Card> rows, {
|
||||
_is.OrderByBuilder<CardTable>? orderBy,
|
||||
_is.OrderByListBuilder<CardTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.delete<Card>(
|
||||
rows,
|
||||
orderBy: orderBy?.call(Card.t),
|
||||
orderByList: orderByList?.call(Card.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes a single [Card].
|
||||
Future<Card> deleteRow(
|
||||
_is.DatabaseSession session,
|
||||
Card row, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.deleteRow<Card>(
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes all rows matching the [where] expression.
|
||||
///
|
||||
/// To specify the order of the returned rows use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the deleted rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<Card>> deleteWhere(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.WhereExpressionBuilder<CardTable> where,
|
||||
_is.OrderByBuilder<CardTable>? orderBy,
|
||||
_is.OrderByListBuilder<CardTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.deleteWhere<Card>(
|
||||
where: where(Card.t),
|
||||
orderBy: orderBy?.call(Card.t),
|
||||
orderByList: orderByList?.call(Card.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Counts the number of rows matching the [where] expression. If omitted,
|
||||
/// will return the count of all rows in the table.
|
||||
Future<int> count(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<CardTable>? where,
|
||||
int? limit,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.count<Card>(
|
||||
where: where?.call(Card.t),
|
||||
limit: limit,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Acquires row-level locks on [Card] rows matching the [where] expression.
|
||||
Future<void> lockRows(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.WhereExpressionBuilder<CardTable> where,
|
||||
required _is.LockMode lockMode,
|
||||
required _is.Transaction transaction,
|
||||
_is.LockBehavior lockBehavior = _is.LockBehavior.wait,
|
||||
}) async {
|
||||
return session.db.lockRows<Card>(
|
||||
where: where(Card.t),
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CardAttachRowRepository {
|
||||
const CardAttachRowRepository._();
|
||||
|
||||
/// Creates a relation between the given [Card] and [Studyset]
|
||||
/// by setting the [Card]'s foreign key `studysetId` to refer to the [Studyset].
|
||||
Future<void> studyset(
|
||||
_is.DatabaseSession session,
|
||||
Card card,
|
||||
_ie5k1jc2.Studyset studyset, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
if (card.id == null) {
|
||||
throw ArgumentError.notNull('card.id');
|
||||
}
|
||||
if (studyset.id == null) {
|
||||
throw ArgumentError.notNull('studyset.id');
|
||||
}
|
||||
|
||||
var $card = card.copyWith(studysetId: studyset.id);
|
||||
await session.db.updateRow<Card>(
|
||||
$card,
|
||||
columns: [Card.t.studysetId],
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
871
halkyon_learn_server/lib/src/generated/card/card_revision.dart
Normal file
871
halkyon_learn_server/lib/src/generated/card/card_revision.dart
Normal file
|
|
@ -0,0 +1,871 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
// ignore_for_file: dead_code, unnecessary_null_comparison
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:typed_data' as _idt;
|
||||
import 'package:halkyon_learn_server/src/generated/protocol.dart' as _i5lsenq5;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
import '../card/card.dart' as _i4926i81;
|
||||
|
||||
/// One version of a card's content, never changed once written (ADR 0001,
|
||||
/// rule 2): editing appends a revision whose parents are the versions it
|
||||
/// replaces, and a card's current versions are the revisions no other
|
||||
/// replaces. Deleting a card blanks every revision's payload first (ADR 0002,
|
||||
/// rule 2).
|
||||
abstract class CardRevision
|
||||
implements _is.TableRow<_is.UuidValue?>, _is.ProtocolSerialization {
|
||||
CardRevision._({
|
||||
this.id,
|
||||
this.spaceId,
|
||||
required this.cardId,
|
||||
this.card,
|
||||
required this.parents,
|
||||
required this.authorId,
|
||||
this.reportId,
|
||||
this.keyId,
|
||||
required this.payload,
|
||||
this.nonce,
|
||||
});
|
||||
|
||||
factory CardRevision({
|
||||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
required _is.UuidValue cardId,
|
||||
_i4926i81.Card? card,
|
||||
required List<_is.UuidValue> parents,
|
||||
required _is.UuidValue authorId,
|
||||
_is.UuidValue? reportId,
|
||||
String? keyId,
|
||||
required _idt.ByteData payload,
|
||||
_idt.ByteData? nonce,
|
||||
}) = _CardRevisionImpl;
|
||||
|
||||
factory CardRevision.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return CardRevision(
|
||||
id: jsonSerialization['id'] == null
|
||||
? null
|
||||
: _is.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
|
||||
spaceId: jsonSerialization['spaceId'] as int?,
|
||||
cardId: _is.UuidValueJsonExtension.fromJson(jsonSerialization['cardId']),
|
||||
card: jsonSerialization['card'] == null
|
||||
? null
|
||||
: _i5lsenq5.Protocol().deserialize<_i4926i81.Card>(
|
||||
jsonSerialization['card'],
|
||||
),
|
||||
parents: _i5lsenq5.Protocol().deserialize<List<_is.UuidValue>>(
|
||||
jsonSerialization['parents'],
|
||||
),
|
||||
authorId: _is.UuidValueJsonExtension.fromJson(
|
||||
jsonSerialization['authorId'],
|
||||
),
|
||||
reportId: jsonSerialization['reportId'] == null
|
||||
? null
|
||||
: _is.UuidValueJsonExtension.fromJson(jsonSerialization['reportId']),
|
||||
keyId: jsonSerialization['keyId'] as String?,
|
||||
payload: _is.ByteDataJsonExtension.fromJson(jsonSerialization['payload']),
|
||||
nonce: jsonSerialization['nonce'] == null
|
||||
? null
|
||||
: _is.ByteDataJsonExtension.fromJson(jsonSerialization['nonce']),
|
||||
);
|
||||
}
|
||||
|
||||
static final t = CardRevisionTable();
|
||||
|
||||
static const db = CardRevisionRepository._();
|
||||
|
||||
@override
|
||||
_is.UuidValue? id;
|
||||
|
||||
/// The space owning this row. Maintained by the sync engine.
|
||||
int? spaceId;
|
||||
|
||||
_is.UuidValue cardId;
|
||||
|
||||
/// The card this is a version of; its revisions go with it.
|
||||
_i4926i81.Card? card;
|
||||
|
||||
/// The revisions this one replaces: none for a card's first, several when it
|
||||
/// resolves a conflict (decided 2026-09-30). Plain ids, since revisions can
|
||||
/// arrive in any order.
|
||||
List<_is.UuidValue> parents;
|
||||
|
||||
/// Who wrote it: the AuthUser's id (a synced table can't relate to the
|
||||
/// server-only AuthUser).
|
||||
_is.UuidValue authorId;
|
||||
|
||||
/// The report this revision answers, once reports exist; null otherwise.
|
||||
_is.UuidValue? reportId;
|
||||
|
||||
/// The encryption key's id; always null (ADR 0002, rule 6).
|
||||
String? keyId;
|
||||
|
||||
/// The content as UTF-8 JSON: {"schemaVersion", "front", "back"}.
|
||||
_idt.ByteData payload;
|
||||
|
||||
/// The encryption nonce; always null, like keyId.
|
||||
_idt.ByteData? nonce;
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => t;
|
||||
|
||||
/// Returns a shallow copy of this [CardRevision]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
CardRevision copyWith({
|
||||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
_is.UuidValue? cardId,
|
||||
_i4926i81.Card? card,
|
||||
List<_is.UuidValue>? parents,
|
||||
_is.UuidValue? authorId,
|
||||
_is.UuidValue? reportId,
|
||||
String? keyId,
|
||||
_idt.ByteData? payload,
|
||||
_idt.ByteData? nonce,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'CardRevision',
|
||||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
'cardId': cardId.toJson(),
|
||||
if (card != null) 'card': card?.toJson(),
|
||||
'parents': parents.toJson(valueToJson: (v) => v.toJson()),
|
||||
'authorId': authorId.toJson(),
|
||||
if (reportId != null) 'reportId': reportId?.toJson(),
|
||||
if (keyId != null) 'keyId': keyId,
|
||||
'payload': payload.toJson(),
|
||||
if (nonce != null) 'nonce': nonce?.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'CardRevision',
|
||||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
'cardId': cardId.toJson(),
|
||||
if (card != null) 'card': card?.toJsonForProtocol(),
|
||||
'parents': parents.toJson(valueToJson: (v) => v.toJson()),
|
||||
'authorId': authorId.toJson(),
|
||||
if (reportId != null) 'reportId': reportId?.toJson(),
|
||||
if (keyId != null) 'keyId': keyId,
|
||||
'payload': payload.toJson(),
|
||||
if (nonce != null) 'nonce': nonce?.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
static CardRevisionInclude include({_i4926i81.CardInclude? card}) {
|
||||
return CardRevisionInclude._(card: card);
|
||||
}
|
||||
|
||||
static CardRevisionIncludeList includeList({
|
||||
_is.WhereExpressionBuilder<CardRevisionTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<CardRevisionTable>? orderBy,
|
||||
_is.OrderByListBuilder<CardRevisionTable>? orderByList,
|
||||
CardRevisionInclude? include,
|
||||
}) {
|
||||
return CardRevisionIncludeList._(
|
||||
where: where,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(CardRevision.t),
|
||||
orderByList: orderByList?.call(CardRevision.t),
|
||||
include: include,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _is.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _Undefined {}
|
||||
|
||||
class _CardRevisionImpl extends CardRevision {
|
||||
_CardRevisionImpl({
|
||||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
required _is.UuidValue cardId,
|
||||
_i4926i81.Card? card,
|
||||
required List<_is.UuidValue> parents,
|
||||
required _is.UuidValue authorId,
|
||||
_is.UuidValue? reportId,
|
||||
String? keyId,
|
||||
required _idt.ByteData payload,
|
||||
_idt.ByteData? nonce,
|
||||
}) : super._(
|
||||
id: id,
|
||||
spaceId: spaceId,
|
||||
cardId: cardId,
|
||||
card: card,
|
||||
parents: parents,
|
||||
authorId: authorId,
|
||||
reportId: reportId,
|
||||
keyId: keyId,
|
||||
payload: payload,
|
||||
nonce: nonce,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [CardRevision]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
@override
|
||||
CardRevision copyWith({
|
||||
Object? id = _Undefined,
|
||||
Object? spaceId = _Undefined,
|
||||
_is.UuidValue? cardId,
|
||||
Object? card = _Undefined,
|
||||
List<_is.UuidValue>? parents,
|
||||
_is.UuidValue? authorId,
|
||||
Object? reportId = _Undefined,
|
||||
Object? keyId = _Undefined,
|
||||
_idt.ByteData? payload,
|
||||
Object? nonce = _Undefined,
|
||||
}) {
|
||||
return CardRevision(
|
||||
id: id is _is.UuidValue? ? id : this.id,
|
||||
spaceId: spaceId is int? ? spaceId : this.spaceId,
|
||||
cardId: cardId ?? this.cardId,
|
||||
card: card is _i4926i81.Card? ? card : this.card?.copyWith(),
|
||||
parents: parents ?? this.parents.map((e0) => e0).toList(),
|
||||
authorId: authorId ?? this.authorId,
|
||||
reportId: reportId is _is.UuidValue? ? reportId : this.reportId,
|
||||
keyId: keyId is String? ? keyId : this.keyId,
|
||||
payload: payload ?? this.payload.clone(),
|
||||
nonce: nonce is _idt.ByteData? ? nonce : this.nonce?.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CardRevisionUpdateTable extends _is.UpdateTable<CardRevisionTable> {
|
||||
CardRevisionUpdateTable(super.table);
|
||||
|
||||
_is.ColumnValue<int, int> spaceId(int? value) => _is.ColumnValue(
|
||||
table.spaceId,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<_is.UuidValue, _is.UuidValue> cardId(_is.UuidValue value) =>
|
||||
_is.ColumnValue(
|
||||
table.cardId,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<List<_is.UuidValue>, List<_is.UuidValue>> parents(
|
||||
List<_is.UuidValue> value,
|
||||
) => _is.ColumnValue(
|
||||
table.parents,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<_is.UuidValue, _is.UuidValue> authorId(_is.UuidValue value) =>
|
||||
_is.ColumnValue(
|
||||
table.authorId,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<_is.UuidValue, _is.UuidValue> reportId(
|
||||
_is.UuidValue? value,
|
||||
) => _is.ColumnValue(
|
||||
table.reportId,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> keyId(String? value) => _is.ColumnValue(
|
||||
table.keyId,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<_idt.ByteData, _idt.ByteData> payload(_idt.ByteData value) =>
|
||||
_is.ColumnValue(
|
||||
table.payload,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<_idt.ByteData, _idt.ByteData> nonce(_idt.ByteData? value) =>
|
||||
_is.ColumnValue(
|
||||
table.nonce,
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
class CardRevisionTable extends _is.Table<_is.UuidValue?> {
|
||||
CardRevisionTable({super.tableRelation}) : super(tableName: 'card_revision') {
|
||||
updateTable = CardRevisionUpdateTable(this);
|
||||
spaceId = _is.ColumnInt(
|
||||
'spaceId',
|
||||
this,
|
||||
);
|
||||
cardId = _is.ColumnUuid(
|
||||
'cardId',
|
||||
this,
|
||||
);
|
||||
parents = _is.ColumnSerializable<List<_is.UuidValue>>(
|
||||
'parents',
|
||||
this,
|
||||
);
|
||||
authorId = _is.ColumnUuid(
|
||||
'authorId',
|
||||
this,
|
||||
);
|
||||
reportId = _is.ColumnUuid(
|
||||
'reportId',
|
||||
this,
|
||||
);
|
||||
keyId = _is.ColumnString(
|
||||
'keyId',
|
||||
this,
|
||||
);
|
||||
payload = _is.ColumnByteData(
|
||||
'payload',
|
||||
this,
|
||||
);
|
||||
nonce = _is.ColumnByteData(
|
||||
'nonce',
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
late final CardRevisionUpdateTable updateTable;
|
||||
|
||||
/// The space owning this row. Maintained by the sync engine.
|
||||
late final _is.ColumnInt spaceId;
|
||||
|
||||
late final _is.ColumnUuid cardId;
|
||||
|
||||
/// The card this is a version of; its revisions go with it.
|
||||
_i4926i81.CardTable? _card;
|
||||
|
||||
/// The revisions this one replaces: none for a card's first, several when it
|
||||
/// resolves a conflict (decided 2026-09-30). Plain ids, since revisions can
|
||||
/// arrive in any order.
|
||||
late final _is.ColumnSerializable<List<_is.UuidValue>> parents;
|
||||
|
||||
/// Who wrote it: the AuthUser's id (a synced table can't relate to the
|
||||
/// server-only AuthUser).
|
||||
late final _is.ColumnUuid authorId;
|
||||
|
||||
/// The report this revision answers, once reports exist; null otherwise.
|
||||
late final _is.ColumnUuid reportId;
|
||||
|
||||
/// The encryption key's id; always null (ADR 0002, rule 6).
|
||||
late final _is.ColumnString keyId;
|
||||
|
||||
/// The content as UTF-8 JSON: {"schemaVersion", "front", "back"}.
|
||||
late final _is.ColumnByteData payload;
|
||||
|
||||
/// The encryption nonce; always null, like keyId.
|
||||
late final _is.ColumnByteData nonce;
|
||||
|
||||
_i4926i81.CardTable get card {
|
||||
if (_card != null) return _card!;
|
||||
_card = _is.createRelationTable(
|
||||
relationFieldName: 'card',
|
||||
field: CardRevision.t.cardId,
|
||||
foreignField: _i4926i81.Card.t.id,
|
||||
tableRelation: tableRelation,
|
||||
createTable: (foreignTableRelation) =>
|
||||
_i4926i81.CardTable(tableRelation: foreignTableRelation),
|
||||
);
|
||||
return _card!;
|
||||
}
|
||||
|
||||
@override
|
||||
List<_is.Column> get columns => [
|
||||
id,
|
||||
spaceId,
|
||||
cardId,
|
||||
parents,
|
||||
authorId,
|
||||
reportId,
|
||||
keyId,
|
||||
payload,
|
||||
nonce,
|
||||
];
|
||||
|
||||
@override
|
||||
_is.Table? getRelationTable(String relationField) {
|
||||
if (relationField == 'card') {
|
||||
return card;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class CardRevisionInclude extends _is.IncludeObject {
|
||||
CardRevisionInclude._({_i4926i81.CardInclude? card}) {
|
||||
_card = card;
|
||||
}
|
||||
|
||||
_i4926i81.CardInclude? _card;
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => {'card': _card};
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => CardRevision.t;
|
||||
}
|
||||
|
||||
class CardRevisionIncludeList extends _is.IncludeList {
|
||||
CardRevisionIncludeList._({
|
||||
_is.WhereExpressionBuilder<CardRevisionTable>? where,
|
||||
super.limit,
|
||||
super.offset,
|
||||
super.orderBy,
|
||||
super.orderByList,
|
||||
super.include,
|
||||
}) {
|
||||
super.where = where?.call(CardRevision.t);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => include?.includes ?? {};
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => CardRevision.t;
|
||||
}
|
||||
|
||||
class CardRevisionRepository {
|
||||
const CardRevisionRepository._();
|
||||
|
||||
final attachRow = const CardRevisionAttachRowRepository._();
|
||||
|
||||
/// Returns a list of [CardRevision]s matching the given query parameters.
|
||||
///
|
||||
/// Use [where] to specify which items to include in the return value.
|
||||
/// If none is specified, all items will be returned.
|
||||
///
|
||||
/// To specify the order of the items use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// The maximum number of items can be set by [limit]. If no limit is set,
|
||||
/// all items matching the query will be returned.
|
||||
///
|
||||
/// [offset] defines how many items to skip, after which [limit] (or all)
|
||||
/// items are read from the database.
|
||||
///
|
||||
/// ```dart
|
||||
/// var persons = await Persons.db.find(
|
||||
/// session,
|
||||
/// where: (t) => t.lastName.equals('Jones'),
|
||||
/// orderBy: (t) => t.firstName,
|
||||
/// limit: 100,
|
||||
/// );
|
||||
/// ```
|
||||
Future<List<CardRevision>> find(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<CardRevisionTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<CardRevisionTable>? orderBy,
|
||||
_is.OrderByListBuilder<CardRevisionTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
CardRevisionInclude? include,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.find<CardRevision>(
|
||||
where: where?.call(CardRevision.t),
|
||||
orderBy: orderBy?.call(CardRevision.t),
|
||||
orderByList: orderByList?.call(CardRevision.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
include: include,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns the first matching [CardRevision] matching the given query parameters.
|
||||
///
|
||||
/// Use [where] to specify which items to include in the return value.
|
||||
/// If none is specified, all items will be returned.
|
||||
///
|
||||
/// To specify the order use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// [offset] defines how many items to skip, after which the next one will be picked.
|
||||
///
|
||||
/// ```dart
|
||||
/// var youngestPerson = await Persons.db.findFirstRow(
|
||||
/// session,
|
||||
/// where: (t) => t.lastName.equals('Jones'),
|
||||
/// orderBy: (t) => t.age,
|
||||
/// );
|
||||
/// ```
|
||||
Future<CardRevision?> findFirstRow(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<CardRevisionTable>? where,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<CardRevisionTable>? orderBy,
|
||||
_is.OrderByListBuilder<CardRevisionTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
CardRevisionInclude? include,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findFirstRow<CardRevision>(
|
||||
where: where?.call(CardRevision.t),
|
||||
orderBy: orderBy?.call(CardRevision.t),
|
||||
orderByList: orderByList?.call(CardRevision.t),
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
include: include,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Finds a single [CardRevision] by its [id] or null if no such row exists.
|
||||
Future<CardRevision?> findById(
|
||||
_is.DatabaseSession session,
|
||||
_is.UuidValue id, {
|
||||
_is.Transaction? transaction,
|
||||
CardRevisionInclude? include,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findById<CardRevision>(
|
||||
id,
|
||||
transaction: transaction,
|
||||
include: include,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts all [CardRevision]s in the list and returns the inserted rows.
|
||||
///
|
||||
/// The returned [CardRevision]s will have their `id` fields set.
|
||||
///
|
||||
/// This is an atomic operation, meaning that if one of the rows fails to
|
||||
/// insert, none of the rows will be inserted.
|
||||
///
|
||||
/// If [ignoreConflicts] is set to `true`, rows that conflict with existing
|
||||
/// rows are silently skipped, and only the successfully inserted rows are
|
||||
/// returned.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the inserted rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<CardRevision>> insert(
|
||||
_is.DatabaseSession session,
|
||||
List<CardRevision> rows, {
|
||||
_is.Transaction? transaction,
|
||||
bool ignoreConflicts = false,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.insert<CardRevision>(
|
||||
rows,
|
||||
transaction: transaction,
|
||||
ignoreConflicts: ignoreConflicts,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts a single [CardRevision] and returns the inserted row.
|
||||
///
|
||||
/// The returned [CardRevision] will have its `id` field set.
|
||||
Future<CardRevision> insertRow(
|
||||
_is.DatabaseSession session,
|
||||
CardRevision row, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.insertRow<CardRevision>(
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts all [CardRevision]s in the list and returns the resulting rows.
|
||||
///
|
||||
/// If a row conflicts on the given [conflictColumns], the existing row is
|
||||
/// updated with the new values. Otherwise, a new row is inserted.
|
||||
///
|
||||
/// If [updateColumns] is provided, only those columns will be updated on
|
||||
/// conflict. If null, all non-conflict, non-id columns are updated.
|
||||
///
|
||||
/// If [updateWhere] is provided, the update only applies to rows matching the
|
||||
/// given expression. Conflicting rows that don't match are skipped and not
|
||||
/// returned, so the resulting list may be shorter than [rows].
|
||||
///
|
||||
/// The returned [CardRevision]s will have their `id` fields set.
|
||||
///
|
||||
/// This is an atomic operation, meaning that if one of the rows fails,
|
||||
/// none of the rows will be affected.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the resulting rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<CardRevision>> upsert(
|
||||
_is.DatabaseSession session,
|
||||
List<CardRevision> rows, {
|
||||
required _is.ColumnSelections<CardRevisionTable> conflictColumns,
|
||||
_is.ColumnSelections<CardRevisionTable>? updateColumns,
|
||||
_is.WhereExpressionBuilder<CardRevisionTable>? updateWhere,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.upsert<CardRevision>(
|
||||
rows,
|
||||
conflictColumns: conflictColumns(CardRevision.t),
|
||||
updateColumns: updateColumns?.call(CardRevision.t),
|
||||
updateWhere: updateWhere?.call(CardRevision.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts a single [CardRevision] and returns the resulting row.
|
||||
///
|
||||
/// If the row conflicts on the given [conflictColumns], the existing row is
|
||||
/// updated. Otherwise, a new row is inserted.
|
||||
///
|
||||
/// If [updateColumns] is provided, only those columns will be updated on
|
||||
/// conflict. If null, all non-conflict, non-id columns are updated.
|
||||
///
|
||||
/// If [updateWhere] is provided, the update only applies when the existing
|
||||
/// row matches the expression. Returns `null` if no row was affected — for
|
||||
/// example when [updateWhere] does not match the conflicting row.
|
||||
///
|
||||
/// The returned [CardRevision] will have its `id` field set.
|
||||
Future<CardRevision?> upsertRow(
|
||||
_is.DatabaseSession session,
|
||||
CardRevision row, {
|
||||
required _is.ColumnSelections<CardRevisionTable> conflictColumns,
|
||||
_is.ColumnSelections<CardRevisionTable>? updateColumns,
|
||||
_is.WhereExpressionBuilder<CardRevisionTable>? updateWhere,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.upsertRow<CardRevision>(
|
||||
row,
|
||||
conflictColumns: conflictColumns(CardRevision.t),
|
||||
updateColumns: updateColumns?.call(CardRevision.t),
|
||||
updateWhere: updateWhere?.call(CardRevision.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [CardRevision]s in the list and returns the updated rows. If
|
||||
/// [columns] is provided, only those columns will be updated. Defaults to
|
||||
/// all columns.
|
||||
/// This is an atomic operation, meaning that if one of the rows fails to
|
||||
/// update, none of the rows will be updated.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the updated rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<CardRevision>> update(
|
||||
_is.DatabaseSession session,
|
||||
List<CardRevision> rows, {
|
||||
_is.ColumnSelections<CardRevisionTable>? columns,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.update<CardRevision>(
|
||||
rows,
|
||||
columns: columns?.call(CardRevision.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [CardRevision]. The row needs to have its id set.
|
||||
/// Optionally, a list of [columns] can be provided to only update those
|
||||
/// columns. Defaults to all columns.
|
||||
Future<CardRevision> updateRow(
|
||||
_is.DatabaseSession session,
|
||||
CardRevision row, {
|
||||
_is.ColumnSelections<CardRevisionTable>? columns,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateRow<CardRevision>(
|
||||
row,
|
||||
columns: columns?.call(CardRevision.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [CardRevision] by its [id] with the specified [columnValues].
|
||||
/// Returns the updated row or null if no row with the given id exists.
|
||||
Future<CardRevision?> updateById(
|
||||
_is.DatabaseSession session,
|
||||
_is.UuidValue id, {
|
||||
required _is.ColumnValueListBuilder<CardRevisionUpdateTable> columnValues,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateById<CardRevision>(
|
||||
id,
|
||||
columnValues: columnValues(CardRevision.t.updateTable),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [CardRevision]s matching the [where] expression with the specified [columnValues].
|
||||
/// Returns the list of updated rows.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the updated rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<CardRevision>> updateWhere(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.ColumnValueListBuilder<CardRevisionUpdateTable> columnValues,
|
||||
required _is.WhereExpressionBuilder<CardRevisionTable> where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<CardRevisionTable>? orderBy,
|
||||
_is.OrderByListBuilder<CardRevisionTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.updateWhere<CardRevision>(
|
||||
columnValues: columnValues(CardRevision.t.updateTable),
|
||||
where: where(CardRevision.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(CardRevision.t),
|
||||
orderByList: orderByList?.call(CardRevision.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes all [CardRevision]s in the list and returns the deleted rows.
|
||||
///
|
||||
/// To specify the order of the returned rows use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// This is an atomic operation, meaning that if one of the rows fail to
|
||||
/// be deleted, none of the rows will be deleted.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the deleted rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<CardRevision>> delete(
|
||||
_is.DatabaseSession session,
|
||||
List<CardRevision> rows, {
|
||||
_is.OrderByBuilder<CardRevisionTable>? orderBy,
|
||||
_is.OrderByListBuilder<CardRevisionTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.delete<CardRevision>(
|
||||
rows,
|
||||
orderBy: orderBy?.call(CardRevision.t),
|
||||
orderByList: orderByList?.call(CardRevision.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes a single [CardRevision].
|
||||
Future<CardRevision> deleteRow(
|
||||
_is.DatabaseSession session,
|
||||
CardRevision row, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.deleteRow<CardRevision>(
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes all rows matching the [where] expression.
|
||||
///
|
||||
/// To specify the order of the returned rows use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the deleted rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<CardRevision>> deleteWhere(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.WhereExpressionBuilder<CardRevisionTable> where,
|
||||
_is.OrderByBuilder<CardRevisionTable>? orderBy,
|
||||
_is.OrderByListBuilder<CardRevisionTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.deleteWhere<CardRevision>(
|
||||
where: where(CardRevision.t),
|
||||
orderBy: orderBy?.call(CardRevision.t),
|
||||
orderByList: orderByList?.call(CardRevision.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Counts the number of rows matching the [where] expression. If omitted,
|
||||
/// will return the count of all rows in the table.
|
||||
Future<int> count(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<CardRevisionTable>? where,
|
||||
int? limit,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.count<CardRevision>(
|
||||
where: where?.call(CardRevision.t),
|
||||
limit: limit,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Acquires row-level locks on [CardRevision] rows matching the [where] expression.
|
||||
Future<void> lockRows(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.WhereExpressionBuilder<CardRevisionTable> where,
|
||||
required _is.LockMode lockMode,
|
||||
required _is.Transaction transaction,
|
||||
_is.LockBehavior lockBehavior = _is.LockBehavior.wait,
|
||||
}) async {
|
||||
return session.db.lockRows<CardRevision>(
|
||||
where: where(CardRevision.t),
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CardRevisionAttachRowRepository {
|
||||
const CardRevisionAttachRowRepository._();
|
||||
|
||||
/// Creates a relation between the given [CardRevision] and [Card]
|
||||
/// by setting the [CardRevision]'s foreign key `cardId` to refer to the [Card].
|
||||
Future<void> card(
|
||||
_is.DatabaseSession session,
|
||||
CardRevision cardRevision,
|
||||
_i4926i81.Card card, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
if (cardRevision.id == null) {
|
||||
throw ArgumentError.notNull('cardRevision.id');
|
||||
}
|
||||
if (card.id == null) {
|
||||
throw ArgumentError.notNull('card.id');
|
||||
}
|
||||
|
||||
var $cardRevision = cardRevision.copyWith(cardId: card.id);
|
||||
await session.db.updateRow<CardRevision>(
|
||||
$cardRevision,
|
||||
columns: [CardRevision.t.cardId],
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -24,12 +24,16 @@ import 'package:serverpod_offline_sync_server/serverpod_offline_sync_server.dart
|
|||
import 'auth/device.dart' as _i6c5ny7n;
|
||||
import 'auth/github_sign_in_refusal.dart' as _ir1gd9p5;
|
||||
import 'auth/github_sign_in_refused_exception.dart' as _i0xfcu5n;
|
||||
import 'card/card.dart' as _ipiw1cmx;
|
||||
import 'card/card_revision.dart' as _ipbyuef1;
|
||||
import 'common/not_found_exception.dart' as _i8oee73b;
|
||||
import 'instance/instance_info.dart' as _iskrg2g3;
|
||||
import 'studyset/studyset.dart' as _ikbclmae;
|
||||
export 'auth/device.dart';
|
||||
export 'auth/github_sign_in_refusal.dart';
|
||||
export 'auth/github_sign_in_refused_exception.dart';
|
||||
export 'card/card.dart';
|
||||
export 'card/card_revision.dart';
|
||||
export 'common/not_found_exception.dart';
|
||||
export 'instance/instance_info.dart';
|
||||
export 'studyset/studyset.dart';
|
||||
|
|
@ -43,6 +47,210 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
static final Protocol _instance = Protocol._().._registerHostProtocols();
|
||||
|
||||
static List<_isp.TableDefinition> get targetTableDefinitions => [
|
||||
_isp.TableDefinition(
|
||||
name: 'card',
|
||||
dartName: 'Card',
|
||||
schema: 'public',
|
||||
module: 'halkyon_learn',
|
||||
columns: [
|
||||
_isp.ColumnDefinition(
|
||||
name: 'id',
|
||||
columnType: _isp.ColumnType.uuid,
|
||||
isNullable: false,
|
||||
dartType: 'UuidValue?',
|
||||
columnDefault: 'random_v7',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'spaceId',
|
||||
columnType: _isp.ColumnType.bigint,
|
||||
isNullable: true,
|
||||
dartType: 'int?',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'studysetId',
|
||||
columnType: _isp.ColumnType.uuid,
|
||||
isNullable: false,
|
||||
dartType: 'UuidValue',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'position',
|
||||
columnType: _isp.ColumnType.text,
|
||||
isNullable: false,
|
||||
dartType: 'String',
|
||||
),
|
||||
],
|
||||
foreignKeys: [
|
||||
_isp.ForeignKeyDefinition(
|
||||
constraintName: 'card_fk_0',
|
||||
columns: ['spaceId'],
|
||||
referenceTable: 'offline_sync_spaces',
|
||||
referenceTableSchema: 'public',
|
||||
referenceColumns: ['id'],
|
||||
onUpdate: _isp.ForeignKeyAction.noAction,
|
||||
onDelete: _isp.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
),
|
||||
_isp.ForeignKeyDefinition(
|
||||
constraintName: 'card_fk_1',
|
||||
columns: ['studysetId'],
|
||||
referenceTable: 'studyset',
|
||||
referenceTableSchema: 'public',
|
||||
referenceColumns: ['id'],
|
||||
onUpdate: _isp.ForeignKeyAction.noAction,
|
||||
onDelete: _isp.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
deferrable: _isp.DeferrableConstraint.initiallyDeferred,
|
||||
),
|
||||
],
|
||||
indexes: [
|
||||
_isp.IndexDefinition(
|
||||
indexName: 'card_space_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isp.IndexElementDefinition(
|
||||
type: _isp.IndexElementDefinitionType.column,
|
||||
definition: 'spaceId',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
_isp.IndexDefinition(
|
||||
indexName: 'card_studyset_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isp.IndexElementDefinition(
|
||||
type: _isp.IndexElementDefinitionType.column,
|
||||
definition: 'studysetId',
|
||||
),
|
||||
_isp.IndexElementDefinition(
|
||||
type: _isp.IndexElementDefinitionType.column,
|
||||
definition: 'position',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
],
|
||||
managed: true,
|
||||
),
|
||||
_isp.TableDefinition(
|
||||
name: 'card_revision',
|
||||
dartName: 'CardRevision',
|
||||
schema: 'public',
|
||||
module: 'halkyon_learn',
|
||||
columns: [
|
||||
_isp.ColumnDefinition(
|
||||
name: 'id',
|
||||
columnType: _isp.ColumnType.uuid,
|
||||
isNullable: false,
|
||||
dartType: 'UuidValue?',
|
||||
columnDefault: 'random_v7',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'spaceId',
|
||||
columnType: _isp.ColumnType.bigint,
|
||||
isNullable: true,
|
||||
dartType: 'int?',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'cardId',
|
||||
columnType: _isp.ColumnType.uuid,
|
||||
isNullable: false,
|
||||
dartType: 'UuidValue',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'parents',
|
||||
columnType: _isp.ColumnType.json,
|
||||
isNullable: false,
|
||||
dartType: 'List<UuidValue>',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'authorId',
|
||||
columnType: _isp.ColumnType.uuid,
|
||||
isNullable: false,
|
||||
dartType: 'UuidValue',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'reportId',
|
||||
columnType: _isp.ColumnType.uuid,
|
||||
isNullable: true,
|
||||
dartType: 'UuidValue?',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'keyId',
|
||||
columnType: _isp.ColumnType.text,
|
||||
isNullable: true,
|
||||
dartType: 'String?',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'payload',
|
||||
columnType: _isp.ColumnType.bytea,
|
||||
isNullable: false,
|
||||
dartType: 'dart:typed_data:ByteData',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'nonce',
|
||||
columnType: _isp.ColumnType.bytea,
|
||||
isNullable: true,
|
||||
dartType: 'dart:typed_data:ByteData?',
|
||||
),
|
||||
],
|
||||
foreignKeys: [
|
||||
_isp.ForeignKeyDefinition(
|
||||
constraintName: 'card_revision_fk_0',
|
||||
columns: ['spaceId'],
|
||||
referenceTable: 'offline_sync_spaces',
|
||||
referenceTableSchema: 'public',
|
||||
referenceColumns: ['id'],
|
||||
onUpdate: _isp.ForeignKeyAction.noAction,
|
||||
onDelete: _isp.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
),
|
||||
_isp.ForeignKeyDefinition(
|
||||
constraintName: 'card_revision_fk_1',
|
||||
columns: ['cardId'],
|
||||
referenceTable: 'card',
|
||||
referenceTableSchema: 'public',
|
||||
referenceColumns: ['id'],
|
||||
onUpdate: _isp.ForeignKeyAction.noAction,
|
||||
onDelete: _isp.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
deferrable: _isp.DeferrableConstraint.initiallyDeferred,
|
||||
),
|
||||
],
|
||||
indexes: [
|
||||
_isp.IndexDefinition(
|
||||
indexName: 'card_revision_space_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isp.IndexElementDefinition(
|
||||
type: _isp.IndexElementDefinitionType.column,
|
||||
definition: 'spaceId',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
_isp.IndexDefinition(
|
||||
indexName: 'card_revision_card_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isp.IndexElementDefinition(
|
||||
type: _isp.IndexElementDefinitionType.column,
|
||||
definition: 'cardId',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
],
|
||||
managed: true,
|
||||
),
|
||||
_isp.TableDefinition(
|
||||
name: 'studyset',
|
||||
dartName: 'Studyset',
|
||||
|
|
@ -152,6 +360,12 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
if (t == _i0xfcu5n.GitHubSignInRefusedException) {
|
||||
return _i0xfcu5n.GitHubSignInRefusedException.fromJson(data) as T;
|
||||
}
|
||||
if (t == _ipiw1cmx.Card) {
|
||||
return _ipiw1cmx.Card.fromJson(data) as T;
|
||||
}
|
||||
if (t == _ipbyuef1.CardRevision) {
|
||||
return _ipbyuef1.CardRevision.fromJson(data) as T;
|
||||
}
|
||||
if (t == _i8oee73b.NotFoundException) {
|
||||
return _i8oee73b.NotFoundException.fromJson(data) as T;
|
||||
}
|
||||
|
|
@ -176,6 +390,12 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
: null)
|
||||
as T;
|
||||
}
|
||||
if (t == _is.getType<_ipiw1cmx.Card?>()) {
|
||||
return (data != null ? _ipiw1cmx.Card.fromJson(data) : null) as T;
|
||||
}
|
||||
if (t == _is.getType<_ipbyuef1.CardRevision?>()) {
|
||||
return (data != null ? _ipbyuef1.CardRevision.fromJson(data) : null) as T;
|
||||
}
|
||||
if (t == _is.getType<_i8oee73b.NotFoundException?>()) {
|
||||
return (data != null ? _i8oee73b.NotFoundException.fromJson(data) : null)
|
||||
as T;
|
||||
|
|
@ -186,6 +406,10 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
if (t == _is.getType<_ikbclmae.Studyset?>()) {
|
||||
return (data != null ? _ikbclmae.Studyset.fromJson(data) : null) as T;
|
||||
}
|
||||
if (t == List<_is.UuidValue>) {
|
||||
return (data as List).map((e) => deserialize<_is.UuidValue>(e)).toList()
|
||||
as T;
|
||||
}
|
||||
if (t == List<_ipdmo8cq.Device>) {
|
||||
return (data as List)
|
||||
.map((e) => deserialize<_ipdmo8cq.Device>(e))
|
||||
|
|
@ -212,6 +436,8 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
_i6c5ny7n.Device => 'Device',
|
||||
_ir1gd9p5.GitHubSignInRefusal => 'GitHubSignInRefusal',
|
||||
_i0xfcu5n.GitHubSignInRefusedException => 'GitHubSignInRefusedException',
|
||||
_ipiw1cmx.Card => 'Card',
|
||||
_ipbyuef1.CardRevision => 'CardRevision',
|
||||
_i8oee73b.NotFoundException => 'NotFoundException',
|
||||
_iskrg2g3.InstanceInfo => 'InstanceInfo',
|
||||
_ikbclmae.Studyset => 'Studyset',
|
||||
|
|
@ -238,6 +464,10 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
return 'GitHubSignInRefusal';
|
||||
case _i0xfcu5n.GitHubSignInRefusedException():
|
||||
return 'GitHubSignInRefusedException';
|
||||
case _ipiw1cmx.Card():
|
||||
return 'Card';
|
||||
case _ipbyuef1.CardRevision():
|
||||
return 'CardRevision';
|
||||
case _i8oee73b.NotFoundException():
|
||||
return 'NotFoundException';
|
||||
case _iskrg2g3.InstanceInfo():
|
||||
|
|
@ -285,6 +515,12 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
if (dataClassName == 'GitHubSignInRefusedException') {
|
||||
return deserialize<_i0xfcu5n.GitHubSignInRefusedException>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'Card') {
|
||||
return deserialize<_ipiw1cmx.Card>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'CardRevision') {
|
||||
return deserialize<_ipbyuef1.CardRevision>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'NotFoundException') {
|
||||
return deserialize<_i8oee73b.NotFoundException>(data['data']);
|
||||
}
|
||||
|
|
@ -346,6 +582,10 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
}
|
||||
}
|
||||
switch (t) {
|
||||
case _ipiw1cmx.Card:
|
||||
return _ipiw1cmx.Card.t;
|
||||
case _ipbyuef1.CardRevision:
|
||||
return _ipbyuef1.CardRevision.t;
|
||||
case _ikbclmae.Studyset:
|
||||
return _ikbclmae.Studyset.t;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,15 @@
|
|||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
import 'card/card.dart' as _ipiw1cmx;
|
||||
import 'card/card_revision.dart' as _ipbyuef1;
|
||||
import 'studyset/studyset.dart' as _ikbclmae;
|
||||
|
||||
/// The tables synchronized between client and server by the
|
||||
/// `serverpod_offline_sync` package, including the ones owned by modules and
|
||||
/// shared packages.
|
||||
final List<_is.Table> syncTables = [_ikbclmae.Studyset.t];
|
||||
final List<_is.Table> syncTables = [
|
||||
_ipiw1cmx.Card.t,
|
||||
_ipbyuef1.CardRevision.t,
|
||||
_ikbclmae.Studyset.t,
|
||||
];
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,366 @@
|
|||
{
|
||||
"__className__": "serverpod.DatabaseDefinition",
|
||||
"schemaVersion": 2,
|
||||
"moduleName": "halkyon_learn",
|
||||
"tables": [
|
||||
{
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "card",
|
||||
"dartName": "Card",
|
||||
"module": "halkyon_learn",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"columnDefault": "random_v7",
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "spaceId",
|
||||
"fieldName": "spaceId",
|
||||
"columnType": 6,
|
||||
"isNullable": true,
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "studysetId",
|
||||
"fieldName": "studysetId",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "position",
|
||||
"fieldName": "position",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"dartType": "String"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "card_fk_0",
|
||||
"columns": [
|
||||
"spaceId"
|
||||
],
|
||||
"referenceTable": "offline_sync_spaces",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "card_fk_1",
|
||||
"columns": [
|
||||
"studysetId"
|
||||
],
|
||||
"referenceTable": "studyset",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4,
|
||||
"deferrable": "initiallyDeferred"
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "card_space_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "card_studyset_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "studysetId"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "position"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "card_revision",
|
||||
"dartName": "CardRevision",
|
||||
"module": "halkyon_learn",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"columnDefault": "random_v7",
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "spaceId",
|
||||
"fieldName": "spaceId",
|
||||
"columnType": 6,
|
||||
"isNullable": true,
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "cardId",
|
||||
"fieldName": "cardId",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "parents",
|
||||
"fieldName": "parents",
|
||||
"columnType": 8,
|
||||
"isNullable": false,
|
||||
"dartType": "List<UuidValue>"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "authorId",
|
||||
"fieldName": "authorId",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "reportId",
|
||||
"fieldName": "reportId",
|
||||
"columnType": 7,
|
||||
"isNullable": true,
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "keyId",
|
||||
"fieldName": "keyId",
|
||||
"columnType": 0,
|
||||
"isNullable": true,
|
||||
"dartType": "String?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "payload",
|
||||
"fieldName": "payload",
|
||||
"columnType": 5,
|
||||
"isNullable": false,
|
||||
"dartType": "dart:typed_data:ByteData"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "nonce",
|
||||
"fieldName": "nonce",
|
||||
"columnType": 5,
|
||||
"isNullable": true,
|
||||
"dartType": "dart:typed_data:ByteData?"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "card_revision_fk_0",
|
||||
"columns": [
|
||||
"spaceId"
|
||||
],
|
||||
"referenceTable": "offline_sync_spaces",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "card_revision_fk_1",
|
||||
"columns": [
|
||||
"cardId"
|
||||
],
|
||||
"referenceTable": "card",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4,
|
||||
"deferrable": "initiallyDeferred"
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "card_revision_space_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "card_revision_card_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "cardId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "studyset",
|
||||
"dartName": "Studyset",
|
||||
"module": "halkyon_learn",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"columnDefault": "random_v7",
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "spaceId",
|
||||
"fieldName": "spaceId",
|
||||
"columnType": 6,
|
||||
"isNullable": true,
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "keyId",
|
||||
"fieldName": "keyId",
|
||||
"columnType": 0,
|
||||
"isNullable": true,
|
||||
"dartType": "String?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "payload",
|
||||
"fieldName": "payload",
|
||||
"columnType": 5,
|
||||
"isNullable": false,
|
||||
"dartType": "dart:typed_data:ByteData"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "nonce",
|
||||
"fieldName": "nonce",
|
||||
"columnType": 5,
|
||||
"isNullable": true,
|
||||
"dartType": "dart:typed_data:ByteData?"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "studyset_fk_0",
|
||||
"columns": [
|
||||
"spaceId"
|
||||
],
|
||||
"referenceTable": "offline_sync_spaces",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "studyset_space_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
}
|
||||
],
|
||||
"installedModules": [
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationVersion",
|
||||
"module": "serverpod",
|
||||
"version": "20260824182259319"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationVersion",
|
||||
"module": "serverpod_auth_idp",
|
||||
"version": "20260924105404509"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationVersion",
|
||||
"module": "serverpod_offline_sync",
|
||||
"version": "20260914143806119"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationVersion",
|
||||
"module": "serverpod_auth_core",
|
||||
"version": "20260924105232991"
|
||||
}
|
||||
],
|
||||
"migrationApiVersion": 1
|
||||
}
|
||||
|
|
@ -0,0 +1,268 @@
|
|||
{
|
||||
"__className__": "serverpod.DatabaseMigration",
|
||||
"actions": [
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationAction",
|
||||
"type": "createTable",
|
||||
"createTable": {
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "card",
|
||||
"dartName": "Card",
|
||||
"module": "halkyon_learn",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"columnDefault": "random_v7",
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "spaceId",
|
||||
"fieldName": "spaceId",
|
||||
"columnType": 6,
|
||||
"isNullable": true,
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "studysetId",
|
||||
"fieldName": "studysetId",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "position",
|
||||
"fieldName": "position",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"dartType": "String"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "card_fk_0",
|
||||
"columns": [
|
||||
"spaceId"
|
||||
],
|
||||
"referenceTable": "offline_sync_spaces",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "card_fk_1",
|
||||
"columns": [
|
||||
"studysetId"
|
||||
],
|
||||
"referenceTable": "studyset",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4,
|
||||
"deferrable": "initiallyDeferred"
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "card_space_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "card_studyset_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "studysetId"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "position"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationAction",
|
||||
"type": "createTable",
|
||||
"createTable": {
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "card_revision",
|
||||
"dartName": "CardRevision",
|
||||
"module": "halkyon_learn",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"columnDefault": "random_v7",
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "spaceId",
|
||||
"fieldName": "spaceId",
|
||||
"columnType": 6,
|
||||
"isNullable": true,
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "cardId",
|
||||
"fieldName": "cardId",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "parents",
|
||||
"fieldName": "parents",
|
||||
"columnType": 8,
|
||||
"isNullable": false,
|
||||
"dartType": "List<UuidValue>"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "authorId",
|
||||
"fieldName": "authorId",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "reportId",
|
||||
"fieldName": "reportId",
|
||||
"columnType": 7,
|
||||
"isNullable": true,
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "keyId",
|
||||
"fieldName": "keyId",
|
||||
"columnType": 0,
|
||||
"isNullable": true,
|
||||
"dartType": "String?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "payload",
|
||||
"fieldName": "payload",
|
||||
"columnType": 5,
|
||||
"isNullable": false,
|
||||
"dartType": "dart:typed_data:ByteData"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "nonce",
|
||||
"fieldName": "nonce",
|
||||
"columnType": 5,
|
||||
"isNullable": true,
|
||||
"dartType": "dart:typed_data:ByteData?"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "card_revision_fk_0",
|
||||
"columns": [
|
||||
"spaceId"
|
||||
],
|
||||
"referenceTable": "offline_sync_spaces",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "card_revision_fk_1",
|
||||
"columns": [
|
||||
"cardId"
|
||||
],
|
||||
"referenceTable": "card",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4,
|
||||
"deferrable": "initiallyDeferred"
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "card_revision_space_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "card_revision_card_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "cardId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"warnings": [],
|
||||
"migrationApiVersion": 1
|
||||
}
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
BEGIN;
|
||||
|
||||
--
|
||||
-- Function: gen_random_uuid_v7()
|
||||
-- Source: https://gist.github.com/kjmph/5bd772b2c2df145aa645b837da7eca74
|
||||
-- License: MIT (copyright notice included on the generator source code).
|
||||
--
|
||||
create or replace function gen_random_uuid_v7()
|
||||
returns uuid
|
||||
as $$
|
||||
begin
|
||||
-- use random v4 uuid as starting point (which has the same variant we need)
|
||||
-- then overlay timestamp
|
||||
-- then set version 7 by flipping the 2 and 1 bit in the version 4 string
|
||||
return encode(
|
||||
set_bit(
|
||||
set_bit(
|
||||
overlay(uuid_send(gen_random_uuid())
|
||||
placing substring(int8send(floor(extract(epoch from clock_timestamp()) * 1000)::bigint) from 3)
|
||||
from 1 for 6
|
||||
),
|
||||
52, 1
|
||||
),
|
||||
53, 1
|
||||
),
|
||||
'hex')::uuid;
|
||||
end
|
||||
$$
|
||||
language plpgsql
|
||||
volatile;
|
||||
|
||||
--
|
||||
-- ACTION CREATE TABLE
|
||||
--
|
||||
CREATE TABLE "card" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"spaceId" bigint,
|
||||
"studysetId" uuid NOT NULL,
|
||||
"position" text NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX "card_space_idx" ON "card" USING btree ("spaceId");
|
||||
CREATE INDEX "card_studyset_idx" ON "card" USING btree ("studysetId", "position");
|
||||
|
||||
--
|
||||
-- ACTION CREATE TABLE
|
||||
--
|
||||
CREATE TABLE "card_revision" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"spaceId" bigint,
|
||||
"cardId" uuid NOT NULL,
|
||||
"parents" json NOT NULL,
|
||||
"authorId" uuid NOT NULL,
|
||||
"reportId" uuid,
|
||||
"keyId" text,
|
||||
"payload" bytea NOT NULL,
|
||||
"nonce" bytea
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX "card_revision_space_idx" ON "card_revision" USING btree ("spaceId");
|
||||
CREATE INDEX "card_revision_card_idx" ON "card_revision" USING btree ("cardId");
|
||||
|
||||
--
|
||||
-- ACTION CREATE FOREIGN KEY
|
||||
--
|
||||
ALTER TABLE ONLY "card"
|
||||
ADD CONSTRAINT "card_fk_0"
|
||||
FOREIGN KEY("spaceId")
|
||||
REFERENCES "offline_sync_spaces"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
ALTER TABLE ONLY "card"
|
||||
ADD CONSTRAINT "card_fk_1"
|
||||
FOREIGN KEY("studysetId")
|
||||
REFERENCES "studyset"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION
|
||||
DEFERRABLE INITIALLY DEFERRED;
|
||||
|
||||
--
|
||||
-- ACTION CREATE FOREIGN KEY
|
||||
--
|
||||
ALTER TABLE ONLY "card_revision"
|
||||
ADD CONSTRAINT "card_revision_fk_0"
|
||||
FOREIGN KEY("spaceId")
|
||||
REFERENCES "offline_sync_spaces"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
ALTER TABLE ONLY "card_revision"
|
||||
ADD CONSTRAINT "card_revision_fk_1"
|
||||
FOREIGN KEY("cardId")
|
||||
REFERENCES "card"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION
|
||||
DEFERRABLE INITIALLY DEFERRED;
|
||||
|
||||
|
||||
--
|
||||
-- MIGRATION VERSION FOR halkyon_learn
|
||||
--
|
||||
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
|
||||
VALUES ('halkyon_learn', '20260930122235111-cards', now())
|
||||
ON CONFLICT ("module")
|
||||
DO UPDATE SET "version" = '20260930122235111-cards', "timestamp" = now();
|
||||
|
||||
--
|
||||
-- MIGRATION VERSION FOR serverpod
|
||||
--
|
||||
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
|
||||
VALUES ('serverpod', '20260824182259319', now())
|
||||
ON CONFLICT ("module")
|
||||
DO UPDATE SET "version" = '20260824182259319', "timestamp" = now();
|
||||
|
||||
--
|
||||
-- MIGRATION VERSION FOR serverpod_auth_idp
|
||||
--
|
||||
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
|
||||
VALUES ('serverpod_auth_idp', '20260924105404509', now())
|
||||
ON CONFLICT ("module")
|
||||
DO UPDATE SET "version" = '20260924105404509', "timestamp" = now();
|
||||
|
||||
--
|
||||
-- MIGRATION VERSION FOR serverpod_offline_sync
|
||||
--
|
||||
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
|
||||
VALUES ('serverpod_offline_sync', '20260914143806119', now())
|
||||
ON CONFLICT ("module")
|
||||
DO UPDATE SET "version" = '20260914143806119', "timestamp" = now();
|
||||
|
||||
--
|
||||
-- MIGRATION VERSION FOR serverpod_auth_core
|
||||
--
|
||||
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
|
||||
VALUES ('serverpod_auth_core', '20260924105232991', now())
|
||||
ON CONFLICT ("module")
|
||||
DO UPDATE SET "version" = '20260924105232991', "timestamp" = now();
|
||||
|
||||
|
||||
COMMIT;
|
||||
|
|
@ -6,3 +6,4 @@
|
|||
|
||||
20260916165410927
|
||||
20260930094729362-studysets
|
||||
20260930122235111-cards
|
||||
|
|
|
|||
Loading…
Reference in a new issue