Is there any way to implement validation for file upload using class-validator? - node.js

I am using class-validator for validate data, I need to implement validation for file upload. Ex: file is not empty (It would be great if also implement file must be image).
I try in following way:
export class FileModel extends Model {
#IsNotEmpty()
file: File
constructor(body: any) {
super();
const {
file,
} = body;
this.file = file;
}
}
But it's always return "file should not be empty" even I select file. is there any way to implement validation for file upload.
Thanks in advance :)

You can create a custom class-validator custom validation decorator:
interface IsFileOptions {
mime: ('image/jpg' | 'image/png' | 'image/jpeg')[];
}
export function IsFile(options: IsFileOptions, validationOptions?: ValidationOptions) {
return function (object: Object, propertyName: string) {
return registerDecorator({
name: 'isFile',
target: object.constructor,
propertyName: propertyName,
constraints: [],
options: validationOptions,
validator: {
validate(value: any, args: ValidationArguments) {
if (value?.mimetype && (options?.mime ?? []).includes(value?.mimetype)) {
return true;
}
return false;
},
}
});
}
}
The preceding custom decorator just checks the mime-type of the file. You can write a more sophisticated implementation and also add file size check and etc.
You can use the custom-decorator like this in your DTO classes:
class UploadImageDto{
#IsFile({ mime: ['image/jpg', 'image/png']})
file: any;
}
Furthermore if you are using class-validator in NestJs you can use nestjs-form-data library which contains #HasMimeType, #IsFile, #MaxFileSize and more file validation decorators out of the box.

Related

I'm trying to use a custom cookie service for my Node oidc authentication, but I don't know how to instantiate it with the needed parameters

This is a Node app with OIDC login. I want to store the JWT in the cookie.
I'm using version 12 of the library 'angular-auth-oidc-client'. I can't use a later version of this library.
The documentation says to set up your app.module.ts like this:
imports: [
/*...*/,
AuthModule.forRoot({
config: {
authority: ...,
redirectUrl: ...,
/*...*/,
storage: new MyCustomStorage()
}
})
So it uses that 'storage' parameter.
This works correctly if I use, for instance, localStorage in my new MyCustomStorage. But I want to use the cookie.
So, if I for instance make my MyCustomStorage like this:
import { CookieService } from 'ngx-cookie-service';
#Injectable({
providedIn: 'root',
})
export class MyCustomStorage implements AbstractSecurityStorage {
constructor(private cookieService: CookieService) {
}
remove(key: string): void {
throw new Error('Method not implemented.');
}
clear(): void {
throw new Error('Method not implemented.');
}
read(key: string) {
let item = this.cookieService.get(key);
if (!!item) {
return JSON.parse(item);
}
else {
return null;
}
}
write(key: string, value: any) {
value = value || null;
//Expiration time can be set in the third parameter of below function.
this.cookieService.set(`${key}`, JSON.stringify(value), undefined, undefined, undefined, true, 'Strict');
return true;
}
}
I believe this would work... however I now get an error in my app.module.ts, because now creating a new MyCustomStorage() requires a CookieService parameter to be injected.
But that CookieService is automatically injected into the MyCustomStorage usually...
Do I need to manually instantiate a new CookieService in the "storage: new MyCustomStorage()" line? If so, it asks for parameters for DOCUMENT and PLATFORM_ID and REQUEST. Do I have to instantiate those as well? If yes... how do I create a new document and platform id for this new CookieService()?

How to read properties in typescript after using Object.defineProperty?

