Google Cloud Customer Care-Anfragen synchronisieren

Sie können Ihre Supportanfragen zwischen Google Cloud und Ihrem CRM-System (Customer-Relationship-Management) wie Jira Service Desk, Zendesk und ServiceNow synchronisieren, indem Sie einen Connector für deren Integration erstellen.

Dieser Connector verwendet die Cloud Support API (CSAPI) von Customer Care. In diesem Dokument wird anhand eines Beispiels erläutert, wie Sie einen Connector erstellen und verwenden können. Sie können das Design an Ihren Anwendungsfall anpassen.

Annahmen

Wir gehen davon aus, wie Ihr CRM funktioniert und in welcher Sprache Sie den Connector schreiben. Wenn Ihr CRM verschiedene Funktionen bietet, können Sie trotzdem einen Connector erstellen, der perfekt funktioniert. Allerdings müssen Sie ihn eventuell anders implementieren als in diesem Leitfaden.

Dieser Leitfaden basiert auf den folgenden Annahmen:

  • Sie erstellen den Connector mit Python und dem Flask-Mikroframework.
    • Wir gehen davon aus, dass Sie Flask verwenden. Das ist ein einfaches Framework, mit dem Sie eine kleine App erstellen können. Sie können auch andere Sprachen oder Frameworks wie Java verwenden.
  • Sie möchten Anhänge, Kommentare, Prioritäten, Fallmetadaten und den Fallstatus synchronisieren. Sie müssen nicht alle Daten synchronisieren, es sei denn, Sie möchten es. Wenn Sie beispielsweise keine Anhänge synchronisieren möchten, synchronisieren Sie sie nicht.
  • Ihr CRM stellt Endpunkte bereit, die die zu synchronisierenden Felder lesen und schreiben können. Wenn Sie alle Felder wie in diesem Leitfaden synchronisieren möchten, achten Sie darauf, dass die Endpunkte Ihres CRM die folgenden Vorgänge unterstützen:
    Vorgang CSAPI-Äquivalent
    Rufen Sie Fälle mit einer unveränderten, statischen ID ab. cases.get
    Supportanfragen erstellen. cases.create
    Supportanfragen schließen cases.close
    Priorität einer Anfrage aktualisieren. cases.patch
    Sie können Anhänge für eine Anfrage auflisten. cases.attachments.list
    Laden Sie einen Anhang zu einer Supportanfrage herunter. media.download
    Laden Sie einen Anhang in eine Anfrage hoch. media.upload
    Kommentare zu einer Anfrage auflisten cases.comments.list
    Fügen Sie einen neuen Kommentar zu einer Anfrage hinzu. cases.comments.create
    Fälle durchgehen* cases.search

*Sie müssen nach dem Zeitpunkt der letzten Aktualisierung filtern können. Außerdem muss angegeben werden, welche Fälle mit dem Kundendienst synchronisiert werden sollen. Wenn Fälle in Ihrem CRM beispielsweise benutzerdefinierte Felder enthalten können, können Sie ein benutzerdefiniertes boolesches Feld mit dem Namen synchronizeWithGoogleCloudSupport ausfüllen und darauf filtern.

Allgemeines Design

Der Connector wird ausschließlich aus Google Cloud-Produkten und Ihrem CRM-System erstellt. Es handelt sich um eine App Engine-Anwendung, die Python im Flask-Mikroframework ausführt. Dabei werden Cloud Tasks verwendet, um regelmäßig die API und Ihr CRM abzufragen und neue Fälle zu aktualisieren. Außerdem werden Änderungen zwischen diesen Anfragen synchronisiert. Einige Metadaten zu Supportanfragen werden in Firestore gespeichert, aber gelöscht, nachdem sie nicht mehr benötigt werden.

Das folgende Diagramm zeigt das allgemeine Design:

Der Connector ruft CSAPI und Ihr CRM auf. Sie umfasst eine App Engine-Anwendung, Cloud Tasks und einige Daten in Firestore.

Connector-Zielvorhaben

Das Hauptziel des Connectors besteht darin, dass bei der Erstellung einer Anfrage, die Sie synchronisieren möchten, in Ihrem CRM-System eine entsprechende Anfrage in Customer Care erstellt wird und alle nachfolgenden Aktualisierungen der Anfrage zwischen ihnen synchronisiert werden. Wenn eine Supportanfrage in Customer Care erstellt wird, muss sie ebenfalls mit Ihrem CRM-System synchronisiert werden.

Insbesondere müssen die folgenden Aspekte synchronisiert werden:

  • Fallerstellung:
    • Wenn in einem System eine Supportanfrage erstellt wird, muss im Connector ein entsprechender Fall in dem anderen System erstellt werden.
    • Wenn ein System nicht verfügbar ist, muss die Anfrage darin erstellt werden, sobald sie verfügbar ist.
  • Kommentare:
    • Wenn ein Kommentar in einem Fall in einem System hinzugefügt wird, muss er zum entsprechenden Fall im anderen System hinzugefügt werden.
  • Anhänge:
    • Wenn ein Anhang zu einer Anfrage in einem System hinzugefügt wird, muss er dem entsprechenden Fall im anderen System hinzugefügt werden.
  • Priorität:
    • Wenn die Priorität einer Supportanfrage in einem System aktualisiert wird, muss die Priorität der entsprechenden Supportanfrage im anderen System aktualisiert werden.
  • Status des Falls:
    • Wenn eine Anfrage in einem System geschlossen wird, muss sie auch in dem anderen System geschlossen werden.

Infrastruktur

Google Cloud-Produkte

Der Connector ist eine App Engine-Anwendung, in der Cloud Firestore im Datastore-Modus zum Speichern von Daten zu den synchronisierten Fällen verwendet wird. Es verwendet Cloud Tasks, um Jobs mit automatischer Wiederholungslogik zu planen.

Für den Zugriff auf Supportanfragen in Customer Care verwendet der Connector ein Dienstkonto, um die V2 Cloud Support API aufzurufen. Sie müssen dem Dienstkonto für die Authentifizierung die entsprechenden Berechtigungen erteilen.

Ihr CRM

Der Connector greift auf Fälle in Ihrem CRM zu, die von Ihnen bereitgestellt werden. Wahrscheinlich rufen Sie eine API auf, die von Ihrem CRM bereitgestellt wird.

Sicherheitsaspekte für Ihre Organisation

Der Connector synchronisiert Fälle in der Organisation, in der Sie ihn erstellen, und in allen untergeordneten Projekten der Organisation. Dadurch können Nutzer in dieser Organisation möglicherweise auf Kundensupportdaten zugreifen, auf die sie nicht zugreifen sollen. Überlegen Sie sich genau, wie Sie Ihre IAM-Rollen strukturieren, um die Sicherheit in Ihrer Organisation aufrechtzuerhalten.

Detaillierte Konfiguration

CSAPI einrichten

So richten Sie die CSAPI ein:

  1. Cloud Customer Care-Support für Ihre Organisation erwerben
  2. Aktivieren Sie die Cloud Support API für das Projekt, in dem Sie den Connector ausführen möchten.
  3. Rufen Sie die Anmeldedaten des standardmäßigen Apps Framework-Dienstkontos ab, das vom Connector verwendet werden soll .
  4. Weisen Sie dem Dienstkonto auf Organisationsebene die folgenden Rollen zu:
    • Tech Support Editor
    • Organization Viewer

Weitere Informationen zum Einrichten der CSAPI finden Sie im Nutzerhandbuch für die Cloud Support API V2.

CSAPI aufrufen

