[CORD-1440] Moving the generative toolchain in a library
Change-Id: Ifa8e8f930ac34e1f8952099b7e34842a52f4664d
diff --git a/lib/xos-genx/xosgenx/targets/chameleon_list_test.xtarget b/lib/xos-genx/xosgenx/targets/chameleon_list_test.xtarget
new file mode 100644
index 0000000..fcb02fb
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/chameleon_list_test.xtarget
@@ -0,0 +1,19 @@
+source /opt/xos/coreapi/tests/testconfig-chameleon.sh
+
+# test modeldefs
+curl -f --silent http://$HOSTNAME:8080/xosapi/v1/modeldefs > /dev/null
+if [[ $? -ne 0 ]]; then
+ echo fail modeldefs
+fi
+
+{% for object in proto.messages %}
+{%- if object.name!='XOSBase' -%}
+curl -f --silent http://$HOSTNAME:8080/xosapi/v1/{{ xproto_unquote(options.app_label) }}/{{ xproto_pluralize(object) | lower }} > /dev/null
+if [[ $? -ne 0 ]]; then
+ echo fail {{ object.name }}
+fi
+{%endif-%}
+{%- endfor %}
+
+echo "okay"
+
diff --git a/lib/xos-genx/xosgenx/targets/django-split.xtarget b/lib/xos-genx/xosgenx/targets/django-split.xtarget
new file mode 100644
index 0000000..13212ae
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/django-split.xtarget
@@ -0,0 +1,42 @@
+{% for m in proto.messages %}{% if not m.options.skip_django -%}
+{% if file_exists(xproto_base_name(m.name)|lower+'_header.py') -%}from {{xproto_base_name(m.name)|lower }}_header import *{%- else -%}from header import *{% endif %}
+{% if file_exists(xproto_base_name(m.name)|lower+'_top.py') -%}{{ include_file(xproto_base_name(m.name)|lower+'_top.py') }} {% endif %}
+{%- for l in m.links %}
+
+{% if l.peer.name != m.name %}
+from core.models.{{ l.peer.name | lower }} import {{ l.peer.name }}
+{% endif %}
+
+{%- endfor %}
+{% for b in m.bases %}
+{% if b.name!='XOSBase' and 'Mixin' not in b.name %}
+from core.models.{{b.name | lower}} import {{ b.name }}
+{% endif %}
+{% endfor %}
+
+
+class {{ m.name }}{{ xproto_base_def(m.name, m.bases) }}:
+ # Primitive Fields (Not Relations)
+ {% for f in m.fields %}
+ {%- if not f.link -%}
+ {{ f.name }} = {{ xproto_django_type(f.type, f.options) }}( {{ xproto_django_options_str(f) }} )
+ {% endif %}
+ {%- endfor %}
+
+ # Relations
+ {% for l in m.links %}{% set peer_name=l.peer.name %}
+ {{ l.src_port }} = {{ xproto_django_link_type(l) }}( {%- if peer_name==m.name -%}'self'{%- else -%}{{ peer_name }} {%- endif -%}, {{ xproto_django_link_options_str(l, l.dst_port ) }} )
+ {%- endfor %}
+
+ # Meta
+ {%- set uniques = xproto_field_graph_components(m.fields) %}
+ {%- if uniques %}
+ class Meta:
+ unique_together = {{ xproto_tuplify(uniques) }}
+ {%- endif %}
+ {% if file_exists(m.name|lower + '_model.py') -%}{{ include_file(m.name|lower + '_model.py') | indent(width=2)}}{%- endif %}
+ pass
+
+{% if file_exists(xproto_base_name(m.name)|lower+'_bottom.py') -%}{{ include_file(xproto_base_name(m.name)|lower+'_bottom.py') }}{% endif %}
++++ {{m.name|lower}}.py
+{% endif %}{% endfor %}
\ No newline at end of file
diff --git a/lib/xos-genx/xosgenx/targets/django.xtarget b/lib/xos-genx/xosgenx/targets/django.xtarget
new file mode 100644
index 0000000..20b4e51
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/django.xtarget
@@ -0,0 +1,42 @@
+{% for m in proto.messages %}{% if not m.options.skip_django -%}
+{% if file_exists(xproto_base_name(m.name)|lower+'_header.py') -%}from {{xproto_base_name(m.name)|lower }}_header import *{%- else -%}from header import *{% endif %}
+{% if file_exists(xproto_base_name(m.name)|lower+'_top.py') -%}{{ include_file(xproto_base_name(m.name)|lower+'_top.py') }} {% endif %}
+
+{%- for l in m.links %}
+
+{% if l.peer.name != m.name %}
+from core.models.{{ l.peer.name | lower }} import {{ l.peer.name }}
+{% endif %}
+
+{%- endfor %}
+{% for b in m.bases %}
+{% if b.name!='XOSBase' and 'Mixin' not in b.name %}
+from core.models.{{b.name | lower}} import {{ b.name }}
+{% endif %}
+{% endfor %}
+
+
+class {{ m.name }}{{ xproto_base_def(m.name, m.bases) }}:
+ # Primitive Fields (Not Relations)
+ {% for f in m.fields %}
+ {%- if not f.link -%}
+ {{ f.name }} = {{ xproto_django_type(f.type, f.options) }}( {{ xproto_django_options_str(f) }} )
+ {% endif %}
+ {%- endfor %}
+
+ # Relations
+ {% for l in m.links %}{% set peer_name=l.peer.name %}
+ {{ l.src_port }} = {{ xproto_django_link_type(l) }}( {%- if peer_name==m.name -%}'self'{%- else -%}{{ peer_name }} {%- endif -%}, {{ xproto_django_link_options_str(l, l.dst_port ) }} )
+ {%- endfor %}
+
+ # Meta
+ {%- set uniques = xproto_field_graph_components(m.fields) %}
+ {%- if uniques %}
+ class Meta:
+ unique_together = {{ xproto_tuplify(uniques) }}
+ {%- endif %}
+ {% if file_exists(m.name|lower + '_model.py') -%}{{ include_file(m.name|lower + '_model.py') | indent(width=2)}}{%- endif %}
+ pass
+
+{% if file_exists(xproto_base_name(m.name)|lower+'_bottom.py') -%}{{ include_file(xproto_base_name(m.name)|lower+'_bottom.py') }}{% endif %}
+{% endif %}{% endfor %}
\ No newline at end of file
diff --git a/lib/xos-genx/xosgenx/targets/dot.xtarget b/lib/xos-genx/xosgenx/targets/dot.xtarget
new file mode 100644
index 0000000..96aceec
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/dot.xtarget
@@ -0,0 +1,7 @@
+digraph {
+{% for m in proto.messages %}
+ {%- for l in m.links %}
+ {{ m.fqn }} -> {{ l.peer.name }};
+ {%- endfor %}
+{% endfor %}
+}
diff --git a/lib/xos-genx/xosgenx/targets/grpc_api.xtarget b/lib/xos-genx/xosgenx/targets/grpc_api.xtarget
new file mode 100644
index 0000000..a0373a3
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/grpc_api.xtarget
@@ -0,0 +1,58 @@
+import base64
+import time
+from protos import xos_pb2
+from google.protobuf.empty_pb2 import Empty
+
+from django.contrib.auth import authenticate as django_authenticate
+from xos.exceptions import *
+from apihelper import XOSAPIHelperMixin, translate_exceptions
+
+class XosService(xos_pb2.xosServicer, XOSAPIHelperMixin):
+ def __init__(self, thread_pool):
+ self.thread_pool = thread_pool
+ XOSAPIHelperMixin.__init__(self)
+
+ def stop(self):
+ pass
+
+{% for object in proto.messages | sort(attribute='name') %}
+{%- if object.name!='XOSBase' %}
+ @translate_exceptions
+ def List{{ object.name }}(self, request, context):
+ user=self.authenticate(context)
+ model=self.get_model("{{ object.name }}")
+ return self.querysetToProto(model, model.objects.all())
+
+ @translate_exceptions
+ def Filter{{ object.name }}(self, request, context):
+ user=self.authenticate(context)
+ model=self.get_model("{{ object.name }}")
+ return self.filter(model, request)
+
+ @translate_exceptions
+ def Get{{ object.name }}(self, request, context):
+ user=self.authenticate(context)
+ model=self.get_model("{{ object.name }}")
+ return self.get(model, request.id)
+
+ @translate_exceptions
+ def Create{{ object.name }}(self, request, context):
+ user=self.authenticate(context)
+ model=self.get_model("{{ object.name }}")
+ return self.create(model, user, request)
+
+ @translate_exceptions
+ def Delete{{ object.name }}(self, request, context):
+ user=self.authenticate(context)
+ model=self.get_model("{{ object.name }}")
+ return self.delete(model, user, request.id)
+
+ @translate_exceptions
+ def Update{{ object.name }}(self, request, context):
+ user=self.authenticate(context)
+ model=self.get_model("{{ object.name }}")
+ return self.update(model, user, request.id, request, context)
+{%- endif %}
+{% endfor %}
+
+
diff --git a/lib/xos-genx/xosgenx/targets/grpc_list_test.xtarget b/lib/xos-genx/xosgenx/targets/grpc_list_test.xtarget
new file mode 100644
index 0000000..e968959
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/grpc_list_test.xtarget
@@ -0,0 +1,49 @@
+import grpc_client
+from grpc_client import Empty
+from testconfig import *
+
+c=grpc_client.InsecureClient("xos-core.cord.lab")
+
+{% for object in proto.messages %}
+{%- if object.name!='XOSBase' %}
+print "testing insecure List{{ object.name }}...",
+c.stub.List{{ object.name }}(Empty())
+print "Okay"
+{%- endif %}
+{%- endfor %}
+
+c=grpc_client.SecureClient("xos-core.cord.lab", username=USERNAME, password=PASSWORD)
+
+{% for object in proto.messages %}
+{%- if object.name!='XOSBase' %}
+print "testing basic secure List{{ object.name }}...",
+c.stub.List{{ object.name }}(Empty())
+print "Okay"
+{%- endif %}
+{%- endfor %}
+
+# now try to login
+c=grpc_client.InsecureClient("xos-core.cord.lab")
+lr=grpc_client.LoginRequest()
+lr.username=USERNAME
+lr.password=PASSWORD
+session=c.utility.Login(lr)
+
+c=grpc_client.SecureClient("xos-core.cord.lab", sessionid=session.sessionid)
+{% for object in proto.messages %}
+{%- if object.name!='XOSBase' %}
+print "testing session secure List{{ object.name }}...",
+c.stub.List{{ object.name }}(Empty())
+print "Okay"
+{%- endif %}
+{%- endfor %}
+
+c=grpc_client.SecureClient("xos-core.cord.lab", sessionid=session.sessionid)
+{% for object in proto.messages %}
+{%- if object.name!='XOSBase' %}
+print "testing session secure xos_orm.{{ object.name }}.objects.all() ...",
+c.xos_orm.{{ object.name }}.objects.all()
+print "Okay"
+{%- endif %}
+{%- endfor %}
+
diff --git a/lib/xos-genx/xosgenx/targets/init.xtarget b/lib/xos-genx/xosgenx/targets/init.xtarget
new file mode 100644
index 0000000..235a657
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/init.xtarget
@@ -0,0 +1,15 @@
+# The hardcoded entries cannot be inferred from the models. To get rid of in the long run
+#
+
+from .xosbase import XOSBase,XOSBaseManager,XOSBaseDeletionManager,PlModelMixIn,ModelLink
+from .contenttype import ContentType
+from .site import Site
+from .dashboardview import DashboardView
+from .user import User
+from .user import UserDashboardView
+
+{% for m in proto.messages -%}
+{% if not m.options.skip_init -%}
+from .{{ m.name | lower }} import {{ m.name }}
+{% endif -%}
+{% endfor -%}
diff --git a/lib/xos-genx/xosgenx/targets/link-graph.xtarget b/lib/xos-genx/xosgenx/targets/link-graph.xtarget
new file mode 100644
index 0000000..04c9dba
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/link-graph.xtarget
@@ -0,0 +1,14 @@
+{
+{%- for model in proto.messages %}
+{%- if model.links %}
+ "{{ model.name }}": [
+ {% for l in model.links -%}
+ ["{{ l.peer }}", "{{ l.src_port }}", "{{ l.dst_port }}"]{% if not loop.last %},{% endif %}
+ {%- endfor %}
+ {%- if model.rlinks %},{% endif %}
+ {% for l in model.rlinks -%}
+ ["{{ l.peer }}", "{{ l.src_port }}", "{{ l.dst_port }}"]{% if not loop.last %},{% endif %}
+ {%- endfor %}
+ ],{% endif -%}
+{% endfor %}
+}
diff --git a/lib/xos-genx/xosgenx/targets/model-deps-graphviz.xtarget b/lib/xos-genx/xosgenx/targets/model-deps-graphviz.xtarget
new file mode 100644
index 0000000..b855a4a
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/model-deps-graphviz.xtarget
@@ -0,0 +1,7 @@
+digraph {{ context.app_label }} {
+{%- for model in proto.messages %}
+ {%- for l in model.links %}
+ "{{ model.name }}" -> "{{ l.peer }}" [label="{{ l.src_port }}"];
+ {%- endfor %}
+{%- endfor %}
+}
diff --git a/lib/xos-genx/xosgenx/targets/model-deps.xtarget b/lib/xos-genx/xosgenx/targets/model-deps.xtarget
new file mode 100644
index 0000000..65a3742
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/model-deps.xtarget
@@ -0,0 +1,8 @@
+{
+{%- for model in proto.messages %}
+{%- if model.links %}
+ "{{ model.name }}": [
+ {{ model.links | map(attribute='peer') | format_list("\"%s\"") |join(',\n\t\t') }}
+ ],{% endif -%}
+{% endfor %}
+}
diff --git a/lib/xos-genx/xosgenx/targets/modeldefs.xtarget b/lib/xos-genx/xosgenx/targets/modeldefs.xtarget
new file mode 100644
index 0000000..615a8cc
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/modeldefs.xtarget
@@ -0,0 +1,37 @@
+items:
+{%- for m in proto.messages | sort(attribute='name') %}
+{%- if m.name != 'XOSBase' %}
+- app: {{ xproto_unquote(xproto_first_non_empty([m.options.app_label, context.app_label, options.app_label])) }}
+ fields:
+ {%- set id_field = {'type':'int32', 'name':'id', 'options':{}} %}
+ {% for f in (xproto_base_fields(m, proto.message_table) + m.fields + [id_field]) | sort(attribute='name') -%}
+ {% if not f.link or f.options.link_type != 'manytomany' -%}
+ - hint: {% if f.options.help_text %}{{ xproto_unquote(f.options.help_text) }}{% else %}''{% endif %}
+ {% if not f.link -%}
+ name: {{ f.name }}
+ {%- else -%}
+ name: {{ f.name }}_id
+ relation: {model: {{ f.options.model }}, type: {{ f.options.link_type }}}
+ {% endif %}
+ type: {{ xproto_type_to_ui_type(f) }}
+ {% set validators = xproto_validators(f) -%}
+ {% if validators -%}
+ validators:
+ {% for v in validators | sort(attribute='name',reverse=True) -%}
+ - {{ v | yaml }}
+ {% endfor %}
+ {% else -%}
+ validators: []
+ {% endif %}
+ {% endif -%}
+ {% endfor %}
+ name: {{ m.name }}
+ {%- set goodlinks = xproto_links_to_modeldef_relations( xproto_base_links(m, proto.message_table) + m.links ) %}
+ {% if goodlinks %}
+ relations:
+ {{ goodlinks | join('\n') | indent(width=2)}}
+ {%- else %}
+ relations: []
+ {%- endif %}
+{%- endif %}
+{% endfor -%}
diff --git a/lib/xos-genx/xosgenx/targets/plurals.xtarget b/lib/xos-genx/xosgenx/targets/plurals.xtarget
new file mode 100644
index 0000000..9cdd6c6
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/plurals.xtarget
@@ -0,0 +1,6 @@
+{% for m in proto.messages %}
+Model {{ m.name }}:
+{% for f in m.fields -%}
+The plural of {{ f.name}} is {{ xproto_pluralize(f) }}.
+{% endfor %}
+{%- endfor %}
diff --git a/lib/xos-genx/xosgenx/targets/proto.xtarget b/lib/xos-genx/xosgenx/targets/proto.xtarget
new file mode 100644
index 0000000..27b9490
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/proto.xtarget
@@ -0,0 +1,8 @@
+{% for m in proto.messages %}
+message {{ m.name }} {
+ option bases = "{{ m.bases | map(attribute='name') | join(",") }}";
+ {%- for f in m.fields %}
+ {{ f.modifier }} {{f.type}} {{f.name}} = {{ f.id }}{% if f.options %} [{% for k,v in f.options.iteritems() %} {{ k }} = "{{ v}}"{% if not loop.last %},{% endif %} {% endfor %}]{% endif %};
+ {%- endfor %}
+}
+{% endfor %}
diff --git a/lib/xos-genx/xosgenx/targets/protoapi.xtarget b/lib/xos-genx/xosgenx/targets/protoapi.xtarget
new file mode 100644
index 0000000..f375dae
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/protoapi.xtarget
@@ -0,0 +1,88 @@
+syntax = "proto3";
+
+package xos;
+
+import "google/protobuf/empty.proto";
+import "google/api/annotations.proto";
+import "common.proto";
+import "xosoptions.proto";
+
+// Note: all fields are wrapped in a "oneof". This causes proto3 to always send
+// fields that are set by the caller, regardless if they are set to a default
+// value. XOS uses this to know when to apply a default value.
+
+{% for object in proto.messages|sort(attribute='name') %}
+{% if object.name != 'XOSBase' -%}
+message {{ object.name }} {
+ {%- if object.name=='CordSubscriberRoot' %}
+ option (contentTypeId) = "rcord.{{ object.name | lower }}";
+ {%- else %}
+ option (contentTypeId) = "{{ xproto_unquote(xproto_first_non_empty([object.options.name, object.options.app_label, options.name, context.app_label])) }}.{{ object.name | lower }}";
+ {%- endif %}
+ {%- set id_field = {'type':'int32', 'name':'id', 'options':{}} -%}
+ {%- for field in (xproto_base_fields(object, proto.message_table) + object.fields + [id_field]) | sort(attribute='name')%}
+ oneof {{ field.name }}_present {
+ {{ xproto_api_type(field) }} {{ field.name }}{% if field.link -%}_id{% endif %} = {{ loop.index }}{{ xproto_api_opts(field) }};
+ }
+ {%- endfor -%}
+
+ {%- for ref in xproto_base_rlinks(object, proto.message_table) + object.rlinks | sort(attribute='src_port') %}
+ {%- if '+' not in ref.src_port and '+' not in ref.dst_port %}
+ repeated int32 {{ ref.src_port }}_ids = {{ loop.index + 100 }} [(reverseForeignKey).modelName = "{{ ref.peer.name }}"];
+ {%- endif -%}
+ {%- endfor %}
+ string class_names = 201;
+ string self_content_type_id = 202;
+}
+
+message {{ xproto_pluralize(object) }} {
+ repeated {{ object.name }} items = 1;
+}
+
+{%- endif %}
+{% endfor %}
+
+service xos {
+{% for object in proto.messages | sort(attribute='name')%}
+{% if object.name != 'XOSBase' -%}
+ rpc List{{ object.name }}(google.protobuf.Empty) returns ({{ xproto_pluralize(object) }}) {
+ option (google.api.http) = {
+ {%- if object.name=='CordSubscriberRoot' %}
+ get: "/xosapi/v1/rcord/{{ xproto_pluralize(object) | lower }}"
+ {%- else %}
+ get: "/xosapi/v1/{{ xproto_unquote(xproto_first_non_empty([object.options.name, object.options.app_label, options.name, context.app_label])) }}/{{ xproto_pluralize(object) | lower }}"
+ {%- endif %}
+ };
+ }
+ rpc Filter{{ object.name }}(Query) returns ({{ xproto_pluralize(object) }}) {
+ }
+ rpc Get{{ object.name }}(ID) returns ({{ object.name }}) {
+ option (google.api.http) = {
+ {%- if object.name=='CordSubscriberRoot' %}
+ get: "/xosapi/v1/rcord/{{ xproto_pluralize(object) | lower }}/{id}"
+ {%- else %}
+ get: "/xosapi/v1/{{ xproto_unquote(xproto_first_non_empty([object.options.name, object.options.app_label, options.name, context.app_label])) }}/{{ xproto_pluralize(object) | lower }}/{id}"
+ {%- endif %}
+ };
+ }
+ rpc Create{{ object.name }}({{ object.name }}) returns ({{ object.name }}) {
+ option (google.api.http) = {
+ post: "/xosapi/v1/{{ xproto_unquote(xproto_first_non_empty([object.options.name, object.options.app_label, options.name, context.app_label])) }}/{{ xproto_pluralize(object) | lower }}"
+ body: "*"
+ };
+ }
+ rpc Update{{ object.name }}({{ object.name }}) returns ({{ object.name }}) {
+ option (google.api.http) = {
+ put: "/xosapi/v1/{{ xproto_unquote(xproto_first_non_empty([object.options.name, object.options.app_label, options.name, context.app_label])) }}/{{ xproto_pluralize(object) | lower }}/{id}"
+ body: "*"
+ };
+ }
+ rpc Delete{{ object.name }}(ID) returns (google.protobuf.Empty) {
+ option (google.api.http) = {
+ delete: "/xosapi/v1/{{ xproto_unquote(xproto_first_non_empty([object.options.name, object.options.app_label, options.name, context.app_label])) }}/{{ xproto_pluralize(object) | lower }}/{id}"
+ };
+ }
+{%- endif %}
+{% endfor %}
+}
+
diff --git a/lib/xos-genx/xosgenx/targets/service.xtarget b/lib/xos-genx/xosgenx/targets/service.xtarget
new file mode 100644
index 0000000..1ba3cff
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/service.xtarget
@@ -0,0 +1,56 @@
+{% if options.legacy =='"True"' -%}
+{% set legacy_tag = '_decl' %}
+{% set legacy = True %}
+from core.models.xosbase import *
+{% else %}
+{% set legacy = False %}
+{% set legacy_tag = '' %}
+from header import *
+{% endif %}
+
+{% for m in proto.messages %}
+{% if file_exists(m.name|lower+'_header.py') -%}from {{m.name|lower }}_header import *{% endif %}
+{% if file_exists(m.name|lower+'_top.py') -%}{{ include_file(m.name|lower+'_top.py') }} {% endif %}
+
+{%- for l in m.links -%}{% set peer_name=l.peer.name %}
+{% if peer_name not in proto.message_names -%}
+from core.models import {{ peer_name }}
+{%- endif -%}
+{%- endfor -%}
+{%- for b in m.bases -%}
+{%- if b.name!='XOSBase' and 'Mixin' not in b.name %}
+from core.models import {{ b.name }}
+{%- endif -%}
+{% endfor %}
+
+{% endfor %}
+
+{% for m in proto.messages %}
+class {{ m.name }}{{ legacy_tag }}{{ xproto_base_def(m.name, m.bases) }}:
+
+ KIND = {{ xproto_first_non_empty([m.options.kind, options.kind, options.name, "Set a kind in your xproto!"]) }}
+
+ class Meta:
+ app_label = {{ xproto_first_non_empty([m.options.app_label, options.app_label, options.name, "Set an app label in your xproto!"]) | lower}}
+ # name = {{ xproto_first_non_empty([m.options.name, options.name, "Set a name in your xproto!"]) }}
+ verbose_name = {{ xproto_first_non_empty([m.options.verbose_name, options.verbose_name, "Set a verbose name in your xproto!"]) }}
+
+ # Primitive Fields (Not Relations)
+ {% for f in m.fields %}
+ {%- if not f.link -%}
+ {{ f.name }} = {{ xproto_django_type(f.type, f.options) }}( {{ xproto_django_options_str(f) }} )
+ {% endif %}
+ {%- endfor %}
+
+ # Relations
+ {% for l in m.links %}{% set peer_name=l.peer.name %}
+ {% if legacy and peer_name in proto.message_names %}{% set peer_tag = legacy_tag %}{% else %}{% set peer_tag = '' %}{% endif -%}
+ {{ l.src_port }} = {{ xproto_django_link_type(l) }}( {%- if peer_name==m.name -%}'self'{%- else -%}{{ peer_name }}{{ peer_tag }} {%- endif -%}, {{ xproto_django_options_str(l, l.dst_port ) }} )
+ {%- endfor %}
+
+ {% if file_exists(m.name|lower + '_model.py') -%}{{ include_file(m.name|lower + '_model.py') | indent(width=2)}}{%- endif %}
+ pass
+
+{% if file_exists(m.name|lower+'_bottom.py') -%}{{ include_file(m.name|lower+'_bottom.py') }}{% endif %}
+{% endfor %}
++++ models{{ legacy_tag }}.py
\ No newline at end of file
diff --git a/lib/xos-genx/xosgenx/targets/service_extender.xtarget b/lib/xos-genx/xosgenx/targets/service_extender.xtarget
new file mode 100644
index 0000000..5b4bc03
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/service_extender.xtarget
@@ -0,0 +1,20 @@
+from core.models.xosbase import *
+{% for m in proto.messages %}
+from models_decl import {{ m.name }}_decl
+{%- endfor %}
+
+{% for m in proto.messages %}
+{% for l in m.links %}{% set peer_name=l.peer.name -%}
+{% if peer_name not in proto.message_names -%}
+from core.models import {{ peer_name }}
+{% endif -%}
+{% endfor -%}
+{% endfor -%}
+
+{% for m in proto.messages %}
+class {{ m.name }}({{ m.name }}_decl):
+ class Meta:
+ proxy = True
+
+{% endfor %}
++++ models.py
diff --git a/lib/xos-genx/xosgenx/targets/tosca.xtarget b/lib/xos-genx/xosgenx/targets/tosca.xtarget
new file mode 100644
index 0000000..b5e51ae
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/tosca.xtarget
@@ -0,0 +1,31 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+node_types:
+{% for m in proto.messages %}
+ tosca.nodes.{{ m.name }}:
+ derived_from: tosca.nodes.Root
+ description: {{ m.name }}
+ properties:
+ no-delete:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to delete this object
+ no-create:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to create this object
+ no-update:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
+ {%- for f in m.fields %}
+ {{ f.name }}:
+ type: {{ f.type }}
+ required: {{ xproto_tosca_required(f.options.blank) }}
+ description: {{ f.options.help_text }}
+ {%- endfor %}
+{%- endfor %}
\ No newline at end of file
diff --git a/lib/xos-genx/xosgenx/targets/xproto.xtarget b/lib/xos-genx/xosgenx/targets/xproto.xtarget
new file mode 100644
index 0000000..513d7a7
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/xproto.xtarget
@@ -0,0 +1,18 @@
+{% for k,v in options.iteritems() %}
+option {{ k }} = {{ v }};
+{% endfor %}
+
+{% for m in proto.messages %}
+message {{ m.name }} ({{ m.bases | join(",") }}){
+ {%- for f in m.fields %}
+ {% if not f.link %}
+ {{ f.modifier }} {{f.type}} {{f.name}} = {{ f.id }}{% if f.options %} [{% for k,v in f.options.iteritems() %} {{ k }} = {{ v }}{% if not loop.last %},{% endif %} {% endfor %}]{% endif %};
+ {% endif %}
+ {%- endfor %}
+
+ {%- for l in m.links %}{% set peer_name=l.peer.name %}
+ {{ l }}
+ {{ l.modifier }} {{ l.link_type }} {{ l.name }} -> {{ l.peer.name }}:{{ l.dst_port }} = 1 {% if l.options %} [{% for k,v in l.options.iteritems() %} {{ k }} = {{ v }}{% if not loop.last %},{% endif %} {% endfor %}]{% endif %};
+ {%- endfor -%}
+}
+{% endfor %}