diff --git a/src/formio/validators/plugins.js b/src/formio/validators/plugins.js
index 129617a42..625bf7c17 100644
--- a/src/formio/validators/plugins.js
+++ b/src/formio/validators/plugins.js
@@ -1,41 +1,30 @@
import {post} from '../../api';
-const errorMessageMap = {
- 'kvk-kvkNumber': 'Invalid Kvk Number',
- 'kvk-rsin': 'Invalid RSIN',
- 'kvk-branchNumber': 'Invalid Branch Number',
- 'phonenumber-international': 'Invalid international phonenumber',
- 'phonenumber-nl': 'Invalid Dutch phonenumber',
-};
+export const pluginsAPIValidator = {
+ key: `validate.backendApi`,
+ check(component, setting, value) {
+ if (!value) return true;
-const pluginAPIValidator = plugin => {
- let defaultMsg = errorMessageMap[plugin];
- // catches undefined too
- if (defaultMsg == null) {
- defaultMsg = 'Invalid';
- }
+ const plugins = component.component.validate.plugins;
+ const {baseUrl} = component.currentForm?.options || component.options;
- return {
- key: `validate.${plugin}`,
- message(component) {
- return component.t(component.errorMessage(defaultMsg), {
- field: component.errorLabel,
- data: component.data,
+ const promises = plugins.map(plugin => {
+ const url = `${baseUrl}validation/plugins/${plugin}`;
+ return post(url, {value}).then(response => {
+ const valid = response.data.isValid;
+ return valid ? true : response.data.messages.join('
');
});
- },
- check(component, setting, value) {
- if (!value) return true;
+ });
+ return Promise.all(promises)
+ .then(results => {
+ const anyValid = results.some(result => result === true);
- const {baseUrl} = component.currentForm?.options || component.options;
- const url = `${baseUrl}validation/plugins/${plugin}`;
- return post(url, {value})
- .then(response => {
- const valid = response.data.isValid;
- return valid ? true : response.data.messages.join('
');
- })
- .catch(() => false);
- },
- };
+ if (anyValid) return true;
+
+ return results.join('
');
+ })
+ .catch(() => false);
+ },
};
/**
@@ -46,13 +35,9 @@ const pluginAPIValidator = plugin => {
*/
const enableValidationPlugins = component => {
if (Array.isArray(component.component.validate.plugins)) {
- for (let plugin of component.component.validate.plugins) {
- const validator = pluginAPIValidator(plugin);
- if (validator == null) continue;
- component.component.validateOn = 'blur';
- component.validator.validators[plugin] = validator;
- component.validators.push(plugin);
- }
+ component.component.validateOn = 'blur';
+ component.validator.validators.backendApi = pluginsAPIValidator;
+ component.validators.push('backendApi');
}
};
diff --git a/src/jstests/formio/components/fixtures/phonenumber.js b/src/jstests/formio/components/fixtures/phonenumber.js
new file mode 100644
index 000000000..effad92bb
--- /dev/null
+++ b/src/jstests/formio/components/fixtures/phonenumber.js
@@ -0,0 +1,15 @@
+const phoneNumberComponent = {
+ label: 'Phone Number',
+ key: 'phonenumber',
+ type: 'phoneNumber',
+ input: true,
+ validate: {
+ backendApi: true,
+ plugins: ['phonenumber-international', 'phonenumber-nl'],
+ },
+};
+
+const validSamples = ['0630123456', '+31630123456'];
+const inValidSamples = ['63012345', '+3163012345'];
+
+export {phoneNumberComponent, validSamples, inValidSamples};
diff --git a/src/jstests/formio/validators/mocks.js b/src/jstests/formio/validators/mocks.js
new file mode 100644
index 000000000..4f96aae89
--- /dev/null
+++ b/src/jstests/formio/validators/mocks.js
@@ -0,0 +1,36 @@
+import {rest} from 'msw';
+
+import {BASE_URL} from 'api-mocks';
+
+const INTERNATIONAL_VALIDATION_ENDPOINT = `${BASE_URL}validation/plugins/phonenumber-international`;
+const DUTCH_VALIDATION_ENDPOINT = `${BASE_URL}validation/plugins/phonenumber-nl`;
+
+export const phoneNumberValidations = {
+ mockValidInternationalPhonenumberPost: rest.post(
+ INTERNATIONAL_VALIDATION_ENDPOINT,
+ async (req, res, ctx) => {
+ return res(ctx.status(200), ctx.json({isValid: true, messages: []}));
+ }
+ ),
+
+ mockInValidInternationalPhonenumberPost: rest.post(
+ INTERNATIONAL_VALIDATION_ENDPOINT,
+ async (req, res, ctx) => {
+ return res(
+ ctx.status(200),
+ ctx.json({isValid: false, messages: ['Invalid international phone number']})
+ );
+ }
+ ),
+
+ mockValidDutchPhonenumberPost: rest.post(DUTCH_VALIDATION_ENDPOINT, async (req, res, ctx) => {
+ return res(ctx.status(200), ctx.json({isValid: true, messages: []}));
+ }),
+
+ mockInValidDutchPhonenumberPost: rest.post(DUTCH_VALIDATION_ENDPOINT, async (req, res, ctx) => {
+ return res(
+ ctx.status(200),
+ ctx.json({isValid: false, messages: ['Invalid dutch phone number']})
+ );
+ }),
+};
diff --git a/src/jstests/formio/validators/pluginapivalidator.spec.js b/src/jstests/formio/validators/pluginapivalidator.spec.js
new file mode 100644
index 000000000..8e18bfae2
--- /dev/null
+++ b/src/jstests/formio/validators/pluginapivalidator.spec.js
@@ -0,0 +1,68 @@
+import {
+ inValidSamples,
+ phoneNumberComponent,
+ validSamples,
+} from 'jstests/formio/components/fixtures/phonenumber';
+
+import {BASE_URL} from 'api-mocks';
+import mswServer from 'api-mocks/msw-server';
+import {pluginsAPIValidator} from 'formio/validators/plugins';
+
+import {phoneNumberValidations} from './mocks';
+
+describe('The OpenForms plugins validation', () => {
+ test('tests expected errors are returned when phone number is invalid', async () => {
+ mswServer.use(
+ phoneNumberValidations.mockInValidDutchPhonenumberPost,
+ phoneNumberValidations.mockInValidInternationalPhonenumberPost
+ );
+
+ const component = {
+ component: phoneNumberComponent,
+ options: {
+ baseUrl: BASE_URL,
+ },
+ };
+
+ for (const sample of inValidSamples) {
+ const result = await pluginsAPIValidator.check(component, undefined, sample);
+ expect(result).toBe('Invalid international phone number
Invalid dutch phone number');
+ }
+ });
+
+ test('tests no errors are returned when phone number is valid', async () => {
+ mswServer.use(
+ phoneNumberValidations.mockValidDutchPhonenumberPost,
+ phoneNumberValidations.mockValidInternationalPhonenumberPost
+ );
+
+ const component = {
+ component: phoneNumberComponent,
+ options: {
+ baseUrl: BASE_URL,
+ },
+ };
+
+ for (const sample of validSamples) {
+ const result = await pluginsAPIValidator.check(component, undefined, sample);
+ expect(result).toBe(true);
+ }
+ });
+
+ test('tests no errors are returned when phone number is null', async () => {
+ mswServer.use(
+ phoneNumberValidations.mockValidDutchPhonenumberPost,
+ phoneNumberValidations.mockValidInternationalPhonenumberPost
+ );
+
+ const component = {
+ component: phoneNumberComponent,
+ options: {
+ baseUrl: BASE_URL,
+ },
+ };
+
+ const result = await pluginsAPIValidator.check(component, undefined, null);
+ expect(result).toBe(true);
+ });
+});