Die Integration von Microsoft Dynamics 365 in Jira Data Center
Sie möchten Microsoft Dynamics und Ihr Atlassian Jira miteinander verbinden? Lesen Sie, wie uns die Integration gelungen ist.
Die Ablösung unseres bisherigen Customer-Relationship-Management (CRM) Systems durch Microsoft Dynamics war ein großes Unterfangen. Ein Projektteam aus Mitgliedern verschiedener Abteilungen stellte sicher, dass es einen möglichst reibungslosen Übergang für unsere Mitarbeiter gab. Ein wichtiger Aspekt dabei war, dass unsere Kollegen weiterhin wie bisher gewohnt ihre Kundendaten direkt in den Jira-Tickets nutzen können, z.B. die Kontaktdaten eines Kunden oder wichtige vertriebliche Informationen zu Aufträgen oder Lizenzen. Da wir keine für unsere Zwecke passende Marketplace-App gefunden haben, entwickelten wir eine eigene Jira-App, welche die Kommunikation mit Dynamics übernimmt.
Im Folgenden möchte ich Ihnen aufzeigen, welche Voraussetzungen dafür erfüllt sein mussten, wie die technische Umsetzung erfolgte und welchen Herausforderungen wir uns dabei stellen mussten.
Bevor es losgehen kann
Damit unsere Jira-App die Microsoft Dynamics Services nutzen kann, ist eine Registrierung der Anwendung im Microsoft Azure Portal nötig.
Dort bekommt die Jira-Anwendung eindeutige IDs zugeordnet. Dadurch wird sichergestellt, dass Dynamics Anfragen von unserem Jira akzeptiert. Denn die Kommunikation zwischen den beiden Anwendungen läuft ausschließlich über die Common Data Service Web API von Dynamics, eine RESTful API. Da die Web API auf offenen Standards aufbaut, kann sie mit verschiedenen Programmiersprachen und Plattformen verwendet werden. In unserem Fall erfolgen die Anfragen an Dynamics durch eine JavaScript-Lösung.
Komponenten der Jira-App
Unsere Jira-App besteht aus zwei Teilen: einem Konfigurationsteil (umgesetzt mittels Atlassian SDK) und dem Kommunikationsteil (umgesetzt mittels JavaScript).
Im Konfigurationsteil erzeugen wir eine Konfigurationsseite in der Jira-Administration und tragen dort die im vorherigen Abschnitt erwähnten eindeutigen Identifikatoren aus dem Azure Portal ein. Somit kann sich unsere App später bei Dynamics authentifizieren.
Der JavaScript-Part ist für die Kommunikation zwischen Jira und Dynamics zuständig und stellt die Verbindung zwischen beiden Welten her. Wir haben uns für eine client-seitige Umsetzung via JavaScript entschieden, um Berechtigungsprüfungen im Client zu ermöglichen und die Last auf dem Jira-Server so gering wie möglich zu halten.
Authentifizierung bei Dynamics
Damit die Dynamics-Daten in Jira genutzt werden können, muss sich der User zunächst via Popup-Dialog in sein Microsoft-Online-Konto einloggen und authentifizieren. Dabei wird nur OAuth als Authentifizierung unterstützt. Als Antwort erhält Jira einen Token. Dieser wird nun bei jeder Anfrage an Dynamics im Response-Header mitgeschickt. Solange der Token gültig ist, erfolgt die Anmeldung bei Dynamics im Hintergrund, ohne dass der Nutzer sich erneut einloggen muss. Für das Authentifizierungshandling verwenden wir die Microsoft Authentication Library und im speziellen dessen JavaScript-Framework MSAL.js.
Daten abfragen
Die Abfragen der Daten von Dynamics erfolgen mithilfe der oben bereits erwähnten RESTful Common Data Service Web API. Da es sich um eine recht komplexe API handelt, stellt Microsoft eine ausführliche Dokumentation mit vielen Beispielen zur Verfügung. Grundlage bilden sogenannte Entitäten, in unserem Fall z.B. account (Firmendaten) und contact (Personendaten). Um beispielsweise den Namen und die vollständige Adresse einer bestimmten Firma abzufragen, wird folgende GET-Anfrage an die Dynamics Web API geschickt: https://<dynamics-crm-url>/api/data/v9.1/accounts(8c48716a-05b9-e911-a827-000d3ab08ce9)?$select=name,address1_composite
Dabei stellt der alphanumerische Code 8c48716a-05b9-e911-a827-000d3ab08ce9 die eindeutige ID (accountid) des gewünschten Datensatzes in Dynamics dar. Mit $select= können die angeforderten Daten auf bestimmte Attribute eingeschränkt werden, in unserem Fall auf den Namen und die Adresse. Als Antwort erhält unser Jira die angeforderten Attribute im JSON-Format:
{
"@odata.context": "https://<dynamics-crm-url>/api/data/v9.1/$metadata#accounts(name,address1_composite)/$entity",
"@odata.etag": "W/\"5822819\"",
"name": "Communardo GmbH",
"address1_composite": "Kleiststr. 10a\r\n\r\n01129 Dresden\r\nDeutschland",
"address1_line1": "Kleiststr. 10a",
"accountid": "8c48716a-05b9-e911-a827-000d3ab08ce9",
"address1_city": "Dresden",
"address1_postalcode": "01129",
"address1_country": "Deutschland"
}
Anfragen bündeln
Durch diese Komplexität sind teilweise bis zu vier Abfragen an die Dynamics Web API nötig, um alle konfigurierten Attribute eines Firmen-Datensatzes abzufragen. Um die Anzahl an Anfragen zu verringern, bietet die Web API die Möglichkeit einer sogenannten Batch-Operation, welche wir auch in unserer App nutzen. Dabei werden mehrere Anfragen in einer einzigen HTTP-Anfrage gruppiert. Laut Dokumentation können Batch-Anfragen bis zu 1000 individuelle Anfragen enthalten. Einschränkungen dabei sind, dass die URLs für GET-Anfragen innerhalb der Batch-Anfrage auf 32768 Zeichen limitiert ist. Weiterhin darf eine Batch-Anfrage keine anderen Batch-Anfragen enthalten.
Batch-Anfragen werden als POST-Anfrage via https://<dynamics-crm-url>/api/data/v9.1/$batch an Dynamics gesendet. Die eigentlichen Anfragen werden im Request Body als Text mitgeschickt, wie im Folgenden anhand von vier GET-Anfragen zu sehen:
--1600928734395
Content-Type: application/http
Content-Transfer-Encoding:binary
GET <dynamics-crm-url>/api/data/v9.1/accounts(8c48716a-05b9-e911-a827-000d3ab08ce9) HTTP/1.1
Host: localhost:8060
--1600928734395
Content-Type: application/http
Content-Transfer-Encoding:binary
GET <dynamics-crm-url>/api/data/v9.1/EntityDefinitions(LogicalName='account') eq 'address1_composite' or LogicalName eq 'address1_fax' or LogicalName eq 'com_business_line' or LogicalName eq 'com_supportdatenblatt' or LogicalName eq 'creditlimit' or LogicalName eq 'new_sicherheitsstufe' or LogicalName eq 'numberofemployees' or LogicalName eq 'com_auftraege' or LogicalName eq 'com_supportvertraege' or LogicalName eq 'com_vertriebsbeauftragter' or LogicalName eq 'new_customerservicemanager' or LogicalName eq 'parentaccountid') HTTP/1.1
Host: localhost:8060
--1600928734395
Content-Type: application/http
Content-Transfer-Encoding:binary
GET <dynamics-crm-url>/api/data/v9.1/EntityDefinitions(LogicalName='account')/Attributes/Microsoft.Dynamics.CRM.PicklistAttributeMetadata eq 'address1_composite' or LogicalName eq 'address1_fax' or LogicalName eq 'com_business_line' or LogicalName eq 'com_supportdatenblatt' or LogicalName eq 'creditlimit' or LogicalName eq 'new_sicherheitsstufe' or LogicalName eq 'numberofemployees' or LogicalName eq 'com_auftraege' or LogicalName eq 'com_supportvertraege' HTTP/1.1
Host: localhost:8060
--1600928734395
Content-Type: application/http
Content-Transfer-Encoding:binary
GET <dynamics-crm-url>/api/data/v9.1/accounts(8c48716a-05b9-e911-a827-000d3ab08ce9) HTTP/1.1
Host: localhost:8060
--1600928734395--
Als Antwort erhält man ebenfalls ein Text-Dokument, welches geparst und als JSON abgespeichert werden kann, z.B.:
--batchresponse_6f7cbc73-6da4-478e-85dc-11a3b5975f27
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1200OK
ETag: W/"18333368"
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; odata.metadata=minimal; odata.streaming=true
OData-Version: 4.0
{"@odata.context":"https://<dynamics-crm-url>/api/data/v9.1/$metadata#accounts(com_jirasearchfield,address1_composite,address1_fax,com_business_line,com_supportdatenblatt,creditlimit,new_sicherheitsstufe,numberofemployees,com_auftraege,com_supportvertraege)/$entity","@odata.etag":"W/\"18333368\"","com_jirasearchfield":"Communardo GmbH","address1_composite":"Kleiststr.\r\n\r\n01129 Dresden\r\nDeutschland","address1_fax":null,"com_business_line":181410000,[...]}
--batchresponse_6f7cbc73-6da4-478e-85dc-11a3b5975f27
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1200OK
Content-Type: application/json; odata.metadata=minimal; odata.streaming=true
OData-Version: 4.0
{"@odata.context":"https://<dynamics-crm-url>/api/data/v9.1/$metadata#EntityDefinitions(LogicalName,Attributes(LogicalName,AttributeType,DisplayName))/$entity","LogicalName":"account",
"MetadataId":"70816501-edb9-4740-a16c-6a5efbc05d84","Attributes":[{"@odata.type":"#Microsoft.Dynamics.CRM.PicklistAttributeMetadata","LogicalName":"com_business_line","AttributeType":"Picklist",
"MetadataId":"7af6128f-25e1-ea11-a813-000d3ad8b29c","DisplayName":{"LocalizedLabels":[{"Label":"Business Line (Supportvertrag)","LanguageCode":1031,"IsManaged":false,"MetadataId":"7ff6128f-25e1-ea11-a813-000d3ad8b29c","HasChanged":null}],"UserLocalizedLabel":{"Label":"Business Line (Supportvertrag)","LanguageCode":1031,"IsManaged":false,"MetadataId":"7ff6128f-25e1-ea11-a813-000d3ad8b29c","HasChanged":null}}},[...]]}
--batchresponse_6f7cbc73-6da4-478e-85dc-11a3b5975f27
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1200OK
Content-Type: application/json; odata.metadata=minimal; odata.streaming=true
OData-Version: 4.0
{"@odata.context":"https://<dynamics-crm-url>/api/data/v9.1/$metadata#EntityDefinitions('account')/Attributes/Microsoft.Dynamics.CRM.PicklistAttributeMetadata(LogicalName,OptionSet(Options),GlobalOptionSet(Options))","value":[{"LogicalName":"com_business_line","MetadataId":"7af6128f-25e1-ea11-a813-000d3ad8b29c","OptionSet":{"MetadataId":"81f6128f-25e1-ea11-a813-000d3ad8b29c","Options":[{"Value":181410000,"Color":"#0000ff","IsManaged":false,"ExternalValue":"","ParentValues":[],"MetadataId":null,"HasChanged":null,"Label":{"LocalizedLabels":[{"Label":"Atlassian Solutions","LanguageCode":1031,"IsManaged":false,"MetadataId":"7bf6128f-25e1-ea11-a813-000d3ad8b29c","HasChanged":null}],[...]]}}]}
--batchresponse_6f7cbc73-6da4-478e-85dc-11a3b5975f27
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1200OK
ETag: W/"18333368"
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; odata.metadata=minimal; odata.streaming=true
OData-Version: 4.0
{"@odata.context":"https://<dynamics-crm-url>/api/data/v9.1/$metadata#accounts(com_vertriebsbeauftragter,new_CustomerServiceManager,parentaccountid,com_vertriebsbeauftragter(),new_CustomerServiceManager(),parentaccountid())/$entity",
"@odata.etag":"W/\"18333368\"","accountid":"8c48716a-05b9-e911-a827-000d3ab08ce9","com_vertriebsbeauftragter":{"@odata.etag":"W/\"17591002\"","ownerid":"ab719acb-297f-ea11-a813-000d3ab95ec9",
"address1_fax":"+49 (351) 833 82-299","organizationid":"d47b658d-6df6-43f6-9ef4-8bdac7d6b4a7","address1_postofficebox":null,"accessmode":0,"address1_upszone":null,"photourl":null,"address1_latitude":null,
"address1_shippingmethodcode":1,"address1_utcoffset":null,"_createdonbehalfby_value":null,"homephone":null,"skills":null,"emailrouteraccessapproval":1,"address2_latitude":null,"address1_longitude":null,"governmentid":null,"address2_longitude":null,"_createdby_value":null,"defaultfilterspopulated":false,"address1_telephone3":null,"mobilephone":null,"address2_fax":null,"preferredaddresscode":1,"address2_city":null,
"defaultodbfoldername":"Dynamics365","address2_stateorprovince":null,"address2_line2":null,"userpuid":"10033FFF907B6BDF","firstname":"Bob",[...]}}
--batchresponse_6f7cbc73-6da4-478e-85dc-11a3b5975f27--
Weitere Informationen und Beispiele zu Batch-Anfragen erhalten Sie hier: https://docs.microsoft.com/de-de/powerapps/developer/data-platform/webapi/execute-batch-operations-using-web-api
Berechtigungsprüfungen
Was die Datensicherheit betrifft stellt Dynamics selbst sicher, dass jeder nur die Datensätze sehen darf, für die er berechtigt ist. Fragt ein Nutzer über die Web API einen Datensatz an, den er nicht sehen darf, wird eine Fehlermeldung als Antwort zurückgesendet, die dem Nutzer als entsprechende Meldung im Benutzerdefinierten Feld angezeigt wird.
Fazit
Unsere client-seitige Lösung ermöglicht es, mittels der komplexen Web API von Dynamics, beliebige Daten aus einem Dynamics CRM im Jira abzurufen und für Nutzer lesbar darzustellen. Bei der Entwicklung der Lösung waren das Authentifizierunghandling und die komplexen Abfragen an die Web API, um alle gewünschten Daten in Jira anzuzeigen, die größten Herausforderungen. Weiterhin geht diese Komplexität mit einer teilweise hohen Anzahl an REST-Anfragen einher, die aber durch das Bündeln in Batch-Operationen reduziert werden können.
Unsere Jira-Dynamics-Integration-App ist nun seit mehreren Monaten erfolgreich in unserem Communardo-Jira im Einsatz und unterstützt unsere Mitarbeiter aus Vertrieb und Support bei ihrer täglichen Arbeit.
Da es sich, abgesehen vom Konfigurationsteil, um eine reine JavaScript-Lösung handelt, ist eine künftige Integration in Jira Cloud mit wenigen Anpassungen möglich.
Sie nutzen Microsoft Dynamics 365 und wollen es in Ihr Jira Data Center integrieren?
Profitieren Sie von unseren Erfahrungen. Wir beraten Sie gern umfassend zu Ihren Möglichkeiten.
Sie haben Fragen oder möchten sich von uns beraten lassen
Vereinbaren Sie einen persönlichen und unverbindlichen Gesprächstermin mit unseren Atlassian Sales Experts
Ansprechpartner*in
Ihr Atlassian Sales TeamWir helfen Ihnen gern
- Kleiststraße 10a, 01129 Dresden
- Tel.: 0800 8 776 776
- sales@communardo.de