Skip to content

Instantly share code, notes, and snippets.

@fl0wo
Created April 17, 2024 15:04
Show Gist options
  • Save fl0wo/ddd552ea319c78b29ded6b940e7584c6 to your computer and use it in GitHub Desktop.
Save fl0wo/ddd552ea319c78b29ded6b940e7584c6 to your computer and use it in GitHub Desktop.
UpdateRowBeforeAndAfterPlugin using LibSQLPlugin
TableNamesTurso = is just a type containing all the "names of the tables"
If you have drizzle: export type TableNamesTurso = keyof NonNullable<typeof schema>; // use ur db
import {LibSQLPlugin} from "libsql-client-hooks";
import { InStatement, Client, ResultSet } from '@libsql/core/api';
import {isUpdateForTable, removeSpaces} 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) => 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 txtQuery = removeSpaces((query as any)?.sql);
const idIndex = txtQuery.indexOf('"id"=?');
const txtQueryBefore = txtQuery.substring(0,idIndex);
const nQMarksBefore = txtQueryBefore.split('?').length - 1;
// 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;
}
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');
}
await this.execWithBeforeAndAfter(this.rowBefore, result.rows[0] as T);
return result;
}
}
export function isUpdateForTable(query: any, tableName?: string): boolean {
if(!tableName) {
// only check that is an update query
return /update/i.test(typeof query === "string" ? query : query.sql);
}
const escapedTableName = tableName.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
const regexp = new RegExp(`update "?${escapedTableName}"? set`, 'i');
return regexp.test(typeof query === "string" ? query : query.sql);
}
export function removeSpaces(txt: string) {
if(!txt) return txt;
return txt.replace(/\s/g, '');
}
@fl0wo
Copy link
Author

fl0wo commented Apr 17, 2024

Before: {
id: 'fake-trade-test',
idBot: 'test_bot_id',
symbol: '',
side: 'sell',
amount: 0,
price: 0,
placedBy: 'bot',
feeRate: null,
feeCoin: null,
feeType: null,
feeCost: null,
profit: null,
profitPercentage: null,
timestamp: 2024-04-17T14:56:42.609Z,
order: null,
type: null,
takerOrMaker: null,
cost: null,
createdAt: 1970-01-20T19:55:51.741Z,
updatedAt: 1970-01-20T19:55:51.741Z
}
After: {
id: 'fake-trade-test',
id_bot: 'test_bot_id',
symbol: '',
side: 'sell',
amount: 0,
price: 0,
placed_by: 'bot',
fee_rate: null,
fee_coin: null,
fee_type: null,
fee_cost: null,
profit: null,
profit_percentage: null,
timestamp: 1713365992938,
order: null,
type: null,
taker_or_maker: null,
cost: null,
created_at: 1713351741,
updated_at: 1713351741
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment