Skip to content

Commit 0d5843d

Browse files
committed
feat(comment-file): file upload and delete and get content
1 parent 923442d commit 0d5843d

File tree

26 files changed

+824
-13
lines changed

26 files changed

+824
-13
lines changed

invenio_requests/alembic/1763728177_create_request_files_table.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,9 @@ def upgrade():
7373
["object_version_id"],
7474
unique=False,
7575
)
76-
# TODO: Is ix_request_files_record_id needed? (We do not have it in Zenodo prod for communities)
7776
op.create_index(
7877
op.f("ix_request_files_record_id"), "request_files", ["record_id"], unique=False
7978
)
80-
# TODO: Create an index like this similarly to what is done for communities with uidx_communities_files_id_key?
81-
# op.create_index(
82-
# "uidx_request_files_id_key", "request_files", ["id", "key"], unique=True
83-
# )
84-
# TODO: Is uidx_request_files_record_id_key needed? (We do not have it in Zenodo prod for communities, but instead have uidx_request_files_id_key)
8579
op.create_index(
8680
"uidx_request_files_record_id_key",
8781
"request_files",
@@ -118,7 +112,6 @@ def downgrade():
118112
)
119113
op.drop_column("request_metadata", "bucket_id")
120114
op.drop_index("uidx_request_files_record_id_key", table_name="request_files")
121-
# op.drop_index(op.f("uidx_request_files_id_key"), table_name="request_files")
122115
op.drop_index(op.f("ix_request_files_record_id"), table_name="request_files")
123116
op.drop_index(
124117
op.f("ix_request_files_object_version_id"), table_name="request_files"

invenio_requests/config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,6 @@
143143
This limits the size of indexed documents when comments have many replies.
144144
Additional replies can be loaded via pagination.
145145
"""
146+
147+
REQUESTS_FILES_DEFAULT_QUOTA_SIZE = 100 * 10**6 # 100MB
148+
REQUESTS_FILES_DEFAULT_MAX_FILE_SIZE = 10 * 10**6 # 10MB

invenio_requests/customizations/request_types.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ class RequestType:
114114
topic_can_be_none = True
115115
"""Determines if the ``topic`` reference accepts ``None``."""
116116

117+
files_can_be_none = False
118+
"""Determines if the ``files`` reference accepts ``None``."""
119+
117120
allowed_creator_ref_types = ["user"]
118121
"""A list of allowed TYPE keys for ``created_by`` reference dicts."""
119122

@@ -143,6 +146,9 @@ def locking_enabled(cls):
143146
resolve_topic_needs = False
144147
"""Whether to resolve needs for the topic entity."""
145148

149+
allowed_files_ref_types = ["enabled"]
150+
"""A list of allowed TYPE keys for ``files`` reference dicts."""
151+
146152
payload_schema = None
147153
payload_schema_cls = None
148154
"""Schema for supported payload fields.
@@ -205,6 +211,13 @@ def _create_marshmallow_schema(cls):
205211
RefBaseSchema.create_from_dict(cls.allowed_topic_ref_types),
206212
allow_none=cls.topic_can_be_none,
207213
),
214+
"files": ma.fields.Nested(
215+
RefBaseSchema.create_from_dict(
216+
cls.allowed_files_ref_types,
217+
special_fields={"enabled": ma.fields.Boolean()},
218+
),
219+
allow_none=cls.files_can_be_none,
220+
),
208221
}
209222

210223
if cls.reviewers_can_be_none():

invenio_requests/ext.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,16 @@
1919
from .resources import (
2020
RequestCommentsResource,
2121
RequestCommentsResourceConfig,
22+
RequestFilesResource,
23+
RequestFilesResourceConfig,
2224
RequestsResource,
2325
RequestsResourceConfig,
2426
)
2527
from .services import (
2628
RequestEventsService,
2729
RequestEventsServiceConfig,
30+
RequestFilesService,
31+
RequestFilesServiceConfig,
2832
RequestsService,
2933
RequestsServiceConfig,
3034
UserModerationRequestService,
@@ -38,7 +42,8 @@ def __init__(self, app=None):
3842
"""Extension initialization."""
3943
self.requests_service = None
4044
self.requests_resource = None
41-
self.request_comments_service = None
45+
self.request_events_service = None
46+
self.request_files_service = None
4247
self._schema_cache = {}
4348
self._events_schema_cache = {}
4449
if app:
@@ -64,6 +69,7 @@ def service_configs(self, app):
6469
class ServiceConfigs:
6570
requests = RequestsServiceConfig.build(app)
6671
request_events = RequestEventsServiceConfig.build(app)
72+
request_files = RequestFilesServiceConfig.build(app)
6773

6874
return ServiceConfigs
6975

@@ -77,6 +83,9 @@ def init_services(self, app):
7783
self.request_events_service = RequestEventsService(
7884
config=service_configs.request_events,
7985
)
86+
self.request_files_service = RequestFilesService(
87+
config=service_configs.request_files,
88+
)
8089
self.user_moderation_requests_service = UserModerationRequestService(
8190
requests_service=self.requests_service,
8291
)
@@ -93,6 +102,11 @@ def init_resources(self, app):
93102
config=RequestCommentsResourceConfig,
94103
)
95104

105+
self.request_files_resource = RequestFilesResource(
106+
service=self.request_files_service,
107+
config=RequestFilesResourceConfig,
108+
)
109+
96110
def init_registry(self, app):
97111
"""Initialize the registry for Requests per type."""
98112
self.request_type_registry = TypeRegistry(
@@ -148,9 +162,11 @@ def init(app):
148162
requests_ext = app.extensions["invenio-requests"]
149163
requests_service = requests_ext.requests_service
150164
events_service = requests_ext.request_events_service
165+
files_service = requests_ext.request_files_service
151166

152167
svc_reg.register(requests_service)
153168
svc_reg.register(events_service)
169+
svc_reg.register(files_service)
154170

155171
idx_reg.register(requests_service.indexer, indexer_id="requests")
156172
idx_reg.register(events_service.indexer, indexer_id="events")

invenio_requests/records/api.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,19 @@
1111
from enum import Enum
1212
from functools import partial
1313

14+
from flask import current_app
1415
from invenio_records.dumpers import SearchDumper
1516
from invenio_records.systemfields import ConstantField, DictField, ModelField
16-
from invenio_records_resources.records.api import Record
17-
from invenio_records_resources.records.systemfields import IndexField
18-
from werkzeug.utils import cached_property
17+
from invenio_records_resources.records.api import FileRecord, Record
18+
from invenio_records_resources.records.systemfields import FilesField, IndexField
1919

2020
from ..customizations import RequestState as State
2121
from .dumpers import (
2222
CalculatedFieldDumperExt,
2323
GrantTokensDumperExt,
2424
ParentChildDumperExt,
2525
)
26-
from .models import RequestEventModel, RequestMetadata
26+
from .models import RequestEventModel, RequestFileMetadata, RequestMetadata
2727
from .systemfields import (
2828
EntityReferenceField,
2929
EventTypeField,
@@ -109,6 +109,22 @@ def pre_commit(self):
109109
super().pre_commit()
110110

111111

112+
def get_files_quota(record=None):
113+
"""Get bucket quota configuration for request files."""
114+
# Returns quota_size and max_file_size from Flask config
115+
# with defaults (100MB total quota, 10MB max file size)
116+
return dict(
117+
quota_size=current_app.config.get("REQUESTS_FILES_DEFAULT_QUOTA_SIZE"),
118+
max_file_size=current_app.config.get("REQUESTS_FILES_DEFAULT_MAX_FILE_SIZE"),
119+
)
120+
121+
122+
class RequestFile(FileRecord):
123+
"""Request file API."""
124+
125+
model_cls = RequestFileMetadata
126+
127+
112128
class Request(Record):
113129
"""A generic request record."""
114130

@@ -183,3 +199,16 @@ class Request(Record):
183199

184200
is_locked = DictField("is_locked")
185201
"""Whether or not the request is locked."""
202+
203+
bucket_id = ModelField(dump=False)
204+
bucket = ModelField(dump=False)
205+
206+
# Files NOT dumped or stored in JSON - internal only
207+
files = FilesField(
208+
store=False, # Don't serialize to request JSON
209+
dump=False, # Don't include in dumps()
210+
file_cls=RequestFile,
211+
delete=False, # Manual management via service
212+
create=False, # Lazy initialization
213+
bucket_args=get_files_quota, # Quota config
214+
)

invenio_requests/records/jsonschemas/requests/request-v1.0.0.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,17 @@
4545
"items": {
4646
"$ref": "local://definitions-v1.0.0.json#/entity_reference"
4747
}
48+
},
49+
"files": {
50+
"type": "object",
51+
"description": "Files associated with the record",
52+
"additionalProperties": false,
53+
"properties": {
54+
"enabled": {
55+
"type": "boolean",
56+
"description": "Set to false for metadata only records."
57+
}
58+
}
4859
}
4960
}
5061
}

invenio_requests/records/mappings/os-v1/requests/request-v1.0.0.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,14 @@
149149
},
150150
"is_locked": {
151151
"type": "boolean"
152+
},
153+
"files": {
154+
"type": "object",
155+
"properties": {
156+
"enabled": {
157+
"type": "boolean"
158+
}
159+
}
152160
}
153161
}
154162
}

invenio_requests/records/mappings/os-v2/requests/request-v1.0.0.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,14 @@
151151
},
152152
"is_locked": {
153153
"type": "boolean"
154+
},
155+
"files": {
156+
"type": "object",
157+
"properties": {
158+
"enabled": {
159+
"type": "boolean"
160+
}
161+
}
154162
}
155163
}
156164
}

invenio_requests/records/mappings/v7/requests/request-v1.0.0.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,14 @@
151151
},
152152
"is_locked": {
153153
"type": "boolean"
154+
},
155+
"files": {
156+
"type": "object",
157+
"properties": {
158+
"enabled": {
159+
"type": "boolean"
160+
}
161+
}
154162
}
155163
}
156164
}

invenio_requests/resources/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@
1111
"""Resources module."""
1212

1313
from .events import RequestCommentsResource, RequestCommentsResourceConfig
14+
from .files import RequestFilesResource, RequestFilesResourceConfig
1415
from .requests import RequestsResource, RequestsResourceConfig
1516

1617
__all__ = (
1718
"RequestsResource",
1819
"RequestsResourceConfig",
1920
"RequestCommentsResource",
2021
"RequestCommentsResourceConfig",
22+
"RequestFilesResource",
23+
"RequestFilesResourceConfig",
2124
)

0 commit comments

Comments
 (0)