/** * Get a registered FeatureFlag by name. * * @param name Name of the FeatureFlag to get * * @return The feature flag with the given name * @throws BadApiRequestException if no feature flag has been registered for that name */ public FeatureFlag forName(String name) throws BadApiRequestException { FeatureFlag flag = NAMES_TO_VALUES.get(name.toUpperCase(Locale.ENGLISH)); return flag != null ? flag : Utils.insteadThrowRuntime( new BadApiRequestException("Invalid feature flag: " + name) ); }
@Override public void validateDuplicateMetrics(ArrayNode metricsJsonArray) { Set<String> metricsList = new HashSet<>(); List<String> duplicateMetrics = new ArrayList<>(); for (int i = 0; i < metricsJsonArray.size(); i++) { String metricName = metricsJsonArray.get(i).get("name").asText(); boolean status = metricsList.add(metricName); if (!status) { duplicateMetrics.add(metricName); } } if (!duplicateMetrics.isEmpty()) { LOG.debug(DUPLICATE_METRICS_IN_API_REQUEST.logFormat(duplicateMetrics.toString())); throw new BadApiRequestException(DUPLICATE_METRICS_IN_API_REQUEST.format(duplicateMetrics.toString())); } }
/** * Confirm count size is non negative. * * @param countRequest The value of the count from the request (if any) * @param count The bound value for the count */ protected void validateCount(String countRequest, int count) { // This is the validation part for count that is inlined here because currently it is very brief. if (count < 0) { LOG.debug(INTEGER_INVALID.logFormat(countRequest, "count")); throw new BadApiRequestException(INTEGER_INVALID.logFormat(countRequest, "count")); } } /**
/** * Builds the paginationParameters object, if the request provides both a perPage and page field. * * @param perPage The number of rows per page. * @param page The page to display. * * @return An Optional wrapping a PaginationParameters if both 'perPage' and 'page' exist. * @throws BadApiRequestException if 'perPage' or 'page' is not a positive integer, or if either one is empty * string but not both. */ protected Optional<PaginationParameters> generatePaginationParameters(String perPage, String page) throws BadApiRequestException { try { return "".equals(perPage) && "".equals(page) ? Optional.empty() : Optional.of(new PaginationParameters(perPage, page)); } catch (BadPaginationException invalidParameters) { throw new BadApiRequestException(invalidParameters.getMessage()); } }
/** * Parses the requested input String by converting it to an integer, while treating null as zero. * * @param value The requested integer value as String. * @param parameterName The parameter name that corresponds to the requested integer value. * * @return The integer corresponding to {@code value} or zero if {@code value} is null. * @throws BadApiRequestException if the input String can not be parsed as an integer. */ protected int generateInteger(String value, String parameterName) throws BadApiRequestException { try { return value == null ? 0 : Integer.parseInt(value); } catch (NumberFormatException nfe) { LOG.debug(INTEGER_INVALID.logFormat(value, parameterName), nfe); throw new BadApiRequestException(INTEGER_INVALID.logFormat(value, parameterName), nfe); } }
@Override @Deprecated public Filter getQueryFilter() { try (TimedPhase timer = RequestLog.startTiming("BuildingDruidFilter")) { return filterBuilder.buildFilters(this.apiFilters); } catch (FilterBuilderException e) { LOG.debug(e.getMessage()); throw new BadApiRequestException(e); } }
/** * Given a filter String, generates a Set of ApiJobStoreFilters. This method will throw a BadApiRequestException if * the filter String cannot be parsed into ApiJobStoreFilters successfully. * * @param filterQuery Expects a URL filterQuery String that may contain multiple filters separated by * comma. The format of a filter String is : * (JobField name)-(operation)[(value or comma separated values)]? * * @return A Set of ApiJobStoreFilters */ public LinkedHashSet<JobRowFilter> buildJobStoreFilter(@NotNull String filterQuery) { // split on '],' to get list of filters return Arrays.stream(filterQuery.split(COMMA_AFTER_BRACKET_PATTERN)) .map( filter -> { try { return new JobRowFilter(filter); } catch (BadFilterException e) { throw new BadApiRequestException(e.getMessage(), e); } } ) .collect(Collectors.toCollection(LinkedHashSet<JobRowFilter>::new)); }
/** * Confirm the top N bucket size (if any) is valid. * * @param topNRequest The value of the count from the request (if any) * @param sorts collection of sorted columns * @param topN The bound value for the count */ protected void validateTopN(String topNRequest, int topN, LinkedHashSet<OrderByColumn> sorts) { // This is the validation part for topN that is inlined here because currently it is very brief. if (topN < 0) { LOG.debug(INTEGER_INVALID.logFormat(topNRequest, "topN")); throw new BadApiRequestException(INTEGER_INVALID.logFormat(topNRequest, "topN")); } else if (topN > 0 && this.sorts.isEmpty()) { LOG.debug(TOP_N_UNSORTED.logFormat(topNRequest)); throw new BadApiRequestException(TOP_N_UNSORTED.format(topNRequest)); } }
/** * Extract valid sort direction. * * @param columnWithDirection Column and its sorting direction * * @return Sorting direction. If no direction provided then the default one will be DESC */ protected SortDirection getSortDirection(List<String> columnWithDirection) { try { return columnWithDirection.size() == 2 ? SortDirection.valueOf(columnWithDirection.get(1).toUpperCase(Locale.ENGLISH)) : SortDirection.DESC; } catch (IllegalArgumentException ignored) { String sortDirectionName = columnWithDirection.get(1); LOG.debug(SORT_DIRECTION_INVALID.logFormat(sortDirectionName)); throw new BadApiRequestException(SORT_DIRECTION_INVALID.format(sortDirectionName)); } }
/** * Parses the asyncAfter parameter into a long describing how long the user is willing to wait for the results of a * synchronous request before the request should become asynchronous. * * @param asyncAfterString asyncAfter should be either a string representation of a long, or the String never * * @return A long describing how long the user is willing to wait * * @throws BadApiRequestException if asyncAfterString is neither the string representation of a natural number, nor * {@code never} */ protected long generateAsyncAfter(String asyncAfterString) throws BadApiRequestException { try { return asyncAfterString.equals(SYNCHRONOUS_REQUEST_FLAG) ? SYNCHRONOUS_ASYNC_AFTER_VALUE : asyncAfterString.equals(ASYNCHRONOUS_REQUEST_FLAG) ? ASYNCHRONOUS_ASYNC_AFTER_VALUE : Long.parseLong(asyncAfterString); } catch (NumberFormatException e) { LOG.debug(INVALID_ASYNC_AFTER.logFormat(asyncAfterString), e); throw new BadApiRequestException(INVALID_ASYNC_AFTER.format(asyncAfterString), e); } } }
String message = ErrorMessageFormat.METRICS_UNDEFINED.logFormat(invalidMetricNames); LOG.error(message); throw new BadApiRequestException(message);
throw new BadApiRequestException(NON_AGGREGATABLE_INVALID.format(invalidDimensionsInFilters));
/** * Bind the table name against a Logical table in the table dictionary. * * @param tableName Name of the logical table from the query * @param table The bound logical table for this query * @param granularity The granularity for this request * @param logicalTableDictionary Dictionary to resolve logical tables against. * * @throws BadApiRequestException if invalid */ protected void validateLogicalTable( String tableName, LogicalTable table, Granularity granularity, LogicalTableDictionary logicalTableDictionary ) throws BadApiRequestException { if (table == null) { LOG.debug(TABLE_UNDEFINED.logFormat(tableName)); throw new BadApiRequestException(TABLE_UNDEFINED.format(tableName)); } }
} catch (BadFilterException filterException) { throw new BadApiRequestException(filterException.getMessage(), filterException);
/** * Generate a Granularity instance based on a path element. * * @param granularity A string representation of the granularity * @param dateTimeZone The time zone to use for this granularity * @param granularityParser The parser for granularity * * @return A granularity instance with time zone information * @throws BadApiRequestException if the string matches no meaningful granularity */ protected Granularity generateGranularity( @NotNull String granularity, @NotNull DateTimeZone dateTimeZone, @NotNull GranularityParser granularityParser ) throws BadApiRequestException { try { return granularityParser.parseGranularity(granularity, dateTimeZone); } catch (GranularityParseException e) { LOG.error(UNKNOWN_GRANULARITY.logFormat(granularity), granularity); throw new BadApiRequestException(e.getMessage()); } }
throw new BadApiRequestException(METRICS_UNDEFINED.format(invalidMetricNames.toString()));
/** * Method to generate DateTime sort column from the map of columns and its direction. * * @param sortColumns LinkedHashMap of columns and its direction. Using LinkedHashMap to preserve the order * * @return Instance of OrderByColumn for dateTime */ protected Optional<OrderByColumn> bindDateTimeSortColumn(LinkedHashMap<String, SortDirection> sortColumns) { if (sortColumns != null && sortColumns.containsKey(DATE_TIME_STRING)) { if (!isDateTimeFirstSortField(sortColumns)) { LOG.debug(DATE_TIME_SORT_VALUE_INVALID.logFormat()); throw new BadApiRequestException(DATE_TIME_SORT_VALUE_INVALID.format()); } else { return Optional.of(new OrderByColumn(DATE_TIME_STRING, sortColumns.get(DATE_TIME_STRING))); } } else { return Optional.empty(); } }
/** * Ensure all request dimensions are part of the logical table. * * @param requestDimensions The dimensions being requested * @param table The logical table being checked * * @throws BadApiRequestException if any of the dimensions do not match the logical table */ protected void validateRequestDimensions(Set<Dimension> requestDimensions, LogicalTable table) throws BadApiRequestException { // Requested dimensions must lie in the logical table requestDimensions = new HashSet<>(requestDimensions); requestDimensions.removeAll(table.getDimensions()); if (!requestDimensions.isEmpty()) { List<String> dimensionNames = requestDimensions.stream() .map(Dimension::getApiName) .collect(Collectors.toList()); LOG.debug(DIMENSIONS_NOT_IN_TABLE.logFormat(dimensionNames, table.getName())); throw new BadApiRequestException(DIMENSIONS_NOT_IN_TABLE.format(dimensionNames, table.getName())); } }
/** * Validate that all metrics are part of the logical table. * * @param logicalMetrics The set of metrics being validated * @param table The logical table for the request * * @throws BadApiRequestException if the requested metrics are not in the logical table */ protected void validateMetrics(Set<LogicalMetric> logicalMetrics, LogicalTable table) throws BadApiRequestException { //get metric names from the logical table Set<String> validMetricNames = table.getLogicalMetrics().stream() .map(LogicalMetric::getName) .collect(Collectors.toSet()); //get metric names from logicalMetrics and remove all the valid metrics Set<String> invalidMetricNames = logicalMetrics.stream() .map(LogicalMetric::getName) .filter(it -> !validMetricNames.contains(it)) .collect(Collectors.toSet()); //requested metrics names are not present in the logical table metric names set if (!invalidMetricNames.isEmpty()) { LOG.debug(METRICS_NOT_IN_TABLE.logFormat(invalidMetricNames, table.getName())); throw new BadApiRequestException( METRICS_NOT_IN_TABLE.format(invalidMetricNames, table.getName()) ); } }
/** * Generates the format in which the response data is expected. * * @param format Expects a URL format query String. * * @return Response format type (CSV or JSON). * @throws BadApiRequestException if the requested format is not found. */ protected ResponseFormatType generateAcceptFormat(String format) throws BadApiRequestException { try { return format == null ? DefaultResponseFormatType.JSON : DefaultResponseFormatType.valueOf(format.toUpperCase(Locale.ENGLISH)); } catch (IllegalArgumentException e) { LOG.error(ACCEPT_FORMAT_INVALID.logFormat(format), e); throw new BadApiRequestException(ACCEPT_FORMAT_INVALID.format(format)); } }