Programowanie Dynamics 365 w języku TypeScript z wykorzystaniem XrmDefinitelyTyped

Wprowadzenie

W ostatnich latach JavaScript stał się jednym z najpopularniejszych języków programowania (7. Miejsce w rankingu TIOBE we wrześniu 2017). Przeszedł on długą drogę, począwszy od obsługi prostych skryptów na stronach internetowych, a skończywszy na w pełni funkcjonalnych i samodzielnych aplikacjach, które odpowiadają za logikę wykonywaną zarówno po stronie klienta jak i serwera (przykład: node.js). Niestety z uwagi na pewne specyficzne cechy oraz niekonsekwencje w projekcie samego języka JavaScript ma zarówno zagorzałych zwolenników, jak i przeciwników (nie jestem pewien, czy liczba tych drugich nie jest przypadkiem większa O_O). Natomiast język TypeScript powstał w celu rozwiązania problemów, z którymi od dawna borykają się programiści JavaScript. Jego adopcja przebiega całkiem sprawnie i wykorzystywany jest on już przez m.in firmę Google (patrz.: najnowsze wersje biblioteki Angular).

Język TypeScript może być również wykorzystywany przez programistów systemu Dynamics 365, dla których JavaScript był do tej pory podstawowym narzędziem, wykorzystywanym do tworzenia kodu obsługującego zdarzenia na formatkach systemu i uruchamianego w przeglądarce (bądź w kliencie mobilnym) użytkownika. Dodatkowym ułatwieniem w przypadku omawianego języka jest dostępność narzędzi, pozwalających na wygenerowanie „silnie-typowanego” modelu interfejsów, reprezentujących model encji systemu Dynamics 365. Jednym z tego typu narzędzi jest XrmDefinitelyTyped, które zostało stworzone przez duńską firmę Delegate A/S.

Poniżej znajdziecie garść informacji na temat wykorzystania wspomnianego powyżej duetu (TypeScript + XrmDefinitelyTyped) do programowania formatek systemu Dynamics 365.

XrmDefinitelyTyped

Czym jest XrmDefinitelyTyped? Mówiąc najprościej – jest to odpowiednik narzędzia CrmSvcUtil z pakietu CRM SDK. Zamiast klas C# generuje on jednak zestaw interfejsów, które znacząco ułatwiają pisanie kodu w języku TypeScript. Tworzony kod w języku TypeScript będziemy mogli po skompilowaniu uruchomić na platformie Dynamics 365. Po uruchomieniu XrmDefinitelyTyped  generuje stosowny model na podstawie metadanych encji z systemu Dynamics 365 (w tym również „silnie typowane” zestawy opcji). W raz z nim dostarczane są również biblioteki, które ułatwiają korzystanie z usług sieciowych Dynamics 365 (zarówno „starej” usługi OData w wersji 2.0 jak i najnowszej WebAPI). Jak już wspominałem powyżej, omawiane narzędzie zostało stworzone przez duńską firmę Delegate. Udostępniono je w postaci kodu źródłowego, który znajdziecie na GitHubie oraz w postaci pakietu Nuget.

Po zainstalowaniu wspomnianego pakietu w naszym projekcie zostanie utworzony folder „XrmDefinitelyTyped” zawierający pakiet narzędzi, które posłużą nam do wygenerowania stosownych interfejsów.

Pierwszą czynnością którą musimy wykonać jest konfiguracja pliku XrmDefinitelyTyped.exe.config. Po zainstalowaniu pakietu Nuget na on następującą postać:

Znaczenie kluczy: „ulr”, „username” oraz „password” jest dość oczywiste. „Out” określa położenie folderu w którym znajdą się wygenerowane przez narzędzie pliki *.ts. „Solutions” oraz „Entities” określają dla których (odpowiednio) solucji lub encji Dynamics 365 powinny być wygenerowane interfejsy. Natomiast parametr „jsLib” określa położenie biblioteki dg.xrmquery.web.js, która ułatwia korzystanie z webowego API Dynamics 365 i również zostanie dodana do projektu w momencie uruchomienia narzędzia.

