Skip to content
This repository was archived by the owner on Feb 9, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/org/labkey/snd/PropertyDescriptorMixin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.labkey.snd;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.labkey.api.gwt.client.util.StringProperty;

/**
* Configures the fields that are not returned when serializing a GWTPropertyDescriptor.
* Ideally we would just add the @JsonIgnore annotations to GWTPropertyDescriptor directly,
* but the GWT compiler would need to have jackson on the classpath which isn't
* necessary.
*/
@JsonIgnoreProperties({
"max",
"min",
"validators", // Must handle separately
"lookupValues",
"defaultTypeValue",
"sortOrder",
"value"
})
public abstract class PropertyDescriptorMixin
{
PropertyDescriptorMixin(@JsonProperty("URL") StringProperty url)
{ }
@JsonProperty("URL")
abstract void setURL(String url); // rename property on deserialize
}
16 changes: 16 additions & 0 deletions src/org/labkey/snd/PropertyValidatorMixin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.labkey.snd;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

/**
* Configures the fields that are not returned when serializing a GWTPropertyValidator.
* Ideally we would just add the @JsonIgnore annotations to GWTPropertyValidator directly,
* but the GWT compiler would need to have jackson on the classpath which isn't
* necessary.
*/
@JsonIgnoreProperties({
"type" // Done outside jackson
})
public abstract class PropertyValidatorMixin
{
}
128 changes: 118 additions & 10 deletions src/org/labkey/snd/SNDController.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.labkey.snd;

import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.json.JSONArray;
import org.json.JSONObject;
import org.labkey.api.action.ApiResponse;
Expand All @@ -27,9 +29,11 @@
import org.labkey.api.action.SimpleApiJsonForm;
import org.labkey.api.action.SimpleViewAction;
import org.labkey.api.action.SpringActionController;
import org.labkey.api.exp.api.ExperimentService;
import org.labkey.api.gwt.client.DefaultValueType;
import org.labkey.api.gwt.client.model.GWTConditionalFormat;
import org.labkey.api.gwt.client.model.GWTPropertyDescriptor;
import org.labkey.api.gwt.client.model.GWTPropertyValidator;
import org.labkey.api.gwt.client.model.PropertyValidatorType;
import org.labkey.api.module.Module;
import org.labkey.api.security.RequiresLogin;
import org.labkey.api.security.RequiresPermission;
Expand All @@ -48,6 +52,7 @@
import org.labkey.api.snd.SNDService;
import org.labkey.api.snd.SuperPackage;
import org.labkey.api.util.DateUtil;
import org.labkey.api.util.JsonUtil;
import org.labkey.api.util.URLHelper;
import org.labkey.api.view.ActionURL;
import org.labkey.api.view.JspView;
Expand All @@ -58,6 +63,7 @@
import org.springframework.validation.Errors;
import org.springframework.web.servlet.ModelAndView;

import java.io.IOException;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
Expand All @@ -80,14 +86,86 @@ public SNDController()
setActionResolver(_actionResolver);
}

private Map<GWTPropertyDescriptor, Object> getExtraFields(JSONArray jsonExtras)
private GWTPropertyValidator jsonToPropertyValidator(JSONObject obj) throws IOException
{
GWTPropertyValidator validator = createPropertyObjectMapper()
.readerFor(GWTPropertyValidator.class)
.readValue(obj.toString());

validator.setType(PropertyValidatorType.getType(obj.getString("type")));
return validator;
}

private GWTConditionalFormat jsonToConditionalFormatter(JSONObject obj) throws IOException
{
return createPropertyObjectMapper()
.readerFor(GWTConditionalFormat.class)
.readValue(obj.toString());
}

private GWTPropertyDescriptor jsonToPropertyDescriptor(JSONObject obj) throws IOException
{
GWTPropertyDescriptor prop = createPropertyObjectMapper()
.readerFor(GWTPropertyDescriptor.class)
.readValue(obj.toString());

// property validators
JSONArray jsonValidators = obj.optJSONArray("validators");
if(null != jsonValidators)
{
List<GWTPropertyValidator> validators = new ArrayList<>();
for (int i = 0; i < jsonValidators.length(); i++)
{
JSONObject jsonValidator = jsonValidators.getJSONObject(i);
if (null != jsonValidator)
{
validators.add(jsonToPropertyValidator(jsonValidator));
}
}
prop.setPropertyValidators(validators);
}

//conditional formats
JSONArray conditionalFormats = obj.optJSONArray("conditionalFormats");
if (null != conditionalFormats)
{
List<GWTConditionalFormat> conditionalFormatters = new ArrayList<>();
for (int j = 0; j < conditionalFormats.length(); j++)
{
JSONObject conditionalFormatter = conditionalFormats.getJSONObject(j);
if (null != conditionalFormatter)
{
conditionalFormatters.add(jsonToConditionalFormatter(conditionalFormatter));
}
}
prop.setConditionalFormats(conditionalFormatters);
}

return prop;
}

static void configureObjectMapper(ObjectMapper om)
{
om.addMixIn(GWTPropertyDescriptor.class, PropertyDescriptorMixin.class);
om.addMixIn(GWTPropertyValidator.class, PropertyValidatorMixin.class);
om.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
}

protected ObjectMapper createPropertyObjectMapper()
{
ObjectMapper mapper = JsonUtil.DEFAULT_MAPPER.copy();
configureObjectMapper(mapper);
return mapper;
}

private Map<GWTPropertyDescriptor, Object> getExtraFields(JSONArray jsonExtras) throws IOException
{
Map<GWTPropertyDescriptor, Object> extras = new HashMap<>();
JSONObject extra;
for (int e = 0; e < jsonExtras.length(); e++)
{
extra = jsonExtras.getJSONObject(e);
extras.put(ExperimentService.get().convertJsonToPropertyDescriptor(extra), extra.get("value"));
extras.put(jsonToPropertyDescriptor(extra), extra.get("value"));
}

return extras;
Expand All @@ -106,7 +184,7 @@ public URLHelper getURL(Object o, Errors errors)
@RequiresPermission(AdminPermission.class)
public class SavePackageAction extends MutatingApiAction<SimpleApiJsonForm>
{
private GWTPropertyDescriptor convertJsonToPropertyDescriptor(JSONObject json, BindException errors)
private GWTPropertyDescriptor convertJsonToPropertyDescriptor(JSONObject json, BindException errors) throws IOException
{
String rangeUri = json.getString("rangeURI");
String format = json.getString("format");
Expand Down Expand Up @@ -144,7 +222,7 @@ else if (rangeUri.equals("string"))
String defaultValue = (String) json.get("defaultValue");
json.put("defaultValue", defaultValue);

return ExperimentService.get().convertJsonToPropertyDescriptor(json);
return jsonToPropertyDescriptor(json);
}

@Override
Expand All @@ -166,7 +244,14 @@ public ApiResponse execute(SimpleApiJsonForm form, BindException errors)
JSONArray jsonExtras = json.optJSONArray("extraFields");
if (null != jsonExtras)
{
pkg.setExtraFields(getExtraFields(jsonExtras));
try
{
pkg.setExtraFields(getExtraFields(jsonExtras));
}
catch (IOException e)
{
errors.reject(ERROR_MSG, "Extra fields could not be parsed: " + e.getMessage());
}
}

// Get categories
Expand Down Expand Up @@ -197,7 +282,14 @@ public ApiResponse execute(SimpleApiJsonForm form, BindException errors)
break;
}
attNames.add(name);
pds.add(convertJsonToPropertyDescriptor(attribs.getJSONObject(i), errors));
try
{
pds.add(convertJsonToPropertyDescriptor(attribs.getJSONObject(i), errors));
}
catch (IOException e)
{
errors.reject("Unable to parse attribute property: " + e.getMessage());
}
}
pkg.setAttributes(pds);
}
Expand Down Expand Up @@ -987,7 +1079,16 @@ public ApiResponse execute(SimpleApiJsonForm form, BindException errors)
List<EventData> eventData = null;
JSONArray eventDataJson = json.has("eventData") ? json.getJSONArray("eventData") : null;
if (eventDataJson != null)
eventData = parseEventData(eventDataJson);
{
try
{
eventData = parseEventData(eventDataJson);
}
catch (IOException e)
{
errors.reject(ERROR_MSG, "Event JSON data could not be parsed: " + e.getMessage());
}
}

Event event = new Event(eventId, subjectId, date, projectIdrev, note, eventData, getContainer());
event.setQcState(qcState);
Expand All @@ -996,7 +1097,14 @@ public ApiResponse execute(SimpleApiJsonForm form, BindException errors)
JSONArray jsonExtras = json.optJSONArray("extraFields");
if (null != jsonExtras)
{
event.setExtraFields(getExtraFields(jsonExtras));
try
{
event.setExtraFields(getExtraFields(jsonExtras));
}
catch (IOException e)
{
errors.reject(ERROR_MSG, "Unable to parse extra fields: " + e.getMessage());
}
}

Event savedEvent = SNDService.get().saveEvent(getContainer(), getUser(), event, validateOnly);
Expand All @@ -1014,7 +1122,7 @@ public ApiResponse execute(SimpleApiJsonForm form, BindException errors)
return response;
}

private List<EventData> parseEventData(JSONArray eventDataJson)
private List<EventData> parseEventData(JSONArray eventDataJson) throws IOException
{
List<EventData> eventDataList = new ArrayList<>();

Expand Down