add a DB browser for the notification SQL, and update docs
This commit is contained in:
@@ -281,14 +281,14 @@ App (`crowd-funder-for-time-pwa`):
|
|||||||
where New Activity starts in parallel with the feed, Affirm Delivery waits
|
where New Activity starts in parallel with the feed, Affirm Delivery waits
|
||||||
for the offer's project, a WebAssembly explanation on the start-up error
|
for the offer's project, a WebAssembly explanation on the start-up error
|
||||||
screen, test warm-up, retry and reporter).
|
screen, test warm-up, retry and reporter).
|
||||||
- [ ] Release the app after endorser-ch is deployed, since detail rows read
|
- [x] Release the app after endorser-ch is deployed, since detail rows read
|
||||||
`claim` from `alertSearch` — next: release (Trent)
|
`claim` from `alertSearch` — next: release (Trent)
|
||||||
|
|
||||||
Notification service (`notification-wakeup-service`):
|
Notification service (`notification-wakeup-service`):
|
||||||
|
|
||||||
- [x] Service changes 1–6, `c` in the SMS link and FCM data, tests, README,
|
- [x] Service changes 1–6, `c` in the SMS link and FCM data, tests, README,
|
||||||
CHANGELOG.
|
CHANGELOG.
|
||||||
- [ ] Deploy to test and production — next: deploy, or tick this if done
|
- [x] Deploy to test and production — next: deploy, or tick this if done
|
||||||
(Trent)
|
(Trent)
|
||||||
|
|
||||||
endorser-ch:
|
endorser-ch:
|
||||||
@@ -298,11 +298,11 @@ endorser-ch:
|
|||||||
sends) gets `toEntity`, so it reaches the owner's
|
sends) gets `toEntity`, so it reaches the owner's
|
||||||
`personalPlanContributions` and `trackedPlanClaims`; migration V23 fills
|
`personalPlanContributions` and `trackedPlanClaims`; migration V23 fills
|
||||||
existing offers without moving `changeSeq`; test, CHANGELOG.
|
existing offers without moving `changeSeq`; test, CHANGELOG.
|
||||||
- [ ] Commit the `isPartOf` change: `CHANGELOG.md`, `sql/README.md`,
|
- [x] Commit the `isPartOf` change: `CHANGELOG.md`, `sql/README.md`,
|
||||||
`sql/V23__offer_is_part_of_to_entity.sqlite3`, `claim.service.js`,
|
`sql/V23__offer_is_part_of_to_entity.sqlite3`, `claim.service.js`,
|
||||||
`endorser.db.service.js`, `test/controller-endorser-6-plans-totals.js` —
|
`endorser.db.service.js`, `test/controller-endorser-6-plans-totals.js` —
|
||||||
next: commit (Trent)
|
next: commit (Trent)
|
||||||
- [ ] Deploy to test, then production, running Flyway on the main DB so V23
|
- [x] Deploy to test, then production, running Flyway on the main DB so V23
|
||||||
applies — next: deploy (Trent)
|
applies — next: deploy (Trent)
|
||||||
- [ ] Leave a person's own gives and offers out of their
|
- [ ] Leave a person's own gives and offers out of their
|
||||||
`personalPlanContributions` (the query includes them when a person
|
`personalPlanContributions` (the query includes them when a person
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
- [ ] Default `apiServer` in every settings fallback. `$accountSettings()`
|
||||||
|
(`src/utils/PlatformServiceMixin.ts`) returns the caller's `defaults` (often
|
||||||
|
`{}`) on its early-return and error paths, so `apiServer` can be undefined;
|
||||||
|
`HomeView` works around this on its own. This gap exists independently of
|
||||||
|
degraded mode.
|
||||||
+48
-14
@@ -1,5 +1,17 @@
|
|||||||
# Plan: Startup Database Failure — Diagnostics and Degraded Mode
|
# Plan: Startup Database Failure — Diagnostics and Degraded Mode
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Closed without building Parts A–C. The startup failure this plan prepared for
|
||||||
|
had a root cause, and the fix in `fix-startup-db-crash.md` removed it: the
|
||||||
|
SQLite plugin's configuration key, migration recording, and Android backup
|
||||||
|
rules. A user who saw the error on every launch started normally after
|
||||||
|
installing a build with that fix.
|
||||||
|
|
||||||
|
The recovery screen from commit d6ccf0e4 remains the answer for any other
|
||||||
|
database failure. Parts A–C below are kept as a design to pick up if startup
|
||||||
|
database failures are reported again; nothing in them has been built.
|
||||||
|
|
||||||
## Goal
|
## Goal
|
||||||
|
|
||||||
When the local database cannot be opened, a user should (1) get a useful
|
When the local database cannot be opened, a user should (1) get a useful
|
||||||
@@ -27,23 +39,25 @@ Requirements:
|
|||||||
- `src/utils/startupError.ts` recognizes that error by its message and hands it
|
- `src/utils/startupError.ts` recognizes that error by its message and hands it
|
||||||
to `StartView` through `sessionStorage`.
|
to `StartView` through `sessionStorage`.
|
||||||
- Three places catch it and redirect to `/start`: the router `errorHandler`
|
- Three places catch it and redirect to `/start`: the router `errorHandler`
|
||||||
and `beforeEach` guard (`src/router/index.ts:344`, `:363-454`), and the Vue
|
and `beforeEach` guard (`src/router/index.ts`), and the Vue global error
|
||||||
global error handler (`src/main.common.ts`).
|
handler (`src/main.common.ts`).
|
||||||
- The guard calls `retrieveAccountDids()` for every route except the
|
- The guard calls `retrieveAccountDids()` for every route except the
|
||||||
identity-setup routes, so with the database down every route except
|
identity-setup routes, so with the database down every route except
|
||||||
`/account`, `/test` and `/logs` lands on `/start`.
|
`/account`, `/test` and `/logs` lands on `/start`.
|
||||||
- `StartView` shows the recovery screen: Try Again, Email/Copy Details,
|
- `StartView` shows the recovery screen: Try Again, Email Details, Copy
|
||||||
"Save a Copy of My Data" (`shareDatabaseFile()`), and "Start Over"
|
Details, "Save a Copy of My Data" (`shareDatabaseFile()`), and "Start Over"
|
||||||
(`resetDatabase()`, which copies the file aside before deleting it).
|
(`resetDatabase()`, which copies the file aside before deleting it). When the
|
||||||
|
browser cannot run WebAssembly, the screen says so and points to the
|
||||||
|
settings that block it.
|
||||||
- API reads work without an identity: `getHeaders()`
|
- API reads work without an identity: `getHeaders()`
|
||||||
(`src/libs/endorserServer.ts:430`) omits the Authorization header when there
|
(`src/libs/endorserServer.ts`) omits the Authorization header when there
|
||||||
is no DID.
|
is no DID.
|
||||||
- Every path that creates, saves, or signs with an identity reads the `secret`
|
- Every path that creates, saves, or signs with an identity reads the `secret`
|
||||||
and `accounts` tables (`src/libs/util.ts`: `saveNewIdentity`,
|
and `accounts` tables (`src/libs/util.ts`: `saveNewIdentity`,
|
||||||
`retrieveFullyDecryptedAccount`), so no action that needs a DID can work while
|
`retrieveFullyDecryptedAccount`), so no action that needs a DID can work while
|
||||||
the database is down.
|
the database is down.
|
||||||
|
|
||||||
## Part A: One-tap diagnostics report
|
## Part A: One-tap diagnostics report (not built)
|
||||||
|
|
||||||
Contents of the report:
|
Contents of the report:
|
||||||
|
|
||||||
@@ -61,10 +75,10 @@ screen shows the exact payload and a Send button; the email option stays for
|
|||||||
users who decline.
|
users who decline.
|
||||||
|
|
||||||
The database-existed flag needs a native-side or `Filesystem.stat` check before
|
The database-existed flag needs a native-side or `Filesystem.stat` check before
|
||||||
`createConnection`; `databaseDirectoryUri()` already resolves the directory on
|
`createConnection`; `databaseDirectoryUri()` resolves the directory on
|
||||||
Android and iOS.
|
Android and iOS.
|
||||||
|
|
||||||
## Part B: Degraded mode
|
## Part B: Degraded mode (not built)
|
||||||
|
|
||||||
- Change the router guard so that a database startup error records the error
|
- Change the router guard so that a database startup error records the error
|
||||||
and lets read-only routes through: home feed, projects, project and claim
|
and lets read-only routes through: home feed, projects, project and claim
|
||||||
@@ -77,13 +91,14 @@ Android and iOS.
|
|||||||
confirm, invite, contacts) with that same reason, instead of the
|
confirm, invite, contacts) with that same reason, instead of the
|
||||||
"someone must register you" text an unregistered user sees.
|
"someone must register you" text an unregistered user sees.
|
||||||
- Default `apiServer` in every settings fallback. `$accountSettings()`
|
- Default `apiServer` in every settings fallback. `$accountSettings()`
|
||||||
(`src/utils/PlatformServiceMixin.ts:1071-1136`) returns `defaultSettings` or
|
(`src/utils/PlatformServiceMixin.ts`) returns the caller's `defaults` (often
|
||||||
the caller's `defaults` (often `{}`) on its early-return and error paths, so
|
`{}`) on its early-return and error paths, so `apiServer` can be undefined;
|
||||||
`apiServer` can be undefined; `HomeView` works around this on its own.
|
`HomeView` works around this on its own. This gap exists independently of
|
||||||
|
degraded mode. (Moved to PLAN.md)
|
||||||
- Order the recovery screen as a ladder: Try Again → Send Diagnostics → Save a
|
- Order the recovery screen as a ladder: Try Again → Send Diagnostics → Save a
|
||||||
Copy → Start Over.
|
Copy → Start Over.
|
||||||
|
|
||||||
## Part C (optional): An identity that does not need SQLite
|
## Part C (optional): An identity that does not need SQLite (not built)
|
||||||
|
|
||||||
Key generation is pure (`generateSeed` → `deriveAddress` → `newIdentifier`).
|
Key generation is pure (`generateSeed` → `deriveAddress` → `newIdentifier`).
|
||||||
In degraded mode the app could generate an identity, require the user to write
|
In degraded mode the app could generate an identity, require the user to write
|
||||||
@@ -99,6 +114,8 @@ encryption setting (`androidIsEncryption: false`) in effect.
|
|||||||
|
|
||||||
## Open questions
|
## Open questions
|
||||||
|
|
||||||
|
These stay open for whoever picks up Parts A–C.
|
||||||
|
|
||||||
- Which server and endpoint receive diagnostics, and how long reports are kept.
|
- Which server and endpoint receive diagnostics, and how long reports are kept.
|
||||||
- Whether to quarantine a corrupt database automatically ("file is not a
|
- Whether to quarantine a corrupt database automatically ("file is not a
|
||||||
database", "malformed") and start fresh. Automatic quarantine trades a
|
database", "malformed") and start fresh. Automatic quarantine trades a
|
||||||
@@ -109,7 +126,8 @@ encryption setting (`androidIsEncryption: false`) in effect.
|
|||||||
user's identity on a new phone; it also gives the Google backup a copy of the
|
user's identity on a new phone; it also gives the Google backup a copy of the
|
||||||
keys together with the secret that decrypts them. The backup rules
|
keys together with the secret that decrypts them. The backup rules
|
||||||
(`android/app/src/main/res/xml/backup_rules.xml`,
|
(`android/app/src/main/res/xml/backup_rules.xml`,
|
||||||
`data_extraction_rules.xml`) include it.
|
`data_extraction_rules.xml`) include it and exclude only the plugin's
|
||||||
|
encrypted preference files.
|
||||||
|
|
||||||
## Rejected
|
## Rejected
|
||||||
|
|
||||||
@@ -119,6 +137,8 @@ encryption setting (`androidIsEncryption: false`) in effect.
|
|||||||
|
|
||||||
## Checklist
|
## Checklist
|
||||||
|
|
||||||
|
Parts A–C are deferred, not scheduled.
|
||||||
|
|
||||||
- [ ] A1. Decide the diagnostics endpoint and payload retention.
|
- [ ] A1. Decide the diagnostics endpoint and payload retention.
|
||||||
- [ ] A2. Capture "database file existed before first connection" during startup.
|
- [ ] A2. Capture "database file existed before first connection" during startup.
|
||||||
- [ ] A3. Build the report payload and the Send Diagnostics button with preview.
|
- [ ] A3. Build the report payload and the Send Diagnostics button with preview.
|
||||||
@@ -127,3 +147,17 @@ encryption setting (`androidIsEncryption: false`) in effect.
|
|||||||
- [ ] B3. Default `apiServer` in all `$accountSettings()` fallbacks.
|
- [ ] B3. Default `apiServer` in all `$accountSettings()` fallbacks.
|
||||||
- [ ] B4. Reorder the recovery screen.
|
- [ ] B4. Reorder the recovery screen.
|
||||||
- [ ] C1. (Optional) Preferences-backed identity with import on recovery.
|
- [ ] C1. (Optional) Preferences-backed identity with import on recovery.
|
||||||
|
|
||||||
|
## History
|
||||||
|
|
||||||
|
- 2026-10-10: commit d6ccf0e4 added the failed step and cause to the startup
|
||||||
|
error, the `startupError.ts` hand-off to `StartView`, and the recovery screen
|
||||||
|
(Try Again, Email/Copy Details, Save a Copy, Start Over).
|
||||||
|
- 2026-10-10: commit f41a807a fixed the root cause (see
|
||||||
|
`fix-startup-db-crash.md`) and wrote this plan. Tagged 1.4.8; the CHANGELOG
|
||||||
|
records it under 1.4.9 as "Startup DB error for new installations".
|
||||||
|
- 2026-10-17: commit d2cdd578 added the WebAssembly explanation to the recovery
|
||||||
|
screen (CHANGELOG 1.4.10).
|
||||||
|
- After 1.4.9: a user who had the startup error on every launch reported
|
||||||
|
normal startup on a build with the fix. The plan was closed with Parts A–C
|
||||||
|
unbuilt.
|
||||||
@@ -10,8 +10,7 @@ Error" on every launch:
|
|||||||
```
|
```
|
||||||
|
|
||||||
Reinstalling did not help. The failure never appeared on developer devices
|
Reinstalling did not help. The failure never appeared on developer devices
|
||||||
installed through adb or Android Studio. Full symptom report:
|
installed through adb or Android Studio.
|
||||||
`startup-db-error-symptoms.md`.
|
|
||||||
|
|
||||||
## Causes
|
## Causes
|
||||||
|
|
||||||
@@ -107,7 +106,8 @@ the app on both platforms.
|
|||||||
## Follow-up
|
## Follow-up
|
||||||
|
|
||||||
The one-tap diagnostics report and a degraded mode for users without a working
|
The one-tap diagnostics report and a degraded mode for users without a working
|
||||||
database are planned in `doc/CHANGES/PLAN-startup-db-failure-recovery.md`.
|
database are designed, but not built, in
|
||||||
|
`PLAN-startup-db-failure-recovery.md` in this directory.
|
||||||
|
|
||||||
## History
|
## History
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/**
|
||||||
|
* Read-only SQL access to the daily-notification-plugin's own SQLite database.
|
||||||
|
*
|
||||||
|
* The plugin keeps its state in a database separate from the app's, created
|
||||||
|
* natively (Room on Android, raw sqlite3 on iOS). It exposes no SQL method, so
|
||||||
|
* this opens the file through @capacitor-community/sqlite as a "non-conformed"
|
||||||
|
* database, which that library always opens read-only. Writing is deliberately
|
||||||
|
* unsupported: the plugin owns the schema and holds its own connection.
|
||||||
|
*
|
||||||
|
* Locations (both resolve from the sqlite plugin's "default" folder):
|
||||||
|
* - Android: <app data>/databases/daily_notification_plugin.db
|
||||||
|
* - iOS: <app>/Documents/daily_notifications.db
|
||||||
|
*
|
||||||
|
* @see https://gitea.anomalistdesign.com/trent_larson/daily-notification-plugin
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Capacitor } from "@capacitor/core";
|
||||||
|
import { CapacitorSQLite, SQLiteConnection } from "@capacitor-community/sqlite";
|
||||||
|
|
||||||
|
const DATABASE_FILE_BY_PLATFORM: Record<string, string> = {
|
||||||
|
android: "daily_notification_plugin.db",
|
||||||
|
ios: "daily_notifications.db",
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Tables the plugin creates on each platform, for query presets. */
|
||||||
|
export const NOTIFICATION_PLUGIN_TABLES_BY_PLATFORM: Record<string, string[]> =
|
||||||
|
{
|
||||||
|
android: [
|
||||||
|
"schedules",
|
||||||
|
"content_cache",
|
||||||
|
"callbacks",
|
||||||
|
"history",
|
||||||
|
"notification_content",
|
||||||
|
"notification_delivery",
|
||||||
|
"notification_config",
|
||||||
|
],
|
||||||
|
ios: ["notif_contents", "notif_deliveries", "notif_config"],
|
||||||
|
};
|
||||||
|
|
||||||
|
let sqlite: SQLiteConnection | undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns the plugin database file name for this platform, or undefined
|
||||||
|
* where the plugin keeps no SQLite database (web, Electron)
|
||||||
|
*/
|
||||||
|
export function notificationPluginDatabaseFile(): string | undefined {
|
||||||
|
return DATABASE_FILE_BY_PLATFORM[Capacitor.getPlatform()];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs one statement against the plugin database and returns its rows.
|
||||||
|
* The connection is opened and closed per call so it never holds a lock
|
||||||
|
* the plugin's background work could wait on.
|
||||||
|
*
|
||||||
|
* @throws if the platform has no plugin database, the file does not exist
|
||||||
|
* yet (the plugin creates it on first use), or the statement writes
|
||||||
|
*/
|
||||||
|
export async function queryNotificationPluginDatabase(
|
||||||
|
statement: string,
|
||||||
|
): Promise<{ path: string; values: unknown[] }> {
|
||||||
|
const database = notificationPluginDatabaseFile();
|
||||||
|
if (!database) {
|
||||||
|
throw new Error(
|
||||||
|
`The notification plugin has no SQLite database on platform "${Capacitor.getPlatform()}".`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
sqlite = sqlite ?? new SQLiteConnection(CapacitorSQLite);
|
||||||
|
const { path } = await CapacitorSQLite.getNCDatabasePath({
|
||||||
|
path: "default",
|
||||||
|
database,
|
||||||
|
});
|
||||||
|
if (!path) {
|
||||||
|
throw new Error(`Could not resolve a path for ${database}.`);
|
||||||
|
}
|
||||||
|
const exists = await sqlite.isNCDatabase(path);
|
||||||
|
if (!exists.result) {
|
||||||
|
throw new Error(
|
||||||
|
`No plugin database at ${path}. The plugin creates it on first use.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const db = await sqlite.createNCConnection(path, 1);
|
||||||
|
try {
|
||||||
|
await db.open();
|
||||||
|
const result = await db.query(statement);
|
||||||
|
return { path, values: result.values ?? [] };
|
||||||
|
} finally {
|
||||||
|
await sqlite.closeNCConnection(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
+102
-1
@@ -49,7 +49,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-8">
|
<div class="mt-8">
|
||||||
<h2 class="text-xl font-bold mb-4">SQL Operations</h2>
|
<h2 class="text-xl font-bold mb-4">App SQL</h2>
|
||||||
<div class="flex gap-2 mt-2">
|
<div class="flex gap-2 mt-2">
|
||||||
<button :class="sqlLinkClasses" @click="setAllTablesQuery">
|
<button :class="sqlLinkClasses" @click="setAllTablesQuery">
|
||||||
All Tables
|
All Tables
|
||||||
@@ -95,6 +95,61 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-8">
|
||||||
|
<h2 class="text-xl font-bold mb-4">
|
||||||
|
Notification Plugin SQL (read-only)
|
||||||
|
</h2>
|
||||||
|
<div v-if="!notificationPluginDatabaseFile">
|
||||||
|
The daily-notification-plugin keeps a database only on Android and iOS.
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<div class="text-sm mb-2">
|
||||||
|
Separate from the app database: {{ notificationPluginDatabaseFile }}
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-wrap gap-2 mt-2">
|
||||||
|
<button
|
||||||
|
:class="sqlLinkClasses"
|
||||||
|
@click="
|
||||||
|
setNotificationPluginQuery(
|
||||||
|
`SELECT * FROM sqlite_master WHERE type='table';`,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
All Tables
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-for="table in notificationPluginTables"
|
||||||
|
:key="table"
|
||||||
|
:class="sqlLinkClasses"
|
||||||
|
@click="setNotificationPluginQuery(`SELECT * FROM ${table};`)"
|
||||||
|
>
|
||||||
|
{{ table }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<textarea
|
||||||
|
v-model="notificationPluginSqlQuery"
|
||||||
|
class="w-full h-32 p-2 border border-gray-300 rounded-md font-mono"
|
||||||
|
placeholder="Enter a SELECT or PRAGMA query here..."
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4">
|
||||||
|
<button
|
||||||
|
:class="primaryButtonClasses"
|
||||||
|
@click="executeNotificationPluginSql"
|
||||||
|
>
|
||||||
|
Execute
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div v-if="notificationPluginSqlResult" class="mt-4">
|
||||||
|
<h3 class="text-lg font-semibold mb-2">Result:</h3>
|
||||||
|
<pre class="bg-gray-100 p-4 rounded-md overflow-x-auto">{{
|
||||||
|
JSON.stringify(notificationPluginSqlResult, null, 2)
|
||||||
|
}}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="mt-8">
|
<div class="mt-8">
|
||||||
<h2 class="text-xl font-bold mb-4">Image Sharing</h2>
|
<h2 class="text-xl font-bold mb-4">Image Sharing</h2>
|
||||||
Populates the "shared-photo" view as if they used "share_target".
|
Populates the "shared-photo" view as if they used "share_target".
|
||||||
@@ -349,6 +404,12 @@ import { testBecomeUser0, testServerRegisterUser } from "@/test";
|
|||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
import { Account } from "../db/tables/accounts";
|
import { Account } from "../db/tables/accounts";
|
||||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||||
|
import {
|
||||||
|
NOTIFICATION_PLUGIN_TABLES_BY_PLATFORM,
|
||||||
|
notificationPluginDatabaseFile,
|
||||||
|
queryNotificationPluginDatabase,
|
||||||
|
} from "@/services/notifications/notificationPluginDatabase";
|
||||||
|
import { Capacitor } from "@capacitor/core";
|
||||||
import EntityGridFunctionPropTest from "../test/EntityGridFunctionPropTest.vue";
|
import EntityGridFunctionPropTest from "../test/EntityGridFunctionPropTest.vue";
|
||||||
import PlatformServiceMixinTest from "../test/PlatformServiceMixinTest.vue";
|
import PlatformServiceMixinTest from "../test/PlatformServiceMixinTest.vue";
|
||||||
|
|
||||||
@@ -421,6 +482,13 @@ export default class Help extends Vue {
|
|||||||
sqlResult: unknown = null;
|
sqlResult: unknown = null;
|
||||||
returnRawResults = false;
|
returnRawResults = false;
|
||||||
|
|
||||||
|
// for SQL operations on the daily-notification-plugin database
|
||||||
|
notificationPluginDatabaseFile = notificationPluginDatabaseFile();
|
||||||
|
notificationPluginTables =
|
||||||
|
NOTIFICATION_PLUGIN_TABLES_BY_PLATFORM[Capacitor.getPlatform()] ?? [];
|
||||||
|
notificationPluginSqlQuery = "";
|
||||||
|
notificationPluginSqlResult: unknown = null;
|
||||||
|
|
||||||
cryptoLib = cryptoLib;
|
cryptoLib = cryptoLib;
|
||||||
|
|
||||||
// for component tests
|
// for component tests
|
||||||
@@ -547,6 +615,11 @@ export default class Help extends Vue {
|
|||||||
this.executeSql();
|
this.executeSql();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setNotificationPluginQuery(query: string) {
|
||||||
|
this.notificationPluginSqlQuery = query;
|
||||||
|
this.executeNotificationPluginSql();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration for notification test buttons
|
* Configuration for notification test buttons
|
||||||
* Eliminates repetitive notification button definitions in template
|
* Eliminates repetitive notification button definitions in template
|
||||||
@@ -1022,6 +1095,34 @@ export default class Help extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a read-only query against the daily-notification-plugin's
|
||||||
|
* separate database (see notificationPluginDatabase.ts for why writes
|
||||||
|
* are not supported)
|
||||||
|
*/
|
||||||
|
async executeNotificationPluginSql() {
|
||||||
|
try {
|
||||||
|
this.notificationPluginSqlResult = await queryNotificationPluginDatabase(
|
||||||
|
this.notificationPluginSqlQuery,
|
||||||
|
);
|
||||||
|
logger.log(
|
||||||
|
"Test Notification Plugin SQL Result:",
|
||||||
|
this.notificationPluginSqlResult,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Test Notification Plugin SQL Error:", error);
|
||||||
|
this.$notify(
|
||||||
|
{
|
||||||
|
group: "alert",
|
||||||
|
type: "danger",
|
||||||
|
title: NOTIFY_SQL_ERROR.title,
|
||||||
|
text: createSqlErrorMessage(error),
|
||||||
|
},
|
||||||
|
5000,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the URL flow from initialization to change propagation.
|
* Tests the URL flow from initialization to change propagation.
|
||||||
* This simulates the flow where a user's DID is set, and then the
|
* This simulates the flow where a user's DID is set, and then the
|
||||||
|
|||||||
Reference in New Issue
Block a user