Browse Source

Merge pull request 'fix problem with repeated bad stringifies of contactMethods on contact export/import' (#148) from fix-contact-import-export into master

Reviewed-on: https://gitea.anomalistdesign.com/trent_larson/crowd-funder-for-time-pwa/pulls/148
master
trentlarson 1 day ago
parent
commit
6ddde21a86
  1. 24
      src/components/DataExportSection.vue
  2. 11
      src/db/tables/contacts.ts
  3. 18
      src/libs/util.ts

24
src/components/DataExportSection.vue

@ -60,9 +60,11 @@ backup and database export, with platform-specific download instructions. * *
<script lang="ts">
import { Component, Prop, Vue } from "vue-facing-decorator";
import * as R from "ramda";
import { AppString, NotificationIface } from "../constants/app";
import { Contact } from "../db/tables/contacts";
import { Contact, ContactMaybeWithJsonStrings, ContactMethod } from "../db/tables/contacts";
import * as databaseUtil from "../db/databaseUtil";
import { logger } from "../utils/logger";
@ -72,6 +74,7 @@ import {
PlatformCapabilities,
} from "../services/PlatformService";
import { contactsToExportJson } from "../libs/util";
import { parseJsonField } from "../db/databaseUtil";
/**
* @vue-component
@ -133,13 +136,13 @@ export default class DataExportSection extends Vue {
*/
public async exportDatabase() {
try {
let allContacts: Contact[] = [];
let allDbContacts: ContactMaybeWithJsonStrings[] = [];
const platformService = PlatformServiceFactory.getInstance();
const result = await platformService.dbQuery(`SELECT * FROM contacts`);
if (result) {
allContacts = databaseUtil.mapQueryResultToValues(
allDbContacts = databaseUtil.mapQueryResultToValues(
result,
) as unknown as Contact[];
) as unknown as ContactMaybeWithJsonStrings[];
}
// if (USE_DEXIE_DB) {
// await db.open();
@ -147,6 +150,19 @@ export default class DataExportSection extends Vue {
// }
// Convert contacts to export format
const allContacts: Contact[] = allDbContacts.map((contact) => {
// first remove the contactMethods field, mostly to cast to a clear type (that will end up with JSON objects)
const exContact: Contact = R.omit(
["contactMethods"],
contact,
);
// now add contactMethods as a true array of ContactMethod objects
exContact.contactMethods = contact.contactMethods
? parseJsonField(contact.contactMethods, [] as Array<ContactMethod>)
: undefined;
return exContact;
});
const exportData = contactsToExportJson(allContacts);
const jsonStr = JSON.stringify(exportData, null, 2);
const blob = new Blob([jsonStr], { type: "application/json" });

11
src/db/tables/contacts.ts

@ -30,6 +30,17 @@ export type ContactWithJsonStrings = Omit<Contact, "contactMethods"> & {
contactMethods?: string;
};
/**
* This is for those cases (eg. with a DB) where field values may be all primitives or may be JSON values.
* See src/db/databaseUtil.ts parseJsonField for more details.
*
* This is so that we can reuse most of the type and don't have to maintain another copy.
* Another approach uses typescript conditionals: https://chatgpt.com/share/6855cdc3-ab5c-8007-8525-726612016eb2
*/
export type ContactMaybeWithJsonStrings = Omit<Contact, "contactMethods"> & {
contactMethods?: string | Array<ContactMethod>;
};
export const ContactSchema = {
contacts: "&did, name", // no need to key by other things
};

18
src/libs/util.ts

@ -17,7 +17,7 @@ import {
updateDefaultSettings,
} from "../db/index";
import { Account, AccountEncrypted } from "../db/tables/accounts";
import { Contact, ContactWithJsonStrings } from "../db/tables/contacts";
import { Contact } from "../db/tables/contacts";
import * as databaseUtil from "../db/databaseUtil";
import { DEFAULT_PASSKEY_EXPIRATION_MINUTES } from "../db/tables/settings";
import {
@ -966,31 +966,19 @@ export interface DatabaseExport {
}
/**
* Converts an array of contacts to the standardized database export JSON format.
* Converts an array of contacts to the export JSON format.
* This format is used for data migration and backup purposes.
*
* @param contacts - Array of Contact objects to convert
* @returns DatabaseExport object in the standardized format
*/
export const contactsToExportJson = (contacts: Contact[]): DatabaseExport => {
// Convert each contact to a plain object and ensure all fields are included
const rows = contacts.map((contact) => {
const exContact: ContactWithJsonStrings = R.omit(
["contactMethods"],
contact,
);
exContact.contactMethods = contact.contactMethods
? JSON.stringify(contact.contactMethods, [])
: undefined;
return exContact;
});
return {
data: {
data: [
{
tableName: "contacts",
rows,
rows: contacts,
},
],
},

Loading…
Cancel
Save