I have the following code on typescript playground and a few questions come up that I am not sure how to get working
class PathInfo {
functionName: string;
httpPath: string;
httpMethod: string;
constructor(functionName: string, httpPath: string, httpMethod: string) {
this.functionName = functionName;
this.httpPath = httpPath;
this.httpMethod = httpMethod;
}
toString(): string {
return "PathInfo["+this.functionName+","+this.httpPath+","+this.httpMethod+"]";
}
}
class AuthRequest {}
class AuthResponse {}
class LoginRequest {}
class LoginResponse {}
const path: any = (thePath: string, type: any) => {
return (target: Function, memberName: string, propertyDescriptor: PropertyDescriptor) => {
const pathMeta = new PathInfo(memberName, path, type);
Object.defineProperty(target, memberName+'pathInfo', {
value: pathMeta,
writable: false
});
//How do I access the stored pathMeta
//console.log("target="+target.pathInfo);
console.log("member="+memberName);
console.log("props="+propertyDescriptor);
}
}
class AuthApiImpl {
#path("/authenticate", AuthResponse)
authenticate(request: AuthRequest): Promise<AuthResponse> {
throw new Error("all this is generated by factory.createApiImpl");
}
#path("/login", LoginResponse)
login(request: LoginRequest): Promise<LoginResponse> {
throw new Error("all this is generated by factory.createApiImpl");
}
};
function printMethods(obj: any) {
console.log("starting to print methods");
for (var id in obj) {
console.log("id="+id);
try {
//How do I access the stored pathMeta here FOR EACH METHOD ->
//console.log("target="+target.pathInfo);
if (typeof(obj[id]) == "function") {
console.log(id+":"+obj[id].toString());
}
} catch (err) {
console.log(id + ": inaccessible"+err);
}
}
}
console.log("starting to run")
const temp = new AuthApiImpl();
printMethods(temp);
console.log("done")
line 64-65, how to read the property that I set
line 40-41, how to read the property that I set
line 58-74, why is this not printing any functions? I want to print all functions and I do NOT want to print properties (just functions)
line 33, Can I access the class name at this point?
line 35, I thought target was a function and would be authorize, then login, BUT if I define the property as JUST 'pathInfo', I get an error that the property is already defined on the target(This implies the target is the class not the function?). I am so confused.
Terribly sorry as I try to focus on a single question, but this one test of writing decorators has given me more questions than answers as I delve into the typescript world.
How can I tweak the code to play more here?
A goal here is as developers define the APIs of other microservices, I can capture a bunch of meta information and store it SOMEWHERE I can use later in startup code. I do not care where I store that really, but just need a clean way of knowing the class I want to extend, the methods, the return types, the http path, etc.
How to get methods of a class
You still can't grab the method names even if you remove the decorator. This isn't a TypeScript specific question.
You need to get the properties of the prototype, not just the object itself.
function printMethods(obj: any) {
console.log("starting to print methods");
const objProto = Object.getPrototypeOf(obj);
console.log(Object.getOwnPropertyNames(objProto));
}
How to access class names
Don't think this is possible with decorators at the moment, but it should be straightforward to just pass in your class name as a string.
Similar issue: TypeScript class decorator get class name
Open issue on GitHub: https://github.com/microsoft/TypeScript/issues/1579
"property is already defined on the target"
Notice if you run the code above you get the following in console.log:
["constructor", "authenticate", "login", "authenticatepathInfo", "loginpathInfo"]
I also want to point out that if you don't even initialize an instance of the class, you'll still get the same error.
I want to read this meta data in nodejs and use that to dynamically create a client implementing the api. Basically, developers never have to write clients and only write the api and the implementation is generated for them.
If I were to do that, I'd probably not use decorators, but mapped types:
// library code
interface ApiMethodInfo {
httpPath: string;
httpMethod: string;
}
type ApiInfo<S extends object> = Record<keyof S, ApiMethodInfo>;
type Client<S extends object> = {[key in keyof S]: S[key] extends (req: infer Req) => infer Res ? (req: Req) => Promise<Res> : never};
function generateClient<S extends object>(apiInfo: ApiInfo<S>): Client<S> {
const client = {} as Client<S>;
for (const key in apiInfo) {
const info = apiInfo[key as keyof S];
client[key] = ((param: any) => invokeApi(info, param)) as any;
}
return client;
}
// application code
interface AuthRequest {}
interface AuthResponse {}
interface LoginRequest {
username: string,
password: string,
}
interface LoginResponse {}
interface MyServer {
authenticate(request: AuthRequest): AuthResponse;
login(request: LoginRequest): LoginResponse;
}
const myApiInfo: ApiInfo<MyServer> = { // compiler verifies that all methods of MyServer are described here
authenticate: {
httpPath: '/authenticate',
httpMethod: 'POST'
},
login: {
httpPath: '/login',
httpMethod: 'POST'
}
}
const myClient = generateClient(myApiInfo); // compiler derives the method signatures from the server implementation
const username = "joe";
const password = "secret";
const response = myClient.login({username, password}); // ... and can therefore check that this call is properly typed
(To understand how these type definitions work, you may want to read the section Creating Types from Types in the TypeScript Handbook)
The weakness of this approach is that while the compiler can derive the client signatures from the server signatures, it will not copy any JSDoc, so client devs can not easily access the API documentation.
In the above code, I chose to specify the metadata in a separate object rather than decorators so the compiler can check exhaustiveness (decorators are always optional; the compiler can not be instructed to require their presence), and because decorators are an experimental language feature that may still change in future releases of the language.
It is entirely possible to populate such a metadata object using decorators if that's what you prefer. Here's what that would look like:
// library code
interface ApiMethodInfo {
httpPath: string;
httpMethod: string;
}
const apiMethodInfo = Symbol("apiMethodInfo");
function api(info: ApiMethodInfo) {
return function (target: any, propertyKey: string) {
target[apiMethodInfo] = target[apiMethodInfo] || {};
target[apiMethodInfo][propertyKey] = info;
}
}
type ApiInfo<S extends object> = Record<keyof S, ApiMethodInfo>;
type Client<S extends object> = {[key in keyof S]: S[key] extends (req: infer Req) => infer Res ? (req: Req) => Promise<Res> : never};
function invokeApi(info: ApiMethodInfo, param: any) {
console.log(info, param);
}
function generateClient<S extends object>(serverClass: new() => S): Client<S> {
const infos = serverClass.prototype[apiMethodInfo]; // a decorator's target is the constructor function's prototype
const client = {} as Client<S>;
for (const key in infos) { // won't encounter apiMethodInfo because Symbol properties are not enumerable
const info = infos[key];
client[key as keyof S] = ((param: any) => invokeApi(info, param)) as any;
}
return client;
}
// application code
interface AuthRequest {}
interface AuthResponse {}
interface LoginRequest {
username: string,
password: string,
}
interface LoginResponse {}
class MyServer {
#api({
httpPath: '/authenticate',
httpMethod: 'POST'
})
authenticate(request: AuthRequest): AuthResponse {
throw new Error("Not implemented yet");
}
#api({
httpPath: '/login',
httpMethod: 'POST'
})
login(request: LoginRequest): LoginResponse {
throw new Error("Not implemented yet");
}
}
const myClient = generateClient(MyServer); // compiler derives the method signatures from the server implementation
const username = "joe";
const password = "secret";
const response = myClient.login({username, password}); // ... and can therefore check that this call is properly typed
Notice how using a Symbol prevents name collisions, and ensures that other code doesn't see this property (unless they look for that particular Symbol), and therefore can not be tripped up by its unexpected presence.
Also notice how MyServer, at runtime, contains the constructor of the class, whose prototype holds the declared instance methods, and it being passed as target to any decorators thereof.
General Advice
May I conclude with some advice for the recovering Java programmer? ;-)
EcmaScript is not Java. While the syntax may look similar, EcmaScript has many useful features Java does not, which often allow writing far less code. For instance, if you need a DTO, it is wholly unnecessary to declare a class with a constructor manually copying each parameter into a property. You can simply declare an interface instead, and create the object using an object literal. I recommend looking through the Modern JavaScript Tutorial to familiarize yourself with these useful language features.
Also, some features behave differently in EcmaScript. In particular, the distinction between class and interface is quite different: Classes are for inheriting methods from a prototype, interfaces for passing data around. It's quite nonsensical to declare a class for a Response that will be deserialized from JSON, because prototypes don't survive serialization.

NestJS/Express: Case-Insensitive Body Fields

I'm struggling to make the fields of my request DTOs case insensitive.
export class ExampleDto {
dateOfBirth?: string
}
Now I want to accept
{ "dateofbirth": "19880101" }
{ "dateOfBirth": "19880101" }
{ "DATEOFBIRTH": "19880101" }
My first thought was to implement a middleware which just looks at the incoming body and "extends it" with lower & upper case mappings for all incoming fields.
But that doesn't meet my requirements due to camel case, which I definitely want to keep as the default.
Any ideas on how to do this?
You could create a custom Pipe where you try the different options and finally return the Dto instance:
export class CaseInsensitiveExampleDtoPipe implements PipeTransform{
transform(body: any, metadata: ArgumentMetadata): ExampleDto {
const dto = new ExampleDto();
dto.dateOfBirth = body.dateOfBirth || body.dateofbirth || body.DATEOFBIRTH;
return dto;
}
In your controller you can then use it as follows:
#UsePipes(new CaseInsensitiveExampleDtoPipe())
async postNewExample(#Body() exampleDto: ExampleDto) {
// ...
}
Since JavaScript properties start existing after their initialization, you cannot "see" the definition of dateOfBirth?: string and therefor you won't be able to match it against the received JSON.
A possible solution for that is to enforce the creation of the properties of all of your DTO's with a constructor:
export class ExampleDto {
dateOfBirth: string
constructor(dateOfBirth: string){
this.dateOfBirth = dateOfBirth;
}
}
Then, you'll be able to iterate over the ExampleDto's properties and match them with a pipe (the received type can be derived from metadata):
#Injectable()
export class IgnoreCasePipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
const dto = new metadata.metatype;
const dtoKeys = Object.getOwnPropertyNames(dto);
Object.keys(value).forEach(key => {
const realKey = dtoKeys.find(dtoKey => dtoKey.toLocaleLowerCase() === key.toLocaleLowerCase());
if (realKey) {
dto[realKey] = value[key];
}
});
return dto;
}
}
Either inject it globally in main.ts or wherever it's needed - just bear in mind that you'll need to create a constructor for each DTO.
Note: this would work for a single-level class. If you want to support something like people: PersonDto[] in your classes then you'll need to recursively find all of the nested keys and match them - something like this.

