JavaScript 快速入门

快速入门介绍了如何设置和运行调用 Google Workspace API 的应用。

Google Workspace 快速入门使用 API 客户端库来处理身份验证和授权流程的一些细节。我们建议您为自己的应用使用客户端库。本快速入门使用适合测试环境的简化身份验证方法。对于生产环境,我们建议您先了解身份验证和授权,然后再选择适合您的应用的访问凭据

创建一个 JavaScript Web 应用,用于向 Google Drive Activity API 发出请求。

目标

  • 设置环境。
  • 设置示例。
  • 运行示例。

前提条件

  • Google 账号

设置环境

如需完成本快速入门,请设置您的环境。

启用 API

在使用 Google API 之前,您需要先在 Google Cloud 项目中启用这些 API。您可以在单个 Google Cloud 项目中启用一个或多个 API。
  • 在 Google Cloud 控制台中,启用 Google Drive Activity API。

    启用 API

如果您使用新的 Google Cloud 项目完成本快速入门,请配置 OAuth 同意屏幕,并将自己添加为测试用户。如果您已为 Cloud 项目完成此步骤,请跳至下一部分。

  1. 在 Google Cloud 控制台中,依次点击“菜单”图标 > API 和服务 > OAuth 权限请求页面

    转到 OAuth 同意屏幕

  2. 用户类型字段中,选择内部,然后点击创建
  3. 填写应用注册表单,然后点击保存并继续
  4. 现在,您可以跳过添加镜重,然后点击保存并继续。 今后,如果您创建的应用要在 Google Workspace 组织之外使用,则必须将用户类型更改为外部,然后添加应用所需的授权范围。

  5. 查看应用注册摘要。如需进行更改,请点击修改。如果应用注册未出现任何问题,请点击返回信息中心

为 Web 应用授权凭据

如需对最终用户进行身份验证并访问应用中的用户数据,您需要创建一个或多个 OAuth 2.0 客户端 ID。客户端 ID 用于向 Google 的 OAuth 服务器标识单个应用。如果您的应用在多个平台上运行,您必须为每个平台分别创建客户端 ID。
  1. 在 Google Cloud 控制台中,依次选择“菜单”图标 > API 和服务 > 凭据

    进入“凭据”页面

  2. 依次点击创建凭据 > OAuth 客户端 ID
  3. 依次点击应用类型 > Web 应用
  4. 名称字段中,输入凭据的名称。此名称仅在 Google Cloud 控制台中显示。
  5. 添加与您的应用相关的已获授权 URI:
    • 客户端应用 (JavaScript) - 在已获授权的 JavaScript 来源下,点击添加 URI。然后,输入要用于浏览器请求的 URI。这用于标识您的应用可以从哪些网域向 OAuth 2.0 服务器发送 API 请求。
    • 服务器端应用(Java、Python 等) - 在已获授权的重定向 URI 下,点击添加 URI。然后,输入 OAuth 2.0 服务器可以向其发送响应的端点 URI。
  6. 点击创建。系统随即会显示“OAuth 客户端已创建”屏幕,其中显示您的新客户端 ID 和客户端密钥。

    记下客户端 ID。客户端密钥不适用于 Web 应用。

  7. 点击确定。新创建的凭据会显示在 OAuth 2.0 客户端 ID 下。

记下这些凭据,因为您将在本快速入门的后面部分用到它们。

创建 API 密钥

  1. 在 Google Cloud 控制台中,依次点击“菜单”图标 > API 和服务 > 凭据

    进入“凭据”页面

  2. 依次点击创建凭据 > API 密钥
  3. 系统会显示您的新 API 密钥。
    • 点击“复制”图标 以复制您的 API 密钥,以便在应用的代码中使用。您也可以在项目凭据的“API 密钥”部分找到该 API 密钥。
    • 点击限制密钥可更新高级设置并限制 API 密钥的使用。如需了解详情,请参阅应用 API 密钥限制

