Python \ Flask \ SQLAlchemy \ Marshmallow - как обработать запрос с повторяющимися значениями без сбоя запроса? - PullRequest
0 голосов
/ 21 июня 2020

Это только моя вторая задача (ошибка, которую мне нужно исправить) в системе Python \ Flask \ SQLAlchemy \ Marshmallow, над которой мне нужно работать. Поэтому, пожалуйста, постарайтесь быть со мной проще:)

Короче: я хотел бы одобрить явно недействительный запрос.

Подробнее:

Мне нужно обработать случай, когда пользователь может отправить запрос с некоторым json, в котором он по ошибке включил повторяющееся значение в список.

Например:

{
    "ciphers": [
        "TLS_AES_256_GCM_SHA384",
        "AES256-SHA256"
    ],
    "is_default": true,
    "tls_versions": [
        "tls10",
        "tls10",
        "tls11",
    ]
}

Что мне нужно сделать состоит в том, чтобы удалить одно из дублированных значений tls1.0, но считать запрос действительным , обновить базу данных правильными и отличными версиями tls, а в ответе вернуть не дублированный json в теле.

Текущие сегменты кода следующие:

Контроллер tls:

...
@client_side_tls_bp.route('/<string:tls_profile_id>', methods=['PUT'])
def update_tls_profile_by_id(tls_profile_id):
    return update_entity_by_id(TlsProfileOperator, entity_name, tls_profile_id)
...

общий контроллер объекта:

...
def update_entity_by_id(operator, entity_name, entity_id):
    """flask route for updating a resource"""
    try:
        entity_body = request.get_json()
    except Exception:
        return make_custom_response("Bad Request", HTTPStatus.BAD_REQUEST)

    entity_obj = operator.get(g.tenant, entity_id, g.correlation)
    if not entity_obj:
        response = make_custom_response(http_not_found_message(entity_name, entity_id), HTTPStatus.NOT_FOUND)
    else:
        updated = operator.update(g.tenant, entity_id, entity_body, g.correlation)
        if updated == "accepted":
            response = make_custom_response("Accepted", HTTPStatus.ACCEPTED)
        else:
            response = make_custom_response(updated, HTTPStatus.OK)

    return response
...

оператор tls:

...
@staticmethod
def get(tenant, name, correlation_id=None):
    try:
        tls_profile = TlsProfile.get_by_name(tenant, name)
        return schema.dump(tls_profile)
    except NoResultFound:
        return None
    except Exception:
        apm_logger.error(f"Failed to get {name} TLS profile", tenant=tenant,
                         consumer=LogConsumer.customer, correlation=correlation_id)
        raise

@staticmethod
def update(tenant, name, json_data, correlation_id=None):

    schema.load(json_data)

    try:
        dependant_vs_names = VirtualServiceOperator.get_dependant_vs_names_locked_by_client_side_tls(tenant, name)
        # locks virtual services and tls profile table simultaneously

        to_update = TlsProfile.get_by_name(tenant, name)
        to_update.update(json_data, commit=False)
        db.session.flush()  # TODO - need to change when 2 phase commit will be implemented
        snapshots = VirtualServiceOperator.get_snapshots_dict(tenant, dependant_vs_names)

        # update QWE
        # TODO handle QWE update atomically!
        for snapshot in snapshots:
            QWEController.update_abc_services(tenant, correlation_id, snapshot)

        db.session.commit()
        apm_logger.info(f"Update successfully {len(dependant_vs_names)} virtual services", tenant=tenant,
                        correlation=correlation_id)
        return schema.dump(to_update)
    except Exception:
        db.session.rollback()
        apm_logger.error(f"Failed to update {name} TLS profile", tenant=tenant,
                         consumer=LogConsumer.customer, correlation=correlation_id)
        raise
...

и в классе схемы api:

...
@validates('_tls_versions')
def validate_client_side_tls_versions(self, value):
    if len(noDuplicatatesList) < 1:
        raise ValidationError("At least a single TLS version must be provided")
    for tls_version in noDuplicatatesList:
        if tls_version not in TlsProfile.allowed_tls_version_values:
            raise ValidationError("Not a valid TLS version")
...

Я бы предпочел решить проблему на уровне схемы, чтобы дублирование не принималось.

Итак, как бы просто удалить дублирование из значения параметра "значение", как я могу распространить список недубликатов обратно, чтобы использовать его для обновления базы данных и ответа?

Спасибо.

1 Ответ

1 голос
/ 21 июня 2020

Я не тестировал, но думаю, что изменение value в функции проверки сработает.

Однако на самом деле это не гарантируется API зефира.

Правильный способ сделать было бы добавить метод post_load для дедупликации.

    @post_load
    def deduplicate_tls(self, data, **kwargs):
        if "tls_versions" in data:
            data["tls_version"] = list(set(data["tls_version"]))
        return data

Это не будет поддерживать порядок, поэтому, если порядок имеет значение, или для проблем, связанных с дедупликацией сам см. { ссылка }.

...