Cisco Prime Service Catalog 11.0 設計者ガイド
フォーム ルールおよび ISF Javascript の使用例の分析
フォーム ルールおよび ISF Javascript の使用例の分析

目次

フォーム ルールおよび ISF Javascript の使用例の分析

この付録は、次の内容で構成されています。

フォーム ルールおよび ISF Javascript の使用例の分析

複雑なサービス カタログでは、数百ものインスタンスを容易に入手できます。ここで、アクティブ フォーム ルールまたは ISF 関数を使用して、サービス フォームの使用性と対話性を向上させることができます。

  1. どのルールが必要でそれらがいつ起動するかを判断するために、使用例の分析を実行します。
  2. 詳細設計分析を実行して、指定された使用例のサポートに必要なディクショナリとフィールド、ディクショナリをフォームに組み込む方法、および要件の実装に必要なツールを判断します。
  3. ディクショナリとフォームを定義します。 フォームの表示とアクセス制御のプロパティを指定します。
  4. アクティブ フォーム ルールを記述し、詳細設計の条件を満たすように並べます。 必要に応じて、JavaScript 関数とライブラリを記述して、関数を適切な HTML イベントまたはイベント群にアタッチします。
  5. 以前定義した再使用可能なフォーム コンポーネントを使用するサービスを定義します。
  6. 必要に応じてテストを実施し、修正を行います。

この項では、サービス フォームに統合されたルールと ISF コードの両方の例、および要件を満たすための実装上の各判断を示します。 これらの例では、次のようないくつかの一般的な設計パターンを示します。

  • 特定のタスクに基づいて、ディクショナリの外観または動作を調整する
  • サービス フォームがロードされた場合、またはユーザが別のフィールドの値を変更した場合に、ディクショナリまたはフィールドを表示/非表示にする
  • マネージャが [ユーザ(Person)] フィールドの変更を検索する
  • ドリル ダウンする
  • 提供時点で、選択リストに値が入力されていることを確認する
  • 値が現在のコンテキストに関連していない場合は、ディクショナリを非表示にしてフィールドをクリアする

使用例の分析

ユーザ要件を確認し、サービス フォームのデフォルトの動作および外観を、ルールまたは ISF によって補完する必要がある場合を判断します。 これらのユーザ要件は不定形の文書にまとめることもできますが、動作とそのトリガー イベントを明示的に定義した表を使用することを推奨します。 次に例を示します。

表 1 例

サービス名

使用例の説明

時点

すべて

個人が高プロファイルの場合は、ステータスを読み取り専用で表示し、そうでない場合はステータスを非表示にする

オーダー

ここで、

サービスは、要件による影響を受ける「すべて」のサービス、またはサービスのリストのいずれかです。

使用の説明は、ビジネス ユーザがアクセス可能な言語で、サービス フォームが目的とする動作を指定します。

時点は、要求の実施中に発生する「すべて」の時点、または 1 つ以上の時点のいずれかです。

現在のプロジェクトの使用例をすべて収集すると、関連する作業のスコープを予測できます。

詳細な設計

詳細設計フェーズでは、プログラマは、以前定義した使用例を確認し、記述する必要のあるルールや JavaScript コンポーネント、およびこれらのルール/関数のトリガー イベントをおおまかに指定する必要があります。 設計には、関連するフォーム、ディクショナリ、およびフィールドの詳細な仕様が組み込まれます。 特に、次の点に注意して分析を実行する必要があります。

  • ISF を使用して、フォーム ルールを補完する必要があるか。 補完する必要がある場合には、自身の開発環境が ISF(JavaScript)の開発、テスト、およびデバッグをサポートするように設定され、必要なスキル セットを備えた担当者が作業できることを確認してください。
  • データ取得ルールまたは SQL オプション リストを使用する必要があるか。 使用する必要がある場合は、開発環境に SQL クエリーをテストおよびデバッグする手段が含まれること、目的のデータにアクセスできるようにデータソースが定義されていること、ソース データの構造に関する知識および必要なスキル セットを備えた担当者が作業できることを確認してください。

シナリオ 1:フォームの外観と動作の動的な調整

機能要件

データベースの作成を要求する場合には、データベース サーバのタイプを、Oracle、SQLServer、または「その他」のデータベースから選択する必要があります。 他の非エンタープライズの標準データベースを選択する場合は、ユーザから追加情報を収集する必要があります。そうしないと、追加フィールドが非表示になります。

