api/utils: add some validations for mood, user and mood/emotion components

Pedro Lucas Porcellis porcellis@eletrotupi.com 6 months ago 06314a5b665164bc343bc1c0a38ca4a562c53b7b
Parents: f850d20
2 file(s) changed
  • api/src/utils/emotions.js +102 -0
  • api/src/utils/validators.js +96 -0
api/src/utils/emotions.js
@@ -0,0 +1,102 @@
1 + // Emotion types enum
2 + export const Emotions = {
3 + JOY: 'joy',
4 + TRUST: 'trust',
5 + FEAR: 'fear',
6 + SURPRISE: 'surprise',
7 + SAD: 'sad',
8 + DISGUST: 'disgust',
9 + ANGRY: 'angry',
10 + ANXIETY: 'anxiety'
11 + };
12 +
13 + // Valid emotion values for validation
14 + export const VALID_EMOTIONS = Object.values(Emotions);
15 +
16 + // Validate emotion type
17 + export function isValidEmotion(emotion) {
18 + return VALID_EMOTIONS.includes(emotion?.toLowerCase());
19 + }
20 +
21 + // Validate intensity value
22 + export function isValidIntensity(intensity) {
23 + const num = Number(intensity);
24 + return !isNaN(num) && num >= 1 && num <= 10;
25 + }
26 +
27 + // Validate rating value
28 + export function isValidRating(rating) {
29 + const num = Number(rating);
30 + return !isNaN(num) && num >= 1 && num <= 10;
31 + }
32 +
33 + // Validate level values (stress, anxiety, energy)
34 + export function isValidLevel(level) {
35 + const num = Number(level);
36 + return !isNaN(num) && num >= 1 && num <= 10;
37 + }
38 +
39 + // Validate mood components array
40 + export function validateMoodComponents(components) {
41 + if (!Array.isArray(components)) {
42 + return { valid: false, error: 'Components must be an array' };
43 + }
44 +
45 + const emotions = new Set();
46 +
47 + for (const component of components) {
48 + if (!Object.hasOwn(component, 'emotion') || !Object.hasOwn(component, 'intensity')) {
49 + return { valid: false, error: 'Each component must have emotion and intensity' };
50 + }
51 +
52 + const emotionLower = component.emotion.toLowerCase();
53 +
54 + if (!isValidEmotion(emotionLower)) {
55 + return { valid: false, error: `Invalid emotion: ${component.emotion}` };
56 + }
57 +
58 + if (!isValidIntensity(component.intensity)) {
59 + return { valid: false, error: `Invalid intensity for ${component.emotion}: must be 1-10` };
60 + }
61 +
62 + if (emotions.has(emotionLower)) {
63 + return { valid: false, error: `Duplicate emotion: ${component.emotion}` };
64 + }
65 +
66 + emotions.add(emotionLower);
67 + }
68 +
69 + return { valid: true };
70 + }
71 +
72 + // Get emotion statistics
73 + export function calculateEmotionStats(moodComponents) {
74 + const stats = {
75 + dominant: null,
76 + average: 0,
77 + breakdown: {}
78 + };
79 +
80 + if (!moodComponents || moodComponents.length === 0) {
81 + return stats;
82 + }
83 +
84 + let maxIntensity = 0;
85 + let totalIntensity = 0;
86 +
87 + moodComponents.forEach(component => {
88 + const intensity = Number(component.intensity);
89 + totalIntensity += intensity;
90 +
91 + stats.breakdown[component.emotion] = intensity;
92 +
93 + if (intensity > maxIntensity) {
94 + maxIntensity = intensity;
95 + stats.dominant = component.emotion;
96 + }
97 + });
98 +
99 + stats.average = totalIntensity / moodComponents.length;
100 +
101 + return stats;
102 + }
api/src/utils/validators.js
@@ -0,0 +1,97 @@
1 + // Email validation
2 + export function isValidEmail(email) {
3 + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
4 + return emailRegex.test(email);
5 + }
6 +
7 + // Password validation (minimum 6 characters)
8 + export function isValidPassword(password) {
9 + return password && password.length >= 6;
10 + }
11 +
12 + import { parseISO, isValid, isFuture, isAfter } from 'date-fns';
13 +
14 + // Date validation
15 + export function isValidDate(dateString) {
16 + if (!dateString) return false;
17 + try {
18 + const date = parseISO(dateString);
19 + return isValid(date);
20 + } catch {
21 + return false;
22 + }
23 + }
24 +
25 + // Check if date is not in the future
26 + export function isNotFutureDate(dateString) {
27 + if (!dateString) return false;
28 + try {
29 + const date = parseISO(dateString);
30 + return isValid(date) && !isFuture(date);
31 + } catch {
32 + return false;
33 + }
34 + }
35 +
36 + // Validate date range
37 + export function isValidDateRange(startDate, endDate) {
38 + if (!startDate || !endDate) return false;
39 + try {
40 + const start = parseISO(startDate);
41 + const end = parseISO(endDate);
42 +
43 + if (!isValid(start) || !isValid(end)) {
44 + return false;
45 + }
46 +
47 + return !isAfter(start, end);
48 + } catch {
49 + return false;
50 + }
51 + }
52 +
53 + // Sanitize string input
54 + export function sanitizeString(str) {
55 + if (typeof str !== 'string') return '';
56 + return str.trim().slice(0, 500); // Limit to 500 chars
57 + }
58 +
59 + // Validate required fields
60 + export function validateRequiredFields(data, fields) {
61 + const missing = [];
62 +
63 + for (const field of fields) {
64 + if (data[field] === undefined || data[field] === null || data[field] === '') {
65 + missing.push(field);
66 + }
67 + }
68 +
69 + return {
70 + valid: missing.length === 0,
71 + missing
72 + };
73 + }
74 +
75 + // Validate number in range
76 + export function isInRange(value, min, max) {
77 + const num = Number(value);
78 + return !isNaN(num) && num >= min && num <= max;
79 + }
80 +
81 + // Parse and validate integer
82 + export function parseIntOrDefault(value, defaultValue = null) {
83 + const parsed = parseInt(value, 10);
84 + return isNaN(parsed) ? defaultValue : parsed;
85 + }
86 +
87 + // Validate pagination parameters
88 + export function validatePagination(page, limit) {
89 + const validPage = Math.max(1, parseIntOrDefault(page, 1));
90 + const validLimit = Math.min(100, Math.max(1, parseIntOrDefault(limit, 20)));
91 +
92 + return {
93 + page: validPage,
94 + limit: validLimit,
95 + offset: (validPage - 1) * validLimit
96 + };
97 + }