Skip to content

Commit b566ea8

Browse files
committed
Merge remote-tracking branch 'upstream/develop' into feature/azure-blob-storage
2 parents 664ec76 + 38bfb2a commit b566ea8

File tree

289 files changed

+3037
-12300
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

289 files changed

+3037
-12300
lines changed

.github/workflows/test-build.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ env:
1111
ELASTICSEARCH6_ARCHIVE: elasticsearch-6.3.1.tar.gz
1212
OSF_DB_PORT: 5432
1313
OSF_DB_PASSWORD: postgres
14+
GITHUB_ACTIONS: true
1415

1516
jobs:
1617
build-cache:
@@ -167,3 +168,36 @@ jobs:
167168
- name: Upload report
168169
if: (success() || failure()) # run this step even if previous step failed
169170
uses: ./.github/actions/gen-report
171+
172+
mailhog:
173+
runs-on: ubuntu-22.04
174+
permissions:
175+
checks: write
176+
needs: build-cache
177+
services:
178+
postgres:
179+
image: postgres
180+
181+
env:
182+
POSTGRES_PASSWORD: ${{ env.OSF_DB_PASSWORD }}
183+
options: >-
184+
--health-cmd pg_isready
185+
--health-interval 10s
186+
--health-timeout 5s
187+
--health-retries 5
188+
ports:
189+
# Maps tcp port 5432 on service container to the host
190+
- 5432:5432
191+
mailhog:
192+
image: mailhog/mailhog
193+
ports:
194+
- 1025:1025
195+
- 8025:8025
196+
steps:
197+
- uses: actions/checkout@v2
198+
- uses: ./.github/actions/start-build
199+
- name: Run tests
200+
run: poetry run python3 -m invoke test-ci-mailhog -n 1 --junit
201+
- name: Upload report
202+
if: (github.event_name != 'pull_request') && (success() || failure()) # run this step even if previous step failed
203+
uses: ./.github/actions/gen-report

