Skip to content
This repository has been archived by the owner on Jul 11, 2022. It is now read-only.

Commit

Permalink
Bug 1173096 - Support for Filtering child resources
Browse files Browse the repository at this point in the history
Added new endpoint : GET /resource/search which supports *ALL* filtering
options from ResourceCriteria class with small exception - it cannot pass
multiple values to filter functions.
  • Loading branch information
Libor Zoubek committed Mar 23, 2015
1 parent b3de3e9 commit e1e7bc3
Show file tree
Hide file tree
Showing 4 changed files with 492 additions and 0 deletions.
Expand Up @@ -115,6 +115,7 @@
import org.rhq.enterprise.server.rest.domain.ResourceWithType;
import org.rhq.enterprise.server.rest.domain.StringValue;
import org.rhq.enterprise.server.rest.helper.ConfigurationHelper;
import org.rhq.enterprise.server.rest.helper.ResourceCriteriaHelper;

/**
* Class that deals with getting data about resources
Expand Down Expand Up @@ -229,6 +230,35 @@ public Response updateResource(@ApiParam("Id of the resource to import") @PathPa

}

@GET
@GZIP
@Path("/search")
@ApiError(code = 406, reason = "The passed inventory status was invalid")
@ApiOperation(value = "Search for resources based on query parameters", notes = "You can use any parameters based on org.rhq.core.domain.criteria.ResourceCriteria#addFilter*,"
+ " , but note that only one value is passed to filter method (even if it may support multiple values)."
+ " For example parameter name=value transforms to ResourceCriteria#addFilterName(value),"
+ " parameter pluginName=value transforms to ResourceCriteria#addFilterPluginName(value)."
+ " For some parameter names, following are equivalent : "
+ ResourceCriteriaHelper.PARAM_SHORTCUTS_TEXT
+ ". For example, to find all running AS7 Standalone Servers on a platform do GET /resource/search?parentId=10001&type=JBossAS7 Standalone Server&availability=UP", responseClass = "ResourceWithType")
public Response searchResourcesByQuery(
@ApiParam("Page size for paging") @QueryParam("ps") @DefaultValue("20") int pageSize,
@ApiParam("Page for paging, 0-based") @QueryParam("page") @DefaultValue("0") Integer page,
@ApiParam("Enable strict filtering") @QueryParam("strict") @DefaultValue("false") boolean strict,
@Context HttpHeaders headers,
@Context UriInfo uriInfo) {

ResourceCriteria criteria = ResourceCriteriaHelper.create(uriInfo.getQueryParameters());
criteria.addSortName(PageOrdering.ASC);

if (page != null) {
criteria.setPaging(page, pageSize);
}
PageList<Resource> ret = resMgr.findResourcesByCriteria(caller, criteria);
Response.ResponseBuilder builder = getResponseBuilderForResourceList(headers, uriInfo, ret);
return builder.build();
}

@GET @GZIP
@Path("/")
@ApiError(code = 406, reason = "The passed inventory status was invalid")
Expand Down
@@ -0,0 +1,184 @@
/*
* RHQ Management Platform
* Copyright (C) 2005-2014 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation, and/or the GNU Lesser
* General Public License, version 2.1, also as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License and the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.rhq.enterprise.server.rest.helper;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.ws.rs.core.MultivaluedMap;

import org.rhq.core.domain.criteria.ResourceCriteria;
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.resource.InventoryStatus;
import org.rhq.core.domain.resource.ResourceCategory;
import org.rhq.enterprise.server.rest.BadArgumentException;

/**
* this helper class builds {@link ResourceCriteria based on query parameters map}
* @author lzoubek
*
*/
public class ResourceCriteriaHelper {

/**
* special parameters are either ignored or handled specialy
*/
private static final List<String> SPECIAL_PARAMS = Arrays.asList("page", "ps", "strict");

/**
* mapping param shortcutName to param full name (this map gets filled in class constructor)
*/
private static final Map<String, String> PARAM_SHORTCUTS = new LinkedHashMap<String, String>();

/**
* we store parameter name shortcuts as text for API documentation purposes
*/
public static final String PARAM_SHORTCUTS_TEXT = "status=inventoryStatus, availability=currentAvailability, category=resourceCategories, plugin=pluginName, parentId=parentResourceId, parentName=parentResourceName, type=resourceTypeName";
static {
String[] pairs = PARAM_SHORTCUTS_TEXT.split(", ");
for (int i = 0; i < pairs.length; i++) {
String[] pair = pairs[i].split("=");
PARAM_SHORTCUTS.put(pair[0], pair[1]);
}
}

/**
* creates new criteria instance based on given params. Currently we support single value addFilterXXX functions, and strict parameter. Paging is ignored.
* @param params query parameters
* @return resource criteria
*/
public static ResourceCriteria create(MultivaluedMap<String,String> params) {
ResourceCriteria criteria = new ResourceCriteria();
criteria.clearPaging();
Method[] methods = ResourceCriteria.class.getMethods();
for (Entry<String, List<String>> e : params.entrySet()) {
String value = params.getFirst(e.getKey());
if (value == null) {
continue;
}
String paramName = paramName(e.getKey());
if (SPECIAL_PARAMS.contains(e.getKey())) {
try {
handleSpecialParam(criteria, e.getKey(), value);
} catch (Exception ex) {
throw new BadArgumentException("Unable to parse [" + e.getKey() + "] value [" + value
+ "] is not valid");
}
} else {
String filterName = "addFilter" + paramName.substring(0, 1).toUpperCase() + paramName.substring(1);
Method m = findMethod(methods, filterName);
if (m != null) {
try {
m.invoke(criteria, getValue(m, paramName, value));
} catch (BadArgumentException bae) {
throw bae;
} catch (Exception ex) {
throw new BadArgumentException("Unable to filter by [" + paramName + "] value [" + value
+ "] is not valid for this filter");
}

} else {
throw new BadArgumentException("Unable to filter by [" + paramName + "] : filter does not exist");
}
}

}
return criteria;
}

private static String paramName(String name) {
String newName = PARAM_SHORTCUTS.get(name);
return newName == null ? name : newName;
}

private static void handleSpecialParam(ResourceCriteria criteria, String filter, String value) {
if ("strict".equals(filter)) {
criteria.setStrict(Boolean.parseBoolean(value));
return;
}
// skip ps and page .. we ignore those
}

private static Object getValue(Method m, String filter, String value) {
Class<?> parameterType = m.getParameterTypes()[0];
if (parameterType.isArray()) {
parameterType = parameterType.getComponentType();
}
if (parameterType.isEnum()) {
return enumParamValue(filter, value);
}
if (parameterType.isAssignableFrom(Integer.class)
|| (parameterType.isPrimitive() && "int".equals(parameterType.getName()))) {
return Integer.parseInt(value);
}
if (parameterType.isAssignableFrom(Long.class)
|| (parameterType.isPrimitive() && "long".equals(parameterType.getName()))) {
return Long.parseLong(value);
}

return value;
}

private static Object enumParamValue(String filter, String value) {
if ("inventoryStatus".equals(filter)) {
try {
return InventoryStatus.valueOf(value.toUpperCase());
} catch (Exception ex) {
throw new BadArgumentException(filter, "Value " + value + " is not in the list of allowed values: "
+ Arrays.toString(InventoryStatus.values()));
}

}
if ("currentAvailability".equals(filter)) {
try {
return AvailabilityType.valueOf(value.toUpperCase());
} catch (Exception ex) {
throw new BadArgumentException(filter, "Value " + value + " is not in the list of allowed values: "
+ Arrays.toString(AvailabilityType.values()));
}
}
if ("resourceCategories".equals(filter)) {
try {
return new ResourceCategory[] { ResourceCategory.valueOf(value.toUpperCase()) };
} catch (Exception ex) {
throw new BadArgumentException(filter, "Value " + value + " is not in the list of allowed values: "
+ Arrays.toString(ResourceCategory.values()));
}
}
return null;

}

private static Method findMethod(Method[] methods, String name) {
for (Method m : methods) {
if (m.getName().equals(name)) {
return m;
}
}
return null;
}
}
@@ -0,0 +1,104 @@
/*
* RHQ Management Platform
* Copyright (C) 2005-2014 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation, and/or the GNU Lesser
* General Public License, version 2.1, also as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License and the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.rhq.enterprise.server.rest.helper;

import javax.ws.rs.core.MultivaluedMap;

import org.testng.annotations.Test;

import org.jboss.resteasy.specimpl.MultivaluedMapImpl;

import org.rhq.core.domain.resource.ResourceCategory;
import org.rhq.enterprise.server.rest.BadArgumentException;

@Test
public class ResourceCriteriaHelperTest {

@Test(expectedExceptions = { BadArgumentException.class }, expectedExceptionsMessageRegExp = ".*does not exist.*")
public void testInvalidParam() {
MultivaluedMap<String, String> params = new MultivaluedMapImpl<String, String>();
params.putSingle("foo", "bar");
ResourceCriteriaHelper.create(params);
}

@Test(expectedExceptions = { BadArgumentException.class }, expectedExceptionsMessageRegExp = ".*inventoryStatus is bad.*")
public void testEnumParamInvalidValue() {
MultivaluedMap<String, String> params = new MultivaluedMapImpl<String, String>();
params.putSingle("inventoryStatus", "FOO");
ResourceCriteriaHelper.create(params);
}

@Test(expectedExceptions = { BadArgumentException.class }, expectedExceptionsMessageRegExp = ".*is not valid.*")
public void testNumericParamInvalidValue() {
MultivaluedMap<String, String> params = new MultivaluedMapImpl<String, String>();
params.putSingle("id", "FOO");
ResourceCriteriaHelper.create(params);
}

public void testSpecialParamValue() {
MultivaluedMap<String, String> params = new MultivaluedMapImpl<String, String>();
params.putSingle("strict", "FOO");
assert ResourceCriteriaHelper.create(params).isStrict() == false;
params.clear();
params.putSingle("strict", "true");
assert ResourceCriteriaHelper.create(params).isStrict() == true;
}

public void testNumericParamValue() {
MultivaluedMap<String, String> params = new MultivaluedMapImpl<String, String>();
params.putSingle("id", "1234");
params.putSingle("startItime", "2");
assert ResourceCriteriaHelper.create(params) != null;
}

public void testEnumParamValue() {
MultivaluedMap<String, String> params = new MultivaluedMapImpl<String, String>();
params.putSingle("inventoryStatus", "NEW");
assert ResourceCriteriaHelper.create(params) != null;
}

public void testStringParamValue() {
MultivaluedMap<String, String> params = new MultivaluedMapImpl<String, String>();
params.putSingle("name", "1234");
assert ResourceCriteriaHelper.create(params) != null;
}

public void testStringParamWithoutValue() {
MultivaluedMap<String, String> params = new MultivaluedMapImpl<String, String>();
params.put("name", null);
assert ResourceCriteriaHelper.create(params) != null;
}

public void testParamShortcuts() {
MultivaluedMap<String, String> params = new MultivaluedMapImpl<String, String>();
params.putSingle("status", "NEW");
params.putSingle("availability", "up");
params.putSingle("type", "FOO");
params.putSingle("category", ResourceCategory.SERVER.getName());
params.putSingle("plugin", "FOO");
params.putSingle("type", "FOO");
params.putSingle("parentName", "FOO");
params.putSingle("parentId", "1");
assert ResourceCriteriaHelper.create(params) != null;
}
}

0 comments on commit e1e7bc3

Please sign in to comment.