Pełną listę dostępnych opcji konfiguracyjnych znajdziecie pod adresem: http://delegateas.github.io/Delegate.XrmDefinitelyTyped/tool-usage.html

Kolejnym krokiem w procesie jest uruchomienie narzędzia: XrmDefinitelyTyped.exe. Wygeneruje ono stosowne skrypty zgodnie z ustawieniami w pliku konfiguracyjnym. Niestety generowane przez program pliki nie są automatycznie dodawane do projektu Visual Studio. Aby dodać je do solucji musimy nacisnąć przycisk „Show all files” w Solution Explorerze i następnie dla wygenerowanych plików/folderów wybrać opcję „Include in project”, która to jest dostępna pod prawym przyciskiem myszy.

Ostatnim wymaganym krokiem przed rozpoczęciem developmentu będzie dodanie pliku tsconfig.json do solucji. Wewnątrz niego możemy zdefiniować położenie folderu, w którym będą znajdować się generowane bilbioteki JavaScript, wersję języka dla wynikowych plików oraz wiele innych parametrów. Szczegółowe informacje na temat możliwości konfiguracji za pomocą pliku tsconfig.json znajdziecie pod adresem: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. W najprostszej wersji, która jest wymagana do rozpoczęcia developmentu, może mieć on następującą postać:

Organizacja projektu

W przypadku pierwszego projektu wykorzystującego TypeScript nad którym przyszło mi pracować, zdecydowaliśmy się na następującą organizację kodu.



W projekcie utworzony został folder TypeScipts, który zawiera następujące podfoldery:

  1. Common – biblioteki współdzielone, wykorzystywane na wielu formatach.
  2. Entities – biblioteki TS, wykorzystywane na formatkach poszczególnych encji.
  3. XRM – biblioteki wygenerowane przez XrmDefinitelyTyped.

Przyjrzyjmy się strukturze poszczególnych bibliotek.

Plik DataServices.ts odpowiada (jak zapewnie się domyślacie) za dostęp do danych w systemie Dynamics 365. Zawiera on stosowne interfejsy oraz klasy, które umożliwiają wykonywanie operacji na danych w systemie Dynamics 365 za pomocą endpointu WebAPI. Omawiana biblioteka korzysta z przestrzeni nazw XrmQuery, pochodzącej z pakietu XrmDefinitelyTyped.

Przykładowy plik DataServices.ts, zawierający klasę OpportunityDataServices, która to pozwala na pobranie z systemu wszystkich szans sprzedaży dla danego klienta, może wyglądać w następujący sposób:

Biblioteka Common.ts „opakowuje” funkcje, które są udostępniane za pomocą standardowej biblioteki Dynamics 365 – Xrm.Page. Poniższy przykład zawiera klasę Form, która udostępnia funkcje: „getId” oraz „getValue”:

W przypadku projektu, w którym chcielibyśmy wykorzystywać testy jednostkowe warto byłoby „ointerfejsować” omawianą klasę. Dzięki temu będziemy w stanie zastępić ją w przypadku uruchomienia testu jednostkowego stosownym „mockiem”.

Ostatnią biblioteką w przykłądzie, którą chciałbym omówić jest plik Account.ts. Jego główną odpowiedzialnością jest obsługa zdarzeń na formatkach Dynamics 365. Przykładowa struktura tego modułu może wyglądać w następujący sposób:

W powyższym przykładzie moduł Foo.Dyn365.Account zawiera następujące klasy:

EventHandlers – klasa udostępniająca funkcje, które obsługują zdarzenia, zachodzące na formatkach systemu Dynamics 365

Ribbon – klasa udostępniająca funkcje wykonywane po naciśnięciu przycisków umieszczanych we „wstążce” systemu

Form – klasa zawierająca logikę obsługi formatki encji Account.

Helper  – klasa zawierająca niepubliczną logikę wykorzystywaną przez klasę Form.

