-
Notifications
You must be signed in to change notification settings - Fork 87
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2058 from opengovsg/refactor/beta/ts
refactor(beta): migrate to TypeScript
- Loading branch information
Showing
8 changed files
with
158 additions
and
63 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]) | ||
}) | ||
}) | ||
}) |