シナリオ

表 2 フォームの外観と動作の動的な調整

データベース ディクショナリ内の [DatabaseType] フィールドの現在値が「その他(Other)」に等しいか。

その場合には、同じディクショナリ内の [説明(Description)] フィールドを表示し、ユーザに対して追加情報を入力するように要求します。

ディクショナリ/フォームの設計

たとえば、NewDatabase という名前のディクショナリを定義します。

  • ディクショナリには、オプション ボタンとしてレンダリングされた DatabaseType という名前のフィールドが含まれており、これを使用して、ユーザは Oracle、SQLServer、またはその他のデータベースを指定できます。
  • [その他(Other)] を選択すると、必ずデータベースのタイプを指定する必要があります。 それ以外の場合は、このフィールドは非表示になります。

詳細ルールの設計

この使用例は、条件付きルールを使用して完全に実装できます。

[AIT_DATABASE.DatabaseServer] フィールドには、フィールドの値が変更された際に起動するルールが必要です。

  • ユーザが選択した値が「Other」の場合、ルールによって、「Other」に依存したディクショナリ フィールドを表示し、そのようなすべてのフィールドを必須にする必要があります。
  • ユーザが選択した値が「Other」ではない場合、ルールによって、「Other」に依存するフィールドが表示されないようにする必要があります。

残念なことに、このリリースの Service Catalog では、if/then/else ロジックがルールに含まれていないため、この設計を実装するには 2 つのルールが必要です。

条件付きルールの実装

2 つの条件付きルールを、次のように定義します。

図 1. 条件付きルールの実装



トリガー イベント

このルールは、いつ適用すべきでしょうか。 より技術的な用語では、ユーザがサービス フォームにデータを入力する過程で「トリガー イベント」とは何であり、このルールを「起動」するタイミングはいつでしょうか。

ユーザが [DatabaseServer] オプション ボタンから値を選択した場合、明らかにルールを実行する必要があります。 したがって、このフォームの [アクティブフォームの動作(Active Form Behavior)] タブを使用して、これら 2 つのルールを「フィールドの変更時」イベントに関連付けます。 この場合は、ルールを適用する順序は関係ありません。いずれかのルールが起動しますが、両方ではありません。

サービス定義の構築

このシナリオをテストするには、定義したばかりのアクティブ フォームが含まれているサービスの定義を完了する必要があります。 簡単なテストを実行するため、そのフォームだけを含むサービスを最初に作成できますが、これは最初のステップにすぎないことは明らかです。 フォームとそのルールは、他のフォームおよびそのルールと相互作用できるため、最も現実的なテストが最良のテストになります。

テスト

複数のブラウザ ウィンドウを使用すると、このシナリオを最も容易にテストできます。 1 つのブラウザで Service Designer を開いた状態で、[アクティブフォームコンポーネント(Active Form Components)] オプションを表示します。 次に、新しいセッションを開始し、サービスをオーダーする権限を持つ My Services ユーザとしてログインします。 何が起きるか観察します。

その後のテストと結果

以前定義したルールは、正しく機能するはずです。 ただし、カスタマーがオペレーティング システムを変更(または最初に選択)した場合にのみ動作がトリガーされるため、実装は不完全です。 ディクショナリが編集可能な場合に、それ以降のシステム時点でフォームを保存して確認、または送信して表示する場合は、[DatabaseServer] フィールドの保存された値を使用してフォームの外観を調整する必要があります。 したがって、フォームがロードされた際に起動させるには、追加のトリガー イベントが必要です。

シナリオ 2:カスタマー情報と発信者情報の操作

機能要件

使用方法に関しては、Customer ディクショナリに影響を与えるシナリオと、Customer-Initiator フォームに影響を与えるシナリオの、2 つの異なるシナリオを考慮する必要があります。

シナリオ

表 3 カスタマー情報と発信者情報の操作

サービスを電子的に提供する場合 …

カスタマー情報の収集に必要な、標準的なフィールドのセットを表示します。

サービスを特定の物理的な場所に提供する必要がある場合 …

標準的なフィールドに加えて、カスタマー プロファイルに格納されたカスタマー ロケーションに関するフィールドを表示しますが、たとえば、ファイル上のロケーションが古い場合や、カスタマーが一時的に別の場所にいる場合のために、代替サービス ロケーションを指定できます。

ディクショナリの設計

設計は、次の 3 つのディクショナリを使用して実装できます。

  • 両方のタイプのサービスで使用可能にする必要のあるすべてのフィールドを含むように、Customer ディクショナリを設計します。これらのフィールドには、常に必須のフィールド、および要求者がサービスの提供先のロケーションを指定または確認する必要がある場合のみ必須のフィールドがあります。
  • 本人に直接提供されるサービスに対してのみ表示される Perform Work ディクショナリを設計します。 このフォームには、作業がカスタマー ロケーションで実行されたか、別の(サービス)ロケーションで実行されたかを問い合わせるフィールドが 1 つあります。
  • デフォルトでは、カスタマーのプロファイルからロケーション情報が入力される Service Location ディクショナリを設計します。ただし、要求者が、この情報とプロファイル内の情報が異なることを指摘した場合には、上書きすることができます。

フォームの設計

Customer ディクショナリと Initiator ディクショナリは、通常、すべての時点で読み取り専用です。 つまり、データは個人のプロファイルから表示され、カスタマーとタスク実行者のいずれによっても変更できません。

ディクショナリ内のすべてのフィールドを読み取り専用にするには、少なくとも次の 3 つの方法があります。

  • フォームの [アクセス制御(Access Control)] タブを使用して、該当する時点と参加者に対してディクショナリを表示可能(編集可能ではない)に指定します。 この制御は、ルールと ISF のいずれでも上書きできません。表示可能ディクショナリは非表示にできますが、編集可能にはできません。 また、フィールド値は、入力フィールド内ではなく、定型文として表示されます (Q:Lightweight 名前空間は機能しますか。)
  • ディクショナリを [アクセス制御(Access Control)] タブで編集可能にしますが、デフォルト フィールドを読み取り専用の HTML 入力タイプを持つように、またロケーション関連フィールドを非表示の入力タイプを持つように定義します。 非表示フィールドと読み取り専用フィールドの両方が、関連付けられた Lightweight 名前空間から値を提供されます。
  • [アクセス制御(Access Control)] タブでディクショナリを編集可能にし、適切な入力タイプを標準フィールドに割り当てますが、フォームがロードされるときに適用されるルールを作成し、ディクショナリ内のすべてのフィールドを読み取り専用にします。 このルールは、非表示フィールドには影響しません。

この場合には、2 番目のオプションが最も大きな意味を持ちます。 たとえば、ユーザが自分の個人ファイルの古いデータを更新できる場合、フィールドを潜在的に書き込み可能にすることができます。 また、追跡をやめるための追加のルールもありません。 これは、Customer-Initiator フォームに対処します。

また、PerformWork ディクショナリと ServiceLocation ディクショナリが使用されているフォームも必要です。 このフォームを ServiceLocation と呼びます。 (Q:名前付け規則?) PerformWork ディクショナリ内のフィールドは、チェック ボックスとして実装できますか。 (作業は、カスタマー サイトで実行されますか。)ServiceLocation ディクショナリには、Customer ディクショナリに含まれる個人フィールドに対応するフィールドが存在する可能性があります。

詳細ルールの設計

ここで、サービス ロケーションが必要なサービスに対するルールが必要になります。 ルールは、次のことを行う場合に必要になります。

  • デフォルトのロケーション値を Customer ディクショナリから ServiceLocation ディクショナリにコピーする場合。 これは、onLoad イベントで実行できます。
  • ユーザが異なるサービス ロケーションを必要とする場合、ServiceLocation フィールドを書き込み可能にします。 これは、[PerformWork] フィールドの onChange イベントで実行する必要があります。 (Q:名称、再度。)

ルールは、ServiceLocation フォームに取り込まれます。

条件付きルールの実装

COBOL を覚えている人はいますか。 多くの場合、COBOL でのコーディングは耐えがたいほど冗長でしたが、本当に簡潔なコマンドが 1 つ存在しました。それは、COPY CORR(esponding)です。 COPY CORR コマンドは、名前によって、1 つの構造体からすべての値を、別の構造体の対応する名前の値にコピーしました。 COPY CORR Dictionary1 TO Dictionary2 を実行できればよいのですが、これは不可能です。 したがって、最初のルール(ServiceLocation_onLoad)をオーダー時点で適用し、すべてのロケーション フィールドに対して値のコピーを実行します。

2 番目のルール(PerformWork_onChange)は直接的であり、最初のシナリオで記述したルールを想起させます。

サービス定義とテストの構築

このシナリオをテストするには、2 つのサービスが必要です。1 つには ServiceLocation フォームが含まれず、もう 1 つには含まれます。

シナリオ 3:機密性の高いデータの保護

機能要件

サービスは、「知る必要がある」人物のみ使用可能であり、サービスの実行に関係するすべての承認者またはタスク実行者は使用できない、社会保障番号やクレジット カード情報のような機密性の高いデータを指定するようユーザに要求します。 これらのデータは、ハッカー行為からも保護する必要があります。

アプローチ 1:ディクショナリとフィールドを非表示にします

デフォルトでは、ディクショナリの表示プロパティが Service Designer で「なし(none)」に設定されている場合、ディクショナリおよび以前入力されたフィールドの値は、生成されユーザに表示されるサービス フォームには含まれません。 (この設定は、2007 よりも前のバージョンの Service Catalog の動作に対する下位互換性のため、変更される可能性があります。 dictionary.permission.none.show newScale プロパティの設定については、Service Catalog 管理者に確認してください)。そのため、フィールドのデータは、ブラウザからページ ソースを表示するために十分な知識も持っているユーザに対しても表示されなくなります。

最初にユーザがフィールド値を入力できるようにするには、(当然ながら)フィールドをフォームで表示する必要があります。また、ディクショナリの表示設定は読み取り/書き込みに設定します。 フィールドの HTML 表現は、「パスワード(password)」に設定できます。 フィールド値は、一連のアスタリスクとして表示されます。

図 2. ディクショナリとフィールドの非表示

これにより、背後から覗き見する人から値を保護できます。 しかし、ページのソースを表示すると、フィールドの値が表示されます。 フィールドの値は ISF の getValue() 関数ではアクセスできず、「未定義(undefined)」が返されます。 フィールドの値は、DOM、つまり次のような JavaScript メソッドを通して使用できます。

document.getElementById('Dictionary.PasswordField').value.  

フィールドの値は、Service Link からもアクセス可能です。

サービス フォームで既存の要求を確認する場合やパスワード フィールド値があるタスクを実行する場合のように、パスワード フィールド値はブラウザに送り返されたときにユーザから見えないようになっています。

アプローチ 2:暗号化を使用します

暗号化と上記の方法のいくつかを組み合わせて使用することにより、機密性の高いデータの保護を強化できます。 パスワード フィールドを使用する代わりに(または、それに加えて)、機密性の高いデータを保存する前に JavaScript 関数を使用して暗号化し、フォームで表示する前に復号化します。 「Tiny Encryption Algorithm」と呼ばれる Web 上のオープンソース アルゴリズムを使用できます。

// Algorithm: David Wheeler & Roger Needham, Cambridge University Computer Lab
//    http://www.cl.cam.ac.uk/ftp/papers/djw-rmn/djw-rmn-tea.html (1994)
//    http://www.cl.cam.ac.uk/ftp/users/djw3/xtea.ps (1997)
//
// JavaScript implementation: Chris Veness, Movable Type Ltd

スクリプトでコードを暗号化/複合化する関数を定義した場合、ユーザが [ソースの表示(View Source)] を実行するとフォームに関数が表示されます。 しかし、ライブラリに関数をインクルードした場合、ソースを表示しようとするユーザに対して関数が表示されず、リバース エンジニアリングの対象になりません。

アプローチ 3:セキュアな文字列を使用します

アプローチ 4:サーバ側のルールを使用します

悪意のある試行によってフォーム データがサーバに送信される前に操作されるという権限の違反を防ぐために、権限付与または RBAC 権限によって管理されるデータ要素にはサーバ側で再検証された値が必要です。 ルールが送信後に実行される場合を除き、データ取得ルールの暗黙的検証を常に有効にします。 詳細については、「サービス フォーム パフォーマンスおよびセキュリティ上の考慮事項」の項を参照してください。

シナリオ 4:フォーム内の値の計算

機能要件

サービスは、オーダーするアイテムの「数量」と「価格」を指定するようユーザに要求します。 サービスは、「合計価格」を計算し、サービス フォームにこの値を表示する必要があります。

ディクショナリ/フォームの設計要件

3 つの数字フィールドを含む、SVC_PRICE と呼ばれるディクショナリを作成する必要があります。 要件の詳細に応じて、フィールドは 10 進数精度の場合とそれ以外の場合があります。 ディクショナリはサービス フォームに含まれており、オーダー時点でカスタマーによる書き込みが可能です。 すべてのフィールドは、テキスト フィールドとしてレンダリングされます。

ISF の詳細設計

[合計(Total)] フィールドは、ユーザによる値の入力が許可されていないため、読み取り専用の必要があります。計算された値が入力されます。 ユーザが価格または数量のいずれかを変更すると、計算を実行する必要があります。

このタスクには、次の 3 つのカスタム イベントが必要です。

  • SVC_PRICE_onLoad イベントは、[ExtendedPrice] フィールドを読み取り専用に設定します。
  • SVC_PRICE_Quantity_onChange イベントは、[ExtendedPrice] を計算します。
  • SVC_PRICE_Price_onChange イベントも、[ExtendedPrice] を計算します。

JavaScript コードとイベント

最初のタスクは、初めてフォームをロードした際に実行するよう推奨します。 そのためには、Script Manager で SVC_PRICE_onLoad と呼ばれる関数を作成します。

SVC_PRICE_onLoad ()
{
  serviceForm.SVC_PRICE.ExtendedPrice.setReadOnly(true);
}

このコードは、[動作(Behavior)] タブの「フォームのロード時(ブラウザ側)」イベントに関連付けられています。

2 番目のタスクでは、数量と価格に基づいて ExtendedPrice を計算します。 数量と価格の両方に対して、「項目の変更時」イベントを使用します。 コードは、両方のフィールドに有効な値があることを確認して ExtendedPrice を計算する必要があります。 この関数は、SVCPRICE_Price_onChange と呼ばれます。

SVC_PRICE_Price_onChange ()
{
  serviceForm.SVC_PRICE.Total.setReadOnly(true);
  var Price = serviceForm.SVC_PRICE.Price.getValue()[0];
  var Quantity = serviceForm.SVC_PRICE.Quantity.getValue()[0];
/* Blank out current value (if any) of ExtendedPrice */
  serviceForm.SVC_PRICE.Total.setValue(['']);
/* Check is required, since check for Numeric data happens only on Submit. */
  if (isNaN (Price))
  {
    alert ('Price is not a number');
    serviceForm.SVC_PRICE.Price.setFocus(true);
    return;
  }
  if (isNaN (Quantity))
  {
    alert ('Quantity is not a number');
    serviceForm.SVC_PRICE.Quantity.setFocus(true);
    return;
  }
  var Total = Price * Quantity;
  serviceForm.SVC_PRICE.Total.setValue([Total]);
}

リファクタリングされた JavaScript コード

Service Designer でスクリプトとして定義された上記のコードは、[価格(Price)] フィールドと [数量(Quantity)] フィールドの 2 つの異なるフィールドの onChange イベントにアタッチできます。 実際に、これは、コードを最初にテストする場合に効率的な方法です。 ただし、このアプローチは、この使用方法を反映しない関数名を持ち、ライブラリを使用しない場合には、長期的に維持することは困難です。 したがって、次のリファクタリングを実行することを推奨します。

  • 関数コードを編集して、関数名を「ComputeExtendedPrice」などの汎用な名前に変更し、スクリプトからコードを抽出してアプリケーションのカスタム ライブラリに配置します。
  • カスタム ライブラリが [スクリプト(Scripts)] > [ライブラリ(Libraries)] で定義され、この関数が要求された際にサービス フォームに取り込まれることを確認してください。
  • 次のような 2 つのスクリプトを作成します。
SVC_PRICE_Price_onChange ()
{
  ComputeExtendedPrice();
}
SVC_PRICE_Quantity_onChange ()
{
  ComputeExtendedPrice();
}
  • これらの関数を、それぞれ [価格(Price)] フィールドと [数量(Quantity)] フィールドの onChange イベントにアタッチします。
  • 改訂したライブラリをアプリケーション サーバにアップロードすることを忘れないでください。

