Skip to content

Commit 6d55ca4

Browse files
deepprakash345Deep Prakash DewanjiDeep Prakash Dewanji
authored
feat: added fd:submit namespace in headless json in case of Submissio… (#1540)
* feat: added fd:submit namespace in headless json in case of Submission View * feat: added fd:submit namespace in headless json in case of Submission View * [feat]: change in exported json to support routing and submission processing * feat: added fd:submit namespace in headless json in case of Submission View * feat: added fd:submit namespace in headless json in case of Submission View * [feat]: change in exported json to support routing and submission processing * fix: added UTs for changes in getAction method with all different scenarios * fix: refactoring to add review comments * fix: added exporter jsons and javadocs as recommended in review * fix: added exporter jsons and javadocs as recommended in review * fix: added exporter jsons and javadocs as recommended in review * fix: moved fd:submit inside properties of exported json and added javadocs * fix: integrated with working fastly api and made minor tweaks in tests and processing code to process response --------- Co-authored-by: Deep Prakash Dewanji <deepprakashdewanji@labusers-mbp-9.corp.adobe.com> Co-authored-by: Deep Prakash Dewanji <deepprakashdewanji@labusers-MacBook-Pro-9.local>
1 parent 66cc02f commit 6d55ca4

14 files changed

+848
-5
lines changed

bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/form/FormConstants.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,4 +142,10 @@ private FormConstants() {
142142

143143
/* The resource type for the pre-selected the linked panel */
144144
public final static String RT_FD_FORM_REVIEW_DATASOURCE_V1 = RT_FD_FORM_PREFIX + "review/v1/datasource";
145+
146+
/** Request header/attribute name for form definition jsonview type */
147+
public static final String X_ADOBE_FORM_DEFINITION = "x-adobe-form-definition";
148+
149+
/** Form definition type indicating submission view */
150+
public static final String FORM_DEFINITION_SUBMISSION = "submission";
145151
}

bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/form/FormStructureParserImpl.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
import com.adobe.cq.forms.core.components.models.form.FormContainer;
3232
import com.adobe.cq.forms.core.components.models.form.FormStructureParser;
33+
import com.adobe.cq.forms.core.components.models.form.HtlUtil;
3334
import com.adobe.cq.forms.core.components.util.ComponentUtils;
3435
import com.adobe.cq.forms.core.components.views.Views;
3536
import com.fasterxml.jackson.core.SerializableString;
@@ -122,9 +123,19 @@ public String getFormDefinition() {
122123
HTMLCharacterEscapes htmlCharacterEscapes = new HTMLCharacterEscapes();
123124
ObjectMapper mapper = new ObjectMapper();
124125
Writer writer = new StringWriter();
125-
ObjectWriter objectWriter = mapper.writerWithView(Views.Publish.class);
126+
ObjectWriter objectWriter;
127+
boolean isSubmissionView = false;
128+
if (request != null) {
129+
HtlUtil htlUtil = request.adaptTo(HtlUtil.class);
130+
isSubmissionView = (htlUtil != null && htlUtil.isEdgeDeliveryRequest())
131+
|| ComponentUtils.shouldIncludeSubmitProperties(request);
132+
}
133+
134+
if (isSubmissionView) {
135+
request.setAttribute(FormConstants.X_ADOBE_FORM_DEFINITION, FormConstants.FORM_DEFINITION_SUBMISSION);
136+
}
137+
objectWriter = mapper.writerWithView(Views.Publish.class);
126138
objectWriter.getFactory().setCharacterEscapes(htmlCharacterEscapes);
127-
// return publish view specific properties only for runtime
128139
objectWriter.writeValue(writer, formContainer);
129140
result = writer.toString();
130141
} catch (Exception e) {

bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/form/ReservedProperties.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,18 @@ private ReservedProperties() {
166166
public static final String FD_XFA_SCRIPTS = "fd:xfaScripts";
167167

168168
public static final String FD_DRAFT_ID = "fd:draftId";
169+
170+
// Begin: Form submission related properties
171+
public static final String FD_SUBMIT_PROPERTIES = "fd:submit";
172+
public static final String PN_SUBMIT_ACTION_TYPE = "actionType";
173+
public static final String PN_SUBMIT_ACTION_NAME = "actionName";
174+
public static final String PN_SUBMIT_EMAIL_TO = "mailto";
175+
public static final String PN_SUBMIT_EMAIL_FROM = "from";
176+
public static final String PN_SUBMIT_EMAIL_SUBJECT = "subject";
177+
public static final String PN_SUBMIT_EMAIL_CC = "cc";
178+
public static final String PN_SUBMIT_EMAIL_BCC = "bcc";
179+
public static final String PN_SUBMIT_SPREADSHEETURL = "spreadsheetUrl";
180+
// End: Form submission related properties
169181
private static final Set<String> reservedProperties = aggregateReservedProperties();
170182

171183
private static Set<String> aggregateReservedProperties() {

bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v2/form/FormContainerImpl.java

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,19 @@
1515
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1616
package com.adobe.cq.forms.core.components.internal.models.v2.form;
1717

18+
import java.util.Arrays;
1819
import java.util.LinkedHashMap;
20+
import java.util.List;
1921
import java.util.Map;
2022
import java.util.function.Consumer;
2123

2224
import javax.annotation.PostConstruct;
2325

2426
import org.apache.commons.lang3.StringUtils;
27+
import org.apache.http.osgi.services.HttpClientBuilderFactory;
2528
import org.apache.sling.api.SlingHttpServletRequest;
2629
import org.apache.sling.api.resource.Resource;
30+
import org.apache.sling.api.resource.ValueMap;
2731
import org.apache.sling.models.annotations.Default;
2832
import org.apache.sling.models.annotations.Exporter;
2933
import org.apache.sling.models.annotations.Model;
@@ -80,9 +84,18 @@ public class FormContainerImpl extends AbstractContainerImpl implements FormCont
8084
private static final String FD_CUSTOM_FUNCTIONS_URL = "fd:customFunctionsUrl";
8185
private static final String FD_DATA_URL = "fd:dataUrl";
8286

87+
/** Constant representing email submit action type */
88+
private static final String SS_EMAIL = "email";
89+
90+
/** Constant representing spreadsheet submit action type */
91+
private static final String SS_SPREADSHEET = "spreadsheet";
92+
8393
@OSGiService(injectionStrategy = InjectionStrategy.OPTIONAL)
8494
private CoreComponentCustomPropertiesProvider coreComponentCustomPropertiesProvider;
8595

96+
@OSGiService(injectionStrategy = InjectionStrategy.OPTIONAL)
97+
private HttpClientBuilderFactory clientBuilderFactory;
98+
8699
private static final String DRAFT_PREFILL_SERVICE = "service://FP/draft/";
87100

88101
@SlingObject(injectionStrategy = InjectionStrategy.OPTIONAL)
@@ -277,6 +290,16 @@ public String getRoleAttribute() {
277290

278291
@Override
279292
public String getAction() {
293+
List<String> supportedSubmitActions = ComponentUtils.getSupportedSubmitActions(clientBuilderFactory);
294+
String resourceType = resource.getValueMap().get("sling:resourceType", String.class);
295+
if (supportedSubmitActions.contains(resource.getValueMap().get(ReservedProperties.PN_SUBMIT_ACTION_NAME))) {
296+
if (resourceType != null && resourceType.contains("/franklin")) {
297+
return "";
298+
} else {
299+
return "https://forms.adobe.com" + ADOBE_GLOBAL_API_ROOT + FORMS_RUNTIME_API_GLOBAL_ROOT + "/submit/" +
300+
ComponentUtils.getEncodedPath(resource.getPath() + ".model.json");
301+
}
302+
}
280303
return getContextPath() + ADOBE_GLOBAL_API_ROOT + FORMS_RUNTIME_API_GLOBAL_ROOT + "/submit/" + getId();
281304
}
282305

@@ -359,7 +382,10 @@ public String getLanguageDirection() {
359382
}
360383
properties.put(FD_CUSTOM_FUNCTIONS_URL, getCustomFunctionUrl());
361384
properties.put(FD_DATA_URL, getDataUrl());
362-
385+
Map<String, Object> submitProperties = getSubmitProperties();
386+
if (submitProperties != null && !submitProperties.isEmpty()) {
387+
properties.put(ReservedProperties.FD_SUBMIT_PROPERTIES, submitProperties);
388+
}
363389
return properties;
364390
}
365391

@@ -424,4 +450,39 @@ public AutoSaveConfiguration getAutoSaveConfig() {
424450
return autoSaveConfig;
425451
}
426452

453+
private Map<String, Object> getSubmitProperties() {
454+
455+
Map<String, Object> submitProps = null;
456+
457+
if (request == null || ComponentUtils.shouldIncludeSubmitProperties(request)) {
458+
submitProps = new LinkedHashMap<>();
459+
List<String> submitActionProperties = Arrays.asList(
460+
ReservedProperties.PN_SUBMIT_ACTION_TYPE,
461+
ReservedProperties.PN_SUBMIT_ACTION_NAME);
462+
463+
List<String> submitEmailProperties = Arrays.asList(
464+
ReservedProperties.PN_SUBMIT_EMAIL_TO,
465+
ReservedProperties.PN_SUBMIT_EMAIL_FROM,
466+
ReservedProperties.PN_SUBMIT_EMAIL_SUBJECT,
467+
ReservedProperties.PN_SUBMIT_EMAIL_CC,
468+
ReservedProperties.PN_SUBMIT_EMAIL_BCC);
469+
470+
List<String> submitSpreadsheetProperties = Arrays.asList(
471+
ReservedProperties.PN_SUBMIT_SPREADSHEETURL);
472+
ValueMap resourceMap = resource.getValueMap();
473+
for (Map.Entry<String, Object> entry : resourceMap.entrySet()) {
474+
if (submitActionProperties.contains(entry.getKey())) {
475+
submitProps.put(entry.getKey(), entry.getValue());
476+
} else if (submitEmailProperties.contains(entry.getKey())) {
477+
submitProps.computeIfAbsent(SS_EMAIL, k -> new LinkedHashMap<String, Object>());
478+
((Map<String, Object>) submitProps.get(SS_EMAIL)).put(entry.getKey(), entry.getValue());
479+
} else if (submitSpreadsheetProperties.contains(entry.getKey())) {
480+
submitProps.computeIfAbsent(SS_SPREADSHEET, k -> new LinkedHashMap<String, Object>());
481+
((Map<String, Object>) submitProps.get(SS_SPREADSHEET)).put(entry.getKey(), entry.getValue());
482+
}
483+
}
484+
}
485+
return submitProps;
486+
}
487+
427488
}

bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/util/ComponentUtils.java

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,41 @@
1616
package com.adobe.cq.forms.core.components.util;
1717

1818
import java.nio.charset.StandardCharsets;
19+
import java.util.ArrayList;
1920
import java.util.Arrays;
2021
import java.util.Base64;
2122
import java.util.Date;
23+
import java.util.List;
2224
import java.util.Objects;
2325
import java.util.Optional;
2426

2527
import org.apache.commons.lang3.ArrayUtils;
2628
import org.apache.commons.lang3.StringUtils;
29+
import org.apache.http.client.config.RequestConfig;
30+
import org.apache.http.client.methods.CloseableHttpResponse;
31+
import org.apache.http.client.methods.HttpGet;
32+
import org.apache.http.impl.client.CloseableHttpClient;
33+
import org.apache.http.osgi.services.HttpClientBuilderFactory;
34+
import org.apache.http.util.EntityUtils;
2735
import org.apache.jackrabbit.JcrConstants;
36+
import org.apache.sling.api.SlingHttpServletRequest;
2837
import org.apache.sling.api.resource.Resource;
2938
import org.apache.sling.api.resource.ResourceResolver;
3039
import org.apache.sling.api.resource.ValueMap;
3140
import org.jetbrains.annotations.NotNull;
3241
import org.jetbrains.annotations.Nullable;
42+
import org.slf4j.Logger;
43+
import org.slf4j.LoggerFactory;
3344

3445
import com.adobe.aemds.guide.utils.GuideUtils;
3546
import com.adobe.cq.forms.core.components.internal.form.FormConstants;
3647
import com.adobe.cq.forms.core.components.models.form.BaseConstraint;
3748
import com.day.cq.i18n.I18n;
3849
import com.day.cq.wcm.api.policies.ContentPolicy;
3950
import com.day.cq.wcm.api.policies.ContentPolicyManager;
51+
import com.fasterxml.jackson.databind.JsonNode;
52+
import com.fasterxml.jackson.databind.ObjectMapper;
53+
import com.fasterxml.jackson.databind.node.ArrayNode;
4054

4155
import static com.adobe.cq.forms.core.components.internal.form.FormConstants.FORM_FIELD_TYPE;
4256

@@ -47,6 +61,7 @@ public class ComponentUtils {
4761

4862
private static final String EDGE_DELIVERY_FRAGMENT_CONTAINER_REL_PATH = "root/section/form";
4963
private static final String[] EDGE_DELIVERY_RESOURCE_TYPES = new String[] { "core/franklin/components/page/v1/page" };
64+
private static final Logger logger = LoggerFactory.getLogger(ComponentUtils.class);
5065

5166
/**
5267
* Private constructor to prevent instantiation of utility class.
@@ -244,4 +259,89 @@ public static boolean isFragmentComponent(Resource resource) {
244259
return resource != null && resource.getValueMap().get(FormConstants.PROP_FRAGMENT_PATH, String.class) != null;
245260
}
246261

262+
/**
263+
* Determines whether submit properties should be included in the form
264+
* definition.
265+
*
266+
* <p>
267+
* This method checks if the request contains x-adobe-form-definition attribute
268+
* or header and if it is equal to submission
269+
* that indicates submit properties should be included in the form definition.
270+
* </p>
271+
*
272+
* @param request the {@link SlingHttpServletRequest} to check
273+
* @return true if submit properties should be included, false otherwise
274+
*/
275+
public static boolean shouldIncludeSubmitProperties(SlingHttpServletRequest request) {
276+
if (request == null) {
277+
return false;
278+
}
279+
String submissionHeaderName = FormConstants.FORM_DEFINITION_SUBMISSION;
280+
return submissionHeaderName.equals(request.getAttribute(FormConstants.X_ADOBE_FORM_DEFINITION)) ||
281+
submissionHeaderName.equals(request.getHeader(FormConstants.X_ADOBE_FORM_DEFINITION));
282+
}
283+
284+
/**
285+
* Retrieves a list of supported submit actions from the Adobe Forms service.
286+
*
287+
* <p>
288+
* This method makes an HTTP GET request to the Adobe Forms service to fetch
289+
* the list of supported submit actions. The response is expected to be a
290+
* JSON object containing a "submissions" array of action names, which is then
291+
* converted to a list.
292+
* </p>
293+
*
294+
* <p>
295+
* If the request fails or an error occurs during processing, an error is logged
296+
* and an empty list is returned.
297+
* </p>
298+
*
299+
* @param clientBuilderFactory The HTTP client builder factory used to create
300+
* the HTTP client
301+
* @return A list of supported submit action names, or an empty list if the
302+
* request fails
303+
*/
304+
public static List<String> getSupportedSubmitActions(HttpClientBuilderFactory clientBuilderFactory) {
305+
String supportedSubmitActionsUrl = "https://forms.adobe.com/adobe/forms/af/submit";
306+
List<String> supportedSubmitActions = new ArrayList<>();
307+
if (clientBuilderFactory == null) {
308+
logger.error("clientBuilderFactory is null");
309+
return supportedSubmitActions;
310+
}
311+
try {
312+
RequestConfig requestConfig = RequestConfig.custom()
313+
.setConnectTimeout(5000)
314+
.setSocketTimeout(5000)
315+
.setConnectionRequestTimeout(5000)
316+
.build();
317+
CloseableHttpClient httpClient = clientBuilderFactory.newBuilder()
318+
.setDefaultRequestConfig(requestConfig)
319+
.build();
320+
HttpGet httpGet = new HttpGet(supportedSubmitActionsUrl);
321+
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
322+
if (isSuccessfulResponse(response)) {
323+
String responseBody = EntityUtils.toString(response.getEntity());
324+
JsonNode rootNode = new ObjectMapper().readTree(responseBody);
325+
ArrayNode submissionsNode = (ArrayNode) rootNode.get("supported");
326+
if (submissionsNode != null && submissionsNode.isArray()) {
327+
submissionsNode.forEach(submission -> {
328+
String submissionText = submission.asText();
329+
supportedSubmitActions.add(submissionText);
330+
});
331+
} else {
332+
logger.error("supported node was null or not an array");
333+
}
334+
}
335+
}
336+
} catch (Exception e) {
337+
logger.error("Error while fetching supported submit actions", e);
338+
}
339+
return supportedSubmitActions;
340+
}
341+
342+
private static boolean isSuccessfulResponse(CloseableHttpResponse response) {
343+
return response.getStatusLine() != null
344+
&& response.getStatusLine().getStatusCode() == java.net.HttpURLConnection.HTTP_OK;
345+
}
346+
247347
}

bundles/af-core/src/test/java/com/adobe/cq/forms/core/Utils.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ public static void testJSONExport(Object model, String expectedJsonResource) {
106106
fail("Unable to find test file " + expectedJsonResource + ".");
107107
}
108108
IOUtils.closeQuietly(is);
109-
// this is added so that other components like form portal, aem form etc don't get validated against schema
109+
// this is added so that other components like form portal, aem form etc don't
110+
// get validated against schema
110111
if (model instanceof FormComponent) {
111112
Utils.testSchemaValidation(model);
112113
}

0 commit comments

Comments
 (0)