Библиотека NodeJs Async: почему обратный вызов не запускается после возникновения ошибки?

Я разрабатываю бэкэнд для сервера NodeJS, который в настоящее время использует библиотеки async и mysql. Обратите внимание, что эти три подпрограммы выполняются внутри функции «водопада» async. Это должно работать следующим образом:

  • Сервер подключается к базе данных MySQL
  • База данных MySQL вставляет новую запись в таблицу «Тег»
    • если сервер пытается вставить дубликат, должна быть выдана ошибка, и тест должен завершиться неудачно
  • Сервер отключается от базы данных MySQL

К сожалению, каждый тест, который я запускаю с мокко, все равно проходит, хотя клиент MySQL сервера пытается вставить дубликат.

Я уже читал документацию по библиотеке async. Он сказал, что поток управления будет перенаправлен на обратный вызов «водопад», если во время выполнения функции «водопад» возникнет ошибка. Я также попытался проконсультироваться с документацией библиотеки "mysql". Но я не мог найти ничего, что могло бы помочь мне с моей проблемой. Следует ли мне поместить код, который может вызвать ошибку, внутри блока try-catch или мне нужно выполнить специальный обратный вызов?

Файл: SqlTagsTest.js

var mysql = require('mysql');
var mySqlUtils = require('../../backend/MySqlHandler/MySqlUtils.js');
var mySqlTags = require('../../backend/MySqlHandler/MySqlTags.js');

