SIL — это язык программирования для автоматизации работы в Atlassian Jira и Confluence. Вы можете почитать больше про SIL вот здесь.
Я часто работаю со скриптами, написанными на SIL, и хотел бы поделиться с Вами моими мыслями по тому, как сделать код SIL «чистым».
В этой статье я сначала сформулирую правила, которыми я руководствуюсь при написании кода на SIL, а затем приведу пример кода и сделаю рефакторинг, используя эти правила.
Теперь рассмотрим пример кода на SIL.
Посмотрев на код достаточно сложно быстро определить, что делает код. Комментарии в коде тоже не очень помогают. Давайте попробуем изменить код в соответствии с правилами выше.
Сначала посмотрим на структуры. Вот один из примеров:
Мы видим, что «storage storage», выглядит непонятным. Было бы понятнее, если бы код был вот таким:
Теперь видно, что мы определяем поле в структуре под именем storage типа Storage. Для того, чтобы мы могли написать такой код, нам необходимо сделать первую букву каждой структуры заглавной:
Теперь давайте переименуем структуры таким образом, чтобы из названия структуры было бы понятно для чего она нужна, и нам не пришлось бы добавлять комментарии:
По моему мнению такие наименования не требуют дополнительных комментариев для структур. Также мы объявим структуры в самом начале кода.
Мы использовали Правила 1, 5, 6.
Теперь посмотрим на код после объявления структур:
Посмотрев на код мы не можем быстро понять, что происходит в коде. Давайте сначала попробуем разбить код на логические блоки:
То есть сначала мы получаем содержимое новой страницы в Confluence, потом создаем http запрос, создаем страницу в Confluence и пишем комментарий в Jira запросе, что страница в Confluence создана. Теперь давайте реализуем этим функции:
Мы вынесли логические блоки в функции (Правило 2). Теперь мы можем просто написать код вот таким образом:
Код у нас содержит четыре логических блока. Сначала мы получаем json новой страницы, потом создаем страницу в Confluence, добавляем комментарий к запросу и возвращаем статус создания страницы в Confluence. Эти четыре строчки дают нам верхнеуровневое представление о том, что делает скрипт. Если мы хотим понять содержимое страницы в Confluence, то мы всегда можем перейти в getNewConfluencePageContent и посмотреть содержимое этой функции. Эта функция содержит только код для создания содержимого страницы и ничего лишнего, таким образом, изучая код функции, мы не будем отвлекаться на другую функциональность.
Мы можем предположить, что нам нужно будет создавать страницы в Confluence не только из одного скрипта. Поэтому давайте создадим файл confluence_helper.incl и вынесем все необходимые функции для создания страницы в Confluence в этот файл:
Я сделал функцию createConfluencePage более общий, добавив в нее еще один параметр confluenceUrl. Таким образом мы выполнили Правило 4.
Теперь наш основной скрипт будет выглядеть вот таким образом:
В первой строке я включил файл confluence_helper.incl, чтобы использовать из него функцию создания страниц в Confluence.
Я сохранил адрес Confluence в константной переменной и изменил имена переменных в соответствии с Google Java notation style (Правило 7).
Мне кажется, что на этом можно остановиться с рефакторингом. В результате рефакторинга мы получили переиспользуемую функцию создания страниц в Confluence, и наш код стал более читабельным и поддерживаемым.
Я часто работаю со скриптами, написанными на SIL, и хотел бы поделиться с Вами моими мыслями по тому, как сделать код SIL «чистым».
В этой статье я сначала сформулирую правила, которыми я руководствуюсь при написании кода на SIL, а затем приведу пример кода и сделаю рефакторинг, используя эти правила.
Правила
- Имена структур начинайте с заглавной буквы.
- Добавляйте элементы массивов, используя метод AddElement.
- Используйте пользовательские функции(user defined routines), чтобы разбить Ваш код на логические блоки и избавиться от комментариев в коде.
- Выносите переиспользуемый код в библиотеки(inclusions).
- Объявляйте структуры перед всем остальным объявлением переменных.
- Давайте значимые имена ст��уктурам и переменным, в этом случае Вам не придется добавлять дополнительные комментарии к структурами и переменным.
- Именуйте переменные и функции в соответствии с Google Java style guide.
Теперь рассмотрим пример кода на SIL.
string USER = currentUser(); // Response struct returnData { string status; } // Project struct space { string key; } // Inner part with content struct storage { string value; string representation; } // Part for storage struct body { storage storage; } // Main entity for sending to Confluence struct reqData { string type; string title; space space; body body; } reqData data; data.type = "page"; data.title = "Page for issue " + key + " " + summary + "."; data.space.key = project; data.body.storage.value = "<p> Author:"+userFullName(USER) + " description: " + description + "</p>"; data.body.storage.representation = "storage"; // Create request HttpRequest request; HttpHeader header = httpCreateHeader("Content-Type", "application/json"); HttpHeader authHeader = httpBasicAuthHeader("admin", "admin"); request.headers += header; request.headers += authHeader; logPrint("WARN", request); //POST string JSONData = toJson(data); logPrint("WARN", JSONData); returnData result = httpPost("http://192.168.54.203:8090/rest/api/content/", request, JSONData); string errMsg = httpGetErrorMessage(); logPrint("ERROR", "Last error message: " + errMsg); logPrint("WARN", result); string COMMENT = "Page created in Confluence " + updated + " by " + userFullName(USER) + " Status : " +result.status + "."; addComment(key, USER, COMMENT); //Return Status return result.status;
Посмотрев на код достаточно сложно быстро определить, что делает код. Комментарии в коде тоже не очень помогают. Давайте попробуем изменить код в соответствии с правилами выше.
Сначала посмотрим на структуры. Вот один из примеров:
struct body { storage storage; }
Мы видим, что «storage storage», выглядит непонятным. Было бы понятнее, если бы код был вот таким:
struct body { Storage storage; }
Теперь видно, что мы определяем поле в структуре под именем storage типа Storage. Для того, чтобы мы могли написать такой код, нам необходимо сделать первую букву каждой структуры заглавной:
// Response struct ReturnData { string status; } // Project struct Space { string key; } // Inner part with content struct Storage { string value; string representation; } // Part for storage struct Body { Storage storage; } // Main entity for sending to Confluence struct ReqData { string type; string title; Space space; Body body; }
Теперь давайте переименуем структуры таким образом, чтобы из названия структуры было бы понятно для чего она нужна, и нам не пришлось бы добавлять комментарии:
struct Space { string key; } struct Storage { string value; string representation; } struct Body { Storage storage; } struct CreateConfluencePageRequest { string type; string title; Space space; Body body; } struct CreateConfluencePageResponse { string status; }
По моему мнению такие наименования не требуют дополнительных комментариев для структур. Также мы объявим структуры в самом начале кода.
Мы использовали Правила 1, 5, 6.
Теперь посмотрим на код после объявления структур:
string USER = currentUser(); CreateConfluencePageRequest data; data.type = "page"; data.title = "Page for issue " + key + " " + summary + "."; data.space.key = project; data.body.storage.value = "<p> Author:"+userFullName(USER) + " description: " + description + "</p>"; data.body.storage.representation = "storage"; // Create request HttpRequest request; HttpHeader header = httpCreateHeader("Content-Type", "application/json"); HttpHeader authHeader = httpBasicAuthHeader("admin", "admin"); request.headers += header; request.headers += authHeader; logPrint("WARN", request); //POST string JSONData = toJson(data); logPrint("WARN", JSONData); CreateConfluencePageResponse result = httpPost("http://192.168.54.203:8090/rest/api/content/", request, JSONData); string errMsg = httpGetErrorMessage(); logPrint("ERROR", "Last error message: " + errMsg); logPrint("WARN", result); string COMMENT = "Page created in Confluence " + updated + " by " + userFullName(USER) + " Status : " +result.status + "."; addComment(key, USER, COMMENT); //Return Status return result.status;
Посмотрев на код мы не можем быстро понять, что происходит в коде. Давайте сначала попробуем разбить код на логические блоки:
getNewConfluencePageContent(); createHttpRequest(); createConfluencePage(); addCommentToJiraIssue();
То есть сначала мы получаем содержимое новой страницы в Confluence, потом создаем http запрос, создаем страницу в Confluence и пишем комментарий в Jira запросе, что страница в Confluence создана. Теперь давайте реализуем этим функции:
function getNewConfluencePageContent() { CreateConfluencePageRequest data; data.type = "page"; data.title = "Page for issue " + key + " " + summary + "."; data.space.key = project; data.body.storage.value = "<p> Author:"+userFullName(currentUser()) + " description: " + description + "</p>"; data.body.storage.representation = "storage"; return toJson(data); } function createHttpRequest() { HttpRequest request; HttpHeader header = httpCreateHeader("Content-Type", "application/json"); HttpHeader authHeader = httpBasicAuthHeader("admin", "admin"); request.headers = addElement(request.headers, header); request.headers += addElement(request.headers, authHeader); logPrint("WARN", request); return request; } function createConfluencePage(string pageJson) { HttpRequest request = createHttpRequest(); CreateConfluencePageResponse result = httpPost("http://192.168.54.203:8090/rest/api/content/", request, pageJson); logPrint("ERROR", "Last error message: " + httpGetErrorMessage()); logPrint("WARN", result); return result; } function addCommentToJiraIssue(string resultStatus) { string COMMENT = "Page created in Confluence " + updated + " by " + userFullName(currentUser()) + " Status : " +resultStatus + "."; addComment(key, currentUser(), COMMENT); } string pageJson = getNewConfluencePageContent(); CreateConfluencePageResponse result = createConfluencePage(pageJson); addCommentToJiraIssue(result.status); return result.status;
Мы вынесли логические блоки в функции (Правило 2). Теперь мы можем просто написать код вот таким образом:
string pageJson = getNewConfluencePageContent(); CreateConfluencePageResponse result = createConfluencePage(pageJson); addCommentToJiraIssue(result.status); return result.status;
Код у нас содержит четыре логических блока. Сначала мы получаем json новой страницы, потом создаем страницу в Confluence, добавляем комментарий к запросу и возвращаем статус создания страницы в Confluence. Эти четыре строчки дают нам верхнеуровневое представление о том, что делает скрипт. Если мы хотим понять содержимое страницы в Confluence, то мы всегда можем перейти в getNewConfluencePageContent и посмотреть содержимое этой функции. Эта функция содержит только код для создания содержимого страницы и ничего лишнего, таким образом, изучая код функции, мы не будем отвлекаться на другую функциональность.
Мы можем предположить, что нам нужно будет создавать страницы в Confluence не только из одного скрипта. Поэтому давайте создадим файл confluence_helper.incl и вынесем все необходимые функции для создания страницы в Confluence в этот файл:
struct Space { string key; } struct Storage { string value; string representation; } struct Body { Storage storage; } struct CreateConfluencePageRequest { string type; string title; Space space; Body body; } struct CreateConfluencePageResponse { string status; } function createHttpRequest() { HttpRequest request; HttpHeader header = httpCreateHeader("Content-Type", "application/json"); HttpHeader authHeader = httpBasicAuthHeader("admin", "admin"); request.headers = addElement(request.headers, header); request.headers += addElement(request.headers, authHeader); logPrint("INFO", request); return request; } function createConfluencePage(string confluenceUrl, string pageJson) { HttpRequest request = createHttpRequest(); CreateConfluencePageResponse result = httpPost(confluenceUrl, request, pageJson); logPrint("ERROR", "Last error message: " + httpGetErrorMessage()); logPrint("INFO", result); return result; }
Я сделал функцию createConfluencePage более общий, добавив в нее еще один параметр confluenceUrl. Таким образом мы выполнили Правило 4.
Теперь наш основной скрипт будет выглядеть вот таким образом:
include "confluence_helper.incl"; function getNewConfluencePageContent() { CreateConfluencePageRequest data; data.type = "page"; data.title = "Page for issue " + key + " " + summary + "."; data.space.key = project; data.body.storage.value = "<p> Author:"+userFullName(currentUser()) + " description: " + description + "</p>"; data.body.storage.representation = "storage"; return toJson(data); } function addCommentToJiraIssue(string resultStatus) { string comment = "Page created in Confluence " + updated + " by " + userFullName(currentUser()) + " Status : " +resultStatus + "."; addComment(key, currentUser(), comment); } const string CONFLUENCE_URL = "http://192.168.54.203:8090/rest/api/content/"; string pageJson = getNewConfluencePageContent(); CreateConfluencePageResponse result = createConfluencePage(CONFLUENCE_URL, pageJson); addCommentToJiraIssue(result.status); return result.status;
В первой строке я включил файл confluence_helper.incl, чтобы использовать из него функцию создания страниц в Confluence.
Я сохранил адрес Confluence в константной переменной и изменил имена переменных в соответствии с Google Java notation style (Правило 7).
Мне кажется, что на этом можно остановиться с рефакторингом. В результате рефакторинга мы получили переиспользуемую функцию создания страниц в Confluence, и наш код стал более читабельным и поддерживаемым.
