معاينة الروابط باستخدام الشرائح الذكية

توضّح هذه الصفحة كيفية إنشاء إضافة Google Workspace تتيح لمستخدمي "مستندات Google" و"جداول بيانات Google" و"العروض التقديمية من Google" معاينة الروابط من خدمة تابعة لجهة خارجية.

يمكن لإضافة Google Workspace رصد روابط الخدمة ومطالبة المستخدمين بمعاينتها. يمكنك تهيئة إضافة لمعاينة أنماط عناوين URL متعددة، مثل الروابط المؤدية إلى طلبات الحصول على الدعم، وعملاء المبيعات المحتملين، والملفات الشخصية للموظفين.

كيف يعاين المستخدمون الروابط

لمعاينة الروابط، يتفاعل المستخدمون مع الشرائح الذكية والبطاقات.

يعاين المستخدم بطاقة.

عندما يكتب المستخدمون عنوان URL أو يلصقونه في مستند، يطلب "مستندات Google" منهم استبدال الرابط بشريحة ذكية. تعرض الشريحة الذكية رمزًا وعنوانًا قصيرًا أو وصفًا لمحتوى الرابط. عندما يمرر المستخدم مؤشر الماوس فوق الشريحة، تظهر له واجهة بطاقة تعرض مزيدًا من المعلومات حول الملف أو الرابط.

يعرض الفيديو التالي كيف يحوّل المستخدم رابطًا إلى شريحة ذكية ويعاين بطاقة:

كيفية معاينة المستخدمين للروابط في "جداول بيانات Google" و"العروض التقديمية من Google"

لا يمكن استخدام الشرائح الذكية التابعة لجهات خارجية لمعاينات الروابط في "جداول بيانات Google" و"العروض التقديمية من Google". عندما يكتب المستخدمون عنوان URL أو يلصقونه في جدول بيانات أو عرض تقديمي، تطلب منهم "جداول بيانات Google" أو "العروض التقديمية من Google" استبدال الرابط بعنوانه كنص مرتبط بدلاً من شريحة. عندما يمرر المستخدم فوق عنوان الرابط، يرى واجهة بطاقة تعرض معلومات حول الرابط.

توضح الصورة التالية كيفية عرض معاينة الرابط في "جداول البيانات" و"العروض التقديمية":

مثال على معاينة الروابط لتطبيقَي "جداول بيانات Google" و"العروض التقديمية من Google"

المتطلبات الأساسية

برمجة تطبيقات

Node.js

Python

Java

اختياري: إعداد المصادقة لخدمة تابعة لجهة خارجية

إذا كانت الإضافة متصلة بخدمة تتطلب إذنًا، على المستخدمين المصادقة على الخدمة لمعاينة الروابط. وهذا يعني أنّه عندما يلصق المستخدمون رابطًا من خدمتك في ملف في "مستندات Google" أو "جداول بيانات Google" أو "العروض التقديمية من Google" للمرة الأولى، يجب أن تستدعي الإضافة خطوات التفويض.

لإعداد خدمة OAuth أو طلب تفويض مخصص، يمكنك الاطّلاع على أحد الأدلة التالية:

يوضّح هذا القسم كيفية إعداد معاينات الروابط لإضافتك، والتي تتضمّن الخطوات التالية:

  1. تهيئة معاينات الروابط في ملف بيان أو مورد نشر الإضافة.
  2. أنشِئ الشريحة الذكية وواجهة البطاقة للروابط.

إعداد معاينات الروابط

لإعداد معاينات الروابط، حدِّد الأقسام والحقول التالية في مورد نشر الإضافة أو ملف بيانها:

  1. ضمن القسم addOns، أضِف الحقل docs لتوسيع "مستندات Google"، والحقل sheets لتوسيع "جداول البيانات"، والحقل slides لتوسيع "العروض التقديمية من Google".
  2. في كل حقل، نفِّذ المشغِّل linkPreviewTriggers الذي يشمل runFunction (يمكنك تحديد هذه الدالة في القسم التالي، إنشاء الشريحة الذكية والبطاقة).

    للاطّلاع على الحقول التي يمكنك تحديدها في عامل التشغيل linkPreviewTriggers، راجِع المستندات المرجعية حول ملفات بيان برمجة التطبيقات أو موارد النشر لأوقات التشغيل الأخرى.

  3. في الحقل oauthScopes، أضِف النطاق https://www.googleapis.com/auth/workspace.linkpreview حتى يتمكن المستخدمون من تفويض الإضافة لمعاينة الروابط نيابةً عنهم.

