I have read the documentation how to use sync validation.
const validate = values => {
const errors = {}
....
if (!values.age) {
errors.age = 'Required'
} else if (isNaN(Number(values.age))) {
errors.age = 'Must be a number'
} else if (Number(values.age) < 18) {
errors.age = 'Sorry, you must be at least 18 years old'
}
return errors
}
My need is I want the number '18' is retrieved from server? I need to setup all constants of the validation on server, and read it on page load only then used in this validation section.
Any help would be appreicated. thank you
I don't know if I understand the question correctly.
why don't you initiate validate method on page load after you fetch all constants from server.
validateConstants.js
export const validateConstants = fetch()
validation.js
import { validateConstants } from 'validateConstants'
const validate = values => {
const errors = {}
....
if (!values.age) {
errors.age = 'Required'
} else if (isNaN(Number(values.age))) {
errors.age = 'Must be a number'
} else if (Number(values.age) < validateConstants.age) {
errors.age = 'Sorry, you must be at least 18 years old'
}
return errors
}
Related
I created an app with Ionic and Firestore that features live chat and I'm having a problem with it.
The conversation is loaded with the method:
refUneConversationMyUserCol.ref.orderBy('date', 'desc').limit(20).get()
To this is added an "onSnapshot" request to retrieve the last message sent live
this.unsubscribeDataUneConversation = refUneConversationMyUserCol.ref.orderBy('date', 'desc').limit(1).onSnapshot(result => {
console.log(result.docs[0].data());
if (this.isCalledBySnapshot === false) {
this.isCalledBySnapshot = true;
} else if (result.docs[0].data().expediteur !== this.authentificationService.uidUserActif) {
const data = result.docs[0].data();
const id = result.docs[0].id;
this.dataUneConversation.push({ id, ...data } as UneConversation);
}
});
It will work perfectly however, when I send a message at the same time (with 2 different accounts talking to each other), I encounter a problem, the onSnapshot is triggered only once and I only receive one message.
I specify that the two messages are sent well in the database, they are only not displayed both during the live session
Do you have any idea why?
Thank you
(Here is the whole method)
async getDataUneConversation(idI: string) {
if (this.loadedDataUneConversation !== idI) {
/* ANCHOR Msg en direct */
this.isCalledBySnapshot = false;
if (this.unsubscribeDataUneConversation) {
await this.unsubscribeDataUneConversation();
}
const refUneConversationMyUserCol = this.afs.collection<User>('users').doc<User>(this.authentificationService.uidUserActif).collection<Conversations>('conversations');
const result = await refUneConversationMyUserCol.ref.orderBy('date', 'desc').limit(20).get();
/* ANCHOR Msg en direct */
this.unsubscribeDataUneConversation = refUneConversationMyUserCol.ref.orderBy('date', 'desc').limit(1).onSnapshot(result => {
console.log(result.docs[0].data());
if (this.isCalledBySnapshot === false) {
this.isCalledBySnapshot = true;
} else if (result.docs[0].data().expediteur !== this.authentificationService.uidUserActif) {
const data = result.docs[0].data();
const id = result.docs[0].id;
this.dataUneConversation.push({ id, ...data } as UneConversation);
}
});
/* ANCHOR Msg en brut */
if (result.docs.length < 20) {
this.infiniteLastUneConversationMax = true;
} else {
this.infiniteLastUneConversationMax = false;
}
this.infiniteLastUneConversation = result.docs[result.docs.length - 1];
this.dataUneConversation = result.docs.map(doc => {
const data = doc.data();
const id = doc.id;
return { id, ...data } as UneConversation;
});
this.dataUneConversation.reverse();
this.loadedDataUneConversation = idI;
}
}
EDIT for working :
this.unsubscribeDataUneConversation = refUneConversationMyUserCol.ref.orderBy('date', 'asc').startAfter(this.dataUneConversation[this.dataUneConversation.length
- 1].date).onSnapshot(result => {
result.docs.forEach(element => {
const data = element.data();
const id = element.id;
if (!this.dataUneConversation.some(e => e.id === element.id)) {
this.dataUneConversation.push({ id, ...data } as UneConversation);
}
});
});
You're limiting live messages to only one last message. In a chat app, you want to listen to all new messages. So the issue is probably in your .limit(1) clause.
But if you do that, I understand that you'll get the whole conversation, with all messages, since the conversation started.
My approach would be like this:
Get the date of the last message from your refUneConversationMyUserCol... conversation loader.
When you do the onSnapshot() to get the last message, do not limit to 1 message, instead, start at a date after the date of the last loaded message.
Since you're ordering by date anyway, this will be an easy fix. Look into "Adding a cursor to your query".
Basically, you'll be saying to Firestore: give me LIVE new messages but start at NOW - and even if there are many messages posted at the same time, you'll get them all, since you're not limiting to 1.
Feel free to ask if this is not clear enough.
officeView.component.ts
setSelectedPerson(id:number)
{
this.pservice.getPerson(id);
localStorage.setItem("selectedPerson", JSON.stringify(this.pservice.person));
}
person.service.ts
getPerson(id:number)
{
this.http.get(personUrl + id).subscribe(response => this.person = response )
}
person:Person;
I'm executing setSelectedPerson method from OfficeViewComponent and here's what I'm hoping to happen:
I ask PersonService to fetch the data from api and assign it to it's variable - PersonService.person;
Now that the response is assigned to the service person variable, I'm expecting it to be stringified and saved in localStorage.
But here's what actually happens:
I ask PersonService to fetch the data, PersonService reacts and proceeds with the request, but by the time PersonService.getPerson() is finished, localStorage has already attempted to collect the data from PersonService.person, which - at that time - was unassigned.
I know there is a way to wait until the service method finishes it's work, but I don't know exactly what should I use.
Return the subscription from the service and use it to set data inside it. You don't need any variable inside your service.
officeView.component.ts :
setSelectedPerson(id:number){
this.pservice.getPerson(id).subscribe(
response => {
localStorage.setItem("selectedPerson", JSON.stringify(response));
},error => {
console.log('Error :',error.error)
}
)
}
person.service.ts :
getPerson(id:number) : Observable<any>{
return this.http.get(personUrl + id);
}
You're right, you should wait until the result is ready.
By then you can call the setSelectedPerson func.
//service func
getPerson(id:number) {
return this.http.get(personUrl + id);
}
//component func
setSelectedPerson(id:number){
this.pservice.getPerson(id).subscribe(data => {
localStorage.setItem("selectedPerson", JSON.stringify(data ));
});
}
The problem with you code is the early subscribe in service itself, Ideally it should be in the component (at the caller)
officeView.component.ts :
setSelectedPerson( id : number ){
this.pservice.getPerson(id).subscribe(
response => {
localStorage.setItem("selectedPerson", JSON.stringify(response));
},error => {
console.log( 'Error :',error.error )
}
)
}
person.service.ts :
getPerson( id : number ) : Observable< any >{
return this.http.get( personUrl + id );
}
person.service.ts
getPerson(id:number) {
return this.http.get(personUrl + id);
person:Person;
and when you calling the HTTP service you should use subscribe
setSelectedPerson(id:number){
this.pservice.getPerson(id).subscribe(data=>{
console.log(data);
});
}
I wrote a helper methods to add a network response listener over Puppeteer page instance. the code looks like this
let Helper = codecept_helper;
class CheckHelper extends Helper {
async listenRequest(listener)
{
const helper = this.helpers['Puppeteer'];
await helper.page.setRequestInterception(true);
helper.page.on("request",listener);
return helper._waitForAction();
}
async listenResponse(listener)
{
const helper = this.helpers['Puppeteer'];
helper.page.on("response",listener);
return helper._waitForAction();
}
}
module.exports = CheckHelper;
then in the test script
let self=this;
I.listenResponse((response)=>{
if(response.url().match(/github.*\.js/) && response.headers()['content-length']>1000) {
//codeceptjs.event.emit(codeceptjs.event.test.failed, self, 'js file is too big!');
//codeceptjs.recorder.throw('js file is too big!')
//codeceptjs.recorder.stop();
//throw new Error('js file is too big!')
}
})
I.amOnPage("https://www.github.com");
i first add response listener, then i goto "github", when some js file size is too big,i will throw out an error,in order too check content size is correctly.
however, even i throw error out (like the comments codes did), the main test flow just not stop, how do i do is the right way?
well,i found a solution later
i recorded all the page response into a custom object in the page instance.
later i wrote a help methods to check whole records.
//in helper.js
startRecordResponse() {
const helper = this.helpers['Puppeteer'];
helper.page.on("response", (res) => {
//record all response instance into savedResponse object inside page, we'll use it later
helper.page.savedResponse = helper.page.savedResponse || {};
helper.page.savedResponse[res.url()] = res;
});
return helper._waitForAction();
}
checkFileIsTooBig(filter, sizeLimit) {
const helper = this.helpers['Puppeteer'];
//use the data recorded in savedResponse object
Object.keys(helper.page.savedResponse).forEach((url) => {
var res = helper.page.savedResponse[url];
if (((filter instanceof RegExp && filter.test(url)) || (typeof filter == "string" && url.indexOf(filter) != -1)) && res.headers()['content-length'] > sizeLimit) {
throw new Error(`file ${url} is too big,${res.headers()['content-length']} > ${sizeLimit}`)
}
})
return helper._waitForAction();
}
then in test file
Before((I) => {
I.startRecordResponse();
I.amOnPage("https://www.github.com");
});
Scenario('github_test', (I) => {
//check a js file contain github is less than 100 bytes
I.checkFileIsTooBig(/github.*\.js/,100);
}
);
I would like to handle asynchronous stream when using Observable in Angular 2.
In detail, before every user's request, I need to get service ticket, if that ticket is valid, user can get proper response. Therefore, I have to do http request first before actual request, as below, I call the method called getServiceTicket(), however, because of asynchronous stream, before I get the valid service ticket, the following http request (getDetail) is performed without valid service ticket. So I tried to use flag like isServiceTicket, but I realize it does not guarantee the sequential running of these two methods. I tried to do some research, but I could not find satisfactory answer. If someone has good solution for this, could you give some advice?
getServiceTicket() {
this.userAuthServie.getServiceTicket().subscribe(
(data: string) => {this.serviceTicket = data; this.isServiceTicket = true;}
);
}
getDetail(id: string) {
this.getServiceTicket();
return this.http.get('https://localhost/detail/' + id + '?ticket=' + this.serviceTicket)
.map( (responseData) => {
return <User>responseData.json();
}
);
}
You can change yor code like below:
user:User;
getDetail(id: string) {
this.userAuthServie.getServiceTicket()
.subscribe((data: string) => {
this.serviceTicket = data;
this.isServiceTicket = true;
this.http.get('https://localhost/detail/' + id + '?ticket=' + this.serviceTicket)
.map( (responseData) => {
this.user= <User>responseData.json();
});
});
}
You can place the second function that you want to run in the success part of the subscribe method of the first function.
getServiceTicket() {
this.userAuthServie.getServiceTicket().subscribe(
(data: string) => {
this.serviceTicket = data; this.isServiceTicket = true;
this.getDetail(id);
}
);
}
If you want to wait for the service ticket to be loaded before any "detail requests" are performed, you should wait on the response of service ticket to resolve. There are multiple ways, one way might be to add the details to a queue, or have getDetails wait until the service ticket is loaded.
getServiceTicket() {
// you need to return the observable from service ticket
return this.userAuthServie
.getServiceTicket()
// use map here to only intercept the value.
// remember that you need to subscribe somewhere else
// if called from elsewhere than getDetail
.map((data: string) => {
this.serviceTicket = data;
this.isServiceTicket = true;
return data;
});
}
getDetail(id: string) {
return this.getServiceTicket()
// we switch the observable from ticket to the one from detail using switchMap
.switchMap((data: string) => {
let url = 'https://localhost/detail/' + id + '?ticket=' + this.serviceTicket;
return this.http
.get(url)
.map((responseData) => {
return <User>responseData.json();
});
});
}
}
Could anyone please help me to retreive the authenticated user who initiated a REST call? I'm using Lift with the RestHelper
In my Boot.scala I have the following:
LiftRules.httpAuthProtectedResource.prepend {
case Req(List("api", "incomingCall", incomingCall), _, GetRequest) => Full(AuthRole("admin"))
}
LiftRules.authentication = HttpBasicAuthentication("lift") {
case (username, password, req) => {
User.find(By(User.firstName, username)) match {
case Full(user) if user.password.match_?(password) => {
userRoles(AuthRole("admin"))
User.logUserIn(user) //I tried with and without this line
true
}
case x => {
false
}
}
}
}
LiftRules.dispatch.append(IncomingCallRest)
And my IncomingCallRest.scala file looks like this:
object IncomingCallRest extends RestHelper {
serve {
case "api" :: "incomingCall" :: incomingCall :: _ JsonGet _ => {
val currentUser = User.currentUser openOr User; //<--- On this line I'm trying to access the User, but it returns a blank user
val messageWithUser = (incomingCall, currentUser.userIdAsString)
ChatServer ! messageWithUser
JString(incomingCall)
}
}
}
User.currentUser does not return the authenticated user.
As you might see my code is based on the ChatServer example. I'm making the same call to User.currentUser from ChatIn.scala and there it works.
Any suggestions?
The creator of Lift suggested the following in an old thread:
Sessions are not initialized this early in the HTTP request/response
cycle. However, RequestVars are. My suggestion is to put User into a
RequestVar and then in your API module, read the RequestVar and put it into
a SessionVar.
I changed my code as follows to implement his suggestions:
//IncomingCallRest.scala
object userIdRequestVar extends RequestVar[String]("Default") //This RequestVar is set in Boot.scala
object IncomingCallRest extends RestHelper {
serve {
case "api" :: "incomingCall" :: incomingCall :: _ JsonGet _ => {
val messageWithUser = (incomingCall, userIdRequestVar.is)
ChatServer ! messageWithUser
JString(incomingCall)
}
}
}
//Boot.scala
LiftRules.authentication = HttpBasicAuthentication("lift") {
case (username, password, req) => {
User.find(By(User.firstName, username)) match {
case Full(user) if user.password.match_?(password) => {
userRoles(AuthRole("admin"))
userIdRequestVar.set(user.userIdAsString) //Set the RequestVar
true
}
case x => {
false
}
}
}
}