Anuvadak (अनुवादक) - New features

in utopian-io •  6 years ago  (edited)

Repository

https://github.com/node-muneem/anuvadak

I've added some new features to Anuvadak.

Project summary

Anuvadak is an opensource project which adds serialization, and body parsing methods to muneem web framework.

Change summary

In the current change, I've added/updated the methods to parse request stream to read XML, multipart forms, URL encoded forms and files. I've also added/updated the methods to write text, js objects as XML. All the methods can be used in the following way;

const muneem = require('muneem')();
const anuvadak = require('anuvadak');
muneem.use(anuvadak.XXX, globalOptions);

muneem.add('handler', function(asked, answer){
        await asked.readXXX();
        //..
    answer.writeXXX( data );
})

Commits

Change detail

Muneem web framework supports core functionality to read text information from the request stream and write text data to respond. All the methods added in this library are to hide details from the user and provide simple to use methods.

In the previous release, Anuvadak was adding all the methods internally, where the user was losing the options to skip particular type of serializer or body parser.

In previous release

const muneem = require('muneem')();
const anuvadak = require('anuvadak');
muneem.use(anuvadak, combinedGlobalOptions);

muneem.add('handler', function(asked, answer){
        await asked.readXXX();
        //..
    answer.writeXXX( data );
})

After current release

const muneem = require('muneem')();
const anuvadak = require('anuvadak');
muneem.use(anuvadak.XXX, globalOptionsForX);
muneem.use(anuvadak.YYY, globalOptionsForY);

muneem.add('handler', function(asked, answer){
        await asked.readXXX();
        //..
    answer.writeXXX( data );
})

It has also made the library simple and clean: https://github.com/node-muneem/anuvadak/blob/master/src/anuvadak.js

XML

XML read and write methods are changed to use new changes in the library. This change is more about using XML specific options instead of extracting relevant options from combined global options. I've used Fast XML Parser so the parsing can be customized as per the user's choice.

URL encoded form

This is the new feature added to read the URL encoded forms. I used qs npm package to parse the query string and extract form detail.

function UrlFormReader(globalOptions){
    if(!globalOptions) globalOptions = {};

    this.readForm =  function(options){
        options = Object.assign({}, globalOptions, options);

        if( !this.queryStr || (options.confirmHeaderParam  && this.headers["content-type"] !== "application/x-www-form-urlencoded" ) ){
            return;
        }
    
        this.body = qs.parse( this.queryStr, options ); //this.body will be object now
        return this.body;
    }
}

Checking header parameters only on demand speed up the processing. Here is the code: https://github.com/node-muneem/anuvadak/blob/master/src/urlForm.js

Form Handler

Form handler is a wrapper around formidable npm package. A user can use this to avoid any complex configuration. This function uses metered request stream set by the Muneem framework. Hence safe to use. Here is the code summary. Some code is replaced with comments for explanation purpose;

const globalOptions = buildOptions(defaultOptions, globalOptionsInput);

//build directory path

this.getInstance = function(optionsInput){
        const options = buildOptions(globalOptions, optionsInput);

        //set upload path

        const form = new formidable.IncomingForm();
        //set form options
        
        form.read = () => {
                form.parse(this.stream);
        }

        return form;
}

This is how it can be used in any request handler;

const muneem = require('muneem')();
const anuvadak = require('anuvadak');
muneem.use(anuvadak.form, globalOptionsForForm);

muneem.add('handler', function(asked, answer){
        var formHandler = asked.getFormHandle(options);

    formHandler.on("field", function(name, value) {
        //..
    });

    formHandler.on("file", function(name, file) {
        //..
    });

    formHandler.read();

        //..
})

Here is the code: https://github.com/node-muneem/anuvadak/blob/master/src/formHandler.js

Form

This feature allows the user to read the form data from the request stream without handling any event of formHandler. A user can configure if the files need to move to a particular location, what if the same file is uploaded multiple times, or if it is larger than expectation etc. Here is the code for the same. I've replaced some part with comments for explanation purpose.

The full code can be found here: https://github.com/node-muneem/anuvadak/blob/master/src/form.js



const form = formHandler.getInstance.bind(this)(options);
//set form options

this.body = {};
form.on('field', (fieldName, value) => {
        if(!options.field.on.receive){
                this.body[fieldName] = value;
        }else{//user want to handle it
                options.field.on.receive(fieldName, value);
        }
});
form.on('file', (fieldName, file) => {
        if(options.file.on.receive){//if user want to handle it
                options.file.on.receive(fieldName, file);//let him handle
        }else{//otherwise move to user defined location
                this.body[fieldName] = file;
                let saveTo = options.file.saveTo ; //if present then move and rename file to given location
                if(!globalOptions.file.saveTo && !saveTo){
                        //do nothing
                }else{
                        if( !isDirectoryExist(saveTo) ){
                                saveTo = path.join(globalOptions.file.saveTo, saveTo);
                                if( isDirectoryExist(saveTo) ){
                                        saveTo = "" ;
                                }
                        }
                        if(saveTo){
                                file.newPath = path.join(saveTo, file.name);
                                if( isExist(file.newPath) ){
                                        options.file.on.duplicate(fieldName, file, saveTo );
                                }else{
                                        fs.rename(file.path
                                                , path.join(uploadTo, options.file.on.name(file.name) )
                                                , options.file.on.move );
                                }
                        }else{//if user has defined the wrong location
                                options.on.error(new Error(`Invalid location to save file: ${saveTo}`));
                        }
                }
        }

});

Now I've used Promise so that the data can be used by the response handler.

        await new Promise( (resolve, reject) => {
            form.parse(this.stream);
            form.on('error', (err) => {
                reject(err);
                options.on.error(err);
            });
            form.on('aborted', () => {
                reject();
                options.on.aborted();
            });
            form.on('end', () => {
                resolve(this.body);
                options.on.end();
            });
        });
Files

Reading files from the request stream is as similar to reading forms in the above approach. However, reading only files with the small change in the code can improve the performance. Hence I've created a separated method for that. It seems more meaning full.

Here is the full code: https://github.com/node-muneem/anuvadak/blob/master/src/files.js

if(options.sync){
        await new Promise( (resolve, reject) => {
                form.parse(this.stream);
                form.on('error', (err) => {
                        reject(err);
                        options.on.error(err);
                });
                form.on('aborted', () => {
                        reject();
                        options.on.aborted();
                });
                form.on('end', () => {
                        resolve(this.body);
                        options.on.end();
                });
        });
}else{
        form.on('error', options.on.error);
        form.on('aborted', options.on.aborted);
        form.on('end', options.on.end);
        form.parse(this.stream);
}

In addition to that relevant unit tests were added.

Contribution

I'm planning to add serializers and body parsers for protobuf, nimn etc in future. If you're interested please support, fork, and suggest improvements.

GitHub Account

https://github.com/amitguptagwl

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

Thank you for your contribution. I can see you have done 5,560 additions and 998 deletions, in a single commit. It's better to divide those commits in smaller so that its better for us to evaluate better.

Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.

To view those questions and the relevant answers related to your post, click here.


Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]

Thanks for your feedback. The reason you've seen the higher number of deletion and addition as most of the files have been renamed. Hence I've described every change in this post.

Hi @amitguptagwl!

Your post was upvoted by @steem-ua, new Steem dApp, using UserAuthority for algorithmic post curation!
Your post is eligible for our upvote, thanks to our collaboration with @utopian-io!
Feel free to join our @steem-ua Discord server

Congratulations @amitguptagwl! You received a personal award!

Happy Birthday! - You are on the Steem blockchain for 1 year!

You can view your badges on your Steem Board and compare to others on the Steem Ranking

You can upvote this notification to help all Steem users. Learn how here!