Skip to content

Commit c76fea4

Browse files
authored
Merge pull request #243 from ral-facilities/release/v1.1.0
Release `v1.1.0`
2 parents 3bfd570 + 6c96847 commit c76fea4

File tree

19 files changed

+293
-273
lines changed

19 files changed

+293
-273
lines changed

.github/dependabot.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,4 @@ updates:
6060
groups:
6161
pip:
6262
patterns:
63-
- "*"
63+
- "*"

.github/workflows/.ci.yml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ jobs:
2121
runs-on: ubuntu-latest
2222
steps:
2323
- name: Checkout repository
24-
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
24+
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
2525

2626
- name: Set up Python
27-
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
27+
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
2828
with:
29-
python-version: "3.12"
29+
python-version: "3.13"
3030
cache: "pip"
3131

3232
- name: Install dependencies
@@ -46,7 +46,7 @@ jobs:
4646
runs-on: ubuntu-latest
4747
steps:
4848
- name: Checkout repository
49-
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
49+
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
5050

5151
- name: Create logging configuration file
5252
run: cp logging.example.ini logging.ini
@@ -73,7 +73,7 @@ jobs:
7373
runs-on: ubuntu-latest
7474
steps:
7575
- name: Checkout repository
76-
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
76+
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
7777

7878
- name: Create logging configuration file
7979
run: cp logging.example.ini logging.ini
@@ -115,17 +115,17 @@ jobs:
115115
PUSH_DOCKER_IMAGE_TO_HARBOR: ${{ inputs.push-docker-image-to-harbor != null && inputs.push-docker-image-to-harbor || 'false' }}
116116
steps:
117117
- name: Check out repo
118-
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
118+
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
119119

120120
- name: Extract metadata (tags, labels) for Docker
121121
id: meta
122-
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
122+
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
123123
with:
124124
images: ${{ vars.HARBOR_URL }}/object-storage-api
125125

126126
- name: Login to Harbor
127127
if: ${{ fromJSON(env.PUSH_DOCKER_IMAGE_TO_HARBOR) }}
128-
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
128+
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
129129
with:
130130
registry: ${{ vars.HARBOR_URL }}
131131
username: ${{ secrets.HARBOR_USERNAME }}

.github/workflows/release-build.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,18 @@ jobs:
1313
runs-on: ubuntu-latest
1414
steps:
1515
- name: Check out repo
16-
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
16+
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
1717

1818
- name: Login to Harbor
19-
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
19+
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
2020
with:
2121
registry: ${{ vars.HARBOR_URL }}
2222
username: ${{ secrets.HARBOR_USERNAME }}
2323
password: ${{ secrets.HARBOR_TOKEN }}
2424

2525
- name: Extract metadata (tags, labels) for Docker
2626
id: meta
27-
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
27+
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
2828
with:
2929
images: ${{ vars.HARBOR_URL }}/object-storage-api
3030
tags: |

.pylintrc

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,11 @@ ignore-paths=
5959
# Emacs file locks
6060
ignore-patterns=^\.#
6161

62-
# List of module names for which member attributes should not be checked
63-
# (useful for modules/projects where namespaces are manipulated during runtime
64-
# and thus existing member attributes cannot be deduced by static analysis). It
65-
# supports qualified module names, as well as Unix pattern matching.
62+
# List of module names for which member attributes should not be checked and
63+
# will not be imported (useful for modules/projects where namespaces are
64+
# manipulated during runtime and thus existing member attributes cannot be
65+
# deduced by static analysis). It supports qualified module names, as well as
66+
# Unix pattern matching.
6667
ignored-modules=
6768

6869
# Python code to execute, usually for sys.path manipulation such as
@@ -86,9 +87,13 @@ load-plugins=
8687
# Pickle collected data for later comparisons.
8788
persistent=yes
8889

90+
# Resolve imports to .pyi stubs if available. May reduce no-member messages and
91+
# increase not-an-iterable messages.
92+
prefer-stubs=no
93+
8994
# Minimum Python version to use for version dependent checks. Will default to
9095
# the version used to run pylint.
91-
py-version=3.12
96+
py-version=3.13
9297

9398
# Discover python modules and packages in the file system subtree.
9499
recursive=no
@@ -302,6 +307,9 @@ max-locals=15
302307
# Maximum number of parents for a class (see R0901).
303308
max-parents=7
304309

310+
# Maximum number of positional arguments for function / method.
311+
max-positional-arguments=5
312+
305313
# Maximum number of public methods for a class (see R0904).
306314
max-public-methods=20
307315

@@ -436,7 +444,7 @@ disable=raw-checker-failed,
436444
# either give multiple identifier separated by comma (,) or put this option
437445
# multiple time (only on the command line, not in the configuration file where
438446
# it should appear only once). See also the "--disable" option for examples.
439-
enable=c-extension-no-member
447+
enable=
440448

441449

442450
[METHOD_ARGS]
@@ -468,6 +476,11 @@ max-nested-blocks=5
468476
# printed.
469477
never-returning-functions=sys.exit,argparse.parse_error
470478

479+
# Let 'consider-using-join' be raised when the separator to join on would be
480+
# non-empty (resulting in expected fixes of the type: ``"- " + " -
481+
# ".join(items)``)
482+
suggest-join-with-non-empty-separator=yes
483+
471484

472485
[REPORTS]
473486

@@ -482,10 +495,10 @@ evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor
482495
# used to format the message information. See doc for all details.
483496
msg-template=
484497

485-
# Set the output format. Available formats are: text, parseable, colorized,
486-
# json2 (improved json format), json (old json format) and msvs (visual
487-
# studio). You can also give a reporter class, e.g.
488-
# mypackage.mymodule.MyReporterClass.
498+
# Set the output format. Available formats are: 'text', 'parseable',
499+
# 'colorized', 'json2' (improved json format), 'json' (old json format), msvs
500+
# (visual studio) and 'github' (GitHub actions). You can also give a reporter
501+
# class, e.g. mypackage.mymodule.MyReporterClass.
489502
#output-format=
490503

491504
# Tells whether to display a full report or only the messages.
@@ -587,7 +600,7 @@ ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace
587600
# of finding the hint is based on edit distance.
588601
missing-member-hint=yes
589602

590-
# The minimum edit distance a name should have in order to be considered a
603+
# The maximum edit distance a name should have in order to be considered a
591604
# similar match for a missing member name.
592605
missing-member-hint-distance=1
593606

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM python:3.12.11-alpine3.22@sha256:9b8808206f4a956130546a32cbdd8633bc973b19db2923b7298e6f90cc26db08 AS base
1+
FROM python:3.13.10-alpine3.23@sha256:65fe04ddc51a8ccbf14ecb882903251e4a124914673001b03c393eb65dd9502a AS base
22

33
WORKDIR /app
44

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ This microservice requires a MongoDB and S3 object storage instance to run again
1010
### Prerequisites
1111

1212
- Docker and Docker Compose installed (if you want to run the microservice inside Docker)
13-
- Python 3.12, MongoDB 7.0 and MinIO installed on your machine (if you are not using Docker)
13+
- Python 3.13, MongoDB 7.0 and MinIO installed on your machine (if you are not using Docker)
1414
- Public key (must be OpenSSH encoded) to decode JWT access tokens (if JWT authentication/authorization is enabled)
1515
- [MongoDB Compass](https://www.mongodb.com/products/compass) installed (if you want to interact with the database using
1616
a GUI)

codecov.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ coverage:
99
target: 75
1010
ignore:
1111
- "test/"
12+
- "object_storage_api/v1/*" # ignore routers as they are tested in the e2e tests
13+
- "object_storage_api/main.py" # ignore main.py as contains router and is tested in the e2e tests

docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ services:
4343
command: minio server /data
4444
volumes:
4545
- ./minio/data:/data
46+
restart: always
4647
ports:
4748
- 9000:9000
4849
- 9001:9001

object_storage_api/core/exceptions.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,21 +45,21 @@ class DatabaseError(BaseAPIException):
4545
class InvalidObjectIdError(DatabaseError):
4646
"""The provided value is not a valid ObjectId."""
4747

48-
status_code = status.HTTP_422_UNPROCESSABLE_ENTITY
48+
status_code = status.HTTP_422_UNPROCESSABLE_CONTENT
4949
response_detail = "Invalid ID given"
5050

5151

5252
class InvalidImageFileError(BaseAPIException):
5353
"""The provided image file is not valid."""
5454

55-
status_code = status.HTTP_422_UNPROCESSABLE_ENTITY
55+
status_code = status.HTTP_422_UNPROCESSABLE_CONTENT
5656
response_detail = "File given is not a valid image"
5757

5858

5959
class FileTypeMismatchException(BaseAPIException):
6060
"""The extension and content type of the provided file do not match."""
6161

62-
status_code = status.HTTP_422_UNPROCESSABLE_ENTITY
62+
status_code = status.HTTP_422_UNPROCESSABLE_CONTENT
6363
response_detail = "File extension and content type do not match"
6464

