Yup context typescript error: Property 'originalValue' does not exist on type 'TestContext<AnyObject>' - react-hook-form

Yup is causing an Typescript error for not typing context values:
Property 'originalValue' does not exist on type 'TestContext<AnyObject>'.
When using yup function test I get a typescript error for originalValue. What should I type context as to remove this error ?
.test('00s', 'not a valid number', (value, context) => {
return context.originalValue.match(ssnRegex)
}),

You can workaround by typecasting context with an extended interface see : https://github.com/DefinitelyTyped/DefinitelyTyped/issues/49512
interface TestContextExtended {
originalValue?: unknown;
}
and in your test :
.test('00s', 'not a valid number', (value, context) => {
const { originalValue } = context as yup.TestContext & TestContextExtended;
if (typeof originalValue === 'string') {
return originalValue.match(ssnRegex);
}
return false;
}),

Related

Use regular PropTypes checks inside a custom validator

I started out with a very simple PropTypes validation:
name: PropTypes.string.isRequired,
Now I need to only have string be required if a certain other condition is false, so:
name: ({name, otherProp}) => {
if (otherProp === 'foo') return null;
if (typeof name === 'string') return null;
return new Error('Error');
}
Which is fine in this case, but what I'd really like to be able to do after the first check is run the regular PropTypes check for string.isRequired inside my custom validator, like maybe:
name: ({name, otherProp}) => {
if (otherProp === 'foo') return null;
return PropTypes.string.isRequired;
}
It seems like there should be a way to do this with PropTypes.checkPropTypes, but I can't figure it out.
I discovered while poking around the code for Material UI a clever way to do something equivalent to this: parallel prop-types checks. They have a very simple utility function chainPropTypes which goes like this:
export default function chainPropTypes(propType1, propType2) {
if (process.env.NODE_ENV === 'production') {
return () => null;
}
return function validate(...args) {
return propType1(...args) || propType2(...args);
};
}
You use this to do a custom prop types check along with a standard check. For example like this:
anchorEl: chainPropTypes(PropTypes.oneOfType([HTMLElementType, PropTypes.func]), (props) => {
if (props.open && (!props.anchorReference || props.anchorReference === 'anchorEl')) {
// ...
You can write your own custom PropType which can take a PropType as an argument.
export const myCustomPropType = (propType, otherProp) => {
return function validate(props, propName, ...rest) {
const originalPropType = propType(props, propName, ...rest);
if (props[otherProp] === 'foo') return originalPropType;
if (typeof props[propName] === 'string') return originalPropType;
return new Error('Error');
};
};
Usage
MyComponent.propTypes {
myProp: myCustomPropType(PropTypes.string.isRequired, 'otherPropName')
}

'String' is not a subtype of type 'int' in flutter

Currently I'm learning Flutter and I have an error during the execution of a project that I can't find. Maybe someone has an idea where the error could be?
This is the error:
[VERBOSE-2:ui_dart_state.cc(157)] Unhandled Exception: type 'String' is not a subtype of type 'int' of 'index'
This is the function in which the error is located:
Future<void> fetchAndSetProducts([bool filterByUser = false]) async {
final filterString = filterByUser ? 'orderBy="creatorId"&equalTo="$userId"' : '';
var url =
'https://xxxx.firebaseio.com/products.json?auth=$authToken&$filterString';
try {
final response = await http.get(url);
final extractedData = json.decode(response.body) as Map<String, dynamic>;
if (extractedData == null) {
return;
}
url =
'https://xxxx.firebaseio.com/userFavorites/$userId.json?auth=$authToken';
final favoriteResponse = await http.get(url);
final favoriteData = json.decode(favoriteResponse.body);
final List<Product> loadedProducts = [];
extractedData.forEach((prodId, prodData) {
loadedProducts.add(Product(
id: prodId,
title: prodData['title'],
description: prodData['description'],
price: prodData['price'],
isFavorite:
favoriteData == null ? false : favoriteData[prodId] ?? false,
imageUrl: prodData['imageUrl'],
));
});
_items = loadedProducts;
notifyListeners();
} catch (error) {
throw (error);
}
}

Receiving returned data from firebase callable functions

I'm playing with Callable HTTPS-functions in iOS. I've created and deployed the following function:
export const generateLoginToken = functions.https.onCall((data, context) => {
const uid = data.user_id
if (!(typeof uid === 'string') || uid.length === 0) {
throw new functions.https.HttpsError('invalid-argument', 'The function must be called with one argument "user_id" ');
}
admin.auth().createCustomToken(uid)
.then((token) => {
console.log("Did create custom token:", token)
return { text: "some_data" };
}).catch((error) => {
console.log("Error creating custom token:", error)
throw new functions.https.HttpsError('internal', 'createCustomToken(uid) has failed for some reason')
})
})
Then I call the function from my iOS-app like this:
let callParameters = ["user_id": userId]
self?.functions.httpsCallable("generateLoginToken").call(callParameters) { [weak self] (result, error) in
if let localError = self?.makeCallableFunctionError(error) {
single(SingleEvent.error(localError))
} else {
print("Result", result)
print("data", result?.data)
if let text = (result?.data as? [String: Any])?["text"] as? String {
single(SingleEvent.success(text))
} else {
let error = NSError.init(domain: "CallableFunctionError", code: 3, userInfo: ["info": "didn't find custom access token in the returned result"])
single(SingleEvent.error(error))
}
}
}
I can see on the logs that the function is invoked on the server with the right parameters, but I can't seem to the get data that is being returned from the function back into the app. It seems that the result.data value is nilfor some reason, even though I return {text: "some_data"} from the cloud function. How come?
Yikes! The issue was that I forgot to return the actual promise from the cloud function. This function is working:
export const generateLoginToken = functions.https.onCall((data, context) => {
const uid = data.user_id
if (!(typeof uid === 'string') || uid.length === 0) {
throw new functions.https.HttpsError('invalid-argument', 'The function must be called with one argument "user_id" ');
}
return admin.auth().createCustomToken(uid)
.then((token) => {
console.log("Did create custom token:", token)
return { text: "some_data" };
}).catch((error) => {
console.log("Error creating custom token:", error)
throw new functions.https.HttpsError('internal', 'createCustomToken(uid) has failed for some reason')
})
})

How to handle TypeORM entity field unique validation error in NestJS?

I've set a custom unique validator decorator on my TypeORM entity field email. NestJS has dependency injection, but the service is not injected.
The error is:
TypeError: Cannot read property 'findByEmail' of undefined
Any help on implementing a custom email validator?
user.entity.ts:
#Column()
#Validate(CustomEmail, {
message: "Title is too short or long!"
})
#IsEmail()
email: string;
My CustomEmail validator is
import {ValidatorConstraint, ValidatorConstraintInterface,
ValidationArguments} from "class-validator";
import {UserService} from "./user.service";
#ValidatorConstraint({ name: "customText", async: true })
export class CustomEmail implements ValidatorConstraintInterface {
constructor(private userService: UserService) {}
async validate(text: string, args: ValidationArguments) {
const user = await this.userService.findByEmail(text);
return !user;
}
defaultMessage(args: ValidationArguments) {
return "Text ($value) is too short or too long!";
}
}
I know I could set unique in the Column options
#Column({
unique: true
})
but this throws a mysql error and the ExceptionsHandler that crashes my app, so I can't handle it myself...
Thankx!
I can propose 2 different approaches here, the first one catches the constraint violation error locally without additional request, and the second one uses a global error filter, catching such errors in the entire application. I personally use the latter.
Local no-db request solution
No need to make additional database request. You can catch the error violating the unique constraint and throw any HttpException you want to the client. In users.service.ts:
public create(newUser: Partial<UserEntity>): Promise<UserEntity> {
return this.usersRepository.save(newUser).catch((e) => {
if (/(email)[\s\S]+(already exists)/.test(e.detail)) {
throw new BadRequestException(
'Account with this email already exists.',
);
}
return e;
});
}
Which will return:
Global error filter solution
Or even create a global QueryErrorFilter:
#Catch(QueryFailedError)
export class QueryErrorFilter extends BaseExceptionFilter {
public catch(exception: any, host: ArgumentsHost): any {
const detail = exception.detail;
if (typeof detail === 'string' && detail.includes('already exists')) {
const messageStart = exception.table.split('_').join(' ') + ' with';
throw new BadRequestException(
exception.detail.replace('Key', messageStart),
);
}
return super.catch(exception, host);
}
}
Then in main.ts:
async function bootstrap() {
const app = await NestFactory.create(/**/);
/* ... */
const { httpAdapter } = app.get(HttpAdapterHost);
app.useGlobalFilters(new QueryErrorFilter(httpAdapter));
/* ... */
await app.listen(3000);
}
bootstrap();
This will give generic $table entity with ($field)=($value) already exists. error message. Example:
I have modified my code. I am checking the uniqueness of username/email in the user service (instead of a custom validator) and return an HttpExcetion in case the user is already inserted in the DB.
The easiest solution!
#Entity()
export class MyEntity extends BaseEntity{
#Column({unique:true}) name:string;
}
export abstract class BaseDataService<T> {
constructor(protected readonly repo: Repository<T>) {}
private async isUnique(t: any) {
const uniqueColumns = this.repo.metadata.uniques.map(
(e) => e.givenColumnNames[0]
);
for (const u of uniqueColumns) {
const count = await this.repo.count({ where: { [u]: ILike(t[u]) } });
if (count > 0) {
throw new UnprocessableEntityException(`${u} must be unique!`);
}
}
}
async save(body: DeepPartial<T>) {
await this.isUnique(body);
try {
return await this.repo.save(body);
} catch (err) {
throw new UnprocessableEntityException(err.message);
}
}
async update(id: number, updated: QueryDeepPartialEntity<T>) {
await this.isUnique(updated)
try {
return await this.repo.update(id, updated);
} catch (err) {
throw new UnprocessableEntityException(err.message);
}
}
}
An approach that works for modern version of NestJS which is based in Daniel Kucal's answer and actually returns the error to the frontend when calling the JSON API is the following:
import {
Catch,
ArgumentsHost,
BadRequestException,
HttpException,
} from '#nestjs/common';
import { BaseExceptionFilter } from '#nestjs/core';
import { QueryFailedError } from 'typeorm';
type ExceptionType = { detail: string; table: string };
#Catch(QueryFailedError)
export class QueryErrorFilter extends BaseExceptionFilter<
HttpException | ExceptionType
> {
public catch(exception: ExceptionType, host: ArgumentsHost): void {
const { detail = null } = exception || {};
if (
!detail ||
typeof detail !== 'string' ||
// deepcode ignore AttrAccessOnNull: <False positive>
!detail.includes('already exists')
) {
return super.catch(exception, host);
} // else
/**
* this regex transform the message `(phone)=(123)` to a more intuitive `with phone: "123"` one,
* the regex is long to prevent mistakes if the value itself is ()=(), for example, (phone)=(()=())
*/
const extractMessageRegex =
/\((.*?)(?:(?:\)=\()(?!.*(\))(?!.*\))=\()(.*?)\)(?!.*\)))(?!.*(?:\)=\()(?!.*\)=\()((.*?)\))(?!.*\)))/;
const messageStart = `${exception.table.split('_').join(' ')} with`;
/** prevent Regex DoS, doesn't treat messages longer than 200 characters */
const exceptionDetail =
exception.detail.length <= 200
? exception.detail.replace(extractMessageRegex, 'with $1: "$3"')
: exception.detail;
super.catch(
new BadRequestException(exceptionDetail.replace('Key', messageStart)),
host,
);
}
}
Also, not forgetting main.ts:
async function bootstrap() {
const app = await NestFactory.create(/**/);
/* ... */
const { httpAdapter } = app.get(HttpAdapterHost);
app.useGlobalFilters(new QueryErrorFilter(httpAdapter));
/* ... */
await app.listen(3000);
}
bootstrap();

