Skip to content

Commit

Permalink
Merge pull request #2058 from opengovsg/refactor/beta/ts
Browse files Browse the repository at this point in the history
refactor(beta): migrate to TypeScript
  • Loading branch information
LoneRifle authored Jun 3, 2021
2 parents f88ad89 + 74a2ec8 commit f02fdb9
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 63 deletions.
1 change: 0 additions & 1 deletion src/public/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,6 @@ require('./modules/forms/services/spcp-session.client.factory.js')
require('./modules/forms/services/submissions.client.factory.js')
require('./modules/forms/services/toastr.client.factory.js')
require('./modules/forms/services/attachment.client.service.js')
require('./modules/forms/services/betas.client.factory.js')
require('./modules/forms/services/captcha.client.service.js')
require('./modules/forms/services/mailto.client.factory.js')

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ angular
'Attachment',
'FormFields',
'$q',
'Betas',
'Auth',
'$state',
'Toastr',
Expand All @@ -43,7 +42,6 @@ function EditFieldsModalController(
Attachment,
FormFields,
$q,
Betas,
Auth,
$state,
Toastr,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict'

const get = require('lodash/get')
const BetaService = require('../../../../services/BetaService')

// Forms controller
angular
Expand All @@ -15,7 +16,6 @@ angular
'$timeout',
'$window',
'Toastr',
'Betas',
ListFormsController,
])

