Skip to content

Bugfix: Add duplicate organization name check #209

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion app/api/dao/organization.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from http import HTTPStatus
from flask import json
from sqlalchemy import func
from sqlalchemy.exc import IntegrityError

from app.database.models.bit_schema.organization import OrganizationModel
from app.database.models.bit_schema.user_extension import UserExtensionModel
from app import messages
Expand Down Expand Up @@ -73,8 +75,14 @@ def update_organization(data):
return messages.NOT_ORGANIZATION_REPRESENTATIVE, HTTPStatus.FORBIDDEN
except AttributeError:
return messages.NOT_ORGANIZATION_REPRESENTATIVE, HTTPStatus.FORBIDDEN
except IntegrityError as e:
if "organizations_name" in e.orig.args[0]:
return messages.ORGANIZATION_NAME_ALREADY_USED, HTTPStatus.CONFLICT
return messages.INVALID_REQUEST_DATA, HTTPStatus.BAD_REQUEST
except Exception:
return messages.UNEXPECTED_ERROR, HTTPStatus.INTERNAL_SERVER_ERROR



@staticmethod
def list_organizations(
name,
Expand Down
10 changes: 8 additions & 2 deletions app/database/models/bit_schema/organization.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import time
from sqlalchemy import null
from sqlalchemy.exc import DBAPIError

from app.database.sqlalchemy_extension import db
from app.utils.bitschema_utils import OrganizationStatus, Timezone
from app.database.models.bit_schema.program import ProgramModel
Expand Down Expand Up @@ -113,8 +115,12 @@ def find_by_email(cls, email: str) -> "OrganizationModel":

def save_to_db(self) -> None:
"""Adds an organization to the database. """
db.session.add(self)
db.session.commit()
try:
db.session.add(self)
db.session.commit()
except DBAPIError:
db.session.rollback()
raise

def delete_from_db(self) -> None:
"""Deletes an organization from the database. """
Expand Down
8 changes: 7 additions & 1 deletion app/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,4 +376,10 @@
}
NOT_ORGANIZATION_REPRESENTATIVE = {
"message": "You have not declared that you are representing an organization."
}
}
INVALID_REQUEST_DATA = {"message": "Data you provided cannot be processed. Make sure your data is acceptable."}
UNEXPECTED_ERROR = {"message": "An unexpected error occurred. Please, try again later or contact the administrator."}

# Non-unique value messages
ORGANIZATION_NAME_ALREADY_USED = {"message": "Organization with that name already exists."}

90 changes: 65 additions & 25 deletions tests/organizations/test_api_update_organization.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from tests.base_test_case import BaseTestCase
from app.api.request_api_utils import post_request, get_request, BASE_MS_API_URL, AUTH_COOKIE
from app.api.models.organization import get_organization_response_model, update_organization_request_model
from tests.test_data import user1
from tests.test_data import user1, user2
from app.database.models.ms_schema.user import UserModel
from app.database.models.bit_schema.user_extension import UserExtensionModel
from app.database.models.bit_schema.organization import OrganizationModel
Expand All @@ -25,7 +25,7 @@ class TestUpdateOrganizationApi(BaseTestCase):
def setUp(self, mock_login, mock_get_user):
super(TestUpdateOrganizationApi, self).setUp()
# set access expiry 4 weeks from today's date (sc*min*hrrs*days)
access_expiry = time.time() + 60*60*24*28
access_expiry = time.time() + 60 * 60 * 24 * 28
success_message = {"access_token": "this is fake token", "access_expiry": access_expiry}
success_code = HTTPStatus.OK

Expand All @@ -36,41 +36,38 @@ def setUp(self, mock_login, mock_get_user):
mock_login.raise_for_status = json.dumps(success_code)

expected_user = marshal(user1, full_user_api_model)

mock_get_response = Mock()
mock_get_response.json.return_value = expected_user
mock_get_response.status_code = success_code

mock_get_user.return_value = mock_get_response
mock_get_user.raise_for_status = json.dumps(success_code)

user_login_success = {
"username": user1.get("username"),
"password": user1.get("password")
}

with self.client:
login_response = self.client.post(
"/login",
data=json.dumps(user_login_success),
follow_redirects=True,
content_type="application/json",
)

test_user1 = UserModel(
name=user1["name"],
username=user1["username"],
password=user1["password"],
email=user1["email"],
password=user1["password"],
email=user1["email"],
terms_and_conditions_checked=user1["terms_and_conditions_checked"]
)
test_user1.need_mentoring = user1["need_mentoring"]
test_user1.available_to_mentor = user1["available_to_mentor"]

test_user1.save_to_db()
self.test_user1_data = UserModel.find_by_email(test_user1.email)
AUTH_COOKIE["user"] = marshal(self.test_user1_data, full_user_api_model)

