17 changed files with 792 additions and 374 deletions
			
			
		@ -1,37 +1,6 @@ | 
				
			|||
export type { | 
				
			|||
  // From common.ts
 | 
				
			|||
  CreateAndSubmitClaimResult, | 
				
			|||
  GenericCredWrapper, | 
				
			|||
  GenericVerifiableCredential, | 
				
			|||
  KeyMeta, | 
				
			|||
  // Exclude types that are also exported from other files
 | 
				
			|||
  // GiveVerifiableCredential,
 | 
				
			|||
  // OfferVerifiableCredential,
 | 
				
			|||
  // RegisterVerifiableCredential,
 | 
				
			|||
  // PlanSummaryRecord,
 | 
				
			|||
  // UserInfo,
 | 
				
			|||
} from "./common"; | 
				
			|||
 | 
				
			|||
export type { | 
				
			|||
  // From claims.ts
 | 
				
			|||
  GiveActionClaim, | 
				
			|||
  OfferClaim, | 
				
			|||
  RegisterActionClaim, | 
				
			|||
} from "./claims"; | 
				
			|||
 | 
				
			|||
export type { | 
				
			|||
  // From records.ts
 | 
				
			|||
  PlanSummaryRecord, | 
				
			|||
} from "./records"; | 
				
			|||
 | 
				
			|||
export type { | 
				
			|||
  // From user.ts
 | 
				
			|||
  UserInfo, | 
				
			|||
  MemberData, | 
				
			|||
} from "./user"; | 
				
			|||
 | 
				
			|||
export * from "./limits"; | 
				
			|||
export * from "./deepLinks"; | 
				
			|||
export * from "./common"; | 
				
			|||
export * from "./claims"; | 
				
			|||
export * from "./claims-result"; | 
				
			|||
export * from "./common"; | 
				
			|||
export * from "./deepLinks"; | 
				
			|||
export * from "./limits"; | 
				
			|||
export * from "./records"; | 
				
			|||
 | 
				
			|||
@ -0,0 +1,297 @@ | 
				
			|||
/** | 
				
			|||
 * @fileoverview Base Database Service for Platform Services | 
				
			|||
 * @author Matthew Raymer | 
				
			|||
 * | 
				
			|||
 * This abstract base class provides common database operations that are | 
				
			|||
 * identical across all platform implementations. It eliminates code | 
				
			|||
 * duplication and ensures consistency in database operations. | 
				
			|||
 * | 
				
			|||
 * Key Features: | 
				
			|||
 * - Common database utility methods | 
				
			|||
 * - Consistent settings management | 
				
			|||
 * - Active identity management | 
				
			|||
 * - Abstract methods for platform-specific database operations | 
				
			|||
 * | 
				
			|||
 * Architecture: | 
				
			|||
 * - Abstract base class with common implementations | 
				
			|||
 * - Platform services extend this class | 
				
			|||
 * - Platform-specific database operations remain abstract | 
				
			|||
 * | 
				
			|||
 * @since 1.1.1-beta | 
				
			|||
 */ | 
				
			|||
 | 
				
			|||
import { logger } from "../../utils/logger"; | 
				
			|||
import { QueryExecResult } from "@/interfaces/database"; | 
				
			|||
 | 
				
			|||
/** | 
				
			|||
 * Abstract base class for platform-specific database services. | 
				
			|||
 * | 
				
			|||
 * This class provides common database operations that are identical | 
				
			|||
 * across all platform implementations (Web, Capacitor, Electron). | 
				
			|||
 * Platform-specific services extend this class and implement the | 
				
			|||
 * abstract database operation methods. | 
				
			|||
 * | 
				
			|||
 * Common Operations: | 
				
			|||
 * - Settings management (update, retrieve, insert) | 
				
			|||
 * - Active identity management | 
				
			|||
 * - Database utility methods | 
				
			|||
 * | 
				
			|||
 * @abstract | 
				
			|||
 * @example | 
				
			|||
 * ```typescript
 | 
				
			|||
 * export class WebPlatformService extends BaseDatabaseService { | 
				
			|||
 *   async dbQuery(sql: string, params?: unknown[]): Promise<QueryExecResult> { | 
				
			|||
 *     // Web-specific implementation
 | 
				
			|||
 *   } | 
				
			|||
 * } | 
				
			|||
 * ``` | 
				
			|||
 */ | 
				
			|||
export abstract class BaseDatabaseService { | 
				
			|||
  /** | 
				
			|||
   * Generate an INSERT statement for a model object. | 
				
			|||
   * | 
				
			|||
   * Creates a parameterized INSERT statement with placeholders for | 
				
			|||
   * all properties in the model object. This ensures safe SQL | 
				
			|||
   * execution and prevents SQL injection. | 
				
			|||
   * | 
				
			|||
   * @param model - Object containing the data to insert | 
				
			|||
   * @param tableName - Name of the target table | 
				
			|||
   * @returns Object containing the SQL statement and parameters | 
				
			|||
   * | 
				
			|||
   * @example | 
				
			|||
   * ```typescript
 | 
				
			|||
   * const { sql, params } = this.generateInsertStatement( | 
				
			|||
   *   { name: 'John', age: 30 }, | 
				
			|||
   *   'users' | 
				
			|||
   * ); | 
				
			|||
   * // sql: "INSERT INTO users (name, age) VALUES (?, ?)"
 | 
				
			|||
   * // params: ['John', 30]
 | 
				
			|||
   * ``` | 
				
			|||
   */ | 
				
			|||
  generateInsertStatement( | 
				
			|||
    model: Record<string, unknown>, | 
				
			|||
    tableName: string, | 
				
			|||
  ): { sql: string; params: unknown[] } { | 
				
			|||
    const keys = Object.keys(model); | 
				
			|||
    const placeholders = keys.map(() => "?").join(", "); | 
				
			|||
    const sql = `INSERT INTO ${tableName} (${keys.join(", ")}) VALUES (${placeholders})`; | 
				
			|||
    const params = keys.map((key) => model[key]); | 
				
			|||
    return { sql, params }; | 
				
			|||
  } | 
				
			|||
 | 
				
			|||
  /** | 
				
			|||
   * Update default settings for the currently active account. | 
				
			|||
   * | 
				
			|||
   * Retrieves the active DID from the active_identity table and updates | 
				
			|||
   * the corresponding settings record. This ensures settings are always | 
				
			|||
   * updated for the correct account. | 
				
			|||
   * | 
				
			|||
   * @param settings - Object containing the settings to update | 
				
			|||
   * @returns Promise that resolves when settings are updated | 
				
			|||
   * | 
				
			|||
   * @throws {Error} If no active DID is found or database operation fails | 
				
			|||
   * | 
				
			|||
   * @example | 
				
			|||
   * ```typescript
 | 
				
			|||
   * await this.updateDefaultSettings({ | 
				
			|||
   *   theme: 'dark', | 
				
			|||
   *   notifications: true | 
				
			|||
   * }); | 
				
			|||
   * ``` | 
				
			|||
   */ | 
				