シナリオ 5:フォーム内の 2 つのフィールドの書式設定

要件

JavaScript 関数を作成し、フォームにある社会保障番号と電話番号の 2 つのフィールドの書式を設定します。 SSN は、「999-99-9999」のように書式化された 9 桁の番号があることを確認する必要があります。ユーザによって作成された書式は無視されます。 電話番号は、「(999) 999-9999」のように書式化された 10 桁の番号である必要があります。

JavaScript

1 つは formatSSN、もう 1 つは formatPhoneNo と呼ばれる 2 つの関数を作成します。 両方の関数を「isfprimerlib.js」という名前のファイルに入れます。 ISF コーディングとベスト プラクティスで説明したように、このファイルは、アプリケーション サーバの RequestCenter.war ディレクトリ下の任意のディレクトリに存在する可能性があります。慣例では、ISF ライブラリは「isfcode」という名前のディレクトリに配置されます。

Script Manager 内の JavaScript ファイルに、ライブラリ参照を作成します。 [ライブラリ(Libraries)] タブで、サービスにアタッチされる JavaScript 関数のライブラリを必ず指定してください。

結果のコードは次のとおりです。

function getOnlyDigits (inValue)
{
  var outValue = '';
  var aChar;
  for (i=0; i < inValue.length; i++)
  {
    aChar = inValue.charAt (i);
    if ('0' <= aChar && aChar <= '9')
    {
      outValue = outValue + aChar;
    }
  }
  return outValue;
}
function testValueLength (inValue, inLen, obField, fieldName)
{
  if ((inValue.length > inLen) || (inValue.length < inLen))
  {
    alert (fieldName + ' must have ' + inLen + ' digits and it has ' + inValue.length);
    eval('serviceForm.'+obField).setFocus(true);
    return false;
  }
  return true;
}
function formatSSN (obField)
{
  var SSNString = getOnlyDigits (eval('serviceForm.'+obField).getValue()[0]);
  if (testValueLength (SSNString, 9, obField, 'SSN'))
  {
     eval('serviceForm.'+obField).setValue([SSNString.slice (0,3) + 
       '-' + SSNString.slice (3,5) + '-' + SSNString.slice (5)]);
  }
}
function formatPhoneNo (obField)
{
  var phoneString = getOnlyDigits (eval('serviceForm.'+obField).getValue()[0]);
  if (testValueLength (phoneString, 10, obField, 'Phone Number'))
  {
    eval('serviceForm.'+obField).setValue(['(' + phoneString.slice (0,3) + ') ' 
      + phoneString.slice (3,6) + '-' + phoneString.slice (6)]);
  }
For the field SSN create a function called Customer_SSN_onChange
 that calls formatSSN
:
Customer_SSN_onChange ()
{
  formatSSN('Customer.SSN');
}

[PhoneNo] フィールドに対して、フィールドの値が変更された場合に formatPhoneNo を呼び出す、Customer_PhoneNo_onChange と呼ばれる別の関数を作成します。 単に別の実装方法を示すため、これらの関数はフィールドの名前を渡して、「値によって」ではなく「参照によって」機能します。

Customer_PhoneNo_onChange ()
{
  formatPhoneNo ('Customer.PhoneNo');
}

それぞれのフィールドの値が変更される場合は、両方の関数が呼び出されます。

サーバ側の関連付けられたコントロール

この項では、外部サーバとの間で、HTTP 要求を介してサービス フォーム データを相互に転送する方法を説明します。 この機能の目的は、アプリケーション サーバとは別の Web サーバに存在する Web ウィジェットを、Service Designer で使用できるようにすることです。 この項では、サービス フォーム データの実際の転送および処理についてのみ説明します。

サービス フォーム データは、HTTP フォーム ポストを介して外部の Web ウィジェットに送信されます。 データは、WDDX パケットとして WDDXData 変数で wddxdataform フォームに渡されます。 WDDX は、シリアル化されたパケットにデータ構造を保存するための XML スキーマです。 これにより、使用するプログラミング言語に依存せずに、データを Service Catalog に渡すか、Service Catalog から渡すことができます。 Service Catalog ページに対する HTTP 要求を使用して、サービス フォームに結果のデータ セットを戻すことができます。