Skip to content

Commit 0f98aa3

Browse files
chore: merge develop into main (#319)
release PR
2 parents bef5019 + ef5ccb4 commit 0f98aa3

File tree

10 files changed

+275
-57
lines changed

10 files changed

+275
-57
lines changed

.github/workflows/build-test-release.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ jobs:
113113
- meta
114114
- test-unit
115115
strategy:
116+
fail-fast: false
116117
matrix:
117118
splunk: ${{ fromJson(needs.meta.outputs.matrix_supportedSplunk) }}
118119
env:

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
[tool.poetry]
1818
name = "splunktaucclib"
19-
version = "6.3.0"
19+
version = "6.4.0-beta.1"
2020
description = ""
2121
authors = ["Splunk <addonfactory@splunk.com>"]
2222
license = "APACHE-2.0"

splunktaucclib/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@
1414
# limitations under the License.
1515
#
1616

17-
__version__ = "6.3.0"
17+
__version__ = "6.4.0-beta.1"

splunktaucclib/rest_handler/endpoint/__init__.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
# limitations under the License.
1515
#
1616

17+
from typing import List, Optional
1718

19+
from .field import RestField
1820
from ..error import RestError
1921
from ..util import get_base_app_name
2022

@@ -28,14 +30,18 @@
2830

2931

3032
class RestModel:
31-
def __init__(self, fields, name=None):
33+
def __init__(
34+
self, fields, name=None, special_fields: Optional[List[RestField]] = None
35+
):
3236
"""
3337
REST Model.
3438
:param name:
3539
:param fields:
40+
:param special_fields:
3641
"""
3742
self.name = name
3843
self.fields = fields
44+
self.special_fields = special_fields if special_fields else []
3945

4046

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

93+
def _loop_field_special(self, meth, name, data, *args, **kwargs):
94+
model = self.model(name)
95+
return [getattr(f, meth)(data, *args, **kwargs) for f in model.special_fields]
96+
97+
def validate_special(self, name, data):
98+
self._loop_field_special("validate", name, data, validate_name=name)
99+
87100
def encode(self, name, data):
88101
self._loop_fields("encode", name, data)
89102

splunktaucclib/rest_handler/endpoint/field.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,11 @@ def __init__(
4141
self.validator = validator
4242
self.converter = converter
4343

44-
def validate(self, data, existing=None):
44+
def validate(self, data, existing=None, validate_name=None):
4545
# update case: check required field in data
4646
if existing and self.name in data and not data.get(self.name) and self.required:
4747
raise RestError(400, "Required field is missing: %s" % self.name)
48-
value = data.get(self.name)
48+
value = data.get(self.name) if not validate_name else validate_name
4949
if not value and existing is None:
5050
if self.required:
5151
raise RestError(400, "Required field is missing: %s" % self.name)

splunktaucclib/rest_handler/handler.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@
3434

3535
__all__ = ["RestHandler"]
3636

37+
BASIC_NAME_VALIDATORS = {
38+
"PROHIBITED_NAME_CHARACTERS": ["*", "\\", "[", "]", "(", ")", "?", ":"],
39+
"PROHIBITED_NAMES": ["default", ".", ".."],
40+
"MAX_LENGTH": 1024,
41+
}
42+
3743

3844
def _check_name_for_create(name):
3945
if name == "default":
@@ -102,13 +108,38 @@ def check_existing(self, name):
102108
else:
103109
return None
104110

111+
def basic_name_validation(name: str):
112+
tmp_name = str(name)
113+
prohibited_chars = BASIC_NAME_VALIDATORS["PROHIBITED_NAME_CHARACTERS"]
114+
prohibited_names = BASIC_NAME_VALIDATORS["PROHIBITED_NAMES"]
115+
max_chars = BASIC_NAME_VALIDATORS["MAX_LENGTH"]
116+
val_err_msg = (
117+
f'{prohibited_names}, string started with "_" and string including any one '
118+
f'of {prohibited_chars} are reserved value which cannot be used for field Name"'
119+
)
120+
121+
if tmp_name.startswith("_") or any(
122+
tmp_name == el for el in prohibited_names
123+
):
124+
raise RestError(400, val_err_msg)
125+
126+
if any(pc in prohibited_chars for pc in tmp_name):
127+
raise RestError(400, val_err_msg)
128+
129+
if len(tmp_name) >= max_chars:
130+
raise RestError(
131+
400, f"Field Name must be less than {max_chars} characters"
132+
)
133+
105134
@wraps(meth)
106135
def wrapper(self, name, data):
107136
self._endpoint.validate(
108137
name,
109138
data,
110139
check_existing(self, name),
111140
)
141+
basic_name_validation(name)
142+
self._endpoint.validate_special(name, data)
112143
self._endpoint.encode(name, data)
113144

114145
return meth(self, name, data)
@@ -194,7 +225,7 @@ def all(self, decrypt=False, **query):
194225
response = self._client.get(
195226
self.path_segment(self._endpoint.internal_endpoint),
196227
output_mode="json",
197-
**query
228+
**query,
198229
)
199230
return self._format_all_response(response, decrypt)
200231

@@ -382,7 +413,7 @@ def _load_credentials(self, name, data):
382413
self._endpoint.internal_endpoint,
383414
name=name,
384415
),
385-
**masked
416+
**masked,
386417
)
387418

388419
def _encrypt_raw_credentials(self, data):

tests/integration/demo/globalConfig.json

Lines changed: 15 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3,41 +3,14 @@
33
"configuration": {
44
"tabs": [
55
{
6-
"name": "logging",
7-
"entity": [
8-
{
9-
"type": "singleSelect",
10-
"label": "Log level",
11-
"options": {
12-
"disableSearch": true,
13-
"autoCompleteFields": [
14-
{
15-
"value": "DEBUG",
16-
"label": "DEBUG"
17-
},
18-
{
19-
"value": "INFO",
20-
"label": "INFO"
21-
},
22-
{
23-
"value": "WARN",
24-
"label": "WARN"
25-
},
26-
{
27-
"value": "ERROR",
28-
"label": "ERROR"
29-
},
30-
{
31-
"value": "CRITICAL",
32-
"label": "CRITICAL"
33-
}
34-
]
35-
},
36-
"defaultValue": "INFO",
37-
"field": "loglevel"
38-
}
39-
],
40-
"title": "Logging"
6+
"type": "loggingTab",
7+
"levels": [
8+
"DEBUG",
9+
"INFO",
10+
"WARN",
11+
"ERROR",
12+
"CRITICAL"
13+
]
4114
}
4215
],
4316
"title": "Configuration",
@@ -55,7 +28,7 @@
5528
{
5629
"type": "regex",
5730
"errorMsg": "Input Name must begin with a letter and consist exclusively of alphanumeric characters and underscores.",
58-
"pattern": "^[a-zA-Z]\\w*$"
31+
"pattern": "^[a-dA-D]\\w*$"
5932
},
6033
{
6134
"type": "string",
@@ -69,19 +42,12 @@
6942
"required": true
7043
},
7144
{
72-
"type": "text",
73-
"label": "Interval",
74-
"validators": [
75-
{
76-
"type": "regex",
77-
"errorMsg": "Interval must be an integer.",
78-
"pattern": "^\\-[1-9]\\d*$|^\\d*$"
79-
}
80-
],
81-
"defaultValue": "300",
45+
"type": "interval",
8246
"field": "interval",
47+
"label": "Interval",
8348
"help": "Time interval of the data input, in seconds.",
84-
"required": true
49+
"required": true,
50+
"defaultValue": "300"
8551
}
8652
],
8753
"title": "Demo"
@@ -144,6 +110,6 @@
144110
"restRoot": "demo",
145111
"version": "0.0.1",
146112
"displayName": "Demo",
147-
"schemaVersion": "0.0.3"
113+
"schemaVersion": "0.0.8"
148114
}
149-
}
115+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import import_declare_test
2+
3+
from splunktaucclib.rest_handler.endpoint import (
4+
field,
5+
validator,
6+
RestModel,
7+
DataInputModel,
8+
)
9+
from splunktaucclib.rest_handler import admin_external, util
10+
from splunktaucclib.rest_handler.admin_external import AdminExternalHandler
11+
import logging
12+
13+
util.remove_http_proxy_env_vars()
14+
15+
16+
special_fields = [
17+
field.RestField(
18+
"name",
19+
required=True,
20+
encrypted=False,
21+
default=None,
22+
validator=validator.AllOf(
23+
validator.Pattern(
24+
regex=r"""^[a-dA-D]\w*$""",
25+
),
26+
validator.String(
27+
max_len=100,
28+
min_len=1,
29+
),
30+
),
31+
)
32+
]
33+
34+
fields = [
35+
field.RestField(
36+
"interval",
37+
required=True,
38+
encrypted=False,
39+
default="300",
40+
validator=validator.Pattern(
41+
regex=r"""^(?:-1|\d+(?:\.\d+)?)$""",
42+
),
43+
),
44+
field.RestField("disabled", required=False, validator=None),
45+
]
46+
model = RestModel(fields, name=None, special_fields=special_fields)
47+
48+
49+
endpoint = DataInputModel(
50+
"demo",
51+
model,
52+
)
53+
54+
55+
if __name__ == "__main__":
56+
logging.getLogger().addHandler(logging.NullHandler())
57+
admin_external.handle(
58+
endpoint,
59+
handler=AdminExternalHandler,
60+
)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import import_declare_test
2+
3+
from splunktaucclib.rest_handler.endpoint import (
4+
field,
5+
validator,
6+
RestModel,
7+
MultipleModel,
8+
)
9+
from splunktaucclib.rest_handler import admin_external, util
10+
from splunktaucclib.rest_handler.admin_external import AdminExternalHandler
11+
import logging
12+
13+
util.remove_http_proxy_env_vars()
14+
15+
16+
special_fields = []
17+
18+
fields_logging = [
19+
field.RestField(
20+
"loglevel", required=True, encrypted=False, default="INFO", validator=None
21+
)
22+
]
23+
model_logging = RestModel(fields_logging, name="logging", special_fields=special_fields)
24+
25+
26+
endpoint = MultipleModel(
27+
"demo_settings",
28+
models=[model_logging],
29+
)
30+
31+
32+
if __name__ == "__main__":
33+
logging.getLogger().addHandler(logging.NullHandler())
34+
admin_external.handle(
35+
endpoint,
36+
handler=AdminExternalHandler,
37+
)

0 commit comments

Comments
 (0)