6565

@@ -73,7 +73,7 @@ class UnsupportedFileExtensionException(BaseAPIException):
7373
class UploadLimitReachedError(BaseAPIException):
7474
"""The limit for the maximum number of attachments or images for the provided `entity_id` has been reached."""
7575

76-
status_code = status.HTTP_422_UNPROCESSABLE_ENTITY
76+
status_code = status.HTTP_422_UNPROCESSABLE_CONTENT
7777

7878
def __init__(self, detail: str, entity_type: str):
7979
"""

object_storage_api/repositories/attachment.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def get(self, attachment_id: str, session: Optional[ClientSession] = None) -> Op
6161
:raises InvalidObjectIdError: If the supplied `attachment_id` is invalid.
6262
"""
6363

64-
logger.info("Retrieving attachment with ID: %s from the database", attachment_id)
64+
logger.info("Retrieving attachment with ID '%s' from the database", attachment_id)
6565

6666
try:
6767
attachment_id = CustomObjectId(attachment_id)
@@ -73,7 +73,7 @@ def get(self, attachment_id: str, session: Optional[ClientSession] = None) -> Op
7373

7474
if attachment:
7575
return AttachmentOut(**attachment)
76-
raise MissingRecordError(detail=f"No attachment found with ID: {attachment_id}", entity_type="attachment")
76+
raise MissingRecordError(detail=f"No attachment found with ID '{attachment_id}'", entity_type="attachment")
7777

7878
def list(self, entity_id: Optional[str], session: Optional[ClientSession] = None) -> list[AttachmentOut]:
7979
"""
@@ -128,7 +128,7 @@ def update(self, attachment_id: str, attachment: AttachmentIn, session: ClientSe
128128
exc.response_detail = "Attachment not found"
129129
raise exc
130130

131-
logger.info("Updating attachment metadata with ID: %s", attachment_id)
131+
logger.info("Updating attachment metadata with ID '%s'", attachment_id)
132132
try:
133133
self._attachments_collection.update_one(
134134
{"_id": attachment_id}, {"$set": attachment.model_dump(by_alias=True)}, session=session
@@ -149,7 +149,7 @@ def delete(self, attachment_id: str, session: Optional[ClientSession] = None) ->
149149
:raises MissingRecordError: If the supplied `attachment_id` is non-existent.
150150
:raises InvalidObjectIdError: If the supplied `attachment_id` is invalid.
151151
"""
152-
logger.info("Deleting attachment with ID: %s from the database", attachment_id)
152+
logger.info("Deleting attachment with ID '%s' from the database", attachment_id)
153153
try:
154154
attachment_id = CustomObjectId(attachment_id)
155155
except InvalidObjectIdError as exc:
@@ -158,7 +158,7 @@ def delete(self, attachment_id: str, session: Optional[ClientSession] = None) ->
158158
raise exc
159159
response = self._attachments_collection.delete_one(filter={"_id": attachment_id}, session=session)
160160
if response.deleted_count == 0:
161-
raise MissingRecordError(f"No attachment found with ID: {attachment_id}", entity_type="attachment")
161+
raise MissingRecordError(f"No attachment found with ID '{attachment_id}'", entity_type="attachment")
162162

163163
def delete_by_entity_id(self, entity_id: str, session: Optional[ClientSession] = None) -> None:
164164
"""
@@ -167,7 +167,7 @@ def delete_by_entity_id(self, entity_id: str, session: Optional[ClientSession] =
167167
:param entity_id: The entity ID of the attachments to delete.
168168
:param session: PyMongo ClientSession to use for database operations.
169169
"""
170-
logger.info("Deleting attachments with entity ID: %s from the database", entity_id)
170+
logger.info("Deleting attachments with entity ID '%s' from the database", entity_id)
171171
try:
172172
entity_id = CustomObjectId(entity_id)
173173
# Given it is deleting multiple, we are not raising an exception if no attachments were found to be deleted
@@ -184,7 +184,7 @@ def count_by_entity_id(self, entity_id: str, session: Optional[ClientSession] =
184184
:param entity_id: The entity ID to use to select which documents to count.
185185
:param session: PyMongo ClientSession to use for database operations.
186186
"""
187-
logger.info("Counting number of attachments with entity ID: %s in the database", str(entity_id))
187+
logger.info("Counting number of attachments with entity ID '%s' in the database", str(entity_id))
188188
return self._attachments_collection.count_documents(
189189
filter={"entity_id": CustomObjectId(entity_id)}, session=session
190190
)

0 commit comments

Comments
 (0)