Commit 55ed426b authored by Robin Knapp's avatar Robin Knapp

wip: upsert

parent b93bd8ca
......@@ -19,7 +19,7 @@ def create_app():
""" Return the flask api object that contains the routes and views. Function is called for instantiation
by gunicorn server """
develop = False if config.DEPLOYMENT_ENV == 'PROD' else True
develop = config.DEPLOYMENT_ENV == 'DEV'
# configure app and routes
app = Flask(__name__, instance_relative_config=True)
......@@ -59,7 +59,7 @@ def create_app():
cache.init_app(app)
app.config['APP_CACHE'] = cache # to access cache from different modules
app.logger.info('Setup of Flask app was successful')
app.logger.warning(f'Setup of Flask in app [{config.DEPLOYMENT_ENV}]-mode was successful')
@app.before_request
def before_request():
......
......@@ -3,6 +3,7 @@ import json
import flask_caching
from flask import Blueprint, request, current_app
from typing import Union
from src.api.server_utils import ValidationErrorResponse, extract_modelname_from_url, Connector, Converter, \
send_to_platform
......@@ -186,6 +187,55 @@ def validate_patch_ld(model: dict, model_type: str, cache: flask_caching.Cache):
log=False)
def detect_upsert_request(model: Union[dict, list], url: str) -> bool:
"""
Detect if model request contains `upsert`-Request. For potentially each datamodel the `id` and `type` attribute
have to be missing. The url must contain the name of the entity. For easier processing returns the type of the model.
Args:
model: Given model as dict or list.
url: Accesed url path. Example: /urn:ngsi-ld:FlowerBed:FlowerBed-3/attrs?
Returns:
res: True if suitable upsert or False if not suitable
Raises:
ValidationException: In case (some) model(s) feature id and type and some dont
"""
# "uniform" models needed -> all models are either upsert / not upsert, else exception
if isinstance(model, list):
# initialize according to first model (upsert / not-upsert)
if not model[0].get('id') and not model[0].get('type') and extract_modelname_from_url(url):
upsert = True
else:
upsert = False
# compare each model to the status of the previous one, abort if NOT the same status was detected
for datamodel in model[1:]:
# upsert
if not datamodel.get('id') and not datamodel.get('type') and extract_modelname_from_url(url):
if upsert:
continue
else:
raise ValidationException(
extern=f'Models for `upsert` and `regular` request detected. Mixing is not supported.',
func='detect_upsert_request', module=__name__)
# not upsert
else:
if not upsert:
continue
else:
raise ValidationException(
extern=f'Models for `upsert` and `regular` request detected. Mixing is not supported.',
func='detect_upsert_request', module=__name__)
else:
upsert = not model.get('id') and not model.get('type') and extract_modelname_from_url(url)
return upsert
@historic_validation_ld_entities.route('/<path:url>', methods=["GET"])
@validation_ld_entities.route('/<path:url>', methods=["GET"])
@historic_validation_v2_entities.route('/<path:url>', methods=["GET"])
......@@ -300,8 +350,7 @@ def post_validation_current_entities(url):
model = ast.literal_eval(request.data.decode('UTF-8')) # must be dict
if isinstance(model, list):
raise ValidationErrorResponse(msg=f'None-historic api does not support json-array.',
module=__name__)
raise ValidationErrorResponse(msg=f'None-historic api does not support json-array.', module=__name__)
forward_url = f'{current_app.config["PLATFORM_ENDPOINT"]}{request.full_path}'
......@@ -338,11 +387,16 @@ def post_validation_current_entities(url):
@historic_validation_ld_entities.route('/<path:url>', methods=["POST"])
@historic_validation_v2_entities.route('/<path:url>', methods=["POST"])
def post_validation_historic_entities(url):
model = ast.literal_eval(request.data.decode('UTF-8')) # must be dict
model = ast.literal_eval(request.data.decode('UTF-8')) # dict or list
forward_url = f'{current_app.config["PLATFORM_ENDPOINT_HISTORIC"]}{request.full_path}'
# upsert request: id and type is missing
if not model.get('id') and not model.get('type') and extract_modelname_from_url(url):
# figure type and upsert-status of the datamodel
try:
upsert = detect_upsert_request(model=model, url=url)
except ValidationException as vex:
raise ValidationErrorResponse(msg=vex.extern_logging, log=False)
if upsert:
current_app.logger.debug(f'<<{__name__}>> Historic Upsert request confirmed.')
if isinstance(model, list):
for m in model:
......
......@@ -385,7 +385,7 @@ class Converter(object):
raise ValidationException(extern=f'Internal error. Please contact admin.'
, func='Converter.convert_v2_to_kv', module=__name__)
except KeyError:
raise ValidationException(extern=f'Attributes are not compliant with model. Caused by attribute'
raise ValidationException(extern=f'Attributes are not compliant with model. Caused by attribute' \
f' "{ld_model_key}"', func='Converter.convert_v2_to_kv', module=__name__)
return kv_model
......
......@@ -37,7 +37,9 @@ class ValidationException(Exception):
def __repr__(self) -> str:
""" Represents the instance by listing the internal and external logging dictionaries."""
return f'{[self.intern_logging, self.extern_logging]}'
if self.extern_logging:
return f'{self.extern_logging}'
return 'ValidationException()'
class Validator(ABC):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment