Classroom アドオンを作成する

これは、Classroom アドオンのチュートリアル シリーズの最初 のチュートリアルです。

このチュートリアルでは、ウェブ アプリケーションを開発して Classroom アドオンとして公開するための準備を行います。今後のチュートリアルでは、このアプリを拡張します。

このチュートリアルでは、次のことを行います。

  • アドオン用の新しい Google Cloud プロジェクトを作成します。
  • プレースホルダのログインボタンを含むスケルトン ウェブアプリを作成します。
  • アドオンの Google Workspace Marketplace ストアの掲載情報を公開します。

完了したら、アドオンをインストールして Classroom アドオンの iframe に読み込むことができます。

前提条件

適切な前提条件を表示する言語を選択してください。

Python

Python の例では、Flask フレームワークを使用します。すべてのチュートリアルの完全な ソースコードは、概要ページからダウンロードできます。このチュートリアルのコードは、/flask/01-basic-app/ ディレクトリにあります。

必要に応じて、Python 3.7 以降をインストールし、pip が使用可能であることを確認します。

python -m ensurepip --upgrade

新しい Python 仮想環境を設定して有効にすることをおすすめします。

python3 -m venv .classroom-addon-env
source .classroom-addon-env/bin/activate

ダウンロードした例の各チュートリアルのサブディレクトリには、requirements.txt が含まれています。pip を使用して、必要なライブラリをすばやくインストールできます。このチュートリアルに必要なライブラリをインストールするには、次のコマンドを使用します。

cd flask/01-basic-app
pip install -r requirements.txt

Node.js

Node.js の例では、Express フレームワークを使用します。すべてのチュートリアルの 完全なソースコードは、概要ページからダウンロードできます。

必要に応じて、NodeJS v16.13 以降をインストールします。

npm を使用して、必要な Node モジュールをインストールします。

npm install

Java

Java の例では、Spring Boot フレームワークを使用します。すべてのチュートリアルの完全なソースコードは、概要ページからダウンロードできます。

マシンに Java 11 以降がインストールされていない場合は、インストールします。

Spring Boot アプリケーションでは、Gradle または Maven を使用してビルドを処理し、依存関係を管理できます。この例には Maven ラッパーが含まれています。これにより、Maven 自体をインストールしなくてもビルドを成功させることができます。

提供されている例を実行するには、プロジェクトをダウンロードしたディレクトリで次のコマンドを実行して、プロジェクトを実行するための前提条件が整っていることを確認します。

java --version
./mvnw --version

Windows の場合:

java -version
mvnw.cmd --version

Google Cloud プロジェクトの設定

Classroom API へのアクセスと必要な認証方法は、Google Cloud プロジェクトによって制御されます。次の手順では、アドオンで使用する新しいプロジェクトを作成して構成するための最小限の手順について説明します。

プロジェクトを作成する

プロジェクト作成ページに移動して、新しい Google Cloud プロジェクトを作成します。新しいプロジェクトには任意の名前を付けることができます。[作成] をクリックします。

新しいプロジェクトが完全に作成されるまでにはしばらく時間がかかります。完了したら、必ずプロジェクトを選択してください。画面上部のプロジェクト セレクタ プルダウン メニューで選択するか、右上の 通知メニューで [プロジェクトを選択] をクリックします。

Google Cloud コンソールでプロジェクトを選択する

Google Workspace Marketplace SDK を Google Cloud プロジェクトに接続する

API ライブラリ ブラウザに移動します。Google Workspace Marketplace SDK を検索します。検索結果のリストに SDK が表示されます。

Google Workspace Marketplace SDK カード

Google Workspace Marketplace SDK カードを選択し、[有効にする] をクリックします。

Google Workspace Marketplace SDK を構成する

Google Workspace Marketplace には、ユーザーと管理者がアドオンをインストールするための掲載情報が用意されています。続行するには、Marketplace SDK の [アプリの構成]、[ストアの掲載情報]、[OAuth 同意画面] を構成します。

アプリの構成

