Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .docker-compose.env
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ INTERNAL_DOMAIN=http://192.168.168.167:5000/
API_DOMAIN=http://localhost:8000/
ELASTIC_URI=192.168.168.167:9200
ELASTIC6_URI=192.168.168.167:9201
ELASTIC8_URI=http://192.168.168.167:9202
OSF_DB_HOST=192.168.168.167
DB_HOST=192.168.168.167
REDIS_HOST=redis://192.168.168.167:6379
Expand Down
2 changes: 1 addition & 1 deletion addons/base/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import waffle
from django.db import transaction
from django.contrib.contenttypes.models import ContentType
from elasticsearch import exceptions as es_exceptions
from elasticsearch6 import exceptions as es_exceptions
from rest_framework import status as http_status

from api.caching.tasks import update_storage_usage_with_size
Expand Down
8 changes: 4 additions & 4 deletions api/base/elasticsearch_dsl_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import datetime
import typing

import elasticsearch_dsl as edsl
import elasticsearch6_dsl as edsl
from rest_framework import generics, exceptions as drf_exceptions
from rest_framework.settings import api_settings as drf_settings
from api.base.settings.defaults import REPORT_FILENAME_FORMAT
Expand All @@ -23,7 +23,7 @@


class ElasticsearchListView(FilterMixin, JSONAPIBaseView, generics.ListAPIView, abc.ABC):
'''abstract view class using `elasticsearch_dsl.Search` as a queryset-analogue
'''abstract view class using `elasticsearch6_dsl.Search` as a queryset-analogue

