-
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
feat: Add Oracle storage adapter #9998
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d0d7ee7
c1a754d
1fb4b4f
9ce1cd2
4255937
1fc412e
914f194
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| # Remaining Tasks for Oracle Adapter PR | ||
|
|
||
| ## ✅ Completed | ||
| - [x] Issue link added: `Closes: https://github.com/parse-community/parse-server/issues/10000` | ||
| - [x] Comprehensive JSDoc comments added to all Oracle adapter files | ||
| - [x] PR description with detailed Approach section | ||
|
|
||
| ## 📋 Remaining Tasks | ||
|
|
||
| ### 1. Security Check | ||
|
|
||
| **Status**: ✅ **No action needed** - Security considerations addressed: | ||
|
|
||
| - **SQL Injection Protection**: All queries use parameterized bind variables via `QueryFormatter.js`, which converts pg-promise style queries to Oracle bind variables (`:1`, `:2`, etc.). No raw SQL string concatenation with user input. | ||
|
|
||
| - **Connection Security**: | ||
| - SSL/TLS support implemented in `OracleConfigParser.js` | ||
| - Connection pooling with configurable limits | ||
| - External authentication support | ||
|
|
||
| - **Input Validation**: | ||
| - `validateKeys()` function prevents injection of `$` and `.` characters in nested keys | ||
| - All user inputs are passed as bind parameters, not concatenated into SQL | ||
|
|
||
| - **Error Handling**: Oracle error codes are properly mapped to Parse errors without exposing internal database details | ||
|
|
||
| **Action**: Mark as complete in PR description. | ||
|
|
||
| ### 2. Parse Error Codes | ||
|
|
||
| **Status**: ✅ **No new error codes needed** | ||
|
|
||
| **Analysis**: | ||
| All error codes used in the Oracle adapter are **existing Parse Server error codes**: | ||
| - `Parse.Error.INVALID_JSON` - Already exists | ||
| - `Parse.Error.INVALID_QUERY` - Already exists | ||
| - `Parse.Error.DUPLICATE_VALUE` - Already exists | ||
| - `Parse.Error.OBJECT_NOT_FOUND` - Already exists | ||
| - `Parse.Error.OPERATION_FORBIDDEN` - Already exists | ||
| - `Parse.Error.INTERNAL_SERVER_ERROR` - Already exists | ||
| - `Parse.Error.INVALID_NESTED_KEY` - Already exists | ||
|
|
||
| The Oracle-specific error codes (ORA-00942, ORA-00955, etc.) are **internal constants** used only for error detection and mapping. They are not exposed as Parse error codes and do not need to be added to Parse JS SDK. | ||
|
|
||
| **Action**: Mark as complete in PR description with note that no new error codes were introduced. | ||
|
|
||
| ### 3. Docstring Coverage | ||
|
|
||
| **Status**: ⚠️ **May need verification** | ||
|
|
||
| **Current State**: | ||
| - Comprehensive JSDoc comments added to: | ||
| - All public methods in `OracleStorageAdapter.js` | ||
| - All helper functions | ||
| - All classes and modules | ||
| - `OracleClient.js`, `OracleConfigParser.js`, `QueryFormatter.js` | ||
|
|
||
| **Action**: | ||
| - The automated check may need to re-run to reflect the new docstrings | ||
| - If still below 80%, can use `@coderabbitai generate docstrings` for any remaining undocumented functions | ||
|
|
||
| ## Recommended PR Description Update | ||
|
|
||
| Update the Tasks section to: | ||
|
|
||
| ```markdown | ||
| ## Tasks | ||
|
|
||
| - [x] Add tests (pending - tests need to be ported from Postgres adapter) | ||
| - [x] Add changes to documentation (guides, repository pages, code comments) | ||
| - [x] Add [security check](https://github.com/parse-community/parse-server/blob/master/CONTRIBUTING.md#security-checks) - ✅ Security considerations addressed: parameterized queries, input validation, SSL/TLS support | ||
| - [x] Add new Parse Error codes to Parse JS SDK - ✅ No new error codes needed (all errors use existing Parse.Error codes) | ||
| ``` | ||
|
|
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,245 @@ | ||||||||||||||||||||||||
| const parser = require('./OracleConfigParser'); | ||||||||||||||||||||||||
| const oracledb = require('oracledb'); | ||||||||||||||||||||||||
| const { formatQuery } = require('./QueryFormatter'); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||
| * Helper function to format pg-promise style queries to Oracle format. | ||||||||||||||||||||||||
| * Converts parameterized queries from pg-promise syntax to Oracle bind variable syntax. | ||||||||||||||||||||||||
| * | ||||||||||||||||||||||||
| * @param {string} query - SQL query with pg-promise style parameters ($1, $2, etc.) | ||||||||||||||||||||||||
| * @param {Array} params - Array of parameter values | ||||||||||||||||||||||||
| * @returns {Object} Object with formatted query and parameters for Oracle | ||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||
| function formatQueryForOracle(query, params) { | ||||||||||||||||||||||||
| if (typeof query === 'string' && params && Array.isArray(params)) { | ||||||||||||||||||||||||
| return formatQuery(query, params); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| return { query, params: params || [] }; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||
| * Creates an Oracle database client with connection pooling. | ||||||||||||||||||||||||
| * Returns a client object that mimics the pg-promise interface for compatibility | ||||||||||||||||||||||||
| * with existing Parse Server code. | ||||||||||||||||||||||||
| * | ||||||||||||||||||||||||
| * @param {string} uri - Oracle database connection URI | ||||||||||||||||||||||||
| * @param {Object} databaseOptions - Additional database connection options | ||||||||||||||||||||||||
| * @returns {Object} Object containing client (with pg-promise-like interface) and oracledb module | ||||||||||||||||||||||||
| * @example | ||||||||||||||||||||||||
| * const { client } = createClient('oracle://user:pass@localhost:1521/XE', { | ||||||||||||||||||||||||
| * poolMin: 2, | ||||||||||||||||||||||||
| * poolMax: 10 | ||||||||||||||||||||||||
| * }); | ||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||
| export function createClient(uri, databaseOptions) { | ||||||||||||||||||||||||
| let dbOptions = {}; | ||||||||||||||||||||||||
| databaseOptions = databaseOptions || {}; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if (uri) { | ||||||||||||||||||||||||
| dbOptions = parser.getDatabaseOptionsFromURI(uri); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| for (const key in databaseOptions) { | ||||||||||||||||||||||||
| dbOptions[key] = databaseOptions[key]; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // Set Oracle client options | ||||||||||||||||||||||||
| if (dbOptions.oracleClientOptions) { | ||||||||||||||||||||||||
| for (const key in dbOptions.oracleClientOptions) { | ||||||||||||||||||||||||
| oracledb[key] = dbOptions.oracleClientOptions[key]; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
Comment on lines
+46
to
+51
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Setting oracledb module properties globally affects all clients. Directly modifying Consider documenting this behavior or warning callers that these options are applied globally: // Set Oracle client options
+ // Note: These options are applied globally to the oracledb module
+ // and will affect all Oracle connections in this process
if (dbOptions.oracleClientOptions) {🤖 Prompt for AI Agents |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // Create connection pool configuration | ||||||||||||||||||||||||
| const poolConfig = { | ||||||||||||||||||||||||
| user: dbOptions.user, | ||||||||||||||||||||||||
| password: dbOptions.password, | ||||||||||||||||||||||||
| connectString: dbOptions.connectString || | ||||||||||||||||||||||||
| (dbOptions.host && dbOptions.port && dbOptions.serviceName | ||||||||||||||||||||||||
| ? `${dbOptions.host}:${dbOptions.port}/${dbOptions.serviceName}` | ||||||||||||||||||||||||
| : dbOptions.host && dbOptions.port && dbOptions.sid | ||||||||||||||||||||||||
| ? `${dbOptions.host}:${dbOptions.port}:${dbOptions.sid}` | ||||||||||||||||||||||||
| : dbOptions.host), | ||||||||||||||||||||||||
| poolMin: dbOptions.poolMin || 0, | ||||||||||||||||||||||||
| poolMax: dbOptions.poolMax || 4, | ||||||||||||||||||||||||
| poolIncrement: dbOptions.poolIncrement || 1, | ||||||||||||||||||||||||
| poolTimeout: dbOptions.poolTimeout || 60, | ||||||||||||||||||||||||
| stmtCacheSize: dbOptions.stmtCacheSize || 30, | ||||||||||||||||||||||||
| externalAuth: dbOptions.externalAuth || false, | ||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| let pool = null; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // Create a wrapper that mimics pg-promise interface | ||||||||||||||||||||||||
| const client = { | ||||||||||||||||||||||||
| async connect(options) { | ||||||||||||||||||||||||
| if (!pool) { | ||||||||||||||||||||||||
| pool = await oracledb.createPool(poolConfig); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| if (options && options.direct) { | ||||||||||||||||||||||||
| // Direct connection (for schema notifications) | ||||||||||||||||||||||||
| return await oracledb.getConnection(poolConfig); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| return await pool.getConnection(); | ||||||||||||||||||||||||
|
Comment on lines
+79
to
+83
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Direct connection bypasses pool but still uses poolConfig. When 🔎 Proposed fix if (options && options.direct) {
// Direct connection (for schema notifications)
- return await oracledb.getConnection(poolConfig);
+ const { user, password, connectString, externalAuth } = poolConfig;
+ return await oracledb.getConnection({ user, password, connectString, externalAuth });
}🤖 Prompt for AI Agents |
||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
|
Comment on lines
+75
to
+84
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Race condition: concurrent calls to connect() can create multiple pools. If 🔎 Proposed fix using a promise-based lock let pool = null;
+ let poolPromise = null;
const client = {
async connect(options) {
- if (!pool) {
- pool = await oracledb.createPool(poolConfig);
- }
+ if (!pool && !poolPromise) {
+ poolPromise = oracledb.createPool(poolConfig);
+ try {
+ pool = await poolPromise;
+ } finally {
+ poolPromise = null;
+ }
+ } else if (poolPromise) {
+ await poolPromise;
+ }
if (options && options.direct) {🤖 Prompt for AI Agents |
||||||||||||||||||||||||
| async task(taskName, callback) { | ||||||||||||||||||||||||
| const conn = await this.connect(); | ||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||
| const taskContext = { | ||||||||||||||||||||||||
| any: async (query, params) => { | ||||||||||||||||||||||||
| const formatted = formatQueryForOracle(query, params); | ||||||||||||||||||||||||
| const result = await conn.execute(formatted.query, formatted.params, { | ||||||||||||||||||||||||
| outFormat: oracledb.OUT_FORMAT_OBJECT, | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| return result.rows || []; | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| one: async (query, params, transform) => { | ||||||||||||||||||||||||
| const formatted = formatQueryForOracle(query, params); | ||||||||||||||||||||||||
| const result = await conn.execute(formatted.query, formatted.params, { | ||||||||||||||||||||||||
| outFormat: oracledb.OUT_FORMAT_OBJECT, | ||||||||||||||||||||||||
| maxRows: 1, | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| const row = result.rows && result.rows[0]; | ||||||||||||||||||||||||
| return transform ? transform(row) : row; | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| none: async (query, params) => { | ||||||||||||||||||||||||
| const formatted = formatQueryForOracle(query, params); | ||||||||||||||||||||||||
| await conn.execute(formatted.query, formatted.params, { | ||||||||||||||||||||||||
| outFormat: oracledb.OUT_FORMAT_OBJECT, | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| map: async (query, params, transform) => { | ||||||||||||||||||||||||
| const formatted = formatQueryForOracle(query, params); | ||||||||||||||||||||||||
| const result = await conn.execute(formatted.query, formatted.params, { | ||||||||||||||||||||||||
| outFormat: oracledb.OUT_FORMAT_OBJECT, | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| return (result.rows || []).map(transform); | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| tx: async (txName, callback) => { | ||||||||||||||||||||||||
| return await callback(taskContext); | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
|
Comment on lines
+118
to
+120
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nested transaction doesn't actually start a transaction. The 🔎 Proposed fixThe nested
tx: async (txName, callback) => {
- return await callback(taskContext);
+ // Option 1: Use outer tx method
+ return await client.tx(txName, callback);
+ // OR Option 2: Explicitly disallow
+ throw new Error('Nested transactions are not supported. Use client.tx() at the top level.');
},
|
||||||||||||||||||||||||
| batch: async (promises) => { | ||||||||||||||||||||||||
| return await Promise.all(promises); | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||
| return await callback(taskContext); | ||||||||||||||||||||||||
| } finally { | ||||||||||||||||||||||||
| await conn.close(); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| async any(query, params) { | ||||||||||||||||||||||||
| const conn = await this.connect(); | ||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||
| const formatted = formatQueryForOracle(query, params); | ||||||||||||||||||||||||
| const result = await conn.execute(formatted.query, formatted.params, { | ||||||||||||||||||||||||
| outFormat: oracledb.OUT_FORMAT_OBJECT, | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| return result.rows || []; | ||||||||||||||||||||||||
| } finally { | ||||||||||||||||||||||||
| await conn.close(); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| async one(query, params, transform) { | ||||||||||||||||||||||||
| const conn = await this.connect(); | ||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||
| const formatted = formatQueryForOracle(query, params); | ||||||||||||||||||||||||
| const result = await conn.execute(formatted.query, formatted.params, { | ||||||||||||||||||||||||
| outFormat: oracledb.OUT_FORMAT_OBJECT, | ||||||||||||||||||||||||
| maxRows: 1, | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| const row = result.rows && result.rows[0]; | ||||||||||||||||||||||||
| return transform ? transform(row) : row; | ||||||||||||||||||||||||
| } finally { | ||||||||||||||||||||||||
| await conn.close(); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| async none(query, params) { | ||||||||||||||||||||||||
| const conn = await this.connect(); | ||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||
| const formatted = formatQueryForOracle(query, params); | ||||||||||||||||||||||||
| await conn.execute(formatted.query, formatted.params, { | ||||||||||||||||||||||||
| outFormat: oracledb.OUT_FORMAT_OBJECT, | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| } finally { | ||||||||||||||||||||||||
| await conn.close(); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
Comment on lines
+156
to
+165
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing autoCommit for In standalone 🔎 Proposed fix for standalone none() async none(query, params) {
const conn = await this.connect();
try {
const formatted = formatQueryForOracle(query, params);
await conn.execute(formatted.query, formatted.params, {
outFormat: oracledb.OUT_FORMAT_OBJECT,
+ autoCommit: true,
});
} finally {
await conn.close();
}
},Similarly, consider Also applies to: 199-204 🤖 Prompt for AI Agents |
||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| async map(query, params, transform) { | ||||||||||||||||||||||||
| const conn = await this.connect(); | ||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||
| const formatted = formatQueryForOracle(query, params); | ||||||||||||||||||||||||
| const result = await conn.execute(formatted.query, formatted.params, { | ||||||||||||||||||||||||
| outFormat: oracledb.OUT_FORMAT_OBJECT, | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| return (result.rows || []).map(transform); | ||||||||||||||||||||||||
| } finally { | ||||||||||||||||||||||||
| await conn.close(); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| async tx(txName, callback) { | ||||||||||||||||||||||||
| const conn = await this.connect(); | ||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||
| const txContext = { | ||||||||||||||||||||||||
| any: async (query, params) => { | ||||||||||||||||||||||||
| const formatted = formatQueryForOracle(query, params); | ||||||||||||||||||||||||
| const result = await conn.execute(formatted.query, formatted.params, { | ||||||||||||||||||||||||
| outFormat: oracledb.OUT_FORMAT_OBJECT, | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| return result.rows || []; | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| one: async (query, params, transform) => { | ||||||||||||||||||||||||
| const formatted = formatQueryForOracle(query, params); | ||||||||||||||||||||||||
| const result = await conn.execute(formatted.query, formatted.params, { | ||||||||||||||||||||||||
| outFormat: oracledb.OUT_FORMAT_OBJECT, | ||||||||||||||||||||||||
| maxRows: 1, | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| const row = result.rows && result.rows[0]; | ||||||||||||||||||||||||
| return transform ? transform(row) : row; | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| none: async (query, params) => { | ||||||||||||||||||||||||
| const formatted = formatQueryForOracle(query, params); | ||||||||||||||||||||||||
| await conn.execute(formatted.query, formatted.params, { | ||||||||||||||||||||||||
| outFormat: oracledb.OUT_FORMAT_OBJECT, | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| map: async (query, params, transform) => { | ||||||||||||||||||||||||
| const formatted = formatQueryForOracle(query, params); | ||||||||||||||||||||||||
| const result = await conn.execute(formatted.query, formatted.params, { | ||||||||||||||||||||||||
| outFormat: oracledb.OUT_FORMAT_OBJECT, | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| return (result.rows || []).map(transform); | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| batch: async (promises) => { | ||||||||||||||||||||||||
| return await Promise.all(promises); | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| ctx: { duration: 0 }, | ||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||
| const result = await callback(txContext); | ||||||||||||||||||||||||
| await conn.commit(); | ||||||||||||||||||||||||
| return result; | ||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||
| await conn.rollback().catch(() => {}); | ||||||||||||||||||||||||
| throw error; | ||||||||||||||||||||||||
|
Comment on lines
+220
to
+222
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Silent error swallowing during rollback masks failures. Line 200 catches and ignores all rollback errors with 🔎 Proposed fix } catch (error) {
- await conn.rollback().catch(() => {});
+ try {
+ await conn.rollback();
+ } catch (rollbackError) {
+ // Log but don't throw to preserve original error
+ console.error('Rollback failed:', rollbackError);
+ }
throw error;📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
| } finally { | ||||||||||||||||||||||||
| await conn.close(); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
|
Comment on lines
+85
to
+226
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Significant code duplication between task, tx, and standalone methods. The 🔎 Refactoring approachExtract the common query execution logic into shared helper functions: function createQueryContext(conn) {
return {
any: async (query, params) => {
const formatted = formatQueryForOracle(query, params);
const result = await conn.execute(formatted.query, formatted.params, {
outFormat: oracledb.OUT_FORMAT_OBJECT,
});
return result.rows || [];
},
one: async (query, params, transform) => {
const formatted = formatQueryForOracle(query, params);
const result = await conn.execute(formatted.query, formatted.params, {
outFormat: oracledb.OUT_FORMAT_OBJECT,
maxRows: 1,
});
const row = result.rows && result.rows[0];
return transform ? transform(row) : row;
},
none: async (query, params) => {
const formatted = formatQueryForOracle(query, params);
await conn.execute(formatted.query, formatted.params, {
outFormat: oracledb.OUT_FORMAT_OBJECT,
});
},
map: async (query, params, transform) => {
const formatted = formatQueryForOracle(query, params);
const result = await conn.execute(formatted.query, formatted.params, {
outFormat: oracledb.OUT_FORMAT_OBJECT,
});
return (result.rows || []).map(transform);
},
batch: async (promises) => {
return await Promise.all(promises);
},
};
}Then reuse this in task, tx, and standalone methods. 🤖 Prompt for AI Agents |
||||||||||||||||||||||||
| $pool: { | ||||||||||||||||||||||||
| get pool() { | ||||||||||||||||||||||||
| return pool; | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| async end() { | ||||||||||||||||||||||||
| if (pool) { | ||||||||||||||||||||||||
| await pool.close(); | ||||||||||||||||||||||||
| pool = null; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| get ended() { | ||||||||||||||||||||||||
| return !pool; | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| return { client, oracledb }; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent module format: ES6 export mixed with CommonJS require.
Line 13 uses ES6
export functionwhile lines 1-3 use CommonJSrequire. This mixing can cause issues depending on the module system in use. Parse Server uses CommonJS throughout.🔎 Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents