ヒント
このレッスンは、New Relic アプリケーションをゼロから構築する方法を学習するコースの一部です。 まだご覧になっていない方は、概要をご覧ください。
コースの各レッスンは前回のレッスンに基づいて構築されるため、このレッスンを開始する前に、前回のレッスン「NRQL データのカスタマイズ」を完了していることを確認してください。
前のレッスンでは、グラフと A/B テストを終了するボタンを含むユーザーエクスペリエンスをアプリケーションに作成しました。 また、New Relic のデータベースから実際のデータをクエリするようにチャートを構成しました。 ただし、2 つのグラフに模擬データが残されています。
- 過去のテスト
 - バージョンごとのキャンセル総数
 
これら両方のチャートには、New Relic からは取得できないデータが含まれています。 Past tests [過去のテスト]には過去のテスト情報が表示され、 Total cancellations per version [バージョンごとの合計キャンセル数]には、時間の経過とともに各バージョンで発生したキャンセル数が表示されます。 まずPast tests [過去のテスト]に焦点を当てます。
Past tests [過去のテストには]過去のテスト情報が表示されるため、完了時に各テストに関する情報を保存する必要があります。 そして、このタスクを達成するために必要なツールがすべて揃っています。 まず、保存アクションをトリガーするEnd test [テスト終了]ボタンがあります。 次に、NerdGraph と NerdStorage を使用してテスト情報を保持する方法を理解します。
テスト情報をNerdStorageに保存する
コースワークリポジトリのadd-nerdstorage/ab-testディレクトリに変更します:
$cd nru-programmability-course/add-nerdstorage/ab-testこのディレクトリには、コースのこの時点でアプリケーションに含まれることが予想されるコードが含まれています。 各レッスンの開始時に正しいディレクトリに移動することで、カスタム コードが残され、間違ったコードがレッスン間で持ち越されることがなくなります。
nerdlets/ab-test-nerdlet/end-test.jsでメソッドEndTestButton.endTest()を作成します。
import React from 'react';import {  AccountStorageMutation,  BlockText,  Button,  Grid,  GridItem,  HeadingText,  Modal,  Select,  SelectItem,} from 'nr1';
class VersionSelector extends React.Component {  constructor(props) {    super(props);  }
  render() {    return (      <Select        onChange={this.props.selectVersion}        value={this.props.selectedVersion}      >        <SelectItem value={'A'}>Version A</SelectItem>        <SelectItem value={'B'}>Version B</SelectItem>      </Select>    );  }}
class EndTestButton extends React.Component {  constructor() {    super(...arguments);
    this.state = {      modalHidden: true,    };
    this.showModal = this.showModal.bind(this);    this.closeModal = this.closeModal.bind(this);    this.endTest = this.endTest.bind(this);  }
  closeModal() {    this.setState({ modalHidden: true });  }
  showModal() {    this.setState({ modalHidden: false });  }
  endTest() {    const today = new Date();    const dd = String(today.getDate()).padStart(2, '0');    const mm = String(today.getMonth() + 1).padStart(2, '0');    const yyyy = today.getFullYear();    const endDate = `${mm}-${dd}-${yyyy}`;
    AccountStorageMutation.mutate({      accountIds: this.props.accountIds,      actionType: AccountStorageMutation.ACTION_TYPE.WRITE_DOCUMENT,      collection: 'past-tests',      documentId: endDate,      document: {        versionADescription: this.props.versionADescription,        versionBDescription: this.props.versionBDescription,        winner: this.props.selectedVersion,      },    });
    this.closeModal();  }
  render() {    return (      <div>        <Button type={Button.TYPE.DESTRUCTIVE} onClick={this.showModal}>          End test        </Button>
        <Modal hidden={this.state.modalHidden} onClose={this.closeModal}>          <HeadingText>Are you sure?</HeadingText>          <BlockText>            If you end the test, all your users will receive the version you            selected:          </BlockText>
          <BlockText spacingType={[BlockText.SPACING_TYPE.LARGE]}>            <b>Version {this.props.selectedVersion}</b>          </BlockText>
          <Button onClick={this.closeModal}>No, continue test</Button>          <Button type={Button.TYPE.DESTRUCTIVE} onClick={this.closeModal}>            Yes, end test          </Button>        </Modal>      </div>    );  }}
export default class EndTestSection extends React.Component {  constructor() {    super(...arguments);
    this.state = {      selectedVersion: 'A',    };
    this.selectVersion = this.selectVersion.bind(this);  }
  selectVersion(event, value) {    this.setState({ selectedVersion: value });  }
  render() {    return (      <Grid className="endTestSection">        <GridItem columnSpan={12}>          <HeadingText className="endTestHeader">            Pick the winner of your A/B test:          </HeadingText>        </GridItem>        <GridItem columnStart={5} columnEnd={6} className="versionSelector">          <VersionSelector            selectedVersion={this.state.selectedVersion}            selectVersion={this.selectVersion}          />        </GridItem>        <GridItem columnStart={7} columnEnd={8}>          <EndTestButton selectedVersion={this.state.selectedVersion}>            End test          </EndTestButton>        </GridItem>      </Grid>    );  }}このコードを使用して、フォーマットされた日付文字列endDateを構築します。 次に、 AccountStorageMutation.mutate()を呼び出してアカウント ストレージを変更します。 アカウント識別子、 WRITE_DOCUMENTアクション タイプ、コレクション名として「過去のテスト」、 documentId endDateおよびバージョンの説明と勝者を含むドキュメント データを渡します。
新しいドキュメントを作成しているか、一致するコレクションとdocumentIdを持つドキュメントが既に存在する場合はドキュメントを更新しているため、 WRITE_DOCUMENTを渡します。 documentIdはendDateであり、1 日に 1 つのレコードのみを作成する場合に役立ちます。
ヒント
1 日に複数のテストが完了する可能性があると思われる場合は、このロジックを変更する必要があります。
メソッドの末尾のthis.props.closeModal()に注意してください。 これにより、テストを終了しようとしたときに表示されるモーダルが閉じます。 したがって、 EndTestButton.render()のYes, end test [はい、テストを終了]ボタンのonClickコールバックを次のように置き換えることができます。
import React from 'react';import {  AccountStorageMutation,  BlockText,  Button,  Grid,  GridItem,  HeadingText,  Modal,  Select,  SelectItem,} from 'nr1';
class VersionSelector extends React.Component {  constructor(props) {    super(props);  }
  render() {    return (      <Select        onChange={this.props.selectVersion}        value={this.props.selectedVersion}      >        <SelectItem value={'A'}>Version A</SelectItem>        <SelectItem value={'B'}>Version B</SelectItem>      </Select>    );  }}
class EndTestButton extends React.Component {  constructor() {    super(...arguments);
    this.state = {      modalHidden: true,    };
    this.showModal = this.showModal.bind(this);    this.closeModal = this.closeModal.bind(this);    this.endTest = this.endTest.bind(this);  }
  closeModal() {    this.setState({ modalHidden: true });  }
  showModal() {    this.setState({ modalHidden: false });  }
  endTest() {    const today = new Date();    const dd = String(today.getDate()).padStart(2, '0');    const mm = String(today.getMonth() + 1).padStart(2, '0');    const yyyy = today.getFullYear();    const endDate = `${mm}-${dd}-${yyyy}`;
    AccountStorageMutation.mutate({      accountIds: this.props.accountIds,      actionType: AccountStorageMutation.ACTION_TYPE.WRITE_DOCUMENT,      collection: 'past-tests',      documentId: endDate,      document: {        versionADescription: this.props.versionADescription,        versionBDescription: this.props.versionBDescription,        winner: this.props.selectedVersion,      },    });
    this.closeModal();  }
  render() {    return (      <div>        <Button type={Button.TYPE.DESTRUCTIVE} onClick={this.showModal}>          End test        </Button>
        <Modal hidden={this.state.modalHidden} onClose={this.closeModal}>          <HeadingText>Are you sure?</HeadingText>          <BlockText>            If you end the test, all your users will receive the version you            selected:          </BlockText>
          <BlockText spacingType={[BlockText.SPACING_TYPE.LARGE]}>            <b>Version {this.props.selectedVersion}</b>          </BlockText>
          <Button onClick={this.closeModal}>No, continue test</Button>          <Button type={Button.TYPE.DESTRUCTIVE} onClick={this.endTest}>            Yes, end test          </Button>        </Modal>      </div>    );  }}
export default class EndTestSection extends React.Component {  constructor() {    super(...arguments);
    this.state = {      selectedVersion: 'A',    };
    this.selectVersion = this.selectVersion.bind(this);  }
  selectVersion(event, value) {    this.setState({ selectedVersion: value });  }
  render() {    return (      <Grid className="endTestSection">        <GridItem columnSpan={12}>          <HeadingText className="endTestHeader">            Pick the winner of your A/B test:          </HeadingText>        </GridItem>        <GridItem columnStart={5} columnEnd={6} className="versionSelector">          <VersionSelector            selectedVersion={this.state.selectedVersion}            selectVersion={this.selectVersion}          />        </GridItem>        <GridItem columnStart={7} columnEnd={8}>          <EndTestButton selectedVersion={this.state.selectedVersion}>            End test          </EndTestButton>        </GridItem>      </Grid>    );  }}テストを終了すると、 endTestテスト データをアカウント ストレージに保存し、モーダルを閉じます。 これはテーブルにデータを入力するのに必要な動作の半分に過ぎず、アカウント ストレージからこのコレクションをクエリする必要もあります。
Nerdlet のindex.jsファイルで、アカウント ID とバージョンの説明をEndTestSectionに渡します。
import React from 'react';import { ChartGroup, Grid, GridItem } from 'nr1';import EndTestSection from './end-test';import NewsletterSignups from './newsletter-signups';import PastTests from './past-tests';import TotalCancellations from './total-cancellations';import TotalSubscriptions from './total-subscriptions';import VersionDescription from './description';import VersionPageViews from './page-views';import VersionTotals from './totals';
const ACCOUNT_ID = YOUR_NEW_RELIC_ACCOUNT_ID;const VERSION_A_DESCRIPTION =  'The newsletter signup message says, "Sign up for our newsletter"';const VERSION_B_DESCRIPTION =  'The newsletter signup message says, "Sign up for our newsletter and get a free shirt!"';
export default class AbTestNerdletNerdlet extends React.Component {  render() {    return (      <div>        <Grid className="wrapper">          <GridItem columnSpan={6}>            <VersionDescription              description={VERSION_A_DESCRIPTION}              version="A"            />          </GridItem>          <GridItem columnSpan={6}>            <VersionDescription              description={VERSION_B_DESCRIPTION}              version="B"            />          </GridItem>          <GridItem columnSpan={12}>            <hr />          </GridItem>          <GridItem columnSpan={12}>            <NewsletterSignups />          </GridItem>          <GridItem columnSpan={6}>            <TotalSubscriptions />          </GridItem>          <GridItem columnSpan={6}>            <TotalCancellations />          </GridItem>          <GridItem columnSpan={6}>            <VersionTotals version="a" accountId={ACCOUNT_ID} />          </GridItem>          <GridItem columnSpan={6}>            <VersionTotals version="b" accountId={ACCOUNT_ID} />          </GridItem>          <ChartGroup>            <GridItem columnSpan={6}>              <VersionPageViews version="a" />            </GridItem>            <GridItem columnSpan={6}>              <VersionPageViews version="b" />            </GridItem>          </ChartGroup>          <GridItem columnSpan={12}>            <EndTestSection              accountId={ACCOUNT_ID}              versionADescription={VERSION_A_DESCRIPTION}              versionBDescription={VERSION_B_DESCRIPTION}            />          </GridItem>          <GridItem columnSpan={12}>            <PastTests />          </GridItem>        </Grid>      </div>    );  }}重要
YOUR_NEW_RELIC_ACCOUNT_ID実際の New Relicアカウント IDに置き換えてください。
これで、 EndTestSectionプロパティ内のこれらの変数にアクセスできるようになりました。
アカウント ID とバージョンの説明をEndTestSectionからEndTestButtonに転送します:
import React from "react";import {  AccountStorageMutation,  BlockText,  Button,  Grid,  GridItem,  HeadingText,  Modal,  Select,  SelectItem,} from "nr1";
class VersionSelector extends React.Component {  constructor(props) {    super(props);  }
  render() {    return (      <Select        onChange={this.props.selectVersion}        value={this.props.selectedVersion}      >        <SelectItem value={"A"}>Version A</SelectItem>        <SelectItem value={"B"}>Version B</SelectItem>      </Select>    );  }}
class EndTestButton extends React.Component {  constructor() {    super(...arguments);
    this.state = {      modalHidden: true,    };
    this.showModal = this.showModal.bind(this);    this.closeModal = this.closeModal.bind(this);    this.endTest = this.endTest.bind(this);  }
  closeModal() {    this.setState({ modalHidden: true });  }
  showModal() {    this.setState({ modalHidden: false });  }
  endTest() {    const today = new Date();    const dd = String(today.getDate()).padStart(2, "0");    const mm = String(today.getMonth() + 1).padStart(2, "0");    const yyyy = today.getFullYear();    const endDate = `${mm}-${dd}-${yyyy}`;
    AccountStorageMutation.mutate({      accountIds: this.props.accountIds,      actionType: AccountStorageMutation.ACTION_TYPE.WRITE_DOCUMENT,      collection: "past-tests",      documentId: endDate,      document: {        versionADescription: this.props.versionADescription,        versionBDescription: this.props.versionBDescription,        winner: this.props.selectedVersion,      },    });
    this.closeModal();  }
  render() {    return (      <div>        <Button type={Button.TYPE.DESTRUCTIVE} onClick={this.showModal}>          End test        </Button>
        <Modal hidden={this.state.modalHidden} onClose={this.closeModal}>          <HeadingText>Are you sure?</HeadingText>          <BlockText>            If you end the test, all your users will receive the version you            selected:          </BlockText>
          <BlockText spacingType={[BlockText.SPACING_TYPE.LARGE]}>            <b>Version {this.props.selectedVersion}</b>          </BlockText>
          <Button onClick={this.closeModal}>No, continue test</Button>          <Button type={Button.TYPE.DESTRUCTIVE} onClick={this.endTest}>            Yes, end test          </Button>        </Modal>      </div>    );  }}
export default class EndTestSection extends React.Component {  constructor() {    super(...arguments);
    this.state = {      selectedVersion: "A",    };
    this.selectVersion = this.selectVersion.bind(this);  }
  selectVersion(event, value) {    this.setState({ selectedVersion: value });  }
  render() {    return (      <Grid className="endTestSection">        <GridItem columnSpan={12}>          <HeadingText className="endTestHeader">            Pick the winner of your A/B test:          </HeadingText>        </GridItem>        <GridItem columnStart={5} columnEnd={6} className="versionSelector">          <VersionSelector            selectedVersion={this.state.selectedVersion}            selectVersion={this.selectVersion}          />        </GridItem>        <GridItem columnStart={7} columnEnd={8}>          <EndTestButton            accountIds={this.props.accountIds}            selectedVersion={this.state.selectedVersion}            versionADescription={this.props.versionADescription}            versionBDescription={this.props.versionBDescription}          >            End test          </EndTestButton>        </GridItem>      </Grid>    );  }}NerdStorageからテスト情報を取得する
past-tests.jsで、 PastTestsを更新してアカウント ストレージからデータを取得し、 TableChartに表示します。
import React from "react";import { AccountStorageQuery, HeadingText, Spinner, TableChart } from "nr1";
export default class PastTests extends React.Component {  render() {    const historicalData = {      metadata: {        id: "past-tests",        name: "Past tests",        columns: [          "endDate",          "versionADescription",          "versionBDescription",          "winner",        ],      },      data: [],    };
    return (      <div>        <HeadingText className="chartHeader">Past tests</HeadingText>        <AccountStorageQuery          accountIds={this.props.accountIds}          collection="past-tests"        >          {({ loading, error, data }) => {            if (loading) {              return <Spinner />;            }            if (error) {              console.debug(error);              return "There was an error fetching your data.";            }            data.forEach(function (currentValue, index) {              historicalData.data.push({                endDate: currentValue.id,                versionADescription: currentValue.document.versionADescription,                versionBDescription: currentValue.document.versionBDescription,                winner: currentValue.document.winner,              });            }, data);            return <TableChart data={[historicalData]} fullWidth />;          }}        </AccountStorageQuery>      </div>    );  }}まず、 historicalDataからモックデータを削除しました。 次に、 AccountStorageQueryを使用して NerdStorage からデータを取得しました。 アカウント識別子とドキュメントを保存したコレクションの名前を渡しました。
クエリから 3 つの戻り値があることに注意してください。
loadingerrordata
クエリが進行中の場合、 loadingはtrueになります。 この場合、 Spinnerユーザー インターフェース コンポーネントを表示して、テーブルがまだ準備ができていないことをユーザーに知らせます。 クエリがエラーを返した場合、そのエラーに関する情報はerror変数で確認できます。 デバッグの目的でその情報をコンソールに記録し、エラーメッセージを返します。 クエリが正常に返された場合は、 data変数のデータにアクセスできます。 データはTableChartに必要な形式と一致しないため、データをループし、 TableChart仕様に従ってフォーマットします。 最後に、 historicalDataをTableChartに渡します。
Nerdlet のindex.jsファイルで、 ACCOUNT_IDをPastTestsに渡します。
import React from "react";import { ChartGroup, Grid, GridItem } from "nr1";import EndTestSection from "./end-test";import NewsletterSignups from "./newsletter-signups";import PastTests from "./past-tests";import TotalCancellations from "./total-cancellations";import TotalSubscriptions from "./total-subscriptions";import VersionDescription from "./description";import VersionPageViews from "./page-views";import VersionTotals from "./totals";
const ACCOUNT_ID = YOUR_NEW_RELIC_ACCOUNT_ID;const VERSION_A_DESCRIPTION = 'The newsletter signup message says, "Sign up for our newsletter"';const VERSION_B_DESCRIPTION = 'The newsletter signup message says, "Sign up for our newsletter and get a free shirt!"';
export default class AbTestNerdletNerdlet extends React.Component {  render() {    return (      <div>        <Grid className="wrapper">          <GridItem columnSpan={6}>            <VersionDescription              description={VERSION_A_DESCRIPTION}              version="A"            />          </GridItem>          <GridItem columnSpan={6}>            <VersionDescription              description={VERSION_B_DESCRIPTION}              version="B"            />          </GridItem>          <GridItem columnSpan={12}>            <hr />          </GridItem>          <GridItem columnSpan={12}>            <NewsletterSignups />          </GridItem>          <GridItem columnSpan={6}>            <TotalSubscriptions />          </GridItem>          <GridItem columnSpan={6}>            <TotalCancellations />          </GridItem>          <GridItem columnSpan={6}>            <VersionTotals version="a" accountId={ACCOUNT_ID} />          </GridItem>          <GridItem columnSpan={6}>            <VersionTotals version="b" accountId={ACCOUNT_ID} />          </GridItem>          <ChartGroup>            <GridItem columnSpan={6}>              <VersionPageViews version="a" />            </GridItem>            <GridItem columnSpan={6}>              <VersionPageViews version="b" />            </GridItem>          </ChartGroup>          <GridItem columnSpan={12}>            <EndTestSection              accountId={ACCOUNT_ID}              versionADescription={VERSION_A_DESCRIPTION}              versionBDescription={VERSION_B_DESCRIPTION}            />          </GridItem>          <GridItem columnSpan={12}>            <PastTests accountId={ACCOUNT_ID} />          </GridItem>        </Grid>      </div>    );  }}重要
YOUR_NEW_RELIC_ACCOUNT_ID実際の New Relicアカウント IDに置き換えてください。
nru-programmability-course/add-nerdstorage/ab-testにある Nerdpack のルートに移動します。
Nerdpack の新しい UUID を生成します。
$nr1 nerdpack:uuid -gf既存の Nerdpack を含むコースワーク リポジトリを複製したため、独自の一意の識別子を生成する必要があります。 この UUID は Nerdpack を New Relic アカウントにマッピングします。 また、アプリがアカウントに代わって Nerdgraph リクエストを行うことも可能になります。
アプリケーションをローカルで提供します。
$nr1 nerdpack:servehttps://one.newrelic.com?nerdpacks=localにアクセスし、 Apps [アプリ] > Your apps [あなたのアプリ]でアプリケーションを表示します。
最初はデータは表示されないはずです。
End test [テストを終了]をクリックし、モーダルでアクションを承認します。 これで、 Past tests [過去のテスト]のデータが表示されます。
ヒント
何かがうまくいかない場合は、 browserのデバッグ ツールを使用して問題を特定してください。
以下の点を確認してください:
- レッスンからコードを正しくコピーしました
 - 新しいUUIDを生成しました
 - プロジェクト内の
YOUR_NEW_RELIC_ACCOUNT_IDのすべてのインスタンスを実際の New Relicアカウント IDに置き換えました 
完了したら、ローカル サーバーのターミナル ウィンドウでCTRL+Cを押して、New Relic アプリケーションの提供を停止します。
すごい仕事! これで、NerdStorage クエリおよびミューテーション コンポーネントの使用経験ができました。 ただし、モックデータを含むグラフがもう 1 つあります。Total cancellations per version [バージョンごとの合計キャンセル数]です。
Total cancellations per version [バージョンごとの合計キャンセル数は]、A/B テスト アプリケーションの他のグラフとは異なります。 New Relic はニュースレターの購読を解除したユーザーの数を把握していないため、その情報は外部ソースから要求する必要があります。 このコースの目的のために、アプリケーションで使用できる偽のニュースレターキャンセルデータを提供する外部データ サービスを作成しました。 この模擬サービスはhttps://api.nerdsletter.net/cancellationsにあります。
New Relic アプリケーションは React アプリケーションです。つまり、React と Javascript の機能を使用して、New Relic だけでなく、任意の外部サービスからデータを収集できます。 後のレッスンでは、New Relic 以外のデータを調べて、ソフトウェアがビジネス目標の達成にどのように役立っているかを知る方法を学習します。 しかし、それを学ぶ前に、モック データ サービスについて知っておく必要があります。それは、Authorization ヘッダーが必要なことです。
このレッスンでは、NerdStorage を使用して、アプリケーション独自のデータ ストア内のデータをクエリおよび変更する方法を学習しました。 NerdStorage は多くのカテゴリのデータを保存するのに最適ですが、外部サービスへの Authorization ヘッダーで渡すトークンなどの機密データには適していません。 そのためには、NerdStorageVault を使用します。
ヒント
このレッスンは、New Relic アプリケーションをゼロから構築する方法を学習するコースの一部です。 次のレッスンに進みましょう: Nerdlet から NerdStorageVault にアクセスします。