From 0f7a15482af576c9cd6ff151737b6b7858594f5b Mon Sep 17 00:00:00 2001 From: Maximov Valery Date: Wed, 15 May 2019 10:51:46 +0300 Subject: [PATCH 1/3] =?UTF-8?q?1.=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4=D0=B0?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D1=84=D0=BE=D1=80=D0=BC=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=BE=D1=82=D1=87=D0=B5?= =?UTF-8?q?=D1=82=D0=B0=20=D0=BF=D0=BE=20=D1=80=D0=B0=D0=B7=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D1=87=D0=B8=D0=BA=D0=B0=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2. Выполнены рефакторинг и исправление ошибок --- README.md | 10 +- examples/example-usr-report.cfg-file.json | 8 + examples/example-usr-report.json | 29 ++ ...20\230\321\210\321\203\320\267\321\213.os" | 140 +++------- ...20\236\321\202\321\207\320\265\321\202.os" | 129 +++------ ...21\202\320\265\320\273\321\217\320\274.os" | 252 ++++++++++++++++++ ...21\207\320\265\320\275\320\270\321\217.os" | 148 ++++++++++ ...21\201\321\202\320\265\320\274\321\213.os" | 12 +- ...20\241\320\276\320\275\320\260\321\200.os" | 123 ++++++++- 9 files changed, 636 insertions(+), 215 deletions(-) create mode 100644 examples/example-usr-report.cfg-file.json create mode 100644 examples/example-usr-report.json create mode 100644 "src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\274\320\260\320\275\320\264\320\260\320\241\320\263\320\265\320\275\320\265\321\200\320\270\321\200\320\276\320\262\320\260\321\202\321\214\320\236\321\202\321\207\320\265\321\202\320\237\320\276\320\237\320\276\320\273\321\214\320\267\320\276\320\262\320\260\321\202\320\265\320\273\321\217\320\274.os" create mode 100644 "src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\236\320\261\321\211\320\265\320\263\320\276\320\235\320\260\320\267\320\275\320\260\321\207\320\265\320\275\320\270\321\217.os" diff --git a/README.md b/README.md index c160910..92b97c0 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,14 @@ SonarQube может создавать несколько замечаний п На данный момент поддерживаются следующие варианты формирования отчета: -- **JSON** - формируется отчет в формате json, метрики выгружаются дважды - для идентификатора метрики и для русского представления. [Пример: examples\example-report.json](examples\example-report.json) -- **HTML** - формируется отчет в формате html, метрики выгнржаются только для русского представления. [Пример: examples\example-report.html](examples\example-report.html) +- **JSON** - формируется отчет в формате json, метрики выгружаются дважды - для идентификатора метрики и для русского представления. [Пример: examples\example-report.json](examples/example-report.json) +- **HTML** - формируется отчет в формате html, метрики выгнржаются только для русского представления. [Пример: examples\example-report.html](examples/example-report.html) + +### Сводный отчет по разработчикам + +При необходимоси получить отчет по разработчикам (не конкретные замечания, а "температуру") и понять, кто в команде как работает можно вручную собирать информацию по страницам SonarQube, но проще использовать **`sonar-helper`**, вызвав команду **`usr-report`** и проанализировать полученный результат. + +На данный момент поддерживается вариант формирования отчета в формате **JSON**. [Пример отчета: examples\example-usr-report.json](examples/example-usr-report.json) ## Примеры diff --git a/examples/example-usr-report.cfg-file.json b/examples/example-usr-report.cfg-file.json new file mode 100644 index 0000000..aa84763 --- /dev/null +++ b/examples/example-usr-report.cfg-file.json @@ -0,0 +1,8 @@ +{ + "--token": "2ef785ca946170b7086668132175eb01386943a1", + "--server": "http://my.sonar.server:9000", + "--projects": "bsp,mdm", + "--statuses": "OPEN", + "--report-path": "usr-rep.json", + "--report-format": "JSON" +} diff --git a/examples/example-usr-report.json b/examples/example-usr-report.json new file mode 100644 index 0000000..4104545 --- /dev/null +++ b/examples/example-usr-report.json @@ -0,0 +1,29 @@ +{ +"ДатаФормирования": "2019-05-06T14:17:17.5125591", +"СерверSonarQube": "http:\/\/my.sonar.server:9000", +"Количество": 2, +"Замечания": [ +{ +"КлючПроекта": "mdm", +"Статус": "OPEN", +"Автор": "my.user@mail.domain", +"Отвественный": "my.user", +"Правило": "RuleCode", +"Критичность": "MAJOR", +"Тип": "BUG", +"Время": 40, +"Количество": 20 +}, +{ +"КлючПроекта": "bsp", +"Статус": "OPEN", +"Автор": "my.user@mail.domain", +"Отвественный": "my.user", +"Правило": "RuleCode2", +"Критичность": "MINOR", +"Тип": "CODE_SMELL", +"Время": 29, +"Количество": 29 +} +] +} diff --git "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\274\320\260\320\275\320\264\320\260\320\227\320\260\320\272\321\200\321\213\321\202\321\214\320\230\321\210\321\203\320\267\321\213.os" "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\274\320\260\320\275\320\264\320\260\320\227\320\260\320\272\321\200\321\213\321\202\321\214\320\230\321\210\321\203\320\267\321\213.os" index b78d09c..30f5160 100644 --- "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\274\320\260\320\275\320\264\320\260\320\227\320\260\320\272\321\200\321\213\321\202\321\214\320\230\321\210\321\203\320\267\321\213.os" +++ "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\274\320\260\320\275\320\264\320\260\320\227\320\260\320\272\321\200\321\213\321\202\321\214\320\230\321\210\321\203\320\267\321\213.os" @@ -1,15 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Служебный модуль с реализацией работы команды issue-resolver +// +// (C) Maximov Valery (aka theshadowco) +// +/////////////////////////////////////////////////////////////////////////////////////////////////// + Перем Лог; -Перем Токен; -Перем АдресСервера; -Перем РодительскийПроект; Перем ДочерниеПроекты; Перем ИсключаемыеПроекты; Перем Статусы; -Перем ИзEDTВКонфигуратор; -Перем Теги; Перем МаксимальноеКоличествоСтатусов; +Перем ПрочитанныеПараметры; + /////////////////////////////////////////////////////////////////////////////////////////////////// // Прикладной интерфейс /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -54,15 +59,19 @@ Лог = ДополнительныеПараметры.Лог; ОшибокНет = ПрочитатьПараметрыЗапуска(ПараметрыКоманды); Если НЕ ОшибокНет Тогда - Возврат СообщитьОбОшибке("Ошибка запуска"); + Возврат ОбщегоНазначения.СообщитьОбОшибке("Ошибка запуска"); КонецЕсли; - + + АдресСервера = ПрочитанныеПараметры.Получить("АдресСервера"); + Токен = ПрочитанныеПараметры.Получить("Токен"); + РодительскийПроект = ПрочитанныеПараметры.Получить("РодительскийПроект"); + Лог.Информация("Получение информации о проектах"); ПроектыSQ = Сонар.ПолучитьПроекты(АдресСервера, Токен); Лог.Информация("Получение информации о родительском проекте"); ОписаниеРодительскогоПроекта = ПроектыSQ.Получить(РодительскийПроект); Если ОписаниеРодительскогоПроекта = Неопределено Тогда - Возврат СообщитьОбОшибке(СтрШаблон("Родительский проект `%1` в списке проектов не обнаружен", РодительскийПроект)); + Возврат ОбщегоНазначения.СообщитьОбОшибке(СтрШаблон("Родительский проект `%1` в списке проектов не обнаружен", РодительскийПроект)); КонецЕсли; Лог.Информация("Получение закрываемых замечаний из родительского проекта"); @@ -70,7 +79,7 @@ АдресСервера, Токен, ОписаниеРодительскогоПроекта, СтрСоединить(Статусы, ","), - ИзEDTВКонфигуратор); + ПрочитанныеПараметры.Получить("ИзEDTВКонфигуратор")); Если НЕ ЗамечанияРодительскогоПроекта.Количество() Тогда @@ -102,6 +111,7 @@ КонецЦикла; Лог.Информация("Закрытие замечаний в дочерних проектах"); + Теги = ПрочитанныеПараметры.Получить("Теги"); Для Каждого ДочернийПроект Из ДочерниеПроекты Цикл Лог.Информация("Закрытие замечаний в проекте `%1`", ДочернийПроект); ОписаниеПроекта = ПроектыSQ.Получить(СокрЛП(ДочернийПроект)); @@ -127,103 +137,33 @@ Функция ПрочитатьПараметрыЗапуска(ПараметрыКоманды) + ПрочитанныеПараметры = ОбщегоНазначения.ПрочитатьПараметрыЗапуска(ПараметрыКоманды, Истина); + ОшибокНет = ПрочитанныеПараметры.Получить("ОшибокНет"); МаксимальноеКоличествоСтатусов = 5; - ОшибокНет = Истина; - - Токен = ПараметрыКоманды["--token"]; - АдресСервера = ПараметрыКоманды["--server"]; - РодительскийПроект = ПараметрыКоманды["--parent-project"]; - ДочерниеПроектыСтрокой = ПараметрыКоманды["--child-projects"]; - ИсключаемыеПроектыСтрокой = ПараметрыКоманды["--exclude-projects"]; - СтатусыСтрокой = ПараметрыКоманды["--statuses"]; - Теги = ПараметрыКоманды["--tags"]; - ИзEDTВКонфигуратор = ПараметрыКоманды["--edt2cfg"]; - - ПутьККонфигурационномуФайлу = ПараметрыКоманды["--cfg-file"]; - Если ПутьККонфигурационномуФайлу <> Неопределено Тогда - - Файл = Новый Файл(ПутьККонфигурационномуФайлу); - Если Файл.Существует() Тогда - ТекстовыйДокумент = Новый ТекстовыйДокумент(); - ТекстовыйДокумент.Прочитать(ПутьККонфигурационномуФайлу); - Лог.Отладка("Содержимое конфигурационного файла: %1%2", Символы.ПС, ТекстовыйДокумент.ПолучитьТекст()); - JSON = Новый ЧтениеJSON(); - JSON.УстановитьСтроку(ТекстовыйДокумент.ПолучитьТекст()); - ПараметрыИзФайла = ПрочитатьJSON(JSON, Истина); - Если Токен = Неопределено Тогда - Токен = ПараметрыИзФайла.Получить("--token"); - КонецЕсли; - Если АдресСервера = Неопределено Тогда - АдресСервера = ПараметрыИзФайла.Получить("--server"); - КонецЕсли; - Если РодительскийПроект = Неопределено Тогда - РодительскийПроект = ПараметрыИзФайла.Получить("--parent-project"); - КонецЕсли; - Если ДочерниеПроектыСтрокой = Неопределено Тогда - ДочерниеПроектыСтрокой = ПараметрыИзФайла.Получить("--child-projects"); - КонецЕсли; - Если ИсключаемыеПроектыСтрокой = Неопределено Тогда - ИсключаемыеПроектыСтрокой = ПараметрыИзФайла.Получить("--exclude-projects"); - КонецЕсли; - Если СтатусыСтрокой = Неопределено Тогда - СтатусыСтрокой = ПараметрыИзФайла.Получить("--statuses"); - КонецЕсли; - Если Теги = Неопределено Тогда - Теги = ПараметрыИзФайла.Получить("--tags"); - КонецЕсли; - Если ИзEDTВКонфигуратор = Ложь И ПараметрыИзФайла.Получить("--edt2cfg") <> Неопределено Тогда - ИзEDTВКонфигуратор = ПараметрыИзФайла.Получить("--edt2cfg"); - КонецЕсли; - Иначе - Лог.Ошибка("Конфигурационный файл по пути `%1` не обнаружен", ОбернутьЗначениеДляПечати(ПутьККонфигурационномуФайлу)); - ОшибокНет = Ложь; - КонецЕсли; - - КонецЕсли; - - Лог.Отладка("Прочитанные параметры:"); - Лог.Отладка(" Токен = `%1`", ОбернутьЗначениеДляПечати(Токен)); - Лог.Отладка(" Адрес сервера = `%1`", ОбернутьЗначениеДляПечати(АдресСервера)); - Лог.Отладка(" Ключ родительского проекта = `%1`", ОбернутьЗначениеДляПечати(РодительскийПроект)); - Лог.Отладка(" Ключ дочерних проектов (строкой) = `%1`", ОбернутьЗначениеДляПечати(ДочерниеПроектыСтрокой)); - Лог.Отладка(" Ключ исключаемых проектов (строкой) = `%1`", ОбернутьЗначениеДляПечати(ИсключаемыеПроектыСтрокой)); - Лог.Отладка(" Статусы строкой = `%1`", ОбернутьЗначениеДляПечати(СтатусыСтрокой)); - Лог.Отладка(" Устанавливаемые теги = `%1`", ОбернутьЗначениеДляПечати(Теги)); - Лог.Отладка(" Нужно переводить из EDT = `%1`", ОбернутьЗначениеДляПечати(ИзEDTВКонфигуратор)); - Если Не ЗначениеЗаполнено(Токен) Тогда - Лог.Ошибка("Не указан токен для авторизации"); - ОшибокНет = Ложь; - КонецЕсли; - - Если Не ЗначениеЗаполнено(АдресСервера) Тогда - Лог.Ошибка("Не указан адрес сервера SonarQube"); - ОшибокНет = Ложь; - КонецЕсли; - - Если Не ЗначениеЗаполнено(РодительскийПроект) Тогда + Если Не ЗначениеЗаполнено(ПрочитанныеПараметры.Получить("РодительскийПроект")) Тогда Лог.Ошибка("Не указан ключ родительского проекта"); ОшибокНет = Ложь; КонецЕсли; - Если Не ЗначениеЗаполнено(ДочерниеПроектыСтрокой) Тогда - Лог.Ошибка("Не указаны ключ дочерних проектов"); + Если Не ЗначениеЗаполнено(ПрочитанныеПараметры.Получить("ДочерниеПроектыСтрокой")) Тогда + Лог.Ошибка("Не указаны ключи дочерних проектов"); ОшибокНет = Ложь; Иначе - ДочерниеПроекты = СтрРазделить(ДочерниеПроектыСтрокой, ",", Ложь); + ДочерниеПроекты = СтрРазделить(ПрочитанныеПараметры.Получить("ДочерниеПроектыСтрокой"), ", ", Ложь); КонецЕсли; ИсключаемыеПроекты = Новый Массив(); - Если ЗначениеЗаполнено(ИсключаемыеПроектыСтрокой) Тогда - ИсключаемыеПроекты = СтрРазделить(ИсключаемыеПроектыСтрокой, ",", Ложь); + Если ЗначениеЗаполнено(ПрочитанныеПараметры.Получить("ИсключаемыеПроектыСтрокой")) Тогда + ИсключаемыеПроекты = СтрРазделить(ПрочитанныеПараметры.Получить("ИсключаемыеПроектыСтрокой"), ", ", Ложь); КонецЕсли; - ИсключаемыеПроекты.Добавить(СокрЛП(РодительскийПроект)); + ИсключаемыеПроекты.Добавить(СокрЛП(ПрочитанныеПараметры.Получить("РодительскийПроект"))); - Если Не ЗначениеЗаполнено(СтатусыСтрокой) Тогда + Если Не ЗначениеЗаполнено(ПрочитанныеПараметры.Получить("СтатусыСтрокой")) Тогда Статусы = СтрРазделить("RESOLVED,CLOSED", ",", Ложь); Иначе Статусы = Новый Массив(); - СтатусыВрем = СтрРазделить(ВРЕГ(СтатусыСтрокой), ",", Ложь); + СтатусыВрем = СтрРазделить(ВРЕГ(ПрочитанныеПараметры.Получить("СтатусыСтрокой")), ", ", Ложь); ВсеСтатусы = СтрРазделить("OPEN,CONFIRMED,REOPENED,RESOLVED,CLOSED", ","); Для Каждого Статус Из СтатусыВрем Цикл Если ВсеСтатусы.Найти(Статус) <> Неопределено Тогда @@ -237,25 +177,11 @@ ОшибокНет = Ложь; КонецЕсли; КонецЕсли; - - Если НЕ ЗначениеЗаполнено(Теги) Тогда - Теги = ""; + + Если НЕ ЗначениеЗаполнено(ПрочитанныеПараметры.Получить("Теги")) Тогда + ПрочитанныеПараметры.Вставить("Теги", ""); КонецЕсли; Возврат ОшибокНет; КонецФункции - -Функция ОбернутьЗначениеДляПечати(Знач Значение) - Если ЗначениеЗаполнено(Значение) Тогда - Возврат Строка(Значение); - КонецЕсли; - Возврат "<Незаполнено>"; -КонецФункции - -Функция СообщитьОбОшибке(ТекстОшибки) - - Лог.Ошибка(ТекстОшибки); - Возврат МенеджерКомандПриложения.РезультатыКоманд().ОшибкаВремениВыполнения; - -КонецФункции diff --git "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\274\320\260\320\275\320\264\320\260\320\241\320\263\320\265\320\275\320\265\321\200\320\270\321\200\320\276\320\262\320\260\321\202\321\214\320\236\321\202\321\207\320\265\321\202.os" "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\274\320\260\320\275\320\264\320\260\320\241\320\263\320\265\320\275\320\265\321\200\320\270\321\200\320\276\320\262\320\260\321\202\321\214\320\236\321\202\321\207\320\265\321\202.os" index 131555f..82db771 100644 --- "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\274\320\260\320\275\320\264\320\260\320\241\320\263\320\265\320\275\320\265\321\200\320\270\321\200\320\276\320\262\320\260\321\202\321\214\320\236\321\202\321\207\320\265\321\202.os" +++ "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\274\320\260\320\275\320\264\320\260\320\241\320\263\320\265\320\275\320\265\321\200\320\270\321\200\320\276\320\262\320\260\321\202\321\214\320\236\321\202\321\207\320\265\321\202.os" @@ -1,13 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Служебный модуль с реализацией работы команды report +// +// (C) Maximov Valery (aka theshadowco) +// +/////////////////////////////////////////////////////////////////////////////////////////////////// + Перем Лог; -Перем Токен; -Перем АдресСервера; -Перем РодительскийПроект; -Перем Проекты; Перем Метрики; Перем ПутьКОтчету; Перем ФорматОтчета; +Перем ПрочитанныеПараметры; + /////////////////////////////////////////////////////////////////////////////////////////////////// // Прикладной интерфейс /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -17,7 +23,7 @@ ОписаниеКоманды = Парсер.ОписаниеКоманды(ИмяКоманды, "Формирует отчет по проекту/списку проектов"); Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, "--token", "Токен для авторизации на сервере SonarQube"); Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, "--server", "Адрес сервера SonarQube (например http://my.sonar.server:9000)"); - Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, "--projects", "Ключ проектов, строкой через запятую (например hrm:develop,buh)"); + Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, "--projects", "Ключи проектов, строкой через запятую (например hrm:develop,buh)"); Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, "--total-metrics", "Вывод в отчет метрик по проекту. | Нужные метрики передаются строкой через запятую. | Возможные значения (ключ - расшифровка): @@ -75,20 +81,18 @@ Лог = ДополнительныеПараметры.Лог; ОшибокНет = ПрочитатьПараметрыЗапуска(ПараметрыКоманды); Если НЕ ОшибокНет Тогда - Возврат СообщитьОбОшибке("Ошибка запуска"); + Возврат ОбщегоНазначения.СообщитьОбОшибке("Ошибка запуска"); КонецЕсли; - Если ПустаяСтрока(ПутьКОтчету) Тогда - ПутьКОтчету = ОбъединитьПути(".", "report." + НРег(ФорматОтчета)); - КонецЕсли; - + АдресСервера = ПрочитанныеПараметры.Получить("АдресСервера"); + Токен = ПрочитанныеПараметры.Получить("Токен"); + Лог.Информация("Получение информации о проектах"); ПроектыSQ = Сонар.ПолучитьПроекты(АдресСервера, Токен); + КоллекцияОтчетов = СоздатьКоллекциюОтчетов(АдресСервера); - КоллекцияОтчетов = СоздатьКоллекциюОтчетов(); Лог.Информация("Формирование отчетов по проектам"); - - КоллекцияПроектов = СтрРазделить(Проекты, ",", Ложь); + КоллекцияПроектов = СтрРазделить(ПрочитанныеПараметры.Получить("Проекты"), ",", Ложь); Для Каждого Проект Из КоллекцияПроектов Цикл Лог.Информация(" Формирование отчета для проекта `%1`", Проект); @@ -115,103 +119,30 @@ Функция ПрочитатьПараметрыЗапуска(ПараметрыКоманды) - МаксимальноеКоличествоСтатусов = 5; - ОшибокНет = Истина; - - Токен = ПараметрыКоманды["--token"]; - АдресСервера = ПараметрыКоманды["--server"]; - Проекты = ПараметрыКоманды["--projects"]; - Метрики = ПараметрыКоманды["--total-metrics"]; - ПутьКОтчету = ПараметрыКоманды["--report-path"]; - ФорматОтчета = ПараметрыКоманды["--report-format"]; + ПрочитанныеПараметры = ОбщегоНазначения.ПрочитатьПараметрыЗапуска(ПараметрыКоманды, Истина); + ОшибокНет = ПрочитанныеПараметры.Получить("ОшибокНет"); - ПутьККонфигурационномуФайлу = ПараметрыКоманды["--cfg-file"]; - Если ПутьККонфигурационномуФайлу <> Неопределено Тогда - - Файл = Новый Файл(ПутьККонфигурационномуФайлу); - Если Файл.Существует() Тогда - ТекстовыйДокумент = Новый ТекстовыйДокумент(); - ТекстовыйДокумент.Прочитать(ПутьККонфигурационномуФайлу); - Лог.Отладка("Содержимое конфигурационного файла: %1%2", Символы.ПС, ТекстовыйДокумент.ПолучитьТекст()); - JSON = Новый ЧтениеJSON(); - JSON.УстановитьСтроку(ТекстовыйДокумент.ПолучитьТекст()); - ПараметрыИзФайла = ПрочитатьJSON(JSON, Истина); - Если Токен = Неопределено Тогда - Токен = ПараметрыИзФайла.Получить("--token"); - КонецЕсли; - Если АдресСервера = Неопределено Тогда - АдресСервера = ПараметрыИзФайла.Получить("--server"); - КонецЕсли; - Если Проекты = Неопределено Тогда - Проекты = ПараметрыИзФайла.Получить("--projects"); - КонецЕсли; - Если Метрики = Неопределено Тогда - Метрики = ПараметрыИзФайла.Получить("--total-metrics"); - КонецЕсли; - Если ПутьКОтчету = Неопределено Тогда - ПутьКОтчету = ПараметрыИзФайла.Получить("--report-path"); - КонецЕсли; - Если ФорматОтчета = Неопределено Тогда - ФорматОтчета = ПараметрыИзФайла.Получить("--report-format"); - КонецЕсли; - Иначе - Лог.Ошибка("Конфигурационный файл по пути `%1` не обнаружен", ОбернутьЗначениеДляПечати(ПутьККонфигурационномуФайлу)); - ОшибокНет = Ложь; - КонецЕсли; - - КонецЕсли; - - Лог.Отладка("Прочитанные параметры:"); - Лог.Отладка(" Токен = `%1`", ОбернутьЗначениеДляПечати(Токен)); - Лог.Отладка(" Адрес сервера = `%1`", ОбернутьЗначениеДляПечати(АдресСервера)); - Лог.Отладка(" Ключи проектов = `%1`", ОбернутьЗначениеДляПечати(Проекты)); - Лог.Отладка(" Метрики проекта = `%1`", ОбернутьЗначениеДляПечати(Метрики)); - Лог.Отладка(" Путь к отчету = `%1`", ОбернутьЗначениеДляПечати(ПутьКОтчету)); - Лог.Отладка(" Формат отчета = `%1`", ОбернутьЗначениеДляПечати(ФорматОтчета)); - - Если Не ЗначениеЗаполнено(Токен) Тогда - Лог.Ошибка("Не указан токен для авторизации"); - ОшибокНет = Ложь; - КонецЕсли; - - Если Не ЗначениеЗаполнено(АдресСервера) Тогда - Лог.Ошибка("Не указан адрес сервера SonarQube"); - ОшибокНет = Ложь; - КонецЕсли; - - Если Не ЗначениеЗаполнено(Проекты) Тогда + Если Не ЗначениеЗаполнено(ПрочитанныеПараметры.Получить("Проекты")) Тогда Лог.Ошибка("Не указан ключ(-и) проекта(-ов)"); ОшибокНет = Ложь; КонецЕсли; - Метрики = ПолучитьПравильныеМетрики(Метрики, ОшибокНет); - - Если Не ЗначениеЗаполнено(ПутьКОтчету) Тогда - ПутьКОтчету = ""; - КонецЕсли; + Метрики = ПолучитьПравильныеМетрики(ПрочитанныеПараметры.Получить("Метрики"), ОшибокНет); + ПутьКОтчету = ПрочитанныеПараметры.Получить("ПутьКОтчету"); + ФорматОтчета = ПрочитанныеПараметры.Получить("ФорматОтчета"); Если Не ЗначениеЗаполнено(ФорматОтчета) Тогда ФорматОтчета = "JSON"; Иначе ФорматОтчета = ПолучитьПравильныйФорматОтчета(ФорматОтчета, ОшибокНет); КонецЕсли; - - Возврат ОшибокНет; - -КонецФункции -Функция ОбернутьЗначениеДляПечати(Знач Значение) - Если ЗначениеЗаполнено(Значение) Тогда - Возврат Строка(Значение); + Если Не ЗначениеЗаполнено(ПутьКОтчету) Тогда + ПутьКОтчету = ОбъединитьПути(".", "report." + НРег(ФорматОтчета)); КонецЕсли; - Возврат "<Незаполнено>"; -КонецФункции - -Функция СообщитьОбОшибке(ТекстОшибки) - Лог.Ошибка(ТекстОшибки); - Возврат МенеджерКомандПриложения.РезультатыКоманд().ОшибкаВремениВыполнения; - + Возврат ОшибокНет; + КонецФункции Функция ПолучитьПравильныйФорматОтчета(Знач ВыбранныйФорматОтчета, ОшибокНет) @@ -220,7 +151,7 @@ Если НЕ (ВРЕГВыбранныйФорматОтчета = "HTML" ИЛИ ВРЕГВыбранныйФорматОтчета = "JSON") Тогда ОшибокНет = Ложь; - Лог.Ошибка("Указан неизвестный формат отчета `%1`", ОбернутьЗначениеДляПечати(ВыбранныйФорматОтчета)); + Лог.Ошибка("Указан неизвестный формат отчета `%1`", ОбщегоНазначения.ОбернутьЗначениеДляПечати(ВыбранныйФорматОтчета)); КонецЕсли; @@ -243,7 +174,7 @@ ПравильныеМетрики.Вставить(СокрЛП(Метрика), 0); Иначе ОшибокНет = Ложь; - Лог.Ошибка("Указана неизвестная метрика `%1`", ОбернутьЗначениеДляПечати(Метрика)); + Лог.Ошибка("Указана неизвестная метрика `%1`", ОбщегоНазначения.ОбернутьЗначениеДляПечати(Метрика)); КонецЕсли; КонецЦикла; КонецЕсли; @@ -252,7 +183,7 @@ КонецФункции -Функция СоздатьКоллекциюОтчетов() +Функция СоздатьКоллекциюОтчетов(АдресСервера) Результат = Новый Структура(); Результат.Вставить("ДатаФормирования", ТекущаяДата()); diff --git "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\274\320\260\320\275\320\264\320\260\320\241\320\263\320\265\320\275\320\265\321\200\320\270\321\200\320\276\320\262\320\260\321\202\321\214\320\236\321\202\321\207\320\265\321\202\320\237\320\276\320\237\320\276\320\273\321\214\320\267\320\276\320\262\320\260\321\202\320\265\320\273\321\217\320\274.os" "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\274\320\260\320\275\320\264\320\260\320\241\320\263\320\265\320\275\320\265\321\200\320\270\321\200\320\276\320\262\320\260\321\202\321\214\320\236\321\202\321\207\320\265\321\202\320\237\320\276\320\237\320\276\320\273\321\214\320\267\320\276\320\262\320\260\321\202\320\265\320\273\321\217\320\274.os" new file mode 100644 index 0000000..b21b869 --- /dev/null +++ "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\274\320\260\320\275\320\264\320\260\320\241\320\263\320\265\320\275\320\265\321\200\320\270\321\200\320\276\320\262\320\260\321\202\321\214\320\236\321\202\321\207\320\265\321\202\320\237\320\276\320\237\320\276\320\273\321\214\320\267\320\276\320\262\320\260\321\202\320\265\320\273\321\217\320\274.os" @@ -0,0 +1,252 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Служебный модуль с реализацией работы команды report +// +// (C) Maximov Valery (aka theshadowco) +// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +Перем Лог; + +Перем ПутьКОтчету; +Перем ФорматОтчета; +Перем СтатусыСтрокой; + +Перем ПрочитанныеПараметры; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Прикладной интерфейс +/////////////////////////////////////////////////////////////////////////////////////////////////// + +Процедура ЗарегистрироватьКоманду(Знач ИмяКоманды, Знач Парсер) Экспорт + + ОписаниеКоманды = Парсер.ОписаниеКоманды(ИмяКоманды, "Формирует сводный отчет по наличию замечаний пользователей"); + Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, "--token", "Токен для авторизации на сервере SonarQube"); + Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, "--server", "Адрес сервера SonarQube (например http://my.sonar.server:9000)"); + Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, "--projects", "Ключи проектов, строкой через запятую (например hrm:develop,buh)"); + Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, "--statuses", "Отбор замечаний только соответствующих статусов. + | Необходимые статусы необходимо передавать строкой через запятую. + | Возможные значения: + | OPEN - открытые + | CONFIRMED - подтвержденные: + | REOPENED - переоткрытые + | RESOLVED - решенные + | CLOSED - закрытые + | + | По умолчанию выбираются только OPEN, CONFIRMED и REOPENED"); + Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, "--report-path", "Путь для сохранения отчета"); + Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, "--report-format", "Формат сохраняемого отчета. + | Возможные варианнты: + | HTML - создает html - страницу + | JSON - выгружает файл json + | + | По умолчанию используется формат JSON"); + Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, "--cfg-file", "Путь к конфигурационному файлу в формате json (utf-8), свойства объекта соответствуют ключам запуска. + | Пример конфигурационного файла находится в каталоге examples"); + + Парсер.ДобавитьКоманду(ОписаниеКоманды); + +КонецПроцедуры + +// Выполняет логику команды +// +// Параметры: +// ПараметрыКоманды - Соответствие - Соответствие ключей командной строки и их значений +// ДополнительныеПараметры - Соответствие - (необязательно) дополнительные параметры +// +Функция ВыполнитьКоманду(Знач ПараметрыКоманды, Знач ДополнительныеПараметры = Неопределено) Экспорт + + Лог = ДополнительныеПараметры.Лог; + ОшибокНет = ПрочитатьПараметрыЗапуска(ПараметрыКоманды); + Если НЕ ОшибокНет Тогда + Возврат ОбщегоНазначения.СообщитьОбОшибке("Ошибка запуска"); + КонецЕсли; + + АдресСервера = ПрочитанныеПараметры.Получить("АдресСервера"); + Токен = ПрочитанныеПараметры.Получить("Токен"); + + Лог.Информация("Получение информации о проектах"); + ПроектыSQ = Сонар.ПолучитьПроекты(АдресСервера, Токен); + КоллекцияОтчетов = СоздатьКоллекциюОтчетов(АдресСервера); + + Лог.Информация("Формирование отчетов по проектам"); + + Проекты = ПрочитанныеПараметры.Получить("Проекты"); + Если Не ЗначениеЗаполнено(Проекты) Тогда + Проекты = ""; + КонецЕсли; + + КоллекцияПроектов = СтрРазделить(Проекты, ",", Ложь); + Замечания = Неопределено; + Для Каждого ОписаниеПроекта Из ПроектыSQ Цикл + + Если КоллекцияПроектов.Количество() И КоллекцияПроектов.Найти(ОписаниеПроекта.Значение.Код) = Неопределено Тогда + Продолжить; + КонецЕсли; + + Лог.Информация(" Формирование отчета для проекта `%1`", ОписаниеПроекта.Значение.Код); + Замечания = Сонар.ПолучитьСводнуюИнформациюПоЗамечаниямиПроекта(АдресСервера, Токен, ОписаниеПроекта.Значение, СтатусыСтрокой, Замечания); + + КонецЦикла; + + КоллекцияОтчетов.Замечания = Замечания; + КоллекцияОтчетов.Количество = КоллекцияОтчетов.Замечания.Количество(); + + Лог.Информация("Сохранение отчетов в файл '%1'", ПутьКОтчету); + СформироватьОтчет(КоллекцияОтчетов); + + Возврат МенеджерКомандПриложения.РезультатыКоманд().Успех; + +КонецФункции + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +Функция ПрочитатьПараметрыЗапуска(ПараметрыКоманды) + + ПрочитанныеПараметры = ОбщегоНазначения.ПрочитатьПараметрыЗапуска(ПараметрыКоманды, Истина); + ОшибокНет = ПрочитанныеПараметры.Получить("ОшибокНет"); + МаксимальноеКоличествоСтатусов = 5; + + Если Не ЗначениеЗаполнено(ПрочитанныеПараметры.Получить("СтатусыСтрокой")) Тогда + СтатусыСтрокой = "OPEN,CONFIRMED,REOPENED"; + Иначе + Статусы = Новый Массив(); + СтатусыВрем = СтрРазделить(ВРЕГ(ПрочитанныеПараметры.Получить("СтатусыСтрокой")), ",", Ложь); + ВсеСтатусы = СтрРазделить("OPEN,CONFIRMED,REOPENED,RESOLVED,CLOSED", ","); + Для Каждого Статус Из СтатусыВрем Цикл + Если ВсеСтатусы.Найти(Статус) <> Неопределено Тогда + Статусы.Добавить(Статус); + Иначе + Лог.Ошибка("Неопознанный статус `%1`", Статус); + КонецЕсли; + КонецЦикла; + Если Статусы.Количество() = 0 Тогда + Лог.Ошибка("Указанный фильтр по статусам не содержит корректных значений"); + ОшибокНет = Ложь; + КонецЕсли; + СтатусыСтрокой = СтрСоединить(Статусы, ","); + КонецЕсли; + + ПутьКОтчету = ПрочитанныеПараметры.Получить("ПутьКОтчету"); + ФорматОтчета = ПрочитанныеПараметры.Получить("ФорматОтчета"); + + Если Не ЗначениеЗаполнено(ФорматОтчета) Тогда + ФорматОтчета = "JSON"; + Иначе + ФорматОтчета = ПолучитьПравильныйФорматОтчета(ФорматОтчета, ОшибокНет); + КонецЕсли; + + Если Не ЗначениеЗаполнено(ПутьКОтчету) Тогда + ПутьКОтчету = ОбъединитьПути(".", "report." + НРег(ФорматОтчета)); + КонецЕсли; + + Возврат ОшибокНет; + +КонецФункции + +Функция ПолучитьПравильныйФорматОтчета(Знач ВыбранныйФорматОтчета, ОшибокНет) + + ВРЕГВыбранныйФорматОтчета = ВРЕГ(ВыбранныйФорматОтчета); + Если ВРЕГВыбранныйФорматОтчета <> "JSON" Тогда + + ОшибокНет = Ложь; + Лог.Ошибка("Указан неизвестный формат отчета `%1`", ОбщегоНазначения.ОбернутьЗначениеДляПечати(ВыбранныйФорматОтчета)); + + КонецЕсли; + + Возврат ВРЕГВыбранныйФорматОтчета; + +КонецФункции + +Функция СоздатьКоллекциюОтчетов(АдресСервера) + + Результат = Новый Структура(); + Результат.Вставить("ДатаФормирования", ТекущаяДата()); + Результат.Вставить("СерверSonarQube", АдресСервера); + Результат.Вставить("Количество", 0); + Результат.Вставить("Замечания", Неопределено); + + Возврат Результат; + +КонецФункции + +Процедура СформироватьОтчет(КоллекцияОтчетов) + + Если ФорматОтчета = "JSON" Тогда + ЗаписьJSON = Новый ЗаписьJSON(); + ЗаписьJSON.ОткрытьФайл(ПутьКОтчету); + КоллекцияОтчетов.Замечания = ТаблицаЗначенийВМассив(КоллекцияОтчетов.Замечания); + ЗаписатьJSON(ЗаписьJSON, КоллекцияОтчетов); + ЗаписьJSON.Закрыть(); + КонецЕсли; + +КонецПроцедуры + +Функция ТаблицаЗначенийВМассив(ТаблицаЗначений, РезультатВФиксированнуюКоллекцию = Ложь) + + МассивРезультат = Новый Массив(); + СтруктураСтрокой = ""; + НоваяСтрока = Неопределено; + + КолонкиСТипомТЧ = Новый Массив; + + Если ТипЗнч(ТаблицаЗначений) = Тип("ТаблицаЗначений") Тогда + + КолонкиТаблицы = ТаблицаЗначений.Колонки; + + ИначеЕсли ТипЗнч(ТаблицаЗначений) = Тип("Массив") И ТипЗнч(ТаблицаЗначений[0]) = Тип("СтрокаТаблицыЗначений") Тогда + + КолонкиТаблицы = ТаблицаЗначений[0].Владелец().Колонки; + + Иначе + + ВызватьИсключение "Не поддерживаемый тип параметра"; + + КонецЕсли; + + Если ТаблицаЗначений.Количество() = 0 Тогда + + Возврат МассивРезультат; + + КонецЕсли; + + Для Каждого Колонка Из КолонкиТаблицы Цикл + + СтруктураСтрокой = СтруктураСтрокой + ?(ПустаяСтрока(СтруктураСтрокой), "", ",") + Колонка.Имя ; + + КонецЦикла; + + Для Каждого Строка Из ТаблицаЗначений Цикл + + НоваяСтрока = Новый Структура(СтруктураСтрокой); + ЗаполнитьЗначенияСвойств(НоваяСтрока, Строка); + + Для Каждого КолонкаСТипомТЧ Из КолонкиСТипомТЧ Цикл + + Если ТипЗнч(Строка[КолонкаСТипомТЧ]) = Тип("ТаблицаЗначений") Тогда + + НоваяСтрока[КолонкаСТипомТЧ] = ТаблицаЗначенийВМассив(Строка[КолонкаСТипомТЧ], РезультатВФиксированнуюКоллекцию); + + КонецЕсли; + + КонецЦикла; + + Если РезультатВФиксированнуюКоллекцию Тогда + + НоваяСтрока = Новый ФиксированнаяСтруктура(НоваяСтрока); + + КонецЕсли; + + МассивРезультат.Добавить(НоваяСтрока); + + КонецЦикла; + + Если РезультатВФиксированнуюКоллекцию Тогда + + МассивРезультат = Новый ФиксированныйМассив(МассивРезультат); + + КонецЕсли; + + Возврат МассивРезультат; + +КонецФункции // ТаблицаЗначенийВМассив diff --git "a/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\236\320\261\321\211\320\265\320\263\320\276\320\235\320\260\320\267\320\275\320\260\321\207\320\265\320\275\320\270\321\217.os" "b/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\236\320\261\321\211\320\265\320\263\320\276\320\235\320\260\320\267\320\275\320\260\321\207\320\265\320\275\320\270\321\217.os" new file mode 100644 index 0000000..78aa74b --- /dev/null +++ "b/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\236\320\261\321\211\320\265\320\263\320\276\320\235\320\260\320\267\320\275\320\260\321\207\320\265\320\275\320\270\321\217.os" @@ -0,0 +1,148 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Служебный модуль с общими методами приложения +// +// (C) Maximov Valery (aka theshadowco) +// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +// ОбернутьЗначениеДляПечати +// Формирует строку из значения для удобной печати +// Параметры: +// Значение - Произвольное - Значение, которое необходимо преобразовать в строку для последующей печати +// +// Возвращаемое значение: +// Строка - Преобразованное в строку входное значение. Если значение не заполнено, то вернется "<Незаполнено>" +// +Функция ОбернутьЗначениеДляПечати(Знач Значение) Экспорт + + Если ЗначениеЗаполнено(Значение) Тогда + Возврат Строка(Значение); + КонецЕсли; + + Возврат "<Незаполнено>"; + +КонецФункции + +// ПрочитатьПараметрыЗапуска +// Читает входные параметры включа конфигурационный файл +// Параметры: +// ПараметрыКоманды - Соответствие - Входные параметры приложения +// ВыполнятьПроверкуАвторизацииСразу - Булево - Выполнение проверки корректности параметров авторизации +// +// Возвращаемое значение: +// Соответствие - Ключи и значения параметров запуска +// +Функция ПрочитатьПараметрыЗапуска(ПараметрыКоманды, ВыполнятьПроверкуАвторизацииСразу) Экспорт + + Лог = Логирование.ПолучитьЛог(ПараметрыСистемы.ИмяЛогаСистемы()); + + ПрочитанныеПараметры = Новый Соответствие(); + ОшибокНет = Истина; + + ВозможныеКлючи = ВозможныеКлючи(); + Для Каждого ВозможныйКлюч Из ВозможныеКлючи Цикл + + ПрочитанныеПараметры.Вставить(ВозможныйКлюч.Ключ, ПараметрыКоманды.Получить(ВозможныйКлюч.Значение)); + + КонецЦикла; + + ПутьККонфигурационномуФайлу = ПараметрыКоманды["--cfg-file"]; + + Если ПутьККонфигурационномуФайлу <> Неопределено Тогда + + Файл = Новый Файл(ПутьККонфигурационномуФайлу); + Если Файл.Существует() Тогда + + ТекстовыйДокумент = Новый ТекстовыйДокумент(); + ТекстовыйДокумент.Прочитать(ПутьККонфигурационномуФайлу); + Лог.Отладка("Содержимое конфигурационного файла: %1%2", Символы.ПС, ТекстовыйДокумент.ПолучитьТекст()); + JSON = Новый ЧтениеJSON(); + JSON.УстановитьСтроку(ТекстовыйДокумент.ПолучитьТекст()); + ПараметрыИзФайла = ПрочитатьJSON(JSON, Истина); + + Для Каждого ВозможныйКлюч Из ВозможныеКлючи Цикл + + ЗначениеИзФайла = ПараметрыИзФайла.Получить(ВозможныйКлюч.Значение); + Если ЗначениеИзФайла <> Неопределено И ПрочитанныеПараметры.Получить(ВозможныйКлюч.Ключ) = Неопределено Тогда + ПрочитанныеПараметры.Вставить(ВозможныйКлюч.Ключ, ЗначениеИзФайла); + КонецЕсли; + + КонецЦикла; + + Иначе + + Лог.Ошибка("Конфигурационный файл по пути `%1` не обнаружен", ОбернутьЗначениеДляПечати(ПутьККонфигурационномуФайлу)); + ОшибокНет = ЛОЖЬ; + + КонецЕсли; + + КонецЕсли; + + Лог.Отладка("Прочитанные параметры:"); + Для Каждого ПрочитанныйПараметр Из ПрочитанныеПараметры Цикл + + Лог.Отладка(" %2 = `%1`", ОбернутьЗначениеДляПечати(ПрочитанныйПараметр.Значение), ПрочитанныйПараметр.Ключ); + + КонецЦикла; + + Если ВыполнятьПроверкуАвторизацииСразу Тогда + + Если Не ЗначениеЗаполнено(ПрочитанныеПараметры.Получить("Токен")) Тогда + + Лог.Ошибка("Не указан токен для авторизации"); + ОшибокНет = Ложь; + + КонецЕсли; + + Если Не ЗначениеЗаполнено(ПрочитанныеПараметры.Получить("АдресСервера")) Тогда + + Лог.Ошибка("Не указан адрес сервера SonarQube"); + ОшибокНет = Ложь; + + КонецЕсли; + + КонецЕсли; + + ПрочитанныеПараметры.Вставить("ОшибокНет", ОшибокНет); + + Возврат ПрочитанныеПараметры; + +КонецФункции + +// СообщитьОбОшибке +// Выводит сообщение об ошибке и возвращает статус ошибочного завершения приложения +// Параметры: +// ТекстОшибки - Строка - Выводимое сообщение об ошибке +// +// Возвращаемое значение: +// Число - Код ошибки выполнения +// +Функция СообщитьОбОшибке(ТекстОшибки) Экспорт + + Лог = Логирование.ПолучитьЛог(ПараметрыСистемы.ИмяЛогаСистемы()); + Лог.Ошибка(ТекстОшибки); + Возврат МенеджерКомандПриложения.РезультатыКоманд().ОшибкаВремениВыполнения; + +КонецФункции + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +Функция ВозможныеКлючи() + + ВозможныеКлючи = Новый Структура(); + ВозможныеКлючи.Вставить("Токен", "--token"); + ВозможныеКлючи.Вставить("АдресСервера", "--server"); + ВозможныеКлючи.Вставить("РодительскийПроект", "--parent-project"); + ВозможныеКлючи.Вставить("ИсключаемыеПроектыСтрокой", "--exclude-projects"); + ВозможныеКлючи.Вставить("СтатусыСтрокой", "--statuses"); + ВозможныеКлючи.Вставить("Теги", "--tags"); + ВозможныеКлючи.Вставить("ИзEDTВКонфигуратор", "--edt2cfg"); + ВозможныеКлючи.Вставить("Проекты", "--projects"); + ВозможныеКлючи.Вставить("Метрики", "--total-metrics"); + ВозможныеКлючи.Вставить("ПутьКОтчету", "--report-path"); + ВозможныеКлючи.Вставить("ФорматОтчета", "--report-format"); + + Возврат ВозможныеКлючи; + +КонецФункции diff --git "a/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\241\320\270\321\201\321\202\320\265\320\274\321\213.os" "b/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\241\320\270\321\201\321\202\320\265\320\274\321\213.os" index d84668d..97bf0d6 100644 --- "a/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\241\320\270\321\201\321\202\320\265\320\274\321\213.os" +++ "b/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\241\320\270\321\201\321\202\320\265\320\274\321\213.os" @@ -50,6 +50,7 @@ ВозможныеКомандыПриложения.Вставить("ПоказатьВерсию", "version"); ВозможныеКомандыПриложения.Вставить("ЗакрытьИшузы", "issue-resolver"); ВозможныеКомандыПриложения.Вставить("СгенерироватьОтчет", "report"); + ВозможныеКомандыПриложения.Вставить("СгенерироватьОтчетПоПользователям", "usr-report"); ВозможныеКомандыПриложения = Новый ФиксированнаяСтруктура(ВозможныеКомандыПриложения); @@ -61,12 +62,11 @@ Процедура ПриРегистрацииКомандПриложения(Знач КлассыРеализацииКоманд) Экспорт - КлассыРеализацииКоманд[ВозможныеКоманды().Помощь] = "КомандаСправкаПоПараметрам"; - КлассыРеализацииКоманд[ИмяКомандыВерсия()] = "КомандаVersion"; - КлассыРеализацииКоманд[ВозможныеКоманды().ЗакрытьИшузы] = "КомандаЗакрытьИшузы"; - КлассыРеализацииКоманд[ВозможныеКоманды().СгенерироватьОтчет] = "КомандаСгенерироватьОтчет"; - //... - //КлассыРеализацииКоманд["<имя команды>"] = "<КлассРеализации>"; + КлассыРеализацииКоманд[ВозможныеКоманды().Помощь] = "КомандаСправкаПоПараметрам"; + КлассыРеализацииКоманд[ИмяКомандыВерсия()] = "КомандаVersion"; + КлассыРеализацииКоманд[ВозможныеКоманды().ЗакрытьИшузы] = "КомандаЗакрытьИшузы"; + КлассыРеализацииКоманд[ВозможныеКоманды().СгенерироватьОтчет] = "КомандаСгенерироватьОтчет"; + КлассыРеализацииКоманд[ВозможныеКоманды().СгенерироватьОтчетПоПользователям] = "КомандаСгенерироватьОтчетПоПользователям"; КонецПроцедуры // ПриРегистрацииКомандПриложения diff --git "a/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\241\320\276\320\275\320\260\321\200.os" "b/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\241\320\276\320\275\320\260\321\200.os" index fee2007..25a87ea 100644 --- "a/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\241\320\276\320\275\320\260\321\200.os" +++ "b/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\241\320\276\320\275\320\260\321\200.os" @@ -176,6 +176,127 @@ КонецФункции +// ПолучитьСводнуюИнформациюПоЗамечаниямиПроекта +// Возвращает свернутую таблицу с пользователями и их замечаниями на проекте проекта +// Параметры: +// АдресСервера - Строка - Адрес (хост) сервера SonarQube +// Токен - Строка - Токен пользователя, от имени которого выполняются запросы к API +// ОписаниеПроекта - Структура - Описание проекта SonarQube +// * Идентификатор - Строка - Идентификатор проекта +// * Код - Строка - Код (Ключ) проекта +// СтатусыСтрокой - Строка - Строка с идентификаторами статусов замечаний +// Замечания - ТаблицаЗначений - Таблица со сводной информацией замечаний, аналогичная выходной. Используется при необходимости дополнять для нескольких проектов +// +// Возвращаемое значение: +// ТаблицаЗначения - Содвная таблица с замечаниями +// * КлючПроекта - Строка - Ключ проекта sonarqube +// * Статус - Строка - Статус замечания +// * Автор - Строка - Пользователь, автор замечания по git (email) +// * Отвественный - Строка, NULL - Пользователь, на которого назначено замечание (учетная запись) +// * Правило - Строка - Ключ правила +// * Критичность - Строка - Критичность замечания +// * Тип - Строка - Тип замечания +// * Время - Число - Время на исправление замечания (в минутах) +// * Количество - Число - Количество замечаний +// +Функция ПолучитьСводнуюИнформациюПоЗамечаниямиПроекта(АдресСервера, Токен, ОписаниеПроекта, СтатусыСтрокой, Замечания = Неопределено) Экспорт + + КлючиКомпонентов = ПолучитьКлючиКомпонентов(АдресСервера, Токен, ОписаниеПроекта.Код, СтатусыСтрокой); + URLШаблон = "issues/search?ps=500&componentKeys=%1%2"; + Если Не ПустаяСтрока(СтатусыСтрокой) Тогда + URLШаблон = URLШаблон + "&statuses=" + СтатусыСтрокой; + КонецЕсли; + URLШаблон = URLШаблон + "&p="; + + КоличествоКомпонентов = КлючиКомпонентов.Количество(); + + Если Замечания = Неопределено Тогда + Замечания = Новый ТаблицаЗначений(); + Замечания.Колонки.Добавить("КлючПроекта"); + Замечания.Колонки.Добавить("Статус"); + Замечания.Колонки.Добавить("Автор"); + Замечания.Колонки.Добавить("Отвественный"); + Замечания.Колонки.Добавить("Правило"); + Замечания.Колонки.Добавить("Критичность"); + Замечания.Колонки.Добавить("Тип"); + Замечания.Колонки.Добавить("Время"); + Замечания.Колонки.Добавить("Количество"); + КонецЕсли; + + Для Ит = 0 По КоличествоКомпонентов - 1 Цикл + КлючКомпонента = КлючиКомпонентов[Ит]; + + НомерСтраницы = 1; + URL = СтрШаблон(URLШаблон, КлючКомпонента, ""); + Пока Истина Цикл + + Ответ = ВыполнитьЗапрос(АдресСервера, Токен, URL + Формат(НомерСтраницы, "ЧГ="), "GET"); + Если Ответ.total > МаксимальныйРазмерПорцииДанных Тогда + // прочитаем компоненты, перенесем их в общий список, а по текущем прочтем только те, что висят именно на нем + КлючиДочернихКомпонентов = ПолучитьКлючиКомпонентов(АдресСервера, Токен, КлючКомпонента, СтатусыСтрокой, Ложь); + Для Каждого КлючДочернегоКомпонента Из КлючиДочернихКомпонентов Цикл + КлючиКомпонентов.Добавить(КлючДочернегоКомпонента); + КонецЦикла; + КоличествоКомпонентов = КлючиКомпонентов.Количество(); + URL = СтрШаблон(URLШаблон, КлючКомпонента, "&onComponentOnly=true"); + Ответ = ВыполнитьЗапрос(АдресСервера, Токен, URL + Формат(НомерСтраницы, "ЧГ="), "GET"); + КонецЕсли; + + Для Каждого ОписаниеОшибки Из Ответ.issues Цикл + + Замечание = Замечания.Добавить(); + Замечание.КлючПроекта = ОписаниеПроекта.Код; + Замечание.Автор = ОписаниеОшибки.author; + Если Не ОписаниеОшибки.Свойство("assignee") Тогда + Замечание.Отвественный = NULL; + Иначе + Замечание.Отвественный = ОписаниеОшибки.assignee; + КонецЕсли; + + Замечание.Правило = ОписаниеОшибки.rule; + Замечание.Статус = ОписаниеОшибки.status; + Замечание.Критичность = ОписаниеОшибки.severity; + Замечание.Тип = ОписаниеОшибки.type; + Замечание.Количество = 1; + Замечание.Время = 0; + + Если ОписаниеОшибки.Свойство("debt") Тогда + ВремяСтрокой = СтрЗаменить(ОписаниеОшибки.debt, "min", ""); + Поз = СтрНайти(ВремяСтрокой, "h"); + Если Поз <> СтрДлина(ВремяСтрокой) Тогда + ВремяСтрокой = СтрЗаменить(ВремяСтрокой, "h", "*60+"); + Иначе + ВремяСтрокой = СтрЗаменить(ВремяСтрокой, "h", "*60"); + КонецЕсли; + + Поз = СтрНайти(ВремяСтрокой, "d"); + Если Поз <> СтрДлина(ВремяСтрокой) Тогда + ВремяСтрокой = СтрЗаменить(ВремяСтрокой, "d", "*480+"); // рабочие дни + Иначе + ВремяСтрокой = СтрЗаменить(ВремяСтрокой, "d", "*480"); + КонецЕсли; + + Замечание.Время = Вычислить(ВремяСтрокой); + + КонецЕсли; + КонецЦикла; + + Если БольшеНетДанных(Ответ) Тогда + Прервать; + КонецЕсли; + + НомерСтраницы = НомерСтраницы + 1; + + КонецЦикла; + + КонецЦикла; + + Замечания.Свернуть("КлючПроекта, Автор, Отвественный, Правило, Критичность, Тип, Статус", "Время, Количество"); + + Возврат Замечания; + +КонецФункции + // ЗаполнитьКоличествоОдинаковыхЗамечанийПроекта // По сформированному набору замечаний дополняет общее количество замечаний со всеми статусами с тем же хэшем // Параметры: @@ -474,7 +595,6 @@ HTTPЗапрос = Новый HTTPЗапрос; HTTPЗапрос.АдресРесурса = Префикс + "api/" + URL; - Сообщить(HTTPЗапрос.АдресРесурса); HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/json"); HTTP = Новый HTTPСоединение(АдресСервера, , Токен); @@ -485,6 +605,7 @@ КонецЕсли; Если ОтветHTTP.КодСостояния = 200 Тогда + // Сообщить(ОтветHTTP.ПолучитьТелоКакСтроку()); json = Новый ЧтениеJSON(); json.УстановитьСтроку(ОтветHTTP.ПолучитьТелоКакСтроку()); Возврат ПрочитатьJson(json); From 4cc32aa4fd5a9d469e3bbcbcae5c075d618d4d4d Mon Sep 17 00:00:00 2001 From: Maximov Valery Date: Thu, 25 Jul 2019 10:55:54 +0300 Subject: [PATCH 2/3] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4=D0=B0=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BD=D0=BE=D1=81?= =?UTF-8?q?=D0=B0=20=D0=B7=D0=B0=D0=BC=D0=B5=D1=87=D0=B0=D0=BD=D0=B8=D1=8F?= =?UTF-8?q?=20=D0=BD=D0=B0=20=D1=83=D0=BA=D0=B0=D0=B7=D0=B0=D0=BD=D0=BD?= =?UTF-8?q?=D0=BE=D0=B3=D0=BE=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D1=82=D0=B5=D0=BB=D1=8F=20=D0=BF=D0=BE=20=D0=BD=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=80=D1=83=20=D1=84=D0=B8=D0=BB=D1=8C=D1=82=D1=80?= =?UTF-8?q?=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/example-transfer.cfg-file.json | 8 + ...20\230\321\210\321\203\320\267\321\213.os" | 167 ++++++++++++++++++ ...21\207\320\265\320\275\320\270\321\217.os" | 2 + ...21\201\321\202\320\265\320\274\321\213.os" | 2 + ...20\241\320\276\320\275\320\260\321\200.os" | 112 +++++++++++- 5 files changed, 286 insertions(+), 5 deletions(-) create mode 100644 examples/example-transfer.cfg-file.json create mode 100644 "src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\274\320\260\320\275\320\264\320\260\320\237\320\265\321\200\320\265\320\275\320\265\321\201\321\202\320\270\320\230\321\210\321\203\320\267\321\213.os" diff --git a/examples/example-transfer.cfg-file.json b/examples/example-transfer.cfg-file.json new file mode 100644 index 0000000..ac8a67a --- /dev/null +++ b/examples/example-transfer.cfg-file.json @@ -0,0 +1,8 @@ +{ + "--token": "2ef785ca946170b7086668132175eb01386943a1", + "--server": "http://my.sonar.server:9000", + "--projects": "bsp,mdm", + "--statuses": "OPEN", + "--assigned": "notme,false", + "--assign": "me" +} diff --git "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\274\320\260\320\275\320\264\320\260\320\237\320\265\321\200\320\265\320\275\320\265\321\201\321\202\320\270\320\230\321\210\321\203\320\267\321\213.os" "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\274\320\260\320\275\320\264\320\260\320\237\320\265\321\200\320\265\320\275\320\265\321\201\321\202\320\270\320\230\321\210\321\203\320\267\321\213.os" new file mode 100644 index 0000000..1ad6675 --- /dev/null +++ "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\274\320\260\320\275\320\264\320\260\320\237\320\265\321\200\320\265\320\275\320\265\321\201\321\202\320\270\320\230\321\210\321\203\320\267\321\213.os" @@ -0,0 +1,167 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Служебный модуль с реализацией работы команды transfer-issues +// +// (C) Maximov Valery (aka theshadowco) +// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +Перем Лог; + +Перем Проекты; +Перем ИсключаемыеПроекты; +Перем Статусы; +Перем МаксимальноеКоличествоСтатусов; + +Перем ПрочитанныеПараметры; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Прикладной интерфейс +/////////////////////////////////////////////////////////////////////////////////////////////////// + +Процедура ЗарегистрироватьКоманду(Знач ИмяКоманды, Знач Парсер) Экспорт + + ОписаниеКоманды = Парсер.ОписаниеКоманды(ИмяКоманды, "Выполняет перенос замечаний на указанного пользователя"); + Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, "--token", "Токен для авторизации на сервере SonarQube"); + Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, "--server", "Адрес сервера SonarQube (например http://my.sonar.server:9000)"); + Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, "--assigned", "Идентификаторы пользователей, с которых необходимо перенести замечания (список через запятую). + |Для выбора 'Не назначено' используйте значение 'false'"); + Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, " - -assign", "Идентификатор пользователя, на которого необходимо назначить замечания. + |Для выбора 'Не назначено' используйте значение 'false'"); + Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, "--projects", "Ключи проектов, строкой через запятую (например hrm:develop,buh:master). + | Для того, чтобы указать все проекты, необходимо передать #all"); + Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, "--exclude-projects", "Ключи проектов, которые необходимо исключить из обработки"); + Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, "--statuses", "Отбор замечаний только соответствующих статусов. + | Необходимые статусы необходимо передавать строкой через запятую. + | Возможные значения: + | OPEN - открытые + | CONFIRMED - подтвержденные: + | REOPENED - переоткрытые + | RESOLVED - решенные + | CLOSED - закрытые + | + | По умолчанию выбираются только RESOLVED и CLOSED"); + Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, "--cfg-file", "Путь к конфигурационному файлу в формате json (utf-8), свойства объекта соответствуют ключам запуска. + | Пример конфигурационного файла находится в каталоге examples"); + + Парсер.ДобавитьКоманду(ОписаниеКоманды); + +КонецПроцедуры + +// Выполняет логику команды +// +// Параметры: +// ПараметрыКоманды - Соответствие - Соответствие ключей командной строки и их значений +// ДополнительныеПараметры - Соответствие - (необязательно) дополнительные параметры +// +Функция ВыполнитьКоманду(Знач ПараметрыКоманды, Знач ДополнительныеПараметры = Неопределено) Экспорт + + Лог = ДополнительныеПараметры.Лог; + ОшибокНет = ПрочитатьПараметрыЗапуска(ПараметрыКоманды); + Если НЕ ОшибокНет Тогда + Возврат ОбщегоНазначения.СообщитьОбОшибке("Ошибка запуска"); + КонецЕсли; + + АдресСервера = ПрочитанныеПараметры.Получить("АдресСервера"); + Токен = ПрочитанныеПараметры.Получить("Токен"); + РодительскийПроект = ПрочитанныеПараметры.Получить("РодительскийПроект"); + + Лог.Информация("Получение информации о проектах"); + ПроектыSQ = Сонар.ПолучитьПроекты(АдресСервера, Токен); + + Если Проекты.Найти("#all") <> Неопределено Тогда + Проекты.Очистить(); + Для Каждого ОписаниеПроекта Из ПроектыSQ Цикл + Если ОписаниеПроекта.Ключ <> РодительскийПроект Тогда + Проекты.Добавить(ОписаниеПроекта.Ключ); + КонецЕсли; + КонецЦикла; + КонецЕсли; + + Для Каждого ИсключаемыйПроект Из ИсключаемыеПроекты Цикл + Позиция = Проекты.Найти(СокрЛП(ИсключаемыйПроект)); + Если Позиция <> Неопределено Тогда + Проекты.Удалить(Позиция); + КонецЕсли; + КонецЦикла; + + ПользователиСтрокой = ПрочитанныеПараметры.Получить("Пользователи"); + Если ЗначениеЗаполнено(ПользователиСтрокой) Тогда + Пользователи = СтрРазделить(ПользователиСтрокой, ",", Ложь); + ПользовательВСписке = Пользователи.Найти(ПрочитанныеПараметры.Получить("НазначитьНа")); + Если ПользовательВСписке <> Неопределено Тогда + Пользователи.Удалить(ПользовательВСписке); + КонецЕсли; + + Если Пользователи.Количество() Тогда + ПользователиСтрокой = СтрСоединить(Пользователи, ","); + Иначе + ПользователиСтрокой = ""; + КонецЕсли; + КонецЕсли; + + Лог.Информация("Чтение замечаний по фильтру"); + + Фильтр = Новый Структура(); + Фильтр.Вставить("Пользователи", ПользователиСтрокой); + Фильтр.Вставить("Статусы", СтрСоединить(Статусы, ",")); + НайденныеЗамечания = Сонар.ПолучитьЗамечанияПоФильтру(АдресСервера, Токен, Проекты, Фильтр); + Если НайденныеЗамечания.Количество() Тогда + Лог.Информация("Найдено `%1` замечаний", НайденныеЗамечания.Количество()); + Лог.Информация("Перенос найденных замечаний на `%1`", ПрочитанныеПараметры.Получить("НазначитьНа")); + Сонар.НазначитьЗамечания(АдресСервера, Токен, НайденныеЗамечания, ПрочитанныеПараметры.Получить("НазначитьНа")); + Иначе + Лог.Информация("Подходящих замечаний не обнаружено"); + КонецЕсли; + + Возврат МенеджерКомандПриложения.РезультатыКоманд().Успех; + +КонецФункции + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +Функция ПрочитатьПараметрыЗапуска(ПараметрыКоманды) + + ПрочитанныеПараметры = ОбщегоНазначения.ПрочитатьПараметрыЗапуска(ПараметрыКоманды, Истина); + ОшибокНет = ПрочитанныеПараметры.Получить("ОшибокНет"); + МаксимальноеКоличествоСтатусов = 5; + + Если Не ЗначениеЗаполнено(ПрочитанныеПараметры.Получить("НазначитьНа")) Тогда + Лог.Ошибка("Не указан идентификатор пользователя, на которого необходимо перенести замечания"); + ОшибокНет = Ложь; + КонецЕсли; + + ИсключаемыеПроекты = Новый Массив(); + Если ЗначениеЗаполнено(ПрочитанныеПараметры.Получить("ИсключаемыеПроектыСтрокой")) Тогда + ИсключаемыеПроекты = СтрРазделить(ПрочитанныеПараметры.Получить("ИсключаемыеПроектыСтрокой"), ", ", Ложь); + КонецЕсли; + + Проекты = Новый Массив(); + Если ЗначениеЗаполнено(ПрочитанныеПараметры.Получить("Проекты")) Тогда + Проекты = СтрРазделить(ПрочитанныеПараметры.Получить("Проекты"), ", ", Ложь); + Иначе + Проекты.Добавить("#all"); + КонецЕсли; + + Если Не ЗначениеЗаполнено(ПрочитанныеПараметры.Получить("СтатусыСтрокой")) Тогда + Статусы = СтрРазделить("OPEN,CONFIRMED,REOPENED", ",", Ложь); + Иначе + Статусы = Новый Массив(); + СтатусыВрем = СтрРазделить(ВРЕГ(ПрочитанныеПараметры.Получить("СтатусыСтрокой")), ", ", Ложь); + ВсеСтатусы = СтрРазделить("OPEN,CONFIRMED,REOPENED,RESOLVED,CLOSED", ","); + Для Каждого Статус Из СтатусыВрем Цикл + Если ВсеСтатусы.Найти(Статус) <> Неопределено Тогда + Статусы.Добавить(Статус); + Иначе + Лог.Ошибка("Неопознанный статус `%1`", Статус); + КонецЕсли; + КонецЦикла; + Если Статусы.Количество() = 0 Тогда + Лог.Ошибка("Указанный фильтр по статусам не содержит корректных значений"); + ОшибокНет = Ложь; + КонецЕсли; + КонецЕсли; + + Возврат ОшибокНет; + +КонецФункции diff --git "a/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\236\320\261\321\211\320\265\320\263\320\276\320\235\320\260\320\267\320\275\320\260\321\207\320\265\320\275\320\270\321\217.os" "b/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\236\320\261\321\211\320\265\320\263\320\276\320\235\320\260\320\267\320\275\320\260\321\207\320\265\320\275\320\270\321\217.os" index 78aa74b..0ee87a3 100644 --- "a/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\236\320\261\321\211\320\265\320\263\320\276\320\235\320\260\320\267\320\275\320\260\321\207\320\265\320\275\320\270\321\217.os" +++ "b/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\236\320\261\321\211\320\265\320\263\320\276\320\235\320\260\320\267\320\275\320\260\321\207\320\265\320\275\320\270\321\217.os" @@ -142,6 +142,8 @@ ВозможныеКлючи.Вставить("Метрики", "--total-metrics"); ВозможныеКлючи.Вставить("ПутьКОтчету", "--report-path"); ВозможныеКлючи.Вставить("ФорматОтчета", "--report-format"); + ВозможныеКлючи.Вставить("Пользователи", "--assigned"); + ВозможныеКлючи.Вставить("НазначитьНа", "--assign"); Возврат ВозможныеКлючи; diff --git "a/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\241\320\270\321\201\321\202\320\265\320\274\321\213.os" "b/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\241\320\270\321\201\321\202\320\265\320\274\321\213.os" index 97bf0d6..560408d 100644 --- "a/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\241\320\270\321\201\321\202\320\265\320\274\321\213.os" +++ "b/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\241\320\270\321\201\321\202\320\265\320\274\321\213.os" @@ -51,6 +51,7 @@ ВозможныеКомандыПриложения.Вставить("ЗакрытьИшузы", "issue-resolver"); ВозможныеКомандыПриложения.Вставить("СгенерироватьОтчет", "report"); ВозможныеКомандыПриложения.Вставить("СгенерироватьОтчетПоПользователям", "usr-report"); + ВозможныеКомандыПриложения.Вставить("ПеренестиИшузы", "transfer-issues"); ВозможныеКомандыПриложения = Новый ФиксированнаяСтруктура(ВозможныеКомандыПриложения); @@ -67,6 +68,7 @@ КлассыРеализацииКоманд[ВозможныеКоманды().ЗакрытьИшузы] = "КомандаЗакрытьИшузы"; КлассыРеализацииКоманд[ВозможныеКоманды().СгенерироватьОтчет] = "КомандаСгенерироватьОтчет"; КлассыРеализацииКоманд[ВозможныеКоманды().СгенерироватьОтчетПоПользователям] = "КомандаСгенерироватьОтчетПоПользователям"; + КлассыРеализацииКоманд[ВозможныеКоманды().ПеренестиИшузы] = "КомандаПеренестиИшузы"; КонецПроцедуры // ПриРегистрацииКомандПриложения diff --git "a/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\241\320\276\320\275\320\260\321\200.os" "b/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\241\320\276\320\275\320\260\321\200.os" index 25a87ea..ed6de1c 100644 --- "a/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\241\320\276\320\275\320\260\321\200.os" +++ "b/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\241\320\276\320\275\320\260\321\200.os" @@ -106,7 +106,7 @@ Ответ = ВыполнитьЗапрос(АдресСервера, Токен, URL + Формат(НомерСтраницы, "ЧГ="), "GET"); Если Ответ.total > МаксимальныйРазмерПорцииДанных Тогда // прочитаем компоненты, перенесем их в общий список, а по текущем прочтем только те, что висят именно на нем - КлючиДочернихКомпонентов = ПолучитьКлючиКомпонентов(АдресСервера, Токен, КлючКомпонента, СтатусыСтрокой, Ложь); + КлючиДочернихКомпонентов = ПолучитьКлючиКомпонентов(АдресСервера, Токен, КлючКомпонента, СтатусыСтрокой, "", Ложь); Для Каждого КлючДочернегоКомпонента Из КлючиДочернихКомпонентов Цикл КлючиКомпонентов.Добавить(КлючДочернегоКомпонента); КонецЦикла; @@ -176,6 +176,72 @@ КонецФункции +// ПолучитьЗамечанияПоФильтру +// Получает массив идентификаторов замечаний по фильтру +// Параметры: +// АдресСервера - Строка - Адрес (хост) сервера SonarQube +// Токен - Строка - Токен пользователя, от имени которого выполняются запросы к API +// Проекты - Массив - Идентификаторы проектов для анализа +// Фильтр - Структура - Фильтры для отбора замечаний +// * Статусы - Строка - Статусы замечаний +// * Пользователи - Строка - Идентификаторы пользователей, на которых назначены замечания +// +// Возвращаемое значение: +// Массив - Идентификаторы замечаний +// +Функция ПолучитьЗамечанияПоФильтру(АдресСервера, Токен, Проекты, Фильтр) Экспорт + + Замечания = Новый Массив(); + URLШаблон = "issues/search?ps=500&componentKeys=%1%2"; + Если Не ПустаяСтрока(Фильтр.Статусы) Тогда + URLШаблон = URLШаблон + "&statuses=" + Фильтр.Статусы; + КонецЕсли; + Если Не ПустаяСтрока(Фильтр.Пользователи) Тогда + URLШаблон = URLШаблон + "&assignees=" + Фильтр.Пользователи; + КонецЕсли; + URLШаблон = URLШаблон + "&p="; + + Для Каждого Проект Из Проекты Цикл + + КлючиКомпонентов = ПолучитьКлючиКомпонентов(АдресСервера, Токен, Проект, Фильтр.Статусы, Фильтр.Пользователи); + КоличествоКомпонентов = КлючиКомпонентов.Количество(); + Для Ит = 0 По КоличествоКомпонентов - 1 Цикл + КлючКомпонента = КлючиКомпонентов[Ит]; + НомерСтраницы = 1; + URL = СтрШаблон(URLШаблон, КлючКомпонента, ""); + Пока Истина Цикл + Ответ = ВыполнитьЗапрос(АдресСервера, Токен, URL + Формат(НомерСтраницы, "ЧГ="), "GET"); + Если Ответ.total > МаксимальныйРазмерПорцииДанных Тогда + // прочитаем компоненты, перенесем их в общий список, а по текущем прочтем только те, что висят именно на нем + КлючиДочернихКомпонентов = ПолучитьКлючиКомпонентов(АдресСервера, Токен, КлючКомпонента, Фильтр.Статусы, Фильтр.Пользователи, Ложь); + Для Каждого КлючДочернегоКомпонента Из КлючиДочернихКомпонентов Цикл + КлючиКомпонентов.Добавить(КлючДочернегоКомпонента); + КонецЦикла; + КоличествоКомпонентов = КлючиКомпонентов.Количество(); + URL = СтрШаблон(URLШаблон, КлючКомпонента, "&onComponentOnly=true"); + Ответ = ВыполнитьЗапрос(АдресСервера, Токен, URL + Формат(НомерСтраницы, "ЧГ="), "GET"); + КонецЕсли; + + Для Каждого ОписаниеОшибки Из Ответ.issues Цикл + Замечания.Добавить(ОписаниеОшибки.key); + КонецЦикла; + + Если БольшеНетДанных(Ответ) Тогда + Прервать; + КонецЕсли; + + НомерСтраницы = НомерСтраницы + 1; + + КонецЦикла; + + КонецЦикла; + + КонецЦикла; + + Возврат Замечания; + +КонецФункции + // ПолучитьСводнуюИнформациюПоЗамечаниямиПроекта // Возвращает свернутую таблицу с пользователями и их замечаниями на проекте проекта // Параметры: @@ -233,7 +299,7 @@ Ответ = ВыполнитьЗапрос(АдресСервера, Токен, URL + Формат(НомерСтраницы, "ЧГ="), "GET"); Если Ответ.total > МаксимальныйРазмерПорцииДанных Тогда // прочитаем компоненты, перенесем их в общий список, а по текущем прочтем только те, что висят именно на нем - КлючиДочернихКомпонентов = ПолучитьКлючиКомпонентов(АдресСервера, Токен, КлючКомпонента, СтатусыСтрокой, Ложь); + КлючиДочернихКомпонентов = ПолучитьКлючиКомпонентов(АдресСервера, Токен, КлючКомпонента, СтатусыСтрокой, "", Ложь); Для Каждого КлючДочернегоКомпонента Из КлючиДочернихКомпонентов Цикл КлючиКомпонентов.Добавить(КлючДочернегоКомпонента); КонецЦикла; @@ -459,6 +525,39 @@ КонецПроцедуры +// НазначитьЗамечания +// Выполняет назначение замечаний на указанного пользователя +// Параметры: +// АдресСервера - Строка - Адрес (хост) сервера SonarQube +// Токен - Строка - Токен пользователя, от имени которого выполняются запросы к API +// Замечания - Массив - Ключи замечаний для переназначения +// НазначитьНа - Строка - Идентификатор пользователя, на которого необходимо назначить замечание +// +Процедура НазначитьЗамечания(АдресСервера, Токен, Замечания, НазначитьНа) Экспорт + + URL = СтрШаблон("issues/bulk_change?assign=%1&issues=", НазначитьНа); + + ЗамечанияДляОбработки = Новый Массив(); + Порция = 500; + Для Каждого Замечание Из Замечания Цикл + + Если ЗамечанияДляОбработки.Количество() = Порция Тогда + КлючиЗамечаний = СтрСоединить(ЗамечанияДляОбработки, ","); + ВыполнитьЗапрос(АдресСервера, Токен, URL + КлючиЗамечаний, "POST"); + ЗамечанияДляОбработки.Очистить(); + КонецЕсли; + + ЗамечанияДляОбработки.Добавить(Замечание); + + КонецЦикла; + + Если ЗамечанияДляОбработки.Количество() Тогда + КлючиЗамечаний = СтрСоединить(ЗамечанияДляОбработки, ","); + ВыполнитьЗапрос(АдресСервера, Токен, URL + КлючиЗамечаний, "POST"); + КонецЕсли; + +КонецПроцедуры + // ПолучитьМетрикиПроекта // Возвращает набор метрик проекта // Параметры: @@ -621,22 +720,25 @@ /////////////////////////////////////////////////////////////////// -Функция ПолучитьКоличествоЗамечанийКомпонента(АдресСервера, Токен, КлючКомпонента, СтатусыСтрокой = "") +Функция ПолучитьКоличествоЗамечанийКомпонента(АдресСервера, Токен, КлючКомпонента, СтатусыСтрокой = "", НазначеныНа = "") URL = "issues/search?ps=1&componentKeys=" + КлючКомпонента; Если Не ПустаяСтрока(СтатусыСтрокой) Тогда URL = URL + "&statuses=" + СтатусыСтрокой; КонецЕсли; + Если Не ПустаяСтрока(НазначеныНа) Тогда + URL = URL + "&assignees=" + НазначеныНа; + КонецЕсли; ОтветСервера = ВыполнитьЗапрос(АдресСервера, Токен, URL, "GET"); Возврат ОтветСервера.total; КонецФункции -Функция ПолучитьКлючиКомпонентов(АдресСервера, Токен, КлючКомпонента, СтатусыСтрокой, ТолькоКаталоги = Истина) +Функция ПолучитьКлючиКомпонентов(АдресСервера, Токен, КлючКомпонента, СтатусыСтрокой, НазначеныНа = "", ТолькоКаталоги = Истина) КлючиКомпонент = Новый СписокЗначений(); - КоличествоЗамечанийПроекта = ПолучитьКоличествоЗамечанийКомпонента(АдресСервера, Токен, КлючКомпонента, СтатусыСтрокой); + КоличествоЗамечанийПроекта = ПолучитьКоличествоЗамечанийКомпонента(АдресСервера, Токен, КлючКомпонента, СтатусыСтрокой, НазначеныНа); Если КоличествоЗамечанийПроекта > МаксимальныйРазмерПорцииДанных Тогда URLШаблон = СтрШаблон("components/tree?component=%1%2&ps=500&p=", КлючКомпонента, ?(ТолькоКаталоги, "&qualifiers=DIR", "")); From b65131e765611d764213add4132ffba8d88272d9 Mon Sep 17 00:00:00 2001 From: Maximov Valery Date: Thu, 25 Jul 2019 11:05:37 +0300 Subject: [PATCH 3/3] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D0=B2=D1=8B=D0=BF=D1=83=D1=81=D0=BA=D0=B0?= =?UTF-8?q?=20=D0=BD=D0=BE=D0=B2=D0=BE=D0=B9=20=D0=B2=D0=B5=D1=80=D1=81?= =?UTF-8?q?=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +++++ ...0\241\320\270\321\201\321\202\320\265\320\274\321\213.os" | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 92b97c0..7c3d39b 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,11 @@ SonarQube может создавать несколько замечаний п На данный момент поддерживается вариант формирования отчета в формате **JSON**. [Пример отчета: examples\example-usr-report.json](examples/example-usr-report.json) +### Перенос замечаний + +При необходимости перенести замечания с одного разработчика на другого (первый например покинул команду) используют возможности массового изменения замечаний. В случае когда замечаний много, много проектов это может отнять большое количество сил т.к. изменять можно порциаями по 500. +**`sonar-helper`** предоставляет возможность выполнить это быстро и просто, для чего необходимо вызвать команду **`transfer-issues`**. + ## Примеры Примеры отчетов и конфигурационных файлов находятся в каталоге [examples](examples) diff --git "a/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\241\320\270\321\201\321\202\320\265\320\274\321\213.os" "b/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\241\320\270\321\201\321\202\320\265\320\274\321\213.os" index 560408d..0db7f94 100644 --- "a/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\241\320\270\321\201\321\202\320\265\320\274\321\213.os" +++ "b/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\241\320\270\321\201\321\202\320\265\320\274\321\213.os" @@ -27,7 +27,7 @@ // Функция ВерсияПродукта() Экспорт - Версия = "1.2.1"; + Версия = "1.3"; Возврат Версия; КонецФункции // ВерсияПродукта()