def test_extract_schema_type_component(self):
     expected_mapping = {
         "component1Prop": "boolean",
         "testComponent": "boolean",
         "component1PropReadonly": "boolean",
         "component1Prop2": "boolean",
     }
     template = Template(
         load_json(FileNames.central_property_validation_template_file))
     for key, val in expected_mapping.items():
         schema = template.get_schema(key, is_component=True)
         schema_type = extract_schema_type(schema)
         assert schema_type == val
def create_device_template(
    cmd,
    app_id: str,
    device_template_id: str,
    payload: dict,
    token: str,
    central_dns_suffix=CENTRAL_ENDPOINT,
) -> Template:
    """
    Create a device template in IoTC

    Args:
        cmd: command passed into az
        app_id: name of app (used for forming request URL)
        device_template_id: case sensitive device template id,
        payload: see example payload available in
            <repo-root>/azext_iot/tests/central/json/device_template_int_test.json
            or check here for more information
            https://docs.microsoft.com/en-us/rest/api/iotcentral/devicetemplates
        token: (OPTIONAL) authorization token to fetch device details from IoTC.
            MUST INCLUDE type (e.g. 'SharedAccessToken ...', 'Bearer ...')
        central_dns_suffix: {centralDnsSuffixInPath} as found in docs

    Returns:
        device: dict
    """

    url = "https://{}.{}/{}/{}".format(app_id, central_dns_suffix, BASE_PATH,
                                       device_template_id)
    headers = _utility.get_headers(token, cmd, has_json_payload=True)

    response = requests.put(url, headers=headers, json=payload)
    return Template(_utility.try_extract_result(response))
    def test_validate_properties_name_miss_under_interface(
        self, mock_device_svc, mock_device_template_svc
    ):

        # setup
        mock_device_template_svc.get_device_template.return_value = Template(
            self._duplicate_property_template
        )

        monitor = PropertyMonitor(
            cmd=None,
            app_id=app_id,
            device_id=device_id,
            token=None,
            central_dns_suffix=None,
        )

        # invalid interface / property
        definition = {"definition": "test_definition"}

        issues = monitor._validate_payload_against_entities(
            definition, list(definition.keys())[0], Severity.warning,
        )

        assert (
            issues[0].details
            == "Device is sending data that has not been defined in the device template."
            " Following capabilities have NOT been defined in the device template '['definition']'."
            " Following capabilities have been defined in the device template (grouped by interface)"
            " '{'urn:sampleApp:groupOne_bz:2': ['addRootProperty', 'addRootPropertyReadOnly', 'addRootProperty2'],"
            " 'urn:sampleApp:groupOne_bz:_rpgcmdpo:1': ['Model', 'Version', 'TotalStorage'],"
            " 'urn:sampleApp:groupTwo_bz:myxqftpsr:2': ['Model', 'Manufacturer'],"
            " 'urn:sampleApp:groupThree_bz:myxqftpsr:2': ['Manufacturer', 'Version', 'Model', 'OsName']}'. "
        )
def get_device_template(
    cmd,
    app_id: str,
    device_template_id: str,
    token: str,
    central_dns_suffix=CENTRAL_ENDPOINT,
) -> Template:
    """
    Get a specific device template from IoTC

    Args:
        cmd: command passed into az
        device_template_id: case sensitive device template id,
        app_id: name of app (used for forming request URL)
        token: (OPTIONAL) authorization token to fetch device details from IoTC.
            MUST INCLUDE type (e.g. 'SharedAccessToken ...', 'Bearer ...')
        central_dns_suffix: {centralDnsSuffixInPath} as found in docs

    Returns:
        device: dict
    """
    url = "https://{}.{}/{}/{}".format(app_id, central_dns_suffix, BASE_PATH,
                                       device_template_id)
    headers = _utility.get_headers(token, cmd)

    response = requests.get(url, headers=headers)
    return Template(_utility.try_extract_result(response))
Exemple #5
0
    def test_validate_against_bad_template_should_not_throw(self):
        # setup
        device_template = "an_unparseable_template"

        properties = MessageProperties(content_encoding=self.encoding,
                                       content_type=self.content_type)
        message = Message(
            body=json.dumps(self.bad_dcm_payload).encode(),
            properties=properties,
            annotations={
                common_parser.DEVICE_ID_IDENTIFIER: self.device_id.encode()
            },
            application_properties=_encode_app_props(self.app_properties),
        )
        args = CommonParserArguments(properties=["all"])
        parser = self._create_parser(device_template=device_template,
                                     message=message,
                                     args=args)

        # haven't found a better way to force the error to occur within parser
        parser._central_template_provider.get_device_template = lambda x: Template(
            device_template)

        # act
        parsed_msg = parser.parse_message()

        # verify
        assert parsed_msg["event"]["payload"] == self.bad_dcm_payload
        assert parsed_msg["event"]["origin"] == self.device_id

        expected_details = strings.device_template_not_found(
            "Could not parse iot central device template.")

        _validate_issues(parser, Severity.error, 1, 1, [expected_details])
def list_device_templates(
    cmd,
    app_id: str,
    token: str,
    central_dns_suffix=CENTRAL_ENDPOINT,
) -> List[Template]:
    """
    Get a list of all device templates in IoTC

    Args:
        cmd: command passed into az
        app_id: name of app (used for forming request URL)
        token: (OPTIONAL) authorization token to fetch device details from IoTC.
            MUST INCLUDE type (e.g. 'SharedAccessToken ...', 'Bearer ...')
        central_dns_suffix: {centralDnsSuffixInPath} as found in docs

    Returns:
        device: dict
    """

    url = "https://{}.{}/{}".format(app_id, central_dns_suffix, BASE_PATH)
    headers = _utility.get_headers(token, cmd)

    response = requests.get(url, headers=headers)

    result = _utility.try_extract_result(response)

    if "value" not in result:
        raise CLIError("Value is not present in body: {}".format(result))

    return [Template(item) for item in result["value"]]
    def test_template_component_list(self):
        expected_component_list = [
            "_rpgcmdpo",
            "RS40OccupancySensorV36fy",
        ]
        template = Template(
            load_json(FileNames.central_property_validation_template_file))

        assert collections.Counter(
            template.components.keys()) == collections.Counter(
                expected_component_list)
Exemple #8
0
    def test_validate_properties_name_miss(
        self, mock_device_svc, mock_device_template_svc
    ):

        # setup
        mock_device_template_svc.get_device_template.return_value = Template(
            self._duplicate_property_template
        )

        monitor = PropertyMonitor(
            cmd=None,
            app_id=app_id,
            device_id=device_id,
            token=None,
            central_dns_suffix=None,
        )

        # invalid interface / property
        definition = {"definition": "test_definition"}

        issues = monitor._validate_payload_against_interfaces(
            definition, list(definition.keys())[0], Severity.warning,
        )

        assert (
            issues[0].details
            == "Device is sending data that has not been defined in the device "
            "template. Following capabilities have NOT been defined in the device template "
            "'['definition']'. Following capabilities have been defined in the device template "
            "(grouped by interface) '{'groupOne_g4': ['Model', 'Version', 'TotalStorage'], "
            "'groupTwo_ed': ['Model', 'Manufacturer'], 'groupThree_ed': ['Manufacturer', "
            "'Version', 'Model', 'OsName']}'. "
        )

        # invalid and valid property with valid interface
        property_collection = {
            "Model": "test_model",
            "Manufacturer": "test_manufacturer",
            "OsName": "test_osName",
        }

        issues = monitor._validate_payload_against_interfaces(
            property_collection, "groupOne_g4", Severity.warning,
        )

        assert (
            issues[0].details
            == "Device is sending data that has not been defined in the device "
            "template. Following capabilities have NOT been defined in the device template "
            "'['Manufacturer', 'OsName']'. Following capabilities have been defined in the device template "
            "(grouped by interface) '{'groupOne_g4': ['Model', 'Version', 'TotalStorage'], "
            "'groupTwo_ed': ['Model', 'Manufacturer'], 'groupThree_ed': ['Manufacturer', "
            "'Version', 'Model', 'OsName']}'. "
        )
 def test_extract_schema_type(self):
     expected_mapping = {
         "Bool": "boolean",
         "Date": "date",
         "DateTime": "dateTime",
         "Double": "double",
         "Duration": "duration",
         "IntEnum": "Enum",
         "StringEnum": "Enum",
         "Float": "float",
         "Geopoint": "geopoint",
         "Long": "long",
         "Object": "Object",
         "String": "string",
         "Time": "time",
         "Vector": "vector",
     }
     template = Template(load_json(FileNames.central_device_template_file))
     for key, val in expected_mapping.items():
         schema = template.get_schema(key)
         schema_type = extract_schema_type(schema)
         assert schema_type == val
    def test_template_interface_list(self):
        expected_interface_list = [
            "urn:sampleApp:groupOne_bz:_rpgcmdpo:1",
            "urn:sampleApp:groupTwo_bz:myxqftpsr:2",
            "urn:sampleApp:groupThree_bz:myxqftpsr:2",
            "urn:sampleApp:groupOne_bz:2",
        ]
        template = Template(
            load_json(FileNames.central_property_validation_template_file))

        assert collections.Counter(
            template.interfaces.keys()) == collections.Counter(
                expected_interface_list)
Exemple #11
0
    def test_validate_invalid_telmetry_component_template_should_fail(self):
        # setup
        device_template = Template(
            load_json(FileNames.central_property_validation_template_file))

        properties = MessageProperties(content_encoding=self.encoding,
                                       content_type=self.content_type)
        message = Message(
            body=json.dumps(self.bad_dcm_payload).encode(),
            properties=properties,
            annotations={
                common_parser.DEVICE_ID_IDENTIFIER:
                self.device_id.encode(),
                common_parser.COMPONENT_NAME_IDENTIFIER:
                list(device_template.components.keys())[1].encode(),
            },
            application_properties=_encode_app_props(self.app_properties),
        )
        args = CommonParserArguments(properties=["all"])
        parser = self._create_parser(device_template=device_template,
                                     message=message,
                                     args=args)

        # act
        parsed_msg = parser.parse_message()

        # verify
        assert parsed_msg["event"]["payload"] == self.bad_dcm_payload
        assert parsed_msg["event"]["origin"] == self.device_id
        device_identifier = str(common_parser.DEVICE_ID_IDENTIFIER, "utf8")
        assert parsed_msg["event"]["annotations"][
            device_identifier] == self.device_id
        component_identifier = str(common_parser.COMPONENT_NAME_IDENTIFIER,
                                   "utf8")
        assert (
            parsed_msg["event"]["annotations"][component_identifier] == list(
                device_template.components.keys())[1])
        properties = parsed_msg["event"]["properties"]
        assert properties["system"]["content_encoding"] == self.encoding
        assert properties["system"]["content_type"] == self.content_type
        assert properties["application"] == self.app_properties

        expected_details = strings.invalid_field_name_component_mismatch_template(
            list(self.bad_dcm_payload.keys()),
            device_template.component_schema_names,
        )

        _validate_issues(parser, Severity.warning, 1, 1, [expected_details])
Exemple #12
0
    def _validate_payload_against_interfaces(
        self,
        payload: dict,
        template: Template,
    ):
        name_miss = []
        for telemetry_name, telemetry in payload.items():
            schema = template.get_schema(name=telemetry_name,
                                         interface_name=self.interface_name)
            if not schema:
                name_miss.append(telemetry_name)
            else:
                self._process_telemetry(telemetry_name, schema, telemetry)

        if name_miss:
            details = strings.invalid_field_name_mismatch_template(
                name_miss, template.schema_names)
            self._add_central_issue(severity=Severity.warning, details=details)
Exemple #13
0
    def test_validate_properties_declared_multiple_interfaces(
            self, mock_device_svc, mock_device_template_svc):

        # setup
        mock_device_template_svc.get_device_template.return_value = Template(
            self._duplicate_property_template)

        monitor = PropertyMonitor(
            cmd=None,
            app_id=app_id,
            device_id=device_id,
            token=None,
            central_dns_suffix=None,
        )

        model = {"Model": "test_model"}

        issues = monitor._validate_payload_against_entities(
            model,
            list(model.keys())[0],
            Severity.warning,
        )

        assert (
            issues[0].details ==
            "Duplicate property: 'Model' found under following "
            "interfaces ['urn:sampleApp:groupOne_bz:_rpgcmdpo:1', 'urn:sampleApp:groupTwo_bz:myxqftpsr:2', "
            "'urn:sampleApp:groupThree_bz:myxqftpsr:2'] "
            "in the device model. Either provide the interface name as part "
            "of the device payload or make the propery name unique in the device model"
        )

        version = {"OsName": "test_osName"}

        issues = monitor._validate_payload_against_entities(
            version,
            list(version.keys())[0],
            Severity.warning,
        )

        assert len(issues) == 0
