Skip to content

Commit b46363a

Browse files
committed
Added the feature of including properties from @PropertyGetter as well
1 parent 86a6735 commit b46363a

File tree

2 files changed

+128
-1
lines changed

2 files changed

+128
-1
lines changed

omod-common/src/main/java/org/openmrs/module/webservices/rest/web/api/impl/SchemaIntrospectionServiceImpl.java

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import org.apache.commons.logging.Log;
2222
import org.apache.commons.logging.LogFactory;
2323
import org.openmrs.api.impl.BaseOpenmrsService;
24+
import org.openmrs.module.webservices.rest.web.annotation.PropertyGetter;
25+
import org.openmrs.module.webservices.rest.web.annotation.PropertySetter;
2426
import org.openmrs.module.webservices.rest.web.api.SchemaIntrospectionService;
2527
import org.openmrs.module.webservices.rest.web.resource.api.Resource;
2628
import org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceHandler;
@@ -139,7 +141,52 @@ public Map<String, String> discoverAvailableProperties(Class<?> delegateType) {
139141
@Override
140142
public Map<String, String> discoverResourceProperties(Resource resource) {
141143
Class<?> delegateType = getDelegateType(resource);
142-
return discoverAvailableProperties(delegateType);
144+
Map<String, String> properties = discoverAvailableProperties(delegateType);
145+
146+
// Also discover properties defined by PropertyGetter and PropertySetter annotations
147+
if (resource != null) {
148+
discoverAnnotatedProperties(resource.getClass(), properties);
149+
}
150+
151+
return properties;
152+
}
153+
154+
/**
155+
* Discovers properties defined by PropertyGetter and PropertySetter annotations in a resource class
156+
* and its superclasses
157+
*
158+
* @param resourceClass The resource class to scan for annotations
159+
* @param properties The map to add discovered properties to
160+
*/
161+
private void discoverAnnotatedProperties(Class<?> resourceClass, Map<String, String> properties) {
162+
// Process the current class and all its superclasses
163+
Class<?> currentClass = resourceClass;
164+
while (currentClass != null && !currentClass.equals(Object.class)) {
165+
// Process all methods in the class
166+
for (Method method : currentClass.getDeclaredMethods()) {
167+
// Check for PropertyGetter annotation
168+
PropertyGetter getter = method.getAnnotation(PropertyGetter.class);
169+
if (getter != null) {
170+
String propertyName = getter.value();
171+
Type returnType = method.getGenericReturnType();
172+
properties.put(propertyName, getTypeName(returnType));
173+
}
174+
175+
// Check for PropertySetter annotation
176+
PropertySetter setter = method.getAnnotation(PropertySetter.class);
177+
if (setter != null && method.getParameterTypes().length > 1) {
178+
String propertyName = setter.value();
179+
// For setters, use the type of the second parameter (after the delegate)
180+
Type paramType = method.getGenericParameterTypes()[1];
181+
// Only add if not already discovered through a getter
182+
if (!properties.containsKey(propertyName)) {
183+
properties.put(propertyName, getTypeName(paramType));
184+
}
185+
}
186+
}
187+
// Move up to the superclass
188+
currentClass = currentClass.getSuperclass();
189+
}
143190
}
144191

145192
/**

omod-common/src/test/java/org/openmrs/module/webservices/rest/web/api/impl/SchemaIntrospectionServiceImplTest.java

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,18 @@
1818
import static org.hamcrest.Matchers.hasKey;
1919
import static org.hamcrest.Matchers.hasSize;
2020

21+
import java.util.ArrayList;
22+
import java.util.List;
2123
import java.util.Map;
2224

2325
import org.junit.Before;
2426
import org.junit.Test;
2527
import org.openmrs.Patient;
28+
import org.openmrs.PatientIdentifier;
2629
import org.openmrs.Person;
2730
import org.openmrs.module.webservices.rest.web.RequestContext;
31+
import org.openmrs.module.webservices.rest.web.annotation.PropertyGetter;
32+
import org.openmrs.module.webservices.rest.web.annotation.PropertySetter;
2833
import org.openmrs.module.webservices.rest.web.resource.api.Resource;
2934
import org.openmrs.module.webservices.rest.web.resource.impl.DelegatingCrudResource;
3035
import org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription;
@@ -152,6 +157,29 @@ public void discoverResourceProperties_shouldCombineGetDelegateTypeAndDiscoverAv
152157
assertThat(properties, hasKey("birthdate"));
153158
}
154159

160+
/**
161+
* @see SchemaIntrospectionServiceImpl#discoverResourceProperties(Resource)
162+
*/
163+
@Test
164+
public void discoverResourceProperties_shouldIncludePropertiesFromAnnotations() {
165+
TestPatientResourceWithAnnotations resource = new TestPatientResourceWithAnnotations();
166+
Map<String, String> properties = service.discoverResourceProperties(resource);
167+
168+
// Verify standard properties from the delegate type
169+
assertThat(properties, hasKey("uuid"));
170+
assertThat(properties, hasKey("patientId"));
171+
172+
// Verify properties defined by @PropertyGetter annotations
173+
assertThat(properties, hasKey("activeIdentifiers"));
174+
assertThat(properties, hasKey("displayName"));
175+
assertThat(properties, hasEntry("activeIdentifiers", "List<PatientIdentifier>"));
176+
assertThat(properties, hasEntry("displayName", "String"));
177+
178+
// Verify properties defined by @PropertySetter annotations
179+
assertThat(properties, hasKey("preferredName"));
180+
assertThat(properties, hasEntry("preferredName", "String"));
181+
}
182+
155183
/**
156184
* Mock DelegatingCrudResource for testing
157185
*/
@@ -187,6 +215,58 @@ public void delete(Patient delegate, String reason, RequestContext context) thro
187215
}
188216
}
189217

218+
/**
219+
* Mock DelegatingCrudResource for testing PropertyGetter and PropertySetter annotations
220+
*/
221+
private class TestPatientResourceWithAnnotations extends DelegatingCrudResource<Patient> {
222+
223+
@PropertyGetter("activeIdentifiers")
224+
public List<PatientIdentifier> getActiveIdentifiers(Patient patient) {
225+
return new ArrayList<PatientIdentifier>();
226+
}
227+
228+
@PropertyGetter("displayName")
229+
public String getDisplayName(Patient patient) {
230+
return patient.getPersonName().getFullName();
231+
}
232+
233+
@PropertySetter("preferredName")
234+
public void setPreferredName(Patient patient, String name) {
235+
// Implementation not needed for test
236+
}
237+
238+
// Standard resource methods
239+
240+
@Override
241+
public Patient newDelegate() {
242+
return new Patient();
243+
}
244+
245+
@Override
246+
public Patient save(Patient delegate) {
247+
return delegate;
248+
}
249+
250+
@Override
251+
public Patient getByUniqueId(String uniqueId) {
252+
return new Patient();
253+
}
254+
255+
@Override
256+
public void purge(Patient delegate, RequestContext context) {
257+
}
258+
259+
@Override
260+
public DelegatingResourceDescription getRepresentationDescription(Representation rep) {
261+
return null;
262+
}
263+
264+
@Override
265+
public void delete(Patient delegate, String reason, RequestContext context) throws ResourceDoesNotSupportOperationException {
266+
throw new ResourceDoesNotSupportOperationException();
267+
}
268+
}
269+
190270
/**
191271
* Mock DelegatingSubResource for testing
192272
*/

0 commit comments

Comments
 (0)