~aleteoryx/muditaos

8c5ad8a42063fa6e542282792c3cd913e135bffa — Lefucjusz 5 months ago df12fba
[BH-2096] Return error code when switching to empty quotes DB

* Added mechanics returning error code 406 Not Acceptable
when trying to switch to empty custom quotes database.
* Minor cleanups.
* Minor fixes in test script.
M module-db/Common/Query.cpp => module-db/Common/Query.cpp +2 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2025, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/blob/master/LICENSE.md

#include "Query.hpp"


@@ 53,7 53,7 @@ bool EndpointListenerWithPages::handleQueryResponse(db::QueryResult *response)
        return ret;
    }

    LOG_ERROR("callback is nullptr!");
    LOG_ERROR("Callback is nullptr!");
    return false;
}


M module-db/Common/Query.hpp => module-db/Common/Query.hpp +7 -6
@@ 1,4 1,4 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2025, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/blob/master/LICENSE.md

#pragma once


@@ 12,10 12,12 @@ namespace db
    using sdesktop::endpoints::Context;
    using sdesktop::endpoints::PagedContext;

    class QueryResult; // Forward declaration
    class QueryResult;

    using QueryCallbackFunction                  = std::function<bool(db::QueryResult *)>;
    using EndpointQueryCallbackFunction          = std::function<bool(db::QueryResult *, Context &)>;
    using EndpointQueryCallbackFunctionWithPages = std::function<bool(db::QueryResult *, PagedContext &)>;

    class QueryListener
    {
      public:


@@ 98,8 100,8 @@ namespace db

        void setRequestQuery(const std::shared_ptr<Query> &requestQueryToSet);
        std::shared_ptr<Query> getRequestQuery() const noexcept;
        void setRecordID(const uint32_t modifiedRecordID);
        [[nodiscard]] auto getRecordID() -> std::optional<uint32_t>;
        void setRecordID(const std::uint32_t modifiedRecordID);
        [[nodiscard]] auto getRecordID() -> std::optional<std::uint32_t>;

        bool handle();



@@ 110,7 112,6 @@ namespace db
        std::shared_ptr<Query> requestQuery;

      private:
        std::optional<uint32_t> recordID;
        std::optional<std::uint32_t> recordID;
    };

} // namespace db

M module-services/service-db/DBServiceAPI.cpp => module-services/service-db/DBServiceAPI.cpp +10 -10
@@ 16,19 16,14 @@
#include <SMSTemplateRecord.hpp>
#include <system/Common.hpp>
#include <Service/Service.hpp>
#include <ThreadRecord.hpp>
#include <Utils.hpp>
#include <log/log.hpp>
#include <queries/messages/threads/QueryThreadGetByNumber.hpp>
#include <queries/phonebook/QueryNumberGetByID.hpp>

#include <utility>
#include <cassert>

namespace constants
{
    constexpr std::uint32_t DefaultTimeoutInMs = 10000U;
    constexpr std::uint32_t BackupTimeoutInMs  = 120000U;
} // namespace constants

struct NotesRecord;


@@ 350,16 345,21 @@ bool DBServiceAPI::QuotesDeleteEntry(sys::Service *serv,
    return result;
}

void DBServiceAPI::QuotesGroupChanged(sys::Service *serv, const std::string &group)
bool DBServiceAPI::QuotesGroupChanged(sys::Service *serv,
                                      const std::string &group,
                                      std::unique_ptr<db::QueryListener> &&listener)
{
    auto query = std::make_unique<Quotes::Messages::InformGroupChanged>(group);
    DBServiceAPI::GetQuery(serv, db::Interface::Name::Quotes, std::move(query));
    query->setQueryListener(std::move(listener));
    const auto [result, _] = DBServiceAPI::GetQuery(serv, db::Interface::Name::Quotes, std::move(query));
    return result;
}

void DBServiceAPI::QuotesIntervalChanged(sys::Service *serv, const std::string &interval)
bool DBServiceAPI::QuotesIntervalChanged(sys::Service *serv, const std::string &interval)
{
    auto query = std::make_unique<Quotes::Messages::InformIntervalChanged>(interval);
    DBServiceAPI::GetQuery(serv, db::Interface::Name::Quotes, std::move(query));
    auto query             = std::make_unique<Quotes::Messages::InformIntervalChanged>(interval);
    const auto [result, _] = DBServiceAPI::GetQuery(serv, db::Interface::Name::Quotes, std::move(query));
    return result;
}

bool DBServiceAPI::QuotesGetGroup(sys::Service *serv, std::unique_ptr<db::QueryListener> &&listener)

M module-services/service-db/DBServiceAPI_GetByQuery.cpp => module-services/service-db/DBServiceAPI_GetByQuery.cpp +2 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2025, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/blob/master/LICENSE.md

#include "service-db/DBServiceAPI.hpp"


@@ 6,7 6,6 @@
#include "service-db/QueryMessage.hpp"

#include <BaseInterface.hpp>
#include <Service/Message.hpp>
#include <Service/Service.hpp>

#include <memory>


@@ 32,5 31,5 @@ sys::SendResult DBServiceAPI::GetQueryWithReply(sys::Service *serv,
                                                std::uint32_t timeout)
{
    auto msg = std::make_shared<db::QueryMessage>(database, std::move(query));
    return serv->bus.sendUnicastSync(msg, service::name::db, timeout);
    return serv->bus.sendUnicastSync(std::move(msg), service::name::db, timeout);
}

M module-services/service-db/include/service-db/DBServiceAPI.hpp => module-services/service-db/include/service-db/DBServiceAPI.hpp +6 -2
@@ 23,10 23,12 @@
#include <vector>

class DBContactMessage;

namespace db
{
    class Query;
} // namespace db

namespace sys
{
    class Service;


@@ 138,8 140,10 @@ class DBServiceAPI
                                const std::string &author,
                                std::unique_ptr<db::QueryListener> &&listener);
    static bool QuotesDeleteEntry(sys::Service *serv, std::uint32_t id, std::unique_ptr<db::QueryListener> &&listener);
    static void QuotesGroupChanged(sys::Service *serv, const std::string &group);
    static void QuotesIntervalChanged(sys::Service *serv, const std::string &interval);
    static bool QuotesGroupChanged(sys::Service *serv,
                                   const std::string &group,
                                   std::unique_ptr<db::QueryListener> &&listener);
    static bool QuotesIntervalChanged(sys::Service *serv, const std::string &interval);
    static bool QuotesGetGroup(sys::Service *serv, std::unique_ptr<db::QueryListener> &&listener);
    static bool QuotesGetInterval(sys::Service *serv, std::unique_ptr<db::QueryListener> &&listener);
};

