Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build: merge Release 4.41.0 into master #517

Merged
merged 24 commits into from
Oct 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b87cf88
Merge pull request #462 from opengovsg/release-v4.40.0
liangyuanruo Oct 13, 2020
8fc5374
fix(deps): bump angular-resource from 1.8.0 to 1.8.1 (#464)
dependabot[bot] Oct 14, 2020
1488ed3
fix(deps): bump validator from 13.1.1 to 13.1.17 (#463)
dependabot[bot] Oct 14, 2020
996e8ab
fix(deps): bump angular-messages from 1.8.0 to 1.8.1 (#468)
dependabot[bot] Oct 14, 2020
10fe178
refactor: convert remaining res.send to res.json (#455)
mantariksh Oct 15, 2020
dedc0fb
chore(deps-dev): bump jasmine-spec-reporter from 5.0.2 to 6.0.0 (#469)
dependabot[bot] Oct 15, 2020
7c1b4b5
chore(deps-dev): bump supertest from 3.4.2 to 5.0.0 (#473)
dependabot[bot] Oct 15, 2020
23c6171
chore(deps-dev): bump jest from 26.4.2 to 26.5.3 (#474)
dependabot[bot] Oct 15, 2020
f1b42da
feat: validate that webhook does not point back to app (#475)
mantariksh Oct 15, 2020
09fa465
fix: upgrade myinfo-gov-client to v2.0.0 (#461)
liangyuanruo Oct 15, 2020
f988396
feat: return HTTP 200 OK for Bounce collection VersionError (#472)
mantariksh Oct 15, 2020
3f798c9
fix: show decrypt progress modal after 3 seconds (#476)
karrui Oct 16, 2020
77e7507
feat: add billing module to handle /billing endpoints (#398)
karrui Oct 16, 2020
0d0ca5f
fix(deps): bump bson-ext from 2.0.3 to 2.0.5 (#478)
dependabot[bot] Oct 19, 2020
ec35a1e
chore(examples-search): log failures from database (#467)
liangyuanruo Oct 19, 2020
2d42c3d
chore: increase node memory limit to 2GB (#482)
karrui Oct 19, 2020
0768ceb
style: increase min-width of .response-stats (#490)
tshuli Oct 20, 2020
fca5dc3
fix: add additional startsWith('@') check when validating domain (#487)
karrui Oct 20, 2020
d10380d
refactor: use celebrate error handler (#458)
mantariksh Oct 20, 2020
509647e
chore(deps-dev): bump husky from 4.2.5 to 4.3.0 (#480)
dependabot[bot] Oct 20, 2020
56b951b
feat: allow form transfer for email mode forms (#488)
arshadali172 Oct 20, 2020
21a01d7
chore: bump version to 4.41.0
tshuli Oct 20, 2020
36c24e7
fix: missing period for decryption modal
tshuli Oct 20, 2020
058ab66
fix: use mobile view for submission search / filter elements up till xl
tshuli Oct 20, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
595 changes: 311 additions & 284 deletions CHANGELOG.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Dockerfile.development
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ WORKDIR /opt/formsg

ENV CHROMIUM_BIN=/usr/bin/chromium-browser
ENV NODE_ENV=development
ENV NODE_OPTIONS=--max-old-space-size=2048
RUN apk update && apk upgrade && \
# Build dependencies for node_modules
apk add --virtual native-deps \
Expand Down
1,597 changes: 887 additions & 710 deletions package-lock.json

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "FormSG",
"description": "Form Manager for Government",
"version": "4.40.0",
"version": "4.41.0",
"homepage": "https://form.gov.sg",
"authors": [
"FormSG <formsg@data.gov.sg>"
Expand Down Expand Up @@ -65,7 +65,7 @@
"@opengovsg/angular-legacy-sortablejs-maintained": "^1.0.0",
"@opengovsg/angular-recaptcha-fallback": "^5.0.0",
"@opengovsg/formsg-sdk": "0.8.2",
"@opengovsg/myinfo-gov-client": "^1.0.4",
"@opengovsg/myinfo-gov-client": "^2.0.0",
"@opengovsg/ng-file-upload": "^12.2.14",
"@opengovsg/spcp-auth-client": "^1.3.5",
"@sentry/browser": "^5.24.2",
Expand All @@ -77,10 +77,10 @@
"angular-aria": "^1.8.0",
"angular-cookies": "~1.8.1",
"angular-drag-scroll": "^0.2.1",
"angular-messages": "^1.8.0",
"angular-messages": "^1.8.1",
"angular-moment": "~1.2.0",
"angular-permission": "~1.1.1",
"angular-resource": "^1.8.0",
"angular-resource": "^1.8.1",
"angular-sanitize": "^1.8.0",
"angular-translate": "^2.18.2",
"angular-translate-loader-partial": "^2.18.3",
Expand All @@ -95,7 +95,7 @@
"body-parser": "^1.18.3",
"bootstrap": "3.4.1",
"boxicons": "1.8.0",
"bson-ext": "^2.0.3",
"bson-ext": "^2.0.5",
"busboy": "^0.3.1",
"celebrate": "^13.0.3",
"compression": "~1.7.2",
Expand Down Expand Up @@ -155,7 +155,7 @@
"ui-select": "^0.19.8",
"uid-generator": "^2.0.0",
"uuid": "^8.3.0",
"validator": "^13.1.1",
"validator": "^13.1.17",
"web-streams-polyfill": "^2.1.1",
"whatwg-fetch": "^3.4.1",
"winston": "^3.3.3",
Expand Down Expand Up @@ -215,12 +215,12 @@
"google-fonts-plugin": "4.1.0",
"html-loader": "~0.5.5",
"htmlhint": "^0.14.1",
"husky": "^4.2.5",
"husky": "^4.3.0",
"jasmine": "^3.6.1",
"jasmine-core": "^3.1.0",
"jasmine-sinon": "^0.4.0",
"jasmine-spec-reporter": "^5.0.2",
"jest": "^26.4.2",
"jasmine-spec-reporter": "^6.0.0",
"jest": "^26.5.3",
"lint-staged": "^10.4.0",
"maildev": "^1.1.0",
"mini-css-extract-plugin": "^0.5.0",
Expand All @@ -237,7 +237,7 @@
"stylelint-config-prettier": "^8.0.2",
"stylelint-config-standard": "^20.0.0",
"stylelint-prettier": "^1.1.2",
"supertest": "^3.3.0",
"supertest": "^5.0.0",
"supertest-session": "^4.1.0",
"terser-webpack-plugin": "^1.2.3",
"testcafe": "^1.8.6",
Expand Down
116 changes: 11 additions & 105 deletions src/app/controllers/admin-console.server.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,22 @@
/**
* Module dependencies.
*/
const _ = require('lodash')
const mongoose = require('mongoose')
const moment = require('moment-timezone')
const { StatusCodes } = require('http-status-codes')

const getLoginModel = require('../models/login.server.model').default
const getSubmissionModel = require('../models/submission.server.model').default
const getFormStatisticsTotalModel = require('../models/form_statistics_total.server.model')
.default
const getFormModel = require('../models/form.server.model').default
const { createReqMeta } = require('../utils/request')

const Login = getLoginModel(mongoose)
const Submission = getSubmissionModel(mongoose)
const FormStatisticsTotal = getFormStatisticsTotalModel(mongoose)
const Form = getFormModel(mongoose)
const _ = require('lodash')

const logger = require('../../config/logger').createLoggerWithLabel(module)
const { createReqMeta } = require('../utils/request')

// Examples search-specific constants
const PAGE_SIZE = 16 // maximum number of results to return
Expand Down Expand Up @@ -548,8 +546,15 @@ const getExampleFormsUsing = (
})
}

mongoQuery.catch((err) => {
return cb(err, StatusCodes.INTERNAL_SERVER_ERROR, {
mongoQuery.catch((error) => {
logger.error({
message: 'Error with Examples Search query',
meta: {
action: 'getExampleFormsUsing',
},
error,
})
return cb(error, StatusCodes.INTERNAL_SERVER_ERROR, {
message: 'Error in retrieving example forms.',
})
})
Expand Down Expand Up @@ -729,102 +734,3 @@ exports.getSingleExampleFormUsingAggregateCollection = function (req, res) {
},
)
}

exports.getLoginStats = function (req, res) {
let { yr, mth, esrvcId } = req.query
let year = parseInt(yr)
let month = parseInt(mth)

const startOfMonth = moment
.tz([year, month], 'Asia/Singapore')
.startOf('month')
const endOfMonth = moment(startOfMonth).endOf('month')
Login.aggregate(
[
{
$match: {
esrvcId,
created: {
$gte: startOfMonth.toDate(),
$lte: endOfMonth.toDate(),
},
},
},
{
$group: {
_id: {
form: '$form',
admin: '$admin',
authType: '$authType',
},
total: { $sum: 1 },
},
},
{
$lookup: {
from: 'users',
localField: '_id.admin',
foreignField: '_id',
as: 'userInfo',
},
},
{
$unwind: '$userInfo',
},
{
$lookup: {
from: 'forms',
localField: '_id.form',
foreignField: '_id',
as: 'formInfo',
},
},
{
$unwind: '$formInfo',
},
{
$project: {
_id: 0,
adminEmail: '$userInfo.email',
formName: '$formInfo.title',
total: '$total',
formId: '$_id.form',
authType: '$_id.authType',
},
},
],
function (error, loginStats) {
if (error) {
logger.error({
message: 'Failed to retrieve billing records',
meta: {
action: 'getLoginStats',
...createReqMeta(req),
},
error,
})
return res
.status(StatusCodes.INTERNAL_SERVER_ERROR)
.json({ message: 'Error in retrieving billing records' })
} else if (!loginStats) {
return res
.status(StatusCodes.NOT_FOUND)
.json({ message: 'No billing records found' })
} else {
logger.info({
message: `Billing search for ${esrvcId} by ${
req.session.user && req.session.user.email
}`,
meta: {
action: 'getLoginStats',
...createReqMeta(req),
},
})

return res.json({
loginStats,
})
}
},
)
}
4 changes: 2 additions & 2 deletions src/app/controllers/admin-forms.server.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@ function makeModule(connection) {
if (!VALID_UPLOAD_FILE_TYPES.includes(req.body.fileType)) {
return res
.status(StatusCodes.BAD_REQUEST)
.send(`Your file type "${req.body.fileType}" is not supported`)
.json(`Your file type "${req.body.fileType}" is not supported`)
}

s3.createPresignedPost(
Expand Down Expand Up @@ -710,7 +710,7 @@ function makeModule(connection) {
if (!VALID_UPLOAD_FILE_TYPES.includes(req.body.fileType)) {
return res
.status(StatusCodes.BAD_REQUEST)
.send(`Your file type "${req.body.fileType}" is not supported`)
.json(`Your file type "${req.body.fileType}" is not supported`)
}

s3.createPresignedPost(
Expand Down
6 changes: 0 additions & 6 deletions src/app/factories/spcp-myinfo.factory.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
const adminConsole = require('../controllers/admin-console.server.controller')
const spcp = require('../controllers/spcp.server.controller')
const myInfo = require('../controllers/myinfo.server.controller')
const admin = require('../controllers/admin-forms.server.controller')
Expand Down Expand Up @@ -111,7 +110,6 @@ const spcpFactory = ({ isEnabled, props }) => {
appendVerifiedSPCPResponses: spcp.appendVerifiedSPCPResponses,
encryptedVerifiedFields: spcp.encryptedVerifiedFields,
passThroughSpcp: admin.passThroughSpcp,
getLoginStats: adminConsole.getLoginStats,
verifyMyInfoVals: myInfo.verifyMyInfoVals,
returnSpcpRedirectURL: spcp.returnSpcpRedirectURL,
singPassLogin: spcp.singPassLogin(ndiConfig),
Expand All @@ -132,10 +130,6 @@ const spcpFactory = ({ isEnabled, props }) => {
appendVerifiedSPCPResponses: (req, res, next) => next(),
encryptedVerifiedFields: (req, res, next) => next(),
passThroughSpcp: (req, res, next) => next(),
getLoginStats: (req, res) =>
res.json({
loginStats: [],
}),
verifyMyInfoVals: (req, res, next) => next(),
returnSpcpRedirectURL: (req, res) =>
res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ message: errMsg }),
Expand Down
76 changes: 71 additions & 5 deletions src/app/models/login.server.model.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { Model, Mongoose, Schema } from 'mongoose'
import { Mongoose, Schema } from 'mongoose'

import { AuthType, ILoginSchema } from '../../types'
import {
AuthType,
ILoginModel,
ILoginSchema,
LoginStatistic,
} from '../../types'

import { AGENCY_SCHEMA_ID } from './agency.server.model'
import { FORM_SCHEMA_ID } from './form.server.model'
Expand Down Expand Up @@ -47,18 +52,79 @@ const LoginSchema = new Schema<ILoginSchema>(
},
)

LoginSchema.statics.aggregateLoginStats = function (
this: ILoginModel,
esrvcId: string,
gte: Date,
lte: Date,
): Promise<LoginStatistic[]> {
return this.aggregate<LoginStatistic>([
{
$match: {
esrvcId,
created: {
$gte: gte,
$lte: lte,
},
},
},
{
$group: {
_id: {
form: '$form',
admin: '$admin',
authType: '$authType',
},
total: { $sum: 1 },
},
},
{
$lookup: {
from: 'users',
localField: '_id.admin',
foreignField: '_id',
as: 'userInfo',
},
},
{
$unwind: '$userInfo',
},
{
$lookup: {
from: 'forms',
localField: '_id.form',
foreignField: '_id',
as: 'formInfo',
},
},
{
$unwind: '$formInfo',
},
{
$project: {
_id: 0,
adminEmail: '$userInfo.email',
formName: '$formInfo.title',
total: '$total',
formId: '$_id.form',
authType: '$_id.authType',
},
},
]).exec()
}

const compileLoginModel = (db: Mongoose) =>
db.model<ILoginSchema>(LOGIN_SCHEMA_ID, LoginSchema)
db.model<ILoginSchema>(LOGIN_SCHEMA_ID, LoginSchema) as ILoginModel

/**
* Retrieves the Login model on the given Mongoose instance. If the model is
* not registered yet, the model will be registered and returned.
* @param db The mongoose instance to retrieve the Login model from
* @returns The login model
*/
const getLoginModel = (db: Mongoose) => {
const getLoginModel = (db: Mongoose): ILoginModel => {
try {
return db.model(LOGIN_SCHEMA_ID) as Model<ILoginSchema>
return db.model(LOGIN_SCHEMA_ID) as ILoginModel
} catch {
return compileLoginModel(db)
}
Expand Down
Loading