How to upload Files and Other fields with Request Body in Nestjs

I want to upload three fields like:
Example Data:
partner_id: 3638,
review: [{'product_id': 155, 'order_sku_id': 155, 'review_title': 'Orange Review','rating': 5 }],
review_images[0][0]: ImageFile00
review_images[0][1]: ImageFile01
review_images[1][0]: ImageFile10
review_images is a array of images like the above.
My approach to upload these data. I have created a Dto:
export class CreateReviewDto {
partner_id : Number;
review: any[];
review_images: any[];
}
Controller:
#Controller('api/v1')
export class ReviewController {
constructor(private readonly reviewService: ReviewService) {}
#Post('/review')
#HttpCode(201)
#UseInterceptors(FilesInterceptor('review_images[]'))
create(#Body() createReviewDto: CreateReviewDto, #UploadedFiles() review_images: Express.Multer.File) {
return this.reviewService.create(params,createReviewDto);
}
}
But images are not coming in POSTMAN. Giving undefined Am I going to a right way? What should I do?
The first thing that I noticed. You should use 'review_images' in your FilesInterceptor (link on documentation)
#Controller('api/v1')
export class ReviewController {
constructor(private readonly reviewService: ReviewService) {}
#Post('/review')
#HttpCode(201)
#UseInterceptors(FilesInterceptor('review_images'))
create(#Body() createReviewDto: CreateReviewDto, #UploadedFiles() review_images: Express.Multer.File) {
return this.reviewService.create(params,createReviewDto);
}
}
The second thing. You should send the right request from postman. I suppose it should look like this

RobinBuschmann/soap-typescript/soap-decorators Example

Can someone please give me a detailed example of RobinBuschmann/soap-typescript/soap-decorators Example. I am looking to create a wsdl xml for node-soap. The example given on github of RobinBuschmann/soap-typescript does not seem to work as is. I put the first three code snippets in a file called createWsdl.js and ran it with "node createWsdl.js" and I get an error. I suspect I am not doing the right thing. Can someone please help me or give me a detailed example that actually works.
I used node-soap and soap-decorators to communicate with Quickbooks. The following is from my app.ts file:
this.express.use(
'/soap',
soap(this.quickbooksController, {
overrideRootElement: {
namespace: '',
xmlnsAttributes: [
{
name: 'xmlns',
value: 'http://developer.intuit.com/'
}
]
}
})
);
The controller is annotated like so:
import { SoapOperation, SoapService } from 'soap-decorators';
import { AuthenticateRequest, AuthenticateResponse } from './model/authenticate.interface';
#SoapService({
portName: 'QBWebConnectorSvcSoap',
serviceName: 'QBWebConnectorSvc',
targetNamespace: 'http://developer.intuit.com/'
})
export class QuickbooksController {
#SoapOperation(AuthenticateResponse)
authenticate(data: AuthenticateRequest, res: (res: AuthenticateResponse) => any): void {
res({ authenticateResult: { string: ['', 'NVU'] } });
}
}
My request and response objects are decorated as XSD types:
import { XSDComplexType, XSDElement } from 'soap-decorators';
#XSDComplexType
export class AuthenticateRequest {
#XSDElement
strUserName: string;
#XSDElement
strPassword: string;
}
#XSDComplexType
class AuthenticateResult {
#XSDElement({ type: 'string' })
string: string[];
}
#XSDComplexType({ name: 'authenticateResponse' })
export class AuthenticateResponse {
#XSDElement
authenticateResult: AuthenticateResult;
}

Resources