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
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,14 @@ private FeatureToggleConstants() {}
* System property: same name ({@code FT_FORMS-25252}); set to {@code "true"} to enable.
*/
public static final String FT_SERVER_SIDE_VALIDATION = "FT_FORMS-25252";

/**
* When enabled, the {@code asyncValidtionExpression} property is exported on form field/container
* components. When disabled, {@link com.adobe.cq.forms.core.components.models.form.BaseConstraint#getAsyncValidtionExpression()}
* returns {@code null} regardless of the stored JCR property value, allowing the property to be
* rolled out to a limited set of customer instances before general availability.
* <p>
* System property: same name ({@code FT_FORMS-26583}); set to {@code "true"} to enable.
*/
public static final String FT_ASYNC_VALIDATION_EXPRESSION = "FT_FORMS-26583";
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ private ReservedProperties() {
public static final String PN_DOR_TEMPLATE_REF = "dorTemplateRef";
public static final String PN_DOR_TYPE = "dorType";
public static final String PN_VALIDATION_EXPRESSION = "validationExpression";
public static final String PN_ASYNC_VALIDATION_EXPRESSION = "asyncValidtionExpression";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo

public static final String PN_REQUIRED = "required";
public static final String PN_AUTOCOMPLETE = "autocomplete";
public static final String PN_ASSIST_PRIORITY = "assistPriority";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,16 @@ default String getValidationExpression() {
return null;
}

/**
* Returns an expression returning boolean value indicating whether the value in the field is valid or not, evaluated
* asynchronously
*
* @return an expression
* @since com.adobe.cq.forms.core.components.models.form 0.0.1
*/
@Nullable
default String getAsyncValidtionExpression() {
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import com.adobe.cq.forms.core.components.internal.form.FeatureToggleConstants;
import com.adobe.cq.forms.core.components.internal.form.ReservedProperties;
import com.adobe.cq.forms.core.components.models.form.AssistPriority;
import com.adobe.cq.forms.core.components.models.form.Base;
Expand Down Expand Up @@ -80,6 +81,10 @@ public abstract class AbstractBaseImpl extends AbstractFormComponentImpl impleme
@Nullable
protected String validationExpression;

@ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL, name = ReservedProperties.PN_ASYNC_VALIDATION_EXPRESSION)
@Nullable
protected String asyncValidtionExpression;

@ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL, name = ReservedProperties.PN_REQUIRED)
@Nullable
@JsonInclude(JsonInclude.Include.NON_NULL)
Expand Down Expand Up @@ -450,4 +455,11 @@ public Type getType() {
public String getValidationExpression() {
return validationExpression;
}

@Override
@Nullable
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public String getAsyncValidtionExpression() {
return ComponentUtils.isToggleEnabled(FeatureToggleConstants.FT_ASYNC_VALIDATION_EXPRESSION) ? asyncValidtionExpression : null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,11 @@ void testGetValidationExpression() {
Mockito.when(baseConstraintMock.getValidationExpression()).thenCallRealMethod();
assertEquals(null, baseConstraintMock.getValidationExpression());
}

@Test
void testGetAsyncValidtionExpression() {
BaseConstraint baseConstraintMock = Mockito.mock(BaseConstraint.class);
Mockito.when(baseConstraintMock.getAsyncValidtionExpression()).thenCallRealMethod();
assertEquals(null, baseConstraintMock.getAsyncValidtionExpression());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public class TextInputImplTest {
private static final String PATH_TEXTINPUT_UNBOUNDFORMELEMENT = CONTENT_ROOT + "/textinput_unboundFormElement";
private static final String PATH_TEXTINPUT_BLANK_DATAREF = CONTENT_ROOT + "/textinput-blank-dataref";
private static final String PATH_TEXTINPUT_BLANK_VALIDATIONEXPRESSION = CONTENT_ROOT + "/textinput-blank-validationExpression";
private static final String PATH_TEXTINPUT_BLANK_ASYNCVALIDTIONEXPRESSION = CONTENT_ROOT + "/textinput-blank-asyncValidtionExpression";
private static final String PATH_TEXTINPUT_DISPLAY_VALUE_EXPRESSION = CONTENT_ROOT + "/textinput-displayValueExpression";
private static final String PATH_TEXTINPUT_PLACEHOLDER_AUTOCOMPLETE = CONTENT_ROOT + "/textinput-placeholder-autocomplete";
private static final String PATH_TEXTINPUT_WITH_VIEWTYPE = CONTENT_ROOT + "/textinput-with-viewtype";
Expand Down Expand Up @@ -369,6 +370,7 @@ void testJSONExportForUnboundFormElement() throws Exception {

@Test
void testJSONExportForCustomized() throws Exception {
System.setProperty(FeatureToggleConstants.FT_ASYNC_VALIDATION_EXPRESSION, "true");
TextInput textInput = Utils.getComponentUnderTest(PATH_TEXTINPUT_CUSTOMIZED, TextInput.class, context);
Utils.testJSONExport(textInput, Utils.getTestExporterJSONPath(BASE, PATH_TEXTINPUT_CUSTOMIZED));
}
Expand Down Expand Up @@ -514,6 +516,26 @@ void testJSONExportForEmptyValidationExpression() throws Exception {
Utils.testJSONExport(textInput, Utils.getTestExporterJSONPath(BASE, PATH_TEXTINPUT_BLANK_VALIDATIONEXPRESSION));
}

@Test
void testJSONExportForEmptyAsyncValidtionExpression() throws Exception {
System.setProperty(FeatureToggleConstants.FT_ASYNC_VALIDATION_EXPRESSION, "true");
TextInput textInput = Utils.getComponentUnderTest(PATH_TEXTINPUT_BLANK_ASYNCVALIDTIONEXPRESSION, TextInput.class, context);
Utils.testJSONExport(textInput, Utils.getTestExporterJSONPath(BASE, PATH_TEXTINPUT_BLANK_ASYNCVALIDTIONEXPRESSION));
}

@Test
void testGetAsyncValidtionExpressionOmittedWhenToggleOff() throws Exception {
TextInput textInput = Utils.getComponentUnderTest(PATH_TEXTINPUT_CUSTOMIZED, TextInput.class, context);
assertEquals(null, textInput.getAsyncValidtionExpression());
}

@Test
void testGetAsyncValidtionExpressionPresentWhenToggleOn() throws Exception {
System.setProperty(FeatureToggleConstants.FT_ASYNC_VALIDATION_EXPRESSION, "true");
TextInput textInput = Utils.getComponentUnderTest(PATH_TEXTINPUT_CUSTOMIZED, TextInput.class, context);
assertEquals("$field == 'validate'", textInput.getAsyncValidtionExpression());
}

@Test
void testPlaceholderAndAutocomplete() throws Exception {
TextInput textInput = Utils.getComponentUnderTest(PATH_TEXTINPUT_PLACEHOLDER_AUTOCOMPLETE, TextInput.class, context);
Expand Down Expand Up @@ -597,6 +619,7 @@ void testEmptyValueEnumGetValue() {
@AfterEach
void tearDown() {
System.clearProperty(FeatureToggleConstants.FT_SKIP_DEFAULT_SET_PROPERTY_EVENT);
System.clearProperty(FeatureToggleConstants.FT_ASYNC_VALIDATION_EXPRESSION);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"id": "textinput-9347d57b66",
"fieldType": "text-input",
"name": "abc",
"type": "string",
"label": {
"value": "def"
},
"events": {
"custom:setProperty": [
"$event.payload"
]
},
"properties": {
"fd:path": "/content/textinput-blank-asyncValidtionExpression"
},
":type": "core/fd/components/form/textinput/v1/textinput"
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
},
"readOnly": false,
"default": "abc",
"asyncValidtionExpression": "$field == 'validate'",
"label": {
"visible": true,
"value": "def"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
"typeMessage" : "incorrect type",
"tooltip": "test-short-description",
"default" : "abc",
"fieldType": "text-input"
"fieldType": "text-input",
"asyncValidtionExpression": "$field == 'validate'"
},
"number-textinput" : {
"jcr:primaryType": "nt:unstructured",
Expand Down Expand Up @@ -167,6 +168,14 @@
"fieldType": "text-input",
"validationExpression": ""
},
"textinput-blank-asyncValidtionExpression" : {
"jcr:primaryType": "nt:unstructured",
"sling:resourceType" : "core/fd/components/form/textinput/v1/textinput",
"name" : "abc",
"jcr:title" : "def",
"fieldType": "text-input",
"asyncValidtionExpression": ""
},
"textinput-displayValueExpression" : {
"jcr:primaryType": "nt:unstructured",
"sling:resourceType" : "core/fd/components/form/textinput/v1/textinput",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@
"validationExpression": {
"type": "string",
"format": "json-formula"
},
"asyncValidtionExpression": {
"type": "string",
"format": "json-formula"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"enum": [
":type",
"appliedCssClassNames",
"asyncValidtionExpression",
"autocomplete",
"constraintMessages",
"dataFormat",
Expand Down Expand Up @@ -160,6 +161,7 @@
"enum": [
":type",
"appliedCssClassNames",
"asyncValidtionExpression",
"autocomplete",
"constraintMessages",
"dataFormat",
Expand Down Expand Up @@ -253,6 +255,7 @@
"enum": [
":type",
"appliedCssClassNames",
"asyncValidtionExpression",
"checked",
"constraintMessages",
"dataFormat",
Expand Down Expand Up @@ -357,6 +360,7 @@
"enum": [
":type",
"appliedCssClassNames",
"asyncValidtionExpression",
"constraintMessages",
"dataFormat",
"dataLayer",
Expand Down Expand Up @@ -450,6 +454,7 @@
"enum": [
":type",
"appliedCssClassNames",
"asyncValidtionExpression",
"constraintMessages",
"dataFormat",
"dataLayer",
Expand Down Expand Up @@ -533,6 +538,7 @@
"enum": [
":type",
"appliedCssClassNames",
"asyncValidtionExpression",
"constraintMessages",
"dataLayer",
"dataRef",
Expand Down Expand Up @@ -628,6 +634,7 @@
"enum": [
":type",
"appliedCssClassNames",
"asyncValidtionExpression",
"constraintMessages",
"dataLayer",
"dataRef",
Expand Down Expand Up @@ -720,6 +727,7 @@
"enum": [
":type",
"appliedCssClassNames",
"asyncValidtionExpression",
"constraintMessages",
"dataLayer",
"dataRef",
Expand Down Expand Up @@ -802,6 +810,7 @@
":type",
"accept",
"appliedCssClassNames",
"asyncValidtionExpression",
"constraintMessages",
"dataLayer",
"dataRef",
Expand Down Expand Up @@ -1070,6 +1079,7 @@
":itemsOrder",
"allowedComponents",
"appliedCssClassNames",
"asyncValidtionExpression",
"columnClassNames",
"columnCount",
"constraintMessages",
Expand Down Expand Up @@ -1157,6 +1167,7 @@
"enum": [
":type",
"appliedCssClassNames",
"asyncValidtionExpression",
"dataLayer",
"description",
"enabled",
Expand Down
2 changes: 2 additions & 0 deletions docs/architecture/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -391,3 +391,5 @@ void testGetProperties() {
```

Create one golden file per fixture variant (default, customized, datalayer). See `radiobutton/` for the naming convention (`exporter-radiobutton.json`, `exporter-radiobutton-customized.json`, etc.).

When authoring a new golden file, don't hand-compute the `id` field (a hash derived from the resource path) — write the golden file with a placeholder/guessed `id`, run the new test once, and copy the actual `id` out of the assertion failure diff.
12 changes: 12 additions & 0 deletions ui.frontend/__tests__/RuleUtils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,18 @@ test('extractFunctionNames extracts from validationExpression and displayValueEx
expect(ids).toContain('formatPhone');
});

test('extractFunctionNames extracts from asyncValidtionExpression', () => {
const formJson = {
':items': {
field1: {
asyncValidtionExpression: 'asyncValidate($value)'
}
}
};
const result = RuleUtils.extractFunctionNames(formJson);
expect(result.map(f => f.id)).toContain('asyncValidate');
});

test('extractFunctionNames extracts from nested panel items', () => {
const formJson = {
':items': {
Expand Down
3 changes: 3 additions & 0 deletions ui.frontend/src/RuleUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ class RuleUtils {
if (item.validationExpression) {
extractFunctionNamesFromRuleExpression(item.validationExpression);
}
if (item.asyncValidtionExpression) {
extractFunctionNamesFromRuleExpression(item.asyncValidtionExpression);
}
if (item.displayValueExpression) {
extractFunctionNamesFromRuleExpression(item.displayValueExpression);
}
Expand Down
Loading