Web API 条件付き演算のサンプル (クライアント側の JavaScript)

注意

エンティティとテーブルの違いがわかりませんか? Microsoft Dataverse で「開発者: 用語を理解する」を参照してください。

このサンプルでは、クライアント側の JavaScript を使用する Microsoft Dataverse Web API を使用して、条件付き演算を実行する方法を説明します。

注意

このサンプルでは、Web API 条件付き演算 サンプル で詳細を説明されている操作を実装し、Web API のサンプル (クライアント側の JavaScript) で説明されている共通のクライアント側の JavaScript 構造を使用します。

前提条件

このサンプルを実行するには、次が必要です:

  • Dataverse 環境へアクセスします。

  • ソリューションのインポートと CRUD 操作を実行する特権を持つユーザー アカウント、通常はシステム管理者またはシステム カスタマイザーのセキュリティ ロールを持つアカウントです。

このサンプルの実行

このサンプルを実行するには、ここから ソリューション パッケージをダウンロードし、コンテンツを抽出して、WebAPIConditionalOperations_1_0_0_0_managed.zip 管理ソリューションを検索します。 管理ソリューションを Dataverse 環境にインポートして、サンプルを実行するにはソリューション構成ページを表示します。 サンプル ソリューションをインポートする方法の詳細については、Web API サンプル (クライアント側の JavaScript) を参照してください。

コード サンプル

このサンプルには 2 つの Web リソースが含まれています:

WebAPIConditionalOperations.html

WebAPIConditionalOperations.html Web リソースは、JavaScript コードを実行するコンテキストを提供します。

<!DOCTYPE html>
<html>
  <head>
    <title>Microsoft CRM Web API Conditional Operations Example</title>
    <meta charset="utf-8" />
    <script
      src="../ClientGlobalContext.js.aspx"
      type="text/javascript"
    ></script>
    <script src="scripts/es6promise.js"></script>
    <script src="scripts/WebAPIConditionalOperations.js"></script>

    <style type="text/css">
      body {
        font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
      }
    </style>
  </head>
  <body>
    <h1>Microsoft CRM Web API Conditional Operations Example</h1>
    <p>
      This page demonstrates the CRM Web API's Conditional Operations using
      JavaScript.
    </p>

    <h2>Instructions</h2>
    <p>
      Choose your preferences and run the JavaScript code. Use your browser's
      developer tools to view the output written to the console (e.g.: in IE11
      or Microsoft Edge, press F12 to load the Developer Tools).
    </p>
    <form id="preferences">
      <p>This sample deletes the single record it creates.</p>
      <input
        type="button"
        name="start_samples"
        value="Start Sample"
        onclick="Sdk.startSample()"
      />
    </form>
  </body>
</html>

WebAPIConditionalOperations.js

WebAPIConditionalOperations.js Web リソースは、このサンプルが実行する操作を定義する JavaScript ライブラリです。

"use strict";
var Sdk = window.Sdk || {};
/**
 * @function getClientUrl
 * @description Get the client URL.
 * @return {string} The client URL.
 */
Sdk.getClientUrl = function () {
  var context;
  // GetGlobalContext defined by including reference to
  // ClientGlobalContext.js.aspx in the HTML page.
  if (typeof GetGlobalContext != "undefined") {
    context = GetGlobalContext();
  } else {
    if (typeof Xrm != "undefined") {
      // Xrm.Page.context defined within the Xrm.Page object model for form scripts.
      context = Xrm.Page.context;
    } else {
      throw new Error("Context is not available.");
    }
  }
  return context.getClientUrl();
};

// Global variables.
var clientUrl = Sdk.getClientUrl(); // e.g.: https://org.crm.dynamics.com
var webAPIPath = "/api/data/v8.1"; // Path to the web API.
var account1Uri; // e.g.: Contoso Ltd (sample)
var initialAcctETagVal; // The initial ETag value of the account created
var updatedAcctETagVal; // The ETag value of the account after it is updated

// Entity properties to select in a request.
var contactProperties = ["fullname", "jobtitle", "annualincome"];
var accountProperties = ["name"];
var taskProperties = ["subject", "description"];

/**
 * @function request
 * @description Generic helper function to handle basic XMLHttpRequest calls.
 * @param {string} action - The request action. String is case-sensitive.
 * @param {string} uri - An absolute or relative URI. Relative URI starts with a "/".
 * @param {object} data - An object representing an entity. Required for create and update actions.
 * @param {object} addHeader - An object with header and value properties to add to the request
 * @returns {Promise} - A Promise that returns either the request object or an error object.
 */
Sdk.request = function (action, uri, data, addHeader) {
  if (!RegExp(action, "g").test("POST PATCH PUT GET DELETE")) {
    // Expected action verbs.
    throw new Error(
      "Sdk.request: action parameter must be one of the following: " +
        "POST, PATCH, PUT, GET, or DELETE."
    );
  }
  if (!typeof uri === "string") {
    throw new Error("Sdk.request: uri parameter must be a string.");
  }
  if (RegExp(action, "g").test("POST PATCH PUT") && !data) {
    throw new Error(
      "Sdk.request: data parameter must not be null for operations that create or modify data."
    );
  }
  if (addHeader) {
    if (
      typeof addHeader.header != "string" ||
      typeof addHeader.value != "string"
    ) {
      throw new Error(
        "Sdk.request: addHeader parameter must have header and value properties that are strings."
      );
    }
  }

  // Construct a fully qualified URI if a relative URI is passed in.
  if (uri.charAt(0) === "/") {
    uri = clientUrl + webAPIPath + uri;
  }

  return new Promise(function (resolve, reject) {
    var request = new XMLHttpRequest();
    request.open(action, encodeURI(uri), true);
    request.setRequestHeader("OData-MaxVersion", "4.0");
    request.setRequestHeader("OData-Version", "4.0");
    request.setRequestHeader("Accept", "application/json");
    request.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    if (addHeader) {
      request.setRequestHeader(addHeader.header, addHeader.value);
    }
    request.onreadystatechange = function () {
      if (this.readyState === 4) {
        request.onreadystatechange = null;
        switch (this.status) {
          case 200: // Success with content returned in response body.
          case 204: // Success with no content returned in response body.
          case 304: // Success with Not Modified.
            resolve(this);
            break;
          default: // All other statuses are error cases.
            var error;
            try {
              error = JSON.parse(request.response).error;
            } catch (e) {
              error = new Error("Unexpected Error");
            }
            reject(error);
            break;
        }
      }
    };
    request.send(JSON.stringify(data));
  });
};

/**
 * @function startSample
 * @description Runs the sample.
 * This sample demonstrates conditional operations using CRM Web API.
 * Results are sent to the debugger's console window.
 */
Sdk.startSample = function () {
  // Initializing...
  console.log("-- Sample started --");

  // Create the CRM account instance.
  var account = {
    name: "Contoso, Ltd",
    telephone1: "555-0000", // Phone number value will increment with each update attempt.
    revenue: 5000000,
    description: "Parent company of Contoso Pharmaceuticals, etc.",
  };

  var uri = "/accounts"; // A relative URi to the account entity.
  Sdk.request("POST", uri, account)
    .then(function (request) {
      console.log("Account entity created.");
      // Assign the Uri to the created account to a global variable.
      account1Uri = request.getResponseHeader("OData-EntityId");

      // Retrieve the created account entity.
      return Sdk.request(
        "GET",
        account1Uri + "?$select=name,revenue,telephone1,description"
      );
    })
    .then(function (request) {
      // Show the current entity properties.
      var account = JSON.parse(request.response);
      console.log(JSON.stringify(account, null, 2));

      initialAcctETagVal = account["@odata.etag"]; // Save the current ETag value.

      // Conditional Get START.
      // Attempt to retrieve using conditional GET with current ETag value.
      // Expecting nothing in the response because entity was not modified.
      console.log("-- Conditional GET section started --");
      var ifNoneMatchETag = {
        header: "If-None-Match",
        value: initialAcctETagVal,
      };
      return Sdk.request(
        "GET",
        account1Uri + "?$select=name,revenue,telephone1,description",
        null,
        ifNoneMatchETag
      );
    })
    .then(function (request) {
      console.log("Instance retrieved using ETag: %s", initialAcctETagVal);
      if (request.status == 304) {
        //Expected:
        console.log("\tEntity was not modified so nothing was returned.");
        console.log(request.response); //Nothing
      } else {
        //Not Expected:
        console.log(JSON.stringify(JSON.parse(request.response), null, 2));
      }

      // Modify the account instance by updating telephone1.
      // This request operation will also update the ETag value.
      return Sdk.request("PUT", account1Uri + "/telephone1", {
        value: "555-0001",
      });
    })
    .then(function (request) {
      console.log("Account telephone number updated.");

      // Re-attempt conditional GET with original ETag value.
      var ifNoneMatchETag = {
        header: "If-None-Match",
        value: initialAcctETagVal,
      };
      return Sdk.request(
        "GET",
        account1Uri + "?$select=name,revenue,telephone1,description",
        null,
        ifNoneMatchETag
      );
    })
    .then(function (request) {
      if (request.status == 200) {
        // Expected.
        console.log("Instance retrieved using ETag: %s", initialAcctETagVal);
        var account = JSON.parse(request.response);
        updatedAcctETagVal = account["@odata.etag"]; //Capture updated ETag.
        console.log(JSON.stringify(account, null, 2));
      } else {
        // Not Expected.
        console.log("Unexpected status: %s", request.status);
      }
      // Conditional Get END.

      // Optimistic concurrency on delete and update START.
      console.log("-- Optimistic concurrency section started --");
      // Attempt to delete original account (only if matches original ETag value).
      var ifMatchETag = { header: "If-Match", value: initialAcctETagVal };
      return Sdk.request("DELETE", account1Uri, null, ifMatchETag);
    })
    .then(
      function (request) {
        // Success not expected.
        console.log("Unexpected status: %s", request.status);
      },
      // Catch error.
      function (error) {
        // DELETE: Precondition failed error expected.
        console.log("Expected Error: %s", error.message);
        console.log(
          "\tAccount not deleted using ETag '%s', status code: '%s'.",
          initialAcctETagVal,
          412
        );

        // Attempt to update account (if matches original ETag value).
        var accountUpdate = {
          telephone1: "555-0002",
          revenue: 6000000,
        };
        var ifMatchETag = { header: "If-Match", value: initialAcctETagVal };
        return Sdk.request("PATCH", account1Uri, accountUpdate, ifMatchETag);
      }
    )
    .then(
      function (request) {
        // Success not expected.
        console.log("Unexpected status: %s", request.status);
      },
      // Catch error.
      function (error) {
        // UPDATE: Precondition failed error expected.
        console.log("Expected Error: %s", error.message);
        console.log(
          "\tAccount not updated using ETag '%s', status code: '%s'.",
          initialAcctETagVal,
          412
        );

        // Re-attempt update if matches current ETag value.
        var accountUpdate = {
          telephone1: "555-0003",
          revenue: 6000000,
        };
        var ifMatchETag = { header: "If-Match", value: updatedAcctETagVal };
        return Sdk.request("PATCH", account1Uri, accountUpdate, ifMatchETag);
      }
    )
    .then(function (request) {
      if (request.status == 204) {
        //No Content
        // Expected.
        console.log(
          "Account successfully updated using ETag '%s', status code: '%s'.",
          updatedAcctETagVal,
          request.status
        );
      } else {
        // Not Expected.
        console.log("Unexpected status: %s", request.status);
      }
      // Retrieve and output current account state.
      return Sdk.request(
        "GET",
        account1Uri + "?$select=name,revenue,telephone1,description"
      );
    })
    .then(function (request) {
      var account = JSON.parse(request.response);
      updatedAcctETagVal = account["@odata.etag"]; // Capture updated ETag.
      console.log(JSON.stringify(account, null, 2));
      // Optimistic concurrency on delete and update END.

      // Controlling upsert operations START.
      console.log("-- Controlling upsert operations section started --");

      // Attempt to insert (without update) some properties for this account.
      var accountUpsert = {
        telephone1: "555-0004",
        revenue: 7500000,
      };
      var ifNoneMatchResource = { header: "If-None-Match", value: "*" };
      return Sdk.request(
        "PATCH",
        account1Uri,
        accountUpsert,
        ifNoneMatchResource
      );
    })
    .then(
      function (request) {
        // Success not expected.
        console.log("Unexpected status: %s", request.status);
      },
      // Catch error.
      function (error) {
        // Precondition failed error expected.
        console.log("Expected Error: %s", error.message);
        console.log(
          "\tAccount not updated using ETag '%s', status code: '%s'.",
          initialAcctETagVal,
          412
        );

        // Attempt to perform same update without creation.
        var accountUpsert = {
          telephone1: "555-0005",
          revenue: 7500000,
        };
        // Perform operation only if matching resource exists.
        var ifMatchResource = { header: "If-Match", value: "*" };
        return Sdk.request(
          "PATCH",
          account1Uri,
          accountUpsert,
          ifMatchResource
        );
      }
    )
    .then(function (request) {
      if (request.status == 204) {
        // No Content.
        // Expected.
        console.log("Account updated using If-Match '*'");
      } else {
        // Not Expected.
        console.log("Unexpected status: %s", request.status);
      }

      // Retrieve and output current account state.
      return Sdk.request(
        "GET",
        account1Uri + "?$select=name,revenue,telephone1,description"
      );
    })
    .then(function (request) {
      var account = JSON.parse(request.response);
      updatedAcctETagVal = account["@odata.etag"]; // Capture updated ETag.
      console.log(JSON.stringify(account, null, 2));

      // Controlling upsert operations END.

      // Prevent update of deleted entity START.
      // Delete the account.
      return Sdk.request("DELETE", account1Uri);
    })
    .then(function (request) {
      if (request.status == 204) {
        console.log("Account was deleted");

        // Attempt to update it.
        var accountUpsert = {
          telephone1: "555-0005",
          revenue: 7500000,
        };
        // Perform operation only if matching resource exists.
        var ifMatchResource = { header: "If-Match", value: "*" };
        return Sdk.request(
          "PATCH",
          account1Uri,
          accountUpsert,
          ifMatchResource
        );
      }
    })
    .then(
      function (request) {
        // Success not expected.
        // Without the If-Match header while using PATCH a new entity would have been created with the
        // same ID as the deleted entity.
        console.log("Unexpected status: %s", request.status);
      },
      // Catch error.
      function (error) {
        // Not found error expected.
        console.log("Expected Error: %s", error.message);
        console.log("\tAccount not updated because it doesn't exist.");
      }
    )
    .catch(function (error) {
      console.log(error.message);
    });
};

関連項目

Dataverse Web API を使用する
Web API を使用する条件付き演算を実行する
Web API のサンプル
Web API 条件付き演算サンプル
Web API 条件付き演算サンプル (C#)
Web API のサンプル (クライアント側の JavaScript)
Web API 基本操作のサンプル (クライアント側の JavaScript)
Web API クエリ データのサンプル (クライアント側の JavaScript)
Web API 機能およびアクションのサンプル (クライアント側 JavaScript)

注意

ドキュメントの言語設定についてお聞かせください。 簡単な調査を行います。 (この調査は英語です)

この調査には約 7 分かかります。 個人データは収集されません (プライバシー ステートメント)。