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
added support for schedules #30
Conversation
self.assertEqual("Weekly", all_schedules[1].frequency) | ||
self.assertEqual("2016-09-18T06:00:00Z", all_schedules[1].next_run_at) | ||
|
||
def test_get_empty(self): |
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.
Do we test this for every content type? I'm not sure it's a super valuable test but it's harmless.
Is it just testing that the Schedule.from_response doesn't choke on an empty input?
If that's the case the mocked request might be unnecessary.
This is minor though, I bet this follows the other test's and that's cool.
🚀 |
1 similar comment
🚀 |
self._next_run_at = None | ||
self._priority = None | ||
self._state = None | ||
self._type = None |
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.
What is type. This doesn't feel like it should be just an arbitrary string
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.
"Extract" or "Subscription" -- maybe more in the future
Whoa, this got even bigger :) It'll take me some time to take a look at it, but I've got a long flight tomorrow... |
parser.add_argument('--username', '-u', required=True, help='username to sign into server') | ||
parser.add_argument('--logging-level', '-l', choices=['debug', 'info', 'error'], default='error', | ||
help='desired logging level (set to error by default)') | ||
args = parser.parse_args() |
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.
The other samples probably need this as well -- but this should be in an if __name__ == '__main__'
block so it only runs as a script, and so that we can check for no arguments and print the help.
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.
Addressing the other samples can be a different Issue#
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.
Fixed this in all samples
from .. import NAMESPACE | ||
|
||
|
||
class IntervalItem(object): |
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 don't have an alternate suggestion, so feel free to ignore, but is IntervalItem the best name we've got?
The nested classes are fine for the enum-y purpose you're using it for (man, in py3 we could use real enums) -- but we might be hitting a point where we just needs a constants or common module that holds all these so the actual class with logic doesn't get muddled down.
I think this is also the case for RequestOptions?
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.
Ignored for now
|
||
cls._validate_time(start_time) | ||
cls._validate_time(end_time) | ||
interval = [(interval_occurrence.lower(), str(interval_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.
If this doesn't need to be mutable can just return a tuple here
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 why this is a list, but it's consistent so I left it
self.start_time = start_time | ||
|
||
@staticmethod | ||
def _validate_time(t): |
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 might be useful just as a function at the module level, and returns true or false, then the classes that use it can decide what exception to throw, or to handle some other way (like using a default 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.
I looked at it for a while and made a small refactor, but pulling it out can happen in another PR and after more conversation.
(_, name, _, _, updated_at, _, frequency, next_run_at, end_schedule_at, execution_order, | ||
priority, interval_item) = self._parse_element(schedule_xml) | ||
|
||
self._set_values(None, name, None, None, updated_at, None, frequency, next_run_at, end_schedule_at, |
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.
Can you use keyword arguments for all of these -- else we're doomed to be lost at sea :)
(Side note, can any of these be moved out of the function, it seems crazy big)
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.
Fixed
|
||
@classmethod | ||
def from_response(cls, resp): | ||
all_schedule_items = list() |
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.
Nit: We seem to use the literal in most places []
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.
fixed
end_schedule_at, execution_order, priority, interval_item) = cls._parse_element(schedule_xml) | ||
|
||
schedule_item = cls(name, priority, schedule_type, execution_order, interval_item) | ||
schedule_item._set_values(id, None, state, created_at, updated_at, None, frequency, next_run_at, |
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.
Same comment about keyword arguments
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.
fixed
|
||
priority = schedule_xml.get('priority', None) | ||
if priority: | ||
priority = int(priority) |
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 will fail if priority is a non empty string that isn't an integer. Do we know that's always safe to do this conversion?
def delete(self, schedule_id): | ||
if not schedule_id: | ||
error = "Schedule ID undefined" | ||
raise ValueError(error) |
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.
The exception should get logged as an error -- or do we not do that on other endpoints?
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.
Confirmed the XSD schema only allows nonNegInts
|
||
return self | ||
|
||
def _set_values(self, id, name, state, created_at, updated_at, schedule_type, frequency, |
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.
@RussTheAerialist I took a local copy of this PR to try and address my own feedback and put it up for review, and this method made me wonder if there was a better way.
I'm thinking of instead of having a giant setter that does nothing but check that attributes are not false -- what if something like this:
- The ScheduleItem constructor has everything initialized to None (or has default args)
- _parse_element returns only non-null/None attributes it gets back as a dict
- We can loop over the dict to apply the attributes in _set_values (and remove the giant function signature) or I think we can do away with the method entirely, but the idea was on the tip of my tongue and it left me.
I can dig in farther, but for now I've at least done this:
schedule_item._set_values(id=id,
name=None,
state=state,
created_at=created_at,
updated_at=updated_at,
schedule_type=None,
frequency=frequency,
next_run_at=next_run_at,
end_schedule_at=end_schedule_at,
execution_order=None,
priority=None,
interval_item=None)
EDIT: I looked at the other *_item files and this is the pattern we use everywhere, I still think it's worth converting everything to keyword arguments though!
#48 supersedes this change |
Original PR by @shinchris #30 * added ability to query and delete schedules * added ability to create and update schedules * intervals for schedules are expressed as (Unit)Interval classes * hourly intervals can take .25 and .5 to represent 15 and 30 minute schedules
No description provided.