مثلاً، راجِع القسم oauthScopes وaddons لمورد النشر التالي الذي يضبط معاينات الروابط لخدمة حالة دعم.

{
  "oauthScopes": [
    "https://www.googleapis.com/auth/workspace.linkpreview"
  ],
  "addOns": {
    "common": {
      "name": "Preview support cases",
      "logoUrl": "https://www.example.com/images/company-logo.png",
      "layoutProperties": {
        "primaryColor": "#dd4b39"
      }
    },
    "docs": {
      "linkPreviewTriggers": [
        {
          "runFunction": "caseLinkPreview",
          "patterns": [
            {
              "hostPattern": "example.com",
              "pathPrefix": "support/cases"
            },
            {
              "hostPattern": "*.example.com",
              "pathPrefix": "cases"
            },
            {
              "hostPattern": "cases.example.com"
            }
          ],
          "labelText": "Support case",
          "logoUrl": "https://www.example.com/images/support-icon.png",
          "localizedLabelText": {
            "es": "Caso de soporte"
          }
        }
      ]
    },
    "sheets": {
      "linkPreviewTriggers": [
        {
          "runFunction": "caseLinkPreview",
          "patterns": [
            {
              "hostPattern": "example.com",
              "pathPrefix": "support/cases"
            },
            {
              "hostPattern": "*.example.com",
              "pathPrefix": "cases"
            },
            {
              "hostPattern": "cases.example.com"
            }
          ],
          "labelText": "Support case",
          "logoUrl": "https://www.example.com/images/support-icon.png",
          "localizedLabelText": {
            "es": "Caso de soporte"
          }
        }
      ]
    },
    "slides": {
      "linkPreviewTriggers": [
        {
          "runFunction": "caseLinkPreview",
          "patterns": [
            {
              "hostPattern": "example.com",
              "pathPrefix": "support/cases"
            },
            {
              "hostPattern": "*.example.com",
              "pathPrefix": "cases"
            },
            {
              "hostPattern": "cases.example.com"
            }
          ],
          "labelText": "Support case",
          "logoUrl": "https://www.example.com/images/support-icon.png",
          "localizedLabelText": {
            "es": "Caso de soporte"
          }
        }
      ]
    }
  }
}

في المثال، روابط معاينة إضافة Google Workspace لخدمة طلب الحصول على الدعم للشركة. تحدد الإضافة ثلاثة أنماط لعنوان URL لمعاينة الروابط. وعندما يتطابق رابط مع أحد أنماط عناوين URL، تنشئ دالة معاودة الاتصال caseLinkPreview بطاقة وشريحة ذكية وتعرضها، أو في "جداول بيانات Google" و"العروض التقديمية من Google"، تستبدل عنوان URL بعنوان الرابط.

إنشاء الشريحة الذكية والبطاقة

لإرجاع شريحة ذكية وبطاقة لرابط ما، يجب تنفيذ أي وظائف حدّدتها في العنصر linkPreviewTriggers.

عندما يتفاعل مستخدِم مع رابط يتطابق مع نمط عنوان URL محدّد، يتم تنشيط مشغّل linkPreviewTriggers وتمرّر وظيفة الاستدعاء المرتبطة به كائن الحدث EDITOR_NAME.matchedUrl.url كوسيطة. يمكنك استخدام حمولة كائن الحدث هذا لإنشاء الشريحة الذكية والبطاقة لمعاينة الرابط.

على سبيل المثال، إذا عاين مستخدم الرابط https://www.example.com/cases/123456 في "مستندات Google"، يتم عرض حمولة الحدث التالية:

JSON

{
  "docs": {
    "matchedUrl": {
        "url": "https://www.example.com/support/cases/123456"
    }
  }
}

لإنشاء واجهة البطاقات، يمكنك استخدام التطبيقات المصغّرة لعرض معلومات حول الرابط. يمكنك أيضًا إنشاء إجراءات تسمح للمستخدمين بفتح الرابط أو تعديل محتواه. للحصول على قائمة بالأدوات والإجراءات المتاحة، راجع المكونات المتوافقة مع بطاقات المعاينة.

