Cloud Function Validation em Apps React Native
10 min
usando validadores de cloud function em um aplicativo react native introdução neste guia, você aprenderá como usar os validadores de função do parse cloud code e o uso de contexto em um aplicativo react native você verá exemplos de validadores implementados em funções de nuvem, como usar contexto entre diferentes funções e também verificará como implementar um componente react native usando essas funções aprimoradas no back4app pré requisitos para completar este tutorial, você precisará um aplicativo react native criado e conectado ao back4app https //www back4app com/docs/react native/parse sdk/react native sdk entender como implantar uma função de nuvem https //www back4app com/docs/get started/cloud functions no back4app se você quiser executar o aplicativo de exemplo deste guia, deve configurar o \<font color="#2166ae">react native paper\</font> biblioteca https //github com/callstack/react native paper objetivo executar funções de cloud code do parse com validadores e uso de contexto no back4app a partir de um aplicativo react native 1 entendendo os validadores de funções de cloud code usar \<font color="#2166ae">funções de cloud code\</font> em seu aplicativo permite uma grande flexibilidade em seu código, tornando possível desacoplar métodos reutilizáveis do seu aplicativo e controlar melhor seu comportamento você pode verificar ou revisar como usá los em nosso guia inicial de funções de nuvem https //www back4app com/docs/get started/cloud functions os dados enviados para essas funções em nuvem devem ser validados para evitar erros inesperados, e o parse cloud code (a partir da versão 4 4 0) oferece um conjunto completo de \<font color="#2166ae">validadores\</font> , que podem ser passados como um \<font color="#2166ae">objeto\</font> ou até mesmo outra função esses testes são chamados antes de executar qualquer código dentro da sua função, então você pode sempre assumir que os dados serão válidos se os validadores os aceitarem pegue a seguinte função em nuvem como exemplo, na qual a média de uma determinada avaliação de filme é calculada o nome do filme é obrigatório( \<font color="#2166ae">filme\</font> ), então você pode usar o seguinte objeto para garantir que ele seja passado se o parâmetro do filme não for passado, a função retornará um erro contendo a mensagem “validação falhou por favor, especifique os dados para o filme ” 1 parse cloud define('getmovieaveragerating', async (request) => { 2 const query = new parse query("review"); 3 query equalto("movie", request params movie); 4 const results = await query find(); 5 let sum = 0; 6 for (let review of results) { 7 sum += review\ get("rate"); 8 } 9 return { 10 result sum / results length, 11 }; 12 },{ 13 fields \['movie'], 14 }); você também pode passar opções mais avançadas para o \<font color="#2166ae">validador\</font> , como exigir que o usuário que chama a função esteja autenticado usando a \<font color="#2166ae">flag requireuser\</font> outra adição útil é adicionar uma condição para validar o valor de um parâmetro, como o seguinte, garantindo que o valor do \<font color="#2166ae">filme\</font> seja uma string e tenha pelo menos 3 caracteres 1 parse cloud define('getmovieaveragerating', async (request) => { 2 const query = new parse query("review"); 3 query equalto("movie", request params movie); 4 const results = await query find(); 5 let sum = 0; 6 for (let review of results) { 7 sum += review\ get("rate"); 8 } 9 return { 10 result sum / results length, 11 }; 12 },{ 13 fields { 14 movie { 15 required true, 16 type string, 17 options val => { 18 return val length >= 3; 19 }, 20 error 'movie must have at least 3 characters' 21 } 22 }, 23 }); a lista completa de \<font color="#2166ae">validação\</font> opções pode ser vista na documentação https //docs parseplatform org/cloudcode/guide/#implementing cloud function validation 2 compreendendo o contexto das funções do cloud code ao usar o cloud code (a partir da versão 4 3 0) triggers de salvamento em sua aplicação, você pode passar um \<font color="#2166ae">contexto\</font> dicionário no método \<font color="#2166ae">parse object save\</font> e também passá lo de um \<font color="#2166ae">beforesave\</font> handler para um \<font color="#2166ae">aftersave\</font> handler isso pode ser usado para garantir a consistência dos dados ou para realizar qualquer tipo de operação assíncrona que só pode ser feita após salvar seu objeto com sucesso tome a seguinte função de nuvem como exemplo, na qual um \<font color="#2166ae">revisão\</font> objeto é salvo e queremos diferenciar se a chamada de salvamento do objeto foi feita a partir do seu aplicativo ou do painel isso será alcançado definindo o contexto antes de salvar o objeto no código do seu aplicativo assim 1 const review = new parse object('review'); 2 review\ set('text', 'great movie!'); 3 review\ set('rate', 5); 4 review\ set('movie', 'mission very possible'); 5	 6 const context = { fromapplication true }; 7 const savedreview = await review\ save(null, {context context}); aqui estão as funções de gatilho de salvamento, note que o objeto de contexto pode ser acessado diretamente da solicitação em ambas 1 parse cloud beforesave('review', async (request) => { 2 // try to get context 3 try { 4 const context = request context; 5 if (context fromapplication === true) { 6 // set a flag in the object before saving 7 request object set('fromapplication', true); 8 } 9 } catch(error){} 10	 11 const text = request object get('text'); 12 if (text length > 20) { 13 // truncate and add a 14 request object set('text', text substring(0, 17) + ' '); 15 } 16 }); 17	 18 parse cloud aftersave('review', async (request) => { 19 // try to get context 20 try { 21 const context = request context; 22 if (context fromapplication === true) { 23 // do something when coming from application, like sending a notification or email 24 console log('got context fromapplication when saving review object'); 25 } 26 } catch(error){} 27 }); 3 usando código de nuvem validado a partir de um componente react native agora vamos usar o mesmo exemplo de componente do último guia https //www back4app com/docs/react native/parse sdk/cloud functions/react native cloud functions como base e adicionar algumas mudanças para destacar as funções agora usando validadores e contexto lembre se de implantar as funções de nuvem mostradas nos passos 1 e 2 e, após isso, alterar a função de criação do objeto “review” para o seguinte, com a adição do argumento de contexto no método de salvamento javascript 1 const createreview = async function () { 2 try { 3 // this values come from state variables linked to 4 // the screen form fields 5 const reviewtextvalue = reviewtext; 6 const reviewratevalue = number(reviewrate); 7	 8 // creates a new parse object instance 9 let review = new parse object('review'); 10	 11 // set data to parse object 12 // simple title field 13 review\ set('text', reviewtextvalue); 14	 15 // simple number field 16 review\ set('rate', reviewratevalue); 17	 18 // set default movie 19 review\ set('movie', 'mission very possible'); 20	 21 // after setting the values, save it on the server 22 try { 23 // add 24 const context = {fromapplication true}; 25 await review\ save(null, {context context}); 26 // success 27 alert alert('success!'); 28 // updates query result list 29 doreviewquery(); 30 rungetmovieaveragerating(); 31 return true; 32 } catch (error) { 33 // error can be caused by lack of internet connection 34 alert alert('error!', error message); 35 return false; 36 } 37 } catch (error) { 38 // error can be caused by lack of field values 39 alert alert('error!', error message); 40 return false; 41 } 42 };1 const createreview = async function () promise\<boolean> { 2 try { 3 // this values come from state variables linked to 4 // the screen form fields 5 const reviewtextvalue string = reviewtext; 6 const reviewratevalue number = number(reviewrate); 7	 8 // creates a new parse object instance 9 let review parse object = new parse object('review'); 10	 11 // set data to parse object 12 // simple title field 13 review\ set('text', reviewtextvalue); 14	 15 // simple number field 16 review\ set('rate', reviewratevalue); 17	 18 // set default movie 19 review\ set('movie', 'mission very possible'); 20	 21 // after setting the values, save it on the server 22 try { 23 const context = {fromapplication true}; 24 await review\ save(null, {context context}); 25 // success 26 alert alert('success!'); 27 // updates query result list 28 doreviewquery(); 29 rungetmovieaveragerating(); 30 return true; 31 } catch (error) { 32 // error can be caused by lack of internet connection 33 alert alert('error!', error message); 34 return false; 35 } 36 } catch (error) { 37 // error can be caused by lack of field values 38 alert alert('error!', error message); 39 return false; 40 } 41 }; para destacar a função validadora de cálculo da média do filme, adicione uma nova função consultando um filme chamado “eu” e adicione outro botão chamando a lembre se de que nosso validador fará a solicitação falhar devido ao comprimento do nome do filme aqui está o código da nova função javascript 1 const createreview = async function () { 2 try { 3 // this values come from state variables linked to 4 // the screen form fields 5 const reviewtextvalue = reviewtext; 6 const reviewratevalue = number(reviewrate); 7	 8 // creates a new parse object instance 9 let review = new parse object('review'); 10	 11 // set data to parse object 12 // simple title field 13 review\ set('text', reviewtextvalue); 14	 15 // simple number field 16 review\ set('rate', reviewratevalue); 17	 18 // set default movie 19 review\ set('movie', 'mission very possible'); 20	 21 // after setting the values, save it on the server 22 try { 23 // add 24 const context = {fromapplication true}; 25 await review\ save(null, {context context}); 26 // success 27 alert alert('success!'); 28 // updates query result list 29 doreviewquery(); 30 rungetmovieaveragerating(); 31 return true; 32 } catch (error) { 33 // error can be caused by lack of internet connection 34 alert alert('error!', error message); 35 return false; 36 } 37 } catch (error) { 38 // error can be caused by lack of field values 39 alert alert('error!', error message); 40 return false; 41 } 42 };1 const createreview = async function () promise\<boolean> { 2 try { 3 // this values come from state variables linked to 4 // the screen form fields 5 const reviewtextvalue string = reviewtext; 6 const reviewratevalue number = number(reviewrate); 7	 8 // creates a new parse object instance 9 let review parse object = new parse object('review'); 10	 11 // set data to parse object 12 // simple title field 13 review\ set('text', reviewtextvalue); 14	 15 // simple number field 16 review\ set('rate', reviewratevalue); 17	 18 // set default movie 19 review\ set('movie', 'mission very possible'); 20	 21 // after setting the values, save it on the server 22 try { 23 const context = {fromapplication true}; 24 await review\ save(null, {context context}); 25 // success 26 alert alert('success!'); 27 // updates query result list 28 doreviewquery(); 29 rungetmovieaveragerating(); 30 return true; 31 } catch (error) { 32 // error can be caused by lack of internet connection 33 alert alert('error!', error message); 34 return false; 35 } 36 } catch (error) { 37 // error can be caused by lack of field values 38 alert alert('error!', error message); 39 return false; 40 } 41 }; é assim que o novo código completo do componente está organizado, note que há uma nova linha no título do item da lista mostrando se a revisão foi criada a partir da aplicação ou não javascript 1 import react, {usestate} from 'react'; 2 import {alert, image, view, scrollview, stylesheet} from 'react native'; 3 import parse from 'parse/react native'; 4 import { 5 list, 6 title, 7 textinput as papertextinput, 8 button as paperbutton, 9 text as papertext, 10 } from 'react native paper'; 11	 12 export const movieratings = () => { 13 // state variable 14 const \[queryresults, setqueryresults] = usestate(null); 15 const \[ratingsaverage, setratingsaverage] = usestate(''); 16 const \[reviewtext, setreviewtext] = usestate(''); 17 const \[reviewrate, setreviewrate] = usestate(''); 18	 19 const rungetmovieaveragerating = async function () { 20 try { 21 const params = { 22 movie 'mission very possible', 23 }; 24 let resultobject = await parse cloud run( 25 'getmovieaveragerating', 26 params, 27 ); 28 // set query results to state variable using state hook 29 setratingsaverage(resultobject result tofixed(1)); 30 return true; 31 } catch (error) { 32 // error can be caused by lack of internet connection 33 // or by not having an valid review object yet 34 alert alert( 35 'error!', 36 'make sure that the cloud function is deployed and that the review class table is created', 37 ); 38 return false; 39 } 40 }; 41	 42 const rungetmemovieaveragerating = async function () { 43 try { 44 const params = { 45 movie 'me', 46 }; 47 let resultobject = await parse cloud run( 48 'getmovieaveragerating', 49 params, 50 ); 51 return true; 52 } catch (error) { 53 // error can be caused by lack of internet connection 54 // or by not having an valid review object yet 55 alert alert('error!', json stringify(error message)); 56 return false; 57 } 58 }; 59	 60 const doreviewquery = async function () { 61 // create our query 62 let parsequery = new parse query('review'); 63 try { 64 let results = await parsequery find(); 65 // set query results to state variable 66 setqueryresults(results); 67 return true; 68 } catch (error) { 69 // error can be caused by lack of internet connection 70 alert alert('error!', error message); 71 return false; 72 } 73 }; 74	 75 const createreview = async function () { 76 try { 77 // this values come from state variables linked to 78 // the screen form fields 79 const reviewtextvalue = reviewtext; 80 const reviewratevalue = number(reviewrate); 81	 82 // creates a new parse object instance 83 let review = new parse object('review'); 84	 85 // set data to parse object 86 // simple title field 87 review\ set('text', reviewtextvalue); 88	 89 // simple number field 90 review\ set('rate', reviewratevalue); 91	 92 // set default movie 93 review\ set('movie', 'mission very possible'); 94	 95 // after setting the values, save it on the server 96 try { 97 const context = {fromapplication true}; 98 await review\ save(null, {context context}); 99 // success 100 alert alert('success!'); 101 // updates query result list 102 doreviewquery(); 103 rungetmovieaveragerating(); 104 return true; 105 } catch (error) { 106 // error can be caused by lack of internet connection 107 alert alert('error!', error message); 108 return false; 109 } 110 } catch (error) { 111 // error can be caused by lack of field values 112 alert alert('error!', error message); 113 return false; 114 } 115 }; 116	 117 return ( 118 <> 119 \<view style={styles header}> 120 \<image 121 style={styles header logo} 122 source={ {uri 'https //blog back4app com/wp content/uploads/2019/05/back4app white logo 500px png',} } 123 /> 124 \<papertext style={styles header text}> 125 \<papertext style={styles header text bold}> 126 {'react native on back4app '} 127 \</papertext> 128 {' cloud code movie ratings'} 129 \</papertext> 130 \</view> 131 \<scrollview style={styles wrapper}> 132 \<view> 133 \<title>{'mission very possible reviews'}\</title> 134 \<papertext>{`ratings average ${ratingsaverage}`}\</papertext> 135 {/ query list /} 136 {queryresults !== null && 137 queryresults !== undefined && 138 queryresults map((result) => ( 139 \<list item 140 key={result id} 141 title={`review text ${result get('text')}`} 142 description={`rate ${result get('rate')}\nfrom app ${ 143 result get('fromapplication') !== undefined ? 'yes' 'no' 144 }`} 145 titlestyle={styles list text} 146 style={styles list item} 147 /> 148 ))} 149 {queryresults === null || 150 queryresults === undefined || 151 (queryresults !== null && 152 queryresults !== undefined && 153 queryresults length <= 0) ? ( 154 \<papertext>{'no results here!'}\</papertext> 155 ) null} 156 \</view> 157 \<view> 158 \<title>action buttons\</title> 159 \<paperbutton 160 onpress={() => rungetmovieaveragerating()} 161 mode="contained" 162 icon="search web" 163 color={'#208aec'} 164 style={styles list button}> 165 {'calculate review average'} 166 \</paperbutton> 167 \<paperbutton 168 onpress={() => rungetmemovieaveragerating()} 169 mode="contained" 170 icon="search web" 171 color={'#208aec'} 172 style={styles list button}> 173 {'calculate me movie review average'} 174 \</paperbutton> 175 \<paperbutton 176 onpress={() => doreviewquery()} 177 mode="contained" 178 icon="search web" 179 color={'#208aec'} 180 style={styles list button}> 181 {'query reviews'} 182 \</paperbutton> 183 \</view> 184 \<view> 185 \<title>add new review\</title> 186 \<papertextinput 187 value={reviewtext} 188 onchangetext={text => setreviewtext(text)} 189 label="text" 190 mode="outlined" 191 style={styles form input} 192 /> 193 \<papertextinput 194 value={reviewrate} 195 onchangetext={text => setreviewrate(text)} 196 keyboardtype={'number pad'} 197 label="rate (1 5)" 198 mode="outlined" 199 style={styles form input} 200 /> 201 \<paperbutton 202 onpress={() => createreview()} 203 mode="contained" 204 icon="plus" 205 style={styles submit button}> 206 {'add'} 207 \</paperbutton> 208 \</view> 209 \</scrollview> 210 \</> 211 ); 212 }; 213	 214 // these define the screen component styles 215 const styles = stylesheet create({ 216 header { 217 alignitems 'center', 218 paddingtop 30, 219 paddingbottom 50, 220 backgroundcolor '#208aec', 221 }, 222 header logo { 223 height 50, 224 width 220, 225 resizemode 'contain', 226 }, 227 header text { 228 margintop 15, 229 color '#f0f0f0', 230 fontsize 16, 231 }, 232 header text bold { 233 color '#fff', 234 fontweight 'bold', 235 }, 236 wrapper { 237 width '90%', 238 alignself 'center', 239 }, 240 list button { 241 margintop 6, 242 marginleft 15, 243 height 40, 244 }, 245 list item { 246 borderbottomwidth 1, 247 borderbottomcolor 'rgba(0, 0, 0, 0 12)', 248 }, 249 list text { 250 fontsize 15, 251 }, 252 form input { 253 height 44, 254 marginbottom 16, 255 backgroundcolor '#fff', 256 fontsize 14, 257 }, 258 submit button { 259 width '100%', 260 maxheight 50, 261 alignself 'center', 262 backgroundcolor '#208aec', 263 }, 264 });1 import react, {fc, reactelement, usestate} from 'react'; 2 import {alert, image, view, scrollview, stylesheet} from 'react native'; 3 import parse from 'parse/react native'; 4 import { 5 list, 6 title, 7 textinput as papertextinput, 8 button as paperbutton, 9 text as papertext, 10 } from 'react native paper'; 11	 12 export const movieratings fc<{}> = ({}) reactelement => { 13 // state variable 14 const \[queryresults, setqueryresults] = usestate(null); 15 const \[ratingsaverage, setratingsaverage] = usestate(''); 16 const \[reviewtext, setreviewtext] = usestate(''); 17 const \[reviewrate, setreviewrate] = usestate(''); 18	 19 const rungetmovieaveragerating = async function () promise\<boolean> { 20 try { 21 const params {movie string} = { 22 movie 'mission very possible', 23 }; 24 let resultobject {result number} = await parse cloud run( 25 'getmovieaveragerating', 26 params, 27 ); 28 // set query results to state variable using state hook 29 setratingsaverage(resultobject result tofixed(1)); 30 return true; 31 } catch (error) { 32 // error can be caused by lack of internet connection 33 // or by not having an valid review object yet 34 alert alert( 35 'error!', 36 'make sure that the cloud function is deployed and that the review class table is created', 37 ); 38 return false; 39 } 40 }; 41	 42 const rungetmemovieaveragerating = async function () promise\<boolean> { 43 try { 44 const params {movie string} = { 45 movie 'me', 46 }; 47 let resultobject object = await parse cloud run( 48 'getmovieaveragerating', 49 params, 50 ); 51 return true; 52 } catch (error) { 53 // error can be caused by lack of internet connection 54 // or by not having an valid review object yet 55 alert alert('error!', json stringify(error message)); 56 return false; 57 } 58 }; 59	 60 const doreviewquery = async function () promise\<boolean> { 61 // create our query 62 let parsequery parse query = new parse query('review'); 63 try { 64 let results \[parse object] = await parsequery find(); 65 // set query results to state variable 66 setqueryresults(results); 67 return true; 68 } catch (error) { 69 // error can be caused by lack of internet connection 70 alert alert('error!', error message); 71 return false; 72 } 73 }; 74	 75 const createreview = async function () promise\<boolean> { 76 try { 77 // this values come from state variables linked to 78 // the screen form fields 79 const reviewtextvalue string = reviewtext; 80 const reviewratevalue number = number(reviewrate); 81	 82 // creates a new parse object instance 83 let review parse object = new parse object('review'); 84	 85 // set data to parse object 86 // simple title field 87 review\ set('text', reviewtextvalue); 88	 89 // simple number field 90 review\ set('rate', reviewratevalue); 91	 92 // set default movie 93 review\ set('movie', 'mission very possible'); 94	 95 // after setting the values, save it on the server 96 try { 97 const context = {fromapplication true}; 98 await review\ save(null, {context context}); 99 // success 100 alert alert('success!'); 101 // updates query result list 102 doreviewquery(); 103 rungetmovieaveragerating(); 104 return true; 105 } catch (error) { 106 // error can be caused by lack of internet connection 107 alert alert('error!', error message); 108 return false; 109 } 110 } catch (error) { 111 // error can be caused by lack of field values 112 alert alert('error!', error message); 113 return false; 114 } 115 }; 116	 117 return ( 118 <> 119 \<view style={styles header}> 120 \<image 121 style={styles header logo} 122 source={ { 123 uri 124 'https //blog back4app com/wp content/uploads/2019/05/back4app white logo 500px png', 125 } } 126 /> 127 \<papertext style={styles header text}> 128 \<papertext style={styles header text bold}> 129 {'react native on back4app '} 130 \</papertext> 131 {' cloud code movie ratings'} 132 \</papertext> 133 \</view> 134 \<scrollview style={styles wrapper}> 135 \<view> 136 \<title>{'mission very possible reviews'}\</title> 137 \<papertext>{`ratings average ${ratingsaverage}`}\</papertext> 138 {/ query list /} 139 {queryresults !== null && 140 queryresults !== undefined && 141 queryresults map((result parse object) => ( 142 \<list item 143 key={result id} 144 title={`review text ${result get('text')}`} 145 description={`rate ${result get('rate')}\nfrom app ${ 146 result get('fromapplication') !== undefined ? 'yes' 'no' 147 }`} 148 titlestyle={styles list text} 149 style={styles list item} 150 /> 151 ))} 152 {queryresults === null || 153 queryresults === undefined || 154 (queryresults !== null && 155 queryresults !== undefined && 156 queryresults length <= 0) ? ( 157 \<papertext>{'no results here!'}\</papertext> 158 ) null} 159 \</view> 160 \<view> 161 \<title>action buttons\</title> 162 \<paperbutton 163 onpress={() => rungetmovieaveragerating()} 164 mode="contained" 165 icon="search web" 166 color={'#208aec'} 167 style={styles list button}> 168 {'calculate review average'} 169 \</paperbutton> 170 \<paperbutton 171 onpress={() => rungetmemovieaveragerating()} 172 mode="contained" 173 icon="search web" 174 color={'#208aec'} 175 style={styles list button}> 176 {'calculate me movie review average'} 177 \</paperbutton> 178 \<paperbutton 179 onpress={() => doreviewquery()} 180 mode="contained" 181 icon="search web" 182 color={'#208aec'} 183 style={styles list button}> 184 {'query reviews'} 185 \</paperbutton> 186 \</view> 187 \<view> 188 \<title>add new review\</title> 189 \<papertextinput 190 value={reviewtext} 191 onchangetext={text => setreviewtext(text)} 192 label="text" 193 mode="outlined" 194 style={styles form input} 195 /> 196 \<papertextinput 197 value={reviewrate} 198 onchangetext={text => setreviewrate(text)} 199 keyboardtype={'number pad'} 200 label="rate (1 5)" 201 mode="outlined" 202 style={styles form input} 203 /> 204 \<paperbutton 205 onpress={() => createreview()} 206 mode="contained" 207 icon="plus" 208 style={styles submit button}> 209 {'add'} 210 \</paperbutton> 211 \</view> 212 \</scrollview> 213 \</> 214 ); 215 }; 216	 217 // these define the screen component styles 218 const styles = stylesheet create({ 219 header { 220 alignitems 'center', 221 paddingtop 30, 222 paddingbottom 50, 223 backgroundcolor '#208aec', 224 }, 225 header logo { 226 height 50, 227 width 220, 228 resizemode 'contain', 229 }, 230 header text { 231 margintop 15, 232 color '#f0f0f0', 233 fontsize 16, 234 }, 235 header text bold { 236 color '#fff', 237 fontweight 'bold', 238 }, 239 wrapper { 240 width '90%', 241 alignself 'center', 242 }, 243 list button { 244 margintop 6, 245 marginleft 15, 246 height 40, 247 }, 248 list item { 249 borderbottomwidth 1, 250 borderbottomcolor 'rgba(0, 0, 0, 0 12)', 251 }, 252 list text { 253 fontsize 15, 254 }, 255 form input { 256 height 44, 257 marginbottom 16, 258 backgroundcolor '#fff', 259 fontsize 14, 260 }, 261 submit button { 262 width '100%', 263 maxheight 50, 264 alignself 'center', 265 backgroundcolor '#208aec', 266 }, 267 }); é assim que o componente deve parecer após a renderização e consulta por uma das funções de consulta conclusão no final deste guia, você aprendeu como usar validadores de função do parse cloud code e contexto no próximo guia, você aprenderá como trabalhar com usuários no parse