Skip to content

Commit

Permalink
fix: validate that a configuration tab with table has field "name" (#261
Browse files Browse the repository at this point in the history
)

Before this fix - `ucc-gen` generated add-on which did not work in Splunk.
  • Loading branch information
Artem Rys authored Jul 12, 2021
1 parent 7d1690e commit 234f1e9
Show file tree
Hide file tree
Showing 4 changed files with 547 additions and 23 deletions.
26 changes: 9 additions & 17 deletions splunk_add_on_ucc_framework/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import time
from pathlib import Path

import jsonschema
from defusedxml import cElementTree as defused_et
from dunamai import Style, Version
from jinja2 import Environment, FileSystemLoader
Expand All @@ -40,6 +39,10 @@
AppManifest,
AppManifestFormatException,
)
from .global_config_validator import (
GlobalConfigValidator,
GlobalConfigValidatorException,
)
from .start_alert_build import alert_build
from .uccrestbuilder import build
from .uccrestbuilder.global_config import (
Expand Down Expand Up @@ -705,18 +708,6 @@ def restore_comments(outputdir, ta_name, comment_map):
file.write("".join(lines))


def validate_config_against_schema(config: dict):
"""
Validates config against JSON schema.
Raises jsonschema.ValidationError if config is not valid.
"""
schema_path = os.path.join(sourcedir, "schema", "schema.json")
with open(schema_path) as f_schema:
schema_raw = f_schema.read()
schema = json.loads(schema_raw)
return jsonschema.validate(instance=config, schema=schema)


def _generate(source, config, ta_version, outputdir=None):
if outputdir is None:
outputdir = os.path.join(os.getcwd(), "output")
Expand Down Expand Up @@ -751,7 +742,7 @@ def _generate(source, config, ta_version, outputdir=None):
manifest = AppManifest()
try:
manifest.read(app_manifest_content)
except app_manifest.AppManifestFormatException:
except AppManifestFormatException:
logger.error(
f"Manifest file @ {app_manifest_path} has invalid format.\n"
f"Please refer to {APP_MANIFEST_WEBSITE}.\n"
Expand All @@ -764,10 +755,11 @@ def _generate(source, config, ta_version, outputdir=None):
try:
with open(config) as f_config:
config_raw = f_config.read()
validate_config_against_schema(json.loads(config_raw))
validator = GlobalConfigValidator(sourcedir, json.loads(config_raw))
validator.validate()
logger.info("Config is valid")
except jsonschema.ValidationError as e:
logger.error("Config is not valid. Error: {}".format(e))
except GlobalConfigValidatorException as e:
logger.error(f"Config is not valid. Error: {e}")
sys.exit(1)

update_ta_version(config, ta_version)
Expand Down
76 changes: 76 additions & 0 deletions splunk_add_on_ucc_framework/global_config_validator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#
# Copyright 2021 Splunk Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import json
import os

import jsonschema


class GlobalConfigValidatorException(Exception):
pass


class GlobalConfigValidator:
"""
GlobalConfigValidator implements different validation for globalConfig.json.
Simple validation should go to JSON schema in
https://github.com/splunk/addonfactory-ucc-base-ui repository.
Custom validation should be implemented here.
"""

def __init__(self, source_dir: str, config: dict):
self._source_dir = source_dir
self._config = config

def _validate_config_against_schema(self) -> None:
"""
Validates config against JSON schema.
Raises jsonschema.ValidationError if config is not valid.
"""
schema_path = os.path.join(self._source_dir, "schema", "schema.json")
with open(schema_path) as f_schema:
schema_raw = f_schema.read()
schema = json.loads(schema_raw)
try:
return jsonschema.validate(instance=self._config, schema=schema)
except jsonschema.ValidationError as e:
raise GlobalConfigValidatorException(e.message)

def _validate_configuration_tab_table_has_name_field(self) -> None:
"""
Validates that if a configuration tab should be rendered as a table,
then it needs to have an entity which has field "name".
"""
pages = self._config["pages"]
configuration = pages["configuration"]
tabs = configuration["tabs"]
for tab in tabs:
if "table" in tab:
entities = tab["entity"]
has_name_field = False
for entity in entities:
if entity["field"] == "name":
has_name_field = True
break
if not has_name_field:
raise GlobalConfigValidatorException(
f"Tab '{tab['name']}' should have entity with field 'name'"
)

def validate(self) -> None:
self._validate_config_against_schema()
self._validate_configuration_tab_table_has_name_field()
32 changes: 26 additions & 6 deletions tests/unit/test_config_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,42 @@
# limitations under the License.
#
import json
import os
import unittest

import jsonschema

import tests.unit.helpers as helpers
from splunk_add_on_ucc_framework import validate_config_against_schema
from splunk_add_on_ucc_framework.global_config_validator import (
GlobalConfigValidator,
GlobalConfigValidatorException,
)


def _path_to_source_dir() -> str:
return os.path.join(
os.getcwd(),
"splunk_add_on_ucc_framework",
)


class ConfigValidationTest(unittest.TestCase):
def test_config_validation_when_valid_config_then_no_exception(self):
config = helpers.get_testdata_file("valid_config.json")
config = json.loads(config)
validate_config_against_schema(config)
validator = GlobalConfigValidator(_path_to_source_dir(), config)
validator.validate()

def test_config_validation_when_invalid_config_then_exception(self):
config = helpers.get_testdata_file("invalid_config_no_configuration_tabs.json")
config = json.loads(config)
with self.assertRaises(jsonschema.ValidationError):
validate_config_against_schema(config)
validator = GlobalConfigValidator(_path_to_source_dir(), config)
with self.assertRaises(GlobalConfigValidatorException):
validator.validate()

def test_config_validation_when_configuration_tab_table_has_no_name_field(self):
config = helpers.get_testdata_file(
"invalid_config_no_name_field_in_configuration_tab_table.json"
)
config = json.loads(config)
validator = GlobalConfigValidator(_path_to_source_dir(), config)
with self.assertRaises(GlobalConfigValidatorException):
validator.validate()
Loading

0 comments on commit 234f1e9

Please sign in to comment.