mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-27 01:25:52 -05:00
Merge pull request #3289 from benjiwheeler/join-flow-validate-email-remotely
Join flow validate email remotely
This commit is contained in:
commit
8f11de675d
3 changed files with 100 additions and 7 deletions
|
@ -4,9 +4,9 @@ const React = require('react');
|
||||||
const PropTypes = require('prop-types');
|
const PropTypes = require('prop-types');
|
||||||
import {Formik} from 'formik';
|
import {Formik} from 'formik';
|
||||||
const {injectIntl, intlShape} = require('react-intl');
|
const {injectIntl, intlShape} = require('react-intl');
|
||||||
const emailValidator = require('email-validator');
|
|
||||||
const FormattedMessage = require('react-intl').FormattedMessage;
|
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||||
|
|
||||||
|
const validate = require('../../lib/validate');
|
||||||
const JoinFlowStep = require('./join-flow-step.jsx');
|
const JoinFlowStep = require('./join-flow-step.jsx');
|
||||||
const FormikInput = require('../../components/formik-forms/formik-input.jsx');
|
const FormikInput = require('../../components/formik-forms/formik-input.jsx');
|
||||||
const FormikCheckbox = require('../../components/formik-forms/formik-checkbox.jsx');
|
const FormikCheckbox = require('../../components/formik-forms/formik-checkbox.jsx');
|
||||||
|
@ -75,11 +75,16 @@ class EmailStep extends React.Component {
|
||||||
}
|
}
|
||||||
validateEmail (email) {
|
validateEmail (email) {
|
||||||
if (!email) return this.props.intl.formatMessage({id: 'general.required'});
|
if (!email) return this.props.intl.formatMessage({id: 'general.required'});
|
||||||
const isValidLocally = emailValidator.validate(email);
|
const localResult = validate.validateEmailLocally(email);
|
||||||
if (isValidLocally) {
|
if (!localResult.valid) return this.props.intl.formatMessage({id: localResult.errMsgId});
|
||||||
return null; // TODO: validate email address remotely
|
return validate.validateEmailRemotely(email).then(
|
||||||
|
remoteResult => {
|
||||||
|
if (remoteResult.valid === true) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return this.props.intl.formatMessage({id: 'registration.validationEmailInvalid'});
|
return this.props.intl.formatMessage({id: remoteResult.errMsgId});
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
validateForm () {
|
validateForm () {
|
||||||
return {};
|
return {};
|
||||||
|
@ -119,6 +124,8 @@ class EmailStep extends React.Component {
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
isSubmitting,
|
isSubmitting,
|
||||||
setFieldError,
|
setFieldError,
|
||||||
|
setFieldTouched,
|
||||||
|
setFieldValue,
|
||||||
validateField
|
validateField
|
||||||
} = props;
|
} = props;
|
||||||
return (
|
return (
|
||||||
|
@ -161,7 +168,11 @@ class EmailStep extends React.Component {
|
||||||
validationClassName="validation-full-width-input"
|
validationClassName="validation-full-width-input"
|
||||||
/* eslint-disable react/jsx-no-bind */
|
/* eslint-disable react/jsx-no-bind */
|
||||||
onBlur={() => validateField('email')}
|
onBlur={() => validateField('email')}
|
||||||
onFocus={() => setFieldError('email', null)}
|
onChange={e => {
|
||||||
|
setFieldValue('email', e.target.value);
|
||||||
|
setFieldTouched('email');
|
||||||
|
setFieldError('email', null);
|
||||||
|
}}
|
||||||
/* eslint-enable react/jsx-no-bind */
|
/* eslint-enable react/jsx-no-bind */
|
||||||
onSetRef={this.handleSetEmailRef}
|
onSetRef={this.handleSetEmailRef}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
module.exports = {};
|
module.exports = {};
|
||||||
const api = require('./api');
|
const api = require('./api');
|
||||||
|
const emailValidator = require('email-validator');
|
||||||
|
|
||||||
module.exports.validateUsernameLocally = username => {
|
module.exports.validateUsernameLocally = username => {
|
||||||
if (!username || username === '') {
|
if (!username || username === '') {
|
||||||
|
@ -67,3 +68,36 @@ module.exports.validatePasswordConfirm = (password, passwordConfirm) => {
|
||||||
}
|
}
|
||||||
return {valid: true};
|
return {valid: true};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports.validateEmailLocally = email => {
|
||||||
|
if (!email || email === '') {
|
||||||
|
return {valid: false, errMsgId: 'general.required'};
|
||||||
|
} else if (emailValidator.validate(email)) {
|
||||||
|
return {valid: true};
|
||||||
|
}
|
||||||
|
return ({valid: false, errMsgId: 'registration.validationEmailInvalid'});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.validateEmailRemotely = email => (
|
||||||
|
new Promise(resolve => {
|
||||||
|
api({
|
||||||
|
host: '', // not handled by API; use existing infrastructure
|
||||||
|
params: {email: email},
|
||||||
|
uri: '/accounts/check_email/'
|
||||||
|
}, (err, body, res) => {
|
||||||
|
if (err || res.statusCode !== 200 || !body || body.length < 1 || !body[0].msg) {
|
||||||
|
resolve({valid: false, errMsgId: 'general.apiError'});
|
||||||
|
}
|
||||||
|
switch (body[0].msg) {
|
||||||
|
case 'valid email':
|
||||||
|
resolve({valid: true});
|
||||||
|
break;
|
||||||
|
case 'Scratch is not allowed to send email to this address.': // e.g., bad TLD or block-listed
|
||||||
|
case 'Enter a valid email address.':
|
||||||
|
default:
|
||||||
|
resolve({valid: false, errMsgId: 'registration.validationEmailInvalid'});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
|
@ -63,4 +63,52 @@ describe('unit test lib/validate.js', () => {
|
||||||
response = validate.validatePasswordConfirm('', 'abcdefg');
|
response = validate.validatePasswordConfirm('', 'abcdefg');
|
||||||
expect(response).toEqual({valid: false, errMsgId: 'registration.validationPasswordConfirmNotEquals'});
|
expect(response).toEqual({valid: false, errMsgId: 'registration.validationPasswordConfirmNotEquals'});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('validate email address locally', () => {
|
||||||
|
let response;
|
||||||
|
expect(typeof validate.validateEmailLocally).toBe('function');
|
||||||
|
|
||||||
|
// permitted addresses:
|
||||||
|
response = validate.validateEmailLocally('abc@def.com');
|
||||||
|
expect(response).toEqual({valid: true});
|
||||||
|
response = validate.validateEmailLocally('abcdefghijklmnopqrst@abcdefghijklmnopqrst.info');
|
||||||
|
expect(response).toEqual({valid: true});
|
||||||
|
response = validate.validateEmailLocally('abc-def-ghi@jkl-mno.org');
|
||||||
|
expect(response).toEqual({valid: true});
|
||||||
|
response = validate.validateEmailLocally('_______@example.com');
|
||||||
|
expect(response).toEqual({valid: true});
|
||||||
|
response = validate.validateEmailLocally('email@example.museum');
|
||||||
|
expect(response).toEqual({valid: true});
|
||||||
|
response = validate.validateEmailLocally('email@example.co.jp');
|
||||||
|
expect(response).toEqual({valid: true});
|
||||||
|
|
||||||
|
// non-permitted addresses:
|
||||||
|
response = validate.validateEmailLocally('');
|
||||||
|
expect(response).toEqual({valid: false, errMsgId: 'general.required'});
|
||||||
|
response = validate.validateEmailLocally('a');
|
||||||
|
expect(response).toEqual({valid: false, errMsgId: 'registration.validationEmailInvalid'});
|
||||||
|
response = validate.validateEmailLocally('abc@def');
|
||||||
|
expect(response).toEqual({valid: false, errMsgId: 'registration.validationEmailInvalid'});
|
||||||
|
response = validate.validateEmailLocally('abc@def.c');
|
||||||
|
expect(response).toEqual({valid: false, errMsgId: 'registration.validationEmailInvalid'});
|
||||||
|
response = validate.validateEmailLocally('abc😄def@emoji.pizza');
|
||||||
|
expect(response).toEqual({valid: false, errMsgId: 'registration.validationEmailInvalid'});
|
||||||
|
response = validate.validateEmailLocally('あいうえお@example.com');
|
||||||
|
expect(response).toEqual({valid: false, errMsgId: 'registration.validationEmailInvalid'});
|
||||||
|
response = validate.validateEmailLocally('Abc..123@example.com');
|
||||||
|
expect(response).toEqual({valid: false, errMsgId: 'registration.validationEmailInvalid'});
|
||||||
|
response = validate.validateEmailLocally('Joe Smith <email@example.com>');
|
||||||
|
expect(response).toEqual({valid: false, errMsgId: 'registration.validationEmailInvalid'});
|
||||||
|
response = validate.validateEmailLocally('email@example@example.com');
|
||||||
|
expect(response).toEqual({valid: false, errMsgId: 'registration.validationEmailInvalid'});
|
||||||
|
response = validate.validateEmailLocally('email@example..com');
|
||||||
|
expect(response).toEqual({valid: false, errMsgId: 'registration.validationEmailInvalid'});
|
||||||
|
|
||||||
|
// edge cases:
|
||||||
|
// these are strictly legal according to email addres spec, but rejected by library we use:
|
||||||
|
response = validate.validateEmailLocally('email@123.123.123.123');
|
||||||
|
expect(response).toEqual({valid: false, errMsgId: 'registration.validationEmailInvalid'});
|
||||||
|
response = validate.validateEmailLocally('much."more unusual"@example.com');
|
||||||
|
expect(response).toEqual({valid: false, errMsgId: 'registration.validationEmailInvalid'});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue