Skip to content

Commit

Permalink
Merge pull request #179 from dynatrace-oss/dev
Browse files Browse the repository at this point in the history
Workaround invalid schema metric name regex pattern
  • Loading branch information
vduseev authored Nov 13, 2024
2 parents b0bb03b + 8878f33 commit 04affa3
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 32 deletions.
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 1.6.19
current_version = 1.6.21
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)
serialize =
{major}.{minor}.{patch}
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ extensions for Dynatrace Extension Framework 2.0.
<p>
<a href="https://pypi.org/project/dt-cli/"><img alt="PyPI" src="https://img.shields.io/pypi/v/dt-cli?color=blue&logo=python&logoColor=white"></a>
<a href="https://pypi.org/project/dt-cli/"><img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/dt-cli?logo=python&logoColor=white"></a>
<a href="https://github.com/dynatrace-oss/dt-cli/actions/workflows/built-test-release.yml"><img alt="GitHub Workflow Status (main branch)" src="https://img.shields.io/github/actions/workflow/status/dynatrace-oss/dt-cli/test.yml?branch=main&logo=github"></a>
<a href="https://github.com/dynatrace-oss/dt-cli/actions/workflows/test.yml"><img alt="GitHub Workflow Status (main branch)" src="https://img.shields.io/github/actions/workflow/status/dynatrace-oss/dt-cli/test.yml?branch=main&logo=github"></a>
</p>

## Features
Expand Down
12 changes: 9 additions & 3 deletions dtcli/scripts/dt.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import re
import sys
from pathlib import Path
from pprint import pprint

import click
import requests # noqa:I201
Expand Down Expand Up @@ -838,16 +839,21 @@ def delete(**kwargs):
)
def validate_schema(instance, **kwargs):
errors = _validate_schema.validate_schema(
instance_object=instance, schema_entrypoint=kwargs["schema_entrypoint"],
warn=functools.partial(click.echo, err=True))
extension_yaml_path=instance, extension_schema_path=kwargs["schema_entrypoint"],
warn=functools.partial(click.echo, err=True),
)
invalid = False
if errors:
invalid = True
for i, e in enumerate(errors):
print(f'{10 * "-"} error {i} {10 * "-"}', file=sys.stderr)
print(f'line: {e["line"]}, column: {e["column"]}', file=sys.stderr)
print(f'path: {e["path"]}', file=sys.stderr)
print(f'cause: {e["cause"]}', file=sys.stderr)
cause = e["cause"]
if isinstance(cause, dict):
pprint(f'cause: {cause}', stream=sys.stderr)
else:
print(f'cause: {e["cause"]}', file=sys.stderr)
if invalid:
print(f"{30 * '-'}", file=sys.stderr)
raise click.ClickException(f"{i + 1} validation errors total, aborting!")
Expand Down
81 changes: 56 additions & 25 deletions dtcli/validate_schema.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import json
import pathlib
from pathlib import Path
from typing import List, Union, Callable
from typing import Any, List, Union, Callable

import jsonschema
from jsonschema import Draft202012Validator, Validator, ValidationError

from referencing import Registry, Resource

import yaml

Expand Down Expand Up @@ -36,37 +38,60 @@ def backtrack_yaml_location(path: List[Union[int, str]], ast: YamlAST) -> yaml.e
assert 0, "unreachable switch branch"


def validate_schema(instance_object: Path, schema_entrypoint: Path, warn: Callable[[str], None]) -> list:
subschemas_path = pathlib.Path(schema_entrypoint).parent
def remove_unsupported_regex_patterns(data: Any) -> dict:
if isinstance(data, dict):
cleanedup_dict = {
k: remove_unsupported_regex_patterns(v) for k, v in data.items()
# if not (k == "pattern" and isinstance(v, str) and r"\\p" in v)
if not (k == "pattern" and isinstance(v, str) and r"\p" in v)
}
return cleanedup_dict
elif isinstance(data, list):
cleanedup_list = [
remove_unsupported_regex_patterns(i) for i in data
]
return cleanedup_list
else:
return data


def validate_schema(extension_yaml_path: Path, extension_schema_path: Path, warn: Callable[[str], None]) -> list:
schema_dir_path = pathlib.Path(extension_schema_path).parent

with open(instance_object, "r") as yaml_in:
with open(extension_yaml_path, "r") as yaml_in:
instance = yaml.safe_load(yaml_in)

with open(schema_entrypoint) as f:
s = json.load(f)
with open(extension_schema_path) as f:
schema_data = json.load(f)
cleanedup_schema_data = remove_unsupported_regex_patterns(schema_data)

schema_store = {}
for subschema_candidate in filter(lambda f: f.is_file, pathlib.Path(subschemas_path).iterdir()):
subschema_paths = [
p for p in schema_dir_path.iterdir() if p.is_file() and extension_schema_path.absolute() != p.absolute()
]

resources: list[tuple[str, dict]] = []
for c in subschema_paths:
try:
with open(subschema_candidate) as f:
sub = json.load(f)
with open(c) as f:
subschema_data = json.load(f)
except json.decoder.JSONDecodeError:
warn(f"skipping subschema {subschema_candidate}, malformed json")
# print(f"skipping subschema {subschema_candidate}, malformed json", file=sys.stderr)
warn(f"skipping subschema {c}, malformed json")
else:
sub_id = sub["$id"]
schema_store[sub_id] = sub

resolver = jsonschema.validators.RefResolver.from_schema(s, store=schema_store)

validator_cls = jsonschema.validators.validator_for(s)
validator_cls.check_schema(s) # against META_SCHEMA
validator = validator_cls(s, resolver=resolver)

with open(instance_object, "r") as f:
cleanedup_subschema_data = remove_unsupported_regex_patterns(subschema_data)
subschema_uri = cleanedup_subschema_data["$id"]
subschema = Resource.from_contents(cleanedup_subschema_data)
resources.append((subschema_uri, subschema))

registry = Registry().with_resources(resources)
validator: Validator = Draft202012Validator(
cleanedup_schema_data,
registry=registry,
)

with open(extension_yaml_path, "r") as f:
file = yaml.compose(f)

def process_validation_error(error):
def process_validation_error(error: ValidationError):
err_loc = backtrack_yaml_location(error.absolute_path, file)

# TODO: add typo finder for some cases - like enum mismatch
Expand All @@ -77,4 +102,10 @@ def process_validation_error(error):
"cause": error.message
}

return list(map(process_validation_error, validator.iter_errors(instance)))
detected_errors = list(validator.iter_errors(instance))
errors_info: list[dict] = []
for err in detected_errors:
error_info = process_validation_error(err)
errors_info.append(error_info)

return errors_info
2 changes: 1 addition & 1 deletion dtcli/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.

__version__ = "1.6.19"
__version__ = "1.6.21"
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ packages = [
]
readme = "README.md"
repository = "https://github.com/dynatrace-oss/dt-cli"
version = "1.6.19"
version = "1.6.21"

[tool.poetry.dependencies]
PyYAML = "^6.0"
Expand Down

0 comments on commit 04affa3

Please sign in to comment.