forked from jsnbuchanan/crowd-funder-for-time-pwa
fix problem with repeated bad stringifies of contactMethods on contact export/import
This commit is contained in:
@@ -60,9 +60,11 @@ backup and database export, with platform-specific download instructions. * *
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Prop, Vue } from "vue-facing-decorator";
|
import { Component, Prop, Vue } from "vue-facing-decorator";
|
||||||
|
import * as R from "ramda";
|
||||||
|
|
||||||
import { AppString, NotificationIface } from "../constants/app";
|
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 * as databaseUtil from "../db/databaseUtil";
|
||||||
|
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
@@ -72,6 +74,7 @@ import {
|
|||||||
PlatformCapabilities,
|
PlatformCapabilities,
|
||||||
} from "../services/PlatformService";
|
} from "../services/PlatformService";
|
||||||
import { contactsToExportJson } from "../libs/util";
|
import { contactsToExportJson } from "../libs/util";
|
||||||
|
import { parseJsonField } from "../db/databaseUtil";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @vue-component
|
* @vue-component
|
||||||
@@ -133,13 +136,13 @@ export default class DataExportSection extends Vue {
|
|||||||
*/
|
*/
|
||||||
public async exportDatabase() {
|
public async exportDatabase() {
|
||||||
try {
|
try {
|
||||||
let allContacts: Contact[] = [];
|
let allDbContacts: ContactMaybeWithJsonStrings[] = [];
|
||||||
const platformService = PlatformServiceFactory.getInstance();
|
const platformService = PlatformServiceFactory.getInstance();
|
||||||
const result = await platformService.dbQuery(`SELECT * FROM contacts`);
|
const result = await platformService.dbQuery(`SELECT * FROM contacts`);
|
||||||
if (result) {
|
if (result) {
|
||||||
allContacts = databaseUtil.mapQueryResultToValues(
|
allDbContacts = databaseUtil.mapQueryResultToValues(
|
||||||
result,
|
result,
|
||||||
) as unknown as Contact[];
|
) as unknown as ContactMaybeWithJsonStrings[];
|
||||||
}
|
}
|
||||||
// if (USE_DEXIE_DB) {
|
// if (USE_DEXIE_DB) {
|
||||||
// await db.open();
|
// await db.open();
|
||||||
@@ -147,6 +150,19 @@ export default class DataExportSection extends Vue {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// Convert contacts to export format
|
// 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 exportData = contactsToExportJson(allContacts);
|
||||||
const jsonStr = JSON.stringify(exportData, null, 2);
|
const jsonStr = JSON.stringify(exportData, null, 2);
|
||||||
const blob = new Blob([jsonStr], { type: "application/json" });
|
const blob = new Blob([jsonStr], { type: "application/json" });
|
||||||
|
|||||||
@@ -30,6 +30,17 @@ export type ContactWithJsonStrings = Omit<Contact, "contactMethods"> & {
|
|||||||
contactMethods?: string;
|
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 = {
|
export const ContactSchema = {
|
||||||
contacts: "&did, name", // no need to key by other things
|
contacts: "&did, name", // no need to key by other things
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import {
|
|||||||
updateDefaultSettings,
|
updateDefaultSettings,
|
||||||
} from "../db/index";
|
} from "../db/index";
|
||||||
import { Account, AccountEncrypted } from "../db/tables/accounts";
|
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 * as databaseUtil from "../db/databaseUtil";
|
||||||
import { DEFAULT_PASSKEY_EXPIRATION_MINUTES } from "../db/tables/settings";
|
import { DEFAULT_PASSKEY_EXPIRATION_MINUTES } from "../db/tables/settings";
|
||||||
import {
|
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.
|
* This format is used for data migration and backup purposes.
|
||||||
*
|
*
|
||||||
* @param contacts - Array of Contact objects to convert
|
* @param contacts - Array of Contact objects to convert
|
||||||
* @returns DatabaseExport object in the standardized format
|
* @returns DatabaseExport object in the standardized format
|
||||||
*/
|
*/
|
||||||
export const contactsToExportJson = (contacts: Contact[]): DatabaseExport => {
|
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 {
|
return {
|
||||||
data: {
|
data: {
|
||||||
data: [
|
data: [
|
||||||
{
|
{
|
||||||
tableName: "contacts",
|
tableName: "contacts",
|
||||||
rows,
|
rows: contacts,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user