Wir verwenden Python, um CSAPI aufzurufen. Wir empfehlen die folgenden zwei Möglichkeiten, CSAPI mit Python aufzurufen:

  1. Aus den Protos generierte Clientbibliotheken. Diese sind moderner und idiomatisch, unterstützen jedoch nicht das Aufrufen der Anhangs-Endpunkte von CSAPI. Weitere Informationen finden Sie unter GAPIC-Generator.
  2. Clientbibliotheken, die aus dem Discovery-Dokument generiert wurden Diese sind älter, unterstützen aber Anhänge. Weitere Informationen finden Sie unter Google API-Client.

Hier ein Beispiel für den Aufruf von CSAPI mithilfe von Clientbibliotheken, die aus dem Discovery-Dokument erstellt wurden:

"""
Gets a support case using the Cloud Support API.

Before running, do the following:
- Set the GOOGLE_APPLICATION_CREDENTIALS environment variable to
your service account credentials.
- Install the Google API Python Client: https://github.com/googleapis/google-api-python-client
- Change NAME to point to a case that your service account has permission to get.
"""

import os
import googleapiclient.discovery

NAME = "projects/some-project/cases/43595344"

def main():
    api_version = "v2"
    supportApiService = googleapiclient.discovery.build(
        serviceName="cloudsupport",
        version=api_version,
        discoveryServiceUrl=f"https://cloudsupport.googleapis.com/$discovery/rest?version={api_version}",
    )

    request = supportApiService.cases().get(
        name=NAME,
    )
    print(request.execute())

if __name__ == "__main__":
    main()

Weitere Beispiele für den Aufruf von CSAPI finden Sie in diesem Repository.

Google-Ressourcennamen, -IDs und -Nummern

organization_id muss die ID Ihrer Organisation sein. Sie können Fälle in Customer Care unter Ihrer Organisation oder in einem Projekt innerhalb Ihrer Organisation erstellen. Geben Sie project_id den Namen eines Projekts, in dem Sie Fälle erstellen können.

Name der Anfrage

Der Name einer Supportanfrage sieht so aus:

  • organizations/{organization_id}/cases/{case_number}
  • projects/{project_id}/cases/{case_number}

Dabei ist case_number die Nummer, die dem Fall zugewiesen wurde. Beispiel: 51234456.

Name des Kommentars

Der Name eines Kommentars sieht so aus:

  • organizations/{organization_id}/cases/{case_number}/comments/{comment_id}

Dabei ist comment_id eine Zahl, die einem Kommentar zugewiesen ist. Beispiel: 3. Zusätzlich zu den Organisationen sind Projekte auch Eltern erlaubt.

Name des Anhangs

Der Name eines Anhangs sieht so aus:

  • organizations/{organization_id}/cases/{case_number}/attachments/{attachment_id}

Dabei ist attachment_id die ID eines Anhangs in einem Fall, falls vorhanden. Beispiel: 0684M00000JvBpnQAF. Zusätzlich zu den Organisationen sind Projekte auch Eltern erlaubt.

Firestore-Entitäten

CaseMapping

Ein CaseMapping ist ein Objekt, das zum Speichern von Metadaten für einen Fall definiert ist.

Sie wird für jeden Fall erstellt, der synchronisiert und gelöscht wird, wenn sie nicht mehr benötigt wird. Weitere Informationen zu den in Firebase unterstützten Datentypen finden Sie unter Unterstützte Datentypen.

CaseMapping hat die folgenden Eigenschaften:

