Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion e2e_config.test.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,11 @@
"notifications.batch.attachment.id": "ATT-7183-1965-7758",
"notifications.batch.id": "MST-3638-2460-4825",
"notifications.category.id": "NTC-6157-0397",
"notifications.contact.id": "CTT-0001-9158",
"notifications.message.id": "MSG-0000-6215-1019-0139",
"notifications.subscriber.id": "NTS-0829-7123-7123",
"integration.extension.id": "EXT-6587-4477",
"integration.term.id": "ETC-6587-4477-0062",
"program.program.id": "PRG-9643-3741",
"notifications.contact.id": "CTT-0001-9158"
"program.document.file.id": "PDM-9643-3741-0001"
}
6 changes: 6 additions & 0 deletions mpt_api_client/resources/program/mixins/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
from mpt_api_client.resources.program.mixins.document_mixin import (
AsyncDocumentMixin,
DocumentMixin,
)
from mpt_api_client.resources.program.mixins.publishable_mixin import (
AsyncPublishableMixin,
PublishableMixin,
)

__all__ = [ # noqa: WPS410
"AsyncDocumentMixin",
"AsyncPublishableMixin",
"DocumentMixin",
"PublishableMixin",
]
26 changes: 26 additions & 0 deletions mpt_api_client/resources/program/mixins/document_mixin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from mpt_api_client.http.mixins import (
AsyncCreateFileMixin,
AsyncDownloadFileMixin,
CreateFileMixin,
DownloadFileMixin,
)
from mpt_api_client.resources.program.mixins.publishable_mixin import (
AsyncPublishableMixin,
PublishableMixin,
)


class DocumentMixin[Model](
CreateFileMixin[Model],
DownloadFileMixin[Model],
PublishableMixin[Model],
):
"""Document mixin."""


class AsyncDocumentMixin[Model](
AsyncCreateFileMixin[Model],
AsyncDownloadFileMixin[Model],
AsyncPublishableMixin[Model],
):
"""Async document mixin."""
16 changes: 16 additions & 0 deletions mpt_api_client/resources/program/programs.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
AsyncPublishableMixin,
PublishableMixin,
)
from mpt_api_client.resources.program.programs_documents import (
AsyncDocumentService,
DocumentService,
)


class Program(Model):
Expand Down Expand Up @@ -71,6 +75,12 @@ def update_settings(self, program_id: str, settings: ResourceData) -> Program:
"""
return self._resource(program_id).put("settings", json=settings)

def documents(self, program_id: str) -> DocumentService:
"""Return program documents service."""
return DocumentService(
http_client=self.http_client, endpoint_params={"program_id": program_id}
)


class AsyncProgramsService(
AsyncGetMixin[Program],
Expand All @@ -92,3 +102,9 @@ async def update_settings(self, program_id: str, settings: ResourceData) -> Prog
settings: Settings data to be updated
"""
return await self._resource(program_id).put("settings", json=settings)

def documents(self, program_id: str) -> AsyncDocumentService:
"""Return async program documents service."""
return AsyncDocumentService(
http_client=self.http_client, endpoint_params={"program_id": program_id}
)
68 changes: 68 additions & 0 deletions mpt_api_client/resources/program/programs_documents.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from mpt_api_client.http import AsyncService, Service
from mpt_api_client.http.mixins import (
AsyncCollectionMixin,
AsyncModifiableResourceMixin,
CollectionMixin,
ModifiableResourceMixin,
)
from mpt_api_client.models import Model
from mpt_api_client.models.model import BaseModel
from mpt_api_client.resources.program.mixins import AsyncDocumentMixin, DocumentMixin


class Document(Model):
"""Document resource.

Attributes:
name: Document name.
type: Document type.
description: Document description.
status: Document status.
filename: Original file name.
size: File size in bytes.
content_type: MIME content type of the document.
url: URL to access the document.
program: Reference to the program.
audit: Audit information (created, updated events).
"""

name: str | None
type: str | None
description: str | None
status: str | None
filename: str | None
size: int | None
content_type: str | None
url: str | None
program: BaseModel | None
audit: BaseModel | None


class DocumentServiceConfig:
"""Document service configuration."""

_endpoint = "/public/v1/program/programs/{program_id}/documents"
_model_class = Document
_collection_key = "data"
_upload_file_key = "file"
_upload_data_key = "document"


