libs/core-forms/src/lib/backend-integration-plugin/backend-integration-plugin.ts
Methods |
constructor()
|
registerActions |
registerActions()
|
Returns :
void
|
⚠️ This plugin is for internal usage only!
pfe.jsonc
file, add the CORE_FORMS_BACKEND_INTEGRATION
to the onPageLeaveActions
of the page where the data should be sent to the backend. There is no action provided for catching the sitekey
of the Turnstile. This is done automatically by the plugin as soon as it has been initialized. The sitekey
will be stored in the $._taly.turnstileSitekey
key of the PFE state. If the request for the sitekey
fails, turnstileSitekey
will be set to an empty string."pages": [
{
"pageId": "some-page-id",
"onPageLeaveActions": [
{
"type": "CORE_FORMS_BACKEND_INTEGRATION"
}
]
}
]
@allianz/taly-core-forms
package to the libraries
array of the pages.jsonc
file"libraries": [
{
"package": "@allianz/taly-core-forms",
"version": "CURRENT_VERSION"
}
]
BackendIntegrationPluginModule
with the options below to the plugins
array of the pages.jsonc
file. talyPrefix
describes how the dynamic form id
s have to start to be recognized by the plugin."plugins": [
{
"package": "@allianz/taly-core-forms",
"modules": [
{
"name": "BackendIntegrationPluginModule",
"options": {
"formId": "1740391937943_v3",
"tenant": "sandbox",
"site": "coreforms",
"instance": "emeaprd",
"talyTurnstileFormfieldId": "turnstile-token",
"talyPrefix": "core-forms-"
}
}
]
}
],
talyTurnstileFormfieldId
) you specified in step 2. This Turnstile custom component has to be a child of a dynamic form with an id starting with the talyPrefix
you defined in step 2.The snippet below shows an example pages.jsonc
configuration of the backend and turnstile plugin in combination with a dynamic form with one input field.
"libraries": [
{
"package": "@allianz/taly-core-forms",
"version": ""
}
],
"plugins": [
{
"package": "@allianz/taly-core-forms",
"modules": [
{
"name": "BackendIntegrationPluginModule",
"options": {
"formId": "123456789",
"tenant": "sandbox",
"site": "coreforms",
"instance": "emeaprd",
"talyTurnstileFormfieldId": "turnstile-token",
"talyPrefix": "core-forms-"
}
}
]
},
{
"package": "@allianz/taly-core-forms",
"modules": [
{
"name": "TurnstileComponentPluginModule"
}
]
}
],
"pages": [
{
"id": "first-page",
"blocks": [
{
"id": "core-forms-my-dynamic-form1",
"form": {
"layout": {
"type": "ONE_COLUMN"
},
"fields": [
{
"id": "street",
"type": "INPUT",
"label": "street",
"inputType": "text"
},
{
"type": "CUSTOM_COMPONENT",
"name": "TurnstileComponent",
"id": "turnstile-token",
"config": {
"sitekey": "1x00000000000000000000AA"
}
}
]
}
}
],
}
]
import { PfeActionsService, PfeBusinessService } from '@allianz/ngx-pfe';
import {
BuildingBlockConfiguration,
DynamicFormBBConfiguration,
PageConfiguration
} from '@allianz/taly-core/schemas';
import { PfeRuntimeConfigService } from '@allianz/taly-pfe-connector';
import { HttpClient, HttpParams } from '@angular/common/http';
import { effect, inject, Injectable } from '@angular/core';
import { CoreFormsBackendIntegrationPluginOptions } from './backend-integration-plugin.module';
@Injectable()
export class BackendIntegrationPlugin {
private readonly pfeActionService = inject(PfeActionsService);
private readonly pfeBusinessService = inject(PfeBusinessService);
private readonly runtimeConfigService = inject(PfeRuntimeConfigService);
private readonly http = inject(HttpClient);
private options!: CoreFormsBackendIntegrationPluginOptions;
private readonly requiredStringProperties: Array<keyof CoreFormsBackendIntegrationPluginOptions> =
['formId', 'tenant', 'site', 'instance', 'talyTurnstileFormfieldId', 'talyPrefix'];
private readonly coreFormsBaseUrl = 'https://forms.di-api.allianz.com/prd/forms';
constructor() {
effect(async () => {
const { pages, plugins } = this.runtimeConfigService.pagesConfig();
const coreFormsPluginConfig = plugins?.find(
(plugin) => plugin.package === '@allianz/taly-core-forms'
);
const backendIntegrationPluginModule = coreFormsPluginConfig?.modules?.find(
(module) => module.name === 'BackendIntegrationPluginModule'
);
const isCoreFormsBackendIntegrationPluginOptions = (
obj: unknown
): obj is CoreFormsBackendIntegrationPluginOptions => {
if (!obj || typeof obj !== 'object') {
return false;
}
const options = obj as Record<string, unknown>;
return this.requiredStringProperties.every(
(prop) => prop in options && typeof options[prop] === 'string'
);
};
if (isCoreFormsBackendIntegrationPluginOptions(backendIntegrationPluginModule?.options)) {
this.options = backendIntegrationPluginModule.options;
await this.fetchTurnstileSitekey(pages);
} else {
this.pfeBusinessService.storeValueByExpression('$._taly.turnstileSitekey', '');
console.error(
`Invalid options format for BackendIntegrationPlugin. Please provide the following plugin options: ${this.requiredStringProperties.join(
', '
)}.`
);
}
});
}
registerActions() {
this.pfeActionService.registerAction(
'CORE_FORMS_BACKEND_INTEGRATION',
this.sendFormDataCoreFormsBackend.bind(this)
);
}
private sendFormDataCoreFormsBackend(): Promise<void> {
const fields = this.getCoreFormsState(this.options.talyPrefix);
const turnstileToken = fields?.[this.options.talyTurnstileFormfieldId];
if (!turnstileToken || typeof turnstileToken !== 'string') {
throw new Error('Could not find the turnstile token in the form');
}
delete fields[this.options.talyTurnstileFormfieldId];
return this.postFormData(turnstileToken, fields);
}
private postFormData(
turnstileToken: string,
fieldsForBackend: Record<string, unknown>
): Promise<void> {
const { formId, tenant, site, instance } = this.options;
const referrer = window.location.href;
const data = JSON.stringify({
version: 1,
formId,
tenant,
site,
instance,
referrer,
fields: fieldsForBackend,
turnstileCaptcha: {
cfturnstileresponse: turnstileToken
},
captchaType: 'turnstile'
});
const options = {
hostname: 'forms.di-api.allianz.com',
port: 443,
path: '/prd/forms',
method: 'POST',
headers: {
'Content-Type': 'application/json',
accept: 'application/json'
}
};
return new Promise<void>((resolve) => {
this.http.post<{ message: string }>(this.coreFormsBaseUrl, data, options).subscribe({
next: (response) => {
this.pfeBusinessService.storeValueByExpression(
'$.coreFormSubmissionSuccessful',
response.message === 'successful'
);
resolve();
},
error: () => {
this.pfeBusinessService.storeValueByExpression('$.coreFormSubmissionSuccessful', false);
resolve();
}
});
});
}
private getCoreFormsState(prefix: string): Record<string, unknown> {
const pfeState = this.pfeBusinessService.getFullState();
return Object.entries(pfeState)
.filter(([formKey]) => formKey.startsWith(prefix)) // Filter keys that start with the prefix
.filter(([, formfields]) => typeof formfields === 'object' && formfields !== null)
.reduce<Record<string, unknown>>((relevantFields, [, formfields]) => {
Object.entries(formfields).forEach(([formfieldKey, formfieldValue]) => {
if (relevantFields[formfieldKey]) {
console.warn(
`The form field with id "${formfieldKey}" is present in more than one form with prefix "${prefix}". Please provide a unique id for each field.`
);
return;
}
if (formfieldValue !== null) {
relevantFields[formfieldKey] = formfieldValue;
}
});
return relevantFields;
}, {});
}
private fetchTurnstileSitekey(pages: PageConfiguration[]): Promise<void> {
const requestUrl = [
this.coreFormsBaseUrl,
this.options.instance,
this.options.tenant,
this.options.site,
this.options.formId
].join('/');
const referrer = window.location.href;
const relevantFormFieldIds = this.getRelevantFormFieldIds(pages);
const params = new HttpParams()
.set('referrer', referrer)
.set('formIds', relevantFormFieldIds.join(','));
return new Promise<void>((resolve) => {
this.http.head(requestUrl, { params, observe: 'response' }).subscribe({
next: (response) => {
const sitekey = response.headers.get('x-cf-turnstile-sitekey');
this.pfeBusinessService.storeValueByExpression('$._taly.turnstileSitekey', sitekey || '');
resolve();
},
error: (error) => {
this.pfeBusinessService.storeValueByExpression('$._taly.turnstileSitekey', '');
console.error(error);
resolve();
}
});
});
}
private getRelevantFormFieldIds(pages: PageConfiguration[]) {
const { talyTurnstileFormfieldId, talyPrefix } = this.options;
function isDynamicFormBbConfig(
block: DynamicFormBBConfiguration | BuildingBlockConfiguration | undefined
): block is DynamicFormBBConfiguration {
if (!block) {
return false;
}
return Boolean((block as DynamicFormBBConfiguration).form);
}
const formFields = pages
.flatMap((page) => page?.blocks)
.filter(isDynamicFormBbConfig)
.filter((block) => block?.id.startsWith(talyPrefix))
.flatMap((block) => block?.form?.fields);
const relevantFormFields = formFields
.filter((formfield) => formfield?.id !== talyTurnstileFormfieldId)
.filter(
(formfield) =>
formfield.type !== 'LINE_BREAK' &&
formfield.type !== 'HEADLINE' &&
formfield.type !== 'PARAGRAPH'
);
return relevantFormFields.map((formfield) => formfield.id);
}
}