diff --git a/alembic/env.py b/alembic/env.py index 3e4e6f9de..ba47c0d95 100644 --- a/alembic/env.py +++ b/alembic/env.py @@ -6,7 +6,7 @@ from api.v1.models import * from api.v1.models.permissions.permissions import Permission from api.v1.models.permissions.role_permissions import role_permissions -from api.v1.models.permissions.user_org_role import user_organization_roles +from api.v1.models.permissions.user_org_role import user_organisation_roles from api.v1.models.permissions.role import Role from api.v1.models.associations import Base diff --git a/alembic/versions/0c0978bc2925_fix_added_is_owner_to_org_user_role_.py b/alembic/versions/0c0978bc2925_fix_added_is_owner_to_org_user_role_.py new file mode 100644 index 000000000..d6bd217a7 --- /dev/null +++ b/alembic/versions/0c0978bc2925_fix_added_is_owner_to_org_user_role_.py @@ -0,0 +1,32 @@ +"""fix: added is_owner to org_user_role table + +Revision ID: 0c0978bc2925 +Revises: 493877a22513 +Create Date: 2024-08-10 04:26:02.132975 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '0c0978bc2925' +down_revision: Union[str, None] = '493877a22513' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('user_organisation_roles', sa.Column('is_owner', sa.Boolean(), server_default='false', nullable=True)) + op.drop_column('user_organisation_roles', 'status') + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('user_organisation_roles', sa.Column('status', sa.VARCHAR(length=20), autoincrement=False, nullable=False)) + op.drop_column('user_organisation_roles', 'is_owner') + # ### end Alembic commands ### diff --git a/alembic/versions/1fc08cda8f3b_ensure_id_and_foreign_keys_are_of_type_.py b/alembic/versions/1fc08cda8f3b_ensure_id_and_foreign_keys_are_of_type_.py index ebbbfbdff..84c4d8760 100644 --- a/alembic/versions/1fc08cda8f3b_ensure_id_and_foreign_keys_are_of_type_.py +++ b/alembic/versions/1fc08cda8f3b_ensure_id_and_foreign_keys_are_of_type_.py @@ -20,17 +20,17 @@ def upgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### - op.add_column('user_organization_roles', sa.Column('role_id', sa.String(), nullable=True)) - op.drop_constraint('user_organization_roles_role_name_fkey', 'user_organization_roles', type_='foreignkey') - op.create_foreign_key(None, 'user_organization_roles', 'roles', ['role_id'], ['id'], ondelete='CASCADE') - op.drop_column('user_organization_roles', 'role_name') + op.add_column('user_organisation_roles', sa.Column('role_id', sa.String(), nullable=True)) + op.drop_constraint('user_organisation_roles_role_name_fkey', 'user_organisation_roles', type_='foreignkey') + op.create_foreign_key(None, 'user_organisation_roles', 'roles', ['role_id'], ['id'], ondelete='CASCADE') + op.drop_column('user_organisation_roles', 'role_name') # ### end Alembic commands ### def downgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### - op.add_column('user_organization_roles', sa.Column('role_name', sa.VARCHAR(), autoincrement=False, nullable=False)) - op.drop_constraint(None, 'user_organization_roles', type_='foreignkey') - op.create_foreign_key('user_organization_roles_role_name_fkey', 'user_organization_roles', 'roles', ['role_name'], ['name'], ondelete='CASCADE') - op.drop_column('user_organization_roles', 'role_id') + op.add_column('user_organisation_roles', sa.Column('role_name', sa.VARCHAR(), autoincrement=False, nullable=False)) + op.drop_constraint(None, 'user_organisation_roles', type_='foreignkey') + op.create_foreign_key('user_organisation_roles_role_name_fkey', 'user_organisation_roles', 'roles', ['role_name'], ['name'], ondelete='CASCADE') + op.drop_column('user_organisation_roles', 'role_id') # ### end Alembic commands ### diff --git a/alembic/versions/224b03e9169c_product_comment_migration.py b/alembic/versions/224b03e9169c_product_comment_migration.py index c84961ade..e2484a9fa 100644 --- a/alembic/versions/224b03e9169c_product_comment_migration.py +++ b/alembic/versions/224b03e9169c_product_comment_migration.py @@ -41,9 +41,9 @@ def upgrade() -> None: if 'is_builtin' not in columns: op.add_column('roles', sa.Column('is_builtin', sa.Boolean(), nullable=True)) - # Check if 'user_organization_roles' table exists - if 'user_organization_roles' in inspector.get_table_names(): - op.alter_column('user_organization_roles', 'role_id', + # Check if 'user_organisation_roles' table exists + if 'user_organisation_roles' in inspector.get_table_names(): + op.alter_column('user_organisation_roles', 'role_id', existing_type=sa.VARCHAR(), nullable=True) # ### end Alembic commands ### @@ -52,7 +52,7 @@ def upgrade() -> None: def downgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### - op.alter_column('user_organization_roles', 'role_id', + op.alter_column('user_organisation_roles', 'role_id', existing_type=sa.VARCHAR(), nullable=False) op.drop_column('roles', 'is_builtin') diff --git a/alembic/versions/3b6d16e973a2_fix_resolved_naming_issues.py b/alembic/versions/3b6d16e973a2_fix_resolved_naming_issues.py index a8eb26f9b..f8bf3d7a5 100644 --- a/alembic/versions/3b6d16e973a2_fix_resolved_naming_issues.py +++ b/alembic/versions/3b6d16e973a2_fix_resolved_naming_issues.py @@ -20,33 +20,33 @@ def upgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### - op.add_column('organizations', sa.Column('name', sa.String(), nullable=False)) - op.add_column('organizations', sa.Column('email', sa.String(), nullable=True)) - op.add_column('organizations', sa.Column('type', sa.String(), nullable=True)) - op.add_column('organizations', sa.Column('description', sa.String(), nullable=True)) - op.drop_constraint('organizations_company_email_key', 'organizations', type_='unique') - op.drop_constraint('organizations_company_name_key', 'organizations', type_='unique') - op.create_unique_constraint(None, 'organizations', ['email']) - op.create_unique_constraint(None, 'organizations', ['name']) - op.drop_column('organizations', 'company_name') - op.drop_column('organizations', 'organization_type') - op.drop_column('organizations', 'lga') - op.drop_column('organizations', 'company_email') + op.add_column('organisations', sa.Column('name', sa.String(), nullable=False)) + op.add_column('organisations', sa.Column('email', sa.String(), nullable=True)) + op.add_column('organisations', sa.Column('type', sa.String(), nullable=True)) + op.add_column('organisations', sa.Column('description', sa.String(), nullable=True)) + op.drop_constraint('organisations_company_email_key', 'organisations', type_='unique') + op.drop_constraint('organisations_company_name_key', 'organisations', type_='unique') + op.create_unique_constraint(None, 'organisations', ['email']) + op.create_unique_constraint(None, 'organisations', ['name']) + op.drop_column('organisations', 'company_name') + op.drop_column('organisations', 'organisation_type') + op.drop_column('organisations', 'lga') + op.drop_column('organisations', 'company_email') # ### end Alembic commands ### def downgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### - op.add_column('organizations', sa.Column('company_email', sa.VARCHAR(), autoincrement=False, nullable=True)) - op.add_column('organizations', sa.Column('lga', sa.VARCHAR(), autoincrement=False, nullable=True)) - op.add_column('organizations', sa.Column('organization_type', sa.VARCHAR(), autoincrement=False, nullable=True)) - op.add_column('organizations', sa.Column('company_name', sa.VARCHAR(), autoincrement=False, nullable=False)) - op.drop_constraint(None, 'organizations', type_='unique') - op.drop_constraint(None, 'organizations', type_='unique') - op.create_unique_constraint('organizations_company_name_key', 'organizations', ['company_name']) - op.create_unique_constraint('organizations_company_email_key', 'organizations', ['company_email']) - op.drop_column('organizations', 'description') - op.drop_column('organizations', 'type') - op.drop_column('organizations', 'email') - op.drop_column('organizations', 'name') + op.add_column('organisations', sa.Column('company_email', sa.VARCHAR(), autoincrement=False, nullable=True)) + op.add_column('organisations', sa.Column('lga', sa.VARCHAR(), autoincrement=False, nullable=True)) + op.add_column('organisations', sa.Column('organisation_type', sa.VARCHAR(), autoincrement=False, nullable=True)) + op.add_column('organisations', sa.Column('company_name', sa.VARCHAR(), autoincrement=False, nullable=False)) + op.drop_constraint(None, 'organisations', type_='unique') + op.drop_constraint(None, 'organisations', type_='unique') + op.create_unique_constraint('organisations_company_name_key', 'organisations', ['company_name']) + op.create_unique_constraint('organisations_company_email_key', 'organisations', ['company_email']) + op.drop_column('organisations', 'description') + op.drop_column('organisations', 'type') + op.drop_column('organisations', 'email') + op.drop_column('organisations', 'name') # ### end Alembic commands ### diff --git a/alembic/versions/493877a22513_fix_replaced_organization_to_.py b/alembic/versions/493877a22513_fix_replaced_organization_to_.py new file mode 100644 index 000000000..7084018d1 --- /dev/null +++ b/alembic/versions/493877a22513_fix_replaced_organization_to_.py @@ -0,0 +1,30 @@ +"""fix: replaced organization to organisation + +Revision ID: 493877a22513 +Revises: ff92a0037698 +Create Date: 2024-08-10 03:48:41.184522 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '493877a22513' +down_revision: Union[str, None] = 'ff92a0037698' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/alembic/versions/a8eeee3dda1c_ensure_id_and_foreign_keys_are_of_type_.py b/alembic/versions/a8eeee3dda1c_ensure_id_and_foreign_keys_are_of_type_.py index ef6709b9d..239a84d24 100644 --- a/alembic/versions/a8eeee3dda1c_ensure_id_and_foreign_keys_are_of_type_.py +++ b/alembic/versions/a8eeee3dda1c_ensure_id_and_foreign_keys_are_of_type_.py @@ -20,17 +20,17 @@ def upgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### - op.add_column('user_organization_roles', sa.Column('role_name', sa.String(), nullable=False)) - op.drop_constraint('user_organization_roles_role_id_fkey', 'user_organization_roles', type_='foreignkey') - op.create_foreign_key(None, 'user_organization_roles', 'roles', ['role_name'], ['name'], ondelete='CASCADE') - op.drop_column('user_organization_roles', 'role_id') + op.add_column('user_organisation_roles', sa.Column('role_name', sa.String(), nullable=False)) + op.drop_constraint('user_organisation_roles_role_id_fkey', 'user_organisation_roles', type_='foreignkey') + op.create_foreign_key(None, 'user_organisation_roles', 'roles', ['role_name'], ['name'], ondelete='CASCADE') + op.drop_column('user_organisation_roles', 'role_id') # ### end Alembic commands ### def downgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### - op.add_column('user_organization_roles', sa.Column('role_id', sa.VARCHAR(), autoincrement=False, nullable=False)) - op.drop_constraint(None, 'user_organization_roles', type_='foreignkey') - op.create_foreign_key('user_organization_roles_role_id_fkey', 'user_organization_roles', 'roles', ['role_id'], ['id'], ondelete='CASCADE') - op.drop_column('user_organization_roles', 'role_name') + op.add_column('user_organisation_roles', sa.Column('role_id', sa.VARCHAR(), autoincrement=False, nullable=False)) + op.drop_constraint(None, 'user_organisation_roles', type_='foreignkey') + op.create_foreign_key('user_organisation_roles_role_id_fkey', 'user_organisation_roles', 'roles', ['role_id'], ['id'], ondelete='CASCADE') + op.drop_column('user_organisation_roles', 'role_name') # ### end Alembic commands ### diff --git a/alembic/versions/aeb162769644_added_sales_model_relate_sales_with_.py b/alembic/versions/aeb162769644_added_sales_model_relate_sales_with_.py index a82d207a7..14faa66dc 100644 --- a/alembic/versions/aeb162769644_added_sales_model_relate_sales_with_.py +++ b/alembic/versions/aeb162769644_added_sales_model_relate_sales_with_.py @@ -1,4 +1,4 @@ -"""Added sales model, relate sales with product, organization, and payments +"""Added sales model, relate sales with product, organisation, and payments Revision ID: aeb162769644 Revises: 854472eb449d @@ -24,12 +24,12 @@ def upgrade() -> None: sa.Column('quantity', sa.Integer(), nullable=False), sa.Column('amount', sa.Float(), nullable=False), sa.Column('product_id', sa.String(), nullable=False), - sa.Column('organization_id', sa.String(), nullable=False), + sa.Column('organisation_id', sa.String(), nullable=False), sa.Column('payment_id', sa.String(), nullable=True), sa.Column('id', sa.String(), nullable=False), sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), - sa.ForeignKeyConstraint(['organization_id'], ['organizations.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['organisation_id'], ['organisations.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['payment_id'], ['payments.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['product_id'], ['products.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('id') diff --git a/alembic/versions/bb0905b42300_ensure_id_and_foreign_keys_are_of_type_.py b/alembic/versions/bb0905b42300_ensure_id_and_foreign_keys_are_of_type_.py index de791afbf..90f6c4e52 100644 --- a/alembic/versions/bb0905b42300_ensure_id_and_foreign_keys_are_of_type_.py +++ b/alembic/versions/bb0905b42300_ensure_id_and_foreign_keys_are_of_type_.py @@ -45,22 +45,22 @@ def upgrade() -> None: sa.ForeignKeyConstraint(['role_id'], ['roles.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('role_id', 'permission_id') ) - op.create_table('user_organization_roles', + op.create_table('user_organisation_roles', sa.Column('user_id', sa.String(), nullable=False), - sa.Column('organization_id', sa.String(), nullable=False), + sa.Column('organisation_id', sa.String(), nullable=False), sa.Column('role_id', sa.String(), nullable=False), sa.Column('status', sa.String(length=20), nullable=False), - sa.ForeignKeyConstraint(['organization_id'], ['organizations.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['organisation_id'], ['organisations.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['role_id'], ['roles.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'), - sa.PrimaryKeyConstraint('user_id', 'organization_id') + sa.PrimaryKeyConstraint('user_id', 'organisation_id') ) # ### end Alembic commands ### def downgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### - op.drop_table('user_organization_roles') + op.drop_table('user_organisation_roles') op.drop_table('role_permissions') op.drop_index(op.f('ix_roles_id'), table_name='roles') op.drop_table('roles') diff --git a/alembic/versions/d8da7731f0aa_initial_migration.py b/alembic/versions/d8da7731f0aa_initial_migration.py index e8007252c..e4d66e352 100644 --- a/alembic/versions/d8da7731f0aa_initial_migration.py +++ b/alembic/versions/d8da7731f0aa_initial_migration.py @@ -50,11 +50,11 @@ def upgrade() -> None: sa.PrimaryKeyConstraint('id') ) op.create_index(op.f('ix_newsletters_id'), 'newsletters', ['id'], unique=False) - op.create_table('organizations', + op.create_table('organisations', sa.Column('company_name', sa.String(), nullable=False), sa.Column('company_email', sa.String(), nullable=True), sa.Column('industry', sa.String(), nullable=True), - sa.Column('organization_type', sa.String(), nullable=True), + sa.Column('organisation_type', sa.String(), nullable=True), sa.Column('country', sa.String(), nullable=True), sa.Column('state', sa.String(), nullable=True), sa.Column('address', sa.String(), nullable=True), @@ -66,7 +66,7 @@ def upgrade() -> None: sa.UniqueConstraint('company_email'), sa.UniqueConstraint('company_name') ) - op.create_index(op.f('ix_organizations_id'), 'organizations', ['id'], unique=False) + op.create_index(op.f('ix_organisations_id'), 'organisations', ['id'], unique=False) op.create_table('product_categories', sa.Column('name', sa.String(), nullable=False), sa.Column('id', sa.String(), nullable=False), @@ -125,7 +125,7 @@ def upgrade() -> None: ) op.create_index(op.f('ix_activity_logs_id'), 'activity_logs', ['id'], unique=False) op.create_table('billing_plans', - sa.Column('organization_id', sa.String(), nullable=False), + sa.Column('organisation_id', sa.String(), nullable=False), sa.Column('name', sa.String(), nullable=False), sa.Column('price', sa.Numeric(), nullable=False), sa.Column('currency', sa.String(), nullable=False), @@ -135,7 +135,7 @@ def upgrade() -> None: sa.Column('id', sa.String(), nullable=False), sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), - sa.ForeignKeyConstraint(['organization_id'], ['organizations.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['organisation_id'], ['organisations.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('id') ) op.create_index(op.f('ix_billing_plans_id'), 'billing_plans', ['id'], unique=False) @@ -156,13 +156,13 @@ def upgrade() -> None: op.create_index(op.f('ix_blogs_id'), 'blogs', ['id'], unique=False) op.create_table('invitations', sa.Column('user_id', sa.String(), nullable=False), - sa.Column('organization_id', sa.String(), nullable=False), + sa.Column('organisation_id', sa.String(), nullable=False), sa.Column('expires_at', sa.DateTime(timezone=True), nullable=False), sa.Column('is_valid', sa.Boolean(), nullable=True), sa.Column('id', sa.String(), nullable=False), sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), - sa.ForeignKeyConstraint(['organization_id'], ['organizations.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['organisation_id'], ['organisations.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('id') ) @@ -278,7 +278,7 @@ def upgrade() -> None: sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), sa.ForeignKeyConstraint(['category_id'], ['product_categories.id'], ondelete='CASCADE'), - sa.ForeignKeyConstraint(['org_id'], ['organizations.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['org_id'], ['organisations.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('id') ) op.create_index(op.f('ix_products_id'), 'products', ['id'], unique=False) @@ -327,14 +327,14 @@ def upgrade() -> None: sa.UniqueConstraint('user_id') ) op.create_index(op.f('ix_token_logins_id'), 'token_logins', ['id'], unique=False) - op.create_table('user_organization', + op.create_table('user_organisation', sa.Column('user_id', sa.String(), nullable=False), - sa.Column('organization_id', sa.String(), nullable=False), + sa.Column('organisation_id', sa.String(), nullable=False), sa.Column('role', sa.Enum('admin', 'user', 'guest', 'owner', name='user_org_role'), nullable=False), sa.Column('status', sa.Enum('member', 'suspended', 'left', name='user_org_status'), nullable=False), - sa.ForeignKeyConstraint(['organization_id'], ['organizations.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['organisation_id'], ['organisations.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'), - sa.PrimaryKeyConstraint('user_id', 'organization_id') + sa.PrimaryKeyConstraint('user_id', 'organisation_id') ) op.create_table('blog_dislikes', sa.Column('blog_id', sa.String(), nullable=False), @@ -425,7 +425,7 @@ def downgrade() -> None: op.drop_table('blog_likes') op.drop_index(op.f('ix_blog_dislikes_id'), table_name='blog_dislikes') op.drop_table('blog_dislikes') - op.drop_table('user_organization') + op.drop_table('user_organisation') op.drop_index(op.f('ix_token_logins_id'), table_name='token_logins') op.drop_table('token_logins') op.drop_index(op.f('ix_testimonials_id'), table_name='testimonials') @@ -464,8 +464,8 @@ def downgrade() -> None: op.drop_table('topics') op.drop_index(op.f('ix_product_categories_id'), table_name='product_categories') op.drop_table('product_categories') - op.drop_index(op.f('ix_organizations_id'), table_name='organizations') - op.drop_table('organizations') + op.drop_index(op.f('ix_organisations_id'), table_name='organisations') + op.drop_table('organisations') op.drop_index(op.f('ix_newsletters_id'), table_name='newsletters') op.drop_table('newsletters') op.drop_index(op.f('ix_faqs_id'), table_name='faqs') diff --git a/alembic/versions/eb6fe394d75b_added_region_timezone_and_language_table.py b/alembic/versions/eb6fe394d75b_added_region_timezone_and_language_table.py index 015109a81..2af6388e2 100644 --- a/alembic/versions/eb6fe394d75b_added_region_timezone_and_language_table.py +++ b/alembic/versions/eb6fe394d75b_added_region_timezone_and_language_table.py @@ -33,7 +33,7 @@ def upgrade() -> None: ) op.create_index(op.f('ix_regions_id'), 'regions', ['id'], unique=False) op.add_column('contact_us', sa.Column('org_id', sa.String(), nullable=False)) - op.create_foreign_key(None, 'contact_us', 'organizations', ['org_id'], ['id'], ondelete='CASCADE') + op.create_foreign_key(None, 'contact_us', 'organisations', ['org_id'], ['id'], ondelete='CASCADE') # ### end Alembic commands ### diff --git a/api/utils/db_validators.py b/api/utils/db_validators.py index 1a3469bad..61fd602df 100644 --- a/api/utils/db_validators.py +++ b/api/utils/db_validators.py @@ -1,6 +1,6 @@ from fastapi import HTTPException, status from sqlalchemy.orm import Session -from api.v1.models import User, Organization +from api.v1.models import User, Organisation def check_model_existence(db: Session, model, id): @@ -15,11 +15,11 @@ def check_model_existence(db: Session, model, id): return obj -def check_user_in_org(user: User, organization: Organization): - """Checks if a user is a member of an organization""" +def check_user_in_org(user: User, organisation: Organisation): + """Checks if a user is a member of an organisation""" - if user not in organization.users and not user.is_super_admin: + if user not in organisation.users and not user.is_super_admin: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, - detail="You are not a member of this organization", + detail="You are not a member of this organisation", ) diff --git a/api/utils/pagination.py b/api/utils/pagination.py index cc212427b..c0c2f9eb4 100644 --- a/api/utils/pagination.py +++ b/api/utils/pagination.py @@ -19,7 +19,7 @@ def paginated_response( Custom response for pagination.\n This takes in four atguments: * db- this is the database session - * model- this is the database table model eg Product, Organization``` + * model- this is the database table model eg Product, Organisation``` * limit- this is the number of items to fetch per page, this would be a query parameter * skip- this is the number of items to skip before fetching the next page of data. This would also be a query parameter @@ -55,7 +55,7 @@ def paginated_response( model=Product, limit=limit, skip=skip, - join=user_organization_association, + join=user_organisation_association, filters={'org_id': org_id} ) ``` diff --git a/api/v1/models/__init__.py b/api/v1/models/__init__.py index 120e32717..2b3de4b23 100644 --- a/api/v1/models/__init__.py +++ b/api/v1/models/__init__.py @@ -6,7 +6,7 @@ from api.v1.models.payment import Payment from api.v1.models.waitlist import Waitlist from api.v1.models.user import User -from api.v1.models.organization import Organization +from api.v1.models.organisation import Organisation from api.v1.models.profile import Profile from api.v1.models.notifications import Notification from api.v1.models.product import ProductVariant, ProductCategory, Product diff --git a/api/v1/models/associations.py b/api/v1/models/associations.py index 1d183e52f..4abbdab2c 100644 --- a/api/v1/models/associations.py +++ b/api/v1/models/associations.py @@ -10,16 +10,16 @@ from api.db.database import Base -user_organization_association = Table( - "user_organization", +user_organisation_association = Table( + "user_organisation", Base.metadata, Column( "user_id", String, ForeignKey("users.id", ondelete="CASCADE"), primary_key=True ), Column( - "organization_id", + "organisation_id", String, - ForeignKey("organizations.id", ondelete="CASCADE"), + ForeignKey("organisations.id", ondelete="CASCADE"), primary_key=True, ), Column( diff --git a/api/v1/models/billing_plan.py b/api/v1/models/billing_plan.py index 69eba80de..784296197 100644 --- a/api/v1/models/billing_plan.py +++ b/api/v1/models/billing_plan.py @@ -7,8 +7,8 @@ class BillingPlan(BaseTableModel): __tablename__ = "billing_plans" - organization_id = Column( - String, ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False + organisation_id = Column( + String, ForeignKey("organisations.id", ondelete="CASCADE"), nullable=False ) name = Column(String, nullable=False) price = Column(Numeric, nullable=False) @@ -17,4 +17,4 @@ class BillingPlan(BaseTableModel): description = Column(String, nullable=True) features = Column(ARRAY(String), nullable=False) - organization = relationship("Organization", back_populates="billing_plans") + organisation = relationship("Organisation", back_populates="billing_plans") diff --git a/api/v1/models/contact_us.py b/api/v1/models/contact_us.py index ea826670a..802bedf18 100644 --- a/api/v1/models/contact_us.py +++ b/api/v1/models/contact_us.py @@ -11,6 +11,6 @@ class ContactUs(BaseTableModel): email = Column(String, nullable=False) title = Column(String, nullable=False) message = Column(Text, nullable=False) - org_id = Column(String, ForeignKey('organizations.id', ondelete="CASCADE"), nullable=False) + org_id = Column(String, ForeignKey('organisations.id', ondelete="CASCADE"), nullable=False) - organization = relationship("Organization", back_populates="contact_us") \ No newline at end of file + organisation = relationship("Organisation", back_populates="contact_us") \ No newline at end of file diff --git a/api/v1/models/invitation.py b/api/v1/models/invitation.py index ac7d2fb77..d66d8e9a4 100644 --- a/api/v1/models/invitation.py +++ b/api/v1/models/invitation.py @@ -7,11 +7,11 @@ class Invitation(BaseTableModel): __tablename__ = "invitations" user_id = Column(String, ForeignKey("users.id", ondelete="CASCADE"), nullable=False) - organization_id = Column( - String, ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False + organisation_id = Column( + String, ForeignKey("organisations.id", ondelete="CASCADE"), nullable=False ) expires_at = Column(DateTime(timezone=True), nullable=False) is_valid = Column(Boolean, default=True) user = relationship("User", back_populates="invitations") - organization = relationship("Organization", back_populates="invitations") + organisation = relationship("Organisation", back_populates="invitations") diff --git a/api/v1/models/organization.py b/api/v1/models/organisation.py similarity index 60% rename from api/v1/models/organization.py rename to api/v1/models/organisation.py index 1ba72d052..1fd7f1225 100644 --- a/api/v1/models/organization.py +++ b/api/v1/models/organisation.py @@ -1,14 +1,14 @@ -""" The Organization model +""" The Organisation model """ from sqlalchemy import Column, String, Text, Enum from sqlalchemy.orm import relationship -from api.v1.models.associations import user_organization_association +from api.v1.models.associations import user_organisation_association from api.v1.models.base_model import BaseTableModel -class Organization(BaseTableModel): - __tablename__ = "organizations" +class Organisation(BaseTableModel): + __tablename__ = "organisations" name = Column(String, nullable=False, unique=True) email = Column(String, nullable=True, unique=True) @@ -20,23 +20,23 @@ class Organization(BaseTableModel): address = Column(String, nullable=True) users = relationship( - "User", secondary=user_organization_association, back_populates="organizations" + "User", secondary=user_organisation_association, back_populates="organisations" ) - billing_plans = relationship("BillingPlan", back_populates="organization", cascade="all, delete-orphan") - invitations = relationship("Invitation", back_populates="organization", cascade="all, delete-orphan") - products = relationship("Product", back_populates="organization", cascade="all, delete-orphan") - contact_us = relationship("ContactUs", back_populates="organization", cascade="all, delete-orphan") + billing_plans = relationship("BillingPlan", back_populates="organisation", cascade="all, delete-orphan") + invitations = relationship("Invitation", back_populates="organisation", cascade="all, delete-orphan") + products = relationship("Product", back_populates="organisation", cascade="all, delete-orphan") + contact_us = relationship("ContactUs", back_populates="organisation", cascade="all, delete-orphan") billing_plans = relationship( - "BillingPlan", back_populates="organization", cascade="all, delete-orphan" + "BillingPlan", back_populates="organisation", cascade="all, delete-orphan" ) invitations = relationship( - "Invitation", back_populates="organization", cascade="all, delete-orphan" + "Invitation", back_populates="organisation", cascade="all, delete-orphan" ) products = relationship( - "Product", back_populates="organization", cascade="all, delete-orphan" + "Product", back_populates="organisation", cascade="all, delete-orphan" ) - sales = relationship('Sales', back_populates='organization', + sales = relationship('Sales', back_populates='organisation', cascade='all, delete-orphan') def __str__(self): diff --git a/api/v1/models/permissions/permissions.py b/api/v1/models/permissions/permissions.py index 68079c98e..e0671d645 100644 --- a/api/v1/models/permissions/permissions.py +++ b/api/v1/models/permissions/permissions.py @@ -5,6 +5,5 @@ class Permission(BaseTableModel): __tablename__ = 'permissions' - #id = Column(String, primary_key=True, index=True, default=lambda: str(uuid7())) title = Column(String, unique=True, nullable=False) diff --git a/api/v1/models/permissions/role.py b/api/v1/models/permissions/role.py index 2c7475a41..f2a382a85 100644 --- a/api/v1/models/permissions/role.py +++ b/api/v1/models/permissions/role.py @@ -5,7 +5,6 @@ class Role(BaseTableModel): __tablename__ = 'roles' - id = Column(String, primary_key=True, index=True, default=lambda: str(uuid7())) name = Column(String, unique=True, nullable=False) description = Column(Text, nullable=True) is_builtin = Column(Boolean, default=False) # True for built-in roles, False for custom roles diff --git a/api/v1/models/permissions/user_org_role.py b/api/v1/models/permissions/user_org_role.py index d5d4fba81..32d84eaf4 100644 --- a/api/v1/models/permissions/user_org_role.py +++ b/api/v1/models/permissions/user_org_role.py @@ -1,10 +1,10 @@ -from sqlalchemy import Table, Column, ForeignKey, String +from sqlalchemy import Table, Column, ForeignKey, String, Boolean from api.v1.models.base_model import BaseTableModel -user_organization_roles = Table( - 'user_organization_roles', BaseTableModel.metadata, +user_organisation_roles = Table( + 'user_organisation_roles', BaseTableModel.metadata, Column("user_id", String, ForeignKey("users.id", ondelete="CASCADE"), primary_key=True), - Column("organization_id", String, ForeignKey("organizations.id", ondelete="CASCADE"), primary_key=True), + Column("organisation_id", String, ForeignKey("organisations.id", ondelete="CASCADE"), primary_key=True), Column('role_id', String, ForeignKey('roles.id', ondelete='CASCADE'), nullable=True), - Column('status', String(20), nullable=False, default="active") -) + Column('is_owner', Boolean, server_default='false') +) \ No newline at end of file diff --git a/api/v1/models/product.py b/api/v1/models/product.py index 9328543b7..75546e4fb 100644 --- a/api/v1/models/product.py +++ b/api/v1/models/product.py @@ -35,7 +35,7 @@ class Product(BaseTableModel): description = Column(Text, nullable=True) price = Column(Numeric, nullable=False) org_id = Column( - String, ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False + String, ForeignKey("organisations.id", ondelete="CASCADE"), nullable=False ) category_id = Column( String, ForeignKey("product_categories.id", ondelete="CASCADE"), nullable=False @@ -52,7 +52,7 @@ class Product(BaseTableModel): variants = relationship( "ProductVariant", back_populates="product", cascade="all, delete-orphan" ) - organization = relationship("Organization", back_populates="products") + organisation = relationship("Organisation", back_populates="products") category = relationship("ProductCategory", back_populates="products") sales = relationship('Sales', back_populates='product', cascade='all, delete-orphan') diff --git a/api/v1/models/sales.py b/api/v1/models/sales.py index c6c899207..cbfa312a5 100644 --- a/api/v1/models/sales.py +++ b/api/v1/models/sales.py @@ -11,7 +11,7 @@ class Sales(BaseTableModel): amount = Column(Float, nullable=False) product_id = Column(String, ForeignKey('products.id', ondelete='CASCADE'), nullable=False) - organization_id = Column(String, ForeignKey('organizations.id', ondelete='CASCADE'), + organisation_id = Column(String, ForeignKey('organisations.id', ondelete='CASCADE'), nullable=False) payment_id = Column(String, ForeignKey('payments.id', ondelete='CASCADE'), nullable=True) @@ -20,7 +20,7 @@ class Sales(BaseTableModel): product = relationship("Product", back_populates="sales") - organization = relationship("Organization", back_populates="sales") + organisation = relationship("Organisation", back_populates="sales") payment = relationship("Payment", back_populates="sales") __table_args__ = ( diff --git a/api/v1/models/user.py b/api/v1/models/user.py index b33da7f5f..8dc48e673 100644 --- a/api/v1/models/user.py +++ b/api/v1/models/user.py @@ -3,7 +3,7 @@ from sqlalchemy import Column, String, text, Boolean from sqlalchemy.orm import relationship -from api.v1.models.associations import user_organization_association +from api.v1.models.associations import user_organisation_association from api.v1.models.base_model import BaseTableModel @@ -23,8 +23,8 @@ class User(BaseTableModel): profile = relationship( "Profile", uselist=False, back_populates="user", cascade="all, delete-orphan" ) - organizations = relationship( - "Organization", secondary=user_organization_association, back_populates="users" + organisations = relationship( + "Organisation", secondary=user_organisation_association, back_populates="users" ) notifications = relationship( "Notification", back_populates="user", cascade="all, delete-orphan" diff --git a/api/v1/routes/__init__.py b/api/v1/routes/__init__.py index 8e9ce4ea3..5de9042b6 100644 --- a/api/v1/routes/__init__.py +++ b/api/v1/routes/__init__.py @@ -5,7 +5,7 @@ from api.v1.routes.auth import auth from api.v1.routes.newsletter import newsletter from api.v1.routes.user import user_router -from api.v1.routes.product import product, non_organization_product +from api.v1.routes.product import product, non_organisation_product from api.v1.routes.product_comment import product_comment from api.v1.routes.notification import notification from api.v1.routes.testimonial import testimonial @@ -18,7 +18,7 @@ from api.v1.routes.profiles import profile from api.v1.routes.jobs import jobs from api.v1.routes.payment import payment -from api.v1.routes.organization import organization +from api.v1.routes.organisation import organisation from api.v1.routes.request_password import pwd_reset from api.v1.routes.activity_logs import activity_logs from api.v1.routes.contact_us import contact_us @@ -52,8 +52,8 @@ api_version_one.include_router(pwd_reset) api_version_one.include_router(user_router) api_version_one.include_router(profile) -api_version_one.include_router(organization) -api_version_one.include_router(non_organization_product) +api_version_one.include_router(organisation) +api_version_one.include_router(non_organisation_product) api_version_one.include_router(product) api_version_one.include_router(payment) api_version_one.include_router(bill_plan) diff --git a/api/v1/routes/analytics.py b/api/v1/routes/analytics.py index e8dd3c2a9..f23b6a9c2 100644 --- a/api/v1/routes/analytics.py +++ b/api/v1/routes/analytics.py @@ -14,7 +14,7 @@ async def get_analytics_line_chart_data(token: Annotated[OAuth2, Depends(oauth2_scheme)], db: Annotated[Session, Depends(get_db)]): """ - Retrieves analytics line-chart-data for an organization or super admin. + Retrieves analytics line-chart-data for an organisation or super admin. Args: token: access_token db: database Session object diff --git a/api/v1/routes/auth.py b/api/v1/routes/auth.py index 2de798942..90c2a4d6a 100644 --- a/api/v1/routes/auth.py +++ b/api/v1/routes/auth.py @@ -12,8 +12,8 @@ from api.v1.schemas.user import LoginRequest, UserCreate, EmailRequest from api.v1.schemas.token import TokenRequest from api.v1.schemas.user import UserCreate, MagicLinkRequest -from api.v1.services.organization import organization_service -from api.v1.schemas.organization import CreateUpdateOrganization +from api.v1.services.organisation import organisation_service +from api.v1.schemas.organisation import CreateUpdateOrganisation from api.db.database import get_db from api.v1.services.user import user_service from api.v1.services.auth import AuthService @@ -28,10 +28,10 @@ def register(background_tasks: BackgroundTasks, response: Response, user_schema: # Create user account user = user_service.create(db=db, schema=user_schema) - # create an organization for the user - org = CreateUpdateOrganization(name=f"{user.first_name}'s Organization", + # create an organisation for the user + org = CreateUpdateOrganisation(name=f"{user.first_name}'s Organisation", email=user.email) - user_org = organization_service.create(db=db, schema=org, user=user) + user_org = organisation_service.create(db=db, schema=org, user=user) # Create access and refresh tokens access_token = user_service.create_access_token(user_id=user.id) diff --git a/api/v1/routes/billing_plan.py b/api/v1/routes/billing_plan.py index 064a8157f..701f91f9f 100644 --- a/api/v1/routes/billing_plan.py +++ b/api/v1/routes/billing_plan.py @@ -15,15 +15,15 @@ bill_plan = APIRouter(prefix="/organisations", tags=["Billing-Plan"]) -@bill_plan.get("/{organization_id}/billing-plans", response_model=success_response) +@bill_plan.get("/{organisation_id}/billing-plans", response_model=success_response) async def retrieve_all_billing_plans( - organization_id: str, db: Session = Depends(get_db) + organisation_id: str, db: Session = Depends(get_db) ): """ Endpoint to get all billing plans """ - plans = billing_plan_service.fetch_all(db=db, organization_id=organization_id) + plans = billing_plan_service.fetch_all(db=db, organisation_id=organisation_id) return success_response( status_code=status.HTTP_200_OK, diff --git a/api/v1/routes/dashboard.py b/api/v1/routes/dashboard.py index 43dc4d65c..ab23bebe5 100644 --- a/api/v1/routes/dashboard.py +++ b/api/v1/routes/dashboard.py @@ -104,7 +104,7 @@ async def get_analytics_summary( end_date: datetime = None ): """ - Retrieves analytics summary data for an organization or super admin. + Retrieves analytics summary data for an organisation or super admin. Args: token: access_token db: database Session object diff --git a/api/v1/routes/invitations.py b/api/v1/routes/invitations.py index 3206c07fc..f501d26e2 100644 --- a/api/v1/routes/invitations.py +++ b/api/v1/routes/invitations.py @@ -11,7 +11,7 @@ invites = APIRouter(prefix="/invite", tags=["Invitation Management"]) -# generate invitation link to join organization +# generate invitation link to join organisation @invites.post("/create", tags=["Invitation Management"]) async def generate_invite_link( invite_schema: invitations.InvitationCreate, @@ -22,11 +22,11 @@ async def generate_invite_link( return invite.InviteService.create(invite_schema, request, session) -# Add user to organization +# Add user to organisation @invites.post("/accept", tags=["Invitation Management"]) -async def add_user_to_organization( +async def add_user_to_organisation( request: Request, - user_data: invitations.UserAddToOrganization, + user_data: invitations.UserAddToOrganisation, session: Session = Depends(get_session), current_user: User = Depends(user_service.get_current_user) ): @@ -40,7 +40,7 @@ async def add_user_to_organization( logging.info(f"Processing invitation ID: {invite_id}") - return invite.InviteService.add_user_to_organization(invite_id, session) + return invite.InviteService.add_user_to_organisation(invite_id, session) @invites.delete("", status_code=status.HTTP_204_NO_CONTENT) diff --git a/api/v1/routes/organization.py b/api/v1/routes/organisation.py similarity index 56% rename from api/v1/routes/organization.py rename to api/v1/routes/organisation.py index 4fd0bcaef..ec66b3939 100644 --- a/api/v1/routes/organization.py +++ b/api/v1/routes/organisation.py @@ -6,124 +6,124 @@ from api.utils.success_response import success_response from api.v1.models.user import User -from api.v1.schemas.organization import ( - CreateUpdateOrganization, +from api.v1.schemas.organisation import ( + CreateUpdateOrganisation, PaginatedOrgUsers, - OrganizationBase, + OrganisationBase, ) from api.db.database import get_db from api.v1.services.user import user_service -from api.v1.services.organization import organization_service +from api.v1.services.organisation import organisation_service from typing import Annotated -organization = APIRouter(prefix="/organisations", tags=["Organizations"]) +organisation = APIRouter(prefix="/organisations", tags=["Organisations"]) -@organization.post( +@organisation.post( "", response_model=success_response, status_code=status.HTTP_201_CREATED ) -def create_organization( - schema: CreateUpdateOrganization, +def create_organisation( + schema: CreateUpdateOrganisation, db: Session = Depends(get_db), current_user: User = Depends(user_service.get_current_user), ): - """Endpoint to create a new organization""" + """Endpoint to create a new organisation""" - new_org = organization_service.create( + new_org = organisation_service.create( db=db, schema=schema, user=current_user, ) # For some reason this line is needed before data can show in the response - print("Created Organization:", new_org) + print("Created Organisation:", new_org) return success_response( status_code=status.HTTP_201_CREATED, - message="Organization created successfully", + message="Organisation created successfully", data=jsonable_encoder(new_org), ) -@organization.get( +@organisation.get( "/{org_id}/users", response_model=success_response, status_code=status.HTTP_200_OK, ) -async def get_organization_users( +async def get_organisation_users( org_id: str, db: Session = Depends(get_db), current_user: User = Depends(user_service.get_current_user), skip: int = 1, limit: int = 10, ): - """Endpoint to fetch all users in an organization""" + """Endpoint to fetch all users in an organisation""" - return organization_service.paginate_users_in_organization(db, org_id, skip, limit) + return organisation_service.paginate_users_in_organisation(db, org_id, skip, limit) -@organization.get("/{org_id}/users/export", status_code=200) -async def export_organization_member_data_to_csv( +@organisation.get("/{org_id}/users/export", status_code=200) +async def export_organisation_member_data_to_csv( org_id: str, db: Session = Depends(get_db), current_user: User = Depends(user_service.get_current_super_admin), ): - """Endpoint to export organization users data to csv""" + """Endpoint to export organisation users data to csv""" - csv_file = organization_service.export_organization_members(db=db, org_id=org_id) + csv_file = organisation_service.export_organisation_members(db=db, org_id=org_id) # Stream the response as a CSV file download response = StreamingResponse(csv_file, media_type="text/csv") response.headers["Content-Disposition"] = ( - f"attachment; filename=organization_{org_id}_members.csv" + f"attachment; filename=organisation_{org_id}_members.csv" ) response.status_code = 200 return response -@organization.patch("/{org_id}", response_model=success_response, status_code=200) -async def update_organization( +@organisation.patch("/{org_id}", response_model=success_response, status_code=200) +async def update_organisation( org_id: str, - schema: CreateUpdateOrganization, + schema: CreateUpdateOrganisation, db: Session = Depends(get_db), current_user: User = Depends(user_service.get_current_user), ): - """Endpoint to update organization""" + """Endpoint to update organisation""" - updated_organization = organization_service.update(db, org_id, schema, current_user) + updated_organisation = organisation_service.update(db, org_id, schema, current_user) return success_response( status_code=status.HTTP_200_OK, - message="Organization updated successfully", - data=jsonable_encoder(updated_organization), + message="Organisation updated successfully", + data=jsonable_encoder(updated_organisation), ) -@organization.get("", status_code=status.HTTP_200_OK) -def get_all_organizations( +@organisation.get("", status_code=status.HTTP_200_OK) +def get_all_organisations( super_admin: Annotated[User, Depends(user_service.get_current_super_admin)], db: Session = Depends(get_db), ): - orgs = organization_service.fetch_all(db) + orgs = organisation_service.fetch_all(db) return success_response( status_code=status.HTTP_200_OK, - message="Retrived all organizations information Successfully", + message="Retrived all organisations information Successfully", data=jsonable_encoder(orgs), ) -@organization.delete("/{org_id}") -async def delete_organization( +@organisation.delete("/{org_id}") +async def delete_organisation( org_id: str, db: Session = Depends(get_db), current_user: User = Depends(user_service.get_current_super_admin), ): - check = organization_service.check_organization_exist(db, org_id) + check = organisation_service.check_organisation_exist(db, org_id) if check: - organization_service.delete(db, id=org_id) + organisation_service.delete(db, id=org_id) return success_response( status_code=status.HTTP_200_OK, - message="Organization with ID {org_id} deleted successfully", + message="Organisation with ID {org_id} deleted successfully", ) diff --git a/api/v1/routes/permissions/roles.py b/api/v1/routes/permissions/roles.py index 65a555021..fd3698573 100644 --- a/api/v1/routes/permissions/roles.py +++ b/api/v1/routes/permissions/roles.py @@ -18,7 +18,7 @@ from api.v1.models.user import User from api.v1.services.user import user_service from api.utils.success_response import success_response -from api.v1.services.organization import organization_service as org_service +from api.v1.services.organisation import organisation_service as org_service role_perm = APIRouter(tags=["permissions management"]) @@ -57,7 +57,7 @@ def create_built_in_role_endpoint( ) def assign_role( request: RoleAssignRequest, - org_id: str = Path(..., description="The ID of the organization"), + org_id: str = Path(..., description="The ID of the organisation"), user_id: str = Path(..., description="The ID of the user"), db: Session = Depends(get_db), current_user: User = Depends(user_service.get_current_user), @@ -70,7 +70,7 @@ def assign_role( response_model=RemoveUserFromRoleResponse, ) def remove_user_from_role( - org_id: str = Path(..., description="The ID of the organization"), + org_id: str = Path(..., description="The ID of the organisation"), user_id: str = Path(..., description="The ID of the user"), role_id: str = Path(..., description="The ID of the role"), db: Session = Depends(get_db), @@ -119,16 +119,16 @@ def delete_role( response_model=List[RoleResponse], tags=["Fetch Roles"], ) -def get_roles_for_organization( - org_id: str = Path(..., description="The ID of the organization"), +def get_roles_for_organisation( + org_id: str = Path(..., description="The ID of the organisation"), db: Session = Depends(get_db), current_user: User = Depends(user_service.get_current_super_admin), ): - roles = (role_service.get_roles_by_organization(db, org_id),) + roles = (role_service.get_roles_by_organisation(db, org_id),) if not roles: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, - detail="Roles not found for the given organization", + detail="Roles not found for the given organisation", ) return success_response( status_code=status.HTTP_200_OK, message="Roles fetched successfully", data=roles diff --git a/api/v1/routes/product.py b/api/v1/routes/product.py index 4ccef644a..cde3239a8 100644 --- a/api/v1/routes/product.py +++ b/api/v1/routes/product.py @@ -27,10 +27,10 @@ from api.v1.services.user import user_service from api.v1.models import User -non_organization_product = APIRouter(prefix="/products", tags=["Products"]) +non_organisation_product = APIRouter(prefix="/products", tags=["Products"]) -@non_organization_product.get("", response_model=success_response, status_code=200) +@non_organisation_product.get("", response_model=success_response, status_code=200) async def get_all_products( current_user: Annotated[User, Depends(user_service.get_current_super_admin)], limit: Annotated[int, Query(ge=1, description="Number of products per page")] = 10, @@ -43,7 +43,7 @@ async def get_all_products( # categories -@non_organization_product.post("/categories", status_code=status.HTTP_201_CREATED) +@non_organisation_product.post("/categories", status_code=status.HTTP_201_CREATED) def create_product_category( category_schema: ProductCategoryCreate, current_user: User = Depends(user_service.get_current_user), @@ -71,7 +71,7 @@ def create_product_category( ) -@non_organization_product.get( +@non_organisation_product.get( "/categories", response_model=success_response, status_code=200 ) def retrieve_categories( @@ -139,7 +139,7 @@ async def get_product_detail( This endpoint retrieve details about a product Args: - org_id (UUID): The unique identifier of the organization + org_id (UUID): The unique identifier of the organisation product_id (UUID): The unique identifier of the product to be retrieved. db (Session): The database session, provided by the `get_db` dependency. current_user (User): The currently authenticated user, obtained from the `get_current_user` dependency. @@ -151,7 +151,7 @@ async def get_product_detail( HTTPException: If the product with the specified `id` does not exist, a 404 error is raised. """ - product = product_service.fetch_single_by_organization( + product = product_service.fetch_single_by_organisation( db, org_id, product_id, current_user ) @@ -246,7 +246,7 @@ def delete_product( status_code=status.HTTP_200_OK, response_model=ProductList, ) -def get_organization_products( +def get_organisation_products( org_id: str, current_user: Annotated[User, Depends(user_service.get_current_user)], limit: Annotated[int, Query(ge=1, description="Number of products per page")] = 10, @@ -254,14 +254,14 @@ def get_organization_products( db: Session = Depends(get_db), ): """ - Endpoint to retrieve a paginated list of products of an organization. + Endpoint to retrieve a paginated list of products of an organisation. Query parameter: - limit: Number of product per page (default: 10, minimum: 1) - page: Page number (starts from 1) """ - products = product_service.fetch_by_organization( + products = product_service.fetch_by_organisation( db, user=current_user, org_id=org_id, limit=limit, page=page ) @@ -288,7 +288,7 @@ def get_organization_products( return success_response( status_code=200, - message="Successfully fetched organizations products", + message="Successfully fetched organisations products", data=data, ) diff --git a/api/v1/routes/user.py b/api/v1/routes/user.py index 7819cc5f7..75f745913 100644 --- a/api/v1/routes/user.py +++ b/api/v1/routes/user.py @@ -197,7 +197,7 @@ async def get_users_by_role( @user_router.get('/organisations', status_code=200, response_model=success_response) -def get_current_user_organizations( +def get_current_user_organisations( db: Session = Depends(get_db), current_user: User = Depends(user_service.get_current_user) ): @@ -206,7 +206,7 @@ def get_current_user_organizations( return success_response( status_code=200, message='Organisations fetched successfully', - data=jsonable_encoder(current_user.organizations) + data=jsonable_encoder(current_user.organisations) ) diff --git a/api/v1/schemas/invitations.py b/api/v1/schemas/invitations.py index 6d8d3dcd8..21482269b 100644 --- a/api/v1/schemas/invitations.py +++ b/api/v1/schemas/invitations.py @@ -1,10 +1,10 @@ from pydantic import BaseModel, EmailStr -class UserAddToOrganization(BaseModel): +class UserAddToOrganisation(BaseModel): invitation_link: str class InvitationCreate(BaseModel): user_email: EmailStr - organization_id: str + organisation_id: str diff --git a/api/v1/schemas/organization.py b/api/v1/schemas/organisation.py similarity index 74% rename from api/v1/schemas/organization.py rename to api/v1/schemas/organisation.py index 91bb35f8b..fe8767800 100644 --- a/api/v1/schemas/organization.py +++ b/api/v1/schemas/organisation.py @@ -5,8 +5,8 @@ from api.utils.success_response import success_response -class OrganizationBase(BaseModel): - """Base organization schema""" +class OrganisationBase(BaseModel): + """Base organisation schema""" id: str created_at: datetime @@ -21,8 +21,8 @@ class OrganizationBase(BaseModel): description: Optional[str] = None -class CreateUpdateOrganization(BaseModel): - """Organization schema to create or update organization""" +class CreateUpdateOrganisation(BaseModel): + """Organisation schema to create or update organisation""" name: str email: Optional[EmailStr] = None @@ -34,8 +34,8 @@ class CreateUpdateOrganization(BaseModel): description: Optional[str] = None -class AddUpdateOrganizationRole(BaseModel): - """Schema to update a user role in an organization""" +class AddUpdateOrganisationRole(BaseModel): + """Schema to update a user role in an organisation""" role: str user_id: str @@ -48,15 +48,15 @@ def role_validator(cls, value): return value -class RemoveUserFromOrganization(BaseModel): - """Schema to delete a user role in an organization""" +class RemoveUserFromOrganisation(BaseModel): + """Schema to delete a user role in an organisation""" user_id: str org_id: str class PaginatedOrgUsers(BaseModel): - """Describe response object for paginated users in organization""" + """Describe response object for paginated users in organisation""" page: int per_page: int per_page: int diff --git a/api/v1/schemas/payment.py b/api/v1/schemas/payment.py index 9283ac8a5..a424a6a4f 100644 --- a/api/v1/schemas/payment.py +++ b/api/v1/schemas/payment.py @@ -43,7 +43,7 @@ class PaymentListResponse(BaseModel): class PaymentDetail(BaseModel): - organization_id: str + organisation_id: str plan_id: str full_name: str billing_option: str diff --git a/api/v1/schemas/plans.py b/api/v1/schemas/plans.py index b5b267baf..4f48ded0f 100644 --- a/api/v1/schemas/plans.py +++ b/api/v1/schemas/plans.py @@ -8,7 +8,7 @@ class CreateSubscriptionPlan(BaseModel): price: int duration: str currency: str - organization_id: str + organisation_id: str features: List[str] diff --git a/api/v1/schemas/product.py b/api/v1/schemas/product.py index b5675ca05..4426fac4b 100644 --- a/api/v1/schemas/product.py +++ b/api/v1/schemas/product.py @@ -88,12 +88,12 @@ class ProductVariantBase(BaseModel): stock: int -class ProductDetailOrganization(BaseModel): +class ProductDetailOrganisation(BaseModel): id: str company_name: str company_email: EmailStr | None = None industry: str | None = None - organization_type: str | None = None + organisation_type: str | None = None country: str | None = None state: str | None = None address: str | None = None @@ -107,7 +107,7 @@ class ProductDetail(BaseModel): name: str description: str | None = None price: float - organization: ProductDetailOrganization + organisation: ProductDetailOrganisation quantity: int image_url: str status: str diff --git a/api/v1/schemas/role.py b/api/v1/schemas/role.py index 21212f64c..fd4a84f59 100644 --- a/api/v1/schemas/role.py +++ b/api/v1/schemas/role.py @@ -4,7 +4,7 @@ class RoleCreate(BaseModel): role_name: str - organization_id: str + organisation_id: str permission_ids: List[str] diff --git a/api/v1/services/analytics.py b/api/v1/services/analytics.py index 5645e64b3..04aa2a183 100644 --- a/api/v1/services/analytics.py +++ b/api/v1/services/analytics.py @@ -9,7 +9,7 @@ from api.v1.services.user import user_service from api.core.base.services import Service from api.v1.services.user import oauth2_scheme -from api.v1.models.user import user_organization_association +from api.v1.models.user import user_organisation_association from api.v1.models.sales import Sales from api.v1.models.product import Product from api.v1.models.user import User @@ -44,17 +44,17 @@ def get_analytics_line_chart(self, token: Annotated[OAuth2, Depends(oauth2_schem # check if the analytics-line-data is for org admin if not user.is_super_admin: - user_organization: object = (db.query(user_organization_association) + user_organisation: object = (db.query(user_organisation_association) .filter_by(user_id=user.id).first()) - if not user_organization: + if not user_organisation: return AnalyticsChartsResponse( - message='User is not part of Any organization yet.', + message='User is not part of Any organisation yet.', status='success', status_code=200, data={month: 0 for month in DATA.values()} ) data = self.get_line_chart_data(db, super_admin=False, - org_id=user_organization.organization_id) + org_id=user_organisation.organisation_id) message: str = 'Successfully retrieved line-charts' # check if user is a super admin @@ -74,7 +74,7 @@ def get_line_chart_data(self, db: Annotated[Session, Depends(get_db)], Args: db: database session object super_admin: boolean signifying revenues for super admin - organization_id: the organization id of the user + organisation_id: the organisation id of the user Returns: MONTHS_AND_DATA: a dict conatining the months(str) and revenue(float) for each month """ @@ -100,7 +100,7 @@ def get_year_revenue(self, db: Annotated[Session, Depends(get_db)], Args: db: database session object super_admin: boolean signifying revenues for super admin - organization_id: the organization id of the user + organisation_id: the organisation id of the user Returns: query result: a list conatining the rows of months(int) and revenue(int) """ @@ -110,7 +110,7 @@ def get_year_revenue(self, db: Annotated[Session, Depends(get_db)], ) if not super_admin: - query = query.filter(Sales.organization_id == org_id) + query = query.filter(Sales.organisation_id == org_id) query = query.group_by( cast(extract('month', Sales.created_at), Integer) @@ -145,9 +145,9 @@ def get_analytics_summary(self, token: Annotated[OAuth2, Depends(oauth2_scheme)] data = self.get_summary_data_super_admin(db, start_date, end_date) message = "Admin Statistics Fetched" else: - user_organization = db.query( - user_organization_association).filter_by(user_id=user.id).first() - if not user_organization: + user_organisation = db.query( + user_organisation_association).filter_by(user_id=user.id).first() + if not user_organisation: data = { "revenue": { "current_month": 0, @@ -169,10 +169,10 @@ def get_analytics_summary(self, token: Annotated[OAuth2, Depends(oauth2_scheme)] "difference_an_hour_ago": 0 } } - message = "User is not part of any organization" + message = "User is not part of any organisation" else: - data = self.get_summary_data_organization( - db, user_organization.organization_id, start_date, end_date) + data = self.get_summary_data_organisation( + db, user_organisation.organisation_id, start_date, end_date) message = "User Statistics Fetched" return AnalyticsSummaryResponse( @@ -223,31 +223,31 @@ def get_summary_data_super_admin(self, db: Session, start_date: datetime, end_da } } - def get_summary_data_organization(self, db: Session, org_id: str, start_date: datetime, end_date: datetime) -> dict: + def get_summary_data_organisation(self, db: Session, org_id: str, start_date: datetime, end_date: datetime) -> dict: total_revenue = db.query(func.sum(Sales.amount)).filter(and_( - Sales.organization_id == org_id, Sales.created_at.between(start_date, end_date))).scalar() or 0 + Sales.organisation_id == org_id, Sales.created_at.between(start_date, end_date))).scalar() or 0 subscriptions = db.query(func.count(BillingPlan.id)).filter(and_( - BillingPlan.organization_id == org_id, BillingPlan.created_at.between(start_date, end_date))).scalar() or 0 + BillingPlan.organisation_id == org_id, BillingPlan.created_at.between(start_date, end_date))).scalar() or 0 sales = db.query(func.count(Sales.id)).filter(and_( - Sales.organization_id == org_id, Sales.created_at.between(start_date, end_date))).scalar() or 0 + Sales.organisation_id == org_id, Sales.created_at.between(start_date, end_date))).scalar() or 0 last_month_start = start_date - timedelta(days=30) last_month_revenue = db.query(func.sum(Sales.amount)).filter(and_( - Sales.organization_id == org_id, Sales.created_at.between(last_month_start, start_date))).scalar() or 0 + Sales.organisation_id == org_id, Sales.created_at.between(last_month_start, start_date))).scalar() or 0 last_month_subscriptions = db.query(func.count(BillingPlan.id)).filter(and_( - BillingPlan.organization_id == org_id, BillingPlan.created_at.between(last_month_start, start_date))).scalar() or 0 + BillingPlan.organisation_id == org_id, BillingPlan.created_at.between(last_month_start, start_date))).scalar() or 0 last_month_sales = db.query(func.count(Sales.id)).filter(and_( - Sales.organization_id == org_id, Sales.created_at.between(last_month_start, start_date))).scalar() or 0 + Sales.organisation_id == org_id, Sales.created_at.between(last_month_start, start_date))).scalar() or 0 last_hour = datetime.utcnow() - timedelta(hours=1) active_now = db.query(func.count(User.id)).filter(and_( User.is_active == True, - User.organizations.any(id=org_id) + User.organisations.any(id=org_id) )).scalar() or 0 previous_hour = last_hour - timedelta(hours=1) active_previous_hour = db.query(func.count(User.id)).filter(and_( User.is_active == True, - User.organizations.any(id=org_id), + User.organisations.any(id=org_id), User.created_at >= last_hour - timedelta(hours=1), User.created_at < last_hour )).scalar() or 0 diff --git a/api/v1/services/api_tests.py b/api/v1/services/api_tests.py index ccb43e025..731040508 100644 --- a/api/v1/services/api_tests.py +++ b/api/v1/services/api_tests.py @@ -316,7 +316,7 @@ def test_create_profile(self): "Expected status code 200, got {}".format(response.status_code), ) - def test_get_all_organization_billing(self): + def test_get_all_organisation_billing(self): headers = {"Authorization": f"Bearer {self.access_token}"} response = requests.get( f"{self.baseUrl}/api/v1/organisation/billing-plans", headers=headers diff --git a/api/v1/services/contact.py b/api/v1/services/contact.py index 8d56b165b..5080ae50d 100644 --- a/api/v1/services/contact.py +++ b/api/v1/services/contact.py @@ -1,8 +1,8 @@ from api.db.database import get_db from api.v1.models.contact_us import ContactUs from fastapi import HTTPException, status -from api.v1.models.associations import user_organization_association -from api.v1.services.organization import OrganizationService +from api.v1.models.associations import user_organisation_association +from api.v1.services.organisation import OrganisationService class ContactMessage(object): @@ -17,8 +17,8 @@ def fetch_message(self, db, message_id): return result def check_admin_access(self, db, admin_id, org_id): - org_service_obj = OrganizationService() - role = org_service_obj.get_organization_user_role(user_id=admin_id, org_id=org_id, db=db) + org_service_obj = OrganisationService() + role = org_service_obj.get_organisation_user_role(user_id=admin_id, org_id=org_id, db=db) if role != 'admin': self.raise_unauthorized_admin() diff --git a/api/v1/services/google_oauth.py b/api/v1/services/google_oauth.py index 060f822e0..22d6c7fb1 100644 --- a/api/v1/services/google_oauth.py +++ b/api/v1/services/google_oauth.py @@ -2,7 +2,7 @@ from datetime import datetime, timezone from api.core.dependencies.email_sender import send_email from api.db.database import get_db -from api.v1.models.organization import Organization +from api.v1.models.organisation import Organisation from api.v1.models.user import User from api.v1.models.oauth import OAuth from api.v1.models.user import User @@ -13,7 +13,7 @@ from api.v1.services.user import user_service from api.v1.schemas.google_oauth import Tokens from api.v1.services.profile import profile_service -from api.v1.models.associations import user_organization_association +from api.v1.models.associations import user_organisation_association class GoogleOauthServices(Service): @@ -207,15 +207,15 @@ def create_new_user( access_token=user_service.create_access_token(new_user.id), refresh_token=user_service.create_refresh_token(new_user.id) ) - organization = Organization( - name = f'{new_user.first_name} {new_user.last_name} Organization' + organisation = Organisation( + name = f'{new_user.first_name} {new_user.last_name} Organisation' ) - db.add_all([profile, oauth_data, organization]) + db.add_all([profile, oauth_data, organisation]) db.commit() # TODO: Ensure to update this later - stmt = user_organization_association.insert().values( - user_id=new_user.id, organization_id=organization.id, role="owner" + stmt = user_organisation_association.insert().values( + user_id=new_user.id, organisation_id=organisation.id, role="owner" ) db.execute(stmt) db.commit() diff --git a/api/v1/services/invite.py b/api/v1/services/invite.py index ba80d8a63..b78546f45 100644 --- a/api/v1/services/invite.py +++ b/api/v1/services/invite.py @@ -7,13 +7,13 @@ from collections import OrderedDict from fastapi.responses import JSONResponse from api.v1.models.invitation import Invitation -from api.v1.models.organization import Organization +from api.v1.models.organisation import Organisation from api.v1.models.user import User from sqlalchemy.exc import IntegrityError from api.v1.models.permissions.role import Role from api.v1.schemas.permissions.roles import RoleCreate from api.v1.services.permissions.role_service import role_service -from api.v1.models.permissions.user_org_role import user_organization_roles +from api.v1.models.permissions.user_org_role import user_organisation_roles from api.v1.schemas import invitations from api.core.base.services import Service from urllib.parse import urlencode @@ -26,30 +26,30 @@ def create( ): user_email = session.query(User).filter_by(email=invite.user_email).first() - org = session.query(Organization).filter_by(id=invite.organization_id).first() + org = session.query(Organisation).filter_by(id=invite.organisation_id).first() if not user_email or not org: exceptions = HTTPException( - status_code=404, detail="invalid user or organization id" + status_code=404, detail="invalid user or organisation id" ) print(exceptions) raise exceptions - user_organizations = session.execute( - user_organization_roles.select().where( - user_organization_roles.c.user_id == user_email.id, - user_organization_roles.c.organization_id == org.id, + user_organisations = session.execute( + user_organisation_roles.select().where( + user_organisation_roles.c.user_id == user_email.id, + user_organisation_roles.c.organisation_id == org.id, ) ).fetchall() - if user_organizations: - logging.warning(f"User {user_email.id} already in organization {org.id}") - raise HTTPException(status_code=400, detail="User already in organization") + if user_organisations: + logging.warning(f"User {user_email.id} already in organisation {org.id}") + raise HTTPException(status_code=400, detail="User already in organisation") expiration = datetime.now(utc) + timedelta(days=1) new_invite = Invitation( user_id=user_email.id, - organization_id=invite.organization_id, + organisation_id=invite.organisation_id, expires_at=expiration, ) @@ -73,7 +73,7 @@ def create( @staticmethod - def add_user_to_organization(invite_id: str, session: Session): + def add_user_to_organisation(invite_id: str, session: Session): logging.info(f"Processing invitation ID: {invite_id}") # Fetch the invitation @@ -96,13 +96,13 @@ def add_user_to_organization(invite_id: str, session: Session): session.commit() raise HTTPException(status_code=400, detail="Expired invitation link") - # Fetch user and organization + # Fetch user and organisation user = session.query(User).filter_by(id=invite.user_id).first() - org = session.query(Organization).filter_by(id=invite.organization_id).first() + org = session.query(Organisation).filter_by(id=invite.organisation_id).first() if not org: - logging.error(f"Organization not found: {invite.organization_id}") - raise HTTPException(status_code=404, detail="Invalid organization ID") + logging.error(f"Organisation not found: {invite.organisation_id}") + raise HTTPException(status_code=404, detail="Invalid organisation ID") if not user: logging.error(f"User not found: {invite.user_id}") @@ -120,25 +120,25 @@ def add_user_to_organization(invite_id: str, session: Session): role_create_data = RoleCreate(name=default_role_name, is_builtin=True) role = role_service.create_role(session, role_create_data) - # Check if the user is already assigned to the role within the organization - existing_assignment = session.query(user_organization_roles).filter_by( + # Check if the user is already assigned to the role within the organisation + existing_assignment = session.query(user_organisation_roles).filter_by( user_id=user.id, - organization_id=org.id, + organisation_id=org.id, role_id=role.id ).first() if existing_assignment: - #logging.warning(f"User {user} is already assigned to the role '{role.name}' in organization {org.id}") + #logging.warning(f"User {user} is already assigned to the role '{role.name}' in organisation {org.id}") raise HTTPException( status_code=400, - detail=f"User is already assigned to the role in this organization" + detail=f"User is already assigned to the role in this organisation" ) try: - # Insert user-organization-role association - stmt = insert(user_organization_roles).values( + # Insert user-organisation-role association + stmt = insert(user_organisation_roles).values( user_id=user.id, - organization_id=org.id, + organisation_id=org.id, role_id=role.id, status="active" # Default status ) @@ -151,10 +151,10 @@ def add_user_to_organization(invite_id: str, session: Session): response = OrderedDict( [ ("status", "success"), - ("message", "User added to organization and default role assigned successfully"), + ("message", "User added to organisation and default role assigned successfully"), ] ) - logging.info(f"User {user.id} added to organization {org.id} and default role assigned successfully.") + logging.info(f"User {user.id} added to organisation {org.id} and default role assigned successfully.") return JSONResponse(content=response) except IntegrityError as e: @@ -162,14 +162,14 @@ def add_user_to_organization(invite_id: str, session: Session): logging.error(f"IntegrityError: {e}") raise HTTPException( status_code=400, - detail="An error occurred while assigning the role to the user in the organization" + detail="An error occurred while assigning the role to the user in the organisation" ) except Exception as e: session.rollback() - logging.error(f"Error adding user to organization: {e}") + logging.error(f"Error adding user to organisation: {e}") raise HTTPException( status_code=500, - detail="An error occurred while adding the user to the organization" + detail="An error occurred while adding the user to the organisation" ) @staticmethod def delete(session: Session, id: str): diff --git a/api/v1/services/organisation.py b/api/v1/services/organisation.py new file mode 100644 index 000000000..e465544db --- /dev/null +++ b/api/v1/services/organisation.py @@ -0,0 +1,290 @@ +import csv +from io import StringIO +import logging +from typing import Any, Optional +from fastapi import HTTPException +from sqlalchemy.orm import Session +from fastapi import HTTPException, status +from sqlalchemy import select +from api.core.base.services import Service +from api.utils.db_validators import check_model_existence, check_user_in_org +from api.utils.pagination import paginated_response +from api.v1.models.product import Product +from api.v1.models.associations import user_organisation_association +from api.v1.models.organisation import Organisation +from api.v1.models.user import User +from api.v1.schemas.organisation import ( + CreateUpdateOrganisation, + AddUpdateOrganisationRole, + RemoveUserFromOrganisation +) + + +class OrganisationService(Service): + """Organisation service functionality""" + + def create(self, db: Session, schema: CreateUpdateOrganisation, user: User): + """Create a new product""" + + # Create a new organisation + new_organisation = Organisation(**schema.model_dump()) + email = schema.model_dump()["email"] + name = schema.model_dump()["name"] + self.check_by_email(db, email) + self.check_by_name(db, name) + + db.add(new_organisation) + db.commit() + db.refresh(new_organisation) + + # Add user as owner to the new organisation + stmt = user_organisation_association.insert().values( + user_id=user.id, organisation_id=new_organisation.id, role="owner" + ) + db.execute(stmt) + db.commit() + + return new_organisation + + + def fetch_all(self, db: Session, **query_params: Optional[Any]): + """Fetch all products with option tto search using query parameters""" + + query = db.query(Organisation) + + # Enable filter by query parameter + if query_params: + for column, value in query_params.items(): + if hasattr(Organisation, column) and value: + query = query.filter( + getattr(Organisation, column).ilike(f"%{value}%") + ) + + return query.all() + + + def fetch(self, db: Session, id: str): + """Fetches an organisation by id""" + + organisation = check_model_existence(db, Organisation, id) + + return organisation + + def get_organisation_user_role(self, user_id: str, org_id: str, db: Session): + try: + stmt = select(user_organisation_association.c.role).where( + user_organisation_association.c.user_id == user_id, + user_organisation_association.c.organisation_id == org_id, + ) + result = db.execute(stmt).scalar_one_or_none() + return result + except Exception as e: + print(f"An error occurred: {e}") + return None + + def update(self, db: Session, id: str, schema, current_user: User): + """Updates a product""" + + organisation = self.fetch(db=db, id=id) + + # check if the current user has the permission to update the organisation + role = self.get_organisation_user_role(current_user.id, id, db) + if role not in ["admin", "owner"]: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions" + ) + + # Update the fields with the provided schema data + update_data = schema.dict(exclude_unset=True) + for key, value in update_data.items(): + setattr(organisation, key, value) + + db.commit() + db.refresh(organisation) + return organisation + + def delete(self, db: Session, id: str): + '''Deletes a product''' + + organisation = self.fetch(db, id=id) + db.delete(organisation) + db.commit() + + def check_user_role_in_org(self, db: Session, user: User, org: Organisation, role: str): + '''Check user role in organisation''' + + if role not in ['user', 'guest', 'admin', 'owner']: + raise HTTPException(status_code=400, detail="Invalid role") + + stmt = user_organisation_association.select().where( + user_organisation_association.c.user_id == user.id, + user_organisation_association.c.organisation_id == org.id, + user_organisation_association.c.role == role, + ) + + result = db.execute(stmt).fetchone() + + if result is None: + raise HTTPException(status_code=403, detail=f"Permission denied as user is not of {role} role") + + + # def update_user_role(self, schema: AddUpdateOrganisationRole, db: Session, org_id: str, user_to_update_id: str): + def update_user_role(self, schema: AddUpdateOrganisationRole, db: Session): + '''Updates a user role''' + + # Fetch the user and organisation + user = check_model_existence(db, User, schema.user_id) + organisation = check_model_existence(db, Organisation, schema.org_id) + + # Check if user is not in organisation + check_user_in_org(user, organisation) + + # Update user role + stmt = user_organisation_association.update().where( + user_organisation_association.c.user_id == schema.user_id, + user_organisation_association.c.organisation_id == schema.org_id, + ).values(role=schema.role) + + db.execute(stmt) + db.commit() + + + # def add_user_to_organisation(self, db: Session, org_id: str, user_id: str): + def add_user_to_organisation(self, schema: AddUpdateOrganisationRole, db: Session): + '''Adds a user to an organisation''' + + # Fetch the user and organisation + user = check_model_existence(db, User, schema.user_id) + organisation = check_model_existence(db, Organisation, schema.org_id) + + # Check if user is not in organisation + check_user_in_org(user, organisation) + + # Check for user role permissions + self.check_user_role_in_org(db=db, user=user, org=organisation, role='admin')\ + or self.check_user_role_in_org(db=db, user=user, org=organisation, role='owner') + + # Update user role + stmt = user_organisation_association.insert().values( + user_id=user.id, + organisation_id=organisation.id, + role=schema.role + ) + + db.execute(stmt) + db.commit() + + + # # def remove_user_from_organisation(self, db: Session, org_id: str, user_id: str): + def remove_user_from_organisation(self, schema: RemoveUserFromOrganisation, db: Session): + '''Deletes a user from an organisation''' + + # Fetch the user and organisation + user = check_model_existence(db, User, schema.user_id) + organisation = check_model_existence(db, Organisation, schema.org_id) + + # Check if user is not in organisation + check_user_in_org(user, organisation) + + # Check for user role permissions + self.check_user_role_in_org(db=db, user=user, org=organisation, role='admin')\ + or self.check_user_role_in_org(db=db, user=user, org=organisation, role='owner') + + # Update user role + stmt = user_organisation_association.delete().where( + user_organisation_association.c.user_id == schema.user_id, + user_organisation_association.c.organisation_id == schema.org_id, + ) + + db.execute(stmt) + db.commit() + + + def get_users_in_organisation(self, db: Session, org_id: str): + '''Fetches all users in an organisation''' + + organisation = check_model_existence(db, Organisation, org_id) + + # Fetch all users associated with the organisation + return organisation.users + + + def paginate_users_in_organisation( + self, + db: Session, + org_id: str, + page: int, + per_page: int + ): + '''Fetches all users in an organisation''' + + check_model_existence(db, Organisation, org_id) + + return paginated_response( + db=db, + model=User, + skip=page, + join=user_organisation_association, + filters={'organisation_id': org_id}, + limit=per_page + ) + + + + def get_user_organisations(self, db: Session, user_id: str): + '''Fetches all organisations that belong to a user''' + + user = check_model_existence(db, User, user_id) + + # Fetch all users associated with the organisation + return user.organisations + + def check_by_email(self, db: Session, email): + """Fetches a user by their email""" + + org = db.query(Organisation).filter(Organisation.email == email).first() + + if org: + raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="an organisation with this email already exist") + + return False + + def check_by_name(self, db: Session, name): + """Fetches a user by their email""" + + org = db.query(Organisation).filter(Organisation.name == name).first() + + if org: + raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="an organisation with this name already exist") + + return False + + def check_organisation_exist(self, db: Session, org_id): + organisation = db.query(Organisation).filter(Organisation.id == org_id).first() + if organisation is None: + raise HTTPException(status_code=404, detail="Organisation not found") + else: + return True + + def export_organisation_members(self, db: Session, org_id: str): + '''Exports the organisation members''' + + org = self.fetch(db=db, id=org_id) + + csv_file = StringIO() + csv_writer = csv.writer(csv_file) + + # Write headers + csv_writer.writerow(["ID", "First name", 'Last name', "Email", 'Date registered']) + + # Write member data + for user in org.users: + csv_writer.writerow([user.id, user.first_name, user.last_name, user.email, user.created_at]) + + # Move to the beginning of the file + csv_file.seek(0) + + return csv_file + + +organisation_service = OrganisationService() diff --git a/api/v1/services/organization.py b/api/v1/services/organization.py deleted file mode 100644 index b214f391f..000000000 --- a/api/v1/services/organization.py +++ /dev/null @@ -1,290 +0,0 @@ -import csv -from io import StringIO -import logging -from typing import Any, Optional -from fastapi import HTTPException -from sqlalchemy.orm import Session -from fastapi import HTTPException, status -from sqlalchemy import select -from api.core.base.services import Service -from api.utils.db_validators import check_model_existence, check_user_in_org -from api.utils.pagination import paginated_response -from api.v1.models.product import Product -from api.v1.models.associations import user_organization_association -from api.v1.models.organization import Organization -from api.v1.models.user import User -from api.v1.schemas.organization import ( - CreateUpdateOrganization, - AddUpdateOrganizationRole, - RemoveUserFromOrganization -) - - -class OrganizationService(Service): - """Organization service functionality""" - - def create(self, db: Session, schema: CreateUpdateOrganization, user: User): - """Create a new product""" - - # Create a new organization - new_organization = Organization(**schema.model_dump()) - email = schema.model_dump()["email"] - name = schema.model_dump()["name"] - self.check_by_email(db, email) - self.check_by_name(db, name) - - db.add(new_organization) - db.commit() - db.refresh(new_organization) - - # Add user as owner to the new organization - stmt = user_organization_association.insert().values( - user_id=user.id, organization_id=new_organization.id, role="owner" - ) - db.execute(stmt) - db.commit() - - return new_organization - - - def fetch_all(self, db: Session, **query_params: Optional[Any]): - """Fetch all products with option tto search using query parameters""" - - query = db.query(Organization) - - # Enable filter by query parameter - if query_params: - for column, value in query_params.items(): - if hasattr(Organization, column) and value: - query = query.filter( - getattr(Organization, column).ilike(f"%{value}%") - ) - - return query.all() - - - def fetch(self, db: Session, id: str): - """Fetches an organization by id""" - - organization = check_model_existence(db, Organization, id) - - return organization - - def get_organization_user_role(self, user_id: str, org_id: str, db: Session): - try: - stmt = select(user_organization_association.c.role).where( - user_organization_association.c.user_id == user_id, - user_organization_association.c.organization_id == org_id, - ) - result = db.execute(stmt).scalar_one_or_none() - return result - except Exception as e: - print(f"An error occurred: {e}") - return None - - def update(self, db: Session, id: str, schema, current_user: User): - """Updates a product""" - - organization = self.fetch(db=db, id=id) - - # check if the current user has the permission to update the organization - role = self.get_organization_user_role(current_user.id, id, db) - if role not in ["admin", "owner"]: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions" - ) - - # Update the fields with the provided schema data - update_data = schema.dict(exclude_unset=True) - for key, value in update_data.items(): - setattr(organization, key, value) - - db.commit() - db.refresh(organization) - return organization - - def delete(self, db: Session, id: str): - '''Deletes a product''' - - organization = self.fetch(db, id=id) - db.delete(organization) - db.commit() - - def check_user_role_in_org(self, db: Session, user: User, org: Organization, role: str): - '''Check user role in organization''' - - if role not in ['user', 'guest', 'admin', 'owner']: - raise HTTPException(status_code=400, detail="Invalid role") - - stmt = user_organization_association.select().where( - user_organization_association.c.user_id == user.id, - user_organization_association.c.organization_id == org.id, - user_organization_association.c.role == role, - ) - - result = db.execute(stmt).fetchone() - - if result is None: - raise HTTPException(status_code=403, detail=f"Permission denied as user is not of {role} role") - - - # def update_user_role(self, schema: AddUpdateOrganizationRole, db: Session, org_id: str, user_to_update_id: str): - def update_user_role(self, schema: AddUpdateOrganizationRole, db: Session): - '''Updates a user role''' - - # Fetch the user and organization - user = check_model_existence(db, User, schema.user_id) - organization = check_model_existence(db, Organization, schema.org_id) - - # Check if user is not in organization - check_user_in_org(user, organization) - - # Update user role - stmt = user_organization_association.update().where( - user_organization_association.c.user_id == schema.user_id, - user_organization_association.c.organization_id == schema.org_id, - ).values(role=schema.role) - - db.execute(stmt) - db.commit() - - - # def add_user_to_organization(self, db: Session, org_id: str, user_id: str): - def add_user_to_organization(self, schema: AddUpdateOrganizationRole, db: Session): - '''Adds a user to an organization''' - - # Fetch the user and organization - user = check_model_existence(db, User, schema.user_id) - organization = check_model_existence(db, Organization, schema.org_id) - - # Check if user is not in organization - check_user_in_org(user, organization) - - # Check for user role permissions - self.check_user_role_in_org(db=db, user=user, org=organization, role='admin')\ - or self.check_user_role_in_org(db=db, user=user, org=organization, role='owner') - - # Update user role - stmt = user_organization_association.insert().values( - user_id=user.id, - organization_id=organization.id, - role=schema.role - ) - - db.execute(stmt) - db.commit() - - - # # def remove_user_from_organization(self, db: Session, org_id: str, user_id: str): - def remove_user_from_organization(self, schema: RemoveUserFromOrganization, db: Session): - '''Deletes a user from an organization''' - - # Fetch the user and organization - user = check_model_existence(db, User, schema.user_id) - organization = check_model_existence(db, Organization, schema.org_id) - - # Check if user is not in organization - check_user_in_org(user, organization) - - # Check for user role permissions - self.check_user_role_in_org(db=db, user=user, org=organization, role='admin')\ - or self.check_user_role_in_org(db=db, user=user, org=organization, role='owner') - - # Update user role - stmt = user_organization_association.delete().where( - user_organization_association.c.user_id == schema.user_id, - user_organization_association.c.organization_id == schema.org_id, - ) - - db.execute(stmt) - db.commit() - - - def get_users_in_organization(self, db: Session, org_id: str): - '''Fetches all users in an organization''' - - organization = check_model_existence(db, Organization, org_id) - - # Fetch all users associated with the organization - return organization.users - - - def paginate_users_in_organization( - self, - db: Session, - org_id: str, - page: int, - per_page: int - ): - '''Fetches all users in an organization''' - - check_model_existence(db, Organization, org_id) - - return paginated_response( - db=db, - model=User, - skip=page, - join=user_organization_association, - filters={'organization_id': org_id}, - limit=per_page - ) - - - - def get_user_organizations(self, db: Session, user_id: str): - '''Fetches all organizations that belong to a user''' - - user = check_model_existence(db, User, user_id) - - # Fetch all users associated with the organization - return user.organizations - - def check_by_email(self, db: Session, email): - """Fetches a user by their email""" - - org = db.query(Organization).filter(Organization.email == email).first() - - if org: - raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="an organization with this email already exist") - - return False - - def check_by_name(self, db: Session, name): - """Fetches a user by their email""" - - org = db.query(Organization).filter(Organization.name == name).first() - - if org: - raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="an organization with this name already exist") - - return False - - def check_organization_exist(self, db: Session, org_id): - organization = db.query(Organization).filter(Organization.id == org_id).first() - if organization is None: - raise HTTPException(status_code=404, detail="Organization not found") - else: - return True - - def export_organization_members(self, db: Session, org_id: str): - '''Exports the organization members''' - - org = self.fetch(db=db, id=org_id) - - csv_file = StringIO() - csv_writer = csv.writer(csv_file) - - # Write headers - csv_writer.writerow(["ID", "First name", 'Last name', "Email", 'Date registered']) - - # Write member data - for user in org.users: - csv_writer.writerow([user.id, user.first_name, user.last_name, user.email, user.created_at]) - - # Move to the beginning of the file - csv_file.seek(0) - - return csv_file - - -organization_service = OrganizationService() diff --git a/api/v1/services/permissions/role_service.py b/api/v1/services/permissions/role_service.py index f2f2cebff..1c78b3f36 100644 --- a/api/v1/services/permissions/role_service.py +++ b/api/v1/services/permissions/role_service.py @@ -1,6 +1,6 @@ from sqlalchemy.orm import Session from api.v1.models.permissions.role import Role -from api.v1.models.permissions.user_org_role import user_organization_roles +from api.v1.models.permissions.user_org_role import user_organisation_roles from api.v1.models.permissions.role_permissions import role_permissions from api.v1.schemas.permissions.roles import RoleDeleteResponse from api.v1.models.permissions.permissions import Permission @@ -11,7 +11,7 @@ from sqlalchemy.exc import IntegrityError from sqlalchemy import update, insert from api.utils.db_validators import check_model_existence -from api.v1.services.organization import organization_service as org_service +from api.v1.services.organisation import organisation_service as org_service class RoleService: @@ -43,23 +43,23 @@ def create_role(db: Session, role: RoleCreate) -> Role: @staticmethod def assign_role_to_user(db: Session, org_id: uuid7, user_id: uuid7, role_id: uuid7): user_org = db.execute( - user_organization_roles.select().where( - user_organization_roles.c.user_id == user_id, - user_organization_roles.c.organization_id == org_id, + user_organisation_roles.select().where( + user_organisation_roles.c.user_id == user_id, + user_organisation_roles.c.organisation_id == org_id, ) ).fetchone() if not user_org: - raise HTTPException(status_code=404, detail="User is not part of the organization") + raise HTTPException(status_code=404, detail="User is not part of the organisation") if user_org.role_id is not None: - raise HTTPException(status_code=400, detail="User already has a role in the organization") + raise HTTPException(status_code=400, detail="User already has a role in the organisation") try: - # Update the role_id for the user-organization pair - stmt = update(user_organization_roles).where( - user_organization_roles.c.user_id == user_id, - user_organization_roles.c.organization_id == org_id, + # Update the role_id for the user-organisation pair + stmt = update(user_organisation_roles).where( + user_organisation_roles.c.user_id == user_id, + user_organisation_roles.c.organisation_id == org_id, ).values(role_id=role_id) db.execute(stmt) @@ -83,16 +83,16 @@ def delete_role(db: Session, role_id: str) -> RoleDeleteResponse: @staticmethod - def get_roles_by_organization(db: Session, organization_id: str): + def get_roles_by_organisation(db: Session, organisation_id: str): roles = ( db.query(Role) - .join(user_organization_roles, Role.id == user_organization_roles.c.role_id) - .filter(user_organization_roles.c.organization_id == organization_id) + .join(user_organisation_roles, Role.id == user_organisation_roles.c.role_id) + .filter(user_organisation_roles.c.organisation_id == organisation_id) .all() ) if not roles: raise HTTPException( - status_code=404, detail="Roles not found for the given organization" + status_code=404, detail="Roles not found for the given organisation" ) return roles @@ -109,10 +109,10 @@ def get_user_role_relation(self, db: Session, user_id: str, org_id: str, role: R if role.name not in ['user', 'guest', 'admin', 'owner']: raise HTTPException(status_code=400, detail="Invalid role") - stmt = user_organization_roles.select().where( - user_organization_roles.c.user_id == user_id, - user_organization_roles.c.organization_id == org_id, - user_organization_roles.c.role_id == role.id, + stmt = user_organisation_roles.select().where( + user_organisation_roles.c.user_id == user_id, + user_organisation_roles.c.organisation_id == org_id, + user_organisation_roles.c.role_id == role.id, ) relation = db.execute(stmt).fetchone() @@ -125,10 +125,10 @@ def get_user_role_relation(self, db: Session, user_id: str, org_id: str, role: R def remove_user_from_role(self, db: Session, org_id: str, user_id: str, role: Role): """Delete user role relationship""" if self.get_user_role_relation(db, user_id, org_id, role): - db.execute(user_organization_roles.delete().where( - user_organization_roles.c.user_id == user_id, - user_organization_roles.c.organization_id == org_id, - user_organization_roles.c.role_id == role.id, + db.execute(user_organisation_roles.delete().where( + user_organisation_roles.c.user_id == user_id, + user_organisation_roles.c.organisation_id == org_id, + user_organisation_roles.c.role_id == role.id, )) db.commit() diff --git a/api/v1/services/product.py b/api/v1/services/product.py index 346fec042..a52ea6a4f 100644 --- a/api/v1/services/product.py +++ b/api/v1/services/product.py @@ -14,7 +14,7 @@ ProductCategory, ) from api.v1.models.user import User -from api.v1.models import Organization +from api.v1.models import Organisation from api.v1.schemas.product import ProductCategoryCreate, ProductCreate from api.utils.db_validators import check_user_in_org from api.v1.schemas.product import ProductFilterResponse @@ -34,9 +34,9 @@ class ProductService(Service): # detail="product doesn't belong to the specified organisation", # ) - # organization = check_model_existence(db, Organization, org_id) + # organisation = check_model_existence(db, Organisation, org_id) - # check_user_in_org(user=current_user, organization=organization) + # check_user_in_org(user=current_user, organisation=organisation) def create( self, db: Session, schema: ProductCreate, org_id: str, current_user: User @@ -45,9 +45,9 @@ def create( # check if user belongs to org - organization = check_model_existence(db, Organization, org_id) + organisation = check_model_existence(db, Organisation, org_id) - check_user_in_org(user=current_user, organization=organization) + check_user_in_org(user=current_user, organisation=organisation) # check if user inputted a valid category @@ -81,16 +81,16 @@ def create( return new_product - def fetch_single_by_organization( + def fetch_single_by_organisation( self, db: Session, org_id: str, product_id: str, current_user: User ) -> Product: """Fetches a product by id""" # check if user belongs to org - organization = check_model_existence(db, Organization, org_id) + organisation = check_model_existence(db, Organisation, org_id) - check_user_in_org(user=current_user, organization=organization) + check_user_in_org(user=current_user, organisation=organisation) product = check_model_existence(db, Product, product_id) return product @@ -100,7 +100,7 @@ def update( ): """Updates a product""" - product = self.fetch_single_by_organization( + product = self.fetch_single_by_organisation( db, org_id, product_id, current_user ) @@ -116,7 +116,7 @@ def update( def delete(self, db: Session, org_id: str, product_id: str, current_user: User): """Deletes a product""" - product: Product = self.fetch_single_by_organization( + product: Product = self.fetch_single_by_organisation( db, org_id, product_id, current_user ) @@ -144,14 +144,14 @@ def fetch(self, db: Session, id: str) -> Product: product = check_model_existence(db, Product, id) return product - def fetch_by_organization(self, db: Session, user, org_id, limit, page): - """Fetches all products of an organization""" + def fetch_by_organisation(self, db: Session, user, org_id, limit, page): + """Fetches all products of an organisation""" - # check if organization exists - organization = check_model_existence(db, Organization, org_id) + # check if organisation exists + organisation = check_model_existence(db, Organisation, org_id) - # Check if the user exist in the organization - check_user_in_org(user=user, organization=organization) + # Check if the user exist in the organisation + check_user_in_org(user=user, organisation=organisation) # calculating offset value from page and limit given offset_value = (page - 1) * limit @@ -202,7 +202,7 @@ def fetch_stock( self, db: Session, product_id: str, current_user: User, org_id: str ) -> dict: """Fetches the current stock level for a specific product""" - product = self.fetch_single_by_organization( + product = self.fetch_single_by_organisation( db=db, org_id=org_id, product_id=product_id, current_user=current_user ) @@ -220,9 +220,9 @@ class ProductCategoryService(Service): @staticmethod def create(db: Session, schema: ProductCategoryCreate, current_user: User): - # organization = check_model_existence(db, Organization, org_id) + # organisation = check_model_existence(db, Organisation, org_id) - # check_user_in_org(user=current_user, organization=organization) + # check_user_in_org(user=current_user, organisation=organisation) try: new_category = ProductCategory(**schema.model_dump()) diff --git a/api/v1/services/user.py b/api/v1/services/user.py index ecbdb963f..77e9d8433 100644 --- a/api/v1/services/user.py +++ b/api/v1/services/user.py @@ -16,7 +16,7 @@ from api.db.database import get_db from api.utils.settings import settings from api.utils.db_validators import check_model_existence -from api.v1.models.associations import user_organization_association +from api.v1.models.associations import user_organisation_association from api.v1.models.user import User from api.v1.models.data_privacy import DataPrivacySetting from api.v1.models.token_login import TokenLogin @@ -516,7 +516,7 @@ def get_users_by_role(self, db: Session, role_id: str, current_user: User): detail="Role ID is required" ) - user_roles = db.query(user_organization_association).filter(user_organization_association.c.user_id == current_user.id, user_organization_association.c.role.in_(['admin', 'owner'])).all() + user_roles = db.query(user_organisation_association).filter(user_organisation_association.c.user_id == current_user.id, user_organisation_association.c.role.in_(['admin', 'owner'])).all() if len(user_roles) == 0: raise HTTPException( @@ -524,7 +524,7 @@ def get_users_by_role(self, db: Session, role_id: str, current_user: User): detail="Permission denied. Admin access required." ) - users = db.query(User).join(user_organization_association).filter(user_organization_association.c.role == role_id).all() + users = db.query(User).join(user_organisation_association).filter(user_organisation_association.c.role == role_id).all() if len(users) == 0: raise HTTPException( diff --git a/scripts/org_seed.py b/scripts/org_seed.py index a844e5de7..437cf9478 100644 --- a/scripts/org_seed.py +++ b/scripts/org_seed.py @@ -5,15 +5,15 @@ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) from api.v1.models.user import User -from api.v1.models.organization import Organization -from api.v1.models.associations import user_organization_association +from api.v1.models.organisation import Organisation +from api.v1.models.associations import user_organisation_association from api.db.database import get_db # create_database() db = next(get_db()) -# Create some organizations -org1 = Organization( +# Create some organisations +org1 = Organisation( name="Tech Corp", email="contact@techcorp.com", industry="Technology", @@ -23,7 +23,7 @@ address="123 Tech Lane", lga="San Francisco" ) -org2 = Organization( +org2 = Organisation( name="Health Co", email="info@healthco.com", industry="Healthcare", @@ -34,7 +34,7 @@ lga="Manhattan" ) -# Add organizations to the session +# Add organisations to the session db.add_all([org1, org2]) db.commit() @@ -58,11 +58,11 @@ db.add_all([user1, user2]) db.commit() -# Add users to organizations with roles -stmt = user_organization_association.insert().values([ - {'user_id': user1.id, 'organization_id': org1.id, 'role': 'admin', 'status': 'member'}, - {'user_id': user2.id, 'organization_id': org1.id, 'role': 'user', 'status': 'member'}, - {'user_id': user2.id, 'organization_id': org2.id, 'role': 'owner', 'status': 'member'}, +# Add users to organisations with roles +stmt = user_organisation_association.insert().values([ + {'user_id': user1.id, 'organisation_id': org1.id, 'role': 'admin', 'status': 'member'}, + {'user_id': user2.id, 'organisation_id': org1.id, 'role': 'user', 'status': 'member'}, + {'user_id': user2.id, 'organisation_id': org2.id, 'role': 'owner', 'status': 'member'}, ]) db.execute(stmt) diff --git a/scripts/seed.py b/scripts/seed.py index 4224a13bd..5602d323a 100644 --- a/scripts/seed.py +++ b/scripts/seed.py @@ -40,12 +40,12 @@ db.add_all([user_1, user_2, user_3]) -org_1 = Organization( - name="Python Org", type="An organization for python develoers" +org_1 = Organisation( + name="Python Org", type="An organisation for python develoers" ) -org_2 = Organization(name="Django Org", type="An organization of django devs") -org_3 = Organization( - name="FastAPI Devs", type="An organization of Fast API devs" +org_2 = Organisation(name="Django Org", type="An organisation of django devs") +org_3 = Organisation( + name="FastAPI Devs", type="An organisation of Fast API devs" ) @@ -108,5 +108,5 @@ db.add_all([job_1, job_2, application_1, application_2]) db.commit() -users = db.query(Organization).first().users +users = db.query(Organisation).first().users print("Seed data succesfully") \ No newline at end of file diff --git a/scripts/seed2.py b/scripts/seed2.py index dbcbf7f68..89c658bda 100644 --- a/scripts/seed2.py +++ b/scripts/seed2.py @@ -7,10 +7,10 @@ # create_database() db = next(get_db()) -# Add sample organizations -org_1 = Organization(id=str(uuid7()), name="Python Org", description="An organization for Python developers") -org_2 = Organization(id=str(uuid7()), name="JavaScript Org", description="An organization for JavaScript developers") -org_3 = Organization(id=str(uuid7()), name="GoLang Org", description="An organization for GoLang developers") +# Add sample organisations +org_1 = Organisation(id=str(uuid7()), name="Python Org", description="An organisation for Python developers") +org_2 = Organisation(id=str(uuid7()), name="JavaScript Org", description="An organisation for JavaScript developers") +org_3 = Organisation(id=str(uuid7()), name="GoLang Org", description="An organisation for GoLang developers") db.add_all([org_1, org_2, org_3]) db.commit() @@ -21,7 +21,7 @@ price=200, currency='$', features=['email', 'messaging'], - organization_id=org_1.id + organisation_id=org_1.id ) db.add_all([plan1]) db.commit() @@ -35,7 +35,7 @@ first_name="User", last_name="One", is_active=True, - organizations=[org_1, org_2] + organisations=[org_1, org_2] ) user_2 = User( @@ -46,7 +46,7 @@ first_name="User", last_name="Two", is_active=True, - organizations=[org_2, org_3] + organisations=[org_2, org_3] ) user_3 = User( @@ -57,7 +57,7 @@ first_name="User", last_name="Three", is_active=True, - organizations=[org_1, org_3] + organisations=[org_1, org_3] ) db.add_all([user_1, user_2, user_3]) @@ -121,21 +121,21 @@ invitation_1 = Invitation( id=str(uuid7()), user_id=user_1.id, - organization_id=org_1.id, + organisation_id=org_1.id, expires_at=datetime.datetime.now() + datetime.timedelta(days=7) ) invitation_2 = Invitation( id=str(uuid7()), user_id=user_2.id, - organization_id=org_2.id, + organisation_id=org_2.id, expires_at=datetime.datetime.now() + datetime.timedelta(days=7) ) invitation_3 = Invitation( id=str(uuid7()), user_id=user_3.id, - organization_id=org_3.id, + organisation_id=org_3.id, expires_at=datetime.datetime.now() + datetime.timedelta(days=7) ) diff --git a/scripts/seed3.py b/scripts/seed3.py index 536f976f0..42c8ce682 100644 --- a/scripts/seed3.py +++ b/scripts/seed3.py @@ -82,7 +82,7 @@ db.commit() for _ in range(20): - organization = Organization( + organisation = Organisation( name=fake.company(), email=fake.email(), industry=fake.job(), @@ -92,7 +92,7 @@ state=fake.state(), address=fake.address(), ) - db.add(organization) + db.add(organisation) db.commit() for _ in range(5): @@ -102,7 +102,7 @@ db.add(category) db.commit() -organizations = db.query(Organization).all() +organisations = db.query(Organisation).all() categories = db.query(ProductCategory).all() for _ in range(20): @@ -110,7 +110,7 @@ name=fake.numerify(text='Intel Core i%-%%##K vs AMD Ryzen % %%##X'), description=fake.paragraph(), price=fake.pydecimal(left_digits=3, right_digits=2, positive=True), - org_id=fake.random_element([ i.id for i in organizations ]), + org_id=fake.random_element([ i.id for i in organisations ]), category_id=fake.random_element([ i.id for i in categories ]), quantity=fake.random_int(min=0, max=100), image_url=fake.image_url(), diff --git a/tests/v1/analytics/test_analytics_summary.py b/tests/v1/analytics/test_analytics_summary.py index d9237d1dc..6d01fc4cb 100644 --- a/tests/v1/analytics/test_analytics_summary.py +++ b/tests/v1/analytics/test_analytics_summary.py @@ -17,7 +17,7 @@ def mock_analytics_service(mocker): mock = mocker.patch( 'api.v1.services.analytics.AnalyticsServices', autospec=True) mock.get_summary_data_super_admin = mocker.Mock() - mock.get_summary_data_organization = mocker.Mock() + mock.get_summary_data_organisation = mocker.Mock() return mock @@ -33,7 +33,7 @@ def mock_get_current_user_super_admin(mocker): @pytest.fixture def mock_get_current_user_user(mocker): - return mocker.patch('api.v1.services.user.user_service.get_current_user', return_value=MagicMock(is_super_admin=False, id="user_id", organization_id="org_id")) + return mocker.patch('api.v1.services.user.user_service.get_current_user', return_value=MagicMock(is_super_admin=False, id="user_id", organisation_id="org_id")) @pytest.fixture diff --git a/tests/v1/analytics/test_line_chart_data.py b/tests/v1/analytics/test_line_chart_data.py index 6cf97afe9..cf0519506 100644 --- a/tests/v1/analytics/test_line_chart_data.py +++ b/tests/v1/analytics/test_line_chart_data.py @@ -56,15 +56,15 @@ def test_get_analytics_line_chart_super_admin(mock_db, mock_user_service, mock_o def test_get_analytics_line_chart_non_super_admin(mock_db, mock_user_service, mock_oauth2_scheme): """ - Test get analytics_line_chart_data for non super_admin with organization + Test get analytics_line_chart_data for non super_admin with organisation """ # Arrange mock_user = MagicMock() mock_user.is_super_admin = False mock_user_service.return_value = mock_user - mock_user_organization = MagicMock() - mock_db.query.return_value.filter_by.return_value.first.return_value = mock_user_organization + mock_user_organisation = MagicMock() + mock_db.query.return_value.filter_by.return_value.first.return_value = mock_user_organisation analytics_service = AnalyticsServices() @@ -78,7 +78,7 @@ def test_get_analytics_line_chart_non_super_admin(mock_db, mock_user_service, mo def test_get_analytics_line_chart_no_org(mock_db, mock_user_service, mock_oauth2_scheme): """ - Test get analytics_line_chart_data for non super_admin without organization + Test get analytics_line_chart_data for non super_admin without organisation """ # Arrange mock_user = MagicMock() diff --git a/tests/v1/billing_plan/test_billing_plan.py b/tests/v1/billing_plan/test_billing_plan.py index 0fd185658..e41a35ffa 100644 --- a/tests/v1/billing_plan/test_billing_plan.py +++ b/tests/v1/billing_plan/test_billing_plan.py @@ -71,7 +71,7 @@ def test_create_new_plans(mock_user_service, mock_db_session): access_token = user_service.create_access_token(user_id=str(uuid7())) data = { "name": "Advanced", - "organization_id": "s2334d", + "organisation_id": "s2334d", "description": "All you need in one pack", "price": 80, "duration": "Monthly", diff --git a/tests/v1/billing_plan/test_get_billing_plan.py b/tests/v1/billing_plan/test_get_billing_plan.py index 150ec67d7..413499670 100644 --- a/tests/v1/billing_plan/test_get_billing_plan.py +++ b/tests/v1/billing_plan/test_get_billing_plan.py @@ -7,7 +7,7 @@ from uuid_extensions import uuid7 from api.db.database import get_db -from api.v1.models.organization import Organization +from api.v1.models.organisation import Organisation from api.v1.services.user import user_service from api.v1.services.billing_plan import billing_plan_service from api.v1.models.user import User @@ -43,9 +43,9 @@ def mock_get_current_user(): ) def mock_org(): - return Organization( + return Organisation( id=str(uuid7()), - name="Test Organization", + name="Test Organisation", created_at=datetime.now(timezone.utc), updated_at=datetime.now(timezone.utc) ) @@ -53,7 +53,7 @@ def mock_org(): def mock_billing_plan(): return BillingPlan( id=str(uuid7()), - organization_id=mock_org().id, + organisation_id=mock_org().id, name="Premium Plan", price=49.99, currency="NGN", # Currency code @@ -71,6 +71,6 @@ def test_get_plan_unauthorized(client, db_session_mock): with patch("api.v1.services.billing_plan.BillingPlanService.fetch", return_value=mock_plan_instance) as mock_fetch: - response = client.get(f'/api/v1/organizations/billing-plans/{mock_plan_instance.id}') + response = client.get(f'/api/v1/organisations/billing-plans/{mock_plan_instance.id}') - assert response.status_code == 404 + assert response.status_code == 401 diff --git a/tests/v1/billing_plan/test_update_billing_plan.py b/tests/v1/billing_plan/test_update_billing_plan.py index 3374f4254..1ed4438fd 100644 --- a/tests/v1/billing_plan/test_update_billing_plan.py +++ b/tests/v1/billing_plan/test_update_billing_plan.py @@ -58,7 +58,7 @@ def test_update_billing_plan(mock_user_service, mock_db_session): access_token = user_service.create_access_token(user_id=str(uuid7())) data = { "name": "Advanced", - "organization_id": "s2334d", + "organisation_id": "s2334d", "description": "All you need in one pack", "price": 80, "duration": "Monthly", diff --git a/tests/v1/blog/test_add_comment.py b/tests/v1/blog/test_add_comment.py index cb61f356f..f1a79d129 100644 --- a/tests/v1/blog/test_add_comment.py +++ b/tests/v1/blog/test_add_comment.py @@ -51,7 +51,7 @@ def test_add_comment_to_blog( test_blog, access_token_user1, ): - # Mock the GET method for Organization + # Mock the GET method for Organisation def mock_get(model, ident): if model == Blog and ident == test_blog.id: return test_blog @@ -59,10 +59,10 @@ def mock_get(model, ident): mock_db_session.get.side_effect = mock_get - # Mock the query for checking if user is in the organization + # Mock the query for checking if user is in the organisation mock_db_session.query().return_value = test_blog - # Test user belonging to the organization + # Test user belonging to the organisation content = {"content": "Test comment"} headers = {'Authorization': f'Bearer {access_token_user1}'} response = client.post(f"/api/v1/blogs/{test_blog.id}/comments", headers=headers, json=content) diff --git a/tests/v1/comment/test_dislike_comment.py b/tests/v1/comment/test_dislike_comment.py index 380034cdc..dc096b8f7 100644 --- a/tests/v1/comment/test_dislike_comment.py +++ b/tests/v1/comment/test_dislike_comment.py @@ -61,7 +61,7 @@ def test_dislike_comment( test_comment, access_token_user1, ): - # Mock the GET method for Organization + # Mock the GET method for Organisation def mock_get(model, ident): if model == Comment and ident == test_comment.id: return test_comment @@ -75,7 +75,7 @@ def mock_get(model, ident): # Mock the query to return null for existing dislikes mock_db_session.query.return_value.filter_by.return_value.first.return_value = [] - # Test user belonging to the organization + # Test user belonging to the organisation headers = {'Authorization': f'Bearer {access_token_user1}'} response = client.post(f"/api/v1/comments/{test_comment.id}/dislike", headers=headers) @@ -93,7 +93,7 @@ def test_dislike_comment_twice( test_comment, access_token_user1, ): - # Mock the GET method for Organization + # Mock the GET method for Organisation def mock_get(model, ident): if model == Comment and ident == test_comment.id: return test_comment @@ -107,7 +107,7 @@ def mock_get(model, ident): # Mock the query to return null for existing dislikes mock_db_session.query.return_value.filter_by.return_value.first.return_value = [test_dislike_comment] - # Test user belonging to the organization + # Test user belonging to the organisation headers = {'Authorization': f'Bearer {access_token_user1}'} response = client.post(f"/api/v1/comments/{test_comment.id}/dislike", headers=headers) diff --git a/tests/v1/faq/delete_faq_test.py b/tests/v1/faq/delete_faq_test.py index 37c915765..c0051165b 100644 --- a/tests/v1/faq/delete_faq_test.py +++ b/tests/v1/faq/delete_faq_test.py @@ -95,10 +95,10 @@ def test_faq_not_found(client, db_session_mock): app.dependency_overrides[user_service.get_current_super_admin] = mock_get_current_admin app.dependency_overrides[faq_service.fetch] = lambda: mock_faq - # Simulate a non-existent organization + # Simulate a non-existent organisation nonexistent_id = str(uuid7()) - # Mock the organization service to raise an exception for a non-existent FAQ + # Mock the organisation service to raise an exception for a non-existent FAQ with patch("api.v1.services.faq.faq_service.fetch", side_effect=HTTPException(status_code=404, detail="FAQ not found")): response = client.delete( f'/api/v1/faqs/{nonexistent_id}', diff --git a/tests/v1/faq/get_single_faq_test.py b/tests/v1/faq/get_single_faq_test.py index 7aff0c23d..94997a6ff 100644 --- a/tests/v1/faq/get_single_faq_test.py +++ b/tests/v1/faq/get_single_faq_test.py @@ -82,10 +82,10 @@ def test_faq_not_found(client, db_session_mock): app.dependency_overrides[user_service.get_current_super_admin] = mock_get_current_admin app.dependency_overrides[faq_service.fetch] = lambda: mock_faq - # Simulate a non-existent organization + # Simulate a non-existent organisation nonexistent_id = str(uuid7()) - # Mock the organization service to raise an exception for a non-existent FAQ + # Mock the organisation service to raise an exception for a non-existent FAQ with patch("api.v1.services.faq.faq_service.fetch", side_effect=HTTPException(status_code=404, detail="FAQ not found")): response = client.get( f'/api/v1/faqs/{nonexistent_id}', diff --git a/tests/v1/faq/update_faq_test.py b/tests/v1/faq/update_faq_test.py index 0a0f4832d..e93c0f4ec 100644 --- a/tests/v1/faq/update_faq_test.py +++ b/tests/v1/faq/update_faq_test.py @@ -132,10 +132,10 @@ def test_faq_not_found(client, db_session_mock): app.dependency_overrides[user_service.get_current_super_admin] = mock_get_current_admin app.dependency_overrides[faq_service.fetch] = lambda: mock_faq - # Simulate a non-existent organization + # Simulate a non-existent organisation nonexistent_id = str(uuid7()) - # Mock the organization service to raise an exception for a non-existent FAQ + # Mock the organisation service to raise an exception for a non-existent FAQ with patch("api.v1.services.faq.faq_service.fetch", side_effect=HTTPException(status_code=404, detail="FAQ not found")): response = client.patch( f'/api/v1/faqs/{nonexistent_id}', diff --git a/tests/v1/invitation/test_accept_invitation.py b/tests/v1/invitation/test_accept_invitation.py index 894c59857..90afadcbb 100644 --- a/tests/v1/invitation/test_accept_invitation.py +++ b/tests/v1/invitation/test_accept_invitation.py @@ -10,16 +10,16 @@ from urllib.parse import urlencode from api.v1.models.permissions.role import Role from api.v1.services.user import user_service -from api.v1.models.permissions.user_org_role import user_organization_roles +from api.v1.models.permissions.user_org_role import user_organisation_roles warnings.filterwarnings("ignore", category=DeprecationWarning) sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))) from main import app from api.v1.models.user import User -from api.v1.models.associations import user_organization_association +from api.v1.models.associations import user_organisation_association from api.v1.models.invitation import Invitation -from api.v1.models.organization import Organization +from api.v1.models.organisation import Organisation from api.v1.services.invite import InviteService from api.db.database import get_db @@ -70,21 +70,21 @@ def create_mock_user(mock_db_session, user_id): return mock_user def create_mock_role_assignment(mock_db_session, user_id, org_id, role_id): - mock_db_session.query(user_organization_roles).filter_by( + mock_db_session.query(user_organisation_roles).filter_by( user_id=user_id, - organization_id=org_id, + organisation_id=org_id, role_id=role_id ).first.return_value = MagicMock() -def create_mock_organization(mock_db_session, org_id): - mock_org = Organization( +def create_mock_organisation(mock_db_session, org_id): + mock_org = Organisation( id=org_id, - name="Test Organization", + name="Test Organisation", created_at=datetime.now(timezone.utc), updated_at=datetime.now(timezone.utc) ) - mock_db_session.query(Organization).filter_by(id=org_id).first.return_value = mock_org + mock_db_session.query(Organisation).filter_by(id=org_id).first.return_value = mock_org return mock_org @@ -92,7 +92,7 @@ def create_mock_invitation(mock_db_session, user_id, org_id, invitation_id, expi mock_invitation = Invitation( id=invitation_id, user_id=user_id, - organization_id=org_id, + organisation_id=org_id, expires_at=expiration, is_valid=is_valid ) @@ -105,14 +105,14 @@ def test_create_invitation_valid_userid(mock_db_session, mock_invite_service): org_id = str(uuid7()) create_mock_user(mock_db_session, user_email) - create_mock_organization(mock_db_session, org_id) + create_mock_organisation(mock_db_session, org_id) access_token = user_service.create_access_token(str(user_email)) mock_db_session.execute.return_value.fetchall.return_value = [] paylod = { "user_email" : user_email, - "organization_id" : org_id + "organisation_id" : org_id } response = client.post(INVITE_CREATE_ENDPOINT, json=paylod, headers={'Authorization': f'Bearer {access_token}'}) @@ -126,14 +126,14 @@ def test_create_invitation_invalid_id(mock_db_session, mock_invite_service): org_id = str(uuid7()) create_mock_user(mock_db_session, user_id) - create_mock_organization(mock_db_session, org_id) + create_mock_organisation(mock_db_session, org_id) access_token = user_service.create_access_token(str(user_id)) mock_db_session.execute.return_value.fetchall.return_value = [] paylod = { "user_id" : user_id, - "organization_id" : org_id + "organisation_id" : org_id } response = client.post(INVITE_CREATE_ENDPOINT, json=paylod, headers={'Authorization': f'Bearer {access_token}'}) @@ -149,10 +149,10 @@ def test_accept_invitation_expired_link(mock_db_session, mock_invite_service): expiration = datetime.now(timezone.utc) - timedelta(days=1) access_token = user_service.create_access_token(str(user_id)) create_mock_user(mock_db_session, user_id) - create_mock_organization(mock_db_session, org_id) + create_mock_organisation(mock_db_session, org_id) create_mock_invitation(mock_db_session, user_id, org_id, invitation_id, expiration, is_valid=True) - mock_invite_service.add_user_to_organization.side_effect = HTTPException(status_code=400, detail="Expired invitation link") + mock_invite_service.add_user_to_organisation.side_effect = HTTPException(status_code=400, detail="Expired invitation link") response = client.post(INVITE_ACCEPT_ENDPOINT, json={ "invitation_link": f"http://testserver/api/v1/invite/accept?invitation_id={invitation_id}" @@ -170,7 +170,7 @@ def test_accept_invitation_malformed_link(mock_db_session): expiration = datetime.now(timezone.utc) - timedelta(days=1) access_token = user_service.create_access_token(str(user_id)) create_mock_user(mock_db_session, user_id) - create_mock_organization(mock_db_session, org_id) + create_mock_organisation(mock_db_session, org_id) create_mock_invitation(mock_db_session, user_id, org_id, invitation_id, expiration, is_valid=True) response = client.post(INVITE_ACCEPT_ENDPOINT, json={ "invitation_link": "http://testserver/api/v1/invite/accept?wrong_param=123" @@ -181,20 +181,20 @@ def test_accept_invitation_malformed_link(mock_db_session): @pytest.mark.usefixtures("mock_db_session", "mock_invite_service") def test_accept_invitation_user_already_assigned(mock_db_session, mock_invite_service): - """Test for accepting an invitation where the user is already assigned to the role in the organization.""" + """Test for accepting an invitation where the user is already assigned to the role in the organisation.""" user_id = str(uuid7()) org_id = str(uuid7()) invitation_id = str(uuid7()) expiration = datetime.now(timezone.utc) + timedelta(days=1) create_mock_user(mock_db_session, user_id) - create_mock_organization(mock_db_session, org_id) + create_mock_organisation(mock_db_session, org_id) create_mock_invitation(mock_db_session, user_id, org_id, invitation_id, expiration, is_valid=True) - # Simulate the scenario where the user is already assigned to the organization - mock_invite_service.add_user_to_organization.side_effect = HTTPException( + # Simulate the scenario where the user is already assigned to the organisation + mock_invite_service.add_user_to_organisation.side_effect = HTTPException( status_code=400, - detail="User is already assigned to the role in this organization" + detail="User is already assigned to the role in this organisation" ) access_token = user_service.create_access_token(str(user_id)) @@ -205,4 +205,4 @@ def test_accept_invitation_user_already_assigned(mock_db_session, mock_invite_se }, headers={'Authorization': f'Bearer {access_token}'}) assert response.status_code == 400 - assert response.json()['message'] == 'User is already assigned to the role in this organization' + assert response.json()['message'] == 'User is already assigned to the role in this organisation' diff --git a/tests/v1/organization/create_organization_test.py b/tests/v1/organization/create_organization_test.py index 32aab995d..9ec35c5c4 100644 --- a/tests/v1/organization/create_organization_test.py +++ b/tests/v1/organization/create_organization_test.py @@ -9,8 +9,8 @@ from api.db.database import get_db from api.v1.services.user import user_service from api.v1.models import User -from api.v1.models.organization import Organization -from api.v1.services.organization import organization_service +from api.v1.models.organisation import Organisation +from api.v1.services.organisation import organisation_service from main import app @@ -29,9 +29,9 @@ def mock_get_current_user(): def mock_org(): - return Organization( + return Organisation( id=str(uuid7()), - name="Test Organization", + name="Test Organisation", created_at=datetime.now(timezone.utc), updated_at=datetime.now(timezone.utc), ) @@ -51,25 +51,25 @@ def client(db_session_mock): app.dependency_overrides = {} -def test_create_organization_success(client, db_session_mock): - """Test to successfully create a new organization""" +def test_create_organisation_success(client, db_session_mock): + """Test to successfully create a new organisation""" # Mock the user service to return the current user app.dependency_overrides[user_service.get_current_user] = ( lambda: mock_get_current_user ) - app.dependency_overrides[organization_service.create] = lambda: mock_org + app.dependency_overrides[organisation_service.create] = lambda: mock_org - # Mock organization creation + # Mock organisation creation db_session_mock.add.return_value = None db_session_mock.commit.return_value = None db_session_mock.refresh.return_value = None - mock_organization = mock_org() + mock_organisation = mock_org() with patch( - "api.v1.services.organization.organization_service.create", - return_value=mock_organization, + "api.v1.services.organisation.organisation_service.create", + return_value=mock_organisation, ) as mock_create: response = client.post( "/api/v1/organisations", @@ -89,21 +89,21 @@ def test_create_organization_success(client, db_session_mock): assert response.status_code == 201 -def test_create_organization_missing_field(client, db_session_mock): - """Test for missing field when creating a new organization""" +def test_create_organisation_missing_field(client, db_session_mock): + """Test for missing field when creating a new organisation""" # Mock the user service to return the current user app.dependency_overrides[user_service.get_current_user] = ( lambda: mock_get_current_user ) - app.dependency_overrides[organization_service.create] = lambda: mock_org - # Mock organization creation + app.dependency_overrides[organisation_service.create] = lambda: mock_org + # Mock organisation creation db_session_mock.add.return_value = None db_session_mock.commit.return_value = None db_session_mock.refresh.return_value = None - mock_organization = mock_org() + mock_organisation = mock_org() with patch( - "api.v1.services.organization.organization_service.create", - return_value=mock_organization, + "api.v1.services.organisation.organisation_service.create", + return_value=mock_organisation, ) as mock_create: response = client.post( "/api/v1/organisations", @@ -118,7 +118,7 @@ def test_create_organization_missing_field(client, db_session_mock): assert response.status_code == 422 -def test_create_organization_unauthorized(client, db_session_mock): +def test_create_organisation_unauthorized(client, db_session_mock): """Test for unauthorized user""" response = client.post( diff --git a/tests/v1/organization/org_products_test.py b/tests/v1/organization/org_products_test.py index d46135786..80c15c760 100644 --- a/tests/v1/organization/org_products_test.py +++ b/tests/v1/organization/org_products_test.py @@ -4,7 +4,7 @@ from api.v1.services.user import user_service from sqlalchemy.orm import Session from api.db.database import get_db -from api.v1.models import User, Product, Organization +from api.v1.models import User, Product, Organisation from api.v1.services.user import user_service from uuid_extensions import uuid7 from unittest.mock import MagicMock @@ -47,23 +47,23 @@ def another_user(): @pytest.fixture -def test_organization(test_user): - organization = Organization( +def test_organisation(test_user): + organisation = Organisation( id=str(uuid7()), name="testorg", ) - organization.users.append(test_user) - return organization + organisation.users.append(test_user) + return organisation @pytest.fixture() -def test_product(test_organization): +def test_product(test_organisation): return Product( id=str(uuid7()), name="testproduct", description="Test product", price=9.99, - org_id=test_organization.id, + org_id=test_organisation.id, ) @@ -77,23 +77,23 @@ def access_token_user2(another_user): return user_service.create_access_token(user_id=another_user.id) -# Test for User in Organization -def test_get_products_for_organization_user_belongs( +# Test for User in Organisation +def test_get_products_for_organisation_user_belongs( mock_db_session, test_user, - test_organization, + test_organisation, test_product, access_token_user1, ): - # Mock the GET method for Organization + # Mock the GET method for Organisation def mock_get(model, ident): - if model == Organization and ident == test_organization.id: - return test_organization + if model == Organisation and ident == test_organisation.id: + return test_organisation return None mock_db_session.get.side_effect = mock_get - # Mock the query for checking if user is in the organization + # Mock the query for checking if user is in the organisation mock_db_session.query.return_value.filter.return_value.first.return_value = ( test_user ) @@ -103,10 +103,10 @@ def mock_get(model, ident): test_product ] - # Test user belonging to the organization + # Test user belonging to the organisation headers = {"Authorization": f"Bearer {access_token_user1}"} response = client.get( - f"/api/v1/organisations/{test_organization.id}/products", headers=headers + f"/api/v1/organisations/{test_organisation.id}/products", headers=headers ) # Debugging statement @@ -119,34 +119,34 @@ def mock_get(model, ident): # products = response.json().get('data', []) -### Test for user not in Organization -def test_get_products_for_organization_user_not_belong( +### Test for user not in Organisation +def test_get_products_for_organisation_user_not_belong( mock_db_session, another_user, - test_organization, + test_organisation, test_product, access_token_user2, ): - # Ensure the organization does not contain another_user - test_organization.users = [] + # Ensure the organisation does not contain another_user + test_organisation.users = [] - # Mock the `get` method for `Organization` + # Mock the `get` method for `Organisation` def mock_get(model, ident): - if model == Organization and ident == test_organization.id: - return test_organization + if model == Organisation and ident == test_organisation.id: + return test_organisation return None mock_db_session.get.side_effect = mock_get - # Mock the query for checking if user is in the organization + # Mock the query for checking if user is in the organisation mock_db_session.query.return_value.filter.return_value.first.return_value = ( another_user ) - # Test user not belonging to the organization + # Test user not belonging to the organisation headers = {"Authorization": f"Bearer {access_token_user2}"} response = client.get( - f"/api/v1/organisations/{test_organization.id}/products", headers=headers + f"/api/v1/organisations/{test_organisation.id}/products", headers=headers ) assert ( @@ -154,19 +154,19 @@ def mock_get(model, ident): ), f"Expected status code 400, got {response.status_code}" -### Test for non-existent Organization -def test_get_products_for_non_existent_organization( +### Test for non-existent Organisation +def test_get_products_for_non_existent_organisation( mock_db_session, test_user, access_token_user1, ): - # Mock the `get` method for `Organization` to return None for non-existent ID + # Mock the `get` method for `Organisation` to return None for non-existent ID def mock_get(model, ident): return None mock_db_session.get.side_effect = mock_get - # Test non-existent organization + # Test non-existent organisation non_existent_id = "non-existent-id" # Use a string since the IDs are UUIDs headers = {"Authorization": f"Bearer {access_token_user1}"} response = client.get( diff --git a/tests/v1/organization/organization_update_test.py b/tests/v1/organization/organization_update_test.py index fc6db86c5..97e53966c 100644 --- a/tests/v1/organization/organization_update_test.py +++ b/tests/v1/organization/organization_update_test.py @@ -7,8 +7,8 @@ from api.db.database import get_db from api.v1.services.user import user_service from api.v1.models import User -from api.v1.models.organization import Organization -from api.v1.services.organization import organization_service +from api.v1.models.organisation import Organisation +from api.v1.services.organisation import organisation_service from main import app @@ -27,9 +27,9 @@ def mock_get_current_user(): def mock_org(): - return Organization( + return Organisation( id=str(uuid7()), - name="Test Organization", + name="Test Organisation", created_at=datetime.now(timezone.utc), updated_at=datetime.now(timezone.utc), ) @@ -49,16 +49,16 @@ def client(db_session_mock): app.dependency_overrides = {} -def test_update_organization_success(client, db_session_mock): - """Test to successfully update an existing organization""" +def test_update_organisation_success(client, db_session_mock): + """Test to successfully update an existing organisation""" org_id = "existing-org-id" current_user = mock_get_current_user() # Get the actual user object app.dependency_overrides[user_service.get_current_user] = lambda: current_user - # Mock the organization fetch and user role retrieval - organization_service.fetch = MagicMock(return_value=mock_org()) - organization_service.get_organization_user_role = MagicMock(return_value="admin") + # Mock the organisation fetch and user role retrieval + organisation_service.fetch = MagicMock(return_value=mock_org()) + organisation_service.get_organisation_user_role = MagicMock(return_value="admin") db_session_mock.commit.return_value = None db_session_mock.refresh.return_value = None @@ -67,7 +67,7 @@ def test_update_organization_success(client, db_session_mock): f"/api/v1/organisations/{org_id}", headers={"Authorization": "Bearer token"}, json={ - "name": "Updated Organization", + "name": "Updated Organisation", "email": "updated@gmail.com", "industry": "Tech", "type": "Tech", @@ -79,12 +79,12 @@ def test_update_organization_success(client, db_session_mock): ) assert response.status_code == 200 - assert response.json()["message"] == "Organization updated successfully" - assert response.json()["data"]["name"] == "Updated Organization" + assert response.json()["message"] == "Organisation updated successfully" + assert response.json()["data"]["name"] == "Updated Organisation" -def test_update_organization_missing_field(client, db_session_mock): - """Test to fail updating an organization due to missing fields""" +def test_update_organisation_missing_field(client, db_session_mock): + """Test to fail updating an organisation due to missing fields""" org_id = "existing-org-id" app.dependency_overrides[user_service.get_current_user] = ( @@ -107,14 +107,14 @@ def test_update_organization_missing_field(client, db_session_mock): assert response.status_code == 422 -def test_update_organization_unauthorized(client, db_session_mock): - """Test to fail updating an organization due to unauthorized access""" +def test_update_organisation_unauthorized(client, db_session_mock): + """Test to fail updating an organisation due to unauthorized access""" org_id = "existing-org-id" response = client.patch( f"/api/v1/organisations/{org_id}", json={ - "name": "Updated Organization", + "name": "Updated Organisation", "email": "updated@gmail.com", "industry": "Tech", "type": "Tech", diff --git a/tests/v1/organization/test_export_user_data.py b/tests/v1/organization/test_export_user_data.py index 8651b2fd3..970af9ea9 100644 --- a/tests/v1/organization/test_export_user_data.py +++ b/tests/v1/organization/test_export_user_data.py @@ -9,10 +9,10 @@ from uuid_extensions import uuid7 from api.db.database import get_db -from api.v1.models.organization import Organization +from api.v1.models.organisation import Organisation from api.v1.services.user import user_service from api.v1.models import User -from api.v1.services.organization import organization_service +from api.v1.services.organisation import organisation_service from main import app @@ -44,8 +44,8 @@ def mock_get_current_admin(): ) -def mock_organization(): - return Organization( +def mock_organisation(): + return Organisation( id=str(uuid7()), name="Health Co", email="info@healthco.com", @@ -85,23 +85,23 @@ def client(db_session_mock): def test_export_success(client, db_session_mock): - """Test to successfully export user data in an organization""" + """Test to successfully export user data in an organisation""" # Mock the user service to return the current user app.dependency_overrides[user_service.get_current_super_admin] = ( lambda: mock_get_current_admin ) - app.dependency_overrides[organization_service.export_organization_members] = ( + app.dependency_overrides[organisation_service.export_organisation_members] = ( lambda: mock_csv_content ) - mock_org = mock_organization() + mock_org = mock_organisation() db_session_mock.add(mock_org) db_session_mock.commit() mock_csv = mock_csv_content() with patch( - "api.v1.services.organization.organization_service.export_organization_members", + "api.v1.services.organisation.organisation_service.export_organisation_members", return_value=mock_csv, ) as mock_export: response = client.get( @@ -116,7 +116,7 @@ def test_export_success(client, db_session_mock): def test_export_unauthorized(client, db_session_mock): """Test export by an unauthorized user.""" - mock_org = mock_organization() + mock_org = mock_organisation() response = client.get( f"/api/v1/organisations/{mock_org.id}/users/export", ) @@ -125,21 +125,21 @@ def test_export_unauthorized(client, db_session_mock): assert response.status_code == 401 -def test_export_organization_not_found(client, db_session_mock): - """Test export when the organization ID does not exist.""" +def test_export_organisation_not_found(client, db_session_mock): + """Test export when the organisation ID does not exist.""" # Mock the user service to return the current super admin user app.dependency_overrides[user_service.get_current_super_admin] = ( mock_get_current_admin ) - # Simulate a non-existent organization + # Simulate a non-existent organisation non_existent_org_id = str(uuid7()) - # Mock the organization service to raise an exception for a non-existent organization + # Mock the organisation service to raise an exception for a non-existent organisation with patch( - "api.v1.services.organization.organization_service.fetch", - side_effect=HTTPException(status_code=404, detail="Organization not found"), + "api.v1.services.organisation.organisation_service.fetch", + side_effect=HTTPException(status_code=404, detail="Organisation not found"), ): response = client.get( f"/api/v1/organisations/{non_existent_org_id}/users/export", diff --git a/tests/v1/organization/test_get_organisation_users.py b/tests/v1/organization/test_get_organisation_users.py index 24525da91..c803411b1 100644 --- a/tests/v1/organization/test_get_organisation_users.py +++ b/tests/v1/organization/test_get_organisation_users.py @@ -10,8 +10,8 @@ from api.utils.success_response import success_response from api.v1.services.user import user_service from api.v1.models import User -from api.v1.models.organization import Organization -from api.v1.services.organization import organization_service +from api.v1.models.organisation import Organisation +from api.v1.services.organisation import organisation_service from main import app @@ -30,15 +30,15 @@ def mock_get_current_user(): def mock_org(): - """Mock organization""" - return Organization( + """Mock organisation""" + return Organisation( id=str(uuid7()), name="Test Company", ) def mock_org_users(): - """Mock organization users""" + """Mock organisation users""" return [mock_get_current_user()] @@ -57,15 +57,15 @@ def client(db_session_mock): def test_get_organisation_users_success(client, db_session_mock): - """Test to successfully get organization users""" + """Test to successfully get organisation users""" app.dependency_overrides[user_service.get_current_user] = ( lambda: mock_get_current_user ) - app.dependency_overrides[organization_service.paginate_users_in_organization] = ( + app.dependency_overrides[organisation_service.paginate_users_in_organisation] = ( lambda: mock_org_users ) - app.dependency_overrides[organization_service.fetch] = lambda: mock_org + app.dependency_overrides[organisation_service.fetch] = lambda: mock_org db_session_mock.add.return_value = None db_session_mock.commit.return_value = None @@ -74,22 +74,22 @@ def test_get_organisation_users_success(client, db_session_mock): mock_orgs_user = success_response( status_code=200, message="users fetched successfully", data={} ) - mock_organization = mock_org() + mock_organisation = mock_org() with patch( - "api.v1.services.organization.organization_service.paginate_users_in_organization", + "api.v1.services.organisation.organisation_service.paginate_users_in_organisation", return_value=mock_orgs_user, ): response = client.get( - f"/api/v1/organisations/{mock_organization.id}/users", + f"/api/v1/organisations/{mock_organisation.id}/users", headers={"Authorization": "Bearer token"}, ) assert response.status_code == 200 -def test_create_organization_unauthorized(client, db_session_mock): - """Test to get all users in an organization without authorization""" +def test_create_organisation_unauthorized(client, db_session_mock): + """Test to get all users in an organisation without authorization""" response = client.get( "/api/v1/organisations/orgs_id/users", diff --git a/tests/v1/payment/test_flutterwave.py b/tests/v1/payment/test_flutterwave.py index 20ab6f28c..87afda820 100644 --- a/tests/v1/payment/test_flutterwave.py +++ b/tests/v1/payment/test_flutterwave.py @@ -37,7 +37,7 @@ def test_user(): @pytest.fixture def mock_request(): - return PaymentDetail(organization_id="se4", plan_id="1", billing_option="Monthly", full_name="helo", redirect_url="http://example.com/redirect") + return PaymentDetail(organisation_id="se4", plan_id="1", billing_option="Monthly", full_name="helo", redirect_url="http://example.com/redirect") @pytest.fixture def mock_plan(): diff --git a/tests/v1/product/create_product_test.py b/tests/v1/product/create_product_test.py index 60a7d9d82..2503d8db7 100644 --- a/tests/v1/product/create_product_test.py +++ b/tests/v1/product/create_product_test.py @@ -16,7 +16,7 @@ from api.db.database import get_db from api.v1.models.user import User from api.v1.models.product import Product -from api.v1.models.organization import Organization +from api.v1.models.organisation import Organisation from api.v1.services.user import user_service, UserService from api.v1.services.product import product_service, ProductService from api.utils.db_validators import check_user_in_org @@ -88,7 +88,7 @@ def mock_foriegn_org(): ) as mock_create: mock_create.side_effect = HTTPException( status_code=status.HTTP_400_BAD_REQUEST, - detail="You are not a member of this organization", + detail="You are not a member of this organisation", ) yield mock_create @@ -105,7 +105,7 @@ def mock_get_current_user(): first_name="AdminTest", last_name="User", is_active=True, - # organizations=[org_1], + # organisations=[org_1], created_at=datetime.now(timezone.utc), updated_at=datetime.now(timezone.utc), ) @@ -190,4 +190,4 @@ def test_user_does_not_belong_to_org( ) assert response.status_code == status.HTTP_400_BAD_REQUEST - assert response.json()["message"] == "You are not a member of this organization" + assert response.json()["message"] == "You are not a member of this organisation" diff --git a/tests/v1/product/delete_product_test.py b/tests/v1/product/delete_product_test.py index c38ce64dd..a7b8969f6 100644 --- a/tests/v1/product/delete_product_test.py +++ b/tests/v1/product/delete_product_test.py @@ -70,7 +70,7 @@ def override_get_current_user(): first_name="AdminTest", last_name="User", is_active=True, - # organizations=[org_1], + # organisations=[org_1], created_at=datetime.now(timezone.utc), updated_at=datetime.now(timezone.utc), ) diff --git a/tests/v1/product/test_get_product_detail.py b/tests/v1/product/test_get_product_detail.py index eea26bc05..83b59c06d 100644 --- a/tests/v1/product/test_get_product_detail.py +++ b/tests/v1/product/test_get_product_detail.py @@ -5,7 +5,7 @@ from uuid_extensions import uuid7 from datetime import datetime, timezone, timedelta -from api.v1.models.organization import Organization +from api.v1.models.organisation import Organisation from api.v1.models.product import Product, ProductCategory from api.v1.models.user import User from main import app @@ -52,9 +52,9 @@ def client(db_session_mock): updated_at=updated_at, ) -# Create test organization +# Create test organisation -org = Organization( +org = Organisation( id=str(org_id), name="hng", email=None, @@ -88,7 +88,7 @@ def client(db_session_mock): ) -# user.organization = org +# user.organisation = org def test_get_product_detail_success(client, db_session_mock): diff --git a/tests/v1/product/test_get_product_stock.py b/tests/v1/product/test_get_product_stock.py index a500a0164..ce978615d 100644 --- a/tests/v1/product/test_get_product_stock.py +++ b/tests/v1/product/test_get_product_stock.py @@ -84,7 +84,7 @@ def mock_fetch_stock(db, product_id, current_user, org_id): else: return None - def mock_check_user_in_org(user, organization): + def mock_check_user_in_org(user, organisation): return True with patch.object(product_service, "fetch_stock", mock_fetch_stock): @@ -122,10 +122,10 @@ async def test_get_product_stock_not_found( # def mock_fetch_stock(db, product_id): # return mock_product -# def mock_check_user_in_org(user, organization): +# def mock_check_user_in_org(user, organisation): # raise HTTPException( # status_code=status.HTTP_400_BAD_REQUEST, -# detail="You are not a member of this organization", +# detail="You are not a member of this organisation", # ) # user = await mock_get_non_member_user_product() diff --git a/tests/v1/product/test_new_product_category.py b/tests/v1/product/test_new_product_category.py index 1da11fbe8..0d48286dc 100644 --- a/tests/v1/product/test_new_product_category.py +++ b/tests/v1/product/test_new_product_category.py @@ -7,10 +7,10 @@ from uuid_extensions import uuid7 from api.db.database import get_db -from api.v1.models.organization import Organization +from api.v1.models.organisation import Organisation from api.v1.models.product import ProductCategory from api.v1.services.product import ProductCategoryService -from api.v1.services.organization import organization_service +from api.v1.services.organisation import organisation_service from api.v1.services.user import user_service from api.v1.models.user import User from main import app @@ -52,9 +52,9 @@ def mock_product_category(): # def mock_org(): -# return Organization( +# return Organisation( # id=str(uuid7()), -# name="Test Organization", +# name="Test Organisation", # created_at=datetime.now(timezone.utc), # updated_at=datetime.now(timezone.utc) # ) @@ -67,7 +67,7 @@ def test_create_category_success(client, db_session_mock): app.dependency_overrides[user_service.get_current_user] = ( lambda: mock_get_current_user ) - # app.dependency_overrides[organization_service.create] = lambda: mock_org + # app.dependency_overrides[organisation_service.create] = lambda: mock_org db_session_mock.add.return_value = None db_session_mock.commit.return_value = None diff --git a/tests/v1/roles_permissions/test_get_roles.py b/tests/v1/roles_permissions/test_get_roles.py index d2eefe9b6..44f368193 100644 --- a/tests/v1/roles_permissions/test_get_roles.py +++ b/tests/v1/roles_permissions/test_get_roles.py @@ -61,8 +61,8 @@ def access_token(mock_db_session): return access_token -def test_get_roles_for_organization_success(mock_db_session, access_token): - """Test fetching roles for a specific organization successfully.""" +def test_get_roles_for_organisation_success(mock_db_session, access_token): + """Test fetching roles for a specific organisation successfully.""" org_id = str(uuid7()) role_name = "TestRole" @@ -76,8 +76,8 @@ def test_get_roles_for_organization_success(mock_db_session, access_token): assert response.status_code == 200 -def test_get_roles_for_organization_not_found(mock_db_session, access_token): - """Test fetching roles for a non-existing organization.""" +def test_get_roles_for_organisation_not_found(mock_db_session, access_token): + """Test fetching roles for a non-existing organisation.""" org_id = str(uuid7()) mock_db_session.query( @@ -90,11 +90,11 @@ def test_get_roles_for_organization_not_found(mock_db_session, access_token): ) assert response.status_code == 404 - assert response.json()["message"] == "Roles not found for the given organization" + assert response.json()["message"] == "Roles not found for the given organisation" -def test_get_roles_for_organization_unauthorized(mock_db_session): - """Test unauthorized access to fetching roles for an organization.""" +def test_get_roles_for_organisation_unauthorized(mock_db_session): + """Test unauthorized access to fetching roles for an organisation.""" org_id = str(uuid7()) diff --git a/tests/v1/roles_permissions/test_remove_user_from_role.py b/tests/v1/roles_permissions/test_remove_user_from_role.py index 99636326b..84db855a8 100644 --- a/tests/v1/roles_permissions/test_remove_user_from_role.py +++ b/tests/v1/roles_permissions/test_remove_user_from_role.py @@ -6,10 +6,10 @@ from main import app from api.db.database import get_db -from api.v1.models import User, Organization +from api.v1.models import User, Organisation from api.v1.services.user import user_service from api.v1.models.permissions.role import Role -from api.v1.models.permissions.user_org_role import user_organization_roles +from api.v1.models.permissions.user_org_role import user_organisation_roles client = TestClient(app) @@ -31,7 +31,7 @@ def mock_user_service(): @pytest.fixture def mock_org_service(): with patch( - "api.v1.services.organization.organization_service", autospec=True + "api.v1.services.organisation.organisation_service", autospec=True ) as org_service_mock: yield org_service_mock @@ -88,23 +88,23 @@ def test_user(user_role): @pytest.fixture() def test_org(): - org = Organization(id=str(uuid7()), name="Organization 1") + org = Organisation(id=str(uuid7()), name="Organisation 1") return org # admin role relation @pytest.fixture def admin_role_relation(): - return user_organization_roles( - organization_id=test_org.id, user_id=test_admin.id, role_id=admin_role.id + return user_organisation_roles( + organisation_id=test_org.id, user_id=test_admin.id, role_id=admin_role.id ) # user role relation @pytest.fixture def user_role_relation(): - return user_organization_roles( - organization_id=test_org.id, user_id=test_user.id, role_id=user_role.id + return user_organisation_roles( + organisation_id=test_org.id, user_id=test_user.id, role_id=user_role.id ) diff --git a/tests/v1/superadmin/test_get_contact.py b/tests/v1/superadmin/test_get_contact.py index 2ea5c9191..62167405c 100644 --- a/tests/v1/superadmin/test_get_contact.py +++ b/tests/v1/superadmin/test_get_contact.py @@ -7,7 +7,7 @@ from sqlalchemy.orm import Session from api.db.database import get_db from unittest import mock -from api.v1.models.associations import user_organization_association +from api.v1.models.associations import user_organisation_association from sqlalchemy import insert client = TestClient(app) diff --git a/tests/v1/user/test_get_current_user_organizations.py b/tests/v1/user/test_get_current_user_organizations.py index 194f34e4c..5fe696fd6 100644 --- a/tests/v1/user/test_get_current_user_organizations.py +++ b/tests/v1/user/test_get_current_user_organizations.py @@ -7,17 +7,17 @@ from uuid_extensions import uuid7 from api.db.database import get_db -from api.v1.models.organization import Organization +from api.v1.models.organisation import Organisation from api.v1.services.user import user_service from api.v1.models import User from main import app -# Mock user with organizations +# Mock user with organisations def mock_get_current_user(): - # Create mock organizations - org1 = Organization(id=str(uuid7()), name="Organization One") - org2 = Organization(id=str(uuid7()), name="Organization Two") + # Create mock organisations + org1 = Organisation(id=str(uuid7()), name="Organisation One") + org2 = Organisation(id=str(uuid7()), name="Organisation Two") # Create mock user mock_user = User( @@ -30,7 +30,7 @@ def mock_get_current_user(): is_super_admin=False, created_at=datetime.now(timezone.utc), updated_at=datetime.now(timezone.utc), - organizations=[org1, org2] + organisations=[org1, org2] ) return mock_user @@ -48,7 +48,7 @@ def client(db_session_mock): app.dependency_overrides = {} -def test_get_user_organizations_unauthorized(client, db_session_mock): +def test_get_user_organisations_unauthorized(client, db_session_mock): '''Test unauthorized response''' response = client.get( diff --git a/tests/v1/user/test_get_users_by_role.py b/tests/v1/user/test_get_users_by_role.py index ecc2a191c..ea51ca34f 100644 --- a/tests/v1/user/test_get_users_by_role.py +++ b/tests/v1/user/test_get_users_by_role.py @@ -3,8 +3,8 @@ from unittest.mock import patch, MagicMock from main import app from api.v1.models.user import User -from api.v1.models.organization import Organization -from api.v1.models.user import user_organization_association +from api.v1.models.organisation import Organisation +from api.v1.models.user import user_organisation_association from api.v1.services.user import user_service, UserService from uuid_extensions import uuid7 from api.db.database import get_db