Last active
April 17, 2024 15:31
-
-
Save fl0wo/8f01e0f96309c84912f3a8f23c31a9b4 to your computer and use it in GitHub Desktop.
UpdateRowBeforeAndAfterPlugin but with diffs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
describe('UpdateRowBeforeAndAfterPlugin', () => { | |
const plugin = new UpdateRowBeforeAndAfterPlugin( | |
{} as any, | |
{} as any, | |
{} as any | |
); | |
it.each([ | |
['UPDATE "users" SET name = "Alice" WHERE id = ?', 0], | |
['UPDATE "users" SET name = "Alice" WHERE id = ? AND age = ?', 0], | |
['UPDATE "users" SET name = "Alice" WHERE age = ? AND id = ?', 1], | |
['UPDATE "users" SET name = "Alice" WHERE age = ? AND id = ? AND name = ?', 1], | |
['UPDATE "users" SET name = "Alice" WHERE age = ? AND name = ? AND id232 = ? AND botid = ? AND notYourId = ? AND id = ?', 5], | |
])('getIndexOfIdQuestionMark(%s) should return %i', (sql, expected) => { | |
const query = {sql}; | |
const result = plugin.getIndexOfIdQuestionMark(query as any); | |
expect(result).toBe(expected); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {LibSQLPlugin} from "libsql-client-hooks"; | |
import {Client, InStatement, ResultSet} from '@libsql/core/api'; | |
import { | |
deepDiffPartials, | |
isUpdateForTable, | |
removeDoubleQuotes, | |
replaceAllSequencesFromQuery | |
} from "./utils"; | |
import {TableNamesTurso} from "../index"; | |
export class UpdateRowBeforeAndAfterPlugin<T> implements LibSQLPlugin { | |
private rowBefore: T | undefined; | |
constructor( | |
private readonly tableName: TableNamesTurso, | |
private readonly queryGetRowBeforeById: (id: any) => Promise<T>, | |
private readonly execWithBeforeAndAfter: ( | |
rowBefore: T, | |
rowAfter: T, | |
rowDiff?: { | |
before: Partial<T>, | |
after: Partial<T> | |
} | |
) => Promise<void> | |
) { | |
} | |
async beforeExecute(query: InStatement, __: Client){ | |
if(!isUpdateForTable(query, this.tableName)) { | |
return query; | |
} | |
//{ | |
// sql: 'update "trades" set "amount" = ?, "id" = ?, "id_bot" = ?, "placed_by" = ?, "price" = ?, "side" = ?, "symbol" = ?, "timestamp" = ? where "trades"."id" = ? returning "id", "id_bot", "symbol", "side", "amount", "price", "placed_by", "fee_rate", "fee_coin", "fee_type", "fee_cost", "profit", "profit_percentage", "timestamp", "order", "type", "taker_or_maker", "cost", "created_at", "updated_at"', | |
// args: [ | |
// 0, | |
// 'fake-trade-test', | |
// 'test_bot_id', | |
// 'bot', | |
// 0, | |
// 'sell', | |
// '', | |
// 1713352086126, | |
// 'fake-trade-test' | |
// ] | |
// } | |
// 1. Find the index of the ? for the "id" field | |
const nQMarksBefore = this.getIndexOfIdQuestionMark(query as any); | |
// 2. Get the value of the ? for the "id" field | |
const args = (query as any)?.args; | |
const rowId = args?.[nQMarksBefore]; | |
// 3. Get the row before the update | |
this.rowBefore = await this.queryGetRowBeforeById(rowId); | |
return query; | |
} | |
getIndexOfIdQuestionMark(query?:{sql?:string}) { | |
if (!query?.sql) { | |
throw new Error('query.sql is not defined'); | |
} | |
const txtQuery = replaceAllSequencesFromQuery( | |
removeDoubleQuotes(query.sql), | |
' = ', | |
'=' | |
); | |
const idIndex = txtQuery.indexOf(' id=?'); | |
const txtQueryBefore = txtQuery.substring(0, idIndex); | |
return txtQueryBefore.split('?').length - 1; | |
} | |
async afterExecute(result: ResultSet, query: InStatement, ___: Client){ | |
if(!isUpdateForTable(query)) { | |
return result; | |
} | |
if(!this.rowBefore) { | |
throw new Error('Row before is not set'); | |
} | |
if(result.rows.length !== 1) { | |
throw new Error('Expected one row to be returned'); | |
} | |
const diff = deepDiffPartials<T>( | |
this.rowBefore, | |
result.rows[0] as T, | |
{ | |
ignoreUndefinedFields: true | |
}); | |
await this.execWithBeforeAndAfter( | |
this.rowBefore, | |
result.rows[0] as T, | |
diff | |
); | |
return result; | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export function deepDiffPartials<T>(rowBefore: T, rowAfter: T, options?:{ | |
ignoreUndefinedFields: boolean | |
}):{ | |
before: Partial<T>, | |
after: Partial<T> | |
} { | |
const before: Partial<T> = {}; | |
const after: Partial<T> = {}; | |
for(const key in rowBefore) { | |
if(options?.ignoreUndefinedFields && (!rowBefore[key] || !rowAfter[key])) { | |
continue; | |
} | |
if(rowBefore[key] !== rowAfter[key]) { | |
before[key] = rowBefore[key]; | |
after[key] = rowAfter[key]; | |
} | |
} | |
return {before, after}; | |
} | |
export function replaceAllSequencesFromQuery(txt: string,sequence:string,replace:string) { | |
if(!txt) return txt; | |
return txt.replace(new RegExp(sequence, 'g'), replace); | |
} | |
export function removeDoubleQuotes(txt: string) { | |
if(!txt) return txt; | |
return txt.replace(/"/g, ''); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment