From 2c89f3d0c3661adeae6c4123a195c4aa4b1460ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torma=20Krist=C3=B3f?= Date: Wed, 25 Nov 2020 05:16:36 +0100 Subject: [PATCH] much work done --- requirements.txt | 1 + src/app.py | 3 +- src/kuberclient.py | 99 +++++++++++++ src/listdiffer.py | 18 +++ src/models.py | 21 ++- src/resources.py | 358 ++++++++++++++++++++++++++++++++++++++++----- src/schemas.py | 48 +++++- 7 files changed, 493 insertions(+), 55 deletions(-) create mode 100644 src/kuberclient.py create mode 100644 src/listdiffer.py diff --git a/requirements.txt b/requirements.txt index 7d9b572..9afe435 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,6 +7,7 @@ psycopg2-binary sqlalchemy flask_sqlalchemy marshmallow +marshmallow_enum marshmallow-sqlalchemy flask-marshmallow py-healthcheck diff --git a/src/app.py b/src/app.py index 37004db..b54ae80 100644 --- a/src/app.py +++ b/src/app.py @@ -8,7 +8,8 @@ from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration from healthcheck import HealthCheck from errorhandlers import register_all_error_handlers -from config import * +from config import SENTRY_DSN, RELEASE_ID, RELEASEMODE, POSTGRES_DB, PORT, POSTGRES_HOSTNAME, POSTGRES_PASSWORD, \ + POSTGRES_USERNAME, DEBUG, JWT_SECRET_KEY from db import db from jwtman import jwtman from fbcrypt import bcrypt diff --git a/src/kuberclient.py b/src/kuberclient.py new file mode 100644 index 0000000..7365503 --- /dev/null +++ b/src/kuberclient.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 + +""" +Kubernetes Client Tools +""" + +__author__ = "@tormakris" +__copyright__ = "Copyright 2020, videON Team" +__module_name__ = "kuberclient" +__version__text__ = "1" + +from kubernetes import client, config + + +class Kubectl: + def __init__(self, name: str, resourcetype: str = "ingest", stream_key: str = "test", push_urls: str = "", + ffmpeg_args: str = "", encode_push_urls: str = ""): + config.load_incluster_config() + self.name = name + self.streamtype = resourcetype.lower(), + self.stream_key = stream_key, + self.push_urls = push_urls, + self.ffmpeg_args = ffmpeg_args, + self.encode_push_url = encode_push_urls + + def __delete_pod(self): + foundpods = client.CoreV1Api().list_namespaced_pod("videon", label_selector=f"app in ({self.name})") + for pod in foundpods.items: + client.CoreV1Api().delete_namespaced_pod(pod.metadata.name, "videon") + + def __create_configmap(self): + configmap = client.V1ConfigMap(api_version="v1", kind="ConfigMap", + data={"TYPE": self.streamtype, "STREAM_KEY": self.stream_key, + "PUSH_URLS": self.push_urls, "FFMPEG_ARGS": self.ffmpeg_args, + "ENCODE_PUSH_URL": self.encode_push_url}) + client.CoreV1Api().create_namespaced_config_map(namespace="videon", name=self.name, body=configmap) + + def __update_configmap(self): + configmap = client.V1ConfigMap(api_version="v1", kind="ConfigMap", + data={"TYPE": self.streamtype, "STREAM_KEY": self.stream_key, + "PUSH_URLS": self.push_urls, "FFMPEG_ARGS": self.ffmpeg_args, + "ENCODE_PUSH_URL": self.encode_push_url}) + client.CoreV1Api().patch_namespaced_config_map(namespace="videon",name=self.name, body=configmap) + + def __create_deployment(self): + envs = [client.V1EnvFromSource(config_map_ref=client.V1ConfigMapEnvSource(name=self.name))] + container = client.V1Container( + name=self.name, + image="registry.kmlabz.com/videon/nginx-streamer", + image_pull_policy="Always", + ports=[client.V1ContainerPort(container_port=1935)], + env_from=envs, + ) + # Template + template = client.V1PodTemplateSpec( + metadata=client.V1ObjectMeta(labels={"app": self.name}), + spec=client.V1PodSpec(containers=[container])) + # Spec + spec = client.V1DeploymentSpec( + replicas=1, + template=template) + # Deployment + deployment = client.V1Deployment( + api_version="apps/v1", + kind="Deployment", + metadata=client.V1ObjectMeta(name=self.name), + spec=spec) + # Creation of the Deployment in specified namespace + client.AppsV1Api().create_namespaced_deployment( + namespace="videon", body=deployment + ) + + def __create_service(self): + core_v1_api = client.CoreV1Api() + body = client.V1Service( + api_version="v1", + kind="Service", + metadata=client.V1ObjectMeta( + name=self.name + ), + spec=client.V1ServiceSpec( + selector={"app": self.name}, + ports=[client.V1ServicePort( + port=1935, + target_port=1935 + )] + ) + ) + # Creation of the Service in specified namespace + core_v1_api.create_namespaced_service(namespace="videon", body=body) + + def create_resource(self): + self.__create_configmap() + self.__create_deployment() + self.__create_service() + + def update_resource(self): + self.__update_configmap() + self.__delete_pod() diff --git a/src/listdiffer.py b/src/listdiffer.py new file mode 100644 index 0000000..877e632 --- /dev/null +++ b/src/listdiffer.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +""" +Python list differs +""" + +__author__ = '@tormakris' +__copyright__ = "Copyright 2020, videON Team" +__module_name__ = "listdiffer" +__version__text__ = "1" + + +def elementsinfirstlistbutnotinsecond(li1, li2): + return list(set(li1) - set(li2)) + + +def elementsinsecondlistbutnotinfirst(li1, li2): + return list(set(li2)-set(li1)) diff --git a/src/models.py b/src/models.py index fdfc1e3..500de8b 100644 --- a/src/models.py +++ b/src/models.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 -from uuid import uuid4 from enum import Enum from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.orm import backref from sqlalchemy.sql import func from flask_bcrypt import generate_password_hash, check_password_hash @@ -18,7 +18,7 @@ __module_name__ = "models" __version__text__ = "1" -class User(db.Model): +class VideonUser(db.Model): id = db.Column(db.Integer, primary_key=True, autoincrement=True) name = db.Column(db.String, nullable=False, unique=True) @@ -42,18 +42,23 @@ class StreamResourceTypeEnum(Enum): class StreamResource(db.Model): - id = db.Column(UUID(as_uuid=True), primary_key=True, unique=True, nullable=False, default=uuid4) + id = db.Column(UUID(as_uuid=True), primary_key=True, unique=True, nullable=False, + server_default=db.text("uuid_generate_v4()")) resource_type = db.Column(db.Enum(StreamResourceTypeEnum), nullable=False) - url = db.Column(db.String, nullable=True) - output_urls = db.relationship('OuputUrls', backref='streamresource', lazy=False) + url = db.Column(db.String, nullable=False) bitrate = db.Column(db.Integer, nullable=True) width = db.Column(db.Integer, nullable=True) height = db.Column(db.Integer, nullable=True) - parent_id = db.Column(db.Integer, db.ForeignKey('stream_resource.id'), nullable=True) + parent_id = db.Column(UUID(as_uuid=True), db.ForeignKey('stream_resource.id'), nullable=True) children = db.relationship('StreamResource', lazy=False) + owner_id = db.Column(db.Integer, db.ForeignKey('videon_user.id'), nullable=False) + owner = db.relationship('VideonUser', backref=backref("stream_resource", cascade="all, delete"), lazy=False) + x = db.Column(db.Integer, nullable=False) + y = db.Column(db.Integer, nullable=False) -class OuputUrls(db.Model): +class OutputUrls(db.Model): id = db.Column(db.Integer, primary_key=True, autoincrement=True) output_url = db.Column(db.String, nullable=False) - streamresource_id = db.Column(db.Integer, db.ForeignKey('stream_resource.id'), nullable=False) + streamresource_id = db.Column(UUID(as_uuid=True), db.ForeignKey('stream_resource.id'), nullable=False) + streamresource = db.relationship('StreamResource', backref=backref('output_urls', cascade="all, delete"), lazy=False) diff --git a/src/resources.py b/src/resources.py index e246ba6..4de21f3 100644 --- a/src/resources.py +++ b/src/resources.py @@ -1,15 +1,17 @@ #!/usr/bin/env python3 -from uuid import uuid4 +from uuid import UUID import datetime -from flask_jwt_extended import create_access_token, jwt_required +from flask_jwt_extended import create_access_token, jwt_required, get_jwt_identity from flask_restful import Resource from flask import request, current_app, abort from db import db -from models import User -from schemas import UserSchema, UserMetadataSchema +from models import VideonUser, StreamResource, StreamResourceTypeEnum, OutputUrls +from schemas import UserSchema, UserMetadataSchema, StreamResourceSchema, IngestInputSchema, EncodeInputSchema, \ + RestreamInputSchema from config import REGISTER_DISABLED +import listdiffer """ Flask Restful endpoints @@ -20,6 +22,9 @@ __copyright__ = "Copyright 2020, videON Team" __module_name__ = "resources" __version__text__ = "1" +INVALID_JSON_SCHEMA_MSG = "invalid json schema" +DB_ERROR_MSG = "db session error" + class SignupApi(Resource): """ @@ -39,18 +44,17 @@ class SignupApi(Resource): userobj = self.userschema.load(body) except Exception as e: current_app.logger.warning(e) - abort(417, "invalid json schema") + abort(417, INVALID_JSON_SCHEMA_MSG) - user = User(name=userobj['name'], password=userobj['password']) + user = VideonUser(name=userobj['name'], password=userobj['password']) try: user.hash_password() db.session.add(user) - db.session.commit() except Exception as e: - db.session.rollback() current_app.logger.warning(e) abort(503, "user already exists") + db.session.commit() return self.usermetadataschema.dump(user), 200 @@ -69,9 +73,9 @@ class LoginApi(Resource): userobj = self.userschema.load(body) except Exception as e: current_app.logger.warning(e) - abort(417, "invalid json schema") + abort(417, INVALID_JSON_SCHEMA_MSG) - user = User.query.filter_by(name=userobj['name']).first_or_404() + user = VideonUser.query.filter_by(name=userobj['name']).first_or_404() authorized = user.check_password(userobj['password']) if not authorized: abort(401, "username or password incorrect") @@ -79,14 +83,13 @@ class LoginApi(Resource): try: user.last_logon = datetime.datetime.now() db.session.add(user) - db.session.commit() except Exception as e: - db.session.rollback() current_app.logger.exception(e) - abort(503, "db session error") + abort(503, DB_ERROR_MSG) expires = datetime.timedelta(days=7) access_token = create_access_token(identity=str(user.name), expires_delta=expires) + db.session.commit() return {'token': access_token}, 200 @@ -97,7 +100,7 @@ class UsersApi(Resource): usermetadataschema = UserMetadataSchema(many=True) def get(self): - users = User.query.all() + users = VideonUser.query.all() return self.usermetadataschema.dump(users), 200 @@ -111,7 +114,7 @@ class UserParameterApi(Resource): :param username: Username of user (url parameter) :return: """ - user = User.query.filter_by(name=username).first_or_404() + user = VideonUser.query.filter_by(name=username).first_or_404() return self.usermetadataschema.dump(user), 200 def delete(self, username: str): @@ -120,16 +123,15 @@ class UserParameterApi(Resource): :param username: Username of user (url parameter) :return: """ - user = User.query.filter_by(name=username).first_or_404() + user = VideonUser.query.filter_by(name=username).first_or_404() try: db.session.delete(user) - db.session.commit() except Exception as e: - db.session.rollback() current_app.logger.exception(e) - abort(503, "db session error") + abort(503, DB_ERROR_MSG) + db.session.commit() return self.usermetadataschema.dump(user), 200 def put(self, username: str): @@ -139,22 +141,21 @@ class UserParameterApi(Resource): :return: """ body = request.get_json() - user = User.query.filter_by(name=username).first_or_404() + user = VideonUser.query.filter_by(name=username).first_or_404() try: userobj = self.userschema.load(body) except Exception as e: current_app.logger.warning(e) - abort(417, "invalid json schema") + abort(417, INVALID_JSON_SCHEMA_MSG) try: user.password = userobj['password'] user.hash_password() - db.session.commit() except Exception as e: - db.session.rollback() current_app.logger.exception(e) - abort(503, "db session error") + abort(503, DB_ERROR_MSG) + db.session.commit() return self.usermetadataschema.dump(user), 200 @@ -162,71 +163,352 @@ class CreateIngestResource(Resource): """ See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/createIngest """ + ingestinputschema = IngestInputSchema(many=False) + streamresourceschema = StreamResourceSchema(many=False) + def post(self): - pass + body = request.get_json() + + try: + ingestobj = self.ingestinputschema.load(body) + except Exception as e: + current_app.logger.warning(e) + abort(417, INVALID_JSON_SCHEMA_MSG) + + ingest = StreamResource(resource_type=StreamResourceTypeEnum['INGEST'], + url="rtmp://zelenka.tormakristof.eu:6969/origin/asdasd", x=ingestobj['x'],y=ingestobj['y']) + + # username = get_jwt_identity() + username = "jozska" + user = VideonUser.query.filter_by(name=username).first_or_404() + ingest.owner_id = user.id + try: + db.session.add(ingest) + except Exception as e: + current_app.logger.warning(e) + abort(503, "object already exists") + + if 'outputNeighbours' in ingestobj: + for neighbour in ingestobj['outputNeighbours']: + neighbourobj = StreamResource.query.filter_by(id=neighbour).first_or_404() + if neighbourobj.resource_type == StreamResourceTypeEnum.INGEST: + abort(400, "ingest cannot be a downstream component") + try: + neighbourobj.parent_id = ingest.id + except Exception as e: + current_app.logger.warning(e) + abort(503, "could not set neighbourhood") + + db.session.commit() + return self.streamresourceschema.dump(ingest), 200 class CreateRestreamResource(Resource): """ See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/createRestream """ + restreaminputschema = RestreamInputSchema(many=False) + streamresourceschema = StreamResourceSchema(many=False) + def post(self): - pass + body = request.get_json() + + try: + restreamobj = self.restreaminputschema.load(body) + except Exception as e: + current_app.logger.warning(e) + abort(417, INVALID_JSON_SCHEMA_MSG) + + restream = StreamResource(resource_type=StreamResourceTypeEnum['RESTREAM'], + url="rtmp://zelenka.tormakristof.eu:6969/origin/asdasd",x=restreamobj['x'],y=restreamobj['y']) + + # username = get_jwt_identity() + username = "jozska" + user = VideonUser.query.filter_by(name=username).first_or_404() + restream.owner_id = user.id + if 'inputNeighbour' in restreamobj: + restream.parent_id = restreamobj['inputNeighbour'] + try: + db.session.add(restream) + db.session.commit() + except Exception as e: + current_app.logger.warning(e) + abort(503, "object already exists") + for url in restreamobj['outputURLs']: + try: + outputurl = OutputUrls(output_url=url, streamresource_id=restream.id) + db.session.add(outputurl) + except Exception as e: + current_app.logger.warning(e) + abort(503, "could not add all outputurls") + + db.session.commit() + return self.streamresourceschema.dump(restream), 200 class CreateEncodeResource(Resource): """ See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/createEncode """ + encodeinputschema = EncodeInputSchema(many=False) + streamresourceschema = StreamResourceSchema(many=False) + def post(self): - pass + body = request.get_json() + + try: + encoderobj = self.encodeinputschema.load(body) + except Exception as e: + current_app.logger.warning(e) + abort(417, INVALID_JSON_SCHEMA_MSG) + + encoder = StreamResource(resource_type=StreamResourceTypeEnum['ENCODE'], + url="rtmp://zelenka.tormakristof.eu:6969/origin/asdasd",x=encoderobj['x'],y=encoderobj['y']) + + # username = get_jwt_identity() + username = "jozska" + user = VideonUser.query.filter_by(name=username).first_or_404() + encoder.owner_id = user.id + + if 'inputNeighbour' in encoderobj: + encoder.parent_id = encoderobj['inputNeighbour'] + + if 'bitrate' in encoderobj: + encoder.bitrate = encoderobj['bitrate'] + + if 'width' in encoderobj: + encoder.bitrate = encoderobj['width'] + + if 'height' in encoderobj: + encoder.bitrate = encoderobj['height'] + + try: + db.session.add(encoder) + except Exception as e: + current_app.logger.warning(e) + abort(503, "object already exists") + + for neighbour in encoderobj['outputNeighbours']: + neighbour = StreamResource.query.filter_by(id=neighbour).first_or_404() + if neighbour.resource_type == StreamResourceTypeEnum.INGEST: + abort(400, "ingest cannot be a downstream component") + try: + neighbour.parent_id = encoder.id + except Exception as e: + current_app.logger.warning(e) + abort(503, "could not add all neighbours") + + db.session.commit() + return self.streamresourceschema.dump(encoder), 200 class GetAllStreamResources(Resource): """ See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/getResource """ + + streamresourceschema = StreamResourceSchema(many=True) + def get(self): - pass + streamreousrces = StreamResource.query.all() + return self.streamresourceschema.dump(streamreousrces), 200 class ManipulateStreamResource(Resource): - def get(self, resourceid: uuid4): + streamresourceschema = StreamResourceSchema(many=False) + + def get(self, resourceid: str): """ See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/getAResource :param resourceid: :return: """ - pass + streamreousrce = StreamResource.query.filter_by(id=UUID(resourceid)).first_or_404() + return self.streamresourceschema.dump(streamreousrce), 200 - def delete(self, resourceid: uuid4): + def delete(self, resourceid: str): """ See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/deleteResource :param resourceid: :return: """ - pass + streamreousrce = StreamResource.query.filter_by(id=UUID(resourceid)).first_or_404() + + try: + db.session.delete(streamreousrce) + except Exception as e: + current_app.logger.exception(e) + abort(503, DB_ERROR_MSG) + + db.session.commit() + return self.streamresourceschema.dump(streamreousrce), 200 class ModifyIngressResource(Resource): """ See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/editIngress """ - def put(self, resourceid: uuid4): - pass + ingestinputschema = IngestInputSchema(many=False) + streamresourceschema = StreamResourceSchema(many=False) + + def put(self, resourceid: str): + body = request.get_json() + + try: + ingestobj = self.ingestinputschema.load(body) + except Exception as e: + current_app.logger.warning(e) + abort(417, INVALID_JSON_SCHEMA_MSG) + + ingest = StreamResource.query.filter_by(id=resourceid).first_or_404() + ingest.x = ingestobj['x'] + ingest.y = ingestobj['y'] + + currentoutputneighbours = ingest.children + currentoutputneighboursids = [outputneighbour.id for outputneighbour in currentoutputneighbours] + + if 'outputNeighbours' in ingestobj: + for removeneighbours in listdiffer.elementsinfirstlistbutnotinsecond(currentoutputneighboursids, + ingestobj['outputNeighbours']): + neighbourobj = StreamResource.query.filter_by(id=removeneighbours).first_or_404() + try: + neighbourobj.parent_id = None + except Exception as e: + current_app.logger.warning(e) + abort(503, "could not remove neighbourhood") + + for addneighbours in listdiffer.elementsinsecondlistbutnotinfirst(currentoutputneighboursids, + ingestobj['outputNeighbours']): + neighbourobj = StreamResource.query.filter_by(id=addneighbours).first_or_404() + if neighbourobj.resource_type == StreamResourceTypeEnum.INGEST: + abort(400, "ingest cannot be a downstream component") + try: + neighbourobj.parent_id = ingest.id + except Exception as e: + current_app.logger.warning(e) + abort(503, "could not remove neighbourhood") + else: + if currentoutputneighbours: + for currentneighbour in currentoutputneighbours: + try: + currentneighbour.parent_id = None + except Exception as e: + current_app.logger.warning(e) + abort(503, "could not remove neighbourhood") + + db.session.commit() + return self.streamresourceschema.dump(ingest), 200 class ModifyRestreamResource(Resource): """ See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/editRestream """ - def put(self, resourceid: uuid4): - pass + restreaminputschema = RestreamInputSchema(many=False) + streamresourceschema = StreamResourceSchema(many=False) + + def put(self, resourceid: str): + body = request.get_json() + + try: + restreamobj = self.restreaminputschema.load(body) + except Exception as e: + current_app.logger.warning(e) + abort(417, INVALID_JSON_SCHEMA_MSG) + + restream = StreamResource.query.filter_by(id=resourceid).first_or_404() + restream.x = restreamobj['x'] + restream.y = restreamobj['y'] + + if 'inputNeighbour' in restreamobj: + try: + restream.parent_id = restreamobj['inputNeighbour'] + except Exception as e: + current_app.logger.warning(e) + abort(503, "could not set parent id") + + currentoutputurls = OutputUrls.query.filter_by(streamresource_id=restream.id).all() + currentoutputurlurls = [currentoutputurl.output_url for currentoutputurl in currentoutputurls] + + for deleteoutputurl in listdiffer.elementsinfirstlistbutnotinsecond(currentoutputurlurls, + restreamobj['outputURLs']): + elementtodelete = OutputUrls.query.filter_by(output_url=deleteoutputurl, + streamresource_id=restream.id).first_or_404() + try: + db.session.delete(elementtodelete) + except Exception as e: + current_app.logger.warning(e) + abort(503, "could not delete url") + for addoutputurl in listdiffer.elementsinsecondlistbutnotinfirst(currentoutputurlurls, + restreamobj['outputURLs']): + elementtoadd = OutputUrls(output_url=addoutputurl, streamresource_id=restream.id) + try: + db.session.add(elementtoadd) + except Exception as e: + current_app.logger.warning(e) + abort(503, "object already exists") + + db.session.commit() + return self.streamresourceschema.dump(restream), 200 class ModifyEncodeResource(Resource): """ See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/editEncode """ - def put(self, resourceid: uuid4): - pass + encodeinputschema = EncodeInputSchema(many=False) + streamresourceschema = StreamResourceSchema(many=False) + + def put(self, resourceid: str): + body = request.get_json() + + try: + encodeobj = self.encodeinputschema.load(body) + except Exception as e: + current_app.logger.warning(e) + abort(417, INVALID_JSON_SCHEMA_MSG) + + encode = StreamResource.query.filter_by(id=resourceid).first_or_404() + encode.x = encodeobj['x'] + encode.y = encodeobj['y'] + + if 'inputNeighbour' in encodeobj: + encode.parent_id = encodeobj['inputNeighbour'] + + if 'bitrate' in encodeobj: + encode.bitrate = encodeobj['bitrate'] + + if 'width' in encodeobj: + encode.bitrate = encodeobj['width'] + + if 'height' in encodeobj: + encode.bitrate = encodeobj['height'] + + currentoutputneighbours = encode.children + currentoutputneighboursids = [] + for outputneighbour in currentoutputneighbours: + currentoutputneighboursids.append(outputneighbour.id) + + if 'outputNeighbours' in encodeobj: + for removeneighbours in listdiffer.elementsinfirstlistbutnotinsecond(currentoutputneighbours, + encodeobj['outputNeighbours']): + neighbourobj = StreamResource.query.filter_by(id=removeneighbours).first_or_404() + neighbourobj.parent_id = None + + for addneighbours in listdiffer.elementsinsecondlistbutnotinfirst(currentoutputneighbours, + encodeobj['outputNeighbours']): + neighbourobj = StreamResource.query.filter_by(id=addneighbours).first_or_404() + if neighbourobj.resource_type == StreamResourceTypeEnum.INGEST: + abort(400, "ingest cannot be a downstream component") + neighbourobj.parent_id = encode.id + else: + if currentoutputneighbours: + for currentneighbour in currentoutputneighbours: + try: + currentneighbour.parent_id = None + except Exception as e: + current_app.logger.warning(e) + abort(503, "could not remove neighbourhood") + + db.session.commit() + return self.streamresourceschema.dump(encode), 200 diff --git a/src/schemas.py b/src/schemas.py index fe74436..7175b35 100644 --- a/src/schemas.py +++ b/src/schemas.py @@ -1,8 +1,10 @@ #!/usr/bin/env python3 from flask_marshmallow.sqla import auto_field +from marshmallow_enum import EnumField -from models import User, StreamResource +from models import VideonUser, StreamResource, StreamResourceTypeEnum, OutputUrls from marshm import ma +from db import db from marshmallow import fields @@ -33,13 +35,17 @@ class UserMetadataSchema(ma.SQLAlchemyAutoSchema): Marshmallow schema generated """ class Meta: - model = User + model = VideonUser exclude = ('timestamp', 'password', 'id',) + sqla_session = db.session creation_date = auto_field("timestamp", dump_only=False) + streamresources = auto_field class IngestInputSchema(ma.Schema): - outputNeighbours = fields.List(fields.UUID(), required=True) + outputNeighbours = fields.List(fields.UUID(), required=False) + x = fields.Integer(required=True) + y = fields.Integer(required=True) class EncodeInputSchema(ma.Schema): @@ -48,11 +54,35 @@ class EncodeInputSchema(ma.Schema): bitrate = fields.Integer(required=False) width = fields.Integer(required=False) height = fields.Integer(required=False) + x = fields.Integer(required=True) + y = fields.Integer(required=True) class RestreamInputSchema(ma.Schema): - inputNeighbour = fields.UUID(required=True) + inputNeighbour = fields.UUID(required=False) outputURLs = fields.List(fields.String(), required=True) + x = fields.Integer(required=True) + y = fields.Integer(required=True) + + +class OutputUrlSchema(ma.SQLAlchemyAutoSchema): + """ + Marshmallow schema generated + """ + class Meta: + model = OutputUrls + fields = ('output_url', ) + sqla_session = db.session + + +class StreamResourceIdSchema(ma.SQLAlchemyAutoSchema): + """ + Marshmallow schema generated + """ + class Meta: + model = StreamResource + fields = ('id',) + sqla_session = db.session class StreamResourceSchema(ma.SQLAlchemyAutoSchema): @@ -61,7 +91,9 @@ class StreamResourceSchema(ma.SQLAlchemyAutoSchema): """ class Meta: model = StreamResource - exclude = ('output_urls', 'parent_id', 'children',) - outputURLs = auto_field('output_urls', dump_only=False) - inputNeighbour = auto_field('parent_id', dump_only=False) - outputNeighbours = auto_field('children', dump_only=False) + exclude = ('parent_id',) + sqla_session = db.session + resource_type = EnumField(StreamResourceTypeEnum, by_value=True, dump_only=True) + output_urls = ma.Pluck(OutputUrlSchema, field_name='output_url', many=True, dump_only=True) + inputNeighbour = auto_field('parent_id', dump_only=True) + children = ma.Pluck(StreamResourceIdSchema, field_name='id', many=True, dump_only=True)