Removed dead code and dead depcrecated route

This commit is contained in:
Alex Janousek
2025-10-30 14:36:30 -04:00
parent 1665e52f63
commit 32f82cce91
12 changed files with 10 additions and 520 deletions

View File

@@ -483,24 +483,6 @@
"is_secret": false
}
],
"tests/app/main/views/organizations/test_organization_invites.py": [
{
"type": "Secret Keyword",
"filename": "tests/app/main/views/organizations/test_organization_invites.py",
"hashed_secret": "bdbb156d25d02fd7792865824201dda1c60f4473",
"is_verified": false,
"line_number": 274,
"is_secret": false
},
{
"type": "Secret Keyword",
"filename": "tests/app/main/views/organizations/test_organization_invites.py",
"hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8",
"is_verified": false,
"line_number": 282,
"is_secret": false
}
],
"tests/app/main/views/test_accept_invite.py": [
{
"type": "Secret Keyword",
@@ -634,5 +616,5 @@
}
]
},
"generated_at": "2025-10-14T19:59:45Z"
"generated_at": "2025-10-30T18:36:19Z"
}

View File

@@ -634,26 +634,6 @@ class RegisterUserFromInviteForm(RegisterUserForm):
raise ValidationError("Cannot be empty")
class RegisterUserFromOrgInviteForm(StripWhitespaceForm):
def __init__(self, invited_org_user):
super().__init__(
organization=invited_org_user.organization,
email_address=invited_org_user.email_address,
)
name = UsaTextInputField(
"Full name", validators=[DataRequired(message="Cannot be empty")]
)
mobile_number = InternationalPhoneNumber(
"Mobile number", validators=[DataRequired(message="Cannot be empty")]
)
password = password()
organization = HiddenField("organization")
email_address = HiddenField("email_address")
auth_type = HiddenField("auth_type", validators=[DataRequired()])
def uswds_checkbox_field_widget(self, field, param_extensions=None, **kwargs):
# error messages
error_message = None

View File

@@ -140,4 +140,4 @@ def accept_org_invite(token):
url_for("main.organization_dashboard", org_id=invited_org_user.organization)
)
else:
return redirect(url_for("main.register_from_org_invite"))
return redirect(url_for("main.sign_in"))

View File

@@ -1,26 +1,13 @@
import json
import uuid
from datetime import datetime, timedelta
from urllib.parse import unquote
from flask import (
abort,
current_app,
flash,
redirect,
render_template,
request,
session,
url_for,
)
from flask import abort, current_app, flash, redirect, render_template, request, url_for
from app import redis_client, user_api_client
from app.enums import InvitedUserStatus
from app.main import main
from app.main.forms import (
RegisterUserFromOrgInviteForm,
SetupUserProfileForm,
)
from app.main.forms import SetupUserProfileForm
from app.main.views import sign_in
from app.main.views.verify import activate_user
from app.models.user import InvitedOrgUser, InvitedUser, User
@@ -34,69 +21,6 @@ def register():
abort(404)
@main.route("/register-from-org-invite", methods=["GET", "POST"])
# TODO This is deprecated, we are now handling invites in the
# login.gov workflow. Leaving it here until we write the new
# org registration.
def register_from_org_invite():
invited_org_user = InvitedOrgUser.from_session()
if not invited_org_user:
abort(404, "No invited_org_user")
form = RegisterUserFromOrgInviteForm(
invited_org_user,
)
form.auth_type.data = "sms_auth"
if form.validate_on_submit():
if (
form.organization.data != invited_org_user.organization
or form.email_address.data != invited_org_user.email_address
):
abort(400, "organization or email address doesn't match")
_do_registration(
form,
send_email=False,
send_sms=True,
organization_id=invited_org_user.organization,
)
invited_org_user.accept_invite()
return redirect(url_for("main.verify"))
return render_template(
"views/register-from-org-invite.html",
invited_org_user=invited_org_user,
form=form,
)
def _do_registration(form, send_sms=True, send_email=True, organization_id=None):
user = User.from_email_address_or_none(form.email_address.data)
if user:
if send_email:
user.send_already_registered_email()
session["expiry_date"] = str(datetime.utcnow() + timedelta(hours=1))
session["user_details"] = {"email": user.email_address, "id": user.id}
else:
user = User.register(
name=form.name.data,
email_address=form.email_address.data,
mobile_number=form.mobile_number.data,
password=form.password.data,
auth_type=form.auth_type.data,
)
if send_email:
user.send_verify_email()
if send_sms:
user.send_verify_code()
session["expiry_date"] = str(datetime.utcnow() + timedelta(hours=1))
session["user_details"] = {"email": user.email_address, "id": user.id}
if organization_id:
session["organization_id"] = organization_id
@main.route("/registration-continue")
def registration_continue():
abort(404)

