def test_get_parent_type(self):
        # todo: consider unusual parent situations (?)
        rfo = rfSchema.getSchemaObject(
            'Example.Example', '/redfish/v1/$metadata#Example.Example')
        assert rfo

        self.assertTrue(rfo.getParentType('Example.v1_0_0.Example'))
        self.assertTrue(rfo.getParentType('Example.Example'))
    def test_get_from_reference(self):
        # todo: consider no schema, consider service
        rfo = rfSchema.getSchemaObject(
            'Example.Example', '/redfish/v1/$metadata#Example.Example')
        assert rfo

        self.assertTrue(
            rfo.getSchemaFromReference('ExampleResource') is not None)
        self.assertTrue(
            rfo.getSchemaFromReference('ExampleResource.v1_0_0') is not None)
        self.assertTrue(
            rfo.getSchemaFromReference('ExampleResource.v1_0_1') is not None)
        self.assertFalse(
            rfo.getSchemaFromReference('Resource.v1_0_1') is not None)
    def test_get_type_tag(self):
        rfo = rfSchema.getSchemaObject(
            'Example.Example', '/redfish/v1/$metadata#Example.Example')
        assert rfo

        self.assertTrue(
            rfo.getTypeTagInSchema('Example.v1_0_0.Example') is not None)
        self.assertFalse(
            rfo.getTypeTagInSchema('Example.v1_0_9.Example') is not None)
        self.assertTrue(rfo.getTypeTagInSchema('Example.Example') is not None)
        self.assertFalse(
            rfo.getTypeTagInSchema('Example.v1_0_0.ExampleEnum') is not None)
        self.assertTrue(
            rfo.getTypeTagInSchema('Example.v1_0_0.ExampleEnum', 'EnumType')
            is not None)
        self.assertTrue(rfo.getTypeTagInSchema('Example') is not None)
    def test_highest_type(self):
        rfo = rfSchema.getSchemaObject(
            'Example.Example', '/redfish/v1/$metadata#Example.Example')
        assert rfo

        self.assertTrue(
            rfo.getHighestType('Example.Example') == 'Example.v1_7_0.Example')
        self.assertTrue(
            rfo.getHighestType('Example.Example', 'Newark.v1_2_1') ==
            'Example.v1_2_1.Example')
        self.assertTrue(
            rfo.getHighestType('Example.Example', 'Newark.v0_0_0') ==
            'Example.Example')
        self.assertTrue(
            rfo.getHighestType('Example.Example', 'Newark') ==
            'Example.v1_7_0.Example')
        self.assertTrue(
            rfo.getHighestType('Example.Links') == 'Example.v1_7_0.Links')
        self.assertTrue(
            rfo.getHighestType('Example.Links', 'Example.v1_1_1') ==
            'Example.v1_0_0.Links')
    def __init__(self,
                 name: str,
                 uri: str,
                 jsondata: dict,
                 typename: str,
                 context: str,
                 parent=None,
                 isComplex=False,
                 forceType=False):
        self.initiated = False
        self.parent = parent
        self.uri, self.name = uri, name
        self.rtime = 0
        self.status = -1
        self.isRegistry = False
        self.errorIndex = {}

        oem = config.get('oemcheck', True)

        # Check if this is a Registry resource
        parent_type = parent.typename if parent is not None and parent is not None else None
        if parent_type is not None and getType(
                parent_type) == 'MessageRegistryFile':
            traverseLogger.debug('{} is a Registry resource'.format(self.uri))
            self.isRegistry = True
            self.context = None
            context = None

        # Check if we provide a valid json
        self.jsondata = jsondata

        traverseLogger.debug("payload: {}".format(
            json.dumps(self.jsondata, indent=4, sort_keys=True)))

        if not isinstance(self.jsondata, dict):
            traverseLogger.error("Resource no longer a dictionary...")
            raise ValueError('This Resource is no longer a Dictionary')

        # Check for @odata.id (todo: regex)
        odata_id = self.jsondata.get('@odata.id')
        if odata_id is None and not isComplex:
            if self.isRegistry:
                traverseLogger.debug(
                    '{}: @odata.id missing, but not required for Registry resource'
                    .format(self.uri))
            else:
                traverseLogger.log(
                    'SERVICE',
                    '{}: Json does not contain @odata.id'.format(self.uri))

        # Get our real type (check for version)
        acquiredtype = typename if forceType else jsondata.get(
            '@odata.type', typename)
        if acquiredtype is None:
            traverseLogger.error(
                '{}:  Json does not contain @odata.type or NavType'.format(
                    uri))
            raise ValueError
        if acquiredtype is not typename and isComplex:
            context = None

        if typename is not None:
            if not oem and 'OemObject' in typename:
                acquiredtype = typename

        if currentService:
            if not oem and 'OemObject' in acquiredtype:
                pass
            else:
                if jsondata.get('@odata.type') is not None:
                    currentService.metadata.add_service_namespace(
                        getNamespace(jsondata.get('@odata.type')))
                if jsondata.get('@odata.context') is not None:
                    # add the namespace to the set of namespaces referenced by this service
                    ns = getNamespace(
                        jsondata.get('@odata.context').split('#')[-1])
                    if '/' not in ns and not ns.endswith('$entity'):
                        currentService.metadata.add_service_namespace(ns)

        # Provide a context for this (todo: regex)
        if context is None:
            context = self.jsondata.get('@odata.context')
            if context is None:
                context = createContext(acquiredtype)
                if self.isRegistry:
                    # If this is a Registry resource, @odata.context is not required; do our best to construct one
                    traverseLogger.debug(
                        '{}: @odata.context missing from Registry resource; constructed context {}'
                        .format(acquiredtype, context))
                elif isComplex:
                    pass
                else:
                    traverseLogger.debug(
                        '{}:  Json does not contain @odata.context'.format(
                            uri))

        self.context = context

        # Get Schema object
        self.schemaObj = rfSchema.getSchemaObject(acquiredtype, self.context)

        if self.schemaObj is None:
            traverseLogger.error(
                "ResourceObject creation: No schema XML for {} {} {}".format(
                    typename, acquiredtype, self.context))
            raise ValueError

        # Use string comprehension to get highest type
        if acquiredtype is typename and not forceType:
            acquiredtype = self.schemaObj.getHighestType(typename, parent_type)
            if not isComplex:
                traverseLogger.debug(
                    'No @odata.type present, assuming highest type {} {}'.
                    format(typename, acquiredtype))

        # Check if we provide a valid type (todo: regex)
        self.typename = acquiredtype
        typename = self.typename

        self.initiated = True

        # get our metadata
        metadata = currentService.metadata if currentService else None

        self.typeobj = rfSchema.getTypeObject(typename, self.schemaObj)

        self.propertyList = self.typeobj.getProperties(
            self.jsondata, topVersion=getNamespace(typename))
        propertyList = [prop.payloadName for prop in self.propertyList]

        # get additional
        self.additionalList = []
        propTypeObj = self.typeobj
        if propTypeObj.propPattern is not None and len(
                propTypeObj.propPattern) > 0:
            prop_pattern = propTypeObj.propPattern.get('Pattern', '.*')
            prop_type = propTypeObj.propPattern.get('Type',
                                                    'Resource.OemObject')

            regex = re.compile(prop_pattern)
            for key in [
                    k for k in self.jsondata
                    if k not in propertyList and regex.fullmatch(k)
            ]:
                val = self.jsondata.get(key)
                value_obj = rfSchema.PropItem(propTypeObj.schemaObj,
                                              propTypeObj.fulltype,
                                              key,
                                              val,
                                              customType=prop_type)
                self.additionalList.append(value_obj)

        if config['uricheck'] and self.typeobj.expectedURI is not None:
            my_id = self.jsondata.get('Id')
            self.errorIndex[
                'bad_uri_schema_uri'] = not self.typeobj.compareURI(
                    uri, my_id)
            self.errorIndex[
                'bad_uri_schema_odata'] = not self.typeobj.compareURI(
                    odata_id, my_id)

            if self.errorIndex['bad_uri_schema_uri']:
                traverseLogger.error('{}: URI not in Redfish.Uris: {}'.format(
                    uri, self.typename))
                if my_id != uri.rsplit('/', 1)[-1]:
                    traverseLogger.error(
                        'Id {} in payload doesn\'t seem to match URI'.format(
                            my_id))
            else:
                traverseLogger.debug('{} in Redfish.Uris: {}'.format(
                    uri, self.typename))

            if self.errorIndex['bad_uri_schema_odata']:
                traverseLogger.error(
                    '{}: odata_id not in Redfish.Uris: {}'.format(
                        odata_id, self.typename))
                if my_id != uri.rsplit('/', 1)[-1]:
                    traverseLogger.error(
                        'Id {} in payload doesn\'t seem to match URI'.format(
                            my_id))
            else:
                traverseLogger.debug('{} in Redfish.Uris: {}'.format(
                    odata_id, self.typename))

        # get annotation
        successService, annotationProps = getAnnotations(
            metadata, self.jsondata)
        if successService:
            self.additionalList.extend(annotationProps)

        # list illegitimate properties together
        self.unknownProperties = [
            k for k in self.jsondata if k not in propertyList +
            [prop.payloadName
             for prop in self.additionalList] and '@odata' not in k
        ]

        self.links = OrderedDict()

        sample = config.get('sample')
        linklimits = config.get('linklimits', {})
        self.links.update(
            self.typeobj.getLinksFromType(self.jsondata, self.context,
                                          self.propertyList, oem, linklimits,
                                          sample))

        self.links.update(
            getAllLinks(self.jsondata,
                        self.additionalList,
                        self.schemaObj,
                        context=context,
                        linklimits=linklimits,
                        sample_size=sample,
                        oemCheck=oem))