Attribut Beschreibung Typ Beispiel
ID Primärschlüssel. Wird von Firestore automatisch zugewiesen, wenn die CaseMapping erstellt wird. Ganzzahl 123456789
googleCaseName Der vollständige Name der Anfrage, einschließlich der Organisations- oder Projekt-ID und der Fallnummer. Textstring organizations/123/cases/456
companyCaseID Die Fall-ID in Ihrem CRM Ganzzahl 789
newContentAt Das letzte Mal, dass ein neuer Inhalt im Google-Fall oder im Fall in Ihrem CRM erkannt wurde. Datum und Uhrzeit 0001-01-01T00:00:00Z
resolvedAt Ein Zeitstempel für die Behebung des Google-Falls. Wird zum Löschen von CaseMappings verwendet, wenn sie nicht mehr benötigt werden. Datum und Uhrzeit 0001-01-01T00:00:00Z
companyUpdatesSyncedAt Ein Zeitstempel des letzten Mals, an dem der Connector Ihr CRM erfolgreich nach Aktualisierungen gefragt und alle Aktualisierungen mit dem Google-Fall synchronisiert hat. Wird verwendet, um Ausfälle zu erkennen. Datum und Uhrzeit 0001-01-01T00:00:00Z
googleUpdatesSyncedAt Ein Zeitstempel des letzten Abgleichs des Connectors mit Google durch den Connector und die Synchronisierung aller Updates für den CRM-Prozess. Wird verwendet, um Ausfälle zu erkennen. Datum und Uhrzeit 0001-01-01T00:00:00Z
outageCommentSentToGoogle Wenn ein Ausfall festgestellt wurde, wird dem Kommentar ein entsprechender Kommentar hinzugefügt. Wird verwendet, um zu verhindern, dass mehrere Ausfallkommentare hinzugefügt werden. Boolesch False
outageCommentSentToCompany Falls ein Ausfall festgestellt wurde, wird einem Kommentar der CRM-Lösung hinzugefügt. Wird verwendet, um zu verhindern, dass mehrere Ausfallkommentare hinzugefügt werden. Boolesch False
priority Die Prioritätsstufe der Anfrage. Ganzzahl 2

Global

Global ist ein Objekt, das globale Variablen für den Connector speichert.

Nur ein Global-Objekt wird erstellt und niemals gelöscht. Sie sieht so aus:

Attribut Beschreibung Typ Beispiel
IDPrimärschlüssel. Wird von Firestore automatisch zugewiesen, wenn dieses Objekt erstellt wird. Ganzzahl 123456789
google_last_polled_at Der Zeitpunkt, zu dem der Kundenservice zuletzt aktualisiert wurde. Datum und Uhrzeit 0001-01-01T00:00:00Z
company_last_polled_at Der Zeitpunkt, zu dem Ihr Unternehmen zuletzt nach Updates gefragt wurde. Datum und Uhrzeit 0001-01-01T00:00:00Z

Tasks

PollGoogleForUpdates

This task is scheduled to run every 60 seconds. It does the following:

  • Search for recently updated cases:
    • Call CSAPI.SearchCases(organization_id, page_size=100, filter="update_time>{Global.google_last_polled_at - GOOGLE_POLLING_WINDOW}")
      • Continue fetching pages as long as a nextPageToken is returned.
      • GOOGLE_POLLING_WINDOW represents the period during which a case is continually checked for updates, even after it has been synced. The larger its value, the more tolerant the connector is to changes that are added while a case is syncing. We recommend that you set GOOGLE_POLLING_WINDOW to 30 minutes to avoid any problems with comments being added out of order.
  • Make a CaseMapping for any new cases:
    • If CaseMapping does not exist for case.name and case.create_time is less than 30 days ago, then create a CaseMapping with the following values:
      Property Value
      caseMappingID N/A
      googleCaseName case.name
      companyCaseID null
      newContentAt current_time
      resolvedAt null
      companyUpdatesSyncedAt current_time
      googleUpdatesSyncedAt null
      outageCommentSentToGoogle False
      outageCommentSentToCompany False
      priority case.priority (converted to an integer)
  • Queue tasks to sync all recently updated cases:
    • Specifically, SyncGoogleCaseToCompany(case.name).
  • Update CaseMappings:
    • For each open CaseMapping not recently updated, update CaseMapping.googleUpdatesSyncedAt to the current time.
  • Update last polled time:
    • Update Global.google_last_polled_at in Firestore to the current time.
Retry logic

Configure this task to retry a few times within the first minute and then expire.

PollCompanyForUpdates

This task is scheduled to run every 60 seconds. It does the following:

  • Search for recently updated cases:
    • Call YOUR_CRM.SearchCases(page_size=100, filter=”update_time>{Global.company_last_polled_at - COMPANY_POLLING_WINDOW} AND synchronizeWithGoogleCloudSupport=true”).
    • COMPANY_POLLING_WINDOW can be set to whatever time duration works for you. For example, 5 minutes.
  • Make a CaseMapping for any new cases:
    • For each case, if CaseMapping does not exist for case.id and case.create_time is less than 30 days ago, create a CaseMapping that looks like this:
      Property Value
      caseMappingID N/A
      googleCaseName null
      companyCaseID case.id
      newContentAt current_time
      resolvedAt null
      companyUpdatesSyncedAt null
      googleUpdatesSyncedAt current_time
      outageCommentSentToGoogle False
      outageCommentSentToCompany False
      priority case.priority (converted to an integer)
  • Queue tasks to sync all recently updated cases:
    • Specifically, queue SyncCompanyCaseToGoogle(case.name).
  • Update CaseMappings:
    • For each open CaseMapping not recently updated, update CaseMapping.companyUpdatesSyncedAt to the current time.
  • Update last polled time:
    • Update Global.company_last_polled_at in Firestore to the current time.
Retry logic

Configure this task to retry a few times within the first minute and then expire.

SyncGoogleUpdatesToCompany(case_name)

Implementation

  • Get the case and case mapping:
    • Get CaseMapping for case_name.
    • Call CSAPI.GetCase(case_name).
  • If necessary, update resolved time and case status:
    • If CaseMapping.resolvedAt == null and case.status == CLOSED:
      • Set CaseMapping.resolvedAt to case.update_time
      • Close the case in the CRM as well
  • Try to connect to an existing case in the CRM. If unable, then make a new one:
    • If CaseMapping.companyCaseID == null:
      • Try to get your CRM case with custom_field_google_name == case_name
        • custom_field_google_name is a custom field you create on the case object in your CRM.
      • If the CRM case can't be found, call YOUR_CRM.CreateCase(case) with the following case:
        Case field name in your CRM Value
        Summary Case.diplay_name
        Priority Case.priority
        Description "CONTENT MIRRORED FROM GOOGLE SUPPORT:\n" + Case.description
        Components "Google Cloud"
        Customer Ticket (custom_field_google_name) case_name
        Attachments N/A
      • Update the CaseMapping with a CaseMapping that looks like this:
        Property Value
        companyCaseID new_case.id
        googleUpdatesSyncedAt current_time
      • Add comment to Google case: "This case is now syncing with Company Case: {case_id}".
  • Synchronize the comments:
    • Get all comments:
      • Call CSAPI.ListComments(case_name, page_size=100). The maximum page size is 100. Continue retrieving successive pages until the oldest comment retrieved is older than googleUpdatesSyncedAt - GOOGLE_POLLING_WINDOW.
      • Call YOUR_CRM.GetComments(case_id, page_size=50). Continue retrieving successive pages until the oldest comment retrieved is older than companyUpdatesSyncedAt - COMPANY_POLLING_WINDOW.
      • Optional: If you'd like, consider caching comments in some way so you can avoid making extra calls here. We leave the implementation of that up to you.
    • Compare both lists of comments to determine if there are new comments on the Google Case.
    • For each new Google comment:
      • Call YOUR_CRM.AddComment(comment.body), starting with "[Google Comment {comment_id}by {comment.actor.display_name}]".
  • Repeat for attachments.
  • Update CaseMapping.googleUpdatesSyncedAt to the current time.
Retry logic

Configure this task to retry indefinitely with exponential backoff.

SyncCompanyUpdatesToGoogle(case_id)

