feat(todo): the sync engine and Todo's first models (ADR 0033, D5, D6)
Todo's server syncs with serverpod_offline_sync as Learn's and Train's do, and holds two synced tables, every payload sealed on the devices: content_key (the person's data key, sealed to the account) and project (the Inbox or one of the person's own, changed when edited). A person's space goes with them, with everything in it. The migration only adds: the two tables, each indexed on spaceId and cascading with its space (ON DELETE CASCADE to offline_sync_spaces), the sync engine's own tables (crdt_*, offline_sync_*) with their foreign keys, the gen_random_uuid_v7() function their ids default to, and the modules' version rows. Nothing is dropped or changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
492c657980
commit
3876e30681
30 changed files with 28530 additions and 5 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,198 @@
|
|||
{
|
||||
"__className__": "serverpod.DatabaseDefinition",
|
||||
"schemaVersion": 2,
|
||||
"moduleName": "halkyon_todo",
|
||||
"tables": [
|
||||
{
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "content_key",
|
||||
"dartName": "ContentKey",
|
||||
"module": "halkyon_todo",
|
||||
"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": "project",
|
||||
"dartName": "Project",
|
||||
"module": "halkyon_todo",
|
||||
"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": "project_fk_0",
|
||||
"columns": [
|
||||
"spaceId"
|
||||
],
|
||||
"referenceTable": "offline_sync_spaces",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "project_space_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
}
|
||||
],
|
||||
"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
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -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 '20261101000019144-projects/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 = [
|
||||
_Migration20261101000019144_projects(),
|
||||
];
|
||||
|
||||
/// List of all client-side database migration versions.
|
||||
static List<String> get versions => migrations.map((m) => m.version).toList();
|
||||
}
|
||||
|
|
@ -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_todo_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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 0033, D5, as Train's ADR 0030, D15): the
|
||||
/// one their projects, tasks and labels 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,673 @@
|
|||
/* 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 project (ADR 0033, D6): the Inbox, made once per person, or one of their
|
||||
/// own. Everything about it is in its sealed payload (D5): its name, colour,
|
||||
/// place among the others, whether it is the Inbox; the instance sees that it
|
||||
/// exists, its size and when it changed. Changed when edited, the last
|
||||
/// writer winning. Synced.
|
||||
abstract class Project
|
||||
implements _isd.TableRow<_isc.UuidValue?>, _isc.ProtocolSerialization {
|
||||
Project._({
|
||||
this.id,
|
||||
this.spaceId,
|
||||
this.keyId,
|
||||
required this.payload,
|
||||
this.nonce,
|
||||
});
|
||||
|
||||
factory Project({
|
||||
_isc.UuidValue? id,
|
||||
int? spaceId,
|
||||
String? keyId,
|
||||
required _idt.ByteData payload,
|
||||
_idt.ByteData? nonce,
|
||||
}) = _ProjectImpl;
|
||||
|
||||
factory Project.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return Project(
|
||||
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 = ProjectTable();
|
||||
|
||||
static const db = ProjectRepository._();
|
||||
|
||||
@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 Todo,
|
||||
/// 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 [Project]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
Project copyWith({
|
||||
_isc.UuidValue? id,
|
||||
int? spaceId,
|
||||
String? keyId,
|
||||
_idt.ByteData? payload,
|
||||
_idt.ByteData? nonce,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'Project',
|
||||
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__': 'Project',
|
||||
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 ProjectInclude include() {
|
||||
return ProjectInclude._();
|
||||
}
|
||||
|
||||
static ProjectIncludeList includeList({
|
||||
_isd.WhereExpressionBuilder<ProjectTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_isd.OrderByBuilder<ProjectTable>? orderBy,
|
||||
_isd.OrderByListBuilder<ProjectTable>? orderByList,
|
||||
ProjectInclude? include,
|
||||
}) {
|
||||
return ProjectIncludeList._(
|
||||
where: where,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(Project.t),
|
||||
orderByList: orderByList?.call(Project.t),
|
||||
include: include,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _isc.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _Undefined {}
|
||||
|
||||
class _ProjectImpl extends Project {
|
||||
_ProjectImpl({
|
||||
_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 [Project]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
@override
|
||||
Project copyWith({
|
||||
Object? id = _Undefined,
|
||||
Object? spaceId = _Undefined,
|
||||
Object? keyId = _Undefined,
|
||||
_idt.ByteData? payload,
|
||||
Object? nonce = _Undefined,
|
||||
}) {
|
||||
return Project(
|
||||
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 ProjectUpdateTable extends _isd.UpdateTable<ProjectTable> {
|
||||
ProjectUpdateTable(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 ProjectTable extends _isd.Table<_isc.UuidValue?> {
|
||||
ProjectTable({super.tableRelation}) : super(tableName: 'project') {
|
||||
updateTable = ProjectUpdateTable(this);
|
||||
spaceId = _isd.ColumnInt(
|
||||
'spaceId',
|
||||
this,
|
||||
);
|
||||
keyId = _isd.ColumnString(
|
||||
'keyId',
|
||||
this,
|
||||
);
|
||||
payload = _isd.ColumnByteData(
|
||||
'payload',
|
||||
this,
|
||||
);
|
||||
nonce = _isd.ColumnByteData(
|
||||
'nonce',
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
late final ProjectUpdateTable 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 Todo,
|
||||
/// 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 ProjectInclude extends _isd.IncludeObject {
|
||||
ProjectInclude._();
|
||||
|
||||
@override
|
||||
Map<String, _isd.Include?> get includes => {};
|
||||
|
||||
@override
|
||||
_isd.Table<_isc.UuidValue?> get table => Project.t;
|
||||
}
|
||||
|
||||
class ProjectIncludeList extends _isd.IncludeList {
|
||||
ProjectIncludeList._({
|
||||
_isd.WhereExpressionBuilder<ProjectTable>? where,
|
||||
super.limit,
|
||||
super.offset,
|
||||
super.orderBy,
|
||||
super.orderByList,
|
||||
super.include,
|
||||
}) {
|
||||
super.where = where?.call(Project.t);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, _isd.Include?> get includes => include?.includes ?? {};
|
||||
|
||||
@override
|
||||
_isd.Table<_isc.UuidValue?> get table => Project.t;
|
||||
}
|
||||
|
||||
class ProjectRepository {
|
||||
const ProjectRepository._();
|
||||
|
||||
/// Returns a list of [Project]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<Project>> find(
|
||||
_isd.DatabaseSession session, {
|
||||
_isd.WhereExpressionBuilder<ProjectTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_isd.OrderByBuilder<ProjectTable>? orderBy,
|
||||
_isd.OrderByListBuilder<ProjectTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
_isd.LockMode? lockMode,
|
||||
_isd.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.find<Project>(
|
||||
where: where?.call(Project.t),
|
||||
orderBy: orderBy?.call(Project.t),
|
||||
orderByList: orderByList?.call(Project.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns the first matching [Project] 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<Project?> findFirstRow(
|
||||
_isd.DatabaseSession session, {
|
||||
_isd.WhereExpressionBuilder<ProjectTable>? where,
|
||||
int? offset,
|
||||
_isd.OrderByBuilder<ProjectTable>? orderBy,
|
||||
_isd.OrderByListBuilder<ProjectTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
_isd.LockMode? lockMode,
|
||||
_isd.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findFirstRow<Project>(
|
||||
where: where?.call(Project.t),
|
||||
orderBy: orderBy?.call(Project.t),
|
||||
orderByList: orderByList?.call(Project.t),
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Finds a single [Project] by its [id] or null if no such row exists.
|
||||
Future<Project?> findById(
|
||||
_isd.DatabaseSession session,
|
||||
_isc.UuidValue id, {
|
||||
_isd.Transaction? transaction,
|
||||
_isd.LockMode? lockMode,
|
||||
_isd.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findById<Project>(
|
||||
id,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts all [Project]s in the list and returns the inserted rows.
|
||||
///
|
||||
/// The returned [Project]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<Project>> insert(
|
||||
_isd.DatabaseSession session,
|
||||
List<Project> rows, {
|
||||
_isd.Transaction? transaction,
|
||||
bool ignoreConflicts = false,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.insert<Project>(
|
||||
rows,
|
||||
transaction: transaction,
|
||||
ignoreConflicts: ignoreConflicts,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts a single [Project] and returns the inserted row.
|
||||
///
|
||||
/// The returned [Project] will have its `id` field set.
|
||||
Future<Project> insertRow(
|
||||
_isd.DatabaseSession session,
|
||||
Project row, {
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.insertRow<Project>(
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts all [Project]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 [Project]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<Project>> upsert(
|
||||
_isd.DatabaseSession session,
|
||||
List<Project> rows, {
|
||||
required _isd.ColumnSelections<ProjectTable> conflictColumns,
|
||||
_isd.ColumnSelections<ProjectTable>? updateColumns,
|
||||
_isd.WhereExpressionBuilder<ProjectTable>? updateWhere,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.upsert<Project>(
|
||||
rows,
|
||||
conflictColumns: conflictColumns(Project.t),
|
||||
updateColumns: updateColumns?.call(Project.t),
|
||||
updateWhere: updateWhere?.call(Project.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts a single [Project] 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 [Project] will have its `id` field set.
|
||||
Future<Project?> upsertRow(
|
||||
_isd.DatabaseSession session,
|
||||
Project row, {
|
||||
required _isd.ColumnSelections<ProjectTable> conflictColumns,
|
||||
_isd.ColumnSelections<ProjectTable>? updateColumns,
|
||||
_isd.WhereExpressionBuilder<ProjectTable>? updateWhere,
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.upsertRow<Project>(
|
||||
row,
|
||||
conflictColumns: conflictColumns(Project.t),
|
||||
updateColumns: updateColumns?.call(Project.t),
|
||||
updateWhere: updateWhere?.call(Project.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [Project]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<Project>> update(
|
||||
_isd.DatabaseSession session,
|
||||
List<Project> rows, {
|
||||
_isd.ColumnSelections<ProjectTable>? columns,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.update<Project>(
|
||||
rows,
|
||||
columns: columns?.call(Project.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [Project]. 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<Project> updateRow(
|
||||
_isd.DatabaseSession session,
|
||||
Project row, {
|
||||
_isd.ColumnSelections<ProjectTable>? columns,
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateRow<Project>(
|
||||
row,
|
||||
columns: columns?.call(Project.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [Project] by its [id] with the specified [columnValues].
|
||||
/// Returns the updated row or null if no row with the given id exists.
|
||||
Future<Project?> updateById(
|
||||
_isd.DatabaseSession session,
|
||||
_isc.UuidValue id, {
|
||||
required _isd.ColumnValueListBuilder<ProjectUpdateTable> columnValues,
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateById<Project>(
|
||||
id,
|
||||
columnValues: columnValues(Project.t.updateTable),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [Project]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<Project>> updateWhere(
|
||||
_isd.DatabaseSession session, {
|
||||
required _isd.ColumnValueListBuilder<ProjectUpdateTable> columnValues,
|
||||
required _isd.WhereExpressionBuilder<ProjectTable> where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_isd.OrderByBuilder<ProjectTable>? orderBy,
|
||||
_isd.OrderByListBuilder<ProjectTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.updateWhere<Project>(
|
||||
columnValues: columnValues(Project.t.updateTable),
|
||||
where: where(Project.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(Project.t),
|
||||
orderByList: orderByList?.call(Project.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes all [Project]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<Project>> delete(
|
||||
_isd.DatabaseSession session,
|
||||
List<Project> rows, {
|
||||
_isd.OrderByBuilder<ProjectTable>? orderBy,
|
||||
_isd.OrderByListBuilder<ProjectTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.delete<Project>(
|
||||
rows,
|
||||
orderBy: orderBy?.call(Project.t),
|
||||
orderByList: orderByList?.call(Project.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes a single [Project].
|
||||
Future<Project> deleteRow(
|
||||
_isd.DatabaseSession session,
|
||||
Project row, {
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.deleteRow<Project>(
|
||||
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<Project>> deleteWhere(
|
||||
_isd.DatabaseSession session, {
|
||||
required _isd.WhereExpressionBuilder<ProjectTable> where,
|
||||
_isd.OrderByBuilder<ProjectTable>? orderBy,
|
||||
_isd.OrderByListBuilder<ProjectTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.deleteWhere<Project>(
|
||||
where: where(Project.t),
|
||||
orderBy: orderBy?.call(Project.t),
|
||||
orderByList: orderByList?.call(Project.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<ProjectTable>? where,
|
||||
int? limit,
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.count<Project>(
|
||||
where: where?.call(Project.t),
|
||||
limit: limit,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Acquires row-level locks on [Project] rows matching the [where] expression.
|
||||
Future<void> lockRows(
|
||||
_isd.DatabaseSession session, {
|
||||
required _isd.WhereExpressionBuilder<ProjectTable> where,
|
||||
required _isd.LockMode lockMode,
|
||||
required _isd.Transaction transaction,
|
||||
_isd.LockBehavior lockBehavior = _isd.LockBehavior.wait,
|
||||
}) async {
|
||||
return session.db.lockRows<Project>(
|
||||
where: where(Project.t),
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -18,15 +18,174 @@ 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 'project/project.dart' as _i9c44spp;
|
||||
export 'privacy/content_key.dart';
|
||||
export 'project/project.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_todo',
|
||||
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: 'project',
|
||||
dartName: 'Project',
|
||||
schema: 'public',
|
||||
module: 'halkyon_todo',
|
||||
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: 'project_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: 'project_space_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isd.IndexElementDefinition(
|
||||
type: _isd.IndexElementDefinitionType.column,
|
||||
definition: 'spaceId',
|
||||
),
|
||||
],
|
||||
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 +213,27 @@ class Protocol extends _isc.SerializationManager {
|
|||
}
|
||||
}
|
||||
|
||||
if (t == _imed3x5d.ContentKey) {
|
||||
return _imed3x5d.ContentKey.fromJson(data) as T;
|
||||
}
|
||||
if (t == _i9c44spp.Project) {
|
||||
return _i9c44spp.Project.fromJson(data) as T;
|
||||
}
|
||||
if (t == _isc.getType<_imed3x5d.ContentKey?>()) {
|
||||
return (data != null ? _imed3x5d.ContentKey.fromJson(data) : null) as T;
|
||||
}
|
||||
if (t == _isc.getType<_i9c44spp.Project?>()) {
|
||||
return (data != null ? _i9c44spp.Project.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 +245,8 @@ class Protocol extends _isc.SerializationManager {
|
|||
|
||||
static String? getClassNameForType(Type type) {
|
||||
return switch (type) {
|
||||
_imed3x5d.ContentKey => 'ContentKey',
|
||||
_i9c44spp.Project => 'Project',
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
|
@ -87,6 +263,12 @@ class Protocol extends _isc.SerializationManager {
|
|||
);
|
||||
}
|
||||
|
||||
switch (data) {
|
||||
case _imed3x5d.ContentKey():
|
||||
return 'ContentKey';
|
||||
case _i9c44spp.Project():
|
||||
return 'Project';
|
||||
}
|
||||
className = _ihk4p5t7.Protocol().getClassNameForObject(data);
|
||||
if (className != null) {
|
||||
return className.contains('.') ? className : 'halkyon_base.$className';
|
||||
|
|
@ -97,6 +279,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 +304,12 @@ class Protocol extends _isc.SerializationManager {
|
|||
if (dataClassName is! String) {
|
||||
return super.deserializeByClassName(data);
|
||||
}
|
||||
if (dataClassName == 'ContentKey') {
|
||||
return deserialize<_imed3x5d.ContentKey>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'Project') {
|
||||
return deserialize<_i9c44spp.Project>(data['data']);
|
||||
}
|
||||
if (dataClassName.startsWith('halkyon_base.')) {
|
||||
data['className'] = dataClassName.substring(13);
|
||||
return _ihk4p5t7.Protocol().deserializeByClassName(data);
|
||||
|
|
@ -124,6 +318,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 +336,71 @@ class Protocol extends _isc.SerializationManager {
|
|||
void _registerHostProtocols() {
|
||||
_ihk4p5t7.Protocol().registerHostProtocol('halkyon_todo', this);
|
||||
_iaic.Protocol().registerHostProtocol('halkyon_todo', this);
|
||||
_ipulbpi2.Protocol().registerHostProtocol('halkyon_todo', this);
|
||||
_ibkpwzgl.Protocol().registerHostProtocol('halkyon_todo', this);
|
||||
_iacc.Protocol().registerHostProtocol('halkyon_todo', 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 _i9c44spp.Project:
|
||||
return _i9c44spp.Project.t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
List<_isd.TableDefinition> getTargetTableDefinitions() =>
|
||||
targetTableDefinitions;
|
||||
|
||||
@override
|
||||
String getModuleName() => 'halkyon_todo';
|
||||
|
||||
|
|
@ -160,6 +419,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 (_) {}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
/* 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 'project/project.dart' as _i9c44spp;
|
||||
|
||||
/// 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,
|
||||
_i9c44spp.Project.t,
|
||||
];
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -3,7 +3,14 @@ type: server
|
|||
client_package_path: ../halkyon_todo_client
|
||||
server_test_tools_path: test/integration/test_tools
|
||||
|
||||
# Private data sync through serverpod_offline_sync (ADR 0001, ADR 0033's D5): 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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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 0033, D5, as Train's ADR 0030, D15): the
|
||||
/// one their projects, tasks and labels 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,670 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:typed_data' as _idt;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
|
||||
/// A project (ADR 0033, D6): the Inbox, made once per person, or one of their
|
||||
/// own. Everything about it is in its sealed payload (D5): its name, colour,
|
||||
/// place among the others, whether it is the Inbox; the instance sees that it
|
||||
/// exists, its size and when it changed. Changed when edited, the last
|
||||
/// writer winning. Synced.
|
||||
abstract class Project
|
||||
implements _is.TableRow<_is.UuidValue?>, _is.ProtocolSerialization {
|
||||
Project._({
|
||||
this.id,
|
||||
this.spaceId,
|
||||
this.keyId,
|
||||
required this.payload,
|
||||
this.nonce,
|
||||
});
|
||||
|
||||
factory Project({
|
||||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
String? keyId,
|
||||
required _idt.ByteData payload,
|
||||
_idt.ByteData? nonce,
|
||||
}) = _ProjectImpl;
|
||||
|
||||
factory Project.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return Project(
|
||||
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 = ProjectTable();
|
||||
|
||||
static const db = ProjectRepository._();
|
||||
|
||||
@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 Todo,
|
||||
/// 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 [Project]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
Project copyWith({
|
||||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
String? keyId,
|
||||
_idt.ByteData? payload,
|
||||
_idt.ByteData? nonce,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'Project',
|
||||
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__': 'Project',
|
||||
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 ProjectInclude include() {
|
||||
return ProjectInclude._();
|
||||
}
|
||||
|
||||
static ProjectIncludeList includeList({
|
||||
_is.WhereExpressionBuilder<ProjectTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<ProjectTable>? orderBy,
|
||||
_is.OrderByListBuilder<ProjectTable>? orderByList,
|
||||
ProjectInclude? include,
|
||||
}) {
|
||||
return ProjectIncludeList._(
|
||||
where: where,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(Project.t),
|
||||
orderByList: orderByList?.call(Project.t),
|
||||
include: include,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _is.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _Undefined {}
|
||||
|
||||
class _ProjectImpl extends Project {
|
||||
_ProjectImpl({
|
||||
_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 [Project]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
@override
|
||||
Project copyWith({
|
||||
Object? id = _Undefined,
|
||||
Object? spaceId = _Undefined,
|
||||
Object? keyId = _Undefined,
|
||||
_idt.ByteData? payload,
|
||||
Object? nonce = _Undefined,
|
||||
}) {
|
||||
return Project(
|
||||
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 ProjectUpdateTable extends _is.UpdateTable<ProjectTable> {
|
||||
ProjectUpdateTable(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 ProjectTable extends _is.Table<_is.UuidValue?> {
|
||||
ProjectTable({super.tableRelation}) : super(tableName: 'project') {
|
||||
updateTable = ProjectUpdateTable(this);
|
||||
spaceId = _is.ColumnInt(
|
||||
'spaceId',
|
||||
this,
|
||||
);
|
||||
keyId = _is.ColumnString(
|
||||
'keyId',
|
||||
this,
|
||||
);
|
||||
payload = _is.ColumnByteData(
|
||||
'payload',
|
||||
this,
|
||||
);
|
||||
nonce = _is.ColumnByteData(
|
||||
'nonce',
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
late final ProjectUpdateTable 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 Todo,
|
||||
/// 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 ProjectInclude extends _is.IncludeObject {
|
||||
ProjectInclude._();
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => {};
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => Project.t;
|
||||
}
|
||||
|
||||
class ProjectIncludeList extends _is.IncludeList {
|
||||
ProjectIncludeList._({
|
||||
_is.WhereExpressionBuilder<ProjectTable>? where,
|
||||
super.limit,
|
||||
super.offset,
|
||||
super.orderBy,
|
||||
super.orderByList,
|
||||
super.include,
|
||||
}) {
|
||||
super.where = where?.call(Project.t);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => include?.includes ?? {};
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => Project.t;
|
||||
}
|
||||
|
||||
class ProjectRepository {
|
||||
const ProjectRepository._();
|
||||
|
||||
/// Returns a list of [Project]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<Project>> find(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<ProjectTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<ProjectTable>? orderBy,
|
||||
_is.OrderByListBuilder<ProjectTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.find<Project>(
|
||||
where: where?.call(Project.t),
|
||||
orderBy: orderBy?.call(Project.t),
|
||||
orderByList: orderByList?.call(Project.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns the first matching [Project] 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<Project?> findFirstRow(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<ProjectTable>? where,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<ProjectTable>? orderBy,
|
||||
_is.OrderByListBuilder<ProjectTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findFirstRow<Project>(
|
||||
where: where?.call(Project.t),
|
||||
orderBy: orderBy?.call(Project.t),
|
||||
orderByList: orderByList?.call(Project.t),
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Finds a single [Project] by its [id] or null if no such row exists.
|
||||
Future<Project?> findById(
|
||||
_is.DatabaseSession session,
|
||||
_is.UuidValue id, {
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findById<Project>(
|
||||
id,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts all [Project]s in the list and returns the inserted rows.
|
||||
///
|
||||
/// The returned [Project]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<Project>> insert(
|
||||
_is.DatabaseSession session,
|
||||
List<Project> rows, {
|
||||
_is.Transaction? transaction,
|
||||
bool ignoreConflicts = false,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.insert<Project>(
|
||||
rows,
|
||||
transaction: transaction,
|
||||
ignoreConflicts: ignoreConflicts,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts a single [Project] and returns the inserted row.
|
||||
///
|
||||
/// The returned [Project] will have its `id` field set.
|
||||
Future<Project> insertRow(
|
||||
_is.DatabaseSession session,
|
||||
Project row, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.insertRow<Project>(
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts all [Project]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 [Project]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<Project>> upsert(
|
||||
_is.DatabaseSession session,
|
||||
List<Project> rows, {
|
||||
required _is.ColumnSelections<ProjectTable> conflictColumns,
|
||||
_is.ColumnSelections<ProjectTable>? updateColumns,
|
||||
_is.WhereExpressionBuilder<ProjectTable>? updateWhere,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.upsert<Project>(
|
||||
rows,
|
||||
conflictColumns: conflictColumns(Project.t),
|
||||
updateColumns: updateColumns?.call(Project.t),
|
||||
updateWhere: updateWhere?.call(Project.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts a single [Project] 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 [Project] will have its `id` field set.
|
||||
Future<Project?> upsertRow(
|
||||
_is.DatabaseSession session,
|
||||
Project row, {
|
||||
required _is.ColumnSelections<ProjectTable> conflictColumns,
|
||||
_is.ColumnSelections<ProjectTable>? updateColumns,
|
||||
_is.WhereExpressionBuilder<ProjectTable>? updateWhere,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.upsertRow<Project>(
|
||||
row,
|
||||
conflictColumns: conflictColumns(Project.t),
|
||||
updateColumns: updateColumns?.call(Project.t),
|
||||
updateWhere: updateWhere?.call(Project.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [Project]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<Project>> update(
|
||||
_is.DatabaseSession session,
|
||||
List<Project> rows, {
|
||||
_is.ColumnSelections<ProjectTable>? columns,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.update<Project>(
|
||||
rows,
|
||||
columns: columns?.call(Project.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [Project]. 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<Project> updateRow(
|
||||
_is.DatabaseSession session,
|
||||
Project row, {
|
||||
_is.ColumnSelections<ProjectTable>? columns,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateRow<Project>(
|
||||
row,
|
||||
columns: columns?.call(Project.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [Project] by its [id] with the specified [columnValues].
|
||||
/// Returns the updated row or null if no row with the given id exists.
|
||||
Future<Project?> updateById(
|
||||
_is.DatabaseSession session,
|
||||
_is.UuidValue id, {
|
||||
required _is.ColumnValueListBuilder<ProjectUpdateTable> columnValues,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateById<Project>(
|
||||
id,
|
||||
columnValues: columnValues(Project.t.updateTable),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [Project]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<Project>> updateWhere(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.ColumnValueListBuilder<ProjectUpdateTable> columnValues,
|
||||
required _is.WhereExpressionBuilder<ProjectTable> where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<ProjectTable>? orderBy,
|
||||
_is.OrderByListBuilder<ProjectTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.updateWhere<Project>(
|
||||
columnValues: columnValues(Project.t.updateTable),
|
||||
where: where(Project.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(Project.t),
|
||||
orderByList: orderByList?.call(Project.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes all [Project]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<Project>> delete(
|
||||
_is.DatabaseSession session,
|
||||
List<Project> rows, {
|
||||
_is.OrderByBuilder<ProjectTable>? orderBy,
|
||||
_is.OrderByListBuilder<ProjectTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.delete<Project>(
|
||||
rows,
|
||||
orderBy: orderBy?.call(Project.t),
|
||||
orderByList: orderByList?.call(Project.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes a single [Project].
|
||||
Future<Project> deleteRow(
|
||||
_is.DatabaseSession session,
|
||||
Project row, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.deleteRow<Project>(
|
||||
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<Project>> deleteWhere(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.WhereExpressionBuilder<ProjectTable> where,
|
||||
_is.OrderByBuilder<ProjectTable>? orderBy,
|
||||
_is.OrderByListBuilder<ProjectTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.deleteWhere<Project>(
|
||||
where: where(Project.t),
|
||||
orderBy: orderBy?.call(Project.t),
|
||||
orderByList: orderByList?.call(Project.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<ProjectTable>? where,
|
||||
int? limit,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.count<Project>(
|
||||
where: where?.call(Project.t),
|
||||
limit: limit,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Acquires row-level locks on [Project] rows matching the [where] expression.
|
||||
Future<void> lockRows(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.WhereExpressionBuilder<ProjectTable> where,
|
||||
required _is.LockMode lockMode,
|
||||
required _is.Transaction transaction,
|
||||
_is.LockBehavior lockBehavior = _is.LockBehavior.wait,
|
||||
}) async {
|
||||
return session.db.lockRows<Project>(
|
||||
where: where(Project.t),
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,13 @@ 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 'project/project.dart' as _i9c44spp;
|
||||
export 'privacy/content_key.dart';
|
||||
export 'project/project.dart';
|
||||
export 'sync_tables.dart';
|
||||
|
||||
class Protocol extends _is.DatabaseSerializationManager {
|
||||
Protocol._();
|
||||
|
|
@ -28,8 +35,137 @@ 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_todo',
|
||||
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: 'project',
|
||||
dartName: 'Project',
|
||||
schema: 'public',
|
||||
module: 'halkyon_todo',
|
||||
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: 'project_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: 'project_space_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isp.IndexElementDefinition(
|
||||
type: _isp.IndexElementDefinitionType.column,
|
||||
definition: 'spaceId',
|
||||
),
|
||||
],
|
||||
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 +198,27 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
}
|
||||
}
|
||||
|
||||
if (t == _imed3x5d.ContentKey) {
|
||||
return _imed3x5d.ContentKey.fromJson(data) as T;
|
||||
}
|
||||
if (t == _i9c44spp.Project) {
|
||||
return _i9c44spp.Project.fromJson(data) as T;
|
||||
}
|
||||
if (t == _is.getType<_imed3x5d.ContentKey?>()) {
|
||||
return (data != null ? _imed3x5d.ContentKey.fromJson(data) : null) as T;
|
||||
}
|
||||
if (t == _is.getType<_i9c44spp.Project?>()) {
|
||||
return (data != null ? _i9c44spp.Project.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 +233,8 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
|
||||
static String? getClassNameForType(Type type) {
|
||||
return switch (type) {
|
||||
_imed3x5d.ContentKey => 'ContentKey',
|
||||
_i9c44spp.Project => 'Project',
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
|
@ -98,6 +251,12 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
);
|
||||
}
|
||||
|
||||
switch (data) {
|
||||
case _imed3x5d.ContentKey():
|
||||
return 'ContentKey';
|
||||
case _i9c44spp.Project():
|
||||
return 'Project';
|
||||
}
|
||||
className = _ik8molhd.Protocol().getClassNameForObject(data);
|
||||
if (className != null) {
|
||||
return className.contains('.') ? className : 'halkyon_base.$className';
|
||||
|
|
@ -108,6 +267,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 +296,12 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
if (dataClassName is! String) {
|
||||
return super.deserializeByClassName(data);
|
||||
}
|
||||
if (dataClassName == 'ContentKey') {
|
||||
return deserialize<_imed3x5d.ContentKey>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'Project') {
|
||||
return deserialize<_i9c44spp.Project>(data['data']);
|
||||
}
|
||||
if (dataClassName.startsWith('halkyon_base.')) {
|
||||
data['className'] = dataClassName.substring(13);
|
||||
return _ik8molhd.Protocol().deserializeByClassName(data);
|
||||
|
|
@ -139,6 +310,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 +332,7 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
void _registerHostProtocols() {
|
||||
_ik8molhd.Protocol().registerHostProtocol('halkyon_todo', this);
|
||||
_iais.Protocol().registerHostProtocol('halkyon_todo', this);
|
||||
_izehhkf5.Protocol().registerHostProtocol('halkyon_todo', this);
|
||||
_ipoqfiqr.Protocol().registerHostProtocol('halkyon_todo', this);
|
||||
_iacs.Protocol().registerHostProtocol('halkyon_todo', this);
|
||||
}
|
||||
|
|
@ -175,6 +351,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 +375,12 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
return table;
|
||||
}
|
||||
}
|
||||
switch (t) {
|
||||
case _imed3x5d.ContentKey:
|
||||
return _imed3x5d.ContentKey.t;
|
||||
case _i9c44spp.Project:
|
||||
return _i9c44spp.Project.t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -218,6 +406,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 (_) {}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
/* 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 'project/project.dart' as _i9c44spp;
|
||||
|
||||
/// 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,
|
||||
_i9c44spp.Project.t,
|
||||
];
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
### A data key of the person's (ADR 0033, D5, as Train's ADR 0030, D15): the
|
||||
### one their projects, tasks and labels 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.
|
||||
class: ContentKey
|
||||
table: content_key
|
||||
database: sync
|
||||
fields:
|
||||
### The key's ID, which the rows it seals name in their `keyId`; sealed
|
||||
### with the key too, so the column is never trusted.
|
||||
dataKeyId: String
|
||||
### The sealed key: the sealer's Ed25519 signature, then the sealed box.
|
||||
sealedKey: ByteData
|
||||
indexes:
|
||||
# Deleting a space cascades into every synced table, and Postgres indexes no
|
||||
# foreign key by itself: without this, deleting a person reads the table.
|
||||
content_key_space_idx:
|
||||
fields: spaceId
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
### A project (ADR 0033, D6): the Inbox, made once per person, or one of their
|
||||
### own. Everything about it is in its sealed payload (D5): its name, colour,
|
||||
### place among the others, whether it is the Inbox; the instance sees that it
|
||||
### exists, its size and when it changed. Changed when edited, the last
|
||||
### writer winning. Synced.
|
||||
class: Project
|
||||
table: project
|
||||
database: sync
|
||||
fields:
|
||||
### The key it is sealed with (ADR 0002's envelope); never null in Todo,
|
||||
### where everything is sealed.
|
||||
keyId: String?
|
||||
### The content, sealed: UTF-8 JSON with a `schemaVersion` once opened.
|
||||
payload: ByteData
|
||||
### The sealing's nonce (ADR 0002's envelope).
|
||||
nonce: ByteData?
|
||||
indexes:
|
||||
# Deleting a space finds its rows through this index (see content_key).
|
||||
project_space_idx:
|
||||
fields: spaceId
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue