Skip to content
Open
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 MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
include LICENSE
recursive-include dxlbootstrap/generate/templates/app/static/schema *
2 changes: 1 addition & 1 deletion dist.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def replace(file_path, pattern, subst):
"--universal"])

# cp -rf config dist
print("\nCopying config in to dist directory\n")
print("\nCopying config into dist directory\n")
copy_tree(os.path.join(DIST_PY_FILE_LOCATION, "config"), os.path.join(DIST_DIRECTORY, "config"))

# Copy everything in to release dir
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
${schemaYaml}
81 changes: 80 additions & 1 deletion dxlbootstrap/generate/templates/app/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import Template, TemplateConfig, TemplateConfigSection, PythonPackageConfigSection
from dxlbootstrap.generate.core.component \
import DirTemplateComponent, FileTemplateComponent, CodeTemplateComponent

from dxlbootstrap.generate.util.schema_utils \
import DxlSchemaWriter

class AppTemplateConfig(TemplateConfig):
"""
Expand Down Expand Up @@ -353,6 +354,83 @@ def _build_sample_directory(context, components_dict):
sample_basic_dir.add_child(basic_sample_comp)
components_dict["basic_sample_comp"] = basic_sample_comp

@staticmethod
def _copy_schema_files(context, components_dict, dir_comp):
"""
Copies the application schema file to the specified directory

:param context: The template context
:param components_dict: Dictionary containing components by name (and other info)
:param dir_comp: The directory component to copy the files to
"""

del components_dict

config = context.template.template_config
app_name = str(config.application_section.name)
service_names = config.application_section.services

yaml_writer = DxlSchemaWriter(app_name)

if service_names:
for service_name in service_names:
service = config.get_service_section(service_name)
service_type = str(service.service_type)

yaml_writer.add_service_def_to_schema(service_type)
yaml_writer.add_service_ref_to_solution(service_type)

request_handlers = service.request_handlers
if request_handlers:
for request_handler in request_handlers:
req_handler_def = config.get_request_handler_section(request_handler)
topic = str(req_handler_def.topic)

yaml_writer.add_request_def_to_schema(topic)
yaml_writer.add_request_ref_to_service(service_type, topic)

event_handlers = config.application_section.event_handlers
if event_handlers:
for event_handler in event_handlers:
event_handler_def = config.get_event_handler_section(event_handler)
topic = str(event_handler_def.topic)

yaml_writer.add_event_def_to_schema(topic)
yaml_writer.add_event_ref_to_solution(topic)

schema_dict = yaml_writer.schema_dict_yaml

file_comp = FileTemplateComponent(
app_name + ".yaml",
"schema/v0.1/schema.tmpl",
{
"schemaContent": schema_dict
}
)
dir_comp.add_child(file_comp)

@staticmethod
def _build_schema_directory(context, components_dict):
"""
Builds the "schema" directory components of the output

:param context: The template context
:param components_dict: Dictionary containing components by name (and other info)
:return: The "schema" directory components of the output
"""

root = components_dict["root"]

schema_dir = DirTemplateComponent("schema")
root.add_child(schema_dir)

schema_version_dir = DirTemplateComponent("v0.1")
schema_dir.add_child(schema_version_dir)

components_dict["schema_comp"] = schema_dir

AppTemplate._copy_schema_files(context, components_dict, schema_version_dir)

def _build_docs_directory(self, context, components_dict):
"""
Builds the "docs" directory components of the output
Expand Down Expand Up @@ -621,6 +699,7 @@ def _get_root_component(self, context):
self._build_root_directory(context, components_dict)
self._build_config_directory(context, components_dict)
self._build_sample_directory(context, components_dict)
self._build_schema_directory(context, components_dict)
self._build_docs_directory(context, components_dict)
self._build_application_directory(context, components_dict)
self._build_event_handlers(context, components_dict)
Expand Down
Empty file.
177 changes: 177 additions & 0 deletions dxlbootstrap/generate/util/schema_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# -*- coding: utf-8 -*-
################################################################################
# Copyright (c) 2019 McAfee LLC - All Rights Reserved.
################################################################################

import copy
import yaml

# The OpenDXL API Specification version
SCHEMA_VERSION = "0.1"

# Keys to be used for schema objects/definitions
SOLUTION_KEY = "solutions"
SERVICE_KEY = "services"
EVENT_KEY = "events"
REQUEST_KEY = "requests"

# Service definition
SERVICE_DICT_TMPL = {
"info": {
#"title"
"version": "<version of this DXL service (may be the same as the solution)>",
"description": "This is a description of this service."
},
"externalDocs": {
"description": "<external link such as a github repository or API>",
"url": "http://opendxl.com"
},
REQUEST_KEY: []
}

# Base message definition
MESSAGE_PROPS_DICT_TMPL = {
"description": "This is a description of this message.",
"payload": {
"properties": {
"property1": {
"description": "This is a description of this property.",
"type": "<message property type (ex: integer, object, string, etc.)>"
},
"property2": {
"description": "This is a description of this property.",
"type": "<message property type (ex: integer, object, string, etc.)>"
}
},
"example": {
"property1": "<Example value of property1>",
"property2": "<Example value of property2>"
}
}
}

