def get_selected( resources_ids: Optional[List[ResourceId]] = None, start: Optional[str] = None, end: Optional[str] = None ) -> Tuple[Union[List[Any], Content], HttpStatusCode]: # TODO This may need a decent refactor - give more freedom # All args are required at once, otherwise return 400 all_not_none = resources_ids and start and end if all_not_none: try: start_as_datetime = DateUtils.parse_string(start) ends_as_datetime = DateUtils.parse_string(end) matches = list( Reservation.filter_by_uuids_and_time_range( resources_ids, start_as_datetime, ends_as_datetime)) matches = [match.as_dict() for match in matches] except (ValueError, AssertionError) as reason: content = {'msg': '{}. {}'.format(GENERAL['bad_request'], reason)} status = 400 except Exception as e: content = {'msg': GENERAL['internal_error'] + str(e)} status = 500 else: content = matches # type: ignore status = 200 else: content = {'msg': GENERAL['bad_request']} status = 400 return content, status
def test_create_job(tables, client, new_user): new_user.save() job_name = 'TestJob' data = { 'name': job_name, 'description': 'testDescription', 'userId': 1, 'startAt': DateUtils.stringify_datetime_to_api_format(datetime.datetime.utcnow() + timedelta(hours=5)), 'stopAt': DateUtils.stringify_datetime_to_api_format(datetime.datetime.utcnow() + timedelta(hours=10)) } resp = client.post(ENDPOINT, headers=HEADERS, data=json.dumps(data)) resp_json = json.loads(resp.data.decode('utf-8')) assert resp.status_code == HTTPStatus.CREATED assert resp_json['job']['id'] is not None assert resp_json['job']['name'] == job_name assert Job.get(int(resp_json['job']['id'])) is not None
def business_create(task: Dict[str, Any]) -> Tuple[Content, HttpStatusCode]: """Creates new Task db record. Fields which require to be of datetime type are explicitly converted here. """ try: new_task = Task( user_id=task['userId'], hostname=task['hostname'], command=task['command'], # TODO Adjust API spec, optional fields spawn_at=DateUtils.try_parse_string(task.get('spawnAt')), terminate_at=DateUtils.try_parse_string(task.get('terminateAt'))) # assert all(task.values()), 'fields cannot be blank or null' new_task.save() except ValueError: # Invalid string format for datetime content, status = {'msg': GENERAL['bad_request']}, 422 except KeyError: # At least one of required fields was not present content, status = {'msg': GENERAL['bad_request']}, 422 except AssertionError as e: content, status = { 'msg': TASK['create']['failure']['invalid'].format(reason=e) }, 422 except Exception as e: log.critical(e) content, status = {'msg': GENERAL['internal_error']}, 500 else: content, status = { 'msg': TASK['create']['success'], 'task': new_task.as_dict() }, 201 finally: return content, status
def stop_at(self, value): if value is not None: self._stop_at = DateUtils.try_parse_string(value) if self._stop_at is None: log.error('Unsupported type (start_at={})'.format(value)) else: self._stop_at = None
def create(restriction: Dict[str, Any]) -> Tuple[Content, HttpStatusCode]: try: new_restriction = Restriction(name=restriction.get('name'), starts_at=restriction['startsAt'], is_global=restriction['isGlobal'], ends_at=DateUtils.try_parse_string( restriction.get('endsAt'))) new_restriction.save() except AssertionError as e: content = { 'msg': RESTRICTION['create']['failure']['invalid'].format(reason=e) } status = HTTPStatus.UNPROCESSABLE_ENTITY.value except Exception as e: content = {'msg': GENERAL['internal_error'] + str(e)} status = HTTPStatus.INTERNAL_SERVER_ERROR.value else: content = { 'msg': RESTRICTION['create']['success'], 'restriction': new_restriction.as_dict(include_groups=True, include_users=True, include_resources=True) } status = HTTPStatus.CREATED.value finally: return content, status
def test_update_not_owned_job(tables, client, new_admin_job): new_admin_job.save() data = { 'name': 'NewName', 'description': 'NewDescription', 'startAt': DateUtils.stringify_datetime_to_api_format(datetime.datetime.utcnow() + timedelta(hours=5)), 'stopAt': DateUtils.stringify_datetime_to_api_format(datetime.datetime.utcnow() + timedelta(hours=10)), } resp = client.put(ENDPOINT + '/{}'.format(new_admin_job.id), headers=HEADERS, data=json.dumps(data)) assert resp.status_code == HTTPStatus.FORBIDDEN
def test_update_job_to_stop_before_start(tables, client, new_job): new_job._status = JobStatus.running new_job.save() data = { 'name': 'NewName', 'description': 'NewDescription', 'startAt': DateUtils.stringify_datetime_to_api_format(datetime.datetime.utcnow() - timedelta(hours=5)), 'stopAt': DateUtils.stringify_datetime_to_api_format(datetime.datetime.utcnow() - timedelta(hours=10)), } resp = client.put(ENDPOINT + '/{}'.format(new_job.id), headers=HEADERS, data=json.dumps(data)) assert resp.status_code == HTTPStatus.UNPROCESSABLE_ENTITY
def start_at(self, value): if value is not None: self._start_at = DateUtils.try_parse_string(value) if self._start_at is None: log.error('Unsupported type (start_at={})'.format(value)) else: assert self._start_at > datetime.utcnow( ), 'Job start time must be in the future!' else: self._start_at = None
def test_update_future_reservation_start(tables, client, future_reservation, permissive_restriction): permissive_restriction.save() future_reservation.save() new_reservation_start = future_reservation.start + timedelta(hours=1) resp = client.put( ENDPOINT + '/' + str(future_reservation.id), headers=HEADERS, data=json.dumps({ 'start': DateUtils.stringify_datetime_to_api_format(new_reservation_start) })) resp_json = json.loads(resp.data.decode('utf-8')) assert resp.status_code == HTTPStatus.CREATED assert resp_json['reservation']['start'] == DateUtils.stringify_datetime( new_reservation_start) assert Reservation.get( future_reservation.id).start == new_reservation_start
def test_create_job_in_the_past(tables, client, new_user): new_user.save() job_name = 'TestJob' data = { 'name': job_name, 'description': 'testDescription', 'userId': 1, 'startAt': DateUtils.stringify_datetime_to_api_format(datetime.datetime.utcnow() - timedelta(hours=5)), 'stopAt': DateUtils.stringify_datetime_to_api_format(datetime.datetime.utcnow() + timedelta(hours=10)) } resp = client.post(ENDPOINT, headers=HEADERS, data=json.dumps(data)) assert resp.status_code == HTTPStatus.UNPROCESSABLE_ENTITY
def test_create_reservation_starting_in_the_past(tables, client, new_user, permissive_restriction): new_user.save() # Create a resource and assign it to the restriction resource = Resource(id='0123456789012345678901234567890123456789') resource.save() past_time = datetime.datetime.now() - timedelta(minutes=2) end_time = past_time + timedelta(hours=1) data = { 'title': 'Test reservation', 'description': 'Test reservation', 'resourceId': '0123456789012345678901234567890123456789', 'userId': new_user.id, 'start': DateUtils.stringify_datetime_to_api_format(past_time), 'end': DateUtils.stringify_datetime_to_api_format(end_time) } resp = client.post(ENDPOINT, headers=HEADERS, data=json.dumps(data)) assert resp.status_code == HTTPStatus.FORBIDDEN
def test_create_reservation(tables, client, new_user, permissive_restriction): new_user.save() # Create a resource and assign it to the restriction resource = Resource(id='0123456789012345678901234567890123456789') resource.save() # Try to create reservation for a period that the user has access to, as specified by the restriction. # Should succeed. now = datetime.datetime.now() data = { 'title': 'Test reservation', 'description': 'Test reservation', 'resourceId': '0123456789012345678901234567890123456789', 'userId': new_user.id, 'start': DateUtils.stringify_datetime_to_api_format(now), 'end': DateUtils.stringify_datetime_to_api_format(now + timedelta(hours=1)) } resp = client.post(ENDPOINT, headers=HEADERS, data=json.dumps(data)) resp_json = json.loads(resp.data.decode('utf-8')) assert resp.status_code == HTTPStatus.CREATED assert Reservation.get(resp_json['reservation']['id']) is not None
def create(reservation: Dict[str, Any]) -> Tuple[Content, HttpStatusCode]: try: new_reservation = Reservation(title=reservation['title'], description=reservation['description'], resource_id=reservation['resourceId'], user_id=reservation['userId'], start=reservation['start'], end=reservation['end']) if not is_admin() and not __is_reservation_owner(new_reservation): raise ForbiddenException( "Cannot reserve resources in another user's name") reservation_start = DateUtils.try_parse_string(new_reservation.start) request_time_limit = timedelta(minutes=1) starts_in_the_future = (reservation_start + request_time_limit) >= datetime.utcnow() if not is_admin() and not starts_in_the_future: raise ForbiddenException("Cannot reserve resources in the past") user = User.get(get_jwt_identity()) if not ReservationVerifier.is_reservation_allowed( user, new_reservation): raise ForbiddenException("Reservation not allowed") new_reservation.save() content = { 'msg': RESERVATION['create']['success'], 'reservation': new_reservation.as_dict() } status = 201 except ForbiddenException as e: content = { 'msg': RESERVATION['create']['failure']['forbidden'].format(reason=e) } status = 403 except AssertionError as e: content = { 'msg': RESERVATION['create']['failure']['invalid'].format(reason=e) } status = 422 except Exception as e: print(e) content = {'msg': GENERAL['internal_error'] + str(e)} status = 500 finally: return content, status
def test_update_active_reservation_start_forbidden(tables, client, active_reservation, permissive_restriction): permissive_restriction.save() active_reservation.save() new_reservation_start = active_reservation.start + timedelta(hours=1) resp = client.put( ENDPOINT + '/' + str(active_reservation.id), headers=HEADERS, data=json.dumps({ 'start': DateUtils.stringify_datetime_to_api_format(new_reservation_start) })) assert resp.status_code == HTTPStatus.FORBIDDEN
def business_update( id: TaskId, new_values: Dict[str, Any]) -> Tuple[Content, HttpStatusCode]: """Updates certain fields of a Task db record, see `allowed_fields`.""" allowed_fields = {'command', 'hostname', 'spawnAt', 'terminateAt'} try: assert set(new_values.keys()).issubset( allowed_fields), 'invalid field is present' task = Task.get(id) for field_name, new_value in new_values.items(): if field_name in {'spawnAt', 'terminateAt'}: new_value = DateUtils.try_parse_string(new_value) field_name = snakecase(field_name) # Check that every field matches assert (field_name is not None) and hasattr( task, field_name), 'task has no {} field'.format(field_name) setattr(task, field_name, new_value) task.save() except NoResultFound: content, status = {'msg': TASK['not_found']}, 404 except ValueError: # Invalid string format for datetime content, status = {'msg': GENERAL['bad_request']}, 422 except AssertionError as e: content, status = { 'msg': TASK['update']['failure']['assertions'].format(reason=e) }, 422 except Exception as e: log.critical(e) content, status = {'msg': GENERAL['internal_error']}, 500 else: content, status = { 'msg': TASK['update']['success'], 'task': task.as_dict() }, 201 finally: return content, status
def start(self, value): self._start = DateUtils.try_parse_string(value) if self._start is None: log.error('Unsupported type (start={})'.format(value))
def _serialize(self, field): if type(field) == datetime.datetime: return DateUtils.stringify_datetime(field) else: return field
def end(self, value): self._end = DateUtils.try_parse_string(value) if self._end is None: log.error('Unsupported type (end={})'.format(value))
def created_at(self, value: str): self._created_at = DateUtils.try_parse_string(value) if self._created_at is None: log.error('Unsupported type (created_at={})'.format(value))
def ends_at(self, value: str): self._ends_at = DateUtils.try_parse_string(value)