Oczywiście opisana powyżej stuktura jest jedynie przykładem. Nie ma żadnych przeciwskazań, żeby w czasie realizacji własnych projektów zdecydować się na inny sposób uporządkowania modułów i plików źródłowych.

Pliki wynikowe kompilowane są do folderu „foo_\scripts”. Nazwę folderu „foo_” zastępujemy oczywiście prefiksem, którego używamy w naszym projekcie.

Uruchomienie kodu

Ostatnim krokiem naszego procesu powinien być deployment kodu do systemu oraz uruchomienie go. W projekcie na którym aktualnie pracuję jest to proces składający się z kilku kroków.

Po skompilowaniu plików TS  w stosownych folderach pojawią się wynikowe biblioteki w języku JavaScript. Możemy dodać je do projektu Visual Studio za pomocą opcji „Include in Project”.

Niestety, aktualny silnik odpowiedzialny za interfejs użytkownika Dynamics 365 („TurboForms”) działa mocno niedeterministycznie w temacie ładowania bibliotek JavaScript dla poszczególnych typów formatek (a często również dla standardowych formatek tego samego typu, ale dla różnych encji). Dlatego pierwszym krokiem w procesie deploymentu bibliotek JavaScript do systemu powinno być stworzenie tzw. „bundle’a”, tj. pliku zawierającego wszystkie zależne biblioteki wymagane do prawidłowego działania formatki. Do tworzenia wspomnianych pakietów wykorzystujemy rozszerzenie Visual Studio o nazwie Visual Studio Bundler & Minifier, o którym pisałem już trochę w tym miejscu. W pliku konfiguracyjnym narzędzia podajemy nazwy plików wynikowych JavaScript, wygenerowanych przez kompilator TypeScript. Pliki JavaScript, które będziemy łączyć muszą być oczywiście częścią projektu Visual Studio. Ostatnim krokiem jest deployment wynikowego „bundle’a” do Dynamics 365 (ręcznie lub za pomocą dowolnego, preferowanego przez nas narzędzia). W końcu …taadaaamm!!! Możemy podziwiać efekty naszej pracy w systemie 😊.

Total Views: 2227 ,
This Article Has 3 Comments
  1. Pingback: dotnetomaniak.pl

  2. G

    Fajny wpis, tylko nie rozumiem tego zdania: „Niestety, aktualny silnik odpowiedzialny za interfejs użytkownika Dynamics 365 („TurboForms”) działa mocno niedeterministycznie w temacie ładowania bibliotek JavaScript dla poszczególnych typów formatek” – masz na to jakiś przykład? Nigdy mi się nie zdarzyło, żeby były z tym problemy. Skrypty ładują się wprawdzie asynchronicznie, ale jeżeli inne biblioteki wywołujesz dopiero w OnLoad formularza, to powinno wszystko działać bez problemu (myślę, że myląca jest tutaj dokumentacja, która mówi, że jak będziemy się w jednym skrypcie odwoływać do innego to się wywali, ale tu chodzi o odwołanie podczas ładowania skryptów, a nie podczas wywoływania zdarzeń na formularzu). Zresztą znam kilkanaście projektów, które na pewno by nie działały, gdyby tak nie było.

    • PG

      Na omawiany problem napotkałem w kilku miejscach:

      1. Klient mobilny
      2. Formatki typu „Quick Create”.
      3. Standardowa formatka encji Quote w systemie, który był migrowany z wersji 2011/2013/2015 (nie jestem pewien, czy ma to jakieś znaczenie).

      Wspomniane sytuacje dotyczą systemu w wersji 8.0. Przyznam, że nie testowałem tego zachowania w wersji 8.2, ponieważ przestawiłem się wówczas na model deploymentu: „Wiele bibliotek JS w VS -> (merge) -> 1 web resource w Dyn CRM/365”, który zawsze działa, więc wspomniany problem przestał mnie zupełnie zajmować.

Comments are now closed.