Exemplo n.º 1
0
    def nav(self, nav_property, key):
        """Navigates to given navigation property and returns the EntitySetProxy"""

        try:
            navigation_property = self._entity_set.entity_type.nav_proprty(nav_property)
        except KeyError:
            raise PyODataException('Navigation property {} is not declared in {} entity type'.format(
                nav_property, self._entity_set.entity_type))

        # Get entity set of navigation property
        association_info = navigation_property.association_info
        association_set = self._service.schema.association_set_by_association(
            association_info.name)

        navigation_entity_set = None
        for entity_set in association_set.end_roles:
            if association_set.end_roles[entity_set] == navigation_property.to_role.role:
                navigation_entity_set = self._service.schema.entity_set(entity_set)

        if not navigation_entity_set:
            raise PyODataException('No association set for role {}'.format(navigation_property.to_role))

        roles = navigation_property.association.end_roles
        if all((role.multiplicity != model.EndRole.MULTIPLICITY_ZERO_OR_MORE for role in roles)):
            return self._get_nav_entity(key, nav_property, navigation_entity_set)

        return EntitySetProxy(
            self._service,
            navigation_entity_set,
            nav_property,
            self._entity_set.name + key.to_key_string())
Exemplo n.º 2
0
    def __init__(self, entity_type, single_key=None, **args):

        self._logger = logging.getLogger(LOGGER_NAME)
        self._proprties = args
        self._entity_type = entity_type
        self._key = entity_type.key_proprties

        # single key does not need property name
        if single_key is not None:

            # check that entity type key consists of exactly one property
            if len(self._key) != 1:
                raise PyODataException(('Key of entity type {} consists of multiple properties {} '
                                        'and cannot be initialized by single value').format(
                                            self._entity_type.name, ', '.join([prop.name for prop in self._key])))

            # get single key property and format key string
            key_prop = self._key[0]
            args[key_prop.name] = single_key

            self._type = EntityKey.TYPE_SINGLE

            self._logger.debug(('Detected single property key, adding pair %s->%s to key'
                                'properties'), key_prop.name, single_key)
        else:
            for key_prop in self._key:
                if key_prop.name not in args:
                    raise PyODataException('Missing value for key property {}'.format(key_prop.name))

            self._type = EntityKey.TYPE_COMPLEX
Exemplo n.º 3
0
    def __new__(cls,
                url,
                connection,
                odata_version=ODATA_VERSION_2,
                namespaces=None,
                config: pyodata.v2.model.Config = None,
                metadata: str = None):
        """Create instance of the OData Client for given URL"""

        logger = logging.getLogger('pyodata.client')

        if odata_version == Client.ODATA_VERSION_2:

            # sanitize url
            url = url.rstrip('/') + '/'

            if metadata is None:
                metadata = _fetch_metadata(connection, url, logger)
            else:
                logger.info('Using static metadata')

            if config is not None and namespaces is not None:
                raise PyODataException(
                    'You cannot pass namespaces and config at the same time')

            if config is None:
                config = pyodata.v2.model.Config()

            if namespaces is not None:
                warnings.warn(
                    "Passing namespaces directly is deprecated. Use class Config instead",
                    DeprecationWarning)
                config.namespaces = namespaces

            # create model instance from received metadata
            logger.info('Creating OData Schema (version: %d)', odata_version)
            schema = pyodata.v2.model.MetadataBuilder(metadata,
                                                      config=config).build()

            # create service instance based on model we have
            logger.info('Creating OData Service (version: %d)', odata_version)
            service = pyodata.v2.service.Service(url,
                                                 schema,
                                                 connection,
                                                 config=config)

            return service

        raise PyODataException(
            f'No implementation for selected odata version {odata_version}')
