diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index 52137f77aa..d101e6cae2 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,5 +1,5 @@
[bumpversion]
-current_version = 62.2.4
+current_version = 62.2.5
commit = True
tag = True
tag_name = v{new_version}
diff --git a/cg/__init__.py b/cg/__init__.py
index a7779ad0db..7870f9e682 100644
--- a/cg/__init__.py
+++ b/cg/__init__.py
@@ -1,2 +1,2 @@
__title__ = "cg"
-__version__ = "62.2.4"
+__version__ = "62.2.5"
diff --git a/cg/clients/freshdesk/freshdesk_client.py b/cg/clients/freshdesk/freshdesk_client.py
index 535ef06821..9a9650f9b1 100644
--- a/cg/clients/freshdesk/freshdesk_client.py
+++ b/cg/clients/freshdesk/freshdesk_client.py
@@ -1,19 +1,13 @@
-import logging
from http import HTTPStatus
+from pathlib import Path
-from pydantic import ValidationError
-from requests import RequestException, Response, Session
+from requests import Session
from requests.adapters import HTTPAdapter
from urllib3 import Retry
from cg.clients.freshdesk.constants import EndPoints
-from cg.clients.freshdesk.exceptions import (
- FreshdeskAPIException,
- FreshdeskModelException,
-)
-from cg.clients.freshdesk.models import TicketCreate, TicketResponse
-
-LOG = logging.getLogger(__name__)
+from cg.clients.freshdesk.models import ReplyCreate, TicketCreate, TicketResponse
+from cg.clients.freshdesk.utils import handle_client_errors, prepare_attachments
class FreshdeskClient:
@@ -24,33 +18,23 @@ def __init__(self, base_url: str, api_key: str):
self.api_key = api_key
self.session = self._get_session()
- def create_ticket(self, ticket: TicketCreate) -> TicketResponse:
- """Create a ticket."""
- LOG.debug(ticket.model_dump_json())
- try:
- response: Response = self.session.post(
- url=self._url(EndPoints.TICKETS),
- json=ticket.model_dump(exclude_none=True),
- )
- response.raise_for_status()
- return TicketResponse.model_validate(response.json())
- except RequestException as error:
- LOG.error(f"Could not create ticket: {error}")
- raise FreshdeskAPIException(error) from error
- except ValidationError as error:
- LOG.error(f"Response from Freshdesk does not fit model: {TicketResponse}.\n{error}")
- raise FreshdeskModelException(error) from error
+ @handle_client_errors
+ def create_ticket(self, ticket: TicketCreate, attachments: list[Path] = None) -> TicketResponse:
+ """Create a ticket with multipart form data."""
+ multipart_data = ticket.to_multipart_data()
+ files = prepare_attachments(attachments) if attachments else None
- def _url(self, endpoint: str) -> str:
- """Get the full URL for the endpoint."""
- return f"{self.base_url}{endpoint}"
+ response = self.session.post(
+ url=f"{self.base_url}{EndPoints.TICKETS}", data=multipart_data, files=files
+ )
+ response.raise_for_status()
+ return TicketResponse.model_validate(response.json())
+ @handle_client_errors
def _get_session(self) -> Session:
- """Configures and sets a session to be used for requests."""
session = Session()
- self._configure_retries(session)
session.auth = (self.api_key, "X")
- session.headers.update({"Content-Type": "application/json"})
+ self._configure_retries(session)
return session
@staticmethod
@@ -69,3 +53,14 @@ def _configure_retries(session: Session) -> None:
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("https://", adapter)
+
+ @handle_client_errors
+ def reply_to_ticket(self, reply: ReplyCreate, attachments: list[Path] = None) -> None:
+ """Send a reply to an existing ticket in Freshdesk."""
+ url = f"{self.base_url}{EndPoints.TICKETS}/{reply.ticket_number}/reply"
+
+ files = prepare_attachments(attachments) if attachments else None
+ multipart_data = reply.to_multipart_data()
+
+ response = self.session.post(url=url, data=multipart_data, files=files)
+ response.raise_for_status()
diff --git a/cg/clients/freshdesk/models.py b/cg/clients/freshdesk/models.py
index 7a0cae7f43..aa0be87ab4 100644
--- a/cg/clients/freshdesk/models.py
+++ b/cg/clients/freshdesk/models.py
@@ -1,6 +1,6 @@
-from datetime import datetime
+from typing import Tuple, Union
-from pydantic import BaseModel
+from pydantic import BaseModel, EmailStr, Field
from cg.clients.freshdesk.constants import Priority, Source, Status
@@ -8,9 +8,10 @@
class TicketCreate(BaseModel):
"""Freshdesk ticket."""
- attachments: list[dict[str, str]] = []
- email: str
+ attachments: list[str | bytes] = Field(default_factory=list)
+ email: EmailStr
email_config_id: int | None = None
+ description: str
name: str
priority: int = Priority.LOW
source: int = Source.EMAIL
@@ -18,19 +19,43 @@ class TicketCreate(BaseModel):
subject: str
tags: list[str] = []
type: str | None = None
+ custom_fields: dict[str, str | int | float | None] = Field(default_factory=dict)
+
+ def to_multipart_data(self) -> list[Tuple[str, str | int | bytes]]:
+ """Custom converter to multipart form data."""
+ multipart_data = []
+
+ for field, value in self.model_dump(exclude_none=True).items():
+ if isinstance(value, list):
+ multipart_data.extend([(f"{field}[]", v) for v in value])
+ elif isinstance(value, dict):
+ multipart_data.extend([(f"{field}[{k}]", v) for k, v in value.items()])
+ else:
+ multipart_data.append((field, value))
+
+ return multipart_data
class TicketResponse(BaseModel):
- """Freshdesk ticket response."""
+ """Response from Freshdesk"""
- attachments: list[dict[str, str]] = []
- created_at: datetime | None = None
- email: str
id: int
- name: str | None = None
- priority: int
- source: int
- status: int
+ description: str
subject: str
- tags: list[str] = []
- type: str | None = None
+ to_emails: list[str] | None = None
+ status: int
+ priority: int
+
+
+class ReplyCreate(BaseModel):
+ """Reply to a ticket."""
+
+ ticket_number: str
+ body: str
+
+ def to_multipart_data(self) -> list[Tuple[str, Union[str, int, bytes]]]:
+ """Custom converter to multipart form data."""
+ multipart_data = [
+ ("body", self.body),
+ ]
+ return multipart_data
diff --git a/cg/clients/freshdesk/utils.py b/cg/clients/freshdesk/utils.py
new file mode 100644
index 0000000000..2e78b23cf9
--- /dev/null
+++ b/cg/clients/freshdesk/utils.py
@@ -0,0 +1,86 @@
+import logging
+from functools import wraps
+from pathlib import Path
+from tempfile import TemporaryDirectory
+
+from pydantic import ValidationError
+from requests import ConnectionError, HTTPError
+from requests.exceptions import MissingSchema
+
+from cg.clients.freshdesk.exceptions import (
+ FreshdeskAPIException,
+ FreshdeskModelException,
+)
+from cg.constants.constants import FileFormat
+from cg.io.controller import WriteFile
+
+LOG = logging.getLogger(__name__)
+
+
+def extract_error_detail(error):
+ """Extract detailed error information from HTTPError."""
+ if error.response is not None:
+ try:
+ return error.response.json()
+ except ValueError:
+ return error.response.text
+ return None
+
+
+def log_and_raise(exception_class, message, error):
+ """Log the error and raise the appropriate exception."""
+ LOG.error(message)
+ raise exception_class(error) from error
+
+
+def handle_client_errors(func):
+ """Decorator to handle and log errors in Freshdesk client methods."""
+
+ @wraps(func)
+ def wrapper(*args, **kwargs):
+ try:
+ return func(*args, **kwargs)
+ except HTTPError as error:
+ error_detail = extract_error_detail(error)
+ log_and_raise(
+ FreshdeskAPIException,
+ f"Failed request to Freshdesk: {error} - Status code: "
+ f"{error.response.status_code if error.response else 'N/A'}, Details: {error_detail}",
+ error,
+ )
+ except (MissingSchema, ConnectionError) as error:
+ log_and_raise(FreshdeskAPIException, f"Request to Freshdesk failed: {error}", error)
+ except ValidationError as error:
+ log_and_raise(
+ FreshdeskModelException, f"Invalid response from Freshdesk: {error}", error
+ )
+ except ValueError as error:
+ log_and_raise(FreshdeskAPIException, f"Operation failed: {error}", error)
+ except Exception as error:
+ log_and_raise(
+ FreshdeskAPIException, f"Unexpected error in Freshdesk client: {error}", error
+ )
+
+ return wrapper
+
+
+def prepare_attachments(attachments: list[Path]) -> list[tuple[str, tuple[str, bytes]]]:
+ """Prepare the attachments for a request."""
+ return [
+ ("attachments[]", (attachment.name, open(attachment, "rb"))) for attachment in attachments
+ ]
+
+
+def create_temp_attachment_file(content: dict, file_name: Path) -> TemporaryDirectory:
+ """Create a file-based attachment."""
+ if content and file_name:
+ directory = TemporaryDirectory()
+ WriteFile.write_file_from_content(
+ content=content,
+ file_format=FileFormat.JSON,
+ file_path=Path(directory.name, "order.json"),
+ )
+ return directory
+ else:
+ LOG.error("Content or file path is None. Cannot create file attachment.")
+ raise ValueError("Both content and file path must be provided and cannot be None")
diff --git a/cg/meta/orders/api.py b/cg/meta/orders/api.py
index 0222d4f971..228a9570e0 100644
--- a/cg/meta/orders/api.py
+++ b/cg/meta/orders/api.py
@@ -10,7 +10,6 @@
import logging
from cg.apps.lims import LimsAPI
-from cg.apps.osticket import OsTicket
from cg.meta.orders.ticket_handler import TicketHandler
from cg.models.orders.order import OrderIn, OrderType
from cg.services.orders.submitters.order_submitter_registry import (
@@ -28,13 +27,13 @@ def __init__(
self,
lims: LimsAPI,
status: Store,
- osticket: OsTicket,
+ ticket_handler: TicketHandler,
submitter_registry: OrderSubmitterRegistry,
):
super().__init__()
self.lims = lims
self.status = status
- self.ticket_handler: TicketHandler = TicketHandler(osticket_api=osticket, status_db=status)
+ self.ticket_handler = ticket_handler
self.submitter_registry = submitter_registry
def submit(self, project: OrderType, order_in: OrderIn, user_name: str, user_mail: str) -> dict:
@@ -45,7 +44,7 @@ def submit(self, project: OrderType, order_in: OrderIn, user_name: str, user_mai
submit_handler = self.submitter_registry.get_order_submitter(project)
submit_handler.order_validation_service.validate_order(order_in)
# detect manual ticket assignment
- ticket_number: str | None = TicketHandler.parse_ticket_number(order_in.name)
+ ticket_number: str | None = self.ticket_handler.parse_ticket_number(order_in.name)
if not ticket_number:
ticket_number = self.ticket_handler.create_ticket(
order=order_in, user_name=user_name, user_mail=user_mail, project=project
@@ -54,7 +53,6 @@ def submit(self, project: OrderType, order_in: OrderIn, user_name: str, user_mai
self.ticket_handler.connect_to_ticket(
order=order_in,
user_name=user_name,
- user_mail=user_mail,
project=project,
ticket_number=ticket_number,
)
diff --git a/cg/meta/orders/ticket_handler.py b/cg/meta/orders/ticket_handler.py
index ba6074068f..91d806fdfa 100644
--- a/cg/meta/orders/ticket_handler.py
+++ b/cg/meta/orders/ticket_handler.py
@@ -4,9 +4,8 @@
from tempfile import TemporaryDirectory
from typing import Any
-from sendmail_container import FormDataRequest
-
-from cg.apps.osticket import OsTicket
+from cg.clients.freshdesk.freshdesk_client import FreshdeskClient
+from cg.clients.freshdesk.models import ReplyCreate, TicketCreate, TicketResponse
from cg.models.orders.order import OrderIn
from cg.models.orders.samples import Of1508Sample
from cg.store.models import Customer, Sample
@@ -20,15 +19,17 @@ class TicketHandler:
NEW_LINE = "
"
- def __init__(self, osticket_api: OsTicket, status_db: Store):
- self.osticket: OsTicket = osticket_api
- self.status_db: Store = status_db
+ def __init__(self, db: Store, client: FreshdeskClient, system_email_id: int, env: str):
+ self.client: FreshdeskClient = client
+ self.status_db: Store = db
+ self.system_email_id: int = system_email_id
+ self.env: str = env
@staticmethod
def parse_ticket_number(name: str) -> str | None:
"""Try to parse a ticket number from a string"""
# detect manual ticket assignment
- ticket_match = re.fullmatch(r"#([0-9]{6})", name)
+ ticket_match = re.fullmatch(r"#(\d{6,10})", name)
if ticket_match:
ticket_id = ticket_match.group(1)
LOG.info(f"{ticket_id}: detected ticket in order name")
@@ -45,22 +46,36 @@ def create_ticket(
order=order,
project=project,
)
- attachment: dict = self.create_attachment(order=order)
- ticket_nr: str | None = self.osticket.open_ticket(
- name=user_name,
- email=user_mail,
- subject=order.name,
- message=message,
- attachment=attachment,
- )
- LOG.info(f"{ticket_nr}: opened new ticket")
- return ticket_nr
+ with TemporaryDirectory() as temp_dir:
+ attachments: Path = self.create_attachment_file(order=order, temp_dir=temp_dir)
+
+ freshdesk_ticket = TicketCreate(
+ email=user_mail,
+ description=message,
+ email_config_id=self.system_email_id,
+ name=user_name,
+ subject=order.name,
+ type="Order",
+ tags=[order.samples[0].data_analysis],
+ custom_fields={
+ "cf_environment": self.env,
+ },
+ attachments=[],
+ )
+ ticket_response: TicketResponse = self.client.create_ticket(
+ ticket=freshdesk_ticket, attachments=[attachments]
+ )
+ LOG.info(f"{ticket_response.id}: opened new ticket in Freshdesk")
- def create_attachment(self, order: OrderIn):
- return self.osticket.create_new_ticket_attachment(
- content=self.replace_empty_string_with_none(obj=order.dict()), file_name="order.json"
- )
+ return ticket_response.id
+
+ def create_attachment_file(self, order: OrderIn, temp_dir: str) -> Path:
+ """Create a single attachment file for the ticket"""
+ order_file_path = Path(temp_dir) / "order.json"
+ with order_file_path.open("w") as order_file:
+ order_file.write(order.json())
+ return order_file_path
def create_xml_sample_list(self, order: OrderIn, user_name: str) -> str:
message = ""
@@ -88,10 +103,7 @@ def create_xml_sample_list(self, order: OrderIn, user_name: str) -> str:
@staticmethod
def create_new_ticket_header(message: str, order: OrderIn, project: str) -> str:
- return (
- f"data:text/html;charset=utf-8, New order with {len(order.samples)} {project} samples:"
- + message
- )
+ return f"New order with {len(order.samples)} {project} samples:" + message
@staticmethod
def add_existing_ticket_header(message: str, order: OrderIn, project: str) -> str:
@@ -178,27 +190,25 @@ def replace_empty_string_with_none(cls, obj: Any) -> Any:
return obj
def connect_to_ticket(
- self, order: OrderIn, user_name: str, user_mail: str, project: str, ticket_number: str
+ self, order: OrderIn, user_name: str, project: str, ticket_number: str
) -> None:
"""Appends a new order message to the ticket selected by the customer"""
LOG.info(f"Connecting order to ticket {ticket_number}")
+
message: str = self.add_existing_ticket_header(
message=self.create_xml_sample_list(order=order, user_name=user_name),
order=order,
project=project,
)
- sender_prefix, email_server_alias = user_mail.split("@")
- temp_dir: TemporaryDirectory = self.osticket.create_connecting_ticket_attachment(
- content=self.replace_empty_string_with_none(obj=order.dict())
- )
- email_form = FormDataRequest(
- sender_prefix=sender_prefix,
- email_server_alias=email_server_alias,
- request_uri=self.osticket.email_uri,
- recipients=self.osticket.osticket_email,
- mail_title=f"[#{ticket_number}]",
- mail_body=message,
- attachments=[Path(f"{temp_dir.name}/order.json")],
- )
- email_form.submit()
- temp_dir.cleanup()
+
+ with TemporaryDirectory() as temp_dir:
+ attachments: Path = self.create_attachment_file(order=order, temp_dir=temp_dir)
+
+ reply = ReplyCreate(ticket_number=ticket_number, body=message)
+
+ self.client.reply_to_ticket(
+ reply=reply,
+ attachments=[attachments],
+ )
+
+ LOG.info(f"Connected order to ticket {ticket_number} in Freshdesk")
diff --git a/cg/server/app.py b/cg/server/app.py
index c9f3cd477c..4df92b1db4 100644
--- a/cg/server/app.py
+++ b/cg/server/app.py
@@ -66,8 +66,6 @@ def _configure_extensions(app: Flask):
ext.db.init_app(app)
ext.lims.init_app(app)
ext.analysis_client.init_app(app)
- if app.config["osticket_api_key"]:
- ext.osticket.init_app(app)
ext.admin.init_app(app, index_view=AdminIndexView(endpoint="admin"))
app.json_provider_class = ext.CustomJSONEncoder
diff --git a/cg/server/app_config.py b/cg/server/app_config.py
index 34e8fe96ba..97f38b3235 100644
--- a/cg/server/app_config.py
+++ b/cg/server/app_config.py
@@ -2,25 +2,29 @@
class AppConfig(BaseSettings):
+ """Default values are overridden by environment variable at run-time."""
+
gunicorn_workers: int = 4
gunicorn_threads: int = 4
- gunicorn_bind: str
+ gunicorn_bind: str = "0.0.0.0:8000"
gunicorn_timeout: int = 400
- cg_sql_database_uri: str
+ cg_sql_database_uri: str = "sqlite:///"
cg_secret_key: str = "thisIsNotASafeKey"
invoice_max_price: int = 750_000
- lims_host: str
- lims_username: str
- lims_password: str
- osticket_api_key: str
- osticket_domain: str
- support_system_email: str
- email_uri: str
- google_oauth_client_id: str
- google_oauth_client_secret: str
- trailblazer_host: str
- trailblazer_service_account: str
- trailblazer_service_account_auth_file: str
+ lims_host: str = "lims_host"
+ lims_username: str = "username"
+ lims_password: str = "password"
+ support_system_email: str = "support@mail.com"
+ email_uri: str = "smtp://localhost"
+ google_oauth_client_id: str = "client_id"
+ google_oauth_client_secret: str = "client_secret"
+ trailblazer_host: str = "trailblazer_host"
+ trailblazer_service_account: str = "service_account"
+ trailblazer_service_account_auth_file: str = "auth_file.json"
+ freshdesk_url: str = "https://company.freshdesk.com"
+ freshdesk_api_key: str = "freshdesk_api_key"
+ freshdesk_order_email_id: int = 10
+ freshdesk_environment: str = "Stage"
app_config = AppConfig()
diff --git a/cg/server/endpoints/orders.py b/cg/server/endpoints/orders.py
index 8261233f47..d2bd64bb15 100644
--- a/cg/server/endpoints/orders.py
+++ b/cg/server/endpoints/orders.py
@@ -28,8 +28,12 @@
from cg.meta.orders.ticket_handler import TicketHandler
from cg.models.orders.order import OrderIn, OrderType
from cg.models.orders.orderform_schema import Orderform
-from cg.server.dto.delivery_message.delivery_message_response import DeliveryMessageResponse
-from cg.server.dto.orders.order_delivery_update_request import OrderDeliveredUpdateRequest
+from cg.server.dto.delivery_message.delivery_message_response import (
+ DeliveryMessageResponse,
+)
+from cg.server.dto.orders.order_delivery_update_request import (
+ OrderDeliveredUpdateRequest,
+)
from cg.server.dto.orders.order_patch_request import OrderDeliveredPatch
from cg.server.dto.orders.orders_request import OrdersRequest
from cg.server.dto.orders.orders_response import Order, OrdersResponse
@@ -39,14 +43,10 @@
delivery_message_service,
lims,
order_service,
- osticket,
order_submitter_registry,
+ ticket_handler,
)
-from cg.store.models import (
- Application,
- Customer,
-)
-
+from cg.store.models import Application, Customer
ORDERS_BLUEPRINT = Blueprint("orders", __name__, url_prefix="/api/v1")
ORDERS_BLUEPRINT.before_request(before_request)
@@ -157,7 +157,10 @@ def create_order_from_form():
def submit_order(order_type):
"""Submit an order for samples."""
api = OrdersAPI(
- lims=lims, status=db, osticket=osticket, submitter_registry=order_submitter_registry
+ lims=lims,
+ status=db,
+ ticket_handler=ticket_handler,
+ submitter_registry=order_submitter_registry,
)
error_message: str
try:
@@ -170,7 +173,7 @@ def submit_order(order_type):
)
project = OrderType(order_type)
order_in = OrderIn.parse_obj(request_json, project=project)
- existing_ticket: str | None = TicketHandler.parse_ticket_number(order_in.name)
+ existing_ticket: str | None = ticket_handler.parse_ticket_number(order_in.name)
if existing_ticket and order_service.store.get_order_by_ticket_id(existing_ticket):
raise OrderExistsError(f"Order with ticket id {existing_ticket} already exists.")
diff --git a/cg/server/ext.py b/cg/server/ext.py
index 3200e7742a..c8cfda6286 100644
--- a/cg/server/ext.py
+++ b/cg/server/ext.py
@@ -6,12 +6,11 @@
from flask_wtf.csrf import CSRFProtect
from cg.apps.lims import LimsAPI
-from cg.apps.osticket import OsTicket
from cg.apps.tb.api import TrailblazerAPI
+from cg.clients.freshdesk.freshdesk_client import FreshdeskClient
+from cg.meta.orders.ticket_handler import TicketHandler
+from cg.server.app_config import app_config
from cg.services.delivery_message.delivery_message_service import DeliveryMessageService
-from cg.services.sample_run_metrics_service.sample_run_metrics_service import (
- SampleRunMetricsService,
-)
from cg.services.orders.order_service.order_service import OrderService
from cg.services.orders.order_summary_service.order_summary_service import (
OrderSummaryService,
@@ -20,6 +19,9 @@
OrderSubmitterRegistry,
setup_order_submitter_registry,
)
+from cg.services.sample_run_metrics_service.sample_run_metrics_service import (
+ SampleRunMetricsService,
+)
from cg.services.sample_service.sample_service import SampleService
from cg.store.database import initialize_database
from cg.store.store import Store
@@ -84,7 +86,6 @@ def init_app(self, app):
admin = Admin(name="Clinical Genomics")
lims = FlaskLims()
-osticket = OsTicket()
analysis_client = AnalysisClient()
delivery_message_service = DeliveryMessageService(store=db, trailblazer_api=analysis_client)
summary_service = OrderSummaryService(store=db, analysis_client=analysis_client)
@@ -95,3 +96,12 @@ def init_app(self, app):
lims=lims,
status_db=db,
)
+freshdesk_client = FreshdeskClient(
+ base_url=app_config.freshdesk_url, api_key=app_config.freshdesk_api_key
+)
+ticket_handler = TicketHandler(
+ db=db,
+ client=freshdesk_client,
+ system_email_id=app_config.freshdesk_order_email_id,
+ env=app_config.freshdesk_environment,
+)
diff --git a/pyproject.toml b/pyproject.toml
index b2e27b3f36..7cfe49704f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
[tool.poetry]
name = "cg"
-version = "62.2.4"
+version = "62.2.5"
description = "Clinical Genomics command center"
authors = ["Clinical Genomics "]
readme = "README.md"
diff --git a/tests/apps/test_osticket.py b/tests/apps/test_osticket.py
deleted file mode 100644
index c16f21b2ca..0000000000
--- a/tests/apps/test_osticket.py
+++ /dev/null
@@ -1,31 +0,0 @@
-""" Test the osticket app """
-
-import logging
-
-import pytest
-
-from cg.apps.osticket import OsTicket
-from cg.exc import TicketCreationError
-
-
-def test_osticket_respone_500(monkeypatch, caplog, response):
- """Test that we log properly when we get a non successful response"""
-
- # GIVEN a ticket server always gives a failure in response
- osticket_api = OsTicket()
- monkeypatch.setattr("requests.post", lambda *args, **kwargs: response)
-
- # WHEN we call open_ticket with ok ticket data
- caplog.set_level(logging.ERROR)
- with pytest.raises(TicketCreationError):
- osticket_api.open_ticket(
- name="dummy_name",
- email="dummy_email",
- subject="dummy_subject",
- message="dummy_message",
- attachment={},
- )
-
- # THEN the response text and reason was logged and a ticket creation error raised
- assert response.reason in caplog.text
- assert response.text in caplog.text
diff --git a/tests/conftest.py b/tests/conftest.py
index 8ca5b26c0f..3f4b3bd4bd 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -75,12 +75,12 @@
from tests.mocks.hk_mock import MockHousekeeperAPI
from tests.mocks.limsmock import LimsSample, LimsUDF, MockLimsAPI
from tests.mocks.madeline import MockMadelineAPI
-from tests.mocks.osticket import MockOsTicket
from tests.mocks.process_mock import ProcessMock
from tests.mocks.scout import MockScoutAPI
from tests.mocks.tb_mock import MockTB
from tests.small_helpers import SmallHelpers
from tests.store_helpers import StoreHelpers
+from cg.clients.freshdesk.freshdesk_client import FreshdeskClient
LOG = logging.getLogger(__name__)
multiqc_json_file = "multiqc_data.json"
@@ -645,11 +645,10 @@ def ticket_id() -> str:
@pytest.fixture
-def osticket(ticket_id: str) -> MockOsTicket:
- """Return a api that mock the os ticket api."""
- api = MockOsTicket()
- api.set_ticket_nr(ticket_id)
- return api
+def freshdesk_client() -> FreshdeskClient:
+ """Return a FreshdeskClient instance with mock parameters."""
+ client = FreshdeskClient(base_url="https://mock.freshdesk.com", api_key="mock_api_key")
+ return client
# Files fixtures
diff --git a/tests/meta/orders/conftest.py b/tests/meta/orders/conftest.py
index 24add743eb..5857e67a8a 100644
--- a/tests/meta/orders/conftest.py
+++ b/tests/meta/orders/conftest.py
@@ -2,6 +2,7 @@
import pytest
+from cg.clients.freshdesk.freshdesk_client import FreshdeskClient
from cg.meta.orders import OrdersAPI
from cg.meta.orders.ticket_handler import TicketHandler
from cg.services.orders.submitters.order_submitter_registry import (
@@ -10,27 +11,31 @@
)
from cg.store.store import Store
from tests.mocks.limsmock import MockLimsAPI
-from tests.mocks.osticket import MockOsTicket
+
+
+@pytest.fixture
+def freshdesk_client():
+ return FreshdeskClient(base_url="https://mock.freshdesk.com", api_key="mock_api_key")
@pytest.fixture(scope="function")
def orders_api(
- base_store,
- osticket: MockOsTicket,
+ base_store: Store,
+ ticket_handler: TicketHandler,
lims_api: MockLimsAPI,
order_submitter_registry: OrderSubmitterRegistry,
) -> OrdersAPI:
return OrdersAPI(
lims=lims_api,
status=base_store,
- osticket=osticket,
+ ticket_handler=ticket_handler,
submitter_registry=order_submitter_registry,
)
@pytest.fixture
-def ticket_handler(store: Store, osticket: MockOsTicket) -> TicketHandler:
- return TicketHandler(status_db=store, osticket_api=osticket)
+def ticket_handler(store: Store, freshdesk_client: FreshdeskClient) -> TicketHandler:
+ return TicketHandler(db=store, client=freshdesk_client, system_email_id=12345, env="production")
@pytest.fixture
diff --git a/tests/meta/orders/test_meta_orders_api.py b/tests/meta/orders/test_meta_orders_api.py
index 885753ef03..04ffe40588 100644
--- a/tests/meta/orders/test_meta_orders_api.py
+++ b/tests/meta/orders/test_meta_orders_api.py
@@ -1,8 +1,9 @@
import datetime as dt
-from unittest.mock import patch, Mock
+from unittest.mock import Mock, patch
import pytest
+from cg.clients.freshdesk.models import TicketResponse
from cg.constants import DataDelivery
from cg.constants.constants import Workflow
from cg.constants.subject import Sex
@@ -27,6 +28,22 @@ def monkeypatch_process_lims(monkeypatch, order_data) -> None:
)
+def mock_freshdesk_ticket_creation(mock_create_ticket, ticket_id: str):
+ """Helper function to mock Freshdesk ticket creation."""
+ mock_create_ticket.return_value = TicketResponse(
+ id=int(ticket_id),
+ description="This is a test description.",
+ subject="Support needed..",
+ status=2,
+ priority=1,
+ )
+
+
+def mock_freshdesk_reply_to_ticket(mock_reply_to_ticket):
+ """Helper function to mock Freshdesk reply to ticket."""
+ mock_reply_to_ticket.return_value = None
+
+
def test_too_long_order_name():
# GIVEN order with more than allowed characters name
long_name = "A super long order name that is longer than sixty-four characters."
@@ -38,7 +55,6 @@ def test_too_long_order_name():
OrderIn(name=long_name, customer="", comment="", samples=[])
-@patch("cg.meta.orders.ticket_handler.FormDataRequest.submit", return_value=None)
@pytest.mark.parametrize(
"order_type",
[
@@ -55,37 +71,44 @@ def test_too_long_order_name():
],
)
def test_submit(
- mail_patch,
all_orders_to_submit: dict,
base_store: Store,
- monkeypatch,
+ monkeypatch: pytest.MonkeyPatch,
order_type: OrderType,
orders_api: OrdersAPI,
ticket_id: str,
user_mail: str,
user_name: str,
):
- order_data = OrderIn.parse_obj(obj=all_orders_to_submit[order_type], project=order_type)
- monkeypatch_process_lims(monkeypatch, order_data)
+ with patch(
+ "cg.clients.freshdesk.freshdesk_client.FreshdeskClient.create_ticket"
+ ) as mock_create_ticket, patch(
+ "cg.clients.freshdesk.freshdesk_client.FreshdeskClient.reply_to_ticket"
+ ) as mock_reply_to_ticket:
+ mock_freshdesk_ticket_creation(mock_create_ticket, ticket_id)
+ mock_freshdesk_reply_to_ticket(mock_reply_to_ticket)
- # GIVEN an order and an empty store
- assert not base_store._get_query(table=Sample).first()
+ order_data = OrderIn.parse_obj(obj=all_orders_to_submit[order_type], project=order_type)
+ monkeypatch_process_lims(monkeypatch, order_data)
- # WHEN submitting the order
+ # GIVEN an order and an empty store
+ assert not base_store._get_query(table=Sample).first()
- result = orders_api.submit(
- project=order_type, order_in=order_data, user_name=user_name, user_mail=user_mail
- )
+ # WHEN submitting the order
+
+ result = orders_api.submit(
+ project=order_type, order_in=order_data, user_name=user_name, user_mail=user_mail
+ )
- # THEN the result should contain the ticket number for the order
- for record in result["records"]:
- if isinstance(record, Pool):
- assert record.ticket == ticket_id
- elif isinstance(record, Sample):
- assert record.original_ticket == ticket_id
- elif isinstance(record, Case):
- for link_obj in record.links:
- assert link_obj.sample.original_ticket == ticket_id
+ # THEN the result should contain the ticket number for the order
+ for record in result["records"]:
+ if isinstance(record, Pool):
+ assert record.ticket == ticket_id
+ elif isinstance(record, Sample):
+ assert record.original_ticket == ticket_id
+ elif isinstance(record, Case):
+ for link_obj in record.links:
+ assert link_obj.sample.original_ticket == ticket_id
@pytest.mark.parametrize(
@@ -99,22 +122,24 @@ def test_submit_ticketexception(
user_mail: str,
user_name: str,
):
- # GIVEN an order that does not have a name (ticket_nr)
- order_data = OrderIn.parse_obj(obj=all_orders_to_submit[order_type], project=order_type)
- order_data.name = "dummy_name"
- with patch("cg.apps.osticket.OsTicket") as os_mock:
- orders_api.ticket_handler.osticket = os_mock.return_value
- orders_api.ticket_handler.osticket.open_ticket.side_effect = TicketCreationError("ERROR")
-
- # WHEN the order is submitted and a TicketCreationError raised
- # THEN the TicketCreationError is not excepted
- with pytest.raises(TicketCreationError):
- orders_api.submit(
- project=order_type,
- order_in=order_data,
- user_name=user_name,
- user_mail=user_mail,
- )
+ # GIVEN a mock Freshdesk ticket creation that raises TicketCreationError
+ with patch(
+ "cg.clients.freshdesk.freshdesk_client.FreshdeskClient.create_ticket",
+ side_effect=TicketCreationError("ERROR"),
+ ):
+ # GIVEN an order that does not have a name (ticket_nr)
+ order_data = OrderIn.parse_obj(obj=all_orders_to_submit[order_type], project=order_type)
+ order_data.name = "dummy_name"
+
+ # WHEN the order is submitted and a TicketCreationError raised
+ # THEN the TicketCreationError is not excepted
+ with pytest.raises(TicketCreationError):
+ orders_api.submit(
+ project=order_type,
+ order_in=order_data,
+ user_name=user_name,
+ user_mail=user_mail,
+ )
@pytest.mark.parametrize(
@@ -123,7 +148,7 @@ def test_submit_ticketexception(
)
def test_submit_illegal_sample_customer(
all_orders_to_submit: dict,
- monkeypatch,
+ monkeypatch: pytest.MonkeyPatch,
order_type: OrderType,
orders_api: OrdersAPI,
sample_store: Store,
@@ -132,7 +157,6 @@ def test_submit_illegal_sample_customer(
):
order_data = OrderIn.parse_obj(obj=all_orders_to_submit[order_type], project=order_type)
monkeypatch_process_lims(monkeypatch, order_data)
- orders_api.ticket_handler = Mock()
# GIVEN we have an order with a customer that is not in the same customer group as customer
# that the samples originate from
new_customer = sample_store.add_customer(
@@ -161,60 +185,65 @@ def test_submit_illegal_sample_customer(
)
-@patch("cg.meta.orders.ticket_handler.FormDataRequest.submit", return_value=None)
@pytest.mark.parametrize(
"order_type",
[OrderType.MIP_DNA, OrderType.MIP_RNA, OrderType.BALSAMIC],
)
def test_submit_scout_legal_sample_customer(
- mail_patch,
all_orders_to_submit: dict,
- monkeypatch,
+ monkeypatch: pytest.MonkeyPatch,
order_type: OrderType,
orders_api: OrdersAPI,
sample_store: Store,
user_mail: str,
user_name: str,
+ ticket_id: str,
):
- order_data = OrderIn.parse_obj(obj=all_orders_to_submit[order_type], project=order_type)
- monkeypatch_process_lims(monkeypatch, order_data)
- orders_api.ticket_handler = Mock()
- # GIVEN we have an order with a customer that is in the same customer group as customer
- # that the samples originate from
- collaboration = sample_store.add_collaboration("customer999only", "customer 999 only group")
- sample_store.session.add(collaboration)
- sample_customer = sample_store.add_customer(
- "customer1",
- "customer 1",
- scout_access=True,
- invoice_address="dummy street 1",
- invoice_reference="dummy nr",
- )
- order_customer = sample_store.add_customer(
- "customer2",
- "customer 2",
- scout_access=True,
- invoice_address="dummy street 2",
- invoice_reference="dummy nr",
- )
- sample_customer.collaborations.append(collaboration)
- order_customer.collaborations.append(collaboration)
- sample_store.session.add(sample_customer)
- sample_store.session.add(order_customer)
- existing_sample: Sample = sample_store._get_query(table=Sample).first()
- existing_sample.customer = sample_customer
- sample_store.session.commit()
- order_data.customer = order_customer.internal_id
-
- for sample in order_data.samples:
- sample.internal_id = existing_sample.internal_id
- break
-
- # WHEN calling submit
- # THEN an OrderError should not be raised on illegal customer
- orders_api.submit(
- project=order_type, order_in=order_data, user_name=user_name, user_mail=user_mail
- )
+ with patch(
+ "cg.clients.freshdesk.freshdesk_client.FreshdeskClient.create_ticket"
+ ) as mock_create_ticket, patch(
+ "cg.clients.freshdesk.freshdesk_client.FreshdeskClient.reply_to_ticket"
+ ) as mock_reply_to_ticket:
+ mock_freshdesk_ticket_creation(mock_create_ticket, ticket_id)
+ mock_freshdesk_reply_to_ticket(mock_reply_to_ticket)
+ order_data = OrderIn.parse_obj(obj=all_orders_to_submit[order_type], project=order_type)
+ monkeypatch_process_lims(monkeypatch, order_data)
+ # GIVEN we have an order with a customer that is in the same customer group as customer
+ # that the samples originate from
+ collaboration = sample_store.add_collaboration("customer999only", "customer 999 only group")
+ sample_store.session.add(collaboration)
+ sample_customer = sample_store.add_customer(
+ "customer1",
+ "customer 1",
+ scout_access=True,
+ invoice_address="dummy street 1",
+ invoice_reference="dummy nr",
+ )
+ order_customer = sample_store.add_customer(
+ "customer2",
+ "customer 2",
+ scout_access=True,
+ invoice_address="dummy street 2",
+ invoice_reference="dummy nr",
+ )
+ sample_customer.collaborations.append(collaboration)
+ order_customer.collaborations.append(collaboration)
+ sample_store.session.add(sample_customer)
+ sample_store.session.add(order_customer)
+ existing_sample: Sample = sample_store._get_query(table=Sample).first()
+ existing_sample.customer = sample_customer
+ sample_store.session.commit()
+ order_data.customer = order_customer.internal_id
+
+ for sample in order_data.samples:
+ sample.internal_id = existing_sample.internal_id
+ break
+
+ # WHEN calling submit
+ # THEN an OrderError should not be raised on illegal customer
+ orders_api.submit(
+ project=order_type, order_in=order_data, user_name=user_name, user_mail=user_mail
+ )
@pytest.mark.parametrize(
@@ -223,7 +252,7 @@ def test_submit_scout_legal_sample_customer(
)
def test_submit_duplicate_sample_case_name(
all_orders_to_submit: dict,
- monkeypatch,
+ monkeypatch: pytest.MonkeyPatch,
order_type: OrderType,
orders_api: OrdersAPI,
ticket_id: str,
@@ -234,7 +263,6 @@ def test_submit_duplicate_sample_case_name(
order_data = OrderIn.parse_obj(obj=all_orders_to_submit[order_type], project=order_type)
store = orders_api.status
customer: Customer = store.get_customer_by_internal_id(customer_internal_id=order_data.customer)
- orders_api.ticket_handler = Mock()
for sample in order_data.samples:
case_id = sample.family_name
if not store.get_case_by_name_and_customer(customer=customer, case_name=case_id):
@@ -262,72 +290,85 @@ def test_submit_duplicate_sample_case_name(
)
-@patch("cg.meta.orders.ticket_handler.FormDataRequest.submit", return_value=None)
@pytest.mark.parametrize(
"order_type",
[OrderType.FLUFFY],
)
def test_submit_fluffy_duplicate_sample_case_name(
- mail_patch,
all_orders_to_submit: dict,
- monkeypatch,
+ monkeypatch: pytest.MonkeyPatch,
order_type: OrderType,
orders_api: OrdersAPI,
user_mail: str,
user_name: str,
+ ticket_id: str,
):
- # GIVEN we have an order with a case that is already in the database
- order_data = OrderIn.parse_obj(obj=all_orders_to_submit[order_type], project=order_type)
- monkeypatch_process_lims(monkeypatch, order_data)
-
- orders_api.submit(
- project=order_type, order_in=order_data, user_name=user_name, user_mail=user_mail
- )
+ with patch(
+ "cg.clients.freshdesk.freshdesk_client.FreshdeskClient.create_ticket"
+ ) as mock_create_ticket, patch(
+ "cg.clients.freshdesk.freshdesk_client.FreshdeskClient.reply_to_ticket"
+ ) as mock_reply_to_ticket:
+ mock_freshdesk_ticket_creation(mock_create_ticket, ticket_id)
+ mock_freshdesk_reply_to_ticket(mock_reply_to_ticket)
+ # GIVEN we have an order with a case that is already in the database
+ order_data = OrderIn.parse_obj(obj=all_orders_to_submit[order_type], project=order_type)
+ monkeypatch_process_lims(monkeypatch, order_data)
- # WHEN calling submit
- # THEN an OrderError should be raised on duplicate case name
- with pytest.raises(OrderError):
orders_api.submit(
- project=order_type,
- order_in=order_data,
- user_name=user_name,
- user_mail=user_mail,
+ project=order_type, order_in=order_data, user_name=user_name, user_mail=user_mail
)
+ # WHEN calling submit
+ # THEN an OrderError should be raised on duplicate case name
+ with pytest.raises(OrderError):
+ orders_api.submit(
+ project=order_type,
+ order_in=order_data,
+ user_name=user_name,
+ user_mail=user_mail,
+ )
+
-@patch("cg.meta.orders.ticket_handler.FormDataRequest.submit", return_value=None)
def test_submit_unique_sample_case_name(
- mail_patch,
orders_api: OrdersAPI,
mip_order_to_submit: dict,
user_name: str,
user_mail: str,
- monkeypatch,
+ monkeypatch: pytest.MonkeyPatch,
+ ticket_id: str,
):
- # GIVEN we have an order with a case that is not existing in the database
- order_data = OrderIn.parse_obj(obj=mip_order_to_submit, project=OrderType.MIP_DNA)
+ with patch(
+ "cg.clients.freshdesk.freshdesk_client.FreshdeskClient.create_ticket"
+ ) as mock_create_ticket, patch(
+ "cg.clients.freshdesk.freshdesk_client.FreshdeskClient.reply_to_ticket"
+ ) as mock_reply_to_ticket:
+ mock_freshdesk_ticket_creation(mock_create_ticket, ticket_id)
+ mock_freshdesk_reply_to_ticket(mock_reply_to_ticket)
+
+ # GIVEN we have an order with a case that is not existing in the database
+ order_data = OrderIn.parse_obj(obj=mip_order_to_submit, project=OrderType.MIP_DNA)
+
+ store = orders_api.status
+
+ sample: MipDnaSample
+ for sample in order_data.samples:
+ case_id = sample.family_name
+ customer: Customer = store.get_customer_by_internal_id(
+ customer_internal_id=order_data.customer
+ )
+ assert not store.get_case_by_name_and_customer(customer=customer, case_name=case_id)
- store = orders_api.status
+ monkeypatch_process_lims(monkeypatch, order_data)
- sample: MipDnaSample
- for sample in order_data.samples:
- case_id = sample.family_name
- customer: Customer = store.get_customer_by_internal_id(
- customer_internal_id=order_data.customer
+ # WHEN calling submit
+ orders_api.submit(
+ project=OrderType.MIP_DNA,
+ order_in=order_data,
+ user_name=user_name,
+ user_mail=user_mail,
)
- assert not store.get_case_by_name_and_customer(customer=customer, case_name=case_id)
-
- monkeypatch_process_lims(monkeypatch, order_data)
- # WHEN calling submit
- orders_api.submit(
- project=OrderType.MIP_DNA,
- order_in=order_data,
- user_name=user_name,
- user_mail=user_mail,
- )
-
- # Then no exception about duplicate names should be thrown
+ # Then no exception about duplicate names should be thrown
def test_validate_sex_inconsistent_sex(
@@ -454,7 +495,6 @@ def test_validate_sex_unknown_new_sex(
# THEN no OrderError should be raised on non-matching sex
-@patch("cg.meta.orders.ticket_handler.FormDataRequest.submit", return_value=None)
@pytest.mark.parametrize(
"order_type",
[
@@ -470,27 +510,34 @@ def test_validate_sex_unknown_new_sex(
],
)
def test_submit_unique_sample_name(
- mail_patch,
all_orders_to_submit: dict,
- monkeypatch,
+ monkeypatch: pytest.MonkeyPatch,
order_type: OrderType,
orders_api: OrdersAPI,
user_mail: str,
user_name: str,
+ ticket_id: str,
):
- # GIVEN we have an order with a sample that is not existing in the database
- order_data = OrderIn.parse_obj(obj=all_orders_to_submit[order_type], project=order_type)
- store = orders_api.status
- assert not store._get_query(table=Sample).first()
-
- monkeypatch_process_lims(monkeypatch, order_data)
-
- # WHEN calling submit
- orders_api.submit(
- project=order_type, order_in=order_data, user_name=user_name, user_mail=user_mail
- )
+ with patch(
+ "cg.clients.freshdesk.freshdesk_client.FreshdeskClient.create_ticket"
+ ) as mock_create_ticket, patch(
+ "cg.clients.freshdesk.freshdesk_client.FreshdeskClient.reply_to_ticket"
+ ) as mock_reply_to_ticket:
+ mock_freshdesk_ticket_creation(mock_create_ticket, ticket_id)
+ mock_freshdesk_reply_to_ticket(mock_reply_to_ticket)
+ # GIVEN we have an order with a sample that is not existing in the database
+ order_data = OrderIn.parse_obj(obj=all_orders_to_submit[order_type], project=order_type)
+ store = orders_api.status
+ assert not store._get_query(table=Sample).first()
+
+ monkeypatch_process_lims(monkeypatch, order_data)
+
+ # WHEN calling submit
+ orders_api.submit(
+ project=order_type, order_in=order_data, user_name=user_name, user_mail=user_mail
+ )
- # Then no exception about duplicate names should be thrown
+ # Then no exception about duplicate names should be thrown
@pytest.mark.parametrize(
@@ -500,7 +547,7 @@ def test_submit_unique_sample_name(
def test_sarscov2_submit_duplicate_sample_name(
all_orders_to_submit: dict,
helpers: StoreHelpers,
- monkeypatch,
+ monkeypatch: pytest.MonkeyPatch,
order_type: OrderType,
orders_api: OrdersAPI,
user_mail: str,
@@ -536,7 +583,6 @@ def store_samples_with_names_from_order(store: Store, helpers: StoreHelpers, ord
store.session.commit()
-@patch("cg.meta.orders.ticket_handler.FormDataRequest.submit", return_value=None)
@pytest.mark.parametrize(
"order_type",
[
@@ -549,24 +595,30 @@ def store_samples_with_names_from_order(store: Store, helpers: StoreHelpers, ord
],
)
def test_not_sarscov2_submit_duplicate_sample_name(
- mail_patch,
all_orders_to_submit: dict,
helpers: StoreHelpers,
- monkeypatch,
+ monkeypatch: pytest.MonkeyPatch,
order_type: OrderType,
orders_api: OrdersAPI,
- sample_store: Store,
user_mail: str,
user_name: str,
+ ticket_id: str,
):
- # GIVEN we have an order with samples that is already in the database
- order_data = OrderIn.parse_obj(obj=all_orders_to_submit[order_type], project=order_type)
- monkeypatch_process_lims(monkeypatch, order_data)
- store_samples_with_names_from_order(orders_api.status, helpers, order_data)
-
- # WHEN calling submit
- orders_api.submit(
- project=order_type, order_in=order_data, user_name=user_name, user_mail=user_mail
- )
+ with patch(
+ "cg.clients.freshdesk.freshdesk_client.FreshdeskClient.create_ticket"
+ ) as mock_create_ticket, patch(
+ "cg.clients.freshdesk.freshdesk_client.FreshdeskClient.reply_to_ticket"
+ ) as mock_reply_to_ticket:
+ mock_freshdesk_ticket_creation(mock_create_ticket, ticket_id)
+ mock_freshdesk_reply_to_ticket(mock_reply_to_ticket)
+ # GIVEN we have an order with samples that is already in the database
+ order_data = OrderIn.parse_obj(obj=all_orders_to_submit[order_type], project=order_type)
+ monkeypatch_process_lims(monkeypatch, order_data)
+ store_samples_with_names_from_order(orders_api.status, helpers, order_data)
+
+ # WHEN calling submit
+ orders_api.submit(
+ project=order_type, order_in=order_data, user_name=user_name, user_mail=user_mail
+ )
- # THEN no OrderError should be raised on duplicate sample name
+ # THEN no OrderError should be raised on duplicate sample name
diff --git a/tests/mocks/osticket.py b/tests/mocks/osticket.py
deleted file mode 100644
index 7ed223fc44..0000000000
--- a/tests/mocks/osticket.py
+++ /dev/null
@@ -1,54 +0,0 @@
-"""Mock the os ticket api"""
-
-import logging
-import os.path
-
-from flask import Flask
-
-from cg.apps.osticket import OsTicket
-from cg.exc import TicketCreationError
-
-LOG = logging.getLogger(__name__)
-
-
-class MockOsTicket(OsTicket):
- """Interface to ticket system."""
-
- def __init__(self):
- self.headers = None
- self.url = None
- self.osticket_email = "james.holden@scilifelab.se"
- self.mail_container_uri = "dummy_uri"
- self._ticket_nr: str = "123456"
- self._should_fail: bool = False
- self._return_none: bool = False
- self.email_uri = "http://localhost:0000/sendmail"
-
- def set_ticket_nr(self, ticket_id: str) -> None:
- self._ticket_nr = ticket_id
-
- def init_app(self, app: Flask):
- """Initialize the API in Flask."""
-
- def setup(
- self,
- api_key: str = None,
- domain: str = None,
- osticket_email: str = None,
- email_uri: str = None,
- ):
- """Initialize the API."""
- self.headers = {"X-API-Key": api_key}
- self.url = os.path.join(domain, "api/tickets.json")
-
- def open_ticket(
- self, attachment: dict, email: str, message: str, name: str, subject: str
- ) -> str | None:
- """Open a new ticket through the REST API."""
- if self._should_fail:
- LOG.error("res.text: %s, reason: %s", self._ticket_nr, "Unknown reason")
- raise TicketCreationError("FAIL")
- if self._return_none:
- return None
-
- return self._ticket_nr
diff --git a/tests/server/conftest.py b/tests/server/conftest.py
index b0f1e107a4..2c1290e47b 100644
--- a/tests/server/conftest.py
+++ b/tests/server/conftest.py
@@ -28,9 +28,6 @@
os.environ["TRAILBLAZER_HOST"] = "dummy_value"
os.environ["CG_SECRET_KEY"] = "dummy_value"
os.environ["GUNICORN_BIND"] = "0.0.0.0:8000"
-os.environ["OSTICKET_API_KEY"] = "dummy_value"
-os.environ["OSTICKET_API_URL"] = "dummy_value"
-os.environ["OSTICKET_DOMAIN"] = "dummy_value"
os.environ["SUPPORT_SYSTEM_EMAIL"] = "dummy_value"
os.environ["EMAIL_URI"] = "dummy_value"