サーバーサイド スクリプティング

サーバーサイド スクリプトは、Google Apps Script スクリプトとして JavaScript で実行されます。このスクリプトは、Apps Script API だけでなく、App Maker のサーバーサイド オブジェクトと API にもアクセスします。スクリプトを使用する前に、Google 拡張サービスを有効にする必要があります。

サーバーサイド スクリプトの基本

時間のかかる計算を実行する場合(UI のブロックを回避するため)や、サイズやセキュリティ上の理由でクライアントに送信したくない情報にアクセスする必要がある場合は、サーバーサイド スクリプトを使用してください。

サーバー スクリプトを呼び出す

サーバー スクリプトを呼び出すには、google.script.run を使用します。詳細については、クライアント サイド スクリプトのドキュメントをご覧ください。

パラメータと戻り値

値は、google.script.runwithSuccessHandler(function) メソッドに返されます。有効なパラメータと戻り値は、NumberBooleanString などの JavaScript プリミティブと、プリミティブ、オブジェクト、配列で構成される JavaScript オブジェクトと配列です。

たとえば、Date オブジェクト、FunctionRecord、または App Maker ウィジェットを渡そうとすると、リクエストは失敗します。より複雑なオブジェクトを渡す場合には、JSON.stringifyJSON.parse を使用します。

サーバー スクリプトを隠す

UI で公開していなくても、サーバー スクリプトで定義した機能にはアプリケーションのすべてのユーザーがアクセスできます。この点には十分に注意してください。他のサーバー スクリプトからしか呼び出せないユーティリティ機能を作成する場合には、名前にアンダースコアを追加する必要があります。たとえば、次のメソッドは public になるため、渡されたユーザーの給与情報がすべてのクライアントで利用可能になります。

function calculateUserSalary(user) {
  // Gets the salary of the user...
  return salary;
}

代わりに、次のようにします。

function calculateUserSalary_(user) {
  // Gets the salary of the user...
  return salary;
}

セキュリティ

サーバー スクリプトを非表示にするのは有効な対策ですが、サーバー スクリプトで重要なデータをクライアントに提供しなければならない場合もあります。この場合は、スクリプトを保護する必要があります。モデルとビューには権限が用意されていますが、サーバー スクリプトの場合は、独自に実装する必要があります。上記の例で、ユーザーに呼び出しを許可し、ユーザー自身またはそのマネージャーがリクエストを送信している場合にのみ、ユーザーの給与情報を返すようにするには、次のように記述します。

function calculateUserSalary(user) {
  var currentUser = Session.getActiveUser().getEmail();
  if (currentUser !== user && !isManager(currentUser, user) {
    throw new Error(currentUser +
        " does not have access to salary for " + user);
  }

  // Gets the salary of the user...
  return salary;
}

データの操作

サーバー スクリプトを使用すると、一般的な方法で App Maker モデルを操作できます。たとえば、新しいレコードの追加、クエリ、リレーションの編集を行うことができます。

レコードの作成

// Assume a model called "Fruits" with a string field called "Name".
var newRecord = app.models.Fruits.newRecord();
newRecord.Name = "Kiwi";  // properties/fields can be read and written.
app.saveRecords([newRecord]);  // The changes won't be committed to the
                               // database unless you remember to save.

レコードの照会

var query = app.models.Person.newQuery();
query.filters.Name._equals = "John";

var records = query.run();
console.log("Found " + records.length + " Johns.");

for (var i in records) {
    var person = records[i];
    console.log("Found person named John " + person.FamilyName);
}

リレーションの操作

次の例では、関連レコードを作成してクエリを実行しています。ここでは、Address モデルを使用しています。このモデルは、Country モデルと多対 1 の関係が設定されています。

var address1 = app.models.Address.newRecord();
address1.zip = 94040;
var address2 = app.models.Address.newRecord();
address2.zip = 94041;
app.saveRecords([address1, address2]); // Save created records.

var query = app.models.Country.newQuery();
query.filters.Name._equals = countryName;
var country = query.run()[0];

country.Address.push(address1, address2);
app.saveRecords([country]); // Save updated relation.

以下のコードのように、関連フィールドでフィルタリングすることもできます。

function addressesForCountry(countryName) {
  var query = app.models.Address.newQuery();
  query.filters.Country.Name._equals = countryName;

  var records = query.run();
  return records;
};

詳細については、リレーション フィルタリングをご覧ください。

CloudSQL トランザクションの制御

次の例では、サーバー スクリプトを使用して Cloud SQL トランザクションを制御しています。この API を使用して、次の処理を行います。

  • トランザクションを開始し、関連レコードをロックする。
  • データベースに新しい変更を commit する。
  • トランザクションをロールバックする。
function submitOrder(orderKey) {

  // Sets a rollback point and starts a new transaction.
  app.transaction.cloudSql.start();

   // Locks all records that are read in the transaction until the end of the transaction.
  app.transaction.cloudSql.setLockOnRead(app.transaction.cloudSql.lockOnRead.UPDATE);

  var orderQuery = app.models.Order.newQuery();
  orderQuery.filters._key._equals = orderKey;
  var order = orderQuery.run()[0];

  // Read related order items
  var orderItems = order.OrderItems;

  var products = [];
  for (var i = 0; i < orderItems.length; i++) {
    // Read related product
    var product  = orderItem.Product;

    if (product.InStockQuantity < orderItem.Quantity) {
      // Insufficient stock to complete an order; transaction is rolled back.
      app.transaction.cloudSql.rollback();
      throw new app.ManagedError("Not enough " + product.Name + " in stock. " +
                                 product.InStockQuantity + " Order: " +
                                 orderItem.Quantity + " can't be completed.");
    }

    // Reduce inStock quantity
    product.InStockQuantity = product.InStockQuantity - orderItem.Quantity;
    products.push(product);
  }

  order.Status = "Submitted";
  app.saveRecords(products);
  app.saveRecords([order]);

  // Order is successfully processed; database changes committed.
  app.transaction.cloudSql.commit();

  return "Order submitted: " + orderKey;
}

トラブルシューティング

App Maker では構文エラーが赤色で表示されます。一般的な問題(未使用の変数など)が見つかると、警告が表示されます。

スクリプトが予期したとおり動作しない場合、App Maker の console オブジェクトを使用して、関連情報を記録できます。例:

function getRecordsForCurrentUser() {
  var currentUser = Session.getActiveUser().getEmail();
  var query = app.models.Person.newQuery();
  query.filters.Email._equals = currentUser;

  var records = query.run();
  console.log("Found " + records.length + " records for " + currentUser);
  return records;
};

ログは、アプリケーションのデプロイメントごとに別々に表示できます。