Exemplo n.º 4
0
        def changeset_handler(changeset, parts):
            """Gets changeset response from HTTP response"""

            logging.getLogger(LOGGER_NAME).debug('Changeset handler called for changeset %s', changeset.id)

            result = []

            # check if changeset response consists of parts, this is important
            # to distinguish cases when server responds with single HTTP response
            # for whole request
            if not isinstance(parts[0], list):
                # raise error (even for successfull status codes) since such changeset response
                # always means something wrong happened on server
                response = ODataHttpResponse.from_string(parts[0])
                raise HttpError('Changeset cannot be processed due to single response received, status code: {}'.format(
                    response.status_code), response)

            for part, req in zip(parts, changeset.requests):
                logging.getLogger(LOGGER_NAME).debug('Changeset handler is processing part %s for request %s', part,
                                                     req)

                if isinstance(req, MultipartRequest):
                    raise PyODataException('Changeset cannot contain nested multipart content')

                # part represents single request, we have to parse
                # content (without checking Content type for binary/http)
                response = ODataHttpResponse.from_string(part[0])

                result.append(req.handler(response))

            return result
Exemplo n.º 5
0
    def parameter(self, name, value):
        '''Sets value of parameter.'''

        # check if param is valid (is declared in metadata)
        try:
            param = self._function_import.get_parameter(name)

            # add parameter as custom query argument
            self.custom(param.name, param.typ.traits.to_literal(value))
        except KeyError:
            raise PyODataException('Function import {0} does not have pararmeter {1}'
                                   .format(self._function_import.name, name))

        return self
Exemplo n.º 6
0
    def set(self, **kwargs):
        """Set properties to be changed."""

        self._logger.info(kwargs)

        for key, val in kwargs.items():
            try:
                self._entity_type.proprty(key)
            except KeyError:
                raise PyODataException(
                    'Property {} is not declared in {} entity type'.format(key, self._entity_type.name))

            self._values[key] = val

        return self
Exemplo n.º 7
0
    def __new__(cls, url, connection, odata_version=ODATA_VERSION_2):
        """Create instance of the OData Client for given URL"""

        logger = logging.getLogger('pyodata.client')

        if odata_version == Client.ODATA_VERSION_2:

            # sanitize url
            url = url.rstrip('/') + '/'

            # download metadata
            logger.info('Fetching metadata')
            resp = connection.get(url + '$metadata')

            logger.debug(
                'Retrieved the response:\n%s\n%s', '\n'.join(
                    (f'H: {key}: {value}'
                     for key, value in resp.headers.items())), resp.content)

            if resp.status_code != 200:
                raise HttpError(
                    'Metadata request failed, status code: {}, body:\n{}'.
                    format(resp.status_code, resp.content), resp)

            mime_type = resp.headers['content-type']
            if not any((typ in ['application/xml', 'text/xml']
                        for typ in mime_type.split(';'))):
                raise HttpError(
                    'Metadata request did not return XML, MIME type: {}, body:\n{}'
                    .format(mime_type, resp.content), resp)

            # create model instance from received metadata
            logger.info('Creating OData Schema (version: %d)', odata_version)
            schema = pyodata.v2.model.schema_from_xml(resp.content)

            # create service instance based on model we have
            logger.info('Creating OData Service (version: %d)', odata_version)
            service = pyodata.v2.service.Service(url, schema, connection)

            return service

        raise PyODataException(
            'No implementation for selected odata version {}'.format(
                odata_version))
Exemplo n.º 8
0
    def _build_values(entity_type, entity):
        """Recursively converts a dictionary of values where some of the values
           might be another entities (navigation properties) into the internal
           representation.
        """

        if isinstance(entity, list):
            return [EntityCreateRequest._build_values(entity_type, item) for item in entity]

        values = {}
        for key, val in entity.items():
            try:
                entity_type.proprty(key)
            except KeyError:
                try:
                    nav_prop = entity_type.nav_proprty(key)
                    val = EntityCreateRequest._build_values(nav_prop.typ, val)
                except KeyError:
                    raise PyODataException('Property {} is not declared in {} entity type'.format(
                        key, entity_type.name))

            values[key] = val

        return values