M module-services/service-db/include/service-db/QuotesMessages.hpp => module-services/service-db/include/service-db/QuotesMessages.hpp +8 -7
@@ 279,6 279,7 @@ namespace Quotes
                return "EnableQuoteByIdResponse";
            }
        };

        class AddQuoteRequest : public db::Query
        {
          public:


@@ 341,9 342,9 @@ namespace Quotes
        class NotificationResult : public db::QueryResult
        {
          public:
            explicit NotificationResult(bool ret)
            explicit NotificationResult(bool success) : success(success)
            {}
            bool ret;
            bool success;

            [[nodiscard]] auto debugInfo() const -> std::string
            {


@@ 354,7 355,7 @@ namespace Quotes
        class InformLanguageChangeRequest : public db::Query
        {
          public:
            explicit InformLanguageChangeRequest() : Query(Query::Type::Read)
            explicit InformLanguageChangeRequest() : Query(Query::Type::Update)
            {}

            auto debugInfo() const -> std::string


@@ 422,7 423,7 @@ namespace Quotes
        class InformGroupChanged : public db::Query
        {
          public:
            explicit InformGroupChanged(const std::string &group) : Query(Query::Type::Read), group(group)
            explicit InformGroupChanged(const std::string &group) : Query(Query::Type::Update), group(group)
            {}
            const std::string group;



@@ 435,7 436,7 @@ namespace Quotes
        class GetGroup : public db::Query
        {
          public:
            explicit GetGroup() : Query(Query::Type::Create)
            explicit GetGroup() : Query(Query::Type::Read)
            {}

            auto debugInfo() const -> std::string


@@ 460,7 461,7 @@ namespace Quotes
        class InformIntervalChanged : public db::Query
        {
          public:
            explicit InformIntervalChanged(const std::string &interval) : Query(Query::Type::Read), interval(interval)
            explicit InformIntervalChanged(const std::string &interval) : Query(Query::Type::Update), interval(interval)
            {}
            const std::string interval;



@@ 473,7 474,7 @@ namespace Quotes
        class GetInterval : public db::Query
        {
          public:
            explicit GetInterval() : Query(Query::Type::Create)
            explicit GetInterval() : Query(Query::Type::Read)
            {}

            auto debugInfo() const -> std::string

M products/BellHybrid/apps/common/src/layouts/HomeScreenLayoutClassicWithQuotes.cpp => products/BellHybrid/apps/common/src/layouts/HomeScreenLayoutClassicWithQuotes.cpp +1 -1
@@ 24,7 24,7 @@ namespace

    constexpr auto authorFont{style::window::font::mediumbigbold};
    constexpr auto authorHeight{42U};
}; // namespace
} // namespace

namespace gui
{

M products/BellHybrid/services/db/agents/QuotesAgent.cpp => products/BellHybrid/services/db/agents/QuotesAgent.cpp +24 -20
@@ 51,10 51,10 @@ namespace Quotes
    auto QuotesAgent::isCustomQuotesDatabaseEmpty() -> bool
    {
        const auto result = quotesDB->query(Queries::getCustomQuotesCount);
        if (result == nullptr || result->getRowCount() == 0) {
        if ((result == nullptr) || (result->getRowCount() == 0)) {
            return true;
        }
        return ((*result)[0].getUInt32() == 0);
        return (*result)[0].getUInt32() == 0;
    }

    auto QuotesAgent::handleReadRandomizedQuote(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>


@@ 62,12 62,13 @@ namespace Quotes
        const auto request = std::dynamic_pointer_cast<Messages::ReadRandomizedQuoteRequest>(query);
        if (request) {
            const auto id = shuffleQuoteModel.getId();
            const auto quotesGroup =
            const auto &quotesGroup =
                settings->getValue(settings::Quotes::selectedGroup, settings::SettingsScope::Global);
            LOG_DEBUG("Shuffle id: %d", id);
            const auto readQuery = quotesGroup == customGroup ? Queries::readCustomQuote : Queries::readPredefinedQuote;
            const auto result    = quotesDB->query(readQuery, id);
            if (result && result->getRowCount() > 0) {
            const auto readQuery =
                (quotesGroup == customGroup) ? Queries::readCustomQuote : Queries::readPredefinedQuote;
            const auto result = quotesDB->query(readQuery, id);
            if (result && (result->getRowCount() > 0)) {
                const IdQuoteAuthorRecord record(result.get());
                auto response = std::make_unique<Messages::ReadRandomizedQuoteResponse>(
                    record.quote_id, record.quote, record.author);


@@ 91,7 92,7 @@ namespace Quotes
        const auto executeResult =
            quotesDB->execute(Queries::addCustomQuote, request->quote.c_str(), request->author.c_str());

        if (executeResult && quotesGroup == customGroup) {
        if (executeResult && (quotesGroup == customGroup)) {
            shuffleQuoteModel.updateList(ListUpdateMode::Forced);
        }



@@ 110,11 111,11 @@ namespace Quotes
            return response;
        }

        const auto quotesGroup = settings->getValue(settings::Quotes::selectedGroup, settings::SettingsScope::Global);
        const auto &quotesGroup = settings->getValue(settings::Quotes::selectedGroup, settings::SettingsScope::Global);
        const auto executeResult =
            quotesDB->execute(Queries::editCustomQuote, request->quote.c_str(), request->author.c_str(), request->id);

        if (executeResult && quotesGroup == customGroup) {
        if (executeResult && (quotesGroup == customGroup)) {
            shuffleQuoteModel.updateList(ListUpdateMode::Forced);
        }



@@ 132,10 133,10 @@ namespace Quotes
            return response;
        }

        const auto quotesGroup   = settings->getValue(settings::Quotes::selectedGroup, settings::SettingsScope::Global);
        const auto &quotesGroup  = settings->getValue(settings::Quotes::selectedGroup, settings::SettingsScope::Global);
        const auto executeResult = quotesDB->execute(Queries::deleteCustomQuote, request->quoteId);

        if (executeResult && quotesGroup == customGroup) {
        if (executeResult && (quotesGroup == customGroup)) {
            if (isCustomQuotesDatabaseEmpty()) {
                // if the list of custom quotes is empty, we change the group to a predefined one
                settings->setValue(settings::Quotes::selectedGroup, predefinedGroup, settings::SettingsScope::Global);


@@ 151,25 152,28 @@ namespace Quotes
    auto QuotesAgent::handleGroupChanged(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>
    {
        const auto request = std::dynamic_pointer_cast<Messages::InformGroupChanged>(query);
        auto response      = std::make_unique<Messages::NotificationResult>(false);
        response->setRequestQuery(query);

        if (request == nullptr) {
            return std::make_unique<Messages::NotificationResult>(false);
            return response;
        }
        if (request->group == customGroup && isCustomQuotesDatabaseEmpty()) {
            // if the list of custom quotes is empty, we change the group to a predefined one
            settings->setValue(settings::Quotes::selectedGroup, predefinedGroup, settings::SettingsScope::Global);
            shuffleQuoteModel.updateList(ListUpdateMode::Forced);
            return std::make_unique<Messages::NotificationResult>(true);

        /* Prevent setting custom group if there are no custom quotes */
        if ((request->group == customGroup) && isCustomQuotesDatabaseEmpty()) {
            return response;
        }

        settings->setValue(settings::Quotes::selectedGroup, request->group, settings::SettingsScope::Global);
        shuffleQuoteModel.updateList(ListUpdateMode::Forced);
        return std::make_unique<Messages::NotificationResult>(true);
        response->success = true;
        return response;
    }

    auto QuotesAgent::handleGetGroup(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>
    {
        const auto &quotesGroup = settings->getValue(settings::Quotes::selectedGroup, settings::SettingsScope::Global);
        auto response          = std::make_unique<Messages::GetGroupResponse>(quotesGroup);
        auto response           = std::make_unique<Messages::GetGroupResponse>(quotesGroup);
        response->setRequestQuery(query);
        return response;
    }


@@ 180,7 184,7 @@ namespace Quotes
        if (request == nullptr) {
            return std::make_unique<Messages::NotificationResult>(false);
        }
        if (!utils::is_number(request->interval) && request->interval != atMidnight) {
        if (!utils::is_number(request->interval) && (request->interval != atMidnight)) {
            return std::make_unique<Messages::NotificationResult>(false);
        }


M products/BellHybrid/services/desktop/endpoints/include/endpoints/quotes/QuotesEndpoint.hpp => products/BellHybrid/services/desktop/endpoints/include/endpoints/quotes/QuotesEndpoint.hpp +0 -1
@@ 17,5 17,4 @@ namespace sdesktop::endpoints
      private:
        std::unique_ptr<QuotesHelper> helper;
    };

} // namespace sdesktop::endpoints

M products/BellHybrid/services/desktop/endpoints/quotes/QuotesHelper.cpp => products/BellHybrid/services/desktop/endpoints/quotes/QuotesHelper.cpp +33 -20
@@ 5,7 5,6 @@
#include <endpoints/Context.hpp>
#include <endpoints/JsonKeyNames.hpp>
#include <endpoints/message/Sender.hpp>
#include <Service/Service.hpp>
#include <service-db/DBServiceAPI.hpp>
#include <service-db/QuotesMessages.hpp>
#include <log/log.hpp>


@@ 15,7 14,7 @@ namespace sdesktop::endpoints
    auto QuotesHelper::processGet(Context &context) -> ProcessResult
    {
        const auto &body = context.getBody();
        if (const auto settings = body[json::quotes::settings].string_value(); !settings.empty()) {
        if (const auto &settings = body[json::quotes::settings].string_value(); !settings.empty()) {
            if (settings == json::quotes::group) {
                auto listener = std::make_unique<db::EndpointListener>(
                    [=](db::QueryResult *result, Context &context) {


@@ 70,21 69,20 @@ namespace sdesktop::endpoints
            auto listener = std::make_unique<db::EndpointListener>(
                [=](db::QueryResult *result, Context &context) {
                    const auto addQuoteResult = dynamic_cast<Quotes::Messages::AddQuoteResponse *>(result);
                    if (addQuoteResult == nullptr || !addQuoteResult->success) {
                    if ((addQuoteResult == nullptr) || !addQuoteResult->success) {
                        context.setResponseStatus(http::Code::InternalServerError);
                        sender::putToSendQueue(context.createSimpleResponse());
                        return false;
                    }

                    context.setResponseBody(
                        json11::Json::object{{json::quotes::quoteID, static_cast<int>(addQuoteResult->quoteId)}});
                    context.setResponseBody(json11::Json::object{{json::quotes::quoteID, addQuoteResult->quoteId}});
                    context.setResponseStatus(http::Code::OK);
                    sender::putToSendQueue(context.createSimpleResponse());
                    return true;
                },
                context);

            DBServiceAPI::QuotesAddNewEntry(owner, quote.c_str(), author.c_str(), std::move(listener));
            DBServiceAPI::QuotesAddNewEntry(owner, quote, author, std::move(listener));
            return {Sent::Yes, std::nullopt};
        }



@@ 97,7 95,7 @@ namespace sdesktop::endpoints
        const auto &body = context.getBody();
        if (const auto &quote = body[json::quotes::quote].string_value(); !quote.empty()) {
            const auto &author = body[json::quotes::author].string_value();
            const auto id     = body[json::quotes::quoteID];
            const auto id      = body[json::quotes::quoteID];

            if (!id.is_number()) {
                return {Sent::No, ResponseContext{.status = http::Code::BadRequest}};


@@ 106,7 104,7 @@ namespace sdesktop::endpoints
            auto listener = std::make_unique<db::EndpointListener>(
                [=](db::QueryResult *result, Context &context) {
                    const auto editQuoteResult = dynamic_cast<Quotes::Messages::EditEntryResponse *>(result);
                    if (editQuoteResult == nullptr || !editQuoteResult->success) {
                    if ((editQuoteResult == nullptr) || !editQuoteResult->success) {
                        context.setResponseStatus(http::Code::InternalServerError);
                        sender::putToSendQueue(context.createSimpleResponse());
                        return false;


@@ 118,20 116,36 @@ namespace sdesktop::endpoints
                },
                context);

            DBServiceAPI::QuotesEditEntry(owner, id.int_value(), quote.c_str(), author.c_str(), std::move(listener));
            DBServiceAPI::QuotesEditEntry(owner, id.int_value(), quote, author, std::move(listener));
            return {Sent::Yes, std::nullopt};
        }
        else if (const auto group = body[json::quotes::group].string_value(); !group.empty()) {
            DBServiceAPI::QuotesGroupChanged(owner, group.c_str());
        }
        else if (const auto &interval = body[json::quotes::interval].string_value(); !interval.empty()) {
            DBServiceAPI::QuotesIntervalChanged(owner, interval.c_str());

        if (const auto &group = body[json::quotes::group].string_value(); !group.empty()) {
            auto listener = std::make_unique<db::EndpointListener>(
                [=](db::QueryResult *result, Context &context) {
                    const auto groupChangedResult = dynamic_cast<Quotes::Messages::NotificationResult *>(result);
                    if ((groupChangedResult == nullptr) || !groupChangedResult->success) {
                        context.setResponseStatus(http::Code::NotAcceptable);
                        sender::putToSendQueue(context.createSimpleResponse());
                        return false;
                    }

                    context.setResponseStatus(http::Code::OK);
                    sender::putToSendQueue(context.createSimpleResponse());
                    return true;
                },
                context);

            DBServiceAPI::QuotesGroupChanged(owner, group, std::move(listener));
            return {Sent::Yes, std::nullopt};
        }
        else {
            return {Sent::No, ResponseContext{.status = http::Code::BadRequest}};

        if (const auto &interval = body[json::quotes::interval].string_value(); !interval.empty()) {
            DBServiceAPI::QuotesIntervalChanged(owner, interval);
            return {Sent::No, ResponseContext{.status = http::Code::OK}};
        }

        return {Sent::No, ResponseContext{.status = http::Code::OK}};
        return {Sent::No, ResponseContext{.status = http::Code::BadRequest}};
    }

    auto QuotesHelper::processDelete(Context &context) -> ProcessResult


@@ 139,9 153,9 @@ namespace sdesktop::endpoints
        const auto &body = context.getBody();
        if (const auto quoteID = body[json::quotes::quoteID]; quoteID.is_number()) {
            auto listener = std::make_unique<db::EndpointListener>(
                [=](db::QueryResult *result, Context context) {
                [=](db::QueryResult *result, Context &context) {
                    const auto deleteResult = dynamic_cast<Quotes::Messages::DeleteQuoteResponse *>(result);
                    if (deleteResult == nullptr || !deleteResult->success) {
                    if ((deleteResult == nullptr) || !deleteResult->success) {
                        context.setResponseStatus(http::Code::InternalServerError);
                        sender::putToSendQueue(context.createSimpleResponse());
                        return false;


@@ 160,5 174,4 @@ namespace sdesktop::endpoints
        LOG_ERROR("Bad request! Quote ID is incorrect or missing!");
        return {Sent::No, ResponseContext{.status = http::Code::BadRequest}};
    }

} // namespace sdesktop::endpoints

M test/custom_quotes.py => test/custom_quotes.py +18 -19
@@ 67,17 67,16 @@ def send_data(port_path: str, payload_str: str, expected_response: int) -> bool:
        print(f'Request failed, status {status}')
        return False
    
def get_request_response(port_path: str, request: str, expected_size: int) -> str:
def get_request_response(port_path: str, request: str, expected_size: int) -> bytes:
    payload_len_str = str(len(request)).rjust(payload_size_len, '0')
    request_str = payload_marker + payload_len_str + request

    response = ''
    with serial.Serial(port_path, port_baudrate, timeout=port_timeout_medium_s) as port:
        port.write(request_str.encode('ascii'))
        response = port.read(expected_size)
    return response
    
def get_str_data(port_path: str, payload_str: str, expected_responses: int, body_str: str) -> str:
def get_str_data(port_path: str, payload_str: str, expected_responses: list, body_str: str) -> str:
    payload_len_str = str(len(payload_str)).rjust(payload_size_len, '0')
    request_str = payload_marker + payload_len_str + payload_str



@@ 241,7 240,7 @@ def download_file(port_path: str, path: str) -> bool:
    }
    payload_str = json.dumps(payload)

    requestID = {
    request_id = {
        'endpoint': endpoint_types['FilesystemUpload'],
        'method': http_methods['GET'],
        'uuid': uuid,


@@ 259,7 258,7 @@ def download_file(port_path: str, path: str) -> bool:
    resp_json = json.loads(response[10:])
    status = resp_json['status']
    if status == 200:
        requestID['body']['rxID'] = resp_json['body']['rxID']
        request_id['body']['rxID'] = resp_json['body']['rxID']
        chunk_size = resp_json['body']['chunkSize']
        file_size = resp_json['body']['fileSize']
    else:


@@ 274,10 273,10 @@ def download_file(port_path: str, path: str) -> bool:
    progress_bar = tqdm(desc=f'Downloading \'{path}\'', total=file_size, unit='bytes', unit_scale=True, unit_divisor=1024)
    with open(filename, 'wb') as file:
        for i in range(1, chunks + 1):
            requestID_str = json.dumps(requestID)
            response = get_request_response(port_path, requestID_str, chunk_rx_data_size)
            request_id_str = json.dumps(request_id)
            response = get_request_response(port_path, request_id_str, chunk_rx_data_size)
            if not response:
                print("Request GET chunkNo: " + requestID['body']['chunkNo'] + " failed!")
                print("Request GET chunkNo: " + request_id['body']['chunkNo'] + " failed!")
                return False

            resp_json = json.loads(response[10:])


@@ 285,7 284,7 @@ def download_file(port_path: str, path: str) -> bool:
            if status == 200:
                data = base64.b64decode(resp_json['body']['data'])
                file.write(data)
                requestID['body']['chunkNo'] = i + 1
                request_id['body']['chunkNo'] = i + 1
                progress_bar.update(len(data))
            else:
                print(f'Request failed, status {status}')


@@ 309,22 308,22 @@ def delete_sync(port_path: str, path: str) -> bool:
    return send_data(port_path, payload_str, 204)

def get_quotes(port_path: str) -> bool:
    if start_sync(port_path) == False:
    if not start_sync(port_path):
        print("Error! Synchronization has not been started.")
        return False
    print("Synchronization has started...")

    if wait_for_sync_end(port_path, 20) == False:
    if not wait_for_sync_end(port_path, 20):
        print("Error! Synchronization fail.")
        return False
    print("Synchronization completed successfully.")

    if download_file(port_path, sync_path) == False:
    if not download_file(port_path, sync_path):
        print("Error! Sync file download fail.")
        return False
    print("Sync file download completed.")

    if delete_sync(port_path, sync_path) == False:
    if not delete_sync(port_path, sync_path):
        print("Error! Failed to delete sync file.")
        return False
    print("Sync file deletion completed successfully.")


@@ 382,10 381,10 @@ def main():
            return
        else:
            print("adding new quotes: " + args.quote)
            status = add_quote(args.port, args.quote, args.author)
            add_quote(args.port, args.quote, args.author)
    elif args.delete:
        print("deleting quotes nr: " + str(args.delete))
        status = delete_quote(args.port, args.delete)
        delete_quote(args.port, args.delete)
    elif args.edit:
        if not args.quote:
            print('Invalid usage: please add quote')


@@ 393,17 392,17 @@ def main():
            return
        else:
            print("editing quotes nr: " + str(args.edit))
            status = edit_quote(args.port, args.edit, args.quote, args.author)
            edit_quote(args.port, args.edit, args.quote, args.author)
    elif args.group:
        print("quotes group: " + args.group)
        status = change_group(args.port, args.group)
        change_group(args.port, args.group)
    elif args.interval:
        print("quotes interval: " + args.interval)
        status = change_interval(args.port, args.interval)
        change_interval(args.port, args.interval)
    elif args.synchro:
        if args.synchro == "quotes":
            print("downloading quotes file")
            status = get_quotes(args.port)
            get_quotes(args.port)
        elif args.synchro == "group":
            group = get_settings(args.port, args.synchro)
            print("quotes group: " + group)