feat(train): the Loop's days read on the phone (ADR 0030, D37, D38)

The server gains Train's synced `day`, `day_series` (a day's heart
rate, deleted with its day) and `night`, each a sealed payload alone;
the migration `days` only adds their tables, indexes and foreign keys
(to their sync space, and a chunk's to its day, deferred), and the
person's deletion takes them with their spaces.

On Android, `LoopDays.kt` reads a band's days from a date to today,
framed by the SDK's sync start and stop, into lines of JSON, then
deletes on the band what the app kept up to the date it names, never
today; `LoopBridge` answers `readDays` with them, one band call at a
time, and `background` schedules `LoopWorker`, a WorkManager job every
four hours that appends the same lines to the person's
`loop_days.txt` while the app doesn't hold the band.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-28 19:53:04 +01:00
commit 88e8693025
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
15 changed files with 24637 additions and 52 deletions

View file

@ -0,0 +1,21 @@
### A day as the person's Polar Loop counted it (ADR 0030, D37): its date, its
### steps, distance, calories and time in each activity class, its Nightly
### Recharge and skin temperature, all in its sealed payload (D7). One row per
### person and date, its ID made from both on the device, so a day read again
### (today, as it goes on) or on another phone writes the same row, the last
### writer winning. Its heart rate is its series (`day_series`). Synced.
class: Day
table: day
database: sync
fields:
### The key it is sealed with (ADR 0002's envelope); never null in Train.
keyId: String?
### The content, sealed: gzipped UTF-8 JSON with a `schemaVersion` once
### opened.
payload: ByteData
### The sealing's nonce (ADR 0002's envelope).
nonce: ByteData?
indexes:
# Deleting a space finds its rows through this index (see content_key).
day_space_idx:
fields: spaceId

View file

@ -0,0 +1,25 @@
### A chunk of a day's 24/7 heart rate from the Polar Loop (ADR 0030, D37):
### up to three hours of samples, in its sealed payload (D7) as a workout's
### series are, sealed for its day (`bound`). Its ID is made from its day's and
### its place in the day, so the day read again rewrites the same chunks.
### Synced; it goes with its day.
class: DaySeries
table: day_series
database: sync
fields:
### The day it is part of.
day: Day?, relation(onDelete=Cascade, deferred)
### The key it is sealed with (ADR 0002's envelope); never null in Train.
keyId: String?
### The content, sealed: gzipped UTF-8 JSON with a `schemaVersion` once
### opened.
payload: ByteData
### The sealing's nonce (ADR 0002's envelope).
nonce: ByteData?
indexes:
# Deleting a space finds its rows through this index (see content_key).
day_series_space_idx:
fields: spaceId
# A day's chunks, to read them together.
day_series_day_idx:
fields: dayId

View file

@ -0,0 +1,20 @@
### A night's sleep as the person's Polar Loop analysed it (ADR 0030, D37):
### when it began and ended, its wake phases and cycles, its rating and its
### skin temperature, all in its sealed payload (D7). One row per person and
### the date the sleep counts for, its ID made from both on the device, the
### last writer winning. Synced.
class: Night
table: night
database: sync
fields:
### The key it is sealed with (ADR 0002's envelope); never null in Train.
keyId: String?
### The content, sealed: gzipped UTF-8 JSON with a `schemaVersion` once
### opened.
payload: ByteData
### The sealing's nonce (ADR 0002's envelope).
nonce: ByteData?
indexes:
# Deleting a space finds its rows through this index (see content_key).
night_space_idx:
fields: spaceId

View file

@ -0,0 +1,671 @@
/* 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: no_leading_underscores_for_library_prefixes
import 'dart:typed_data' as _idt;
import 'package:serverpod/serverpod.dart' as _is;
/// A day as the person's Polar Loop counted it (ADR 0030, D37): its date, its
/// steps, distance, calories and time in each activity class, its Nightly
/// Recharge and skin temperature, all in its sealed payload (D7). One row per
/// person and date, its ID made from both on the device, so a day read again
/// (today, as it goes on) or on another phone writes the same row, the last
/// writer winning. Its heart rate is its series (`day_series`). Synced.
abstract class Day
implements _is.TableRow<_is.UuidValue?>, _is.ProtocolSerialization {
Day._({
this.id,
this.spaceId,
this.keyId,
required this.payload,
this.nonce,
});
factory Day({
_is.UuidValue? id,
int? spaceId,
String? keyId,
required _idt.ByteData payload,
_idt.ByteData? nonce,
}) = _DayImpl;
factory Day.fromJson(Map<String, dynamic> jsonSerialization) {
return Day(
id: jsonSerialization['id'] == null
? null
: _is.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
spaceId: jsonSerialization['spaceId'] as int?,
keyId: jsonSerialization['keyId'] as String?,
payload: _is.ByteDataJsonExtension.fromJson(jsonSerialization['payload']),
nonce: jsonSerialization['nonce'] == null
? null
: _is.ByteDataJsonExtension.fromJson(jsonSerialization['nonce']),
);
}
static final t = DayTable();
static const db = DayRepository._();
@override
_is.UuidValue? id;
/// The space owning this row. Maintained by the sync engine.
int? spaceId;
/// The key it is sealed with (ADR 0002's envelope); never null in Train.
String? keyId;
/// The content, sealed: gzipped UTF-8 JSON with a `schemaVersion` once
/// opened.
_idt.ByteData payload;
/// The sealing's nonce (ADR 0002's envelope).
_idt.ByteData? nonce;
@override
_is.Table<_is.UuidValue?> get table => t;
/// Returns a shallow copy of this [Day]
/// with some or all fields replaced by the given arguments.
@_is.useResult
Day copyWith({
_is.UuidValue? id,
int? spaceId,
String? keyId,
_idt.ByteData? payload,
_idt.ByteData? nonce,
});
@override
Map<String, dynamic> toJson() {
return {
'__className__': 'Day',
if (id != null) 'id': id?.toJson(),
if (spaceId != null) 'spaceId': spaceId,
if (keyId != null) 'keyId': keyId,
'payload': payload.toJson(),
if (nonce != null) 'nonce': nonce?.toJson(),
};
}
@override
Map<String, dynamic> toJsonForProtocol() {
return {
'__className__': 'Day',
if (id != null) 'id': id?.toJson(),
if (spaceId != null) 'spaceId': spaceId,
if (keyId != null) 'keyId': keyId,
'payload': payload.toJson(),
if (nonce != null) 'nonce': nonce?.toJson(),
};
}
static DayInclude include() {
return DayInclude._();
}
static DayIncludeList includeList({
_is.WhereExpressionBuilder<DayTable>? where,
int? limit,
int? offset,
_is.OrderByBuilder<DayTable>? orderBy,
_is.OrderByListBuilder<DayTable>? orderByList,
DayInclude? include,
}) {
return DayIncludeList._(
where: where,
limit: limit,
offset: offset,
orderBy: orderBy?.call(Day.t),
orderByList: orderByList?.call(Day.t),
include: include,
);
}
@override
String toString() {
return _is.SerializationManager.encode(this);
}
}
class _Undefined {}
class _DayImpl extends Day {
_DayImpl({
_is.UuidValue? id,
int? spaceId,
String? keyId,
required _idt.ByteData payload,
_idt.ByteData? nonce,
}) : super._(
id: id,
spaceId: spaceId,
keyId: keyId,
payload: payload,
nonce: nonce,
);
/// Returns a shallow copy of this [Day]
/// with some or all fields replaced by the given arguments.
@_is.useResult
@override
Day copyWith({
Object? id = _Undefined,
Object? spaceId = _Undefined,
Object? keyId = _Undefined,
_idt.ByteData? payload,
Object? nonce = _Undefined,
}) {
return Day(
id: id is _is.UuidValue? ? id : this.id,
spaceId: spaceId is int? ? spaceId : this.spaceId,
keyId: keyId is String? ? keyId : this.keyId,
payload: payload ?? this.payload.clone(),
nonce: nonce is _idt.ByteData? ? nonce : this.nonce?.clone(),
);
}
}
class DayUpdateTable extends _is.UpdateTable<DayTable> {
DayUpdateTable(super.table);
_is.ColumnValue<int, int> spaceId(int? value) => _is.ColumnValue(
table.spaceId,
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 DayTable extends _is.Table<_is.UuidValue?> {
DayTable({super.tableRelation}) : super(tableName: 'day') {
updateTable = DayUpdateTable(this);
spaceId = _is.ColumnInt(
'spaceId',
this,
);
keyId = _is.ColumnString(
'keyId',
this,
);
payload = _is.ColumnByteData(
'payload',
this,
);
nonce = _is.ColumnByteData(
'nonce',
this,
);
}
late final DayUpdateTable updateTable;
/// The space owning this row. Maintained by the sync engine.
late final _is.ColumnInt spaceId;
/// The key it is sealed with (ADR 0002's envelope); never null in Train.
late final _is.ColumnString keyId;
/// The content, sealed: gzipped UTF-8 JSON with a `schemaVersion` once
/// opened.
late final _is.ColumnByteData payload;
/// The sealing's nonce (ADR 0002's envelope).
late final _is.ColumnByteData nonce;
@override
List<_is.Column> get columns => [
id,
spaceId,
keyId,
payload,
nonce,
];
}
class DayInclude extends _is.IncludeObject {
DayInclude._();
@override
Map<String, _is.Include?> get includes => {};
@override
_is.Table<_is.UuidValue?> get table => Day.t;
}
class DayIncludeList extends _is.IncludeList {
DayIncludeList._({
_is.WhereExpressionBuilder<DayTable>? where,
super.limit,
super.offset,
super.orderBy,
super.orderByList,
super.include,
}) {
super.where = where?.call(Day.t);
}
@override
Map<String, _is.Include?> get includes => include?.includes ?? {};
@override
_is.Table<_is.UuidValue?> get table => Day.t;
}
class DayRepository {
const DayRepository._();
/// Returns a list of [Day]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<Day>> find(
_is.DatabaseSession session, {
_is.WhereExpressionBuilder<DayTable>? where,
int? limit,
int? offset,
_is.OrderByBuilder<DayTable>? orderBy,
_is.OrderByListBuilder<DayTable>? orderByList,
_is.Transaction? transaction,
_is.LockMode? lockMode,
_is.LockBehavior? lockBehavior,
}) async {
return session.db.find<Day>(
where: where?.call(Day.t),
orderBy: orderBy?.call(Day.t),
orderByList: orderByList?.call(Day.t),
limit: limit,
offset: offset,
transaction: transaction,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Returns the first matching [Day] 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<Day?> findFirstRow(
_is.DatabaseSession session, {
_is.WhereExpressionBuilder<DayTable>? where,
int? offset,
_is.OrderByBuilder<DayTable>? orderBy,
_is.OrderByListBuilder<DayTable>? orderByList,
_is.Transaction? transaction,
_is.LockMode? lockMode,
_is.LockBehavior? lockBehavior,
}) async {
return session.db.findFirstRow<Day>(
where: where?.call(Day.t),
orderBy: orderBy?.call(Day.t),
orderByList: orderByList?.call(Day.t),
offset: offset,
transaction: transaction,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Finds a single [Day] by its [id] or null if no such row exists.
Future<Day?> findById(
_is.DatabaseSession session,
_is.UuidValue id, {
_is.Transaction? transaction,
_is.LockMode? lockMode,
_is.LockBehavior? lockBehavior,
}) async {
return session.db.findById<Day>(
id,
transaction: transaction,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Inserts all [Day]s in the list and returns the inserted rows.
///
/// The returned [Day]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<Day>> insert(
_is.DatabaseSession session,
List<Day> rows, {
_is.Transaction? transaction,
bool ignoreConflicts = false,
bool noReturn = false,
}) async {
return session.db.insert<Day>(
rows,
transaction: transaction,
ignoreConflicts: ignoreConflicts,
noReturn: noReturn,
);
}
/// Inserts a single [Day] and returns the inserted row.
///
/// The returned [Day] will have its `id` field set.
Future<Day> insertRow(
_is.DatabaseSession session,
Day row, {
_is.Transaction? transaction,
}) async {
return session.db.insertRow<Day>(
row,
transaction: transaction,
);
}
/// Upserts all [Day]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 [Day]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<Day>> upsert(
_is.DatabaseSession session,
List<Day> rows, {
required _is.ColumnSelections<DayTable> conflictColumns,
_is.ColumnSelections<DayTable>? updateColumns,
_is.WhereExpressionBuilder<DayTable>? updateWhere,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.upsert<Day>(
rows,
conflictColumns: conflictColumns(Day.t),
updateColumns: updateColumns?.call(Day.t),
updateWhere: updateWhere?.call(Day.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Upserts a single [Day] 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 [Day] will have its `id` field set.
Future<Day?> upsertRow(
_is.DatabaseSession session,
Day row, {
required _is.ColumnSelections<DayTable> conflictColumns,
_is.ColumnSelections<DayTable>? updateColumns,
_is.WhereExpressionBuilder<DayTable>? updateWhere,
_is.Transaction? transaction,
}) async {
return session.db.upsertRow<Day>(
row,
conflictColumns: conflictColumns(Day.t),
updateColumns: updateColumns?.call(Day.t),
updateWhere: updateWhere?.call(Day.t),
transaction: transaction,
);
}
/// Updates all [Day]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<Day>> update(
_is.DatabaseSession session,
List<Day> rows, {
_is.ColumnSelections<DayTable>? columns,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.update<Day>(
rows,
columns: columns?.call(Day.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Updates a single [Day]. 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<Day> updateRow(
_is.DatabaseSession session,
Day row, {
_is.ColumnSelections<DayTable>? columns,
_is.Transaction? transaction,
}) async {
return session.db.updateRow<Day>(
row,
columns: columns?.call(Day.t),
transaction: transaction,
);
}
/// Updates a single [Day] by its [id] with the specified [columnValues].
/// Returns the updated row or null if no row with the given id exists.
Future<Day?> updateById(
_is.DatabaseSession session,
_is.UuidValue id, {
required _is.ColumnValueListBuilder<DayUpdateTable> columnValues,
_is.Transaction? transaction,
}) async {
return session.db.updateById<Day>(
id,
columnValues: columnValues(Day.t.updateTable),
transaction: transaction,
);
}
/// Updates all [Day]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<Day>> updateWhere(
_is.DatabaseSession session, {
required _is.ColumnValueListBuilder<DayUpdateTable> columnValues,
required _is.WhereExpressionBuilder<DayTable> where,
int? limit,
int? offset,
_is.OrderByBuilder<DayTable>? orderBy,
_is.OrderByListBuilder<DayTable>? orderByList,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.updateWhere<Day>(
columnValues: columnValues(Day.t.updateTable),
where: where(Day.t),
limit: limit,
offset: offset,
orderBy: orderBy?.call(Day.t),
orderByList: orderByList?.call(Day.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Deletes all [Day]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<Day>> delete(
_is.DatabaseSession session,
List<Day> rows, {
_is.OrderByBuilder<DayTable>? orderBy,
_is.OrderByListBuilder<DayTable>? orderByList,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.delete<Day>(
rows,
orderBy: orderBy?.call(Day.t),
orderByList: orderByList?.call(Day.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Deletes a single [Day].
Future<Day> deleteRow(
_is.DatabaseSession session,
Day row, {
_is.Transaction? transaction,
}) async {
return session.db.deleteRow<Day>(
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<Day>> deleteWhere(
_is.DatabaseSession session, {
required _is.WhereExpressionBuilder<DayTable> where,
_is.OrderByBuilder<DayTable>? orderBy,
_is.OrderByListBuilder<DayTable>? orderByList,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.deleteWhere<Day>(
where: where(Day.t),
orderBy: orderBy?.call(Day.t),
orderByList: orderByList?.call(Day.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<DayTable>? where,
int? limit,
_is.Transaction? transaction,
}) async {
return session.db.count<Day>(
where: where?.call(Day.t),
limit: limit,
transaction: transaction,
);
}
/// Acquires row-level locks on [Day] rows matching the [where] expression.
Future<void> lockRows(
_is.DatabaseSession session, {
required _is.WhereExpressionBuilder<DayTable> where,
required _is.LockMode lockMode,
required _is.Transaction transaction,
_is.LockBehavior lockBehavior = _is.LockBehavior.wait,
}) async {
return session.db.lockRows<Day>(
where: where(Day.t),
lockMode: lockMode,
lockBehavior: lockBehavior,
transaction: transaction,
);
}
}

View file

@ -0,0 +1,778 @@
/* 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_train_server/src/generated/protocol.dart' as _iw76w3x7;
import 'package:serverpod/serverpod.dart' as _is;
import '../day/day.dart' as _irdxguic;
/// A chunk of a day's 24/7 heart rate from the Polar Loop (ADR 0030, D37):
/// up to three hours of samples, in its sealed payload (D7) as a workout's
/// series are, sealed for its day (`bound`). Its ID is made from its day's and
/// its place in the day, so the day read again rewrites the same chunks.
/// Synced; it goes with its day.
abstract class DaySeries
implements _is.TableRow<_is.UuidValue?>, _is.ProtocolSerialization {
DaySeries._({
this.id,
this.spaceId,
required this.dayId,
this.day,
this.keyId,
required this.payload,
this.nonce,
});
factory DaySeries({
_is.UuidValue? id,
int? spaceId,
required _is.UuidValue dayId,
_irdxguic.Day? day,
String? keyId,
required _idt.ByteData payload,
_idt.ByteData? nonce,
}) = _DaySeriesImpl;
factory DaySeries.fromJson(Map<String, dynamic> jsonSerialization) {
return DaySeries(
id: jsonSerialization['id'] == null
? null
: _is.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
spaceId: jsonSerialization['spaceId'] as int?,
dayId: _is.UuidValueJsonExtension.fromJson(jsonSerialization['dayId']),
day: jsonSerialization['day'] == null
? null
: _iw76w3x7.Protocol().deserialize<_irdxguic.Day>(
jsonSerialization['day'],
),
keyId: jsonSerialization['keyId'] as String?,
payload: _is.ByteDataJsonExtension.fromJson(jsonSerialization['payload']),
nonce: jsonSerialization['nonce'] == null
? null
: _is.ByteDataJsonExtension.fromJson(jsonSerialization['nonce']),
);
}
static final t = DaySeriesTable();
static const db = DaySeriesRepository._();
@override
_is.UuidValue? id;
/// The space owning this row. Maintained by the sync engine.
int? spaceId;
_is.UuidValue dayId;
/// The day it is part of.
_irdxguic.Day? day;
/// The key it is sealed with (ADR 0002's envelope); never null in Train.
String? keyId;
/// The content, sealed: gzipped UTF-8 JSON with a `schemaVersion` once
/// opened.
_idt.ByteData payload;
/// The sealing's nonce (ADR 0002's envelope).
_idt.ByteData? nonce;
@override
_is.Table<_is.UuidValue?> get table => t;
/// Returns a shallow copy of this [DaySeries]
/// with some or all fields replaced by the given arguments.
@_is.useResult
DaySeries copyWith({
_is.UuidValue? id,
int? spaceId,
_is.UuidValue? dayId,
_irdxguic.Day? day,
String? keyId,
_idt.ByteData? payload,
_idt.ByteData? nonce,
});
@override
Map<String, dynamic> toJson() {
return {
'__className__': 'DaySeries',
if (id != null) 'id': id?.toJson(),
if (spaceId != null) 'spaceId': spaceId,
'dayId': dayId.toJson(),
if (day != null) 'day': day?.toJson(),
if (keyId != null) 'keyId': keyId,
'payload': payload.toJson(),
if (nonce != null) 'nonce': nonce?.toJson(),
};
}
@override
Map<String, dynamic> toJsonForProtocol() {
return {
'__className__': 'DaySeries',
if (id != null) 'id': id?.toJson(),
if (spaceId != null) 'spaceId': spaceId,
'dayId': dayId.toJson(),
if (day != null) 'day': day?.toJsonForProtocol(),
if (keyId != null) 'keyId': keyId,
'payload': payload.toJson(),
if (nonce != null) 'nonce': nonce?.toJson(),
};
}
static DaySeriesInclude include({_irdxguic.DayInclude? day}) {
return DaySeriesInclude._(day: day);
}
static DaySeriesIncludeList includeList({
_is.WhereExpressionBuilder<DaySeriesTable>? where,
int? limit,
int? offset,
_is.OrderByBuilder<DaySeriesTable>? orderBy,
_is.OrderByListBuilder<DaySeriesTable>? orderByList,
DaySeriesInclude? include,
}) {
return DaySeriesIncludeList._(
where: where,
limit: limit,
offset: offset,
orderBy: orderBy?.call(DaySeries.t),
orderByList: orderByList?.call(DaySeries.t),
include: include,
);
}
@override
String toString() {
return _is.SerializationManager.encode(this);
}
}
class _Undefined {}
class _DaySeriesImpl extends DaySeries {
_DaySeriesImpl({
_is.UuidValue? id,
int? spaceId,
required _is.UuidValue dayId,
_irdxguic.Day? day,
String? keyId,
required _idt.ByteData payload,
_idt.ByteData? nonce,
}) : super._(
id: id,
spaceId: spaceId,
dayId: dayId,
day: day,
keyId: keyId,
payload: payload,
nonce: nonce,
);
/// Returns a shallow copy of this [DaySeries]
/// with some or all fields replaced by the given arguments.
@_is.useResult
@override
DaySeries copyWith({
Object? id = _Undefined,
Object? spaceId = _Undefined,
_is.UuidValue? dayId,
Object? day = _Undefined,
Object? keyId = _Undefined,
_idt.ByteData? payload,
Object? nonce = _Undefined,
}) {
return DaySeries(
id: id is _is.UuidValue? ? id : this.id,
spaceId: spaceId is int? ? spaceId : this.spaceId,
dayId: dayId ?? this.dayId,
day: day is _irdxguic.Day? ? day : this.day?.copyWith(),
keyId: keyId is String? ? keyId : this.keyId,
payload: payload ?? this.payload.clone(),
nonce: nonce is _idt.ByteData? ? nonce : this.nonce?.clone(),
);
}
}
class DaySeriesUpdateTable extends _is.UpdateTable<DaySeriesTable> {
DaySeriesUpdateTable(super.table);
_is.ColumnValue<int, int> spaceId(int? value) => _is.ColumnValue(
table.spaceId,
value,
);
_is.ColumnValue<_is.UuidValue, _is.UuidValue> dayId(_is.UuidValue value) =>
_is.ColumnValue(
table.dayId,
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 DaySeriesTable extends _is.Table<_is.UuidValue?> {
DaySeriesTable({super.tableRelation}) : super(tableName: 'day_series') {
updateTable = DaySeriesUpdateTable(this);
spaceId = _is.ColumnInt(
'spaceId',
this,
);
dayId = _is.ColumnUuid(
'dayId',
this,
);
keyId = _is.ColumnString(
'keyId',
this,
);
payload = _is.ColumnByteData(
'payload',
this,
);
nonce = _is.ColumnByteData(
'nonce',
this,
);
}
late final DaySeriesUpdateTable updateTable;
/// The space owning this row. Maintained by the sync engine.
late final _is.ColumnInt spaceId;
late final _is.ColumnUuid dayId;
/// The day it is part of.
_irdxguic.DayTable? _day;
/// The key it is sealed with (ADR 0002's envelope); never null in Train.
late final _is.ColumnString keyId;
/// The content, sealed: gzipped UTF-8 JSON with a `schemaVersion` once
/// opened.
late final _is.ColumnByteData payload;
/// The sealing's nonce (ADR 0002's envelope).
late final _is.ColumnByteData nonce;
_irdxguic.DayTable get day {
if (_day != null) return _day!;
_day = _is.createRelationTable(
relationFieldName: 'day',
field: DaySeries.t.dayId,
foreignField: _irdxguic.Day.t.id,
tableRelation: tableRelation,
createTable: (foreignTableRelation) =>
_irdxguic.DayTable(tableRelation: foreignTableRelation),
);
return _day!;
}
@override
List<_is.Column> get columns => [
id,
spaceId,
dayId,
keyId,
payload,
nonce,
];
@override
_is.Table? getRelationTable(String relationField) {
if (relationField == 'day') {
return day;
}
return null;
}
}
class DaySeriesInclude extends _is.IncludeObject {
DaySeriesInclude._({_irdxguic.DayInclude? day}) {
_day = day;
}
_irdxguic.DayInclude? _day;
@override
Map<String, _is.Include?> get includes => {'day': _day};
@override
_is.Table<_is.UuidValue?> get table => DaySeries.t;
}
class DaySeriesIncludeList extends _is.IncludeList {
DaySeriesIncludeList._({
_is.WhereExpressionBuilder<DaySeriesTable>? where,
super.limit,
super.offset,
super.orderBy,
super.orderByList,
super.include,
}) {
super.where = where?.call(DaySeries.t);
}
@override
Map<String, _is.Include?> get includes => include?.includes ?? {};
@override
_is.Table<_is.UuidValue?> get table => DaySeries.t;
}
class DaySeriesRepository {
const DaySeriesRepository._();
final attachRow = const DaySeriesAttachRowRepository._();
/// Returns a list of [DaySeries]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<DaySeries>> find(
_is.DatabaseSession session, {
_is.WhereExpressionBuilder<DaySeriesTable>? where,
int? limit,
int? offset,
_is.OrderByBuilder<DaySeriesTable>? orderBy,
_is.OrderByListBuilder<DaySeriesTable>? orderByList,
_is.Transaction? transaction,
DaySeriesInclude? include,
_is.LockMode? lockMode,
_is.LockBehavior? lockBehavior,
}) async {
return session.db.find<DaySeries>(
where: where?.call(DaySeries.t),
orderBy: orderBy?.call(DaySeries.t),
orderByList: orderByList?.call(DaySeries.t),
limit: limit,
offset: offset,
transaction: transaction,
include: include,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Returns the first matching [DaySeries] 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<DaySeries?> findFirstRow(
_is.DatabaseSession session, {
_is.WhereExpressionBuilder<DaySeriesTable>? where,
int? offset,
_is.OrderByBuilder<DaySeriesTable>? orderBy,
_is.OrderByListBuilder<DaySeriesTable>? orderByList,
_is.Transaction? transaction,
DaySeriesInclude? include,
_is.LockMode? lockMode,
_is.LockBehavior? lockBehavior,
}) async {
return session.db.findFirstRow<DaySeries>(
where: where?.call(DaySeries.t),
orderBy: orderBy?.call(DaySeries.t),
orderByList: orderByList?.call(DaySeries.t),
offset: offset,
transaction: transaction,
include: include,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Finds a single [DaySeries] by its [id] or null if no such row exists.
Future<DaySeries?> findById(
_is.DatabaseSession session,
_is.UuidValue id, {
_is.Transaction? transaction,
DaySeriesInclude? include,
_is.LockMode? lockMode,
_is.LockBehavior? lockBehavior,
}) async {
return session.db.findById<DaySeries>(
id,
transaction: transaction,
include: include,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Inserts all [DaySeries]s in the list and returns the inserted rows.
///
/// The returned [DaySeries]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<DaySeries>> insert(
_is.DatabaseSession session,
List<DaySeries> rows, {
_is.Transaction? transaction,
bool ignoreConflicts = false,
bool noReturn = false,
}) async {
return session.db.insert<DaySeries>(
rows,
transaction: transaction,
ignoreConflicts: ignoreConflicts,
noReturn: noReturn,
);
}
/// Inserts a single [DaySeries] and returns the inserted row.
///
/// The returned [DaySeries] will have its `id` field set.
Future<DaySeries> insertRow(
_is.DatabaseSession session,
DaySeries row, {
_is.Transaction? transaction,
}) async {
return session.db.insertRow<DaySeries>(
row,
transaction: transaction,
);
}
/// Upserts all [DaySeries]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 [DaySeries]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<DaySeries>> upsert(
_is.DatabaseSession session,
List<DaySeries> rows, {
required _is.ColumnSelections<DaySeriesTable> conflictColumns,
_is.ColumnSelections<DaySeriesTable>? updateColumns,
_is.WhereExpressionBuilder<DaySeriesTable>? updateWhere,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.upsert<DaySeries>(
rows,
conflictColumns: conflictColumns(DaySeries.t),
updateColumns: updateColumns?.call(DaySeries.t),
updateWhere: updateWhere?.call(DaySeries.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Upserts a single [DaySeries] 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 [DaySeries] will have its `id` field set.
Future<DaySeries?> upsertRow(
_is.DatabaseSession session,
DaySeries row, {
required _is.ColumnSelections<DaySeriesTable> conflictColumns,
_is.ColumnSelections<DaySeriesTable>? updateColumns,
_is.WhereExpressionBuilder<DaySeriesTable>? updateWhere,
_is.Transaction? transaction,
}) async {
return session.db.upsertRow<DaySeries>(
row,
conflictColumns: conflictColumns(DaySeries.t),
updateColumns: updateColumns?.call(DaySeries.t),
updateWhere: updateWhere?.call(DaySeries.t),
transaction: transaction,
);
}
/// Updates all [DaySeries]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<DaySeries>> update(
_is.DatabaseSession session,
List<DaySeries> rows, {
_is.ColumnSelections<DaySeriesTable>? columns,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.update<DaySeries>(
rows,
columns: columns?.call(DaySeries.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Updates a single [DaySeries]. 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<DaySeries> updateRow(
_is.DatabaseSession session,
DaySeries row, {
_is.ColumnSelections<DaySeriesTable>? columns,
_is.Transaction? transaction,
}) async {
return session.db.updateRow<DaySeries>(
row,
columns: columns?.call(DaySeries.t),
transaction: transaction,
);
}
/// Updates a single [DaySeries] by its [id] with the specified [columnValues].
/// Returns the updated row or null if no row with the given id exists.
Future<DaySeries?> updateById(
_is.DatabaseSession session,
_is.UuidValue id, {
required _is.ColumnValueListBuilder<DaySeriesUpdateTable> columnValues,
_is.Transaction? transaction,
}) async {
return session.db.updateById<DaySeries>(
id,
columnValues: columnValues(DaySeries.t.updateTable),
transaction: transaction,
);
}
/// Updates all [DaySeries]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<DaySeries>> updateWhere(
_is.DatabaseSession session, {
required _is.ColumnValueListBuilder<DaySeriesUpdateTable> columnValues,
required _is.WhereExpressionBuilder<DaySeriesTable> where,
int? limit,
int? offset,
_is.OrderByBuilder<DaySeriesTable>? orderBy,
_is.OrderByListBuilder<DaySeriesTable>? orderByList,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.updateWhere<DaySeries>(
columnValues: columnValues(DaySeries.t.updateTable),
where: where(DaySeries.t),
limit: limit,
offset: offset,
orderBy: orderBy?.call(DaySeries.t),
orderByList: orderByList?.call(DaySeries.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Deletes all [DaySeries]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<DaySeries>> delete(
_is.DatabaseSession session,
List<DaySeries> rows, {
_is.OrderByBuilder<DaySeriesTable>? orderBy,
_is.OrderByListBuilder<DaySeriesTable>? orderByList,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.delete<DaySeries>(
rows,
orderBy: orderBy?.call(DaySeries.t),
orderByList: orderByList?.call(DaySeries.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Deletes a single [DaySeries].
Future<DaySeries> deleteRow(
_is.DatabaseSession session,
DaySeries row, {
_is.Transaction? transaction,
}) async {
return session.db.deleteRow<DaySeries>(
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<DaySeries>> deleteWhere(
_is.DatabaseSession session, {
required _is.WhereExpressionBuilder<DaySeriesTable> where,
_is.OrderByBuilder<DaySeriesTable>? orderBy,
_is.OrderByListBuilder<DaySeriesTable>? orderByList,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.deleteWhere<DaySeries>(
where: where(DaySeries.t),
orderBy: orderBy?.call(DaySeries.t),
orderByList: orderByList?.call(DaySeries.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<DaySeriesTable>? where,
int? limit,
_is.Transaction? transaction,
}) async {
return session.db.count<DaySeries>(
where: where?.call(DaySeries.t),
limit: limit,
transaction: transaction,
);
}
/// Acquires row-level locks on [DaySeries] rows matching the [where] expression.
Future<void> lockRows(
_is.DatabaseSession session, {
required _is.WhereExpressionBuilder<DaySeriesTable> where,
required _is.LockMode lockMode,
required _is.Transaction transaction,
_is.LockBehavior lockBehavior = _is.LockBehavior.wait,
}) async {
return session.db.lockRows<DaySeries>(
where: where(DaySeries.t),
lockMode: lockMode,
lockBehavior: lockBehavior,
transaction: transaction,
);
}
}
class DaySeriesAttachRowRepository {
const DaySeriesAttachRowRepository._();
/// Creates a relation between the given [DaySeries] and [Day]
/// by setting the [DaySeries]'s foreign key `dayId` to refer to the [Day].
Future<void> day(
_is.DatabaseSession session,
DaySeries daySeries,
_irdxguic.Day day, {
_is.Transaction? transaction,
}) async {
if (daySeries.id == null) {
throw ArgumentError.notNull('daySeries.id');
}
if (day.id == null) {
throw ArgumentError.notNull('day.id');
}
var $daySeries = daySeries.copyWith(dayId: day.id);
await session.db.updateRow<DaySeries>(
$daySeries,
columns: [DaySeries.t.dayId],
transaction: transaction,
);
}
}

View file

@ -0,0 +1,670 @@
/* 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: no_leading_underscores_for_library_prefixes
import 'dart:typed_data' as _idt;
import 'package:serverpod/serverpod.dart' as _is;
/// A night's sleep as the person's Polar Loop analysed it (ADR 0030, D37):
/// when it began and ended, its wake phases and cycles, its rating and its
/// skin temperature, all in its sealed payload (D7). One row per person and
/// the date the sleep counts for, its ID made from both on the device, the
/// last writer winning. Synced.
abstract class Night
implements _is.TableRow<_is.UuidValue?>, _is.ProtocolSerialization {
Night._({
this.id,
this.spaceId,
this.keyId,
required this.payload,
this.nonce,
});
factory Night({
_is.UuidValue? id,
int? spaceId,
String? keyId,
required _idt.ByteData payload,
_idt.ByteData? nonce,
}) = _NightImpl;
factory Night.fromJson(Map<String, dynamic> jsonSerialization) {
return Night(
id: jsonSerialization['id'] == null
? null
: _is.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
spaceId: jsonSerialization['spaceId'] as int?,
keyId: jsonSerialization['keyId'] as String?,
payload: _is.ByteDataJsonExtension.fromJson(jsonSerialization['payload']),
nonce: jsonSerialization['nonce'] == null
? null
: _is.ByteDataJsonExtension.fromJson(jsonSerialization['nonce']),
);
}
static final t = NightTable();
static const db = NightRepository._();
@override
_is.UuidValue? id;
/// The space owning this row. Maintained by the sync engine.
int? spaceId;
/// The key it is sealed with (ADR 0002's envelope); never null in Train.
String? keyId;
/// The content, sealed: gzipped UTF-8 JSON with a `schemaVersion` once
/// opened.
_idt.ByteData payload;
/// The sealing's nonce (ADR 0002's envelope).
_idt.ByteData? nonce;
@override
_is.Table<_is.UuidValue?> get table => t;
/// Returns a shallow copy of this [Night]
/// with some or all fields replaced by the given arguments.
@_is.useResult
Night copyWith({
_is.UuidValue? id,
int? spaceId,
String? keyId,
_idt.ByteData? payload,
_idt.ByteData? nonce,
});
@override
Map<String, dynamic> toJson() {
return {
'__className__': 'Night',
if (id != null) 'id': id?.toJson(),
if (spaceId != null) 'spaceId': spaceId,
if (keyId != null) 'keyId': keyId,
'payload': payload.toJson(),
if (nonce != null) 'nonce': nonce?.toJson(),
};
}
@override
Map<String, dynamic> toJsonForProtocol() {
return {
'__className__': 'Night',
if (id != null) 'id': id?.toJson(),
if (spaceId != null) 'spaceId': spaceId,
if (keyId != null) 'keyId': keyId,
'payload': payload.toJson(),
if (nonce != null) 'nonce': nonce?.toJson(),
};
}
static NightInclude include() {
return NightInclude._();
}
static NightIncludeList includeList({
_is.WhereExpressionBuilder<NightTable>? where,
int? limit,
int? offset,
_is.OrderByBuilder<NightTable>? orderBy,
_is.OrderByListBuilder<NightTable>? orderByList,
NightInclude? include,
}) {
return NightIncludeList._(
where: where,
limit: limit,
offset: offset,
orderBy: orderBy?.call(Night.t),
orderByList: orderByList?.call(Night.t),
include: include,
);
}
@override
String toString() {
return _is.SerializationManager.encode(this);
}
}
class _Undefined {}
class _NightImpl extends Night {
_NightImpl({
_is.UuidValue? id,
int? spaceId,
String? keyId,
required _idt.ByteData payload,
_idt.ByteData? nonce,
}) : super._(
id: id,
spaceId: spaceId,
keyId: keyId,
payload: payload,
nonce: nonce,
);
/// Returns a shallow copy of this [Night]
/// with some or all fields replaced by the given arguments.
@_is.useResult
@override
Night copyWith({
Object? id = _Undefined,
Object? spaceId = _Undefined,
Object? keyId = _Undefined,
_idt.ByteData? payload,
Object? nonce = _Undefined,
}) {
return Night(
id: id is _is.UuidValue? ? id : this.id,
spaceId: spaceId is int? ? spaceId : this.spaceId,
keyId: keyId is String? ? keyId : this.keyId,
payload: payload ?? this.payload.clone(),
nonce: nonce is _idt.ByteData? ? nonce : this.nonce?.clone(),
);
}
}
class NightUpdateTable extends _is.UpdateTable<NightTable> {
NightUpdateTable(super.table);
_is.ColumnValue<int, int> spaceId(int? value) => _is.ColumnValue(
table.spaceId,
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 NightTable extends _is.Table<_is.UuidValue?> {
NightTable({super.tableRelation}) : super(tableName: 'night') {
updateTable = NightUpdateTable(this);
spaceId = _is.ColumnInt(
'spaceId',
this,
);
keyId = _is.ColumnString(
'keyId',
this,
);
payload = _is.ColumnByteData(
'payload',
this,
);
nonce = _is.ColumnByteData(
'nonce',
this,
);
}
late final NightUpdateTable updateTable;
/// The space owning this row. Maintained by the sync engine.
late final _is.ColumnInt spaceId;
/// The key it is sealed with (ADR 0002's envelope); never null in Train.
late final _is.ColumnString keyId;
/// The content, sealed: gzipped UTF-8 JSON with a `schemaVersion` once
/// opened.
late final _is.ColumnByteData payload;
/// The sealing's nonce (ADR 0002's envelope).
late final _is.ColumnByteData nonce;
@override
List<_is.Column> get columns => [
id,
spaceId,
keyId,
payload,
nonce,
];
}
class NightInclude extends _is.IncludeObject {
NightInclude._();
@override
Map<String, _is.Include?> get includes => {};
@override
_is.Table<_is.UuidValue?> get table => Night.t;
}
class NightIncludeList extends _is.IncludeList {
NightIncludeList._({
_is.WhereExpressionBuilder<NightTable>? where,
super.limit,
super.offset,
super.orderBy,
super.orderByList,
super.include,
}) {
super.where = where?.call(Night.t);
}
@override
Map<String, _is.Include?> get includes => include?.includes ?? {};
@override
_is.Table<_is.UuidValue?> get table => Night.t;
}
class NightRepository {
const NightRepository._();
/// Returns a list of [Night]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<Night>> find(
_is.DatabaseSession session, {
_is.WhereExpressionBuilder<NightTable>? where,
int? limit,
int? offset,
_is.OrderByBuilder<NightTable>? orderBy,
_is.OrderByListBuilder<NightTable>? orderByList,
_is.Transaction? transaction,
_is.LockMode? lockMode,
_is.LockBehavior? lockBehavior,
}) async {
return session.db.find<Night>(
where: where?.call(Night.t),
orderBy: orderBy?.call(Night.t),
orderByList: orderByList?.call(Night.t),
limit: limit,
offset: offset,
transaction: transaction,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Returns the first matching [Night] 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<Night?> findFirstRow(
_is.DatabaseSession session, {
_is.WhereExpressionBuilder<NightTable>? where,
int? offset,
_is.OrderByBuilder<NightTable>? orderBy,
_is.OrderByListBuilder<NightTable>? orderByList,
_is.Transaction? transaction,
_is.LockMode? lockMode,
_is.LockBehavior? lockBehavior,
}) async {
return session.db.findFirstRow<Night>(
where: where?.call(Night.t),
orderBy: orderBy?.call(Night.t),
orderByList: orderByList?.call(Night.t),
offset: offset,
transaction: transaction,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Finds a single [Night] by its [id] or null if no such row exists.
Future<Night?> findById(
_is.DatabaseSession session,
_is.UuidValue id, {
_is.Transaction? transaction,
_is.LockMode? lockMode,
_is.LockBehavior? lockBehavior,
}) async {
return session.db.findById<Night>(
id,
transaction: transaction,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Inserts all [Night]s in the list and returns the inserted rows.
///
/// The returned [Night]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<Night>> insert(
_is.DatabaseSession session,
List<Night> rows, {
_is.Transaction? transaction,
bool ignoreConflicts = false,
bool noReturn = false,
}) async {
return session.db.insert<Night>(
rows,
transaction: transaction,
ignoreConflicts: ignoreConflicts,
noReturn: noReturn,
);
}
/// Inserts a single [Night] and returns the inserted row.
///
/// The returned [Night] will have its `id` field set.
Future<Night> insertRow(
_is.DatabaseSession session,
Night row, {
_is.Transaction? transaction,
}) async {
return session.db.insertRow<Night>(
row,
transaction: transaction,
);
}
/// Upserts all [Night]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 [Night]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<Night>> upsert(
_is.DatabaseSession session,
List<Night> rows, {
required _is.ColumnSelections<NightTable> conflictColumns,
_is.ColumnSelections<NightTable>? updateColumns,
_is.WhereExpressionBuilder<NightTable>? updateWhere,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.upsert<Night>(
rows,
conflictColumns: conflictColumns(Night.t),
updateColumns: updateColumns?.call(Night.t),
updateWhere: updateWhere?.call(Night.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Upserts a single [Night] 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 [Night] will have its `id` field set.
Future<Night?> upsertRow(
_is.DatabaseSession session,
Night row, {
required _is.ColumnSelections<NightTable> conflictColumns,
_is.ColumnSelections<NightTable>? updateColumns,
_is.WhereExpressionBuilder<NightTable>? updateWhere,
_is.Transaction? transaction,
}) async {
return session.db.upsertRow<Night>(
row,
conflictColumns: conflictColumns(Night.t),
updateColumns: updateColumns?.call(Night.t),
updateWhere: updateWhere?.call(Night.t),
transaction: transaction,
);
}
/// Updates all [Night]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<Night>> update(
_is.DatabaseSession session,
List<Night> rows, {
_is.ColumnSelections<NightTable>? columns,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.update<Night>(
rows,
columns: columns?.call(Night.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Updates a single [Night]. 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<Night> updateRow(
_is.DatabaseSession session,
Night row, {
_is.ColumnSelections<NightTable>? columns,
_is.Transaction? transaction,
}) async {
return session.db.updateRow<Night>(
row,
columns: columns?.call(Night.t),
transaction: transaction,
);
}
/// Updates a single [Night] by its [id] with the specified [columnValues].
/// Returns the updated row or null if no row with the given id exists.
Future<Night?> updateById(
_is.DatabaseSession session,
_is.UuidValue id, {
required _is.ColumnValueListBuilder<NightUpdateTable> columnValues,
_is.Transaction? transaction,
}) async {
return session.db.updateById<Night>(
id,
columnValues: columnValues(Night.t.updateTable),
transaction: transaction,
);
}
/// Updates all [Night]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<Night>> updateWhere(
_is.DatabaseSession session, {
required _is.ColumnValueListBuilder<NightUpdateTable> columnValues,
required _is.WhereExpressionBuilder<NightTable> where,
int? limit,
int? offset,
_is.OrderByBuilder<NightTable>? orderBy,
_is.OrderByListBuilder<NightTable>? orderByList,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.updateWhere<Night>(
columnValues: columnValues(Night.t.updateTable),
where: where(Night.t),
limit: limit,
offset: offset,
orderBy: orderBy?.call(Night.t),
orderByList: orderByList?.call(Night.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Deletes all [Night]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<Night>> delete(
_is.DatabaseSession session,
List<Night> rows, {
_is.OrderByBuilder<NightTable>? orderBy,
_is.OrderByListBuilder<NightTable>? orderByList,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.delete<Night>(
rows,
orderBy: orderBy?.call(Night.t),
orderByList: orderByList?.call(Night.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Deletes a single [Night].
Future<Night> deleteRow(
_is.DatabaseSession session,
Night row, {
_is.Transaction? transaction,
}) async {
return session.db.deleteRow<Night>(
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<Night>> deleteWhere(
_is.DatabaseSession session, {
required _is.WhereExpressionBuilder<NightTable> where,
_is.OrderByBuilder<NightTable>? orderBy,
_is.OrderByListBuilder<NightTable>? orderByList,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.deleteWhere<Night>(
where: where(Night.t),
orderBy: orderBy?.call(Night.t),
orderByList: orderByList?.call(Night.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<NightTable>? where,
int? limit,
_is.Transaction? transaction,
}) async {
return session.db.count<Night>(
where: where?.call(Night.t),
limit: limit,
transaction: transaction,
);
}
/// Acquires row-level locks on [Night] rows matching the [where] expression.
Future<void> lockRows(
_is.DatabaseSession session, {
required _is.WhereExpressionBuilder<NightTable> where,
required _is.LockMode lockMode,
required _is.Transaction transaction,
_is.LockBehavior lockBehavior = _is.LockBehavior.wait,
}) async {
return session.db.lockRows<Night>(
where: where(Night.t),
lockMode: lockMode,
lockBehavior: lockBehavior,
transaction: transaction,
);
}
}

View file

@ -21,11 +21,17 @@ import 'package:serverpod_auth_idp_server/serverpod_auth_idp_server.dart'
as _iais;
import 'package:serverpod_offline_sync_server/serverpod_offline_sync_server.dart'
as _izehhkf5;
import 'day/day.dart' as _ikmv9f3y;
import 'day/day_series.dart' as _ir1ofny6;
import 'day/night.dart' as _i17cgxdf;
import 'map/map_source.dart' as _ikw0qg8x;
import 'privacy/content_key.dart' as _imed3x5d;
import 'profile/profile.dart' as _ia5j8fch;
import 'workout/workout.dart' as _i5b9b17z;
import 'workout/workout_series.dart' as _i92mefto;
export 'day/day.dart';
export 'day/day_series.dart';
export 'day/night.dart';
export 'map/map_source.dart';
export 'privacy/content_key.dart';
export 'profile/profile.dart';
@ -102,6 +108,237 @@ class Protocol extends _is.DatabaseSerializationManager {
],
managed: true,
),
_isp.TableDefinition(
name: 'day',
dartName: 'Day',
schema: 'public',
module: 'halkyon_train',
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: '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: 'day_fk_0',
columns: ['spaceId'],
referenceTable: 'offline_sync_spaces',
referenceTableSchema: 'public',
referenceColumns: ['id'],
onUpdate: _isp.ForeignKeyAction.noAction,
onDelete: _isp.ForeignKeyAction.cascade,
matchType: null,
),
],
indexes: [
_isp.IndexDefinition(
indexName: 'day_space_idx',
tableSpace: null,
elements: [
_isp.IndexElementDefinition(
type: _isp.IndexElementDefinitionType.column,
definition: 'spaceId',
),
],
type: 'btree',
isUnique: false,
isPrimary: false,
),
],
managed: true,
),
_isp.TableDefinition(
name: 'day_series',
dartName: 'DaySeries',
schema: 'public',
module: 'halkyon_train',
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: 'dayId',
columnType: _isp.ColumnType.uuid,
isNullable: false,
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: 'day_series_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: 'day_series_fk_1',
columns: ['dayId'],
referenceTable: 'day',
referenceTableSchema: 'public',
referenceColumns: ['id'],
onUpdate: _isp.ForeignKeyAction.noAction,
onDelete: _isp.ForeignKeyAction.cascade,
matchType: null,
deferrable: _isp.DeferrableConstraint.initiallyDeferred,
),
],
indexes: [
_isp.IndexDefinition(
indexName: 'day_series_space_idx',
tableSpace: null,
elements: [
_isp.IndexElementDefinition(
type: _isp.IndexElementDefinitionType.column,
definition: 'spaceId',
),
],
type: 'btree',
isUnique: false,
isPrimary: false,
),
_isp.IndexDefinition(
indexName: 'day_series_day_idx',
tableSpace: null,
elements: [
_isp.IndexElementDefinition(
type: _isp.IndexElementDefinitionType.column,
definition: 'dayId',
),
],
type: 'btree',
isUnique: false,
isPrimary: false,
),
],
managed: true,
),
_isp.TableDefinition(
name: 'night',
dartName: 'Night',
schema: 'public',
module: 'halkyon_train',
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: '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: 'night_fk_0',
columns: ['spaceId'],
referenceTable: 'offline_sync_spaces',
referenceTableSchema: 'public',
referenceColumns: ['id'],
onUpdate: _isp.ForeignKeyAction.noAction,
onDelete: _isp.ForeignKeyAction.cascade,
matchType: null,
),
],
indexes: [
_isp.IndexDefinition(
indexName: 'night_space_idx',
tableSpace: null,
elements: [
_isp.IndexElementDefinition(
type: _isp.IndexElementDefinitionType.column,
definition: 'spaceId',
),
],
type: 'btree',
isUnique: false,
isPrimary: false,
),
],
managed: true,
),
_isp.TableDefinition(
name: 'profile',
dartName: 'Profile',
@ -368,6 +605,15 @@ class Protocol extends _is.DatabaseSerializationManager {
}
}
if (t == _ikmv9f3y.Day) {
return _ikmv9f3y.Day.fromJson(data) as T;
}
if (t == _ir1ofny6.DaySeries) {
return _ir1ofny6.DaySeries.fromJson(data) as T;
}
if (t == _i17cgxdf.Night) {
return _i17cgxdf.Night.fromJson(data) as T;
}
if (t == _ikw0qg8x.MapSource) {
return _ikw0qg8x.MapSource.fromJson(data) as T;
}
@ -383,6 +629,15 @@ class Protocol extends _is.DatabaseSerializationManager {
if (t == _i92mefto.WorkoutSeries) {
return _i92mefto.WorkoutSeries.fromJson(data) as T;
}
if (t == _is.getType<_ikmv9f3y.Day?>()) {
return (data != null ? _ikmv9f3y.Day.fromJson(data) : null) as T;
}
if (t == _is.getType<_ir1ofny6.DaySeries?>()) {
return (data != null ? _ir1ofny6.DaySeries.fromJson(data) : null) as T;
}
if (t == _is.getType<_i17cgxdf.Night?>()) {
return (data != null ? _i17cgxdf.Night.fromJson(data) : null) as T;
}
if (t == _is.getType<_ikw0qg8x.MapSource?>()) {
return (data != null ? _ikw0qg8x.MapSource.fromJson(data) : null) as T;
}
@ -422,6 +677,9 @@ class Protocol extends _is.DatabaseSerializationManager {
static String? getClassNameForType(Type type) {
return switch (type) {
_ikmv9f3y.Day => 'Day',
_ir1ofny6.DaySeries => 'DaySeries',
_i17cgxdf.Night => 'Night',
_ikw0qg8x.MapSource => 'MapSource',
_imed3x5d.ContentKey => 'ContentKey',
_ia5j8fch.Profile => 'Profile',
@ -444,6 +702,12 @@ class Protocol extends _is.DatabaseSerializationManager {
}
switch (data) {
case _ikmv9f3y.Day():
return 'Day';
case _ir1ofny6.DaySeries():
return 'DaySeries';
case _i17cgxdf.Night():
return 'Night';
case _ikw0qg8x.MapSource():
return 'MapSource';
case _imed3x5d.ContentKey():
@ -494,6 +758,15 @@ class Protocol extends _is.DatabaseSerializationManager {
if (dataClassName is! String) {
return super.deserializeByClassName(data);
}
if (dataClassName == 'Day') {
return deserialize<_ikmv9f3y.Day>(data['data']);
}
if (dataClassName == 'DaySeries') {
return deserialize<_ir1ofny6.DaySeries>(data['data']);
}
if (dataClassName == 'Night') {
return deserialize<_i17cgxdf.Night>(data['data']);
}
if (dataClassName == 'MapSource') {
return deserialize<_ikw0qg8x.MapSource>(data['data']);
}
@ -583,6 +856,12 @@ class Protocol extends _is.DatabaseSerializationManager {
}
}
switch (t) {
case _ikmv9f3y.Day:
return _ikmv9f3y.Day.t;
case _ir1ofny6.DaySeries:
return _ir1ofny6.DaySeries.t;
case _i17cgxdf.Night:
return _i17cgxdf.Night.t;
case _imed3x5d.ContentKey:
return _imed3x5d.ContentKey.t;
case _ia5j8fch.Profile:

View file

@ -11,6 +11,9 @@
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod/serverpod.dart' as _is;
import 'day/day.dart' as _ikmv9f3y;
import 'day/day_series.dart' as _ir1ofny6;
import 'day/night.dart' as _i17cgxdf;
import 'privacy/content_key.dart' as _imed3x5d;
import 'profile/profile.dart' as _ia5j8fch;
import 'workout/workout.dart' as _i5b9b17z;
@ -21,6 +24,9 @@ import 'workout/workout_series.dart' as _i92mefto;
/// shared packages.
final List<_is.Table> syncTables = [
_imed3x5d.ContentKey.t,
_ikmv9f3y.Day.t,
_ir1ofny6.DaySeries.t,
_i17cgxdf.Night.t,
_ia5j8fch.Profile.t,
_i5b9b17z.Workout.t,
_i92mefto.WorkoutSeries.t,

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,687 @@
{
"__className__": "serverpod.DatabaseDefinition",
"schemaVersion": 2,
"moduleName": "halkyon_train",
"tables": [
{
"__className__": "serverpod.TableDefinition",
"name": "content_key",
"dartName": "ContentKey",
"module": "halkyon_train",
"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": "dataKeyId",
"fieldName": "dataKeyId",
"columnType": 0,
"isNullable": false,
"dartType": "String"
},
{
"__className__": "serverpod.ColumnDefinition",
"name": "sealedKey",
"fieldName": "sealedKey",
"columnType": 5,
"isNullable": false,
"dartType": "dart:typed_data:ByteData"
}
],
"foreignKeys": [
{
"__className__": "serverpod.ForeignKeyDefinition",
"constraintName": "content_key_fk_0",
"columns": [
"spaceId"
],
"referenceTable": "offline_sync_spaces",
"referenceTableSchema": "public",
"referenceColumns": [
"id"
],
"onUpdate": 3,
"onDelete": 4
}
],
"indexes": [
{
"__className__": "serverpod.IndexDefinition",
"indexName": "content_key_space_idx",
"elements": [
{
"__className__": "serverpod.IndexElementDefinition",
"type": 0,
"definition": "spaceId"
}
],
"type": "btree",
"isUnique": false,
"isPrimary": false
}
],
"managed": true
},
{
"__className__": "serverpod.TableDefinition",
"name": "day",
"dartName": "Day",
"module": "halkyon_train",
"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": "day_fk_0",
"columns": [
"spaceId"
],
"referenceTable": "offline_sync_spaces",
"referenceTableSchema": "public",
"referenceColumns": [
"id"
],
"onUpdate": 3,
"onDelete": 4
}
],
"indexes": [
{
"__className__": "serverpod.IndexDefinition",
"indexName": "day_space_idx",
"elements": [
{
"__className__": "serverpod.IndexElementDefinition",
"type": 0,
"definition": "spaceId"
}
],
"type": "btree",
"isUnique": false,
"isPrimary": false
}
],
"managed": true
},
{
"__className__": "serverpod.TableDefinition",
"name": "day_series",
"dartName": "DaySeries",
"module": "halkyon_train",
"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": "dayId",
"fieldName": "dayId",
"columnType": 7,
"isNullable": false,
"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": "day_series_fk_0",
"columns": [
"spaceId"
],
"referenceTable": "offline_sync_spaces",
"referenceTableSchema": "public",
"referenceColumns": [
"id"
],
"onUpdate": 3,
"onDelete": 4
},
{
"__className__": "serverpod.ForeignKeyDefinition",
"constraintName": "day_series_fk_1",
"columns": [
"dayId"
],
"referenceTable": "day",
"referenceTableSchema": "public",
"referenceColumns": [
"id"
],
"onUpdate": 3,
"onDelete": 4,
"deferrable": "initiallyDeferred"
}
],
"indexes": [
{
"__className__": "serverpod.IndexDefinition",
"indexName": "day_series_space_idx",
"elements": [
{
"__className__": "serverpod.IndexElementDefinition",
"type": 0,
"definition": "spaceId"
}
],
"type": "btree",
"isUnique": false,
"isPrimary": false
},
{
"__className__": "serverpod.IndexDefinition",
"indexName": "day_series_day_idx",
"elements": [
{
"__className__": "serverpod.IndexElementDefinition",
"type": 0,
"definition": "dayId"
}
],
"type": "btree",
"isUnique": false,
"isPrimary": false
}
],
"managed": true
},
{
"__className__": "serverpod.TableDefinition",
"name": "night",
"dartName": "Night",
"module": "halkyon_train",
"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": "night_fk_0",
"columns": [
"spaceId"
],
"referenceTable": "offline_sync_spaces",
"referenceTableSchema": "public",
"referenceColumns": [
"id"
],
"onUpdate": 3,
"onDelete": 4
}
],
"indexes": [
{
"__className__": "serverpod.IndexDefinition",
"indexName": "night_space_idx",
"elements": [
{
"__className__": "serverpod.IndexElementDefinition",
"type": 0,
"definition": "spaceId"
}
],
"type": "btree",
"isUnique": false,
"isPrimary": false
}
],
"managed": true
},
{
"__className__": "serverpod.TableDefinition",
"name": "profile",
"dartName": "Profile",
"module": "halkyon_train",
"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": "profile_fk_0",
"columns": [
"spaceId"
],
"referenceTable": "offline_sync_spaces",
"referenceTableSchema": "public",
"referenceColumns": [
"id"
],
"onUpdate": 3,
"onDelete": 4
}
],
"indexes": [
{
"__className__": "serverpod.IndexDefinition",
"indexName": "profile_space_idx",
"elements": [
{
"__className__": "serverpod.IndexElementDefinition",
"type": 0,
"definition": "spaceId"
}
],
"type": "btree",
"isUnique": false,
"isPrimary": false
}
],
"managed": true
},
{
"__className__": "serverpod.TableDefinition",
"name": "workout",
"dartName": "Workout",
"module": "halkyon_train",
"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": "workout_fk_0",
"columns": [
"spaceId"
],
"referenceTable": "offline_sync_spaces",
"referenceTableSchema": "public",
"referenceColumns": [
"id"
],
"onUpdate": 3,
"onDelete": 4
}
],
"indexes": [
{
"__className__": "serverpod.IndexDefinition",
"indexName": "workout_space_idx",
"elements": [
{
"__className__": "serverpod.IndexElementDefinition",
"type": 0,
"definition": "spaceId"
}
],
"type": "btree",
"isUnique": false,
"isPrimary": false
}
],
"managed": true
},
{
"__className__": "serverpod.TableDefinition",
"name": "workout_series",
"dartName": "WorkoutSeries",
"module": "halkyon_train",
"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": "workoutId",
"fieldName": "workoutId",
"columnType": 7,
"isNullable": false,
"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": "workout_series_fk_0",
"columns": [
"spaceId"
],
"referenceTable": "offline_sync_spaces",
"referenceTableSchema": "public",
"referenceColumns": [
"id"
],
"onUpdate": 3,
"onDelete": 4
},
{
"__className__": "serverpod.ForeignKeyDefinition",
"constraintName": "workout_series_fk_1",
"columns": [
"workoutId"
],
"referenceTable": "workout",
"referenceTableSchema": "public",
"referenceColumns": [
"id"
],
"onUpdate": 3,
"onDelete": 4,
"deferrable": "initiallyDeferred"
}
],
"indexes": [
{
"__className__": "serverpod.IndexDefinition",
"indexName": "workout_series_space_idx",
"elements": [
{
"__className__": "serverpod.IndexElementDefinition",
"type": 0,
"definition": "spaceId"
}
],
"type": "btree",
"isUnique": false,
"isPrimary": false
},
{
"__className__": "serverpod.IndexDefinition",
"indexName": "workout_series_workout_idx",
"elements": [
{
"__className__": "serverpod.IndexElementDefinition",
"type": 0,
"definition": "workoutId"
}
],
"type": "btree",
"isUnique": false,
"isPrimary": false
}
],
"managed": true
}
],
"installedModules": [
{
"__className__": "serverpod.DatabaseMigrationVersion",
"module": "halkyon_base",
"version": "20261026140329453-assistant-budgets"
},
{
"__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": "halkyon_oidc",
"version": "20261023180644988"
},
{
"__className__": "serverpod.DatabaseMigrationVersion",
"module": "serverpod_auth_core",
"version": "20260924105232991"
}
],
"migrationApiVersion": 1
}

View file

@ -0,0 +1,305 @@
{
"__className__": "serverpod.DatabaseMigration",
"actions": [
{
"__className__": "serverpod.DatabaseMigrationAction",
"type": "createTable",
"createTable": {
"__className__": "serverpod.TableDefinition",
"name": "day",
"dartName": "Day",
"module": "halkyon_train",
"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": "day_fk_0",
"columns": [
"spaceId"
],
"referenceTable": "offline_sync_spaces",
"referenceTableSchema": "public",
"referenceColumns": [
"id"
],
"onUpdate": 3,
"onDelete": 4
}
],
"indexes": [
{
"__className__": "serverpod.IndexDefinition",
"indexName": "day_space_idx",
"elements": [
{
"__className__": "serverpod.IndexElementDefinition",
"type": 0,
"definition": "spaceId"
}
],
"type": "btree",
"isUnique": false,
"isPrimary": false
}
],
"managed": true
}
},
{
"__className__": "serverpod.DatabaseMigrationAction",
"type": "createTable",
"createTable": {
"__className__": "serverpod.TableDefinition",
"name": "day_series",
"dartName": "DaySeries",
"module": "halkyon_train",
"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": "dayId",
"fieldName": "dayId",
"columnType": 7,
"isNullable": false,
"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": "day_series_fk_0",
"columns": [
"spaceId"
],
"referenceTable": "offline_sync_spaces",
"referenceTableSchema": "public",
"referenceColumns": [
"id"
],
"onUpdate": 3,
"onDelete": 4
},
{
"__className__": "serverpod.ForeignKeyDefinition",
"constraintName": "day_series_fk_1",
"columns": [
"dayId"
],
"referenceTable": "day",
"referenceTableSchema": "public",
"referenceColumns": [
"id"
],
"onUpdate": 3,
"onDelete": 4,
"deferrable": "initiallyDeferred"
}
],
"indexes": [
{
"__className__": "serverpod.IndexDefinition",
"indexName": "day_series_space_idx",
"elements": [
{
"__className__": "serverpod.IndexElementDefinition",
"type": 0,
"definition": "spaceId"
}
],
"type": "btree",
"isUnique": false,
"isPrimary": false
},
{
"__className__": "serverpod.IndexDefinition",
"indexName": "day_series_day_idx",
"elements": [
{
"__className__": "serverpod.IndexElementDefinition",
"type": 0,
"definition": "dayId"
}
],
"type": "btree",
"isUnique": false,
"isPrimary": false
}
],
"managed": true
}
},
{
"__className__": "serverpod.DatabaseMigrationAction",
"type": "createTable",
"createTable": {
"__className__": "serverpod.TableDefinition",
"name": "night",
"dartName": "Night",
"module": "halkyon_train",
"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": "night_fk_0",
"columns": [
"spaceId"
],
"referenceTable": "offline_sync_spaces",
"referenceTableSchema": "public",
"referenceColumns": [
"id"
],
"onUpdate": 3,
"onDelete": 4
}
],
"indexes": [
{
"__className__": "serverpod.IndexDefinition",
"indexName": "night_space_idx",
"elements": [
{
"__className__": "serverpod.IndexElementDefinition",
"type": 0,
"definition": "spaceId"
}
],
"type": "btree",
"isUnique": false,
"isPrimary": false
}
],
"managed": true
}
}
],
"warnings": [],
"migrationApiVersion": 1
}

View file

@ -0,0 +1,171 @@
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 "day" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
"spaceId" bigint,
"keyId" text,
"payload" bytea NOT NULL,
"nonce" bytea
);
-- Indexes
CREATE INDEX "day_space_idx" ON "day" USING btree ("spaceId");
--
-- ACTION CREATE TABLE
--
CREATE TABLE "day_series" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
"spaceId" bigint,
"dayId" uuid NOT NULL,
"keyId" text,
"payload" bytea NOT NULL,
"nonce" bytea
);
-- Indexes
CREATE INDEX "day_series_space_idx" ON "day_series" USING btree ("spaceId");
CREATE INDEX "day_series_day_idx" ON "day_series" USING btree ("dayId");
--
-- ACTION CREATE TABLE
--
CREATE TABLE "night" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
"spaceId" bigint,
"keyId" text,
"payload" bytea NOT NULL,
"nonce" bytea
);
-- Indexes
CREATE INDEX "night_space_idx" ON "night" USING btree ("spaceId");
--
-- ACTION CREATE FOREIGN KEY
--
ALTER TABLE ONLY "day"
ADD CONSTRAINT "day_fk_0"
FOREIGN KEY("spaceId")
REFERENCES "offline_sync_spaces"("id")
ON DELETE CASCADE
ON UPDATE NO ACTION;
--
-- ACTION CREATE FOREIGN KEY
--
ALTER TABLE ONLY "day_series"
ADD CONSTRAINT "day_series_fk_0"
FOREIGN KEY("spaceId")
REFERENCES "offline_sync_spaces"("id")
ON DELETE CASCADE
ON UPDATE NO ACTION;
ALTER TABLE ONLY "day_series"
ADD CONSTRAINT "day_series_fk_1"
FOREIGN KEY("dayId")
REFERENCES "day"("id")
ON DELETE CASCADE
ON UPDATE NO ACTION
DEFERRABLE INITIALLY DEFERRED;
--
-- ACTION CREATE FOREIGN KEY
--
ALTER TABLE ONLY "night"
ADD CONSTRAINT "night_fk_0"
FOREIGN KEY("spaceId")
REFERENCES "offline_sync_spaces"("id")
ON DELETE CASCADE
ON UPDATE NO ACTION;
--
-- MIGRATION VERSION FOR halkyon_train
--
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
VALUES ('halkyon_train', '20261028181405893-days', now())
ON CONFLICT ("module")
DO UPDATE SET "version" = '20261028181405893-days', "timestamp" = now();
--
-- MIGRATION VERSION FOR halkyon_base
--
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
VALUES ('halkyon_base', '20261026140329453-assistant-budgets', now())
ON CONFLICT ("module")
DO UPDATE SET "version" = '20261026140329453-assistant-budgets', "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;

View file

@ -7,3 +7,4 @@
20261028075229917-initial
20261028101012641-workouts
20261028164640000-profile
20261028181405893-days

View file

@ -51,42 +51,51 @@ void main() {
expect(trainBase().deletePersonData, contains(deleteTrainData));
});
test('deleting a person deletes their space with every key, workout, '
"series and profile in it, and nobody else's", () async {
for (final person in [alice, bob]) {
await _workoutOf(session, person);
}
for (final table in [
'content_key',
'workout',
'workout_series',
'profile',
]) {
expect(await rowsOf(alice, table), 1, reason: table);
}
test(
'deleting a person deletes their space with every key, workout, '
"series, profile, day, day's series and night in it, and nobody else's",
() async {
for (final person in [alice, bob]) {
await _workoutOf(session, person);
}
for (final table in [
'content_key',
'workout',
'workout_series',
'profile',
'day',
'day_series',
'night',
]) {
expect(await rowsOf(alice, table), 1, reason: table);
}
await session.db.transaction(
(transaction) => deleteTrainData(session, alice, transaction),
);
await session.db.transaction(
(transaction) => deleteTrainData(session, alice, transaction),
);
// SELECT count(*) FROM "offline_sync_spaces" WHERE "uuidSpaceId" = $1
expect(
await OfflineSyncSpace.db.count(
session,
where: (t) => t.uuidSpaceId.equals(alice),
),
0,
);
for (final table in [
'content_key',
'workout',
'workout_series',
'profile',
]) {
expect(await rowsOf(alice, table), 0, reason: table);
expect(await rowsOf(bob, table), 1, reason: table);
}
});
// SELECT count(*) FROM "offline_sync_spaces" WHERE "uuidSpaceId" = $1
expect(
await OfflineSyncSpace.db.count(
session,
where: (t) => t.uuidSpaceId.equals(alice),
),
0,
);
for (final table in [
'content_key',
'workout',
'workout_series',
'profile',
'day',
'day_series',
'night',
]) {
expect(await rowsOf(alice, table), 0, reason: table);
expect(await rowsOf(bob, table), 1, reason: table);
}
},
);
},
// The sync engine's database, as the running server has it. Writing
// through it takes transactions of its own, outside the one a test would
@ -106,9 +115,10 @@ void main() {
/// database wraps it.
final _syncDatabases = Expando<OfflineSyncDatabase>();
/// A key, a workout, a series of it and a profile in [person]'s personal
/// space, written as their device would through the sync engine (payloads
/// stand in for sealed ones: the server never reads them).
/// A key, a workout, a series of it, a profile, a day, a series of it and a
/// night in [person]'s personal space, written as their device would through
/// the sync engine (payloads stand in for sealed ones: the server never reads
/// them).
Future<void> _workoutOf(Session session, UuidValue person) =>
_syncDatabases[session]!.transactionForUser(person, (transaction) async {
final sealed = ByteData.sublistView(Uint8List.fromList([1, 2, 3]));
@ -137,4 +147,24 @@ Future<void> _workoutOf(Session session, UuidValue person) =>
Profile(keyId: 'p1.0.key', payload: sealed, nonce: sealed),
transaction: transaction,
);
final day = await Day.db.insertRow(
session,
Day(keyId: 'p1.0.key', payload: sealed, nonce: sealed),
transaction: transaction,
);
await DaySeries.db.insertRow(
session,
DaySeries(
dayId: day.id!,
keyId: 'p1.0.key',
payload: sealed,
nonce: sealed,
),
transaction: transaction,
);
await Night.db.insertRow(
session,
Night(keyId: 'p1.0.key', payload: sealed, nonce: sealed),
transaction: transaction,
);
});