Expand All @@ -29,7 +29,6 @@ function ListFormsController(
$timeout,
$window,
Toastr,
Betas,
) {
const vm = this

Expand Down Expand Up @@ -172,7 +171,7 @@ function ListFormsController(
}

vm.duplicateForm = function (formIndex) {
const missingBetaPermissions = Betas.getMissingFieldPermissions(
const missingBetaPermissions = BetaService.getMissingFieldPermissions(
vm.user,
vm.myforms[formIndex],
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const { groupLogicUnitsByField } = require('shared/util/logic')
const { reorder } = require('shared/util/immutable-array-fns')
const FieldFactory = require('../../helpers/field-factory')
const { UPDATE_FORM_TYPES } = require('../constants/update-form-types')
const BetaService = require('../../../../services/BetaService')

const newFields = new Set() // Adding a fieldTypes will add a "new" label.

Expand Down Expand Up @@ -35,7 +36,6 @@ function editFormDirective() {
'FormFields',
'$uibModal',
'responseModeEnum',
'Betas',
'$window',
'Attachment',
editFormController,
Expand All @@ -48,15 +48,14 @@ function editFormController(
FormFields,
$uibModal,
responseModeEnum,
Betas,
$window,
Attachment,
) {
// For use in component.
$scope.responseModeEnum = responseModeEnum

$scope.isBetaField = function (fieldType) {
return Betas.isBetaField(fieldType)
return BetaService.isBetaField(fieldType)
}

$scope.isNewField = function (fieldType) {
Expand All @@ -65,7 +64,7 @@ function editFormController(

$scope.adminHasAccess = function (fieldType) {
const user = $scope.myform.admin
return Betas.userHasAccessToFieldType(user, fieldType)
return BetaService.userHasAccessToFieldType(user, fieldType)
}

$scope.getFieldTitle = FormFields.getFieldTitle
Expand Down
50 changes: 0 additions & 50 deletions src/public/modules/forms/services/betas.client.factory.js

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
'use strict'
const BetaService = require('../../../services/BetaService')

angular.module('users').directive('examplesCard', [examplesCard])

Expand All @@ -21,7 +22,6 @@ function examplesCard() {
'GTag',
'Auth',
'$location',
'Betas',
'Toastr',
examplesCardController,
],
Expand All @@ -37,7 +37,6 @@ function examplesCardController(
GTag,
Auth,
$location,
Betas,
Toastr,
) {
$scope.user = Auth.getUser()
Expand Down Expand Up @@ -79,7 +78,7 @@ function examplesCardController(
*/

$scope.useTemplate = function () {
const missingBetaPermissions = Betas.getMissingFieldPermissions(
const missingBetaPermissions = BetaService.getMissingFieldPermissions(
$scope.user,
$scope.form,
)
Expand Down
75 changes: 75 additions & 0 deletions src/public/services/BetaService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { get } from 'lodash'

// Re-use the backend types for now so that we have some type safety.
// Given that this is used mostly by JavaScript modules, the lack of
// mongo-specific types should not present a problem.
// Change the types to frontend equivalents as and when available.
import { IField, IForm, IUser } from '../../types'

type BetaFeature = {
flag: string
matches: (field: IField) => boolean
}

type BetaFeaturesDictionary = { [featureName: string]: BetaFeature }

const BETA_FEATURES_FIELD: BetaFeaturesDictionary = {
// This is an example of how to add fields to this object
// featureName: {
// flag: 'betaFlagName',
// matches: (field) =>
// field.fieldType === 'mobile' &&
// (field.isVerifiable === true),
// },
}

const getBetaFeaturesForFields = (
formFields: IField[] | undefined,
betaFeaturesField: BetaFeaturesDictionary,
): string[] => {
const betaFeatures = new Set<string>()
if (formFields) {
for (const field of formFields) {
for (const [name, feature] of Object.entries(betaFeaturesField)) {
if (feature.matches(field)) {
betaFeatures.add(name)
}
}
}
}
return Array.from(betaFeatures)
}

export const getMissingFieldPermissions = (
user: IUser,
form: IForm,
betaFeaturesField = BETA_FEATURES_FIELD,
): string[] => {
const betaFeatures = getBetaFeaturesForFields(
form.form_fields,
betaFeaturesField,
)
return betaFeatures.filter((name) => {
const flag = get(betaFeaturesField, [name, 'flag'])
return flag && !get(user, ['betaFlags', flag], false)
})
}

export const isBetaField = (
fieldType: string,
betaFeaturesField = BETA_FEATURES_FIELD,
): BetaFeature | undefined => {
return betaFeaturesField[fieldType]
}

export const userHasAccessToFieldType = (
user: IUser,
fieldType: string,
betaFeaturesField = BETA_FEATURES_FIELD,
): boolean => {
const flag = get(betaFeaturesField, [fieldType, 'flag'])
return (
!isBetaField(fieldType, betaFeaturesField) ||
(Boolean(flag) && get(user, ['betaFlags', flag], false))
)
}
76 changes: 76 additions & 0 deletions src/public/services/__tests__/BetaService.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { IField, IForm, IUser } from '../../../types'
import * as BetaService from '../BetaService'

describe('BetaService', () => {
const nonBetaFeature = 'nonBetaFeature'
const featureOne = 'featureOne'
const featureTwo = 'featureTwo'
const betaFeaturesField = {
[featureOne]: {
flag: 'betaFlagOne',
matches(field: IField) {
return field.title === featureOne
},
},
[featureTwo]: {
flag: 'betaFlagTwo',
matches(field: IField) {
return field.title === featureTwo
},
},
}
const userWithFeatureOne = {
betaFlags: {
[betaFeaturesField[featureOne].flag]: true,
},
} as IUser
describe('isBetaField', () => {
it('returns truthy for defined beta fields', () => {
const result = BetaService.isBetaField(featureOne, betaFeaturesField)
expect(result).toBeTruthy()
})
it('returns falsy for fields not defined as beta', () => {
const result = BetaService.isBetaField(nonBetaFeature, betaFeaturesField)
expect(result).toBeFalsy()
})
})
describe('userHasAccessToFieldType', () => {
it('returns true for features not defined as beta', () => {
const result = BetaService.userHasAccessToFieldType(
userWithFeatureOne,
nonBetaFeature,
betaFeaturesField,
)
expect(result).toBeTrue()
})
it('returns true for beta features that the user has access to', () => {
const result = BetaService.userHasAccessToFieldType(
userWithFeatureOne,
featureOne,
betaFeaturesField,
)
expect(result).toBeTrue()
})
it('returns false for beta features that the user lacks access to', () => {
const result = BetaService.userHasAccessToFieldType(
userWithFeatureOne,
featureTwo,
betaFeaturesField,
)
expect(result).toBeFalse()
})
})
describe('getBetaFeaturesForFields', () => {
const form = {
form_fields: [{ title: featureTwo }],
} as IForm
it('lists the beta features that the user lacks', () => {
const result = BetaService.getMissingFieldPermissions(
userWithFeatureOne,
form,
betaFeaturesField,
)
expect(result).toStrictEqual([featureTwo])
})
})
})

0 comments on commit f02fdb9

Please sign in to comment.