Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP]OK validator dependency management #53

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions tools/ok_validator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# OPen Knowledge Validator
Empty file added tools/ok_validator/__init__.py
Empty file.
33 changes: 33 additions & 0 deletions tools/ok_validator/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[tool.poetry]
name="okvalidator"
version="0.1.0"
description="An Open Knowledge validator"
authors = [
"Elijah Ahianyo [email protected]",
]
readme="README.md"

repository = "https://github.com/helpfulengineering/project-data-platform/tools/ok_validator"

classifiers = [
"Development Status :: 3 - Alpha",

"Intended Audience :: Developers",
"Topic :: Software Development :: Open Knowledge",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3 :: Only",
]


[tool.poetry.dependencies]
python = "^3.7"
pyyaml = "6.0"

[tool.poetry.group.dev.dependencies]
pytest = "^7.2.1"
11 changes: 11 additions & 0 deletions tools/ok_validator/src/okh.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from .validate import OKValidator

__REQUIRED_FIELDS__ = [
"bom",
"title",
]


class OKHValidator(OKValidator):
def __init__(self):
super().__init__(__REQUIRED_FIELDS__)
Empty file added tools/ok_validator/src/okw.py
Empty file.
88 changes: 88 additions & 0 deletions tools/ok_validator/src/validate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"""Open knowledge validator"""
import yaml
from pathlib import Path
from typing import List, Union
from collections import namedtuple

Error = namedtuple("Error", ["type", "msg"])


class OKValidator:
def __init__(self, required_fields: List[str]):
self.required_fields = required_fields

def _validate_yaml(self, yaml_content: dict) -> bool:
"""
Validate the YAML content to check if it contains the required fields.
"""
for field in self.required_fields:
if field not in yaml_content:
return False
return True

def validate(self, src: Union[str, Path, dict], raise_exception=False) -> bool:
"""
Validate the YAML source, which can be a file path, YAML content string,
Path object, or YAML dictionary.
"""
# breakpoint()
if not isinstance(src, Union[str, dict, Path]):
return self.return_value_or_error(
Error(
ValueError,
"src should be one of the following: a string path, "
"a Path object, or Yaml dict",
),
raise_exception,
)

if isinstance(src, dict):
return self._validate_yaml(src)

if isinstance(src, Path):
src = str(src)

try:
with open(src, "r") as yaml_file:
yaml_content = yaml.safe_load(yaml_file)
if yaml_content is None:
return self.return_value_or_error(
Error(
ValueError,
"The YAML file is empty or contains invalid " "syntax.",
),
raise_exception,
)
else:
return self._validate_yaml(yaml_content)
except FileNotFoundError:
return self.return_value_or_error(
Error(
FileNotFoundError,
"File not found. Please provide a valid YAML " "file path.",
),
raise_exception,
)
except yaml.YAMLError:
return self.return_value_or_error(
Error(yaml.YAMLError, ""), raise_exception
)

@staticmethod
def return_value_or_error(result: Error, raise_exception: bool = False):
"""Return a bool or raise an exception.

Args:
result: exception to raise.
raise_exception: If set to true, the provided exception will be raised.
"""
if not raise_exception:
return False

if not isinstance(result, Error):
raise TypeError(
f"result arg needs to be of type,{type(Error)}. "
f"Got {type(result)} instead."
)

raise result.type(result.msg)
Empty file.
3 changes: 3 additions & 0 deletions tools/ok_validator/tests/test_okh.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""Unit tests for okh validator"""

# TODO: add tests
109 changes: 109 additions & 0 deletions tools/ok_validator/tests/test_validate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
"""Unit tests for ok validator"""

import pytest
from tools.ok_validator.src.validate import OKValidator, Error


@pytest.fixture
def okh_string():
return """
title: mock okh title.
description: mock description for okh.
bom: mock bom field.
"""


@pytest.fixture
def okh_yaml_file(tmp_path, okh_string):
okh_file = tmp_path / "okh.yaml"
okh_file.touch()
okh_file.write_text(okh_string)

return okh_file


@pytest.fixture
def okh_dict():
return {"title": "mock okh title.", "description": "mock description for okh.", "bom": "mock bom field."}


@pytest.fixture
def ok_validator():
return OKValidator(["bom", "title"])


@pytest.mark.parametrize(
"fixture", ["okh_yaml_file", "okh_dict"]
)
def test_validate_yaml_file(fixture, request, ok_validator):
"""Test that the validate method returns the correct boolean.

Args:
fixture: fixtures of accepted src formats.
request: pytest request.
ok_validator: Validator instance.
"""
okh = request.getfixturevalue(fixture)
assert ok_validator.validate(okh)


def test_validate_with_non_existent_file(tmp_path, ok_validator):
"""Test that the validate method returns False when file does not
exist.

Args:
tmp_path: Test location.
ok_validator: Validator instance.
"""
file = tmp_path / "okh.yaml"
assert not ok_validator.validate(file)


def test_validate_with_invalid_file(tmp_path, ok_validator):
"""Test that the validate method returns False when an invalid Yaml file
is passed.

Args:
tmp_path: Test location.
ok_validator: Validator Instance.
"""
file = tmp_path / "okh.yaml"
file.touch()
assert not ok_validator.validate(file)

file.write_text("""title: invalid_field: mock title
description: mock description.
""")

assert not ok_validator.validate(file)


def test_validate_raise_exception(tmp_path, ok_validator):
"""Test that an exception is raised when the raise_exception parameter
is set to True on the validator method.

Args:
tmp_path: The test location.
ok_validator: Validator instance.
"""
file = tmp_path / "okh.yaml"

with pytest.raises(FileNotFoundError):
ok_validator.validate(file, raise_exception=True)


def test_return_value_or_error():
"""Test that the right exception is raised."""

error = Error(type=ValueError, msg="raised a value error")
with pytest.raises(error.type) as err:
OKValidator.return_value_or_error(error, raise_exception=True)
assert err.value.args[0] == error.msg


def test_return_value_or_error_with_wrong_error_type():
"""Test that an exception is raised when the wrong type is provided
with the `raise_exception` flag set to True.
"""
with pytest.raises(TypeError):
OKValidator.return_value_or_error(str, raise_exception=True)
Loading