class DocumentService(
DocumentMixin[Document],
ModifiableResourceMixin[Document],
CollectionMixin[Document],
Service[Document],
DocumentServiceConfig,
):
"""Program documents service."""


class AsyncDocumentService(
AsyncDocumentMixin[Document],
AsyncModifiableResourceMixin[Document],
AsyncCollectionMixin[Document],
AsyncService[Document],
DocumentServiceConfig,
):
"""Async program documents service."""
38 changes: 38 additions & 0 deletions tests/e2e/program/program/document/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import pytest


@pytest.fixture
def document_id(e2e_config):
return e2e_config["program.document.file.id"]


@pytest.fixture
def invalid_document_id(e2e_config):
return "PDM-0000-0000-0000"


@pytest.fixture
def document_data_factory():
def factory(
document_type: str = "File",
):
return {
"name": "E2E Created Program Document",
"description": "E2E Created Program Document",
"type": document_type,
"language": "en-us",
"url": "",
"documentType": document_type,
}

return factory


@pytest.fixture
def vendor_document_service(mpt_vendor, program_id):
return mpt_vendor.program.programs.documents(program_id)


@pytest.fixture
def async_vendor_document_service(async_mpt_vendor, program_id):
return async_mpt_vendor.program.programs.documents(program_id)
102 changes: 102 additions & 0 deletions tests/e2e/program/program/document/test_async_document.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import pytest

from mpt_api_client.exceptions import MPTAPIError
from mpt_api_client.rql.query_builder import RQLQuery

pytestmark = [pytest.mark.flaky]


@pytest.fixture
async def created_document_from_file(async_vendor_document_service, document_data_factory, pdf_fd):
document_data = document_data_factory(document_type="File")
document = await async_vendor_document_service.create(document_data, pdf_fd)
yield document, document_data
try:
await async_vendor_document_service.delete(document.id)
except MPTAPIError as error:
print(f"TEARDOWN - Unable to delete document {document.id}: {error.title}") # noqa: WPS421


@pytest.fixture
async def created_document_from_url(async_vendor_document_service, document_data_factory, pdf_url):
document_data = document_data_factory(document_type="Online")
document_data["url"] = pdf_url
document = await async_vendor_document_service.create(document_data)
yield document, document_data
try:
await async_vendor_document_service.delete(document.id)
except MPTAPIError as error:
print(f"TEARDOWN - Unable to delete document {document.id}: {error.title}") # noqa: WPS421


def test_create_document(created_document_from_file): # noqa: AAA01
result, document_data = created_document_from_file
assert result.name == document_data["name"]
assert result.description == document_data["description"]


def test_create_document_from_url(created_document_from_url): # noqa: AAA01
result, document_data = created_document_from_url
assert result.name == document_data["name"]
assert result.description == document_data["description"]


async def test_update_document(async_vendor_document_service, created_document_from_file):
update_data = {"name": "Updated e2e test document"}
document, _ = created_document_from_file

result = await async_vendor_document_service.update(document.id, update_data)

assert result.name == update_data["name"]


async def test_get_document(async_vendor_document_service, document_id):
result = await async_vendor_document_service.get(document_id)

assert result.id == document_id


async def test_download_document(async_vendor_document_service, document_id):
result = await async_vendor_document_service.download(document_id)

assert result.file_contents is not None
assert result.filename == "empty.pdf"


async def test_iterate_documents(async_vendor_document_service, document_id):
documents = [doc async for doc in async_vendor_document_service.iterate()]

result = any(doc.id == document_id for doc in documents)

assert result is True


async def test_filter_documents(async_vendor_document_service, created_document_from_file):
document, _ = created_document_from_file
filtered_service = async_vendor_document_service.filter(RQLQuery(id=document.id))

result = [doc async for doc in filtered_service.iterate()]

assert len(result) == 1
assert result[0].id == document.id


async def test_not_found_document(async_vendor_document_service, invalid_document_id):
with pytest.raises(MPTAPIError):
await async_vendor_document_service.get(invalid_document_id)


async def test_publish_document(async_vendor_document_service, created_document_from_file):
document, _ = created_document_from_file
result = await async_vendor_document_service.publish(document.id)

assert result.status == "Published"


async def test_unpublish_document(async_vendor_document_service, created_document_from_file):
document, _ = created_document_from_file
await async_vendor_document_service.publish(document.id)

result = await async_vendor_document_service.unpublish(document.id)

assert result.status == "Unpublished"
Loading