Conseil
Cette leçon fait partie d’un cours qui vous apprend à créer une application New Relic à partir de zéro. Si vous ne l’avez pas déjà fait, consultez la présentation.
Chaque leçon du cours s'appuie sur la précédente, alors assurez-vous d'avoir terminé la dernière leçon, Accéder à NerdStorage depuis votre nerdlet, avant de commencer celle-ci.
Tout au long de ce cours, vous créez une application New Relic qui collecte des données télémétriques à partir d'un service Web de démonstration qui exécute un test A/B sur un formulaire d'inscription à une newsletter. L'objectif de votre application New Relic est de vous aider à comprendre comment les changements de conception impactent le nombre d'abonnements à des newsletters de haute qualité que votre service reçoit. L'objectif commercial, augmenter le nombre d'abonnements à la newsletter de qualité de votre service, repose principalement sur trois informations clés :
- Nombre de pages vues par version
- Nombre d'abonnement par version
- Nombre d'annulations
Les annulations sont importantes car si une version de conception de votre formulaire d'inscription à la newsletter génère un nombre élevé d'abonnements mais également un nombre élevé d'annulations, alors ces abonnements n'ont pas autant de valeur.
Dans les leçons précédentes, vous avez collecté des données sur les pages vues et les abonnements à partir de la base de données de New Relic (NRDB), mais vous avez toujours besoin de données d'annulation. Votre application de démonstration ne signale pas les données d'annulation à New Relic. Vous devez donc récupérer ces données à partir d'une source externe. Nous avons fourni un service sur https://api.nerdsletter.net/cancellations pour renvoyer de fausses données d'annulation aux fins de ce cours. Si vous visitez cette URL dans votre navigateur, vous verrez un bref message : « Non autorisé ». C'est parce que nous avons créé ce service avec l'exigence que quiconque requests ses données doit passer un en-tête d'autorisation avec le jeton porteur ABC123
.
Donc, avant de pouvoir demander des données d'annulation à api.nerdsletter.net, vous devez implémenter quelques nouveaux comportements dans votre application :
- Fournir un mécanisme pour saisir un jeton d'autorisation
- Conserver le jeton d'autorisation dans un datastore sécurisé
Pour saisir votre jeton d'autorisation, vous utiliserez un Modal
avec un TextField
. Le datastore sécurisé que vous utiliserez s'appelle NerdStorageVault
. Il est différent de NerdStorage
, que vous avez utilisé dans la dernière leçon, car il prend uniquement en charge le stockage utilisateur et crypte ses données.
Stockez votre jeton API
Accédez au répertoire add-nerdstoragevault/ab-test du référentiel de cours:
$cd nru-programmability-course/add-nerdstoragevault/ab-test
Ce répertoire contient le code que nous attendons de votre application à ce stade du cours. En naviguant vers le bon répertoire au début de chaque leçon, vous laissez votre code personnalisé derrière vous, vous protégeant ainsi du transport de code incorrect d'une leçon à l'autre.
Dans le fichier index.js
de votre Nerdlet, initialisez state
dans AbTestNerdletNerdlet
avec un jeton par défaut null
:
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 = 123456 // <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 { constructor() { super(...arguments);
this.state = { token: null, } }
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> }}
Votre Nerdlet utilise cet état token
pour gérer le jeton d'autorisation que vous transmettrez ultérieurement au service tiers. Cependant, state
d’un composant ne constitue pas une solution à long terme pour la gestion des données. Pour cela, vous avez besoin NerdStorageVault
.
Implémentez une méthode, appelée storeToken()
, qui mute les données NerdStorageVault et liez cette méthode à l'instance AbTestNerdletNerdlet
:
import React from 'react';import { ChartGroup, Grid, GridItem, NerdGraphMutation,} 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 = 123456 // <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 { constructor() { super(...arguments);
this.state = { token: null, }
this.storeToken = this.storeToken.bind(this); }
storeToken(newToken) { if (newToken != this.state.token) { const mutation = ` mutation($key: String!, $token: SecureValue!) { nerdStorageVaultWriteSecret( scope: { actor: CURRENT_USER } secret: { key: $key, value: $token } ) { status errors { message type } } } `; const variables = { key: "api_token", token: newToken, }; NerdGraphMutation.mutate({ mutation: mutation, variables: variables }).then( (data) => { if (data.data.nerdStorageVaultWriteSecret.status === "SUCCESS") { this.setState({token: newToken}) } } ); } }
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> }}
Lorsque vous appelez storeToken()
avec une nouvelle valeur de jeton, votre Nerdlet utilise les API NerdGraph
pour muter les données NerdStorageVault
pour la clé api_token
. Si la demande à NerdGraph
réussit, storeToken()
met à jour state.token
afin que le nouveau jeton soit accessible localement.
Contrairement à NerdStorage
, qui dispose de composants de requête et de mutation pour votre commodité, NerdStorageVault
n'a aucun composant dans le SDK. Au lieu de cela, vous devez utiliser NerdGraphQuery
et NerdGraphMutation
pour interagir avec lui
Important
Il est important de se rappeler que NerdStorageVault
est limité à la portée de l’utilisateur. Tout autre utilisateur de votre application New Relic aura ses propres données NerdStorageVault
. Cela signifie que même les autres utilisateurs de votre compte devront saisir leur jeton séparément.
interrogez votre jeton API
Tout d’abord, créez des méthodes et state
pour afficher et masquer votre prompt de jeton API :
import React from 'react';import { ChartGroup, Grid, GridItem, NerdGraphMutation,} 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 = 123456 // <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 { constructor() { super(...arguments);
this.state = { hideTokenPrompt: true, token: null, }
this.storeToken = this.storeToken.bind(this); this.showPrompt = this.showPrompt.bind(this); this.hidePrompt = this.hidePrompt.bind(this); }
storeToken(newToken) { if (newToken != this.state.token) { const mutation = ` mutation($key: String!, $token: SecureValue!) { nerdStorageVaultWriteSecret( scope: { actor: CURRENT_USER } secret: { key: $key, value: $token } ) { status errors { message type } } } `; const variables = { key: "api_token", token: newToken, }; NerdGraphMutation.mutate({ mutation: mutation, variables: variables }).then( (data) => { if (data.data.nerdStorageVaultWriteSecret.status === "SUCCESS") { this.setState({token: newToken}) } } ); } }
showPrompt() { this.setState({ hideTokenPrompt: false }); }
hidePrompt() { this.setState({ hideTokenPrompt: true }); }
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> }}
state.hideTokenPrompt
détermine si le prompt est visible ou non. Maintenant, vous avez besoin d’un mécanisme pour révéler le prompt, qui est masqué par défaut.
requête NerdStorageVault pour votre api_token
:
import React from 'react';import { ChartGroup, Grid, GridItem, NerdGraphMutation, NerdGraphQuery,} 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 = 123456 // <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 { constructor() { super(...arguments);
this.state = { hideTokenPrompt: true, token: null, }
this.storeToken = this.storeToken.bind(this); this.showPrompt = this.showPrompt.bind(this); this.hidePrompt = this.hidePrompt.bind(this); }
storeToken(newToken) { if (newToken != this.state.token) { const mutation = ` mutation($key: String!, $token: SecureValue!) { nerdStorageVaultWriteSecret( scope: { actor: CURRENT_USER } secret: { key: $key, value: $token } ) { status errors { message type } } } `; const variables = { key: "api_token", token: newToken, }; NerdGraphMutation.mutate({ mutation: mutation, variables: variables }).then( (data) => { if (data.data.nerdStorageVaultWriteSecret.status === "SUCCESS") { this.setState({token: newToken}) } } ); } }
showPrompt() { this.setState({ hideTokenPrompt: false }); }
hidePrompt() { this.setState({ hideTokenPrompt: true }); }
componentDidMount() { const query = ` query($key: String!) { actor { nerdStorageVault { secret(key: $key) { value } } } } `; const variables = { key: "api_token", };
NerdGraphQuery.query( { query: query, variables: variables, } ).then( ({ loading, error, data }) => { if (error) { console.error(error); this.showPrompt(); }
if (data && data.actor.nerdStorageVault.secret) { this.setState({ token: data.actor.nerdStorageVault.secret.value }) } else { this.showPrompt(); } } ) }
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> }}
Ici, dans componentDidMount()
, vous avez demandé à NerdGraph
vos données api_token
. componentDidMount()
est une méthode de cycle de vie React qui est appelée lorsque votre composant est monté dans l'arborescence des composants. Cela signifie qu'au début de son processus de configuration, votre application demandera votre api_token
.
Si la requête NerdGraph
répond avec succès avec votre jeton de NerdStorageVault
, elle définit le jeton dans state
. Sinon, il affiche le prompt pour que vous puissiez saisir un jeton.
C'est idéal pour stocker un jeton initial, mais que se passe-t-il si vous entrez le mauvais jeton ou si l'API change ? Vous avez besoin d’un moyen de révéler l’ prompt à la demande. Ensuite, vous créerez l’ prompt jeton réelle et un bouton pour appeler manuellement l’ prompt.
Créez votre prompt
Dans nerdlets/ab-test-nerdlet
, ajoutez un nouveau fichier Javascript nommé token-prompt.js
:
$touch token-prompt.js
Dans ce nouveau fichier, créez un bouton qui vous permet de saisir un nouveau jeton à la demande :
import React from 'react';import { Button } from 'nr1';
class ApiTokenButton extends React.Component { constructor(props) { super(props) }
render() { return ( <Button onClick={this.props.showPrompt}>Update API token</Button> ) }}
ApiTokenButton
reçoit showPrompt()
dans ses accessoires et appelle cette méthode lorsque son Button
est cliqué.
Créez une prompt de jeton en utilisant un Modal
avec un TextField
:
import React from 'react';import { Button, Modal, TextField,} from 'nr1';
class ApiTokenButton extends React.Component { constructor(props) { super(props) }
render() { return ( <Button onClick={this.props.showPrompt}>Update API token</Button> ) }}
class ApiTokenPrompt extends React.Component { constructor() { super(...arguments);
this.state = { token: null, tokenError: false, };
this.submitToken = this.submitToken.bind(this); this.hideTokenError = this.hideTokenError.bind(this); this.changeToken = this.changeToken.bind(this); this.keyPress = this.keyPress.bind(this); }
showTokenError() { this.setState({ tokenError: true }); }
hideTokenError() { this.setState({ tokenError: false }); }
changeToken(event) { this.setState({ token: event.target.value }); }
submitToken(event) { event.preventDefault();
if (this.state.token) { this.props.storeToken(this.state.token) this.hideTokenError() this.props.hidePrompt() } else { this.showTokenError() } }
keyPress(event) { if(event.keyCode == 13) { event.preventDefault();
this.submitToken(event); } }
render() { return <Modal hidden={this.props.hideTokenPrompt} onClose={this.props.hidePrompt}> To see cancellation data, you need to enter an API token for your backend service: <form> <TextField label="API token" onChange={this.changeToken} onKeyDown={this.keyPress} invalid={this.state.tokenError ? "Token required" : false} /> <Button type={Button.TYPE.PRIMARY} onClick={this.submitToken}>Submit</Button> </form> </Modal> }}
ApiTokenPrompt
rend un Modal
avec un TextField
, un Button
et une prompt explicative. Vous utilisez le Modal
pour saisir votre jeton API. Il fournit également une gestion des erreurs de base si vous essayez de soumettre le formulaire sans valeur de jeton.
Il est important de distinguer le token
dans AbTestNerdletNerdlet.state
et le token
dans ApiTokenPrompt.state
. Le token
dans state
de votre Nerdlet est le jeton actuel que votre Nerdlet connaît. C'est ce jeton qui correspond à ce qui est dans NerdStorageVault
. Le token
dans ApiTokenPrompt.state
est une valeur fluide qui change lorsque vous mettez à jour le texte dans TextField
. Lorsque vous appuyez sur Submit dans la fenêtre modale, ApiTokenPrompt
soumet son token
à la méthode storeToken()
de votre Nerdlet. Ensuite, storeToken()
mute NerdStorageVault
avec le nouveau jeton.
Vous avez également mis en œuvre quelques méthodes pour améliorer l'expérience utilisateur :
keyPress()
soumet le jeton lorsque la toucheRETURN
est enfoncéeshowTokenError()
ethideTokenError()
rappelle à l'utilisateur qu'il doit saisir un jeton avant de soumettre le formulaire
Exportez vos composants pour pouvoir les utiliser dans votre Nerdlet :
import React from 'react';import { Button, Modal, TextField,} from 'nr1';
class ApiTokenButton extends React.Component { constructor(props) { super(props) }
render() { return ( <Button onClick={this.props.showPrompt}>Update API token</Button> ) }}
class ApiTokenPrompt extends React.Component { constructor() { super(...arguments);
this.state = { token: null, tokenError: false, };
this.submitToken = this.submitToken.bind(this); this.hideTokenError = this.hideTokenError.bind(this); this.changeToken = this.changeToken.bind(this); this.keyPress = this.keyPress.bind(this); }
showTokenError() { this.setState({ tokenError: true }); }
hideTokenError() { this.setState({ tokenError: false }); }
changeToken(event) { this.setState({ token: event.target.value }); }
submitToken(event) { event.preventDefault();
if (this.state.token) { this.props.storeToken(this.state.token) this.hideTokenError() this.props.hidePrompt() } else { this.showTokenError() } }
keyPress(event) { if(event.keyCode == 13) { event.preventDefault();
this.submitToken(event); } }
render() { return <Modal hidden={this.props.hideTokenPrompt} onClose={this.props.hidePrompt}> To see cancellation data, you need to enter an API token for your backend service: <form> <TextField label="API token" onChange={this.changeToken} onKeyDown={this.keyPress} invalid={this.state.tokenError ? "Token required" : false} /> <Button type={Button.TYPE.PRIMARY} onClick={this.submitToken}>Submit</Button> </form> </Modal> }}
export { ApiTokenButton, ApiTokenPrompt }
Dans le fichier index.js
de votre Nerdlet, importez ApiTokenButton
et ApiTokenPrompt
et ajoutez-les à render()
:
import React from 'react';import { ChartGroup, Grid, GridItem, NerdGraphMutation, NerdGraphQuery,} 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';import { ApiTokenButton, ApiTokenPrompt } from './token-prompt';
const ACCOUNT_ID = 123456 // <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 { constructor() { super(...arguments);
this.state = { hideTokenPrompt: true, token: null, }
this.storeToken = this.storeToken.bind(this); this.showPrompt = this.showPrompt.bind(this); this.hidePrompt = this.hidePrompt.bind(this); }
storeToken(newToken) { if (newToken != this.state.token) { const mutation = ` mutation($key: String!, $token: SecureValue!) { nerdStorageVaultWriteSecret( scope: { actor: CURRENT_USER } secret: { key: $key, value: $token } ) { status errors { message type } } } `; const variables = { key: "api_token", token: newToken, }; NerdGraphMutation.mutate({ mutation: mutation, variables: variables }).then( (data) => { if (data.data.nerdStorageVaultWriteSecret.status === "SUCCESS") { this.setState({token: newToken}) } } ); } }
showPrompt() { this.setState({ hideTokenPrompt: false }); }
hidePrompt() { this.setState({ hideTokenPrompt: true }); }
componentDidMount() { const query = ` query($key: String!) { actor { nerdStorageVault { secret(key: $key) { value } } } } `; const variables = { key: "api_token", };
NerdGraphQuery.query( { query: query, variables: variables, } ).then( ({ loading, error, data }) => { if (error) { console.error(error); this.showPrompt(); }
if (data && data.actor.nerdStorageVault.secret) { this.setState({ token: data.actor.nerdStorageVault.secret.value }) } else { this.showPrompt(); } } ) }
render() { return <div> <ApiTokenPrompt hideTokenPrompt={this.state.hideTokenPrompt} hidePrompt={this.hidePrompt} showPrompt={this.showPrompt} storeToken={this.storeToken} />
<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> <GridItem columnSpan={12}> <ApiTokenButton showPrompt={this.showPrompt} /> </GridItem> </Grid> </div> }}
Accédez à la racine de votre Nerdpack à nru-programmability-course/add-nerdstoragevault/ab-test
.
Générez un nouvel UUID pour votre Nerdpack :
$nr1 nerdpack:uuid -gf
Étant donné que vous avez cloné le référentiel de cours qui contenait un Nerdpack existant, vous devez générer votre propre identifiant unique. Cet UUID mappe votre Nerdpack à votre compte New Relic. Il permet également à votre application de faire requests Nerdgraph au nom de votre compte.
Présentez votre demande localement :
$nr1 nerdpack:serve
Accédez à https://one.newrelic.com?nerdpacks=local et affichez votre application sous Apps > Your apps.
Lorsque vous visitez votre application pour la première fois, le prompt est automatiquement révélée. Saisissez « ABC123 » dans le TextField
, car c'est le jeton attendu par le service tiers. Une fois que vous avez soumis votre jeton et que votre Nerdlet masque le prompt, cliquez sur Update API token en bas de votre application New Relic pour le révéler à nouveau.
Conseil
Si quelque chose ne fonctionne pas, utilisez les outils de débogage de votre navigateur pour essayer d'identifier le problème.
Assurez-vous de :
- J'ai copié correctement le code de la leçon
- Générer un nouvel UUID
- Remplacé toutes les instances de
<YOUR NEW RELIC ACCOUNT ID>
dans votre projet par votre identifiant de compteNew Relic actuel
Transmettez votre jeton API à TotalCancellations
Dans index.js
, transmettez le jeton API à TotalCancellations
afin d'être prêt à faire une demande au service tiers :
import React from 'react';import { ChartGroup, Grid, GridItem, NerdGraphMutation, NerdGraphQuery,} 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';import { ApiTokenButton, ApiTokenPrompt } from './token-prompt';
const ACCOUNT_ID = 123456 // <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 { constructor() { super(...arguments);
this.state = { hideTokenPrompt: true, token: null, }
this.storeToken = this.storeToken.bind(this); this.showPrompt = this.showPrompt.bind(this); this.hidePrompt = this.hidePrompt.bind(this); }
storeToken(newToken) { if (newToken != this.state.token) { const mutation = ` mutation($key: String!, $token: SecureValue!) { nerdStorageVaultWriteSecret( scope: { actor: CURRENT_USER } secret: { key: $key, value: $token } ) { status errors { message type } } } `; const variables = { key: "api_token", token: newToken, }; NerdGraphMutation.mutate({ mutation: mutation, variables: variables }).then( (data) => { if (data.data.nerdStorageVaultWriteSecret.status === "SUCCESS") { this.setState({token: newToken}) } } ); } }
showPrompt() { this.setState({ hideTokenPrompt: false }); }
hidePrompt() { this.setState({ hideTokenPrompt: true }); }
componentDidMount() { const query = ` query($key: String!) { actor { nerdStorageVault { secret(key: $key) { value } } } } `; const variables = { key: "api_token", };
NerdGraphQuery.query( { query: query, variables: variables, } ).then( ({ loading, error, data }) => { if (error) { console.error(error); this.showPrompt(); }
if (data && data.actor.nerdStorageVault.secret) { this.setState({ token: data.actor.nerdStorageVault.secret.value }) } else { this.showPrompt(); } } ) }
render() { return <div> <ApiTokenPrompt hideTokenPrompt={this.state.hideTokenPrompt} hidePrompt={this.hidePrompt} showPrompt={this.showPrompt} storeToken={this.storeToken} />
<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 token={this.state.token} /> </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> <GridItem columnSpan={12}> <ApiTokenButton showPrompt={this.showPrompt} /> </GridItem> </Grid> </div> }}
Dans total-cancellations.js
, log le jeton à la console de votre navigateur :
import React from 'react';import { HeadingText, PieChart } from 'nr1';
export default class TotalCancellations extends React.Component { constructor() { super(...arguments);
this.state = { lastToken: null } }
componentDidUpdate() { if (this.props.token && this.props.token != this.state.lastToken) { console.log(`requesting data with api token ${this.props.token}`) this.setState({lastToken: this.props.token}) } }
render() { const cancellationsA = { metadata: { id: 'cancellations-A', name: 'Version A', viz: 'main', color: 'blue', }, data: [ { y: 118 }, ], } const cancellationsB = { metadata: { id: 'cancellations-B', name: 'Version B', viz: 'main', color: 'green', }, data: [ { y: 400 }, ], } return <div> <HeadingText className="chartHeader"> Total cancellations per version </HeadingText> <PieChart data={[cancellationsA, cancellationsB]} fullWidth /> </div> }}
Ici, vous avez implémenté une autre méthode de cycle de vie React, appelée componentDidUpdate()
. Désormais, chaque fois que state.token
de votre Nerdlet change, TotalCancellations
obtient un nouveau jeton, ce qui déclenche componentDidUpdate()
. Dans componentDidUpdate()
, vous vérifiez que le jeton entrant n'est pas le même que le dernier jeton qu'il connaissait, qui est stocké dans l'état local. Si le jeton entrant est différent, vous enregistrez un message avec le nouveau jeton et mettez à jour state.lastToken
.
Cette logique prépare votre code pour une modification future visant à utiliser votre jeton API dans une demande à un service tiers.
Avec votre Nerdpack servi localement, affichez votre application pour voir le log de TotalCancellations
dans la console de votre navigateur. Si vous changez votre jeton, vous verrez un autre log de TotalCancellations
avec votre jeton mis à jour.
Conseil
Si quelque chose ne fonctionne pas, utilisez les outils de débogage de votre navigateur pour essayer d'identifier le problème.
Assurez-vous de :
- J'ai copié correctement le code de la leçon
- Générer un nouvel UUID
- Remplacé toutes les instances de
<YOUR NEW RELIC ACCOUNT ID>
dans votre projet par votre identifiant de compteNew Relic actuel
Une fois que vous avez terminé, arrêtez de diffuser votre application New Relic en appuyant sur CTRL+C
dans la fenêtre de terminal de votre serveur local.
Vous savez maintenant comment utiliser NerdGraphQuery
et NerdGraphMutation
pour gérer les données dans NerdStorageVault
! N'oubliez pas d'utiliser NerdStorage
pour les données non sensibles de votre application New Relic et NerdStorageVault
pour les éléments sensibles comme les jetons, les mots de passe et autres secrets. En bonus, vous avez créé un moyen de gérer votre jeton dans NerdStorageVault
depuis l'interface utilisateur. Vous avez également transmis le jeton à votre composant TotalCancellations
pour une utilisation ultérieure.
Que ce soit avec NrqlQuery
, AccountStorageQuery
, AccountStorageMutation
, NerdGraphQuery
ou NerdGraphMutation
, vous avez appris plusieurs façons d'interagir avec les données New Relic dans votre application New Relic. Mais les applications New Relic ne sont pas simplement une autre façon d’afficher les données New Relic. L'objectif des applications New Relic est de vous montrer comment votre logiciel vous aide à atteindre vos objectifs commerciaux. Parfois, les données New Relic sont tout ce dont vous avez besoin pour y parvenir, mais d'autres fois, vous devez chercher au-delà de New Relic des données pour combler les lacunes.
Conseil
Cette leçon fait partie d’un cours qui vous apprend à créer une application New Relic à partir de zéro. Passez à la leçon suivante : Récupérer des données à partir d’un service tiers.