لإنشاء الشريحة الذكية والبطاقة لمعاينة رابط:

  1. نفِّذ الوظيفة التي حدّدتها في القسم linkPreviewTriggers من مورد نشر الإضافة أو ملف البيان:
    1. يجب أن تقبل الدالة كائن حدث يحتوي على EDITOR_NAME.matchedUrl.url كوسيطة وتعرض كائن Card واحدًا.
    2. إذا كانت الخدمة تتطلب تفويضًا، يجب أن تتضمن الوظيفة أيضًا تدفق التفويض.
  2. بالنسبة إلى كل بطاقة معاينة، نفِّذ أي وظائف لمعاودة الاتصال توفر تفاعلات أدوات للواجهة. على سبيل المثال، إذا قمت بتضمين زر مكتوب عليه "View link" (عرض الرابط)، يمكنك إنشاء إجراء يحدد وظيفة معاودة الاتصال لفتح الرابط في نافذة جديدة. لمزيد من المعلومات حول تفاعلات الأدوات، راجع إجراءات الإضافات.

يُنشئ الرمز التالي دالة رد الاتصال caseLinkPreview لـ "مستندات Google":

برمجة تطبيقات

apps-script/3p-resources/3p-resources.gs
/**
* Entry point for a support case link preview.
*
* @param {!Object} event The event object.
* @return {!Card} The resulting preview link card.
*/
function caseLinkPreview(event) {

  // If the event object URL matches a specified pattern for support case links.
  if (event.docs.matchedUrl.url) {

    // Uses the event object to parse the URL and identify the case details.
    const caseDetails = parseQuery(event.docs.matchedUrl.url);

    // Builds a preview card with the case name, and description
    const caseHeader = CardService.newCardHeader()
      .setTitle(`Case ${caseDetails["name"][0]}`);
    const caseDescription = CardService.newTextParagraph()
      .setText(caseDetails["description"][0]);

    // Returns the card.
    // Uses the text from the card's header for the title of the smart chip.
    return CardService.newCardBuilder()
      .setHeader(caseHeader)
      .addSection(CardService.newCardSection().addWidget(caseDescription))
      .build();
  }
}

/**
* Extracts the URL parameters from the given URL.
*
* @param {!string} url The URL to parse.
* @return {!Map} A map with the extracted URL parameters.
*/
function parseQuery(url) {
  const query = url.split("?")[1];
  if (query) {
    return query.split("&")
    .reduce(function(o, e) {
      var temp = e.split("=");
      var key = temp[0].trim();
      var value = temp[1].trim();
      value = isNaN(value) ? value : Number(value);
      if (o[key]) {
        o[key].push(value);
      } else {
        o[key] = [value];
      }
      return o;
    }, {});
  }
  return null;
}

Node.js

العقدة/3p-resources/index.js
/**
 * 
 * A support case link preview.
 *
 * @param {!URL} url The event object.
 * @return {!Card} The resulting preview link card.
 */
function caseLinkPreview(url) {
  // Builds a preview card with the case name, and description
  // Uses the text from the card's header for the title of the smart chip.
  // Parses the URL and identify the case details.
  const name = `Case ${url.searchParams.get("name")}`;
  return {
    action: {
      linkPreview: {
        title: name,
        previewCard: {
          header: {
            title: name
          },
          sections: [{
            widgets: [{
              textParagraph: {
                text: url.searchParams.get("description")
              }
            }]
          }]
        }
      }
    }
  };
}

Python

python/3p-resources/create_link_preview/main.py

def case_link_preview(url):
    """A support case link preview.
    Args:
      url: A matching URL.
    Returns:
      The resulting preview link card.
    """

    # Parses the URL and identify the case details.
    query_string = parse_qs(url.query)
    name = f'Case {query_string["name"][0]}'
    # Uses the text from the card's header for the title of the smart chip.
    return {
        "action": {
            "linkPreview": {
                "title": name,
                "previewCard": {
                    "header": {
                        "title": name
                    },
                    "sections": [{
                        "widgets": [{
                            "textParagraph": {
                                "text": query_string["description"][0]
                            }
                        }]
                    }],
                }
            }
        }
    }

Java

java/3p-resources/src/main/java/CreateLinkPreview.java
/**
 * A support case link preview.
 *
 * @param url A matching URL.
 * @return The resulting preview link card.
 */
