Xem trước đường liên kết bằng khối thông minh

Trang này giải thích cách tạo một tiện ích bổ sung cho Google Workspace để cho phép người dùng Google Tài liệu, Trang tính và Trang trình bày xem trước các đường liên kết của một dịch vụ bên thứ ba.

Tiện ích bổ sung của Google Workspace có thể phát hiện các đường liên kết đến dịch vụ của bạn và nhắc người dùng xem trước các đường liên kết đó. Bạn có thể định cấu hình tiện ích bổ sung để xem trước nhiều mẫu URL, chẳng hạn như các đường liên kết đến yêu cầu hỗ trợ, khách hàng tiềm năng bán hàng và hồ sơ nhân viên.

Cách người dùng xem trước đường liên kết

Để xem trước đường liên kết, người dùng tương tác với khối thông minhthẻ.

Người dùng xem trước thẻ

Khi người dùng nhập hoặc dán URL vào tài liệu, Google Tài liệu sẽ nhắc họ thay thế đường liên kết bằng một khối thông minh. Khối thông minh cho thấy một biểu tượng và tiêu đề hoặc phần mô tả ngắn về nội dung của đường liên kết. Khi người dùng di chuột qua khối, họ sẽ thấy giao diện thẻ giúp xem trước thêm thông tin về tệp hoặc đường liên kết.

Video sau đây cho biết cách người dùng chuyển đổi đường liên kết thành khối thông minh và xem trước thẻ:

Cách người dùng xem trước đường liên kết trong Trang tính và Trang trình bày

Khối thông minh của bên thứ ba không được hỗ trợ cho bản xem trước đường liên kết trong Trang tính và Trang trình bày. Khi người dùng nhập hoặc dán một URL vào một bảng tính hoặc bản trình bày, Trang tính hoặc Trang trình bày sẽ nhắc họ thay thế đường liên kết bằng tiêu đề dưới dạng văn bản liên kết thay vì một khối. Khi người dùng di chuột lên tiêu đề của đường liên kết, họ sẽ thấy giao diện thẻ cho thấy bản xem trước thông tin về đường liên kết.

Hình ảnh sau đây cho thấy cách bản xem trước đường liên kết hiển thị trong Trang tính và Trang trình bày:

Ví dụ về bản xem trước liên kết cho Trang tính và Trang trình bày

Điều kiện tiên quyết

Apps Script

  • Tài khoản Google Workspace.
  • Tiện ích bổ sung của Google Workspace. Để tạo một tiện ích bổ sung, hãy làm theo phần quickstart này.

Node.js

  • Tài khoản Google Workspace.
  • Tiện ích bổ sung của Google Workspace. Để tạo một tiện ích bổ sung, hãy làm theo phần quickstart này.

Python

  • Tài khoản Google Workspace.
  • Tiện ích bổ sung của Google Workspace. Để tạo một tiện ích bổ sung, hãy làm theo phần quickstart này.

Java

  • Tài khoản Google Workspace.
  • Tiện ích bổ sung của Google Workspace. Để tạo một tiện ích bổ sung, hãy làm theo phần quickstart này.

Không bắt buộc: Thiết lập phương thức xác thực với một dịch vụ của bên thứ ba

Nếu tiện ích bổ sung của bạn kết nối với một dịch vụ yêu cầu cấp phép, thì người dùng phải xác thực dịch vụ đó để xem trước các đường liên kết. Điều này có nghĩa là khi người dùng dán một đường liên kết từ dịch vụ của bạn vào tệp Tài liệu, Trang tính hoặc Trang trình bày lần đầu tiên, tiện ích bổ sung của bạn phải gọi quy trình uỷ quyền.

Để thiết lập dịch vụ OAuth hoặc lời nhắc uỷ quyền tuỳ chỉnh, hãy xem một trong các hướng dẫn sau:

Phần này giải thích cách thiết lập tính năng xem trước đường liên kết cho tiện ích bổ sung của bạn, bao gồm các bước sau:

  1. Định cấu hình bản xem trước đường liên kết trong tệp kê khai hoặc tài nguyên triển khai của tiện ích bổ sung.
  2. Tạo giao diện thẻ và khối thông minh cho các đường liên kết của bạn.

Định cấu hình bản xem trước đường liên kết

Để định cấu hình xem trước đường liên kết, hãy chỉ định các phần và trường sau trong tệp kê khai hoặc tài nguyên triển khai của tiện ích bổ sung:

  1. Trong phần addOns, hãy thêm trường docs để mở rộng Tài liệu, trường sheets để mở rộng Trang tính và trường slides để mở rộng Trang trình bày.
  2. Trong mỗi trường, hãy triển khai điều kiện kích hoạt linkPreviewTriggers bao gồm runFunction (Bạn định nghĩa hàm này trong phần sau: Tạo thẻ và khối thông minh).

    Để tìm hiểu về những trường mà bạn có thể chỉ định trong điều kiện kích hoạt linkPreviewTriggers, hãy xem tài liệu tham khảo cho tệp kê khai Apps Script hoặc tài nguyên triển khai cho các thời gian chạy khác.

  3. Trong trường oauthScopes, hãy thêm phạm vi https://www.googleapis.com/auth/workspace.linkpreview để người dùng có thể uỷ quyền cho tiện ích bổ sung xem trước các đường liên kết thay mặt họ.