View File

@@ -1,33 +0,0 @@
{% extends "base.html" %}
{% from "components/page-footer.html" import page_footer %}
{% from "components/form.html" import form_wrapper %}
{% block per_page_title %}
Create an account
{% endblock %}
{% block maincolumn_content %}
<div class="grid-row">
<div class="grid-col-8">
<h1 class="font-body-2xl margin-bottom-3">Create an account</h1>
<p>Your account will be created with this email: {{invited_org_user.email_address}}</p>
{% call form_wrapper() %}
{{ form.name(param_extensions={}) }}
<div class="extra-tracking">
{{ form.mobile_number(param_extensions={
"hint": {"text": "Well send you a security code by text message"}
}) }}
</div>
{{ form.password(param_extensions={
"hint": {"text": "At least 8 characters"},
"autocomplete": "new-password"
}) }}
{{ page_footer("Continue") }}
{{form.organization}}
{{form.email_address}}
{% endcall %}
</div>
</div>
{% endblock %}

View File

@@ -1,200 +0,0 @@
const { src, dest, series } = require('gulp');
const mergeStream = require('merge-stream');
const uswds = require('@uswds/compile');
const { exec } = require('child_process');
const plugins = {};
plugins.addSrc = require('gulp-add-src');
plugins.babel = require('gulp-babel');
plugins.cleanCSS = require('gulp-clean-css');
plugins.concat = require('gulp-concat');
plugins.prettyerror = require('gulp-prettyerror');
plugins.uglify = require('gulp-uglify');
const paths = {
src: 'app/assets/',
dist: 'app/static/',
npm: 'node_modules/'
};
const javascripts = () => {
// Files that don't use NotifyModules and can be uglified
const localUglified = src([
paths.src + 'javascripts/modules/init.js',
paths.src + 'javascripts/modules/uswds-modules.js',
paths.src + 'javascripts/modules/show-hide-content.js',
paths.src + 'javascripts/radioSelect.js',
paths.src + 'javascripts/liveSearch.js',
paths.src + 'javascripts/preventDuplicateFormSubmissions.js',
paths.src + 'javascripts/errorBanner.js',
paths.src + 'javascripts/notifyModal.js',
paths.src + 'javascripts/date.js',
paths.src + 'javascripts/sidenav.js',
paths.src + 'javascripts/validation.js',
paths.src + 'javascripts/scrollPosition.js',
])
.pipe(plugins.prettyerror())
.pipe(
plugins.babel({
presets: ['@babel/preset-env'],
})
)
.pipe(plugins.uglify());
// Files that use NotifyModules - split into two groups to avoid stream issues
const notifyModules1 = src([
paths.src + 'javascripts/copyToClipboard.js',
paths.src + 'javascripts/enhancedTextbox.js',
paths.src + 'javascripts/fileUpload.js',
paths.src + 'javascripts/errorTracking.js',
paths.src + 'javascripts/fullscreenTable.js',
paths.src + 'javascripts/templateFolderForm.js',
])
.pipe(plugins.prettyerror())
.pipe(
plugins.babel({
presets: ['@babel/preset-env'],
})
);
const notifyModules2 = src([
paths.src + 'javascripts/collapsibleCheckboxes.js',
paths.src + 'javascripts/updateStatus.js',
paths.src + 'javascripts/timeoutPopup.js',
paths.src + 'javascripts/main.js',
])
.pipe(plugins.prettyerror())
.pipe(
plugins.babel({
presets: ['@babel/preset-env'],
})
);
// First create vendored with jquery-expose immediately after jQuery
const vendoredWithExpose = src([
paths.npm + 'jquery/dist/jquery.min.js',
paths.src + 'javascripts/jquery-expose.js',
paths.npm + 'query-command-supported/dist/queryCommandSupported.min.js',
paths.npm + 'textarea-caret/index.js',
paths.npm + 'cbor-js/cbor.js',
paths.npm + 'd3/dist/d3.min.js'
]);
// Concatenate all streams
const mainBundle = mergeStream(vendoredWithExpose, localUglified, notifyModules1, notifyModules2);
// Use the mainBundle as the base and append remaining non-transpiled files at the end
return mainBundle
.pipe(plugins.addSrc.append(paths.src + 'javascripts/listEntry.js'))
.pipe(plugins.addSrc.append(paths.src + 'javascripts/stick-to-window-when-scrolling.js'))
.pipe(plugins.addSrc.append(paths.src + 'javascripts/totalMessagesChart.js'))
.pipe(plugins.addSrc.append(paths.src + 'javascripts/activityChart.js'))
.pipe(plugins.addSrc.append(paths.src + 'javascripts/job-polling.js'))
.pipe(plugins.concat('all.js'))
.pipe(dest(paths.dist + 'javascripts/'));
};
// Task to copy `gtm_head.js`
const copyGtmHead = () => {
return src(paths.src + 'js/gtm_head.js').pipe(dest(paths.dist + 'js/'));
};
// Task to copy `setTimezone.js`
const copySetTimezone = () => {
return src(paths.src + 'js/setTimezone.js').pipe(dest(paths.dist + 'js/'));
};
// Task to copy images
const copyImages = () => {
return src(paths.src + 'images/**/*', { encoding: false }).pipe(
dest(paths.dist + 'images/')
);
};
// Task to pdf files
const copyPDF = () => {
return src(paths.src + 'pdf/**/*', { encoding: false }).pipe(
dest(paths.dist + 'pdf/')
);
};
const copyUSWDSJS = () => {
return src('node_modules/@uswds/uswds/dist/js/uswds.min.js')
.pipe(dest(paths.dist + 'js/'));
};
// Configure USWDS paths
uswds.settings.version = 3;
uswds.settings.compile = {
sass: true,
javascript: true,
sprites: false
};
uswds.paths.dist.css = paths.dist + 'css';
uswds.paths.dist.js = paths.dist + 'js';
uswds.paths.dist.img = paths.dist + 'img';
uswds.paths.dist.pdf = paths.dist + 'pdf';
uswds.paths.dist.fonts = paths.dist + 'fonts';
uswds.paths.dist.theme = paths.src + 'sass/uswds';
// Task to compile USWDS styles
const styles = async () => {
await uswds.compileSass();
};
// Task to copy USWDS assets
const copyUSWDSAssets = () => {
return src([
'node_modules/@uswds/uswds/dist/img/**/*',
'node_modules/@uswds/uswds/dist/fonts/**/*'
], { encoding: false })
.pipe(dest((file) => {
if (file.path.includes('/img/')) {
return paths.dist + 'img/';
} else if (file.path.includes('/fonts/')) {
return paths.dist + 'fonts/';
}
return paths.dist;
}));
};
// Optional backstopJS task
// Install gulp globally and run `gulp backstopTest`
const backstopTest = (done) => {
exec(
'npx backstop test --configPath=backstop.config.js',
(err, stdout, stderr) => {
console.log(stdout);
console.error(stderr);
done(err);
}
);
};
// Optional backstopJS reference task
// Install gulp globally and run `gulp backstopReference`
const backstopReference = (done) => {
exec(
'npx backstop reference --configPath=backstop.config.js',
(err, stdout, stderr) => {
console.log(stdout);
console.error(stderr);
done(err);
}
);
};
// Export tasks
exports.default = series(
styles,
javascripts,
copyGtmHead,
copySetTimezone,
copyImages,
copyPDF,
copyUSWDSAssets,
copyUSWDSJS
);
exports.backstopTest = backstopTest;
exports.backstopReference = backstopReference;

