javascriptmicroservicesmoleculermoleculer-web

Moleculer micro service rooting not working in case of separate services rooting


when i try ti implement code like that it works: gateway.service.ts

{
                path: "/api",
                whitelist: [ "**"],
                use: [],
                mergeParams: true,
                authentication: true,
                authorization: true,
                autoAliases: true,
                aliases: {
                    "login": "auth.login",
                },


                bodyParsers: {
                    json: {
                        strict: false,
                        limit: "1MB",
                    },
                    urlencoded: {
                        extended: true,
                        limit: "1MB",
                    },
                },

                
                mappingPolicy: "all", // Available values: "all", "restrict"

                // Enable/disable logging
                logging: true,
            },
 {
                path: "/controller",
                bodyParsers: {
                    json: false,
                    urlencoded: false
                },
                aliases: {
                    "POST /upload": {
                        params: {
                            file: { type: "file", required: true },
                            checkList: { type: "string", default:'' }
                        },
                        type: "multipart",
                        busboyConfig: {
                          limits: {
                            files: 3
                          }
                        },
                        action: "controller.post"
                      }
                },

                busboyConfig: {
                    
                    limits: {
                        files: 3
                    }
                },

                mappingPolicy: "restrict"
            }

but when i separte every rooting for the specific service it not works : like that:

gateway.service.ts

{
                path: "/api",
                whitelist: [ "**"],
                use: [],
                mergeParams: true,
                authentication: true,
                authorization: true,
                autoAliases: true,
                aliases: {
                    "login": "auth.login",
                },


                bodyParsers: {
                    json: {
                        strict: false,
                        limit: "1MB",
                    },
                    urlencoded: {
                        extended: true,
                        limit: "1MB",
                    },
                },

                
                mappingPolicy: "all", // Available values: "all", "restrict"

                // Enable/disable logging
                logging: true,
            },

controller.service.ts // here it not works but i don't know why

settings:{
routes:[
{
                path: "/controller",
                bodyParsers: {
                    json: false,
                    urlencoded: false
                },
                aliases: {
                    "POST /upload": {
                        params: {
                            file: { type: "file", required: true },
                            checkList: { type: "string", default:'' }
                        },
                        type: "multipart",
                        busboyConfig: {
                          limits: {
                            files: 3
                          }
                        },
                        action: "controller.post"
                      }
                },

                busboyConfig: {
                    
                    limits: {
                        files: 3
                    }
                },

                mappingPolicy: "restrict"
            }
]
}

i tried to impelement rooting and aliases of every service in it x.service.ts file it not works when i put every rooting settings in it file , it works only when i put all the settings in the gateway.service.ts file


Solution

  • try this trick you can make a class that

        import Busboy from "@fastify/busboy";
        import _ from "lodash";
        
        class FileUploaderHandler {
            static async handle($ctx: any, route: any, req: any, res: any): Promise<any> {
                return new Promise((resolve, reject) => {
                    const ctx = req.$ctx;
        
                    const files: any = [];
                    let promises: any = [];
        
                    const busboyConfig = { limits: {} };
        
                    const busboyOptions = _.defaultsDeep(
                        {
                            // very important : it pass the header from coming request to busboy
                            //  it need the usage of symbol cause header key is a symbol in the request
                            headers: req[Object.getOwnPropertySymbols(req)[1]],
                        },
                        busboyConfig,
                        busboyConfig,
                    );
        
                    const busboy = new Busboy(busboyOptions);
                    (<any>ctx.meta).$multipart = {};
                    let numOfFiles = 0;
                    let hasField = false;
        
                    busboy.on("file", (fieldname, file, filename, encoding, mimetype) => {
                        let parsedFile = {
                            fieldname: fieldname,
                            filename: filename,
                            encoding: encoding,
                            mimetype: mimetype,
                            file: file,
                            actionName: ctx.params.req.$action.name,
                        };
                        files[files.length] = parsedFile;
                        // send file to the service
                        promises.push(
                            ctx
                                .call(
                                    ctx.params.req.$action.name.split(".")[0] + ".upload",
                                    file,
                                    _.defaultsDeep({}, route.opts.callOptions, {
                                        meta: {
                                            file: parsedFile,
                                        },
                                    }),
                                )
                                .catch((err: any) => {
                                    file.resume();
                                }),
                        );
    
                    // if we indicate file size limit per file in config
    
                    file.on("limit", () => {
                        if (_.isFunction(busboyOptions.onFileSizeLimit)) {
                            busboyOptions.onFileSizeLimit.call(ctx.service, file, busboy);
                        }
                    });
    
                    numOfFiles++;
                    // it must be called else "finish" not be invoked
                });
    
                //if there is a normal field (string, etc ..)
    
                busboy.on("field", (field, value) => {
                    hasField = true;
                    ctx.meta.$multipart[field] = value;
                });
    
                busboy.on("finish", async () => {
                    await ctx.service.Promise.all(promises);
                    resolve(files);
                });
    
                busboy.on("error", (err: any) => {
                    req.unpipe(req.busboy);
                    req.resume();
                });
    
                // if there is error to be thrown by busboy
    
                ["partsLimit", "filesLimit", "fieldsLimit"].forEach((event) => {
                    if (_.isFunction(busboyOptions[`on${_.capitalize(event)}`])) {
                        busboy.on(event, () =>
                            busboyOptions[`on${_.capitalize(event)}`].call(
                                ctx.service,
                                busboy,
                                this,
                                ctx.service,
                            ),
                        );
                    }
                });
    
                req.pipe(busboy);
            });
        }
    }
    
        export default FileUploaderHandler;` 
    

    then you can call it in your onBeforeCall in your gateway :

    async onBeforeCall(
                    ctx: Context<unknown, Meta>,
                    route: Route,
                    req: IncomingRequest,
                    res: GatewayResponse,
                ): Promise<void> {
                    // Set request headers to context meta
                    ctx.meta.userAgent = req.headers["user-agent"];
                    // check if there are multipart action
                    if (req.$action.isMultipart) { // to avoid regression
                        const files = await FileUploaderHandler.handle(ctx, route, req, res);
                        (<any>ctx.meta).files = files;
                    }
                }
    

    in your service you can use it like that :

        import: {
            rest: {
                method: "POST",
                path: "/import",
            },
            isMultipart: true, // you should add it here
            handler(ctx: any): any {
                const directoryFile = ctx.meta.files.find(
                    (file: any) => file.fieldname == "name_of_field_here",
                );
                if (directoryFile) {
                    
                    return {
                        message: "file uploaded successfully",
                        files: ctx.meta.files,
                    };
                } 
            },
        },
        upload: {
            handler(ctx: any): any {
                return new Promise((resolve, reject) => {
                    if (typeof ctx.params.pipe == "function") {
                        // file upload
                        const startTime = Date.now();
                        const f = fs.createWriteStream(
                            path.join("upload/", ctx.meta.file.filename),
                        );
                        f.on("close", () => {
                            
                            resolve({});
                        });
                        ctx.params.pipe(f);
                    } else {
                        
                        reject(new Error("NO_FILES"));
                    }
                });
            },
        },