			|||
  async updateDefaultSettings( | 
				
			|||
    settings: Record<string, unknown>, | 
				
			|||
  ): Promise<void> { | 
				
			|||
    // Get current active DID and update that identity's settings
 | 
				
			|||
    const activeIdentity = await this.getActiveIdentity(); | 
				
			|||
    const activeDid = activeIdentity.activeDid; | 
				
			|||
 | 
				
			|||
    if (!activeDid) { | 
				
			|||
      logger.warn( | 
				
			|||
        "[BaseDatabaseService] No active DID found, cannot update default settings", | 
				
			|||
      ); | 
				
			|||
      return; | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    const keys = Object.keys(settings); | 
				
			|||
    const setClause = keys.map((key) => `${key} = ?`).join(", "); | 
				
			|||
    const sql = `UPDATE settings SET ${setClause} WHERE accountDid = ?`; | 
				
			|||
    const params = [...keys.map((key) => settings[key]), activeDid]; | 
				
			|||
    await this.dbExec(sql, params); | 
				
			|||
  } | 
				
			|||
 | 
				
			|||
  /** | 
				
			|||
   * Update the active DID in the active_identity table. | 
				
			|||
   * | 
				
			|||
   * Sets the active DID and updates the lastUpdated timestamp. | 
				
			|||
   * This is used when switching between different accounts/identities. | 
				
			|||
   * | 
				
			|||
   * @param did - The DID to set as active | 
				
			|||
   * @returns Promise that resolves when the update is complete | 
				
			|||
   * | 
				
			|||
   * @example | 
				
			|||
   * ```typescript
 | 
				
			|||
   * await this.updateActiveDid('did:example:123'); | 
				
			|||
   * ``` | 
				
			|||
   */ | 
				
			|||
  async updateActiveDid(did: string): Promise<void> { | 
				
			|||
    await this.dbExec( | 
				
			|||
      "UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1", | 
				
			|||
      [did], | 
				
			|||
    ); | 
				
			|||
  } | 
				
			|||
 | 
				
			|||
  /** | 
				
			|||
   * Get the currently active DID from the active_identity table. | 
				
			|||
   * | 
				
			|||
   * Retrieves the active DID that represents the currently selected | 
				
			|||
   * account/identity. This is used throughout the application to | 
				
			|||
   * ensure operations are performed on the correct account. | 
				
			|||
   * | 
				
			|||
   * @returns Promise resolving to object containing the active DID | 
				
			|||
   * | 
				
			|||
   * @example | 
				
			|||
   * ```typescript
 | 
				
			|||
   * const { activeDid } = await this.getActiveIdentity(); | 
				
			|||
   * console.log('Current active DID:', activeDid); | 
				
			|||
   * ``` | 
				
			|||
   */ | 
				
			|||
  async getActiveIdentity(): Promise<{ activeDid: string }> { | 
				
			|||
    const result = (await this.dbQuery( | 
				
			|||
      "SELECT activeDid FROM active_identity WHERE id = 1", | 
				
			|||
    )) as QueryExecResult; | 
				
			|||
    return { | 
				
			|||
      activeDid: (result?.values?.[0]?.[0] as string) || "", | 
				
			|||
    }; | 
				
			|||
  } | 
				
			|||
 | 
				
			|||
  /** | 
				
			|||
   * Insert a new DID into the settings table with default values. | 
				
			|||
   * | 
				
			|||
   * Creates a new settings record for a DID with default configuration | 
				
			|||
   * values. Uses INSERT OR REPLACE to handle cases where settings | 
				
			|||
   * already exist for the DID. | 
				
			|||
   * | 
				
			|||
   * @param did - The DID to create settings for | 
				
			|||
   * @returns Promise that resolves when settings are created | 
				
			|||
   * | 
				
			|||
   * @example | 
				
			|||
   * ```typescript
 | 
				
			|||
   * await this.insertNewDidIntoSettings('did:example:123'); | 
				
			|||
   * ``` | 
				
			|||
   */ | 
				
			|||
  async insertNewDidIntoSettings(did: string): Promise<void> { | 
				
			|||
    // Import constants dynamically to avoid circular dependencies
 | 
				
			|||
    const { DEFAULT_ENDORSER_API_SERVER, DEFAULT_PARTNER_API_SERVER } = | 
				
			|||
      await import("@/constants/app"); | 
				
			|||
 | 
				
			|||
    // Use INSERT OR REPLACE to handle case where settings already exist for this DID
 | 
				
			|||
    // This prevents duplicate accountDid entries and ensures data integrity
 | 
				
			|||
    await this.dbExec( | 
				
			|||
      "INSERT OR REPLACE INTO settings (accountDid, finishedOnboarding, apiServer, partnerApiServer) VALUES (?, ?, ?, ?)", | 
				
			|||
      [did, false, DEFAULT_ENDORSER_API_SERVER, DEFAULT_PARTNER_API_SERVER], | 
				
			|||
    ); | 
				
			|||
  } | 
				
			|||
 | 
				
			|||
  /** | 
				
			|||
   * Update settings for a specific DID. | 
				
			|||
   * | 
				
			|||
   * Updates settings for a particular DID rather than the active one. | 
				
			|||
   * This is useful for bulk operations or when managing multiple accounts. | 
				
			|||
   * | 
				
			|||
   * @param did - The DID to update settings for | 
				
			|||
   * @param settings - Object containing the settings to update | 
				
			|||
   * @returns Promise that resolves when settings are updated | 
				
			|||
   * | 
				
			|||
   * @example | 
				
			|||
   * ```typescript
 | 
				
			|||
   * await this.updateDidSpecificSettings('did:example:123', { | 
				
			|||
   *   theme: 'light', | 
				
			|||
   *   notifications: false | 
				
			|||
   * }); | 
				
			|||
   * ``` | 
				
			|||
   */ | 
				
			|||
  async updateDidSpecificSettings( | 
				
			|||
    did: string, | 
				
			|||
    settings: Record<string, unknown>, | 
				
			|||
  ): Promise<void> { | 
				
			|||
    const keys = Object.keys(settings); | 
				
			|||
    const setClause = keys.map((key) => `${key} = ?`).join(", "); | 
				
			|||
    const sql = `UPDATE settings SET ${setClause} WHERE accountDid = ?`; | 
				
			|||
    const params = [...keys.map((key) => settings[key]), did]; | 
				
			|||
    await this.dbExec(sql, params); | 
				
			|||
  } | 
				
			|||
 | 
				
			|||
  /** | 
				
			|||
   * Retrieve settings for the currently active account. | 
				
			|||
   * | 
				
			|||
   * Gets the active DID and retrieves all settings for that account. | 
				
			|||
   * Excludes the 'id' column from the returned settings object. | 
				
			|||
   * | 
				
			|||
   * @returns Promise resolving to settings object or null if no active DID | 
				
			|||
   * | 
				
			|||
   * @example | 
				
			|||
   * ```typescript
 | 
				
			|||
   * const settings = await this.retrieveSettingsForActiveAccount(); | 
				
			|||
   * if (settings) { | 
				
			|||
   *   console.log('Theme:', settings.theme); | 
				
			|||
   *   console.log('Notifications:', settings.notifications); | 
				
			|||
   * } | 
				
			|||
   * ``` | 
				
			|||
   */ | 
				
			|||
  async retrieveSettingsForActiveAccount(): Promise<Record< | 
				
			|||
    string, | 
				
			|||
    unknown | 
				
			|||
  > | null> { | 
				
			|||
    // Get current active DID from active_identity table
 | 
				
			|||
    const activeIdentity = await this.getActiveIdentity(); | 
				
			|||
    const activeDid = activeIdentity.activeDid; | 
				
			|||
 | 
				
			|||
    if (!activeDid) { | 
				
			|||
      return null; | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    const result = (await this.dbQuery( | 
				
			|||
      "SELECT * FROM settings WHERE accountDid = ?", | 
				
			|||
      [activeDid], | 
				
			|||
    )) as QueryExecResult; | 
				
			|||
    if (result?.values?.[0]) { | 
				
			|||
      // Convert the row to an object
 | 
				
			|||
      const row = result.values[0]; | 
				
			|||
      const columns = result.columns || []; | 
				
			|||
      const settings: Record<string, unknown> = {}; | 
				
			|||
 | 
				
			|||
      columns.forEach((column: string, index: number) => { | 
				
			|||
        if (column !== "id") { | 
				
			|||
          // Exclude the id column
 | 
				
			|||
          settings[column] = row[index]; | 
				
			|||
        } | 
				
			|||
      }); | 
				
			|||
 | 
				
			|||
      return settings; | 
				
			|||
    } | 
				
			|||
    return null; | 
				
			|||
  } | 
				
			|||
 | 
				
			|||
  // Abstract methods that must be implemented by platform-specific services
 | 
				
			|||
 | 
				
			|||
  /** | 
				
			|||
   * Execute a database query (SELECT operations). | 
				
			|||
   * | 
				
			|||
   * @abstract | 
				
			|||
   * @param sql - SQL query string | 
				
			|||
   * @param params - Optional parameters for prepared statements | 
				
			|||
   * @returns Promise resolving to query results | 
				
			|||
   */ | 
				
			|||
  abstract dbQuery(sql: string, params?: unknown[]): Promise<unknown>; | 
				
			|||
 | 
				
			|||
  /** | 
				
			|||
   * Execute a database statement (INSERT, UPDATE, DELETE operations). | 
				
			|||
   * | 
				
			|||
   * @abstract | 
				
			|||
   * @param sql - SQL statement string | 
				
			|||
   * @param params - Optional parameters for prepared statements | 
				
			|||
   * @returns Promise resolving to execution results | 
				
			|||
   */ | 
				
			|||
  abstract dbExec(sql: string, params?: unknown[]): Promise<unknown>; | 
				
			|||
} | 
				
			|||
					Loading…
					
					
				
		Reference in new issue