Add resource management to document and email template schemas, including validation and normalization functions for resource names. Ensure unique resource names and proper structure for resource references.
All checks were successful
farmcontrol/farmcontrol-scheduler/pipeline/head This commit looks good

This commit is contained in:
Tom Butcher 2026-09-13 19:51:46 +01:00
parent c9d2f35bc0
commit 21517ca5c6
2 changed files with 96 additions and 5 deletions

View File

@ -2,8 +2,33 @@ import mongoose from 'mongoose';
import { generateId } from '../../utils.js'; import { generateId } from '../../utils.js';
const { Schema } = mongoose; const { Schema } = mongoose;
const RENDER_DOCUMENT_TEMPLATE_CALL = const RESOURCE_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
/fc\.renderDocumentTemplate\s*\(\s*(['"])([^'"]+)\1/g;
function normalizeTemplateResourceName(value) {
return String(value ?? '')
.trim()
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '');
}
function validateTemplateResources(resources) {
const names = new Set();
for (const resource of resources || []) {
if (
!RESOURCE_NAME_PATTERN.test(resource?.name || '') ||
resource?.file == null ||
resource.file === '' ||
names.has(resource.name)
) {
return false;
}
names.add(resource.name);
}
return true;
}
const RENDER_DOCUMENT_TEMPLATE_CALL = /fc\.renderDocumentTemplate\s*\(\s*(['"])([^'"]+)\1/g;
function extractRenderDocumentTemplateReferences(content) { function extractRenderDocumentTemplateReferences(content) {
if (content == null || typeof content !== 'string' || content === '') { if (content == null || typeof content !== 'string' || content === '') {
@ -103,6 +128,27 @@ const documentTemplateSchema = new Schema(
refPath: 'objectType', refPath: 'objectType',
required: false, required: false,
}, },
resources: {
type: [
new Schema(
{
name: {
type: String,
required: true,
set: normalizeTemplateResourceName,
match: RESOURCE_NAME_PATTERN,
},
file: { type: Schema.Types.ObjectId, ref: 'file', required: true },
},
{ _id: true }
),
],
default: [],
validate: {
validator: validateTemplateResources,
message: 'Template resource names must be unique.',
},
},
}, },
{ timestamps: true } { timestamps: true }
); );

View File

@ -2,6 +2,32 @@ import mongoose from 'mongoose';
import { generateId } from '../../utils.js'; import { generateId } from '../../utils.js';
const { Schema } = mongoose; const { Schema } = mongoose;
const RESOURCE_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
function normalizeTemplateResourceName(value) {
return String(value ?? '')
.trim()
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '');
}
function validateTemplateResources(resources) {
const names = new Set();
for (const resource of resources || []) {
if (
!RESOURCE_NAME_PATTERN.test(resource?.name || '') ||
resource?.file == null ||
resource.file === '' ||
names.has(resource.name)
) {
return false;
}
names.add(resource.name);
}
return true;
}
const RENDER_EMAIL_TEMPLATE_CALL = /fc\.renderEmailTemplate\s*\(\s*(['"])([^'"]+)\1/g; const RENDER_EMAIL_TEMPLATE_CALL = /fc\.renderEmailTemplate\s*\(\s*(['"])([^'"]+)\1/g;
function extractReferences(content) { function extractReferences(content) {
@ -36,13 +62,32 @@ const emailTemplateSchema = new Schema(
active: { type: Boolean, required: true, default: true }, active: { type: Boolean, required: true, default: true },
global: { type: Boolean, required: true, default: false }, global: { type: Boolean, required: true, default: false },
parent: { type: Schema.Types.ObjectId, ref: 'emailTemplate', required: false }, parent: { type: Schema.Types.ObjectId, ref: 'emailTemplate', required: false },
referencedTemplates: [ referencedTemplates: [{ type: Schema.Types.ObjectId, ref: 'emailTemplate', required: false }],
{ type: Schema.Types.ObjectId, ref: 'emailTemplate', required: false },
],
subject: { type: String, required: false, default: '' }, subject: { type: String, required: false, default: '' },
content: { type: String, required: false, default: '<Container></Container>' }, content: { type: String, required: false, default: '<Container></Container>' },
testObject: { type: Schema.Types.ObjectId, refPath: 'objectType', required: false }, testObject: { type: Schema.Types.ObjectId, refPath: 'objectType', required: false },
attachments: { type: [emailTemplateAttachmentSchema], default: [] }, attachments: { type: [emailTemplateAttachmentSchema], default: [] },
resources: {
type: [
new Schema(
{
name: {
type: String,
required: true,
set: normalizeTemplateResourceName,
match: RESOURCE_NAME_PATTERN,
},
file: { type: Schema.Types.ObjectId, ref: 'file', required: true },
},
{ _id: true }
),
],
default: [],
validate: {
validator: validateTemplateResources,
message: 'Template resource names must be unique.',
},
},
}, },
{ timestamps: true } { timestamps: true }
); );