unable to understand why validateOpt will return `None` instead of `JsError` - playframework-2.6

In the following code, I am unable to understand why validateOpt might return value JsSuccess(None) instead of JsError
def getQuestion = silhouette.UserAwareAction.async{
implicit request => {
val body: AnyContent = request.body
val jsonBodyOption: Option[JsValue] = body.asJson
jsonBodyOption.map((jsonBody:JsValue) => { //body is json
val personJsonJsResultOption = jsonBody.validateOpt[Person]//check that json structure is correct
personJsonJsResultOption match {
case personSuccessOption: JsSuccess[Option[Person]] => { //json is correct
val personOption = personSuccessOption.getOrElse(None) //why would getOrElse return None??
personOption match {
case Some(person) => {
... }
case None =>{ //I am not sure when this will be triggered.
...
}
}
}
}
case e: JsError => {
...
}
}
}
})
.getOrElse(//body is not json
...)
}
}

validateOpt by design considers success to be not only when body provides actual Person but also when Person is null or not provided. Note how documentation explains why JsSuccess(None) is returned:
/**
* If this result contains `JsNull` or is undefined, returns `JsSuccess(None)`.
* Otherwise returns the result of validating as an `A` and wrapping the result in a `Some`.
*/
def validateOpt[A](implicit rds: Reads[A]): JsResult[Option[A]]
Seems like your requirement is that Person must always be provided to be considered successful, so in this case validate should be used instead of validateOpt.

Related

Angular Material Data Table - How To Setup filterPredicate For A Column With Type Ahead / Auto Complete Search?

