Skip to content

Commit

Permalink
Merge pull request #1101 from Chia-Network/reset-audit-table-to-gener…
Browse files Browse the repository at this point in the history
…ation

feat: added resetToGeneration audit endpoint
  • Loading branch information
TheLastCicada authored May 31, 2024
2 parents 18730fc + 7de1b68 commit 319e2a9
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 6 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

71 changes: 68 additions & 3 deletions src/controllers/audit.controller.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Audit } from '../models';

import _ from 'lodash';
import {
paginationParams,
optionallyPaginatedResponse,
} from '../utils/helpers';
import { assertIfReadOnlyMode } from '../utils/data-assertions.js';

export const findAll = async (req, res) => {
try {
Expand All @@ -20,7 +21,7 @@ export const findAll = async (req, res) => {
return res.json(optionallyPaginatedResponse(auditResults, page, limit));
} catch (error) {
res.status(400).json({
message: 'Can not retreive audit data',
message: 'Can not retrieve audit data',
error: error.message,
success: false,
});
Expand All @@ -32,9 +33,73 @@ export const findConflicts = async (req, res) => {
return res.json(await Audit.findConflicts());
} catch (error) {
res.status(400).json({
message: 'Can not retreive audit data',
message: 'Can not retrieve audit data',
error: error.message,
success: false,
});
}
};

export const resetToGeneration = async (req, res) => {
try {
await assertIfReadOnlyMode();
const { generation, orgUid } = req.body;

const result = await Audit.resetToGeneration(generation, orgUid);
if (_.isNil(result)) {
throw new Error('query failed');
}
return res.json({
message: result
? 'reset to generation ' + String(generation)
: 'no matching records',
success: true,
});
} catch (error) {
if (error.message === 'SQLITE_BUSY: database is locked') {
res.status(400).json({
message: 'failed to change generation',
error: 'cadt is currently syncing, please try again later',
success: false,
});
} else {
res.status(400).json({
message: 'failed to change generation',
error: error.message,
success: false,
});
}
}
};

export const resetToDate = async (req, res) => {
try {
await assertIfReadOnlyMode();
const { date, orgUid, includeHomeOrg } = req.body;

const result = orgUid
? await Audit.resetOrgToDate(date, orgUid)
: await Audit.resetToDate(date, includeHomeOrg);
if (_.isNil(result)) {
throw new Error('query failed');
}
return res.json({
message: result ? 'reset to date ' + String(date) : 'no matching records',
success: true,
});
} catch (error) {
if (error.message === 'SQLITE_BUSY: database is locked') {
res.status(400).json({
message: 'failed to reset to date',
error: 'cadt is currently syncing, please try again later',
success: false,
});
} else {
res.status(400).json({
message: 'failed to reset to date',
error: error.message,
success: false,
});
}
}
};
51 changes: 51 additions & 0 deletions src/models/audit/audit.model.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { sequelize, safeMirrorDbHandler } from '../../database';
import { AuditMirror } from './audit.model.mirror';
import ModelTypes from './audit.modeltypes.cjs';
import findDuplicateIssuancesSql from './sql/find-duplicate-issuances.sql.js';
import { Organization } from '../organizations/index.js';

class Audit extends Model {
static async create(values, options) {
Expand Down Expand Up @@ -45,6 +46,56 @@ class Audit extends Model {
const [results] = await sequelize.query(findDuplicateIssuancesSql);
return results;
}

static async resetToGeneration(generation, orgUid) {
const where = {
generation: { [Sequelize.Op.gt]: generation },
};

if (orgUid) {
where.orgUid = orgUid;
}

return await Audit.destroy({ where });
}

static async resetToDate(date, includeHomeOrg) {
const timestampInSeconds = Math.round(new Date(date).valueOf() / 1000);
const homeOrgUid = Organization.getHomeOrg()?.uid;

const conditions = [
sequelize.where(
sequelize.cast(
sequelize.col('onChainConfirmationTimeStamp'),
'UNSIGNED',
),
{ [Sequelize.Op.gt]: timestampInSeconds },
),
];

if (!includeHomeOrg && homeOrgUid) {
conditions.push({ orguid: { [Sequelize.Op.ne]: homeOrgUid } });
}

return await Audit.destroy({ where: { [Sequelize.Op.and]: conditions } });
}

static async resetOrgToDate(date, orgUid) {
const timestampInSeconds = Math.round(new Date(date).valueOf() / 1000);

return await Audit.destroy({
where: {
orgUid: orgUid,
[Sequelize.Op.and]: sequelize.where(
sequelize.cast(
sequelize.col('onchainConfirmationTimeStamp'),
'UNSIGNED',
),
{ [Sequelize.Op.gt]: timestampInSeconds },
),
},
});
}
}

Audit.init(ModelTypes, {
Expand Down
22 changes: 21 additions & 1 deletion src/routes/v1/resources/audit.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import express from 'express';
import joiExpress from 'express-joi-validation';

import { AuditController } from '../../../controllers';
import { auditGetSchema } from '../../../validations';
import {
auditGetSchema,
auditResetToDateSchema,
auditResetToGenerationSchema,
} from '../../../validations';

const validator = joiExpress.createValidator({ passError: true });
const AuditRouter = express.Router();
Expand All @@ -17,4 +21,20 @@ AuditRouter.get('/findConflicts', (req, res) => {
return AuditController.findConflicts(req, res);
});

AuditRouter.post(
'/resetToGeneration',
validator.body(auditResetToGenerationSchema),
(req, res) => {
return AuditController.resetToGeneration(req, res);
},
);

AuditRouter.post(
'/resetToDate',
validator.body(auditResetToDateSchema),
(req, res) => {
return AuditController.resetToDate(req, res);
},
);

export { AuditRouter };
2 changes: 2 additions & 0 deletions src/tasks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import syncRegistries from './sync-registries';
import syncOrganizationMeta from './sync-organization-meta';
import syncGovernanceBody from './sync-governance-body';
import mirrorCheck from './mirror-check';
import resetAuditTable from './reset-audit-table';

const scheduler = new ToadScheduler();

Expand All @@ -25,6 +26,7 @@ const start = () => {
syncRegistries,
syncOrganizationMeta,
mirrorCheck,
resetAuditTable,
];
defaultJobs.forEach((defaultJob) => {
jobRegistry[defaultJob.id] = defaultJob;
Expand Down
66 changes: 66 additions & 0 deletions src/tasks/reset-audit-table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { SimpleIntervalJob, Task } from 'toad-scheduler';
import { Audit, Meta } from '../models';
import { logger } from '../config/logger.cjs';
import dotenv from 'dotenv';
import _ from 'lodash';
dotenv.config();

const task = new Task('reset-audit-table', async () => {
try {
const metaResult = await Meta.findOne({
where: {
metaKey: 'may2024AuditResetTaskHasRun',
},
attributes: ['metaValue'],
raw: true,
});

const taskHasRun = metaResult?.metaValue;

if (taskHasRun === 'true') {
return;
}

logger.info('performing audit table reset');

const where = { type: 'NO CHANGE' };
const noChangeEntries = await Audit.findAll({ where });

if (noChangeEntries.length) {
const result = await Audit.resetToDate('2024-05-11');
logger.info(
'audit table has been reset, records modified: ' + String(result),
);
}

if (_.isNil(taskHasRun)) {
await Meta.create({
metaKey: 'may2024AuditResetTaskHasRun',
metaValue: 'true',
});
} else {
await Meta.update(
{ metavalue: 'true' },
{
where: {
metakey: 'may2024AuditResetTaskHasRun',
},
returning: true,
},
);
}
} catch (error) {
logger.error('Retrying in 600 seconds', error);
}
});

const job = new SimpleIntervalJob(
{
seconds: 600,
runImmediately: true,
},
task,
{ id: 'reset-audit-table', preventOverrun: true },
);

export default job;
11 changes: 11 additions & 0 deletions src/validations/audit.validations.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,14 @@ export const auditGetSchema = Joi.object()
})
.with('page', 'limit')
.with('limit', 'page');

export const auditResetToGenerationSchema = Joi.object().keys({
generation: Joi.number(),
orgUid: Joi.string(),
});

export const auditResetToDateSchema = Joi.object().keys({
date: Joi.date(),
orgUid: Joi.string().optional(),
includeHomeOrg: Joi.bool().optional(),
});

0 comments on commit 319e2a9

Please sign in to comment.