CHANGELOG

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
We follow the CalVer (https://calver.org/) versioning scheme: YY.MINOR.MICRO.
44

5+
25.15.0 (2025-08-29)
6+
====================
7+
8+
- Notification Refactor project phase 1 release
9+
- Removed OSF Groups
10+
- Removed notifications for OSF Meetings
11+
- Removed notifications for OSF Comments
12+
- Removed OSF Quickfiles code
13+
- Added mailhog integration to improve email tests
14+
515
25.13.0 (2025-08-08)
616
====================
717

README-docker-compose.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -271,10 +271,8 @@
271271
docker compose run --rm web python3 -m scripts.parse_citation_styles
272272
```
273273
- Start ember_osf_web
274-
- Needed for quickfiles feature:
275-
```bash
276-
docker compose up -d ember_osf_web
277-
```
274+
- Needed for ember app:
275+
- `docker-compose up -d ember_osf_web`
278276
- OPTIONAL: Register OAuth Scopes
279277
- Needed for things such as the ember-osf dummy app
280278
```bash

addons/base/views.py

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
from addons.base import exceptions as addon_errors
2323
from addons.base.models import BaseStorageAddon
24-
from addons.osfstorage.models import OsfStorageFile
2524
from addons.osfstorage.models import OsfStorageFileNode
2625
from addons.osfstorage.utils import enqueue_update_analytics
2726

@@ -34,7 +33,6 @@
3433
from framework.exceptions import HTTPError
3534
from framework.flask import redirect
3635
from framework.sentry import log_exception
37-
from framework.routing import proxy_url
3836
from framework.transactions.handlers import no_auto_transaction
3937
from website import mails
4038
from website import settings
@@ -483,7 +481,7 @@ def _construct_payload(auth, resource, credentials, waterbutler_settings):
483481

484482
@must_be_signed
485483
@no_auto_transaction
486-
@must_be_valid_project(quickfiles_valid=True, preprints_valid=True)
484+
@must_be_valid_project(preprints_valid=True)
487485
def create_waterbutler_log(payload, **kwargs):
488486
with transaction.atomic():
489487
try:
@@ -603,7 +601,7 @@ def create_waterbutler_log(payload, **kwargs):
603601
metadata = payload.get('metadata') or payload.get('destination')
604602

605603
target_node = AbstractNode.load(metadata.get('nid'))
606-
if target_node and not target_node.is_quickfiles and payload['action'] != 'download_file':
604+
if target_node and payload['action'] != 'download_file':
607605
update_storage_usage_with_size(payload)
608606

609607
with transaction.atomic():
@@ -1006,13 +1004,17 @@ def persistent_file_download(auth, **kwargs):
10061004
file = BaseFileNode.active.filter(_id=id_or_guid).first()
10071005
if not file:
10081006
guid = Guid.load(id_or_guid)
1009-
if guid:
1010-
file = guid.referent
1011-
else:
1007+
if not guid:
10121008
raise HTTPError(http_status.HTTP_404_NOT_FOUND, data={
10131009
'message_short': 'File Not Found',
10141010
'message_long': 'The requested file could not be found.'
10151011
})
1012+
1013+
file = guid.referent
1014+
if type(file) is Preprint:
1015+
referent, _ = Guid.load_referent(id_or_guid)
1016+
file = referent.primary_file
1017+
10161018
if not file.is_file:
10171019
raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={
10181020
'message_long': 'Downloading folders is not permitted.'
@@ -1032,16 +1034,6 @@ def persistent_file_download(auth, **kwargs):
10321034
)
10331035

10341036

1035-
def addon_view_or_download_quickfile(**kwargs):
1036-
fid = kwargs.get('fid', 'NOT_AN_FID')
1037-
file_ = OsfStorageFile.load(fid)
1038-
if not file_:
1039-
raise HTTPError(http_status.HTTP_404_NOT_FOUND, data={
1040-
'message_short': 'File Not Found',
1041-
'message_long': 'The requested file could not be found.'
1042-
})
1043-
return proxy_url(f'/project/{file_.target._id}/files/osfstorage/{fid}/')
1044-
10451037
def addon_view_file(auth, node, file_node, version):
10461038
# TODO: resolve circular import issue
10471039
from addons.wiki import settings as wiki_settings

addons/boa/tests/test_tasks.py

Lines changed: 13 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ def setUp(self):
3838
self.output_file_name = 'fake_boa_script_results.txt'
3939
self.job_id = '1a2b3c4d5e6f7g8'
4040

41+
from conftest import start_mock_send_grid
42+
self.mock_send_grid = start_mock_send_grid(self)
43+
4144
def tearDown(self):
4245
super().tearDown()
4346

@@ -52,9 +55,10 @@ def test_boa_error_code(self):
5255
assert BoaErrorCode.FILE_TOO_LARGE_ERROR == 6
5356
assert BoaErrorCode.JOB_TIME_OUT_ERROR == 7
5457

58+
@mock.patch('website.mails.settings.USE_EMAIL', True)
59+
@mock.patch('website.mails.settings.USE_CELERY', False)
5560
def test_handle_boa_error(self):
56-
with mock.patch('addons.boa.tasks.send_mail', return_value=None) as mock_send_mail, \
57-
mock.patch('addons.boa.tasks.sentry.log_message', return_value=None) as mock_sentry_log_message, \
61+
with mock.patch('addons.boa.tasks.sentry.log_message', return_value=None) as mock_sentry_log_message, \
5862
mock.patch('addons.boa.tasks.logger.error', return_value=None) as mock_logger_error:
5963
return_value = handle_boa_error(
6064
self.error_message,
@@ -68,24 +72,7 @@ def test_handle_boa_error(self):
6872
output_file_name=self.output_file_name,
6973
job_id=self.job_id
7074
)
71-
mock_send_mail.assert_called_with(
72-
to_addr=self.user_username,
73-
mail=ADDONS_BOA_JOB_FAILURE,
74-
fullname=self.user_fullname,
75-
code=BoaErrorCode.UNKNOWN,
76-
message=self.error_message,
77-
query_file_name=self.query_file_name,
78-
file_size=self.file_size,
79-
max_file_size=boa_settings.MAX_SUBMISSION_SIZE,
80-
query_file_full_path=self.file_full_path,
81-
output_file_name=self.output_file_name,
82-
job_id=self.job_id,
83-
max_job_wait_hours=self.max_job_wait_hours,
84-
project_url=self.project_url,
85-
boa_job_list_url=boa_settings.BOA_JOB_LIST_URL,
86-
boa_support_email=boa_settings.BOA_SUPPORT_EMAIL,
87-
osf_support_email=osf_settings.OSF_SUPPORT_EMAIL,
88-
)
75+
self.mock_send_grid.assert_called()
8976
mock_sentry_log_message.assert_called_with(self.error_message, skip_session=True)
9077
mock_logger_error.assert_called_with(self.error_message)
9178
assert return_value == BoaErrorCode.UNKNOWN
@@ -167,9 +154,14 @@ def setUp(self):
167154
boa_settings.REFRESH_JOB_INTERVAL = DEFAULT_REFRESH_JOB_INTERVAL
168155
boa_settings.MAX_JOB_WAITING_TIME = DEFAULT_MAX_JOB_WAITING_TIME
169156

157+
from conftest import start_mock_send_grid
158+
self.mock_send_grid = start_mock_send_grid(self)
159+
170160
def tearDown(self):
171161
super().tearDown()
172162

163+
@mock.patch('website.mails.settings.USE_EMAIL', True)
164+
@mock.patch('website.mails.settings.USE_CELERY', False)
173165
async def test_submit_success(self):
174166
with mock.patch('osf.models.user.OSFUser.objects.get', return_value=self.user), \
175167
mock.patch('osf.models.user.OSFUser.get_or_create_cookie', return_value=self.user_cookie), \
@@ -179,7 +171,6 @@ async def test_submit_success(self):
179171
mock.patch('boaapi.boa_client.BoaClient.query', return_value=self.mock_job), \
180172
mock.patch('boaapi.boa_client.BoaClient.close', return_value=None) as mock_close, \
181173
mock.patch('asyncio.sleep', new_callable=AsyncMock, return_value=None) as mock_async_sleep, \
182-
mock.patch('addons.boa.tasks.send_mail', return_value=None) as mock_send_mail, \
183174
mock.patch('addons.boa.tasks.handle_boa_error', return_value=None) as mock_handle_boa_error:
184175
return_value = await submit_to_boa_async(
185176
self.host,
@@ -199,19 +190,7 @@ async def test_submit_success(self):
199190
assert self.mock_job.refresh.call_count == 4
200191
assert mock_async_sleep.call_count == 4
201192
mock_close.assert_called()
202-
mock_send_mail.assert_called_with(
203-
to_addr=self.user.username,
204-
mail=ADDONS_BOA_JOB_COMPLETE,
205-
fullname=self.user.fullname,
206-
query_file_name=self.query_file_name,
207-
query_file_full_path=self.file_full_path,
208-
output_file_name=self.output_file_name,
209-
job_id=self.mock_job.id,
210-
project_url=self.project_url,
211-
boa_job_list_url=boa_settings.BOA_JOB_LIST_URL,
212-
boa_support_email=boa_settings.BOA_SUPPORT_EMAIL,
213-
osf_support_email=osf_settings.OSF_SUPPORT_EMAIL,
214-
)
193+
self.mock_send_grid.assert_called()
215194
mock_handle_boa_error.assert_not_called()
216195

217196
async def test_download_error(self):

addons/osfstorage/tests/test_views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from framework.auth import cas
2525

2626
from osf import features
27-
from osf.models import Tag, QuickFilesNode
27+
from osf.models import Tag
2828
from osf.models import files as models
2929
from addons.osfstorage.apps import osf_storage_root
3030
from addons.osfstorage import utils

addons/osfstorage/views.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -314,9 +314,6 @@ def osfstorage_create_child(file_node, payload, **kwargs):
314314
if not (name or user) or '/' in name:
315315
raise HTTPError(http_status.HTTP_400_BAD_REQUEST)
316316

317-
if getattr(file_node.target, 'is_quickfiles', False) and is_folder:
318-
raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={'message_long': 'You may not create a folder for QuickFiles'})
319-
320317
try:
321318
# Create a save point so that we can rollback and unlock
322319
# the parent record

admin/base/urls.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
re_path(r'^maintenance/', include('admin.maintenance.urls', namespace='maintenance')),
3131
re_path(r'^meetings/', include('admin.meetings.urls', namespace='meetings')),
3232
re_path(r'^metrics/', include('admin.metrics.urls', namespace='metrics')),
33-
re_path(r'^osf_groups/', include('admin.osf_groups.urls', namespace='osf_groups')),
3433
re_path(r'^management/', include('admin.management.urls', namespace='management')),
3534
re_path(r'^internet_archive/', include('admin.internet_archive.urls', namespace='internet_archive')),
3635
re_path(r'^schema_responses/', include('admin.schema_responses.urls', namespace='schema_responses')),

admin/common_auth/forms.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class UserRegistrationForm(forms.Form):
2222

2323
# TODO: Moving to guardian, find a better way to distinguish "admin-like" groups from object permission groups
2424
group_perms = forms.ModelMultipleChoiceField(
25-
queryset=Group.objects.exclude(Q(name__startswith='collections_') | Q(name__startswith='reviews_') | Q(name__startswith='preprint_') | Q(name__startswith='node_') | Q(name__startswith='osfgroup_') | Q(name__startswith='draft_registration_')),
25+
queryset=Group.objects.exclude(Q(name__startswith='collections_') | Q(name__startswith='reviews_') | Q(name__startswith='preprint_') | Q(name__startswith='node_') | Q(name__startswith='draft_registration_')),
2626
required=False,
2727
widget=forms.CheckboxSelectMultiple
2828
)

admin/institutions/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
re_path(r'^import/$', views.ImportInstitution.as_view(), name='import'),
1010
re_path(r'^(?P<institution_id>[0-9]+)/$', views.InstitutionDetail.as_view(), name='detail'),
1111
re_path(r'^(?P<institution_id>[0-9]+)/export/$', views.InstitutionExport.as_view(), name='export'),
12+
re_path(r'^(?P<institution_id>[0-9]+)/monthly_report/$', views.InstitutionMonthlyReporterDo.as_view(), name='monthly_report'),
1213
re_path(r'^(?P<institution_id>[0-9]+)/delete/$', views.DeleteInstitution.as_view(), name='delete'),
1314
re_path(r'^(?P<institution_id>[0-9]+)/deactivate/$', views.DeactivateInstitution.as_view(), name='deactivate'),
1415
re_path(r'^(?P<institution_id>[0-9]+)/reactivate/$', views.ReactivateInstitution.as_view(), name='reactivate'),

0 commit comments

Comments
 (0)