Marketplace SDK の [アプリの構成] ページに移動します。 次の情報を入力します。

  • [アプリの表示] を Public または Private に設定します。

    • 公開設定は、最終的にエンドユーザーにリリースされるアプリを対象としています。一般公開アプリは、エンドユーザーに公開される前に承認プロセスを経る必要がありますが、インストールしてテストできるユーザーを下書き として指定できます。これは公開前の状態であり、承認のために送信する前にアドオンをテストして開発できます。
    • 非公開設定は、内部テストと開発に適しています。非公開アプリは、プロジェクトが作成されたドメイン内のユーザーのみがインストールできます。したがって、プロジェクトが Google Workspace for Education サブスクリプションのあるドメインで作成された場合にのみ、公開設定を非公開に 設定してください。そうしないと、テストユーザーは Classroom アドオンを起動できません。
  • インストールをドメイン管理者に制限する場合は、[インストール設定] を [Admin Only install] に設定します。

  • [アプリの統合] で [Classroom アドオン] を選択します。安全なアタッチメント設定 URI が求められます。これは、ユーザーがアドオンを開いたときに読み込まれる URL です。 このチュートリアルでは、 https://<your domain>/addon-discoveryに設定します。

  • [**許可されるアタッチメント URI 接頭辞**] は、 AddOnAttachment で設定された URI を、courses.*.addOnAttachments.create メソッドと courses.*.addOnAttachments.patch メソッドを使用して検証するために使用されます。検証はリテラル文字列の接頭辞の一致であり、現時点ではワイルドカードを使用できません。https://localhost:5000/https://cdn.myedtech.com/ など、コンテンツ サーバーのルートドメインを少なくとも 1 つ追加します。

  • 前のステップで OAuth 同意画面に指定したのと同じOAuth スコープ を追加します。

  • [デベロッパー リンク] で、組織に適したフィールドに入力します。

ストアの掲載情報

Marketplace SDK の [ストアの掲載情報] ページに移動します。 次の情報を入力します。

  • [アプリの詳細] で、言語を追加するか、すでにリストされている 言語の横にあるプルダウンを開きます。アプリケーション名と説明を入力します。これらは、アドオンの Google Workspace Marketplace ストアの掲載情報ページに表示されます。 [完了] をクリックして保存します。
  • アドオンのカテゴリ を選択します。
  • [グラフィック アセット] で、必須フィールドの画像を指定します。これらは後で変更できます。現時点ではプレースホルダにすることもできます。
  • [サポートリンク] で、リクエストされた URL を指定します。前のステップで [アプリの表示] を [非公開] に設定した場合は、 プレースホルダにすることもできます。

前の手順でアプリの公開設定を [非公開] に設定した場合は、[公開] をクリックします。アプリはすぐにインストールできるようになります。[アプリの表示] を [**公開**] に設定した場合は、[**下書きテスター**] 領域にテストユーザーのメールアドレスを追加し、[**下書きを保存**] をクリックします。

OAuth 同意画面は、ユーザーがアプリを初めて承認するときに表示されます。有効にしたスコープで指定された個人情報とアカウント情報へのアクセスをアプリに許可するよう求めるメッセージが表示されます。

OAuth 同意画面の作成ページに移動します。次の情報を入力します。

  • [ユーザーの種類] を [外部] に設定します。[作成] をクリックします。
  • 次のページで、必要なアプリの詳細と連絡先情報を入力します。 [承認済みドメイン] に、アプリをホストするドメインを指定します。[保存して次へ] をクリックします。
  • ウェブアプリに必要なOAuth スコープ を追加します。スコープとその目的の詳細については、OAuth 構成ガイドをご覧ください。

    Google が login_hint クエリ パラメータを送信するには、次のスコープのいずれか 1 つ以上をリクエストする必要があります。この動作の詳細については、OAuth 構成ガイドをご覧ください。

    • https://www.googleapis.com/auth/userinfo.email(すでに含まれています)
    • https://www.googleapis.com/auth/userinfo.profile(すでに含まれています)

    次のスコープは、Classroom アドオンに固有のものです。

    • https://www.googleapis.com/auth/classroom.addons.teacher
    • https://www.googleapis.com/auth/classroom.addons.student

    アプリがエンドユーザーに必要とする他の Google API スコープも追加します。

    [保存して次へ] をクリックします。

  • [テストユーザー] ページに、テスト アカウントのメールアドレスを一覧表示します。 [保存して次へ] をクリックします。

設定が正しいことを確認し、ダッシュボードに戻ります。

アドオンをインストールする

Marketplace SDK の [ストアの掲載情報] ページの上部にあるリンクを使用して、アドオンをインストールできるようになりました。ページ上部の [Marketplace で表示] をクリックして掲載情報を表示し、[インストール] を選択します。

基本的なウェブアプリをビルドする

2 つのルートを持つスケルトン ウェブ アプリケーションを設定します。今後のチュートリアルではこのアプリケーションを拡張するため、現時点ではアドオンのランディング ページ /addon-discoveryと、会社のサイトのモック インデックス ページ/を作成します。

iframe 内のウェブアプリの例

次の 2 つのエンドポイントを実装します。

  • /: 現在のタブとアドオンの iframe を閉じるボタンと、ウェルカム メッセージを表示します。
  • /addon-discovery: ウェルカム メッセージと 2 つのボタンを表示します。1 つはアドオンの iframe を閉じるボタン、もう 1 つは新しいタブでウェブサイトを開くボタンです。

ウィンドウまたは iframe を作成して閉じるボタンを追加します。これらは、次のチュートリアルでユーザーを新しいタブに安全にポップアップして認証を行う方法を示しています。

ユーティリティ スクリプトを作成する

static/scripts ディレクトリを作成します。新しいファイル addon-utils.js を作成します。次の 2 つの関数を追加します。

/**
 *   Opens a given destination route in a new window. This function uses
 *   window.open() so as to force window.opener to retain a reference to the
 *   iframe from which it was called.
 *   @param {string} destinationURL The endpoint to open, or "/" if none is
 *   provided.
 */
function openWebsiteInNewTab(destinationURL = '/') {
  window.open(destinationURL, '_blank');
}

/**
 *   Close the iframe by calling postMessage() in the host Classroom page. This
 *   function can be called directly when in a Classroom add-on iframe.
 *
 *   Alternatively, it can be used to close an add-on iframe in another window.
 *   For example, if an add-on iframe in Window 1 opens a link in a new Window 2
 *   using the openWebsiteInNewTab function, you can call
 *   window.opener.closeAddonIframe() from Window 2 to close the iframe in Window
 *   1.
 */
function closeAddonIframe() {
  window.parent.postMessage({
    type: 'Classroom',
    action: 'closeIframe',
  }, '*');
};

ルートを作成する

/addon-discovery エンドポイントと / エンドポイントを実装します。

Python

アプリケーション ディレクトリを設定する

この例では、アプリケーション ロジックを Python モジュールとして構成します。これは、提供されている例の webapp ディレクトリです。

サーバー モジュールのディレクトリ(webapp など)を作成します。static ディレクトリをモジュール ディレクトリに移動します。モジュール ディレクトリに template ディレクトリも作成します。HTML ファイルはここに配置します。

サーバー モジュールをビルドする*

モジュール ディレクトリに __init__.py ファイルを作成し、次のインポートと宣言を追加します。

from flask import Flask
import config

app = Flask(__name__)
app.config.from_object(config.Config)

# Load other module script files. This import statement refers to the
# 'routes.py' file described below.
from webapp import routes

次に、ウェブアプリのルートを処理するファイルを作成します。これは、提供されている例の webapp/routes.py です。このファイルに 2 つのルートを実装します。

from webapp import app
import flask

@app.route("/")
def index():
    return flask.render_template("index.html",
                                message="You've reached the index page.")

@app.route("/classroom-addon")
def classroom_addon():
    return flask.render_template(
        "addon-discovery.html",
        message="You've reached the addon discovery page.")

ルートはどちらも、それぞれの Jinja テンプレートに message 変数を渡します。これは、ユーザーが到達したページを特定するのに役立ちます。

構成ファイルと起動ファイルを作成する

アプリケーションのルート ディレクトリに、main.py ファイルと config.py ファイルを作成します。config.py で秘密鍵を構成します。

import os