def createResourceObject(name,
                         uri,
                         jsondata=None,
                         typename=None,
                         context=None,
                         parent=None,
                         isComplex=False):
    """
    Factory for resource object, move certain work here
    """
    traverseLogger.debug('Creating ResourceObject {} {} {}'.format(
        name, uri, typename))
    oem = config.get('oemcheck', True)

    # Create json from service or from given
    original_jsondata = jsondata
    if jsondata is None and not isComplex:
        success, jsondata, status, rtime = callResourceURI(uri)
        traverseLogger.debug('{}, {}, {}'.format(success, jsondata, status))
        if not success:
            traverseLogger.error('{}:  URI could not be acquired: {}'.format(
                uri, status))
            return None
    else:
        success, jsondata, status, rtime = True, jsondata, -1, 0

    if not isinstance(jsondata, dict):
        if not isComplex:
            traverseLogger.error("Resource no longer a dictionary...")
        else:
            traverseLogger.debug("ComplexType does not have val")
        return success, None, status
        return None

    acquiredtype = jsondata.get('@odata.type', typename)
    if acquiredtype is None:
        traverseLogger.error(
            '{}:  Json does not contain @odata.type or NavType'.format(uri))
        return None

    if typename is not None:
        if not oem and 'OemObject' in typename:
            acquiredtype = typename

    original_context = context
    if context is None:
        context = jsondata.get('@odata.context')
        if context is None:
            context = createContext(acquiredtype)

    # Get Schema object
    schemaObj = rfSchema.getSchemaObject(acquiredtype, context)
    if schemaObj is None:
        traverseLogger.warn(
            "ResourceObject creation: No schema XML for {} {}".format(
                acquiredtype, context))
        return None

    forceType = False
    # Check if this is a Registry resource
    parent_type = parent.typename if parent is not None and parent.typeobj is not None else None

    # get highest type if type is invalid
    if schemaObj.getTypeTagInSchema(acquiredtype) is None:
        if schemaObj.getTypeTagInSchema(
                getNamespaceUnversioned(acquiredtype)) is not None:
            traverseLogger.warn(
                "Namespace version of type appears missing from SchemaXML, attempting highest type: {}"
                .format(acquiredtype))
            acquiredtype = schemaObj.getHighestType(acquiredtype, parent_type)
            typename = acquiredtype
            traverseLogger.warn("New namespace: {}".format(typename))
            forceType = True
        else:
            traverseLogger.warn(
                "getResourceObject: Namespace appears nonexistent in SchemaXML: {} {}"
                .format(acquiredtype, context))
            return None

    # check odata.id if it corresponds
    odata_id = jsondata.get('@odata.id', '')

    currentType = acquiredtype
    baseObj = schemaObj
    success = True
    allTypes = []
    while currentType not in allTypes and success:
        allTypes.append(currentType)
        success, baseObj, currentType = baseObj.getParentType(
            currentType, 'EntityType')
        traverseLogger.debug('success = {}, currentType = {}'.format(
            success, currentType))

    uri_item = uri
    scheme, netloc, path, params, query, fragment = urlparse(uri_item)
    scheme, netloc, path, params, query, fragment_odata = urlparse(odata_id)

    if 'Resource.Resource' in allTypes:
        if fragment is '':
            if original_jsondata is None:
                traverseLogger.debug(
                    'Acquired resource OK {}'.format(uri_item))
            else:
                traverseLogger.debug(
                    'Acquired resource thru AutoExpanded means {}'.format(
                        uri_item))
                traverseLogger.info(
                    'Regetting resource from URI {}'.format(uri_item))
                new_payload = createResourceObject(name, uri_item, None,
                                                   typename, context, parent,
                                                   isComplex)
                if new_payload is None:
                    traverseLogger.warn(
                        'Could not acquire resource, reverting to original payload...'
                    )
        else:
            if original_jsondata is None:
                traverseLogger.warn(
                    'Acquired Resource.Resource type with fragment, could cause issues  {}'
                    .format(uri_item))
            else:
                traverseLogger.warn(
                    'Found uri with fragment, which Resource.Resource types do not use {}'
                    .format(uri_item))
        if fragment_odata is '':
            pass
        else:
            traverseLogger.warn(
                '@odata.id should not have a fragment'.format(odata_id))

    elif 'Resource.ReferenceableMember' in allTypes:
        if fragment is not '':
            pass
        else:
            traverseLogger.warn(
                'No fragment, but ReferenceableMembers require it {}'.format(
                    uri_item))
        if fragment_odata is not '':
            pass
        else:
            traverseLogger.warn(
                '@odata.id should have a fragment'.format(odata_id))

    newResource = ResourceObj(name,
                              uri,
                              jsondata,
                              typename,
                              original_context,
                              parent,
                              isComplex,
                              forceType=forceType)
    newResource.rtime = rtime
    newResource.status = status

    return newResource
 def test_schema_object(self):
     # todo: consider no schema, consider service
     rfo = rfSchema.getSchemaObject(
         'Example.Example', '/redfish/v1/$metadata#Example.Example')
     self.assertTrue(rfo is not None, 'SchemaObject not created')