diff --git a/src/digital_twin_tooling/app/__init__.py b/src/digital_twin_tooling/app/__init__.py index f3e51f6..d5baacb 100644 --- a/src/digital_twin_tooling/app/__init__.py +++ b/src/digital_twin_tooling/app/__init__.py @@ -12,3 +12,4 @@ from digital_twin_tooling.app import project_api from digital_twin_tooling.app import project_execution_api +from digital_twin_tooling.app import server_api diff --git a/src/digital_twin_tooling/app/project_api.py b/src/digital_twin_tooling/app/project_api.py index c862b77..7d792e0 100644 --- a/src/digital_twin_tooling/app/project_api.py +++ b/src/digital_twin_tooling/app/project_api.py @@ -14,15 +14,16 @@ def add_ids(conf): if 'configurations' in conf: - confgis= conf['configurations'] + confgis = conf['configurations'] for c in confgis: if 'id' not in c: - c.update({'id':str(uuid.uuid4())}) + c.update({'id': str(uuid.uuid4())}) if 'tasks' in c: for t in c['tasks']: if 'id' not in t: t.update({'id': str(uuid.uuid4())}) + @app.route('/') def index(): """Base uel. @@ -36,7 +37,7 @@ def index(): return "Welcome" -@app.route('/project/schemas', methods=['GET']) +@app.route('/project/schema', methods=['GET']) def get_project_schemas(): """List all schemas --- @@ -49,7 +50,7 @@ def get_project_schemas(): with project_mgmt.get_schema(version="0.0.2") as stream: schema = yaml.load(stream, Loader=yaml.FullLoader) return app.response_class( - response=json.dumps([schema]), + response=json.dumps(schema), status=200, mimetype='application/json' ) @@ -67,7 +68,7 @@ def project_list(): """ base = Path(app.config["PROJECT_BASE"]) - return json.dumps([f.parent.name for f in base.glob('*/projects.yml')]) + return json.dumps([f.parent.name for f in base.glob('*/project.yml')]) @app.route('/projects/', methods=['POST']) @@ -322,13 +323,20 @@ def project_post_configurations_create(projectname): if request.json: new_data = request.json - new_data.update({'id': str(uuid.uuid4())}) - with open(path, 'r') as f: conf = yaml.load(f, Loader=yaml.FullLoader) + if 'configurations' not in conf: conf['configurations'] = [] + # Add id if not present. If present verify that id is unique + if not 'id' in new_data: + new_data.update({'id': str(uuid.uuid4())}) + else: + for c in conf['configurations']: + if 'id' in c and c['id'] == new_data['id']: + abort(400, f"Id '{new_data['id']}' is not unique!") + conf['configurations'].append(new_data) try: diff --git a/src/digital_twin_tooling/app/server_api.py b/src/digital_twin_tooling/app/server_api.py new file mode 100644 index 0000000..388098e --- /dev/null +++ b/src/digital_twin_tooling/app/server_api.py @@ -0,0 +1,29 @@ + + +from digital_twin_tooling.app import app +from flask import request + +@app.route('/server/shutdown', methods=['GET']) +def get_server_shutdown(): + """List all schemas + --- + + responses: + 200: + description: Accepted shutdown + + """ + shutdown_server() + + return app.response_class( + response="Shutting down..", + status=200, + mimetype='text/plain' + ) + + +def shutdown_server(): + func = request.environ.get('werkzeug.server.shutdown') + if func is None: + raise RuntimeError('Not running with the Werkzeug Server') + func() \ No newline at end of file diff --git a/src/digital_twin_tooling/data/schema-0.0.2.yml b/src/digital_twin_tooling/data/schema-0.0.2.yml index 08c266a..48c9c57 100644 --- a/src/digital_twin_tooling/data/schema-0.0.2.yml +++ b/src/digital_twin_tooling/data/schema-0.0.2.yml @@ -11,6 +11,9 @@ properties: "^[a-zA-Z0-9._-]+$": type: object properties: + type: + description: The type of tool + $ref: '#/$defs/tool_type' path: description: Local path to the tool may be relative to this file type: string @@ -53,7 +56,8 @@ $defs: port: type: integer type: - type: string + description: The type of server + $ref: '#/$defs/server_type' embedded: type: boolean required: @@ -123,6 +127,9 @@ $defs: "^.*$": type: object properties: + name: + description: User friendly name of the signal + type: string source: description: A source signal definition, describing how to obtain the signal and its type once decoded type: object @@ -169,6 +176,17 @@ $defs: - boolean - string + server_type: + description: The valid server types + enum: + - AMQP + + tool_type: + description: The valid tool types + enum: + - rabbitmq + - maestroV2 + maestro_v2: description: 'Maestro v2 configuration' type: object diff --git a/src/digital_twin_tooling/project_mgmt.py b/src/digital_twin_tooling/project_mgmt.py index 0fe7919..dfb7519 100644 --- a/src/digital_twin_tooling/project_mgmt.py +++ b/src/digital_twin_tooling/project_mgmt.py @@ -245,8 +245,8 @@ def prepare_data_repeater(conf, data_repeater_conf, output_dir: Path, base_dir=P import tempfile with tempfile.NamedTemporaryFile(prefix="AMQP-PROXY", suffix=".fmu", dir=str(output_dir), delete=False) as output_file: - - tool_path = Path(conf['tools']['rabbitmq']['path']) + toolId = data_repeater_conf['prepare']['tool'] + tool_path = Path(conf['tools'][toolId]['path']) if not tool_path.is_absolute(): tool_path = base_dir / tool_path diff --git a/tests/digital_twin_tooling/basic2.yml b/tests/digital_twin_tooling/basic2.yml index bbafbc4..893a627 100644 --- a/tests/digital_twin_tooling/basic2.yml +++ b/tests/digital_twin_tooling/basic2.yml @@ -28,13 +28,6 @@ servers: type: AMQP embedded: true - server_98: - name: Implicit embedded SQLite datastore - type: MQLite - embedded: true - - - configurations: - name: data loging of Watertank from Remote AMQP System 1 tasks: diff --git a/tests/digital_twin_tooling/test_api_project_data.py b/tests/digital_twin_tooling/test_api_project_data.py index d1dbf03..d517730 100644 --- a/tests/digital_twin_tooling/test_api_project_data.py +++ b/tests/digital_twin_tooling/test_api_project_data.py @@ -5,9 +5,10 @@ class TestProjectData(BaseCase): def test_project_schema(self): - response = self.app.get('/project/schemas') + response = self.app.get('/project/schema') self.assertEqual(200, response.status_code) - self.assertEqual(1, len(response.json)) + self.assertTrue(response.is_json) + self.assertEqual(5, len(response.json)) if __name__ == '__main__': diff --git a/tests/digital_twin_tooling/test_api_project_manipulation.py b/tests/digital_twin_tooling/test_api_project_manipulation.py index 6e499d1..23a677d 100644 --- a/tests/digital_twin_tooling/test_api_project_manipulation.py +++ b/tests/digital_twin_tooling/test_api_project_manipulation.py @@ -84,7 +84,7 @@ def test_post_sub_element_server(self): server_json = yaml.safe_load(''' server_98: name: Implicit embedded SQLite datastore - type: MQLite + type: AMQP embedded: true''') response = self.app.post('/projects/p1/config/servers', headers={"Content-Type": "application/json"}, @@ -110,7 +110,7 @@ def test_post_sub_element_tools(self): server_json = yaml.safe_load(''' my_tool: name: Some random toop - type: MaestroV2 + type: maestroV2 path: tools/something.jar''') response = self.app.post('/projects/p1/config/tools', headers={"Content-Type": "application/json"},