mirror of
https://github.com/GSA/notifications-api.git
synced 2026-09-08 08:28:24 -04:00
use joinedload to only hit the database once per request
also: * only include active orgs * write lots of tests
This commit is contained in:
@@ -1,6 +1,9 @@
|
|||||||
from random import (SystemRandom)
|
from random import (SystemRandom)
|
||||||
from datetime import (datetime, timedelta)
|
from datetime import (datetime, timedelta)
|
||||||
|
|
||||||
from sqlalchemy import func
|
from sqlalchemy import func
|
||||||
|
from sqlalchemy.orm import joinedload
|
||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from app.models import (User, VerifyCode)
|
from app.models import (User, VerifyCode)
|
||||||
|
|
||||||
@@ -113,3 +116,16 @@ def update_user_password(user, password):
|
|||||||
user.password_changed_at = datetime.utcnow()
|
user.password_changed_at = datetime.utcnow()
|
||||||
db.session.add(user)
|
db.session.add(user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_and_accounts(user_id):
|
||||||
|
return User.query.filter(
|
||||||
|
User.id == user_id
|
||||||
|
).options(
|
||||||
|
# eagerly load the user's services and organisations, and also the service's org and vice versa
|
||||||
|
# (so we can see if the user knows about it)
|
||||||
|
joinedload('services'),
|
||||||
|
joinedload('organisations'),
|
||||||
|
joinedload('organisations.services'),
|
||||||
|
joinedload('services.organisation'),
|
||||||
|
).one()
|
||||||
|
|||||||
@@ -116,11 +116,11 @@ class User(db.Model):
|
|||||||
services = db.relationship(
|
services = db.relationship(
|
||||||
'Service',
|
'Service',
|
||||||
secondary='user_to_service',
|
secondary='user_to_service',
|
||||||
backref=db.backref('user_to_service', lazy='dynamic'))
|
backref='user_to_service')
|
||||||
organisations = db.relationship(
|
organisations = db.relationship(
|
||||||
'Organisation',
|
'Organisation',
|
||||||
secondary='user_to_organisation',
|
secondary='user_to_organisation',
|
||||||
backref=db.backref('users', lazy='dynamic'))
|
backref='users')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def password(self):
|
def password(self):
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ from app.dao.users_dao import (
|
|||||||
create_secret_code,
|
create_secret_code,
|
||||||
save_user_attribute,
|
save_user_attribute,
|
||||||
update_user_password,
|
update_user_password,
|
||||||
count_user_verify_codes
|
count_user_verify_codes,
|
||||||
|
get_user_and_accounts
|
||||||
)
|
)
|
||||||
from app.dao.permissions_dao import permission_dao
|
from app.dao.permissions_dao import permission_dao
|
||||||
from app.dao.services_dao import dao_fetch_service_by_id
|
from app.dao.services_dao import dao_fetch_service_by_id
|
||||||
@@ -397,8 +398,7 @@ def update_password(user_id):
|
|||||||
|
|
||||||
@user_blueprint.route('/<uuid:user_id>/organisations-and-services', methods=['GET'])
|
@user_blueprint.route('/<uuid:user_id>/organisations-and-services', methods=['GET'])
|
||||||
def get_organisations_and_services_for_user(user_id):
|
def get_organisations_and_services_for_user(user_id):
|
||||||
user = get_user_by_id(user_id=user_id)
|
user = get_user_and_accounts(user_id)
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'organisations': [
|
'organisations': [
|
||||||
{
|
{
|
||||||
@@ -410,10 +410,10 @@ def get_organisations_and_services_for_user(user_id):
|
|||||||
'name': service.name
|
'name': service.name
|
||||||
}
|
}
|
||||||
for service in org.services
|
for service in org.services
|
||||||
if service.active and user in service.users
|
if service.active and service in user.services
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
for org in user.organisations
|
for org in user.organisations if org.active
|
||||||
],
|
],
|
||||||
'services_without_organisations': [
|
'services_without_organisations': [
|
||||||
{
|
{
|
||||||
@@ -426,7 +426,7 @@ def get_organisations_and_services_for_user(user_id):
|
|||||||
# but not one that the user can see.
|
# but not one that the user can see.
|
||||||
(
|
(
|
||||||
not service.organisation or
|
not service.organisation or
|
||||||
user not in service.organisation.users
|
service.organisation not in user.organisations
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ from app.models import (
|
|||||||
)
|
)
|
||||||
from app.dao.permissions_dao import default_service_permissions
|
from app.dao.permissions_dao import default_service_permissions
|
||||||
from tests import create_authorization_header
|
from tests import create_authorization_header
|
||||||
|
from tests.app.db import create_service, create_organisation, create_user
|
||||||
|
|
||||||
|
|
||||||
def test_get_user_list(admin_request, sample_service):
|
def test_get_user_list(admin_request, sample_service):
|
||||||
@@ -605,3 +606,128 @@ def test_cannot_update_user_password_using_attributes_method(admin_request, samp
|
|||||||
_expected_status=400
|
_expected_status=400
|
||||||
)
|
)
|
||||||
assert resp['message']['_schema'] == ['Unknown field name password']
|
assert resp['message']['_schema'] == ['Unknown field name password']
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_orgs_and_services_nests_services(admin_request, sample_user):
|
||||||
|
org1 = create_organisation(name='org1')
|
||||||
|
org2 = create_organisation(name='org2')
|
||||||
|
service1 = create_service(service_name='service1')
|
||||||
|
service2 = create_service(service_name='service2')
|
||||||
|
service3 = create_service(service_name='service3')
|
||||||
|
|
||||||
|
org1.services = [service1, service2]
|
||||||
|
org2.services = []
|
||||||
|
|
||||||
|
sample_user.organisations = [org1, org2]
|
||||||
|
sample_user.services = [service1, service2, service3]
|
||||||
|
|
||||||
|
resp = admin_request.get('user.get_organisations_and_services_for_user', user_id=sample_user.id)
|
||||||
|
|
||||||
|
assert resp == {
|
||||||
|
'organisations': [
|
||||||
|
{
|
||||||
|
'name': org1.name,
|
||||||
|
'id': str(org1.id),
|
||||||
|
'services': [
|
||||||
|
{
|
||||||
|
'name': service1.name,
|
||||||
|
'id': str(service1.id)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': service2.name,
|
||||||
|
'id': str(service2.id)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': org2.name,
|
||||||
|
'id': str(org2.id),
|
||||||
|
'services': []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'services_without_organisations': [
|
||||||
|
{
|
||||||
|
'name': service3.name,
|
||||||
|
'id': str(service3.id)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_orgs_and_services_only_returns_active(admin_request, sample_user):
|
||||||
|
org1 = create_organisation(name='org1', active=True)
|
||||||
|
org2 = create_organisation(name='org2', active=False)
|
||||||
|
|
||||||
|
# in an active org
|
||||||
|
service1 = create_service(service_name='service1', active=True)
|
||||||
|
service2 = create_service(service_name='service2', active=False)
|
||||||
|
# active but in an inactive org
|
||||||
|
service3 = create_service(service_name='service3', active=True)
|
||||||
|
# not in an org
|
||||||
|
service4 = create_service(service_name='service4', active=True)
|
||||||
|
service5 = create_service(service_name='service5', active=False)
|
||||||
|
|
||||||
|
org1.services = [service1, service2]
|
||||||
|
org2.services = [service3]
|
||||||
|
|
||||||
|
sample_user.organisations = [org1, org2]
|
||||||
|
sample_user.services = [service1, service2, service3, service4, service5]
|
||||||
|
|
||||||
|
resp = admin_request.get('user.get_organisations_and_services_for_user', user_id=sample_user.id)
|
||||||
|
|
||||||
|
assert resp == {
|
||||||
|
'organisations': [
|
||||||
|
{
|
||||||
|
'name': org1.name,
|
||||||
|
'id': str(org1.id),
|
||||||
|
'services': [
|
||||||
|
{
|
||||||
|
'name': service1.name,
|
||||||
|
'id': str(service1.id)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'services_without_organisations': [
|
||||||
|
{
|
||||||
|
'name': service4.name,
|
||||||
|
'id': str(service4.id)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_orgs_and_services_only_shows_users_orgs_and_services(admin_request, sample_user):
|
||||||
|
other_user = create_user(email='other@user.com')
|
||||||
|
|
||||||
|
org1 = create_organisation(name='org1')
|
||||||
|
org2 = create_organisation(name='org2')
|
||||||
|
service1 = create_service(service_name='service1')
|
||||||
|
service2 = create_service(service_name='service2')
|
||||||
|
|
||||||
|
org1.services = [service1]
|
||||||
|
|
||||||
|
sample_user.organisations = [org2]
|
||||||
|
sample_user.services = [service1]
|
||||||
|
|
||||||
|
other_user.organisations = [org1, org2]
|
||||||
|
other_user.services = [service1, service2]
|
||||||
|
|
||||||
|
resp = admin_request.get('user.get_organisations_and_services_for_user', user_id=sample_user.id)
|
||||||
|
|
||||||
|
assert resp == {
|
||||||
|
'organisations': [
|
||||||
|
{
|
||||||
|
'name': org2.name,
|
||||||
|
'id': str(org2.id),
|
||||||
|
'services': []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
# service1 belongs to org1, but the user doesn't know about org1
|
||||||
|
'services_without_organisations': [
|
||||||
|
{
|
||||||
|
'name': service1.name,
|
||||||
|
'id': str(service1.id)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user