/**
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
*
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
* graphic logo is a trademark of OpenMRS Inc.
*/
package org.openmrs.module.webservices.rest.web;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.junit.Assert;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.junit.Ignore;
import org.junit.Test;
import org.openmrs.User;
import org.openmrs.module.webservices.rest.web.annotation.PropertySetter;
import org.openmrs.module.webservices.rest.web.annotation.RepHandler;
import org.openmrs.module.webservices.rest.web.representation.Representation;
import org.openmrs.module.webservices.rest.web.v1_0.resource.openmrs1_8.UserResource1_8;
import org.openmrs.module.webservices.rest.web.resource.impl.BaseDelegatingResource;
import org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription;
import org.openmrs.util.Reflect;
import org.openmrs.web.test.BaseModuleWebContextSensitiveTest;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.util.ReflectionUtils;
/**
* Contains tests for Representation Descriptions of all resources
*/
@Ignore
public class DelegatingCrudResourceTest extends BaseModuleWebContextSensitiveTest {
/**
* This test looks at all subclasses of DelegatingCrudResource, and test all {@link RepHandler}
* methods to make sure they are all capable of running without exceptions. It also checks that
*/
@SuppressWarnings("rawtypes")
@Test
@Ignore
public void testAllReprsentationDescriptions() throws Exception {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(true);
//only match subclasses of BaseDelegatingResource
provider.addIncludeFilter(new AssignableTypeFilter(BaseDelegatingResource.class));
// scan in org.openmrs.module.webservices.rest.web.resource package
Set<BeanDefinition> components = provider
.findCandidateComponents("org.openmrs.module.webservices.rest.web.resource");
if (CollectionUtils.isEmpty(components))
Assert.fail("Faile to load any resource classes");
for (BeanDefinition component : components) {
Class resourceClass = Class.forName(component.getBeanClassName());
for (Method method : ReflectionUtils.getAllDeclaredMethods(resourceClass)) {
ParameterizedType parameterizedType = (ParameterizedType) resourceClass.getGenericSuperclass();
Class openmrsClass = (Class) parameterizedType.getActualTypeArguments()[0];
//User Resource is special in that the Actual parameterized Type isn't a standard domain object, so we also
//need to look up fields and methods from the org.openmrs.User class
boolean isUserResource = resourceClass.equals(UserResource1_8.class);
List<Object> refDescriptions = new ArrayList<Object>();
if (method.getName().equals("getRepresentationDescription")
&& method.getDeclaringClass().equals(resourceClass)) {
//get all the rep definitions for all representations
refDescriptions.add(method.invoke(resourceClass.newInstance(), new Object[] { Representation.REF }));
refDescriptions.add(method.invoke(resourceClass.newInstance(), new Object[] { Representation.DEFAULT }));
refDescriptions.add(method.invoke(resourceClass.newInstance(), new Object[] { Representation.FULL }));
}
for (Object value : refDescriptions) {
if (value != null) {
DelegatingResourceDescription des = (DelegatingResourceDescription) value;
for (String key : des.getProperties().keySet()) {
if (!key.equals("uri") && !key.equals("display") && !key.equals("auditInfo")) {
boolean hasFieldOrPropertySetter = (ReflectionUtils.findField(openmrsClass, key) != null);
if (!hasFieldOrPropertySetter) {
hasFieldOrPropertySetter = hasSetterMethod(key, resourceClass);
if (!hasFieldOrPropertySetter && isUserResource)
hasFieldOrPropertySetter = (ReflectionUtils.findField(User.class, key) != null);
}
if (!hasFieldOrPropertySetter)
hasFieldOrPropertySetter = hasSetterMethod(key, resourceClass);
//TODO replace this hacky way that we are using to check if there is a get method for a
//collection that has no actual getter e.g activeIdentifers and activeAttributes for Patient
if (!hasFieldOrPropertySetter) {
hasFieldOrPropertySetter = (ReflectionUtils.findMethod(openmrsClass,
"get" + StringUtils.capitalize(key)) != null);
if (!hasFieldOrPropertySetter && isUserResource)
hasFieldOrPropertySetter = (ReflectionUtils.findMethod(User.class, "get"
+ StringUtils.capitalize(key)) != null);
}
if (!hasFieldOrPropertySetter)
hasFieldOrPropertySetter = isallowedMissingProperty(resourceClass, key);
Assert.assertTrue("No property found for '" + key + "' for " + openmrsClass
+ " nor setter method on resource " + resourceClass, hasFieldOrPropertySetter);
}
}
}
}
}
}
}
/**
* Convenience method that checks of the specified resource class has a method for setting the
* given property
*
* @param propName
* @param resource
* @return
*/
@SuppressWarnings("rawtypes")
private static boolean hasSetterMethod(String propName, Class resourceClass) {
for (Method candidate : resourceClass.getMethods()) {
PropertySetter ann = candidate.getAnnotation(PropertySetter.class);
if (ann != null && ann.value().equals(propName)) {
return true;
}
}
return false;
}
/**
* Convenience method that checks if the specified property is included among the allowed
* missing properties of the given resource class via reflection
*
* @param clazz
* @param fieldName
* @return
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws InstantiationException
*/
@SuppressWarnings("rawtypes")
private static boolean isallowedMissingProperty(Class resourceClass, String propName) throws IllegalArgumentException,
IllegalAccessException, InstantiationException {
List<Field> fields = Reflect.getAllFields(resourceClass);
if (CollectionUtils.isNotEmpty(fields)) {
for (Field field : fields) {
if (field.getName().equals("allowedMissingProperties"))
return ((Set) field.get(resourceClass.newInstance())).contains(propName);
}
}
return false;
}
}