def test_rest_client_get(self, mock_call):
        """Test get"""

        auth = mock.create_autospec(Credentials)
        rest_client.get(auth, "http://test", {})
        mock_call.assert_called_with(auth,
                                     'GET',
                                     "http://test",
                                     headers={},
                                     params=None)

        rest_client.get(auth, "http://test", {}, params={'a':1})
        mock_call.assert_called_with(auth,
                                     'GET',
                                     "http://test",
                                     headers={},
                                     params={'a':1})

        mock_call.return_value.json.side_effect = ValueError("Value Error!")
        with self.assertRaises(RestCallException):
            rest_client.get(auth, "http://test", {})

        mock_call.side_effect = RestCallException(None, "Boom!", None)
        with self.assertRaises(RestCallException):
            rest_client.get(auth, "http://test", {})
    def test_rest_client_get(self, mock_call):
        """Test get"""

        auth = mock.create_autospec(Credentials)
        rest_client.get(auth, "http://test", {})
        mock_call.assert_called_with(auth,
                                     'GET',
                                     "http://test",
                                     headers={},
                                     params=None)

        rest_client.get(auth, "http://test", {}, params={'a': 1})
        mock_call.assert_called_with(auth,
                                     'GET',
                                     "http://test",
                                     headers={},
                                     params={'a': 1})

        mock_call.return_value.json.side_effect = ValueError("Value Error!")
        with self.assertRaises(RestCallException):
            rest_client.get(auth, "http://test", {})

        mock_call.side_effect = RestCallException(None, "Boom!", None)
        with self.assertRaises(RestCallException):
            rest_client.get(auth, "http://test", {})
    def list_files(self):
        """
        Lists the users files.
        This refers to files loaded by the user (sometimes referred to as
        assets) as distinct from task or job outputs generated during
        task processing.

        :Returns:
            - A :class:`.Response` object containing a list of files as
              dictionaries, with data:
              ``['id','name','lastModifiedBy','lastModifiedTime','link']``
            - If the call failed or if the response is incomplete/malformed
              a :class:`.Response` object with a :class:`.RestCallException`.
        """
        self._log.debug("list_files, no params")
        url = self.url("files")

        try:
            resp = rest_client.get(self._auth, url, self.headers)

        except RestCallException as exp:
            return Response(False, exp)

        if "files" not in resp or not isinstance(resp["files"], list):
            return Response(False, RestCallException(KeyError, "files key not in response message", resp))

        return Response(True, resp["files"])
    def get_pool(self, pool_id=None, url=None):
        """
        Gets information about a pool.
        Pool info can be retrieved by supplying **either** the pool's ID
        **or** a URL to the pool. If both are supplied, URL is used.

        :Kwargs:
            - pool_id (str): ID of the pool on which info is requested.
            - url (str): A complete URL to the pool info.

        :Returns:
            - A :class:`.Response` object containing the pool details as a
              dictionary, if successful. Otherwise the Response will
              contain the :exc:`.RestCallException`.
            - :class:`.RestCallException` if neither pool ID or URL are
              supplied.
            - :class:`.RestCallException` if pool details dictionary is
              malformed / missing necessary keys
        """
        self._log.debug("get_pool, pool_id={0}, url={1}".format(pool_id, url))
        if not url and pool_id:
            url = self.url("pools/{poolid}").format(poolid=pool_id)

        elif not url and not pool_id:
            return Response(False, RestCallException(AttributeError, "Either pool_id or url must be set", None))

        try:
            get_resp = rest_client.get(self._auth, url, self.headers)

        except RestCallException as exp:
            return Response(False, exp)

        else:
            return Response(True, get_resp)
    def list_output_files(self, job_id):
        """Lists the intermediate output files produced during a job.

        :Args:
            - job_id (str): The ID of the job whose outputs will be listed.

        :Returns:
            - A list of the outputs represented as dictionaries, each with the
              'name' and 'type' of the output as well as a download 'link'.
              Contained in a :class:`.Response`. If the call failed, Response
              will contain the :class:`.RestCallException`.
        """
        self._log.debug("list_output_files, job_id={0}".format(job_id))
        url = self.url("jobs/{jobid}/outputs/files").format(jobid=job_id)

        try:
            get_resp = rest_client.get(self._auth, url, self.headers)

        except RestCallException as exp:
            return Response(False, exp)

        outputs = []
        if not "outputs" in get_resp or not isinstance(get_resp["outputs"], list):

            return Response(False, RestCallException(KeyError, "outputs key not in response message", get_resp))

        for output in get_resp["outputs"]:
            outputs.append(
                {
                    "name": output.get("name"),
                    "link": output.get("link", {"href": None}).get("href"),
                    "type": output.get("kind"),
                }
            )
        return Response(True, outputs)
    def list_outputs(self, job_id):
        """
        Lists the output files produced by a job.
        This method will only list final outputs of the job, created by a
        Merge Task.
        To retrieve a list of all files created by all tasks of the job use
        :meth:`.list_output_files()`.
        Can be used to ascertain the output filenames before calling the
        generic output download URL.

        :Args:
            - job_id (str): ID of the job whose outputs will be listed.

        :Returns:
            - A list of the outputs represented as dictionaries, each with
              the 'name' and 'type' of the output as well as a download
              'link'. Contained in a :class:`.Response`. If the call failed,
              Response will contain the :class:`.RestCallException`.
        """
        self._log.debug("list_outputs, job_id={0}".format(job_id))
        url = self.url("jobs/{jobid}/outputs").format(jobid=job_id)

        try:
            get_resp = rest_client.get(self._auth, url, self.headers)

        except RestCallException as exp:
            return Response(False, exp)

        outputs = []
        if not "jobOutputs" in get_resp or not isinstance(get_resp["jobOutputs"], list):

            return Response(False, RestCallException(KeyError, "jobOutputs key not in response message", get_resp))

        # Reformat output dictionary to be more manageable
        for output in get_resp["jobOutputs"]:
            outputs.append(
                {
                    "name": output.get("name"),
                    "link": output.get("link", {"href": None}).get("href"),
                    "type": output.get("kind"),
                }
            )
        return Response(True, outputs)
    def get_log(self, job_id, start=None, max_lines=100):
        """
        Gets log messages for a job.
        These are the Batch Apps system logs, rather than those of the
        application.

        :Args:
            - job_id (str): The ID of the job on which to download the logs.

        :Kwargs:
            - start (str): The start time from which the logs will be
              downloaded. If not specified, the default is from the
              beginning of the job.
            - max_lines (int): The max number of logging messages to retrieve.
              The default is 100. If set to ``None``, all messages from start
              time will be retrieved.

        :Returns:
            - A :class:`.Response` object with a dictionary containing the
              timestamp of the most recent message returned and a list of
              the log messages, represented as dictionaries, with the message
              text, timestamp and task id that the message applies to.
            - If the call failed, the response contains the
              :class:`.RestCallException`.
        """
        self._log.debug("get_log, job_id={0}, start={1}, max_lines={2}".format(job_id, start, max_lines))

        url = self.url("jobs/{jobid}/log").format(jobid=job_id)
        get_params = {}
        if start:
            get_params["since"] = str(start)
        if max_lines:
            get_params["maxResults"] = int(max_lines)

        try:
            get_resp = rest_client.get(self._auth, url, self.headers, params=get_params)

        except RestCallException as exp:
            return Response(False, exp)

        else:
            # TODO: Check for specific keys here.
            return Response(True, get_resp)
    def list_pools(self):
        """Lists the users pools.

        :Returns:
            - :class:`.Response` object containing success of call. If
              successful, the ``Response.result`` will contain a list of
              pool dictionaries. If failed, ``Response.result`` will
              hold the :exc:`.RestCallException`.
        """
        self._log.debug("list_pools, no params")

        url = self.url("pools")

        try:
            get_resp = rest_client.get(self._auth, url, self.headers)

        except RestCallException as exp:
            return Response(False, exp)

        else:
            return Response(True, get_resp)
    def get_job(self, job_id=None, url=None):
        """
        Gets information about a job.
        Job info can be retrieved by supplying **either** the job's ID
        **or** a URL to the job. If both are supplied, URL is used.

        :Kwargs:
            - job_id (str): ID of the job on which info is requested.
            - url (str): A complete URL to the job info.

        :Returns:
            - A :class:`.Response` object containing the job details as a
              dictionary, if successful. Otherwise the Response will
              contain the :exc:`.RestCallException`.

        :Raises:
            - :class:`.RestCallException` if neither job ID or URL are
              supplied.
            - :class:`.RestCallException` if job details dictionary is
              malformed / missing necessary keys
        """
        self._log.debug("get_job, job_id={0}, url={1}".format(job_id, url))
        if not url and job_id:
            url = self.url("jobs/{jobid}").format(jobid=job_id)

        elif not url and not job_id:
            return Response(False, RestCallException(AttributeError, "Either job_id or url must be set", None))

        try:
            get_resp = rest_client.get(self._auth, url, self.headers)

        except RestCallException as exp:
            return Response(False, exp)

        else:
            if utils.valid_keys(get_resp, ["id", "name", "type"]):
                return Response(True, get_resp)
            return Response(False, RestCallException(KeyError, "incorrectly formatted job response", get_resp))
    def list_task_outputs(self, job_id, task):
        """Lists the output files produced by a task.

        :Args:
            - job_id (str): The ID of the job the task outputs belong to.
            - task (int, str): The ID of the task whose outputs will be listed.

        :Returns:
            - A list of the outputs represented as dictionaries, each with the
              'name' and 'type' of the output as well as a download 'link'.
              Contained in a :class:`.Response`. If the call failed,
              Response will contain the :class:`.RestCallException`.
        """
        self._log.debug("list_task_outputs, job_id={0}, " "task={1}".format(job_id, task))

        url = self.url("jobs/{jobid}/tasks/{taskid}/outputs/files")
        url = url.format(url, jobid=job_id, taskid=task)

        try:
            resp = rest_client.get(self._auth, url, self.headers)

        except RestCallException as exp:
            return Response(False, exp)

        outputs = []
        if "outputs" not in resp or not isinstance(resp["outputs"], list):
            return Response(False, RestCallException(KeyError, "outputs key not in response message", resp))

        for output in resp["outputs"]:
            outputs.append(
                {
                    "name": output.get("name"),
                    "link": output.get("link", {"href": None}).get("href"),
                    "type": output.get("kind"),
                }
            )
        return Response(True, outputs)
    def list_jobs(self, index=0, per_call=10, name=None):
        """Lists the users jobs.

        :Kwargs:
            - index (int): The starting index from which the list of jobs will
              be returned. The default is 0, i.e. return all jobs from the
              start.
            - per_call (int): The number of job entries from ``index`` to
              return. The default is 10.
            - name (str): Return only the jobs whose name contains the given
              string. The default is None.

        :Returns:
            - :class:`.Response` object containing success of call. If
              successful, the ``Response.result`` will contain a list of
              jobs as dictionaries. If failed, ``Response.result`` will
              hold the :exc:`.RestCallException`.
        """
        self._log.debug("list_jobs, index={0}, per_call={1}, name={2}".format(index, per_call, name))

        url = self.url("jobs")
        req_set = {"maxResults": per_call, "startIndex": index}

        if name:
            req_set["nameContains"] = str(name)

        try:
            get_resp = rest_client.get(self._auth, url, self.headers, params=req_set)

        except RestCallException as exp:
            return Response(False, exp)

        else:
            if "jobs" in get_resp:
                return Response(True, get_resp)

            return Response(False, RestCallException(KeyError, "Key not in response message", get_resp))
    def list_tasks(self, job_id=None, url=None):
        """
        List the tasks of a job.
        Either ``job_id`` *or* ``url`` must be set. If both are set ``url``
        will be used.

        :Kwargs:
            - job_id (str): ID of of the job to list the tasks for.
              The default is None.
            - url (str): Direct URL to the task list of the job
              (supplied by :meth:`.BatchAppsApi.get_job()`)

        :Returns:
            - A :class:`.Response` object containing the list of task
              dictionaries if the call is successful.
            - A :class:`.Response` object containing the
              :class:`.RestCallException` is the call failed.
        """
        self._log.debug("list_tasks, job_id={0}, url={1}".format(job_id, url))
        if not url and job_id:
            url = self.url("jobs/{jobid}/tasks").format(jobid=job_id)

        elif not url and not job_id:
            return Response(False, RestCallException(AttributeError, "Either job_id or url must get set", None))

        try:
            resp = rest_client.get(self._auth, url, self.headers)

        except RestCallException as exp:
            return Response(False, exp)

        else:
            if "tasks" not in resp or not isinstance(resp["tasks"], list):
                return Response(False, RestCallException(KeyError, "tasks key not in response message", resp))

            return Response(True, resp["tasks"])