Пример #1
0
    def post(self):
        """
        Resume a previously paused tasks/jobs
        :param id: id of task
        :type id: str
        :keyword user_id: user_id of tasks' owner
        :type user_id: int
        :rtype json

        :Example:
            task_ids = {
                'ids': [fasdff12n22m2jnr5n6skf,ascv3h5k1j43k6k8k32k345jmn,123n23n4n43m2kkcj53vdsxc]
            }
            headers = {'Authorization': 'Bearer <access_token>', 'Content-Type' : 'application/json'}
            response = requests.post(API_URL + '/v1/tasks/resume/', headers=headers, data=json.dumps(task_ids))

        .. Response::
            {
               "message":"Tasks have been resumed successfully"
            }

        .. Status:: 200 (OK)
                    207 (Not all paused)
                    404 (Bad Request)
                    500 (Internal Server Error)

        """
        user_id = request.user.id
        raise_if_scheduler_not_running()
        try:
            req_data = request.get_json()
        except Exception:
            raise InvalidUsage("Bad Request, data should be in JSON")
        task_ids = req_data['ids'] if 'ids' in req_data and isinstance(
            req_data['ids'], list) else None
        if not task_ids:
            raise InvalidUsage("Bad Request, No data in ids", error_code=400)
        # Filter jobs that are not None
        valid_tasks = [
            task for task in task_ids if scheduler.get_job(job_id=task)
        ]
        if valid_tasks:
            # Only keep jobs that belonged to the auth user
            valid_tasks = filter(
                lambda task_id: scheduler.get_job(job_id=task_id).args[0] ==
                user_id, valid_tasks)
            # Resume each job
            for _id in valid_tasks:
                scheduler.resume_job(job_id=_id)
            if len(valid_tasks) != len(task_ids):
                none_tasks = filter(
                    lambda task_id: scheduler.get_job(job_id=task_id) is None,
                    task_ids)
                return dict(message='Unable to resume %s tasks' %
                            len(none_tasks),
                            paused=valid_tasks,
                            not_found=none_tasks), 207
            return dict(message="Tasks have been successfully resumed")
        raise InvalidUsage('Bad request, invalid data in request',
                           error_code=400)
Пример #2
0
def validate_periodic_job(data):
    """
    Validate periodic job and check for missing or invalid data. If found then raise error
    :param data: JSON job post data
    """
    valid_data = dict()

    frequency = get_valid_integer_from_dict(data, 'frequency')
    start_datetime_obj = DatetimeUtils(
        get_valid_datetime_from_dict(data, 'start_datetime'))
    end_datetime = get_valid_datetime_from_dict(data, 'end_datetime')

    # If value of frequency is not integer or lesser than 1 hour then throw exception
    if frequency < SchedulerUtils.MIN_ALLOWED_FREQUENCY:
        raise InvalidUsage(
            'Invalid value of frequency. Value should be greater than or equal to '
            '%s' % SchedulerUtils.MIN_ALLOWED_FREQUENCY)
    valid_data.update({'frequency': frequency})

    # If job is not in 0-30 seconds in past or greater than current datetime.
    relative_end_datetime = end_datetime - datetime.timedelta(
        seconds=frequency)
    if not start_datetime_obj.is_in_future(neg_offset=REQUEST_TIMEOUT):
        raise InvalidUsage(
            "start_datetime and end_datetime should be in future.")
    elif start_datetime_obj.value > relative_end_datetime:
        raise InvalidUsage(
            "start_datetime should be less than (end_datetime - frequency)")

    valid_data.update({'start_datetime': start_datetime_obj.value})
    valid_data.update({'end_datetime': end_datetime})

    return valid_data