Obscure Dart syntax

While working through the the Dart Route library example for client side coding I came across this snippet.
var router = new Router()
..addHandler(urls.one, showOne)
..addHandler(urls.two, showTwo)
..addHandler(urls.home, (_) => null)
..listen();
My question is how does (_) => null work? It seems to specify a function that returns a null value but what does (_) mean?
(_) means it is a function with one parameter but you don't care about that parameter, so it's just named _. You could also write (ignoreMe) => null. The important thing here is, that there needs to be a function that accepts one parameter. What you do with it, is your thing.
(_) => null means : a function that take one parameter named _ and returning null. It could be seen as a shortcut for (iDontCareVariable) => null.
A similar function with no parameter would be () => null.
A similar function with more parameters would be (_, __, ___) => null.
Note that _ is not a special syntax defined at langauge level. It is just a variable name that can be used inside the function body. As example : (_) => _.
I will try explain this by the example.
void main() {
var helloFromTokyo = (name) => 'こんにちわ $name';
var greet = new Greet();
greet.addGreet('London', helloFromLondon)
..addGreet('Tokyo', helloFromTokyo)
..addGreet('Berlin', helloFromBerlin)
..addGreet('Mars', (_) => null)
..addGreet('Me', (name) => 'Privet, chuvak! You name is $name?')
..addGreet('Moon', null);
greet.greet('Vasya Pupkin');
}
String helloFromLondon(String name) {
return 'Hello, $name';
}
String helloFromBerlin(String name) {
return 'Guten tag, $name';
}
class Greet {
Map<String, Function> greets = new Map<String, Function>();
Greet addGreet(String whence, String sayHello(String name)) {
greets[whence] = sayHello;
return this;
}
void greet(String name) {
for(var whence in greets.keys) {
var action = greets[whence];
if(action == null) {
print('From $whence: no reaction');
} else {
var result = action(name);
if(result == null) {
print('From $whence: silent');
} else {
print('From $whence: $result');
}
}
}
}
}
Output:
From London: Hello, Vasya Pupkin
From Tokyo: こんにちわ Vasya Pupkin
From Berlin: Guten tag, Vasya Pupkin
From Mars: silent
From Me: Privet, chuvak! You name is Vasya Pupkin?
From Moon: no reaction

Resources