设置示例

  1. 在工作目录中,创建一个名为 index.html 的文件。
  2. index.html 文件中,粘贴以下示例代码:

    drive/activity-v2/index.html
    <!DOCTYPE html>
    <html>
      <head>
        <title>Drive Activity API Quickstart</title>
        <meta charset="utf-8" />
      </head>
      <body>
        <p>Drive Activity API Quickstart</p>
    
        <!--Add buttons to initiate auth sequence and sign out-->
        <button id="authorize_button" onclick="handleAuthClick()">Authorize</button>
        <button id="signout_button" onclick="handleSignoutClick()">Sign Out</button>
    
        <pre id="content" style="white-space: pre-wrap;"></pre>
    
        <script type="text/javascript">
          /* exported gapiLoaded */
          /* exported gisLoaded */
          /* exported handleAuthClick */
          /* exported handleSignoutClick */
    
          // TODO(developer): Set to client ID and API key from the Developer Console
          const CLIENT_ID = '<YOUR_CLIENT_ID>';
          const API_KEY = '<YOUR_API_KEY>';
    
          // Discovery doc URL for APIs used by the quickstart
          const DISCOVERY_DOC = 'https://www.googleapis.com/discovery/v1/apis/driveactivity/v2/rest';
    
          // Authorization scopes required by the API; multiple scopes can be
          // included, separated by spaces.
          const SCOPES = 'https://www.googleapis.com/auth/drive.activity.readonly';
    
          let tokenClient;
          let gapiInited = false;
          let gisInited = false;
    
          document.getElementById('authorize_button').style.visibility = 'hidden';
          document.getElementById('signout_button').style.visibility = 'hidden';
    
          /**
           * Callback after api.js is loaded.
           */
          function gapiLoaded() {
            gapi.load('client', initializeGapiClient);
          }
    
          /**
           * Callback after the API client is loaded. Loads the
           * discovery doc to initialize the API.
           */
          async function initializeGapiClient() {
            await gapi.client.init({
              apiKey: API_KEY,
              discoveryDocs: [DISCOVERY_DOC],
            });
            gapiInited = true;
            maybeEnableButtons();
          }
    
          /**
           * Callback after Google Identity Services are loaded.
           */
          function gisLoaded() {
            tokenClient = google.accounts.oauth2.initTokenClient({
              client_id: CLIENT_ID,
              scope: SCOPES,
              callback: '', // defined later
            });
            gisInited = true;
            maybeEnableButtons();
          }
    
          /**
           * Enables user interaction after all libraries are loaded.
           */
          function maybeEnableButtons() {
            if (gapiInited && gisInited) {
              document.getElementById('authorize_button').style.visibility = 'visible';
            }
          }
    
          /**
           *  Sign in the user upon button click.
           */
          function handleAuthClick() {
            tokenClient.callback = async (resp) => {
              if (resp.error !== undefined) {
                throw (resp);
              }
              document.getElementById('signout_button').style.visibility = 'visible';
              document.getElementById('authorize_button').innerText = 'Refresh';
              await listActivities();
            };
    
            if (gapi.client.getToken() === null) {
              // Prompt the user to select a Google Account and ask for consent to share their data
              // when establishing a new session.
              tokenClient.requestAccessToken({prompt: 'consent'});
            } else {
              // Skip display of account chooser and consent dialog for an existing session.
              tokenClient.requestAccessToken({prompt: ''});
            }
          }
    
          /**
           *  Sign out the user upon button click.
           */
          function handleSignoutClick() {
            const token = gapi.client.getToken();
            if (token !== null) {
              google.accounts.oauth2.revoke(token.access_token);
              gapi.client.setToken('');
              document.getElementById('content').innerText = '';
              document.getElementById('authorize_button').innerText = 'Authorize';
              document.getElementById('signout_button').style.visibility = 'hidden';
            }
          }
    
          /**
           * Print recent activity.
           */
          async function listActivities() {
            let response;
            try {
              response = await gapi.client.driveactivity.activity.query({
                'pageSize': 10,
              });
            } catch (err) {
              document.getElementById('content').innerText = err.message;
              return;
            }
    
            const activities = response.result.activities;
            if (!activities || activities.length == 0) {
              document.getElementById('content').innerText = 'No activities found.';
              return;
            }
            // Flatten to string to display
            const output = activities.reduce(
                (str, activity) => {
                  const time = getTimeInfo(activity);
                  const action = getActionInfo(activity['primaryActionDetail']);
                  const actors = activity.actors.map(getActorInfo);
                  const targets = activity.targets.map(getTargetInfo);
                  return `${str}${time}: ${truncated(actors)}, ${action}, ${truncated(targets)}\n`;
                },
                'Activities:\n');
            document.getElementById('content').innerText = output;
          }
    
          // Utility methods for formatting activity records
    
          /**
           * Returns a string representation of the first N elements in a list.
           * @param {string[]} array - Values to join
           * @param {number} limit - # of elements to show
           * @return {string} formatted string
           */
          function truncated(array, limit = 2) {
            const contents = array.slice(0, limit).join(', ');
            const more = array.length > limit ? ', ...' : '';
            return `[${contents}${more}]`;
          }
    
          /**
           * Returns the first found property name in an object.
           * @param {object} object - Object to search
           * @return {string} key name or 'unknown'
           */
          function getOneOf(object) {
            const props = Object.getOwnPropertyNames(object);
            if (props.length === 0) {
              return 'unknown';
            }
            return props[0];
          }
    
          /**
           * Returns a time associated with an activity.
           * @param {object} activity - Activity record
           * @return {string} Formatted timestamp
           */
          function getTimeInfo(activity) {
            if ('timestamp' in activity) {
              return activity.timestamp;
            }
            if ('timeRange' in activity) {
              return activity.timeRange.endTime;
            }
            return 'unknown';
          }
    
          /**
           * Returns the type of action.
           * @param {object} actionDetail
           * @return {string} Action type as string
           */
          function getActionInfo(actionDetail) {
            return getOneOf(actionDetail);
          }
    
          /**
           * Returns user information, or the type of user if not a known user.
           * @param {object} user - User record
           * @return {string} user type as string
           */
          function getUserInfo(user) {
            if ('knownUser' in user) {
              const knownUser = user['knownUser'];
              const isMe = knownUser['isCurrentUser'] || false;
              return isMe ? 'people/me' : knownUser['personName'];
            }
            return getOneOf(user);
          }
    
          /**
           * Returns actor information, or the type of actor if not a user.
           * @param {object} actor - Actor record
           * @return {string} actor type as string
           */
          function getActorInfo(actor) {
            if ('user' in actor) {
              return getUserInfo(actor['user']);
            }
            return getOneOf(actor);
          }
    
          /**
           * Returns the type of a target and an associated title.
           * @param {object} target - Activity target record
           * @return {string} target type as string
           */
          function getTargetInfo(target) {
            if ('driveItem' in target) {
              const title = target.driveItem.title || 'unknown';
              return `driveItem:"${title}"`;
            }
            if ('drive' in target) {
              const title = target.drive.title || 'unknown';
              return `drive:"${title}"`;
            }
            if ('fileComment' in target) {
              const parent = target.fileComment.parent || {};
              const title = parent.title || 'unknown';
              return `fileComment:"${title}"`;
            }
            return `${getOneOf(target)}:unknown`;
          }
        </script>
        <script async defer src="https://apis.google.com/js/api.js" onload="gapiLoaded()"></script>
        <script async defer src="https://accounts.google.com/gsi/client" onload="gisLoaded()"></script>
      </body>
    </html>

    替换以下内容:

运行示例

  1. 在工作目录中,安装 http-server 软件包:

    npm install http-server
    
  2. 在工作目录中,启动 Web 服务器:

    npx http-server -p 8000
    
  1. 在浏览器中,前往 http://localhost:8000
  2. 您会看到授予访问权限的提示:
    1. 如果您尚未登录 Google 账号,请在系统提示时登录。如果您登录了多个账号,请选择一个账号用于授权。
    2. 点击接受

您的 JavaScript 应用运行并调用 Google Drive Activity API。

后续步骤