class Config(object):
    # Note: A secret key is included in the sample so that it works.
    # If you use this code in your application, replace this with a truly secret
    # key. See https://flask.palletsprojects.com/quickstart/#sessions.
    SECRET_KEY = os.environ.get(
        'SECRET_KEY') or "REPLACE ME - this value is here as a placeholder."

main.py ファイルで、モジュールをインポートして Flask サーバーを起動します。

from webapp import app

if __name__ == "__main__":
    # Run the application over HTTPs with a locally stored certificate and key.
    # Defaults to https://localhost:5000.
    app.run(
        host="localhost",
        ssl_context=("localhost.pem", "localhost-key.pem"),
        debug=True)

Node.js

ルートは、app.js ファイルに次の行で登録されます。

const websiteRouter = require('./routes/index');
const addonRouter = require('./routes/classroom-addon');

app.use('/', websiteRouter);
app.use('/addon-discovery', addonRouter);

/01-basic-app/routes/index.js を開き、コードを確認します。このルートは、エンドユーザーが会社のウェブサイトにアクセスしたときに到達します。このルートは、index Handlebars テンプレートを使用してレスポンスをレンダリングし、テンプレートに title 変数と message 変数を含むデータ オブジェクトを渡します。

router.get('/', function (req, res, next) {
  res.render('index', {
    title: 'Education Technology',
    message: 'Welcome to our website!'
  });
});

2 番目のルート /01-basic-app/routes/classroom-addon.js を開き、コードを確認します。このルートは、エンドユーザーがアドオンにアクセスしたときに到達します。このルートは discovery Handlebars テンプレートを使用し、さらに addon.hbs レイアウトを使用して、会社のウェブサイトとは異なる方法でページをレンダリングします。

router.get('/', function (req, res, next) {
  res.render('discovery', {
    layout: 'addon.hbs',
    title: 'Education Technology Classroom add-on',
    message: `Welcome.`
  });
});

Java

Java コードの例では、モジュールを使用してチュートリアルの手順をパッケージ化します。これは最初のチュートリアルであるため、コードは step_01_basic_app モジュールにあります。モジュールを使用してプロジェクトを実装する必要はありません。チュートリアルの各ステップに沿って、単一のプロジェクトをビルドすることをおすすめします。

エンドポイントを定義するコントローラ クラス(この例では Controller.java)を作成します。このファイルで、spring-boot-starter-web 依存関係から @GetMapping アノテーションをインポートします。

import org.springframework.web.bind.annotation.GetMapping;

クラスの目的を示すために、クラス定義の上に Spring Framework コントローラ アノテーションを追加します。

@org.springframework.stereotype.Controller
public class Controller {

次に、2 つのルートと、エラー処理用の追加ルートを実装します。

/** Returns the index page that will be displayed when the add-on opens in a
*   new tab.
*   @param model the Model interface to pass error information that's
*   displayed on the error page.
*   @return the index page template if successful, or the onError method to
*   handle and display the error message.
*/
@GetMapping(value = {"/"})
public String index(Model model) {
  try {
    return "index";
  } catch (Exception e) {
    return onError(e.getMessage(), model);
  }
}

/** Returns the add-on discovery page that will be displayed when the iframe
*   is first opened in Classroom.
*   @param model the Model interface to pass error information that's
*   displayed on the error page.
*   @return the addon-discovery page.
*/
@GetMapping(value = {"/addon-discovery"})
public String addon_discovery(Model model) {
  try {
    return "addon-discovery";
  } catch (Exception e) {
    return onError(e.getMessage(), model);
  }
}

/** Handles application errors.
*   @param errorMessage message to be displayed on the error page.
*   @param model the Model interface to pass error information to display on
*   the error page.
*   @return the error page.
*/
@GetMapping(value = {"/error"})
public String onError(String errorMessage, Model model) {
  model.addAttribute("error", errorMessage);
  return "error";
}

アドオンをテストする

サーバーを起動します。次に、Google Classroom教師のテストユーザーとしてログインします。[授業] タブに移動し、新しい課題 を作成します。[アドオン] ピッカーからアドオンを選択します。iframe が開き 、アドオンが Marketplace SDK の [アプリの構成] ページで指定した **アタッチメント設定 URI** を読み込みます。

おめでとうございます!次のステップに進む準備ができました。Google SSO でユーザーにログインします