I've read the various implementations of filterPredicate on SO, Github, etc but they aren't helpful for me to understand what to do with type ahead searches.
I enter a letter into an input form field, say p, and I receive all the data with last names starting with p from the db. That part of my setup works fine. However, I don't want to hit the db again when I type the next letter, say r. I want to filter the data table for last names starting with pr. This is where the trouble starts.
When I type the second letter I have an if/else statement that tests if the var I'm using has >1 in the string. When it does I pass params to a function for the custom filtering on the table with the data already downloaded from the db. I'm avoiding a db call with every letter, which does work. I don't understand "(data, filter)". They seem like params but aren't. How do they work? What code is needed to finish this?
(I have `dataSource.filter = filterValue; working fine elsewhere.)
Params explained:
column = user_name
filterValue = pr...
The confusion:
public filterColumn(column: string, filterValue: string, dataSource) {
dataSource.filterPredicate = (data, filter) => {
console.log('data in filter column: ', data); // Never called.
// What goes here?
// return ???;
}
}
My dataSource object. I see filterPredicate, data, and filter properties to work with. Rather abstract how to use them.
dataSource in filterColumn: MatTableDataSource {_renderData: BehaviorSubject, _filter: BehaviorSubject, _internalPageChanges: Subject, _renderChangesSubscription: Subscriber, sortingDataAccessor: ƒ, …}
filterPredicate: (data, filter) => {…}arguments: [Exception: TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
at Function.invokeGetter (<anonymous>:2:14)]caller: (...)length: 2name: ""__proto__: ƒ ()[[FunctionLocation]]: data-utilities.service.ts:43[[Scopes]]: Scopes[3]
filteredData: (3) [{…}, {…}, {…}]
sortData: (data, sort) => {…}
sortingDataAccessor: (data, sortHeaderId) => {…}
_data: BehaviorSubject {_isScalar: false, observers: Array(1), closed: false, isStopped: false, hasError: false, …}
_filter: BehaviorSubject {_isScalar: false, observers: Array(1), closed: false, isStopped: false, hasError: false, …}
_internalPageChanges: Subject {_isScalar: false, observers: Array(1), closed: false, isStopped: false, hasError: false, …}
_paginator: MatPaginator {_isInitialized: true, _pendingSubscribers: null, initialized: Observable, _disabled: false, _intl: MatPaginatorIntl, …}
_renderChangesSubscription: Subscriber {closed: false, _parentOrParents: null, _subscriptions: Array(1), syncErrorValue: null, syncErrorThrown: false, …}
_renderData: BehaviorSubject {_isScalar: false, observers: Array(1), closed: false, isStopped: false, hasError: false, …}data: (...)filter: (...)paginator: (...)sort: (...)__proto__: DataSource
I've included most of the component I made in Angular for typeahead search. The guts of the typeahead code is in the utilities shared component at the bottom. I used a shared component here because I'll use this in many places. However, I think it is a hack and a more elegant answer is possible. This works, it is easy, but not all that pretty. I can't afford more time to figure out pretty now. I suspect the answer is in RegEx.
In the typeahead.compoent in the .pipe you'll find how I call the code in the utility.
This code is in a shared component typeahead.component.ts
public searchLastName$ = new Subject<string>(); // Binds to the html text box element.
ngAfterViewInit() {
// -------- For Column Incremental Queries --------- //
// searchLastName$ binds to the html element.
this.searchLastName$.subscribe(result => {
this.queryLastName(result);
});
}
// --------- LAST NAME INCREMENTAL QUERY --------------- //
private queryLastName(filterValue) {
// Custom filter for this function. If in ngOnInit on the calling component then it applies
// to the whole calling component. We need various filters so that doesn't work.
this.membersComponent.dataSource.filterPredicate = (data: { last_name: string }, filterValue: string) =>
data.last_name.trim().toLowerCase().indexOf(filterValue) !== -1;
// When the first letter is typed then get data from db. After that just filter the table.
if (filterValue.length === 1) {
filterValue = filterValue.trim(); // Remove whitespace
// filterValue = filterValue.toUpperCase(); // MatTableDataSource defaults to lowercase matches
const lastNameSearch = gql`
query ($last_name: String!) {
lastNameSearch(last_name: $last_name) {
...membersTableFrag
}
}
${membersTableFrag}
`;
this.apollo
.watchQuery({
query: lastNameSearch,
variables: {
last_name: filterValue,
},
})
.valueChanges
.pipe(
map(returnedArray => {
// console.log('returnedArray in map: ', returnedArray); // All last_name's with the letter in them someplace.
const membersArray = returnedArray.data['lastNameSearch']; // extract items array from GraphQL JSON array
// For case insensitive search
const newArray = membersArray.filter(this.utilitiesService.filterBy(filterValue, 'last_name'));
return newArray;
})
)
.subscribe(result => {
this.membersComponent.dataSource.data = result;
});
} else {
// Filter the table instead of calling the db for each letter entered.
// Note: Apollo client doesn't seem able to query the cache with this kind of search.
filterValue = filterValue.trim(); // Remove whitespace
filterValue = filterValue.toLowerCase(); // MatTableDataSource defaults to lowercase matches
// Interface and redefinition of filterPredicate in the ngOnInit
this.membersComponent.dataSource.filter = filterValue; // Filters all columns unless modifed by filterPredicate.
}
}
utilities.service.ts
// -------------- DATABASE COLUMN SEARCH -------------
// Shared with other components with tables.
// For case insensitive search.
// THIS NEEDS TO BE CLEANED UP BUT I'M MOVING ON, MAYBE LATER
public filterBy = (filterValue, column) => {
return (item) => {
const charTest = item[column].charAt(0);
if (charTest === filterValue.toLowerCase()) {
return true;
} else if (charTest === filterValue.toUpperCase()) {
return true;
} else {
return false;
}
}
};

How to make angular2 custom validators to run after we get the value

If I pass hard coded values in offerCheck validator it is working fine. But if I get values from api, null values is getting passed in paramets. Form is getting executed before we get the values from service. Please help me to make validate check after getting values from api.
this.newOffer = "aaa";
this.oldOffer = "aaa";
constructor(fb: FormBuilder) {
this.formGroup = fb.group({
'offer': [null, Validators.compose([Validators.required, this.offerCheck(newOfer, oldOffer)])],
})
offerCheck(new, old) {
return (control: FormControl) => {
if (new == old) {
return true;
}
}
}
What you want is probably the AsyncValidatorFn, here's a very simple example of how to create one:
export const OfferCheck: AsyncValidatorFn = (control: AbstractControl): Observable<boolean> => {
if (new == old) {
return Observable.of(this.http.get('/some-endpoint').first().map(res => res.data));
}
};
You don't provide enough information so this is just a guess on how you'd want it to be. But it should point you in the right decision.
An alternative method would be to use setValidators of the control(s) after you've fetched the data:
this.formGroup.get('offer').setValidators([Validators.required, this.offerCheck(newOfer, oldOffer)]);
I hope this helps.

Invalidate Falcor jsonGraph fragment using jsonGraphEnvelope

I'm trying to invalidate a part of my jsonGraph object via the response from the falcor-router after making a CREATE call. I can successfully do so when returning a list of pathValues, similar to this earlier SE question:
{
route: 'foldersById[{keys:ids}].folders.createSubFolder',
call(callPath, args, refPaths, thisPaths) {
return createNewFolderSomehow(...)
.subscribe(folder => {
const folderPathValue = {
path: ['foldersById', folder.parentId, 'folders', folder.parentSubFolderCount -1],
value: $ref(['foldersById', folder.id])
};
const folderCollectionLengthPathValue = {
path: ['folderList', 'length'],
invalidated: true
};
return [folderPathValue, folderCollectionLengthPathValue];
});
})
}
However, when returning the equivalent (afaik) jsonGraphEnvelope, the invalidated path is dropped from the response:
{
route: 'foldersById[{keys:ids}].folders.createSubFolder',
call(callPath, args, refPaths, thisPaths) {
return createNewFolderSomehow(...)
.subscribe(folder => {
const newFolderPath = ['foldersById', folder.parentId, 'folders', folder.parentSubFolderCount -1];
return {
jsonGraph: R.assocPath(folderPath, $ref(['foldersById', folder.id]), {})
paths: [newFolderPath],
invalidated: [['folderList', 'length']]
};
});
})
}
Am I misunderstanding how a jsonGraphEnvelope works (had assumed it was a longhand format equivalent to an array of PathValues)? Or is this likely a bug?
Looks like a bug to me.
Invalidations don't seem to be handled in the part of the code responsible for merging partial JSONGraph envelopes returned from routes into the JSONGraph envelope response (see here), while they are handled in the path-value merge (see here).
I can't find any issue about this on GitHub so I invite you to open one.

Parsing Tweets with akka-streams

Im using the following code to connect to Twitter and get Tweets, however Im not able to create JsValue. If only map with byteString.utf8String I can see the strings. But when I add framing i get an error:
Read 7925 bytes which is more than 1000 without seeing a line terminator
No matter how long i make the input im still getting this error. What do I need to change to get a stream of JsValue in my websocket?
Information about how one is expected to consume twitter streams can be found here
#Singleton
class TwitterController #Inject() (ws: WSClient, conf: Configuration) {
def tweets = WebSocket.accept[JsValue,JsValue] { implicit request =>
Flow.fromSinkAndSource(Sink.ignore, queryToSource("cat"))
}
def queryToSource(keyword: String): Source[JsValue, NotUsed] = {
val url = "https://stream.twitter.com/1.1/statuses/filter.json"
credentials.map { case (consumerKey, requestToken) =>
val request = ws.url(url)
.sign(OAuthCalculator(consumerKey, requestToken))
.withQueryString("track" -> keyword)
.withMethod("GET")
streamResponse(request)
.via(Framing.delimiter(ByteString("\r\n"), maximumFrameLength = 1000, allowTruncation = true))
.map { byteString =>
Json.parse(byteString.utf8String)
// byteString.utf8String
}
} getOrElse {
Source.single(Json.parse("Twitter credentials missing"))
}
}
private def streamResponse(request:WSRequest): Source[ByteString, NotUsed] = Source.fromFuture(request.stream()).flatMapConcat(_.body)
lazy val credentials: Option[(ConsumerKey, RequestToken)] = for {
apiKey <- conf.getString("twitter.apiKey")
apiSecret <- conf.getString("twitter.apiSecret")
token <- conf.getString("twitter.token")
tokenSecret <- conf.getString("twitter.tokenSecret")
} yield (
ConsumerKey(apiKey, apiSecret),
RequestToken(token, tokenSecret)
)
}

How to set status code and headers in spray-routing based on a future result

I'm using spray-routing with Akka to define a route like
def items = path("items") {
get {
complete {
actor.ask(GetItems)(requestTimeout).mapTo[Either[NoChange, Items]] map {
result => result match {
case Left(_) => StatusCodes.NotModified
case Right(items) =>
// here I want to set an HTTP Response header based on a
// field within items -- items.revision
items
}
}
}
}
}
The actor.ask returns a Future that gets mapped to a Future[Either[NoChange, Items]]. "complete" is happy to deal with the Future[StatusCodes...] or the Future[Items] but I'm not sure how to set an HTTP Response header within the Future.
If the header weren't being set within the Future then I could just wrap the complete in a directive but how do I set a header within the complete?
I'm using Spray 1.2.0.
Thanks for any pointers in the right direction!
If you are trying to do this inside of complete all branches of the expression inside must result in a type that can be marshalled by complete.
You could try a structure like this to make it work:
complete {
actor.ask(GetItems)(requestTimeout).mapTo[Either[NoChange, Items]] map {
result => result match {
case Left(_) => StatusCodes.NotModified: ToResponseMarshallable
case Right(items) =>
// here I want to set an HTTP Response header based on a
// field within items -- items.revision
val headers = // items...
HttpResponse(..., headers = headers): ToResponseMarshallable
}
}
}
This ensures that the type of the expression you pass to complete is Future[ToResponseMarshallable] which should always be marshallable.
A better way, though, is to use the onSuccess directive that lets you use other directives after a future was completed:
get {
def getResult() = actor.ask(GetItems)(requestTimeout).mapTo[Either[NoChange, Items]]
onSuccess(getResult()) {
case Left(_) => complete(StatusCodes.NotModified)
case Right(items) =>
// do whatever you want, e.g.
val extraHeaders = // items.revisions
respondWithHeaders(extraHeaders) {
complete(...)
}
}
}

Resources