optional parameter in Angular service - angular-services

I have this service in my Angular Project
getActividades(usuarioId: number, año: number, mes: number, quincena?: number): Observable<IActividad[]> {
return this.http.get<IActividad[]>(`${this.urlWebApi}/empleados/${usuarioId}/actividades?year=${año}&mes=${mes}&quincena=${quincena}`)
.pipe(
//tap(data => console.log('Listado Empleados', JSON.stringify(data))),
tap(data => console.log('Listado actividades', data)),
catchError(this.handleError)
);
}
where the last parameter quincena is optional, but this call gets me an error
Any idea, please?

Related

Why I'm getting Validation failed (numeric string is expected)

I have this code
#Get()
#ApiQuery({
name: "code",
type: String,
required: false,
})
#ApiQuery({
name: "id",
type: Number,
required: false,
})
async read(
#Query("entity") entity: string,
#Query("code") code: string,
#Query("id", ParseIntPipe) id: number
): Promise<Q> {
return this.service.readAsync({ where: { codigo: code, id: id } });
}
Why I'm getting Validation failed (numeric string is expected) when I request to http://localhost:3000/api/v1/endpoint?entity=a&code=b
I know is related with id param, but I don't know how to solve this.
I want to be able to use code or id params according my needs.
If I request to http://localhost:3000/api/v1/endpoint?entity=a&code=b&id=1 or http://localhost:3000/api/v1/endpoint?entity=a&id=1 all is fine.
here #Query("id", ParseIntPipe) id: number you're saying that the query parameter id is required and must be an integer.
Thus, if you do GET /endpoint?entity=a&code=b, it will reply with bad request as there's no id parameter.
You can use the DefaultValuePipe pipe if id should be optional and will have a fallback value.
If you don't want any default value, then you'll need to write your own pipe (that could extends ParseIntPipe). Or you could use the builtin one ValidationPipe with class-validator decorators.
ParserIntPipe doesn't work on optional parameters, from its source code, you can see
async transform(value: string, metadata: ArgumentMetadata): Promise<number> {
if (!this.isNumeric(value)) {
throw this.exceptionFactory(
'Validation failed (numeric string is expected)',
);
}
return parseInt(value, 10);
}
/**
* #returns `true` if `value` is a valid integer number
*/
protected isNumeric(value: string): boolean {
return (
['string', 'number'].includes(typeof value) &&
/^-?\d+$/.test(value) &&
isFinite(value as any)
);
}
As per Micael Levi answer, you either provide a default value using DefaultValuePipe in case it was missing, or you build your own custom pipe that pass parameter undefined value

Difference between arg: number as function parameters, and Number(arg)

Following a NestJS tutorial, I encountered something that drew my attention (coming from a service.ts file):
public async putCarById(id: number, property_name: string, property_value: string)// : Promise<any>
{
console.log(id); // gives provided number, for example, 1
const carId = Number(id);
console.log(typeof (id)); // will output "string" - why ?
console.log(typeof (carId)); // will output "number" -- which I understand
(...)
I could not find why it was necessary to do the "cast" while id was supposed to be received as a number.

Wrong data from client passes GraphQL validation

I've made simple CRUD app with React and Apollo client on NestJS server with GraphQL API.
I have this simple Mutations:
schema.gql:
type Mutation {
createUser(input: CreateUserInput!): User! // CreateUserInput type you can see in user.input.ts below
updateUser(id: ID!, input: UpdateUserInput!): User!
deleteUser(id: ID!): User!
}
user.input.ts:
import { InputType, Field } from "#nestjs/graphql";
import { EmailScalar } from "../email.scalar-type";
#InputType()
export class CreateUserInput {
// EmailScalar is a custom Scalar GraphQL Type that i took from the internet and it worked well
#Field(() => EmailScalar)
readonly email: string;
#Field()
readonly name: string;
}
"EmailScalar" type checks if "email" input has *#*.* format basically
And when i make createUser Query to GraphQL API like this:
It cannot pass validation
(because Email type works fine)
But when Query sent from client - it passes validation:
NestJS server log (from code below)
users.resolver.ts:
#Mutation(() => User)
async createUser(#Args('input') input: CreateUserInput) { // Type from user.input.ts
Logger.log(input); // log from screenshot, so if it's here it passed validation
return this.usersService.create(input); // usersService makes requests to MongoDB
}
And it gets into MongoDB
Here is client side part:
App.tsx:
...
// CreateUserInput class is not imported to App.tsx (it is at server part) but it seems to be fine with it
const ADD_USER = gql`
mutation AddMutation($input: CreateUserInput!) {
createUser(input: $input) {
id
name
email
}
}
`
function App(props: any) {
const { loading, error, data } = useQuery(GET_USERS);
const [addUser] = useMutation(
ADD_USER,
{
update: (cache: any, { data: { createUser } }: any) => {
const { users } = cache.readQuery({ query: GET_USERS });
cache.writeQuery({
query: GET_USERS,
data: {
users: [createUser, ...users],
},
})
}
}
);
...
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
return <UserTable users={data.users} addUser={addUser} updateUser={updateUser} deleteUser={deleteUser} />;
}
Can someone please explain to me, how does client Query passes validation and what have i done wrong?
Even two empty strings can pass through.
Never worked with NestJS, Apollo, React or GraphQL before, so I'm kinda lost.
For full code:
https://github.com/N238635/nest-react-crud-test
This is how your custom scalar's methods are defined:
parseValue(value: string): string {
return value;
}
serialize(value: string): string {
return value;
}
parseLiteral(ast: ValueNode): string {
if (ast.kind !== Kind.STRING) {
throw new GraphQLError('Query error: Can only parse strings got a: ' + ast.kind, [ast]);
}
// Regex taken from: http://stackoverflow.com/a/46181/761555
var re = /^([\w-]+(?:\.[\w-]+)*)#((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
if (!re.test(ast.value)) {
throw new GraphQLError('Query error: Not a valid Email', [ast]);
}
return ast.value;
}
parseLiteral is called when parsing literal values inside the query (i.e. literal strings wrapped in double quotes). parseValue is called when parsing variable values. When your client sends the query, it sends the value as a variable, not as a literal value. So parseValue is used instead of parseLiteral. But your parseValue does not do any kind of validation -- you just return the value as-is. You need to implement the validation logic in both methods.
It would also be a good idea to implement the serialize method so that your scalar can be used for both input and response validation.

#IsPhoneNumber() npm class validator how to add multiple countries code

In Nest js dto I want to validate user mobile number with multiple countries Regex. How can I do this?
#IsPhoneNumber('IN', {
message: (args: ValidationArguments) => {
if (args.value.length !== 10) {
throw new BadRequestException(`${args.value} Wrong Phone Number`);
} else {
throw new InternalServerErrorException();
}
},
})
Different countries has different length of phone numbers. And my suggestion is to keep list of country codes instead of custom regex. It's easier to maintain, and it's more readable. So solution is:
parse phone number
if it's valid check country code
if it's valid pass to next built-in decorator
So I've created my own decorator with libphonenumber-js
Usage in DTO:
export class PhoneDto {
#ToPhone
#IsString({ message: 'must be a valid number' })
readonly phone!: string;
}
Implementation:
import { Transform } from 'class-transformer';
import { parsePhoneNumberFromString } from 'libphonenumber-js';
const validCountries = ['US', 'UK'];
export const ToPhone = Transform(
(value: any) => {
if (typeof value !== 'string') return undefined;
const parsed = parsePhoneNumberFromString(value);
if (!parsed) return undefined;
if (!validCountries.includes(parsed.country)) return undefined;
return parsed.number;
},
{ toClassOnly: true },
);
And yes, this solution adds one more library, it could be slower (actually it depends on your countries list) because of parsing, but as I said before It's more readable and maintainable.
Passing a restricted set of locations to #IsPhoneNumber(region: string) is currently not supported.
Your only chance is to pass "ZZ" as region which will force users to enter numbers with the intl. prefix (see docs)

How to write regex in a sequelize.js field validation.isIn.args

I had wrote two regex in isIn.args, i want the phone validator can check my input value is one of these, but failed.
var ValidateMe = sequelize.define('Foo', {
phone: {
type: Sequelize.STRING(20),
validate: {
isIn: {
args: [[
// moible number of china
/^(13|14|15|17|18)\d{9}$/i,
// telphone number of china
/^((\(\d{2,3}\))|(\d{3}\-)|(\d{3}))?(\(0\d{2,3}\)|0\d{2,3}-)?[1-9]\d{6,7}(\-\d{1,4})?$/i
]],
msg: "moible or telphone number format error!"
}
}
}
})
I want get the result:
var inputNumber = '15208282123'; // or '8008123'
( /^(13|14|15|17|18)\d{9}$/i.test(inputNumber)
|| /^((\(\d{2,3}\))|(\d{3}\-)|(\d{3}))?(\(0\d{2,3}\)|0\d{2,3}-)?[1-9]\d{6,7}(\-\d{1,4})?$/i.test(inputNumber)
) === true;
Run it in the browser console, result is true.
sequelize "isIn" validator doesn't provide regex support, it checks weather value is directly present in the list of args or not, instead "is" validator supports regex, but i don't think it will support more than one regex at a time, you need to either convert 2 reg ex into 1 or create custom validator which checks both the regex and return true if one of them is passed, something like below
var ValidateMe = sequelize.define('Foo', {
phone: {
type: Sequelize.STRING(20),
validate: {
validatePhone: function(value) {
if(!/^(13|14|15|17|18)\d{9}$/i.test(value) && !/^((\(\d{2,3}\))|(\d{3}\-)|(\d{3}))?(\(0\d{2,3}\)|0\d{2,3}-)?[1-9]\d{6,7}(\-\d{1,4})?$/i.test(value)) {
throw new Error('phone format error!')
}
}
}
}
})

Resources