Skip to content

Commit

Permalink
IFRS: Do idempotence in core (GSI-1091) (#61)
Browse files Browse the repository at this point in the history
* IFRS: Remove idempotence handler layer

* IFRS: Update tests (remove outbox tests)

* IFRS: Bump version number from 2.0.3 -> 2.1.0

* IFRS: log FileNotInRegistryError instead of raise

* IFRS: Revert to outbox subscriber

* IFRS: Update the delete method doc strings

* IFRS: Log delete-type events as errors, not warnings
  • Loading branch information
TheByronHimes authored Nov 19, 2024
1 parent d50a6d6 commit aee5cc3
Show file tree
Hide file tree
Showing 17 changed files with 155 additions and 743 deletions.
2 changes: 1 addition & 1 deletion services/ifrs/.readme_generation/description.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ This service provides functionality to administer files stored in an S3-compatib
object storage.
All file-related metadata is stored in an internal mongodb database, owned and controlled
by this service.
It exposes no REST API enpoints and communicates with other services via events.
It exposes no REST API endpoints and communicates with other services via events.

### Events consumed:

Expand Down
8 changes: 4 additions & 4 deletions services/ifrs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This service provides functionality to administer files stored in an S3-compatib
object storage.
All file-related metadata is stored in an internal mongodb database, owned and controlled
by this service.
It exposes no REST API enpoints and communicates with other services via events.
It exposes no REST API endpoints and communicates with other services via events.

### Events consumed:

Expand Down Expand Up @@ -36,21 +36,21 @@ We recommend using the provided Docker container.

A pre-build version is available at [docker hub](https://hub.docker.com/repository/docker/ghga/internal-file-registry-service):
```bash
docker pull ghga/internal-file-registry-service:2.0.4
docker pull ghga/internal-file-registry-service:2.1.0
```

Or you can build the container yourself from the [`./Dockerfile`](./Dockerfile):
```bash
# Execute in the repo's root dir:
docker build -t ghga/internal-file-registry-service:2.0.4 .
docker build -t ghga/internal-file-registry-service:2.1.0 .
```

For production-ready deployment, we recommend using Kubernetes, however,
for simple use cases, you could execute the service using docker
on a single server:
```bash
# The entrypoint is preconfigured:
docker run -p 8080:8080 ghga/internal-file-registry-service:2.0.4 --help
docker run -p 8080:8080 ghga/internal-file-registry-service:2.1.0 --help
```

If you prefer not to use containers, you may install the service from source:
Expand Down
2 changes: 1 addition & 1 deletion services/ifrs/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "ifrs"
version = "2.0.4"
version = "2.1.0"
description = "Internal File Registry Service - This service acts as a registry for the internal location and representation of files."
readme = "README.md"
authors = [
Expand Down
63 changes: 40 additions & 23 deletions services/ifrs/src/ifrs/adapters/inbound/event_sub.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
from pydantic import Field
from pydantic_settings import BaseSettings

from ifrs.ports.inbound.idempotent import IdempotenceHandlerPort
from ifrs.core.models import FileMetadataBase
from ifrs.ports.inbound.file_registry import FileRegistryPort

log = logging.getLogger(__name__)

Expand All @@ -48,7 +49,7 @@ class OutboxSubTranslatorConfig(BaseSettings):
)


class NonstagedFileRequestedListener(
class NonstagedFileRequestedTranslator(
DaoSubscriberProtocol[event_schemas.NonStagedFileRequested]
):
"""A class that consumes NonStagedFileRequested events."""
Expand All @@ -60,28 +61,31 @@ def __init__(
self,
*,
config: OutboxSubTranslatorConfig,
idempotence_handler: IdempotenceHandlerPort,
file_registry: FileRegistryPort,
):
self._idempotence_handler = idempotence_handler
self._file_registry = file_registry
self.event_topic = config.files_to_stage_topic

async def changed(
self, resource_id: str, update: event_schemas.NonStagedFileRequested
) -> None:
"""Consume change event (created or updated) for download request data."""
await self._idempotence_handler.upsert_nonstaged_file_requested(
resource_id=resource_id, update=update
await self._file_registry.stage_registered_file(
file_id=resource_id,
decrypted_sha256=update.decrypted_sha256,
outbox_object_id=update.target_object_id,
outbox_bucket_id=update.target_bucket_id,
)

async def deleted(self, resource_id: str) -> None:
"""Consume event indicating the deletion of a NonStagedFileRequested event."""
log.warning(
"""This should never be called because these events are stateless and not saved."""
log.error(
"Received DELETED-type event for NonStagedFileRequested with resource ID '%s'",
resource_id,
)


class FileDeletionRequestedListener(
class FileDeletionRequestedTranslator(
DaoSubscriberProtocol[event_schemas.FileDeletionRequested]
):
"""A class that consumes FileDeletionRequested events."""
Expand All @@ -93,28 +97,26 @@ def __init__(
self,
*,
config: OutboxSubTranslatorConfig,
idempotence_handler: IdempotenceHandlerPort,
file_registry: FileRegistryPort,
):
self._idempotence_handler = idempotence_handler
self._file_registry = file_registry
self.event_topic = config.files_to_delete_topic

async def changed(
self, resource_id: str, update: event_schemas.FileDeletionRequested
) -> None:
"""Consume change event (created or updated) for File Deletion Requests."""
await self._idempotence_handler.upsert_file_deletion_requested(
resource_id=resource_id, update=update
)
await self._file_registry.delete_file(file_id=resource_id)

async def deleted(self, resource_id: str) -> None:
"""Consume event indicating the deletion of a File Deletion Request."""
log.warning(
"""This should never be called because these events are stateless and not saved."""
log.error(
"Received DELETED-type event for FileDeletionRequested with resource ID '%s'",
resource_id,
)


class FileValidationSuccessListener(
class FileValidationSuccessTranslator(
DaoSubscriberProtocol[event_schemas.FileUploadValidationSuccess]
):
"""A class that consumes FileUploadValidationSuccess events."""
Expand All @@ -126,22 +128,37 @@ def __init__(
self,
*,
config: OutboxSubTranslatorConfig,
idempotence_handler: IdempotenceHandlerPort,
file_registry: FileRegistryPort,
):
self._idempotence_handler = idempotence_handler
self._file_registry = file_registry
self.event_topic = config.files_to_register_topic

async def changed(
self, resource_id: str, update: event_schemas.FileUploadValidationSuccess
) -> None:
"""Consume change event (created or updated) for FileUploadValidationSuccess events."""
await self._idempotence_handler.upsert_file_upload_validation_success(
resource_id=resource_id, update=update
file_without_object_id = FileMetadataBase(
file_id=resource_id,
decrypted_sha256=update.decrypted_sha256,
decrypted_size=update.decrypted_size,
upload_date=update.upload_date,
decryption_secret_id=update.decryption_secret_id,
encrypted_part_size=update.encrypted_part_size,
encrypted_parts_md5=update.encrypted_parts_md5,
encrypted_parts_sha256=update.encrypted_parts_sha256,
content_offset=update.content_offset,
storage_alias=update.s3_endpoint_alias,
)

await self._file_registry.register_file(
file_without_object_id=file_without_object_id,
staging_object_id=update.object_id,
staging_bucket_id=update.bucket_id,
)

async def deleted(self, resource_id: str) -> None:
"""Consume event indicating the deletion of a FileUploadValidationSuccess events."""
log.warning(
"""This should never be called because these events are stateless and not saved."""
log.error(
"Received DELETED-type event for FileUploadValidationSuccess with resource ID '%s'",
resource_id,
)
167 changes: 0 additions & 167 deletions services/ifrs/src/ifrs/adapters/inbound/idempotent.py

This file was deleted.

Loading

0 comments on commit aee5cc3

Please sign in to comment.