JsonObject caseLinkPreview(URL url) throws UnsupportedEncodingException {
  // Parses the URL and identify the case details.
  Map<String, String> caseDetails = new HashMap<String, String>();
  for (String pair : url.getQuery().split("&")) {
      caseDetails.put(URLDecoder.decode(pair.split("=")[0], "UTF-8"), URLDecoder.decode(pair.split("=")[1], "UTF-8"));
  }

  // Builds a preview card with the case name, and description
  // Uses the text from the card's header for the title of the smart chip.
  JsonObject cardHeader = new JsonObject();
  String caseName = String.format("Case %s", caseDetails.get("name"));
  cardHeader.add("title", new JsonPrimitive(caseName));

  JsonObject textParagraph = new JsonObject();
  textParagraph.add("text", new JsonPrimitive(caseDetails.get("description")));

  JsonObject widget = new JsonObject();
  widget.add("textParagraph", textParagraph);

  JsonArray widgets = new JsonArray();
  widgets.add(widget);

  JsonObject section = new JsonObject();
  section.add("widgets", widgets);

  JsonArray sections = new JsonArray();
  sections.add(section);

  JsonObject previewCard = new JsonObject();
  previewCard.add("header", cardHeader);
  previewCard.add("sections", sections);

  JsonObject linkPreview = new JsonObject();
  linkPreview.add("title", new JsonPrimitive(caseName));
  linkPreview.add("previewCard", previewCard);

  JsonObject action = new JsonObject();
  action.add("linkPreview", linkPreview);

  JsonObject renderActions = new JsonObject();
  renderActions.add("action", action);

  return renderActions;
}

المكوّنات المتوافقة لبطاقات المعاينة

تتيح إضافات Google Workspace استخدام التطبيقات المصغّرة والإجراءات التالية لبطاقات معاينة الروابط:

برمجة تطبيقات

حقل "خدمة البطاقات" النوع
TextParagraph تطبيق مصغّر
DecoratedText تطبيق مصغّر
Image تطبيق مصغّر
IconImage تطبيق مصغّر
ButtonSet تطبيق مصغّر
TextButton تطبيق مصغّر
ImageButton تطبيق مصغّر
Grid تطبيق مصغّر
Divider تطبيق مصغّر
OpenLink الإجراء
Navigation الإجراء
لا يتوفر سوى طريقة updateCard.

JSON

حقل البطاقة (google.apps.card.v1) النوع
TextParagraph تطبيق مصغّر
DecoratedText تطبيق مصغّر
Image تطبيق مصغّر
Icon تطبيق مصغّر
ButtonList تطبيق مصغّر
Button تطبيق مصغّر
Grid تطبيق مصغّر
Divider تطبيق مصغّر
OpenLink الإجراء
Navigation الإجراء
الإجراء updateCard متاح فقط.

مثال كامل: إضافة طلب الحصول على الدعم

يعرض المثال التالي "إضافة في Google Workspace" لمعاينة الروابط المؤدية إلى طلبات الحصول على الدعم لإحدى الشركات في "مستندات Google".

يؤدي المثال إلى ما يلي:

  • معاينة روابط طلبات الحصول على الدعم، مثل https://www.example.com/support/cases/1234 تعرض الشريحة الذكية رمز دعم، وتتضمّن بطاقة المعاينة رقم تعريف الطلب ووصفًا.
  • في حال ضبط لغة المستخدم على الإسبانية، ستترجم الشريحة الذكية labelText إلى اللغة الإسبانية.

مورد النشر

برمجة تطبيقات

Apps-script/3p-resources/appsscript.json
{
  "timeZone": "America/New_York",
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "oauthScopes": [
    "https://www.googleapis.com/auth/workspace.linkpreview",
    "https://www.googleapis.com/auth/workspace.linkcreate"
  ],
  "addOns": {
    "common": {
      "name": "Manage support cases",
      "logoUrl": "https://developers.google.com/workspace/add-ons/images/support-icon.png",
      "layoutProperties": {
        "primaryColor": "#dd4b39"
      }
    },
    "docs": {
      "linkPreviewTriggers": [
        {
          "runFunction": "caseLinkPreview",
          "patterns": [
            {
              "hostPattern": "example.com",
              "pathPrefix": "support/cases"
            },
            {
              "hostPattern": "*.example.com",
              "pathPrefix": "cases"
            },
            {
              "hostPattern": "cases.example.com"
            }
          ],
          "labelText": "Support case",
          "localizedLabelText": {
            "es": "Caso de soporte"
          },
          "logoUrl": "https://developers.google.com/workspace/add-ons/images/support-icon.png"
        }
      ],
      "createActionTriggers": [
        {
          "id": "createCase",
          "labelText": "Create support case",
          "localizedLabelText": {
            "es": "Crear caso de soporte"
          },
          "runFunction": "createCaseInputCard",
          "logoUrl": "https://developers.google.com/workspace/add-ons/images/support-icon.png"
        }
      ]
    }
  }
}