describe('Create new tag',function(){
    var connection = "";
    var queryParams = "";
    before(function(done){
        connection = mysql.createConnection({
            host:'myHost',
            user:'root',
            password:'myPassword',
            database:'myDb'
        });
        queryParams = {
            name: 'Tag',
            description: 'Description',
            parent: 'root',
            creator: 'me',
            admin: false
        };
        done();
    });
    it('should create a new tag inside the database',function(){
        mySqlTags.CreateTag(connection,queryParams);
});

Файл: MySqlTags.js

var sqlUtils = require('./MySqlUtils.js');
var reflectChanges = require('./MySqlReflection.js');
var async = require('async');

function CreateTag(connection,queryParams){
  async.waterfall([
    function SetupData(callback){
      callback(null,connection,queryParams);
    },
    sqlUtils.ConnectToDatabase,
    sqlUtils.InsertTagIntoDatabase,
    sqlUtils.CloseDatabaseConnection
  ],function(err,results){
      if (err)
      {
          throw err;
      }
  });
}

function DeleteTag(connection,queryParams,serverResponse){
  async.waterfall([
    function setupData(callback){
      callback(null,connection,queryParams);
    },
    sqlUtils.ConnectToDatabase,
    reflectChanges.DeleteTagEntry,
    reflectChanges.GenerateUncategorizedTag,
    reflectChanges.UpdateChildrensParent,
    sqlUtils.CloseDatabaseConnection
  ],function(error,results){
    if (error){
      serverResponse(queryParams,error,false);
    }else{
      serverResponse(queryParams.name,true);
    }
  });
}

function EditTag(queryParams,serverResponse){
  async.waterfall([
    sqlUtils.ConnectToDatabase,
    sqlUtils.CloseDatabaseConnection
  ],function(error,results){
    if (error){
      serverResponse(queryParams.name,false);
    }else{
      console.info("No errors!");
      serverResponse(queryParams.name,true);
    }
  });
}

module.exports = {
  CreateTag: CreateTag,
  DeleteTag: DeleteTag,
  EditTag: EditTag
}

Файл: MySqlUtils.js

var mySql = require('mysql');
var reflectChanges = require('./MySqlReflection.js');
var async = require('async');

/**
 * connects to a MySql database 
 * @param {connection which will be established} connection 
 * @param {paremeters which are executed} queryParams 
 * @param {callback which is required by the async library} callback 
 */
function ConnectToDatabase(connection,queryParams,callback){
  connection.connect(function(error){
    if (error){
      console.info(error);
      throw error;
    }else{
       callback(null,connection,queryParams);
     }
  });
}

/**
 * closes an existing connection to the MySql database
 * @param { connection which is passed from the previous function } connection 
 * @param { callback when database connection was successfully closed } callback 
 */
function CloseDatabaseConnection(connection,queryParams,callback)
{
  connection.end(function(error)
  {
    if (error){
        throw error;
    }else{
      callback(null,connection,queryParams);
    }
  });
}

/**
 * get all files which will be affected by the particular query
 * @param {*} connection 
 * @param {*} queryParams 
 * @param {*} callback 
 */
function GetAffectedFiles(connection,queryParams,callback)
{
    const query = "SELECT Name,Tags FROM Tag";
    const params = [];
    var affectedRows = [];

    connection.query(query,params,function(error,results,fields){
        if (error){
            return connection.rollback(function(){
                throw error;
            });
        }else{
            for(var currRow = 0; currRow < results.length; currRow++)
            {
                if (results[currRow].Name === queryParams.name){
                    affectedRows.push(results[currRow].Name);
                }
            }
        }
        queryParams.affectedRows = affectedRows;
        callback(connection,queryParams,callback);
    });
}

/**
 * inserts a new tag into the database
 * @param { connection which will be passed from the previous function } connection 
 * @param { field values of the original row } queryParams 
 * @param { callback called when query was executed successfully } callback 
 */
function InsertTagIntoDatabase(connection,queryParams,callback){
  const query = "INSERT INTO Tag(Id,Name,Description,Parent,Creator,Admin) VALUES((SELECT UUID()),?,?,?,?,?)";
  const params = [queryParams.name,queryParams.description,queryParams.parent,queryParams.creator,queryParams.admin];
  connection.query(query,params,function(error,results,fields){
    if (error){
      throw error;
      throw error;
    }else{
      callback(null,connection,queryParams);
    }
  });
}

/**
 * process chain which allows will be triggered upon tag deletion
 * @param { connection which was established between } connection 
 * @param { queryParameters which will be passed to subroutines } queryParams 
 * @param { callback which will be called upon termination } callback 
 */
function ReflectTagRemoval(connection,queryParams,callback){
  async.waterfall([
    function SetupPrequisites(callback){
      callback(null,connection,queryParams);
    },
    reflect.DeleteTagEntry,
    reflect.UpdateChildrenTag,
    reflect.UpdateFileTags,
    function PassFurther()
    {
      callback(null,connection,queryParams);
    }
  ]);
}

function ReflectTagUpdate(connection,queryParams,callback){
  async.waterfall([
    function SetupPrequisites(callback){
      callback(null,connection,queryParams);
    },
    ConnectToDatabase,
    reflectChanges.EditTagEntry,
    reflectChanges.EditTagChildrenAfterChanges,
    reflectChanges.UpdateFileTagsAfterEdit,
    function PassFurther(callback){
      callback(null,connection,queryParams);
    }
  ]);
}

function ValidateLoginCredentials(connection,loginCredentials,callback)
{
  const query = "SELECT Name,Password FROM User WHERE Name = ? AND Password = ?";
  const params = [loginCredentials.username,loginCredentials.password];

  connection.query(query,params,function(error,results,fields){
    if (error)
    {
      throw error;
    }
    callback(null,connection);
  });
}

module.exports = 
{
  ConnectToDatabase: ConnectToDatabase,
  InsertTagIntoDatabase: InsertTagIntoDatabase,
  ReflectTagRemoval: ReflectTagRemoval,
  ReflectTagUpdate: ReflectTagUpdate,
  ValidateLoginCredentials: ValidateLoginCredentials,
  CloseDatabaseConnection : CloseDatabaseConnection
}

Извините за глупый вопрос, но: вы уверены, что описание вашей таблицы Tag включает ключ UNIQUE, отличный от Id?

dun32 19.12.2018 17:51

да. Таблица включает столбцы «Идентификатор», который представляет собой UUID, и «Имя», которое является уникальным значением типа «ТЕКСТ».

IlikedCPlusPlus 19.12.2018 17:56

ну, я пробовал на своем сервере разработки: mysql выдает ошибку, когда вы вставляете дубликат, как ожидалось, и ваш код мне кажется правильным. Просто вещь: когда вы получаете ошибку, вы ее бросаете => вместо этого вы должны использовать обратный вызов (ошибка), чтобы вы могли управлять окончанием соединения. Не могу больше, извини. С Уважением

dun32 19.12.2018 18:25

Спасибо за совет :) Пока я не мог найти никакого обходного пути, это решение дает мне возможность отправить ответ клиенту.

IlikedCPlusPlus 19.12.2018 22:04
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
В последние годы архитектура микросервисов приобрела популярность как способ построения масштабируемых и гибких приложений. Laravel , популярный PHP...
Как построить CRUD-приложение в Laravel
Как построить CRUD-приложение в Laravel
Laravel - это популярный PHP-фреймворк, который позволяет быстро и легко создавать веб-приложения. Одной из наиболее распространенных задач в...
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
В предыдущем посте мы создали функциональность вставки и чтения для нашей динамической СУБД. В этом посте мы собираемся реализовать функции обновления...
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
Роли и разрешения пользователей без пакета Laravel 9
Роли и разрешения пользователей без пакета Laravel 9
Этот пост изначально был опубликован на techsolutionstuff.com .
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
В предыдущей статье мы завершили установку базы данных, для тех, кто не знает.
0
4
32
0

Другие вопросы по теме