Skip to content

Commit

Permalink
feat: Added schemaGenerator.py file
Browse files Browse the repository at this point in the history
  • Loading branch information
tbalar-splunk committed Mar 3, 2021
1 parent 887f419 commit be0b945
Showing 1 changed file with 371 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,371 @@
# SPDX-FileCopyrightText: 2020 2020
#
# SPDX-License-Identifier: Apache-2.0

from builtins import object
import json
import os
from jsl import AnyOfField, ArrayField, BooleanField, DictField, Document
from jsl import DocumentField, NumberField, OneOfField, StringField, UriField

###
# TODO Need to fix this: Make sure schemagenerator generates proper element for hook
# For time being do the following change in generated schema.json
# replace: "hook": {"$ref": "#/definitions/Hooks"} with "hook": {"type": "object"}
###

# Base Document Class with restricting properties population
class DocumentWithoutAddProp(Document):
class Options(object):
additional_properties = False


# Document Element with Label and Value element. Possible values are text, numeric and boolean types
class ValueLabelPair(DocumentWithoutAddProp):
value = OneOfField([
NumberField(),
StringField(max_length=250),
BooleanField()
])
label = StringField(required=True, max_length=100)


class OAuthFields(DocumentWithoutAddProp):
oauth_field = StringField(max_length=100)
label = StringField(max_length=100)
field = StringField(max_length=100)
help = StringField(max_length=200)
encrypted = BooleanField()



# Base Validator Wrapper component which is extension of DocumentWithoutAddProp Wrapper Component
class ValidatorBase(DocumentWithoutAddProp):
errorMsg = StringField(max_length=400)


# MetaData component for detailing brief imformation of document/component
class Meta(DocumentWithoutAddProp):
displayName = StringField(required=True, max_length=200)
name = StringField(required=True, pattern="^[^<>\:\"\/\\\|\?\*]+$")
restRoot = StringField(required=True, pattern="^\w+$")
apiVersion = StringField(required=True, pattern="^(?:\d{1,3}\.){2}\d{1,3}$")
version = StringField(required=True)
schemaVersion = StringField( pattern="^(?:\d{1,3}\.){2}\d{1,3}$")


# Text validator for the String Field Value input
class StringValidator(ValidatorBase):
type = StringField(required=True, enum=["string"])
minLength = NumberField(required=True, minimum=0)
maxLength = NumberField(required=True, minimum=0)


# Numeric validator for the Number Field Value input
class NumberValidator(ValidatorBase):
type = StringField(required=True, enum=["number"])
range = ArrayField(NumberField(), required=True)


# Regex validator for the text Field Value input
class RegexValidator(ValidatorBase):
type = StringField(required=True, enum=["regex"])
pattern = StringField(required=True)


# Email validator for the text Field Value input
class EmailValidator(ValidatorBase):
type = StringField(required=True, enum=["email"])


# Ipv4 represenation validator
class Ipv4Validator(ValidatorBase):
type = StringField(required=True, enum=["ipv4"])


# Date Validator
class DateValidator(ValidatorBase):
type = StringField(required=True, enum=["date"])


# URL Validator
class UrlValidator(ValidatorBase):
type = StringField(required=True, enum=["url"])

# Entity for Alert Actions
class AlertEntity(DocumentWithoutAddProp):
field = StringField(required=True, pattern="^\w+$")
label = StringField(required=True, max_length=30)
type = StringField(required=True,
enum=["text", "singleSelect", "checkbox", "radio", "singleSelectSplunkSearch"])
help = StringField(max_length=200)
defaultValue = OneOfField([
NumberField(),
StringField(max_length=250),
BooleanField()
])
required = BooleanField()
search = StringField(max_length=200)
valueField = StringField(max_length=200)
labelField = StringField(max_length=200)
options = DictField(
properties={
"items": ArrayField(DocumentField(ValueLabelPair, as_ref=True))
}
)

##
# Entity Form Field Component Wrapper having field name, label, field types, help/tooltips support, default value
# Validators and UI Controls such as visibility etc
##
class Entity(DocumentWithoutAddProp):
field = StringField(required=True, pattern="^\w+$")
label = StringField(required=True, max_length=30)
type = StringField(required=True,
enum=["custom", "text", "singleSelect", "checkbox", "multipleSelect", "radio", "placeholder", "oauth", "helpLink"])
help = StringField(max_length=200)
tooltip = StringField(max_length=250)
defaultValue = OneOfField([
NumberField(),
StringField(max_length=250),
BooleanField()
])
options = DictField(
properties={
"disableSearch": BooleanField(),
"autoCompleteFields": OneOfField([
ArrayField(DictField(
properties={
"label": StringField(required=True, max_length=150),
"children": ArrayField(DocumentField(ValueLabelPair, as_ref=True), required=True)
}
)),
ArrayField(DocumentField(ValueLabelPair, as_ref=True))
]),
"endpointUrl": StringField(max_length=350),
"denyList": StringField(max_length=350),
"allowList": StringField(max_length=350),
"delimiter": StringField(max_length=1),
"items": ArrayField(DocumentField(ValueLabelPair, as_ref=True)),
"referenceName": StringField(max_length=250),
"enable": BooleanField(),
"placeholder": StringField(max_length=250),
"display": BooleanField(),
"labelField": StringField(max_length=250),
"src": StringField(max_length=250),
"defaultValue": StringField(max_length=250),
"disableonEdit": BooleanField(),
"basic": ArrayField(DocumentField(OAuthFields, as_ref=True)),
"oauth": ArrayField(DocumentField(OAuthFields, as_ref=True)),
"auth_type": ArrayField(StringField(max_length=100)),
"auth_label": StringField(max_length=250),
"oauth_popup_width": NumberField(),
"oauth_popup_height": NumberField(),
"oauth_timeout": NumberField(),
"auth_code_endpoint": StringField(max_length=350),
"access_token_endpoint": StringField(max_length=350),
"text": StringField(max_length=50),
"link": StringField()
}
)
required = BooleanField()
encrypted = BooleanField()
# List of inbuilt field validator
validators = ArrayField(AnyOfField([
DocumentField(StringValidator, as_ref=True),
DocumentField(NumberValidator, as_ref=True),
DocumentField(RegexValidator, as_ref=True),
DocumentField(EmailValidator, as_ref=True),
DocumentField(Ipv4Validator, as_ref=True),
DocumentField(UrlValidator, as_ref=True),
DocumentField(DateValidator, as_ref=True)
]))


##
# Input Entity is super class of Entity to restrict the predefined field name holding entity field
##
class InputsEntity(Entity):
# Prevnet Splunk reserved inputs field keys being used in the user customized inputs
# https://jira.splunk.com/browse/ADDON-13014#comment-1493170

field = StringField(
required=True,
pattern="(?!^(?:persistentQueueSize|queueSize|start_by_shell|output_mode|output_field|owner|app|sharing)$)(?:^\w+$)"
)


##
# ConfigurationEntity is super class of Entity to restrict the predefined field name holding entity field such as
# output_mode
# output_field
# owner
# app
# sharing
# ##
class ConfigurationEntity(Entity):
field = StringField(
required=True,
pattern="(?!^(?:output_mode|output_field|owner|app|sharing)$)(?:^\w+$)"
)


##
# Table component schema with headers, additional info and customization row extension
##
class Table(DocumentWithoutAddProp):
moreInfo = ArrayField(DictField(
properties={
"field": StringField(required=True, pattern="^\w+$"),
"label": StringField(required=True, max_length=30),
"mapping": DictField(required=False)
}
))
# Header field names needs to be display on UI
header = ArrayField(DictField(
properties={
"field": StringField(required=True, pattern="^\w+$"),
"label": StringField(required=True, max_length=30),
"mapping": DictField(required=False),
"customCell": DictField(required=False)
}
), required=True)
# custom Row implementation if required for special cases
customRow = DictField(required=False)


##
# Input table having all functions of table and edit|delete|clone|enable/disable actions for each row
##
class InputsTable(Table):
actions = ArrayField(StringField(enum=["edit", "delete", "clone", "enable"]), required=True)


##
# Configuration table having all functions of table and edit|delete|clone|enable/disable actions for each row
##
class ConfigurationTable(Table):
actions = ArrayField(StringField(enum=["edit", "delete", "clone"]), required=True)


##
# Hook attribute scheme to define custom Hook for various events such as on load, on save and on save
##
class Hooks(DocumentWithoutAddProp):
saveValidator = StringField(max_length=3000)
onLoad = StringField(max_length=3000)
onChange = StringField(max_length=3000)