View File

@@ -3,7 +3,7 @@
"version": "0.0.1",
"description": "Admin front end for Notify",
"engines": {
"node": ">=24.0.0"
"node": "^24.10.0"
},
"scripts": {
"lint": "jshint app/assets/javascripts",

4
poetry.lock generated
View File

@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand.
# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand.
[[package]]
name = "ago"
@@ -4421,4 +4421,4 @@ cffi = ["cffi (>=1.17) ; python_version >= \"3.13\" and platform_python_implemen
[metadata]
lock-version = "2.1"
python-versions = "^3.13.2"
content-hash = "1f845516e43b849ec3778eeec39783aafc81cdef092b3528f9a3a35e1604dae5"
content-hash = "9e8c9d15671b2e9567ada7a222d79490b9b6eae6c26a141c536dc004853fbe23"

View File

@@ -67,7 +67,6 @@ markupsafe = "^3.0.3"
python-dateutil = "^2.9.0.post0"
pyyaml = "^6.0.3"
requests = "^2.32.5"
six = "^1.16.0"
urllib3 = "^2.5.0"
webencodings = "^0.5.1"
virtualenv = "<20.36"

View File

@@ -1,5 +1,5 @@
[tool:pytest]
norecursedirs=node_modules bower_components
norecursedirs=node_modules
xfail_strict=true
[tool:isort]
@@ -10,4 +10,4 @@ multi_line_output = 3
[tool:flake8]
exclude = venv*,__pycache__,node_modules,cache,build
max-line-length = 120
extend_ignore=B306, W504, E203
extend_ignore=B306, W504, E203

View File

@@ -245,7 +245,7 @@ def test_user_accepts_invite(
client_request.get(
"main.accept_org_invite",
token="thisisnotarealtoken",
_expected_redirect=url_for("main.register_from_org_invite"),
_expected_redirect=url_for("main.sign_in"),
)
mock_check_org_invite_token.assert_called_once_with("thisisnotarealtoken")
@@ -253,167 +253,6 @@ def test_user_accepts_invite(
mock_get_users_for_organization.assert_called_once_with(ORGANISATION_ID)
def test_registration_from_org_invite_404s_if_user_not_in_session(
client_request, mocker
):
mocker.patch("app.notify_client.user_api_client.UserApiClient.deactivate_user")
client_request.logout()
client_request.get(
"main.register_from_org_invite",
_expected_status=404,
)
@pytest.mark.parametrize(
("data", "error"),
[
(
{
"name": "Bad Mobile",
"mobile_number": "not good",
"password": "validPassword!", # noqa
},
"The string supplied did not seem to be a phone number",
),
(
{
"name": "Bad Password",
"mobile_number": "+12021234123",
"password": "password", # noqa
},
"Choose a password thats harder to guess",
),
],
)
def test_registration_from_org_invite_has_bad_data(
client_request,
sample_org_invite,
data,
error,
mock_get_invited_org_user_by_id,
mocker,
):
mocker.patch("app.notify_client.user_api_client.UserApiClient.deactivate_user")
client_request.logout()
with client_request.session_transaction() as session:
session["invited_org_user_id"] = sample_org_invite["id"]
page = client_request.post(
"main.register_from_org_invite",
_data=data,
_expected_status=200,
)
assert error in page.text
@pytest.mark.parametrize(
"diff_data",
[["email_address"], ["organization"], ["email_address", "organization"]],
)
def test_registration_from_org_invite_has_different_email_or_organization(
client_request,
sample_org_invite,
diff_data,
mock_get_invited_org_user_by_id,
):
client_request.logout()
with client_request.session_transaction() as session:
session["invited_org_user_id"] = sample_org_invite["id"]
data = {
"name": "Test User",
"mobile_number": "+12024900460",
"password": "validPassword!",
"email_address": sample_org_invite["email_address"],
"organization": sample_org_invite["organization"],
}
for field in diff_data:
data[field] = "different"
client_request.post(
"main.register_from_org_invite",
_data=data,
_expected_status=400,
)
def test_org_user_registers_with_email_already_in_use(
client_request,
sample_org_invite,
mock_get_user_by_email,
mock_accept_org_invite,
mock_add_user_to_organization,
mock_send_already_registered_email,
mock_register_user,
mock_get_invited_org_user_by_id,
):
client_request.logout()
with client_request.session_transaction() as session:
session["invited_org_user_id"] = sample_org_invite["id"]
client_request.post(
"main.register_from_org_invite",
_data={
"name": "Test User",
"mobile_number": "+12024900460",
"password": "validPassword!",
"email_address": sample_org_invite["email_address"],
"organization": sample_org_invite["organization"],
},
_expected_redirect=url_for("main.verify"),
)
mock_get_user_by_email.assert_called_once_with(sample_org_invite["email_address"])
assert mock_register_user.called is False
assert mock_send_already_registered_email.called is False
def test_org_user_registration(
client_request,
sample_org_invite,
mock_email_is_not_already_in_use,
mock_register_user,
mock_send_verify_code,
mock_get_user_by_email,
mock_send_verify_email,
mock_accept_org_invite,
mock_add_user_to_organization,
mock_get_invited_org_user_by_id,
):
client_request.logout()
with client_request.session_transaction() as session:
session["invited_org_user_id"] = sample_org_invite["id"]
client_request.post(
"main.register_from_org_invite",
_data={
"name": "Test User",
"email_address": sample_org_invite["email_address"],
"mobile_number": "+12024900460",
"password": "validPassword!",
"organization": sample_org_invite["organization"],
},
_expected_redirect=url_for("main.verify"),
)
assert mock_get_user_by_email.called is False
mock_register_user.assert_called_once_with(
"Test User",
sample_org_invite["email_address"],
"+12024900460",
"validPassword!",
"sms_auth",
)
mock_send_verify_code.assert_called_once_with(
"6ce466d0-fd6a-11e5-82f5-e0accb9d11a6",
"sms",
"+12024900460",
)
mock_get_invited_org_user_by_id.assert_called_once_with(sample_org_invite["id"])
@pytest.mark.skip("TODO unskip asap")
def test_verified_org_user_redirects_to_dashboard(
client_request,

View File

@@ -152,7 +152,6 @@ EXCLUDED_ENDPOINTS = tuple(
"received_text_messages_callback",
"redact_template",
"register",
"register_from_org_invite",
"registration_continue",
"remove_user_from_organization",
"remove_user_from_service",