Пример #3
0
    def delete(self):
        """
        Deletes multiple tasks whose ids are given in list in request data.
        :param kwargs:
        :return:
        :Example:
            task_ids = {
                'ids': [fasdff12n22m2jnr5n6skf,ascv3h5k1j43k6k8k32k345jmn,123n23n4n43m2kkcj53vdsxc]
            }
            headers = {
                        'Authorization': 'Bearer <access_token>',
                        'Content-Type': 'application/json'
                       }
            data = json.dumps(task_ids)
            response = requests.delete(
                                        API_URL + '/v1/tasks/',
                                        data=data,
                                        headers=headers,
                                    )
        .. Response::
            {
                'message': '3 task have been removed successfully'
            }
        .. Status:: 200 (Resource deleted)
                    207 (Not all removed)
                    400 (Bad request)
                    500 (Internal Server Error)
        """

        user_id = request.user.id
        raise_if_scheduler_not_running()
        # get task_ids for tasks to be removed
        try:
            req_data = request.get_json()
        except Exception:
            raise InvalidUsage(
                "Bad Request, data should be in JSON",
                error_code=SchedulerServiceApiException.CODE_INVALID_USAGE)
        task_ids = req_data['ids'] if 'ids' in req_data and isinstance(
            req_data['ids'], list) else None
        if not task_ids:
            raise InvalidUsage("Bad request, No data in ids", error_code=400)
        # check if tasks_ids list is not empty
        removed = remove_tasks(task_ids, user_id=user_id)
        if len(removed) == len(task_ids):
            logger.info('Job with ids %s removed successfully.' % task_ids)
            return dict(message='%s Tasks removed successfully' % len(removed))

        # removed_jobs have valid jobs - filters in remove_tasks
        # job[1] contains job id
        removed_jobs = map(lambda job: job[1], removed)
        not_removed = list(set(task_ids) - set(removed_jobs))
        if not_removed:
            logger.info(
                'Job with ids %s removed successfully and unable to remove jobs %s'
                % (task_ids, not_removed))
            return dict(message='Unable to remove %s tasks' % len(not_removed),
                        removed=removed,
                        not_removed=not_removed), 207
Пример #4
0
    def post(self):
        """
        Pause tasks which are currently running.

        :keyword user_id: user_id of tasks' owner
        :type user_id: int
        :rtype json

        :Example:
            task_ids = {
                'ids': [fasdff12n22m2jnr5n6skf,ascv3h5k1j43k6k8k32k345jmn,123n23n4n43m2kkcj53vdsxc]
            }
            headers = {'Authorization': 'Bearer <access_token>', 'Content-Type' : 'application/json'}
            response = requests.post(API_URL + '/v1/tasks/pause/', headers=headers, data=json.dumps(task_ids))

        .. Response::
            {
               "message":"Tasks have been paused successfully"
            }

        .. Status:: 200 (OK)
                    400 (Bad Request)
                    207 (Not all Paused)
                    500 (Internal Server Error)

        """
        user_id = request.user.id
        raise_if_scheduler_not_running()
        try:
            req_data = request.get_json()
        except Exception:
            raise InvalidUsage("Bad Request, data should be in json")
        task_ids = req_data['ids'] if 'ids' in req_data and isinstance(
            req_data['ids'], list) else None
        if not task_ids:
            raise InvalidUsage("Bad request, No data in ids", error_code=400)
        # Filter tasks which are None and of the current user
        valid_tasks = filter(
            lambda task_id: scheduler.get_job(job_id=task_id) and scheduler.
            get_job(job_id=task_id).args[0] == user_id, task_ids)
        if valid_tasks:
            for _id in valid_tasks:
                scheduler.pause_job(job_id=_id)
            if len(valid_tasks) != len(task_ids):
                none_tasks = filter(
                    lambda task_id: scheduler.get_job(job_id=task_id) is None,
                    task_ids)
                return dict(message='Unable to pause %s tasks' %
                            len(none_tasks),
                            paused=valid_tasks,
                            not_found=none_tasks), 207

            return dict(message="Tasks have been successfully paused")
        raise InvalidUsage('Bad request, invalid data in request',
                           error_code=400)
Пример #5
0
def filter_jobs_using_task_category(tasks, task_category):
    """
    Filters jobs based on task_type
    :param tasks: APScheduler tasks
    :type tasks: list
    :param task_category: `user` or `general`
    :type task_category: str
    :return: Returns the user or general tasks in a list depending upon task_category
    """
    if not (isinstance(task_category, basestring)
            and task_category.lower() in [
                SchedulerUtils.CATEGORY_USER.lower(),
                SchedulerUtils.CATEGORY_GENERAL.lower()
            ]):
        raise InvalidUsage(
            "task_category should be of type string with value of "
            "`{0}` or `{1}`".format(SchedulerUtils.CATEGORY_USER,
                                    SchedulerUtils.CATEGORY_GENERAL))

    if task_category.lower() == SchedulerUtils.CATEGORY_GENERAL.lower():
        tasks = filter(
            lambda _task: _task.name != SchedulerUtils.RUN_JOB_METHOD_NAME,
            tasks)
    else:
        tasks = filter(
            lambda _task: _task.name == SchedulerUtils.RUN_JOB_METHOD_NAME,
            tasks)

    return tasks
Пример #6
0
def get_valid_url_from_dict(data, key):
    """
    Check if url is valid, if no, then raise invalid value exception
    """
    assert isinstance(data, dict)
    value = get_valid_data_from_dict(data, key)
    if not is_valid_url_format(value):
        raise InvalidUsage(error_message='Invalid value of %s.' % key)
    return value
Пример #7
0
def get_valid_integer_from_dict(data, key):
    """
    Check if integer is valid, if no, then raise invalid value exception
    """
    assert isinstance(data, dict)
    value = get_valid_data_from_dict(data, key)
    if not str(value).isdigit():
        raise InvalidUsage(
            error_message='Invalid value of %s. It should be integer' % key)
    return value
Пример #8
0
def get_valid_task_name_from_dict(data, key):
    """
    Check if the task_name value has alphanumeric, unique and allowed characters.
    Also `run_job` shouldn't be the task_name as its a default callback for every user based task
    :param data: dictionary consisting of acpscheduler tasks
    :type data: dict
    :param key: key to get from data dictionary
    :type key: str
    """
    assert isinstance(data, dict)
    value = str(get_valid_data_from_dict(data, key))
    general_msg = "Invalid value of %s %s. %s should be unique, alphanumeric and allowed characters are [-, _ ]. " \
                  "Note: `%s` is not allowed as a task name." % (key, value, key, SchedulerUtils.RUN_JOB_METHOD_NAME)
    if value.lower() == SchedulerUtils.RUN_JOB_METHOD_NAME.lower():
        raise InvalidUsage(error_message=general_msg)

    allowed_characters = ['-', '_']
    if any(c for c in value if not (c.isalnum() or c in allowed_characters)):
        raise InvalidUsage(error_message=general_msg)

    return value
Пример #9
0
def get_valid_data_from_dict(data, key):
    """
    Methods to Check if key exist and returns associated value
    :param data: json data with keyvalue pair
    :param key: key to check if missing or invalid value
    :return: value of associated key
    """
    assert isinstance(data, dict)
    try:
        value = data[key]
    except KeyError:
        raise InvalidUsage(error_message="Missing key: %s" % key)
    return value
Пример #10
0
def get_valid_datetime_from_dict(data, key):
    """
    Check if datetime is valid, if no, then raise invalid value exception
    """
    assert isinstance(data, dict)
    value = get_valid_data_from_dict(data, key)
    try:
        value = parse(value).replace(tzinfo=timezone('UTC'))
    except Exception:
        raise InvalidUsage(
            error_message=
            "Invalid value of %s %s. %s should be in datetime format" %
            (key, value, key))
    return value
Пример #11
0
def validate_one_time_job(data):
    """
    Validate one time job POST data.
    if run_datetime is already passed then raise error 500
    :param data:
    :return:
    """
    valid_data = dict()
    run_datetime_obj = DatetimeUtils(
        get_valid_datetime_from_dict(data, 'run_datetime'))
    valid_data.update({'run_datetime': run_datetime_obj.value})

    # If job is not in 0-30 seconds in past or greater than current datetime.
    if not run_datetime_obj.is_in_future(neg_offset=REQUEST_TIMEOUT):
        raise InvalidUsage(
            "Cannot schedule job of already passed time. run_datetime is in past"
        )
    return valid_data
Пример #12
0
def filter_paused_jobs(tasks, is_paused):
    """
    Filter paused jobs only from tasks list
    :param tasks: APScheduler tasks
    :type tasks: list
    :param is_paused: If `true` or `1`, then filter only paused jobs. If its false, filter running jobs.
    :type is_paused: object (bool, str, int)
    :return: Returns the paused or not running tasks in a list depending upon is_paused arg
    """
    if not str(is_paused).lower() in ['true', 'false', '1', '0']:
        raise InvalidUsage(
            "paused should be of type bool or int. If `true` or `1`, paused jobs will be "
            "returned and if `false` or `0`, then returns only running jobs")
    if str(is_paused).lower() in ["true", "1"]:
        tasks = filter(lambda _task: _task.next_run_time is None, tasks)
    else:
        tasks = filter(lambda _task: _task.next_run_time, tasks)

    return tasks
Пример #13
0
def dummy_request_method(_request):
    """
    Dummy endpoint to test GET, POST, DELETE request using celery
    :param _request:
    :return:
    """
    env_key = flask_app.config.get(TalentConfigKeys.ENV_KEY)
    if not (env_key == TalentEnvs.DEV or env_key == TalentEnvs.JENKINS):
        raise ForbiddenError("You are not authorized to access this endpoint.")

    user_id = _request.user.id
    try:
        task = _request.get_json()
    except BadRequest:
        raise InvalidUsage('Given data is not JSON serializable')

    # Post data param expired. If yes, then expire the token
    expired = task.get('expired', False)

    url = task.get('url', '')

    if expired:
        # Set the date in past to expire request oauth token.
        # This is to test that run_job method refresh token or not
        expiry = datetime.utcnow() - timedelta(days=5)
        expiry = expiry.strftime('%Y-%m-%d %H:%M:%S')

        # Expire oauth token and then pass it to run_job. And run_job should refresh token and send request to URL
        db.db.session.commit()
        token = Token.query.filter_by(user_id=_request.user.id).first()
        token.update(expires=expiry)
        run_job(user_id, _request.oauth_token, url, task.get('content-type', 'application/json'),
                task.get('post_data', dict()))
    else:
        try:
            # Try deleting the user if exist
            db.db.session.commit()
            test_user_id = task['test_user_id']
            test_user = User.query.filter_by(id=test_user_id).first()
            test_user.delete()
        except Exception as e:
            logger.exception(e.message)
Пример #14
0
def filter_jobs_using_task_type(tasks, task_type):
    """
    Filters jobs based on task_type
    :param tasks: APScheduler tasks
    :type tasks: list
    :param task_type: `periodic` or `one_time`
    :type task_type: str
    :return: Returns the periodic or one_time tasks in a list depending upon task_type arg
    """
    if not (isinstance(task_type, basestring) and task_type.lower() in [
            SchedulerUtils.ONE_TIME.lower(),
            SchedulerUtils.PERIODIC.lower()
    ]):
        raise InvalidUsage(
            "task_type should be of type string with value of `{0}` or `{1}`".
            format(SchedulerUtils.ONE_TIME, SchedulerUtils.PERIODIC))
    if task_type.lower() == SchedulerUtils.PERIODIC.lower():
        tasks = filter(
            lambda _task: isinstance(_task.trigger, IntervalTrigger), tasks)
    else:
        tasks = filter(lambda _task: isinstance(_task.trigger, DateTrigger),
                       tasks)

    return tasks
Пример #15
0
    def post(self):
        """
        This method takes data to create or schedule a task for scheduler.
        :Example:
            for interval or periodic schedule
            task = {
                "task_name": "custom-task",     # Field required only in case of general task
                "frequency": 3601,
                "task_type": "periodic",
                "start_datetime": "2015-12-05T08:00:00",
                "end_datetime": "2016-01-05T08:00:00",
                "url": "http://getTalent.com/sms/send/",
                "post_data": {
                    "campaign_name": "SMS Campaign",
                    "phone_number": "09230862348",
                    "smart_list_id": 123456,
                    "content": "text to be sent as sms"
                }
            }
            for one_time schedule
            task = {
                "task_name": "custom-task",     # Field required only in case of general task
                "task_type": "one_time",
                "run_datetime": "2015-12-05T08:00:00",
                "url": "http://getTalent.com/email/send/",
                "post_data": {
                    "campaign_name": "Email Campaign",
                    "email": "*****@*****.**",
                    "smart_list_id": 123456,
                    "content": "content to be sent as email"
                }
            }
            In case of authenticated user
            headers = {
                        'Authorization': 'Bearer <access_token>',
                        'Content-Type': 'application/json'
                       }
            In case of SECRET_KEY
            headers = {'Authorization': 'Bearer <access_token>',
                        'X-Talent-Server-Key-ID': '<secret_key>',
                        'Content-Type': 'application/json'
                        }
            data = json.dumps(task)
            response = requests.post(
                                        API_URL + '/v1/tasks/',
                                        data=data,
                                        headers=headers,
                                    )
        .. Response::
            {
                "id" : "5das76nbv950nghg8j8-33ddd3kfdw2"
            }
        .. Status:: 201 (Resource Created)
                    400 (Bad Request)
                    401 (Unauthorized to access getTalent)
                    500 (Internal Server Error)
        :return: id of created task
        """
        # get JSON post request data
        raise_if_scheduler_not_running()
        try:
            task = request.get_json()
        except Exception:
            raise InvalidUsage("Bad Request, data should be in json")

        task_id = schedule_job(task, request.user.id if request.user else None,
                               request.oauth_token)

        headers = {'Location': '/v1/tasks/%s' % task_id}
        response = json.dumps(dict(id=task_id))
        return ApiResponse(response, status=201, headers=headers)
Пример #16
0
    def get(self):
        """
        This action returns a list of apscheduler scheduled tasks.
        :return tasks_data: a dictionary containing list of tasks
        :rtype: json


        :Example (in case of pagination):
            By default, it will return 30 jobs (max)

            Case 1:

             >>> headers = {'Authorization': 'Bearer <access_token>'}
             >>> response = requests.get(API_URL + '/admin/v1/tasks?page=2', headers=headers)

            # Returns 30 jobs ranging from 30-59 ( zero-based index )

            Case 2:

             >>> headers = {'Authorization': 'Bearer <access_token>'}
             >>> response = requests.get(API_URL + '/admin/v1/tasks?page=2&per_page=35', headers=headers)

            # Returns 35 jobs ranging from 35-69 ( zero-based index )

        :Example:

        In case of task_category filter

             >>> headers = {'Authorization': 'Bearer <access_token>'}
             >>> response = requests.get(API_URL + '/admin/v1/tasks?task_category=general', headers=headers)

        In case of task_type filter

             >>> headers = {'Authorization': 'Bearer <access_token>'}
             >>> response = requests.get(API_URL + '/admin/v1/tasks?task_type=periodic', headers=headers)

        In case of jobs of a specific user

             >>> headers = {'Authorization': 'Bearer <access_token>'}
             >>> response = requests.get(API_URL + '/admin/v1/tasks?user_id=2', headers=headers)


        In case of paused or running jobs only

             >>> headers = {'Authorization': 'Bearer <access_token>'}
             # returns paused jobs only
             >>> response = requests.get(API_URL + '/admin/v1/tasks?paused=true', headers=headers)
             # returns running jobs only
             >>> response = requests.get(API_URL + '/admin/v1/tasks?paused=false', headers=headers)


        Response will be similar to get all tasks endpoint with few additional fields

        .. Response::

            {
                "tasks": [
                    {
                        "id": "5das76nbv950nghg8j8-33ddd3kfdw2",
                        "user_email": "*****@*****.**",
                        "task_type": "user",
                        "data":{
                            "url": "http://getTalent.com/sms/send/",
                            "request_method": "post",
                            "post_data": {
                                "phone_number": "09230862348",
                                "smart_list_id": 123456,
                                "content": "text to be sent as sms"
                                "some_other_kwarg": "abc",
                                "campaign_name": "SMS Campaign"
                            },
                        "frequency": 3601,      # in seconds
                        "start_datetime": "2015-11-05T08:00:00",
                        "end_datetime": "2015-12-05T08:00:00"
                        "next_run_datetime": "2015-11-05T08:20:30",
                        "task_type": "periodic"
                    }
               ]
            }


        .. Status:: 200 (OK)
                    400 (Invalid Usage)
                    401 (Unauthorized Error)
                    500 (Internal Server Error)

        """

        # In case of higher number of scheduled task running for a particular user and user wants to get only
        # a limited number of jobs by specifying page and per_page parameter, then return only specified jobs

        # Default per_page size
        default_per_page = 30

        # If user didn't specify page or per_page, then it should default to 1 and 30 respectively.
        page, per_page = get_pagination_params(
            request, default_per_page=default_per_page)

        raise_if_scheduler_not_running()
        tasks = scheduler.get_jobs()

        # Get all param filters
        user_id = request.args.get('user_id')
        paused = request.args.get('paused')
        task_type = request.args.get('task_type')
        task_category = request.args.get('task_category')

        # If user_id is given then only return jobs of that particular user
        if user_id:
            if not (str(user_id).isdigit() and int(user_id) > 0):
                raise InvalidUsage("user_id should be of type int")
            tasks = filter(lambda _task: _task.args[0] == int(user_id), tasks)

        # If paused is given then only return paused jobs.
        if paused:
            tasks = filter_paused_jobs(tasks, is_paused=paused)

        # If task_type is specified then return only specified `one_time` or `periodic` jobs
        if task_type:
            tasks = filter_jobs_using_task_type(tasks, task_type=task_type)

        # If task_category is given then return only specified `user` or `general` job
        if task_category:
            tasks = filter_jobs_using_task_category(
                tasks, task_category=task_category)

        # The following case should never occur. As the general jobs are independent of user. So, if user use such
        # a filter then raise Invalid usage api exception.
        if user_id and task_category == SchedulerUtils.CATEGORY_GENERAL:
            raise InvalidUsage(
                error_message=
                "user and task_category cannot be used together. General jobs are independent of users."
            )

        tasks_count = len(tasks)

        # If page is 1, and per_page is 30 then task_indices will look like list of integers e.g [0-29]
        task_indices = range((page - 1) * per_page, page * per_page)

        tasks = [
            serialize_task_admin(tasks[index]) for index in task_indices
            if index < tasks_count and tasks[index]
        ]

        tasks = [task for task in tasks if task]

        header = generate_pagination_headers(tasks_count, per_page, page)
        return ApiResponse(response=dict(tasks=tasks), headers=header)
Пример #17
0
def schedule_job(data, user_id=None, access_token=None):
    """
    Schedule job using POST data and add it to APScheduler. Which calls the callback method when job time comes
    :param data: the data like url, frequency, post_data, start_datetime and end_datetime of job which is required
    for creating job of APScheduler
    :param user_id: the user_id of user who is creating job
    :param access_token: CSRF access token for the sending post request to url with post_data
    :return:
    """
    # Validate json data
    try:
        validate(instance=data,
                 schema=base_job_schema,
                 format_checker=FormatChecker())
        if data.get('task_type') == SchedulerUtils.PERIODIC:
            validate(instance=data,
                     schema=periodic_task_job_schema,
                     format_checker=FormatChecker())
        elif data.get('task_type') == SchedulerUtils.ONE_TIME:
            validate(instance=data,
                     schema=one_time_task_job_schema,
                     format_checker=FormatChecker())
    except ValidationError as e:
        raise InvalidUsage(error_message="Schema validation error: %s" %
                           e.message)

    job_config = dict()
    job_config['post_data'] = data.get('post_data', dict())
    content_type = data.get('content-type', 'application/json')

    job_config['task_type'] = get_valid_data_from_dict(data, 'task_type')
    job_config['url'] = get_valid_url_from_dict(data, 'url')

    # Server to Server call. We check if a job with a certain 'task_name'
    # is already running as we only allow one such task to run at a time.
    # If there is already such task we raise an exception.
    if user_id is None:
        job_config['task_name'] = get_valid_task_name_from_dict(
            data, 'task_name')
        jobs = scheduler.get_jobs()
        jobs = filter(lambda task: task.name == job_config['task_name'], jobs)
        job_config['is_jwt_request'] = True
        # There should be a unique task named job. If a job already exist then it should raise error
        if jobs and len(jobs) == 1:
            raise TaskAlreadyScheduledError(
                'Task name %s is already scheduled' % jobs[0].name)
    else:
        job_config['task_name'] = None
        is_jwt_request = data.get('is_jwt_request')
        if is_jwt_request and (str(is_jwt_request).lower()
                               not in ['true', 'false']):
            raise InvalidUsage(
                'is_jwt_request(Optional) value is invalid. It should be either True or False'
            )
        job_config[
            'is_jwt_request'] = is_jwt_request if is_jwt_request and str(
                is_jwt_request).lower() == 'true' else None

    trigger = str(job_config['task_type']).lower().strip()
    request_method = data.get('request_method', 'post')

    callback_method = 'scheduler_service.modules.scheduler:run_job'

    # make a UUID based on the host ID and current time and pass it to add job method to fix race condition.
    # See run_job method above
    lock_uuid = str(uuid.uuid1()) + str(uuid.uuid4())

    if trigger == SchedulerUtils.PERIODIC:
        valid_data = validate_periodic_job(data)

        try:
            job = scheduler.add_job(
                callback_method,
                name=job_config['task_name'],
                trigger='interval',
                seconds=valid_data['frequency'],
                start_date=valid_data['start_datetime'],
                end_date=valid_data['end_datetime'],
                misfire_grace_time=SchedulerUtils.MAX_MISFIRE_TIME,
                args=[
                    user_id, access_token, job_config['url'], content_type,
                    job_config['post_data'],
                    job_config.get('is_jwt_request'), request_method
                ],
                kwargs=dict(lock_uuid=lock_uuid))
            # Due to request timeout delay, there will be a delay in scheduling job sometimes.
            # And if start time is passed due to this request delay, then job should be run
            job_start_time_obj = DatetimeUtils(valid_data['start_datetime'])
            if not job_start_time_obj.is_in_future():
                run_job(user_id,
                        access_token,
                        job_config['url'],
                        content_type,
                        job_config['post_data'],
                        job_config.get('is_jwt_request'),
                        kwargs=dict(lock_uuid=lock_uuid))
            logger.info(
                'schedule_job: Task has been added and will start at %s ' %
                valid_data['start_datetime'])
        except Exception as e:
            logger.error(e.message)
            raise JobNotCreatedError("Unable to create the job.")
        logger.info('CODE-VERONICA: job id: {} schedule {}'.format(
            job.id, lock_uuid))
        return job.id
    elif trigger == SchedulerUtils.ONE_TIME:
        valid_data = validate_one_time_job(data)
        try:
            job = scheduler.add_job(
                callback_method,
                name=job_config['task_name'],
                trigger='date',
                coalesce=True,
                run_date=valid_data['run_datetime'],
                misfire_grace_time=SchedulerUtils.MAX_MISFIRE_TIME,
                args=[
                    user_id, access_token, job_config['url'], content_type,
                    job_config['post_data'],
                    job_config.get('is_jwt_request'), request_method
                ],
                kwargs=dict(lock_uuid=lock_uuid))
            logger.info(
                'schedule_job: Task has been added and will run at %s ' %
                valid_data['run_datetime'])
            logger.info('CODE-VERONICA: job id: {} schedule {}'.format(
                job.id, lock_uuid))
            return job.id
        except Exception as e:
            logger.error(e.message)
            raise JobNotCreatedError(
                "Unable to create job. Invalid data given")
    else:
        raise TriggerTypeError(
            "Task type not correct. Please use either %s or %s as task type." %
            (SchedulerUtils.ONE_TIME, SchedulerUtils.PERIODIC))