-
Notifications
You must be signed in to change notification settings - Fork 114
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #137 from holidayextras/parse-and-joi-validate-sea…
…rch-filter Parse and joi validate search filter: update changelog and bump version
- Loading branch information
Showing
9 changed files
with
218 additions
and
83 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
/* @flow weak */ | ||
"use strict"; | ||
var filter = module.exports = { }; | ||
|
||
|
||
var FILTER_OPERATORS = ["<", ">", "~", ":"]; | ||
var STRING_ONLY_OPERATORS = ["~", ":"]; | ||
|
||
|
||
filter._resourceDoesNotHaveProperty = function(resourceConfig, key) { | ||
if (resourceConfig.attributes[key]) return null; | ||
return { | ||
status: "403", | ||
code: "EFORBIDDEN", | ||
title: "Invalid filter", | ||
detail: resourceConfig.resource + " do not have attribute or relationship '" + key + "'" | ||
}; | ||
}; | ||
|
||
filter._relationshipIsForeign = function(resourceConfig, key) { | ||
var relationSettings = resourceConfig.attributes[key]._settings; | ||
if (!relationSettings || !relationSettings.__as) return null; | ||
return { | ||
status: "403", | ||
code: "EFORBIDDEN", | ||
title: "Invalid filter", | ||
detail: "Filter relationship '" + key + "' is a foreign reference and does not exist on " + resourceConfig.resource | ||
}; | ||
}; | ||
|
||
filter._splitElement = function(element) { | ||
if (!element) return null; | ||
if (FILTER_OPERATORS.indexOf(element[0]) !== -1) { | ||
return { operator: element[0], value: element.substring(1) }; | ||
} | ||
return { operator: null, value: element }; | ||
}; | ||
|
||
filter._stringOnlyOperator = function(operator, attributeConfig) { | ||
if (!operator || !attributeConfig) return null; | ||
if (STRING_ONLY_OPERATORS.indexOf(operator) !== -1 && attributeConfig._type !== "string") { | ||
return "operator " + operator + " can only be applied to string attributes"; | ||
} | ||
return null; | ||
}; | ||
|
||
filter._parseScalarFilterElement = function(attributeConfig, scalarElement) { | ||
if (!scalarElement) return { error: "invalid or empty filter element" }; | ||
|
||
var splitElement = filter._splitElement(scalarElement); | ||
if (!splitElement) return { error: "empty filter" }; | ||
|
||
var error = filter._stringOnlyOperator(splitElement.operator, attributeConfig); | ||
if (error) return { error: error }; | ||
|
||
if (attributeConfig._settings) { // relationship attribute: no further validation | ||
return { result: splitElement }; | ||
} | ||
|
||
var validateResult = attributeConfig.validate(splitElement.value); | ||
if (validateResult.error) { | ||
return { error: validateResult.error.message }; | ||
} | ||
|
||
var validatedElement = { operator: splitElement.operator, value: validateResult.value }; | ||
return { result: validatedElement }; | ||
}; | ||
|
||
filter._parseFilterElementHelper = function(attributeConfig, filterElement) { | ||
if (!filterElement) return { error: "invalid or empty filter element" }; | ||
|
||
var parsedElements = [].concat(filterElement).map(function(scalarElement) { | ||
return filter._parseScalarFilterElement(attributeConfig, scalarElement); | ||
}); | ||
|
||
if (parsedElements.length === 1) return parsedElements[0]; | ||
|
||
var errors = parsedElements.reduce(function(combined, element) { | ||
if (!combined) { | ||
if (!element.error) return combined; | ||
return [ element.error ]; | ||
} | ||
return combined.concat(element.error); | ||
}, null); | ||
|
||
if (errors) return { error: errors }; | ||
|
||
var results = parsedElements.map(function(element) { | ||
return element.result; | ||
}); | ||
|
||
return { result: results }; | ||
}; | ||
|
||
filter._parseFilterElement = function(attributeName, attributeConfig, filterElement) { | ||
var helperResult = filter._parseFilterElementHelper(attributeConfig, filterElement); | ||
|
||
if (helperResult.error) { | ||
return { | ||
error: { | ||
status: "403", | ||
code: "EFORBIDDEN", | ||
title: "Invalid filter", | ||
detail: "Filter value for key '" + attributeName + "' is invalid: " + helperResult.error | ||
} | ||
}; | ||
} | ||
return { result: helperResult.result }; | ||
}; | ||
|
||
filter.parseAndValidate = function(request) { | ||
if (!request.params.filter) return null; | ||
|
||
var resourceConfig = request.resourceConfig; | ||
|
||
var processedFilter = { }; | ||
var error; | ||
var filterElement; | ||
var parsedFilterElement; | ||
|
||
for (var key in request.params.filter) { | ||
filterElement = request.params.filter[key]; | ||
|
||
if (!Array.isArray(filterElement) && filterElement instanceof Object) continue; // skip deep filters | ||
|
||
error = filter._resourceDoesNotHaveProperty(resourceConfig, key); | ||
if (error) return error; | ||
|
||
error = filter._relationshipIsForeign(resourceConfig, key); | ||
if (error) return error; | ||
|
||
parsedFilterElement = filter._parseFilterElement(key, resourceConfig.attributes[key], filterElement); | ||
if (parsedFilterElement.error) return parsedFilterElement.error; | ||
|
||
processedFilter[key] = [].concat(parsedFilterElement.result); | ||
} | ||
|
||
request.processedFilter = processedFilter; | ||
|
||
return null; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.