feat(server): tree, tags, review modes, settings and assets (ADR 0004)
The schema of the features planned for the first release, as one migration: every migration of a synced table locks older app versions out of syncing (ADR 0002, rule 3), which costs nothing before that release. New card types need no migration; they are the next version of the revision payload. - studyset: `parent` (the tree; cycles and orphans show at the top level) and `position` among siblings (fractional index; existing studysets at 'a0'). - tag (its name in the payload) and card_tag, one row per assignment, so concurrent adds and removes merge. - review_log.mode: review, reverse (a schedule of its own) or cram (never moves a schedule); existing reviews are 'review'. - person_settings: study goals now, preferences later; random ids, since an id derived from the person could be claimed by someone else and break the owner's sync. - asset: a file's hash, type, size and name; the file goes into Serverpod's private cloud storage, served through an owner-checked endpoint (to come). Migration 20260930211154224-tree-tags-assets only adds: four tables with their space indexes and foreign keys, three columns with defaults. On devices SQLite rebuilds studyset and review_log to add foreign keys, which is safe only with foreign keys off, as Serverpod's runner does; a new test replays every client migration on a database with a row in each of our synced tables and checks that none is lost (with foreign keys on, it fails as it should). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
4c2a363160
commit
29a2105a55
37 changed files with 23962 additions and 12 deletions
|
|
@ -126,6 +126,8 @@ A spaced-repetition learning app: studysets of flashcards, reviewed on an FSRS s
|
|||
- **Content people edit is append-only revisions** (ADR 0001 rule 2, ADR 0003; the reference is `CardRevision`): a revision is never updated except to blank it, and `parents` lists the revisions it replaces. A save's parents are all current heads, which is also how a conflict is resolved. `mergeRevisions` (`feature/card/model/card_merge.dart`) merges several heads per side against their newest common ancestor; a side changed differently is a conflict the person settles, never merged silently. Deleting blanks every revision, then deletes.
|
||||
- **Reviews are an append-only log** (`ReviewLog`: card, rating, `reviewedAt` in UTC, `durationMs`). A card's schedule is never stored: `scheduleOf` (`feature/study/model/card_schedule.dart`) replays its reviews in time order with FSRS-6 (`fsrs`, pinned exactly), and only the final due date is fuzzed, from the card's id and review count, so every device computes the same. The study queue (`study_queue.dart`): due cards earliest first, then new cards in studyset order, 20 per studyset and local day. Deleting a card deletes its reviews.
|
||||
- **Order is a fractional index** (`fractional_indexing_dart`, rocicorp's algorithm) in its own column, last writer wins. Two devices appending at once can pick the same key, so ties sort by id.
|
||||
- **Tree, tags, modes, settings and files** (ADR 0004): a studyset's `parent` and sibling `position` form the tree, and studysets in a cycle or without their parent show at the top level; a card's tags are `card_tag` rows, one per assignment, never updated, and a duplicate counts once; `review_log.mode` is `review`, `reverse` (its own schedule) or `cram` (never moves a schedule), and a schedule replays the reviews of one mode; `person_settings` is meant as one row per person, and extra rows fold into the one with the smallest id; an `asset` row stands for a file in private cloud storage, found by its hash. **Never derive a row's id from a person or anything else guessable**: ids are unique across the whole table, so someone could claim it first in their own space and break the owner's sync.
|
||||
- **Every client migration keeps a device's rows** (`test/core/sync/device_migration_test.dart` replays each on a database with a row in every synced table of ours; SQLite rebuilds tables to add foreign keys, which Serverpod's runner does with foreign keys off). A new synced table needs a row in that test, which fails until it has one.
|
||||
- **Every synced table indexes `spaceId`** (`indexes: <table>_space_idx: fields: spaceId`, e.g. `studyset.spy.yaml`): deleting a space cascades into every synced table, and Postgres doesn't index foreign keys by itself, so account deletion and pruning would otherwise read whole tables.
|
||||
- **The client's local migration** (in `halkyon_learn_client/lib/migrations/`, written by the same `create-migration`) creates the device's SQLite schema from the whole definition, so it also contains the server's own tables. Only the synced tables and the bookkeeping ever hold data on a device.
|
||||
- **Serverpod's version moves together with `serverpod_offline_sync`**, which pins Serverpod exactly: the root `pubspec.yaml` overrides the four core packages to the project's version. Upgrade Serverpod only together with those overrides and the CLI version CI generates code with (`SERVERPOD_VERSION` in `.forgejo/workflows/ci.yaml`), after reading the sync package's changelog.
|
||||
|
|
|
|||
86
docs/adr/0004-tree-tags-review-modes-settings-assets.md
Normal file
86
docs/adr/0004-tree-tags-review-modes-settings-assets.md
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
# ADR 0004: Studyset tree, tags, review modes, settings and assets, in one migration
|
||||
|
||||
- **Status:** accepted
|
||||
- **Date:** 2026-09-30
|
||||
|
||||
## Context
|
||||
|
||||
The features decided for the first public release need data the synced tables don't
|
||||
have yet:
|
||||
|
||||
- a studyset tree (studysets inside studysets, studied one at a time or as a whole
|
||||
subtree);
|
||||
- tags on cards, to study and filter by;
|
||||
- study modes beyond the schedule: reversed cards (back to front) and cramming;
|
||||
- study goals on a home dashboard, which should follow a person to every device;
|
||||
- importing Anki decks with their images and sounds.
|
||||
|
||||
Every migration of a synced table locks older app versions out of syncing (ADR 0002,
|
||||
rule 3). Before the first release that costs nothing; after it, every device has to
|
||||
update before it syncs again. So all of this comes in one migration now,
|
||||
`20260930211154224-tree-tags-assets`, and later content fields go into the versioned
|
||||
payloads instead. New card types (multiple choice, vocabulary, drag-and-drop, map
|
||||
questions) need no migration at all: they are the next schema version of the card
|
||||
revision's payload, and the per-side merge of ADR 0003 becomes per field.
|
||||
|
||||
## Decision
|
||||
|
||||
1. **The tree is a parent and a position.** `studyset.parent` names the studyset it
|
||||
sits in (null at the top level), and `studyset.position` orders siblings with a
|
||||
fractional index, as `card.position` does (ADR 0003). A move updates one column, and
|
||||
the last writer wins. Two devices moving studysets into each other while offline
|
||||
make a cycle, and a studyset can lose its parent to a deletion on another device;
|
||||
the app shows the studysets caught either way at the top level until someone moves
|
||||
them. Existing studysets start at the top level with position `a0`; ties sort by id.
|
||||
2. **Tags are rows, and so are their assignments.** `tag` holds a name in its payload
|
||||
(content, like a studyset's title); `card_tag` links a card and a tag, one row per
|
||||
assignment, never updated. Removing a tag deletes one row, and adding it on another
|
||||
device at the same time makes another, so concurrent edits merge without losing
|
||||
either. A card with the same tag twice counts once.
|
||||
3. **Reviews say how the card was studied.** `review_log.mode` is `review` (front to
|
||||
back), `reverse` (back to front, with a schedule of its own, as Anki's reverse cards
|
||||
have) or `cram` (outside the schedule: it counts for streaks and the activity
|
||||
heatmap, and never moves a schedule). A card's schedule replays only the reviews of
|
||||
one mode. Existing reviews are `review`.
|
||||
4. **A person's settings are one synced row.** `person_settings` holds settings in a
|
||||
versioned payload: study goals now, other preferences later. Two devices creating it
|
||||
while offline make two rows; the app keeps the one with the smallest id and folds the
|
||||
other in. Its id is random like every row's: an id derived from the person could be
|
||||
claimed first by someone else in their own space, and the primary key's uniqueness
|
||||
would then break the owner's sync.
|
||||
5. **A file is a synced row plus a stored file.** `asset` holds a file's SHA-256, media
|
||||
type and size, and its name in the payload. The file itself goes into the server's
|
||||
private cloud storage (Serverpod's `CloudStorage`, in the database unless configured
|
||||
otherwise), at a path derived from the space and the hash, and is served only by an
|
||||
endpoint that checks the owner. Devices cache files by hash, and the hash checks each
|
||||
download. The same file added twice is stored once; two rows for it, from two devices
|
||||
offline, count as one. Content refers to a file by the row's id.
|
||||
|
||||
## Consequences
|
||||
|
||||
- The server migration only adds: four tables, three columns with defaults, indexes,
|
||||
and foreign keys (deferred where sync inserts rows in any order). The running server
|
||||
keeps working while it is applied, before the new server replaces it.
|
||||
- On devices, SQLite can't add a foreign key to a table, so the migration rebuilds
|
||||
`studyset` and `review_log` (a new table, the rows copied, the old one dropped).
|
||||
Dropping them with foreign keys enforced would cascade into cards and reviews or fail;
|
||||
Serverpod's SQLite migration runner switches foreign keys off around migrations.
|
||||
`device_migration_test.dart` replays every client migration on a database holding a
|
||||
row in each of our synced tables and checks that none is lost, and fails if a synced
|
||||
table has no row there.
|
||||
- Stored files outlive their rows unless deleted with them: deleting a person, or a
|
||||
file nobody refers to anymore, has to delete the stored file too. That comes with the
|
||||
file endpoints.
|
||||
- Media makes the database, and so its backups, grow. The storage can move to S3 or
|
||||
similar later without changing the rows, since the path is derived from the hash.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **The tree inside the payload:** no new columns, but a move and a title edit on two
|
||||
devices would overwrite each other, as both live in one payload.
|
||||
- **Tags inside the card revisions:** no new tables, but every tag change would write a
|
||||
revision, and filtering by tag would decode every card.
|
||||
- **No mode column:** cramming would leave no trace (no streak), and reversed answers
|
||||
would feed the normal schedule and skew it.
|
||||
- **Media after the release:** the asset table would then need a migration that locks
|
||||
older app versions out.
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,976 @@
|
|||
{
|
||||
"__className__": "serverpod.DatabaseDefinition",
|
||||
"schemaVersion": 2,
|
||||
"moduleName": "halkyon_learn",
|
||||
"tables": [
|
||||
{
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "asset",
|
||||
"dartName": "Asset",
|
||||
"module": "halkyon_learn",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"columnDefault": "random_v7",
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "spaceId",
|
||||
"fieldName": "spaceId",
|
||||
"columnType": 6,
|
||||
"isNullable": true,
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "sha256",
|
||||
"fieldName": "sha256",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"dartType": "String"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "mimeType",
|
||||
"fieldName": "mimeType",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"dartType": "String"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "byteSize",
|
||||
"fieldName": "byteSize",
|
||||
"columnType": 6,
|
||||
"isNullable": false,
|
||||
"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": "asset_fk_0",
|
||||
"columns": [
|
||||
"spaceId"
|
||||
],
|
||||
"referenceTable": "offline_sync_spaces",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "asset_space_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "asset_hash_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "sha256"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "card",
|
||||
"dartName": "Card",
|
||||
"module": "halkyon_learn",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"columnDefault": "random_v7",
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "spaceId",
|
||||
"fieldName": "spaceId",
|
||||
"columnType": 6,
|
||||
"isNullable": true,
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "studysetId",
|
||||
"fieldName": "studysetId",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "position",
|
||||
"fieldName": "position",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"dartType": "String"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "card_fk_0",
|
||||
"columns": [
|
||||
"spaceId"
|
||||
],
|
||||
"referenceTable": "offline_sync_spaces",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "card_fk_1",
|
||||
"columns": [
|
||||
"studysetId"
|
||||
],
|
||||
"referenceTable": "studyset",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4,
|
||||
"deferrable": "initiallyDeferred"
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "card_space_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "card_studyset_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "studysetId"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "position"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "card_revision",
|
||||
"dartName": "CardRevision",
|
||||
"module": "halkyon_learn",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"columnDefault": "random_v7",
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "spaceId",
|
||||
"fieldName": "spaceId",
|
||||
"columnType": 6,
|
||||
"isNullable": true,
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "cardId",
|
||||
"fieldName": "cardId",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "parents",
|
||||
"fieldName": "parents",
|
||||
"columnType": 8,
|
||||
"isNullable": false,
|
||||
"dartType": "List<UuidValue>"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "authorId",
|
||||
"fieldName": "authorId",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "reportId",
|
||||
"fieldName": "reportId",
|
||||
"columnType": 7,
|
||||
"isNullable": true,
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "keyId",
|
||||
"fieldName": "keyId",
|
||||
"columnType": 0,
|
||||
"isNullable": true,
|
||||
"dartType": "String?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "payload",
|
||||
"fieldName": "payload",
|
||||
"columnType": 5,
|
||||
"isNullable": false,
|
||||
"dartType": "dart:typed_data:ByteData"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "nonce",
|
||||
"fieldName": "nonce",
|
||||
"columnType": 5,
|
||||
"isNullable": true,
|
||||
"dartType": "dart:typed_data:ByteData?"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "card_revision_fk_0",
|
||||
"columns": [
|
||||
"spaceId"
|
||||
],
|
||||
"referenceTable": "offline_sync_spaces",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "card_revision_fk_1",
|
||||
"columns": [
|
||||
"cardId"
|
||||
],
|
||||
"referenceTable": "card",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4,
|
||||
"deferrable": "initiallyDeferred"
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "card_revision_space_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "card_revision_card_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "cardId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "card_tag",
|
||||
"dartName": "CardTag",
|
||||
"module": "halkyon_learn",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"columnDefault": "random_v7",
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "spaceId",
|
||||
"fieldName": "spaceId",
|
||||
"columnType": 6,
|
||||
"isNullable": true,
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "cardId",
|
||||
"fieldName": "cardId",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "tagId",
|
||||
"fieldName": "tagId",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "card_tag_fk_0",
|
||||
"columns": [
|
||||
"spaceId"
|
||||
],
|
||||
"referenceTable": "offline_sync_spaces",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "card_tag_fk_1",
|
||||
"columns": [
|
||||
"cardId"
|
||||
],
|
||||
"referenceTable": "card",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4,
|
||||
"deferrable": "initiallyDeferred"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "card_tag_fk_2",
|
||||
"columns": [
|
||||
"tagId"
|
||||
],
|
||||
"referenceTable": "tag",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4,
|
||||
"deferrable": "initiallyDeferred"
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "card_tag_space_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "card_tag_card_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "cardId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "card_tag_tag_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "tagId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "person_settings",
|
||||
"dartName": "PersonSettings",
|
||||
"module": "halkyon_learn",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"columnDefault": "random_v7",
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "spaceId",
|
||||
"fieldName": "spaceId",
|
||||
"columnType": 6,
|
||||
"isNullable": true,
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "keyId",
|
||||
"fieldName": "keyId",
|
||||
"columnType": 0,
|
||||
"isNullable": true,
|
||||
"dartType": "String?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "payload",
|
||||
"fieldName": "payload",
|
||||
"columnType": 5,
|
||||
"isNullable": false,
|
||||
"dartType": "dart:typed_data:ByteData"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "nonce",
|
||||
"fieldName": "nonce",
|
||||
"columnType": 5,
|
||||
"isNullable": true,
|
||||
"dartType": "dart:typed_data:ByteData?"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "person_settings_fk_0",
|
||||
"columns": [
|
||||
"spaceId"
|
||||
],
|
||||
"referenceTable": "offline_sync_spaces",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "person_settings_space_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "review_log",
|
||||
"dartName": "ReviewLog",
|
||||
"module": "halkyon_learn",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"columnDefault": "random_v7",
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "spaceId",
|
||||
"fieldName": "spaceId",
|
||||
"columnType": 6,
|
||||
"isNullable": true,
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "cardId",
|
||||
"fieldName": "cardId",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "rating",
|
||||
"fieldName": "rating",
|
||||
"columnType": 6,
|
||||
"isNullable": false,
|
||||
"dartType": "int"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "reviewedAt",
|
||||
"fieldName": "reviewedAt",
|
||||
"columnType": 4,
|
||||
"isNullable": false,
|
||||
"dartType": "DateTime"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "durationMs",
|
||||
"fieldName": "durationMs",
|
||||
"columnType": 6,
|
||||
"isNullable": false,
|
||||
"dartType": "int"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "mode",
|
||||
"fieldName": "mode",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"columnDefault": "'review'",
|
||||
"dartType": "String"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "review_log_fk_0",
|
||||
"columns": [
|
||||
"spaceId"
|
||||
],
|
||||
"referenceTable": "offline_sync_spaces",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "review_log_fk_1",
|
||||
"columns": [
|
||||
"cardId"
|
||||
],
|
||||
"referenceTable": "card",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4,
|
||||
"deferrable": "initiallyDeferred"
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "review_log_space_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "review_log_card_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "cardId"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "reviewedAt"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "studyset",
|
||||
"dartName": "Studyset",
|
||||
"module": "halkyon_learn",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"columnDefault": "random_v7",
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "spaceId",
|
||||
"fieldName": "spaceId",
|
||||
"columnType": 6,
|
||||
"isNullable": true,
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "parentId",
|
||||
"fieldName": "parentId",
|
||||
"columnType": 7,
|
||||
"isNullable": true,
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "position",
|
||||
"fieldName": "position",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"columnDefault": "'a0'",
|
||||
"dartType": "String"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "keyId",
|
||||
"fieldName": "keyId",
|
||||
"columnType": 0,
|
||||
"isNullable": true,
|
||||
"dartType": "String?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "payload",
|
||||
"fieldName": "payload",
|
||||
"columnType": 5,
|
||||
"isNullable": false,
|
||||
"dartType": "dart:typed_data:ByteData"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "nonce",
|
||||
"fieldName": "nonce",
|
||||
"columnType": 5,
|
||||
"isNullable": true,
|
||||
"dartType": "dart:typed_data:ByteData?"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "studyset_fk_0",
|
||||
"columns": [
|
||||
"spaceId"
|
||||
],
|
||||
"referenceTable": "offline_sync_spaces",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "studyset_fk_1",
|
||||
"columns": [
|
||||
"parentId"
|
||||
],
|
||||
"referenceTable": "studyset",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4,
|
||||
"deferrable": "initiallyDeferred"
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "studyset_space_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "studyset_parent_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "parentId"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "position"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "tag",
|
||||
"dartName": "Tag",
|
||||
"module": "halkyon_learn",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"columnDefault": "random_v7",
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "spaceId",
|
||||
"fieldName": "spaceId",
|
||||
"columnType": 6,
|
||||
"isNullable": true,
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "keyId",
|
||||
"fieldName": "keyId",
|
||||
"columnType": 0,
|
||||
"isNullable": true,
|
||||
"dartType": "String?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "payload",
|
||||
"fieldName": "payload",
|
||||
"columnType": 5,
|
||||
"isNullable": false,
|
||||
"dartType": "dart:typed_data:ByteData"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "nonce",
|
||||
"fieldName": "nonce",
|
||||
"columnType": 5,
|
||||
"isNullable": true,
|
||||
"dartType": "dart:typed_data:ByteData?"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "tag_fk_0",
|
||||
"columns": [
|
||||
"spaceId"
|
||||
],
|
||||
"referenceTable": "offline_sync_spaces",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "tag_space_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
}
|
||||
],
|
||||
"installedModules": [
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationVersion",
|
||||
"module": "serverpod",
|
||||
"version": "20260824182259319"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationVersion",
|
||||
"module": "serverpod_auth_idp",
|
||||
"version": "20260924105404509"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationVersion",
|
||||
"module": "serverpod_offline_sync",
|
||||
"version": "20260914143806119"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationVersion",
|
||||
"module": "serverpod_auth_core",
|
||||
"version": "20260924105232991"
|
||||
}
|
||||
],
|
||||
"migrationApiVersion": 1
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,546 @@
|
|||
{
|
||||
"__className__": "serverpod.DatabaseMigration",
|
||||
"actions": [
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationAction",
|
||||
"type": "createTable",
|
||||
"createTable": {
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "asset",
|
||||
"dartName": "Asset",
|
||||
"module": "halkyon_learn",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"columnDefault": "random_v7",
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "spaceId",
|
||||
"fieldName": "spaceId",
|
||||
"columnType": 6,
|
||||
"isNullable": true,
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "sha256",
|
||||
"fieldName": "sha256",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"dartType": "String"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "mimeType",
|
||||
"fieldName": "mimeType",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"dartType": "String"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "byteSize",
|
||||
"fieldName": "byteSize",
|
||||
"columnType": 6,
|
||||
"isNullable": false,
|
||||
"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": "asset_fk_0",
|
||||
"columns": [
|
||||
"spaceId"
|
||||
],
|
||||
"referenceTable": "offline_sync_spaces",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "asset_space_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "asset_hash_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "sha256"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationAction",
|
||||
"type": "createTable",
|
||||
"createTable": {
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "card_tag",
|
||||
"dartName": "CardTag",
|
||||
"module": "halkyon_learn",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"columnDefault": "random_v7",
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "spaceId",
|
||||
"fieldName": "spaceId",
|
||||
"columnType": 6,
|
||||
"isNullable": true,
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "cardId",
|
||||
"fieldName": "cardId",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "tagId",
|
||||
"fieldName": "tagId",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "card_tag_fk_0",
|
||||
"columns": [
|
||||
"spaceId"
|
||||
],
|
||||
"referenceTable": "offline_sync_spaces",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "card_tag_fk_1",
|
||||
"columns": [
|
||||
"cardId"
|
||||
],
|
||||
"referenceTable": "card",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4,
|
||||
"deferrable": "initiallyDeferred"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "card_tag_fk_2",
|
||||
"columns": [
|
||||
"tagId"
|
||||
],
|
||||
"referenceTable": "tag",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4,
|
||||
"deferrable": "initiallyDeferred"
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "card_tag_space_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "card_tag_card_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "cardId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "card_tag_tag_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "tagId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationAction",
|
||||
"type": "createTable",
|
||||
"createTable": {
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "person_settings",
|
||||
"dartName": "PersonSettings",
|
||||
"module": "halkyon_learn",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"columnDefault": "random_v7",
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "spaceId",
|
||||
"fieldName": "spaceId",
|
||||
"columnType": 6,
|
||||
"isNullable": true,
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "keyId",
|
||||
"fieldName": "keyId",
|
||||
"columnType": 0,
|
||||
"isNullable": true,
|
||||
"dartType": "String?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "payload",
|
||||
"fieldName": "payload",
|
||||
"columnType": 5,
|
||||
"isNullable": false,
|
||||
"dartType": "dart:typed_data:ByteData"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "nonce",
|
||||
"fieldName": "nonce",
|
||||
"columnType": 5,
|
||||
"isNullable": true,
|
||||
"dartType": "dart:typed_data:ByteData?"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "person_settings_fk_0",
|
||||
"columns": [
|
||||
"spaceId"
|
||||
],
|
||||
"referenceTable": "offline_sync_spaces",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "person_settings_space_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationAction",
|
||||
"type": "alterTable",
|
||||
"alterTable": {
|
||||
"__className__": "serverpod.TableMigration",
|
||||
"name": "review_log",
|
||||
"schema": "public",
|
||||
"addColumns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "mode",
|
||||
"fieldName": "mode",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"columnDefault": "'review'",
|
||||
"dartType": "String"
|
||||
}
|
||||
],
|
||||
"deleteColumns": [],
|
||||
"modifyColumns": [],
|
||||
"addIndexes": [],
|
||||
"deleteIndexes": [],
|
||||
"addForeignKeys": [],
|
||||
"deleteForeignKeys": [],
|
||||
"warnings": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationAction",
|
||||
"type": "alterTable",
|
||||
"alterTable": {
|
||||
"__className__": "serverpod.TableMigration",
|
||||
"name": "studyset",
|
||||
"schema": "public",
|
||||
"addColumns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "parentId",
|
||||
"fieldName": "parentId",
|
||||
"columnType": 7,
|
||||
"isNullable": true,
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "position",
|
||||
"fieldName": "position",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"columnDefault": "'a0'",
|
||||
"dartType": "String"
|
||||
}
|
||||
],
|
||||
"deleteColumns": [],
|
||||
"modifyColumns": [],
|
||||
"addIndexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "studyset_parent_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "parentId"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "position"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"deleteIndexes": [],
|
||||
"addForeignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "studyset_fk_1",
|
||||
"columns": [
|
||||
"parentId"
|
||||
],
|
||||
"referenceTable": "studyset",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4,
|
||||
"deferrable": "initiallyDeferred"
|
||||
}
|
||||
],
|
||||
"deleteForeignKeys": [],
|
||||
"warnings": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationAction",
|
||||
"type": "createTable",
|
||||
"createTable": {
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "tag",
|
||||
"dartName": "Tag",
|
||||
"module": "halkyon_learn",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"columnDefault": "random_v7",
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "spaceId",
|
||||
"fieldName": "spaceId",
|
||||
"columnType": 6,
|
||||
"isNullable": true,
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "keyId",
|
||||
"fieldName": "keyId",
|
||||
"columnType": 0,
|
||||
"isNullable": true,
|
||||
"dartType": "String?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "payload",
|
||||
"fieldName": "payload",
|
||||
"columnType": 5,
|
||||
"isNullable": false,
|
||||
"dartType": "dart:typed_data:ByteData"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "nonce",
|
||||
"fieldName": "nonce",
|
||||
"columnType": 5,
|
||||
"isNullable": true,
|
||||
"dartType": "dart:typed_data:ByteData?"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "tag_fk_0",
|
||||
"columns": [
|
||||
"spaceId"
|
||||
],
|
||||
"referenceTable": "offline_sync_spaces",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "tag_space_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"warnings": [],
|
||||
"migrationApiVersion": 1
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import 'package:serverpod_database/serverpod_database.dart';
|
|||
part '20260930094729362-studysets/migration.dart';
|
||||
part '20260930122235111-cards/migration.dart';
|
||||
part '20260930145136285-reviews/migration.dart';
|
||||
part '20260930211154224-tree-tags-assets/migration.dart';
|
||||
|
||||
/// Migration registry for the client-side database.
|
||||
class MigrationRegistry {
|
||||
|
|
@ -16,6 +17,7 @@ class MigrationRegistry {
|
|||
_Migration20260930094729362_studysets(),
|
||||
_Migration20260930122235111_cards(),
|
||||
_Migration20260930145136285_reviews(),
|
||||
_Migration20260930211154224_tree_tags_assets(),
|
||||
];
|
||||
|
||||
/// List of all client-side database migration versions.
|
||||
|
|
|
|||
756
halkyon_learn_client/lib/src/protocol/asset/asset.dart
Normal file
756
halkyon_learn_client/lib/src/protocol/asset/asset.dart
Normal file
|
|
@ -0,0 +1,756 @@
|
|||
/* 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 file in a person's content, an image or a sound (ADR 0004). This row is synced;
|
||||
/// the file itself is in the server's private cloud storage, reachable only
|
||||
/// through an endpoint that checks the owner, and each device caches it by its
|
||||
/// hash. Content refers to it by the row's id.
|
||||
///
|
||||
/// A file added twice (the same image in two imported cards) is one file in
|
||||
/// storage, since its path is its hash; two rows for it may exist when two
|
||||
/// devices added it while offline, and count as one.
|
||||
abstract class Asset
|
||||
implements _isd.TableRow<_isc.UuidValue?>, _isc.ProtocolSerialization {
|
||||
Asset._({
|
||||
this.id,
|
||||
this.spaceId,
|
||||
required this.sha256,
|
||||
required this.mimeType,
|
||||
required this.byteSize,
|
||||
this.keyId,
|
||||
required this.payload,
|
||||
this.nonce,
|
||||
});
|
||||
|
||||
factory Asset({
|
||||
_isc.UuidValue? id,
|
||||
int? spaceId,
|
||||
required String sha256,
|
||||
required String mimeType,
|
||||
required int byteSize,
|
||||
String? keyId,
|
||||
required _idt.ByteData payload,
|
||||
_idt.ByteData? nonce,
|
||||
}) = _AssetImpl;
|
||||
|
||||
factory Asset.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return Asset(
|
||||
id: jsonSerialization['id'] == null
|
||||
? null
|
||||
: _isc.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
|
||||
spaceId: jsonSerialization['spaceId'] as int?,
|
||||
sha256: jsonSerialization['sha256'] as String,
|
||||
mimeType: jsonSerialization['mimeType'] as String,
|
||||
byteSize: jsonSerialization['byteSize'] 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 = AssetTable();
|
||||
|
||||
static const db = AssetRepository._();
|
||||
|
||||
@override
|
||||
_isc.UuidValue? id;
|
||||
|
||||
/// The space owning this row. Maintained by the sync engine.
|
||||
int? spaceId;
|
||||
|
||||
/// The file's SHA-256, in lowercase hex: its name in storage and in every
|
||||
/// device's cache, and the check that a download is the file that was added.
|
||||
String sha256;
|
||||
|
||||
/// Its media type, e.g. 'image/png' or 'audio/mpeg'.
|
||||
String mimeType;
|
||||
|
||||
/// Its size in bytes.
|
||||
int byteSize;
|
||||
|
||||
/// The encryption key's id; always null (ADR 0002), as on studysets.
|
||||
String? keyId;
|
||||
|
||||
/// The content as UTF-8 JSON: {"schemaVersion", "fileName"}; the name it came
|
||||
/// with is content, like a studyset's title.
|
||||
_idt.ByteData payload;
|
||||
|
||||
/// The encryption nonce; always null, like keyId.
|
||||
_idt.ByteData? nonce;
|
||||
|
||||
@override
|
||||
_isd.Table<_isc.UuidValue?> get table => t;
|
||||
|
||||
/// Returns a shallow copy of this [Asset]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
Asset copyWith({
|
||||
_isc.UuidValue? id,
|
||||
int? spaceId,
|
||||
String? sha256,
|
||||
String? mimeType,
|
||||
int? byteSize,
|
||||
String? keyId,
|
||||
_idt.ByteData? payload,
|
||||
_idt.ByteData? nonce,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'Asset',
|
||||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
'sha256': sha256,
|
||||
'mimeType': mimeType,
|
||||
'byteSize': byteSize,
|
||||
if (keyId != null) 'keyId': keyId,
|
||||
'payload': payload.toJson(),
|
||||
if (nonce != null) 'nonce': nonce?.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'Asset',
|
||||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
'sha256': sha256,
|
||||
'mimeType': mimeType,
|
||||
'byteSize': byteSize,
|
||||
if (keyId != null) 'keyId': keyId,
|
||||
'payload': payload.toJson(),
|
||||
if (nonce != null) 'nonce': nonce?.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
static AssetInclude include() {
|
||||
return AssetInclude._();
|
||||
}
|
||||
|
||||
static AssetIncludeList includeList({
|
||||
_isd.WhereExpressionBuilder<AssetTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_isd.OrderByBuilder<AssetTable>? orderBy,
|
||||
_isd.OrderByListBuilder<AssetTable>? orderByList,
|
||||
AssetInclude? include,
|
||||
}) {
|
||||
return AssetIncludeList._(
|
||||
where: where,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(Asset.t),
|
||||
orderByList: orderByList?.call(Asset.t),
|
||||
include: include,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _isc.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _Undefined {}
|
||||
|
||||
class _AssetImpl extends Asset {
|
||||
_AssetImpl({
|
||||
_isc.UuidValue? id,
|
||||
int? spaceId,
|
||||
required String sha256,
|
||||
required String mimeType,
|
||||
required int byteSize,
|
||||
String? keyId,
|
||||
required _idt.ByteData payload,
|
||||
_idt.ByteData? nonce,
|
||||
}) : super._(
|
||||
id: id,
|
||||
spaceId: spaceId,
|
||||
sha256: sha256,
|
||||
mimeType: mimeType,
|
||||
byteSize: byteSize,
|
||||
keyId: keyId,
|
||||
payload: payload,
|
||||
nonce: nonce,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [Asset]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
@override
|
||||
Asset copyWith({
|
||||
Object? id = _Undefined,
|
||||
Object? spaceId = _Undefined,
|
||||
String? sha256,
|
||||
String? mimeType,
|
||||
int? byteSize,
|
||||
Object? keyId = _Undefined,
|
||||
_idt.ByteData? payload,
|
||||
Object? nonce = _Undefined,
|
||||
}) {
|
||||
return Asset(
|
||||
id: id is _isc.UuidValue? ? id : this.id,
|
||||
spaceId: spaceId is int? ? spaceId : this.spaceId,
|
||||
sha256: sha256 ?? this.sha256,
|
||||
mimeType: mimeType ?? this.mimeType,
|
||||
byteSize: byteSize ?? this.byteSize,
|
||||
keyId: keyId is String? ? keyId : this.keyId,
|
||||
payload: payload ?? this.payload.clone(),
|
||||
nonce: nonce is _idt.ByteData? ? nonce : this.nonce?.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AssetUpdateTable extends _isd.UpdateTable<AssetTable> {
|
||||
AssetUpdateTable(super.table);
|
||||
|
||||
_isd.ColumnValue<int, int> spaceId(int? value) => _isd.ColumnValue(
|
||||
table.spaceId,
|
||||
value,
|
||||
);
|
||||
|
||||
_isd.ColumnValue<String, String> sha256(String value) => _isd.ColumnValue(
|
||||
table.sha256,
|
||||
value,
|
||||
);
|
||||
|
||||
_isd.ColumnValue<String, String> mimeType(String value) => _isd.ColumnValue(
|
||||
table.mimeType,
|
||||
value,
|
||||
);
|
||||
|
||||
_isd.ColumnValue<int, int> byteSize(int value) => _isd.ColumnValue(
|
||||
table.byteSize,
|
||||
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 AssetTable extends _isd.Table<_isc.UuidValue?> {
|
||||
AssetTable({super.tableRelation}) : super(tableName: 'asset') {
|
||||
updateTable = AssetUpdateTable(this);
|
||||
spaceId = _isd.ColumnInt(
|
||||
'spaceId',
|
||||
this,
|
||||
);
|
||||
sha256 = _isd.ColumnString(
|
||||
'sha256',
|
||||
this,
|
||||
);
|
||||
mimeType = _isd.ColumnString(
|
||||
'mimeType',
|
||||
this,
|
||||
);
|
||||
byteSize = _isd.ColumnInt(
|
||||
'byteSize',
|
||||
this,
|
||||
);
|
||||
keyId = _isd.ColumnString(
|
||||
'keyId',
|
||||
this,
|
||||
);
|
||||
payload = _isd.ColumnByteData(
|
||||
'payload',
|
||||
this,
|
||||
);
|
||||
nonce = _isd.ColumnByteData(
|
||||
'nonce',
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
late final AssetUpdateTable updateTable;
|
||||
|
||||
/// The space owning this row. Maintained by the sync engine.
|
||||
late final _isd.ColumnInt spaceId;
|
||||
|
||||
/// The file's SHA-256, in lowercase hex: its name in storage and in every
|
||||
/// device's cache, and the check that a download is the file that was added.
|
||||
late final _isd.ColumnString sha256;
|
||||
|
||||
/// Its media type, e.g. 'image/png' or 'audio/mpeg'.
|
||||
late final _isd.ColumnString mimeType;
|
||||
|
||||
/// Its size in bytes.
|
||||
late final _isd.ColumnInt byteSize;
|
||||
|
||||
/// The encryption key's id; always null (ADR 0002), as on studysets.
|
||||
late final _isd.ColumnString keyId;
|
||||
|
||||
/// The content as UTF-8 JSON: {"schemaVersion", "fileName"}; the name it came
|
||||
/// with is content, like a studyset's title.
|
||||
late final _isd.ColumnByteData payload;
|
||||
|
||||
/// The encryption nonce; always null, like keyId.
|
||||
late final _isd.ColumnByteData nonce;
|
||||
|
||||
@override
|
||||
List<_isd.Column> get columns => [
|
||||
id,
|
||||
spaceId,
|
||||
sha256,
|
||||
mimeType,
|
||||
byteSize,
|
||||
keyId,
|
||||
payload,
|
||||
nonce,
|
||||
];
|
||||
}
|
||||
|
||||
class AssetInclude extends _isd.IncludeObject {
|
||||
AssetInclude._();
|
||||
|
||||
@override
|
||||
Map<String, _isd.Include?> get includes => {};
|
||||
|
||||
@override
|
||||
_isd.Table<_isc.UuidValue?> get table => Asset.t;
|
||||
}
|
||||
|
||||
class AssetIncludeList extends _isd.IncludeList {
|
||||
AssetIncludeList._({
|
||||
_isd.WhereExpressionBuilder<AssetTable>? where,
|
||||
super.limit,
|
||||
super.offset,
|
||||
super.orderBy,
|
||||
super.orderByList,
|
||||
super.include,
|
||||
}) {
|
||||
super.where = where?.call(Asset.t);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, _isd.Include?> get includes => include?.includes ?? {};
|
||||
|
||||
@override
|
||||
_isd.Table<_isc.UuidValue?> get table => Asset.t;
|
||||
}
|
||||
|
||||
class AssetRepository {
|
||||
const AssetRepository._();
|
||||
|
||||
/// Returns a list of [Asset]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<Asset>> find(
|
||||
_isd.DatabaseSession session, {
|
||||
_isd.WhereExpressionBuilder<AssetTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_isd.OrderByBuilder<AssetTable>? orderBy,
|
||||
_isd.OrderByListBuilder<AssetTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
_isd.LockMode? lockMode,
|
||||
_isd.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.find<Asset>(
|
||||
where: where?.call(Asset.t),
|
||||
orderBy: orderBy?.call(Asset.t),
|
||||
orderByList: orderByList?.call(Asset.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns the first matching [Asset] 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<Asset?> findFirstRow(
|
||||
_isd.DatabaseSession session, {
|
||||
_isd.WhereExpressionBuilder<AssetTable>? where,
|
||||
int? offset,
|
||||
_isd.OrderByBuilder<AssetTable>? orderBy,
|
||||
_isd.OrderByListBuilder<AssetTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
_isd.LockMode? lockMode,
|
||||
_isd.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findFirstRow<Asset>(
|
||||
where: where?.call(Asset.t),
|
||||
orderBy: orderBy?.call(Asset.t),
|
||||
orderByList: orderByList?.call(Asset.t),
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Finds a single [Asset] by its [id] or null if no such row exists.
|
||||
Future<Asset?> findById(
|
||||
_isd.DatabaseSession session,
|
||||
_isc.UuidValue id, {
|
||||
_isd.Transaction? transaction,
|
||||
_isd.LockMode? lockMode,
|
||||
_isd.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findById<Asset>(
|
||||
id,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts all [Asset]s in the list and returns the inserted rows.
|
||||
///
|
||||
/// The returned [Asset]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<Asset>> insert(
|
||||
_isd.DatabaseSession session,
|
||||
List<Asset> rows, {
|
||||
_isd.Transaction? transaction,
|
||||
bool ignoreConflicts = false,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.insert<Asset>(
|
||||
rows,
|
||||
transaction: transaction,
|
||||
ignoreConflicts: ignoreConflicts,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts a single [Asset] and returns the inserted row.
|
||||
///
|
||||
/// The returned [Asset] will have its `id` field set.
|
||||
Future<Asset> insertRow(
|
||||
_isd.DatabaseSession session,
|
||||
Asset row, {
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.insertRow<Asset>(
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts all [Asset]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 [Asset]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<Asset>> upsert(
|
||||
_isd.DatabaseSession session,
|
||||
List<Asset> rows, {
|
||||
required _isd.ColumnSelections<AssetTable> conflictColumns,
|
||||
_isd.ColumnSelections<AssetTable>? updateColumns,
|
||||
_isd.WhereExpressionBuilder<AssetTable>? updateWhere,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.upsert<Asset>(
|
||||
rows,
|
||||
conflictColumns: conflictColumns(Asset.t),
|
||||
updateColumns: updateColumns?.call(Asset.t),
|
||||
updateWhere: updateWhere?.call(Asset.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts a single [Asset] 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 [Asset] will have its `id` field set.
|
||||
Future<Asset?> upsertRow(
|
||||
_isd.DatabaseSession session,
|
||||
Asset row, {
|
||||
required _isd.ColumnSelections<AssetTable> conflictColumns,
|
||||
_isd.ColumnSelections<AssetTable>? updateColumns,
|
||||
_isd.WhereExpressionBuilder<AssetTable>? updateWhere,
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.upsertRow<Asset>(
|
||||
row,
|
||||
conflictColumns: conflictColumns(Asset.t),
|
||||
updateColumns: updateColumns?.call(Asset.t),
|
||||
updateWhere: updateWhere?.call(Asset.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [Asset]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<Asset>> update(
|
||||
_isd.DatabaseSession session,
|
||||
List<Asset> rows, {
|
||||
_isd.ColumnSelections<AssetTable>? columns,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.update<Asset>(
|
||||
rows,
|
||||
columns: columns?.call(Asset.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [Asset]. 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<Asset> updateRow(
|
||||
_isd.DatabaseSession session,
|
||||
Asset row, {
|
||||
_isd.ColumnSelections<AssetTable>? columns,
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateRow<Asset>(
|
||||
row,
|
||||
columns: columns?.call(Asset.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [Asset] by its [id] with the specified [columnValues].
|
||||
/// Returns the updated row or null if no row with the given id exists.
|
||||
Future<Asset?> updateById(
|
||||
_isd.DatabaseSession session,
|
||||
_isc.UuidValue id, {
|
||||
required _isd.ColumnValueListBuilder<AssetUpdateTable> columnValues,
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateById<Asset>(
|
||||
id,
|
||||
columnValues: columnValues(Asset.t.updateTable),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [Asset]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<Asset>> updateWhere(
|
||||
_isd.DatabaseSession session, {
|
||||
required _isd.ColumnValueListBuilder<AssetUpdateTable> columnValues,
|
||||
required _isd.WhereExpressionBuilder<AssetTable> where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_isd.OrderByBuilder<AssetTable>? orderBy,
|
||||
_isd.OrderByListBuilder<AssetTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.updateWhere<Asset>(
|
||||
columnValues: columnValues(Asset.t.updateTable),
|
||||
where: where(Asset.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(Asset.t),
|
||||
orderByList: orderByList?.call(Asset.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes all [Asset]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<Asset>> delete(
|
||||
_isd.DatabaseSession session,
|
||||
List<Asset> rows, {
|
||||
_isd.OrderByBuilder<AssetTable>? orderBy,
|
||||
_isd.OrderByListBuilder<AssetTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.delete<Asset>(
|
||||
rows,
|
||||
orderBy: orderBy?.call(Asset.t),
|
||||
orderByList: orderByList?.call(Asset.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes a single [Asset].
|
||||
Future<Asset> deleteRow(
|
||||
_isd.DatabaseSession session,
|
||||
Asset row, {
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.deleteRow<Asset>(
|
||||
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<Asset>> deleteWhere(
|
||||
_isd.DatabaseSession session, {
|
||||
required _isd.WhereExpressionBuilder<AssetTable> where,
|
||||
_isd.OrderByBuilder<AssetTable>? orderBy,
|
||||
_isd.OrderByListBuilder<AssetTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.deleteWhere<Asset>(
|
||||
where: where(Asset.t),
|
||||
orderBy: orderBy?.call(Asset.t),
|
||||
orderByList: orderByList?.call(Asset.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<AssetTable>? where,
|
||||
int? limit,
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.count<Asset>(
|
||||
where: where?.call(Asset.t),
|
||||
limit: limit,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Acquires row-level locks on [Asset] rows matching the [where] expression.
|
||||
Future<void> lockRows(
|
||||
_isd.DatabaseSession session, {
|
||||
required _isd.WhereExpressionBuilder<AssetTable> where,
|
||||
required _isd.LockMode lockMode,
|
||||
required _isd.Transaction transaction,
|
||||
_isd.LockBehavior lockBehavior = _isd.LockBehavior.wait,
|
||||
}) async {
|
||||
return session.db.lockRows<Asset>(
|
||||
where: where(Asset.t),
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ 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 'asset/asset.dart' as _ipb7vkxr;
|
||||
import 'auth/device.dart' as _i6c5ny7n;
|
||||
import 'auth/github_sign_in_refusal.dart' as _ir1gd9p5;
|
||||
import 'auth/github_sign_in_refused_exception.dart' as _i0xfcu5n;
|
||||
|
|
@ -29,7 +30,11 @@ import 'card/card_revision.dart' as _ipbyuef1;
|
|||
import 'common/not_found_exception.dart' as _i8oee73b;
|
||||
import 'instance/instance_info.dart' as _iskrg2g3;
|
||||
import 'review/review_log.dart' as _irzjbdqz;
|
||||
import 'settings/person_settings.dart' as _irnhx5e5;
|
||||
import 'studyset/studyset.dart' as _ikbclmae;
|
||||
import 'tag/card_tag.dart' as _i6jz6nyq;
|
||||
import 'tag/tag.dart' as _ie8t3ar4;
|
||||
export 'asset/asset.dart';
|
||||
export 'auth/device.dart';
|
||||
export 'auth/github_sign_in_refusal.dart';
|
||||
export 'auth/github_sign_in_refused_exception.dart';
|
||||
|
|
@ -38,7 +43,10 @@ export 'card/card_revision.dart';
|
|||
export 'common/not_found_exception.dart';
|
||||
export 'instance/instance_info.dart';
|
||||
export 'review/review_log.dart';
|
||||
export 'settings/person_settings.dart';
|
||||
export 'studyset/studyset.dart';
|
||||
export 'tag/card_tag.dart';
|
||||
export 'tag/tag.dart';
|
||||
export 'client.dart';
|
||||
export 'sync_tables.dart';
|
||||
|
||||
|
|
@ -50,6 +58,108 @@ class Protocol extends _isd.DatabaseSerializationManager {
|
|||
static final Protocol _instance = Protocol._().._registerHostProtocols();
|
||||
|
||||
static List<_isd.TableDefinition> get targetTableDefinitions => [
|
||||
_isd.TableDefinition(
|
||||
name: 'asset',
|
||||
dartName: 'Asset',
|
||||
schema: 'public',
|
||||
module: 'halkyon_learn',
|
||||
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: 'sha256',
|
||||
columnType: _isd.ColumnType.text,
|
||||
isNullable: false,
|
||||
dartType: 'String',
|
||||
),
|
||||
_isd.ColumnDefinition(
|
||||
name: 'mimeType',
|
||||
columnType: _isd.ColumnType.text,
|
||||
isNullable: false,
|
||||
dartType: 'String',
|
||||
),
|
||||
_isd.ColumnDefinition(
|
||||
name: 'byteSize',
|
||||
columnType: _isd.ColumnType.bigint,
|
||||
isNullable: false,
|
||||
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: 'asset_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: 'asset_space_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isd.IndexElementDefinition(
|
||||
type: _isd.IndexElementDefinitionType.column,
|
||||
definition: 'spaceId',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
_isd.IndexDefinition(
|
||||
indexName: 'asset_hash_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isd.IndexElementDefinition(
|
||||
type: _isd.IndexElementDefinitionType.column,
|
||||
definition: 'spaceId',
|
||||
),
|
||||
_isd.IndexElementDefinition(
|
||||
type: _isd.IndexElementDefinitionType.column,
|
||||
definition: 'sha256',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
],
|
||||
managed: true,
|
||||
),
|
||||
_isd.TableDefinition(
|
||||
name: 'card',
|
||||
dartName: 'Card',
|
||||
|
|
@ -254,6 +364,182 @@ class Protocol extends _isd.DatabaseSerializationManager {
|
|||
],
|
||||
managed: true,
|
||||
),
|
||||
_isd.TableDefinition(
|
||||
name: 'card_tag',
|
||||
dartName: 'CardTag',
|
||||
schema: 'public',
|
||||
module: 'halkyon_learn',
|
||||
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: 'cardId',
|
||||
columnType: _isd.ColumnType.uuid,
|
||||
isNullable: false,
|
||||
dartType: 'UuidValue',
|
||||
),
|
||||
_isd.ColumnDefinition(
|
||||
name: 'tagId',
|
||||
columnType: _isd.ColumnType.uuid,
|
||||
isNullable: false,
|
||||
dartType: 'UuidValue',
|
||||
),
|
||||
],
|
||||
foreignKeys: [
|
||||
_isd.ForeignKeyDefinition(
|
||||
constraintName: 'card_tag_fk_0',
|
||||
columns: ['spaceId'],
|
||||
referenceTable: 'offline_sync_spaces',
|
||||
referenceTableSchema: 'public',
|
||||
referenceColumns: ['id'],
|
||||
onUpdate: _isd.ForeignKeyAction.noAction,
|
||||
onDelete: _isd.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
),
|
||||
_isd.ForeignKeyDefinition(
|
||||
constraintName: 'card_tag_fk_1',
|
||||
columns: ['cardId'],
|
||||
referenceTable: 'card',
|
||||
referenceTableSchema: 'public',
|
||||
referenceColumns: ['id'],
|
||||
onUpdate: _isd.ForeignKeyAction.noAction,
|
||||
onDelete: _isd.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
deferrable: _isd.DeferrableConstraint.initiallyDeferred,
|
||||
),
|
||||
_isd.ForeignKeyDefinition(
|
||||
constraintName: 'card_tag_fk_2',
|
||||
columns: ['tagId'],
|
||||
referenceTable: 'tag',
|
||||
referenceTableSchema: 'public',
|
||||
referenceColumns: ['id'],
|
||||
onUpdate: _isd.ForeignKeyAction.noAction,
|
||||
onDelete: _isd.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
deferrable: _isd.DeferrableConstraint.initiallyDeferred,
|
||||
),
|
||||
],
|
||||
indexes: [
|
||||
_isd.IndexDefinition(
|
||||
indexName: 'card_tag_space_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isd.IndexElementDefinition(
|
||||
type: _isd.IndexElementDefinitionType.column,
|
||||
definition: 'spaceId',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
_isd.IndexDefinition(
|
||||
indexName: 'card_tag_card_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isd.IndexElementDefinition(
|
||||
type: _isd.IndexElementDefinitionType.column,
|
||||
definition: 'cardId',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
_isd.IndexDefinition(
|
||||
indexName: 'card_tag_tag_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isd.IndexElementDefinition(
|
||||
type: _isd.IndexElementDefinitionType.column,
|
||||
definition: 'tagId',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
],
|
||||
managed: true,
|
||||
),
|
||||
_isd.TableDefinition(
|
||||
name: 'person_settings',
|
||||
dartName: 'PersonSettings',
|
||||
schema: 'public',
|
||||
module: 'halkyon_learn',
|
||||
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: 'person_settings_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: 'person_settings_space_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isd.IndexElementDefinition(
|
||||
type: _isd.IndexElementDefinitionType.column,
|
||||
definition: 'spaceId',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
],
|
||||
managed: true,
|
||||
),
|
||||
_isd.TableDefinition(
|
||||
name: 'review_log',
|
||||
dartName: 'ReviewLog',
|
||||
|
|
@ -297,6 +583,13 @@ class Protocol extends _isd.DatabaseSerializationManager {
|
|||
isNullable: false,
|
||||
dartType: 'int',
|
||||
),
|
||||
_isd.ColumnDefinition(
|
||||
name: 'mode',
|
||||
columnType: _isd.ColumnType.text,
|
||||
isNullable: false,
|
||||
dartType: 'String',
|
||||
columnDefault: '\'review\'',
|
||||
),
|
||||
],
|
||||
foreignKeys: [
|
||||
_isd.ForeignKeyDefinition(
|
||||
|
|
@ -374,6 +667,19 @@ class Protocol extends _isd.DatabaseSerializationManager {
|
|||
isNullable: true,
|
||||
dartType: 'int?',
|
||||
),
|
||||
_isd.ColumnDefinition(
|
||||
name: 'parentId',
|
||||
columnType: _isd.ColumnType.uuid,
|
||||
isNullable: true,
|
||||
dartType: 'UuidValue?',
|
||||
),
|
||||
_isd.ColumnDefinition(
|
||||
name: 'position',
|
||||
columnType: _isd.ColumnType.text,
|
||||
isNullable: false,
|
||||
dartType: 'String',
|
||||
columnDefault: '\'a0\'',
|
||||
),
|
||||
_isd.ColumnDefinition(
|
||||
name: 'keyId',
|
||||
columnType: _isd.ColumnType.text,
|
||||
|
|
@ -404,6 +710,17 @@ class Protocol extends _isd.DatabaseSerializationManager {
|
|||
onDelete: _isd.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
),
|
||||
_isd.ForeignKeyDefinition(
|
||||
constraintName: 'studyset_fk_1',
|
||||
columns: ['parentId'],
|
||||
referenceTable: 'studyset',
|
||||
referenceTableSchema: 'public',
|
||||
referenceColumns: ['id'],
|
||||
onUpdate: _isd.ForeignKeyAction.noAction,
|
||||
onDelete: _isd.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
deferrable: _isd.DeferrableConstraint.initiallyDeferred,
|
||||
),
|
||||
],
|
||||
indexes: [
|
||||
_isd.IndexDefinition(
|
||||
|
|
@ -419,6 +736,90 @@ class Protocol extends _isd.DatabaseSerializationManager {
|
|||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
_isd.IndexDefinition(
|
||||
indexName: 'studyset_parent_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isd.IndexElementDefinition(
|
||||
type: _isd.IndexElementDefinitionType.column,
|
||||
definition: 'parentId',
|
||||
),
|
||||
_isd.IndexElementDefinition(
|
||||
type: _isd.IndexElementDefinitionType.column,
|
||||
definition: 'position',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
],
|
||||
managed: true,
|
||||
),
|
||||
_isd.TableDefinition(
|
||||
name: 'tag',
|
||||
dartName: 'Tag',
|
||||
schema: 'public',
|
||||
module: 'halkyon_learn',
|
||||
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: 'tag_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: 'tag_space_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isd.IndexElementDefinition(
|
||||
type: _isd.IndexElementDefinitionType.column,
|
||||
definition: 'spaceId',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
],
|
||||
managed: true,
|
||||
),
|
||||
|
|
@ -463,6 +864,9 @@ class Protocol extends _isd.DatabaseSerializationManager {
|
|||
}
|
||||
}
|
||||
|
||||
if (t == _ipb7vkxr.Asset) {
|
||||
return _ipb7vkxr.Asset.fromJson(data) as T;
|
||||
}
|
||||
if (t == _i6c5ny7n.Device) {
|
||||
return _i6c5ny7n.Device.fromJson(data) as T;
|
||||
}
|
||||
|
|
@ -487,9 +891,21 @@ class Protocol extends _isd.DatabaseSerializationManager {
|
|||
if (t == _irzjbdqz.ReviewLog) {
|
||||
return _irzjbdqz.ReviewLog.fromJson(data) as T;
|
||||
}
|
||||
if (t == _irnhx5e5.PersonSettings) {
|
||||
return _irnhx5e5.PersonSettings.fromJson(data) as T;
|
||||
}
|
||||
if (t == _ikbclmae.Studyset) {
|
||||
return _ikbclmae.Studyset.fromJson(data) as T;
|
||||
}
|
||||
if (t == _i6jz6nyq.CardTag) {
|
||||
return _i6jz6nyq.CardTag.fromJson(data) as T;
|
||||
}
|
||||
if (t == _ie8t3ar4.Tag) {
|
||||
return _ie8t3ar4.Tag.fromJson(data) as T;
|
||||
}
|
||||
if (t == _isc.getType<_ipb7vkxr.Asset?>()) {
|
||||
return (data != null ? _ipb7vkxr.Asset.fromJson(data) : null) as T;
|
||||
}
|
||||
if (t == _isc.getType<_i6c5ny7n.Device?>()) {
|
||||
return (data != null ? _i6c5ny7n.Device.fromJson(data) : null) as T;
|
||||
}
|
||||
|
|
@ -521,9 +937,19 @@ class Protocol extends _isd.DatabaseSerializationManager {
|
|||
if (t == _isc.getType<_irzjbdqz.ReviewLog?>()) {
|
||||
return (data != null ? _irzjbdqz.ReviewLog.fromJson(data) : null) as T;
|
||||
}
|
||||
if (t == _isc.getType<_irnhx5e5.PersonSettings?>()) {
|
||||
return (data != null ? _irnhx5e5.PersonSettings.fromJson(data) : null)
|
||||
as T;
|
||||
}
|
||||
if (t == _isc.getType<_ikbclmae.Studyset?>()) {
|
||||
return (data != null ? _ikbclmae.Studyset.fromJson(data) : null) as T;
|
||||
}
|
||||
if (t == _isc.getType<_i6jz6nyq.CardTag?>()) {
|
||||
return (data != null ? _i6jz6nyq.CardTag.fromJson(data) : null) as T;
|
||||
}
|
||||
if (t == _isc.getType<_ie8t3ar4.Tag?>()) {
|
||||
return (data != null ? _ie8t3ar4.Tag.fromJson(data) : null) as T;
|
||||
}
|
||||
if (t == List<_isc.UuidValue>) {
|
||||
return (data as List).map((e) => deserialize<_isc.UuidValue>(e)).toList()
|
||||
as T;
|
||||
|
|
@ -548,6 +974,7 @@ class Protocol extends _isd.DatabaseSerializationManager {
|
|||
|
||||
static String? getClassNameForType(Type type) {
|
||||
return switch (type) {
|
||||
_ipb7vkxr.Asset => 'Asset',
|
||||
_i6c5ny7n.Device => 'Device',
|
||||
_ir1gd9p5.GitHubSignInRefusal => 'GitHubSignInRefusal',
|
||||
_i0xfcu5n.GitHubSignInRefusedException => 'GitHubSignInRefusedException',
|
||||
|
|
@ -556,7 +983,10 @@ class Protocol extends _isd.DatabaseSerializationManager {
|
|||
_i8oee73b.NotFoundException => 'NotFoundException',
|
||||
_iskrg2g3.InstanceInfo => 'InstanceInfo',
|
||||
_irzjbdqz.ReviewLog => 'ReviewLog',
|
||||
_irnhx5e5.PersonSettings => 'PersonSettings',
|
||||
_ikbclmae.Studyset => 'Studyset',
|
||||
_i6jz6nyq.CardTag => 'CardTag',
|
||||
_ie8t3ar4.Tag => 'Tag',
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
|
@ -574,6 +1004,8 @@ class Protocol extends _isd.DatabaseSerializationManager {
|
|||
}
|
||||
|
||||
switch (data) {
|
||||
case _ipb7vkxr.Asset():
|
||||
return 'Asset';
|
||||
case _i6c5ny7n.Device():
|
||||
return 'Device';
|
||||
case _ir1gd9p5.GitHubSignInRefusal():
|
||||
|
|
@ -590,8 +1022,14 @@ class Protocol extends _isd.DatabaseSerializationManager {
|
|||
return 'InstanceInfo';
|
||||
case _irzjbdqz.ReviewLog():
|
||||
return 'ReviewLog';
|
||||
case _irnhx5e5.PersonSettings():
|
||||
return 'PersonSettings';
|
||||
case _ikbclmae.Studyset():
|
||||
return 'Studyset';
|
||||
case _i6jz6nyq.CardTag():
|
||||
return 'CardTag';
|
||||
case _ie8t3ar4.Tag():
|
||||
return 'Tag';
|
||||
}
|
||||
className = _iaic.Protocol().getClassNameForObject(data);
|
||||
if (className != null) {
|
||||
|
|
@ -620,6 +1058,9 @@ class Protocol extends _isd.DatabaseSerializationManager {
|
|||
if (dataClassName is! String) {
|
||||
return super.deserializeByClassName(data);
|
||||
}
|
||||
if (dataClassName == 'Asset') {
|
||||
return deserialize<_ipb7vkxr.Asset>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'Device') {
|
||||
return deserialize<_i6c5ny7n.Device>(data['data']);
|
||||
}
|
||||
|
|
@ -644,9 +1085,18 @@ class Protocol extends _isd.DatabaseSerializationManager {
|
|||
if (dataClassName == 'ReviewLog') {
|
||||
return deserialize<_irzjbdqz.ReviewLog>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'PersonSettings') {
|
||||
return deserialize<_irnhx5e5.PersonSettings>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'Studyset') {
|
||||
return deserialize<_ikbclmae.Studyset>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'CardTag') {
|
||||
return deserialize<_i6jz6nyq.CardTag>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'Tag') {
|
||||
return deserialize<_ie8t3ar4.Tag>(data['data']);
|
||||
}
|
||||
if (dataClassName.startsWith('serverpod_auth_idp.')) {
|
||||
data['className'] = dataClassName.substring(19);
|
||||
return _iaic.Protocol().deserializeByClassName(data);
|
||||
|
|
@ -698,14 +1148,22 @@ class Protocol extends _isd.DatabaseSerializationManager {
|
|||
}
|
||||
}
|
||||
switch (t) {
|
||||
case _ipb7vkxr.Asset:
|
||||
return _ipb7vkxr.Asset.t;
|
||||
case _ipiw1cmx.Card:
|
||||
return _ipiw1cmx.Card.t;
|
||||
case _ipbyuef1.CardRevision:
|
||||
return _ipbyuef1.CardRevision.t;
|
||||
case _irzjbdqz.ReviewLog:
|
||||
return _irzjbdqz.ReviewLog.t;
|
||||
case _irnhx5e5.PersonSettings:
|
||||
return _irnhx5e5.PersonSettings.t;
|
||||
case _ikbclmae.Studyset:
|
||||
return _ikbclmae.Studyset.t;
|
||||
case _i6jz6nyq.CardTag:
|
||||
return _i6jz6nyq.CardTag.t;
|
||||
case _ie8t3ar4.Tag:
|
||||
return _ie8t3ar4.Tag.t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ abstract class ReviewLog
|
|||
required this.rating,
|
||||
required this.reviewedAt,
|
||||
required this.durationMs,
|
||||
});
|
||||
String? mode,
|
||||
}) : mode = mode ?? 'review';
|
||||
|
||||
factory ReviewLog({
|
||||
_isc.UuidValue? id,
|
||||
|
|
@ -40,6 +41,7 @@ abstract class ReviewLog
|
|||
required int rating,
|
||||
required DateTime reviewedAt,
|
||||
required int durationMs,
|
||||
String? mode,
|
||||
}) = _ReviewLogImpl;
|
||||
|
||||
factory ReviewLog.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
|
|
@ -59,6 +61,7 @@ abstract class ReviewLog
|
|||
jsonSerialization['reviewedAt'],
|
||||
),
|
||||
durationMs: jsonSerialization['durationMs'] as int,
|
||||
mode: jsonSerialization['mode'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -88,6 +91,12 @@ abstract class ReviewLog
|
|||
/// milliseconds.
|
||||
int durationMs;
|
||||
|
||||
/// How the card was studied (ADR 0004): 'review' (front to back), 'reverse'
|
||||
/// (back to front, with a schedule of its own, as Anki's reverse cards have),
|
||||
/// or 'cram' (outside the schedule: counts for streaks and the activity
|
||||
/// heatmap, never moves a schedule). Reviews from before modes are 'review'.
|
||||
String mode;
|
||||
|
||||
@override
|
||||
_isd.Table<_isc.UuidValue?> get table => t;
|
||||
|
||||
|
|
@ -102,6 +111,7 @@ abstract class ReviewLog
|
|||
int? rating,
|
||||
DateTime? reviewedAt,
|
||||
int? durationMs,
|
||||
String? mode,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
|
|
@ -114,6 +124,7 @@ abstract class ReviewLog
|
|||
'rating': rating,
|
||||
'reviewedAt': reviewedAt.toJson(),
|
||||
'durationMs': durationMs,
|
||||
'mode': mode,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -128,6 +139,7 @@ abstract class ReviewLog
|
|||
'rating': rating,
|
||||
'reviewedAt': reviewedAt.toJson(),
|
||||
'durationMs': durationMs,
|
||||
'mode': mode,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -170,6 +182,7 @@ class _ReviewLogImpl extends ReviewLog {
|
|||
required int rating,
|
||||
required DateTime reviewedAt,
|
||||
required int durationMs,
|
||||
String? mode,
|
||||
}) : super._(
|
||||
id: id,
|
||||
spaceId: spaceId,
|
||||
|
|
@ -178,6 +191,7 @@ class _ReviewLogImpl extends ReviewLog {
|
|||
rating: rating,
|
||||
reviewedAt: reviewedAt,
|
||||
durationMs: durationMs,
|
||||
mode: mode,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [ReviewLog]
|
||||
|
|
@ -192,6 +206,7 @@ class _ReviewLogImpl extends ReviewLog {
|
|||
int? rating,
|
||||
DateTime? reviewedAt,
|
||||
int? durationMs,
|
||||
String? mode,
|
||||
}) {
|
||||
return ReviewLog(
|
||||
id: id is _isc.UuidValue? ? id : this.id,
|
||||
|
|
@ -201,6 +216,7 @@ class _ReviewLogImpl extends ReviewLog {
|
|||
rating: rating ?? this.rating,
|
||||
reviewedAt: reviewedAt ?? this.reviewedAt,
|
||||
durationMs: durationMs ?? this.durationMs,
|
||||
mode: mode ?? this.mode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -235,6 +251,11 @@ class ReviewLogUpdateTable extends _isd.UpdateTable<ReviewLogTable> {
|
|||
table.durationMs,
|
||||
value,
|
||||
);
|
||||
|
||||
_isd.ColumnValue<String, String> mode(String value) => _isd.ColumnValue(
|
||||
table.mode,
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
class ReviewLogTable extends _isd.Table<_isc.UuidValue?> {
|
||||
|
|
@ -260,6 +281,11 @@ class ReviewLogTable extends _isd.Table<_isc.UuidValue?> {
|
|||
'durationMs',
|
||||
this,
|
||||
);
|
||||
mode = _isd.ColumnString(
|
||||
'mode',
|
||||
this,
|
||||
hasDefault: true,
|
||||
);
|
||||
}
|
||||
|
||||
late final ReviewLogUpdateTable updateTable;
|
||||
|
|
@ -283,6 +309,12 @@ class ReviewLogTable extends _isd.Table<_isc.UuidValue?> {
|
|||
/// milliseconds.
|
||||
late final _isd.ColumnInt durationMs;
|
||||
|
||||
/// How the card was studied (ADR 0004): 'review' (front to back), 'reverse'
|
||||
/// (back to front, with a schedule of its own, as Anki's reverse cards have),
|
||||
/// or 'cram' (outside the schedule: counts for streaks and the activity
|
||||
/// heatmap, never moves a schedule). Reviews from before modes are 'review'.
|
||||
late final _isd.ColumnString mode;
|
||||
|
||||
_i4926i81.CardTable get card {
|
||||
if (_card != null) return _card!;
|
||||
_card = _isd.createRelationTable(
|
||||
|
|
@ -304,6 +336,7 @@ class ReviewLogTable extends _isd.Table<_isc.UuidValue?> {
|
|||
rating,
|
||||
reviewedAt,
|
||||
durationMs,
|
||||
mode,
|
||||
];
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -0,0 +1,677 @@
|
|||
/* 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 person's settings that follow them to every device (ADR 0004): study goals
|
||||
/// now, other preferences later. The content lives in the payload, so a new
|
||||
/// setting changes the payload's schema version, not this synced table.
|
||||
///
|
||||
/// Meant as one row per person. Two devices creating it while offline make two;
|
||||
/// the app then keeps the one with the smallest id, folding the other's settings
|
||||
/// in. Its id is random like every other row's: an id derived from the person
|
||||
/// could be claimed first by someone else, and would then break their sync.
|
||||
abstract class PersonSettings
|
||||
implements _isd.TableRow<_isc.UuidValue?>, _isc.ProtocolSerialization {
|
||||
PersonSettings._({
|
||||
this.id,
|
||||
this.spaceId,
|
||||
this.keyId,
|
||||
required this.payload,
|
||||
this.nonce,
|
||||
});
|
||||
|
||||
factory PersonSettings({
|
||||
_isc.UuidValue? id,
|
||||
int? spaceId,
|
||||
String? keyId,
|
||||
required _idt.ByteData payload,
|
||||
_idt.ByteData? nonce,
|
||||
}) = _PersonSettingsImpl;
|
||||
|
||||
factory PersonSettings.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return PersonSettings(
|
||||
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 = PersonSettingsTable();
|
||||
|
||||
static const db = PersonSettingsRepository._();
|
||||
|
||||
@override
|
||||
_isc.UuidValue? id;
|
||||
|
||||
/// The space owning this row. Maintained by the sync engine.
|
||||
int? spaceId;
|
||||
|
||||
/// The encryption key's id; always null (ADR 0002), as on studysets.
|
||||
String? keyId;
|
||||
|
||||
/// The content as UTF-8 JSON: {"schemaVersion", …}.
|
||||
_idt.ByteData payload;
|
||||
|
||||
/// The encryption nonce; always null, like keyId.
|
||||
_idt.ByteData? nonce;
|
||||
|
||||
@override
|
||||
_isd.Table<_isc.UuidValue?> get table => t;
|
||||
|
||||
/// Returns a shallow copy of this [PersonSettings]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
PersonSettings copyWith({
|
||||
_isc.UuidValue? id,
|
||||
int? spaceId,
|
||||
String? keyId,
|
||||
_idt.ByteData? payload,
|
||||
_idt.ByteData? nonce,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'PersonSettings',
|
||||
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__': 'PersonSettings',
|
||||
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 PersonSettingsInclude include() {
|
||||
return PersonSettingsInclude._();
|
||||
}
|
||||
|
||||
static PersonSettingsIncludeList includeList({
|
||||
_isd.WhereExpressionBuilder<PersonSettingsTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_isd.OrderByBuilder<PersonSettingsTable>? orderBy,
|
||||
_isd.OrderByListBuilder<PersonSettingsTable>? orderByList,
|
||||
PersonSettingsInclude? include,
|
||||
}) {
|
||||
return PersonSettingsIncludeList._(
|
||||
where: where,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(PersonSettings.t),
|
||||
orderByList: orderByList?.call(PersonSettings.t),
|
||||
include: include,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _isc.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _Undefined {}
|
||||
|
||||
class _PersonSettingsImpl extends PersonSettings {
|
||||
_PersonSettingsImpl({
|
||||
_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 [PersonSettings]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
@override
|
||||
PersonSettings copyWith({
|
||||
Object? id = _Undefined,
|
||||
Object? spaceId = _Undefined,
|
||||
Object? keyId = _Undefined,
|
||||
_idt.ByteData? payload,
|
||||
Object? nonce = _Undefined,
|
||||
}) {
|
||||
return PersonSettings(
|
||||
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 PersonSettingsUpdateTable extends _isd.UpdateTable<PersonSettingsTable> {
|
||||
PersonSettingsUpdateTable(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 PersonSettingsTable extends _isd.Table<_isc.UuidValue?> {
|
||||
PersonSettingsTable({super.tableRelation})
|
||||
: super(tableName: 'person_settings') {
|
||||
updateTable = PersonSettingsUpdateTable(this);
|
||||
spaceId = _isd.ColumnInt(
|
||||
'spaceId',
|
||||
this,
|
||||
);
|
||||
keyId = _isd.ColumnString(
|
||||
'keyId',
|
||||
this,
|
||||
);
|
||||
payload = _isd.ColumnByteData(
|
||||
'payload',
|
||||
this,
|
||||
);
|
||||
nonce = _isd.ColumnByteData(
|
||||
'nonce',
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
late final PersonSettingsUpdateTable updateTable;
|
||||
|
||||
/// The space owning this row. Maintained by the sync engine.
|
||||
late final _isd.ColumnInt spaceId;
|
||||
|
||||
/// The encryption key's id; always null (ADR 0002), as on studysets.
|
||||
late final _isd.ColumnString keyId;
|
||||
|
||||
/// The content as UTF-8 JSON: {"schemaVersion", …}.
|
||||
late final _isd.ColumnByteData payload;
|
||||
|
||||
/// The encryption nonce; always null, like keyId.
|
||||
late final _isd.ColumnByteData nonce;
|
||||
|
||||
@override
|
||||
List<_isd.Column> get columns => [
|
||||
id,
|
||||
spaceId,
|
||||
keyId,
|
||||
payload,
|
||||
nonce,
|
||||
];
|
||||
}
|
||||
|
||||
class PersonSettingsInclude extends _isd.IncludeObject {
|
||||
PersonSettingsInclude._();
|
||||
|
||||
@override
|
||||
Map<String, _isd.Include?> get includes => {};
|
||||
|
||||
@override
|
||||
_isd.Table<_isc.UuidValue?> get table => PersonSettings.t;
|
||||
}
|
||||
|
||||
class PersonSettingsIncludeList extends _isd.IncludeList {
|
||||
PersonSettingsIncludeList._({
|
||||
_isd.WhereExpressionBuilder<PersonSettingsTable>? where,
|
||||
super.limit,
|
||||
super.offset,
|
||||
super.orderBy,
|
||||
super.orderByList,
|
||||
super.include,
|
||||
}) {
|
||||
super.where = where?.call(PersonSettings.t);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, _isd.Include?> get includes => include?.includes ?? {};
|
||||
|
||||
@override
|
||||
_isd.Table<_isc.UuidValue?> get table => PersonSettings.t;
|
||||
}
|
||||
|
||||
class PersonSettingsRepository {
|
||||
const PersonSettingsRepository._();
|
||||
|
||||
/// Returns a list of [PersonSettings]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<PersonSettings>> find(
|
||||
_isd.DatabaseSession session, {
|
||||
_isd.WhereExpressionBuilder<PersonSettingsTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_isd.OrderByBuilder<PersonSettingsTable>? orderBy,
|
||||
_isd.OrderByListBuilder<PersonSettingsTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
_isd.LockMode? lockMode,
|
||||
_isd.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.find<PersonSettings>(
|
||||
where: where?.call(PersonSettings.t),
|
||||
orderBy: orderBy?.call(PersonSettings.t),
|
||||
orderByList: orderByList?.call(PersonSettings.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns the first matching [PersonSettings] 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<PersonSettings?> findFirstRow(
|
||||
_isd.DatabaseSession session, {
|
||||
_isd.WhereExpressionBuilder<PersonSettingsTable>? where,
|
||||
int? offset,
|
||||
_isd.OrderByBuilder<PersonSettingsTable>? orderBy,
|
||||
_isd.OrderByListBuilder<PersonSettingsTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
_isd.LockMode? lockMode,
|
||||
_isd.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findFirstRow<PersonSettings>(
|
||||
where: where?.call(PersonSettings.t),
|
||||
orderBy: orderBy?.call(PersonSettings.t),
|
||||
orderByList: orderByList?.call(PersonSettings.t),
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Finds a single [PersonSettings] by its [id] or null if no such row exists.
|
||||
Future<PersonSettings?> findById(
|
||||
_isd.DatabaseSession session,
|
||||
_isc.UuidValue id, {
|
||||
_isd.Transaction? transaction,
|
||||
_isd.LockMode? lockMode,
|
||||
_isd.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findById<PersonSettings>(
|
||||
id,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts all [PersonSettings]s in the list and returns the inserted rows.
|
||||
///
|
||||
/// The returned [PersonSettings]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<PersonSettings>> insert(
|
||||
_isd.DatabaseSession session,
|
||||
List<PersonSettings> rows, {
|
||||
_isd.Transaction? transaction,
|
||||
bool ignoreConflicts = false,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.insert<PersonSettings>(
|
||||
rows,
|
||||
transaction: transaction,
|
||||
ignoreConflicts: ignoreConflicts,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts a single [PersonSettings] and returns the inserted row.
|
||||
///
|
||||
/// The returned [PersonSettings] will have its `id` field set.
|
||||
Future<PersonSettings> insertRow(
|
||||
_isd.DatabaseSession session,
|
||||
PersonSettings row, {
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.insertRow<PersonSettings>(
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts all [PersonSettings]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 [PersonSettings]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<PersonSettings>> upsert(
|
||||
_isd.DatabaseSession session,
|
||||
List<PersonSettings> rows, {
|
||||
required _isd.ColumnSelections<PersonSettingsTable> conflictColumns,
|
||||
_isd.ColumnSelections<PersonSettingsTable>? updateColumns,
|
||||
_isd.WhereExpressionBuilder<PersonSettingsTable>? updateWhere,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.upsert<PersonSettings>(
|
||||
rows,
|
||||
conflictColumns: conflictColumns(PersonSettings.t),
|
||||
updateColumns: updateColumns?.call(PersonSettings.t),
|
||||
updateWhere: updateWhere?.call(PersonSettings.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts a single [PersonSettings] 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 [PersonSettings] will have its `id` field set.
|
||||
Future<PersonSettings?> upsertRow(
|
||||
_isd.DatabaseSession session,
|
||||
PersonSettings row, {
|
||||
required _isd.ColumnSelections<PersonSettingsTable> conflictColumns,
|
||||
_isd.ColumnSelections<PersonSettingsTable>? updateColumns,
|
||||
_isd.WhereExpressionBuilder<PersonSettingsTable>? updateWhere,
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.upsertRow<PersonSettings>(
|
||||
row,
|
||||
conflictColumns: conflictColumns(PersonSettings.t),
|
||||
updateColumns: updateColumns?.call(PersonSettings.t),
|
||||
updateWhere: updateWhere?.call(PersonSettings.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [PersonSettings]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<PersonSettings>> update(
|
||||
_isd.DatabaseSession session,
|
||||
List<PersonSettings> rows, {
|
||||
_isd.ColumnSelections<PersonSettingsTable>? columns,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.update<PersonSettings>(
|
||||
rows,
|
||||
columns: columns?.call(PersonSettings.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [PersonSettings]. 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<PersonSettings> updateRow(
|
||||
_isd.DatabaseSession session,
|
||||
PersonSettings row, {
|
||||
_isd.ColumnSelections<PersonSettingsTable>? columns,
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateRow<PersonSettings>(
|
||||
row,
|
||||
columns: columns?.call(PersonSettings.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [PersonSettings] by its [id] with the specified [columnValues].
|
||||
/// Returns the updated row or null if no row with the given id exists.
|
||||
Future<PersonSettings?> updateById(
|
||||
_isd.DatabaseSession session,
|
||||
_isc.UuidValue id, {
|
||||
required _isd.ColumnValueListBuilder<PersonSettingsUpdateTable>
|
||||
columnValues,
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateById<PersonSettings>(
|
||||
id,
|
||||
columnValues: columnValues(PersonSettings.t.updateTable),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [PersonSettings]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<PersonSettings>> updateWhere(
|
||||
_isd.DatabaseSession session, {
|
||||
required _isd.ColumnValueListBuilder<PersonSettingsUpdateTable>
|
||||
columnValues,
|
||||
required _isd.WhereExpressionBuilder<PersonSettingsTable> where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_isd.OrderByBuilder<PersonSettingsTable>? orderBy,
|
||||
_isd.OrderByListBuilder<PersonSettingsTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.updateWhere<PersonSettings>(
|
||||
columnValues: columnValues(PersonSettings.t.updateTable),
|
||||
where: where(PersonSettings.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(PersonSettings.t),
|
||||
orderByList: orderByList?.call(PersonSettings.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes all [PersonSettings]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<PersonSettings>> delete(
|
||||
_isd.DatabaseSession session,
|
||||
List<PersonSettings> rows, {
|
||||
_isd.OrderByBuilder<PersonSettingsTable>? orderBy,
|
||||
_isd.OrderByListBuilder<PersonSettingsTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.delete<PersonSettings>(
|
||||
rows,
|
||||
orderBy: orderBy?.call(PersonSettings.t),
|
||||
orderByList: orderByList?.call(PersonSettings.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes a single [PersonSettings].
|
||||
Future<PersonSettings> deleteRow(
|
||||
_isd.DatabaseSession session,
|
||||
PersonSettings row, {
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.deleteRow<PersonSettings>(
|
||||
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<PersonSettings>> deleteWhere(
|
||||
_isd.DatabaseSession session, {
|
||||
required _isd.WhereExpressionBuilder<PersonSettingsTable> where,
|
||||
_isd.OrderByBuilder<PersonSettingsTable>? orderBy,
|
||||
_isd.OrderByListBuilder<PersonSettingsTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.deleteWhere<PersonSettings>(
|
||||
where: where(PersonSettings.t),
|
||||
orderBy: orderBy?.call(PersonSettings.t),
|
||||
orderByList: orderByList?.call(PersonSettings.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<PersonSettingsTable>? where,
|
||||
int? limit,
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.count<PersonSettings>(
|
||||
where: where?.call(PersonSettings.t),
|
||||
limit: limit,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Acquires row-level locks on [PersonSettings] rows matching the [where] expression.
|
||||
Future<void> lockRows(
|
||||
_isd.DatabaseSession session, {
|
||||
required _isd.WhereExpressionBuilder<PersonSettingsTable> where,
|
||||
required _isd.LockMode lockMode,
|
||||
required _isd.Transaction transaction,
|
||||
_isd.LockBehavior lockBehavior = _isd.LockBehavior.wait,
|
||||
}) async {
|
||||
return session.db.lockRows<PersonSettings>(
|
||||
where: where(PersonSettings.t),
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -8,11 +8,14 @@
|
|||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
// ignore_for_file: dead_code, unnecessary_null_comparison
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:typed_data' as _idt;
|
||||
import 'package:halkyon_learn_client/src/protocol/protocol.dart' as _itx5j3f5;
|
||||
import 'package:serverpod_client/serverpod_client.dart' as _isc;
|
||||
import 'package:serverpod_database/serverpod_database.dart' as _isd;
|
||||
import '../studyset/studyset.dart' as _ie5k1jc2;
|
||||
|
||||
/// A studyset: the cards, notes and paths that belong together; what people study and
|
||||
/// share. Synced between a person's devices; its space owns it, so it has no owner column.
|
||||
|
|
@ -25,14 +28,20 @@ abstract class Studyset
|
|||
Studyset._({
|
||||
this.id,
|
||||
this.spaceId,
|
||||
this.parentId,
|
||||
this.parent,
|
||||
String? position,
|
||||
this.keyId,
|
||||
required this.payload,
|
||||
this.nonce,
|
||||
});
|
||||
}) : position = position ?? 'a0';
|
||||
|
||||
factory Studyset({
|
||||
_isc.UuidValue? id,
|
||||
int? spaceId,
|
||||
_isc.UuidValue? parentId,
|
||||
_ie5k1jc2.Studyset? parent,
|
||||
String? position,
|
||||
String? keyId,
|
||||
required _idt.ByteData payload,
|
||||
_idt.ByteData? nonce,
|
||||
|
|
@ -44,6 +53,15 @@ abstract class Studyset
|
|||
? null
|
||||
: _isc.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
|
||||
spaceId: jsonSerialization['spaceId'] as int?,
|
||||
parentId: jsonSerialization['parentId'] == null
|
||||
? null
|
||||
: _isc.UuidValueJsonExtension.fromJson(jsonSerialization['parentId']),
|
||||
parent: jsonSerialization['parent'] == null
|
||||
? null
|
||||
: _itx5j3f5.Protocol().deserialize<_ie5k1jc2.Studyset>(
|
||||
jsonSerialization['parent'],
|
||||
),
|
||||
position: jsonSerialization['position'] as String?,
|
||||
keyId: jsonSerialization['keyId'] as String?,
|
||||
payload: _isc.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['payload'],
|
||||
|
|
@ -64,6 +82,19 @@ abstract class Studyset
|
|||
/// The space owning this row. Maintained by the sync engine.
|
||||
int? spaceId;
|
||||
|
||||
_isc.UuidValue? parentId;
|
||||
|
||||
/// The studyset this one sits in; null at the top level (ADR 0004). A move is
|
||||
/// an update of this column, and the last writer wins. Two devices moving
|
||||
/// studysets into each other make a cycle, and a studyset whose parent is gone
|
||||
/// has none: the app shows both at the top level until someone moves them.
|
||||
_ie5k1jc2.Studyset? parent;
|
||||
|
||||
/// Where the studyset stands among its siblings: a fractional index, like a
|
||||
/// card's (ADR 0003). Studysets from before the tree all start at 'a0'; ties
|
||||
/// sort by id.
|
||||
String position;
|
||||
|
||||
/// The encryption key's id; always null, since content isn't end-to-end encrypted
|
||||
/// (ADR 0002). Kept so opt-in encryption of single studysets stays possible.
|
||||
String? keyId;
|
||||
|
|
@ -83,6 +114,9 @@ abstract class Studyset
|
|||
Studyset copyWith({
|
||||
_isc.UuidValue? id,
|
||||
int? spaceId,
|
||||
_isc.UuidValue? parentId,
|
||||
_ie5k1jc2.Studyset? parent,
|
||||
String? position,
|
||||
String? keyId,
|
||||
_idt.ByteData? payload,
|
||||
_idt.ByteData? nonce,
|
||||
|
|
@ -93,6 +127,9 @@ abstract class Studyset
|
|||
'__className__': 'Studyset',
|
||||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
if (parentId != null) 'parentId': parentId?.toJson(),
|
||||
if (parent != null) 'parent': parent?.toJson(),
|
||||
'position': position,
|
||||
if (keyId != null) 'keyId': keyId,
|
||||
'payload': payload.toJson(),
|
||||
if (nonce != null) 'nonce': nonce?.toJson(),
|
||||
|
|
@ -105,14 +142,17 @@ abstract class Studyset
|
|||
'__className__': 'Studyset',
|
||||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
if (parentId != null) 'parentId': parentId?.toJson(),
|
||||
if (parent != null) 'parent': parent?.toJsonForProtocol(),
|
||||
'position': position,
|
||||
if (keyId != null) 'keyId': keyId,
|
||||
'payload': payload.toJson(),
|
||||
if (nonce != null) 'nonce': nonce?.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
static StudysetInclude include() {
|
||||
return StudysetInclude._();
|
||||
static StudysetInclude include({_ie5k1jc2.StudysetInclude? parent}) {
|
||||
return StudysetInclude._(parent: parent);
|
||||
}
|
||||
|
||||
static StudysetIncludeList includeList({
|
||||
|
|
@ -145,12 +185,18 @@ class _StudysetImpl extends Studyset {
|
|||
_StudysetImpl({
|
||||
_isc.UuidValue? id,
|
||||
int? spaceId,
|
||||
_isc.UuidValue? parentId,
|
||||
_ie5k1jc2.Studyset? parent,
|
||||
String? position,
|
||||
String? keyId,
|
||||
required _idt.ByteData payload,
|
||||
_idt.ByteData? nonce,
|
||||
}) : super._(
|
||||
id: id,
|
||||
spaceId: spaceId,
|
||||
parentId: parentId,
|
||||
parent: parent,
|
||||
position: position,
|
||||
keyId: keyId,
|
||||
payload: payload,
|
||||
nonce: nonce,
|
||||
|
|
@ -163,6 +209,9 @@ class _StudysetImpl extends Studyset {
|
|||
Studyset copyWith({
|
||||
Object? id = _Undefined,
|
||||
Object? spaceId = _Undefined,
|
||||
Object? parentId = _Undefined,
|
||||
Object? parent = _Undefined,
|
||||
String? position,
|
||||
Object? keyId = _Undefined,
|
||||
_idt.ByteData? payload,
|
||||
Object? nonce = _Undefined,
|
||||
|
|
@ -170,6 +219,9 @@ class _StudysetImpl extends Studyset {
|
|||
return Studyset(
|
||||
id: id is _isc.UuidValue? ? id : this.id,
|
||||
spaceId: spaceId is int? ? spaceId : this.spaceId,
|
||||
parentId: parentId is _isc.UuidValue? ? parentId : this.parentId,
|
||||
parent: parent is _ie5k1jc2.Studyset? ? parent : this.parent?.copyWith(),
|
||||
position: position ?? this.position,
|
||||
keyId: keyId is String? ? keyId : this.keyId,
|
||||
payload: payload ?? this.payload.clone(),
|
||||
nonce: nonce is _idt.ByteData? ? nonce : this.nonce?.clone(),
|
||||
|
|
@ -185,6 +237,18 @@ class StudysetUpdateTable extends _isd.UpdateTable<StudysetTable> {
|
|||
value,
|
||||
);
|
||||
|
||||
_isd.ColumnValue<_isc.UuidValue, _isc.UuidValue> parentId(
|
||||
_isc.UuidValue? value,
|
||||
) => _isd.ColumnValue(
|
||||
table.parentId,
|
||||
value,
|
||||
);
|
||||
|
||||
_isd.ColumnValue<String, String> position(String value) => _isd.ColumnValue(
|
||||
table.position,
|
||||
value,
|
||||
);
|
||||
|
||||
_isd.ColumnValue<String, String> keyId(String? value) => _isd.ColumnValue(
|
||||
table.keyId,
|
||||
value,
|
||||
|
|
@ -210,6 +274,15 @@ class StudysetTable extends _isd.Table<_isc.UuidValue?> {
|
|||
'spaceId',
|
||||
this,
|
||||
);
|
||||
parentId = _isd.ColumnUuid(
|
||||
'parentId',
|
||||
this,
|
||||
);
|
||||
position = _isd.ColumnString(
|
||||
'position',
|
||||
this,
|
||||
hasDefault: true,
|
||||
);
|
||||
keyId = _isd.ColumnString(
|
||||
'keyId',
|
||||
this,
|
||||
|
|
@ -229,6 +302,19 @@ class StudysetTable extends _isd.Table<_isc.UuidValue?> {
|
|||
/// The space owning this row. Maintained by the sync engine.
|
||||
late final _isd.ColumnInt spaceId;
|
||||
|
||||
late final _isd.ColumnUuid parentId;
|
||||
|
||||
/// The studyset this one sits in; null at the top level (ADR 0004). A move is
|
||||
/// an update of this column, and the last writer wins. Two devices moving
|
||||
/// studysets into each other make a cycle, and a studyset whose parent is gone
|
||||
/// has none: the app shows both at the top level until someone moves them.
|
||||
_ie5k1jc2.StudysetTable? _parent;
|
||||
|
||||
/// Where the studyset stands among its siblings: a fractional index, like a
|
||||
/// card's (ADR 0003). Studysets from before the tree all start at 'a0'; ties
|
||||
/// sort by id.
|
||||
late final _isd.ColumnString position;
|
||||
|
||||
/// The encryption key's id; always null, since content isn't end-to-end encrypted
|
||||
/// (ADR 0002). Kept so opt-in encryption of single studysets stays possible.
|
||||
late final _isd.ColumnString keyId;
|
||||
|
|
@ -239,21 +325,48 @@ class StudysetTable extends _isd.Table<_isc.UuidValue?> {
|
|||
/// The encryption nonce; always null, like keyId.
|
||||
late final _isd.ColumnByteData nonce;
|
||||
|
||||
_ie5k1jc2.StudysetTable get parent {
|
||||
if (_parent != null) return _parent!;
|
||||
_parent = _isd.createRelationTable(
|
||||
relationFieldName: 'parent',
|
||||
field: Studyset.t.parentId,
|
||||
foreignField: _ie5k1jc2.Studyset.t.id,
|
||||
tableRelation: tableRelation,
|
||||
createTable: (foreignTableRelation) =>
|
||||
_ie5k1jc2.StudysetTable(tableRelation: foreignTableRelation),
|
||||
);
|
||||
return _parent!;
|
||||
}
|
||||
|
||||
@override
|
||||
List<_isd.Column> get columns => [
|
||||
id,
|
||||
spaceId,
|
||||
parentId,
|
||||
position,
|
||||
keyId,
|
||||
payload,
|
||||
nonce,
|
||||
];
|
||||
|
||||
@override
|
||||
_isd.Table? getRelationTable(String relationField) {
|
||||
if (relationField == 'parent') {
|
||||
return parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class StudysetInclude extends _isd.IncludeObject {
|
||||
StudysetInclude._();
|
||||
StudysetInclude._({_ie5k1jc2.StudysetInclude? parent}) {
|
||||
_parent = parent;
|
||||
}
|
||||
|
||||
_ie5k1jc2.StudysetInclude? _parent;
|
||||
|
||||
@override
|
||||
Map<String, _isd.Include?> get includes => {};
|
||||
Map<String, _isd.Include?> get includes => {'parent': _parent};
|
||||
|
||||
@override
|
||||
_isd.Table<_isc.UuidValue?> get table => Studyset.t;
|
||||
|
|
@ -281,6 +394,10 @@ class StudysetIncludeList extends _isd.IncludeList {
|
|||
class StudysetRepository {
|
||||
const StudysetRepository._();
|
||||
|
||||
final attachRow = const StudysetAttachRowRepository._();
|
||||
|
||||
final detachRow = const StudysetDetachRowRepository._();
|
||||
|
||||
/// Returns a list of [Studyset]s matching the given query parameters.
|
||||
///
|
||||
/// Use [where] to specify which items to include in the return value.
|
||||
|
|
@ -311,6 +428,7 @@ class StudysetRepository {
|
|||
_isd.OrderByBuilder<StudysetTable>? orderBy,
|
||||
_isd.OrderByListBuilder<StudysetTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
StudysetInclude? include,
|
||||
_isd.LockMode? lockMode,
|
||||
_isd.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
|
|
@ -321,6 +439,7 @@ class StudysetRepository {
|
|||
limit: limit,
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
include: include,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
|
|
@ -350,6 +469,7 @@ class StudysetRepository {
|
|||
_isd.OrderByBuilder<StudysetTable>? orderBy,
|
||||
_isd.OrderByListBuilder<StudysetTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
StudysetInclude? include,
|
||||
_isd.LockMode? lockMode,
|
||||
_isd.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
|
|
@ -359,6 +479,7 @@ class StudysetRepository {
|
|||
orderByList: orderByList?.call(Studyset.t),
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
include: include,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
|
|
@ -369,12 +490,14 @@ class StudysetRepository {
|
|||
_isd.DatabaseSession session,
|
||||
_isc.UuidValue id, {
|
||||
_isd.Transaction? transaction,
|
||||
StudysetInclude? include,
|
||||
_isd.LockMode? lockMode,
|
||||
_isd.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findById<Studyset>(
|
||||
id,
|
||||
transaction: transaction,
|
||||
include: include,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
|
|
@ -672,3 +795,56 @@ class StudysetRepository {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class StudysetAttachRowRepository {
|
||||
const StudysetAttachRowRepository._();
|
||||
|
||||
/// Creates a relation between the given [Studyset] and [Studyset]
|
||||
/// by setting the [Studyset]'s foreign key `parentId` to refer to the [Studyset].
|
||||
Future<void> parent(
|
||||
_isd.DatabaseSession session,
|
||||
Studyset studyset,
|
||||
_ie5k1jc2.Studyset parent, {
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
if (studyset.id == null) {
|
||||
throw ArgumentError.notNull('studyset.id');
|
||||
}
|
||||
if (parent.id == null) {
|
||||
throw ArgumentError.notNull('parent.id');
|
||||
}
|
||||
|
||||
var $studyset = studyset.copyWith(parentId: parent.id);
|
||||
await session.db.updateRow<Studyset>(
|
||||
$studyset,
|
||||
columns: [Studyset.t.parentId],
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class StudysetDetachRowRepository {
|
||||
const StudysetDetachRowRepository._();
|
||||
|
||||
/// Detaches the relation between this [Studyset] and the [Studyset] set in `parent`
|
||||
/// by setting the [Studyset]'s foreign key `parentId` to `null`.
|
||||
///
|
||||
/// This removes the association between the two models without deleting
|
||||
/// the related record.
|
||||
Future<void> parent(
|
||||
_isd.DatabaseSession session,
|
||||
Studyset studyset, {
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
if (studyset.id == null) {
|
||||
throw ArgumentError.notNull('studyset.id');
|
||||
}
|
||||
|
||||
var $studyset = studyset.copyWith(parentId: null);
|
||||
await session.db.updateRow<Studyset>(
|
||||
$studyset,
|
||||
columns: [Studyset.t.parentId],
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,17 +11,25 @@
|
|||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:serverpod_database/serverpod_database.dart' as _isd;
|
||||
import 'asset/asset.dart' as _ipb7vkxr;
|
||||
import 'card/card.dart' as _ipiw1cmx;
|
||||
import 'card/card_revision.dart' as _ipbyuef1;
|
||||
import 'review/review_log.dart' as _irzjbdqz;
|
||||
import 'settings/person_settings.dart' as _irnhx5e5;
|
||||
import 'studyset/studyset.dart' as _ikbclmae;
|
||||
import 'tag/card_tag.dart' as _i6jz6nyq;
|
||||
import 'tag/tag.dart' as _ie8t3ar4;
|
||||
|
||||
/// 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 = [
|
||||
_ipb7vkxr.Asset.t,
|
||||
_ipiw1cmx.Card.t,
|
||||
_ipbyuef1.CardRevision.t,
|
||||
_i6jz6nyq.CardTag.t,
|
||||
_irnhx5e5.PersonSettings.t,
|
||||
_irzjbdqz.ReviewLog.t,
|
||||
_ikbclmae.Studyset.t,
|
||||
_ie8t3ar4.Tag.t,
|
||||
];
|
||||
|
|
|
|||
795
halkyon_learn_client/lib/src/protocol/tag/card_tag.dart
Normal file
795
halkyon_learn_client/lib/src/protocol/tag/card_tag.dart
Normal file
|
|
@ -0,0 +1,795 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
// ignore_for_file: dead_code, unnecessary_null_comparison
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:halkyon_learn_client/src/protocol/protocol.dart' as _itx5j3f5;
|
||||
import 'package:serverpod_client/serverpod_client.dart' as _isc;
|
||||
import 'package:serverpod_database/serverpod_database.dart' as _isd;
|
||||
import '../card/card.dart' as _i4926i81;
|
||||
import '../tag/tag.dart' as _isqed5gg;
|
||||
|
||||
/// A card having a tag (ADR 0004): one row per assignment, never updated. Removing
|
||||
/// the tag deletes the row, and adding it on another device at the same time
|
||||
/// makes a new row, so concurrent edits merge without losing either; a card
|
||||
/// with the same tag twice counts once. Synced; goes with its card and its tag.
|
||||
abstract class CardTag
|
||||
implements _isd.TableRow<_isc.UuidValue?>, _isc.ProtocolSerialization {
|
||||
CardTag._({
|
||||
this.id,
|
||||
this.spaceId,
|
||||
required this.cardId,
|
||||
this.card,
|
||||
required this.tagId,
|
||||
this.tag,
|
||||
});
|
||||
|
||||
factory CardTag({
|
||||
_isc.UuidValue? id,
|
||||
int? spaceId,
|
||||
required _isc.UuidValue cardId,
|
||||
_i4926i81.Card? card,
|
||||
required _isc.UuidValue tagId,
|
||||
_isqed5gg.Tag? tag,
|
||||
}) = _CardTagImpl;
|
||||
|
||||
factory CardTag.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return CardTag(
|
||||
id: jsonSerialization['id'] == null
|
||||
? null
|
||||
: _isc.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
|
||||
spaceId: jsonSerialization['spaceId'] as int?,
|
||||
cardId: _isc.UuidValueJsonExtension.fromJson(jsonSerialization['cardId']),
|
||||
card: jsonSerialization['card'] == null
|
||||
? null
|
||||
: _itx5j3f5.Protocol().deserialize<_i4926i81.Card>(
|
||||
jsonSerialization['card'],
|
||||
),
|
||||
tagId: _isc.UuidValueJsonExtension.fromJson(jsonSerialization['tagId']),
|
||||
tag: jsonSerialization['tag'] == null
|
||||
? null
|
||||
: _itx5j3f5.Protocol().deserialize<_isqed5gg.Tag>(
|
||||
jsonSerialization['tag'],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static final t = CardTagTable();
|
||||
|
||||
static const db = CardTagRepository._();
|
||||
|
||||
@override
|
||||
_isc.UuidValue? id;
|
||||
|
||||
/// The space owning this row. Maintained by the sync engine.
|
||||
int? spaceId;
|
||||
|
||||
_isc.UuidValue cardId;
|
||||
|
||||
/// The card tagged.
|
||||
_i4926i81.Card? card;
|
||||
|
||||
_isc.UuidValue tagId;
|
||||
|
||||
/// The tag it has.
|
||||
_isqed5gg.Tag? tag;
|
||||
|
||||
@override
|
||||
_isd.Table<_isc.UuidValue?> get table => t;
|
||||
|
||||
/// Returns a shallow copy of this [CardTag]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
CardTag copyWith({
|
||||
_isc.UuidValue? id,
|
||||
int? spaceId,
|
||||
_isc.UuidValue? cardId,
|
||||
_i4926i81.Card? card,
|
||||
_isc.UuidValue? tagId,
|
||||
_isqed5gg.Tag? tag,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'CardTag',
|
||||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
'cardId': cardId.toJson(),
|
||||
if (card != null) 'card': card?.toJson(),
|
||||
'tagId': tagId.toJson(),
|
||||
if (tag != null) 'tag': tag?.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'CardTag',
|
||||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
'cardId': cardId.toJson(),
|
||||
if (card != null) 'card': card?.toJsonForProtocol(),
|
||||
'tagId': tagId.toJson(),
|
||||
if (tag != null) 'tag': tag?.toJsonForProtocol(),
|
||||
};
|
||||
}
|
||||
|
||||
static CardTagInclude include({
|
||||
_i4926i81.CardInclude? card,
|
||||
_isqed5gg.TagInclude? tag,
|
||||
}) {
|
||||
return CardTagInclude._(
|
||||
card: card,
|
||||
tag: tag,
|
||||
);
|
||||
}
|
||||
|
||||
static CardTagIncludeList includeList({
|
||||
_isd.WhereExpressionBuilder<CardTagTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_isd.OrderByBuilder<CardTagTable>? orderBy,
|
||||
_isd.OrderByListBuilder<CardTagTable>? orderByList,
|
||||
CardTagInclude? include,
|
||||
}) {
|
||||
return CardTagIncludeList._(
|
||||
where: where,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(CardTag.t),
|
||||
orderByList: orderByList?.call(CardTag.t),
|
||||
include: include,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _isc.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _Undefined {}
|
||||
|
||||
class _CardTagImpl extends CardTag {
|
||||
_CardTagImpl({
|
||||
_isc.UuidValue? id,
|
||||
int? spaceId,
|
||||
required _isc.UuidValue cardId,
|
||||
_i4926i81.Card? card,
|
||||
required _isc.UuidValue tagId,
|
||||
_isqed5gg.Tag? tag,
|
||||
}) : super._(
|
||||
id: id,
|
||||
spaceId: spaceId,
|
||||
cardId: cardId,
|
||||
card: card,
|
||||
tagId: tagId,
|
||||
tag: tag,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [CardTag]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
@override
|
||||
CardTag copyWith({
|
||||
Object? id = _Undefined,
|
||||
Object? spaceId = _Undefined,
|
||||
_isc.UuidValue? cardId,
|
||||
Object? card = _Undefined,
|
||||
_isc.UuidValue? tagId,
|
||||
Object? tag = _Undefined,
|
||||
}) {
|
||||
return CardTag(
|
||||
id: id is _isc.UuidValue? ? id : this.id,
|
||||
spaceId: spaceId is int? ? spaceId : this.spaceId,
|
||||
cardId: cardId ?? this.cardId,
|
||||
card: card is _i4926i81.Card? ? card : this.card?.copyWith(),
|
||||
tagId: tagId ?? this.tagId,
|
||||
tag: tag is _isqed5gg.Tag? ? tag : this.tag?.copyWith(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CardTagUpdateTable extends _isd.UpdateTable<CardTagTable> {
|
||||
CardTagUpdateTable(super.table);
|
||||
|
||||
_isd.ColumnValue<int, int> spaceId(int? value) => _isd.ColumnValue(
|
||||
table.spaceId,
|
||||
value,
|
||||
);
|
||||
|
||||
_isd.ColumnValue<_isc.UuidValue, _isc.UuidValue> cardId(
|
||||
_isc.UuidValue value,
|
||||
) => _isd.ColumnValue(
|
||||
table.cardId,
|
||||
value,
|
||||
);
|
||||
|
||||
_isd.ColumnValue<_isc.UuidValue, _isc.UuidValue> tagId(
|
||||
_isc.UuidValue value,
|
||||
) => _isd.ColumnValue(
|
||||
table.tagId,
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
class CardTagTable extends _isd.Table<_isc.UuidValue?> {
|
||||
CardTagTable({super.tableRelation}) : super(tableName: 'card_tag') {
|
||||
updateTable = CardTagUpdateTable(this);
|
||||
spaceId = _isd.ColumnInt(
|
||||
'spaceId',
|
||||
this,
|
||||
);
|
||||
cardId = _isd.ColumnUuid(
|
||||
'cardId',
|
||||
this,
|
||||
);
|
||||
tagId = _isd.ColumnUuid(
|
||||
'tagId',
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
late final CardTagUpdateTable updateTable;
|
||||
|
||||
/// The space owning this row. Maintained by the sync engine.
|
||||
late final _isd.ColumnInt spaceId;
|
||||
|
||||
late final _isd.ColumnUuid cardId;
|
||||
|
||||
/// The card tagged.
|
||||
_i4926i81.CardTable? _card;
|
||||
|
||||
late final _isd.ColumnUuid tagId;
|
||||
|
||||
/// The tag it has.
|
||||
_isqed5gg.TagTable? _tag;
|
||||
|
||||
_i4926i81.CardTable get card {
|
||||
if (_card != null) return _card!;
|
||||
_card = _isd.createRelationTable(
|
||||
relationFieldName: 'card',
|
||||
field: CardTag.t.cardId,
|
||||
foreignField: _i4926i81.Card.t.id,
|
||||
tableRelation: tableRelation,
|
||||
createTable: (foreignTableRelation) =>
|
||||
_i4926i81.CardTable(tableRelation: foreignTableRelation),
|
||||
);
|
||||
return _card!;
|
||||
}
|
||||
|
||||
_isqed5gg.TagTable get tag {
|
||||
if (_tag != null) return _tag!;
|
||||
_tag = _isd.createRelationTable(
|
||||
relationFieldName: 'tag',
|
||||
field: CardTag.t.tagId,
|
||||
foreignField: _isqed5gg.Tag.t.id,
|
||||
tableRelation: tableRelation,
|
||||
createTable: (foreignTableRelation) =>
|
||||
_isqed5gg.TagTable(tableRelation: foreignTableRelation),
|
||||
);
|
||||
return _tag!;
|
||||
}
|
||||
|
||||
@override
|
||||
List<_isd.Column> get columns => [
|
||||
id,
|
||||
spaceId,
|
||||
cardId,
|
||||
tagId,
|
||||
];
|
||||
|
||||
@override
|
||||
_isd.Table? getRelationTable(String relationField) {
|
||||
if (relationField == 'card') {
|
||||
return card;
|
||||
}
|
||||
if (relationField == 'tag') {
|
||||
return tag;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class CardTagInclude extends _isd.IncludeObject {
|
||||
CardTagInclude._({
|
||||
_i4926i81.CardInclude? card,
|
||||
_isqed5gg.TagInclude? tag,
|
||||
}) {
|
||||
_card = card;
|
||||
_tag = tag;
|
||||
}
|
||||
|
||||
_i4926i81.CardInclude? _card;
|
||||
|
||||
_isqed5gg.TagInclude? _tag;
|
||||
|
||||
@override
|
||||
Map<String, _isd.Include?> get includes => {
|
||||
'card': _card,
|
||||
'tag': _tag,
|
||||
};
|
||||
|
||||
@override
|
||||
_isd.Table<_isc.UuidValue?> get table => CardTag.t;
|
||||
}
|
||||
|
||||
class CardTagIncludeList extends _isd.IncludeList {
|
||||
CardTagIncludeList._({
|
||||
_isd.WhereExpressionBuilder<CardTagTable>? where,
|
||||
super.limit,
|
||||
super.offset,
|
||||
super.orderBy,
|
||||
super.orderByList,
|
||||
super.include,
|
||||
}) {
|
||||
super.where = where?.call(CardTag.t);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, _isd.Include?> get includes => include?.includes ?? {};
|
||||
|
||||
@override
|
||||
_isd.Table<_isc.UuidValue?> get table => CardTag.t;
|
||||
}
|
||||
|
||||
class CardTagRepository {
|
||||
const CardTagRepository._();
|
||||
|
||||
final attachRow = const CardTagAttachRowRepository._();
|
||||
|
||||
/// Returns a list of [CardTag]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<CardTag>> find(
|
||||
_isd.DatabaseSession session, {
|
||||
_isd.WhereExpressionBuilder<CardTagTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_isd.OrderByBuilder<CardTagTable>? orderBy,
|
||||
_isd.OrderByListBuilder<CardTagTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
CardTagInclude? include,
|
||||
_isd.LockMode? lockMode,
|
||||
_isd.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.find<CardTag>(
|
||||
where: where?.call(CardTag.t),
|
||||
orderBy: orderBy?.call(CardTag.t),
|
||||
orderByList: orderByList?.call(CardTag.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
include: include,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns the first matching [CardTag] 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<CardTag?> findFirstRow(
|
||||
_isd.DatabaseSession session, {
|
||||
_isd.WhereExpressionBuilder<CardTagTable>? where,
|
||||
int? offset,
|
||||
_isd.OrderByBuilder<CardTagTable>? orderBy,
|
||||
_isd.OrderByListBuilder<CardTagTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
CardTagInclude? include,
|
||||
_isd.LockMode? lockMode,
|
||||
_isd.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findFirstRow<CardTag>(
|
||||
where: where?.call(CardTag.t),
|
||||
orderBy: orderBy?.call(CardTag.t),
|
||||
orderByList: orderByList?.call(CardTag.t),
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
include: include,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Finds a single [CardTag] by its [id] or null if no such row exists.
|
||||
Future<CardTag?> findById(
|
||||
_isd.DatabaseSession session,
|
||||
_isc.UuidValue id, {
|
||||
_isd.Transaction? transaction,
|
||||
CardTagInclude? include,
|
||||
_isd.LockMode? lockMode,
|
||||
_isd.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findById<CardTag>(
|
||||
id,
|
||||
transaction: transaction,
|
||||
include: include,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts all [CardTag]s in the list and returns the inserted rows.
|
||||
///
|
||||
/// The returned [CardTag]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<CardTag>> insert(
|
||||
_isd.DatabaseSession session,
|
||||
List<CardTag> rows, {
|
||||
_isd.Transaction? transaction,
|
||||
bool ignoreConflicts = false,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.insert<CardTag>(
|
||||
rows,
|
||||
transaction: transaction,
|
||||
ignoreConflicts: ignoreConflicts,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts a single [CardTag] and returns the inserted row.
|
||||
///
|
||||
/// The returned [CardTag] will have its `id` field set.
|
||||
Future<CardTag> insertRow(
|
||||
_isd.DatabaseSession session,
|
||||
CardTag row, {
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.insertRow<CardTag>(
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts all [CardTag]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 [CardTag]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<CardTag>> upsert(
|
||||
_isd.DatabaseSession session,
|
||||
List<CardTag> rows, {
|
||||
required _isd.ColumnSelections<CardTagTable> conflictColumns,
|
||||
_isd.ColumnSelections<CardTagTable>? updateColumns,
|
||||
_isd.WhereExpressionBuilder<CardTagTable>? updateWhere,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.upsert<CardTag>(
|
||||
rows,
|
||||
conflictColumns: conflictColumns(CardTag.t),
|
||||
updateColumns: updateColumns?.call(CardTag.t),
|
||||
updateWhere: updateWhere?.call(CardTag.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts a single [CardTag] 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 [CardTag] will have its `id` field set.
|
||||
Future<CardTag?> upsertRow(
|
||||
_isd.DatabaseSession session,
|
||||
CardTag row, {
|
||||
required _isd.ColumnSelections<CardTagTable> conflictColumns,
|
||||
_isd.ColumnSelections<CardTagTable>? updateColumns,
|
||||
_isd.WhereExpressionBuilder<CardTagTable>? updateWhere,
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.upsertRow<CardTag>(
|
||||
row,
|
||||
conflictColumns: conflictColumns(CardTag.t),
|
||||
updateColumns: updateColumns?.call(CardTag.t),
|
||||
updateWhere: updateWhere?.call(CardTag.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [CardTag]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<CardTag>> update(
|
||||
_isd.DatabaseSession session,
|
||||
List<CardTag> rows, {
|
||||
_isd.ColumnSelections<CardTagTable>? columns,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.update<CardTag>(
|
||||
rows,
|
||||
columns: columns?.call(CardTag.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [CardTag]. 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<CardTag> updateRow(
|
||||
_isd.DatabaseSession session,
|
||||
CardTag row, {
|
||||
_isd.ColumnSelections<CardTagTable>? columns,
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateRow<CardTag>(
|
||||
row,
|
||||
columns: columns?.call(CardTag.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [CardTag] by its [id] with the specified [columnValues].
|
||||
/// Returns the updated row or null if no row with the given id exists.
|
||||
Future<CardTag?> updateById(
|
||||
_isd.DatabaseSession session,
|
||||
_isc.UuidValue id, {
|
||||
required _isd.ColumnValueListBuilder<CardTagUpdateTable> columnValues,
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateById<CardTag>(
|
||||
id,
|
||||
columnValues: columnValues(CardTag.t.updateTable),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [CardTag]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<CardTag>> updateWhere(
|
||||
_isd.DatabaseSession session, {
|
||||
required _isd.ColumnValueListBuilder<CardTagUpdateTable> columnValues,
|
||||
required _isd.WhereExpressionBuilder<CardTagTable> where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_isd.OrderByBuilder<CardTagTable>? orderBy,
|
||||
_isd.OrderByListBuilder<CardTagTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.updateWhere<CardTag>(
|
||||
columnValues: columnValues(CardTag.t.updateTable),
|
||||
where: where(CardTag.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(CardTag.t),
|
||||
orderByList: orderByList?.call(CardTag.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes all [CardTag]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<CardTag>> delete(
|
||||
_isd.DatabaseSession session,
|
||||
List<CardTag> rows, {
|
||||
_isd.OrderByBuilder<CardTagTable>? orderBy,
|
||||
_isd.OrderByListBuilder<CardTagTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.delete<CardTag>(
|
||||
rows,
|
||||
orderBy: orderBy?.call(CardTag.t),
|
||||
orderByList: orderByList?.call(CardTag.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes a single [CardTag].
|
||||
Future<CardTag> deleteRow(
|
||||
_isd.DatabaseSession session,
|
||||
CardTag row, {
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.deleteRow<CardTag>(
|
||||
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<CardTag>> deleteWhere(
|
||||
_isd.DatabaseSession session, {
|
||||
required _isd.WhereExpressionBuilder<CardTagTable> where,
|
||||
_isd.OrderByBuilder<CardTagTable>? orderBy,
|
||||
_isd.OrderByListBuilder<CardTagTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.deleteWhere<CardTag>(
|
||||
where: where(CardTag.t),
|
||||
orderBy: orderBy?.call(CardTag.t),
|
||||
orderByList: orderByList?.call(CardTag.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<CardTagTable>? where,
|
||||
int? limit,
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.count<CardTag>(
|
||||
where: where?.call(CardTag.t),
|
||||
limit: limit,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Acquires row-level locks on [CardTag] rows matching the [where] expression.
|
||||
Future<void> lockRows(
|
||||
_isd.DatabaseSession session, {
|
||||
required _isd.WhereExpressionBuilder<CardTagTable> where,
|
||||
required _isd.LockMode lockMode,
|
||||
required _isd.Transaction transaction,
|
||||
_isd.LockBehavior lockBehavior = _isd.LockBehavior.wait,
|
||||
}) async {
|
||||
return session.db.lockRows<CardTag>(
|
||||
where: where(CardTag.t),
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CardTagAttachRowRepository {
|
||||
const CardTagAttachRowRepository._();
|
||||
|
||||
/// Creates a relation between the given [CardTag] and [Card]
|
||||
/// by setting the [CardTag]'s foreign key `cardId` to refer to the [Card].
|
||||
Future<void> card(
|
||||
_isd.DatabaseSession session,
|
||||
CardTag cardTag,
|
||||
_i4926i81.Card card, {
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
if (cardTag.id == null) {
|
||||
throw ArgumentError.notNull('cardTag.id');
|
||||
}
|
||||
if (card.id == null) {
|
||||
throw ArgumentError.notNull('card.id');
|
||||
}
|
||||
|
||||
var $cardTag = cardTag.copyWith(cardId: card.id);
|
||||
await session.db.updateRow<CardTag>(
|
||||
$cardTag,
|
||||
columns: [CardTag.t.cardId],
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Creates a relation between the given [CardTag] and [Tag]
|
||||
/// by setting the [CardTag]'s foreign key `tagId` to refer to the [Tag].
|
||||
Future<void> tag(
|
||||
_isd.DatabaseSession session,
|
||||
CardTag cardTag,
|
||||
_isqed5gg.Tag tag, {
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
if (cardTag.id == null) {
|
||||
throw ArgumentError.notNull('cardTag.id');
|
||||
}
|
||||
if (tag.id == null) {
|
||||
throw ArgumentError.notNull('tag.id');
|
||||
}
|
||||
|
||||
var $cardTag = cardTag.copyWith(tagId: tag.id);
|
||||
await session.db.updateRow<CardTag>(
|
||||
$cardTag,
|
||||
columns: [CardTag.t.tagId],
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
669
halkyon_learn_client/lib/src/protocol/tag/tag.dart
Normal file
669
halkyon_learn_client/lib/src/protocol/tag/tag.dart
Normal file
|
|
@ -0,0 +1,669 @@
|
|||
/* 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 tag a person gives cards (ADR 0004): a name to study and filter by. Synced; its
|
||||
/// space owns it. The name is content, so it lives in the payload, like a
|
||||
/// studyset's title.
|
||||
abstract class Tag
|
||||
implements _isd.TableRow<_isc.UuidValue?>, _isc.ProtocolSerialization {
|
||||
Tag._({
|
||||
this.id,
|
||||
this.spaceId,
|
||||
this.keyId,
|
||||
required this.payload,
|
||||
this.nonce,
|
||||
});
|
||||
|
||||
factory Tag({
|
||||
_isc.UuidValue? id,
|
||||
int? spaceId,
|
||||
String? keyId,
|
||||
required _idt.ByteData payload,
|
||||
_idt.ByteData? nonce,
|
||||
}) = _TagImpl;
|
||||
|
||||
factory Tag.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return Tag(
|
||||
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 = TagTable();
|
||||
|
||||
static const db = TagRepository._();
|
||||
|
||||
@override
|
||||
_isc.UuidValue? id;
|
||||
|
||||
/// The space owning this row. Maintained by the sync engine.
|
||||
int? spaceId;
|
||||
|
||||
/// The encryption key's id; always null (ADR 0002), as on studysets.
|
||||
String? keyId;
|
||||
|
||||
/// The content as UTF-8 JSON: {"schemaVersion", "name"}.
|
||||
_idt.ByteData payload;
|
||||
|
||||
/// The encryption nonce; always null, like keyId.
|
||||
_idt.ByteData? nonce;
|
||||
|
||||
@override
|
||||
_isd.Table<_isc.UuidValue?> get table => t;
|
||||
|
||||
/// Returns a shallow copy of this [Tag]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
Tag copyWith({
|
||||
_isc.UuidValue? id,
|
||||
int? spaceId,
|
||||
String? keyId,
|
||||
_idt.ByteData? payload,
|
||||
_idt.ByteData? nonce,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'Tag',
|
||||
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__': 'Tag',
|
||||
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 TagInclude include() {
|
||||
return TagInclude._();
|
||||
}
|
||||
|
||||
static TagIncludeList includeList({
|
||||
_isd.WhereExpressionBuilder<TagTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_isd.OrderByBuilder<TagTable>? orderBy,
|
||||
_isd.OrderByListBuilder<TagTable>? orderByList,
|
||||
TagInclude? include,
|
||||
}) {
|
||||
return TagIncludeList._(
|
||||
where: where,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(Tag.t),
|
||||
orderByList: orderByList?.call(Tag.t),
|
||||
include: include,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _isc.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _Undefined {}
|
||||
|
||||
class _TagImpl extends Tag {
|
||||
_TagImpl({
|
||||
_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 [Tag]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
@override
|
||||
Tag copyWith({
|
||||
Object? id = _Undefined,
|
||||
Object? spaceId = _Undefined,
|
||||
Object? keyId = _Undefined,
|
||||
_idt.ByteData? payload,
|
||||
Object? nonce = _Undefined,
|
||||
}) {
|
||||
return Tag(
|
||||
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 TagUpdateTable extends _isd.UpdateTable<TagTable> {
|
||||
TagUpdateTable(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 TagTable extends _isd.Table<_isc.UuidValue?> {
|
||||
TagTable({super.tableRelation}) : super(tableName: 'tag') {
|
||||
updateTable = TagUpdateTable(this);
|
||||
spaceId = _isd.ColumnInt(
|
||||
'spaceId',
|
||||
this,
|
||||
);
|
||||
keyId = _isd.ColumnString(
|
||||
'keyId',
|
||||
this,
|
||||
);
|
||||
payload = _isd.ColumnByteData(
|
||||
'payload',
|
||||
this,
|
||||
);
|
||||
nonce = _isd.ColumnByteData(
|
||||
'nonce',
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
late final TagUpdateTable updateTable;
|
||||
|
||||
/// The space owning this row. Maintained by the sync engine.
|
||||
late final _isd.ColumnInt spaceId;
|
||||
|
||||
/// The encryption key's id; always null (ADR 0002), as on studysets.
|
||||
late final _isd.ColumnString keyId;
|
||||
|
||||
/// The content as UTF-8 JSON: {"schemaVersion", "name"}.
|
||||
late final _isd.ColumnByteData payload;
|
||||
|
||||
/// The encryption nonce; always null, like keyId.
|
||||
late final _isd.ColumnByteData nonce;
|
||||
|
||||
@override
|
||||
List<_isd.Column> get columns => [
|
||||
id,
|
||||
spaceId,
|
||||
keyId,
|
||||
payload,
|
||||
nonce,
|
||||
];
|
||||
}
|
||||
|
||||
class TagInclude extends _isd.IncludeObject {
|
||||
TagInclude._();
|
||||
|
||||
@override
|
||||
Map<String, _isd.Include?> get includes => {};
|
||||
|
||||
@override
|
||||
_isd.Table<_isc.UuidValue?> get table => Tag.t;
|
||||
}
|
||||
|
||||
class TagIncludeList extends _isd.IncludeList {
|
||||
TagIncludeList._({
|
||||
_isd.WhereExpressionBuilder<TagTable>? where,
|
||||
super.limit,
|
||||
super.offset,
|
||||
super.orderBy,
|
||||
super.orderByList,
|
||||
super.include,
|
||||
}) {
|
||||
super.where = where?.call(Tag.t);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, _isd.Include?> get includes => include?.includes ?? {};
|
||||
|
||||
@override
|
||||
_isd.Table<_isc.UuidValue?> get table => Tag.t;
|
||||
}
|
||||
|
||||
class TagRepository {
|
||||
const TagRepository._();
|
||||
|
||||
/// Returns a list of [Tag]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<Tag>> find(
|
||||
_isd.DatabaseSession session, {
|
||||
_isd.WhereExpressionBuilder<TagTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_isd.OrderByBuilder<TagTable>? orderBy,
|
||||
_isd.OrderByListBuilder<TagTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
_isd.LockMode? lockMode,
|
||||
_isd.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.find<Tag>(
|
||||
where: where?.call(Tag.t),
|
||||
orderBy: orderBy?.call(Tag.t),
|
||||
orderByList: orderByList?.call(Tag.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns the first matching [Tag] 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<Tag?> findFirstRow(
|
||||
_isd.DatabaseSession session, {
|
||||
_isd.WhereExpressionBuilder<TagTable>? where,
|
||||
int? offset,
|
||||
_isd.OrderByBuilder<TagTable>? orderBy,
|
||||
_isd.OrderByListBuilder<TagTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
_isd.LockMode? lockMode,
|
||||
_isd.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findFirstRow<Tag>(
|
||||
where: where?.call(Tag.t),
|
||||
orderBy: orderBy?.call(Tag.t),
|
||||
orderByList: orderByList?.call(Tag.t),
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Finds a single [Tag] by its [id] or null if no such row exists.
|
||||
Future<Tag?> findById(
|
||||
_isd.DatabaseSession session,
|
||||
_isc.UuidValue id, {
|
||||
_isd.Transaction? transaction,
|
||||
_isd.LockMode? lockMode,
|
||||
_isd.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findById<Tag>(
|
||||
id,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts all [Tag]s in the list and returns the inserted rows.
|
||||
///
|
||||
/// The returned [Tag]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<Tag>> insert(
|
||||
_isd.DatabaseSession session,
|
||||
List<Tag> rows, {
|
||||
_isd.Transaction? transaction,
|
||||
bool ignoreConflicts = false,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.insert<Tag>(
|
||||
rows,
|
||||
transaction: transaction,
|
||||
ignoreConflicts: ignoreConflicts,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts a single [Tag] and returns the inserted row.
|
||||
///
|
||||
/// The returned [Tag] will have its `id` field set.
|
||||
Future<Tag> insertRow(
|
||||
_isd.DatabaseSession session,
|
||||
Tag row, {
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.insertRow<Tag>(
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts all [Tag]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 [Tag]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<Tag>> upsert(
|
||||
_isd.DatabaseSession session,
|
||||
List<Tag> rows, {
|
||||
required _isd.ColumnSelections<TagTable> conflictColumns,
|
||||
_isd.ColumnSelections<TagTable>? updateColumns,
|
||||
_isd.WhereExpressionBuilder<TagTable>? updateWhere,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.upsert<Tag>(
|
||||
rows,
|
||||
conflictColumns: conflictColumns(Tag.t),
|
||||
updateColumns: updateColumns?.call(Tag.t),
|
||||
updateWhere: updateWhere?.call(Tag.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts a single [Tag] 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 [Tag] will have its `id` field set.
|
||||
Future<Tag?> upsertRow(
|
||||
_isd.DatabaseSession session,
|
||||
Tag row, {
|
||||
required _isd.ColumnSelections<TagTable> conflictColumns,
|
||||
_isd.ColumnSelections<TagTable>? updateColumns,
|
||||
_isd.WhereExpressionBuilder<TagTable>? updateWhere,
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.upsertRow<Tag>(
|
||||
row,
|
||||
conflictColumns: conflictColumns(Tag.t),
|
||||
updateColumns: updateColumns?.call(Tag.t),
|
||||
updateWhere: updateWhere?.call(Tag.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [Tag]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<Tag>> update(
|
||||
_isd.DatabaseSession session,
|
||||
List<Tag> rows, {
|
||||
_isd.ColumnSelections<TagTable>? columns,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.update<Tag>(
|
||||
rows,
|
||||
columns: columns?.call(Tag.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [Tag]. 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<Tag> updateRow(
|
||||
_isd.DatabaseSession session,
|
||||
Tag row, {
|
||||
_isd.ColumnSelections<TagTable>? columns,
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateRow<Tag>(
|
||||
row,
|
||||
columns: columns?.call(Tag.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [Tag] by its [id] with the specified [columnValues].
|
||||
/// Returns the updated row or null if no row with the given id exists.
|
||||
Future<Tag?> updateById(
|
||||
_isd.DatabaseSession session,
|
||||
_isc.UuidValue id, {
|
||||
required _isd.ColumnValueListBuilder<TagUpdateTable> columnValues,
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateById<Tag>(
|
||||
id,
|
||||
columnValues: columnValues(Tag.t.updateTable),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [Tag]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<Tag>> updateWhere(
|
||||
_isd.DatabaseSession session, {
|
||||
required _isd.ColumnValueListBuilder<TagUpdateTable> columnValues,
|
||||
required _isd.WhereExpressionBuilder<TagTable> where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_isd.OrderByBuilder<TagTable>? orderBy,
|
||||
_isd.OrderByListBuilder<TagTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.updateWhere<Tag>(
|
||||
columnValues: columnValues(Tag.t.updateTable),
|
||||
where: where(Tag.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(Tag.t),
|
||||
orderByList: orderByList?.call(Tag.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes all [Tag]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<Tag>> delete(
|
||||
_isd.DatabaseSession session,
|
||||
List<Tag> rows, {
|
||||
_isd.OrderByBuilder<TagTable>? orderBy,
|
||||
_isd.OrderByListBuilder<TagTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.delete<Tag>(
|
||||
rows,
|
||||
orderBy: orderBy?.call(Tag.t),
|
||||
orderByList: orderByList?.call(Tag.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes a single [Tag].
|
||||
Future<Tag> deleteRow(
|
||||
_isd.DatabaseSession session,
|
||||
Tag row, {
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.deleteRow<Tag>(
|
||||
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<Tag>> deleteWhere(
|
||||
_isd.DatabaseSession session, {
|
||||
required _isd.WhereExpressionBuilder<TagTable> where,
|
||||
_isd.OrderByBuilder<TagTable>? orderBy,
|
||||
_isd.OrderByListBuilder<TagTable>? orderByList,
|
||||
_isd.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.deleteWhere<Tag>(
|
||||
where: where(Tag.t),
|
||||
orderBy: orderBy?.call(Tag.t),
|
||||
orderByList: orderByList?.call(Tag.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<TagTable>? where,
|
||||
int? limit,
|
||||
_isd.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.count<Tag>(
|
||||
where: where?.call(Tag.t),
|
||||
limit: limit,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Acquires row-level locks on [Tag] rows matching the [where] expression.
|
||||
Future<void> lockRows(
|
||||
_isd.DatabaseSession session, {
|
||||
required _isd.WhereExpressionBuilder<TagTable> where,
|
||||
required _isd.LockMode lockMode,
|
||||
required _isd.Transaction transaction,
|
||||
_isd.LockBehavior lockBehavior = _isd.LockBehavior.wait,
|
||||
}) async {
|
||||
return session.db.lockRows<Tag>(
|
||||
where: where(Tag.t),
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -69,6 +69,8 @@ dev_dependencies:
|
|||
# The sync tests (test/sync/) sign in like the app and read the server's database.
|
||||
postgres: ^3.5.16
|
||||
serverpod_auth_core_client: 4.0.3
|
||||
# The device-migration test builds local databases like the sync engine does.
|
||||
sqlite3: ^3.6.0
|
||||
yaml: ^3.1.4
|
||||
|
||||
dependency_overrides:
|
||||
|
|
|
|||
127
halkyon_learn_flutter/test/core/sync/device_migration_test.dart
Normal file
127
halkyon_learn_flutter/test/core/sync/device_migration_test.dart
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:halkyon_learn_client/migrations/migration_registry.dart';
|
||||
import 'package:sqlite3/sqlite3.dart';
|
||||
|
||||
/// Every device keeps its content through every upgrade of its local database:
|
||||
/// each client migration, applied to a database at the version before it that
|
||||
/// holds a row in every synced table, loses none of them and breaks no foreign
|
||||
/// key.
|
||||
///
|
||||
/// SQLite can't add a foreign key to a table, so a migration may rebuild one
|
||||
/// (create a copy, move the rows, drop the original). Dropping it with foreign
|
||||
/// keys enforced would delete its children through their cascades, or fail.
|
||||
/// Serverpod's SQLite migration runner switches foreign keys off around
|
||||
/// migrations for that reason (`sqlite_migration_runner.dart`), and so does
|
||||
/// this test, which applies the SQL the same way.
|
||||
void main() {
|
||||
final migrations = MigrationRegistry.migrations;
|
||||
for (var i = 1; i < migrations.length; i++) {
|
||||
final from = migrations[i - 1];
|
||||
final to = migrations[i];
|
||||
|
||||
test('upgrading a device from ${from.version} to ${to.version} keeps every '
|
||||
'row', () {
|
||||
final db = sqlite3.openInMemory();
|
||||
addTearDown(db.close);
|
||||
db
|
||||
..execute('PRAGMA foreign_keys = ON')
|
||||
..execute(from.definitionSql);
|
||||
final rows = _fillSyncedTables(db);
|
||||
|
||||
db
|
||||
..execute('PRAGMA foreign_keys = OFF')
|
||||
..execute(to.migrationSql)
|
||||
..execute('PRAGMA foreign_keys = ON');
|
||||
|
||||
expect({for (final table in rows.keys) table: _count(db, table)}, rows);
|
||||
expect(db.select('PRAGMA foreign_key_check'), isEmpty);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
final _random = Random(1);
|
||||
|
||||
Uint8List _id() =>
|
||||
Uint8List.fromList(List.generate(16, (_) => _random.nextInt(256)));
|
||||
|
||||
/// Puts one row into each of our synced tables the database has, and returns how
|
||||
/// many rows each then holds. A synced table this doesn't know fails the test:
|
||||
/// it needs a row here, so its upgrades are checked too.
|
||||
Map<String, int> _fillSyncedTables(Database db) {
|
||||
final studyset = _id();
|
||||
final card = _id();
|
||||
final tag = _id();
|
||||
final rows = <String, (String, List<Object?>)>{
|
||||
'studyset': (
|
||||
"INSERT INTO studyset (id, spaceId, payload) VALUES (?, 1, x'00')",
|
||||
[studyset],
|
||||
),
|
||||
'card': (
|
||||
'INSERT INTO card (id, spaceId, studysetId, position) '
|
||||
"VALUES (?, 1, ?, 'a0')",
|
||||
[card, studyset],
|
||||
),
|
||||
'card_revision': (
|
||||
'INSERT INTO card_revision (id, spaceId, cardId, parents, authorId, '
|
||||
"payload) VALUES (?, 1, ?, '[]', ?, x'00')",
|
||||
[_id(), card, _id()],
|
||||
),
|
||||
'review_log': (
|
||||
'INSERT INTO review_log (id, spaceId, cardId, rating, reviewedAt, '
|
||||
'durationMs) VALUES (?, 1, ?, 3, 0, 1000)',
|
||||
[_id(), card],
|
||||
),
|
||||
'tag': (
|
||||
"INSERT INTO tag (id, spaceId, payload) VALUES (?, 1, x'00')",
|
||||
[tag],
|
||||
),
|
||||
'card_tag': (
|
||||
'INSERT INTO card_tag (id, spaceId, cardId, tagId) VALUES (?, 1, ?, ?)',
|
||||
[_id(), card, tag],
|
||||
),
|
||||
'person_settings': (
|
||||
"INSERT INTO person_settings (id, spaceId, payload) VALUES (?, 1, x'00')",
|
||||
[_id()],
|
||||
),
|
||||
'asset': (
|
||||
'INSERT INTO asset (id, spaceId, sha256, mimeType, byteSize, payload) '
|
||||
"VALUES (?, 1, 'ab', 'image/png', 1, x'00')",
|
||||
[_id()],
|
||||
),
|
||||
};
|
||||
|
||||
final synced = {
|
||||
for (final table in db.select(
|
||||
"SELECT name FROM sqlite_master WHERE type = 'table'",
|
||||
))
|
||||
// Ours: synced (a spaceId), not the sync engine's own bookkeeping, which
|
||||
// only the engine's migrations change.
|
||||
if (!RegExp('^(crdt|offline_sync)_').hasMatch(table['name'] as String) &&
|
||||
db
|
||||
.select('PRAGMA table_info("${table['name']}")')
|
||||
.any((column) => column['name'] == 'spaceId'))
|
||||
table['name'] as String,
|
||||
};
|
||||
expect(
|
||||
synced.difference(rows.keys.toSet()),
|
||||
isEmpty,
|
||||
reason: 'every synced table needs a row in this test',
|
||||
);
|
||||
|
||||
db.execute(
|
||||
'INSERT INTO offline_sync_spaces (id, uuidSpaceId) VALUES (1, ?)',
|
||||
[_id()],
|
||||
);
|
||||
for (final MapEntry(key: table, value: (sql, parameters)) in rows.entries) {
|
||||
if (synced.contains(table)) {
|
||||
db.execute(sql, parameters);
|
||||
}
|
||||
}
|
||||
return {for (final table in synced) table: _count(db, table)};
|
||||
}
|
||||
|
||||
int _count(Database db, String table) =>
|
||||
db.select('SELECT count(*) AS n FROM "$table"').single['n'] as int;
|
||||
33
halkyon_learn_server/lib/src/asset/asset.spy.yaml
Normal file
33
halkyon_learn_server/lib/src/asset/asset.spy.yaml
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
### A file in a person's content, an image or a sound (ADR 0004). This row is synced;
|
||||
### the file itself is in the server's private cloud storage, reachable only
|
||||
### through an endpoint that checks the owner, and each device caches it by its
|
||||
### hash. Content refers to it by the row's id.
|
||||
###
|
||||
### A file added twice (the same image in two imported cards) is one file in
|
||||
### storage, since its path is its hash; two rows for it may exist when two
|
||||
### devices added it while offline, and count as one.
|
||||
class: Asset
|
||||
table: asset
|
||||
database: sync
|
||||
fields:
|
||||
### The file's SHA-256, in lowercase hex: its name in storage and in every
|
||||
### device's cache, and the check that a download is the file that was added.
|
||||
sha256: String
|
||||
### Its media type, e.g. 'image/png' or 'audio/mpeg'.
|
||||
mimeType: String
|
||||
### Its size in bytes.
|
||||
byteSize: int
|
||||
### The encryption key's id; always null (ADR 0002), as on studysets.
|
||||
keyId: String?
|
||||
### The content as UTF-8 JSON: {"schemaVersion", "fileName"}; the name it came
|
||||
### with is content, like a studyset's title.
|
||||
payload: ByteData
|
||||
### The encryption nonce; always null, like keyId.
|
||||
nonce: ByteData?
|
||||
indexes:
|
||||
# Deleting a space finds its rows through this index (see studyset.spy.yaml).
|
||||
asset_space_idx:
|
||||
fields: spaceId
|
||||
# A space's file with a given hash, to add a file only once.
|
||||
asset_hash_idx:
|
||||
fields: spaceId, sha256
|
||||
753
halkyon_learn_server/lib/src/generated/asset/asset.dart
Normal file
753
halkyon_learn_server/lib/src/generated/asset/asset.dart
Normal file
|
|
@ -0,0 +1,753 @@
|
|||
/* 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 file in a person's content, an image or a sound (ADR 0004). This row is synced;
|
||||
/// the file itself is in the server's private cloud storage, reachable only
|
||||
/// through an endpoint that checks the owner, and each device caches it by its
|
||||
/// hash. Content refers to it by the row's id.
|
||||
///
|
||||
/// A file added twice (the same image in two imported cards) is one file in
|
||||
/// storage, since its path is its hash; two rows for it may exist when two
|
||||
/// devices added it while offline, and count as one.
|
||||
abstract class Asset
|
||||
implements _is.TableRow<_is.UuidValue?>, _is.ProtocolSerialization {
|
||||
Asset._({
|
||||
this.id,
|
||||
this.spaceId,
|
||||
required this.sha256,
|
||||
required this.mimeType,
|
||||
required this.byteSize,
|
||||
this.keyId,
|
||||
required this.payload,
|
||||
this.nonce,
|
||||
});
|
||||
|
||||
factory Asset({
|
||||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
required String sha256,
|
||||
required String mimeType,
|
||||
required int byteSize,
|
||||
String? keyId,
|
||||
required _idt.ByteData payload,
|
||||
_idt.ByteData? nonce,
|
||||
}) = _AssetImpl;
|
||||
|
||||
factory Asset.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return Asset(
|
||||
id: jsonSerialization['id'] == null
|
||||
? null
|
||||
: _is.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
|
||||
spaceId: jsonSerialization['spaceId'] as int?,
|
||||
sha256: jsonSerialization['sha256'] as String,
|
||||
mimeType: jsonSerialization['mimeType'] as String,
|
||||
byteSize: jsonSerialization['byteSize'] 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 = AssetTable();
|
||||
|
||||
static const db = AssetRepository._();
|
||||
|
||||
@override
|
||||
_is.UuidValue? id;
|
||||
|
||||
/// The space owning this row. Maintained by the sync engine.
|
||||
int? spaceId;
|
||||
|
||||
/// The file's SHA-256, in lowercase hex: its name in storage and in every
|
||||
/// device's cache, and the check that a download is the file that was added.
|
||||
String sha256;
|
||||
|
||||
/// Its media type, e.g. 'image/png' or 'audio/mpeg'.
|
||||
String mimeType;
|
||||
|
||||
/// Its size in bytes.
|
||||
int byteSize;
|
||||
|
||||
/// The encryption key's id; always null (ADR 0002), as on studysets.
|
||||
String? keyId;
|
||||
|
||||
/// The content as UTF-8 JSON: {"schemaVersion", "fileName"}; the name it came
|
||||
/// with is content, like a studyset's title.
|
||||
_idt.ByteData payload;
|
||||
|
||||
/// The encryption nonce; always null, like keyId.
|
||||
_idt.ByteData? nonce;
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => t;
|
||||
|
||||
/// Returns a shallow copy of this [Asset]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
Asset copyWith({
|
||||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
String? sha256,
|
||||
String? mimeType,
|
||||
int? byteSize,
|
||||
String? keyId,
|
||||
_idt.ByteData? payload,
|
||||
_idt.ByteData? nonce,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'Asset',
|
||||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
'sha256': sha256,
|
||||
'mimeType': mimeType,
|
||||
'byteSize': byteSize,
|
||||
if (keyId != null) 'keyId': keyId,
|
||||
'payload': payload.toJson(),
|
||||
if (nonce != null) 'nonce': nonce?.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'Asset',
|
||||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
'sha256': sha256,
|
||||
'mimeType': mimeType,
|
||||
'byteSize': byteSize,
|
||||
if (keyId != null) 'keyId': keyId,
|
||||
'payload': payload.toJson(),
|
||||
if (nonce != null) 'nonce': nonce?.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
static AssetInclude include() {
|
||||
return AssetInclude._();
|
||||
}
|
||||
|
||||
static AssetIncludeList includeList({
|
||||
_is.WhereExpressionBuilder<AssetTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<AssetTable>? orderBy,
|
||||
_is.OrderByListBuilder<AssetTable>? orderByList,
|
||||
AssetInclude? include,
|
||||
}) {
|
||||
return AssetIncludeList._(
|
||||
where: where,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(Asset.t),
|
||||
orderByList: orderByList?.call(Asset.t),
|
||||
include: include,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _is.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _Undefined {}
|
||||
|
||||
class _AssetImpl extends Asset {
|
||||
_AssetImpl({
|
||||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
required String sha256,
|
||||
required String mimeType,
|
||||
required int byteSize,
|
||||
String? keyId,
|
||||
required _idt.ByteData payload,
|
||||
_idt.ByteData? nonce,
|
||||
}) : super._(
|
||||
id: id,
|
||||
spaceId: spaceId,
|
||||
sha256: sha256,
|
||||
mimeType: mimeType,
|
||||
byteSize: byteSize,
|
||||
keyId: keyId,
|
||||
payload: payload,
|
||||
nonce: nonce,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [Asset]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
@override
|
||||
Asset copyWith({
|
||||
Object? id = _Undefined,
|
||||
Object? spaceId = _Undefined,
|
||||
String? sha256,
|
||||
String? mimeType,
|
||||
int? byteSize,
|
||||
Object? keyId = _Undefined,
|
||||
_idt.ByteData? payload,
|
||||
Object? nonce = _Undefined,
|
||||
}) {
|
||||
return Asset(
|
||||
id: id is _is.UuidValue? ? id : this.id,
|
||||
spaceId: spaceId is int? ? spaceId : this.spaceId,
|
||||
sha256: sha256 ?? this.sha256,
|
||||
mimeType: mimeType ?? this.mimeType,
|
||||
byteSize: byteSize ?? this.byteSize,
|
||||
keyId: keyId is String? ? keyId : this.keyId,
|
||||
payload: payload ?? this.payload.clone(),
|
||||
nonce: nonce is _idt.ByteData? ? nonce : this.nonce?.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AssetUpdateTable extends _is.UpdateTable<AssetTable> {
|
||||
AssetUpdateTable(super.table);
|
||||
|
||||
_is.ColumnValue<int, int> spaceId(int? value) => _is.ColumnValue(
|
||||
table.spaceId,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> sha256(String value) => _is.ColumnValue(
|
||||
table.sha256,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> mimeType(String value) => _is.ColumnValue(
|
||||
table.mimeType,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<int, int> byteSize(int value) => _is.ColumnValue(
|
||||
table.byteSize,
|
||||
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 AssetTable extends _is.Table<_is.UuidValue?> {
|
||||
AssetTable({super.tableRelation}) : super(tableName: 'asset') {
|
||||
updateTable = AssetUpdateTable(this);
|
||||
spaceId = _is.ColumnInt(
|
||||
'spaceId',
|
||||
this,
|
||||
);
|
||||
sha256 = _is.ColumnString(
|
||||
'sha256',
|
||||
this,
|
||||
);
|
||||
mimeType = _is.ColumnString(
|
||||
'mimeType',
|
||||
this,
|
||||
);
|
||||
byteSize = _is.ColumnInt(
|
||||
'byteSize',
|
||||
this,
|
||||
);
|
||||
keyId = _is.ColumnString(
|
||||
'keyId',
|
||||
this,
|
||||
);
|
||||
payload = _is.ColumnByteData(
|
||||
'payload',
|
||||
this,
|
||||
);
|
||||
nonce = _is.ColumnByteData(
|
||||
'nonce',
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
late final AssetUpdateTable updateTable;
|
||||
|
||||
/// The space owning this row. Maintained by the sync engine.
|
||||
late final _is.ColumnInt spaceId;
|
||||
|
||||
/// The file's SHA-256, in lowercase hex: its name in storage and in every
|
||||
/// device's cache, and the check that a download is the file that was added.
|
||||
late final _is.ColumnString sha256;
|
||||
|
||||
/// Its media type, e.g. 'image/png' or 'audio/mpeg'.
|
||||
late final _is.ColumnString mimeType;
|
||||
|
||||
/// Its size in bytes.
|
||||
late final _is.ColumnInt byteSize;
|
||||
|
||||
/// The encryption key's id; always null (ADR 0002), as on studysets.
|
||||
late final _is.ColumnString keyId;
|
||||
|
||||
/// The content as UTF-8 JSON: {"schemaVersion", "fileName"}; the name it came
|
||||
/// with is content, like a studyset's title.
|
||||
late final _is.ColumnByteData payload;
|
||||
|
||||
/// The encryption nonce; always null, like keyId.
|
||||
late final _is.ColumnByteData nonce;
|
||||
|
||||
@override
|
||||
List<_is.Column> get columns => [
|
||||
id,
|
||||
spaceId,
|
||||
sha256,
|
||||
mimeType,
|
||||
byteSize,
|
||||
keyId,
|
||||
payload,
|
||||
nonce,
|
||||
];
|
||||
}
|
||||
|
||||
class AssetInclude extends _is.IncludeObject {
|
||||
AssetInclude._();
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => {};
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => Asset.t;
|
||||
}
|
||||
|
||||
class AssetIncludeList extends _is.IncludeList {
|
||||
AssetIncludeList._({
|
||||
_is.WhereExpressionBuilder<AssetTable>? where,
|
||||
super.limit,
|
||||
super.offset,
|
||||
super.orderBy,
|
||||
super.orderByList,
|
||||
super.include,
|
||||
}) {
|
||||
super.where = where?.call(Asset.t);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => include?.includes ?? {};
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => Asset.t;
|
||||
}
|
||||
|
||||
class AssetRepository {
|
||||
const AssetRepository._();
|
||||
|
||||
/// Returns a list of [Asset]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<Asset>> find(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<AssetTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<AssetTable>? orderBy,
|
||||
_is.OrderByListBuilder<AssetTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.find<Asset>(
|
||||
where: where?.call(Asset.t),
|
||||
orderBy: orderBy?.call(Asset.t),
|
||||
orderByList: orderByList?.call(Asset.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns the first matching [Asset] 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<Asset?> findFirstRow(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<AssetTable>? where,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<AssetTable>? orderBy,
|
||||
_is.OrderByListBuilder<AssetTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findFirstRow<Asset>(
|
||||
where: where?.call(Asset.t),
|
||||
orderBy: orderBy?.call(Asset.t),
|
||||
orderByList: orderByList?.call(Asset.t),
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Finds a single [Asset] by its [id] or null if no such row exists.
|
||||
Future<Asset?> findById(
|
||||
_is.DatabaseSession session,
|
||||
_is.UuidValue id, {
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findById<Asset>(
|
||||
id,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts all [Asset]s in the list and returns the inserted rows.
|
||||
///
|
||||
/// The returned [Asset]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<Asset>> insert(
|
||||
_is.DatabaseSession session,
|
||||
List<Asset> rows, {
|
||||
_is.Transaction? transaction,
|
||||
bool ignoreConflicts = false,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.insert<Asset>(
|
||||
rows,
|
||||
transaction: transaction,
|
||||
ignoreConflicts: ignoreConflicts,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts a single [Asset] and returns the inserted row.
|
||||
///
|
||||
/// The returned [Asset] will have its `id` field set.
|
||||
Future<Asset> insertRow(
|
||||
_is.DatabaseSession session,
|
||||
Asset row, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.insertRow<Asset>(
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts all [Asset]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 [Asset]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<Asset>> upsert(
|
||||
_is.DatabaseSession session,
|
||||
List<Asset> rows, {
|
||||
required _is.ColumnSelections<AssetTable> conflictColumns,
|
||||
_is.ColumnSelections<AssetTable>? updateColumns,
|
||||
_is.WhereExpressionBuilder<AssetTable>? updateWhere,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.upsert<Asset>(
|
||||
rows,
|
||||
conflictColumns: conflictColumns(Asset.t),
|
||||
updateColumns: updateColumns?.call(Asset.t),
|
||||
updateWhere: updateWhere?.call(Asset.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts a single [Asset] 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 [Asset] will have its `id` field set.
|
||||
Future<Asset?> upsertRow(
|
||||
_is.DatabaseSession session,
|
||||
Asset row, {
|
||||
required _is.ColumnSelections<AssetTable> conflictColumns,
|
||||
_is.ColumnSelections<AssetTable>? updateColumns,
|
||||
_is.WhereExpressionBuilder<AssetTable>? updateWhere,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.upsertRow<Asset>(
|
||||
row,
|
||||
conflictColumns: conflictColumns(Asset.t),
|
||||
updateColumns: updateColumns?.call(Asset.t),
|
||||
updateWhere: updateWhere?.call(Asset.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [Asset]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<Asset>> update(
|
||||
_is.DatabaseSession session,
|
||||
List<Asset> rows, {
|
||||
_is.ColumnSelections<AssetTable>? columns,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.update<Asset>(
|
||||
rows,
|
||||
columns: columns?.call(Asset.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [Asset]. 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<Asset> updateRow(
|
||||
_is.DatabaseSession session,
|
||||
Asset row, {
|
||||
_is.ColumnSelections<AssetTable>? columns,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateRow<Asset>(
|
||||
row,
|
||||
columns: columns?.call(Asset.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [Asset] by its [id] with the specified [columnValues].
|
||||
/// Returns the updated row or null if no row with the given id exists.
|
||||
Future<Asset?> updateById(
|
||||
_is.DatabaseSession session,
|
||||
_is.UuidValue id, {
|
||||
required _is.ColumnValueListBuilder<AssetUpdateTable> columnValues,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateById<Asset>(
|
||||
id,
|
||||
columnValues: columnValues(Asset.t.updateTable),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [Asset]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<Asset>> updateWhere(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.ColumnValueListBuilder<AssetUpdateTable> columnValues,
|
||||
required _is.WhereExpressionBuilder<AssetTable> where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<AssetTable>? orderBy,
|
||||
_is.OrderByListBuilder<AssetTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.updateWhere<Asset>(
|
||||
columnValues: columnValues(Asset.t.updateTable),
|
||||
where: where(Asset.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(Asset.t),
|
||||
orderByList: orderByList?.call(Asset.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes all [Asset]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<Asset>> delete(
|
||||
_is.DatabaseSession session,
|
||||
List<Asset> rows, {
|
||||
_is.OrderByBuilder<AssetTable>? orderBy,
|
||||
_is.OrderByListBuilder<AssetTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.delete<Asset>(
|
||||
rows,
|
||||
orderBy: orderBy?.call(Asset.t),
|
||||
orderByList: orderByList?.call(Asset.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes a single [Asset].
|
||||
Future<Asset> deleteRow(
|
||||
_is.DatabaseSession session,
|
||||
Asset row, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.deleteRow<Asset>(
|
||||
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<Asset>> deleteWhere(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.WhereExpressionBuilder<AssetTable> where,
|
||||
_is.OrderByBuilder<AssetTable>? orderBy,
|
||||
_is.OrderByListBuilder<AssetTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.deleteWhere<Asset>(
|
||||
where: where(Asset.t),
|
||||
orderBy: orderBy?.call(Asset.t),
|
||||
orderByList: orderByList?.call(Asset.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<AssetTable>? where,
|
||||
int? limit,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.count<Asset>(
|
||||
where: where?.call(Asset.t),
|
||||
limit: limit,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Acquires row-level locks on [Asset] rows matching the [where] expression.
|
||||
Future<void> lockRows(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.WhereExpressionBuilder<AssetTable> where,
|
||||
required _is.LockMode lockMode,
|
||||
required _is.Transaction transaction,
|
||||
_is.LockBehavior lockBehavior = _is.LockBehavior.wait,
|
||||
}) async {
|
||||
return session.db.lockRows<Asset>(
|
||||
where: where(Asset.t),
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ 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 'asset/asset.dart' as _ipb7vkxr;
|
||||
import 'auth/device.dart' as _i6c5ny7n;
|
||||
import 'auth/github_sign_in_refusal.dart' as _ir1gd9p5;
|
||||
import 'auth/github_sign_in_refused_exception.dart' as _i0xfcu5n;
|
||||
|
|
@ -29,7 +30,11 @@ import 'card/card_revision.dart' as _ipbyuef1;
|
|||
import 'common/not_found_exception.dart' as _i8oee73b;
|
||||
import 'instance/instance_info.dart' as _iskrg2g3;
|
||||
import 'review/review_log.dart' as _irzjbdqz;
|
||||
import 'settings/person_settings.dart' as _irnhx5e5;
|
||||
import 'studyset/studyset.dart' as _ikbclmae;
|
||||
import 'tag/card_tag.dart' as _i6jz6nyq;
|
||||
import 'tag/tag.dart' as _ie8t3ar4;
|
||||
export 'asset/asset.dart';
|
||||
export 'auth/device.dart';
|
||||
export 'auth/github_sign_in_refusal.dart';
|
||||
export 'auth/github_sign_in_refused_exception.dart';
|
||||
|
|
@ -38,7 +43,10 @@ export 'card/card_revision.dart';
|
|||
export 'common/not_found_exception.dart';
|
||||
export 'instance/instance_info.dart';
|
||||
export 'review/review_log.dart';
|
||||
export 'settings/person_settings.dart';
|
||||
export 'studyset/studyset.dart';
|
||||
export 'tag/card_tag.dart';
|
||||
export 'tag/tag.dart';
|
||||
export 'sync_tables.dart';
|
||||
|
||||
class Protocol extends _is.DatabaseSerializationManager {
|
||||
|
|
@ -49,6 +57,108 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
static final Protocol _instance = Protocol._().._registerHostProtocols();
|
||||
|
||||
static List<_isp.TableDefinition> get targetTableDefinitions => [
|
||||
_isp.TableDefinition(
|
||||
name: 'asset',
|
||||
dartName: 'Asset',
|
||||
schema: 'public',
|
||||
module: 'halkyon_learn',
|
||||
columns: [
|
||||
_isp.ColumnDefinition(
|
||||
name: 'id',
|
||||
columnType: _isp.ColumnType.uuid,
|
||||
isNullable: false,
|
||||
dartType: 'UuidValue?',
|
||||
columnDefault: 'random_v7',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'spaceId',
|
||||
columnType: _isp.ColumnType.bigint,
|
||||
isNullable: true,
|
||||
dartType: 'int?',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'sha256',
|
||||
columnType: _isp.ColumnType.text,
|
||||
isNullable: false,
|
||||
dartType: 'String',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'mimeType',
|
||||
columnType: _isp.ColumnType.text,
|
||||
isNullable: false,
|
||||
dartType: 'String',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'byteSize',
|
||||
columnType: _isp.ColumnType.bigint,
|
||||
isNullable: false,
|
||||
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: 'asset_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: 'asset_space_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isp.IndexElementDefinition(
|
||||
type: _isp.IndexElementDefinitionType.column,
|
||||
definition: 'spaceId',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
_isp.IndexDefinition(
|
||||
indexName: 'asset_hash_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isp.IndexElementDefinition(
|
||||
type: _isp.IndexElementDefinitionType.column,
|
||||
definition: 'spaceId',
|
||||
),
|
||||
_isp.IndexElementDefinition(
|
||||
type: _isp.IndexElementDefinitionType.column,
|
||||
definition: 'sha256',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
],
|
||||
managed: true,
|
||||
),
|
||||
_isp.TableDefinition(
|
||||
name: 'card',
|
||||
dartName: 'Card',
|
||||
|
|
@ -253,6 +363,182 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
],
|
||||
managed: true,
|
||||
),
|
||||
_isp.TableDefinition(
|
||||
name: 'card_tag',
|
||||
dartName: 'CardTag',
|
||||
schema: 'public',
|
||||
module: 'halkyon_learn',
|
||||
columns: [
|
||||
_isp.ColumnDefinition(
|
||||
name: 'id',
|
||||
columnType: _isp.ColumnType.uuid,
|
||||
isNullable: false,
|
||||
dartType: 'UuidValue?',
|
||||
columnDefault: 'random_v7',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'spaceId',
|
||||
columnType: _isp.ColumnType.bigint,
|
||||
isNullable: true,
|
||||
dartType: 'int?',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'cardId',
|
||||
columnType: _isp.ColumnType.uuid,
|
||||
isNullable: false,
|
||||
dartType: 'UuidValue',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'tagId',
|
||||
columnType: _isp.ColumnType.uuid,
|
||||
isNullable: false,
|
||||
dartType: 'UuidValue',
|
||||
),
|
||||
],
|
||||
foreignKeys: [
|
||||
_isp.ForeignKeyDefinition(
|
||||
constraintName: 'card_tag_fk_0',
|
||||
columns: ['spaceId'],
|
||||
referenceTable: 'offline_sync_spaces',
|
||||
referenceTableSchema: 'public',
|
||||
referenceColumns: ['id'],
|
||||
onUpdate: _isp.ForeignKeyAction.noAction,
|
||||
onDelete: _isp.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
),
|
||||
_isp.ForeignKeyDefinition(
|
||||
constraintName: 'card_tag_fk_1',
|
||||
columns: ['cardId'],
|
||||
referenceTable: 'card',
|
||||
referenceTableSchema: 'public',
|
||||
referenceColumns: ['id'],
|
||||
onUpdate: _isp.ForeignKeyAction.noAction,
|
||||
onDelete: _isp.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
deferrable: _isp.DeferrableConstraint.initiallyDeferred,
|
||||
),
|
||||
_isp.ForeignKeyDefinition(
|
||||
constraintName: 'card_tag_fk_2',
|
||||
columns: ['tagId'],
|
||||
referenceTable: 'tag',
|
||||
referenceTableSchema: 'public',
|
||||
referenceColumns: ['id'],
|
||||
onUpdate: _isp.ForeignKeyAction.noAction,
|
||||
onDelete: _isp.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
deferrable: _isp.DeferrableConstraint.initiallyDeferred,
|
||||
),
|
||||
],
|
||||
indexes: [
|
||||
_isp.IndexDefinition(
|
||||
indexName: 'card_tag_space_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isp.IndexElementDefinition(
|
||||
type: _isp.IndexElementDefinitionType.column,
|
||||
definition: 'spaceId',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
_isp.IndexDefinition(
|
||||
indexName: 'card_tag_card_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isp.IndexElementDefinition(
|
||||
type: _isp.IndexElementDefinitionType.column,
|
||||
definition: 'cardId',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
_isp.IndexDefinition(
|
||||
indexName: 'card_tag_tag_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isp.IndexElementDefinition(
|
||||
type: _isp.IndexElementDefinitionType.column,
|
||||
definition: 'tagId',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
],
|
||||
managed: true,
|
||||
),
|
||||
_isp.TableDefinition(
|
||||
name: 'person_settings',
|
||||
dartName: 'PersonSettings',
|
||||
schema: 'public',
|
||||
module: 'halkyon_learn',
|
||||
columns: [
|
||||
_isp.ColumnDefinition(
|
||||
name: 'id',
|
||||
columnType: _isp.ColumnType.uuid,
|
||||
isNullable: false,
|
||||
dartType: 'UuidValue?',
|
||||
columnDefault: 'random_v7',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'spaceId',
|
||||
columnType: _isp.ColumnType.bigint,
|
||||
isNullable: true,
|
||||
dartType: 'int?',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: '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: 'person_settings_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: 'person_settings_space_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isp.IndexElementDefinition(
|
||||
type: _isp.IndexElementDefinitionType.column,
|
||||
definition: 'spaceId',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
],
|
||||
managed: true,
|
||||
),
|
||||
_isp.TableDefinition(
|
||||
name: 'review_log',
|
||||
dartName: 'ReviewLog',
|
||||
|
|
@ -296,6 +582,13 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
isNullable: false,
|
||||
dartType: 'int',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'mode',
|
||||
columnType: _isp.ColumnType.text,
|
||||
isNullable: false,
|
||||
dartType: 'String',
|
||||
columnDefault: '\'review\'',
|
||||
),
|
||||
],
|
||||
foreignKeys: [
|
||||
_isp.ForeignKeyDefinition(
|
||||
|
|
@ -373,6 +666,19 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
isNullable: true,
|
||||
dartType: 'int?',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'parentId',
|
||||
columnType: _isp.ColumnType.uuid,
|
||||
isNullable: true,
|
||||
dartType: 'UuidValue?',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'position',
|
||||
columnType: _isp.ColumnType.text,
|
||||
isNullable: false,
|
||||
dartType: 'String',
|
||||
columnDefault: '\'a0\'',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'keyId',
|
||||
columnType: _isp.ColumnType.text,
|
||||
|
|
@ -403,6 +709,17 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
onDelete: _isp.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
),
|
||||
_isp.ForeignKeyDefinition(
|
||||
constraintName: 'studyset_fk_1',
|
||||
columns: ['parentId'],
|
||||
referenceTable: 'studyset',
|
||||
referenceTableSchema: 'public',
|
||||
referenceColumns: ['id'],
|
||||
onUpdate: _isp.ForeignKeyAction.noAction,
|
||||
onDelete: _isp.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
deferrable: _isp.DeferrableConstraint.initiallyDeferred,
|
||||
),
|
||||
],
|
||||
indexes: [
|
||||
_isp.IndexDefinition(
|
||||
|
|
@ -418,6 +735,90 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
_isp.IndexDefinition(
|
||||
indexName: 'studyset_parent_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isp.IndexElementDefinition(
|
||||
type: _isp.IndexElementDefinitionType.column,
|
||||
definition: 'parentId',
|
||||
),
|
||||
_isp.IndexElementDefinition(
|
||||
type: _isp.IndexElementDefinitionType.column,
|
||||
definition: 'position',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
],
|
||||
managed: true,
|
||||
),
|
||||
_isp.TableDefinition(
|
||||
name: 'tag',
|
||||
dartName: 'Tag',
|
||||
schema: 'public',
|
||||
module: 'halkyon_learn',
|
||||
columns: [
|
||||
_isp.ColumnDefinition(
|
||||
name: 'id',
|
||||
columnType: _isp.ColumnType.uuid,
|
||||
isNullable: false,
|
||||
dartType: 'UuidValue?',
|
||||
columnDefault: 'random_v7',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'spaceId',
|
||||
columnType: _isp.ColumnType.bigint,
|
||||
isNullable: true,
|
||||
dartType: 'int?',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: '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: 'tag_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: 'tag_space_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isp.IndexElementDefinition(
|
||||
type: _isp.IndexElementDefinitionType.column,
|
||||
definition: 'spaceId',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
],
|
||||
managed: true,
|
||||
),
|
||||
|
|
@ -454,6 +855,9 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
}
|
||||
}
|
||||
|
||||
if (t == _ipb7vkxr.Asset) {
|
||||
return _ipb7vkxr.Asset.fromJson(data) as T;
|
||||
}
|
||||
if (t == _i6c5ny7n.Device) {
|
||||
return _i6c5ny7n.Device.fromJson(data) as T;
|
||||
}
|
||||
|
|
@ -478,9 +882,21 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
if (t == _irzjbdqz.ReviewLog) {
|
||||
return _irzjbdqz.ReviewLog.fromJson(data) as T;
|
||||
}
|
||||
if (t == _irnhx5e5.PersonSettings) {
|
||||
return _irnhx5e5.PersonSettings.fromJson(data) as T;
|
||||
}
|
||||
if (t == _ikbclmae.Studyset) {
|
||||
return _ikbclmae.Studyset.fromJson(data) as T;
|
||||
}
|
||||
if (t == _i6jz6nyq.CardTag) {
|
||||
return _i6jz6nyq.CardTag.fromJson(data) as T;
|
||||
}
|
||||
if (t == _ie8t3ar4.Tag) {
|
||||
return _ie8t3ar4.Tag.fromJson(data) as T;
|
||||
}
|
||||
if (t == _is.getType<_ipb7vkxr.Asset?>()) {
|
||||
return (data != null ? _ipb7vkxr.Asset.fromJson(data) : null) as T;
|
||||
}
|
||||
if (t == _is.getType<_i6c5ny7n.Device?>()) {
|
||||
return (data != null ? _i6c5ny7n.Device.fromJson(data) : null) as T;
|
||||
}
|
||||
|
|
@ -512,9 +928,19 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
if (t == _is.getType<_irzjbdqz.ReviewLog?>()) {
|
||||
return (data != null ? _irzjbdqz.ReviewLog.fromJson(data) : null) as T;
|
||||
}
|
||||
if (t == _is.getType<_irnhx5e5.PersonSettings?>()) {
|
||||
return (data != null ? _irnhx5e5.PersonSettings.fromJson(data) : null)
|
||||
as T;
|
||||
}
|
||||
if (t == _is.getType<_ikbclmae.Studyset?>()) {
|
||||
return (data != null ? _ikbclmae.Studyset.fromJson(data) : null) as T;
|
||||
}
|
||||
if (t == _is.getType<_i6jz6nyq.CardTag?>()) {
|
||||
return (data != null ? _i6jz6nyq.CardTag.fromJson(data) : null) as T;
|
||||
}
|
||||
if (t == _is.getType<_ie8t3ar4.Tag?>()) {
|
||||
return (data != null ? _ie8t3ar4.Tag.fromJson(data) : null) as T;
|
||||
}
|
||||
if (t == List<_is.UuidValue>) {
|
||||
return (data as List).map((e) => deserialize<_is.UuidValue>(e)).toList()
|
||||
as T;
|
||||
|
|
@ -542,6 +968,7 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
|
||||
static String? getClassNameForType(Type type) {
|
||||
return switch (type) {
|
||||
_ipb7vkxr.Asset => 'Asset',
|
||||
_i6c5ny7n.Device => 'Device',
|
||||
_ir1gd9p5.GitHubSignInRefusal => 'GitHubSignInRefusal',
|
||||
_i0xfcu5n.GitHubSignInRefusedException => 'GitHubSignInRefusedException',
|
||||
|
|
@ -550,7 +977,10 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
_i8oee73b.NotFoundException => 'NotFoundException',
|
||||
_iskrg2g3.InstanceInfo => 'InstanceInfo',
|
||||
_irzjbdqz.ReviewLog => 'ReviewLog',
|
||||
_irnhx5e5.PersonSettings => 'PersonSettings',
|
||||
_ikbclmae.Studyset => 'Studyset',
|
||||
_i6jz6nyq.CardTag => 'CardTag',
|
||||
_ie8t3ar4.Tag => 'Tag',
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
|
@ -568,6 +998,8 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
}
|
||||
|
||||
switch (data) {
|
||||
case _ipb7vkxr.Asset():
|
||||
return 'Asset';
|
||||
case _i6c5ny7n.Device():
|
||||
return 'Device';
|
||||
case _ir1gd9p5.GitHubSignInRefusal():
|
||||
|
|
@ -584,8 +1016,14 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
return 'InstanceInfo';
|
||||
case _irzjbdqz.ReviewLog():
|
||||
return 'ReviewLog';
|
||||
case _irnhx5e5.PersonSettings():
|
||||
return 'PersonSettings';
|
||||
case _ikbclmae.Studyset():
|
||||
return 'Studyset';
|
||||
case _i6jz6nyq.CardTag():
|
||||
return 'CardTag';
|
||||
case _ie8t3ar4.Tag():
|
||||
return 'Tag';
|
||||
}
|
||||
className = _iais.Protocol().getClassNameForObject(data);
|
||||
if (className != null) {
|
||||
|
|
@ -618,6 +1056,9 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
if (dataClassName is! String) {
|
||||
return super.deserializeByClassName(data);
|
||||
}
|
||||
if (dataClassName == 'Asset') {
|
||||
return deserialize<_ipb7vkxr.Asset>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'Device') {
|
||||
return deserialize<_i6c5ny7n.Device>(data['data']);
|
||||
}
|
||||
|
|
@ -642,9 +1083,18 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
if (dataClassName == 'ReviewLog') {
|
||||
return deserialize<_irzjbdqz.ReviewLog>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'PersonSettings') {
|
||||
return deserialize<_irnhx5e5.PersonSettings>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'Studyset') {
|
||||
return deserialize<_ikbclmae.Studyset>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'CardTag') {
|
||||
return deserialize<_i6jz6nyq.CardTag>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'Tag') {
|
||||
return deserialize<_ie8t3ar4.Tag>(data['data']);
|
||||
}
|
||||
if (dataClassName.startsWith('serverpod_auth_idp.')) {
|
||||
data['className'] = dataClassName.substring(19);
|
||||
return _iais.Protocol().deserializeByClassName(data);
|
||||
|
|
@ -697,14 +1147,22 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
}
|
||||
}
|
||||
switch (t) {
|
||||
case _ipb7vkxr.Asset:
|
||||
return _ipb7vkxr.Asset.t;
|
||||
case _ipiw1cmx.Card:
|
||||
return _ipiw1cmx.Card.t;
|
||||
case _ipbyuef1.CardRevision:
|
||||
return _ipbyuef1.CardRevision.t;
|
||||
case _irzjbdqz.ReviewLog:
|
||||
return _irzjbdqz.ReviewLog.t;
|
||||
case _irnhx5e5.PersonSettings:
|
||||
return _irnhx5e5.PersonSettings.t;
|
||||
case _ikbclmae.Studyset:
|
||||
return _ikbclmae.Studyset.t;
|
||||
case _i6jz6nyq.CardTag:
|
||||
return _i6jz6nyq.CardTag.t;
|
||||
case _ie8t3ar4.Tag:
|
||||
return _ie8t3ar4.Tag.t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue