Skip to content

Commit

Permalink
RESTWS-953:Support setting values of type "Location" (#617)
Browse files Browse the repository at this point in the history
  • Loading branch information
mogoodrich authored Aug 12, 2024
1 parent 1390386 commit 4f3e8ec
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.openmrs.ConceptNumeric;
import org.openmrs.Drug;
import org.openmrs.Encounter;
import org.openmrs.Location;
import org.openmrs.Obs;
import org.openmrs.Patient;
import org.openmrs.api.APIException;
Expand All @@ -35,6 +36,7 @@
import org.openmrs.module.webservices.rest.web.ConversionUtil;
import org.openmrs.module.webservices.rest.web.RequestContext;
import org.openmrs.module.webservices.rest.web.RestConstants;
import org.openmrs.module.webservices.rest.web.RestUtil;
import org.openmrs.module.webservices.rest.web.annotation.PropertyGetter;
import org.openmrs.module.webservices.rest.web.annotation.PropertySetter;
import org.openmrs.module.webservices.rest.web.annotation.Resource;
Expand Down Expand Up @@ -315,6 +317,7 @@ public Object getValue(Obs obs) throws ConversionException {
return Context.getLocationService().getLocation(new Integer(obs.getValueText()));
}
catch (NumberFormatException e) {
// TDOO; we really shouldn't be supporting two ways of storing a location obs, should only use the location id
return Context.getLocationService().getLocationByUuid(obs.getValueText());
}
} else {
Expand Down Expand Up @@ -394,23 +397,28 @@ public static void setConcept(Obs obs, Object value) {
@PropertySetter("value")
public static void setValue(Obs obs, Object value) throws ParseException, ConversionException, IOException {
if (value != null) {

// special case for complex obs
if (obs.isComplex()) {
byte[] bytes = DatatypeConverter.parseBase64Binary(value.toString());

ComplexData complexData = new ComplexData(obs.getUuid() + ".raw", new ByteArrayInputStream(bytes));
obs.setComplexData(complexData);
} else if (obs.getConcept().getDatatype().isCoded()) {
// setValueAsString is not implemented for coded obs (in core)

//We want clients to be able to fetch a coded value in one rest call
//and set the returned payload as the obs value
return;
}

// special case for data type coded (setValueAsString is not implemented for coded obs (in core))
if (obs.getConcept().getDatatype().isCoded()) {
// We want clients to be able to fetch a coded value in one rest call
// and set the returned payload as the obs value
// (ie support setting based on posting the entire REST rep or just the concept uuid)
if (value instanceof Map) {
Object uuid = ((Map) value).get(RestConstants.PROPERTY_UUID);
if (uuid != null) {
value = uuid.toString();
}
}

Concept valueCoded = (Concept) ConversionUtil.convert(value, Concept.class);
if (valueCoded == null) {
//try checking if this this is value drug
Expand All @@ -421,58 +429,86 @@ public static void setValue(Obs obs, Object value) throws ParseException, Conver
} else {
throw new ObjectNotFoundException(obs.getConcept().getName().getName() + ":" + value.toString());
}

} else {
obs.setValueCoded(valueCoded);
}

} else {
if (obs.getConcept().isNumeric()) {
//get the actual persistent object rather than the hibernate proxy
ConceptNumeric concept = Context.getConceptService().getConceptNumeric(obs.getConcept().getId());
String units = concept.getUnits();
if (StringUtils.isNotBlank(units)) {
String originalValue = value.toString().trim();
if (originalValue.endsWith(units))
value = originalValue.substring(0, originalValue.indexOf(units)).trim();
else {
//check that that this value has no invalid units
try {
Double.parseDouble(originalValue);
}
catch (NumberFormatException e) {
throw new APIException(originalValue + " has invalid units", e);
}
return;
}

// special case for Location
String potentialLocationUuid = null;

// if this is a representation of an object, get the uuid property as potential location uuid
if (value instanceof Map) {
Object uuid = ((Map) value).get(RestConstants.PROPERTY_UUID);
if (uuid != null) {
potentialLocationUuid = uuid.toString();
}
}
else {
// otherwise, we will test if the value itself is a location uuid
potentialLocationUuid = value.toString();
}

// if there is a potential uuid, see if there is a matching location, and,if so, set the value text as the primary key
if (RestUtil.isUuid(potentialLocationUuid)) {
Location location = Context.getLocationService().getLocationByUuid(potentialLocationUuid);
if (location != null) {
obs.setValueText(location.getLocationId().toString());
obs.setComment("org.openmrs.Location");
return;
}
}

// handle all other types using obs.setValueAsString after special conversions for numeric and boolean
if (obs.getConcept().isNumeric()) {
//get the actual persistent object rather than the hibernate proxy
ConceptNumeric concept = Context.getConceptService().getConceptNumeric(obs.getConcept().getId());
String units = concept.getUnits();
if (StringUtils.isNotBlank(units)) {
String originalValue = value.toString().trim();
if (originalValue.endsWith(units))
value = originalValue.substring(0, originalValue.indexOf(units)).trim();
else {
//check that this value has no invalid units
try {
Double.parseDouble(originalValue);
}
catch (NumberFormatException e) {
throw new APIException(originalValue + " has invalid units", e);
}
}
} else if (obs.getConcept().getDatatype().isBoolean()) {
if (value instanceof Concept) {
value = ((Concept) value).getUuid();
}
} else if (obs.getConcept().getDatatype().isBoolean()) {
if (value instanceof Concept) {
value = ((Concept) value).getUuid();
}
if (value.equals(Context.getConceptService().getTrueConcept().getUuid())) {
value = true;
} else if (value.equals(Context.getConceptService().getFalseConcept().getUuid())) {
value = false;
} else if (!value.getClass().isAssignableFrom(Boolean.class)) {
List<String> trueValues = Arrays.asList("true", "1", "on", "yes");
List<String> falseValues = Arrays.asList("false", "0", "off", "no");

String val = value.toString().trim().toLowerCase();
if (trueValues.contains(val)) {
value = Boolean.TRUE;
} else if (falseValues.contains(val)) {
value = Boolean.FALSE;
}
if (value.equals(Context.getConceptService().getTrueConcept().getUuid())) {
value = true;
} else if (value.equals(Context.getConceptService().getFalseConcept().getUuid())) {
value = false;
} else if (!value.getClass().isAssignableFrom(Boolean.class)) {
List<String> trueValues = Arrays.asList("true", "1", "on", "yes");
List<String> falseValues = Arrays.asList("false", "0", "off", "no");

String val = value.toString().trim().toLowerCase();
if (trueValues.contains(val)) {
value = Boolean.TRUE;
} else if (falseValues.contains(val)) {
value = Boolean.FALSE;
}

if (!(Boolean.TRUE.equals(value) || Boolean.FALSE.equals(value))) {
throw new ConversionException("Unexpected value: " + value + " set as the value of boolean. "
+ trueValues + falseValues + ", ConceptService.getTrueConcept or "
+ ", ConceptService.getFalseConcept expected");
}

if (!(Boolean.TRUE.equals(value) || Boolean.FALSE.equals(value))) {
throw new ConversionException("Unexpected value: " + value + " set as the value of boolean. "
+ trueValues + falseValues + ", ConceptService.getTrueConcept or "
+ ", ConceptService.getFalseConcept expected");
}
}
obs.setValueAsString(value.toString());
}
obs.setValueAsString(value.toString());


} else {
throw new APIException("The value for an observation cannot be null");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,46 @@ public void setValue_shouldReturnDrug() throws Exception {
ObsResource1_8.setValue(obs, drugUuid);
assertEquals(drug, new ObsResource1_8().getValue(obs));
}


@Test
public void setValue_shouldSupportPassingInFullConceptRep() throws Exception {
Obs obs = new Obs();
obs.setConcept(Context.getConceptService().getConceptByUuid("89ca642a-dab6-4f20-b712-e12ca4fc6d36"));
Concept valueCoded = Context.getConceptService().getConceptByUuid("32d3611a-6699-4d52-823f-b4b788bac3e3");
ObsResource1_8.setValue(obs, ConversionUtil.convertToRepresentation(valueCoded, Representation.FULL));
assertEquals(valueCoded, new ObsResource1_8().getValue(obs));
}

@Test
public void setValue_shouldReturnLocation() throws Exception {
Obs obs = new Obs();
Concept concept = Context.getConceptService().getConceptByUuid("96408258-000b-424e-af1a-403919332938");
obs.setConcept(concept);
String locationUuid = "8d6c993e-c2cc-11de-8d13-0010c6dffd0f";
Location location = Context.getLocationService().getLocationByUuid(locationUuid);
ObsResource1_8.setValue(obs, locationUuid);
assertEquals(location, new ObsResource1_8().getValue(obs));
}

@Test
public void setValue_shouldSupportPassingInFullLocationRep() throws Exception {
Obs obs = new Obs();
obs.setConcept(Context.getConceptService().getConceptByUuid("96408258-000b-424e-af1a-403919332938"));
String locationUuid = "8d6c993e-c2cc-11de-8d13-0010c6dffd0f";
Location location = Context.getLocationService().getLocationByUuid(locationUuid);
ObsResource1_8.setValue(obs, ConversionUtil.convertToRepresentation(location, Representation.FULL));
assertEquals(location, new ObsResource1_8().getValue(obs));
}

@Test
public void setValue_shouldReturnValueText() throws Exception {
Obs obs = new Obs();
Concept concept = Context.getConceptService().getConceptByUuid("96408258-000b-424e-af1a-403919332938");
obs.setConcept(concept);
ObsResource1_8.setValue(obs, "cheesecake");
assertEquals("cheesecake", new ObsResource1_8().getValue(obs));
}

@Test
public void setValue_shouldReturnUuidForConceptFalse() throws Exception {
Obs obs = new Obs();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
Expand Down Expand Up @@ -64,6 +65,9 @@ public class RestUtil implements GlobalPropertyListener {
private static Log log = LogFactory.getLog(RestUtil.class);

private static boolean contextEnabled = true;

private static final Pattern UUID_REGEX =
Pattern.compile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$");

/**
* Looks up the admin defined global property for the system limit
Expand Down Expand Up @@ -936,4 +940,8 @@ public static Class<?> getSupportedClass(Resource resource) {
.supportedClass();
}
}

public static boolean isUuid(String str) {
return StringUtils.isNotBlank(str) && UUID_REGEX.matcher(str).matches();
}
}

0 comments on commit 4f3e8ec

Please sign in to comment.