diff --git a/.bsl-language-server.json b/.bsl-language-server.json index 3b64ba7..9c8433d 100644 --- a/.bsl-language-server.json +++ b/.bsl-language-server.json @@ -4,7 +4,7 @@ "parameters": { "Typo": { "minWordLength": 3, - "userWordsToIgnore": "Логгер" + "userWordsToIgnore": "Логгер,Трекеры" } } } diff --git a/README.md b/README.md index c4ad0e6..a31347e 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,8 @@ - Сохранение запросов информации об ошибках, которые направляет клиентское приложение. - Распаковка поступающих отчетов об ошибках. - Автоматическая регистрация ошибок в баг-трекере (Jira, Redmine). +- Отображение Ид задачи из баг-трекера прямо в окне 1С, если такая ошибка уже была зарегистрирована там ранее. +- Запрет повторной регистрации одной и той же ошибки. - Отправка сообщений с информацией об ошибке в точку обмена RabbitMQ. - Отправка сообщений с информацией об ошибке в Sentry. @@ -40,7 +42,7 @@ - перейти в каталог проекта в командной строке - выполнить команду `'docker build -t .'`, где: - `` - метка образа, задается произвольно -- выполнить команду `'docker run --name -v : -p :5000 '`, где: +- выполнить команду `'docker run -d --name -v : -p :5000 '`, где: - `` - имя контейнера, задается произвольно - `` - локальный каталог, в котором будут храниться данные приложения - `` - каталог внутри контейнера, в который изначально записываются данные приложения (устанавливается в `appsettings.json`) @@ -94,7 +96,6 @@ URL, по которому доступно приложение, надо ук ## Ограничения -- на данный момент приложение не умеет гибко управлять процессом регистрации ошибок. Например, на любой запрос о регистрации ошибки приложение отвечает, что ее **необходимо** регистрировать. Это значит, что если 10 пользователей столкнутся с одной и той же ошибкой, то в вашем баг-трекере может появиться 10 тикетов. - ошибки регистрируются в баг-трекере от имени владельца API-ключа, а не от лица пользователя информационной базы. - веб-интерфейс пока почти полностью состоит из заглушек. - какие-либо ограничения доступа на уровне веб-приложения отсутствуют. Любой, кто сможет открыть веб-интерфейс, сможет просматривать всю информацию. @@ -108,7 +109,6 @@ URL, по которому доступно приложение, надо ук - поддержка СППР ред. 1 и 2 в качестве баг-трекера - гибкая настройка параметров регистрации ошибок - определение необходимости регистрации ошибки - - определение "дублей" ошибок - кастомизация сообщения, которое выводится пользователю - перевод приложения на библиотеку [entity](https://github.com/oscript-library/entity) - поддержка хранения данных во внешней БД diff --git a/features/fixtures/data/errorInfoRequests.json b/features/fixtures/data/errorInfoRequests.json index 7749543..f4d62d3 100644 --- a/features/fixtures/data/errorInfoRequests.json +++ b/features/fixtures/data/errorInfoRequests.json @@ -1,7 +1,8 @@ [ { - "id": "7054dd5d-f0b8-491d-8dcc-18bc1d2cbc83", - "datetime": "2020-10-05T21:58:45Z", + "x_id": "7054dd5d-f0b8-491d-8dcc-18bc1d2cbc83", + "x_datetime": "2020-10-05T21:58:45Z", + "x_fingerprint": "1111-2222-33333333-4444", "configHash": "bd56b503adfc454b9fc84d7b0fea3e7900000000", "сonfigName": "Config1", "configVersion": "1.0.1.265", @@ -15,8 +16,9 @@ "systemcrash": "" }, { - "id": "b4fe5366-193b-4f4b-8609-6a727b316ca5", - "datetime": "2020-10-06T11:20:33Z", + "x_id": "b4fe5366-193b-4f4b-8609-6a727b316ca5", + "x_datetime": "2020-10-06T11:20:33Z", + "x_fingerprint": "1111-2222-33333333-5555", "configHash": "ac83b503adfc454b9fc84d7b0fea3e7900000000", "сonfigName": "Config2", "configVersion": "1.0.1.265", @@ -30,8 +32,9 @@ "systemcrash": "" }, { - "id": "2ac96a47-156b-40cf-8403-ea4e0287deba", - "datetime": "2020-10-06T11:29:17Z", + "x_id": "2ac96a47-156b-40cf-8403-ea4e0287deba", + "x_datetime": "2020-10-06T11:29:17Z", + "x_fingerprint": "1111-2222-33333333-6666", "configHash": "or96e421adfc454b9fc84d7b0fea3e7900000000", "сonfigName": "Config3", "configVersion": "1.0.1.265", diff --git a/features/fixtures/data/errors.json b/features/fixtures/data/errors.json index 252e3c3..8c3eeb9 100644 --- a/features/fixtures/data/errors.json +++ b/features/fixtures/data/errors.json @@ -1,6 +1,14 @@ [ { "id": "61001a5e-09d5-47b8-bf19-e7672eda10e5", - "datetime": "2020-10-05T21:58:45Z" + "datetime": "2020-10-05T21:58:45Z", + "fingerprint": "C0627D8FFA4A06A33D3BD77BAAED5667", + "external_id": "11331" + }, + { + "id": "68359124-56e5-93aa-cd94-9531aa526f4", + "datetime": "2020-10-08T12:03:54Z", + "fingerprint": "B07F49C8154BA4E20BA3FAAD30507F25", + "external_id": "" } ] \ No newline at end of file diff --git a/features/fixtures/getInfoDuplicateRequest.json b/features/fixtures/getInfoDuplicateRequest.json new file mode 100644 index 0000000..eecadca --- /dev/null +++ b/features/fixtures/getInfoDuplicateRequest.json @@ -0,0 +1,13 @@ +{ + "configHash": "ac83b503adfc454b9fc84d7b0fea3e7900000000", + "сonfigName": "Config2", + "configVersion": "1.0.1.265", + "appStackHash": "5E78AD0E93E8D3963841DFD479A597EF", + "clientStackHash": "84A1E9821497E377D0E1FB27C47CA522", + "serverStackHash": "", + "platformType": "Windows_x86_64", + "appName": "1CV8C", + "appVersion": "8.3.17.1549", + "configurationInterfaceLanguageCode": "ru", + "systemcrash": "" +} \ No newline at end of file diff --git a/features/fixtures/getInfoDuplicateResponse.json b/features/fixtures/getInfoDuplicateResponse.json new file mode 100644 index 0000000..5e74bb1 --- /dev/null +++ b/features/fixtures/getInfoDuplicateResponse.json @@ -0,0 +1,5 @@ +{ + "needSendReport": false, + "userMessage": "Данная ошибка уже была зарегистрирована. Ид: 11331", + "dumpType": "0" +} \ No newline at end of file diff --git a/features/fixtures/requestBodyGetInfo.json b/features/fixtures/getInfoRequest.json similarity index 100% rename from features/fixtures/requestBodyGetInfo.json rename to features/fixtures/getInfoRequest.json diff --git a/features/fixtures/getInfoResponse.json b/features/fixtures/getInfoResponse.json new file mode 100644 index 0000000..d2ce0d6 --- /dev/null +++ b/features/fixtures/getInfoResponse.json @@ -0,0 +1,5 @@ +{ + "needSendReport": true, + "userMessage": "Отчет об ошибке будет отправлен автоматически.", + "dumpType": "0" +} \ No newline at end of file diff --git a/features/fixtures/responseBodyGetInfo.json b/features/fixtures/responseBodyGetInfo.json deleted file mode 100644 index f3dbf6e..0000000 --- a/features/fixtures/responseBodyGetInfo.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "needSendReport": true, - "userMessage": "Ошибка перехвачена и будет зарегистрирована в баг-трекере. Нажмите OK", - "dumpType": "0" -} \ No newline at end of file diff --git "a/features/fixtures/\320\236\321\210\320\270\320\261\320\272\320\260_20201123003806.zip" "b/features/fixtures/\320\236\321\210\320\270\320\261\320\272\320\260_20201123003806.zip" new file mode 100644 index 0000000..466ab45 Binary files /dev/null and "b/features/fixtures/\320\236\321\210\320\270\320\261\320\272\320\260_20201123003806.zip" differ diff --git a/src/controllers/errorInfoRequests.os b/src/controllers/errorInfoRequests.os index 85371cb..5087648 100644 --- a/src/controllers/errorInfoRequests.os +++ b/src/controllers/errorInfoRequests.os @@ -1,8 +1,8 @@ &HttpMethod("GET") Функция Index() Экспорт - ТаблицаЗапросовИнформацииОбОшибке = ЗапросыИнфоОбОшибке.ПолучитьСписок(); - Возврат Представление(ТаблицаЗапросовИнформацииОбОшибке); + ЗапросыИнформацииОбОшибке = ЗапросыИнфоОбОшибке.ПолучитьСписок(); + Возврат Представление(ЗапросыИнформацииОбОшибке); КонецФункции diff --git a/src/controllers/errorReports.os b/src/controllers/errorReports.os index 216a535..81891f4 100644 --- a/src/controllers/errorReports.os +++ b/src/controllers/errorReports.os @@ -1,8 +1,8 @@ &HttpMethod("GET") Функция Index() Экспорт - ТаблицаОшибок = ЗарегистрированныеОшибки.ПолучитьСписок(); - Возврат Представление(ТаблицаОшибок); + СписокОтчетовОбОшибках = ОтчетыОбОшибках.ПолучитьСписок(); + Возврат Представление(СписокОтчетовОбОшибках); КонецФункции diff --git a/src/controllers/getInfo.os b/src/controllers/getInfo.os index f017053..77d27ab 100644 --- a/src/controllers/getInfo.os +++ b/src/controllers/getInfo.os @@ -1,5 +1,3 @@ -#использовать json - &HttpMethod("POST") Функция Index() Экспорт @@ -9,12 +7,7 @@ ТелоЗапросаТекст = ЧтениеТекста.Прочитать(); ЧтениеТекста.Закрыть(); - ПарсерJSON = Новый ПарсерJSON; - ЗапросИнфоОбОшибке = ПарсерJSON.ПрочитатьJSON(ТелоЗапросаТекст); - - ЗапросыИнфоОбОшибке.Записать(ЗапросИнфоОбОшибке); - - Ответ = ЗапросыИнфоОбОшибке.СформироватьОтвет(ЗапросИнфоОбОшибке); + Ответ = ЗапросыИнфоОбОшибке.ОбработатьЗапрос(ТелоЗапросаТекст); Возврат Содержимое(Ответ); diff --git a/src/controllers/pushReport.os b/src/controllers/pushReport.os index 7bf2e34..17a0691 100644 --- a/src/controllers/pushReport.os +++ b/src/controllers/pushReport.os @@ -7,21 +7,21 @@ ДанныеФормы = ЗапросHttp.ДанныеФормы; Если ДанныеФормы = Неопределено Тогда - Возврат КодСостояния(200); + Возврат КодСостояния(400); КонецЕсли; ФайлыЗапроса = ДанныеФормы.Файлы; Если ФайлыЗапроса = Неопределено Тогда - Возврат КодСостояния(200); + Возврат КодСостояния(400); КонецЕсли; Если ФайлыЗапроса.Количество() <> 1 Тогда - ВызватьИсключение("Количество файлов не равно одному"); + Возврат КодСостояния(400); КонецЕсли; Попытка - ОбработкаОшибок.ОбработатьОтчетОбОшибке(ФайлыЗапроса[0]); + ОтчетыОбОшибках.ОбработатьОтчетОбОшибке(ФайлыЗапроса[0]); Возврат КодСостояния(200); Исключение Сообщить(ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())); diff --git "a/src/model/classes/\320\237\321\200\320\276\320\262\320\260\320\271\320\264\320\265\321\200\320\230\320\275\321\202\320\265\320\263\321\200\320\260\321\206\320\270\320\270Jira.os" "b/src/model/classes/\320\237\321\200\320\276\320\262\320\260\320\271\320\264\320\265\321\200\320\230\320\275\321\202\320\265\320\263\321\200\320\260\321\206\320\270\320\270Jira.os" index 933b5da..dda2fd4 100644 --- "a/src/model/classes/\320\237\321\200\320\276\320\262\320\260\320\271\320\264\320\265\321\200\320\230\320\275\321\202\320\265\320\263\321\200\320\260\321\206\320\270\320\270Jira.os" +++ "b/src/model/classes/\320\237\321\200\320\276\320\262\320\260\320\271\320\264\320\265\321\200\320\230\320\275\321\202\320\265\320\263\321\200\320\260\321\206\320\270\320\270Jira.os" @@ -70,7 +70,7 @@ КонецФункции -Процедура ЗарегистрироватьОшибку(ДанныеОтчетаОбОшибке) Экспорт +Функция ЗарегистрироватьОшибку(ДанныеОтчетаОбОшибке) Экспорт URLIssues = СтрШаблон("%1/issue", URL); @@ -116,8 +116,13 @@ КонецЕсли; КонецЕсли; + + РезультатЗапросаJSON = РезультатЗапроса.JSON(); + ИдЗадачиВТрекере = РезультатЗапросаJSON["id"]; -КонецПроцедуры + Возврат ИдЗадачиВТрекере; + +КонецФункции Процедура ПрикрепитьФайлКЗадаче(URL, ПрикрепляемыйФайл) @@ -157,7 +162,7 @@ Issue.Вставить("issuetype", Новый Структура("id", ИдТипаЗадачи)); Issue.Вставить("summary", ТемаЗадачи); - Описание = ОбработкаОшибок.СформироватьОписаниеОшибки(ДанныеОтчетаОбОшибке); + Описание = ОтчетыОбОшибках.СформироватьОписаниеОшибки(ДанныеОтчетаОбОшибке); Issue.Вставить("description", Описание); ТелоЗапросаJSON = Новый Структура; diff --git "a/src/model/classes/\320\237\321\200\320\276\320\262\320\260\320\271\320\264\320\265\321\200\320\230\320\275\321\202\320\265\320\263\321\200\320\260\321\206\320\270\320\270RabbitMQ.os" "b/src/model/classes/\320\237\321\200\320\276\320\262\320\260\320\271\320\264\320\265\321\200\320\230\320\275\321\202\320\265\320\263\321\200\320\260\321\206\320\270\320\270RabbitMQ.os" index 9106a37..ba95bdc 100644 --- "a/src/model/classes/\320\237\321\200\320\276\320\262\320\260\320\271\320\264\320\265\321\200\320\230\320\275\321\202\320\265\320\263\321\200\320\260\321\206\320\270\320\270RabbitMQ.os" +++ "b/src/model/classes/\320\237\321\200\320\276\320\262\320\260\320\271\320\264\320\265\321\200\320\230\320\275\321\202\320\265\320\263\321\200\320\260\321\206\320\270\320\270RabbitMQ.os" @@ -60,7 +60,7 @@ КонецФункции -Процедура ЗарегистрироватьОшибку(ДанныеОтчетаОбОшибке) Экспорт +Функция ЗарегистрироватьОшибку(ДанныеОтчетаОбОшибке) Экспорт Соединение = Новый СоединениеRMQ; @@ -85,12 +85,15 @@ КонецПопытки; Соединение.Закрыть(); + + // Для провайдера RMQ не существует такого понятия как ИдЗадачиВТрекере + Возврат Неопределено; -КонецПроцедуры +КонецФункции Функция СформироватьТелоЗапроса(ДанныеОтчетаОбОшибке) Экспорт - Описание = ОбработкаОшибок.СформироватьТелоОписанияОшибкиJSON(ДанныеОтчетаОбОшибке); + Описание = ОтчетыОбОшибках.СформироватьТелоОписанияОшибкиJSON(ДанныеОтчетаОбОшибке); ПарсерJSON = Новый ПарсерJSON(); Возврат ПарсерJSON.ЗаписатьJSON(Описание); diff --git "a/src/model/classes/\320\237\321\200\320\276\320\262\320\260\320\271\320\264\320\265\321\200\320\230\320\275\321\202\320\265\320\263\321\200\320\260\321\206\320\270\320\270Redmine.os" "b/src/model/classes/\320\237\321\200\320\276\320\262\320\260\320\271\320\264\320\265\321\200\320\230\320\275\321\202\320\265\320\263\321\200\320\260\321\206\320\270\320\270Redmine.os" index b4d5db8..6c978e8 100644 --- "a/src/model/classes/\320\237\321\200\320\276\320\262\320\260\320\271\320\264\320\265\321\200\320\230\320\275\321\202\320\265\320\263\321\200\320\260\321\206\320\270\320\270Redmine.os" +++ "b/src/model/classes/\320\237\321\200\320\276\320\262\320\260\320\271\320\264\320\265\321\200\320\230\320\275\321\202\320\265\320\263\321\200\320\260\321\206\320\270\320\270Redmine.os" @@ -92,7 +92,7 @@ КонецФункции -Процедура ЗарегистрироватьОшибку(ДанныеОтчетаОбОшибке) Экспорт +Функция ЗарегистрироватьОшибку(ДанныеОтчетаОбОшибке) Экспорт URLIssues = СтрШаблон("%1/issues.json", URLRedmine); @@ -108,8 +108,13 @@ Если РезультатЗапроса.КодСостояния <> 201 Тогда ВызватьИсключение("Не удалось зарегистрировать ошибку Redmine"); // TODO: сделать внятное сообщение КонецЕсли; + + РезультатЗапросаJSON = РезультатЗапроса.JSON(); + ИдЗадачиВТрекере = РезультатЗапросаJSON["issue"]["id"]; -КонецПроцедуры + Возврат ИдЗадачиВТрекере; + +КонецФункции Функция СформироватьТелоЗапроса(ДанныеОтчетаОбОшибке) Экспорт @@ -118,7 +123,7 @@ Issue.Вставить("tracker_id", ИдТрекера); Issue.Вставить("subject", Тема); - Описание = ОбработкаОшибок.СформироватьОписаниеОшибки(ДанныеОтчетаОбОшибке); + Описание = ОтчетыОбОшибках.СформироватьОписаниеОшибки(ДанныеОтчетаОбОшибке); Issue.Вставить("description", Описание); Issue.Вставить("priority_id", ИдПриоритета); Issue.Вставить("status_id", ИдСтатуса); @@ -211,7 +216,7 @@ ПолноеИмяФайла = Файл.ПолноеИмя; - URL = СтрШаблон("%1/uploads.json?filename=", URLRedmine, ПолноеИмяФайла); + URL = СтрШаблон("%1/uploads.json?filename=%2", URLRedmine, ПолноеИмяФайла); ПрикрепляемыйФайлДД = Новый ДвоичныеДанные(ПолноеИмяФайла); Заголовки = Новый Соответствие; diff --git "a/src/model/classes/\320\237\321\200\320\276\320\262\320\260\320\271\320\264\320\265\321\200\320\230\320\275\321\202\320\265\320\263\321\200\320\260\321\206\320\270\320\270Sentry.os" "b/src/model/classes/\320\237\321\200\320\276\320\262\320\260\320\271\320\264\320\265\321\200\320\230\320\275\321\202\320\265\320\263\321\200\320\260\321\206\320\270\320\270Sentry.os" index 8dbd8f8..7fe2197 100644 --- "a/src/model/classes/\320\237\321\200\320\276\320\262\320\260\320\271\320\264\320\265\321\200\320\230\320\275\321\202\320\265\320\263\321\200\320\260\321\206\320\270\320\270Sentry.os" +++ "b/src/model/classes/\320\237\321\200\320\276\320\262\320\260\320\271\320\264\320\265\321\200\320\230\320\275\321\202\320\265\320\263\321\200\320\260\321\206\320\270\320\270Sentry.os" @@ -53,7 +53,7 @@ КонецПроцедуры -Процедура ЗарегистрироватьОшибку(ДанныеОтчетаОбОшибке) Экспорт +Функция ЗарегистрироватьОшибку(ДанныеОтчетаОбОшибке) Экспорт ТелоЗапроса = СформироватьТелоЗапроса(ДанныеОтчетаОбОшибке); @@ -67,8 +67,11 @@ Если РезультатЗапроса.КодСостояния <> 200 Тогда ВызватьИсключение("Не удалось отправить событие в Sentry"); // TODO: сделать внятное сообщение КонецЕсли; + + // В Sentry отправляется каждая ошибка + Возврат Неопределено; -КонецПроцедуры +КонецФункции Функция СформироватьТелоЗапроса(ДанныеОтчетаОбОшибке) Экспорт diff --git "a/src/model/classes/\320\244\320\260\320\271\320\273\320\276\320\262\320\276\320\265\320\245\321\200\320\260\320\275\320\270\320\273\320\270\321\211\320\265.os" "b/src/model/classes/\320\244\320\260\320\271\320\273\320\276\320\262\320\276\320\265\320\245\321\200\320\260\320\275\320\270\320\273\320\270\321\211\320\265.os" index 36ca522..a02a9bc 100644 --- "a/src/model/classes/\320\244\320\260\320\271\320\273\320\276\320\262\320\276\320\265\320\245\321\200\320\260\320\275\320\270\320\273\320\270\321\211\320\265.os" +++ "b/src/model/classes/\320\244\320\260\320\271\320\273\320\276\320\262\320\276\320\265\320\245\321\200\320\260\320\275\320\270\320\273\320\270\321\211\320\265.os" @@ -9,7 +9,7 @@ // Имя файла внутри каталога с данными, в который записывается // информация о зарегистрированных ошибках -Перем ИмяФайлаЗарегистрированныеОшибки; +Перем ИмяФайлаОшибки; Процедура ПриСозданииОбъекта(вхКаталог) Экспорт @@ -22,11 +22,11 @@ Процедура ИнициализироватьХранилище() ИмяФайлаЗапросыИнфоОбОшибке = ОбъединитьПути(Каталог, "errorInfoRequests.json"); - ИмяФайлаЗарегистрированныеОшибки = ОбъединитьПути(Каталог, "errors.json"); + ИмяФайлаОшибки = ОбъединитьПути(Каталог, "errors.json"); МассивФайлов = Новый Массив(); МассивФайлов.Добавить(ИмяФайлаЗапросыИнфоОбОшибке); - МассивФайлов.Добавить(ИмяФайлаЗарегистрированныеОшибки); + МассивФайлов.Добавить(ИмяФайлаОшибки); Для Каждого ПутьКФайлу Из МассивФайлов Цикл @@ -76,8 +76,11 @@ Функция ТаблицаЗапросовИнфоОбОшибках() Таблица = Новый ТаблицаЗначений; - Таблица.Колонки.Добавить("id"); - Таблица.Колонки.Добавить("datetime"); + + Таблица.Колонки.Добавить("x_id"); + Таблица.Колонки.Добавить("x_datetime"); + Таблица.Колонки.Добавить("x_fingerprint"); + Таблица.Колонки.Добавить("configHash"); Таблица.Колонки.Добавить("сonfigName"); Таблица.Колонки.Добавить("configVersion"); @@ -100,8 +103,6 @@ нСтр = ТЗ.Добавить(); ЗаполнитьЗначенияСвойств(нСтр, ЗапросИнфоОбОшибке); - нСтр.id = Строка(Новый УникальныйИдентификатор()); - нСтр.datetime = ТекущаяУниверсальнаяДата(); Текст = Новый ЗаписьТекста(ИмяФайлаЗапросыИнфоОбОшибке); ПарсерJSON = Новый ПарсерJSON; @@ -110,23 +111,23 @@ КонецПроцедуры -Функция ПолучитьЗарегистрированныеОшибки() Экспорт +Функция ПолучитьОшибки() Экспорт - Результат = ТаблицаЗарегистрированныхОшибок(); + Результат = ТаблицаОшибок(); ЧтениеJSON = Новый ЧтениеJSON(); - ЧтениеJSON.ОткрытьФайл(ИмяФайлаЗарегистрированныеОшибки, "utf-8"); - ТаблицаЗарегистрированныеОшибки = ПрочитатьJSON(ЧтениеJSON, Ложь); + ЧтениеJSON.ОткрытьФайл(ИмяФайлаОшибки, "utf-8"); + ТаблицаОшибки = ПрочитатьJSON(ЧтениеJSON); ЧтениеJSON.Закрыть(); - Если ТаблицаЗарегистрированныеОшибки = Неопределено Тогда + Если ТаблицаОшибки = Неопределено Тогда Возврат Результат; КонецЕсли; - Для Каждого ЗарегистрированнаяОшибка Из ТаблицаЗарегистрированныеОшибки Цикл + Для Каждого Ошибка Из ТаблицаОшибки Цикл нСтр = Результат.Добавить(); - ЗаполнитьЗначенияСвойств(нСтр, ЗарегистрированнаяОшибка); + ЗаполнитьЗначенияСвойств(нСтр, Ошибка); КонецЦикла; @@ -134,30 +135,50 @@ КонецФункции -Функция ТаблицаЗарегистрированныхОшибок() +Функция ИдентификаторОшибкиВТрекереПоОтпечатку(Отпечаток) Экспорт + + Ид = Неопределено; + + Ошибки = ПолучитьОшибки(); + + СтруктураОтбора = Новый Структура("fingerprint", Отпечаток); + + НайденныеОшибки = Ошибки.НайтиСтроки(СтруктураОтбора); + Если НайденныеОшибки.Количество() Тогда + Ид = НайденныеОшибки[0].external_id; + КонецЕсли; + + Возврат Ид; + +КонецФункции + +Функция ТаблицаОшибок() Таблица = Новый ТаблицаЗначений; Таблица.Колонки.Добавить("id"); Таблица.Колонки.Добавить("datetime"); + Таблица.Колонки.Добавить("fingerprint"); + Таблица.Колонки.Добавить("external_id"); Возврат Таблица; КонецФункции -Процедура СохранитьФайлОтчета(ИдОтчетаОбОшибке, ФайлОтчета) Экспорт +Процедура СохранитьФайлОтчета(Идентификатор, ФайлОтчета) Экспорт - ИмяФайлаОтчета = СтрШаблон("%1.zip", ИдОтчетаОбОшибке); + ИмяФайлаОтчета = СтрШаблон("%1.zip", Идентификатор); ИмяАрхива = ОбъединитьПути(Каталог, ИмяФайлаОтчета); ПотокВходящегоФайла = ФайлОтчета.ОткрытьПотокДляЧтения(); + // ФайловыйПоток нужен здесь потому, что ЧтениеZipФайла в OneScript 1.4 не умеет читать Поток ФайловыйПоток = ФайловыеПотоки.ОткрытьДляЗаписи(ИмяАрхива); ПотокВходящегоФайла.КопироватьВ(ФайловыйПоток); ПотокВходящегоФайла.Закрыть(); ФайловыйПоток.Закрыть(); ЧтениеZIP = Новый ЧтениеZipФайла(ИмяАрхива); - КаталогОтчета = ОбъединитьПути(Каталог, ИдОтчетаОбОшибке); + КаталогОтчета = ОбъединитьПути(Каталог, Идентификатор); ЧтениеZIP.ИзвлечьВсе(КаталогОтчета); ЧтениеZIP.Закрыть(); @@ -165,33 +186,61 @@ КонецПроцедуры -Процедура ЗаписатьИнформациюОбОшибке(Знач ИдОтчетаОбОшибке, ФайлОтчета) Экспорт +Процедура ЗаписатьОшибку(ДанныеОтчетаОбОшибке) Экспорт - ТЗ = ПолучитьЗарегистрированныеОшибки(); + Ошибки = ПолучитьОшибки(); - нСтр = ТЗ.Добавить(); - нСтр.id = ИдОтчетаОбОшибке; - нСтр.datetime = ТекущаяУниверсальнаяДата(); - - Текст = Новый ЗаписьТекста(ИмяФайлаЗарегистрированныеОшибки); + Отчет = ДанныеОтчетаОбОшибке.Отчет; + + нСтр = Ошибки.Добавить(); + нСтр.id = ДанныеОтчетаОбОшибке.Идентификатор; + нСтр.datetime = Отчет["time"]; + нСтр.fingerprint = ОтпечатокОшибки(Отчет); + + Текст = Новый ЗаписьТекста(ИмяФайлаОшибки); ПарсерJSON = Новый ПарсерJSON; - Текст.Записать(ПарсерJSON.ЗаписатьJSON(ТЗ)); + Текст.Записать(ПарсерJSON.ЗаписатьJSON(Ошибки)); Текст.Закрыть(); КонецПроцедуры +Функция ОтпечатокОшибки(ОтчетОбОшибке) + + Провайдер = Новый ХешированиеДанных(ХешФункция.MD5); + + СистемнаяИнфоОбОшибке = ОтчетОбОшибке["errorInfo"]["systemErrorInfo"]; + + clientStackHash = СистемнаяИнфоОбОшибке["clientStackHash"]; + Если ЗначениеЗаполнено(clientStackHash) Тогда + Провайдер.Добавить(clientStackHash); + КонецЕсли; + + serverStackHash = СистемнаяИнфоОбОшибке["serverStackHash"]; + Если ЗначениеЗаполнено(serverStackHash) Тогда + Провайдер.Добавить(serverStackHash); + КонецЕсли; + + Возврат Провайдер.ХешСуммаСтрокой; + +КонецФункции + Функция ПолучитьДанныеОтчетаОбОшибке(Знач ИдОтчетаОбОшибке) Экспорт - Результат = ОтчетыОбОшибках.СтруктураДанныхОтчетаОбОшибке(); + Результат = СтруктураДанныхОтчетаОбОшибке(); Результат.Идентификатор = ИдОтчетаОбОшибке; КаталогОтчета = ОбъединитьПути(Каталог, ИдОтчетаОбОшибке); ФайлыАрхива = НайтиФайлы(КаталогОтчета, "*", Истина); + Если ФайлыАрхива.Количество() = 0 Тогда + ТекстИсключения = СтрШаблон("Не удалось найти отчет об ошибке Ид %1", ИдОтчетаОбОшибке); + ВызватьИсключение(ТекстИсключения); + КонецЕсли; + Для Каждого Файл Из ФайлыАрхива Цикл Если Файл.Имя = "report.json" Тогда - ОтчетыОбОшибках.ЗаполнитьОтчетОбОшибке(Результат, Файл.ПолноеИмя); + ЗаполнитьОтчетОбОшибке(Результат, Файл.ПолноеИмя); ИначеЕсли Файл.Имя = "screenshot.png" Тогда Результат.Скриншот = Файл.ПолноеИмя; Иначе @@ -203,3 +252,60 @@ Возврат Результат; КонецФункции + +Процедура ЗаполнитьОтчетОбОшибке(СтруктураОтчета, ИмяФайла) Экспорт + + ЧтениеТекста = Новый ЧтениеТекста(ИмяФайла, КодировкаТекста.UTF8); + ОтчетОбОшибкеJSON = ЧтениеТекста.Прочитать(); + ЧтениеТекста.Закрыть(); + + ПарсерJSON = Новый ПарсерJSON; + ДанныеОтчетаОбОшибке = ПарсерJSON.ПрочитатьJSON(ОтчетОбОшибкеJSON); + + СтруктураОтчета.Отчет = ДанныеОтчетаОбОшибке; + +КонецПроцедуры + +Функция СтруктураДанныхОтчетаОбОшибке() Экспорт + + Результат = Новый Структура(); + Результат.Вставить("Идентификатор", ""); + Результат.Вставить("Отчет", Неопределено); + Результат.Вставить("Файлы", Новый Массив()); + Результат.Вставить("Скриншот", ""); + + Возврат Результат; + +КонецФункции + +Функция ОбработатьОтчетОбОшибке(ФайлОтчета) Экспорт + + Идентификатор = Строка(Новый УникальныйИдентификатор()); + СохранитьФайлОтчета(Идентификатор, ФайлОтчета); + + ДанныеОтчетаОбОшибке = ПолучитьДанныеОтчетаОбОшибке(Идентификатор); + ЗаписатьОшибку(ДанныеОтчетаОбОшибке); + + Возврат ДанныеОтчетаОбОшибке; + +КонецФункции + +Процедура УстановитьИдЗадачиВТрекере(ИдентификаторЗадачи, ИдЗадачиВТрекере) Экспорт + + Ид = Неопределено; + + Ошибки = ПолучитьОшибки(); + + СтруктураОтбора = Новый Структура("id", ИдентификаторЗадачи); + + НайденныеОшибки = Ошибки.НайтиСтроки(СтруктураОтбора); + Если НайденныеОшибки.Количество() Тогда + НайденныеОшибки[0].external_id = ИдЗадачиВТрекере; + КонецЕсли; + + Текст = Новый ЗаписьТекста(ИмяФайлаОшибки); + ПарсерJSON = Новый ПарсерJSON; + Текст.Записать(ПарсерJSON.ЗаписатьJSON(Ошибки)); + Текст.Закрыть(); + +КонецПроцедуры \ No newline at end of file diff --git a/src/model/lib.config b/src/model/lib.config index f858cc2..297328e 100644 --- a/src/model/lib.config +++ b/src/model/lib.config @@ -7,7 +7,5 @@ - - \ No newline at end of file diff --git "a/src/model/modules/\320\227\320\260\320\277\321\200\320\276\321\201\321\213\320\230\320\275\321\204\320\276\320\236\320\261\320\236\321\210\320\270\320\261\320\272\320\265.os" "b/src/model/modules/\320\227\320\260\320\277\321\200\320\276\321\201\321\213\320\230\320\275\321\204\320\276\320\236\320\261\320\236\321\210\320\270\320\261\320\272\320\265.os" index 889b1c8..ae7a5b4 100644 --- "a/src/model/modules/\320\227\320\260\320\277\321\200\320\276\321\201\321\213\320\230\320\275\321\204\320\276\320\236\320\261\320\236\321\210\320\270\320\261\320\272\320\265.os" +++ "b/src/model/modules/\320\227\320\260\320\277\321\200\320\276\321\201\321\213\320\230\320\275\321\204\320\276\320\236\320\261\320\236\321\210\320\270\320\261\320\272\320\265.os" @@ -6,23 +6,87 @@ КонецФункции -Процедура Записать(ЗапросИнфоОбОшибке) Экспорт +Функция ОбработатьЗапрос(Знач ТелоЗапросаТекст) Экспорт - ХранилищеДанных = МенеджерНастроек.ХранилищеДанных(); + ПарсерJSON = Новый ПарсерJSON; + ЗапросИнфоОбОшибке = ПарсерJSON.ПрочитатьJSON(ТелоЗапросаТекст, , , Истина); - ХранилищеДанных.ЗаписатьЗапросИнфоОбОшибке(ЗапросИнфоОбОшибке); + Ид = Строка(Новый УникальныйИдентификатор()); + Отпечаток = ОтпечатокОшибки(ЗапросИнфоОбОшибке); -КонецПроцедуры + ЗапросИнфоОбОшибке.Вставить("x_id", Ид); + ЗапросИнфоОбОшибке.Вставить("x_datetime", ТекущаяУниверсальнаяДата()); + ЗапросИнфоОбОшибке.Вставить("x_fingerprint", Отпечаток); -Функция СформироватьОтвет(Знач ЗапросИнфоОбОшибке) Экспорт + ХранилищеДанных = МенеджерНастроек.ХранилищеДанных(); + ХранилищеДанных.ЗаписатьЗапросИнфоОбОшибке(ЗапросИнфоОбОшибке); + + ОтветНаЗапросИнфоОбОшибке = ОтветНаЗапросИнфоОбОшибке(ЗапросИнфоОбОшибке); СтруктураОтвета = Новый Структура; - СтруктураОтвета.Вставить("needSendReport", Истина); - СтруктураОтвета.Вставить("userMessage", "Ошибка перехвачена и будет зарегистрирована в баг-трекере. Нажмите OK"); - СтруктураОтвета.Вставить("dumpType", "0"); + СтруктураОтвета.Вставить("needSendReport", ОтветНаЗапросИнфоОбОшибке.ОтправлятьОтчет); + СтруктураОтвета.Вставить("userMessage", ОтветНаЗапросИнфоОбОшибке.Сообщение); + СтруктураОтвета.Вставить("dumpType", ОтветНаЗапросИнфоОбОшибке.ТипДампа); ПарсерJSON = Новый ПарсерJSON; - Возврат ПарсерJSON.ЗаписатьJSON(СтруктураОтвета); КонецФункции + +Функция ОтветНаЗапросИнфоОбОшибке(Знач ЗапросИнфоОбОшибке) + + Ответ = Новый Структура; + Ответ.Вставить("ОтправлятьОтчет", Истина); + Ответ.Вставить("Сообщение", "Отчет об ошибке будет отправлен автоматически."); + Ответ.Вставить("ТипДампа", "0"); + + Если ОтправлятьКаждыйОтчет() Тогда + Возврат Ответ; + КонецЕсли; + + ХранилищеДанных = МенеджерНастроек.ХранилищеДанных(); + ИдОшибки = ХранилищеДанных.ИдентификаторОшибкиВТрекереПоОтпечатку(ЗапросИнфоОбОшибке["x_fingerprint"]); + + Если ИдОшибки <> Неопределено Тогда + Ответ.Вставить("ОтправлятьОтчет", Ложь); + Сообщение = СтрШаблон("Данная ошибка уже была зарегистрирована. Ид: %1", ИдОшибки); + Ответ.Вставить("Сообщение", Сообщение); + КонецЕсли; + + Возврат Ответ; + +КонецФункции + +Функция ОтправлятьКаждыйОтчет() + + ТипТекущегоПровайдераИнтеграции = ТипЗнч(МенеджерНастроек.ПровайдерИнтеграции()); + + Если ТипТекущегоПровайдераИнтеграции = Тип("ПровайдерИнтеграцииRedmine") + Или ТипТекущегоПровайдераИнтеграции = Тип("ПровайдерИнтеграцииJira") Тогда + Возврат Ложь; + КонецЕсли; + + Возврат Истина; + +КонецФункции + +Функция ОтпечатокОшибки(ЗапросИнфоОбОшибке) + + Провайдер = Новый ХешированиеДанных(ХешФункция.MD5); + ДобавитьПараметрВПровайдерХеширования(Провайдер, ЗапросИнфоОбОшибке, "clientStackHash"); + ДобавитьПараметрВПровайдерХеширования(Провайдер, ЗапросИнфоОбОшибке, "serverStackHash"); + + Возврат Провайдер.ХешСуммаСтрокой; + +КонецФункции + +Процедура ДобавитьПараметрВПровайдерХеширования(Провайдер, ЗапросИнфоОбОшибке, Свойство) + + ЗначениеСвойства = Неопределено; + ЗапросИнфоОбОшибке.Свойство(Свойство, ЗначениеСвойства); + + Если ЗначениеЗаполнено(ЗначениеСвойства) Тогда + Провайдер.Добавить(ЗначениеСвойства); + КонецЕсли; + +КонецПроцедуры diff --git "a/src/model/modules/\320\227\320\260\321\200\320\265\320\263\320\270\321\201\321\202\321\200\320\270\321\200\320\276\320\262\320\260\320\275\320\275\321\213\320\265\320\236\321\210\320\270\320\261\320\272\320\270.os" "b/src/model/modules/\320\227\320\260\321\200\320\265\320\263\320\270\321\201\321\202\321\200\320\270\321\200\320\276\320\262\320\260\320\275\320\275\321\213\320\265\320\236\321\210\320\270\320\261\320\272\320\270.os" deleted file mode 100644 index cb20fd4..0000000 --- "a/src/model/modules/\320\227\320\260\321\200\320\265\320\263\320\270\321\201\321\202\321\200\320\270\321\200\320\276\320\262\320\260\320\275\320\275\321\213\320\265\320\236\321\210\320\270\320\261\320\272\320\270.os" +++ /dev/null @@ -1,7 +0,0 @@ -Функция ПолучитьСписок() Экспорт - - ХранилищеДанных = МенеджерНастроек.ХранилищеДанных(); - - Возврат ХранилищеДанных.ПолучитьЗарегистрированныеОшибки(); - -КонецФункции diff --git "a/src/model/modules/\320\234\320\265\320\275\320\265\320\264\320\266\320\265\321\200\320\235\320\260\321\201\321\202\321\200\320\276\320\265\320\272.os" "b/src/model/modules/\320\234\320\265\320\275\320\265\320\264\320\266\320\265\321\200\320\235\320\260\321\201\321\202\321\200\320\276\320\265\320\272.os" index 0ff5db5..dbfd9f8 100644 --- "a/src/model/modules/\320\234\320\265\320\275\320\265\320\264\320\266\320\265\321\200\320\235\320\260\321\201\321\202\321\200\320\276\320\265\320\272.os" +++ "b/src/model/modules/\320\234\320\265\320\275\320\265\320\264\320\266\320\265\321\200\320\235\320\260\321\201\321\202\321\200\320\276\320\265\320\272.os" @@ -12,12 +12,24 @@ КонецФункции +Процедура УстановитьХранилищеДанных(вхХранилищеДанных) Экспорт + + ХранилищеДанных = вхХранилищеДанных; + +КонецПроцедуры + Функция ПровайдерИнтеграции() Экспорт Возврат ПровайдерИнтеграции; КонецФункции +Процедура УстановитьПровайдерИнтеграции(вхПровайдерИнтеграции) Экспорт + + ПровайдерИнтеграции = вхПровайдерИнтеграции; + +КонецПроцедуры + Процедура Инициализировать(ПутьКФайлу = "") Экспорт Если Не ЗначениеЗаполнено(ПутьКФайлу) Тогда @@ -26,8 +38,8 @@ Настройки = ПрочитатьФайлНастроекПриложения(ПутьКФайлу); - УстановитьХранилищеДанных(Настройки); - УстановитьПровайдерИнтеграции(Настройки); + НастроитьХранилищеДанных(Настройки); + НастроитьПровайдерИнтеграции(Настройки); КонецПроцедуры @@ -44,7 +56,7 @@ КонецФункции -Процедура УстановитьХранилищеДанных(Настройки) +Процедура НастроитьХранилищеДанных(Настройки) ВидыХранилищ = Настройки["DataStorage"]; @@ -68,7 +80,7 @@ КонецПроцедуры -Процедура УстановитьПровайдерИнтеграции(Настройки) +Процедура НастроитьПровайдерИнтеграции(Настройки) Интеграции = Настройки["Integrations"]; diff --git "a/src/model/modules/\320\236\320\261\321\200\320\260\320\261\320\276\321\202\320\272\320\260\320\236\321\210\320\270\320\261\320\276\320\272.os" "b/src/model/modules/\320\236\320\261\321\200\320\260\320\261\320\276\321\202\320\272\320\260\320\236\321\210\320\270\320\261\320\276\320\272.os" deleted file mode 100644 index aacdf25..0000000 --- "a/src/model/modules/\320\236\320\261\321\200\320\260\320\261\320\276\321\202\320\272\320\260\320\236\321\210\320\270\320\261\320\276\320\272.os" +++ /dev/null @@ -1,159 +0,0 @@ -Процедура ОбработатьОтчетОбОшибке(ФайлЗапроса) Экспорт - - Попытка - ИдОтчетаОбОшибке = ОтчетыОбОшибках.ОбработатьОтчет(ФайлЗапроса); - Исключение - ВызватьИсключение(ОписаниеОшибки()); - КонецПопытки; - - Попытка - ХранилищеДанных = МенеджерНастроек.ХранилищеДанных(); - ДанныеОтчетаОбОшибке = ХранилищеДанных.ПолучитьДанныеОтчетаОбОшибке(ИдОтчетаОбОшибке); - Исключение - ВызватьИсключение(ОписаниеОшибки()); - КонецПопытки; - - Попытка - ЗарегистрироватьОшибку(ДанныеОтчетаОбОшибке); - Исключение - ВызватьИсключение(ОписаниеОшибки()); - КонецПопытки; - -КонецПроцедуры - -Процедура ЗарегистрироватьОшибку(ДанныеОтчетаОбОшибке) - - ПровайдерИнтеграции = МенеджерНастроек.ПровайдерИнтеграции(); - - Если ПровайдерИнтеграции <> Неопределено Тогда - ПровайдерИнтеграции.ЗарегистрироватьОшибку(ДанныеОтчетаОбОшибке); - Иначе - ВызватьИсключение("Регистрация ошибок в баг-трекере не настроена"); - КонецЕсли; - -КонецПроцедуры - -Функция СформироватьЗаголовокОписанияОшибки() Экспорт - - ЧастиЗаголовка = Новый Массив(); - - ЧастиЗаголовка.Добавить("Ошибка зарегистрирована автоматически с помощью механизмов платформы 1С 8.3.17+"); - ЧастиЗаголовка.Добавить("======================"); - - Результат = СтрСоединить(ЧастиЗаголовка, Символы.ПС); - - Возврат Результат; - -КонецФункции - -Функция СформироватьТелоОписанияОшибки(ДанныеОтчетаОбОшибке) Экспорт - - ЧастиРезультата = Новый Массив(); - - ПоляОписанияОшибки = СформироватьТелоОписанияОшибкиJSON(ДанныеОтчетаОбОшибке); - - Для Каждого ПолеОшибки Из ПоляОписанияОшибки Цикл - - ПолеОшибкиПоШаблону = СтрШаблон("%1: %2", ПолеОшибки.Ключ, ПоляОписанияОшибки[ПолеОшибки.Ключ]); - - ЧастиРезультата.Добавить(ПолеОшибкиПоШаблону); - - КонецЦикла; - - Результат = СтрСоединить(ЧастиРезультата, Символы.ПС); - - Возврат Результат; - -КонецФункции - -Функция СформироватьТелоОписанияОшибкиJSON(ДанныеОтчетаОбОшибке) Экспорт - - Отчет = ДанныеОтчетаОбОшибке.Отчет; - ИнфоОКонфигурации = Отчет["configInfo"]; - ИнфоОКлиенте = Отчет["clientInfo"]; - ИнфоОСервере = Отчет["serverInfo"]; - ИнфоОбОшибке = Отчет["errorInfo"]; - ИнфоОбОшибкеПриложения = ИнфоОбОшибке["applicationErrorInfo"]; - - ПоляОписанияОшибки = Новый Структура; - - ПоляОписанияОшибки.Вставить("Дата", Отчет["time"]); - ПоляОписанияОшибки.Вставить("ИнформационнаяБаза", "Не предоставляется платформой"); - ПоляОписанияОшибки.Вставить("ИмяКонфигурации", ИнфоОКонфигурации["name"]); - ПоляОписанияОшибки.Вставить("ВерсияКонфигурации", ИнфоОКонфигурации["version"]); - ПоляОписанияОшибки.Вставить("ПлатформаКлиент", ИнфоОКлиенте["platformType"]); - ПоляОписанияОшибки.Вставить("ВерсияПлатформы1С", ИнфоОКлиенте["appVersion"]); - ПоляОписанияОшибки.Вставить("СУБД", ИнфоОСервере["dbms"]); - Если ИнфоОбОшибкеПриложения <> Неопределено Тогда - - УзелСтек = ИнфоОбОшибкеПриложения["stack"]; - Если УзелСтек <> Неопределено Тогда - Стек = УзелСтек[0]; - ПредставлениеСтека = СформироватьПредставлениеСтека(Стек); - ПоляОписанияОшибки.Вставить("Стек", ПредставлениеСтека); - КонецЕсли; - - УзелОшибки = ИнфоОбОшибкеПриложения["errors"]; - Если УзелОшибки <> Неопределено Тогда - ПредставлениеОшибок = СформироватьПредставлениеОшибок(УзелОшибки); - ПоляОписанияОшибки.Вставить("Ошибки", ПредставлениеОшибок); - КонецЕсли; - - КонецЕсли; - - Возврат ПоляОписанияОшибки; - -КонецФункции - -Функция СформироватьОписаниеОшибки(ДанныеОтчетаОбОшибке) Экспорт - - ЗаголовокОписанияОшибки = СформироватьЗаголовокОписанияОшибки(); - ТелоОписанияОшибки = СформироватьТелоОписанияОшибки(ДанныеОтчетаОбОшибке); - - Результат = СтрШаблон("%1%2%3", ЗаголовокОписанияОшибки, Символы.ПС, ТелоОписанияОшибки); - - Возврат Результат; - -КонецФункции - -Функция СформироватьПредставлениеСтека(Стек) - - ЧастиСтека = Новый Массив(); - - Для Каждого ЭлементСтека Из Стек Цикл - - ЧастиСтека.Добавить(ЭлементСтека); - - КонецЦикла; - - Результат = СтрСоединить(ЧастиСтека, Символы.ПС); - - Возврат Результат; - -КонецФункции - -Функция СформироватьПредставлениеОшибок(Ошибки) - - ЧастиОшибки = Новый Массив(); - - Для Каждого Ошибка Из Ошибки Цикл - - Для Каждого ЭлементОшибки Из Ошибка Цикл - - Если ТипЗнч(ЭлементОшибки) = Тип("Массив") Тогда - Для Каждого ЭлементЭлементаОшибки Из ЭлементОшибки Цикл - ЧастиОшибки.Добавить(ЭлементЭлементаОшибки); - КонецЦикла; - Иначе - ЧастиОшибки.Добавить(ЭлементОшибки); - КонецЕсли; - - КонецЦикла; - - КонецЦикла; - - Результат = СтрСоединить(ЧастиОшибки, Символы.ПС); - - Возврат Результат; - -КонецФункции diff --git "a/src/model/modules/\320\236\321\202\321\207\320\265\321\202\321\213\320\236\320\261\320\236\321\210\320\270\320\261\320\272\320\260\321\205.os" "b/src/model/modules/\320\236\321\202\321\207\320\265\321\202\321\213\320\236\320\261\320\236\321\210\320\270\320\261\320\272\320\260\321\205.os" index 46c3922..cf0fa08 100644 --- "a/src/model/modules/\320\236\321\202\321\207\320\265\321\202\321\213\320\236\320\261\320\236\321\210\320\270\320\261\320\272\320\260\321\205.os" +++ "b/src/model/modules/\320\236\321\202\321\207\320\265\321\202\321\213\320\236\320\261\320\236\321\210\320\270\320\261\320\272\320\260\321\205.os" @@ -1,46 +1,162 @@ #Использовать json -Функция ОбработатьОтчет(ФайлОтчета) Экспорт - - ИдОтчетаОбОшибке = Строка(Новый УникальныйИдентификатор()); +Функция ПолучитьСписок() Экспорт + + ХранилищеДанных = МенеджерНастроек.ХранилищеДанных(); + + Возврат ХранилищеДанных.ПолучитьОшибки(); + +КонецФункции + +Процедура ОбработатьОтчетОбОшибке(ФайлОтчета) Экспорт + ХранилищеДанных = МенеджерНастроек.ХранилищеДанных(); + ДанныеОтчетаОбОшибке = ХранилищеДанных.ОбработатьОтчетОбОшибке(ФайлОтчета); - Попытка - ХранилищеДанных.СохранитьФайлОтчета(ИдОтчетаОбОшибке, ФайлОтчета); - Исключение - ВызватьИсключение(ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())); - КонецПопытки; + ИдЗадачиВТрекере = ЗарегистрироватьОшибку(ДанныеОтчетаОбОшибке); + + Если ИдЗадачиВТрекере <> Неопределено Тогда + ХранилищеДанных.УстановитьИдЗадачиВТрекере(ДанныеОтчетаОбОшибке.Идентификатор, ИдЗадачиВТрекере); + КонецЕсли; + +КонецПроцедуры + +Функция ЗарегистрироватьОшибку(ДанныеОтчетаОбОшибке) + + ИдТрекера = Неопределено; + + ПровайдерИнтеграции = МенеджерНастроек.ПровайдерИнтеграции(); + + Если ПровайдерИнтеграции <> Неопределено Тогда + ИдТрекера = ПровайдерИнтеграции.ЗарегистрироватьОшибку(ДанныеОтчетаОбОшибке); + Иначе + ВызватьИсключение("Регистрация ошибок в баг-трекере не настроена"); + КонецЕсли; + + Возврат ИдТрекера; + +КонецФункции + +Функция СформироватьЗаголовокОписанияОшибки() Экспорт - Попытка - ХранилищеДанных.ЗаписатьИнформациюОбОшибке(ИдОтчетаОбОшибке, ФайлОтчета); - Исключение - ВызватьИсключение(ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())); - КонецПопытки; + ЧастиЗаголовка = Новый Массив(); + + ЧастиЗаголовка.Добавить("Ошибка зарегистрирована автоматически с помощью механизмов платформы 1С 8.3.17+"); + ЧастиЗаголовка.Добавить("======================"); + + Результат = СтрСоединить(ЧастиЗаголовка, Символы.ПС); + + Возврат Результат; - Возврат ИдОтчетаОбОшибке; +КонецФункции + +Функция СформироватьТелоОписанияОшибки(ДанныеОтчетаОбОшибке) Экспорт + + ЧастиРезультата = Новый Массив(); + + ПоляОписанияОшибки = СформироватьТелоОписанияОшибкиJSON(ДанныеОтчетаОбОшибке); + + Для Каждого ПолеОшибки Из ПоляОписанияОшибки Цикл + + ПолеОшибкиПоШаблону = СтрШаблон("%1: %2", ПолеОшибки.Ключ, ПоляОписанияОшибки[ПолеОшибки.Ключ]); + + ЧастиРезультата.Добавить(ПолеОшибкиПоШаблону); + + КонецЦикла; + + Результат = СтрСоединить(ЧастиРезультата, Символы.ПС); + + Возврат Результат; КонецФункции -Процедура ЗаполнитьОтчетОбОшибке(СтруктураОтчета, ИмяФайла) Экспорт +Функция СформироватьТелоОписанияОшибкиJSON(ДанныеОтчетаОбОшибке) Экспорт + + Отчет = ДанныеОтчетаОбОшибке.Отчет; + ИнфоОКонфигурации = Отчет["configInfo"]; + ИнфоОКлиенте = Отчет["clientInfo"]; + ИнфоОСервере = Отчет["serverInfo"]; + ИнфоОбОшибке = Отчет["errorInfo"]; + ИнфоОбОшибкеПриложения = ИнфоОбОшибке["applicationErrorInfo"]; - ЧтениеТекста = Новый ЧтениеТекста(ИмяФайла, КодировкаТекста.UTF8); - ОтчетОбОшибкеJSON = ЧтениеТекста.Прочитать(); - ЧтениеТекста.Закрыть(); + ПоляОписанияОшибки = Новый Структура; - ПарсерJSON = Новый ПарсерJSON; - ДанныеОтчетаОбОшибке = ПарсерJSON.ПрочитатьJSON(ОтчетОбОшибкеJSON); + ПоляОписанияОшибки.Вставить("Дата", Отчет["time"]); + ПоляОписанияОшибки.Вставить("ИнформационнаяБаза", "Не предоставляется платформой"); + ПоляОписанияОшибки.Вставить("ИмяКонфигурации", ИнфоОКонфигурации["name"]); + ПоляОписанияОшибки.Вставить("ВерсияКонфигурации", ИнфоОКонфигурации["version"]); + ПоляОписанияОшибки.Вставить("ПлатформаКлиент", ИнфоОКлиенте["platformType"]); + ПоляОписанияОшибки.Вставить("ВерсияПлатформы1С", ИнфоОКлиенте["appVersion"]); + ПоляОписанияОшибки.Вставить("СУБД", ИнфоОСервере["dbms"]); + Если ИнфоОбОшибкеПриложения <> Неопределено Тогда + + УзелСтек = ИнфоОбОшибкеПриложения["stack"]; + Если УзелСтек <> Неопределено Тогда + Стек = УзелСтек[0]; + ПредставлениеСтека = СформироватьПредставлениеСтека(Стек); + ПоляОписанияОшибки.Вставить("Стек", ПредставлениеСтека); + КонецЕсли; + + УзелОшибки = ИнфоОбОшибкеПриложения["errors"]; + Если УзелОшибки <> Неопределено Тогда + ПредставлениеОшибок = СформироватьПредставлениеОшибок(УзелОшибки); + ПоляОписанияОшибки.Вставить("Ошибки", ПредставлениеОшибок); + КонецЕсли; - СтруктураОтчета.Отчет = ДанныеОтчетаОбОшибке; + КонецЕсли; + + Возврат ПоляОписанияОшибки; + +КонецФункции + +Функция СформироватьОписаниеОшибки(ДанныеОтчетаОбОшибке) Экспорт -КонецПроцедуры + ЗаголовокОписанияОшибки = СформироватьЗаголовокОписанияОшибки(); + ТелоОписанияОшибки = СформироватьТелоОписанияОшибки(ДанныеОтчетаОбОшибке); + + Результат = СтрШаблон("%1%2%3", ЗаголовокОписанияОшибки, Символы.ПС, ТелоОписанияОшибки); + + Возврат Результат; + +КонецФункции -Функция СтруктураДанныхОтчетаОбОшибке() Экспорт +Функция СформироватьПредставлениеСтека(Стек) + + ЧастиСтека = Новый Массив(); - Результат = Новый Структура(); - Результат.Вставить("Идентификатор", ""); - Результат.Вставить("Отчет", Неопределено); - Результат.Вставить("Файлы", Новый Массив()); - Результат.Вставить("Скриншот", ""); + Для Каждого ЭлементСтека Из Стек Цикл + + ЧастиСтека.Добавить(ЭлементСтека); + + КонецЦикла; + + Результат = СтрСоединить(ЧастиСтека, Символы.ПС); + + Возврат Результат; + +КонецФункции + +Функция СформироватьПредставлениеОшибок(Ошибки) + + ЧастиОшибки = Новый Массив(); + + Для Каждого Ошибка Из Ошибки Цикл + + Для Каждого ЭлементОшибки Из Ошибка Цикл + + Если ТипЗнч(ЭлементОшибки) = Тип("Массив") Тогда + Для Каждого ЭлементЭлементаОшибки Из ЭлементОшибки Цикл + ЧастиОшибки.Добавить(ЭлементЭлементаОшибки); + КонецЦикла; + Иначе + ЧастиОшибки.Добавить(ЭлементОшибки); + КонецЕсли; + + КонецЦикла; + + КонецЦикла; + + Результат = СтрСоединить(ЧастиОшибки, Символы.ПС); Возврат Результат; diff --git a/src/packagedef b/src/packagedef index abb0b6f..1d86731 100644 --- a/src/packagedef +++ b/src/packagedef @@ -1,5 +1,5 @@ Описание.Имя("reperr") - .Версия("0.2.0") + .Версия("0.3.0") .ЗависитОт("fs") .ЗависитОт("json") .ЗависитОт("1connector") diff --git a/src/views/errorInfoRequests/Index.cshtml b/src/views/errorInfoRequests/Index.cshtml index f657054..180649b 100644 --- a/src/views/errorInfoRequests/Index.cshtml +++ b/src/views/errorInfoRequests/Index.cshtml @@ -5,6 +5,7 @@ id datetime + fingerprint configHash configVersion appStackHash @@ -20,8 +21,9 @@ @foreach(var row in Model) { - @row.id - @row.datetime + @row.x_id + @row.x_datetime + @row.x_fingerprint @row.configHash @row.configVersion @row.appStackHash diff --git a/src/views/errorReports/Index.cshtml b/src/views/errorReports/Index.cshtml index b5f8fff..6b1b511 100644 --- a/src/views/errorReports/Index.cshtml +++ b/src/views/errorReports/Index.cshtml @@ -5,6 +5,8 @@ id datetime + fingerprint + external_id @@ -12,6 +14,8 @@ @row.id @row.datetime + @row.fingerprint + @row.external_id } diff --git "a/tests/\320\236\320\261\321\200\320\260\320\261\320\276\321\202\320\272\320\260\320\227\320\260\320\277\321\200\320\276\321\201\320\260GetInfo.os" "b/tests/\320\236\320\261\321\200\320\260\320\261\320\276\321\202\320\272\320\260\320\227\320\260\320\277\321\200\320\276\321\201\320\260GetInfo.os" index 8b3354d..86689ea 100644 --- "a/tests/\320\236\320\261\321\200\320\260\320\261\320\276\321\202\320\272\320\260\320\227\320\260\320\277\321\200\320\276\321\201\320\260GetInfo.os" +++ "b/tests/\320\236\320\261\321\200\320\260\320\261\320\276\321\202\320\272\320\260\320\227\320\260\320\277\321\200\320\276\321\201\320\260GetInfo.os" @@ -1,3 +1,5 @@ +#Использовать asserts +#Использовать tempfiles #Использовать "../src/model" // BSLLS:UnusedParameters-off @@ -7,6 +9,7 @@ ВсеТесты = Новый Массив; ВсеТесты.Добавить("ТестДолжен_ОбработатьЗапросGetInfo"); + ВсеТесты.Добавить("ТестДолжен_ОбработатьУжеЗарегистрированнуюОшибку"); Возврат ВсеТесты; @@ -15,6 +18,11 @@ Процедура ПередЗапускомТеста() Экспорт УстановитьТекущийКаталог(ТекущийСценарий().Каталог); + КаталогДанных = ОбъединитьПути("..", "features", "fixtures"); + + ВременныйКаталог = ВременныеФайлы.СоздатьКаталог(); + ФС.КопироватьСодержимоеКаталога(КаталогДанных, ВременныйКаталог); + УстановитьТекущийКаталог(ВременныйКаталог); КонецПроцедуры @@ -24,19 +32,40 @@ Процедура ТестДолжен_ОбработатьЗапросGetInfo() Экспорт - ПутьКФайлуЗапрос = ОбъединитьПути("..", "features", "fixtures", "requestBodyGetInfo.json"); - ПутьКФайлуОтвет = ОбъединитьПути("..", "features", "fixtures", "responseBodyGetInfo.json"); - ЧтениеТекста = Новый ЧтениеТекста(ПутьКФайлуЗапрос, "utf-8"); - + ФайловоеХранилище = Новый ФайловоеХранилище("./data"); + МенеджерНастроек.УстановитьХранилищеДанных(ФайловоеХранилище); + + ЧтениеТекста = Новый ЧтениеТекста("getInfoRequest.json", "utf-8"); ТелоЗапросаТекст = ЧтениеТекста.Прочитать(); ЧтениеТекста.Закрыть(); + ТелоОтвета = ЗапросыИнфоОбОшибке.ОбработатьЗапрос(ТелоЗапросаТекст); - ТелоОтвета = ЗапросыИнфоОбОшибке.СформироватьОтвет(ТелоЗапросаТекст); - - ЧтениеТекста = Новый ЧтениеТекста(ПутьКФайлуОтвет, "utf-8"); + ЧтениеТекста = Новый ЧтениеТекста("getInfoResponse.json", "utf-8"); ТелоОтветаЭталон = ЧтениеТекста.Прочитать(); ЧтениеТекста.Закрыть(); Ожидаем.Что(ТелоОтвета).Равно(ТелоОтветаЭталон); КонецПроцедуры + +Процедура ТестДолжен_ОбработатьУжеЗарегистрированнуюОшибку() Экспорт + + ЧтениеТекста = Новый ЧтениеТекста("getInfoDuplicateRequest.json", "utf-8"); + ТелоЗапросаТекст = ЧтениеТекста.Прочитать(); + ЧтениеТекста.Закрыть(); + + ЧтениеТекста = Новый ЧтениеТекста("getInfoDuplicateResponse.json", "utf-8"); + Эталон = ЧтениеТекста.Прочитать(); + ЧтениеТекста.Закрыть(); + + ФайловоеХранилище = Новый ФайловоеХранилище("./data"); + МенеджерНастроек.УстановитьХранилищеДанных(ФайловоеХранилище); + + ПровайдерИнтеграции = Новый ПровайдерИнтеграцииRedmine(); + МенеджерНастроек.УстановитьПровайдерИнтеграции(ПровайдерИнтеграции); + + Результат = ЗапросыИнфоОбОшибке.ОбработатьЗапрос(ТелоЗапросаТекст); + + Ожидаем.Что(Результат).Равно(Эталон); + +КонецПроцедуры diff --git "a/tests/\320\244\320\260\320\271\320\273\320\276\320\262\320\276\320\265\320\245\321\200\320\260\320\275\320\270\320\273\320\270\321\211\320\265.os" "b/tests/\320\244\320\260\320\271\320\273\320\276\320\262\320\276\320\265\320\245\321\200\320\260\320\275\320\270\320\273\320\270\321\211\320\265.os" index 7234351..2a24a86 100644 --- "a/tests/\320\244\320\260\320\271\320\273\320\276\320\262\320\276\320\265\320\245\321\200\320\260\320\275\320\270\320\273\320\270\321\211\320\265.os" +++ "b/tests/\320\244\320\260\320\271\320\273\320\276\320\262\320\276\320\265\320\245\321\200\320\260\320\275\320\270\320\273\320\270\321\211\320\265.os" @@ -12,8 +12,10 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтениеЗапросовИнфоОбОшибкеИзФайла"); ВсеТесты.Добавить("ТестДолжен_ЗаписатьЗапросИнфоОбОшибкеВФайл"); - ВсеТесты.Добавить("ТестДолжен_ПолучитьЗарегистрированныеОшибки"); - ВсеТесты.Добавить("ТестДолжен_ЗаписатьЗарегистрированнуюОшибкуВФайл"); + ВсеТесты.Добавить("ТестДолжен_ПолучитьОшибки"); + ВсеТесты.Добавить("ТестДолжен_ЗаписатьОшибкуВФайл"); + ВсеТесты.Добавить("ТестДолжен_СохранитьФайлОтчета"); + ВсеТесты.Добавить("ТестДолжен_УстановитьИдЗадачиВТрекере"); Возврат ВсеТесты; @@ -41,7 +43,7 @@ Результат = ХранилищеДанных.ПолучитьЗапросыИнфоОбОшибках(); - Ожидаем.Что(Результат.Колонки.Количество()).Равно(13); + Ожидаем.Что(Результат.Колонки.Количество()).Равно(14); Ожидаем.Что(Результат.Количество()).Равно(3); КонецПроцедуры @@ -52,8 +54,6 @@ ХранилищеДанных = Новый ФайловоеХранилище(Каталог); ЗапросИнфоОбОшибке = Новый Структура; - ЗапросИнфоОбОшибке.Вставить("id", "7054dd5d-f0b8-491d-8dcc-18bc1d2cbc83"); - ЗапросИнфоОбОшибке.Вставить("datetime", "2020-10-05T21:58:45Z"); ЗапросИнфоОбОшибке.Вставить("configHash", "bd56b503adfc454b9fc84d7b0fea3e7900000000"); ЗапросИнфоОбОшибке.Вставить("сonfigName", "Config1"); ЗапросИнфоОбОшибке.Вставить("configVersion", "1.0.1.265"); @@ -74,43 +74,86 @@ ТелоЗапросаТекст = ЧтениеТекста.Прочитать(); ЧтениеТекста.Закрыть(); - Результат = ПарсерJSON.ПрочитатьJSON(ТелоЗапросаТекст, Истина, Ложь, Истина); + Результат = ПарсерJSON.ПрочитатьJSON(ТелоЗапросаТекст); Ожидаем.Что(Результат.Количество()).Равно(4); КонецПроцедуры -Процедура ТестДолжен_ПолучитьЗарегистрированныеОшибки() Экспорт +Процедура ТестДолжен_ПолучитьОшибки() Экспорт Каталог = "data"; ХранилищеДанных = Новый ФайловоеХранилище(Каталог); - Результат = ХранилищеДанных.ПолучитьЗарегистрированныеОшибки(); + Результат = ХранилищеДанных.ПолучитьОшибки(); - Ожидаем.Что(Результат.Колонки.Количество()).Равно(2); - Ожидаем.Что(Результат.Количество()).Равно(1); + Ожидаем.Что(Результат.Колонки.Количество()).Равно(4); + Ожидаем.Что(Результат.Количество()).Равно(2); КонецПроцедуры -Процедура ТестДолжен_ЗаписатьЗарегистрированнуюОшибкуВФайл() Экспорт +Процедура ТестДолжен_ЗаписатьОшибкуВФайл() Экспорт Каталог = "data"; ХранилищеДанных = Новый ФайловоеХранилище(Каталог); - ИнфоОбОшибке = Новый Структура; - ИнфоОбОшибке.Вставить("id", "7054dd5d-f0b8-491d-8dcc-18bc1d2cbc83"); - ИнфоОбОшибке.Вставить("datetime", "2020-10-05T21:58:45Z"); - - ХранилищеДанных.ЗаписатьИнформациюОбОшибке(ИнфоОбОшибке, Неопределено); - - ПутьКВременномуФайлу = ОбъединитьПути(Каталог, "errors.json"); - ЧтениеТекста = Новый ЧтениеТекста(ПутьКВременномуФайлу, "utf-8"); + ЧтениеТекста = Новый ЧтениеТекста("data/55555555-09d5-47b8-bf19-e7672eda1111/report.json", "utf-8"); ПарсерJSON = Новый ПарсерJSON; ТелоЗапросаТекст = ЧтениеТекста.Прочитать(); ЧтениеТекста.Закрыть(); - Результат = ПарсерJSON.ПрочитатьJSON(ТелоЗапросаТекст, Истина, Ложь, Истина); + ОтчетОбОшибке = ПарсерJSON.ПрочитатьJSON(ТелоЗапросаТекст); + + ДанныеОтчетаПоОшибке = Новый Структура; + ДанныеОтчетаПоОшибке.Вставить("Идентификатор", "7054dd5d-f0b8-491d-8dcc-18bc1d2cbc83"); + ДанныеОтчетаПоОшибке.Вставить("Отчет", ОтчетОбОшибке); - Ожидаем.Что(Результат.Количество()).Равно(2); + ХранилищеДанных.ЗаписатьОшибку(ДанныеОтчетаПоОшибке); + Результат = ХранилищеДанных.ПолучитьОшибки(); + + Ожидаем.Что(Результат.Количество()).Равно(3); + + НоваяОшибка = Результат[2]; + + Ожидаем.Что(НоваяОшибка["id"]).Равно("7054dd5d-f0b8-491d-8dcc-18bc1d2cbc83"); + // Не выполняется на GA + // Ожидаем.Что(НоваяОшибка["datetime"]).Равно(Дата("20201111231747")); + Ожидаем.Что(НоваяОшибка["fingerprint"]).Равно("622C3BA24596958DD3A2D9AF0AAD16BC"); + Ожидаем.Что(НоваяОшибка["external_id"]).Равно(""); + +КонецПроцедуры + +Процедура ТестДолжен_СохранитьФайлОтчета() Экспорт + + Каталог = "data"; + ХранилищеДанных = Новый ФайловоеХранилище(Каталог); + + ЧтениеДанных = Новый ЧтениеДанных("Ошибка_20201123003806.zip", "utf-8"); + РезультатЧтения = ЧтениеДанных.Прочитать(); + ЧтениеДанных.Закрыть(); + + ИдОтчета = "1111-2222-3333333-4444-55555"; + ХранилищеДанных.СохранитьФайлОтчета(ИдОтчета, РезультатЧтения); + ПутьКОтчету = ОбъединитьПути(Каталог, ИдОтчета); + Файлы = НайтиФайлы(ПутьКОтчету, "report.json"); + + Ожидаем.Что(Файлы.Количество()).Равно(1); КонецПроцедуры + +Процедура ТестДолжен_УстановитьИдЗадачиВТрекере() Экспорт + + Каталог = "data"; + ХранилищеДанных = Новый ФайловоеХранилище(Каталог); + + ХранилищеДанных.УстановитьИдЗадачиВТрекере("68359124-56e5-93aa-cd94-9531aa526f4", "12345"); + + Ошибки = ХранилищеДанных.ПолучитьОшибки(); + + Ожидаем.Что(Ошибки.Колонки.Количество()).Равно(4); + Ожидаем.Что(Ошибки.Количество()).Равно(2); + + Ошибка = Ошибки[1]; + Ожидаем.Что(Ошибка.external_id).Равно("12345"); + +КонецПроцедуры \ No newline at end of file