]> PHS Git Server - phs-api.git/commitdiff
Cleaning up the base model file.
authorcharleswrayjr <charleswrayjr@gmail.com>
Fri, 12 Sep 2025 01:19:07 +0000 (20:19 -0500)
committercharleswrayjr <charleswrayjr@gmail.com>
Fri, 12 Sep 2025 01:19:07 +0000 (20:19 -0500)
.idea/inspectionProfiles/Project_Default.xml
src/models/model.js

index 1b359020cfe64e197777c74424203b457171792b..e02b944891f46c1fa533ce3ef6905945a96b1cc3 100644 (file)
@@ -3,7 +3,7 @@
     <option name="myName" value="Project Default" />
     <inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
       <Languages>
-        <language minSize="48" name="JavaScript" />
+        <language minSize="61" name="JavaScript" />
       </Languages>
     </inspection_tool>
   </profile>
index 3a91a6c5d05d0a94e7e56ee063dbc8648d94f27d..83f2fad430fcc1c810560d0971f4c4c03f1970b3 100755 (executable)
@@ -1,18 +1,22 @@
-const HttpError = require('http-errors');
+/**
+ * @file Base model class for database operations
+ */
+
+const HttpError = require( 'http-errors' );
+
 
 /**
  * Custom error for not found records
  * @extends HttpError
- * @property {string} name - Error name
  */
 class NotFoundError extends HttpError {
   /**
    * @param {string} message - Error message
    */
-  constructor(message) {
+  constructor( message ) {
     super( 404, message );
     this.name = 'NotFoundError';
-  }
+  };
 }
 
 /**
@@ -23,13 +27,12 @@ class ValidationError extends HttpError {
   /**
    * @param {string} message - Error message
    */
-  constructor(message) {
-    super(400, message);
+  constructor( message ) {
+    super( 400, message );
     this.name = 'ValidationError';
-  }
+  };
 }
 
-// noinspection DuplicatedCode
 /**
  * Base model class for database operations
  */
@@ -38,8 +41,10 @@ class Model {
    * Create a model instance
    * @param {Object} [props] - Properties to initialize the model
    */
-  constructor(props) {
-    props && Object.keys(props).forEach(c => { this[c] = props[c]; });
+  constructor( props ) {
+    props && Object.keys( props ).forEach( c => {
+      this[c] = props[c];
+    } );
     /** @type {string} Database table name */
     this.table = '';
     /** @type {string[]} Allowed columns for queries */
@@ -55,7 +60,7 @@ class Model {
     /** @type {string|undefined} Default ORDER BY clause */
     this.default_order_by = undefined;
     /** @type {Function} Function to instantiate a model */
-    this.instance = _props => new Model(_props);
+    this.instance = _props => new Model( _props );
     /** @type {string|undefined} GROUP BY clause */
     this.group_by = undefined;
   }
@@ -66,10 +71,10 @@ class Model {
    * @param {string} prepend - Table alias
    * @returns {string} WHERE clause
    */
-  where_clause(keys, prepend) {
+  where_clause( keys, prepend ) {
     if (!keys?.length) return '';
-    return `WHERE ${keys.map((k, index) => `${prepend}.${k} = $${index + 1}`).join(' AND ')}`;
-  }
+    return `WHERE ${ keys.map( ( k, index ) => `${ prepend }.${ k } = $${ index + 1 }` ).join( ' AND ' ) }`;
+  };
 
   /**
    * Filter WHERE object to valid columns
@@ -77,19 +82,19 @@ class Model {
    * @param {string[]} default_columns - Allowed columns
    * @returns {{keys: string[], values: any[]}} Keys and values for the query
    */
-  build_where(where, default_columns) {
+  build_where( where, default_columns ) {
     const keys = [];
     const values = [];
     if (where) {
-      Object.keys(where).forEach(k => {
-        if (default_columns.includes(k)) {
-          keys.push(k);
-          values.push(where[k]);
+      Object.keys( where ).forEach( k => {
+        if (default_columns.includes( k )) {
+          keys.push( k );
+          values.push( where[k] );
         }
-      });
+      } );
     }
     return { keys, values };
-  }
+  };
 
   /**
    * Find one record
@@ -97,45 +102,44 @@ class Model {
    * @param {string[]} [excludes] - Fields to exclude from result
    * @returns {Promise<Object|null>} Found record or null
    */
-  async find_one(where, excludes = []) {
-    const { keys, values } = this.build_where(where, this.default_columns);
+  async find_one( where, excludes = [] ) {
+    const { keys, values } = this.build_where( where, this.default_columns );
     const result = await phsdb.query(
-      `${this.base_query} ${this.where_clause(keys, this.prepend)} ${this.group_by ? this.group_by : ''}`,
+      `${ this.base_query } ${ this.where_clause( keys, this.prepend ) } ${ this.group_by ? this.group_by : '' }`,
       values,
-      { plain: true }
+      { plain:true }
     );
     if (!result) return null;
-    const found = this.instance(result);
-    excludes?.forEach(e => delete found[e]);
+    const found = this.instance( result );
+    excludes?.forEach( e => delete found[e] );
     return found;
-  }
+  };
 
-  // noinspection JSUnusedGlobalSymbols
   /**
    * Find one record (raw data)
    * @param {Object} where - Conditions for the WHERE clause
    * @param {string[]} [excludes] - Fields to exclude from result
    * @returns {Promise<Object|null>} Found record or null
    */
-  async find_one_simple(where, excludes = []) {
-    const { keys, values } = this.build_where(where, this.default_columns);
+  async find_one_simple( where, excludes = [] ) {
+    const { keys, values } = this.build_where( where, this.default_columns );
     const result = await phsdb.query(
-      `${this.base_query} ${this.where_clause(keys, this.prepend)} ${this.group_by ? this.group_by : ''}`,
+      `${ this.base_query } ${ this.where_clause( keys, this.prepend ) } ${ this.group_by ? this.group_by : '' }`,
       values,
-      { plain: true }
+      { plain:true }
     );
     if (!result) return null;
-    excludes?.forEach(e => delete result[e]);
+    excludes?.forEach( e => delete result[e] );
     return result;
-  }
+  };
 
   /**
    * Get base list query
    * @returns {string} Base list query
    */
   get_base_list_query() {
-    return `${this.base_list_query}`;
-  }
+    return `${ this.base_list_query }`;
+  };
 
   /**
    * Find many records
@@ -146,29 +150,28 @@ class Model {
    * @param {number} [offset=0] - Number of records to skip
    * @returns {Promise<Object[]>} Array of records
    */
-  async find_many(where, excludes = [], order_by = null, limit = 100, offset = 0) {
-    const { keys, values } = this.build_where(where, this.default_columns);
-    values.push(limit, offset);
+  async find_many( where, excludes = [], order_by = null, limit = 100, offset = 0 ) {
+    const { keys, values } = this.build_where( where, this.default_columns );
+    values.push( limit, offset );
     const results = await phsdb.query(
       `
-        ${this.get_base_list_query()}
-        ${this.where_clause(keys, this.prepend)}
-        ${this.group_by ? this.group_by : ''}
-        ${order_by ? `ORDER BY ${order_by.name} ${order_by.direction ?? 'asc'}` : this.default_order_by ?? ''}
-        LIMIT $${values.length - 1} OFFSET $${values.length}
+        ${ this.get_base_list_query() }
+        ${ this.where_clause( keys, this.prepend ) }
+        ${ this.group_by ? this.group_by : '' }
+        ${ order_by ? `ORDER BY ${ order_by.name } ${ order_by.direction ?? 'asc' }` : this.default_order_by ?? '' }
+        LIMIT $${ values.length - 1 } OFFSET $${ values.length }
       `,
       values
     );
     const res = [];
     for (const result of results) {
-      const found = this.instance(result);
-      excludes?.forEach(e => delete found[e]);
-      res.push(found);
+      const found = this.instance( result );
+      excludes?.forEach( e => delete found[e] );
+      res.push( found );
     }
     return res;
-  }
+  };
 
-  // noinspection JSUnusedGlobalSymbols
   /**
    * Find many records (raw data)
    * @param {Object} [where] - Conditions for the WHERE clause
@@ -178,28 +181,28 @@ class Model {
    * @param {number} [offset=0] - Number of records to skip
    * @returns {Promise<Object[]>} Array of records
    */
-  async find_many_lite(where, excludes = [], order_by = null, limit = 100, offset = 0) {
-    const { keys, values } = this.build_where(where, this.default_columns);
-    values.push(limit, offset);
+  async find_many_lite( where, excludes = [], order_by = null, limit = 100, offset = 0 ) {
+    const { keys, values } = this.build_where( where, this.default_columns );
+    values.push( limit, offset );
     const results = await phsdb.query(
       `
-        ${this.get_base_list_query()}
-        ${this.where_clause(keys, this.prepend)}
-        ${this.group_by ? this.group_by : ''}
-        ${order_by ? `ORDER BY ${order_by.name} ${order_by.direction ?? 'asc'}` : this.default_order_by ?? ''}
-        LIMIT $${values.length - 1} OFFSET $${values.length}
+        ${ this.get_base_list_query() }
+        ${ this.where_clause( keys, this.prepend ) }
+        ${ this.group_by ? this.group_by : '' }
+        ${ order_by ? `ORDER BY ${ order_by.name } ${ order_by.direction ?? 'asc' }` : this.default_order_by ?? '' }
+        LIMIT $${ values.length - 1 } OFFSET $${ values.length }
       `,
       values
     );
     const res = [];
     for (const result of results) {
       const data_values = {};
-      Object.keys(result).forEach(k => data_values[k] = result[k]);
-      excludes?.forEach(e => delete data_values[e]);
-      res.push(data_values);
+      Object.keys( result ).forEach( k => data_values[k] = result[k] );
+      excludes?.forEach( e => delete data_values[e] );
+      res.push( data_values );
     }
     return res;
-  }
+  };
 
   /**
    * Update the record
@@ -209,16 +212,18 @@ class Model {
    * @throws {ValidationError} If no valid fields are provided or ID is missing
    * @throws {NotFoundError} If record is not found
    */
-  async update(params, identifier = 'id') {
-    if (!this[identifier]) throw new ValidationError('Record ID is required for update');
-    const { updates, values, position } = this.create_update_fields(params);
-    values.push(this[identifier]);
-    const query_str = `UPDATE ${this.table} SET ${updates} WHERE ${identifier}=$${position} RETURNING *;`;
-    logger.debug(`Update query: ${query_str}`);
-    const result = await phsdb.query(query_str, values, { plain: true });
-    if (!result) throw new NotFoundError(`Record not found in ${this.table}`);
-    return this.instance(result);
-  }
+  async update( params, identifier = 'id' ) {
+    if (!this[identifier]) throw new ValidationError( 'Record ID is required for update' );
+    const { updates, values, position } = this.create_update_fields( params );
+    values.push( this[identifier] );
+    const query_str = `UPDATE ${ this.table }
+                       SET ${ updates }
+                       WHERE ${ identifier } = $${ position } RETURNING *;`;
+    logger.debug( `Update query: ${ query_str }` );
+    const result = await phsdb.query( query_str, values, { plain:true } );
+    if (!result) throw new NotFoundError( `Record not found in ${ this.table }` );
+    return this.instance( result );
+  };
 
   /**
    * Create update fields string and values
@@ -226,52 +231,60 @@ class Model {
    * @returns {{updates: string, values: any[], position: number}} Update query components
    * @throws {ValidationError} If no valid fields are provided
    */
-  create_update_fields(params) {
+  create_update_fields( params ) {
     let position = 1;
     const values = [];
     let updates = '';
     for (const column of this.default_columns) {
-      if (params.hasOwnProperty(column) && !this.update_exclude_columns.includes(column)) {
+      if (params.hasOwnProperty( column ) && !this.update_exclude_columns.includes( column )) {
         const value = params[column];
         this[column] = value;
-        values.push(value);
-        updates += (updates ? ',' : '') + `${column}=$${position++}`;
+        values.push( value );
+        updates += (updates ? ',' : '') + `${ column }=$${ position++ }`;
       }
     }
-    if (!updates) throw new ValidationError('No valid fields provided for update');
+    if (!updates) throw new ValidationError( 'No valid fields provided for update' );
     return { updates, values, position };
-  }
+  };
 
   /**
    * Serialize to JSON, excluding internal properties
    * @returns {Object<any>} Serialized object
    */
   toJSON() {
-    const { default_columns, prepend, base_query, base_list_query, table, update_exclude_columns, instance, ...rest } = this;
+    const {
+      default_columns,
+      prepend,
+      base_query,
+      base_list_query,
+      table,
+      update_exclude_columns,
+      instance,
+      ...rest
+    } = this;
     return rest;
-  }
+  };
 
-  // noinspection JSUnusedGlobalSymbols
   /**
    * Execute transaction
    * @param {Function} callback - Transaction callback
    * @returns {Promise<any>} Transaction result
    * @throws {Error} If transaction fails
    */
-  async with_transaction(callback) {
-    const client = await require('../phsdb').get_client();
+  async with_transaction( callback ) {
+    const client = await require( '../phsdb' ).get_client();
     try {
-      await client.query('BEGIN');
-      const result = await callback(client);
-      await client.query('COMMIT');
+      await client.query( 'BEGIN' );
+      const result = await callback( client );
+      await client.query( 'COMMIT' );
       return result;
     } catch (error) {
-      await client.query('ROLLBACK');
+      await client.query( 'ROLLBACK' );
       throw error;
     } finally {
-      await require('../phsdb').client_release(client);
+      await require( '../phsdb' ).client_release( client );
     }
-  }
+  };
 }
 
 module.exports = { Model, NotFoundError, ValidationError };
\ No newline at end of file