Ví dụ: hãy xem phần oauthScopesaddons của tài nguyên triển khai sau đây để định cấu hình bản xem trước đường liên kết cho một dịch vụ yêu cầu hỗ trợ.

{
  "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"
          }
        }
      ]
    }
  }
}

Trong ví dụ, Tiện ích bổ sung Google Workspace giúp xem trước đường liên kết cho dịch vụ yêu cầu hỗ trợ của công ty. Tiện ích bổ sung này chỉ định 3 mẫu URL để xem trước đường liên kết. Bất cứ khi nào một đường liên kết khớp với một trong các mẫu URL, hàm callback caseLinkPreview sẽ tạo và hiển thị một thẻ và một khối thông minh (hoặc trong Trang tính và Trang trình bày sẽ thay thế URL bằng tiêu đề của đường liên kết).

Tạo khối và thẻ thông minh

Để trả về thẻ và khối thông minh cho một đường liên kết, bạn phải triển khai mọi hàm mà bạn đã chỉ định trong đối tượng linkPreviewTriggers.

Khi người dùng tương tác với một đường liên kết khớp với mẫu URL được chỉ định, trình kích hoạt linkPreviewTriggers sẽ kích hoạt và hàm gọi lại của nó sẽ truyền đối tượng sự kiện EDITOR_NAME.matchedUrl.url làm đối số. Bạn sử dụng tải trọng của đối tượng sự kiện này để tạo khối và thẻ thông minh cho bản xem trước đường liên kết.

Ví dụ: nếu người dùng xem trước đường liên kết https://www.example.com/cases/123456 trong Tài liệu, thì tải trọng sự kiện sau đây sẽ được trả về:

JSON

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

Để tạo giao diện thẻ, bạn sẽ sử dụng các tiện ích để hiển thị thông tin về đường liên kết. Bạn cũng có thể tạo các hành động cho phép người dùng mở đường liên kết hoặc sửa đổi nội dung của đường liên kết. Để biết danh sách các tiện ích và thao tác có sẵn, hãy xem phần Các thành phần được hỗ trợ cho thẻ xem trước.

Cách tạo khối và thẻ thông minh cho bản xem trước đường liên kết:

  1. Triển khai hàm mà bạn đã chỉ định trong phần linkPreviewTriggers của tệp kê khai hoặc tài nguyên triển khai của tiện ích bổ sung:
    1. Hàm phải chấp nhận một đối tượng sự kiện có chứa EDITOR_NAME.matchedUrl.url làm đối số và trả về một đối tượng Card duy nhất.
    2. Nếu dịch vụ của bạn yêu cầu uỷ quyền, thì hàm đó cũng phải gọi quy trình uỷ quyền.
  2. Đối với mỗi thẻ xem trước, hãy triển khai bất kỳ hàm callback nào cung cấp khả năng tương tác của tiện ích cho giao diện đó. Ví dụ: nếu thêm nút "Xem đường liên kết", bạn có thể tạo một thao tác chỉ định hàm callback để mở đường liên kết trong cửa sổ mới. Để tìm hiểu thêm về các lượt tương tác với tiện ích, hãy xem bài viết Hành động của tiện ích bổ sung.

Mã sau đây sẽ tạo hàm callback caseLinkPreview cho Tài liệu:

Apps Script

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

nút/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;
}

Các thành phần được hỗ trợ cho thẻ xem trước

Tiện ích bổ sung của Google Workspace hỗ trợ các tiện ích và thao tác sau đây cho thẻ xem trước đường liên kết:

Apps Script

trường Dịch vụ thẻ Loại
TextParagraph Tiện ích
DecoratedText Tiện ích
Image Tiện ích
IconImage Tiện ích
ButtonSet Tiện ích
TextButton Tiện ích
ImageButton Tiện ích
Grid Tiện ích
Divider Tiện ích
OpenLink Hành động
Navigation Hành động
Chỉ hỗ trợ phương thức updateCard.

JSON

Trường thẻ (google.apps.card.v1) Loại
TextParagraph Tiện ích
DecoratedText Tiện ích
Image Tiện ích
Icon Tiện ích
ButtonList Tiện ích
Button Tiện ích
Grid Tiện ích
Divider Tiện ích
OpenLink Hành động
Navigation Hành động
Chỉ hỗ trợ phương thức updateCard.

Ví dụ đầy đủ: Tiện ích bổ sung cho yêu cầu hỗ trợ

Ví dụ sau đây minh hoạ một tiện ích bổ sung của Google Workspace giúp xem trước đường liên kết đến các yêu cầu hỗ trợ của công ty trong Google Tài liệu.

Ví dụ sẽ thực hiện những việc sau:

  • Xem trước đường liên kết đến các yêu cầu hỗ trợ, chẳng hạn như https://www.example.com/support/cases/1234. Khối thông minh sẽ hiện một biểu tượng hỗ trợ, còn thẻ xem trước chứa mã yêu cầu và nội dung mô tả.
  • Nếu ngôn ngữ của người dùng được đặt thành tiếng Tây Ban Nha, thì khối thông minh sẽ bản địa hoá labelText sang tiếng Tây Ban Nha.

Tài nguyên triển khai

Apps Script

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

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

nút/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;
  }

}