Skip to content

Commit

Permalink
feat(staging): officer verification (#2124)
Browse files Browse the repository at this point in the history
* feat: run sendPasscodeCreationMessage conditionally

* feat: add GovsgVerification model and migration

* chore: add TODO

* feat: implement storePrecreatedPasscode

* refactor: set all fields in govsg_verification to be non-null on db-level

* feat: create an empty GovsgMessages tab

* feat: add table to GovsgMessages

* feat: add GET /campaign/:campaignId/govsg/messages endpoint

* feat: call govsg messages endpoint from frontend

* feat: remove order

* feat: handle button reply

* fix: add missing diff

* feat: allow only whitelisted users to conduct GovsgV

* refactor: wrap <GovsgMessages> in function
  • Loading branch information
jia1 committed Jul 27, 2023
1 parent c8ca8f3 commit b94ce90
Show file tree
Hide file tree
Showing 20 changed files with 811 additions and 14 deletions.
2 changes: 2 additions & 0 deletions backend/src/core/models/initialize-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
GovsgOp,
GovsgTemplate,
GovsgTemplatesAccess,
GovsgVerification,
} from '@govsg/models'
import { UserExperimental } from './user/user-experimental'

Expand Down Expand Up @@ -92,6 +93,7 @@ export const initializeModels = (sequelize: Sequelize): void => {
GovsgTemplate,
GovsgTemplatesAccess,
CampaignGovsgTemplate,
GovsgVerification,
]
const phonebookModels = [ManagedListCampaign]
sequelize.addModels([
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use strict'

module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('govsg_verification', {
id: {
type: Sequelize.DataTypes.BIGINT,
allowNull: false,
primaryKey: true,
autoIncrement: true,
},
govsg_message_id: {
type: Sequelize.DataTypes.BIGINT,
allowNull: false,
references: {
model: 'govsg_messages',
key: 'id',
},
onUpdate: 'CASCADE',
},
passcode_creation_wamid: {
type: Sequelize.DataTypes.STRING(255),
allowNull: false,
},
passcode: {
type: Sequelize.DataTypes.STRING(255),
allowNull: false,
},
created_at: {
type: Sequelize.DataTypes.DATE,
allowNull: false,
},
updated_at: {
type: Sequelize.DataTypes.DATE,
allowNull: false,
},
})
},

down: async (queryInterface, _) => {
await queryInterface.dropTable('govsg_verification')
},
}
50 changes: 50 additions & 0 deletions backend/src/govsg/middlewares/govsg-verification.middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Request, Response } from 'express'
import { GovsgMessage } from '@govsg/models'
import { GovsgVerification } from '@govsg/models/govsg-verification'

export const listMessages = async (
req: Request,
res: Response
): Promise<Response | void> => {
const { campaignId } = req.params
const { offset, limit } = req.query
const { rows, count } = await GovsgMessage.findAndCountAll({
where: {
campaignId: +campaignId,
},
offset: +(offset as string),
limit: +(limit as string),
include: [
{
model: GovsgVerification,
attributes: ['passcode'],
},
],
})
const messages = rows.map((row) => {
const {
agency,
recipient,
officer_name,
recipient_name,
officer_designation,
...remainingParams
} = row.params as any
console.debug(

Check warning on line 33 in backend/src/govsg/middlewares/govsg-verification.middleware.ts

View workflow job for this annotation

GitHub Actions / test-backend

Unexpected console statement

Check warning on line 33 in backend/src/govsg/middlewares/govsg-verification.middleware.ts

View workflow job for this annotation

GitHub Actions / Deploy backend to AWS Elastic Beanstalk / lint-test

Unexpected console statement
`Excluding agency=${agency} and officer_designation=${officer_designation} from params=${row.params}`
)
return {
...row.get({ plain: true }),
name: recipient_name,
mobile: recipient,
data: JSON.stringify(remainingParams),
passcode: row.govsgVerification.passcode,
sent: row.sendAttemptedAt,
officer: officer_name,
}
})
return res.json({
messages,
total_count: count,
})
}
1 change: 1 addition & 0 deletions backend/src/govsg/middlewares/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * as GovsgMiddleware from './govsg.middleware'
export * as GovsgTemplateMiddleware from './govsg-template.middleware'
export * as GovsgStatsMiddleware from './govsg-stats.middleware'
export * as GovsgVerificationMiddleware from './govsg-verification.middleware'
13 changes: 13 additions & 0 deletions backend/src/govsg/models/govsg-message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,32 @@ import {
Column,
DataType,
ForeignKey,
HasOne,
Model,
Table,
} from 'sequelize-typescript'
import { GovsgVerification } from './govsg-verification'

@Table({ tableName: 'govsg_messages', underscored: true, timestamps: true })
export class GovsgMessage extends Model<GovsgMessage> {
@Column({
type: DataType.BIGINT,
autoIncrement: true,
primaryKey: true,
allowNull: false,
})
id: number

@ForeignKey(() => Campaign)
@Column(DataType.BIGINT)
campaignId: number

@BelongsTo(() => Campaign)
campaign: Campaign

@HasOne(() => GovsgVerification)
govsgVerification: GovsgVerification

@Column(DataType.STRING)
recipient: string

Expand Down
33 changes: 33 additions & 0 deletions backend/src/govsg/models/govsg-verification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {
BelongsTo,
Column,
DataType,
ForeignKey,
Model,
Table,
} from 'sequelize-typescript'
import { GovsgMessage } from './govsg-message'

@Table({ tableName: 'govsg_verification', underscored: true, timestamps: true })
export class GovsgVerification extends Model<GovsgVerification> {
@Column({
type: DataType.BIGINT,
autoIncrement: true,
primaryKey: true,
allowNull: false,
})
id: number

@BelongsTo(() => GovsgMessage)
govsgMessage: GovsgMessage

@ForeignKey(() => GovsgMessage)
@Column({ type: DataType.BIGINT, allowNull: false })
govsgMessageId: number

@Column({ type: DataType.STRING, allowNull: false })
passcodeCreationWamid: string

@Column({ type: DataType.STRING, allowNull: false })
passcode: string
}
1 change: 1 addition & 0 deletions backend/src/govsg/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './govsg-message'
export * from './govsg-message-transactional'
export * from './govsg-template'
export * from './govsg-template-access'
export * from './govsg-verification'
15 changes: 15 additions & 0 deletions backend/src/govsg/routes/govsg-campaign.routes.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { GovsgMessageStatus } from '@core/constants'
import {
CampaignMiddleware,
JobMiddleware,
Expand All @@ -7,6 +8,7 @@ import {
GovsgMiddleware,
GovsgStatsMiddleware,
GovsgTemplateMiddleware,
GovsgVerificationMiddleware,
} from '@govsg/middlewares'
import { Joi, Segments, celebrate } from 'celebrate'
import { Router } from 'express'
Expand Down Expand Up @@ -125,4 +127,17 @@ router.get(
GovsgStatsMiddleware.getDeliveredRecipients
)

router.get(
'/messages',
celebrate({
[Segments.QUERY]: Joi.object({
limit: Joi.number().integer().min(1).max(100).default(10),
offset: Joi.number().integer().min(0).default(0),
status: Joi.string().valid(...Object.values(GovsgMessageStatus)),
}),
}),
CampaignMiddleware.canEditCampaign,
GovsgVerificationMiddleware.listMessages
)

export default router
Loading

0 comments on commit b94ce90

Please sign in to comment.