How can I create a custom validation for my JSON component?

System Information
  • Strapi Version: 4.10.5
  • Operating System: Linux
  • Database: postgres
  • Node Version: v14.21.3
  • NPM Version: 6.14.18
  • Yarn Version: 1.22.19

Hello! I have this JSON component:

{
  "collectionName": "components_simple_module_simple_modules",
  "info": {
    "displayName": "Simple_module",
    "description": ""
  },
  "options": {},
  "attributes": {
    "theme": {
      "type": "enumeration",
      "enum": ["NEUTRAL", "PRIMARY"],
      "default": "NEUTRAL",
      "required": true
    },
    "main_title": {
      "type": "string",
      "maxLength": 100,
      "required": true
    },
    "main_description": {
      "type": "richtext",
      "maxLength": 600
    },
    "subtitle": {
      "displayName": "Subtitle",
      "type": "component",
      "repeatable": true,
      "component": "simple-module.subtitle",
      "max": 3
    },
    "cta": {
      "displayName": "cta",
      "type": "component",
      "repeatable": false,
      "component": "simple-module.cta"
    },
    "data_section_analytic": {
      "type": "string",
      "required": true
    }
  }
}

How and where do I create a custom validation for this JSON component where the cta can only be saved if the main_title and main_description are filled in?

I’ve already tried creating a hook and initializing it in the project’s bootstrap, I’ve tried creating it in ./extensions as well, but all unsuccessful attempts.

[RESOLVED]

My final code:

const { ValidationError } = require('@strapi/utils').errors;

const validateSimpleModule = (event) => {
  const { params } = event;
  const { data } = params;
  // eslint-disable-next-line camelcase
  const { main_title, main_description, cta, subtitle } = data;

  // eslint-disable-next-line camelcase
  if (main_title && cta) {
    // eslint-disable-next-line camelcase
    if (!main_description) {
      throw new ValidationError(
        `Não é possível salvar o conteúdo dessa forma!`,
      );
    }
  }

  // eslint-disable-next-line camelcase
  if (main_title) {
    // eslint-disable-next-line camelcase
    if (!main_description || !cta || !subtitle) {
      throw new ValidationError(
        `Não é possível salvar o conteúdo dessa forma!`,
      );
    }
  }
};

module.exports = {
  async bootstrap({ strapi }) {
    // In order to resolve 502 problems in internal elb(between elb and target group), setting headers to 61 to 65 seconds.
    strapi.server.httpServer.keepAliveTimeout = 61 * 1000;
    strapi.server.httpServer.headersTimeout = 65 * 1000;

    // Listen lifecyles
    strapi.db.lifecycles.subscribe({
      models: ['simple-module.simple-module'], // listen only this model

      async beforeCreate(event) {
        validateSimpleModule(event);
      },

      async beforeUpdate(event) {
        validateSimpleModule(event);
      },
    });
  },
};