From 75f8c6429abf3b6017aca92803d1f83d92799725 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Thu, 2 Apr 2020 00:12:56 -0400 Subject: [PATCH 1/4] added join token route; handle route alongside existing student signup uri --- src/routes.json | 7 ++++ .../studentregistration.jsx | 42 ++++++++++++++----- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/routes.json b/src/routes.json index ebc57e309..781348730 100644 --- a/src/routes.json +++ b/src/routes.json @@ -280,6 +280,13 @@ "view": "studentregistration/studentregistration", "title": "Class Registration" }, + { + "name": "student-registration-token-only", + "pattern": "^/join/:token", + "routeAlias": "/classes/(complete_registration|.+/register/.+)", + "view": "studentregistration/studentregistration", + "title": "Class Registration" + }, { "name": "teacher-faq", "pattern": "^/educators/faq/?$", diff --git a/src/views/studentregistration/studentregistration.jsx b/src/views/studentregistration/studentregistration.jsx index 5f93d3c73..78443686b 100644 --- a/src/views/studentregistration/studentregistration.jsx +++ b/src/views/studentregistration/studentregistration.jsx @@ -32,11 +32,22 @@ class StudentRegistration extends React.Component { } componentDidMount () { this.setState({waiting: true}); // eslint-disable-line react/no-did-mount-set-state + + // set uri and params + let uri; + let params; + if (this.props.classroomId === null || typeof this.props.classroomId === 'undefined') { + // configure for token-only endpoint + uri = `/classtoken/${this.props.classroomToken}`; + } else { + // configure for endpoint expecting classroomId and token + uri = `/classrooms/${this.props.classroomId}`; + params = {token: this.props.classroomToken}; + } + api({ - uri: `/classrooms/${this.props.classroomId}`, - params: { - token: this.props.classroomToken - } + uri: uri, + params: params }, (err, body, res) => { this.setState({waiting: false}); if (err) { @@ -164,14 +175,23 @@ StudentRegistration.defaultProps = { const IntlStudentRegistration = injectIntl(StudentRegistration); -const [classroomId, _, classroomToken] = document.location.pathname.split('/').filter(p => { - if (p) { - return p; +// parse either format of student registration url: +// "class register": http://scratch.mit.edu/classes/3/register/c0256654e1be +// "join token": http://scratch.mit.edu/join/c025r54ebe +let classroomId = null; +let classroomToken = null; +const classRegisterRegexp = /^\/?classes\/(\d*)\/register\/([a-zA-Z0-9]*)\/?$/; +const classRegisterMatch = classRegisterRegexp.exec(document.location.pathname); +if (classRegisterMatch) { + classroomId = classRegisterMatch[1]; + classroomToken = classRegisterMatch[2]; +} else { + const joinTokenRegexp = /^\/?join\/([a-zA-Z0-9]*)\/?$/; + const joinTokenMatch = joinTokenRegexp.exec(document.location.pathname); + if (joinTokenMatch) { + classroomToken = joinTokenMatch[1]; } - return null; -}) - .slice(-3); - +} const props = {classroomId, classroomToken}; render(, document.getElementById('app')); From 81678b70a75d1c8a85bb3692bc07853ac7374e14 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Fri, 3 Apr 2020 16:11:45 -0400 Subject: [PATCH 2/4] refactored uri pathname parsing to library --- src/lib/route.js | 15 ++++++++ .../studentregistration.jsx | 34 +++---------------- test/unit/lib/route.test.js | 26 ++++++++++++++ 3 files changed, 45 insertions(+), 30 deletions(-) create mode 100644 src/lib/route.js create mode 100644 test/unit/lib/route.test.js diff --git a/src/lib/route.js b/src/lib/route.js new file mode 100644 index 000000000..4eee7620d --- /dev/null +++ b/src/lib/route.js @@ -0,0 +1,15 @@ +module.exports = {}; + +// try to extract classroom token from either of two routes +module.exports.getURIClassroomToken = uriPathname => { + // first try to match /classes/CLASSROOM_ID/register/CLASSROOM_TOKEN + const classRegisterRegexp = /^\/?classes\/\d*\/register\/([a-zA-Z0-9]*)\/?$/; + const classRegisterMatch = classRegisterRegexp.exec(uriPathname); + if (classRegisterMatch) return classRegisterMatch[1]; + // if regex match failed, try to match /join/CLASSROOM_TOKEN + const joinTokenRegexp = /^\/?join\/([a-zA-Z0-9]*)\/?$/; + const joinTokenMatch = joinTokenRegexp.exec(uriPathname); + if (joinTokenMatch) return joinTokenMatch[1]; + // if neither matched + return null; +}; diff --git a/src/views/studentregistration/studentregistration.jsx b/src/views/studentregistration/studentregistration.jsx index 78443686b..35bfee2ed 100644 --- a/src/views/studentregistration/studentregistration.jsx +++ b/src/views/studentregistration/studentregistration.jsx @@ -6,6 +6,7 @@ const React = require('react'); const api = require('../../lib/api'); const injectIntl = require('../../lib/intl.jsx').injectIntl; const intlShape = require('../../lib/intl.jsx').intlShape; +const route = require('../../lib/route'); const Deck = require('../../components/deck/deck.jsx'); const Progression = require('../../components/progression/progression.jsx'); @@ -33,21 +34,8 @@ class StudentRegistration extends React.Component { componentDidMount () { this.setState({waiting: true}); // eslint-disable-line react/no-did-mount-set-state - // set uri and params - let uri; - let params; - if (this.props.classroomId === null || typeof this.props.classroomId === 'undefined') { - // configure for token-only endpoint - uri = `/classtoken/${this.props.classroomToken}`; - } else { - // configure for endpoint expecting classroomId and token - uri = `/classrooms/${this.props.classroomId}`; - params = {token: this.props.classroomToken}; - } - api({ - uri: uri, - params: params + uri: `/classtoken/${this.props.classroomToken}` }, (err, body, res) => { this.setState({waiting: false}); if (err) { @@ -57,7 +45,7 @@ class StudentRegistration extends React.Component { }) }); } - if (res.statusCode === 404) { + if (res.statusCode >= 400) { // TODO: Use react-router for this window.location = '/404'; } @@ -178,20 +166,6 @@ const IntlStudentRegistration = injectIntl(StudentRegistration); // parse either format of student registration url: // "class register": http://scratch.mit.edu/classes/3/register/c0256654e1be // "join token": http://scratch.mit.edu/join/c025r54ebe -let classroomId = null; -let classroomToken = null; -const classRegisterRegexp = /^\/?classes\/(\d*)\/register\/([a-zA-Z0-9]*)\/?$/; -const classRegisterMatch = classRegisterRegexp.exec(document.location.pathname); -if (classRegisterMatch) { - classroomId = classRegisterMatch[1]; - classroomToken = classRegisterMatch[2]; -} else { - const joinTokenRegexp = /^\/?join\/([a-zA-Z0-9]*)\/?$/; - const joinTokenMatch = joinTokenRegexp.exec(document.location.pathname); - if (joinTokenMatch) { - classroomToken = joinTokenMatch[1]; - } -} -const props = {classroomId, classroomToken}; +const props = {classroomToken: route.getURIClassroomToken(document.location.pathname)}; render(, document.getElementById('app')); diff --git a/test/unit/lib/route.test.js b/test/unit/lib/route.test.js new file mode 100644 index 000000000..2acb17599 --- /dev/null +++ b/test/unit/lib/route.test.js @@ -0,0 +1,26 @@ +const route = require('../../../src/lib/route'); + +describe('unit test lib/route.js', () => { + + test('getURIClassroomToken exists', () => { + expect(typeof route.getURIClassroomToken).toBe('function'); + }); + + test('getURIClassroomToken parses URI paths like /classes/21/register/r9n5f5xk', () => { + let response; + response = route.getURIClassroomToken('/classes/21/register/r9n5f5xk'); + expect(response).toEqual('r9n5f5xk'); + }); + + test('getURIClassroomToken parses URI paths like /join/e2dcfkx95', () => { + let response; + response = route.getURIClassroomToken('/join/e2dcfkx95'); + expect(response).toEqual('e2dcfkx95'); + }); + + test('getURIClassroomToken works with trailing slash', () => { + let response; + response = route.getURIClassroomToken('/join/r9n5f5xk/'); + expect(response).toEqual('r9n5f5xk'); + }); +}); From 2972c528f4ccfcea32a22febb74b043ea60162a6 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 6 Apr 2020 10:36:06 -0400 Subject: [PATCH 3/4] use state.classroom.id instead of url param classroomId --- src/views/studentregistration/studentregistration.jsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/views/studentregistration/studentregistration.jsx b/src/views/studentregistration/studentregistration.jsx index 35bfee2ed..6605d6145 100644 --- a/src/views/studentregistration/studentregistration.jsx +++ b/src/views/studentregistration/studentregistration.jsx @@ -75,7 +75,7 @@ class StudentRegistration extends React.Component { ), country: formData.user.country, is_robot: formData.user.isRobot, - classroom_id: this.props.classroomId, + classroom_id: this.state.classroom.id, classroom_token: this.props.classroomToken } }, (err, body, res) => { @@ -100,7 +100,7 @@ class StudentRegistration extends React.Component { }); } handleGoToClass () { - window.location = `/classes/${this.props.classroomId}/`; + window.location = `/classes/${this.state.classroom.id}/`; } render () { const usernameDescription = this.props.intl.formatMessage({id: 'registration.studentUsernameStepDescription'}); @@ -151,13 +151,11 @@ class StudentRegistration extends React.Component { } StudentRegistration.propTypes = { - classroomId: PropTypes.string.isRequired, classroomToken: PropTypes.string.isRequired, intl: intlShape }; StudentRegistration.defaultProps = { - classroomId: null, classroomToken: null }; From 05e61cc842cb3b885f2f5236469ca789e6ddb35a Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Tue, 7 Apr 2020 14:27:07 -0400 Subject: [PATCH 4/4] changed student signup route from /join/TOKEN to /signup/TOKEN --- src/lib/route.js | 8 ++++---- src/routes.json | 4 ++-- src/views/studentregistration/studentregistration.jsx | 2 +- test/unit/lib/route.test.js | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/lib/route.js b/src/lib/route.js index 4eee7620d..ebd94762f 100644 --- a/src/lib/route.js +++ b/src/lib/route.js @@ -6,10 +6,10 @@ module.exports.getURIClassroomToken = uriPathname => { const classRegisterRegexp = /^\/?classes\/\d*\/register\/([a-zA-Z0-9]*)\/?$/; const classRegisterMatch = classRegisterRegexp.exec(uriPathname); if (classRegisterMatch) return classRegisterMatch[1]; - // if regex match failed, try to match /join/CLASSROOM_TOKEN - const joinTokenRegexp = /^\/?join\/([a-zA-Z0-9]*)\/?$/; - const joinTokenMatch = joinTokenRegexp.exec(uriPathname); - if (joinTokenMatch) return joinTokenMatch[1]; + // if regex match failed, try to match /signup/CLASSROOM_TOKEN + const signupTokenRegexp = /^\/?signup\/([a-zA-Z0-9]*)\/?$/; + const signupTokenMatch = signupTokenRegexp.exec(uriPathname); + if (signupTokenMatch) return signupTokenMatch[1]; // if neither matched return null; }; diff --git a/src/routes.json b/src/routes.json index 781348730..bc7fde6b1 100644 --- a/src/routes.json +++ b/src/routes.json @@ -282,8 +282,8 @@ }, { "name": "student-registration-token-only", - "pattern": "^/join/:token", - "routeAlias": "/classes/(complete_registration|.+/register/.+)", + "pattern": "^/signup/:token", + "routeAlias": "/signup/.+)", "view": "studentregistration/studentregistration", "title": "Class Registration" }, diff --git a/src/views/studentregistration/studentregistration.jsx b/src/views/studentregistration/studentregistration.jsx index 6605d6145..5b8ff8b39 100644 --- a/src/views/studentregistration/studentregistration.jsx +++ b/src/views/studentregistration/studentregistration.jsx @@ -163,7 +163,7 @@ const IntlStudentRegistration = injectIntl(StudentRegistration); // parse either format of student registration url: // "class register": http://scratch.mit.edu/classes/3/register/c0256654e1be -// "join token": http://scratch.mit.edu/join/c025r54ebe +// "signup token": http://scratch.mit.edu/signup/c025r54ebe const props = {classroomToken: route.getURIClassroomToken(document.location.pathname)}; render(, document.getElementById('app')); diff --git a/test/unit/lib/route.test.js b/test/unit/lib/route.test.js index 2acb17599..98a00cb1b 100644 --- a/test/unit/lib/route.test.js +++ b/test/unit/lib/route.test.js @@ -12,15 +12,15 @@ describe('unit test lib/route.js', () => { expect(response).toEqual('r9n5f5xk'); }); - test('getURIClassroomToken parses URI paths like /join/e2dcfkx95', () => { + test('getURIClassroomToken parses URI paths like /signup/e2dcfkx95', () => { let response; - response = route.getURIClassroomToken('/join/e2dcfkx95'); + response = route.getURIClassroomToken('/signup/e2dcfkx95'); expect(response).toEqual('e2dcfkx95'); }); test('getURIClassroomToken works with trailing slash', () => { let response; - response = route.getURIClassroomToken('/join/r9n5f5xk/'); + response = route.getURIClassroomToken('/signup/r9n5f5xk/'); expect(response).toEqual('r9n5f5xk'); }); });