All files / services/workflow-service-api/src/shared/schemas index.js

100% Statements 104/104
100% Branches 1/1
100% Functions 0/0
100% Lines 104/104

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 1051x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x  
const Joi = require('joi');
 
/**
 * Schémas de validation pour l'API Workflow
 */
 
// Destination (« raffinage + cible »). Le raffinage (mode/spec/llmModel) est porté par
// la destination ; les destinations de même couple sont dédupliquées en un seul plan LLM.
//   - 'display'     : aucune cible, id absent → output seul.
//   - 'assertion-kb'|'data-kb' : id = KB (le schéma/prompt de la KB complète le spec).
//   - 'api'         : id = API externe.
const destinationSchema = Joi.object({
  type: Joi.string().valid('assertion-kb', 'data-kb', 'semantic-kb', 'api', 'display').required(),
  id: Joi.string().when('type', {
    is: 'display',
    then: Joi.string().allow('', null).optional(),
    otherwise: Joi.string().required()
  }),
  prompt: Joi.string().allow('').optional(),   // '' = pas de prompt specifique (Joi refuse la chaine vide par defaut)
  promptMode: Joi.string().valid('relevance', 'specific').optional(),
  refinement: Joi.object({
    mode: Joi.string().valid('assertion', 'fact', 'data', 'semantic').optional(),
    spec: Joi.object().optional(),
    llmModel: Joi.string().optional()
  }).optional()
});
 
const schemas = {
  // Extraction avec workflow existant - supporte 3 modes: web, fileUrl, fileUploaded
  // Le raffinage vient exclusivement des destinations (destination.refinement) : plus de
  // mode/spec/llmModel au niveau du workflow.
  extract: Joi.object({
    url: Joi.string().uri(),
    fileUrl: Joi.string().uri(),
    fileId: Joi.string(),
    filename: Joi.string(),
    maxItems: Joi.number().integer().min(1).max(100).optional(),
    extractionSpec: Joi.string().optional(),
    destinations: Joi.array().items(destinationSchema).optional()
  }).xor('url', 'fileUrl', 'fileId'),
 
  // Extraction générique - toutes les infos de config dans le body
  extractGeneric: Joi.object({
    url: Joi.string().uri(),
    fileUrl: Joi.string().uri(),
    fileId: Joi.string(),
    filename: Joi.string(),
    maxItems: Joi.number().integer().min(1).max(100).optional(),
    extractionSpec: Joi.string().optional(),
    routerId: Joi.string().optional(),
    targetKnowledgeBases: Joi.array().items(Joi.string()).optional()
  }).xor('url', 'fileUrl', 'fileId'),
 
  extractWithSteps: Joi.object({
    // Soit extractor (web/file), soit input (texte brut)
    extractor: Joi.object({
      type: Joi.string().valid('web', 'file', 'url').required(),
      url: Joi.string().uri(),
      fileUrl: Joi.string().uri(),
      fileId: Joi.string(),
      filename: Joi.string().when('fileId', {
        is: Joi.exist(),
        then: Joi.required(),
        otherwise: Joi.optional()
      }),
      config: Joi.object().optional(),
      browserProvider: Joi.string().valid('playwright', 'brightdata', 'browserless').optional(),
    }).xor('url', 'fileUrl', 'fileId'),
 
    // Input texte brut (alternative à extractor)
    // input: { content: string, source?: string }
    input: Joi.object({
      content: Joi.string().min(1).required(),
      source: Joi.string().optional()
    }),
 
    // Clé d'idempotence fournie par l'appelant : si présente, devient le jobId/WorkflowId (dédup Temporal).
    idempotencyKey: Joi.string().optional(),
 
    // Destination unique (extraction manuelle) ou liste de destinations (programmatique/crawler)
    destination: Joi.alternatives().try(
      destinationSchema,
      Joi.array().items(destinationSchema)
    ).optional()
  }).xor('extractor', 'input'),
 
  // Extraction web (legacy)
  webExtraction: Joi.object({
    url: Joi.string().uri().required(),
    max_items: Joi.number().integer().min(1).max(100).allow(null).default(50)
  }),
 
  // Extraction fichier (legacy)
  fileExtraction: Joi.object({
    file_url: Joi.string().uri(),
    max_items: Joi.number().integer().min(1).max(100).allow(null).default(50)
  }),
 
  jobId: Joi.object({
    job_id: Joi.string().required()
  })
};
 
module.exports = schemas;