-
Notifications
You must be signed in to change notification settings - Fork 68
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
Support non-numeric values for Usage chart filter parameters #716
Support non-numeric values for Usage chart filter parameters #716
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also you need to create an integration test to confirm that the change works properly.
docs/software-requirements.md
Outdated
@@ -76,7 +76,7 @@ added with this command for CentOS 7: | |||
# yum install httpd php php-cli php-mysql php-gd php-mcrypt \ | |||
gmp-devel php-gmp php-pdo php-xml \ | |||
php-pear-MDB2 php-pear-MDB2-Driver-mysql \ | |||
java-1.7.0-openjdk java-1.7.0-openjdk-devel \ | |||
java-1.8.0-openjdk java-1.8.0-openjdk-devel \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this documentation change belongs in this pull request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has been reverted.
* @return int[]|array all current queries return an int array. If no rows are found then | ||
* an empty array is returned. | ||
*/ | ||
$lookupValue = function ($query, array $values) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that the PHP PDO already has a function that will return the values of one column in an array.
$lookupvalue = function ($query, array $values) {
$db = DB::factory('database');
$stmt = $db->prepare($query);
$stmt->execute($values);
return implode(',', $stmt->fetchAll(PDO::FETCH_COLUMN, 0) );
};
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very cool, thanks! This will be included in the next commit.
{ | ||
|
||
/** | ||
* Anonymous function to prevent code duplication. $query is expected to minimally fulfill |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I note that this anonymous function does not prevent code duplication since it only ever needs to be called once per call to translateFilterValues. In fact it results in more lines of code than simply having the code inline at the bottom of the switch statement and having the switch statement set the value of the $query.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah... best intentions and all... Thanks for the catch. The code will be updated and actually simplified in the next set of commits.
}; | ||
|
||
if (!is_numeric($usageFilterValue)) { | ||
$usageFilterValues = explode(',', $usageFilterValue); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The $usageFilterValue
variable already went through an explode()
on line 976. Therefore it cannot have any more commas to be exploded again and the bulk of this code is unnecessary. Please can you double check this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Double checked, you are correct, this value doesn't need to be exploded again. Here is the function body w/ updates so far ( Just finishing up writing integration tests before committing ) :
// If the value is already a number then just return it. We do this because this function is
// about tranlsating a non-numeric value to a numeric one ( i.e. an id )
if (is_numeric($usageFilterValue)) {
return $usageFilterValue;
}
$query = null;
switch ($usageFilterType) {
case 'pi':
// NOTE: At the time of writing there is no instance where multiple records have
// the same username but different person_id values.
$query = "SELECT DISTINCT sa.person_id AS value FROM modw.systemaccount sa WHERE sa.username = :value;";
break;
case 'resource':
$query = "SELECT id AS value FROM modw.resourcefact WHERE code = :value";
break;
case 'project':
$query = "SELECT account_id AS value FROM modw_cloud.account WHERE display = :value";
break;
}
if ($query !== null) {
$db = DB::factory('database');
$stmt = $db->prepare($query);
$stmt->execute(array(':value' => $usageFilterValue));
return implode(',', $stmt->fetchAll(PDO::FETCH_COLUMN, 0));
}
return $usageFilterValue;
196b1ad
to
e3b250e
Compare
$stmt = $db->prepare($query); | ||
$stmt->execute(array(':value' => $usageFilterValue)); | ||
|
||
return implode(',', $stmt->fetchAll(PDO::FETCH_COLUMN, 0)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you want to implode all results here. The rest of the code will only work if there is a single return value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update is in the latest version of the PR
What is the expected behavior of the code if a string value is supplied that does not match in the database. I.e if I ask for filtering by a PI that does not exist. |
@smgallo Thoughts? What currently occurs is that that particular filter / value combo is ignored. So, |
@@ -1156,14 +1155,38 @@ private function translateFilterValue($usageFilterType, $usageFilterValue) | |||
} | |||
|
|||
if ($query !== null) { | |||
$filterValueIsString = preg_match('/[\'"]+/', $usageFilterValue) === 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This regex will match quotes anywhere in the string not just those surrounding it. It should be anchored at the start of the string and shouldn't have the +
(e.g., /^[\'"]/
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should also be anchored at the end probably /^[\'"].*[\'"]$/
$filterValueIsString = preg_match('/[\'"]+/', $usageFilterValue) === 1; | ||
|
||
$value = $filterValueIsString | ||
? preg_replace('/[\'"]+/', '', $usageFilterValue) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This regex will trim any quote character from the string, which may not be what we want if we allow passing of a person's name in the future as it will remove apostrophes from anywhere in the string. We would be better served using trim($usageFilterValue, '\'"')
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, didn't know that PHP's trim allowed a char list to be trimmed. I'll update that.
if ($rows !== false) { | ||
$rowCount = count($rows); | ||
|
||
if ($rowCount === 0 && !$filterValueIsString) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we are now forcing the caller to specify whether the value is an integer id or a string that needs lookup in a dimension table, we can get rid of this logic. If the value is not enclosed in quotes we can return immediately.
} | ||
|
||
// If a string was provided but no id(s) were found then exception out. | ||
throw new Exception("Invalid filter value detected"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets include the invalid filter in the error message:
throw new Exception("Invalid filter value detected"); | |
throw new Exception(sprintf("Invalid filter value detected: %s", $usageFilterValue)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
facepalm Ignore the most recent commit, added the type instead of the value.
Added the ability for Usage chart requests to provide non-numeric values for '_filter' parameters. The new feature will attempt to translate these non-numeric values into appropriate id values that can be used during chart generation. Specifically this new feature is to support integration w/ Coldfront.
Per code review comments I've simplified this function. - Moved the `is_numeric` check to the top of the function and used it as a short circuit. - Removed the anonymous function and moved the code it contained to the bottom of the function, executed contingent on which `case`, if any, is hit.
This new test checks that the `_filter` variables accept non-id values (optionally, multiple comma delimited values). The test data includes the following: - Tests run as all users ( pub, cd, cs, pi, usr ) - Multiple Realms ( Jobs, Cloud ) - For the Jobs realm, all combinations of: ( including single, multiple, numeric & non-numeric values ) - 'resource', 'resource_filter' - 'pi', 'pi_filter' - For the Cloud realm: ( including single, multiple, numeric, and non-numeric values ) - 'project', 'project_filter'
As this function should only ever return one value the code has been updated so that this is the case.
- Updated `translateFilterValue` to return an array of values and the calling code to work with an array as opposed to a single value. This is to support the use case of a system account being associated with more than one person. - Updated `translateFilterValue` to only interpret `$usageFilterValue` as a string if it contains quotes ( single or double ). This allows us to identify when the calling code is passing us a value that is intended to be interpreted as a string not a number. - Updated Test Cases so that string values are quoted with both single / double quotes. - Added additional test cases that test what happens when a string value is provided that is not found in the db.
Refactored a portion of the `convertChartRequest` function and combined the two `for-each` loops that were populating the `$meFilters` variable. Now we have one loop that has the same functionality. Also a tiny comment update in the `translateFilterValue` function for the 'pi' filter type. Now that we always return an array the previous comment didn't really make sense anymore.
Now that we have a reliable test for what constitutes a "string" ( i.e. must be quoted ) we can simplify the internals of `translateFilterValue` by removing the vast majority of the if/elseif block. Also added a few comments to help provide context.
Added `$usageFilterType` to the message for the exception thrown in `translateFilterValue`.
Added the invalid `$usageFilterValue` to the exception message and updated the section in the tests that handled detecting when exceptions are thrown to handle this update.
Co-Authored-By: ryanrath <ryanrath@buffalo.edu>
ab0fc2a
to
0f83768
Compare
Description
Added the ability for Usage chart requests to provide non-numeric values for
'_filter' parameters. The new feature will attempt to translate these
non-numeric values into appropriate id values that can be used during chart
generation. Specifically this new feature is to support integration w/
Coldfront.
Motivation and Context
Tests performed
Types of changes
Checklist: