{"id":12542,"date":"2022-01-17T07:00:03","date_gmt":"2022-01-17T06:00:03","guid":{"rendered":"https:\/\/sii.pl\/blog\/?p=12542"},"modified":"2025-05-07T13:04:52","modified_gmt":"2025-05-07T11:04:52","slug":"azure-logicapp-oraz-functionapp-umozliwienie-weryfikacji-wysylki-i-tresci-wiadomosci-e-mail-jako-jeden-z-elementow-testow-e2e","status":"publish","type":"post","link":"https:\/\/sii.pl\/blog\/azure-logicapp-oraz-functionapp-umozliwienie-weryfikacji-wysylki-i-tresci-wiadomosci-e-mail-jako-jeden-z-elementow-testow-e2e\/","title":{"rendered":"Azure LogicApp oraz FunctionApp \u2013 umo\u017cliwienie weryfikacji wysy\u0142ki i tre\u015bci wiadomo\u015bci e-mail jako jeden z element\u00f3w test\u00f3w E2E"},"content":{"rendered":"\n<p>W dzisiejszych czasach chyba \u017caden software developer nie ma w\u0105tpliwo\u015bci w kwestii potrzeby testowania wytworzonego przez siebie kodu.&nbsp;Podczas prac nad wysy\u0142k\u0105 wiadomo\u015bci e-mail potrzebowa\u0142em rozwi\u0105zania, kt\u00f3re pozwoli na weryfikacj\u0119 tematu wiadomo\u015bci oraz jej zawarto\u015bci. W kr\u00f3tkim czasie uda\u0142o si\u0119 opracowa\u0107 proste rozwi\u0105zanie, oparte o platform\u0119 chmurow\u0105 Azure, kt\u00f3re chcia\u0142bym zaprezentowa\u0107.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Problem i zastosowane rozwi\u0105zania\/narz\u0119dzia<\/h2>\n\n\n\n<p>Problemem by\u0142o umo\u017cliwienie testowania wysy\u0142ki oraz zawarto\u015bci wiadomo\u015bci e-mail jako elementu test\u00f3w E2E. Aby znale\u017a\u0107 rozwi\u0105zanie skorzysta\u0142em z:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Konta na platformie Azure,<\/li><li>Microsoft Visual Studio 2019,<\/li><li>Azure Functions\/.NET Core 3.1,<\/li><li>Azure LogicApp,<\/li><li>PowerShell,<\/li><li>Postman,<\/li><li>Konta Microsoft Outlook Shared Mailbox.<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Opis koncepcyjny proponowanego rozwi\u0105zania<\/h2>\n\n\n\n<p>Platforma chmurowa Azure daje wiele mo\u017cliwo\u015bci. Jedn\u0105 z nich jest tworzenie niewielkich aplikacji w szybki spos\u00f3b, jednocze\u015bnie pozwalaj\u0105c na ich bardzo \u0142atw\u0105 i wygodn\u0105 integracj\u0119. Wobec tego przyjrzyjmy si\u0119 proponowanemu rozwi\u0105zaniu opartemu o komponenty Azure (Ryc. 1).<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.1-1024x138-1.png\"><img decoding=\"async\" width=\"1024\" height=\"138\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.1-1024x138-1.png\" alt=\"Ryc. 1 Proponowane rozwi\u0105zanie oparte o komponenty Azure\" class=\"wp-image-18540\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.1-1024x138-1.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.1-1024x138-1-300x40.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.1-1024x138-1-768x104.png 768w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption>Ryc. 1 Proponowane rozwi\u0105zanie oparte o komponenty Azure<\/figcaption><\/figure><\/div>\n\n\n\n<p>Najwa\u017cniejszym elementem jest w tym przypadku&nbsp;<strong>Azure LogicApp<\/strong>, kt\u00f3ry ma trzy podstawowe zadania:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Odpytywanie mailowej skrzynki wsp\u00f3\u0142dzielonej o nadchodz\u0105ce wiadomo\u015bci e-mail.<\/li><li>Procesowanie otrzymanej wiadomo\u015bci.<\/li><li>Zapis wiadomo\u015bci do Azure Blob Storage.<\/li><\/ul>\n\n\n\n<p>Azure FunctionApp udost\u0119pnia endpoint pozwalaj\u0105cy na pobranie zapisanego wcze\u015bniej bloba w formie tekstowej.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Rozwi\u0105zanie<\/h2>\n\n\n\n<p>Pora przyjrze\u0107 si\u0119 szczeg\u00f3\u0142om technicznym. Opisz\u0119 krok po kroku proces tworzenia aplikacji. Dla uproszczenia, wy\u0142\u0105czaj\u0105c jednak kroki zwi\u0105zane z tworzeniem samych zasob\u00f3w, gdy\u017c istnieje wiele tutoriali, jak poradzi\u0107 sobie z tym tematem. Znajdziemy je na przyk\u0142ad w oficjalnej dokumentacji Microsoftu:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/logic-apps\/quickstart-create-first-logic-app-workflow\" rel=\"nofollow\" >Quickstart: Create an integration workflow with multi-tenant Azure Logic Apps and the Azure portal<\/a>&nbsp;(zdecydujmy si\u0119 na Consumption Plan podczas tworzenia zasobu),<\/li><li><a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/azure-functions\/functions-create-function-app-portal\" rel=\"nofollow\" >Create your first function in the Azure portal<\/a>,<\/li><li><a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/storage\/common\/storage-account-create?tabs=azure-portal\" rel=\"nofollow\" >Create a storage account.<\/a><\/li><\/ul>\n\n\n\n<p>Dodatkowo, w ko\u0144cowej cz\u0119\u015bci artyku\u0142u opisz\u0119 procedur\u0119 deploymentu z u\u017cyciem ARM template odzwierciedlaj\u0105cego stworzone zasoby. Pozwoli to na szybkie odtworzenie opisanego rozwi\u0105zania bez konieczno\u015bci \u201ewyklikiwania\u201d wszystkiego w portalu.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Implementacja<\/h3>\n\n\n\n<p>Po utworzeniu LogicApp przejd\u017amy do implementacji. Wybierzmy utworzony zas\u00f3b LogicApp. Nast\u0119pnie z menu po lewej w sekcji Development Tools kliknijmy Logic App Designer oraz, z dost\u0119pnych opcji, Blank Logic App. Pierwszym krokiem jest decyzja, co b\u0119dzie wyzwala\u0142o wykonanie utworzonej funkcji (Ryc. 2).<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.2-1.png\"><img decoding=\"async\" width=\"769\" height=\"371\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.2-1.png\" alt=\"Ryc. 2 Proponowane opcje wyzwolenia aplikacji\" class=\"wp-image-18542\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.2-1.png 769w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.2-1-300x145.png 300w\" sizes=\"(max-width: 769px) 100vw, 769px\" \/><\/a><figcaption>Ryc. 2 Proponowane opcje wyzwolenia aplikacji<\/figcaption><\/figure><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Konfiguracja connectora<\/h3>\n\n\n\n<p>Dost\u0119pnych opcji s\u0105 setki, natomiast w naszym przypadku skorzystamy z connectora Office 365 Outlook i akcji \u201eWhen a new email arrives in a shared mailbox (V2)\u201d. Wybra\u0142em shared mailbox, poniewa\u017c ta skrzynka po ustawieniu odpowiednich uprawnie\u0144 mo\u017ce by\u0107 dost\u0119pna dla ka\u017cdego cz\u0142onka zespo\u0142u, u\u0142atwiaj\u0105c wsp\u00f3ln\u0105 prac\u0119.<\/p>\n\n\n\n<p>Konfiguracja connectora powinna by\u0107 nast\u0119puj\u0105ca (Ryc. 3):<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-3-1-1.png\"><img decoding=\"async\" width=\"753\" height=\"409\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-3-1-1.png\" alt=\"Ryc. 3 Konfiguracja connectora Outlook\" class=\"wp-image-18544\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-3-1-1.png 753w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-3-1-1-300x163.png 300w\" sizes=\"(max-width: 753px) 100vw, 753px\" \/><\/a><figcaption>Ryc. 3 Konfiguracja connectora Outlook<a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-3-1-1.png\"><\/a><\/figcaption><\/figure><\/div>\n\n\n\n<p>Original Mailbox Address jest adresem naszej skrzynki wsp\u00f3\u0142dzielonej. Inne opcje pozwalaj\u0105 na filtrowanie wiadomo\u015bci e-mail. Wed\u0142ug ustawie\u0144, skrzynka sprawdzana jest co 20 sekund. Po wprowadzeniu parametr\u00f3w, musimy uwierzytelni\u0107 dost\u0119p do skrzynki poprzez zalogowanie si\u0119. Warto wspomnie\u0107, \u017ce po skorzystaniu z connector\u00f3w w naszej Resource Group zostan\u0105 stworzone dodatkowe zasoby typu API Connection.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Inicjalizacja zmiennej<\/h3>\n\n\n\n<p>Otrzymywane wiadomo\u015bci e-mail powinny by\u0107 mo\u017cliwe do jednoznacznego zidentyfikowania, wobec tego za\u0142\u00f3\u017cmy, \u017ce w tytule wiadomo\u015bci znajduje si\u0119 unikalne OrderId. U\u017cyjemy go r\u00f3wnie\u017c jako nazwy pliku zapisanego w Blob Storage.<\/p>\n\n\n\n<p>Dodajmy wobec tego krok w LogicApp, pozwalaj\u0105cy na zainicjalizowanie zmiennej warto\u015bci\u0105 tego Id. Wybierzmy kolejno New Step, Variables, Initialize variable (Ryc. 4).<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-4-1024x527-1.png\"><img decoding=\"async\" width=\"1024\" height=\"527\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-4-1024x527-1.png\" alt=\"Ryc. 4 Inicjalizacja zmiennej\" class=\"wp-image-18546\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-4-1024x527-1.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-4-1024x527-1-300x154.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-4-1024x527-1-768x395.png 768w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption>Ryc. 4 Inicjalizacja zmiennej<\/figcaption><\/figure><\/div>\n\n\n\n<p>Jako warto\u015b\u0107 wyra\u017cenia Expression podajmy:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nsubstring(triggerBody()?&#x5B;&#039;Subject&#039;], add(9, lastIndexOf(triggerBody()?&#x5B;&#039;Subject&#039;], &#039;ORDERID: &#039;)), 10)\n<\/pre><\/div>\n\n\n<p>Jest to wyra\u017cenie pozwalaj\u0105ce na wyszukanie w temacie wiadomo\u015bci e-mail ci\u0105gu znak\u00f3w \u201eORDERID: \u201d oraz pobranie kolejnych 10 znak\u00f3w, kt\u00f3re b\u0119d\u0105 stanowi\u0107 w\u0142a\u015bnie parametr orderId.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nsubstring(triggerBody()?&#x5B;&#039;Subject&#039;], add(9, lastIndexOf(triggerBody()?&#x5B;&#039;Subject&#039;], &#039;ORDERID: &#039;)), 10)\n<\/pre><\/div>\n\n\n<p>Jest to wyra\u017cenie pozwalaj\u0105ce na wyszukanie w temacie wiadomo\u015bci e-mail ci\u0105gu znak\u00f3w \u201eORDERID: \u201d oraz pobranie kolejnych 10 znak\u00f3w, kt\u00f3re b\u0119d\u0105 stanowi\u0107 w\u0142a\u015bnie parametr orderId.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Po\u0142\u0105czenie do Azure Storage Account<\/h3>\n\n\n\n<p>Kolejny krok, kt\u00f3ry powinni\u015bmy doda\u0107, to po\u0142\u0105czenia do Azure Storage Account: New Step, Azure Blob Storage, Create New Blob (Ryc. 5).<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-5-1.png\"><img decoding=\"async\" width=\"721\" height=\"297\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-5-1.png\" alt=\"Ryc. 5 Dodanie po\u0142\u0105czenia z Storage Account\" class=\"wp-image-18548\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-5-1.png 721w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-5-1-300x124.png 300w\" sizes=\"(max-width: 721px) 100vw, 721px\" \/><\/a><figcaption>Ryc. 5 Dodanie po\u0142\u0105czenia z Storage Account<\/figcaption><\/figure><\/div>\n\n\n\n<p>Nast\u0119pnie skonfigurujmy to po\u0142\u0105czenie (Ryc. 6):<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-6-1.png\"><img decoding=\"async\" width=\"749\" height=\"407\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-6-1.png\" alt=\"Ryc. 6 Konfiguracja po\u0142\u0105czenia z Storage Account\" class=\"wp-image-18550\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-6-1.png 749w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-6-1-300x163.png 300w\" sizes=\"(max-width: 749px) 100vw, 749px\" \/><\/a><figcaption>Ryc. 6 Konfiguracja po\u0142\u0105czenia z Storage Account<\/figcaption><\/figure><\/div>\n\n\n\n<p>Parametry to:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Connection Name \u2013 dowolna nazwa po\u0142\u0105czenia,<\/li><li>Authentication Type \u2013 typ uwierzytelnienia \u2013 Access Key, czyli klucz dost\u0119powy,<\/li><li>Azure Storage Account Name \u2013 nazwa Storage Account,<\/li><li>Azure Storage Account Access Key \u2013 klucz dost\u0119powy.<\/li><\/ul>\n\n\n\n<p>Account Name oraz Access Key mo\u017cemy odnale\u017a\u0107 we wcze\u015bniej stworzonym Storage Account (Ryc. 7):<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-7-1.png\"><img decoding=\"async\" width=\"1002\" height=\"595\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-7-1.png\" alt=\"Ryc. 7 Parametry niezb\u0119dne do skonfigurowania po\u0142\u0105czenia z Storage Account\" class=\"wp-image-18552\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-7-1.png 1002w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-7-1-300x178.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-7-1-768x456.png 768w\" sizes=\"(max-width: 1002px) 100vw, 1002px\" \/><\/a><figcaption>Ryc. 7 Parametry niezb\u0119dne do skonfigurowania po\u0142\u0105czenia z Storage Account<\/figcaption><\/figure><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Implementacja akcji tworzenia bloba<\/h3>\n\n\n\n<p>Po uzyskaniu po\u0142\u0105czenia ze Storage Account musimy zaimplementowa\u0107 akcj\u0119 tworzenia bloba (Ryc. 8):<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-8-1.png\"><img decoding=\"async\" width=\"753\" height=\"342\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-8-1.png\" alt=\"Ryc. 8 Konfiguracja tworzenia bloba z wiadomo\u015bci\u0105 e-mail\" class=\"wp-image-18554\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-8-1.png 753w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-8-1-300x136.png 300w\" sizes=\"(max-width: 753px) 100vw, 753px\" \/><\/a><figcaption>Ryc. 8 Konfiguracja tworzenia bloba z wiadomo\u015bci\u0105 e-mail<\/figcaption><\/figure><\/div>\n\n\n\n<p>Parametry:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Storage Account Name \u2013 wybieramy wcze\u015bniej skonfigurowane po\u0142\u0105czenie,<\/li><li>Folder Path \u2013 nazwa kontenera Blob Storage, gdzie przechowywane b\u0119d\u0105 wiadomo\u015bci e-mail,<\/li><li>Blob Name \u2013 nazw\u0105 bloba b\u0119dzie unikaly orderId,<\/li><li>Blob Content \u2013 zawarto\u015bci\u0105 bloba b\u0119dzie temat oraz tre\u015b\u0107 wiadomo\u015bci e-mail oddzielone od siebie separatorem.<\/li><\/ul>\n\n\n\n<p>Musimy mie\u0107 r\u00f3wnie\u017c pewno\u015b\u0107, \u017ce skonfigurowany kontener istnieje, wobec czego dodajmy go w Storage Account (Ryc. 9). Niestety, na chwil\u0119 obecn\u0105 API Azure nie pozwala na tworzenie kontener\u00f3w z poziomu LogicApp. Mo\u017cna to jednak zrobi\u0107 np. poprzez wywo\u0142anie FunctionApp, kt\u00f3ra to zadanie jest w stanie wykona\u0107. Automatyzacja tego procesu jest jednak poza zakresem niniejszego artyku\u0142u.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-9-1.png\"><img decoding=\"async\" width=\"789\" height=\"613\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-9-1.png\" alt=\"Ryc. 9 Dodanie kontenera e-mails do Blob Storage\" class=\"wp-image-18556\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-9-1.png 789w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-9-1-300x233.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-9-1-768x597.png 768w\" sizes=\"(max-width: 789px) 100vw, 789px\" \/><\/a><figcaption>Ryc. 9 Dodanie kontenera e-mails do Blob Storage<\/figcaption><\/figure><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Przetestowanie dzia\u0142ania aplikacji<\/h3>\n\n\n\n<p>W tym momencie jeste\u015bmy gotowi do przetestowania zapisu wiadomo\u015bci e-mail poprzez wys\u0142anie maila na wcze\u015bniej utworzon\u0105 skrzynk\u0119 (pami\u0119tajmy jednak, aby tytu\u0142 zawiera\u0142 wyra\u017cenie \u201eORDERID: \u201d). Wiadomo\u015b\u0107 powinna znale\u017a\u0107 si\u0119 we wcze\u015bniej utworzonym kontenerze emails.<\/p>\n\n\n\n<p>Poprawne (b\u0105d\u017a nie) wykonanie naszej aplikacji mo\u017cemy podgl\u0105dn\u0105\u0107 w sekcji Overview stworzonej LogicApp. Klikaj\u0105c w konkretny wiersz, uzyskamy dost\u0119p do szczeg\u00f3\u0142\u00f3w wykonania \u2013 jest to r\u00f3wnie\u017c spos\u00f3b na debuggowanie (Ryc. 10).<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-10-1024x365-1.png\"><img decoding=\"async\" width=\"1024\" height=\"365\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-10-1024x365-1.png\" alt=\"Ryc. 10 Status wykona\u0144 aplikacji LogicApp\" class=\"wp-image-18558\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-10-1024x365-1.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-10-1024x365-1-300x107.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-10-1024x365-1-768x274.png 768w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption>Ryc. 10 Status wykona\u0144 aplikacji LogicApp<\/figcaption><\/figure><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Odczytywanie wiadomo\u015bci w solucji z testami E2E<\/h3>\n\n\n\n<p>Na chwil\u0119 obecn\u0105 mamy zapisan\u0105 wiadomo\u015b\u0107 w Azure Storage Account, natomiast chcieliby\u015bmy w \u0142atwy i szybki spos\u00f3b, m\u00f3c t\u0105 wiadomo\u015b\u0107 odczyta\u0107 w naszej solucji z testami E2E (w solucji testowej wystarczy wykona\u0107 najzwyklejsze zapytanie HTTP GET). Wobec tego stw\u00f3rzmy FunctionApp, kt\u00f3re posiada\u0107 b\u0119dzie nast\u0119puj\u0105ce funkcjonalno\u015bci:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Mo\u017cliwo\u015b\u0107 odczytu bloba na podstawie jego nazwy.<\/li><li>Czyszczenie kontenera z blob\u00f3w starszych ni\u017c okre\u015blono.<\/li><\/ul>\n\n\n\n<p>W Visual Studio stw\u00f3rzmy now\u0105 solucj\u0119 oraz dodajmy do niej projekt Azure Functions (nast\u0119pnie wybierzmy .NET Core 3 LTS jako framework aplikacji oraz Http Trigger jako spos\u00f3b wyzwolenia akcji) (Ryc. 11):<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-11-1-1024x259-1.png\"><img decoding=\"async\" width=\"1024\" height=\"259\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-11-1-1024x259-1.png\" alt=\"Ryc. 11 Tworzenie FunctionApp w Visual Studio\" class=\"wp-image-18560\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-11-1-1024x259-1.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-11-1-1024x259-1-300x76.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-11-1-1024x259-1-768x194.png 768w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption>Ryc. 11 Tworzenie FunctionApp w Visual Studio<\/figcaption><\/figure><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Kod funkcji pozwalaj\u0105cy na pobranie wiadomo\u015bci e-mail<\/h3>\n\n\n\n<p>Kod funkcji, umo\u017cliwiaj\u0105cy pobranie wiadomo\u015bci e-mail z bloba, wygl\u0105da nast\u0119puj\u0105co:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n&#x5B;FunctionName(&quot;get-email&quot;)]\n        public async Task&amp;lt;IActionResult&gt; Run(\n            &#x5B;HttpTrigger(AuthorizationLevel.Function, &quot;get&quot;, Route = null)] HttpRequest req,\n            &#x5B;Blob(Constants.EmailContainerName, FileAccess.Read)] CloudBlobContainer myBlobContainer,\n            ILogger log,\n            IBinder binder)\n        {\n            log.LogInformation(&quot;C# HTTP trigger GetEmail function starts processing a request.&quot;);\n            await myBlobContainer.CreateIfNotExistsAsync();\n            string orderId = req.Query&#x5B;&quot;orderid&quot;];\n            if (string.IsNullOrEmpty(orderId))\n                return new BadRequestObjectResult(&quot;Please pass an orderId on the query string&quot;);\n            var result = await _blobRepository.ReadEmail(binder, orderId);\n            return new OkObjectResult(result);\n        }\n<\/pre><\/div>\n\n\n<p>Natomiast kod funkcji odpowiedzialnej za czyszczenie kontenera jest nast\u0119puj\u0105cy (tym razem jest to funkcja wyzwalana poprzez Timer Trigger konfigurowany przy pomocy wyra\u017cenia CRON):<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n&#x5B;FunctionName(&quot;cleanup-emails&quot;)]\n        public static async Task Run(&#x5B;TimerTrigger(&quot;0 50 * * * *&quot;)] TimerInfo myTimer,\n            &#x5B;Blob(Constants.EmailContainerName, FileAccess.ReadWrite)] CloudBlobContainer myBlobContainer,\n            ILogger log)\n        {\n            log.LogInformation($&quot;{nameof(CleanupEmails)} Timer trigger function executed at: {DateTime.Now}&quot;);\n\n            await myBlobContainer.CreateIfNotExistsAsync();\n\n            BlobContinuationToken blobContinuationToken = null;\n            var blobList = await myBlobContainer.ListBlobsSegmentedAsync(blobContinuationToken);\n            var cloudBlobList = blobList.Results\n                .Select(blb =&gt; blb as ICloudBlob)\n                .Where(blb =&gt; blb.Properties.LastModified &amp;lt; DateTime.Now.AddHours(-1));\n\n            foreach (var item in cloudBlobList)\n            {\n                await item.DeleteIfExistsAsync();\n            }\n        }\n<\/pre><\/div>\n\n\n<p>My\u015bl\u0119, \u017ce kod obydwu funkcji jest samoopisuj\u0105cy si\u0119, wi\u0119c nie b\u0119d\u0119 zag\u0142\u0119bia\u0142 si\u0119 w jego szczeg\u00f3\u0142y. Pe\u0142ne rozwi\u0105zanie mo\u017cna znale\u017a\u0107&nbsp;<a href=\"https:\/\/github.com\/mpyssii\/EmailTests\" rel=\"nofollow\" >na moim GitHubie<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Ostatnie kroki<\/h3>\n\n\n\n<p>Ostatnim krokiem jest stworzenie Publish Profile oraz deploy FunctionApp z Visual Studio do Azure. Kliknijmy prawym klawiszem na projekt zawieraj\u0105cy funkcje i wybierzmy Publish oraz kolejno Azure, Azure FunctionApp (Windows) i w okienku konfiguracyjnym Publish zaznaczmy nasz\u0105 subskrypcj\u0119, Resource Group oraz stworzon\u0105 wcze\u015bniej FunctionApp.<\/p>\n\n\n\n<p>Kliknijmy Finish, co pozwoli nam utworzy\u0107 Publish Profile. Wtedy pozostaje ju\u017c sam deploy, kt\u00f3ry b\u0119dzie mia\u0142 miejsce po klikni\u0119ciu przycisku Publish. Przy pomocy narz\u0119dzia Postman, kt\u00f3re pozwala na wykonanie zapyta\u0144 m. in. do endpoint\u00f3w FunctionApp, zweryfikujmy poprawno\u015b\u0107 dzia\u0142ania aplikacji (Ryc. 12):<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-12-1024x609-1.png\"><img decoding=\"async\" width=\"1024\" height=\"609\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-12-1024x609-1.png\" alt=\"Ryc. 12 Odpowied\u017a z endpointa pobieraj\u0105cego zawarto\u015b\u0107 bloba\" class=\"wp-image-18562\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-12-1024x609-1.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-12-1024x609-1-300x178.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-12-1024x609-1-768x457.png 768w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption>Ryc. 12 Odpowied\u017a z endpointa pobieraj\u0105cego zawarto\u015b\u0107 bloba<\/figcaption><\/figure><\/div>\n\n\n\n<p>Na powy\u017cszej grafice wida\u0107, \u017ce byli\u015bmy w stanie odczyta\u0107 zapisan\u0105 wiadomo\u015b\u0107 e-mail sk\u0142adaj\u0105c\u0105 si\u0119 z tematu, separatora oraz tre\u015bci wiadomo\u015bci. Dzi\u0119ki temu jeste\u015bmy w stanie zweryfikowa\u0107, co tylko nam si\u0119 podoba w ramach test\u00f3w E2E.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Podsumowanie<\/h2>\n\n\n\n<p>Platforma Azure udost\u0119pnia bardzo du\u017c\u0105 ilo\u015b\u0107 prostych rozwi\u0105za\u0144, kt\u00f3re jeste\u015bmy w stanie u\u017cy\u0107 bardzo szybko i z \u0142atwo\u015bci\u0105 zintegrowa\u0107 mi\u0119dzy sob\u0105. Jednymi z najbardziej podstawowych s\u0105 LogicApps oraz FunctionApps, kt\u00f3re zosta\u0142y przedstawione w opisanym wy\u017cej przyk\u0142adzie.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Bonus<\/h2>\n\n\n\n<p>Jako dodatek i u\u0142atwienie, we&nbsp;<a href=\"https:\/\/github.com\/mpyssii\/EmailTests\" rel=\"nofollow\" >wspomnianym wcze\u015bniej ju\u017c repozytorium<\/a>&nbsp;zamie\u015bci\u0142em ARM template z opisem infrastruktury u\u017cytej w moim przyk\u0142adzie oraz skrypt PowerShell, pozwalaj\u0105cy na \u0142atwy deploy ARM template.<\/p>\n\n\n\n<p>Po sklonowaniu repozytorium, skonfigurujmy odpowiednie parametry w pliku template.parameters.json pami\u0119taj\u0105c jednocze\u015bnie, \u017ce w tym przypadku resourceName b\u0119dzie globalnym adresem FunctionApp, wobec czego musi by\u0107 unikalne.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n&quot;parameters&quot;: {\n        &quot;resourceGroupName&quot;: {\n            &quot;value&quot;: &quot;sii-email-notifications&quot;\n        },\n        &quot;resourceGroupLocation&quot;: {\n            &quot;value&quot;: &quot;westeurope&quot;\n        },\n        &quot;resourceName&quot;: {\n            &quot;value&quot;: &quot;sii-email-notifications-test&quot;\n        },\n        &quot;resourceLocation&quot;: {\n            &quot;value&quot;: &quot;westeurope&quot;\n        }\n    }\n<\/pre><\/div>\n\n\n<p>Kolejnym krokiem jest wykonanie skryptu PowerShell (wymagana jest wcze\u015bniejsza instalacja modu\u0142\u00f3w Az.Accounts oraz Az.Resources)<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n.\\Deploy.ps1 -useParameters\n<\/pre><\/div>\n\n\n<p>kt\u00f3ry pozwoli na deploy ARM template do bie\u017c\u0105cej subskrypcji. Pami\u0119tajmy jednak, \u017ce sama infrastruktura to w tym przypadku zbyt ma\u0142o. Trzeba wykona\u0107 r\u00f3wnie\u017c dodatkowe kroki w celu poprawnej konfiguracji pe\u0142nej aplikacji:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Uwierzytelnienie po\u0142\u0105czenia do Outlook 365 w LogicApp (Ryc. 13).<\/li><li>Uwierzytelnienie po\u0142\u0105czenia do Azure Storage Account w LogicApp (zgodnie z przedstawionym wcze\u015bniej opisem).<\/li><li>Utworzenie kontenera emails w Blob Storage (zgodnie z przedstawionym wcze\u015bniej opisem).<\/li><li><\/li><\/ul>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-13-1.png\"><img decoding=\"async\" width=\"754\" height=\"340\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-13-1.png\" alt=\"Ryc. 13 Konfiguracja po\u0142\u0105czenia Office 365\" class=\"wp-image-18564\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-13-1.png 754w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Ryc.-13-1-300x135.png 300w\" sizes=\"(max-width: 754px) 100vw, 754px\" \/><\/a><figcaption>Ryc. 13 Konfiguracja po\u0142\u0105czenia Office 365<\/figcaption><\/figure><\/div>\n\n\n\n<p>Stworzony skrypt Deploy.ps1 umo\u017cliwia r\u00f3wnie\u017c deploy do dowolnej subskrypcji. Wystarczy tylko doda\u0107 parametr -subscriptionId &lt;subscriptionId> do wywo\u0142ania skryptu.<\/p>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;12542&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;25&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;4.8&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;11&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;4.8\\\/5 ( votes: 25)&quot;,&quot;size&quot;:&quot;18&quot;,&quot;title&quot;:&quot;Azure LogicApp oraz FunctionApp \u2013 umo\u017cliwienie weryfikacji wysy\u0142ki i tre\u015bci wiadomo\u015bci e-mail jako jeden z element\u00f3w test\u00f3w E2E&quot;,&quot;width&quot;:&quot;133.7&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} ( {votes}: {count})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 133.7px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 14.4px;\">\n            4.8\/5 ( votes: 25)    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>W dzisiejszych czasach chyba \u017caden software developer nie ma w\u0105tpliwo\u015bci w kwestii potrzeby testowania wytworzonego przez siebie kodu.&nbsp;Podczas prac nad &hellip; <a class=\"continued-btn\" href=\"https:\/\/sii.pl\/blog\/azure-logicapp-oraz-functionapp-umozliwienie-weryfikacji-wysylki-i-tresci-wiadomosci-e-mail-jako-jeden-z-elementow-testow-e2e\/\">Continued<\/a><\/p>\n","protected":false},"author":329,"featured_media":12565,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_editorskit_title_hidden":false,"_editorskit_reading_time":0,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","inline_featured_image":false,"footnotes":""},"categories":[1314],"tags":[287,129,961,146],"class_list":["post-12542","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development-na-twardo","tag-software-development","tag-c","tag-azure","tag-testing"],"acf":[],"aioseo_notices":[],"republish_history":[],"featured_media_url":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/01\/Azure-LogicApp-oraz-FunctionApp.png","category_names":["Development na twardo"],"_links":{"self":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/12542"}],"collection":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/users\/329"}],"replies":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/comments?post=12542"}],"version-history":[{"count":2,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/12542\/revisions"}],"predecessor-version":[{"id":18566,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/12542\/revisions\/18566"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media\/12565"}],"wp:attachment":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media?parent=12542"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/categories?post=12542"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/tags?post=12542"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}