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

chore: merge develop into main #319

Merged
merged 2 commits into from
Oct 9, 2024
Merged
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 .github/workflows/build-test-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ jobs:
- meta
- test-unit
strategy:
fail-fast: false
matrix:
splunk: ${{ fromJson(needs.meta.outputs.matrix_supportedSplunk) }}
env:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

[tool.poetry]
name = "splunktaucclib"
version = "6.3.0"
version = "6.4.0-beta.1"
description = ""
authors = ["Splunk <addonfactory@splunk.com>"]
license = "APACHE-2.0"
Expand Down
2 changes: 1 addition & 1 deletion splunktaucclib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@
# limitations under the License.
#

__version__ = "6.3.0"
__version__ = "6.4.0-beta.1"
15 changes: 14 additions & 1 deletion splunktaucclib/rest_handler/endpoint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
# limitations under the License.
#

from typing import List, Optional

from .field import RestField
from ..error import RestError
from ..util import get_base_app_name

Expand All @@ -28,14 +30,18 @@


class RestModel:
def __init__(self, fields, name=None):
def __init__(
self, fields, name=None, special_fields: Optional[List[RestField]] = None
):
"""
REST Model.
:param name:
:param fields:
:param special_fields:
"""
self.name = name
self.fields = fields
self.special_fields = special_fields if special_fields else []


class RestEndpoint:
Expand Down Expand Up @@ -84,6 +90,13 @@ def _loop_fields(self, meth, name, data, *args, **kwargs):
def validate(self, name, data, existing=None):
self._loop_fields("validate", name, data, existing=existing)

def _loop_field_special(self, meth, name, data, *args, **kwargs):
model = self.model(name)
return [getattr(f, meth)(data, *args, **kwargs) for f in model.special_fields]

def validate_special(self, name, data):
self._loop_field_special("validate", name, data, validate_name=name)

def encode(self, name, data):
self._loop_fields("encode", name, data)

Expand Down
4 changes: 2 additions & 2 deletions splunktaucclib/rest_handler/endpoint/field.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ def __init__(
self.validator = validator
self.converter = converter

def validate(self, data, existing=None):
def validate(self, data, existing=None, validate_name=None):
# update case: check required field in data
if existing and self.name in data and not data.get(self.name) and self.required:
raise RestError(400, "Required field is missing: %s" % self.name)
value = data.get(self.name)
value = data.get(self.name) if not validate_name else validate_name
if not value and existing is None:
if self.required:
raise RestError(400, "Required field is missing: %s" % self.name)
Expand Down
35 changes: 33 additions & 2 deletions splunktaucclib/rest_handler/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@

__all__ = ["RestHandler"]

BASIC_NAME_VALIDATORS = {
"PROHIBITED_NAME_CHARACTERS": ["*", "\\", "[", "]", "(", ")", "?", ":"],
"PROHIBITED_NAMES": ["default", ".", ".."],
"MAX_LENGTH": 1024,
}


def _check_name_for_create(name):
if name == "default":
Expand Down Expand Up @@ -102,13 +108,38 @@ def check_existing(self, name):
else:
return None

def basic_name_validation(name: str):
tmp_name = str(name)
prohibited_chars = BASIC_NAME_VALIDATORS["PROHIBITED_NAME_CHARACTERS"]
prohibited_names = BASIC_NAME_VALIDATORS["PROHIBITED_NAMES"]
max_chars = BASIC_NAME_VALIDATORS["MAX_LENGTH"]
val_err_msg = (
f'{prohibited_names}, string started with "_" and string including any one '
f'of {prohibited_chars} are reserved value which cannot be used for field Name"'
)

if tmp_name.startswith("_") or any(
tmp_name == el for el in prohibited_names
):
raise RestError(400, val_err_msg)

if any(pc in prohibited_chars for pc in tmp_name):
raise RestError(400, val_err_msg)

if len(tmp_name) >= max_chars:
raise RestError(
400, f"Field Name must be less than {max_chars} characters"
)

@wraps(meth)
def wrapper(self, name, data):
self._endpoint.validate(
name,
data,
check_existing(self, name),
)
basic_name_validation(name)
self._endpoint.validate_special(name, data)
self._endpoint.encode(name, data)

return meth(self, name, data)
Expand Down Expand Up @@ -194,7 +225,7 @@ def all(self, decrypt=False, **query):
response = self._client.get(
self.path_segment(self._endpoint.internal_endpoint),
output_mode="json",
**query
**query,
)
return self._format_all_response(response, decrypt)

Expand Down Expand Up @@ -382,7 +413,7 @@ def _load_credentials(self, name, data):
self._endpoint.internal_endpoint,
name=name,
),
**masked
**masked,
)

def _encrypt_raw_credentials(self, data):
Expand Down
64 changes: 15 additions & 49 deletions tests/integration/demo/globalConfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,14 @@
"configuration": {
"tabs": [
{
"name": "logging",
"entity": [
{
"type": "singleSelect",
"label": "Log level",
"options": {
"disableSearch": true,
"autoCompleteFields": [
{
"value": "DEBUG",
"label": "DEBUG"
},
{
"value": "INFO",
"label": "INFO"
},
{
"value": "WARN",
"label": "WARN"
},
{
"value": "ERROR",
"label": "ERROR"
},
{
"value": "CRITICAL",
"label": "CRITICAL"
}
]
},
"defaultValue": "INFO",
"field": "loglevel"
}
],
"title": "Logging"
"type": "loggingTab",
"levels": [
"DEBUG",
"INFO",
"WARN",
"ERROR",
"CRITICAL"
]
}
],
"title": "Configuration",
Expand All @@ -55,7 +28,7 @@
{
"type": "regex",
"errorMsg": "Input Name must begin with a letter and consist exclusively of alphanumeric characters and underscores.",
"pattern": "^[a-zA-Z]\\w*$"
"pattern": "^[a-dA-D]\\w*$"
},
{
"type": "string",
Expand All @@ -69,19 +42,12 @@
"required": true
},
{
"type": "text",
"label": "Interval",
"validators": [
{
"type": "regex",
"errorMsg": "Interval must be an integer.",
"pattern": "^\\-[1-9]\\d*$|^\\d*$"
}
],
"defaultValue": "300",
"type": "interval",
"field": "interval",
"label": "Interval",
"help": "Time interval of the data input, in seconds.",
"required": true
"required": true,
"defaultValue": "300"
}
],
"title": "Demo"
Expand Down Expand Up @@ -144,6 +110,6 @@
"restRoot": "demo",
"version": "0.0.1",
"displayName": "Demo",
"schemaVersion": "0.0.3"
"schemaVersion": "0.0.8"
}
}
}
60 changes: 60 additions & 0 deletions tests/integration/demo/package/bin/demo_rh_demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import import_declare_test

from splunktaucclib.rest_handler.endpoint import (
field,
validator,
RestModel,
DataInputModel,
)
from splunktaucclib.rest_handler import admin_external, util
from splunktaucclib.rest_handler.admin_external import AdminExternalHandler
import logging

util.remove_http_proxy_env_vars()


special_fields = [
field.RestField(
"name",
required=True,
encrypted=False,
default=None,
validator=validator.AllOf(
validator.Pattern(
regex=r"""^[a-dA-D]\w*$""",
),
validator.String(
max_len=100,
min_len=1,
),
),
)
]

fields = [
field.RestField(
"interval",
required=True,
encrypted=False,
default="300",
validator=validator.Pattern(
regex=r"""^(?:-1|\d+(?:\.\d+)?)$""",
),
),
field.RestField("disabled", required=False, validator=None),
]
model = RestModel(fields, name=None, special_fields=special_fields)


endpoint = DataInputModel(
"demo",
model,
)


if __name__ == "__main__":
logging.getLogger().addHandler(logging.NullHandler())
admin_external.handle(
endpoint,
handler=AdminExternalHandler,
)
37 changes: 37 additions & 0 deletions tests/integration/demo/package/bin/demo_rh_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import import_declare_test

from splunktaucclib.rest_handler.endpoint import (
field,
validator,
RestModel,
MultipleModel,
)
from splunktaucclib.rest_handler import admin_external, util
from splunktaucclib.rest_handler.admin_external import AdminExternalHandler
import logging

util.remove_http_proxy_env_vars()


special_fields = []

fields_logging = [
field.RestField(
"loglevel", required=True, encrypted=False, default="INFO", validator=None
)
]
model_logging = RestModel(fields_logging, name="logging", special_fields=special_fields)


endpoint = MultipleModel(
"demo_settings",
models=[model_logging],
)


if __name__ == "__main__":
logging.getLogger().addHandler(logging.NullHandler())
admin_external.handle(
endpoint,
handler=AdminExternalHandler,
)
Loading
Loading