builds a `Search` based on `self.get_default_search()` and the request's
query parameters for filtering, sorting, and pagination -- fetches only
Expand All @@ -36,7 +36,7 @@ class ElasticsearchListView(FilterMixin, JSONAPIBaseView, generics.ListAPIView,

@abc.abstractmethod
def get_default_search(self) -> edsl.Search | None:
'''the base `elasticsearch_dsl.Search` for this list, based on url path
'''the base `elasticsearch6_dsl.Search` for this list, based on url path

(common jsonapi query parameters will be considered automatically)
'''
Expand Down Expand Up @@ -95,7 +95,7 @@ def finalize_response(self, request, response, *args, **kwargs):
# (filtering handled in-view to reuse logic from FilterMixin)
filter_backends = ()

# note: because elasticsearch_dsl.Search supports slicing and gives results when iterated on,
# note: because elasticsearch6_dsl.Search supports slicing and gives results when iterated on,
# it works fine with default pagination

# override rest_framework.generics.GenericAPIView
Expand Down
16 changes: 12 additions & 4 deletions api/base/settings/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,10 +316,18 @@
HASHIDS_SALT = 'pinkhimalayan'

# django-elasticsearch-metrics
ELASTICSEARCH_DSL = {
'default': {
'hosts': osf_settings.ELASTIC6_URI,
'retry_on_timeout': True,
DJELME_AUTOSETUP = True
DJELME_BACKENDS = {
'osfmetrics_es6': {
'elasticsearch_metrics.imps.elastic6': {
'hosts': osf_settings.ELASTIC6_URI,
'retry_on_timeout': True,
},
},
'osfmetrics_es8': {
'elasticsearch_metrics.imps.elastic8': {
'hosts': osf_settings.ELASTIC8_URI,
},
},
}
# Store yearly indices for time-series metrics
Expand Down
4 changes: 2 additions & 2 deletions api/metrics/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
from django.http import JsonResponse, HttpResponse, Http404
from django.utils import timezone

from elasticsearch.exceptions import NotFoundError, RequestError
from elasticsearch_dsl.connections import get_connection
from elasticsearch6.exceptions import NotFoundError, RequestError
from elasticsearch6_dsl.connections import get_connection

from framework.auth.oauth_scopes import CoreScopes

Expand Down
11 changes: 6 additions & 5 deletions api_tests/institutions/views/test_institution_department_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def populate_counts(self, user, user2, user3, user4, admin, institution):
department_name='Old Department',
public_project_count=1,
private_project_count=1,
).save(refresh=True)
).save()

_this_month = YearMonth.from_date(datetime.date.today())

Expand All @@ -56,7 +56,7 @@ def populate_counts(self, user, user2, user3, user4, admin, institution):
department_name='New Department',
public_project_count=1,
private_project_count=1,
).save(refresh=True)
).save()

# A second user entered the department
InstitutionalUserReport(
Expand All @@ -66,7 +66,7 @@ def populate_counts(self, user, user2, user3, user4, admin, institution):
department_name='New Department',
public_project_count=1,
private_project_count=1,
).save(refresh=True)
).save()

# A new department with a single user to test sorting
InstitutionalUserReport(
Expand All @@ -76,7 +76,7 @@ def populate_counts(self, user, user2, user3, user4, admin, institution):
department_name='Smaller Department',
public_project_count=1,
private_project_count=1,
).save(refresh=True)
).save()

# A user with no department
InstitutionalUserReport(
Expand All @@ -85,7 +85,7 @@ def populate_counts(self, user, user2, user3, user4, admin, institution):
institution_id=institution._id,
public_project_count=1,
private_project_count=1,
).save(refresh=True)
).save()

@pytest.fixture()
def admin(self, institution):
Expand Down Expand Up @@ -113,6 +113,7 @@ def test_auth(self, app, url, user, admin):
assert resp.json['data'] == []

def test_get(self, app, url, admin, institution, populate_counts):
InstitutionalUserReport._get_connection().indices.refresh(InstitutionalUserReport._template_pattern)
resp = app.get(url, auth=admin.auth)

assert resp.json['data'] == [{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ def test_get_empty(self, app, url, institutional_admin):
assert resp.json['meta'] == {'version': '2.0'}

def test_get_report(self, app, url, institutional_admin, institution, reports, unshown_reports):
InstitutionMonthlySummaryReport._get_connection().indices.refresh(InstitutionMonthlySummaryReport._template_pattern)
resp = app.get(url, auth=institutional_admin.auth)
assert resp.status_code == 200

Expand Down Expand Up @@ -149,6 +150,7 @@ def test_get_report_with_multiple_months_and_institutions(
monthly_logged_in_user_count=270,
monthly_active_user_count=260,
)
InstitutionMonthlySummaryReport._get_connection().indices.refresh(InstitutionMonthlySummaryReport._template_pattern)

resp = app.get(url, auth=institutional_admin.auth)
assert resp.status_code == 200
Expand Down Expand Up @@ -189,6 +191,7 @@ def test_get_with_valid_report_dates(self, app, url, institution, institutional_
institution,
user_count=4133,
)
InstitutionMonthlySummaryReport._get_connection().indices.refresh(InstitutionMonthlySummaryReport._template_pattern)

resp = app.get(f'{url}?report_yearmonth=2024-08', auth=institutional_admin.auth)
assert resp.status_code == 200
Expand All @@ -213,6 +216,7 @@ def test_get_with_invalid_report_date(self, app, url, institution, institutional
institution,
user_count=999,
)
InstitutionMonthlySummaryReport._get_connection().indices.refresh(InstitutionMonthlySummaryReport._template_pattern)

# Request with an invalid report_date format
resp = app.get(f'{url}?report_yearmonth=invalid-date', auth=institutional_admin.auth)
Expand All @@ -233,6 +237,7 @@ def test_get_without_report_date_uses_most_recent(self, app, url, institution, i
institution,
user_count=999,
)
InstitutionMonthlySummaryReport._get_connection().indices.refresh(InstitutionMonthlySummaryReport._template_pattern)

resp = app.get(url, auth=institutional_admin.auth)
assert resp.status_code == 200
Expand All @@ -247,5 +252,5 @@ def _summary_report_factory(yearmonth, institution, **kwargs):
institution_id=institution._id,
**kwargs,
)
report.save(refresh=True)
report.save()
return report
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ def test_get_empty(self, app, url, institutional_admin):
assert _resp.json['data'] == []

def test_get_reports(self, app, url, institutional_admin, institution, reports, unshown_reports):
InstitutionalUserReport._get_connection().indices.refresh(InstitutionalUserReport._template_pattern)
_resp = app.get(url, auth=institutional_admin.auth)
assert _resp.status_code == 200
assert len(_resp.json['data']) == len(reports)
Expand All @@ -100,6 +101,7 @@ def test_get_reports(self, app, url, institutional_admin, institution, reports,
assert len(response_object['attributes']['contacts']) == 0

def test_filter_reports(self, app, url, institutional_admin, institution, reports, unshown_reports):
InstitutionalUserReport._get_connection().indices.refresh(InstitutionalUserReport._template_pattern)
for _query, _expected_user_ids in (
({'filter[department]': 'nunavum'}, set()),
({'filter[department]': 'incidentally'}, set()),
Expand Down Expand Up @@ -135,6 +137,7 @@ def test_filter_reports(self, app, url, institutional_admin, institution, report
assert set(_user_ids(_resp)) == _expected_user_ids

def test_sort_reports(self, app, url, institutional_admin, institution, reports, unshown_reports):
InstitutionalUserReport._get_connection().indices.refresh(InstitutionalUserReport._template_pattern)
for _query, _expected_user_id_list in (
({'sort': 'storage_byte_count'}, ['u_sparse', 'u_orc', 'u_blargl', 'u_orcomma']),
({'sort': '-storage_byte_count'}, ['u_orcomma', 'u_blargl', 'u_orc', 'u_sparse']),
Expand All @@ -144,6 +147,7 @@ def test_sort_reports(self, app, url, institutional_admin, institution, reports,
assert list(_user_ids(_resp)) == _expected_user_id_list

def test_paginate_reports(self, app, url, institutional_admin, institution, reports, unshown_reports):
InstitutionalUserReport._get_connection().indices.refresh(InstitutionalUserReport._template_pattern)
for _query, _expected_user_id_list in (
({'sort': 'storage_byte_count', 'page[size]': 2}, ['u_sparse', 'u_orc']),
({'sort': 'storage_byte_count', 'page[size]': 2, 'page': 2}, ['u_blargl', 'u_orcomma']),
Expand Down Expand Up @@ -178,6 +182,7 @@ def test_get_report_formats_csv_tsv(self, app, url, institutional_admin, institu
month_last_active='2018-02',
month_last_login='2018-02',
)
InstitutionalUserReport._get_connection().indices.refresh(InstitutionalUserReport._template_pattern)

resp = app.get(f'{url}?format={format_type}', auth=institutional_admin.auth)
assert resp.status_code == 200
Expand Down Expand Up @@ -281,6 +286,7 @@ def test_csv_tsv_ignores_pagination(self, app, url, institutional_admin, institu
str(736662999298 + i),
f'Jalen Hurts #{i}',
])
InstitutionalUserReport._get_connection().indices.refresh(InstitutionalUserReport._template_pattern)

# Make request for CSV format with page[size]=10
resp = app.get(f'{url}?format={format_type}', auth=institutional_admin.auth)
Expand Down Expand Up @@ -346,6 +352,7 @@ def test_get_report_format_table_json(self, app, url, institutional_admin, insti
month_last_active='2018-02',
month_last_login='2018-02',
)
InstitutionalUserReport._get_connection().indices.refresh(InstitutionalUserReport._template_pattern)

resp = app.get(f'{url}?format=json_report', auth=institutional_admin.auth)
assert resp.status_code == 200
Expand Down Expand Up @@ -411,6 +418,7 @@ def test_correct_number_of_contact_messages(self, app, url, institutional_admin,
department_name='a department, or so, that happens, incidentally, to have commas',
storage_byte_count=736662999298,
)
InstitutionalUserReport._get_connection().indices.refresh(InstitutionalUserReport._template_pattern)

receiver = user1
with capture_notifications():
Expand Down Expand Up @@ -477,5 +485,5 @@ def _report_factory(yearmonth, institution, **kwargs):
institution_id=institution._id,
**kwargs,
)
_report.save(refresh=True)
_report.save()
return _report
3 changes: 1 addition & 2 deletions api_tests/metrics/test_composite_query.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import time
import pytest
from datetime import datetime
from osf_tests.factories import (
Expand Down Expand Up @@ -75,7 +74,7 @@ def test_elasticsearch_agg_query(self, app, user, base_url, preprint):
path=preprint.primary_file.path,
timestamp=datetime(year=2020, month=2, day=1)
)
time.sleep(1) # gives ES some time to update
PreprintDownload._get_connection().indices.refresh(PreprintDownload._template_pattern)

resp = app.post_json_api(post_url, payload, auth=user.auth)
assert resp.status_code == 200
Expand Down
2 changes: 1 addition & 1 deletion api_tests/metrics/test_preprint_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from django.utils import timezone
from waffle.testutils import override_switch
from elasticsearch.exceptions import RequestError
from elasticsearch6.exceptions import RequestError

from osf import features
from api.base.settings import API_PRIVATE_BASE as API_BASE
Expand Down
5 changes: 2 additions & 3 deletions api_tests/metrics/test_registries_moderation_metrics.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import pytest
from waffle.testutils import override_switch

import time
from osf import features
from osf_tests.factories import RegistrationFactory, AuthUserFactory
from osf.utils.workflows import RegistrationModerationStates, RegistrationModerationTriggers
Expand Down Expand Up @@ -32,7 +31,7 @@ def test_record_transitions(self, registration):
registration.creator,
'Metrics is easy'
)
time.sleep(1)
RegistriesModerationMetrics._get_connection().indices.refresh(RegistriesModerationMetrics._template_pattern)

assert RegistriesModerationMetrics.search().count() == 1
data = RegistriesModerationMetrics.search().execute()['hits']['hits'][0]['_source']
Expand Down Expand Up @@ -81,7 +80,7 @@ def test_registries_moderation_view(self, app, user, base_url, registration):
registration.creator,
'Metrics is easy'
)
time.sleep(1)
RegistriesModerationMetrics._get_connection().indices.refresh(RegistriesModerationMetrics._template_pattern)

res = app.get(base_url, auth=user.auth, expect_errors=True)
data = res.json
Expand Down
60 changes: 20 additions & 40 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import contextlib
from unittest import mock
import logging
import os
import re

from django.core.management import call_command
from django.db import transaction
from elasticsearch import exceptions as es_exceptions
from elasticsearch_dsl.connections import connections
from elasticsearch_metrics.registry import registry as es_metrics_registry
from elasticsearch6_dsl.connections import connections
from website import settings as osf_settings
from elasticsearch_metrics.tests._test_util import RealElasticTestCase
from faker import Factory
import pytest
import responses
Expand Down Expand Up @@ -138,47 +136,29 @@ def es6_client(setup_connections):


@pytest.fixture(scope='function', autouse=True)
def _es_metrics_marker(request, worker_id):
def _es_metrics_marker(request):
"""Clear out all indices and index templates before and after
tests marked with `es_metrics`.
"""
marker = request.node.get_closest_marker('es_metrics')
if marker:
es6_client = request.getfixturevalue('es6_client')
_temp_prefix = 'temp_metrics_'
_temp_wildcard = f'{_temp_prefix}-{worker_id}*'

def _teardown_es_temps():
es6_client.indices.delete(index=_temp_wildcard)
try:
es6_client.indices.delete_template(_temp_wildcard)
except es_exceptions.NotFoundError:
pass

@contextlib.contextmanager
def _mock_metric_names():
with contextlib.ExitStack() as _exit:
for _metric_class in es_metrics_registry.get_metrics():
_exit.enter_context(mock.patch.object(
_metric_class,
'_template_name', # also used to construct index names
f'{_temp_prefix}-{worker_id}{_metric_class._template_name}',
))
_exit.enter_context(mock.patch.object(
_metric_class,
'_template', # a wildcard string for indexes and templates
f'{_temp_prefix}-{worker_id}{_metric_class._template}',
))
yield

_teardown_es_temps()
with _mock_metric_names():
call_command('sync_metrics')
yield
_teardown_es_temps()
else:

if not marker:
yield
return

connections.create_connection(
alias='osfmetrics_es6',
hosts=osf_settings.ELASTIC6_URI,
)

class _Es6TestCase(RealElasticTestCase, autosetup_djelme_backends=True):
...
es6_test_case = _Es6TestCase()
es6_test_case.setUp()
try:
yield
finally:
es6_test_case.tearDown()

@pytest.fixture
def mock_share_responses():
Expand Down
Loading
Loading