# Request definition
REQUEST_DICT_TMPL = copy.deepcopy(MESSAGE_PROPS_DICT_TMPL)
REQUEST_DICT_TMPL["response"] = copy.deepcopy(MESSAGE_PROPS_DICT_TMPL)

# Event definition
EVENT_DICT_TMPL = copy.deepcopy(MESSAGE_PROPS_DICT_TMPL)
EVENT_DICT_TMPL["isIncoming"] = True


def topic_ref_transform(ref_string, topic):
"""
Converts a topic string with forward-slashes into a JSON reference-friendly format
("/" becomes "~1"), then places the new string into the provided base reference path.

:param ref_string: The base reference path
:param topic: The DXL topic string to convert
"""
return str(ref_string.format(topic.replace("/", "~1")))


class DxlSchemaWriter(object):

def __init__(self, app_name):
"""
Constructs the schema writer.

:param app_name: The application name
"""

# Define the app name and the general template for the schema
self.app_name = app_name
self.schema_dict_tmpl = {
"openDxlApi": SCHEMA_VERSION,
"info": {
"title": app_name,
"version": "0.1",
"description": "This is a general description of this API specification.",
"contact": {
"url": "www.company-website-url.com"
}
},
SOLUTION_KEY: {
app_name: {
"info": {
"title": app_name,
"version": "<solution/product version number>",
"description": "This is a description of this solution."
},
"externalDocs": {
"description": "This is a link to further documentation for this solution.",
"url": "http://opendxl.com"
},
SERVICE_KEY: [],
EVENT_KEY: []
}
},
SERVICE_KEY: {},
EVENT_KEY: {},
REQUEST_KEY: {}
}

@property
def schema_dict_yaml(self):
"""
Returns the current constructed version of the schema.
"""
return yaml.dump(self.schema_dict_tmpl, default_flow_style=False)

def add_service_ref_to_solution(self, service_type):
"""
Adds a service reference to a solution's "services" array.

:param service_type: The service type
"""
self.schema_dict_tmpl[SOLUTION_KEY][self.app_name][SERVICE_KEY]\
.append({"$ref": topic_ref_transform("#/services/{0}", service_type)})

def add_event_ref_to_solution(self, event_topic):
"""
Adds an event message reference to a solution's "events" array.

:param event_topic: The DXL topic on which the event is sent
"""
self.schema_dict_tmpl[SOLUTION_KEY][self.app_name][EVENT_KEY]\
.append({"$ref": topic_ref_transform("#/events/{0}", event_topic)})

def add_request_ref_to_service(self, service_type, request_topic):
"""
Adds a request message reference to a service's "requests" array.

:param service_type: The DXL topic on which the event is sent
:param request_topic: The DXL topic on which the request is sent
"""
self.schema_dict_tmpl[SERVICE_KEY][service_type][REQUEST_KEY]\
.append({"$ref": topic_ref_transform("#/requests/{0}", request_topic)})

def add_service_def_to_schema(self, service_type):
"""
Adds a service definition to the schema. Uses the service type as a
key for the service definition object.

:param service_type: The service type
"""
self.schema_dict_tmpl[SERVICE_KEY][service_type] = \
copy.deepcopy(SERVICE_DICT_TMPL)

def add_request_def_to_schema(self, request_topic):
"""
Adds a request message definition to the schema. Uses the request
topic as a key for the request definition object.

:param request_topic: The DXL topic on which the request is sent
"""
self.schema_dict_tmpl[REQUEST_KEY][request_topic] = \
copy.deepcopy(REQUEST_DICT_TMPL)

def add_event_def_to_schema(self, event_topic):
"""
Adds an event message definition to the schema. Uses the event
topic as a key for the event definition object.

:param event_topic: The DXL topic on which the event is sent
"""
self.schema_dict_tmpl[EVENT_KEY][event_topic] = \
copy.deepcopy(EVENT_DICT_TMPL)
8 changes: 6 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ def run(self):

# Requirements
install_requires=[
"dxlclient>=4.1.0.184"
"dxlclient>=4.1.0.184",
"pyyaml>=5.1"
],

tests_require=TEST_REQUIREMENTS,
Expand Down Expand Up @@ -106,11 +107,14 @@ def run(self):
"dxlbootstrap.generate.templates.client.static.sample.basic",
"dxlbootstrap.generate.templates.client.static.sample.basic.code",
"dxlbootstrap.generate.templates.client.static.doc",
"dxlbootstrap.generate.templates.client.static.doc.sdk"
"dxlbootstrap.generate.templates.client.static.doc.sdk",
"dxlbootstrap.generate.util"
],

package_data={'': ['*.tmpl']},

include_package_data=True,

# Details
url="http://www.mcafee.com/",

Expand Down
2 changes: 2 additions & 0 deletions tests/test_dxlbootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ def test_generate_application_command(self):
mock_print.assert_called_with("Generation succeeded.")
self.assertTrue(os.path.exists(
os.path.join(app_dir, "geolocationservice", "app.py")))
self.assertTrue(os.path.exists(
os.path.join(app_dir, "schema/v0.1", "geolocationservice.yaml")))

def test_generate_client_command(self):
with _TempDir("genclient") as temp_dir, \
Expand Down