Skip to content

Commit

Permalink
fix: Address PR comments, use user account instead of admin
Browse files Browse the repository at this point in the history
  • Loading branch information
Darun Seethammagari authored and Darun Seethammagari committed Aug 16, 2023
1 parent 178d049 commit 4d4e8bd
Show file tree
Hide file tree
Showing 10 changed files with 327 additions and 139 deletions.
7 changes: 4 additions & 3 deletions frontend/src/components/Editor/Editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,22 +261,23 @@ const Editor = ({

async function executeIndexerFunction(option = "latest", startingBlockHeight = null) {
setIsExecutingIndexerFunction(() => true)
const schemaName = indexerDetails.accountId.concat("_", indexerDetails.indexerName).replace(/[^a-zA-Z0-9]/g, '_');

switch (option) {
case "debugList":
await indexerRunner.executeIndexerFunctionOnHeights(heights, indexingCode, schema, option)
await indexerRunner.executeIndexerFunctionOnHeights(heights, indexingCode, schema, schemaName, option)
break
case "specific":
if (startingBlockHeight === null && Number(startingBlockHeight) === 0) {
console.log("Invalid Starting Block Height: starting block height is null or 0")
break
}

await indexerRunner.start(startingBlockHeight, indexingCode, schema, option)
await indexerRunner.start(startingBlockHeight, indexingCode, schema, schemaName, option)
break
case "latest":
const latestHeight = await requestLatestBlockHeight()
if (latestHeight) await indexerRunner.start(latestHeight - 10, indexingCode, schema, option)
if (latestHeight) await indexerRunner.start(latestHeight - 10, indexingCode, schema, schemaName, option)
}
setIsExecutingIndexerFunction(() => false)
}
Expand Down
31 changes: 22 additions & 9 deletions frontend/src/utils/indexerRunner.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default class IndexerRunner {
this.shouldStop = false;
}

async start(startingHeight, indexingCode, schema, option) {
async start(startingHeight, indexingCode, schema, schemaName, option) {
this.currentHeight = startingHeight;
this.shouldStop = false;
console.clear()
Expand All @@ -32,7 +32,7 @@ export default class IndexerRunner {
this.stop()
}
if (blockDetails) {
await this.executeIndexerFunction(this.currentHeight, blockDetails, indexingCode, schema);
await this.executeIndexerFunction(this.currentHeight, blockDetails, indexingCode, schema, schemaName);
this.currentHeight++;
await this.delay(1000);
}
Expand All @@ -50,10 +50,23 @@ export default class IndexerRunner {
return new Promise((resolve) => setTimeout(resolve, ms));
}

async executeIndexerFunction(height, blockDetails, indexingCode, schema) {
validateTableNames(tableNames) {
if (!(Array.isArray(tableNames) && tableNames.length > 0)) {
throw new Error("Schema does not have any tables. There should be at least one table.");
}
const correctTableNameFormat = /^[a-zA-Z_][a-zA-Z0-9_]*$/;

tableNames.forEach(name => {
if (!correctTableNameFormat.test(name)) {
throw new Error(`Table name ${name} is not formatted correctly. Table names must not start with a number and only contain alphanumerics or underscores.`);
}
});
}

async executeIndexerFunction(height, blockDetails, indexingCode, schema, schemaName) {
let innerCode = indexingCode.match(/getBlock\s*\([^)]*\)\s*{([\s\S]*)}/)[1];
let tableNames = Array.from(schema.matchAll(/CREATE TABLE\s+"(\w+)"/g), match => match[1]); // Get first capturing group of each match
console.log('Retrieved the following table names from schema: ', tableNames);
this.validateTableNames(tableNames);

if (blockDetails) {
const block = Block.fromStreamerMessage(blockDetails);
Expand All @@ -62,11 +75,11 @@ export default class IndexerRunner {
block.events()

console.log(block)
await this.runFunction(blockDetails, height, innerCode, tableNames);
await this.runFunction(blockDetails, height, innerCode, schemaName, tableNames);
}
}

async executeIndexerFunctionOnHeights(heights, indexingCode, schema) {
async executeIndexerFunctionOnHeights(heights, indexingCode, schema, schemaName) {
console.clear()
console.group('%c Welcome! Lets test your indexing logic on some Near Blocks!', 'color: white; background-color: navy; padding: 5px;');
if (heights.length === 0) {
Expand All @@ -83,14 +96,14 @@ export default class IndexerRunner {
console.log(error)
}
console.time('Indexing Execution Complete')
this.executeIndexerFunction(height, blockDetails, indexingCode, schema)
this.executeIndexerFunction(height, blockDetails, indexingCode, schema, schemaName)
console.timeEnd('Indexing Execution Complete')
console.groupEnd()
}
console.groupEnd()
}

async runFunction(streamerMessage, blockHeight, indexerCode, tableNames) {
async runFunction(streamerMessage, blockHeight, indexerCode, schemaName, tableNames) {
const innerCodeWithBlockHelper =
`
const block = Block.fromStreamerMessage(streamerMessage);
Expand Down Expand Up @@ -149,7 +162,7 @@ export default class IndexerRunner {
log: async (message) => {
this.handleLog(blockHeight, message);
},
db: this.buildDatabaseContext(blockHeight, 'debug', tableNames)
db: this.buildDatabaseContext(blockHeight, schemaName, tableNames)
};

wrappedFunction(Block, streamerMessage, context);
Expand Down
33 changes: 11 additions & 22 deletions runner/src/dml-handler/dml-handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@ import DmlHandler from './dml-handler';
describe('DML Handler tests', () => {
let pgClient: any;

const ACCOUNT = 'test_near';
const SCHEMA = 'test_schema';
const TABLE_NAME = 'test_table';

test('Test valid insert one with array', async () => {
beforeEach(() => {
pgClient = {
setUser: jest.fn(),
query: jest.fn().mockReturnValue({ rows: [] }),
format: pgFormat,
};
});

test('Test valid insert one with array', async () => {
const inputObj = {
account_id: 'test_acc_near',
block_height: 999,
Expand All @@ -24,18 +28,13 @@ describe('DML Handler tests', () => {

const dmlHandler = new DmlHandler(pgClient);

await dmlHandler.insert(SCHEMA, TABLE_NAME, [inputObj]);
await dmlHandler.insert(ACCOUNT, SCHEMA, TABLE_NAME, [inputObj]);
expect(pgClient.query.mock.calls).toEqual([
['INSERT INTO test_schema.test_table (account_id,block_height,block_timestamp,content,receipt_id,accounts_liked) VALUES (\'test_acc_near\', \'999\', \'UTC\', \'test_content\', \'111\', \'["cwpuzzles.near","devbose.near"]\') RETURNING *;', []]
]);
});

test('Test valid insert multiple rows with array', async () => {
pgClient = {
query: jest.fn().mockReturnValue({ rows: [] }),
format: pgFormat,
};

const inputObj = [{
account_id: 'morgs_near',
block_height: 1,
Expand All @@ -49,47 +48,37 @@ describe('DML Handler tests', () => {

const dmlHandler = new DmlHandler(pgClient);

await dmlHandler.insert(SCHEMA, TABLE_NAME, [inputObj]);
await dmlHandler.insert(ACCOUNT, SCHEMA, TABLE_NAME, [inputObj]);
expect(pgClient.query.mock.calls).toEqual([
['INSERT INTO test_schema.test_table (0,1) VALUES (\'{"account_id":"morgs_near","block_height":1,"receipt_id":"abc"}\'::jsonb, \'{"account_id":"morgs_near","block_height":2,"receipt_id":"abc"}\'::jsonb) RETURNING *;', []]
]);
});

test('Test valid select on two fields', async () => {
pgClient = {
query: jest.fn().mockReturnValue({ rows: [] }),
format: pgFormat,
};

const inputObj = {
account_id: 'test_acc_near',
block_height: 999,
};

const dmlHandler = new DmlHandler(pgClient);

await dmlHandler.select(SCHEMA, TABLE_NAME, inputObj, 0);
await dmlHandler.select(ACCOUNT, SCHEMA, TABLE_NAME, inputObj, 0);
expect(pgClient.query.mock.calls).toEqual([
['SELECT * FROM test_schema.test_table WHERE account_id=$1 AND block_height=$2;', Object.values(inputObj)]
['SELECT * FROM test_schema.test_table WHERE account_id=$1 AND block_height=$2', Object.values(inputObj)]
]);
});

test('Test valid select on two fields with limit', async () => {
pgClient = {
query: jest.fn().mockReturnValue({ rows: [] }),
format: pgFormat,
};

const inputObj = {
account_id: 'test_acc_near',
block_height: 999,
};

const dmlHandler = new DmlHandler(pgClient);

await dmlHandler.select(SCHEMA, TABLE_NAME, inputObj, 1);
await dmlHandler.select(ACCOUNT, SCHEMA, TABLE_NAME, inputObj, 1);
expect(pgClient.query.mock.calls).toEqual([
['SELECT * FROM test_schema.test_table WHERE account_id=$1 AND block_height=$2 LIMIT 1;', Object.values(inputObj)]
['SELECT * FROM test_schema.test_table WHERE account_id=$1 AND block_height=$2 LIMIT 1', Object.values(inputObj)]
]);
});
});
28 changes: 13 additions & 15 deletions runner/src/dml-handler/dml-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,39 +16,37 @@ export default class DmlHandler {
this.pgClient = pgClient;
}

async insert (schemaName: string, tableName: string, objects: any[]): Promise<any[]> {
console.log('Inserting object %s into table %s on schema %s', JSON.stringify(objects), tableName, schemaName);
async insert (account: string, schemaName: string, tableName: string, objects: any[]): Promise<any[]> {
if (!objects?.length) {
return [];
}
await this.pgClient.setUser(account); // Set Postgres user to account's user

const keys = Object.keys(objects[0]);
// Get array of values from each object, and return array of arrays as result. Expects all objects to have the same number of items in same order
const values = objects.map(obj => keys.map(key => obj[key]));

const query = `INSERT INTO ${schemaName}.${tableName} (${keys.join(',')}) VALUES %L RETURNING *;`;
const result = await wrapError(async () => await this.pgClient.query(this.pgClient.format(query, values), []), `Failed to execute ${query} on schema ${schemaName}.${tableName}`);
if (!(result.rows && result.rows.length > 0)) {

const result = await wrapError(async () => await this.pgClient.query(this.pgClient.format(query, values), []), `Failed to execute '${query}' on ${schemaName}.${tableName}.`);
if (result.rows?.length === 0) {
console.log('No rows were inserted.');
}
return result.rows;
}

async select (schemaName: string, tableName: string, object: any, limit: number): Promise<any[]> {
async select (account: string, schemaName: string, tableName: string, object: any, limit: number): Promise<any[]> {
await this.pgClient.setUser(account); // Set Postgres user to account's user

const roundedLimit = Math.round(limit);
console.log('Selecting objects with values %s from table %s on schema %s with %s limit', JSON.stringify(object), tableName, schemaName, limit === 0 ? 'no' : roundedLimit.toString());
console.log(object);
const keys = Object.keys(object);
console.log(keys);
const values = Object.values(object);
const param = Array.from({ length: keys.length }, (_, index) => `${keys[index]}=$${index + 1}`).join(' AND ');

let query = `SELECT * FROM ${schemaName}.${tableName} WHERE ${param}`;
if (roundedLimit <= 0) {
query = query.concat(';');
} else {
query = query.concat(' LIMIT ', roundedLimit.toString(), ';');
if (roundedLimit > 0) {
query = query.concat(' LIMIT ', roundedLimit.toString());
}
const result = await wrapError(async () => await this.pgClient.query(this.pgClient.format(query), values), `Failed to execute ${query} on ${schemaName}.${tableName}`);

const result = await wrapError(async () => await this.pgClient.query(this.pgClient.format(query), values), `Failed to execute '${query}' on ${schemaName}.${tableName}.`);
if (!(result.rows && result.rows.length > 0)) {
console.log('No rows were selected.');
}
Expand Down
Loading

0 comments on commit 4d4e8bd

Please sign in to comment.