Exemplo n.º 9
0
    def __init__(self, service, entity_set, entity_type, proprties=None, entity_key=None):
        self._logger = logging.getLogger(LOGGER_NAME)
        self._service = service
        self._entity_set = entity_set
        self._entity_type = entity_type
        self._key_props = entity_type.key_proprties
        self._cache = dict()
        self._entity_key = entity_key

        self._logger.debug('New entity proxy instance of type %s from properties: %s', entity_type.name, proprties)

        # cache values of individual properties if provided
        if proprties is not None:

            # first, cache values of direct properties
            for type_proprty in self._entity_type.proprties():
                if type_proprty.name in proprties:
                    if proprties[type_proprty.name] is not None:
                        self._cache[type_proprty.name] = type_proprty.typ.traits.from_json(proprties[type_proprty.name])
                    else:
                        # null value is in literal form for now, convert it to python representation
                        self._cache[type_proprty.name] = type_proprty.typ.traits.from_literal(
                            type_proprty.typ.null_value)

            # then, assign all navigation properties
            for prop in self._entity_type.nav_proprties:

                if prop.name in proprties:

                    # entity type of navigation property
                    prop_etype = prop.to_role.entity_type

                    # cache value according to multiplicity
                    if prop.to_role.multiplicity in \
                            [model.EndRole.MULTIPLICITY_ONE,
                             model.EndRole.MULTIPLICITY_ZERO_OR_ONE]:

                        # cache None in case we receive nothing (null) instead of entity data
                        if proprties[prop.name] is None:
                            self._cache[prop.name] = None
                        else:
                            self._cache[prop.name] = EntityProxy(service, None, prop_etype, proprties[prop.name])

                    elif prop.to_role.multiplicity == model.EndRole.MULTIPLICITY_ZERO_OR_MORE:
                        # default value is empty array
                        self._cache[prop.name] = []

                        # if there are no entities available, received data consists of
                        # metadata properties only.
                        if 'results' in proprties[prop.name]:

                            # available entities are serialized in results array
                            for entity in proprties[prop.name]['results']:
                                self._cache[prop.name].append(EntityProxy(service, None, prop_etype, entity))
                    else:
                        raise PyODataException('Unknown multiplicity {0} of association role {1}'
                                               .format(prop.to_role.multiplicity, prop.to_role.name))

        # build entity key if not provided
        if self._entity_key is None:
            # try to build key from available property values
            try:
                # if key seems to be simple (consists of single property)
                if len(self._key_props) == 1:
                    self._entity_key = EntityKey(entity_type, self._cache[self._key_props[0].name])
                else:
                    # build complex key
                    self._entity_key = EntityKey(entity_type, **self._cache)
            except KeyError:
                pass
            except PyODataException:
                pass
Exemplo n.º 10
0
    def __new__(cls,
                url,
                connection,
                odata_version=ODATA_VERSION_2,
                namespaces=None,
                config: pyodata.v2.model.Config = None):
        """Create instance of the OData Client for given URL"""

        logger = logging.getLogger('pyodata.client')

        if odata_version == Client.ODATA_VERSION_2:

            # sanitize url
            url = url.rstrip('/') + '/'

            # download metadata
            logger.info('Fetching metadata')
            resp = connection.get(url + '$metadata')

            logger.debug(
                'Retrieved the response:\n%s\n%s', '\n'.join(
                    (f'H: {key}: {value}'
                     for key, value in resp.headers.items())), resp.content)

            if resp.status_code != 200:
                raise HttpError(
                    'Metadata request failed, status code: {}, body:\n{}'.
                    format(resp.status_code, resp.content), resp)

            mime_type = resp.headers['content-type']
            if not any((typ in ['application/xml', 'text/xml']
                        for typ in mime_type.split(';'))):
                raise HttpError(
                    'Metadata request did not return XML, MIME type: {}, body:\n{}'
                    .format(mime_type, resp.content), resp)

            if config is not None and namespaces is not None:
                raise PyODataException(
                    'You cannot pass namespaces and config at the same time')

            if config is None:
                config = pyodata.v2.model.Config()

            if namespaces is not None:
                warnings.warn(
                    "Passing namespaces directly is deprecated. Use class Config instead",
                    DeprecationWarning)
                config.namespaces = namespaces

            # create model instance from received metadata
            logger.info('Creating OData Schema (version: %d)', odata_version)
            schema = pyodata.v2.model.MetadataBuilder(resp.content,
                                                      config=config).build()

            # create service instance based on model we have
            logger.info('Creating OData Service (version: %d)', odata_version)
            service = pyodata.v2.service.Service(url, schema, connection)

            return service

        raise PyODataException(
            'No implementation for selected odata version {}'.format(
                odata_version))