feat(train): the sync engine and Train's first models (ADR 0030, D14)

Train's server syncs with serverpod_offline_sync as Learn's does, and
holds three synced tables, every payload sealed on the devices:
content_key (the person's data key, sealed to the account), workout (its
type, times, name and totals, changed when edited) and workout_series
(chunks of a workout's samples, written once). A person's space goes
with them, with everything in it.

The migration only adds: the three tables, each indexed on spaceId and
cascading with its space (a series also with its workout), and the sync
engine's own tables.

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 11:13:04 +01:00
commit 7b20851a0a
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
34 changed files with 31210 additions and 5 deletions

View file

@ -0,0 +1,318 @@
{
"__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": "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,19 @@
/// AUTOGENERATED BY SERVERPOD create-migration - DO NOT EDIT BY HAND
/// Merging: resolve registry conflicts the same as migration_registry.txt.
library;
import 'package:serverpod_database/serverpod_database.dart';
part '20261028101012641-workouts/migration.dart';
/// Migration registry for the client-side database.
class MigrationRegistry {
/// Ordered list of all client-side database migrations.
/// New migrations are always appended at the end.
static final List<MigrationVersionSql> migrations = [
_Migration20261028101012641_workouts(),
];
/// List of all client-side database migration versions.
static List<String> get versions => migrations.map((m) => m.version).toList();
}

View file

@ -10,6 +10,7 @@
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _ida;
import 'package:halkyon_base_client/halkyon_base_client.dart' as _ihk4p5t7;
import 'package:halkyon_oidc_client/halkyon_oidc_client.dart' as _ibkpwzgl;
import 'package:http/http.dart' as _i85jenna;
@ -18,12 +19,18 @@ import 'package:serverpod_auth_core_client/serverpod_auth_core_client.dart'
import 'package:serverpod_auth_idp_client/serverpod_auth_idp_client.dart'
as _iaic;
import 'package:serverpod_client/serverpod_client.dart' as _isc;
import 'package:serverpod_database/serverpod_database.dart' as _isd;
import 'package:serverpod_offline_sync_client/serverpod_offline_sync_client.dart'
as _ipulbpi2;
import 'protocol.dart' as _il2as5qe;
import 'sync_tables.dart' as _ii3f3u05;
import 'package:halkyon_train_client/migrations/migration_registry.dart';
class Modules {
Modules(Client client) {
base = _ihk4p5t7.Caller(client);
serverpod_auth_idp = _iaic.Caller(client);
offline_sync = _ipulbpi2.Caller(client);
oidc = _ibkpwzgl.Caller(client);
serverpod_auth_core = _iacc.Caller(client);
}
@ -32,6 +39,8 @@ class Modules {
late final _iaic.Caller serverpod_auth_idp;
late final _ipulbpi2.Caller offline_sync;
late final _ibkpwzgl.Caller oidc;
late final _iacc.Caller serverpod_auth_core;
@ -76,7 +85,61 @@ class Client extends _isc.ServerpodClientShared {
Map<String, _isc.ModuleEndpointCaller> get moduleLookup => {
'base': modules.base,
'serverpod_auth_idp': modules.serverpod_auth_idp,
'offline_sync': modules.offline_sync,
'oidc': modules.oidc,
'serverpod_auth_core': modules.serverpod_auth_core,
};
/// Creates a new client-side database session for the given path.
///
/// The [path] is the file path to the SQLite database file. Since SQLite uses
/// WAL mode, note that `[path]-shm` and `[path]-wal` files might also exist
/// transiently for the database while the session is open.
///
/// If [runMigrations] is true, pending migrations will be applied when
/// opening the database. Be careful when setting this to false, as it might
/// lead to inconsistencies between the models and the database.
///
/// If [isDebugMode] is true, the database integrity will be verified after
/// the migrations are applied to provide feedback of possible issues. On a
/// Flutter application, this should be set to [kDebugMode].
_ida.Future<_isd.ClientDatabaseSession> createSession(
String path, {
bool runMigrations = true,
bool isDebugMode = false,
}) async {
return await _isd.ClientDatabaseSession.open(
path,
_il2as5qe.Protocol(),
clientMigrations: MigrationRegistry.migrations,
runMigrations: runMigrations,
isDebugMode: isDebugMode,
);
}
/// Creates a new client-side database session for the given path, wrapped
/// with the `serverpod_offline_sync` engine for the tables declared with
/// `database: sync`. See [createSession] for the [path], [runMigrations] and
/// [isDebugMode] parameters.
///
/// The [persistentUserId] is the user all local operations belong to. When
/// omitted, the user must be passed through the transaction.
_ida.Future<_ipulbpi2.OfflineSyncDatabaseSession> createSyncSession(
String path, {
bool runMigrations = true,
bool isDebugMode = false,
_isc.UuidValue? persistentUserId,
}) async {
final session = _ipulbpi2.OfflineSyncDatabaseSession.wraps(
await createSession(
path,
runMigrations: runMigrations,
isDebugMode: isDebugMode,
),
syncTables: _ii3f3u05.syncTables,
persistentUserId: persistentUserId,
);
await session.db.initialize();
return session;
}
}

View file

@ -0,0 +1,647 @@
/* 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_client/serverpod_client.dart' as _isc;
import 'package:serverpod_database/serverpod_database.dart' as _isd;
/// A data key of the person's (ADR 0030, D7 and D15): the one their workouts
/// and everything else of Train's are sealed with. Sealed on a device to the
/// account's X25519 key and signed by it (halkyon_keys' `sealDataKey`), under
/// an ID that says what it is for, so only the person's devices open it and
/// the instance can't relabel it; the instance keeps it as it is. Synced, so
/// the key arrives with the data, works offline and goes with the space.
/// Never updated.
abstract class ContentKey
implements _isd.TableRow<_isc.UuidValue?>, _isc.ProtocolSerialization {
ContentKey._({
this.id,
this.spaceId,
required this.dataKeyId,
required this.sealedKey,
});
factory ContentKey({
_isc.UuidValue? id,
int? spaceId,
required String dataKeyId,
required _idt.ByteData sealedKey,
}) = _ContentKeyImpl;
factory ContentKey.fromJson(Map<String, dynamic> jsonSerialization) {
return ContentKey(
id: jsonSerialization['id'] == null
? null
: _isc.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
spaceId: jsonSerialization['spaceId'] as int?,
dataKeyId: jsonSerialization['dataKeyId'] as String,
sealedKey: _isc.ByteDataJsonExtension.fromJson(
jsonSerialization['sealedKey'],
),
);
}
static final t = ContentKeyTable();
static const db = ContentKeyRepository._();
@override
_isc.UuidValue? id;
/// The space owning this row. Maintained by the sync engine.
int? spaceId;
/// The key's ID, which the rows it seals name in their `keyId`; sealed
/// with the key too, so the column is never trusted.
String dataKeyId;
/// The sealed key: the sealer's Ed25519 signature, then the sealed box.
_idt.ByteData sealedKey;
@override
_isd.Table<_isc.UuidValue?> get table => t;
/// Returns a shallow copy of this [ContentKey]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
ContentKey copyWith({
_isc.UuidValue? id,
int? spaceId,
String? dataKeyId,
_idt.ByteData? sealedKey,
});
@override
Map<String, dynamic> toJson() {
return {
'__className__': 'ContentKey',
if (id != null) 'id': id?.toJson(),
if (spaceId != null) 'spaceId': spaceId,
'dataKeyId': dataKeyId,
'sealedKey': sealedKey.toJson(),
};
}
@override
Map<String, dynamic> toJsonForProtocol() {
return {
'__className__': 'ContentKey',
if (id != null) 'id': id?.toJson(),
if (spaceId != null) 'spaceId': spaceId,
'dataKeyId': dataKeyId,
'sealedKey': sealedKey.toJson(),
};
}
static ContentKeyInclude include() {
return ContentKeyInclude._();
}
static ContentKeyIncludeList includeList({
_isd.WhereExpressionBuilder<ContentKeyTable>? where,
int? limit,
int? offset,
_isd.OrderByBuilder<ContentKeyTable>? orderBy,
_isd.OrderByListBuilder<ContentKeyTable>? orderByList,
ContentKeyInclude? include,
}) {
return ContentKeyIncludeList._(
where: where,
limit: limit,
offset: offset,
orderBy: orderBy?.call(ContentKey.t),
orderByList: orderByList?.call(ContentKey.t),
include: include,
);
}
@override
String toString() {
return _isc.SerializationManager.encode(this);
}
}
class _Undefined {}
class _ContentKeyImpl extends ContentKey {
_ContentKeyImpl({
_isc.UuidValue? id,
int? spaceId,
required String dataKeyId,
required _idt.ByteData sealedKey,
}) : super._(
id: id,
spaceId: spaceId,
dataKeyId: dataKeyId,
sealedKey: sealedKey,
);
/// Returns a shallow copy of this [ContentKey]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
@override
ContentKey copyWith({
Object? id = _Undefined,
Object? spaceId = _Undefined,
String? dataKeyId,
_idt.ByteData? sealedKey,
}) {
return ContentKey(
id: id is _isc.UuidValue? ? id : this.id,
spaceId: spaceId is int? ? spaceId : this.spaceId,
dataKeyId: dataKeyId ?? this.dataKeyId,
sealedKey: sealedKey ?? this.sealedKey.clone(),
);
}
}
class ContentKeyUpdateTable extends _isd.UpdateTable<ContentKeyTable> {
ContentKeyUpdateTable(super.table);
_isd.ColumnValue<int, int> spaceId(int? value) => _isd.ColumnValue(
table.spaceId,
value,
);
_isd.ColumnValue<String, String> dataKeyId(String value) => _isd.ColumnValue(
table.dataKeyId,
value,
);
_isd.ColumnValue<_idt.ByteData, _idt.ByteData> sealedKey(
_idt.ByteData value,
) => _isd.ColumnValue(
table.sealedKey,
value,
);
}
class ContentKeyTable extends _isd.Table<_isc.UuidValue?> {
ContentKeyTable({super.tableRelation}) : super(tableName: 'content_key') {
updateTable = ContentKeyUpdateTable(this);
spaceId = _isd.ColumnInt(
'spaceId',
this,
);
dataKeyId = _isd.ColumnString(
'dataKeyId',
this,
);
sealedKey = _isd.ColumnByteData(
'sealedKey',
this,
);
}
late final ContentKeyUpdateTable updateTable;
/// The space owning this row. Maintained by the sync engine.
late final _isd.ColumnInt spaceId;
/// The key's ID, which the rows it seals name in their `keyId`; sealed
/// with the key too, so the column is never trusted.
late final _isd.ColumnString dataKeyId;
/// The sealed key: the sealer's Ed25519 signature, then the sealed box.
late final _isd.ColumnByteData sealedKey;
@override
List<_isd.Column> get columns => [
id,
spaceId,
dataKeyId,
sealedKey,
];
}
class ContentKeyInclude extends _isd.IncludeObject {
ContentKeyInclude._();
@override
Map<String, _isd.Include?> get includes => {};
@override
_isd.Table<_isc.UuidValue?> get table => ContentKey.t;
}
class ContentKeyIncludeList extends _isd.IncludeList {
ContentKeyIncludeList._({
_isd.WhereExpressionBuilder<ContentKeyTable>? where,
super.limit,
super.offset,
super.orderBy,
super.orderByList,
super.include,
}) {
super.where = where?.call(ContentKey.t);
}
@override
Map<String, _isd.Include?> get includes => include?.includes ?? {};
@override
_isd.Table<_isc.UuidValue?> get table => ContentKey.t;
}
class ContentKeyRepository {
const ContentKeyRepository._();
/// Returns a list of [ContentKey]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<ContentKey>> find(
_isd.DatabaseSession session, {
_isd.WhereExpressionBuilder<ContentKeyTable>? where,
int? limit,
int? offset,
_isd.OrderByBuilder<ContentKeyTable>? orderBy,
_isd.OrderByListBuilder<ContentKeyTable>? orderByList,
_isd.Transaction? transaction,
_isd.LockMode? lockMode,
_isd.LockBehavior? lockBehavior,
}) async {
return session.db.find<ContentKey>(
where: where?.call(ContentKey.t),
orderBy: orderBy?.call(ContentKey.t),
orderByList: orderByList?.call(ContentKey.t),
limit: limit,
offset: offset,
transaction: transaction,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Returns the first matching [ContentKey] 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<ContentKey?> findFirstRow(
_isd.DatabaseSession session, {
_isd.WhereExpressionBuilder<ContentKeyTable>? where,
int? offset,
_isd.OrderByBuilder<ContentKeyTable>? orderBy,
_isd.OrderByListBuilder<ContentKeyTable>? orderByList,
_isd.Transaction? transaction,
_isd.LockMode? lockMode,
_isd.LockBehavior? lockBehavior,
}) async {
return session.db.findFirstRow<ContentKey>(
where: where?.call(ContentKey.t),
orderBy: orderBy?.call(ContentKey.t),
orderByList: orderByList?.call(ContentKey.t),
offset: offset,
transaction: transaction,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Finds a single [ContentKey] by its [id] or null if no such row exists.
Future<ContentKey?> findById(
_isd.DatabaseSession session,
_isc.UuidValue id, {
_isd.Transaction? transaction,
_isd.LockMode? lockMode,
_isd.LockBehavior? lockBehavior,
}) async {
return session.db.findById<ContentKey>(
id,
transaction: transaction,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Inserts all [ContentKey]s in the list and returns the inserted rows.
///
/// The returned [ContentKey]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<ContentKey>> insert(
_isd.DatabaseSession session,
List<ContentKey> rows, {
_isd.Transaction? transaction,
bool ignoreConflicts = false,
bool noReturn = false,
}) async {
return session.db.insert<ContentKey>(
rows,
transaction: transaction,
ignoreConflicts: ignoreConflicts,
noReturn: noReturn,
);
}
/// Inserts a single [ContentKey] and returns the inserted row.
///
/// The returned [ContentKey] will have its `id` field set.
Future<ContentKey> insertRow(
_isd.DatabaseSession session,
ContentKey row, {
_isd.Transaction? transaction,
}) async {
return session.db.insertRow<ContentKey>(
row,
transaction: transaction,
);
}
/// Upserts all [ContentKey]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 [ContentKey]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<ContentKey>> upsert(
_isd.DatabaseSession session,
List<ContentKey> rows, {
required _isd.ColumnSelections<ContentKeyTable> conflictColumns,
_isd.ColumnSelections<ContentKeyTable>? updateColumns,
_isd.WhereExpressionBuilder<ContentKeyTable>? updateWhere,
_isd.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.upsert<ContentKey>(
rows,
conflictColumns: conflictColumns(ContentKey.t),
updateColumns: updateColumns?.call(ContentKey.t),
updateWhere: updateWhere?.call(ContentKey.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Upserts a single [ContentKey] 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 [ContentKey] will have its `id` field set.
Future<ContentKey?> upsertRow(
_isd.DatabaseSession session,
ContentKey row, {
required _isd.ColumnSelections<ContentKeyTable> conflictColumns,
_isd.ColumnSelections<ContentKeyTable>? updateColumns,
_isd.WhereExpressionBuilder<ContentKeyTable>? updateWhere,
_isd.Transaction? transaction,
}) async {
return session.db.upsertRow<ContentKey>(
row,
conflictColumns: conflictColumns(ContentKey.t),
updateColumns: updateColumns?.call(ContentKey.t),
updateWhere: updateWhere?.call(ContentKey.t),
transaction: transaction,
);
}
/// Updates all [ContentKey]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<ContentKey>> update(
_isd.DatabaseSession session,
List<ContentKey> rows, {
_isd.ColumnSelections<ContentKeyTable>? columns,
_isd.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.update<ContentKey>(
rows,
columns: columns?.call(ContentKey.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Updates a single [ContentKey]. 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<ContentKey> updateRow(
_isd.DatabaseSession session,
ContentKey row, {
_isd.ColumnSelections<ContentKeyTable>? columns,
_isd.Transaction? transaction,
}) async {
return session.db.updateRow<ContentKey>(
row,
columns: columns?.call(ContentKey.t),
transaction: transaction,
);
}
/// Updates a single [ContentKey] by its [id] with the specified [columnValues].
/// Returns the updated row or null if no row with the given id exists.
Future<ContentKey?> updateById(
_isd.DatabaseSession session,
_isc.UuidValue id, {
required _isd.ColumnValueListBuilder<ContentKeyUpdateTable> columnValues,
_isd.Transaction? transaction,
}) async {
return session.db.updateById<ContentKey>(
id,
columnValues: columnValues(ContentKey.t.updateTable),
transaction: transaction,
);
}
/// Updates all [ContentKey]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<ContentKey>> updateWhere(
_isd.DatabaseSession session, {
required _isd.ColumnValueListBuilder<ContentKeyUpdateTable> columnValues,
required _isd.WhereExpressionBuilder<ContentKeyTable> where,
int? limit,
int? offset,
_isd.OrderByBuilder<ContentKeyTable>? orderBy,
_isd.OrderByListBuilder<ContentKeyTable>? orderByList,
_isd.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.updateWhere<ContentKey>(
columnValues: columnValues(ContentKey.t.updateTable),
where: where(ContentKey.t),
limit: limit,
offset: offset,
orderBy: orderBy?.call(ContentKey.t),
orderByList: orderByList?.call(ContentKey.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Deletes all [ContentKey]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<ContentKey>> delete(
_isd.DatabaseSession session,
List<ContentKey> rows, {
_isd.OrderByBuilder<ContentKeyTable>? orderBy,
_isd.OrderByListBuilder<ContentKeyTable>? orderByList,
_isd.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.delete<ContentKey>(
rows,
orderBy: orderBy?.call(ContentKey.t),
orderByList: orderByList?.call(ContentKey.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Deletes a single [ContentKey].
Future<ContentKey> deleteRow(
_isd.DatabaseSession session,
ContentKey row, {
_isd.Transaction? transaction,
}) async {
return session.db.deleteRow<ContentKey>(
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<ContentKey>> deleteWhere(
_isd.DatabaseSession session, {
required _isd.WhereExpressionBuilder<ContentKeyTable> where,
_isd.OrderByBuilder<ContentKeyTable>? orderBy,
_isd.OrderByListBuilder<ContentKeyTable>? orderByList,
_isd.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.deleteWhere<ContentKey>(
where: where(ContentKey.t),
orderBy: orderBy?.call(ContentKey.t),
orderByList: orderByList?.call(ContentKey.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(
_isd.DatabaseSession session, {
_isd.WhereExpressionBuilder<ContentKeyTable>? where,
int? limit,
_isd.Transaction? transaction,
}) async {
return session.db.count<ContentKey>(
where: where?.call(ContentKey.t),
limit: limit,
transaction: transaction,
);
}
/// Acquires row-level locks on [ContentKey] rows matching the [where] expression.
Future<void> lockRows(
_isd.DatabaseSession session, {
required _isd.WhereExpressionBuilder<ContentKeyTable> where,
required _isd.LockMode lockMode,
required _isd.Transaction transaction,
_isd.LockBehavior lockBehavior = _isd.LockBehavior.wait,
}) async {
return session.db.lockRows<ContentKey>(
where: where(ContentKey.t),
lockMode: lockMode,
lockBehavior: lockBehavior,
transaction: transaction,
);
}
}

View file

@ -18,15 +18,273 @@ import 'package:serverpod_auth_core_client/serverpod_auth_core_client.dart'
import 'package:serverpod_auth_idp_client/serverpod_auth_idp_client.dart'
as _iaic;
import 'package:serverpod_client/serverpod_client.dart' as _isc;
import 'package:serverpod_database/serverpod_database.dart' as _isd;
import 'package:serverpod_offline_sync_client/serverpod_offline_sync_client.dart'
as _ipulbpi2;
import 'privacy/content_key.dart' as _imed3x5d;
import 'workout/workout.dart' as _i5b9b17z;
import 'workout/workout_series.dart' as _i92mefto;
export 'privacy/content_key.dart';
export 'workout/workout.dart';
export 'workout/workout_series.dart';
export 'client.dart';
export 'sync_tables.dart';
class Protocol extends _isc.SerializationManager {
class Protocol extends _isd.DatabaseSerializationManager {
Protocol._();
factory Protocol() => _instance;
static final Protocol _instance = Protocol._().._registerHostProtocols();
static List<_isd.TableDefinition> get targetTableDefinitions => [
_isd.TableDefinition(
name: 'content_key',
dartName: 'ContentKey',
schema: 'public',
module: 'halkyon_train',
columns: [
_isd.ColumnDefinition(
name: 'id',
columnType: _isd.ColumnType.uuid,
isNullable: false,
dartType: 'UuidValue?',
columnDefault: 'random_v7',
),
_isd.ColumnDefinition(
name: 'spaceId',
columnType: _isd.ColumnType.bigint,
isNullable: true,
dartType: 'int?',
),
_isd.ColumnDefinition(
name: 'dataKeyId',
columnType: _isd.ColumnType.text,
isNullable: false,
dartType: 'String',
),
_isd.ColumnDefinition(
name: 'sealedKey',
columnType: _isd.ColumnType.bytea,
isNullable: false,
dartType: 'dart:typed_data:ByteData',
),
],
foreignKeys: [
_isd.ForeignKeyDefinition(
constraintName: 'content_key_fk_0',
columns: ['spaceId'],
referenceTable: 'offline_sync_spaces',
referenceTableSchema: 'public',
referenceColumns: ['id'],
onUpdate: _isd.ForeignKeyAction.noAction,
onDelete: _isd.ForeignKeyAction.cascade,
matchType: null,
),
],
indexes: [
_isd.IndexDefinition(
indexName: 'content_key_space_idx',
tableSpace: null,
elements: [
_isd.IndexElementDefinition(
type: _isd.IndexElementDefinitionType.column,
definition: 'spaceId',
),
],
type: 'btree',
isUnique: false,
isPrimary: false,
),
],
managed: true,
),
_isd.TableDefinition(
name: 'workout',
dartName: 'Workout',
schema: 'public',
module: 'halkyon_train',
columns: [
_isd.ColumnDefinition(
name: 'id',
columnType: _isd.ColumnType.uuid,
isNullable: false,
dartType: 'UuidValue?',
columnDefault: 'random_v7',
),
_isd.ColumnDefinition(
name: 'spaceId',
columnType: _isd.ColumnType.bigint,
isNullable: true,
dartType: 'int?',
),
_isd.ColumnDefinition(
name: 'keyId',
columnType: _isd.ColumnType.text,
isNullable: true,
dartType: 'String?',
),
_isd.ColumnDefinition(
name: 'payload',
columnType: _isd.ColumnType.bytea,
isNullable: false,
dartType: 'dart:typed_data:ByteData',
),
_isd.ColumnDefinition(
name: 'nonce',
columnType: _isd.ColumnType.bytea,
isNullable: true,
dartType: 'dart:typed_data:ByteData?',
),
],
foreignKeys: [
_isd.ForeignKeyDefinition(
constraintName: 'workout_fk_0',
columns: ['spaceId'],
referenceTable: 'offline_sync_spaces',
referenceTableSchema: 'public',
referenceColumns: ['id'],
onUpdate: _isd.ForeignKeyAction.noAction,
onDelete: _isd.ForeignKeyAction.cascade,
matchType: null,
),
],
indexes: [
_isd.IndexDefinition(
indexName: 'workout_space_idx',
tableSpace: null,
elements: [
_isd.IndexElementDefinition(
type: _isd.IndexElementDefinitionType.column,
definition: 'spaceId',
),
],
type: 'btree',
isUnique: false,
isPrimary: false,
),
],
managed: true,
),
_isd.TableDefinition(
name: 'workout_series',
dartName: 'WorkoutSeries',
schema: 'public',
module: 'halkyon_train',
columns: [
_isd.ColumnDefinition(
name: 'id',
columnType: _isd.ColumnType.uuid,
isNullable: false,
dartType: 'UuidValue?',
columnDefault: 'random_v7',
),
_isd.ColumnDefinition(
name: 'spaceId',
columnType: _isd.ColumnType.bigint,
isNullable: true,
dartType: 'int?',
),
_isd.ColumnDefinition(
name: 'workoutId',
columnType: _isd.ColumnType.uuid,
isNullable: false,
dartType: 'UuidValue',
),
_isd.ColumnDefinition(
name: 'keyId',
columnType: _isd.ColumnType.text,
isNullable: true,
dartType: 'String?',
),
_isd.ColumnDefinition(
name: 'payload',
columnType: _isd.ColumnType.bytea,
isNullable: false,
dartType: 'dart:typed_data:ByteData',
),
_isd.ColumnDefinition(
name: 'nonce',
columnType: _isd.ColumnType.bytea,
isNullable: true,
dartType: 'dart:typed_data:ByteData?',
),
],
foreignKeys: [
_isd.ForeignKeyDefinition(
constraintName: 'workout_series_fk_0',
columns: ['spaceId'],
referenceTable: 'offline_sync_spaces',
referenceTableSchema: 'public',
referenceColumns: ['id'],
onUpdate: _isd.ForeignKeyAction.noAction,
onDelete: _isd.ForeignKeyAction.cascade,
matchType: null,
),
_isd.ForeignKeyDefinition(
constraintName: 'workout_series_fk_1',
columns: ['workoutId'],
referenceTable: 'workout',
referenceTableSchema: 'public',
referenceColumns: ['id'],
onUpdate: _isd.ForeignKeyAction.noAction,
onDelete: _isd.ForeignKeyAction.cascade,
matchType: null,
deferrable: _isd.DeferrableConstraint.initiallyDeferred,
),
],
indexes: [
_isd.IndexDefinition(
indexName: 'workout_series_space_idx',
tableSpace: null,
elements: [
_isd.IndexElementDefinition(
type: _isd.IndexElementDefinitionType.column,
definition: 'spaceId',
),
],
type: 'btree',
isUnique: false,
isPrimary: false,
),
_isd.IndexDefinition(
indexName: 'workout_series_workout_idx',
tableSpace: null,
elements: [
_isd.IndexElementDefinition(
type: _isd.IndexElementDefinitionType.column,
definition: 'workoutId',
),
],
type: 'btree',
isUnique: false,
isPrimary: false,
),
],
managed: true,
),
..._ihk4p5t7.Protocol() is _isd.DatabaseSerializationManager
? (_ihk4p5t7.Protocol() as _isd.DatabaseSerializationManager)
.getTargetTableDefinitions()
: [],
..._iaic.Protocol() is _isd.DatabaseSerializationManager
? (_iaic.Protocol() as _isd.DatabaseSerializationManager)
.getTargetTableDefinitions()
: [],
..._ipulbpi2.Protocol() is _isd.DatabaseSerializationManager
? (_ipulbpi2.Protocol() as _isd.DatabaseSerializationManager)
.getTargetTableDefinitions()
: [],
..._ibkpwzgl.Protocol() is _isd.DatabaseSerializationManager
? (_ibkpwzgl.Protocol() as _isd.DatabaseSerializationManager)
.getTargetTableDefinitions()
: [],
..._iacc.Protocol() is _isd.DatabaseSerializationManager
? (_iacc.Protocol() as _isd.DatabaseSerializationManager)
.getTargetTableDefinitions()
: [],
];
static String? getClassNameFromObjectJson(dynamic data) {
if (data is! Map) return null;
final className = data['__className__'] as String?;
@ -54,12 +312,34 @@ class Protocol extends _isc.SerializationManager {
}
}
if (t == _imed3x5d.ContentKey) {
return _imed3x5d.ContentKey.fromJson(data) as T;
}
if (t == _i5b9b17z.Workout) {
return _i5b9b17z.Workout.fromJson(data) as T;
}
if (t == _i92mefto.WorkoutSeries) {
return _i92mefto.WorkoutSeries.fromJson(data) as T;
}
if (t == _isc.getType<_imed3x5d.ContentKey?>()) {
return (data != null ? _imed3x5d.ContentKey.fromJson(data) : null) as T;
}
if (t == _isc.getType<_i5b9b17z.Workout?>()) {
return (data != null ? _i5b9b17z.Workout.fromJson(data) : null) as T;
}
if (t == _isc.getType<_i92mefto.WorkoutSeries?>()) {
return (data != null ? _i92mefto.WorkoutSeries.fromJson(data) : null)
as T;
}
try {
return _ihk4p5t7.Protocol().deserialize<T>(data, t);
} on _isc.DeserializationTypeNotFoundException catch (_) {}
try {
return _iaic.Protocol().deserialize<T>(data, t);
} on _isc.DeserializationTypeNotFoundException catch (_) {}
try {
return _ipulbpi2.Protocol().deserialize<T>(data, t);
} on _isc.DeserializationTypeNotFoundException catch (_) {}
try {
return _ibkpwzgl.Protocol().deserialize<T>(data, t);
} on _isc.DeserializationTypeNotFoundException catch (_) {}
@ -71,6 +351,9 @@ class Protocol extends _isc.SerializationManager {
static String? getClassNameForType(Type type) {
return switch (type) {
_imed3x5d.ContentKey => 'ContentKey',
_i5b9b17z.Workout => 'Workout',
_i92mefto.WorkoutSeries => 'WorkoutSeries',
_ => null,
};
}
@ -87,6 +370,14 @@ class Protocol extends _isc.SerializationManager {
);
}
switch (data) {
case _imed3x5d.ContentKey():
return 'ContentKey';
case _i5b9b17z.Workout():
return 'Workout';
case _i92mefto.WorkoutSeries():
return 'WorkoutSeries';
}
className = _ihk4p5t7.Protocol().getClassNameForObject(data);
if (className != null) {
return className.contains('.') ? className : 'halkyon_base.$className';
@ -97,6 +388,12 @@ class Protocol extends _isc.SerializationManager {
? className
: 'serverpod_auth_idp.$className';
}
className = _ipulbpi2.Protocol().getClassNameForObject(data);
if (className != null) {
return className.contains('.')
? className
: 'serverpod_offline_sync.$className';
}
className = _ibkpwzgl.Protocol().getClassNameForObject(data);
if (className != null) {
return className.contains('.') ? className : 'halkyon_oidc.$className';
@ -116,6 +413,15 @@ class Protocol extends _isc.SerializationManager {
if (dataClassName is! String) {
return super.deserializeByClassName(data);
}
if (dataClassName == 'ContentKey') {
return deserialize<_imed3x5d.ContentKey>(data['data']);
}
if (dataClassName == 'Workout') {
return deserialize<_i5b9b17z.Workout>(data['data']);
}
if (dataClassName == 'WorkoutSeries') {
return deserialize<_i92mefto.WorkoutSeries>(data['data']);
}
if (dataClassName.startsWith('halkyon_base.')) {
data['className'] = dataClassName.substring(13);
return _ihk4p5t7.Protocol().deserializeByClassName(data);
@ -124,6 +430,10 @@ class Protocol extends _isc.SerializationManager {
data['className'] = dataClassName.substring(19);
return _iaic.Protocol().deserializeByClassName(data);
}
if (dataClassName.startsWith('serverpod_offline_sync.')) {
data['className'] = dataClassName.substring(23);
return _ipulbpi2.Protocol().deserializeByClassName(data);
}
if (dataClassName.startsWith('halkyon_oidc.')) {
data['className'] = dataClassName.substring(13);
return _ibkpwzgl.Protocol().deserializeByClassName(data);
@ -138,10 +448,73 @@ class Protocol extends _isc.SerializationManager {
void _registerHostProtocols() {
_ihk4p5t7.Protocol().registerHostProtocol('halkyon_train', this);
_iaic.Protocol().registerHostProtocol('halkyon_train', this);
_ipulbpi2.Protocol().registerHostProtocol('halkyon_train', this);
_ibkpwzgl.Protocol().registerHostProtocol('halkyon_train', this);
_iacc.Protocol().registerHostProtocol('halkyon_train', this);
}
@override
_isd.Table? getTableForType(Type t) {
{
var protocol = _ihk4p5t7.Protocol();
var table = protocol is _isd.DatabaseSerializationManager
? (protocol as _isd.DatabaseSerializationManager).getTableForType(t)
: null;
if (table != null) {
return table;
}
}
{
var protocol = _iaic.Protocol();
var table = protocol is _isd.DatabaseSerializationManager
? (protocol as _isd.DatabaseSerializationManager).getTableForType(t)
: null;
if (table != null) {
return table;
}
}
{
var protocol = _ipulbpi2.Protocol();
var table = protocol is _isd.DatabaseSerializationManager
? (protocol as _isd.DatabaseSerializationManager).getTableForType(t)
: null;
if (table != null) {
return table;
}
}
{
var protocol = _ibkpwzgl.Protocol();
var table = protocol is _isd.DatabaseSerializationManager
? (protocol as _isd.DatabaseSerializationManager).getTableForType(t)
: null;
if (table != null) {
return table;
}
}
{
var protocol = _iacc.Protocol();
var table = protocol is _isd.DatabaseSerializationManager
? (protocol as _isd.DatabaseSerializationManager).getTableForType(t)
: null;
if (table != null) {
return table;
}
}
switch (t) {
case _imed3x5d.ContentKey:
return _imed3x5d.ContentKey.t;
case _i5b9b17z.Workout:
return _i5b9b17z.Workout.t;
case _i92mefto.WorkoutSeries:
return _i92mefto.WorkoutSeries.t;
}
return null;
}
@override
List<_isd.TableDefinition> getTargetTableDefinitions() =>
targetTableDefinitions;
@override
String getModuleName() => 'halkyon_train';
@ -160,6 +533,9 @@ class Protocol extends _isc.SerializationManager {
try {
return _iaic.Protocol().mapRecordToJson(record);
} catch (_) {}
try {
return _ipulbpi2.Protocol().mapRecordToJson(record);
} catch (_) {}
try {
return _ibkpwzgl.Protocol().mapRecordToJson(record);
} catch (_) {}

View file

@ -0,0 +1,25 @@
/* 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 'package:serverpod_database/serverpod_database.dart' as _isd;
import 'privacy/content_key.dart' as _imed3x5d;
import 'workout/workout.dart' as _i5b9b17z;
import 'workout/workout_series.dart' as _i92mefto;
/// The tables synchronized between client and server by the
/// `serverpod_offline_sync` package, including the ones owned by modules and
/// shared packages.
final List<_isd.Table> syncTables = [
_imed3x5d.ContentKey.t,
_i5b9b17z.Workout.t,
_i92mefto.WorkoutSeries.t,
];

View file

@ -0,0 +1,674 @@
/* 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_client/serverpod_client.dart' as _isc;
import 'package:serverpod_database/serverpod_database.dart' as _isd;
/// A workout (ADR 0030, D14): a run, a ride, a row. Everything about it is in
/// its sealed payload (D7): its type, when it started and ended, its name,
/// notes and totals; the instance sees that it exists, its size and when it
/// changed. Changed when edited, the last writer winning, as a studyset's
/// payload in Learn; its samples are its series (`workout_series`), which
/// never change. Synced.
abstract class Workout
implements _isd.TableRow<_isc.UuidValue?>, _isc.ProtocolSerialization {
Workout._({
this.id,
this.spaceId,
this.keyId,
required this.payload,
this.nonce,
});
factory Workout({
_isc.UuidValue? id,
int? spaceId,
String? keyId,
required _idt.ByteData payload,
_idt.ByteData? nonce,
}) = _WorkoutImpl;
factory Workout.fromJson(Map<String, dynamic> jsonSerialization) {
return Workout(
id: jsonSerialization['id'] == null
? null
: _isc.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
spaceId: jsonSerialization['spaceId'] as int?,
keyId: jsonSerialization['keyId'] as String?,
payload: _isc.ByteDataJsonExtension.fromJson(
jsonSerialization['payload'],
),
nonce: jsonSerialization['nonce'] == null
? null
: _isc.ByteDataJsonExtension.fromJson(jsonSerialization['nonce']),
);
}
static final t = WorkoutTable();
static const db = WorkoutRepository._();
@override
_isc.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,
/// where everything is sealed.
String? keyId;
/// The content, sealed: UTF-8 JSON with a `schemaVersion` once opened.
_idt.ByteData payload;
/// The sealing's nonce (ADR 0002's envelope).
_idt.ByteData? nonce;
@override
_isd.Table<_isc.UuidValue?> get table => t;
/// Returns a shallow copy of this [Workout]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
Workout copyWith({
_isc.UuidValue? id,
int? spaceId,
String? keyId,
_idt.ByteData? payload,
_idt.ByteData? nonce,
});
@override
Map<String, dynamic> toJson() {
return {
'__className__': 'Workout',
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__': 'Workout',
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 WorkoutInclude include() {
return WorkoutInclude._();
}
static WorkoutIncludeList includeList({
_isd.WhereExpressionBuilder<WorkoutTable>? where,
int? limit,
int? offset,
_isd.OrderByBuilder<WorkoutTable>? orderBy,
_isd.OrderByListBuilder<WorkoutTable>? orderByList,
WorkoutInclude? include,
}) {
return WorkoutIncludeList._(
where: where,
limit: limit,
offset: offset,
orderBy: orderBy?.call(Workout.t),
orderByList: orderByList?.call(Workout.t),
include: include,
);
}
@override
String toString() {
return _isc.SerializationManager.encode(this);
}
}
class _Undefined {}
class _WorkoutImpl extends Workout {
_WorkoutImpl({
_isc.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 [Workout]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
@override
Workout copyWith({
Object? id = _Undefined,
Object? spaceId = _Undefined,
Object? keyId = _Undefined,
_idt.ByteData? payload,
Object? nonce = _Undefined,
}) {
return Workout(
id: id is _isc.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 WorkoutUpdateTable extends _isd.UpdateTable<WorkoutTable> {
WorkoutUpdateTable(super.table);
_isd.ColumnValue<int, int> spaceId(int? value) => _isd.ColumnValue(
table.spaceId,
value,
);
_isd.ColumnValue<String, String> keyId(String? value) => _isd.ColumnValue(
table.keyId,
value,
);
_isd.ColumnValue<_idt.ByteData, _idt.ByteData> payload(_idt.ByteData value) =>
_isd.ColumnValue(
table.payload,
value,
);
_isd.ColumnValue<_idt.ByteData, _idt.ByteData> nonce(_idt.ByteData? value) =>
_isd.ColumnValue(
table.nonce,
value,
);
}
class WorkoutTable extends _isd.Table<_isc.UuidValue?> {
WorkoutTable({super.tableRelation}) : super(tableName: 'workout') {
updateTable = WorkoutUpdateTable(this);
spaceId = _isd.ColumnInt(
'spaceId',
this,
);
keyId = _isd.ColumnString(
'keyId',
this,
);
payload = _isd.ColumnByteData(
'payload',
this,
);
nonce = _isd.ColumnByteData(
'nonce',
this,
);
}
late final WorkoutUpdateTable updateTable;
/// The space owning this row. Maintained by the sync engine.
late final _isd.ColumnInt spaceId;
/// The key it is sealed with (ADR 0002's envelope); never null in Train,
/// where everything is sealed.
late final _isd.ColumnString keyId;
/// The content, sealed: UTF-8 JSON with a `schemaVersion` once opened.
late final _isd.ColumnByteData payload;
/// The sealing's nonce (ADR 0002's envelope).
late final _isd.ColumnByteData nonce;
@override
List<_isd.Column> get columns => [
id,
spaceId,
keyId,
payload,
nonce,
];
}
class WorkoutInclude extends _isd.IncludeObject {
WorkoutInclude._();
@override
Map<String, _isd.Include?> get includes => {};
@override
_isd.Table<_isc.UuidValue?> get table => Workout.t;
}
class WorkoutIncludeList extends _isd.IncludeList {
WorkoutIncludeList._({
_isd.WhereExpressionBuilder<WorkoutTable>? where,
super.limit,
super.offset,
super.orderBy,
super.orderByList,
super.include,
}) {
super.where = where?.call(Workout.t);
}
@override
Map<String, _isd.Include?> get includes => include?.includes ?? {};
@override
_isd.Table<_isc.UuidValue?> get table => Workout.t;
}
class WorkoutRepository {
const WorkoutRepository._();
/// Returns a list of [Workout]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<Workout>> find(
_isd.DatabaseSession session, {
_isd.WhereExpressionBuilder<WorkoutTable>? where,
int? limit,
int? offset,
_isd.OrderByBuilder<WorkoutTable>? orderBy,
_isd.OrderByListBuilder<WorkoutTable>? orderByList,
_isd.Transaction? transaction,
_isd.LockMode? lockMode,
_isd.LockBehavior? lockBehavior,
}) async {
return session.db.find<Workout>(
where: where?.call(Workout.t),
orderBy: orderBy?.call(Workout.t),
orderByList: orderByList?.call(Workout.t),
limit: limit,
offset: offset,
transaction: transaction,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Returns the first matching [Workout] 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<Workout?> findFirstRow(
_isd.DatabaseSession session, {
_isd.WhereExpressionBuilder<WorkoutTable>? where,
int? offset,
_isd.OrderByBuilder<WorkoutTable>? orderBy,
_isd.OrderByListBuilder<WorkoutTable>? orderByList,
_isd.Transaction? transaction,
_isd.LockMode? lockMode,
_isd.LockBehavior? lockBehavior,
}) async {
return session.db.findFirstRow<Workout>(
where: where?.call(Workout.t),
orderBy: orderBy?.call(Workout.t),
orderByList: orderByList?.call(Workout.t),
offset: offset,
transaction: transaction,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Finds a single [Workout] by its [id] or null if no such row exists.
Future<Workout?> findById(
_isd.DatabaseSession session,
_isc.UuidValue id, {
_isd.Transaction? transaction,
_isd.LockMode? lockMode,
_isd.LockBehavior? lockBehavior,
}) async {
return session.db.findById<Workout>(
id,
transaction: transaction,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Inserts all [Workout]s in the list and returns the inserted rows.
///
/// The returned [Workout]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<Workout>> insert(
_isd.DatabaseSession session,
List<Workout> rows, {
_isd.Transaction? transaction,
bool ignoreConflicts = false,
bool noReturn = false,
}) async {
return session.db.insert<Workout>(
rows,
transaction: transaction,
ignoreConflicts: ignoreConflicts,
noReturn: noReturn,
);
}
/// Inserts a single [Workout] and returns the inserted row.
///
/// The returned [Workout] will have its `id` field set.
Future<Workout> insertRow(
_isd.DatabaseSession session,
Workout row, {
_isd.Transaction? transaction,
}) async {
return session.db.insertRow<Workout>(
row,
transaction: transaction,
);
}
/// Upserts all [Workout]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 [Workout]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<Workout>> upsert(
_isd.DatabaseSession session,
List<Workout> rows, {
required _isd.ColumnSelections<WorkoutTable> conflictColumns,
_isd.ColumnSelections<WorkoutTable>? updateColumns,
_isd.WhereExpressionBuilder<WorkoutTable>? updateWhere,
_isd.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.upsert<Workout>(
rows,
conflictColumns: conflictColumns(Workout.t),
updateColumns: updateColumns?.call(Workout.t),
updateWhere: updateWhere?.call(Workout.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Upserts a single [Workout] 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 [Workout] will have its `id` field set.
Future<Workout?> upsertRow(
_isd.DatabaseSession session,
Workout row, {
required _isd.ColumnSelections<WorkoutTable> conflictColumns,
_isd.ColumnSelections<WorkoutTable>? updateColumns,
_isd.WhereExpressionBuilder<WorkoutTable>? updateWhere,
_isd.Transaction? transaction,
}) async {
return session.db.upsertRow<Workout>(
row,
conflictColumns: conflictColumns(Workout.t),
updateColumns: updateColumns?.call(Workout.t),
updateWhere: updateWhere?.call(Workout.t),
transaction: transaction,
);
}
/// Updates all [Workout]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<Workout>> update(
_isd.DatabaseSession session,
List<Workout> rows, {
_isd.ColumnSelections<WorkoutTable>? columns,
_isd.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.update<Workout>(
rows,
columns: columns?.call(Workout.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Updates a single [Workout]. 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<Workout> updateRow(
_isd.DatabaseSession session,
Workout row, {
_isd.ColumnSelections<WorkoutTable>? columns,
_isd.Transaction? transaction,
}) async {
return session.db.updateRow<Workout>(
row,
columns: columns?.call(Workout.t),
transaction: transaction,
);
}
/// Updates a single [Workout] by its [id] with the specified [columnValues].
/// Returns the updated row or null if no row with the given id exists.
Future<Workout?> updateById(
_isd.DatabaseSession session,
_isc.UuidValue id, {
required _isd.ColumnValueListBuilder<WorkoutUpdateTable> columnValues,
_isd.Transaction? transaction,
}) async {
return session.db.updateById<Workout>(
id,
columnValues: columnValues(Workout.t.updateTable),
transaction: transaction,
);
}
/// Updates all [Workout]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<Workout>> updateWhere(
_isd.DatabaseSession session, {
required _isd.ColumnValueListBuilder<WorkoutUpdateTable> columnValues,
required _isd.WhereExpressionBuilder<WorkoutTable> where,
int? limit,
int? offset,
_isd.OrderByBuilder<WorkoutTable>? orderBy,
_isd.OrderByListBuilder<WorkoutTable>? orderByList,
_isd.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.updateWhere<Workout>(
columnValues: columnValues(Workout.t.updateTable),
where: where(Workout.t),
limit: limit,
offset: offset,
orderBy: orderBy?.call(Workout.t),
orderByList: orderByList?.call(Workout.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Deletes all [Workout]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<Workout>> delete(
_isd.DatabaseSession session,
List<Workout> rows, {
_isd.OrderByBuilder<WorkoutTable>? orderBy,
_isd.OrderByListBuilder<WorkoutTable>? orderByList,
_isd.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.delete<Workout>(
rows,
orderBy: orderBy?.call(Workout.t),
orderByList: orderByList?.call(Workout.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Deletes a single [Workout].
Future<Workout> deleteRow(
_isd.DatabaseSession session,
Workout row, {
_isd.Transaction? transaction,
}) async {
return session.db.deleteRow<Workout>(
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<Workout>> deleteWhere(
_isd.DatabaseSession session, {
required _isd.WhereExpressionBuilder<WorkoutTable> where,
_isd.OrderByBuilder<WorkoutTable>? orderBy,
_isd.OrderByListBuilder<WorkoutTable>? orderByList,
_isd.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.deleteWhere<Workout>(
where: where(Workout.t),
orderBy: orderBy?.call(Workout.t),
orderByList: orderByList?.call(Workout.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(
_isd.DatabaseSession session, {
_isd.WhereExpressionBuilder<WorkoutTable>? where,
int? limit,
_isd.Transaction? transaction,
}) async {
return session.db.count<Workout>(
where: where?.call(Workout.t),
limit: limit,
transaction: transaction,
);
}
/// Acquires row-level locks on [Workout] rows matching the [where] expression.
Future<void> lockRows(
_isd.DatabaseSession session, {
required _isd.WhereExpressionBuilder<WorkoutTable> where,
required _isd.LockMode lockMode,
required _isd.Transaction transaction,
_isd.LockBehavior lockBehavior = _isd.LockBehavior.wait,
}) async {
return session.db.lockRows<Workout>(
where: where(Workout.t),
lockMode: lockMode,
lockBehavior: lockBehavior,
transaction: transaction,
);
}
}

View file

@ -0,0 +1,789 @@
/* 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_client/src/protocol/protocol.dart' as _ip4ob8rr;
import 'package:serverpod_client/serverpod_client.dart' as _isc;
import 'package:serverpod_database/serverpod_database.dart' as _isd;
import '../workout/workout.dart' as _ickoe38i;
/// A chunk of one of a workout's series (ADR 0030, D14): up to 10 minutes of
/// positions, heart rate, strokes or pauses, written once as the workout is
/// saved and never changed, as Learn's review log. Its kind, its time and
/// its samples are in its sealed payload (D7): gzipped JSON, each column a
/// list of whole-number differences from the value before, sealed for its
/// workout (`bound`), so the instance can't move it to another. Synced; it
/// goes with its workout.
abstract class WorkoutSeries
implements _isd.TableRow<_isc.UuidValue?>, _isc.ProtocolSerialization {
WorkoutSeries._({
this.id,
this.spaceId,
required this.workoutId,
this.workout,
this.keyId,
required this.payload,
this.nonce,
});
factory WorkoutSeries({
_isc.UuidValue? id,
int? spaceId,
required _isc.UuidValue workoutId,
_ickoe38i.Workout? workout,
String? keyId,
required _idt.ByteData payload,
_idt.ByteData? nonce,
}) = _WorkoutSeriesImpl;
factory WorkoutSeries.fromJson(Map<String, dynamic> jsonSerialization) {
return WorkoutSeries(
id: jsonSerialization['id'] == null
? null
: _isc.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
spaceId: jsonSerialization['spaceId'] as int?,
workoutId: _isc.UuidValueJsonExtension.fromJson(
jsonSerialization['workoutId'],
),
workout: jsonSerialization['workout'] == null
? null
: _ip4ob8rr.Protocol().deserialize<_ickoe38i.Workout>(
jsonSerialization['workout'],
),
keyId: jsonSerialization['keyId'] as String?,
payload: _isc.ByteDataJsonExtension.fromJson(
jsonSerialization['payload'],
),
nonce: jsonSerialization['nonce'] == null
? null
: _isc.ByteDataJsonExtension.fromJson(jsonSerialization['nonce']),
);
}
static final t = WorkoutSeriesTable();
static const db = WorkoutSeriesRepository._();
@override
_isc.UuidValue? id;
/// The space owning this row. Maintained by the sync engine.
int? spaceId;
_isc.UuidValue workoutId;
/// The workout it is part of.
_ickoe38i.Workout? workout;
/// 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
_isd.Table<_isc.UuidValue?> get table => t;
/// Returns a shallow copy of this [WorkoutSeries]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
WorkoutSeries copyWith({
_isc.UuidValue? id,
int? spaceId,
_isc.UuidValue? workoutId,
_ickoe38i.Workout? workout,
String? keyId,
_idt.ByteData? payload,
_idt.ByteData? nonce,
});
@override
Map<String, dynamic> toJson() {
return {
'__className__': 'WorkoutSeries',
if (id != null) 'id': id?.toJson(),
if (spaceId != null) 'spaceId': spaceId,
'workoutId': workoutId.toJson(),
if (workout != null) 'workout': workout?.toJson(),
if (keyId != null) 'keyId': keyId,
'payload': payload.toJson(),
if (nonce != null) 'nonce': nonce?.toJson(),
};
}
@override
Map<String, dynamic> toJsonForProtocol() {
return {
'__className__': 'WorkoutSeries',
if (id != null) 'id': id?.toJson(),
if (spaceId != null) 'spaceId': spaceId,
'workoutId': workoutId.toJson(),
if (workout != null) 'workout': workout?.toJsonForProtocol(),
if (keyId != null) 'keyId': keyId,
'payload': payload.toJson(),
if (nonce != null) 'nonce': nonce?.toJson(),
};
}
static WorkoutSeriesInclude include({_ickoe38i.WorkoutInclude? workout}) {
return WorkoutSeriesInclude._(workout: workout);
}
static WorkoutSeriesIncludeList includeList({
_isd.WhereExpressionBuilder<WorkoutSeriesTable>? where,
int? limit,
int? offset,
_isd.OrderByBuilder<WorkoutSeriesTable>? orderBy,
_isd.OrderByListBuilder<WorkoutSeriesTable>? orderByList,
WorkoutSeriesInclude? include,
}) {
return WorkoutSeriesIncludeList._(
where: where,
limit: limit,
offset: offset,
orderBy: orderBy?.call(WorkoutSeries.t),
orderByList: orderByList?.call(WorkoutSeries.t),
include: include,
);
}
@override
String toString() {
return _isc.SerializationManager.encode(this);
}
}
class _Undefined {}
class _WorkoutSeriesImpl extends WorkoutSeries {
_WorkoutSeriesImpl({
_isc.UuidValue? id,
int? spaceId,
required _isc.UuidValue workoutId,
_ickoe38i.Workout? workout,
String? keyId,
required _idt.ByteData payload,
_idt.ByteData? nonce,
}) : super._(
id: id,
spaceId: spaceId,
workoutId: workoutId,
workout: workout,
keyId: keyId,
payload: payload,
nonce: nonce,
);
/// Returns a shallow copy of this [WorkoutSeries]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
@override
WorkoutSeries copyWith({
Object? id = _Undefined,
Object? spaceId = _Undefined,
_isc.UuidValue? workoutId,
Object? workout = _Undefined,
Object? keyId = _Undefined,
_idt.ByteData? payload,
Object? nonce = _Undefined,
}) {
return WorkoutSeries(
id: id is _isc.UuidValue? ? id : this.id,
spaceId: spaceId is int? ? spaceId : this.spaceId,
workoutId: workoutId ?? this.workoutId,
workout: workout is _ickoe38i.Workout?
? workout
: this.workout?.copyWith(),
keyId: keyId is String? ? keyId : this.keyId,
payload: payload ?? this.payload.clone(),
nonce: nonce is _idt.ByteData? ? nonce : this.nonce?.clone(),
);
}
}
class WorkoutSeriesUpdateTable extends _isd.UpdateTable<WorkoutSeriesTable> {
WorkoutSeriesUpdateTable(super.table);
_isd.ColumnValue<int, int> spaceId(int? value) => _isd.ColumnValue(
table.spaceId,
value,
);
_isd.ColumnValue<_isc.UuidValue, _isc.UuidValue> workoutId(
_isc.UuidValue value,
) => _isd.ColumnValue(
table.workoutId,
value,
);
_isd.ColumnValue<String, String> keyId(String? value) => _isd.ColumnValue(
table.keyId,
value,
);
_isd.ColumnValue<_idt.ByteData, _idt.ByteData> payload(_idt.ByteData value) =>
_isd.ColumnValue(
table.payload,
value,
);
_isd.ColumnValue<_idt.ByteData, _idt.ByteData> nonce(_idt.ByteData? value) =>
_isd.ColumnValue(
table.nonce,
value,
);
}
class WorkoutSeriesTable extends _isd.Table<_isc.UuidValue?> {
WorkoutSeriesTable({super.tableRelation})
: super(tableName: 'workout_series') {
updateTable = WorkoutSeriesUpdateTable(this);
spaceId = _isd.ColumnInt(
'spaceId',
this,
);
workoutId = _isd.ColumnUuid(
'workoutId',
this,
);
keyId = _isd.ColumnString(
'keyId',
this,
);
payload = _isd.ColumnByteData(
'payload',
this,
);
nonce = _isd.ColumnByteData(
'nonce',
this,
);
}
late final WorkoutSeriesUpdateTable updateTable;
/// The space owning this row. Maintained by the sync engine.
late final _isd.ColumnInt spaceId;
late final _isd.ColumnUuid workoutId;
/// The workout it is part of.
_ickoe38i.WorkoutTable? _workout;
/// The key it is sealed with (ADR 0002's envelope); never null in Train.
late final _isd.ColumnString keyId;
/// The content, sealed: gzipped UTF-8 JSON with a `schemaVersion` once
/// opened.
late final _isd.ColumnByteData payload;
/// The sealing's nonce (ADR 0002's envelope).
late final _isd.ColumnByteData nonce;
_ickoe38i.WorkoutTable get workout {
if (_workout != null) return _workout!;
_workout = _isd.createRelationTable(
relationFieldName: 'workout',
field: WorkoutSeries.t.workoutId,
foreignField: _ickoe38i.Workout.t.id,
tableRelation: tableRelation,
createTable: (foreignTableRelation) =>
_ickoe38i.WorkoutTable(tableRelation: foreignTableRelation),
);
return _workout!;
}
@override
List<_isd.Column> get columns => [
id,
spaceId,
workoutId,
keyId,
payload,
nonce,
];
@override
_isd.Table? getRelationTable(String relationField) {
if (relationField == 'workout') {
return workout;
}
return null;
}
}
class WorkoutSeriesInclude extends _isd.IncludeObject {
WorkoutSeriesInclude._({_ickoe38i.WorkoutInclude? workout}) {
_workout = workout;
}
_ickoe38i.WorkoutInclude? _workout;
@override
Map<String, _isd.Include?> get includes => {'workout': _workout};
@override
_isd.Table<_isc.UuidValue?> get table => WorkoutSeries.t;
}
class WorkoutSeriesIncludeList extends _isd.IncludeList {
WorkoutSeriesIncludeList._({
_isd.WhereExpressionBuilder<WorkoutSeriesTable>? where,
super.limit,
super.offset,
super.orderBy,
super.orderByList,
super.include,
}) {
super.where = where?.call(WorkoutSeries.t);
}
@override
Map<String, _isd.Include?> get includes => include?.includes ?? {};
@override
_isd.Table<_isc.UuidValue?> get table => WorkoutSeries.t;
}
class WorkoutSeriesRepository {
const WorkoutSeriesRepository._();
final attachRow = const WorkoutSeriesAttachRowRepository._();
/// Returns a list of [WorkoutSeries]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<WorkoutSeries>> find(
_isd.DatabaseSession session, {
_isd.WhereExpressionBuilder<WorkoutSeriesTable>? where,
int? limit,
int? offset,
_isd.OrderByBuilder<WorkoutSeriesTable>? orderBy,
_isd.OrderByListBuilder<WorkoutSeriesTable>? orderByList,
_isd.Transaction? transaction,
WorkoutSeriesInclude? include,
_isd.LockMode? lockMode,
_isd.LockBehavior? lockBehavior,
}) async {
return session.db.find<WorkoutSeries>(
where: where?.call(WorkoutSeries.t),
orderBy: orderBy?.call(WorkoutSeries.t),
orderByList: orderByList?.call(WorkoutSeries.t),
limit: limit,
offset: offset,
transaction: transaction,
include: include,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Returns the first matching [WorkoutSeries] 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<WorkoutSeries?> findFirstRow(
_isd.DatabaseSession session, {
_isd.WhereExpressionBuilder<WorkoutSeriesTable>? where,
int? offset,
_isd.OrderByBuilder<WorkoutSeriesTable>? orderBy,
_isd.OrderByListBuilder<WorkoutSeriesTable>? orderByList,
_isd.Transaction? transaction,
WorkoutSeriesInclude? include,
_isd.LockMode? lockMode,
_isd.LockBehavior? lockBehavior,
}) async {
return session.db.findFirstRow<WorkoutSeries>(
where: where?.call(WorkoutSeries.t),
orderBy: orderBy?.call(WorkoutSeries.t),
orderByList: orderByList?.call(WorkoutSeries.t),
offset: offset,
transaction: transaction,
include: include,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Finds a single [WorkoutSeries] by its [id] or null if no such row exists.
Future<WorkoutSeries?> findById(
_isd.DatabaseSession session,
_isc.UuidValue id, {
_isd.Transaction? transaction,
WorkoutSeriesInclude? include,
_isd.LockMode? lockMode,
_isd.LockBehavior? lockBehavior,
}) async {
return session.db.findById<WorkoutSeries>(
id,
transaction: transaction,
include: include,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Inserts all [WorkoutSeries]s in the list and returns the inserted rows.
///
/// The returned [WorkoutSeries]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<WorkoutSeries>> insert(
_isd.DatabaseSession session,
List<WorkoutSeries> rows, {
_isd.Transaction? transaction,
bool ignoreConflicts = false,
bool noReturn = false,
}) async {
return session.db.insert<WorkoutSeries>(
rows,
transaction: transaction,
ignoreConflicts: ignoreConflicts,
noReturn: noReturn,
);
}
/// Inserts a single [WorkoutSeries] and returns the inserted row.
///
/// The returned [WorkoutSeries] will have its `id` field set.
Future<WorkoutSeries> insertRow(
_isd.DatabaseSession session,
WorkoutSeries row, {
_isd.Transaction? transaction,
}) async {
return session.db.insertRow<WorkoutSeries>(
row,
transaction: transaction,
);
}
/// Upserts all [WorkoutSeries]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 [WorkoutSeries]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<WorkoutSeries>> upsert(
_isd.DatabaseSession session,
List<WorkoutSeries> rows, {
required _isd.ColumnSelections<WorkoutSeriesTable> conflictColumns,
_isd.ColumnSelections<WorkoutSeriesTable>? updateColumns,
_isd.WhereExpressionBuilder<WorkoutSeriesTable>? updateWhere,
_isd.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.upsert<WorkoutSeries>(
rows,
conflictColumns: conflictColumns(WorkoutSeries.t),
updateColumns: updateColumns?.call(WorkoutSeries.t),
updateWhere: updateWhere?.call(WorkoutSeries.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Upserts a single [WorkoutSeries] 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 [WorkoutSeries] will have its `id` field set.
Future<WorkoutSeries?> upsertRow(
_isd.DatabaseSession session,
WorkoutSeries row, {
required _isd.ColumnSelections<WorkoutSeriesTable> conflictColumns,
_isd.ColumnSelections<WorkoutSeriesTable>? updateColumns,
_isd.WhereExpressionBuilder<WorkoutSeriesTable>? updateWhere,
_isd.Transaction? transaction,
}) async {
return session.db.upsertRow<WorkoutSeries>(
row,
conflictColumns: conflictColumns(WorkoutSeries.t),
updateColumns: updateColumns?.call(WorkoutSeries.t),
updateWhere: updateWhere?.call(WorkoutSeries.t),
transaction: transaction,
);
}
/// Updates all [WorkoutSeries]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<WorkoutSeries>> update(
_isd.DatabaseSession session,
List<WorkoutSeries> rows, {
_isd.ColumnSelections<WorkoutSeriesTable>? columns,
_isd.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.update<WorkoutSeries>(
rows,
columns: columns?.call(WorkoutSeries.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Updates a single [WorkoutSeries]. 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<WorkoutSeries> updateRow(
_isd.DatabaseSession session,
WorkoutSeries row, {
_isd.ColumnSelections<WorkoutSeriesTable>? columns,
_isd.Transaction? transaction,
}) async {
return session.db.updateRow<WorkoutSeries>(
row,
columns: columns?.call(WorkoutSeries.t),
transaction: transaction,
);
}
/// Updates a single [WorkoutSeries] by its [id] with the specified [columnValues].
/// Returns the updated row or null if no row with the given id exists.
Future<WorkoutSeries?> updateById(
_isd.DatabaseSession session,
_isc.UuidValue id, {
required _isd.ColumnValueListBuilder<WorkoutSeriesUpdateTable> columnValues,
_isd.Transaction? transaction,
}) async {
return session.db.updateById<WorkoutSeries>(
id,
columnValues: columnValues(WorkoutSeries.t.updateTable),
transaction: transaction,
);
}
/// Updates all [WorkoutSeries]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<WorkoutSeries>> updateWhere(
_isd.DatabaseSession session, {
required _isd.ColumnValueListBuilder<WorkoutSeriesUpdateTable> columnValues,
required _isd.WhereExpressionBuilder<WorkoutSeriesTable> where,
int? limit,
int? offset,
_isd.OrderByBuilder<WorkoutSeriesTable>? orderBy,
_isd.OrderByListBuilder<WorkoutSeriesTable>? orderByList,
_isd.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.updateWhere<WorkoutSeries>(
columnValues: columnValues(WorkoutSeries.t.updateTable),
where: where(WorkoutSeries.t),
limit: limit,
offset: offset,
orderBy: orderBy?.call(WorkoutSeries.t),
orderByList: orderByList?.call(WorkoutSeries.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Deletes all [WorkoutSeries]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<WorkoutSeries>> delete(
_isd.DatabaseSession session,
List<WorkoutSeries> rows, {
_isd.OrderByBuilder<WorkoutSeriesTable>? orderBy,
_isd.OrderByListBuilder<WorkoutSeriesTable>? orderByList,
_isd.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.delete<WorkoutSeries>(
rows,
orderBy: orderBy?.call(WorkoutSeries.t),
orderByList: orderByList?.call(WorkoutSeries.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Deletes a single [WorkoutSeries].
Future<WorkoutSeries> deleteRow(
_isd.DatabaseSession session,
WorkoutSeries row, {
_isd.Transaction? transaction,
}) async {
return session.db.deleteRow<WorkoutSeries>(
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<WorkoutSeries>> deleteWhere(
_isd.DatabaseSession session, {
required _isd.WhereExpressionBuilder<WorkoutSeriesTable> where,
_isd.OrderByBuilder<WorkoutSeriesTable>? orderBy,
_isd.OrderByListBuilder<WorkoutSeriesTable>? orderByList,
_isd.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.deleteWhere<WorkoutSeries>(
where: where(WorkoutSeries.t),
orderBy: orderBy?.call(WorkoutSeries.t),
orderByList: orderByList?.call(WorkoutSeries.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(
_isd.DatabaseSession session, {
_isd.WhereExpressionBuilder<WorkoutSeriesTable>? where,
int? limit,
_isd.Transaction? transaction,
}) async {
return session.db.count<WorkoutSeries>(
where: where?.call(WorkoutSeries.t),
limit: limit,
transaction: transaction,
);
}
/// Acquires row-level locks on [WorkoutSeries] rows matching the [where] expression.
Future<void> lockRows(
_isd.DatabaseSession session, {
required _isd.WhereExpressionBuilder<WorkoutSeriesTable> where,
required _isd.LockMode lockMode,
required _isd.Transaction transaction,
_isd.LockBehavior lockBehavior = _isd.LockBehavior.wait,
}) async {
return session.db.lockRows<WorkoutSeries>(
where: where(WorkoutSeries.t),
lockMode: lockMode,
lockBehavior: lockBehavior,
transaction: transaction,
);
}
}
class WorkoutSeriesAttachRowRepository {
const WorkoutSeriesAttachRowRepository._();
/// Creates a relation between the given [WorkoutSeries] and [Workout]
/// by setting the [WorkoutSeries]'s foreign key `workoutId` to refer to the [Workout].
Future<void> workout(
_isd.DatabaseSession session,
WorkoutSeries workoutSeries,
_ickoe38i.Workout workout, {
_isd.Transaction? transaction,
}) async {
if (workoutSeries.id == null) {
throw ArgumentError.notNull('workoutSeries.id');
}
if (workout.id == null) {
throw ArgumentError.notNull('workout.id');
}
var $workoutSeries = workoutSeries.copyWith(workoutId: workout.id);
await session.db.updateRow<WorkoutSeries>(
$workoutSeries,
columns: [WorkoutSeries.t.workoutId],
transaction: transaction,
);
}
}

View file

@ -13,3 +13,4 @@ dependencies:
path: ../../../packages/halkyon_base_client
serverpod_auth_idp_client: 4.0.4
serverpod_client: 4.0.4
serverpod_offline_sync_client: 0.0.10

View file

@ -40,6 +40,7 @@ dependencies:
path_provider: ^2.1.6
serverpod_auth_idp_flutter: 4.0.4
serverpod_flutter: 4.0.4
serverpod_offline_sync_client: 0.0.10
dev_dependencies:
flutter_driver:

View file

@ -3,7 +3,14 @@ type: server
client_package_path: ../halkyon_train_client
server_test_tools_path: test/integration/test_tools
# Private data sync through serverpod_offline_sync (ADR 0001, ADR 0030's D12): its
# module, and the experimental `database: sync` for the models it syncs.
modules:
serverpod_offline_sync:
nickname: offline_sync
# What every Halkyon server has (ADR 0014): `client.modules.base` in the app.
halkyon_base:
nickname: base
experimental_features:
databaseSync: true

View file

@ -19,6 +19,8 @@ import 'package:serverpod_auth_core_server/serverpod_auth_core_server.dart'
as _iacs;
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;
export 'future_calls.dart' show ServerpodFutureCallsGetter;
class Endpoints extends _is.EndpointDispatch {
@ -28,6 +30,8 @@ class Endpoints extends _is.EndpointDispatch {
..initializeEndpoints(server);
modules['serverpod_auth_idp'] = _iais.Endpoints()
..initializeEndpoints(server);
modules['serverpod_offline_sync'] = _izehhkf5.Endpoints()
..initializeEndpoints(server);
modules['halkyon_oidc'] = _ipoqfiqr.Endpoints()
..initializeEndpoints(server);
modules['serverpod_auth_core'] = _iacs.Endpoints()

View file

@ -0,0 +1,646 @@
/* 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 data key of the person's (ADR 0030, D7 and D15): the one their workouts
/// and everything else of Train's are sealed with. Sealed on a device to the
/// account's X25519 key and signed by it (halkyon_keys' `sealDataKey`), under
/// an ID that says what it is for, so only the person's devices open it and
/// the instance can't relabel it; the instance keeps it as it is. Synced, so
/// the key arrives with the data, works offline and goes with the space.
/// Never updated.
abstract class ContentKey
implements _is.TableRow<_is.UuidValue?>, _is.ProtocolSerialization {
ContentKey._({
this.id,
this.spaceId,
required this.dataKeyId,
required this.sealedKey,
});
factory ContentKey({
_is.UuidValue? id,
int? spaceId,
required String dataKeyId,
required _idt.ByteData sealedKey,
}) = _ContentKeyImpl;
factory ContentKey.fromJson(Map<String, dynamic> jsonSerialization) {
return ContentKey(
id: jsonSerialization['id'] == null
? null
: _is.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
spaceId: jsonSerialization['spaceId'] as int?,
dataKeyId: jsonSerialization['dataKeyId'] as String,
sealedKey: _is.ByteDataJsonExtension.fromJson(
jsonSerialization['sealedKey'],
),
);
}
static final t = ContentKeyTable();
static const db = ContentKeyRepository._();
@override
_is.UuidValue? id;
/// The space owning this row. Maintained by the sync engine.
int? spaceId;
/// The key's ID, which the rows it seals name in their `keyId`; sealed
/// with the key too, so the column is never trusted.
String dataKeyId;
/// The sealed key: the sealer's Ed25519 signature, then the sealed box.
_idt.ByteData sealedKey;
@override
_is.Table<_is.UuidValue?> get table => t;
/// Returns a shallow copy of this [ContentKey]
/// with some or all fields replaced by the given arguments.
@_is.useResult
ContentKey copyWith({
_is.UuidValue? id,
int? spaceId,
String? dataKeyId,
_idt.ByteData? sealedKey,
});
@override
Map<String, dynamic> toJson() {
return {
'__className__': 'ContentKey',
if (id != null) 'id': id?.toJson(),
if (spaceId != null) 'spaceId': spaceId,
'dataKeyId': dataKeyId,
'sealedKey': sealedKey.toJson(),
};
}
@override
Map<String, dynamic> toJsonForProtocol() {
return {
'__className__': 'ContentKey',
if (id != null) 'id': id?.toJson(),
if (spaceId != null) 'spaceId': spaceId,
'dataKeyId': dataKeyId,
'sealedKey': sealedKey.toJson(),
};
}
static ContentKeyInclude include() {
return ContentKeyInclude._();
}
static ContentKeyIncludeList includeList({
_is.WhereExpressionBuilder<ContentKeyTable>? where,
int? limit,
int? offset,
_is.OrderByBuilder<ContentKeyTable>? orderBy,
_is.OrderByListBuilder<ContentKeyTable>? orderByList,
ContentKeyInclude? include,
}) {
return ContentKeyIncludeList._(
where: where,
limit: limit,
offset: offset,
orderBy: orderBy?.call(ContentKey.t),
orderByList: orderByList?.call(ContentKey.t),
include: include,
);
}
@override
String toString() {
return _is.SerializationManager.encode(this);
}
}
class _Undefined {}
class _ContentKeyImpl extends ContentKey {
_ContentKeyImpl({
_is.UuidValue? id,
int? spaceId,
required String dataKeyId,
required _idt.ByteData sealedKey,
}) : super._(
id: id,
spaceId: spaceId,
dataKeyId: dataKeyId,
sealedKey: sealedKey,
);
/// Returns a shallow copy of this [ContentKey]
/// with some or all fields replaced by the given arguments.
@_is.useResult
@override
ContentKey copyWith({
Object? id = _Undefined,
Object? spaceId = _Undefined,
String? dataKeyId,
_idt.ByteData? sealedKey,
}) {
return ContentKey(
id: id is _is.UuidValue? ? id : this.id,
spaceId: spaceId is int? ? spaceId : this.spaceId,
dataKeyId: dataKeyId ?? this.dataKeyId,
sealedKey: sealedKey ?? this.sealedKey.clone(),
);
}
}
class ContentKeyUpdateTable extends _is.UpdateTable<ContentKeyTable> {
ContentKeyUpdateTable(super.table);
_is.ColumnValue<int, int> spaceId(int? value) => _is.ColumnValue(
table.spaceId,
value,
);
_is.ColumnValue<String, String> dataKeyId(String value) => _is.ColumnValue(
table.dataKeyId,
value,
);
_is.ColumnValue<_idt.ByteData, _idt.ByteData> sealedKey(
_idt.ByteData value,
) => _is.ColumnValue(
table.sealedKey,
value,
);
}
class ContentKeyTable extends _is.Table<_is.UuidValue?> {
ContentKeyTable({super.tableRelation}) : super(tableName: 'content_key') {
updateTable = ContentKeyUpdateTable(this);
spaceId = _is.ColumnInt(
'spaceId',
this,
);
dataKeyId = _is.ColumnString(
'dataKeyId',
this,
);
sealedKey = _is.ColumnByteData(
'sealedKey',
this,
);
}
late final ContentKeyUpdateTable updateTable;
/// The space owning this row. Maintained by the sync engine.
late final _is.ColumnInt spaceId;
/// The key's ID, which the rows it seals name in their `keyId`; sealed
/// with the key too, so the column is never trusted.
late final _is.ColumnString dataKeyId;
/// The sealed key: the sealer's Ed25519 signature, then the sealed box.
late final _is.ColumnByteData sealedKey;
@override
List<_is.Column> get columns => [
id,
spaceId,
dataKeyId,
sealedKey,
];
}
class ContentKeyInclude extends _is.IncludeObject {
ContentKeyInclude._();
@override
Map<String, _is.Include?> get includes => {};
@override
_is.Table<_is.UuidValue?> get table => ContentKey.t;
}
class ContentKeyIncludeList extends _is.IncludeList {
ContentKeyIncludeList._({
_is.WhereExpressionBuilder<ContentKeyTable>? where,
super.limit,
super.offset,
super.orderBy,
super.orderByList,
super.include,
}) {
super.where = where?.call(ContentKey.t);
}
@override
Map<String, _is.Include?> get includes => include?.includes ?? {};
@override
_is.Table<_is.UuidValue?> get table => ContentKey.t;
}
class ContentKeyRepository {
const ContentKeyRepository._();
/// Returns a list of [ContentKey]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<ContentKey>> find(
_is.DatabaseSession session, {
_is.WhereExpressionBuilder<ContentKeyTable>? where,
int? limit,
int? offset,
_is.OrderByBuilder<ContentKeyTable>? orderBy,
_is.OrderByListBuilder<ContentKeyTable>? orderByList,
_is.Transaction? transaction,
_is.LockMode? lockMode,
_is.LockBehavior? lockBehavior,
}) async {
return session.db.find<ContentKey>(
where: where?.call(ContentKey.t),
orderBy: orderBy?.call(ContentKey.t),
orderByList: orderByList?.call(ContentKey.t),
limit: limit,
offset: offset,
transaction: transaction,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Returns the first matching [ContentKey] 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<ContentKey?> findFirstRow(
_is.DatabaseSession session, {
_is.WhereExpressionBuilder<ContentKeyTable>? where,
int? offset,
_is.OrderByBuilder<ContentKeyTable>? orderBy,
_is.OrderByListBuilder<ContentKeyTable>? orderByList,
_is.Transaction? transaction,
_is.LockMode? lockMode,
_is.LockBehavior? lockBehavior,
}) async {
return session.db.findFirstRow<ContentKey>(
where: where?.call(ContentKey.t),
orderBy: orderBy?.call(ContentKey.t),
orderByList: orderByList?.call(ContentKey.t),
offset: offset,
transaction: transaction,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Finds a single [ContentKey] by its [id] or null if no such row exists.
Future<ContentKey?> findById(
_is.DatabaseSession session,
_is.UuidValue id, {
_is.Transaction? transaction,
_is.LockMode? lockMode,
_is.LockBehavior? lockBehavior,
}) async {
return session.db.findById<ContentKey>(
id,
transaction: transaction,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Inserts all [ContentKey]s in the list and returns the inserted rows.
///
/// The returned [ContentKey]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<ContentKey>> insert(
_is.DatabaseSession session,
List<ContentKey> rows, {
_is.Transaction? transaction,
bool ignoreConflicts = false,
bool noReturn = false,
}) async {
return session.db.insert<ContentKey>(
rows,
transaction: transaction,
ignoreConflicts: ignoreConflicts,
noReturn: noReturn,
);
}
/// Inserts a single [ContentKey] and returns the inserted row.
///
/// The returned [ContentKey] will have its `id` field set.
Future<ContentKey> insertRow(
_is.DatabaseSession session,
ContentKey row, {
_is.Transaction? transaction,
}) async {
return session.db.insertRow<ContentKey>(
row,
transaction: transaction,
);
}
/// Upserts all [ContentKey]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 [ContentKey]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<ContentKey>> upsert(
_is.DatabaseSession session,
List<ContentKey> rows, {
required _is.ColumnSelections<ContentKeyTable> conflictColumns,
_is.ColumnSelections<ContentKeyTable>? updateColumns,
_is.WhereExpressionBuilder<ContentKeyTable>? updateWhere,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.upsert<ContentKey>(
rows,
conflictColumns: conflictColumns(ContentKey.t),
updateColumns: updateColumns?.call(ContentKey.t),
updateWhere: updateWhere?.call(ContentKey.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Upserts a single [ContentKey] 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 [ContentKey] will have its `id` field set.
Future<ContentKey?> upsertRow(
_is.DatabaseSession session,
ContentKey row, {
required _is.ColumnSelections<ContentKeyTable> conflictColumns,
_is.ColumnSelections<ContentKeyTable>? updateColumns,
_is.WhereExpressionBuilder<ContentKeyTable>? updateWhere,
_is.Transaction? transaction,
}) async {
return session.db.upsertRow<ContentKey>(
row,
conflictColumns: conflictColumns(ContentKey.t),
updateColumns: updateColumns?.call(ContentKey.t),
updateWhere: updateWhere?.call(ContentKey.t),
transaction: transaction,
);
}
/// Updates all [ContentKey]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<ContentKey>> update(
_is.DatabaseSession session,
List<ContentKey> rows, {
_is.ColumnSelections<ContentKeyTable>? columns,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.update<ContentKey>(
rows,
columns: columns?.call(ContentKey.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Updates a single [ContentKey]. 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<ContentKey> updateRow(
_is.DatabaseSession session,
ContentKey row, {
_is.ColumnSelections<ContentKeyTable>? columns,
_is.Transaction? transaction,
}) async {
return session.db.updateRow<ContentKey>(
row,
columns: columns?.call(ContentKey.t),
transaction: transaction,
);
}
/// Updates a single [ContentKey] by its [id] with the specified [columnValues].
/// Returns the updated row or null if no row with the given id exists.
Future<ContentKey?> updateById(
_is.DatabaseSession session,
_is.UuidValue id, {
required _is.ColumnValueListBuilder<ContentKeyUpdateTable> columnValues,
_is.Transaction? transaction,
}) async {
return session.db.updateById<ContentKey>(
id,
columnValues: columnValues(ContentKey.t.updateTable),
transaction: transaction,
);
}
/// Updates all [ContentKey]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<ContentKey>> updateWhere(
_is.DatabaseSession session, {
required _is.ColumnValueListBuilder<ContentKeyUpdateTable> columnValues,
required _is.WhereExpressionBuilder<ContentKeyTable> where,
int? limit,
int? offset,
_is.OrderByBuilder<ContentKeyTable>? orderBy,
_is.OrderByListBuilder<ContentKeyTable>? orderByList,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.updateWhere<ContentKey>(
columnValues: columnValues(ContentKey.t.updateTable),
where: where(ContentKey.t),
limit: limit,
offset: offset,
orderBy: orderBy?.call(ContentKey.t),
orderByList: orderByList?.call(ContentKey.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Deletes all [ContentKey]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<ContentKey>> delete(
_is.DatabaseSession session,
List<ContentKey> rows, {
_is.OrderByBuilder<ContentKeyTable>? orderBy,
_is.OrderByListBuilder<ContentKeyTable>? orderByList,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.delete<ContentKey>(
rows,
orderBy: orderBy?.call(ContentKey.t),
orderByList: orderByList?.call(ContentKey.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Deletes a single [ContentKey].
Future<ContentKey> deleteRow(
_is.DatabaseSession session,
ContentKey row, {
_is.Transaction? transaction,
}) async {
return session.db.deleteRow<ContentKey>(
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<ContentKey>> deleteWhere(
_is.DatabaseSession session, {
required _is.WhereExpressionBuilder<ContentKeyTable> where,
_is.OrderByBuilder<ContentKeyTable>? orderBy,
_is.OrderByListBuilder<ContentKeyTable>? orderByList,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.deleteWhere<ContentKey>(
where: where(ContentKey.t),
orderBy: orderBy?.call(ContentKey.t),
orderByList: orderByList?.call(ContentKey.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<ContentKeyTable>? where,
int? limit,
_is.Transaction? transaction,
}) async {
return session.db.count<ContentKey>(
where: where?.call(ContentKey.t),
limit: limit,
transaction: transaction,
);
}
/// Acquires row-level locks on [ContentKey] rows matching the [where] expression.
Future<void> lockRows(
_is.DatabaseSession session, {
required _is.WhereExpressionBuilder<ContentKeyTable> where,
required _is.LockMode lockMode,
required _is.Transaction transaction,
_is.LockBehavior lockBehavior = _is.LockBehavior.wait,
}) async {
return session.db.lockRows<ContentKey>(
where: where(ContentKey.t),
lockMode: lockMode,
lockBehavior: lockBehavior,
transaction: transaction,
);
}
}

View file

@ -19,6 +19,15 @@ import 'package:serverpod_auth_core_server/serverpod_auth_core_server.dart'
as _iacs;
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 'privacy/content_key.dart' as _imed3x5d;
import 'workout/workout.dart' as _i5b9b17z;
import 'workout/workout_series.dart' as _i92mefto;
export 'privacy/content_key.dart';
export 'workout/workout.dart';
export 'workout/workout_series.dart';
export 'sync_tables.dart';
class Protocol extends _is.DatabaseSerializationManager {
Protocol._();
@ -28,8 +37,234 @@ class Protocol extends _is.DatabaseSerializationManager {
static final Protocol _instance = Protocol._().._registerHostProtocols();
static List<_isp.TableDefinition> get targetTableDefinitions => [
_isp.TableDefinition(
name: 'content_key',
dartName: 'ContentKey',
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: 'dataKeyId',
columnType: _isp.ColumnType.text,
isNullable: false,
dartType: 'String',
),
_isp.ColumnDefinition(
name: 'sealedKey',
columnType: _isp.ColumnType.bytea,
isNullable: false,
dartType: 'dart:typed_data:ByteData',
),
],
foreignKeys: [
_isp.ForeignKeyDefinition(
constraintName: 'content_key_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: 'content_key_space_idx',
tableSpace: null,
elements: [
_isp.IndexElementDefinition(
type: _isp.IndexElementDefinitionType.column,
definition: 'spaceId',
),
],
type: 'btree',
isUnique: false,
isPrimary: false,
),
],
managed: true,
),
_isp.TableDefinition(
name: 'workout',
dartName: 'Workout',
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: 'workout_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: 'workout_space_idx',
tableSpace: null,
elements: [
_isp.IndexElementDefinition(
type: _isp.IndexElementDefinitionType.column,
definition: 'spaceId',
),
],
type: 'btree',
isUnique: false,
isPrimary: false,
),
],
managed: true,
),
_isp.TableDefinition(
name: 'workout_series',
dartName: 'WorkoutSeries',
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: 'workoutId',
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: 'workout_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: 'workout_series_fk_1',
columns: ['workoutId'],
referenceTable: 'workout',
referenceTableSchema: 'public',
referenceColumns: ['id'],
onUpdate: _isp.ForeignKeyAction.noAction,
onDelete: _isp.ForeignKeyAction.cascade,
matchType: null,
deferrable: _isp.DeferrableConstraint.initiallyDeferred,
),
],
indexes: [
_isp.IndexDefinition(
indexName: 'workout_series_space_idx',
tableSpace: null,
elements: [
_isp.IndexElementDefinition(
type: _isp.IndexElementDefinitionType.column,
definition: 'spaceId',
),
],
type: 'btree',
isUnique: false,
isPrimary: false,
),
_isp.IndexDefinition(
indexName: 'workout_series_workout_idx',
tableSpace: null,
elements: [
_isp.IndexElementDefinition(
type: _isp.IndexElementDefinitionType.column,
definition: 'workoutId',
),
],
type: 'btree',
isUnique: false,
isPrimary: false,
),
],
managed: true,
),
..._ik8molhd.Protocol.targetTableDefinitions,
..._iais.Protocol.targetTableDefinitions,
..._izehhkf5.Protocol.targetTableDefinitions,
..._ipoqfiqr.Protocol.targetTableDefinitions,
..._iacs.Protocol.targetTableDefinitions,
..._isp.Protocol.targetTableDefinitions,
@ -62,12 +297,34 @@ class Protocol extends _is.DatabaseSerializationManager {
}
}
if (t == _imed3x5d.ContentKey) {
return _imed3x5d.ContentKey.fromJson(data) as T;
}
if (t == _i5b9b17z.Workout) {
return _i5b9b17z.Workout.fromJson(data) as T;
}
if (t == _i92mefto.WorkoutSeries) {
return _i92mefto.WorkoutSeries.fromJson(data) as T;
}
if (t == _is.getType<_imed3x5d.ContentKey?>()) {
return (data != null ? _imed3x5d.ContentKey.fromJson(data) : null) as T;
}
if (t == _is.getType<_i5b9b17z.Workout?>()) {
return (data != null ? _i5b9b17z.Workout.fromJson(data) : null) as T;
}
if (t == _is.getType<_i92mefto.WorkoutSeries?>()) {
return (data != null ? _i92mefto.WorkoutSeries.fromJson(data) : null)
as T;
}
try {
return _ik8molhd.Protocol().deserialize<T>(data, t);
} on _is.DeserializationTypeNotFoundException catch (_) {}
try {
return _iais.Protocol().deserialize<T>(data, t);
} on _is.DeserializationTypeNotFoundException catch (_) {}
try {
return _izehhkf5.Protocol().deserialize<T>(data, t);
} on _is.DeserializationTypeNotFoundException catch (_) {}
try {
return _ipoqfiqr.Protocol().deserialize<T>(data, t);
} on _is.DeserializationTypeNotFoundException catch (_) {}
@ -82,6 +339,9 @@ class Protocol extends _is.DatabaseSerializationManager {
static String? getClassNameForType(Type type) {
return switch (type) {
_imed3x5d.ContentKey => 'ContentKey',
_i5b9b17z.Workout => 'Workout',
_i92mefto.WorkoutSeries => 'WorkoutSeries',
_ => null,
};
}
@ -98,6 +358,14 @@ class Protocol extends _is.DatabaseSerializationManager {
);
}
switch (data) {
case _imed3x5d.ContentKey():
return 'ContentKey';
case _i5b9b17z.Workout():
return 'Workout';
case _i92mefto.WorkoutSeries():
return 'WorkoutSeries';
}
className = _ik8molhd.Protocol().getClassNameForObject(data);
if (className != null) {
return className.contains('.') ? className : 'halkyon_base.$className';
@ -108,6 +376,12 @@ class Protocol extends _is.DatabaseSerializationManager {
? className
: 'serverpod_auth_idp.$className';
}
className = _izehhkf5.Protocol().getClassNameForObject(data);
if (className != null) {
return className.contains('.')
? className
: 'serverpod_offline_sync.$className';
}
className = _ipoqfiqr.Protocol().getClassNameForObject(data);
if (className != null) {
return className.contains('.') ? className : 'halkyon_oidc.$className';
@ -131,6 +405,15 @@ class Protocol extends _is.DatabaseSerializationManager {
if (dataClassName is! String) {
return super.deserializeByClassName(data);
}
if (dataClassName == 'ContentKey') {
return deserialize<_imed3x5d.ContentKey>(data['data']);
}
if (dataClassName == 'Workout') {
return deserialize<_i5b9b17z.Workout>(data['data']);
}
if (dataClassName == 'WorkoutSeries') {
return deserialize<_i92mefto.WorkoutSeries>(data['data']);
}
if (dataClassName.startsWith('halkyon_base.')) {
data['className'] = dataClassName.substring(13);
return _ik8molhd.Protocol().deserializeByClassName(data);
@ -139,6 +422,10 @@ class Protocol extends _is.DatabaseSerializationManager {
data['className'] = dataClassName.substring(19);
return _iais.Protocol().deserializeByClassName(data);
}
if (dataClassName.startsWith('serverpod_offline_sync.')) {
data['className'] = dataClassName.substring(23);
return _izehhkf5.Protocol().deserializeByClassName(data);
}
if (dataClassName.startsWith('halkyon_oidc.')) {
data['className'] = dataClassName.substring(13);
return _ipoqfiqr.Protocol().deserializeByClassName(data);
@ -157,6 +444,7 @@ class Protocol extends _is.DatabaseSerializationManager {
void _registerHostProtocols() {
_ik8molhd.Protocol().registerHostProtocol('halkyon_train', this);
_iais.Protocol().registerHostProtocol('halkyon_train', this);
_izehhkf5.Protocol().registerHostProtocol('halkyon_train', this);
_ipoqfiqr.Protocol().registerHostProtocol('halkyon_train', this);
_iacs.Protocol().registerHostProtocol('halkyon_train', this);
}
@ -175,6 +463,12 @@ class Protocol extends _is.DatabaseSerializationManager {
return table;
}
}
{
var table = _izehhkf5.Protocol().getTableForType(t);
if (table != null) {
return table;
}
}
{
var table = _ipoqfiqr.Protocol().getTableForType(t);
if (table != null) {
@ -193,6 +487,14 @@ class Protocol extends _is.DatabaseSerializationManager {
return table;
}
}
switch (t) {
case _imed3x5d.ContentKey:
return _imed3x5d.ContentKey.t;
case _i5b9b17z.Workout:
return _i5b9b17z.Workout.t;
case _i92mefto.WorkoutSeries:
return _i92mefto.WorkoutSeries.t;
}
return null;
}
@ -218,6 +520,9 @@ class Protocol extends _is.DatabaseSerializationManager {
try {
return _iais.Protocol().mapRecordToJson(record);
} catch (_) {}
try {
return _izehhkf5.Protocol().mapRecordToJson(record);
} catch (_) {}
try {
return _ipoqfiqr.Protocol().mapRecordToJson(record);
} catch (_) {}

View file

@ -12,8 +12,11 @@
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:io' as _idi;
import 'package:serverpod/serverpod.dart' as _is;
import 'package:serverpod_offline_sync_server/serverpod_offline_sync_server.dart'
as _izehhkf5;
import 'endpoints.dart' as _iavctuc6;
import 'protocol.dart' as _il2as5qe;
import 'sync_tables.dart' as _ii3f3u05;
export 'package:serverpod/serverpod.dart' hide Serverpod;
export 'future_calls.dart' show ServerpodFutureCallsGetter;
@ -26,6 +29,10 @@ export 'future_calls.dart' show ServerpodFutureCallsGetter;
/// ```dart
/// final pod = Serverpod(args);
/// ```
///
/// The `serverpod_offline_sync` engine is initialized with the tables
/// declared with `database: sync`, wrapping any provided
/// [databaseInterceptor] with `offlineSyncDatabaseInterceptor`.
class Serverpod extends _is.Serverpod {
Serverpod(
List<String> args, {
@ -56,6 +63,19 @@ class Serverpod extends _is.Serverpod {
securityContextConfig: securityContextConfig,
experimentalFeatures: experimentalFeatures,
runtimeParametersBuilder: runtimeParametersBuilder,
databaseInterceptor: databaseInterceptor,
);
databaseInterceptor:
(
session,
inner,
) => _izehhkf5.offlineSyncDatabaseInterceptor(
session,
databaseInterceptor?.call(
session,
inner,
) ??
inner,
),
) {
initializeOfflineSync(syncTables: _ii3f3u05.syncTables);
}
}

View file

@ -0,0 +1,25 @@
/* 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 'package:serverpod/serverpod.dart' as _is;
import 'privacy/content_key.dart' as _imed3x5d;
import 'workout/workout.dart' as _i5b9b17z;
import 'workout/workout_series.dart' as _i92mefto;
/// The tables synchronized between client and server by the
/// `serverpod_offline_sync` package, including the ones owned by modules and
/// shared packages.
final List<_is.Table> syncTables = [
_imed3x5d.ContentKey.t,
_i5b9b17z.Workout.t,
_i92mefto.WorkoutSeries.t,
];

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 workout (ADR 0030, D14): a run, a ride, a row. Everything about it is in
/// its sealed payload (D7): its type, when it started and ended, its name,
/// notes and totals; the instance sees that it exists, its size and when it
/// changed. Changed when edited, the last writer winning, as a studyset's
/// payload in Learn; its samples are its series (`workout_series`), which
/// never change. Synced.
abstract class Workout
implements _is.TableRow<_is.UuidValue?>, _is.ProtocolSerialization {
Workout._({
this.id,
this.spaceId,
this.keyId,
required this.payload,
this.nonce,
});
factory Workout({
_is.UuidValue? id,
int? spaceId,
String? keyId,
required _idt.ByteData payload,
_idt.ByteData? nonce,
}) = _WorkoutImpl;
factory Workout.fromJson(Map<String, dynamic> jsonSerialization) {
return Workout(
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 = WorkoutTable();
static const db = WorkoutRepository._();
@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,
/// where everything is sealed.
String? keyId;
/// The content, sealed: 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 [Workout]
/// with some or all fields replaced by the given arguments.
@_is.useResult
Workout copyWith({
_is.UuidValue? id,
int? spaceId,
String? keyId,
_idt.ByteData? payload,
_idt.ByteData? nonce,
});
@override
Map<String, dynamic> toJson() {
return {
'__className__': 'Workout',
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__': 'Workout',
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 WorkoutInclude include() {
return WorkoutInclude._();
}
static WorkoutIncludeList includeList({
_is.WhereExpressionBuilder<WorkoutTable>? where,
int? limit,
int? offset,
_is.OrderByBuilder<WorkoutTable>? orderBy,
_is.OrderByListBuilder<WorkoutTable>? orderByList,
WorkoutInclude? include,
}) {
return WorkoutIncludeList._(
where: where,
limit: limit,
offset: offset,
orderBy: orderBy?.call(Workout.t),
orderByList: orderByList?.call(Workout.t),
include: include,
);
}
@override
String toString() {
return _is.SerializationManager.encode(this);
}
}
class _Undefined {}
class _WorkoutImpl extends Workout {
_WorkoutImpl({
_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 [Workout]
/// with some or all fields replaced by the given arguments.
@_is.useResult
@override
Workout copyWith({
Object? id = _Undefined,
Object? spaceId = _Undefined,
Object? keyId = _Undefined,
_idt.ByteData? payload,
Object? nonce = _Undefined,
}) {
return Workout(
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 WorkoutUpdateTable extends _is.UpdateTable<WorkoutTable> {
WorkoutUpdateTable(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 WorkoutTable extends _is.Table<_is.UuidValue?> {
WorkoutTable({super.tableRelation}) : super(tableName: 'workout') {
updateTable = WorkoutUpdateTable(this);
spaceId = _is.ColumnInt(
'spaceId',
this,
);
keyId = _is.ColumnString(
'keyId',
this,
);
payload = _is.ColumnByteData(
'payload',
this,
);
nonce = _is.ColumnByteData(
'nonce',
this,
);
}
late final WorkoutUpdateTable 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,
/// where everything is sealed.
late final _is.ColumnString keyId;
/// The content, sealed: 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 WorkoutInclude extends _is.IncludeObject {
WorkoutInclude._();
@override
Map<String, _is.Include?> get includes => {};
@override
_is.Table<_is.UuidValue?> get table => Workout.t;
}
class WorkoutIncludeList extends _is.IncludeList {
WorkoutIncludeList._({
_is.WhereExpressionBuilder<WorkoutTable>? where,
super.limit,
super.offset,
super.orderBy,
super.orderByList,
super.include,
}) {
super.where = where?.call(Workout.t);
}
@override
Map<String, _is.Include?> get includes => include?.includes ?? {};
@override
_is.Table<_is.UuidValue?> get table => Workout.t;
}
class WorkoutRepository {
const WorkoutRepository._();
/// Returns a list of [Workout]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<Workout>> find(
_is.DatabaseSession session, {
_is.WhereExpressionBuilder<WorkoutTable>? where,
int? limit,
int? offset,
_is.OrderByBuilder<WorkoutTable>? orderBy,
_is.OrderByListBuilder<WorkoutTable>? orderByList,
_is.Transaction? transaction,
_is.LockMode? lockMode,
_is.LockBehavior? lockBehavior,
}) async {
return session.db.find<Workout>(
where: where?.call(Workout.t),
orderBy: orderBy?.call(Workout.t),
orderByList: orderByList?.call(Workout.t),
limit: limit,
offset: offset,
transaction: transaction,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Returns the first matching [Workout] 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<Workout?> findFirstRow(
_is.DatabaseSession session, {
_is.WhereExpressionBuilder<WorkoutTable>? where,
int? offset,
_is.OrderByBuilder<WorkoutTable>? orderBy,
_is.OrderByListBuilder<WorkoutTable>? orderByList,
_is.Transaction? transaction,
_is.LockMode? lockMode,
_is.LockBehavior? lockBehavior,
}) async {
return session.db.findFirstRow<Workout>(
where: where?.call(Workout.t),
orderBy: orderBy?.call(Workout.t),
orderByList: orderByList?.call(Workout.t),
offset: offset,
transaction: transaction,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Finds a single [Workout] by its [id] or null if no such row exists.
Future<Workout?> findById(
_is.DatabaseSession session,
_is.UuidValue id, {
_is.Transaction? transaction,
_is.LockMode? lockMode,
_is.LockBehavior? lockBehavior,
}) async {
return session.db.findById<Workout>(
id,
transaction: transaction,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Inserts all [Workout]s in the list and returns the inserted rows.
///
/// The returned [Workout]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<Workout>> insert(
_is.DatabaseSession session,
List<Workout> rows, {
_is.Transaction? transaction,
bool ignoreConflicts = false,
bool noReturn = false,
}) async {
return session.db.insert<Workout>(
rows,
transaction: transaction,
ignoreConflicts: ignoreConflicts,
noReturn: noReturn,
);
}
/// Inserts a single [Workout] and returns the inserted row.
///
/// The returned [Workout] will have its `id` field set.
Future<Workout> insertRow(
_is.DatabaseSession session,
Workout row, {
_is.Transaction? transaction,
}) async {
return session.db.insertRow<Workout>(
row,
transaction: transaction,
);
}
/// Upserts all [Workout]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 [Workout]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<Workout>> upsert(
_is.DatabaseSession session,
List<Workout> rows, {
required _is.ColumnSelections<WorkoutTable> conflictColumns,
_is.ColumnSelections<WorkoutTable>? updateColumns,
_is.WhereExpressionBuilder<WorkoutTable>? updateWhere,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.upsert<Workout>(
rows,
conflictColumns: conflictColumns(Workout.t),
updateColumns: updateColumns?.call(Workout.t),
updateWhere: updateWhere?.call(Workout.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Upserts a single [Workout] 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 [Workout] will have its `id` field set.
Future<Workout?> upsertRow(
_is.DatabaseSession session,
Workout row, {
required _is.ColumnSelections<WorkoutTable> conflictColumns,
_is.ColumnSelections<WorkoutTable>? updateColumns,
_is.WhereExpressionBuilder<WorkoutTable>? updateWhere,
_is.Transaction? transaction,
}) async {
return session.db.upsertRow<Workout>(
row,
conflictColumns: conflictColumns(Workout.t),
updateColumns: updateColumns?.call(Workout.t),
updateWhere: updateWhere?.call(Workout.t),
transaction: transaction,
);
}
/// Updates all [Workout]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<Workout>> update(
_is.DatabaseSession session,
List<Workout> rows, {
_is.ColumnSelections<WorkoutTable>? columns,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.update<Workout>(
rows,
columns: columns?.call(Workout.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Updates a single [Workout]. 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<Workout> updateRow(
_is.DatabaseSession session,
Workout row, {
_is.ColumnSelections<WorkoutTable>? columns,
_is.Transaction? transaction,
}) async {
return session.db.updateRow<Workout>(
row,
columns: columns?.call(Workout.t),
transaction: transaction,
);
}
/// Updates a single [Workout] by its [id] with the specified [columnValues].
/// Returns the updated row or null if no row with the given id exists.
Future<Workout?> updateById(
_is.DatabaseSession session,
_is.UuidValue id, {
required _is.ColumnValueListBuilder<WorkoutUpdateTable> columnValues,
_is.Transaction? transaction,
}) async {
return session.db.updateById<Workout>(
id,
columnValues: columnValues(Workout.t.updateTable),
transaction: transaction,
);
}
/// Updates all [Workout]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<Workout>> updateWhere(
_is.DatabaseSession session, {
required _is.ColumnValueListBuilder<WorkoutUpdateTable> columnValues,
required _is.WhereExpressionBuilder<WorkoutTable> where,
int? limit,
int? offset,
_is.OrderByBuilder<WorkoutTable>? orderBy,
_is.OrderByListBuilder<WorkoutTable>? orderByList,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.updateWhere<Workout>(
columnValues: columnValues(Workout.t.updateTable),
where: where(Workout.t),
limit: limit,
offset: offset,
orderBy: orderBy?.call(Workout.t),
orderByList: orderByList?.call(Workout.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Deletes all [Workout]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<Workout>> delete(
_is.DatabaseSession session,
List<Workout> rows, {
_is.OrderByBuilder<WorkoutTable>? orderBy,
_is.OrderByListBuilder<WorkoutTable>? orderByList,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.delete<Workout>(
rows,
orderBy: orderBy?.call(Workout.t),
orderByList: orderByList?.call(Workout.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Deletes a single [Workout].
Future<Workout> deleteRow(
_is.DatabaseSession session,
Workout row, {
_is.Transaction? transaction,
}) async {
return session.db.deleteRow<Workout>(
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<Workout>> deleteWhere(
_is.DatabaseSession session, {
required _is.WhereExpressionBuilder<WorkoutTable> where,
_is.OrderByBuilder<WorkoutTable>? orderBy,
_is.OrderByListBuilder<WorkoutTable>? orderByList,
_is.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.deleteWhere<Workout>(
where: where(Workout.t),
orderBy: orderBy?.call(Workout.t),
orderByList: orderByList?.call(Workout.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<WorkoutTable>? where,
int? limit,
_is.Transaction? transaction,
}) async {
return session.db.count<Workout>(
where: where?.call(Workout.t),
limit: limit,
transaction: transaction,
);
}
/// Acquires row-level locks on [Workout] rows matching the [where] expression.
Future<void> lockRows(
_is.DatabaseSession session, {
required _is.WhereExpressionBuilder<WorkoutTable> where,
required _is.LockMode lockMode,
required _is.Transaction transaction,
_is.LockBehavior lockBehavior = _is.LockBehavior.wait,
}) async {
return session.db.lockRows<Workout>(
where: where(Workout.t),
lockMode: lockMode,
lockBehavior: lockBehavior,
transaction: transaction,
);
}
}

Some files were not shown because too many files have changed in this diff Show more