self.correct_payload_organization = {
"representative_department": "H&R Department",
"name": "Company ABC",
Expand All @@ -83,31 +80,77 @@ def setUp(self, mock_login, mock_get_user):
"status": "Draft",
}


def test_api_dao_create_organization_successfully(self):
test_user_extension = UserExtensionModel(
user_id=self.test_user1_data.id,
timezone="AUSTRALIA_MELBOURNE"
)
test_user_extension.is_organization_rep = True
test_user_extension.save_to_db()

response = self.client.put(
"/organization",
headers={"Authorization": AUTH_COOKIE["Authorization"].value},
data=json.dumps(
dict(self.correct_payload_organization)
),
dict(self.correct_payload_organization)
),
follow_redirects=True,
content_type="application/json",
)
self.assertEqual(HTTPStatus.CREATED, response.status_code)
self.assertEqual(messages.ORGANIZATION_SUCCESSFULLY_CREATED, json.loads(response.data))

def test_api_dao_create_organization_when_name_not_unique(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NenadPantelic perhaps rename this to "...._when_name_already_exist" to avoid ambiguity coz organization name is unique.

# new user
test_user = UserModel(
name=user2["name"],
username=user2["username"],
password=user2["password"],
email=user2["email"],
terms_and_conditions_checked=user2["terms_and_conditions_checked"]
)
test_user.need_mentoring = user2["need_mentoring"]
test_user.available_to_mentor = user2["available_to_mentor"]

test_user.save_to_db()
test_user_extension = UserExtensionModel(
user_id=self.test_user1_data.id,
timezone="AUSTRALIA_MELBOURNE"
)
test_user_extension.is_organization_rep = True
test_user_extension.save_to_db()
organization = OrganizationModel(
rep_id=test_user.id,
name="Company ABC",
email="[email protected]",
address="506 Elizabeth St, Melbourne VIC 3000, Australia",
website="https://www.ames.net.au",
timezone="AUSTRALIA_MELBOURNE",
)
organization.rep_department = "H&R Department"
organization.about = "This is about ABC"
organization.phone = "321-456-789"
organization.status = "DRAFT"
# joined one month prior to access date
organization.join_date = time.time() - 60 * 60 * 24 * 7
organization.save_to_db()

# test creating the org with the same name (with another user)
response = self.client.put(
"/organization",
headers={"Authorization": AUTH_COOKIE["Authorization"].value},
data=json.dumps(
dict(self.correct_payload_organization)
),
follow_redirects=True,
content_type="application/json",
)
self.assertEqual(HTTPStatus.CONFLICT, response.status_code)
self.assertEqual(messages.ORGANIZATION_NAME_ALREADY_USED, json.loads(response.data))

def test_api_dao_update_organization_successfully(self):
# prepare existing organization
organization = OrganizationModel(
rep_id=self.test_user1_data.id,
rep_id=self.test_user1_data.id,
name="Company ABC",
email="[email protected]",
address="506 Elizabeth St, Melbourne VIC 3000, Australia",
Expand All @@ -119,18 +162,17 @@ def test_api_dao_update_organization_successfully(self):
organization.phone = "321-456-789"
organization.status = "DRAFT"
# joined one month prior to access date
organization.join_date = time.time() - 60*60*24*7
organization.join_date = time.time() - 60 * 60 * 24 * 7

db.session.add(organization)
db.session.commit()

test_user_extension = UserExtensionModel(
user_id=self.test_user1_data.id,
timezone="AUSTRALIA_MELBOURNE"
)
test_user_extension.is_organization_rep = True
test_user_extension.save_to_db()

update_payload_organization = {
"representative_department": "H&R Department",
"name": "Company ABC",
Expand All @@ -142,13 +184,12 @@ def test_api_dao_update_organization_successfully(self):
"phone": "321-456-789",
"status": "Publish",
}

response = self.client.put(
"/organization",
headers={"Authorization": AUTH_COOKIE["Authorization"].value},
data=json.dumps(
dict(update_payload_organization)
),
dict(update_payload_organization)
),
follow_redirects=True,
content_type="application/json",
)
Expand All @@ -162,15 +203,14 @@ def test_api_dao_get_organization_not_representative(self):
)
test_user_extension.is_organization_rep = False
test_user_extension.save_to_db()

response = self.client.put(
"/organization",
headers={"Authorization": AUTH_COOKIE["Authorization"].value},
data=json.dumps(
dict(self.correct_payload_organization)
),
dict(self.correct_payload_organization)
),
follow_redirects=True,
content_type="application/json",
)
self.assertEqual(HTTPStatus.FORBIDDEN, response.status_code)
self.assertEqual(messages.NOT_ORGANIZATION_REPRESENTATIVE, response.json)
self.assertEqual(messages.NOT_ORGANIZATION_REPRESENTATIVE, response.json)