diff --git a/.prettierignore b/.prettierignore index 3f61f3725ed..98e0a9bc410 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,5 @@ **/build +**/dist **/.github **/*.env **/node_modules @@ -16,4 +17,5 @@ packages/chain-events/.eslintrc.js **/contractTypes **/factories **/eth/types +**/eth/artifacts **/.eslintrc.js diff --git a/packages/chain-events/Procfile b/packages/chain-events/Procfile index b64bf8a8628..79a89848f55 100644 --- a/packages/chain-events/Procfile +++ b/packages/chain-events/Procfile @@ -1,6 +1,6 @@ -web: node --max_old_space_size=$(../../../../scripts/get-max-old-space-size.sh) packages/chain-events/build/chain-events/services/app/Server.js -subscriber1: CHAIN_SUBSCRIBER_INDEX=0 node --max_old_space_size=$(../../../../scripts/get-max-old-space-size.sh) packages/chain-events/build/chain-events/services/ChainSubscriber/chainSubscriber.js run-as-script -subscriber2: CHAIN_SUBSCRIBER_INDEX=1 node --max_old_space_size=$(../../../../scripts/get-max-old-space-size.sh) packages/chain-events/build/chain-events/services/ChainSubscriber/chainSubscriber.js run-as-script -subscriber3: CHAIN_SUBSCRIBER_INDEX=2 node --max_old_space_size=$(../../../../scripts/get-max-old-space-size.sh) packages/chain-events/build/chain-events/services/ChainSubscriber/chainSubscriber.js run-as-script -consumer: node --max_old_space_size=$(../../../../scripts/get-max-old-space-size.sh) packages/chain-events/build/chain-events/services/ChainEventsConsumer/chainEventsConsumer.js run-as-script +web: node --max_old_space_size=$(./scripts/get-max-old-space-size.sh) packages/chain-events/build/chain-events/services/app/Server.js +subscriber1: CHAIN_SUBSCRIBER_INDEX=0 node --max_old_space_size=$(./scripts/get-max-old-space-size.sh) packages/chain-events/build/chain-events/services/ChainSubscriber/chainSubscriber.js run-as-script +subscriber2: CHAIN_SUBSCRIBER_INDEX=1 node --max_old_space_size=$(./scripts/get-max-old-space-size.sh) packages/chain-events/build/chain-events/services/ChainSubscriber/chainSubscriber.js run-as-script +subscriber3: CHAIN_SUBSCRIBER_INDEX=2 node --max_old_space_size=$(./scripts/get-max-old-space-size.sh) packages/chain-events/build/chain-events/services/ChainSubscriber/chainSubscriber.js run-as-script +consumer: node --max_old_space_size=$(./scripts/get-max-old-space-size.sh) packages/chain-events/build/chain-events/services/ChainEventsConsumer/chainEventsConsumer.js run-as-script release: cd packages/chain-events && npx sequelize-cli db:migrate --config services/database/sequelize.json diff --git a/packages/chain-events/package.json b/packages/chain-events/package.json index fd4459fccaa..3a561807089 100644 --- a/packages/chain-events/package.json +++ b/packages/chain-events/package.json @@ -30,9 +30,9 @@ "batch-poll": "ts-node -T ./scripts/batchPoller.ts", "preyalcpublish": "yarn build", "emit-events": "ts-node --project tsconfig.json ./scripts/emitChainEvents.ts", - "start-app": "ts-node --project tsconfig.services.json services/app/Server.ts", - "start-consumer": "ts-node --project tsconfig.services.json services/ChainEventsConsumer/chainEventsConsumer.ts run-as-script", - "start-subscriber": "ts-node --project tsconfig.services.json services/ChainSubscriber/chainSubscriber.ts run-as-script", + "start-app": "ts-node-dev --max-old-space-size=4096 --respawn --transpile-only --project tsconfig.services.json services/app/Server.ts", + "start-consumer": "ts-node-dev --max-old-space-size=4096 --respawn --transpile-only --project tsconfig.services.json services/ChainEventsConsumer/chainEventsConsumer.ts run-as-script", + "start-subscriber": "ts-node-dev --max-old-space-size=4096 --respawn --transpile-only --project tsconfig.services.json services/ChainSubscriber/chainSubscriber.ts run-as-script", "start-all": "concurrently -p '{name}' -c yellow,blue,magenta -n app,consumer,subscriber 'yarn start-app' 'yarn start-consumer' 'yarn start-subscriber'", "create-db": "npx sequelize db:create", "migrate-db": "npx sequelize db:migrate", @@ -81,6 +81,7 @@ "pg-format": "^1.0.4", "rollbar": "^2.25.2", "sleep-promise": "^8.0.1", + "ts-node-dev": "^2.0.0", "typescript-logging": "^0.6.4", "underscore": "^1.10.2", "web3": "^1.3.1", diff --git a/packages/chain-events/scripts/migrateChainEntities.ts b/packages/chain-events/scripts/migrateChainEntities.ts index fb7623b5a1e..78477a4891b 100644 --- a/packages/chain-events/scripts/migrateChainEntities.ts +++ b/packages/chain-events/scripts/migrateChainEntities.ts @@ -28,6 +28,9 @@ import type { BrokerConfig } from 'rascal'; import { RABBITMQ_URI } from '../../commonwealth/server/config'; import { constructSubstrateUrl } from '../../commonwealth/shared/substrate'; import { CHAIN_EVENT_SERVICE_SECRET, CW_SERVER_URL } from '../services/config'; +import NotificationsHandler from '../services/ChainEventsConsumer/ChainEventHandlers/notification'; +import models from '../services/database/database'; +import { EventKind } from '../src/chains/substrate/types'; const log = factory.getLogger(formatFilename(__filename)); @@ -95,6 +98,14 @@ async function migrateChainEntity( rmqController, chain ); + + const excludedNotificationEvents = [EventKind.DemocracyTabled]; + const notificationsHandler = new NotificationsHandler( + models, + rmqController, + excludedNotificationEvents + ); + let fetcher: IStorageFetcher; const range: IDisconnectedRange = { startBlock: 0 }; if (chainInstance.base === ChainBase.Substrate) { @@ -153,7 +164,8 @@ async function migrateChainEntity( try { // eslint-disable-next-line no-await-in-loop const dbEvent = await migrationHandler.handle(event); - await entityArchivalHandler.handle(event, dbEvent); + const ceEvent = await entityArchivalHandler.handle(event, dbEvent); + await notificationsHandler.handle(event, ceEvent); } catch (e) { log.error(`Event handle failure: ${e.message}`); } diff --git a/packages/chain-events/scripts/publishCustomRabbitMQMessage.ts b/packages/chain-events/scripts/publishCustomRabbitMQMessage.ts new file mode 100644 index 00000000000..9a9bd46334b --- /dev/null +++ b/packages/chain-events/scripts/publishCustomRabbitMQMessage.ts @@ -0,0 +1,83 @@ +import type { IEventData } from '../src/chains/aave/types'; +import type { CWEvent } from '../src'; +import { SupportedNetwork } from '../src'; +import { publishRmqMsg } from 'common-common/src/rabbitmq/util'; +import { RABBITMQ_API_URI } from '../services/config'; +import { RascalExchanges, RascalRoutingKeys } from 'common-common/src/rabbitmq'; +import models from 'chain-events/services/database/database'; + +async function main() { + const ceData = { + id: 10, + kind: 'proposal-created', + values: ['0'], + targets: ['0xE710CEd57456D3A16152c32835B5FB4E72D9eA5b'], + endBlock: 16203604, + executor: '0x64c7d40c07EFAbec2AafdC243bF59eaF2195c6dc', + ipfsHash: + '0x3876d28a014bc20432dcc3549ba95710446b98431d84c7f84fde6abe1baf527f', + proposer: '0xb55a948763e0d386b6dEfcD8070a522216AE42b1', + strategy: '0x90Dfd35F4a0BB2d30CDf66508085e33C353475D9', + calldatas: [ + '0x00000000000000000000000092d6c1e31e14520e676a687f0a93788b716beff5000000000000000000000000a8541f948411b3f95d9e89e8d339a56a9ed3d00b000000000000000000000000000000000000000000002fa54641bae8aaa00000', + ], + signatures: ['transfer(address,address,uint256)'], + startBlock: 16177324, + }; + const chainEvent: CWEvent = { + blockNumber: 16170754, + data: ceData, + network: SupportedNetwork.Aave, + chain: 'dydx', + }; + + const publishJson = await publishRmqMsg( + RABBITMQ_API_URI, + RascalExchanges.ChainEvents, + RascalRoutingKeys.ChainEvents, + chainEvent + ); + + console.log(publishJson); +} + +async function clear() { + try { + let eventsDeleted = 0, + entitiesDeleted = 0; + await models.sequelize.transaction(async (t) => { + const entityId = ( + await models.ChainEntity.findOne({ + where: { chain: 'dydx', type_id: '10' }, + transaction: t, + }) + )?.id; + + if (entityId) { + eventsDeleted = await models.ChainEvent.destroy({ + where: { entity_id: entityId }, + transaction: t, + }); + + entitiesDeleted = await models.ChainEntity.destroy({ + where: { id: entityId }, + transaction: t, + }); + } else { + console.log('Entity does not exist.'); + } + }); + + console.log( + `Events deleted: ${eventsDeleted}\nEntities deleted: ${entitiesDeleted}` + ); + } catch (e) { + console.log('Failed to clear - reverted.'); + console.error(e); + } + + process.exit(1); +} + +if (process.argv[2] === 'clear') clear(); +else main(); diff --git a/packages/chain-events/scripts/testSequelizeQuery.ts b/packages/chain-events/scripts/testSequelizeQuery.ts deleted file mode 100644 index 46015304ac4..00000000000 --- a/packages/chain-events/scripts/testSequelizeQuery.ts +++ /dev/null @@ -1,21 +0,0 @@ -import models from '../services/database/database'; - -async function testSequelizeQuery() { - const eventTypes = ( - await models.ChainEventType.findAll({ - attributes: ['id'], - where: { chain: 'edgeware' }, - }) - ).map((x) => x.id); - - const dbResult = await models.ChainEvent.max('block_number', { - where: { - chain_event_type_id: eventTypes, - }, - }); - - console.log('Database Result:', dbResult); - process.exit(0); -} - -testSequelizeQuery(); diff --git a/packages/chain-events/services/ChainEventsConsumer/ChainEventHandlers/entityArchival.ts b/packages/chain-events/services/ChainEventsConsumer/ChainEventHandlers/entityArchival.ts index 39989846aa9..8a0253a9038 100644 --- a/packages/chain-events/services/ChainEventsConsumer/ChainEventHandlers/entityArchival.ts +++ b/packages/chain-events/services/ChainEventsConsumer/ChainEventHandlers/entityArchival.ts @@ -9,6 +9,7 @@ import { RascalPublications } from 'common-common/src/rabbitmq/types'; import type { DB } from '../../database/database'; +import type { ChainEventInstance } from 'chain-events/services/database/models/chain_event'; import type { CWEvent, IChainEntityKind, @@ -40,7 +41,10 @@ export default class extends IEventHandler { * `dbEvent` is the database entry corresponding to the `event`. */ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - public async handle(event: CWEvent, dbEvent) { + public async handle( + event: CWEvent, + dbEvent: ChainEventInstance + ) { // eslint-disable-next-line @typescript-eslint/no-shadow const log = factory.getLogger( addPrefix(__filename, [event.network, event.chain]) @@ -173,5 +177,7 @@ export default class extends IEventHandler { break; } } + + return dbEvent; } } diff --git a/packages/chain-events/services/ChainEventsConsumer/ChainEventHandlers/migration.ts b/packages/chain-events/services/ChainEventsConsumer/ChainEventHandlers/migration.ts index ce476149235..bb70619778b 100644 --- a/packages/chain-events/services/ChainEventsConsumer/ChainEventHandlers/migration.ts +++ b/packages/chain-events/services/ChainEventsConsumer/ChainEventHandlers/migration.ts @@ -2,26 +2,22 @@ * Processes events during migration, upgrading from simple notifications to entities. */ import type { WhereOptions } from 'sequelize'; -import type { - RabbitMQController, - RmqCENotificationCUD, - RmqCETypeCUD, -} from 'common-common/src/rabbitmq'; -import { RascalPublications } from 'common-common/src/rabbitmq'; +import type { RabbitMQController } from 'common-common/src/rabbitmq'; import { factory, formatFilename } from 'common-common/src/logging'; import type { CWEvent } from '../../../src'; import { - IEventHandler, + EntityEventKind, eventToEntity, getUniqueEntityKey, - EntityEventKind, + IEventHandler, } from '../../../src'; import type { DB } from '../../database/database'; import type { ChainEventAttributes, ChainEventInstance, } from '../../database/models/chain_event'; + const log = factory.getLogger(formatFilename(__filename)); export default class extends IEventHandler { @@ -47,48 +43,18 @@ export default class extends IEventHandler { fieldValue: string, eventType: EntityEventKind ) => { - const [dbEventType, created] = - await this._models.ChainEventType.findOrCreate({ - where: { - id: `${chain}-${event.data.kind.toString()}`, - chain, - event_network: event.network, - event_name: event.data.kind.toString(), - }, - }); - log.trace( - `${created ? 'created' : 'found'} chain event type: ${dbEventType.id}` - ); - - if (created) { - const publishData: RmqCETypeCUD.RmqMsgType = { - chainEventTypeId: dbEventType.id, - cud: 'create', - }; - - await this._rmqController.safePublish( - publishData, - dbEventType.id, - RascalPublications.ChainEventTypeCUDMain, - { - sequelize: this._models.sequelize, - model: this._models.ChainEventType, - } - ); - } - const queryFieldName = `event_data.${fieldName}`; const queryArgs: WhereOptions = eventType === EntityEventKind.Vote ? { - chain_event_type_id: dbEventType.id, [queryFieldName]: fieldValue, // votes will be unique by data rather than by type event_data: event.data as any, + chain, } : { - chain_event_type_id: dbEventType.id, [queryFieldName]: fieldValue, + chain, }; const existingEvent = await this._models.ChainEvent.findOne({ where: queryArgs, @@ -101,31 +67,12 @@ export default class extends IEventHandler { } log.info('No existing event found, creating new event in db!'); - const dbEvent = await this._models.ChainEvent.create({ - chain_event_type_id: dbEventType.id, + return await this._models.ChainEvent.create({ block_number: event.blockNumber, event_data: event.data, + network: event.network, + chain, }); - - const formattedEvent: ChainEventAttributes = dbEvent.toJSON(); - formattedEvent.ChainEventType = dbEventType.toJSON(); - - const publishData: RmqCENotificationCUD.RmqMsgType = { - ChainEvent: formattedEvent, - event, - cud: 'create', - }; - - await this._rmqController.safePublish( - publishData, - dbEvent.id, - RascalPublications.ChainEventNotificationsCUDMain, - { - sequelize: this._models.sequelize, - model: this._models.ChainEvent, - } - ); - return dbEvent; }; const entity = eventToEntity(event.network, event.data.kind); diff --git a/packages/chain-events/services/ChainEventsConsumer/ChainEventHandlers/notification.ts b/packages/chain-events/services/ChainEventsConsumer/ChainEventHandlers/notification.ts index 9d243bacc9a..b62a059c6c1 100644 --- a/packages/chain-events/services/ChainEventsConsumer/ChainEventHandlers/notification.ts +++ b/packages/chain-events/services/ChainEventsConsumer/ChainEventHandlers/notification.ts @@ -3,11 +3,15 @@ import type { RmqCENotificationCUD } from 'common-common/src/rabbitmq/types'; import { RascalPublications } from 'common-common/src/rabbitmq/types'; import { addPrefix, factory } from '../../../src/logging'; -import type { ChainEventAttributes } from '../../database/models/chain_event'; +import type { ChainEventInstance } from '../../database/models/chain_event'; import type { DB } from '../../database/database'; import type { CWEvent, IChainEventKind } from 'chain-events/src'; -import { IEventHandler } from 'chain-events/src'; +import { + EntityEventKind, + eventToEntity, + IEventHandler, +} from 'chain-events/src'; export default class extends IEventHandler { public readonly name = 'Notification Producer'; @@ -21,7 +25,7 @@ export default class extends IEventHandler { } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - public async handle(event: CWEvent, dbEvent) { + public async handle(event: CWEvent, dbEvent: ChainEventInstance) { const log = factory.getLogger( addPrefix(__filename, [event.network, event.chain]) ); @@ -36,25 +40,24 @@ export default class extends IEventHandler { return dbEvent; } - let dbEventType; - try { - dbEventType = await dbEvent.getChainEventType(); - if (!dbEventType) { - log.error(`Failed to fetch event type! Ignoring.`); - return; - } - } catch (e) { - log.error( - `Failed to get chain-event type for event: ${JSON.stringify(event)}` - ); + if (!dbEvent.entity_id) { + log.info(`No related entity, skipping!`); return dbEvent; } - const formattedEvent: ChainEventAttributes = dbEvent.toJSON(); - formattedEvent.ChainEventType = dbEventType.toJSON(); + const [, eventEntityKind] = eventToEntity(event.network, event.data.kind); + if ( + eventEntityKind != EntityEventKind.Create && + eventEntityKind != EntityEventKind.Complete + ) { + log.trace( + `Event does not mark the creation or completion of an entity. Skipping event!` + ); + return dbEvent; + } const publishData: RmqCENotificationCUD.RmqMsgType = { - ChainEvent: formattedEvent, + ChainEvent: dbEvent.toJSON(), event, cud: 'create', }; diff --git a/packages/chain-events/services/ChainEventsConsumer/ChainEventHandlers/storage.ts b/packages/chain-events/services/ChainEventsConsumer/ChainEventHandlers/storage.ts index 530fd26f631..e1cd27f04e7 100644 --- a/packages/chain-events/services/ChainEventsConsumer/ChainEventHandlers/storage.ts +++ b/packages/chain-events/services/ChainEventsConsumer/ChainEventHandlers/storage.ts @@ -2,10 +2,7 @@ * Generic handler that stores the event in the database. */ import { addPrefix, factory } from 'common-common/src/logging'; -import type { - RabbitMQController, - RmqCETypeCUD, -} from 'common-common/src/rabbitmq'; +import type { RabbitMQController } from 'common-common/src/rabbitmq'; import { RascalPublications } from 'common-common/src/rabbitmq'; import NodeCache from 'node-cache'; import hash from 'object-hash'; @@ -82,51 +79,11 @@ export default class extends IEventHandler { return; } - // locate event type and add event (and event type if needed) to database - const [dbEventType, created] = - await this._models.ChainEventType.findOrCreate({ - where: { - id: `${chain}-${event.data.kind.toString()}`, - chain, - event_network: event.network, - event_name: event.data.kind.toString(), - }, - }); - - if (created) { - const publishData: RmqCETypeCUD.RmqMsgType = { - chainEventTypeId: dbEventType.id, - cud: 'create', - }; - - await this._rmqController.safePublish( - publishData, - dbEventType.id, - RascalPublications.ChainEventTypeCUDMain, - { - sequelize: this._models.sequelize, - model: this._models.ChainEventType, - } - ); - - log.info(`STORAGE HANDLER MESSAGE PUBLISHED`); - } - - if (!dbEventType) { - log.error(`unknown event type: ${event.data.kind}`); - return; - } else { - if (created) { - log.info(`Created new ChainEventType: ${dbEventType.id}`); - } else { - log.trace(`found chain event type: ${dbEventType.id}`); - } - } - const eventData = { - chain_event_type_id: dbEventType.id, block_number: event.blockNumber, event_data: event.data, + network: event.network, + chain, }; // duplicate event check @@ -137,8 +94,6 @@ export default class extends IEventHandler { if (!cachedEvent) { const dbEvent = await this._models.ChainEvent.create(eventData); - // populate chainEventType, so we don't need to re-populate it in subsequence handlers - dbEvent.ChainEventType = dbEventType; // no need to save the entire event data since the key is the hash of the data this.eventCache.set(eventKey, true); diff --git a/packages/chain-events/services/ChainEventsConsumer/chainEventsConsumer.ts b/packages/chain-events/services/ChainEventsConsumer/chainEventsConsumer.ts index 67b3178723a..9fd3927769f 100644 --- a/packages/chain-events/services/ChainEventsConsumer/chainEventsConsumer.ts +++ b/packages/chain-events/services/ChainEventsConsumer/chainEventsConsumer.ts @@ -78,8 +78,8 @@ export async function setupChainEventConsumer(): Promise { // WARNING: due to dbEvent in each handler ORDER OF HANDLERS MATTERS! const allChainEventHandlers = [ storageHandler, - notificationsHandler, entityArchivalHandler, + notificationsHandler, ]; // setup Chain diff --git a/packages/chain-events/services/ChainEventsConsumer/republishMessages.ts b/packages/chain-events/services/ChainEventsConsumer/republishMessages.ts deleted file mode 100644 index 172f687e0a6..00000000000 --- a/packages/chain-events/services/ChainEventsConsumer/republishMessages.ts +++ /dev/null @@ -1,52 +0,0 @@ -import type { - RabbitMQController, - RmqCETypeCUD, -} from 'common-common/src/rabbitmq'; -import { - RascalPublications, - RepublishFailedMessages, -} from 'common-common/src/rabbitmq'; -import * as Sequelize from 'sequelize'; - -import type { DB } from '../database/database'; - -/** - * A worker that periodically republishes data from the database if it's queued value is between -1 and 5. A queued - * value of -1 - * - */ -export class RepublishMessages extends RepublishFailedMessages { - constructor(_rmqController: RabbitMQController, _models: DB) { - super(_rmqController, _models, 180000); - } - - protected async job(): Promise { - const result = await this._models.ChainEventType.findAll({ - where: { - queued: { - [Sequelize.Op.between]: [-1, 5], - }, - }, - }); - - // TODO - // if (result.length > 100) {} - - for (const eventType of result) { - const publishData: RmqCETypeCUD.RmqMsgType = { - chainEventTypeId: eventType.id, - cud: 'create', - }; - - await this._rmqController.safePublish( - publishData, - eventType.id, - RascalPublications.ChainEventTypeCUDMain, - { - sequelize: this._models.sequelize, - model: this._models.ChainEventType, - } - ); - } - } -} diff --git a/packages/chain-events/services/ChainSubscriber/util.ts b/packages/chain-events/services/ChainSubscriber/util.ts index cfc40a44d25..e398aed4ffb 100644 --- a/packages/chain-events/services/ChainSubscriber/util.ts +++ b/packages/chain-events/services/ChainSubscriber/util.ts @@ -8,6 +8,7 @@ import type { SubstrateEvents } from '../../src'; import { createListener, ErcLoggingHandler, + getChainEventNetwork, LoggingHandler, SupportedNetwork, } from '../../src'; @@ -218,15 +219,15 @@ async function setupNewListeners( ) { for (const chain of newChains) { let network: SupportedNetwork; - if (chain.base === ChainBase.Substrate) - network = SupportedNetwork.Substrate; - else if (chain.base === ChainBase.CosmosSDK) - network = SupportedNetwork.Cosmos; - else if (chain.network === ChainNetwork.Compound) - network = SupportedNetwork.Compound; - else if (chain.network === ChainNetwork.Aave) - network = SupportedNetwork.Aave; - + try { + network = getChainEventNetwork(chain.network, chain.base); + } catch (e) { + log.error( + `Unknown chain base: ${chain.base} \tand network: ${chain.network}`, + e + ); + continue; + } try { log.info(`Starting listener for: ${chain.id}`); listenerInstances[chain.id] = await createListener(chain.id, network, { @@ -379,21 +380,8 @@ export function getListenerNames( async function discoverReconnectRange(this: DB, chain: string) { let latestBlock; try { - const eventTypes = ( - await this.ChainEventType.findAll({ - where: { chain }, - }) - ).map((x) => x.id); - - if (eventTypes.length === 0) { - log.info(`[${chain}]: No event types exist in the database`); - return { startBlock: null }; - } - latestBlock = await this.ChainEvent.max('block_number', { - where: { - chain_event_type_id: eventTypes, - }, + where: { chain }, }); if (latestBlock) { diff --git a/packages/chain-events/services/app/routes/entities.ts b/packages/chain-events/services/app/routes/entities.ts index 8bc45eb5e26..7debfb30255 100644 --- a/packages/chain-events/services/app/routes/entities.ts +++ b/packages/chain-events/services/app/routes/entities.ts @@ -1,5 +1,5 @@ import type { Response, NextFunction, Request } from 'express'; -import { AppError } from 'common-common/src/errors'; +import { AppError, ServerError } from 'common-common/src/errors'; import type { DB } from '../../database/database'; @@ -22,7 +22,6 @@ const entities: any = async ( { model: models.ChainEvent, order: [[models.ChainEvent, 'id', 'asc']], - include: [models.ChainEventType], }, ], order: [['created_at', 'DESC']], @@ -42,11 +41,17 @@ const entities: any = async ( if (req.query.completed) { entityFindOptions.where.completed = true; } - const entities = await models.ChainEntity.findAll(entityFindOptions); - return res.json({ - status: 'Success', - result: entities.map((e) => e.toJSON()), - }); + + try { + const fetchedEntities = await models.ChainEntity.findAll(entityFindOptions); + return res.json({ + status: 'Success', + result: fetchedEntities.map((e) => e.toJSON()), + }); + } catch (err) { + console.error(err); + return next(new ServerError(`Failed to fetch entities from DB`, err)); + } }; export default entities; diff --git a/packages/chain-events/services/app/routes/eventActivity.ts b/packages/chain-events/services/app/routes/eventActivity.ts index 14d2ede2298..34e56fabd3c 100644 --- a/packages/chain-events/services/app/routes/eventActivity.ts +++ b/packages/chain-events/services/app/routes/eventActivity.ts @@ -1,6 +1,5 @@ import type { NextFunction, Request, Response } from 'express'; -import { AppError } from 'common-common/src/errors'; -import { QueryTypes } from 'sequelize'; +import { AppError, ServerError } from 'common-common/src/errors'; import type { DB } from '../../database/database'; @@ -18,26 +17,21 @@ const eventActivity: any = async ( return next(new AppError(Errors.NeedLimit)); } - const events = await models.sequelize.query( - ` - SELECT ce.id, - ce.chain_event_type_id, - ce.block_number, - ce.event_data, - ce.created_at, - ce.updated_at, - ce.entity_id, - cet.chain, - cet.event_network - FROM "ChainEvents" ce - JOIN "ChainEventTypes" cet ON ce.chain_event_type_id = cet.id - ORDER BY ce.created_at DESC - LIMIT ?; - `, - { replacements: [req.query.limit], raw: true, type: QueryTypes.SELECT } - ); - - return res.json({ status: 'Success', result: events }); + try { + // we can order by id since the resulting order is almost exactly the same as when ordered by created_at + // but ordering by id is much faster due to primary key index + const events = await models.ChainEvent.findAll({ + order: [['id', 'DESC']], + limit: req.query.limit, + }); + return res.json({ + status: 'Success', + result: events.map((e) => e.toJSON()), + }); + } catch (e) { + console.error(e); + return next(new ServerError(`Failed to fetch events from DB`, e)); + } }; export default eventActivity; diff --git a/packages/chain-events/services/database/database.ts b/packages/chain-events/services/database/database.ts index a7ca48f25d0..0c3b3339c34 100644 --- a/packages/chain-events/services/database/database.ts +++ b/packages/chain-events/services/database/database.ts @@ -6,8 +6,6 @@ import type { ChainEntityModelStatic } from './models/chain_entity'; import ChainEntityFactory from './models/chain_entity'; import type { ChainEventModelStatic } from './models/chain_event'; import ChainEventFactory from './models/chain_event'; -import type { ChainEventTypeModelStatic } from './models/chain_event_type'; -import ChainEventTypeFactory from './models/chain_event_type'; import { factory, formatFilename } from 'common-common/src/logging'; const log = factory.getLogger(formatFilename(__filename)); @@ -15,7 +13,6 @@ const log = factory.getLogger(formatFilename(__filename)); export type Models = { ChainEntity: ChainEntityModelStatic; ChainEvent: ChainEventModelStatic; - ChainEventType: ChainEventTypeModelStatic; }; export interface DB extends Models { @@ -52,7 +49,6 @@ export const sequelize = new Sequelize(DATABASE_URI, { const models: Models = { ChainEntity: ChainEntityFactory(sequelize, DataTypes), ChainEvent: ChainEventFactory(sequelize, DataTypes), - ChainEventType: ChainEventTypeFactory(sequelize, DataTypes), }; const db: DB = { diff --git a/packages/chain-events/services/database/migrations/20221206100637-move-network-to-event.js b/packages/chain-events/services/database/migrations/20221206100637-move-network-to-event.js new file mode 100644 index 00000000000..428270b2058 --- /dev/null +++ b/packages/chain-events/services/database/migrations/20221206100637-move-network-to-event.js @@ -0,0 +1,41 @@ +'use strict'; + +module.exports = { + up: async (queryInterface, Sequelize) => { + await queryInterface.sequelize.transaction(async (t) => { + await queryInterface.addColumn( + 'ChainEvents', + 'network', + { + type: Sequelize.STRING, + allowNull: true, + }, + { transaction: t } + ); + + await queryInterface.sequelize.query( + ` + UPDATE "ChainEvents" CE + SET network = CET.event_network + FROM "ChainEventTypes" CET + WHERE CE.chain_event_type_id = CET.id; + `, + { transaction: t } + ); + + await queryInterface.changeColumn( + 'ChainEvents', + 'network', + { + type: Sequelize.STRING, + allowNull: false, + }, + { transaction: t } + ); + }); + }, + + down: async (queryInterface, Sequelize) => { + await queryInterface.removeColumn('ChainEvents', 'network'); + }, +}; diff --git a/packages/chain-events/services/database/migrations/20230401044247-redo-cet-migrations.js b/packages/chain-events/services/database/migrations/20230401044247-redo-cet-migrations.js new file mode 100644 index 00000000000..59a3b1e23c5 --- /dev/null +++ b/packages/chain-events/services/database/migrations/20230401044247-redo-cet-migrations.js @@ -0,0 +1,40 @@ +'use strict'; + +module.exports = { + up: async (queryInterface, Sequelize) => { + await queryInterface.sequelize.transaction(async (t) => { + await queryInterface.addColumn( + 'ChainEvents', + 'chain', + { + type: Sequelize.STRING, + allowNull: true, + }, + { transaction: t } + ); + await queryInterface.sequelize.query( + ` + UPDATE "ChainEvents" CE + SET chain = CET.chain + FROM "ChainEventTypes" CET + WHERE CE.chain_event_type_id = CET.id; + `, + { transaction: t } + ); + + await queryInterface.changeColumn( + 'ChainEvents', + 'chain', + { + type: Sequelize.STRING, + allowNull: false, + }, + { transaction: t } + ); + }); + }, + + down: async (queryInterface, Sequelize) => { + await queryInterface.removeColumn('ChainEvents', 'chain'); + }, +}; diff --git a/packages/chain-events/services/database/migrations/20230401044247-remove-chain-event-types.js b/packages/chain-events/services/database/migrations/20230401044247-remove-chain-event-types.js new file mode 100644 index 00000000000..0d8fa60bba0 --- /dev/null +++ b/packages/chain-events/services/database/migrations/20230401044247-remove-chain-event-types.js @@ -0,0 +1,45 @@ +'use strict'; + +module.exports = { + up: async (queryInterface, Sequelize) => { + await queryInterface.sequelize.transaction(async (t) => { + await queryInterface.removeColumn('ChainEvents', 'chain_event_type_id', { + transaction: t, + }); + await queryInterface.dropTable('ChainEventTypes', { transaction: t }); + }); + }, + + down: async (queryInterface, Sequelize) => { + await queryInterface.sequelize.transaction(async (t) => { + await queryInterface.createTable( + 'ChainEventTypes', + { + // id = chain-event_name (event_name is value of string enum) + id: { type: Sequelize.STRING, primaryKey: true }, + chain: { type: Sequelize.STRING, allowNull: false }, + // should never be null, but added here for migration purposes + event_network: { type: Sequelize.STRING, allowNull: true }, + event_name: { type: Sequelize.STRING, allowNull: false }, + queued: { + type: Sequelize.SMALLINT, + allowNull: false, + defaultValue: 0, + }, + }, + { transaction: t } + ); + + await queryInterface.addColumn( + 'ChainEvents', + 'chain_event_type_id', + { + type: Sequelize.STRING, + allowNull: true, + references: { model: 'ChainEventTypes', key: 'id' }, + }, + { transaction: t } + ); + }); + }, +}; diff --git a/packages/chain-events/services/database/models/chain_entity.ts b/packages/chain-events/services/database/models/chain_entity.ts index 24dd00f45ed..6cd71a80b56 100644 --- a/packages/chain-events/services/database/models/chain_entity.ts +++ b/packages/chain-events/services/database/models/chain_entity.ts @@ -1,7 +1,7 @@ import type * as Sequelize from 'sequelize'; import type { DataTypes } from 'sequelize'; -import type { ChainEventAttributes } from './chain_event'; +import type { ChainEventAttributes, ChainEventInstance } from './chain_event'; import type { ModelStatic, ModelInstance } from './types'; export type ChainEntityAttributes = { @@ -18,7 +18,9 @@ export type ChainEntityAttributes = { ChainEvents?: ChainEventAttributes[]; }; -export type ChainEntityInstance = ModelInstance; +export type ChainEntityInstance = ModelInstance & { + getChainEvents: Sequelize.HasManyGetAssociationsMixin; +}; export type ChainEntityModelStatic = ModelStatic; diff --git a/packages/chain-events/services/database/models/chain_event.ts b/packages/chain-events/services/database/models/chain_event.ts index 7ba1827c13e..dd69cd9745b 100644 --- a/packages/chain-events/services/database/models/chain_event.ts +++ b/packages/chain-events/services/database/models/chain_event.ts @@ -3,27 +3,27 @@ import type { DataTypes } from 'sequelize'; import type { ModelStatic, ModelInstance } from './types'; import type { - ChainEventTypeAttributes, - ChainEventTypeInstance, -} from './chain_event_type'; -import type { ChainEntityAttributes } from './chain_entity'; + ChainEntityAttributes, + ChainEntityInstance, +} from './chain_entity'; +import type { SupportedNetwork } from '../../../src'; export type ChainEventAttributes = { id: number; - chain_event_type_id: string; block_number: number; event_data: any; queued: number; entity_id?: number; + network: SupportedNetwork; + chain: string; created_at?: Date; updated_at?: Date; - ChainEventType?: ChainEventTypeAttributes; ChainEntity?: ChainEntityAttributes; }; export type ChainEventInstance = ModelInstance & { - getChainEventType: Sequelize.HasOneGetAssociationMixin; + getChainEntity: Sequelize.HasOneGetAssociationMixin; }; export type ChainEventModelStatic = ModelStatic; @@ -36,13 +36,14 @@ export default ( 'ChainEvent', { id: { type: dataTypes.INTEGER, primaryKey: true, autoIncrement: true }, - chain_event_type_id: { type: dataTypes.STRING, allowNull: false }, block_number: { type: dataTypes.INTEGER, allowNull: false }, entity_id: { type: dataTypes.INTEGER, allowNull: true }, event_data: { type: dataTypes.JSONB, allowNull: false }, created_at: { type: dataTypes.DATE, allowNull: false }, updated_at: { type: dataTypes.DATE, allowNull: false }, queued: { type: dataTypes.SMALLINT, allowNull: false, defaultValue: 0 }, + chain: { type: dataTypes.STRING, allowNull: false }, + network: { type: dataTypes.STRING, allowNull: false }, }, { tableName: 'ChainEvents', @@ -59,11 +60,6 @@ export default ( ); ChainEvent.associate = (models) => { - // master event type - models.ChainEvent.belongsTo(models.ChainEventType, { - foreignKey: 'chain_event_type_id', - targetKey: 'id', - }); models.ChainEvent.belongsTo(models.ChainEntity, { foreignKey: 'entity_id', targetKey: 'id', diff --git a/packages/chain-events/services/database/models/chain_event_type.ts b/packages/chain-events/services/database/models/chain_event_type.ts deleted file mode 100644 index 940fcccc448..00000000000 --- a/packages/chain-events/services/database/models/chain_event_type.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type * as Sequelize from 'sequelize'; -import type { DataTypes } from 'sequelize'; - -import type { ChainEventAttributes } from './chain_event'; -import type { ModelStatic, ModelInstance } from './types'; - -export type ChainEventTypeAttributes = { - id: string; - chain: string; - event_network: string; - event_name: string; - queued: number; - ChainEvents?: ChainEventAttributes[]; -}; - -export type ChainEventTypeInstance = ModelInstance; - -export type ChainEventTypeModelStatic = ModelStatic; - -export default ( - sequelize: Sequelize.Sequelize, - dataTypes: typeof DataTypes -): ChainEventTypeModelStatic => { - const ChainEventType = sequelize.define( - 'ChainEventType', - { - // id = chain-event_name (event_name is value of string enum) - id: { type: dataTypes.STRING, primaryKey: true }, - chain: { type: dataTypes.STRING, allowNull: false }, - // should never be null, but added here for migration purposes - event_network: { type: dataTypes.STRING, allowNull: true }, - event_name: { type: dataTypes.STRING, allowNull: false }, - queued: { type: dataTypes.SMALLINT, allowNull: false, defaultValue: 0 }, - }, - { - tableName: 'ChainEventTypes', - timestamps: false, - underscored: true, - indexes: [{ fields: ['id'] }, { fields: ['chain', 'event_name'] }], - } - ); - - ChainEventType.associate = (models) => { - // many emitted events of this type - models.ChainEventType.hasMany(models.ChainEvent, { as: 'events' }); - }; - - return ChainEventType; -}; diff --git a/packages/chain-events/src/interfaces.ts b/packages/chain-events/src/interfaces.ts index d6eba5ba625..93e98e70848 100644 --- a/packages/chain-events/src/interfaces.ts +++ b/packages/chain-events/src/interfaces.ts @@ -19,6 +19,7 @@ import type { Api as CompoundApi } from './chains/compound/types'; import type { Api as CommonwealthApi } from './chains/commonwealth/types'; import type { Api as AaveApi } from './chains/aave/types'; import type { Listener } from './Listener'; +import { ChainBase, ChainNetwork } from 'common-common/src/types'; // add other events here as union types export type IChainEntityKind = @@ -206,6 +207,27 @@ export interface IEventTitle { export type TitlerFilter = (kind: IChainEventKind) => IEventTitle; +/** + * This function takes the network and base attribute of our current chains model and returns the relevant chain-event + * network. The chain-event network is an instance of SupportedNetwork enum. This is NOT the same as the network on + * the commonwealth chains model. This function is useful for determining which SupportedNetwork listener a + * Commonwealth 'chain' should use. Throws if the given chainNetwork and chainBase don't match a chain-event network + * i.e. SupportedNetwork. + * @param chainNetwork The network attribute of the Commonwealth chain model + * @param chainBase The base attribute of the Commonwealth chain model + */ +export function getChainEventNetwork( + chainNetwork: string, + chainBase: string +): SupportedNetwork { + if (chainBase === ChainBase.Substrate) return SupportedNetwork.Substrate; + else if (chainBase === ChainBase.CosmosSDK) return SupportedNetwork.Cosmos; + else if (chainNetwork === ChainNetwork.Compound) + return SupportedNetwork.Compound; + else if (chainNetwork === ChainNetwork.Aave) return SupportedNetwork.Aave; + else throw new Error('No matching SupportedNetwork'); +} + /** * Returns the key of the value that is unique to the entities chain and type i.e. the key whose associated value * becomes the type_id of the chain-entity. The combination of chain, type, and type_id must/will always be unique. diff --git a/packages/chain-events/test/integration/service-tests/chainEventsConsumer.test.ts b/packages/chain-events/test/integration/service-tests/chainEventsConsumer.test.ts index 7f7b9819425..56731b0510d 100644 --- a/packages/chain-events/test/integration/service-tests/chainEventsConsumer.test.ts +++ b/packages/chain-events/test/integration/service-tests/chainEventsConsumer.test.ts @@ -4,7 +4,6 @@ import { RascalQueues, RascalRoutingKeys, RmqCENotificationCUD, - RmqCETypeCUD, RmqEntityCUD, } from 'common-common/src/rabbitmq'; import type { ServiceConsumer } from 'common-common/src/serviceConsumer'; @@ -185,21 +184,6 @@ describe('Tests for the ChainEventsConsumer service', () => { expect(dbResult).to.not.be.null; - // check whether the new chain-event-type was pushed to the appropriate queue - const message = await getRmqMessage( - RABBITMQ_API_URI, - RascalQueues.ChainEventTypeCUDMain, - false - ); - expect(message).to.have.property('length'); - expect(message.length).to.equal(1); - expect(JSON.parse(message[0].payload)).to.deep.equal({ - chainEventTypeId: 'random-chain-transfer', - cud: 'create', - }); - expect(RmqCETypeCUD.isValidMsgFormat(JSON.parse(message[0].payload))).to.be - .true; - await models.ChainEvent.destroy({ where: { block_number: chainEvent.blockNumber, @@ -212,10 +196,6 @@ describe('Tests for the ChainEventsConsumer service', () => { }, }, }); - - await models.ChainEventType.destroy({ - where: { id: `${chain}-${ceData.kind}` }, - }); }); it('Should push new chain-events to the chain-event notifications queue', async () => { diff --git a/packages/common-common/src/rabbitmq/rabbitMQConfig.ts b/packages/common-common/src/rabbitmq/rabbitMQConfig.ts index 3e4dfdc4c70..7c8e94501e5 100644 --- a/packages/common-common/src/rabbitmq/rabbitMQConfig.ts +++ b/packages/common-common/src/rabbitmq/rabbitMQConfig.ts @@ -117,10 +117,6 @@ export function getRabbitMQConfig(rabbitmq_uri: string): Rascal.BrokerConfig { 'x-message-ttl': 600000, }, }, - [RascalQueues.ChainEventTypeCUDMain]: { - ...queueConfig, - options: queueOptions, - }, [RascalQueues.SnapshotProposalNotifications]: { ...queueConfig, options: { @@ -161,12 +157,6 @@ export function getRabbitMQConfig(rabbitmq_uri: string): Rascal.BrokerConfig { destinationType: 'queue', bindingKey: RascalBindings.ChainEventNotifications, }, - [RascalBindings.ChainEventType]: { - source: RascalExchanges.CUD, - destination: RascalQueues.ChainEventTypeCUDMain, - destinationType: 'queue', - bindingKey: RascalRoutingKeys.ChainEventTypeCUD, - }, [RascalBindings.SnapshotProposalNotifications]: { source: RascalExchanges.Notifications, destination: RascalQueues.SnapshotProposalNotifications, @@ -207,11 +197,6 @@ export function getRabbitMQConfig(rabbitmq_uri: string): Rascal.BrokerConfig { routingKey: RascalRoutingKeys.ChainEventNotifications, ...publicationConfig, }, - [RascalPublications.ChainEventTypeCUDMain]: { - exchange: RascalExchanges.CUD, - routingKey: RascalRoutingKeys.ChainEventTypeCUD, - ...publicationConfig, - }, [RascalPublications.SnapshotProposalNotifications]: { exchange: RascalExchanges.Notifications, routingKey: RascalRoutingKeys.SnapshotProposalNotifications, @@ -240,10 +225,6 @@ export function getRabbitMQConfig(rabbitmq_uri: string): Rascal.BrokerConfig { queue: RascalQueues.ChainEventNotifications, ...subscriptionConfig, }, - [RascalSubscriptions.ChainEventTypeCUDMain]: { - queue: RascalQueues.ChainEventTypeCUDMain, - ...subscriptionConfig, - }, [RascalSubscriptions.SnapshotProposalNotifications]: { queue: RascalQueues.SnapshotProposalNotifications, ...subscriptionConfig, diff --git a/packages/common-common/src/rabbitmq/rabbitMQController.ts b/packages/common-common/src/rabbitmq/rabbitMQController.ts index b4be0f3f28e..e3a8fc99ff5 100644 --- a/packages/common-common/src/rabbitmq/rabbitMQController.ts +++ b/packages/common-common/src/rabbitmq/rabbitMQController.ts @@ -9,15 +9,13 @@ import type Rollbar from 'rollbar'; import type { Sequelize } from 'sequelize'; import type { ChainEntityModelStatic } from 'chain-events/services/database/models/chain_entity'; import type { ChainEventModelStatic } from 'chain-events/services/database/models/chain_event'; -import type { ChainEventTypeModelStatic } from 'chain-events/services/database/models/chain_event_type'; import { factory, formatFilename } from 'common-common/src/logging'; const log = factory.getLogger(formatFilename(__filename)); export type SafeRmqPublishSupported = | ChainEntityModelStatic - | ChainEventModelStatic - | ChainEventTypeModelStatic; + | ChainEventModelStatic; export class RabbitMQControllerError extends Error { constructor(msg: string) { diff --git a/packages/common-common/src/rabbitmq/types/chainEventNotificationsCUD.ts b/packages/common-common/src/rabbitmq/types/chainEventNotificationsCUD.ts index 90792696438..a8e5359ce2c 100644 --- a/packages/common-common/src/rabbitmq/types/chainEventNotificationsCUD.ts +++ b/packages/common-common/src/rabbitmq/types/chainEventNotificationsCUD.ts @@ -23,19 +23,16 @@ export const RmqCENotificationCUD: RmqMsgNamespace = { - getInvalidFormatError(chainEventType: any): RmqMsgFormatError { - return new RmqMsgFormatError( - `The following chain-event-type is improperly formatted: ${JSON.stringify( - chainEventType - )}` - ); - }, - - isValidMsgFormat(data: any): data is IRmqMsgCreateCETypeCUD { - return !!( - data.chainEventTypeId && - typeof data.chainEventTypeId === 'string' && - data.cud === 'create' - ); - }, - - checkMsgFormat(data: any): void { - const valid = this.isValidMsgFormat(data); - if (!valid) { - console.log( - `The following chain-event-type is improperly formatted: ${JSON.stringify( - data - )}` - ); - throw this.getInvalidFormatError(data); - } - }, -}; - -export namespace RmqCETypeCUD { - export type RmqMsgType = IRmqMsgCreateCETypeCUD; -} diff --git a/packages/common-common/src/rabbitmq/types/index.ts b/packages/common-common/src/rabbitmq/types/index.ts index f9ce9e90b6c..573bf664049 100644 --- a/packages/common-common/src/rabbitmq/types/index.ts +++ b/packages/common-common/src/rabbitmq/types/index.ts @@ -1,10 +1,8 @@ import type { RmqEntityCUD } from './chainEntityCUD'; import type { RmqCENotificationCUD } from './chainEventNotificationsCUD'; -import type { RmqCETypeCUD } from './chainEventTypeCUD'; export * from './chainEntityCUD'; export * from './chainEventNotificationsCUD'; -export * from './chainEventTypeCUD'; export * from './chainEvents'; export * from './chainEventNotification'; @@ -30,7 +28,6 @@ export class RmqMsgFormatError extends Error { export type TRmqMessages = | RmqEntityCUD.RmqMsgType | RmqCENotificationCUD.RmqMsgType - | RmqCETypeCUD.RmqMsgType | RmqCWEvent.RmqMsgType | RmqCENotification.RmqMsgType | RmqSnapshotEvent.RmqMsgType @@ -47,7 +44,6 @@ export enum RascalPublications { ChainEntityCUDMain = 'ChainEntityCUDMainPublication', ChainEventNotificationsCUDMain = 'ChainEventNotificationsCUDMainPublication', ChainEventNotifications = 'ChainEventNotificationsPublication', - ChainEventTypeCUDMain = 'ChainEventTypeCUDMainPublication', SnapshotProposalNotifications = 'SnapshotProposalNotificationsPublication', SnapshotListener = 'SnapshotListenerPublication', } @@ -57,7 +53,6 @@ export enum RascalSubscriptions { ChainEntityCUDMain = 'ChainEntityCUDMainSubscription', ChainEventNotificationsCUDMain = 'ChainEventNotificationsCUDSubscription', ChainEventNotifications = 'ChainEventNotificationsSubscription', - ChainEventTypeCUDMain = 'ChainEventTypeCUDMainSubscription', SnapshotProposalNotifications = 'SnapshotProposalNotificationsSubscription', SnapshotListener = 'SnapshotListenerSubscription', } @@ -75,7 +70,6 @@ export enum RascalQueues { ChainEntityCUDMain = 'ChainEntityCUDMainQueue', ChainEventNotificationsCUDMain = 'ChainEventNotificationsCUDMainQueue', ChainEventNotifications = 'ChainEventNotificationsQueue', - ChainEventTypeCUDMain = 'ChainEventTypeCUDMainQueue', DeadLetter = 'DeadLetterQueue', SnapshotProposalNotifications = 'SnapshotProposalNotificationsQueue', SnapshotListener = 'SnapshotListenerQueue', @@ -86,7 +80,6 @@ export enum RascalBindings { ChainEntityCUDMain = 'ChainEntityCUDMainBinding', ChainEventNotificationsCUD = 'ChainEventNotificationsCUDBinding', ChainEventNotifications = 'ChainEventNotificationsBinding', - ChainEventType = 'ChainEventTypeBinding', SnapshotProposalNotifications = 'SnapshotProposalNotificationsBinding', SnapshotListener = 'SnapshotListenerBinding', DeadLetter = 'DeadLetterBinding', @@ -97,7 +90,6 @@ export enum RascalRoutingKeys { ChainEntityCUD = 'ChainEntityCUD', ChainEventNotificationsCUD = 'ChainEventNotificationsCUD', ChainEventNotifications = 'ChainEventNotifications', - ChainEventTypeCUD = 'ChainEventTypeCUD', SnapshotProposalNotifications = 'SnapshotProposalNotifications', SnapshotListener = 'SnapshotListener', DeadLetter = 'DeadLetter', diff --git a/packages/commonwealth/client/scripts/controllers/server/chain_entities.ts b/packages/commonwealth/client/scripts/controllers/server/chain_entities.ts index 742591b39f8..7159917c3af 100644 --- a/packages/commonwealth/client/scripts/controllers/server/chain_entities.ts +++ b/packages/commonwealth/client/scripts/controllers/server/chain_entities.ts @@ -1,5 +1,4 @@ /* eslint-disable no-restricted-syntax */ -import app from 'state'; import type { CWEvent, IChainEntityKind, @@ -16,8 +15,9 @@ import type { ProposalType } from 'common-common/src/types'; import { ChainBase, ChainNetwork } from 'common-common/src/types'; import getFetch from 'helpers/getFetch'; import type { ChainInfo } from 'models'; -import { ChainEntity, ChainEvent, ChainEventType } from 'models'; +import { ChainEntity, ChainEvent } from 'models'; import { proposalSlugToChainEntityType } from '../../identifiers'; +import app from 'state'; export function chainToEventNetwork(c: ChainInfo): SupportedNetwork { if (c.base === ChainBase.Substrate) return SupportedNetwork.Substrate; @@ -174,20 +174,9 @@ class ChainEntityController { // eslint-disable-next-line no-continue if (!eventEntity) continue; const [entityKind] = eventEntity; - // create event type - const eventType = new ChainEventType( - `${chain}-${cwEvent.data.kind.toString()}`, - chain, - network, - cwEvent.data.kind.toString() - ); // create event - const event = new ChainEvent( - cwEvent.blockNumber, - cwEvent.data, - eventType - ); + const event = new ChainEvent(cwEvent.blockNumber, cwEvent.data); // create entity const fieldName = getUniqueEntityKey(network, entityKind); diff --git a/packages/commonwealth/client/scripts/controllers/server/notifications.ts b/packages/commonwealth/client/scripts/controllers/server/notifications.ts index 452bf2bc914..5ef99079a72 100644 --- a/packages/commonwealth/client/scripts/controllers/server/notifications.ts +++ b/packages/commonwealth/client/scripts/controllers/server/notifications.ts @@ -2,7 +2,7 @@ import $ from 'jquery'; import { redraw } from 'mithrilInterop'; -import { ChainEventType, Notification, NotificationSubscription } from 'models'; +import { Notification, NotificationSubscription } from 'models'; import { modelFromServer } from 'models/NotificationSubscription'; import { EventEmitter } from 'events'; @@ -44,6 +44,9 @@ interface NotifOptions { class NotificationsController { private _discussionStore: NotificationStore = new NotificationStore(); private _chainEventStore: NotificationStore = new NotificationStore(); + // these are the chains that chain-events has active listeners for (used to detemine what chains are shown on the + // notification settings page + private _chainEventSubscribedChainIds: string[] = []; private _maxChainEventNotificationId: number = Number.POSITIVE_INFINITY; private _maxDiscussionNotificationId: number = Number.POSITIVE_INFINITY; @@ -54,6 +57,10 @@ class NotificationsController { public isLoaded = new EventEmitter(); public isUpdated = new EventEmitter(); + public get chainEventSubscribedChainIds(): string[] { + return this._chainEventSubscribedChainIds; + } + public get numPages(): number { return this._numPages; } @@ -88,7 +95,6 @@ class NotificationsController { if (subscription) { return this.enableSubscriptions([subscription]); } else { - // TODO: Change to POST /subscription return post( '/createSubscription', { @@ -97,7 +103,7 @@ class NotificationsController { is_active: true, }, (result) => { - const newSubscription = NotificationSubscription.fromJSON(result); + const newSubscription = modelFromServer(result); if (newSubscription.category === 'chain-event') app.socket.chainEventsNs.addChainEventSubscriptions([ newSubscription, @@ -340,25 +346,13 @@ class NotificationsController { for (const subscriptionJSON of subscriptions) { const subscription = NotificationSubscription.fromJSON(subscriptionJSON); - // save the chainEventType for the subscription if the subscription type is chain-event - let chainEventType = null; - if (subscriptionJSON.ChainEventType) { - chainEventType = ChainEventType.fromJSON( - subscriptionJSON.ChainEventType - ); - } - // save the notification read + notification instances if any for (const notificationsReadJSON of subscriptionJSON.NotificationsReads) { const data = { is_read: notificationsReadJSON.is_read, ...notificationsReadJSON.Notification, }; - const notification = Notification.fromJSON( - data, - subscription, - chainEventType - ); + const notification = Notification.fromJSON(data, subscription); if (subscription.category === 'chain-event') { if (!this._chainEventStore.getById(notification.id)) @@ -393,11 +387,18 @@ class NotificationsController { }); } + public getSubscribedChains() { + return post('/getSubscribedChains', {}, (result) => { + this._chainEventSubscribedChainIds = result.map((x) => x.id); + }); + } + public async refresh() { await Promise.all([ this.getDiscussionNotifications(), this.getChainEventNotifications(), this.getSubscriptions(), + this.getSubscribedChains(), ]); this.isLoaded.emit('redraw'); return Promise.resolve(); diff --git a/packages/commonwealth/client/scripts/controllers/server/socket/chainEventsNs.ts b/packages/commonwealth/client/scripts/controllers/server/socket/chainEventsNs.ts index f7229cac6d1..691dc587528 100644 --- a/packages/commonwealth/client/scripts/controllers/server/socket/chainEventsNs.ts +++ b/packages/commonwealth/client/scripts/controllers/server/socket/chainEventsNs.ts @@ -25,12 +25,12 @@ export class ChainEventsNamespace { public addChainEventSubscriptions(subs: NotificationSubscription[]) { if (this._isConnected) { - const eventTypes = subs.filter((x) => !!x.chainEventTypeId); const roomsToJoin = []; - for (const eventType of eventTypes) { - if (!this.subscriptionRoomsJoined.has(eventType)) { - roomsToJoin.push(eventType); - this.subscriptionRoomsJoined.add(eventType); + for (const sub of subs) { + const chain = sub?.Chain?.id || sub.Chain; + if (!this.subscriptionRoomsJoined.has(chain)) { + roomsToJoin.push(chain); + this.subscriptionRoomsJoined.add(chain); } } if (roomsToJoin.length > 0) { @@ -44,12 +44,12 @@ export class ChainEventsNamespace { public deleteChainEventSubscriptions(subs: NotificationSubscription[]) { if (this._isConnected) { - const eventTypes = subs.filter((x) => !!x.chainEventTypeId); const roomsToLeave = []; - for (const eventType of eventTypes) { - if (this.subscriptionRoomsJoined.has(eventType)) { - roomsToLeave.push(eventType); - this.subscriptionRoomsJoined.delete(eventType); + for (const sub of subs) { + const chain = sub?.Chain?.id || sub.Chain; + if (this.subscriptionRoomsJoined.has(chain)) { + roomsToLeave.push(chain); + this.subscriptionRoomsJoined.delete(chain); } } @@ -64,8 +64,7 @@ export class ChainEventsNamespace { private onChainEvent(notification: ChainEventNotification) { const subscription = app.user.notifications.subscriptions.find( - (sub) => - sub.chainEventTypeId === notification.ChainEvent.ChainEventType.id + (sub) => sub.getChain === notification.ChainEvent.chain ); if (!subscription) { // will theoretically never happen as subscriptions are added/removed on Socket.io as they happen locally diff --git a/packages/commonwealth/client/scripts/models/ChainEvent.ts b/packages/commonwealth/client/scripts/models/ChainEvent.ts index 19eb7efe118..9d21e1b942d 100644 --- a/packages/commonwealth/client/scripts/models/ChainEvent.ts +++ b/packages/commonwealth/client/scripts/models/ChainEvent.ts @@ -1,30 +1,32 @@ -import type { IChainEventData } from 'chain-events/src'; import _ from 'underscore'; -import ChainEventType from './ChainEventType'; +import type { IChainEventData, SupportedNetwork } from 'chain-events/src'; class ChainEvent { public readonly id?: number; public readonly blockNumber: number; public readonly data: IChainEventData; - public readonly type: ChainEventType; + public readonly chain: string; + public readonly network: SupportedNetwork; public eq(e: ChainEvent) { return e.data.kind === this.data.kind && _.isEqual(this.data, e.data); } - constructor(blockNumber, data, type, id?) { + constructor(blockNumber, data, id?, chain?, network?) { this.id = id; this.blockNumber = blockNumber; this.data = data; - this.type = type; + this.chain = chain; + this.network = network; } - public static fromJSON(json, chainEventType?: ChainEventType) { + public static fromJSON(json) { return new ChainEvent( json.blockNumber || json.block_number, json.data || json.event_data, - chainEventType || ChainEventType.fromJSON(json.ChainEventType), - json.id + json.id, + json.chain, + json.network ); } } diff --git a/packages/commonwealth/client/scripts/models/ChainEventType.ts b/packages/commonwealth/client/scripts/models/ChainEventType.ts index 21a29bbf5a2..58bb68cc5ca 100644 --- a/packages/commonwealth/client/scripts/models/ChainEventType.ts +++ b/packages/commonwealth/client/scripts/models/ChainEventType.ts @@ -22,5 +22,3 @@ class ChainEventType { ); } } - -export default ChainEventType; diff --git a/packages/commonwealth/client/scripts/models/DashboardActivityNotification.ts b/packages/commonwealth/client/scripts/models/DashboardActivityNotification.ts index 4c6e604f7b4..19a5259a377 100644 --- a/packages/commonwealth/client/scripts/models/DashboardActivityNotification.ts +++ b/packages/commonwealth/client/scripts/models/DashboardActivityNotification.ts @@ -1,5 +1,4 @@ import type { IChainEventData, SupportedNetwork } from 'chain-events/src'; -import type { ChainEventType } from 'models'; import moment from 'moment'; import { ProfileWithAddress } from '../views/components/component_kit/cw_avatar_group'; @@ -21,7 +20,6 @@ class DashboardActivityNotification { } // Chain Event Notifications - public readonly typeId?: ChainEventType; public readonly blockNumber?: number; public readonly eventData?: IChainEventData; public readonly chainEventId?: number; @@ -39,7 +37,6 @@ class DashboardActivityNotification { likeCount, commentCount, isRead, - typeId, blockNumber, eventData, id, @@ -58,7 +55,6 @@ class DashboardActivityNotification { likeCount?: number; commentCount?: number; isRead?: boolean; - typeId?: ChainEventType; blockNumber?: number; eventData?: IChainEventData; id?: number; @@ -77,7 +73,6 @@ class DashboardActivityNotification { this.likeCount = likeCount; this.commentCount = commentCount; this._isRead = isRead; - this.typeId = typeId; this.blockNumber = blockNumber; this.eventData = eventData; this.chainEventId = id; @@ -99,7 +94,6 @@ class DashboardActivityNotification { likeCount: json.like_count, commentCount: json.comment_count, isRead: json.is_read, - typeId: json.chain_event_type_id, blockNumber: json.block_number, eventData: json.event_data, id: json.id, diff --git a/packages/commonwealth/client/scripts/models/IChainAdapter.ts b/packages/commonwealth/client/scripts/models/IChainAdapter.ts index 70bef03c3ea..ba008a69188 100644 --- a/packages/commonwealth/client/scripts/models/IChainAdapter.ts +++ b/packages/commonwealth/client/scripts/models/IChainAdapter.ts @@ -78,7 +78,7 @@ abstract class IChainAdapter { activeUsers, numVotingThreads, communityBanner, - contracts, + contractsWithTemplatesData, communityRoles, } = response.result; this.app.topics.initialize(topics, true); @@ -86,7 +86,7 @@ abstract class IChainAdapter { this.meta.setAdmins(admins); this.app.recentActivity.setMostActiveUsers(activeUsers); this.meta.setBanner(communityBanner); - this.app.contracts.initialize(contracts, true); + this.app.contracts.initialize(contractsWithTemplatesData, true); // add community roles to the chain's roles this.meta.communityRoles = communityRoles; diff --git a/packages/commonwealth/client/scripts/models/Notification.ts b/packages/commonwealth/client/scripts/models/Notification.ts index 38963c5ab25..6d51262f449 100644 --- a/packages/commonwealth/client/scripts/models/Notification.ts +++ b/packages/commonwealth/client/scripts/models/Notification.ts @@ -1,4 +1,3 @@ -import type { ChainEventType } from 'models/index'; import moment from 'moment'; import ChainEvent from './ChainEvent'; import type NotificationSubscription from './NotificationSubscription'; @@ -32,20 +31,14 @@ class Notification { } } - public static fromJSON( - json, - subscription: NotificationSubscription, - chainEventType?: ChainEventType - ) { + public static fromJSON(json, subscription: NotificationSubscription) { return new Notification( json.id, json.notification_data, json.is_read, json.created_at, subscription, - json?.ChainEvent - ? ChainEvent.fromJSON(json.ChainEvent, chainEventType) - : undefined + json?.ChainEvent ? ChainEvent.fromJSON(json.ChainEvent) : undefined ); } } diff --git a/packages/commonwealth/client/scripts/models/NotificationSubscription.ts b/packages/commonwealth/client/scripts/models/NotificationSubscription.ts index 54429e8711e..60f35e21379 100644 --- a/packages/commonwealth/client/scripts/models/NotificationSubscription.ts +++ b/packages/commonwealth/client/scripts/models/NotificationSubscription.ts @@ -15,7 +15,7 @@ class NotificationSubscription { public readonly Thread: ThreadT; public readonly id?: number; - public readonly chainEventTypeId?: any; + public readonly chainEntityId?: any; private _immediateEmail: boolean; public get immediateEmail() { @@ -35,6 +35,11 @@ class NotificationSubscription { return this._isActive; } + // TODO: should resolve Chain vs chain + public get getChain() { + return this.Chain.id || this.Chain; + } + public enable() { this._isActive = true; } @@ -51,7 +56,6 @@ class NotificationSubscription { createdAt, immediateEmail, Chain?, - ChainEventTypeId?, comment?: CommentT, thread?: ThreadT ) { @@ -62,7 +66,6 @@ class NotificationSubscription { this.createdAt = moment(createdAt); this._immediateEmail = immediateEmail; this.Chain = Chain; - this.chainEventTypeId = ChainEventTypeId; this.Comment = comment; this.Thread = thread; } @@ -76,7 +79,6 @@ class NotificationSubscription { json.created_at, json.immediate_email, json.chain_id, - json.chain_event_type_id, json.Comment || json.offchain_comment_id, json.Thread || json.offchain_thread_id ); @@ -92,7 +94,6 @@ export const modelFromServer = (subscription: SubscriptionInstance) => { created_at, immediate_email, Chain, - chain_event_type_id, Comment, Thread, } = subscription; @@ -125,7 +126,6 @@ export const modelFromServer = (subscription: SubscriptionInstance) => { created_at, immediate_email, Chain, - chain_event_type_id, modeledComment, modeledThread ); diff --git a/packages/commonwealth/client/scripts/models/index.ts b/packages/commonwealth/client/scripts/models/index.ts index 42ea28c1c83..32a6150697b 100644 --- a/packages/commonwealth/client/scripts/models/index.ts +++ b/packages/commonwealth/client/scripts/models/index.ts @@ -30,7 +30,6 @@ export { default as SearchQuery } from './SearchQuery'; export { default as SearchResult } from './SearchResult'; export { default as SocialAccount } from './SocialAccount'; export { default as StorageModule } from './StorageModule'; -export { default as ChainEventType } from './ChainEventType'; export { default as ChainEvent } from './ChainEvent'; export { default as ChainEntity } from './ChainEntity'; export { default as StarredCommunity } from './StarredCommunity'; diff --git a/packages/commonwealth/client/scripts/views/pages/notification_settings/index.tsx b/packages/commonwealth/client/scripts/views/pages/notification_settings/index.tsx index 00a97d0cdc0..5a12ee1a254 100644 --- a/packages/commonwealth/client/scripts/views/pages/notification_settings/index.tsx +++ b/packages/commonwealth/client/scripts/views/pages/notification_settings/index.tsx @@ -15,7 +15,6 @@ import { CWButton } from '../../components/component_kit/cw_button'; import { PopoverMenu } from '../../components/component_kit/cw_popover/cw_popover_menu'; import { CWTextInput } from '../../components/component_kit/cw_text_input'; import { CWCard } from '../../components/component_kit/cw_card'; -import { CWSpinner } from '../../components/component_kit/cw_spinner'; import { isWindowExtraSmall } from '../../components/component_kit/helpers'; import { User } from '../../components/user/user'; import { PageLoading } from '../loading'; @@ -27,6 +26,7 @@ import { bundleSubs } from './helpers'; import { useCommonNavigate } from 'navigation/helpers'; import useForceRerender from 'hooks/useForceRerender'; import { redraw } from 'mithrilInterop'; +import { NotificationCategories } from 'common-common/src/types'; const emailIntervalFrequencyMap = { never: 'Never', @@ -87,7 +87,28 @@ const NotificationSettingsPage = () => { return ; } - const bundledSubs = bundleSubs(app?.user.notifications.subscriptions); + // bundled discussion subscriptions + const bundledSubs = bundleSubs( + app?.user.notifications.subscriptions.filter( + (x) => x.category !== 'chain-event' + ) + ); + // bundled chain-event subscriptions + const chainEventSubs = bundleSubs( + app?.user.notifications.subscriptions.filter( + (x) => x.category === 'chain-event' + ) + ); + + const subscribedChainIds = + app?.user.notifications.chainEventSubscribedChainIds; + + // chains/communities the user has addresses for but does not have existing subscriptions for + const relevantSubscribedChains = app?.user.addresses + .map((x) => x.chain) + .filter( + (x) => subscribedChainIds.includes(x.id) && !chainEventSubs[x.id] + ); return ( @@ -99,7 +120,110 @@ const NotificationSettingsPage = () => { Notification settings for all new threads, comments, mentions, likes, and chain events in the following communities. -
+ + Chain Events + +
+ + Community + + + Email + + + In-App + +
+ {relevantSubscribedChains.map((chain) => { + return ( +
+
+
+
+ + + {chain.name} + +
+
+ { + handleEmailSubscriptions(false, []); + }} + /> + { + app.user.notifications + .subscribe(NotificationCategories.ChainEvent, chain.id) + .then(() => { + forceRerender(); + }); + }} + /> +
+
+ ); + })} + {Object.entries(chainEventSubs).map(([chainName, subs]) => { + const chainInfo = app.config.chains.getById(chainName); + const hasSomeEmailSubs = subs.some((s) => s.immediateEmail); + const hasSomeInAppSubs = subs.some((s) => s.isActive); + return ( +
+
+
+
+ + + {chainInfo?.name} + +
+
+ { + handleEmailSubscriptions(hasSomeEmailSubs, subs) + }} + /> + { + handleSubscriptions(hasSomeInAppSubs, subs) + }} + /> +
+
+ ); + })} + + Discussion + +
Scheduled Email Digest @@ -122,7 +246,7 @@ const NotificationSettingsPage = () => { onClick: () => { app.user.updateEmailInterval('weekly'); setCurrentFrequency('weekly'); - redraw(); + forceRerender(); }, }, { @@ -130,7 +254,7 @@ const NotificationSettingsPage = () => { onClick: () => { app.user.updateEmailInterval('never'); setCurrentFrequency('never'); - redraw(); + forceRerender(); }, }, ]} @@ -185,7 +309,7 @@ const NotificationSettingsPage = () => { try { app.user.updateEmail(email); setSentEmail(true); - // redraw(); + // forceRerender(); } catch (e) { console.log(e); } diff --git a/packages/commonwealth/client/scripts/views/pages/notifications/notification_row_components.tsx b/packages/commonwealth/client/scripts/views/pages/notifications/notification_row_components.tsx index 90e5d6f5ddf..c77e00ed0fa 100644 --- a/packages/commonwealth/client/scripts/views/pages/notifications/notification_row_components.tsx +++ b/packages/commonwealth/client/scripts/views/pages/notifications/notification_row_components.tsx @@ -28,7 +28,7 @@ export const ChainEventNotificationRow = (props: NotificationRowProps) => { throw new Error('chain event notification does not have expected data'); } - const chainId = notification.chainEvent.type.chain; + const chainId = notification.chainEvent.chain; if (app.isCustomDomain() && chainId !== app.customDomainId()) { return; @@ -36,7 +36,7 @@ export const ChainEventNotificationRow = (props: NotificationRowProps) => { const chainEvent: CWEvent = { blockNumber: notification.chainEvent.blockNumber, - network: notification.chainEvent.type.eventNetwork, + network: notification.chainEvent.network, data: notification.chainEvent.data, }; diff --git a/packages/commonwealth/client/scripts/views/pages/user_dashboard/user_dashboard_chain_event_row.tsx b/packages/commonwealth/client/scripts/views/pages/user_dashboard/user_dashboard_chain_event_row.tsx index 0b2fa2cc825..28599145a2d 100644 --- a/packages/commonwealth/client/scripts/views/pages/user_dashboard/user_dashboard_chain_event_row.tsx +++ b/packages/commonwealth/client/scripts/views/pages/user_dashboard/user_dashboard_chain_event_row.tsx @@ -48,7 +48,7 @@ export const UserDashboardChainEventRow = ( onClick={(e) => { e.preventDefault(); e.stopPropagation(); - navigate(`/${chain?.id}`, {}, null); + if (chain?.id) navigate(`/${chain?.id}`, {}, null); }} > diff --git a/packages/commonwealth/client/styles/pages/notification_settings/index.scss b/packages/commonwealth/client/styles/pages/notification_settings/index.scss index d7c259b80c8..8ca74a2eaa6 100644 --- a/packages/commonwealth/client/styles/pages/notification_settings/index.scss +++ b/packages/commonwealth/client/styles/pages/notification_settings/index.scss @@ -96,6 +96,18 @@ margin-bottom: 40px; } + .discussion-section-margin { + margin: 30px 0 8px 0; + } + + .chain-events-section-margin { + margin: 0 0 8px 0; + } + + .chain-events-subscriptions-padding { + padding-left: 40px; + } + .column-header-row { @include communityBundleColumns; diff --git a/packages/commonwealth/server-test.ts b/packages/commonwealth/server-test.ts index 852aa1c5ed3..af48510d5a4 100644 --- a/packages/commonwealth/server-test.ts +++ b/packages/commonwealth/server-test.ts @@ -16,7 +16,7 @@ import http from 'http'; import passport from 'passport'; import Rollbar from 'rollbar'; import favicon from 'serve-favicon'; -import setupAPI from 'server/routing/router'; // performance note: this takes 15 seconds +import setupAPI from './server/routing/router'; // performance note: this takes 15 seconds import { TokenBalanceCache } from 'token-balance-cache/src/index'; import { ROLLBAR_SERVER_TOKEN, SESSION_SECRET } from './server/config'; diff --git a/packages/commonwealth/server/CommonwealthConsumer/CommonwealthConsumer.ts b/packages/commonwealth/server/CommonwealthConsumer/CommonwealthConsumer.ts index a0706c189b3..21250558564 100644 --- a/packages/commonwealth/server/CommonwealthConsumer/CommonwealthConsumer.ts +++ b/packages/commonwealth/server/CommonwealthConsumer/CommonwealthConsumer.ts @@ -12,7 +12,6 @@ import { RABBITMQ_URI, ROLLBAR_SERVER_TOKEN } from '../config'; import models from '../database'; import { processChainEntityCUD } from './messageProcessors/chainEntityCUDQueue'; import { processChainEventNotificationsCUD } from './messageProcessors/chainEventNotificationsCUDQueue'; -import { processChainEventTypeCUD } from './messageProcessors/chainEventTypeCUDQueue'; import { processSnapshotMessage } from './messageProcessors/snapshotConsumer'; const log = factory.getLogger(formatFilename(__filename)); @@ -59,12 +58,6 @@ export async function setupCommonwealthConsumer(): Promise { msgProcessorContext: context, }; - const ceTypeCUDProcessorRmqSub: RabbitMQSubscription = { - messageProcessor: processChainEventTypeCUD, - subscriptionName: RascalSubscriptions.ChainEventTypeCUDMain, - msgProcessorContext: context, - }; - const snapshotEventProcessorRmqSub: RabbitMQSubscription = { messageProcessor: processSnapshotMessage, subscriptionName: RascalSubscriptions.SnapshotListener, @@ -74,7 +67,6 @@ export async function setupCommonwealthConsumer(): Promise { const subscriptions: RabbitMQSubscription[] = [ chainEntityCUDProcessorRmqSub, ceNotifsCUDProcessorRmqSub, - ceTypeCUDProcessorRmqSub, snapshotEventProcessorRmqSub, ]; diff --git a/packages/commonwealth/server/CommonwealthConsumer/messageProcessors/chainEventNotificationsCUDQueue.ts b/packages/commonwealth/server/CommonwealthConsumer/messageProcessors/chainEventNotificationsCUDQueue.ts index a9b5c674634..55344fc38b4 100644 --- a/packages/commonwealth/server/CommonwealthConsumer/messageProcessors/chainEventNotificationsCUDQueue.ts +++ b/packages/commonwealth/server/CommonwealthConsumer/messageProcessors/chainEventNotificationsCUDQueue.ts @@ -29,16 +29,11 @@ export async function processChainEventNotificationsCUD( dbNotification = await emitNotifications( this.models, NotificationCategories.ChainEvent, - chainEvent.ChainEventType.id, + data.ChainEvent.chain, + chainEvent, { chainEvent, - chainEventType: chainEvent.ChainEventType, - chain_id: chainEvent.ChainEventType.chain, - }, - { - chainEvent, - chainEventType: chainEvent.ChainEventType, - chain: chainEvent.ChainEventType.chain, + chain: chainEvent.chain, }, data.event.excludeAddresses, data.event.includeAddresses diff --git a/packages/commonwealth/server/CommonwealthConsumer/messageProcessors/chainEventTypeCUDQueue.ts b/packages/commonwealth/server/CommonwealthConsumer/messageProcessors/chainEventTypeCUDQueue.ts deleted file mode 100644 index dd89378aea0..00000000000 --- a/packages/commonwealth/server/CommonwealthConsumer/messageProcessors/chainEventTypeCUDQueue.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { RmqCETypeCUD } from 'common-common/src/rabbitmq/types/chainEventTypeCUD'; -import type { Logger } from 'typescript-logging'; -import type { DB } from '../../models'; - -export type Ithis = { - models: DB; - log: Logger; -}; - -export async function processChainEventTypeCUD( - this: Ithis, - data: RmqCETypeCUD.RmqMsgType -) { - RmqCETypeCUD.checkMsgFormat(data); - - try { - await this.models.ChainEventType.create({ - id: data.chainEventTypeId, - }); - } catch (e) { - this.log.error( - `An error occurred while saving the ${data.chainEventTypeId} chain-event-type`, - e - ); - throw e; - } -} diff --git a/packages/commonwealth/server/database.ts b/packages/commonwealth/server/database.ts index 4a0dc7de370..5f285ccc445 100644 --- a/packages/commonwealth/server/database.ts +++ b/packages/commonwealth/server/database.ts @@ -9,7 +9,6 @@ import ChainFactory from './models/chain'; import ChainCategoryFactory from './models/chain_category'; import ChainCategoryTypeFactory from './models/chain_category_type'; import ChainEntityMetaFactory from './models/chain_entity_meta'; -import ChainEventTypeFactory from './models/chain_event_type'; import ChainNodeFactory from './models/chain_node'; import ChatChannelFactory from './models/chat_channel'; import ChatMessageFactory from './models/chat_message'; @@ -87,7 +86,6 @@ const models: Models = { ChainNode: ChainNodeFactory(sequelize, DataTypes), ChatChannel: ChatChannelFactory(sequelize, DataTypes), ChainEntityMeta: ChainEntityMetaFactory(sequelize, DataTypes), - ChainEventType: ChainEventTypeFactory(sequelize, DataTypes), ChatMessage: ChatMessageFactory(sequelize, DataTypes), Collaboration: CollaborationFactory(sequelize, DataTypes), Contract: ContractFactory(sequelize, DataTypes), diff --git a/packages/commonwealth/server/migrations/20221226212006-remove-unused-entity-id.js b/packages/commonwealth/server/migrations/20221226212006-remove-unused-entity-id.js index 72ece171415..7e81e8fdf54 100644 --- a/packages/commonwealth/server/migrations/20221226212006-remove-unused-entity-id.js +++ b/packages/commonwealth/server/migrations/20221226212006-remove-unused-entity-id.js @@ -7,7 +7,7 @@ module.exports = { down: async (queryInterface, Sequelize) => { await queryInterface.addColumn('Subscriptions', 'chain_entity_id', { - type: Sequelize.INTEGER, + type: Sequelize.STRING, allowNull: true, references: { model: 'ChainEntityMeta', key: 'id' }, }); diff --git a/packages/commonwealth/server/migrations/20221226221226-delete-existing-ce-subs-notifs.js b/packages/commonwealth/server/migrations/20221226221226-delete-existing-ce-subs-notifs.js new file mode 100644 index 00000000000..b96bdb83bf9 --- /dev/null +++ b/packages/commonwealth/server/migrations/20221226221226-delete-existing-ce-subs-notifs.js @@ -0,0 +1,293 @@ +'use strict'; + +module.exports = { + up: async (queryInterface, Sequelize) => { + await queryInterface.sequelize.transaction(async (t) => { + ///////// Notifications and NotificationsRead + + // create relevant indexes + await queryInterface.sequelize.query( + ` + CREATE INDEX idx_notifications_read_notification_id ON "NotificationsRead" (notification_id); + `, + { transaction: t, raw: true } + ); + await queryInterface.sequelize.query( + ` + CREATE INDEX idx_notifications_category_id ON "Notifications" (category_id); + `, + { transaction: t, raw: true } + ); + await queryInterface.sequelize.query( + ` + CREATE INDEX idx_subscriptions_category_id ON "Subscriptions" (category_id); + `, + { transaction: t, raw: true } + ); + + console.log('Indexes created'); + + // delete notification reads that are associated with chain-event notifications + await queryInterface.sequelize.query( + ` + DELETE FROM "NotificationsRead" nr + USING "Notifications" n + WHERE nr.notification_id = n.id AND n.category_id = 'chain-event'; + `, + { + transaction: t, + raw: true, + } + ); + console.log('NR deleted'); + + await queryInterface.sequelize.query( + ` + ALTER TABLE "Notifications" RENAME TO "OldNotifications"; + `, + { transaction: t, raw: true } + ); + + console.log('Notifications table renamed'); + + await queryInterface.sequelize.query( + ` + CREATE TABLE "Notifications" AS + SELECT * + FROM "OldNotifications" + WHERE category_id <> 'chain-event'; + `, + { transaction: t, raw: true } + ); + console.log('New notifications table created'); + + await queryInterface.dropTable('OldNotifications', { + transaction: t, + raw: true, + cascade: true, + }); + console.log('Old notifications table dropped'); + + await queryInterface.sequelize.query( + ` + ALTER TABLE "Notifications" + ADD CONSTRAINT "Notifications_category_id_fkey" + FOREIGN KEY (category_id) REFERENCES "NotificationCategories"(name); + `, + { transaction: t, raw: true } + ); + console.log('category key added'); + + await queryInterface.sequelize.query( + ` + ALTER TABLE "Notifications" + ADD CONSTRAINT "Notifications_chain_id_fkey" + FOREIGN KEY (chain_id) REFERENCES "Chains"(id); + `, + { transaction: t, raw: true } + ); + console.log('chain key added'); + + await queryInterface.sequelize.query( + ` + ALTER TABLE "Notifications" + ADD CONSTRAINT "Notifications_pkey1" PRIMARY KEY (id); + `, + { transaction: t, raw: true } + ); + console.log('primary key added'); + + await queryInterface.sequelize.query( + ` + CREATE SEQUENCE "Notifications_id_seq1"; + `, + { transaction: t, raw: true } + ); + console.log('Sequence created'); + + await queryInterface.sequelize.query( + ` + SELECT setval('"Notifications_id_seq1"', (SELECT MAX(id) FROM "Notifications")); + `, + { transaction: t, raw: true } + ); + console.log('Sequence set'); + + await queryInterface.sequelize.query( + ` + ALTER TABLE "Notifications" + ALTER COLUMN id SET DEFAULT nextval('"Notifications_id_seq1"'); + `, + { transaction: t, raw: true } + ); + console.log('Sequence default'); + + await queryInterface.sequelize.query( + ` + ALTER TABLE "Notifications" + ADD CONSTRAINT "Notifications_unique_chain_event_id" UNIQUE (chain_event_id); + `, + { transaction: t, raw: true } + ); + console.log('Added unique constraint'); + + await queryInterface.sequelize.query( + ` + CREATE INDEX new_chain_event_id ON "Notifications" (chain_event_id); + `, + { transaction: t, raw: true } + ); + console.log('Added new_chain_event_id index'); + + await queryInterface.sequelize.query( + ` + ALTER TABLE "NotificationsRead" + ADD CONSTRAINT "NotificationsRead_notification_id_fkey" -- create a new foreign key constraint + FOREIGN KEY (notification_id) REFERENCES "Notifications" (id); + `, + { transaction: t, raw: true } + ); + console.log('Added NotificationsRead notification_id foreign key'); + + ///////////////////// Subscriptions ///////////////////////////////////// + + await queryInterface.sequelize.query( + ` + ALTER TABLE "Subscriptions" RENAME TO "OldSubscriptions"; + `, + { transaction: t, raw: true } + ); + console.log('Subscriptions table renamed'); + + await queryInterface.sequelize.query( + ` + CREATE TABLE "Subscriptions" AS + SELECT * + FROM "OldSubscriptions" + WHERE category_id <> 'chain-event'; + `, + { transaction: t, raw: true } + ); + console.log('New subscriptions table created'); + + await queryInterface.dropTable('OldSubscriptions', { + transaction: t, + raw: true, + cascade: true, + }); + console.log('Old subscriptions table dropped'); + + await queryInterface.sequelize.query( + ` + ALTER TABLE "Subscriptions" + ADD CONSTRAINT "Subscriptions_category_id_fkey" + FOREIGN KEY (category_id) REFERENCES "NotificationCategories"(name); + `, + { transaction: t, raw: true } + ); + console.log('category key added'); + + await queryInterface.sequelize.query( + ` + ALTER TABLE "Subscriptions" + ADD CONSTRAINT "Subscriptions_subscriber_id_fkey" + FOREIGN KEY (subscriber_id) REFERENCES "Users"(id); + `, + { transaction: t, raw: true } + ); + console.log('subscriber id key added'); + + await queryInterface.sequelize.query( + ` + ALTER TABLE "Subscriptions" + ADD CONSTRAINT "Subscriptions_pkey" PRIMARY KEY (id); + `, + { transaction: t, raw: true } + ); + console.log('primary key added'); + + await queryInterface.sequelize.query( + ` + CREATE SEQUENCE "Subscriptions_id_seq"; + `, + { transaction: t, raw: true } + ); + console.log('Sequence created'); + + await queryInterface.sequelize.query( + ` + SELECT setval('"Subscriptions_id_seq"', (SELECT MAX(id) FROM "Subscriptions")); + `, + { transaction: t, raw: true } + ); + console.log('Sequence set'); + + await queryInterface.sequelize.query( + ` + ALTER TABLE "Subscriptions" + ALTER COLUMN id SET DEFAULT nextval('"Subscriptions_id_seq"'); + `, + { transaction: t, raw: true } + ); + console.log('Sequence default'); + + await queryInterface.sequelize.query( + ` + CREATE INDEX subscriptions_offchain_thread_id ON "Subscriptions" (offchain_thread_id); + `, + { transaction: t, raw: true } + ); + + await queryInterface.sequelize.query( + ` + ALTER TABLE "NotificationsRead" + ADD CONSTRAINT "NotificationsRead_subscription_id_fkey" + FOREIGN KEY (subscription_id) REFERENCES "Subscriptions"(id); + `, + { transaction: t, raw: true } + ); + console.log('Notifications read subscription id key added'); + + // delete the created indices + await queryInterface.sequelize.query( + ` + DROP INDEX idx_notifications_read_notification_id; + `, + { transaction: t, raw: true } + ); + console.log('Indexes dropped'); + + await queryInterface.sequelize.query( + ` + ALTER TABLE "Subscriptions" + ALTER COLUMN created_at SET NOT NULL, + ALTER COLUMN updated_at SET NOT NULL, + ALTER COLUMN category_id SET NOT NULL, + ALTER COLUMN subscriber_id SET NOT NULL, + ALTER COLUMN object_id SET NOT NULL, + ALTER COLUMN is_active SET NOT NULL, + ALTER COLUMN immediate_email SET NOT NULL, + ALTER COLUMN is_active SET DEFAULT True, + ALTER COLUMN immediate_email SET DEFAULT False; + `, + { transaction: t, raw: true } + ); + console.log('Added NOT NULL and DEFAULT constraints to Subscriptions'); + + await queryInterface.sequelize.query( + ` + ALTER TABLE "Notifications" + ALTER COLUMN created_at SET NOT NULL, + ALTER COLUMN updated_at SET NOT NULL, + ALTER COLUMN category_id SET NOT NULL; + `, + { transaction: t, raw: true } + ); + console.log('Added NOT NULL constraints to Notifications'); + }); + }, + + down: async (queryInterface, Sequelize) => { + // irreversible + }, +}; diff --git a/packages/commonwealth/server/migrations/20230401035238-redo-cet-migration.js b/packages/commonwealth/server/migrations/20230401035238-redo-cet-migration.js new file mode 100644 index 00000000000..0c7c55d598a --- /dev/null +++ b/packages/commonwealth/server/migrations/20230401035238-redo-cet-migration.js @@ -0,0 +1,52 @@ +'use strict'; + +module.exports = { + up: async (queryInterface, Sequelize) => { + await queryInterface.sequelize.transaction(async (t) => { + await queryInterface.removeColumn( + 'Subscriptions', + 'chain_event_type_id', + { + transaction: t, + } + ); + await queryInterface.removeColumn('Subscriptions', 'chain_entity_id', { + transaction: t, + }); + await queryInterface.dropTable('ChainEventTypes', { transaction: t }); + }); + }, + + down: async (queryInterface, Sequelize) => { + await queryInterface.sequelize.transaction(async (t) => { + await queryInterface.createTable( + 'ChainEventTypes', + { + id: { type: Sequelize.STRING, primaryKey: true }, + }, + { transaction: t } + ); + await queryInterface.addColumn( + 'Subscriptions', + 'chain_event_type_id', + { + type: Sequelize.STRING, + allowNull: true, + references: { model: 'ChainEventTypes', key: 'id' }, + }, + { transaction: t } + ); + + await queryInterface.addColumn( + 'Subscriptions', + 'chain_entity_id', + { + type: Sequelize.INTEGER, + allowNull: true, + references: { model: 'ChainEntityMeta', key: 'id' }, + }, + { transaction: t } + ); + }); + }, +}; diff --git a/packages/commonwealth/server/models.ts b/packages/commonwealth/server/models.ts index b39b15ca63a..c278e6f3e4c 100644 --- a/packages/commonwealth/server/models.ts +++ b/packages/commonwealth/server/models.ts @@ -7,7 +7,6 @@ import type { ChainModelStatic } from './models/chain'; import type { ChainCategoryModelStatic } from './models/chain_category'; import type { ChainCategoryTypeModelStatic } from './models/chain_category_type'; import type { ChainEntityMetaModelStatic } from './models/chain_entity_meta'; -import type { ChainEventTypeModelStatic } from './models/chain_event_type'; import type { ChainNodeModelStatic } from './models/chain_node'; import type { ChatChannelModelStatic } from './models/chat_channel'; import type { ChatMessageModelStatic } from './models/chat_message'; @@ -56,7 +55,6 @@ export type Models = { ChainCategory: ChainCategoryModelStatic; ChainCategoryType: ChainCategoryTypeModelStatic; ChainEntityMeta: ChainEntityMetaModelStatic; - ChainEventType: ChainEventTypeModelStatic; ChainNode: ChainNodeModelStatic; ChatChannel: ChatChannelModelStatic; ChatMessage: ChatMessageModelStatic; diff --git a/packages/commonwealth/server/models/chain_event_type.ts b/packages/commonwealth/server/models/chain_event_type.ts deleted file mode 100644 index 47b1a05d52f..00000000000 --- a/packages/commonwealth/server/models/chain_event_type.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type * as Sequelize from 'sequelize'; -import type { DataTypes } from 'sequelize'; -import type { ModelInstance, ModelStatic } from './types'; - -export type ChainEventTypeAttributes = { - id: string; -}; - -export type ChainEventTypeInstance = ModelInstance; - -export type ChainEventTypeModelStatic = ModelStatic; - -export default ( - sequelize: Sequelize.Sequelize, - dataTypes: typeof DataTypes -): ChainEventTypeModelStatic => { - const ChainEventType = sequelize.define( - 'ChainEventType', - { - // id = chain-event_name (event_name is value of string enum) - id: { type: dataTypes.STRING, primaryKey: true }, - }, - { - tableName: 'ChainEventTypes', - timestamps: false, - underscored: true, - indexes: [{ fields: ['id'] }], - } - ); - - ChainEventType.associate = (models) => { - models.ChainEventType.hasMany(models.Subscription, { - foreignKey: 'chain_event_type_id', - }); - }; - - return ChainEventType; -}; diff --git a/packages/commonwealth/server/models/notification.ts b/packages/commonwealth/server/models/notification.ts index cbde97a4ea8..bcda8eb6fbd 100644 --- a/packages/commonwealth/server/models/notification.ts +++ b/packages/commonwealth/server/models/notification.ts @@ -12,6 +12,7 @@ export type NotificationAttributes = { chain_id?: string; category_id: string; chain_event_id?: number; + entity_id: number; created_at?: Date; updated_at?: Date; NotificationsRead?: NotificationsReadAttributes[]; @@ -33,8 +34,8 @@ export default ( id: { type: dataTypes.INTEGER, primaryKey: true, autoIncrement: true }, notification_data: { type: dataTypes.TEXT, allowNull: false }, chain_event_id: { type: dataTypes.INTEGER, allowNull: true }, - // for backwards compatibility of threads associated with OffchainCommunities rather than a proper chain - chain_id: { type: dataTypes.STRING, allowNull: true }, + entity_id: { type: dataTypes.INTEGER, allowNull: true }, + chain_id: { type: dataTypes.STRING, allowNull: true }, // for backwards compatibility of threads associated with OffchainCommunities rather than a proper chain category_id: { type: dataTypes.STRING, allowNull: false }, }, { diff --git a/packages/commonwealth/server/models/subscription.ts b/packages/commonwealth/server/models/subscription.ts index 3f3baad8ddd..80e08b95c44 100644 --- a/packages/commonwealth/server/models/subscription.ts +++ b/packages/commonwealth/server/models/subscription.ts @@ -33,8 +33,6 @@ export type SubscriptionAttributes = { chain_id?: string; offchain_thread_id?: number; offchain_comment_id?: number; - chain_event_type_id?: string; - chain_entity_id?: number; snapshot_id?: string; User?: UserAttributes; @@ -91,8 +89,6 @@ export default ( chain_id: { type: dataTypes.STRING, allowNull: true }, offchain_thread_id: { type: dataTypes.INTEGER, allowNull: true }, offchain_comment_id: { type: dataTypes.INTEGER, allowNull: true }, - chain_event_type_id: { type: dataTypes.STRING, allowNull: true }, - chain_entity_id: { type: dataTypes.INTEGER, allowNull: true }, snapshot_id: { type: Sequelize.STRING, allowNull: true, @@ -132,14 +128,6 @@ export default ( foreignKey: 'offchain_thread_id', targetKey: 'id', }); - models.Subscription.belongsTo(models.ChainEventType, { - foreignKey: 'chain_event_type_id', - targetKey: 'id', - }); - models.Subscription.belongsTo(models.ChainEntityMeta, { - foreignKey: 'chain_entity_id', - targetKey: 'id', - }); models.Subscription.belongsTo(models.Comment, { foreignKey: 'offchain_comment_id', targetKey: 'id', diff --git a/packages/commonwealth/server/routes/deleteChain.ts b/packages/commonwealth/server/routes/deleteChain.ts index ab1407010a8..43d2366ef7a 100644 --- a/packages/commonwealth/server/routes/deleteChain.ts +++ b/packages/commonwealth/server/routes/deleteChain.ts @@ -175,12 +175,6 @@ const deleteChain = async ( transaction: t, }); - // TODO: delete chain-event-types in chain-events - await models.ChainEventType.destroy({ - where: { id: { [Op.like]: `%${chain.id}%` } }, - transaction: t, - }); - // notifications + notifications_read (cascade) await models.Notification.destroy({ where: { chain_id: chain.id }, diff --git a/packages/commonwealth/server/routes/getChainEventServiceData.ts b/packages/commonwealth/server/routes/getChainEventServiceData.ts index b16bdd94b46..7a0b5bc3b83 100644 --- a/packages/commonwealth/server/routes/getChainEventServiceData.ts +++ b/packages/commonwealth/server/routes/getChainEventServiceData.ts @@ -18,7 +18,6 @@ export const getChainEventServiceData = async ( res: Response, next: NextFunction ) => { - console.log(`${JSON.stringify(req.body)}`); if (!req.body.secret) { return next(new AppError(Errors.NeedSecret)); } diff --git a/packages/commonwealth/server/routes/getSubscribedChains.ts b/packages/commonwealth/server/routes/getSubscribedChains.ts index ce79124d8d7..00fa79cbf06 100644 --- a/packages/commonwealth/server/routes/getSubscribedChains.ts +++ b/packages/commonwealth/server/routes/getSubscribedChains.ts @@ -1,7 +1,5 @@ -import { AppError } from 'common-common/src/errors'; -import type { NextFunction, Request, Response } from 'express'; -import { CHAIN_EVENT_SERVICE_SECRET } from '../config'; import type { DB } from '../models'; +import type { NextFunction, Request, Response } from 'express'; export const Errors = { NeedSecret: 'Must provide the secret to use this route', @@ -14,14 +12,6 @@ export const getSubscribedChains = async ( res: Response, next: NextFunction ) => { - if (!req.body.secret) { - return next(new AppError(Errors.NeedSecret)); - } - - if (req.body.secret != CHAIN_EVENT_SERVICE_SECRET) { - return next(new AppError(Errors.InvalidSecret)); - } - const chains = await models.Chain.findAll({ where: { has_chain_events_listener: true }, }); diff --git a/packages/commonwealth/server/routes/subscription/createSubscription.ts b/packages/commonwealth/server/routes/subscription/createSubscription.ts index b22b6edd92f..b822fc22874 100644 --- a/packages/commonwealth/server/routes/subscription/createSubscription.ts +++ b/packages/commonwealth/server/routes/subscription/createSubscription.ts @@ -66,19 +66,6 @@ export default async ( }); if (!comment) return next(new AppError(Errors.NoComment)); obj = { offchain_comment_id: Number(p_id), chain_id: comment.chain }; - } else { - if (!req.body.chain_id) - return next(new AppError(Errors.ChainRequiredForEntity)); - const chainEntityMeta = await models.ChainEntityMeta.findOne({ - where: { - ce_id: req.body.chain_entity_id, - }, - }); - if (!chainEntityMeta) return next(new AppError(Errors.NoChainEntity)); - obj = { - chain_id: chainEntityMeta.chain, - chain_entity_id: chainEntityMeta.ce_id, - }; } break; } @@ -92,27 +79,26 @@ export default async ( }, }); if (!chain) return next(new AppError(Errors.InvalidChain)); - const chainEventType = await models.ChainEventType.findOne({ - where: { - id: req.body.object_id, - }, - }); - if (!chainEventType) - return next(new AppError(Errors.InvalidChainEventId)); - obj = { chain_id: p_entity, chain_event_type_id: req.body.object_id }; + + // object_id = req.body.object_id = [chain_id]_chainEvents + obj = { chain_id: p_entity }; break; } default: return next(new AppError(Errors.InvalidNotificationCategory)); } - const subscription = await models.Subscription.create({ - subscriber_id: req.user.id, - category_id: req.body.category, - object_id: req.body.object_id, - is_active: !!req.body.is_active, - ...obj, - }); + const subscription = ( + await models.Subscription.create({ + subscriber_id: req.user.id, + category_id: req.body.category, + object_id: req.body.object_id, + is_active: !!req.body.is_active, + ...obj, + }) + ).toJSON(); + + subscription.Chain = chain.toJSON(); - return res.json({ status: 'Success', result: subscription.toJSON() }); + return res.json({ status: 'Success', result: subscription }); }; diff --git a/packages/commonwealth/server/routes/subscription/viewSubscriptions.ts b/packages/commonwealth/server/routes/subscription/viewSubscriptions.ts index c45fcd410f3..2517314ab8e 100644 --- a/packages/commonwealth/server/routes/subscription/viewSubscriptions.ts +++ b/packages/commonwealth/server/routes/subscription/viewSubscriptions.ts @@ -32,10 +32,6 @@ export default async ( as: 'Comment', include: [models.Address], }, - { - model: models.ChainEventType, - as: 'ChainEventType', - }, { model: models.Chain, as: 'Chain', diff --git a/packages/commonwealth/server/routes/viewNotifications.ts b/packages/commonwealth/server/routes/viewNotifications.ts index 60f74c08e06..c7c26a2a12d 100644 --- a/packages/commonwealth/server/routes/viewNotifications.ts +++ b/packages/commonwealth/server/routes/viewNotifications.ts @@ -156,11 +156,6 @@ export default async ( NotificationsReads: [], // also contains notifications ...nr.Subscription, }; - // If the Notification is a chain-event notification then save the chain event type - if (nr.Notification.chain_event_id) { - subscriptionsObj[nr.subscription_id].chain_event_type_id = - chainEvent.ChainEventType.id; - } } // push the NotificationsRead + Notifications data to the NotificationsRead array for each subscription diff --git a/packages/commonwealth/server/scripts/emails.ts b/packages/commonwealth/server/scripts/emails.ts index 3a864d11199..c4ea5f8530f 100644 --- a/packages/commonwealth/server/scripts/emails.ts +++ b/packages/commonwealth/server/scripts/emails.ts @@ -1,8 +1,4 @@ -import type { - CWEvent, - IChainEventData, - SupportedNetwork, -} from 'chain-events/src'; +import type { CWEvent } from 'chain-events/src'; import { Label as ChainEventLabel } from 'chain-events/src'; import { factory, formatFilename } from 'common-common/src/logging'; @@ -10,7 +6,14 @@ import { NotificationCategories } from 'common-common/src/types'; import { capitalize } from 'lodash'; import { Op } from 'sequelize'; import { getForumNotificationCopy } from '../../shared/notificationFormatter'; -import type { IPostNotificationData } from '../../shared/types'; +import type { + IPostNotificationData, + IChainEventNotificationData, + IChatNotification, + ICommunityNotificationData, + SnapshotEventType, + SnapshotNotification, +} from '../../shared/types'; import { DynamicTemplate } from '../../shared/types'; import { SENDGRID_API_KEY } from '../config'; import type { UserAttributes } from '../models/user'; @@ -22,31 +25,34 @@ const sgMail = require('@sendgrid/mail'); sgMail.setApiKey(SENDGRID_API_KEY); export const createImmediateNotificationEmailObject = async ( - notification_data, + notification_data: + | IPostNotificationData + | ICommunityNotificationData + | IChainEventNotificationData + | IChatNotification + | (SnapshotNotification & { eventType: SnapshotEventType }), category_id, models ) => { - if (notification_data.chainEvent && notification_data.chainEventType) { + if ( + (notification_data).block_number && + (notification_data).event_data + ) { + const ceInstance = notification_data; // construct compatible CW event from DB by inserting network from type const evt: CWEvent = { - blockNumber: notification_data.chainEvent.block_number, - data: notification_data.chainEvent.event_data as IChainEventData, - network: notification_data.chainEventType - .event_network as SupportedNetwork, + blockNumber: ceInstance.block_number, + data: ceInstance.event_data, + network: ceInstance.network, }; try { - const chainEventLabel = ChainEventLabel( - notification_data.chainEventType.chain, - evt - ); + const chainEventLabel = ChainEventLabel(ceInstance.chain, evt); if (!chainEventLabel) return; const subject = `${ process.env.NODE_ENV !== 'production' ? '[dev] ' : '' - }${chainEventLabel.heading} event on ${capitalize( - notification_data.chainEventType.chain - )}`; + }${chainEventLabel.heading} event on ${capitalize(ceInstance.chain)}`; return { from: 'Commonwealth ', @@ -56,8 +62,8 @@ export const createImmediateNotificationEmailObject = async ( templateId: DynamicTemplate.ImmediateEmailNotification, dynamic_template_data: { notification: { - chainId: notification_data.chainEventType.chain, - blockNumber: notification_data.chainEvent.blockNumber, + chainId: ceInstance.chain, + blockNumber: ceInstance.block_number, subject, label: subject, path: null, diff --git a/packages/commonwealth/server/scripts/enforceDataConsistency.ts b/packages/commonwealth/server/scripts/enforceDataConsistency.ts index ded0d1c671b..6624707aeb6 100644 --- a/packages/commonwealth/server/scripts/enforceDataConsistency.ts +++ b/packages/commonwealth/server/scripts/enforceDataConsistency.ts @@ -17,12 +17,10 @@ import models from '../database'; * throw when it attempts to insert and that message will be retried and eventually dead-lettered unnecessarily. * * @param ce_db_uri {string} The URI of the chain-events database to sync with - * @param enforceEventTypes {boolean} A boolean indicating whether chain-event-types should be synced * @param enforceEntities {boolean} A boolean indicating whether chain-event-entities should be synced */ export async function enforceDataConsistency( ce_db_uri: string, - enforceEventTypes = true, enforceEntities = true ) { // if the function is called with run-as-script i.e. yarn runEnforceDataConsistency ensure that CONFIRM=true is passed @@ -33,18 +31,6 @@ export async function enforceDataConsistency( ); process.exit(0); } - - const chainEventTypeSyncQuery = ` - WITH existingIds AS (SELECT id FROM "ChainEventTypes") - INSERT - INTO "ChainEventTypes" - SELECT "NewCETypes".id - FROM dblink('${ce_db_uri}', - 'SELECT id FROM "ChainEventTypes"') as "NewCETypes"(id varchar(255)) - WHERE "NewCETypes".id NOT IN (SELECT * FROM existingIds) - RETURNING id; - `; - const chainEntitySyncQuery = ` WITH existingCeIds AS (SELECT ce_id FROM "ChainEntityMeta") INSERT INTO "ChainEntityMeta" (ce_id, chain, author, type_id) @@ -56,14 +42,6 @@ export async function enforceDataConsistency( `; await models.sequelize.transaction(async (t) => { - if (enforceEventTypes) { - const result = await models.sequelize.query(chainEventTypeSyncQuery, { - type: QueryTypes.INSERT, - raw: true, - transaction: t, - }); - console.log('ChainEventTypes synced:', result); - } if (enforceEntities) { const result = await models.sequelize.query(chainEntitySyncQuery, { type: QueryTypes.INSERT, diff --git a/packages/commonwealth/server/socket/createNamespace.ts b/packages/commonwealth/server/socket/createNamespace.ts index 3e86489e050..147f145ad05 100644 --- a/packages/commonwealth/server/socket/createNamespace.ts +++ b/packages/commonwealth/server/socket/createNamespace.ts @@ -32,19 +32,16 @@ export function createNamespace(io: Server, namespace: WebsocketNamespaces) { ); }); - socket.on( - WebsocketMessageNames.NewSubscriptions, - (eventTypes: string[]) => { - if (eventTypes.length > 0) { - log.info( - `socket_id = ${socket.id}, user_id = ${ - (socket).user.id - } joining ${JSON.stringify(eventTypes)}` - ); - socket.join(eventTypes); - } + socket.on(WebsocketMessageNames.NewSubscriptions, (chain: string[]) => { + if (chain.length > 0) { + log.info( + `socket_id = ${socket.id}, user_id = ${ + (socket).user.id + } joining ${JSON.stringify(chain)}` + ); + socket.join(chain); } - ); + }); socket.on( WebsocketMessageNames.DeleteSubscriptions, @@ -88,7 +85,7 @@ export async function publishToChainEventsRoom( notification: ChainEventNotification ) { this.server - .to(notification.ChainEvent.ChainEventType.id) + .to(notification.ChainEvent.chain) .emit(WebsocketMessageNames.ChainEventNotification, notification); } diff --git a/packages/commonwealth/server/util/emitNotifications.ts b/packages/commonwealth/server/util/emitNotifications.ts index fd88fd985a9..7c5885103e3 100644 --- a/packages/commonwealth/server/util/emitNotifications.ts +++ b/packages/commonwealth/server/util/emitNotifications.ts @@ -19,6 +19,7 @@ import { import type { WebhookContent } from '../webhookNotifier'; import send from '../webhookNotifier'; import { factory, formatFilename } from 'common-common/src/logging'; +import { SupportedNetwork } from 'chain-events/src'; const log = factory.getLogger(formatFilename(__filename)); @@ -50,8 +51,22 @@ export default async function emitNotifications( }; // typeguard function to differentiate between chain event notifications as needed - const isChainEventData = - (notification_data).chainEvent !== undefined; + let chainEvent: IChainEventNotificationData; + const isChainEventData = !!( + typeof (notification_data).id === 'number' && + typeof (notification_data).block_number === 'number' && + (notification_data).event_data && + Object.values(SupportedNetwork).includes( + (notification_data).network + ) && + (notification_data).chain && + typeof (notification_data).chain === 'string' && + typeof (notification_data).entity_id === 'number' + ); + + if (isChainEventData) { + chainEvent = notification_data; + } // retrieve distinct user ids given a set of addresses const fetchUsersFromAddresses = async ( @@ -101,8 +116,7 @@ export default async function emitNotifications( isChainEventData ? { where: { - chain_event_id: (notification_data) - .chainEvent.id, + chain_event_id: chainEvent.id, }, } : { @@ -115,18 +129,12 @@ export default async function emitNotifications( // if the notification does not yet exist create it here if (!notification) { if (isChainEventData) { - const event: any = (notification_data) - .chainEvent; - event.ChainEventType = (( - notification_data - )).chainEventType; - notification = await models.Notification.create({ - notification_data: JSON.stringify(event), - chain_event_id: (notification_data) - .chainEvent.id, + notification_data: JSON.stringify(chainEvent), + chain_event_id: chainEvent.id, category_id: 'chain-event', - chain_id: (notification_data).chain_id, + chain_id: chainEvent.chain, + entity_id: chainEvent.entity_id, }); } else { notification = await models.Notification.create({ @@ -154,14 +162,6 @@ export default async function emitNotifications( console.trace(e); } - // create NotificationsRead instances - // await models.NotificationsRead.bulkCreate(subscribers.map((subscription) => ({ - // subscription_id: subscription.id, - // notification_id: notification.id, - // is_read: false, - // user_id: subscription.subscriber_id - // }))); - let query = `INSERT INTO "NotificationsRead" VALUES `; const replacements = []; for (const subscription of subscriptions) { @@ -201,14 +201,8 @@ export default async function emitNotifications( // send emails for (const subscription of subscriptions) { - if ( - msg && - isChainEventData && - (notification_data).chainEventType?.chain - ) { - msg.dynamic_template_data.notification.path = `${SERVER_URL}/${ - (notification_data).chainEventType.chain - }/notifications?id=${notification.id}`; + if (msg && isChainEventData && chainEvent.chain) { + msg.dynamic_template_data.notification.path = `${SERVER_URL}/${chainEvent.chain}/notifications?id=${notification.id}`; } if (msg && subscription?.immediate_email && subscription?.User) { // kick off async call and immediately return @@ -234,7 +228,7 @@ export default async function emitNotifications( ) { await send(models, { notificationCategory: category_id, - ...(webhook_data as any), + ...(webhook_data as Required), }); } diff --git a/packages/commonwealth/shared/types.ts b/packages/commonwealth/shared/types.ts index 6d89725c747..7629a39ec78 100644 --- a/packages/commonwealth/shared/types.ts +++ b/packages/commonwealth/shared/types.ts @@ -90,11 +90,7 @@ export interface ICommunityNotificationData { chain: string; } -export interface IChainEventNotificationData { - chainEvent: any; - chainEventType: any; - chain_id: string; -} +export interface IChainEventNotificationData extends ChainEventAttributes {} export interface ISnapshotNotificationData { created_at: Date; diff --git a/packages/commonwealth/test/integration/enforceDataConsistency.spec.ts b/packages/commonwealth/test/integration/enforceDataConsistency.spec.ts index dcf812402c8..497ffca0eca 100644 --- a/packages/commonwealth/test/integration/enforceDataConsistency.spec.ts +++ b/packages/commonwealth/test/integration/enforceDataConsistency.spec.ts @@ -8,56 +8,6 @@ import { enforceDataConsistency } from '../../server/scripts/enforceDataConsiste const { assert } = chai; describe('Tests for enforceDataConsistency script', () => { - it('Should copy chain-event-type ids from the CE db to the main db', async () => { - const cetID = 'test-event-type'; - - // ensure event type doesn't exist in either database - await cwModels.ChainEventType.destroy({ - where: { id: cetID }, - }); - await ceModels.ChainEventType.destroy({ - where: { id: cetID }, - }); - - // ensure type does not already exist in main db - const cwResults = await cwModels.ChainEventType.findAll({ - where: { id: cetID }, - }); - assert.equal(cwResults.length, 0); - - // ensure type does not already exist in ce db - const ceResults = await ceModels.ChainEventType.findAll({ - where: { id: cetID }, - }); - assert.equal(ceResults.length, 0); - - // add test event type to ce db - await ceModels.sequelize.query( - ` - INSERT INTO "ChainEventTypes" (id, chain, event_name, event_network, queued) VALUES - ('${cetID}', 'edgeware', 'some-event-name', 'substrate', -1); - `, - { raw: true, type: QueryTypes.INSERT, logging: console.log } - ); - - // run consistency script - await enforceDataConsistency(CE_DB_URI, true, false); - - // ensure type id has been transferred to cw db - const newResults = await cwModels.ChainEventType.findAll({ - where: { id: cetID }, - }); - assert.equal(newResults.length, 1); - - await cwModels.ChainEventType.destroy({ - where: { id: cetID }, - }); - - await ceModels.ChainEventType.destroy({ - where: { id: cetID }, - }); - }); - it('Should copy chain-entities from the CE db to the main db', async () => { const author = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'; const { id } = await ceModels.ChainEntity.create({ @@ -72,7 +22,7 @@ describe('Tests for enforceDataConsistency script', () => { }); // run consistency script - await enforceDataConsistency(CE_DB_URI, false, true); + await enforceDataConsistency(CE_DB_URI, true); // ensure type id has been transferred to cw db const newResults = await cwModels.ChainEntityMeta.findAll({ diff --git a/packages/commonwealth/test/integration/events/entityArchivalHandler.spec.ts b/packages/commonwealth/test/integration/events/entityArchivalHandler.spec.ts index 1cdcd362eb3..589412fbc2b 100644 --- a/packages/commonwealth/test/integration/events/entityArchivalHandler.spec.ts +++ b/packages/commonwealth/test/integration/events/entityArchivalHandler.spec.ts @@ -1,5 +1,3 @@ -/* eslint-disable no-unused-expressions */ -/* eslint-disable dot-notation */ import chai from 'chai'; import chaiHttp from 'chai-http'; import 'chai/register-should'; @@ -14,8 +12,6 @@ import { MockRabbitMQController } from 'common-common/src/rabbitmq/mockRabbitMQC import { EventEmitter } from 'events'; import type { BrokerConfig } from 'rascal'; -import { resetDatabase } from '../../../server-test'; - chai.use(chaiHttp); const { assert } = chai; @@ -29,10 +25,6 @@ const setupDbEvent = async (event: CWEvent) => { }; describe('Edgeware Archival Event Handler Tests', () => { - before('reset database', async () => { - await resetDatabase(); - }); - it('should create chain entity from event', async () => { const event: CWEvent = { blockNumber: 10, @@ -47,7 +39,6 @@ describe('Edgeware Archival Event Handler Tests', () => { }; const dbEvent = await setupDbEvent(event); - const dbEventType = await dbEvent.getChainEventType(); const eventHandler = new EntityArchivalHandler( models, @@ -96,32 +87,8 @@ describe('Edgeware Archival Event Handler Tests', () => { }; const createDbEvent = await setupDbEvent(createEvent); - const createDbEventType = await createDbEvent.getChainEventType(); const updateDbEvent = await setupDbEvent(updateEvent); - const updateDbEventType = await updateDbEvent.getChainEventType(); - // set up wss expected results - const mockWssServer = new EventEmitter(); - // let nEmissions = 0; - // mockWssServer.on(WebsocketMessageNames.ChainEntity, (payload) => { - // assert.equal(payload.event, WebsocketMessageNames.ChainEntity); - // if (nEmissions === 0) { - // assert.deepEqual(payload.data.chainEvent, createDbEvent.toJSON()); - // assert.deepEqual(payload.data.chainEventType, createDbEventType.toJSON()); - // assert.equal(payload.data.chainEntity.chain, 'edgeware'); - // assert.equal(payload.data.chainEntity.type, SubstrateTypes.EntityKind.TreasuryProposal); - // assert.equal(payload.data.chainEntity.type_id, '5'); - // } else if (nEmissions === 1) { - // assert.deepEqual(payload.data.chainEvent, updateDbEvent.toJSON()); - // assert.deepEqual(payload.data.chainEventType, updateDbEventType.toJSON()); - // assert.equal(payload.data.chainEntity.chain, 'edgeware'); - // assert.equal(payload.data.chainEntity.type, SubstrateTypes.EntityKind.TreasuryProposal); - // assert.equal(payload.data.chainEntity.type_id, '5'); - // } else { - // assert.fail('more than 2 emissions'); - // } - // nEmissions++; - // }); const eventHandler = new EntityArchivalHandler( models, rmqController, diff --git a/packages/commonwealth/test/integration/events/migrationHandler.spec.ts b/packages/commonwealth/test/integration/events/migrationHandler.spec.ts index bbb464369fb..7d6e8d47ec5 100644 --- a/packages/commonwealth/test/integration/events/migrationHandler.spec.ts +++ b/packages/commonwealth/test/integration/events/migrationHandler.spec.ts @@ -1,5 +1,3 @@ -/* eslint-disable no-unused-expressions */ -/* eslint-disable dot-notation */ import chai from 'chai'; import chaiHttp from 'chai-http'; import 'chai/register-should'; @@ -14,8 +12,6 @@ import { getRabbitMQConfig } from 'common-common/src/rabbitmq'; import { MockRabbitMQController } from 'common-common/src/rabbitmq/mockRabbitMQController'; import type { BrokerConfig } from 'rascal'; -import { resetDatabase } from '../../../server-test'; - chai.use(chaiHttp); const { assert } = chai; @@ -29,10 +25,6 @@ const setupDbEvent = async (event: CWEvent) => { }; describe('Edgeware Migration Event Handler Tests', () => { - before('reset database', async () => { - await resetDatabase(); - }); - it('should create new event', async () => { // setup const event: CWEvent = { @@ -60,18 +52,12 @@ describe('Edgeware Migration Event Handler Tests', () => { assert.deepEqual(dbEvent.event_data, event.data); const chainEvents = await models['ChainEvent'].findAll({ where: { - chain_event_type_id: 'edgeware-democracy-started', + chain: 'edgeware', block_number: 10, }, }); assert.lengthOf(chainEvents, 1); assert.deepEqual(chainEvents[0].toJSON(), dbEvent.toJSON()); - - const dbEventType = await dbEvent.getChainEventType(); - const chainEventType = await models['ChainEventType'].findOne({ - where: { id: 'edgeware-democracy-started' }, - }); - assert.deepEqual(chainEventType.toJSON(), dbEventType.toJSON()); }); it('should upgrade existing event', async () => { @@ -111,18 +97,12 @@ describe('Edgeware Migration Event Handler Tests', () => { assert.deepEqual(dbEvent.event_data, currentEvent.data); const chainEvents = await models['ChainEvent'].findAll({ where: { - chain_event_type_id: 'edgeware-preimage-noted', + chain: 'edgeware', block_number: 10, }, }); assert.lengthOf(chainEvents, 1); assert.deepEqual(chainEvents[0].toJSON(), dbEvent.toJSON()); - - const dbEventType = await dbEvent.getChainEventType(); - const chainEventType = await models['ChainEventType'].findOne({ - where: { id: 'edgeware-preimage-noted' }, - }); - assert.deepEqual(chainEventType.toJSON(), dbEventType.toJSON()); }); it('should ignore irrelevant events', async () => { diff --git a/packages/commonwealth/test/integration/events/rabbitmqProducer.spec.ts b/packages/commonwealth/test/integration/events/rabbitmqProducer.spec.ts index 40e100effd5..865b0344a73 100644 --- a/packages/commonwealth/test/integration/events/rabbitmqProducer.spec.ts +++ b/packages/commonwealth/test/integration/events/rabbitmqProducer.spec.ts @@ -1,5 +1,5 @@ import { assert } from 'chai'; -import { RabbitMqHandler } from 'chain-events/services/ChainEventsConsumer/ChainEventHandlers/rabbitMQ'; +import { RabbitMqHandler } from 'chain-events/services/ChainEventsConsumer/ChainEventHandlers'; import type { CWEvent } from 'chain-events/src'; import { RascalPublications, diff --git a/packages/commonwealth/test/integration/events/storageHandler.spec.ts b/packages/commonwealth/test/integration/events/storageHandler.spec.ts index 9179679c84a..754351373b7 100644 --- a/packages/commonwealth/test/integration/events/storageHandler.spec.ts +++ b/packages/commonwealth/test/integration/events/storageHandler.spec.ts @@ -1,5 +1,3 @@ -/* eslint-disable no-unused-expressions */ -/* eslint-disable dot-notation */ import chai from 'chai'; import chaiHttp from 'chai-http'; import 'chai/register-should'; @@ -13,7 +11,6 @@ import { getRabbitMQConfig } from 'common-common/src/rabbitmq'; import { MockRabbitMQController } from 'common-common/src/rabbitmq/mockRabbitMQController'; import type { BrokerConfig } from 'rascal'; -import { resetDatabase } from '../../../server-test'; import * as modelUtils from '../../util/modelUtils'; chai.use(chaiHttp); @@ -28,7 +25,6 @@ let loggedInAddr, loggedInAddrId; describe('Event Storage Handler Tests', () => { before('reset database', async () => { - await resetDatabase(); const result = await modelUtils.createAndVerifyAddress({ chain }); loggedInAddr = result.address; loggedInAddrId = result.address_id; @@ -57,18 +53,12 @@ describe('Event Storage Handler Tests', () => { assert.deepEqual(dbEvent.event_data, event.data); const chainEvents = await models['ChainEvent'].findAll({ where: { - chain_event_type_id: 'edgeware-democracy-started', + chain: 'edgeware', block_number: 10, }, }); assert.lengthOf(chainEvents, 1); assert.deepEqual(chainEvents[0].toJSON(), dbEvent.toJSON()); - - const dbEventType = await dbEvent.getChainEventType(); - const chainEventType = await models['ChainEventType'].findOne({ - where: { id: 'edgeware-democracy-started' }, - }); - assert.deepEqual(chainEventType.toJSON(), dbEventType.toJSON()); }); it('should truncate long preimage args', async () => { @@ -111,7 +101,7 @@ describe('Event Storage Handler Tests', () => { assert.deepEqual(dbEvent.event_data, truncatedData); const chainEvents = await models['ChainEvent'].findAll({ where: { - chain_event_type_id: 'edgeware-preimage-noted', + chain: 'edgeware', block_number: 10, }, }); @@ -138,18 +128,12 @@ describe('Event Storage Handler Tests', () => { assert.deepEqual(dbEvent.event_data, event.data); const chainEvents = await models['ChainEvent'].findAll({ where: { - chain_event_type_id: 'edgeware-democracy-exploded', + chain: 'edgeware', block_number: 13, }, }); assert.lengthOf(chainEvents, 1); assert.deepEqual(chainEvents[0].toJSON(), dbEvent.toJSON()); - - const dbEventType = await dbEvent.getChainEventType(); - const chainEventType = await models['ChainEventType'].findOne({ - where: { id: 'edgeware-democracy-exploded' }, - }); - assert.deepEqual(chainEventType.toJSON(), dbEventType.toJSON()); }); it('should not create chain event for excluded event type', async () => { @@ -172,7 +156,7 @@ describe('Event Storage Handler Tests', () => { assert.isUndefined(dbEvent); const chainEvents = await models['ChainEvent'].findAll({ where: { - chain_event_type_id: 'edgeware-reward', + chain: 'edgeware', block_number: 13, }, }); @@ -200,7 +184,7 @@ describe('Event Storage Handler Tests', () => { assert.deepEqual(dbEvent.event_data, event.data); const chainEvents = await models['ChainEvent'].findAll({ where: { - chain_event_type_id: 'edgeware-bonded', + chain: 'edgeware', block_number: 14, }, }); @@ -229,7 +213,7 @@ describe('Event Storage Handler Tests', () => { assert.isUndefined(dbEvent); const chainEvents = await models['ChainEvent'].findAll({ where: { - chain_event_type_id: 'edgeware-bonded', + chain: 'edgeware', block_number: 15, }, }); diff --git a/packages/commonwealth/test/integration/events/util/index.ts b/packages/commonwealth/test/integration/events/util/index.ts deleted file mode 100644 index 62f358727b2..00000000000 --- a/packages/commonwealth/test/integration/events/util/index.ts +++ /dev/null @@ -1,63 +0,0 @@ -import StorageHandler from 'chain-events/services/ChainEventsConsumer/ChainEventHandlers/storage'; -import ceModels from 'chain-events/services/database/database'; -import type { CWEvent } from 'chain-events/src'; -import { - getRabbitMQConfig, - RabbitMQController, -} from 'common-common/src/rabbitmq'; -import { NotificationCategories } from 'common-common/src/types'; -import { RABBITMQ_URI } from '../../../../server/config'; -import models from '../../../../server/database'; - -interface ISetupSubscriptionsFunction { - (email: string, address: string, chain: string): Promise; -} - -const setupSubscriptions: ISetupSubscriptionsFunction = async ( - email: string, - address: string, - chain: string -) => { - const user = await models['User'].create({ - email, - emailVerified: true, - isAdmin: false, - lastVisited: '{}', - }); - - await models['Address'].create({ - user_id: user.id, - address, - chain, - // selected: true, - verification_token: 'PLACEHOLDER', - verification_token_expires: null, - verified: new Date(), - created_at: new Date(), - updated_at: new Date(), - }); - - await models['Subscription'].create({ - subscriber_id: user.id, - category_id: NotificationCategories.ChainEvent, - object_id: 'edgeware-democracy-started', - is_active: true, - }); - - await models['Subscription'].create({ - subscriber_id: user.id, - category_id: NotificationCategories.ChainEvent, - object_id: 'edgeware-slash', - is_active: true, - }); - - return user.id; -}; - -const setupDbEvent = async (event: CWEvent) => { - const controller = new RabbitMQController(getRabbitMQConfig(RABBITMQ_URI)); - const storageHandler = new StorageHandler(ceModels, controller, 'edgeware'); - return storageHandler.handle(event); -}; - -export { setupSubscriptions, setupDbEvent }; diff --git a/packages/commonwealth/test/systemTests/consumer.test.ts b/packages/commonwealth/test/systemTests/consumer.test.ts index 6c510f34758..5f7c69b6c6e 100644 --- a/packages/commonwealth/test/systemTests/consumer.test.ts +++ b/packages/commonwealth/test/systemTests/consumer.test.ts @@ -48,39 +48,6 @@ describe('Tests for the commonwealth-app consumer', () => { await new Promise((resolve) => setTimeout(resolve, 10000)); }); - it('Should process chain-event-type messages from the ChainEventTypeCUD queue', async () => { - const cetCUD = { - chainEventTypeId: uuidv4(), - cud: 'create', - }; - - const publishJson = await publishRmqMsg( - RABBITMQ_API_URI, - RascalExchanges.CUD, - RascalRoutingKeys.ChainEventTypeCUD, - cetCUD - ); - - // ensure the event was properly published - expect(publishJson.routed, 'Failed to publish message').to.be.true; - - // give time for the consumer to process the message - await new Promise((resolve) => setTimeout(resolve, 2000)); - - const dbResult = await models.ChainEventType.findOne({ - where: { - id: cetCUD.chainEventTypeId, - }, - }); - expect(dbResult).to.not.be.null; - - await models.ChainEventType.destroy({ - where: { - id: cetCUD.chainEventTypeId, - }, - }); - }); - it('Should process chain-event-notification messages from the CENotificationsCUD queue', async () => { const ceData: ITransfer = { kind: EventKind.Transfer, @@ -111,11 +78,11 @@ describe('Tests for the commonwealth-app consumer', () => { const chainEvent: ChainEventAttributes = { id: maxCeId + 1, - chain_event_type_id: cet.id, block_number: cwEvent.blockNumber, event_data: ceData, queued: -1, - ChainEventType: cet, + network: cwEvent.network, + chain: cwEvent.chain, }; const ceNotifCUD: RmqCENotificationCUD.RmqMsgType = { diff --git a/scripts/load-env-var.sh b/scripts/load-env-var.sh index 47840247e42..bba6d90a244 100755 --- a/scripts/load-env-var.sh +++ b/scripts/load-env-var.sh @@ -19,6 +19,6 @@ load-env-var () { } if [ "${1}" != "--source-only" ]; then - main "${@}" + load-env-var "${@}" fi diff --git a/yarn.lock b/yarn.lock index ecafb6a07c3..80749c4be5c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8206,7 +8206,7 @@ clone-response@^1.0.2: dependencies: mimic-response "^1.0.0" -clone@2.x, clone@^2.0.0, clone@^2.1.1, clone@^2.1.2: +clone@2.x, clone@^2.0.0, clone@^2.1.1: version "2.1.2" resolved "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz" integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= @@ -9295,7 +9295,28 @@ deep-equal@^1.0.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" -deep-equal@^2.0.2, deep-equal@^2.2.0: +deep-equal@^2.0.3: + version "2.0.5" + resolved "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.5.tgz" + integrity sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw== + dependencies: + call-bind "^1.0.0" + es-get-iterator "^1.1.1" + get-intrinsic "^1.0.1" + is-arguments "^1.0.4" + is-date-object "^1.0.2" + is-regex "^1.1.1" + isarray "^2.0.5" + object-is "^1.1.4" + object-keys "^1.1.1" + object.assign "^4.1.2" + regexp.prototype.flags "^1.3.0" + side-channel "^1.0.3" + which-boxed-primitive "^1.0.1" + which-collection "^1.0.1" + which-typed-array "^1.1.2" + +deep-equal@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.0.tgz#5caeace9c781028b9ff459f33b779346637c43e6" integrity sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw== @@ -9318,27 +9339,6 @@ deep-equal@^2.0.2, deep-equal@^2.2.0: which-collection "^1.0.1" which-typed-array "^1.1.9" -deep-equal@^2.0.3: - version "2.0.5" - resolved "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.5.tgz" - integrity sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw== - dependencies: - call-bind "^1.0.0" - es-get-iterator "^1.1.1" - get-intrinsic "^1.0.1" - is-arguments "^1.0.4" - is-date-object "^1.0.2" - is-regex "^1.1.1" - isarray "^2.0.5" - object-is "^1.1.4" - object-keys "^1.1.1" - object.assign "^4.1.2" - regexp.prototype.flags "^1.3.0" - side-channel "^1.0.3" - which-boxed-primitive "^1.0.1" - which-collection "^1.0.1" - which-typed-array "^1.1.2" - deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -11638,7 +11638,7 @@ fast-diff@1.1.2: resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz" integrity sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig== -fast-diff@1.2.0, fast-diff@^1.1.2: +fast-diff@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== @@ -13554,7 +13554,7 @@ is-color-stop@^1.0.0: rgb-regex "^1.0.1" rgba-regex "^1.0.0" -is-core-module@^2.10.0, is-core-module@^2.11.0, is-core-module@^2.5.0, is-core-module@^2.7.0, is-core-module@^2.9.0: +is-core-module@^2.10.0, is-core-module@^2.5.0, is-core-module@^2.7.0: version "2.11.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== @@ -14309,12 +14309,7 @@ js-sha256@^0.9.0: resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== -js-sha3@0.5.7, js-sha3@^0.5.7: - version "0.5.7" - resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" - integrity sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g== - -js-sha3@0.8.0, js-sha3@^0.8.0: +js-sha3@0.5.7, js-sha3@0.8.0, js-sha3@^0.5.7, js-sha3@^0.8.0: version "0.8.0" resolved "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz" integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== @@ -17016,11 +17011,6 @@ param-case@^3.0.4: dot-case "^3.0.4" tslib "^2.0.3" -parchment@2.0.0-dev.2: - version "2.0.0-dev.2" - resolved "https://registry.yarnpkg.com/parchment/-/parchment-2.0.0-dev.2.tgz#9d6fe57b3721317bd1c481ea38ffa9b287d496b8" - integrity sha512-4fgRny4pPISoML08Zp7poi52Dff3E2G1ORTi2D/acJ/RiROdDAMDB6VcQNfBcmehrX5Wixp6dxh6JjLyE5yUNQ== - parchment@^1.1.2, parchment@^1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz" @@ -17231,7 +17221,7 @@ path-key@^3.1.0: resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.6, path-parse@^1.0.7: +path-parse@^1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -18563,15 +18553,6 @@ quick-lru@^4.0.1: resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== -quill-delta@4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/quill-delta/-/quill-delta-4.2.1.tgz#ad4f191cdf3be5079c5dc3991b9603a5cc0db69a" - integrity sha512-Y2nksOj6Q+4hizre8n0dml76vLNGK4/y86EoI1d7rv6EL1bx7DPDYRmqQMPu1UqFQO/uQuVHQ3fOmm4ZSzWrfA== - dependencies: - deep-equal "^1.0.1" - extend "^3.0.2" - fast-diff "1.2.0" - quill-delta@^3.6.2: version "3.6.3" resolved "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz" @@ -18600,7 +18581,7 @@ quill-mention@^2.2.7: dependencies: quill "^1.3.4" -quill@^1.3.4, quill@^1.3.6, quill@^1.3.7: +quill@^1.3.4, quill@^1.3.6, quill@^1.3.7, quill@^2.0.0-dev.4: version "1.3.7" resolved "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz" integrity sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g== @@ -18612,18 +18593,6 @@ quill@^1.3.4, quill@^1.3.6, quill@^1.3.7: parchment "^1.1.4" quill-delta "^3.6.2" -quill@^2.0.0-dev.4: - version "2.0.0-dev.4" - resolved "https://registry.yarnpkg.com/quill/-/quill-2.0.0-dev.4.tgz#130e38efe7a16b3766d767d750c8aacc038945e7" - integrity sha512-9WmMVCEIhf3lDdhzl+i+GBDeDl0BFi65waC4Im8Y4HudEJ9kEEb1lciAz9A8pcDmLMjiMbvz84lNt/U5OBS8Vg== - dependencies: - clone "^2.1.2" - deep-equal "^2.0.2" - eventemitter3 "^4.0.0" - extend "^3.0.2" - parchment "2.0.0-dev.2" - quill-delta "4.2.1" - raf-schd@^4.0.2: version "4.0.3" resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.3.tgz#5d6c34ef46f8b2a0e880a8fcdb743efc5bfdbc1a" @@ -19241,14 +19210,7 @@ resolve-url@^0.2.1: resolved "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@1.17.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" - integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== - dependencies: - path-parse "^1.0.6" - -resolve@^1.0.0, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.3.2, resolve@^1.5.0, resolve@^1.8.1, resolve@^1.9.0: +resolve@1.17.0, resolve@^1.0.0, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.3.2, resolve@^1.5.0, resolve@^1.8.1, resolve@^1.9.0, resolve@^2.0.0-next.4: version "1.20.0" resolved "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -19256,24 +19218,6 @@ resolve@^1.0.0, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14 is-core-module "^2.2.0" path-parse "^1.0.6" -resolve@^1.22.0: - version "1.22.2" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" - integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== - dependencies: - is-core-module "^2.11.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -resolve@^2.0.0-next.4: - version "2.0.0-next.4" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" - integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== - dependencies: - is-core-module "^2.9.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - responselike@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz" @@ -20956,11 +20900,6 @@ supports-hyperlinks@^2.3.0: has-flag "^4.0.0" supports-color "^7.0.0" -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== - svg-tags@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz"