diff --git a/.gitignore b/.gitignore index 8cd8dd1..42445f1 100644 --- a/.gitignore +++ b/.gitignore @@ -144,4 +144,4 @@ broker/broker dump.rdb -examples \ No newline at end of file +examples diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..68ca047 --- /dev/null +++ b/CHANGES @@ -0,0 +1,23 @@ +# 2020-12-9 + +## am + +建立apiflow路径文件。 + +**TODO** + +- 重写MetaConfig +- 重写ViewFlow + + +## pm + +改写renderer 和 response 部分 + +# 2020-12-11 + +完成一个基本流程 + +# 2020-12-12 + +handler 负责分派请求到不同的流处理。 \ No newline at end of file diff --git a/README.md b/README.md index 7004fbe..0cace65 100644 --- a/README.md +++ b/README.md @@ -856,3 +856,12 @@ model类及meta文件的具体路径。 ### 运行项目 python manage.py runserver + + +## 通过model_config生成models.py + +在app(account)下新建model_config文件夹,里面放置model的config.json文件。 + + python manage.py gen_models --models account.model_config + +可以获得**account/models.py**文件。 \ No newline at end of file diff --git a/arkfbp/common/django/app/automation/flows/modeling.py b/arkfbp/common/django/app/automation/flows/modeling.py index a293bc8..79b26e4 100644 --- a/arkfbp/common/django/app/automation/flows/modeling.py +++ b/arkfbp/common/django/app/automation/flows/modeling.py @@ -15,6 +15,7 @@ PaginationNode) from arkfbp.utils.util import get_class_from_path, json_load +# import rest_framework # ModelSerializerNode metadata _AUTO_MODEL_SERIALIZER_NODE_NAME = 'auto_model_serializer' diff --git a/arkfbp/common/django/drf/__init__.py b/arkfbp/common/django/drf/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/arkfbp/common/django/drf/flows/__init__.py b/arkfbp/common/django/drf/flows/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/arkfbp/common/django/drf/flows/create_handler.py b/arkfbp/common/django/drf/flows/create_handler.py new file mode 100644 index 0000000..8201c41 --- /dev/null +++ b/arkfbp/common/django/drf/flows/create_handler.py @@ -0,0 +1,41 @@ +""" +siteapi flow +""" +from arkfbp.node import StartNode, StopNode +from arkfbp.flow import Flow +from common.django.drf.nodes.create import CreateCore + + +class CreateHandler(Flow): + """ + Main FLow. + """ + + def create_nodes(self): + """ + These nodes cannot be changed,if you are sure to do that, + you can initialize a new SerializerCore node. + """ + return [ + { + 'cls': StartNode, + 'id': 'start', + 'next': 'inter', + 'x': None, + 'y': None + }, + { + 'cls': CreateCore, + 'id': 'inter', + 'next': 'stop', + 'x': None, + 'y': None + }, + { + 'cls': StopNode, + 'id': 'stop', + 'next': None, + 'x': None, + 'y': None + } + ] diff --git a/arkfbp/common/django/drf/flows/destroy_handler.py b/arkfbp/common/django/drf/flows/destroy_handler.py new file mode 100644 index 0000000..c499877 --- /dev/null +++ b/arkfbp/common/django/drf/flows/destroy_handler.py @@ -0,0 +1,42 @@ +""" +siteapi flow +""" +from arkfbp.node import StartNode, StopNode +from arkfbp.flow import Flow +from common.django.drf.nodes.destroy import DestroyCore +from common.django.drf.nodes.intermediate import InterMediateCore + + +class DestroyHandler(Flow): + """ + Main FLow. + """ + + def create_nodes(self): + """ + These nodes cannot be changed,if you are sure to do that, + you can initialize a new SerializerCore node. + """ + return [ + { + 'cls': StartNode, + 'id': 'start', + 'next': 'destroy', + 'x': None, + 'y': None + }, + { + 'cls': DestroyCore, + 'id': 'destroy', + 'next': 'stop', + 'x': None, + 'y': None + }, + { + 'cls': StopNode, + 'id': 'stop', + 'next': None, + 'x': None, + 'y': None + } + ] diff --git a/arkfbp/common/django/drf/flows/dispatch.py b/arkfbp/common/django/drf/flows/dispatch.py new file mode 100644 index 0000000..c75c802 --- /dev/null +++ b/arkfbp/common/django/drf/flows/dispatch.py @@ -0,0 +1,58 @@ +""" +siteapi flow +""" +from arkfbp.node import StartNode, StopNode +from common.django.drf.nodes.init_request import InitRequestCore +from common.django.drf.nodes.method_handler import HandlerCore +from common.django.drf.generics import ModelViewSet + +from common.django.drf.nodes.renderer import RendererCore + + +class Main(ModelViewSet): + """ + Main FLow. + """ + + def create_nodes(self): + """ + These nodes cannot be changed,if you are sure to do that, + you can initialize a new SerializerCore node. + """ + return [ + { + 'cls': StartNode, + 'id': 'start', + 'next': 'init', + 'x': None, + 'y': None + }, + { + 'cls': InitRequestCore, + 'id': 'init', + 'next': 'handler_core', + 'x': None, + 'y': None + }, + { + 'cls': HandlerCore, + 'id': 'handler_core', + 'next': 'renderer_core', + 'x': None, + 'y': None + }, + { + 'cls': RendererCore, + 'id': 'renderer_core', + 'next': 'stop', + 'x': None, + 'y': None + }, + { + 'cls': StopNode, + 'id': 'stop', + 'next': None, + 'x': None, + 'y': None + } + ] diff --git a/arkfbp/common/django/drf/flows/list_handler.py b/arkfbp/common/django/drf/flows/list_handler.py new file mode 100644 index 0000000..48f1f42 --- /dev/null +++ b/arkfbp/common/django/drf/flows/list_handler.py @@ -0,0 +1,49 @@ +""" +siteapi flow +""" +from arkfbp.node import StartNode, StopNode +from arkfbp.flow import Flow +from common.django.drf.nodes.get_queryset import QuerysetCore +from common.django.drf.nodes.many_serialize import ManySerializeCore + + +class ListHandler(Flow): + """ + Main FLow. + """ + + def create_nodes(self): + """ + These nodes cannot be changed,if you are sure to do that, + you can initialize a new SerializerCore node. + """ + return [ + { + 'cls': StartNode, + 'id': 'start', + 'next': 'get_queryset', + 'x': None, + 'y': None + }, + { + 'cls': QuerysetCore, + 'id': 'get_queryset', + 'next': 'serialize', + 'x': None, + 'y': None + }, + { + 'cls': ManySerializeCore, + 'id': 'serialize', + 'next': 'stop', + 'x': None, + 'y': None + }, + { + 'cls': StopNode, + 'id': 'stop', + 'next': None, + 'x': None, + 'y': None + } + ] diff --git a/arkfbp/common/django/drf/flows/retrieve_handler.py b/arkfbp/common/django/drf/flows/retrieve_handler.py new file mode 100644 index 0000000..86afbd0 --- /dev/null +++ b/arkfbp/common/django/drf/flows/retrieve_handler.py @@ -0,0 +1,49 @@ +""" +siteapi flow +""" +from arkfbp.node import StartNode, StopNode +from arkfbp.flow import Flow +from common.django.drf.nodes.get_object import GetObjectCore +from common.django.drf.nodes.obj_serialize import ObjSerializeCore + + +class RetrieveHandler(Flow): + """ + Main FLow. + """ + + def create_nodes(self): + """ + These nodes cannot be changed,if you are sure to do that, + you can initialize a new SerializerCore node. + """ + return [ + { + 'cls': StartNode, + 'id': 'start', + 'next': 'get_object', + 'x': None, + 'y': None + }, + { + 'cls': GetObjectCore, + 'id': 'get_object', + 'next': 'serialize', + 'x': None, + 'y': None + }, + { + 'cls': ObjSerializeCore, + 'id': 'serialize', + 'next': 'stop', + 'x': None, + 'y': None + }, + { + 'cls': StopNode, + 'id': 'stop', + 'next': None, + 'x': None, + 'y': None + } + ] diff --git a/arkfbp/common/django/drf/flows/update_handler.py b/arkfbp/common/django/drf/flows/update_handler.py new file mode 100644 index 0000000..e0eecaf --- /dev/null +++ b/arkfbp/common/django/drf/flows/update_handler.py @@ -0,0 +1,49 @@ +""" +siteapi flow +""" +from arkfbp.node import StartNode, StopNode +from arkfbp.flow import Flow +from common.django.drf.nodes.get_object import GetObjectCore +from common.django.drf.nodes.update_obj_serialize import UpdateObjSerializerCore + + +class UpdateHandler(Flow): + """ + Main FLow. + """ + + def create_nodes(self): + """ + These nodes cannot be changed,if you are sure to do that, + you can initialize a new SerializerCore node. + """ + return [ + { + 'cls': StartNode, + 'id': 'start', + 'next': 'get_object', + 'x': None, + 'y': None + }, + { + 'cls': GetObjectCore, + 'id': 'get_object', + 'next': 'update', + 'x': None, + 'y': None + }, + { + 'cls': UpdateObjSerializerCore, + 'id': 'update', + 'next': 'stop', + 'x': None, + 'y': None + }, + { + 'cls': StopNode, + 'id': 'stop', + 'next': None, + 'x': None, + 'y': None + } + ] diff --git a/arkfbp/common/django/drf/generics.py b/arkfbp/common/django/drf/generics.py new file mode 100644 index 0000000..597833d --- /dev/null +++ b/arkfbp/common/django/drf/generics.py @@ -0,0 +1,115 @@ +"""Process requests from django. + +A flow is a django view which process request. + We convert an interface in config.json to an APIView, and process requests with flows. +""" +from arkfbp.graph import GraphParser +from rest_framework import serializers +from rest_framework.viewsets import ModelViewSet as ModelViewSetBase +from arkfbp.executer import Executer +from arkfbp.flow.base import Flow, FLOW_ERROR +from state import FlowState, AppState + + +class ViewsetMeta(type): + """ + 要用这个元类生成一个具有全部求值环境的ModelViewSet类 + """ + + def __new__(cls, *args, **kwargs): + return super().__new__(cls, *args, **kwargs) + + +class GeneralSerializer(serializers.ModelSerializer): + + class Meta: + model = None + fields = '__all__' + + +# pylint: disable=abstract-method +class ModelViewSet(ModelViewSetBase, Flow): + """ + 经过改造后的ModelViewSet基类。混合两个主要特性: Flow 的特性, 标准查改增删特性 + """ + + def __init__(self, *args, **kwargs): + self.graph = self.create_graph() + self._state = FlowState() + self._app_state = AppState() + manual_state = self.create_state() + if isinstance(manual_state, dict): + self.state.commit(manual_state) + self._request = None + self._response = None + self._status = 'CREATED' + self.error = None + super().__init__(*args, **kwargs) + + def get_queryset(self): + model = self.model + return model.objects.all() + + def get_serializer_class(self): + if self.serializer_class: + return self.serializer_class + else: + GeneralSerializer.Meta.model = self.model + return GeneralSerializer + + def dispatch(self, request, *args, **kwargs): + """ + override django view function dispatch. + """ + self.args = args + self.kwargs = kwargs + if request.method in self.allowed_methods: + return Executer.start_flow(self, request, *args, **kwargs) + + @classmethod + def set_http_method(cls, method: list): + """ + set http method. + """ + cls.allow_http_method = method + + def shutdown(self, outputs, **kwargs): + """ + shutdown the flow. + """ + self.response_status = kwargs.get('response_status', 200) + super().shutdown(outputs) + + @property + def response(self): + """ + generate a http response. + """ + return self.outputs + + def terminate(self, exception): + """ + when a exception raises in a flow,it will be called. + """ + self._status = FLOW_ERROR + self.error = exception + response = self.handle_exception(exception) + + response = self.finalize_response(self.request, response, *self.args, **self.kwargs) + self.outputs = response + + def run_sub_flow(self, sub_flow, *args, **kwargs): + """在当前上下文环境运行一个sub_flow,获得结果。""" + graph_parser = GraphParser(sub_flow.graph) + graph_node = graph_parser.get_entry_node() + outputs = None + while graph_node: + # 获取`node`实例 + graph_node = graph_parser.parse_graph_node(graph_node) + node = graph_node.instance + # 运行`node`实例 + outputs = Executer.start_node(node, self, graph_node, *args, **kwargs) + if not self.valid_status(): + return self.outputs + graph_node = graph_node.next_graph_node(outputs) + return outputs diff --git a/arkfbp/common/django/drf/meta_config.py b/arkfbp/common/django/drf/meta_config.py new file mode 100644 index 0000000..3796c83 --- /dev/null +++ b/arkfbp/common/django/drf/meta_config.py @@ -0,0 +1,123 @@ +""" +Automatic crash project code. +""" +import importlib +import os + +from django.apps import apps +from common.django.drf.generics import ViewsetMeta +from django.urls import path +from common.django.drf.flows.dispatch import Main as ModelViewSet +#from .generics import ModelViewSet +from arkfbp.common.django.app.automation.flows.meta_config.main import Main as MetaConfigView + +from django.apps import apps +from rest_framework.routers import DefaultRouter, SimpleRouter +from utils.util import json_load + +REQUIRED_FIELDS = ('name', 'type', 'meta', 'api', 'module') +DEFAULT_API_TYPES = {'create': 'POST', 'delete': 'DELETE', 'update': 'PATCH', 'retrieve': 'GET'} +ALLOW_HTTP_METHOD = ('POST', 'GET', 'DELETE', 'PUT', 'PATCH') + + +#def _import_serializer_class(self, location: str): +# """ +# Resolves a dot-notation string to serializer class. +# . will automatically be interpreted as: +# .serializers. +# """ +# pieces = location.split(".") +# class_name = pieces.pop() +# +# if pieces[len(pieces) - 1] != "serializers": +# pieces.append("serializers") +# +# module = importlib.import_module(".".join(pieces)) +# return getattr(module, class_name) + +def parse_router(router_info): + return None + +def parse_env_from_conf(app_label, filename, conf): + """从conf文件里面读取router, queryset, model, serializer 等、RESTAPI求值环境所需要的变量。""" + router_info = conf.get('api', None) + route_path = conf.get('name', None) + model_name = conf.get('model', None) + serializer_class_name = conf.get('serializer_class', None) + + model = None + queryset = None + serializer_class = None + router = None + try: + model = apps.get_model(model_name) + except FloatingPointError as e: + print(e) + queryset = model.objects.all() + if serializer_class_name: + serializer_class = importlib.import_module(serializer_class_name) + if router_info: + router = parse_router(router_info) + else: + router = SimpleRouter() + return router, route_path, { + 'model': model, + 'queryset': queryset, + 'serializer_class': serializer_class + } + + +class MetaConfigs: + """从各个app/api_config里面取出含有"model"字段的conf,每个conf代表一个viewset和一系列url。""" + + def __init__(self, apps): + self._apps = apps + + def get_urls(self): + urlpatterns = [] + for app_label in self._apps: + urls = self.get_urls_of_one_app(app_label) + urlpatterns += urls + return urlpatterns + + def get_urls_of_one_app(self, app_label): + """从一个app的 api_config里面读出所有具有model定义的conf.json生成一套Viewset. + """ + urlpatterns = [] + app_path = apps.get_app_config(app_label).path + conf_path = os.path.join(app_path, 'api_config') + for root, _, files in os.walk(conf_path): + for file in files: + if file.endswith('.json'): + data = json_load(os.path.join(conf_path, file)) + filename = file.split('.')[0] + if 'model' in data: # 如果config里面定义了 model字段,就说明这是一套查改增删接口。 + viewset_name = f'{app_label}' + router, route_path, env = parse_env_from_conf(app_label, filename, data) + Viewset = ViewsetMeta(viewset_name, (ModelViewSet,), env) + router.register(route_path, Viewset) + urlpatterns += router.urls + urlpatterns += self.config_url() + return urlpatterns + + def config_url(self): + """ + add config url + """ + urlpatterns = [] + for app_label in self._apps: + urlpatterns += self.config_url_of_one_app(app_label) + return urlpatterns + + def config_url_of_one_app(self, app_label): + urlpatterns = [] + app_path = apps.get_app_config(app_label).path + conf_path = os.path.join(app_path, 'api_config') + for root, _, files in os.walk(conf_path): + for file in files: + if file.endswith('.json'): + _cls_attrs = {'allow_http_method': ['GET'], 'file_dir': app_path, 'debug': False} + _cls_bases = (MetaConfigView, ) + view_class = type('MetaConfig', _cls_bases, _cls_attrs) + urlpatterns += [path(f'meta_config//', view_class.pre_as_view(), name='meta_config')] + return urlpatterns diff --git a/arkfbp/common/django/drf/nodes/__init__.py b/arkfbp/common/django/drf/nodes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/arkfbp/common/django/drf/nodes/create.py b/arkfbp/common/django/drf/nodes/create.py new file mode 100644 index 0000000..02410f4 --- /dev/null +++ b/arkfbp/common/django/drf/nodes/create.py @@ -0,0 +1,19 @@ +""" +Permission Core Nodes +""" +from arkfbp.node import FunctionNode +from rest_framework import status +from rest_framework.response import Response + + +class CreateCore(FunctionNode): + """ + permission core node. + """ + def run(self, *args, **kwargs): + serializer_cls = self.flow.get_serializer_class() + data = self.flow.request.data + srl = serializer_cls(data=data) + srl.is_valid(raise_exception=True) + srl.save() + return Response(srl.data) diff --git a/arkfbp/common/django/drf/nodes/destroy.py b/arkfbp/common/django/drf/nodes/destroy.py new file mode 100644 index 0000000..843eb13 --- /dev/null +++ b/arkfbp/common/django/drf/nodes/destroy.py @@ -0,0 +1,17 @@ +""" +Permission Core Nodes +""" +from arkfbp.node import FunctionNode +from rest_framework import status +from rest_framework.response import Response + + +class DestroyCore(FunctionNode): + """ + permission core node. + """ + def run(self, *args, **kwargs): + obj = self.flow.get_object() + obj.delete() + response = Response(status=status.HTTP_204_NO_CONTENT) + return response diff --git a/arkfbp/common/django/drf/nodes/get_object.py b/arkfbp/common/django/drf/nodes/get_object.py new file mode 100644 index 0000000..39b7c06 --- /dev/null +++ b/arkfbp/common/django/drf/nodes/get_object.py @@ -0,0 +1,18 @@ +""" +Permission Core Nodes +""" +from arkfbp.node import FunctionNode +from rest_framework.response import Response + + +class GetObjectCore(FunctionNode): + """ + permission core node. + """ + def run(self, *args, **kwargs): + """获取该请求指向的数据实体。 + inputs: None + outputs: Model instance + """ + return self.flow.get_object() + diff --git a/arkfbp/common/django/drf/nodes/get_queryset.py b/arkfbp/common/django/drf/nodes/get_queryset.py new file mode 100644 index 0000000..062710e --- /dev/null +++ b/arkfbp/common/django/drf/nodes/get_queryset.py @@ -0,0 +1,17 @@ +""" +Permission Core Nodes +""" +from arkfbp.executer import Executer +from arkfbp.node import FunctionNode +from common.django.app.automation.flows.modeling import get_api_config, get_permission, API_PERMISSION, set_flow_debug + +# Editor your node here. + + +class QuerysetCore(FunctionNode): + """ + permission core node. + """ + def run(self, *args, **kwargs): + queryset = self.flow.get_queryset() + return queryset diff --git a/arkfbp/common/django/drf/nodes/init_request.py b/arkfbp/common/django/drf/nodes/init_request.py new file mode 100644 index 0000000..0d672c9 --- /dev/null +++ b/arkfbp/common/django/drf/nodes/init_request.py @@ -0,0 +1,26 @@ +""" +Permission Core Nodes +""" +from arkfbp.node import FunctionNode + + +class InitRequestCore(FunctionNode): + """ + permission core node. + """ + def run(self, *args, **kwargs): + req = self.flow.request + request = self.flow.initialize_request(req, *args, **kwargs) + self.flow.request = request + self.flow.headers = self.flow.default_response_headers + self.flow.format_kwarg = self.flow.get_format_suffix(**kwargs) + + # Perform content negotiation and store the accepted info on the request + neg = self.flow.perform_content_negotiation(request) + request.accepted_renderer, request.accepted_media_type = neg + + # Determine the API version, if versioning is in use. + version, scheme = self.flow.determine_version(request, *args, **kwargs) + request.version, request.versioning_scheme = version, scheme + return None + diff --git a/arkfbp/common/django/drf/nodes/intermediate.py b/arkfbp/common/django/drf/nodes/intermediate.py new file mode 100644 index 0000000..03c8c1f --- /dev/null +++ b/arkfbp/common/django/drf/nodes/intermediate.py @@ -0,0 +1,14 @@ +""" +Permission Core Nodes +""" +from arkfbp.node import FunctionNode +from rest_framework.response import Response + + +class InterMediateCore(FunctionNode): + """ + permission core node. + """ + def run(self, *args, **kwargs): + response = Response() + return response diff --git a/arkfbp/common/django/drf/nodes/many_serialize.py b/arkfbp/common/django/drf/nodes/many_serialize.py new file mode 100644 index 0000000..fdac611 --- /dev/null +++ b/arkfbp/common/django/drf/nodes/many_serialize.py @@ -0,0 +1,16 @@ +""" +Permission Core Nodes +""" +from arkfbp.node import FunctionNode +from rest_framework.response import Response + + +class ManySerializeCore(FunctionNode): + """ + permission core node. + """ + def run(self, *args, **kwargs): + queryset = self.inputs + serializer_cls = self.flow.get_serializer_class() + srl = serializer_cls(queryset, many=True) + return Response(srl.data) diff --git a/arkfbp/common/django/drf/nodes/method_handler.py b/arkfbp/common/django/drf/nodes/method_handler.py new file mode 100644 index 0000000..cb33ac9 --- /dev/null +++ b/arkfbp/common/django/drf/nodes/method_handler.py @@ -0,0 +1,33 @@ +""" +Permission Core Nodes +""" +from arkfbp.node import FlowNode + + +class HandlerCore(FlowNode): + """ + permission core node. + """ + from common.django.drf.flows.list_handler import ListHandler + from common.django.drf.flows.retrieve_handler import RetrieveHandler + from common.django.drf.flows.create_handler import CreateHandler + from common.django.drf.flows.update_handler import UpdateHandler + from common.django.drf.flows.destroy_handler import DestroyHandler + list_handler = ListHandler() + create_handler = CreateHandler() + retrieve_handler = RetrieveHandler() + update_handler = UpdateHandler() + destroy_handler = DestroyHandler() + dispatcher = { + 'list': list_handler, + 'retrieve': retrieve_handler, + 'create': create_handler, + 'update': update_handler, + 'partial_update': update_handler, + 'destroy': destroy_handler + } + + def get_child_flow(self, *args, **kwargs): + return self.dispatcher[self.flow.action] + + diff --git a/arkfbp/common/django/drf/nodes/obj_serialize.py b/arkfbp/common/django/drf/nodes/obj_serialize.py new file mode 100644 index 0000000..b8f0b83 --- /dev/null +++ b/arkfbp/common/django/drf/nodes/obj_serialize.py @@ -0,0 +1,16 @@ +""" +Permission Core Nodes +""" +from arkfbp.node import FunctionNode +from rest_framework.response import Response + + +class ObjSerializeCore(FunctionNode): + """ + permission core node. + """ + def run(self, *args, **kwargs): + obj = self.inputs + serializer_cls = self.flow.get_serializer_class() + srl = serializer_cls(obj) + return Response(srl.data) diff --git a/arkfbp/common/django/drf/nodes/renderer.py b/arkfbp/common/django/drf/nodes/renderer.py new file mode 100644 index 0000000..3abb7ce --- /dev/null +++ b/arkfbp/common/django/drf/nodes/renderer.py @@ -0,0 +1,22 @@ +""" +Permission Core Nodes +""" +from arkfbp.node import FunctionNode +from rest_framework.response import Response + + +class RendererCore(FunctionNode): + """Render response to HttpResponse + + The input is a Drf Response instance here. + And the output will be an HttpResponse + """ + def run(self, *args, **kwargs): + # the inputs is an Drf Response Here. + # + assert isinstance(self.inputs, Response), 'Require Response instance as inputs' + request = self.flow.request + + response = self.flow.finalize_response(request, self.inputs, *args, **kwargs) + + return response diff --git a/arkfbp/common/django/drf/nodes/update_obj_serialize.py b/arkfbp/common/django/drf/nodes/update_obj_serialize.py new file mode 100644 index 0000000..b8da439 --- /dev/null +++ b/arkfbp/common/django/drf/nodes/update_obj_serialize.py @@ -0,0 +1,18 @@ +""" +Permission Core Nodes +""" +from arkfbp.node import FunctionNode +from rest_framework.response import Response + + +class UpdateObjSerializerCore(FunctionNode): + """ + permission core node. + """ + def run(self, *args, **kwargs): + obj = self.inputs + serializer_cls = self.flow.get_serializer_class() + srl = serializer_cls(obj, data=self.flow.request.data, partial=True) + srl.is_valid(raise_exception=True) + srl.save() + return Response(srl.data) diff --git a/arkfbp/common/django/drf/utils.py b/arkfbp/common/django/drf/utils.py new file mode 100644 index 0000000..b2de2ef --- /dev/null +++ b/arkfbp/common/django/drf/utils.py @@ -0,0 +1,47 @@ +import json +import os + +def convert_def_to_model_lines(definition): + lines = [] + model_name = definition.get('className') + lines += [f'class {model_name}(models.Model):'] + for field in definition.get('fields'): + field_name = field.get('name') + field_func = field.get('field') + field_props = field.get('properties') + wrapper = '"%s"' + prop_string = ', '.join([f'{key}={value if not isinstance(value, str) else wrapper % value}' for + key, value in field_props.items()]) + field_line = f' {field_name} = models.{field_func}({prop_string})' + lines += [field_line,] + lines += [''] + return lines + + +def convert_json_model_to_lines(json_model): + lines = [] + models = json_model.get('models') + for model in models: + lines += ['' ] + model_string = convert_def_to_model_lines(model) + lines += model_string + return lines + + +def convert_json_file_to_model_lines(json_file): + with open(json_file, 'r', encoding='utf8') as _file: + data = json.load(_file) + lines = convert_json_model_to_lines(data) + return lines + +def convert_directory_configs_to_models(directory): + lines = [] + lines += ['from django.db import models'] + lines += [''] + + for root, dirs, files in os.walk(directory): + for filename in files: + if filename.endswith('.json'): + lines += convert_json_file_to_model_lines(os.path.join(root, filename)) + + return lines diff --git a/arkfbp/common/django/management/commands/gen_models.py b/arkfbp/common/django/management/commands/gen_models.py new file mode 100644 index 0000000..c3b652a --- /dev/null +++ b/arkfbp/common/django/management/commands/gen_models.py @@ -0,0 +1,22 @@ +import os + +from common.django.drf.utils import convert_directory_configs_to_models +from django.core.management.base import BaseCommand + + +class Command(BaseCommand): + help = "generate model files" + + def handle(self, **options): + current_path = os.path.abspath(os.path.curdir) + config_path = '/'.join( options.get('models').split('.')) + real_path = os.path.join(current_path, config_path) + model_path = os.path.dirname(real_path) + lines = convert_directory_configs_to_models(real_path) + model_filename = os.path.join(model_path, 'models.py') + with open(model_filename, 'w') as model_file: + model_file.write('\n'.join(lines)) + + + def add_arguments(self, parser): + parser.add_argument('--models', type=str, help='Specifies the import path for the target flow name.') diff --git a/arkfbp/flow/base.py b/arkfbp/flow/base.py index bbb2a37..c108f31 100644 --- a/arkfbp/flow/base.py +++ b/arkfbp/flow/base.py @@ -165,8 +165,8 @@ def log_debug(self): sys.stdout.write(f'Inputs: {node.inputs}\n') sys.stdout.write(f'Outputs: {node.outputs}\n') sys.stdout.write('****** END *******\n\n') - if self.valid_status(target=FLOW_ERROR): - raise self.error + #if self.valid_status(target=FLOW_ERROR): + # raise self.error sys.stdout.write(f'------------- DEBUG END ({self}) -------------\n') def before_initialize(self, *args, **kwargs): diff --git a/arkfbp/node/__init__.py b/arkfbp/node/__init__.py index 6496c2b..6d36b8b 100644 --- a/arkfbp/node/__init__.py +++ b/arkfbp/node/__init__.py @@ -2,6 +2,7 @@ from .api_node import APINode from .base import Node from .function_node import FunctionNode +from .flow_node import FlowNode from .if_node import IFNode from .loop_node import LoopNode from .nop_node import NopNode diff --git a/arkfbp/node/flow_node.py b/arkfbp/node/flow_node.py new file mode 100644 index 0000000..e08b638 --- /dev/null +++ b/arkfbp/node/flow_node.py @@ -0,0 +1,23 @@ +from executer import Executer + +from .base import Node + +# FunctionNode metadata +_NODE_NAME = 'flow' +_NODE_KIND = 'flow' + + +class FlowNode(Node): + name = _NODE_NAME + kind = _NODE_KIND + child_flow = None + + def get_child_flow(self, *args, **kwargs): + if not self.child_flow: + raise NotImplemented('You must assign flow for a flow node') + return self.child_flow + + def run(self, *args, **kwargs): + _flow = self.get_child_flow(*args, **kwargs) + ret = self.flow.run_sub_flow(_flow, self.inputs, *args, **kwargs) + return ret \ No newline at end of file