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

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

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

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

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

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

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

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

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

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

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

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

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

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

Node.js

Python

جافا

(اختياري) إعداد المصادقة على خدمة خارجية

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

لإعداد خدمة OAuth أو طلب تفويض مخصّص، اطّلِع على مقالة ربط إضافتك بخدمة خارجية.

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

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

ضبط معاينات الروابط

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

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

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

  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" أو "العروض التقديمية من 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. لكل بطاقة معاينة، نفِّذ أي دوال رد اتصال توفّر تفاعلاً مع الأدوات للواجهة. على سبيل المثال، إذا أضفت زرًا بعنوان "عرض الرابط"، يمكنك إنشاء إجراء يحدِّد دالة رد اتصال لفتح الرابط في نافذة جديدة. لمزيد من المعلومات عن تفاعلات الأدوات، اطّلِع على مقالة إجراءات الإضافات.

ينشئ الرمز البرمجي التالي دالة رد الاتصال 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

node/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/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 مع الأدوات والإجراءات التالية لبطاقات معاينة الروابط:

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

حقل Card Service النوع
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

node/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/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;
  }

}