Implementation:

  • Get the case and case mapping.
    • Get CaseMapping for case.id.
    • Call YOUR_CRM.GetCase(case.id).
  • If necessary, update resolved time and case status:
    • If CaseMapping.resolvedAt == null and case.status == CLOSED:
      • Set CaseMapping.resolvedAt to case.update_time
      • Close the case in CSAPI as well
  • Try to connect to an existing case in CSAPI. If unable, then make a new one:
    • If CaseMapping.googleCaseName == null:
      • Search through cases in CSAPI. Try to find a case that has a comment containing “This case is now syncing with Company Case: {case_id}”. If you're able to find one, then set googleCaseName equal to its name.
      • Otherwise, call CSAPI.CreateCase(case):
  • Synchronize the comments.
    • Get all comments for the case from CSAPI and the CRM:
      • Call CSAPI.ListComments(case_name, page_size=100). Continue retrieving successive pages until the oldest comment retrieved is older than googleUpdatesSyncedAt - GOOGLE_POLLING_WINDOW.
      • Call YOUR_CRM.GetComments(case_id, page_size=50). Continue retrieving successive pages until the oldest comment retrieved is older than companyUpdatesSyncedAt - COMPANY_POLLING_WINDOW.
      • NOTE: If you'd like, consider caching comments in some way so you can avoid making extra calls here. We leave the implementation of that up to you.
    • Compare both lists of comments to determine if there are new comments on the CRM case.
    • For each new Company comment:
      • Call CSAPI.AddComment, starting with "[Company Comment {comment.id} by {comment.author.displayName}]".
  • Repeat for attachments.
  • Update CaseMapping.companyUpdatesSyncedAt to the current time.
Retry logic

Configure this task to retry indefinitely with exponential backoff.

CleanUpCaseMappings

This task is scheduled to run daily. It deletes any CaseMapping for a case that has been closed for 30 days according to resolvedAt.

Retry logic

Configure this task to retry with exponential backoff for up to 24 hours.

DetectOutages

This task is scheduled to run once every 5 minutes. It detects outages and alerts your Google and CRM cases (when possible) if a case is not syncing within the expected latency_tolerance.

latency_tolerance is defined as follows, where Time Since New Content = currentTime - newContentAt:

Priority Fresh (<1 hour) Default (1 hour-1day) Stale (>1 day)
P0 10 min 10 min 15 min
P1 10 min 15 min 60 min
P2 10 min 20 min 120 min
P3 10 min 20 min 240 min
P4 10 min 30 min 240 min

The latency that is relevant for the connector is not request latency, but rather the latency between when a change is made in one system and when it is propagated to the other. We make latency_tolerance dependent on priority and freshness to avoid spamming cases unnecessarily. If there is a short outage, such as scheduled maintenance on either system, we don't need to alert P4 cases that haven't been updated recently.

When DetectOutages runs, it does the following:

  • Determine if a CaseMapping needs an outage comment, whereupon it adds one:
    • For each CaseMapping in Firestore:
      • If recently added (companyCaseId or googleUpdatesSyncedAt is not defined), then ignore.
      • If current_time > googleUpdatesSyncedAt + latency_tolerance OR current_time > companyUpdatesSyncedAt + latency_tolerance:
        • If !outageCommentSentToGoogle:
          • Try:
            • Add comment to Google that "This case has not synced properly in {duration since sync}."
            • Set outageCommentSentToGoogle = True.
        • If !outageCommentSentToCompany:
          • Try:
            • Add comment to your CRM that "This case has not synced properly in {duration since sync}."
            • Set outageCommentSentToCompany = True.
      • Else:
        • If outageCommentSentToGoogle:
          • Try:
            • Add comment to Google that "Syncing has resumed."
            • Set outageCommentSentToGoogle = False.
        • If outageCommentSentToCompany:
          • Try:
            • Add comment to your CRM that "Syncing has resumed."
            • Set outageCommentSentToCompany = False.
  • Return a failing status code (4xx or 5xx) if an outage is detected. This causes any monitoring you've set up to notice that there is a problem with the task.
Retry logic

Configure this task to retry a few times within the first 5 minutes and then expire.

What's next

Your connector is now ready to use.

If you'd like, you can also implement unit tests and integration tests. Also, you can add monitoring to check that the connector is working correctly on an ongoing basis.