예제 #1
0
    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 = central_models.TemplateV1(
            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']}'. "
        )
예제 #2
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: central_models.TemplateV1(
            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])
예제 #3
0
    def test_template_component_list(self):
        expected_component_list = [
            "_rpgcmdpo",
            "RS40OccupancySensorV36fy",
        ]
        template = central_models.TemplateV1(
            load_json(FileNames.central_property_validation_template_file)
        )

        assert collections.Counter(template.components.keys()) == collections.Counter(
            expected_component_list
        )
예제 #4
0
 def test_extract_schema_type_component(self):
     expected_mapping = {
         "component1Prop": "boolean",
         "testComponent": "boolean",
         "component1PropReadonly": "boolean",
         "component1Prop2": "boolean",
     }
     template = central_models.TemplateV1(
         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
예제 #5
0
    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 = central_models.TemplateV1(
            load_json(FileNames.central_property_validation_template_file)
        )

        assert collections.Counter(template.interfaces.keys()) == collections.Counter(
            expected_interface_list
        )
예제 #6
0
    def test_validate_invalid_telmetry_component_template_should_fail(self):
        # setup
        device_template = central_models.TemplateV1(
            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])
예제 #7
0
def create_device_template(
    cmd,
    app_id: str,
    device_template_id: str,
    payload: dict,
    token: str,
    central_dns_suffix=CENTRAL_ENDPOINT,
    api_version=ApiVersion.v1.value,
) -> Union[central_models.TemplatePreview, central_models.TemplateV1]:
    """
    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)

    # Construct parameters
    query_parameters = {}
    query_parameters["api-version"] = api_version

    response = requests.put(url,
                            headers=headers,
                            json=payload,
                            params=query_parameters)
    if api_version == ApiVersion.preview.value:
        return central_models.TemplatePreview(
            _utility.try_extract_result(response))
    else:
        return central_models.TemplateV1(_utility.try_extract_result(response))
예제 #8
0
def list_device_templates(
    cmd,
    app_id: str,
    token: str,
    central_dns_suffix=CENTRAL_ENDPOINT,
    api_version=ApiVersion.v1.value,
) -> List[Union[central_models.TemplatePreview, central_models.TemplateV1]]:
    """
    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)

    # Construct parameters
    query_parameters = {}
    query_parameters["api-version"] = api_version

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

    result = _utility.try_extract_result(response)

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

    if api_version == ApiVersion.preview.value:
        return [
            central_models.TemplatePreview(item) for item in result["value"]
        ]
    else:
        return [central_models.TemplateV1(item) for item in result["value"]]
예제 #9
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 = central_models.TemplateV1(
            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
예제 #10
0
def get_device_template(
    cmd,
    app_id: str,
    device_template_id: str,
    token: str,
    central_dns_suffix=CENTRAL_ENDPOINT,
    api_version=ApiVersion.v1.value,
) -> Union[central_models.TemplatePreview, central_models.TemplateV1]:
    """
    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)

    # Construct parameters
    query_parameters = {}
    query_parameters["api-version"] = api_version

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

    if api_version == ApiVersion.preview.value:
        return central_models.TemplatePreview(
            _utility.try_extract_result(response))
    else:
        return central_models.TemplateV1(_utility.try_extract_result(response))
예제 #11
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 = central_models.TemplateV1(
            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']}'. ")
예제 #12
0
 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 = central_models.TemplateV1(
         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
예제 #13
0
 def _get_template(self):
     return central_models.TemplateV1(
         load_json(FileNames.central_device_template_file))
예제 #14
0
 def test_object_deep(self, value, expected_result):
     template = central_models.TemplateV1(
         load_json(FileNames.central_deeply_nested_device_template_file)
     )
     schema = template.get_schema("RidiculousObject")
     assert validate(schema, value) == expected_result
예제 #15
0
 def test_str_enum(self, value, expected_result):
     template = central_models.TemplateV1(
         load_json(FileNames.central_device_template_file)
     )
     schema = template.get_schema("StringEnum")
     assert validate(schema, value) == expected_result