Compare commits

...

5 Commits

Author SHA1 Message Date
blake hardee
e3dbe59fb9 implement service and resource api and corrected services 2024-11-04 23:24:48 -05:00
pmoharana-cmd
b3db1144ca Update tag service for resourceTag attachemnt and added tests 2024-10-20 14:16:35 -04:00
pmoharana-cmd
8532f8c4c1 Initialize resource testing suite 2024-10-20 14:15:56 -04:00
pmoharana-cmd
e26201b810 Fix resource service bug for querying by id 2024-10-20 14:15:01 -04:00
pmoharana-cmd
21167f7a6b Update entities and models for resource/service tagging 2024-10-20 14:14:03 -04:00
22 changed files with 565 additions and 151 deletions

View File

@ -24,3 +24,58 @@ def get_all(
subject = user_svc.get_user_by_uuid(user_id)
return resource_svc.get_resource_by_user(subject)
@api.get("/{id}", response_model=Resource, tags=["Resource"])
def get_by_id(
user_id: str,
id: int,
resource_svc: ResourceService = Depends(),
user_svc: UserService = Depends(),
):
subject = user_svc.get_user_by_uuid(user_id)
resource = resource_svc.get_resource_by_id(id)
return resource
@api.post("/", response_model=Resource, tags=["Resource"])
def create_service(
user_id: str,
resource: Resource,
resource_svc: ResourceService = Depends(),
user_svc: UserService = Depends(),
):
subject = user_svc.get_user_by_uuid(user_id)
new_resource = resource_svc.create(subject, resource)
return new_resource
@api.put("/{resource_id}", response_model=Resource, tags=["Resource"])
def update_service(
resource_id: int,
user_id: str,
resource: Resource,
resource_svc: ResourceService = Depends(),
user_svc: UserService = Depends(),
):
resource.id = resource_id
subject = user_svc.get_user_by_uuid(user_id)
updated_resource = resource_svc.update(subject, resource)
return updated_resource
@api.delete("/{resource_id}", response_model=Resource, tags=["Resource"])
def delete_service_tag_by_id(
resource_id: int,
tag_id: int,
user_id: str,
resource_svc: ResourceService = Depends(),
user_svc: UserService = Depends(),
):
subject = user_svc.get_user_by_uuid(user_id)
resource = resource_svc.get_resource_by_id(resource_id)
tag = resource_svc._tag_service.get_tag_by_id(tag_id)
resource_svc.remove_tag(subject, resource, tag)
return resource_svc.get_resource_by_id(resource_id)

View File

@ -1,6 +1,6 @@
from fastapi import APIRouter, Depends
from ..services import ServiceService, UserService
from ..models.service_model import Service
from fastapi import APIRouter, Depends, HTTPException
from ..services import ServiceService, UserService, TagService
from ..models import Service, Tag
from typing import List
@ -24,3 +24,58 @@ def get_all(
subject = user_svc.get_user_by_uuid(user_id)
return service_svc.get_service_by_user(subject)
@api.get("/{id}", response_model=Service, tags=["Service"])
def get_by_id(
user_id: str,
id: int,
service_svc: ServiceService = Depends(),
user_svc: UserService = Depends(),
):
subject = user_svc.get_user_by_uuid(user_id)
service = service_svc.get_service_by_id(id)
return service
@api.post("/", response_model=Service, tags=["Service"])
def create_service(
user_id: str,
service: Service,
service_svc: ServiceService = Depends(),
user_svc: UserService = Depends(),
):
subject = user_svc.get_user_by_uuid(user_id)
new_service = service_svc.create(subject, service)
return new_service
@api.put("/{service_id}", response_model=Service, tags=["Service"])
def update_service(
service_id: int,
user_id: str,
service: Service,
service_svc: ServiceService = Depends(),
user_svc: UserService = Depends(),
):
service.id = service_id
subject = user_svc.get_user_by_uuid(user_id)
updated_service = service_svc.update(subject, service)
return updated_service
@api.delete("/{service_id}", response_model=Service, tags=["Service"])
def delete_service_tag_by_id(
service_id: int,
tag_id: int,
user_id: str,
service_svc: ServiceService = Depends(),
user_svc: UserService = Depends(),
):
subject = user_svc.get_user_by_uuid(user_id)
service = service_svc.get_service_by_id(service_id)
tag = service_svc._tag_service.get_tag_by_id(tag_id)
service_svc.remove_tag(subject, service, tag)
return service_svc.get_service_by_id(service_id)

View File

@ -31,8 +31,8 @@ class ResourceEntity(EntityBase):
link: Mapped[str] = mapped_column(String, nullable=False)
program: Mapped[Program_Enum] = mapped_column(Enum(Program_Enum), nullable=False)
# relationships
resourceTags: Mapped[list["ResourceTagEntity"]] = relationship(
back_populates="resource", cascade="all,delete"
resource_tags: Mapped[list["ResourceTagEntity"]] = relationship(
back_populates="resource", cascade="all,delete-orphan"
)
@classmethod
@ -64,4 +64,5 @@ class ResourceEntity(EntityBase):
summary=self.summary,
link=self.link,
program=self.program,
tags=[tag_entity.tag.to_model() for tag_entity in self.resource_tags],
)

View File

@ -1,6 +1,7 @@
""" Defines the table for resource tags """
# Import our mapped SQL types from SQLAlchemy
from importlib.resources import Resource
from sqlalchemy import ForeignKey, Integer, String, DateTime
# Import mapping capabilities from the SQLAlchemy ORM
@ -15,6 +16,8 @@ from datetime import datetime
# Import self for to model
from typing import Self
from ..models import ResourceTag
class ResourceTagEntity(EntityBase):
@ -27,20 +30,20 @@ class ResourceTagEntity(EntityBase):
tagId: Mapped[int] = mapped_column(ForeignKey("tag.id"))
# relationships
resource: Mapped["ResourceEntity"] = relationship(back_populates="resourceTags")
tag: Mapped["TagEntity"] = relationship(back_populates="resourceTags")
resource: Mapped["ResourceEntity"] = relationship(back_populates="resource_tags")
tag: Mapped["TagEntity"] = relationship(back_populates="resource")
# @classmethod
# def from_model (cls, model: resource_tag_model) -> Self:
# return cls (
# id = model.id,
# resourceId = model.resourceId,
# tagId = model.tagId,
# )
def to_model(self) -> ResourceTag:
return ResourceTag(
id=self.id,
resourceId=self.resourceId,
tagId=self.tagId,
)
# def to_model (self) -> resource_tag_model:
# return user_model(
# id = self.id,
# resourceId = self.resourceId,
# tagId = self.tagId,
# )
@classmethod
def from_model(cls, model: "ResourceTag") -> Self:
return cls(
id=model.id,
resourceId=model.resourceId,
tagId=model.tagId,
)

View File

@ -20,6 +20,7 @@ from backend.models.service_model import Service
from typing import Self
from backend.models.enum_for_models import ProgramTypeEnum
class ServiceEntity(EntityBase):
# set table name
@ -32,16 +33,33 @@ class ServiceEntity(EntityBase):
status: Mapped[str] = mapped_column(String(32), nullable=False)
summary: Mapped[str] = mapped_column(String(100), nullable=False)
requirements: Mapped[list[str]] = mapped_column(ARRAY(String))
program: Mapped[ProgramTypeEnum] = mapped_column(Enum(ProgramTypeEnum), nullable=False)
program: Mapped[ProgramTypeEnum] = mapped_column(
Enum(ProgramTypeEnum), nullable=False
)
# relationships
serviceTags: Mapped[list["ServiceTagEntity"]] = relationship(
back_populates="service", cascade="all,delete"
service_tags: Mapped[list["ServiceTagEntity"]] = relationship(
back_populates="service", cascade="all,delete-orphan"
)
def to_model(self) -> Service:
return Service(id=self.id, name=self.name, status=self.status, summary=self.summary, requirements=self.requirements, program=self.program)
return Service(
id=self.id,
name=self.name,
status=self.status,
summary=self.summary,
requirements=self.requirements,
program=self.program,
tags=[tag.tag.to_model() for tag in self.service_tags],
)
@classmethod
def from_model(cls, model:Service) -> Self:
return cls(id=model.id, name=model.name, status=model.status, summary=model.summary, requirements=model.requirements, program=model.program)
def from_model(cls, model: Service) -> Self:
return cls(
id=model.id,
name=model.name,
status=model.status,
summary=model.summary,
requirements=model.requirements,
program=model.program,
)

View File

@ -9,6 +9,10 @@ from sqlalchemy.orm import Mapped, mapped_column, relationship
# Import the EntityBase that we are extending
from .entity_base import EntityBase
from typing import Self
from ..models.service_tag_model import ServiceTag
class ServiceTagEntity(EntityBase):
@ -21,5 +25,16 @@ class ServiceTagEntity(EntityBase):
tagId: Mapped[int] = mapped_column(ForeignKey("tag.id"))
# relationships
service: Mapped["ServiceEntity"] = relationship(back_populates="serviceTags")
tag: Mapped["TagEntity"] = relationship(back_populates="serviceTags")
service: Mapped["ServiceEntity"] = relationship(back_populates="service_tags")
tag: Mapped["TagEntity"] = relationship(back_populates="service")
def to_model(self) -> ServiceTag:
return ServiceTag(id=self.id, serviceId=self.serviceId, tagId=self.tagId)
@classmethod
def from_model(cls, model: "ServiceTag") -> Self:
return cls(
id=model.id,
serviceId=model.serviceId,
tagId=model.tagId,
)

View File

@ -16,22 +16,25 @@ from ..models.tag_model import Tag
from typing import Self
class TagEntity(EntityBase):
#set table name
# set table name
__tablename__ = "tag"
#set fields
# set fields
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.now)
content: Mapped[str] = mapped_column(String(100), nullable=False)
#relationships
resourceTags: Mapped[list["ResourceTagEntity"]] = relationship(back_populates="tag", cascade="all,delete")
serviceTags: Mapped[list["ServiceTagEntity"]] = relationship(back_populates="tag", cascade="all,delete")
# relationships
resource: Mapped[list["ResourceTagEntity"]] = relationship(
back_populates="tag", cascade="all,delete", overlaps="tag"
)
service: Mapped[list["ServiceTagEntity"]] = relationship(
back_populates="tag", cascade="all,delete", overlaps="tag"
)
@classmethod
def from_model(cls, model: Tag) -> Self:
"""
@ -42,10 +45,10 @@ class TagEntity(EntityBase):
Returns:
self: The entity
"""
return cls(
id=model.id,
content=model.id,
content=model.content,
)
def to_model(self) -> Tag:
@ -56,10 +59,4 @@ class TagEntity(EntityBase):
User: A User model for API usage
"""
return Tag(
id=self.id,
content=self.content,
)
return Tag(id=self.id, content=self.content, created_at=self.created_at)

View File

@ -0,0 +1,7 @@
from .user_model import User
from .resource_model import Resource
from .service_model import Service
from .tag_model import Tag
from .enum_for_models import ProgramTypeEnum, UserTypeEnum
from .resource_tag_model import ResourceTag
from .service_tag_model import ServiceTag

View File

@ -4,6 +4,7 @@ from typing import List
from datetime import datetime
from typing import Optional
from .enum_for_models import ProgramTypeEnum
from .tag_model import Tag
class Resource(BaseModel):
@ -13,3 +14,4 @@ class Resource(BaseModel):
link: str = Field(..., max_length=150, description="link to the resource")
program: ProgramTypeEnum
created_at: Optional[datetime]
tags: List[Tag] = []

View File

@ -1,13 +1,7 @@
from pydantic import BaseModel, Field
from enum import Enum
from typing import List
from datetime import datetime
from typing import Optional
from .tag_model import Tag
from .resource_model import Resource
from pydantic import BaseModel
class ResourceTag(Resource, BaseModel):
class ResourceTag(BaseModel):
id: int | None = None
resourceid: int | None = None
tagid: List[Tag]
tagId: int
resourceId: int

View File

@ -4,6 +4,7 @@ from typing import List
from datetime import datetime
from typing import Optional
from .enum_for_models import ProgramTypeEnum
from .tag_model import Tag
class Service(BaseModel):
@ -14,3 +15,4 @@ class Service(BaseModel):
summary: str
requirements: List[str]
program: ProgramTypeEnum
tags: List[Tag] = []

View File

@ -4,16 +4,12 @@ from typing import List
from datetime import datetime
from typing import Optional
from .enum_for_models import ProgramTypeEnum
from .enum_for_models import UserTypeEnum
from .service_model import Service
from .tag_model import Tag
from pydantic import BaseModel
from datetime import datetime
class ServiceTag(Service, BaseModel):
class ServiceTag(BaseModel):
id: int | None = None
serviceid: int | None = None
tagId: List[Tag]
serviceId: int | None = None
tagId: int

View File

@ -6,7 +6,12 @@ from ..database import engine, _engine_str
from ..env import getenv
from .. import entities
from ..test.services import user_test_data, service_test_data, resource_test_data
from ..test.services import (
user_test_data,
service_test_data,
resource_test_data,
tag_test_data,
)
database = getenv("POSTGRES_DATABASE")
@ -23,5 +28,6 @@ entities.EntityBase.metadata.create_all(engine)
with Session(engine) as session:
user_test_data.insert_test_data(session)
service_test_data.insert_test_data(session)
tag_test_data.insert_test_data(session)
resource_test_data.insert_test_data(session)
session.commit()

View File

@ -1,4 +1,6 @@
from fastapi import Depends
from backend.services.tag import TagService
from ..database import db_session
from sqlalchemy.orm import Session
from sqlalchemy import select
@ -6,13 +8,18 @@ from ..models.resource_model import Resource
from ..entities.resource_entity import ResourceEntity
from ..models.user_model import User, UserTypeEnum
from .exceptions import ResourceNotFoundException
from .exceptions import ProgramNotAssignedException, ResourceNotFoundException
class ResourceService:
def __init__(self, session: Session = Depends(db_session)):
def __init__(
self,
session: Session = Depends(db_session),
tag_service: TagService = Depends(),
):
self._session = session
self._tag_service = tag_service
def get_resource_by_user(self, subject: User):
"""Resource method getting all of the resources that a user has access to based on role"""
@ -43,15 +50,22 @@ class ResourceService:
Returns:
Resource: Object added to table
"""
if resource.role != user.role or resource.group != user.group:
raise PermissionError(
"User does not have permission to add resources in this role or group."
if user.role != UserTypeEnum.ADMIN:
raise ProgramNotAssignedException(
f"User is not {UserTypeEnum.ADMIN}, cannot update resource"
)
resource_entity = ResourceEntity.from_model(resource)
self._session.add(resource_entity)
self._session.commit()
self._session.flush()
for tag in resource.tags:
tag_entity = self._tag_service.get_or_create_tag(tag.content)
self._tag_service.add_tag_resource(
user, tag_entity, ResourceEntity.to_model(resource_entity)
)
self._session.commit()
return resource_entity.to_model()
def get_by_id(self, user: User, id: int) -> Resource:
@ -72,8 +86,7 @@ class ResourceService:
self._session.query(ResourceEntity)
.filter(
ResourceEntity.id == id,
ResourceEntity.role == user.role,
ResourceEntity.group == user.group,
ResourceEntity.program.in_(user.program),
)
.one_or_none()
)
@ -97,22 +110,38 @@ class ResourceService:
Raises:
ResourceNotFoundException: If no resource is found with the corresponding ID
"""
if resource.role != user.role or resource.group != user.group:
raise PermissionError(
"User does not have permission to update this resource."
if user.role != UserTypeEnum.ADMIN:
raise ProgramNotAssignedException(
f"User is not {UserTypeEnum.ADMIN}, cannot update service"
)
obj = self._session.get(ResourceEntity, resource.id) if resource.id else None
resource_entity = (
self._session.query(ResourceEntity)
.filter(ResourceEntity.id == resource.id)
.one_or_none()
)
if obj is None:
self._tag_service.delete_all_tags_service(resource)
if resource_entity is None:
raise ResourceNotFoundException(
f"No resource found with matching id: {resource.id}"
"The resource you are searching for does not exist."
)
obj.update_from_model(resource) # Assuming an update method exists
self._session.commit()
resource_entity.name = resource.name
resource_entity.status = resource.status
resource_entity.summary = resource.summary
resource_entity.requirements = resource.requirements
resource_entity.program = resource.program
return obj.to_model()
for tag in resource.tags:
tag_entity = self._tag_service.get_or_create_tag(tag.content)
self._tag_service.add_tag_service(
user, tag_entity, ResourceEntity.to_model(resource_entity)
)
self._session.commit()
return resource_entity.to_model()
def delete(self, user: User, id: int) -> None:
"""

View File

@ -1,5 +1,9 @@
from typing import List
from fastapi import Depends
from backend.entities.service_tag_entity import ServiceTagEntity
from backend.entities.tag_entity import TagEntity
from ..database import db_session
from sqlalchemy.orm import Session
from sqlalchemy import func, select, and_, func, or_, exists, or_
@ -12,12 +16,19 @@ from backend.services.exceptions import (
ServiceNotFoundException,
ProgramNotAssignedException,
)
from . import TagService
from ..models import Tag
class ServiceService:
def __init__(self, session: Session = Depends(db_session)):
def __init__(
self,
session: Session = Depends(db_session),
tag_service: TagService = Depends(),
):
self._session = session
self._tag_service = tag_service
def get_service_by_program(self, program: ProgramTypeEnum) -> list[Service]:
"""Service method getting services belonging to a particular program."""
@ -33,8 +44,8 @@ class ServiceService:
if entity is None:
raise ServiceNotFoundException(f"Service with id: {id} does not exist")
return entity.to_model()
service = entity.to_model()
return service
def get_service_by_name(self, name: str) -> Service:
"""Service method getting services by id."""
@ -44,7 +55,9 @@ class ServiceService:
if entity is None:
raise ServiceNotFoundException(f"Service with name: {name} does not exist")
return entity.to_model()
service = entity.to_model()
# service.tags.extend(TagService.get_tags_for_service(TagService, service))
return service
def get_service_by_user(self, subject: User):
"""Service method getting all of the services that a user has access to based on role"""
@ -73,8 +86,8 @@ class ServiceService:
query = select(ServiceEntity)
entities = self._session.scalars(query).all()
return [service.to_model() for service in entities]
services = [service.to_model() for service in entities]
return services
def create(self, subject: User, service: Service) -> Service:
"""Creates/adds a service to the table."""
@ -85,6 +98,14 @@ class ServiceService:
service_entity = ServiceEntity.from_model(service)
self._session.add(service_entity)
self._session.flush()
for tag in service.tags:
tag_entity = self._tag_service.get_or_create_tag(tag.content)
self._tag_service.add_tag_service(
subject, tag_entity, ServiceEntity.to_model(service_entity)
)
self._session.commit()
return service_entity.to_model()
@ -95,7 +116,13 @@ class ServiceService:
f"User is not {UserTypeEnum.ADMIN}, cannot update service"
)
service_entity = self._session.get(ServiceEntity, service.id)
service_entity = (
self._session.query(ServiceEntity)
.filter(ServiceEntity.id == service.id)
.one_or_none()
)
self._tag_service.delete_all_tags_service(service)
if service_entity is None:
raise ServiceNotFoundException(
@ -108,20 +135,49 @@ class ServiceService:
service_entity.requirements = service.requirements
service_entity.program = service.program
self._session.commit()
for tag in service.tags:
tag_entity = self._tag_service.get_or_create_tag(tag.content)
self._tag_service.add_tag_service(
subject, tag_entity, ServiceEntity.to_model(service_entity)
)
self._session.commit()
return service_entity.to_model()
def delete(self, subject: User, service: Service) -> None:
"""Deletes a service from the table."""
if subject.role != UserTypeEnum.ADMIN:
raise ProgramNotAssignedException(f"User is not {UserTypeEnum.ADMIN}")
service_entity = self._session.get(ServiceEntity, service.id)
service_entity = (
self._session.query(ServiceEntity)
.filter(ServiceEntity.id == service.id)
.one_or_none()
)
if service_entity is None:
raise ServiceNotFoundException(
"The service you are searching for does not exist."
)
self._tag_service.delete_all_tags_service(service_entity.to_model())
self._session.delete(service_entity)
self._session.commit()
def add_tag(self, subject: User, service: Service, tag: Tag):
service = self.get_service_by_id(service.id)
tag = self._tag_service.get_tag_by_id(tag.id)
self._tag_service.add_tag_service(subject, service.id, tag.id)
def remove_tag(self, subject: User, service: Service, tag: Tag) -> None:
service_tag = (
self._session.query(ServiceTagEntity)
.filter(
ServiceTagEntity.serviceId == service.id,
ServiceTagEntity.tagId == tag.id,
)
.one_or_none()
)
if service_tag is None:
raise Exception(
f"No tag with id {tag.id} found for service with id {service.id}."
)
self._session.delete(service_tag)
self._session.commit()

View File

@ -1,15 +1,13 @@
from fastapi import Depends
from backend.models.enum_for_models import UserTypeEnum
from ..database import db_session
from sqlalchemy.orm import Session
from ..models.tag_model import Tag
from ..entities.tag_entity import TagEntity
from ..entities.resource_tag_entity import ResourceTagEntity
from ..entities.service_tag_entity import ServiceTagEntity
from .exceptions import ResourceNotFoundException
from ..models.user_model import User
from ..models.resource_model import Resource
from ..models.service_model import Service
from sqlalchemy import select
from ..entities import TagEntity, ResourceTagEntity, ServiceTagEntity
from ..models import User, Resource, Service, Tag, ResourceTag, ServiceTag
from .exceptions import ProgramNotAssignedException, ResourceNotFoundException
from datetime import datetime
class TagService:
@ -20,39 +18,33 @@ class TagService:
def all(self) -> list[Tag]:
"""Returns a list of all Tags"""
query = select(TagEntity)
query = select(TagEntity)
entities = self._session.scalars(query).all()
return [entity.to_model() for entity in entities]
def get_tag_by_id(self, id:int) -> Tag:
def get_tag_by_id(self, id: int) -> Tag:
"""Returns tag based on it's id."""
tag = self._session.query(TagEntity).filter(TagEntity.id == id).one_or_none()
return tag.to_model()
return tag
def create(self, user: User, tag: Tag) -> Tag:
def create(self, tag: Tag) -> Tag:
"""Creates a tag in the database with id and content."""
# Add user permission check here if needed
tag_entity = TagEntity.from_model(tag)
self._session.add(tag_entity)
self._session.commit
self._session.commit()
return tag_entity.to_model()
def delete(self, user: User, id:int) -> None:
def delete(self, id: int) -> None:
"""Method to delete a tag from the database, along with all connections."""
tag = (
self._session.query(TagEntity)
.filter(
TagEntity.id == id
)
.one_or_none()
)
tag = self._session.query(TagEntity).filter(TagEntity.id == id).one_or_none()
if tag is None:
raise ResourceNotFoundException(f"No tag found with matching id: {id}")
@ -60,20 +52,16 @@ class TagService:
resource_tags = (
self._session.query(ResourceTagEntity)
.filter(
ResourceTagEntity.tagId == id
)
.filter(ResourceTagEntity.tagId == id)
.all()
)
for tag in resource_tags:
self._session.delete(tag)
service_tags = (
self._session.query(ServiceTagEntity)
.filter(
ServiceTagEntity.tagId == id
)
.filter(ServiceTagEntity.tagId == id)
.all()
)
@ -81,42 +69,147 @@ class TagService:
self._session.delete(tag)
self._session.commit()
def get_tags_for_resource(self, user: User, resource: Resource) -> list[Tag]:
def get_tags_for_resource(self, resource: Resource) -> list[Tag]:
"""Get tags based on a resource."""
tags: list[Tag]
tags: list[Tag] = []
resource_tags = (
self._session.query(ResourceTagEntity)
.filter(
ResourceTagEntity.tagId == resource.id
)
.filter(ResourceTagEntity.tagId == resource.id)
.all()
)
if resource_tags is None:
raise ResourceNotFoundException(f"No tags found for resource with id: {resource.id}")
raise ResourceNotFoundException(
f"No tags found for resource with id: {resource.id}"
)
for tag in resource_tags:
tags.append(self.get_tag_by_id(tag.id))
return tags
def get_tags_for_service(self, user: User, service: Service) -> list[Tag]:
def get_tags_for_service(self, service: Service) -> list[Tag]:
"""Get tags based on a resource."""
tags: list[Tag]
tags: list[Tag] = []
service_tags = (
self._session.query(ServiceTagEntity)
.filter(
ServiceTagEntity.tagId == service.id
)
.filter(ServiceTagEntity.serviceId == service.id)
.all()
)
if service_tags is None:
raise ResourceNotFoundException(f"No tags found for service with id: {service.id}")
raise ResourceNotFoundException(
f"No tags found for service with id: {service.id}"
)
for tag in service_tags:
tags.append(self.get_tag_by_id(tag.id))
return tags
def add_tag_resource(self, user: User, tag: Tag, resource: Resource) -> None:
"""Adds a tag to a resource"""
if user.role != UserTypeEnum.ADMIN:
raise ProgramNotAssignedException(
f"User is not {UserTypeEnum.ADMIN}, cannot update resource tags"
)
existing_tag = (
self._session.query(ResourceTagEntity)
.filter(
ResourceTagEntity.tagId == tag.id,
ResourceTagEntity.resourceId == resource.id,
)
.first()
)
if existing_tag:
raise Exception(
f"Tag with id {tag.id} already exists for resource with id {resource.id}."
)
resource_tag_entity = ResourceTagEntity.from_model(
ResourceTag(tagId=tag.id, resourceId=resource.id)
)
try:
self._session.add(resource_tag_entity)
self._session.commit()
except Exception as e:
self._session.rollback() # Rollback in case of error
raise Exception("Failed to add tag to resource") from e
def add_tag_service(self, user: User, tag: Tag, service: Service) -> None:
"""Adds a tag to a service"""
existing_tag = (
self._session.query(ServiceTagEntity)
.filter(
ServiceTagEntity.tagId == tag.id,
ServiceTagEntity.serviceId == service.id,
)
.first()
)
if existing_tag:
raise Exception(
f"Tag with id {tag.id} already exists for service with id {service.id}."
)
service_tag_entity = ServiceTagEntity.from_model(
ServiceTag(tagId=tag.id, serviceId=service.id)
)
try:
self._session.add(service_tag_entity)
self._session.commit()
except Exception as e:
self._session.rollback() # Rollback in case of error
raise Exception("Failed to add tag to service") from e
def delete_all_tags_service(self, service: Service) -> None:
"""Deletes all service tags for a service"""
service_tags = self._session.query(ServiceTagEntity).filter(
ServiceTagEntity.serviceId == service.id
)
if service_tags.count() == 0:
raise ResourceNotFoundException
service_tags.delete(synchronize_session=False)
self._session.commit()
def delete_all_tags_resource(self, resource: Resource) -> None:
"""Deletes all resource tags for a resource"""
resource_tags = self._session.query(ResourceTagEntity).filter(
ResourceTagEntity.resourceId == resource.id
)
if resource_tags.count() == 0:
raise ResourceNotFoundException
resource_tags.delete(synchronize_session=False)
self._session.commit()
def get_or_create_tag(self, content: str) -> Tag:
existing_tag = (
self._session.query(TagEntity)
.filter(TagEntity.content == content)
.one_or_none()
)
if existing_tag:
return existing_tag.to_model()
try:
tag = Tag(content=content)
tag_entity = TagEntity.from_model(tag)
self._session.add(tag_entity)
self._session.commit()
return tag_entity.to_model()
except Exception as e:
self._session.rollback()
raise Exception(f"Failed to create tag with content: {tag.content}") from e

View File

@ -4,7 +4,12 @@ import pytest
from sqlalchemy import Engine, create_engine, text
from sqlalchemy.orm import Session
from sqlalchemy.exc import OperationalError
from .services import user_test_data, tag_test_data, service_test_data
from .services import (
user_test_data,
tag_test_data,
service_test_data,
resource_test_data,
)
from ..database import _engine_str
from ..env import getenv
@ -57,5 +62,6 @@ def setup_insert_data_fixture(session: Session):
user_test_data.insert_fake_data(session)
tag_test_data.insert_fake_data(session)
service_test_data.insert_fake_data(session)
resource_test_data.insert_fake_data(session)
session.commit()
yield

View File

@ -3,11 +3,7 @@
import pytest
from unittest.mock import create_autospec
from sqlalchemy.orm import Session
from ...services import UserService
from ...services import TagService
from ...services import ServiceService
from ...services import UserService, TagService, ServiceService, ResourceService
@pytest.fixture()
@ -15,12 +11,20 @@ def user_svc(session: Session):
"""This fixture is used to test the UserService class"""
return UserService(session)
@pytest.fixture()
def tag_svc(session: Session):
"""This fixture is used to test the TagService class"""
return TagService(session)
@pytest.fixture()
def service_svc(session: Session):
"""This fixture is used to test the ServiceService class"""
return ServiceService(session)
return ServiceService(session)
@pytest.fixture()
def resource_svc(session: Session):
"""This fixutre is used to test the ResourceService class"""
return ResourceService(session)

View File

@ -0,0 +1,10 @@
from backend.services import ResourceService, TagService
from .user_test_data import admin
from .fixtures import resource_svc, tag_svc
def test_temp(resource_svc: ResourceService, tag_svc: TagService):
resources = resource_svc.get_resource_by_user(admin)
tags = tag_svc.all()
print(tags)
print(resources)

View File

@ -3,7 +3,8 @@ from sqlalchemy.orm import Session
from ...entities import ServiceEntity
from ...models.enum_for_models import ProgramTypeEnum
from ...models.service_model import Service
from ...models import Service, Tag
from .tag_test_data import tags
service1 = Service(
id=1,
@ -95,6 +96,7 @@ service_1 = Service(
summary="24/7 support for individuals in crisis",
requirements=["Anonymous", "Confidential"],
program=ProgramTypeEnum.DOMESTIC,
tags=[tags[0], tags[1]],
)
service_2 = Service(

View File

@ -2,13 +2,55 @@
# PyTest
import pytest
from ...services.tag import TagService
from .fixtures import tag_svc
from .tag_test_data import tag1, tag2, tag3
from . import tag_test_data
from ...services import TagService, ResourceService, ServiceService
from .fixtures import tag_svc, resource_svc, service_svc
from .tag_test_data import tag_to_create, tag_to_create_no_id, tags
from .user_test_data import admin
def test_get_all(tag_svc: TagService):
"""Test that all tags can be retrieved."""
tags = tag_svc.all()
assert len(tags) == 3
all_tags = tag_svc.all()
assert len(all_tags) == len(tags)
def test_create_tag(tag_svc: TagService):
"""Test creation of tag"""
created_tag = tag_svc.create(admin, tag_to_create)
assert created_tag.content == tag_to_create.content
assert len(tag_svc.all()) == len(tags) + 1
def test_create_tag_no_id(tag_svc: TagService):
"""Test creation of tag"""
created_tag = tag_svc.create(admin, tag_to_create_no_id)
queried_tag = tag_svc.get_tag_by_id(4)
assert created_tag.content == tag_to_create_no_id.content
assert len(tag_svc.all()) == len(tags) + 1
assert queried_tag.content == created_tag.content
def test_resource_tag_creation(tag_svc: TagService, resource_svc: ResourceService):
"""Test creation and attachment of resource tag"""
resource = resource_svc.get_resource_by_user(admin)[0]
tag_svc.add_tag_resource(admin, tags[0], resource)
updated_resource = resource_svc.get_by_id(admin, resource.id)
assert len(resource.tags) == 0
assert len(updated_resource.tags) == 1
assert resource.id == updated_resource.id
def test_service_tag_creation(tag_svc: TagService, service_svc: ServiceService):
"""Test creation and attachment of service tag"""
service = service_svc.get_service_by_user(admin)[0]
tag_svc.add_tag_service(admin, tags[0], service)
updated_service = service_svc.get_service_by_id(service.id)
assert len(service.tags) == 0
assert len(updated_service.tags) == 1
assert service.id == updated_service.id

View File

@ -11,7 +11,9 @@ tag2 = Tag(id=2, content="Tag 2", created_at=datetime.now())
tag3 = Tag(id=3, content="Tag 3", created_at=datetime.now())
tagToCreate = Tag(id=4, content="Tag 4", created_at=datetime.now())
tag_to_create = Tag(id=4, content="Tag 4", created_at=datetime.now())
tag_to_create_no_id = Tag(content="Tag 5", created_at=datetime.now())
tags = [tag1, tag2, tag3]
@ -61,6 +63,25 @@ def insert_fake_data(session: Session):
session.commit()
def insert_test_data(session: Session):
"""Inserts test organization data into the test session."""
global tags
# Create entities for test organization data
entities = []
for tag in tags:
entity = TagEntity.from_model(tag)
session.add(entity)
entities.append(entity)
# Reset table IDs to prevent ID conflicts
reset_table_id_seq(session, TagEntity, TagEntity.id, len(tags) + 1)
# Commit all changes
session.commit()
@pytest.fixture(autouse=True)
def fake_data_fixture(session: Session):
"""Insert fake data the session automatically when test is run.