diff --git a/README.en.md b/README.en.md new file mode 100644 index 00000000..1c397095 --- /dev/null +++ b/README.en.md @@ -0,0 +1,178 @@ +[![ru](https://img.shields.io/badge/lang-ru-red.svg)](https://github.com/qa-guru/allure-notifications/blob/master/README.md) + +# Allure notifications +**Allure notifications** is the library that allows to send automatic notifications about the results of automated tests to your preferred messenger (Telegram, Slack, Skype, Email, Mattermost, Discord, Loop, Rocket.Chat). + +Notification languages: 🇬🇧 🇫🇷 🇷🇺 🇺🇦 🇧🇾 🇨🇳 + +## Content ++ [How it works](#how-it-works) ++ [What the notifications look like](#what-the-notifications-look-like) ++ [How to use in your project](#how-to-use-in-your-project) + + +## How it works +After an autotest has finished its work, the `summary.json` file is generated in the `allure-report/widgets` folder. This file contains general statistic about the test results and uses to form the notification sent by the bot (the diagram is drawn and the corresponding text is added). + +Example of a `summary.json` file +``` +{ + "reportName" : "Allure Report", + "testRuns" : [ ], + "statistic" : { + "failed" : 182, + "broken" : 70, + "skipped" : 118, + "passed" : 439, + "unknown" : 42, + "total" : 851 + }, + "time" : { + "start" : 1590795193703, + "stop" : 1590932641296, + "duration" : 11311, + "minDuration" : 7901, + "maxDuration" : 109870, + "sumDuration" : 150125 + } +} +``` + +## What the notifications look like +Example of a notification in Telegram + +![telegram](https://user-images.githubusercontent.com/109241600/213396660-c70adc4c-7a0f-4926-8d9d-473c6c433dd2.png) + +## How to use in your project + +1. Setup Java +2. Create `notifications` folder in the root of your project +3. Download [the latest version](https://github.com/qa-guru/allure-notifications/releases) of the `allure-notifications-.jar` file and place it in the `notifications` folder in your project +4. In the `notifications` folder create the `config.json` file with the following structure (keep the base section and the messenger to which notifications need to be sent): +``` +{ + "base": { + "logo": "", + "project": "", + "environment": "", + "comment": "", + "reportLink": "", + "language": "ru", + "allureFolder": "", + "enableChart": false, + "templatePath": "" + }, + "telegram": { + "token": "", + "chat": "", + "replyTo": "", + "templatePath": "" + }, + "slack": { + "token": "", + "chat": "", + "replyTo": "", + "templatePath": "" + }, + "mattermost": { + "url": "", + "token": "", + "chat": "", + "templatePath": "" + }, + "rocketChat" : { + "url": "", + "auth_token": "", + "user_id": "", + "channel": "", + "templatePath": "" + }, + "skype": { + "appId": "", + "appSecret": "", + "serviceUrl": "", + "conversationId": "", + "botId": "", + "botName": "", + "templatePath": "" + }, + "mail": { + "host": "", + "port": "", + "username": "", + "password": "", + "securityProtocol": null, + "from": "", + "recipient": "", + "templatePath": "" + }, + "discord": { + "botToken": "", + "channelId": "", + "templatePath": "" + }, + "loop": { + "webhookUrl": "", + "templatePath": "" + }, + "proxy": { + "host": "", + "port": 0, + "username": "", + "password": "" + } +} +``` +The `proxy` block is used if you need to specify additional proxy configuration.\ +Additionally, in any configuration you can specify the `templatePath` parameter to set the path to your own Freemarker template for the message. +``` +{ + "base": { + ... + }, + "mail": { + "host": "smtp.gmail.com", + "port": "465", + "username": "username", + "password": "password", + "securityProtocol": "SSL", + "from": "test@gmail.com", + "recipient": "test1@gmail.com", + "templatePath": "templates/html_custom.ftl" + } +} +``` +5. Fill the `base` block in the `config.json` file + +Example: +``` +"base": { + "project": "some project", + "environment": "some env", + "comment": "some comment", + "reportLink": "", + "language": "en", + "allureFolder": "build/allure-report/", + "enableChart": true, + "logo": "logo.png", + "durationFormat": "HH:mm:ss.SSS" +} +``` + +Fields: ++ `project`, `environment`, `comment` - the name of the project, the name of the environment, and a custom comment. ++ `reportLink` - the link to the Allure report with results of tests. ++ `language` - the language in which the notification text will be formed (options: `en` / `fr` / `ru` / `ua` / `by` / `cn`). ++ `allureFolder` - the path to the folder with Allure results. ++ `enableChart` - whether the chart should be displayed (options: `true` / `false`). ++ `logo` - path to the logo file (if filled, the corresponding logo will be displayed in the top left corner of the chart). ++ `durationFormat` (optional, default value is `HH:mm:ss.SSS`) - specifies the desired output format for the test duration. +6. Fill in the `config.json` file block with the information about the chosen messenger. +7. Execute the following command in terminal: +``` +java "-DconfigFile=notifications/config.json" -jar notifications/allure-notifications-4.6.1.jar +``` +Note: + ++ by the time of execution the `summary.json` file should already be generated. ++ in the command-line text you need to specify the version of the `jar` file that you downloaded in the previous steps. diff --git a/README.md b/README.md index 3c5ab028..dcf1861d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![en](https://img.shields.io/badge/lang-en-red.svg)](https://github.com/qa-guru/allure-notifications/blob/master/README.en.md) + # Allure notifications **Allure notifications** - это библиотека, позволяющая выполнять автоматическое оповещение о результатах прохождения автотестов, которое направляется в нужный вам мессенджер (Telegram, Slack, Skype, Email, Mattermost, Discord, Loop, Rocket.Chat). @@ -75,28 +77,33 @@ Languages: 🇬🇧 🇫🇷 🇷🇺 🇺🇦 🇧🇾 🇨🇳 "reportLink": "", "language": "ru", "allureFolder": "", - "enableChart": false + "enableChart": false, + "templatePath": "" }, "telegram": { "token": "", "chat": "", - "replyTo": "" + "replyTo": "", + "templatePath": "" }, "slack": { "token": "", "chat": "", - "replyTo": "" + "replyTo": "", + "templatePath": "" }, "mattermost": { "url": "", "token": "", - "chat": "" + "chat": "", + "templatePath": "" }, "rocketChat" : { "url": "", "auth_token": "", "user_id": "", - "channel": "" + "channel": "", + "templatePath": "" }, "skype": { "appId": "", @@ -104,7 +111,8 @@ Languages: 🇬🇧 🇫🇷 🇷🇺 🇺🇦 🇧🇾 🇨🇳 "serviceUrl": "", "conversationId": "", "botId": "", - "botName": "" + "botName": "", + "templatePath": "" }, "mail": { "host": "", @@ -113,14 +121,17 @@ Languages: 🇬🇧 🇫🇷 🇷🇺 🇺🇦 🇧🇾 🇨🇳 "password": "", "securityProtocol": null, "from": "", - "recipient": "" + "recipient": "", + "templatePath": "" }, "discord": { "botToken": "", - "channelId": "" + "channelId": "", + "templatePath": "" }, "loop": { - "webhookUrl": "" + "webhookUrl": "", + "templatePath": "" }, "proxy": { "host": "", @@ -130,7 +141,26 @@ Languages: 🇬🇧 🇫🇷 🇷🇺 🇺🇦 🇧🇾 🇨🇳 } } ``` -Блок `proxy` используется если нужно указать дополнительную конфигурацию proxy. +Блок `proxy` используется если нужно указать дополнительную конфигурацию proxy.\ +Также в любой из конфигураций мессенджера можно дополнительно указать параметр `templatePath`, чтобы установить путь к собственному Freemarker шаблону для сообщения. +Пример: +``` +{ + "base": { + ... + }, + "mail": { + "host": "smtp.gmail.com", + "port": "465", + "username": "username", + "password": "password", + "securityProtocol": "SSL", + "from": "test@gmail.com", + "recipient": "test1@gmail.com", + "templatePath": "templates/html_custom.ftl" + } +} +``` diff --git a/allure-notifications-api/build.gradle b/allure-notifications-api/build.gradle index 24fc9367..2f804c28 100644 --- a/allure-notifications-api/build.gradle +++ b/allure-notifications-api/build.gradle @@ -16,7 +16,9 @@ dependencies { testImplementation('org.junit.jupiter:junit-jupiter:5.10.3') testRuntimeOnly('org.junit.platform:junit-platform-launcher') - testImplementation('org.mockito:mockito-junit-jupiter:4.11.0') + testImplementation platform(group: 'org.mockito', name: 'mockito-bom', version: '4.11.0') + testImplementation(group: 'org.mockito', name: 'mockito-junit-jupiter') + testImplementation(group: 'org.mockito', name: 'mockito-inline') } tasks.withType(Test).configureEach { diff --git a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/discord/DiscordClient.java b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/discord/DiscordClient.java index 5f451185..151df34d 100644 --- a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/discord/DiscordClient.java +++ b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/discord/DiscordClient.java @@ -3,7 +3,7 @@ import guru.qa.allure.notifications.clients.Notifier; import guru.qa.allure.notifications.config.discord.Discord; import guru.qa.allure.notifications.exceptions.MessagingException; -import guru.qa.allure.notifications.template.MarkdownTemplate; +import guru.qa.allure.notifications.template.MessageTemplate; import guru.qa.allure.notifications.template.data.MessageData; import kong.unirest.ContentType; import kong.unirest.Unirest; @@ -23,7 +23,7 @@ public void sendText(MessageData messageData) throws MessagingException { .routeParam("channelId", discord.getChannelId()) .header("Authorization", "Bot " + discord.getBotToken()) .header("Content-Type", ContentType.APPLICATION_FORM_URLENCODED.getMimeType()) - .field("content", new MarkdownTemplate(messageData).create()) + .field("content", new MessageTemplate(messageData).createMessageFromTemplate(discord.getTemplatePath())) .asString() .getBody(); } @@ -34,7 +34,7 @@ public void sendPhoto(MessageData messageData, byte[] chartImage) throws Messagi .routeParam("channelId", discord.getChannelId()) .header("Authorization", "Bot " + discord.getBotToken()) .field("file", new ByteArrayInputStream(chartImage), ContentType.IMAGE_PNG, "chart.png") - .field("content", new MarkdownTemplate(messageData).create()) + .field("content", new MessageTemplate(messageData).createMessageFromTemplate(discord.getTemplatePath())) .asString() .getBody(); } diff --git a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/loop/LoopClient.java b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/loop/LoopClient.java index 6a9a0417..0acf5173 100644 --- a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/loop/LoopClient.java +++ b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/loop/LoopClient.java @@ -3,7 +3,7 @@ import guru.qa.allure.notifications.clients.Notifier; import guru.qa.allure.notifications.config.loop.Loop; import guru.qa.allure.notifications.exceptions.MessagingException; -import guru.qa.allure.notifications.template.MarkdownTemplate; +import guru.qa.allure.notifications.template.MessageTemplate; import guru.qa.allure.notifications.template.data.MessageData; import kong.unirest.ContentType; import kong.unirest.Unirest; @@ -21,7 +21,7 @@ public LoopClient(Loop loop) { @Override public void sendText(MessageData messageData) throws MessagingException { Map body = new HashMap<>(); - body.put("text", new MarkdownTemplate(messageData).create()); + body.put("text", new MessageTemplate(messageData).createMessageFromTemplate(loop.getTemplatePath())); Unirest.post(loop.getWebhookUrl()) .header("Content-Type", ContentType.APPLICATION_JSON.getMimeType()) @@ -35,7 +35,7 @@ public void sendPhoto(MessageData messageData, byte[] chartImage) throws Messagi String encodedChartImage = Base64.getEncoder().encodeToString(chartImage); Map body = new HashMap<>(); - body.put("text", new MarkdownTemplate(messageData).create()); + body.put("text", new MessageTemplate(messageData).createMessageFromTemplate(loop.getTemplatePath())); Map attachment = new HashMap<>(); attachment.put("image_url", "data:image/png;base64," + encodedChartImage); diff --git a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/mail/Email.java b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/mail/Email.java index 2a7f2c5b..352b8369 100644 --- a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/mail/Email.java +++ b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/mail/Email.java @@ -3,7 +3,7 @@ import guru.qa.allure.notifications.clients.Notifier; import guru.qa.allure.notifications.config.mail.Mail; import guru.qa.allure.notifications.exceptions.MessagingException; -import guru.qa.allure.notifications.template.HTMLTemplate; +import guru.qa.allure.notifications.template.MessageTemplate; import guru.qa.allure.notifications.template.data.MessageData; public class Email implements Notifier { @@ -20,13 +20,14 @@ public void sendText(MessageData messageData) throws MessagingException { letter.from(mail.getFrom()) .to(mail.getRecipient()) .subject(messageData.getProject()) - .text(new HTMLTemplate(messageData).create()) + .text(new MessageTemplate(messageData).createMessageFromTemplate(mail.getTemplatePath())) .send(); } @Override public void sendPhoto(MessageData messageData, byte[] chartImage) throws MessagingException { - String message = "
" + new HTMLTemplate(messageData).create(); + String message = "
" + new MessageTemplate(messageData).createMessageFromTemplate( + mail.getTemplatePath()); letter.from(mail.getFrom()) .to(mail.getRecipient()) .subject(messageData.getProject()) diff --git a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/mattermost/MattermostClient.java b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/mattermost/MattermostClient.java index 0500ad6e..b7e9102f 100644 --- a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/mattermost/MattermostClient.java +++ b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/mattermost/MattermostClient.java @@ -5,7 +5,7 @@ import guru.qa.allure.notifications.config.mattermost.Mattermost; import guru.qa.allure.notifications.exceptions.MessageBuildException; import guru.qa.allure.notifications.exceptions.MessagingException; -import guru.qa.allure.notifications.template.MarkdownTemplate; +import guru.qa.allure.notifications.template.MessageTemplate; import kong.unirest.ContentType; import guru.qa.allure.notifications.template.data.MessageData; import kong.unirest.Unirest; @@ -48,7 +48,7 @@ public void sendPhoto(MessageData messageData, byte[] chartImage) throws Messagi private void send(MessageData messageData, Map body) throws MessageBuildException { body.put("channel_id", mattermost.getChat()); - body.put("message", new MarkdownTemplate(messageData).create()); + body.put("message", new MessageTemplate(messageData).createMessageFromTemplate(mattermost.getTemplatePath())); Unirest.post("https://{uri}/api/v4/posts") .routeParam("uri", mattermost.getUrl()) diff --git a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/rocket/RocketChatClient.java b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/rocket/RocketChatClient.java index 121b6083..8f1366c4 100644 --- a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/rocket/RocketChatClient.java +++ b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/rocket/RocketChatClient.java @@ -3,7 +3,7 @@ import guru.qa.allure.notifications.clients.Notifier; import guru.qa.allure.notifications.config.rocket.RocketChat; import guru.qa.allure.notifications.exceptions.MessagingException; -import guru.qa.allure.notifications.template.RocketTemplate; +import guru.qa.allure.notifications.template.MessageTemplate; import guru.qa.allure.notifications.template.data.MessageData; import java.io.ByteArrayInputStream; import java.util.HashMap; @@ -22,7 +22,7 @@ public RocketChatClient(RocketChat rocket) { public void sendText(MessageData messageData) throws MessagingException { Map body = new HashMap<>(); body.put("channel", rocketChat.getChannel()); - body.put("text", new RocketTemplate(messageData).create()); + body.put("text", new MessageTemplate(messageData).createMessageFromTemplate(rocketChat.getTemplatePath())); Unirest.post(rocketChat.getUrl() + "/api/v1/chat.postMessage") .header("X-Auth-Token", rocketChat.getToken()) .header("X-User-Id", rocketChat.getUserId()) diff --git a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/skype/SkypeClient.java b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/skype/SkypeClient.java index 71080709..0744b3c4 100644 --- a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/skype/SkypeClient.java +++ b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/skype/SkypeClient.java @@ -7,7 +7,7 @@ import guru.qa.allure.notifications.config.skype.Skype; import guru.qa.allure.notifications.exceptions.MessageBuildException; import guru.qa.allure.notifications.exceptions.MessagingException; -import guru.qa.allure.notifications.template.MarkdownTemplate; +import guru.qa.allure.notifications.template.MessageTemplate; import kong.unirest.ContentType; import guru.qa.allure.notifications.template.data.MessageData; import kong.unirest.Unirest; @@ -68,7 +68,7 @@ private SkypeMessage createSimpleMessage(MessageData messageData) throws Message return SkypeMessage.builder() .type("message") .from(from) - .text(new MarkdownTemplate(messageData).create()) + .text(new MessageTemplate(messageData).createMessageFromTemplate(skype.getTemplatePath())) .build(); } diff --git a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/slack/SlackClient.java b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/slack/SlackClient.java index bacff92c..13b9af01 100644 --- a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/slack/SlackClient.java +++ b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/slack/SlackClient.java @@ -3,7 +3,7 @@ import guru.qa.allure.notifications.clients.Notifier; import guru.qa.allure.notifications.config.slack.Slack; import guru.qa.allure.notifications.exceptions.MessagingException; -import guru.qa.allure.notifications.template.MarkdownTemplate; +import guru.qa.allure.notifications.template.MessageTemplate; import guru.qa.allure.notifications.template.data.MessageData; import kong.unirest.ContentType; import kong.unirest.Unirest; @@ -20,7 +20,7 @@ public SlackClient(Slack slack) { @Override public void sendText(MessageData messageData) throws MessagingException { String body = String.format("channel=%s&text=%s", - slack.getChat(), new MarkdownTemplate(messageData).create()); + slack.getChat(), new MessageTemplate(messageData).createMessageFromTemplate(slack.getTemplatePath())); Unirest.post("https://slack.com/api/chat.postMessage") .header("Authorization", "Bearer " + slack.getToken()) @@ -37,7 +37,8 @@ public void sendPhoto(MessageData messageData, byte[] chartImage) throws Messagi .field("file", new ByteArrayInputStream(chartImage), ContentType.IMAGE_PNG, "chart.png") .field("channels", slack.getChat()) .field("filename", " ") - .field("initial_comment", new MarkdownTemplate(messageData).create()) + .field("initial_comment", new MessageTemplate(messageData).createMessageFromTemplate( + slack.getTemplatePath())) .asString() .getBody(); } diff --git a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/telegram/TelegramClient.java b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/telegram/TelegramClient.java index 7ab0a2a1..4afe3150 100644 --- a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/telegram/TelegramClient.java +++ b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/clients/telegram/TelegramClient.java @@ -3,7 +3,7 @@ import guru.qa.allure.notifications.clients.Notifier; import guru.qa.allure.notifications.config.telegram.Telegram; import guru.qa.allure.notifications.exceptions.MessagingException; -import guru.qa.allure.notifications.template.TelegramTemplate; +import guru.qa.allure.notifications.template.MessageTemplate; import guru.qa.allure.notifications.template.data.MessageData; import kong.unirest.ContentType; import kong.unirest.Unirest; @@ -20,11 +20,11 @@ public TelegramClient(Telegram telegram) { @Override public void sendText(MessageData messageData) throws MessagingException { Unirest.post("https://api.telegram.org/bot{token}/sendMessage") - .routeParam("token", telegram.token()) + .routeParam("token", telegram.getToken()) .header("Content-Type", ContentType.APPLICATION_FORM_URLENCODED.getMimeType()) - .field("chat_id", telegram.chat()) - .field("reply_to_message_id", telegram.replyTo() + "") - .field("text", new TelegramTemplate(messageData).create()) + .field("chat_id", telegram.getChat()) + .field("reply_to_message_id", telegram.getReplyTo() + "") + .field("text", new MessageTemplate(messageData).createMessageFromTemplate(telegram.getTemplatePath())) .field("parse_mode", "HTML") .asString() .getBody(); @@ -33,11 +33,11 @@ public void sendText(MessageData messageData) throws MessagingException { @Override public void sendPhoto(MessageData messageData, byte[] chartImage) throws MessagingException { Unirest.post("https://api.telegram.org/bot{token}/sendPhoto") - .routeParam("token", telegram.token()) + .routeParam("token", telegram.getToken()) .field("photo", new ByteArrayInputStream(chartImage), ContentType.IMAGE_PNG, "chart.png") - .field("chat_id", telegram.chat()) - .field("reply_to_message_id", telegram.replyTo()) - .field("caption", new TelegramTemplate(messageData).create()) + .field("chat_id", telegram.getChat()) + .field("reply_to_message_id", telegram.getReplyTo()) + .field("caption", new MessageTemplate(messageData).createMessageFromTemplate(telegram.getTemplatePath())) .field("parse_mode", "HTML") .asString() .getBody(); diff --git a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/discord/Discord.java b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/discord/Discord.java index cd4df2f2..398d0820 100644 --- a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/discord/Discord.java +++ b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/discord/Discord.java @@ -9,4 +9,6 @@ public class Discord { private String botToken; @SerializedName("channelId") private String channelId; + @SerializedName("templatePath") + private String templatePath = "/templates/markdown.ftl"; } diff --git a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/loop/Loop.java b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/loop/Loop.java index 21bf726f..130e2585 100644 --- a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/loop/Loop.java +++ b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/loop/Loop.java @@ -7,4 +7,6 @@ public class Loop { @SerializedName("webhookUrl") private String webhookUrl; + @SerializedName("templatePath") + private String templatePath = "/templates/markdown.ftl"; } \ No newline at end of file diff --git a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/mail/Mail.java b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/mail/Mail.java index 3fd6d69f..a4e9ed87 100644 --- a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/mail/Mail.java +++ b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/mail/Mail.java @@ -24,4 +24,6 @@ public class Mail { private String from; @SerializedName("recipient") private String recipient; + @SerializedName("templatePath") + private String templatePath = "/templates/html.ftl"; } diff --git a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/mattermost/Mattermost.java b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/mattermost/Mattermost.java index 4e46096f..35c4750d 100644 --- a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/mattermost/Mattermost.java +++ b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/mattermost/Mattermost.java @@ -16,4 +16,6 @@ public class Mattermost { private String token; @SerializedName("chat") private String chat; + @SerializedName("templatePath") + private String templatePath = "/templates/markdown.ftl"; } diff --git a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/rocket/RocketChat.java b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/rocket/RocketChat.java index 73c175b2..06589b55 100644 --- a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/rocket/RocketChat.java +++ b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/rocket/RocketChat.java @@ -14,4 +14,6 @@ public class RocketChat { private String userId; @SerializedName("channel") private String channel; + @SerializedName("templatePath") + private String templatePath = "/templates/rocket.ftl"; } diff --git a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/skype/Skype.java b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/skype/Skype.java index 5e8900b1..267076c9 100644 --- a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/skype/Skype.java +++ b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/skype/Skype.java @@ -22,4 +22,6 @@ public class Skype { private String botId; @SerializedName("botName") private String botName; + @SerializedName("templatePath") + private String templatePath = "/templates/markdown.ftl"; } diff --git a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/slack/Slack.java b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/slack/Slack.java index fe73ab91..8c1cd250 100644 --- a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/slack/Slack.java +++ b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/slack/Slack.java @@ -16,4 +16,6 @@ public class Slack { private String chat; @SerializedName("replyTo") private String replyTo; + @SerializedName("templatePath") + private String templatePath = "/templates/markdown.ftl"; } diff --git a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/telegram/Telegram.java b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/telegram/Telegram.java index c0cc6d35..09dc8747 100644 --- a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/telegram/Telegram.java +++ b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/config/telegram/Telegram.java @@ -2,11 +2,14 @@ import com.google.gson.annotations.SerializedName; +import lombok.Getter; + /** * @author kadehar * @since 4.0 * Model class representing telegram settings. */ +@Getter public class Telegram { @SerializedName("token") private String token; @@ -14,28 +17,6 @@ public class Telegram { private String chat; @SerializedName("replyTo") private String replyTo; - - public String token() { - return token; - } - - public void setToken(String token) { - this.token = token; - } - - public String chat() { - return chat; - } - - public void setChat(String chat) { - this.chat = chat; - } - - public String replyTo() { - return replyTo; - } - - public void setReplyTo(String replyTo) { - this.replyTo = replyTo; - } + @SerializedName("templatePath") + private String templatePath = "/templates/telegram.ftl"; } diff --git a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/template/HTMLTemplate.java b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/template/HTMLTemplate.java deleted file mode 100644 index 5c796b57..00000000 --- a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/template/HTMLTemplate.java +++ /dev/null @@ -1,21 +0,0 @@ -package guru.qa.allure.notifications.template; - -import guru.qa.allure.notifications.exceptions.MessageBuildException; -import guru.qa.allure.notifications.template.data.MessageData; - -/** - * @author kadehar - * @since 4.0 - * Utility class for HTML template creation. - */ -public class HTMLTemplate { - private final MessageData messageData; - - public HTMLTemplate(MessageData messageData) { - this.messageData = messageData; - } - - public String create() throws MessageBuildException { - return new MessageTemplate(messageData).of("html.ftl"); - } -} diff --git a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/template/MarkdownTemplate.java b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/template/MarkdownTemplate.java deleted file mode 100644 index 8a34bada..00000000 --- a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/template/MarkdownTemplate.java +++ /dev/null @@ -1,21 +0,0 @@ -package guru.qa.allure.notifications.template; - -import guru.qa.allure.notifications.exceptions.MessageBuildException; -import guru.qa.allure.notifications.template.data.MessageData; - -/** - * @author kadehar - * @since 4.0 - * Utility class for markdown template creation. - */ -public class MarkdownTemplate { - private final MessageData messageData; - - public MarkdownTemplate(MessageData messageData) { - this.messageData = messageData; - } - - public String create() throws MessageBuildException { - return new MessageTemplate(messageData).of("markdown.ftl"); - } -} diff --git a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/template/MessageTemplate.java b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/template/MessageTemplate.java index dbae9548..f1b95b19 100644 --- a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/template/MessageTemplate.java +++ b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/template/MessageTemplate.java @@ -7,9 +7,11 @@ import guru.qa.allure.notifications.template.data.MessageData; import lombok.extern.slf4j.Slf4j; +import java.io.File; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; +import java.util.Optional; /** * @author kadehar @@ -26,21 +28,24 @@ public MessageTemplate(MessageData messageData) { this.messageData = messageData; } - public String of(String templateFile) throws MessageBuildException { - log.info("Processing template {}", templateFile); + public String createMessageFromTemplate(String templatePath) throws MessageBuildException { + log.info("Processing template {}", templatePath); Template template; try { log.info("Parsing template"); - template = templateConfig.configure().getTemplate(templateFile); + File templateAsFile = new File(templatePath); + template = templateAsFile.exists() + ? templateConfig.configure(Optional.of(templateAsFile)).getTemplate(templateAsFile.getName()) + : templateConfig.configure(Optional.empty()).getTemplate(templatePath); } catch (IOException ex) { - throw new MessageBuildException(String.format("Unable to parse template %s!", templateFile), ex); + throw new MessageBuildException(String.format("Unable to parse template %s!", templatePath), ex); } Writer writer = new StringWriter(); try { log.info("Convert template to string"); template.process(messageData.getValues(), writer); } catch (TemplateException | IOException ex) { - throw new MessageBuildException(String.format("Unable to parse template %s!", templateFile), ex); + throw new MessageBuildException(String.format("Unable to parse template %s!", templatePath), ex); } return writer.toString(); } diff --git a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/template/RocketTemplate.java b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/template/RocketTemplate.java deleted file mode 100644 index f07f79e8..00000000 --- a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/template/RocketTemplate.java +++ /dev/null @@ -1,17 +0,0 @@ -package guru.qa.allure.notifications.template; - -import guru.qa.allure.notifications.exceptions.MessageBuildException; -import guru.qa.allure.notifications.template.data.MessageData; - -public class RocketTemplate { - - private final MessageData messageData; - - public RocketTemplate(MessageData messageData) { - this.messageData = messageData; - } - - public String create() throws MessageBuildException { - return new MessageTemplate(messageData).of("rocket.ftl"); - } -} diff --git a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/template/TelegramTemplate.java b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/template/TelegramTemplate.java deleted file mode 100644 index b72d014d..00000000 --- a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/template/TelegramTemplate.java +++ /dev/null @@ -1,19 +0,0 @@ -package guru.qa.allure.notifications.template; - -import guru.qa.allure.notifications.exceptions.MessageBuildException; -import guru.qa.allure.notifications.template.data.MessageData; - -/** - * Utility class for telegram html template creation. - */ -public class TelegramTemplate { - private final MessageData messageData; - - public TelegramTemplate(MessageData messageData) { - this.messageData = messageData; - } - - public String create() throws MessageBuildException { - return new MessageTemplate(messageData).of("telegram.ftl"); - } -} diff --git a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/template/config/TemplateConfig.java b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/template/config/TemplateConfig.java index d26ab115..452a60a0 100644 --- a/allure-notifications-api/src/main/java/guru/qa/allure/notifications/template/config/TemplateConfig.java +++ b/allure-notifications-api/src/main/java/guru/qa/allure/notifications/template/config/TemplateConfig.java @@ -1,11 +1,15 @@ package guru.qa.allure.notifications.template.config; +import static freemarker.template.Configuration.VERSION_2_3_31; + +import java.io.File; +import java.io.IOException; +import java.util.Optional; + import freemarker.template.Configuration; import guru.qa.allure.notifications.template.MessageTemplate; import lombok.extern.slf4j.Slf4j; -import static freemarker.template.Configuration.VERSION_2_3_31; - /** * @author kadehar * @since 4.0 @@ -14,12 +18,17 @@ @Slf4j public class TemplateConfig { - public Configuration configure() { + public Configuration configure(Optional template) throws IOException { log.info("Configuring template engine..."); final Configuration config = new Configuration(VERSION_2_3_31); log.info("Set directory for templates loading..."); - config.setClassForTemplateLoading(MessageTemplate.class, "/templates"); + if (template.isPresent()) { + config.setDirectoryForTemplateLoading(template.get().getParentFile()); + } + else { + config.setClassForTemplateLoading(MessageTemplate.class, "/"); + } log.info("Done."); log.info("Set UTF-8 encoding..."); config.setDefaultEncoding("UTF-8"); diff --git a/allure-notifications-api/src/test/java/guru/qa/allure/notifications/template/MessageTemplateTests.java b/allure-notifications-api/src/test/java/guru/qa/allure/notifications/template/MessageTemplateTests.java new file mode 100644 index 00000000..3f6aa194 --- /dev/null +++ b/allure-notifications-api/src/test/java/guru/qa/allure/notifications/template/MessageTemplateTests.java @@ -0,0 +1,69 @@ +package guru.qa.allure.notifications.template; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.mockConstruction; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockedConstruction; +import org.mockito.junit.jupiter.MockitoExtension; + +import freemarker.template.Configuration; +import freemarker.template.Template; +import guru.qa.allure.notifications.exceptions.MessageBuildException; +import guru.qa.allure.notifications.template.config.TemplateConfig; +import guru.qa.allure.notifications.template.data.MessageData; + +@ExtendWith(MockitoExtension.class) +class MessageTemplateTests { + public static final String FILE_NAME = "testTemplate.ftl"; + + public static final String FILESYSTEM_PATH = "guru/qa/allure/notifications/template/" + FILE_NAME; + public static final String RESOURCES_PATH = "/templates/" + FILE_NAME; + + @Mock private MessageData messageData; + @Mock private Configuration configuration; + @Mock private Template template; + + @Test + void shouldCreateMessageFromTemplateFromFileSystem() throws MessageBuildException, IOException { + try (MockedConstruction configMock = mockConstruction(TemplateConfig.class, + (mock, context) -> when(mock.configure(any())).thenReturn(configuration))) { + String fileSystemPath = ClassLoader.getSystemResource(FILESYSTEM_PATH).getFile(); + when(configuration.getTemplate(FILE_NAME)).thenReturn(template); + when(messageData.getValues()).thenReturn(new HashMap<>()); + MessageTemplate messageTemplate = new MessageTemplate(messageData); + messageTemplate.createMessageFromTemplate(fileSystemPath); + + TemplateConfig templateConfig = configMock.constructed().get(0); + verify(templateConfig).configure(argThat(f -> + new File(fileSystemPath).toString().equals(f.get().toString()))); + verify(configuration).getTemplate(FILE_NAME); + } + } + + @Test + void shouldCreateMessageFromTemplateFromJar() throws MessageBuildException, IOException { + try (MockedConstruction configMock = mockConstruction(TemplateConfig.class, + (mock, context) -> when(mock.configure(any())).thenReturn(configuration))) { + when(configuration.getTemplate(RESOURCES_PATH)).thenReturn(template); + when(messageData.getValues()).thenReturn(new HashMap<>()); + MessageTemplate messageTemplate = new MessageTemplate(messageData); + messageTemplate.createMessageFromTemplate(RESOURCES_PATH); + + TemplateConfig templateConfig = configMock.constructed().get(0); + verify(templateConfig).configure(Optional.empty()); + verify(configuration).getTemplate(RESOURCES_PATH); + } + } +} diff --git a/allure-notifications-api/src/test/resources/guru/qa/allure/notifications/template/testTemplate.ftl b/allure-notifications-api/src/test/resources/guru/qa/allure/notifications/template/testTemplate.ftl new file mode 100644 index 00000000..d1843187 --- /dev/null +++ b/allure-notifications-api/src/test/resources/guru/qa/allure/notifications/template/testTemplate.ftl @@ -0,0 +1,15 @@ +<#compress> +

${results}:

+ ${environment}: ${env}
+ ${comment}: ${comm}
+ ${duration}: ${time}
+ ${totalScenarios}: ${total} +
    + <#if passed != 0 >
  • ${totalPassed}: ${passed} (${passedPercentage} %)
  • + <#if failed != 0 >
  • ${totalFailed}: ${failed} (${failedPercentage} %)
  • + <#if broken != 0 >
  • ${totalBroken}: ${broken}
  • + <#if unknown != 0 >
  • ${totalUnknown}: ${unknown}
  • + <#if skipped != 0 >
  • ${totalSkipped}: ${skipped}
  • +
+ <#if reportLink??>${reportAvailableAtLink}: ${reportLink} + \ No newline at end of file