##
# Tab Content holding the wrapper of Table and Rest Handler Mapping
##
class TabContent(DocumentWithoutAddProp):
entity = ArrayField(DocumentField(ConfigurationEntity, as_ref=True), required=True)
name = StringField(required=True, pattern="^[\/\w]+$", max_length=250)
title = StringField(required=True, max_length=50)
options = DocumentField(Hooks, as_ref=True)
table = DocumentField(ConfigurationTable, as_ref=True)
conf = StringField(required=False, max_length=100)
restHandlerName = StringField(required=False, max_length=100)
# Provisioning tab level hook on configuration page
hook = DocumentField(Hooks, as_ref=True)


##
# ConfigurationPage Component having tabbing pages for configuration of various module
# Each tab is individual TabContent
##
class ConfigurationPage(DocumentWithoutAddProp):
title = StringField(required=True, max_length=60)
description = StringField(max_length=200)
tabs = ArrayField(DocumentField(TabContent, as_ref=True), required=True, min_items=1)


##
# InputsPage Component having table and entity dialogue driven by service handler to add new entry
##
class InputsPage(DocumentWithoutAddProp):
title = StringField(required=True, max_length=60)
description = StringField(max_length=200)
table = DocumentField(InputsTable, as_ref=True, required=True)
services = ArrayField(DictField(
properties={
"name": StringField(required=True, pattern="^[0-9a-zA-Z][0-9a-zA-Z_-]*$", max_length=50),
"title": StringField(required=True, max_length=100),
"entity": ArrayField(DocumentField(InputsEntity, as_ref=True), required=True),
"options": DocumentField(Hooks, as_ref=True),
"groups": ArrayField(DictField(
properties={
"options": DictField(
properties={
"isExpandable": BooleanField(),
"expand": BooleanField()
}
),
"label": StringField(required=True, max_length=100),
"field": ArrayField(StringField(required=True, pattern="^\w+$"))
}
), required=False),
"style": StringField(required=False, enum=["page", "dialog"]),
"hook": DictField(required=False),
"conf": StringField(required=False, max_length=100),
"restHandlerName": StringField(required=False, max_length=100)
}
), required=True)
menu = DictField(required=False)


##
# Main Page to holding configuration and input page
##
class Pages(DocumentWithoutAddProp):
configuration = DocumentField(ConfigurationPage, as_ref=True, required=False)
inputs = DocumentField(InputsPage, as_ref=True, required=False)


##
# Component holding Technology dict in active_response
##
class Technology(DocumentWithoutAddProp):
version = ArrayField(StringField(required=True, pattern="^\d+(?:\.\d+)*$"),required=True, min_items=1)
product = StringField(required=True, max_length=100)
vendor = StringField(required=True, max_length=100)


##
# Main Component holding the alert actions
##
class Alerts(DocumentWithoutAddProp):
name = StringField(required=True, pattern="^[a-zA-Z0-9_]+$", max_length=100)
label = StringField(required=True, max_length=100)
description = StringField(required=True)
activeResponse = DictField(properties={
"task": ArrayField(StringField(required=True), required=True, min_items=1),
"supportsAdhoc": BooleanField(required=True),
"subject": ArrayField(StringField(required=True), required=True, min_items=1),
"category": ArrayField(StringField(required=True), required=True, min_items=1),
"technology": ArrayField(DocumentField(Technology,as_ref=True),required=True, min_items=1),
"drilldownUri":StringField(required=False),
"sourcetype":StringField(required=False, pattern="^[a-zA-Z0-9:-_]+$", max_length=50)
}, required=False)
entity = ArrayField(DocumentField(AlertEntity, as_ref=True))

##
# Root Component holding all pages and meta information
##
class UCCConfig(DocumentWithoutAddProp):
meta = DocumentField(Meta, as_ref=True, required=True)
pages = DocumentField(Pages, as_ref=True, required=True)
alerts = ArrayField(DocumentField(Alerts, as_ref=True), required=False, min_items=1)

##
# SchemaGenerator responsible to generate schema json file holding information of Flow of UI based on UCCConfig Object
##
if __name__ == "__main__":
formated = json.dumps(UCCConfig.get_schema(ordered=True), indent=4)
formated = formated.replace("__main__.", "")

cur_dir = os.path.dirname(__file__)
with open(os.path.join(cur_dir, 'schema.json'), 'w+') as schema_handler:
schema_handler.write(formated)

0 comments on commit be0b945

Please sign in to comment.