refactor(learn): own rows name shared content by its ID alone
ADR 0044, D26. The sync engine links no row of one space to a row of another: a reference across spaces is blanked, on the device and on the server alike (found by experiment, 11 Nov 2026). Reviews, card, note and path tags, PDF readings and path answers are the person's own (D2, D25) but will name cards, notes, paths, steps and PDFs of spaces they share, so those six tables keep their columns without the relation. Every gateway already deletes them as it deletes what they name. The migration drops the nine foreign keys of those tables and adds the three to `tag` again, which Serverpod numbers anew; no column or row changes. The synced tables' hash covers foreign keys, so released apps stop syncing with a server that has it until they update: deploy it together with a release of Learn. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
6b1e9a0226
commit
358d983011
21 changed files with 32974 additions and 1147 deletions
|
|
@ -8,13 +8,10 @@
|
|||
// 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 '../learning_path/learning_path_step.dart' as _i0qbf905;
|
||||
|
||||
/// One answer to a step of a learning path, never changed once written: where a
|
||||
/// person stands in a path isn't stored but derived from these, as a card's
|
||||
|
|
@ -26,7 +23,6 @@ abstract class LearningPathAnswer
|
|||
this.id,
|
||||
this.spaceId,
|
||||
required this.stepId,
|
||||
this.step,
|
||||
required this.answeredAt,
|
||||
required this.outcome,
|
||||
this.answer,
|
||||
|
|
@ -38,7 +34,6 @@ abstract class LearningPathAnswer
|
|||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
required _is.UuidValue stepId,
|
||||
_i0qbf905.LearningPathStep? step,
|
||||
required DateTime answeredAt,
|
||||
required String outcome,
|
||||
String? answer,
|
||||
|
|
@ -53,11 +48,6 @@ abstract class LearningPathAnswer
|
|||
: _is.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
|
||||
spaceId: jsonSerialization['spaceId'] as int?,
|
||||
stepId: _is.UuidValueJsonExtension.fromJson(jsonSerialization['stepId']),
|
||||
step: jsonSerialization['step'] == null
|
||||
? null
|
||||
: _i5lsenq5.Protocol().deserialize<_i0qbf905.LearningPathStep>(
|
||||
jsonSerialization['step'],
|
||||
),
|
||||
answeredAt: _is.DateTimeJsonExtension.fromJson(
|
||||
jsonSerialization['answeredAt'],
|
||||
),
|
||||
|
|
@ -80,10 +70,8 @@ abstract class LearningPathAnswer
|
|||
/// The space owning this row. Maintained by the sync engine.
|
||||
int? spaceId;
|
||||
|
||||
_is.UuidValue stepId;
|
||||
|
||||
/// The step answered.
|
||||
_i0qbf905.LearningPathStep? step;
|
||||
_is.UuidValue stepId;
|
||||
|
||||
/// When, in UTC.
|
||||
DateTime answeredAt;
|
||||
|
|
@ -116,7 +104,6 @@ abstract class LearningPathAnswer
|
|||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
_is.UuidValue? stepId,
|
||||
_i0qbf905.LearningPathStep? step,
|
||||
DateTime? answeredAt,
|
||||
String? outcome,
|
||||
String? answer,
|
||||
|
|
@ -130,7 +117,6 @@ abstract class LearningPathAnswer
|
|||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
'stepId': stepId.toJson(),
|
||||
if (step != null) 'step': step?.toJson(),
|
||||
'answeredAt': answeredAt.toJson(),
|
||||
'outcome': outcome,
|
||||
if (answer != null) 'answer': answer,
|
||||
|
|
@ -146,7 +132,6 @@ abstract class LearningPathAnswer
|
|||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
'stepId': stepId.toJson(),
|
||||
if (step != null) 'step': step?.toJsonForProtocol(),
|
||||
'answeredAt': answeredAt.toJson(),
|
||||
'outcome': outcome,
|
||||
if (answer != null) 'answer': answer,
|
||||
|
|
@ -155,10 +140,8 @@ abstract class LearningPathAnswer
|
|||
};
|
||||
}
|
||||
|
||||
static LearningPathAnswerInclude include({
|
||||
_i0qbf905.LearningPathStepInclude? step,
|
||||
}) {
|
||||
return LearningPathAnswerInclude._(step: step);
|
||||
static LearningPathAnswerInclude include() {
|
||||
return LearningPathAnswerInclude._();
|
||||
}
|
||||
|
||||
static LearningPathAnswerIncludeList includeList({
|
||||
|
|
@ -192,7 +175,6 @@ class _LearningPathAnswerImpl extends LearningPathAnswer {
|
|||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
required _is.UuidValue stepId,
|
||||
_i0qbf905.LearningPathStep? step,
|
||||
required DateTime answeredAt,
|
||||
required String outcome,
|
||||
String? answer,
|
||||
|
|
@ -202,7 +184,6 @@ class _LearningPathAnswerImpl extends LearningPathAnswer {
|
|||
id: id,
|
||||
spaceId: spaceId,
|
||||
stepId: stepId,
|
||||
step: step,
|
||||
answeredAt: answeredAt,
|
||||
outcome: outcome,
|
||||
answer: answer,
|
||||
|
|
@ -218,7 +199,6 @@ class _LearningPathAnswerImpl extends LearningPathAnswer {
|
|||
Object? id = _Undefined,
|
||||
Object? spaceId = _Undefined,
|
||||
_is.UuidValue? stepId,
|
||||
Object? step = _Undefined,
|
||||
DateTime? answeredAt,
|
||||
String? outcome,
|
||||
Object? answer = _Undefined,
|
||||
|
|
@ -229,7 +209,6 @@ class _LearningPathAnswerImpl extends LearningPathAnswer {
|
|||
id: id is _is.UuidValue? ? id : this.id,
|
||||
spaceId: spaceId is int? ? spaceId : this.spaceId,
|
||||
stepId: stepId ?? this.stepId,
|
||||
step: step is _i0qbf905.LearningPathStep? ? step : this.step?.copyWith(),
|
||||
answeredAt: answeredAt ?? this.answeredAt,
|
||||
outcome: outcome ?? this.outcome,
|
||||
answer: answer is String? ? answer : this.answer,
|
||||
|
|
@ -321,10 +300,8 @@ class LearningPathAnswerTable extends _is.Table<_is.UuidValue?> {
|
|||
/// The space owning this row. Maintained by the sync engine.
|
||||
late final _is.ColumnInt spaceId;
|
||||
|
||||
late final _is.ColumnUuid stepId;
|
||||
|
||||
/// The step answered.
|
||||
_i0qbf905.LearningPathStepTable? _step;
|
||||
late final _is.ColumnUuid stepId;
|
||||
|
||||
/// When, in UTC.
|
||||
late final _is.ColumnDateTime answeredAt;
|
||||
|
|
@ -347,19 +324,6 @@ class LearningPathAnswerTable extends _is.Table<_is.UuidValue?> {
|
|||
/// The encryption nonce; null when readable, like keyId.
|
||||
late final _is.ColumnByteData nonce;
|
||||
|
||||
_i0qbf905.LearningPathStepTable get step {
|
||||
if (_step != null) return _step!;
|
||||
_step = _is.createRelationTable(
|
||||
relationFieldName: 'step',
|
||||
field: LearningPathAnswer.t.stepId,
|
||||
foreignField: _i0qbf905.LearningPathStep.t.id,
|
||||
tableRelation: tableRelation,
|
||||
createTable: (foreignTableRelation) =>
|
||||
_i0qbf905.LearningPathStepTable(tableRelation: foreignTableRelation),
|
||||
);
|
||||
return _step!;
|
||||
}
|
||||
|
||||
@override
|
||||
List<_is.Column> get columns => [
|
||||
id,
|
||||
|
|
@ -371,25 +335,13 @@ class LearningPathAnswerTable extends _is.Table<_is.UuidValue?> {
|
|||
keyId,
|
||||
nonce,
|
||||
];
|
||||
|
||||
@override
|
||||
_is.Table? getRelationTable(String relationField) {
|
||||
if (relationField == 'step') {
|
||||
return step;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class LearningPathAnswerInclude extends _is.IncludeObject {
|
||||
LearningPathAnswerInclude._({_i0qbf905.LearningPathStepInclude? step}) {
|
||||
_step = step;
|
||||
}
|
||||
|
||||
_i0qbf905.LearningPathStepInclude? _step;
|
||||
LearningPathAnswerInclude._();
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => {'step': _step};
|
||||
Map<String, _is.Include?> get includes => {};
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => LearningPathAnswer.t;
|
||||
|
|
@ -417,8 +369,6 @@ class LearningPathAnswerIncludeList extends _is.IncludeList {
|
|||
class LearningPathAnswerRepository {
|
||||
const LearningPathAnswerRepository._();
|
||||
|
||||
final attachRow = const LearningPathAnswerAttachRowRepository._();
|
||||
|
||||
/// Returns a list of [LearningPathAnswer]s matching the given query parameters.
|
||||
///
|
||||
/// Use [where] to specify which items to include in the return value.
|
||||
|
|
@ -449,7 +399,6 @@ class LearningPathAnswerRepository {
|
|||
_is.OrderByBuilder<LearningPathAnswerTable>? orderBy,
|
||||
_is.OrderByListBuilder<LearningPathAnswerTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
LearningPathAnswerInclude? include,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
|
|
@ -460,7 +409,6 @@ class LearningPathAnswerRepository {
|
|||
limit: limit,
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
include: include,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
|
|
@ -490,7 +438,6 @@ class LearningPathAnswerRepository {
|
|||
_is.OrderByBuilder<LearningPathAnswerTable>? orderBy,
|
||||
_is.OrderByListBuilder<LearningPathAnswerTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
LearningPathAnswerInclude? include,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
|
|
@ -500,7 +447,6 @@ class LearningPathAnswerRepository {
|
|||
orderByList: orderByList?.call(LearningPathAnswer.t),
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
include: include,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
|
|
@ -511,14 +457,12 @@ class LearningPathAnswerRepository {
|
|||
_is.DatabaseSession session,
|
||||
_is.UuidValue id, {
|
||||
_is.Transaction? transaction,
|
||||
LearningPathAnswerInclude? include,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findById<LearningPathAnswer>(
|
||||
id,
|
||||
transaction: transaction,
|
||||
include: include,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
|
|
@ -818,30 +762,3 @@ class LearningPathAnswerRepository {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LearningPathAnswerAttachRowRepository {
|
||||
const LearningPathAnswerAttachRowRepository._();
|
||||
|
||||
/// Creates a relation between the given [LearningPathAnswer] and [LearningPathStep]
|
||||
/// by setting the [LearningPathAnswer]'s foreign key `stepId` to refer to the [LearningPathStep].
|
||||
Future<void> step(
|
||||
_is.DatabaseSession session,
|
||||
LearningPathAnswer learningPathAnswer,
|
||||
_i0qbf905.LearningPathStep step, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
if (learningPathAnswer.id == null) {
|
||||
throw ArgumentError.notNull('learningPathAnswer.id');
|
||||
}
|
||||
if (step.id == null) {
|
||||
throw ArgumentError.notNull('step.id');
|
||||
}
|
||||
|
||||
var $learningPathAnswer = learningPathAnswer.copyWith(stepId: step.id);
|
||||
await session.db.updateRow<LearningPathAnswer>(
|
||||
$learningPathAnswer,
|
||||
columns: [LearningPathAnswer.t.stepId],
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,12 +8,9 @@
|
|||
// 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 '../pdf_source/pdf_source.dart' as _ijdmmu1w;
|
||||
|
||||
/// How far a PDF has been read, as an entry that is never changed once written:
|
||||
/// the furthest page isn't stored but derived from these, as a card's schedule
|
||||
|
|
@ -26,7 +23,6 @@ abstract class PdfReading
|
|||
this.id,
|
||||
this.spaceId,
|
||||
required this.pdfSourceId,
|
||||
this.pdfSource,
|
||||
required this.readAt,
|
||||
required this.kind,
|
||||
required this.page,
|
||||
|
|
@ -36,7 +32,6 @@ abstract class PdfReading
|
|||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
required _is.UuidValue pdfSourceId,
|
||||
_ijdmmu1w.PdfSource? pdfSource,
|
||||
required DateTime readAt,
|
||||
required String kind,
|
||||
required int page,
|
||||
|
|
@ -51,11 +46,6 @@ abstract class PdfReading
|
|||
pdfSourceId: _is.UuidValueJsonExtension.fromJson(
|
||||
jsonSerialization['pdfSourceId'],
|
||||
),
|
||||
pdfSource: jsonSerialization['pdfSource'] == null
|
||||
? null
|
||||
: _i5lsenq5.Protocol().deserialize<_ijdmmu1w.PdfSource>(
|
||||
jsonSerialization['pdfSource'],
|
||||
),
|
||||
readAt: _is.DateTimeJsonExtension.fromJson(jsonSerialization['readAt']),
|
||||
kind: jsonSerialization['kind'] as String,
|
||||
page: jsonSerialization['page'] as int,
|
||||
|
|
@ -72,10 +62,8 @@ abstract class PdfReading
|
|||
/// The space owning this row. Maintained by the sync engine.
|
||||
int? spaceId;
|
||||
|
||||
_is.UuidValue pdfSourceId;
|
||||
|
||||
/// The PDF read.
|
||||
_ijdmmu1w.PdfSource? pdfSource;
|
||||
_is.UuidValue pdfSourceId;
|
||||
|
||||
/// When, in UTC.
|
||||
DateTime readAt;
|
||||
|
|
@ -98,7 +86,6 @@ abstract class PdfReading
|
|||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
_is.UuidValue? pdfSourceId,
|
||||
_ijdmmu1w.PdfSource? pdfSource,
|
||||
DateTime? readAt,
|
||||
String? kind,
|
||||
int? page,
|
||||
|
|
@ -110,7 +97,6 @@ abstract class PdfReading
|
|||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
'pdfSourceId': pdfSourceId.toJson(),
|
||||
if (pdfSource != null) 'pdfSource': pdfSource?.toJson(),
|
||||
'readAt': readAt.toJson(),
|
||||
'kind': kind,
|
||||
'page': page,
|
||||
|
|
@ -124,15 +110,14 @@ abstract class PdfReading
|
|||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
'pdfSourceId': pdfSourceId.toJson(),
|
||||
if (pdfSource != null) 'pdfSource': pdfSource?.toJsonForProtocol(),
|
||||
'readAt': readAt.toJson(),
|
||||
'kind': kind,
|
||||
'page': page,
|
||||
};
|
||||
}
|
||||
|
||||
static PdfReadingInclude include({_ijdmmu1w.PdfSourceInclude? pdfSource}) {
|
||||
return PdfReadingInclude._(pdfSource: pdfSource);
|
||||
static PdfReadingInclude include() {
|
||||
return PdfReadingInclude._();
|
||||
}
|
||||
|
||||
static PdfReadingIncludeList includeList({
|
||||
|
|
@ -166,7 +151,6 @@ class _PdfReadingImpl extends PdfReading {
|
|||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
required _is.UuidValue pdfSourceId,
|
||||
_ijdmmu1w.PdfSource? pdfSource,
|
||||
required DateTime readAt,
|
||||
required String kind,
|
||||
required int page,
|
||||
|
|
@ -174,7 +158,6 @@ class _PdfReadingImpl extends PdfReading {
|
|||
id: id,
|
||||
spaceId: spaceId,
|
||||
pdfSourceId: pdfSourceId,
|
||||
pdfSource: pdfSource,
|
||||
readAt: readAt,
|
||||
kind: kind,
|
||||
page: page,
|
||||
|
|
@ -188,7 +171,6 @@ class _PdfReadingImpl extends PdfReading {
|
|||
Object? id = _Undefined,
|
||||
Object? spaceId = _Undefined,
|
||||
_is.UuidValue? pdfSourceId,
|
||||
Object? pdfSource = _Undefined,
|
||||
DateTime? readAt,
|
||||
String? kind,
|
||||
int? page,
|
||||
|
|
@ -197,9 +179,6 @@ class _PdfReadingImpl extends PdfReading {
|
|||
id: id is _is.UuidValue? ? id : this.id,
|
||||
spaceId: spaceId is int? ? spaceId : this.spaceId,
|
||||
pdfSourceId: pdfSourceId ?? this.pdfSourceId,
|
||||
pdfSource: pdfSource is _ijdmmu1w.PdfSource?
|
||||
? pdfSource
|
||||
: this.pdfSource?.copyWith(),
|
||||
readAt: readAt ?? this.readAt,
|
||||
kind: kind ?? this.kind,
|
||||
page: page ?? this.page,
|
||||
|
|
@ -268,10 +247,8 @@ class PdfReadingTable extends _is.Table<_is.UuidValue?> {
|
|||
/// The space owning this row. Maintained by the sync engine.
|
||||
late final _is.ColumnInt spaceId;
|
||||
|
||||
late final _is.ColumnUuid pdfSourceId;
|
||||
|
||||
/// The PDF read.
|
||||
_ijdmmu1w.PdfSourceTable? _pdfSource;
|
||||
late final _is.ColumnUuid pdfSourceId;
|
||||
|
||||
/// When, in UTC.
|
||||
late final _is.ColumnDateTime readAt;
|
||||
|
|
@ -284,19 +261,6 @@ class PdfReadingTable extends _is.Table<_is.UuidValue?> {
|
|||
/// The page reached, from 1; 0 for 'reset'.
|
||||
late final _is.ColumnInt page;
|
||||
|
||||
_ijdmmu1w.PdfSourceTable get pdfSource {
|
||||
if (_pdfSource != null) return _pdfSource!;
|
||||
_pdfSource = _is.createRelationTable(
|
||||
relationFieldName: 'pdfSource',
|
||||
field: PdfReading.t.pdfSourceId,
|
||||
foreignField: _ijdmmu1w.PdfSource.t.id,
|
||||
tableRelation: tableRelation,
|
||||
createTable: (foreignTableRelation) =>
|
||||
_ijdmmu1w.PdfSourceTable(tableRelation: foreignTableRelation),
|
||||
);
|
||||
return _pdfSource!;
|
||||
}
|
||||
|
||||
@override
|
||||
List<_is.Column> get columns => [
|
||||
id,
|
||||
|
|
@ -306,25 +270,13 @@ class PdfReadingTable extends _is.Table<_is.UuidValue?> {
|
|||
kind,
|
||||
page,
|
||||
];
|
||||
|
||||
@override
|
||||
_is.Table? getRelationTable(String relationField) {
|
||||
if (relationField == 'pdfSource') {
|
||||
return pdfSource;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class PdfReadingInclude extends _is.IncludeObject {
|
||||
PdfReadingInclude._({_ijdmmu1w.PdfSourceInclude? pdfSource}) {
|
||||
_pdfSource = pdfSource;
|
||||
}
|
||||
|
||||
_ijdmmu1w.PdfSourceInclude? _pdfSource;
|
||||
PdfReadingInclude._();
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => {'pdfSource': _pdfSource};
|
||||
Map<String, _is.Include?> get includes => {};
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => PdfReading.t;
|
||||
|
|
@ -352,8 +304,6 @@ class PdfReadingIncludeList extends _is.IncludeList {
|
|||
class PdfReadingRepository {
|
||||
const PdfReadingRepository._();
|
||||
|
||||
final attachRow = const PdfReadingAttachRowRepository._();
|
||||
|
||||
/// Returns a list of [PdfReading]s matching the given query parameters.
|
||||
///
|
||||
/// Use [where] to specify which items to include in the return value.
|
||||
|
|
@ -384,7 +334,6 @@ class PdfReadingRepository {
|
|||
_is.OrderByBuilder<PdfReadingTable>? orderBy,
|
||||
_is.OrderByListBuilder<PdfReadingTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
PdfReadingInclude? include,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
|
|
@ -395,7 +344,6 @@ class PdfReadingRepository {
|
|||
limit: limit,
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
include: include,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
|
|
@ -425,7 +373,6 @@ class PdfReadingRepository {
|
|||
_is.OrderByBuilder<PdfReadingTable>? orderBy,
|
||||
_is.OrderByListBuilder<PdfReadingTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
PdfReadingInclude? include,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
|
|
@ -435,7 +382,6 @@ class PdfReadingRepository {
|
|||
orderByList: orderByList?.call(PdfReading.t),
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
include: include,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
|
|
@ -446,14 +392,12 @@ class PdfReadingRepository {
|
|||
_is.DatabaseSession session,
|
||||
_is.UuidValue id, {
|
||||
_is.Transaction? transaction,
|
||||
PdfReadingInclude? include,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findById<PdfReading>(
|
||||
id,
|
||||
transaction: transaction,
|
||||
include: include,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
|
|
@ -751,30 +695,3 @@ class PdfReadingRepository {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PdfReadingAttachRowRepository {
|
||||
const PdfReadingAttachRowRepository._();
|
||||
|
||||
/// Creates a relation between the given [PdfReading] and [PdfSource]
|
||||
/// by setting the [PdfReading]'s foreign key `pdfSourceId` to refer to the [PdfSource].
|
||||
Future<void> pdfSource(
|
||||
_is.DatabaseSession session,
|
||||
PdfReading pdfReading,
|
||||
_ijdmmu1w.PdfSource pdfSource, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
if (pdfReading.id == null) {
|
||||
throw ArgumentError.notNull('pdfReading.id');
|
||||
}
|
||||
if (pdfSource.id == null) {
|
||||
throw ArgumentError.notNull('pdfSource.id');
|
||||
}
|
||||
|
||||
var $pdfReading = pdfReading.copyWith(pdfSourceId: pdfSource.id);
|
||||
await session.db.updateRow<PdfReading>(
|
||||
$pdfReading,
|
||||
columns: [PdfReading.t.pdfSourceId],
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -706,17 +706,6 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
),
|
||||
_isp.ForeignKeyDefinition(
|
||||
constraintName: 'card_tag_fk_1',
|
||||
columns: ['cardId'],
|
||||
referenceTable: 'card',
|
||||
referenceTableSchema: 'public',
|
||||
referenceColumns: ['id'],
|
||||
onUpdate: _isp.ForeignKeyAction.noAction,
|
||||
onDelete: _isp.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
deferrable: _isp.DeferrableConstraint.initiallyDeferred,
|
||||
),
|
||||
_isp.ForeignKeyDefinition(
|
||||
constraintName: 'card_tag_fk_2',
|
||||
columns: ['tagId'],
|
||||
referenceTable: 'tag',
|
||||
referenceTableSchema: 'public',
|
||||
|
|
@ -2152,17 +2141,6 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
onDelete: _isp.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
),
|
||||
_isp.ForeignKeyDefinition(
|
||||
constraintName: 'learning_path_answer_fk_1',
|
||||
columns: ['stepId'],
|
||||
referenceTable: 'learning_path_step',
|
||||
referenceTableSchema: 'public',
|
||||
referenceColumns: ['id'],
|
||||
onUpdate: _isp.ForeignKeyAction.noAction,
|
||||
onDelete: _isp.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
deferrable: _isp.DeferrableConstraint.initiallyDeferred,
|
||||
),
|
||||
],
|
||||
indexes: [
|
||||
_isp.IndexDefinition(
|
||||
|
|
@ -2453,17 +2431,6 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
),
|
||||
_isp.ForeignKeyDefinition(
|
||||
constraintName: 'learning_path_tag_fk_1',
|
||||
columns: ['pathId'],
|
||||
referenceTable: 'learning_path',
|
||||
referenceTableSchema: 'public',
|
||||
referenceColumns: ['id'],
|
||||
onUpdate: _isp.ForeignKeyAction.noAction,
|
||||
onDelete: _isp.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
deferrable: _isp.DeferrableConstraint.initiallyDeferred,
|
||||
),
|
||||
_isp.ForeignKeyDefinition(
|
||||
constraintName: 'learning_path_tag_fk_2',
|
||||
columns: ['tagId'],
|
||||
referenceTable: 'tag',
|
||||
referenceTableSchema: 'public',
|
||||
|
|
@ -2766,17 +2733,6 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
),
|
||||
_isp.ForeignKeyDefinition(
|
||||
constraintName: 'note_tag_fk_1',
|
||||
columns: ['noteId'],
|
||||
referenceTable: 'note',
|
||||
referenceTableSchema: 'public',
|
||||
referenceColumns: ['id'],
|
||||
onUpdate: _isp.ForeignKeyAction.noAction,
|
||||
onDelete: _isp.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
deferrable: _isp.DeferrableConstraint.initiallyDeferred,
|
||||
),
|
||||
_isp.ForeignKeyDefinition(
|
||||
constraintName: 'note_tag_fk_2',
|
||||
columns: ['tagId'],
|
||||
referenceTable: 'tag',
|
||||
referenceTableSchema: 'public',
|
||||
|
|
@ -2885,17 +2841,6 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
onDelete: _isp.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
),
|
||||
_isp.ForeignKeyDefinition(
|
||||
constraintName: 'pdf_reading_fk_1',
|
||||
columns: ['pdfSourceId'],
|
||||
referenceTable: 'pdf_source',
|
||||
referenceTableSchema: 'public',
|
||||
referenceColumns: ['id'],
|
||||
onUpdate: _isp.ForeignKeyAction.noAction,
|
||||
onDelete: _isp.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
deferrable: _isp.DeferrableConstraint.initiallyDeferred,
|
||||
),
|
||||
],
|
||||
indexes: [
|
||||
_isp.IndexDefinition(
|
||||
|
|
@ -3277,17 +3222,6 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
onDelete: _isp.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
),
|
||||
_isp.ForeignKeyDefinition(
|
||||
constraintName: 'review_log_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(
|
||||
|
|
|
|||
|
|
@ -8,12 +8,9 @@
|
|||
// 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 '../card/card.dart' as _i4926i81;
|
||||
|
||||
/// One rating of a card, never changed once written: a card's schedule isn't
|
||||
/// stored but derived on each device by replaying its reviews in time order,
|
||||
|
|
@ -25,7 +22,6 @@ abstract class ReviewLog
|
|||
this.id,
|
||||
this.spaceId,
|
||||
required this.cardId,
|
||||
this.card,
|
||||
required this.rating,
|
||||
required this.reviewedAt,
|
||||
required this.durationMs,
|
||||
|
|
@ -36,7 +32,6 @@ abstract class ReviewLog
|
|||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
required _is.UuidValue cardId,
|
||||
_i4926i81.Card? card,
|
||||
required int rating,
|
||||
required DateTime reviewedAt,
|
||||
required int durationMs,
|
||||
|
|
@ -50,11 +45,6 @@ abstract class ReviewLog
|
|||
: _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'],
|
||||
),
|
||||
rating: jsonSerialization['rating'] as int,
|
||||
reviewedAt: _is.DateTimeJsonExtension.fromJson(
|
||||
jsonSerialization['reviewedAt'],
|
||||
|
|
@ -74,10 +64,8 @@ abstract class ReviewLog
|
|||
/// The space owning this row. Maintained by the sync engine.
|
||||
int? spaceId;
|
||||
|
||||
_is.UuidValue cardId;
|
||||
|
||||
/// The card reviewed.
|
||||
_i4926i81.Card? card;
|
||||
_is.UuidValue cardId;
|
||||
|
||||
/// How well it was recalled, FSRS's ratings: 1 again, 2 hard, 3 good,
|
||||
/// 4 easy.
|
||||
|
|
@ -109,7 +97,6 @@ abstract class ReviewLog
|
|||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
_is.UuidValue? cardId,
|
||||
_i4926i81.Card? card,
|
||||
int? rating,
|
||||
DateTime? reviewedAt,
|
||||
int? durationMs,
|
||||
|
|
@ -122,7 +109,6 @@ abstract class ReviewLog
|
|||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
'cardId': cardId.toJson(),
|
||||
if (card != null) 'card': card?.toJson(),
|
||||
'rating': rating,
|
||||
'reviewedAt': reviewedAt.toJson(),
|
||||
'durationMs': durationMs,
|
||||
|
|
@ -137,7 +123,6 @@ abstract class ReviewLog
|
|||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
'cardId': cardId.toJson(),
|
||||
if (card != null) 'card': card?.toJsonForProtocol(),
|
||||
'rating': rating,
|
||||
'reviewedAt': reviewedAt.toJson(),
|
||||
'durationMs': durationMs,
|
||||
|
|
@ -145,8 +130,8 @@ abstract class ReviewLog
|
|||
};
|
||||
}
|
||||
|
||||
static ReviewLogInclude include({_i4926i81.CardInclude? card}) {
|
||||
return ReviewLogInclude._(card: card);
|
||||
static ReviewLogInclude include() {
|
||||
return ReviewLogInclude._();
|
||||
}
|
||||
|
||||
static ReviewLogIncludeList includeList({
|
||||
|
|
@ -180,7 +165,6 @@ class _ReviewLogImpl extends ReviewLog {
|
|||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
required _is.UuidValue cardId,
|
||||
_i4926i81.Card? card,
|
||||
required int rating,
|
||||
required DateTime reviewedAt,
|
||||
required int durationMs,
|
||||
|
|
@ -189,7 +173,6 @@ class _ReviewLogImpl extends ReviewLog {
|
|||
id: id,
|
||||
spaceId: spaceId,
|
||||
cardId: cardId,
|
||||
card: card,
|
||||
rating: rating,
|
||||
reviewedAt: reviewedAt,
|
||||
durationMs: durationMs,
|
||||
|
|
@ -204,7 +187,6 @@ class _ReviewLogImpl extends ReviewLog {
|
|||
Object? id = _Undefined,
|
||||
Object? spaceId = _Undefined,
|
||||
_is.UuidValue? cardId,
|
||||
Object? card = _Undefined,
|
||||
int? rating,
|
||||
DateTime? reviewedAt,
|
||||
int? durationMs,
|
||||
|
|
@ -214,7 +196,6 @@ class _ReviewLogImpl extends ReviewLog {
|
|||
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(),
|
||||
rating: rating ?? this.rating,
|
||||
reviewedAt: reviewedAt ?? this.reviewedAt,
|
||||
durationMs: durationMs ?? this.durationMs,
|
||||
|
|
@ -294,10 +275,8 @@ class ReviewLogTable extends _is.Table<_is.UuidValue?> {
|
|||
/// The space owning this row. Maintained by the sync engine.
|
||||
late final _is.ColumnInt spaceId;
|
||||
|
||||
late final _is.ColumnUuid cardId;
|
||||
|
||||
/// The card reviewed.
|
||||
_i4926i81.CardTable? _card;
|
||||
late final _is.ColumnUuid cardId;
|
||||
|
||||
/// How well it was recalled, FSRS's ratings: 1 again, 2 hard, 3 good,
|
||||
/// 4 easy.
|
||||
|
|
@ -319,19 +298,6 @@ class ReviewLogTable extends _is.Table<_is.UuidValue?> {
|
|||
/// what follows it, its rating means nothing.
|
||||
late final _is.ColumnString mode;
|
||||
|
||||
_i4926i81.CardTable get card {
|
||||
if (_card != null) return _card!;
|
||||
_card = _is.createRelationTable(
|
||||
relationFieldName: 'card',
|
||||
field: ReviewLog.t.cardId,
|
||||
foreignField: _i4926i81.Card.t.id,
|
||||
tableRelation: tableRelation,
|
||||
createTable: (foreignTableRelation) =>
|
||||
_i4926i81.CardTable(tableRelation: foreignTableRelation),
|
||||
);
|
||||
return _card!;
|
||||
}
|
||||
|
||||
@override
|
||||
List<_is.Column> get columns => [
|
||||
id,
|
||||
|
|
@ -342,25 +308,13 @@ class ReviewLogTable extends _is.Table<_is.UuidValue?> {
|
|||
durationMs,
|
||||
mode,
|
||||
];
|
||||
|
||||
@override
|
||||
_is.Table? getRelationTable(String relationField) {
|
||||
if (relationField == 'card') {
|
||||
return card;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class ReviewLogInclude extends _is.IncludeObject {
|
||||
ReviewLogInclude._({_i4926i81.CardInclude? card}) {
|
||||
_card = card;
|
||||
}
|
||||
|
||||
_i4926i81.CardInclude? _card;
|
||||
ReviewLogInclude._();
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => {'card': _card};
|
||||
Map<String, _is.Include?> get includes => {};
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => ReviewLog.t;
|
||||
|
|
@ -388,8 +342,6 @@ class ReviewLogIncludeList extends _is.IncludeList {
|
|||
class ReviewLogRepository {
|
||||
const ReviewLogRepository._();
|
||||
|
||||
final attachRow = const ReviewLogAttachRowRepository._();
|
||||
|
||||
/// Returns a list of [ReviewLog]s matching the given query parameters.
|
||||
///
|
||||
/// Use [where] to specify which items to include in the return value.
|
||||
|
|
@ -420,7 +372,6 @@ class ReviewLogRepository {
|
|||
_is.OrderByBuilder<ReviewLogTable>? orderBy,
|
||||
_is.OrderByListBuilder<ReviewLogTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
ReviewLogInclude? include,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
|
|
@ -431,7 +382,6 @@ class ReviewLogRepository {
|
|||
limit: limit,
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
include: include,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
|
|
@ -461,7 +411,6 @@ class ReviewLogRepository {
|
|||
_is.OrderByBuilder<ReviewLogTable>? orderBy,
|
||||
_is.OrderByListBuilder<ReviewLogTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
ReviewLogInclude? include,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
|
|
@ -471,7 +420,6 @@ class ReviewLogRepository {
|
|||
orderByList: orderByList?.call(ReviewLog.t),
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
include: include,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
|
|
@ -482,14 +430,12 @@ class ReviewLogRepository {
|
|||
_is.DatabaseSession session,
|
||||
_is.UuidValue id, {
|
||||
_is.Transaction? transaction,
|
||||
ReviewLogInclude? include,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findById<ReviewLog>(
|
||||
id,
|
||||
transaction: transaction,
|
||||
include: include,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
|
|
@ -787,30 +733,3 @@ class ReviewLogRepository {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ReviewLogAttachRowRepository {
|
||||
const ReviewLogAttachRowRepository._();
|
||||
|
||||
/// Creates a relation between the given [ReviewLog] and [Card]
|
||||
/// by setting the [ReviewLog]'s foreign key `cardId` to refer to the [Card].
|
||||
Future<void> card(
|
||||
_is.DatabaseSession session,
|
||||
ReviewLog reviewLog,
|
||||
_i4926i81.Card card, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
if (reviewLog.id == null) {
|
||||
throw ArgumentError.notNull('reviewLog.id');
|
||||
}
|
||||
if (card.id == null) {
|
||||
throw ArgumentError.notNull('card.id');
|
||||
}
|
||||
|
||||
var $reviewLog = reviewLog.copyWith(cardId: card.id);
|
||||
await session.db.updateRow<ReviewLog>(
|
||||
$reviewLog,
|
||||
columns: [ReviewLog.t.cardId],
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
// 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 '../card/card.dart' as _i4926i81;
|
||||
import '../tag/tag.dart' as _isqed5gg;
|
||||
|
||||
/// A card having a tag (ADR 0004): one row per assignment, never updated. Removing
|
||||
|
|
@ -26,7 +25,6 @@ abstract class CardTag
|
|||
this.id,
|
||||
this.spaceId,
|
||||
required this.cardId,
|
||||
this.card,
|
||||
required this.tagId,
|
||||
this.tag,
|
||||
});
|
||||
|
|
@ -35,7 +33,6 @@ abstract class CardTag
|
|||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
required _is.UuidValue cardId,
|
||||
_i4926i81.Card? card,
|
||||
required _is.UuidValue tagId,
|
||||
_isqed5gg.Tag? tag,
|
||||
}) = _CardTagImpl;
|
||||
|
|
@ -47,11 +44,6 @@ abstract class CardTag
|
|||
: _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'],
|
||||
),
|
||||
tagId: _is.UuidValueJsonExtension.fromJson(jsonSerialization['tagId']),
|
||||
tag: jsonSerialization['tag'] == null
|
||||
? null
|
||||
|
|
@ -71,10 +63,8 @@ abstract class CardTag
|
|||
/// The space owning this row. Maintained by the sync engine.
|
||||
int? spaceId;
|
||||
|
||||
_is.UuidValue cardId;
|
||||
|
||||
/// The card tagged.
|
||||
_i4926i81.Card? card;
|
||||
_is.UuidValue cardId;
|
||||
|
||||
_is.UuidValue tagId;
|
||||
|
||||
|
|
@ -91,7 +81,6 @@ abstract class CardTag
|
|||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
_is.UuidValue? cardId,
|
||||
_i4926i81.Card? card,
|
||||
_is.UuidValue? tagId,
|
||||
_isqed5gg.Tag? tag,
|
||||
});
|
||||
|
|
@ -102,7 +91,6 @@ abstract class CardTag
|
|||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
'cardId': cardId.toJson(),
|
||||
if (card != null) 'card': card?.toJson(),
|
||||
'tagId': tagId.toJson(),
|
||||
if (tag != null) 'tag': tag?.toJson(),
|
||||
};
|
||||
|
|
@ -115,20 +103,13 @@ abstract class CardTag
|
|||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
'cardId': cardId.toJson(),
|
||||
if (card != null) 'card': card?.toJsonForProtocol(),
|
||||
'tagId': tagId.toJson(),
|
||||
if (tag != null) 'tag': tag?.toJsonForProtocol(),
|
||||
};
|
||||
}
|
||||
|
||||
static CardTagInclude include({
|
||||
_i4926i81.CardInclude? card,
|
||||
_isqed5gg.TagInclude? tag,
|
||||
}) {
|
||||
return CardTagInclude._(
|
||||
card: card,
|
||||
tag: tag,
|
||||
);
|
||||
static CardTagInclude include({_isqed5gg.TagInclude? tag}) {
|
||||
return CardTagInclude._(tag: tag);
|
||||
}
|
||||
|
||||
static CardTagIncludeList includeList({
|
||||
|
|
@ -162,14 +143,12 @@ class _CardTagImpl extends CardTag {
|
|||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
required _is.UuidValue cardId,
|
||||
_i4926i81.Card? card,
|
||||
required _is.UuidValue tagId,
|
||||
_isqed5gg.Tag? tag,
|
||||
}) : super._(
|
||||
id: id,
|
||||
spaceId: spaceId,
|
||||
cardId: cardId,
|
||||
card: card,
|
||||
tagId: tagId,
|
||||
tag: tag,
|
||||
);
|
||||
|
|
@ -182,7 +161,6 @@ class _CardTagImpl extends CardTag {
|
|||
Object? id = _Undefined,
|
||||
Object? spaceId = _Undefined,
|
||||
_is.UuidValue? cardId,
|
||||
Object? card = _Undefined,
|
||||
_is.UuidValue? tagId,
|
||||
Object? tag = _Undefined,
|
||||
}) {
|
||||
|
|
@ -190,7 +168,6 @@ class _CardTagImpl extends CardTag {
|
|||
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(),
|
||||
tagId: tagId ?? this.tagId,
|
||||
tag: tag is _isqed5gg.Tag? ? tag : this.tag?.copyWith(),
|
||||
);
|
||||
|
|
@ -240,29 +217,14 @@ class CardTagTable extends _is.Table<_is.UuidValue?> {
|
|||
/// The space owning this row. Maintained by the sync engine.
|
||||
late final _is.ColumnInt spaceId;
|
||||
|
||||
late final _is.ColumnUuid cardId;
|
||||
|
||||
/// The card tagged.
|
||||
_i4926i81.CardTable? _card;
|
||||
late final _is.ColumnUuid cardId;
|
||||
|
||||
late final _is.ColumnUuid tagId;
|
||||
|
||||
/// The tag it has.
|
||||
_isqed5gg.TagTable? _tag;
|
||||
|
||||
_i4926i81.CardTable get card {
|
||||
if (_card != null) return _card!;
|
||||
_card = _is.createRelationTable(
|
||||
relationFieldName: 'card',
|
||||
field: CardTag.t.cardId,
|
||||
foreignField: _i4926i81.Card.t.id,
|
||||
tableRelation: tableRelation,
|
||||
createTable: (foreignTableRelation) =>
|
||||
_i4926i81.CardTable(tableRelation: foreignTableRelation),
|
||||
);
|
||||
return _card!;
|
||||
}
|
||||
|
||||
_isqed5gg.TagTable get tag {
|
||||
if (_tag != null) return _tag!;
|
||||
_tag = _is.createRelationTable(
|
||||
|
|
@ -286,9 +248,6 @@ class CardTagTable extends _is.Table<_is.UuidValue?> {
|
|||
|
||||
@override
|
||||
_is.Table? getRelationTable(String relationField) {
|
||||
if (relationField == 'card') {
|
||||
return card;
|
||||
}
|
||||
if (relationField == 'tag') {
|
||||
return tag;
|
||||
}
|
||||
|
|
@ -297,23 +256,14 @@ class CardTagTable extends _is.Table<_is.UuidValue?> {
|
|||
}
|
||||
|
||||
class CardTagInclude extends _is.IncludeObject {
|
||||
CardTagInclude._({
|
||||
_i4926i81.CardInclude? card,
|
||||
_isqed5gg.TagInclude? tag,
|
||||
}) {
|
||||
_card = card;
|
||||
CardTagInclude._({_isqed5gg.TagInclude? tag}) {
|
||||
_tag = tag;
|
||||
}
|
||||
|
||||
_i4926i81.CardInclude? _card;
|
||||
|
||||
_isqed5gg.TagInclude? _tag;
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => {
|
||||
'card': _card,
|
||||
'tag': _tag,
|
||||
};
|
||||
Map<String, _is.Include?> get includes => {'tag': _tag};
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => CardTag.t;
|
||||
|
|
@ -744,29 +694,6 @@ class CardTagRepository {
|
|||
class CardTagAttachRowRepository {
|
||||
const CardTagAttachRowRepository._();
|
||||
|
||||
/// Creates a relation between the given [CardTag] and [Card]
|
||||
/// by setting the [CardTag]'s foreign key `cardId` to refer to the [Card].
|
||||
Future<void> card(
|
||||
_is.DatabaseSession session,
|
||||
CardTag cardTag,
|
||||
_i4926i81.Card card, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
if (cardTag.id == null) {
|
||||
throw ArgumentError.notNull('cardTag.id');
|
||||
}
|
||||
if (card.id == null) {
|
||||
throw ArgumentError.notNull('card.id');
|
||||
}
|
||||
|
||||
var $cardTag = cardTag.copyWith(cardId: card.id);
|
||||
await session.db.updateRow<CardTag>(
|
||||
$cardTag,
|
||||
columns: [CardTag.t.cardId],
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Creates a relation between the given [CardTag] and [Tag]
|
||||
/// by setting the [CardTag]'s foreign key `tagId` to refer to the [Tag].
|
||||
Future<void> tag(
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
// 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 '../learning_path/learning_path.dart' as _ixd0fbzl;
|
||||
import '../tag/tag.dart' as _isqed5gg;
|
||||
|
||||
/// A learning path having a tag, as CardTag is for cards (ADR 0004): one row per
|
||||
|
|
@ -26,7 +25,6 @@ abstract class LearningPathTag
|
|||
this.id,
|
||||
this.spaceId,
|
||||
required this.pathId,
|
||||
this.path,
|
||||
required this.tagId,
|
||||
this.tag,
|
||||
});
|
||||
|
|
@ -35,7 +33,6 @@ abstract class LearningPathTag
|
|||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
required _is.UuidValue pathId,
|
||||
_ixd0fbzl.LearningPath? path,
|
||||
required _is.UuidValue tagId,
|
||||
_isqed5gg.Tag? tag,
|
||||
}) = _LearningPathTagImpl;
|
||||
|
|
@ -47,11 +44,6 @@ abstract class LearningPathTag
|
|||
: _is.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
|
||||
spaceId: jsonSerialization['spaceId'] as int?,
|
||||
pathId: _is.UuidValueJsonExtension.fromJson(jsonSerialization['pathId']),
|
||||
path: jsonSerialization['path'] == null
|
||||
? null
|
||||
: _i5lsenq5.Protocol().deserialize<_ixd0fbzl.LearningPath>(
|
||||
jsonSerialization['path'],
|
||||
),
|
||||
tagId: _is.UuidValueJsonExtension.fromJson(jsonSerialization['tagId']),
|
||||
tag: jsonSerialization['tag'] == null
|
||||
? null
|
||||
|
|
@ -71,10 +63,8 @@ abstract class LearningPathTag
|
|||
/// The space owning this row. Maintained by the sync engine.
|
||||
int? spaceId;
|
||||
|
||||
_is.UuidValue pathId;
|
||||
|
||||
/// The path tagged.
|
||||
_ixd0fbzl.LearningPath? path;
|
||||
_is.UuidValue pathId;
|
||||
|
||||
_is.UuidValue tagId;
|
||||
|
||||
|
|
@ -91,7 +81,6 @@ abstract class LearningPathTag
|
|||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
_is.UuidValue? pathId,
|
||||
_ixd0fbzl.LearningPath? path,
|
||||
_is.UuidValue? tagId,
|
||||
_isqed5gg.Tag? tag,
|
||||
});
|
||||
|
|
@ -102,7 +91,6 @@ abstract class LearningPathTag
|
|||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
'pathId': pathId.toJson(),
|
||||
if (path != null) 'path': path?.toJson(),
|
||||
'tagId': tagId.toJson(),
|
||||
if (tag != null) 'tag': tag?.toJson(),
|
||||
};
|
||||
|
|
@ -115,20 +103,13 @@ abstract class LearningPathTag
|
|||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
'pathId': pathId.toJson(),
|
||||
if (path != null) 'path': path?.toJsonForProtocol(),
|
||||
'tagId': tagId.toJson(),
|
||||
if (tag != null) 'tag': tag?.toJsonForProtocol(),
|
||||
};
|
||||
}
|
||||
|
||||
static LearningPathTagInclude include({
|
||||
_ixd0fbzl.LearningPathInclude? path,
|
||||
_isqed5gg.TagInclude? tag,
|
||||
}) {
|
||||
return LearningPathTagInclude._(
|
||||
path: path,
|
||||
tag: tag,
|
||||
);
|
||||
static LearningPathTagInclude include({_isqed5gg.TagInclude? tag}) {
|
||||
return LearningPathTagInclude._(tag: tag);
|
||||
}
|
||||
|
||||
static LearningPathTagIncludeList includeList({
|
||||
|
|
@ -162,14 +143,12 @@ class _LearningPathTagImpl extends LearningPathTag {
|
|||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
required _is.UuidValue pathId,
|
||||
_ixd0fbzl.LearningPath? path,
|
||||
required _is.UuidValue tagId,
|
||||
_isqed5gg.Tag? tag,
|
||||
}) : super._(
|
||||
id: id,
|
||||
spaceId: spaceId,
|
||||
pathId: pathId,
|
||||
path: path,
|
||||
tagId: tagId,
|
||||
tag: tag,
|
||||
);
|
||||
|
|
@ -182,7 +161,6 @@ class _LearningPathTagImpl extends LearningPathTag {
|
|||
Object? id = _Undefined,
|
||||
Object? spaceId = _Undefined,
|
||||
_is.UuidValue? pathId,
|
||||
Object? path = _Undefined,
|
||||
_is.UuidValue? tagId,
|
||||
Object? tag = _Undefined,
|
||||
}) {
|
||||
|
|
@ -190,7 +168,6 @@ class _LearningPathTagImpl extends LearningPathTag {
|
|||
id: id is _is.UuidValue? ? id : this.id,
|
||||
spaceId: spaceId is int? ? spaceId : this.spaceId,
|
||||
pathId: pathId ?? this.pathId,
|
||||
path: path is _ixd0fbzl.LearningPath? ? path : this.path?.copyWith(),
|
||||
tagId: tagId ?? this.tagId,
|
||||
tag: tag is _isqed5gg.Tag? ? tag : this.tag?.copyWith(),
|
||||
);
|
||||
|
|
@ -241,29 +218,14 @@ class LearningPathTagTable extends _is.Table<_is.UuidValue?> {
|
|||
/// The space owning this row. Maintained by the sync engine.
|
||||
late final _is.ColumnInt spaceId;
|
||||
|
||||
late final _is.ColumnUuid pathId;
|
||||
|
||||
/// The path tagged.
|
||||
_ixd0fbzl.LearningPathTable? _path;
|
||||
late final _is.ColumnUuid pathId;
|
||||
|
||||
late final _is.ColumnUuid tagId;
|
||||
|
||||
/// The tag it has.
|
||||
_isqed5gg.TagTable? _tag;
|
||||
|
||||
_ixd0fbzl.LearningPathTable get path {
|
||||
if (_path != null) return _path!;
|
||||
_path = _is.createRelationTable(
|
||||
relationFieldName: 'path',
|
||||
field: LearningPathTag.t.pathId,
|
||||
foreignField: _ixd0fbzl.LearningPath.t.id,
|
||||
tableRelation: tableRelation,
|
||||
createTable: (foreignTableRelation) =>
|
||||
_ixd0fbzl.LearningPathTable(tableRelation: foreignTableRelation),
|
||||
);
|
||||
return _path!;
|
||||
}
|
||||
|
||||
_isqed5gg.TagTable get tag {
|
||||
if (_tag != null) return _tag!;
|
||||
_tag = _is.createRelationTable(
|
||||
|
|
@ -287,9 +249,6 @@ class LearningPathTagTable extends _is.Table<_is.UuidValue?> {
|
|||
|
||||
@override
|
||||
_is.Table? getRelationTable(String relationField) {
|
||||
if (relationField == 'path') {
|
||||
return path;
|
||||
}
|
||||
if (relationField == 'tag') {
|
||||
return tag;
|
||||
}
|
||||
|
|
@ -298,23 +257,14 @@ class LearningPathTagTable extends _is.Table<_is.UuidValue?> {
|
|||
}
|
||||
|
||||
class LearningPathTagInclude extends _is.IncludeObject {
|
||||
LearningPathTagInclude._({
|
||||
_ixd0fbzl.LearningPathInclude? path,
|
||||
_isqed5gg.TagInclude? tag,
|
||||
}) {
|
||||
_path = path;
|
||||
LearningPathTagInclude._({_isqed5gg.TagInclude? tag}) {
|
||||
_tag = tag;
|
||||
}
|
||||
|
||||
_ixd0fbzl.LearningPathInclude? _path;
|
||||
|
||||
_isqed5gg.TagInclude? _tag;
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => {
|
||||
'path': _path,
|
||||
'tag': _tag,
|
||||
};
|
||||
Map<String, _is.Include?> get includes => {'tag': _tag};
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => LearningPathTag.t;
|
||||
|
|
@ -747,29 +697,6 @@ class LearningPathTagRepository {
|
|||
class LearningPathTagAttachRowRepository {
|
||||
const LearningPathTagAttachRowRepository._();
|
||||
|
||||
/// Creates a relation between the given [LearningPathTag] and [LearningPath]
|
||||
/// by setting the [LearningPathTag]'s foreign key `pathId` to refer to the [LearningPath].
|
||||
Future<void> path(
|
||||
_is.DatabaseSession session,
|
||||
LearningPathTag learningPathTag,
|
||||
_ixd0fbzl.LearningPath path, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
if (learningPathTag.id == null) {
|
||||
throw ArgumentError.notNull('learningPathTag.id');
|
||||
}
|
||||
if (path.id == null) {
|
||||
throw ArgumentError.notNull('path.id');
|
||||
}
|
||||
|
||||
var $learningPathTag = learningPathTag.copyWith(pathId: path.id);
|
||||
await session.db.updateRow<LearningPathTag>(
|
||||
$learningPathTag,
|
||||
columns: [LearningPathTag.t.pathId],
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Creates a relation between the given [LearningPathTag] and [Tag]
|
||||
/// by setting the [LearningPathTag]'s foreign key `tagId` to refer to the [Tag].
|
||||
Future<void> tag(
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
// 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 '../note/note.dart' as _iu6jc5sx;
|
||||
import '../tag/tag.dart' as _isqed5gg;
|
||||
|
||||
/// A note having a tag, as CardTag is for cards (ADR 0004): one row per
|
||||
|
|
@ -26,7 +25,6 @@ abstract class NoteTag
|
|||
this.id,
|
||||
this.spaceId,
|
||||
required this.noteId,
|
||||
this.note,
|
||||
required this.tagId,
|
||||
this.tag,
|
||||
});
|
||||
|
|
@ -35,7 +33,6 @@ abstract class NoteTag
|
|||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
required _is.UuidValue noteId,
|
||||
_iu6jc5sx.Note? note,
|
||||
required _is.UuidValue tagId,
|
||||
_isqed5gg.Tag? tag,
|
||||
}) = _NoteTagImpl;
|
||||
|
|
@ -47,11 +44,6 @@ abstract class NoteTag
|
|||
: _is.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
|
||||
spaceId: jsonSerialization['spaceId'] as int?,
|
||||
noteId: _is.UuidValueJsonExtension.fromJson(jsonSerialization['noteId']),
|
||||
note: jsonSerialization['note'] == null
|
||||
? null
|
||||
: _i5lsenq5.Protocol().deserialize<_iu6jc5sx.Note>(
|
||||
jsonSerialization['note'],
|
||||
),
|
||||
tagId: _is.UuidValueJsonExtension.fromJson(jsonSerialization['tagId']),
|
||||
tag: jsonSerialization['tag'] == null
|
||||
? null
|
||||
|
|
@ -71,10 +63,8 @@ abstract class NoteTag
|
|||
/// The space owning this row. Maintained by the sync engine.
|
||||
int? spaceId;
|
||||
|
||||
_is.UuidValue noteId;
|
||||
|
||||
/// The note tagged.
|
||||
_iu6jc5sx.Note? note;
|
||||
_is.UuidValue noteId;
|
||||
|
||||
_is.UuidValue tagId;
|
||||
|
||||
|
|
@ -91,7 +81,6 @@ abstract class NoteTag
|
|||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
_is.UuidValue? noteId,
|
||||
_iu6jc5sx.Note? note,
|
||||
_is.UuidValue? tagId,
|
||||
_isqed5gg.Tag? tag,
|
||||
});
|
||||
|
|
@ -102,7 +91,6 @@ abstract class NoteTag
|
|||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
'noteId': noteId.toJson(),
|
||||
if (note != null) 'note': note?.toJson(),
|
||||
'tagId': tagId.toJson(),
|
||||
if (tag != null) 'tag': tag?.toJson(),
|
||||
};
|
||||
|
|
@ -115,20 +103,13 @@ abstract class NoteTag
|
|||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
'noteId': noteId.toJson(),
|
||||
if (note != null) 'note': note?.toJsonForProtocol(),
|
||||
'tagId': tagId.toJson(),
|
||||
if (tag != null) 'tag': tag?.toJsonForProtocol(),
|
||||
};
|
||||
}
|
||||
|
||||
static NoteTagInclude include({
|
||||
_iu6jc5sx.NoteInclude? note,
|
||||
_isqed5gg.TagInclude? tag,
|
||||
}) {
|
||||
return NoteTagInclude._(
|
||||
note: note,
|
||||
tag: tag,
|
||||
);
|
||||
static NoteTagInclude include({_isqed5gg.TagInclude? tag}) {
|
||||
return NoteTagInclude._(tag: tag);
|
||||
}
|
||||
|
||||
static NoteTagIncludeList includeList({
|
||||
|
|
@ -162,14 +143,12 @@ class _NoteTagImpl extends NoteTag {
|
|||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
required _is.UuidValue noteId,
|
||||
_iu6jc5sx.Note? note,
|
||||
required _is.UuidValue tagId,
|
||||
_isqed5gg.Tag? tag,
|
||||
}) : super._(
|
||||
id: id,
|
||||
spaceId: spaceId,
|
||||
noteId: noteId,
|
||||
note: note,
|
||||
tagId: tagId,
|
||||
tag: tag,
|
||||
);
|
||||
|
|
@ -182,7 +161,6 @@ class _NoteTagImpl extends NoteTag {
|
|||
Object? id = _Undefined,
|
||||
Object? spaceId = _Undefined,
|
||||
_is.UuidValue? noteId,
|
||||
Object? note = _Undefined,
|
||||
_is.UuidValue? tagId,
|
||||
Object? tag = _Undefined,
|
||||
}) {
|
||||
|
|
@ -190,7 +168,6 @@ class _NoteTagImpl extends NoteTag {
|
|||
id: id is _is.UuidValue? ? id : this.id,
|
||||
spaceId: spaceId is int? ? spaceId : this.spaceId,
|
||||
noteId: noteId ?? this.noteId,
|
||||
note: note is _iu6jc5sx.Note? ? note : this.note?.copyWith(),
|
||||
tagId: tagId ?? this.tagId,
|
||||
tag: tag is _isqed5gg.Tag? ? tag : this.tag?.copyWith(),
|
||||
);
|
||||
|
|
@ -240,29 +217,14 @@ class NoteTagTable extends _is.Table<_is.UuidValue?> {
|
|||
/// The space owning this row. Maintained by the sync engine.
|
||||
late final _is.ColumnInt spaceId;
|
||||
|
||||
late final _is.ColumnUuid noteId;
|
||||
|
||||
/// The note tagged.
|
||||
_iu6jc5sx.NoteTable? _note;
|
||||
late final _is.ColumnUuid noteId;
|
||||
|
||||
late final _is.ColumnUuid tagId;
|
||||
|
||||
/// The tag it has.
|
||||
_isqed5gg.TagTable? _tag;
|
||||
|
||||
_iu6jc5sx.NoteTable get note {
|
||||
if (_note != null) return _note!;
|
||||
_note = _is.createRelationTable(
|
||||
relationFieldName: 'note',
|
||||
field: NoteTag.t.noteId,
|
||||
foreignField: _iu6jc5sx.Note.t.id,
|
||||
tableRelation: tableRelation,
|
||||
createTable: (foreignTableRelation) =>
|
||||
_iu6jc5sx.NoteTable(tableRelation: foreignTableRelation),
|
||||
);
|
||||
return _note!;
|
||||
}
|
||||
|
||||
_isqed5gg.TagTable get tag {
|
||||
if (_tag != null) return _tag!;
|
||||
_tag = _is.createRelationTable(
|
||||
|
|
@ -286,9 +248,6 @@ class NoteTagTable extends _is.Table<_is.UuidValue?> {
|
|||
|
||||
@override
|
||||
_is.Table? getRelationTable(String relationField) {
|
||||
if (relationField == 'note') {
|
||||
return note;
|
||||
}
|
||||
if (relationField == 'tag') {
|
||||
return tag;
|
||||
}
|
||||
|
|
@ -297,23 +256,14 @@ class NoteTagTable extends _is.Table<_is.UuidValue?> {
|
|||
}
|
||||
|
||||
class NoteTagInclude extends _is.IncludeObject {
|
||||
NoteTagInclude._({
|
||||
_iu6jc5sx.NoteInclude? note,
|
||||
_isqed5gg.TagInclude? tag,
|
||||
}) {
|
||||
_note = note;
|
||||
NoteTagInclude._({_isqed5gg.TagInclude? tag}) {
|
||||
_tag = tag;
|
||||
}
|
||||
|
||||
_iu6jc5sx.NoteInclude? _note;
|
||||
|
||||
_isqed5gg.TagInclude? _tag;
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => {
|
||||
'note': _note,
|
||||
'tag': _tag,
|
||||
};
|
||||
Map<String, _is.Include?> get includes => {'tag': _tag};
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => NoteTag.t;
|
||||
|
|
@ -744,29 +694,6 @@ class NoteTagRepository {
|
|||
class NoteTagAttachRowRepository {
|
||||
const NoteTagAttachRowRepository._();
|
||||
|
||||
/// Creates a relation between the given [NoteTag] and [Note]
|
||||
/// by setting the [NoteTag]'s foreign key `noteId` to refer to the [Note].
|
||||
Future<void> note(
|
||||
_is.DatabaseSession session,
|
||||
NoteTag noteTag,
|
||||
_iu6jc5sx.Note note, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
if (noteTag.id == null) {
|
||||
throw ArgumentError.notNull('noteTag.id');
|
||||
}
|
||||
if (note.id == null) {
|
||||
throw ArgumentError.notNull('note.id');
|
||||
}
|
||||
|
||||
var $noteTag = noteTag.copyWith(noteId: note.id);
|
||||
await session.db.updateRow<NoteTag>(
|
||||
$noteTag,
|
||||
columns: [NoteTag.t.noteId],
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Creates a relation between the given [NoteTag] and [Tag]
|
||||
/// by setting the [NoteTag]'s foreign key `tagId` to refer to the [Tag].
|
||||
Future<void> tag(
|
||||
|
|
|
|||
|
|
@ -7,7 +7,11 @@ table: learning_path_answer
|
|||
database: sync
|
||||
fields:
|
||||
### The step answered.
|
||||
step: LearningPathStep?, relation(onDelete=Cascade, deferred)
|
||||
# The step it names, by its ID alone, without a relation (Halkyon's ADR
|
||||
# 0044, D26): this row is the person's own, and the step may be in a
|
||||
# space they share, which the sync engine never links into. Deleting the
|
||||
# step deletes this row on the device that deletes it.
|
||||
stepId: UuidValue
|
||||
### When, in UTC.
|
||||
answeredAt: DateTime
|
||||
### 'right' or 'wrong' for an exercise; 'continued' for an info step passed;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,11 @@ table: pdf_reading
|
|||
database: sync
|
||||
fields:
|
||||
### The PDF read.
|
||||
pdfSource: PdfSource?, relation(onDelete=Cascade, deferred)
|
||||
# The PDF it names, by its ID alone, without a relation (Halkyon's ADR
|
||||
# 0044, D26): this row is the person's own, and the PDF may be in a
|
||||
# space they share, which the sync engine never links into. Deleting the
|
||||
# PDF deletes this row on the device that deletes it.
|
||||
pdfSourceId: UuidValue
|
||||
### When, in UTC.
|
||||
readAt: DateTime
|
||||
### 'reached' for a page reached (marking the PDF finished reaches its last
|
||||
|
|
|
|||
|
|
@ -7,7 +7,11 @@ table: review_log
|
|||
database: sync
|
||||
fields:
|
||||
### The card reviewed.
|
||||
card: Card?, relation(onDelete=Cascade, deferred)
|
||||
# The card it names, by its ID alone, without a relation (Halkyon's ADR
|
||||
# 0044, D26): this row is the person's own, and the card may be in a
|
||||
# space they share, which the sync engine never links into. Deleting the
|
||||
# card deletes this row on the device that deletes it.
|
||||
cardId: UuidValue
|
||||
### How well it was recalled, FSRS's ratings: 1 again, 2 hard, 3 good,
|
||||
### 4 easy.
|
||||
rating: int
|
||||
|
|
|
|||
|
|
@ -7,7 +7,11 @@ table: card_tag
|
|||
database: sync
|
||||
fields:
|
||||
### The card tagged.
|
||||
card: Card?, relation(onDelete=Cascade, deferred)
|
||||
# The card it names, by its ID alone, without a relation (Halkyon's ADR
|
||||
# 0044, D26): this row is the person's own, and the card may be in a
|
||||
# space they share, which the sync engine never links into. Deleting the
|
||||
# card deletes this row on the device that deletes it.
|
||||
cardId: UuidValue
|
||||
### The tag it has.
|
||||
tag: Tag?, relation(onDelete=Cascade, deferred)
|
||||
indexes:
|
||||
|
|
|
|||
|
|
@ -7,7 +7,11 @@ table: learning_path_tag
|
|||
database: sync
|
||||
fields:
|
||||
### The path tagged.
|
||||
path: LearningPath?, relation(onDelete=Cascade, deferred)
|
||||
# The learning path it names, by its ID alone, without a relation (Halkyon's ADR
|
||||
# 0044, D26): this row is the person's own, and the learning path may be in a
|
||||
# space they share, which the sync engine never links into. Deleting the
|
||||
# learning path deletes this row on the device that deletes it.
|
||||
pathId: UuidValue
|
||||
### The tag it has.
|
||||
tag: Tag?, relation(onDelete=Cascade, deferred)
|
||||
indexes:
|
||||
|
|
|
|||
|
|
@ -7,7 +7,11 @@ table: note_tag
|
|||
database: sync
|
||||
fields:
|
||||
### The note tagged.
|
||||
note: Note?, relation(onDelete=Cascade, deferred)
|
||||
# The note it names, by its ID alone, without a relation (Halkyon's ADR
|
||||
# 0044, D26): this row is the person's own, and the note may be in a
|
||||
# space they share, which the sync engine never links into. Deleting the
|
||||
# note deletes this row on the device that deletes it.
|
||||
noteId: UuidValue
|
||||
### The tag it has.
|
||||
tag: Tag?, relation(onDelete=Cascade, deferred)
|
||||
indexes:
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,172 @@
|
|||
{
|
||||
"__className__": "serverpod.DatabaseMigration",
|
||||
"actions": [
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationAction",
|
||||
"type": "alterTable",
|
||||
"alterTable": {
|
||||
"__className__": "serverpod.TableMigration",
|
||||
"name": "card_tag",
|
||||
"schema": "public",
|
||||
"addColumns": [],
|
||||
"deleteColumns": [],
|
||||
"modifyColumns": [],
|
||||
"addIndexes": [],
|
||||
"deleteIndexes": [],
|
||||
"addForeignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "card_tag_fk_1",
|
||||
"columns": [
|
||||
"tagId"
|
||||
],
|
||||
"referenceTable": "tag",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4,
|
||||
"deferrable": "initiallyDeferred"
|
||||
}
|
||||
],
|
||||
"deleteForeignKeys": [
|
||||
"card_tag_fk_2",
|
||||
"card_tag_fk_1"
|
||||
],
|
||||
"warnings": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationAction",
|
||||
"type": "alterTable",
|
||||
"alterTable": {
|
||||
"__className__": "serverpod.TableMigration",
|
||||
"name": "learning_path_answer",
|
||||
"schema": "public",
|
||||
"addColumns": [],
|
||||
"deleteColumns": [],
|
||||
"modifyColumns": [],
|
||||
"addIndexes": [],
|
||||
"deleteIndexes": [],
|
||||
"addForeignKeys": [],
|
||||
"deleteForeignKeys": [
|
||||
"learning_path_answer_fk_1"
|
||||
],
|
||||
"warnings": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationAction",
|
||||
"type": "alterTable",
|
||||
"alterTable": {
|
||||
"__className__": "serverpod.TableMigration",
|
||||
"name": "learning_path_tag",
|
||||
"schema": "public",
|
||||
"addColumns": [],
|
||||
"deleteColumns": [],
|
||||
"modifyColumns": [],
|
||||
"addIndexes": [],
|
||||
"deleteIndexes": [],
|
||||
"addForeignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "learning_path_tag_fk_1",
|
||||
"columns": [
|
||||
"tagId"
|
||||
],
|
||||
"referenceTable": "tag",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4,
|
||||
"deferrable": "initiallyDeferred"
|
||||
}
|
||||
],
|
||||
"deleteForeignKeys": [
|
||||
"learning_path_tag_fk_2",
|
||||
"learning_path_tag_fk_1"
|
||||
],
|
||||
"warnings": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationAction",
|
||||
"type": "alterTable",
|
||||
"alterTable": {
|
||||
"__className__": "serverpod.TableMigration",
|
||||
"name": "note_tag",
|
||||
"schema": "public",
|
||||
"addColumns": [],
|
||||
"deleteColumns": [],
|
||||
"modifyColumns": [],
|
||||
"addIndexes": [],
|
||||
"deleteIndexes": [],
|
||||
"addForeignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "note_tag_fk_1",
|
||||
"columns": [
|
||||
"tagId"
|
||||
],
|
||||
"referenceTable": "tag",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4,
|
||||
"deferrable": "initiallyDeferred"
|
||||
}
|
||||
],
|
||||
"deleteForeignKeys": [
|
||||
"note_tag_fk_2",
|
||||
"note_tag_fk_1"
|
||||
],
|
||||
"warnings": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationAction",
|
||||
"type": "alterTable",
|
||||
"alterTable": {
|
||||
"__className__": "serverpod.TableMigration",
|
||||
"name": "pdf_reading",
|
||||
"schema": "public",
|
||||
"addColumns": [],
|
||||
"deleteColumns": [],
|
||||
"modifyColumns": [],
|
||||
"addIndexes": [],
|
||||
"deleteIndexes": [],
|
||||
"addForeignKeys": [],
|
||||
"deleteForeignKeys": [
|
||||
"pdf_reading_fk_1"
|
||||
],
|
||||
"warnings": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationAction",
|
||||
"type": "alterTable",
|
||||
"alterTable": {
|
||||
"__className__": "serverpod.TableMigration",
|
||||
"name": "review_log",
|
||||
"schema": "public",
|
||||
"addColumns": [],
|
||||
"deleteColumns": [],
|
||||
"modifyColumns": [],
|
||||
"addIndexes": [],
|
||||
"deleteIndexes": [],
|
||||
"addForeignKeys": [],
|
||||
"deleteForeignKeys": [
|
||||
"review_log_fk_1"
|
||||
],
|
||||
"warnings": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"warnings": [],
|
||||
"migrationApiVersion": 1
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
BEGIN;
|
||||
|
||||
--
|
||||
-- ACTION ALTER TABLE
|
||||
--
|
||||
ALTER TABLE "card_tag" DROP CONSTRAINT IF EXISTS "card_tag_fk_2";
|
||||
ALTER TABLE "card_tag" DROP CONSTRAINT IF EXISTS "card_tag_fk_1";
|
||||
--
|
||||
-- ACTION ALTER TABLE
|
||||
--
|
||||
ALTER TABLE "learning_path_answer" DROP CONSTRAINT IF EXISTS "learning_path_answer_fk_1";
|
||||
--
|
||||
-- ACTION ALTER TABLE
|
||||
--
|
||||
ALTER TABLE "learning_path_tag" DROP CONSTRAINT IF EXISTS "learning_path_tag_fk_2";
|
||||
ALTER TABLE "learning_path_tag" DROP CONSTRAINT IF EXISTS "learning_path_tag_fk_1";
|
||||
--
|
||||
-- ACTION ALTER TABLE
|
||||
--
|
||||
ALTER TABLE "note_tag" DROP CONSTRAINT IF EXISTS "note_tag_fk_2";
|
||||
ALTER TABLE "note_tag" DROP CONSTRAINT IF EXISTS "note_tag_fk_1";
|
||||
--
|
||||
-- ACTION ALTER TABLE
|
||||
--
|
||||
ALTER TABLE "pdf_reading" DROP CONSTRAINT IF EXISTS "pdf_reading_fk_1";
|
||||
--
|
||||
-- ACTION ALTER TABLE
|
||||
--
|
||||
ALTER TABLE "review_log" DROP CONSTRAINT IF EXISTS "review_log_fk_1";
|
||||
--
|
||||
-- ACTION CREATE FOREIGN KEY
|
||||
--
|
||||
ALTER TABLE ONLY "card_tag"
|
||||
ADD CONSTRAINT "card_tag_fk_1"
|
||||
FOREIGN KEY("tagId")
|
||||
REFERENCES "tag"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION
|
||||
DEFERRABLE INITIALLY DEFERRED;
|
||||
--
|
||||
-- ACTION CREATE FOREIGN KEY
|
||||
--
|
||||
ALTER TABLE ONLY "learning_path_tag"
|
||||
ADD CONSTRAINT "learning_path_tag_fk_1"
|
||||
FOREIGN KEY("tagId")
|
||||
REFERENCES "tag"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION
|
||||
DEFERRABLE INITIALLY DEFERRED;
|
||||
--
|
||||
-- ACTION CREATE FOREIGN KEY
|
||||
--
|
||||
ALTER TABLE ONLY "note_tag"
|
||||
ADD CONSTRAINT "note_tag_fk_1"
|
||||
FOREIGN KEY("tagId")
|
||||
REFERENCES "tag"("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', '20261111110923694-own-rows-by-id', now())
|
||||
ON CONFLICT ("module")
|
||||
DO UPDATE SET "version" = '20261111110923694-own-rows-by-id', "timestamp" = now();
|
||||
|
||||
--
|
||||
-- MIGRATION VERSION FOR halkyon_base
|
||||
--
|
||||
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
|
||||
VALUES ('halkyon_base', '20261111100053843-shared-space-keys', now())
|
||||
ON CONFLICT ("module")
|
||||
DO UPDATE SET "version" = '20261111100053843-shared-space-keys', "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 halkyon_oidc
|
||||
--
|
||||
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
|
||||
VALUES ('halkyon_oidc', '20261023180644988', now())
|
||||
ON CONFLICT ("module")
|
||||
DO UPDATE SET "version" = '20261023180644988', "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;
|
||||
|
|
@ -55,3 +55,4 @@
|
|||
20261110225358224-file-store-in-halkyon-base
|
||||
20261111091720823-shared-spaces
|
||||
20261111100124819-shared-space-keys
|
||||
20261111110923694-own-rows-by-id
|
||||
|
|
|
|||
|
|
@ -0,0 +1,199 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:halkyon_learn_server/src/generated/protocol.dart';
|
||||
import 'package:halkyon_learn_server/src/shared_space/sync_space_grants.dart';
|
||||
import 'package:serverpod/serverpod.dart';
|
||||
import 'package:serverpod_offline_sync_server/serverpod_offline_sync_server.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../support/auth_fixtures.dart';
|
||||
import '../support/sync_database.dart';
|
||||
import '../test_tools/serverpod_test_tools.dart';
|
||||
|
||||
/// A person's own rows name what is in a space they share by its ID alone
|
||||
/// (Halkyon's ADR 0044, D26): the sync engine links no row of one space to
|
||||
/// a row of another, so a relation would lose the card a review names.
|
||||
void main() {
|
||||
withServerpod(
|
||||
'Own rows naming shared content',
|
||||
(sessionBuilder, endpoints) {
|
||||
late Session session;
|
||||
late UuidValue alice;
|
||||
late UuidValue bob;
|
||||
late UuidValue space;
|
||||
final grants = SyncSpaceGrants();
|
||||
|
||||
setUp(() async {
|
||||
Serverpod.instance.initializeOfflineSync(syncTables: syncTables);
|
||||
useLearnBase(sessionBuilder);
|
||||
session = sessionBuilder.build();
|
||||
alice = const Uuid().v7obj();
|
||||
bob = const Uuid().v7obj();
|
||||
space = await session.db.transaction(
|
||||
(t) => grants.create(session, owner: alice, transaction: t),
|
||||
);
|
||||
await session.db.transaction(
|
||||
(t) =>
|
||||
grants.grant(session, space, bob, writes: true, transaction: t),
|
||||
);
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
for (final each in [alice, bob, space]) {
|
||||
await session.db.unsafeExecute(
|
||||
'DELETE FROM "offline_sync_spaces" WHERE "uuidSpaceId" = @space',
|
||||
parameters: QueryParameters.named({'space': each.uuid}),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test("a member's review, tag, reading and answer keep naming the "
|
||||
"shared space's card, note, path, step and PDF", () async {
|
||||
final payload = ByteData(1);
|
||||
final shared = await asPerson(session, alice, space: space, (t) async {
|
||||
final studyset = await Studyset.db.insertRow(
|
||||
session,
|
||||
Studyset(payload: payload),
|
||||
transaction: t,
|
||||
);
|
||||
final card = await Card.db.insertRow(
|
||||
session,
|
||||
Card(studysetId: studyset.id!, position: 'a0'),
|
||||
transaction: t,
|
||||
);
|
||||
final note = await Note.db.insertRow(
|
||||
session,
|
||||
Note(studysetId: studyset.id!, position: 'a0'),
|
||||
transaction: t,
|
||||
);
|
||||
final path = await LearningPath.db.insertRow(
|
||||
session,
|
||||
LearningPath(
|
||||
studysetId: studyset.id!,
|
||||
position: 'a0',
|
||||
payload: payload,
|
||||
),
|
||||
transaction: t,
|
||||
);
|
||||
final step = await LearningPathStep.db.insertRow(
|
||||
session,
|
||||
LearningPathStep(pathId: path.id!, position: 'a0', kind: 'text'),
|
||||
transaction: t,
|
||||
);
|
||||
final pdf = await PdfSource.db.insertRow(
|
||||
session,
|
||||
PdfSource(studysetId: studyset.id!, payload: payload),
|
||||
transaction: t,
|
||||
);
|
||||
return (
|
||||
card: card.id!,
|
||||
note: note.id!,
|
||||
path: path.id!,
|
||||
step: step.id!,
|
||||
pdf: pdf.id!,
|
||||
);
|
||||
});
|
||||
|
||||
final own = await asPerson(session, bob, (t) async {
|
||||
final tag = await Tag.db.insertRow(
|
||||
session,
|
||||
Tag(payload: payload),
|
||||
transaction: t,
|
||||
);
|
||||
final now = DateTime.now().toUtc();
|
||||
return (
|
||||
review: (await ReviewLog.db.insertRow(
|
||||
session,
|
||||
ReviewLog(
|
||||
cardId: shared.card,
|
||||
rating: 3,
|
||||
reviewedAt: now,
|
||||
durationMs: 1000,
|
||||
),
|
||||
transaction: t,
|
||||
)).id!,
|
||||
cardTag: (await CardTag.db.insertRow(
|
||||
session,
|
||||
CardTag(cardId: shared.card, tagId: tag.id!),
|
||||
transaction: t,
|
||||
)).id!,
|
||||
noteTag: (await NoteTag.db.insertRow(
|
||||
session,
|
||||
NoteTag(noteId: shared.note, tagId: tag.id!),
|
||||
transaction: t,
|
||||
)).id!,
|
||||
pathTag: (await LearningPathTag.db.insertRow(
|
||||
session,
|
||||
LearningPathTag(pathId: shared.path, tagId: tag.id!),
|
||||
transaction: t,
|
||||
)).id!,
|
||||
reading: (await PdfReading.db.insertRow(
|
||||
session,
|
||||
PdfReading(
|
||||
pdfSourceId: shared.pdf,
|
||||
readAt: now,
|
||||
kind: 'reached',
|
||||
page: 2,
|
||||
),
|
||||
transaction: t,
|
||||
)).id!,
|
||||
answer: (await LearningPathAnswer.db.insertRow(
|
||||
session,
|
||||
LearningPathAnswer(
|
||||
stepId: shared.step,
|
||||
answeredAt: now,
|
||||
outcome: 'right',
|
||||
),
|
||||
transaction: t,
|
||||
)).id!,
|
||||
);
|
||||
});
|
||||
|
||||
final read = await asPerson(
|
||||
session,
|
||||
bob,
|
||||
(t) async => (
|
||||
review: await ReviewLog.db.findById(
|
||||
session,
|
||||
own.review,
|
||||
transaction: t,
|
||||
),
|
||||
cardTag: await CardTag.db.findById(
|
||||
session,
|
||||
own.cardTag,
|
||||
transaction: t,
|
||||
),
|
||||
noteTag: await NoteTag.db.findById(
|
||||
session,
|
||||
own.noteTag,
|
||||
transaction: t,
|
||||
),
|
||||
pathTag: await LearningPathTag.db.findById(
|
||||
session,
|
||||
own.pathTag,
|
||||
transaction: t,
|
||||
),
|
||||
reading: await PdfReading.db.findById(
|
||||
session,
|
||||
own.reading,
|
||||
transaction: t,
|
||||
),
|
||||
answer: await LearningPathAnswer.db.findById(
|
||||
session,
|
||||
own.answer,
|
||||
transaction: t,
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(read.review?.cardId, shared.card);
|
||||
expect(read.cardTag?.cardId, shared.card);
|
||||
expect(read.noteTag?.noteId, shared.note);
|
||||
expect(read.pathTag?.pathId, shared.path);
|
||||
expect(read.reading?.pdfSourceId, shared.pdf);
|
||||
expect(read.answer?.stepId, shared.step);
|
||||
});
|
||||
},
|
||||
databaseInterceptor: syncDatabaseInterceptor,
|
||||
rollbackDatabase: RollbackDatabase.disabled,
|
||||
);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue