示例#1
0
    def _get_formatted_query(self, fields, limit, order_by, offset):
        """
        Converts the query to a ServiceNow-interpretable format
        :return: ServiceNow query
        """

        if not isinstance(order_by, list):
            raise InvalidUsage("Argument order_by must be of type list()")

        if not isinstance(fields, list):
            raise InvalidUsage("Argument fields must be of type list()")

        if isinstance(self.query, query.QueryBuilder):
            sysparm_query = str(self.query)
        elif isinstance(self.query, dict):  # Dict-type query
            try:
                items = self.query.iteritems()  # Python 2
            except AttributeError:
                items = self.query.items()  # Python 3

            sysparm_query = '^'.join(
                ['%s=%s' % (field, value) for field, value in items])
        elif isinstance(self.query, str):  # String-type query
            sysparm_query = self.query
        else:
            raise InvalidUsage("Query must be instance of %s, %s or %s" %
                               (query.QueryBuilder, str, dict))

        for field in order_by:
            if field[0] == '-':
                sysparm_query += "^ORDERBYDESC%s" % field[1:]
            else:
                sysparm_query += "^ORDERBY%s" % field

        params = {'sysparm_query': sysparm_query}
        params.update(self.request_params)

        if limit is not None:
            params.update({
                'sysparm_limit': limit,
                'sysparm_suppress_pagination_header': True
            })

        if offset is not None:
            params.update({'sysparm_offset': offset})

        if len(fields) > 0:
            params.update({'sysparm_fields': ",".join(fields)})

        return params
示例#2
0
    def attach(self, file):
        """Attaches the queried record with `file` and returns the response after validating the response

        :param file: File to attach to the record
        :raise:
            :NoResults: if query returned no results
            :NotImplementedError: if query returned more than one result (currently not supported)
        :return: The attachment record metadata
        """
        try:
            result = self.get_one()
            if 'sys_id' not in result:
                raise NoResults()
        except MultipleResults:
            raise NotImplementedError(
                'Attaching a file to multiple records is not supported')
        except NoResults:
            raise NoResults(
                'Attempted to attach file to a non-existing record')

        if not os.path.isfile(file):
            raise InvalidUsage(
                "Attachment '%s' must be an existing regular file" % file)

        response = self.session.post(
            self._get_attachment_url('upload'),
            data={
                'table_name': self.table,
                'table_sys_id': result['sys_id'],
                'file_name': ntpath.basename(file)
            },
            files={'file': open(file, 'rb')},
            headers={'content-type': None}  # Temporarily override header
        )
        return self._get_content(response)
示例#3
0
    def update(self, payload):
        """Updates the queried record with `payload` and returns the updated record after validating the response

        :param payload: Payload to update the record with
        :raise:
            :NoResults: if query returned no results
            :NotImplementedError: if query returned more than one result (currently not supported)
        :return: The updated record
        """
        try:
            result = self.get_one()
            if 'sys_id' not in result:
                raise NoResults()
        except MultipleResults:
            raise NotImplementedError(
                "Update of multiple records is not supported")
        except NoResults as e:
            e.args = ('Cannot update a non-existing record', )
            raise

        if not isinstance(payload, dict):
            raise InvalidUsage("Update payload must be of type dict")

        response = self.session.put(
            self._get_table_url(sys_id=result['sys_id']),
            data=json.dumps(payload))
        return self._get_content(response)
示例#4
0
    def __init__(self,
                 instance=None,
                 host=None,
                 user=None,
                 password=None,
                 raise_on_empty=True,
                 request_params=None,
                 use_ssl=True,
                 session=None):
        """Creates a client ready to handle requests

        :param instance: instance name, used to construct host
        :param host: host can be passed as an alternative to instance
        :param user: username
        :param password: password
        :param raise_on_empty: whether or not to raise an exception on 404 (no matching records)
        :param request_params: request params to send with requests
        :param use_ssl: Enable or disable SSL
        :param session: a requests session object
        """

        if (host and instance) is not None:
            raise InvalidUsage(
                "Instance and host are mutually exclusive, you cannot use both."
            )

        if type(use_ssl) is not bool:
            raise InvalidUsage("Argument use_ssl must be of type bool")

        if type(raise_on_empty) is not bool:
            raise InvalidUsage("Argument raise_on_empty must be of type bool")

        if not (host or instance):
            raise InvalidUsage("You must supply an instance name or a host")

        if not (user and password) and not session:
            raise InvalidUsage(
                "You must provide either username and password or a session object"
            )
        elif (user and session) is not None:
            raise InvalidUsage(
                "Provide either username and password or a session, not both.")

        if request_params is not None:
            if isinstance(request_params, dict):
                self.request_params = request_params
            else:
                raise InvalidUsage("Request params must be of type dict")
        else:
            self.request_params = {}

        self.instance = instance
        self.host = host
        self._user = user
        self._password = password
        self.raise_on_empty = raise_on_empty
        self.use_ssl = use_ssl

        self.base_url = self._get_base_url()
        self.session = self._get_session(session)
示例#5
0
    def set_token(self, token):
        """Validates token and creates a pysnow compatible session

        :param token: dict containing the information required to create an OAuth2Session
        """

        expected_keys = set(("token_type", "refresh_token", "access_token", "scope", "expires_in", "expires_at"))
        if not expected_keys <= set(token):
            raise InvalidUsage("Token should contain a dictionary obtained from generate_token()")

        self.token = token
        self.session = self._get_oauth_session()
示例#6
0
文件: client.py 项目: poinea/pysnow
    def __init__(self,
                 instance,
                 user=None,
                 password=None,
                 raise_on_empty=True,
                 default_payload=None,
                 session=None):
        """Sets configuration and creates a session object used in `Request` later on

        You must either provide a username and password or a requests session.
        If you provide a requests session it must handle the authentication.
        For example, providing a session can be used to do OAuth authentication.

        :param instance: instance name, used to resolve FQDN in `Request`
        :param user: username
        :param password: password
        :param raise_on_empty: whether or not to raise an exception on 404 (no matching records)
        :param default_payload: default payload to send with all requests, set i.e. 'sysparm_limit' here
        :param session: a requests session object
        """

        if ((not (user and password)) and not session) or ((user or password)
                                                           and session):
            raise InvalidUsage(
                "You must either provide username and password or a session")

        # Connection properties
        self.instance = instance
        self._user = user
        self._password = password
        self.raise_on_empty = raise_on_empty
        self.default_payload = default_payload or dict()

        # Sets default payload for all requests, i.e. sysparm_limit, sysparm_offset etc
        if not isinstance(self.default_payload, dict):
            raise InvalidUsage("Payload must be of type dict")

        # Create new session object
        self.session = self._get_session(session)
示例#7
0
    def _get_formatted_query(self, fields, limit=None):
        """
        Converts the query to a ServiceNow-interpretable format
        :return: ServiceNow query
        """

        if isinstance(self.query, query.QueryBuilder):
            sysparm_query = str(self.query)
        elif isinstance(self.query, dict):  # Dict-type query
            try:
                items = self.query.iteritems()  # Python 2
            except AttributeError:
                items = self.query.items()  # Python 3

            sysparm_query = '^'.join(
                ['%s=%s' % (field, value) for field, value in items])
        elif isinstance(self.query, str):  # String-type query
            sysparm_query = self.query
        else:
            raise InvalidUsage("Query must be instance of %s, %s or %s" %
                               (query.QueryBuilder, str, dict))

        result = {'sysparm_query': sysparm_query}
        result.update(self.default_payload)

        if limit is not None:
            result.update({
                'sysparm_limit': limit,
                'sysparm_suppress_pagination_header': True
            })

        if len(fields) > 0:
            if isinstance(fields, list):
                result.update({'sysparm_fields': ",".join(fields)})
            else:
                raise InvalidUsage("You must pass the fields as a list")

        return result
示例#8
0
    def clone(self, reset_fields=list()):
        """Clones the queried record

        :param reset_fields: Fields to reset
        :raise:
            :NoResults: if query returned no results
            :NotImplementedError: if query returned more than one result (currently not supported)
            :UnexpectedResponse: informs the user about what likely went wrong
        :return: The cloned record
        """

        if not isinstance(reset_fields, list):
            raise InvalidUsage("reset_fields must be a list() of fields")

        try:
            response = self.get_one()
            if 'sys_id' not in response:
                raise NoResults()
        except MultipleResults:
            raise NotImplementedError(
                'Cloning multiple records is not supported')
        except NoResults as e:
            e.args = ('Cannot clone a non-existing record', )
            raise

        payload = {}

        # Iterate over fields in the result
        for field in response:
            # Ignore fields in reset_fields
            if field in reset_fields:
                continue

            item = response[field]
            # Check if the item is of type dict and has a sys_id ref (value)
            if isinstance(item, dict) and 'value' in item:
                payload[field] = item['value']
            else:
                payload[field] = item

        try:
            return self.insert(payload)
        except UnexpectedResponse as e:
            if e.status_code == 403:
                # User likely attempted to clone a record without resetting a unique field
                e.args = (
                    'Unable to create clone. Make sure unique fields has been reset.',
                )
            raise
示例#9
0
    def upload(self, sys_id, file_path, name=None, multipart=False):
        """Attaches a new file to the provided record

        :param sys_id: the sys_id of the record to attach the file to
        :param file_path: local absolute path of the file to upload
        :param name: custom name for the uploaded file (instead of basename)
        :param multipart: whether or not to use multipart
        :return: the inserted record
        """

        if not isinstance(multipart, bool):
            raise InvalidUsage('Multipart must be of type bool')

        resource = self.resource

        if name is None:
            name = os.path.basename(file_path)

        resource.parameters.add_custom({
            'table_name': self.table_name,
            'table_sys_id': sys_id,
            'file_name': name
        })

        data = open(file_path, 'rb').read()
        headers = {}

        if multipart:
            headers["Content-Type"] = "multipart/form-data"
            path_append = '/upload'
        else:
            headers["Content-Type"] = magic.from_file(
                file_path, mime=True) if HAS_MAGIC else "text/plain"
            path_append = '/file'

        return resource.request(method='POST',
                                data=data,
                                headers=headers,
                                path_append=path_append)
示例#10
0
    def __init__(self, client_id=None, client_secret=None, token_updater=None, *args, **kwargs):
        if not (client_secret and client_id):
            raise InvalidUsage('You must supply a client_id and client_secret')

        if token_updater is None:
            warnings.warn("No token_updater was supplied to OauthClient, you won't be notified of refreshes")

        if kwargs.get('session') or kwargs.get('user'):
            warnings.warn('pysnow.OAuthClient manages sessions internally, '
                          'provided user / password credentials or sessions will be ignored.')

        # Forcibly set session, user and password
        kwargs['session'] = OAuth2Session(client=LegacyApplicationClient(client_id=client_id))
        kwargs['user'] = None
        kwargs['password'] = None

        super(OAuthClient, self).__init__(*args, **kwargs)

        self.token_updater = token_updater
        self.client_id = client_id
        self.client_secret = client_secret

        self.token_url = "%s/oauth_token.do" % self._get_base_url()
示例#11
0
    def __init__(self,
                 instance=None,
                 host=None,
                 user=None,
                 password=None,
                 raise_on_empty=True,
                 default_payload=None,
                 request_params=None,
                 use_ssl=True,
                 session=None):
        """Sets configuration and creates a session object used in `Request` later on

        You must either provide a username and password or a requests session.
        If you provide a requests session it must handle the authentication.
        For example, providing a session can be used to do OAuth authentication.

        :param instance: instance name, used to construct host
        :param host: host can be passed as an alternative to instance
        :param user: username
        :param password: password
        :param raise_on_empty: whether or not to raise an exception on 404 (no matching records)
        :param default_payload: deprecated, use request_params
        :param request_params: request params to send with requests
        :param use_ssl: Enable or disable SSL
        :param session: a requests session object
        """

        # We allow either host or instance, not both
        if (host and instance) is not None:
            raise InvalidUsage("Got both 'instance' and 'host'. Panic.")

        if ((not (user and password)) and not session) or ((user or password)
                                                           and session):
            raise InvalidUsage(
                "You must either provide username and password or a session")

        # Check if host or instance was passed
        if instance:
            self.host = "%s.service-now.com" % instance
        elif host:
            self.host = host
        else:
            raise InvalidUsage(
                "You must pass 'instance' or 'host' to create a client")

        # Check if SSL was requested
        if use_ssl is True:
            self.base_url = "https://%s" % self.host
        elif use_ssl is False:
            self.base_url = "http://%s" % self.host
        else:
            raise InvalidUsage("use_ssl: boolean value expected")

        # Connection properties
        self.instance = instance
        self._user = user
        self._password = password
        self.raise_on_empty = raise_on_empty
        self.request_params = self.default_payload = {}

        # default_payload is deprecated, let user know
        if default_payload is not None:
            warnings.warn(
                "default_payload is deprecated, please use request_params instead",
                DeprecationWarning)
            self.request_params = self.default_payload = default_payload

        if request_params is not None:
            self.request_params = request_params

        # Sets request parameters for requests
        if not isinstance(self.request_params, dict):
            raise InvalidUsage("Payload must be of type dict")

        # Create new session object
        self.session = self._get_session(session)