Skip to content

Commit

Permalink
Merge pull request #16 from meero-com/chore/add-license
Browse files Browse the repository at this point in the history
License the project and correct wording
  • Loading branch information
tbobm authored Apr 22, 2024
2 parents 4780d20 + a3fb8cb commit 3928993
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 26 deletions.
22 changes: 22 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
MIT License

Copyright (c) 2024 Meero

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Media Archiver
# Archiver

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

[![Quality analysis](https://github.com/meero-com/archiver/actions/workflows/quality.yaml/badge.svg)](https://github.com/meero-com/archiver/actions/workflows/quality.yaml) [![CICD](https://github.com/meero-com/archiver/actions/workflows/ci.yaml/badge.svg)](https://github.com/meero-com/archiver/actions/workflows/ci.yaml)

Expand All @@ -7,7 +9,7 @@ Create a zip archive of several S3 files and upload it to an S3 bucket.
## Features

- Retrieve payloads from an SQS Queue.
- Create a Zip archive from a list of media.
- Create a Zip archive from a list of files.
- Optionally create a directory structure inside the zip.
- Upload the zip file to an S3 bucket.
- Notify a consumer once the archive is ready to be downloaded.
Expand Down Expand Up @@ -48,7 +50,7 @@ Below is a table with each required and optional environment variables:
| `SQS_DESTINATION_QUEUE` | SQS Queue to send message to downstream consumer. | `None` |
| `SQS_SOURCE_QUEUE` | SQS Queue to retrieve payloads from. | `None` |
| `S3_DESTINATION_BUCKET` | Target S3 bucket to store the archive files. | `None` |
| `S3_SOURCE_BUCKET` | Source S3 bucket that contains the media used to create the archive by default. | `None` |
| `S3_SOURCE_BUCKET` | Source S3 bucket that contains the files used to create the archive by default. | `None` |
| `S3_FILE_PREFIX` | The prefix that will be added to each archive created. | `download` |
| `APP_DEBUG` | Enable debug log level. | `0` |
| `DEV_MODE` | Use console rendering for logs instead of JSON rendering. | `0` |
Expand All @@ -59,7 +61,7 @@ This project is intended to be deployed as an ECS Service inside AWS.
It requires the following permissions:

- Interact with an SQS Queue.
- Read from the source S3 bucket (media files).
- Read from the source S3 bucket (S3 objects).
- Write to the destination S3 bucket (zip archives).

It should be able to scale based on the SQS Queue's number of message.
Expand Down
6 changes: 3 additions & 3 deletions src/archiver/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
Each processed message will trigger the following processing:
- Create an in-memory file-like object based ZipFile.
- Download each media and write it to the archive.
- Download each file and write it to the archive.
- Upload the archive to the S3_DESTINATION_BUCKET.
- Send SQS message to downstream consumers.
"""
Expand Down Expand Up @@ -40,7 +40,7 @@
process_file,
generate_s3_source_infos_from_payload
)
from archiver.payload import MediaSpec, PayloadSpec
from archiver.payload import FileSpec, PayloadSpec
from archiver.logs import bootstrap_logging


Expand Down Expand Up @@ -91,7 +91,7 @@ def process_archive(s3_client, sqs_client, sqs_response, logger):
)
archive_s3_path = generate_s3_destination_path(datetime.now())

source_files = [MediaSpec(**item) for item in content['files']]
source_files = [FileSpec(**item) for item in content['files']]

logger.info(
"processing message for archive %s",
Expand Down
6 changes: 3 additions & 3 deletions src/archiver/payload.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
from pydantic import BaseModel


class MediaSpec(BaseModel):
"""Media definition spec.
class FileSpec(BaseModel):
"""File definition spec.
The `source` attribute represents an S3 prefix or a full s3 path.
The optional `destination` attribute sets the directory and
file name in the created Zip archive.
Expand All @@ -17,7 +17,7 @@ class MediaSpec(BaseModel):

class PayloadSpec(BaseModel):
"""Archiver payload spec."""
files: List[MediaSpec]
files: List[FileSpec]
metadata: Optional[Dict[str, Any]] = None


Expand Down
12 changes: 6 additions & 6 deletions src/archiver/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import re

from archiver.config import S3_SOURCE_BUCKET, S3_FILE_PREFIX
from archiver.payload import MediaSpec
from archiver.payload import FileSpec


def process_file(s3_client, zip_file, file, s3_bucket, s3_key):
Expand All @@ -29,19 +29,19 @@ def process_file(s3_client, zip_file, file, s3_bucket, s3_key):
return data


def _forge_zip_path(file: MediaSpec):
def _forge_zip_path(file: FileSpec):
"""Return the path of the file used inside the Zip archive.
This function covers the following scenarios:
- `destination` has no value: path is `source`;
- `destination` has no extension: put `source` in `destination` directory;
- `destination` has an extension: `destination` is file location.
>>> _forge_zip_path(MediaSpec("abc/def/file.jpg", None))
>>> _forge_zip_path(FileSpec("abc/def/file.jpg", None))
'file.jpg'
>>> _forge_zip_path(MediaSpec("file.jpg", "directory"))
>>> _forge_zip_path(FileSpec("file.jpg", "directory"))
'directory/file.jpg'
>>> _forge_zip_path(MediaSpec("file.jpg", "directory/pretty name.jpg"))
>>> _forge_zip_path(FileSpec("file.jpg", "directory/pretty name.jpg"))
'directory/pretty name.jpg'
"""
_, filename = path.split(file.source)
Expand Down Expand Up @@ -81,7 +81,7 @@ def generate_s3_destination_path(creation_date: datetime) -> str:
archive_path = f"{digest}/{file_prefix}-{filename}.zip"
return archive_path

def generate_s3_source_infos_from_payload(file: MediaSpec):
def generate_s3_source_infos_from_payload(file: FileSpec):
"""Extract s3 source informations from the payload
The source can be just an s3 key in the S3_SOURCE_BUCKET or a
Expand Down
20 changes: 10 additions & 10 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
)
def test_forge_zip_path(source, destination, expected_path):
import archiver.utils
from archiver.payload import MediaSpec
media_spec = MediaSpec(source=source, destination=destination)
assert archiver.utils._forge_zip_path(media_spec) == expected_path
from archiver.payload import FileSpec
file_spec = FileSpec(source=source, destination=destination)
assert archiver.utils._forge_zip_path(file_spec) == expected_path


def test_generate_destination_path():
Expand All @@ -36,13 +36,13 @@ def test_generate_destination_path():
)
def test_generate_s3_source_with_no_s3_uri(source, destination):
from archiver.utils import generate_s3_source_infos_from_payload
from archiver.payload import MediaSpec
from archiver.payload import FileSpec
from archiver.config import S3_SOURCE_BUCKET

media_spec = MediaSpec(source=source, destination=destination)
s3_bucket, s3_key = generate_s3_source_infos_from_payload(media_spec)
file_spec = FileSpec(source=source, destination=destination)
s3_bucket, s3_key = generate_s3_source_infos_from_payload(file_spec)
assert s3_bucket == S3_SOURCE_BUCKET
assert s3_key == media_spec.source
assert s3_key == file_spec.source

@pytest.mark.parametrize(
'source,destination',
Expand All @@ -52,10 +52,10 @@ def test_generate_s3_source_with_no_s3_uri(source, destination):
)
def test_generate_s3_source_with_s3_uri(source, destination):
from archiver.utils import generate_s3_source_infos_from_payload
from archiver.payload import MediaSpec
from archiver.payload import FileSpec
from archiver.config import S3_SOURCE_BUCKET

media_spec = MediaSpec(source=source, destination=destination)
s3_bucket, s3_key = generate_s3_source_infos_from_payload(media_spec)
file_spec = FileSpec(source=source, destination=destination)
s3_bucket, s3_key = generate_s3_source_infos_from_payload(file_spec)
assert s3_bucket == "bucket_name"
assert s3_key == "abc/def/file.jpg"

0 comments on commit 3928993

Please sign in to comment.