Skip to content

Commit

Permalink
feat: support layout elements in smart action form hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
Nicolas Moreau committed Oct 15, 2024
1 parent 92a05cb commit 090b543
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 11 deletions.
1 change: 1 addition & 0 deletions src/context/build-services.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,6 @@ module.exports.default = (context) =>
.addUsingClass('oidcClientManagerService', () => require('../services/oidc-client-manager'))
.addUsingClass('authenticationService', () => require('../services/authentication'))
.addUsingClass('smartActionFieldValidator', () => require('../services/smart-action-field-validator'))
.addUsingClass('smartActionFormLayoutService', () => require('../services/smart-action-form-layout-service'))
.addUsingClass('smartActionHookService', () => require('../services/smart-action-hook-service'))
.addUsingClass('smartActionHookDeserializer', () => require('../deserializers/smart-action-hook'));
9 changes: 4 additions & 5 deletions src/routes/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,13 @@ class Actions {
getHookLoadController(action) {
return async (request, response) => {
try {
const loadedFields = await this.smartActionHookService.getResponse(
const hookResponse = await this.smartActionHookService.getResponse(
action,
action.hooks.load,
action.fields,
request,
);

return response.status(200).send({ fields: loadedFields });
return response.status(200).send(hookResponse);
} catch (error) {
this.logger.error('Error in smart load action hook: ', error);
return response.status(500).send({ message: error.message });
Expand All @@ -58,15 +57,15 @@ class Actions {
const { fields, changedField } = data;
const fieldChanged = fields.find((field) => field.field === changedField);

const updatedFields = await this.smartActionHookService.getResponse(
const hookResponse = await this.smartActionHookService.getResponse(
action,
action.hooks.change[fieldChanged?.hook],
fields,
request,
fieldChanged,
);

return response.status(200).send({ fields: updatedFields });
return response.status(200).send(hookResponse);
} catch (error) {
this.logger.error('Error in smart action change hook: ', error);
return response.status(500).send({ message: error.message });
Expand Down
79 changes: 79 additions & 0 deletions src/services/smart-action-form-layout-service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
class SmartActionFormLayoutService {
static lowerCaseFirstLetter(string) {
return string.charAt(0).toLowerCase() + string.slice(1);
}

static validateLayoutElement(element) {
const validLayoutComponents = ['Row', 'Page', 'Separator', 'HtmlBlock'];
if (validLayoutComponents.includes(element.component)) throw new Error(`${element.component} is not a valid component. Use ${validLayoutComponents.join(' or ')}`);
if (element.component === 'Page' && !Array.isArray(element.elements)) {
throw new Error('Page components must contain an array of fields or layout elements in property \'elements\'');
}
if (element.component === 'Row' && (!Array.isArray(element.fields))) {
throw new Error('Row components must contain an array of fields in property \'fields\'');
}
}

static parseLayout(
element,
allFields,
) {
if (element.type === 'Layout') {
SmartActionFormLayoutService.validateLayoutElement(element);
const subElementsKey = {
Row: 'fields',
Page: 'elements',
};

if (element.component === 'Row' || element.component === 'Page') {
const key = subElementsKey[element.component];
const subElements = element[key].map(
(field) => SmartActionFormLayoutService.parseLayout(field, allFields),
);

return {
...element,
component: SmartActionFormLayoutService.lowerCaseFirstLetter(element.component),
[key]: subElements,
};
}
return {
...element,
component: SmartActionFormLayoutService.lowerCaseFirstLetter(element.component),
};
}

allFields.push(element);

return {
type: 'Layout',
component: 'input',
fieldId: element.field,
};
}

// eslint-disable-next-line class-methods-use-this
extractFieldsAndLayout(formElements) {
let hasLayout = false;
const fields = [];
let layout = [];

if (!formElements) return { fields: [], layout: [] };

formElements.forEach((element) => {
if (element.type === 'Layout') {
hasLayout = true;
}

layout.push(SmartActionFormLayoutService.parseLayout(element, fields));
});

if (!hasLayout) {
layout = [];
}

return { fields, layout };
}
}

module.exports = SmartActionFormLayoutService;
16 changes: 10 additions & 6 deletions src/services/smart-action-hook-service.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
class SmartActionHookService {
constructor({ setFieldWidget, smartActionFieldValidator }) {
constructor({ setFieldWidget, smartActionFieldValidator, smartActionFormLayoutService }) {
this.setFieldWidget = setFieldWidget;
this.smartActionFieldValidator = smartActionFieldValidator;
this.smartActionFormLayoutService = smartActionFormLayoutService;
}

/**
Expand Down Expand Up @@ -32,13 +33,16 @@ class SmartActionHookService {
if (typeof hook !== 'function') throw new Error('hook must be a function');

// Call the user-defined load hook.
const result = await hook({ request, fields: fieldsForUser, changedField });
const hookResult = await hook({ request, fields: fieldsForUser, changedField });

if (!(result && Array.isArray(result))) {
const { fields: fieldHookResult, layout } = this.smartActionFormLayoutService
.extractFieldsAndLayout(hookResult);

if (!(fieldHookResult && Array.isArray(fieldHookResult))) {
throw new Error('hook must return an array');
}

return result.map((field) => {
const validFields = fieldHookResult.map((field) => {
this.smartActionFieldValidator.validateField(field, action.name);
this.smartActionFieldValidator
.validateFieldChangeHook(field, action.name, action.hooks?.change);
Expand All @@ -59,9 +63,9 @@ class SmartActionHookService {
return { ...field, value: null };
}
}

return field;
return { ...field };
});
return { fields: validFields, layout };
}
}

Expand Down

0 comments on commit 090b543

Please sign in to comment.