Skip to content
Open
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
2 changes: 2 additions & 0 deletions src/main/java/com/autotune/analyzer/Analyzer.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ public static void addServlets(ServletContextHandler context) {
context.addServlet(MetadataProfileService.class, ServerContext.UPDATE_METADATA_PROFILE);
context.addServlet(LayerService.class, ServerContext.CREATE_LAYER);
context.addServlet(LayerService.class, ServerContext.LIST_LAYERS);
context.addServlet(LayerService.class, ServerContext.UPDATE_LAYER);
context.addServlet(LayerService.class, ServerContext.DELETE_LAYER);

// Adding UI support API's
context.addServlet(ListNamespaces.class, ServerContext.LIST_NAMESPACES);
Expand Down
20 changes: 10 additions & 10 deletions src/main/java/com/autotune/analyzer/kruizeLayer/Tunable.java
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,13 @@ public void validate() throws InvalidBoundsException {

// Check for mixed configuration (both categorical and numeric)
if (hasCategorical && hasBounds) {
throw new InvalidBoundsException("ERROR: Tunable: " +
throw new InvalidBoundsException(
String.format(AnalyzerErrorConstants.APIErrors.CreateLayerAPI.TUNABLE_MIXED_CONFIG, name));
}

// Check for missing configuration (neither categorical nor numeric)
if (!hasCategorical && !hasBounds) {
throw new InvalidBoundsException("ERROR: Tunable: " +
throw new InvalidBoundsException(
String.format(AnalyzerErrorConstants.APIErrors.CreateLayerAPI.TUNABLE_MISSING_CONFIG, name));
}

Expand All @@ -130,7 +130,7 @@ public void validate() throws InvalidBoundsException {
*/
private void validateBounds() throws InvalidBoundsException {
if (upperBoundValue == null || lowerBoundValue == null) {
throw new InvalidBoundsException("ERROR: Tunable: " +
throw new InvalidBoundsException(
String.format(AnalyzerErrorConstants.APIErrors.CreateLayerAPI.TUNABLE_NULL_BOUNDS, name));
}

Expand All @@ -140,35 +140,35 @@ private void validateBounds() throws InvalidBoundsException {
upper = Double.parseDouble(upperBoundValue);
lower = Double.parseDouble(lowerBoundValue);
} catch (NumberFormatException e) {
throw new InvalidBoundsException("ERROR: Tunable: " +
throw new InvalidBoundsException(
String.format(AnalyzerErrorConstants.APIErrors.CreateLayerAPI.TUNABLE_NON_NUMERIC_BOUNDS,
name, upperBoundValue, lowerBoundValue));
}

if (step == null) {
throw new InvalidBoundsException("ERROR: Tunable: " +
throw new InvalidBoundsException(
String.format(AnalyzerErrorConstants.APIErrors.CreateLayerAPI.TUNABLE_NULL_STEP, name));
}

if (step <= 0) {
throw new InvalidBoundsException("ERROR: Tunable: " +
throw new InvalidBoundsException(
String.format(AnalyzerErrorConstants.APIErrors.CreateLayerAPI.TUNABLE_INVALID_STEP, name, step));
}

if (upper < 0 || lower < 0) {
throw new InvalidBoundsException("ERROR: Tunable: " +
throw new InvalidBoundsException(
String.format(AnalyzerErrorConstants.APIErrors.CreateLayerAPI.TUNABLE_NEGATIVE_BOUNDS,
name, upper, lower));
}

if (lower >= upper) {
throw new InvalidBoundsException("ERROR: Tunable: " +
throw new InvalidBoundsException(
String.format(AnalyzerErrorConstants.APIErrors.CreateLayerAPI.TUNABLE_INVALID_BOUND_RANGE,
name, lower, upper));
}

if (step > (upper - lower)) {
throw new InvalidBoundsException("ERROR: Tunable: " +
throw new InvalidBoundsException(
String.format(AnalyzerErrorConstants.APIErrors.CreateLayerAPI.TUNABLE_STEP_TOO_LARGE,
name, step, (upper - lower)));
}
Expand All @@ -179,7 +179,7 @@ private void validateBounds() throws InvalidBoundsException {
*/
private void validateCategorical() {
if (choices == null || choices.isEmpty()) {
throw new IllegalArgumentException("ERROR: Tunable: " +
throw new IllegalArgumentException(
String.format(AnalyzerErrorConstants.APIErrors.CreateLayerAPI.TUNABLE_EMPTY_CHOICES, name));
}
}
Expand Down
92 changes: 83 additions & 9 deletions src/main/java/com/autotune/analyzer/serviceObjects/Converters.java
Original file line number Diff line number Diff line change
Expand Up @@ -543,15 +543,60 @@ public static MetadataProfile convertInputJSONToCreateMetadataProfile(String inp
return metadataProfile;
}

/**
* Helper method to get a required string field from a JSONObject with proper validation
* @param json The JSONObject to read from
* @param fieldName The field name to retrieve
* @param objectType The type of object (e.g., "Query object", "Label object") for error messages
* @param fieldDescription The description of the field (e.g., "datasource", "string") for error messages
* @return The string value of the field
* @throws IllegalArgumentException if field is missing or null
*/
private static String getRequiredString(JSONObject json, String fieldName, String objectType, String fieldDescription) throws IllegalArgumentException {
if (!json.has(fieldName)) {
throw new IllegalArgumentException(
String.format("%s must have '%s' field", objectType, fieldName));
}
if (json.isNull(fieldName)) {
throw new IllegalArgumentException(
String.format("%s %s cannot be null", objectType.replace(" object", ""), fieldDescription));
}
String value = json.getString(fieldName);
if (value.trim().isEmpty()) {
throw new IllegalArgumentException(
String.format("%s %s cannot be empty", objectType.replace(" object", ""), fieldDescription));
}
return value;
}

public static KruizeLayer convertInputJSONToCreateLayer(String inputData) throws Exception, MonitoringAgentNotSupportedException {
KruizeLayer kruizeLayer = null;

if (inputData != null) {
JSONObject jsonObject = new JSONObject(inputData);

// Validate and get apiVersion
if (!jsonObject.has(AnalyzerConstants.API_VERSION)) {
throw new IllegalArgumentException("Request must have 'apiVersion' field");
}
String apiVersion = jsonObject.getString(AnalyzerConstants.API_VERSION);
if (!apiVersion.contains("/")) {
throw new IllegalArgumentException("Invalid apiVersion format: " + apiVersion);
}

// Validate and get kind
if (!jsonObject.has(AnalyzerConstants.KIND)) {
throw new IllegalArgumentException("Request must have 'kind' field");
}
String kind = jsonObject.getString(AnalyzerConstants.KIND);
if (!"KruizeLayer".equals(kind)) {
throw new IllegalArgumentException("'kind' must be 'KruizeLayer', got: " + kind);
}

// Parse metadata
// Validate and parse metadata
if (!jsonObject.has(AnalyzerConstants.AutotuneObjectConstants.METADATA)) {
throw new IllegalArgumentException("Request must have 'metadata' object");
}
JSONObject metadataObject = jsonObject.optJSONObject(AnalyzerConstants.AutotuneObjectConstants.METADATA);
String name = null;
if (metadataObject != null) {
Expand All @@ -560,7 +605,7 @@ public static KruizeLayer convertInputJSONToCreateLayer(String inputData) throws

// Parse basic layer fields
String layerName = jsonObject.optString(AnalyzerConstants.AutotuneConfigConstants.LAYER_NAME, null);
String details = jsonObject.has(AnalyzerConstants.AutotuneConfigConstants.DETAILS) ? jsonObject.getString(AnalyzerConstants.AutotuneConfigConstants.DETAILS) : null;
String details = jsonObject.optString(AnalyzerConstants.AutotuneConfigConstants.DETAILS, null);

// Parse layer_presence
String presence = null;
Expand All @@ -570,17 +615,21 @@ public static KruizeLayer convertInputJSONToCreateLayer(String inputData) throws

JSONObject layerPresenceObject = jsonObject.optJSONObject("layer_presence");
if (layerPresenceObject != null) {
presence = layerPresenceObject.has("presence") ? layerPresenceObject.getString("presence") : null;
presence = layerPresenceObject.optString("presence", null);

// Parse queries array if present
if (layerPresenceObject.has("queries")) {
JSONArray queriesArray = layerPresenceObject.getJSONArray("queries");
queries = new ArrayList<>();
for (Object queryObj : queriesArray) {
// Check for null elements in queries array
if (queryObj == null || queryObj == JSONObject.NULL) {
throw new IllegalArgumentException("Queries array contains null elements");
}
JSONObject queryJsonObject = (JSONObject) queryObj;
String datasource = queryJsonObject.getString("datasource");
String query = queryJsonObject.getString("query");
String key = queryJsonObject.has("key") ? queryJsonObject.getString("key") : null;
String datasource = getRequiredString(queryJsonObject, "datasource", "Query object", "datasource");
String query = getRequiredString(queryJsonObject, "query", "Query object", "string");
String key = queryJsonObject.optString("key", null);
LayerPresenceQuery layerPresenceQuery = new LayerPresenceQuery(datasource, query, key);
queries.add(layerPresenceQuery);
}
Expand All @@ -590,9 +639,14 @@ public static KruizeLayer convertInputJSONToCreateLayer(String inputData) throws
if (layerPresenceObject.has("label")) {
JSONArray labelArray = layerPresenceObject.getJSONArray("label");
if (labelArray.length() > 0) {
JSONObject labelObject = labelArray.getJSONObject(0);
labelName = labelObject.getString("name");
labelValue = labelObject.getString("value");
Object labelObj = labelArray.get(0);
// Check for null element in label array
if (labelObj == null || labelObj == JSONObject.NULL) {
throw new IllegalArgumentException("Label array contains null elements");
}
JSONObject labelObject = (JSONObject) labelObj;
labelName = getRequiredString(labelObject, "name", "Label object", "name");
labelValue = getRequiredString(labelObject, "value", "Label object", "value");
}
}
}
Expand All @@ -603,7 +657,27 @@ public static KruizeLayer convertInputJSONToCreateLayer(String inputData) throws
if (tunablesArray != null) {
tunables = new ArrayList<>();
for (Object tunableObj : tunablesArray) {
// Check for null elements in tunables array
if (tunableObj == null || tunableObj == JSONObject.NULL) {
throw new IllegalArgumentException("Tunables array contains null element");
}
JSONObject tunableJsonObject = (JSONObject) tunableObj;

// Validate tunable name using helper method
String tunableName = getRequiredString(tunableJsonObject, "name", "Tunable object", "name");

// Validate value_type with custom error messages that include tunable name
if (!tunableJsonObject.has("value_type")) {
throw new IllegalArgumentException(String.format("Tunable '%s' missing value_type field", tunableName));
}
if (tunableJsonObject.isNull("value_type")) {
throw new IllegalArgumentException(String.format("Tunable '%s' has null value_type", tunableName));
}
String valueType = tunableJsonObject.getString("value_type");
if (!"double".equals(valueType) && !"integer".equals(valueType) && !"categorical".equals(valueType)) {
throw new IllegalArgumentException(String.format("Tunable '%s' has invalid value_type. Must be one of: double, integer, categorical", tunableName));
}

Tunable tunable = new Gson().fromJson(tunableJsonObject.toString(), Tunable.class);
tunables.add(tunable);
}
Expand Down
Loading
Loading