JSON

{
  "oauthScopes": [
    "https://www.googleapis.com/auth/workspace.linkpreview"
  ],
  "addOns": {
    "common": {
      "name": "Preview support cases",
      "logoUrl": "https://developers.google.com/workspace/add-ons/images/support-icon.png",
      "layoutProperties": {
        "primaryColor": "#dd4b39"
      }
    },
    "docs": {
      "linkPreviewTriggers": [
        {
          "runFunction": "URL",
          "patterns": [
            {
              "hostPattern": "example.com",
              "pathPrefix": "support/cases"
            },
            {
              "hostPattern": "*.example.com",
              "pathPrefix": "cases"
            },
            {
              "hostPattern": "cases.example.com"
            }
          ],
          "labelText": "Support case",
          "localizedLabelText": {
            "es": "Caso de soporte"
          },
          "logoUrl": "https://developers.google.com/workspace/add-ons/images/support-icon.png"
        }
      ]
    }
  }
}

الرمز

برمجة تطبيقات

apps-script/3p-resources/3p-resources.gs
/**
* Entry point for a support case link preview.
*
* @param {!Object} event The event object.
* @return {!Card} The resulting preview link card.
*/
function caseLinkPreview(event) {

  // If the event object URL matches a specified pattern for support case links.
  if (event.docs.matchedUrl.url) {

    // Uses the event object to parse the URL and identify the case details.
    const caseDetails = parseQuery(event.docs.matchedUrl.url);

    // Builds a preview card with the case name, and description
    const caseHeader = CardService.newCardHeader()
      .setTitle(`Case ${caseDetails["name"][0]}`);
    const caseDescription = CardService.newTextParagraph()
      .setText(caseDetails["description"][0]);

    // Returns the card.
    // Uses the text from the card's header for the title of the smart chip.
    return CardService.newCardBuilder()
      .setHeader(caseHeader)
      .addSection(CardService.newCardSection().addWidget(caseDescription))
      .build();
  }
}

/**
* Extracts the URL parameters from the given URL.
*
* @param {!string} url The URL to parse.
* @return {!Map} A map with the extracted URL parameters.
*/
function parseQuery(url) {
  const query = url.split("?")[1];
  if (query) {
    return query.split("&")
    .reduce(function(o, e) {
      var temp = e.split("=");
      var key = temp[0].trim();
      var value = temp[1].trim();
      value = isNaN(value) ? value : Number(value);
      if (o[key]) {
        o[key].push(value);
      } else {
        o[key] = [value];
      }
      return o;
    }, {});
  }
  return null;
}

Node.js

العقدة/3p-resources/index.js
/**
 * Responds to any HTTP request related to link previews.
 *
 * @param {Object} req An HTTP request context.
 * @param {Object} res An HTTP response context.
 */
exports.createLinkPreview = (req, res) => {
  const event = req.body;
  if (event.docs.matchedUrl.url) {
    const url = event.docs.matchedUrl.url;
    const parsedUrl = new URL(url);
    // If the event object URL matches a specified pattern for preview links.
    if (parsedUrl.hostname === 'example.com') {
      if (parsedUrl.pathname.startsWith('/support/cases/')) {
        return res.json(caseLinkPreview(parsedUrl));
      }
    }
  }
};


/**
 * 
 * A support case link preview.
 *
 * @param {!URL} url The event object.
 * @return {!Card} The resulting preview link card.
 */
function caseLinkPreview(url) {
  // Builds a preview card with the case name, and description
  // Uses the text from the card's header for the title of the smart chip.
  // Parses the URL and identify the case details.
  const name = `Case ${url.searchParams.get("name")}`;
  return {
    action: {
      linkPreview: {
        title: name,
        previewCard: {
          header: {
            title: name
          },
          sections: [{
            widgets: [{
              textParagraph: {
                text: url.searchParams.get("description")
              }
            }]
          }]
        }
      }
    }
  };
}

Python

python/3p-resources/create_link_preview/main.py
from typing import Any, Mapping
from urllib.parse import urlparse, parse_qs

import flask
import functions_framework


@functions_framework.http
def create_link_preview(req: flask.Request):
    """Responds to any HTTP request related to link previews.
    Args:
      req: An HTTP request context.
    Returns:
      An HTTP response context.
    """
    event = req.get_json(silent=True)
    if event["docs"]["matchedUrl"]["url"]:
        url = event["docs"]["matchedUrl"]["url"]
        parsed_url = urlparse(url)
        # If the event object URL matches a specified pattern for preview links.
        if parsed_url.hostname == "example.com":
            if parsed_url.path.startswith("/support/cases/"):
                return case_link_preview(parsed_url)

    return {}




def case_link_preview(url):
    """A support case link preview.
    Args:
      url: A matching URL.
    Returns:
      The resulting preview link card.
    """

    # Parses the URL and identify the case details.
    query_string = parse_qs(url.query)
    name = f'Case {query_string["name"][0]}'
    # Uses the text from the card's header for the title of the smart chip.
    return {
        "action": {
            "linkPreview": {
                "title": name,
                "previewCard": {
                    "header": {
                        "title": name
                    },
                    "sections": [{
                        "widgets": [{
                            "textParagraph": {
                                "text": query_string["description"][0]
                            }
                        }]
                    }],
                }
            }
        }
    }

Java

java/3p-resources/src/main/java/CreateLinkPreview.java
import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;

import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;

public class CreateLinkPreview implements HttpFunction {
  private static final Gson gson = new Gson();

  /**
   * Responds to any HTTP request related to link previews.
   *
   * @param request An HTTP request context.
   * @param response An HTTP response context.
   */
  @Override
  public void service(HttpRequest request, HttpResponse response) throws Exception {
    JsonObject event = gson.fromJson(request.getReader(), JsonObject.class);
    String url = event.getAsJsonObject("docs")
        .getAsJsonObject("matchedUrl")
        .get("url")
        .getAsString();
    URL parsedURL = new URL(url);
    // If the event object URL matches a specified pattern for preview links.
    if ("example.com".equals(parsedURL.getHost())) {
      if (parsedURL.getPath().startsWith("/support/cases/")) {
        response.getWriter().write(gson.toJson(caseLinkPreview(parsedURL)));
        return;
      }
    }

    response.getWriter().write("{}");
  }


  /**
   * A support case link preview.
   *
   * @param url A matching URL.
   * @return The resulting preview link card.
   */
  JsonObject caseLinkPreview(URL url) throws UnsupportedEncodingException {
    // Parses the URL and identify the case details.
    Map<String, String> caseDetails = new HashMap<String, String>();
    for (String pair : url.getQuery().split("&")) {
        caseDetails.put(URLDecoder.decode(pair.split("=")[0], "UTF-8"), URLDecoder.decode(pair.split("=")[1], "UTF-8"));
    }

    // Builds a preview card with the case name, and description
    // Uses the text from the card's header for the title of the smart chip.
    JsonObject cardHeader = new JsonObject();
    String caseName = String.format("Case %s", caseDetails.get("name"));
    cardHeader.add("title", new JsonPrimitive(caseName));

    JsonObject textParagraph = new JsonObject();
    textParagraph.add("text", new JsonPrimitive(caseDetails.get("description")));

    JsonObject widget = new JsonObject();
    widget.add("textParagraph", textParagraph);

    JsonArray widgets = new JsonArray();
    widgets.add(widget);

    JsonObject section = new JsonObject();
    section.add("widgets", widgets);

    JsonArray sections = new JsonArray();
    sections.add(section);

    JsonObject previewCard = new JsonObject();
    previewCard.add("header", cardHeader);
    previewCard.add("sections", sections);

    JsonObject linkPreview = new JsonObject();
    linkPreview.add("title", new JsonPrimitive(caseName));
    linkPreview.add("previewCard", previewCard);

    JsonObject action = new JsonObject();
    action.add("linkPreview", linkPreview);

    JsonObject renderActions = new JsonObject();
    renderActions.add("action", action);

    return renderActions;
  }

}