Exemple #14
0
    def test_validate_properties_severity_level(
        self, mock_device_svc, mock_device_template_svc
    ):

        # setup
        mock_device_template_svc.get_device_template.return_value = Template(
            self._duplicate_property_template
        )

        monitor = PropertyMonitor(
            cmd=None,
            app_id=app_id,
            device_id=device_id,
            token=None,
            central_dns_suffix=None,
        )

        # severity level info
        definition = {"definition": "test_definition"}

        issues = monitor._validate_payload_against_interfaces(
            definition, list(definition.keys())[0], Severity.info,
        )

        assert (
            issues[0].details
            == "Device is sending data that has not been defined in the device "
            "template. Following capabilities have NOT been defined in the device template "
            "'['definition']'. Following capabilities have been defined in the device template "
            "(grouped by interface) '{'groupOne_g4': ['Model', 'Version', 'TotalStorage'], "
            "'groupTwo_ed': ['Model', 'Manufacturer'], 'groupThree_ed': ['Manufacturer', "
            "'Version', 'Model', 'OsName']}'. "
        )

        # severity level error
        issues = monitor._validate_payload_against_interfaces(
            definition, list(definition.keys())[0], Severity.error,
        )

        assert len(issues) == 0
Exemple #15
0
    def test_validate_properties_name_miss_under_component(
            self, mock_device_svc, mock_device_template_svc):

        # setup
        mock_device_template_svc.get_device_template.return_value = Template(
            self._duplicate_property_template)

        monitor = PropertyMonitor(
            cmd=None,
            app_id=app_id,
            device_id=device_id,
            token=None,
            central_dns_suffix=None,
        )

        # invalid component property
        definition = {
            PNP_DTDLV2_COMPONENT_MARKER: "c",
            "data": {
                "definition": "test_definition"
            },
        }

        issues = monitor._validate_payload_against_entities(
            definition,
            list(definition.keys())[0],
            Severity.warning,
        )

        assert (
            issues[0].details ==
            "Device is sending data that has not been defined in the device template. "
            "Following capabilities have NOT been defined in the device template '['data']'. "
            "Following capabilities have been defined in the device template (grouped by components) "
            "'{'_rpgcmdpo': ['component1Prop', 'testComponent', 'component1PropReadonly', 'component1Prop2'], "
            "'RS40OccupancySensorV36fy': ['component2prop', 'testComponent', 'component2PropReadonly', "
            "'component2Prop2', 'component1Telemetry']}'. ")
    def _validate_payload(self, payload: dict, template: Template,
                          is_component: bool):
        name_miss = []
        for telemetry_name, telemetry in payload.items():
            schema = template.get_schema(
                name=telemetry_name,
                identifier=self.component_name,
                is_component=is_component,
            )
            if not schema:
                name_miss.append(telemetry_name)
            else:
                self._process_telemetry(telemetry_name, schema, telemetry)

        if name_miss:
            if is_component:
                details = strings.invalid_field_name_component_mismatch_template(
                    name_miss, template.component_schema_names)
            else:
                details = strings.invalid_field_name_mismatch_template(
                    name_miss,
                    template.schema_names,
                )
            self._add_central_issue(severity=Severity.warning, details=details)
 def test_object_deep(self, value, expected_result):
     template = Template(
         load_json(FileNames.central_deeply_nested_device_template_file))
     schema = template.get_schema("RidiculousObject")
     assert validate(schema, value) == expected_result
 def test_str_enum(self, value, expected_result):
     template = Template(load_json(FileNames.central_device_template_file))
     schema = template.get_schema("StringEnum")
     assert validate(schema, value) == expected_result
Exemple #19
0
 def _get_template(self):
     return Template(load_json(FileNames.central_device_template_file))