-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
App Configuration: recurring time window filter #40093
base: feature/spring-boot-3
Are you sure you want to change the base?
App Configuration: recurring time window filter #40093
Conversation
… to time window settings
API change check APIView has identified API level changes in this PR and created following API reviews. |
type: "Daily", | ||
interval: 1 | ||
range: | ||
type: "NoEnd" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is end
needed if there is a range type that is "NoEnd"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When using the recurrence pattern, we need to provide both start
and end
. The time window is end
- start
.
If the range type is "NoEnd", then we don't need to provide other paramters(endDate
, numberOfOccurrences
) in range scope.
For example, if we want to enable a feature flag in 2:00 AM ~3:00 AM every day from 2024/05/11. Then the config yaml should be:
start: "Sat, 11 May 2024 02:00:00 GMT",
end: "Sat, 11 May 2024 03:00:00 GMT",
recurrence:
pattern:
type: "Daily",
interval: 1
range:
type: "NoEnd"
If we want to enable a feature flag in 2:00 AM ~3:00 AM every day from 2024/05/11 to 2024/06/11. Then the config yaml should be:
start: "Sat, 11 May 2024 02:00:00 GMT",
end: "Sat, 11 May 2024 03:00:00 GMT",
recurrence:
pattern:
type: "Daily",
interval: 1
range:
type: "EndDate",
endDate: "Tue, 11 June 2024 00:00:00 GMT"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the intro paragraph no longer works as it goes agains the recurring items. Maybe instead of what we have here where both are one section we make it 2 different sections. I know it's the same filter, but they don't operate the same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But if we make it into 2 difference sections, then it's also confused. It looks like we have 2 fiters with same name "TimeWindowFilter", different parameters.
Another solution, how about updating the example, not using NoEnd
, use Numbered
instaed?
start: "Fri, 22 Mar 2024 20:00:00 GMT",
end: "Sat, 23 Mar 2024 02:00:00 GMT",
recurrence:
pattern:
type: "Daily",
interval: 1
range:
type: "Numbered"
numberOfOccurrences: 3
} | ||
|
||
private boolean validateRecurrencePattern() { | ||
if (!validateInterval()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we follow the Spring validation pattern these should be errors thrown and validation methods have void returns.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to use void return
|
||
// Time window duration must be shorter than how frequently it occurs | ||
final Duration intervalDuration = Duration.ofDays((long) pattern.getInterval() * RecurrenceConstants.DAYS_PER_WEEK); | ||
final Duration timeWindowDuration = Duration.between(settings.getStart(), settings.getEnd()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should move this down to where it is validated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated. Move these logic to the validateTimeWindowDuration
function.
...pring/cloud/feature/management/implementation/timewindow/recurrence/RecurrenceValidator.java
Outdated
Show resolved
Hide resolved
try { | ||
DayOfWeek.valueOf(dayOfWeek.toUpperCase()); | ||
} catch (IllegalArgumentException e) { | ||
reason = RecurrenceConstants.UNRECOGNIZED_VALUE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the reason we should be clearer that it is an invalid date of the week.
* @param firstDayOfWeek The first day of the week. | ||
* @return True if the duration is compliant with days of week, false otherwise. | ||
*/ | ||
private boolean isDurationCompliantWithDaysOfWeek(Duration duration, int interval, List<String> daysOfWeek, String firstDayOfWeek) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to be verify, this method is checking to make sure if I have a time window open the first one closes before the next starts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic seems like it might not work around daylight savings time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this method isDurationCompliantWithDaysOfWeek
is to make sure the first one close before the next.
type: "Daily", | ||
interval: 1 | ||
range: | ||
type: "NoEnd" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the intro paragraph no longer works as it goes agains the recurring items. Maybe instead of what we have here where both are one section we make it 2 different sections. I know it's the same filter, but they don't operate the same.
public static final String OUT_OF_RANGE = "The value of parameter %s is out of the accepted range."; | ||
public static final String UNRECOGNIZED_VALUE = "The value of parameter %s is unrecognizable."; | ||
public static final String REQUIRED_PARAMETER = "Value cannot be null for required parameter: %s"; | ||
public static final String NOT_MATCHED = "%s date is not a valid first occurrence."; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if my understanding right. Usually the "%s" will be replaced with the parameter name.
So if there's a NOT_MATCHED
error, then the error message will be "The start date Start is not a valid first occurrence. " Would it be repeated when showing "The start date Start"?
…non-null when Recurrence is not null
*/ | ||
public boolean isMatch() { | ||
final RecurrenceValidator validator = new RecurrenceValidator(settings); | ||
if (!validator.validateSettings()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this was made a static method we don't need to create a new instance of RecurrenceValidator
, this should be called in a static way.
@@ -0,0 +1,105 @@ | |||
// Copyright (c) Microsoft Corporation. All rights reserved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it was that validation should be done in the model class, moved to the model package (you already did), so did this too have a toString method (You might have cleaned this up.)
|
||
- Parameters | ||
|
||
| Property | Relevance | Description | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mrm9084 I merged all parameters to one table. It's clear that we support these 2 types for recurrence patter. But the shortcoming is that I need to declare the supported and unsupported types for each property.
Please let me know if you have any ideas about how to make it better.
|
||
if (!StringUtils.hasText(start) && !StringUtils.hasText(end)) { | ||
if (settings.getStart() == null && settings.getEnd() == null) { | ||
LOGGER.warn("The {} feature filter is not valid for feature {}. It must specify either {}, {}, or both.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mrm9084 Shall we throw exception here?
In the validations of recurrence, I just throw exception, because if there're invalid value for the enum type, it's easier to throw exception. Maybe we need to keep consistent?
|
||
| Property | Relevance | Description | | ||
|-----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | ||
| **type** | Required | Two valid values: `Daily`, `Weekly`. | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, this type
can be optional, because we have a default value as Daily
.
My concern is that if I announce all parameters to be optional, then maybe users will not define any parameters in recurrence pattern like the following.
start: "Mon, 13 May 2024 02:00:00 GMT",
end: "Mon, 13 May 2024 03:00:00 GMT",
recurrence:
range:
type: "NoEnd"
Shall we support this? Or throw invalid exception since there's no pattern
parameter? @mrm9084
/** | ||
* @param numberOfOccurrences the repeat times to be set | ||
* */ | ||
public void setNumberOfOccurrences(int numberOfOccurrences) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is different from the setInterval(interger interval)
/** | ||
* @param interval the time units to be set | ||
* */ | ||
public void setInterval(Integer interval) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You use int
type in public void setNumberOfOccurrences(int numberOfOccurrences)
Is there any reason use Integer
here? It doesn't make sense to do null check here, since Interval
has default value 1
.
|
||
// Check whether "Start" is a valid first occurrence | ||
final RecurrencePattern pattern = settings.getRecurrence().getPattern(); | ||
if (pattern.getDaysOfWeek().stream().noneMatch((dayOfWeekStr) -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should it be dayOfWeek instead of dayOfWeekStr? getDaysOfWeek returns List
Add "Recurrence" parameter for TimeWindowFilter using Outlook-style schema:
https://learn.microsoft.com/en-us/graph/outlook-schedule-recurring-events#using-patterns-and-ranges-to-create-recurring-events
Example:
The main logic of how to check whether current time is within any recurring time window:
Find the previous occurrence of the recurring time window, let's call it prevOccurrenceStart
Check whether the current time: time is within the time window: prevOccurrenceStart ~ prevOccurrenceStart + End - Start