Merge pull request #602 from grafana/dev

Merge dev to main
This commit is contained in:
Michael Derynck 2022-10-03 11:26:57 -06:00 committed by GitHub
commit 2e924efd74
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 72 additions and 36 deletions

View file

@ -1,5 +1,9 @@
# Change Log
## v1.0.39 (2022-10-03)
- Fix issue in v1.0.38 blocking the creation of schedules and webhooks in the UI
## v1.0.38 (2022-09-30)
- Fix exception handling for adding resolution notes when slack and oncall users are out of sync.

View file

@ -10,6 +10,7 @@ from rest_framework.serializers import ValidationError
from rest_framework.test import APIClient
from apps.alerts.models import EscalationPolicy
from apps.schedules.ical_utils import memoized_users_in_ical
from apps.schedules.models import (
CustomOnCallShift,
OnCallSchedule,
@ -742,6 +743,8 @@ def test_filter_events_final_schedule(
request_date = start_date
user_a, user_b, user_c, user_d, user_e = (make_user_for_organization(organization, username=i) for i in "ABCDE")
# clear users pks <-> organization cache (persisting between tests)
memoized_users_in_ical.cache_clear()
shifts = (
# user, priority, start time (h), duration (hs)
@ -837,7 +840,7 @@ def test_next_shifts_per_user(
make_schedule,
make_on_call_shift,
):
organization, user, token = make_organization_and_user_with_plugin_token()
organization, admin, token = make_organization_and_user_with_plugin_token()
client = APIClient()
schedule = make_schedule(
@ -848,6 +851,8 @@ def test_next_shifts_per_user(
tomorrow = timezone.now().replace(hour=0, minute=0, second=0, microsecond=0) + timezone.timedelta(days=1)
user_a, user_b, user_c, user_d = (make_user_for_organization(organization, username=i) for i in "ABCD")
# clear users pks <-> organization cache (persisting between tests)
memoized_users_in_ical.cache_clear()
shifts = (
# user, priority, start time (h), duration (hs)
@ -860,16 +865,16 @@ def test_next_shifts_per_user(
for user, priority, start_h, duration in shifts:
data = {
"start": tomorrow + timezone.timedelta(hours=start_h),
"rotation_start": tomorrow,
"rotation_start": tomorrow + timezone.timedelta(hours=start_h),
"duration": timezone.timedelta(hours=duration),
"priority_level": priority,
"frequency": CustomOnCallShift.FREQUENCY_DAILY,
"schedule": schedule,
}
on_call_shift = make_on_call_shift(
organization=organization, shift_type=CustomOnCallShift.TYPE_RECURRENT_EVENT, **data
organization=organization, shift_type=CustomOnCallShift.TYPE_ROLLING_USERS_EVENT, **data
)
on_call_shift.users.add(user)
on_call_shift.add_rolling_users([[user]])
# override in the past: 17-18 / D
# won't be listed, but user D will still be included in the response
@ -896,10 +901,10 @@ def test_next_shifts_per_user(
)
override.add_rolling_users([[user_c]])
# final sdhedule: 7-12: B, 15-16: A, 16-17: B, 17-18: C (override), 18-20: C
# final schedule: 7-12: B, 15-16: A, 16-17: B, 17-18: C (override), 18-20: C
url = reverse("api-internal:schedule-next-shifts-per-user", kwargs={"pk": schedule.public_primary_key})
response = client.get(url, format="json", **make_user_auth_headers(user, token))
response = client.get(url, format="json", **make_user_auth_headers(admin, token))
assert response.status_code == status.HTTP_200_OK
expected = {

View file

@ -5,6 +5,7 @@ from calendar import monthrange
from uuid import uuid4
import pytz
from dateutil import relativedelta
from django.apps import apps
from django.conf import settings
from django.core.validators import MinLengthValidator
@ -353,6 +354,13 @@ class CustomOnCallShift(models.Model):
ONE_DAY = 1
ONE_HOUR = 1
def add_months(year, month, months_add):
"""
Utility method for month calculation. E.g. (2022, 12) + 1 month = (2023, 1)
"""
dt = timezone.datetime.min.replace(year=year, month=month) + relativedelta.relativedelta(months=months_add)
return dt.year, dt.month
current_event = Event.from_ical(event_ical)
# take shift interval, not event interval. For rolling_users shift it is not the same.
interval = self.interval or 1
@ -385,7 +393,8 @@ class CustomOnCallShift(models.Model):
days_for_next_event = DAYS_IN_A_MONTH - current_event_start.day + ONE_DAY
# count next event start date with respect to event interval
for i in range(1, interval):
next_month_days = monthrange(current_event_start.year, current_event_start.month + i)[1]
year, month = add_months(current_event_start.year, current_event_start.month, i)
next_month_days = monthrange(year, month)[1]
days_for_next_event += next_month_days
next_event_start = current_event_start + timezone.timedelta(days=days_for_next_event)

View file

@ -354,11 +354,11 @@ def test_rolling_users_event_with_interval_monthly(
user_2 = make_user_for_organization(organization)
schedule = make_schedule(organization, schedule_class=OnCallScheduleCalendar)
start_date = timezone.now().replace(day=1, microsecond=0)
days_for_next_month_1 = monthrange(start_date.year, start_date.month)[1]
days_for_next_month_2 = monthrange(start_date.year, start_date.month + 1)[1] + days_for_next_month_1
days_for_next_month_3 = monthrange(start_date.year, start_date.month + 2)[1] + days_for_next_month_2
days_for_next_month_4 = monthrange(start_date.year, start_date.month + 3)[1] + days_for_next_month_3
start_date = timezone.datetime(year=2022, month=10, day=1, hour=10, minute=30)
days_for_next_month_1 = monthrange(2022, 10)[1]
days_for_next_month_2 = monthrange(2022, 11)[1] + days_for_next_month_1
days_for_next_month_3 = monthrange(2022, 12)[1] + days_for_next_month_2
days_for_next_month_4 = monthrange(2023, 1)[1] + days_for_next_month_3
data = {
"priority_level": 1,
@ -718,19 +718,19 @@ def test_rolling_users_with_diff_start_and_rotation_start_monthly(
user_3 = make_user_for_organization(organization)
schedule = make_schedule(organization, schedule_class=OnCallScheduleWeb)
now = timezone.now().replace(day=1, microsecond=0)
days_in_curr_month = monthrange(now.year, now.month)[1]
days_in_next_month = monthrange(now.year, now.month + 1)[1]
start_date = timezone.datetime(year=2022, month=12, day=1, hour=10, minute=30)
days_in_curr_month = monthrange(2022, 12)[1]
days_in_next_month = monthrange(2023, 1)[1]
data = {
"priority_level": 1,
"start": now,
"week_start": now.weekday(),
"rotation_start": now + timezone.timedelta(days=days_in_curr_month - 1, hours=1),
"start": start_date,
"week_start": start_date.weekday(),
"rotation_start": start_date + timezone.timedelta(days=days_in_curr_month - 1, hours=1),
"duration": timezone.timedelta(seconds=1800),
"frequency": CustomOnCallShift.FREQUENCY_MONTHLY,
"schedule": schedule,
"until": now + timezone.timedelta(days=days_in_curr_month + days_in_next_month + 10, minutes=1),
"until": start_date + timezone.timedelta(days=days_in_curr_month + days_in_next_month + 10, minutes=1),
}
rolling_users = [[user_1], [user_2], [user_3]]
on_call_shift = make_on_call_shift(
@ -738,7 +738,7 @@ def test_rolling_users_with_diff_start_and_rotation_start_monthly(
)
on_call_shift.add_rolling_users(rolling_users)
date = now + timezone.timedelta(minutes=5)
date = start_date + timezone.timedelta(minutes=5)
# rotation starts from user_2, because user_1 started earlier than rotation start date
user_2_on_call_dates = [date + timezone.timedelta(days=days_in_curr_month)]
user_3_on_call_dates = [date + timezone.timedelta(days=days_in_curr_month + days_in_next_month)]
@ -774,9 +774,9 @@ def test_rolling_users_with_diff_start_and_rotation_start_monthly_by_monthday(
user_3 = make_user_for_organization(organization)
schedule = make_schedule(organization, schedule_class=OnCallScheduleWeb)
start_date = timezone.now().replace(day=1, microsecond=0)
days_in_curr_month = monthrange(start_date.year, start_date.month)[1]
days_in_next_month = monthrange(start_date.year, start_date.month + 1)[1]
start_date = timezone.datetime(year=2022, month=12, day=1, hour=10, minute=30)
days_in_curr_month = monthrange(2022, 12)[1]
days_in_next_month = monthrange(2023, 1)[1]
data = {
"priority_level": 1,

View file

@ -4,6 +4,7 @@ import pytest
import pytz
from django.utils import timezone
from apps.schedules.ical_utils import memoized_users_in_ical
from apps.schedules.models import CustomOnCallShift, OnCallSchedule, OnCallScheduleCalendar, OnCallScheduleWeb
from common.constants.role import Role
@ -236,6 +237,8 @@ def test_filter_events_ical_all_day(make_organization, make_user_for_organizatio
schedule.cached_ical_file_primary = calendar.to_ical()
for u in ("@Bernard Desruisseaux", "@Bob", "@Alex"):
make_user_for_organization(organization, username=u)
# clear users pks <-> organization cache (persisting between tests)
memoized_users_in_ical.cache_clear()
day_to_check_iso = "2021-01-27T15:27:14.448059+00:00"
parsed_iso_day_to_check = datetime.datetime.fromisoformat(day_to_check_iso).replace(tzinfo=pytz.UTC)

View file

@ -1,5 +1,9 @@
# Change Log
## v1.0.39 (2022-10-03)
- Fix issue in v1.0.38 blocking the creation of schedules and webhooks in the UI
## v1.0.38 (2022-09-30)
- Fix exception handling for adding resolution notes when slack and oncall users are out of sync.

View file

@ -60,14 +60,19 @@ class OutgoingWebhooks extends React.Component<OutgoingWebhooksProps, OutgoingWe
query: { id },
} = this.props;
if (id) {
const outgoingWebhook = await store.outgoingWebhookStore
if (!id) {return;}
let outgoingWebhook: OutgoingWebhook | void = undefined;
const isNewWebhook = id === 'new';
if (!isNewWebhook) {
outgoingWebhook = await store.outgoingWebhookStore
.loadItem(id, true)
.catch((error) => this.setState({ errorData: { ...getWrongTeamResponseInfo(error) } }));
}
if (outgoingWebhook) {
this.setState({ outgoingWebhookIdToEdit: id });
}
if (outgoingWebhook || isNewWebhook) {
this.setState({ outgoingWebhookIdToEdit: id });
}
};

View file

@ -85,7 +85,13 @@ class SchedulesPage extends React.Component<SchedulesPageProps, SchedulesPageSta
query: { id },
} = this.props;
if (id) {
if (!id) {return;}
let scheduleId: string = undefined;
const isNewSchedule = id === 'new';
if (!isNewSchedule) {
// load schedule only for valid id
const schedule = await store.scheduleStore
.loadItem(id, true)
.catch((error) => this.setState({ errorData: { ...getWrongTeamResponseInfo(error) } }));
@ -93,13 +99,13 @@ class SchedulesPage extends React.Component<SchedulesPageProps, SchedulesPageSta
return;
}
const schedules = store.scheduleStore.getSearchResult();
const scheduleId = schedules && schedules.find((res) => res.id === id)?.id;
if (scheduleId || id === 'new') {
this.setState({ scheduleIdToEdit: id });
} else {
openErrorNotification(`Schedule with id=${id} is not found. Please select schedule from the list.`);
}
scheduleId = schedule.id;
}
if (scheduleId || isNewSchedule) {
this.setState({ scheduleIdToEdit: id });
} else {
openErrorNotification(`Schedule with id=${id} is not found. Please select schedule from the list.`);
}
};