Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add validation of query params in channel name #191

Merged
merged 19 commits into from
Nov 2, 2020
Merged

feat: add validation of query params in channel name #191

merged 19 commits into from
Nov 2, 2020

Conversation

juergenbr
Copy link
Contributor

Description

  • added validateChannelNames test suite
  • added validateChannelName validator
  • added method to check for URL parameters in string with RegEx

Related issue(s)
Fixes #145

@derberg
Copy link
Member

derberg commented Oct 29, 2020

@juergenbr looks good. 2 questions:

  • as you can see our linter doesn't like regex 😄 (btw, you can run in locally with npm run lint. What do you think about using URL API instead? like
    const channelName = '/user/signedup?foo=1';
    const url = new URL('http://'+channelName);
    console.log(url.search) //logs ?foo=1;
    
  • I think it would be possible to integrate new function with validateChannelParams (of course name would have to be changed to validateChannel. Asking because this way validator would iterate over list of channels only onece, not twice 🤔 what do you think?

@juergenbr
Copy link
Contributor Author

@derberg good points, yes. I thought that using a similar expression as parseUrlVariables would not be an issue, sorry.
How should a case look like when both checks fail? Should I handle them via a try catch block and output both error messages?

@derberg
Copy link
Member

derberg commented Oct 29, 2020

@juergenbr you can keep regex, I just suggested different approaches as then you do not have issues with a linter. You can also just modify your function to have a similar approach as in parseUrlVariables and that is fine too. Just do not create new regex instance, my guess it is the issue.

regarding case when 2 issues show up at the same time, don't worry about it, they do not have to be thrown at the same time. We have such a case here already https://github.com/asyncapi/parser-js/blob/master/lib/customValidators.js#L180-L196

@juergenbr
Copy link
Contributor Author

@derberg oh okay, no problem I'll fix it.
Thanks for the reference, then I'll handle it similarly!

@juergenbr
Copy link
Contributor Author

Test cases now work as expected, but due to the merge the new method got a little too complex so I split part of the logic. Please let me know if this is fine or if I should find another way.

Copy link
Member

@derberg derberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is getting a very good shape. I added some comments, long story short -> you need to add one more test case there that I do not think is covered with current implementation

lib/utils.js Outdated
if (typeof str !== 'string') return;
const channelName = str;
const url = new URL(`http://${channelName}`);
console.log(url.search); //logs ?foo=1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please remove this console.log

if (invalidChannelName.size) {
throw new ParserError({
type: validationError,
title: 'Channel names with parameters exist',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
title: 'Channel names with parameters exist',
title: 'Channel names contain query parameters',

type: validationError,
title: 'Channel names with parameters exist',
parsedJSON,
validationErrors: groupValidationErrors('channels', 'channels contain invalid name with url parameters ', invalidChannelName, asyncapiYAMLorJSON, initialFormat)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
validationErrors: groupValidationErrors('channels', 'channels contain invalid name with url parameters ', invalidChannelName, asyncapiYAMLorJSON, initialFormat)
validationErrors: groupValidationErrors('channels', 'channels contain invalid name with url query parameters', invalidChannelName, asyncapiYAMLorJSON, initialFormat)

const variables = parseUrlVariables(key);
const notProvidedChannelParams = notProvidedParams.get(tilde(key));
const parameters = parseUrlParameters(key);
if (!variables && !parameters)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because of the following ifs, I don't think we need this one

chnlsMap.forEach((val, key) => {
const variables = parseUrlVariables(key);
const notProvidedChannelParams = notProvidedParams.get(tilde(key));
const parameters = parseUrlParameters(key);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe rename parameters to queryParameters and parseUrlParameters to parseUrlQueryParameters?

this will make code slightly clearer. Problem with current naming is that this function validates, query params and also if variables have params object. So you see "parameters" in 2 different contexts with totally different meaning. This is why I think we need to make super clear that it is query param here

lib/customValidators.js Show resolved Hide resolved
const inputString = `{
"channels": {
"test/test01": {
"parameters": {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are validating here a channel name, so instead of providing a parameter here, I think you could just pass empty object

const inputString = `{
"channels": {
"/user/signedup?foo=1": {
"subscribe": {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as with the other test case, I think you can easily just pass an empty channel object


return true;

function setNotProvidedParams(variables, val, key, notProvidedChannelParams) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can move this function outside this function, as a separate private helper function, because it has the potential to be used in other validations, just don't forget about jsdoc.

Why? because I'm pretty sure you will need to use it in query params validation too, like here

    if (parameters) {
      invalidChannelName.set(key,
        parameters);
    }

this logic is here for handling use case where more that once the error of the same title appear during validation. In other words, I'm pretty sure the current implementation is not able to handle a case where you have 2 different channels containing query parameters. Please add such a test case and you will see what I mean.

@juergenbr
Copy link
Contributor Author

@derberg thanks again for your detailed feedback. I refactored the code to pull the additional method into the utils file. I also found a way to combine exceptions from both validations so that none "overrules" the other.

Regarding the case with 2 or more channels that contain query parameters: since the code you mentioned is inside the loop this case is covered. I added a test case and it is working as expected, returning 2 validation errors inside the array and returning them with correct messages. Same works if one channel has a missing parameter and one contains a query parameter.

Copy link
Member

@derberg derberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some comments regarding naming of errors and jsdoc.
Great idea with displaying errors from different validations, I like it.

What is missing:

  • test case for channel that has no parameter object and name of channel is user/{userId}/signedup?foo=1. So 2 different errors for the same channel
  • in tests, I cannot see in error information about the channel (the one that failed validation) locatin in the file. Things like endColumn and others. Notice that channel variables validaton uses tilde function to make it work

if (notProvidedParams.size || invalidChannelName.size) {
throw new ParserError({
type: validationError,
title: 'Channel validation failed wiht following errors',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tiny spelling mistake here, with not wiht

Suggested change
title: 'Channel validation failed wiht following errors',
title: 'Channel validation failed',


//combine validation errors of both checks and output them as one array
const parameterValidationErrors = groupValidationErrors('channels', 'channel does not have a corresponding parameter object for', notProvidedParams, asyncapiYAMLorJSON, initialFormat);
const nameValidationErrors = groupValidationErrors('channels', 'channels contain invalid name with url query parameters ', invalidChannelName, asyncapiYAMLorJSON, initialFormat);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const nameValidationErrors = groupValidationErrors('channels', 'channels contain invalid name with url query parameters ', invalidChannelName, asyncapiYAMLorJSON, initialFormat);
const nameValidationErrors = groupValidationErrors('channels', 'channel contains invalid name with query parameters ', invalidChannelName, asyncapiYAMLorJSON, initialFormat);

type: validationError,
title: 'Channel validation failed wiht following errors',
parsedJSON,
validationErrors: allValidationErrors //groupValidationErrors('channels', 'channel does not have a corresponding parameter object for', notProvidedParams, asyncapiYAMLorJSON, initialFormat)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please remove this comment

lib/utils.js Outdated
* @function setNotProvidedParams
* @private
* @param {String} variables array of all identified URL variables in a channel name
* @param {Object} val The channel object for which to identify the missing parameters
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @param {Object} val The channel object for which to identify the missing parameters
* @param {Object} val the channel object for which to identify the missing parameters

lib/utils.js Outdated
*
* @function setNotProvidedParams
* @private
* @param {String} variables array of all identified URL variables in a channel name
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @param {String} variables array of all identified URL variables in a channel name
* @param {Array<String>} variables array of all identified URL variables in a channel name

lib/utils.js Outdated
@@ -304,3 +317,25 @@ utils.groupValidationErrors = (root, errorMessage, errorElements, asyncapiYAMLor

return errors;
};

/**
* return all missing properties for a channel
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* return all missing properties for a channel
* extend map with channel params missing corresponding param object

lib/utils.js Outdated
* @param {Object} val The channel object for which to identify the missing parameters
* @param {String} key the channel name.
* @param {Array<Object>} notProvidedChannelParams concatinated list of missing parameters for all channels
* @param {Map} notProvidedParams result map of all missing parameters
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @param {Map} notProvidedParams result map of all missing parameters
* @param {Map} notProvidedParams result map of all missing parameters extended by this function

@juergenbr
Copy link
Contributor Author

Sorry for the doc errors, I implemented your suggestions, added the location information and a test case to cover a channel violating both checks at the same time.

lib/customValidators.js Outdated Show resolved Hide resolved
@derberg derberg changed the title feat: Add validation of query params in channel name feat: add validation of query params in channel name Nov 2, 2020
juergenbr and others added 2 commits November 2, 2020 11:42
@sonarqubecloud
Copy link

sonarqubecloud bot commented Nov 2, 2020

Kudos, SonarCloud Quality Gate passed!

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities (and Security Hotspot 0 Security Hotspots to review)
Code Smell A 0 Code Smells

No Coverage information No Coverage information
No Duplication information No Duplication information

Copy link
Member

@derberg derberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Great work @juergenbr !

@derberg derberg merged commit d871aff into asyncapi:master Nov 2, 2020
@asyncapi-bot
Copy link
Contributor

🎉 This PR is included in version 1.1.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@derberg
Copy link
Member

derberg commented Nov 2, 2020

@all-contributors please add @juergenbr for code

@allcontributors
Copy link
Contributor

@derberg

I've put up a pull request to add @juergenbr! 🎉

@WaleedAshraf
Copy link
Contributor

This is failing with error

err { Error: Invalid URL: http:///
        at parse (/Users/waleedashraf/projects/waleed/asyncapi-validator/node_modules/@asyncapi/parser/lib/parser.js:108:11)
        at process._tickCallback (internal/process/next_tick.js:68:7)
      type: 'https://github.com/asyncapi/parser-js/unexpected-error',
      title: 'Invalid URL: http:///',
      parsedJSON:
       { asyncapi: '2.0.0',
         id: 'urn:com:slack:rtm:api',
         info: { title: 'Slack Real Time Messaging API', version: '1.0.0' },
         servers: { production: [Object] },
         channels: { '/': [Object] },
         components:
          { securitySchemes: [Object],
            schemas: [Object],
            messages: [Object] } },
      message: 'Invalid URL: http:///' } Invalid URL: http:///

Schema: https://raw.githubusercontent.com/asyncapi/asyncapi/master/examples/2.0.0/slack-rtm.yml

Because the channel name is just /

@derberg
Copy link
Member

derberg commented Nov 13, 2020

@WaleedAshraf fix provided #196

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add validation of query params in channel name
4 participants