Skip to content

Commit

Permalink
Merge pull request #256 from HubSpot/normalize_data_before_validation
Browse files Browse the repository at this point in the history
Normalize Slack form element presentational data
  • Loading branch information
omotnyk authored Dec 7, 2021
2 parents eaf228f + 1d00b4c commit a2116c6
Show file tree
Hide file tree
Showing 16 changed files with 481 additions and 109 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,20 @@ public int getMaxLength() {

public abstract Optional<String> getValue();

protected void validateBaseTextElementProps() {

super.validateBaseElementProperties();
if (getMinLength() < 0) {
throw new IllegalStateException("Min length cannot be negative, got " + getMinLength());
}

if (getMaxLength() < 0) {
throw new IllegalStateException("Max length cannot be negative, got " + getMaxLength());
protected void validateBaseTextElementProps(AbstractSlackDialogFormTextElement normalized) {
super.validateBaseElementProperties(normalized);
int normalizedMinLength = normalized.getMinLength();
if (normalizedMinLength < 0) {
throw new IllegalStateException("Min length cannot be negative, got " + normalizedMinLength);
}

if (getMinLength() > getMaxLength()) {
throw new IllegalStateException("Min length must be <= max length, got " + getMinLength() + ", " + getMaxLength());
int normalizedMaxLength = normalized.getMaxLength();
if (normalizedMaxLength < 0) {
throw new IllegalStateException("Max length cannot be negative, got " + normalizedMaxLength);
}

if (getHint().isPresent() && getHint().get().length() > 150) {
throw new IllegalStateException("Hint cannot exceed 150 chars, got '" + getHint().get() + "'");
if (normalizedMinLength > normalizedMaxLength) {
throw new IllegalStateException("Min length must be <= max length, got " + normalizedMinLength + ", " + normalizedMaxLength);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@
import com.hubspot.immutables.style.HubSpotStyle;
import com.hubspot.slack.client.models.actions.SlackDataSource;
import com.hubspot.slack.client.models.dialog.form.SlackFormElementTypes;
import com.hubspot.slack.client.models.dialog.form.elements.helpers.SlackDialogElementNormalizer;

@Immutable
@HubSpotStyle
@JsonNaming(SnakeCaseStrategy.class)
@JsonInclude(Include.NON_EMPTY)
public abstract class AbstractSlackFormSelectElement extends SlackDialogFormElement {
public abstract class AbstractSlackFormSelectElement extends SlackDialogFormElement implements HasOptions {
@Default
@Override
public SlackFormElementTypes getType() {
Expand All @@ -33,63 +34,69 @@ public SlackDataSource getDataSource() {
return SlackDataSource.STATIC;
}

public abstract List<SlackFormOption> getOptions();
public abstract List<SlackFormOptionGroup> getOptionGroups();
public abstract Optional<String> getValue();
public abstract List<SlackFormOption> getSelectedOptions();
public abstract Optional<Integer> getMinQueryLength();

@Check
public void validate() {
super.validateBaseElementProperties();
public AbstractSlackFormSelectElement validate() {
AbstractSlackFormSelectElement normalized = SlackDialogElementNormalizer.normalize(this);
super.validateBaseElementProperties(normalized);
List<String> errors = new ArrayList<>();

int numOptions = getOptions().size();
int numOptionGroups = getOptionGroups().size();
List<SlackFormOption> normalizedOptions = normalized.getOptions();
int numOptions = normalizedOptions.size();
List<SlackFormOptionGroup> normalizedOptionGroups = normalized.getOptionGroups();
int numOptionGroups = normalizedOptionGroups.size();

if (numOptions > 100) {
errors.add("Cannot have more than 100 options");
int maxOptionsNumber = SlackDialogFormElementLengthLimits.MAX_OPTIONS_NUMBER.getLimit();
if (numOptions > maxOptionsNumber) {
errors.add(String.format("Cannot have more than %s options", maxOptionsNumber));
}

if (numOptionGroups > 100) {
errors.add("Cannot have more than 100 option groups");
int maxOptionGroupsNumber = SlackDialogFormElementLengthLimits.MAX_OPTION_GROUPS_NUMBER.getLimit();
if (numOptionGroups > maxOptionGroupsNumber) {
errors.add(String.format("Cannot have more than %s option groups", maxOptionGroupsNumber));
}

if (getDataSource().equals(SlackDataSource.STATIC)) {
if (normalized.getDataSource().equals(SlackDataSource.STATIC)) {
if (numOptions == 0 && numOptionGroups == 0) {
errors.add("Either options or option groups are required for static data source types");
}
}

if (getValue().isPresent() && getDataSource().equals(SlackDataSource.EXTERNAL)) {
Optional<String> normalizedValue = normalized.getValue();
if (normalizedValue.isPresent() && normalized.getDataSource().equals(SlackDataSource.EXTERNAL)) {
errors.add("Cannot use value for external data source, must use selected options");
}

if (getValue().isPresent()) {
if (normalizedValue.isPresent()) {
boolean valueIsSomeOptionValue = getOptions().stream()
.anyMatch(option -> option.getValue().equalsIgnoreCase(getValue().get()));
.anyMatch(option -> option.getValue().equalsIgnoreCase(normalizedValue.get()));
if (!valueIsSomeOptionValue) {
errors.add("Value must exactly match the value field for one provided option");
}
}

if (!getSelectedOptions().isEmpty()) {
if (getSelectedOptions().size() != 1) {
List<SlackFormOption> normalizedSelectedOptions = normalized.getSelectedOptions();
if (!normalizedSelectedOptions.isEmpty()) {
if (normalizedSelectedOptions.size() != 1) {
errors.add("Selected options must be a single element array");
}
if (!getOptionGroups().isEmpty()) {
boolean selectedOptionIsInOptionsGroup = getOptionGroups().stream()
if (!normalizedOptionGroups.isEmpty()) {
boolean selectedOptionIsInOptionsGroup = normalizedOptionGroups.stream()
.map(SlackFormOptionGroup::getOptions)
.collect(Collectors.toList())
.stream()
.flatMap(List::stream)
.anyMatch(option -> option.equals(getSelectedOptions().get(0)));
.anyMatch(option -> option.equals(normalizedSelectedOptions.get(0)));
if (!selectedOptionIsInOptionsGroup) {
errors.add("Selected option must exactly match an option in the provided options groups");
}
} else if (!getOptions().isEmpty()) {
boolean selectedOptionIsInOptions = getOptions().stream()
.anyMatch(option -> option.equals(getSelectedOptions().get(0)));
} else if (!normalizedOptions.isEmpty()) {
boolean selectedOptionIsInOptions = normalizedOptions.stream()
.anyMatch(option -> option.equals(normalizedSelectedOptions.get(0)));
if (!selectedOptionIsInOptions) {
errors.add("Selected option must exactly match an option in the provided options");
}
Expand All @@ -99,5 +106,6 @@ public void validate() {
if (!errors.isEmpty()) {
throw new IllegalStateException(errors.toString());
}
return normalized;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.hubspot.slack.client.models.dialog.form.elements;

import java.util.Optional;

import org.immutables.value.Value.Check;
import org.immutables.value.Value.Default;
import org.immutables.value.Value.Immutable;
Expand All @@ -10,6 +12,7 @@
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import com.hubspot.immutables.style.HubSpotStyle;
import com.hubspot.slack.client.models.dialog.form.SlackFormElementTypes;
import com.hubspot.slack.client.models.dialog.form.elements.helpers.SlackDialogElementNormalizer;

@Immutable
@HubSpotStyle
Expand All @@ -23,19 +26,28 @@ public SlackFormElementTypes getType() {
}

@Check
public void validate() {
super.validateBaseTextElementProps();
public AbstractSlackFormTextElement validate() {
AbstractSlackFormTextElement normalized = SlackDialogElementNormalizer.normalize(this);
super.validateBaseTextElementProps(normalized);

if (getMaxLength() > 150) {
throw new IllegalStateException("Form text element cannot have max length > 150 chars, got " + getMaxLength());
int normalizedMaxLength = normalized.getMaxLength();
int maxTextElementValueLength = SlackDialogFormElementLengthLimits.MAX_TEXT_ELEMENT_VALUE_LENGTH.getLimit();
if (normalizedMaxLength > maxTextElementValueLength) {
String errorMessage = String.format("Form text element cannot have max length > %s chars, got %s", maxTextElementValueLength, normalizedMaxLength);
throw new IllegalStateException(errorMessage);
}

if (getMinLength() > 150) {
throw new IllegalStateException("Form text element cannot have min length > 150 chars, got " + getMinLength());
int normalizedMinLength = normalized.getMinLength();
if (normalizedMinLength > maxTextElementValueLength) {
String errorMessage = String.format("Form text element cannot have min length > %s chars, got %s", maxTextElementValueLength, normalizedMinLength);
throw new IllegalStateException(errorMessage);
}

if (getValue().isPresent() && getValue().get().length() > 150) {
throw new IllegalStateException("Value cannot exceed 150 chars, got '" + getValue().get() + "'");
Optional<String> value = normalized.getValue();
if (value.isPresent() && value.get().length() > maxTextElementValueLength) {
String errorMessage = String.format("Value cannot exceed %s chars, got %s", maxTextElementValueLength, value.get());
throw new IllegalStateException(errorMessage);
}
return normalized;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.hubspot.slack.client.models.dialog.form.elements;

import java.util.Optional;

import org.immutables.value.Value.Check;
import org.immutables.value.Value.Default;
import org.immutables.value.Value.Immutable;
Expand All @@ -10,6 +12,7 @@
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import com.hubspot.immutables.style.HubSpotStyle;
import com.hubspot.slack.client.models.dialog.form.SlackFormElementTypes;
import com.hubspot.slack.client.models.dialog.form.elements.helpers.SlackDialogElementNormalizer;

@Immutable
@HubSpotStyle
Expand All @@ -23,19 +26,28 @@ public SlackFormElementTypes getType() {
}

@Check
public void validate() {
super.validateBaseTextElementProps();
public AbstractSlackFormTextareaElement validate() {
AbstractSlackFormTextareaElement normalized = SlackDialogElementNormalizer.normalize(this);
super.validateBaseTextElementProps(normalized);

if (getMaxLength() > 3000) {
throw new IllegalStateException("Form text area cannot have max length > 3000 chars, got " + getMaxLength());
int normalizedMaxLength = normalized.getMaxLength();
int maxTextareaElementValueLengthLimit = SlackDialogFormElementLengthLimits.MAX_TEXT_AREA_ELEMENT_VALUE_LENGTH.getLimit();
if (normalizedMaxLength > maxTextareaElementValueLengthLimit) {
String errorMessage = String.format("Form text area cannot have max length > %s chars, got %s", maxTextareaElementValueLengthLimit, normalizedMaxLength);
throw new IllegalStateException(errorMessage);
}

if (getMinLength() > 3000) {
throw new IllegalStateException("Form text area cannot have min length > 3000 chars, got " + getMinLength());
int normalizedMinLength = normalized.getMinLength();
if (normalizedMinLength > maxTextareaElementValueLengthLimit) {
String errorMessage = String.format("Form text area cannot have min length > %s chars, got %s", maxTextareaElementValueLengthLimit, normalizedMinLength);
throw new IllegalStateException(errorMessage);
}

if (getValue().isPresent() && getValue().get().length() > 3000) {
throw new IllegalStateException("Value cannot exceed 3000 chars, got '" + getValue().get() + "'");
Optional<String> value = normalized.getValue();
if (value.isPresent() && value.get().length() > maxTextareaElementValueLengthLimit) {
String errorMessage = String.format("Value cannot exceed %s chars, got %s", maxTextareaElementValueLengthLimit, value.get());
throw new IllegalStateException(errorMessage);
}
return normalized;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.hubspot.slack.client.models.dialog.form.elements;

public interface HasLabel {
String getLabel();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.hubspot.slack.client.models.dialog.form.elements;

import java.util.List;

public interface HasOptions {
List<SlackFormOption> getOptions();
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,40 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.hubspot.slack.client.models.dialog.form.SlackFormElementTypes;

public abstract class SlackDialogFormElement {
public abstract class SlackDialogFormElement implements HasLabel {
public abstract SlackFormElementTypes getType();

public abstract String getName();
public abstract String getLabel();

public abstract Optional<String> getPlaceholder();

@JsonProperty("optional")
public abstract Optional<Boolean> isOptional();

protected void validateBaseElementProperties() {
if (getLabel().length() > 24) {
throw new IllegalStateException("Label cannot exceed 24 chars, got " + getLabel());
protected void validateBaseElementProperties(SlackDialogFormElement normalized) {
String normalizedLabel = normalized.getLabel();
int maxLabelLength = SlackDialogFormElementLengthLimits.MAX_LABEL_LENGTH.getLimit();
if (normalizedLabel.length() > maxLabelLength) {
String errorMessage = String.format("Label cannot exceed %s chars, got %s", maxLabelLength, normalizedLabel);
throw new IllegalStateException(errorMessage);
}

if (getName().length() > 300) {
throw new IllegalStateException("Name cannot exceed 300 chars, got " + getName());
String normalizedName = normalized.getName();
int maxNameLength = SlackDialogFormElementLengthLimits.MAX_NAME_LENGTH.getLimit();
if (normalizedName.length() > maxNameLength) {
String errorMessage = String.format("Name cannot exceed %s chars, got %s", maxNameLength, normalizedName);
throw new IllegalStateException(errorMessage);
}

if (getPlaceholder().isPresent() && getPlaceholder().get().length() > 150) {
throw new IllegalStateException("Placeholder length cannot exceed 150 chars, got " + getPlaceholder().get());
Optional<String> normalizedPlaceholder = normalized.getPlaceholder();
int maxPlaceholderLength = SlackDialogFormElementLengthLimits.MAX_PLACEHOLDER_LENGTH.getLimit();
if (normalizedPlaceholder.isPresent() && normalizedPlaceholder.get().length() > maxPlaceholderLength) {
String errorMessage = String.format(
"Placeholder cannot exceed %s chars, got %s",
maxPlaceholderLength,
normalizedPlaceholder.get()
);
throw new IllegalStateException(errorMessage);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.hubspot.slack.client.models.dialog.form.elements;

public enum SlackDialogFormElementLengthLimits {
MAX_LABEL_LENGTH(24),
MAX_NAME_LENGTH(300),
MAX_PLACEHOLDER_LENGTH(150),
MAX_HINT_LENGTH(150),
MAX_TEXT_ELEMENT_VALUE_LENGTH(150),
MAX_TEXT_AREA_ELEMENT_VALUE_LENGTH(3000),
MAX_OPTION_LABEL_LENGTH(75),
MAX_OPTION_VALUE_LENGTH(75),
MAX_OPTIONS_NUMBER(100),
MAX_OPTION_GROUPS_NUMBER(100);

private final int limit;

SlackDialogFormElementLengthLimits(int limit) {
this.limit = limit;
}

public int getLimit() {
return limit;
}
}
Original file line number Diff line number Diff line change
@@ -1,37 +1,40 @@
package com.hubspot.slack.client.models.dialog.form.elements;

import java.util.List;

import org.immutables.value.Value.Check;
import org.immutables.value.Value.Immutable;

import com.fasterxml.jackson.databind.PropertyNamingStrategy.SnakeCaseStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import com.google.common.base.Strings;
import com.hubspot.immutables.style.HubSpotStyle;
import com.hubspot.slack.client.models.dialog.form.elements.helpers.SlackDialogElementNormalizer;

@Immutable
@HubSpotStyle
@JsonNaming(SnakeCaseStrategy.class)
public interface SlackFormOptionGroupIF {
String getLabel();
List<SlackFormOption> getOptions();

public interface SlackFormOptionGroupIF extends HasLabel, HasOptions {
@Check
default void validate() {
String label = getLabel();
int numOptionGroups = getOptions().size();
default SlackFormOptionGroupIF validate() {
SlackFormOptionGroupIF normalized = SlackDialogElementNormalizer.normalize(this);
String label = normalized.getLabel();
int numOptions = normalized.getOptions().size();

if (Strings.isNullOrEmpty(label)) {
throw new IllegalStateException("Must provide a label");
}

if (label.length() > 75) {
throw new IllegalStateException("Label cannot exceed 75 chars - '" + label + "'");
int maxOptionLabelLength = SlackDialogFormElementLengthLimits.MAX_OPTION_LABEL_LENGTH.getLimit();
if (label.length() > maxOptionLabelLength) {
String errorMessage = String.format("Label cannot exceed %s chars - '%s'", maxOptionLabelLength, label);
throw new IllegalStateException(errorMessage);
}

if (numOptionGroups > 100) {
throw new IllegalStateException("Cannot have more than 100 option groups. Has " + numOptionGroups);
int maxOptionsNumber = SlackDialogFormElementLengthLimits.MAX_OPTIONS_NUMBER.getLimit();
if (numOptions > maxOptionsNumber) {
String errorMessage = String.format("Cannot have more than %s option groups. Has %s", maxOptionsNumber, numOptions);
throw new IllegalStateException(errorMessage);
}

return normalized;
}
}
Loading

0 comments on commit a2116c6

Please sign in to comment.